如何使用 python 的 PIL 以一定角度绘制文本?

发布于 2024-07-07 07:22:44 字数 171 浏览 10 评论 0原文

使用 Python 我希望能够使用 PIL 以不同角度绘制文本。

例如,假设您正在钟面周围绘制数字。 数字 3 将按预期显示,而 12 将逆时针旋转 90 度绘制。

因此,我需要能够从许多不同的角度绘制许多不同的弦。

Using Python I want to be able to draw text at different angles using PIL.

For example, imagine you were drawing the number around the face of a clock. The number 3 would appear as expected whereas 12 would we drawn rotated counter-clockwise 90 degrees.

Therefore, I need to be able to draw many different strings at many different angles.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

残龙傲雪 2024-07-14 07:22:44

将文本绘制到临时空白图像中,旋转该图像,然后将其粘贴到原始图像上。 您可以将这些步骤包装在一个函数中。 祝你好运,找出要使用的确切坐标——我冰冷的大脑现在还不能胜任。

该演示在图像上倾斜书写黄色文本:

# Demo to add rotated text to an image using PIL

import Image
import ImageFont, ImageDraw, ImageOps

im=Image.open("stormy100.jpg")

f = ImageFont.load_default()
txt=Image.new('L', (500,50))
d = ImageDraw.Draw(txt)
d.text( (0, 0), "Someplace Near Boulder",  font=f, fill=255)
w=txt.rotate(17.5,  expand=1)

im.paste( ImageOps.colorize(w, (0,0,0), (255,255,84)), (242,60),  w)

Draw text into a temporary blank image, rotate that, then paste that onto the original image. You could wrap up the steps in a function. Good luck figuring out the exact coordinates to use - my cold-fogged brain isn't up to it right now.

This demo writes yellow text on a slant over an image:

# Demo to add rotated text to an image using PIL

import Image
import ImageFont, ImageDraw, ImageOps

im=Image.open("stormy100.jpg")

f = ImageFont.load_default()
txt=Image.new('L', (500,50))
d = ImageDraw.Draw(txt)
d.text( (0, 0), "Someplace Near Boulder",  font=f, fill=255)
w=txt.rotate(17.5,  expand=1)

im.paste( ImageOps.colorize(w, (0,0,0), (255,255,84)), (242,60),  w)
独木成林 2024-07-14 07:22:44

在创建 Image 对象之前了解文本的像素大小也很有用。 我在绘制图表时使用了这样的代码。 然后我就没有遇到任何问题,例如数据标签的对齐(图像与文本一样大)。

(...)
img_main = Image.new("RGB", (200, 200))
font = ImageFont.load_default()

# Text to be rotated...
rotate_text = u'This text should be rotated.'

# Image for text to be rotated
img_txt = Image.new('L', font.getsize(rotate_text))
draw_txt = ImageDraw.Draw(img_txt)
draw_txt.text((0,0), rotate_text, font=font, fill=255)
t = img_value_axis.rotate(90, expand=1)

本页已描述了将两个图像连接在一起的其余部分。
当你旋转“不规则”的角度时,你必须稍微改进一下这段代码。 它实际上适用于 90、180、270...

It's also usefull to know our text's size in pixels before we create an Image object. I used such code when drawing graphs. Then I got no problems e.g. with alignment of data labels (the image is exactly as big as the text).

(...)
img_main = Image.new("RGB", (200, 200))
font = ImageFont.load_default()

# Text to be rotated...
rotate_text = u'This text should be rotated.'

# Image for text to be rotated
img_txt = Image.new('L', font.getsize(rotate_text))
draw_txt = ImageDraw.Draw(img_txt)
draw_txt.text((0,0), rotate_text, font=font, fill=255)
t = img_value_axis.rotate(90, expand=1)

The rest of joining the two images together is already described on this page.
When you rotate by an "unregular" angle, you have to improve this code a little bit. It actually works for 90, 180, 270...

你爱我像她 2024-07-14 07:22:44

这是一个受答案启发的工作版本,但它无需打开或保存图像即可工作。

这两个图像具有彩色背景和不同于零的 Alpha 通道,以显示正在发生的情况。 将两个 Alpha 通道从 92 更改为 0 将使它们完全透明。

from PIL import Image, ImageFont, ImageDraw

