关闭

数字图像处理,中值滤波和均值滤波及其改进算法的C++实现

标签: 图像处理空间域中值滤波邻域均值
11703人阅读 评论(0) 收藏 举报
分类:

中值滤波

中值滤波法是一种非线性平滑技术,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值.中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升(或下降)的为二维数据序列。二维中值滤波输出为g(x,y)=med{f(x-k,y-l),(k,l∈W)} ,其中,f(x,y),g(x,y)分别为原始图像和处理后图像。W为二维模板,通常为3*3,5*5区域,也可以是不同的的形状,如线状,圆形,十字形,圆环形等。

以3*3的滤波窗口为例:

中值滤波就是在3*3中的像素中寻找中值。 来看这样一个描述图(不然无图无真相)

这把可以清晰地看到, 这里有6,2,0,3,97,4,19,3,10这些像素, 然后中间的这些像素值就被这些像素的中位数也就是中值取代了。为了满足和前面一篇文章的格式相对应, 我们马上进入下一个单元, 来看看在平滑和降噪方面的功效!

原图1                                                                           中值滤波之后

 

噪声图(5%)                                                                中值滤波后:

 

很明显中值滤波不仅使得图像变得平滑(也可以理解为图像变得模糊了),但是其对去除了椒盐噪声特别有效,因为中值滤波就是专门针对去除孤立点噪声孕育为生的。从这里中值的逻辑来看, 我们做中值操作的时候, 那么白色(接近255的像素点)和黑色(接近0的像素)因为在窗口排序时是最大最小值, 除非周围的颜色都是黑色或者白色,不然一般都会被剔除掉,所以,一般来说这个中值滤波是去除椒盐噪声的非常理想的选择。

最后还是贴一段我运行的代码:

////////////////中值滤波////////////////////////////////
int media_filter(unsigned char* inbuffer, int width, int height, unsigned char* outbuffer)
{
	int mid=0;
	int winsize = 3;//滤波窗口大小
	int* windows = new int[winsize*winsize];
	int pos = (winsize - 1) / 2;
	memcpy(outbuffer, inbuffer, width*height);
	for (int m = pos; m < height - pos; m++)
	{
		for (int n = pos; n < width - pos; n++)//(m,n)是窗口中心位置
		{
			//提取winsize*winsize的数据进入滤波窗
			int winpos = 0;
			for (int i = -pos; i < (winsize - pos); i++)
				for (int j = -pos; j < (winsize - pos); j++)
					windows[winpos++] = inbuffer[(m + i)*width + n + j];
			//对滤波窗中的数据排序取中值
			sort(windows, winsize*winsize);
			mid = windows[(winsize*winsize)/2];
			outbuffer[m*width + n] = mid;
		}
	}
	//对边界进行处理
	//未被处理的地方均取被处理的最边界
	for (int k = 0; k < pos; k++)
	for (int l = pos; l < width - pos; l++)
		outbuffer[k*width + l] = outbuffer[pos * width + l];//1到pos这几行未被处理的边界数据,始终取被处理的最上边界一层像素

	for (int a = height - pos; a < height; a++)
	for (int b = pos; b < width - pos; b++)
		outbuffer[a*width + b] = outbuffer[(height - pos - 1)*width + b];

	for (int c = 0; c < pos; c++)
	for (int d = 0; d < height; d++)
		outbuffer[d*width + c] = outbuffer[d*width + pos];

	for (int e = width - pos; e < width; e++)
	for (int f = 0; f < height; f++)
		outbuffer[f*width + e] = outbuffer[f*width + width - pos - 1];
	
	delete[] windows;
	windows = NULL;
	return 0;
}



均值滤波

把每个像素都用周围的9个像素来做均值操作 ”, 比如说这里有一个例子:

 

sample

非常明显的, 这个3*3区域像素的颜色值分别是5,3,6,2,1,9,8,4,7那么中间的1这个像素的过滤后的值就是这些值的平均值, 也就是前面的计算方法: (5+3+6+2+1+9+8+4+7)/9=5

一目了然。那么这个均值滤波器有什么用处呢?

主要还是平滑图像的用处, 有的图像的锐度很高,用这样的均值算法,可以把锐度降低。使得图像看上去更加自然,下面就有几幅图我们可以看出一些端倪:

原图:                                                                          原图均值滤波处理之后:

 

