File size: 5,663 Bytes
1a5455d
2b4a005
 
 
 
 
1a5455d
0f53078
1a5455d
b75b567
1b4dd95
cfd19ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
b7b5aa9
 
cfd19ee
 
87b01f5
 
 
 
 
 
cfd19ee
 
 
87b01f5
 
 
 
 
 
 
 
 
 
cfd19ee
 
87b01f5
 
 
 
 
 
 
 
 
cfd19ee
87b01f5
cfd19ee
 
 
87b01f5
 
 
 
 
 
cfd19ee
 
87b01f5
 
 
 
 
 
 
 
 
 
 
 
 
cfd19ee
2b4a005
 
1a5455d
2b4a005
1a5455d
2b4a005
05bfa3c
53610b5
05bfa3c
 
2b4a005
b7b5aa9
2b4a005
b7b5aa9
2b4a005
 
 
b7b5aa9
2b4a005
 
 
 
 
 
 
 
 
 
 
 
 
11355bf
1a5455d
2b4a005
 
1a5455d
2b4a005
 
 
b7b5aa9
2b4a005
 
122de80
7fd7ca4
 
 
cb54a47
2b4a005
38f06f9
2b4a005
 
 
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import streamlit as st
import seaborn as sns
import matplotlib.pyplot as plt
import os, random
import cv2
from tensorflow.keras.models import load_model
from PIL import Image
import numpy as np

model = load_model('sudoku_net.h5')

def preprocess(image):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 
    blur = cv2.GaussianBlur(gray, (3,3),6) 
    #blur = cv2.bilateralFilter(gray,9,75,75)
    threshold_img = cv2.adaptiveThreshold(blur,255,1,1,11,2)
    return threshold_img

def main_outline(contour):
    biggest = np.array([])
    max_area = 0
    for i in contour:
        area = cv2.contourArea(i)
        if area >50:
            peri = cv2.arcLength(i, True)
            approx = cv2.approxPolyDP(i , 0.02* peri, True)
            if area > max_area and len(approx) ==4:
                biggest = approx
                max_area = area
    return biggest ,max_area

def reframe(points):
    points = points.reshape((4, 2))
    points_new = np.zeros((4,1,2),dtype = np.int32)
    add = points.sum(1)
    points_new[0] = points[np.argmin(add)]
    points_new[3] = points[np.argmax(add)]
    diff = np.diff(points, axis =1)
    points_new[1] = points[np.argmin(diff)]
    points_new[2] = points[np.argmax(diff)]
    return points_new


def splitcells(img):
    rows = np.vsplit(img,9)
    boxes = []
    for r in rows:
        cols = np.hsplit(r,9)
        for box in cols:
            boxes.append(box)
    return boxes


def CropCell(cells):
    Cells_croped = []
    for image in cells:
        
        img = np.array(image)
        img = img[4:46, 6:46]
        img = Image.fromarray(img)
        Cells_croped.append(img)
        
    return Cells_croped


def read_cells(cell,model):

    result = []
    for image in cell:
        # preprocess the image as it was in the model 
        img = np.asarray(image)
        img = img[4:img.shape[0] - 4, 4:img.shape[1] -4]
        img = cv2.resize(img, (32, 32))
        img = img / 255
        img = img.reshape(1, 32, 32, 1)
        # getting predictions and setting the values if probabilities are above 65% 
        
        predictions = model.predict(img)
        classIndex = np.argmax(predictions, axis=1)
        probabilityValue = np.amax(predictions)
        
        if probabilityValue > 0.65:
            result.append(classIndex[0])
        else:
            result.append(0)
    return result



#This function finds the next box to solve 

def find_empty(board):
    for i in range(len(board)):
        for j in range(len(board)):
            if board[i][j] == 0:
               return i,j
    return None

#Function to fill in the possible values by evaluating rows collumns and smaller cells

def valid(board, num, pos):
    # check row
    for i in range(len(board)):
        if board[pos[0]][i]==num and pos[1]!=i:
                 return False
    
    
    # check column
    for i in range(len(board)):
        if board[i][pos[1]]==num and pos[0]!=i:
            return False
        
   
        
    # check 3X3 cube
    box_x=pos[0]//3
    box_y=pos[1]//3
    
    for i in range(box_x*3,box_x+3):
        for j in range(box_y*3,box_y+3):
            if board[i][j]==num and [i,j]!=pos:
                return False
   
    return True



#function to loop over untill a valid answer is found. 

def solve(board):
    find=find_empty(board)
    if not find:
        return True
    else:
        row,col=find
        
    
    for i in range(1,10):
        if valid(board,i,[row,col]):
            board[row][col]=i
            
            if solve(board):
                return True
            
            board[row][col]=0
    return False

    
def main():
    st.title('Sudoku Solver')

    uploaded_file = st.file_uploader("Upload an image", type=["jpg", "jpeg", "png"])

    if uploaded_file is not None:
        col1, col2 = st.columns(2)
        puzzle = Image.open(uploaded_file)
        col1.image(puzzle, use_column_width=True)
        puzzle = np.asarray(puzzle)
        # Resizing puzzle to be solved
        puzzle = cv2.resize(puzzle, (450,450))
        # Preprocessing Puzzle 
        su_puzzle = preprocess(puzzle)
        
        # Finding the outline of the sudoku puzzle in the image
        su_contour_1= su_puzzle.copy()
        su_contour_2= puzzle.copy()
        su_contour, hierarchy = cv2.findContours(su_puzzle,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
        cv2.drawContours(su_contour_1, su_contour,-1,(0,255,0),3)
        
        black_img = np.zeros((450,450,3), np.uint8)
        su_biggest, su_maxArea = main_outline(su_contour)
        if su_biggest.size != 0:
            su_biggest = reframe(su_biggest)
            cv2.drawContours(su_contour_2,su_biggest,-1, (0,255,0),10)
            su_pts1 = np.float32(su_biggest)
            su_pts2 = np.float32([[0,0],[450,0],[0,450],[450,450]])
            su_matrix = cv2.getPerspectiveTransform(su_pts1,su_pts2)  
            su_imagewrap = cv2.warpPerspective(puzzle,su_matrix,(450,450))
            su_imagewrap =cv2.cvtColor(su_imagewrap, cv2.COLOR_BGR2GRAY)
            _, su_imagewrap = cv2.threshold(su_imagewrap, 127, 255, cv2.THRESH_BINARY)

        sudoku_cell = splitcells(su_imagewrap)
        sudoku_cell_croped= CropCell(sudoku_cell)

        grid = read_cells(sudoku_cell_croped, model)
        grid = np.asarray(grid)
        grid = np.reshape(grid,(9,9))
        solve(grid)
    
        # Display the solution or appropriate message
        if solve(grid):
            with col2:
                st.subheader("Sudoku Solved:")
                solve(grid)
            
        else:
            col2.write("No solution found.")

if __name__ == '__main__':
    main()