二值图像分析—凸包检测

凸包概念

在一个多变形边缘或者内部任意两个点的连线都包含在多边形边界或者内部。 包含点集合S中所有点的最小凸多边形称为凸包(Convex Hull)。

凸包(Convex Hull)是一个计算几何(图形学)中常见的概念。数学定义:在一个向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包

给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形。在图像处理过程中,我们常常需要寻找图像中包围某个物体的凸包。凸包跟多边形逼近很像,只不过它是包围物体最外层的一个凸集,这个凸集是所有能包围这个物体的凸集的交集。如下图所示:
二值图像分析—凸包检测_第1张图片
在上图中,绿色线条所包围的凸集即为白色图形的凸包。
二值图像分析—凸包检测_第2张图片

OpenCV中的API

在OpenCV中,通过函数convexHull够很容易的找到一系列的凸包,比如由点组成的轮廓。通过convexHull函数可以找到轮廓的凸包。

对形状凸包缺陷分析时使用convexityDefects()函数,每个缺陷区包含四个特征量:起始点,结束点,距离及最远点。

1.convexHull函数
  • 说明
    找到一系列的凸包的点集。

    函数使用Sklansky的算法Sklansky82找到一个2维点集的凸包。算法的空间代价是 O ( N l o g N ) O(N logN) O(NlogN)

  • 声明

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

    points Input 2D point set, stored in std::vector or Mat.
    hull 输出的凸包。它要么是指数的整数向量,要么是点的向量。在第一种情况下,包元素是原数组中凸包点的基于0的索引(因为凸包点集是原点集的子集)。在第二种情况下,包元本身就是凸包点。
    clockwise 定位标志。假设坐标系的X轴指向右边,Y轴指向上面。若为true,输出的凸包是顺时针的。否则,输出的凸包是逆时针的。
    returnPoints 操作标志。对于矩阵,当标记为真时,函数返回凸包点。或者,返回凸包点的指数。当输出数组是std::vector型时候,标志被忽略,并且输出取决于向量的类型。std::vector时returnPoints=false,std::vector时returnPoints=true。
2.convexityDefects函数
  • 说明
    查找轮廓的凸度缺陷。

    下图显示了手部轮廓的凸度缺陷:

