OpenCV函数subtract()使用心得及需要注意的地方

Posted 昊虹算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV函数subtract()使用心得及需要注意的地方相关的知识,希望对你有一定的参考价值。

OpenCV函数subtract()的原型如下:

void cv::subtract(	InputArray 	src1,
					InputArray 	src2,
					OutputArray dst,
					InputArray 	mask = noArray(),
					int 	dtype = -1 
				)	

官方文档说明如下:

看了以上文档并结合自己的使用,补充说明以下几点:
1 函数subtract()不仅能做相同大小矩阵之间的差值运算,还可做矩阵与标量之间的相减运算。注意,当矩阵是多通道时,相应的标量类似于一个向量。比如三通道的矩阵与标量做相减运算,那么相应的标量需要三个数,形式上应该是cv::Scalar(x1,x2,x3)

2 当做相同大小矩阵的差值运算时,可以直接用减号“-”代替函数调用形式,此时用减号“-”通过运算符重载调用函数subtract()。
即如果A和B是两个大小相同的矩阵,则

cv::subtract(image1, image2, image3);

等效于下面这条语句:

image3 = image1 - image2;

3 dst矩阵并不需要事先定义大小、类型。比如下面的代码是可以的:

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main( )

	cv::Mat image1( 3, 3, CV_8UC1, cv::Scalar(5) );
	cv::Mat image2( 3, 3, CV_8UC1, cv::Scalar(10) );
	cv::Mat image3;
	cv::subtract(image1, image2, image3, cv::noArray(), CV_16S);

	// 输出矩阵结果
	std::cout <<"imag1的数据如下:\\n"<<image1 << std::endl<< std::endl;
	std::cout <<"imag2的数据如下:\\n"<<image2 << std::endl<< std::endl;
	std::cout <<"imag3的数据如下:\\n"<<image3 << std::endl<< std::endl;

  return 0;

4 如果运算结果中可能有负值并且要保留负值,有以下两种方法解决:
① 将两个相减的矩阵数据类型设为有符号型或用函数convertTo()转化为有符号型,不用去考虑目标矩阵的数据类型,假如目标矩阵原来的数据类型为CV_8U,则会自动进行转换为有符号型。比如下面的代码:

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main( )

	cv::Mat image1( 3, 3, CV_16S, cv::Scalar(5) );
	cv::Mat image2( 3, 3, CV_16S, cv::Scalar(10) );
	cv::Mat image3( 3, 3, CV_8UC1, cv::Scalar(0));
	
	cv::subtract(image1, image2, image3, cv::noArray(), CV_16S);
	
	// 输出矩阵结果
	std::cout <<"imag1的数据如下:\\n"<<image1 << std::endl<< std::endl;
	std::cout <<"imag2的数据如下:\\n"<<image2 << std::endl<< std::endl;
	std::cout <<"imag3的数据如下:\\n"<<image3 << std::endl<< std::endl;


  return 0;

运行结果如下:

② 直接设置函数subtract()的第四个参数为有符号型,函数subtract()的第四个参数实际上是设置目标矩阵的数据类型,比如上面的代码便是采用的这种方法。示例代码如下:

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main( )

	cv::Mat image1( 3, 3, CV_8UC1, cv::Scalar(5) );
	cv::Mat image2( 3, 3, CV_8UC1, cv::Scalar(10) );
	cv::Mat image3;
	cv::subtract(image1, image2, image3, cv::noArray(), CV_16S);

	// 输出矩阵结果
	std::cout <<"imag1的数据如下:\\n"<<image1 << std::endl<< std::endl;
	std::cout <<"imag2的数据如下:\\n"<<image2 << std::endl<< std::endl;
	std::cout <<"imag3的数据如下:\\n"<<image3 << std::endl<< std::endl;

  return 0;


用这种方法处理时,目标矩阵的数据类型也会被重置,比如下面的代码中我事先设置的img3的数据类型为CV_8U,但是经过函数subtract()的运算后,它的数据类型被改变成了CV_16S

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main( )

	cv::Mat image1( 3, 3, CV_8UC1, cv::Scalar(5) );
	cv::Mat image2( 3, 3, CV_8UC1, cv::Scalar(10) );
	cv::Mat image3( 3, 3, CV_8UC1, cv::Scalar(0));

	std::cout <<"未经函数subtract()运算前imag3的数据类型为:\\n"<<image3.depth() << std::endl<< std::endl;
	
	cv::subtract(image1, image2, image3, cv::noArray(), CV_16S);


	// 输出矩阵结果
	std::cout <<"imag1的数据如下:\\n"<<image1 << std::endl<< std::endl;
	std::cout <<"imag2的数据如下:\\n"<<image2 << std::endl<< std::endl;
	std::cout <<"imag3的数据如下:\\n"<<image3 << std::endl<< std::endl;

	std::cout <<"经过函数subtract()运算后imag3的数据类型为:\\n"<<image3.depth() << std::endl<< std::endl;

  return 0;

运行结果如下:

在OpenCV的数据类型中:

0代表CV_8U - 8-bit unsigned integers ( 0..255 )
1代表CV_8S - 8-bit signed integers ( -128..127 )
2代表CV_16U - 16-bit unsigned integers ( 0..65535 )
3代表CV_16S - 16-bit signed integers ( -32768..32767 )
4代表CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
5代表CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
6代表CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

③ 官方文档对这个函数的描述中有下面这句话:
Saturation is not applied when the output array has the depth CV_32S.
这句话的意思是说如果output array 的数据类型为CV_32S,那么Saturation运算不会被执行。但我按下面的代码测试没有效果:

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

int main( )

	cv::Mat image1( 3, 3, CV_8UC1, cv::Scalar(5) );
	cv::Mat image2( 3, 3, CV_8UC1, cv::Scalar(10) );
	cv::Mat image3( 3, 3, CV_32SC1,cv::Scalar(0));
	
	cv::subtract(image1, image2, image3, cv::noArray());


	// 输出矩阵结果
	std::cout <<"imag1的数据如下:\\n"<<image1 << std::endl<< std::endl;
	std::cout <<"imag2的数据如下:\\n"<<image2 << std::endl<< std::endl;
	std::cout <<"imag3的数据如下:\\n"<<image3 << std::endl<< std::endl;

  return 0;

运行结果如下:

可见,虽然img3的数据类型被我设置成了CV_32S,但是还是执行了Saturation运算,如果没有执行Saturation运算,img3中的元素值应该是-5才对。所以,这种方法大家就不要用了。

5 函数subtract()可以设置掩码矩阵,被掩码的元素不参与相减运算,这一点在实现很多算法时对于我们是十分方便的。

以上是关于OpenCV函数subtract()使用心得及需要注意的地方的主要内容,如果未能解决你的问题,请参考以下文章

python opencv学习——算术运算和逻辑运算

python opencv学习——算术运算和逻辑运算

python使用numpy中的np.add函数和np.subtract函数对二维numpy数组进行相加或者相减(Adding And Subtracting Matrices)

Android深度探索——第十章读书笔记及心得

OPENCV linux 编译安装问题心得

《Android深度探索》第十章心得体会