PIL如何根据图像大小缩放文本大小
Posted
技术标签:
【中文标题】PIL如何根据图像大小缩放文本大小【英文标题】:PIL how to scale text size in relation to the size of the image 【发布时间】:2011-06-21 14:13:38 【问题描述】:我正在尝试动态缩放要放置在不同但已知尺寸的图像上的文本。文本将作为水印应用。有没有办法根据图像尺寸缩放文本?我不要求文本占据整个表面区域,只是为了足够可见,使其易于识别且难以删除。我正在使用 Python Imaging Library 1.1.7 版。在 Linux 上。
我希望能够设置文本大小与图像尺寸的比率,比如大小的 1/10 之类的。
我一直在查看字体大小属性来更改大小,但我没有运气创建一个算法来缩放它。我想知道是否有更好的方法。
关于如何实现这一目标的任何想法?
谢谢
【问题讨论】:
【参考方案1】:尽管其他答案说字体大小不是线性缩放的,但在我测试的所有示例中,它们确实是线性缩放的(在 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
的不同值,发现只要不是太小,没有任何区别。
【讨论】:
【参考方案2】:我知道这是一个老问题,我也用过 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
语句及其后的 2 行,因为您已经在 while
中获得了正确的字体大小)
这是在几分钟内完成的,因此我们对任何改进表示赞赏!我希望这可以帮助一些正在寻找性能更友好的解决方案的人。
【讨论】:
int(jumpsize / 2)
可以替换为jumpsize // 2
。
感谢@MarkRansom 我调整了它【参考方案3】:
您可以只增加字体大小,直到找到合适的值。 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()
功能与您的其他图像编辑过程相比是小菜一碟。
【讨论】:
【参考方案4】:通常,当您更改字体大小时,它不会是字体大小的线性变化。
现在这通常取决于软件、字体等...这个示例取自Typophile,使用 LaTex + Computer Modern 字体。如您所见,它不完全是线性缩放。因此,如果您在非线性字体缩放方面遇到问题,那么我不确定如何解决它,但一个建议可能是。
-
将字体渲染为尽可能接近所需的大小,然后通过常规图像缩放算法放大/缩小...
请接受它不完全是线性缩放,并尝试创建某种表格/算法,为字体选择最接近的点大小以匹配图像大小。
【讨论】:
感谢回复,Paul 建议的方法解决了。不过还是谢谢。 我看到图像的第一个想法是“如果你不使用文本缩放行高,那么当然它看起来不会是线性的”。但是对比线条的长度可以看到,6pt字体的渲染长度是12pt的一半多,5pt是10pt的一半多。以上是关于PIL如何根据图像大小缩放文本大小的主要内容,如果未能解决你的问题,请参考以下文章