| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifndef FLANN_INDEX_TESTING_H_ |
| #define FLANN_INDEX_TESTING_H_ |
|
|
| #include <cstring> |
| #include <cassert> |
| #include <cmath> |
|
|
| #include "FLANN/util/matrix.h" |
| #include "FLANN/algorithms/nn_index.h" |
| #include "FLANN/util/result_set.h" |
| #include "FLANN/util/logger.h" |
| #include "FLANN/util/timer.h" |
|
|
|
|
| namespace flann |
| { |
|
|
| inline int countCorrectMatches(size_t* neighbors, size_t* groundTruth, int n) |
| { |
| int count = 0; |
| for (int i=0; i<n; ++i) { |
| for (int k=0; k<n; ++k) { |
| if (neighbors[i]==groundTruth[k]) { |
| count++; |
| break; |
| } |
| } |
| } |
| return count; |
| } |
|
|
|
|
| template <typename Distance> |
| typename Distance::ResultType computeDistanceRaport(const Matrix<typename Distance::ElementType>& inputData, typename Distance::ElementType* target, |
| size_t* neighbors, size_t* groundTruth, int veclen, int n, const Distance& distance) |
| { |
| typedef typename Distance::ResultType DistanceType; |
|
|
| DistanceType ret = 0; |
| for (int i=0; i<n; ++i) { |
| DistanceType den = distance(inputData[groundTruth[i]], target, veclen); |
| DistanceType num = distance(inputData[neighbors[i]], target, veclen); |
|
|
| if ((den==0)&&(num==0)) { |
| ret += 1; |
| } |
| else { |
| ret += num/den; |
| } |
| } |
|
|
| return ret; |
| } |
|
|
| template <typename Index, typename Distance> |
| float search_with_ground_truth(Index& index, const Matrix<typename Distance::ElementType>& inputData, |
| const Matrix<typename Distance::ElementType>& testData, const Matrix<size_t>& matches, int nn, int checks, |
| float& time, typename Distance::ResultType& dist, const Distance& distance, int skipMatches) |
| { |
| typedef typename Distance::ElementType ElementType; |
| typedef typename Distance::ResultType DistanceType; |
|
|
| if (matches.cols<size_t(nn)) { |
| Logger::info("matches.cols=%d, nn=%d\n",matches.cols,nn); |
| throw FLANNException("Ground truth is not computed for as many neighbors as requested"); |
| } |
|
|
| SearchParams searchParams(checks); |
|
|
| size_t* indices = new size_t[nn+skipMatches]; |
| DistanceType* dists = new DistanceType[nn+skipMatches]; |
|
|
| Matrix<size_t> indices_mat(indices, 1, nn+skipMatches); |
| Matrix<DistanceType> dists_mat(dists, 1, nn+skipMatches); |
|
|
| size_t* neighbors = indices + skipMatches; |
|
|
| int correct = 0; |
| DistanceType distR = 0; |
| StartStopTimer t; |
| int repeats = 0; |
| while (t.value<0.2) { |
| repeats++; |
| t.start(); |
| correct = 0; |
| distR = 0; |
| for (size_t i = 0; i < testData.rows; i++) { |
| index.knnSearch(Matrix<ElementType>(testData[i], 1, testData.cols), indices_mat, dists_mat, nn+skipMatches, searchParams); |
|
|
| correct += countCorrectMatches(neighbors,matches[i], nn); |
| distR += computeDistanceRaport<Distance>(inputData, testData[i], neighbors, matches[i], testData.cols, nn, distance); |
| } |
| t.stop(); |
| } |
| time = float(t.value/repeats); |
|
|
| delete[] indices; |
| delete[] dists; |
|
|
| float precicion = (float)correct/(nn*testData.rows); |
|
|
| dist = distR/(testData.rows*nn); |
|
|
| Logger::info("%8d %10.4g %10.5g %10.5g %10.5g\n", |
| checks, precicion, time, 1000.0 * time / testData.rows, dist); |
|
|
| return precicion; |
| } |
|
|
|
|
| template <typename Index, typename Distance> |
| float test_index_checks(Index& index, const Matrix<typename Distance::ElementType>& inputData, |
| const Matrix<typename Distance::ElementType>& testData, const Matrix<size_t>& matches, |
| int checks, float& precision, const Distance& distance, int nn = 1, int skipMatches = 0) |
| { |
| typedef typename Distance::ResultType DistanceType; |
|
|
| Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n"); |
| Logger::info("---------------------------------------------------------\n"); |
|
|
| float time = 0; |
| DistanceType dist = 0; |
| precision = search_with_ground_truth(index, inputData, testData, matches, nn, checks, time, dist, distance, skipMatches); |
|
|
| return time; |
| } |
|
|
| template <typename Index, typename Distance> |
| float test_index_precision(Index& index, const Matrix<typename Distance::ElementType>& inputData, |
| const Matrix<typename Distance::ElementType>& testData, const Matrix<size_t>& matches, |
| float precision, int& checks, const Distance& distance, int nn = 1, int skipMatches = 0) |
| { |
| typedef typename Distance::ResultType DistanceType; |
| const float SEARCH_EPS = 0.001f; |
|
|
| Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n"); |
| Logger::info("---------------------------------------------------------\n"); |
|
|
| int c2 = 1; |
| float p2; |
| int c1 = 1; |
| |
| float time; |
| DistanceType dist; |
|
|
| p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); |
|
|
| if (p2>precision) { |
| Logger::info("Got as close as I can\n"); |
| checks = c2; |
| return time; |
| } |
|
|
| while (p2<precision) { |
| c1 = c2; |
| |
| c2 *=2; |
| p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); |
| } |
|
|
| int cx; |
| float realPrecision; |
| if (fabs(p2-precision)>SEARCH_EPS) { |
| Logger::info("Start linear estimation\n"); |
| |
| |
|
|
| cx = (c1+c2)/2; |
| realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); |
| while (fabs(realPrecision-precision)>SEARCH_EPS) { |
|
|
| if (realPrecision<precision) { |
| c1 = cx; |
| } |
| else { |
| c2 = cx; |
| } |
| cx = (c1+c2)/2; |
| if (cx==c1) { |
| Logger::info("Got as close as I can\n"); |
| break; |
| } |
| realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); |
| } |
|
|
| c2 = cx; |
| p2 = realPrecision; |
|
|
| } |
| else { |
| Logger::info("No need for linear estimation\n"); |
| cx = c2; |
| realPrecision = p2; |
| } |
|
|
| checks = cx; |
| return time; |
| } |
|
|
|
|
| template <typename Index, typename Distance> |
| void test_index_precisions(Index& index, const Matrix<typename Distance::ElementType>& inputData, |
| const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, |
| float* precisions, int precisions_length, const Distance& distance, int nn = 1, int skipMatches = 0, float maxTime = 0) |
| { |
| typedef typename Distance::ResultType DistanceType; |
|
|
| const float SEARCH_EPS = 0.001; |
|
|
| |
| std::sort(precisions, precisions+precisions_length); |
|
|
| int pindex = 0; |
| float precision = precisions[pindex]; |
|
|
| Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n"); |
| Logger::info("---------------------------------------------------------\n"); |
|
|
| int c2 = 1; |
| float p2; |
|
|
| int c1 = 1; |
| float p1; |
|
|
| float time; |
| DistanceType dist; |
|
|
| p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); |
|
|
| |
| |
| |
| while (precisions[pindex]<p2 && pindex<precisions_length) { |
| pindex++; |
| } |
|
|
| if (pindex==precisions_length) { |
| Logger::info("Got as close as I can\n"); |
| return; |
| } |
|
|
| for (int i=pindex; i<precisions_length; ++i) { |
|
|
| precision = precisions[i]; |
| while (p2<precision) { |
| c1 = c2; |
| p1 = p2; |
| c2 *=2; |
| p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches); |
| if ((maxTime> 0)&&(time > maxTime)&&(p2<precision)) return; |
| } |
|
|
| int cx; |
| float realPrecision; |
| if (fabs(p2-precision)>SEARCH_EPS) { |
| Logger::info("Start linear estimation\n"); |
| |
| |
|
|
| cx = (c1+c2)/2; |
| realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); |
| while (fabs(realPrecision-precision)>SEARCH_EPS) { |
|
|
| if (realPrecision<precision) { |
| c1 = cx; |
| } |
| else { |
| c2 = cx; |
| } |
| cx = (c1+c2)/2; |
| if (cx==c1) { |
| Logger::info("Got as close as I can\n"); |
| break; |
| } |
| realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches); |
| } |
|
|
| c2 = cx; |
| p2 = realPrecision; |
|
|
| } |
| else { |
| Logger::info("No need for linear estimation\n"); |
| cx = c2; |
| realPrecision = p2; |
| } |
|
|
| } |
| } |
|
|
| } |
|
|
| #endif |
|
|