基于Python的Pillow库实现封面自动生成,包含水平渐变背景、半透明装饰图形(圆环、三角形)、半透明遮罩层及文字层,支持主副标题及自定义比例,输出PNG格式图片。
写博客时,封面图制作常耗费大量时间。专栏封面需要1:1比例,文章封面推荐16:9比例,网络上找到的图片尺寸大多不符合要求,还需要进入Photoshop或Illustrator中手动裁剪、添加文字。重复劳动过多,因此决定使用Python自动化生成封面。为简化流程,封面背景不直接使用图片,而是采用渐变填充,同时在左下角和右上角加入半透明装饰图形,使封面具备一定设计感,避免过于单调。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
内置4种渐变背景,用户可根据需求方便扩充。
内置4种边缘装饰图形:圆环、三角形、正方形、六边形。
文字支持两种方案:
整个实现过程可拆解为四个核心步骤,下面逐一说明。

首先创建一个封装类 BlogCoverGenerator,在 __init__ 方法中定义所需属性,再编写生成封面的核心方法。需要注意,后续操作频繁使用透明度,因此基础图形的 mode 需设置为 RGBA,生成的图片格式必须为 PNG。
class BlogCoverGenerator:
def __init__(self,
title: str,
sub_title: str,
title_h_ratio: float=.5,
ratio_pair: tuple[int]=(16, 9),
bg_gradient: BackgroundGradient=BackgroundGradient.skyline,
bg_shape: BackgroundShape=BackgroundShape.circle,
min_size='1M'):
# 封面主标题
self.title = title
# 封面副标题
self.sub_title = sub_title
# 如果没有副标题,主标题需要垂直居中
self.no_sub_title = False
if self.sub_title is None or '' == self.sub_title.strip():
self.no_sub_title = True
# 标题区域垂直方向占比(从正中心开始计算),参考值0.3~0.6
self.title_h_ratio = title_h_ratio
if self.title_h_ratio < 0.3:
self.title_h_ratio = 0.3
elif self.title_h_ratio > 0.6:
self.title_h_ratio = 0.6
# 字体位置
self.zh_font_location = ''
self.en_font_location = ''
# 封面宽高比,16:9, 4:3, 1:1等,以16:9为例,需要传入(16, 9)
self.ratio_pair = ratio_pair
# 封面渐变背景色
self.bg_gradient = bg_gradient
# 封面背景几何图形,目前支持三角形、六边形、圆形
self.bg_shape = bg_shape
# 封面大小。如果传入字符串,支持的单位为k, M;也可以传入数值
self.min_size = self._parse_min_size(min_size)
if self.min_size > 178956970:
# ImageDraw.text大小限制
self.min_size = 178956970
def generate_cover(self, output_path: str, output_file_name: str):
width, height = self._get_cover_size()
img = Image.new('RGBA', (width, height))
# 第1层,渐变背景
self._display_gradient_bg(img)
# 第2层,边缘装饰图形
img = self._display_decorate_shape(img)
# 第3层,半透明遮罩
img = self._display_transparent_mask(img)
# 第4层,文字
img = self._display_title(img)
# 保存
img.sa ve(os.path.join(output_path, output_file_name))
首先定义一个渐变枚举,将几种配色方案列出。
from enum import Enum
class BackgroundGradient(Enum):
skyline = ['#1488CC', '#2B32B2']
cool_brown = ['#603813', '#b29f94']
rose_water = ['#E55D87', '#5FC3E4']
crystal_clear = ['#159957', '#155799']
Pillow 官方文档中未提供直接绘制渐变的方法,此处仅实现水平方向渐变。实现思路是:在起始颜色和结束颜色之间按宽度计算步长,通过循环逐行绘制不同颜色的垂直线条。代码如下:
class BlogCoverGenerator:
def _display_gradient_bg(self, base_img: Image):
img_w, img_h = base_img.size
draw = ImageDraw.Draw(base_img)
start_color, end_color = self.bg_gradient.value
if '#' in start_color:
start_color = ImageColor.getrgb(start_color)
if '#' in end_color:
end_color = ImageColor.getrgb(end_color)
# 水平方向渐变,渐变步长
step_r = (end_color[0] - start_color[0]) / img_w
step_g = (end_color[1] - start_color[1]) / img_w
step_b = (end_color[2] - start_color[2]) / img_w
for i in range(0, img_w):
bg_r = round(start_color[0] + step_r * i)
bg_g = round(start_color[1] + step_g * i)
bg_b = round(start_color[2] + step_b * i)
draw.line([(i, 0), (i, img_h)], fill=(bg_r, bg_g, bg_b))
这一步的效果图如下:

