我先放代码和结果,再分析过程。
1.实现代码
# coding=utf-8
import numpy as np
import cv2 as cv
def paste_ROI_to_image(image_in, ROI, paste_area):
image = image_in.copy()
y1, x1, y2, x2 = paste_area
ROI = cv.resize(ROI, (x2 - x1 + 1, y2 - y1 +1)) #cv.resize(src, dsize=(width, height))
image = image.astype(np.float)
ROI = ROI.astype(np.float)
# alpha通道
alpha_image = image[y1:y2+1, x1:x2+1, 3]/255.0
alpha_ROI = ROI[:,:,3]/255.0
alpha = 1 - (1 - alpha_image)*(1 - alpha_ROI)
# BGR通道
for i in range(3):
image[y1:y2+1, x1:x2+1, i] = (image[y1:y2+1, x1:x2+1, i]*alpha_image*(1 - alpha_ROI) + ROI[:,:,i]*alpha_ROI) / alpha
image[y1:y2+1, x1:x2+1, 3] = alpha*255
image = image.astype(np.uint8)
return image
image = cv.imread('image.jpg', cv.IMREAD_UNCHANGED) #
ROI = cv.imread('ROI.png', cv.IMREAD_UNCHANGED)
image_R = paste_ROI_to_image(image, ROI, [50, 0, 206, 117])
cv.imwrite('image_R.png', image_R)
结果:
image ROI image_R
代码分析:
(1)先导入使用到的两个模块:numpy和Python-OpenCV(cv2)
(2)定义了一个函数paste_ROI_to_image来完成透明素材的粘贴。函数中,先使用cv.resize函数来调整素材ROI的尺寸,使其和粘贴区域的尺寸是匹配的。x表示图像的列数,y表示图像中的行数。
(3)粘贴透明素材。(原理在后面解释)
(4)显示粘贴结果。
2.粘贴透明素材的原理
首先,透明素材ROI具有透明度是因为它除了有RGB三个颜色通道以外,还有一个alpha通道。包含alpha通道的图像一般存储为png格式。(jpg格式图像是没有alpha通道的,比如程序中的image.jpg图就只有RGB三个颜色通道)我们粘贴透明图像时就利用到了这个透明通道。
使粘贴的效果能够保持透明的效果,其实就是让image和ROI的RGB三个通道进行混合。
那么,怎么混合呢?混合的比例如何知道呢?
alpha通道的值不就给我们一个值吗?alpha通道的值取值范围是0-255。0表示完全透明,255表示完全不透明。
(1)alpha通道:
setp1: 将alpha通道值取值范围由0-255转换到0-1
alpha_image = image[y1:y2+1, x1:x2+1, 3]/255.0
alpha1_ROI = ROI[:,:,3]/255.0
setp2: 计算合成后的图像的透明度:
alpha = 1 - (1 - alpha_image)*(1 - alpha_ROI)
这个公式可以这么理解,剩余透明度(1 - alpha_image) 和(1 - alpha_ROI)混合后,得到的图像透明度。
(2)RGB通道:
image[R,G,B] = (image[R,G,B]*alpha_image*(1 - alpha_ROI) + ROI[R,G,B]*alpha_ROI) / alpha
这个公式怎么理解呢?其实就是按照透明度来分配image和ROI的RGB混合比例。
ROI是上层,其颜色的透出部分为 ROI[R,G,B]*alpha_ROI,然后按照最后合成图像的透明度分配比例,则ROI提供的RGB值为:
ROI[R,G,B]*alpha_ROI) / alpha
image是底层,其颜色的透出部分为image[R,G,B]*alpha_image,而由于上层的ROI透过了alpha_ROI,留给image的只有(1 - alpha_ROI), 所以image提供的RGB值为:
image[R,G,B]*alpha_image*(1 - alpha_ROI) / alpha
(3)合并alpha通道和RGB通道
image[y1:y2+1, x1:x2+1, 3] = alpha*255
image = image.astype(np.uint8)