Pillow生成大鸟转转转梗图

发布于 / 技术 / 0 条评论

应群友需求,近期使用Python3和Pillow制作了一个梗图的生成器,具体生成的产品大概就是这样:

把代码现在记录一下。

首先,搭建一个代码框架。这个框架引入了必须的类还有一些字体编码设置,防止后续过程中出现文本问题等。

# coding=utf-8
# 引入必须的库文件
from PIL import Image,ImageDraw,ImageFont
def make_text(info,filename):
    # 获取文字信息
    uptext = info[0]
    downtext = info[1]

if __name__ == "__main__":
    # execute only if run as a script
    make_text(['用Python生成','大鸟转转酒吧!'],'test.png')

make_text为生成文字的主方法,设置了两行文字的内容和输出的文件名。在这里,我们用这篇文章的特色图片作为例子。依照目前程序的设计,最终你的图片会生成到程序运行目录的test.png中。

接下来填充第一段方法,在此先不考虑具体的变色如何实现,只是将文字直接填充到图片上导出。

# coding=utf-8
# 引入必须的库文件
from PIL import Image,ImageDraw,ImageFont
def make_text(info,filename):
    # 获取文字信息
    uptext = info[0]
    downtext = info[1]

    # 设置字体,这里选择了更纱黑体,斜体,70号字
    font = ImageFont.truetype("sarasa-bolditalic.ttc", 70)
    # 新建一个背景文件,把Draw对象初始化
    background = Image.new(mode = 'RGBA',size = (500,500),color = 'white')
    draw = ImageDraw.Draw(background)
    # 通过textSize函数获取文字所占面积
    up_size_width,up_size_heigth = draw.textsize(uptext, font=font, spacing=6)
    down_size_width,down_size_heigth = draw.textsize(downtext, font=font, spacing=6)
    # 通过文字面积计算图片面积
    image_size = (down_size_width+130,down_size_heigth+up_size_heigth+30)
    # 修改背景文件大小
    background = background.resize(image_size, Image.ANTIALIAS)
    draw = ImageDraw.Draw(background)
    draw.text((10,10), uptext, fill=(0,0,0), font=font)
    draw.text((110,up_size_heigth+10), downtext, fill=(0,0,0), font=font)
    background.save(filename)

if __name__ == "__main__":
    # execute only if run as a script
    make_text(['用Python生成','大鸟转转酒吧!'],'test.png')

完成这一步并执行代码后,你会得到一个白色背景的图片,上边写着你设置好的文字。

现在,我们尝试给这个文字添加上初始的渐变色。在这里要得知一个alpha图层的概念,alpha图层代表图片的透明度,用0-255来表示。另外,渐变色的生成,我们可以用(初始颜色-结束颜色)/渐变长度来计算出图片的渐变步进,之后初始颜色-渐变步进*渐变长度获得某一特定像素点的颜色值。所以代码接下来这样改

# coding=utf-8
# 引入必须的库文件
from PIL import Image,ImageDraw,ImageFont
def make_text(info,filename):
    # 获取文字信息
    uptext = info[0]
    downtext = info[1]

    # 设置字体,这里选择了更纱黑体,斜体,70号字
    font = ImageFont.truetype("sarasa-bolditalic.ttc", 70)
    # 新建一个背景文件,把Draw对象初始化
    background = Image.new(mode = 'RGBA',size = (500,500),color = 'white')
    draw = ImageDraw.Draw(background)
    # 通过textSize函数获取文字所占面积
    up_size_width,up_size_heigth = draw.textsize(uptext, font=font, spacing=6)
    down_size_width,down_size_heigth = draw.textsize(downtext, font=font, spacing=6)
    # 通过文字面积计算图片面积
    image_size = (down_size_width+130,down_size_heigth+up_size_heigth+30)
    # 修改背景文件大小并为背景赋值渐变色
    background = background.resize(image_size, Image.ANTIALIAS)
    color_list_one = [[255,255,255],[209,100,35],[138,3,0],[196,19,25],[12,4,2],[242,242,242],[162,181,188],[242,242,242],[255,255,255]]
    color_size_one = [10,40,10,25,10,42,42,10]
    background = make_change_color(background,color_list_one,color_size_one)
    # 配置文字图层,绘画透明的文字
    middle_layer = Image.new(mode = 'RGBA',size = image_size,color = 'white')
    draw = ImageDraw.Draw(middle_layer)
    draw.text((10,10), uptext, fill=(0,0,0,0), font=font)
    draw.text((110,up_size_heigth+10), downtext, fill=(0,0,0,0), font=font)
    # 文字图层和背景图层合并,文字图层在上,背景图层在下
    background = Image.alpha_composite(background, middle_layer)
    background.save(filename)