text = 'TEST'
font = ImageFont.truetype(r'C:\Windows\Fonts\Arial.ttf', 50)
width, height = font.getsize(text)

image1 = Image.new('RGBA', (200, 150), (0, 128, 0, 92))
draw1 = ImageDraw.Draw(image1)
draw1.text((0, 0), text=text, font=font, fill=(255, 128, 0))

image2 = Image.new('RGBA', (width, height), (0, 0, 128, 92))
draw2 = ImageDraw.Draw(image2)
draw2.text((0, 0), text=text, font=font, fill=(0, 255, 128))

image2 = image2.rotate(30, expand=1)

px, py = 10, 10
sx, sy = image2.size
image1.paste(image2, (px, py, px + sx, py + sy), image2)

image1.show()

Here is a working version, inspired by the answer, but it works without opening or saving images.

The two images have colored background and alpha channel different from zero to show what's going on. Changing the two alpha channels from 92 to 0 will make them completely transparent.

from PIL import Image, ImageFont, ImageDraw

text = 'TEST'
font = ImageFont.truetype(r'C:\Windows\Fonts\Arial.ttf', 50)
width, height = font.getsize(text)

image1 = Image.new('RGBA', (200, 150), (0, 128, 0, 92))
draw1 = ImageDraw.Draw(image1)
draw1.text((0, 0), text=text, font=font, fill=(255, 128, 0))

image2 = Image.new('RGBA', (width, height), (0, 0, 128, 92))
draw2 = ImageDraw.Draw(image2)
draw2.text((0, 0), text=text, font=font, fill=(0, 255, 128))

image2 = image2.rotate(30, expand=1)

px, py = 10, 10
sx, sy = image2.size
image1.paste(image2, (px, py, px + sx, py + sy), image2)

image1.show()
庆幸我还是我 2024-07-14 07:22:44

前面的答案绘制成一个新图像,旋转它,然后将其绘制回源图像。 这会留下文本痕迹。 我们不希望这样。

这是一个版本,它会裁剪将要绘制的源图像区域,旋转它,绘制到其中,然后将其旋转回来。 这意味着我们可以立即在最终表面上绘制,而不必求助于掩模。

def draw_text_90_into (text: str, into, at):
    # Measure the text area
    font = ImageFont.truetype (r'C:\Windows\Fonts\Arial.ttf', 16)
    wi, hi = font.getsize (text)

    # Copy the relevant area from the source image
    img = into.crop ((at[0], at[1], at[0] + hi, at[1] + wi))

    # Rotate it backwards
    img = img.rotate (270, expand = 1)

    # Print into the rotated area
    d = ImageDraw.Draw (img)
    d.text ((0, 0), text, font = font, fill = (0, 0, 0))

    # Rotate it forward again
    img = img.rotate (90, expand = 1)

    # Insert it back into the source image
    # Note that we don't need a mask
    into.paste (img, at)

添加支持其他角度、颜色等很简单。

The previous answers draw into a new image, rotate it, and draw it back into the source image. This leaves text artifacts. We don't want that.

Here is a version that instead crops the area of the source image that will be drawn onto, rotates it, draws into that, and rotates it back. This means that we draw onto the final surface immediately, without having to resort to masks.

def draw_text_90_into (text: str, into, at):
    # Measure the text area
    font = ImageFont.truetype (r'C:\Windows\Fonts\Arial.ttf', 16)
    wi, hi = font.getsize (text)

    # Copy the relevant area from the source image
    img = into.crop ((at[0], at[1], at[0] + hi, at[1] + wi))

    # Rotate it backwards
    img = img.rotate (270, expand = 1)

    # Print into the rotated area
    d = ImageDraw.Draw (img)
    d.text ((0, 0), text, font = font, fill = (0, 0, 0))

    # Rotate it forward again
    img = img.rotate (90, expand = 1)

    # Insert it back into the source image
    # Note that we don't need a mask
    into.paste (img, at)

Supporting other angles, colors etc is trivial to add.

手长情犹 2024-07-14 07:22:44

这是对角加水印的更完整示例。 通过计算对角线角度和字体大小来处理任意图像比例、大小和文本长度。

from PIL import Image, ImageFont, ImageDraw
import math

# sample dimensions
pdf_width = 1000
pdf_height = 1500

