C++/Qt:最小外接矩形

以凸包的某条边作为矩形的一条边,求包围所有点的面积最小外接矩形。(感觉是对的)下图中蓝色为凸包,红色为MBR。

           

 

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

一、求凸包,返回 xjListCH

参考:凸包

二、MBR中用到的函数

//返回 点与点的平面距离
double TwoDistancePointAndPoint(const xjPoint &p1, const xjPoint &p2)
{
	double x1 = p1.x, y1 = p1.y;
	double x2 = p2.x, y2 = p2.y;

	double dis = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));

	return dis;
}

//返回 点(p0)  到线(p1,p2)的距离
double TwoDistancePointAndLine(const xjPoint &p0, const xjPoint &p1,const xjPoint &p2)
{
	double dis12 = TwoDistancePointAndPoint(p1, p2);//线段长度
	double dis01 = TwoDistancePointAndPoint(p0, p1);//p1与p0的距离
	double dis02 = TwoDistancePointAndPoint(p0, p2);//p2与p0的距离
	double HalfC = (dis12 + dis01 + dis02) / 2;// 半周长
	double s = sqrt(HalfC * (HalfC - dis12) * (HalfC - dis01) * (HalfC - dis02));//海伦公式求面积
	double xj2DisPL = 2 * s / dis12;// 返回点到线的距离(利用三角形面积公式求高)

	return xj2DisPL;
}

三、最小外接矩形

