图像卷积概念
-
数字信号处理中的卷积: 卷积是两个变量在某范围内相乘后求和的结果。如果卷积的变量是序列x(n)和h(n),则卷积的结果:
通过“打脸”案例理解卷积:卷积理解
-
数字图像处理中的卷积:
数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用 卷积核(卷积模板)在图像上滑动,将图像点上的像素灰度值与对应的卷积核上的数值相乘,然后将所有相乘后的值相加作为卷积核中间像素对应的图像上像素的灰度值,并最终滑动完所有图像的过程。
-
卷积核的选择规则:
A 、卷积核的大小一般是奇数,这样的话它是按照中间的像素点中心对称的,所以卷积核一般都是3x3,5x5或者7x7。有中心了,也有了半径的称呼,例如5x5大小的核的半径就是2;
B、卷积核所有的元素之和一般要等于1,这是为了原始图像的能量(亮度)守恒。其实也有卷积核元素相加不为1的情况,下面就会说到;
C 、如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像更亮,反之,如果小于1,那么得到的图像就会变暗。如果和为0,图像不会变黑,但也会非常暗;
D、对于滤波后的结构,可能会出现负数或者大于255的数值。对这种情况,我们将他们直接截断到0和255之间即可。对于负数,也可以取绝对值。 -
不同卷积核下的卷积意义:(卷积操作的目的)
利用像素点和其邻域像素之前的空间关系,通过加权求和的操作,实现平滑,去燥,模糊(blurring),锐化(sharpening),边缘检测(edge detection)和 边缘提取 等功能。各种卷积核的意义
图像卷积操作
- 通过代码实现平滑均值滤波
- 可以自己通过代码进行卷积操作,也可以使用API :
blur
//API
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);
图像边缘像素操作
-
边界补充问题: 上面的卷积操作,也反映出一个问题,例如,原始图片尺寸为7x7 ,卷积核的大小为3x3,当卷积核沿着图片滑动后只能滑动出一个的图片出来,这就造成了卷积后的图片和卷积前的图片尺寸不一致,这显然不是我们想要的结果,所以为了避免这种情况,需要先对原始图片做边界填充处理,即,我们需要先把原始图像填充为 9x9 的尺寸。那么填充的方法一般有: 补零、边界复制、镜像、块复制
-
API 中的参数选择有:
最常用的是 BORDER_REFLECT_101 ,填充示意(粗体为填充的像素): 尾-----> (头-------尾)<-----------头
CONSTANT为常量值填充;
REPLICATE为镜像填充;
WRAP为块复制填充
-
边缘填充的方式,锚定点都对卷积结果有影响
-
利用API :
copyMakeBorder
可以进行边缘填充
CV_EXPORTS_W void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar() );
//边缘像素的处理
//边缘填充
int border = 8;//填充的像素个数
Mat border_m;
copyMakeBorder(src, border_m, border, border, border, border, BORDER_CONSTANT, Scalar(0, 0, 255));//边缘加了个红框
imshow("border fill demo", border_m);