Search code examples
c++cvlfeat

VlFeat kdtree setup and query


I've managed to get VlFeat's SIFT implmentation working and I'd like to try matching two sets of image descriptors.

SIFT's feature vectors are 128 element float arrays, I've stored the descriptor lists in std::vectors as shown in the snippet below:

std::vector<std::vector<float> > ldescriptors = leftImage->descriptors;
std::vector<std::vector<float> > rdescriptors = rightImage->descriptors;

/* KDTree, L1 comparison metric, dimension 128, 1 tree, L1 metric */
VlKDForest* forest = vl_kdforest_new(VL_TYPE_FLOAT, 128, 1, VlDistanceL1);

/* Build the tree from the left descriptors */
vl_kdforest_build(forest, ldescriptors.size(), ldescriptors.data());

/* Searcher object */
VlKDForestSearcher* searcher = vl_kdforest_new_searcher(forest);
VlKDForestNeighbor neighbours[2];

/* Query the first ten points for now */
for(int i=0; i < 10; i++){
    int nvisited = vl_kdforestsearcher_query(searcher, &neighbours, 2, rdescriptors[i].data());

    cout << nvisited << neighbours[0].distance << neighbours[1].distance;

}

As far as I can tell that should work, but all I get out, for the distances, are nan's. The length of the descriptor arrays checkout so there does seem to be data going into the tree. I've plotted the keypoints and they also look reasonable, so the data is fairly sane.

What am I missing?

Rather sparse documentation here (links to the API): http://www.vlfeat.org/api/kdtree.html


Solution

  • What am I missing?

    The 2nd argument of vl_kdforestsearcher_query takes a pointer to VlKDForestNeighbor:

    vl_size
    vl_kdforestsearcher_query(
      VlKDForestSearcher *self,
      VlKDForestNeighbor *neighbors,
      vl_size numNeighbors,
      void const *query
    );
    

    But here you declared VlKDForestNeighbor neighbours[2]; and then passed &neighbours as 2nd parameter which is not correct - your compiler probably issued a incompatible pointer types warning.

    Since you declared an array, what you must do instead is either pass explicitly a pointer to the 1st neighbor:

    int nvisited = vl_kdforestsearcher_query(searcher, &neighbours[0], 2, qrys[i]);
    

    Or alternatively let the compiler do it for you:

    int nvisited = vl_kdforestsearcher_query(searcher, neighbours, 2, qrys[i]);
    

    EDIT

    There is indeed a second (major) problem related to the way you build the kd-tree with ldescriptors.data().

    Here you pass a std::vector<float>* pointer when VLFeat expects a float * contiguous array containing all your data points in row major order. So what you can do is copying your data in this format:

    float *data = new float[128*ldescriptors.size()];
    
    for (unsigned int i = 0; i < ldescriptors.size(); i++)
      std::copy(ldescriptors[i].begin(), ldescriptors[i].end(), data + 128*i);
    
    vl_kdforest_build(forest, ldescriptors.size(), data);
    
    // ...
    
    // then, right after `vl_kdforest_delete(forest);`
    // do a `delete[] data;`