Python 是否有办法使用matplotlib以指定的宽度和高度打印文本?

Python 是否有办法使用matplotlib以指定的宽度和高度打印文本?,python,matplotlib,Python,Matplotlib,我想使用matplotlib为我的工作创建一个序列徽标 序列徽标如中所示。 每个字符都有一个特定的高度。我想用matplotlib做这个 如何更改字体的纵横比?我尝试使用matplotlib.transform中的Affine2D,如下所示,但没有成功 ax.text(1,1,“A”).set_变换(matplotlib.transforms.Affine2D().scale(0.5,1)) 有一个简单的解决方法吗?我在Matplotlib中尝试了不同的拉伸文本宽度的方法,但都没有成功。可能他

我想使用matplotlib为我的工作创建一个序列徽标

序列徽标如中所示。 每个字符都有一个特定的高度。我想用matplotlib做这个

如何更改字体的纵横比?我尝试使用matplotlib.transform中的Affine2D,如下所示,但没有成功

ax.text(1,1,“A”).set_变换(matplotlib.transforms.Affine2D().scale(0.5,1))

有一个简单的解决方法吗?

我在Matplotlib中尝试了不同的拉伸文本宽度的方法,但都没有成功。可能他们还没有正确地实现拉伸,我甚至在文档中看到这样的通知,比如
这个特性还没有实现用于字体拉伸功能之一

因此,我决定编写自己的助手函数来完成这项任务。它使用PIL模块的绘图功能,您必须安装一次下一个模块
python-m pip install-numpy matplotlib

请注意,“我的助手”函数
text\u draw\u mpl(…)
接受以绘图单位表示的x、y偏移和宽度、高度,例如,如果绘图上的x/y范围为0到1,则必须在函数值中使用,如0.1、0.2、0.3、0.4

我的另一个助手函数
text\u draw\u np(…)
是低级函数,可能您永远不会直接使用它,它使用以像素表示的宽度和高度,并在输出时生成形状的RGB数组
(高度,宽度,3)
(3表示RGB颜色)

在“我的函数”中,可以将背景色(
bg
参数)和前景色(
color
参数)作为字符串颜色名称(如
'magenta'
'blue'
)传递,也可以将绿色作为RGB元组,如
(0,255,0)
。默认情况下,如果未提供,则前景色为黑色,背景色为白色

请注意,我的函数支持参数
remove_gaps
,如果
True
,则将从绘制的文本图片的所有侧面删除空白,如果
False
,则保留空白。空白是通过在字体文件中绘制图示符的方式引入的,例如,小写字母
m
顶部的空间较大,大写字母
T
顶部的空间较小。字体有这样的空间,所以整个文本具有相同的高度,并且两行文本之间有一些间隙,不能合并

另外请注意,我提供了默认Windows Arial font
c:/Windows/fonts/Arial.ttf
,如果您有Linux,或者您想要其他字体,只需从internet下载任何免费的Unicode TrueType(.ttf)字体(例如),并将该字体放在脚本附近,然后在下面的代码中修改路径。此外,PIL模块还支持其他格式,如其支持的文档中所述:TrueType和OpenType字体(以及FreeType库支持的其他字体格式)

输出:


