Exercise3——空间域滤波


前言

在图片处理当中,总会遇到一些图片拥有各种各样的噪点,这些东西细微,但是却又无时无刻的影响着你去处理这张图片。因此,我们就拥有了图片平滑的操作。

图片平滑

图片平滑主要是去除噪音或者模糊图像,也就是去除图片当中的细小的细节或弥合目标之间的间隙。

从信号频率来讲,信号变化缓慢的地方为低频,信号变化剧烈的地方为高频。(比如图片的边缘,灰度的跳跃,噪声的变化,这些都可能造成灰度的剧烈变化,也就是信号的剧烈变化)

那么,只要我们在空间域或者信号域将低频的信号通过,高频的信号滤走,既可以实现图片的平滑了。

噪声

何为噪声呢,噪声可以理解为影响我们看图片的理解其信息的因素。

噪声的出现一般都是因为图片在获取,存储,处理和传输过程当中受到的电气系统或者外界的影响出现的。

因此,我们也可以将噪声理解为不可预测的随机误差,可以看作是一个随机过程,因此,我们可以用概率论与数理统计的方法将其描述出来,同样,也给了我们消除噪声的可能。

噪声的分类

噪声的分类有很多种,可以根据其产生原因,统计特性,幅度分布,噪声频率,噪声与信号的关系等等来分类。

而噪声与信号的分类可以分为乘性噪声和加性噪声

假设信号为$S(t)$,噪声为$N(t)$,那么

为乘性噪音

为加性噪音

而由于加性噪音与信号不相关,因此,大多情况我们都是将乘性噪音近似为加性来处理的。

卷积模板

卷积模板是一种领域运算的方式,主要有卷积和相关两种,可以实现图片的平滑,锐化,边缘检测等等功能

一般是用矩阵表示,用于定义参与了领域运算的相对位置与相关系数

看上去很复杂,实际是这条公式也相当于(假设这是一个3x3的邻域运算)

注意,虽然写成了矩阵的形式,但实际上不是矩阵运算,而是将每一个数值的结果相加。而$w(x,y)$与$f(x,y)$分别表示运算的因子与对应点上的灰度级别。

边界问题

由于领域运算使用了一个像素点以及其周围的点来运算,因此,会出现一些边界上的问题,比如在第一行的时候就没有第零行给他去运算了。

一般来说,在这个时候可以将第一行的值当作第零行去运算,也就是扩充图像,复制图像的边界的像素值来扩充边界使得在边界时仍然可算

也可以直接忽视第一行,直接对第二行开始进行运算从而避免边界问题。也就是对边界像素不处理

矩阵的大小

一边来说,领域运算的窗口大小为3x3,但是取5x5或者2x2等等的其他值也不是不可以。

运算复杂度

