27

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

在此处输入图像描述

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

通过相交,我的意思是也接触。希望这使它100%清楚:

在此处输入图像描述

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

4

3 回答 3

16

更新:我之前误解了这个问题。我们不想删除完全在其他矩形内的矩形。我们只想替换相交的矩形。因此,对于第一种情况,我们什么都不做。

新的 api (2.4.9) 支持 & 和 | 运营商。

来自opencv 文档

  • rect = rect1 & rect2(矩形交点)
  • 矩形 = 矩形 1 | rect2(包含 rect2 和 rect3 的最小面积矩形)

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

  • 矩形 == 矩形1

所以现在很容易完成任务。对于每对矩形 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。我在这里给出了一些伪代码(我认为可以很容易地翻译)

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

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方法。

于 2014-06-05T13:25:30.543 回答
10

为了完成您想要的,我们将使用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并排显示。

原来的 处理

如果您绝对需要矩形形状,您只需要使用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 实现了最知名的算法。

于 2013-09-29T15:34:27.243 回答
5

给定两个形式为 的边界框轮廓(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)

这是视觉结果: 之前->之后

在此处输入图像描述 在此处输入图像描述

于 2019-10-02T03:12:51.437 回答