麻辣GIS微信平台

更多 GIS 干货

微信关注不错过

如何获取一堆点的最小面积外接矩形?

最近做点云范围提取时候,小编遇到一个问题,给一堆离散点,怎么求包住它们的最小面积矩形?小编大约翻了一下网上成熟的实现,总结了一下实现的方法,有兴趣的小伙伴可以参考。

什么是最小面积外接矩形

最小面积外接矩形,英文常叫 Minimum Area Rectangle、Minimum Bounding Rectangle,缩写 MAR 或 MBR。和常见最小外接矩形不同,它允许矩形旋转任意角度,但要求面积最小。如下图:

算法原理

最容易想到的做法是先求凸包,把多边形绕质心每次转 1°,每次算一遍轴对齐外接矩形面积,转满 360° 后取最小值。这个方法虽然可行,但这种方法比较耗费计算,且最后的精准度与步长有关。

在 polygon generalisation 领域这个问题叫 “smallest surrounding rectangle”。关键性质是:最小面积外接矩形的朝向,必然与凸包某一条边的方向一致。因此不必按固定角度步长穷举,只要遍历凸包每条边,把该边当作矩形的一条边方向,在这个朝向下求外接矩形面积,取最小即可。

具体步骤如下。

  1. 对点集求凸包
  2. 遍历凸包每条边,计算边的方向角或单位方向向量。
  3. 以该边方向为基准,把凸包顶点投影到“沿边方向”和“垂直边方向”两个轴上,分别取 min/max,得到该朝向下面积最小的外接矩形。
  4. 比较所有边的结果,取面积最小。

凸包顶点数通常远小于原始点数,边数也就是 O(h),h 为凸包顶点数,整体效率比“每 1° 转一次”高得多。3D 情形思路类似,但要测试凸包各个面的朝向,比 2D 更复杂一些。

Python实现

按这个思路,小编用 NumPy 和 SciPy 写了一个 Python 版本,返回矩形四个角点坐标:

import numpy as np
from scipy.spatial import ConvexHull

def minimum_bounding_rectangle(points):
    """求点集的最小面积外接矩形,返回 4x2 角点坐标。"""
    pts = np.asarray(points, dtype=float)
    hull = ConvexHull(pts)
    vertices = pts[hull.vertices]
    n = len(vertices)

    min_area = np.inf
    best_corners = None

    for i in range(n):
        p1 = vertices[i]
        p2 = vertices[(i + 1) % n]
        edge = p2 - p1
        length = np.linalg.norm(edge)
        if length == 0:
            continue

        v = edge / length
        w = np.array([-v[1], v[0]])

        proj_v = vertices @ v
        proj_w = vertices @ w
        min_v, max_v = proj_v.min(), proj_v.max()
        min_w, max_w = proj_w.min(), proj_w.max()
        area = (max_v - min_v) * (max_w - min_w)

        if area < min_area:
            min_area = area
            local = np.array([
                [max_v, min_w],
                [min_v, min_w],
                [min_v, max_w],
                [max_v, max_w],
            ])
            best_corners = local @ np.vstack([v, w])

    return best_corners

需要注意的是,如果绘图时 x、y 轴比例不一致,矩形在屏幕里可能看起来像平行四边形,那是显示比例问题。

其他实现方式

如果不想自己写代码,也有一些现成工具,比如:

  1. OpenCV 提供 minAreaRect,输入 2D 点集即可返回旋转矩形
  2. PostGIS 从 2.5.0 起提供 ST_OrientedEnvelope,底层调用 GEOS 的 GEOSMinimumRotatedRectangle;Shapely 对应 oriented_envelope(),别名 minimum_rotated_rectangle(),GeoPandas 里也可写 geometry.minimum_rotated_rectangle

总结

虽然了解了原理,也找到了一个基础版本,但小编最后还是使用了 GeoPandas来实现了业务功能,原理归原理,实现归实现,毕竟这么大的开源软件写的一定比我好~

参考

  1. Finding minimum-area-rectangle for given points?:https://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
  2. Whitebox GAT:https://www.whiteboxgeo.com/
  3. OpenCV minAreaRect 教程:https://docs.opencv.org/4.x/de/d62/tutorial_bounding_rotated_ellipses.html

相关阅读

麻辣GIS-Sailor

作者:

GIS爱好者,学GIS,更爱玩GIS。

声明

1.本文所分享的所有需要用户下载使用的内容(包括但不限于软件、数据、图片)来自于网络或者麻辣GIS粉丝自行分享,版权归该下载资源的合法拥有者所有,如有侵权请第一时间联系本站删除。

2.下载内容仅限个人学习使用,请切勿用作商用等其他用途,否则后果自负。

手机阅读
公众号关注
知识星球
手机阅读
麻辣GIS微信公众号关注
最新GIS干货
关注麻辣GIS知识星球
私享圈子
没有下文

留言板(小编看到第一时间回复)