我想学过图像处理的人没有人会不知道中值滤波的,最早的时候我是在冈萨雷斯的图像处理课本[1]中学到的,后来在看Sonka的书[2]的时候又看到了中值滤波的介绍,下面我试着结合课本所学和网上的资料自己整理一篇中值滤波的介绍。
中值滤波器是一种统计排序滤波器,由Tukey于1971年在文献[3]中提出。所谓的统计排序滤波器是一种非线性的空间滤波器,它的响应基于图像滤波器包围的图像区域中像素的排序,然后用统计排序结果决定的值代替中心像素的值。除了中值滤波器外,最大值滤波器和最小值滤波器也是统计排序滤波器。在介绍中值滤波器之前,我们先来了解一下中值的概念,中值是一个将概率分布的高半部分与低半部分分开的值。对一个随机变量x而言,x<M的概率为0.5。对于有限实数集,其排序后中间的数值即为它的中值。这样,我们就可以很清楚的知道,中值滤波就是将邻域内像素(包括中心像素值)灰度值的中值代替中心像素的值。
在图像处理中,中值滤波的使用非常的普遍,这是因为对于一定类型的随机噪声,它提供了一种优秀的去噪能力,相比于小尺寸的线性平滑滤波器的模糊程度要低很多。而且它对处理脉冲噪声(椒盐噪声)非常有效。那它为什么能够去噪呢?我们知道,因为噪声的出现,使某像素点比周围的像素亮(暗)许多,若与周围的像素值一起排序,噪声点则位于序列的前端或末端,序列中值通常没有受到噪声污染,因此,可以用中值取代原像素值来达到出去噪声的效果。
下面我们看一个直观的例子:
f = imread('C:/a.jpg');
f_gray = rgb2gray(f);
subplot(1,3,1);
imshow(f_gray);
title('原图');
fn = imnoise(f_gray, 'salt & pepper', 0.2);
subplot(1,3,2);
imshow(fn);
title('椒盐噪声');
fm = medfilt2(fn, 'symmetric');
subplot(1,3,3);
imshow(fm);
title('中值滤波');
从上图可以看出,中值滤波后的效果还是相当不错的。
接下来,我们着重看看如何实现中值滤波。
Pseudo Code:
allocate outputPixelValue[image width][image height]
edgex := (window width / 2) rounded down
edgey := (window height / 2) rounded down
for x from edgex to image width - edgex
for y from edgey to image height - edgey
allocate colorArray[window width][window height]
for fx from 0 to window width
for fy from 0 to window height
colorArray[fx][fy] := inputPixelValue[x + fx - edgex][y + fy - edgey]
sort all entries in colorArray[][]
outputPixelValue[x][y] := colorArray[window width / 2][window height / 2]
C++ Code:
bool median_Filter (unsigned char* input, unsigned char* output, int height, int weight)
{
if (input == NULL)
{
return false;
}
memset (output, 0, sizeof(unsigned char)*height*weight);
for (int i = 1; i < height-1; ++i)
{
for (int j = 1; j < weight-1; ++j)
{
int k = 0;
unsigned char filter_Region[9];
for (int ii = i-1; ii < i+2; ++ii)
{
for (int jj = j-1; jj < j+2; ++jj)
{
filter_Region[k++] = input[ii+width + jj];
}
}
for (int m = 0; m < 5; ++m)
{
int min = m;
for (int n = m+1; n < 9; ++n)
{
if (filter_Region[n] < filter_Region[min])
{
min = n;
}
}
unsigned char tmp = filter_Region[m];
filter_Region[m] = filter_Region[min];
filter_Region[min] = tmp;
}
output[i*width + j] = filter_Region[4];
}
}
return true;
}
从上面的程序可以发现,在每个像素位置上都要对一个矩形内部的所有像素进行排序,这样的开销会变得很大。因此下面介绍一种更为有效的方法[4],我们注意到当窗口沿着行移动一列时,窗口内容的变化只是丢掉最左边的列取代为一个新的右侧列,对于m行n列的中值窗口,mn-2*m个像素没有变化,并不需要重新排序。算法如下: