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

返回Opencv-Python教程

原文链接:http://www.juzicode.com/opencv-python-add-addweighted

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教程

发表评论

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