Opencv imshow is right whereas imwrite is wrong?
Question:
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
Answers:
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 np.maximum
:
result[y1:y2, x1:x2, 3] = np.maximum(coverImg[y1:y2, x1:x2, 3], back[y1:y2, x1:x2, 3])
The full script (with added imports and call to cv2.waitKey
):
import cv2
import numpy as np
coverImg = cv2.imread('front.png', cv2.IMREAD_UNCHANGED)
back = cv2.imread('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])
result[y1:y2, x1:x2, 3] = np.maximum(coverImg[y1:y2, x1:x2, 3], back[y1:y2, x1:x2, 3])
cv2.imshow("result", result)
cv2.waitKey()
res2 = cv2.imwrite("result.png", result)
This produces:
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
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 np.maximum
:
result[y1:y2, x1:x2, 3] = np.maximum(coverImg[y1:y2, x1:x2, 3], back[y1:y2, x1:x2, 3])
The full script (with added imports and call to cv2.waitKey
):
import cv2
import numpy as np
coverImg = cv2.imread('front.png', cv2.IMREAD_UNCHANGED)
back = cv2.imread('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])
result[y1:y2, x1:x2, 3] = np.maximum(coverImg[y1:y2, x1:x2, 3], back[y1:y2, x1:x2, 3])
cv2.imshow("result", result)
cv2.waitKey()
res2 = cv2.imwrite("result.png", result)
This produces: