【问题标题】:Properly render text with a given font in Python and accurately detect its boundaries在 Python 中使用给定字体正确渲染文本并准确检测其边界
【发布时间】:2019-05-16 16:04:43
【问题描述】:

这可能看起来很简单,我也认为会是这样,但显然不是。我一定花了一个星期的时间来完成这项工作,但看在我的爱上,我无法做到。

我需要什么

我需要在 Python 中使用任何给定字体(类似手写)呈现任何给定字符串(仅包含标准字符)。字体必须从 TTF 文件加载。我还需要能够准确地检测它的边界(获得文本的确切开始和结束位置,垂直和水平),最好是在绘制它之前。最后,如果输出是一个我可以继续处理的数组,而不是写入磁盘的图像文件,那真的会让我的生活更轻松。

我的尝试

Imagemagick 绑定(即 Wand):在设置图像大小并在其上渲染文本之前,无法弄清楚如何获取文本指标。

Pango 通过 Pycairo 绑定:几乎不存在文档,无法弄清楚如何从文件中加载 TrueType 字体。

PIL(枕头):最有希望的选择。我已经设法准确地计算出任何文本的高度(令人惊讶的是,这不是 getsize 返回的高度),但对于某些字体来说,宽度似乎有问题。不仅如此,那些宽度有问题的字体也会被错误地渲染。即使图像足够大,它们也会被截断。

这里有一些例子,带有“令人困惑”的文字:

字体:Lovers Quarrel

结果:

字体:Miss Fajardose

结果:

这是我用来生成图像的代码:

from PIL import Image, ImageDraw, ImageFont
import cv2
import numpy as np
import glob
import os

font_size = 75
font_paths = sorted(glob.glob('./fonts/*.ttf'))
text = "Puzzling"
background_color = 180
text_color = 50
color_variance = 60
cv2.namedWindow('display', 0)

for font_path in font_paths:

    font = ImageFont.truetype(font_path, font_size)
    text_width, text_height = font.getsize(text)

    ascent, descent = font.getmetrics()
    (width, baseline), (offset_x, offset_y) = font.font.getsize(text)

    # +100 added to see that text gets cut off
    PIL_image = Image.new('RGB', (text_width-offset_x+100, text_height-offset_y), color=0x888888)
    draw = ImageDraw.Draw(PIL_image)
    draw.text((-offset_x, -offset_y), text, font=font, fill=0)

    cv2.imshow('display', np.array(PIL_image))
    k = cv2.waitKey()
    if chr(k & 255) == 'q':
        break

一些问题

字体有问题吗?一些同事告诉我可能是这样,但我不这么认为,因为 Imagemagick 通过命令行正确渲染了它们。

我的代码有问题吗?我是不是做错了什么导致文本被截断?

最后,这是 PIL 中的错误吗?在这种情况下,您建议我使用哪个库来解决我的问题?我应该再试一次 Pango 和 Wand 吗?

【问题讨论】:

  • 在命令行 ImageMagick 中,您可以在创建文本时使用 -debug annotate 获取字体指标。见imagemagick.org/Usage/text/#font_info。我不知道这在 Wand 中是否可用。但是您可以使用 Python 子进程调用来执行此操作。
  • 如果您知道文本需要放入的框,那么stackoverflow.com/a/39557083/740553 可能更符合您的要求。
  • @fmw42 谢谢,我也许可以用它做点什么,尽管在做了一些测试之后,指标似乎也不完全正确,在大多数情况下,PIL 在计算高度方面做得更好。
  • @Mike'Pomax'Kamermans 我需要一个恒定的字体大小,我需要知道它需要多少空间,而不是相反。
  • 注意:当我在最新版本的 PIL 中尝试使用最新版本的链接字体时,不再出现这种情况。 PIL 现在可以正确渲染了。

标签: python fonts python-imaging-library text-rendering


【解决方案1】:

pyvips 似乎正确地做到了这一点。我试过这个:

$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> x = pyvips.Image.text("Puzzling", dpi=300, font="Miss Fajardose", fontfile="/home/john/pics/MissFajardose-Regular.ttf")
>>> x.write_to_file("x.png")

制作:

pyvips 文档对选项进行了快速介绍:

https://libvips.github.io/pyvips/vimage.html#pyvips.Image.text

或者 C 库文档有更多详细信息:

http://libvips.github.io/libvips/API/current/libvips-create.html#vips-text

它制作了一个单波段 8 位图像的抗锯齿文本,您可以将其用于进一步处理、传递给 NumPy 或 PIL 等。介绍中有一节介绍如何将 libvips 图像转换为数组:

https://libvips.github.io/pyvips/intro.html#numpy-and-pil

【讨论】:

  • 感谢您的回答,效果很好,但有些字体的高度不正确(它们被剪掉了)。 Imagemagick 对这些字体也有一些问题。我开始认为可能真的没有正确指定指标,但在这种情况下,我无法理解 PIL 如何设法正确呈现它们。 (例如:Ananda Hastakchyar)。
  • 你能发布一个失败字体的链接吗?我没有得到fonts.google.com/?query=Ananda+Hastakchyar的结果
  • 哦,在这里找到它1001fonts.com/ananda-hastakchyar-font.html我会更新我的答案。
  • 使用 Ananda Hastakchyar 字体,带有 libvips 8.6.3 的 pyvips 无法在顶部和底部留出足够的空间。这是因为这种手写字体故意在通常的着墨区域之外乱涂乱写——例如,如果您尝试在文字处理器中选择字体,您会发现一行上的下一行将与下一行上的上一行重叠。我已经在 HEAD 8.6 中修复了这个问题,改进将在 8.6.4 中,感谢您指出这一点。 github.com/jcupitt/libvips/commit/…
  • 根据 pyvips docs for text[1],它可以返回一个Image 或“list[Image, Dict[str, mixed]]”,但它没有解释当第二种类型时被返回,我不清楚第二种类型到底是什么意思 List[Union[Image, Dict[str, Any]]] ?用于文本的 libvips 文档并不表示这样的内容。 [1]:libvips.github.io/pyvips/vimage.html#pyvips.Image.text
猜你喜欢
  • 2011-10-05
  • 2021-03-17
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多