C++ 计算凸包点的最小旋转矩形

news2024/12/21 17:50:42

RotateRect.h


#include <vector>

/**
* @brief 计算点集最小旋转外接矩形
*/
class RotateRect {
public:
	enum { CALIPERS_MAXHEIGHT = 0, CALIPERS_MINAREARECT = 1, CALIPERS_MAXDIST = 2 };
	struct Point {
		float x, y;
	};
	using Points = std::vector<Point>;
	struct Sizef {
		float width, height;
	};
	struct Rect {
		float left, top, right, bottom;
	};
	explicit RotateRect(const Points& pts);

	const Point& center() const;
	const Sizef& size() const;
	float angle() const;
public:
	void update();
	Points toPoints() const;
	Rect getOutLine() const;

private:
	double crossProduct(const Point& A, const Point& B, const Point& C) const;
	double calculateDistance(const Point& A, const Point& B) const;
	Points convexHull() const;
	/* we will use usual cartesian coordinates */
	void rotatingCalipers(const Point* pts, int n, int mode, float* out) const;

private:
	const Points& m_inputs;
	Point m_center;
	//! returns width and height of the rectangle
	Sizef m_size;
	//! returns the rotation angle. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle.
	float m_angle;
};

 RotateRect.cpp


#include "RotateRect.h"
#include <cmath>
#include <assert.h>

#ifndef CV_PI
#define CV_PI   3.1415926535897932384626433832795
#endif

RotateRect::RotateRect(const RotateRect::Points& pts) : m_inputs(pts), m_angle(0), m_size{ 0, 0 }, m_center{ 0, 0 } {}

/*
 * @brief 计算BA与CA之间面积
 * @param [in] A 点
 * @param [in] B 点
 * @param [in] C 点
 * @return
	BA x CA > 0, C在B的逆时针方向,B在C右边
	BA x CA < 0, C在B的顺时针方向,B在C左边
	BA x CA = 0, C和B共线,可能正方向,也可能反方向
 */
double RotateRect::crossProduct(const RotateRect::Point& A, const RotateRect::Point& B, const RotateRect::Point& C) const {
	return (B.x - A.x) * (C.y - A.y) - (C.x - A.x) * (B.y - A.y);
}


/*
 * @brief 找到x方向最小点为起始点,查找所有点最右边点,到起始点结束
 * @return 所有凸包点,逆时针方向
 */
RotateRect::Points RotateRect::convexHull() const {
	const auto& points = m_inputs;
	int n = points.size();
	if (n <= 3) {
		return points;
	}

	std::vector<Point> hull;
	int l = 0;
	for (int i = 1; i < n; i++) {
		if (points[i].x < points[l].x) {
			l = i;
		}
	}

	int p = l, q;
	do {
		hull.push_back(points[p]);
		q = (p + 1) % n;
		for (int i = 0; i < n; i++) {
			if (crossProduct(points[p], points[i], points[q]) > 0) {
				q = i;
			}
		}
		p = q;
	} while (p != l);

	return hull;
}

double RotateRect::calculateDistance(const RotateRect::Point& A, const RotateRect::Point& B) const {
	return std::sqrt(std::pow(B.x - A.x, 2) + std::pow(B.y - A.y, 2));
}

void rotate90CCW(const RotateRect::Point& in, RotateRect::Point& out)
{
	out.x = -in.y;
	out.y = in.x;
}

void rotate90CW(const RotateRect::Point& in, RotateRect::Point& out)
{
	out.x = in.y;
	out.y = -in.x;
}

void rotate180(const RotateRect::Point& in, RotateRect::Point& out)
{
	out.x = -in.x;
	out.y = -in.y;
}

/* return true if first vector is to the right (clockwise) of the second */
bool firstVecIsRight(const RotateRect::Point& vec1, const RotateRect::Point& vec2)
{
	RotateRect::Point tmp;
	rotate90CW(vec1, tmp);
	return tmp.x * vec2.x + tmp.y * vec2.y < 0;
}

/*
 * @brief 计算凸包点的最小旋转矩形
 */
 /* we will use usual cartesian coordinates */
