一、邊緣檢測概念
圖像的邊緣檢測的原理是檢測出圖像中所有灰度值變化較大的點,而且這些點連接起來就構成了若干線條,這些線條就可以稱為圖像的邊緣。效果如圖:

接下來介紹一下邊緣提取的幾種算子,具體證明過程可能會比較簡單,重點在函數的使用上。
二、算法實現:
1.索貝爾算子
索貝爾算子(Sobel operator)計算。
- C++: void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0, intborderType=BORDER_DEFAULT )
- 參數說明:
- src – 輸入圖像。
- dst – 輸出圖像,與輸入圖像同樣大小,擁有同樣個數的通道。
- 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
當 ddepth為-1時, 輸出圖像將和輸入圖像有相同的深度。輸入8位圖像則會截取頂端的導數。
- xorder – x方向導數運算參數。
- yorder – y方向導數運算參數。
- ksize – Sobel內核的大小,可以是:1,3,5,7。
- scale – 可選的縮放導數的比例常數。
- delta – 可選的增量常數被疊加到導數中。
- borderType – 用於判斷圖像邊界的模式。
代碼注釋:
在邊沿檢測中,常用的一種模板是Sobel 算子。Sobel 算子有兩個,一個是檢測水平邊沿的;另一個是檢測垂直平邊沿的 。 由於Sobel算子是濾波算子的形式,用於提取邊緣,可以利用快速卷積函數,簡單有效,因此應用廣泛。美中不足的是,Sobel算子並沒有將圖像的主體與背景嚴格地區分開來,換言之就是Sobel算子沒有基於圖像灰度進行處理,由於Sobel算子沒有嚴格地模擬人的視覺生理特征,所以提取的圖像輪廓有時並不能令人滿意。在觀測一幅圖像的時候,我們往往首先注意的是圖像與背景不同的部分,正是這個部分將主體突出顯示,基於該理論,我們可以給出閾值化輪廓提取算法,該算法已在數學上證明當像素點滿足正態分布時所求解是最優的。
//在x方向求圖像近似導數
Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
//在y方向求圖像近似導數
Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
2.Canny算子
函數原型:
void cvCanny(const CvArr* image, CvArr* edges,double threshold1,double threshold2,int aperture_size=3);
函數說明:
第一個參數表示輸入圖像,必須為單通道灰度圖。
第二個參數表示輸出的邊緣圖像,為單通道黑白圖。
第三個參數和第四個參數表示閾值,這二個閾值中當中的小閾值用來控制邊緣連接,大的閾值用來控制強邊緣的初始分割即如果一個像素的梯度大與上限值,則被認為是邊緣像素,如果小於下限閾值,則被拋棄。如果該點的梯度在兩者之間則當這個點與高於上限值的像素點連接時我們才保留,否則刪除。
第五個參數表示Sobel 算子大小,默認為3即表示一個3*3的矩陣。詳細的數學原理可以查閱專業書籍。
代碼注釋:
Canny算子實現起來較為麻煩,Canny算子是一個具有濾波,增強,檢測的多階段的優化算子,在進行處理前,Canny算子先利用高斯平滑濾波器來平滑圖像以除去噪聲,Canny分割算法采用一階偏導的有限差分來計算梯度幅值和方向,在處理過程中,Canny算子還將經過一個非極大值抑制的過程,最后Canny算子還采用兩個閾值來連接邊緣。
再強調一下Canny算子的兩個參數:第一個是低閾值,第二個高閾值。高閾值比較嚴格,求的邊緣很少,認為高閾值的邊緣都是有效。低閾值寬松,求的邊緣很多(一般包括了高閾值求到的邊緣),其中不少是無效的邊緣(反正不想要的)。先用高閾值求邊緣。canny求得的邊緣希望是連在一起的(通常是封閉的),但高閾值求的邊緣一般斷斷續續。斷開的地方如果低閾值求的邊緣存在,就用低閾值的邊緣接上去,目的讓邊緣盡量都連在一起。其它情況下低閾值的邊緣是不用的。兩個閾值是有區別的,高的那個閾值是將要提取輪廓的物體與背景區分開來,就像閾值分割的那個參數一樣,是決定目標與背景對比度的,低的那個閾值是用來平滑邊緣的輪廓,有時高的閾值設置太大了,可能邊緣輪廓不連續或者不夠平滑,通過低閾值來平滑輪廓線,或者使不連續的部分連接起來
Canny(src_gray, grad_y, 30,80);
三、代碼實現
#include <opencv.hpp>
using namespace cv;
int main()
{
Mat srcImage;
srcImage = imread("d://1.png");
imshow("【原圖】", srcImage);
Mat dst=srcImage; // 構造目標類
//Sobel(srcImage, dst, 0, 1, 0);
Canny(srcImage, dst,30,80);
imshow("【處理后】", dst);
waitKey();
return 0;
}
效果圖:
Canny算子處理:

Sobel處理:

