0

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

deltheil
  • 15,496
  • 2
  • 44
  • 64
Josh
  • 2,658
  • 31
  • 36

1 Answers1

1

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;`
deltheil
  • 15,496
  • 2
  • 44
  • 64
  • Ok, I think that was a typo in the question, it actually errors rather than warns. But the distances are still either ~1, inf or nan. I've written a brute force matcher to double check the data's not being lost along the way and that works as expected. – Josh Feb 22 '15 at 00:37
  • I've edited my answer with a fix for the main problem. – deltheil Feb 22 '15 at 16:54
  • Many thanks, that makes sense - I was thinking in Python/np.flatten() mode. The numbers look reasonable now. – Josh Feb 23 '15 at 01:34