void RotateRect::rotatingCalipers(const RotateRect::Point* points, int n, int mode, float* out) const
{
	using Point = RotateRect::Point;
	float minarea = FLT_MAX;
	float max_dist = 0;
	char buffer[32] = {};
	int i, k;
	std::vector<float> abuf(n * 3);
	float* inv_vect_length = abuf.data();
	Point* vect = (Point*)(inv_vect_length + n);
	int left = 0, bottom = 0, right = 0, top = 0;
	int seq[4] = { -1, -1, -1, -1 };
	Point rot_vect[4];

	/* rotating calipers sides will always have coordinates
	 (a,b) (-b,a) (-a,-b) (b, -a)
	 */
	 /* this is a first base vector (a,b) initialized by (1,0) */
	float orientation = 0;
	float base_a;
	float base_b = 0;

	float left_x, right_x, top_y, bottom_y;
	Point pt0 = points[0];

	left_x = right_x = pt0.x;
	top_y = bottom_y = pt0.y;

	for (i = 0; i < n; i++)
	{
		double dx, dy;

		if (pt0.x < left_x)
			left_x = pt0.x, left = i;

		if (pt0.x > right_x)
			right_x = pt0.x, right = i;

		if (pt0.y > top_y)
			top_y = pt0.y, top = i;

		if (pt0.y < bottom_y)
			bottom_y = pt0.y, bottom = i;

		Point pt = points[(i + 1) & (i + 1 < n ? -1 : 0)];

		dx = pt.x - pt0.x;
		dy = pt.y - pt0.y;

		vect[i].x = (float)dx;
		vect[i].y = (float)dy;
		inv_vect_length[i] = (float)(1. / std::sqrt(dx * dx + dy * dy));

		pt0 = pt;
	}

	// find convex hull orientation
	{
		double ax = vect[n - 1].x;
		double ay = vect[n - 1].y;

		for (i = 0; i < n; i++)
		{
			double bx = vect[i].x;
			double by = vect[i].y;

			double convexity = ax * by - ay * bx;

			if (convexity != 0)
			{
				orientation = (convexity > 0) ? 1.f : (-1.f);
				break;
			}
			ax = bx;
			ay = by;
		}
		assert(orientation != 0);
	}
	base_a = orientation;

	/*****************************************************************************************/
	/*                         init calipers position                                        */
	seq[0] = bottom;
	seq[1] = right;
	seq[2] = top;
	seq[3] = left;
	/*****************************************************************************************/
	/*                         Main loop - evaluate angles and rotate calipers               */

	/* all of edges will be checked while rotating calipers by 90 degrees */
	for (k = 0; k < n; k++)
	{
		/* number of calipers edges, that has minimal angle with edge */
		int main_element = 0;

		/* choose minimum angle between calipers side and polygon edge by dot product sign */
		rot_vect[0] = vect[seq[0]];
		rotate90CW(vect[seq[1]], rot_vect[1]);
		rotate180(vect[seq[2]], rot_vect[2]);
		rotate90CCW(vect[seq[3]], rot_vect[3]);
		for (i = 1; i < 4; i++)
		{
			if (firstVecIsRight(rot_vect[i], rot_vect[main_element]))
				main_element = i;
		}

		/*rotate calipers*/
		{
			//get next base
			int pindex = seq[main_element];
			float lead_x = vect[pindex].x * inv_vect_length[pindex];
			float lead_y = vect[pindex].y * inv_vect_length[pindex];
			switch (main_element)
			{
			case 0:
				base_a = lead_x;
				base_b = lead_y;
				break;
			case 1:
				base_a = lead_y;
				base_b = -lead_x;
				break;
			case 2:
				base_a = -lead_x;
				base_b = -lead_y;
				break;
			case 3:
				base_a = -lead_y;
				base_b = lead_x;
				break;
			default:
				assert("main_element should be 0, 1, 2 or 3" && false);
			}
		}
		/* change base point of main edge */
		seq[main_element] += 1;
		seq[main_element] = (seq[main_element] == n) ? 0 : seq[main_element];

		switch (mode)
		{
		case RotateRect::CALIPERS_MAXHEIGHT:
		{
			/* now main element lies on edge aligned to calipers side */

			/* find opposite element i.e. transform  */
			/* 0->2, 1->3, 2->0, 3->1                */
			int opposite_el = main_element ^ 2;

			float dx = points[seq[opposite_el]].x - points[seq[main_element]].x;
			float dy = points[seq[opposite_el]].y - points[seq[main_element]].y;
			float dist;

			if (main_element & 1)
				dist = (float)fabs(dx * base_a + dy * base_b);
			else
				dist = (float)fabs(dx * (-base_b) + dy * base_a);

			if (dist > max_dist)
				max_dist = dist;
		}
		break;
		case RotateRect::CALIPERS_MINAREARECT:
			/* find area of rectangle */
		{
			float height;
			float area;

			/* find vector left-right */
			float dx = points[seq[1]].x - points[seq[3]].x;
			float dy = points[seq[1]].y - points[seq[3]].y;

			/* dotproduct */
			float width = dx * base_a + dy * base_b;

			/* find vector left-right */
			dx = points[seq[2]].x - points[seq[0]].x;
			dy = points[seq[2]].y - points[seq[0]].y;

			/* dotproduct */
			height = -dx * base_b + dy * base_a;

			area = width * height;
			if (area <= minarea)
			{
				float* buf = (float*)buffer;

				minarea = area;
				/* leftist point */
				((int*)buf)[0] = seq[3];
				buf[1] = base_a;
				buf[2] = width;
				buf[3] = base_b;
				buf[4] = height;
				/* bottom point */
				((int*)buf)[5] = seq[0];
				buf[6] = area;
			}
		}
		break;
		}                       /*switch */
	}                           /* for */

	switch (mode)
	{
	case RotateRect::CALIPERS_MINAREARECT:
	{
		float* buf = (float*)buffer;

		float A1 = buf[1];
		float B1 = buf[3];

		float A2 = -buf[3];
		float B2 = buf[1];

		float C1 = A1 * points[((int*)buf)[0]].x + points[((int*)buf)[0]].y * B1;
		float C2 = A2 * points[((int*)buf)[5]].x + points[((int*)buf)[5]].y * B2;

		float idet = 1.f / (A1 * B2 - A2 * B1);

		float px = (C1 * B2 - C2 * B1) * idet;
		float py = (A1 * C2 - A2 * C1) * idet;

		out[0] = px;
		out[1] = py;

		out[2] = A1 * buf[2];
		out[3] = B1 * buf[2];

		out[4] = A2 * buf[4];
		out[5] = B2 * buf[4];
	}
	break;
	case RotateRect::CALIPERS_MAXHEIGHT:
	{
		out[0] = max_dist;
	}
	break;
	}
}

