Edge Detection

Technology Robotix Society
COMPUTER VISION & ROBOTICS
8 min readJul 2, 2019

Introduction

As the name suggests, edge detection is a means to locate the edges of objects in a particular image. This has applications in detecting corners, sharp turn, and in identifying shapes. Edge detection has a lot of scope for precision and efficiency, and there are many well-developed algorithms to deal with it. Here we will be studying a more basic conversion which will help us understand the concepts of edge detection.

In this program, we use the gray-scale equivalents of the pixels, which represent the intensity of the pixel, for our edge detection. We cycle through each pixel, and check all the 8 pixels around it, in a sub-unit of 3X3. We store the highest value of the brightness and the lowest value of the difference. If the difference between the highest and the lowest value is greater than a particular threshold, then that particular pixel is considered to belong to an edge, and in the resultant image, we designate as black (part of an edge). Otherwise, we designate it white. The final edge-detected image is a binary image where the black portions represent the edges.

The figure shown below shows a rather rudimentary example of edge detection:

General Edge Detection

Program
Given below is a function which takes as parameters an image and the threshold, and returns the image with only edges, based on the threshold

#include “opencv2/core/core.hpp”
#include “opencv2/imgproc/imgproc.hpp”
#include “opencv2/highgui/highgui.hpp”
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
Mat EdgeDetect(Mat img, int threshold){
Mat result(img.rows,img.cols,CV_8UC1);
//the result image matrix is initialized Mat
grayscale(img.rows,img.cols,CV_8UC1);
//the grayscale image matrix is initialized
cvtColor(img,grayscale,CV_BGR2GRAY);
//Convert image to a grayscale image
int i,j,k,l,max,min,val,r = img.rows,c = img.cols; for(i=1;i<r-1;i++){
//loop through rows 1 to r-1 , leaving the rows at the edge
for(j=1;j<c-1;j++){
//loop through columns 1 to c-1 , leaving the columns at the edge
max = min = grayscale.at<uchar>(i,j);
//set max and min to same grayscale value at current pixel
for(k=i-1;k<=i+1;k++){
//loop through rows in kernel (3x3) created around pixel(i,j)
for(l=j-1;l<=j+1;l++){
//loop through columns in the kernel created around pixel(i,j)
val = grayscale.at<uchar>(k,l);
if(val>max)
//Set new maximum value for the given kernel
max = val;
else if(val<min)
//Set new minimum value for the given kernel
min = val;
}
}
//Compare difference of max and min values with threshold
if(max-min>threshold)
result.at<uchar>(i,j) = 0;
//If difference > threshold , set pixel value to 0 or black
else
result.at<uchar>(i,j) = 255;
//if difference < threshold, set pixel value to 255 or white
}
}
grayscale.release();
//Delete instance grayscale by calling release()
return result;
//Return the resultant image with detected edges
}
int main(){
string file_name;
int threshold;
cout<<”Enter name of file:”;
cin>>file_name;
cout<<”Enter threshold:”;
cin>>threshold;
Mat image = imread(file_name);
Mat edge_img = EdgeDetect(image,threshold);
imshow(“Edges Extracted”,edge_img);
waitKey(0);
image.release();
edge_img.release();
return 0;
}

Understanding the code

The above function is a modular one, for the purpose of edge detection. The image to be worked upon is given as a parameter, as is the threshold which will be used for our algorithm. In the function, we accept these parameters and create the other variables needed for it.
With the nested for loops, we cycle through each pixel of the loaded image. Then we have another nested for loop of 3 iterations each to focus on the sub-unit around the pixel [i,j]. The maximum and minimum values of the gray-scale equivalents are computed and if the difference between them is higher than the threshold, as has been explained, the pixel in the resultant binary image is assigned 0 to indicate black or part of an edge, or otherwise 255 to indicate white or not an edge.
Finally, the resultant image is returned to the calling function.

Canny Edge Detection

This is one of the most efficient and successful edge detection methods. It utilizes a multi-stage algorithm, operating on the gray-scale version of the image under consideration. The algorithm involves computations of the rate of change of pixel values in any particular direction. If the change is very high, then, similar to the basic algorithm we discussed before, the pixel is expected to be part of an edge. The algorithm also checks whether the gradient is a local maxima at that point, which helps it to be more precise. The thresholds of this edge detector are also very important.

The upper threshold specifies the value above which a gradient would definitely be considered part of an edge. As for the lower threshold, gradients below it are to be discarded entirely, while values in between the two are to be investigated for possible edge linking, i.e if nearby pixels happen to be part of an edge or not, and if they fulfill other criteria.

Program
The program below utilizes Canny edge detection on the frames of a video

