凸包最小外接矩形

获取点的凸包之后,可以实现以下获取凸包的最小外接矩形,获取思路:
1、以其中两点作为矩形的一条边
2、以该边作为x轴基坐标,并做y轴基坐标
3、将所有点以该基坐标进行旋转,找到以该边为基准的所有点的x坐标的最小和最大值,既y轴的最大值
4、获取该范围的面积值,并获取边界数据
5、重复每条边,并每次判断保存最小面积及参数
6、最终输出最小外接矩形的参数

参考:https://blog.csdn.net/wang_heng199/article/details/74477738
https://blog.csdn.net/chuchur/article/details/84966164

#include <iostream>
#include <cmath>
using namespace std;
/* min value */
#define FLT_MIN 1.175494351e-38F
/* max value */
#define FLT_MAX 3.402823466e+38F
struct Point
{
	float x, y;
};
struct OBB {
    Point u[2]; //x, y轴
    Point c;    //中心点
    float e[2]; //半长,半宽
};
float Dot(Point v, Point p)
{
    return v.x * p.x + v.y * p.y;
}
float Length(Point p)
{
    return sqrt(p.x*p.x + p.y*p.y);
}
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;}
}
vector<Point> My_MinareaRec(vector<Point> myPoint, vector<Point> boundRect)
{
    int ptsNum = myPoint.size();
    OBB obb;
    //必须是凸包
    float minArea = FLT_MAX;
    Point *pts = new Point[ ptsNum ];
    for(int i = 0; i < ptsNum; i++)
    {
        pts[i].x = myPoint[i].x;
        pts[i].y = myPoint[i].y;
    }
    //初始化边e
    Point *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 u1
    Point 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;
        //维护完毕
        //求面积 设置obb
        float 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;
        }
    }
    boundRect.push_back(Point(obb.e[0] * 2, obb.e[1] * 2));
    boundRect.push_back(obb.c + ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] ));
    boundRect.push_back(obb.c + ( obb.u[0] * obb.e[0] - obb.u[1] * obb.e[1] ));
    boundRect.push_back(obb.c - ( obb.u[0] * obb.e[0] + obb.u[1] * obb.e[1] ));
    boundRect.push_back(obb.c + ( obb.u[1] * obb.e[1] - obb.u[0] * obb.e[0] ));
    cout << boundRect.size() << endl;
    delete[] pts;
    delete[] e;
    return boundRect;
}
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是对点云数据求凸包最小外接矩形的C++代码: ```cpp #include <iostream> #include <vector> #include <algorithm> #include <cmath> using namespace std; // 定义点结构体 struct Point { double x, y; Point() {} Point(double x, double y) : x(x), y(y) {} }; // 定义向量结构体 struct Vector { double x, y; Vector() {} Vector(double x, double y) : x(x), y(y) {} Vector(Point A, Point B) : x(B.x - A.x), y(B.y - A.y) {} double length() { return hypot(x, y); } // 计算向量长度 }; // 两个点是否相等 bool operator==(const Point& A, const Point& B) { return A.x == B.x && A.y == B.y; } // 向量点乘 double operator*(const Vector& A, const Vector& B) { return A.x * B.x + A.y * B.y; } // 向量叉乘 double operator^(const Vector& A, const Vector& B) { return A.x * B.y - A.y * B.x; } // 求点集的凸包 vector<Point> convexHull(vector<Point> points) { int n = points.size(); if (n < 3) return points; sort(points.begin(), points.end(), [](const Point& A, const Point& B) { return A.x < B.x || (A.x == B.x && A.y < B.y); }); vector<Point> ch; for (int i = 0; i < n; i++) { while (ch.size() >= 2 && ((ch[ch.size() - 1] - ch[ch.size() - 2]) ^ (points[i] - ch[ch.size() - 2])) <= 0) { ch.pop_back(); } ch.push_back(points[i]); } int k = ch.size(); for (int i = n - 2; i >= 0; i--) { while (ch.size() >= k + 1 && ((ch[ch.size() - 1] - ch[ch.size() - 2]) ^ (points[i] - ch[ch.size() - 2])) <= 0) { ch.pop_back(); } ch.push_back(points[i]); } if (ch.size() > 1) ch.pop_back(); return ch; } // 求点集的最小外接矩形 double minBoundingRect(vector<Point> points) { vector<Point> ch = convexHull(points); int n = ch.size(); if (n < 3) return 0; double ans = 1e18; for (int i = 0, j = 2; i < n; i++) { Vector v1(ch[i], ch[(i + 1) % n]), v2; while ((v2 = Vector(ch[i], ch[(j + 1) % n])) ^ v1 >= 0) j = (j + 1) % n; double len1 = v1.length(), len2 = v2.length(); ans = min(ans, len1 * len2 * abs(v1 * v2) / 2 / abs(v1 ^ v2)); } return ans; } int main() { // 输入点云数据 vector<Point> points = {{-1, 0}, {0, 2}, {2, 1}, {1, -1}}; // 求点集的最小外接矩形 double rect = minBoundingRect(points); cout << "Minimum bounding rectangle: " << rect << endl; return 0; } ``` 注意:上述代码中的 `minBoundingRect` 函数需要先求出点集的凸包,然后枚举凸包上的每条边,计算以该边为对角线的矩形面积,并取所有面积的最小值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值