【问题标题】:Detect maze location on an image检测图像上的迷宫位置
【发布时间】:2021-04-13 01:03:55
【问题描述】:

我正在尝试从照片中定位迷宫位置。

我想得到的是迷宫角落的 (x,y) 点。

如您所见,我将cv2.Canny() 应用到图片上,并得到了一个非常干净的图像作为开始。

所以下一步就是定位迷宫了。

我已经搜索了一段时间,所有 SOF 问题都要求找到“完美”矩形的位置,例如this onethis one 但在我的情况下,矩形没有闭合轮廓,因此它们的代码在我的情况下不起作用。

还查看了 OpenCV 代码,他们都试图找到轮廓并将这些轮廓绘制到图像上,但这对我不起作用。我刚刚得到了 1 个大轮廓,它与我的照片的边框相距甚远。

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)      
cnts = imutils.grab_contours(cnts)

更新 1

原图:

代码:

import cv2
from PIL import Image
from matplotlib import pyplot as plt
import numpy as np
import imutils

img = cv2.imread('maze.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

edges = cv2.Canny(img,100,200)


f,axarr = plt.subplots(1,2,figsize=(15,15))

axarr[0].imshow(img)
axarr[1].imshow(edges)

plt.show()

【问题讨论】:

  • 请提供原图,不要在matplotlib内绘制。首先:对图像进行灰度化,使用一些相对较大的阈值进行二值化阈值(因为纸张非常白),一些形态学以获得漂亮的轮廓,找到轮廓。
  • 嗨@HansHirse 感谢您的评论。在 Jupyter Notebook 中显示图像很常见,我可以知道为什么我不应该这样做吗?右侧图像是灰度图像,已经是二进制(0 或 255)。是的,我试图找到我在问题中描述的轮廓,但它们都没有像我在问题中解释的那样工作。
  • 这里提供原图,方便大家下载,自己尝试。没有人愿意从保存的绘图图像中手动剪切(近似)图像,特别是因为与处理原始图像相比,它会给出不同的结果。此外,如果没有看到更多关于 minimal reproducible example 的代码,无论如何都很难提供帮助。
  • @HansHirse 是有道理的。我已经上传了原始照片和我的代码。

标签: python opencv image-processing maze opencv-python


【解决方案1】:

一种方法可能是使用形态学reconstruction,尽管它可能是一种临时解决方案。 假设迷宫始终位于照片的中心,您可以使用迷宫中心部分的“窗口”作为迷宫的形态重建的种子/标记,因为所有边界都是连接的。 结果,您会将迷宫从照片中隔离出来。 通过获得孤立迷宫的边界框,可以“轻松”获得拐角的 (x,y)。

例如:

from skimage.morphology import reconstruction
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
import numpy as np


maze = imread('/home/user/.../maze.jpg', as_gray=True)
h, w = maze.shape

#creating the seed 
seed = np.zeros_like(maze)
size = 40

#we create a window in the center of the image
seed[h//2-size:h//2+size, w//2-size:w//2+size] = maze[h//2-size:h//2+size, w//2-size:w//2+size]

seed 将包含带有黑框的迷宫的中心部分。请记住,seed 的大小必须与maze 相同,因此框架的存在。

我们第一次应用重建并将结果二值化。默认情况下,reconstruction 使用具有 (3,3) 形状的结构元素。阈值的值可以根据图像进行调整。

rec_1 = reconstruction(seed, maze)
rec_1[rec_1 < 0.70] = 0.
rec_1[rec_1 >= 0.70] = 1.

这是rec_1

这很好,但我们可以让它变得更好一点。让我们再次应用reconstruction,但这次使用更大的窗口和erosion而不是dilation作为重建方法:

seed_2 = np.ones_like(rec_1)
size_2 = 240
seed_2[h//2-size_2:h//2+size_2, w//2-size_2:w//2+size_2] = recon[h//2-size_2:h//2+size_2, w//2-size_2:w//2+size_2]
rec_2 = reconstruction(seed_2, rec_1, method='erosion', selem=np.ones((11,11)))

注意我也在使用更大的结构元素,形状为(11,11)

最后的重建步骤给了我们这个结果:

接下来的步骤是使用边界框方法来获取左上角和右下角 (x, y) 坐标。

这些结果将是一个近似值,因为在原始图像中,迷宫不是完全平坦的,我们将依赖这样一个事实,即迷宫的入口和出口恰好位于这些位置,而不是迷宫中的任何其他位置。

此外,这可能只需更少的步骤即可完成。

更新: 如建议的那样,可以通过使用凸包、二元腐蚀和角点检测技术而不是边界框方法来获得精确坐标。

首先我们反转rec_2,然后我们得到凸包,应用侵蚀来缩小矩形的大小并计算角峰坐标。

from skimage.util import invert
from skimage.morphology import binary_erosion
from skimage.morphology.convex_hull import convex_hull_image
from skimage.feature import corner_harris, corner_subpix

rec_2_inv = invert(rec_2)
hull = convex_hull_image(rec_2_inv)
hull_eroded = binary_erosion(hull, selem=np.ones(30,30))

coords = corner_peaks(corner_harris(hull_eroded), min_distance=5, threshold_rel=0.02)
coords_subpix = corner_subpix(rec_2_inv, coords, window_size=13)[2:4]

这是侵蚀凸包:

最后我们绘制带有坐标的最终图像:

fig, ax = plt.subplots()
ax.imshow(rec_2_inv, cmap=plt.cm.gray)
ax.plot(coords_subpix[:, 1], coords_subpix[:, 0], '+r', markersize=15)
plt.savefig('maze_with_coordinates.png', bbox_tight=True)

coords_subpix 保存坐标的数值,我故意分配两个值而不是四个值。 这些值是:

[[1611.48876404  104.50561798]
 [2695.07777778 1679.67222222]]

大部分更新代码都是使用 scikit-image example's 参数完成的,只进行了最少的调整。

【讨论】:

  • 多么好的解决方案!不要在这里使用边界框,而是反转最后一张图像,进行一些形态闭合,得到凸包,并应用一些角点检测,例如Shi-Tomasi。这样,您将获得“准确”的角点,而不是边界框中的一些近似值。
  • 嗨@Franva,我不知道corner_subpix() 的细节,但您可以阅读更多关于它的信息herehere。当我进行实验时,当我使用太大的 selem 形状时,它返回 nan。为了“修复它”,我只是将 selem 形状转了一个档次,直到它返回数值。
  • @Franva ... 关于为什么我们需要在已经有 4 个角的情况下使用 corner_subpix(),我可能是错的,但我认为这可能是一种安全措施,以确保这些坐标真正代表角并且不是误报。此外,它还为问题所需的坐标提供了精度。
  • @Franva 最后,关于形态重建的工作原理,我强烈建议您阅读docs 中引用的论文。本质上,它依靠膨胀来使种子“生长”并将其与原始图像相交以创建更大的表示。重复此步骤直到原始图像和相交部分之间没有差异并使其增长不会改变结果。但这是一种形态重建。
  • @Franva 感谢您的客气话!我确实在hull_eroded 中提到了selem。基本上,selem 越大,侵蚀像素的速度就越快,但它也会对角坐标的计算方式产生影响。我们应用侵蚀来收缩凸包,使其足以“适合”迷宫外边界内。一旦合适,左上角和右下角就是迷宫进出的坐标。也可以随意尝试corner detection 的其他方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 2021-06-25
  • 2019-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多