在 Python 中使用给定字体正确渲染文本并准确检测其边界

kik*_*s34 7 python fonts text-rendering python-imaging-library

这可能是一件非常简单的事情,我也认为是这样,但显然不是。我一定花了一个星期的时间来尝试完成这项工作,但出于对我的热爱,我无法做到。

我需要的

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

我试过的

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

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

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

以下是一些示例,文本为“Puzzling”:

字体:恋人争吵

结果:

恋人争吵渲染

字体:法哈多斯小姐

结果:

法哈多斯小姐渲染

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

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
Run Code Online (Sandbox Code Playgroud)

一些问题

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

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

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

jcu*_*itt 5

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")
Run Code Online (Sandbox Code Playgroud)

使:

在此处输入图片说明

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

  • 使用字体 Ananda Hastakchyar,使用 libvips 8.6.3 的 pyvips 无法在顶部和底部留出足够的空间。这是因为这种手绘字体故意在通常的墨迹区域之外乱涂乱画——例如,如果您尝试在文字处理器中选择字体,您会发现一行的下行部分与下一行的上行部分重叠。我已经在 HEAD 8.6 中修复了这个问题,改进将在 8.6.4 中,感谢您指出这一点。https://github.com/jcupitt/libvips/commit/878c77a035ef0a32db7c249ccb31118932e790d3 (2认同)