中值滤波

本文主要介绍了以下几点内容:

  • 什么是中值滤波以及中值滤波的基本原理
  • 中值滤波有何特点以及应用场景是什么
  • 中值滤波的简单算法实现以及优化后的高效中值滤波

中值滤波

在图像处理中,在进行图像处理操作之前往往要对图像进行滤波操作。中值滤波为一种非线性的数字滤波器,它的原理基于用像素点邻域点集像素值的中值代替像素点的值,从而消除孤立的噪声点。中值滤波是非线性的、对斑点噪声椒盐噪声的滤波处理效果比较好,只要选取合适的阈值阀,中值滤波能在保留较好的边缘下降噪。

  • 斑点噪声

斑点噪声是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.

滤波效果.png

references

【1】图像处理、分析与机器视觉4th-page135 【2】中值滤波-维基百科 【3】斑点噪声-百度知道 【4】椒盐噪声-维基百科


微信公众号