如何有效地找到点集合的边界框?

Posted

技术标签:

【中文标题】如何有效地找到点集合的边界框?【英文标题】:How to efficiently find the bounding box of a collection of points? 【发布时间】:2017-09-21 04:26:54 【问题描述】:

我有几个点存储在一个数组中。我需要找到这些点的界限,即。包围所有点的矩形。我知道如何用普通的 Python 解决这个问题。

我想知道有没有比简单的 max、min 数组或内置方法更好的方法来解决问题。

points = [[1, 3], [2, 4], [4, 1], [3, 3], [1, 6]]
b = bounds(points) # the function I am looking for
# now b = [[1, 1], [4, 6]]

【问题讨论】:

分享你将如何在 Python 中解决它?我们可以尝试改进它。怎么样:np.min(points,0) and np.max(points,0) 除非你的数据点已经有某种排序,否则你不能做得比 O(n) 更好。所以你不妨使用幼稚的 min 和 max 方法。 @Divakar 有帮助 【参考方案1】:

我获得性能的方法是尽可能将事情降低到 C 级别:

def bounding_box(points):
    x_coordinates, y_coordinates = zip(*points)

    return [(min(x_coordinates), min(y_coordinates)), (max(x_coordinates), max(y_coordinates))]

根据我的(粗略)衡量,这比@ReblochonMasque 的bounding_box_naive() 快1.5 倍。而且显然更优雅。 ;-)

【讨论】:

【参考方案2】:

您不能比O(n) 做得更好,因为您必须遍历所有点才能确定xymaxmin

但是,你可以减少常数因子,并且只遍历列表一次;但是,目前尚不清楚这是否会给您带来更好的执行时间,如果确实如此,它将用于大量的点集合。

[编辑]:事实上它没有,“幼稚”的方法是最有效的。

这是“幼稚”的方法:(它是两者中最快的)

def bounding_box_naive(points):
    """returns a list containing the bottom left and the top right 
    points in the sequence
    Here, we use min and max four times over the collection of points
    """
    bot_left_x = min(point[0] for point in points)
    bot_left_y = min(point[1] for point in points)
    top_right_x = max(point[0] for point in points)
    top_right_y = max(point[1] for point in points)

    return [(bot_left_x, bot_left_y), (top_right_x, top_right_y)]

和(也许?)不那么天真:

def bounding_box(points):
    """returns a list containing the bottom left and the top right 
    points in the sequence
    Here, we traverse the collection of points only once, 
    to find the min and max for x and y
    """
    bot_left_x, bot_left_y = float('inf'), float('inf')
    top_right_x, top_right_y = float('-inf'), float('-inf')
    for x, y in points:
        bot_left_x = min(bot_left_x, x)
        bot_left_y = min(bot_left_y, y)
        top_right_x = max(top_right_x, x)
        top_right_y = max(top_right_y, y)

    return [(bot_left_x, bot_left_y), (top_right_x, top_right_y)]

分析结果:

import random
points = [(random.randrange(-1000, 1000), random.randrange(-1000, 1000))  for _ in range(1000000)]

%timeit bounding_box_naive(points)
%timeit bounding_box(points)

大小 = 1,000 点

1000 loops, best of 3: 573 µs per loop
1000 loops, best of 3: 1.46 ms per loop

大小 = 10,000 点

100 loops, best of 3: 5.7 ms per loop
100 loops, best of 3: 14.7 ms per loop

大小 100,000 点

10 loops, best of 3: 66.8 ms per loop
10 loops, best of 3: 141 ms per loop

大小 1,000,000 点

1 loop, best of 3: 664 ms per loop
1 loop, best of 3: 1.47 s per loop

显然,第一个“不那么天真”的方法要快一个因子2.5 - 3

【讨论】:

+1,但我很好奇内联三元语句的性能与两元素 min 调用相比如何——或者,如果它更大/更小,只是一个 if: (update assignment) 每个循环内有 4 个循环和 1 个比较,而循环内有 1 个循环和 4 个比较。我认为这只是“移动工作”。如果你真的想要速度,你应该看看 numba JIT 或类似的东西。 嘿嘿嘿,这也是我的猜测,但是在你评论之后,我不得不回去测量它。谢谢你推我@wim。 (结果张贴在上面)

以上是关于如何有效地找到点集合的边界框?的主要内容,如果未能解决你的问题,请参考以下文章

如何有效地找到我的测试行和训练集之间的欧几里得距离?

有效地找到第 k 个集合位在位集中的位置

OpenCV - 在二进制图像中找到最大斑点的边界框

如何有效地从点创建线串?

无论文档边界如何,都可以有效地计算大型语料库中的词频

有效地找到不在大小为 40、400 或 4000 的集合中的整数