一.基本思路
基於邊緣檢測的圖像分割方法的基本思路是先確定圖像中的邊緣像素,然后再把這些像素連接在一起就構成所需的區域邊界。
圖像邊緣:圖像邊緣,即表示圖像中一個區域的終結和另一個區域的開始,圖像中相鄰區域之間的像素集合構成了圖像的邊緣。所以,圖像邊緣可以理解為圖像灰度發生空間突變的像素的集合。圖像邊緣有兩個要素,即:方向和幅度。沿着邊緣走向的像素值變化比較平緩;而沿着垂直於邊緣的走向,像素值則變化得比較大。因此,根據這一變化特點,通常會采用一階和二階導數來描述和檢測邊緣。
綜上,圖像中的邊緣檢測可以通過對灰度值求導數來確定,而導數可以通過微分算子計算來實現。在數字圖像處理中,通常是利用差分計算來近似代替微分運算。

圖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 }
運行結果: