中值滤波是一种典型的非线性滤波技术。它在一定条件下可以克服线性滤波器如最小均方滤波,均值滤波等带来的图像细节模糊,而且对滤波脉冲干扰及图像扫描噪声非常有效。

传统的中值滤波一般采用含有奇数个点的滑动窗口,用窗口中各点灰度值的中值来替代指定点的灰度值。对于奇数个元素,中值为大小排序后中间的数值;对于偶数个元素,中值为排序后中间两个元素灰度值的平均值。

中值滤波是一种典型的低通滤波器,主要用来抑制脉冲噪声,它能彻底滤除尖波干扰噪声,同时又具有能较好地保护目标图像边缘的特点。

标准一维中值滤波器的定义为:

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窗口内像素排列

中值滤波器 java 中值滤波器工作原理_中值滤波器 java

中值滤波器 java 中值滤波器工作原理_中值滤波_02

中值滤波器 java 中值滤波器工作原理_中值滤波器 java_03

首先对窗口内的每一列分别计算最大值,中值和最小值,这样就得到了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个元素的中值。

算法实现:

中值滤波器 java 中值滤波器工作原理_中值滤波_04

中值滤波器 java 中值滤波器工作原理_中值滤波_05

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);
        }
    }
}