OpenCV从一个正方形向量中提取图像区域

4
我有一张包含正方形的图片,需要提取该正方形包含的区域。应用 OpenCV 发行版中每个示例都可用的 squares.c 脚本后,我获得了一个正方形向量,然后需要为它们保存图像。
用户 karlphillip 建议如下:
for (size_t x = 0; x < squares.size(); x++) 
{
    Rect roi(squares[x][0].x, squares[x][0].y, 
             squares[x][1].x - squares[x][0].x, 
             squares[x][3].y - squares[x][0].y);
    Mat subimage(image, roi);
}

为了为原始图像中检测到的所有正方形生成一个名为subimage的新Mat。
正如卡尔提醒我的那样,图像中检测到的点可能并不代表一个完美的正方形(正如你在上面的图像中看到的)。但是,我刚才向您建议的代码假定它们是这样的。
事实上,我得到了这个错误:
OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width &&
      roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height &&
      roi.y + roi.height <= m.rows) in Mat, file /usr/include/opencv/cxmat.hpp, 
      line 187

terminate called after throwing an instance of 'cv::Exception'
what():  /usr/include/opencv/cxmat.hpp:187: error: (-215) 0 <= roi.x && 
       0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y &&
       0 <= roi.height && roi.y + roi.height <= m.rows in function Mat

Aborted

建议让脚本也接受非完美平方数?
1个回答

11

我觉得我需要澄清一下那段代码的几个问题。

首先,它假定检测到的区域是一个完美的正方形,因为忽略了squares[x]内部的一些点以创建一个新的Mat

其次,它还假定组成该区域的点是按顺时针方向检测到的,从图像左上角的p0开始:

(p0)  1st----2nd  (p1)
       |      |
       |      |
(p3)  4th----3rd  (p2)

这可能并不适用于所有检测到的地区。这意味着以下代码:

Rect roi(squares[x][0].x, squares[x][0].y, 
         squares[x][1].x - squares[x][0].x, 
         squares[x][3].y - squares[x][0].y);

可能会生成无效尺寸的 ROI,例如负宽度和高度值,这就是为什么 OpenCV 在 Mat subimage(image, roi); 抛出 cv::Exception 的原因。

你应该编写一个代码,识别区域的左上点并将其称为 p0,然后是右侧最近邻居 p1,接着找到区域的右下点并将其称为 p2,剩下的就是 p3。之后,组装 ROI 很容易:

Rect roi(p0.x, p0.y, 
         p1.x - p0.x, 
         p3.y - p0.y);

编辑:

我在阅读OpenCV v2.3的文档时,找到了一个优秀的解决方案。它自动化了我之前描述的过程,并使事情变得更加容易和清晰。您可以使用这个技巧将向量中的4个点排序为有意义的Rect结构:

// Data returned and filled by findSquares(). Check the example squares.cpp for more info on this function.
vector<vector<Point> > squares;

for (size_t i = 0; i < squares.size(); i++)
{
    Rect rectangle = boundingRect(Mat(squares[i]));
    cout << "#" << i << " rectangle x:" << rectangle.x << " y:" << rectangle.y << " " << rectangle.width << "x" << rectangle.height << endl;
}

虽然这个技巧在那个示例图像(http://img12.imageshack.us/img12/4104/26725680.jpg)上完美运行,但如果我尝试使用不同的图像(例如http://img202.imageshack.us/img202/9962/66647480.jpg,但它会发生在许多其他输入图像中),我仍然会收到相同的异常... - Marco L.
嗯,在squares[i][j]中,i将代表向量中的一个正方形,而j将用作设置构成正方形的点集的索引,范围从0到3。如果你理解了这一点,那么你是正确的。 - karlphillip
1
你应该打印出所有X的值,并查看检测到的所有正方形的所有坐标。也许在检测过程中发生了一些奇怪的事情,你正在看一个孤立的案例。无论如何,我在我的答案中解释的是正确的,从现在开始就取决于你和你的调试技能了。 - karlphillip
1
@Marco 顺便说一下,我刚刚更新了答案,并加入了代码,这将为你节省大量的麻烦。它自动化了识别点并将它们组织成一个矩形结构的过程。这是在OpenCV 2.3文档中找到的。这是我停止使用在线文档(适用于v2.1)并开始使用随OpenCV 2.3.x附带的文档的又一个原因。我想现在你可以把我的答案作为官方答案了。 - karlphillip
这个演示成功的一个重要秘诀是确保Canny算法结果显示出你所寻找的物体的所有边缘。你选择做的每一件事都需要针对这个单一目的:增强Canny检测。因此,添加适当的调试,比如在Canny()之后调用imshow()或将结果保存到磁盘进行进一步分析,并尝试调整Canny的参数以改善检测效果,或者像我使用medianBlur()dilate()那样添加/删除滤波器。例如,如果你正在寻找的正方形在Canny中没有显示出来,可能需要更多地模糊它。 - karlphillip
显示剩余7条评论

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