读者们从小都应该学过数字的加减乘除运算,比如3+5、16 * 38等等。其实图像也是可以进行加减乘除运算的??什么,图像也可以进行加减乘除运算,why???其实原理很简单,以一张640 *360的单通道的灰度图为例,他就是一个640列360行的矩阵,当然可以和另外一个640列360行的矩阵进行加减乘除运算了。
还不明白,别着急,咱们会有详细的举例说明。这篇文章干货满满啊... ...
Add()函数就是将两个已知的图像相加,并输出一个新的函数。这种加法使用时,当两个像素点的像素值之和大于255时,就将结果值等于255(就是最大值了)。代码如下:
Mat dstMat = new Mat();
CvInvoke.Add(srcMat1, srcMat2, dstMat);
CvInvoke.Imshow("Add image, " + dstMat.Size.ToString(), dstMat);
其中srcMat1和srcMat2就是两个输入的原始图像,dstMat就是输出图像。使用这个函数时要注意:
比如下图,图片1是 黑夜.jpg,图片2是 黑猪.jpg,两个一相加,就合成了下面黑夜中母猪飞上天的照片,这感觉是不是一下就来了。
再看看下面,街景.jpg+沙滩.jpg的效果。
这个函数是计算两个图像的加权和。Weighted就是权重的意思,怎么理解呢,先看官方说明:
public static void AddWeighted(
IInputArray src1, // 输入图像1
double alpha, // 图像1的权重
IInputArray src2, // 输入图像2
double beta, // 图像2的权重
double gamma, // 一个加到权重总和上的标量值,干什么用的不知道,我一般都是写成 0
IOutputArray dst, // 输出图像
DepthType dtype = DepthType.Default // 输出图像的可选深度,干什么用的不知道,我一般都不指明
)
使用时代码如下:
Mat dstMat = new Mat();
CvInvoke.AddWeighted(srcMat1, 0.8, srcMat2, 0.2, 0, dstMat);
CvInvoke.Imshow("AddWeighted image, " + dstMat.Size.ToString(), dstMat);
简单的理解就是以图像1的百分比加上图像2的百分比,合成一个新图像。街景.jpg * 0.8 +沙滩.jpg * 0.2,输出结果如下:
街景.jpg * 0.2 +沙滩.jpg * 0.8,输出结果如下:
使用时要注意两幅图片尺寸必须相同。
Subtract()的功能就是将两张图像的每个像素的值相减,并且将结果输出到新图像。官方对该函数定义如下:
public static void Subtract(
IInputArray src1, // 输入图像1,被减数
IInputArray src2, // 输入图像2,减数
IOutputArray dst, //目标图像
IInputArray mask = null, // 掩码,属于可选参数
DepthType dtype = DepthType.Default // 输出图像深度,可以不写
)
代码使用如下:
Mat dstMat = new Mat();
CvInvoke.Subtract(srcMat1, srcMat2, dstMat); // 相减后,小于0则自动改成0
CvInvoke.Imshow("Subtract image, " + dstMat.Size.ToString(), dstMat);
Subtract()使用时,两个点像素值相减,如果值小于0,则结果就是0。街景.jpg减去沙滩.jpg,效果如下:
相比Subtract(),AbsDiff()函数再相减时,结果小于0,会取其绝对值。其函数主要作用是计算两张图片的不同之处。这个用出非常大,读者可以仔细想想。代码使用方法如下:
Mat dstMat = new Mat();
CvInvoke.AbsDiff(srcMat1, srcMat2, dstMat); // 相减后,小于0则取绝对值
CvInvoke.Imshow("AbsDiff image, " + dstMat.Size.ToString(), dstMat);
比如一张全黑图片减去沙滩.jpg,结果如下:
这样好理解了吗????
Multipy()函数的作用是将两个输入图像的对应像素点相乘,并将结果输出到新图像。官方定义如下:
public static void Multiply(
IInputArray src1, // 输入图像1,作为被乘数
IInputArray src2, // 输入图像2,作为乘数
IOutputArray dst, // 输出图像
double scale = 1.0, // 缩放因子,可以在最终结果基础上再乘以一个系数
DepthType dtype = DepthType.Default // // 输出图像深度,可以不写
)
具体来说,对于每个像素点(x,y),输出图像的像素值为:
要注意,相乘的两张照片尺寸要相同。相乘后结果最大值是255,最小值是0,代码如下:
Mat dstMat = new Mat();
CvInvoke.Multiply(srcMat1, srcMat2, dstMat, 1); // 相乘后,最大值255,最小值0
CvInvoke.Imshow("Multiply image, " + dstMat.Size.ToString(), dstMat);
街景.jpg乘以沙滩.jpg,效果如下:
上面演示的是利用Multipy()函数对两张尺寸相同的照片相乘。在实际使用过程中,还有一种情况是:某张图片,希望蓝色、绿色通道保持不变,而红色通道放大5倍。这种情况下也可以用Multipy()函数来实现,代码如下:
Mat dstMat = new Mat();
Mat tempMat = new Mat(new System.Drawing.Size(srcMat1.Cols, srcMat1.Rows), DepthType.Cv8U, 3);
tempMat.SetTo(new MCvScalar(1, 1, 5));
CvInvoke.Multiply(srcMat1, tempMat, dstMat, 1); // 相乘后,最大值255,最小值0
CvInvoke.Imshow("Multiply image, " + dstMat.Size.ToString(), dstMat);
这时候就是srcMat1乘以值为(1,1,5)的Mat变量,以街景照片为例,只把红色通道放大5倍效果如下:
对比Multipy()函数,Divide()函数的定义与其完全相同。在Emgu CV中,两张图片相除,必须是尺寸和类型必须相同,相除后,最大值255,最小值0,代码如下:
Mat dstMat = new Mat();
CvInvoke.Divide(srcMat1, srcMat2, dstMat, 1); // 相除后,最大值255,最小值0
CvInvoke.Imshow("Divide image, " + dstMat.Size.ToString(), dstMat);
街景.jpg除以沙滩.jpg,效果如下:
参考第六小节,如果是让街景.jpg的蓝色通道缩小5倍,绿色、红色通道数值不变,可以这样写:
Mat tempMat = new Mat(new System.Drawing.Size(srcMat1.Cols, srcMat1.Rows), DepthType.Cv8U, 3);
tempMat.SetTo(new MCvScalar(5, 1, 1));
Mat dstMat = new Mat();
CvInvoke.Divide(srcMat1, tempMat, dstMat, 1); // 相除后,最大值255,最小值0
CvInvoke.Imshow("Divide image, " + dstMat.Size.ToString(), dstMat);
街景.jpg 的蓝色通道缩小5倍,实际效果如下:
在Emgu CV中,图像还可以直接与数字进行最简单的四则运算,分别如下:
Mat dstMat = srcMat1.Clone();
dstMat += 50.00; // 加法
dstMat -= 50.00; // 加法
dstMat *= 2.00; // 乘法
dstMat /= 5.00; // 乘法
街景.jpg + 50 的效果如下:
街景.jpg / 2.45 的效果如下:
下一篇介绍Emgu CV中图像的按位运算方法。
原创不易,请勿抄袭。共同进步,相互学习。