二值图像分析—凸包检测_第3张图片

  • 声明
    void convexityDefects(
      InputArray contour, 
      InputArray convexhull, 
      OutputArray convexityDefects 
    );
    
  • 参数
    contour 输入轮廓
    convexhull 利用凸包得到的凸包应该包含构成凸包的等高线点的指数。
    convexityDefects 凸性缺陷的输出向量。在c++和新的Python/Java接口中,每个凹凸性缺陷都表示为4个元素的整数向量(即#Vec4i):(start_index、end_index、farthest_pt_index、fixpt_depth),其中索引是基于0的索引。在凸性缺陷的原始轮廓中,起点、终点和最远点,fixpt_depth是最远点轮廓点到凸点距离的定点近似(8位小数)。也就是说,获取深度的浮点值将是fixpt_depth/256.0。

应用

首先需要对正在处理的图像进行二值化处理,找到轮廓,最后找到凸包。

void findConvexHull(Mat& src) {
	//1 图片预处理
	//1.1 图片转化成灰度图像
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//1.2 图像模糊消除噪音
	Mat blur_img;
	blur(gray, blur_img, Size(3, 3),Point(-1,-1));
	imshow("blur", blur_img);

	//1.3 图片转化成2值图像
	Mat binary;
	threshold(blur_img, binary, 127, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("binary", binary);


	//2 已将图像转换为二进制斑点。接下来,我们需要找到这些斑点的边界。
    //  查找轮廓 找到轮廓可以为我们提供每个斑点周围的边界点列表。
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	//mode 轮廓检索模式:RETR_TREE
	//method 轮廓逼近法:CHAIN_APPROX_SIMPLE
	findContours(binary, contours, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));
	Mat dst = Mat::zeros(binary.size(),CV_8UC3);
	for (size_t i = 0; i < contours.size(); i++)
	{
		drawContours(dst, contours, i, Scalar(255, 0, 0), 2);
	}
	imshow("dst", dst);

	//3 已经找到了轮廓,我们现在可以为每个轮廓找到凸包。这可以通过使用凸包函数来完成。
	vector<vector<Point>> hull(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
		convexHull(Mat(contours[i]), hull[i]);
	}
	//4 绘制凸包
	//凸包本身只是轮廓,因此我们可以使用OpenCV的drawContours。
	for (size_t i = 0; i < hull.size(); i++)
	{
		drawContours(dst, hull, i, Scalar(0, 0, 255), 2);
	}
	imshow("dst_final", dst);

}

int main() {
	Mat src = imread("D:/test/dot.png");
	if (src.empty()) {
		cout << " input the image error!" << endl;
	}
	imshow("src", src);
	findConvexHull(src);


	waitKey(0);
	return 0;
}

二值图像分析—凸包检测_第4张图片
二值图像分析—凸包检测_第5张图片
二值图像分析—凸包检测_第6张图片
二值图像分析—凸包检测_第7张图片
二值图像分析—凸包检测_第8张图片


二值图像分析—凸包检测_第9张图片
二值图像分析—凸包检测_第10张图片
二值图像分析—凸包检测_第11张图片

void findConvexHull(Mat& src) {
	//1 图片预处理
	//1.1 图片转化成灰度图像
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	imshow("gray", gray);

	//1.2 图像模糊消除噪音
	Mat blur_img;
	blur(gray, blur_img, Size(3, 3),Point(-1,-1));
	imshow("blur", blur_img);

	//1.3 图片转化成2值图像
	Mat binary;
	threshold(blur_img, binary, 127, 255, THRESH_BINARY | THRESH_OTSU);
	imshow("binary", binary);


	//2 已将图像转换为二进制斑点。接下来,我们需要找到这些斑点的边界。
    //  查找轮廓 找到轮廓可以为我们提供每个斑点周围的边界点列表。
	vector<vector<Point>> contours;
	vector<Vec4i> hierarchy;
	//mode 轮廓检索模式:RETR_TREE
	//method 轮廓逼近法:CHAIN_APPROX_SIMPLE
	findContours(binary, contours, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));
	Mat dst = Mat::zeros(binary.size(),CV_8UC3);
	for (size_t i = 0; i < contours.size(); i++)
	{
		drawContours(dst, contours, i, Scalar(255, 0, 0), 2);
	}
	imshow("dst", dst);

	//3 已经找到了轮廓,我们现在可以为每个轮廓找到凸包。这可以通过使用凸包函数来完成。
	vector<vector<Point>> hull(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]), hull[i]);
		//int类型凸包检测
		convexHull(Mat(contours[i]), inthull[i]);
		//凸包缺陷检测
		convexityDefects(Mat(contours[i]), inthull[i], hullDefect[i]);
	}
	//4 绘制凸包
	//凸包本身只是轮廓,因此我们可以使用OpenCV的drawContours。
	for (size_t i = 0; i < hull.size(); i++)
	{
		drawContours(dst, hull, i, Scalar(0, 0, 255), 2);
		//绘制缺陷
		size_t count = contours[i].size();
		if (count < 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 > 20 && depth < 80) {
				line(dst, ptStart, ptFar, Scalar(0, 255, 0), 2);
				line(dst, ptEnd, ptFar, Scalar(0, 255, 0), 2);
				circle(dst, ptStart, 4, Scalar(255, 0, 154), 2);
				circle(dst, ptEnd, 4, Scalar(255, 0, 154), 2);
				circle(dst, ptFar, 4, Scalar(100, 0, 255), 2);
			}
			iterDefects++;
		}
	}
	imshow("dst_final", dst);
}

int main() {
	//Mat src = imread("D:/test/huahua.png");
	//Mat src1 = imread("D:/test/s1.png");
	//Mat src2 = imread("D:/test/s3.png");

	Mat src = imread("D:/test/hand.png");
	if (src.empty()) {
		cout << " input the image error!" << endl;
	}
	imshow("src", src);
	
	findConvexHull(src);
	waitKey(0);

	return 0;

}

二值图像分析—凸包检测_第12张图片
二值图像分析—凸包检测_第13张图片
二值图像分析—凸包检测_第14张图片
二值图像分析—凸包检测_第15张图片

学习:

官方链接

Opencv3笔记22——寻找物体的凸包

opencv学习笔记(三十)凸包

凸包检测

你可能感兴趣的:(#,OpenCV图像处理,C++,OpenCV)