我正在尝试使用OpenCV简化以下图像:
我们这里有很多红色的形状。其中一些完全包含其他人。他们中的一些人与邻居相交。我的目标是通过用其union的多边形的边界框替换任何两个相交的形状来统一所有相交的形状。 (重复直到没有更多相交的形状)。
通过交叉,我的意思是触摸。希望这能让它100%清晰:
我正在尝试使用标准形态学操作有效地做到这一点;显然它可以在O(N ^ 2)中天真地完成,但那太慢了。扩张没有帮助,因为一些形状只相隔1px,如果它们没有相交,我不希望它们合并。
答案 0 :(得分:14)
更新:我之前误解了这个问题。我们不想删除完全在其他内部的矩形。我们只想替换相交的矩形。因此,对于第一种情况,我们不得不采取任何行动。
新api(2.4.9)支持&和|运算符。
来自opencv doc:
它还支持平等比较(==)
所以现在很容易完成任务。对于每对矩形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);
结果如下所示,其中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)
以下是视觉效果:->
之前