SudokuCV / app.py
gurwindersingh's picture
Update app.py
d6f1324 verified
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()