Нарисуйте наложенный текст с помощью PIL (LOW) - PullRequest
0 голосов
/ 14 июня 2019

Я пытаюсь нарисовать выделенный текст с помощью ПОДУШКИ, чтобы символы перекрывались.Однако перекрытие происходит в «z-layer» манере, то есть символы все еще непрозрачны и не совсем прозрачны.

Мой код выглядит следующим образом:

YC_CHARS = '0123456789'
YC_LENGTH = 6
YC_WIDTH = 200
YC_HEIGHT = 60
YC_BACKCOLOR = (255, 255, 255, 255)
YC_BACKCOLOR_TR = (255, 255, 255, 0)
YC_TEXTCOLOR = (0, 0, 0, 255)
YC_FONTS =  ['fonts/antquab.ttf', 'fonts/ariblk.ttf', 'fonts/arlrdbd.ttf',
             'fonts/comic.ttf', 'fonts/impact.ttf']
YC_FONTSZ = list(range(40, 51, 5))

def synth_captcha():

    def outline_text(dr, pos, text, fnt, stroke, fill):
        "Draw outline-style text"
        dr.text((pos[0]-1, pos[1]), text, font=fnt, fill=stroke)
        dr.text((pos[0]+1, pos[1]), text, font=fnt, fill=stroke)
        dr.text((pos[0], pos[1]-1), text, font=fnt, fill=stroke)
        dr.text((pos[0], pos[1]+1), text, font=fnt, fill=stroke)
        dr.text(tuple(pos), text, font=fnt, fill=fill)

    img = Image.new('RGB', (YC_WIDTH, YC_HEIGHT), color=YC_BACKCOLOR)

    digit_offset = [random.randint(1, 3), random.randint(1, 10)]
    digit_sz = [0, 0]
    digit_offset = [1, 0]

    for i in range(YC_LENGTH):

        digit = random.choice(YC_CHARS)        
        font = ImageFont.truetype(random.choice(YC_FONTS), random.choice(YC_FONTSZ))
        draw = ImageDraw.Draw(img, mode='RGBA')

        digit_offset[0] += digit_sz[0] + random.randint(-digit_sz[0]//1.5, 0)
        digit_sz = draw.textsize(digit, font=font)
        digit_offset[1] = random.randint(1, max(2, YC_HEIGHT - digit_sz[1] - 2))
        outline_text(draw, digit_offset, digit, font, YC_TEXTCOLOR, YC_BACKCOLOR_TR)

    img.save('img.png')

Этот код создаетследующее изображение:
actual image

Но мне нужно иметь его следующим образом:
expected image

1 Ответ

0 голосов
/ 14 июня 2019

Вам нужно использовать Image.alpha_composite для рисования объектов с частичной непрозрачностью.

Пример кода:

def synth_captcha():

    def outline_text(dr, pos, text, fnt, stroke, fill):
        "Draw outline-style text"
        dr.text((pos[0]-1, pos[1]), text, font=fnt, fill=stroke)
        dr.text((pos[0]+1, pos[1]), text, font=fnt, fill=stroke)
        dr.text((pos[0], pos[1]-1), text, font=fnt, fill=stroke)
        dr.text((pos[0], pos[1]+1), text, font=fnt, fill=stroke)
        dr.text(tuple(pos), text, font=fnt, fill=fill)

    img = Image.new('RGBA', (YC_WIDTH, YC_HEIGHT), color=YC_BACKCOLOR)

    digit_offset = [random.randint(1, 3), random.randint(1, 10)]
    digit_sz = [0, 0]
    digit_offset = [1, 0]

    for i in range(YC_LENGTH):

        digit = random.choice(YC_CHARS)        
        font = ImageFont.truetype(random.choice(YC_FONTS), random.choice(YC_FONTSZ))
        txt = Image.new('RGBA', img.size, YC_BACKCOLOR_TR)
        draw = ImageDraw.Draw(txt)

        digit_offset[0] += digit_sz[0] + random.randint(-digit_sz[0]//1.5, 0)
        digit_sz = draw.textsize(digit, font=font)
        digit_offset[1] = random.randint(1, max(2, YC_HEIGHT - digit_sz[1] - 2))
        outline_text(draw, digit_offset, digit, font, YC_TEXTCOLOR, YC_BACKCOLOR_TR)

        img = Image.alpha_composite(img, txt)

    return img

Редактировать: Изменение YC_BACKCOLOR_TR на (0, 0, 0, 0) даст более солидные штрихи.Я не уверен, почему, но это, вероятно, связано с тем, как dr.text обрабатывает прозрачность на границе символов.

Я предполагаю, что он просто смешивает значение RGBA как 4d-вектор, поэтомудля пикселя, который обычно рисуется с непрозрачностью 50%, смешивание (0, 0, 0, 255) с (255, 255, 255, 0) приводит к (128, 128, 128, 128).Когда этот пиксель имеет альфа-композицию на белом фоне, он становится (192, 192, 192, 255), который светлее серого, чем должен быть.Я думаю, что в вашем случае работа только над альфа-картой (т.е. YC_BACKCOLOR_TR = (0, 0, 0, 0)) является разумным подходом, но мне любопытно, есть ли лучший или концептуально более простой способ сделать это.

...