图像滤波,即在尽量保留图像细节特征的条件下对噪声进行抑制,通过抑制高频段来减少噪音,同时会照成图像一定程度上的模糊,这也叫做平滑或者低通滤波器。
滤波
滤波本来应该是在傅立叶变换的频谱上对图像进行处理,但由于傅立叶的卷积特性
滤波后的图像可以由原图像和滤波算子做卷积生成(频域的乘积 => 空域的卷积)
高斯滤波
高斯滤波器是利用高斯核的一个2维的卷积算子(线性滤波器),对于抑制服从正态分布的噪声非常有效。
高斯分布
一维高斯分布
其中,σ描述正态分布资料数据分布的离散程度,σ越大,数据分布越分散,σ越小,数据分布越集中。也称为是正态分布的形状参数,σ越大,曲线越扁平,反之,σ越小,曲线越瘦高。
二维高斯分布
高斯核
高斯核主要有两个参数:高斯核的大小、离散程度σ
1
| h = fspecial('gaussian',hsize,sigma)
|
高斯核的大小:按高斯分布,理论上需要一个无限大的卷积核。但实际上,仅需要取均值周围3倍标准差内的值(高斯核单边大小为3σ),以外部分直接去掉即可。(没必要用很大的高斯核)
离散程度σ:σ越大,数据分布越分散,平滑程度越强,对高频的抑制程度更大。
高斯滤波再高斯滤波还是高斯滤波,两个σ的高斯滤波相当于一次$\sqrt{2}\sigma$。
Matlab实现
Gauss1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| img = imread('Lenna.jpg'); img = rgb2gray(img); sigma = 1; gausFilter = fspecial('gaussian', [5,5], sigma); gaus= imfilter(img, gausFilter, 'replicate'); subplot(2,3,1),imshow(img,[]),title('原图'); subplot(2,3,2),imshow(gausFilter,[]),title('高斯核'); subplot(2,3,3),imshow(gaus,[]),title('高斯滤波后的图像');
F1 = DFT(img); F2 = DFT(gausFilter); F3 = DFT(gaus); subplot(2,3,4),imshow(F1,[]),title('原图的频谱图'); subplot(2,3,5),imshow(F2,[]),title('高斯核的频谱图'); subplot(2,3,6),imshow(F3,[]),title('高斯滤波后的频谱图');
|
Gauss1 2 3 4 5 6 7
| function F = DFT(img)
img_Double = double(img); F = fft2(img_Double); F = fftshift(F); F = log(1+abs(F)); end
|
可以看出图像的高频部分受到了抑制,但并不是完全截断了高频。空域中的高斯到频域中依旧是高斯。
openCV实现(openCV提供的函数)
cv :: GaussianBlur
openCV自带的高斯滤波器:cv :: GaussianBlur
- 库文件:
#include <opencv2/imgproc.hpp>
- 作用:该函数将源图像与指定的高斯内核进行卷积。
- 调用方法
src |
输入图像;图像可以具有任意数量的通道,这些通道可以独立处理,但深度应为CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。 |
dst |
输出与src大小和类型相同的图像。 |
size |
高斯核大小。ksize.width和ksize.height可以不同,但它们都必须为正数和奇数。或者,它们可以为零,然后根据sigma计算得出。 |
sigmaX |
X方向上的高斯核标准偏差。 |
sigmaY |
Y方向的高斯核标准差;如果sigmaY为零,则将其设置为等于sigmaX;如果两个sigmas为零,则分别从ksize.width和ksize.height计算得出(有关详细信息,请参见getGaussianKernel);为了完全控制结果,而不管将来可能对所有这些语义进行的修改,建议指定所有ksize,sigmaX和sigmaY。 |
borderType |
像素外推方法,请参见BorderTypes |
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <opencv2/opencv.hpp> #include <opencv2/imgproc.hpp> using namespace cv;
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst; if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; } GaussianBlur(src, dst, Size(5,5), 0);
imshow("dst",dst); imwrite("gas_openCV.jpg", dst); waitKey(); return 0; }
|
结果
原图像和经高斯滤波后图像的傅立叶变换频谱
C++实现(函数自己编写)
如利用5*5的高斯核,对图像进行卷积。
代码
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
| #include <opencv2/opencv.hpp> using namespace cv;
void Gaussian_Blur(Mat &img, Mat &dst){ int Kernel[5][5] = { {1, 4, 7, 4, 1}, {4,16,26,16, 4}, {7,26,41,26, 7}, {4,16,26,16, 4}, {1, 4, 7, 4, 1}, }; for (int nrow = 2; nrow < img.rows-2; nrow++) { for (int ncol = 2; ncol < img.cols-2; ncol++) { int point = 0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { point += Kernel[i][j] * img.ptr<uchar>(nrow+i-2)[ncol+j-2]; } } dst.ptr<uchar>(nrow)[ncol] = point/273; } } }
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst = src.clone(); if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; } GaussianBlur(src, dst, Size(5,5), 0);
imshow("dst",dst); imwrite("gas.jpg", dst); waitKey(); return 0; }
|
结果
该高斯滤波结果与openCV滤波结果基本一致。
均值滤波
均值滤波是空间域线性滤波器,其中所得到的图像中的每个像素具有的值等于其邻近的像素的输入图像中的平均值。它是低通(“模糊”)滤波器的一种形式。
openCV实现(openCV提供的函数)
cv::blur
openCV自带的均值滤波器:cv::blur
- 库文件:
#include <opencv2/imgproc.hpp>
- 作用:使用归一化框滤波模糊图像。
- 调用方法
src |
输入图像;它可以具有任意数量的通道,这些通道可以独立处理,但深度应为CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。 |
dst |
输出与src大小和类型相同的图像。 |
大小 |
内核大小模糊。 |
锚 |
锚点 默认值Point(-1,-1)表示锚点位于内核中心。 |
borderType |
用于推断图像外部像素的边框模式 |
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <opencv2/opencv.hpp> #include <opencv2/imgproc.hpp> using namespace cv;
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst; if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; }
blur(src, dst, Size(5, 5));
imshow("dst",dst); imwrite("box_openCV.jpg", dst); waitKey(); return 0; }
|
结果
原图像和经均值滤波后图像的傅立叶变换频谱
C++实现(函数自己编写)
利用5*5的均值滤波器
代码
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
| #include <opencv2/opencv.hpp> using namespace cv;
void Blur(Mat &img, Mat &dst){ int Kernel[5][5] = { {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1}, }; for (int nrow = 2; nrow < img.rows-2; nrow++) { for (int ncol = 2; ncol < img.cols-2; ncol++) { int point = 0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { point += Kernel[i][j] * img.ptr<uchar>(nrow+i-2)[ncol+j-2]; } } dst.ptr<uchar>(nrow)[ncol] = point/25; } } }
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst = src.clone(); if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; } Blur(src, dst);
imshow("dst",dst); imwrite("box.jpg", dst); waitKey(); return 0; }
|
结果
中值滤波
中值滤波是一种非线性滤波器,利用采集领域的中值的方法,对噪声进行抑制。尤其对于椒盐噪声有用。
openCV实现(openCV提供的函数)
openCV自带的均值滤波器:cv::medianBlur
- 库文件:
#include <opencv2/imgproc.hpp>
- 作用:使用中值滤波模糊图像。
- 调用方法
src |
输入1、3或4通道图像; 当ksize为3或5时,图像深度应为CV_8U,CV_16U或CV_32F,对于较大的光圈,只能为CV_8U。 |
dst |
与src具有相同大小和类型的目标数组。 |
大小 |
孔径线性尺寸;它必须是奇数且大于1,例如:3、5、7 … |
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <opencv2/opencv.hpp> #include <opencv2/imgproc.hpp> using namespace cv;
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst; if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; } medianBlur(src, dst, 5);
imshow("dst",dst); imwrite("median_openCV.jpg", dst); waitKey(); return 0; }
|
结果
C++实现(函数自己编写)
代码
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
| #include <opencv2/opencv.hpp> using namespace cv;
void median_blur(Mat &img, Mat &dst, int ksize){ for (int nrow = 2; nrow < img.rows-2; nrow++) { for (int ncol = 2; ncol < img.cols-2; ncol++) { vector<int> points; for (int i = 0; i < ksize; i++) { for (int j = 0; j < ksize; j++) { points.push_back(img.ptr<uchar>(nrow+i-2)[ncol+j-2]); } }
for (int i = 0; i < points.size()/2+1; i++) { int minIndex = i; int j; for (j = i+1; j < points.size(); j++) { if (points[j] < points[i]) { minIndex = j; } } int temp = points[i]; points[i] = points[minIndex]; points[minIndex] = temp; } dst.ptr<uchar>(nrow)[ncol] = points[points.size()/2]; } } }
int main() { Mat src = imread("Lenna.jpg", IMREAD_GRAYSCALE); Mat dst = src.clone(); if (src.empty()) { printf("读取图像文件失败"); system("pause"); return 0; } median_blur(src, dst, 5); imshow("dst",dst); imwrite("median.jpg", dst); waitKey(); return 0; }
|
结果
对比
概述
从Lenna图无法看出上面三种滤波太大的区别,但可以明显看出在同样的领域大小下,高斯滤波的模糊化程度小于其余两种。
高斯滤波和均值滤波均是线性滤波,而中值滤波为非线性滤波。
这三种滤波适合处理的噪声不同,高斯滤波和均值滤波更适宜抑制高斯噪声,尤其是高斯滤波最适合高斯噪声,而中值滤波更适合抑制椒盐噪声。
高斯噪声的抑制程度对比
这是一张加入高斯噪声的图像。
下面分别该高斯噪声图像是用5*5的高斯滤波、均值滤波、中值滤波处理后的图像。
椒盐噪声的抑制程度对比
这是一张加入椒盐噪声的图像。
下面分别该椒盐噪声图像是用5*5的高斯滤波、均值滤波、中值滤波处理后的图像。