结合交叉边界矩形的有效方法

时间:2013-09-29 15:13:39

标签: opencv image-processing

我正在尝试使用OpenCV简化以下图像:

enter image description here

我们这里有很多红色的形状。其中一些完全包含其他人。他们中的一些人与邻居相交。我的目标是通过用其union的多边形的边界框替换任何两个相交的形状来统一所有相交的形状。 (重复直到没有更多相交的形状)。

通过交叉,我的意思是触摸。希望这能让它100%清晰:

enter image description here

我正在尝试使用标准形态学操作有效地做到这一点;显然它可以在O(N ^ 2)中天真地完成,但那太慢了。扩张没有帮助,因为一些形状只相隔1px,如果它们没有相交,我不希望它们合并。

3 个答案:

答案 0 :(得分:14)

更新:我之前误解了这个问题。我们不想删除完全在其他内部的矩形。我们只想替换相交的矩形。因此,对于第一种情况,我们不得不采取任何行动。

新api(2.4.9)支持&和|运算符。

来自opencv doc

  • rect = rect1& rect2(矩形交叉点)
  • rect = rect1 | rect2(包含rect2和rect3的最小区域矩形)

它还支持平等比较(==)

  • rect == rect1

所以现在很容易完成任务。对于每对矩形rect1和rect2,

if((rect1 & rect2) == rect1) ... // rect1 is completely inside rect2; do nothing.
else if((rect1 & rect2).area() > 0) // they intersect; merge them.
    newrect = rect1 | rect2;
    ... // remove rect1 and rect2 from list and insert newrect.

更新2:(用于在java中翻译)

我非常了解java。我也从未使用过java API。我在这里给出一些psudo代码(我认为可以轻松翻译)

对于&运算符,我们需要一个找到两个矩形相交的方法。

Method: Intersect (Rect A, Rect B)
left = max(A.x, B.x)
top  = max(A.y, B.y)
right = min(A.x + A.width, B.x + B.width)
bottom = min(A.y + A.height, B.y + B.height)
if(left <= right && top <= bottom) return Rect(left, top, right - left, bottom - top)
else return Rect()

对于|运算符,我们需要一个类似的方法

Method: Merge (Rect A, Rect B)
left = min(A.x, B.x)
top  = min(A.y, B.y)
right = max(A.x + A.width, B.x + B.width)
bottom = max(A.y + A.height, B.y + B.height)
return Rect(left, top, right - left, bottom - top)

对于==运算符,我们可以使用重载的equals方法。

答案 1 :(得分:9)

要完成您想要的任务,我们将使用findContours。这里的关键点是了解mode设置为CV_RETR_TREE时的工作原理。在这种情况下,hierarchy的构造方式是每个偶数深度级别包含外部轮廓,而奇数深度级别包含内部轮廓。我们在这里需要的是遍历层次树,打印与甚至深度级别相关的轮廓。

首先,我们找到名为original

的图像的轮廓
typedef std::vector<std::vector<cv::Point> > Contours;
typedef std::vector<cv::Vec4i> Hierarchy;

Contours contours;
Hierarchy hierarchy;
cv::findContours(original, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);

要在名为processed的图像上打印外部轮廓,我们需要一个递归函数。

void printExternalContours(cv::Mat img, Contours const& contours, Hierarchy const& hierarchy, int const idx)
{
    //for every contour of the same hierarchy level
    for(int i = idx; i >= 0; i = hierarchy[i][0])
    {
        //print it
        cv::drawContours(img, contours, i, cv::Scalar(255));

        //for every of its internal contours
        for(int j = hierarchy[i][2]; j >= 0; j = hierarchy[j][0])
        {
            //recursively print the external contours of its children
            printExternalContours(img, contours, hierarchy, hierarchy[j][2]);
        }
    }
}

printExternalContours(processed, contours, hierarchy, 0);

结果如下所示,其中originalprocessed并排显示。

original processed

如果您绝对需要矩形形状,则只需使用boundingRect获得给定一组点的最小包围矩形(在这种情况下为每个轮廓)并使用rectangle作为绘图。换句话说,替换

cv::drawContours(img, contours, i, cv::Scalar(255));

通过

cv::rectangle(img, cv::boundingRect(contours[i]), cv::Scalar(255));

findContours期待一张8位图像,你可以从你的原件制作一个灰色图像,然后将其限制为一个完美的黑色背景,或者也许就足以使用你的红色通道例如,只需确保背景完全是黑色。

关于findContours复杂性,我无法证明它比O(N ^ 2)更好,也没有我在快速找到任何输入之后谷歌搜索,但我相信OpenCV实现了最着名的算法。

答案 2 :(得分:1)

给出(x,y,w,h)形式的两个边界框轮廓,这是一个创建单个边界框的功能(假设两个框相互接触或彼此接触)。返回组合边界框的(x,y,w,h),即左上角x,左上角y,宽度和高度。这是一个例子

(x1,y1)       w1                           (x3,y3)         w3
  ._____________________.                    .____________________________.
  |                     |                    |                            | 
  |                     |  h1                |                            |
  |   (x2,y2)           |                    |                            |
  |     ._______________|_______.      -->   |                            |
  |     |               |       |            |                            |  h3
  ._____|_______________.       |            |                            |
        |                       |  h2        |                            |
        |                       |            |                            |
        |           w2          |            |                            |
        ._______________________.            .____________________________.

代码

def combineBoundingBox(box1, box2):
    x = min(box1[0], box2[0])
    y = min(box1[1], box2[1])
    w = box2[0] + box2[2] - box1[0]
    h = max(box1[1] + box1[3], box2[1] + box2[3]) - y
    return (x, y, w, h)

示例

有了这两个边界框,

>>> print(box1)
>>> print(box2)
(132, 85, 190, 231)
(264, 80, 121, 230)

>>> new = combineBoundingBox(box1, box2) 
>>> print(new)
(132, 80, 253, 236)

以下是视觉效果:->之前

enter image description here enter image description here