0%

高斯滤波&均值滤波&中值滤波

​ 图像滤波,即在尽量保留图像细节特征的条件下对噪声进行抑制,通过抑制高频段来减少噪音,同时会照成图像一定程度上的模糊,这也叫做平滑或者低通滤波器。

滤波

  滤波本来应该是在傅立叶变换的频谱上对图像进行处理,但由于傅立叶的卷积特性

滤波后的图像可以由原图像和滤波算子做卷积生成(频域的乘积 => 空域的卷积)

高斯滤波

​ 高斯滤波器是利用高斯核的一个2维的卷积算子(线性滤波器),对于抑制服从正态分布的噪声非常有效。

高斯分布

一维高斯分布

  其中,σ描述正态分布资料数据分布的离散程度,σ越大,数据分布越分散,σ越小,数据分布越集中。也称为是正态分布的形状参数,σ越大,曲线越扁平,反之,σ越小,曲线越瘦高。

二维高斯分布

高斯核

  高斯核主要有两个参数:高斯核的大小、离散程度σ

1
h = fspecial('gaussian',hsize,sigma)

高斯核的大小:按高斯分布,理论上需要一个无限大的卷积核。但实际上,仅需要取均值周围3倍标准差内的值(高斯核单边大小为3σ),以外部分直接去掉即可。(没必要用很大的高斯核)

离散程度σ:σ越大,数据分布越分散,平滑程度越强,对高频的抑制程度更大。

  高斯滤波再高斯滤波还是高斯滤波,两个σ的高斯滤波相当于一次$\sqrt{2}\sigma$。

Matlab实现

Gauss
1
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('高斯滤波后的频谱图');
Gauss
1
2
3
4
5
6
7
function F = DFT(img)
% 离散傅里叶变换频谱图
img_Double = double(img);% 将灰度图归一化处理
F = fft2(img_Double); % 二维快速傅里叶变换
F = fftshift(F); % FFT频谱平移
F = log(1+abs(F)); % 频谱对数变换(由于幅度值范围很大,所以要取对数处理)
end

  可以看出图像的高频部分受到了抑制,但并不是完全截断了高频。空域中的高斯到频域中依旧是高斯。

  

openCV实现(openCV提供的函数)

cv :: GaussianBlur

​ openCV自带的高斯滤波器:cv :: GaussianBlur

  • 库文件:#include <opencv2/imgproc.hpp>
  • 作用:该函数将源图像与指定的高斯内核进行卷积。
  • 调用方法
void cv::GaussianBlur ( InputArray src,
OutputArray dst,
Size ksize,
double sigmaX,
double sigmaY = 0,
int borderType = BORDER_DEFAULT
)
  • 参量
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;
}

// 高斯滤波,高斯核大小为5*5
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;

/*
利用5*5的高斯核进行高斯滤波
img 输入原图像
dst 高斯滤波后的输出图像
*/
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},
}; //5*5的高斯核

// 与5*5的高斯核做卷积
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;
}

// 高斯滤波,高斯核大小为5*5
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>
  • 作用:使用归一化框滤波模糊图像。
  • 调用方法
void cv::blur ( InputArray src,
OutputArray dst,
Size ksize,
Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT
)
  • 参数
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;
}

// 均值滤波,核大小为5*5
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;

/*
利用5*5的均值滤波
img 输入原图像
dst 均值滤波后的输出图像
*/
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},
}; //5*5的核

// 与5*5的核做卷积
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;
}

// 均值滤波,核大小为5*5
Blur(src, dst);

imshow("dst",dst);
imwrite("box.jpg", dst);
waitKey();
return 0;
}

结果

中值滤波

​ 中值滤波是一种非线性滤波器,利用采集领域的中值的方法,对噪声进行抑制。尤其对于椒盐噪声有用。

openCV实现(openCV提供的函数)

cv::medianBlur

​ openCV自带的均值滤波器:cv::medianBlur

  • 库文件:#include <opencv2/imgproc.hpp>
  • 作用:使用中值滤波模糊图像。
  • 调用方法
void cv::medianBlur ( InputArray src,
OutputArray dst,
int ksize
)
  • 参数
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;
}

// 中值滤波,领域大小为5*5
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;

/*
中值滤波
img 输入原图像
dst 中值滤波后的输出图像
ksize 领域大小
*/
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;
}

// 中值滤波,领域大小为5*5
median_blur(src, dst, 5);
imshow("dst",dst);
imwrite("median.jpg", dst);
waitKey();
return 0;
}

结果

对比

概述

​ 从Lenna图无法看出上面三种滤波太大的区别,但可以明显看出在同样的领域大小下,高斯滤波的模糊化程度小于其余两种。

​ 高斯滤波和均值滤波均是线性滤波,而中值滤波为非线性滤波。

​ 这三种滤波适合处理的噪声不同,高斯滤波和均值滤波更适宜抑制高斯噪声,尤其是高斯滤波最适合高斯噪声,而中值滤波更适合抑制椒盐噪声。

高斯噪声的抑制程度对比

​ 这是一张加入高斯噪声的图像。

​ 下面分别该高斯噪声图像是用5*5的高斯滤波、均值滤波、中值滤波处理后的图像。

椒盐噪声的抑制程度对比

​ 这是一张加入椒盐噪声的图像。

​ 下面分别该椒盐噪声图像是用5*5的高斯滤波、均值滤波、中值滤波处理后的图像。