1. 程式人生 > >Sobel邊緣檢測演算法及OpenCV函式實現

Sobel邊緣檢測演算法及OpenCV函式實現

轉自https://www.cnblogs.com/herenzhiming/articles/6526741.html  https://blog.csdn.net/qaz_wz/article/details/79052246

  • 演算法原理

索貝爾運算元(Sobel operator)主要用作邊緣檢測,在技術上,它是一離散性差分運算元,用來運算影象亮度函式的灰度之近似值。在影象的任何一點使用此運算元,將會產生對應的灰度向量或是其法向量

Sobel卷積因子為:

該運算元包含兩組3x3的矩陣,分別為橫向及縱向,將之與影象作平面卷積,即可分別得出橫向及縱向的亮度差分近似值。如果以A代表原始影象,Gx及Gy分別代表經橫向及縱向邊緣檢測的影象灰度值,其公式如下:

具體計算如下:

Gx = (-1)*f(x-1, y-1) + 0*f(x,y-1) + 1*f(x+1,y-1)

      +(-2)*f(x-1,y) + 0*f(x,y)+2*f(x+1,y)

      +(-1)*f(x-1,y+1) + 0*f(x,y+1) + 1*f(x+1,y+1)

= [f(x+1,y-1)+2*f(x+1,y)+f(x+1,y+1)]-[f(x-1,y-1)+2*f(x-1,y)+f(x-1,y+1)]

 

Gy =1* f(x-1, y-1) + 2*f(x,y-1)+ 1*f(x+1,y-1)

      +0*f(x-1,y) 0*f(x,y) + 0*f(x+1,y)

      +(-1)*f(x-1,y+1) + (-2)*f(x,y+1) + (-1)*f(x+1, y+1)

= [f(x-1,y-1) + 2f(x,y-1) + f(x+1,y-1)]-[f(x-1, y+1) + 2*f(x,y+1)+f(x+1,y+1)]

 

其中f(a,b), 表示影象(a,b)點的灰度值;

影象的每一個畫素的橫向及縱向灰度值通過以下公式結合,來計算該點灰度的大小:

通常,為了提高效率 使用不開平方的近似值:

如果梯度G大於某一閥值則認為該點(x,y)為邊緣點。

然後可用以下公式計算梯度方向:

Sobel運算元根據畫素點上下、左右鄰點灰度加權差,在邊緣處達到極值這一現象檢測邊緣。對噪聲具有平滑作用,提供較為精確的邊緣方向資訊,邊緣定位精度不夠高。當對精度要求不是很高時,是一種較為常用的邊緣檢測方法。

附帶知識:

普利維特運算元(Prewitt operate) 

除sobel邊緣檢測外 還有Prewitt運算元, 它的卷積因子如下:

其他計算 和sobel差不多;

Prewitt運算元利用畫素點上下、左右鄰點灰度差,在邊緣處達到極值檢測邊緣。對噪聲具有平滑作用,定位精度不夠高。

羅伯茨交叉邊緣檢測(Roberts Cross operator

卷積因子如下:

灰度公式為:

近似公式為:

 

具體計算如下:

G(x,y)=abs(f(x,y)-f(x+1,y+1))+abs(f(x,y+1)-f(x+1,y))

灰度方向 計算公式為:

Roberts運算元採用對角線方向相鄰兩畫素之差近似梯度幅值檢測邊緣。檢測水平和垂直邊緣的效果好於斜向邊緣,定位精度高,對噪聲敏感

其他邊緣檢測技術:

參考文章:

http://homepages.inf.ed.ac.uk/rbf/HIPR2/featops.htm

http://homepages.inf.ed.ac.uk/rbf/HIPR2/sobel.htm

 

  • OpenCV實現

Sobel函式使用擴充套件的 Sobel 運算元,來計算一階、二階、三階或混合影象差分。

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,輸出影象的深度,支援如下src.depth()和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 方向上的差分階數。
第五個引數,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函式

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

const char* path = "C:/Users/Administrator/Desktop/opencv/1.jpg";
const char* load_win = "load image";
const char* gray_win = "gray imgae";

int main(int argc, char** argv)
{
    Mat src, dst;
    src = imread(path);
    if (src.empty())
    {
        cout << "could not load..." << endl;
        return -1;
    }
    imshow(load_win, src);

    GaussianBlur(src, dst, Size(3, 3), 0, 0);
    Mat src_gray;
    cvtColor(dst, src_gray, CV_BGR2GRAY);
    imshow(gray_win, src_gray);

    Mat x_grad, y_grad;
    Sobel(src_gray, x_grad, CV_16S, 1, 0, 3);
    Sobel(src_gray, y_grad, CV_16S, 0, 1, 3);
    convertScaleAbs(x_grad, x_grad);
    convertScaleAbs(y_grad, y_grad);
    imshow("x_grad", x_grad);
    imshow("y_grad", y_grad);

    waitKey(0);
    return 0;
}