OpenCV Learning: Image Edge Detection

1. Detailed explanation of Canny function in OpenCV

The Canny function uses the Canny algorithm to detect the edge of the image.

  1. void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )  
  • The first parameter, the image of the InputArray type, the input image, that is, the source image, can be filled with an object of the Mat class, and it needs to be a single-channel 8-bit image.
  • The second parameter, the edges of the OutputArray type, the output edge map, needs to have the same size and type as the source image.
  • The third parameter, the threshold1 of double type, the first hysteresis threshold.
  • The fourth parameter, the double type of threshold2, the second hysteresis threshold.
  • The fifth parameter, apertureSize of type int, represents the aperture size of the Sobel operator, which has a default value of 3.
  • The sixth parameter, L2gradient of type bool, is an identifier for calculating the gradient magnitude of the image, with a default value of false.

    It should be noted that the smaller of the function threshold 1 and threshold 2 is used for edge connection, and the larger one is used to control the initial segment of strong edges. The recommended ratio of high and low thresholds is between 2:1 and 3:1

dem:1:

demo2:

//-----------------------------------【头文件包含部分】---------------------------------------  
//            描述:包含程序所依赖的头文件  
//----------------------------------------------------------------------------------------------  
#include <opencv2/opencv.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  
  
//-----------------------------------【命名空间声明部分】---------------------------------------  
//            描述:包含程序所使用的命名空间  
//-----------------------------------------------------------------------------------------------  
using namespace cv;  
//-----------------------------------【main( )函数】--------------------------------------------  
//            描述:控制台应用程序的入口函数,我们的程序从这里开始  
//-----------------------------------------------------------------------------------------------  
int main( )  
{  
    //载入原始图    
    Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
    Mat src1=src.clone();  
  
    //显示原始图   
    imshow("【原始图】Canny边缘检测", src);   
  
    //----------------------------------------------------------------------------------  
    //  一、最简单的canny用法,拿到原图后直接用。  
    //----------------------------------------------------------------------------------  
    Canny( src, src, 150, 100,3 );  
    imshow("【效果图】Canny边缘检测", src);   
  
      
    //----------------------------------------------------------------------------------  
    //  二、高阶的canny用法,转成灰度图,降噪,用canny,最后将得到的边缘作为掩码,拷贝原图到效果图上,得到彩色的边缘图  
    //----------------------------------------------------------------------------------  
    Mat dst,edge,gray;  
  
    // 【1】创建与src同类型和大小的矩阵(dst)  
    dst.create( src1.size(), src1.type() );  
  
    // 【2】将原图像转换为灰度图像  
    cvtColor( src1, gray, CV_BGR2GRAY );  
  
    // 【3】先用使用 3x3内核来降噪  
    blur( gray, edge, Size(3,3) );  
  
    // 【4】运行Canny算子  
    Canny( edge, edge, 3, 9,3 );  
  
    //【5】将g_dstImage内的所有元素设置为0   
    dst = Scalar::all(0);  
  
    //【6】使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中  
    src1.copyTo( dst, edge);  
  
    //【7】显示效果图   
    imshow("【效果图】Canny边缘检测2", dst);   
  
  
    waitKey(0);   
  
    return 0;   
} 

 

2. Explanation of related theories and concepts of sobel operator

The Sobel function uses the extended Sobel operator to compute first-, second-, third-order, or mixed image differences

C++: void Sobel (  
InputArray src,//输入图  
 OutputArray dst,//输出图  
 int ddepth,//输出图像的深度  
 int dx,  
 int dy,  
 int ksize=3,  
 double scale=1,  
 double delta=0,  
 int borderType=BORDER_DEFAULT );
  • The first parameter, src of InputArray type, is the input image, just fill in the Mat type.
  • The second parameter, the dst of the OutputArray type, that is, the target image, the output parameter of the function, needs to have the same size and type as the source image.
  • The third parameter, ddepth of int type, the depth of the output image, supports the following combinations of src.depth() and ddepth:
    • 若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
    • 若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
    • 若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
    • If src.depth() = CV_64F, take ddepth = -1/CV_64F
  • The fourth parameter, int type dx, is the difference order in the x direction.
  • The fifth parameter, int type dy, the difference order in the y direction.
  • The sixth parameter, int type ksize, has a default value of 3, indicating the size of the Sobel core; it must be 1, 3, 5 or 7.
  • The seventh parameter, the scale of the double type, is an optional scaling factor when calculating the derivative value. The default value is 1, which means that no scaling is applied by default. We can refer to the related introduction of getDerivKernels in the documentation to get more information about this parameter.
  • The eighth parameter, delta of type double, represents the optional delta value before the result is stored in the target image (the second parameter dst), with a default value of 0.
  • The ninth parameter, the borderType of the int type, is our old friend (Wannian is the last parameter), the border mode, and the default value is BORDER_DEFAULT. This parameter can get more detailed information at borderInterpolate in the official documentation.

 

 