/*返回 最小外接矩形点*/
/* xjListCH 凸包点 */
void xjMinimumBoundingRectangle(QList<xjPoint> &xjListMBRpoint, double &length, double &width, QList<xjPoint> xjListCH)
{
	//外接矩形
	QList<xjPoint> xjListCH2;
	int xjPcount = xjListCH.size();
	for (int i = 0; i < xjPcount; i++)
	{
		xjListCH2.append(xjListCH.at(i));
	}
	qSort(xjListCH2.begin(), xjListCH2.end(), [](const xjPoint &a, const xjPoint &b) {return a.x < b.x; });
	double minX = xjListCH2.at(0).x;
	double maxX = xjListCH2.at(xjPcount - 1).x;
	qSort(xjListCH2.begin(), xjListCH2.end(), [](const xjPoint &a, const xjPoint &b) {return a.y < b.y; });
	double minY = xjListCH2.at(0).y;
	double maxY = xjListCH2.at(xjPcount - 1).y;

	//依次判断
	double minArea = 99999999;
	xjListCH.push_back(xjListCH.at(0));
	for (int a = 0; a < xjListCH.size() - 1; a++)
	{
		xjPoint p0 = xjListCH.at(a);
		xjPoint p1 = xjListCH.at(a + 1);

		if ((p0.y == p1.y)|| (p0.x == p1.x)) // 水平或垂直
		{
			double side1 = maxY - minY;
			double side0 = maxX - minX;
			double xjArea = side0 * side1;

			if (xjArea <= minArea)
			{
				length = max(side0, side1);
				width = min(side0, side1);
				minArea = xjArea;

				//外接矩形四个点
				xjPoint pLB;
				pLB.x = minX;
				pLB.y = minY;
				pLB.z = 0;
				xjPoint pRB;
				pRB.x = maxX;
				pRB.y = minY;
				pRB.z = 0;
				xjPoint pRT;
				pRT.x = maxX;
				pRT.y = maxY;
				pRT.z = 0;
				xjPoint pLT;
				pLT.x = minX;
				pLT.y = maxY;
				pLT.z = 0;

				xjListMBRpoint.clear();
				xjListMBRpoint.append(pLB);
				xjListMBRpoint.append(pRB);
				xjListMBRpoint.append(pRT);
				xjListMBRpoint.append(pLT);
			}
		}
		else //不水平 不垂直
		{
			double k1 = (p1.y - p0.y) / (p1.x - p0.x);
			double b1 = p0.y - k1 * p0.x;
			double side0 = -3;
			xjPoint Pside0;
			for (int j = 0; j < xjListCH.size(); j++)
			{
				if ((j == a) || (j == (a + 1)))
					continue;

				xjPoint p = xjListCH.at(j);
				double dis = abs(TwoDistancePointAndLine(p, p0, p1));
				if (dis >= side0)
				{
					side0 = dis;
					Pside0.x = p.x;
					Pside0.y = p.y;
					Pside0.z = p.z;
				}
			}
			double b11 = Pside0.y - k1 * Pside0.x;

			//垂直方向
			double k2 = -1.0 / k1;
			double bb = p0.y - k2 * p0.x;
			double side1_positive = -3;
			xjPoint Pside1_positive;
			double side1_negative = 9999999;
			xjPoint Pside1_negative;
			for (int j = 0; j < xjListCH.size(); j++)
			{
				xjPoint p = xjListCH.at(j);
				double dis = (k2*p.x - p.y + bb) / (sqrt(k2*k2 + 1));
				if ((dis>=0)&&(dis >= side1_positive))
				{
					side1_positive = dis;
					Pside1_positive.x = p.x;
					Pside1_positive.y = p.y;
					Pside1_positive.z = p.z;
				}
				if ((dis<0)&&(dis <= side1_negative))
				{
					side1_negative = dis;
					Pside1_negative.x = p.x;
					Pside1_negative.y = p.y;
					Pside1_negative.z = p.z;
				}
			}

			double b2 = Pside1_positive.y - k2 * Pside1_positive.x;
			double b22 = Pside1_negative.y - k2 * Pside1_negative.x;

			//面积和周长
			double side1 = abs(side1_positive)+abs(side1_negative);
			double xjArea = side0 * side1;
			
			if (xjArea <= minArea)
			{
				length = max(side0, side1);
				width = min(side0, side1);
				minArea = xjArea;

				//外接矩形四个点
				xjPoint br0;
				br0.x = (b1 - b22) / (k2 - k1);
				br0.y = k1 * br0.x + b1;
				xjPoint br1;
				br1.x = (b11-b22) / (k2-k1);
				br1.y =  k1* br1.x + b11;
				xjPoint br2;
				br2.x = (b2-b11) / (k1-k2);
				br2.y =  k1* br2.x + b11;
				xjPoint br3;
				br3.x = (b2-b1) / (k1-k2);
				br3.y = k1 * br3.x + b1;

				xjListMBRpoint.clear();
				xjListMBRpoint.append(br0);
				xjListMBRpoint.append(br1);
				xjListMBRpoint.append(br2);
				xjListMBRpoint.append(br3);
			}
		}
	}

	//MBR提示信息
	QString MBRinfo = "chMBR: length = " + QString::number(length, 'f', 4) + ", ";
	MBRinfo += "width = " + QString::number(width, 'f', 4) + ", ";
	MBRinfo += "minimum area = " + QString::number(minArea) + ", ";
	MBRinfo += "circumference = " + QString::number((length + width) * 2);
}

 

  • 5
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
以下是一个C++示例代码,使用OpenCV库来绘制连通域的矩形和最小矩形: ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 读取图像 Mat image = imread("image.jpg", IMREAD_GRAYSCALE); // 进行二值化处理 Mat binary; threshold(image, binary, 0, 255, THRESH_BINARY | THRESH_OTSU); // 查找连通域 Mat labels, stats, centroids; int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids); // 绘制连通域矩形和最小矩形 for (int i = 1; i < num_labels; ++i) { // 获取连通域的矩形 Rect bounding_rect(stats.at<int>(i, CC_STAT_LEFT), stats.at<int>(i, CC_STAT_TOP), stats.at<int>(i, CC_STAT_WIDTH), stats.at<int>(i, CC_STAT_HEIGHT)); // 获取连通域的最小矩形 RotatedRect rotated_rect = minAreaRect(Mat(binary, bounding_rect)); // 绘制连通域矩形 rectangle(image, bounding_rect, Scalar(0, 255, 0), 2); // 获取最小矩形的四个顶点 Point2f vertices[4]; rotated_rect.points(vertices); // 绘制最小矩形 for (int j = 0; j < 4; ++j) line(image, vertices[j], vertices[(j + 1) % 4], Scalar(0, 0, 255), 2); } // 显示结果 imshow("Image", image); waitKey(0); return 0; } ``` 在这个示例代码中,我们首先读取一张图像并进行二值化处理。然后使用`connectedComponentsWithStats`函数查找连通域,并获取每个连通域的矩形和最小矩形。最后,使用`rectangle`和`line`函数绘制连通域的矩形和最小矩形,并显示结果图像。请确保在运行代码之前安装OpenCV库并将图像路径替换为你自己的图像路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

累了就要打游戏

把我养胖,搞代码

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

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

打赏作者

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

抵扣说明:

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

余额充值