I just want to cover an png image with another png image, cv2.imshow
got the right result, cv2.imwrite
got the strange result.
coverImg = cv2.imread('./images/cover.png', cv2.IMREAD_UNCHANGED)
back = cv2.imread('./images/back.png', cv2.IMREAD_UNCHANGED)
x_offset = y_offset = 0
y1, y2 = y_offset, y_offset coverImg.shape[0]
x1, x2 = x_offset, x_offset coverImg.shape[1]
alpha_s = coverImg[:, :, 3] / 255.0
alpha_l = 1.0 - alpha_s
result = back.copy()
for c in range(0, 3):
result[y1:y2, x1:x2, c] = (alpha_s * coverImg[y1:y2, x1:x2, c]
alpha_l * result[y1:y2, x1:x2, c])
cv2.imshow("result", result)
res2 = cv2.imwrite("./result.png", result)
result.dtype
is uint8
CodePudding user response:
The problem occurs because you're modifying a copy of the original background image, which you loaded as BGRA, but do not modify the alpha channel on the result. Since the background image is mostly transparent (other than the shadows), so is the result when viewed by something that supports alpha.
To fix this and keep the result partially transparent (where appropriate), you need to merge the alpha channels as well. Since alpha=0
means fully transparent, alpha=255
means fully opaque, and our goal is to retain the opaque parts of both images, let's take max(foreground_alpha, background_alpha)
for each pixel. This can be accomplished using
CodePudding user response:
Your arrays are of floating point type because you use division.
imshow
and imread
treat float arrays differently.
imshow
assumes the brightness to be ranging from 0.0 to 1.0, if the data is float (0 to 255 for uint8).
imwrite
just converts to uint8
, so the range still needs to be 0 to 255 for floats.
You should convert your float data to uint8
using .astype(np.uint8)
You also need to mind the transparency channel. imshow
ignores that, doesn't "show" its effects. It will show you the color of pixels that are marked as transparent.