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()可以设置掩码矩阵,被掩码的元素不参与相减运算,这一点在实现很多算法时对于我们是十分方便的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值