Search code examples
pythonpython-imaging-library

How do I get smooth text with pythons PIL?


In my Script I open a tiff image and have to paste some text on it in the bottom right corner with PIL. So far so good. But the quality of the text is unusable! The the text should be white. Instead it has pixels cut out. It looks as though, when copying the text onto the original image, there is a cutoff on the alpha channel. Even when setting the mask attribute in Image.paste there is no difference.

I thought this is something basic. Is there a way to get usable text on an image in PIL? Or is there a better package?

I am on a MacBook Pro Ventura 13.4.1, PIL 10.0.1, Python 3.8

from PIL import Image as PILImage, ImageDraw, ImageFont
import math

padding = (10, 16)
img = PILImage.open('/path/to/tiff/image.tiff')
draw = ImageDraw.Draw(img)

text = 'Holi Moli, this looks terrible'
font = ImageFont.truetype('/path/to/font/FreeMono.ttf', 24)
text_img = PILImage.new('RGB', img.size, (255, 255, 255))
textImg = ImageDraw.Draw(text_img)

# get size of rendered text
bbox = draw.textbbox((0,0), text, font)
# calculate position of text background
bgX = img.width - padding[0] - bbox[2]
bgY = img.height - padding[1] - bbox[3]
textX = img.width - math.ceil(padding[0] * 0.5) - bbox[2]
textY = img.height - math.ceil( padding[1] * 0.5) - bbox[3]
draw.text(xy=(textX, textY), text=text, font=font, fill=(255, 255, 255, 0))

img.paste(im=draw._image, box=(0, 0), mask=draw._image)
img.show()

SOLVED: Two things have to be considered. As suggested in @MarkSetchell's answer I did not need to draw the text onto another context. But the real problem with the badly rendered text came from using RGBA Mode.

I do not know why, but it seems that there is a problem with the alpha channel when drawing text in rgba mode. No matter if I draw it directly onto the opened image or copy it from another context. But most important: I could solve my issue by using RGB Mode on the image.

Enlarged added text on black background of tiff

The whole image with text

Sample images including exif data from this repo: https://github.com/ianare/exif-samples/tree/master/tiff


Solution

  • Not sure why you have these 2 lines:

    text_img = PILImage.new('RGB', img.size, (255, 255, 255))
    textImg = ImageDraw.Draw(text_img)
    

    Nor am I sure why you are pasting the text onto the image when you have already drawn it. So, if you simplify your code to incorporate the above ideas, you get:

    #!/usr/bin/env python3
    
    from PIL import Image as PILImage, ImageDraw, ImageFont
    import math
    
    padding = (10, 16)
    img = PILImage.open('BSG1.tiff')
    draw = ImageDraw.Draw(img)
    
    text = 'Gar nicht übel'
    font = ImageFont.truetype('/Users/mark/Library/Fonts/FreeMono.ttf', 24)
    
    bbox = draw.textbbox((0,0), text, font)
    # calculate position of text background
    bgX = img.width - padding[0] - bbox[2]
    bgY = img.height - padding[1] - bbox[3]
    textX = img.width - math.ceil(padding[0] * 0.5) - bbox[2]
    textY = img.height - math.ceil( padding[1] * 0.5) - bbox[3]
    draw.text(xy=(textX, textY), text=text, font=font, fill=(255, 255, 255))
    
    img.show()
    

    enter image description here