├── README.md
├── camerman.tif
├── canny_edge_detection.m
├── garbage
├── 13.png
├── 14.png
├── 15.png
├── 16.png
├── 17.png
├── 18.png
├── 19.png
├── 20.png
├── 21.png
├── 22.png
├── 23.png
├── 24.png
└── 25.png
├── house.tif
├── lena.tif
├── main.m
└── van.tif
/README.md:
--------------------------------------------------------------------------------
1 | # CANNY EDGE DETECTION ALGORITHM
2 | Matlab implementation of Canny edge detection algorithm from scratch.
3 | # IMPLEMENTATION
4 | Algorithm contains following steps:
5 | **1.** Filtering input image with Gaussian filter with given standard deviation (filter size should be equal or greater than 6 * sigma)
6 | **2.** Determining horizontal and vertical gradients of the filtered image
7 |
8 | Original Image | Gradients in x direction | Gradients in y direction
9 | :-------------------------:|:-------------------------:|:-------------------------:
10 |  |  | 
11 |
12 | **3.** Determining magnitude and angle od the gradients with following formulas:
13 | 
14 |
15 | Gradient magnitude | Gradient angle
16 | :-------------------------:|:-------------------------:
17 |  | 
18 |
19 | **4.** Quantization of the angle of the gradient on the following directions: 0, -45, 45, 90 degrees.
20 | 
21 | **5.** Supression of the gradients that do not represent local maximum.
22 | We iterate thorugh every pixel of the magnitude of the gradient and we read qunatized value of the gradient angle for that pixel. For every direction of the gradient angle specific matrix of 0s and 1s is defined:
23 | 
24 | We take the matrix that corresponds to the read quantized value of the gradient angle and multiply it with 3x3 gradient magnitude surrounding of the pixel. If the maximum value in the resulting 3x3 matrix is not in the middle, the pixel value is set to zero. This must NOT be done inplace!
25 | 
26 | **6.** Determining the maps of weak and strong edges based on low and high threshold.
27 | All values of the magnitude of the gradient that are higher than T_high are set to value 1 in the map of the combined edges and they go into the map of the strong edges. All values between T_low and T_high are set to value 50/250 in the map of the combined edges and they go into the map of the weak edges. All values below T_low are set to 0.
28 |
29 | Weak edges | Strong edges | Combined edges
30 | :-------------------------:|:-------------------------:|:-------------------------:
31 |  |  | 
32 |
33 | **7.** Determining final map of edges by including weak edges that are connected with strong edges.
34 | We iterate thorugh the combined map of the edges and if the edge is weak we check the 3x3 surrounding arround that edge. If in that surrounding exists at least 1 strong edge, the value of the weak edge is set to strong. This must NOT be done in place! This procedure repeats until there is no change from weak to strong.
35 | 
36 |
37 | # TEST
38 | Run the **main.m** to test the algorithm on the *house.tif*, *camerman.tif*, *van.tif* and *lena.tif* images.
39 |
--------------------------------------------------------------------------------
/camerman.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/camerman.tif
--------------------------------------------------------------------------------
/canny_edge_detection.m:
--------------------------------------------------------------------------------
1 | function J = canny_edge_detection(I, sigma, Tl, Th)
2 | %canny_edge_detection function uses Canny's algorithm to detect edges on an
3 | %image
4 | % Input image I is type double in range of [0,1].
5 | % Parameter sigma is the standard deviation of the Gaussian filter used in
6 | % the DRoG operator.
7 | % Tl and Th are the absolut values of the low and high
8 | % thresholds used to distinguish weak and strong edges.
9 | % Output image J is binary (0,1), and it only contains edges.
10 | %
11 | % J = canny_edge_detection(I, sigma) uses the default values for Tl = 0.02,
12 | % and for Th = 0.5.
13 | % J = canny_edge_detection(I) uses the default values for sigma = sqrt(2),
14 | % Tl = 0.02, and for Th = 0.5.
15 | %
16 | %
17 | %See also edge
18 |
19 | % --- Argument verification --- %
20 | if (nargin < 1) || (nargin>4)
21 | error('Error: Number of parameters sent to the function ''canny_edge_detection'' exceeds expected range');
22 | elseif nargin == 3
23 | error('Error: Function ''canny_edge_detection'' recieved only 1 threshold');
24 | elseif nargin == 2
25 | Tl = 0.2;
26 | Th = 0.4*Tl;
27 | elseif nargin == 1
28 | Tl = 0.2;
29 | Th = 0.4*Tl;
30 | sigma = sqrt(2);
31 | end
32 | % Checking a pixel value format of parameter I
33 | if (~isa(I,'double'))
34 | error('Error: Input argument I in ''canny_edge_detection'' function has to be a type double');
35 | end
36 | % Checking a if value of parameter limit exceeds demanded range
37 | if (min(I(:))<0 || max(I(:))>1)
38 | error('Error: Input argument I in ''canny_edge_detection'' function has to be in range of [0,1]');
39 | end
40 |
41 | % 1. i 2. KORAK: Filtriranje ulazne slike Gausovom funkcijom i odredjivanje
42 | % horizontalnih i vertikalnih gradijenata
43 | [I_dx, I_dy] = DRoG(I, sigma);
44 |
45 | % 3. KORAK: Odredjivanje magnitude i ugla gradijenta
46 | [Id_magnitude, Id_angle] = magnitude_and_angle(I_dx, I_dy);
47 |
48 | % 4. KORAK: Kvantizacija gradijenta
49 | Id_angle = gradient_quantization(Id_angle);
50 |
51 | % 5. KORAK: Potiskivanje vrednosti gradijenta koji ne predstavljaju
52 | % lokalne maksimume
53 | window_size = 3;
54 | J = non_max_supression(Id_magnitude, Id_angle, window_size);
55 |
56 | % 6. KORAK: Odredjivanja mapa jakih i slabih ivica
57 | weak = 50/250;
58 | strong = 1;
59 | [J, ~, ~] = threshold(J, Tl, Th, weak, strong);
60 |
61 | % 7. KORAK: Povezivanje slabih ivica sa jakim
62 | window_size = 3;
63 | J = hysteresis(J, window_size, weak, strong);
64 | end
65 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 | %
67 | % Lokalna Funkcija : DRoG
68 | %
69 | function [I_dx, I_dy] = DRoG(I, sigma)
70 | %
71 | % Primenjuje DRoG operator na ulaznu sliku I.
72 | % Gausova funkcija operatora ima standradnu devijaciju sigma,
73 | % a dimenzija prozora je prvi neparan ceo broj veci ili jednak 6*sigma
74 | %
75 | % OUTPUTS:
76 | % I_dx: Parcijalni gradijent slike u x pravcu
77 | % I_dy: Parcijalni gradijent slike u y pravcu
78 |
79 | % definisanje dimenzije prozora
80 | kernel_size = ceil(6*sigma);
81 | if (mod(kernel_size, 2) == 0)
82 | kernel_size = kernel_size + 1;
83 | end
84 | if kernel_size < 3 % minimalna velicina kernela je 3
85 | kernel_size = 3;
86 | end
87 | % pravljenje Gausovog filtra
88 | Gauss_filter = fspecial('gaussian', kernel_size, sigma);
89 | % numericki gradijent
90 | [Hx, Hy] = gradient(Gauss_filter);
91 | Hx = Hx/sum(sum(abs(Hx)));
92 | Hy = Hy/sum(sum(abs(Hy)));
93 | % filtriranje (primena operatora)
94 | I_dx = imfilter(I, Hx, 'replicate', 'same');
95 | I_dy = imfilter(I, Hy, 'replicate', 'same');
96 | end
97 |
98 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99 | %
100 | % Local function : magnitude_and_angle
101 | %
102 | function [Id_magnitude, Id_angle] = magnitude_and_angle(I_dx, I_dy)
103 | %
104 | % Funkcija koja racuna magnitudu i ugao gradijenta
105 | %
106 |
107 | Id_magnitude = sqrt(I_dx.^2 + I_dy.^2);
108 | Id_angle = atan(I_dy./I_dx);
109 | Id_angle(Id_magnitude == 0) = 0;
110 | Id_angle = Id_angle*360/2/pi; % konverzija iz radijana u stepene
111 | end
112 |
113 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 | %
115 | % Local function : gradient_quantization
116 | %
117 | function Id_angle = gradient_quantization(Id_angle)
118 | %
119 | % Kvantizuje gradijent na 4 nivoa: -45, 0, 45, 90
120 | %
121 |
122 | Id_angle(Id_angle > -67.5 & Id_angle < -22.5) = -45;
123 | Id_angle(Id_angle >= -22.5 & Id_angle <=22.5) = 0;
124 | Id_angle(Id_angle > 22.5 & Id_angle < 67.5) = 45;
125 | Id_angle(Id_angle >= 67.5 & Id_angle <= 90) = 90;
126 | Id_angle(Id_angle >= -90 & Id_angle <= -67.5) = 90;
127 | end
128 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129 | %
130 | % Local function: non_max_supression
131 | %
132 | function J = non_max_supression(Id_magnitude,Id_angle, window_size)
133 | %
134 | % Potiskivanje vrednosti gradijenata koji ne predstavljaju lokalne maksimume
135 | %
136 |
137 | [N, M] = size(Id_magnitude);
138 | half = (window_size-1)/2; % pola dimenzije prozora
139 | J = Id_magnitude; % incijalizacija izlazne slike
140 | Id_magnitude = padarray(Id_magnitude, [half, half], 'replicate');
141 |
142 | for i = 1+half:N+half
143 | for j = 1+half:M+half
144 | switch Id_angle(i-half,j-half)
145 | case 45
146 | window = Id_magnitude(i-half:i+half,j-half:j+half).*[1 0 0;...
147 | 0 1 0;...
148 | 0 0 1];
149 | case 90
150 | window = Id_magnitude(i-half:i+half,j-half:j+half).*[0 1 0;...
151 | 0 1 0;...
152 | 0 1 0];
153 | case -45
154 | window = Id_magnitude(i-half:i+half,j-half:j+half).*[0 0 1;...
155 | 0 1 0;...
156 | 1 0 0];
157 | case 0
158 | window = Id_magnitude(i-half:i+half,j-half:j+half).*[0 0 0;...
159 | 1 1 1;...
160 | 0 0 0];
161 | end
162 |
163 | max_ind = find(window == max(window(:)));
164 | if(max_ind ~= (window_size^2-1)/2+1) % ako pixel sa max vrednoscu nije u sredini prozora
165 | J(i-half,j-half) = 0; % ne sme da bude inplace
166 | end
167 | end
168 | end
169 | end
170 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
171 | %
172 | % Local function: threshold
173 | %
174 | function [J, Weak_edges, Strong_edges] = threshold(I, Tl, Th, weak, strong)
175 | %
176 | % Odredjivanje mapa jakih i slabih ivica na osnovu pragova
177 | %
178 | % INPUTS:
179 | % J: magnituda gradijenta
180 | % Tl: apsolutna vrednost donjeg praga
181 | % Th: apsolutna vrednost gornjeg praga
182 | % weak: intenzitet piksela koji ce biti dodeljen slabim ivicama
183 | % strong: intenzitet piksela koji ce biti dodeljen jakim ivicama
184 | % OUTPUTS:
185 | % J: mapa ivica koja sadrzi i jake i slabe ivice
186 | % Weak_edges: mapa ivica koja sadrzi samo slabe ivice
187 | % Strong_edges: mapa ivica koja sadrzi samo jake ivice
188 | %
189 | J = I;
190 | J(J >= Th) = strong;
191 | J(J > Tl & J < Th) = weak;
192 | J(J <= Tl) = 0;
193 | Weak_edges = J;
194 | Weak_edges(J == weak) = 1;
195 | Weak_edges(J == strong) = 0;
196 | Strong_edges = J;
197 | Strong_edges(J == weak) = 0;
198 | end
199 |
200 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201 | %
202 | % Local function: hysteresis
203 | %
204 | function J = hysteresis(I, window_size, weak, strong)
205 | %
206 | % Svi pikseli koji u svom susedstvu imaju bar jednu jaku ivicu
207 | % proglasavaju se ivicnim pikselom
208 | %
209 | J = I; % incijalizacija izlazne matrice
210 | [N, M] = size(J);
211 | half = (window_size-1)/2;
212 | changed = true;
213 | while changed
214 | changed = false;
215 | Edges = padarray(J, [half, half], 'replicate'); % posle prolaska kroz sve piksele update-uj ivice
216 | for i = 1+half:N+half % u ove dve petlje prodji kroz sve piksele jednom
217 | for j = 1+half:M+half
218 | if Edges(i, j) == weak % ako je ivica slaba
219 | window = Edges(i-half:i+half, j-half:j+half); % okolina oko slabe ivice
220 | if ~isempty(find(window == strong)) % ako u okolini ima bar jedan 'jak' piksel
221 | J(i-half, j-half) = strong; % ne sme biti inplace
222 | changed = true;
223 | end
224 | end
225 | end
226 | end
227 | end
228 | J(J == weak) = 0; % ivice koju su ostale slabe stavi na 0
229 | end
230 |
231 |
232 |
--------------------------------------------------------------------------------
/garbage/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/13.png
--------------------------------------------------------------------------------
/garbage/14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/14.png
--------------------------------------------------------------------------------
/garbage/15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/15.png
--------------------------------------------------------------------------------
/garbage/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/16.png
--------------------------------------------------------------------------------
/garbage/17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/17.png
--------------------------------------------------------------------------------
/garbage/18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/18.png
--------------------------------------------------------------------------------
/garbage/19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/19.png
--------------------------------------------------------------------------------
/garbage/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/20.png
--------------------------------------------------------------------------------
/garbage/21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/21.png
--------------------------------------------------------------------------------
/garbage/22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/22.png
--------------------------------------------------------------------------------
/garbage/23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/23.png
--------------------------------------------------------------------------------
/garbage/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/24.png
--------------------------------------------------------------------------------
/garbage/25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/garbage/25.png
--------------------------------------------------------------------------------
/house.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/house.tif
--------------------------------------------------------------------------------
/lena.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/lena.tif
--------------------------------------------------------------------------------
/main.m:
--------------------------------------------------------------------------------
1 | clear all;close all;clc;
2 | % testing 'lena.tif' image
3 | lena = imread('lena.tif');
4 | figure(1);
5 | imshow(lena);
6 |
7 | I = double(lena)/255;
8 | J = canny_edge_detection(I, 1, 0.1, 0.2);
9 | figure(2)
10 | imshow(J);
11 | % testing 'house.tif' image
12 | house = imread('house.tif');
13 | figure(3);
14 | imshow(house);
15 | I = double(house)/255;
16 | J = canny_edge_detection(I, 1, 0.01, 0.05);
17 | figure(4);
18 | imshow(J);
19 | % testing 'camerman.tif' image
20 | camerman = imread('camerman.tif');
21 | figure(5);
22 | imshow(camerman);
23 | I = double(camerman)/255;
24 | J = canny_edge_detection(I, 1.5, 0.06, 0.12);
25 | figure(6);
26 | imshow(J);
27 | % testing 'van.tif' image
28 | van = imread('van.tif');
29 | figure(7);
30 | imshow(van);
31 | I = double(van)/255;
32 | J = canny_edge_detection(I, 2, 0.01, 0.065);
33 | figure(8);
34 | imshow(J);
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/van.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/854525f29483c88d976c9583ca77c460abf96a14/van.tif
--------------------------------------------------------------------------------