注意:由于我没有设置Android开发环境,因此我将解释一般原理并给你一个Python实现的示例。将其移植到 Java 应该相当简单。请随意将您的代码作为单独的答案发布。
你需要做一些类似于addWeighted
操作确实如此,那就是操作
然而,在你的情况下, α 需要是一个矩阵(即我们需要每个像素不同的混合系数)。
示例图片
让我们使用一些示例图像来说明这一点。我们可以使用 Lena 图像作为样本人脸:
该图像作为透明叠加:
该图像作为不透明的叠加层:
混合矩阵
为了获得alpha矩阵,我们可以使用阈值确定前景(覆盖)和背景(脸部)蒙版,或者使用输入图像中的 Alpha 通道(如果可用)。
在值范围为 0.0 .. 1.0 的浮点图像上执行此操作非常有用。然后我们可以将两个掩码之间的关系表示为
foreground_mask = 1.0 - background_mask
即两个掩码相加得到的结果都是掩码。
对于 RGBA 格式的叠加图像,我们得到以下前景和背景蒙版:
当我们在 RGB 格式的情况下使用阈值、侵蚀和模糊时,我们得到以下前景和背景蒙版:
加权和
现在我们可以计算两个加权部分:
foreground_part = overlay_image * foreground_mask
background_part = face_image * background_mask
对于 RGBA 叠加,前景和背景部分如下所示:
对于 RGB 叠加,前景和背景部分如下所示:
最后将它们相加,并将图像转换回 0-255 范围内的 8 位整数。
操作结果如下(分别为 RGBA 和 RGB 叠加):
代码示例 - RGB 叠加
import numpy as np
import cv2
# ==============================================================================
def blend_non_transparent(face_img, overlay_img):
# Let's find a mask covering all the non-black (foreground) pixels
# NB: We need to do this on grayscale version of the image
gray_overlay = cv2.cvtColor(overlay_img, cv2.COLOR_BGR2GRAY)
overlay_mask = cv2.threshold(gray_overlay, 1, 255, cv2.THRESH_BINARY)[1]
# Let's shrink and blur it a little to make the transitions smoother...
overlay_mask = cv2.erode(overlay_mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)))
overlay_mask = cv2.blur(overlay_mask, (3, 3))
# And the inverse mask, that covers all the black (background) pixels
background_mask = 255 - overlay_mask
# Turn the masks into three channel, so we can use them as weights
overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)
# Create a masked out face image, and masked out overlay
# We convert the images to floating point in range 0.0 - 1.0
face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))
# And finally just add them together, and rescale it back to an 8bit integer image
return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))
# ==============================================================================
# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_img = cv2.imread("overlay.png", -1)
result_1 = blend_non_transparent(face_img, overlay_img)
cv2.imwrite("merged.png", result_1)
代码示例 - RGBA 叠加
import numpy as np
import cv2
# ==============================================================================
def blend_transparent(face_img, overlay_t_img):
# Split out the transparency mask from the colour info
overlay_img = overlay_t_img[:,:,:3] # Grab the BRG planes
overlay_mask = overlay_t_img[:,:,3:] # And the alpha plane
# Again calculate the inverse mask
background_mask = 255 - overlay_mask
# Turn the masks into three channel, so we can use them as weights
overlay_mask = cv2.cvtColor(overlay_mask, cv2.COLOR_GRAY2BGR)
background_mask = cv2.cvtColor(background_mask, cv2.COLOR_GRAY2BGR)
# Create a masked out face image, and masked out overlay
# We convert the images to floating point in range 0.0 - 1.0
face_part = (face_img * (1 / 255.0)) * (background_mask * (1 / 255.0))
overlay_part = (overlay_img * (1 / 255.0)) * (overlay_mask * (1 / 255.0))
# And finally just add them together, and rescale it back to an 8bit integer image
return np.uint8(cv2.addWeighted(face_part, 255.0, overlay_part, 255.0, 0.0))
# ==============================================================================
# We load the images
face_img = cv2.imread("lena.png", -1)
overlay_t_img = cv2.imread("overlay_transparent.png", -1) # Load with transparency
result_2 = blend_transparent(face_img, overlay_t_img)
cv2.imwrite("merged_transparent.png", result_2)