#text_to_be_rotated = 'Harry Moreno'
text_to_be_rotated = 'Harry Moreno ([email protected])'
message_length = len(text_to_be_rotated)

# load font (tweak ratio based on your particular font)
FONT_RATIO = 1.5
DIAGONAL_PERCENTAGE = .5
diagonal_length = int(math.sqrt((pdf_width**2) + (pdf_height**2)))
diagonal_to_use = diagonal_length * DIAGONAL_PERCENTAGE
font_size = int(diagonal_to_use / (message_length / FONT_RATIO))
font = ImageFont.truetype(r'./venv/lib/python3.7/site-packages/reportlab/fonts/Vera.ttf', font_size)
#font = ImageFont.load_default() # fallback

# target
image = Image.new('RGBA', (pdf_width, pdf_height), (0, 128, 0, 92))

# watermark
opacity = int(256 * .5)
mark_width, mark_height = font.getsize(text_to_be_rotated)
watermark = Image.new('RGBA', (mark_width, mark_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark)
draw.text((0, 0), text=text_to_be_rotated, font=font, fill=(0, 0, 0, opacity))
angle = math.degrees(math.atan(pdf_height/pdf_width))
watermark = watermark.rotate(angle, expand=1)

# merge
wx, wy = watermark.size
px = int((pdf_width - wx)/2)
py = int((pdf_height - wy)/2)
image.paste(watermark, (px, py, px + wx, py + wy), watermark)

image.show()

它位于 Colab https://colab.research.google.com/drive /1ERl7PiX6xKy5H9EEMulBKPgglF6euCNA?usp=sharing 您应该向 Colab 提供示例图像。
500 x 300 300 x 500

Here's a fuller example of watermarking diagonally. Handles arbitrary image ratios, sizes and text lengths by calculating the angle of the diagonal and font size.

from PIL import Image, ImageFont, ImageDraw
import math

# sample dimensions
pdf_width = 1000
pdf_height = 1500

#text_to_be_rotated = 'Harry Moreno'
text_to_be_rotated = 'Harry Moreno ([email protected])'
message_length = len(text_to_be_rotated)

# load font (tweak ratio based on your particular font)
FONT_RATIO = 1.5
DIAGONAL_PERCENTAGE = .5
diagonal_length = int(math.sqrt((pdf_width**2) + (pdf_height**2)))
diagonal_to_use = diagonal_length * DIAGONAL_PERCENTAGE
font_size = int(diagonal_to_use / (message_length / FONT_RATIO))
font = ImageFont.truetype(r'./venv/lib/python3.7/site-packages/reportlab/fonts/Vera.ttf', font_size)
#font = ImageFont.load_default() # fallback

# target
image = Image.new('RGBA', (pdf_width, pdf_height), (0, 128, 0, 92))

# watermark
opacity = int(256 * .5)
mark_width, mark_height = font.getsize(text_to_be_rotated)
watermark = Image.new('RGBA', (mark_width, mark_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(watermark)
draw.text((0, 0), text=text_to_be_rotated, font=font, fill=(0, 0, 0, opacity))
angle = math.degrees(math.atan(pdf_height/pdf_width))
watermark = watermark.rotate(angle, expand=1)

# merge
wx, wy = watermark.size
px = int((pdf_width - wx)/2)
py = int((pdf_height - wy)/2)
image.paste(watermark, (px, py, px + wx, py + wy), watermark)

image.show()

Here it is in a colab https://colab.research.google.com/drive/1ERl7PiX6xKy5H9EEMulBKPgglF6euCNA?usp=sharing you should provide an example image to the colab.
500 by 300300 by 500

虚拟世界 2024-07-14 07:22:44

我并不是说这会很容易,或者这个解决方案一定适合您,但请查看此处的文档:

http://effbot.org/imagingbook/pil-index.htm

特别要注意 Image、ImageDraw 和 ImageFont 模块。

这是一个可以帮助您的示例:


import Image
im = Image.new("RGB", (100, 100))
import ImageDraw
draw = ImageDraw.Draw(im)
draw.text((50, 50), "hey")
im.rotate(45).show()

为了做你真正想做的事情,你可能需要制作一堆单独的正确旋转的文本图像,然后通过一些更奇特的操作将它们组合在一起。 毕竟它看起来可能仍然不太好。 例如,我不确定如何处理抗锯齿等,但这可能不好。 祝你好运,如果有人有更简单的方法,我也有兴趣知道。

I'm not saying this is going to be easy, or that this solution will necessarily be perfect for you, but look at the documentation here:

http://effbot.org/imagingbook/pil-index.htm

and especially pay attention to the Image, ImageDraw, and ImageFont modules.

Here's an example to help you out:


import Image
im = Image.new("RGB", (100, 100))
import ImageDraw
draw = ImageDraw.Draw(im)
draw.text((50, 50), "hey")
im.rotate(45).show()

To do what you really want you may need to make a bunch of separate correctly rotated text images and then compose them all together with some more fancy manipulation. And after all that it still may not look great. I'm not sure how antialiasing and such is handled for instance, but it might not be good. Good luck, and if anyone has an easier way, I'd be interested to know as well.

云柯 2024-07-14 07:22:44

如果您使用 aggdraw,则可以使用 settransform() 来旋转文本。 由于 effbot.org 处于离线状态,因此它有点没有记录。

# Matrix operations
def translate(x, y):
    return np.array([[1, 0, x], [0, 1, y], [0, 0, 1]])


def rotate(angle):
    c, s = np.cos(angle), np.sin(angle)
    return np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])


def draw_text(image, text, font, x, y, angle):
    """Draw text at x,y and rotated angle radians on the given PIL image"""
    m = np.matmul(translate(x, y), rotate(angle))
    transform = [m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]]
    draw = aggdraw.Draw(image)
    draw.settransform(transform)
    draw.text((tx, ty), text, font)
    draw.settransform()
    draw.flush()

