diff --git a/OpenCV Projects/Sudoku Solver/README.md b/OpenCV Projects/Sudoku Solver/README.md new file mode 100644 index 000000000..a60d7d1bd --- /dev/null +++ b/OpenCV Projects/Sudoku Solver/README.md @@ -0,0 +1,28 @@ +# Sudoku Solver + +This project is a Sudoku solver that reads a Sudoku puzzle from an image, recognizes the digits using a pre-trained OCR model, and then solves the puzzle. The final solved puzzle is displayed by overlaying the solution onto the original image. + +## Features +- Extracts the Sudoku grid from an image using image processing techniques. +- Recognizes the digits in the puzzle using a pre-trained OCR model. +- Solves the puzzle using a backtracking algorithm. +- Overlays the solution onto the original image. + +## How it Works + +### 1. Image Preprocessing: + The input image is processed to detect the Sudoku grid. Contours are used to locate the largest rectangular region in the image, which is assumed to be the Sudoku puzzle. + +### 2. Digit Recognition: + The extracted grid is split into 81 cells, and each cell is resized to match the input size of the pre-trained OCR model. The model predicts the digits in the puzzle, assigning a number to each cell or marking it as empty. + +### 3. Puzzle Solving: + The recognized digits are passed into a backtracking algorithm, which solves the Sudoku puzzle. If the puzzle cannot be solved, the model might have misread a digit. + +### 4. Output: + The solved digits are overlaid back onto the original image using inverse perspective transformation, showing the solution in the correct position. + +## Usage + +1. Place your Sudoku puzzle image in the project directory (e.g., `sudoku1.jpg`). +2. Run the script. diff --git a/OpenCV Projects/Sudoku Solver/model-OCR.h5 b/OpenCV Projects/Sudoku Solver/model-OCR.h5 new file mode 100644 index 000000000..6dcfa77e8 Binary files /dev/null and b/OpenCV Projects/Sudoku Solver/model-OCR.h5 differ diff --git a/OpenCV Projects/Sudoku Solver/sudoku solver.ipynb b/OpenCV Projects/Sudoku Solver/sudoku solver.ipynb new file mode 100644 index 000000000..64256525b --- /dev/null +++ b/OpenCV Projects/Sudoku Solver/sudoku solver.ipynb @@ -0,0 +1,835 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "935b6e5a-3f98-4bb9-8e20-23783323be7d", + "metadata": {}, + "outputs": [], + "source": [ + "# Importing all necessary libraries\n", + "\n", + "import cv2\n", + "import numpy as np\n", + "from tensorflow.keras.models import load_model\n", + "import imutils" + ] + }, + { + "cell_type": "markdown", + "id": "27b0c5ea-c73b-4c71-91ba-7b8293f9d466", + "metadata": {}, + "source": [ + "### Adding useful functions for solving" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0dd6cca2-1817-4cb1-a938-8cbe69caacdf", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to check for empty block\n", + "\n", + "def find_empty(board):\n", + " for i in range(len(board)):\n", + " for j in range(len(board[0])):\n", + " if board[i][j] == 0:\n", + " return (i, j) # row, col" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f2314efa-d7af-4fc3-91af-8a4079e68458", + "metadata": {}, + "outputs": [], + "source": [ + "# Function to validate whether number repeats inside row, column and box\n", + "\n", + "def valid(board, num, pos):\n", + " # Check row\n", + " for i in range(len(board[0])):\n", + " if board[pos[0]][i] == num and pos[1] != i:\n", + " return False\n", + "\n", + " # Check column\n", + " for i in range(len(board)):\n", + " if board[i][pos[1]] == num and pos[0] != i:\n", + " return False\n", + "\n", + " # Check box\n", + " box_x = pos[1] // 3\n", + " box_y = pos[0] // 3\n", + "\n", + " for i in range(box_y*3, box_y*3 + 3):\n", + " for j in range(box_x * 3, box_x*3 + 3):\n", + " if board[i][j] == num and (i,j) != pos:\n", + " return False\n", + "\n", + " return True" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "98c6d394-a704-46b0-89ee-c63422dcad5a", + "metadata": {}, + "outputs": [], + "source": [ + "def solve(board):\n", + " find = find_empty(board)\n", + " if not find:\n", + " return True\n", + " else:\n", + " row, col = find\n", + "\n", + " for i in range(1,10):\n", + " if valid(board, i, (row, col)):\n", + " board[row][col] = i\n", + "\n", + " if solve(board):\n", + " return True\n", + " board[row][col] = 0\n", + " return False" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4438c9b5-c5a8-4025-8011-be9c11f0554a", + "metadata": {}, + "outputs": [], + "source": [ + "# Takes a matrix and returns a fully solved sudoku board\n", + "\n", + "def get_board(bo):\n", + " if solve(bo):\n", + " return bo\n", + " else:\n", + " raise ValueError" + ] + }, + { + "cell_type": "markdown", + "id": "37d6f170-fd76-4fbe-bf52-208ff641951b", + "metadata": {}, + "source": [ + "### Further implementations" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "77fed785-6240-440d-8164-b15c345ffabe", + "metadata": {}, + "outputs": [], + "source": [ + "classes = np.arange(0, 10)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "88810654-9ad2-4325-bf0f-dab1a18b327d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\shawn\\anaconda3\\Lib\\site-packages\\keras\\src\\layers\\convolutional\\base_conv.py:107: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.\n", + " super().__init__(activity_regularizer=activity_regularizer, **kwargs)\n", + "C:\\Users\\shawn\\anaconda3\\Lib\\site-packages\\keras\\src\\optimizers\\base_optimizer.py:33: UserWarning: Argument `decay` is no longer supported and will be ignored.\n", + " warnings.warn(\n", + "WARNING:absl:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.\n", + "WARNING:absl:Error in loading the saved optimizer state. As a result, your model is starting with a freshly initialized optimizer.\n" + ] + }, + { + "data": { + "text/html": [ + "
Model: \"sequential\"\n",
+ "
\n"
+ ],
+ "text/plain": [
+ "\u001b[1mModel: \"sequential\"\u001b[0m\n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓\n", + "┃ Layer (type) ┃ Output Shape ┃ Param # ┃\n", + "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩\n", + "│ conv2d (Conv2D) │ (None, 46, 46, 32) │ 320 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_1 (Conv2D) │ (None, 44, 44, 64) │ 18,496 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_2 (Conv2D) │ (None, 21, 21, 64) │ 36,928 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_3 (Conv2D) │ (None, 19, 19, 128) │ 73,856 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout (Dropout) │ (None, 19, 19, 128) │ 0 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_4 (Conv2D) │ (None, 17, 17, 128) │ 147,584 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_5 (Conv2D) │ (None, 8, 8, 64) │ 73,792 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_6 (Conv2D) │ (None, 6, 6, 16) │ 9,232 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_1 (Dropout) │ (None, 6, 6, 16) │ 0 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ flatten (Flatten) │ (None, 576) │ 0 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense (Dense) │ (None, 500) │ 288,500 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_2 (Dropout) │ (None, 500) │ 0 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense_1 (Dense) │ (None, 50) │ 25,050 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_3 (Dropout) │ (None, 50) │ 0 │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense_2 (Dense) │ (None, 10) │ 510 │\n", + "└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘\n", + "\n" + ], + "text/plain": [ + "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓\n", + "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\n", + "┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩\n", + "│ conv2d (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m46\u001b[0m, \u001b[38;5;34m46\u001b[0m, \u001b[38;5;34m32\u001b[0m) │ \u001b[38;5;34m320\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_1 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m44\u001b[0m, \u001b[38;5;34m44\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m18,496\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_2 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m21\u001b[0m, \u001b[38;5;34m21\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m36,928\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_3 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m19\u001b[0m, \u001b[38;5;34m19\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m73,856\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m19\u001b[0m, \u001b[38;5;34m19\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_4 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m17\u001b[0m, \u001b[38;5;34m17\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m147,584\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_5 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m8\u001b[0m, \u001b[38;5;34m8\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m73,792\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ conv2d_6 (\u001b[38;5;33mConv2D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m6\u001b[0m, \u001b[38;5;34m6\u001b[0m, \u001b[38;5;34m16\u001b[0m) │ \u001b[38;5;34m9,232\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_1 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m6\u001b[0m, \u001b[38;5;34m6\u001b[0m, \u001b[38;5;34m16\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ flatten (\u001b[38;5;33mFlatten\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m576\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m500\u001b[0m) │ \u001b[38;5;34m288,500\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_2 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m500\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense_1 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m50\u001b[0m) │ \u001b[38;5;34m25,050\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dropout_3 (\u001b[38;5;33mDropout\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m50\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │\n", + "├──────────────────────────────────────┼─────────────────────────────┼─────────────────┤\n", + "│ dense_2 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m10\u001b[0m) │ \u001b[38;5;34m510\u001b[0m │\n", + "└──────────────────────────────────────┴─────────────────────────────┴─────────────────┘\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Total params: 674,270 (2.57 MB)\n", + "\n" + ], + "text/plain": [ + "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m674,270\u001b[0m (2.57 MB)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Trainable params: 674,268 (2.57 MB)\n", + "\n" + ], + "text/plain": [ + "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m674,268\u001b[0m (2.57 MB)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Non-trainable params: 0 (0.00 B)\n", + "\n" + ], + "text/plain": [ + "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
Optimizer params: 2 (12.00 B)\n", + "\n" + ], + "text/plain": [ + "\u001b[1m Optimizer params: \u001b[0m\u001b[38;5;34m2\u001b[0m (12.00 B)\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "model = load_model('model-OCR.h5') # Loading a pre trained optical character recognition model to identify numbers\n", + "print(model.summary())" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "80de7585-cfe7-404a-8c3f-cefa5b532736", + "metadata": {}, + "outputs": [], + "source": [ + "input_size = 48" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1db38472-77c8-42bb-ae50-df40a5117377", + "metadata": {}, + "outputs": [], + "source": [ + "def get_perspective(img, location, height = 900, width = 900):\n", + " \"\"\"Takes an image and location os interested region.\n", + " And return the only the selected region with a perspective transformation\"\"\"\n", + " pts1 = np.float32([location[0], location[3], location[1], location[2]])\n", + " pts2 = np.float32([[0, 0], [width, 0], [0, height], [width, height]])\n", + "\n", + " # Apply Perspective Transform Algorithm\n", + " matrix = cv2.getPerspectiveTransform(pts1, pts2)\n", + " result = cv2.warpPerspective(img, matrix, (width, height))\n", + " return result\n", + "\n", + "def get_InvPerspective(img, masked_num, location, height = 900, width = 900):\n", + " \"\"\"Takes original image as input\"\"\"\n", + " pts1 = np.float32([[0, 0], [width, 0], [0, height], [width, height]])\n", + " pts2 = np.float32([location[0], location[3], location[1], location[2]])\n", + "\n", + " # Apply Perspective Transform Algorithm\n", + " matrix = cv2.getPerspectiveTransform(pts1, pts2)\n", + " result = cv2.warpPerspective(masked_num, matrix, (img.shape[1], img.shape[0]))\n", + " return result" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0f3b9929-b851-4980-84d2-d1f7a6e07ca2", + "metadata": {}, + "outputs": [], + "source": [ + "def find_board(img):\n", + " \"\"\"Takes an image as input and finds a sudoku board inside of the image\"\"\"\n", + " gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n", + " bfilter = cv2.bilateralFilter(gray, 13, 20, 20)\n", + " edged = cv2.Canny(bfilter, 30, 180)\n", + " keypoints = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n", + " contours = imutils.grab_contours(keypoints)\n", + "\n", + " newimg = cv2.drawContours(img.copy(), contours, -1, (0, 255, 0), 3)\n", + " # cv2.imshow(\"Contour\", newimg)\n", + "\n", + "\n", + " contours = sorted(contours, key=cv2.contourArea, reverse=True)[:15]\n", + " location = None\n", + " \n", + " # Finds rectangular contour\n", + " for contour in contours:\n", + " approx = cv2.approxPolyDP(contour, 15, True)\n", + " if len(approx) == 4:\n", + " location = approx\n", + " break\n", + " result = get_perspective(img, location)\n", + " return result, location" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "25cacc85-9f57-4c09-ab12-dd409a48199c", + "metadata": {}, + "outputs": [], + "source": [ + "# split the board into 81 individual images\n", + "def split_boxes(board):\n", + " \"\"\"Takes a sudoku board and split it into 81 cells. \n", + " each cell contains an element of that board either given or an empty cell.\"\"\"\n", + " rows = np.vsplit(board,9)\n", + " boxes = []\n", + " for r in rows:\n", + " cols = np.hsplit(r,9)\n", + " for box in cols:\n", + " box = cv2.resize(box, (input_size, input_size))/255.0\n", + " # cv2.imshow(\"Splitted block\", box)\n", + " # cv2.waitKey(50)\n", + " boxes.append(box)\n", + " cv2.destroyAllWindows()\n", + " return boxes\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4e4a5964-37b1-45e1-be14-e3ed20471900", + "metadata": {}, + "outputs": [], + "source": [ + "def displayNumbers(img, numbers, color=(255, 165, 0)):\n", + " \"\"\"Displays 81 numbers in an image or mask at the same position of each cell of the board\"\"\"\n", + " W = int(img.shape[1]/9)\n", + " H = int(img.shape[0]/9)\n", + " for i in range (9):\n", + " for j in range (9):\n", + " if numbers[(j*9)+i] !=0:\n", + " cv2.putText(img, str(numbers[(j*9)+i]), (i*W+int(W/2)-int((W/4)), int((j+0.7)*H)), cv2.FONT_HERSHEY_COMPLEX, 2, color, 2, cv2.LINE_AA)\n", + " return img" + ] + }, + { + "cell_type": "markdown", + "id": "26fbf5c2-0070-4b7f-88f2-76a26c5c715c", + "metadata": {}, + "source": [ + "### Testing the model and the functions" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "191c96b2-2098-4b20-ae74-bc4380680270", + "metadata": {}, + "outputs": [], + "source": [ + "# Read image\n", + "img = cv2.imread('sudoku1.jpg')" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "ae836dd7-376c-4813-a08d-73e762b30cb6", + "metadata": {}, + "outputs": [], + "source": [ + "# extract board from input image\n", + "board, location = find_board(img)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "e0d49f4b-eac7-40b5-bab4-0a0345b51630", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(900, 900)\n" + ] + } + ], + "source": [ + "gray = cv2.cvtColor(board, cv2.COLOR_BGR2GRAY)\n", + "print(gray.shape)\n", + "rois = split_boxes(gray)\n", + "rois = np.array(rois).reshape(-1, input_size, input_size, 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9fd1582a-6a69-4070-8a04-f28d3c3fa3ef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[1m3/3\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m0s\u001b[0m 61ms/step\n", + "[[1.00000000e+00 1.81138393e-08 1.17702639e-10 9.48800830e-11\n", + " 4.66661345e-12 1.94069941e-10 1.61227254e-10 6.19362073e-09\n", + " 6.59010138e-11 4.20535473e-09]\n", + " [1.47242956e-14 8.91949329e-13 2.55275072e-08 9.99999881e-01\n", + " 3.03975352e-11 5.43944578e-09 2.34189431e-14 3.14115270e-12\n", + " 8.63154810e-08 9.91372817e-11]\n", + " [9.50243356e-13 3.60882250e-13 2.34318875e-08 4.71955114e-12\n", + " 5.55219369e-12 5.27075317e-17 3.46211599e-20 3.30960093e-10\n", + " 6.69457935e-12 1.00000000e+00]\n", + " [1.04127740e-09 9.99999881e-01 4.36704362e-12 2.79899700e-14\n", + " 1.65201874e-09 2.79763734e-13 6.05068431e-14 7.66247226e-08\n", + " 4.74961630e-14 1.15957854e-10]\n", + " [1.00000000e+00 8.05741140e-10 3.49267273e-12 2.91280975e-12\n", + " 6.99971900e-14 5.97130765e-12 4.13649583e-12 1.88298613e-10\n", + " 1.91965411e-12 1.64973840e-10]\n", + " [1.00000000e+00 1.48447432e-09 4.08541213e-12 2.51807342e-12\n", + " 1.39817520e-13 7.36998136e-12 6.46519860e-12 3.20873605e-10\n", + " 2.04210910e-12 1.89087523e-10]\n", + " [1.00000000e+00 2.35324715e-09 7.12158717e-12 4.72184705e-12\n", + " 2.72077445e-13 1.25280116e-11 1.07040730e-11 5.38699252e-10\n", + " 3.86252055e-12 3.67173264e-10]\n", + " [1.00000000e+00 1.25425870e-09 3.02500342e-12 1.57490145e-12\n", + " 1.20167331e-13 5.11546621e-12 5.48012417e-12 3.15884485e-10\n", + " 1.31954918e-12 1.27118899e-10]\n", + " [1.00000000e+00 3.68809561e-09 1.55201841e-11 5.94521481e-12\n", + " 7.10093984e-13 1.70560267e-11 2.24188706e-11 1.55217383e-09\n", + " 5.29551576e-12 3.37712441e-10]\n", + " [2.04866527e-10 1.67712333e-08 4.76627696e-11 2.37324978e-14\n", + " 1.00000000e+00 6.67619511e-15 4.77941171e-08 2.41930000e-11\n", + " 3.94767802e-10 2.72722800e-09]\n", + " [1.00000000e+00 7.47707762e-09 4.59281189e-11 1.89408714e-11\n", + " 2.41086946e-12 6.13508619e-11 7.86132201e-11 6.19953022e-09\n", + " 2.24428722e-11 2.33016895e-09]\n", + " [1.98186668e-17 6.15333089e-23 4.56222923e-14 8.12837020e-10\n", + " 4.67799250e-16 2.87077928e-17 1.39234649e-14 3.91983024e-20\n", + " 1.00000000e+00 1.67162464e-11]\n", + " [1.00000000e+00 4.59152005e-09 2.81719578e-11 1.39982045e-11\n", + " 1.02171841e-12 2.97379274e-11 3.33793687e-11 1.92197991e-09\n", + " 1.17306555e-11 8.89721463e-10]\n", + " [1.33567903e-08 1.10407733e-12 2.23906120e-08 5.44287272e-12\n", + " 1.26116770e-08 1.36645963e-06 9.99997973e-01 1.40524203e-12\n", + " 5.15613067e-07 6.15134610e-08]\n", + " [1.00000000e+00 1.54261426e-09 4.77596695e-12 1.74165196e-12\n", + " 2.06239214e-13 5.58891345e-12 7.56439529e-12 5.59822133e-10\n", + " 1.73251409e-12 1.64976671e-10]\n", + " [1.00000000e+00 2.34091835e-09 7.38794009e-12 4.10122197e-12\n", + " 3.00684770e-13 1.19160220e-11 1.11568090e-11 6.80869416e-10\n", + " 3.59637363e-12 3.75123488e-10]\n", + " [1.00000000e+00 1.32796385e-09 2.73145634e-12 8.26479022e-13\n", + " 1.59717441e-13 4.51925086e-12 7.04260652e-12 6.46910359e-10\n", + " 9.10659460e-13 1.16416377e-10]\n", + " [7.91579780e-08 4.74538941e-09 9.99999523e-01 1.36168396e-07\n", + " 6.10998541e-08 1.91019023e-08 6.73562317e-09 2.22071549e-07\n", + " 6.46073017e-09 1.71536424e-10]\n", + " [1.54724333e-07 4.43551151e-09 9.99996781e-01 2.54040941e-07\n", + " 3.04350785e-08 5.19094234e-09 1.43963186e-09 2.76117680e-06\n", + " 3.93276700e-08 2.29541008e-09]\n", + " [1.00000000e+00 1.15325616e-08 6.88402252e-11 3.12236868e-11\n", + " 4.26938996e-12 9.96164679e-11 1.24143182e-10 8.04032840e-09\n", + " 4.02295870e-11 4.05122158e-09]\n", + " [1.00000000e+00 4.92530594e-09 4.94071208e-11 1.06964194e-11\n", + " 1.99593792e-12 2.76204771e-11 5.00830453e-11 5.12303178e-09\n", + " 1.35497316e-11 1.10713683e-09]\n", + " [3.03163939e-12 5.95731364e-12 3.64998164e-06 2.75094038e-07\n", + " 7.57723537e-11 9.99995947e-01 1.57338008e-07 8.46037518e-10\n", + " 1.49876958e-10 4.74258299e-10]\n", + " [1.69774950e-17 1.59236749e-22 9.57069303e-14 2.30105912e-09\n", + " 1.16045864e-15 1.01668928e-16 2.93291562e-14 1.26501804e-19\n", + " 1.00000000e+00 3.13839406e-11]\n", + " [1.00000000e+00 7.09047887e-09 4.50475213e-11 8.54939296e-12\n", + " 2.85749549e-12 3.32564636e-11 6.60931032e-11 6.12784135e-09\n", + " 1.20126426e-11 8.89497531e-10]\n", + " [1.61131854e-11 7.27235783e-09 2.44060744e-10 2.08306764e-14\n", + " 1.57648714e-10 2.54908589e-13 3.73783036e-13 1.00000000e+00\n", + " 4.15293901e-16 4.52550705e-11]\n", + " [1.00000000e+00 2.19231278e-09 2.80986410e-12 7.37357712e-13\n", + " 3.00289334e-13 4.56529823e-12 9.86846715e-12 8.97663111e-10\n", + " 1.00648450e-12 1.24347935e-10]\n", + " [1.00000000e+00 1.47059220e-09 3.97275919e-12 1.27648196e-12\n", + " 2.08405396e-13 5.94117941e-12 8.54937648e-12 8.45087833e-10\n", + " 1.31451252e-12 1.47473464e-10]\n", + " [2.23638784e-17 5.20616998e-24 8.35837713e-15 1.72723683e-10\n", + " 1.71976838e-17 9.41044257e-18 9.83273453e-15 2.37569180e-21\n", + " 1.00000000e+00 2.89427943e-12]\n", + " [9.99999881e-01 7.66080674e-08 4.01302741e-10 1.21687230e-10\n", + " 6.82011322e-11 4.35267850e-10 8.41016257e-10 6.03628223e-08\n", + " 1.83182872e-10 2.12390319e-08]\n", + " [1.00000000e+00 1.02140101e-08 8.57160454e-11 1.46434895e-11\n", + " 6.01715422e-12 4.42332213e-11 1.12044332e-10 1.20906076e-08\n", + " 1.86746261e-11 1.85796056e-09]\n", + " [1.00000000e+00 1.35334801e-08 1.44839696e-10 3.20196022e-11\n", + " 7.06318858e-12 7.29009908e-11 1.35828584e-10 8.56685567e-09\n", + " 3.40993830e-11 2.23633712e-09]\n", + " [1.00000000e+00 4.73004791e-09 5.08928004e-11 9.71004267e-12\n", + " 1.82601547e-12 2.07736901e-11 4.22119839e-11 3.18414317e-09\n", + " 1.04898113e-11 6.92782387e-10]\n", + " [1.00000000e+00 1.41697436e-08 1.42050427e-10 1.36855726e-11\n", + " 1.06193344e-11 5.32689101e-11 1.77751633e-10 1.79589286e-08\n", + " 2.50100548e-11 1.59166702e-09]\n", + " [1.00000000e+00 9.49651024e-09 6.87458840e-11 8.39468946e-12\n", + " 4.38854552e-12 3.32839416e-11 9.43133627e-11 6.71393696e-09\n", + " 1.46565399e-11 9.05463149e-10]\n", + " [1.00000000e+00 7.19620585e-09 3.14082545e-11 2.36226468e-12\n", + " 2.84311593e-12 1.80565164e-11 7.26536262e-11 6.71004496e-09\n", + " 5.43720798e-12 3.57073898e-10]\n", + " [1.00000000e+00 4.49967263e-09 2.37786596e-11 6.98473571e-12\n", + " 9.58736129e-13 2.08879181e-11 3.20779167e-11 1.63242353e-09\n", + " 6.82116473e-12 3.38562484e-10]\n", + " [9.99999285e-01 3.27563470e-07 2.24083596e-09 1.53382607e-09\n", + " 4.52343468e-10 4.06696588e-09 4.62660221e-09 1.92161735e-07\n", + " 3.32298433e-09 2.31440765e-07]\n", + " [2.42511476e-07 5.64118574e-09 9.99996185e-01 2.42291605e-07\n", + " 6.74047484e-08 8.29447977e-09 3.89568777e-09 3.20804452e-06\n", + " 5.30311617e-08 3.04027870e-09]\n", + " [1.00000000e+00 3.30623329e-09 2.51304116e-11 6.39533912e-12\n", + " 1.03200868e-12 1.76147031e-11 3.00188797e-11 2.97979152e-09\n", + " 8.10135292e-12 7.86700927e-10]\n", + " [1.00000000e+00 3.41055983e-09 2.31131884e-11 3.75860801e-12\n", + " 1.30199605e-12 1.33018058e-11 3.18907539e-11 4.42328396e-09\n", + " 5.68999925e-12 6.60321520e-10]\n", + " [1.00000000e+00 2.03706652e-09 2.07900207e-11 3.72330292e-12\n", + " 6.29652255e-13 1.01628280e-11 1.91912094e-11 1.94698480e-09\n", + " 4.90407584e-12 3.54579754e-10]\n", + " [3.24202037e-12 4.28253319e-14 1.18339587e-08 2.56937267e-13\n", + " 5.74537488e-13 1.35301612e-18 6.68170367e-21 2.70320571e-11\n", + " 1.50204502e-12 1.00000000e+00]\n", + " [1.00000000e+00 4.22949498e-09 8.66311536e-12 2.48381459e-12\n", + " 8.07031904e-13 1.24805496e-11 2.26057818e-11 1.66310332e-09\n", + " 3.62692472e-12 3.85662002e-10]\n", + " [1.00000000e+00 2.91159785e-09 3.38505938e-12 1.36974232e-12\n", + " 3.04925112e-13 8.23522674e-12 1.18841777e-11 7.36788075e-10\n", + " 1.51892767e-12 1.73186174e-10]\n", + " [1.00000000e+00 2.63998423e-09 6.65109027e-12 3.34980459e-12\n", + " 3.46474423e-13 1.42454095e-11 1.54123346e-11 7.87618304e-10\n", + " 3.18418707e-12 2.66068834e-10]\n", + " [4.01031849e-14 1.10625701e-12 2.64422333e-08 9.99999881e-01\n", + " 2.96555697e-11 2.40825404e-08 6.50915343e-14 2.39156026e-12\n", + " 1.28763077e-07 2.19119126e-10]\n", + " [1.00000000e+00 2.33565665e-08 1.11971057e-10 1.17426832e-10\n", + " 6.67567000e-12 2.62681793e-10 1.94974481e-10 7.27671479e-09\n", + " 1.07718487e-10 8.50158166e-09]\n", + " [5.35052777e-07 1.98121866e-10 6.46449109e-07 1.19303345e-09\n", + " 2.88042628e-07 1.33322565e-05 9.99959469e-01 3.28572308e-10\n", + " 2.19677004e-05 3.80267988e-06]\n", + " [1.00000000e+00 3.15942428e-09 2.61794857e-11 7.20120664e-12\n", + " 9.35322132e-13 1.81487412e-11 2.89412643e-11 2.51603205e-09\n", + " 8.10671669e-12 6.72781719e-10]\n", + " [1.00000000e+00 1.28403677e-09 7.83764633e-12 2.60600569e-12\n", + " 2.01350289e-13 6.06520303e-12 7.72961382e-12 5.93651905e-10\n", + " 2.41755574e-12 1.85978913e-10]\n", + " [1.00000000e+00 5.27407273e-09 2.57288652e-11 6.78623087e-12\n", + " 1.54789354e-12 2.34864836e-11 3.85913523e-11 3.52272433e-09\n", + " 8.21843722e-12 6.57837507e-10]\n", + " [1.00000000e+00 5.16898657e-09 8.15082637e-12 2.93037968e-12\n", + " 8.72073843e-13 1.60735574e-11 2.40861359e-11 2.05222928e-09\n", + " 3.58294709e-12 4.78925344e-10]\n", + " [3.49520150e-14 6.63962607e-10 2.32582616e-13 6.09317535e-18\n", + " 1.00000000e+00 7.31402455e-19 1.05612819e-11 1.61026926e-13\n", + " 3.00158200e-13 2.57299945e-11]\n", + " [6.98462945e-12 8.24860492e-12 6.36849595e-09 3.36196484e-13\n", + " 1.92141246e-12 5.06562495e-17 3.04464892e-20 2.08002629e-10\n", + " 1.41394513e-12 1.00000000e+00]\n", + " [1.00000000e+00 1.81691675e-08 6.44654677e-11 1.15101789e-10\n", + " 3.06896197e-12 2.22902002e-10 1.24597679e-10 3.54055474e-09\n", + " 6.49044360e-11 4.72197126e-09]\n", + " [1.00000000e+00 7.21572269e-09 3.01063237e-11 3.09955984e-11\n", + " 1.09894483e-12 6.14886267e-11 4.20315380e-11 1.75318982e-09\n", + " 2.01885036e-11 1.59608127e-09]\n", + " [1.00000000e+00 7.48421147e-09 6.35864625e-11 3.27037356e-11\n", + " 1.97294785e-12 5.32183360e-11 5.59881724e-11 2.65814859e-09\n", + " 2.46006219e-11 1.36443290e-09]\n", + " [1.00000000e+00 1.03974802e-08 1.13371784e-10 4.83704292e-11\n", + " 3.61676964e-12 8.27443600e-11 9.84634110e-11 4.50308102e-09\n", + " 4.03218466e-11 1.94335636e-09]\n", + " [2.42548936e-09 9.99999881e-01 2.27400113e-11 1.54214057e-13\n", + " 4.72127715e-09 2.39768990e-12 5.39968840e-13 1.75306127e-07\n", + " 4.69313878e-13 1.02728703e-09]\n", + " [1.00000000e+00 7.79905029e-09 5.34179680e-11 1.62607636e-11\n", + " 2.40055914e-12 4.28101270e-11 6.26466309e-11 3.68652020e-09\n", + " 1.63497393e-11 7.96677557e-10]\n", + " [1.00000000e+00 8.57534044e-09 1.73299413e-11 9.02803006e-12\n", + " 1.42337509e-12 3.49646666e-11 4.19186491e-11 1.79271964e-09\n", + " 9.12450671e-12 7.08229864e-10]\n", + " [2.44347674e-13 8.36711256e-12 5.97698104e-08 9.99999642e-01\n", + " 1.56963678e-10 4.16361967e-09 4.66740768e-14 1.03680519e-11\n", + " 2.71961966e-07 6.99602043e-10]\n", + " [1.00000000e+00 5.23433741e-09 2.05554913e-11 9.52715684e-12\n", + " 1.03719062e-12 3.21172811e-11 3.64027454e-11 1.89418992e-09\n", + " 8.63502152e-12 5.67298042e-10]\n", + " [1.00000000e+00 1.73647408e-08 4.10802746e-11 3.79038911e-10\n", + " 1.28409100e-12 5.06102660e-10 1.18627608e-10 6.78255008e-10\n", + " 1.03035372e-10 6.63069422e-09]\n", + " [3.01624155e-15 3.25208575e-11 7.12345493e-14 2.59404767e-17\n", + " 1.00000000e+00 5.78185682e-18 3.22820302e-13 8.54187901e-12\n", + " 6.71667379e-14 1.20791169e-10]\n", + " [1.00000000e+00 4.15490220e-09 3.38506723e-11 5.47947833e-11\n", + " 6.49698990e-13 8.07836714e-11 3.99131700e-11 6.47853715e-10\n", + " 2.98908988e-11 1.28910171e-09]\n", + " [1.50115285e-13 5.68260976e-12 2.25830483e-08 9.99999404e-01\n", + " 2.01837630e-10 1.12099805e-07 4.99035112e-13 7.67412436e-12\n", + " 4.43871841e-07 1.89370852e-09]\n", + " [1.00000000e+00 1.54819402e-09 4.80789280e-12 5.30377391e-12\n", + " 1.34147644e-13 1.26751561e-11 8.54976766e-12 2.11040976e-10\n", + " 3.36444241e-12 2.36121733e-10]\n", + " [1.00000000e+00 6.68860345e-09 2.54761073e-11 2.91887903e-11\n", + " 1.03915108e-12 6.53198606e-11 4.43728179e-11 1.02275333e-09\n", + " 2.00071869e-11 1.04824405e-09]\n", + " [1.00000000e+00 1.63655507e-08 2.87145620e-11 2.06704133e-11\n", + " 2.90038588e-12 1.02094215e-10 1.01135225e-10 2.72967915e-09\n", + " 2.14264977e-11 1.55700397e-09]\n", + " [1.00000000e+00 2.96840259e-08 3.67386052e-11 3.68887976e-11\n", + " 4.83568524e-12 1.80293044e-10 1.76043694e-10 3.44571793e-09\n", + " 3.22701171e-11 2.00794603e-09]\n", + " [7.39158423e-14 1.51453162e-18 2.67292022e-11 3.59574273e-08\n", + " 6.12078639e-13 1.50004046e-13 5.13985070e-11 2.38407720e-16\n", + " 1.00000000e+00 6.42054854e-10]\n", + " [2.34271393e-08 2.09879181e-06 3.18804503e-08 3.27036592e-11\n", + " 7.27377270e-08 2.19474261e-09 6.50273668e-10 9.99997616e-01\n", + " 4.95438196e-12 1.23743078e-07]\n", + " [1.00000000e+00 6.70707623e-09 2.89783267e-11 1.42756890e-10\n", + " 6.01556836e-13 2.08315101e-10 6.55220045e-11 4.77591688e-10\n", + " 4.66746086e-11 2.68815903e-09]\n", + " [1.00000000e+00 3.18772853e-09 1.40672586e-11 8.71471229e-11\n", + " 2.47915566e-13 9.43065209e-11 2.69540570e-11 1.37266754e-10\n", + " 2.96147620e-11 1.24544075e-09]\n", + " [1.00000000e+00 6.67023237e-09 2.61556783e-11 1.82312887e-10\n", + " 5.74033605e-13 1.82388368e-10 5.17268901e-11 2.63756683e-10\n", + " 5.49732690e-11 2.25877450e-09]\n", + " [1.00000000e+00 1.85539317e-09 6.12565857e-12 1.15849257e-11\n", + " 1.48777664e-13 2.33249219e-11 1.26214542e-11 1.63774952e-10\n", + " 6.17794314e-12 3.43776674e-10]\n", + " [1.00000000e+00 3.81201115e-09 1.56297128e-11 3.28247846e-11\n", + " 4.20451217e-13 5.58428061e-11 2.92014295e-11 3.73494291e-10\n", + " 1.61946810e-11 6.75642986e-10]\n", + " [4.11106043e-15 9.10757650e-11 8.03249069e-14 2.73032261e-18\n", + " 1.00000000e+00 5.12957600e-19 4.45564673e-13 2.81903651e-13\n", + " 5.91577636e-14 1.53565233e-11]\n", + " [9.99999881e-01 1.36827794e-07 6.73569603e-11 1.26952920e-10\n", + " 1.81476674e-11 4.48993093e-10 3.54932889e-10 7.36159045e-09\n", + " 8.28056096e-11 6.45145981e-09]\n", + " [1.00000000e+00 1.48462522e-08 5.31949970e-11 1.64234779e-10\n", + " 2.13578135e-12 2.53502885e-10 1.25961602e-10 8.98156327e-10\n", + " 7.60863525e-11 2.37383091e-09]]\n" + ] + } + ], + "source": [ + "# get prediction\n", + "prediction = model.predict(rois)\n", + "print(prediction)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "adab47ef-3153-4c47-98d2-17fa0c1aa545", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 3, 9, 1, 0, 0, 0, 0, 0, 4, 0, 8, 0, 6, 0, 0, 0, 2, 2, 0, 0, 5, 8, 0, 7, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 9, 0, 0, 0, 3, 0, 6, 0, 0, 0, 0, 4, 9, 0, 0, 0, 0, 1, 0, 0, 3, 0, 0, 4, 0, 3, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0, 4, 0, 0]\n" + ] + } + ], + "source": [ + "predicted_numbers = []\n", + "# get classes from prediction\n", + "for i in prediction: \n", + " index = (np.argmax(i)) # returns the index of the maximum number of the array\n", + " predicted_number = classes[index]\n", + " predicted_numbers.append(predicted_number)\n", + "\n", + "print(predicted_numbers)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "53177b6c-1c4e-4e73-82bc-4c8c1a9d6845", + "metadata": {}, + "outputs": [], + "source": [ + "# reshape the list \n", + "board_num = np.array(predicted_numbers).astype('uint8').reshape(9, 9)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "ddf499bb-d55a-49c5-a906-f0abfbdf773b", + "metadata": {}, + "outputs": [], + "source": [ + "# solve the board\n", + "try:\n", + " solved_board_nums = get_board(board_num)\n", + "\n", + " # create a binary array of the predicted numbers. 0 means unsolved numbers of sudoku and 1 means given number.\n", + " binArr = np.where(np.array(predicted_numbers)>0, 0, 1)\n", + " # print(binArr)\n", + " # get only solved numbers for the solved board\n", + " flat_solved_board_nums = solved_board_nums.flatten()*binArr\n", + " # create a mask\n", + " mask = np.zeros_like(board)\n", + " # displays solved numbers in the mask in the same position where board numbers are empty\n", + " solved_board_mask = displayNumbers(mask, flat_solved_board_nums)\n", + " # cv2.imshow(\"Solved Mask\", solved_board_mask)\n", + " inv = get_InvPerspective(img, solved_board_mask, location)\n", + " # cv2.imshow(\"Inverse Perspective\", inv)\n", + " combined = cv2.addWeighted(img, 0.7, inv, 1, 0)\n", + " cv2.imshow(\"Final result\", combined)\n", + " # cv2.waitKey(0)\n", + " \n", + "\n", + "except:\n", + " print(\"Solution doesn't exist. Model misread digits.\")\n", + "\n", + "cv2.imshow(\"Input image\", img)\n", + "# cv2.imshow(\"Board\", board)\n", + "cv2.waitKey(0)\n", + "cv2.destroyAllWindows()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89251692-aba0-4071-a5d8-874d36f73392", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/OpenCV Projects/Sudoku Solver/sudoku1.jpg b/OpenCV Projects/Sudoku Solver/sudoku1.jpg new file mode 100644 index 000000000..10b7ecc35 Binary files /dev/null and b/OpenCV Projects/Sudoku Solver/sudoku1.jpg differ