Is there a way to outline text with a dark line in PIL?
You can take a look at this Text Outline Using PIL:
import Image, ImageFont, ImageDrawimport win32api, osx, y = 10, 10fname1 = "c:/test.jpg"im = Image.open(fname1)pointsize = 30fillcolor = "red"shadowcolor = "yellow"text = "hi there"font = win32api.GetWindowsDirectory() + "\\Fonts\\ARIALBD.TTF"draw = ImageDraw.Draw(im)font = ImageFont.truetype(font, pointsize)# thin borderdraw.text((x-1, y), text, font=font, fill=shadowcolor)draw.text((x+1, y), text, font=font, fill=shadowcolor)draw.text((x, y-1), text, font=font, fill=shadowcolor)draw.text((x, y+1), text, font=font, fill=shadowcolor)# thicker borderdraw.text((x-1, y-1), text, font=font, fill=shadowcolor)draw.text((x+1, y-1), text, font=font, fill=shadowcolor)draw.text((x-1, y+1), text, font=font, fill=shadowcolor)draw.text((x+1, y+1), text, font=font, fill=shadowcolor)# now draw the text over itdraw.text((x, y), text, font=font, fill=fillcolor)fname2 = "c:/test2.jpg"im.save(fname2)os.startfile(fname2)
Check the stroke_width option, which implements stroke/border/outline
effect. For shadow
effect, you can draw with a small offset. Bellow is an example to add subtitle to image.
#!/usr/bin/env pythonfrom PIL import Image, ImageDraw, ImageFontdef add_subtitle( bg, text="nice", xy=("center", 20), font="font/SourceHanSansSC-Regular.otf", font_size=53, font_color=(255, 255, 255), stroke=2, stroke_color=(0, 0, 0), shadow=(4, 4), shadow_color=(0, 0, 0),): """draw subtitle on image by pillow Args: bg(PIL image): image to add subtitle text(str): subtitle xy(tuple): absolute top left location of subtitle ...: extra style of subtitle Returns: bg(PIL image): image with subtitle """ stroke_width = stroke xy = list(xy) W, H = bg.width, bg.height font = ImageFont.truetype(str(font), font_size) w, h = font.getsize(text, stroke_width=stroke_width) if xy[0] == "center": xy[0] = (W - w) // 2 if xy[1] == "center": xy[1] = (H - h) // 2 draw = ImageDraw.Draw(bg) if shadow: draw.text( (xy[0] + shadow[0], xy[1] + shadow[1]), text, font=font, fill=shadow_color ) draw.text( (xy[0], xy[1]), text, font=font, fill=font_color, stroke_width=stroke_width, stroke_fill=stroke_color, ) return bgif __name__ == "__main__": bg = Image.open("./Screenshot_20200626-131218.png") bg = add_subtitle(bg) bg.save("out.png")
This is how I handled the problem when I needed to do it for frame counters. Just a heads up if you start to push this too far for the thickness, then you will need more draws to cover your areas you're missing.
from PIL import Image,ImageDraw,ImageFontimport os#setting variblesimgFile = "frame_0.jpg"output = "frame_edit_0.jpg"font = ImageFont.truetype("arial.ttf", 30)text = "SEQ_00100_SHOT_0004_FR_0001"textColor = 'white'shadowColor = 'black'outlineAmount = 3#open imageimg = Image.open(imgFile)draw = ImageDraw.Draw(img)#get the size of the imageimgWidth,imgHeight = img.size#get text sizetxtWidth, txtHeight = draw.textsize(text, font=font)#get location to place textx = imgWidth - txtWidth - 100y = imgHeight - txtHeight - 100#create outline textfor adj in range(outlineAmount): #move right draw.text((x-adj, y), text, font=font, fill=shadowColor) #move left draw.text((x+adj, y), text, font=font, fill=shadowColor) #move up draw.text((x, y+adj), text, font=font, fill=shadowColor) #move down draw.text((x, y-adj), text, font=font, fill=shadowColor) #diagnal left up draw.text((x-adj, y+adj), text, font=font, fill=shadowColor) #diagnal right up draw.text((x+adj, y+adj), text, font=font, fill=shadowColor) #diagnal left down draw.text((x-adj, y-adj), text, font=font, fill=shadowColor) #diagnal right down draw.text((x+adj, y-adj), text, font=font, fill=shadowColor)#create normal text on imagedraw.text((x,y), text, font=font, fill=textColor)img.save(output)print 'Finished'os.startfile(output)