美文网首页互联网@时事传播图像处理
OpenCV图像处理系列八 --- 腐蚀与膨胀

OpenCV图像处理系列八 --- 腐蚀与膨胀

作者: 307656af5a04 | 来源:发表于2019-04-22 07:17 被阅读13次

    今天,我们一起来学习图像形态学操作中两种最基本的形态学操作,即腐蚀与膨胀。

    一、理论

    数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换等。

    腐蚀与膨胀主要实现以下功能:

    • 消除噪声
    • 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素。
    • 寻找图像中的明显的极大值区域或极小值区域
    • 求出图像的梯度

    1、腐蚀

    腐蚀对图像求局部最小值;腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与卷积核(我们称之为B)进行卷积。

    核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。

    1)腐蚀的数学表达式

    腐蚀的数学表达式

    2)腐蚀效果演示

    腐蚀效果演示

    3)腐蚀的作用:

    • 对象大小减小一个像素(3x3)
    • 平滑对象边缘
    • 弱化或者分割图像之间的半岛型连接

    2、膨胀

    膨胀对图像求局部的最大值;核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素;这样就会使图像中的高亮区域逐渐增长。

    核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。

    1)膨胀的数学表达式

    膨胀的数学表达式 膨胀

    膨胀的作用:

    • 对象大小增加一个像素(3x3)
    • 平滑对象边缘
    • 减少或者填充对象之间的距离

    小结:

    可以看做膨胀是将白色区域扩大,腐蚀是将黑色区域扩大。

    二、OpenCV腐蚀与膨胀API函数详解

    1、腐蚀

    1)函数原型:

    void erode(InputArray src, 
        OutputArray dst, 
        InputArray kernel,
        Point anchor = Point(-1, -1), 
        int iterations = 1,
        int borderType = BORDER_CONSTANT,
        const Scalar& borderValue = 
        morphologyDefaultBorderValue());
    

    2)函数功能:

    用指定的卷积核腐蚀图像,使用像素邻域内的局部极小运算符来腐蚀一张图片,从src输入,由dst输出,支持就地(in-place)操作。

    3)参数详解:

    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一;

    • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型;

    • 第三个参数,InputArray类型的kernel,腐蚀操作的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵);

    • 第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心,我们一般不用管它;

    • 第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1;

    • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT;

    • 第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。

    第三个参数一般用getStructuringElement()函数获取卷积核的大小;getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。

    其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:

    • 矩形: MORPH_RECT
    • 交叉形: MORPH_CROSS
    • 椭圆形: MORPH_ELLIPSE

    而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。

    我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1, -1),表示锚点位于中心;并且需要注意,十字形的element形状唯一依赖于锚点的位置;而在其他情况下,锚点只是影响了形态学运算结果的偏移。

    4)实例:

    #include <iostream>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
        //载入原图 
        Mat image = imread("lena.png");
    
        if (image.empty())
        {
            printf("image error!");
            return -1;
        }
    
        //创建窗口 
        namedWindow("【原图】腐蚀操作");
        namedWindow("【效果图】腐蚀操作");
    
        //显示原图
        imshow("【原图】腐蚀操作", image);
    
    
        //获取自定义核
        Mat element = getStructuringElement(
            MORPH_RECT, 
            Size(15, 15));
    
        Mat out;
    
        //进行腐蚀操作
        erode(image, out, element);
    
        //显示效果图
        imshow("【效果图】腐蚀操作", out);
    
        waitKey(0);
    
        return 0;
    }
    

    实验结果:

    卷积核为15腐蚀的实验结果

    2、膨胀

    1)函数原型:

    void dilate(InputArray src,
        OutputArray dst,
        InputArray kernel,
        Point anchor = Point(-1, -1),
        int iterations = 1,
        int borderType = BORDER_CONSTANT,
        const Scalar& borderValue =
        morphologyDefaultBorderValue());
    

    2)函数功能:
    使用像素邻域内的局部极大运算符来膨胀一张图片,从src输入,由dst输出,支持就地(in-place)操作。

    3)参数详解:

    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一;

    • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型;

    • 第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核;

    • 第四个参数,Point类型的anchor,锚的位置,其有默认值( - 1, - 1),表示锚位于中心;

    • 第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1;

    • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT;

    • 第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。

    4)实例:

    int main()
    {
    
        //载入原图 
        Mat image = imread("3.png");
    
        if (image.empty())
        {
            printf("image error!");
            return -1;
        }
    
        //创建窗口 
        namedWindow("【原图】膨胀操作");
        namedWindow("【效果图】膨胀操作");
    
        //显示原图
        imshow("【原图】膨胀操作", image);
    
        //获取自定义核
        Mat element = getStructuringElement(
            MORPH_RECT, 
            Size(5, 5));
    
        Mat out;
        //进行膨胀操作
        dilate(image, out, element);
    
        //显示效果图
        imshow("【效果图】膨胀操作", out);
    
        waitKey(0);
    
        return 0;
    }
    

    实验结果:

    卷积核为5膨胀的实验结果

    三、综合实例

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    //原始图和效果图
    Mat g_srcImage, g_dstImage;
    
    //0表示腐蚀erode, 1表示膨胀dilate
    int g_nTrackbarNumer = 0;
    
    //结构元素(内核矩阵)的尺寸
    int g_nStructElementSize = 3; 
    
    //膨胀和腐蚀的处理函数
    void Process();
    
    //回调函数
    void on_TrackbarNumChange(int, void *);
    
    //回调函数
    void on_ElementSizeChange(int, void *);
    
    int main()
    {
        //载入原图
        g_srcImage = imread("lena.png");
    
        if (!g_srcImage.data) 
        { 
            printf("image error!");
            return -1; 
        }
    
        //显示原始图
        namedWindow("【原始图】");
        imshow("【原始图】", g_srcImage);
    
        //进行初次腐蚀操作并显示效果图
        namedWindow("【效果图】");
    
        //获取自定义核
        Mat element = getStructuringElement(
            MORPH_RECT, 
            Size(2 * g_nStructElementSize + 1, 
                2 * g_nStructElementSize + 1), 
            Point(g_nStructElementSize, 
                g_nStructElementSize));
        
        erode(g_srcImage, g_dstImage, element);
        
        imshow("【效果图】", g_dstImage);
    
        //创建轨迹条
        createTrackbar("腐蚀/膨胀",
            "【效果图】", 
            &g_nTrackbarNumer, 
            1, 
            on_TrackbarNumChange);
    
        createTrackbar("内核尺寸", 
            "【效果图】", 
            &g_nStructElementSize, 
            21, 
            on_ElementSizeChange);
    
        waitKey(0);
        return 0;
    }
    
    //进行自定义的腐蚀和膨胀操作
    void Process()
    {
        //获取自定义核
        Mat element = getStructuringElement(
            MORPH_RECT, 
            Size(2 * g_nStructElementSize + 1, 
                2 * g_nStructElementSize + 1), 
            Point(g_nStructElementSize, 
                g_nStructElementSize));
    
        //进行腐蚀或膨胀操作
        if (g_nTrackbarNumer == 0) {
            erode(g_srcImage, g_dstImage, element);
        }
        else {
            dilate(g_srcImage, g_dstImage, element);
        }
    
        //显示效果图
        imshow("【效果图】", g_dstImage);
    }
    
    //腐蚀和膨胀之间切换开关的回调函数
    void on_TrackbarNumChange(int, void *)
    {
        //腐蚀和膨胀之间效果已经切换,
        //回调函数体内需调用一次Process函数,
        //使改变后的效果立即生效并显示出来
        Process();
    }
    
    //腐蚀和膨胀操作内核改变时的回调函数
    void on_ElementSizeChange(int, void *)
    {
        //内核尺寸已改变,回调函数体内需
        //调用一次Process函数,使改变后的
        //效果立即生效并显示出来
        Process();
    }
    

    实验结果:

    用滑动条控制图像腐蚀膨胀以及内核大小

    好了,今天OpenCV学到这里就结束了,喜欢的朋友可以给我点个赞哦!!!

    相关文章

      网友评论

        本文标题:OpenCV图像处理系列八 --- 腐蚀与膨胀

        本文链接:https://www.haomeiwen.com/subject/ghjjgqtx.html