• 滤波、形态学腐蚀与卷积(合集)


    https://blog.csdn.net/qq_36285879/article/details/82810705

    S1.1 滤波、形态学腐蚀与卷积(合集)

    参考:《学习OpenCV3》、《数字图像处理编程入门》
    文章目录

    S1.1 滤波、形态学腐蚀与卷积(合集)
    滤波器
    简单模糊与方形滤波
    中值滤波
    高斯滤波
    双边滤波
    导数和梯度
    Sobel算子
    Scharr滤波器
    拉普拉斯变换
    图像形态学
    膨胀&腐蚀
    通用形态学函数
    开操作与闭操作
    形态学梯度
    顶帽和黑帽
    滤波器

    毛星云那本有滤波器源码解析。

    简单模糊与方形滤波

    用blur函数实现
    在imgproc中我们找到定义:(为了大家训练英文,就不翻译了,其实是我懒-。-)

    /*
    The call `blur(src, dst, ksize, anchor, borderType)` is equivalent to `boxFilter(src, dst, src.type(), anchor, true, borderType)`.和boxFilter相等,待会会讲

    @param src input image; it can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
    @param dst output image of the same size and type as src.
    @param ksize blurring kernel size.
    @param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel
    center.默认为中心
    @param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes
    @sa boxFilter, bilateralFilter, GaussianBlur, medianBlur
    */
    CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
    Size ksize, Point anchor = Point(-1,-1),
    int borderType = BORDER_DEFAULT );
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3;

    blur(src, dst0, Size(5,5), Point(-1,-1), BORDER_DEFAULT);

    imshow("src", src);
    imshow("blur5x5,default", dst0);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    这是一种低通滤波器(low pass filter),把中间像素值替换为21x21个像素的平均值。

    在链接中就讲到:

    在灰度连续变化的图象中,如果出现了与相邻象素的灰度相差很大的点,比如说一片暗区中突然出现了一个亮点,人眼能很容易觉察到。就象看老电影时,由于胶片太旧,屏幕上经常会出现一些亮斑。这种情况被认为是一种噪声。灰度突变在频域中代表了一种高频分量,低通滤波器的作用就是滤掉高频分量,从而达到减少图象噪声的目的。当然这种方法会对原本的图像造成伤害。

    用boxFilter函数实现
    多了一个ddepth参数(图像深度),一个normalize参数(归不归一化,就是除不除滤波器面积)

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3;

    blur(src, dst0, Size(11,11), Point(-1,-1), BORDER_DEFAULT);
    boxFilter(src, dst1, CV_8U, Size(11, 11), Point(-1, -1), true, BORDER_DEFAULT);
    boxFilter(src, dst2, CV_8U, Size(11, 11), Point(-1, -1), false, BORDER_DEFAULT);
    boxFilter(src, dst3, CV_16U, Size(11, 11), Point(-1, -1), false, BORDER_DEFAULT);

    imshow("src", src);
    imshow("blur11x11,default", dst0);
    imshow("boxFilter11x11,default", dst1);
    imshow("boxFilter11x11,false,default", dst2);
    imshow("boxFilter11x11,16U,default", dst3);


    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27


    中值滤波

    之前找的是平均值,现在找的是中值。这也是一种低通滤波器

    参数类型Size也变为了int,只能处理正方形区域。(不知道为啥要这样改)

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3;

    blur(src, dst0, Size(11,11), Point(-1,-1), BORDER_DEFAULT);
    medianBlur(src, dst1, 11);

    imshow("src", src);
    imshow("blur11x11,default", dst0);
    imshow("medianBlur11", dst1);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22


    很有艺术风格呀。可能就是因为中值滤波是实实在在的图像原有的像素值的原因。

    高斯滤波

    是一种有效的滤波器。

    因为以模板中心位置为原点,所以二维高斯公式中的μ1与μ2 mu_1与mu2μ
    1

    与μ2都等于0。

    σ sigmaσ越大,高斯函数越不凸,对应的卷积模板大小也就是越大。

    /*
    @param src input image; the image can have any number of channels, which are processed
    independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.输入
    @param dst output image of the same size and type as src.输出
    @param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be positive and odd. Or, they can be zero's and then they are computed from sigma.

    @param sigmaX Gaussian kernel standard deviation in X direction.
    @param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be equal to sigmaX, if both sigmas are zeros, they are computed from ksize. width and ksize.height, respectively (see #getGaussianKernel for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ksize, sigmaX, and sigmaY.如果sigmaY=0,(默认也是0),就和sigmaX相等。如果sigmaX=sigmaY=0,sigmaX和sigmaY自动适应ksize。具体看getGaussianKernel函数

    @param borderType pixel extrapolation method, see #BorderTypes

    @sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur
    */
    CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT );
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    getGaussianKernel函数
    getGaussianKernel返回一个高斯核矩阵,不过它输出的是一维矩阵。。。具体可以看这篇blog:

    https://blog.csdn.net/u012633319/article/details/80921023

    CV_EXPORTS_W Mat getGaussianKernel( int ksize, double sigma, int ktype = CV_64F );
    1
    高斯滤波演示
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3;

    blur(src, dst0, Size(11,11), Point(-1,-1), BORDER_DEFAULT);
    medianBlur(src, dst1, 11);
    GaussianBlur(src, dst2, Size(11,11), 0, 0);

    imshow("src", src);
    imshow("blur11x11,default", dst0);
    imshow("medianBlur11", dst1);
    imshow("GraussianBlur11,1,1", dst2);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24


    可以看出,由于加强与邻域的关系,高斯滤波更不模糊。(也有可能是参数的原因)

    高斯模糊把边缘也模糊了,边缘变粗。

    双边滤波

    能解决高斯不能解决的边缘问题。

    《学习OpenCV》说能产生卡通效果,我感觉看不出来,如果调的好,说不定能产生倩女幽魂的效果。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3;

    blur(src, dst0, Size(11,11), Point(-1,-1), BORDER_DEFAULT);
    medianBlur(src, dst1, 11);
    GaussianBlur(src, dst2, Size(11,11), 2, 0);
    bilateralFilter(src, dst3, 16, 150, 150);

    imshow("src", src);
    imshow("blur11x11,default", dst0);
    imshow("medianBlur11", dst1);
    imshow("GraussianBlur11,1,1", dst2);
    imshow("bilateralFilter11", dst3);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26


    导数和梯度

    卷积可以近似计算导数,当然这个涉及时域的知识。

    从梯度图我们可以观察到图像的边缘了。

    Sobel算子

    百度百科就可以理解Sobel算子是怎么回事了:

    https://baike.baidu.com/item/Sobel算子/11000092?fr=aladdin

    其实就是常用的两个固定模板。

    当然还是强烈推荐好书:《数字图像处理编程入门》:微盘数字图像处理编程入门 里面用很简洁的方法解释了Sobel为啥能检测边缘。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {

    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1, dst2, dst3, dst4;

    Sobel(src, dst0, CV_8U, 1, 1);
    Sobel(src, dst1, CV_8U, 0, 1);
    Sobel(src, dst2, CV_8U, 1, 0);
    Sobel(src, dst3, CV_8U, 1, 2);
    Sobel(src, dst4, CV_8U, 2, 1);

    imshow("src", src);
    imshow("Sobel,1,1", dst0);
    imshow("Sobel,0,1", dst1);
    imshow("Sobel,1,0", dst2);
    imshow("Sobel,1,2", dst3);
    imshow("Sobel,2,1", dst4);


    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32


    我们对x求导则得到竖的边缘,对y求导得到横的边缘。

    Sobel算子的缺点是核较小的时候准确率不高。对于大型的核,精度不太显著。(用Scharr滤波器可以解决)【不是很理解】

    Scharr滤波器

    同Sobel一样,是常用的固定模板。

    把Sobel函数的ksize设置为CV_SCHARR即可。

    可以自己试一下,感觉没差。

    拉普拉斯变换

    经典变换

    这是一种高通滤波器。(中间为负数)

    直接上代码好了:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src = imread("images/favorite/Lena.jpg");
    Mat dst0, dst1, dst2, dst3, dst4;

    Laplacian(src, dst0, CV_8U, 1);
    Laplacian(src, dst2, CV_8U, 3);

    imshow("src", src);
    imshow("Laplacian,1", dst0);
    imshow("Laplacian,3", dst2);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22


    效果的确很好。

    图像形态学

    图像形态学能做出好玩的一些操作。

    膨胀&腐蚀

    膨胀和腐蚀是相反的一组操作。它依据图像的亮度。

    在OpenCV中,膨胀和腐蚀的边界用的的空白常量填充(BORDER_CONSTANT)。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src;
    Mat dst0, dst1;

    src = imread("images/favorite/Lena.jpg");

    // int g_nStructElementSize = 3;

    // Mat element0 = getStructuringElement(MORPH_RECT,
    // Size(2*g_nStructElementSize+1, 2*g_nStructElementSize+1),
    // Point(g_nStructElementSize, g_nStructElementSize));
    // Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));

    erode(src, dst0, Mat());
    dilate(src, dst1, Mat());

    imshow("erode", dst0);
    imshow("dilate", dst1);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30


    左边杨幂,右边白眼妞。哈哈哈。

    另外,膨胀操作能把一张小狗图变成毛绒小狗图。

    通用形态学函数

    这个函数包含了多种形态学方法,由参数op决定:

    操作值 形态学操作名 是否需要零时图像
    cv::MOP_OPEN 开操作 否
    cv::MOP_CLOSE 闭操作 否
    cv::MOP_GRADIENT 形态学梯度 总是需要
    cv::MOP_TOPHAT 顶帽操作 就地需要
    cv::MOP_BACKHAT 底帽操作 就地需要
    开操作与闭操作

    开操作就把图像先腐蚀后膨胀。闭操作反之。

    当你设定多次迭代的时候,实际顺序是:膨胀-膨胀-腐蚀-腐蚀

    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src;
    Mat dst0, dst1, dst2, dst3;

    src = imread("images/favorite/Lena.jpg");

    morphologyEx(src, dst0, CV_MOP_OPEN, Mat(), Point(-1, -1), 2);
    morphologyEx(src, dst1, CV_MOP_OPEN, Mat(), Point(-1, -1), 3);
    morphologyEx(src, dst2, CV_MOP_CLOSE, Mat(), Point(-1, -1), 2);
    morphologyEx(src, dst3, CV_MOP_CLOSE, Mat(), Point(-1, -1), 3);

    imshow("open2", dst0);
    imshow("open3", dst1);
    imshow("close2", dst2);
    imshow("close3", dst3);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27


    形态学梯度

    gradient(src)=dilate(src)−erode(src) gradient(src) = dilate(src) - erode(src)
    gradient(src)=dilate(src)−erode(src)

    //
    //// cout << src;
    // cvtColor(src, src, CV_HSV2BGR);
    // imshow("src", src);
    //
    // waitKey(0);
    // return 0;
    //}


    //16.腐蚀与膨胀
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include <cstdio>

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat src;
    Mat dst0, dst1, dst2, dst3;

    src = imread("images/favorite/Lena.jpg", 0);

    morphologyEx(src, dst0, CV_MOP_GRADIENT, Mat());

    imshow("gradient", dst0);

    waitKey(0);
    return 0;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32


    有点像粉笔画。

    顶帽和黑帽

    TopHat(src)=src−open(src)BackHat(src)=close(src)−src TopHat(src) = src-open(src)\BackHat(src)= close(src) - src
    TopHat(src)=src−open(src)
    BackHat(src)=close(src)−src


    ---------------------
    作者:nerd呱呱
    来源:CSDN
    原文:https://blog.csdn.net/qq_36285879/article/details/82810705
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    vue打包编译报错,These dependencies were not found:core-js/modules/es
    JS 新语法「可选链」「双问号」已进入 Stage 3
    vue 本地和线上跨域的问题 个人解决方案
    vue-router懒加载或者按需加载
    brew 切换国内的源
    vue 数组、对象 深度拷贝和赋值
    全局axios默认值 和 自定义实例默认值
    npm install 报node-sass错误
    linux端口探测
    linux批量操作(一)
  • 原文地址:https://www.cnblogs.com/jukan/p/10947321.html
Copyright © 2020-2023  润新知