两个矩形重叠多少?

时间:2012-02-17 07:17:42

标签: algorithm math geometry bounding-box

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

我试图确定,它们不仅重叠,而且它们有多重叠?我试图弄清楚他们是否真的是同一个矩形给予或采取一些摆动的空间。那么他们的面积95%是一样的吗?

计算重叠百分比的任何帮助?

11 个答案:

答案 0 :(得分:64)

计算交叉区域,也是一个矩形:

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%)。

答案 1 :(得分:18)

交叉的公式将是

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

然后联盟将是S=SA+SB-SI

最后,比率为SI / S

答案 2 :(得分:9)

我最近遇到了这个问题并应用了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

答案 3 :(得分:6)

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

首先,考虑一种定义二维框的方法是:

  • (x,y)左上角
  • (x,y)右下角点

这看起来像:

Example Rectangle

我用三角形表示左上角,用圆圈表示右下角。在此示例中,这是为了避免使用诸如x1, x2之类的不透明语法。

两个重叠的矩形可能看起来像这样:

Two Rectangles

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

Rectangle Overlap

一旦您认识到这一点,很明显,重叠是找到并乘以两条深色线的结果:

Defining Overlap

每条线的长度是我们要比较的线之间的圆点的最小值,减去三角形点的最大值。

Finding Overlap

在这里,我使用的是两个色调的形状,以显示橙色和蓝色都进行了比较。形状后面的小写字母表示沿该轴比较了三角形。

例如,在上一个图像的顶部方程式中,您可以看到比较了橙色和蓝色三角形以寻找两者之间的最大值。比较的属性是x属性。橙色和蓝色三角形之间的x最大值为210。

表示同一件事的另一种方法是:可以将最靠近的最远点减去最长的最近点,从而找到适合我们比较的两条线的长度线的一侧。

Showing Overlap

找到这些行即可获得重叠区域的完整信息。

The Overlap

有了这个,找到重叠的百分比就很简单了:

Finding the percentage of overlap

但是,请等待,如果橙色矩形与蓝色矩形不重叠,那么您将遇到问题:

A Breaking Example

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

符合我们符号学的最终公式:

The Formula

值得注意的是,可能不需要使用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)

答案 4 :(得分:5)

修复以前的答案,使比率介于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

答案 5 :(得分:4)

假设矩形必须与xy轴平行,因为这似乎是之前评论和答案的情况。

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

考虑案例

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 :(得分:2)

@ 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

答案 7 :(得分:2)

[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

答案 8 :(得分:1)

@ 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

答案 9 :(得分:0)

这是C#中的有效函数:

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

<body>
  <header>
    <nav class="navbar navbar--fixed navbar-expand-md bg-dark navbar-dark py-1">
      <div class="container">
        <!-- Brand -->
        <a asp-controller="home" asp-action="index" class="navbar-brand">
          <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32" style="enable-background: new 0 0 32 32;" xml:space="preserve" fill="white">
                        <g id="logo" class="logo">
                            <path
                                d="M2 20h20v2H2v-2zm2-8h2v7H4v-7zm5 0h2v7H9v-7zm4 0h2v7h-2v-7zm5 0h2v7h-2v-7zM2 7l10-5 10 5v4H2V7zm2 1.236V9h16v-.764l-8-4-8 4zM12 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" />
                        </g>
                    </svg> BankNet
        </a>

        <!-- Toggler/collapsibe Button -->
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
                    <span class="navbar-toggler-icon"></span>
                </button>

        <div class="collapse navbar-collapse" id="collapsibleNavbar">
          <ul class="navbar-nav">
            <li class="nav-item">
              <a asp-controller="Home" asp-action="Index" class="nav-link">Home</a>
            </li>
            <li class="nav-item">
              <a asp-controller="Home" asp-action="Features" class="nav-link">Features</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
                                Utilities
                            </a>
              <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                <a asp-controller="Utilities" asp-action="CurrencyConverter" class="dropdown-item">Currency Converter</a>
                <a asp-controller="Utilities" asp-action="TaxCalculator" class="dropdown-item">Tax
                                    Calculator</a>
              </div>
            </li>
          </ul>

          <ul class="navbar-nav ml-auto">
            <li href="#" class="nav-link">
              <a asp-controller="Account" asp-action="Account" class="nav-link">Account</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  </header>
  <main role="main" class="pb-3">
    <nav class="navbar p-1" id="toggle-navbar">
      <div class="nav-item">
        <a class="btn btn-dark text-light" id="submenu-toggle">Toggle Menu</a>
      </div>
    </nav>

    <div id="wrapper">
      <div id="sidebar-wrapper">
        <ul class="sidebar-nav">
          <li class="sidebar-brand">
            Hello {Name}
          </li>
          <li>
            <a href="#Accounts">Accounts</a>
          </li>
          <li>
            <a href="#Transfers">Transfers</a>
          </li>
          <li>
            <a href="#Loans">Loans</a>
          </li>
          </li>
          <li>
            <a href="#Options">Account options</a>
          </li>
        </ul>
      </div>

      <div id="page-content-wrapper">
        <div class="container-fluid">
          <div class="row">
            <div class="col-lg-12">
              <h1>Hello World</h1>
              <h1>Hello World</h1>
              <h1>Hello World</h1>
              <h1>Hello World</h1>
              <h1>Hello World</h1>
              <h1>Hello World</h1>
            </div>
          </div>
        </div>
      </div>
    </div>
  </main>
</body>

答案 10 :(得分:-1)

测试了@ user3025064的答案,对于所有情况,结果都是正确的,除非一个矩形完全包围在另一个矩形内。 因此,获得SI后,您需要按如下方式计算比率:

S=SA+SB-SI
ratio = SI / S
if SI == SA  or SI == SB:
    ratio = 1
return ratio*100

当一个封装在另一个内时,将得到100。 另一种方法是计算SI / SA和SI / SB并检查其中一个是否等于1。