diff --git a/ALGORITHM.md b/ALGORITHM.md new file mode 100644 index 0000000..488e25a --- /dev/null +++ b/ALGORITHM.md @@ -0,0 +1,42 @@ +# Algorithm Explanation + +- Step 001: Load the image + + step 1 - image + +- Step 002: Normalize the image + + step 2 - normalized + +- Step 003: Erode the image + + step 3 - eroded + +- Step 004: Threshold the image + + step 4 - threshold + +- Step 005: Dilate the image + + step 5 - dilated + +- Step 006: Crop the image + + step 6 - cropped + +- Step 007: Show the maze with start and end points + + step 7 - maze + +- Step 008: Create the maze bitmap + + step 8 - maze bitmap + +- Step 009: Find the maze path + + step 9 - maze path + +- Step 010: Show the solution + + step 10 - solution + diff --git a/cv_maze.py b/cv_maze.py index 157e60f..44d66f8 100644 --- a/cv_maze.py +++ b/cv_maze.py @@ -53,13 +53,13 @@ def solve_maze(image, debug=False): # cv2.fillPoly(image, [np.int32(r.corners)], (0, 0, 0)) # erode the image - eroded = cv2.erode(normalized_image, np.ones((7, 7), np.uint8)) + eroded = cv2.erode(normalized_image, np.ones((9, 9), np.uint8)) # eroded = cv2.GaussianBlur(eroded, (15, 15), 0) debug_image("Eroded", eroded) # binarize the image - _, thresh = cv2.threshold(eroded, 150, 255, cv2.THRESH_BINARY) + _, thresh = cv2.threshold(eroded, 200, 255, cv2.THRESH_BINARY) thresh = cv2.bitwise_not(thresh) debug_image("Threshold", thresh) diff --git a/debug-steps/001_Image.png b/debug-steps/001_Image.png index a205fd7..b8b9308 100644 Binary files a/debug-steps/001_Image.png and b/debug-steps/001_Image.png differ diff --git a/debug-steps/003_Eroded.png b/debug-steps/003_Eroded.png index a6b82fa..17e35f2 100644 Binary files a/debug-steps/003_Eroded.png and b/debug-steps/003_Eroded.png differ diff --git a/debug-steps/004_Threshold.png b/debug-steps/004_Threshold.png index 830080d..5ad9029 100644 Binary files a/debug-steps/004_Threshold.png and b/debug-steps/004_Threshold.png differ diff --git a/debug-steps/005_Dilated.png b/debug-steps/005_Dilated.png index c501e79..8df276d 100644 Binary files a/debug-steps/005_Dilated.png and b/debug-steps/005_Dilated.png differ diff --git a/debug-steps/006_Cropped.png b/debug-steps/006_Cropped.png index cfc5633..a3d609a 100644 Binary files a/debug-steps/006_Cropped.png and b/debug-steps/006_Cropped.png differ diff --git a/debug-steps/007_Maze.png b/debug-steps/007_Maze.png index fd0195d..5060fdc 100644 Binary files a/debug-steps/007_Maze.png and b/debug-steps/007_Maze.png differ diff --git a/debug-steps/008_Maze Bitmap.png b/debug-steps/008_Maze Bitmap.png index ec29df4..a28f5b1 100644 Binary files a/debug-steps/008_Maze Bitmap.png and b/debug-steps/008_Maze Bitmap.png differ diff --git a/debug-steps/009_Maze Path.png b/debug-steps/009_Maze Path.png index 12b57d2..f6e6fd6 100644 Binary files a/debug-steps/009_Maze Path.png and b/debug-steps/009_Maze Path.png differ diff --git a/debug-steps/010_Solution.png b/debug-steps/010_Solution.png index aa8478b..7dd9235 100644 Binary files a/debug-steps/010_Solution.png and b/debug-steps/010_Solution.png differ diff --git a/main_camera_test.py b/main_camera_test.py new file mode 100644 index 0000000..82ba3e7 --- /dev/null +++ b/main_camera_test.py @@ -0,0 +1,37 @@ +import cv2 + +import numpy as np +import utils +import cv_maze + +camera = cv2.VideoCapture(0) +camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) +camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) + +while utils.wait_frame(): + ret, frame = camera.read() + + if not ret: + break + + utils.display_image("Camera", frame) + + # convert the image to grayscale + image_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + + # extract the tags + tag_registry = utils.extract_tags_dict_single(image_gray) + + # draw borders around the tags + for r in tag_registry.values(): + (ptA, ptB, ptC, ptD) = r.corners + + cv2.line(frame, np.int32(ptA), np.int32(ptB), (0, 255, 0), 5) + cv2.line(frame, np.int32(ptB), np.int32(ptC), (0, 255, 0), 5) + cv2.line(frame, np.int32(ptC), np.int32(ptD), (0, 255, 0), 5) + cv2.line(frame, np.int32(ptD), np.int32(ptA), (0, 255, 0), 5) + + utils.display_image("Tags", frame) + + +camera.release() diff --git a/utils.py b/utils.py index c2bd8e4..106b757 100644 --- a/utils.py +++ b/utils.py @@ -4,13 +4,22 @@ import time import numpy as np -import dt_apriltags from dt_apriltags import Detector, Detection +from threading import Timer + WINDOW_LABELS = set() +def move_window(label, x, y): + """ + Move the window with the given label to the given position + """ + + cv2.moveWindow(label, x, y) + + def display_image(label, image, default_width: int = 800, default_height: int = 600, interpolation: int = cv2.INTER_LINEAR): """ Display an image fitted in a window with the given label and size, optionally setting the interpolation method @@ -22,11 +31,15 @@ def display_image(label, image, default_width: int = 800, default_height: int = new_width = default_height * width // height image = cv2.resize(image, (new_width, default_height), interpolation=interpolation) - WINDOW_LABELS.add(label) cv2.namedWindow(label, cv2.WINDOW_NORMAL) cv2.resizeWindow(label, default_width, default_height) cv2.imshow(label, image) - cv2.imwrite(f"debug-steps/{len(WINDOW_LABELS):03}_{label}.png", image) + + if label not in WINDOW_LABELS: + cv2.imwrite(f"debug-steps/{len(WINDOW_LABELS)+1:03}_{label}.png", image) + Timer(5.0, move_window, args=(label, 0, 0)).start() + + WINDOW_LABELS.add(label) def wait_frame():