def make_change_color(image,color_list,color_size):
    # 对颜色进行演算并逐行填充到图片上实现渐变效果。
    image_size = image.size
    draw = ImageDraw.Draw(image)
    color_change_start = 0
    for color_flag in range(0,len(color_size)):
        color_start = color_list[color_flag]
        color_end = color_list[color_flag+1]
        color_length = color_size[color_flag]
        # 这里简化了运算,因为简化运算在部分情况下会造成图片发生颜色条纹
        color_step_R = int((color_start[0]-color_end[0])/color_length)
        color_step_G = int((color_start[1]-color_end[1])/color_length)
        color_step_B = int((color_start[2]-color_end[2])/color_length)
        for y in range(color_change_start,color_change_start+color_length):
            # 将步进参数赋值到颜色上
            color_fill_R = color_start[0] - color_step_R * (y-color_change_start)
            color_fill_G = color_start[1] - color_step_G * (y-color_change_start)
            color_fill_B = color_start[2] - color_step_B * (y-color_change_start)
            for x in range(0,image_size[0]):
                draw.point((x,y),fill = (color_fill_R,color_fill_G,color_fill_B))
        color_change_start = color_change_start + color_length;
    return image

if __name__ == "__main__":
    # execute only if run as a script
    make_text(['用Python生成','大鸟转转酒吧!'],'test.png')

可以看出,这一步生成的图片有比较明显的图片断层等变化,是因为这一步中运算采用了比较简化的运算方法,会导致在最终一行对渐变色产生偏差。例如,从43换到13,距离为12。此时步进为2.5,运算后会变成2。43-2×12=19,与13相差较大。误差就是这样诞生的。

接下来,对文字开始进行描边。在这里采用的方法是给文字在每个方向移动相应的像素后描绘,产生渐变的效果。缺点是在文字的边角处会产生一些生硬的拐角,另外会导致多次重复描绘,影响程序效率。

# coding=utf-8
# 引入必须的库文件
from PIL import Image,ImageDraw,ImageFont
def make_text(info,filename):
    # 获取文字信息
    uptext = info[0]
    downtext = info[1]

    # 设置字体,这里选择了更纱黑体,斜体,70号字
    font = ImageFont.truetype("sarasa-bolditalic.ttc", 70)
    # 新建一个背景文件,把Draw对象初始化
    background = Image.new(mode = 'RGBA',size = (500,500),color = 'white')
    draw = ImageDraw.Draw(background)
    # 通过textSize函数获取文字所占面积
    up_size_width,up_size_heigth = draw.textsize(uptext, font=font, spacing=6)
    down_size_width,down_size_heigth = draw.textsize(downtext, font=font, spacing=6)
    # 通过文字面积计算图片面积
    image_size = (down_size_width+130,down_size_heigth+up_size_heigth+30)
    # 修改背景文件大小并为背景赋值渐变色
    background = background.resize(image_size, Image.ANTIALIAS)
    color_list_one = [[255,255,255],[209,100,35],[138,3,0],[196,19,25],[12,4,2],[242,242,242],[162,181,188],[242,242,242],[255,255,255]]
    color_size_one = [10,40,10,25,10,42,42,10]
    background = make_change_color(background,color_list_one,color_size_one)
    # 配置文字图层,绘画透明的文字
    middle_layer = Image.new(mode = 'RGBA',size = image_size,color = 'white')
    draw = ImageDraw.Draw(middle_layer)
    draw.text((10,10), uptext, fill=(0,0,0,0), font=font)
    draw.text((110,up_size_heigth+10), downtext, fill=(0,0,0,0), font=font)
    # 给上半行文字添加一个宽度为2的绿色描边
    make_text_contur(draw,(10,10),uptext,font,(0,0,0,0),(0,255,0,255),2)
    # 文字图层和背景图层合并,文字图层在上,背景图层在下
    background = Image.alpha_composite(background, middle_layer)
    background.save(filename)

