OpenCV-Python教程:图像加减乘除运算的共性问题

OpenCV-Python教程:图像加减乘除运算的共性问题,第1张

概述原文链接:http://www.juzicode.com/archives/6109返回Opencv-Python教程在前面的4篇文章中我们分别介绍了图像的加减乘除四种运算,这四种运算函数接口长得比较像,用法类似,有必要总结对比下。1、函数接口OpenCV-Python是OpenCV的Python接口,通过对比原生的C++接口,可以更详细地了

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

返回Opencv-Python教程

在前面的4篇文章中我们分别介绍了图像的加减乘除四种运算,这四种运算函数接口长得比较像,用法类似,有必要总结对比下。

1、函数接口

OpenCV-Python是OpenCV的Python接口,通过对比原生的C++接口,可以更详细地了解函数的使用方法。

运算方式C++接口Python接口
加法voID cv::add ( inputArray src1,
inputArray src2,
OutputArray dst,
inputArray mask = noArray(),
int dtype = -1
)
dst = cv2.add( src1, src2[, dst[, mask[, dtype]]] )
减法voID cv::subtract ( inputArray src1,
inputArray src2,
OutputArray dst,
inputArray mask = noArray(),
int dtype = -1
)
dst=cv2.subtract(src1, src2[, dst[, mask[, dtype]]])
乘法voID cv::multiply ( inputArray src1,
inputArray src2,
OutputArray dst,
double scale = 1,
int dtype = -1
)
dst = cv2.multiply( src1, src2[, dst[, scale[, dtype]]] )
除法voID cv::divIDe ( inputArray src1,
inputArray src2,
OutputArray dst,
double scale = 1,
int dtype = -1
)
voID cv::divIDe ( double scale,
inputArray src2,
OutputArray dst,
int dtype = -1
)
dst = cv2.divIDe( src1, src2[, dst[, scale[, dtype]]] )

dst = cv2.divIDe( scale, src2[, dst[, dtype]] )

从上面可以看到C++接口的函数返回值都是voID,返回图像都是通过dst传递出来的;mask掩码默认为noArray(),未传入掩码图像;scale参数默认为1,表示不作缩放;dtype为-1,根据src1和src2自动推导dst的数据类型,如果src1和src2图像的数据类型不一致时则需要显式的指定。

四则运算的函数入参几乎都长得一样,先做下入参的说明:

src1:源图像1,可以是图像对象或标量数据;src2:源图像2,可以是图像对象或标量数据;dst:目标图像,一般在Python接口中因为函数直接返回了新生成的目标图像,可以不传入;mask:掩码;scale:缩放比例,用于乘法和除法中,先和src1相乘再作用于src2,关于divIDe函数scale变量OpenCV官方文档写的有点歧义,OpenCV-Python教程:图像的除法运算中做了特别说明;dtype:数据类型,如果src1和src2都是图像对象且数据类型一致,则可以不用设置;如果src1或src2其中1个是标量数据,另外一个是图像对象,也可以不设置,生成的目标图像数据类型和图像对象一致。如果src1和src2都是图像对象且数据类型不一致,则需要显式说明;

 

2、入参传递方法

我们先以add为例看下入参的书写形式:dst = cv.add( src1, src2[, dst[, mask[, dtype]]] ),这里dst之后的参数就不是必须要传入的参数,这种写法的含义表示如果在传参的时候不写形参名称,就必须按照位置参数的方式依次传递,第3个位置参数是dst,第4个位置参数为mask,第5个位置参数为dtype。如果不想传入dst或者mask参数,但是又必须传入dtype参数,一种方法是指明dtype参数名称的方式书写比如dtype=xxx,或者将第3和第4个位置参数传入None”占位”,再传第5个位置参数作为dtype:

dst = cv2.add(src1, src2, dtype=cv2.CV_8UC3)dst = cv2.add(src1, src2, None, None, cv2.CV_8UC3)

下面是一个对比2种传参方式的完整例子,这个例子中构造了2个3通道2×5大小的图像对象(numpy数组),然后用不同的传参方式进行add()运算:

