首页 >> 大全

【OpenCV入门教程之十一】 形态学图像处理 二 开运算 闭运算 形态学梯度

2023-11-16 大全 24 作者:考证青年

知乎:

邮箱:

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

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

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

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

形态学开运算__形态学中的开运算有什么作用

形态学中的开运算有什么作用__形态学开运算

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

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

形态学中的开运算有什么作用_形态学开运算_

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

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

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

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

_形态学中的开运算有什么作用_形态学开运算

这是的官方主页传送门:

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

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

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

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

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

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

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

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

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

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

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

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

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

_形态学中的开运算有什么作用_形态学开运算

OK,我们开始讲解。

1.1 开运算( )

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

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

形态学中的开运算有什么作用__形态学开运算

实际效果图:

_形态学中的开运算有什么作用_形态学开运算

1.2 闭运算( )

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

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

形态学开运算__形态学中的开运算有什么作用

实际效果图:

形态学中的开运算有什么作用_形态学开运算_

1.3 形态学梯度(t)

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

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

形态学开运算__形态学中的开运算有什么作用

实际素材效果图:

_形态学中的开运算有什么作用_形态学开运算

_形态学开运算_形态学中的开运算有什么作用

1.4 顶帽(Top Hat)

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

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

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

如下所示:

形态学开运算_形态学中的开运算有什么作用_

素材效果图:

_形态学开运算_形态学中的开运算有什么作用

1.5黑帽(Black Hat)

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

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

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

形态学中的开运算有什么作用_形态学开运算_

实际素材效果图:

形态学开运算__形态学中的开运算有什么作用

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

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

//-----------------------------------【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" );    }}

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

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

3.1 函数详解

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3.2 开运算示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include#include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 闭运算示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include #include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 形态学梯度示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include#include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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)示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include#include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 黑帽运算()示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include #include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 腐蚀(调用版)示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include #include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 膨胀(调用版)示例程序

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

//-----------------------------------【头文件包含部分】---------------------------------------//            描述:包含程序所依赖的头文件//----------------------------------------------------------------------------------------------#include #include#include //-----------------------------------【命名空间声明部分】---------------------------------------//            描述:包含程序所使用的命名空间//-----------------------------------------------------------------------------------------------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 #include #include //-----------------------------------【命名空间声明部分】--------------------------------------//  描述:包含程序所使用的命名空间//----------------------------------------------------------------------------------------------- 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浅墨"  );}

放出一些效果图:

首先是原图:

形态学开运算__形态学中的开运算有什么作用

非常帅气的 有木有!

腐蚀效果图:

形态学开运算__形态学中的开运算有什么作用

膨胀效果图:

形态学中的开运算有什么作用_形态学开运算_

开运算效果图:

形态学中的开运算有什么作用__形态学开运算

闭运算效果图:

形态学开运算_形态学中的开运算有什么作用_

顶帽运算效果图:

_形态学中的开运算有什么作用_形态学开运算

黑帽运算效果图:

形态学中的开运算有什么作用__形态学开运算

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

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

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

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

形态学开运算_形态学中的开运算有什么作用_

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了