Pillow 绘制时为什么指定的 alpha 渐变不符合预期?
结论
这段代码有两个容易混淆的地方:圆弧远离缺口时,alpha 公式会把透明度截断为 0;而 alpha 只控制透明度,不会直接让圆弧的 RGB 颜色渐变为背景色。若要让不透明的圆弧颜色渐变到背景色,可以按比例混合两种颜色。若要使用透明度,则应先在单独的透明图层上绘制,再将图层合成到背景图上。
问题中给出的背景值是 (0, 0, 0, 255),即不透明黑色。代码注释称它是灰色,但注释与实际 RGB 值不一致。问题没有附上“Gives me:”后面的结果截图,所以下面的判断只依据提供的代码。
alpha 公式为何与注释不符
代码使用:
alpha_ratio = 1.0 - (1.5 * (diff / math.pi))
当 diff == 0 时,alpha 为 255。当 diff == math.pi 时,alpha_ratio 为 -0.5,随后会被 max(..., 0) 限制为 0。因此,最远处的 alpha 不会像注释所说的那样约为 127。从 diff >= 2 * math.pi / 3 开始,alpha 就会被限制为 0。
如果只想让 alpha 从 255 线性降到约 127,可以把系数改为 0.5:
alpha_ratio = 1.0 - 0.5 * (diff / math.pi)
alpha = int(255 * alpha_ratio)
这只会改变透明度,不会直接让 RGB 颜色渐变到背景色。
让圆弧颜色渐变到背景色
如果最终图像需要保持不透明,可以根据 diff 计算混合比例,再逐个混合圆弧色和背景色。下面的例子会让颜色在 diff 从 gap_half_width 增加到 math.pi 时逐渐接近黑色背景:
bg_rgb = (0, 0, 0)
fade = (diff - gap_half_width) / (math.pi - gap_half_width)
fade = max(0.0, min(fade, 1.0))
rgb = tuple(
round(base_color[channel] * (1.0 - fade) + bg_rgb[channel] * fade)
for channel in range(3)
)
draw.ellipse(
[x - thickness, y - thickness, x + thickness, y + thickness],
fill=(*rgb, 255),
outline=None
)
这里的颜色会从 base_color 逐渐变为 bg_rgb,绘制时始终保持 255 不透明度。背景不是黑色时,把 bg_rgb 改为实际的背景 RGB 值即可。代码中的 (0, 0, 0, 255) 实际上是黑色;灰色背景则需要使用三个相同且大于零的 RGB 分量。
如果需要保留 alpha 透明度
可以先在透明图层上绘制圆弧,再将图层合成到不透明背景上:
background = Image.new("RGBA", (size, size), (0, 0, 0, 255))
overlay = Image.new("RGBA", (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(overlay)
# 在 overlay 上绘制圆弧,fill 中使用计算出的 alpha
# draw.ellipse(..., fill=(*base_color, alpha))
img = Image.alpha_composite(background, overlay)
合成到背景时,透明度会与背景混合。检查结果时,也可以将 img 保存为 PNG。查看器显示透明区域的方式可能受其背景显示设置影响。
注意事项
alpha=255表示完全不透明,alpha=0表示完全透明。- 需要将颜色淡化到背景色时,直接混合 RGB 通道更明确;需要透出底层图像时,则使用 alpha 并进行合成。
- 修改 alpha 公式中的系数只会改变透明度曲线,不能单独实现 RGB 颜色渐变。
备注:内容仅供参考。
