【问题标题】:How much do two rectangles overlap?两个矩形重叠多少?
【发布时间】:2012-03-08 14:55:37
【问题描述】:

我有两个矩形 a 和 b,它们的边平行于坐标系的轴。我有它们的坐标为 x1,y1,x2,y2。

我试图确定,它们不仅重叠,而且重叠多少?我试图弄清楚它们是否真的是同一个矩形,给予或占据一点回旋余地。那么它们的面积 95% 是一样的吗?

对计算重叠百分比有帮助吗?

【问题讨论】:

  • 到目前为止你有什么想法?
  • 你的意思是,2个矩形在一个给定坐标的盒子里?
  • @sans481 是的,我更正了这个问题,谢谢。
  • 你试过了吗?如果有,你做了什么?
  • user3025064 的答案是正确的答案。

标签: algorithm math geometry bounding-box


【解决方案1】:

计算交点的面积,也是一个矩形:

SI = Max(0, Min(XA2, XB2) - Max(XA1, XB1)) * Max(0, Min(YA2, YB2) - Max(YA1, YB1))

从那里计算并集的面积:

SU = SA + SB - SI

你可以考虑比例

SI / SU

(在完美重叠的情况下为 100%,降至 0%)。

【讨论】:

  • 哇。这正是我在谢谢你之后的样子!我没有正确地考虑它。工会概念的引入是我所缺少的。谢谢!。
  • SA 和 SB 是 A 和 B 的面积吗?对于 SI,我将矩形移到右下角越多,值就越高。
  • 举个数值例子。
  • 如果他标注变量是什么就更好了。
  • 对于其他变量,变量假设A 是矩形A,B 是矩形B; X 是 X 维度,Y 是 Y 维度; 1 是(顶部或左侧)点,2 是(底部或右侧)点。因此:XA2 是右侧X 的值 rect AXB2 是右侧X 的值 rect B; XA1 是矩形 A 的左侧 X 值; XB1 是矩形 B 的左侧 X 值; YA2是矩形A的底部Y值; YB2 是 rect B 的底部 Y 值; YA1 是 rect A 的顶部 Y 值;而YB1是矩形B的顶部Y值。最后,SI是相交区域。
【解决方案2】:

虽然给出的公认答案是正确的,但我认为值得以一种使答案的基本原理完全显而易见的方式探索这个答案。这是一种太常见的算法,无法给出不完整(或更糟糕,有争议)的答案。此外,只看一眼给定的公式,您可能会错过算法的美感和可扩展性,以及正在做出的隐含决策。

首先,考虑一种定义二维盒子的方法:

  • (x, y) 表示左上角
  • (x, y) 表示右下角

这可能看起来像:

我用三角形表示左上角,用圆圈表示右下角。这是为了避免在本例中出现像 x1, x2 这样的不透明语法。

两个重叠的矩形可能如下所示:

请注意,要找到重叠部分,您正在寻找橙色和蓝色碰撞的地方:

一旦你认识到这一点,很明显重叠是找到并相乘这两条暗线的结果:

每条线的长度是两个圆点的最小值,减去两个三角形点的最大值。

在这里,我使用了两个色调的三角形(和圆形)来显示橙色点和蓝色点是相互比较的。双色三角形后面的小写字母“y”表示沿y轴比较三角形,小“x”表示沿x轴比较。

例如,要查找深蓝色线的长度,您可以查看三角形进行比较以查找两者之间的最大值。比较的属性是 x 属性。三角形之间的最大 x 值为 210。

另一种说法是: 通过从直线最远侧的最近点减去直线最近侧的最远点,得到同时适合橙色线和蓝色线的新线的长度。

找到这些线可以提供有关重叠区域的完整信息。

一旦你有了这个,找出重叠的百分比是微不足道的:

但是等等,如果橙色矩形不与蓝色矩形重叠,那么你就会遇到问题:

在这个例子中,我们的重叠区域得到了 -850,这是不对的。更糟糕的是,如果检测不与任一维度重叠(不在 x 轴或 y 轴上),那么您仍然会得到一个正数,因为 两个 维度都是负数。这就是为什么您将Max(0, ...) * Max(0, ...) 视为解决方案的一部分的原因;它确保如果任何重叠为负,您将从您的函数中获得 0。

与我们的符号系统保持一致的最终公式:

值得注意的是,可能不需要使用max(0, ...) 函数。您可能想知道某些东西是否沿其一个维度而不是所有维度重叠;如果您使用 max 那么您将删除该信息。出于这个原因,请考虑如何处理非重叠边界框。通常,max 函数很好用,但值得注意的是它在做什么。

最后,请注意,由于此比较仅涉及线性测量,因此可以缩放到任意尺寸或任意重叠的四边形。

总结一下:

intersecting_area = 
max(0, min(orange.circle.x, blue.circle.x) 
  - max(orange.triangle.x, blue.triangle.x)) 
  * max(0, min(orange.circle.y, blue.circle.y) 
  - max(orange.triangle.y, blue.triangle.y)
)
percent_coverage = intersecting_area 
   / (orange_area + blue_area - intersecting_area)

【讨论】:

  • 感谢您的精彩解释。如果边界框在另一个边界框内怎么办?
  • @prb 取这个等式:max(0, min(orange.circle.x, blue.circle.x) - max(orange.triangle.x, blue.triangle.x)) * max(0, min(orange.circle.y, blue.circle.y) - max(orange.triangle.y, blue.triangle.y)) 并输入数字,使所有橙色三角形都大于蓝色三角形(但小于蓝色圆圈)并且所有橙色圆圈都小于蓝色圆圈(但比蓝色三角形更多)。报告您的发现
  • 有没有办法为多个边界框做到这一点?
  • @prb 你在上一个中发现了什么?其他人可以从您的评论中受益。
  • 多么好的解决方案! ?
【解决方案3】:

我最近也遇到了这个问题并应用了 Yves 的答案,但不知何故导致了错误的区域大小,所以我重写了它。

假设两个矩形 A 和 B,找出它们重叠的程度,如果重叠,则返回区域大小:

IF A.right < B.left OR A.left > B.right
    OR A.bottom < B.top OR A.top > B.bottom THEN RETURN 0

width := IF A.right > B.right THEN B.right - A.left ELSE A.right - B.left
height := IF A.bottom > B.bottom THEN B.bottom - A.top ELSE A.bottom - B.top

RETURN width * height