def make_text_contur(draw, pos, text, font, fill, border='black', abp=1):
    # 将文字向周边移动后描绘实现描边效果
    x, y = pos
    shadowcolor = border
    for bp in range(1,abp):
        draw.text((x-bp, y), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y), text, font=font, fill=shadowcolor)
        draw.text((x, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x, y+bp), text, font=font, fill=shadowcolor)
        draw.text((x-bp, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x-bp, y+bp), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y+bp), text, font=font, fill=shadowcolor)
    draw.text((x, y), text, font=font, fill=fill)


def make_change_color(image,color_list,color_size):
    # 对颜色进行演算并逐行填充到图片上实现渐变效果。
    image_size = image.size
    draw = ImageDraw.Draw(image)
    color_change_start = 0
    for color_flag in range(0,len(color_size)):
        color_start = color_list[color_flag]
        color_end = color_list[color_flag+1]
        color_length = color_size[color_flag]
        # 这里简化了运算,因为简化运算在部分情况下会造成图片发生颜色条纹
        color_step_R = int((color_start[0]-color_end[0])/color_length)
        color_step_G = int((color_start[1]-color_end[1])/color_length)
        color_step_B = int((color_start[2]-color_end[2])/color_length)
        for y in range(color_change_start,color_change_start+color_length):
            # 将步进参数赋值到颜色上
            color_fill_R = color_start[0] - color_step_R * (y-color_change_start)
            color_fill_G = color_start[1] - color_step_G * (y-color_change_start)
            color_fill_B = color_start[2] - color_step_B * (y-color_change_start)
            for x in range(0,image_size[0]):
                draw.point((x,y),fill = (color_fill_R,color_fill_G,color_fill_B))
        color_change_start = color_change_start + color_length;
    return image

if __name__ == "__main__":
    # execute only if run as a script
    make_text(['用Python生成','大鸟转转酒吧!'],'test.png')

执行完这步后,产品会如图所示:

至此,本程序中所有的函数都出现过了,后续的步骤就是慢慢调整图片的样式和描边等步骤。渐变色边框部分的方法基本上和主文字一致,首先生成一张边框的渐变色图片,之后将镂空的文字图片叠加到图层上,再在新的图片上描绘文字,镂空主文字后叠加。即可获得相应的效果。实际上整张图片是三层图片结构。

理解这张图片后再进行编码就不会很困难了。整个程序的全部代码如下

# coding=utf-8
# 引入必须的库文件
from PIL import Image,ImageDraw,ImageFont

def make_text(info,filename):
    # 获取文字信息
    uptext = info[0]
    downtext = info[1]
    # 设置字体,这里选择了更纱黑体,斜体,70号字
    font = ImageFont.truetype("sarasa-bolditalic.ttc", 70)
    # 新建一个背景文件,把Draw对象初始化
    background = Image.new(mode = 'RGBA',size = (500,500),color = 'white')
    draw = ImageDraw.Draw(background)
    # 通过textSize函数获取文字所占面积
    up_size_width,up_size_heigth = draw.textsize(uptext, font=font, spacing=6)
    down_size_width,down_size_heigth = draw.textsize(downtext, font=font, spacing=6)
    # 通过文字面积计算图片面积
    image_size = (down_size_width+130,down_size_heigth+up_size_heigth+30)
    # 修改背景文件大小并为背景赋值渐变色
    background = background.resize(image_size, Image.ANTIALIAS)
    color_list_one = [[255,255,255],[209,100,35],[138,3,0],[196,19,25],[12,4,2],[242,242,242],[162,181,188],[242,242,242],[255,255,255]]
    color_size_one = [10,40,10,25,10,42,42,10]
    background = make_change_color(background,color_list_one,color_size_one)
    # 配置描边渐变图层,设置描边渐变色
    contur_color_layer = Image.new(mode = 'RGBA',size = image_size,color = 'white')
    color_list_two = [[0,0,0],[64,53,54],[192,193,194],[49,60,60],[162,168,157],[127,113,116],[220,210,210],[0,0,0],[64,53,54],[192,193,194],[49,60,60],[162,168,157],[127,113,116],[220,210,210]]
    color_size_two = [10,15,20,25,25,17,10,10,15,20,25,25,17]
    draw = ImageDraw.Draw(contur_color_layer)
    make_change_color(contur_color_layer,color_list_two,color_size_two)
    # 配置文字图层,绘画透明的文字外边框
    middle_layer = Image.new(mode = 'RGBA',size = image_size,color = 'white')
    draw = ImageDraw.Draw(middle_layer)
    make_text_contur(draw,(11,11), uptext,font,(203,181,107,0),(219,209,216,0),5)
    make_text_contur(draw,(111,up_size_heigth+11), downtext,font,(203,181,107,0),(219,209,216,0),5)
    # 将透明的文字图层和渐变图层合并,文字图层在上,渐变图层在下
    middle_layer = Image.alpha_composite(contur_color_layer,middle_layer)
    # 绘画描边
    draw = ImageDraw.Draw(middle_layer)
    make_text_contur(draw,(9,11), uptext,font,(238,198,15,255),(238,198,15,255),3)
    make_text_contur(draw,(109,up_size_heigth+11), downtext,font,(71,73,85,255),(0,0,0,255),3)
    make_text_contur(draw,(109,up_size_heigth+11), downtext,font,(71,73,85,255),(71,73,85,255),1)
    # 将主要文字扣成透明色,准备和背景图层合并
    draw.text((10,10), uptext, fill=(0,0,0,0), font=font)
    draw.text((110,up_size_heigth+10), downtext, fill=(0,0,0,0), font=font)
    # 文字图层和背景图层合并,文字图层在上,背景图层在下
    background = Image.alpha_composite(background, middle_layer)
    background.save(filename)