In general, the ksize x ksize kernel is used to calculate the derivative. However, there is a special case - when ksize is 1, 3 x 1 or 1 x 3 kernels tend to be used. And in this case, no Gaussian smoothing operation is performed.

demo1:

The example code for calling the Sobel function is as follows. This is just to teach you how to use the Sobel function, instead of using a cvtColor to convert the original image; into a grayscale image, but directly use the color image to operate.

//-----------------------------------【头文件包含部分】---------------------------------------  
//            描述:包含程序所依赖的头文件  
//----------------------------------------------------------------------------------------------  
#include <opencv2/opencv.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  
  
//-----------------------------------【命名空间声明部分】---------------------------------------  
//            描述:包含程序所使用的命名空间  
//-----------------------------------------------------------------------------------------------  
using namespace cv;  
//-----------------------------------【main( )函数】--------------------------------------------  
//            描述:控制台应用程序的入口函数,我们的程序从这里开始  
//-----------------------------------------------------------------------------------------------  
int main( )  
{  
    //【0】创建 grad_x 和 grad_y 矩阵  
    Mat grad_x, grad_y;  
    Mat abs_grad_x, abs_grad_y,dst;  
  
    //【1】载入原始图    
    Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
  
    //【2】显示原始图   
    imshow("【原始图】sobel边缘检测", src);   
  
    //【3】求 X方向梯度  
    Sobel( src, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_x, abs_grad_x );  
    imshow("【效果图】 X方向Sobel", abs_grad_x);   
  
    //【4】求Y方向梯度  
    Sobel( src, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( grad_y, abs_grad_y );  
    imshow("【效果图】Y方向Sobel", abs_grad_y);   
  
    //【5】合并梯度(近似)  
    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );  
    imshow("【效果图】整体方向Sobel", dst);   
  
    waitKey(0);   
    return 0;   
}  

 

3. Theories and concepts related to Laplace operator

C++: void Laplacian(InputArray src,OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT );  
  • The first parameter, the image of the InputArray type, the input image, that is, the source image, can be filled with an object of the Mat class, and it needs to be a single-channel 8-bit image.
  • The second parameter, edges of type OutputArray, the output edge map, needs to have the same size and number of channels as the source image.
  • The third parameter, ddept of type int, is the depth of the target image.
  • The fourth parameter, ksize of type int, is the aperture size of the filter used to calculate the second derivative. The size must be a positive odd number and has a default value of 1.
  • The fifth parameter, the scale of the double type, is an optional scale factor when calculating the Laplace value, with a default value of 1.
  • The sixth parameter, delta of type double, represents the optional delta value before the result is stored in the target image (the second parameter dst), with a default value of 0.
  • The seventh parameter, borderType of int type, border mode, the default value is BORDER_DEFAULT. This parameter can be more detailed information at borderInterpolate() in the official documentation.

The Laplacian( ) function actually mainly uses the operation of the sobel operator. It obtains the Laplace transform result of the image we loaded by adding the derivatives in the x-direction and y-direction of the image calculated by the sobel operator.

demo1:

//-----------------------------------【头文件包含部分】---------------------------------------  
//            描述:包含程序所依赖的头文件  
//----------------------------------------------------------------------------------------------  
#include <opencv2/opencv.hpp>  
#include<opencv2/highgui/highgui.hpp>  
#include<opencv2/imgproc/imgproc.hpp>  
  
