PIL如何根据图像的大小缩放文本大小

时间:2011-02-04 19:41:36

标签: python fonts image-manipulation python-imaging-library scaling

我正在尝试动态缩放文本以放置在不同但已知尺寸的图像上。该文本将作为水印应用。有没有办法缩放与图像尺寸相关的文本?我不要求文本占据整个表面区域,只要足够可见,以便易于识别且难以移除。我正在使用Python Imaging Library 1.1.7版。在Linux上。

我希望能够设置文字大小与图像尺寸的比例,比如大小的1/10。

我一直在查看字体大小属性来更改大小,但我没有运气来创建算法来扩展它。我想知道是否有更好的方法。

关于如何实现这一目标的任何想法?

由于

4 个答案:

答案 0 :(得分:52)

你可以增加字体大小,直到找到合适的方式。 font.getsize()是告诉您渲染文本有多大的函数。

import ImageFont, ImageDraw, Image

image = Image.open('hsvwheel.png')
draw = ImageDraw.Draw(image)
txt = "Hello World"
fontsize = 1  # starting font size

# portion of image width you want text width to be
img_fraction = 0.50

font = ImageFont.truetype("arial.ttf", fontsize)
while font.getsize(txt)[0] < img_fraction*image.size[0]:
    # iterate until the text size is just larger than the criteria
    fontsize += 1
    font = ImageFont.truetype("arial.ttf", fontsize)

# optionally de-increment to be sure it is less than criteria
fontsize -= 1
font = ImageFont.truetype("arial.ttf", fontsize)

print 'final font size',fontsize
draw.text((10, 25), txt, font=font) # put the text on the image
image.save('hsvwheel_txt.png') # save it

如果这对您来说效率不高,您可以实现根查找方案,但我猜测font.getsize()函数与其他图像编辑过程相比是小土豆。

答案 1 :(得分:7)

通常,当您更改字体大小时,它不会是字体大小的线性变化。

Non-linear Scaling

现在这通常取决于软件,字体等......此示例取自Typophile并使用LaTex + Computer Modern字体。正如您所看到的,它不完全是线性缩放。因此,如果您在使用非线性字体缩放时遇到问题,那么我不确定如何解决它,但有一个建议可能是。

  1. 将字体渲染为您想要的尺寸,然后通过常规图像缩放算法向上/向下缩放...
  2. 只是接受它不会完全是线性缩放并尝试创建某种表格/算法,以便为字体选择最接近的点大小以匹配图像大小。

答案 2 :(得分:4)

我知道这是一个古老的问题,已经用我也使用过的solution了。谢谢@Paul!

尽管每次迭代将字体大小增加一倍可能会很耗时(至少对于我在可怜的小型服务器上而言)。因此,例如。小文字(例如“ Foo”)大约需要1-2秒,具体取决于图像大小。

为解决此问题,我调整了Pauls代码,使其搜索数字的方式类似于二进制搜索。

breakpoint = img_fraction * photo.size[0]
jumpsize = 75
while True:
    if font.getsize(text)[0] < breakpoint:
        fontsize += jumpsize
    else:
        jumpsize = jumpsize // 2
        fontsize -= jumpsize
    font = ImageFont.truetype(font_path, fontsize)
    if jumpsize <= 1:
        break

就像这样,它会增大字体大小,直到它超过断点为止,然后从那里开始上下移动(将跳转大小每次减小一半),直到其大小合适为止。

这样,我可以将步长从大约200+减少到大约10,因此从大约1-2秒减少到0.04到0.08秒。

这是Pauls代码的替代品(用于while语句及其后两行,因为您已经在while中获得了正确的字体大小)

这是在几分钟内完成的,因此任何改进都值得赞赏!我希望这可以为正在寻求更高性能的解决方案的人提供帮助。

答案 3 :(得分:0)

尽管其他答案说字体大小不会线性缩放,但在我测试的所有示例中,它们确实线性缩放(在 1-2% 内)。

因此,如果您需要一个更简单、更高效的版本,可以在几个百分点内工作,您可以复制/粘贴以下内容:

from PIL import ImageFont, ImageDraw, Image

def find_font_size(text, font, image, target_width_ratio):
    tested_font_size = 100
    tested_font = ImageFont.truetype(font, tested_font_size)
    observed_width, observed_height = get_text_size(text, image, tested_font)
    estimated_font_size = tested_font_size / (observed_width / image.width) * target_width_ratio
    return round(estimated_font_size)

def get_text_size(text, image, font):
    im = Image.new('RGB', (image.width, image.height))
    draw = ImageDraw.Draw(im)
    return draw.textsize(text, font)

然后可以像这样使用函数 find_font_size()(完整示例):

width_ratio = 0.5  # Portion of the image the text width should be (between 0 and 1)
font_family = "arial.ttf"
text = "Hello World"

image = Image.open('image.jpg')
editable_image = ImageDraw.Draw(image)
font_size = find_font_size(text, font_family, image, width_ratio)
font = ImageFont.truetype(font_family, font_size)
print(f"Font size found = {font_size} - Target ratio = {width_ratio} - Measured ratio = {get_text_size(text, image, font)[0] / image.width}")

editable_image.text((10, 10), text, font=font)
image.save('output.png')

对于 225x225 图像将打印的内容:

>> Font size found = 22 - Target ratio = 0.5 - Measured ratio = 0.502

我用各种字体和图片大小测试了 find_font_size(),它适用于所有情况。

如果你想知道这个函数是如何工作的,基本上tested_font_size是用来找出如果我们使用这个特定的字体大小来生成文本会得到什么比例。然后,我们使用交叉乘法规则来获得目标字体大小。

我测试了 tested_font_size 的不同值,发现只要不是太小,没有任何区别。

相关问题