PIL: draw transparent text on top of an image - python

In a project I have, I am trying to create an outline over some text. The overall idea is to offset a black text slightly transparent from the original white text.
For some reasons, I get the black text but nether in transparent. Here is the MCVE:
image = Image.open("spongebob.gif").convert("RGBA")
draw = ImageDraw.Draw(image, "RGBA")
font = ImageFont.truetype("impact.ttf", 25)
draw.text((0, 0), "This text should be 5% alpha", fill=(0, 0, 0, 15), font=font)
image.save("foo.gif")
The result:
What did I miss?

This should work for you.
from PIL import Image, ImageDraw, ImageFont
image = Image.open("spongebob.gif").convert("RGBA")
txt = Image.new('RGBA', image.size, (255,255,255,0))
font = ImageFont.truetype("impact.ttf", 25)
d = ImageDraw.Draw(txt)
d.text((0, 0), "This text should be 5% alpha", fill=(0, 0, 0, 15), font=font)
combined = Image.alpha_composite(image, txt)
combined.save("foo.gif")
edit: The reference example from the Pillow documentation
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#example-draw-partial-opacity-text

Related

How do I Anchor Text and Shrink it to fit it on an Image

I fount this code off of the PIL API(here is the link: https://pillow.readthedocs.io/en/stable/handbook/text-anchors.html) and I wanted to also shrink it depending on the size of the text while it is centered.
here is the anchoring code
from PIL import Image, ImageDraw, ImageFont
font = ImageFont.truetype("mont.ttf", 48)
im = Image.new("RGB", (200, 200), "white")
d = ImageDraw.Draw(im)
d.text((100, 100), "Quick", fill="black", anchor="ms", font=font)
im.save('text.png')
And the outcome looks like this:
But if you increase the word size it looks like this:
So I just want the text to be centered and shrunk to fit the image
No detail about the requirements, so here only for result image with fixed size (200, 200), so font size will be changed.
Find the size of text by ImageDraw.textsize
Draw on an image with same width as the text by ImageDraw.text
Resize image to (200-2*border, 200-2*border) by Image.resize
Paste the resized image to a 200x200 image by Image.paste
from PIL import Image, ImageDraw, ImageFont
def text_to_image(text, filename='text.png', border=20):
im = Image.new("RGB", (1, 1), "white")
font = ImageFont.truetype("calibri.ttf", 48)
draw = ImageDraw.Draw(im)
size = draw.textsize(text, font=font)
width = max(size)
im = Image.new("RGB", (width, width), "white")
draw = ImageDraw.Draw(im)
draw.text((width//2, width//2), text, anchor='mm', fill="black", font=font)
im = im.resize((200-2*border, 200-2*border), resample=Image.LANCZOS)
new_im = Image.new("RGB", (200, 200), "white")
new_im.paste(im, (border, border))
new_im.show()
# new_im.save(filename)
text_to_image("Hello World")

Pillow, how to put the text in the center of the image

I use the Pillow (PIL) 6.0 and add text in the image. And I want to put the text in the center of the image. Here is my code,
import os
import string
from PIL import Image
from PIL import ImageFont, ImageDraw, ImageOps
width, height = 100, 100
text = 'H'
font_size = 100
os.makedirs('./{}'.format(text), exist_ok=True)
img = Image.new("L", (width, height), color=0) # "L": (8-bit pixels, black and white)
font = ImageFont.truetype("arial.ttf", font_size)
draw = ImageDraw.Draw(img)
w, h = draw.textsize(text, font=font)
draw.text(((width-w)/2, (height-h)/2), text=text, fill='white', font=font)
img.save('H.png')
Here is the output:
Question:
The text is in the center horizontally, but not in the center vertically. How can I put it in the center horizontally and vertically?
Text always have some added space around characters, e.g. if we create a box that is the exact size reported for your 'H'
img = Image.new("L", (width, height), color=0) # "L": (8-bit pixels, black and white)
font = ImageFont.truetype("arial.ttf", font_size)
draw = ImageDraw.Draw(img)
w, h = draw.textsize(text, font=font)
# draw.text(((width-w)/2, (height-h)/2), text=text, fill='white', font=font)
# img.save('H.png')
img2 = Image.new("L", (w, h), color=0) # "L": (8-bit pixels, black and white)
draw2 = ImageDraw.Draw(img2)
draw2.text((0, 0)), text=text, fill='white', font=font)
img2.save('H.png')
gives the bounding box:
Knowing that line height is normally ~20% larger than the glyphs/characters (+ some trial and error), and we can figure out the extent of the extra space. (The extra space for width is equally distributed so not interesting for centering).
draw2.text((0, 0 - int(h*0.21)), text=text, fill='white', font=font)
which moves the 'H' to the top:
Plugging this back into your original code:
img = Image.new("L", (width, height), color=0) # "L": (8-bit pixels, black and white)
font = ImageFont.truetype("arial.ttf", font_size)
draw = ImageDraw.Draw(img)
w, h = draw.textsize(text, font=font)
h += int(h*0.21)
draw.text(((width-w)/2, (height-h)/2), text=text, fill='white', font=font)
img.save('H.png')
gives:
The 0.21 factor usually works well for a large range of font sizes for the same font. E.g. just plugging in font size 30:
Use of anchors can help with this
import os
import string
from PIL import Image
from PIL import ImageFont, ImageDraw, ImageOps
width, height = 100, 100
text = 'H'
font_size = 100
os.makedirs('./{}'.format(text), exist_ok=True)
img = Image.new("L", (width, height), color=0) # "L": (8-bit pixels, black and white)
font = ImageFont.truetype("arial.ttf", font_size)
draw = ImageDraw.Draw(img)
draw.text(((width)/2, (height)/2), text=text, fill='white', font=font, anchor="mm", align='center')
img.save('H.png')
It works fine without w and h
P.S.: I've tested it, and it can work well with non-English characters also

Correctly centring text (PIL/Pillow)

I'm drawing some text on a black strip, and then pasting the result on top of a base image, using PIL. One criticality is having the text position perfectly in the center of the black strip.
I cater for that via the following code:
from PIL import Image, ImageFont, ImageDraw
background = Image.new('RGB', (strip_width, strip_height)) #creating the black strip
draw = ImageDraw.Draw(background)
font = ImageFont.truetype("/usr/share/fonts/truetype/freefont/FreeSansBold.ttf", 16)
text_width, text_height = draw.textsize("Foooo Barrrr!")
position = ((strip_width-text_width)/2,(strip_height-text_height)/2)
draw.text(position,"Foooo Barrrr!",(255,255,255),font=font)
offset = (0,base_image_height/2)
base_image.paste(background,offset)
Notice how I'm setting position.
Now with all said and done, the result looks like so:
The text isn't precisely middled. It's slightly to the right and down. How do I improve my algorithm?
Remember to pass your font to draw.textsize as a second parameter (and also make sure you are really using the same text and font arguments to draw.textsize and draw.text).
Here's what worked for me:
from PIL import Image, ImageFont, ImageDraw
def center_text(img, font, text, color=(255, 255, 255)):
draw = ImageDraw.Draw(img)
text_width, text_height = draw.textsize(text, font)
position = ((strip_width-text_width)/2,(strip_height-text_height)/2)
draw.text(position, text, color, font=font)
return img
Usage:
strip_width, strip_height = 300, 50
text = "Foooo Barrrr!!"
background = Image.new('RGB', (strip_width, strip_height)) #creating the black strip
font = ImageFont.truetype("times", 24)
center_text(background, font, "Foooo Barrrr!")
Result:

A watermark inside a rectangle which filled the certain color

I want to add a watermark at a picture. But just a text, but a rectangle filled with the black color and a white text inside it.
For now, I only can put a text:
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
img = Image.open("in.jpg")
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf", 66)
#font = ImageFont.truetype("Arialbd.ttf", 66)
draw.text((width - 510, height-100),"copyright",(209,239,8), font=font)
img.save('out.jpg')
This will draw the text on a black rectangular background:
from PIL import Image, ImageFont, ImageDraw
img = Image.open("in.jpg")
width, height = img.width, img.height
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(
"/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf", 66)
x, y = (width - 510, height-100)
# x, y = 10, 10
text = "copyright"
w, h = font.getsize(text)
draw.rectangle((x, y, x + w, y + h), fill='black')
draw.text((x, y), text, fill=(209, 239, 8), font=font)
img.save('out.jpg')
Using imagemagick, a better looking watermark could be made with
from PIL import Image, ImageFont, ImageDraw
font = ImageFont.truetype(
"/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf", 66)
text = "copyright"
size = font.getsize(text)
img = Image.new('RGBA', size=size, color=(0, 0, 0, 0))
draw = ImageDraw.Draw(img)
draw.text((0, 0), text, fill=(209, 239, 8), font=font)
img.save('label.jpg')
and then calling (through subprocess if you wish) something like
composite -dissolve 25% -gravity south label.jpg in.jpg out.jpg
or if you make label with a white background,
composite -compose bumpmap -gravity southeast label.jpg in.jpg out.jpg
To run these commands from within the Python script, you could use subprocess like this:
import subprocess
import shlex
from PIL import Image, ImageFont, ImageDraw
font = ImageFont.truetype(
"/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf", 66)
text = "copyright"
size = font.getsize(text)
img = Image.new('RGBA', size=size, color='white')
draw = ImageDraw.Draw(img)
draw.text((0, 0), text, fill=(209, 239, 8), font=font)
img.save('label.jpg')
cmd = 'composite -compose bumpmap -gravity southeast label.jpg in.jpg out.jpg'
proc = subprocess.Popen(shlex.split(cmd))
proc.communicate()

python PIL chops top of my draw.text

I am using python and PIL to draw some text. I stumbled upon certain tutorial 'cause I needed some guides on how to center text within certain bounds:
the code works but it has some strange behavior. It eats the top of string. Here's how it should like http://img855.imageshack.us/img855/9292/qttempya4744.png
and here's how it looks http://img546.imageshack.us/img546/8541/outn.jpg
here's my code :
import os
from PIL import ImageDraw, ImageFont, Image
def draw_text(text, size, fill=None):
font = ImageFont.truetype('C:\exl.ttf', 30)
size = font.getsize(text)# Returns the width and height of the given text, as a 2-tuple.
size = (size[0],size[1]+15)
im = Image.new('RGBA', size, (0, 0, 0, 0)) # Create a blank image with the given size
draw = ImageDraw.Draw(im)
draw.text((0, 25), text, font=font, fill=fill) #Draw text
return im
img = draw_text('zod', 30, (82, 124, 178))
img.save('C:\out.jpg',"JPEG")
print 'Complete!'
os.startfile('C:\out.jpg')
I have this bug with other fonts too (tried Arial and Verdana).
help plz :)
I have managed to fix this. It appears that drawtext has some issues with certain font sizes.
I've done a bit of experimenting, and it can clearly be seen here that certain font sizes get their top chopped off http://img838.imageshack.us/img838/7677/pilfontsize.jpg
Code for testing above mentioned :
from PIL import ImageDraw, ImageFont, Image
im = Image.new('RGBA', (700, 1600), (0, 0, 0, 0))
fSize = 1
yVal = 1
while fSize <= 50:
font = ImageFont.truetype('arial.ttf', fSize)
fString = "This line is in Arial font size " + str(fSize)
size = font.getsize(fString)
draw = ImageDraw.Draw(im)
draw.text((5, yVal), fString, font=font, fill=None)
fSize += 1
yVal += fSize + 5
Anyhow, I made it, and I'm proud :D
This rendering issue should get worked into by developers of PIL.

Categories

Resources