OpenCV(C++)图像运算

版权声明:转载需获得本人许可,否则后果自负。 https://blog.csdn.net/Liziwepdl/article/details/84898598

图像在计算机中就是一个普通的数值矩阵存在的,所以也就能够相应的进行各种运算,这些运算构成了图像处理的基本操作。图像加法可以混合两幅图像进行图像融合,比如在处理照片的贴图;图像减法可以用来去掉运动图像的背景,来进行目标定位追踪。这篇文章主要介绍图像的算术运算,逻辑运算,重映射变换等。

代数运算

算术运算包括加、减、乘、除和位运算,这些运算操作的特点是提供两个输入参数,得到一个输出结果。有时候还可以运算操作的权重系数,或者指定掩码。

加法运算拥有多种格式,能够灵活的进行各种不同的加法操作。下面主要介绍几种加法运算的用法。

最普通的加法操作 c[i] = a[i] + b[i] 。

cv::add(imageA,imageB,resultC);

将图像加上一个常数 c[i] = a[i] + k, 注意传入的Scalar是表示颜色的对象,需要根据图像的通道数来具体定义。

//灰度图像
cv::add(imageA,cv::Scalar(k),resultC);    
//彩色图像
cv::add(imageA,cv::Scalar(k,i,j),resultC);    

将两幅图像进行加权混合 c[i] = k1*a[i] + k2*b[i] + k3 。

cv::addWeighted(imageA,k1,imageB,k2,k3,resultC);

还能进行线性相加 c[i] = k*a[i] + b[i]。

cv::scaleAdd(imageA,k,imageB,resultC);

这些都是图像整体的加法运算,还能够添加一个掩码参数,掩码的作用就是让图像的某一部分参与运算,其他部分保持原样。具体来说,掩码通常与要运算的图像大小一样,当掩码的值非空(即为真),则在对应的图像位置进行运算,否则不运算。

cv::add(imageA,imageB,resultC,mask);

除了加法运算外,还有减法 cv::subtract、乘法 cv::multiply、除法 cv::divede、差的绝对值cv::absdiff,这些函数也有多种格式。

位运算符也是常用的一类运算符,如与运算 cv::bitwise_and、或运算cv::bitwise_or、异或运算 cv::bitwise_xor、非运算 cv::bitwise_not。cv::max 和 cv::min 能够找到两幅图像中的最大或者最小值。 此外还能对单张图像进行数学操作,如 cv::sqrt、cv::pow、cv::abs、cv::exp等等。

值得注意的一点是,图像像素的值范围通常是0-255,所以经过这些操作之后,有可能值会超出这个范围,然后会进行补码运算使值在unsigned char 能表达的范围之内,会造成结果错误的情况。所以记得使用 cv::saturate_cast 函数,这个函数能够把小于0的调整为0,大于255的调整为255,把浮点数调整为最接近的整数,以确保结果在预定的像素范围之内。

重载运算符

大部分的C++中的运算符在Opencv中都进行了重载,所以进行算术运算符时可以直接使用运算符。例如+、 -、 *、 / 和 &、 | 、^ 、~ 都能够直接使用,而比较运算符<、>、==、>=等也进行了重载,运算结果会返回一个8 bit 的二值图像。

C = 4 * A + 7 * B + 8;
C = A >= B;

图像还是一个矩阵,所以矩阵的操作也进行了重载。矩阵乘法A*B,矩阵求逆 A.inv(),矩阵转置 A.t(),求矩阵行列式 A.determinant(),叉乘 A.cross(B),点乘 A.dot(B)。此外,所有复合赋值运算符+=、-=、&=也是可以使用的。

图像通道分割

有时候需要对多通道的图像进行通道分割,对不同的通道进行不同的操作。这时候就可以使用 cv::split 函数,将图像的三个通道分别放到三个Mat对象中。而把三个通道合并可以使用 cv::merge 函数,即合并成一彩色图像。例如把一张图像与蓝色通道进行混合,可以这样实现:

std::vector<cv::Mat> planes;
cv::split(image,planes);
planes[0] += image2;
cv.merge(planes,result);

注意:在Opencv中通道顺序是BGR,蓝色表示channels[0],绿色表示channels[1],红色表示channels[2]。

图像重映射

前面都是改变图像的像素值,如果只想改变像素的位置,可以使用 remap 函数,只需要定义好映射参数,然后将映射参数应用到输入图像上就可以。映射参数分为 X 坐标轴 和 Y 坐标轴,它们都用浮点数类型的 cv::Mat 表示,然后根据某种规则创建映射参数。下面展示一段用remap函数反转图像的代码(虽然已有方法 flip() 可以直接做到) :

using namespace cv;
int main()
{
	//变量定义
	Mat srcImage, dstImage;
	Mat map_x, map_y;
	//载入原始图
	srcImage = imread("E:\\写作插图\\下载.jpg", 1);
	if (!srcImage.data) 
	{ 
		printf("error\n"); 
		return false; 
	}
	imshow("原始图", srcImage);
	//创建和原始图一样的效果图,x重映射图,y重映射图
	dstImage.create(srcImage.size(), srcImage.type());
	map_x.create(srcImage.size(), CV_32FC1);
	map_y.create(srcImage.size(), CV_32FC1);
	//双层循环,创建映射参数
	for (int j = 0; j < srcImage.rows; j++)
	{
		for (int i = 0; i < srcImage.cols; i++)
		{
			//改变map_x & map_y的值.
			map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);
			map_y.at<float>(j, i) = static_cast<float>(j);
		}
	}
	//进行重映射操作
	remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
	//显示效果
	imshow("映射图像", dstImage);
	waitKey(0);
	return 0;
}

值得注意的是,映射参数是浮点数,所以可以映射到一个非整数值,因此必须进行插值操作,常用的差值操作有INTER_NEAREST - 最近邻插值 ,INTER_LINEAR – 双线性插值(默认值),INTER_CUBIC – 双三次样条插值,这里使用的就是INTER_LINEAR插值方法。

原始图与映射图片的对比

欢迎大家关注公众号“计算机视觉与机器学习”
计算机视觉和机器学习

猜你喜欢

转载自blog.csdn.net/Liziwepdl/article/details/84898598