Franva
Franva

Reputation: 7077

Detect maze location on an image

I'm trying to locate the maze location from a photo.

What I'd like to get is the (x,y) points of the corners of the maze.

As you could see I applied cv2.Canny() to the picture and got a really nice clean image as a start.

enter image description here

So the next step is to locate the maze.

I have searched for a while, all SOF questions are asking for finding locations of "perfect" rectangles, e.g. this one and this one But in my case, the rectangle doesn't have a closed contour, so codes for them won't work in my case.

Also have had a look at the OpenCV code, they all try to find Contours and draw those contours onto the image, but it doesn't work for me. I just got 1 big contour which goes alone the borders of my photo.

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

Update 1

Original Photo: enter image description here

Code:

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()

Upvotes: 0

Views: 445

Answers (1)

pazitos10
pazitos10

Reputation: 1709

One approach could be using morphological reconstruction, although it would probably be an ad-hoc solution. Assuming the maze is always in the center of the photo, you could use a "window" of the central portion of the maze as seed/mark for a morphological reconstruction of the maze given that all borders are connected. As a result you would have isolated the maze out of the photo. The (x,y) of the corners would be "easy" to get by obtaining the bounding box of the isolated maze.

For example:

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 would contain a centered portion of the maze with a black frame. Keep in mind, seed must be the same size as maze, thus the existence of the frame.

first seed for reconstruction

We apply the reconstruction for the first time and binarize the result. By default, reconstruction uses a structural element with (3,3) shape. The values for the threshold can be adjusted depending the image.

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

This is rec_1: first reconstruction attempt

It's good but we can make it a little bit better. Let's apply reconstruction again, but this time using a bigger window and erosion instead of dilation as the reconstruction method:

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)))

Notice I'm using a bigger structural element too, with a shape of (11,11).

The final reconstruction step gives us this result:

final reoconstruction

The following step would be to use a bounding box method to get the top left and bottom right (x, y) coordinates.

Those results will be an approximation given that in the original image, the maze is not perfectly flat and we would be relying the fact that the maze's entry and exit are exactly in those positions and not anywhere else in the maze.

Also, this can probably be done with fewer steps.

UPDATE: As suggested, the exact coordinates can be obtained by using convex hull, binary erosion and corner detection techniques instead of a bounding box method.

First we invert rec_2, then we get the convex hull, apply erosion to shrink the size of the rectangle and calculate the corner peaks coordinates.

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]

This is the eroded convex hull: Eroded convex hull

Finally we plot the final image with the coordinates over it:

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)

Annotated entry and exit on the maze

Where coords_subpix holds the numerical values of the coordinates and I purposely assign two values instead of four. Those values are:

[[1611.48876404  104.50561798]
 [2695.07777778 1679.67222222]]

Most of the update code was done using scikit-image example's parameters with minimal tweaking.

Upvotes: 5

Related Questions