OpenCV 彩色图像的自适应中值滤波 C++

前面讲过中值滤波,只要脉冲噪声的空间密度不大,性能就会很好(根据经验Pa和Pb小于0.2)。见 https://blog.csdn.net/cyf15238622067/article/details/87854437

下面证明 自适应中值滤波可处理更大概率(密度)的脉冲噪音;

自适应中值滤波的另一个优点是平滑非脉冲噪声时试图保留细节,这是传统中指滤波所做不到的。

自适应中值滤波也工作在Sxy内,只是会根据某些条件改变(增大)Sxy的尺寸。记住滤波器的输出是一个单值,用于替代xy出的值。

点(x,y)是给定时刻窗口Sxy的中心。

记录如下符号:

                                             z_{min}=S_{xy} 中的最小灰度值

                                            z_{max}=S_{xy} 中的最大灰度值

                                           z_{med}=S_{xy}  中灰度值的中值

                                           z_{xy}= 是坐标(x,y)的灰度值

                                           S_{max}=S_{xy} 允许的最大尺寸

自适应中值滤波以两个进程工作,表示进程A和进程B;如下所示:

进程A:                           A1 = z_{med} - z_{min}

                                        A2 = z_{med} - z_{max}

                                       如果A1 > 0 且 A2 < 0,则转到进程B

                                       否则增大窗口尺寸

                                       如果窗口尺寸小于S_{xy},则重复进程A

                                       否则输出z_{med}

进程B:                         B1 = z_{xy}-z_{min}

                                      B2 = z_{xy}-z_{max}

                                       如果B1> 0 且 B2 < 0 ,则输出z_{xy}

                                      否则输出z_{med}

理解该算法的机理关键在于,记住它的3个目的:去除椒盐(脉冲)噪音,平滑其他非脉冲噪音,并减少诸如物体边界细化或粗化等失真;

z_{min} z_{max}在算法统计上认为是类脉冲噪声分量,即使他们在图像中不是最低或最高的可能像素值。

进程A 是先判断中值是不是脉冲(噪音),如果是增大窗口继续寻找中值,如果不是转到进程B

进程B是判断当前位置像素值是不是脉冲(噪音),如果不是保留自己值,如果是用中值替代。

这样有些像素值不变,减小了失真。

如果噪音越大,则需要的窗口越大,消耗时间越长。

 

例子:

原始图像 -----------------------------------------------------------------噪音图像

中值滤波   -----------------------------------------------------------------------------------自适应中值滤波

 

代码实现:

#include
#include
#include
#include

//盐噪声
void saltNoise(cv::Mat img, int n)
{
    int x, y;
    for (int i = 0;i < n / 2;i++)
    {
        x = std::rand() % img.cols;
        y = std::rand() % img.rows;
        if (img.type() == CV_8UC1)
        {
            img.at(y, x) = 255;
        }
        else if (img.type() == CV_8UC3)
        {
            img.at(y, x)[0] = 255;
            img.at(y, x)[1] = 255;
            img.at(y, x)[2] = 255;
        }
    }
}

//椒噪声
void pepperNoise(cv::Mat img, int n)
{
    int x, y;
    for (int i = 0;i < n / 2;i++)
    {
        x = std::rand() % img.cols;
        y = std::rand() % img.rows;
        if (img.type() == CV_8UC1)
        {
            img.at(y, x) = 0;
        }
        else if (img.type() == CV_8UC3)
        {
            img.at(y, x)[0] = 0;
            img.at(y, x)[1] = 0;
            img.at(y, x)[2] = 0;
        }
    }
}

// 中值滤波器
uchar medianFilter(cv::Mat img, int row, int col, int kernelSize)
{
    std::vector pixels;
    for (int y = -kernelSize / 2;y <= kernelSize / 2;y++)
    {
        for (int x = -kernelSize / 2;x <= kernelSize / 2;x++)
        {
            pixels.push_back(img.at(row + y, col + x));
        }
    }
    sort(pixels.begin(), pixels.end());
    auto med = pixels[kernelSize*kernelSize / 2];
    return med;
}

// 自适应中值滤波器
uchar adaptiveMedianFilter(cv::Mat &img, int row, int col, int kernelSize, int maxSize)
{
    std::vector pixels;
    for (int y = -kernelSize / 2;y <= kernelSize / 2;y++)
    {
        for (int x = -kernelSize / 2;x <= kernelSize / 2;x++)
        {
            pixels.push_back(img.at(row + y, col + x));
        }
    }

    sort(pixels.begin(), pixels.end());

    auto min = pixels[0];
    auto max = pixels[kernelSize*kernelSize - 1];
    auto med = pixels[kernelSize*kernelSize / 2];
    auto zxy = img.at(row, col);
    if (med > min && med < max)
    {
        // to B
        if (zxy > min && zxy < max)
            return zxy;
        else
            return med;
    }
    else
    {
        kernelSize += 2;
        if (kernelSize <= maxSize)
            return adaptiveMedianFilter(img, row, col, kernelSize, maxSize);// 增大窗口尺寸,继续A过程。
        else
            return med;
    }
}


int main( int argc, char *argv[])
{
    int minSize = 3;
    int maxSize = 7;
    cv::Mat img;
    img = cv::imread(argv[1]);
//    cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);
    cv::imshow("src", img);
    saltNoise(img, 200000);
    pepperNoise(img, 200000);
    cv::imshow("noise", img);

    cv::Mat temp = img.clone();
    std::vector bgr;
    cv::split(img, bgr );
    cv::copyMakeBorder(bgr[0], bgr[0], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
    cv::copyMakeBorder(bgr[1], bgr[1], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
    cv::copyMakeBorder(bgr[2], bgr[2], maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, cv::BorderTypes::BORDER_REFLECT);
    for (int j = maxSize / 2;j < bgr[0].rows - maxSize / 2;j++)
    {
        for (int i = maxSize / 2;i < bgr[0].cols - maxSize / 2;i++)
        {
            bgr[0].at(j, i) = adaptiveMedianFilter(bgr[0], j, i, minSize, maxSize);
            bgr[1].at(j, i) = adaptiveMedianFilter(bgr[1], j, i, minSize, maxSize);
            bgr[2].at(j, i) = adaptiveMedianFilter(bgr[2], j, i, minSize, maxSize);
        }
    }
    cv::Mat color_dst;
    cv::merge(bgr,color_dst );
    cv::imshow("sdapt_color_dst", color_dst);


    // 中值滤波
    cv::Mat img2, media_dst;
//    int kernelSize = 3;
//    cv::copyMakeBorder(temp, img2, kernelSize / 2, kernelSize / 2, kernelSize / 2, kernelSize / 2, cv::BorderTypes::BORDER_REFLECT);
//    for (int j = kernelSize / 2;j < img2.rows - kernelSize / 2;j++)
//    {
//        for (int i = kernelSize / 2;i < img2.cols - kernelSize / 2;i++)
//        {
//            img2.at(j, i) = medianFilter(img2, j, i, kernelSize);
//        }
//    }
//    cv::imshow("medianFilter", img2);
    cv::medianBlur(temp,media_dst, 3 );
    cv::imshow("media_dst", media_dst);
    cv::waitKey();
    cv::destroyAllWindows();

    return 0;
}
 

你可能感兴趣的:(OpenCV,C++,数字图像处理,OpenCV,C++数字图像处理,冈萨雷斯第三版)