1,图像卷积概念
滤波通常是指对图像中特定频率的分量进行过滤或抑制。图像滤波是在尽可能保留图像细节特征的条件下对目标图像的噪声进行抑制,是常用的图像预处理操作。
数据采集都会带有一定的噪声,图像的噪声可以理解为灰度值的随机变化。对图像在空间域存在的随机噪声,可以通过平滑技术进行抑制或去除,称为空间域图像滤波。
频率域滤波是通过傅里叶变换方法实现的,而空间域滤波则是通过相关与卷积运算实现。常用的平滑处理算法有基于二维离散卷积的高斯平滑、均值平滑,基于统计方法的中值平滑,保留边缘信息的双边滤波、导向滤波等。
空间滤波器是由邻域和定义的操作构成的,滤波器规定了滤波时采用的邻域形状及该区域内像素值的处理方法。滤波器也被称为 “核”、“模板”、“窗口”、“掩模”、“算子”,一般在信号处理中称为 “滤波器”,在数学领域称为 “核”。线性滤波器就是指基于线性核的滤波,也就是卷积运算。
滤波器核是指像素周围某一大小的矩形邻域,也称为模板、滑动窗口。
**卷积运算(Convolution operation)**是利用模板对图像进行邻域操作:将滤波器模板的中心移动到待处理的像素点,对模板区域的各点加权相乘后求和。
大小为 s*t 的核(模板) w 与图像 f(x,y) 的相关运算 ( w ⋄ f ) ( x , y ) (w \diamond f)(x,y) (w⋄f)(x,y) 的数学描述为:
相关运算的计算步骤如下:
(1)将模板在图像中逐点移动,模板中心移动到被处理的像素点上;
(2)将模板区域中的各点的系数(权值)与图像的像素值相乘,对乘积求和,即加权求和;
(3)将加权求和结果赋值给模板中心的像素。
数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用 卷积核(卷积模板)在图像上滑动,将图像点上的像素灰度值与对应的卷积核上的数值相乘,然后将所有相乘后的值相加作为卷积核中间像素对应的图像上像素的灰度值,并最终滑动完所有图像的过程。
卷积核的选择规则:
A 、卷积核的大小一般是奇数,这样的话它是按照中间的像素点中心对称的,所以卷积核一般都是3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2;
B、卷积核所有的元素之和一般要等于1,这是为了原始图像的能量(亮度)守恒。其实也有卷积核元素相加不为1的情况,下面就会说到;
C 、如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗;
D、对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。
不同卷积核下的卷积意义:(卷积操作的目的)
利用像素点和其邻域像素之前的空间关系,通过加权求和的操作,实现平滑,去燥,模糊(blurring),锐化(sharpening),边缘检测(edge detection)和 边缘提取 等功能。
2,图像卷积操作
通过代码实现平滑均值滤波
可以自己通过代码进行卷积操作,也可以使用API : blur
CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
Mat src = imread("F:/code/images/test1.png");
if (src.empty()) {
printf("fail to read");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", src);
//手工进行图像卷积计算
//均值滤波
int width = src.cols;
int height = src.rows;
Mat result = src.clone();
for (int row = 1; row < height - 1; row++) {
for (int col = 1; col < width - 1; col++) {
// 3*3 卷积核
int sb = result.at<Vec3b>(row, col)[0] + result.at<Vec3b>(row - 1, col - 1)[0] + result.at<Vec3b>(row - 1, col)[0] + result.at<Vec3b>(row - 1, col + 1)[0] + result.at<Vec3b>(row, col - 1)[0] + result.at<Vec3b>(row, col + 1)[0] + result.at<Vec3b>(row + 1, col - 1)[0] + result.at<Vec3b>(row + 1, col)[0] + result.at<Vec3b>(row + 1, col + 1)[0];
int sg = result.at<Vec3b>(row, col)[1] + result.at<Vec3b>(row - 1, col - 1)[1] + result.at<Vec3b>(row - 1, col)[1] + result.at<Vec3b>(row - 1, col + 1)[1] + result.at<Vec3b>(row, col - 1)[1] + result.at<Vec3b>(row, col + 1)[1] + result.at<Vec3b>(row + 1, col - 1)[1] + result.at<Vec3b>(row + 1, col)[1] + result.at<Vec3b>(row + 1, col + 1)[1];
int sr = result.at<Vec3b>(row, col)[2] + result.at<Vec3b>(row - 1, col - 1)[2] + result.at<Vec3b>(row - 1, col)[2] + result.at<Vec3b>(row - 1, col + 1)[2] + result.at<Vec3b>(row, col - 1)[2] + result.at<Vec3b>(row, col + 1)[2] + result.at<Vec3b>(row + 1, col - 1)[2] + result.at<Vec3b>(row + 1, col)[2] + result.at<Vec3b>(row + 1, col + 1)[2];
result.at<Vec3b>(row, col)[0] = sb / 9;
result.at<Vec3b>(row, col)[1] = sg / 9;
result.at<Vec3b>(row, col)[2] = sr / 9;
}
}
imshow("mean filter My", result);
//调用API : blur 进行均值滤波
Mat dst;
blur(src, dst, Size(3,3), Point(-1,-1), BORDER_DEFAULT);
imshow("mean filter API", dst);
3,图像边缘像素操作
边界补充问题: 上面的卷积操作,也反映出一个问题,例如,原始图片尺寸为7x7 ,卷积核的大小为3x3,当卷积核沿着图片滑动后只能滑动出一个的图片出来,这就造成了卷积后的图片和卷积前的图片尺寸不一致,这显然不是我们想要的结果,所以为了避免这种情况,需要先对原始图片做边界填充处理,即,我们需要先把原始图像填充为 9x9 的尺寸。那么填充的方法一般有: 补零、边界复制、镜像、块复制
你的鼓励将是我创作的最大动力