SFIT特徵(四)

在上一章節中,我們知道,對於每個穩定檢測的關鍵點,我們賦予其三個信息:座標位置、尺度(哪一組哪一層)以及主方向(統計鄰域像素點的梯度方向和大小計算直方圖而來)。接下來,我們需要爲每個關鍵點建立一個128維度的描述符。

6.1採樣區域
關鍵點的描述符是通過統計其鄰域採樣點的梯度方向和大小而來的,所以首先需要確定採樣區域的範圍。
首先鄰域採樣點的梯度計算是在關鍵點對應的高斯圖像上進行的(根據關鍵點的尺度信息確定高斯過濾參數),然後,將關鍵點鄰域劃分爲dd (Lowe中建議值爲4)的子區域,每個子區域作爲一個種子點,種子點包含8個方向,將子區域內的採樣點按照其梯度方向分配至相應的8個方向之一。

每個子區域的爲邊長同樣是3σoct (與確定關鍵點主方向相同)的正方形區域,所以總邊長爲3σoctd 。但由於後期我們需要對採樣點梯度進行插值,所以將窗口擴展至3σoct(d+1) 。爲了保證旋轉不變性,所以我們將座標軸旋轉,使其與關鍵點的主方向對齊,則鄰域區域的最終邊長爲radius=3(2)σoct(d+1) 。鄰域邊長變換示意圖如下所示,

這裏寫圖片描述

6.2採樣點子區域標號計算
每個採樣點(x,y)T 的梯度和方向按照5.1的公式進行計算,這裏不再說明。(注意,這裏的座標(x,y)T 表示以關鍵點爲原點的座標系的座標)
確定好採樣區域之後,每個採樣點(x,y)T 在新座標系(爲了保證旋轉不變性,將座標軸旋轉至於關鍵點主方向對齊)下的座標方程(x,y)T 爲,

(x,y)T=(cosθsinθsinθcosθ)(x,y)T

接下來,我們需要確定採樣點在新座標系下應當被分配到哪個子區域中(dd 子區域),由於每個子區域的邊長大小爲3σoct ,所以採樣點所對應的子區域標號爲,
(x′′,y′′)T=13σoct(x,y)T+d/2

6.3採樣點插值生成種子點向量
Lowe建議,每個採樣點梯度採用大小爲σ=0.5d 的高斯函數進行處理,即,

mag(a+x,b+y)e(x)2+(y)22(0.5d)2

其中,(a,b) 表示關鍵點在高斯圖像中的座標,(x,y) 表示採樣點在採樣區域內的局部座標,mag() 表示對應像素點的梯度大小。

另外,每個採樣點的梯度方向相對於關鍵點主方向進行旋轉,即採樣點的梯度方向減掉關鍵點主方向的值作爲採樣點的梯度方向進行後續計算。

經過上面的步驟,我們知道了每個採樣點的梯度大小和方向,以及在新座標下所分配的子區域標號,但注意的是,每個採樣點的子區域標號往往不是整數的,也就說是採樣點對周圍鄰近的幾個子區域都有一定的貢獻。所以,接下來,我們採用插值的方法計算每個採樣點對鄰近子區域,鄰近bin方向的貢獻程度。

插值思想如下,紅色點表示的是採樣點,dr1dr2dc1dc2do1do2 分別表採樣點距離行r0r1 ,列c0c1 ,方向o1o1 的距離比值。則以G爲例,G表示最終對右下子區域方向o0 的貢獻程度爲magdr1dc1do1

這裏寫圖片描述

如果關鍵點周圍統計子區域大小44 ,每個子區域按照8個bin方向統計,那麼對於一個關鍵點而言,其描述符向量總的長度爲128維度。下圖所示是關鍵點子區域大小爲22 ,每個子區域取8個bin方向的示意圖。

這裏寫圖片描述

6.4歸一化、設置閾值
經過上面的介紹,我們已經爲每個關鍵點分配了一個128維度的描述符向量,爲了增強魯棒性,去除光照等的影響,需要歸一化處理。另外,可能由於相機飽和度變化造成某些方向梯度值過大,可以通過設置閾值,截斷較大的梯度值(Lowe中取閾值爲0.2)。

至此,我們已經瞭解瞭如何針對一幅圖像,生成旋轉、尺度等不變性的特徵描述符。



6.5C++源代碼

