對於做圖像處理的工程師來說,Sobel非常熟悉且常用。但是當我們需要使用Sobel進行梯度運算,且希望得到“數學結果”(作為下一步運算的基礎)而不是“圖片效果”的時候,就必須深入了解Sobel的知識原理和OpenCV實現的細節(當然我們是OpenCV支持則)。這里對具體內容進行研究。
在x方向上用Sobel算子進行近似一階求導的結果
Sobel算子效果,y方向近似一階導數
OpenCV中給出了函數使用的定義
cv::InputArray src, // 源圖像
cv::OutputArray dst, // 目標圖像
int ddepth, // 像素深度 (如CV_8U)
int xorder, // x方向對應的倒數順序
int yorder, // y方向對應的倒數順序
cv::Size ksize = 3, // 核大小
double scale = 1, // 閾值
double delta = 0, // 偏移
int borderType = cv::BORDER_DEFAULT // 邊框外推方法
);
1、其中src和dst是源圖像和目標圖像,可以通過指明參數ddepth來確定目標圖像的深度或類型(如CV_32F)。舉個例子,如果src是一幅8位圖像,那么dst需要至少CV_16S的深度保證不出現溢出;
2、xorder和yorder是求導順序,其取值范圍為0、1和2。0表示在這個方向上不進行求導,那2代表什么?
3、ksize是一個奇數,表示了調用的濾波器的寬和高,目前最大支持到31;
4、閾值和偏移將在把結果存入dst前調用,這有助於你將求導結果可視化.borderType參數的功能與其他卷積操作完全一樣。
上面有一個遺留問題,就是xorder(yorder)取2的時候代表什么?為此翻閱OpenCV源碼
step1
step 2
step3
int dx, int dy, int _ksize, bool normalize, int ktype )
{
int i, j, ksizeX = _ksize, ksizeY = _ksize;
if( ksizeX == 1 && dx > 0 )
ksizeX = 3;
if( ksizeY == 1 && dy > 0 )
ksizeY = 3;
CV_Assert( ktype == CV_32F || ktype == CV_64F );
_kx.create(ksizeX, 1, ktype, -1, true);
_ky.create(ksizeY, 1, ktype, -1, true);
Mat kx = _kx.getMat();
Mat ky = _ky.getMat();
if( _ksize % 2 == 0 || _ksize > 31 )
CV_Error( CV_StsOutOfRange, "The kernel size must be odd and not larger than 31" );
std::vector<int> kerI(std::max(ksizeX, ksizeY) + 1);
CV_Assert( dx >= 0 && dy >= 0 && dx+dy > 0 );
for( int k = 0; k < 2; k++ )
{
Mat* kernel = k == 0 ? &kx : &ky;
int order = k == 0 ? dx : dy;
int ksize = k == 0 ? ksizeX : ksizeY;
CV_Assert( ksize > order );
if( ksize == 1 )
kerI[0] = 1;
else if( ksize == 3 )
{
if( order == 0 )
kerI[0] = 1, kerI[1] = 2, kerI[2] = 1;
else if( order == 1 )
kerI[0] = -1, kerI[1] = 0, kerI[2] = 1;
else
kerI[0] = 1, kerI[1] = -2, kerI[2] = 1;
}
else
{
int oldval, newval;
kerI[0] = 1;
for( i = 0; i < ksize; i++ )
kerI[i+1] = 0;
for( i = 0; i < ksize - order - 1; i++ )
{
oldval = kerI[0];
for( j = 1; j <= ksize; j++ )
{
newval = kerI[j]+kerI[j-1];
kerI[j-1] = oldval;
oldval = newval;
}
}
for( i = 0; i < order; i++ )
{
oldval = -kerI[0];
for( j = 1; j <= ksize; j++ )
{
newval = kerI[j-1] - kerI[j];
kerI[j-1] = oldval;
oldval = newval;
}
}
}
Mat temp(kernel->rows, kernel->cols, CV_32S, &kerI[0]);
double scale = !normalize ? 1. : 1./(1 << (ksize-order-1));
temp.convertTo(*kernel, ktype, scale);
}
}

那么,可以看見,當order ==2 時候,生成了[1 -2 1]作為類似的模板,不管是什么,這個不是我想要的。
除了上面看到的,還可以發現同時設置xorder 和 yorder的時候,最后並沒有看到相加的動作。而如果我們計算的結果是梯度場的時候,就不僅要算xorder,而且要算yorder,並且最后要把這兩個結果求和。如果自己編碼,那么可能如下:
vx = *(lpSrc + IMGW + 1) - *(lpSrc + IMGW - 1) +
*(lpSrc + 1)*2 - *(lpSrc - 1)*2 +
*(lpSrc - IMGW + 1) - *(lpSrc - IMGW - 1);
//求y方向偏導
vy = *(lpSrc + IMGW - 1) - *(lpSrc - IMGW - 1) +
*(lpSrc + IMGW)*2 - *(lpSrc - IMGW)*2 +
*(lpSrc + IMGW + 1) - *(lpSrc - IMGW + 1);
gradSum += (abs(vx)+abs(vy));

Mat matTst = Mat(Size( 11 , 11 ),CV_8UC1,Scalar( 0 ));
line(matTst,Point( 5 , 0 ),Point( 5 , 11 ),Scalar( 255 ));
line(matTst,Point( 0 , 5 ),Point( 11 , 5 ),Scalar( 255 ));
Sobel(matTst,matX,CV_16SC1,1,0);



Sobel(matTst,matY,CV_16SC1,0,1);

Sobel(matTst,matXY,CV_16SC1,1,1);


