我是靠谱客的博主 儒雅缘分,最近开发中收集的这篇文章主要介绍opencv中findcontours(二),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

hierarchy

[Next, Previous, First Child, Parent]

理解轮廓层级

轮廓层级展示
图中总共有8条轮廓,2和2a分别表示外层和里层的轮廓,3和3a也是一样。从图中看得出来:

  1. 轮廓0/1/2是最外层的轮廓,我们可以说它们处于同一轮廓等级:0级
  2. 轮廓2a是轮廓2的子轮廓,反过来说2是2a的父轮廓。轮廓2a算一个等级:1级
  3. 同样3是2a的子轮廓,轮廓3处于一个等级:2级
  4. 类似的,3a是3的子轮廓,等等…………
    这里面OpenCV关注的就是两个概念:同一轮廓等级和轮廓间的子属关系。

如果我们打印出cv.findContours()函数的返回值hierarchy,会发现它是一个包含4个值的数组:[Next, Previous, First Child, Parent]
Next:与当前轮廓处于同一层级的下一条轮廓
举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next=1;同理,对轮廓1来说,Next=2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next=-1。
Previous:与当前轮廓处于同一层级的上一条轮廓
跟前面一样,对于轮廓1来说,Previous=0;对于轮廓2,Previous=1;对于轮廓2a,没有上一条轮廓了,所以Previous=-1。
First Child:当前轮廓的第一条子轮廓
比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child=2a;对轮廓3,First Child=3a。
Parent:当前轮廓的父轮廓
比如2a的父轮廓是2,Parent=2;轮廓2没有父轮廓,所以Parent=-1。 

 轮廓寻找方式

int型的mode,定义轮廓的检索模式:

  • 取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
  • 取值二:CV_RETR_LIST   检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
  • 取值三:CV_RETR_CCOMP  检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
  • 取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

  method取值“CV_CHAIN_APPROX_NONE”,保存轮廓上所有点

RETR_EXTERNAL

这种方式只寻找最高层级的轮廓,也就是它只会找到前面我们所说的3条最外层轮廓:


	cv::Mat src = cv::imread("01.png", cv::IMREAD_COLOR);

	cv::Mat b;
	cv::extractChannel(src, b, 0);

	std::vector<vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(b > 50, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

	std::for_each(std::begin(hierarchy), std::end(hierarchy), [](cv::Vec4i p) {std::cout << p << std::endl; });

	cv::drawContours(src, contours, -1, cv::Scalar(255, 0, 0), 2);

[1, -1, -1, -1] //轮廓0
[2, 0, -1, -1] //轮廓1
[-1, 1, -1, -1] //轮廓2 

 RETR_LIST

	cv::Mat src = cv::imread("01.png", cv::IMREAD_COLOR);

	cv::Mat b;
	cv::extractChannel(src, b, 0);

	std::vector<vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(b > 50, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE);

	std::for_each(std::begin(hierarchy), std::end(hierarchy), [](cv::Vec4i p) {std::cout << p << std::endl; });

	cv::drawContours(src, contours, -1, cv::Scalar(255, 0, 0), 2);

 这是最简单的一种寻找方式,它不建立轮廓间的子属关系,也就是所有轮廓都属于同一层级。这样,hierarchy中的后两个值[First Child, Parent]都为-1。比如同样的图,我们使用RETR_LIST来寻找轮廓: 

[1, -1, -1, -1]
[2, 0, -1, -1]
[3, 1, -1, -1]
[4, 2, -1, -1]
[5, 3, -1, -1]
[6, 4, -1, -1]
[7, 5, -1, -1]
[8, 6, -1, -1]
[9, 7, -1, -1]
[10, 8, -1, -1]
[-1, 9, -1, -1]

因为没有从属关系,所以轮廓0的下一条是1,1的下一条是2……

如果你不需要轮廓层级信息的话,RETR_LIST更推荐使用,因为性能更好

  

 RETR_CCOMP

分别表示第 i个轮廓的一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号

它把所有的轮廓只分为2个层级,不是外层的就是里层的。 

	cv::Mat src = cv::imread("01.png", cv::IMREAD_COLOR);

	cv::Mat b;
	cv::extractChannel(src, b, 0);

	std::vector<vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(b > 50, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_NONE);

	std::for_each(std::begin(hierarchy), std::end(hierarchy), [](cv::Vec4i p) {std::cout << p << std::endl; });

	for (int i = 0; i < hierarchy.size(); ++i)
	{
		if (hierarchy[i][2] != -1) //里层
		{
			cv::drawContours(src, contours, i, cv::Scalar(255, 0, 0), 2);
		}
		if (hierarchy[i][3] != -1) //外层
		{
			cv::drawContours(src, contours, i, cv::Scalar(0, 255, 0), 2);
		}
		if ((hierarchy[i][3] == -1) && (hierarchy[i][2] == -1)) //非里外层
		{
			cv::drawContours(src, contours, i, cv::Scalar(0, 0, 255), 2);
		}
	}

	cv::imshow("src", src);

[3, -1, 1, -1]
[2, -1, -1, 0]
[-1, 1, -1, 0]
[5, 0, 4, -1]
[-1, -1, -1, 3]
[7, 3, 6, -1]
[-1, -1, -1, 5]
[8, 5, -1, -1]
[9, 7, -1, -1]
[-1, 8, 10, -1]
[-1, -1, -1, 9]

 

OpenCV的findContours源码与原理-阿里云开发者社区

最后

以上就是儒雅缘分为你收集整理的opencv中findcontours(二)的全部内容,希望文章能够帮你解决opencv中findcontours(二)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部