66// simply include the original FLANN <flann/flann.hpp> and replace the cvflann
77// namespace by the flann namespace and the benchmark will still work.
88// NOTE: The OpenCV version of FLANN performs quite a bit faster for queries
9- // than https://github.com/mariusmuja/flann. About 30% for a single NN. Tree
10- // build times are the same. Simply used Release for building like with all
11- // other libs.
9+ // than https://github.com/mariusmuja/flann. About an order of magnitude for a
10+ // single NN. Tree build times are the same.
1211class BmOpenCvFlann : public pico_tree ::Benchmark {
1312 public:
1413};
1514
15+ // Change the namespace below to switch between FLANN versions.
16+ namespace fl = cvflann;
17+ // namespace fl = flann;
18+
19+ // The OpenCV version of FLANN has improved query performance over the original
20+ // but does not include the flann::L2_3D<Scalar> distance class:
21+ // https://github.com/mariusmuja/flann/blob/master/src/cpp/flann/algorithms/dist.h
22+ // This distance class gives a reasonable performance boost over
23+ // (cv)flann::L2_Simple because it uses a compile time constant dimension count.
24+ // NOTE: Strictly speaking it shouldn't be part of the performance test.
25+ namespace cvflann {
26+
27+ template <class T >
28+ struct L2_3D {
29+ typedef bool is_kdtree_distance;
30+
31+ typedef T ElementType;
32+ typedef typename Accumulator<T>::Type ResultType;
33+
34+ template <typename Iterator1, typename Iterator2>
35+ ResultType operator ()(
36+ Iterator1 a,
37+ Iterator2 b,
38+ size_t size,
39+ ResultType /* worst_dist*/ = -1 ) const {
40+ ResultType result = ResultType ();
41+ ResultType diff;
42+ diff = *a++ - *b++;
43+ result += diff * diff;
44+ diff = *a++ - *b++;
45+ result += diff * diff;
46+ diff = *a++ - *b++;
47+ result += diff * diff;
48+ return result;
49+ }
50+
51+ template <typename U, typename V>
52+ inline ResultType accum_dist (const U& a, const V& b, int ) const {
53+ return (a - b) * (a - b);
54+ }
55+ };
56+
57+ } // namespace cvflann
58+
59+ template <typename Scalar, int Dim>
60+ Scalar* RawCopy (std::vector<Point<Scalar, Dim>> const & points) {
61+ Scalar* copy = new Scalar[points.size () * Dim];
62+ std::copy (
63+ points.begin ()->data , points.begin ()->data + points.size () * Dim, copy);
64+ return copy;
65+ }
66+
1667// ****************************************************************************
1768// Building the tree
1869// ****************************************************************************
1970
2071BENCHMARK_DEFINE_F (BmOpenCvFlann, BuildRt)(benchmark::State& state) {
2172 int max_leaf_size = state.range (0 );
22- cvflann::Matrix<Scalar> matrix (
23- points_tree_.data ()->data , points_tree_.size (), 3 );
73+
2474 // Reorder will change the order of the input to fit the generated indices,
25- // but it will replace (delete) the original input.
26- cvflann::KDTreeSingleIndexParams pindex (max_leaf_size, false );
75+ // but it will replace (delete) the original input. It is set to true because
76+ // we use it to improve query times during the Knn test.
77+ // The tree takes ownership of the copied data.
78+ // NOTE: One could argue the copy is part of the benchmark, but didn't add it.
79+ fl::Matrix<Scalar> matrix (RawCopy (points_tree_), points_tree_.size (), 3 );
80+ fl::KDTreeSingleIndexParams pindex (max_leaf_size, true );
2781
2882 for (auto _ : state) {
29- cvflann ::KDTreeSingleIndex<cvflann::L2_Simple <Scalar>> tree (matrix, pindex);
83+ fl ::KDTreeSingleIndex<fl::L2_3D <Scalar>> tree (matrix, pindex);
3084 tree.buildIndex ();
3185 }
3286}
@@ -40,46 +94,41 @@ BENCHMARK_REGISTER_F(BmOpenCvFlann, BuildRt)
4094// Knn
4195// ****************************************************************************
4296
43- BENCHMARK_DEFINE_F (BmOpenCvFlann, KnnRt )(benchmark::State& state) {
97+ BENCHMARK_DEFINE_F (BmOpenCvFlann, KnnCt )(benchmark::State& state) {
4498 int max_leaf_size = state.range (0 );
4599 int knn_count = state.range (1 );
46100
47- cvflann::Matrix<Scalar> matrix (
48- points_tree_.data ()->data , points_tree_.size (), 3 );
49101 // Reorder will change the order of the input to fit the generated indices,
50- // but it will replace (and delete) the original input. Note that the reorder
51- // option does not appear to have effect on the performance of the queries.
52- cvflann::KDTreeSingleIndexParams pindex (max_leaf_size, false );
53- // It seems that there are different versions of FLANN. For example, the
54- // OpenCV version does not have the flann::L2_3D<Scalar> distance class, like
55- // found on the following GitHub respository:
56- // https://github.com/mariusmuja/flann/blob/master/src/cpp/flann/algorithms/dist.h
57- // However, using the exact same one or a custom metric where the dimensions
58- // are know at compile time does not appear to really impact the performance.
59- cvflann::KDTreeSingleIndex<cvflann::L2_Simple<Scalar>> tree (matrix, pindex);
102+ // but it will replace (and delete) the original input. The reorder option
103+ // does has a small positive effect on the performance of the queries.
104+ // The tree takes ownership of the copied data.
105+ fl::Matrix<Scalar> matrix (RawCopy (points_tree_), points_tree_.size (), 3 );
106+ fl::KDTreeSingleIndexParams pindex (max_leaf_size, true );
107+ // "Custom" L2_3D metric has a decent positive effect on query times.
108+ fl::KDTreeSingleIndex<fl::L2_3D<Scalar>> tree (matrix, pindex);
60109 tree.buildIndex ();
61110
62111 // Search all nodes, no approximate search and no sorting.
63- cvflann ::SearchParams psearch (-1 , 0 .0f , false );
112+ fl ::SearchParams psearch (-1 , 0 .0f , false );
64113
65114 // There is also the option to query them all at once, but this doesn't really
66115 // change performance and this version looks more like the other benchmarks.
67116 for (auto _ : state) {
68117 std::vector<Index> indices (knn_count);
69118 std::vector<Scalar> distances (knn_count);
70- cvflann ::Matrix<Index> mat_indices (indices.data (), 1 , knn_count);
71- cvflann ::Matrix<Scalar> mat_distances (distances.data (), 1 , knn_count);
119+ fl ::Matrix<Index> mat_indices (indices.data (), 1 , knn_count);
120+ fl ::Matrix<Scalar> mat_distances (distances.data (), 1 , knn_count);
72121
73122 for (auto & p : points_test_) {
74- cvflann ::Matrix<Scalar> query (p.data , 1 , 3 );
123+ fl ::Matrix<Scalar> query (p.data , 1 , 3 );
75124 tree.knnSearch (query, mat_indices, mat_distances, knn_count, psearch);
76125 }
77126 }
78127}
79128
80129// Argument 1: Maximum leaf size.
81130// Argument 2: K nearest neighbors.
82- BENCHMARK_REGISTER_F (BmOpenCvFlann, KnnRt )
131+ BENCHMARK_REGISTER_F (BmOpenCvFlann, KnnCt )
83132 ->Unit(benchmark::kMillisecond )
84133 ->Args({1 , 1 })
85134 ->Args({6 , 1 })
0 commit comments