PAYATHON 2026

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 颜色渐变。

备注:内容仅供参考。