File size: 6,519 Bytes
1a5455d
2b4a005
 
1a5455d
0f53078
1a5455d
484d173
b75b567
1b4dd95
cfd19ee
ac11265
 
 
 
 
747ff01
d6f1324
cfd19ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
 
 
 
 
 
b7b5aa9
cfd19ee
 
 
 
 
 
 
 
b7b5aa9
 
cfd19ee
 
87b01f5
 
 
 
 
 
cfd19ee
 
 
87b01f5
 
 
 
 
 
 
 
 
 
cfd19ee
 
87b01f5
 
 
 
 
 
 
 
 
cfd19ee
87b01f5
cfd19ee
 
 
87b01f5
 
 
 
 
 
cfd19ee
 
87b01f5
 
 
 
 
 
 
 
 
 
 
 
 
4360d4c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfd19ee
2b4a005
 
1a5455d
2b4a005
1a5455d
2b4a005
05bfa3c
8a83802
ac11265
 
05bfa3c
ac11265
747ff01
2b4a005
b7b5aa9
2b4a005
b7b5aa9
2b4a005
 
 
b7b5aa9
2b4a005
 
 
 
 
 
 
 
 
 
 
 
ac11265
 
 
 
 
 
 
11355bf
1a5455d
2b4a005
 
1a5455d
2b4a005
 
 
b7b5aa9
2b4a005
 
6ba792b
 
143b687
6ba792b
 
 
 
8a83802
81a35ba
2b4a005
81a35ba
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
import streamlit as st
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):
    if len(image.shape) == 2:
        gray = image  # No need to convert, it's already grayscale
    else:
    # Convert RGB image to grayscale using OpenCV
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) 
#     blur = cv2.GaussianBlur(gray, (3,3),6) 
    blur = cv2.bilateralFilter(gray,6,3,3)
    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 display(quiz):
    for row in range(9):
        if row % 3 == 0 and row != 0:
            print("....................")

        for col in range(9):
            if col % 3 == 0 and col != 0:
                print("|", end=" ")

            if col == 8:
                print(quiz[row][col])
            else:
                print(str(quiz[row][col]) + " ", end="")

    
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)
        col1.subheader("Sudoku Puzzle:")
        # puzzle = cv2.imread(uploaded_file)
        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)
            if len(su_imagewrap.shape) == 2:
                su_imagewrap = su_imagewrap  # No need to convert, it's already grayscale
            else:
                # Convert RGB image to grayscale using OpenCV
                su_imagewrap = cv2.cvtColor(su_imagewrap, cv2.COLOR_RGB2GRAY)
            _, 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):
           
            col2.subheader("Sudoku Solved:")
            col2.table(grid)
        
        else:
            st.write("No Solution Found.")

if __name__ == '__main__':
    main()