Hi everyone,
In my current application I have implemented a brute force descriptor matcher to get correspondences between 2 images, using fcvSumOfSquaredDiffs36xNs8. Since there are KDTrees supported within the new FastCV version I would like to implement a matcher using these KDTrees and kNN for better runtime performance.
I've implemented my first approach with kdtrees similar to the brute force approach, but my computed correspondences seem to be wrong (I don't get a correct homography, but I did get one with my brute force algorithm). What would be the correct way? As the code below shows, I'm creating a kdTree from the descriptors of the feature points of my target image. Then I loop over my reference image descriptors and query for the nearest neighbor within the kdTree. I'm only searching for 2 nearest neighbors (maxNN), since I only need the best match as correspondence and the second best match for a ratio test to reject outliers. I'm not sure how to set the other parameters, such as maxDist and maxChecks. Is my approach wrong? If yes, what would be the correct way and what would be good values for maxDist and maxChecks?
float32_t* invLenB = (float32_t*)fcvMemAlloc(state.numCorners*sizeof(float32_t), 16); int8_t **descriptorsB = (int8_t **) fcvMemAlloc(state.numCorners * sizeof(int8_t *), 16); for (uint32_t i = 0; i < state.numCorners; i++) { descriptorsB[i] = state.imageFeatureBuf[i].descriptor; invLenB[i] = (float)(1/sqrt((float)state.imageFeatureBuf[i].normSq)); } fcvKDTreeDatas8f32* kdTree; fcvKDTreeCreate36s8f32(*descriptorsB, invLenB, state.numCorners, &kdTree); int maxNN = 2; for(int k = 0; k < state.numCornersReferenceImg; k++){ int32_t numFound; int32_t* indicesFound = (int32_t*)fcvMemAlloc(maxNN*sizeof(int32_t), 8); float32_t* distancesFound = (float32_t*)fcvMemAlloc(maxNN*sizeof(float32_t), 8); fcvFeatureType feature = state.referenceFeatureBuf[k]; float invLen = (float)(1/sqrt((float)feature.normSq)); fcvKDTreeQuery36s8f32(kdTree, feature.descriptor, invLen, maxNN, 0.1, 200, NULL, &numFound, indicesFound, distancesFound); if(numFound >0){ int correspondenceIdx = indicesFound[0]; fcvFeatureType correspondenceFeature = state.imageFeatureBuf[correspondenceIdx]; } }
Normal 0 false false false EN-US X-NONE X-NONE MicrosoftInternetExplorer4
Assume that your target descriptors and their inverse lengths are in buffers declared as
int8_t* vectors;
float32_t* vecInvLen;
and their number is numDescriptorsDB. Somewhere in your program you have to have to initialize kdtree for these target descriptors
fcvKDTreeDatas8f32* kdTree = 0;
int err = fcvKDTreeCreate36s8f32( vectors, vecInvLen, numDescriptorsDB, &kdTree );
Now, assume that you extracted numQueries descriptors from camera frame and they are stored
in buffers like below:
int8_t* queries;
float32_t* queryInvLen;
Assume you want numNN (NN – nearest neighbor)results for each query so you declare buffers for them
int32_t* foundInds;
float32_t* foundDists;
int32_t numFound = 0;
I am not going into details how the above pointers are initialiazed, whether they point to static arrays or dynamically allocated blocks. They must point to buffers big enough to accept numNN indices and distances. Now we search for NNS say for i-th query
err = fcvKDTreeQuery36s8f32( kdTree, queries[ i ], queryInvLen[ i ], numNN, 0.1f, 32, 0, &numFound, foundInds, foundDists );
0.1 is max distance allowed between query and its NN. Number of NNs will be returned in numFound, indices to NNs (referring to vectors array) and distances to NNs will be returned in foundInds and foundDists respectively.
Once you do not need kdtree anymore you may destroy it
err = fcvKDTreeDestroy36s8f32( kdTree );
So, basically the usage of kdtree involves just 3 function calls (creation, querying and destruction).