void RotateRect::update()
{
	using Point = RotateRect::Point;
	std::vector<Point> hull;
	Point out[3];
	hull = convexHull();

	int n = hull.size();
	const Point* hpoints = &hull[0];

	if (n > 2)
	{
		rotatingCalipers(hpoints, n, RotateRect::CALIPERS_MINAREARECT, (float*)out);
		m_center.x = out[0].x + (out[1].x + out[2].x) * 0.5f;
		m_center.y = out[0].y + (out[1].y + out[2].y) * 0.5f;
		m_size.width = (float)std::sqrt((double)out[1].x * out[1].x + (double)out[1].y * out[1].y);
		m_size.height = (float)std::sqrt((double)out[2].x * out[2].x + (double)out[2].y * out[2].y);
		m_angle = (float)atan2((double)out[1].y, (double)out[1].x);
	}
	else if (n == 2)
	{
		m_center.x = (hpoints[0].x + hpoints[1].x) * 0.5f;
		m_center.y = (hpoints[0].y + hpoints[1].y) * 0.5f;
		double dx = hpoints[1].x - hpoints[0].x;
		double dy = hpoints[1].y - hpoints[0].y;
		m_size.width = (float)std::sqrt(dx * dx + dy * dy);
		m_size.height = 0;
		m_angle = (float)atan2(dy, dx);
	}
	else
	{
		if (n == 1)
			m_center = hpoints[0];
	}

	m_angle = (float)(m_angle * 180 / CV_PI);
}


RotateRect::Points RotateRect::toPoints() const
{
	RotateRect::Points pt(4);

	double _angle = m_angle * CV_PI / 180.;
	float b = (float)cos(_angle) * 0.5f;
	float a = (float)sin(_angle) * 0.5f;

	pt[0].x = m_center.x - a * m_size.height - b * m_size.width;
	pt[0].y = m_center.y + b * m_size.height - a * m_size.width;
	pt[1].x = m_center.x + a * m_size.height - b * m_size.width;
	pt[1].y = m_center.y - b * m_size.height - a * m_size.width;
	pt[2].x = 2 * m_center.x - pt[0].x;
	pt[2].y = 2 * m_center.y - pt[0].y;
	pt[3].x = 2 * m_center.x - pt[1].x;
	pt[3].y = 2 * m_center.y - pt[1].y;
	return pt;
}

