OpenCV学习笔记(十二):边缘检测:Canny(),Sobel(),Laplace(),Scharr滤波器
OpenCV學(xué)習(xí)筆記(十二):邊緣檢測:Canny(),Sobel(),Laplace(),Scharr濾波器
1)濾波:邊緣檢測的算法主要是基于圖像強度的一階和二階導(dǎo)數(shù),但導(dǎo)數(shù)通常對噪聲很敏感,因此必須采用濾波器來改善與噪聲有關(guān)的邊緣檢測器的性能。常見的濾波方法主要有高斯濾波,即采用離散化的高斯函數(shù)產(chǎn)生一組歸一化的高斯核,然后基于高斯核函數(shù)對圖像灰度矩陣的每一點進(jìn)行加權(quán)求和。
2)增強:增強邊緣的基礎(chǔ)是確定圖像各點鄰域強度的變化值。增強算法可以將圖像灰度點鄰域強度值有顯著變化的點凸顯出來。在具體編程實現(xiàn)時,可通過計算梯度幅值來確定。
3)檢測:經(jīng)過增強的圖像,往往鄰域中有很多點的梯度值比較大,而在特定的應(yīng)用中,這些點并不是我們要找的邊緣點,所以應(yīng)該采用某種方法來對這些點進(jìn)行取舍。實際工程中,常用的方法是通過閾值化方法來檢測。
1、Canny()算子:
Canny邊緣檢測算子是一個多級邊緣檢測算法
C++: void Canny(InputArray image, //輸入圖像,即源圖像 OutputArray edges, // 輸出的邊緣圖 double threshold1, // 第一個滯后性閾值 double threshold2, // 第二個滯后性閾值// 這個函數(shù)閾值1和閾值2兩者的小者用于邊緣連接,而大者用來控制強邊緣的初始段,推薦的高低閾值比在2:1到3:1之間。 int apertureSize=3, // 表示應(yīng)用Sobel算子的孔徑大小 bool L2gradient=false // 一個計算圖像梯度幅值的標(biāo)識 )2、Sobel()算子:
Sobel 算子是一個主要用作邊緣檢測的離散微分算子 (discrete differentiation operator)。它Sobel算子結(jié)合了高斯平滑和微分求導(dǎo),用來計算圖像灰度函數(shù)的近似梯度。在圖像的任何一點使用此算子,將會產(chǎn)生對應(yīng)的梯度矢量或是其法矢量。
void Sobel ( InputArray src, // 輸入圖 OutputArray dst, // 輸出圖 int 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// 若src.depth() = CV_64F, 取ddepth = -1/CV_64F int dx, // x 方向上的差分階數(shù) int dy, // y 方向上的差分階數(shù) int ksize=3, // 表示Sobel核的大小;必須取1,3,5或7 double scale=1, // 計算導(dǎo)數(shù)值時可選的縮放因子 double delta=0, // 表示在結(jié)果存入目標(biāo)圖之前可選的delta值 int borderType=BORDER_DEFAULT // 邊界模式 );3、Laplace()算子:
Laplacian 算子是n維歐幾里德空間中的一個二階微分算子,定義為梯度grad()的散度div()。Laplacian( )函數(shù)其實主要是利用sobel算子的運算。它通過加上sobel算子運算出的圖像x方向和y方向上的導(dǎo)數(shù),來得到我們載入圖像的拉普拉斯變換結(jié)果。
void Laplacian(InputArray src, // 源圖像 OutputArray dst, // 輸出的邊緣圖 int ddepth, // 輸出圖像的深度 int ksize=1, // 用于計算二階導(dǎo)數(shù)的濾波器的孔徑尺寸 double scale=1, // 計算拉普拉斯值的時候可選的比例因子 double delta=0, // 表示在結(jié)果存入目標(biāo)圖之前可選的delta值 intborderType=BORDER_DEFAULT // 邊界模式 );4、scharr濾波器:
scharr一般我就直接稱它為濾波器,而不是算子,主要是配合Sobel算子的運算而存在的。
scharr算子與Sobel的不同點是在平滑部分,這里所用的平滑算子是1/16?[3,10,3],相比于1/4?[1,2,1],中心元素占的權(quán)重更重,這可能是相對于圖像這種隨機性較強的信號,鄰域相關(guān)性不大,所以鄰域平滑應(yīng)該使用相對較小的標(biāo)準(zhǔn)差的高斯函數(shù),也就是更瘦高的模板。
4、代碼示例:
#include <opencv2/opencv.hpp>using namespace cv; using namespace std;//原圖,原圖的灰度版,目標(biāo)圖 Mat g_srcImage, g_srcGrayImage,g_dstImage;//Canny邊緣檢測相關(guān)變量 Mat g_cannyDetectedEdges; int g_cannyLowThreshold=1;//TrackBar位置參數(shù) //Sobel邊緣檢測相關(guān)變量 Mat g_sobelGradient_X, g_sobelGradient_Y; Mat g_sobelAbsGradient_X, g_sobelAbsGradient_Y; int g_sobelKernelSize=1;//TrackBar位置參數(shù) //Scharr濾波器相關(guān)變量 Mat g_scharrGradient_X, g_scharrGradient_Y; Mat g_scharrAbsGradient_X, g_scharrAbsGradient_Y;int main() {//載入原圖g_srcImage = imread("F:/C++/2. OPENCV 3.1.0/7.1 edge detection 邊緣檢測/5.jpg");if( !g_srcImage.data ) { printf("Oh,no,讀取srcImage錯誤~! \n"); return false; }//顯示原始圖namedWindow("【原始圖】");imshow("【原始圖】", g_srcImage);// 1、創(chuàng)建與src同類型和大小的矩陣(dst)g_dstImage.create( g_srcImage.size(), g_srcImage.type() );// 2、將原圖像轉(zhuǎn)換為灰度圖像cvtColor( g_srcImage, g_srcGrayImage, COLOR_BGR2GRAY );// 3、創(chuàng)建trackbarnamedWindow( "【效果圖】Canny邊緣檢測", WINDOW_AUTOSIZE );namedWindow( "【效果圖】Sobel邊緣檢測", WINDOW_AUTOSIZE );createTrackbar( "參數(shù)值:", "【效果圖】Canny邊緣檢測", &g_cannyLowThreshold, 120, on_Canny);createTrackbar( "參數(shù)值:", "【效果圖】Sobel邊緣檢測", &g_sobelKernelSize, 3, on_Sobel);// 4、調(diào)用回調(diào)函數(shù)on_Canny(0, 0);on_Sobel(0, 0);// 5、調(diào)用封裝了Scharr邊緣檢測代碼的函數(shù)Scharr( );//輪詢獲取按鍵信息,若按下Q,程序退出while((char(waitKey(1)) != 'q')) {} }1)Canny邊緣檢測窗口滾動條的回調(diào)函數(shù)
void on_Canny(int, void*) {// 先使用 3x3內(nèi)核來降噪blur( g_srcGrayImage, g_cannyDetectedEdges, Size(3,3) );// 運行我們的Canny算子// threshold1:滯后性閾值1(較小值用于邊緣連接,較大值控制強邊緣的初始段)// threshold2:滯后性閾值2// apertureSize sobel算子的孔徑大小Canny( g_cannyDetectedEdges, g_cannyDetectedEdges, g_cannyLowThreshold, g_cannyLowThreshold*3, 3 );//先將g_dstImage內(nèi)的所有元素設(shè)置為0g_dstImage = Scalar::all(0);//使用Canny算子輸出的邊緣圖g_cannyDetectedEdges作為掩碼,來將原圖g_srcImage拷到目標(biāo)圖g_dstImage中g_srcImage.copyTo( g_dstImage, g_cannyDetectedEdges);//顯示效果圖imshow( "【效果圖】Canny邊緣檢測", g_dstImage ); }2)Sobel邊緣檢測窗口滾動條的回調(diào)函數(shù)
void on_Sobel(int, void*) {//Sobel算子是一階導(dǎo)數(shù)的邊緣檢測算子,在算法實現(xiàn)過程中,通過3×3模板作為核與圖像中的每個像素點做卷積和運算,然后選取合適的閾值以提取邊緣。//Sobel算子算法的優(yōu)點是計算簡單,速度快。但是由于只采用了2個方向的模板,只能檢測水平和垂直方向的邊緣,//因此這種算法對于紋理較為復(fù)雜的圖像,其邊緣檢測效果就不是很理想。該算法認(rèn)為://凡灰度新值大于或等于閾值的像素點時都是邊緣點。//這種判斷欠合理,會造成邊緣點的誤判,因為許多噪聲點的灰度值也很大。// ddepth:輸出圖像的深度// dx,dy: x y方向上的差分階數(shù)// scale :計算導(dǎo)數(shù)時的縮放因子// 求 X方向梯度Sobel( g_srcImage, g_sobelGradient_X, CV_16S, 1, 0, (2*g_sobelKernelSize+1), 1, 1, BORDER_DEFAULT );// 對于輸入數(shù)組的每個元素,convertScaleAbs函數(shù)依次執(zhí)行三個操作:縮放、取絕對值、轉(zhuǎn)換為無符號8位類型 :// dst(I)=saturate\_cast<uchar>(|src(I)?alpha+beta|)convertScaleAbs( g_sobelGradient_X, g_sobelAbsGradient_X ); // Mat grad_xROI(grad_x,Rect(0,0,20,20)); // cout<<"M="<<endl<<grad_xROI<<endl; // imshow("【效果圖】 X方向Sobel1", grad_x);// 求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 );// 合并梯度addWeighted( g_sobelAbsGradient_X, 0.5, g_sobelAbsGradient_Y, 0.5, 0, g_dstImage );//顯示效果圖imshow("【效果圖】Sobel邊緣檢測", g_dstImage);}3)Scharr邊緣檢測相關(guān)代碼的函數(shù)
void Scharr( ) {// scharr算子與Sobel的不同點是在平滑部分,這里所用的平滑算子是1/16?[3,10,3],// 相比于1/4?[1,2,1],中心元素占的權(quán)重更重,這可能是相對于圖像這種隨機性較強的信號,// 鄰域相關(guān)性不大,所以鄰域平滑應(yīng)該使用相對較小的標(biāo)準(zhǔn)差的高斯函數(shù),也就是更瘦高的模板。// 求 X方向梯度Scharr( g_srcImage, g_scharrGradient_X, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT );convertScaleAbs( g_scharrGradient_X, g_scharrAbsGradient_X );//計算絕對值,并將結(jié)果轉(zhuǎn)換成8位// 求Y方向梯度Scharr( g_srcImage, g_scharrGradient_Y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT );convertScaleAbs( g_scharrGradient_Y, g_scharrAbsGradient_Y );//計算絕對值,并將結(jié)果轉(zhuǎn)換成8位// 合并梯度addWeighted( g_scharrAbsGradient_X, 0.5, g_scharrAbsGradient_Y, 0.5, 0, g_dstImage );//顯示效果圖imshow("【效果圖】Scharr濾波器", g_dstImage); }結(jié)果:
總結(jié)
以上是生活随笔為你收集整理的OpenCV学习笔记(十二):边缘检测:Canny(),Sobel(),Laplace(),Scharr滤波器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python判断变量类型
- 下一篇: 图像识别:利用KNN实现手写数字识别(m