一.基本思路
基于边缘检测的图像分割方法的基本思路是先确定图像中的边缘像素,然后再把这些像素连接在一起就构成所需的区域边界。
图像边缘:图像边缘,即表示图像中一个区域的终结和另一个区域的开始,图像中相邻区域之间的像素集合构成了图像的边缘。所以,图像边缘可以理解为图像灰度发生空间突变的像素的集合。图像边缘有两个要素,即:方向和幅度。沿着边缘走向的像素值变化比较平缓;而沿着垂直于边缘的走向,像素值则变化得比较大。因此,根据这一变化特点,通常会采用一阶和二阶导数来描述和检测边缘。
综上,图像中的边缘检测可以通过对灰度值求导数来确定,而导数可以通过微分算子计算来实现。在数字图像处理中,通常是利用差分计算来近似代替微分运算。
图1:图像边缘类型及导数曲线规律示例
梯度幅值计算:
设f(x,y)f(x,y)为连续图像函数,GxGx和GyGy分别为xx方向和yy方向的梯度,且在点(x,y)(x,y)处的梯度可以表示为一个矢量,梯度定义如下:
对应欧式距离梯度幅值: |G(x,y)|=G2x+G2y−−−−−−−√|G(x,y)|=Gx2+Gy2
对应棋盘距离梯度幅值: |G4(x,y)|=|Gx|+|Gy||G4(x,y)|=|Gx|+|Gy|
对应街区距离梯度幅值: |G8(x,y)|=max{|Gx|+|Gy|}|G8(x,y)|=max{|Gx|+|Gy|}
梯度矢量幅角表示的梯度方向是函数f(x,y)f(x,y)增加最快的方向: ϕ(x,y)=arctan(Gx/Gy)
二.Canny算子
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/highgui/highgui.hpp> 4 #include<iostream> 5 #include<stdlib.h> 6 using namespace cv; 7 using namespace std; 8 int main() 9 { 10 Mat src=imread("E:\\rice.jpg"); 11 Mat src1=src.clone();//复制 12 imshow("原始图",src); 13 Mat dst,edge,gray; 14 dst.create(src1.size(),src1.type());//创建同类型和大小的矩阵 15 cvtColor(src1,gray,COLOR_BGR2GRAY);//灰度化 16 blur(gray,edge,Size(3,3));//滤波去燥 17 Canny(edge,edge,3,9,3);//运用Canny算子 18 dst=Scalar::all(0);//目标图像所以元素置0 19 src1.copyTo(dst,edge);//使用Canny算子输出的边缘图edge作为掩码,来将原图src拷到目标图dst中来输出 20 imshow("效果图",dst); 21 waitKey(0); 22 return 0; 23 24 }
运行结果:
三.Sobel算子
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/highgui/highgui.hpp> 4 #include<iostream> 5 #include<stdlib.h> 6 using namespace cv; 7 using namespace std; 8 int main() 9 { 10 Mat grad_x,grad_y;//创建图像矩阵 11 Mat abs_grad_x,abs_grad_y,dst; 12 Mat src=imread("E:\\rice.jpg"); 13 imshow("原始图",src); 14 //求X方向的梯度与边缘检测 15 Sobel(src,grad_x,CV_16S,1,0,3,1,1,BORDER_DEFAULT);//Sobel的应用 16 convertScaleAbs(grad_x,abs_grad_x);//使用线性变换转换输入数组元素成8位无符号整型 17 imshow("x方向Sobel",abs_grad_x); 18 //求X方向的梯度与边缘检测 19 Sobel(src,grad_y,CV_16S,0,1,3,1,1,BORDER_DEFAULT);//Sobel的应用 20 convertScaleAbs(grad_y,abs_grad_y);//计算绝对值,并将结果转化成8位,使用线性变换转换输入数组元素成8位无符号整型 21 imshow("y方向Sobel",abs_grad_y); 22 //求全方向的梯度与边缘检测(合并梯度,近似) 23 addWeighted(abs_grad_x,0.5,abs_grad_y,0.5,0,dst); 24 imshow("整体方向Sobel",dst); 25 waitKey(0); 26 return 0; 27 28 }
运行结果:
四.Laplacian算子
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/highgui/highgui.hpp> 4 #include<iostream> 5 #include<stdlib.h> 6 using namespace cv; 7 using namespace std; 8 int main() 9 { 10 Mat src,src_gray,dst,abs_dst; 11 src=imread("E:\\rice.jpg"); 12 imshow("原始图",src); 13 GaussianBlur(src,src,Size(3,3),0,0,BORDER_DEFAULT);//高斯滤波去燥 14 cvtColor(src,src_gray,COLOR_BGR2GRAY);//灰度化 15 Laplacian(src_gray,dst,CV_16S,3,1,0,BORDER_DEFAULT);//使用Laplacian函数 16 convertScaleAbs(dst,abs_dst);//计算绝对值,并将结果转化成8位 17 imshow("Laplacian",abs_dst); 18 waitKey(0); 19 return 0; 20 21 }
运行结果:
五.综合实例
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/core.hpp> 3 #include <opencv2/highgui/highgui.hpp> 4 #include<iostream> 5 #include<stdlib.h> 6 using namespace cv; 7 using namespace std; 8 int g_sobelKernelSize=1; 9 int g_cannyLowThreshold=1; 10 Mat g_srcImage,g_srcGrayImage,g_dstImage; 11 void on_Canny(int,void*) 12 { 13 14 Mat g_cannyDetectedEdges; 15 blur(g_srcGrayImage,g_cannyDetectedEdges,Size(3,3)); 16 Canny(g_cannyDetectedEdges,g_cannyDetectedEdges,3,9,3); 17 g_dstImage=Scalar::all(0); 18 g_srcImage.copyTo(g_dstImage,g_cannyDetectedEdges); 19 imshow("Canny边缘检测",g_dstImage); 20 } 21 void on_Sobel(int,void*) 22 { 23 24 Mat g_sobelGradient_x,g_sobelGradient_y; 25 Mat g_sobelAbsGradient_x,g_sobelAbsGradient_y; 26 Sobel(g_srcImage,g_sobelGradient_x,CV_16S,1,0,(2*g_sobelKernelSize+1),1,1,BORDER_DEFAULT); 27 convertScaleAbs(g_sobelGradient_x,g_sobelAbsGradient_x); 28 Sobel(g_srcImage,g_sobelGradient_y,CV_16S,1,0,(2*g_sobelKernelSize+1),1,1,BORDER_DEFAULT); 29 convertScaleAbs(g_sobelGradient_y,g_sobelAbsGradient_y); 30 addWeighted(g_sobelAbsGradient_x,0.5,g_sobelAbsGradient_y,0.5,0,g_dstImage); 31 imshow("Sobel边缘检测",g_dstImage); 32 } 33 void Scharr() 34 { 35 Mat g_scharrGradient_x,g_scharrGradient_y; 36 Mat g_scharrAbsGradient_x,g_scharrAbsGradient_y; 37 Scharr(g_srcImage,g_scharrGradient_x,CV_16S,1,0,1,0,BORDER_DEFAULT); 38 convertScaleAbs(g_scharrGradient_x,g_scharrAbsGradient_x); 39 Scharr(g_srcImage,g_scharrGradient_y,CV_16S,1,0,1,0,BORDER_DEFAULT); 40 convertScaleAbs(g_scharrGradient_y,g_scharrAbsGradient_y); 41 addWeighted(g_scharrAbsGradient_x,0.5,g_scharrAbsGradient_y,0.5,0,g_dstImage); 42 imshow("Scharr边缘检测",g_dstImage); 43 } 44 45 int main(int argc,char**argv) 46 { 47 system("color 2F"); 48 g_srcImage=imread("E:\\rice.jpg"); 49 if(!g_srcImage.data) 50 { 51 printf("error!"); 52 return false; 53 } 54 namedWindow("原始图"); 55 imshow("原始图",g_srcImage); 56 g_dstImage.create(g_srcImage.size(),g_srcImage.type()); 57 cvtColor(g_srcImage,g_srcGrayImage,COLOR_BGR2GRAY); 58 namedWindow("Canny边缘检测",WINDOW_AUTOSIZE); 59 namedWindow("Sobel边缘检测",WINDOW_AUTOSIZE); 60 createTrackbar("参数值:","Sobel边缘检测",&g_sobelKernelSize,3,on_Sobel); 61 createTrackbar("参数值:","Canny边缘检测",&g_cannyLowThreshold,120,on_Canny); 62 on_Canny(0,0); 63 on_Sobel(0,0); 64 Scharr(); 65 while((char(waitKey(1))!='q')) 66 { 67 } 68 return 0; 69 70 }
运行结果: