滤波器的原理就是对一个领域(一块较小的区域),对该邻域包围的图像像素执行预定义操作,滤波产生一个新的像素,且坐标为邻域中心的坐标。滤波器的中心访问图像中每一个像素的位置,就产生了处理过的图像也就是滤波图像。简言之,选定要作用于目标位置的像素点(领域),按照一定的运算操作,把通过这些像素点得到的新的像素值赋给目标位置。
可见滤波器是由两部分组成:领域,和领域下的需要执行的预定义操作。如果这个操作是线性的,滤波器就是线性滤波器,否则就是非线性滤波器。

线性滤波器:

使用3X3的领域线性滤波器的机理如下图所示;

3x3快速中值滤波_线性滤波器

图像中任意一点(x, y)处滤波器的操作结果g(x,y)就是滤波器系数与滤波器所包围的像素乘积之和,

g(x,y)=w(-1,-1)f(x-1,y-1) + w(-1,0)f(x-1,y) + w(-1,1)f(x-1,y+1) + …… + w(1,0)f(x+1,y) + w(1,1)f(x+1,y+1)

当滤波器系数都为1时就是均值滤波器此时新的像素值为g(x,y)/9。

若系数为3x3快速中值滤波_非线性滤波器_02

为加权均值滤波器。此时新的像素值为g(x,y)/16。

实现如下:

void LinearFilter(string src)
{
    //kernel
    float arr[3][3] = {1,2,1,2,4,2,1,2,1};
    /*for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            arr[i][j] = 1;
        }
    }*/
    cv::Mat image = imread(src,0);
    namedWindow("original");
    imshow("original", image);
    cv::Mat image1 = Mat(Size(image.cols + 4, image.rows + 4), image.type(), Scalar(0));
    Mat dst = Mat(Size(image.cols + 4, image.rows + 4), image.type(), Scalar(0));
    Mat ImageROI = image1(Rect(2, 2, image.cols, image.rows));
    image.copyTo(ImageROI, image);
    Mat dst1 = Mat(Size(image.cols + 4, image.rows + 4), image.type(), Scalar(0));
//加权均值滤波器
    for (int i = 2; i < image1.rows - 2; i++)
    {
        for (int j = 2; j < image1.cols - 2; j++)
        {
            if (image.channels() == 1)
            {
                uchar data = (arr[0][0] * image1.at<uchar>(i - 1, j - 1) + arr[0][1] * image1.at<uchar>(i - 1, j) + arr[0][2] * image1.at<uchar>(i - 1, j + 1) + arr[1][0] * image1.at<uchar>(i, j - 1) + arr[1][1] * image1.at<uchar>(i, j) + arr[1][2] * image1.at<uchar>(i, j + 1) + arr[2][0] * image1.at<uchar>(i + 1, j - 1) + arr[2][1] * image1.at<uchar>(i + 1, j) + arr[2][2] * image1.at<uchar>(i + 1, j + 1))/16;

                dst1.at<uchar>(i, j) = data;
            }
        }
    }
    //均值滤波器
    for (int i = 2; i < image1.rows - 2; i++)
    {
        for (int j = 2; j < image1.cols - 2; j++)
        {
            if (image.channels() == 1)
            {
                uchar data = (image1.at<uchar>(i - 1, j - 1) +  image1.at<uchar>(i - 1, j) + image1.at<uchar>(i - 1, j + 1) + image1.at<uchar>(i, j - 1) + image1.at<uchar>(i, j) +  image1.at<uchar>(i, j + 1) + image1.at<uchar>(i + 1, j - 1) +  image1.at<uchar>(i + 1, j) + image1.at<uchar>(i + 1, j + 1)) / 9;

                dst.at<uchar>(i, j) = data;
            }
        }
    }
    namedWindow("linear9");
    imshow("linear9", dst);
    namedWindow("linear16");
    imshow("linear16", dst1);
}

注意:需要在计算data值时就进行取平均值,uchar类型的最大值是255,当data超过255时,就不再是想要的值。

高斯滤波原理是和均值滤波一样的,只是滤波器系数的变化而已,产生Guass滤波系数的函数是3x3快速中值滤波_高斯滤波_03

以sigma=1,3X3的高斯滤波为例:

void guassFilter(Mat &src, Mat &dst)
{
    double pi = 3.14159265358;
    double sum = 0;
    double arr[3][3];
    for (int i = 1; i < 4; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            arr[i-1][j-1] = exp(-((i-2)*(i-2) + (j-2)*(j-2)) / (2*1.0)) / (2 * pi);
            sum += arr[i - 1][j - 1];
        }
    }
    //归一化
    for (int i = 1; i < 4; i++)
    {
        for (int j = 1; j < 4; j++)
        {
            arr[i - 1][j - 1] = arr[i - 1][j - 1] / sum;            
        }
    }


    for (int i = 2; i < dst.rows - 2; i++)
    {
        for (int j = 2; j < dst.cols - 2; j++)
        {
            if (src.channels() == 1)
            {
                uchar data = (arr[0][0] * src.at<uchar>(i - 1, j - 1) + arr[0][1] * src.at<uchar>(i - 1, j) + arr[0][2] * src.at<uchar>(i - 1, j + 1) + arr[1][0] * src.at<uchar>(i, j - 1) + arr[1][1] * src.at<uchar>(i, j) + arr[1][2] * src.at<uchar>(i, j + 1) + arr[2][0] * src.at<uchar>(i + 1, j - 1) + arr[2][1] * src.at<uchar>(i + 1, j) + arr[2][2] * src.at<uchar>(i + 1, j + 1));

                dst.at<uchar>(i, j) = data;
            }
        }
    }
    namedWindow("Guass");
    imshow("Guass", dst);
}

非线性滤波器

非线性滤波器以3X3的中指滤波器为例:
中指滤波器
是将邻域所包围的图像像素进行排序,取其中值赋值给滤波器中心位置的像素。
代码如下

void swap(uchar *p1, uchar *p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}
//内插排序
void insertSort(uchar *a, int len)
{
    int i, j;
    for (i = 0; i<len; i++)
    {
        for (j = i + 1; j >= 1; j--)
        {
            if (a[j]<a[j - 1])
                swap(&a[j], &a[j - 1]);
        }
    }
}

void midValueFilter(string src)
{
    cv::Mat image = imread(src, 0);
    namedWindow("original");
    imshow("original", image);
    cv::Mat image1 = Mat(Size(image.cols + 4, image.rows + 4), image.type(), Scalar(0));
    Mat dst = Mat(Size(image.cols + 4, image.rows + 4), image.type(), Scalar(0));
    Mat ImageROI = image1(Rect(2, 2, image.cols, image.rows));
    image.copyTo(ImageROI, image);
    for (int i = 2; i < image1.rows - 2; i++)
    {
        for (int j = 2; j < image1.cols - 2; j++)
        {
            if (image.channels() == 1)
            {
                uchar data[9] = { image1.at<uchar>(i - 1, j - 1) , image1.at<uchar>(i - 1, j) , image1.at<uchar>(i - 1, j + 1) ,image1.at<uchar>(i, j - 1) , image1.at<uchar>(i, j) , image1.at<uchar>(i, j + 1),image1.at<uchar>(i + 1, j - 1) , image1.at<uchar>(i + 1, j) , image1.at<uchar>(i + 1, j + 1) };
                insertSort(data, 9);
                dst.at<uchar>(i, j) = data[4];
            }
        }
    }
    namedWindow("midValue");
    imshow("midValue", dst);
}

我在使用vs2013运行时会出现数组越界异常

3x3快速中值滤波_高斯滤波_04

在网上找了很多技术文章大都是说,项目占用堆栈较大时,超出了vs编译器限定的对战的大小,就会报异常。

把 project->配置属性->c/c++->代码生成->基本运行时检查 设置为 “默认值”即可解决问题