OpenCV-Python教程:图像的加法运算(add,addWeighted)

返回Opencv-Python教程

原文链接:http://www.juzicode.com/archives/5503

1、图像相加add()

2个图像对象img,img2如果通道数一样、大小一样,可以使用cv2.add(img,img2)相加,得到一个新的图像。

下面的例子分别读取lena.jpg和opencv-logo.png,因为默认方式读取得到的图像都是3通道,然后截取行列大小一样的部分图像,再用add()方法将2个图像相加:

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

img = cv2.imread('..\\lena.jpg')[0:512,0:512] #截取部分,保证大小一致
img2 = cv2.imread('..\\opencv-logo.png' )[0:512,0:512]

img3 = cv2.add(img,img2)
cv2.imshow('add',img3)
cv2.waitKey(0)

运行结果:

从运行的结果看,2个图像中白色部分会覆盖了原来的位置,那是因为add()方法相加的图像如果超过了阈值会被截断。比如某个像素点在图像1中的值为uint8类型的(200,200,200),而该像素点在图像2中的值为(127,127,127),使用add()方法相加后该像素点的值3个通道都超过了255,就会被截断到255,所以最后图像中的值就为(255,255,255)的纯白色。

我们可以将像素值打印出来验证下,这里提取位置为[161,199]像素值,对比add()方法的值和直接计算相加后的值:

import cv2
img = cv2.imread('..\lena.jpg')[0:512,0:512] #截取部分,保证大小一致
img2 = cv2.imread('..\opencv-logo.png' )[0:512,0:512]
img3 = cv2.add(img,img2)
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])

运行结果:

img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [109 105 255]

从上面运行结果可以看到在[161,199]像素点img和img2如果直接相加的和应该为[ 109 105 456],但是其中的R通道相加后大于255,在这里被截断到255,所以最后的结果为[ 109 105 255]。

在OpenCV中图像的算术运算遵循“饱和运算”的规则,如果计算的结果超过了阈值范围,则就近进行截断,比如unit8类型的数据范围是【0,255】,如果2个数值直接相加的结果大于255就会赋值为255,在后面介绍图像减法时也会看到如果2个数值直接相减的结果小于0就会赋值为0

2、图像相加 运算符+

另外也可以通过 + 号的方法将2个图像相加:

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

img = cv2.imread('..\\lena.jpg')[0:512,0:512] #截取部分,保证大小一致
img2 = cv2.imread('..\\opencv-logo.png' )[0:512,0:512]
img3 = img+img2
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])
cv2.imshow('add+',img3)
cv2.waitKey(0)

运行结果:

VX公众号: 桔子code / juzicode.com
cv2.version: 4.5.2
img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [109 105 200]

 

从运行结果看,+运算符将2个图像相加,新图像和lena.jpg差异不大,但是和opencv-logo.png相差甚远。从像素[161,199]的运算结果看,当2个值相加大于255时,会将这个值对256求余得到新的像素值

为什么用符号+运算后的结果会对256求模?

因为在这个例子里imread()默认方式读出的图像,返回的是一个numpy数组,而这个numpy数组的数据类型是unit8,对应C语言里unit8的类型实际就是unsigned char类型,在C语言中unsigned char类型的数据表示的范围是[0,255]的闭区间,如果直接相加的结果大于255就会出现“溢出”,相对于对256进行求模。下面是用C语言对unsigned char类型的数据进行加减运算的例子:

//VX公众号:桔子code / juzicode.com
#include "stdio.h"
int main()
{
    unsigned char a,b,c;
    a = 201;
    b = 255;
    c = a + b;
    printf("a=%d, b=%d, a+b=%d\n", a, b, c);
    a = 0;
    b = 109;
    c = a - b;
    printf("a=%d, b=%d, a-b=%d\n", a, b, c);
    return 0;
}

运行结果:

a=201, b=255, a+b=200
a=0, b=109, a-b=147

3、加权加法addWeighted()

addWighted()可以用来实现2幅图像的不同权值的加法,实现图像混合的效果。

  • 第1个参数是图像1 src;
  • 第2个参数是图像1的权值 alpha;
  • 第3个参数为图像2 dst;
  • 第4个参数为图像2的权值 beta;
  • 第5个参数附加数值gamma,单个数值,即使是多通道图像也使用单个数值;
  • 第6个参数可选,返回图像的实例,等同于函数返回结果
  • 第7个参数可选,dtype,表示像素值的数据类型

alpha和beta的关系并不要求二者的和为1,新图像的结果可以表示为:dst(I)=saturate(src1(I)∗alpha+src2(I)∗beta+gamma)。

下面这个例子对lena.jpg和opencv-logo.png进行2种不同权值的融合:

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

img = cv2.imread('..\\lena.jpg')[0:512,0:512] #截取部分,保证大小一致
img2 = cv2.imread('..\\opencv-logo.png' )[0:512,0:512] 
img3 = cv2.addWeighted(img,0.5,img2,0.2,10)
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])
cv2.imshow('addWeighted-0.5-0.2',img3)

img3 = cv2.addWeighted(img,0.2,img2,0.5,10)
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])
cv2.imshow('addWeighted-0.2-0.5',img3)
cv2.waitKey(0)

运行结果:

VX公众号: 桔子code / juzicode.com
cv2.__version__: 4.5.2
img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [ 64  62 162]

img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [ 32  31 178]

当addWeighted()的参数alpha=1,beta=1,gamma=0时,等同于add():

import cv2
print('VX公众号: 桔子code / juzicode.com')
print('cv2.__version__:',cv2.__version__)

img = cv2.imread('..\\lena.jpg')[0:512,0:512] #截取部分,保证大小一致
img2 = cv2.imread('..\\opencv-logo.png' )[0:512,0:512] 
print('addWeighted(img,1,img2,1,0):')
img3 = cv2.addWeighted(img,1,img2,1,0)
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])
cv2.imshow('addWeighted-1-1',img3)
 
print('add(img,img2):')
img3 = cv2.add(img,img2)
print('img[161,199]:',img[161,199])
print('img2[161,199]:',img2[161,199])
print('img3[161,199]:',img3[161,199])
cv2.imshow('add',img3)
cv2.waitKey(0)

运行结果:

cv2.__version__: 4.5.2
addWeighted(img,1,img2,1,0):
img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [109 105 255]
add(img,img2):
img[161,199]: [109 105 201]
img2[161,199]: [  0   0 255]
img3[161,199]: [109 105 255]

小结:用add()、addWeighted()进行图像的加法运算时,是一种“饱和运算”,比如图像是uinit8类型的,直接计算的结果如果大于255则被截断在255;如果2个图像直接用运算符+进行计算,实际是按照numpy数组计算,最终是对256求模计算后的结果。

原文链接:http://www.juzicode.com/archives/5503

 

扩展阅读:

  1. OpenCV-Python教程

OpenCV-Python教程:图像的加法运算(add,addWeighted)》有2条评论

  1. I want to add a transparent image like a cloth on human body part. I have done the pa c0 rt like where to put that cloth. I calculated the points also but the main part is how to place the transparent image on my human body image. Anybody knows simple python code to place transparent image on another image. please help me . The first is our overlay, the image that we want to overlay on top of the original image using a supplied level of alpha transparency.

    1. you can use addWeighted like this:addWeighted(body, 0.7, cloth, 0.3),where body.shape=cloth.shape

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注