const RotateRect::Point& RotateRect::center() const {
	return m_center;
}

const RotateRect::Sizef& RotateRect::size() const {
	return m_size;
}

float RotateRect::angle() const {
	return m_angle;
}


RotateRect::Rect RotateRect::getOutLine() const {
	if (m_inputs.empty())
		return { 0, 0, 0, 0 };
	using Number = std::numeric_limits<float>;
	RotateRect::Rect rect{ Number::max(), Number::max(), Number::min(), Number::min() };
	for (const auto& point : m_inputs) {
		if (point.x < rect.left)
			rect.left = point.x;
		if (point.x > rect.right)
			rect.right = point.x;

		if (point.y < rect.top)
			rect.top = point.y;
		if (point.y > rect.bottom)
			rect.bottom = point.y;
	}
	return rect;
}

main.cpp


RotateRect::Points myPoints = { {233, 86}, {322, 106}, {214, 154}, {307, 176}, {286, 209}, {331, 183}, {346, 319}, {392, 294}, {356, 346}, {419, 311}, {419, 311}, {778, 1031}, {840, 995} };
    RotateRect myRotateRect(myPoints);
    myRotateRect.update();

 


创作不易,小小的支持一下吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1843940.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASP .Net Core创建一个httppost请求并添加证书

ASP .Net Core创建一个httppost请求并添加证书 创建.net Core程序&#xff0c;使用自签名证书&#xff0c;可以处理https的get和post请求。 创建证书 创建自签名证书的流程可以在这里查看&#xff1a; https://blog.csdn.net/GoodCooking/article/details/139815278创建完毕…

【Python】一文向您详细解析内置装饰器 @lru_cache

【Python】一文向您详细解析内置装饰器 lru_cache 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&a…

鸿蒙开发通信与连接:【@ohos.nfc.tag (标准NFC-Tag)】

标准NFC-Tag 本模块主要用于操作及管理NFC Tag。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import tag from ohos.nfc.tag;tag.getNfcATag getNfcATag(tagInfo: TagInfo): Nf…

劲爆!Kimi月之暗面可以接入微信,创建自己个性化的AI Agent(人工智能体) (一)

前言 随着人工智能技术的不断发展&#xff0c;越来越多的企业和个人开始关注如何利用AI技术提升用户体验和交互效果。本文将介绍如何使用抖音的扣子&#xff08;Douyin Button&#xff09;配置自己的AI Agent&#xff0c;搭载kimi, 实现智能互动&#xff0c;提升用户体验。 k…

【Python机器学习实战】 | 基于PCA主成分分析技术读入空气质量监测数据进行数据预处理并计算空气质量综合评测结果

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

LeetCode 1164, 125, 94

目录 1164. 指定日期的产品价格题目链接表要求知识点思路代码 125. 验证回文串题目链接标签简单版思路代码 复杂版思路代码 94. 二叉树的中序遍历题目链接标签递归思路代码 迭代思路代码 1164. 指定日期的产品价格 题目链接 1164. 指定日期的产品价格 表 表Products的字段为…

LVGL使用GUI Guider配置STM32界面详细笔记教程

0、说明 接着前面几篇博客对LVGL的使用和介绍&#xff0c;这篇博客主要是使用和介绍快速配置LVGL图形界面编程的工具&#xff0c;GUI Guider。本文使用的工程代码&#xff0c;均是基于前几篇博客的基础上的&#xff0c;如需下载已配置好的LVGL-MCU工程环境&#xff0c;可通过如…

docker启动nacos挂载目录并修改配置文件

1 通过 Docker 拉取 nacos 镜像 docker pull nacos/nacos-server:v2.1.22 创建宿主机挂载目录 # 用于挂载 nacos 的日志 mkdir -p /mydata/nacos_home/logs/ # 用于挂载 nacos 的配置 mkdir -p /mydata/nacos_home/conf/ # 用于挂载 nacos 的数据 mkdir -p /mydata/nacos_hom…

接口联调测试工作总结

接口联调测试工作已经告一段落&#xff0c;现在总结如下: 1、首先接口联调测试的价值 2、接口联调要有工作思路 3、接口联调工作准备 4、接口联调测试数据设计 5、接口联调脚本研发 6、脚本联调测试 测试业务本身需要接口联调调用 独立接口正确&#xff0c;但有可能接口…

三甲硅烷基胺(TSA)可用作半导体前驱体材料 南大光电为我国代表企业

