In this tutorial you will learn how to:
Filter out any noise. The Gaussian filter is used for this purpose. An example of a Gaussian kernel of that might be used is shown below:
Find the intensity gradient of the image. For this, we follow a procedure analogous to Sobel:
Apply a pair of convolution masks (in and directions:
Find the gradient strength and direction with:
The direction is rounded to one of four possible angles (namely 0, 45, 90 or 135)
Non-maximum suppression is applied. This removes pixels that are not considered to be part of an edge. Hence, only thin lines (candidate edges) will remain.
Hysteresis: The final step. Canny does use two thresholds (upper and lower):
Canny recommended a upper:lower ratio between 2:1 and 3:1.
For more details, you can always consult your favorite Computer Vision book.
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
using namespace cv;
/// Global variables
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
char* window_name = "Edge Map";
/**
* @function CannyThreshold
* @brief Trackbar callback - Canny thresholds input with a ratio 1:3
*/
void CannyThreshold(int, void*)
{
/// Reduce noise with a kernel 3x3
blur( src_gray, detected_edges, Size(3,3) );
/// Canny detector
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
/// Using Canny's output as a mask, we display our result
dst = Scalar::all(0);
src.copyTo( dst, detected_edges);
imshow( window_name, dst );
}
/** @function main */
int main( int argc, char** argv )
{
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
/// Create a matrix of the same type and size as src (for dst)
dst.create( src.size(), src.type() );
/// Convert the image to grayscale
cvtColor( src, src_gray, COLOR_BGR2GRAY );
/// Create a window
namedWindow( window_name, WINDOW_AUTOSIZE );
/// Create a Trackbar for user to enter threshold
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
/// Show the image
CannyThreshold(0, 0);
/// Wait until user exit program by pressing a key
waitKey(0);
return 0;
}
Create some needed variables:
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
char* window_name = "Edge Map";
Note the following:
a. We establish a ratio of lower:upper threshold of 3:1 (with the variable *ratio*)
b. We set the kernel size of :math:`3` (for the Sobel operations to be performed internally by the Canny function)
c. We set a maximum value for the lower Threshold of :math:`100`.
Loads the source image:
/// Load an image
src = imread( argv[1] );
if( !src.data )
{ return -1; }
Create a matrix of the same type and size of src (to be dst)
dst.create( src.size(), src.type() );
Convert the image to grayscale (using the function cvtColor:
cvtColor( src, src_gray, COLOR_BGR2GRAY );
Create a window to display the results
namedWindow( window_name, WINDOW_AUTOSIZE );
Create a Trackbar for the user to enter the lower threshold for our Canny detector:
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
Observe the following:
Let’s check the CannyThreshold function, step by step:
First, we blur the image with a filter of kernel size 3:
blur( src_gray, detected_edges, Size(3,3) );
Second, we apply the OpenCV function Canny:
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
where the arguments are:
We fill a dst image with zeros (meaning the image is completely black).
dst = Scalar::all(0);
Finally, we will use the function copyTo to map only the areas of the image that are identified as edges (on a black background).
src.copyTo( dst, detected_edges);
copyTo copy the src image onto dst. However, it will only copy the pixels in the locations where they have non-zero values. Since the output of the Canny detector is the edge contours on a black background, the resulting dst will be black in all the area but the detected edges.
We display our result:
imshow( window_name, dst );
After compiling the code above, we can run it giving as argument the path to an image. For example, using as an input the following image:
Moving the slider, trying different threshold, we obtain the following result:
Notice how the image is superposed to the black background on the edge regions.