Python PIL如何根据图像大小调整文本大小

Python PIL如何根据图像大小调整文本大小,python,fonts,image-manipulation,python-imaging-library,scaling,Python,Fonts,Image Manipulation,Python Imaging Library,Scaling,我正在尝试动态缩放文本以放置在不同但已知尺寸的图像上。文本将作为水印应用。有没有办法根据图像尺寸缩放文本?我不要求文本占据整个表面区域,只是为了足够的可视性,使其易于识别且难以删除。我使用的是Python映像库版本1.1.7。在Linux上 我想能够设置文本大小与图像尺寸的比率,比如说1/10大小之类的 我一直在查看字体大小属性来更改大小,但我没有幸创建一个算法来缩放它。我想知道是否有更好的办法 有什么办法可以做到这一点吗 谢谢通常,当您更改字体大小时,字体大小不会发生线性变化 现在这通常取决于

我正在尝试动态缩放文本以放置在不同但已知尺寸的图像上。文本将作为水印应用。有没有办法根据图像尺寸缩放文本?我不要求文本占据整个表面区域,只是为了足够的可视性,使其易于识别且难以删除。我使用的是Python映像库版本1.1.7。在Linux上

我想能够设置文本大小与图像尺寸的比率,比如说1/10大小之类的

我一直在查看字体大小属性来更改大小,但我没有幸创建一个算法来缩放它。我想知道是否有更好的办法

有什么办法可以做到这一点吗


谢谢

通常,当您更改字体大小时,字体大小不会发生线性变化

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

将字体渲染为您想要的大小,然后通过常规图像缩放算法进行上/下缩放。。。 只需接受这并不完全是线性缩放,并尝试创建某种表格/算法,为字体选择与图像大小最接近的点大小。
您可以增加字体大小,直到找到合适的字体。font.getsize是一个函数,用于告诉您渲染文本的大小

from PIL 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函数与其他图像编辑过程相比是微不足道的。

我知道这是一个老生常谈的问题,已经用我也使用过的方法回答了。谢谢你,保罗

虽然每次迭代增加一个字体大小可能会很耗时,至少对我可怜的小服务器来说是这样。例如,像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秒

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


这是在几分钟内完成的,因此任何改进都是值得赞赏的!我希望这能帮助那些正在寻找性能更友好的解决方案的人。

尽管其他答案说字体大小不是线性缩放的,但在我测试的所有示例中,字体大小都在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,它在所有情况下都有效

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


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

谢谢你的回复,Paul建议的方法解决了这个问题。不过还是要谢谢你。我的第一个想法是,如果你不使用文本缩放行高,那么它当然不会看起来是线性的。但是比较线条的长度,您可以看到6pt字体的渲染长度是12pt字体的一半以上,5pt字体的渲染长度是10pt字体的一半以上。intjumpsize/2可以替换为jumpsize//2。感谢@MarkRansom我调整了它