├── .gitignore ├── README.md ├── algorithms ├── Boolean-Function-Edge-Detector │ ├── boolean_function.py │ └── results │ │ ├── lena-noisy.jpeg │ │ ├── lena.png │ │ └── simple-shapes.png ├── Canny-Edge-Detector │ ├── canny_edge_detector.py │ └── results │ │ ├── lena-noisy.jpeg │ │ ├── lena.png │ │ └── simple-shapes.png ├── LoG-Edge-Detector │ ├── Convolved-Kernels │ │ └── LoG_convolved.py │ └── Seperated-Kernels │ │ ├── LoG_seperated.py │ │ └── results │ │ ├── lena-noisy.jpeg │ │ ├── lena.png │ │ └── simple-shapes.png ├── Prewit-Edge-Detector │ ├── prewit.py │ └── results │ │ ├── lena-noisy.jpeg │ │ ├── lena.png │ │ └── simple-shapes.png ├── Robert-Cross-Detector │ ├── results │ │ ├── lena-noisy.jpeg │ │ ├── lena.png │ │ └── simple-shapes.png │ └── robert_cross.py └── Sobel-Edge-Detector │ ├── results │ ├── lena-noisy.jpeg │ ├── lena.png │ └── simple-shapes.png │ └── sobel.py ├── report.pdf ├── requirements.txt └── test-images ├── lena-noisy.jpeg ├── lena.png └── simple-shapes.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/python,virtualenv 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,virtualenv 4 | 5 | ### Python ### 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # poetry 103 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 104 | # This is especially recommended for binary packages to ensure reproducibility, and is more 105 | # commonly ignored for libraries. 106 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 107 | #poetry.lock 108 | 109 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 110 | __pypackages__/ 111 | 112 | # Celery stuff 113 | celerybeat-schedule 114 | celerybeat.pid 115 | 116 | # SageMath parsed files 117 | *.sage.py 118 | 119 | # Environments 120 | .env 121 | .venv 122 | env/ 123 | venv/ 124 | ENV/ 125 | env.bak/ 126 | venv.bak/ 127 | 128 | # Spyder project settings 129 | .spyderproject 130 | .spyproject 131 | 132 | # Rope project settings 133 | .ropeproject 134 | 135 | # mkdocs documentation 136 | /site 137 | 138 | # mypy 139 | .mypy_cache/ 140 | .dmypy.json 141 | dmypy.json 142 | 143 | # Pyre type checker 144 | .pyre/ 145 | 146 | # pytype static type analyzer 147 | .pytype/ 148 | 149 | # Cython debug symbols 150 | cython_debug/ 151 | 152 | # PyCharm 153 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 154 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 155 | # and can be added to the global gitignore or merged into this file. For a more nuclear 156 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 157 | #.idea/ 158 | 159 | ### VirtualEnv ### 160 | # Virtualenv 161 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 162 | [Bb]in 163 | [Ii]nclude 164 | [Ll]ib 165 | [Ll]ib64 166 | [Ll]ocal 167 | [Ss]cripts 168 | pyvenv.cfg 169 | pip-selfcheck.json 170 | 171 | # End of https://www.toptal.com/developers/gitignore/api/python,virtualenv 172 | 173 | # MAC OS 174 | .DS_STORE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Edge-Detection-Algorithms 2 | 3 | algorithms 4 | 5 | In this repository I've implemented some important edge detection algorithms which are based on computer vision techniques. These algorithms are as follows: 6 | 7 | - [Sobel Algorithm](./algorithms/Sobel-Edge-Detector) 8 | 9 | - [Prewit Algorithm](./algorithms/Prewit-Edge-Detector) 10 | 11 | - [Robert-Cross Algorithm](./algorithms/Robert-Cross-Detector) 12 | 13 | - Laplace of Gaussian Algorithm (LoG) 14 | 15 | - [Using Seperated Kernels](./algorithms/LoG-Edge-Detector/Seperated-Kernels) 16 | 17 | - [Using Convolved Kernels](./algorithms/LoG-Edge-Detector/Convolved-Kernels) 18 | 19 | - [Boolean Function Algorithm](./algorithms/Boolean-Function-Edge-Detector) 20 | 21 | - [Canny Algorithm](./algorithms/Canny-Edge-Detector) 22 | 23 | Actually, I implemented these algorithms to complete my technical report called "A Study on Edge Detection Algorithms for Grayscale Images" which was done as a part of "Research and Technical Presentation" course at AUT. 24 | 25 | My technical report is available in `report.pdf` file. 26 | -------------------------------------------------------------------------------- /algorithms/Boolean-Function-Edge-Detector/boolean_function.py: -------------------------------------------------------------------------------- 1 | from statistics import variance 2 | import tkinter as tk 3 | import tkinter.filedialog as fd 4 | import cv2 5 | import numpy as np 6 | from scipy.signal import convolve2d 7 | 8 | boolean_functions_matrices = [ 9 | np.array([[0, 1, 1], 10 | [0, 1, 1], 11 | [0, 1, 1]]), 12 | np.array([[0, 0, 0], 13 | [1, 1, 1], 14 | [1, 1, 1]]), 15 | np.array([[1, 1, 0], 16 | [1, 1, 0], 17 | [1, 1, 0]]), 18 | np.array([[1, 1, 1], 19 | [1, 1, 1], 20 | [0, 0, 0]]), 21 | 22 | np.array([[1, 1, 1], 23 | [0, 1, 1], 24 | [0, 0, 1]]), 25 | np.array([[1, 0, 0], 26 | [1, 1, 0], 27 | [1, 1, 1]]), 28 | np.array([[0, 0, 1], 29 | [0, 1, 1], 30 | [1, 1, 1]]), 31 | np.array([[1, 1, 1], 32 | [1, 1, 0], 33 | [1, 0, 0]]), 34 | 35 | np.array([[0, 1, 1], 36 | [0, 1, 1], 37 | [0, 0, 1]]), 38 | np.array([[1, 0, 0], 39 | [1, 1, 0], 40 | [1, 1, 0]]), 41 | np.array([[0, 0, 0], 42 | [0, 1, 1], 43 | [1, 1, 1]]), 44 | np.array([[1, 1, 1], 45 | [1, 1, 0], 46 | [0, 0, 0]]), 47 | 48 | np.array([[1, 1, 1], 49 | [0, 1, 1], 50 | [0, 0, 0]]), 51 | np.array([[0, 0, 0], 52 | [1, 1, 0], 53 | [1, 1, 1]]), 54 | np.array([[0, 0, 1], 55 | [0, 1, 1], 56 | [0, 1, 1]]), 57 | np.array([[1, 1, 0], 58 | [1, 1, 0], 59 | [1, 0, 0]]), 60 | ] 61 | 62 | def perform_boolean_functions(image, window_size=3, c=0): 63 | new_img = np.zeros(image.shape, dtype=np.uint8) 64 | for i in range(1,image.shape[0]): 65 | for j in range(1,image.shape[1]): 66 | # Calculate the local window 67 | local_window = image[i-window_size//2:i+window_size//2 + 1, j-window_size//2:j+window_size//2 + 1].copy() 68 | # Calculate the mean and standard deviation 69 | threshold = np.mean(local_window) - c 70 | # Compare local window value with threshold 71 | local_window[local_window > threshold] = 255 72 | local_window[local_window <= threshold] = 0 73 | # Check if local window is equal to a boolean function 74 | local_window //= 255 75 | for boolean_function in boolean_functions_matrices: 76 | if np.all(local_window == boolean_function): 77 | new_img[i, j] = 255 78 | break 79 | return new_img 80 | 81 | def calculate_variance(image, window_size=3): 82 | new_img = np.zeros(image.shape, dtype=np.uint8) 83 | for i in range(1,image.shape[0]): 84 | for j in range(1,image.shape[1]): 85 | # Calculate the local window 86 | local_window = image[i-window_size//2:i+window_size//2 + 1, j-window_size//2:j+window_size//2 + 1].copy() 87 | variance_local_window = np.var(local_window) 88 | # Calculate the mean and standard deviation 89 | new_img[i, j] = variance_local_window 90 | return new_img 91 | 92 | def threshold(img, threshold): 93 | new_img = np.zeros(img.shape, dtype=np.uint8) 94 | new_img[img > threshold] = 255 95 | return new_img 96 | 97 | def boolean_function_algorithm(img, window_size=3, c=0, global_threshold=0): 98 | global boolean_function_image, variance_image, final_image 99 | boolean_function_image = perform_boolean_functions(img, window_size=window_size, c = c) 100 | variance_image = calculate_variance(img, window_size=3) 101 | global_threshold = int(global_threshold * np.max(variance_image) / 500) 102 | global_thresholded_image = threshold(variance_image, global_threshold) 103 | final_image = boolean_function_image & global_thresholded_image 104 | final_image = cv2.putText(final_image, f'C: {c} T: {global_threshold}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 105 | # Show results 106 | cv2.imshow('Boolean Function', boolean_function_image) 107 | cv2.imshow('Global Threshold', global_thresholded_image) 108 | cv2.imshow('Final Image', final_image) 109 | 110 | def on_c_trackbar(val): 111 | c = cv2.getTrackbarPos('C', 'Trackbars') 112 | thresh = cv2.getTrackbarPos('T', 'Trackbars') 113 | boolean_function_algorithm(img, window_size=3, c=c, global_threshold=thresh) 114 | 115 | def on_thresh_trackbar(val): 116 | global img, variance_image, final_image 117 | c = cv2.getTrackbarPos('C', 'Trackbars') 118 | variance_image = calculate_variance(img, window_size=3) 119 | thresh = int(cv2.getTrackbarPos('T', 'Trackbars') * np.max(variance_image) / 500) 120 | global_thresholded_image = threshold(variance_image, thresh) 121 | final_image = boolean_function_image & global_thresholded_image 122 | final_image = cv2.putText(final_image, f'C: {c} T: {thresh}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 123 | cv2.imshow('Global Threshold', global_thresholded_image) 124 | cv2.imshow('Final Image', final_image) 125 | 126 | IMAGE_PATH = '../../test-images/lena-noisy.jpeg' 127 | IMAGE_NAME = IMAGE_PATH.split('/')[-1] 128 | img = cv2.imread(IMAGE_PATH, 0) 129 | boolean_function_image = None 130 | variance_image = None 131 | final_image = None 132 | cv2.imshow('Image', img) 133 | 134 | cv2.namedWindow('Trackbars') 135 | cv2.resizeWindow('Trackbars', 640, 240) 136 | cv2.createTrackbar("C", "Trackbars", 0, 255, on_c_trackbar) 137 | cv2.createTrackbar("T", "Trackbars", 0, 500, on_thresh_trackbar) 138 | 139 | on_c_trackbar(1) 140 | on_thresh_trackbar(1) 141 | 142 | cv2.waitKey(0) 143 | cv2.destroyAllWindows() 144 | 145 | # # Save image 146 | cv2.imwrite('results/' + IMAGE_NAME, final_image) -------------------------------------------------------------------------------- /algorithms/Boolean-Function-Edge-Detector/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Boolean-Function-Edge-Detector/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/Boolean-Function-Edge-Detector/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Boolean-Function-Edge-Detector/results/lena.png -------------------------------------------------------------------------------- /algorithms/Boolean-Function-Edge-Detector/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Boolean-Function-Edge-Detector/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/Canny-Edge-Detector/canny_edge_detector.py: -------------------------------------------------------------------------------- 1 | from typing import final 2 | import numpy as np 3 | from scipy.signal import convolve2d 4 | import cv2 5 | 6 | def any_neighbor_has_value(img, i, j, value=0): 7 | k_start = 0 if j - 1 < 0 else -1 8 | k_end = 0 if j + 1 > img.shape[1] - 1 else 1 9 | l_start = 0 if i - 1 < 0 else -1 10 | l_end = 0 if i + 1 > img.shape[0] - 1 else 1 11 | for k in range(k_start, k_end): 12 | for l in range(l_start, l_end): 13 | if img[i+l, j+k] == value: 14 | return True 15 | return False 16 | 17 | def is_valid_pixel(img, i, j): 18 | return i < img.shape[0] and i >= 0 and j < img.shape[1] and j >= 0 19 | 20 | def valid_neighbors(img, i, j): 21 | neighbors = [] 22 | for k in range(-1, 2 , 1): 23 | for p in range(-1, 2, 1): 24 | if is_valid_pixel(img, i + k, j + p): 25 | neighbors.append((i + k, j + p)) 26 | return neighbors 27 | 28 | def double_threshold_image(img, low_thresh, high_thresh, weak_value=100, strong_value=255): 29 | highThreshold = img.max() * high_thresh; 30 | lowThreshold = highThreshold * low_thresh; 31 | 32 | # Thresholding the image 33 | strong = np.where(img >= highThreshold) 34 | zeros = np.where(img < lowThreshold) 35 | weak = np.where((img < highThreshold) & (img >= lowThreshold)) 36 | 37 | new_image = np.zeros(img.shape, dtype=np.uint8) 38 | new_image[strong] = strong_value 39 | new_image[weak] = weak_value 40 | new_image[zeros] = 0 41 | return new_image 42 | 43 | def hysteresis_tracking(img, weak_value=100, strong_value=255): 44 | img_row, img_col = img.shape 45 | 46 | new_img = img.copy() 47 | for i in range(img_row): 48 | for j in range(img_col): 49 | if new_img[i,j] == strong_value: 50 | queue = [] 51 | for neighbor in valid_neighbors(new_img, i, j): 52 | if new_img[neighbor[0], neighbor[1]] == weak_value: 53 | queue.append(neighbor) 54 | while len(queue) > 0: 55 | x, y = queue.pop(0) 56 | if new_img[x,y] == weak_value: 57 | new_img[x,y] = strong_value 58 | for neighbor in valid_neighbors(new_img, x, y): 59 | if new_img[neighbor[0], neighbor[1]] == weak_value: 60 | queue.append(neighbor) 61 | 62 | for i in range(img_row): 63 | for j in range(img_col): 64 | if new_img[i,j] == weak_value: 65 | new_img[i,j] = 0 66 | 67 | return new_img 68 | 69 | def convolve(image, kernel): 70 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 71 | return np.array(convolved_matrix, dtype=np.float64) 72 | 73 | def sobel_algorithm(img): 74 | Kx = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]], np.float32) 75 | Ky = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], np.float32) 76 | 77 | Ix = convolve(img, Kx) 78 | Iy = convolve(img, Ky) 79 | G = np.hypot(Ix, Iy) 80 | G = np.array(G / G.max() * 255, dtype=np.uint8) 81 | theta = np.arctan2(Iy, Ix) 82 | return (G, theta) 83 | 84 | def non_max_supression(gradient_magnitude, edge_directions): 85 | M, N = gradient_magnitude.shape 86 | angle = edge_directions * 180. / np.pi 87 | angle[angle < 0] += 180 88 | 89 | new_img = np.zeros((M,N), dtype=np.uint8) 90 | for i in range(1,M-1): 91 | for j in range(1,N-1): 92 | q = 255 93 | r = 255 94 | 95 | #angle 0 96 | if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180): 97 | q = gradient_magnitude[i, j+1] 98 | r = gradient_magnitude[i, j-1] 99 | #angle 45 100 | elif (22.5 <= angle[i,j] < 67.5): 101 | q = gradient_magnitude[i+1, j-1] 102 | r = gradient_magnitude[i-1, j+1] 103 | #angle 90 104 | elif (67.5 <= angle[i,j] < 112.5): 105 | q = gradient_magnitude[i+1, j] 106 | r = gradient_magnitude[i-1, j] 107 | #angle 135 108 | elif (112.5 <= angle[i,j] < 157.5): 109 | q = gradient_magnitude[i-1, j-1] 110 | r = gradient_magnitude[i+1, j+1] 111 | 112 | if (gradient_magnitude[i,j] >= q) and (gradient_magnitude[i,j] >= r): 113 | new_img[i,j] = gradient_magnitude[i,j] 114 | return new_img 115 | 116 | 117 | def canny_algorithm(img): 118 | global max_edges_image, low_thresh_tracker, high_thresh_tracker, sigma_tracker, kernel_size_tracker 119 | # Apply Gaussian blur 120 | blurred_image = None 121 | if sigma_tracker != 0: 122 | blurred_image = cv2.GaussianBlur(img, (kernel_size_tracker, kernel_size_tracker), sigma_tracker) 123 | else: 124 | blurred_image = img 125 | # Apply Sobel algorithm 126 | gradient_magnitude, gradient_direction = sobel_algorithm(blurred_image) 127 | # Apply non maximum suppression 128 | max_edges_image = non_max_supression(gradient_magnitude, gradient_direction) 129 | cv2.imshow("Blurred", blurred_image) 130 | cv2.imshow("Sobel", gradient_magnitude) 131 | cv2.imshow("Non max", max_edges_image) 132 | 133 | handle_thresholds(max_edges_image) 134 | 135 | def handle_bluring(): 136 | canny_algorithm(img) 137 | 138 | def on_kernel_trackbar(val): 139 | global kernel_size_tracker 140 | kernel_size_tracker = (cv2.getTrackbarPos('K', 'Trackbars') + 1) * 2 + 1 141 | handle_bluring() 142 | 143 | def on_sigma_trackbar(val): 144 | global sigma_tracker 145 | sigma_tracker = cv2.getTrackbarPos('Sigma', 'Trackbars') / 10 146 | handle_bluring() 147 | 148 | def handle_thresholds(max_edges_image): 149 | global sigma_tracker, kernel_size_tracker, low_thresh_tracker, high_thresh_tracker, final_image 150 | # Apply double thresholding 151 | thresholded_image = double_threshold_image(max_edges_image, low_thresh_tracker, high_thresh_tracker) 152 | # Apply hysteresis tracking 153 | final_image = hysteresis_tracking(thresholded_image) 154 | final_image = cv2.putText(final_image, f'LT: {low_thresh_tracker} HT: {high_thresh_tracker} K: {kernel_size_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) 155 | 156 | # Show results 157 | cv2.imshow('Threshold image', thresholded_image) 158 | cv2.imshow('Final image', final_image) 159 | 160 | 161 | def on_low_thresh_trackbar(val): 162 | global max_edge_image, low_thresh_tracker 163 | low_thresh_tracker = cv2.getTrackbarPos('Low Threshold', 'Trackbars') / 100 164 | handle_thresholds(max_edges_image) 165 | 166 | def on_high_thresh_trackbar(val): 167 | global max_edge_image, high_thresh_tracker 168 | high_thresh_tracker = cv2.getTrackbarPos('High Threshold', 'Trackbars') / 100 169 | handle_thresholds(max_edges_image) 170 | 171 | IMAGE_FILE = '../../test-images/bal.jpg' 172 | IMAGE_NAME = IMAGE_FILE.split('/')[-1] 173 | img = cv2.imread(IMAGE_FILE, 0) 174 | max_edges_image = None 175 | kernel_size_tracker = 25 176 | sigma_tracker = 4 177 | low_thresh_tracker = 0.7 178 | high_thresh_tracker = 0.17 179 | final_image = None 180 | 181 | #Creating trackbar window 182 | cv2.namedWindow('Trackbars') 183 | cv2.resizeWindow('Trackbars', 640, 240) 184 | cv2.createTrackbar("K", "Trackbars", 1, 20, on_kernel_trackbar) 185 | cv2.createTrackbar("Sigma", "Trackbars", 1, 240, on_sigma_trackbar) 186 | cv2.createTrackbar("Low Threshold", "Trackbars", 1, 100, on_low_thresh_trackbar) 187 | cv2.createTrackbar("High Threshold", "Trackbars", 1, 100, on_high_thresh_trackbar) 188 | 189 | canny_algorithm(img) 190 | 191 | # cv2.imshow('OpenCV Canny', cv2.Canny(img, 100, 140)) 192 | cv2.waitKey(0) 193 | cv2.destroyAllWindows() 194 | 195 | final_image = cv2.putText(final_image, f'LT: {low_thresh_tracker} HT: {high_thresh_tracker} K: {kernel_size_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 196 | cv2.imwrite('results/' + IMAGE_NAME, final_image) -------------------------------------------------------------------------------- /algorithms/Canny-Edge-Detector/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Canny-Edge-Detector/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/Canny-Edge-Detector/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Canny-Edge-Detector/results/lena.png -------------------------------------------------------------------------------- /algorithms/Canny-Edge-Detector/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Canny-Edge-Detector/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/LoG-Edge-Detector/Convolved-Kernels/LoG_convolved.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.filedialog as fd 3 | import cv2 4 | import numpy as np 5 | from scipy.signal import convolve2d 6 | 7 | def convolve(image, kernel): 8 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 9 | return convolved_matrix 10 | 11 | def any_neighbor_zero(img, i, j): 12 | for k in range(-1,2): 13 | for l in range(-1,2): 14 | if img[i+k, j+l] == 0: 15 | return True 16 | return False 17 | 18 | def zero_crossing(image): 19 | z_c_image = np.zeros(image.shape) 20 | 21 | # For each pixel, count the number of positive 22 | # and negative pixels in the neighborhood 23 | 24 | for i in range(1, image.shape[0] - 1): 25 | for j in range(1, image.shape[1] - 1): 26 | negative_count = 0 27 | positive_count = 0 28 | neighbour = [image[i+1, j-1],image[i+1, j],image[i+1, j+1],image[i, j-1],image[i, j+1],image[i-1, j-1],image[i-1, j],image[i-1, j+1]] 29 | d = max(neighbour) 30 | e = min(neighbour) 31 | for h in neighbour: 32 | if h>0: 33 | positive_count += 1 34 | elif h<0: 35 | negative_count += 1 36 | 37 | 38 | # If both negative and positive values exist in 39 | # the pixel neighborhood, then that pixel is a 40 | # potential zero crossing 41 | 42 | z_c = ((negative_count > 0) and (positive_count > 0)) 43 | 44 | # Change the pixel value with the maximum neighborhood 45 | # difference with the pixel 46 | 47 | if z_c: 48 | if image[i,j]>0: 49 | z_c_image[i, j] = image[i,j] + np.abs(e) 50 | elif image[i,j]<0: 51 | z_c_image[i, j] = np.abs(image[i,j]) + d 52 | 53 | # Normalize and change datatype to 'uint8' (optional) 54 | z_c_norm = z_c_image/z_c_image.max()*255 55 | z_c_image = np.uint8(z_c_norm) 56 | 57 | return z_c_image 58 | 59 | 60 | def LoG(sigma, x, y): 61 | '''Compute the LoG operator at a given point''' 62 | laplace = -1/(np.pi*sigma**4)*(1-(x**2+y**2)/(2*sigma**2))*np.exp(-(x**2+y**2)/(2*sigma**2)) 63 | return laplace 64 | 65 | def LoG_discrete(sigma, n): 66 | '''Compute the discrete LoG operator''' 67 | l = np.zeros((n,n)) 68 | for i in range(n): 69 | for j in range(n): 70 | l[i,j] = LoG(sigma, i-(n-1)/2, j-(n-1)/2) 71 | return l 72 | 73 | def double_threshold_image(img, low_thresh, high_thresh, weak_value=70, strong_value=255): 74 | # Thresholding the image 75 | strong = np.where(img > high_thresh) 76 | zeros = np.where(img < low_thresh) 77 | weak = np.where((img <= high_thresh) & (img >= low_thresh)) 78 | 79 | new_image = np.zeros(img.shape, dtype=np.uint8) 80 | new_image[strong] = strong_value 81 | new_image[weak] = weak_value 82 | new_image[zeros] = 0 83 | return new_image 84 | 85 | def is_valid_pixel(img, i, j): 86 | return i < img.shape[0] and i >= 0 and j < img.shape[1] and j >= 0 87 | 88 | def valid_neighbors(img, i, j): 89 | neighbors = [] 90 | for k in range(-1, 2 , 1): 91 | for p in range(-1, 2, 1): 92 | if is_valid_pixel(img, i + k, j + p): 93 | neighbors.append((i + k, j + p)) 94 | return neighbors 95 | 96 | def hysteresis_tracking(img, weak_value=70, strong_value=255): 97 | img_row, img_col = img.shape 98 | 99 | new_img = img.copy() 100 | for i in range(img_row): 101 | for j in range(img_col): 102 | if new_img[i,j] == strong_value: 103 | queue = [] 104 | for neighbor in valid_neighbors(new_img, i, j): 105 | if new_img[neighbor[0], neighbor[1]] == weak_value: 106 | queue.append(neighbor) 107 | while len(queue) > 0: 108 | x, y = queue.pop(0) 109 | if new_img[x,y] == weak_value: 110 | new_img[x,y] = strong_value 111 | for neighbor in valid_neighbors(new_img, x, y): 112 | if new_img[neighbor[0], neighbor[1]] == weak_value: 113 | queue.append(neighbor) 114 | 115 | for i in range(img_row): 116 | for j in range(img_col): 117 | if new_img[i,j] == weak_value: 118 | new_img[i,j] = 0 119 | 120 | return new_img 121 | 122 | def LoG_convolved(): 123 | global img, log_image, final_image, kernel_tracker, sigma_tracker, low_threshold_tracker, high_threshold_tracker 124 | # Apply LoG kernel 125 | kernel = np.round(LoG_discrete(sigma_tracker, kernel_tracker)*(-40/LoG(sigma_tracker,0,0))) 126 | log_image = zero_crossing(convolve(img, kernel)) 127 | thresh_image = double_threshold_image(log_image, low_threshold_tracker, high_threshold_tracker) 128 | final_image = hysteresis_tracking(thresh_image) 129 | final_image = cv2.putText(final_image, f'LT: {low_threshold_tracker} HT: {high_threshold_tracker} K: {kernel_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) 130 | # Show results 131 | cv2.imshow('Thresholded image', thresh_image) 132 | cv2.imshow('Final image', final_image) 133 | 134 | def on_kernel_trackbar(val): 135 | global img, kernel_tracker, sigma_tracker 136 | kernel_tracker = cv2.getTrackbarPos('K', 'Trackbars') 137 | kernel_tracker = (kernel_tracker + 1) * 2 + 1 138 | sigma_tracker = (kernel_tracker - 1) / 6 139 | print(kernel_tracker, sigma_tracker) 140 | LoG_convolved() 141 | 142 | def handle_threshold_trackbars(): 143 | global log_image, threshold_tracker, final_image, low_threshold_tracker, high_threshold_tracker 144 | low_threshold_tracker = int(cv2.getTrackbarPos('Low Threshold', 'Trackbars')) 145 | high_threshold_tracker =int(cv2.getTrackbarPos('High Threshold', 'Trackbars')) 146 | 147 | thresh_image = double_threshold_image(log_image, low_threshold_tracker, high_threshold_tracker) 148 | final_image = hysteresis_tracking(thresh_image) 149 | final_image = cv2.putText(final_image, f'LT: {low_threshold_tracker} HT: {high_threshold_tracker} K: {kernel_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 150 | cv2.imshow('Thresholded image', thresh_image) 151 | cv2.imshow('Final image', final_image) 152 | 153 | def on_low_thresh_trackbar(val): 154 | handle_threshold_trackbars() 155 | 156 | def on_high_thresh_trackbar(val): 157 | handle_threshold_trackbars() 158 | 159 | IMAGE_FILE = '../../../test-images/simple-shapes.png' 160 | img = cv2.imread(IMAGE_FILE, 0) 161 | cv2.imshow('Original image', img) 162 | log_image = img 163 | final_image = img 164 | low_threshold_tracker = 0 165 | high_threshold_tracker = 0 166 | sigma_tracker = 0 167 | kernel_tracker = 0 168 | #Creating trackbar window 169 | cv2.namedWindow('Trackbars') 170 | cv2.resizeWindow('Trackbars', 640, 240) 171 | cv2.createTrackbar("K", "Trackbars", 1, 20, on_kernel_trackbar) 172 | cv2.createTrackbar("Low Threshold", "Trackbars", 1, 255, on_low_thresh_trackbar) 173 | cv2.createTrackbar("High Threshold", "Trackbars", 1, 255, on_high_thresh_trackbar) 174 | 175 | on_kernel_trackbar(4) 176 | cv2.waitKey(0) 177 | cv2.destroyAllWindows() 178 | -------------------------------------------------------------------------------- /algorithms/LoG-Edge-Detector/Seperated-Kernels/LoG_seperated.py: -------------------------------------------------------------------------------- 1 | from typing import final 2 | import cv2 3 | import numpy as np 4 | from scipy.signal import convolve2d 5 | 6 | def convolve(image, kernel): 7 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 8 | return convolved_matrix 9 | 10 | def any_neighbor_has_value(img, i, j, value=0): 11 | k_start = 0 if j - 1 < 0 else -1 12 | k_end = 0 if j + 1 > img.shape[1] - 1 else 1 13 | l_start = 0 if i - 1 < 0 else -1 14 | l_end = 0 if i + 1 > img.shape[0] - 1 else 1 15 | for k in range(k_start, k_end): 16 | for l in range(l_start, l_end): 17 | if img[i+l, j+k] == value: 18 | return True 19 | return False 20 | 21 | def zero_crossing(image): 22 | z_c_image = np.zeros(image.shape) 23 | 24 | # For each pixel, count the number of positive 25 | # and negative pixels in the neighborhood 26 | 27 | for i in range(1, image.shape[0] - 1): 28 | for j in range(1, image.shape[1] - 1): 29 | negative_count = 0 30 | positive_count = 0 31 | neighbour = [image[i+1, j-1],image[i+1, j],image[i+1, j+1],image[i, j-1],image[i, j+1],image[i-1, j-1],image[i-1, j],image[i-1, j+1]] 32 | d = max(neighbour) 33 | e = min(neighbour) 34 | for h in neighbour: 35 | if h>0: 36 | positive_count += 1 37 | elif h<0: 38 | negative_count += 1 39 | 40 | 41 | # If both negative and positive values exist in 42 | # the pixel neighborhood, then that pixel is a 43 | # potential zero crossing 44 | 45 | z_c = ((negative_count > 0) and (positive_count > 0)) 46 | 47 | # Change the pixel value with the maximum neighborhood 48 | # difference with the pixel 49 | 50 | if z_c: 51 | if image[i,j]>0: 52 | z_c_image[i, j] = image[i,j] + np.abs(e) 53 | elif image[i,j]<0: 54 | z_c_image[i, j] = np.abs(image[i,j]) + d 55 | 56 | return np.uint8(z_c_image / np.max(z_c_image) * 255) 57 | 58 | def double_threshold_image(img, low_thresh, high_thresh, weak_value=70, strong_value=255): 59 | # Thresholding the image 60 | strong = np.where(img > high_thresh) 61 | zeros = np.where(img < low_thresh) 62 | weak = np.where((img <= high_thresh) & (img >= low_thresh)) 63 | 64 | new_image = np.zeros(img.shape, dtype=np.uint8) 65 | new_image[strong] = strong_value 66 | new_image[weak] = weak_value 67 | new_image[zeros] = 0 68 | return new_image 69 | 70 | def is_valid_pixel(img, i, j): 71 | return i < img.shape[0] and i >= 0 and j < img.shape[1] and j >= 0 72 | 73 | def valid_neighbors(img, i, j): 74 | neighbors = [] 75 | for k in range(-1, 2 , 1): 76 | for p in range(-1, 2, 1): 77 | if is_valid_pixel(img, i + k, j + p): 78 | neighbors.append((i + k, j + p)) 79 | return neighbors 80 | 81 | def hysteresis_tracking(img, weak_value=70, strong_value=255): 82 | img_row, img_col = img.shape 83 | 84 | new_img = img.copy() 85 | for i in range(img_row): 86 | for j in range(img_col): 87 | if new_img[i,j] == strong_value: 88 | queue = [] 89 | for neighbor in valid_neighbors(new_img, i, j): 90 | if new_img[neighbor[0], neighbor[1]] == weak_value: 91 | queue.append(neighbor) 92 | while len(queue) > 0: 93 | x, y = queue.pop(0) 94 | if new_img[x,y] == weak_value: 95 | new_img[x,y] = strong_value 96 | for neighbor in valid_neighbors(new_img, x, y): 97 | if new_img[neighbor[0], neighbor[1]] == weak_value: 98 | queue.append(neighbor) 99 | 100 | for i in range(img_row): 101 | for j in range(img_col): 102 | if new_img[i,j] == weak_value: 103 | new_img[i,j] = 0 104 | 105 | return new_img 106 | 107 | def LoG_seperated(img): 108 | global log_image, final_image, kernel_tracker, sigma_tracker, low_threshold_tracker, high_threshold_tracker 109 | #Smoothing the image using gaussian kernel 110 | if sigma_tracker != 0: 111 | blurred_image = cv2.GaussianBlur(img, (kernel_tracker, kernel_tracker), sigma_tracker) 112 | else: 113 | blurred_image = img 114 | cv2.imshow('Gaussian Blur', blurred_image) 115 | # Apply laplacian kernel 116 | kernel = np.array([[1, 1, 1], 117 | [1, -8, 1], 118 | [1, 1, 1]]) 119 | 120 | log_image = zero_crossing(convolve(blurred_image, kernel)) 121 | cv2.imshow('zero crossing', log_image) 122 | l_image = zero_crossing(convolve(img, kernel)) 123 | cv2.imshow('zero crossing original', l_image) 124 | # Thresholding the image 125 | thresh_image = double_threshold_image(log_image, low_threshold_tracker, high_threshold_tracker) 126 | cv2.imshow('Thresholded image', thresh_image) 127 | final_image = hysteresis_tracking(thresh_image) 128 | final_image = cv2.putText(final_image, f'LT: {low_threshold_tracker} HT: {high_threshold_tracker} K: {kernel_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1) 129 | 130 | # Show results 131 | cv2.imshow('Final image', final_image) 132 | 133 | def handle_trackbar(): 134 | global img, threshold_tracker, kernel_tracker, sigma_tracker 135 | sigma_tracker = int(cv2.getTrackbarPos('Sigma', 'Trackbars')) 136 | kernel_tracker = int(cv2.getTrackbarPos('K', 'Trackbars') + 1) * 2 + 1 137 | LoG_seperated(img) 138 | 139 | def on_sigma_trackbar(val): 140 | handle_trackbar() 141 | 142 | def on_kernel_trackbar(val): 143 | handle_trackbar() 144 | 145 | def handle_threshold_trackbars(): 146 | global log_image, threshold_tracker, final_image 147 | low_threshold_tracker = int(cv2.getTrackbarPos('Low Threshold', 'Trackbars')) 148 | high_threshold_tracker =int(cv2.getTrackbarPos('High Threshold', 'Trackbars')) 149 | 150 | thresh_image = double_threshold_image(log_image, low_threshold_tracker, high_threshold_tracker) 151 | final_image = hysteresis_tracking(thresh_image) 152 | final_image = cv2.putText(final_image, f'LT: {low_threshold_tracker} HT: {high_threshold_tracker} K: {kernel_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 153 | cv2.imshow('Thresholded image', thresh_image) 154 | cv2.imshow('Final image', final_image) 155 | 156 | def on_low_thresh_trackbar(val): 157 | handle_threshold_trackbars() 158 | 159 | def on_high_thresh_trackbar(val): 160 | handle_threshold_trackbars() 161 | 162 | 163 | 164 | IMAGE_FILE = '../../../test-images/lena-noisy.jpeg' 165 | IMAGE_NAME = IMAGE_FILE.split('/')[-1] 166 | img = cv2.imread(IMAGE_FILE, 0) 167 | log_image = img 168 | final_image = img 169 | low_threshold_tracker = 0 170 | high_threshold_tracker = 0 171 | sigma_tracker = 0 172 | kernel_tracker = 1 173 | 174 | #Creating trackbar window 175 | cv2.namedWindow('Trackbars') 176 | cv2.resizeWindow('Trackbars', 640, 240) 177 | cv2.createTrackbar("K", "Trackbars", 1, 20, on_kernel_trackbar) 178 | cv2.createTrackbar("Sigma", "Trackbars", 1, 24, on_sigma_trackbar) 179 | cv2.createTrackbar("Low Threshold", "Trackbars", 1, 255, on_low_thresh_trackbar) 180 | cv2.createTrackbar("High Threshold", "Trackbars", 1, 255, on_high_thresh_trackbar) 181 | 182 | 183 | on_sigma_trackbar(1) 184 | on_kernel_trackbar(1) 185 | 186 | cv2.waitKey(0) 187 | cv2.destroyAllWindows() 188 | 189 | # Save image 190 | final_image = cv2.putText(final_image, f'LT: {low_threshold_tracker} HT: {high_threshold_tracker} K: {kernel_tracker}, Sig: {sigma_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 191 | cv2.imwrite('results/' + IMAGE_NAME, final_image) -------------------------------------------------------------------------------- /algorithms/LoG-Edge-Detector/Seperated-Kernels/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/LoG-Edge-Detector/Seperated-Kernels/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/LoG-Edge-Detector/Seperated-Kernels/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/LoG-Edge-Detector/Seperated-Kernels/results/lena.png -------------------------------------------------------------------------------- /algorithms/LoG-Edge-Detector/Seperated-Kernels/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/LoG-Edge-Detector/Seperated-Kernels/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/Prewit-Edge-Detector/prewit.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.filedialog as fd 3 | import cv2 4 | import numpy as np 5 | from scipy.signal import convolve2d 6 | 7 | def convolve(image, kernel): 8 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 9 | return np.array(convolved_matrix, dtype=np.float64) 10 | 11 | def prewit_algorithm(img): 12 | # Apply Prewit Gx kernel 13 | x_gradient_matrix = convolve(img, gx_kernel) 14 | # Apply Prewit Gy kernel 15 | y_gradient_matrix = convolve(img, gy_kernel) 16 | # Combine the gradient matrices to get the gradient magnitude 17 | gradient_magnitude = np.sqrt(x_gradient_matrix**2 + y_gradient_matrix**2) 18 | return gradient_magnitude / np.max(gradient_magnitude) * 255 19 | 20 | def on_tr_trackbar(val): 21 | global gradient_image, threshold_tracker, img 22 | gradient_magnitude = prewit_algorithm(img) 23 | 24 | threshold_tracker = int(cv2.getTrackbarPos('Threshold', 'Trackbars')) 25 | 26 | gradient_image = np.zeros(gradient_magnitude.shape, dtype=np.uint8) 27 | gradient_image[gradient_magnitude > threshold_tracker] = 255 28 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 29 | 30 | # Show results 31 | cv2.imshow('Gray Scale', img) 32 | cv2.imshow('Prewit Operator', gradient_image) 33 | 34 | IMAGE_FILE = '../../test-images/simple-shapes.png' 35 | 36 | IMAGE_NAME = IMAGE_FILE.split('/')[-1] 37 | img = cv2.imread(IMAGE_FILE, 0) 38 | 39 | threshold_tracker = 0 40 | 41 | gradient_image = None 42 | 43 | gx_kernel = np.array([[1, 0, -1], 44 | [1, 0, -1], 45 | [1, 0, -1]]) 46 | 47 | gy_kernel = np.array([[1, 1, 1], 48 | [0, 0, 0], 49 | [-1, -1, -1]]) 50 | 51 | #Creating trackbar window 52 | cv2.namedWindow('Trackbars') 53 | cv2.resizeWindow('Trackbars', 1000, 240) 54 | cv2.createTrackbar("Threshold", "Trackbars", 0, 255, on_tr_trackbar) 55 | 56 | on_tr_trackbar(50) 57 | 58 | cv2.waitKey(0) 59 | cv2.destroyAllWindows() 60 | 61 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 62 | cv2.imwrite('results/' + IMAGE_NAME, gradient_image) -------------------------------------------------------------------------------- /algorithms/Prewit-Edge-Detector/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Prewit-Edge-Detector/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/Prewit-Edge-Detector/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Prewit-Edge-Detector/results/lena.png -------------------------------------------------------------------------------- /algorithms/Prewit-Edge-Detector/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Prewit-Edge-Detector/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/Robert-Cross-Detector/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Robert-Cross-Detector/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/Robert-Cross-Detector/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Robert-Cross-Detector/results/lena.png -------------------------------------------------------------------------------- /algorithms/Robert-Cross-Detector/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Robert-Cross-Detector/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/Robert-Cross-Detector/robert_cross.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.filedialog as fd 3 | import cv2 4 | import numpy as np 5 | from scipy.signal import convolve2d 6 | 7 | def convolve(image, kernel): 8 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 9 | return np.array(convolved_matrix, dtype=np.float64) 10 | 11 | def robert_cross_algorithm(img): 12 | # Apply Robert Cross Gx kernel 13 | x_gradient_matrix = convolve(img, gx_kernel) 14 | # Apply Robert Cross Gy kernel 15 | y_gradient_matrix = convolve(img, gy_kernel) 16 | # Combine the gradient matrices to get the gradient magnitude 17 | gradient_matrix = np.sqrt(x_gradient_matrix**2 + y_gradient_matrix**2) 18 | return gradient_matrix / np.max(gradient_matrix) * 255 19 | 20 | def on_tr_trackbar(val): 21 | global gradient_image, threshold_tracker, img 22 | gradient_magnitude = robert_cross_algorithm(img) 23 | 24 | threshold_tracker = int(cv2.getTrackbarPos('Threshold', 'Trackbars')) 25 | 26 | gradient_image = np.zeros(gradient_magnitude.shape, dtype=np.uint8) 27 | gradient_image[gradient_magnitude > threshold_tracker] = 255 28 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 29 | 30 | # Show results 31 | cv2.imshow('Gray Scale', img) 32 | cv2.imshow('Robert Cross Operator', gradient_image) 33 | 34 | IMAGE_FILE = '../../test-images/simple-shapes.png' 35 | IMAGE_NAME = IMAGE_FILE.split('/')[-1] 36 | img = cv2.imread(IMAGE_FILE, 0) 37 | 38 | threshold_tracker = 0 39 | 40 | gradient_image = None 41 | 42 | gx_kernel = np.array([[-1, 0], 43 | [0, 1]]) 44 | 45 | gy_kernel = np.array([[0, -1], 46 | [1, 0]]) 47 | 48 | #Creating trackbar window 49 | cv2.namedWindow('Trackbars') 50 | cv2.resizeWindow('Trackbars', 640, 240) 51 | cv2.createTrackbar("Threshold", "Trackbars", 0, 255, on_tr_trackbar) 52 | 53 | on_tr_trackbar(50) 54 | 55 | cv2.waitKey(0) 56 | cv2.destroyAllWindows() 57 | 58 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 59 | cv2.imwrite('results/' + IMAGE_NAME, gradient_image) 60 | 61 | -------------------------------------------------------------------------------- /algorithms/Sobel-Edge-Detector/results/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Sobel-Edge-Detector/results/lena-noisy.jpeg -------------------------------------------------------------------------------- /algorithms/Sobel-Edge-Detector/results/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Sobel-Edge-Detector/results/lena.png -------------------------------------------------------------------------------- /algorithms/Sobel-Edge-Detector/results/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/algorithms/Sobel-Edge-Detector/results/simple-shapes.png -------------------------------------------------------------------------------- /algorithms/Sobel-Edge-Detector/sobel.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import tkinter.filedialog as fd 3 | import cv2 4 | import numpy as np 5 | from scipy.signal import convolve2d 6 | import time 7 | 8 | def convolve(image, kernel): 9 | convolved_matrix = convolve2d(image, kernel, mode='same', boundary='symm') 10 | return np.array(convolved_matrix, dtype=np.float64) 11 | 12 | def sobel_algorithm(img): 13 | # Apply Sobel Gx kernel 14 | x_gradient_matrix = convolve(img, gx_kernel) 15 | # Apply Sobel Gy kernel 16 | y_gradient_matrix = convolve(img, gy_kernel) 17 | # Combine the gradient matrices to get the gradient magnitude 18 | gradient_magnitude = np.sqrt(x_gradient_matrix**2 + y_gradient_matrix**2) 19 | return gradient_magnitude / np.max(gradient_magnitude) * 255 20 | 21 | def on_tr_trackbar(val): 22 | global gradient_image, threshold_tracker, img 23 | threshold_tracker = int(cv2.getTrackbarPos('Threshold', 'Trackbars')) 24 | 25 | start_time = time.time() 26 | gradient_magnitude = sobel_algorithm(img) 27 | gradient_image = np.zeros(gradient_magnitude.shape, dtype=np.uint8) 28 | gradient_image[gradient_magnitude > threshold_tracker] = 255 29 | print(time.time() - start_time) 30 | 31 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 32 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1) 33 | 34 | # Show results 35 | cv2.imshow('Gray Scale', img) 36 | cv2.imshow('Sobel Operator', gradient_image) 37 | 38 | IMAGE_FILE = '../../test-images/simple-shapes.png' 39 | IMAGE_NAME = IMAGE_FILE.split('/')[-1] 40 | img = cv2.imread(IMAGE_FILE, 0) 41 | 42 | threshold_tracker = 0 43 | 44 | gradient_image = None 45 | 46 | gx_kernel = np.array([[1, 0, -1], 47 | [2, 0, -2], 48 | [1, 0, -1]]) 49 | 50 | gy_kernel = np.array([[1, 2, 1], 51 | [0, 0, 0], 52 | [-1, -2, -1]]) 53 | 54 | #Creating trackbar window 55 | cv2.namedWindow('Trackbars') 56 | cv2.resizeWindow('Trackbars', 640, 240) 57 | cv2.createTrackbar("Threshold", "Trackbars", 0, 255, on_tr_trackbar) 58 | 59 | on_tr_trackbar(50) 60 | 61 | cv2.waitKey(0) 62 | cv2.destroyAllWindows() 63 | 64 | gradient_image = cv2.putText(gradient_image, f'T: {threshold_tracker}', (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1) 65 | cv2.imwrite('results/' + IMAGE_NAME, gradient_image) -------------------------------------------------------------------------------- /report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/report.pdf -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | opencv-python==4.5.5.64 2 | scipy==1.8.0 -------------------------------------------------------------------------------- /test-images/lena-noisy.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/test-images/lena-noisy.jpeg -------------------------------------------------------------------------------- /test-images/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/test-images/lena.png -------------------------------------------------------------------------------- /test-images/simple-shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Amirparsa-Sal/Edge-Detection-Algorithms/a18c4b67d18989c64b3c4888c95abb94e9a8fb50/test-images/simple-shapes.png --------------------------------------------------------------------------------