卷积运算的复杂度是很高的,因为对于每一个像素点,都要取其领域进行一次运算。以3x3的窗口为例,一个像素点就要进行9次乘法,8次加法以及一次除法,那么对于一个NxN的图片来说时间复杂度就是o(n^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
IMGDATA advenage(IMGDATA data)
{
BYTE * newData = new BYTE[data.length];

for (int i = 0; i < data.height; i++)
{
for (int j = 0; j < data.width; j++)
{
int up, down,left,right;
if (i == 0)
up = 0;
else
up = i - 1;

if (i == data.height - 1)
down = data.height - 1;
else
down = i + 1;

if (j == 0)
left = 0;
else
left = j - 1;

if (j == data.width - 1)
right = data.width - 1;
else
right = j + 1;
//领域计算
newData[i * data.width + j] = clamp(
(data.pImg[up * data.width + left] + 2 * data.pImg[up * data.width + j] + data.pImg[up * data.width + right] +
2 * data.pImg[i * data.width + left] + 4 * data.pImg[i * data.width + j] + 2 * data.pImg[i * data.width + right] +
data.pImg[down * data.width + left] + 2 * data.pImg[down * data.width + j] + data.pImg[down * data.width + right]) / 16);
}

}

delete[] data.pImg;
data.pImg = newData;
return data;
}

中值滤波

中值滤波与平均滤波的原理差不多,都是用于除去突变像素的。

中值滤波是统计排序滤波器的一种。

他的思路则是先获取一个像素点的领域的所有的点,然后,再对其进行排序,当取中值为输出值时则是中值滤波,当取最大值时为最大值滤波,取最小值时为最小值滤波。

现在是中值滤波,自然要取中间值了。

代码实现

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
//这里取的是3x3的领域
IMGDATA mid(IMGDATA data)
{
BYTE *newData = new BYTE[data.length];
for(int i = 1;i < data.height - 1;i++)
{
for(int j = 1;j < data.width - 1;j++)
{
int arr[9] = {
data.pImg[(i - 1) * data.width + j - 1],data.pImg[(i - 1) * data.width + j],data.pImg[(i - 1)*data.width + j + 1],
data.pImg[(i)* data.width + j - 1],data.pImg[(i)* data.width + j],data.pImg[(i)*data.width + j + 1],
data.pImg[(i + 1) * data.width + j - 1],data.pImg[(i + 1) * data.width + j],data.pImg[(i + 1)*data.width + j + 1] };
newData[i * data.width + j] = getMid(arr);
//delete[] arr;
}
}


IMGDATA newImg = data;
newImg.pImg = newData;
return newImg;
}



int getMid(int arr[9])
{
for(int i = 0;i < 9;i++)
{
for(int j = 0 ;j < 8;j++)
{
if(arr[j] > arr[j + 1])
{
const int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr[4];
}

总结

当然,这两个滤波器想要实现比较好的效果都是要进行多次滤波的。不过,当滤波的次数一多,就会出现图像比较模糊的现象,毕竟这两种滤波本质上都是对图片进行平滑化处理嘛,自然会模糊图片了。

图片锐化

图片锐化,指的是增强图片当中的细节或者是被模糊的了的细节。

锐化的处理可以用空间微分解决,因为,微分算子的强度与当前的图像点的突变强度有关,而突变强度强的地方也就是图片当中的细节。

在图片处理当中,一般有两种微分方法,一阶与二阶

对于大部分图像增强来说,二阶微分比一阶要好,因为二阶微分展现的细节比较强,而一阶微分通常用于提取图像边缘

拉普拉斯算子

对于二阶图像函数$f(x,y)$,拉普拉斯变换定义为

换作算子的矩阵形式就是

或者是其扩展版,也就是包含了对角线领域的版本

有了公式实现也就很简单了

代码实现

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
IMGDATA laplace(IMGDATA data)
{
BYTE * newData = new BYTE[data.length];

for (int i = 0; i < data.height; i++)
{
for (int j = 0; j < data.width; j++)
{
int up, down, left, right;
if (i == 0)
up = 0;
else
up = i - 1;

if (i == data.height - 1)
down = data.height - 1;
else
down = i + 1;

if (j == 0)
left = 0;
else
left = j - 1;

if (j == data.width - 1)
right = data.width - 1;
else
right = j + 1;

newData[i * data.width + j] = clamp(
1 * data.pImg[up * data.width + left] + 1 * data.pImg[up * data.width + j] + 1 * data.pImg[up * data.width + right] +
1 * data.pImg[i * data.width + left] + -8 * data.pImg[i * data.width + j] + 1 * data.pImg[i * data.width + right] +
1 * data.pImg[down * data.width + left] + 1 * data.pImg[down * data.width + j] + 1 *data.pImg[down * data.width + right]);
}

}

IMGDATA imgData = data;

imgData.pImg = newData;
return imgData;
}

效果大致就是这样

当然这样还不算完,这只是提取出图片的细节地方而已。然后就是对细节的增强了。

只是简单的相加而已

1
2
//我的中间系数为负
laplaceIMG = data + (laplaceIMG * -1);
  • 本文作者: ShinyGX
  • 本文链接: https://ShinyGX.github.io/posts/12b12cb9/
  • 版权声明: 本博客所有文章除特别声明外,均采用 https://creativecommons.org/licenses/by-nc-sa/3.0/ 许可协议。转载请注明出处!
0%