Alpha combinando duas imagens com OpenCV e / ou Numpy
Gostaria de adicionar um retângulo semitransparente preenchido com uma cor sólida a um PNG semitransparente já carregado. Aqui está um exemplo de imagem de entrada que estou usando:
Essa imagem é carregada com um cv2.IMREAD_UNCHANGEDsinalizador padrão para que o canal alfa seja perfeitamente preservado. Essa imagem de entrada é armazenada na imagevariável.
Este é o meu código que tenho até agora:
# 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))
E aqui está o resultado que estou obtendo:
À primeira vista, parece aceitável . Temos nossa imagem de entrada com um retângulo semitransparente no meio. No entanto, após uma inspeção mais detalhada, podemos observar um comportamento estranho ao misturar canais alfa (marcados com setas):
Parece que alfa não é misturado, o que resulta em pixels da imagem original sendo totalmente opacos ou totalmente transparentes.
Talvez meu método de mesclar PNG transparente com formas semitransparentes esteja longe do ideal. Como observação lateral, tentei o cv2.addWeightedmétodo, mas produziu resultados ainda piores.
Gostaria que a solução se limitasse a OpenCV e / ou Numpy. Qualquer ajuda seria muito apreciada.
Respostas
Como não foi apontado, esta é de fato uma duplicata. Apenas no caso de alguém tropeçar nisso, a resposta de Mark Setchell funciona muito bem:
# 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)