精华内容
下载资源
问答
  • 测定了9 个常见绿化树种――白蜡、大叶黄杨、垂柳、国槐、毛白杨、玉兰、紫叶李、元宝枫和银杏的单位叶面积滞尘量及其粒径组成,并观测了各树种叶面微形态结构.结果表明,国贸桥和北京植物园9 个树种 PM、PM>10、PM2.5...
  • 参与旅游的古村落形态变化的博弈解释,王巍,,旅游业蓬勃发展的今天,古村落游吸引越来越多的旅游者。受到旅游冲击的古村落在旅游发展不同阶段的形态变化是不可避免的。随着时
  • 对希腊新约及类似文本形态的分析和解释 对所有NT书籍的第一本书都已经制作完成,可以生成lexemes.yaml 。 我现在正在审查遗漏的项目,词性和引理方面的分歧,对派生和变形形式进行分析,并生成主要部分。 目前...
  • 煤层气地层中的陷落柱不利于...本区的精细解释结果为井位部署,尤其是水平井位部署提供了技术保障。在全区的200口开发井中,由于避开了陷落柱,使低效井比例由前期的28%降低到10%,有效提高了水平井质量及单井日产气量。
  • 上篇文章中,我们重点了解了腐蚀和膨胀这两种最基本的形态学操作,而运用这两个基本操作,我们可以实现更高级的形态学变换。 所以,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行...


    本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

    文章链接: http://blog.csdn.net/poem_qianmo/article/details/23184547

    作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442

    知乎:http://www.zhihu.com/people/mao-xing-yun

    邮箱: happylifemxy@163.com

    写作当前博文时配套使用的OpenCV版本: 2.4.8

     

    上篇文章中,我们重点了解了腐蚀和膨胀这两种最基本的形态学操作,而运用这两个基本操作,我们可以实现更高级的形态学变换。

    所以,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”、“黑帽”等等。

     先上几张示例程序的截图吧:


    有没有很熟悉这张图?没错,这就是最近热映的电影Captain America~

    下面这张图的效果就有些凶残了:

    OK,截图先看到这里。在正文之前先来唠唠和主题相关的事情。


    第一件事,OpenCV最新版本更新到了2.4.9。


    在写这篇博文的两天之前(4月25日上午),OpenCV官网页面显示最新版本还是2.4.8,但是通过浅墨细心地发现,文档页面的标题已经悄悄而低调地改成了2.4.9.所以我们当时应该可以去断定,OpenCV2.4.9应该马上就要和我们见面了。

     

    果然,OpenCV2.4.9就在两天后(4月27日),正式在OpenCV官方网站上上线了。现在转到OpenCV的官方主页,赫然发现最新版本已然显示为2.4.9:

    这是OpenCV的官方主页传送门:http://opencv.org/

    大家可以自己前去看看以及下载最新版本的OpenCV。如果不出意外的话呢,下次文章我们就将紧跟时代,用上最新版本的OpenCV2.4.9进行讲解和程序的书写,所以,大家在看这篇文章之后呢,可以去下载当前最新的2.4.9版本并装上配置好。




    第二件事,是浅墨想跟大家做一个关于OpenCV系列文章的书写内容和风格的思想汇报。


    是这样的,浅墨发现最近几期写出来的文章有些偏离自己开始开这个专栏的最初的愿望——原理和概念部分占的比重有些大,有些弱化OpenCV实际的使用。

    写这些博文的初心是教大家如何使用OpenCV来写代码,原理部分我想很多朋友应该多少都懂,就算某些同学对某些概念有些模糊,大家也完全可以带着关键词句去google或者百度。

    浅墨的想法是,以后的专栏文章原理部分尽量从简,“深入”的源码剖析部分也是从简,重点突出“浅出”部分,让大家快速上手OpenCV函数的使用,这样浅墨的工作量也会小很多,更新也会更勤。

    PS:浅墨其实每次在写图像处理原理部分的时候都特纠结,因为浅墨其实感兴趣的和大家一样,也是如何写代码,而不是那些多多少少让人提不起兴趣来的图像处理公式和概念。这往往就照成了博文更新的拖延症。

    所以呢,在浅墨以后写的OpenCV文章中,原理和深入部分我们就点到为止,文章的拳头内容是“浅出”部分,重点教大家如何快速上手OpenCV API。我想这也是大家一直期待和想要看到的浅墨出品的文章的样子吧。:)

    OK,大概就是这些。我们开始今天的正题。






    一、理论与概念讲解——从现象到本质



    首先呢,要知道形态学的高级形态,往往都是建立在腐蚀和膨胀这两个基本操作之上的。而关于腐蚀和膨胀,概念和细节以及相关代码可以看浅墨之前写的这篇文章:


    【OpenCV入门教程之十】 形态学图像处理(一):膨胀与腐蚀


    对膨胀和腐蚀心中有数了,接下来的高级形态学操作,应该就不难理解。

    另外,为了下面对比和演示以及理解的方便,浅墨自己制作了一张毛笔字图,这里先上原图:

     

     OK,我们开始讲解。





    1.1 开运算(Opening Operation)

     



    开运算(Opening Operation),其实就是先腐蚀后膨胀的过程。其数学表达式如下:




    开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。效果图是这样的:

    实际效果图:

     




    1.2 闭运算(Closing Operation)


    先膨胀后腐蚀的过程称为闭运算(Closing Operation),其数学表达式如下:

     

    闭运算能够排除小型黑洞(黑色区域)。效果图如下所示:

     

    实际效果图:





    1.3 形态学梯度(MorphologicalGradient)


    形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,数学表达式如下:

     

    对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓,如下所示:

     

    实际素材效果图:





    1.4 顶帽(Top Hat)


    顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差,数学表达式如下:

    因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。

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

    如下所示:

    素材效果图:

     

     



    1.5 黑帽(Black Hat)



    黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:


    黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。

    所以,黑帽运算用来分离比邻近点暗一些的斑块。非常完美的轮廓效果图:

     

    实际素材效果图:





     

    二、深入——OpenCV源码分析溯源




    本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来一起看一下morphologyEx函数的源代码。

    //-----------------------------------【erode()函数中文注释版源代码】----------------------------  
    //   说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码  
    //   OpenCV源代码版本:2.4.8  
    //   源码路径:…\opencv\sources\modules\imgproc\src\morph.cpp  
    //   源文件中如下代码的起始行数:1369行  
    //   中文注释by浅墨  
    //--------------------------------------------------------------------------------------------------------   
    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函数,为非常简明干净的代码。

     




     


    三、浅出——API函数快速上手

     



    3.1 morphologyEx函数详解



    上面我们已经讲到,morphologyEx函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来了解它的参数意义和使用方法。

    C++: void morphologyEx(
    InputArray src,
    OutputArray dst,
    int op,
    InputArraykernel,
    Pointanchor=Point(-1,-1),
    intiterations=1,
    intborderType=BORDER_CONSTANT,
    constScalar& borderValue=morphologyDefaultBorderValue() );

    • 第一个参数,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)
      • MORPH_GRADIENT -形态学梯度(Morphological gradient)
      • MORPH_TOPHAT - “顶帽”(“Top hat”)
      • MORPH_BLACKHAT - “黑帽”(“Black hat“)

    另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。

     

    • 第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:

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

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

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

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

    getStructuringElement函数相关的调用示例代码如下:

    int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
     
    //获取自定义核
    Mat element =getStructuringElement(MORPH_RECT,
           Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
           Point(g_nStructElementSize, g_nStructElementSize ));


    调用这样之后,我们便可以在接下来调用erode、dilate或morphologyEx函数时,kernel参数填保存getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。

    • 第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
    • 第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
    • 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
    • 第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

    其中的这些操作都可以进行就地(in-place)操作。且对于多通道图像,每一个通道都是单独进行操作。

     OK,讲解完毕,下面就是使用的范例。



    高能预警!高能预警!高能预警!

    一大波示例代码正在逼近。

    为了方便大家需要的时候随时取用。下面我们依次列举出开运算,闭运算,形态学梯度,顶帽,黑帽,腐蚀,膨胀的效果实现简化版完整代码。

    其实说白了,这些代码基本上内容一致,其实就是改一下morphologyEx里面的第三个标识符参数而已。核都是选的MORPH_RECT,矩形元素结构。

    另外,通过看源码我们发现,最基本的腐蚀和膨胀操作也可以用morphologyEx函数来实现,他们由morphologyEx函数源码中switch的前两个case来实现(虽然在case体内就是简单地各自调用了一下erode和dilation函数,但还是有写出来的必要)。所以在这里,我们也用morphologyEx再重新来实现一遍他们。

    按着顺序来列出吧,就直接列详细注释好的代码和运行结果了。

     




    3.2 开运算示例程序


    OpenCV中调用morphologyEx函数进行开运算操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】开运算"); 
           namedWindow("【效果图】开运算"); 
           //显示原始图 
           imshow("【原始图】开运算", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_OPEN, element);
           //显示效果图 
           imshow("【效果图】开运算", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:







    3.3 闭运算示例程序



    OpenCV中调用morphologyEx函数进行闭运算操作的示例程序如下:


    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】闭运算"); 
           namedWindow("【效果图】闭运算"); 
           //显示原始图 
           imshow("【原始图】闭运算", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_CLOSE, element);
           //显示效果图 
           imshow("【效果图】闭运算", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:





    3.4 形态学梯度示例程序



    OpenCV中调用morphologyEx函数进行形态学梯度操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】形态学梯度"); 
           namedWindow("【效果图】形态学梯度"); 
           //显示原始图 
           imshow("【原始图】形态学梯度", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_GRADIENT, element);
           //显示效果图 
           imshow("【效果图】形态学梯度", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:






    3.5 顶帽运算(Top Hat)示例程序



    OpenCV中调用morphologyEx函数进行顶帽运算操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】顶帽运算"); 
           namedWindow("【效果图】顶帽运算"); 
           //显示原始图 
           imshow("【原始图】顶帽运算", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_TOPHAT, element);
           //显示效果图 
           imshow("【效果图】顶帽运算", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:




    3.6 黑帽运算(BlackHat)示例程序


    OpenCV中调用morphologyEx函数进行黑帽运算操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】黑帽运算"); 
           namedWindow("【效果图】黑帽运算"); 
           //显示原始图 
           imshow("【原始图】黑帽运算", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_BLACKHAT, element);
           //显示效果图 
           imshow("【效果图】黑帽运算", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:



     



    3.7 腐蚀(morphologyEx调用版)示例程序



    OpenCV中调用morphologyEx函数进行腐蚀操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】腐蚀"); 
           namedWindow("【效果图】腐蚀"); 
           //显示原始图 
           imshow("【原始图】腐蚀", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_ERODE, element);
           //显示效果图 
           imshow("【效果图】腐蚀", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:



     



    3.8 膨胀(morphologyEx调用版)示例程序



    OpenCV中调用morphologyEx函数进行膨胀操作的示例程序如下:

    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace cv;
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //载入原始图  
           Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
           //创建窗口  
           namedWindow("【原始图】膨胀"); 
           namedWindow("【效果图】膨胀"); 
           //显示原始图 
           imshow("【原始图】膨胀", image); 
           //定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); 
           //进行形态学操作
           morphologyEx(image,image, MORPH_DILATE, element);
           //显示效果图 
           imshow("【效果图】膨胀", image); 
     
           waitKey(0); 
     
           return 0; 
    }

    运行效果图:



     








    四、综合示例——在实战中熟稔

     



    依然是每篇文章都会配给大家的一个详细注释的博文配套示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

    这个示例程序中,一共有四个显示图像的窗口。

    原始图一个,开/闭运算为一个,腐蚀/膨胀为一个,顶帽/黑帽运算为一个。

    分别使用滚动条,来控制得到的形态学效果。且迭代值为10的时候,为中间。

    另外,还可以通过键盘按键1,2,3以及空格,来调节成不同的元素结构(矩形、椭圆、十字形)。说明页面如下:





    废话不多说,上代码吧:


    //-----------------------------------【程序说明】----------------------------------------------
    //		程序名称::《【OpenCV入门教程之十一】形态学图像处理(一):膨胀与腐蚀  》 博文配套源码 
    //		开发所用IDE版本:Visual Studio 2010
    //   		开发所用OpenCV版本:	2.4.8
    //		2014年4月25日 Create by 浅墨
    //----------------------------------------------------------------------------------------------
    
    //-----------------------------------【头文件包含部分】---------------------------------------
    //		描述:包含程序所依赖的头文件
    //---------------------------------------------------------------------------------------------- 
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    
    //-----------------------------------【命名空间声明部分】--------------------------------------
    //		描述:包含程序所使用的命名空间
    //----------------------------------------------------------------------------------------------- 
    using namespace std;
    using namespace cv;
    
    
    //-----------------------------------【全局变量声明部分】--------------------------------------
    //		描述:全局变量声明
    //-----------------------------------------------------------------------------------------------
    Mat g_srcImage, g_dstImage;//原始图和效果图
    int g_nElementShape = MORPH_RECT;//元素结构的形状
    
    //变量接收的TrackBar位置参数
    int g_nMaxIterationNum = 10;
    int g_nOpenCloseNum = 0;
    int g_nErodeDilateNum = 0;
    int g_nTopBlackHatNum = 0;
    
    
    
    //-----------------------------------【全局函数声明部分】--------------------------------------
    //		描述:全局函数声明
    //-----------------------------------------------------------------------------------------------
    static void on_OpenClose(int, void*);//回调函数
    static void on_ErodeDilate(int, void*);//回调函数
    static void on_TopBlackHat(int, void*);//回调函数
    static void ShowHelpText();//帮助文字显示
    
    
    //-----------------------------------【main( )函数】--------------------------------------------
    //		描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
    	//改变console字体颜色
    	system("color 2F");  
    
    	ShowHelpText();
    
    	//载入原图
    	g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的素材图
    	if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }
    
    	//显示原始图
    	namedWindow("【原始图】");
    	imshow("【原始图】", g_srcImage);
    
    	//创建三个窗口
    	namedWindow("【开运算/闭运算】",1);
    	namedWindow("【腐蚀/膨胀】",1);
    	namedWindow("【顶帽/黑帽】",1);
    
    	//参数赋值
    	g_nOpenCloseNum=9;
    	g_nErodeDilateNum=9;
    	g_nTopBlackHatNum=2;
    
    	//分别为三个窗口创建滚动条
    	createTrackbar("迭代值", "【开运算/闭运算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
    	createTrackbar("迭代值", "【腐蚀/膨胀】",&g_nErodeDilateNum,g_nMaxIterationNum*2+1,on_ErodeDilate);
    	createTrackbar("迭代值", "【顶帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);
    
    	//轮询获取按键信息
    	while(1)
    	{
    		int c;
    
    		//执行回调函数
    		on_OpenClose(g_nOpenCloseNum, 0);
    		on_ErodeDilate(g_nErodeDilateNum, 0);
    		on_TopBlackHat(g_nTopBlackHatNum,0);
    
    		//获取按键
    		c = waitKey(0);
    
    		//按下键盘按键Q或者ESC,程序退出
    		if( (char)c == 'q'||(char)c == 27 )
    			break;
    		//按下键盘按键1,使用椭圆(Elliptic)结构元素结构元素MORPH_ELLIPSE
    		if( (char)c == 49 )//键盘按键1的ASII码为49
    			g_nElementShape = MORPH_ELLIPSE;
    		//按下键盘按键2,使用矩形(Rectangle)结构元素MORPH_RECT
    		else if( (char)c == 50 )//键盘按键2的ASII码为50
    			g_nElementShape = MORPH_RECT;
    		//按下键盘按键3,使用十字形(Cross-shaped)结构元素MORPH_CROSS
    		else if( (char)c == 51 )//键盘按键3的ASII码为51
    			g_nElementShape = MORPH_CROSS;
    		//按下键盘按键space,在矩形、椭圆、十字形结构元素中循环
    		else if( (char)c == ' ' )
    			g_nElementShape = (g_nElementShape + 1) % 3;
    	}
    
    	return 0;
    }
    
    
    //-----------------------------------【on_OpenClose( )函数】----------------------------------
    //		描述:【开运算/闭运算】窗口的回调函数
    //-----------------------------------------------------------------------------------------------
    static void on_OpenClose(int, void*)
    {
    	//偏移量的定义
    	int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
    	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    	//自定义核
    	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
    	//进行操作
    	if( offset < 0 )
    		morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
    	else
    		morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
    	//显示图像
    	imshow("【开运算/闭运算】",g_dstImage);
    }
    
    
    //-----------------------------------【on_ErodeDilate( )函数】----------------------------------
    //		描述:【腐蚀/膨胀】窗口的回调函数
    //-----------------------------------------------------------------------------------------------
    static void on_ErodeDilate(int, void*)
    {
    	//偏移量的定义
    	int offset = g_nErodeDilateNum - g_nMaxIterationNum;	//偏移量
    	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    	//自定义核
    	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
    	//进行操作
    	if( offset < 0 )
    		erode(g_srcImage, g_dstImage, element);
    	else
    		dilate(g_srcImage, g_dstImage, element);
    	//显示图像
    	imshow("【腐蚀/膨胀】",g_dstImage);
    }
    
    
    //-----------------------------------【on_TopBlackHat( )函数】--------------------------------
    //		描述:【顶帽运算/黑帽运算】窗口的回调函数
    //----------------------------------------------------------------------------------------------
    static void on_TopBlackHat(int, void*)
    {
    	//偏移量的定义
    	int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
    	int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
    	//自定义核
    	Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
    	//进行操作
    	if( offset < 0 )
    		morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
    	else
    		morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
    	//显示图像
    	imshow("【顶帽/黑帽】",g_dstImage);
    }
    
    //-----------------------------------【ShowHelpText( )函数】----------------------------------
    //		描述:输出一些帮助信息
    //----------------------------------------------------------------------------------------------
    static void ShowHelpText()
    {
    //输出一些帮助信息
    	printf("\n\n\n\t请调整滚动条观察图像效果~\n\n");
    	printf( "\n\n\t按键操作说明: \n\n"
    		"\t\t键盘按键【ESC】或者【Q】- 退出程序\n"
    		"\t\t键盘按键【1】- 使用椭圆(Elliptic)结构元素\n"
    		"\t\t键盘按键【2】- 使用矩形(Rectangle )结构元素\n"
    		"\t\t键盘按键【3】- 使用十字型(Cross-shaped)结构元素\n"
    		"\t\t键盘按键【空格SPACE】- 在矩形、椭圆、十字形结构元素中循环\n"
    		"\n\n\t\t\t\t\t\t\t\t by浅墨"
    		);
    }

    放出一些效果图:

    首先是原图:


    非常帅气的Captain America有木有!

    腐蚀效果图:


    膨胀效果图:



    开运算效果图:



    闭运算效果图:



    顶帽运算效果图:


    黑帽运算效果图:



    好的,就放出这些效果图吧,具体更多的运行效果大家就自己下载示例程序回去玩~

     

    本篇文章的配套源代码请点击这里下载:


    【浅墨OpenCV入门教程之十一】配套源代码下载

     


    OK,今天的内容大概就是这些,我们下篇文章见:)




    展开全文
  • OpenCV与EmguCV中的形态学滤波

    千次阅读 2016-06-28 14:41:32
    形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。数学形态学是由一组形态学的代数运...

    http://blog.csdn.net/u013162930/article/details/51775789

    形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。

    数学形态学是由一组形态学的代数运算子组成的,它的基本运算有4个: 膨胀、腐蚀、开启和闭合, 它们在二值图像灰度图像中各有特点。

    简单来讲,形态学操作就是基于形状的一系列图像处理操作。

    OpenCV为进行图像的形态学变换提供了快捷、方便的函数。基本的形态学操作有二种,他们是:膨胀与腐蚀(Dilation与Erosion)。

    膨胀与腐蚀能实现多种多样的功能:消除噪声、分割(isolate)出独立的图像元素以及在图像中连接(join)相邻的元素。形态学也常被用于寻找图像中的明显的极大值区域或极小值区域以及求出图像的梯度。


    ①膨胀 dilate

    OpenCV中的函数原型如下:

    void dilate(  InputArray src,  OutputArray dst,  InputArray kernel,  Point anchor=Point(-1,-1),  int iterations=1,  int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() );  
    • 第一个参数,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(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

    我们也可以使用函数getStructuringElement配合这第三个参数的使用从而得到自定义的核。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。

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

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

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

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

    eg。

    Mat srcImage=imread("M:/图像处理实验/dilate/src.bmp");  
    Mat dstImage;  
    //自定义核
    Mat structuringE = getStructuringElement(MORPH_RECT,Size(5,5),Point(2,2));
    dilate( srcImage, dstImage, structuringE, Point(2,2), 1, BORDER_DEFAULT); 
    imwrite("M:/图像处理实验/dilate/dst.bmp", dstImage);
    EmguCV中的函数原型:
    Public Shared Sub Dilate(src As Emgu.CV.IInputArray, dst As Emgu.CV.IOutputArray, element As Emgu.CV.IInputArray, anchor As System.Drawing.Point, iterations As Integer, borderType As Emgu.CV.CvEnum.BorderType, borderValue As Emgu.CV.Structure.MCvScalar)

    参数含义与OpenCV中相同

    第三个参数可使用如下函数来获取自定义核:

    Public Shared Function GetStructuringElement(shape As Emgu.CV.CvEnum.ElementShape, ksize As System.Drawing.Size, anchor As System.Drawing.Point) As Emgu.CV.Mat
    eg。

    Dim img As Image(Of Gray, Byte) = New Image(Of Gray, Byte)("M:\图像处理实验\dilate\src.bmp")
    Dim StructingElement As Emgu.CV.Mat = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, New Size(5, 5), New Point(2, 2))
    CvInvoke.Dilate(img, img, StructingElement, New Point(2, 2), 1, Emgu.CV.CvEnum.BorderType.Default, New Emgu.CV.Structure.MCvScalar(0))
    img.Save("M:\图像处理实验\dilate\src-result.bmp")
    此函数的参数没有默认值,使用起来比较繁琐。也可以如下调用。
    Emgu.CV.Image(Of TColor, TDepth).Dilate(iterations As Integer) As Emgu.CV.Image(Of TColor, TDepth)

    膨胀是指将图像(或图像中的一部分区域,A)与核B进行卷积。

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

    膨胀是求局部最大值的操作。

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

    由此可见,膨胀和腐蚀操作是对图像中的高亮区域进行的,也就是图像的白色区域。

    腐蚀 erode
    OpenCV中的函数原型如下:

    void erode( InputArray src,  OutputArray dst,  InputArray kernel,  Point anchor=Point(-1,-1),  int iterations=1,  int borderType=BORDER_CONSTANT,  const Scalar& borderValue=morphologyDefaultBorderValue() );
    • 第一个参数,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(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
    eg。
    Mat srcImage=imread("M:/图像处理实验/erode/src.bmp");  
    Mat dstImage;  
    Mat structuringE = getStructuringElement(MORPH_RECT,Size(5,5),Point(2,2));
    erode( srcImage, dstImage, structuringE, Point(2,2), 1, BORDER_DEFAULT); 
    imwrite("M:/图像处理实验/erode/dst.bmp", dstImage);


    EmguCV中的函数原型如下:
    Public Shared Sub Erode(src As Emgu.CV.IInputArray, dst As Emgu.CV.IOutputArray, element As Emgu.CV.IInputArray, anchor As System.Drawing.Point, iterations As Integer, borderType As Emgu.CV.CvEnum.BorderType, borderValue As Emgu.CV.Structure.MCvScalar)
    函数参数含义与OpenCV相同
    eg。
    Dim img As Image(Of Gray, Byte) = New Image(Of Gray, Byte)("M:\图像处理实验\erode\src.bmp")        
    Dim StructingElement As Emgu.CV.Mat = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, New Size(5, 5), New Point(2, 2))
    CvInvoke.Erode(img, img, StructingElement, New Point(2, 2), 1, Emgu.CV.CvEnum.BorderType.Default, New Emgu.CV.Structure.MCvScalar(0))
    img.Save("M:\图像处理实验\erode\src-result.bmp")


    腐蚀和膨胀是一对相反的操作,所以腐蚀就是求局部最小值的操作。

    我们一般都会把腐蚀和膨胀对应起来学习理解。



    ③ 开运算

    开运算,其实就是先腐蚀后膨胀的过程。

    开运算可以用来消除小物体,在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。



    ④闭运算

    闭运算,就是先膨胀后腐蚀的过程。

    闭运算可以用来排除小型黑洞。

    eg。(VB.NET、EmguCV)

    Dim img As Image(Of Gray, Byte) = New Image(Of Gray, Byte)("M:\图像处理实验\二维码\二维码.bmp")
    Dim StructingElement As Emgu.CV.Mat = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, New Size(5, 5), New Point(2, 2))
    '先膨胀        
    CvInvoke.Dilate(img, img, StructingElement, New Point(2, 2), 1, Emgu.CV.CvEnum.BorderType.Default, New Emgu.CV.Structure.MCvScalar(0))
    '再腐蚀
    CvInvoke.Erode(img, img, StructingElement, New Point(2, 2), 1, Emgu.CV.CvEnum.BorderType.Default, New Emgu.CV.Structure.MCvScalar(0))
    img.Save("M:\图像处理实验\result\result.bmp")
    原图像与进行了闭运算后的输出结果比较:

    本文中的OpenCv与EmguCV均用的是3.0以上的版本。

    参考文献:

    Bradski & Kaebler ·《学习OpenCV(中文版)》· 清华大学出版社 · 2009

    冈萨雷斯 · 《数字图像处理》 · 电子工业出版社 · 2011

    展开全文
  • 形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。 数学形态学(Mathematical ...


    本系列文章由@浅墨_毛星云 出品,转载请注明出处。  

    文章链接: http://blog.csdn.net/poem_qianmo/article/details/23710721

    作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 

    写作当前博文时配套使用的OpenCV版本: 2.4.8



    本篇文章中,我们一起探究了图像处理中,最基本的形态学运算——膨胀与腐蚀。浅墨在文章开头友情提醒,用人物照片做腐蚀和膨胀的素材图片得到的效果会比较惊悚,毁三观的,不建议尝试。。。。。。。。。。


    OK,开始吧,依然是先放一张截图:





    一、理论与概念讲解——从现象到本质



    1.1 形态学概述

     

    形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。

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

     

    简单来讲,形态学操作就是基于形状的一系列图像处理操作。OpenCV为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有二种,他们是:膨胀与腐蚀(Dilation与Erosion)。

    膨胀与腐蚀能实现多种多样的功能,主要如下:

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

     


    我们在这里给出下文会用到的,用于对比膨胀与腐蚀运算的“浅墨”字样毛笔字原图:

     

    在进行腐蚀和膨胀的讲解之前,首先需要注意,腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。

     





    1.2 膨胀

     

    其实,膨胀就是求局部最大值的操作。

    按数学方面来说,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。

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

     

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



    膨胀的数学表达式:


    膨胀效果图(毛笔字):

     

    照片膨胀效果图:


     



    1.3 腐蚀


    再来看一下腐蚀,大家应该知道,膨胀和腐蚀是一对好基友,是相反的一对操作,所以腐蚀就是求局部最小值的操作。

    我们一般都会把腐蚀和膨胀对应起来理解和学习。下文就可以看到,两者的函数原型也是基本上一样的。

     

    原理图:

     

    腐蚀的数学表达式:

     

    腐蚀效果图(毛笔字):


    照片腐蚀效果图:

     

     浅墨表示这张狗狗超可爱:D

     

     



    二、深入——OpenCV源码分析溯源

     


    直接上源码吧,在…\opencv\sources\modules\imgproc\src\ morph.cpp路径中 的第1353行开始就为erode(腐蚀)函数的源码,1361行为dilate(膨胀)函数的源码。

    //-----------------------------------【erode()函数中文注释版源代码】---------------------------- 
    //    说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码 
    //    OpenCV源代码版本:2.4.8 
    //    源码路径:…\opencv\sources\modules\imgproc\src\ morph.cpp 
    //    源文件中如下代码的起始行数:1353行 
    //    中文注释by浅墨 
    //--------------------------------------------------------------------------------------------------------  
    void cv::erode( InputArray src, OutputArraydst, InputArray kernel,
                    Point anchor, int iterations,
                    int borderType, constScalar& borderValue )
    {
    //调用morphOp函数,并设定标识符为MORPH_ERODE
       morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType,borderValue );
    }

    //-----------------------------------【dilate()函数中文注释版源代码】---------------------------- 
    //    说明:以下代码为来自于计算机开源视觉库OpenCV的官方源代码 
    //    OpenCV源代码版本:2.4.8 
    //    源码路径:…\opencv\sources\modules\imgproc\src\ morph.cpp 
    //    源文件中如下代码的起始行数:1361行 
    //    中文注释by浅墨 
    //-------------------------------------------------------------------------------------------------------- 
    void cv::dilate( InputArray src,OutputArray dst, InputArray kernel,
                     Point anchor, int iterations,
                     int borderType, constScalar& borderValue )
    {
    //调用morphOp函数,并设定标识符为MORPH_DILATE
       morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType,borderValue );
    }


    可以发现erode和dilate这两个函数内部就是调用了一下morphOp,只是他们调用morphOp时,第一个参数标识符不同,一个为MORPH_ERODE(腐蚀),一个为MORPH_DILATE(膨胀)。

    morphOp函数的源码在…\opencv\sources\modules\imgproc\src\morph.cpp中的第1286行,有兴趣的朋友们可以研究研究,这里就不费时费力花篇幅展开分析了。

     

     

     

    三、浅出——API函数快速上手

     



    3.1  形态学膨胀——dilate函数

     


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

    函数原型:

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

    参数详解:

    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
    • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    • 第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。

    我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。

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

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

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

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

    getStructuringElement函数相关的调用示例代码如下:

     int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
     
    //获取自定义核
    Mat element = getStructuringElement(MORPH_RECT,
    	Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
    	Point( g_nStructElementSize, g_nStructElementSize ));


    调用这样之后,我们便可以在接下来调用erode或dilate函数时,第三个参数填保存了getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。


    • 第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
    • 第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
    • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
    • 第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
    •  

    使用erode函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。

    调用范例:

           	//载入原图 
           	Mat image = imread("1.jpg");
    	//获取自定义核
           	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
           	Mat out;
           	//进行膨胀操作
           	dilate(image, out, element);

    用上面核心代码架起来的完整程序代码:

     

    //-----------------------------------【头文件包含部分】---------------------------------------
    //     描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //     描述:包含程序所使用的命名空间
    //----------------------------------------------------------------------------------------------- 
    using namespace std;
    using namespace cv;
     
    //-----------------------------------【main( )函数】--------------------------------------------
    //     描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main(  )
    {
     
           //载入原图 
           Mat image = imread("1.jpg");
     
           //创建窗口 
           namedWindow("【原图】膨胀操作");
           namedWindow("【效果图】膨胀操作");
     
           //显示原图
           imshow("【原图】膨胀操作", image);
     
    	//获取自定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
           Mat out;
    	//进行膨胀操作
           dilate(image,out, element);
     
           //显示效果图
           imshow("【效果图】膨胀操作", out);
     
           waitKey(0);
     
           return 0;
    }

     运行截图:



     

     

     

    3.2 形态学腐蚀——erode函数



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

     

    看一下函数原型:

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

    参数详解:

    • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
    • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
    • 第三个参数,InputArray类型的kernel,腐蚀操作的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。(具体看上文中浅出部分dilate函数的第三个参数讲解部分)
    • 第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心,我们一般不用管它。
    • 第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
    • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
    • 第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。

    同样的,使用erode函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。

    调用范例:

           	//载入原图 
           	Mat image = imread("1.jpg");
    	//获取自定义核
           	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
           	Mat out;
           	//进行腐蚀操作
           	erode(image,out, element);

    用上面核心代码架起来的完整程序代码:

     

    //-----------------------------------【头文件包含部分】---------------------------------------
    //     描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/core/core.hpp>
    #include<opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //     描述:包含程序所使用的命名空间
    //----------------------------------------------------------------------------------------------- 
    using namespace std;
    using namespace cv;
     
    //-----------------------------------【main( )函数】--------------------------------------------
    //     描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main(  )
    {
           //载入原图 
           Matimage = imread("1.jpg");
     
            //创建窗口 
           namedWindow("【原图】腐蚀操作");
           namedWindow("【效果图】腐蚀操作");
     
           //显示原图
           imshow("【原图】腐蚀操作", image);
     
            
    //获取自定义核
           Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
           Mat out;
     
    //进行腐蚀操作
           erode(image,out, element);
     
           //显示效果图
           imshow("【效果图】腐蚀操作", out);
     
           waitKey(0);
     
           return 0;
    }


    运行结果:

     

     

     

     

    四、综合示例——在实战中熟稔

     

     

    依然是每篇文章都会配给大家的一个详细注释的博文配套示例程序,把这篇文章中介绍的知识点以代码为载体,展现给大家。

    这个示例程序中的效果图窗口有两个滚动条,顾名思义,第一个滚动条“腐蚀/膨胀”用于在腐蚀/膨胀之间进行切换;第二个滚动条”内核尺寸”用于调节形态学操作时的内核尺寸,以得到效果不同的图像,有一定的可玩性。废话不多说,上代码吧:

     
    //-----------------------------------【程序说明】----------------------------------------------
    //            程序名称::《【OpenCV入门教程之十】形态学图像处理(一):膨胀与腐蚀  》 博文配套源码
    //            开发所用IDE版本:Visual Studio 2010
    //          开发所用OpenCV版本: 2.4.8
    //            2014年4月14日 Create by 浅墨
    //            浅墨的微博:@浅墨_毛星云
    //------------------------------------------------------------------------------------------------
     
    //-----------------------------------【头文件包含部分】---------------------------------------
    //            描述:包含程序所依赖的头文件
    //----------------------------------------------------------------------------------------------
    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include <iostream>
     
    //-----------------------------------【命名空间声明部分】---------------------------------------
    //            描述:包含程序所使用的命名空间
    //-----------------------------------------------------------------------------------------------
    using namespace std;
    using namespace cv;
     
     
    //-----------------------------------【全局变量声明部分】--------------------------------------
    //            描述:全局变量声明
    //-----------------------------------------------------------------------------------------------
    Mat g_srcImage, g_dstImage;//原始图和效果图
    int g_nTrackbarNumer = 0;//0表示腐蚀erode, 1表示膨胀dilate
    int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
     
     
    //-----------------------------------【全局函数声明部分】--------------------------------------
    //            描述:全局函数声明
    //-----------------------------------------------------------------------------------------------
    void Process();//膨胀和腐蚀的处理函数
    void on_TrackbarNumChange(int, void *);//回调函数
    void on_ElementSizeChange(int, void *);//回调函数
     
     
    //-----------------------------------【main( )函数】--------------------------------------------
    //            描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-----------------------------------------------------------------------------------------------
    int main( )
    {
           //改变console字体颜色
           system("color5E"); 
     
           //载入原图
           g_srcImage= imread("1.jpg");
           if(!g_srcImage.data ) { printf("Oh,no,读取srcImage错误~!\n"); return false; }
          
           //显示原始图
           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);
     
           //输出一些帮助信息
           cout<<endl<<"\t嗯。运行成功,请调整滚动条观察图像效果~\n\n"
                  <<"\t按下“q”键时,程序退出~!\n"
                  <<"\n\n\t\t\t\tby浅墨";
     
           //轮询获取按键信息,若下q键,程序退出
           while(char(waitKey(1))!= 'q') {}
     
           return 0;
    }
     
    //-----------------------------【Process( )函数】------------------------------------
    //            描述:进行自定义的腐蚀和膨胀操作
    //-----------------------------------------------------------------------------------------
    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);
    }
     
     
    //-----------------------------【on_TrackbarNumChange( )函数】------------------------------------
    //            描述:腐蚀和膨胀之间切换开关的回调函数
    //-----------------------------------------------------------------------------------------------------
    void on_TrackbarNumChange(int, void *)
    {
           //腐蚀和膨胀之间效果已经切换,回调函数体内需调用一次Process函数,使改变后的效果立即生效并显示出来
           Process();
    }
     
     
    //-----------------------------【on_ElementSizeChange( )函数】-------------------------------------
    //            描述:腐蚀和膨胀操作内核改变时的回调函数
    //-----------------------------------------------------------------------------------------------------
    void on_ElementSizeChange(int, void *)
    {
           //内核尺寸已改变,回调函数体内需调用一次Process函数,使改变后的效果立即生效并显示出来
           Process();
    }


     

    放出一些效果图吧。原始图:

     


    膨胀效果图:

     






    腐蚀效果图:







    腐蚀和膨胀得到的图,都特有喜感,但千变万变,还是原图好看:



    OK,就放出这些吧,具体更多的运行效果大家就自己下载示例程序回去玩吧。


    本篇文章到这里就基本结束了,最后放出文章配套示例程序的打包下载地址。

     

    本篇文章的配套源代码请点击这里下载:


    【浅墨OpenCV入门教程之十】配套源代码下载

     


    OK,今天的内容大概就是这些,我们下篇文章见:)




    展开全文
  • 形态方程可以在材料系统和其他自然科学的显微图像的观察和分析中找到许多应用,形态方程还可以重新解释代数几何的一些重要基本概念,例如:1)要构造图像数学语言并构造微结构的图像数学模型(IMM); 2)构造复杂...
  • OpenCV形态学操作

    千次阅读 2018-11-09 04:46:21
    OpenCV形态学操作

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                   

    OpenCV形态学操作

     

    一、图像腐蚀 膨胀 细化的基本原理

    1.图像细化的基本原理
    图像形态学处理的概念
    数字图像处理中的形态学处理是指将数字形态学作为工具从图像中提取对于表达和描绘区域形状有用处的图像分量,比如边界、骨架以及凸壳,还包括用于预处理或后处理的形态学过滤、细化和修剪等。图像形态学处理中我们感兴趣的主要是二值图像。
    在二值图像中,所有黑色像素的集合是图像完整的形态学描述,二值图像的各个分量是Z2的元素。假定二值图像A和形态学处理的结构元素B是定义在笛卡儿网格上的集合,网格中值为1的点是集合的元素,当结构元素的原点移到点(x,y)时,记为Sxy,为简单起见,结构元素为3x3,且全都为1,在这种限制下,决定输出结果的是逻辑运算。

    二值图像的逻辑运算
    逻辑运算尽管本质上很简单,但对于实现以形态学为基础额图像处理算法是一种有力的补充手段。在图像处理中用到的主要逻辑运算是:与、或和非(求补),它们可以互相组合形成其他逻辑运算。

    膨胀和腐蚀
    膨胀和腐蚀这两种操作是形态学处理的基础,许多形态学算法都是以这两种运算为基础的。
    膨胀
    是以得到B的相对与它自身原点的映像并且由z对映像进行移位为基础的。AB膨胀是所有位移z的集合,这样, 和A至少有一个元素是重叠的。我们可以把上式改写为:
    结构元素B可以看作一个卷积模板,区别在于膨胀是以集合运算为基础的,卷积是以算术运算为基础的,但两者的处理过程是相似的。
    用结构元素B,扫描图像A的每一个像素
    用结构元素与其覆盖的二值图像做操作
    如果都为0,结果图像的该像素为0。否则为1

    腐蚀
    Z中的集合ABBA进行腐蚀的整个过程如下:
    用结构元素B,扫描图像A的每一个像素
    用结构元素与其覆盖的二值图像做操作
    如果都为1,结果图像的该像素为1。否则为0
    腐蚀处理的结果是使原来的二值图像减小一圈。

    击中(匹配)或击不中变换
    假设集合A是由3个子集XYZ组成的集合,击中(匹配)的目的是要在A中找到X的位置,我们设X被包围在一个小窗口W中,与W有关的X的局部背景定义为集合的差(WX),则XA内能得到精确拟合位置集合是由XA的腐蚀后由(WX)对A的补集Ac腐蚀的交集,这个交集就是我们要找的位置,我们用集合B来表示由XX的背景构成的集合,我们可以令B=(B1B2),这里B1XB2=(WX),则在A中对B进行匹配可以表示为:
    A⊙B
    我们称为形态学上的击中或击不中变换。

     

    开闭操作
    开操作是先腐蚀、后膨胀处理。

    闭操作是先膨胀、后腐蚀处理。

     

    (6) 细化
    图像细化一般作为一种图像预处理技术出现,目的是提取源图像的骨架,即是将原图像中线条宽度大于1个像素的线条细化成只有一个像素宽,形成骨架,形成骨架后能比较容易的分析图像,如提取图像的特征。
    细化基本思想是层层剥夺,即从线条边缘开始一层一层向里剥夺,直到线条剩下一个像素的为止。图像细化大大地压缩了原始图像地数据量,并保持其形状的基本拓扑结构不变,从而为文字识别中的特征抽取等应用奠定了基础。细化算法应满足以下条件:
    将条形区域变成一条薄线;
    薄线应位与原条形区域的中心;
    薄线应保持原图像的拓扑特性。
    细化分成串行细化和并行细化,串行细化即是一边检测满足细化条件的点,一边删除细化点;并行细化即是检测细化点的时候不进行点的删除只进行标记,而在检测完整幅图像后一次性去除要细化的点。
    常用的图像细化算法有hilditch算法,pavlidis算法和rosenfeld算法等。
    注:进行细化算法前要先对图像进行二值化,即图像中只包含两种颜色。

    具体详细的图像形态学资料参考:http://wenku.baidu.com/view/1923d18fcc22bcd126ff0ccc.html

     

    二、OpenCv形态学操作相关函数

    1MorphologyEx         高级形态学变换

    void cvMorphologyEx( const CvArr* src, CvArr* dst, CvArr* temp,
    IplConvKernel* element, int operation, int iterations=1 );
    src
    输入图像.
    dst
    输出图像.
    temp
    临时图像,某些情况下需要
    element
    结构元素
    operation
    形态操作的类型:
    CV_MOP_OPEN -
    开运算
    CV_MOP_CLOSE -
    闭运算
    CV_MOP_GRADIENT -
    形态梯度
    CV_MOP_TOPHAT - "
    顶帽"
    CV_MOP_BLACKHAT - "
    黑帽"
    iterations
    膨胀和腐蚀次数.
    函数 cvMorphologyEx 在膨胀和腐蚀基本操作的基础上,完成一些高级的形态变换:

    开运算
    dst=open(src,element)=dilate(erode(src,element),element)
    闭运算
    dst=close(src,element)=erode(dilate(src,element),element)
    形态梯度
    dst=morph_grad(src,element)=dilate(src,element)-erode(src,element)
    "
    顶帽"
    dst=tophat(src,element)=src-open(src,element)
    "
    黑帽"
    dst=blackhat(src,element)=close(src,element)-src
    临时图像 temp 在形态梯度以及对顶帽黑帽操作时的 in-place 模式下需要。

    2Dilate    使用任意结构元素膨胀图像

    void cvDilate( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 );
    src
    输入图像.
    dst
    输出图像.
    element
    用于膨胀的结构元素。若为 NULL, 则使用 3×3 长方形的结构元素
    iterations
    膨胀的次数
    函数 cvDilate 对输入图像使用指定的结构元进行膨胀,该结构决定每个具有最小值象素点的邻域形状:
    dst=dilate(src,element): dst(x,y)=max((x',y') in element))src(x+x',y+y')
    函数支持(in-place)模式。膨胀可以重复进行 (iterations) . 对彩色图像,每个彩色通道单独处理。

    3Erode    使用任意结构元素腐蚀图像

    void cvErode( const CvArr* src, CvArr* dst, IplConvKernel* element=NULL, int iterations=1 );
    src
    输入图像.
    dst
    输出图像.
    element
    用于腐蚀的结构元素。若为 NULL, 则使用 3×3 长方形的结构元素
    iterations
    腐蚀的次数
    函数 cvErode 对输入图像使用指定的结构元素进行腐蚀,该结构元素决定每个具有最小值象素点的邻域形状:
    dst=erode(src,element): dst(x,y)=min((x',y') in element))src(x+x',y+y')
    函数可能是本地操作,不需另外开辟存储空间的意思。腐蚀可以重复进行 (iterations) . 对彩色图像,每个彩色通道单独处理。

    注:CreateStructuringElementEx  创建结构元素;ReleaseStructuringElement 删除结构元素。

     

    三、OpenCv形态学实例代码:

    1、腐蚀、膨胀、开运算、闭运算

    内容参考:http://blog.csdn.net/gnuhpc/archive/2009/06/21/4286177.aspx

    /******************************* 

    数学形态运算,最常见的基本运算有七种, 

    分别为:腐蚀、膨胀、开运算、闭运算、击中、细化和粗化, 

    它们是全部形态学的基础。 

    ********************************/ 

    #include "cv.h"  

    #include "highgui.h"  

    #include <stdlib.h>  

    #include <stdio.h>  

    IplImage *src=0;  

    IplImage *dst=0;  

    IplConvKernel *element=0;//声明一个结构元素  

    int element_shape=CV_SHAPE_RECT;//长方形形状的元素  

    int max_iters=10;  

    int open_close_pos=0;  

    int erode_dilate_pos=0;  

    void OpenClose(int pos)  

    {  

        int n=open_close_pos-max_iters;  

        int an=n>0?n:-n;  

        element = cvCreateStructuringElementEx(an*2+1,   an*2+1,an,an,element_shape,0);//创建结构元素  

         

        if (n<0)  

        {  

            cvErode(src,dst,element,1);//腐蚀图像  

            cvDilate(dst,dst,element,1);//膨胀图像  

        }  

        else 

        {         

            cvDilate(dst,dst,element,1);//膨胀图像  

            cvErode(src,dst,element,1);//腐蚀图像  

        }  

        cvReleaseStructuringElement(&element);  

        cvShowImage("Open/Close",dst);  

    }  

    void ErodeDilate(int pos)  

    {  

        int n=erode_dilate_pos-max_iters;  

        int an=n>0?n:-n;  

        element = cvCreateStructuringElementEx(an*2+1,an*2+1,an,an,element_shape,0);  

        if (n<0)  

        {  

            cvErode(src,dst,element,1);  

        }  

        else 

        {  

            cvDilate(src,dst,element,1);  

        }  

        cvReleaseStructuringElement(&element);  

        cvShowImage("Erode/Dilate",dst);  

    }  

    int main(int argc,char **argv)  

    {  

        char *filename =argc ==2?argv[1]:(char *)"lena.jpg";      

        if( (src = cvLoadImage(filename,1)) == 0 )  

            return -1;  

        dst=cvCloneImage(src);  

        cvNamedWindow("Open/Close",1);  

        cvNamedWindow("Erode/Dilate",1);  

        open_close_pos = erode_dilate_pos = max_iters;  

        cvCreateTrackbar("iterations","Open/Close",&open_close_pos,max_iters*2+1,OpenClose);  

        cvCreateTrackbar("iterations","Erode/Dilate",&erode_dilate_pos,max_iters*2+1,ErodeDilate);  

        for (;;)  

        {  

            int c;  

            OpenClose(open_close_pos);  

            ErodeDilate(erode_dilate_pos);  

            c= cvWaitKey(0);  

            if (c==27)  

            {  

                break;  

            }  

            switch(c) {  

            case 'e':  

                element_shape=CV_SHAPE_ELLIPSE;  

                break;  

            case 'r':  

                element_shape=CV_SHAPE_RECT;  

                break;  

            case '/r':  

                element_shape=(element_shape+1)%3;  

                break;  

            default:  

                break;   

            }  

        }  

        cvReleaseImage(&src);  

        cvReleaseImage(&dst);  

         

        cvDestroyWindow("Open/Close");  

        cvDestroyWindow("Erode/Dilate");  

        return 0;  

    }  

    /***************************** 

    腐蚀和膨胀,看上去好像是一对互逆的操作,实际上,这两种操作不具有互逆的关系。 

    开运算和闭运算正是依据腐蚀和膨胀的不可逆性,演变而来的。 

    先腐蚀后膨胀的过程就称为开运算。 

    闭运算是通过对腐蚀和膨胀的另一种不同次序的执行而得到的, 

    闭运算是先膨胀后腐蚀的过程,其功能是用来填充物体内细小空洞、连接邻近物体、平滑其边界, 

    同时不明显改变不明显改变其面积。 

    ******************************/ 

    2opencv实现二值图像细化

    内容参考:http://blog.csdn.net/byxdaz/archive/2010/06/02/5642669.aspx

     

               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

    新的改变

    我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

    1. 全新的界面设计 ,将会带来全新的写作体验;
    2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
    3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
    4. 全新的 KaTeX数学公式 语法;
    5. 增加了支持甘特图的mermaid语法1 功能;
    6. 增加了 多屏幕编辑 Markdown文章功能;
    7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
    8. 增加了 检查列表 功能。

    功能快捷键

    撤销:Ctrl/Command + Z
    重做:Ctrl/Command + Y
    加粗:Ctrl/Command + B
    斜体:Ctrl/Command + I
    标题:Ctrl/Command + Shift + H
    无序列表:Ctrl/Command + Shift + U
    有序列表:Ctrl/Command + Shift + O
    检查列表:Ctrl/Command + Shift + C
    插入代码:Ctrl/Command + Shift + K
    插入链接:Ctrl/Command + Shift + L
    插入图片:Ctrl/Command + Shift + G

    合理的创建标题,有助于目录的生成

    直接输入1次#,并按下space后,将生成1级标题。
    输入2次#,并按下space后,将生成2级标题。
    以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

    如何改变文本的样式

    强调文本 强调文本

    加粗文本 加粗文本

    标记文本

    删除文本

    引用文本

    H2O is是液体。

    210 运算结果是 1024.

    插入链接与图片

    链接: link.

    图片: Alt

    带尺寸的图片: Alt

    当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

    如何插入一段漂亮的代码片

    博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

    // An highlighted block var foo = 'bar'; 

    生成一个适合你的列表

    • 项目
      • 项目
        • 项目
    1. 项目1
    2. 项目2
    3. 项目3
    • 计划任务
    • 完成任务

    创建一个表格

    一个简单的表格是这么创建的:

    项目Value
    电脑$1600
    手机$12
    导管$1

    设定内容居中、居左、居右

    使用:---------:居中
    使用:----------居左
    使用----------:居右

    第一列第二列第三列
    第一列文本居中第二列文本居右第三列文本居左

    SmartyPants

    SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

    TYPEASCIIHTML
    Single backticks'Isn't this fun?'‘Isn’t this fun?’
    Quotes"Isn't this fun?"“Isn’t this fun?”
    Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

    创建一个自定义列表

    Markdown
    Text-to- HTML conversion tool
    Authors
    John
    Luke

    如何创建一个注脚

    一个具有注脚的文本。2

    注释也是必不可少的

    Markdown将文本转换为 HTML

    KaTeX数学公式

    您可以使用渲染LaTeX数学表达式 KaTeX:

    Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

    Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

    你可以找到更多关于的信息 LaTeX 数学表达式here.

    新的甘特图功能,丰富你的文章

    gantt
            dateFormat  YYYY-MM-DD
            title Adding GANTT diagram functionality to mermaid
            section 现有任务
            已完成               :done,    des1, 2014-01-06,2014-01-08
            进行中               :active,  des2, 2014-01-09, 3d
            计划一               :         des3, after des2, 5d
            计划二               :         des4, after des3, 5d
    
    • 关于 甘特图 语法,参考 这儿,

    UML 图表

    可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

    张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

    这将产生一个流程图。:

    链接
    长方形
    圆角长方形
    菱形
    • 关于 Mermaid 语法,参考 这儿,

    FLowchart流程图

    我们依旧会支持flowchart的流程图:

    • 关于 Flowchart流程图 语法,参考 这儿.

    导出与导入

    导出

    如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

    导入

    如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
    继续你的创作。


    1. mermaid语法说明 ↩︎

    2. 注脚的解释 ↩︎

    展开全文
  • 形态学变换

    2014-03-25 21:26:42
    目标 本文档尝试解答如下问题: ...开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 原理 Note   以下内容来自于Bradski和Kaehler的大作 Learn
  • MORPHOLOGICAL PROCESSING : 用于提取连接组件,提供算法解释(手写笔记)
  • 从物模实验着手,通过实验数据与现场施工数据对比方法,解释上述施工现象。煤层气施工压力是煤岩裂缝扩展在地面的响应结果,与裂缝形态关系密切。调研裂缝形态影响因素并应用物模实验方法,人工起裂多种形态裂缝。起裂...
  • 为了进一步提高地震属性解释的精度和效率,通过将数学形态学原理引入地震属性图像处理与分析中,结合断层地质体的平面展布特征,形成了线状特征的地质解译流程,以特定的采区为例,实现了断层的自动地质解译。研究结果...
  • opencv 形态学变换 morphologyEx函数

    万次阅读 多人点赞 2017-05-26 14:14:45
    opencv 形态学变换 morphologyEx函数 demo:http://download.csdn.net/detail/keen_zuxwang/9852594 高级形态学变换: 开运算: 先腐蚀,再膨胀,可清除一些小东西(亮的),放大局部低亮度的区域 闭运算: 先...
  • opencv: 形态学 转换(图示+源码)

    千次阅读 2017-07-26 09:38:49
    OpenCV中的形态学转换操作有七种:腐蚀,膨胀,开运算,闭运算,形态学梯度,礼帽,黑帽。 API参照表 中文名 英文名 api 原理 个人理解 腐蚀 erode erosion = cv2.erode(src=girl_pic, kernel=...
  • 数字图像处理 - 形态学腐蚀

    千次阅读 2017-10-26 16:16:38
    形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。 数学形态学(Mathematical ...
  • 数学形态学的扛鼎之作,深入浅出的解释数学形态学的原理和效果
  • 图像处理基础——灰度级形态

    千次阅读 2019-09-24 10:35:33
    文章目录简介膨胀 腐蚀腐蚀膨胀开运算 闭运算一些基本的灰度级形态学算法形态学平滑形态学梯度顶帽变换 底帽变换灰度级形态学重建 简介 形态学只适用于Region操作 或者是二值化后的图片 需要延伸到灰度形态学才能...
  • OpenCV之图像形态学运算

    千次阅读 2013-01-27 23:07:23
     腐蚀和膨胀这两类形态学运算的原理性解释网上很多,稍微搜索一下就可以获得比较全面的了解,而且在实际应用中很少有单独使用腐蚀或膨胀运算的,通常是将两者组合起来使用,也就是先腐蚀后膨胀的开运算、先膨胀后...
  • 形态学重建之孔洞填充

    千次阅读 2020-11-28 15:52:30
    白菜苗1、什么是膨胀(如果已经了解,请往下看)2、什么是孔洞填充(如果已经了解,请往下看)3、什么是形态学重建(如果已经了解,请往下看)4、什么是测地膨胀(如果已经了解,请往下看)5、什么是形态学重建之...
  • 利用这些数据,进行了主成分分析,以确定这些属性中的哪一个解释了数据的最大差异。 结果表明,高度和基础面积是与植物年龄最密切相关的特性。 这也是第一次证明收获时间可以减少一两年,这在经济上对生产者有利,...
  • morphogenesis —— 形态发生

    千次阅读 2016-07-28 19:04:56
    胚胎是专指有性生殖而言,是指雄性生殖细胞和雌性生殖...形态形成指生物发生中产生新的形态过程。多细胞生物既有时间上的分化,又有空间上的分化。在个体的细胞数目大量增加的同时,分化程度越来越复杂,细胞间的差异也
  • 数学形态学是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、图像顶帽运算和图像底帽运算、骨架抽取、形态学梯度、Top-hat变换等...
  • 图像形态学操作

    千次阅读 2019-02-23 09:47:42
    图像形态学操作 微信公众号:幼儿园的学霸 个人的学习笔记,关于OpenCV,关于机器学习, … 问题或建议,请公众号留言; 看到一段话,深有感触 作为软件开发者,我们曾经写过的或者正在写的每一行代码都对我们的...
  • 然后我们调查这种模式的潜在解释。 一种可能性是,编辑更喜欢发表由分享其意识形态的作者撰写的文章。 另一种可能性是,编辑客观上更擅长评估与他们的意识形态相同的作者撰写的文章的贡献。 我们发现有证据表明,...
  • 本章介绍由数学形态学衍生的二值图像形态学算法,主要包括形态学膨胀、腐蚀、开运算和闭运算四种常用算法,并以此为基础讲解形态学轮廓提取算法,结合C语言编程实现,通俗易懂,图文并茂。
  • 采用可扩散形态发生素梯度的模型经常被用来解释早期胚胎的区域特征。 在这些模型中,分泌因子的浓度梯度会产生不同的细胞命运,这是基于细胞对这种形态发生素的局部浓度的不同React。 近年来,人们对参与脊椎动物中...
  • 通俗易懂-形态学概念介绍

    千次阅读 2017-06-29 10:23:26
    形态学现在学完基本的几个了,但我还是不知道什么是形态学!原理其实就是和“卷积”在图像处理中的应用一样,就是一个“内核”遍历图像之后进行处理,内核的不同使得处理得到的图像效果也是不同的。下面介绍几种形态...
  • 主要形态学操作有 膨胀、腐蚀 、开运算,闭运算,形态学梯度,顶帽,黑帽。对其原理及opencv的实现进行总结。  参考博客及资料如下:图像处理--形态学...
  • 前一节我们讨论了两种最基本的形态学操作: 腐蚀 (Erosion) 膨胀 (Dilation) 运用这两个基本操作,我们可以实现更高级的形态学变换。这篇文档将会简要介绍OpenCV提供的5种高级形态学操作: 如何使用OpenCV函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,278
精华内容 16,511
关键字:

形态解释