使用OpenCV和/或Numpy进行Alpha混合两个图像

11

我想在已经加载的半透明PNG图片上添加一个填充了纯色的半透明矩形。这是我使用的示例输入图像:

enter image description here

该图像使用标准的cv2.IMREAD_UNCHANGED标志加载,以便完全保留 Alpha 通道。该输入图像存储在 image 变量中。

以下是迄今为止我所编写的代码:

# get image dimensions
imgHeight, imgWidth = image.shape[:2]

# create empty overlay layer with 4 channels
overlay = np.zeros((imgHeight, imgWidth, 4), dtype = "uint8")

# draw semi-transparent red rectangle
overlay[200:300, 0:imgWidth] = (0, 0, 255, 200)

# extract alpha channel from overlay
alpha = cv2.split(overlay)[3]

# compute mask
mask = (np.multiply(alpha, 1.0 / 255))[:, :, np.newaxis]

# blend input image and overlay
output = cv2.convertScaleAbs(overlay * mask + image * (1 - mask))

这是我得到的结果:

enter image description here

乍一看它看起来还可以接受。我们在输入图像中有一个半透明矩形。然而,仔细观察后,我们可以观察到 alpha 通道混合时的奇怪行为(用箭头标记):

enter image description here

似乎完全没有混合 alpha,这导致原始图像像素只能是完全不透明或完全透明。

也许我的方法并不理想,用于将透明的 PNG 图像与半透明的形状混合。顺便说一下,我确实尝试过 cv2.addWeighted 方法,但结果更糟糕了。

我希望解决方案局限于 OpenCV 和/或 Numpy。非常感谢任何帮助。


我只希望我能授予Mark Setchell你链接答案的赏金。 - Pono
2个回答

10

正如unlut所指出的那样,这确实是一个重复问题。以防有人偶然发现它,Mark Setchell的答案效果还不错:

# get image dimensions
imgHeight, imgWidth = image.shape[:2]

# create empty overlay layer with 4 channels
overlay = np.zeros((imgHeight, imgWidth, 4), dtype = "uint8")

# draw semi-transparent red rectangle
overlay[200:300, 0:imgWidth] = (0, 0, 255, 200)

# Extract the RGB channels
srcRGB = image[...,:3]
dstRGB = overlay[...,:3]

# Extract the alpha channels and normalise to range 0..1
srcA = image[...,3]/255.0
dstA = overlay[...,3]/255.0

# Work out resultant alpha channel
outA = srcA + dstA*(1-srcA)

# Work out resultant RGB
outRGB = (srcRGB*srcA[...,np.newaxis] + dstRGB*dstA[...,np.newaxis]*(1-srcA[...,np.newaxis])) / outA[...,np.newaxis]

# Merge RGB and alpha (scaled back up to 0..255) back into single image
outRGBA = np.dstack((outRGB,outA*255)).astype(np.uint8)

plt.imshow(outRGBA)

不确定为什么,但是被注释为“#计算结果RGB:outRGB =(srcRGB * srcA ...”的那一行会抛出“RuntimeWarning:invalid value encountered in true_divide”的警告。 - Pono
@Pono,你的outA变量里可能有一些零值(或NaN)。 - unlut

0

使用alpha混合的简单工作: https://learnopencv.com/alpha-blending-using-opencv-cpp-python/

import cv2

# Read the images
foreground = cv2.imread("puppets.png")
background = cv2.imread("ocean.png")
alpha = cv2.imread("puppets_alpha.png")

# Convert uint8 to float
foreground = foreground.astype(float)
background = background.astype(float)

# Normalize the alpha mask to keep intensity between 0 and 1
alpha = alpha.astype(float)/255

# Multiply the foreground with the alpha matte
foreground = cv2.multiply(alpha, foreground)

# Multiply the background with ( 1 - alpha )
background = cv2.multiply(1.0 - alpha, background)

# Add the masked foreground and background.
outImage = cv2.add(foreground, background)

# Display image
cv2.imshow("outImg", outImage/255)
cv2.waitKey(0)

太完美了!唯一一个使用简单快速方法正确地完成任务的。我已经修改了它,现在它按照预期运行。 - Softlion

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