如何使用PIL将一个透明的png图像与另一个图像合并?

241

我有一张透明的png图片foo.png,我使用以下代码打开了另一张图片:

im = Image.open("foo2.png")

现在我需要将foo.pngfoo2.png合并。

(foo.png包含一些文本,我希望将该文本打印在foo2.png上)


103
在Python中,不要在命令末尾使用;:它很丑陋。 - nosklo
9
我会记住的,谢谢! - Arackna
相关问题请参考此处 - pjpscriv
8个回答

491
from PIL import Image

background = Image.open("test1.png")
foreground = Image.open("test2.png")

background.paste(foreground, (0, 0), foreground)
background.show()

.paste()的第一个参数是要粘贴的图像,第二个是坐标,而秘密酱是第三个参数。它指示将用于粘贴图像的掩码。如果传递具有透明度的图像,则使用alpha通道作为掩码。

请查阅文档


15
为确保前景在所有情况下都包含透明度,请使用 foreground.convert('RGBA') 作为遮罩参数。 - Mark Ransom
3
谢谢。我太忘记了第三个参数。 - Silouane Gerin
23
我收到了一个“ValueError: bad transparency mask”的错误信息。 - Deniz Ozger
16
秘制酱汁味道不错。 - AFP_555
13
为了解决"ValueError: bad transparency mask"的问题,使用"bg.paste(fg, (0, 0), fg.convert('RGBA'))"。 - Mingwei Samuel
显示剩余11条评论

98

Image.paste 在背景图像包含透明度时的效果不如预期。您需要使用真正的Alpha Compositing

Pillow 2.0 包含一个名为 alpha_composite 的函数可以实现该功能。

background = Image.open("test1.png")
foreground = Image.open("test2.png")

Image.alpha_composite(background, foreground).save("test3.png")

编辑:两个图像都需要是RGBA类型。因此,如果它们是调色板等,则需要调用 convert('RGBA') 。如果背景没有alpha通道,则可以使用常规的paste方法(这应该更快)。


我刚刚使用PIL的paste()函数将一个半透明图像叠加到另一个图像上,效果和我预期的一样。那么它没有按照你的预期工作在哪个方面? - Peter Hansen
3
@PeterHansen,如果背景图像包含透明度,paste()函数的工作结果可能与预期不符。 - homm
1
@PeterHansen 这里有一个例子:https://github.com/python-pillow/Pillow/issues/924#issuecomment-61848826 - homm
4
我也遇到了“ValueError: image has wrong mode”的问题,@DenizOzger - digitaldavenyc
2
数值错误:图像不匹配。因为此方法仅在图像大小相同时才有效。 - Trect
显示剩余3条评论

87

正如 olt 已经指出的那样,当源和目标都包含alpha通道时,Image.paste 无法正常工作。

考虑下面的情况:

两个测试图像,都包含 alpha 通道:

enter image description here enter image description here

layer1 = Image.open("layer1.png")
layer2 = Image.open("layer2.png")

使用Image.paste合成图像的方法如下:

final1 = Image.new("RGBA", layer1.size)
final1.paste(layer1, (0,0), layer1)
final1.paste(layer2, (0,0), layer2)

使用Image.alpha_composite合成图像,会产生以下图像(红色覆盖的部分完全来自第二层的 alpha 通道。像素没有被正确混合):

enter image description here

示例代码如下:

final2 = Image.new("RGBA", layer1.size)
final2 = Image.alpha_composite(final2, layer1)
final2 = Image.alpha_composite(final2, layer2)

生成以下(正确的)图像:

enter image description here


4
谢谢提供截图!非常有帮助! - Viet
1
但是 alpha_composite 无法设置偏移量,您介意给出一个完全替换 paste 函数的示例吗? - Mithril
3
我想你需要创建一个与目标图像相同大小的新空白图像,在适当的位置粘贴图层并使用alpha_compositing将新图像混合到目标图像上。 - P.Melch
1
我得到了一个 ValueError 错误:图像不匹配。 - Student
2
图片需要具有相同的尺寸。 - P.Melch
非常感谢!它解决了我长期以来一直在处理的透明度问题。 - Ali Naderi

