本文主要介绍了以下几点内容:
- 什么是中值滤波以及中值滤波的基本原理
- 中值滤波有何特点以及应用场景是什么
- 中值滤波的简单算法实现以及优化后的高效中值滤波
中值滤波
在图像处理中,在进行图像处理操作之前往往要对图像进行滤波操作。中值滤波为一种非线性的数字滤波器,它的原理基于用像素点邻域点集像素值的中值代替像素点的值,从而消除孤立的噪声点。中值滤波是非线性的、对斑点噪声和椒盐噪声的滤波处理效果比较好,只要选取合适的阈值阀,中值滤波能在保留较好的边缘下降噪。
斑点噪声是SAR成像系统的一大特色,源自基本分辨单元内地物的随机散射,在图像上表现为信号相关(如在空间上相关)的小斑点,它既降低了图像的画面质量,又严重影响图像的自动分割、分类、目标检测以及其它定量专题信息的提取 。
椒盐噪声也称为脉冲噪声,是图像中经常见到的一种噪声,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。椒盐噪声的成因可能是影像讯号受到突如其来的强烈干扰而产生、模数转换器或位元传输错误等。例如失效的感应器导致像素值为最小值,饱和的感应器导致像素值为最大值。
中值滤波算法步骤以及代码实现
基于中值滤波的设计思想,算法步骤非常简单
(1) 用一个滑动窗口去遍历图像,这个滑动窗口的范围就是像素点的邻域。
(2) 获取滑动窗口的像素值集合,并且排序,得到中值替换原像素点。
(3) 遍历图像重复步骤(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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
// This main.cpp
// median filtering sample
// author:mango
// copyright: https://mangoroom.cn
#include<iostream>
#include<vector>
#include<opencv2/opencv.hpp>
// median filtering
void MedianFilter(const cv::Mat& input_image, cv::Mat& output_image, const int& kernel_size)
{
// 输入参数检查
if (input_image.empty())
{
throw "Input image is empty!!!";
}
else if(input_image.channels() != 1)
{
throw "Input image not be gray!!!";
}
// 遍历图像
output_image = input_image.clone();
int rows = input_image.rows;
int cols = input_image.cols;
std::vector<int> filter_windows(kernel_size*kernel_size, 0);
for (auto i = kernel_size / 2; i < rows - (kernel_size / 2); i++)
{
for (auto j = kernel_size / 2; j < cols - (kernel_size / 2); j++)
{
// 滤波窗口元素排序
int index = 0;
for (auto m = 0; m < kernel_size; m++)
{
for (auto n = 0; n < kernel_size; n++)
{
filter_windows.at(index) = input_image.at<uchar>(i - kernel_size / 2 + m, j - kernel_size / 2 + n);
index++;
}
}
std::sort(filter_windows.begin(), filter_windows.end());
// 更新图像像素
output_image.at<uchar>(i, j) = filter_windows.at(kernel_size * kernel_size / 2);
}
}
}
int main()
{
cv::Mat img = cv::imread("Noise_salt_and_pepper.png", 0);
cv::Mat dst;
MedianFilter(img, dst, 3);
cv::imshow("img", img);
cv::imshow("dst", dst);
cv::waitKey(0);
return 0;
}
|
但是以上直白思路的算法效率是非常低的,每个滑动窗口中的像素点每一次都需要重新排序,假如窗口选取比较大和图像比较大,显然这开销是巨大的。我们发现窗口每一次移动的时候,窗口内容丢掉的只是最左侧的一列而新增的是最右侧的一例,对于窗口的其他像素点并没有发生变化,不需要重新排序。此优化的算法步骤如下:
(1)置$t = \frac{mn}{2}$
如果m和n都为奇数,则对t取整,这样我们总是可以避免不必要的浮点数运算。
(2)将窗口移至一个新行的开始,对其内容排序。建立窗口像素的直方图H,确定其中值m,记下亮度小于或者等于m的像素数目$n_m$。
(3)对于最左列亮度是$p_g$的每个像素p,做
$$H[p_g] = H[p_g] - 1$$
进一步,如果$p_g \leq m$, 置$n_m = n_m-1$。
(4)将窗口右移一列,对于最右列亮度是$p_g$的每个像素$p$,做
$$H[p_g] = H[p_g] + 1$$
如果$p_g \leq m$, 置$n_m = n_m + 1$。
(5)如果$n_m = t$,则跳转至步骤8.
(6)如果$n_m > t$ 则跳转至步骤7。
重复
$$m = m + 1$$
$$n_m = n_m + H[m]$$
直到$n_m \geq t$则跳转至步骤8。
(7)(此时有$n_m > t$。重复
$$n_m = n_m - H[m]$$
$$m = m - 1$$
直到$n_m \leq t$。
(8) 如果有窗口的右侧列不是图像的有边界,则跳转至步骤3。
(9) 如果窗口的底行不是图像的下边界,跳转至步骤2.
references
【1】图像处理、分析与机器视觉4th-page135
【2】中值滤波-维基百科
【3】斑点噪声-百度知道
【4】椒盐噪声-维基百科