一、簡介
sobel算子主要是用於獲得數字圖像的一階梯度,常見的應用是邊緣檢測。
Ⅰ.水平變化: 將 I 與一個奇數大小的內核進行卷積。比如,當內核大小為3時,
的計算結果為:
Ⅱ.垂直變化: 將: I 與一個奇數大小的內核進行卷積。比如,當內核大小為3時,
的計算結果為:
Opencv中Sobel函數使用擴展的Sobel算子,來計算一階、二階、三階或混合圖像差分。
CV_EXPORTS_W 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 );
- 第一個參數,InputArray 類型的src,為輸入圖像,填Mat類型即可。
- 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。
- 第三個參數,int類型的ddepth,輸出圖像的深度。
- 第四個參數,int類型dx,x 方向上的差分階數。
- 第五個參數,int類型dy,y方向上的差分階數。
- 第六個參數,int類型ksize,有默認值3,表示Sobel核的大小;必須取1,3,5或7。
- 第七個參數,double類型的scale,計算導數值時可選的縮放因子,默認值是1,表示默認情況下是沒有應用縮放的。我們可以在文檔中查閱getDerivKernels的相關介紹,來得到這個參數的更多信息。
- 第八個參數,double類型的delta,表示在結果存入目標圖(第二個參數dst)之前可選的delta值,有默認值0。
- 第九個參數, int類型的borderType,我們的老朋友了(萬年是最后一個參數),邊界模式,默認值為BORDER_DEFAULT。這個參數可以在官方文檔中borderInterpolate處得到更詳細的信息。
具體的Sobel算子使用實例如下面代碼所示:
/// Generate grad_x and grad_y Mat grad_x, grad_y; Mat abs_grad_x, abs_grad_y; /// Gradient X //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT ); //Calculates the first, second, third, or mixed image derivatives using an extended Sobel operator. Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT ); convertScaleAbs( grad_x, abs_grad_x ); /// Gradient Y //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT ); Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT ); convertScaleAbs( grad_y, abs_grad_y ); /// Total Gradient (approximate) addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
二、Sobel算子定位
Sobel定位主要函數為 plateSobelLocate() ,主要處理函數有兩個,一個是 sobelFrtSearch() ,另一個是對定位區域進行偏斜扭轉的函數deskew(),deskew()后面會統一詳細講解,這邊我們主要看一下通過sobel算子定位的函數 sobelFrtSearch() 。
sobelFrtSearch()函數中通過 sobelOper() 進行sobel定位,主要步驟如下:
1、對圖像進行高斯濾波,為Sobel算子計算去除干擾噪聲;
2、圖像灰度化,提高運算速度;
3、對圖像進行Sobel運算,得到圖像的一階水平方向導數;
4、通過otsu進行閾值分割;
5、通過形態學閉操作,連接車牌區域。
此處通過Sobel算子進行車牌定位,僅僅做水平方向求導,而不做垂直方向求導。這樣做的意義是,如果做了垂直方向求導,會檢測出很多水平邊緣。水平邊緣多也許有利於生成更精確的輪廓,但是由於有些車子前端太多的水平邊緣了,例如車頭排氣孔,標志等等,很多的水平邊緣會誤導我們的連接結果,導致我們得不到一個恰好的車牌位置。
具體實現代碼如下所示:

1 int CPlateLocate::sobelOper(const Mat &in, Mat &out, int blurSize, int morphW,int morphH) { 2 Mat mat_blur; 3 mat_blur = in.clone(); 4 GaussianBlur(in, mat_blur, Size(blurSize, blurSize), 0, 0, BORDER_DEFAULT); 5 6 Mat mat_gray; 7 if (mat_blur.channels() == 3) 8 cvtColor(mat_blur, mat_gray, CV_RGB2GRAY); 9 else 10 mat_gray = mat_blur; 11 12 int scale = SOBEL_SCALE; 13 int delta = SOBEL_DELTA; 14 int ddepth = SOBEL_DDEPTH; 15 16 Mat grad_x, grad_y; 17 Mat abs_grad_x, abs_grad_y; 18 19 20 Sobel(mat_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT); 21 convertScaleAbs(grad_x, abs_grad_x); 22 23 Mat grad; 24 addWeighted(abs_grad_x, SOBEL_X_WEIGHT, 0, 0, 0, grad); 25 26 Mat mat_threshold; 27 double otsu_thresh_val = 28 threshold(grad, mat_threshold, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY); 29 30 31 Mat element = getStructuringElement(MORPH_RECT, Size(morphW, morphH)); 32 morphologyEx(mat_threshold, mat_threshold, MORPH_CLOSE, element); 33 34 out = mat_threshold; 35 36 return 0; 37 }
上述圖像經過處理之后,可以直接對圖像輪廓進行搜索,輪廓搜索將全圖的輪廓都搜索出來了,需要進行篩選,對輪廓求最小外接矩形,並在 verifySizes() 中進行驗證,不滿足條件的刪除。 具體實現代碼如下所示:

1 int CPlateLocate::sobelFrtSearch(const Mat &src, 2 vector<Rect_<float>> &outRects) { 3 Mat src_threshold; 4 5 sobelOper(src, src_threshold, m_GaussianBlurSize, m_MorphSizeWidth, 6 m_MorphSizeHeight); 7 8 vector<vector<Point>> contours; 9 findContours(src_threshold, 10 contours, // a vector of contours 11 CV_RETR_EXTERNAL, 12 CV_CHAIN_APPROX_NONE); // all pixels of each contours 13 14 vector<vector<Point>>::iterator itc = contours.begin(); 15 16 vector<RotatedRect> first_rects; 17 18 while (itc != contours.end()) { 19 RotatedRect mr = minAreaRect(Mat(*itc)); 20 21 22 if (verifySizes(mr)) { 23 first_rects.push_back(mr); 24 25 float area = mr.size.height * mr.size.width; 26 float r = (float) mr.size.width / (float) mr.size.height; 27 if (r < 1) r = (float) mr.size.height / (float) mr.size.width; 28 } 29 30 ++itc; 31 } 32 33 for (size_t i = 0; i < first_rects.size(); i++) { 34 RotatedRect roi_rect = first_rects[i]; 35 36 Rect_<float> safeBoundRect; 37 if (!calcSafeRect(roi_rect, src, safeBoundRect)) continue; 38 39 outRects.push_back(safeBoundRect); 40 } 41 return 0; 42 }
經過上述步驟后,為了進一步提高搜索的准確性,EasyPR里面對第一次搜索出的矩形擴大一定范圍后,進行了二次搜素,具體函數為 sobelSecSearchPart() 。sobelSecSearchPart() 函數和 sobelFrtSearch() 大致過程是類似的,此處不再詳細敘述,唯一的不同是sobelSecSearchPart() 對車牌上鉚釘的去除進行了對應的處理。之后對定位區域進行偏斜扭轉 deskew()處理之后,即可得到車牌定位的結果。