Spaces:
Sleeping
Sleeping
| 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() |