#include “opencv2/core/core.hpp”
#include “opencv2/imgproc/imgproc.hpp”
#include “opencv2/highgui/highgui.hpp”
#include <iostream>
#include <string>
using namespace cv;
using namespace std;
void UseCanny(Mat img){
Mat grayscale(img.rows,img.cols,CV_8UC1);
//Initialize a grayscale image matrix
cvtColor(img,grayscale,CV_BGR2GRAY);
//Convert image to grayscale image using cvtColor function
int lowthresh = 20, highthresh = 100;
//Set low and high threshold values respectively
string win_name = “Edge-extracted image”;
namedWindow(win_name,CV_WINDOW_AUTOSIZE);
//Create a named window using the string defined above
createTrackbar(“Low Threshold”,win_name,&lowthresh,150);
//Create trackbar and set min value to lower threshold value
createTrackbar(“High Threshold”,win_name,&highthresh,255);
//Create trackbar and set max value to upper threshold value
while(1){
//Infinite loop
Mat result = grayscale.clone();
//Create copy of grayscale image & store into a new object ‘result’
Canny(result,result,lowthresh,highthresh,3);
/*Apply Canny Edge Detection Function using low and high threshold
values to result image and overwrite the result image*/
imshow(win_name,result);
//Show the image in the window defined by the string win_name
char ch = waitKey(33); if(ch==27) //If Esc key is pressed , breakout of the loop
break;
}
grayscale.release();
// Destroy grayscale matrix image
destroyAllWindows();
// Destroy any open windows
}
int main(){
string fname;
cout<<”Enter name of file:”;
cin>>fname;
Mat image = imread(fname);
UseCanny(image);
image.release();
return 0;
}

Understanding the code

Till the initialization of the variable frame, the code is similar to the one before. In this case, however, we need one more image variable other than the result, and that is an intermediate to store the grayscale equivalent of the frame. Both of these variables must, for the purpose of being used in pre-defined functions, be initialized with the cvCreateImage function, which requires the dimensions of the frame to be known. To avoid taking a separate frame for initialization outside the while loop, we are creating the two other image variables inside the loop itself, using them for edge detection, displaying them and releasing them from memory before repeating the operation.

The cvCanny function takes the first parameter as the grayscale image it has to operate upon; the second parameter is the destination image and the next two parameters are the lower and upper thresholds respectively. The final parameter is the size of the kernel for gradient computation, which is usually taken as 3. The resultant image is shown in the window and the action is repeated like before. A point to note is that in Canny edge detection, the edges are shown in white and the background in black.

Sobel Edge Detection

This is another widely used edge detection method that utilizes the Sobel operator, which calculates the approximate opposite of the gradient of the image intensity function. It also works on grayscale images as input. It uses something called convolution kernels of dimensions usually 3X3 for the purpose of getting the gradient at each point, in principle similar to the approach we used for our rudimentary method.
The gradients in the direction of each axis are used for determining whether the pixel is part of an edge or not.

Program
The program given below depicts the utilization of the Sobel Edge detection method

#include “stdafx.h”#include <stdio.h>#include <highgui.h>
#include <cv.h>
#include <cxcore.h>
void main(){
//Sobel edge detection for a video
CvCapture* capture = cvCreateFileCapture(“testIP_vid.avi”);
//The video is loaded into a pointer of type CvCapture
IplImage* frame;
IplImage*gscale_res;
IplImage* edgeframe;
//Declaration of structure variables to store frames
char *win = “video”;
char *winEdge = “edges”;
//Declaration of character pointers to store window names
cvNamedWindow(win,CV_WINDOW_AUTOSIZE);
//Window is created for original image
cvNamedWindow(winEdge,CV_WINDOW_AUTOSIZE);
//Window is created for resultant image
while(1) {
frame = cvQueryFrame(capture);
//Image under consideration is captured as a shot of the video
gscale_res = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_8U,1);
/*Creation of the image variable to store both the grayscale intermediate and final result*/
edgeframe = cvCreateImage(cvSize(frame->width,frame->height),IPL_DEPTH_16S,1);
//The 16-bit signed image for the result of the Sobel Edge detection
cvCvtColor(frame,gscale_res,CV_BGR2GRAY);
//Gray-scale intermediate created
cvSobel(gscale_res,edgeframe,1,0,3);
/* Sobel edge detection function is applied on the grayscale image and the result is put in the other variable*/
cvConvertScaleAbs(edgeframe,gscale_res,1,0);
/*The 16-bit signed image is converted to an 8-bit unsigned version so it can be displayed*/

cvShowImage(win,frame);
//Original image is shown
cvShowImage(winEdge,gscale_res);
//Resultant image with edges detected, is shown
char c = cvWaitKey(33);
//This is to account for the frame speed of the loaded video
cvReleaseImage(&gscale_res);
cvReleaseImage(&edgeframe);
if(c==27)
/*If the character of code 27 or the ESCAPE character is entered, the loop will break*/
break;
}
cvReleaseCapture(&capture);
//Video is released from memory
cvReleaseImage(&frame);
//Images released from memory
cvDestroyWindow(win);

cvDestroyWindow(winEdge);
//Windows closed
}

Understanding the code

Most of the program is largely similar to the Canny Edge detection one, so we will focus only on the differences. One of the major variations is that the Sobel edge detection function requires the destination image to have a larger depth than the source one. So we must make ‘edgeframe’ the 16-bit intermediate in this case to store the result of the Sobel operation. Moreover, a 16-bit signed image cannot be shown with the cvShowImage function properly. So we have to convert it back to 8-bit unsigned format. To save memory space and code lines, we use the variable for the grayscale intermediate to also store the final resultant image, thus calling it gscale_res.

The 16-bit signed image is converted to the 8-bit unsigned version through the cvConvertScaleAbs() function. We will not go into the details of this particular function as it will not be needed anywhere else. The first two parameters are the source and destination image, and the next two should be 1 and 0 respectively. After the images are generated, they are shown as in the Canny edge detection program.

For further details, visit our website www.robotix.in

--

--

Technology Robotix Society
COMPUTER VISION & ROBOTICS

The hub of all Robotics related activities at IIT Kharagpur and fast becoming the nerve center of Robotics in India.