From cd42e38013e5ed80ecaf5874ff83c889725ce99f Mon Sep 17 00:00:00 2001 From: ippei ito Date: Sat, 14 Mar 2015 04:38:07 +0900 Subject: [PATCH 01/10] FlannBasedMatcher(LshIndex) in the feature2d optimization for continuance additional train() Current implementation of miniflann is releasing the trained index, and rebuilding the index from the beginning. But, some indexing algorithms like the LSH are able to add the indexing data after that. This branch is implementation of that optimization for LshIndex FlannBasedMatcher in the feature2d. --- modules/features2d/src/matchers.cpp | 14 ++++- .../include/opencv2/flann/autotuned_index.h | 7 +++ .../include/opencv2/flann/composite_index.h | 7 +++ .../include/opencv2/flann/flann_base.hpp | 10 ++++ .../flann/hierarchical_clustering_index.h | 8 +++ .../include/opencv2/flann/kdtree_index.h | 7 +++ .../opencv2/flann/kdtree_single_index.h | 7 +++ .../include/opencv2/flann/kmeans_index.h | 7 +++ .../include/opencv2/flann/linear_index.h | 7 +++ .../flann/include/opencv2/flann/lsh_index.h | 18 +++++- .../flann/include/opencv2/flann/lsh_table.h | 4 +- .../flann/include/opencv2/flann/miniflann.hpp | 3 +- .../flann/include/opencv2/flann/nn_index.h | 5 ++ modules/flann/src/miniflann.cpp | 55 ++++++++++++------- 14 files changed, 133 insertions(+), 26 deletions(-) diff --git a/modules/features2d/src/matchers.cpp b/modules/features2d/src/matchers.cpp index 893ad7743a..f001ef4882 100644 --- a/modules/features2d/src/matchers.cpp +++ b/modules/features2d/src/matchers.cpp @@ -531,10 +531,20 @@ void FlannBasedMatcher::clear() void FlannBasedMatcher::train() { - if( flannIndex.empty() || mergedDescriptors.size() < addedDescCount ) + int trained = mergedDescriptors.size(); + if (flannIndex.empty() || trained < addedDescCount) { mergedDescriptors.set( trainDescCollection ); - flannIndex = new flann::Index( mergedDescriptors.getDescriptors(), *indexParams ); + + // construct flannIndex class, if empty or Algorithm not equal FLANN_INDEX_LSH + if (flannIndex.empty() || flannIndex->getAlgorithm() != cvflann::FLANN_INDEX_LSH) + { + flannIndex = new flann::Index(mergedDescriptors.getDescriptors(), *indexParams); + } + else + { + flannIndex->build(mergedDescriptors.getDescriptors(), mergedDescriptors.getDescriptors().rowRange(trained, mergedDescriptors.size()), *indexParams, cvflann::FLANN_DIST_HAMMING); + } } } diff --git a/modules/flann/include/opencv2/flann/autotuned_index.h b/modules/flann/include/opencv2/flann/autotuned_index.h index 2b9ca91a42..e82d053568 100644 --- a/modules/flann/include/opencv2/flann/autotuned_index.h +++ b/modules/flann/include/opencv2/flann/autotuned_index.h @@ -94,6 +94,13 @@ public: } } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * Method responsible with building the index. */ diff --git a/modules/flann/include/opencv2/flann/composite_index.h b/modules/flann/include/opencv2/flann/composite_index.h index 527ca1ad77..9372007467 100644 --- a/modules/flann/include/opencv2/flann/composite_index.h +++ b/modules/flann/include/opencv2/flann/composite_index.h @@ -130,6 +130,13 @@ public: return kmeans_index_->usedMemory() + kdtree_index_->usedMemory(); } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * \brief Builds the index */ diff --git a/modules/flann/include/opencv2/flann/flann_base.hpp b/modules/flann/include/opencv2/flann/flann_base.hpp index b5ba7d79e2..bb5b120099 100644 --- a/modules/flann/include/opencv2/flann/flann_base.hpp +++ b/modules/flann/include/opencv2/flann/flann_base.hpp @@ -124,6 +124,16 @@ public: delete nnIndex_; } + /** + * implementation for algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + if (!loaded_) { + nnIndex_->addIndex(wholeData, additionalData); + } + } + /** * Builds the index. */ diff --git a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h index 54cb8e55fc..dddd98ad52 100644 --- a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h +++ b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h @@ -378,6 +378,14 @@ public: return pool.usedMemory+pool.wastedMemory+memoryCounter; } + + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * Builds the index */ diff --git a/modules/flann/include/opencv2/flann/kdtree_index.h b/modules/flann/include/opencv2/flann/kdtree_index.h index dc0971c9ef..8c0024a4de 100644 --- a/modules/flann/include/opencv2/flann/kdtree_index.h +++ b/modules/flann/include/opencv2/flann/kdtree_index.h @@ -117,6 +117,13 @@ public: delete[] var_; } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * Builds the index */ diff --git a/modules/flann/include/opencv2/flann/kdtree_single_index.h b/modules/flann/include/opencv2/flann/kdtree_single_index.h index 30488ad567..2b507e16b7 100644 --- a/modules/flann/include/opencv2/flann/kdtree_single_index.h +++ b/modules/flann/include/opencv2/flann/kdtree_single_index.h @@ -110,6 +110,13 @@ public: if (reorder_) delete[] data_.data; } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * Builds the index */ diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index 8bcb63d72f..f95e2fcabd 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -362,6 +362,13 @@ public: return pool_.usedMemory+pool_.wastedMemory+memoryCounter_; } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + /** * Builds the index */ diff --git a/modules/flann/include/opencv2/flann/linear_index.h b/modules/flann/include/opencv2/flann/linear_index.h index 5aa7a5cfac..f54e3dee86 100644 --- a/modules/flann/include/opencv2/flann/linear_index.h +++ b/modules/flann/include/opencv2/flann/linear_index.h @@ -85,6 +85,13 @@ public: return 0; } + /** + * Dummy implementation for other algorithms of addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + } + void buildIndex() { /* nothing to do here for linear search */ diff --git a/modules/flann/include/opencv2/flann/lsh_index.h b/modules/flann/include/opencv2/flann/lsh_index.h index 23988d436a..5d00bea950 100644 --- a/modules/flann/include/opencv2/flann/lsh_index.h +++ b/modules/flann/include/opencv2/flann/lsh_index.h @@ -104,6 +104,20 @@ public: LshIndex(const LshIndex&); LshIndex& operator=(const LshIndex&); + /** + * Implementation for the LSH addable indexes after that. + */ + void addIndex(const Matrix& wholeData, const Matrix& additionalData) + { + tables_.resize(table_number_); + for (unsigned int i = 0; i < table_number_; ++i) { + lsh::LshTable& table = tables_[i]; + // Add the features to the table with indexed offset + table.add(wholeData.rows - additionalData.rows, additionalData); + } + dataset_ = wholeData; + } + /** * Builds the index */ @@ -126,8 +140,8 @@ public: lsh::LshTable& table = tables_[i]; table = lsh::LshTable(feature_size_, key_size_, indices); - // Add the features to the table - table.add(dataset_); + // Add the features to the table with offset 0 + table.add(0, dataset_); } } diff --git a/modules/flann/include/opencv2/flann/lsh_table.h b/modules/flann/include/opencv2/flann/lsh_table.h index 0defa52a77..bfc486dbc5 100644 --- a/modules/flann/include/opencv2/flann/lsh_table.h +++ b/modules/flann/include/opencv2/flann/lsh_table.h @@ -194,13 +194,13 @@ public: /** Add a set of features to the table * @param dataset the values to store */ - void add(Matrix dataset) + void add(int indexed_ofst, Matrix dataset) { #if USE_UNORDERED_MAP buckets_space_.rehash((buckets_space_.size() + dataset.rows) * 1.2); #endif // Add the features to the table - for (unsigned int i = 0; i < dataset.rows; ++i) add(i, dataset[i]); + for (unsigned int i = 0; i < dataset.rows; ++i) add(i + indexed_ofst, dataset[i]); // Now that the table is full, optimize it for speed/space optimize(); } diff --git a/modules/flann/include/opencv2/flann/miniflann.hpp b/modules/flann/include/opencv2/flann/miniflann.hpp index ada375691c..121f8d05c3 100644 --- a/modules/flann/include/opencv2/flann/miniflann.hpp +++ b/modules/flann/include/opencv2/flann/miniflann.hpp @@ -134,7 +134,8 @@ public: CV_WRAP Index(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2); virtual ~Index(); - CV_WRAP virtual void build(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2); + CV_WRAP virtual void build(InputArray wholefeatures, InputArray additionalfeatures, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2); + CV_WRAP virtual void knnSearch(InputArray query, OutputArray indices, OutputArray dists, int knn, const SearchParams& params=SearchParams()); diff --git a/modules/flann/include/opencv2/flann/nn_index.h b/modules/flann/include/opencv2/flann/nn_index.h index d14e83a92c..4a874f5867 100644 --- a/modules/flann/include/opencv2/flann/nn_index.h +++ b/modules/flann/include/opencv2/flann/nn_index.h @@ -59,6 +59,11 @@ public: */ virtual void buildIndex() = 0; + /** + * \brief implementation for algorithms of addable indexes after that. + */ + virtual void addIndex(const Matrix& wholeData, const Matrix& additionalData) = 0; + /** * \brief Perform k-nearest neighbor search * \param[in] queries The query points for which to find the nearest neighbors diff --git a/modules/flann/src/miniflann.cpp b/modules/flann/src/miniflann.cpp index 3747102728..a84f2408c9 100644 --- a/modules/flann/src/miniflann.cpp +++ b/modules/flann/src/miniflann.cpp @@ -308,7 +308,7 @@ SearchParams::SearchParams( int checks, float eps, bool sorted ) template void -buildIndex_(void*& index, const Mat& data, const IndexParams& params, const Distance& dist = Distance()) +buildIndex_(void*& index, const Mat& wholedata, const Mat& data, const IndexParams& params, const Distance& dist = Distance()) { typedef typename Distance::ElementType ElementType; if(DataType::type != data.type()) @@ -317,15 +317,25 @@ buildIndex_(void*& index, const Mat& data, const IndexParams& params, const Dist CV_Error(CV_StsBadArg, "Only continuous arrays are supported"); ::cvflann::Matrix dataset((ElementType*)data.data, data.rows, data.cols); - IndexType* _index = new IndexType(dataset, get_params(params), dist); - _index->buildIndex(); - index = _index; + + IndexType* _index = NULL; + if( !index || getParam(params, "algorithm", FLANN_INDEX_LINEAR) != FLANN_INDEX_LSH) // currently, additional index support is the lsh algorithm only. + { + _index = new IndexType(dataset, get_params(params), dist); + _index->buildIndex(); + index = _index; + } + else // build additional lsh index + { + ::cvflann::Matrix wholedataset((ElementType*)wholedata.data, wholedata.rows, wholedata.cols); + ((IndexType*)index)->addIndex(wholedataset, dataset); + } } template void -buildIndex(void*& index, const Mat& data, const IndexParams& params, const Distance& dist = Distance()) +buildIndex(void*& index, const Mat& wholedata, const Mat& data, const IndexParams& params, const Distance& dist = Distance()) { - buildIndex_ >(index, data, params, dist); + buildIndex_ >(index, wholedata, data, params, dist); } #if CV_NEON @@ -348,21 +358,28 @@ Index::Index(InputArray _data, const IndexParams& params, flann_distance_t _dist featureType = CV_32F; algo = FLANN_INDEX_LINEAR; distType = FLANN_DIST_L2; - build(_data, params, _distType); + build(_data, _data, params, _distType); } -void Index::build(InputArray _data, const IndexParams& params, flann_distance_t _distType) +void Index::build(InputArray &_wholedata, InputArray &_data, const IndexParams& params, flann_distance_t _distType) { - release(); algo = getParam(params, "algorithm", FLANN_INDEX_LINEAR); - if( algo == FLANN_INDEX_SAVED ) + + if (algo != FLANN_INDEX_LSH) // do not release if algo == FLANN_INDEX_LSH + { + release(); + } + if (algo == FLANN_INDEX_SAVED) { load(_data, getParam(params, "filename", std::string())); return; } Mat data = _data.getMat(); - index = 0; + if (algo != FLANN_INDEX_LSH) // do not clear if algo == FLANN_INDEX_LSH + { + index = 0; + } featureType = data.type(); distType = _distType; @@ -374,29 +391,29 @@ void Index::build(InputArray _data, const IndexParams& params, flann_distance_t switch( distType ) { case FLANN_DIST_HAMMING: - buildIndex< HammingDistance >(index, data, params); + buildIndex< HammingDistance >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_L2: - buildIndex< ::cvflann::L2 >(index, data, params); + buildIndex< ::cvflann::L2 >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_L1: - buildIndex< ::cvflann::L1 >(index, data, params); + buildIndex< ::cvflann::L1 >(index, _wholedata.getMat(), data, params); break; #if MINIFLANN_SUPPORT_EXOTIC_DISTANCE_TYPES case FLANN_DIST_MAX: - buildIndex< ::cvflann::MaxDistance >(index, data, params); + buildIndex< ::cvflann::MaxDistance >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_HIST_INTERSECT: - buildIndex< ::cvflann::HistIntersectionDistance >(index, data, params); + buildIndex< ::cvflann::HistIntersectionDistance >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_HELLINGER: - buildIndex< ::cvflann::HellingerDistance >(index, data, params); + buildIndex< ::cvflann::HellingerDistance >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_CHI_SQUARE: - buildIndex< ::cvflann::ChiSquareDistance >(index, data, params); + buildIndex< ::cvflann::ChiSquareDistance >(index, _wholedata.getMat(), data, params); break; case FLANN_DIST_KL: - buildIndex< ::cvflann::KL_Divergence >(index, data, params); + buildIndex< ::cvflann::KL_Divergence >(index, _wholedata.getMat(), data, params); break; #endif default: From 8efdfd9dfc8d58a869c4c291d1325a9c46a204c3 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Sat, 14 Mar 2015 06:07:55 +0900 Subject: [PATCH 02/10] Fix unused-parameter Warning --- modules/flann/include/opencv2/flann/autotuned_index.h | 2 +- modules/flann/include/opencv2/flann/composite_index.h | 2 +- .../flann/include/opencv2/flann/hierarchical_clustering_index.h | 2 +- modules/flann/include/opencv2/flann/kdtree_index.h | 2 +- modules/flann/include/opencv2/flann/kdtree_single_index.h | 2 +- modules/flann/include/opencv2/flann/kmeans_index.h | 2 +- modules/flann/include/opencv2/flann/linear_index.h | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/flann/include/opencv2/flann/autotuned_index.h b/modules/flann/include/opencv2/flann/autotuned_index.h index e82d053568..43a7bcd920 100644 --- a/modules/flann/include/opencv2/flann/autotuned_index.h +++ b/modules/flann/include/opencv2/flann/autotuned_index.h @@ -97,7 +97,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/composite_index.h b/modules/flann/include/opencv2/flann/composite_index.h index 9372007467..02b7bc1f31 100644 --- a/modules/flann/include/opencv2/flann/composite_index.h +++ b/modules/flann/include/opencv2/flann/composite_index.h @@ -133,7 +133,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h index dddd98ad52..59423ae0cd 100644 --- a/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h +++ b/modules/flann/include/opencv2/flann/hierarchical_clustering_index.h @@ -382,7 +382,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/kdtree_index.h b/modules/flann/include/opencv2/flann/kdtree_index.h index 8c0024a4de..1b8af4a597 100644 --- a/modules/flann/include/opencv2/flann/kdtree_index.h +++ b/modules/flann/include/opencv2/flann/kdtree_index.h @@ -120,7 +120,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/kdtree_single_index.h b/modules/flann/include/opencv2/flann/kdtree_single_index.h index 2b507e16b7..252fc4c5e1 100644 --- a/modules/flann/include/opencv2/flann/kdtree_single_index.h +++ b/modules/flann/include/opencv2/flann/kdtree_single_index.h @@ -113,7 +113,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/kmeans_index.h b/modules/flann/include/opencv2/flann/kmeans_index.h index f95e2fcabd..9c0f4e2df8 100644 --- a/modules/flann/include/opencv2/flann/kmeans_index.h +++ b/modules/flann/include/opencv2/flann/kmeans_index.h @@ -365,7 +365,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } diff --git a/modules/flann/include/opencv2/flann/linear_index.h b/modules/flann/include/opencv2/flann/linear_index.h index f54e3dee86..0ea084a5f5 100644 --- a/modules/flann/include/opencv2/flann/linear_index.h +++ b/modules/flann/include/opencv2/flann/linear_index.h @@ -88,7 +88,7 @@ public: /** * Dummy implementation for other algorithms of addable indexes after that. */ - void addIndex(const Matrix& wholeData, const Matrix& additionalData) + void addIndex(const Matrix& /*wholeData*/, const Matrix& /*additionalData*/) { } From f7d52c774e467e783eb646ff428bcb2611312594 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Sat, 14 Mar 2015 06:58:34 +0900 Subject: [PATCH 03/10] Fix compile error on linux change cv::InputArray& to cv::InputArray --- modules/flann/src/miniflann.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/flann/src/miniflann.cpp b/modules/flann/src/miniflann.cpp index a84f2408c9..5ce3e9051a 100644 --- a/modules/flann/src/miniflann.cpp +++ b/modules/flann/src/miniflann.cpp @@ -361,7 +361,7 @@ Index::Index(InputArray _data, const IndexParams& params, flann_distance_t _dist build(_data, _data, params, _distType); } -void Index::build(InputArray &_wholedata, InputArray &_data, const IndexParams& params, flann_distance_t _distType) +void Index::build(InputArray _wholedata, InputArray _data, const IndexParams& params, flann_distance_t _distType) { algo = getParam(params, "algorithm", FLANN_INDEX_LINEAR); From 7d665a47540d08e3c25bb51dd91d3c77c8aff9e2 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Sat, 14 Mar 2015 09:35:53 +0900 Subject: [PATCH 04/10] Fix warn conversion from 'size_t' to 'int'. add comment for doxygen --- modules/flann/include/opencv2/flann/lsh_index.h | 4 +++- modules/flann/include/opencv2/flann/lsh_table.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/flann/include/opencv2/flann/lsh_index.h b/modules/flann/include/opencv2/flann/lsh_index.h index 5d00bea950..2b89337d91 100644 --- a/modules/flann/include/opencv2/flann/lsh_index.h +++ b/modules/flann/include/opencv2/flann/lsh_index.h @@ -106,6 +106,8 @@ public: /** * Implementation for the LSH addable indexes after that. + * @param wholeData whole dataset with the input features + * @param additionalData additional dataset with the input features */ void addIndex(const Matrix& wholeData, const Matrix& additionalData) { @@ -113,7 +115,7 @@ public: for (unsigned int i = 0; i < table_number_; ++i) { lsh::LshTable& table = tables_[i]; // Add the features to the table with indexed offset - table.add(wholeData.rows - additionalData.rows, additionalData); + table.add((int)(wholeData.rows - additionalData.rows), additionalData); } dataset_ = wholeData; } diff --git a/modules/flann/include/opencv2/flann/lsh_table.h b/modules/flann/include/opencv2/flann/lsh_table.h index bfc486dbc5..cef01b2e1b 100644 --- a/modules/flann/include/opencv2/flann/lsh_table.h +++ b/modules/flann/include/opencv2/flann/lsh_table.h @@ -192,6 +192,7 @@ public: } /** Add a set of features to the table + * @param indexed_ofst previous indexed offset * @param dataset the values to store */ void add(int indexed_ofst, Matrix dataset) From 7c740b6a3196cbac385e226ae6d3b413d461fda3 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Wed, 25 Mar 2015 00:08:47 +0900 Subject: [PATCH 05/10] Test accuracy for Pull Request # 3829 --- .../test/test_lshindex_flannbased_matcher.cpp | 523 ++++++++++++++++++ modules/features2d/test/test_precomp.hpp | 1 + modules/ts/CMakeLists.txt | 2 +- 3 files changed, 525 insertions(+), 1 deletion(-) create mode 100644 modules/features2d/test/test_lshindex_flannbased_matcher.cpp diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp new file mode 100644 index 0000000000..dd83a24392 --- /dev/null +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -0,0 +1,523 @@ +/* +/********************************************************************* +* Software License Agreement (BSD License) +* +* Copyright (c) 2015, Willow Garage, Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following +* disclaimer in the documentation and/or other materials provided +* with the distribution. +* * Neither the name of the Willow Garage nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*********************************************************************/ + +/* + Authors: Ippei Ito + + for OpenCV2.4/OpenCV3.0 + + Test for Pull Request # 3829 + https://github.com/Itseez/opencv/pull/3829 + + This test code creates brute force matcher for accuracy of reference, and the test target matcher. + Then, add() and train() transformed query image descriptors, and some outlier images descriptors to both matchers. + Then, compared with the query image by match() and findHomography() to detect outlier and calculate accuracy. + And each drawMatches() images are saved, if SAVE_DRAW_MATCHES_IMAGES is true. + Finally, compare accuracies between the brute force matcher and the test target matcher. + + The lsh algorithm uses std::random_shuffle in lsh_index.h to make the random indexes table. + So, in relation to default random seed value of the execution environment or by using "srand(time(0)) function", + the match time and accuracy of the match results are different, each time the code ran. + And the match time becomes late in relation to the number of the hash collision times. +*/ + +#include "test_precomp.hpp" +#include "opencv2/ts.hpp" // for FilePath::CreateFolder() +#include // for time() + +// If defined, the match time and accuracy of the match results are a little different, each time the code ran. +#define INIT_RANDOM_SEED + +// If defined, some outlier images descriptors add() the matcher. +#define TRAIN_WITH_OUTLIER_IMAGES + +// If true, save drawMatches() images. +#define SAVE_DRAW_MATCHES_IMAGES false + +// if true, verbose output +#define SHOW_DEBUG_LOG true + +#if CV_MAJOR_VERSION==2 +#define OrbCreate new ORB(4000) +#elif CV_MAJOR_VERSION==3 +#define OrbCreate ORB::create(4000) +#define AKazeCreate AKAZE::create() +#endif + +using namespace cv; +using namespace std; + +int testno_for_make_filename = 0; + +// -------------------------------------------------------------------------------------- +// Parameter class to transform query image +// -------------------------------------------------------------------------------------- +class testparam +{ +public: + string transname; + void(*transfunc)(float, const Mat&, Mat&); + float from, to, step; + testparam(string _transname, void(*_transfunc)(float, const Mat&, Mat&), float _from, float _to, float _step) : + transname(_transname), + transfunc(_transfunc), + from(_from), + to(_to), + step(_step) + {} +}; + +// -------------------------------------------------------------------------------------- +// from matching_to_many_images.cpp +// -------------------------------------------------------------------------------------- +int maskMatchesByTrainImgIdx(const vector& matches, int trainImgIdx, vector& mask) +{ + int matchcnt = 0; + mask.resize(matches.size()); + fill(mask.begin(), mask.end(), 0); + for (size_t i = 0; i < matches.size(); i++) + { + if (matches[i].imgIdx == trainImgIdx) + { + mask[i] = 1; + matchcnt++; + } + } + return matchcnt; +} + +int calcHomographyAndInlierCount(const vector& query_kp, const vector& train_kp, const vector& match, vector &mask, Mat &homography) +{ + // make query and current train image keypoint pairs + std::vector srcPoints, dstPoints; + for (unsigned int i = 0; i < match.size(); ++i) + { + if (mask[i] != 0) // is current train image ? + { + srcPoints.push_back(query_kp[match[i].queryIdx].pt); + dstPoints.push_back(train_kp[match[i].trainIdx].pt); + } + } + // calc homography + vector inlierMask; + homography = findHomography(srcPoints, dstPoints, RANSAC, 3.0, inlierMask); + + // update outlier mask + int j = 0; + for (unsigned int i = 0; i < match.size(); ++i) + { + if (mask[i] != 0) // is current train image ? + { + if (inlierMask.size() == 0 || inlierMask[j] == 0) // is outlier ? + { + mask[i] = 0; + } + j++; + } + } + + // count inlier + int inlierCnt = 0; + for (unsigned int i = 0; i < mask.size(); ++i) + { + if (mask[i] != 0) + { + inlierCnt++; + } + } + return inlierCnt; +} + +void drawDetectedRectangle(Mat& imgResult, const Mat& homography, const Mat& imgQuery) +{ + std::vector query_corners(4); + query_corners[0] = Point(0, 0); + query_corners[1] = Point(imgQuery.cols, 0); + query_corners[2] = Point(imgQuery.cols, imgQuery.rows); + query_corners[3] = Point(0, imgQuery.rows); + std::vector train_corners(4); + perspectiveTransform(query_corners, train_corners, homography); + line(imgResult, train_corners[0] + query_corners[1], train_corners[1] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[1] + query_corners[1], train_corners[2] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[2] + query_corners[1], train_corners[3] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[3] + query_corners[1], train_corners[0] + query_corners[1], Scalar(0, 255, 0), 4); +} + +// -------------------------------------------------------------------------------------- +// transform query image, extract&compute, train, matching and save result image function +// -------------------------------------------------------------------------------------- +typedef struct tagTrainInfo +{ + int traindesccnt; + double traintime; + double matchtime; + double accuracy; +}TrainInfo; + +TrainInfo transImgAndTrain( + Feature2D *fe, + DescriptorMatcher *matcher, + const string &matchername, + const Mat& imgQuery, const vector& query_kp, const Mat& query_desc, + const vector& imgOutliers, const vector >& outliers_kp, const vector& outliers_desc, const int totalOutlierDescCnt, + const float t, const testparam &tp, + const int testno, const bool bVerboseOutput, const bool bSaveDrawMatches) +{ + TrainInfo ti; + + // transform query image + Mat imgTransform; + (tp.transfunc)(t, imgQuery, imgTransform); + + // extract kp and compute desc from transformed query image + vector trans_query_kp; + Mat trans_query_desc; +#if CV_MAJOR_VERSION==2 + (*fe)(imgTransform, Mat(), trans_query_kp, trans_query_desc); +#elif CV_MAJOR_VERSION==3 + fe->detectAndCompute(imgTransform, Mat(), trans_query_kp, trans_query_desc); +#endif + // add&train transformed query desc and outlier desc + matcher->clear(); + matcher->add(vector(1, trans_query_desc)); + double s = (double)getTickCount(); + matcher->train(); + ti.traintime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + ti.traindesccnt = trans_query_desc.rows; +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + // same as matcher->add(outliers_desc); matcher->train(); + for (unsigned int i = 0; i < outliers_desc.size(); ++i) + { + matcher->add(vector(1, outliers_desc[i])); + s = (double)getTickCount(); + matcher->train(); + ti.traintime += 1000.0*((double)getTickCount() - s) / getTickFrequency(); + } + ti.traindesccnt += totalOutlierDescCnt; +#endif + // matching + vector match; + s = (double)getTickCount(); + matcher->match(query_desc, match); + ti.matchtime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + + // prepare a directory and variables for save matching images + vector mask; + Mat imgResult; + const char resultDir[] = "result"; + if (bSaveDrawMatches) + { + testing::internal::FilePath fp = testing::internal::FilePath(resultDir); + fp.CreateFolder(); + } + + char buff[2048]; + int matchcnt; + + // save query vs transformed query matching image with detected rectangle + matchcnt = maskMatchesByTrainImgIdx(match, (int)0, mask); + // calc homography and inlier + Mat homography; + int inlierCnt = calcHomographyAndInlierCount(query_kp, trans_query_kp, match, mask, homography); + ti.accuracy = (double)inlierCnt / (double)mask.size()*100.0; + drawMatches(imgQuery, query_kp, imgTransform, trans_query_kp, match, imgResult, Scalar::all(-1), Scalar::all(128), mask, DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + if (inlierCnt) + { + // draw detected rectangle + drawDetectedRectangle(imgResult, homography, imgQuery); + } + // draw status + sprintf(buff, "%s accuracy:%-3.2f%% %d descriptors training time:%-3.2fms matching :%-3.2fms", matchername.c_str(), ti.accuracy, ti.traindesccnt, ti.traintime, ti.matchtime); + putText(imgResult, buff, Point(0, 12), FONT_HERSHEY_PLAIN, 0.8, Scalar(0., 0., 255.)); + sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp.transname.c_str(), t); + if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; + +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + // save query vs outlier matching image(s) + for (unsigned int i = 0; i bfmatcher; // brute force matcher for accuracy of reference + Ptr flmatcher; // flann matcher to test + Ptr fe; // feature detector extractor + Mat imgQuery; // query image + vector imgOutliers; // outlier image + vector query_kp; // query key points detect from imgQuery + Mat query_desc; // query descriptors extract from imgQuery + vector > outliers_kp; + vector outliers_desc; + int totalOutlierDescCnt; + + string flmatchername; + testparam tp; + double target_accuracy_margin_from_bfmatcher; + +public: + + // + // constructor + // + CV_FeatureDetectorMatcherBaseTest(testparam _tp, double _accuracy_margin, Ptr _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : + tp(_tp), + fe(_fe), + flmatcher(_flmatcher), + flmatchername(_flmatchername), + target_accuracy_margin_from_bfmatcher(_accuracy_margin) + { +#if defined(INIT_RANDOM_SEED) + // from test/test_eigen.cpp + srand((unsigned int)time(0)); +#endif + // create brute force matcher for accuracy of reference + bfmatcher = makePtr(norm_type_for_bfmatcher); + } + + // + // Main Test method + // + virtual void run(int) + { + // load query image + string strQueryFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png"; + imgQuery = imread(strQueryFile, 0); + if (imgQuery.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strQueryFile.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + // load outlier images + char* outliers[] = { (char*)"baboon.png", (char*)"fruits.png", (char*)"airplane.png" }; + for (unsigned int i = 0; i < sizeof(outliers) / sizeof(char*); i++) + { + string strOutlierFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/" + outliers[i]; + Mat imgOutlier = imread(strOutlierFile, 0); + if (imgQuery.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strOutlierFile.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + imgOutliers.push_back(imgOutlier); + } + + // extract and compute keypoints and descriptors from query image +#if CV_MAJOR_VERSION==2 + (*fe)(imgQuery, Mat(), query_kp, query_desc); +#elif CV_MAJOR_VERSION==3 + fe->detectAndCompute(imgQuery, Mat(), query_kp, query_desc); +#endif + // extract and compute keypoints and descriptors from outlier images + fe->detect(imgOutliers, outliers_kp); + ((DescriptorExtractor*)fe)->compute(imgOutliers, outliers_kp, outliers_desc); + totalOutlierDescCnt = 0; + for (unsigned int i = 0; i < outliers_desc.size(); ++i) totalOutlierDescCnt += outliers_desc[i].rows; + + if (SHOW_DEBUG_LOG) + { + cout << query_kp.size() << " keypoints extracted from query image." << endl; +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + cout << totalOutlierDescCnt << " keypoints extracted from outlier image(s)." << endl; +#endif + } + // compute brute force matcher accuracy for reference + double totalTrainTime = 0.; + double totalMatchTime = 0.; + double totalAccuracy = 0.; + int cnt = 0; + for (float t = tp.from; t <= tp.to; t += tp.step, ++testno_for_make_filename, ++cnt) + { + if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " BFMatcher " << t; + + TrainInfo ti = transImgAndTrain(fe, bfmatcher, "BFMatcher", + imgQuery, query_kp, query_desc, + imgOutliers, outliers_kp, outliers_desc, + totalOutlierDescCnt, + t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES); + totalTrainTime += ti.traintime; + totalMatchTime += ti.matchtime; + totalAccuracy += ti.accuracy; + } + double bf_average_accuracy = totalAccuracy / cnt; + if (SHOW_DEBUG_LOG) + { + cout << "total training time: " << totalTrainTime << "ms" << endl; + cout << "total matching time: " << totalMatchTime << "ms" << endl; + cout << "average accuracy:" << bf_average_accuracy << "%" << endl; + } + + // test the target matcher + totalTrainTime = 0.; + totalMatchTime = 0.; + totalAccuracy = 0.; + cnt = 0; + for (float t = tp.from; t <= tp.to; t += tp.step, ++testno_for_make_filename, ++cnt) + { + if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " " << flmatchername << " " << t; + + TrainInfo ti = transImgAndTrain(fe, flmatcher, flmatchername, + imgQuery, query_kp, query_desc, + imgOutliers, outliers_kp, outliers_desc, + totalOutlierDescCnt, + t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES); + + totalTrainTime += ti.traintime; + totalMatchTime += ti.matchtime; + totalAccuracy += ti.accuracy; + } + double average_accuracy = totalAccuracy / cnt; + double target_average_accuracy = bf_average_accuracy * target_accuracy_margin_from_bfmatcher; + + if (SHOW_DEBUG_LOG) + { + cout << "total training time: " << totalTrainTime << "ms" << endl; + cout << "total matching time: " << totalMatchTime << "ms" << endl; + cout << "average accuracy:" << average_accuracy << "%" << endl; + cout << "threshold of the target matcher average accuracy as error :" << target_average_accuracy << "%" << endl; + cout << "accuracy degraded " << (100.0 - (average_accuracy / bf_average_accuracy *100.0)) << "% from BFMatcher.(lower percentage is better)" << endl; + } + // compare accuracies between the brute force matcher and the test target matcher + if (average_accuracy < target_average_accuracy) + { + ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername, tp.transname.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + } + return; + } + +}; + +// -------------------------------------------------------------------------------------- +// Transform Functions +// -------------------------------------------------------------------------------------- +static void rotate(float deg, const Mat& src, Mat& dst) +{ + warpAffine(src, dst, getRotationMatrix2D(Point2f(src.cols / 2.0f, src.rows / 2.0f), deg, 1), src.size(), INTER_CUBIC); +} +static void scale(float scale, const Mat& src, Mat& dst) +{ + resize(src, dst, Size((int)(src.cols*scale), (int)(src.rows*scale)), INTER_AREA); +} +static void blur(float k, const Mat& src, Mat& dst) +{ + GaussianBlur(src, dst, Size((int)k, (int)k), 0); +} + +// -------------------------------------------------------------------------------------- +// Tests Registrations +// -------------------------------------------------------------------------------------- +#define SHORT_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 10..24% measured.lower percentage is better.) for lsh key size=16. +#define MIDDLE_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 7..24% measured.lower percentage is better.) for lsh key size=24. +#define LONG_LSH_KEY_ACCURACY_MARGIN 0.90 // The margin for FlannBasedMatcher. 10% degraded from BFMatcher(Actually, about -29...7% measured.lower percentage is better.) for lsh key size=31. + +TEST(BlurredQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(BlurredQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(BlurredQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} + +TEST(ScaledQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(ScaledQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(ScaledQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} + +TEST(RotatedQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(RotatedQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(RotatedQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} diff --git a/modules/features2d/test/test_precomp.hpp b/modules/features2d/test/test_precomp.hpp index bc2d8be6e9..b0fb9a593e 100644 --- a/modules/features2d/test/test_precomp.hpp +++ b/modules/features2d/test/test_precomp.hpp @@ -14,6 +14,7 @@ #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" +#include "opencv2/calib3d/calib3d.hpp" #include #endif diff --git a/modules/ts/CMakeLists.txt b/modules/ts/CMakeLists.txt index bf7da104de..4d4bebda80 100644 --- a/modules/ts/CMakeLists.txt +++ b/modules/ts/CMakeLists.txt @@ -9,7 +9,7 @@ set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) -ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video) +ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video opencv_calib3d) ocv_glob_module_sources() ocv_module_include_directories() From 56cae1b293ac05bc898c236330593e7946c1f654 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Wed, 25 Mar 2015 03:11:44 +0900 Subject: [PATCH 06/10] Test accuracy for PR#3829.Fix Some Error, Modify SLA. --- .../test/test_lshindex_flannbased_matcher.cpp | 68 ++++++++----------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp index dd83a24392..64616b2e5a 100644 --- a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -1,42 +1,34 @@ -/* -/********************************************************************* -* Software License Agreement (BSD License) -* -* Copyright (c) 2015, Willow Garage, Inc. -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above -* copyright notice, this list of conditions and the following -* disclaimer in the documentation and/or other materials provided -* with the distribution. -* * Neither the name of the Willow Garage nor the names of its -* contributors may be used to endorse or promote products derived -* from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -*********************************************************************/ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright (c) 2015 Ippei Ito. All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ /* - Authors: Ippei Ito - - for OpenCV2.4/OpenCV3.0 + For OpenCV2.4/OpenCV3.0 Test for Pull Request # 3829 https://github.com/Itseez/opencv/pull/3829 @@ -434,7 +426,7 @@ public: // compare accuracies between the brute force matcher and the test target matcher if (average_accuracy < target_average_accuracy) { - ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername, tp.transname.c_str()); + ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername.c_str(), tp.transname.c_str()); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); } return; From d1902a12760ff083cd64fda7c780228a224f0e8b Mon Sep 17 00:00:00 2001 From: ippei ito Date: Wed, 25 Mar 2015 05:16:30 +0900 Subject: [PATCH 07/10] Test accuracy for PR#3829.Fix warning on gcc, comment out INIT_RANDOM_SEED for same result each ran. --- .../test/test_lshindex_flannbased_matcher.cpp | 66 ++++++++++++------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp index 64616b2e5a..235951349e 100644 --- a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -50,7 +50,7 @@ #include // for time() // If defined, the match time and accuracy of the match results are a little different, each time the code ran. -#define INIT_RANDOM_SEED +//#define INIT_RANDOM_SEED // If defined, some outlier images descriptors add() the matcher. #define TRAIN_WITH_OUTLIER_IMAGES @@ -184,14 +184,14 @@ TrainInfo transImgAndTrain( const string &matchername, const Mat& imgQuery, const vector& query_kp, const Mat& query_desc, const vector& imgOutliers, const vector >& outliers_kp, const vector& outliers_desc, const int totalOutlierDescCnt, - const float t, const testparam &tp, + const float t, const testparam *tp, const int testno, const bool bVerboseOutput, const bool bSaveDrawMatches) { TrainInfo ti; // transform query image Mat imgTransform; - (tp.transfunc)(t, imgQuery, imgTransform); + (tp->transfunc)(t, imgQuery, imgTransform); // extract kp and compute desc from transformed query image vector trans_query_kp; @@ -253,7 +253,7 @@ TrainInfo transImgAndTrain( // draw status sprintf(buff, "%s accuracy:%-3.2f%% %d descriptors training time:%-3.2fms matching :%-3.2fms", matchername.c_str(), ti.accuracy, ti.traindesccnt, ti.traintime, ti.matchtime); putText(imgResult, buff, Point(0, 12), FONT_HERSHEY_PLAIN, 0.8, Scalar(0., 0., 255.)); - sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp.transname.c_str(), t); + sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t); if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; #if defined(TRAIN_WITH_OUTLIER_IMAGES) @@ -264,13 +264,13 @@ TrainInfo transImgAndTrain( drawMatches(imgQuery, query_kp, imgOutliers[i], outliers_kp[i], match, imgResult, Scalar::all(-1), Scalar::all(128), mask);// , DrawMatchesFlags::DRAW_RICH_KEYPOINTS); sprintf(buff, "query_num:%d train_num:%d matched:%d %d descriptors training time:%-3.2fms matching :%-3.2fms", (int)query_kp.size(), (int)outliers_kp[i].size(), matchcnt, ti.traindesccnt, ti.traintime, ti.matchtime); putText(imgResult, buff, Point(0, 12), FONT_HERSHEY_PLAIN, 0.8, Scalar(0., 0., 255.)); - sprintf(buff, "%s/res%03d_%s_%s%.1f_outlier%02d.png", resultDir, testno, matchername.c_str(), tp.transname.c_str(), t, i); + sprintf(buff, "%s/res%03d_%s_%s%.1f_outlier%02d.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t, i); if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; } #endif if (bVerboseOutput) { - cout << tp.transname <<" image matching accuracy:" << ti.accuracy << "% " << ti.traindesccnt << " train:" << ti.traintime << "ms match:" << ti.matchtime << "ms" << endl; + cout << tp->transname <<" image matching accuracy:" << ti.accuracy << "% " << ti.traindesccnt << " train:" << ti.traintime << "ms match:" << ti.matchtime << "ms" << endl; } return ti; @@ -283,9 +283,9 @@ class CV_FeatureDetectorMatcherBaseTest : public cvtest::BaseTest { private: - Ptr bfmatcher; // brute force matcher for accuracy of reference - Ptr flmatcher; // flann matcher to test - Ptr fe; // feature detector extractor + DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference + DescriptorMatcher* flmatcher; // flann matcher to test + Feature2D* fe; // feature detector extractor Mat imgQuery; // query image vector imgOutliers; // outlier image vector query_kp; // query key points detect from imgQuery @@ -295,7 +295,7 @@ private: int totalOutlierDescCnt; string flmatchername; - testparam tp; + testparam *tp; double target_accuracy_margin_from_bfmatcher; public: @@ -303,7 +303,7 @@ public: // // constructor // - CV_FeatureDetectorMatcherBaseTest(testparam _tp, double _accuracy_margin, Ptr _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : + CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, Feature2D* _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : tp(_tp), fe(_fe), flmatcher(_flmatcher), @@ -315,7 +315,7 @@ public: srand((unsigned int)time(0)); #endif // create brute force matcher for accuracy of reference - bfmatcher = makePtr(norm_type_for_bfmatcher); + bfmatcher = new BFMatcher(norm_type_for_bfmatcher); } // @@ -372,7 +372,7 @@ public: double totalMatchTime = 0.; double totalAccuracy = 0.; int cnt = 0; - for (float t = tp.from; t <= tp.to; t += tp.step, ++testno_for_make_filename, ++cnt) + for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt) { if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " BFMatcher " << t; @@ -398,7 +398,7 @@ public: totalMatchTime = 0.; totalAccuracy = 0.; cnt = 0; - for (float t = tp.from; t <= tp.to; t += tp.step, ++testno_for_make_filename, ++cnt) + for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt) { if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " " << flmatchername << " " << t; @@ -426,7 +426,7 @@ public: // compare accuracies between the brute force matcher and the test target matcher if (average_accuracy < target_average_accuracy) { - ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername.c_str(), tp.transname.c_str()); + ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername.c_str(), tp->transname.c_str()); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); } return; @@ -459,57 +459,75 @@ static void blur(float k, const Mat& src, Mat& dst) TEST(BlurredQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); test.safe_run(); } TEST(BlurredQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); test.safe_run(); } TEST(BlurredQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, SHORT_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 16, 2)), "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 24, 2)), "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(tp, LONG_LSH_KEY_ACCURACY_MARGIN, OrbCreate, new FlannBasedMatcher(makePtr(1, 31, 2)), "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); test.safe_run(); } From 65b0ecfec5815c1d98083d74cabb8964404dee60 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Wed, 25 Mar 2015 18:24:52 +0900 Subject: [PATCH 08/10] Test accuracy for PR#3829.Fix WOrder warning on gcc --- .../test/test_lshindex_flannbased_matcher.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp index 235951349e..51e2175708 100644 --- a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -283,9 +283,12 @@ class CV_FeatureDetectorMatcherBaseTest : public cvtest::BaseTest { private: + testparam *tp; + double target_accuracy_margin_from_bfmatcher; + Feature2D* fe; // feature detector extractor + DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference DescriptorMatcher* flmatcher; // flann matcher to test - Feature2D* fe; // feature detector extractor Mat imgQuery; // query image vector imgOutliers; // outlier image vector query_kp; // query key points detect from imgQuery @@ -295,8 +298,6 @@ private: int totalOutlierDescCnt; string flmatchername; - testparam *tp; - double target_accuracy_margin_from_bfmatcher; public: @@ -305,10 +306,10 @@ public: // CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, Feature2D* _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : tp(_tp), + target_accuracy_margin_from_bfmatcher(_accuracy_margin), fe(_fe), flmatcher(_flmatcher), - flmatchername(_flmatchername), - target_accuracy_margin_from_bfmatcher(_accuracy_margin) + flmatchername(_flmatchername) { #if defined(INIT_RANDOM_SEED) // from test/test_eigen.cpp From b7276359e9224bae01932180819190d9833eba72 Mon Sep 17 00:00:00 2001 From: ippei ito Date: Wed, 25 Mar 2015 00:08:47 +0900 Subject: [PATCH 09/10] Test accuracy for PR#3829 --- .../test/test_lshindex_flannbased_matcher.cpp | 534 ++++++++++++++++++ modules/features2d/test/test_precomp.hpp | 1 + modules/ts/CMakeLists.txt | 2 +- 3 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 modules/features2d/test/test_lshindex_flannbased_matcher.cpp diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp new file mode 100644 index 0000000000..51e2175708 --- /dev/null +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -0,0 +1,534 @@ +/*********************************************************************** + * Software License Agreement (BSD License) + * + * Copyright (c) 2015 Ippei Ito. All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *************************************************************************/ + +/* + For OpenCV2.4/OpenCV3.0 + + Test for Pull Request # 3829 + https://github.com/Itseez/opencv/pull/3829 + + This test code creates brute force matcher for accuracy of reference, and the test target matcher. + Then, add() and train() transformed query image descriptors, and some outlier images descriptors to both matchers. + Then, compared with the query image by match() and findHomography() to detect outlier and calculate accuracy. + And each drawMatches() images are saved, if SAVE_DRAW_MATCHES_IMAGES is true. + Finally, compare accuracies between the brute force matcher and the test target matcher. + + The lsh algorithm uses std::random_shuffle in lsh_index.h to make the random indexes table. + So, in relation to default random seed value of the execution environment or by using "srand(time(0)) function", + the match time and accuracy of the match results are different, each time the code ran. + And the match time becomes late in relation to the number of the hash collision times. +*/ + +#include "test_precomp.hpp" +#include "opencv2/ts.hpp" // for FilePath::CreateFolder() +#include // for time() + +// If defined, the match time and accuracy of the match results are a little different, each time the code ran. +//#define INIT_RANDOM_SEED + +// If defined, some outlier images descriptors add() the matcher. +#define TRAIN_WITH_OUTLIER_IMAGES + +// If true, save drawMatches() images. +#define SAVE_DRAW_MATCHES_IMAGES false + +// if true, verbose output +#define SHOW_DEBUG_LOG true + +#if CV_MAJOR_VERSION==2 +#define OrbCreate new ORB(4000) +#elif CV_MAJOR_VERSION==3 +#define OrbCreate ORB::create(4000) +#define AKazeCreate AKAZE::create() +#endif + +using namespace cv; +using namespace std; + +int testno_for_make_filename = 0; + +// -------------------------------------------------------------------------------------- +// Parameter class to transform query image +// -------------------------------------------------------------------------------------- +class testparam +{ +public: + string transname; + void(*transfunc)(float, const Mat&, Mat&); + float from, to, step; + testparam(string _transname, void(*_transfunc)(float, const Mat&, Mat&), float _from, float _to, float _step) : + transname(_transname), + transfunc(_transfunc), + from(_from), + to(_to), + step(_step) + {} +}; + +// -------------------------------------------------------------------------------------- +// from matching_to_many_images.cpp +// -------------------------------------------------------------------------------------- +int maskMatchesByTrainImgIdx(const vector& matches, int trainImgIdx, vector& mask) +{ + int matchcnt = 0; + mask.resize(matches.size()); + fill(mask.begin(), mask.end(), 0); + for (size_t i = 0; i < matches.size(); i++) + { + if (matches[i].imgIdx == trainImgIdx) + { + mask[i] = 1; + matchcnt++; + } + } + return matchcnt; +} + +int calcHomographyAndInlierCount(const vector& query_kp, const vector& train_kp, const vector& match, vector &mask, Mat &homography) +{ + // make query and current train image keypoint pairs + std::vector srcPoints, dstPoints; + for (unsigned int i = 0; i < match.size(); ++i) + { + if (mask[i] != 0) // is current train image ? + { + srcPoints.push_back(query_kp[match[i].queryIdx].pt); + dstPoints.push_back(train_kp[match[i].trainIdx].pt); + } + } + // calc homography + vector inlierMask; + homography = findHomography(srcPoints, dstPoints, RANSAC, 3.0, inlierMask); + + // update outlier mask + int j = 0; + for (unsigned int i = 0; i < match.size(); ++i) + { + if (mask[i] != 0) // is current train image ? + { + if (inlierMask.size() == 0 || inlierMask[j] == 0) // is outlier ? + { + mask[i] = 0; + } + j++; + } + } + + // count inlier + int inlierCnt = 0; + for (unsigned int i = 0; i < mask.size(); ++i) + { + if (mask[i] != 0) + { + inlierCnt++; + } + } + return inlierCnt; +} + +void drawDetectedRectangle(Mat& imgResult, const Mat& homography, const Mat& imgQuery) +{ + std::vector query_corners(4); + query_corners[0] = Point(0, 0); + query_corners[1] = Point(imgQuery.cols, 0); + query_corners[2] = Point(imgQuery.cols, imgQuery.rows); + query_corners[3] = Point(0, imgQuery.rows); + std::vector train_corners(4); + perspectiveTransform(query_corners, train_corners, homography); + line(imgResult, train_corners[0] + query_corners[1], train_corners[1] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[1] + query_corners[1], train_corners[2] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[2] + query_corners[1], train_corners[3] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[3] + query_corners[1], train_corners[0] + query_corners[1], Scalar(0, 255, 0), 4); +} + +// -------------------------------------------------------------------------------------- +// transform query image, extract&compute, train, matching and save result image function +// -------------------------------------------------------------------------------------- +typedef struct tagTrainInfo +{ + int traindesccnt; + double traintime; + double matchtime; + double accuracy; +}TrainInfo; + +TrainInfo transImgAndTrain( + Feature2D *fe, + DescriptorMatcher *matcher, + const string &matchername, + const Mat& imgQuery, const vector& query_kp, const Mat& query_desc, + const vector& imgOutliers, const vector >& outliers_kp, const vector& outliers_desc, const int totalOutlierDescCnt, + const float t, const testparam *tp, + const int testno, const bool bVerboseOutput, const bool bSaveDrawMatches) +{ + TrainInfo ti; + + // transform query image + Mat imgTransform; + (tp->transfunc)(t, imgQuery, imgTransform); + + // extract kp and compute desc from transformed query image + vector trans_query_kp; + Mat trans_query_desc; +#if CV_MAJOR_VERSION==2 + (*fe)(imgTransform, Mat(), trans_query_kp, trans_query_desc); +#elif CV_MAJOR_VERSION==3 + fe->detectAndCompute(imgTransform, Mat(), trans_query_kp, trans_query_desc); +#endif + // add&train transformed query desc and outlier desc + matcher->clear(); + matcher->add(vector(1, trans_query_desc)); + double s = (double)getTickCount(); + matcher->train(); + ti.traintime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + ti.traindesccnt = trans_query_desc.rows; +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + // same as matcher->add(outliers_desc); matcher->train(); + for (unsigned int i = 0; i < outliers_desc.size(); ++i) + { + matcher->add(vector(1, outliers_desc[i])); + s = (double)getTickCount(); + matcher->train(); + ti.traintime += 1000.0*((double)getTickCount() - s) / getTickFrequency(); + } + ti.traindesccnt += totalOutlierDescCnt; +#endif + // matching + vector match; + s = (double)getTickCount(); + matcher->match(query_desc, match); + ti.matchtime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + + // prepare a directory and variables for save matching images + vector mask; + Mat imgResult; + const char resultDir[] = "result"; + if (bSaveDrawMatches) + { + testing::internal::FilePath fp = testing::internal::FilePath(resultDir); + fp.CreateFolder(); + } + + char buff[2048]; + int matchcnt; + + // save query vs transformed query matching image with detected rectangle + matchcnt = maskMatchesByTrainImgIdx(match, (int)0, mask); + // calc homography and inlier + Mat homography; + int inlierCnt = calcHomographyAndInlierCount(query_kp, trans_query_kp, match, mask, homography); + ti.accuracy = (double)inlierCnt / (double)mask.size()*100.0; + drawMatches(imgQuery, query_kp, imgTransform, trans_query_kp, match, imgResult, Scalar::all(-1), Scalar::all(128), mask, DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + if (inlierCnt) + { + // draw detected rectangle + drawDetectedRectangle(imgResult, homography, imgQuery); + } + // draw status + sprintf(buff, "%s accuracy:%-3.2f%% %d descriptors training time:%-3.2fms matching :%-3.2fms", matchername.c_str(), ti.accuracy, ti.traindesccnt, ti.traintime, ti.matchtime); + putText(imgResult, buff, Point(0, 12), FONT_HERSHEY_PLAIN, 0.8, Scalar(0., 0., 255.)); + sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t); + if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; + +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + // save query vs outlier matching image(s) + for (unsigned int i = 0; i transname.c_str(), t, i); + if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; + } +#endif + if (bVerboseOutput) + { + cout << tp->transname <<" image matching accuracy:" << ti.accuracy << "% " << ti.traindesccnt << " train:" << ti.traintime << "ms match:" << ti.matchtime << "ms" << endl; + } + + return ti; +} + +// -------------------------------------------------------------------------------------- +// Main Test Class +// -------------------------------------------------------------------------------------- +class CV_FeatureDetectorMatcherBaseTest : public cvtest::BaseTest +{ +private: + + testparam *tp; + double target_accuracy_margin_from_bfmatcher; + Feature2D* fe; // feature detector extractor + + DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference + DescriptorMatcher* flmatcher; // flann matcher to test + Mat imgQuery; // query image + vector imgOutliers; // outlier image + vector query_kp; // query key points detect from imgQuery + Mat query_desc; // query descriptors extract from imgQuery + vector > outliers_kp; + vector outliers_desc; + int totalOutlierDescCnt; + + string flmatchername; + +public: + + // + // constructor + // + CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, Feature2D* _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : + tp(_tp), + target_accuracy_margin_from_bfmatcher(_accuracy_margin), + fe(_fe), + flmatcher(_flmatcher), + flmatchername(_flmatchername) + { +#if defined(INIT_RANDOM_SEED) + // from test/test_eigen.cpp + srand((unsigned int)time(0)); +#endif + // create brute force matcher for accuracy of reference + bfmatcher = new BFMatcher(norm_type_for_bfmatcher); + } + + // + // Main Test method + // + virtual void run(int) + { + // load query image + string strQueryFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png"; + imgQuery = imread(strQueryFile, 0); + if (imgQuery.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strQueryFile.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + // load outlier images + char* outliers[] = { (char*)"baboon.png", (char*)"fruits.png", (char*)"airplane.png" }; + for (unsigned int i = 0; i < sizeof(outliers) / sizeof(char*); i++) + { + string strOutlierFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/" + outliers[i]; + Mat imgOutlier = imread(strOutlierFile, 0); + if (imgQuery.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strOutlierFile.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + imgOutliers.push_back(imgOutlier); + } + + // extract and compute keypoints and descriptors from query image +#if CV_MAJOR_VERSION==2 + (*fe)(imgQuery, Mat(), query_kp, query_desc); +#elif CV_MAJOR_VERSION==3 + fe->detectAndCompute(imgQuery, Mat(), query_kp, query_desc); +#endif + // extract and compute keypoints and descriptors from outlier images + fe->detect(imgOutliers, outliers_kp); + ((DescriptorExtractor*)fe)->compute(imgOutliers, outliers_kp, outliers_desc); + totalOutlierDescCnt = 0; + for (unsigned int i = 0; i < outliers_desc.size(); ++i) totalOutlierDescCnt += outliers_desc[i].rows; + + if (SHOW_DEBUG_LOG) + { + cout << query_kp.size() << " keypoints extracted from query image." << endl; +#if defined(TRAIN_WITH_OUTLIER_IMAGES) + cout << totalOutlierDescCnt << " keypoints extracted from outlier image(s)." << endl; +#endif + } + // compute brute force matcher accuracy for reference + double totalTrainTime = 0.; + double totalMatchTime = 0.; + double totalAccuracy = 0.; + int cnt = 0; + for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt) + { + if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " BFMatcher " << t; + + TrainInfo ti = transImgAndTrain(fe, bfmatcher, "BFMatcher", + imgQuery, query_kp, query_desc, + imgOutliers, outliers_kp, outliers_desc, + totalOutlierDescCnt, + t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES); + totalTrainTime += ti.traintime; + totalMatchTime += ti.matchtime; + totalAccuracy += ti.accuracy; + } + double bf_average_accuracy = totalAccuracy / cnt; + if (SHOW_DEBUG_LOG) + { + cout << "total training time: " << totalTrainTime << "ms" << endl; + cout << "total matching time: " << totalMatchTime << "ms" << endl; + cout << "average accuracy:" << bf_average_accuracy << "%" << endl; + } + + // test the target matcher + totalTrainTime = 0.; + totalMatchTime = 0.; + totalAccuracy = 0.; + cnt = 0; + for (float t = tp->from; t <= tp->to; t += tp->step, ++testno_for_make_filename, ++cnt) + { + if (SHOW_DEBUG_LOG) cout << "Test No." << testno_for_make_filename << " " << flmatchername << " " << t; + + TrainInfo ti = transImgAndTrain(fe, flmatcher, flmatchername, + imgQuery, query_kp, query_desc, + imgOutliers, outliers_kp, outliers_desc, + totalOutlierDescCnt, + t, tp, testno_for_make_filename, SHOW_DEBUG_LOG, SAVE_DRAW_MATCHES_IMAGES); + + totalTrainTime += ti.traintime; + totalMatchTime += ti.matchtime; + totalAccuracy += ti.accuracy; + } + double average_accuracy = totalAccuracy / cnt; + double target_average_accuracy = bf_average_accuracy * target_accuracy_margin_from_bfmatcher; + + if (SHOW_DEBUG_LOG) + { + cout << "total training time: " << totalTrainTime << "ms" << endl; + cout << "total matching time: " << totalMatchTime << "ms" << endl; + cout << "average accuracy:" << average_accuracy << "%" << endl; + cout << "threshold of the target matcher average accuracy as error :" << target_average_accuracy << "%" << endl; + cout << "accuracy degraded " << (100.0 - (average_accuracy / bf_average_accuracy *100.0)) << "% from BFMatcher.(lower percentage is better)" << endl; + } + // compare accuracies between the brute force matcher and the test target matcher + if (average_accuracy < target_average_accuracy) + { + ts->printf(cvtest::TS::LOG, "Bad average accuracy %f < %f while test %s %s query\n", average_accuracy, target_average_accuracy, flmatchername.c_str(), tp->transname.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + } + return; + } + +}; + +// -------------------------------------------------------------------------------------- +// Transform Functions +// -------------------------------------------------------------------------------------- +static void rotate(float deg, const Mat& src, Mat& dst) +{ + warpAffine(src, dst, getRotationMatrix2D(Point2f(src.cols / 2.0f, src.rows / 2.0f), deg, 1), src.size(), INTER_CUBIC); +} +static void scale(float scale, const Mat& src, Mat& dst) +{ + resize(src, dst, Size((int)(src.cols*scale), (int)(src.rows*scale)), INTER_AREA); +} +static void blur(float k, const Mat& src, Mat& dst) +{ + GaussianBlur(src, dst, Size((int)k, (int)k), 0); +} + +// -------------------------------------------------------------------------------------- +// Tests Registrations +// -------------------------------------------------------------------------------------- +#define SHORT_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 10..24% measured.lower percentage is better.) for lsh key size=16. +#define MIDDLE_LSH_KEY_ACCURACY_MARGIN 0.72 // The margin for FlannBasedMatcher. 28% degraded from BFMatcher(Actually, about 7..24% measured.lower percentage is better.) for lsh key size=24. +#define LONG_LSH_KEY_ACCURACY_MARGIN 0.90 // The margin for FlannBasedMatcher. 10% degraded from BFMatcher(Actually, about -29...7% measured.lower percentage is better.) for lsh key size=31. + +TEST(BlurredQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(BlurredQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(BlurredQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); + testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} + +TEST(ScaledQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(ScaledQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(ScaledQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); + testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} + +TEST(RotatedQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 16, 2)); + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(RotatedQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 24, 2)); + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + test.safe_run(); +} +TEST(RotatedQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) +{ + Ptr fe = OrbCreate; + Ptr fl = makePtr(makePtr(1, 31, 2)); + testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + test.safe_run(); +} diff --git a/modules/features2d/test/test_precomp.hpp b/modules/features2d/test/test_precomp.hpp index bc2d8be6e9..b0fb9a593e 100644 --- a/modules/features2d/test/test_precomp.hpp +++ b/modules/features2d/test/test_precomp.hpp @@ -14,6 +14,7 @@ #include "opencv2/imgproc/imgproc_c.h" #include "opencv2/features2d/features2d.hpp" #include "opencv2/highgui/highgui.hpp" +#include "opencv2/calib3d/calib3d.hpp" #include #endif diff --git a/modules/ts/CMakeLists.txt b/modules/ts/CMakeLists.txt index bf7da104de..4d4bebda80 100644 --- a/modules/ts/CMakeLists.txt +++ b/modules/ts/CMakeLists.txt @@ -9,7 +9,7 @@ set(OPENCV_MODULE_IS_PART_OF_WORLD FALSE) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) -ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video) +ocv_add_module(ts opencv_core opencv_features2d opencv_highgui opencv_imgproc opencv_video opencv_calib3d) ocv_glob_module_sources() ocv_module_include_directories() From b8c00854fe78e198f2d9d19a250a2eb27abaf95c Mon Sep 17 00:00:00 2001 From: ippei ito Date: Thu, 2 Apr 2015 19:58:19 +0900 Subject: [PATCH 10/10] instead of "using namespace cv" put all the tests into cv:: namespace. --- .../test/test_lshindex_flannbased_matcher.cpp | 183 +++++++++--------- 1 file changed, 91 insertions(+), 92 deletions(-) diff --git a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp index 51e2175708..ddc81b0c5d 100644 --- a/modules/features2d/test/test_lshindex_flannbased_matcher.cpp +++ b/modules/features2d/test/test_lshindex_flannbased_matcher.cpp @@ -62,13 +62,12 @@ #define SHOW_DEBUG_LOG true #if CV_MAJOR_VERSION==2 -#define OrbCreate new ORB(4000) +#define OrbCreate new cv::ORB(4000) #elif CV_MAJOR_VERSION==3 -#define OrbCreate ORB::create(4000) -#define AKazeCreate AKAZE::create() +#define OrbCreate cv::ORB::create(4000) +#define AKazeCreate cv::AKAZE::create() #endif -using namespace cv; using namespace std; int testno_for_make_filename = 0; @@ -80,9 +79,9 @@ class testparam { public: string transname; - void(*transfunc)(float, const Mat&, Mat&); + void(*transfunc)(float, const cv::Mat&, cv::Mat&); float from, to, step; - testparam(string _transname, void(*_transfunc)(float, const Mat&, Mat&), float _from, float _to, float _step) : + testparam(string _transname, void(*_transfunc)(float, const cv::Mat&, cv::Mat&), float _from, float _to, float _step) : transname(_transname), transfunc(_transfunc), from(_from), @@ -94,7 +93,7 @@ public: // -------------------------------------------------------------------------------------- // from matching_to_many_images.cpp // -------------------------------------------------------------------------------------- -int maskMatchesByTrainImgIdx(const vector& matches, int trainImgIdx, vector& mask) +int maskMatchesByTrainImgIdx(const vector& matches, int trainImgIdx, vector& mask) { int matchcnt = 0; mask.resize(matches.size()); @@ -110,7 +109,7 @@ int maskMatchesByTrainImgIdx(const vector& matches, int trainImgIdx, vec return matchcnt; } -int calcHomographyAndInlierCount(const vector& query_kp, const vector& train_kp, const vector& match, vector &mask, Mat &homography) +int calcHomographyAndInlierCount(const vector& query_kp, const vector& train_kp, const vector& match, vector &mask, cv::Mat &homography) { // make query and current train image keypoint pairs std::vector srcPoints, dstPoints; @@ -124,7 +123,7 @@ int calcHomographyAndInlierCount(const vector& query_kp, const vector< } // calc homography vector inlierMask; - homography = findHomography(srcPoints, dstPoints, RANSAC, 3.0, inlierMask); + homography = findHomography(srcPoints, dstPoints, cv::RANSAC, 3.0, inlierMask); // update outlier mask int j = 0; @@ -152,19 +151,19 @@ int calcHomographyAndInlierCount(const vector& query_kp, const vector< return inlierCnt; } -void drawDetectedRectangle(Mat& imgResult, const Mat& homography, const Mat& imgQuery) +void drawDetectedRectangle(cv::Mat& imgResult, const cv::Mat& homography, const cv::Mat& imgQuery) { - std::vector query_corners(4); - query_corners[0] = Point(0, 0); - query_corners[1] = Point(imgQuery.cols, 0); - query_corners[2] = Point(imgQuery.cols, imgQuery.rows); - query_corners[3] = Point(0, imgQuery.rows); - std::vector train_corners(4); + std::vector query_corners(4); + query_corners[0] = cv::Point(0, 0); + query_corners[1] = cv::Point(imgQuery.cols, 0); + query_corners[2] = cv::Point(imgQuery.cols, imgQuery.rows); + query_corners[3] = cv::Point(0, imgQuery.rows); + std::vector train_corners(4); perspectiveTransform(query_corners, train_corners, homography); - line(imgResult, train_corners[0] + query_corners[1], train_corners[1] + query_corners[1], Scalar(0, 255, 0), 4); - line(imgResult, train_corners[1] + query_corners[1], train_corners[2] + query_corners[1], Scalar(0, 255, 0), 4); - line(imgResult, train_corners[2] + query_corners[1], train_corners[3] + query_corners[1], Scalar(0, 255, 0), 4); - line(imgResult, train_corners[3] + query_corners[1], train_corners[0] + query_corners[1], Scalar(0, 255, 0), 4); + line(imgResult, train_corners[0] + query_corners[1], train_corners[1] + query_corners[1], cv::Scalar(0, 255, 0), 4); + line(imgResult, train_corners[1] + query_corners[1], train_corners[2] + query_corners[1], cv::Scalar(0, 255, 0), 4); + line(imgResult, train_corners[2] + query_corners[1], train_corners[3] + query_corners[1], cv::Scalar(0, 255, 0), 4); + line(imgResult, train_corners[3] + query_corners[1], train_corners[0] + query_corners[1], cv::Scalar(0, 255, 0), 4); } // -------------------------------------------------------------------------------------- @@ -179,55 +178,55 @@ typedef struct tagTrainInfo }TrainInfo; TrainInfo transImgAndTrain( - Feature2D *fe, - DescriptorMatcher *matcher, + cv::Feature2D *fe, + cv::DescriptorMatcher *matcher, const string &matchername, - const Mat& imgQuery, const vector& query_kp, const Mat& query_desc, - const vector& imgOutliers, const vector >& outliers_kp, const vector& outliers_desc, const int totalOutlierDescCnt, + const cv::Mat& imgQuery, const vector& query_kp, const cv::Mat& query_desc, + const vector& imgOutliers, const vector >& outliers_kp, const vector& outliers_desc, const int totalOutlierDescCnt, const float t, const testparam *tp, const int testno, const bool bVerboseOutput, const bool bSaveDrawMatches) { TrainInfo ti; // transform query image - Mat imgTransform; + cv::Mat imgTransform; (tp->transfunc)(t, imgQuery, imgTransform); // extract kp and compute desc from transformed query image - vector trans_query_kp; - Mat trans_query_desc; + vector trans_query_kp; + cv::Mat trans_query_desc; #if CV_MAJOR_VERSION==2 - (*fe)(imgTransform, Mat(), trans_query_kp, trans_query_desc); + (*fe)(imgTransform, cv::Mat(), trans_query_kp, trans_query_desc); #elif CV_MAJOR_VERSION==3 fe->detectAndCompute(imgTransform, Mat(), trans_query_kp, trans_query_desc); #endif // add&train transformed query desc and outlier desc matcher->clear(); - matcher->add(vector(1, trans_query_desc)); - double s = (double)getTickCount(); + matcher->add(vector(1, trans_query_desc)); + double s = (double)cv::getTickCount(); matcher->train(); - ti.traintime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + ti.traintime = 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency(); ti.traindesccnt = trans_query_desc.rows; #if defined(TRAIN_WITH_OUTLIER_IMAGES) // same as matcher->add(outliers_desc); matcher->train(); for (unsigned int i = 0; i < outliers_desc.size(); ++i) { - matcher->add(vector(1, outliers_desc[i])); - s = (double)getTickCount(); + matcher->add(vector(1, outliers_desc[i])); + s = (double)cv::getTickCount(); matcher->train(); - ti.traintime += 1000.0*((double)getTickCount() - s) / getTickFrequency(); + ti.traintime += 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency(); } ti.traindesccnt += totalOutlierDescCnt; #endif // matching - vector match; - s = (double)getTickCount(); + vector match; + s = (double)cv::getTickCount(); matcher->match(query_desc, match); - ti.matchtime = 1000.0*((double)getTickCount() - s) / getTickFrequency(); + ti.matchtime = 1000.0*((double)cv::getTickCount() - s) / cv::getTickFrequency(); // prepare a directory and variables for save matching images vector mask; - Mat imgResult; + cv::Mat imgResult; const char resultDir[] = "result"; if (bSaveDrawMatches) { @@ -241,10 +240,10 @@ TrainInfo transImgAndTrain( // save query vs transformed query matching image with detected rectangle matchcnt = maskMatchesByTrainImgIdx(match, (int)0, mask); // calc homography and inlier - Mat homography; + cv::Mat homography; int inlierCnt = calcHomographyAndInlierCount(query_kp, trans_query_kp, match, mask, homography); ti.accuracy = (double)inlierCnt / (double)mask.size()*100.0; - drawMatches(imgQuery, query_kp, imgTransform, trans_query_kp, match, imgResult, Scalar::all(-1), Scalar::all(128), mask, DrawMatchesFlags::DRAW_RICH_KEYPOINTS); + drawMatches(imgQuery, query_kp, imgTransform, trans_query_kp, match, imgResult, cv::Scalar::all(-1), cv::Scalar::all(128), mask, cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); if (inlierCnt) { // draw detected rectangle @@ -252,7 +251,7 @@ TrainInfo transImgAndTrain( } // draw status sprintf(buff, "%s accuracy:%-3.2f%% %d descriptors training time:%-3.2fms matching :%-3.2fms", matchername.c_str(), ti.accuracy, ti.traindesccnt, ti.traintime, ti.matchtime); - putText(imgResult, buff, Point(0, 12), FONT_HERSHEY_PLAIN, 0.8, Scalar(0., 0., 255.)); + putText(imgResult, buff, cv::Point(0, 12), cv::FONT_HERSHEY_PLAIN, 0.8, cv::Scalar(0., 0., 255.)); sprintf(buff, "%s/res%03d_%s_%s%.1f_inlier.png", resultDir, testno, matchername.c_str(), tp->transname.c_str(), t); if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; @@ -261,9 +260,9 @@ TrainInfo transImgAndTrain( for (unsigned int i = 0; i transname.c_str(), t, i); if (bSaveDrawMatches && !imwrite(buff, imgResult)) cout << "Image " << buff << " can not be saved (may be because directory " << resultDir << " does not exist)." << endl; } @@ -285,16 +284,16 @@ private: testparam *tp; double target_accuracy_margin_from_bfmatcher; - Feature2D* fe; // feature detector extractor - - DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference - DescriptorMatcher* flmatcher; // flann matcher to test - Mat imgQuery; // query image - vector imgOutliers; // outlier image - vector query_kp; // query key points detect from imgQuery - Mat query_desc; // query descriptors extract from imgQuery - vector > outliers_kp; - vector outliers_desc; + cv::Feature2D* fe; // feature detector extractor + + cv::DescriptorMatcher* bfmatcher; // brute force matcher for accuracy of reference + cv::DescriptorMatcher* flmatcher; // flann matcher to test + cv::Mat imgQuery; // query image + vector imgOutliers; // outlier image + vector query_kp; // query key points detect from imgQuery + cv::Mat query_desc; // query descriptors extract from imgQuery + vector > outliers_kp; + vector outliers_desc; int totalOutlierDescCnt; string flmatchername; @@ -304,7 +303,7 @@ public: // // constructor // - CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, Feature2D* _fe, DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : + CV_FeatureDetectorMatcherBaseTest(testparam* _tp, double _accuracy_margin, cv::Feature2D* _fe, cv::DescriptorMatcher *_flmatcher, string _flmatchername, int norm_type_for_bfmatcher) : tp(_tp), target_accuracy_margin_from_bfmatcher(_accuracy_margin), fe(_fe), @@ -316,7 +315,7 @@ public: srand((unsigned int)time(0)); #endif // create brute force matcher for accuracy of reference - bfmatcher = new BFMatcher(norm_type_for_bfmatcher); + bfmatcher = new cv::BFMatcher(norm_type_for_bfmatcher); } // @@ -326,7 +325,7 @@ public: { // load query image string strQueryFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png"; - imgQuery = imread(strQueryFile, 0); + imgQuery = cv::imread(strQueryFile, 0); if (imgQuery.empty()) { ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strQueryFile.c_str()); @@ -339,7 +338,7 @@ public: for (unsigned int i = 0; i < sizeof(outliers) / sizeof(char*); i++) { string strOutlierFile = string(cvtest::TS::ptr()->get_data_path()) + "shared/" + outliers[i]; - Mat imgOutlier = imread(strOutlierFile, 0); + cv::Mat imgOutlier = cv::imread(strOutlierFile, 0); if (imgQuery.empty()) { ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", strOutlierFile.c_str()); @@ -351,13 +350,13 @@ public: // extract and compute keypoints and descriptors from query image #if CV_MAJOR_VERSION==2 - (*fe)(imgQuery, Mat(), query_kp, query_desc); + (*fe)(imgQuery, cv::Mat(), query_kp, query_desc); #elif CV_MAJOR_VERSION==3 fe->detectAndCompute(imgQuery, Mat(), query_kp, query_desc); #endif // extract and compute keypoints and descriptors from outlier images fe->detect(imgOutliers, outliers_kp); - ((DescriptorExtractor*)fe)->compute(imgOutliers, outliers_kp, outliers_desc); + ((cv::DescriptorExtractor*)fe)->compute(imgOutliers, outliers_kp, outliers_desc); totalOutlierDescCnt = 0; for (unsigned int i = 0; i < outliers_desc.size(); ++i) totalOutlierDescCnt += outliers_desc[i].rows; @@ -438,17 +437,17 @@ public: // -------------------------------------------------------------------------------------- // Transform Functions // -------------------------------------------------------------------------------------- -static void rotate(float deg, const Mat& src, Mat& dst) +static void rotate(float deg, const cv::Mat& src, cv::Mat& dst) { - warpAffine(src, dst, getRotationMatrix2D(Point2f(src.cols / 2.0f, src.rows / 2.0f), deg, 1), src.size(), INTER_CUBIC); + cv::warpAffine(src, dst, getRotationMatrix2D(cv::Point2f(src.cols / 2.0f, src.rows / 2.0f), deg, 1), src.size(), cv::INTER_CUBIC); } -static void scale(float scale, const Mat& src, Mat& dst) +static void scale(float scale, const cv::Mat& src, cv::Mat& dst) { - resize(src, dst, Size((int)(src.cols*scale), (int)(src.rows*scale)), INTER_AREA); + cv::resize(src, dst, cv::Size((int)(src.cols*scale), (int)(src.rows*scale)), cv::INTER_CUBIC); } -static void blur(float k, const Mat& src, Mat& dst) +static void blur(float k, const cv::Mat& src, cv::Mat& dst) { - GaussianBlur(src, dst, Size((int)k, (int)k), 0); + GaussianBlur(src, dst, cv::Size((int)k, (int)k), 0); } // -------------------------------------------------------------------------------------- @@ -460,75 +459,75 @@ static void blur(float k, const Mat& src, Mat& dst) TEST(BlurredQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 16, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 16, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(BlurredQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 24, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 24, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(BlurredQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 31, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 31, 2)); testparam tp("blurred", blur, 1.0f, 11.0f, 2.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 16, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 16, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 24, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 24, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(ScaledQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 31, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 31, 2)); testparam tp("scaled", scale, 0.5f, 1.5f, 0.1f); - CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshShortKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 16, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 16, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, SHORT_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 16, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshMiddleKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 24, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 24, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, MIDDLE_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 24, 2)", cv::NORM_HAMMING); test.safe_run(); } TEST(RotatedQueryFlannBasedLshLongKeyMatcherAdditionalTrainTest, accuracy) { - Ptr fe = OrbCreate; - Ptr fl = makePtr(makePtr(1, 31, 2)); + cv::Ptr fe = OrbCreate; + cv::Ptr fl = cv::makePtr(cv::makePtr(1, 31, 2)); testparam tp("rotated", rotate, 0.0f, 359.0f, 30.0f); - CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", NORM_HAMMING); + CV_FeatureDetectorMatcherBaseTest test(&tp, LONG_LSH_KEY_ACCURACY_MARGIN, fe, fl, "FlannLsh(1, 31, 2)", cv::NORM_HAMMING); test.safe_run(); }