详解OpenCV的函数convexHull()和函数convexityDefects(),并利用它们)做凸包(凸壳)检测及凸包(凸壳)的缺陷检测

要理解凸包(凸壳)检测,首无要知道什么是凸包(凸壳),凸包(凸壳)是指如果在集合A内连接任意两个点的直线段都在A的内部,则称集合A是凸形的。简单点理解,就是一个多边型,没有凹的地方。凸包(凸壳)能包含点集中所有的点,凸包检测常应用在物体识别、手势识别及边界检测等领域。

OpenCV中提供了用于对物体轮廓凸包进行检测的函数convexHull(),对形状的凸包缺陷分析的函数convexityDefects(),下面分别介绍这两个函数。

函数convexHull()原型如下

void convexHull( 	InputArray points, 
					OutputArray hull, 
					bool clockwise = false, 
					bool returnPoints = true );

官方文档对参数意义的说明如下

points – Input 2D point set, stored in std::vector or Mat. 
hull – Output convex hull. It is either an integer vector of indices or vector of points. In the first case, the hull elements are 0-based indices of the convex hull points in the original array (since the set of convex hull points is a subset of the original point set). In the second case, hull elements are the convex hull points themselves. 
clockwise – Orientation flag. If it is true, the output convex hull is oriented clockwise. Otherwise, it is oriented counter-clockwise. The usual screen coordinate system is assumed so that the origin is at the top-left corner, x axis is oriented to the right, and y axis is oriented downwards. 
orientation – Convex hull orientation parameter in the old API, CV_CLOCKWISE or CV_COUNTERCLOCKWISE. 
returnPoints – Operation flag. In case of a matrix, when the flag is true, the function returns convex hull points. Otherwise, it returns indices of the convex hull points. When the output array is std::vector, the flag is ignored, and the output depends on the type of the vector: std::vector<int> implies returnPoints=true, std::vector<Point> implies returnPoints=false. 
翻译如下

points:输入二维点集(一般为轮廓点集),这些点集被存储在容器vector或Mat中,在下面的源码中,我是强制转化为了Mat类型。
hull:凸包点集输出。它的数据类型要么为整型向量,要么为点集向量。如果是整型向量,那么存储的只是索引,索引的对象是你输入的二维点集,之所以可以这样做,是因为函数检测到的凸包是输入点集的子集。如果是点集向量,那么这些点集就是凸包本身的点。
clockwise:凸包方向的标志位。如果是true,那么是基于顺时针方向,如果是false,那么是基于反时针方向。我测试了下两种结果下输出凸包,结果是没有任何区别。如下面两幅图所示:


returnPoints:函数输出类型,如果OutputArray是一个矩阵变量(MAT),那么returnPoints==true时,输出点坐标,否则,输出坐标索引,索引的对象是输入的二维点集。当然,如果你的OutputArray本来就是一个向量了,那这个参数的值会被无视掉,因为向量在初始化的时候是需要规定好类型的,要么为int型,要么为point型。

下面是使用函数convexHull()进行凸包检测的示例代码:

代码中用到的图片下载链接:https://pan.baidu.com/s/1uAClJ3xklpSGE1iA-7py5g?pwd=h237

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0
//VS版本:2013

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

int main()
{

	Mat srcGary = imread("F:/material/images/P0044-hand-02.jpg",0);

	imshow("srcGary", srcGary);

	// 阈值化操作
	Mat threMat;
	int thresh = 128;
	threshold(srcGary, threMat, thresh, 255, THRESH_BINARY);

	// 轮廓检测
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	findContours(threMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	// 绘制轮廓
	Mat contours_img(srcGary.size(), CV_8U, Scalar(0));
	drawContours(contours_img, contours, -1, Scalar(255), 1);
	imshow("contours_img", contours_img);

	//凸包检测
	vector<vector<Point> > pointHull(contours.size());
	vector<vector<int> >   intHull(contours.size());

	for (size_t i = 0; i < contours.size(); i++)
	{
		// 输出结果为Point类型的凸包检测
		convexHull(Mat(contours[i]), pointHull[i], false);
		// 输出结果为int类型的凸包检测
		convexHull(Mat(contours[i]), intHull[i], false);
	}

	//绘制凸包
	Mat convex_hull_img = Mat::zeros(threMat.size(), CV_8UC3);
	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(0, 0, 255);
		drawContours(convex_hull_img, pointHull, i, color, 1, 8, vector<Vec4i>(), 0, Point());

	}
	imshow("convex_hull_img", convex_hull_img);


	
	waitKey();
	return 0;
}

运行结果如下:

原图如下:

原图的轮廓如下:

 轮廓的凸包如下:

 

从上面的运行结果中我们也对凸包的概念有了直观的认识。

下面讲解函数convexHull()

在解释这个函数各参数的意义前,先要知道什么叫凸包(凸壳)的缺陷检测?要说明这个问题,配图说明是最好的方式,看了下面这幅图你就知道是怎么回事了!

 上图中黑色双箭头部分所指就是红色线绘制的凸包的缺陷,大家可以结合图形仔细体会一下什么叫凸包缺陷。从图中我们可以看出,凸包缺陷在运算上就像是凸包减去轮廓的图形。

