Search code examples
pythonimage-processingmazeopencv

How to find out the locations of entry and exit from a maze picture


Just wondering is it possible to find out the entry and exit points of a maze in a picture?

I have highlighted the 2 points with red and blue for explaining purpose, but they do not exist in the original picture, so please don't count on them.

The locations of the entry and exit can be vary, e.g. they could be in the middle of an edge, but not limited to the locations which are the corner or the middle.

I notice that there are 2 black arrows pointing to the 2 locations, but how to locate the 2 locations without the help of these 2 arrows?

enter image description here

Update 1

I should upload the processed image here:

enter image description here

After applied some image processing procedures, I have gotten the extracted maze. But this is not what I am asking, let's get back to the topic and the extracted maze image should be the starting point for this question.


Solution

  • I started with your extracted maze image. The first thing to do is to find the four corners of the maze. We can find them by just looking for the four most extreme (closest to the corner of the image) points. We can connect up these four corners to make a (sort of) rectangle that encloses our maze.

    enter image description here

    The idea is to find the longest line of non-occupied (white) points that is perpendicular to the edges of our rectangle. To simplify the math for this we can rectify the image since we know the four corners.

    Running through the edges and "raycasting" until we hit a wall, here are some graphs of the length of each line found along each edge.

    Top Edge

    enter image description here

    Left Edge

    enter image description here

    Bottom Edge

    enter image description here

    Right Edge

    enter image description here

    Now we can just find the longest line for each edge, take the highest two, and those are our entrances to the maze.

    enter image description here

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    # tuplifies a point for opencv
    def tup(p):
        return (int(p[0]), int(p[1]));
    
    # load image
    img = cv2.imread("maze.png");
    
    # resize
    scale = 0.5;
    h, w = img.shape[:2];
    h = int(h*scale);
    w = int(w*scale);
    img = cv2.resize(img, (w,h));
    copy = np.copy(img);
    
    # mask image
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
    mask = cv2.inRange(gray, 100, 255);
    
    # find corners
    corners = [[[0,0], 0] for a in range(4)];
    for y in range(h):
        # progress check
        print(str(y) + " of " + str(h));
        for x in range(w):
            # check pixel
            if mask[y][x] == 0:
                # scores
                scores = [];
                scores.append((h - y) + (w - x)); # top-left
                scores.append((h - y) + x); # top-right
                scores.append(y + x); # bottom-right
                scores.append(y + (w - x)); # bottom-left
                
                # check corners
                for a in range(len(scores)):
                    if scores[a] > corners[a][1]:
                        corners[a][1] = scores[a];
                        corners[a][0] = [x, y];
    
    # draw connecting lines
    for a in range(len(corners)):
        prev = corners[a-1][0];
        curr = corners[a][0];
        cv2.line(img, tup(prev), tup(curr), (0,200,0), 2);
    
    # draw corners
    for corner in corners:
        cv2.circle(img, tup(corner[0]), 4, (255,255,0), -1);
    
    # re-orient to make the math easier
    rectify = np.array([[0,0], [w,0], [w,h], [0,h]]);
    numped_corners = [corner[0] for corner in corners];
    numped_corners = np.array(numped_corners);
    hmat, _ = cv2.findHomography(numped_corners, rectify);
    rect = cv2.warpPerspective(copy, hmat, (w,h));
    
    # redo mask
    gray = cv2.cvtColor(rect, cv2.COLOR_BGR2GRAY);
    mask = cv2.inRange(gray, 100, 255); 
    
    # dilate
    kernel = np.ones((3,3), np.uint8);
    mask = cv2.erode(mask, kernel, iterations = 5);
    
    # find entrances
    top = []; # [score, point]
    # top side
    for x in range(w):
        y = 0;
        while mask[y][x] == 255:
            y += 1;
        top.append([y, [x,0]]);
    # left side
    left = [];
    for y in range(h):
        x = 0;
        while mask[y][x] == 255:
            x += 1;
        left.append([x, [0,y]]);
    # bottom side
    bottom = [];
    for x in range(w):
        y = h-1;
        while mask[y][x] == 255:
            y -= 1;
        bottom.append([(h - y) - 1, [x, h-1]]);
    # right side
    right = [];
    for y in range(h):
        x = w-1;
        while mask[y][x] == 255:
            x -= 1;
        right.append([(w - x) - 1, [w-1,y]]);
    
    # combine
    scores = [top, left, bottom, right];
    
    # plot
    for side in scores:
        fig = plt.figure();
        ax = plt.axes();
        y = [score[0] for score in side];
        x = [a for a in range(len(y))];
        ax.plot(x, y);
        plt.show();
    
    # get the top score for each side
    highscores = []; # [score, [x, y]];
    for side in scores:
        top_score = -1;
        top_point = [-1, -1];
        for score in side:
            if score[0] > top_score:
                top_score = score[0];
                top_point = score[1];
        highscores.append([top_score, top_point]);
    
    # get the top two (assuming that there are two entrances to the maze)
    one = [0, [0,0]];
    two = [0, [0,0]];
    for side in highscores:
        if side[0] > one[0]:
            two = one[:];
            one = side[:];
        elif side[0] > two[0]:
            two = side[:];
    
    # draw the entrances
    cv2.circle(rect, tup(one[1]), 5, (0,0,255), -1);
    cv2.circle(rect, tup(two[1]), 5, (0,0,255), -1);
    
    # show
    cv2.imshow("Image", img);
    cv2.imshow("Rect", rect);
    cv2.waitKey(0);