import numpy as npimport cv2print('VX公众号: 桔子code / juzicode.com')print('cv2.__version__:',cv2.__version__)img1 = np.arange(0,2*5*3,1,dtype=np.uint8).reshape(2,5,3)img2 = np.arange(200,200+2*5*3,1,dtype=np.uint8).reshape(2,5,3)print('img1:\n',img1)print('img2:\n',img2)img_ret = cv2.add(img1,img2,None,None,cv2.CV_32FC3)print('img_ret:\n',img_ret)img_ret2 = cv2.add(img1,img2,dtype=cv2.CV_32FC3)print('img_ret2:\n',img_ret2)

对比2种方法传参方法的效果是一样的:

img_ret: [[[200. 202. 204.]  [206. 208. 210.]  [212. 214. 216.]  [218. 220. 222.]  [224. 226. 228.]] [[230. 232. 234.]  [236. 238. 240.]  [242. 244. 246.]  [248. 250. 252.]  [254. 256. 258.]]]img_ret2: [[[200. 202. 204.]  [206. 208. 210.]  [212. 214. 216.]  [218. 220. 222.]  [224. 226. 228.]] [[230. 232. 234.]  [236. 238. 240.]  [242. 244. 246.]  [248. 250. 252.]  [254. 256. 258.]]]

 

3、dst参数和返回值关系

dst参数在四则运算的Python接口中是可以不传值的,当不传值时函数返回值就是运算后的结果。如果dst传值,dst经过计算后的实例是否和函数返回值一致呢?下面通过一个例子来看下,这个例子中构造了2个单通道的3×5大小的图像对象(numpy数组),用ID()函数获取dst和函数返回值img_ret的唯一标识符,二者相等说明是同一个实例,另外当修改img_ret后同时dst也被修改,这一点也证实二者确实是同一个实例:

import numpy as npimport cv2print('VX公众号: 桔子code / juzicode.com')print('cv2.__version__:',cv2.__version__)img1 = np.arange(0,3*5,1,dtype=np.uint8).reshape(3,5) #创建3行5列数组img2 = np.arange(200,200+3*5,1,dtype=np.uint8).reshape(3,5)dst = np.zeros((3,5),dtype=np.uint8) print('img1:\n',img1)print('img2:\n',img2)img_ret = cv2.add(img1,img2,dst)print('img_ret:\n',img_ret)print('dst:\n',dst)print('dst和img_ret是否同一实例:',ID(dst) == ID(img_ret))img_ret[:,:2] = 0print('img_ret:\n',img_ret)print('dst:\n',dst)

运行结果:

img1: [[ 0  1  2  3  4] [ 5  6  7  8  9] [10 11 12 13 14]]img2: [[200 201 202 203 204] [205 206 207 208 209] [210 211 212 213 214]]img_ret: [[200 202 204 206 208] [210 212 214 216 218] [220 222 224 226 228]]dst: [[200 202 204 206 208] [210 212 214 216 218] [220 222 224 226 228]]dst和img_ret是否同一实例: Trueimg_ret: [[  0   0 204 206 208] [  0   0 214 216 218] [  0   0 224 226 228]]dst: [[  0   0 204 206 208] [  0   0 214 216 218] [  0   0 224 226 228]]

 

4、图像和标量数据的运算

src1和src2如果都是图像对象(numpy数组),二者则要求shape属性一致,这样二者做四则运算时,相同下标的数据相互之间进行计算。如果其中之一为标量数据类型,则标量数据和图像对象的每一个元素都进行一次计算。

需要特别注意的是,如果其中的图像对象是多通道的数据时,但标量数据是单个数值,这时只会作用到图像对象的第1个通道,其他的通道则会和0进行计算,如果要多通道都和该数值作用,则需要构建一个包含4个数值的元组。即使是3通道的图像,也要构建一个4元组!

下面是一个3通道图像和单个数值进行计算的例子:

import numpy as npimport cv2print('VX公众号: 桔子code / juzicode.com')print('cv2.__version__:',cv2.__version__)img1 = np.arange(0,2*3*3,1,dtype=np.uint8).reshape(2,3,3)print('img1:\n',img1) img_add = cv2.add(img1,100)print('img_add:\n',img_add) img_div = cv2.divIDe(100,img1)print('img_div:\n',img_div)

