Opencv:将平面图图像转换为数据模型
Posted
技术标签:
【中文标题】Opencv:将平面图图像转换为数据模型【英文标题】:Opencv: Convert floorplan image into data model 【发布时间】:2013-12-06 16:38:27 【问题描述】:我的计划是从纸上绘制的平面图中提取信息。我已经设法检测到 70-80% 的拉门:
现在我想从墙上创建一个数据模型。正如您在此处看到的那样,我已经设法提取它们:
从中我创建了轮廓:
我现在的想法是从该图像中获取线条的交点并从中创建一个数据模型。但是,如果我使用 houghlines 算法,我会得到这样的结果:
是否有人对如何获得交叉点有不同的想法,甚至对如何获得模型有不同的想法?会很不错。
PS:我正在使用 javacv。但是opencv中的算法也可以,因为我可以翻译。
【问题讨论】:
您对我的回答有任何疑问吗? 您可以使用分类算法(ADABoost、SVM)来检测平面图上的门、楼梯等物体,也许还可以使用交叉路口的拐角检测算法(想到哈里斯)。 啊,你给的赏金太快了。我要回答这个问题:( 来吧,你仍然可以这样做。这不仅是为了获得积分,也是为了帮助大多数人。因此,如果您有“更好”的解决方案,请告诉我:) @Schnodderbalken - 我很想知道您是否在这方面取得了进一步的成功,以及您是否愿意分享您的代码。我正在做一些非常相似的事情。我从 GaussianBlur 开始,然后是 Canny;然后进行 HoughLinesP 分析以提取墙线。我接近了,但没有你上面的示例输出那么干净。 【参考方案1】:首先,您还可以使用线段检测器来检测线条: http://www.ipol.im/pub/art/2012/gjmr-lsd/
如果我理解正确,那么问题是每条“真实”行都有几条不同的短线。您可以取“短线”的所有端点并使用 fitLine() 近似穿过一条线: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=fitline#fitline
【讨论】:
OpenCV C++ 实现了线段检测器 (LSD):docs.opencv.org/trunk/modules/imgproc/doc/…【参考方案2】:令我震惊的是,您真正想要的不一定是墙壁,而是房间——偶然地被墙壁包围。
此外,虽然您的“墙壁”数据看起来相当嘈杂(即有很多小部分可能会与小房间混淆) - 但您的“房间”数据却不是(没有很多幻像房间中间的墙壁)。
因此,检测房间(大约轴对齐的矩形,不包含超过特定阈值的白色像素)并通过查看附近像素之间的边界来推断墙壁可能是有益的。
我会分三个阶段来实现:首先,尝试从 houghlines 的输出中检测几个主轴(我会首先使用 K-means 聚类算法,然后按摩输出以获得垂直轴)。使用此数据更好地对齐图像。
其次,开始在图像周围随机播种黑色区域的小矩形。在各个方向上“增长”这些矩形,直到每一边都达到某个阈值以上的白色像素,或者它们进入另一个矩形。继续播种,直到覆盖图像的大部分区域。
第三,找到未被矩形覆盖的区域(希望也是矩形),并将它们折叠成线:
独立处理 x&y 轴上矩形的坐标 - 作为区间的集合 对这些坐标进行排序并查找构成一个矩形的上界和另一个矩形的下界的相邻坐标。 天真地尝试将沿每个轴发现的这些间隙配对,并测试生成的候选矩形是否与房间相交。丢弃相交的矩形。 将这些新矩形沿其主轴折叠成线。 然后可以在某个最小距离内连接线末端的点(通过延长线直到它们相遇)。这种方法有一些缺点:
它不能很好地处理非轴对齐的墙壁。幸运的是,无论如何,您可能大部分时间都希望这些自动对齐。 很可能将墙壁中的小门口视为墙壁的一部分 - 线条图中的意外间隙。这些必须单独检测并添加回重建的绘图中。 它不能很好地处理嘈杂的数据 - 但看起来您已经完成了使用 opencv 对数据进行去噪的出色工作!我很抱歉没有包含任何代码 sn-ps - 但我认为传达想法比细节更重要(如果您希望我扩展其中的任何内容,请发表评论)。另请注意,虽然几年前我使用过 opencv,但我绝不是专家 - 所以它可能已经有一些原语可以为你做这些。
【讨论】:
这是最详细的答案。感谢您的努力。【参考方案3】:尝试将霍夫变换图像或原始轮廓图像中的线条扩大 1 个像素。您可以通过用 2 或 3 的线条粗细绘制更大的线条来做到这一点(如果您使用霍夫变换来获取线条),或者您可以使用此代码手动扩展它们。
void dilate_one(cv::Mat& grid)
cv::Size sz = grid.size();
cv::Mat sc_copy = grid.clone();
for(int i = 1; i < sz.height -1; i++)
for(int j = 1; j < sz.width -1; j++)
if(grid.at<uchar>(i,j) != 0)
sc_copy.at<uchar>(i+1,j) = 255;
sc_copy.at<uchar>(i-1,j) = 255;
sc_copy.at<uchar>(i,j+1) = 255;
sc_copy.at<uchar>(i,j-1) = 255;
sc_copy.at<uchar>(i-1,j-1) = 255;
sc_copy.at<uchar>(i+1,j+1) = 255;
sc_copy.at<uchar>(i-1,j+1) = 255;
sc_copy.at<uchar>(i+1,j-1) = 255;
grid = sc_copy;
在霍夫变换之后,您有一组表示您的线的向量,存储为cv::Vec4i v
这具有线的端点。最简单的解决方案是匹配每条线的端点并找到最接近的端点。您可以使用简单的 L1 或 L2 范数来计算距离。
p1 = cv::Point2i(v[0],v[1])
和 p2 = cv::point2i(v[2],v[3]))
非常接近的点应该是交叉点。唯一的问题是 T 路口可能没有端点,但这在您的图像中似乎不是问题。
【讨论】:
【参考方案4】:我只是在这里提出一个想法,但您可以尝试从对原始图像进行阈值处理开始(这可能会产生有趣的结果,因为您的绘图是在白纸上的)。然后,通过对二值图像执行区域增长分割,您最终可能会得到相互分割的房间以及从背景分割的房间(识别房间和背景的标准可能是区域相似性)。由此,您将能够根据您的问题构建不同的模型:例如,房间的相对位置、面积,甚至组成(即整个平面图包含大房间,其中包含较小的房间等等)。
【讨论】:
以上是关于Opencv:将平面图图像转换为数据模型的主要内容,如果未能解决你的问题,请参考以下文章