├── 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 | ![o1](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/15.png) | ![gx](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/13.png) | ![gy](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/14.png) 11 | 12 | **3.** Determining magnitude and angle od the gradients with following formulas:
13 | ![img 15](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/18.png) 14 | 15 | Gradient magnitude | Gradient angle 16 | :-------------------------:|:-------------------------: 17 | ![o1](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/16.png) | ![gx](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/17.png) 18 | 19 | **4.** Quantization of the angle of the gradient on the following directions: 0, -45, 45, 90 degrees.
20 | ![quantization](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/19.png)
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 | ![matriciies](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/25.png)
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 | ![supression](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/20.png)
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 | ![o1](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/22.png) | ![gx](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/21.png) | ![gy](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/23.png) 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 | ![final](https://github.com/Digital-Image-Processing-kosta/Canny-edge-detection-algorithm/blob/master/garbage/24.png)
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 --------------------------------------------------------------------------------