Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect Sudoku grid board in OpenCV

I'm working on a personal project using opencv in python. Want to detect a sudoku grid.

The original image is:

enter image description here

So far I have created this:

enter image description here

Then tried to select a big blob. Result may be similar to this:

enter image description here

I got a black image as result:

enter image description here

The code is:

import cv2
import numpy as np

def find_biggest_blob(outerBox):
   max = -1
   maxPt = (0, 0)

   h, w = outerBox.shape[:2]
   mask = np.zeros((h + 2, w + 2), np.uint8)

   for y in range(0, h):
     for x in range(0, w):

       if outerBox[y, x] >= 128:

         area = cv2.floodFill(outerBox, mask, (x, y), (0, 0, 64))


   #cv2.floodFill(outerBox, mask, maxPt, (255, 255, 255))

   image_path = 'Images/Results/sudoku-find-biggest-blob.jpg'

   cv2.imwrite(image_path, outerBox)

   cv2.imshow(image_path, outerBox)


 def main():
   image = cv2.imread('Images/Test/sudoku-grid-detection.jpg', 0)

   find_biggest_blob(image)

   cv2.waitKey(0)

   cv2.destroyAllWindows() 


if __name__ == '__main__':
   main()

The code in repl is: https://repl.it/@gmunumel/SudokuSolver

Any idea?

like image 347
Gabriel Muñumel Avatar asked Aug 24 '19 08:08

Gabriel Muñumel


People also ask

What is Sudoku grid?

Terminology and grid layout. A Sudoku (i.e. the puzzle) is a partially completed grid. A grid has 9 rows, 9 columns and 9 boxes, each having 9 cells (81 total). Boxes can also be called blocks or regions. Three horizontally adjacent blocks are a band, and three vertically adjacent blocks are a stack.

What is the object of Sudoku?

The object of the puzzle is to fill the remaining squares, using all the numbers 1–9 exactly once in each row, column, and the nine 3 × 3 subgrids. Sudoku is based entirely on logic, without any arithmetic involved, and the level of difficulty is determined by the quantity and positions of the original numbers.

How many possible Sudoku?

There are exactly 6, 670, 903, 752, 021, 072, 936, 960 possible solutions to Sudoku (about 10^21) .


1 Answers

Here's an approach:

  • Convert image to grayscale and median blur to smooth image
  • Adaptive threshold to obtain binary image
  • Find contours and filter for largest contour
  • Perform perspective transform to obtain top-down view

After converting to grayscale and median blurring, we adaptive threshold to obtain a binary image

enter image description here

Next we find contours and filter using contour area. Here's the detected board

enter image description here

Now to get a top-down view of the image, we perform a perspective transform. Here's the result

enter image description here

import cv2
import numpy as np

def perspective_transform(image, corners):
    def order_corner_points(corners):
        # Separate corners into individual points
        # Index 0 - top-right
        #       1 - top-left
        #       2 - bottom-left
        #       3 - bottom-right
        corners = [(corner[0][0], corner[0][1]) for corner in corners]
        top_r, top_l, bottom_l, bottom_r = corners[0], corners[1], corners[2], corners[3]
        return (top_l, top_r, bottom_r, bottom_l)

    # Order points in clockwise order
    ordered_corners = order_corner_points(corners)
    top_l, top_r, bottom_r, bottom_l = ordered_corners

    # Determine width of new image which is the max distance between 
    # (bottom right and bottom left) or (top right and top left) x-coordinates
    width_A = np.sqrt(((bottom_r[0] - bottom_l[0]) ** 2) + ((bottom_r[1] - bottom_l[1]) ** 2))
    width_B = np.sqrt(((top_r[0] - top_l[0]) ** 2) + ((top_r[1] - top_l[1]) ** 2))
    width = max(int(width_A), int(width_B))

    # Determine height of new image which is the max distance between 
    # (top right and bottom right) or (top left and bottom left) y-coordinates
    height_A = np.sqrt(((top_r[0] - bottom_r[0]) ** 2) + ((top_r[1] - bottom_r[1]) ** 2))
    height_B = np.sqrt(((top_l[0] - bottom_l[0]) ** 2) + ((top_l[1] - bottom_l[1]) ** 2))
    height = max(int(height_A), int(height_B))

    # Construct new points to obtain top-down view of image in 
    # top_r, top_l, bottom_l, bottom_r order
    dimensions = np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], 
                    [0, height - 1]], dtype = "float32")

    # Convert to Numpy format
    ordered_corners = np.array(ordered_corners, dtype="float32")

    # Find perspective transform matrix
    matrix = cv2.getPerspectiveTransform(ordered_corners, dimensions)

    # Return the transformed image
    return cv2.warpPerspective(image, matrix, (width, height))

image = cv2.imread('1.jpg')
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray, 3)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,3)

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)

for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.015 * peri, True)
    transformed = perspective_transform(original, approx)
    break

cv2.imshow('transformed', transformed)
cv2.imwrite('board.png', transformed)
cv2.waitKey()
like image 185
nathancy Avatar answered Sep 30 '22 10:09

nathancy