实现代码较多,这里主要说明思路。
先定义一个装饰图形枚举:
from enum import Enum
class BackgroundShape(Enum):
circle = 1
triangle = 2
square = 3
hexagon = 4
装饰图形需要具备透明度,因此使用 Image.alpha_composite() 将半透明图形混合到渐变背景上。
需要注意的是,文字区域为核心区域,装饰图形不能覆盖它。文字区域的控制参数为 title_h_ratio。
Pillow 的 ImageDraw 类提供了 regular_polygon() 方法用于绘制正多边形,但该方法不支持设置轮廓宽度(默认值为1)。而我们需要指定轮廓宽度。如果使用 polygon() 自行计算顶点坐标,遇到旋转多边形时操作较为复杂。因此这里采用一个变通方法:
BlogCoverGenerator
@staticmethod
def _width_regular_polygon(draw: ImageDraw, width: int,
bounding_circle, n_sides, rotation=0, fill=None, outline=None):
"""
pillow提供的regular_polygon,不支持对outline设置width,自定义方法,支持轮廓宽度
"""
start = bounding_circle[2]
for i in np.arange(0, width, 0.05):
new_bounding_circle = (bounding_circle[0], bounding_circle[1], start + i)
draw.regular_polygon(bounding_circle=new_bounding_circle, n_sides=n_sides,
rotation=rotation, fill=fill, outline=outline)
这里的 bounding_circle 是元组 (x, y, r),表示多边形的外切圆,包含圆心坐标和半径。绘制圆环时使用的 ImageDraw.ellipse() 需要左上角和右下角坐标,可转换为 bounding_circle 的形式,从而复用位置参数。
这一步的效果图(以圆环为例):

这一步较为简单,直接查看代码:
class BlogCoverGenerator:
@staticmethod
def _display_transparent_mask(base_img: Image):
img_w, img_h = base_img.size
img_mask = Image.new('RGBA', (img_w, img_h), color=(0, 0, 0, 135))
return Image.alpha_composite(base_img, img_mask)
这里有两个关键点。第一,需要根据标题文字判断是中文还是英文,选择对应的字体。
class BlogCoverGenerator:
def _get_real_font(self, target_title: str, font_size: int):
for ch in target_title:
if u'u4e00' <= ch <= u'u9fff':
# 中文字体
return ImageFont.truetype(self.zh_font_location, font_size)
# 英文字体
return ImageFont.truetype(self.en_font_location, font_size)
def set_fonts(self, zh_font_location: str, en_font_location: str):
self.zh_font_location = zh_font_location
self.en_font_location = en_font_location
第二,字体大小需要动态调整。这里采用预渲染并检查字体宽度的方法,使用 ImageFont.getbbox()。
class BlogCoverGenerator:
def _display_title(self, base_img: Image):
def get_checked_font_size(target_title: str, font_size: int, max_width: int):
# 预检查,判断文字宽度是否超出封面
check_font = self._get_real_font(target_title, font_size)
_, _, check_w, check_h = check_font.getbbox(target_title)
if check_w > max_width:
scale_ratio = max_width / check_w
font_size = int(font_size * scale_ratio)
return font_size
最终效果图如下:

以上为使用Python自动生成带装饰图形的渐变背景文字封面的完整实现,思路清晰,代码易于扩展,感兴趣可直接使用。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述