Is there a way to outline text with a dark line in PIL?

后端 未结 5 1483
轮回少年
轮回少年 2020-12-17 15:04

I\'m using python/PIL to write text on a set of PNG images. I was able to get the font I wanted, but I\'d like to now outline the text in black.

相关标签:
5条回答
  • 2020-12-17 15:13

    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,ImageFont
    import os
    
    #setting varibles
    imgFile = "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 image
    img = Image.open(imgFile)
    draw = ImageDraw.Draw(img)
    
    #get the size of the image
    imgWidth,imgHeight = img.size
    
    #get text size
    txtWidth, txtHeight = draw.textsize(text, font=font)
    
    #get location to place text
    x = imgWidth - txtWidth - 100
    y = imgHeight - txtHeight - 100
    
    #create outline text
    for 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 image
    draw.text((x,y), text, font=font, fill=textColor)
    
    img.save(output)
    print 'Finished'
    os.startfile(output)
    
    0 讨论(0)
  • 2020-12-17 15:25

    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 python
    from PIL import Image, ImageDraw, ImageFont
    
    
    def 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 bg
    
    
    if __name__ == "__main__":
        bg = Image.open("./Screenshot_20200626-131218.png")
        bg = add_subtitle(bg)
        bg.save("out.png")
    
    0 讨论(0)
  • 2020-12-17 15:25

    I was looking for that on the internet and I saw that PIL doesn't have native support to add text border. In that case, I create this proposal method based on Alec Bennett solution.

    The problem that I found with that solution is about how to create a smooth border on larger border size. Problem Example

    If we walk over 360 degrees or 2pi radians, adding the same text, we could create a better implementation of the text border functionality.

    Here's the example function

        def add_text(image, text, location, font, fontsize=14, fontcolor=(0, 0, 0), 
                     border=0, border_color=(0, 0, 0), points=15):
            font_format = ImageFont.truetype(font, fontsize)
            drawer = ImageDraw.Draw(image)
    
            if border:
                (x, y) = location
                for step in range(0, math.floor(border * points), 1):
                    angle = step * 2 * math.pi / math.floor(border * points)
                    drawer.text((x - border * math.cos(angle), y - border * math.sin(angle)), text, border_color, font=font_format)
            drawer.text(location, text, fontcolor, font=font_format)
            return image
    

    The same image, text and config generate the next Final Result

    0 讨论(0)
  • 2020-12-17 15:26

    You can take a look at this Text Outline Using PIL:

    import Image, ImageFont, ImageDraw
    
    import win32api, os
    
    x, y = 10, 10
    
    fname1 = "c:/test.jpg"
    im = Image.open(fname1)
    pointsize = 30
    fillcolor = "red"
    shadowcolor = "yellow"
    
    text = "hi there"
    
    font = win32api.GetWindowsDirectory() + "\\Fonts\\ARIALBD.TTF"
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype(font, pointsize)
    
    # thin border
    draw.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 border
    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)
    draw.text((x+1, y+1), text, font=font, fill=shadowcolor)
    
    # now draw the text over it
    draw.text((x, y), text, font=font, fill=fillcolor)
    
    fname2 = "c:/test2.jpg"
    im.save(fname2)
    
    os.startfile(fname2)
    
    0 讨论(0)
  • 2020-12-17 15:27
    import Image, ImageFont, ImageDraw
    
    import win32api, os
    
    x, y = 10, 10
    
    fname1 = "c:/test.jpg"
    im = Image.open(fname1)
    pointsize = 30
    fillcolor = "red"
    shadowcolor = "yellow"
    
    text = "hi there"
    
    font = win32api.GetWindowsDirectory() + "\\Fonts\\ARIALBD.TTF"
    draw = ImageDraw.Draw(im)
    font = ImageFont.truetype(font, pointsize)
    
    # thin border
    draw.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 border
    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)
    draw.text((x+1, y+1), text, font=font, fill=shadowcolor)
    
    # now draw the text over it
    draw.text((x, y), text, font=font, fill=fillcolor)
    
    fname2 = "c:/test2.jpg"
    im.save(fname2)
    
    os.startfile(fname2)
    
    0 讨论(0)
提交回复
热议问题