C++/Qt:凸包

12 篇文章 1 订阅

 

 分享给有需要的人,代码质量勿喷。

首先是需要用到的结构体和函数

//自定义结构体
struct xjPoint
{
	double x;
	double y;
	double z;
};


/*返回 两点的平面距离*/
double xjGet2DistancePP(xjPoint p1, xjPoint p2)
{
	return (sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)));
}


/*返回 两个向量的叉积(p0是公共点)*/
double xjGetCrossProduct(xjPoint p1, xjPoint p2, xjPoint p0)
{
	return ((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y));
}

---------------第一种写法------------ 

/*方式一、返回 凸包点集,通过 Graham Scan 方法*/
QList<xjPoint> xjGetConvexHullByGrahamScan(QList<xjPoint> listPoint)
{
	QList<xjPoint> xjListResult;

	int i = 0, j = 0, k = 0;
	xjPoint tmP = listPoint[0];

	//排序 找到最下且偏左的点
	for (i = 1; i < listPoint.size(); i++)
	{
		if ((listPoint[i].y < tmP.y) || ((listPoint[i].y == tmP.y) && (listPoint[i].x < tmP.x)))
		{
			tmP = listPoint[i];
			k = i;
		}
	}
	listPoint[k] = listPoint[0];
	listPoint[0] = tmP;

	//排序 按极角从小到大,距离从近到远 
	for (i = 1; i < listPoint.size(); i++)
	{
		k = i;
		for (j = i + 1; j < listPoint.size(); j++)
		{
			double cross = xjGetCrossProduct(listPoint[j], listPoint[k], listPoint[0]);
			double disance_j = xjGet2DistancePP(listPoint[0], listPoint[j]);
			double disance_k = xjGet2DistancePP(listPoint[0], listPoint[k]);
			if ((cross > 0) || ((cross == 0) && (disance_j < disance_k)))
			{
				k = j;//k保存极角最小的那个点,或者相同距离原点最近
			}
		}
		tmP = listPoint[i];
		listPoint[i] = listPoint[k];
		listPoint[k] = tmP;
	}

	//添加第一、二个凸包点
	xjListResult.append(listPoint[0]);
	xjListResult.append(listPoint[1]);

	遍历
	for (i = 2; i < listPoint.size(); ++i)
	{
		for (j = i + 1; j < listPoint.size(); ++j)
		{
			if (xjGetCrossProduct(listPoint[i], listPoint[j], xjListResult[xjListResult.size() - 1]) < 0)
			{
				i = j;
			}
		}
		xjListResult.append(listPoint[i]);
	}

	return xjListResult;
}

-----------第二种写法-----------

 /*方式二:返回 凸包点集,通过 Graham Scan 方法*/
 QList<xjPoint> xjGetConvexHull(QList<xjPoint> list)
 {
	 QList<xjPoint> newList;

	 /*最下偏左的点 肯定是凸包点*/
	 qSort(list.begin(), list.end(), [](const xjPoint &a, const xjPoint &b)
	 {
		 if (a.y == b.y)
			 return a.x < b.x;
		 else
			 return a.y < b.y;
	 });
	 xjPoint p0 = list.at(0);

	 //临时点集 坐标偏移
	 QList<xjPoint> tempList;
	 for (int i = 0; i < list.size(); i++)
	 {
		 xjPoint p;
		 p.x = list.at(i).x - p0.x;
		 p.y = list.at(i).y - p0.y;
		 p.z = list.at(i).z - p0.z;
		 tempList.append(p);
	 }
	 newList.append(tempList.at(0));

	 //按极角从小到大,距离偏短进行排序
	 qSort(tempList.begin(), tempList.end(), [](const xjPoint &a, const xjPoint &b)
	 {
		 if (atan2(a.y, a.x) != atan2(b.y, b.x))
			 return (atan2(a.y, a.x)) < (atan2(b.y, b.x));
		 return a.x < b.x;
	 });
	 xjPoint p1 = tempList.at(1);
	 newList.append(p1);

	 //获取凸包点
	 for (int j = 2; j < tempList.size(); j++)
	 {
		 for (int k = j + 1; k < tempList.size(); k++)
		 {
			 double xjCross = xjGetCrossProduct(tempList.at(j), tempList.at(k), newList.at(newList.size() - 1));
			 if (xjCross < 0)
			 {
				 j = k;
			 }
		 }
		 newList.append(tempList.at(j));
	 }


	 //回复坐标
	 for (int i = 0; i < newList.size(); i++)
	 {
		 xjPoint p = newList.at(i);
		 p.x += p0.x;
		 p.y += p0.y;
		 p.z += p0.z;
		 newList.replace(i, p);
	 }

	 return newList;
 }

-----------另外,通过 OpenCV检测凸包-----------

// 1 原始点:(0,0)-(10,10)-(5,5)-(0,10)-(10,0)
std::vector<cv::Point> vPoint;
for (size_t i = 0; i < 5; i++)
{
    cv::Point p0;
    p0.x = 0;
    p0.y = 0;

    cv::Point p1;
    p1.x = 10;
    p1.y = 10;

    cv::Point p2;
    p2.x = 5;
    p2.y = 5;

    cv::Point p3;
    p3.x = 0;
    p3.y = 10;

    cv::Point p4;
    p4.x = 10;
    p4.y = 0;

    vPoint.push_back(p0);
    vPoint.push_back(p1);
    vPoint.push_back(p2);
    vPoint.push_back(p3);
    vPoint.push_back(p4);
}

// 2 凸包
std::vector<cv::Point> vCH;//定义凸包顶点集
cv::convexHull(vPoint, vCH, false, true);//构建该轮廓的凸包

// 3 结果:(10,10)-(0,10)-(0,0)-(10,0)
for (size_t j = 0; j < vCH.size(); j++)
{
    qDebug() << vCH[j].x << "_____" << vCH[j].y;
}

 

  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

累了就要打游戏

把我养胖,搞代码

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值