OpenCV4入门系列教程51:filter2D(图像掩码与滤波)

入门系列索引:系列教程索引地址

上一篇:OpenCV4入门系列教程50:kalman(卡尔曼)滤波

官方指导文件对filter2D()函数的描述为:Convolves an image with kernel即利用内核实现对图像的卷积运算。

函数原型为:

1
2
3
4
5
6
7
void filter2D( InputArray src, 
OutputArray dst,
int ddepth,
InputArray kernel,
Point anchor=Point(-1,-1),
double delta=0,
int borderType=BORDER_DEFAULT );

参数说明:

  • src: 输入图像
  • dst: 输出图像,和输入图像具有相同的尺寸和通道数量
  • ddepth: 目标图像深度,如果没写将生成与原图像深度相同的图像。
  • kernel: 卷积核(或者是相关核),一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,可以先使用split()函数将图像通道事先分开。
  • anchor: 内核的基准点(anchor),其默认值为(-1,-1)说明位于kernel的中心位置。基准点即kernel中与进行处理的像素点重合的点。
  • delta: 在储存目标图像前可选的添加到像素的值,默认值为0
  • borderType: 像素向外逼近的方法,默认值是BORDER_DEFAULT,即对全部边界进行计算。

该函数使用于任意线性滤波器的图像,支持就地操作。当其中心移动到图像外,函数可以根据指定的边界模式进行插值运算。函数实质上是计算kernel与图像的相关性而不是卷积:

dst(x,y)=0xkernel.cols0ykernel.rowskernel(x,y)src(x+xanchor.x,y+yanchor.y)dst(x,y)=\sum_{0 \leq x' \leq kernel.cols \\ \\ 0 \leq y'\leq kernel.rows} kernel(x',y') * src(x+x'-anchor.x,y+y'-anchor.y)

也就是说kernel并不是中心点的镜像,如果需要一个正真的卷积,使用函数flip()并将中心点设置为(kernel.cols - anchor.x - 1, kernel.rows - anchor.y -1).

该函数在大核(11x11或更大)的情况下使用基于DFT的算法,而在小核情况下使用直接算法(使用createLinearFilter()检索得到).

测试代码1:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 功能:代码 4-5 图像掩码操作的两种实现
// 作者:朱伟 zhu1988wei@163.com
// 来源:《OpenCV图像处理编程实例》
// 博客:http://blog.csdn.net/zhuwei1988
// 更新:2016-8-1
// 说明:版权所有,引用或摘录请联系作者,并按照上面格式注明出处,谢谢。//
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <stdio.h>
using namespace cv;
using namespace std;
// 基于像素邻域掩码操作
cv::Mat Myfilter2D(cv::Mat srcImage) {
const int nChannels = srcImage.channels();
cv::Mat resultImage(srcImage.size(), srcImage.type());
for (int j = 1; j < srcImage.rows - 1; ++j) {
// 获取邻域指针
const uchar *previous = srcImage.ptr<uchar>(j - 1);
const uchar *current = srcImage.ptr<uchar>(j);
const uchar *next = srcImage.ptr<uchar>(j + 1);
uchar * output = resultImage.ptr<uchar>(j);
for (int i = nChannels; i < nChannels * (srcImage.cols - 1); ++i) {
// 4-邻域均值掩码操作
*output++ = saturate_cast<uchar>(
(current[ i - nChannels ] + current[ i + nChannels ] + previous[ i ] + next[ i ]) /
4);
}
}
// 边界处理
resultImage.row(0).setTo(Scalar(0));
resultImage.row(resultImage.rows - 1).setTo(Scalar(0));
resultImage.col(0).setTo(Scalar(0));
resultImage.col(resultImage.cols - 1).setTo(Scalar(0));
return resultImage;
}
// 自带库掩码操作
cv::Mat filter2D_(cv::Mat srcImage) {
cv::Mat resultImage(srcImage.size(), srcImage.type());
Mat kern = (Mat_<float>(3, 3) << 0, 1, 0, 1, 0, 1, 0, 1, 0) / (float)(4);
filter2D(srcImage, resultImage, srcImage.depth(), kern);
return resultImage;
}
int main() {
cv::Mat srcImage = cv::imread("lena.jpg");
if (!srcImage.data)
return 0;
cv::Mat srcGray;
cvtColor(srcImage, srcGray, COLOR_BGR2GRAY);
imshow("srcGray", srcGray);
cv::Mat resultImage = Myfilter2D(srcGray);
imshow("resultImage", resultImage);
cv::Mat resultImage2 = filter2D_(srcGray);
imshow("resultImage2", resultImage2);
cv::waitKey(0);
return 0;
}

测试效果:

filter2d

测试代码2:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Program to apply a simple filter matrix to an image
// Author: Samarth Manoj Brahmbhatt, University of Pennsylvania

//---------------------------------【头文件、命名空间包含部分】----------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main() {

// Mat img = imread("image.jpg", CV_LOAD_IMAGE_GRAYSCALE), img_filtered;
Mat img = imread("lena.jpg"), img_filtered;

// 滤波器内核,用于检测垂直边缘(Filter kernel for detecting vertical edges )
float vertical_fk[ 5 ][ 5 ] = {
{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {-1, -2, 6, -2, -1}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};

// 滤波器内核,用于检测水平边缘(Filter kernel for detecting horizontal edges )
float horizontal_fk[ 5 ][ 5 ] = {
{0, 0, -1, 0, 0}, {0, 0, -2, 0, 0}, {0, 0, 6, 0, 0}, {0, 0, -2, 0, 0}, {0, 0, -1, 0, 0}};

Mat filter_kernel = Mat(5, 5, CV_32FC1, vertical_fk);

//应用滤波器
filter2D(img, img_filtered, -1, filter_kernel);

namedWindow("Image");
namedWindow("Filtered image");
imshow("Image", img);
imshow("Filtered image", img_filtered);

imwrite("filtered_image.jpg", img_filtered);

while (char(waitKey(1)) != 'q') {
}

return 0;
}

测试效果:

filter2d

下一篇:OpenCV4入门系列教程52:添加噪声