11"""
2- Canny Edge Detector - It is used to identify edges in images.
2+ Canny Edge Detector - It is used to identify edges in images.
33Implementation of the Canny Edge Detection algorithm using NumPy.
44
55https://en.wikipedia.org/wiki/Canny_edge_detector
@@ -16,8 +16,8 @@ def grayscale(image: np.ndarray) -> np.ndarray:
1616 To convert RGB -> grayscale using luminance weights.
1717 """
1818 return np .dot (image [..., :3 ], [0.299 , 0.587 , 0.114 ]).astype (np .uint8 )
19- #gray = 0.299R+0.587G+0.114B
20- #np.uint8 = converts values to 8-bit integers(0-255)
19+ # gray = 0.299R+0.587G+0.114B
20+ # np.uint8 = converts values to 8-bit integers(0-255)
2121
2222
2323def gaussian_kernel (kernel_size : int = 5 , sigma : float = 1.4 ) -> np .ndarray :
@@ -28,15 +28,13 @@ def gaussian_kernel(kernel_size: int = 5, sigma: float = 1.4) -> np.ndarray:
2828 """
2929 if kernel_size % 2 == 0 :
3030 raise ValueError ("kernel's size must be odd" )
31- #as we need a center
31+ # as we need a center
3232
3333 center = kernel_size // 2
3434
3535 x , y = np .mgrid [- center : center + 1 , - center : center + 1 ]
36- #assigns weights, center gets largest while farther pixels get smaller values.
37- kernel = (1 / (2 * pi * sigma ** 2 )) * np .exp (
38- - ((x ** 2 + y ** 2 ) / (2 * sigma ** 2 ))
39- )
36+ # assigns weights, center gets largest while farther pixels get smaller values.
37+ kernel = (1 / (2 * pi * sigma ** 2 )) * np .exp (- ((x ** 2 + y ** 2 ) / (2 * sigma ** 2 )))
4038
4139 return kernel / kernel .sum ()
4240
@@ -54,19 +52,25 @@ def convolve(image: np.ndarray, kernel: np.ndarray) -> np.ndarray:
5452 padding = kernel_size // 2
5553
5654 padded_image = np .pad (image , padding , mode = "constant" )
57- #convolution near borders needs neighbors
55+ # convolution near borders needs neighbors
5856 output = np .zeros_like (image , dtype = np .float64 )
5957
6058 for row in range (image_height ):
6159 for column in range (image_width ):
62- region = padded_image [row : row + kernel_size ,column : column + kernel_size ]
60+ region = padded_image [
61+ row : row + kernel_size , column : column + kernel_size
62+ ]
6363
6464 output [row , column ] = np .sum (region * kernel )
65- #multiply and add
65+ # multiply and add
6666 return output
6767
6868
69- def gaussian_blur (image : np .ndarray ,kernel_size : int = 5 ,sigma : float = 1.4 ,) -> np .ndarray :
69+ def gaussian_blur (
70+ image : np .ndarray ,
71+ kernel_size : int = 5 ,
72+ sigma : float = 1.4 ,
73+ ) -> np .ndarray :
7074 """
7175 Blurring image using Gaussian filter.
7276
@@ -107,16 +111,19 @@ def sobel_gradients(image: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
107111 gradient_x = convolve (image , sobel_x )
108112 gradient_y = convolve (image , sobel_y )
109113
110- gradient_magnitude = np .hypot (gradient_x , gradient_y )# edge strength
114+ gradient_magnitude = np .hypot (gradient_x , gradient_y ) # edge strength
111115
112116 gradient_magnitude = (gradient_magnitude / gradient_magnitude .max ()) * 255
113- #normalize values to 0-255
117+ # normalize values to 0-255
114118 gradient_direction = np .arctan2 (gradient_y , gradient_x )
115- #computes edge direction angle, just tan^-1(Gy/Gx)
119+ # computes edge direction angle, just tan^-1(Gy/Gx)
116120 return gradient_magnitude , gradient_direction
117121
118122
119- def non_maximum_suppression (magnitude : np .ndarray ,direction : np .ndarray ,) -> np .ndarray :
123+ def non_maximum_suppression (
124+ magnitude : np .ndarray ,
125+ direction : np .ndarray ,
126+ ) -> np .ndarray :
120127 """
121128 Suppress non-maximum gradient values.
122129
@@ -139,7 +146,7 @@ def non_maximum_suppression(magnitude: np.ndarray,direction: np.ndarray,) -> np.
139146
140147 current_angle = angle [row , column ]
141148
142- if ( 0 <= current_angle < 22.5 or 157.5 <= current_angle <= 180 ) :
149+ if 0 <= current_angle < 22.5 or 157.5 <= current_angle <= 180 :
143150 neighbor_1 = magnitude [row , column + 1 ]
144151 neighbor_2 = magnitude [row , column - 1 ]
145152
@@ -155,13 +162,20 @@ def non_maximum_suppression(magnitude: np.ndarray,direction: np.ndarray,) -> np.
155162 neighbor_1 = magnitude [row - 1 , column - 1 ]
156163 neighbor_2 = magnitude [row + 1 , column + 1 ]
157164
158- if (magnitude [row , column ] >= neighbor_1 and magnitude [row , column ] >= neighbor_2 ):
165+ if (
166+ magnitude [row , column ] >= neighbor_1
167+ and magnitude [row , column ] >= neighbor_2
168+ ):
159169 suppressed [row , column ] = magnitude [row , column ]
160170
161171 return suppressed
162172
163173
164- def double_threshold (image : np .ndarray ,low_threshold_ratio : float = 0.05 ,high_threshold_ratio : float = 0.15 ,) -> tuple [np .ndarray , int , int ]:
174+ def double_threshold (
175+ image : np .ndarray ,
176+ low_threshold_ratio : float = 0.05 ,
177+ high_threshold_ratio : float = 0.15 ,
178+ ) -> tuple [np .ndarray , int , int ]:
165179 """
166180 Apply double thresholding.
167181 To separate strong edges from weak edges.
@@ -196,7 +210,11 @@ def double_threshold(image: np.ndarray,low_threshold_ratio: float = 0.05,high_th
196210 return result , weak , strong
197211
198212
199- def hysteresis (image : np .ndarray ,weak : int ,strong : int = 255 ,) -> np .ndarray :
213+ def hysteresis (
214+ image : np .ndarray ,
215+ weak : int ,
216+ strong : int = 255 ,
217+ ) -> np .ndarray :
200218 """
201219 Track edges using hysteresis.
202220
@@ -230,7 +248,13 @@ class CannyEdgeDetector:
230248 Canny Edge Detector implementation.
231249 """
232250
233- def __init__ (self ,kernel_size : int = 5 ,sigma : float = 1.4 ,low_threshold_ratio : float = 0.05 ,high_threshold_ratio : float = 0.15 ,) -> None :
251+ def __init__ (
252+ self ,
253+ kernel_size : int = 5 ,
254+ sigma : float = 1.4 ,
255+ low_threshold_ratio : float = 0.05 ,
256+ high_threshold_ratio : float = 0.15 ,
257+ ) -> None :
234258 self .kernel_size = kernel_size
235259 self .sigma = sigma
236260 self .low_threshold_ratio = low_threshold_ratio
@@ -259,9 +283,7 @@ def detect(self, image_path: str) -> np.ndarray:
259283 self .sigma ,
260284 )
261285
262- gradient_magnitude , gradient_direction = sobel_gradients (
263- blurred_image
264- )
286+ gradient_magnitude , gradient_direction = sobel_gradients (blurred_image )
265287
266288 suppressed_image = non_maximum_suppression (
267289 gradient_magnitude ,
@@ -288,4 +310,4 @@ def detect(self, image_path: str) -> np.ndarray:
288310
289311 detected_edges = detector .detect ("Screenshot 2026-05-11 065624.png" )
290312
291- cv2 .imwrite ("canny_edges.jpg" , detected_edges )
313+ cv2 .imwrite ("canny_edges.jpg" , detected_edges )
0 commit comments