三甲硅烷基胺&#xff08;TSA&#xff09;可用作半导体前驱体材料 南大光电为我国代表企业 三甲硅烷基胺&#xff08;TSA&#xff09;是一种无机化合物&#xff0c;分子式为&#xff08;SiH3&#xff09;3N&#xff0c;其外观呈无色流动性液体。三甲硅烷基胺具有易水解、自燃性…

Java面向对象的三大特性之一——继承

目录 一、继承概念 二、为什么要继承 三、继承语法&#xff08;关键字extends&#xff09; 四、父类成员访问 1、子类中访问父类的成员变量 &#xff08;1&#xff09;子类和父类不存在同名的成员变量 &#xff08;2&#xff09;子类和父类中存在同名的成员变量 2、子类中访…

CVPR 2024揭幕,清华大学论文接收量霸榜,轻松碾压斯坦福、麻省理工

CVPR2024 会议之眼 快讯 会议介绍 2024 年 CVPR &#xff08;Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议&#xff0c;于6月17日至21日正在美国西雅图召开。CVPR是计算机视觉和模式识别领域的顶级会议之一。与ICCV和ECCV并称为计算…

Vue中使用ElementUI组件Form组件的校验validate

先准备一些el-form元素 这里面el-form中:model(v-bind:model)是单项绑定的&#xff0c;如果你写成了v-model""可能会出现校验没有效果的情况。 这是校验过后的结果了 现在开始使用下吧&#xff01; 1.在el-form中绑定一个ref&#xff0c;名字自拟,后续触发检验结果…

ASM字节码插桩技术初探

一、ASM简介 ASM(全称&#xff1a;ASMifier Class Visitor)是一个java字节码操纵框架&#xff0c;ASM 提供了许多 API 和工具,可以直接以二进制形式读取和修改类文件、动态生成类或者增强既有类的功能。 1、 ASM 主要作用 asm用于生成、编辑、分析java的class…

仿真模拟--telnet服务两种认证模式(自作)

自己做的笔记,有问题或看不懂请见解一下~ 目录 两个路由器间实现telnet服务(password认证模式) server client 两个路由器间实现telnet服务(aaa认证模式) server client 改名 tab键补齐 不会就扣问号 ? save 两个路由器间实现telnet服务…

【车载开发系列】CAN通信总线再理解(中篇)

【车载开发系列】CAN通信总线再理解&#xff08;中篇&#xff09; 九. CAN总线标准十. CAN物理层十一. CAN数据链路层1&#xff09;CAN的通信帧类型2&#xff09;CAN的标准帧格式1. CAN ID2. 数据场 3&#xff09;CAN总线仲裁 十二. CAN应用层1&#xff09;CANopen2&#xff09…

蚓链数字化营销系统,开启企业无限可能!

蚓链数字化营销系统带给企业的不仅仅是卖货方案&#xff0c;其重要的是让企业具备拥有融合资源的能力、实现在更多业态跨界赚钱的能力、及打造品牌价值的能力。 在当今数字化时代&#xff0c;蚓链数字化营销系统正为企业带来新的变革与机遇。它所赋予企业的&#xff0c;绝非仅仅…

[Vulnhub] Sleepy JDWP+Tomcat+Reverse+Reverse-enginnering

信息收集 Server IP AddressPorts Opening192.168.8.100TCP:21,8009,9001 $ nmap -sV -sC 192.168.8.100 -p- --min-rate 1000 -Pn Starting Nmap 7.92 ( https://nmap.org ) at 2024-06-20 05:06 EDT Nmap scan report for 192.168.8.100 (192.168.8.100) Host is up (0.00…

Python学习打卡:day11

day11 笔记来源于&#xff1a;黑马程序员python教程&#xff0c;8天python从入门到精通&#xff0c;学python看这套就够了 目录 day1183、自定义 Python 包创建包导入包方式1方式2方式3方式4 84、安装第三方包安装第三方包——pippip的网络优化 安装第三方包——PyCharm 85、…

【DKN: Deep Knowledge-Aware Network for News Recommendation】

DKN: Deep Knowledge-Aware Network for News Recommendation 摘要 在线新闻推荐系统旨在解决新闻信息爆炸的问题&#xff0c;为用户进行个性化推荐。 总体而言&#xff0c;新闻语言高度凝练&#xff0c;充满知识实体和常识。 然而&#xff0c;现有的方法并没有意识到这些外部…