您可以执行
ax.text(1,1,“A”,fontsize=20)…
但这不会改变外观。您想将宽度/高度比设置为非默认值吗?顺便问一下,您使用Affine2D的做法有什么问题?根本不起作用?还是太长而写不出这样一行代码?或者说它起作用了,但不是很精确?您也可以将transform对象作为参数传递。text(1,1,“A”,transform=…Affine2D…)如果
。set\u transform
不起作用,因为我所做的描述不好。我无法上载图像,因为我是此服务的新手。是的,我想改变纵横比以满足制作序列徽标的要求。看了之后,我想通过应用
Affine2D().scale(0.5,1)
某种程度上也可以用于文本,但结果是文本粘在图像的左下角。我为您的任务实现了自己的解决方案,告诉您是否有不适合您的内容!感谢您运行的示例和对文档的调查!我没有注意到matplotlib中没有拉伸文本的函数(是的,我知道如果有,几乎没有人使用这个函数)。这个例子对我很有用,而且还可以!但我担心正文顶部的空白。这个可以调整吗?创建序列徽标时,需要填充顶部、底部、左侧和右侧字符之间的边距,因此如果可以更改该部分,这将非常有用。@IwanoNatsuki此边距实际上来自字体字形本身,即顶部的间隙包含在字体文件中绘制符号的边界框中。这意味着不同的字体文件可能包含或不包含此间隙。你可以试试其他字体。一般来说,我不知道对于不同的字体,应该消除多少间隙。也可能是Unicode字母表中的某些字母有此间隙,而其他字母不在同一字体文件中。例如,如果您使用大写字母
H
,则间隙较小,对于较小的
m
,间隙将更大。@IwanoNatsuki消除此间隙的一种方法是使用numpy搜索背景相等的行并将其删除。如果你愿意,我就这么做。但这意味着,在这种情况下,如果你觉得合适的话,所有大写字母的大小都将与小写字母相同。我明白了。后一种方法是搜索角色边界,对我来说很好。序列标志,特别是共识标志,类似于和只有4个字符;A、 T、G和C。与“I”不同,当这些宽度和高度固定为相同大小时,它们看起来很好。它容易实现吗?这看起来有点难实现…@IwanoNatsuki我已经更新了我的代码,以支持有间隙和消除间隙,并在我的答案中附上了有间隙和无间隙的文本图片。如果这是你想要的,请看一看!
def text_draw_np(text, width, height, *, font = 'c:/windows/fonts/arial.ttf', bg = (255, 255, 255), color = (0, 0, 0), remove_gaps = False, cache = {}):
    import math, numpy as np, PIL.Image, PIL.ImageDraw, PIL.ImageFont, PIL.ImageColor
    def get_font(fname, size):
        key = ('font', fname, size)
        if key not in cache:
            cache[key] = PIL.ImageFont.truetype(fname, size = size, encoding = 'unic')
        return cache[key]
    width, height = math.ceil(width), math.ceil(height)
    pil_font = get_font(font, 24)
    text_width, text_height = pil_font.getsize(text)
    pil_font = get_font(font, math.ceil(1.2 * 24 * max(width / text_width, height / text_height)))
    text_width, text_height = pil_font.getsize(text)
    canvas = PIL.Image.new('RGB', (text_width, text_height), bg)
    draw = PIL.ImageDraw.Draw(canvas)
    draw.text((0, 0), text, font = pil_font, fill = color)
    if remove_gaps:
        a = np.asarray(canvas)
        bg_rgb = PIL.ImageColor.getrgb(bg)
        b = np.zeros_like(a)
        b[:, :, 0] = bg_rgb[0]; b[:, :, 1] = bg_rgb[1]; b[:, :, 2] = bg_rgb[2]
        t0 = np.any((a != b).reshape(a.shape[0], -1), axis = -1)
        top, bot = np.flatnonzero(t0)[0], np.flatnonzero(t0)[-1]
        t0 = np.any((a != b).transpose(1, 0, 2).reshape(a.shape[1], -1), axis = -1)
        lef, rig = np.flatnonzero(t0)[0], np.flatnonzero(t0)[-1]
        a = a[top : bot, lef : rig]
        canvas = PIL.Image.fromarray(a)
    canvas = canvas.resize((width, height), PIL.Image.LANCZOS)
    return np.asarray(canvas)
    
def text_draw_mpl(fig, ax, text, offset_x, offset_y, width, height, **nargs):
    axbb = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
    pxw, pxh = axbb.width * fig.dpi * width / (ax.get_xlim()[1] - ax.get_xlim()[0]), axbb.height * fig.dpi * height / (ax.get_ylim()[1] - ax.get_ylim()[0])
    ax.imshow(text_draw_np(text, pxw * 1.2, pxh * 1.2, **nargs), extent = (offset_x, offset_x + width, offset_y, offset_y + height), aspect = 'auto', interpolation = 'lanczos')

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_ylim(0, 1000)
ax.set_xlim(0, 1000)
text_draw_mpl(fig, ax, 'Hello!', 100, 500, 150, 500, color = 'green', bg = 'magenta', remove_gaps = True)
text_draw_mpl(fig, ax, 'World!', 100, 200, 800, 100, color = 'blue', bg = 'yellow', remove_gaps = True)
text_draw_mpl(fig, ax, ' Gaps ', 400, 500, 500, 200, color = 'red', bg = 'gray', remove_gaps = False)
plt.show()