首页/新闻资讯/正文详情

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

发布时间:2026/11/5 10:16:51 来源:云帆数科 栏目:资讯中心
C++ 计算凸包点的最小旋转矩形
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::vectorPoint; 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::vectorPoint 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::vectorfloat 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::vectorPoint 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_limitsfloat; 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.cppRotateRect::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();创作不易小小的支持一下吧

相关推荐

接雨水-双指针42-pythonc++
接雨水-双指针42-pythonc++

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 5:47:50

通过数据库url,实现前端界面图片、文件回显
通过数据库url,实现前端界面图片、文件回显

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 10:41:39

springboot+vue+mysql实现前端上传图片到本地并将url存入本地数据库
springboot+vue+mysql实现前端上传图片到本地并将url存入本地数据库

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 10:41:39

机器学习数据标注中的认知偏差防控与工程实践
机器学习数据标注中的认知偏差防控与工程实践

1. 数据标注中的认知偏差问题剖析数据标注作为机器学习项目的基础环节,其质量直接影响模型性能。我在参与医疗影像标注项目时,曾遇到标注员将良性肿瘤误标为恶性肿瘤的情况,导致模型召回率虚高。这种系统性偏差往往源于人类认知的固有模式。认… · 2026/11/5 10:16:40

VLAN端口类型实验:Access与Trunk数据帧处理详解
VLAN端口类型实验:Access与Trunk数据帧处理详解

1. 实验背景与目标 作为一名网络工程师,我经常需要在实验室环境中验证各种网络协议和配置的实际效果。这次我决定深入研究VLAN端口类型对数据帧处理的影响,因为这是实际网络部署中最基础也最容易出错的部分。 VLAN(虚拟局域网)是… · 2026/11/5 10:16:40

VS Code+Docker搭建MySQL开发环境全指南
VS Code+Docker搭建MySQL开发环境全指南

1. 开发环境配置思路 在VS Code中通过Docker连接MySQL数据库,本质上是在本地搭建一个轻量级的开发环境。这种组合方案特别适合需要频繁切换项目或测试环境的开发者,既能保持本机环境干净,又能快速部署不同版本的数据库服务。 我推荐这个方案… · 2026/11/5 10:16:40

SSM电竞酒店管理系统架构设计与实践
SSM电竞酒店管理系统架构设计与实践

1. 项目背景与核心价值 电竞酒店作为近年来快速崛起的新型业态,完美融合了传统酒店住宿与专业电竞设备服务。我在实际考察中发现,目前市场上大多数电竞酒店仍在使用传统酒店管理系统或简单拼凑的解决方案,存在几个痛点:房间设备状… · 2026/11/5 10:15:39

PHPCMS v9.6.3整站模板:前后台一体化交付方案
PHPCMS v9.6.3整站模板:前后台一体化交付方案

简介:本资源是一套开箱即用的PHPCMS整站模板,面向PHP初学者、中小型网站开发者及高校信息化建设人员,解决PHPCMS二次开发中前后台界面设计与快速部署难题。压缩包共3043个文件,涵盖1415个PHP核心逻辑文件、379个JS交互脚本、375个… · 2026/11/5 10:12:37

Java Socket实战:局域网聊天与文件传输项目全解析
Java Socket实战:局域网聊天与文件传输项目全解析

简介:Chat是一款基于Java开发的局域网聊天软件,面向具备Java基础、希望理解网络通信与桌面应用整合的开发者与学习者,用于在本地网络环境中实现文字交流与文件传输。压缩包共7个文件,约14KB,以Java源码文件为核心&… · 2026/11/5 10:12:37

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

多智能体协作系统架构设计与实践优化
多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39

大模型系统性入门实战指南:从原理到部署微调的全链路路线
大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39

鸿蒙文件系统沙箱机制解析与开发实践
鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码