//-----------------------------------【命名空间声明部分】---------------------------------------  
//            描述:包含程序所使用的命名空间  
//-----------------------------------------------------------------------------------------------  
using namespace cv;  
  
  
//-----------------------------------【main( )函数】--------------------------------------------  
//            描述:控制台应用程序的入口函数,我们的程序从这里开始  
//-----------------------------------------------------------------------------------------------  
int main( )  
{  
    //【0】变量的定义  
    Mat src,src_gray,dst, abs_dst;  
  
    //【1】载入原始图    
    src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图  
  
    //【2】显示原始图   
    imshow("【原始图】图像Laplace变换", src);   
  
    //【3】使用高斯滤波消除噪声  
    GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );  
  
    //【4】转换为灰度图  
    cvtColor( src, src_gray, CV_RGB2GRAY );  
  
    //【5】使用Laplace函数  
    Laplacian( src_gray, dst, CV_16S, 3, 1, 0, BORDER_DEFAULT );  
  
    //【6】计算绝对值,并将结果转换成8位  
    convertScaleAbs( dst, abs_dst );  
  
    //【7】显示效果图  
    imshow( "【效果图】图像Laplace变换", abs_dst );  
  
    waitKey(0);   
  
    return 0;   
}  

 

 

4. Comprehensive code summary

//-----------------------------------【程序说明】----------------------------------------------  
//      程序名称::《【OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑合辑》 博文配套源码   
//      开发所用IDE版本:Visual Studio 2010  
//      <span style="white-space:pre">  </span>开发所用OpenCV版本:    2.4.9  
//      2014年5月11日 Create by 浅墨  
//      浅墨的微博:@浅墨_毛星云 http://weibo.com/1723155442/profile?topnav=1&wvr=5&user=1  
//      浅墨的知乎:http://www.zhihu.com/people/mao-xing-yun  
//      浅墨的豆瓣:http://www.douban.com/people/53426472/  
//----------------------------------------------------------------------------------------------  
  
  
  
//-----------------------------------【头文件包含部分】---------------------------------------  
//      描述:包含程序所依赖的头文件  
//----------------------------------------------------------------------------------------------   
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
  
//-----------------------------------【命名空间声明部分】--------------------------------------  
//      描述:包含程序所使用的命名空间  
//-----------------------------------------------------------------------------------------------   
using namespace cv;  
  
  
//-----------------------------------【全局变量声明部分】--------------------------------------  
//      描述:全局变量声明  
//-----------------------------------------------------------------------------------------------  
//原图,原图的灰度版,目标图  
Mat g_srcImage, g_srcGrayImage,g_dstImage;  
  
//Canny边缘检测相关变量  
Mat g_cannyDetectedEdges;  
int g_cannyLowThreshold=1;//TrackBar位置参数    
  
//Sobel边缘检测相关变量  
Mat g_sobelGradient_X, g_sobelGradient_Y;  
Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y;  
int g_sobelKernelSize=1;//TrackBar位置参数    
  