【讨论】:

    【解决方案4】:

    只是修正以前的答案,使比率在 0 和 1 之间(使用 Python):

        # (x1,y1) top-left coord, (x2,y2) bottom-right coord, (w,h) size
        A = {'x1': 0, 'y1': 0, 'x2': 99, 'y2': 99, 'w': 100, 'h': 100}
        B = {'x1': 0, 'y1': 0, 'x2': 49, 'y2': 49, 'w':  50, 'h':  50}
    
        # overlap between A and B
        SA = A['w']*A['h']
        SB = B['w']*B['h']
        SI = np.max([ 0, 1 + np.min([A['x2'],B['x2']]) - np.max([A['x1'],B['x1']]) ]) * np.max([ 0, 1 + np.min([A['y2'],B['y2']]) - np.max([A['y1'],B['y1']]) ])
        SU = SA + SB - SI
        overlap_AB = float(SI) / float(SU)
        print 'overlap between A and B: %f' % overlap_AB
    
        # overlap between A and A
        B = A
        SB = B['w']*B['h']
        SI = np.max([ 0, 1 + np.min([A['x2'],B['x2']]) - np.max([A['x1'],B['x1']]) ]) * np.max([ 0, 1 + np.min([A['y2'],B['y2']]) - np.max([A['y1'],B['y1']]) ])
        SU = SA + SB - SI
        overlap_AA = float(SI) / float(SU)
        print 'overlap between A and A: %f' % overlap_AA
    

    输出将是:

        overlap between A and B: 0.250000
        overlap between A and A: 1.000000
    

    【讨论】:

    • 注意。这个答案使用 NumPy。
    • @Alessio B 一个矩形在另一个矩形内的情况怎么样?
    【解决方案5】:

    假设矩形必须平行于xy 轴,因为这似乎是以前的 cmets 和答案的情况。

    我还不能发表评论,但我想指出,前面的两个答案似乎都忽略了一个边矩形完全在另一个矩形边内的情况。如果我错了,请纠正我。

    考虑案例

    a: (1,1), (4,4)
    b: (2,2), (5,3)
    

    在这种情况下,我们看到对于交叉点,高度必须为bTop - bBottom,因为b 的垂直部分完全包含在a 中。

    我们只需要添加更多的情况如下:(如果将top和bottom视为左右相同的东西,可以缩短代码,这样就不需要复制两次条件块,但这应该做。)

    if aRight <= bLeft or bRight <= aLeft or aTop <= bBottom or bTop <= aBottom:
        # There is no intersection in these cases
        return 0
    else:
        # There is some intersection
    
        if aRight >= bRight and aLeft <= bLeft:
            # From x axis point of view, b is wholly contained in a
            width = bRight - bLeft
        elif bRight >= aRight and bLeft <= aLeft:
            # From x axis point of view, a is wholly contained in b
            width = aRight - aLeft
        elif aRight >= bRight:
            width = bRight - aLeft
        else:
            width = aRight - bLeft
    
        if aTop >= bTop and aBottom <= bBottom:
            # From y axis point of view, b is wholly contained in a
            height = bTop - bBottom
        elif bTop >= aTop and bBottom <= aBottom:
            # From y axis point of view, a is wholly contained in b
            height = aTop - aBottom
        elif aTop >= bTop:
            height = bTop - aBottom
        else:
            height = aTop - bBottom
    
    return width * height
    

    【讨论】:

      【解决方案6】:
      [ymin_a, xmin_a, ymax_a, xmax_a] = list(bbox_a)
      [ymin_b, xmin_b, ymax_b, xmax_b] = list(bbox_b)
      
      x_intersection = min(xmax_a, xmax_b) - max(xmin_a, xmin_b) + 1
      y_intersection = min(ymax_a, ymax_b) - max(ymin_a, ymin_b) + 1
      
      if x_intersection <= 0 or y_intersection <= 0:
          return 0
      else:
          return x_intersection * y_intersection
      

      【讨论】:

        【解决方案7】:

        @User3025064 是正确的并且是最简单的解决方案,但是,必须首先检查不相交的矩形的排他性,例如矩形 A 和 B(在 Visual Basic 中):

        If A.Top =< B.Bottom or A.Bottom => B.Top or A.Right =< B.Left or A.Left => B.Right then
            Exit sub   'No intersection
        else
            width = ABS(Min(XA2, XB2) - Max(XA1, XB1))
            height = ABS(Min(YA2, YB2) - Max(YA1, YB1))
            Area = width * height      'Total intersection area.
        End if
        

        【讨论】:

          【解决方案8】:

          这是 C# 中的一个工作函数:

              public double calculateOverlapPercentage(Rectangle A, Rectangle B)
              {
                  double result = 0.0;
                  //trivial cases
                  if (!A.IntersectsWith(B)) return 0.0; 
                  if (A.X == B.X && A.Y == B.Y && A.Width == B.Width && A.Height == B.Height) return 100.0;
          
          
                  //# overlap between A and B
                  double SA = A.Width * A.Height;
                  double SB = B.Width * B.Height;
                  double SI = Math.Max(0,  Math.Min(A.Right, B.Right) - Math.Max(A.Left, B.Left)) *
                              Math.Max(0, Math.Min(A.Bottom, B.Bottom) - Math.Max(A.Top, B.Top));
                  double SU = SA + SB - SI;
                  result = SI / SU; //ratio
                  result *= 100.0; //percentage
                  return result;
              }
          

          【讨论】:

            【解决方案9】:

            @user3025064 的答案是正确的答案。接受的答案无意中翻转了内部的 MAX 和 MIN 调用。 如果我们使用给出的公式 MAX(0,x) 而不是 ABS(x),我们也不需要首先检查它们是否相交。如果它们不相交,则 MAX(0,x) 返回零,这使得相交区域为 0(即不相交)。

            我建议@Yves Daoust 修正他的答案,因为它是任何搜索该问题的人都可以接受的答案。再一次,这里是正确的交集公式:

            SI = Max(0, Min(XA2, XB2) - Max(XA1, XB1)) * Max(0, Min(YA2, YB2) - Max(YA1, YB1))

            其余的照常。联合:

            SU = SA + SB - SI

            和比例:

            SI/SU

            【讨论】:

            • 你确定吗?我已根据您的建议更新了正确答案,但有 30 人建议 Yves 是正确答案,所以我希望您能再次为我检查您的假设。谢谢。
            • 试试这个反例:两个矩形,并排不重叠,所以XA1&lt;XA2&lt;XB1&lt;XB2。根据 Yves 的交点宽度为:w = Max(0, Max(XA2, XB2) - Min(XA1, XB1)) = XB2-XA1,这是一个包含两个矩形之间间隙的大矩形。在固定公式中,w = Max(0, Min(XA2, XB2) - Max(XA1, XB1)) = Max(0, XA2-XB1) = 0 因为 XA2w=0 表示没有交集。
            猜你喜欢
            • 2017-01-15
            • 2017-05-13
            • 2018-09-29
            • 2012-06-21
            • 2023-03-18
            • 2014-05-13
            • 1970-01-01
            • 2011-09-14
            相关资源
            最近更新 更多