这里还是可以明显的感觉到不同的, 没有好坏之分,就是第二幅图片看上去更为平滑。 继续我们的问题, 那这里均值平滑是否具有去除噪声的功能呢? 我们搞来了椒盐噪声(就是随机的白点,黑点)来试试手:

噪声图(5%椒盐噪声):                         均值滤波平滑处理之后:

 

首先这里的噪声还是比较小的, 只有5%,从均值的效果来看的话, 我可以说几乎没有用,其实直观的想也可以判断, 因为这里的处理并没有剔除这些噪声点, 而只是微弱地降低了噪声,所以效果可以想见的。。

好吧, 最后的时候还是贴上一段处理的C++均值滤波算法代码:


////////////////均值滤波////////////////////////////////
int mean_filter(unsigned char* inbuffer, int width, int height, unsigned char* outbuffer)
{
	int mid=0;
	int winsize = 3;//滤波窗口大小
	int* windows = new int[winsize*winsize];
	int pos = (winsize - 1) / 2;
	memcpy(outbuffer, inbuffer, width*height);
	for (int m = pos; m < height - pos; m++)
	{
		for (int n = pos; n < width - pos; n++)//(m,n)是窗口中心位置
		{
			//提取winsize*winsize的数据进入滤波窗
			int winpos = 0;
			for (int i = -pos; i < (winsize - pos); i++)
				for (int j = -pos; j < (winsize - pos); j++)
					windows[winpos++] = inbuffer[(m + i)*width + n + j];
			mid=getaverage(windows, winsize*winsize);//对滤波窗中的数据取平均值
			outbuffer[m*width + n] = mid;
		}
	}
	//对边界进行处理
	//未被处理的地方均取被处理的最边界
	for (int k = 0; k < pos; k++)
	for (int l = pos; l < width - pos; l++)
		outbuffer[k*width + l] = outbuffer[pos * width + l];//1到pos这几行未被处理的边界数据,始终取被处理的最上边界一层像素

	for (int a = height - pos; a < height; a++)
	for (int b = pos; b < width - pos; b++)
		outbuffer[a*width + b] = outbuffer[(height - pos - 1)*width + b];

	for (int c = 0; c < pos; c++)
	for (int d = 0; d < height; d++)
		outbuffer[d*width + c] = outbuffer[d*width + pos];

	for (int e = width - pos; e < width; e++)
	for (int f = 0; f < height; f++)
		outbuffer[f*width + e] = outbuffer[f*width + width - pos - 1];
	
	delete[] windows;
	windows = NULL;
	return 0;
}


一种改进的中值滤波(HMF)

在中值滤波器和均值滤波器之后, 这次看到是中值滤波器的改进, Hybrid Median Filter ,这个名字我实在不好把握实在不知道翻译成什么比较妥当.只好沿用了混合动力车里面的混合二字, 姑且把这种滤波器命名为混合中值滤波器. 这个滤波器的工作流程较之原先有些繁琐, 来看看这张图

flow

图截的有些模糊, 但是还是看出了大致的流程, 首先当前像素的上下左右和自身取中值1 然后左上右上左下右下和自身取中值2 完了前面的两个中值和当前像素值再取一次中值 , 得到的值就是最后的终极像素值了. 可以看到这个流程还是有些复杂的.. 那他的效果如何捏? 我们还是和前几次一样来观察一把:

原图:                                                      HMF之后:

 

噪声污染:                                                 HMF之后:

 

从前面的图片可以看到, 图片从高锐度经由混合中值滤波之后, 锐度下降了. 但是在去噪声的环节上, 这个所谓的混合中值滤波器表现的甚至不如中值滤波来的效果好, 当然因为这里的噪声是随机的, 可能有些因素在其中. 但从原理上分析.这种滤波器确实存在不能过滤掉的可能, 原因在什么地方呢? 且看我们这个大图中的一个小局部:

