[VLFeat]Dense Sift的C源碼學習

VLFeat是一個很好用的開源庫,其中實現了計算機視覺常用的算法,使用的語言是C和matlab。
官網:http://www.vlfeat.org/index.html
在官網下載最新版本後,在matlab中添加路徑即可。
1,Dense Sift
在文章《sift特徵提取算法》中提到,提取圖像的sift特徵分4步:構建DOG尺度空間;關鍵點定位;關鍵點方向賦值;生成描述子。
這裏產生的sift特徵點是sparse sift,而在實際實驗中,使用較多的則是dense sift。
dense sift省去了前3步,即直接指定關鍵點位置和描述子採樣區域,計算sift特徵。
主要過程是:
1,用一個patch在圖像上以一定步長step滑動,代碼中step=1,這個patch就是描述子採樣區域,patch size是4bins*4bins,bin size可以自己指定,代碼中是3pixels*3pixels。這裏說的bin對應到《sift特徵提取》中的第4步就是指子區域area。圖中的bounding box是sift特徵點的範圍。
這裏寫圖片描述
2,計算每個像素點的梯度(同sparse sift),統計每個bin內的像素點在8個方向上的梯度直方圖,這樣就生成了4*4*8維的sift特徵。
這裏寫圖片描述
在matlab中直接調用vl_dsift:

% 讀入圖像
I = vl_impattern('roofs1') ;
I = single(vl_imdown(rgb2gray(I))) ;
binSize = 8 ;
magnif = 3 ;
% 得到指定尺度的高斯圖像
Is = vl_imsmooth(I, sqrt((binSize/magnif)^2 - .25)) ;
% 計算dense sift
[frame, descr] = vl_dsift(Is, 'size', binSize) ;
% frame中存儲每個sift點的座標,descr存儲每個sift點的128維特徵向量

主要實現代碼在vl_dsift.c, dsift.c中
參數說明:
size: 是每個bin的大小,代碼中是3pixels*3pixels
step: patch移動步長
bounds: sift點的區域,含minX, minY, maxX,maxY四個值
norm: 本來是存在frame中的一列,存儲descriptor的值的和,用於normalization
geometry: [4 4 8],在4bins*4bins的範圍內,對每個bin統計8個方向的梯度直方圖
verbose: 一般bin大小和patch大小都取正方形,也就是長和寬一致,如果打開這個verbose開關,則要對sizeX和sizeY分別賦值。
自定義數據結構在文件dsift.h中:

typedef struct VlDsiftKeypoint_
{
  double x ; /**< x coordinate */
  double y ; /**< y coordinate */
  double s ; /**< scale */
  double norm ; /**< SIFT descriptor norm */
} VlDsiftKeypoint ;

/** @brief Dense SIFT descriptor geometry */
typedef struct VlDsiftDescriptorGeometry_
{
  int numBinT ;  /**< number of orientation bins */
  int numBinX ;  /**< number of bins along X */
  int numBinY ;  /**< number of bins along Y */
  int binSizeX ; /**< size of bins along X */
  int binSizeY ; /**< size of bins along Y */
} VlDsiftDescriptorGeometry ;
typedef struct VlDsiftFilter_
{
  int imWidth ;            /**< @internal @brief image width */
  int imHeight ;           /**< @internal @brief image height */

  int stepX ;              /**< frame sampling step X */
  int stepY ;              /**< frame sampling step Y */

  int boundMinX ;          /**< frame bounding box min X */
  int boundMinY ;          /**< frame bounding box min Y */
  int boundMaxX ;          /**< frame bounding box max X */
  int boundMaxY ;          /**< frame bounding box max Y */

  /** descriptor parameters */
  VlDsiftDescriptorGeometry geom ;

  int useFlatWindow ;      /**< flag: whether to approximate the Gaussian window with a flat one */
  double windowSize ;      /**< size of the Gaussian window */

  int numFrames ;          /**< number of sampled frames */
  int descrSize ;          /**< size of a descriptor */
  VlDsiftKeypoint *frames ; /**< frame buffer */
  float *descrs ;          /**< descriptor buffer */

  int numBinAlloc ;        /**< buffer allocated: descriptor size */
  int numFrameAlloc ;      /**< buffer allocated: number of frames  */
  int numGradAlloc ;       /**< buffer allocated: number of orientations */

  float **grads ;          /**< gradient buffer */
  float *convTmp1 ;        /**< temporary buffer */
  float *convTmp2 ;        /**< temporary buffer */
}  VlDsiftFilter ;

主要邏輯代碼註釋如下:
vl_dsift.c中的mex函數:

