精华内容
下载资源
问答
  • 该程序对二值图像执行形态学操作 示例运行: >> 二进制 该程序对二值图像执行形态学操作为 MATLAB 输入图像... 请注意,如果您输入的是灰度或 RGB 图像(不是二进制), 它将通过阈值转换为二进制图像将图像或其副本...
  • 形态学操作

    2016-06-23 22:41:44
    MATLAB实现二值图像和灰度图像的形态学操作,包括:腐蚀、膨胀、开、闭。参见博客http://blog.csdn.net/u010839382/article/details/51747252
  • 用于图像形态学操作的原始图像以及生成结果,这些图像均为二值图像,简单便于观察算法的生成结果
  • 二、形态学操作 1、回顾 2、开操作 3、闭操作 4、形态学梯度 5、顶帽 6、黑帽 三、OpenCV中的形态学操作 0、核生成的API 1、形态学操作 1.API 2.代码展示 3.执行结果 一、前言 继续填坑。 如果想看其他有关于OpenCV...
  • 图像形态学操作时候,可以通过自定义的结构元素实现结构元素 对输入图像一些对象敏感、另外一些对象不敏感,这样就会让敏 感的对象改变而不敏感的对象保留输出。通过使用两个最基本的 形态学操作 – 膨胀与腐蚀,...
  • 二、形态学操作 1、什么是形态学操作 2、形态学操作的分类 三、OpenCV中的基本形态学操作 0、核生成的API 1、膨胀 1.API 2.代码展示 3.执行结果 2、腐蚀 一、前言 继续填坑。 如果想看其他有关于OpenCV学习方法介绍...
  • 一、前言 继续填坑。...二、形态学操作 1、回顾 首先我们先回顾一下形态学操作。 首先我们学习了形态学的两个基本操作,分别是:膨胀和腐蚀。 (1)膨胀:跟卷积操作类似,假设有图像A和结构元素B,结构
  • 该代码演示了阈值图像或二进制图像上的形态图像处理操作
  • 图像形态学操作

    千次阅读 2019-02-23 09:47:42
    图像形态学操作 微信公众号:幼儿园的学霸 个人的学习笔记,关于OpenCV,关于机器学习, … 问题或建议,请公众号留言; 看到一段话,深有感触 作为软件开发者,我们曾经写过的或者正在写的每一行代码都对我们的...

    图像形态学操作

    微信公众号:幼儿园的学霸
    个人的学习笔记,关于OpenCV,关于机器学习, … 问题或建议,请公众号留言;

    看到一段话,深有感触

    作为软件开发者,我们曾经写过的或者正在写的每一行代码都对我们的渐进式互联世界负有重要的责任。它在很大程度上改变了人类的命运。我们应该为自己所做的事情感到自豪,并且必须将责任进行到底,因为整个世界都在期待我们创造更多的魔力,实现更多的创新。

    目录

    前言结构元素腐蚀与膨胀操作开闭运算形态学梯度顶帽、黑帽morphologyEx函数源码:

    前言

    形态学图像处理的基本运算有:膨胀、腐蚀、开操作和闭操作,击中与击不中变换,TOP-HAT变换,黑帽变换等。
    形态学的应用:消除噪声、边界提取、区域填充、连通分量提取、凸壳、细化、粗化等;分割出独立的图像元素,或者图像中相邻的元素;求取图像中明显的极大值区域和极小值区域;求取图像梯度

    开操作- open   open() = dilate(erode())
        先腐蚀后膨胀,可以去掉小的对象,假设对象是前景色,背景是黑色
    闭操作- close  close() = erode(dilate())
        先膨胀后腐蚀(bin2),可以填充前景上小的洞(fill hole),假设对象是前景色,背景是黑色
    形态学梯度- Morphological Gradient   dst = dilate() - erode()    可以让像素间的像素值形成梯度
        膨胀减去腐蚀,又称为基本梯度(其它还包括-内部梯度(腐蚀减原图还是原图减腐蚀?)、方向梯度(在xy方向进行计算得出的结果))
    顶帽 – top hat
        顶帽 是原图像与开操作之间的差值图像,结果类似于 !开操作 的图像结果
    黑帽 – black hat
        黑帽是闭操作图像与源图像的差值图像,可以检测出原图前景色中的黑点
    

    上列的操作基本都是基于膨胀与腐蚀操作的,这些操作在二值图像(位图)、灰度图像中用的特别多,在彩色图像上效果不是很明显,可以将彩色图像转成二值图像

    结构元素

    在讲各种形态学操作之前,先来看看结构元素:
    膨胀和腐蚀操作的核心内容是结构元素。(后面的开闭运算等重要的也是结构元素的设计,一个合适的结构元素的设计可以带来很好的处理效果)一般来说结构元素是由元素为1或者0的矩阵组成。结构元素为1的区域定义了图像的领域,领域内的像素在进行膨胀和腐蚀等形态学操作时要进行考虑。
    一般来说,二维或者平面结构的结构元素要比处理的图像小得多。结构元素的中心像素,即结构元素的原点,与输入图像中感兴趣的像素值(即要处理的像素值)相对应。三维的结构元素使用0和1来定义x-y平面中结构元素的范围,使用高度值定义第三维。

    OpenCV中获取结构元素的函数为:

    //返回值:返回指定形状和尺寸的结构元素
    cv::Mat kernel = getStructuringElement(int shape,Size ksize,Point anchor);
    //结构元素的定义:形状 (MORPH_RECT(矩形核)
    \MORPH_CROSS(十字交叉形核) \MORPH_ELLIPSE(椭圆形核));结构元素大小;锚点 默认是Point(-1, -1)意思就是中心像素,也可以自己指定
    

    如下所示为3中结构元素示例:
    代码:

        cv::Mat elementRect,elementCross,elementEllipse;
    
        elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
        elementCross = cv::getStructuringElement(cv::MORPH_CROSS,cv::Size(3,3),cv::Point(-1,-1));
        elementEllipse = cv::getStructuringElement(cv::MORPH_ELLIPSE,cv::Size(5,5),cv::Point(-1,-1));
    
        std::cout<<"3X3矩形核:"<<std::endl<<elementRect<<std::endl;
        std::cout<<"3X3十字交叉形核:"<<std::endl<<elementCross<<std::endl;
        std::cout<<"5X5椭圆形核:"<<std::endl<<elementEllipse<<std::endl;
    

    输出结果:

    3X3矩形核:
    [  1,   1,   1;
       1,   1,   1;
       1,   1,   1]
    3X3十字交叉形核:
    [  0,   1,   0;
       1,   1,   1;
       0,   1,   0]
    5X5椭圆形核:
    [  0,   0,   1,   0,   0;
       1,   1,   1,   1,   1;
       1,   1,   1,   1,   1;
       1,   1,   1,   1,   1;
       0,   0,   1,   0,   0]
    

    此外,我们也可以自定义结构元素,如下:
    使用Mat_模板类自定义5×5大小十字形、菱形、方形、x形结构元素:

        //自定义核(结构元素)
        cv::Mat_<uchar> cross(5,5);
        cv::Mat_<uchar> diamond(5,5);
        cv::Mat_<uchar> x(5,5);
        cv::Mat_<uchar> square(5,5);
    
        // Creating the cross-shaped structuring element
        cross <<
              0, 0, 1, 0, 0,
                0, 0, 1, 0, 0,
                1, 1, 1, 1, 1,
                0, 0, 1, 0, 0,
                0, 0, 1, 0, 0;
    
        // Creating the diamond-shaped structuring element
        diamond <<
                0, 0, 1, 0, 0,
                0, 1, 1, 1, 0,
                1, 1, 1, 1, 1,
                0, 1, 1, 1, 0,
                0, 0, 1, 0, 0;
    
        // Creating the x-shaped structuring element
        x <<
          1, 0, 0, 0, 1,
                0, 1, 0, 1, 0,
                0, 0, 1, 0, 0,
                0, 1, 0, 1, 0,
                1, 0, 0, 0, 1;
    
        // Creating the square-shaped structuring element
        square <<
               1, 1, 1, 1, 1,
                1, 1, 1, 1, 1,
                1, 1, 1, 1, 1,
                1, 1, 1, 1, 1,
                1, 1, 1, 1, 1;
    
        int xnr = x.rows;
        int xnl = x.cols;
        for(int j = 0;j<xnr;j++)
        {
            char *data = x.ptr<char>(j);
            for(int i = 0; i<xnl; i++)
            {
                int value = data[i];
                std::cout<<value<<" ";
            }
            std::cout<<std::endl;
        }
    

    腐蚀与膨胀操作

    腐蚀和膨胀是形态学的基本操作,在灰度图像中也是如此,在二值图像中腐蚀和膨胀定义为对图像进行translation以后的“与”和“或”的逻辑操作结果。 也可以这样理解:

    腐蚀的具体操作是:用一个结构元素(一般是3×3的大小)扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为1,则该像素为1,否则为0。

    膨胀的具体操作是:用一个结构元素(一般是3×3的大小)扫描图像中的每一个像素,用结构元素中的每一个像素与其覆盖的像素做“与”操作,如果都为0,则该像素为0,否则为1。

    在灰度图像中,为了保存灰度信息,“与”和“或”操作被对应的替换成了“最大值”和“最小值”操作这样就给出了灰度图像中腐蚀和膨胀的操作定义。
    以矩阵中的操作为例,腐蚀和膨胀操作过程如图1和图2所示(图片来源于网络):
    腐蚀操作:

     

    图1 腐蚀操作

    图1 腐蚀操作

     

     

    膨胀操作:

     

    图2 膨胀操作

    图2 膨胀操作

     

     

    腐蚀的作用是消除物体边界点,使目标缩小,可以消除小于结构元素的噪声点;腐蚀了就意味着图像中物体的边界被侵蚀了,轮廓向内收缩,体积变小了。
    膨胀的作用是将与物体接触的所有背景点合并到物体中,使目标增大,可添补目标中的空洞,通过对图像的胀大,使图像中的物体的轮廓向外发散,体积变大。

    腐蚀就是把当前像素替换成所定义的像素集中的最小像素值。由于输入的二值图像只包含黑色(0)和白色(255)像素,因此如果结构元素覆盖的图像区域中有黑色像素,则锚点所在像素(x,y)将会被替换成黑色0,否则替换成白色255。而物体的边界通常会有黑色像素,所以腐蚀相当于收缩边界。

    膨胀是腐蚀的反运算,它把当前像素(原点所在位置(x,y))替换成所定义的像素集中的最大像素值。由于输入的二值图像只包含黑色(0)和白色(255)像素,因此当结构元素覆盖的图像中有白色(物体),则该结构元素原点所在位置(x,y)的值将会被替换成白色255。

    要注意的是这里的膨胀和腐蚀的概念是针对图像中高亮区域而言的(二值图像中对应像素值255,白色),变大和缩小也是针对图像中高亮的部分。

    所以对图像执行膨胀,对高亮区域是胀大,面积增大,对暗区域相当于是腐蚀,面积缩小

    在OpenCV中进行腐蚀与膨胀操作可以采用函数cv::erode()cv::dilate(),或者采用cv::morphologyEx()函数并输出需要进行的形态学操作类型。如下为cv::dilate()的函数原型,cv::erode()函数参数与此类似:

    void dilate(
        InputArray src,//输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一
        OutputArray dst,//目标图像,需要和源图片有一样的尺寸和类型
        InputArray kernel,//操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核,可以使用getStructuringElement来创建结构元素
        Point anchor=Point(-1,-1),//锚点的位置,其有默认值(-1,-1),表示锚位于中心
        int iterations=1,//迭代使用该函数的次数,默认值为1
        int borderType=BORDER_CONSTANT,//用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT
        const Scalar& borderValue=morphologyDefaultBorderValue() //当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释
    );
    
    //使用dilate函数时,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。
    

    以下为代码示例及腐蚀和膨胀操作结果:
    代码:

    int main()
    {
        //首先读入图像,并二值化
        cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
        cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
    
        cv::imshow("srcImage",srcImage);
    
    
        //获取进行形态学操作的核
        cv::Mat elementRect;
        elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
    
        //膨胀
        cv::Mat dilateImage;
        cv::dilate(srcImage,dilateImage,elementRect);
        //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_DILATE,elementRect);
        cv::imshow("dilateImage",dilateImage);
    
        //腐蚀
        cv::Mat erodeImage;
        cv::erode(srcImage,erodeImage,elementRect);
        //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_ERODE,elementRect);
        cv::imshow("erodeImage",erodeImage);
    
        cv::waitKey(0);
    
        retrun 0;
    }
    

    操作结果:
    原始二值化图像:

     

    图3 二值化图像

    图3 二值化图像

     

     

    腐蚀和膨胀后图像:

     

    图4 腐蚀和膨胀后图像

    图4 腐蚀和膨胀后图像

     

     

    开闭运算

    开运算:先腐蚀后膨胀,作用:用来消除图像中细小对象,在纤细点处分离物体和平滑较大物体的边界而有不明显改变其面积和形状,所有小到不能容纳结构元素的物体都会被移除。
    闭运算:先膨胀后腐蚀,作用:用来填充目标内部的细小孔洞(fill hole),将断开的邻近目标连接,在不明显改变物体面积和形状的情况下平滑其边界,基本上所有小到不能完整容纳结构元素的空隙或间隙,都会被闭运算消除(即连起来)
    OpenCV代码:

    int main()
    {
        //首先读入图像,并二值化
        cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
        cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
    
        cv::imshow("srcImage",srcImage);
    
    
        //获取进行形态学操作的核
        cv::Mat elementRect;
        elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
    
        //开运算
        cv::Mat opendImage;
        cv::morphologyEx(srcImage,opendImage,cv::MORPH_OPEN,elementRect);
        cv::cvtColor(opendImage,opendImage,cv::COLOR_GRAY2BGR);
        cv::putText(opendImage,"opendImage",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
    
        //闭运算
        cv::Mat closedImage;
        cv::morphologyEx(srcImage,closedImage,cv::MORPH_CLOSE,elementRect);
        cv::cvtColor(closedImage,closedImage,cv::COLOR_GRAY2BGR);
        cv::putText(closedImage,"closedImage",cv::Point(0,30),cv::FONT_HERSHEY_SIMPLEX,1.2,cv::Scalar(0,255,0),2);
    
        cv::hconcat(opendImage,closedImage,opendImage);
        cv::imshow("res",opendImage);
    
        cv::resize(srcImage,srcImage,cv::Size(),0.5,0.5);
        cv::resize(opendImage,opendImage,cv::Size(),0.5,0.5);
    
        cv::imwrite("srcImage.jpg",srcImage);
        cv::imwrite("opendImage.jpg",opendImage);
    
        cv::waitKey(0);
        return 0;
    }
    

    图像开闭运算操作后结果:

     

     

    图5 开闭操作后图像

    图5 开闭操作后图像

     

     

    形态学梯度

    通常所说形态学梯度(Morphological Gradient)是膨胀图像与腐蚀图像的之差得到的图像,也是基本梯度。数学表达式如下:

    dst = morph_grad(src,element) = dilate(src,element) - erode(src,element)
    

    梯度用于刻画目标边界或边缘位于图像灰度级剧烈变化的区域,形态学梯度根据膨胀或者腐蚀与原图作差组合来实现增强结构元素领域中像素的强度,突出高亮区域的外围。计算图像的形态学梯度是形态学重要操作,常常将膨胀和腐蚀基础操作组合起来一起使用实现一些复杂的图像形态学梯度。可以计算的梯度常见如下四种:

    • 基本梯度:
      基本梯度是用膨胀后的图像减去腐蚀后的图像得到差值图像,称为梯度图像也是OpenCV中支持的计算形态学梯度的方法,而此方法得到梯度有被称为基本梯度。
    • 内部梯度:
      是用原图像减去腐蚀之后的图像得到差值图像,称为图像的内部梯度。
    • 外部梯度:
      是用图像膨胀之后再减去原来的图像得到的差值图像,称为图像的外部梯度。
    • 方向梯度:
      方向梯度是使用X方向与Y方向的直线作为结构元素之后得到图像梯度,用X方向直线做结构元素分别膨胀与腐蚀之后得到图像求差值之后称为X方向梯度,用Y方向直线做结构元素分别膨胀与腐蚀之后得到图像求差值之后称为Y方向梯度。
    • 特点
      形态学梯度操作的输出图像像素值是在对应结构元素而非局部过渡区域所定义的领域中灰度级强度变化的最大值。
      对二值图像进行形态学操作可以将团块(blob)的边缘突出出来,可以用形态学梯度来保留物体的边缘轮廓。
      代码:
    int main()
    {
    //首先读入图像,并二值化
        cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
        cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
    
        cv::imshow("srcImage",srcImage);
    
        //获取进行形态学操作的核
        cv::Mat elementRect;
        elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
    
        //膨胀
        cv::Mat dilateImage;
        cv::dilate(srcImage,dilateImage,elementRect);
        //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_DILATE,elementRect);
    
        //腐蚀
        cv::Mat erodeImage;
        cv::erode(srcImage,erodeImage,elementRect);
        //cv::morphologyEx(srcImage,dilateImage,cv::MORPH_ERODE,elementRect);
    
    
        //1.计算基本梯度:膨胀后的图像减去腐蚀后的图像
        cv::Mat basicGradient;
        cv::subtract(dilateImage, erodeImage, basicGradient, cv::Mat());
        //cv::morphologyEx(srcImage,basicGradient,cv::MORPH_GRADIENT,elementRect);//两者等效
        cv::imshow("basicGradient", basicGradient);
    
        //2.计算内部梯度:原图像减去腐蚀之后的图像
        cv::Mat internalGradientImg;
        cv::subtract(srcImage, erodeImage, internalGradientImg, cv::Mat());
        cv::imshow("internalGradientImg", internalGradientImg);
    
        //3.计算外部梯度:膨胀后的图像减去原图像
        cv::Mat externalGradientImg;
        cv::subtract(dilateImage, srcImage, externalGradientImg, cv::Mat());
        cv::imshow("externalGradientImg", externalGradientImg);
    
        //4.方向梯度:使用X方向与Y方向的直线作为结构元素---------
        //cv::Mat hse = getStructuringElement(cv::MORPH_RECT, cv::Size(srcImage.cols / 16, 1),cv::Point(-1,-1));
        //cv::Mat vse = getStructuringElement(cv::MORPH_RECT, cv::Size(1, srcImage.rows / 16),cv::Point(-1,-1));
        cv::Mat hse = getStructuringElement(cv::MORPH_RECT, cv::Size(3, 1),cv::Point(-1,-1));
        cv::Mat vse = getStructuringElement(cv::MORPH_RECT, cv::Size(1, 3),cv::Point(-1,-1));
        cv::Mat  xDirectImg, yDirectImg;
        //4.1 X 方向梯度:膨胀与腐蚀之后得到图像求差值
        cv::erode(srcImage, erodeImage, hse);
        cv::dilate(srcImage, dilateImage, hse);
        cv::subtract(dilateImage, erodeImage, xDirectImg, cv::Mat());
        cv::imshow("xDirectImg", xDirectImg);
        //cv::imshow("dilateImage",dilateImage);
        //cv::imshow("erodeImage",erodeImage);
        //4.2 Y 方向梯度:膨胀与腐蚀之后得到图像求差值
        cv::erode(srcImage, erodeImage, vse);
        cv::dilate(srcImage, dilateImage, vse);
        cv::subtract(dilateImage, erodeImage, yDirectImg, cv::Mat());
        cv::imshow("yDirectImg", yDirectImg);
    
        cv::waitKey(0);
        return 0;
    }
    

    为了显示效果,在进行方向梯度操作时,采用的是二值化图像。其实也可用RGB图像进行操作,(PS.知道4种梯度是怎么用腐蚀,膨胀组合的就好)
    操作结果:
    基本梯度:

     

    图6 基本梯度

    图6 基本梯度



    内部、外部梯度:

     

     

    图7 内部、外部梯度

    图7 内部、外部梯度



    方向梯度:

     

     

    图8 方向梯度

    图8 方向梯度

     

    顶帽、黑帽

    当我们在处理一张图片的时候(例如提取眼底彩照的血管等信息),很多情况需要对这张图片进行一定的预处理,而通常我们就会用顶帽或者底帽变换。
    顶帽变换:原图像与开操作对象的差。公式如下:

    dst = tophat(src,element) = src - open(src,element)
    

    因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。因此,顶帽变换用于凸显暗背景上的亮物体。对二值图来说,进行顶帽变换或之后底帽变换看起来就像是加一个阴影,有一种立体的效果。

    顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

    黑帽变换:闭操作与原图像的差值。,数学表达式为:

    dst = blackhat(src,element) = close(src,element) - src;
    

    黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块。黑帽变换可以用于凸显亮背景上的暗物体。二值图效果与顶帽变换相比,就是一个方向相反的阴影。
    代码如下:

    int main()
    {
        //首先读入图像
        cv::Mat srcImage = cv::imread("../pictures/000177.png",cv::IMREAD_GRAYSCALE);
        //cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
    
        cv::imshow("srcImage",srcImage);
    
        //获取进行形态学操作的核
        cv::Mat elementRect;
        elementRect = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3),cv::Point(-1,-1));
    
        //顶帽、黑帽
        cv::Mat topHatImage;
        cv::morphologyEx(srcImage,topHatImage,cv::MORPH_TOPHAT,elementRect,cv::Point(-1,-1));
        cv::imshow("topHatImage",topHatImage);
        再进一步,既然已经可以提取这个图的背景,那么通过背景与二值化图像异或,即可提取前景。
        //cv::Mat bit_xor;
        //cv::threshold(srcImage,srcImage,125,255,cv::THRESH_BINARY);
        //bitwise_xor(srcImage, topHatImage, bit_xor);
        //imshow("异或", bit_xor);
    
    
        cv::Mat blackHatImage;
        cv::morphologyEx(srcImage,blackHatImage,cv::MORPH_TOPHAT,elementRect,cv::Point(-1,-1));
        cv::imshow("blackHatImage",blackHatImage);
    
        cv::waitKey(0);
        return 0;
    }
    

    操作结果:
    原始灰度图像:

     

     

    图9 原始灰度图像

    图9 原始灰度图像



    顶帽、黑帽操作结果:

     

    [

    图10 顶帽、黑帽操作结果

    图10 顶帽、黑帽操作结果

     

     

    morphologyEx函数源码:

    从上面代码中可以看到,OpenCV中对图像的形态学操作都是通过morphologyEx()函数完成的,在OpenCV中其源码如下:

    void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,
                           InputArray kernel, Pointanchor, int iterations,
                           int borderType, constScalar& borderValue )
    {
        //拷贝Mat数据到临时变量
       Mat src = _src.getMat(), temp;
       _dst.create(src.size(), src.type());
       Mat dst = _dst.getMat();
    
        //一个大switch,根据不同的标识符取不同的操作
       switch( op )
        {
       case MORPH_ERODE:
           erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
           break;
       case MORPH_DILATE:
           dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
           break;
       case MORPH_OPEN:
           erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
           dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
           break;
       case CV_MOP_CLOSE:
           dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
           erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
           break;
       case CV_MOP_GRADIENT:
           erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
           dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
           dst -= temp;
           break;
       case CV_MOP_TOPHAT:
           if( src.data != dst.data )
               temp = dst;
           erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
            dilate( temp, temp, kernel, anchor,iterations, borderType, borderValue );
           dst = src - temp;
           break;
       case CV_MOP_BLACKHAT:
           if( src.data != dst.data )
               temp = dst;
           dilate( src, temp, kernel, anchor, iterations, borderType, borderValue);
           erode( temp, temp, kernel, anchor, iterations, borderType, borderValue);
           dst = temp - src;
           break;
       default:
           CV_Error( CV_StsBadArg, "unknown morphological operation" );
        }
    }
    

    看上面的源码可以发现,其实morphologyEx函数其实就是内部一个大switch而已。根据不同的标识符取不同的操作。比如开运算MORPH_OPEN,按我们上文中讲解的数学表达式,就是先腐蚀后膨胀,即依次调用erode和dilate函数,为非常简明干净的代码。



    下面的是我的公众号二维码图片,欢迎关注。

    图注:幼儿园的学霸

    图注:幼儿园的学霸

     

    展开全文
  • labview图像形态学操作

    2018-10-22 21:48:39
    实现二值形态学中的膨胀,腐蚀,开,闭等运算。可以设置结构元素和连通区域,直观感受和对比各类形态学操作的效果。
  • 形态学操作 主要应用于 二值图像 morphologyEx(src, dest, CV_MOP_BLACKHAT, kernel); Mat src:输入图像 Mat dest:输出结果 int OPT:CV_MOP_OPEN/ CV_MOP_CLOSE/ CV_MOP_GRADIENT / CV_MOP_TOPHAT/ CV_MOP_...
  • 形态学操作内容简介 内容简介   本篇博客介绍的形态学图像处理操作。

    内容简介

      本篇博客介绍的形态学图像处理操作。形态学在数学语言里是集合论。在图像处理中,形态学是很重要的分支,但是它又不像傅里叶变换等内容,包含详细的公式推导。通常形态学操作的数学表达都比较抽象,不太容易理解。因此,本篇博客打算以文字与实践相结合的方式进行阐述。
      本篇文章内容中,所有具有处理结果图像的形态学操作均是本人根据教材自己编写的代码,由于代码较多,之后会放在个人github中,如在公布之前有需要的读者,可以留言说明。
      本篇内容主要知识和相关操作图片来自冈萨雷斯第三版的《数字图像处理》,如对细节内容感兴趣的读者可以查阅相关书籍进行参考。本篇博文的内容与原书内容的顺序有所出入,如有不足欢迎留言指正,谢谢。

    1. 预备知识——结构元

      其实结构元(SE,Structure Element)最直接的理解就是卷积操作中的卷积核,或者是空间域滤波中提到的滤波器。虽然形态学操作中结构元的形状可以是任意的,但是由于在图像操作中,为了方便计算,通常要求结构元是矩形的阵列,对于任意形状的结构元,如果不满足矩形的要求,则用0将其填充为矩形即可。
      另外,结构元内部的有效元素不像滤波器那样有权值,通常结构元中只分为两种元素,就是0和1,不会出现其他数值的系数。(当然对有些算法来说也有例外)。结构元对图像进行的操作也和卷积非常类似,就是由结构元的中心依次滑过图像,然后进行设计好的操作即可。接下来就开始介绍图像处理中常见的形态学操作。

    2. 形态学基本操作

      形态学基本操作是形态学最重要的基础,几乎所有复杂的形态学操作都由基本形态学操作组合而成。在本章节中,主要介绍的是二值图像的形态学操作,因此本章中默认图像都是二值图像,且由0表示背景像素,1表示前景像素。
      下面结合集合论的数学表达以及实际使用案例对各种操作进行介绍。

    2.1 腐蚀和膨胀

      1).腐蚀(erode)
    A ⊖ B = { z ∣ ( B ) z ⊆ A } ⇔ A ⊖ B = { z ∣ ( B ) z ∩ A c = ∅ } A\ominus B=\{z|(B)_z\subseteq A\} \Leftrightarrow A\ominus B=\{z|(B)_z\cap A^c=\empty\} AB={z(B)zA}AB={z(B)zAc=}
      其中, A c A^c Ac是图像A的补集,即1-A。上式表达的意思就是,在z的移动下,如果结构元B能够完全包含在图像A中(完全包含意味着结构元B中的有效元素对应于图像A中的位置全都是前景像素。z的移动就是针对于图像A的所有像素范围的移动。
    在这里插入图片描述
      上图中,左边实线围住的9x9方格就是结构元B,红色代表其有效区域,白色代表其无效区域(0填充)。右侧实现代表图像A,浅蓝色部分代表A的前景像素,白色代表背景像素。当结构元滑动到1区域时,所有有效区域对应的像素都是前景像素(紫色区域,红+蓝),因此称此时 B ⊆ A B\subseteq A BA,此时,1号位置在结果图像中应该置1。反之,如果只有部分区域重合,如结构元处于2号位置,那么说明此时的2号点不满足腐蚀条件,应该在结果图像中置0。
      下面是腐蚀操作对实际图像的处理效果。

    • 第一张为原图。
    • 第二张为用11x11的全1模板作结构元进行的腐蚀,可以看到,由于细线的宽度小于11像素,所以细线不可能完全包含SE,因此细线部分被“腐蚀”。
    • 第三张图和第二张一样,只是使用的是opencv自带的cv2.erode函数,可以看到,和自己编写的代码结果优点区别,图像边界的轮廓并不是全黑的,因为我自己写的腐蚀操作是带padding的,也就是会根据SE的尺寸对图像周边填0,而教材中和opencv中的腐蚀函数是不对图像进行填充的,它们直接忽略了会使SE越出图像的像素点,因此那些点的像素值保留不变。
    • 第四张图结构元尺寸为15x15,可以看到,连最粗的线都被腐蚀了。
    • 第五张SE尺寸为45x45,就可以将除了中心方块以外的所有元素腐蚀掉。
      在这里插入图片描述   在这里插入图片描述   在这里插入图片描述  在这里插入图片描述   在这里插入图片描述

      2).膨胀(dilate)
    A ⊕ B = { z ∣ ( B ^ ) z ∩ A ≠ ∅ } ⇔ A ⊕ B = { z ∣ [ ( B ^ ) z ∩ A ] ⊆ A } A\oplus B=\{z|(\hat B)_z\cap A\ne\empty\} \Leftrightarrow A\oplus B=\{z|[(\hat B)_z\cap A]\subseteq A\} AB={z(B^)zA=}AB={z[(B^)zA]A}
      其中, B ^ \hat B B^称为结构元B的反射,指的是,结构元B绕结构元的中心(通常设置在SE矩阵中心)旋转180°后的新结构元。对于对称的结构元,就有 B = B ^ B=\hat B B=B^
      膨胀的集合含义就是,SE和图像的前景像素至少有一个交点,那么当前移动到的像素就置1,否则置0。在膨胀的条件下,之前图示中的1,2号区域均会被置1。(腐蚀时,2号区域是被置0的)。
      下面是膨胀操作对实际图像的处理效果。

    • 第一张图是原图,可以看到原图中的字母多数都有断裂现象,这里需要着重观察右下角的“year"单词;
    • 第二张图是opencv自带的cv2.dilate函数,用SE尺寸3x3的核得到的结果。可以看到,膨胀后,所有字母都变粗了(膨胀了),大部分断裂的区域都连接上了。但是这和教材中的处理结果略有不同,因为opencv提供的膨胀操作,不支持自定义结构元,其结构元是3x3的全一矩阵。而教材中使用的SE是3x3的四个角为无效元素的十字型结构元。
    • 第三张图是自己编写的dilate函数,结构元也使用和教材中一模一样,因此结果是和教材里一样的。注意观察可以发现,图3相比于图2膨胀程度更高,因此,"year"字母中的"ea"的断裂修复效果也比图2更好。
      在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

      3).腐蚀和膨胀的对偶性

    • ( A ⊖ B ) c = A c ⊕ B ^ (A\ominus B)^c=A^c \oplus \hat B (AB)c=AcB^
    • ( A ⊕ B ) c = A c ⊖ B ^ (A\oplus B)^c=A^c \ominus \hat B (AB)c=AcB^

    2.2 开操作与闭操作

      开操作和闭操作都是基于腐蚀和膨胀操作的。
      1).开操作(opening)
    A ∘ B = ( A ⊖ B ) ⊕ B A\circ B=(A\ominus B)\oplus B AB=(AB)B
      开操作就是先用SE腐蚀图像A,再对腐蚀后的结果进行膨胀。第一反应或许会觉得,这操作不是很多余吗?其实细想后发现,其中自有妙处。假设图像中存在很多细小琐碎的颗粒或噪声,那么经过第一步腐蚀后,这些细小的噪声就会被腐蚀而消失,尽管紧接着进行了膨胀操作,但是由于细小颗粒已经完全消失,因此即使膨胀也无法恢复。相反,那些图像中的主体,经过腐蚀后仍然存在,再经过膨胀就可以恢复原样。这么一说,开操作是不是非常有用呢?仔细理解形态学操作的含义非常重要,虽然形态学操作单独拆开看都不是很复杂,但是要在应用中发挥最巧妙的效果,需要使用者对形态学各种操作能得到的效果进行深入理解。否则只会弄巧成拙或完全无法发挥形态学图像处理的威力。
      开操作的性质:

    • A ∘ B A\circ B AB是A的一个子集(子图像);
    • 如果C是D的一个子集,则 C ∘ B C\circ B CB D ∘ B D\circ B DB的一个子集;
    • ( A ∘ B ) ∘ B = A ∘ B (A\circ B)\circ B=A\circ B (AB)B=AB

      以下是开操作的实际操作效果:

    • 图1是原图像,可以看到,在采集指纹的图像中,存在很多不需要的噪声干扰;
    • 图2是使用opencv的cv2.morphologyEx函数进行的图像开操作,其效果和教材中的开操作效果出入较大,猜测原因是opencv提供的形态学函数只支持输入结构元的尺寸(kernel),但是具体该参数如何生成结构元并未说明。所以具体原因需要阅读opencv源码后再进行分析,如果看完源码知道原因了会及时在这里更新。有知道的读者也可以留言说明。因此,这也说明了,不懂得原理没有自己实践实现过算法,只会调用第三方库是很危险的。因为你不理解别人的库中算法是如何实现的,和你实际想要的效果是否一致。因此,对于第三方库,源码阅读是非常必要的。
    • 图3是根据之前编写的腐蚀、膨胀操作代码实现的开操作,可以看到,这个效果和教材中是一模一样的,开操作使得细小的噪声完全消失了,而指纹主体也通过膨胀很好的恢复了。

    在这里插入图片描述 在这里插入图片描述在这里插入图片描述
      2).闭操作(closing)
    A ∙ B = ( A ⊕ B ) ⊖ B A\bullet B=(A\oplus B)\ominus B AB=(AB)B
      闭操作和开操作正好相反,是先膨胀再腐蚀。如果图像中存在细小的孔洞或者缝隙,通过膨胀后就可以填充细小孔洞和缝隙,使其和主体连接起来,而再腐蚀的时候,只会腐蚀图像主体,而不会再恢复缝隙等瑕疵。因此,闭操作很适合拿来修补图像瑕疵的。
      闭操作的性质:

    • A是 A ∙ B A\bullet B AB的一个子集(子图像);
    • 如果C是D的一个子集,则 C ∙ B C\bullet B CB D ∙ B D\bullet B DB的一个子集;
    • ( A ∙ B ) ∙ B = A ∙ B (A\bullet B)\bullet B=A\bullet B (AB)B=AB
        以下是闭操作的实际操作效果:
    • 下图是在开操作得到的图3基础上进一步进行闭操作的结果图,可以看到,相比于之前的图像,闭操作将很多指纹中断裂的区域连接起来,使得指纹的图像更加完整。但是对于较大的裂缝,闭操作是无能为力的,当然你可以增大SE的尺寸,但是这样很可能使得指纹不同纹路也连接起来,反而得不偿失。
      在这里插入图片描述
        3).开操作和闭操作的对偶性
    • ( A ∙ B ) c = ( A c ∘ B ^ ) (A\bullet B)^c=(A^c\circ \hat B) (AB)c=(AcB^)
    • ( A ∘ B ) c = ( A c ∙ B ^ ) (A\circ B)^c=(A^c\bullet \hat B) (AB)c=(AcB^)

    2.3 击中和击不中变换(hit and miss transform)

      这个基础形态学变换就没有之前几个那么直观了。理解起来比较抽象。首先,假设有个结构元 B 1 B_1 B1 B 1 B_1 B1的有效元素共同组成了特定的形状。其次,设W是一个比 B 1 B_1 B1尺寸更大的全1模板,记 B 2 = W − B 1 B_2=W-B_1 B2=WB1,即从模板W中将 B 1 B_1 B1挖去。然后,击中和击不中变换就可以表达为:
    A ⊛ B = ( A ⊖ D ) ∩ [ A c ⊖ ( W − D ) ] = ( A ⊖ B 1 ) ∩ ( A c ⊖ B 2 ) A\circledast B=(A\ominus D)\cap[A^c\ominus(W-D)]=(A\ominus B_1)\cap (A^c\ominus B_2) AB=(AD)[Ac(WD)]=(AB1)(AcB2)
      击中和击不中变换的直观理解
      假设有图像A(如下图1), A = C ∪ D ∪ E A=C\cup D\cup E A=CDE,表示A图像中有C,D,E三个前景子区域。如果我们想要在图像A中寻找是否存在形状D(图1中的45x45的正方形),怎么办?这时候,我们就可以将结构元 B 1 = D B_1=D B1=D,然后选择合适的W求出 B 2 = W − D B_2=W-D B2=WD,对图像进行击中和击不中变换,变换所得到的结果,如果A中存在和D一样的形状,则会留下一个点,该点是D形状所在位置的中心;如果不存在D,那么得到的图像就是空。
      其中,击中和击不中变换中的 A ⊖ B 1 A\ominus B_1 AB1是在图像中找到与 B 1 B_1 B1匹配的结果,即击中结果,如下图2。注意,击中并不代表说A图像中有与 B 1 B_1 B1一模一样的形状,比如左侧长方形,其中包含形状D,但是并不与D完全相同。所以才需要进一步进行击不中变换。而 A c ⊖ B 2 A^c\ominus B_2 AcB2中的 B 2 B_2 B2可以理解为一个框架的中间掏出一个与D形状一模一样的空缺(见下图5,黑色区域为45x45,白色区域为47x47),框架的白色区域通常设置为1个像素宽,图中为了显示而故意夸大了。然后将该框架试图通过图像A,白色区域表示实体,框架的实体和图像中的实体实会相互阻隔而无法通过的。因此可以看到,图像A中只有比形状D小的实体可以穿过 B 2 B_2 B2,这些 B 2 B_2 B2能穿过的地方的集合,就叫做击不中区域,如下图3中的白色区域,就代表框架 B 2 B_2 B2的中心对准白色区域任意点时,框架可以穿过图像(或者说图像A中的实体击不中框架)。
      综上所述,将击中结果和击不中结果相交,最终所得到的结果为下图4。相交的结果直观点可以理解为,既可以被D形状完全击中,又可以被以D形状为空的框架完全穿过的实体,那就肯定只能是和D形状一模一样的实体。图4中可以看到一个白点,这就说明图像A中存在与形状D一模一样的区域,且该区域中心的位置就是图4白点所在位置。
    在这里插入图片描述  在这里插入图片描述  在这里插入图片描述  在这里插入图片描述  在这里插入图片描述
      击中和击不中变换更常用的场景——击中变换
      在某些场合中,我们可能对检测某个集合内由1和0组成的特定模式感兴趣(这种情况下,要求结构元中的1对于图像的前景元素,结构元中的0对应图像的背景元素,结构元中的x则表示对该区域不感兴趣,可以为任意元素),此时,击中与击不中变换就不需要击不中变换(因为前景和背景都属于我们感兴趣的范围),而简单地转换成了特定模式的腐蚀操作。(和原始的腐蚀不同,原始的腐蚀的结构元中,结构元中为0的部分相当于这里的x,它不要求对于的图像像素一定是背景元素,而可以是任意元素)。
      以下图为例,假设此时的结构元为下图左的样子,其中红色为1区域,灰色为0区域,x为不感兴趣区域。那么,此时图像要匹配,则需要满足SE的红色区域对应的图像都为前景像素,灰色区域对应的图像均为背景像素,而x区域对应的图像则不感兴趣,不做要求。如果满足上述条件,则将当前位置的结果图像置1,或者说是结构元SE击中图像/在图像中找到了匹配。下图中间的实线区域代表图像。下图右侧的实线区域中,1号位置表示在图像中找到了SE的匹配;而2号区域,中间正上方和正下方不满足需要为背景图像的要求,因此该位置并未找到匹配。
    在这里插入图片描述

    3. 基本的形态学算法

      有了形态学的基础操作,就可以将其进行组合得到基本的形态学算法,以下一一介绍。同样,下述算法都是基于二值图像的操作。基于灰度图像的形态学算法之后还会介绍。

    3.1 二值图像边缘提取

    β ( A ) = A − ( A ⊖ B ) \beta(A)=A-(A\ominus B) β(A)=A(AB)
      其中, β ( A ) \beta(A) β(A)是提取出来的A图像的边缘图像;B是适当的结构元,如果B尺寸比较大,那么通常提取到的边缘就会比较粗。稍微观察一下就会理解,二值图像的边缘其实就是图像A被腐蚀掉的部分。因此只要腐蚀前和腐蚀后的图像相减,就可以得到边缘。具体效果见下图:
    在这里插入图片描述  在这里插入图片描述

    3.2 孔洞填充

      算法步骤:

    • 首先,确认图像孔洞中的任意一个像素,将其标记为1,注意,如果有多个孔洞,则对于要填充的每个孔洞,都需要一个孔洞内部的任意像素。将其他位置都标记为0,作为迭代初始阵列 X 0 X_0 X0 X 0 X_0 X0的尺寸和标记孔洞的像素位置都要和原图像需保持一致。
    • 选择合适的结构元B,比如下图中的两种都可以。
      在这里插入图片描述
    • 进行如下迭代过程: X k = ( X k − 1 ⊕ B ) ∩ A c , k = 1 , 2 , . . . X_k=(X_{k-1}\oplus B)\cap A^c,k=1,2,... Xk=(Xk1B)Ac,k=1,2,...
    • 直到 X K = X K − 1 X_K=X_{K-1} XK=XK1则停止迭代。此时, d s t = A + X K dst=A+X_K dst=A+XK就可以得到孔洞填充后的图像,其中 X K X_K XK是将孔洞转换为前景像素后的图像。

      为什么迭代公式中要存在" ∩ A c \cap A^c Ac "这一步?这称为“条件膨胀”,有兴趣的读者可以试试去掉这一步,那么最终的孔洞膨胀的结果将会填满整个图像,得到一张全白的图。通过与 A c A^c Ac进行交集的操作,保证我们膨胀的过程中, X k X_k Xk图像始终不会将原图像的前景图像覆盖,也就是用原图像的前景像素限制了膨胀的空间,所以才叫条件膨胀。
      下面是对图像进行孔洞填充的示意图,左图为原图,右图为填充后的结果。中间的动图是填充过程。我只取了上半部分的孔洞进行填充,所以最终并没有把所有孔洞都填满(初始化标记孔洞位置太麻烦了)。另外,可以看到,其中有个孔洞的中间是有个白点的,而在填充时这个白点也没有被填(动图中的小黑点),所以可以从中理解条件膨胀的含义。
    在这里插入图片描述  在这里插入图片描述  在这里插入图片描述

    3.3 连通分量的提取

      其实算法和孔洞填充一样,只是第一步初始化的时候,选择的标记像素点是想要提取的连通分量中的任意一点。第三步迭代的时候,条件膨胀从A的补集替换为A本身。这是很直观的,因为我们现在想要提取的是图像前景,因此需要将膨胀的元素限制在图像前景范围内。
      算法步骤:

    • 首先,确认图像连通分量中的任意一个像素,将其标记为1,注意,如果有多个连通分量,则对于要提取的每个分量,都需要一个连通分量内部的任意像素。将其他位置都标记为0,作为迭代初始阵列 X 0 X_0 X0 X 0 X_0 X0的尺寸和标记连通分量内部像素的位置都要和原图像需保持一致。
    • 选择合适的结构元B,比如下图中的两种都可以。
      在这里插入图片描述
    • 进行如下迭代过程: X k = ( X k − 1 ⊕ B ) ∩ A , k = 1 , 2 , . . . X_k=(X_{k-1}\oplus B)\cap A,k=1,2,... Xk=(Xk1B)A,k=1,2,...
    • 直到 X K = X K − 1 X_K=X_{K-1} XK=XK1则停止迭代。此时, d s t = X K dst=X_K dst=XK就可以得到提取的连通分量。

      如果你只想要提取图中某个连通区域,不希望提取其他区域,并且可以确认该连通区域一定会覆盖图像中某一点,那么这种算法可以很好地完成任务,提取想要的连通部分。
      连通分量的提取实践如下,使用的原图像是孔洞填充的原图像,只提取了其中两个较小的圆环:
    在这里插入图片描述

    3.4 凸壳(convex hull)

      定义:如果集合A内任意两个点的直线段都在A的内部,则称集合A是凸形的。任意集合S的凸壳H是包含S的最小凸集。差集H-S称为S的凸缺
      算法步骤

    • 设原图像为A,首先,确定算法中需要使用的四种结构元,如下图。
      在这里插入图片描述
    • 对于每个结构元 B i , i = 1 , 2 , 3 , 4 B^i,i=1,2,3,4 Bi,i=1,2,3,4,均作如下迭代:
      • X 0 i = A X_0^i=A X0i=A
      • X k i = ( X k − 1 ⊛ B i ) ∪ X k − 1 , k = 1 , 2 , 3... X_k^i=(X_{k-1}\circledast B^i)\cup X_{k-1},\quad k=1,2,3... Xki=(Xk1Bi)Xk1,k=1,2,3...
      • 直到 X K i = X K − 1 i X_K^i=X^i_{K-1} XKi=XK1i,记录 D i = X K i D^i=X_K^i Di=XKi
    • 最终,得到图像A的凸壳为: C ( A ) = ⋃ i = 1 4 D i C(A)=\bigcup_{i=1}^4 D^i C(A)=i=14Di

      上述过程有两点说明,均很重要!
      1). 算法中的 ⊛ \circledast 仅包含击中变换的击中和击不中变换,关于什么是仅包含击中变换,可以参考2.3节最后的解释;
      2). 原《数字图像处理》教材中,迭代公式是 “ X k i = ( X k − 1 ⊛ B i ) ∪ A X_k^i=(X_{k-1}\circledast B^i)\cup A Xki=(Xk1Bi)A” ,经验证后发现是错的,迭代公式应该是 “ X k i = ( X k − 1 ⊛ B i ) ∪ X k − 1 X_k^i=(X_{k-1}\circledast B^i)\cup X_{k-1} Xki=(Xk1Bi)Xk1” 。读者也可自行验证。

      上述算法有一个明显的缺点,就是求出的凸壳可能不是包含原图像的最小凸集。优化最终结果有个常用的办法,就是让求出的凸壳不超过初始点集在水平和垂直方向上的尺寸。当然,也可以针对求出的结果进行进一步的限制生长,但是通常会损失算法效率,需要慎重考虑。
      凸壳算法的具体实践如下图,图1是原始图像,图2动图是整个求解凸壳的过程,动图可按2行3列观看,其中第一行第一列是 B 1 B^1 B1的迭代过程,第二行第一列是 B 2 B^2 B2的迭代过程,第一行第二列是 B 3 B^3 B3的迭代过程,第二行第二列是 B 4 B^4 B4的迭代过程,第一行第三列是每次迭代结束后叠加在一起的 C ( A ) C(A) C(A)
    在这里插入图片描述    在这里插入图片描述

    展开全文
  • 一、图像的形态学操作(Morphological Image Processing)  形态学一般指生物学中研究动物和植物结构的一个分支。用数学形态学(也称图像代数)表示以形态为基础对图像进行分析的数学工具。基本思想是用具有一定...

    一、图像的形态学操作(Morphological Image Processing)

           形态学一般指生物学中研究动物和植物结构的一个分支。用数学形态学(也称图像代数)表示以形态为基础对图像进行分析的数学工具。基本思想是用具有一定形态的结构元素去度量和提取图像中的对应形状以达到对图像分析和识别的目的。形态学图像处理的数学基础和所用语言是集合论。形态学图像处理的应用可以简化图像数据,保持它们基本的形状特性,并除去不相干的结构。

          形态学图像处理的基本运算有:膨胀、腐蚀、开操作和闭操作,击中与击不中变换,TOP-HAT变换,黑帽变换等

          形态学的应用:消除噪声、边界提取、区域填充、连通分量提取、凸壳、细化、粗化等;分割出独立的图像元素,或者图像中相邻的元素;求取图像中明显的极大值区域和极小值区域;求取图像梯度

    在讲各种形态学操作之前,先来看看结构元素:

           膨胀和腐蚀操作的核心内容是结构元素。(后面的开闭运算等重要的也是结构元素的设计,一个合适的结构元素的设计可以带来很好的处理效果)一般来说结构元素是由元素为1或者0的矩阵组成。结构元素为1的区域定义了图像的领域,领域内的像素在进行膨胀和腐蚀等形态学操作时要进行考虑。

           一般来说,二维或者平面结构的结构元素要比处理的图像小得多。结构元素的中心像素,即结构元素的原点,与输入图像中感兴趣的像素值(即要处理的像素值)相对应。三维的结构元素使用01来定义x-y平面中结构元素的范围,使用高度值定义第三维。

    OpenCV里面的API介绍:

    Mat kernel = getStructuringElement(int shape,Size ksize,Point anchor);
    //结构元素的定义:形状 (MORPH_RECT \MORPH_CROSS(交叉形) \MORPH_ELLIPSE);结构元素大小;锚点 默认是Point(-1, -1)意思就是中心像素,也可以自己指定

     结构元素的选取,依据具体项目问题对象而定,远不止这几种大可自己设计。

    1.1 膨胀(Dilate)

             膨胀是腐蚀运算的对偶运算,其作用是在结构元素的约束下将与目标区域相接触的背景合并到该目标物中,使目标边界向外部扩张,物体的面积增大了相应数量的点。

    集合论思想:

    原理步骤:

    OpenCV里面的API介绍:

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

    参数解释:

    • src:原图像。
    • dst:目标图像。
    • element:腐蚀操作的内核。 如果不指定,默认为一个简单的 3x3 矩阵。否则,我们就要明确指定它的形状,可以使用函数getStructuringElement().
    • anchor:默认为Point(-1,-1),内核中心点。省略时为默认值。
    • iterations:腐蚀次数。省略时为默认值1。
    • borderType:推断边缘类型,具体参见borderInterpolate函数。默认为BORDER_DEFAULT,省略时为默认值。
    • borderValue:边缘值,具体可参见createMorphoogyFilter函数。可省略。

    1.2 腐蚀(Erode)

             腐蚀是一种在结构元素约束下消除目标图形的部分边界点,使其边界向内部收缩的的算法,具有收缩目标区域的作用。

    集合论思想:

                     

    原理步骤:

    OpenCV里面的API介绍:

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

    参数和膨胀一模一样;

    1.3 开运算(Open)

    开运算:先腐蚀后膨胀;

    作用:用来消除图像中细小对象,在纤细点处分离物体和平滑较大物体的边界而有不明显改变其面积和形状。

    集合论思想:

             

    开运算的几何解释:

                          

    Opencv里面的API介绍:——见最后总结

    1.4 闭运算(Close)

           闭运算:先膨胀后腐蚀;

             作用:用来填充目标内部的细小孔洞(fill hole),将断开的邻近目标连接,在不明显改变物体面积和形状的情况下平滑其边界。

    集合论思想:

              

    闭运算的几何解释:

                       

    Opencv里面的API介绍:——见最后总结

    1.5 形态学梯度、顶帽、黑帽

    (1)形态学梯度(Morphological gradient)

             形态学梯度操作能描述图像亮度变化的剧烈程度;当我们想要突出高亮区域的外围时,则可以选用形态学梯度来突出边缘,可以保留物体的边缘轮廓。

    常见的几种梯度:

    基本梯度,膨胀减去腐蚀:

    Opencv里面的API介绍:——见最后总结

     

    (2)礼/顶帽(Top hat)

             顶帽是原图与原图的开运算的差值图像。开运算放大了裂缝或者局部低亮度的区域,所以,从原图中减去开运算后的图,得到的结果突出了比原图轮廓周围的区域更明亮的区域,这个操作与选择的核的大小有关。TopHat运算一般用来分离比邻近点亮一些的斑块,可以使用这个运算提取背景。

    Opencv里面的API介绍:——见最后总结

    (3) 黑帽(Black hat)

            黑帽是闭运算结果与原图的差值图像。黑帽运算的结果突出了比原图轮廓周围区域更暗的区域,所以黑帽运算用来分离比邻近点暗一些的斑块。

    Opencv里面的API介绍:——见最后总结

    1.6 总结:

    //形态学操作(开、闭、形态学梯度、顶帽、黑帽)
    	morphologyEx(src, dst, MORPH_BLACKHAT, kernel);
    	//第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
    	//第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
    	//第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
    	//MORPH_OPEN – 开运算(Opening operation):先腐蚀后膨胀,去掉小对象
    	//MORPH_CLOSE – 闭运算(Closing operation):先膨胀后腐蚀,可以填充一些小的空洞(fill hole)
    	//MORPH_GRADIENT - 形态学梯度(Morphological gradient):膨胀后的图减去腐蚀后的图(设置恰当的参数可以得到目标的大致边缘)
    	//MORPH_TOPHAT - “顶帽”(“Top hat”):原图像与开操作之间的差值图像(可以用来观察开运算除去了哪些小目标)
    	//MORPH_BLACKHAT - “黑帽”(“Black hat“):闭操作图像与源图像的差值图像(可以观察闭运算的效果)
    	//第四个参数:结构元素即形态学运算的内核
    	//第五个参数,Point类型的anchor,锚的位置,其有默认值( - 1, - 1),表示锚位于中心。
    	//第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
    	//第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
    	//第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档

    示例程序:

    //形态学的操作的学习
    #include "stdafx.h"
    #include<opencv2/opencv.hpp>
    #include<iostream>
    
    using namespace cv;
    
    char output[] = "Morphlogy_image";
    Mat dst, src;
    void Morphlogy_Operators(int, void*);
    int element_size = 3;
    int max_size = 21;
    int main(int argc, char**argv)
    {
    	Mat src = imread("C:/Users/59235/Desktop/工作/异常1/temp1.bmp");
    	if (!src.data)
    	{
    		printf("could not load image...\n");
    		return -1;
    	}
    	namedWindow("input image", CV_WINDOW_AUTOSIZE);
    	imshow("input image", src);
    
    	namedWindow(output, CV_WINDOW_AUTOSIZE);
    	//轨迹条或滑动条(Trackbar)的控件工具
    	createTrackbar("elementSIZE:", output, &element_size, max_size, Morphlogy_Operators);
    	//参数一、trackbarname:滑动空间的名称;参数二、winname:滑动空间用于依附的图像窗口的名称;数三、value:初始化阈值;参数四、count:滑动控件的刻度范围;参数五、TrackbarCallback是回调函数
    	Morphlogy_Operators(0, 0);
    
    	waitKey(0);
    	return 0;
    }
    //各种形态学操作
    void Morphlogy_Operators(int, void*)
    {
    	int s = element_size + 1;
    	Mat kernel = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
    	//结构元素的定义:形状 (MORPH_RECT \MORPH_CROSS(交叉形) \MORPH_ELLIPSE);结构元素大小;锚点 默认是Point(-1, -1)意思就是中心像素
    
    
    	//膨胀
    	//dilate(src,dst,kernel);
    
    	//腐蚀
    	//erode(src, dst, kernel);
    
    	//形态学操作(开、闭、形态学梯度、顶帽、黑帽)
    	morphologyEx(src, dst, MORPH_BLACKHAT, kernel);
    	//第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
    	//第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
    	//第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
    	//MORPH_OPEN – 开运算(Opening operation):先腐蚀后膨胀,去掉小对象
    	//MORPH_CLOSE – 闭运算(Closing operation):先膨胀后腐蚀,可以填充一些小的空洞(fill hole)
    	//MORPH_GRADIENT - 形态学梯度(Morphological gradient):膨胀后的图减去腐蚀后的图(设置恰当的参数可以得到目标的大致边缘)
    	//MORPH_TOPHAT - “顶帽”(“Top hat”):原图像与开操作之间的差值图像(可以用来观察开运算除去了哪些小目标)
    	//MORPH_BLACKHAT - “黑帽”(“Black hat“):闭操作图像与源图像的差值图像(可以观察闭运算的效果)
    	//第四个参数:结构元素即形态学运算的内核
    	//第五个参数,Point类型的anchor,锚的位置,其有默认值( - 1, - 1),表示锚位于中心。
    	//第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
    	//第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
    	//第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档
    
    	imshow(output, dst);
    
    	return;
    }

           总之,形态学操作的灵活运用,可以起到消除噪声,分割出独立的图像元素以及在图像中连接起相邻的区域。

    展开全文
  • Opencv图像处理——数学形态学操作

    千次阅读 2021-02-22 13:54:22
    文章目录前言二、1.2、总结参考来源 前言 本文讲述图像处理的形态学操作 # 一、 二、 1. 2、 总结 参考来源 如有疑问,请留言! 如有错误,敬请指正!

    前言

    本文讲述图像处理的形态学操作


    一、数学形态学图像处理基本概念

    在这里插入图片描述

    1.1、数学形态学简介

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

    1.2、数学形态学组成

           数学形态学是由一组形态学的代数运算子组成的,它的基本运算有4个: 膨胀(或扩张)、腐蚀(或侵蚀)、开启和闭合,它们在二值图像和灰度图像中各有特点。基于这些基本运算还可推导和组合成各种数学形态学实用算法,用它们可以进行图像形状和结构的分析及处理,包括图像分割、特征抽取、边缘检测、 图像滤波、图像增强和恢复等。数学形态学方法利用一个称作结构元素的“探针”收集图像的信息,当探针在图像中不断移动时, 便可考察图像各个部分之间的相互关系,从而了解图像的结构特征。数学形态学基于探测的思想,与人的FOA(Focus Of Attention)的视觉特点有类似之处。作为探针的结构元素,可直接携带知识(形态、大小、甚至加入灰度和色度信息)来探测、研究图像的结构特点。

    1.3、数学形态学应用

           数学形态学的基本思想及方法适用于与图像处理有关的各个方面,如基于击中/击不中变换的目标识别,基于流域概念的图像分割,基于腐蚀和开运算的骨架抽取及图像编码压缩,基于测地距离的图像重建,基于形态学滤波器的颗粒分析等。
    数学形态学是一门建立在严格数学理论基础上的学科,其基本思想和方法对图像处理的理论和技术产生了重大影响。事实上,数学形态学已经构成一种新的图像处理方法和理论,成为计算机数字图像处理及分形理论的一个重要研究领域,并且已经应用在多门学科的数字图像分析和处理的过程中。这门学科在计算机文字识别, 计算机显微图像分析(如定量金相分析,颗粒分析), 医学图像处理(例如细胞检测、心脏的运动过程研究、脊椎骨癌图像自动数量描述),图像编码压缩,工业检测(如食品检验和印刷电路自动检测),材料科学, 机器人视觉,汽车运动情况监测等方面都取得了非常成功的应用。另外,数学形态学在指纹检测、经济地理、合成音乐和断层X光照像等领域也有良好的应用前景。形态学方法已成为图像应用领域工程技术人员的必备工具。

    二、二值图像

    2.1、含义

           二值图像是指每个像素点均为黑色或者白色的图像。二值图像一般用来描述字符图像,其优点是占用空间少;缺点是当表示人物,风景的图像时,二值图像只能展示其边缘信息,图像内部的纹理特征表现不明显。这时候要使用纹理特征更为丰富的灰度图像。

    2.2、二值图像样例

    在这里插入图片描述

    2.3、图像二值化处理逻辑

           图像的二值化处理是将图像上的点的灰度值为0或255,也就是将整个图像呈现出明显的黑白效果。即将256个亮度等级的灰度图像通过适当的阈值选取而获得仍然可以反映图像整体和局部特征的二值化图像。在数字图像处理中,要进行二值图像的处理与分析,首先要把灰度图像二值化,得到二值化图像,这样子有利于在对图像做进一步处理时,图像的集合性质只与像素值为0或255的点的位置有关,不再涉及像素的多级值,使处理变得简单,而且数据的处理和压缩量小。为了得到理想的二值图像,一般采用封闭、连通的边界定义不交叠的区域。所有灰度大于或等于阈值的像素被判定为属于特定物体,其灰度值为255表示,否则这些像素点被排除在物体区域以外,灰度值为0,表示背景或者例外的物体区域。


    2.4、opencv二值化处理函数

    2.4.1、threshold()函数

    简介:对每个数组元素应用一个固定级别的阈值,手动指定一个阈值,以此阈值来进行二值化处理
    函数定义:

    double threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type );
    /*
    src输入数组(多通道,8位或32位浮点),即图像。
    dst输出数组的大小和类型与src的通道数相同。
    thresh参数阈值。
    maxval用于#THRESH_BINARY和#THRESH_BINARY_INV thresholding的最大值类型。
    type如果使用大津法或三角形法,则返回计算出的阈值。
    */
    

    2.4.2、adaptiveThreshold()函数

    简介:对数组应用自适应阈值,通过设定最后两个参数来调整效果
    函数定义:

    void adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, 
    		int adaptiveMethod, int thresholdType, int blockSize, double C );
    /*
    src源8位单通道图像。
    dst目标映像与src大小和类型相同的param 
    maxValue指定给满足条件的像素的非零值
    adaptiveMethod自适应阈值算法的使用#BORDER_REPLICATE |#BORDER_ISOLATED用于处理边界。
    thresholdType Thresholding type必须是#THRESH_BINARY或#THRESH_BINARY_INV,
    blockSize用于计算阈值的像素邻域的大小像素:3、5、7等等。
    参数C常数从平均值或加权平均值中减去。正常情况下为正,但也可能为零或负。
    */
    

    三、Opencv图像基本二值运算

    3.1、腐蚀操作:erode()函数

    简介:使用指定的结构元素来腐蚀源图像,该结构元素决定取最小值的像素邻域的形状
    定义:

    void erode( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1,
                int borderType = BORDER_CONSTANT,  const Scalar& borderValue = morphologyDefaultBorderValue() );
    /*
    src:为输入图像对象通道数可以是任意的,但深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F。
    dst:输出与src大小和类型相同的图像。
    kernel:用于扩展的内核结构元素;如果elemenat=Mat(),则为3 x 3矩形使用结构元素。
    anchor:可以使用#getStructuringElement创建内核anchor元素中锚点的位置;默认值(-1,-1)表示锚点位于元素中心。
    iterations:迭代次数应用腐蚀。
    borderType:像素外推方法,不支持边框环绕。
    borderValue:如果是常量border,则为border值
    */
    

    3.2、膨胀操作:dilate()函数

    简介:使用指定的结构元素来扩展源图像,该结构元素确定取最大值的像素邻域的形状
    定义:

    void dilate( InputArray src, OutputArray dst, InputArray kernel, Point anchor = Point(-1,-1), int iterations = 1,
                 int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
    /*
    src:为输入图像对象通道数可以是任意的,但深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F。
    dst:输出与src大小和类型相同的图像。
    kernel:用于扩展的内核结构元素;如果elemenat=Mat(),则为3 x 3矩形使用结构元素。
    anchor:可以使用#getStructuringElement创建内核anchor元素中锚点的位置;默认值(-1,-1)表示锚点位于元素中心。
    iterations 迭代应用扩展的次数。
    borderType:像素外推方法,不支持边框环绕。
    borderValue:如果是常量border,则为border值
    */
    

    3.3、morphologyEx()函数

    简介:morphologyEx可以使用侵蚀和扩张来执行高级形态转换基本操作。任何操作都可以在原地完成。在多通道图像的情况下,每个通道独立处理。
    定义:

    void morphologyEx( InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), 
    				   int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
    /*
    src:源图像。通道的数量可以是任意的。深度应该是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F。
    dst:目标映像的大小和类型与源映像相同。
    Type:形态学运算的类型,请参见#MorphTypes
    kernel:内核结构元素。可以使用#getStructuringElement创建它。
    anchor :锚定与内核的锚定位置。负值表示锚点位于内核中心。
    iterations :迭代应用腐蚀和膨胀的次数。
    borderType:像素外推方法,请参见#BorderTypes。#不支持边框自动换行。
    borderValue:如果是常量Border,则为Border值。默认值具有特殊的意思。
    迭代次数是应用侵蚀或膨胀操作的次数。
    例如,具有两次迭代的打开操作(#MORPH_OPEN)等价于apply
    */
    

    3.3.1、开操作:MORPH_OPEN

    先腐蚀后膨胀的操作。
    作用:消除细小物体,在纤细处分离物体和平滑较大物体边界。

    3.3.2、闭操作:MORPH_CLOSE

    先膨胀后腐蚀的操作。
    作用:填充物体内细小空洞,连接邻近物体和平滑边界。

    3.3.3、梯度运算:MORPH_GRADIENT

    对二值图像进行这一操作可以将团块的边缘突出来。
    作用:形态学梯度来保留物体的边缘轮廓

    3.3.4、顶帽操作:MORPH_TOPHAT

    从原图中减去开运算后的图,得到的效果图突出了比原型轮廓周围的区域更明亮的区域
    作用:分离比邻近点亮一些的斑块

    3.3.5、黑帽操作:MORPH_BLACKHAT

    突出了比原图轮廓周围的区域更暗的区域
    作用:分离比邻近点暗一些的斑块

    3.4、 getStructuringElement()函数

    简介:返回用于形态学操作的指定大小和形状的结构元素
    作用:我们一般利用这个函数来配合前面各个函数定义中的参数kernel来使用,构造一个符合的结构元素
    定义:

    Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
    /*
    shape:元素可以是#MorphShapes之一的形状
    ksize:结构元素的大小。
    anchor:锚定元素内的锚定位置。默认值\f$(-1,-1)\f$表示锚在中间。只有十字形元素的形状取决于锚点位置。在其他情况下,锚定只会调节结果的大小操作转移。
    */
    

    3.5、createTrackbar()函数

    简介:函数createTrackbar创建具有指定名称的轨迹栏(滑块或范围控件)和范围,将变量值指定为与轨迹栏同步的位置,并指定要在轨迹栏位置更改时调用的回调函数onChange。创建的轨迹栏是显示在指定的窗口中。
    定义:

    int createTrackbar(const String& trackbarname, const String& winname, int* value, 
    					int count,TrackbarCallback onChange = 0,void* userdata = 0);
    /*
    trackbarname:创建的轨迹栏的名称。
    winname:将用作所创建轨迹栏父级的窗口的名称。
    value:指向整数变量的可选指针,该整数变量的值反映滑块。创建时,滑块位置由该变量定义。
    count:滑块的最大位置。最小位置总是0。
    onChange:指向每次滑块改变位置时要调用的函数的指针。这个函数的原型应该是void Foo(int,void\*);其中第一个参数是trackbar位置,第二个参数是用户数据(见下一个参数)。如果回调是空指针,不调用回调,但只更新值。
    userdata:按原样传递给回调的用户数据。它可以用来处理轨迹栏不使用全局变量的事件。
    */ 
    

    3.6、形态学操作类型枚举:

    enum MorphTypes{
        MORPH_ERODE    = 0, //!< see #erode
        MORPH_DILATE   = 1, //!< see #dilate
        MORPH_OPEN     = 2, //!< an opening operation
        MORPH_CLOSE    = 3, //!< a closing operation
        MORPH_GRADIENT = 4, //!< a morphological gradient
        MORPH_TOPHAT   = 5, //!< "top hat"
        MORPH_BLACKHAT = 6, //!< "black hat"
        MORPH_HITMISS  = 7  //!< "hit or miss"
    };
    

    四、代码演示:

    4.1、腐蚀与膨胀

    代码块

    //腐蚀与膨胀
    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace std;
    using namespace cv;
    
    Mat src, dst;
    int element_size = 2;
    int max_size = 20;
    void CallBack_Demo(int, void*) {
    	int s = element_size * 2 + 1;
    	Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
    	dilate(src, dst, structureElement, Point(-1, -1));//膨胀
    	
    	//erode(src, dst, structureElement);//腐蚀
    	imshow("output_image", dst);
    }
    int main() {
    	src = imread("D:\\Myfile\\素材照片\\opencv素材照片\\4.jpg");
    	if (!src.data) {
    		cout << "could not load image..." << endl;
    		return 0;
    	}
    	namedWindow("input_image", WINDOW_AUTOSIZE);
    	imshow("input_image", src);
    	namedWindow("output_image", WINDOW_AUTOSIZE);
    	createTrackbar("Element Size :", "output_image", &element_size, max_size, CallBack_Demo);
    	CallBack_Demo(0, 0);
    	imshow("output_image", dst);
    
    	waitKey(0);
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述

    4.2、开、闭、梯度、顶帽、黑帽等操作

    代码块:

    //形态学操作
    #include <opencv2/opencv.hpp>
    #include <iostream>
    #include "facedetectcnn.h"
    
    using namespace std;
    using namespace cv;
    int main() {
    	Mat src, dst;
    	src = imread("D:\\Myfile\\素材照片\\opencv素材照片\\11.jpg");
    	if (!src.data) {
    		cout << "could not load image..." << endl;
    		return 0;
    	}
    	namedWindow("input_image", WINDOW_AUTOSIZE);
    	imshow("input_image", src);
    	
    	//Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));//结构元素
    	//morphologyEx(src, dst, MORPH_OPEN, kernel);//开操作,先腐蚀后膨胀
    	//morphologyEx(src, dst, MORPH_CLOSE, kernel);//闭操作,先膨胀后腐蚀
    
    	//梯度:膨胀减去腐蚀,基本梯度(内部梯度,方向梯度)
    	//morphologyEx(src, dst, MORPH_GRADIENT, kernel);
    
    	//顶帽 原图像和开操作图像
    	//morphologyEx(src, dst, MORPH_TOPHAT, kernel);
    
    	//黑帽 原图像和闭操作图像
    	//morphologyEx(src, dst, MORPH_BLACKHAT, kernel);
    
    	//提取水平和垂直线
    	cvtColor(src, dst, COLOR_BGR2GRAY);
    	Mat Bin_dst;
    	adaptiveThreshold(dst, Bin_dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);//二值化图像
    	Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
    	Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.rows / 16), Point(-1, -1));
    	Mat kernels = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    	//Mat temp;
    	//erode(Bin_dst, temp, kernels);
    	//dilate(temp, dst, kernels);
    	morphologyEx(Bin_dst, dst, MORPH_OPEN, hline);
    	bitwise_not(dst, dst);
    	blur(dst, dst, Size(3, 3), Point(-1, -1));
    	namedWindow("output_image", WINDOW_AUTOSIZE);
    	imshow("output_image", dst);
    	waitKey(0);
    	return 0;
    }
    

    运行结果:
    在这里插入图片描述

    总结

    本文讲述数学形态学操作:腐蚀、膨胀、开、闭、梯度、顶帽、黑帽

    参考来源

    如有疑问,请留言!
    如有错误,敬请指正!

    展开全文
  • OpenCV形态学操作

    千次阅读 2018-11-09 04:46:21
    OpenCV形态学操作
  • 数字图像处理中的形态学操作

    千次阅读 2019-04-21 19:00:07
    概要:首先,我们要在大的方向上对图像处理中的形态学操作有所认识,就是对二值图像和灰度图像的处理,其实这两者很类似,只要能把对二值图像的处理完全理解了,对灰度图像也就是维数扩展而已...
  • 眼底图像形态学操作MATLAB代码
  • 针对医学超声图像低对比度和强噪声给医疗诊断和图像处理所带来的困难,通过基于多尺度形态学操作的方法实现图像增强和噪声抑制的目的。该方法将传统的图像增强概念延伸到数学形态学多尺度空间中,利用多尺度形态学操作...
  • [MATLAB] 常用形态学操作函数

    千次阅读 2018-04-14 20:41:15
    腐蚀和膨胀是数学形态学上的名词,如果用于图像处理上则就称为图像二值形态学形态学主要是为了获取物体的拓扑和结构信息,通过物体和结构元素相互作用的某些运算,得到物体更本质的形态。当形态学运用到图像处理中...
  • opencv形态学操作概念,开操作、闭操作、梯度操作、顶帽和黑帽原理和接口使用详解,此工程是各接口Demo与注释详解,欢迎下载学习使用
  • 形态学操作,例如腐蚀,膨胀,开运算,闭运算等 形态学操作是根据图像形状进行的简单操作 一般情况下对二值化图像进行的操作。 需要输入两个参数: 一个是原始图像, 一个被称为结构化元素或核,它是用来决定操作...
  • 一、开操作 Open 算法说明: 对图像进行 先腐蚀后膨胀 的操作 dst = open(src, element) = dilate(erode(dst, element)) 应用: 假设图片对象前景色为白色,背景色为黑色,通过开操作,可以去掉小范围的对象...
  • 数学形态学操作

    2018-04-11 08:35:48
    包括,腐蚀膨胀,开闭运算等数学形态学操作的matlab代码
  • 本章的主角是morphologyEx函数,它利用基本的腐蚀和膨胀技术,来执行更加高级的形态学变换。 opencv中提供的API: void morphologyEx(src, dst, int op, InputArray kernel, Point ancho, int iterations=1, int ...
  • 在整个项目中,形态学操作被用于处理显微血液涂片图像并将红细胞与其他血细胞区分开来。 要求 MATLAB > R2019b Matlab 实时编辑器 数据集 从下载数据集 ALL_IDB1 执行 首先,RGB 着色转换为 HSV(色调、饱和度、值)...
  • 目录 1. 图像腐蚀 2. 图像膨胀 3. 图像开运算 4. 图像闭运算 5. 图像梯度运算 ...6. 图像礼帽(顶帽)操作 ...所以这样的操作会将图像(一般是二值化图像)白色局域变瘦小,黑色局域变多 命令:dst = ...
  • 这个应用程序支持大多数形态学操作,包括那些可以采用多个 STRELS 的(如 BWHITMISS)。 现在支持所有STRELS,包括任意STRELS。 您可以轻松地看到更改STREL的大小或形状,或尝试不同的形态学操作的效果。 您还可以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 54,235
精华内容 21,694
关键字:

形态学操作