【问题标题】:PIL merge of two images with alpha channels - not working as expectedPIL 合并两个带有 alpha 通道的图像 - 没有按预期工作
【发布时间】:2012-12-10 23:06:23
【问题描述】:

这里有很多关于 SO 的问题提供了当前问题的答案,但是输出不是预期的。

目标是合并两个 RGBA 图像。每张图片的alpha通道信息都不一样。

当前(简化的)代码是:

from PIL import Image

image = '1.png'
watermark = '2.png'

wmark = Image.open(watermark)
img = Image.open(image)

img.paste(wmark, (0, 0), wmark)
img.save("result.png", "PNG")

这两张图片是:

背景

前景

预期输出

实际结果

如果您没有看到差异,这里是最终版本的 Alpha 通道(为了更好的可视化而倒置)。

预期结果 - Alpha 通道

实际结果 - alpha 通道

话虽如此,有什么办法可以做到这一点还是我做错了什么?

编辑 - @zenpoy 评论后的澄清:

如果前景图像有一定的不透明度,我希望在叠加两张图像时考虑到这一点,但我不希望将第二张图像的 Alpha 通道添加到第一张图像中。就像将一块玻璃(前景图像)放在纸质图像(背景)前面一样。

换句话说,如果背景图像是 RGB 而不是 RGBA,那么最终图像应该没有 alpha 信息。

【问题讨论】:

  • 您能提供一个数学解释来说明您要达到的目标吗?您对每个通道中每个像素的值有何期望?
  • 我一直对 PIL 无法正确处理 alpha 感到失望。

标签: python python-imaging-library


【解决方案1】:

从您最初的描述来看,以下想法似乎是等效的。设 X, Y 为两个 RGBA 图像。考虑 X 中的 RGB 波段和 Y 中的 RGBA 波段,合并 X 和 Y,生成图像 Z。将 Z 中的波段 A 设置为 X 中的波段 A。这与您的最终陈述相矛盾,但它似乎给出了预期在这种情况下输出。

所以,这是代码:

image = '1.png'
watermark = '2.png'

wmark = Image.open(watermark)
img = Image.open(image)

ia, wa = None, None
if len(img.getbands()) == 4:
    ir, ig, ib, ia = img.split()
    img = Image.merge('RGB', (ir, ig, ib))
if len(wmark.getbands()) == 4:
    wa = wmark.split()[-1]

img.paste(wmark, (0, 0), wmark)
if ia:
    if wa:
        # XXX This seems to solve the contradiction, discard if unwanted.
        ia = max_alpha(wa, ia)
    img.putalpha(ia)

img.save('result.png')

函数max_alpha在哪里:

def max_alpha(a, b):
    # Assumption: 'a' and 'b' are of same size
    im_a = a.load()
    im_b = b.load()
    width, height = a.size

    alpha = Image.new('L', (width, height))
    im = alpha.load()
    for x in xrange(width):
        for y in xrange(height):
            im[x, y] = max(im_a[x, y], im_b[x, y])
    return alpha

这个新功能似乎考虑到了上面提到的矛盾。

【讨论】:

  • 您使用的是哪个版本的 PIL?我这里有 1.1.7。至少对于本示例中提供的图像,您的代码在 img.split() 上失败,/usr/lib64/python2.7/site-packages/PIL/Image.py", line 1497 上出现内部 PIL 错误 AttributeError: 'NoneType' object has no attribute 'bands'
  • 我也有 1.1.7,这有点奇怪。尝试在img = Image.open(image) 行之后添加img.load()
  • 请注意,如果图像没有 alpha 通道,代码将在 img.split() 行上失败。替代方案可能是channels = img.split()img = Image.merge('RGB', channels[:3])if len(channels) == 4: img.putalpha(channel[3])。 PS:谢谢,输出仍然不是预期的,但足够接近(实际上更好)。在预期的结果中,十字交叉点的 alpha 值较弱,就好像减去了不透明度一样。您的建议在该地区保持 alpha 不变。
  • 很好,我已经更新了这个例子,让它更加健壮一些。我添加了一个新功能max_alpha,这似乎是你想要的,你能检查一下吗?谢谢。
猜你喜欢
  • 2018-09-19
  • 2019-06-12
  • 1970-01-01
  • 2018-01-02
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多