If you a using aggdraw, you can use settransform() to rotate the text. It's a bit undocumented, since effbot.org is offline.

# Matrix operations
def translate(x, y):
    return np.array([[1, 0, x], [0, 1, y], [0, 0, 1]])


def rotate(angle):
    c, s = np.cos(angle), np.sin(angle)
    return np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])


def draw_text(image, text, font, x, y, angle):
    """Draw text at x,y and rotated angle radians on the given PIL image"""
    m = np.matmul(translate(x, y), rotate(angle))
    transform = [m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2]]
    draw = aggdraw.Draw(image)
    draw.settransform(transform)
    draw.text((tx, ty), text, font)
    draw.settransform()
    draw.flush()
櫻之舞 2024-07-14 07:22:44

Pillow 还支持 Image.transpose(method) 将图像旋转 90、180 或 270 度,并相应地校正尺寸。 但是,它不适用于其他角度:

from PIL import Image, ImageDraw, ImageFont

# Create the original image
img = Image.new( 'RGB', (400, 600), color='#D0D0D0')
    
# Create a second image with the title
img_title = Image.new( 'RGB', (600,60), color='#E0FFFF')
draw_title = ImageDraw.Draw(img_title)
font = ImageFont.truetype("arialbd.ttf", size = 40)
draw_title.text( (590, 30), 'Rotate title on the left', 
    font=font, anchor="rm", fill="#80E0C0")

# Rotate the image containing the title
img_title = img_title.transpose(Image.Transpose.ROTATE_90)

# Paste rotated title over the original image
img.paste( img_title, (0,0))

# Save the original image
img.save('Image with rotated title.png')

带有旋转标题的图像.png

Pillow also supports Image.transpose(method) to rotate an image 90, 180 or 270 degrees, and corrects the size accordingly. However, it does not work for other angles:

from PIL import Image, ImageDraw, ImageFont

# Create the original image
img = Image.new( 'RGB', (400, 600), color='#D0D0D0')
    
# Create a second image with the title
img_title = Image.new( 'RGB', (600,60), color='#E0FFFF')
draw_title = ImageDraw.Draw(img_title)
font = ImageFont.truetype("arialbd.ttf", size = 40)
draw_title.text( (590, 30), 'Rotate title on the left', 
    font=font, anchor="rm", fill="#80E0C0")

# Rotate the image containing the title
img_title = img_title.transpose(Image.Transpose.ROTATE_90)

# Paste rotated title over the original image
img.paste( img_title, (0,0))

# Save the original image
img.save('Image with rotated title.png')

Image with rotated title.png

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文