19

还可以使用混合:

im1 = Image.open("im1.png")
im2 = Image.open("im2.png")
blended = Image.blend(im1, im2, alpha=0.5)
blended.save("blended.png")

1
这个对我来说在美学上起作用了。图片必须具有完全相同的大小,但没关系。粘贴功能对我来说不太够用... - Liviu Sosu
3
数值错误:图像不匹配。 - Schütze
2
可能它们的尺寸不同。您可能需要缩放或裁剪其中一个。 - nvd
2
@Schütze,请查看nvd的评论,因为他/她没有使用 @blahblah 提及你。 - MilkyWay90
@Schütze 我不得不添加这些行以避免“图像不匹配”:cloud_as_img.convert("RGBA") cloud_as_img.putalpha(255) cloud_as_img.save(temp_file_name) - Jordan The Genius

3

这是我的代码,用于合并两张不同大小的图片,每张图片都有透明度和偏移量:

from PIL import Image

background = Image.open('image1.png')
foreground = Image.open("image2.png")

x = background.size[0]//2
y = background.size[1]//2

background = Image.alpha_composite(
    Image.new("RGBA", background.size),
    background.convert('RGBA')
)

background.paste(
    foreground,
    (x, y),
    foreground
)

background.show()

这段代码混合了之前的答案,将具有偏移量的元素与处理具有不同尺寸并带有透明度的图像相结合。


这个答案似乎不太正确。我提交了一个编辑,我认为它可以修复它 :) - pjpscriv

3

我曾经有类似的问题,并且很难找到答案。下面的函数允许您在特定偏移量处粘贴具有透明度参数的图像覆盖另一张图像。

import Image

def trans_paste(fg_img,bg_img,alpha=1.0,box=(0,0)):
    fg_img_trans = Image.new("RGBA",fg_img.size)
    fg_img_trans = Image.blend(fg_img_trans,fg_img,alpha)
    bg_img.paste(fg_img_trans,box,fg_img_trans)
    return bg_img

bg_img = Image.open("bg.png")
fg_img = Image.open("fg.png")
p = trans_paste(fg_img,bg_img,.7,(250,100))
p.show()

ValueError: images do not match - lllllllllllll

2
def trans_paste(bg_img,fg_img,box=(0,0)):
    fg_img_trans = Image.new("RGBA",bg_img.size)
    fg_img_trans.paste(fg_img,box,mask=fg_img)
    new_img = Image.alpha_composite(bg_img,fg_img_trans)
    return new_img

3
你好,你能否在回答中加入更多背景信息呢?否则请求者可能无法了解其背后的原因。 - jimf

1

the key code is:

_, _, _, alpha = image_element_copy.split()
image_bg_copy.paste(image_element_copy, box=(x0, y0, x1, y1), mask=alpha)

完整功能如下:
def paste_image(image_bg, image_element, cx, cy, w, h, rotate=0, h_flip=False):
    image_bg_copy = image_bg.copy()
    image_element_copy = image_element.copy()

    image_element_copy = image_element_copy.resize(size=(w, h))
    if h_flip:
        image_element_copy = image_element_copy.transpose(Image.FLIP_LEFT_RIGHT)
    image_element_copy = image_element_copy.rotate(rotate, expand=True)
    _, _, _, alpha = image_element_copy.split()
    # image_element_copy's width and height will change after rotation
    w = image_element_copy.width
    h = image_element_copy.height
    x0 = cx - w // 2
    y0 = cy - h // 2
    x1 = x0 + w
    y1 = y0 + h
    image_bg_copy.paste(image_element_copy, box=(x0, y0, x1, y1), mask=alpha)
    return image_bg_copy

上述函数支持以下功能:

  • 位置设置(cx, cy)
  • 自动调整图像元素大小至(w, h)
  • 旋转图像元素而不裁剪它
  • 水平翻转

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接