void
mexFunction(int nout, mxArray *out[],
            int nin, const mxArray *in[])
{
/*前面是輸入參數的處理,此處從Do job部分開始*/
    //參數初始化
   int numFrames ;
    int descrSize ;
    VlDsiftKeypoint const *frames ;
    float const *descrs ;
    int k, i ;

  VlDsiftFilter *dsift ;

   //M,N是圖像width,height
    dsift = vl_dsift_new (M, N) ;
    vl_dsift_set_geometry(dsift, &geom) ;
    //step[0],step[1]分別代表x,y方向上的移動步長,verbose打開時值不相等
    vl_dsift_set_steps(dsift, step[0], step[1]) ;

   if (bounds) {
      vl_dsift_set_bounds(dsift,
                          VL_MAX(bounds[1], 0),
                          VL_MAX(bounds[0], 0),
                          VL_MIN(bounds[3], M - 1),
                          VL_MIN(bounds[2], N - 1));
    }
    vl_dsift_set_flat_window(dsift, useFlatWindow) ;


   if (windowSize >= 0) {
      vl_dsift_set_window_size(dsift, windowSize) ;
    }
   numFrames = vl_dsift_get_keypoint_num (dsift) ;
    descrSize = vl_dsift_get_descriptor_size (dsift) ;
    geom = *vl_dsift_get_geometry (dsift) ;
   /*此處省略一部分代碼,是處理verbose打開的情況*/
      //計算sift特徵
      vl_dsift_process (dsift, data) ;

   //這裏得到的frames中還包含norm
    frames = vl_dsift_get_keypoints (dsift) ;
    descrs = vl_dsift_get_descriptors (dsift) ;
  /*後面將frames和descrs中的數據處理(歸一化等)再輸出*/
}

vl_dsift_process函數在dsift.c中:

void vl_dsift_process (VlDsiftFilter* self, float const* im)
{
  int t, x, y ;

  _vl_dsift_alloc_buffers (self) ;

  for (t = 0 ; t < self->geom.numBinT ; ++t)
    memset (self->grads[t], 0,
            sizeof(float) * self->imWidth * self->imHeight) ;

#undef at
#define at(x,y) (im[(y)*self->imWidth+(x)])

  //對每一個像素點計算梯度(幅值和幅角),norm,

  for (y = 0 ; y < self->imHeight ; ++ y) {
    for (x = 0 ; x < self->imWidth ; ++ x) {
      float gx, gy ;
      float angle, mod, nt, rbint ;
      int bint ;

      //y方向梯度
      if (y == 0) {
        gy = at(x,y+1) - at(x,y) ;
      } else if (y == self->imHeight - 1) {
        gy = at(x,y) - at(x,y-1) ;
      } else {
        gy = 0.5F * (at(x,y+1) - at(x,y-1)) ;
      }

      //x方向梯度
      if (x == 0) {
        gx = at(x+1,y) - at(x,y) ;
      } else if (x == self->imWidth - 1) {
        gx = at(x,y) - at(x-1,y) ;
      } else {
        gx = 0.5F * (at(x+1,y) - at(x-1,y)) ;
      }

      //計算幅角
      angle = vl_fast_atan2_f (gy,gx) ;
      //計算幅值
      mod = vl_fast_sqrt_f (gx*gx + gy*gy) ;

      //計算8個方向的值,把角度值轉換成實數值
      nt = vl_mod_2pi_f (angle) * (self->geom.numBinT / (2*VL_PI)) ;
      bint = (int) vl_floor_f (nt) ;
      rbint = nt - bint ;

      //存梯度信息,統計直方圖
      self->grads [(bint    ) % self->geom.numBinT][x + y * self->imWidth] = (1 - rbint) * mod ;
      self->grads [(bint + 1) % self->geom.numBinT][x + y * self->imWidth] = (    rbint) * mod ;
    }
  }
  //這裏的flat_window是一種比高斯函數較快的平滑方法
    if (self->useFlatWindow) {
    _vl_dsift_with_flat_window(self) ;
  } else {
    _vl_dsift_with_gaussian_window(self) ;
  }

  {
    VlDsiftKeypoint* frameIter = self->frames ;
    float * descrIter = self->descrs ;
    int framex, framey, bint ;

    int frameSizeX = self->geom.binSizeX * (self->geom.numBinX - 1) + 1 ;
    int frameSizeY = self->geom.binSizeY * (self->geom.numBinY - 1) + 1 ;
    int descrSize = vl_dsift_get_descriptor_size (self) ;

    float deltaCenterX = 0.5F * self->geom.binSizeX * (self->geom.numBinX - 1) ;
    float deltaCenterY = 0.5F * self->geom.binSizeY * (self->geom.numBinY - 1) ;

    float normConstant = frameSizeX * frameSizeY ;

    for (framey  = self->boundMinY ;
         framey <= self->boundMaxY - frameSizeY + 1 ;
         framey += self->stepY) {

      for (framex  = self->boundMinX ;
           framex <= self->boundMaxX - frameSizeX + 1 ;
           framex += self->stepX) {

        frameIter->x    = framex + deltaCenterX ;
        frameIter->y    = framey + deltaCenterY ;

        //norm是以當前像素點爲中心點的patch中所有像素的梯度幅值的平均值
        {
          float mass = 0 ;
          for (bint = 0 ; bint < descrSize ; ++ bint)
            mass += descrIter[bint] ;
          mass /= normConstant ;
          frameIter->norm = mass ;
        }

        /* L2 normalize */
        _vl_dsift_normalize_histogram (descrIter, descrIter + descrSize) ;

        /* clamp */
        for(bint = 0 ; bint < descrSize ; ++ bint)
          if (descrIter[bint] > 0.2F) descrIter[bint] = 0.2F ;

        /* L2 normalize */
        _vl_dsift_normalize_histogram (descrIter, descrIter + descrSize) ;

        frameIter ++ ;
        descrIter += descrSize ;
      } /* for framex */
    } /* for framey */
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章