Python 图像库 - 文本渲染
Posted
技术标签:
【中文标题】Python 图像库 - 文本渲染【英文标题】:Python Imaging Library - Text rendering 【发布时间】:2011-07-21 20:11:07 【问题描述】:我正在尝试使用 PIL 渲染一些文本,但坦率地说,结果很糟糕。
例如,这是我在 Photoshop 中写的一些文字:
以及 PIL 的结果:
如您所见,PIL 的结果并不令人满意。也许我只是挑剔,但是有什么方法可以使用 PIL 绘制文本,从而获得更接近我的参考图像的结果?
这是我在 Python 2.7 和 PIL 1.1.7 上使用的代码
image = Image.new("RGBA", (288,432), (255,255,255))
usr_font = ImageFont.truetype("resources/HelveticaNeueLight.ttf", 25)
d_usr = ImageDraw.Draw(image)
d_usr = d_usr.text((105,280), "Travis L.",(0,0,0), font=usr_font)
【问题讨论】:
【参考方案1】:我想出了我自己认为可以接受的解决方案。
我所做的是使文本变大,例如需要放大 3 倍,然后通过抗锯齿将其缩小,这不是 100% 完美,但它比默认值要好得多,而且不会需要 cairo 或 pango。
例如,
image = Image.new("RGBA", (600,150), (255,255,255))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("resources/HelveticaNeueLight.ttf", fontsize)
draw.text((10, 0), txt, (0,0,0), font=font)
img_resized = image.resize((188,45), Image.ANTIALIAS)
你最终得到了这个结果,
这比我以前用相同字体得到的要好得多。
【讨论】:
我需要文本居中,而不是左对齐。有没有办法使用上述解决方案来做到这一点? 请记住,尽管字体通常为不同的大小提供不同的形状(不仅仅是放大和缩小一种形状)。这在较小的目标尺寸中尤其明显。 另一种说法:不要假设你需要这样做。我发现使用这个函数渲染文本很慢,这可能会增加不必要的减速。【参考方案2】:尝试使用pycairo - Cairo 绘图库的 python 绑定 - 它对于更精细的绘图很有用,带有抗锯齿线, 等等 - 你也可以生成基于矢量的图像
正确处理字体,布局复杂,需要使用 “pango”和“pangocairo”库也是如此。虽然它们是制作的 对于严肃的字体工作(所有 GTK+ 小部件都使用 pango 进行字体渲染), 可用的文档和示例非常差。
下面的示例显示了系统中可用的打印并呈现 在命令行上作为参数传递的字体系列中的示例文本。
# -*- coding: utf-8 -*-
import cairo
import pango
import pangocairo
import sys
surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, 320, 120)
context = cairo.Context(surf)
#draw a background rectangle:
context.rectangle(0,0,320,120)
context.set_source_rgb(1, 1, 1)
context.fill()
#get font families:
font_map = pangocairo.cairo_font_map_get_default()
families = font_map.list_families()
# to see family names:
print [f.get_name() for f in font_map.list_families()]
#context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
# Positions drawing origin so that the text desired top-let corner is at 0,0
context.translate(50,25)
pangocairo_context = pangocairo.CairoContext(context)
pangocairo_context.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
layout = pangocairo_context.create_layout()
fontname = sys.argv[1] if len(sys.argv) >= 2 else "Sans"
font = pango.FontDescription(fontname + " 25")
layout.set_font_description(font)
layout.set_text(u"Travis L.")
context.set_source_rgb(0, 0, 0)
pangocairo_context.update_layout(layout)
pangocairo_context.show_layout(layout)
with open("cairo_text.png", "wb") as image_file:
surf.write_to_png(image_file)
【讨论】:
呃。对于一个简单的应用程序来说,这看起来有太多的依赖关系。 (我正在尝试使用自制软件在 10.6 上设置 pango 和 cairo)......我可能需要重新考虑一些事情。不过感谢您的帮助。 还有其他成像库可能更容易设置。我首先想到的是 pygame,但它不能对文本做任何严肃的工作(根本没有抗锯齿或亚像素渲染)。也许 imagemagick (pythonmagick) 的包装器更容易处理。 只是一个更新:Pygame 确实有抗锯齿和漂亮的字体渲染。只是它的一些绘图原语没有。而且它比上面的 pango + cairo 示例要容易处理一个数量级。 @jsbueno Pygame 的字体渲染在 Windows 上很难看,大多数字体指标都被破坏了,甚至没有接近真实的渲染。 (那是pygame 1.9。可能最后的版本更好,没有尝试)。【参考方案3】:我从未使用过 PIL,但快速查看 Draw 方法的文档表明 PIL 提供了一种渲染简单图形的方法。 Photoshop 提供了一种渲染复杂图形的方法。要获得接近 Photoshop 的结果,至少需要字体提示和抗锯齿。 PIL 的文档甚至没有暗示有这样的能力。您可能想考虑使用一个可以更好地在图像上呈现文本的外部工具。例如,ImageMagick(您需要使用 8 位版本,它处理标准 24 位 RGB)。你可以在这里找到一些文本绘图示例:http://www.imagemagick.org/Usage/draw/
【讨论】:
感谢 dave,您的回答对我非常有帮助,因为我希望以编程方式生成大型单个字母作为各种颜色的图像。 Imagemagick 能够轻松地进行定位并处理图像尺寸和其他参数。【参考方案4】:建议:使用 Wand 或其他 Imaging 库
这是一个使用魔杖的例子 -
from wand.color import Color
from wand.image import Image
from wand.drawing import Drawing
from wand.compat import nested
with Drawing() as draw:
with Image(width=1000, height=100, background=Color('lightblue')) as img:
draw.font_family = 'Indie Flower'
draw.font_size = 40.0
draw.push()
draw.fill_color = Color('hsl(0%, 0%, 0%)')
draw.text(0,int(img.height/2 + 20), 'Hello, world!')
draw.pop()
draw(img)
img.save(filename='image.png')
【讨论】:
对我来说比其他两个工作得更好。这是我第一次听说“魔杖”,它是一个 Python 绑定到 ImageMagick。【参考方案5】:在 python3 中有一个别名字体选项。我在任何地方都找不到这个答案,希望它可以帮助像我这样在谷歌上找到这个问题并且不得不挖掘很长时间才能找到答案的人。
draw = ImageDraw.Draw(img)
draw.fontmode = "L"
Mentioned in the docs here
【讨论】:
使用"L"
仍然会为我生成抗锯齿文本,但draw.fontmode = "1"
有效。作为参考,这些模式记录在here; ImageDraw
的 fontmode
成员似乎没有在任何地方记录;感谢您抽出宝贵时间发布此答案!
^^ @ReignofError 为我工作,谢谢!【参考方案6】:
您也可以尝试将字体写入两次,它可以极大地提高质量。
image = Image.new("RGBA", (288,432), (255,255,255))
usr_font = ImageFont.truetype("resources/HelveticaNeueLight.ttf", 25)
d_usr = ImageDraw.Draw(image)
d_usr = d_usr.text((105,280), "Travis L.",(0,0,0), font=usr_font)
d_usr = d_usr.text((105,280), "Travis L.",(0,0,0), font=usr_font)
【讨论】:
确实让它更大胆,但我不能说它更好。也许这是主观的。 边框粗而不那么清晰,我认为这更好。至少它接近 Photoshop 或 GIMP 的标准质量。 为什么在同一个地方第二次渲染同样的东西会有不同? @martineau - 我猜当你画两次时,灰色阴影而不是全黑的像素会变暗。以上是关于Python 图像库 - 文本渲染的主要内容,如果未能解决你的问题,请参考以下文章
用于读取图像内文本的最佳 Python/Ruby 库 [关闭]