可以此计算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. Security+认证备考经验分享(501版本)
  2. SpringBoot 搭建
  3. 让自己的主机成为证书颁发机构
  4. Makefile_06:如果引用其它的 Makefile
  5. 华为c语言编程规范_单片机开发之C语言编程基本规范
  6. 对于0基础来说,Python 中有哪些难以理解的概念?我似乎明白了
  7. lgg8各个版本_LG正式推出G8SThinQ 搭载骁龙855
  8. c语言补码转源码函数,求原码、补码,反码(C语言源代码)
  9. 思维导图一定要用计算机来完成吗,程序猿不仅可以用电脑敲代码,还可以用来制作思维导图...
  10. 龟兔赛跑Description乌龟与兔子在马路上赛跑,马路边可以随地进行休息。
  11. sort()与拉姆达表达式
  12. 苹果手机上网速度慢_人群中手机网速不好,怎么办?
  13. 我在达芬奇的笔记本里,找到了用户画像的起源
  14. AE 动画的分层与组合
  15. 金庸小说中的农业漏洞[ZT]
  16. 怎么将静图变动图?用这个网站就够了
  17. 自助取款机系统(python+mysql+GUI)
  18. scm概念的scm存储
  19. linux oracle访问mysql_linux oracle 查询所有数据库
  20. java文件快速扫描仪_java扫描仪上传文件

热门文章

  1. matlab里如何输入圆周,如何在MATLAB中打印小圆圈?
  2. Linuxftp服务
  3. linux ssd加速机械硬盘,关于linux:机械硬盘随机IO慢的超乎你的想象
  4. Android Studio——Spinner 修改字体颜色和字体大小
  5. @Validated和@Valid使用
  6. 数据结构——树和二叉树
  7. 编程课程与数学的关系
  8. .基金从业资格考试信息
  9. 我参加NVIDIA Sky Hackathon(系统环境配置)
  10. Access根据出生日期计算年龄_不好意思,Power Query里根据出生日期计算年龄有点儿繁琐!...