Home > Articles > Graphics > Canny Edge Detection

Canny Edge Detection

1 july 2007, Simon Strandgaard

WARNING: this is a work in progress. This is NOT a reference of how canny is supposed to work, because I havn't gotten it right yet. Please take a look at the non-maximum suppression step and tell me what wrong with it. If you have image-data of how its supposed to look, then I am terrible interessed.

I have roughly followed this document (except 1/115 had to be 1/159):
http://www.pages.drexel.edu/~weg22/can_tut.html.

Found a better text about canny:
http://idlastro.gsfc.nasa.gov/idl_html_help/CANNY.html.

article that describes non-maximum suppression:
http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MARBLE/low/edges/canny.htm.

here is images of each step, so we can check if we are doing it right:
http://robotics.eecs.berkeley.edu/~mayi/imgproc/cademo.html
http://cs.nyu.edu/~cil217/Vision/vision_edge_detector.htm

interactive canny filter
http://matlabserver.cs.rug.nl/cgi-bin/matweb.exe.

STEP0: before anything

before anything

Here is the original Lena picture.

STEP1: gaussian blur

gauss on original image

Converted to grayscale by adding red + green + blue. Then applied a 5x5 filter.

STEP2: deltax

deltax on gauss image

Measure horizontal changes by applying a 3x3 filter to the output from step1.

STEP3: deltay

deltay on gauss image

Measure vertical changes by applying a 3x3 filter to the output from step1.

STEP4: gradient

pytagoras with deltax and deltay

Measure length of deltax and deltay using pythagoras.

STEP5: direction

atan2 with deltax and deltay

Find the direction of deltax and deltay. Remap from degrees into the range 0 to 4.

if the gradient is zero then use a value of 0
    0 ..  22.5 is remapped into 1
 22.5 ..  67.5 is remapped into 2
 67.5 .. 112.5 is remapped into 3
112.5 .. 157.5 is remapped into 4
157.5 .. 180   is remapped into 1

AFAIK directions is only used when doing nonmaximum suppression. Is this correct?

STEP6: nonmaximum suppression

ignore non-edge pixels

I need more info about how this step is supposed to work. My understanding is that one needs to identify possible-edge-pixels.

How is output supposed to look in this step?

for(int y=1; y<height-1; ++y) 
for(int x=1; x<width-1; ++x) {
    int dir = _step5_direction[y * width + x];
    if(!dir) continue;
    
    // choose 2 neighbour pixels depending on
    // our own orientation.
    int i = 0;
    int j = 0;
    switch(dir) {
    case DIRECTION_0:   i =  1; j = 0; break;
    case DIRECTION_45:  i =  1; j = 1; break;
    case DIRECTION_90:  i =  0; j = 1; break;
    case DIRECTION_135: i = -1; j = 1; break;
    }
    int a = _step4_gradient[(y + j) * width + x + i];
    int b = _step4_gradient[(y - j) * width + x - i];
    int center = _step4_gradient[y * width + x];
    
    /*
    we got an edge if the centerpixel has the greatest magnitude. 
    
    example:  0, 2, 4  in this case the magnitude
    is increasing. so the centerpixel is not an edge.
    
    example:  5, 8, 3  in this case we got an edge.
    */
    if((center >= a) && (center >= b)) {
        _step6_max[y * width + x] = 1;
    }
}

STEP7: thresholding

follow edges

Start tracking when a gradient pixel is greater than upper threshold.
Stop tracking when a gradient pixel is below the lower threshold.

Tracking is a recursive operation where you look at the 8 neighbour pixels. If the conditions is satisfied for a pixel then tracking is repeated with that pixel in the center.

I'm looking for something more sensitive to color changes. I wonder if there is a good RGB edge detection algorithm.