多个重叠矩形的边界框

6

enter image description here

如何获取多个重叠矩形的边界框?如果有不重叠的矩形,则可能有多个边界框。
我有一个包含n个对象的数组,表示矩形。每个对象表示一个矩形,例如:{left: 178, top: 67, width: 20, height: 14}。它们也可以用其他方式表示,比如leftX、topY、rightX、bottomY;可以轻松转换。
我不想要最大非抑制算法。文献中是否有特定的算法可以实现这一点?我会尝试在JavaScript中进行转换。
编辑:AuxTaco solution只适用于矩形一个接一个地重叠;如果按指定顺序绘制矩形(如下图所示),则会得到3个边界区域。

enter image description here

编辑2:也许一个有趣的情况是以下内容:{{link1:enter image description here}}
矩形1和2重叠,它们的边界框与矩形3重叠;然而,我对这种特殊情况不感兴趣,可以将3视为单独的矩形。

1
当有5个矩形,其中1、2相交,3、4相交,5孤立时会发生什么?在这种情况下预期的输出是什么? - Tarun Lalwani
3个边界框;一个包含1、2;一个包含3、4;一个包含5。 - Shury
1
你能为这种情况添加示例坐标吗? - Tarun Lalwani
@tarunLalwani {left: 74, top: 66.89999389648438, width: 80.5, height: 71} {left: 111.5, top: 95.89999389648438, width: 125, height: 84} {left: 177, top: 120.89999389648438, width: 168.5, height: 90} {left: 93, top: 258.8999938964844, width: 81.5, height: 81} {left: 265.5, top: 320.8999938964844, width: 92, height: 83} {left: 393, top: 210.89999389648438, width: 88.5, height: 95} - Shury
2个回答

6

我已经提供了一种方法,应该适合您的情况。以下是该方法的摘要

  • 从一个空碰撞数组开始
  • 碰撞数组中的每个元素都将存储与任何矩形相碰撞的矩形数组
  • 遍历我们拥有的矩形列表
  • 如果矩形不与任何元素相碰撞,则将其附加到碰撞数组中
  • 如果矩形仅与一个元素碰撞,则将其附加到该碰撞数组元素中
  • 如果矩形与数组中的多个元素碰撞,则将所有这些元素合并为一个,然后删除其余元素
  • 最终碰撞数组仅具有碰撞数组元素
  • 然后可以计算每个这些碰撞的边界矩形,这只是一个最小/最大问题。

现在来看代码

function doRectsCollide(a, b) {
    return !(
        ((a.top + a.height) < (b.top)) ||
        (a.top > (b.top + b.height)) ||
        ((a.left + a.width) < b.left) ||
        (a.left > (b.left + b.width))
    );
}

var collisions = [];

var rectangles = [
    {left: 74, top: 66.89999389648438, width: 80.5, height: 71},
    {left: 111.5, top: 95.89999389648438, width: 125, height: 84},
    {left: 177, top: 120.89999389648438, width: 168.5, height: 90},
    {left: 93, top: 258.8999938964844, width: 81.5, height: 81},
    {left: 265.5, top: 320.8999938964844, width: 92, height: 83},
    {left: 393, top: 210.89999389648438, width: 88.5, height: 95}
];

for (rectangle of rectangles) {
    var collisions_indexes = [];

    index = 0;
    for (currentColission of collisions) {
        for (rect of currentColission) {
            if (doRectsCollide(rect, rectangle) === true) {
                collisions_indexes.push(index)
                break
            }
        }
        index++;
    }

    if (collisions_indexes.length == 0) {
        // this rectangle collides with none and should be appened to collisions array
        collisions.push([rectangle])
    } else if (collisions_indexes.length >= 1) {
        // there is just one collision, we can merge the same
        collisions[collisions_indexes[0]].push(rectangle)

        // now we have got multiple collisions, so we need to merge all the collisions with the first one
        // and remove the colission ones
        for (var i = 1; i < collisions_indexes.length; i++) {
            // we use - (i - 1) because we will remove the collision once we merge it
            // so after each merge the collision index actually shift by -1

            var new_index = collisions_indexes[i] - (i - 1);
            // extend the first colliding array with the new collision match
            collisions[collisions_indexes[0]].push(...collisions[new_index])

            // now we remove the element from our collision since it is merged with some other
            collisions.splice(new_index, 1);
        }

    }
}

console.log(JSON.stringify(collisions, null, 2));
//now we have a array of collision which will have all colliding ones
for (collision of collisions) {
    // compute bounding rectangle from rectangles array in collision
}

现在同样的输出是

[
    [
        {"left":74,"top":66.89999389648438,"width":80.5,"height":71},
        {"left":111.5,"top":95.89999389648438,"width":125,"height":84},
        {"left":177,"top":120.89999389648438,"width":168.5,"height":90}
    ],
    [{"left":93,"top":258.8999938964844,"width":81.5,"height":81}],
    [{"left":265.5,"top":320.8999938964844,"width":92,"height":83}],
    [{"left":393,"top":210.89999389648438,"width":88.5,"height":95}]
]

在最后一行,collisions数组的每个元素都应该有一个矩形或一组与之相撞的矩形,对吗?在这种情况下,https://i.imgur.com/Hz5H7yf.png 的collisions数组输出是 https://i.imgur.com/M1uknej.png,正好是最初的矩形数组。 - Shury
1
@Shury,现在检查一下,内部循环中有一个小问题,我使用了collisions而不是collision,除此之外,所有逻辑都可以正常工作,不需要更改。 - Tarun Lalwani
非常感谢您的帮助。我明白了算法的工作原理 :) - Shury

2
我不知道特定算法的名称,但这可以简化为2D碰撞检测:最初的回答。
function combineRects (rect1, rect2) {
  return a rectangle object representing the bounding box of the union of rect1 and rect2;
}
function doRectsCollide (rect1, rect2) {
  return true if rect1 and rect2 intersect;
}

const rectangles = [ your rectangle objects ];
const boundingBoxes = rectangles.reduce((boxes, rect) => {
  // Start with an empty array of bounding boxes.
  // For each rectangle, find the bounding box it intersects.
  const boundingBoxIndex = boxes.findIndex(doRectsCollide.bind(null, rect));
  if (boundingBoxIndex === -1) {
    // If there is none, push the rectangle into the bounding box array.
    boxes.push(rect);
    return boxes;
  } else {
    // Otherwise,
    // replace the intersected bounding box with a new box that includes the rectangle.
    boxes[boundingBoxIndex] = combineRects(boxes[boundingBoxIndex], rect);
    return boxes;
  }
}, []);

在您的示例中,这是相当有效的(每个矩形仅与最多3个其他矩形进行比较),但在最坏情况下(没有重叠的矩形),速度会变慢为O(n^2)。可以通过使用比原始数组更好的东西来存储边界框来改进它。"最初的回答"

嗯,我发现上面的代码有一个问题。请考虑按照以下顺序获取矩形:https://i.imgur.com/D3TzQzI.png 这将输出3个元素 (3) [{...}, {...}, {...}]。 - Shury

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接