图像处理中的滤波

#滤波

滤波(Wave filtering)是将信号中特定波段频率滤除的操作,是抑制和防止干扰的一项重要措施。在图像处理中,滤波是图像预处理的一种。图像处理中滤波将信号中特定的波段频率滤除,从而保留所需要的波段频率信号。根据选择保留的不同频段可以体现这么两个作用

消除图像中混入的噪声 对应的是低通滤波,噪声在图像中一般是高频信号。 为图像识别抽取出图像特征 这里的特征一般为边缘纹理的特征,对应的是高通滤波,图像中边缘和纹理细节是高频信号。

#滤波的分类

图像中滤波算法的分类有很多,可以分为线性滤波和非线性滤波,可以分为相关滤波和卷积滤波,还可以分为高通滤波和低通滤波,空间滤波和频域滤波。

线性滤波和非线性滤波

  • 线性滤波

维基百科解释为:用于时变输入信号的线性运算,在图像处理中可以这么理解,对于输入的信号(即要处理的图像),进行的是线性的运算,得出的结果作为输出图像。可以参考下图 线性滤波原理

首先有一个滤波器的模板(这里是3x3大小),模板里有系数,f(x,y)的值等于模板系数与f(x,y)周围一的像素点相乘求和,这个运算是线性的。在线性的滤波器中,运算的不同即为滤波器的系数模板不同。

线性滤波的包含方框滤波、均值滤波、高斯滤波、拉普拉斯滤波、sobel算子等。

  • 非线性滤波

输出的信号响应是由输入经过非线性的运算得到的。比如典型的中值滤波,就是取像素点邻域的中值作为像素的的响应输出。

非线性滤波包含中值滤波和双边滤波。

卷积滤波和相关滤波

首先要注意的是卷积滤波和相关滤波都属于线性滤波,两者的区别是加权系数的对应相乘顺序有所不同。

卷积和相关

高通滤波和低通滤波

高通滤波与低通滤波之分是相对于滤波的目的而言的,简而言之,高通滤波器就是去除图像中的低频部分,保留高频。表现就是经高通滤波后,保留了图像的高频边缘和纹理细节,所以高通滤波对应的是图像的锐化。低通滤波则是相反的,处理的结果是保留低频部分去除高频部分,在图像上的表现是纹理细节都被模糊了,所以低通滤波对应的是图像的平滑模糊。

空间滤波和频域滤波 空间滤波即直接在像素坐标上对图像数据进行处理滤波,频域滤波则是先把图像由空间域变换到频域,在频域进行处理,结束以后再由频域变换会空间域。

空间滤波和频域滤波.png

下面是几个具体的滤波算法的例子

##方框滤波 方框滤波,线性滤波里面最简单的一个。即用一个已确定的模板系数去与像素点领域相乘,所得结果即为像素点的响应。

均值滤波器模板

##均值滤波

均值滤波则是方框滤波的特例,将方框滤波的系数模板归一化之后便是均值滤波

均值滤波模板

可以看到,均值滤波的结果是取像素点周围领域的平均值作为响应输出。

##高斯滤波

高斯滤波的模板系数就稍显复杂,其模板系数是服从高斯分布的。

先看看一维的高斯分布

一维高斯分布

二维的高斯分布 二维高斯分布

所以高斯滤波的模板系数,应该服从上图中的锥形山峰。那如何计算高斯分布的模板系数呢,我们取均值为模板的中心点,计算公式如下 高斯滤波模板系数计算公式

