中值滤波是一种典型的非线性滤波技术。它在一定条件下可以克服线性滤波器如最小均方滤波,均值滤波等带来的图像细节模糊,而且对滤波脉冲干扰及图像扫描噪声非常有效。
传统的中值滤波一般采用含有奇数个点的滑动窗口,用窗口中各点灰度值的中值来替代指定点的灰度值。对于奇数个元素,中值为大小排序后中间的数值;对于偶数个元素,中值为排序后中间两个元素灰度值的平均值。
中值滤波是一种典型的低通滤波器,主要用来抑制脉冲噪声,它能彻底滤除尖波干扰噪声,同时又具有能较好地保护目标图像边缘的特点。
标准一维中值滤波器的定义为:
yk = med{ xK-N, xk-N+1, ... ,xk, ... ,xK+N }
式中,med表示取中值操作。中值滤波的滤波方法是对滑动滤波窗口(2N+1)内的像素做大小排序,滤波结果的输出像素值规定为该序列的中值。
二维中值滤波的窗口形状和尺寸设计对滤波的效果影响较大,不同的图像内容和不同的应用要求,往往采用不同的形状和尺寸。常用的二维中值滤波窗口有线状,方形,圆形,十字形及圆环形等,窗口尺寸一般选为3。当然也可以采用其他尺寸,主要视具体情况而定。
1.中值的计算关键在于对滑动窗口内的像素进行排序,排序算法的选择是影响中值滤波算法的重要因素。传统的排序算法是基于冒泡排序法,若窗口中的像素为m,则每个窗口排序需要m(m-2)/2次像素的比较操作,时间复杂度为O(m2)。此外常规的滤波算法每移动一次窗口就要进行一次排序。当一幅图像的大小为NXN时,则整个计算需要O(m2N2)时间,当窗口较大时,计算量很大,较费时。
2.为了提高中值滤波的实现速度,针对3X3中值滤波,介绍一种快速的并行中值滤波方法。下图为3X3窗口内像素排列
首先对窗口内的每一列分别计算最大值,中值和最小值,这样就得到了3组数据
最大值组:Max0 = max[P0,P3,P6],Max1 = max[P1,P4,P7],Max2 = max[P2,P5,P8]
中值组: Med0 = med[P0,P3,P6],Med1 = med[P1,P4,P7], Med2 = med[P2,P5,P8]
最小值组:Min0 = Min[P0,P3,P6],Min1 = Min[P1,P4,P7],Min2 = max[P2,P5,P8]
由此可以看到,最大值组中的最大值与最小值组中的最小值一定是9个元素中的最大值和最小值,不可能为中值,剩下7个;中值组中的最大值至少大于5个像素,中值组中的最小值至少小于5个像素,不可能为中值,剩下5个;最大值组中的中值至少大于5个元素,最小值组中的中值至少小于5个元素,不可能为中值,最后剩下3个要比较的元素,即
最大值组中的最小值Maxmin,中值组中的中值Medmed,最小值组中的最大值MinMax;找出这三个值中的中值为9个元素的中值。
算法实现:
View Code
/**********************************************************************
*
* 函数名称:
* MedFilter(int FilterH, int FilterW, int FilterCX, int FilterCY)
*
* 参数:
* int FilterH 模板的高度
* int FilterW 模板的宽度
* int FilterCX 模板的中心元素X坐标 ( < FilterW - 1)
* int FilterCY 模板的中心元素Y坐标 ( < FilterH - 1)
*
* 返回值:
* void
*
* 说明:
* 中值滤波的算法
*
**********************************************************************/
void CImgEnhance::MedianFilter(int FilterH, int FilterW, int FilterCX, int FilterCY)
{
unsigned char* pSrc;
unsigned char* pDst;
int i,j,k,l;
unsigned char* value; //指向滤波器数组的指针
if(m_pImgDataOut != NULL)
{
delete []m_pImgDataOut;
m_pImgDataOut = NULL;
}
//计算图像每行的字节数
int lineByte = (m_imgWidth * m_nBitCount / 8 + 3) / 4 * 4;
if(m_nBitCount != 8)
{
AfxMessageBox("只能处理8位灰度图像!");
return ;
}
//分配内存,以保存新图像
m_nBitCountOut = m_nBitCount;
int lineByteOut = (m_imgWidth * m_nBitCountOut / 8 + 3) / 4 * 4;
if (!m_pImgDataOut)
{
//为处理后的图像分配内存空间
m_pImgDataOut = new unsigned char[lineByteOut * m_imgHeight];
}
int pixelByte = m_nBitCountOut / 8;
for(i = 0; i < m_imgHeight; i++){
for(j = 0; j < m_imgWidth * pixelByte; j++)
*(m_pImgDataOut + i * lineByteOut +j) = *(m_pImgData + i * lineByteOut + j);
}
//暂时分配内存,以保存滤波器数组
value = new unsigned char[FilterH * FilterW];
for (i = FilterCY; i < m_imgHeight - FilterH ; i++)//+ FilterCY + 1
{
for (j = FilterCX; j < m_imgWidth - FilterW ; j++)//+ FilterCX + 1
{
pDst = m_pImgDataOut + lineByte * (m_imgHeight - 1 - i) + j;
for (k = 0; k < FilterH; k++)
{
for (l = 0; l < FilterW; l++)
{
pSrc = m_pImgData + lineByte * (m_imgHeight - l - i
+ FilterCY - k) + j - FilterCX + l;
value[k * FilterW + l] = *pSrc;
}
}
*pDst = FindMedianValue(value,FilterW * FilterH);
}
}
}