运行结果:

cv2.__version__: 4.5.2img1: [[[ 0  1  2]  [ 3  4  5]  [ 6  7  8]] [[ 9 10 11]  [12 13 14]  [15 16 17]]]img_add: [[[100   1   2]  [103   4   5]  [106   7   8]] [[109  10  11]  [112  13  14]  [115  16  17]]]img_div: [[[ 0  0  0]  [33  0  0]  [17  0  0]] [[11  0  0]  [ 8  0  0]  [ 7  0  0]]]

从运行结果看,单个数值只作用到了图像的第1通道上。如果要作用图像的多个通道,则需要传入一个包含4个数值的元组:

img1 = np.arange(0,2*3*3,1,dtype=np.uint8).reshape(2,3,3)print('img1:\n',img1) img_add = cv2.add(img1,(100,100,100,0))  #包含4个元素的元组print('img_add:\n',img_add) img_div = cv2.divIDe((100,100,100,0),img1)print('img_div:\n',img_div)

运行结果:

img1: [[[ 0  1  2]  [ 3  4  5]  [ 6  7  8]] [[ 9 10 11]  [12 13 14]  [15 16 17]]]img_add: [[[100 101 102]  [103 104 105]  [106 107 108]] [[109 110 111]  [112 113 114]  [115 116 117]]]img_div: [[[  0 100  50]  [ 33  25  20]  [ 17  14  12]] [[ 11  10   9]  [  8   8   7]  [  7   6   6]]]

从前面的介绍可以看到除法运算有2种接口形式divIDe( src1, src2[, dst[, scale[, dtype]]] )、divIDe( scale, src2[, dst[, dtype]] ),第1种接口形式下src1或者src2可以是标量数据类型,但是当src1是一个数值型的标量数据类型时,这时第1个位置参数是当做标量数值只作用于第1个通道(第1种接口形式),还是当成float型的scale变量作用于所有的通道(第2种接口形式),就产生了参数解析的“二义性”,如果是在C++接口里就要报编译错误啦。在 OpenCV-Python教程:图像的除法运算 中我们看到这种情况实际是按照第1种形式下的标量数值来处理的。如果要调用第2种形式的接口必须显式地写明形参变量的名称。

 

5、dtype在什么时候需要显式声明

在src1和src2都是图像对象时,如果二者的数据类型不一致,无法自动推导出返回的图像实例该采用哪种数据类型,这时就需要传入dtype参数指明生成图像所采用的数据类型,否则会报“functions have different types”错误。需要注意的是dtype参数不是用numpy的uint8,float32等数据类型,而是采用OpenCV的CV_8U、CV_32F等数据类型。

import numpy as npimport cv2print('VX公众号: 桔子code / juzicode.com')print('cv2.__version__:',cv2.__version__)img1 = np.arange(0,2*3,1,dtype=np.uint8).reshape(2,3)img2 = np.arange(0,2*3,1,dtype=np.float32).reshape(2,3)img_add = cv2.add(img1,img2,dtype=cv2.CV_8UC1)print('img_add:\n',img_add) img_mult = cv2.multiply(img1,img2,dtype=cv2.CV_32FC1)print('img_mult:\n',img_mult) 

运行结果:

cv2.__version__: 4.5.2img_add: [[ 0  2  4] [ 6  8 10]]img_mult: [[ 0.  1.  4.] [ 9. 16. 25.]]

 

小结:这篇文章总结了加减乘除四种运算的共性,比如dst入参的使用、入参传递的用法、标量和图像运算的特点、以及dtype参数在图像数据类型不一致时必须显式声明。

 

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

扩展阅读:OpenCV-Python教程OpenCV-Python教程:图像的加法运算OpenCV-Python教程:图像的减法运算、标量加减运算OpenCV-Python教程:图像的乘法运算OpenCV-Python教程:图像的除法运算 总结

以上是内存溢出为你收集整理的OpenCV-Python教程:图像加减乘除运算的共性问题全部内容,希望文章能够帮你解决OpenCV-Python教程:图像加减乘除运算的共性问题所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1184791.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-03
下一篇 2022-06-03

发表评论

登录后才能评论

评论列表(0条)

保存