def make_text_contur(draw, pos, text, font, fill, border='black', abp=1):
    # 将文字向周边移动后描绘实现描边效果
    x, y = pos
    shadowcolor = border
    for bp in range(1,abp):
        draw.text((x-bp, y), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y), text, font=font, fill=shadowcolor)
        draw.text((x, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x, y+bp), text, font=font, fill=shadowcolor)
        draw.text((x-bp, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y-bp), text, font=font, fill=shadowcolor)
        draw.text((x-bp, y+bp), text, font=font, fill=shadowcolor)
        draw.text((x+bp, y+bp), text, font=font, fill=shadowcolor)
    draw.text((x, y), text, font=font, fill=fill)

def make_change_color(image,color_list,color_size):
    # 对颜色进行演算并逐行填充到图片上实现渐变效果。
    image_size = image.size
    draw = ImageDraw.Draw(image)
    color_change_start = 0
    for color_flag in range(0,len(color_size)):
        color_start = color_list[color_flag]
        color_end = color_list[color_flag+1]
        color_length = color_size[color_flag]
        # 这里简化了运算,因为简化运算在部分情况下会造成图片发生颜色条纹
        color_step_R = int((color_start[0]-color_end[0])/color_length)
        color_step_G = int((color_start[1]-color_end[1])/color_length)
        color_step_B = int((color_start[2]-color_end[2])/color_length)
        for y in range(color_change_start,color_change_start+color_length):
            # 将颜色和步进参数结合运算出填充色
            color_fill_R = color_start[0] - color_step_R * (y-color_change_start)
            color_fill_G = color_start[1] - color_step_G * (y-color_change_start)
            color_fill_B = color_start[2] - color_step_B * (y-color_change_start)
            for x in range(0,image_size[0]):
                draw.point((x,y),fill = (color_fill_R,color_fill_G,color_fill_B))
        color_change_start = color_change_start + color_length;
    return image

if __name__ == "__main__":
    make_text(['用Python生成','大鸟转转酒吧!'],'test.png')

源代码和具体效果图可以点击按钮到github查看。欢迎star!

PS:如果需要透明背景图片的话,可以在保存图片前添加

    background = Image.alpha_composite(background, middle_layer)
    for x in range(0,image_size[0]):
        for y in range(0,image_size[1]):
            r,g,b,alpha = background.getpixel((x,y))
            if r==255 and g==255 and b==255:
                background.putpixel((x,y), (r,g,b,0))
    background.save(filename)

替换所有颜色值为255,255,255的图片的alpha通道为0,从而实现白色背景透明的效果。

本网站在未特殊说明的情况下采用知识共享署名-非商业性使用-相同方式共享 3.0 协议进行许可。
<-数据丢失->