//Scharr滤波器相关变量  
Mat g_scharrGradient_X, g_scharrGradient_Y;  
Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;  
  
  
//-----------------------------------【全局函数声明部分】--------------------------------------  
//      描述:全局函数声明  
//-----------------------------------------------------------------------------------------------  
static void ShowHelpText( );  
static void on_Canny(int, void*);//Canny边缘检测窗口滚动条的回调函数  
static void on_Sobel(int, void*);//Sobel边缘检测窗口滚动条的回调函数  
void Scharr( );//封装了Scharr边缘检测相关代码的函数  
  
  
//-----------------------------------【main( )函数】--------------------------------------------  
//      描述:控制台应用程序的入口函数,我们的程序从这里开始  
//-----------------------------------------------------------------------------------------------  
int main( int argc, char** argv )  
{  
    //改变console字体颜色  
    system("color 2F");    
  
    //显示欢迎语  
    ShowHelpText();  
  
    //载入原图  
    g_srcImage = imread("1.jpg");  
    if( !g_srcImage.data ) { printf("Oh,no,读取srcImage错误~! \n"); return false; }  
  
    //显示原始图  
    namedWindow("【原始图】");  
    imshow("【原始图】", g_srcImage);  
  
    // 创建与src同类型和大小的矩阵(dst)  
    g_dstImage.create( g_srcImage.size(), g_srcImage.type() );  
  
    // 将原图像转换为灰度图像  
    cvtColor( g_srcImage, g_srcGrayImage, CV_BGR2GRAY );  
  
    // 创建显示窗口  
    namedWindow( "【效果图】Canny边缘检测", CV_WINDOW_AUTOSIZE );  
    namedWindow( "【效果图】Sobel边缘检测", CV_WINDOW_AUTOSIZE );  
  
    // 创建trackbar  
    createTrackbar( "参数值:", "【效果图】Canny边缘检测", &g_cannyLowThreshold, 120, on_Canny );  
    createTrackbar( "参数值:", "【效果图】Sobel边缘检测", &g_sobelKernelSize, 3, on_Sobel );  
  
    // 调用回调函数  
    on_Canny(0, 0);  
    on_Sobel(0, 0);  
  
    //调用封装了Scharr边缘检测代码的函数  
    Scharr( );  
  
    //轮询获取按键信息,若按下Q,程序退出  
    while((char(waitKey(1)) != 'q')) {}  
  
    return 0;  
}  
  
  
//-----------------------------------【ShowHelpText( )函数】----------------------------------  
//      描述:输出一些帮助信息  
//----------------------------------------------------------------------------------------------  
static void ShowHelpText()  
{  
    //输出一些帮助信息  
    printf( "\n\n\t嗯。运行成功,请调整滚动条观察图像效果~\n\n"  
        "\t按下“q”键时,程序退出~!\n"  
        "\n\n\t\t\t\t by浅墨" );  
}  
  
  
//-----------------------------------【on_Canny( )函数】----------------------------------  
//      描述:Canny边缘检测窗口滚动条的回调函数  
//-----------------------------------------------------------------------------------------------  
void on_Canny(int, void*)  
{  
    // 先使用 3x3内核来降噪  
    blur( g_srcGrayImage, g_cannyDetectedEdges, Size(3,3) );  
  
    // 运行我们的Canny算子  
    Canny( g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3 );  
  
    //先将g_dstImage内的所有元素设置为0   
    g_dstImage = Scalar::all(0);  
  
    //使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图g_srcImage拷到目标图g_dstImage中  
    g_srcImage.copyTo( g_dstImage, g_cannyDetectedEdges);  
  
    //显示效果图  
    imshow( "【效果图】Canny边缘检测", g_dstImage );  
}  
  
  
  
//-----------------------------------【on_Sobel( )函数】----------------------------------  
//      描述:Sobel边缘检测窗口滚动条的回调函数  
//-----------------------------------------------------------------------------------------  
void on_Sobel(int, void*)  
{  
    // 求 X方向梯度  
    Sobel( g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( g_sobelGradient_X, g_sobelAbsGradient_X );//计算绝对值,并将结果转换成8位  
  
    // 求Y方向梯度  
    Sobel( g_srcImage, g_sobelGradient_Y, CV_16S, 0, 1, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );  
    convertScaleAbs( g_sobelGradient_Y, g_sobelAbsGradient_Y );//计算绝对值,并将结果转换成8位  
  
    // 合并梯度  
    addWeighted( g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage );  
  
    //显示效果图  
    imshow("【效果图】Sobel边缘检测", g_dstImage);   
  
}  
  
  
//-----------------------------------【Scharr( )函数】----------------------------------  
//      描述:封装了Scharr边缘检测相关代码的函数  
//-----------------------------------------------------------------------------------------  
void Scharr( )  
{  
    // 求 X方向梯度  
    Scharr( g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );  
    convertScaleAbs( g_scharrGradient_X, g_scharrAbsGradient_X );//计算绝对值,并将结果转换成8位  
  
    // 求Y方向梯度  
    Scharr( g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );  
    convertScaleAbs( g_scharrGradient_Y, g_scharrAbsGradient_Y );//计算绝对值,并将结果转换成8位  
  
    // 合并梯度  
    addWeighted( g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage );  
  
    //显示效果图  
    imshow("【效果图】Scharr滤波器", g_dstImage);   
}  

 

Reference: https://blog.csdn.net/poem_qianmo/article/details/25560901

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325160593&siteId=291194637