/*
* 生成關鍵點描述符。
* img:高斯圖像(按照keypoint的scale高斯濾波的圖像)
* ptf:keypoints座標
* ori:keypoint的主方向
* d/n:keypoints鄰域子區域邊長爲4,每個區域內取8個直方圖bins
*/
static void calcSIFTDescriptor( const Mat& img, Point2f ptf, float ori, float scl,
                               int d, int n, float* dst )
{
    Point pt(cvRound(ptf.x), cvRound(ptf.y));
    //轉換爲弧度值
    float cos_t = cosf(ori*(float)(CV_PI/180));
    float sin_t = sinf(ori*(float)(CV_PI/180));
    float bins_per_rad = n / 360.f;
    float exp_scale = -1.f/(d * d * 0.5f);
    float hist_width = SIFT_DESCR_SCL_FCTR * scl;//3*\sgima_oct
    //採樣半徑爲:3*\sigma_oct*\sqrt(2)*(d+1)
    int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f);
    cos_t /= hist_width;
    sin_t /= hist_width;

    int i, j, k, len = (radius*2+1)*(radius*2+1), histlen = (d+2)*(d+2)*(n+2);
    int rows = img.rows, cols = img.cols;

    AutoBuffer<float> buf(len*6 + histlen);
    float *X = buf, *Y = X + len, *Mag = Y, *Ori = Mag + len, *W = Ori + len;
    float *RBin = W + len, *CBin = RBin + len, *hist = CBin + len;

    for( i = 0; i < d+2; i++ )
    {
        for( j = 0; j < d+2; j++ )
            for( k = 0; k < n+2; k++ )
                hist[(i*(d+2) + j)*(n+2) + k] = 0.;
    }

    //依次遍歷每個採樣點
    for( i = -radius, k = 0; i <= radius; i++ )
        for( j = -radius; j <= radius; j++ )
        {
            // Calculate sample's histogram array coords rotated relative to ori.
            // Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e.
            // r_rot = 1.5) have full weight placed in row 1 after interpolation.

            // 旋轉採樣點與keypoint的主方向對齊後的位置
            float c_rot = j * cos_t - i * sin_t;
            float r_rot = j * sin_t + i * cos_t;
            float rbin = r_rot + d/2 - 0.5f;
            float cbin = c_rot + d/2 - 0.5f;

            //採樣點在高斯圖像中的座標
            int r = pt.y + i, c = pt.x + j;

            if( rbin > -1 && rbin < d && cbin > -1 && cbin < d &&
                r > 0 && r < rows - 1 && c > 0 && c < cols - 1 )
            {
                //計算採樣點梯度
                float dx = (float)(img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1));
                float dy = (float)(img.at<sift_wt>(r-1, c) - img.at<sift_wt>(r+1, c));

                X[k] = dx; Y[k] = dy; RBin[k] = rbin; CBin[k] = cbin;
                W[k] = (c_rot * c_rot + r_rot * r_rot)*exp_scale;//權重
                k++;
            }
        }

    //計算每個像素點的梯度方向、大小和權重
    len = k;
    fastAtan2(Y, X, Ori, len, true);
    magnitude(X, Y, Mag, len);
    exp(W, W, len);

    for( k = 0; k < len; k++ )
    {
        float rbin = RBin[k], cbin = CBin[k];
        float obin = (Ori[k] - ori)*bins_per_rad;//梯度方向爲像素點的方向-關鍵點主方向(保持旋轉不變性)
        float mag = Mag[k]*W[k];

        //計算採樣點距離周圍子區域及相鄰bin的距離
        int r0 = cvFloor( rbin );
        int c0 = cvFloor( cbin );
        int o0 = cvFloor( obin );
        rbin -= r0;
        cbin -= c0;
        obin -= o0;

        if( o0 < 0 )
            o0 += n;
        if( o0 >= n )
            o0 -= n;

        // histogram update using tri-linear interpolation(x/y/orientation)
        float v_r1 = mag*rbin, v_r0 = mag - v_r1;
        float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11;
        float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01;

        float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111;
        float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101;
        float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011;
        float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001;

        int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0;
        hist[idx] += v_rco000;
        hist[idx+1] += v_rco001;
        hist[idx+(n+2)] += v_rco010;
        hist[idx+(n+3)] += v_rco011;
        hist[idx+(d+2)*(n+2)] += v_rco100;
        hist[idx+(d+2)*(n+2)+1] += v_rco101;
        hist[idx+(d+3)*(n+2)] += v_rco110;
        hist[idx+(d+3)*(n+2)+1] += v_rco111;
    }
   ......
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章