Sunday, December 6, 2015

Sudoku Solver

Hello Everyone!. Ever got bored of solving sudoku's. Then for you the solution is here, to create something that could solve and show us the solved sudoku . I have done it using python and an opencv python library. The functions used in this can be easily found out in the Opencv documentation refer them or comment below for any further clarification.

In this I have used KNearest(), a machine learning algorithm that is available in Opencv, which can be used to recognize stuffs but here I have used it to recognize the digits in the sudoku. Well Before that you have create your training data, which will be used for recognizing the digits.


Input Image
Int MAIN():
 This is the basic sudoku solver which does not require any complex image processing techniques because the image taken is a binary image without any noise. Hence just finding the contours will satisfy the needs.
Let's first look at training the OCR for recognizing the numbers in the given input image
Basically, training the OCR requires hundreds of images for it to be used in recognizing images of different types. Since the images we use are only computer typed digits and the given input image is assumed to be taken in the binary form we are going to use only 1 image for each digit.  I hope this enough for recognizing images which are in the binary form without any noise. Let's look forward towards training the OCR.

The first Part of the code reads the input image and converts the given image into gray.

The we find contours in the given image with the arguments saying the function to find contours in the tree form and to make simple approximation on the contours found, hence only minimum number of points required to draw the contour are saved.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from matplotlib import pyplot as plt
import numpy as np
import cv2
img = cv2.imread('sudoku.png',1)
frame=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
samples=np.empty((0,100))
responses=[]
keys = [i for i in range(48,58)]
_,cnt,hr = cv2.findContours(frame,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(img,cnt,-1,(255,0,0),2)
Image with contours

The below code snippet extracts each and every individual box of the sudoku and checks out whether the box considered has any child contour. If it does then the box having the child is extracted out and the user is asked to enter the response of the extracted image and the entered response is recorded in a variable. The sample used is resized into 10X10 image which is again reshaped into row array and its type converted and saved into a file. It's done so because the file size is reduced when the size of the image is small.
The sample image and its corresponding response is saved into a npz file.

Thus training the OCR can be accomplished.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
count=0
numb=[]
for i in range(len(cnt)):
 area=cv2.contourArea(cnt[i])
 if area>=40000:
  x,y,w,z=hr[0][i]
  if w!=-1:
   a,b,c,d=cv2.boundingRect(cnt[w])
   print c,d
   errx=100-c
   erry=180-d
   b=b-(erry/2)
   a=a-(errx/2)
   c=100
   d=180
   roi=frame[b:b+d,a:a+c]
   cv2.imshow("1",roi)
   cv2.rectangle(img,(a,b),(a+c,b+d),(0,255,0),4)
   q=cv2.resize(img,(400,400))
   cv2.imshow("frame",q)
   count=count+1
   key=cv2.waitKey(0)
   if key in keys:
    responses.append(int(chr(key)))
    roismall=cv2.resize(roi,(10,10))
    roismall=roismall.reshape(1,100)
    samples=np.append(samples,roismall,0)
    if key not in numb:
     numb.append(key)
     picname= chr(key)+".png"
     cv2.imwrite(picname,img[b:b+d,a:a+c])
plt.imshow(img),plt.title("frame")
plt.xticks(),plt.yticks()
plt.show()
cv2.waitKey(0)
print count
samples=np.array(samples,np.float32)
responses = np.array(responses,np.float32)
responses = responses.reshape((responses.size,1))
np.savez('knn.npz',train=samples,traindata=responses)
print "training complete"
SudokuTrain.py


Part-2:


The next part is loading the training data and extracting the individual boxes and processing them by converting the given images to the form it has been saved and then giving it to the findNearest function which gives the nearest  out an array containing the calculated result. The values recognized are stored in a matrix. The matrix is then given to the sudoku solving algorithm. The algorithm first checks out for an empty cell and assigns it a value from 1 to 9 which is passed to the a function which checks the validity. The function checks whether the assigned value is valid or not, if it is valid the recursively calls itself which again does the same job. If suppose the is not compatible with any of the values assigned for the function returns to its parent function which  was recursively called and then the previous function assigns another value to and the process repeats itself.

Output Image

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from matplotlib import pyplot as plt
import numpy as np
import cv2
img = cv2.imread('sudoku\sudoku.png',1)
deflt="D:\sudoku\\"
frame=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
print frame.shape
samples=np.empty((0,100))
responses=[]
keys = [i for i in range(48,58)]
_,cnt,hr = cv2.findContours(frame,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img,cnt,-1,(255,0,0),2)
count=0
model= cv2.ml.KNearest_create()
with np.load('D:\Sriram\pyprograms\sudoku\knn.npz') as data:
 print data.files
 train1=data['train']
 train_labels= data['traindata']
 print train1
 print train_labels
model.train(train1,cv2.ml.ROW_SAMPLE,train_labels)
matrix=np.zeros((9,9),np.uint8)
print matrix
for i in range(len(cnt)):
 area=cv2.contourArea(cnt[i])
 if area>=40000:
  x,y,w,z=hr[0][i]
  if w!=-1:
   a,b,c,d=cv2.boundingRect(cnt[w])
   #print c,d
   errx=100-c
   erry=180-d
   b=b-int(erry/2)
   a=a-int(errx/2)
   c=100
   d=180
   roi=frame[b:b+d,a:a+c]
   cv2.rectangle(img,(a,b),(a+c,b+d),(255,0,0),4)
   q=cv2.resize(img,(400,400))
   cv2.imshow("frame",q)
   roismall=cv2.resize(roi,(10,10))
   roismall=roismall.reshape(1,100)
   roismall1=np.float32(roismall)
   r=model.findNearest(roismall1,1)
   print int(r[1][0])
   count=count+1
   count1=81-count
   row=int(count1/9)
   col=int(count1%9)
   matrix[row][col]=int(r[1][0])
  else:
   count=count+1
   count1=81-count
   row=int(count1/9)
   col=int(count1%9)
   matrix[row][col]=0
print count
#print matrix
def nextcell(matrix,i,j):
 for x in range(i,9):
  for y in range(j,9):
   if matrix[x][y]==0:
    return x,y
 for x in range(0,9):
  for y in range(0,9):
   if matrix[x][y]==0:
    return x,y
 return -1,-1
def checkcell(matrix,i,j,val):
 rowcheck=colcheck=1
 for x in range(9):
  if val==matrix[i][x]:
   rowcheck=0
 if rowcheck==1:
  for x in range(9):
   if val==matrix[x][j]:
    colcheck=0
 if colcheck==1 and rowcheck==1:
  topx=3*(i/3)
  topy=3*(j/3)
  for x in range(topx,topx+3):
   for y in range(topy,topy+3):
    if matrix[x][y]==val:
     return False
  return True
 return False
def sudokusolve(matrix,i=0,j=0):
 i,j=nextcell(matrix,i,j)
 if i==-1:
  return True
 for val in range(1,10):
  if checkcell(matrix,i,j,val):
   matrix[i][j]=val
   if sudokusolve(matrix,i,j):
    return True
   matrix[i][j]=0
 return False

if sudokusolve(matrix,0,0):
 print matrix
 print "Eureka"
else:
 print "Could not solve"
count=0
val=0
for i in range(len(cnt)):
 area=cv2.contourArea(cnt[i])
 if area>=40000:
  x,y,w,z=hr[0][i]
  if w!=-1:
   count=count+1
   continue
  else:
   count=count+1
   count1=81-count
   row=int(count1/9)
   col=int(count1%9)
   val=matrix[row][col]
   s=str(val)
   a,b,c,d=cv2.boundingRect(cnt[i])
   im=cv2.imread(deflt+s+".png",1)
   img[b+10:b+190,a+50:a+150]=im
plt.imshow(img),plt.title("frame")
plt.xticks([]),plt.yticks([])
plt.show()
cv2.destroyAllWindows()
Test.py

Thank you
Happy Coding.
Cheers!!!
#IIE

No comments:

Post a Comment