本章要点:
平滑处理的基本方法 |
噪声消除法 |
邻域平均法 |
中值滤波 |
产生噪声 |
众所周知,实际获得的图像在形成、传输、接收和处理的过程中,不可避免地存在着外部干扰和内部干扰,如光电转换过程中敏感元件灵敏度的不均匀性、数字化过程的量化噪声、传输过程中的误差以及人为因素等,均会存在着一定程度的噪声干扰。噪声恶化了图像质量,使图像模糊,特征淹没,给分析带来困难。因此,去除噪声,恢复原始图像是图像处理中的一个重要内容。消除图像噪声的工作称之为图像平滑或滤波。平滑的目的有两个:改善图像质量和抽出对象特征。
由于噪声源众多(如光栅扫描,底片颗粒,机械元件,信道传输等),噪声种类复杂(如加性噪声,乘性噪声,量化噪声等),所以平滑方法也多种多样。平滑可以在空间域进行,也可以在频率域进行。本章主要介绍在空间域上图像平滑常用的方法,频率域平滑将在第九章中论述。
在空域法中,图像平滑常用的方法是采用均值滤波或中值滤波。对于均值滤波采用一个有奇数点的滑动窗口在图像上滑动,窗口中心点所对应像素的灰度值用窗口内所有像素的平均值代替,在取均值过程中,如果窗口规定了各个像素点所占的权重,也就是各个像素点的系数,则称为加权均值滤波。对于中值滤波,窗口中心点所对应像素的灰度值用窗口内内所有像素的中间值代替。实现均值或中值滤波时,为了简便编程工作,可以定义一个N×N的模板数组。另外,在用窗口扫描图像过程中,对于图像四个边缘的像素点,可以不处理;也可以用灰度值为”0”的像素点扩展图像的边缘。
在空间域平滑滤波有很多种算法,其中最常见的有:线性平滑、非线性平滑、自适应平滑。
1.线性平滑
线性平滑就是对每一个像素点的灰度值用它的邻域值来代替,其邻域大小为:N×N,N一般取奇数。如3×3均值滤波、N×N均值滤波器。经过线性平滑滤波,相当于图像经过了一个二维的低通滤波器,虽然是降低了噪声,但同时也模糊了图像边缘和细节,这是这类滤波器存在的通病。
2.非线性平滑
非线性平滑是对线性平滑的一种改进,即不对所有像素都用它的邻域平均值来代替,而是取一个阈值,当像素灰度值与其邻域平均值之间的差值大于已知值时才以均值代替;当像素灰度值与其邻域平均值之间的差值不大于阈值时取其本身的灰度值。如超限邻域平均法。非线性平滑可消除一些孤立的噪声点,对图像的细节影响不大,但对物体的边缘点会带来一定的失真。
3.自适应平滑
自适应控制是一种根据当时、当地情况来进行控制的方法,所以这种算法要有一个适应的目标。根据目的的不同,可以有各种各样的自适应图像处理方法。考虑到图像中目标物体和背景一般都具有不同的统计特性,即不同的均值和方差,为保留一定的边缘信息,可采用自适应的局部平滑滤波。这样可以得到较好的图像细节。自适应平滑法是以尽量不模糊边缘轮廓为目标的。如选择式掩模平滑法。
总之,平滑滤波主要目的是减少噪声。为了更好地达到此目的,必须对图像中的噪声情况有所了解。图像中的噪声种类很多,对图像信号幅度和相位的影响十分复杂,有些噪声和图像信号互相不相关,有些是相关的,噪声本身之间也有些是相关的。必须针对具体情况采用不同的平滑滤波算法,否则很难获得满意的处理效果。
5.2 噪声消除法
本节介绍对于二值图像上噪声的消除方法。
二值图像的黑白点噪声滤波
消除孤立黑像素点
5.2.1 二值图像的黑白点噪声滤波
1.理论基础
本程序消去二值图像f(i,j)上的黑白的噪声,当像f(i,j)周围的8个像素的平均值为a时,若|f(i,j)-a|的值在127.5以上,则对f(i,j)的黑白进行翻转,若不到127.5则f(i,j)不变。
2.实现步骤
(1)取得图像大小、数据区,并把数据区复制到缓冲区中;
(2)循环取得各点像素值;
(3)取得该点周围8像素值的平均值;
(4)平均值与该点像素值相比,若大于127.5则把该点颜色反转;
(5)把缓冲区中改动的数据复制到原数据区中。
3.程序代码
/**************************************************************
*函数名称:HeiBaiFanZhuan()
*函数类型:void
*功能:对二值图像的黑白点噪声消除。
**************************************************************/
void ZaoShengXiaoChuDib::HeiBaiFanZhuan()
{
int averg;
BYTE *p_data; //原图数据区指针
int wide,height; //原图长、宽
p_data=this->GetData ();//取得原图的数据区指针
wide=this->GetWidth (); //取得原图的数据区宽
height=this->GetHeight (); //取得原图的数据区高
GuDing(100); //进行二值化
BYTE* p_temp=new BYTE[wide*height];// 申请并分配中间缓存
memcpy(p_temp,m_pData,wide*height);// 复制图像数据到中间缓存
//用3*3屏蔽窗口的8近邻均值进行滤波
for(int j=1;j for(int i=1;i averg=0; //求周围8近邻均值 averg=(int)((p_data[(j-1)*wide+(i-1)]+p_data[(j-1)*wide+i] +p_data[(j-1)*wide+(i+1)]+p_data[j*wide+(i-1)] +p_data[j*wide+i+1]+p_data[(j+1)*wide+(i-1)] +p_data[(j+1)*wide+i]+p_data[(j+1)*wide+i+1])/8); if(abs(averg-p_temp[j*wide+i])>127.5) p_temp[j*wide+i]=averg; } } memcpy(p_data,p_temp,wide*height); delete p_temp; } 4.处理效果 (a)原图 (b) 二值图像的黑白点噪声滤波 图5-1二值图像的黑白点噪声滤波 5.2.2 消除孤立黑像素点 1.理论基础 对图像像素的处理方式上可以划分为点处理和区域处理。点处理是一种输出像素值仅取决于输入像素值的图像处理方法;区域处理的输出像素值不仅与输入的像素值有关,而且与输入像素在一定的范围内的相邻像素值有关。区域处理在数字图像处理中占有重要地位。区域处理在处理某一像素时,利用与该像素相邻的一组像素,经过某种变换得到处理后图像中某点的的像素值。目标像素的邻域一般是由像素组成的二维矩阵,该矩阵的大小为奇数,目标像素位于该矩阵的,即目标像素就是区域的中心像素。经过处理后,目标像素的值为经过特定算法计算后所得的结果。 一幅图像往往可能受到各种噪声源的干扰,从而这些噪声使图像表现为一些孤立像素点,它们像雪花飘落在画面上一样。 本程序在二值图像f中,消除孤立于周围的黑像素点(变成白的)。像素的四邻域和八邻域关系如图5-2 连通图所示。 * * * * * · * * · * * * * * 四邻域 八邻域 图5-2 连通图 在4点邻域的情况下,若黑像素f(i,j)的上下左右4个像素全为白(0),则f(i,j)也取为0。 在8点邻域的情况下,若黑像素f(i,j)的周围8个像素全为白(0),则f(i,j)也取为0。 2.实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值; (3)判断为4连接或8连接; (4)为4连接时,若该点上下左右4个像素全为白,则该点置白; 为8连接时,若该点周围8个像素全为白,则该点置白; (5) 把缓冲区中改动的数据复制到原数据区中。 3.程序代码 /************************************************************** *函数名称:black(int connec) *函数类型:void *参数:int connec,设定的连通选择 *功能:对二值图像进行消除孤立黑像素点。 **************************************************************/ void ZaoShengXiaoChuDib::black(int connec) { // 指向DIB像素指针 BYTE *p_data; p_data=this->GetData(); //取得原图的数据区指针 int wide=this->GetWidth(); //取得原图的数据区宽 int height=this->GetHeight(); //取得原图的数据区高 //二值化 GuDing(100); // 申请并分配中间缓存 BYTE* p_temp=new BYTE[wide*height]; // 复制图像数据到中间缓存 memcpy(p_temp,p_data,wide*height); // 4连接的情况下,消去上下左右都为255(白点)的孤立黑点 if (connec==4) { for (int j=1;j for (int i=1;i if (*(p_temp + wide * j + i)==255) continue; if((*(p_temp + wide * (j-1) + i)+ *(p_temp + wide * (j+1) + i)+ *(p_temp + wide * j + i-1)+ *(p_temp + wide * j + i+1))==255*4) *(p_data + wide * j + i)=255; } } } // 8连接的情况下,消去周围都为255(白点)的孤立黑点 if (connec==8) { for (int j = 1; j < height-1; j ++) { for (int i = 1; i < wide-1; i ++) { if (*(p_temp + wide * j + i)==255) continue; if((*(p_temp + wide * (j-1) + i)+ *(p_temp + wide * (j+1) + i)+ *(p_temp + wide * j + i-1)+ *(p_temp + wide * j + i-1)+ *(p_temp + wide * (j-1) + i-1)+ *(p_temp + wide * (j+1) + i+1)+ *(p_temp + wide * (j-1) + i+1)+ *(p_temp + wide * (j+1) + i-1))==255*8) *(p_data + wide * j + i)=255; } } } } 4.效果对比图 (a)原图 (b)4连接消除孤立点的效果图 (c)8连接消除孤立点的效果图 图5-3 消除孤立点效果图 从上图可知,4连接或8连接没有删除全部噪声,这是由于这些噪声并不是孤立的,在放大图像下可知如此。由于8连接要求的条件更加苛刻,因此不能满足条件的噪声要比4连接的多,遗留下的噪声也多。 5.3 邻域平均法 噪声点像素的灰度与它们邻近像素有显著的不同,根据噪声点的这一空间特性,我们用邻域平均法和阈值平均法。像素的四邻域和八邻域关系如图5-2连通图所示。 f(i,j)像素与周围邻域之间的相互关系为: 本节介绍的基于邻域的平滑方法有: 3*3均值滤波; 超限邻域平均法; N*N均值滤波器; 选择式掩模平滑。 5.3.1 3×3均值滤波 1.理论基础 设f(i,j)为给定的含有噪声的图像,经过简单邻域平均处理后为g(i,j),在数学上可表现为 g(i,j)=∑f(i,j) /M 其中(i,j) 式中S是所取邻域中的各邻近像素的坐标,M是邻域中包含的邻近像素的个数,可以这样说明。在f(i,j)上按行(或列)对每个像素选取一定尺寸的邻域,并用邻域中邻近像素的平均灰度来置换这一像素值,对全部像素处理后可获得g(x,y)。对于邻域可以有不同的选取方式,如下所示: 模板1 模板2 模板3 为了保持平滑处理后的图像的平均值不变,模板内各元素之和为1。有时,为了突出原点(i,j)本身的重要性,以便尽量抑制图像中的模糊效应,在模板中心和较近的元素,可以赋以大的加权值(如模板3)。 可见,3×3均值滤波处理是以图像模糊为代价来换取噪声的减小的,且面积(即模板大小)越大,噪声减少越显著。如果f(i,j)是噪声点,其邻近像素灰度与之相差很大,一旦用简单邻域平均法,即邻近像素的平均值来置换它,能明显地将噪声点压制下去,使邻域中灰度接近均匀,起到平滑灰度的作用,因此,邻域平均法具有显著地平滑噪声的效果,邻域平均法是一种平滑技术。 本程序中采用“模板2”,即把当前图像f(i,j)周围8个像素的平均灰度作为该像素值。 2.实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值; (3)取得该点周围8像素值的平均值; (4)把缓冲区中改动的数据复制到原数据区中。 3.程序代码 /************************************************************** *函数名称:threethree() *函数类型:void *功能:对图像进行3*3均值滤波处理。 **************************************************************/ void ZaoShengXiaoChuDib::threethree() { int averg; BYTE *p_data; //原图数据区指针 int wide,height; //原图长、宽 p_data=this->GetData ();//取得原图的数据区指针 wide=this->GetWidth (); //取得原图的数据区宽 height=this->GetHeight (); //取得原图的数据区高 BYTE* p_temp=new BYTE[wide*height]; int size=wide*height; memset(p_temp,255,size); //用3*3屏蔽窗口的8近邻均值进行滤波 for(int j=1;j for(int i=1;i averg=0; //求周围8近邻均值 averg=(int)(p_data[(j-1)*wide+(i-1)]+p_data[(j-1)*wide+i] +p_data[(j-1)*wide+(i+1)]+p_data[j*wide+(i-1)] +p_data[j*wide+i+1]+p_data[(j+1)*wide+(i-1)] +p_data[(j+1)*wide+i]+p_data[(j+1)*wide+i+1])/8; p_temp[j*wide+i]=averg; } } memcpy(p_data,p_temp,wide*height); delete p_temp; } 4.效果对比图 (a)原图 (b) 邻域平均法 图5-4邻域平均法 5.3.2 超限邻域平均法 1.理论基础 邻域平均法虽然简单,但它存在着边缘模糊的效应,本来不是噪声的边缘处,应该保留原有的灰度差,而邻域平均法使边缘处的灰度趋向均匀,造成了边缘模糊,为了减少模糊效应,需求改进的途径。力求找到解决清除噪声和边缘模糊这对矛盾的最佳统一。 阈值的邻域平均法以某个灰度值T作为阈值,如果某个像素的灰度大于其邻近像素的平均值 ,并超过阈值,才使用平均灰度置换这个像素灰度,它的数学表达式: f(i,j) 若(f(i,j)=f(i,j)- f(i,j))>T, g(i,j)= (5-1) f(i,j) 其它。 此式表明,若某点值与其邻域平均值相差超过 T,用平均值代替,进行平均处理,可去除噪声;否则还保留原值,不进行平均处理,从而减少模糊。这种算法对抑制椒盐噪声比较有效,同时也能交好地保护仅有微小灰度差的图像细节。 2.实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值; (3)取得该点周围8像素值的平均值; (4)判断该点的像素值是否大于平均值,若大于平均值则将平均值赋于该点,否则还保留原像素的灰度值。 (5)把缓冲区中改动的数据复制到原数据区中。 3.程序代码 /************************************************************** *函数名称:Chaoxian(int T) *函数类型:void *参数:int T,设定的阈值 *功能:超限邻域平均法。 **************************************************************/ void ZaoShengXiaoChuDib::Chaoxian(int T) { int averg; BYTE *p_data; //原图数据区指针 int wide,height; //原图长、宽 p_data=this->GetData ();//取得原图的数据区指针 wide=this->GetWidth (); //取得原图的数据区宽 height=this->GetHeight (); //取得原图的数据区高 BYTE* p_temp=new BYTE[wide*height]; int size=wide*height; memset(p_temp,255,size); //用3*3屏蔽窗口的8近邻均值进行滤波 for(int j=1;j for(int i=1;i averg=0; //求周围8近邻均值 averg=(int)(p_data[(j-1)*wide+(i-1)]+p_data[(j-1)*wide+i] +p_data[(j-1)*wide+(i+1)]+p_data[j*wide+(i-1)] +p_data[j*wide+i+1]+p_data[(j+1)*wide+(i-1)] +p_data[(j+1)*wide+i]+p_data[(j+1)*wide+i+1])/8; if(abs(p_temp[j*wide+i]-averg)>T) p_temp[j*wide+i]=averg; } } memcpy(p_data,p_temp,wide*height); delete p_temp; } 4.效果图 图5-5 超限邻域平均法(阈值为100) 5.3.3 N×N均值滤波器 1.理论基础 在本程序中当灰度图像f中以像素f(i,j)为中心的N×N屏蔽窗口(N=3,5,7…)内平均灰度值为a时,无条件作f(i,j)=a处理,N由用户给定,且取N值越大,噪声减少越明显。但平均是以图像的模糊为代价的。 2.实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)取得N值; (3)循环取得各点像素值; (4)算出以该点像素为中心的N×N屏蔽窗口内平均值; (5)把该点像素值置为平均值; (6)把缓冲区中改动的数据复制到原数据区中。 3.程序代码 /************************************************************** *函数名称:nn(int n) *函数类型:void *参数:int n,设定的屏蔽窗口参数(奇数) *功能:对图像进行n*n均值滤波处理。 **************************************************************/ void ZaoShengXiaoChuDib::nn(int n) { DWORD size; size=GetSize(); BYTE *p_data; int xx,yy,n2,sum; int wide,height; //原图长、宽 BYTE* p_temp=new BYTE [size]; memset(p_temp,255,size); if(n<3||n%2!=1)//确认n为奇数 AfxMessageBox("请输入一个大于等于3的奇数"); if(n>=3&&n%2==1) n2=(n-1)/2; p_data=this->GetData();//取得原图的数据区指针 wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 //用N*N屏蔽窗口平均化 for(int j=n2;j for(int i=n2;i sum=0; for(yy=j-n2;yy<=j+n2;yy++) for(xx=i-n2;xx<=i+n2;xx++) sum+=p_data[yy*wide+xx]; //把n*n屏蔽窗口内的平均值四舍五入后作为显示图像像素值 p_temp[j*wide+i]=(int)((float)sum/(n*n)+0.5); } } memcpy(p_data,p_temp,size); delete p_temp; } 5.3.4 选择式掩模平滑 1.理论基础 噪声消除法和邻域平均法在消除噪声的同时,都不可避免的带来平均化的缺憾,致使尖锐变化的边缘或线变得模糊。考虑到图像中目标物体和背景一般都具有不同的统计特性,即不同的均值和方差,为保留一定的边缘信息,可采用自适应的局部平滑滤波。这样可以得到较好的图像细节。自适应平滑法是以尽量不模糊边缘轮廓为目的。 选择式掩模平滑法取5 × 5窗口,如图5-2所示。在窗口内以中心像素f(i,j)为基准点,制作4个五边形、4个六边形、1个边长为3的正方形共9种形状的屏蔽窗口,分别计算每个窗口内的平均值及方差,由于含有尖锐边沿的区域,方差必定较平缓区域为大,因此采用方差最小的屏蔽窗口进行平均化,这种方法在完成滤波操作的同时,又不破坏区域边界的细节。这种采用9种形状的屏蔽窗口,分别计算各窗口内的灰度值方差,并采用方差最小的屏蔽窗口进行平均化方法,也称作自适应平滑方法。 (a)周围9邻近 (b)左7邻近 (c)上7邻近 (d)右7邻近 (e) 下7邻近 (f) 左上7邻近 (g)右上7邻近 (h)右下7邻近 (i)左下7邻近 图5-2 5×5窗口选择式掩模平滑法 计算个掩模的均值(ai)及方差(ki)。 ai= ki= 式中m=1,2,3...,Q;Q为各掩模对应的像素个数; 在此基础上,对ki排序,最小方差Kimin 随对应的掩模的灰度级均值ai 作为f(i,j)的平滑输出(凡含有尖锐边沿的区域,方差必定较平缓区域为大)G(i,j)。 用同样的方法作用于每一个像素(即窗口是5×5 ,边缘两行两列如不作延伸,将处理不到),即可完成全帧图像的平滑。 2.实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值;分别计算各掩模的均值(ai)及方差(ki); i=1,2,3...,9 (3)在此基础上,对ki排序,最小方差Kimin 所对应的掩模的灰度级均值ai 作为f(i,j)的平滑输出,(凡含有尖锐边沿的区域,方差必定较平缓区域为大)G(i,j)。 (4)同样的方法作用于每一个像素(即窗口是5×5 ,边缘两行两列如不作延伸,将处理不到),即可完成全帧图像的平滑。 (5)把缓冲区中改动的数据复制到原数据区中。 3.程序代码 /************************************************************** *函数名称:jubupingjun() *函数类型:void *功能:对图像进行局部平均化处理。 **************************************************************/ void ZaoShengXiaoChuDib::jubupingjun() { DWORD size; size=GetSize(); BYTE *p_data;//原图数据区指针 BYTE* p_temp=new BYTE[size]; int wide,height; int n,ji[9],nmin; float mean[9],bunsan[9],bmin; p_data=this->GetData();//取得原图的数据区指针 wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 memset(p_temp,255,size); for(int j=2;j<=height-3;j++) for(int i=2;i<=wide-3;i++) { //求9种近邻区域的均值及其方差 //第1近邻区域 ji[0]=p_data[(j-1)*wide+(i-1)]; ji[1]=p_data[(j-1)*wide+i]; ji[2]=p_data[(j-1)*wide+(i+1)]; ji[3]=p_data[j*wide+(i-1)]; ji[4]=p_data[j*wide+i]; ji[5]=p_data[j*wide+(i+1)]; ji[6]=p_data[(j+1)*wide+(i-1)]; ji[7]=p_data[(j+1)*wide+i]; ji[8]=p_data[(j+1)*wide+(i+1)]; mean[0]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6]+ji[7] +ji[8])/9; bunsan[0]=0; for(n=0;n<=8;n++) bunsan[0]+=ji[n]*ji[n]-mean[0]*mean[0]; //第2近邻区域 ji[0]=p_data[(j-2)*wide+(i-1)]; ji[1]=p_data[(j-2)*wide+i]; ji[2]=p_data[(j-2)*wide+(i+1)]; ji[3]=p_data[(j-1)*wide+(i-1)]; ji[4]=p_data[(j-1)*wide+i]; ji[5]=p_data[(j-1)*wide+(i+1)]; ji[6]=p_data[j*wide+i]; mean[1]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[1]=0; for(n=0;n<=6;n++) bunsan[1]+=ji[n]*ji[n]-mean[1]*mean[1]; //第3近邻区域 ji[0]=p_data[(j-1)*wide+(i-2)]; ji[1]=p_data[(j-1)*wide+(i-1)]; ji[2]=p_data[j*wide+(i-2)]; ji[3]=p_data[j*wide+(i-1)]; ji[4]=p_data[j*wide+i]; ji[5]=p_data[(j+1)*wide+(i-2)]; ji[6]=p_data[(j+1)*wide+(i-1)]; mean[2]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[2]=0; for(n=0;n<=6;n++) bunsan[2]+=ji[n]*ji[n]-mean[2]*mean[2]; //第4近邻区域 ji[0]=p_data[j*wide+i]; ji[1]=p_data[(j+1)*wide+(i-1)]; ji[2]=p_data[(j+1)*wide+i]; ji[3]=p_data[(j+1)*wide+(i+1)]; ji[4]=p_data[(j+2)*wide+(i-1)]; ji[5]=p_data[(j+2)*wide+i]; ji[6]=p_data[(j+2)*wide+(i+1)]; mean[3]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[3]=0; for(n=0;n<=6;n++) bunsan[3]+=ji[n]*ji[n]-mean[3]*mean[3]; //第5近邻区域 ji[0]=p_data[(j-1)*wide+(i+1)]; ji[1]=p_data[(j-1)*wide+(i+2)]; ji[2]=p_data[j*wide+i]; ji[3]=p_data[j*wide+(i+1)]; ji[4]=p_data[j*wide+(i+2)]; ji[5]=p_data[(j+1)*wide+(i+1)]; ji[6]=p_data[(j+1)*wide+(i+2)]; mean[4]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[4]=0; for(n=0;n<=6;n++) bunsan[4]+=ji[n]*ji[n]-mean[4]*mean[4]; //第6近邻区域 ji[0]=p_data[(j-2)*wide+(i+1)]; ji[1]=p_data[(j-2)*wide+(i+2)]; ji[2]=p_data[(j-1)*wide+i]; ji[3]=p_data[(j-1)*wide+(i+1)]; ji[4]=p_data[(j-1)*wide+(i+2)]; ji[5]=p_data[j*wide+i]; ji[6]=p_data[j*wide+(i+1)]; mean[5]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[5]=0; for(n=0;n<=6;n++) bunsan[5]+=ji[n]*ji[n]-mean[5]*mean[5]; //第7近邻区域 ji[0]=m_pData[(j-2)*wide+(i-2)]; ji[1]=p_data[(j-2)*wide+(i-1)]; ji[2]=p_data[(j-1)*wide+(i-2)]; ji[3]=p_data[(j-1)*wide+(i-1)]; ji[4]=p_data[(j-1)*wide+i]; ji[5]=p_data[j*wide+(i-1)]; ji[6]=p_data[j*wide+i]; mean[6]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[6]=0; for(n=0;n<=6;n++) bunsan[6]+=ji[n]*ji[n]-mean[6]*mean[6]; //第8近邻区域 ji[0]=m_pData[j*wide+(i-1)]; ji[1]=p_data[j*wide+i]; ji[2]=p_data[(j+1)*wide+(i-2)]; ji[3]=p_data[(j+1)*wide+(i-1)]; ji[4]=p_data[(j+1)*wide+i]; ji[5]=p_data[(j+2)*wide+(i-2)]; ji[6]=p_data[(j+2)*wide+(i-1)]; mean[7]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[7]=0; for(n=0;n<=6;n++) bunsan[7]+=ji[n]*ji[n]-mean[7]*mean[7]; //第9近邻区域 ji[0]=p_data[j*wide+i]; ji[1]=p_data[j*wide+(i+1)]; ji[2]=p_data[(j+1)*wide+i]; ji[3]=p_data[(j+1)*wide+(i+1)]; ji[4]=p_data[(j+1)*wide+(i+2)]; ji[5]=p_data[(j+2)*wide+(i+1)]; ji[6]=p_data[(j+2)*wide+(i+2)]; mean[8]=(float)(ji[0]+ji[1]+ji[2]+ji[3]+ji[4]+ji[5]+ji[6])/7; bunsan[8]=0; for(n=0;n<=6;n++) bunsan[8]+=ji[n]*ji[n]-mean[8]*mean[8]; //求方差最小的近邻区域nmin bmin=bunsan[0]; nmin=0; for(n=0;n<=8;n++) { if(bmin>bunsan[n]) { bmin=bunsan[n]; nmin=n; } //把nmin的值四舍五入后作为显示图像的值 p_temp[j*wide+i]=(int)(mean[nmin]+0.5); } } memcpy(p_data,p_temp,size); delete p_temp; } 4 处理效果 (a)原图 (b)7*7均值滤波 (c) 选择式掩模平滑法 图5-6 邻域平均法 从图上可见平均法存在着边缘模糊的效应。 5.4 中值滤波 平均滤波往往不只是把干扰去除,还常把图像的边缘模糊,因而造成视觉上的失真,如果目的只要把干扰去除,而不是刻意让图像模糊,则中值滤波是比较好的选择。 中值滤波是一种非线性的信号处理方法,与其对应的中值滤波器也是一种非线性的滤波器。中值滤波器是在1971年有J。w。Jukey首先提出并应用在一维信号处理技术中(时间序列分析),后来被二维图像信号处理技术所引用。它在一定的条件下,可以克服线形滤波器如最小均方滤波、平均值滤波等所带来的图像细节模糊,而且对滤波脉冲干扰及图像扫描噪声最为有效。特别适合用在有很强的胡椒粉式或脉冲式的干扰时,因为这些干扰值与其邻近像素的灰度值有很大的差异,因此经排序后取中值的结果是强迫将此干扰变成与其邻近的某些像素的灰度值一样,达到去除干扰的效果。在实际运算过程中并不需要图像的统计特性,这也带来不少方便,但是对一些细节特别多,特别是点、线、尖顶细节多的图像不宜采用中值滤波方法。 一般地,设有一个一维序列ƒ1,ƒ2,ƒ3,……,ƒn。取该窗口长度(点数)为m(m为奇数),对次一维序列进行中值滤波,就是从序列中相继抽取出m个数ƒi-v, ……,ƒi-1, ƒi ,ƒi+1, ……,ƒi+v;其中ƒi为窗口的中心点值,v=(m-1)/2。再将这m个点值按其数值大小排序,取中间的那个数作为滤波输出,用数学公式表示为: yi=Med ƒi-v, ……,ƒi-1, ƒi ,ƒi+1, ……,ƒi+v 其中i∈Z,v=(m-1)/2。 中值滤波一般采用一个含有奇数个点的滑动窗口,将窗口中各点灰度值的中值来替代指定点(一般是窗口的中心点)的灰度值。假设窗口内有五点,其值依次为[1,4,6,0,7],重新排序后(从小到大)为[0,1,4,6,7],则Med[1,4,6,0,7]=4。此例若用平滑滤波,窗口也是取4,而平滑滤波输出为: (1+4+6+0+7)÷ 5 = 3.6 二维中值滤波可有下式表示: yi=Med ƒij 二维中值滤波的窗口形状和尺寸设计对滤波的效果影响较大,不同的图像内容和不同的应用要求,往往采用不同的形状和尺寸。常用的二维中值滤波窗口有线状、方形、圆形、十字形及圆环形等,窗口尺寸一般先用3,再取5,逐点增大,直到其滤波效果满意为止。就一般经验来讲,对于有缓变的较长轮廓线物体的图像,采用方形或圆形窗口为宜,对于包含有尖顶角物体的图像,适宜用十字形窗口。滤波窗口大小的选择,一般以不超过图像中最小有效物体的尺寸为宜。 中值滤波的性质有: (1)非线形:两序列f(r),g(r) Med f(r)+g(r) ≠Med f(r) + Med g(r) (2)对尖峰性干扰效果好,即保持边缘的陡度又去掉干扰,对高斯分布噪声效果差; (3)对噪声延续距离小于W/2的噪声抑制效果好,W为窗口长度。 对于奇数个元素,中值是指按大小排序后,中间的数值;对于偶数个元素,中值是指排序后中间两个元素灰度值的平均值。 常用的二维中值滤波窗口形状有线状、方形、圆形、十字形等。下面主要介绍: N×N中值滤波器; 十字型中值滤波器; N×N最大值滤波器。 其他形状的中值滤波也可以由此类似完成。 5.4.1 N*N中值滤波器 1. 理论基础 本程序计算灰度图像f中以像素f(i,j)为中心的N×N屏蔽窗口(N=3,5,7…)内灰度的中值为u, 无条件作f(i,j)=u处理,n由用户给定。 2. 实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)取得N值; (3)循环取得各点像素值; (4)对以该点像素为中心的N×N屏蔽窗口包括的各点像素值进行排序,得到中间值。 (5)把该点像素值置为中间值; (6)把缓冲区中改动的数据复制到原数据区中。 3. 程序代码 /************************************************************** *函数名称:nnzhong(int n) *函数类型:void *参数:int n,设定的屏蔽窗口参数(奇数) *功能:对图像进行N×N中值滤波处理。 **************************************************************/ void ZaoShengXiaoChuDib::nnzhong(int n) { DWORD size; size=GetSize(); BYTE* p_temp=new BYTE [size]; memset(p_temp,255,size); int yy,xx,n2,nn,chuo,chg,m,medi,madom,mado[1000]; BYTE *p_data; //原图数据区指针 int wide,height; //原图长、宽 if(n<3||n%2!=1)//检查取值是否为3.5.7等的奇数 AfxMessageBox("请输入一个大于等于3的奇数"); if(n>=3&&n%2==1) n2=(n-1)/2; nn=n*n;chuo=(nn-1)/2; p_data=this->GetData();//取得原图的数据区指针 wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 //N×N中值滤波 for(int j=n2;j //把n*n屏蔽窗口部分的所有像素值放入mado[m] m=0; for(yy=j-n2;yy<=j+n2;yy++) for(xx=i-n2;xx<=i+n2;xx++) { mado[m]=p_data[yy*wide+xx]; m++; } //把mado[m]中的值按下降顺序用冒泡法排序 do{ chg=0; for(m=0;m if(mado[m] madom=mado[m]; mado[m]=mado[m+1]; mado[m+1]=madom; chg=1; } } }while(chg==1); //求中值medi medi=mado[chuo]; //把中值代入显示图像中 p_temp[j*wide+i]=medi; } memcpy(p_data,p_temp,size); delete p_temp; } 5.4.2 十字型中值滤波器 1 理论基础 本程序计算灰度图像f中以像素f(i,j)为中心得十字型屏蔽窗口(十字型的纵向和横向的长度为N, N=3,5,7…)内灰度值的中值u, 无条件作f(i,j)=u处理,N由用户给定。 图5-7 5×5十字型中值滤波器 2. 实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)取得N值; (3)循环取得各点像素值; (4)计算以该像素为中心的型屏蔽窗口(十字型的纵向和横向的长度为N)内灰度值中值; (5)把该点像素值置为中间值; (6)把缓冲区中改动的数据复制到原数据区中。 3 程序代码 /************************************************************** *函数名称:shizi(int n) *函数类型:void *参数:int n,设定的屏蔽窗口参数(奇数) *功能:对图像进行十字型中值滤波。 **************************************************************/ void ZaoShengXiaoChuDib::shizi(int n) { DWORD size; size=GetSize(); BYTE* p_temp=new BYTE [size]; int yy,xx,n2,nn,chuo,chg,m,medi,madom,mado[1000]; BYTE *p_data,*p; //原图数据区指针 int wide,height; //原图长、宽 if(n<3||n%2!=1)//检查取值是否为3.5.7等的奇数 AfxMessageBox("请输入一个大于等于3的奇数"); if(n>=3&&n%2==1) n2=(n-1)/2; nn=n+n-1; chuo=(nn-1)/2; p_data=this->GetData();//取得原图的数据区指针 wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 //十字型中值滤波 for(int j=n2;j //把十字型屏蔽窗口部分的所有像素值代入mado[m] //(代入含中心的垂直部分) m=0; for(yy=j-n2;yy<=j+n2;yy++) { mado[m]=p_data[yy*wide+i]; m++; } //(代入中心以外的的水平部分) for(xx=i-n2;xx<=i+n2;xx++) { if(xx==i)continue; mado[m]=p_data[j*wide+xx]; m++; } //把mado[m]的内容按下降顺序冒泡法分类 do{ chg=0; for(m=0;m if(mado[m] madom=mado[m]; mado[m]=mado[m+1]; mado[m+1]=madom; chg=1; } } }while(chg==1); //求中值medi medi=mado[chuo]; p_temp[j*wide+i]=medi; p=p_temp+j*wide+i; } memcpy(m_pData,p_temp,size); delete p_temp; } 5.4.3 N×N最大值滤波器 1 理论基础 本程序计算灰度图像f中以像素f(i,j)为中心的N×N屏蔽窗口(N=3,5,7…)内灰度的最大值为u,无条件作f(i,j)=u处理,N由用户给定 2. 实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)取得N值; (3)循环取得各点像素值; (4)取得以该点为中心的N×N屏蔽窗口包括的各点像素值中的最大值; (5)把该点像素值置为最大; (6)把缓冲区中改动的数据复制到原数据区中。 3. 程序代码 /************************************************************** *函数名称:nnzuida(int n) *函数类型:void *参数:int n,设定的屏蔽窗口参数(奇数) *功能:对图像进行N*N最大值滤波。 **************************************************************/ void ZaoShengXiaoChuDib::nnzuida(int n) { DWORD size; size=GetSize(); int yy,xx,n2,nn,m,madomax,mado[1000]; BYTE *p_data,*p; //原图数据区指针 int wide,height; BYTE* p_temp=new BYTE [size]; if(n<3||n%2!=1)//确认n是3以上的奇数 AfxMessageBox("请输入一个大于等于3的奇数"); if(n>=3&&n%2==1) n2=(n-1)/2; nn=n*n; p_data=this->GetData();//取得原图的数据区指针 memcpy(p_temp,p_data,size); wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 //n*n最大值频率滤波 for(int j=n2;j //把n*n屏蔽窗口部分复制到mado[m] m=0; for(yy=j-n2;yy<=j+n2;yy++) for(xx=i-n2;xx<=i+n2;xx++) { mado[m]=p_data[yy*wide+xx]; m++; } //找出mado[m]中的最大值madomax,用作最大频率值 madomax=mado[0]; for(m=1;m p_temp[j*wide+i]=madomax; p=p_temp+j*wide+i; } memcpy(m_pData,p_temp,size); delete p_temp; } 4处理效果 图5-8 中值滤波图 N×N中值滤波、十字型中值滤波、N*N最大值滤波之后的效果大体相同。 5.5 产生噪声 以上介绍的是消除噪声的几种方法。在实际应用中有时需要模拟噪声,下面介绍虚拟出两种噪声的方法: 随机噪声; 椒盐噪声。 5.5.1 随机噪声 1. 理论基础 本程序通过计算机所产生的随机数给图像加噪声。 2. 实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值; (3)取得随机数(rand()/1024); (4)该点像素值加上随机数; (5)把缓冲区中改动的数据复制到原数据区中。 3. 程序代码 /************************************************************** *函数名称:suijizaosheng() *函数类型:void *功能:对图像进行随机噪声处理。 **************************************************************/ void ZaoShengXiaoChuDib::suijizaosheng() { int noisepoint; DWORD size; size=GetSize(); BYTE *p_data; //原图数据区指针 int wide,height; BYTE* p_temp=new BYTE [size]; p_data=this->GetData();//取得原图的数据区指针 memcpy(p_temp,p_data,size); wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 //产生随机噪声 for(int j=0;j noisepoint=rand()/1024; p_temp[j*wide+i]=(p_temp[j*wide+i]*224/256+(int)noisepoint); } memcpy(m_pData,p_temp,size); delete p_temp; } 5.5.2椒盐噪声 1. 理论基础 本程序通过计算机所产生的随机数的大小来给图像加噪声。 2. 实现步骤 (1)取得图像大小、数据区,并把数据区复制到缓冲区中; (2)循环取得各点像素值; (3)若产生的随机数大于特定值,把该点置0; (4)把缓冲区中改动的数据复制到原数据区中。 3. 程序代码 /************************************************************** *函数名称:jiaoyanzaosheng() *函数类型:void *功能:对图像进行椒盐噪声处理。 **************************************************************/ void ZaoShengXiaoChuDib::jiaoyanzaosheng() { DWORD size; size=GetSize(); BYTE *p_data; //原图数据区指针 int wide,height; BYTE* p_temp=new BYTE [size]; p_data=this->GetData();//取得原图的数据区指针 memcpy(p_temp,p_data,size); wide=this->GetWidth(); //取得原图的数据区宽 height=this->GetHeight(); //取得原图的数据区高 for(int j=0;j if(rand()>31500) p_temp[j*wide+i]=0; } memcpy(m_pData,p_temp,size); delete p_temp; } 4.处理效果 (a) 随机噪声 (b) 椒盐噪声 图5-9 产生噪声 小结 消除图像噪声的工作称之为图像平滑或滤波。本章主要介绍在空间域图像平滑常用的方法,包括噪声消除法、邻域平均法、中值滤波,以及产生噪声的方法。图像中的噪声种类很多,对图像信号幅度和相位的影响十分复杂,必须针对具体情况采用不同的平滑滤波算法,否则很难获得满意的处理效果。 习题 1.简述平滑处理的基本方法。 2.简述消除孤立黑像素点的基本方法。 3.简述N×N均值滤波器的实现方法并编程实现。 4.简述N×N中值滤波器的实现方法并编程实现。 5.简述十字型中值滤波器的实现方法并编程实现。
平均中所取的领近像素点越多,平滑的效果越好,但会使轮廓变得模糊。由于轮廓线往往是图像中含有重要的信息部分,所以在平滑中要解决的主要矛盾是如何既能消除噪声,又能保持轮廓尽可能不模糊。f(i-1,j-1) f(i-1,j) f(i-1,j+1) f(i,j-1) f(i,j) f(i,j+1) f(i+1,j-1) f(+1,j) f(i+1,j+1)