18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑

摘要:
其数学表达式如下:  开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

、理论与概念讲解

1、开运算(Opening Operation)

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

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第1张

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

2、闭运算(Closing Operation)

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

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第2张

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

3、形态学梯度(MorphologicalGradient)

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

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第3张

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

4、顶帽(Top Hat)

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

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第4张

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

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

5、黑帽(Black Hat)

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

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第5张

  黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块。非常完美的轮廓效果图:

二、示例

1、morphologyEx函数详解

  上面我们已经讲到,morphologyEx函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。函数原型如下:

1 C++: voidmorphologyEx(
2   InputArray src,
3   OutputArray dst,
4   intop,
5   InputArraykernel,
6   Pointanchor=Point(-1,-1),
7   intiterations=1,
8   intborderType=BORDER_CONSTANT,
9   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我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:
                矩形: MORPH_RECT
                交叉形: MORPH_CROSS
                椭圆形: MORPH_ELLIPSE

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

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

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

1 int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
2  
3 //获取自定义核
4 Mat element =getStructuringElement(MORPH_RECT,
5        Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
6        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)操作。且对于多通道图像,每一个通道都是单独进行操作。

2、开运算

1 #include <opencv2/opencv.hpp>
2 #include<opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】开运算");
13     namedWindow("【效果图】开运算");
14     //显示原始图
15     imshow("【原始图】开运算", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作AC
19 morphologyEx(image,image, MORPH_OPEN, element);
20     //显示效果图
21     imshow("【效果图】开运算", image);
22     imwrite("开运算.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     开运算

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第6张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第7张

3、闭运算

1 #include <opencv2/opencv.hpp>
2 #include <opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】闭运算");
13     namedWindow("【效果图】闭运算");
14     //显示原始图
15     imshow("【原始图】闭运算", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_CLOSE, element);
20     //显示效果图
21     imshow("【效果图】闭运算", image);
22     imwrite("闭运算.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     闭运算

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第8张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第9张

3、梯度

1 #include <opencv2/opencv.hpp>
2 #include<opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】形态学梯度");
13     namedWindow("【效果图】形态学梯度");
14     //显示原始图
15     imshow("【原始图】形态学梯度", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_GRADIENT, element);
20     //显示效果图
21     imshow("【效果图】形态学梯度", image);
22     imwrite("梯度.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     梯度

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第10张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第11张

4、顶帽运算

1 #include <opencv2/opencv.hpp>
2 #include<opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】顶帽运算");
13     namedWindow("【效果图】顶帽运算");
14     //显示原始图
15     imshow("【原始图】顶帽运算", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_TOPHAT, element);
20     //显示效果图
21     imshow("【效果图】顶帽运算", image);
22     imwrite("顶帽.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     顶帽

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第12张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第13张

6、黑帽运算

1 #include <opencv2/opencv.hpp>
2 #include <opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】黑帽运算");
13     namedWindow("【效果图】黑帽运算");
14     //显示原始图
15     imshow("【原始图】黑帽运算", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_BLACKHAT, element);
20     //显示效果图
21     imshow("【效果图】黑帽运算", image);
22     imwrite("黑帽.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     黑帽

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第14张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第15张

7、腐蚀(morphologyEx调用版)

1 #include <opencv2/opencv.hpp>
2 #include <opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】腐蚀");
13     namedWindow("【效果图】腐蚀");
14     //显示原始图
15     imshow("【原始图】腐蚀", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_ERODE, element);
20     //显示效果图
21     imshow("【效果图】腐蚀", image);
22     imwrite("腐蚀.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     腐蚀

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第16张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第17张

8、膨胀(morphologyEx调用版)示例

1 #include <opencv2/opencv.hpp>
2 #include<opencv2/highgui/highgui.hpp>
3 #include<opencv2/imgproc/imgproc.hpp>
4 
5 using namespacecv;
6 
7 intmain( )
8 {
9     //载入原始图
10     Mat image = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
11     //创建窗口
12     namedWindow("【原始图】膨胀");
13     namedWindow("【效果图】膨胀");
14     //显示原始图
15     imshow("【原始图】膨胀", image);
16     //定义核
17     Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
18     //进行形态学操作
19 morphologyEx(image,image, MORPH_DILATE, element);
20     //显示效果图
21     imshow("【效果图】膨胀", image);
22     imwrite("膨胀.jpg", image);
23     waitKey(0);
24 
25     return 0;
26 }

效果展示:

原图     膨胀

18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第18张18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑第19张

三、综合示例

1 #include <opencv2/opencv.hpp>
2 #include <opencv2/highgui/highgui.hpp>
3 #include <opencv2/imgproc/imgproc.hpp>
4 
5 using namespacestd;
6 using namespacecv;
7 
8 Mat g_srcImage, g_dstImage;//原始图和效果图
9 int g_nElementShape = MORPH_RECT;//元素结构的形状
10 
11 //变量接收的TrackBar位置参数
12 int g_nMaxIterationNum = 10;
13 int g_nOpenCloseNum = 0;
14 int g_nErodeDilateNum = 0;
15 int g_nTopBlackHatNum = 0;
16 
17 static void on_OpenClose(int, void*);//回调函数
18 static void on_ErodeDilate(int, void*);//回调函数
19 static void on_TopBlackHat(int, void*);//回调函数
20 static void ShowHelpText();//帮助文字显示
21 
22 intmain( )
23 {
24 
25 ShowHelpText();
26 
27     //载入原图
28     g_srcImage = imread("1.jpg");//工程目录下需要有一张名为1.jpg的素材图
29     if( !g_srcImage.data )
30 {
31         cout << "载入错误!" <<endl;
32 }
33     //显示原始图
34     namedWindow("【原始图】");
35     imshow("【原始图】", g_srcImage);
36 
37     //创建三个窗口
38     namedWindow("【开运算/闭运算】",1);
39     namedWindow("【腐蚀/膨胀】",1);
40     namedWindow("【顶帽/黑帽】",1);
41 
42     //参数赋值
43     g_nOpenCloseNum=9;
44     g_nErodeDilateNum=9;
45     g_nTopBlackHatNum=2;
46 
47     //分别为三个窗口创建滚动条
48     createTrackbar("迭代值", "【开运算/闭运算】",&g_nOpenCloseNum,g_nMaxIterationNum*2+1,on_OpenClose);
49     createTrackbar("迭代值", "【腐蚀/膨胀】",&g_nErodeDilateNum,g_nMaxIterationNum*2+1,on_ErodeDilate);
50     createTrackbar("迭代值", "【顶帽/黑帽】",&g_nTopBlackHatNum,g_nMaxIterationNum*2+1,on_TopBlackHat);
51 
52     //轮询获取按键信息
53     while(1)
54 {
55         intc;
56 
57         //执行回调函数
58         on_OpenClose(g_nOpenCloseNum, 0);
 59         on_ErodeDilate(g_nErodeDilateNum, 0);
 60         on_TopBlackHat(g_nTopBlackHatNum,0);
 61 
 62         //获取按键
 63         c = waitKey(0);
 64 
 65         //按下键盘按键Q或者ESC,程序退出
 66         if( (char)c == 'q'||(char)c == 27 )
 67             break;
 68         //按下键盘按键1,使用椭圆(Elliptic)结构元素结构元素MORPH_ELLIPSE
 69         if( (char)c == 49 )//键盘按键1的ASII码为49
 70             g_nElementShape = MORPH_ELLIPSE;
 71         //按下键盘按键2,使用矩形(Rectangle)结构元素MORPH_RECT
 72         else if( (char)c == 50 )//键盘按键2的ASII码为50
 73             g_nElementShape = MORPH_RECT;
 74         //按下键盘按键3,使用十字形(Cross-shaped)结构元素MORPH_CROSS
 75         else if( (char)c == 51 )//键盘按键3的ASII码为51
 76             g_nElementShape = MORPH_CROSS;
 77         //按下键盘按键space,在矩形、椭圆、十字形结构元素中循环
 78         else if( (char)c == ' ' )
 79             g_nElementShape = (g_nElementShape + 1) % 3;
 80     }
 81 
 82     return 0;
 83 }
 84 
 85 static void on_OpenClose(int, void*)
 86 {
 87     //偏移量的定义
 88     int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
 89     int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
 90     //自定义核
 91     Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
 92     //进行操作
 93     if( offset < 0 )
 94         morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
 95     else
 96         morphologyEx(g_srcImage, g_dstImage, CV_MOP_CLOSE, element);
 97     //显示图像
 98     imshow("【开运算/闭运算】",g_dstImage);
 99 }
100 
101 
102 static void on_ErodeDilate(int, void*)
103 {
104     //偏移量的定义
105     int offset = g_nErodeDilateNum - g_nMaxIterationNum;    //偏移量
106     int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
107     //自定义核
108     Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
109     //进行操作
110     if( offset < 0 )
111         erode(g_srcImage, g_dstImage, element);
112     else
113         dilate(g_srcImage, g_dstImage, element);
114     //显示图像
115     imshow("【腐蚀/膨胀】",g_dstImage);
116 }
117 
118 
119 static void on_TopBlackHat(int, void*)
120 {
121     //偏移量的定义
122     int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
123     int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
124     //自定义核
125     Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset*2+1, Absolute_offset*2+1), Point(Absolute_offset, Absolute_offset) );
126     //进行操作
127     if( offset < 0 )
128         morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT , element);
129     else
130         morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
131     //显示图像
132     imshow("【顶帽/黑帽】",g_dstImage);
133 }
134 
135 static void ShowHelpText()
136 {
137 //输出一些帮助信息
138     cout << "


	请调整滚动条观察图像效果~

";
139     cout << "

	按键操作说明: 

"
140         "		键盘按键【ESC】或者【Q】- 退出程序
"
141         "		键盘按键【1】- 使用椭圆(Elliptic)结构元素
"
142         "		键盘按键【2】- 使用矩形(Rectangle )结构元素
"
143         "		键盘按键【3】- 使用十字型(Cross-shaped)结构元素
"
144         "		键盘按键【空格SPACE】- 在矩形、椭圆、十字形结构元素中循环
"
145         "

								";
146 }

免责声明:文章转载自《18、【opencv入门】形态学图像处理(一):开运算、闭运算、形态学梯度、顶帽、黑帽合辑》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇IOS常用代码整理【转】lvextend 无法使用 virtualbox 扩容的vdi下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

logic:iterate 遍历

1. 遍历集合 <logic:iterate> 的 name 属性指定需要进行遍历的集合对象, 它每次从集合中检索出一个元素, 然后把它放在page 范围内, 并以id 属性指定的字符串来命名这个元素, 例如: <% Vector animals = new Vector(); animals.addElement("Dog"); ani...

十一种通用滤波算法(转)

1、限幅滤波法(又称程序判断滤波法) A、方法:    根据经验判断,确定两次采样允许的最大偏差值(设为A)    每次检测到新值时判断:    如果本次值与上次值之差<=A,则本次值有效    如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值 B、优点:    能有效克服因偶然因素引起的脉冲干扰 C、缺点    无法...

05-移动和PC配置多页面打包引入element和vant冲突

element是全局引入的,那么vant需要手动按需引入,才不会报错(npm run build会报错,npm run serve不会报错,只会提示notify的warning) vant 如何手动引入?分为三步: 第一步:分别引入组件和样式 第二步:这是最关键的一步,组件赋名的时候要这样 如果上面这种注册写法还是会报错,那么可以换成下面这种写法:...

[Selenium] CSS3 选择器

http://www.cnblogs.com/MasterMonkInTemple/category/564552.html 在 CSS 中,选择器是一种模式,用于选择需要添加样式的元素。 "CSS" 列指示该属性是在哪个 CSS 版本中定义的。(CSS1、CSS2 还是 CSS3。) 选择器 例子 例子描述 CSS .class .intro 选...

element el-upload组件获取文件名

组件的连接:http://element-cn.eleme.io/#/zh-CN/component/upload 需求:点x按钮,获取文件名传到后端服务,把文件从服务器删除 分析: 仔细看文档,会发现默认传有两个参数file,fileList,都是object类型;file.name就可以拿到当前操作的文件名...

使用Selenium和openCV对HTML5 canvas游戏进行自动化功能测试(一)

上一篇讲了HTML5 canvas游戏的基本工作原理,接下来讲如何进行自动化功能测试。 Selenium是一个跨平台的跨浏览器的对网页进行自动化测试的工具。从Selenium 2.0开始Selenium就和WebDriver合体了。如果你还不了解Selenium怎么用,可以看看官网上的例子。Selenium支持各种语言的binding,方便起见,下面的测试...