把图放大之后看的很清楚了, 在红色的圆圈圈出来的地方, 不出意外的发现了水平连续的3个白色像素. 再联想到我们之前经过HMF之后的效果图就不难得出结论, 就是者连续的3个点造成了最后的这个白色像素. 怎么说呢? 很简单, 在HMF的第一步过程中, 因为我们选择的点是5各点, 上下左右中, 那么经过计算, 就是白色无疑了, 不管第二步得到什么像素, 在第三步的时候, 让2个白色取中值, 肯定得到的白色像素了! 仔细想来确实是这个么道理.  但是median filter的时候因为要整体考虑9个像素的中值, 这里很有可能就不是白色了! 所以这个混合中值滤波器会在保持线条的边缘上有独到之处(因为比较容易保持联系像素的颜色不受伤害), 而消除噪声还是median filter表现更胜一筹.老规矩, 最后再贴一下处理的函数代码:

unsigned char median(unsigned char* elements, int width)  
{  
    //   Order elements (only half of them)  
    for (int i = 0; i < (width >> 1) + 1; ++i)  
    {  
        //   Find position of minimum element  
        int min = i;  
        for (int j = i + 1; j < width; ++j)  
            if (elements[j] < elements[min])  
                min = j;  
        //   Put found minimum element in its place  
        unsigned char temp = elements[i];  
        elements[i] = elements[min];  
        elements[min] = temp;  
    }  
    //   Get result - the middle element  
    return elements[width >> 1];  
}  
/** 
** method to remove noise from the corrupted image by hybrid median value 
* @param corrupted input grayscale binary array with corrupted info 
* @param smooth output data for smooth result, the memory need to be allocated outside of the function 
* @param width width of the input grayscale image 
* @param height height of the input grayscale image 
*/  
void hybridMedianFilter (unsigned char* corrupted, unsigned char* smooth, int width, int height)  
{  
    memcpy ( smooth, corrupted, width*height*sizeof(unsigned char) );  
    for (int j=1;j<height-1;j++)  
    {  
        for (int i=1;i<width-1;i++)  
        {  
            unsigned char window[5];  
            unsigned char results[3];  
            //   Pick up cross-window elements  
            window[0] = corrupted[(j - 1) * width + i];  
            window[1] = corrupted[j * width + i - 1];  
            window[2] = corrupted[j * width + i];  
            window[3] = corrupted[j * width + i + 1];  
            window[4] = corrupted[(j + 1) * width + i];  
            //   Get median  
            results[0] = median(window, 5);  
            //   Pick up x-window elements  
            window[0] = corrupted[(j - 1) * width + i - 1];  
            window[1] = corrupted[(j - 1) * width + i + 1];  
            window[2] = corrupted[j * width + i];  
            window[3] = corrupted[(j + 1) * width + i - 1];  
            window[4] = corrupted[(j + 1) * width + i + 1];  
            //   Get median  
            results[1] = median(window, 5);  
            //   Pick up leading element  
            results[2] = corrupted[j * width + i];  
            //   Get result  
            smooth[j*width+i] = median(results, 3);  
        }  
    }  
}

因为要取中值, 还附加了一个取中值的函数median



实验结果:

以下所有结果均为C++处理。
1,对原图像进行处理的结果
可以发现对边缘有就很好的保护能力:(左为原图)


2,对噪声图像进行处理的结果
注意这是matlab里面的椒盐噪声,最左边是噪声图像,中间是本文改进中值滤波(HMF)算法,最右边是自适应中值滤波。
去噪效果一般。椒盐是中值滤波系列最容易处理的噪声,可以见得该改进算法有它的不足之处,在椒盐噪声的处理效果上明显不如经典的自适应中值滤波!



中值滤波对噪声图像的效果,存在明显的不足(模糊效果):





参考资源:

【1】百度百科,http://baike.baidu.com/link?url=mQ37rozxl6jHqzdVY56zOO_tpSvInR0EA8AGK5W242ljIXOxPz_xtTT8Bgi8fOVocmp6XpiI1FK6byALfUir1a

【2】hhygcy,http://blog.csdn.net/hhygcy/article/details/4325462


注:本文是在原作者hhygcy基础上二次加工重写,代码有较大改动

2
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:503784次
    • 积分:7700
    • 等级:
    • 排名:第2870名
    • 原创:239篇
    • 转载:50篇
    • 译文:0篇
    • 评论:161条
    博客专栏
    个人介绍
    非CS科班出身,喜欢用C/C++思考数学 ,专注于图像处理和软件开发!本博客基于交流和记录学习的历程为目的,与诸君共勉,欢迎交流。同时,博文有不少文字并非一字一字地敲打,若侵权,请联系本人,

    E-mail:tangyb7172@foxmail.com
    最新评论