这里附上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::vector<std::vector<double> >getModel(const int& n, const double& sigmma)//求解nxn的模板系数
{
const double pi = 3.1415926;
const double weight = 1.0 /(2.0 * pi * sigmma * sigmma);//gaussion公式中的系数

double sum = 0.0;
std::vector<std::vector<double>> res(n, std::vector<double>(n, 0.0));

for(int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
{
res[i][j] = weight * std::exp(-((i - n / 2) * (i - n / 2) + (j - n / 2)*(j - n / 2)) / (2.0*sigmma*sigmma));//n/2为公式中都均值,即原点在图像中心
}

return res;
}

假如不想手动计算高斯滤波的模板系数,也可以取模板的近似值作为模板。如3x3和5x5的模板可以取

高斯滤波模板

##拉普拉斯算子

拉普拉斯滤波属于高通滤波,算子定义为

拉普拉斯算子定义

下面由算子推导计算模板系数 在x方向上,二阶导数的微分可以由差分近似表示,有

x方向

类似地,在y方向上有

y方向

所以拉普拉斯算子在离散的情况下,可以近似为 离散拉普拉斯

那么可以得矩阵的模板系数

拉普拉斯模板系数

还有一般常用的拉普拉斯模板

拉普拉斯模板

##Roberts算子

Roberts算子比较简单,使用2x2的领域 Roberts算子

计算响应值的方式如下

Roberts算子计算

##sobel算子

sobel算子

##Robinson算子

Robinson算子

##Kirsch算子

Kirsch算子

以上所有有的算子都属于线性滤波,在得到离散情况下的算子模板系数之后,就可以用这个模板去对图像进行操作,使用模板系数去遍历图像,操作可以选择卷积或者相关。这些算子也称卷积掩模。

这里先给出二维卷积的代码实现

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
//二维卷积的实现
#include<cassert>
#include<vector>


void conv2(int** filter, int **mat, int** res, const int filter_rows, const int filter_cols, const int mat_rows, const int mat_cols);//指针数组版本
std::vector<std::vector<int> > conv2(std::vector<std::vector<int> > filter, std::vector<std::vector<int> > mat);//向量版本


int main(void)
{
return 0;
}//main

void conv2(int** filter, int **mat, int** res, const int filter_rows, const int filter_cols, const int mat_rows, const int mat_cols)
{
assert(filter_cols < mat_cols && filter_rows < mat_rows);
for(int i = 0; i < mat_rows - 1; ++i)
for (int j = 0; j < mat_cols - 1; ++j)
{
int tmp = 0;
for (int m = 0; m < filter_rows; ++m)
for (int n = 0; n < filter_cols; ++n)
if(0 <= i -m && i - m < mat_rows && 0 <= j - n && j - n < mat_cols)
tmp += filter[m][n] * mat[i - m][j - n];//卷积公式

res[i][j] = tmp;
}
}

std::vector<std::vector<int> > conv2(std::vector<std::vector<int> > filter, std::vector<std::vector<int> > mat )//向量版本
{
const int filter_rows = filter.size();
const int filter_cols = filter[0].size();

const int mat_rows = mat.size();
const int mat_cols = mat[0].size();

assert(filter_cols < mat_cols && filter_rows < mat_rows);
std::vector<std::vector<int> > res(mat_rows, std::vector<int>(mat_cols, 0));

for (int i = 0; i < mat_rows - 1; ++i)
for (int j = 0; j < mat_cols - 1; ++j)
{
int tmp = 0;
for (int m = 0; m < filter_rows; ++m)
for (int n = 0; n < filter_cols; ++n)
if (0 <= i - m && i - m < mat_rows && 0 <= j - n && j - n < mat_cols)
tmp += filter[m][n] * mat[i - m][j - n];//卷积公式

res[i][j] = tmp;
}
return res;
}

然后以高斯滤波为例,给出这些模板系数卷积滤波的实现

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
void mycv::gaussianFilter(cv::Mat& src, cv::Mat& dst)
{
const int rows = src.rows;
const int cols = src.cols;
dst = cv::Mat(src.size(), src.type(), cv::Scalar::all(0));

std::vector<std::vector<double>> gauss = getModel(3);//3x3模板系数

switch (src.channels())
{
case 1://灰度图
for(int i = 0; i < rows -1; ++i)
for (int j = 0; j < cols - 1; ++j)
{
double tmp = 0.0;
for(int m = 0; m < gauss.size(); ++m)
for (int n = 0; n < gauss[0].size(); ++n)
{
if (i - m >= 0 & i - m < rows && j - n >= 0 && j - n < cols)
tmp += gauss[m][n] * static_cast<double>(src.at<uchar>(i - m, j - n));//卷积公式
}

dst.at<uchar>(i, j) = static_cast<int>(tmp);
}
break;
case 3://彩色
for (int i = 0; i < rows - 1; ++i)
for (int j = 0; j < cols - 1; ++j)
for (int k = 0; k < 3; ++k)
{
double tmp = 0.0;
for (int m = 0; m < gauss.size(); ++m)
for (int n = 0; n < gauss[0].size(); ++n)
if (i - m >= 0 & i - m < rows && j - n >= 0 && j - n < cols)
tmp += gauss[m][n] * static_cast<double>(src.at<cv::Vec3b>(i - m, j - n)[k]);//卷积公式

dst.at<cv::Vec3b>(i, j)[k] = static_cast<int>(tmp);
}
break;
default:
break;
}
}

-------------本文结束感谢您的阅读-------------