OpenCV4学习笔记(25)——凸包检测

今天要整理的笔记是关于图像凸包检测的内容,首先需要了解什么是凸包。

1. 凸包的含义
凸包,是图形学中的一个概念,指的是一个完全凸起、没有凹处的多边形,也叫凸多边形,一般来说,凸包都是伴随着某个点集存在的,也被称为某个点集的凸包。
对于一个点集而言,如果存在一个凸多边形完全地包含了这个点集的所有点,也就是说该点集内的点要么在这个凸多边形的边上,要么就是在这个凸多边形的内部,那么就称这个凸多边形是这个点集的凸包。所谓凸包检测,其实就是寻找一个包含某个点集中所有点的最小凸多边形,当然前提是这个点集本身形成的轮廓得是一个凸轮廓。
凸包检测经常应用在物体识别、手势识别及边界检测等领域,还可以应用于离散点统计时判断两个不同的点集能否被一条直线分隔开,借此也可对一幅图像中的两个目标物体判断其是否有重合,也就是判断检测出的两个凸包是否相接。

2.OpenCV实现凸包检测
在OpenCV中提供了三个与凸包检测相关的API,分别是isContourConvex(contours)convexHull(points, hull, clockwise, returnPoints)intersectConvexConvex()

第一个APIisContourConvex(contours)是用来判断某个轮廓是不是一个凸轮廓,其返回的是一个布尔值,当输入轮廓是凸轮廓时返回true,不是凸轮廓时返回false。不过要注意的是,我们输入的这个轮廓必须是一个简单的轮廓,也就是说它要么是凸轮廓,要么就是凹轮廓,不可以出现自相交的情况,否则返回值就会出现未定义的情况。

第二个APIconvexHull(points, hull, clockwise, returnPoints)就是用来寻找凸包的,OpenCV中使用Sklansky算法来查找输入点集的凸包,该算法在当前实现中具有O(N logN)的复杂度。其参数介绍如下:
第一个参数points:输入的点集(轮廓点集);
第二个参数hull:得到的凸包的顶点坐标点集,输出形式由第四个参数returnPoints决定;
第三个参数clockwise:寻找凸包顶点的方向是顺时针或逆时针;使用默认参数false即可;
第四个参数returnPoints:如果为true,则hull输出为凸包的顶点集;如果为false,则hull输出为int类型的vector,包含元素为凸包的各个顶点在轮廓点集对应的index。

第三个APIintersectConvexConvex()是用来求取两个凸包之间的交点,其前两个参数是输入的两个凸包点集,第三个参数就是找到的交点。使用这个API就可以用来判断两个凸包是否有相接的部分。

下面来看一下实现凸包检测的代码:

//读取图像并进行预处理
	Mat hand, hand_gray, hand_gaus, hand_binary;
	hand = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\hand.jpg");
	resize(hand, hand, Size(500, 600));
	cvtColor(hand, hand_gray, COLOR_BGR2GRAY);
	GaussianBlur(hand_gray, hand_gaus, Size(), 3, 3);
	threshold(hand_gaus, hand_binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("hand_binary", hand_binary);
	//轮廓检测
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	findContours(hand_binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	//遍历轮廓
	for (int i = 0; i < contours.size(); i++)
	{
		if (isContourConvex(contours[i]));				//判断该轮廓是不是凸包,若是返回true
		{
			vector<Point> hull;				//定义凸包顶点集
			convexHull(contours[i], hull, false, true);				//构建该轮廓的凸包
		
			for (size_t j = 0; j < hull.size(); j++)
			{
				circle(hand, hull[j], 2, Scalar(0, 255, 0), 1, 8, 0);			//将顶点绘制出来
				
				line(hand, hull[j % hull.size()], hull[(j + 1) % hull.size()], Scalar(0, 0, 255), 1, 8, 0);		
			}
		}
	}
	imshow("hand", hand);

上述代码中,就将图像中的手掌部分的凸包给检测了出来,并进行凸包绘制。要注意进行凸包检测之前,必须先判断该轮廓是否为凸轮廓,只有当一个轮廓是凸轮廓时才能进行凸包检测。

而且最后绘制凸包时,是在凸包点集中的每两个顶点间绘制线段,点集索引要记得对点集的size进行取余操作,确保将顶点连成封闭区域,才能形成凸包。

下面看一下凸包检测对于手掌的检测效果:
在这里插入图片描述
可见这里准确的将手指这些凸起部分给检测出来了,可以看到预处理后的二值图像是具有比较好的效果的,所以才能使后续的凸包检测效果比较理想。可见图像处理前的预处理操作是十分重要的,如果能得到比较好的效果的预处理图像,那么对于后续操作都会方便很多。

今天的笔记就整理到此啦,明天继续~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

发布了36 篇原创文章 · 获赞 43 · 访问量 1801

猜你喜欢

转载自blog.csdn.net/weixin_45224869/article/details/104869390