函数convexHull()原型如下

void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );

三个参数说明如下:

contour---生成凸包的轮廓点集,一般就是轮廓检测函数findContours()的输出。

convexhull----函数convexHull()的输出,里面存储的是凸包信息,在这里只能是int类型(即vector<vector<int>>类型),而不能是vector<vector<Point>>类型。

convexityDefects ----类型为vector<vector<Vec4i>>类型,每一个凸包缺陷由若干个Vec4i来描述,每一个Vec4i由四个整型数据构成,这四个整型数据的名称分别为:start_index, end_index, farthest_pt_index, fixpt_depth。具体意义我用下面这张图来说明。

上面的黄色数字标的①②③④⑤⑥六个部分实际上就描述了手的轮廓凸包的六个凸包缺陷。这里以⑥为例说明start_index, end_index, farthest_pt_index, fixpt_depth的意义。

start_index:凸包缺陷的起点,上图⑥中的A或B就代表起点,A或B都位于轮廓上。

end_index: 凸包缺陷的终点,上图⑥中的A或B就代表终点,A或B都位于轮廓上。

farthest_pt_index:凸包缺陷中轮廓与凸包相距的最远点,上图⑥中的C点就是凸包缺陷中轮廓与凸包相距的最远点,C点也位于轮廓上。

​fixpt_depth:上图中C点距离凸包的距离。它以8位定点小数来近似表示,所以如果要换成浮点数,应该除以256,即the floating-point value=fixpt_depth/256.0。为什么有这个换算式?可参看下面这篇博文:
https://blog.csdn.net/niaolianjiulin/article/details/82764511

​明白了上面的参数意义后,接下来我们把上面的函数convexHull()的示例程序完善,得到函数convexHull()的示例代码如下:

代码中用到的图片下载链接:https://pan.baidu.com/s/1uAClJ3xklpSGE1iA-7py5g?pwd=h237

//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601

//OpenCV版本:3.0
//VS版本:2013

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

int main()
{

	Mat srcGary = imread("F:/material/images/P0044-hand-02.jpg",0);

	imshow("srcGary", srcGary);

	// 阈值化操作
	Mat threMat;
	int thresh = 128;
	threshold(srcGary, threMat, thresh, 255, THRESH_BINARY);

	// 轮廓检测
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;
	findContours(threMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

	// 绘制轮廓
	Mat contours_img(srcGary.size(), CV_8U, Scalar(0));
	drawContours(contours_img, contours, -1, Scalar(255), 1);
	imshow("contours_img", contours_img);

	//凸包检测和凸包缺陷检测
	vector<vector<Point> > pointHull(contours.size());
	vector<vector<int> >   intHull(contours.size());
	vector<vector<Vec4i> > hullDefect(contours.size());

	for (size_t i = 0; i < contours.size(); i++)
	{
		// 输出结果为Point类型的凸包检测
		convexHull(Mat(contours[i]), pointHull[i], false);
		// 输出结果为int类型的凸包检测
		convexHull(Mat(contours[i]), intHull[i], false);

		//凸包缺陷检测
		convexityDefects(Mat(contours[i]), intHull[i], hullDefect[i]);
	}

	//绘制凸包及凸包缺陷
	Mat convex_hull_img = contours_img.clone();
	cvtColor(convex_hull_img, convex_hull_img, COLOR_GRAY2BGR);


	for (size_t i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(0, 0, 255);
		drawContours(convex_hull_img, pointHull, i, color, 1, 8, vector<Vec4i>(), 0, Point());

		// 绘制缺陷
		size_t count = contours[i].size();
		if (count < 300) //小于300个点的轮廓略去
			continue;

		// 凸包缺陷迭代器设置
		vector<Vec4i>::iterator iterDefects = hullDefect[i].begin();
		// 遍历得到凸包缺陷的4个特征量并进行绘制
		while (iterDefects != hullDefect[i].end()) 
		{
			Vec4i& v = (*iterDefects);
			// 起始位置
			int startidx = v[0];
			Point ptStart(contours[i][startidx]);
			// 终止位置
			int endidx = v[1];
			Point ptEnd(contours[i][endidx]);
			// 内凸壳的最远的点缺陷
			int faridx = v[2];
			Point ptFar(contours[i][faridx]);
			// 凸包之间的最远点
			int depth = v[3] / 256;
			if (depth > 10 && depth < 100)
			{
				line(convex_hull_img, ptStart, ptFar, CV_RGB(0, 255, 0), 2);
				line(convex_hull_img, ptEnd, ptFar, CV_RGB(0, 255, 0), 2);
				circle(convex_hull_img, ptStart, 4, Scalar(255, 0, 0), 2);//ptStart用蓝色
				circle(convex_hull_img, ptEnd, 4, Scalar(255, 0, 128), 2);//ptEnd用紫色
				circle(convex_hull_img, ptFar, 4, Scalar(128, 0, 255), 2);//ptFar用粉红色
			}
			iterDefects++;
		}


	}
	imshow("convex_hull_img", convex_hull_img);


	
	cv::waitKey();
	return 0;
}

运行结果如下:

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值