From: pemmanuelviel
Date: Mon, 10 Aug 2020 13:26:40 +0000 (+0200)
Subject: Merge pull request #17643 from pemmanuelviel:pev--new-flann-demo
X-Git-Tag: submit/tizen/20210224.033012~2^2~15^2~73
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fe9ff64d641be19ce38fd3c36489615704b339c2;p=platform%2Fupstream%2Fopencv.git
Merge pull request #17643 from pemmanuelviel:pev--new-flann-demo
* Add a FLANN example showing how to search a query image in a dataset
* Clean: remove warning
* Replace dependency to boost::filesystem by calls to core/utils/filesystem
* Wait for escape key to exit
* Add an example of binary descriptors support
* Add program options for saving and loading the flann structure
* Fix warnings on Win64
* Fix warnings on 3.4 branch still relying on C++03
* Add ctor to img_info structure
* Comments modification
* * Demo file of FLANN moved and renamed
* Fix distances type when using binary vectors in the FLANN example
* Rename FLANN example file
* Remove dependency of the flann example to opencv_contrib's SURF.
* Remove mention of FLANN and other descriptors that aimed at giving hint on the other options
* Cleaner program options management
* Make waitKey usage minimal in FLANN example
* Fix the conditions order
* Use cv::Ptr
---
diff --git a/samples/cpp/flann_search_dataset.cpp b/samples/cpp/flann_search_dataset.cpp
new file mode 100644
index 0000000000..01ef93f821
--- /dev/null
+++ b/samples/cpp/flann_search_dataset.cpp
@@ -0,0 +1,250 @@
+// flann_search_dataset.cpp
+// Naive program to search a query picture in a dataset illustrating usage of FLANN
+
+#include
+#include
+#include "opencv2/core.hpp"
+#include "opencv2/core/utils/filesystem.hpp"
+#include "opencv2/highgui.hpp"
+#include "opencv2/features2d.hpp"
+#include "opencv2/flann.hpp"
+
+using namespace cv;
+using std::cout;
+using std::endl;
+
+#define _ORB_
+
+const char* keys =
+ "{ help h | | Print help message. }"
+ "{ dataset | | Path to the images folder used as dataset. }"
+ "{ image | | Path to the image to search for in the dataset. }"
+ "{ save | | Path and filename where to save the flann structure to. }"
+ "{ load | | Path and filename where to load the flann structure from. }";
+
+struct img_info {
+ int img_index;
+ unsigned int nbr_of_matches;
+
+ img_info(int _img_index, unsigned int _nbr_of_matches)
+ : img_index(_img_index)
+ , nbr_of_matches(_nbr_of_matches)
+ {}
+};
+
+
+int main( int argc, char* argv[] )
+{
+ //-- Test the program options
+ CommandLineParser parser( argc, argv, keys );
+ if (parser.has("help"))
+ {
+ parser.printMessage();
+ return -1;
+ }
+
+ const cv::String img_path = parser.get("image");
+ Mat img = imread( samples::findFile( img_path ), IMREAD_GRAYSCALE );
+ if (img.empty() )
+ {
+ cout << "Could not open the image "<< img_path << endl;
+ return -1;
+ }
+
+ const cv::String db_path = parser.get("dataset");
+ if (!utils::fs::isDirectory(db_path))
+ {
+ cout << "Dataset folder "<< db_path.c_str() <<" doesn't exist!" << endl;
+ return -1;
+ }
+
+ const cv::String load_db_path = parser.get("load");
+ if ((load_db_path != String()) && (!utils::fs::exists(load_db_path)))
+ {
+ cout << "File " << load_db_path.c_str()
+ << " where to load the flann structure from doesn't exist!" << endl;
+ return -1;
+ }
+
+ const cv::String save_db_path = parser.get("save");
+
+ //-- Step 1: Detect the keypoints using a detector, compute the descriptors
+ // in the folder containing the images of the dataset
+#ifdef _SIFT_
+ int minHessian = 400;
+ Ptr detector = SIFT::create( minHessian );
+#elif defined(_ORB_)
+ Ptr detector = ORB::create();
+#else
+ cout << "Missing or unknown defined descriptor. "
+ "Only SIFT and ORB are currently interfaced here" << endl;
+ return -1;
+#endif
+
+ std::vector db_keypoints;
+ Mat db_descriptors;
+ std::vector db_images_indice_range; //store the range of indices per image
+ std::vector db_indice_2_image_lut; //match descriptor indice to its image
+
+ db_images_indice_range.push_back(0);
+ std::vector files;
+ utils::fs::glob(db_path, cv::String(), files);
+ for (std::vector::iterator itr = files.begin(); itr != files.end(); ++itr)
+ {
+ Mat tmp_img = imread( *itr, IMREAD_GRAYSCALE );
+ if (!tmp_img.empty())
+ {
+ std::vector kpts;
+ Mat descriptors;
+ detector->detectAndCompute( tmp_img, noArray(), kpts, descriptors );
+
+ db_keypoints.insert( db_keypoints.end(), kpts.begin(), kpts.end() );
+ db_descriptors.push_back( descriptors );
+ db_images_indice_range.push_back( db_images_indice_range.back()
+ + static_cast(kpts.size()) );
+ }
+ }
+
+ //-- Set the LUT
+ db_indice_2_image_lut.resize( db_images_indice_range.back() );
+ const int nbr_of_imgs = static_cast( db_images_indice_range.size()-1 );
+ for (int i = 0; i < nbr_of_imgs; ++i)
+ {
+ const unsigned int first_indice = db_images_indice_range[i];
+ const unsigned int last_indice = db_images_indice_range[i+1];
+ std::fill( db_indice_2_image_lut.begin() + first_indice,
+ db_indice_2_image_lut.begin() + last_indice,
+ i );
+ }
+
+ //-- Step 2: build the structure storing the descriptors
+#if defined(_SIFT_)
+ cv::Ptr > > index;
+ if (load_db_path != String())
+ index = cv::makePtr > >(db_descriptors,
+ cvflann::SavedIndexParams(load_db_path));
+ else
+ index = cv::makePtr > >(db_descriptors,
+ cvflann::KDTreeIndexParams(4));
+
+#elif defined(_ORB_)
+ cv::Ptr > > index;
+ if (load_db_path != String())
+ index = cv::makePtr > >
+ (db_descriptors, cvflann::SavedIndexParams(load_db_path));
+ else
+ index = cv::makePtr > >
+ (db_descriptors, cvflann::LshIndexParams());
+#else
+ cout<< "Descriptor not listed. Set the proper FLANN distance for this descriptor" <save(save_db_path);
+
+
+ // Return if no query image was set
+ if (img_path == String())
+ return 0;
+
+ //-- Detect the keypoints and compute the descriptors for the query image
+ std::vector img_keypoints;
+ Mat img_descriptors;
+ detector->detectAndCompute( img, noArray(), img_keypoints, img_descriptors );
+
+
+ //-- Step 3: retrieve the descriptors in the dataset matching the ones of the query image
+ // /!\ knnSearch doesn't follow OpenCV standards by not initialising empty Mat properties
+ const int knn = 2;
+ Mat indices(img_descriptors.rows, knn, CV_32S);
+#if defined(_SIFT_)
+#define DIST_TYPE float
+ Mat dists(img_descriptors.rows, knn, CV_32F);
+#elif defined(_ORB_)
+#define DIST_TYPE int
+ Mat dists(img_descriptors.rows, knn, CV_32S);
+#endif
+ index->knnSearch( img_descriptors, indices, dists, knn, cvflann::SearchParams(32) );
+
+ //-- Filter matches using the Lowe's ratio test
+ const float ratio_thresh = 0.7f;
+ std::vector good_matches; //contains
+ std::vector matches_per_img_histogram( nbr_of_imgs, 0 );
+ for (int i = 0; i < dists.rows; ++i)
+ {
+ if (dists.at(i,0) < ratio_thresh * dists.at(i,1))
+ {
+ const int indice_in_db = indices.at(i,0);
+ DMatch dmatch(i, indice_in_db, db_indice_2_image_lut[indice_in_db],
+ static_cast(dists.at(i,0)));
+ good_matches.push_back( dmatch );
+ matches_per_img_histogram[ db_indice_2_image_lut[indice_in_db] ]++;
+ }
+ }
+
+
+ //-- Step 4: find the dataset image with the highest proportion of matches
+ std::multimap images_infos;
+ for (int i = 0; i < nbr_of_imgs; ++i)
+ {
+ const unsigned int nbr_of_matches = matches_per_img_histogram[i];
+ if (nbr_of_matches < 4) //we need at leat 4 points for a homography
+ continue;
+
+ const unsigned int nbr_of_kpts = db_images_indice_range[i+1] - db_images_indice_range[i];
+ const float inverse_proportion_of_retrieved_kpts =
+ static_cast(nbr_of_kpts) / static_cast(nbr_of_matches);
+
+ img_info info(i, nbr_of_matches);
+ images_infos.insert( std::pair(inverse_proportion_of_retrieved_kpts,
+ info) );
+ }
+
+ if (images_infos.begin() == images_infos.end())
+ {
+ cout<<"No good match could be found."<first;
+ float new_matches_proportion = best_matches_proportion;
+ img_info best_img = images_infos.begin()->second;
+
+ std::multimap::iterator it = images_infos.begin();
+ ++it;
+ while ((it!=images_infos.end()) && (it->first < 1.1*best_matches_proportion))
+ {
+ const float ratio = new_matches_proportion / it->first;
+ if( it->second.nbr_of_matches * (ratio * ratio) > best_img.nbr_of_matches)
+ {
+ new_matches_proportion = it->first;
+ best_img = it->second;
+ }
+ ++it;
+ }
+
+ //-- Step 5: filter goodmatches that belong to the best image match of the dataset
+ std::vector filtered_good_matches;
+ for (std::vector::iterator itr(good_matches.begin()); itr != good_matches.end(); ++itr)
+ {
+ if (itr->imgIdx == best_img.img_index)
+ filtered_good_matches.push_back(*itr);
+ }
+
+ //-- Retrieve the best image match from the dataset
+ Mat db_img = imread( files[best_img.img_index], IMREAD_GRAYSCALE );
+
+ //-- Draw matches
+ Mat img_matches;
+ drawMatches( img, img_keypoints, db_img, db_keypoints, filtered_good_matches, img_matches, Scalar::all(-1),
+ Scalar::all(-1), std::vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
+
+ //-- Show detected matches
+ imshow("Good Matches", img_matches );
+ waitKey();
+
+ return 0;
+}