可以此计算2D图形的OBB

计算几何中有这样一条结果:凸多边形的最小包围矩形至少有一条边与多边形的一条边共线。

暴力算法

遍历每一条边构造包围矩形比较面积大小。说是构造包围矩形,其实只需要投影点到边以及垂直边上取距离最远两点距离得长宽后求面积即可。

算法的全部时间消耗为O(n^2)。

/* min value */
#define FLT_MIN 1.175494351e-38F
/* max value */
#define FLT_MAX 3.402823466e+38Fstruct OBB {Point u[2]; //x, y轴Point c;    //中心点float e[2];    //半长,半宽
};float MinAreaRec(Point *pts, int ptsNum, OBB &obb) {float minArea = FLT_MAX;for(int i = 0, j = ptsNum - 1; i < ptsNum; j = i, i++) {//遍历边Point u0 = pts[i] - pts[j];//构造边u0 = u0/Length(u0);Point u1 = Point(0-u0.y, u0.x);//与u0垂直float min0 = 0.0f, max0 = 0.0f, min1 = 0.0f, max1 = 0.0f;for(int k = 0; k < ptsNum; k++) {//遍历点Point d = pts[k] - pts[j];//投影在u0float dot = Dot(d, u0);if(dot < min0) min0 = dot;if(dot > max0) max0 = dot;//投影在u1dot = Dot(d,u1);if(dot < min1) min1 = dot;if(dot > max1) max1 = dot;}float area = (max0 - min0) * (max1 - min1);if( area < minArea ) {minArea = area;obb.c = pts[j] + ( u0 * (max0 + min0) + u1 * (max1 + min1) )*0.5f;obb.u[0] = u0;obb.u[1] = u1;obb.e[0] = (max0 - min0)*0.5f;obb.e[1] = (max1 - min1)*0.5f;}}return minArea;
}Point * GetOBBPoints(OBB obb) {//获取OBB四个顶点坐标Point *pts = new Point [4];pts[0] = obb.c + ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] );pts[1] = obb.c + ( obb.u[0] * obb.e[0] - obb.u[1] * obb.e[1] );pts[2] = obb.c - ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] );pts[3] = obb.c + ( obb.u[1] * obb.e[1] - obb.u[0] * obb.e[0] );return pts;
}

旋转卡尺(旋转卡壳)算法

使用旋转卡尺算法可将计算凸多边形的最小包围矩形的时间消耗减少至O(n log n)。

取坐标上两极值点构成平行线,旋转两线,当线与多边形一条边重合时,计算构成矩形面积。

继续旋转,直至旋转角度超过90度。取最小面积。

该算法仅对凸体有效(暴力法对凸体凹体均有效),因此需要先计算凸体,该算法的时间复杂度受限于凸体的计算过程,在确定凸体的前提下,该算法可达O(n)。

凸体效果

凹体效果(故先需要对凹体进行凸体计算,凸体计算在此不进行介绍)

分析:

首先默认一平行边已经与某一边重合(一般我们取第一条边)。

计算以该边方向为x轴方向的xy坐标系(u0,u1)。

则四条边的方向为(u0, u1, -u0, -u1),我们只需要记录其所在的点就可以了。

确定最初的极值点。

接下来就是计算该旋转哪条边了。

计算出旋转角度最小的一条边。

旋转角度a的计算:利用已知向量v1,v2,根据Dot(v1,v2) = |v1|*|v2|*cosa计算cosa的值,再由于在角度0~180度之间,角度越大相应cos值越小。

由于旋转的角度在4条边中是最小的,故其他三条边进行相同角度的旋转时,绝对不会与下一条边重合,除非最小角度有两个相同的,但这也不影响计算,因为下次就成旋转角度0(非重合边)的成为重合边,再下次就没有了。不移动的三点为新的极值点,若原重合边旋转角度不是最小,则原来的重合边中后一点成为新的极值点,最小旋转角度的边旋转后碰到的边成为新的重合边。

需要注意的是,以下图为例,

投影在x轴最大的极值点计算与u1角度,而投影在x轴最小的极值点计算与-u1的角度。(负!因为这个失误卡了两天TAT)

同理,投影在y轴最大的极值点计算与-u0角度,而投影在y轴最小的极值点(即重合边中后一点)计算与u0的角度。

计算出角度后,尽管我们根据上边所描述的能够知道有哪几点为极值点。但是我们并不知道哪个点具体是哪个极值点,这时进行重新计算,即投影比较。这里只须计算3点即可。(重合边是投影在y轴最小,无需计算)

重设极值点后进行面积计算,直至u0旋转至回到初始边即可停止(或旋转n次,n为边个数)。

根据最小面积时候的极值点以及xy轴(u0,u1)可以确定obb。

整理步骤如下:

以e0为重合边初始化u0u1;

求3个极值点在pts中下标imax0 imax1 imin0 ;

for(int i = 0; i < ptsNum ; i++) {

求最小旋转角的顶点的下标;

if( 0 == 最小旋转角的顶点的下标 )break;

if(最小旋转角的顶点的下标 == 原3个极值点中的一个) {

把原重合边后端点设为极值点;

设置新重合边;

}

if(最小旋转角的顶点的下标 == 原重合边后一点) 设置新重合边;

维护极值点,确定各个极值点为什么的极值点;

求面积area;

if(area < minArea) minArea = area;//在此你也可以设置obb

}

float Cos(Point v, Point p1) {float dot = Dot(v,p1);float cos = dot/(Length(v)*Length(p1));return cos;
}void Setu0u1(Point e, Point &u0, Point &u1) {//以e方向为x轴方向,设定xy轴u0 = e / Length(e);u1 = Point( 0 - u0.y, u0.x);
}int GetMinAngleIndex(int imin1, int imax0, int imax1, int imin0,Point* e, Point u0, Point u1) {//返回旋转角度最小(cos值最大)的点的下标int imin_angle_index = 0;float cos = 0, maxCos = FLT_MIN;cos = Cos(e[imin1], u0);if(cos > maxCos){maxCos = cos; imin_angle_index = imin1;}cos = Cos(e[imax0], u1);if(cos > maxCos){maxCos = cos; imin_angle_index = imax0;}cos = Cos(e[imax1], Point(0-u0.x,0-u0.y));if(cos > maxCos){maxCos = cos; imin_angle_index = imax1;}cos = Cos(e[imin0], Point(0-u1.x,0-u1.y));if(cos > maxCos){maxCos = cos; imin_angle_index = imin0;}return imin_angle_index;
}void SetMinMax(Point*pts, int i, int iu, Point u0, Point u1,float &max0, float &min0, float &max1,int & new_imax0, int &new_imin0, int &new_imax1)
{//找到x轴投影最大最小,y轴投影最大的长度(y轴最小则是重合边上点,长度为0)//以及极值点在pts中的下标Point d =  pts[i] - pts[iu];float dist0 = Dot( d, u0);if(dist0 > max0){ max0 = dist0; new_imax0 = i;}if(dist0 < min0){ min0 = dist0; new_imin0 = i;}float dist1 = Dot( d, u1);if(dist1 > max1){ max1 = dist1; new_imax1 = i;}
}float MinAreaRec2(Point *pts, int ptsNum, OBB &obb) {//旋转卡壳算法//必须是凸包float minArea = FLT_MAX;//初始化边ePoint *e = new Point[ ptsNum ];for(int i = 0; i < ptsNum; i++) {e[i] = pts[(i+1)%ptsNum] - pts[i];}int iu = 0;//以e[0]为重合边//初始化u0 u1Point u0,u1;Setu0u1(e[iu], u0, u1);int imax0 = 0, imax1 = 0, imin0 = 0, imin1 = 0;float min0 = FLT_MAX, max0 = FLT_MIN,max1 = FLT_MIN, min1 = 0;//min1其实可以不需要设定的,始终为0//只是为了理解方便加上//要去掉则需要把下方用到的min1都改为0//求三个极值坐标for( int i = 0; i < ptsNum; i++) {SetMinMax(pts, i, iu, u0, u1,max0, min0, max1,imax0, imin0, imax1) ;}for(int i = 0; i < ptsNum ; i++) {int iminangle = 0;iminangle = GetMinAngleIndex((iu+1)%ptsNum, imax0, imax1, imin0, e, u0, u1);if(iminangle == 0)break;//旋转回了初始点 没必要继续if(iminangle == imax0){imax0 = (iu + 1)%ptsNum;iu = iminangle;}else if(iminangle == imax1){imax1 = (iu + 1)%ptsNum;iu = iminangle;}else if(iminangle == imin0){imin0 = (iu + 1)%ptsNum;iu = iminangle;}else if(iminangle == (iu+1)%ptsNum){iu = (iu+1)%ptsNum;}Setu0u1(e[iu], u0, u1);//重设u0u1//维护三个极值点int new_imax0 = imax0, new_imax1 = imax1, new_imin0 = imin0;min0 =FLT_MAX, max0 = FLT_MIN, max1 = FLT_MIN;//确定原先imax0在新坐标系中是什么极值SetMinMax(pts, imax0, iu, u0, u1,max0, min0, max1,new_imax0, new_imin0, new_imax1) ;//确定原先imax1在新坐标系中是什么极值SetMinMax(pts, imax1, iu, u0, u1,max0, min0, max1,new_imax0, new_imin0, new_imax1) ;//确定原先imin0在新坐标系中是什么极值SetMinMax(pts, imin0, iu, u0, u1,max0, min0, max1,new_imax0, new_imin0, new_imax1) ;imax0 = new_imax0;imax1 = new_imax1;imin0 = new_imin0;//维护完毕//求面积 设置obbfloat area = (max0 - min0)*(max1 - min1);if(area < minArea) {minArea = area;obb.e[0] = (max0 - min0)*0.5f;obb.e[1] = (max1 - min1)*0.5f;obb.u[0] = u0;obb.u[1] = u1;obb.c = pts[iu] + ( u0 * (max0 + min0) + u1 * (max1 + min1) )*0.5f;}}return minArea;
}

凸多边形最小面积包围矩形相关推荐

  1. Matlab最小面积包围四边形

    对于存在透视变换的物体,提取时最小面积包围矩形不能满足要求,google到一个求最小面积包围四边形的算法,虽然速度较慢.以提取书本为例,实验结果和代码如下. booktest.m I = imread ...

  2. LeetCode 963. 最小面积矩形 II

    文章目录 1. 题目 2. 解题 1. 题目 给定在 xy 平面上的一组点,确定由这些点组成的任何矩形的最小面积,其中矩形的边不一定平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1 ...

  3. leetcode最小面积_LeetCode—— 939. 最小面积矩形(JavaScript)

    给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1: 输入:[[1,1],[1,3],[3,1],[3,3 ...

  4. leetcode最小面积_Code Review Swift 算法题: 最小面积矩形  Leetcode 的动人之处

    题目描述: 939. 最小面积矩形 给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1: 输入:[[1,1 ...

  5. LeetCode 939. 最小面积矩形(哈希)

    文章目录 1. 题目 2. 解题 1. 题目 给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1: 输入: ...

  6. halcon18算子:最小包围矩形smallest_rectangle2()

    halcon的最小包围矩形smallest_rectangle2()算子: 输入: region 输出: row:最小包围矩形的中心点的行坐标 col:最小包围矩形的中心的列坐标 Phi:最小包围矩形 ...

  7. Leetcode 939:最小面积矩形(最详细的解法!!!)

    给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1: 输入:[[1,1],[1,3],[3,1],[3,3 ...

  8. 939. 最小面积矩形

    给定在 xy 平面上的一组点,确定由这些点组成的矩形的最小面积,其中矩形的边平行于 x 轴和 y 轴. 如果没有任何矩形,就返回 0. 示例 1: 输入:[[1,1],[1,3],[3,1],[3,3 ...

  9. 如何拟合点云目标的最小外包围box

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 文章导读 本文介绍点云聚类后的点集包围框拟合方法,分别对不同形式的包围框分析其构建方法.拟合特性,通过 ...

最新文章

  1. python冒泡排序
  2. 使用Spring Security 资源服务器来保护Spring Cloud 微服务
  3. 一套图 搞懂“时间复杂度”(转载)
  4. ubuntu20.04 黑屏 进入不了图形界面 dev/sda1: clean
  5. 法兰程序CAD开发的进展
  6. MySQL5.7更改密码时出现ERROR 1054
  7. lbp特征提取算法 知乎_图像-LBP特征描述算子-人脸检测
  8. 聚能聊每周精选 第十五期
  9. VS2012统计代码量
  10. matlab 大于并且小于,matlab分段函数中大于、小于(“」”、“「”)的应用
  11. 水经注万能地图下载器怎么设置系统参数
  12. 爱心~~~红色的~~~小爱心组成的大爱心~~~
  13. win10修改系统字体(替换OneNote中Calibri字体)
  14. 用诺模图可视化你的模型
  15. day02 Nacos集群配置、Feign远程调用和统一网关Gateway
  16. visionPro中混淆阈值是什么?
  17. 计算机网络 - chunk协议
  18. 云+社区技术沙龙丨解析腾讯最新开源项目背后的技术栈
  19. PR工程版本转换攻略,低版本PR打开高版本工程
  20. CPCI-S检索(原ISTP)、CPCI-SSH源期刊检索简介

热门文章

  1. IOS OpenGL ES GPUImage 图像Lanczos重取样模糊效果 GPUImageLanczosResamplingFilter
  2. 教你一招:使用最快速的方式激活windows10专业版
  3. java 怎样设置文本域的字体颜色_java的JTextArea中怎么改变字体颜色
  4. 什么是项目生命周期?如何划分项目阶段?有什么意义?
  5. 连接一个HTTPS网站的前300毫秒,都发生了什么? | ArcBlock 课堂预告
  6. Linux常用命令——ss命令
  7. (一百三十五)Android O探索WLAN扫描(WIFI SCAN ALWAYS)
  8. 视频加密选择在线加密还是软件加密好?
  9. 小米路由器登录服务器无响应是怎么回事,小米路由器登录界面打不开怎么办? | 192路由网...
  10. C语言__bitwise宏定义