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

imshow:
imshow result

imwrite:
enter image description here

my back.png
back.png

my cover.png
cover.png

Asked By: pigLoveRabbit520

||

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:

Output image

Answered By: Dan MaĊĦek
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.