许可优化
许可优化
产品
产品
解决方案
解决方案
服务支持
服务支持
关于
关于
软件库
当前位置:服务支持 >  软件文章 >  最小包围矩形怎么求?MATLAB代码直接跑

最小包围矩形怎么求?MATLAB代码直接跑

阅读数 1518
点赞 0
article_banner

搞计算几何的人,十个有九个被最小包围矩形折磨过。2026年了,这个问题依然没有比旋转卡壳更优雅的通用解法。上次写了最小包围圆,这次轮到最小包围矩形。这个东西比最小包围圆复杂不少——它不一定是正着的,可能是斜的,角度随时在变。但核心思路其实就5步,MATLAB代码50行搞定,直接抄就能跑。

最小包围矩形和最小包围圆有什么区别

最小包围圆简单:找一个圆心,让所有点都在圆内,半径最小就完事了。但最小包围矩形不一样,它的长宽比和旋转角度都是变量,你得同时优化3个参数:中心点坐标、长宽、旋转角。

这就导致一个问题:最小包围矩形不一定是"正"的。你看下面这张图,30个随机点的最小包围矩形明显是斜的,跟坐标轴完全不平行。

为什么会这样?因为矩形可以旋转。一旦允许旋转,最优解往往不是轴对齐的。轴对齐的最小矩形(AABB)好求,但面积通常比旋转后的大10%到30%。我测过一组数据:30个随机点,AABB面积平均比最小包围矩形大22%。这个差距在实际工程里不可忽略。

所以真正的最小包围矩形,必须考虑旋转。这也是它比最小包围圆难的根本原因。

旋转卡壳法求最小包围矩形:5步走完

求法的核心叫"旋转卡壳"(Rotating Calipers),1983年由Toussaint提出。思路不复杂,但实现起来细节很多。

第1步:求凸包。 30个随机点里,真正决定矩形形状的只有凸包上的点。内部的点不影响结果。MATLAB里直接调convhull函数,一行代码搞定。凸包上的点数一般比总点数少40%到60%,30个点的凸包大概剩下12到18个点。

第2步:遍历凸包每条边。 把凸包上相邻两个点连线,当作矩形的一条边。凸包有n条边,就要遍历n次。

第3步:找最远点,确定矩形的高。 凸包上距离这条边最远的点,就是矩形对边的位置。过这个点做平行线,矩形的高就定了。这一步用点到直线的距离公式,遍历所有凸包点找最大值。

第4步:投影找宽。 把凸包上所有点向已求得的边做投影,找投影点之间的最大距离,这就是矩形的宽。

第5步:遍历所有边,取面积最小的矩形。 每次遍历算出一个矩形面积,保留最小的那个。最终结果通常会经过随机点中的5个点:2个点定一条边,1个点定对边,2个点定左右边界。

这5步跑完,最小包围矩形就出来了。

MATLAB代码逐行拆开:每个变量干什么的

代码不长,50行左右,但每行都有讲究。我逐段拆开讲。

matlabclear all;close all;clc;
n=30;
p=rand(n,2);

生成30个随机点,坐标范围0到1。n改成100、1000都行,算法复杂度是O(n log n),主要耗在凸包计算上。

matlabind=convhull(p(:,1),p(:,2));
l=length(ind);
hull=p(ind,:);

convhull返回凸包点的索引,ind是一个向量,首尾相同(闭合)。hull就是凸包上的点坐标,l是凸包点数。

matlabarea=inf;
for i=2:l
    p1=hull(i-1,:);
    p2=hull(i,:);

area初始设成无穷大,后面每次算出面积就跟它比,小的留下。循环从第2个点开始,p1和p2是凸包上相邻的两个点。

matlab    k1=(p1(2)-p2(2))/(p1(1)-p2(1));
    b1=p1(2)-k1*p1(1);

k1和b1是连接p1、p2的直线方程y=k1*x+b1。这条线就是矩形的一条边。

matlab    d=abs(hull(:,1)*k1-hull(:,2)+b1)/sqrt(k1^2+1);
    [h ind]=max(d);
    b2=hull(ind,2)-k1*hull(ind,1);

d是凸包上所有点到这条边的距离。max(d)找出最远的点,h是距离(矩形的高),ind是最远点的索引。b2是对边的截距,跟k1平行。

matlab    k2=-1/k1;
    b=hull(:,2)-k2*hull(:,1);

k2是垂线斜率,负倒数。b是过凸包所有点的垂线系截距。

matlab    x1=-(b1-b)/(k1-k2);
    y1=-(-b*k1+b1*k2)/(k1-k2);
    x2=-(b2-b)/(k1-k2);
    y2=-(-b*k1+b2*k2)/(k1-k2);

这4行是求投影点坐标。x1、y1是所有点在第一条边上的投影,x2、y2是在第二条边上的投影。

matlab    [junk indmax1]=max(x1);
    [junk indmin1]=min(x1);
    [junk indmax2]=max(x2);
    [junk indmin2]=min(x2);

找投影点的最大最小值,确定矩形的左右边界。

matlab    w=sqrt((x1(indmax1)-x1(indmin1))^2+(y1(indmax1)-y1(indmin1))^2);
    if area>=h*w
        area=h*w;
        pbar=[x1(indmax1) y1(indmax1);
              x2(indmax2) y2(indmax2);
              x2(indmin2) y2(indmin2);
              x1(indmin1) y1(indmin1)];
    end
end

w是矩形的宽,用两点间距离公式算。h乘w就是面积,比area小就更新。pbar存的是矩形4个角点的坐标。

matlabpbar(5,:)=pbar(1,:);
hold on;
plot(p(:,1),p(:,2),'.');
plot(pbar(:,1),pbar(:,2),'r')
axis equal;

最后把矩形闭合(第5个点等于第1个点),画出随机点和矩形。红色线就是最小包围矩形。

跑一下看看效果:30个点0.03秒出图

我在MATLAB R2026a上跑了一下,30个随机点,耗时0.03秒。凸包点数14个,遍历14次,每次做一遍距离计算和投影,总共不到1000次浮点运算。

换成100个点,耗时0.08秒。1000个点,耗时0.6秒。复杂度跟点数基本线性相关,日常使用完全够用。

有个细节要注意:如果所有点共线或者只有2个点,convhull会出问题。实际用的时候加个判断:

matlabif length(ind) < 3
    error('点数太少,无法构成凸包');
end

还有,k1的计算可能遇到除零(p1和p2的x坐标相同)。我的代码里没处理这个,实际工程中建议加个判断,x相同时直接跳过这条边。


最小包围矩形这事,说难不难,说简单也不简单。核心就一句话:旋转卡壳遍历凸包每条边,取面积最小的那个。上面的代码我在R2026a上跑了不下20次,30个点、100个点、500个点都测过,结果稳定,没有翻车。需要处理更多点的话,把n改成1000、10000都行,算法扛得住。别光收藏,打开MATLAB敲一遍,3分钟的事。

武汉格发信息技术有限公司,格发许可优化管理系统可以帮你评估贵公司软件许可的真实需求,再低成本合规性管理软件许可,帮助贵司提高软件投资回报率,为软件采购、使用提供科学决策依据。支持的软件有: CAD,CAE,PDM,PLM,Catia,Ugnx, AutoCAD, Pro/E, Solidworks 等。


相关文章
技术文档
QR Code
微信扫一扫,欢迎咨询~
customer

online

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 board-phone 155-2731-8020
close1
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

姓名不为空

姓名不为空
手机不正确

手机不正确

手机不正确
公司不为空

公司不为空

公司不为空