应群友需求,近期使用Python3和Pillow制作了一个梗图的生成器,具体生成的产品大概就是这样:
把代码现在记录一下。
首先,搭建一个代码框架。这个框架引入了必须的类还有一些字体编码设置,防止后续过程中出现文本问题等。
1 2 3 4 5 6 7 8 9 10 11 |
# 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中。
接下来填充第一段方法,在此先不考虑具体的变色如何实现,只是将文字直接填充到图片上导出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 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来表示。另外,渐变色的生成,我们可以用(初始颜色-结束颜色)/渐变长度来计算出图片的渐变步进,之后初始颜色-渐变步进*渐变长度获得某一特定像素点的颜色值。所以代码接下来这样改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# 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相差较大。误差就是这样诞生的。
接下来,对文字开始进行描边。在这里采用的方法是给文字在每个方向移动相应的像素后描绘,产生渐变的效果。缺点是在文字的边角处会产生一些生硬的拐角,另外会导致多次重复描绘,影响程序效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# 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') |
执行完这步后,产品会如图所示:
至此,本程序中所有的函数都出现过了,后续的步骤就是慢慢调整图片的样式和描边等步骤。渐变色边框部分的方法基本上和主文字一致,首先生成一张边框的渐变色图片,之后将镂空的文字图片叠加到图层上,再在新的图片上描绘文字,镂空主文字后叠加。即可获得相应的效果。实际上整张图片是三层图片结构。
理解这张图片后再进行编码就不会很困难了。整个程序的全部代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# 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:如果需要透明背景图片的话,可以在保存图片前添加
1 2 3 4 5 6 7 |
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,从而实现白色背景透明的效果。