图像卷积
由于采集图像的设备可能受到光子噪声、暗电流噪声等干扰,使得采集到的图像具有噪声,另外图像信号在传输过程中也可能产生噪音
因此去除图像中的噪声是图像预处理中十分重要的步骤,图像滤波是图像噪声去除的重要方式
图像卷积filter2D()
卷积常用在信号处理中,而图像数据可以看作一种信号数据,例如图像中每一行可以看作测量亮度变化的信号数据
因此,可以对图像进行卷积操作,在信号处理中,卷积操作需要给出一个卷积函数与信号进行计算
图像的卷积需要给出一个卷积模板与原始图像进行卷积计算,整个过程可以看成是一个卷积模板在另外一个大的图像上移动
对每个卷积模板覆盖的区域进行点乘,得到的值作为中心像素点的输出值
卷积首先需要将卷积模板旋转180°,之后从图像的左上角开始移动旋转后的卷积模板,从左到右,从上到下,依次进行卷积计算
最终得到卷积后的图形,卷积模板又称为卷积核或者内核,是一个固定大小的二维矩阵,矩阵存放着预先设定的数值
图像卷积过程可以大致分为五步
第一步:将卷积模板旋转180°,由于多数情况中卷积模板中的数据是中心对称的,因此有时这步可以省略,但是如果卷积模板不是中心对称的,必须将模板进行旋转
第二步:将卷积模板中心放在原图像中需要计算卷积的像素上,卷积模板中其余部分对应在原图像相应的像素上,如图5-1所示,卷积模板和待卷积矩阵中黄色区域分别是卷积模板的中心和对应点,定位结果中阴影区域为模板覆盖的区域

第三步:用卷积模板中的系数乘以图像中对应位置的像素数值,并对所有结果求和,针对图5-1表示的卷积步骤,其计算过程如式所示,最终计算结果为84
第四步:将计算结果存放在原图像中与卷积模板中心点像对应的像素处,即图5-1里待卷积矩阵中的黄色像素处,结果如图5-2所示
第五步:将卷积模板在图像中从左至右从上到下移动,重复以上3个步骤,直到处理完所有的像素值,每一次循环的处理结果如图5-3所示

这种方法只能对图像中心区域进行卷积,而由于卷积模板中心无法放置在图像的边缘像素处,因此图像边缘区域没有进行卷积运算
为了解决这个问题,可主动将图像的边缘外推出去(例:用0在原始图像周围增加一层像素)
从而解决模板图像中部分数据没有对应像素的问题
最后一个像素值已经接近CV_8U数据类型的最大值,因此,如果卷积模板选取不当,极有可能造成卷积结果超出数据范围的情况发生
因此,图像卷积操作常将卷积模板通过缩放使得所有数值的和为1,进而解决卷积后数值越界的情况发生
OpenCV4提供了filter2D()函数实现图像和卷积模板之间的卷积运算
filter2D()函数原型:
void filter2D(InputArray src,
OutputArray dst,
int ddepth,
InputArray kernel,
Point anchor = Point(-1,-1),
int boderType = BORDER_DEFAULT
)
- src:输入图像
- dst:输出图像
ddepth:输出图像的数据类型(深度),根据输入图像的数据类型不同,拥有不同的取值范围
当赋值为-1时,输出图像的数据类型自动选择
- kernel:卷积核,CV_32FC1的矩阵
anchor:内核的基准点(锚点),默认值(-1,-1)代表内核基准点位于kernel的中心位置
基准点是卷积核中与进行处理的像素点的像素点重合的点,位置必须在卷积核的内部
- delta:偏值,在计算结果中加上偏值
- borderType:像素外推法选择标志,默认参数为BORDER_DEFAULT,表示不包含边界值倒序填充
该函数用于实现图像和卷积模板之间的卷积运算,函数第一个参数为输入的待卷积图像,允许输入图像为多通道图像
如果需要用不同的卷积模板对不同的通道进行菊娜姬操作,就需要先使用split()函数将多个通道分离之后单独对每一个通道
求取卷积运算,第二个参数为输出图像,尺寸和通道数与第一个参数保持一致,输出图像的数据类型由第三个参数进行选择
第四个参数为卷积模板矩阵,第五个参数可以指定卷积模板的中心位置,卷积偏值表示在卷积步骤第二步计算结果上再加上偏值作为结果
注意:filter()函数不会将卷积模板进行旋转,热狗卷积模板不对称,那么需要将卷积模板旋转180°后再输入第四个参数
filter2D()函数输出图像数据类型与输入图像数据类型联系
| 输入图像数据类型 | 输出图像数据类型 |
|---|---|
| CV_8U | -1 / CV_16S / CV_32F / CV_64F |
| CV_16U / CV_16S | -1 / CV_32F / CV_64F |
| CV_32F | -1 / CV_32F / CV_64F |
| CV_64F | -1 / CV_64F |
示例:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
//待卷积矩阵
uchar points[25]= {
1,2,3,4,5,
6,7,8,9,10,
11,12,13,14,15,
16,17,18,19,20,
21,22,23,24,25
};
Mat img(5,5,CV_8UC1,points);
//卷积模板
Mat kernel = (Mat_<float>(3,3) << 1,2,1,
2,0,2,
1,2,1);
Mat kernel_norm = kernel / 12; //卷积模板归一化
//未归一化卷积结果和归一化卷积结果
Mat result,result_norm;
filter2D(img,result,CV_32F,kernel,Point(-1,-1),2,BORDER_CONSTANT);
filter2D(img,result_norm,CV_32F,kernel_norm,Point(-1,-1),2,BORDER_CONSTANT);
cout << "result=" << endl << result << endl;
cout << "result_norm" << endl << result_norm << endl;
//图像卷积
Mat lena = imread("E:\\CLion\\opencv_xin\\SG\\SG_0.jpg");
if (lena.empty()) {
cout << "Error" << endl;
return -1;
}
Mat lena_filter;
filter2D(lena,lena_filter,-1,kernel_norm,Point(-1,-1),2,BORDER_CONSTANT);
imshow("lena_filter",lena_filter);
imshow("lena",lena);
waitKey(0);
return 0;
}
注:ucahr:图像处理中常常使用的一种数据类型uchar,一般它指的就是unsigned char,是一种8-bit无符号整形数据,范围为[0, 255]