flann_search_dataset.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // flann_search_dataset.cpp
  2. // Naive program to search a query picture in a dataset illustrating usage of FLANN
  3. #include <iostream>
  4. #include <vector>
  5. #include "opencv2/core.hpp"
  6. #include "opencv2/core/utils/filesystem.hpp"
  7. #include "opencv2/highgui.hpp"
  8. #include "opencv2/features2d.hpp"
  9. #include "opencv2/flann.hpp"
  10. using namespace cv;
  11. using std::cout;
  12. using std::endl;
  13. #define _ORB_
  14. const char* keys =
  15. "{ help h | | Print help message. }"
  16. "{ dataset | | Path to the images folder used as dataset. }"
  17. "{ image | | Path to the image to search for in the dataset. }"
  18. "{ save | | Path and filename where to save the flann structure to. }"
  19. "{ load | | Path and filename where to load the flann structure from. }";
  20. struct img_info {
  21. int img_index;
  22. unsigned int nbr_of_matches;
  23. img_info(int _img_index, unsigned int _nbr_of_matches)
  24. : img_index(_img_index)
  25. , nbr_of_matches(_nbr_of_matches)
  26. {}
  27. };
  28. int main( int argc, char* argv[] )
  29. {
  30. //-- Test the program options
  31. CommandLineParser parser( argc, argv, keys );
  32. if (parser.has("help"))
  33. {
  34. parser.printMessage();
  35. return -1;
  36. }
  37. const cv::String img_path = parser.get<String>("image");
  38. Mat img = imread( samples::findFile( img_path ), IMREAD_GRAYSCALE );
  39. if (img.empty() )
  40. {
  41. cout << "Could not open the image "<< img_path << endl;
  42. return -1;
  43. }
  44. const cv::String db_path = parser.get<String>("dataset");
  45. if (!utils::fs::isDirectory(db_path))
  46. {
  47. cout << "Dataset folder "<< db_path.c_str() <<" doesn't exist!" << endl;
  48. return -1;
  49. }
  50. const cv::String load_db_path = parser.get<String>("load");
  51. if ((load_db_path != String()) && (!utils::fs::exists(load_db_path)))
  52. {
  53. cout << "File " << load_db_path.c_str()
  54. << " where to load the flann structure from doesn't exist!" << endl;
  55. return -1;
  56. }
  57. const cv::String save_db_path = parser.get<String>("save");
  58. //-- Step 1: Detect the keypoints using a detector, compute the descriptors
  59. // in the folder containing the images of the dataset
  60. #ifdef _SIFT_
  61. int minHessian = 400;
  62. Ptr<Feature2D> detector = SIFT::create( minHessian );
  63. #elif defined(_ORB_)
  64. Ptr<Feature2D> detector = ORB::create();
  65. #else
  66. cout << "Missing or unknown defined descriptor. "
  67. "Only SIFT and ORB are currently interfaced here" << endl;
  68. return -1;
  69. #endif
  70. std::vector<KeyPoint> db_keypoints;
  71. Mat db_descriptors;
  72. std::vector<unsigned int> db_images_indice_range; //store the range of indices per image
  73. std::vector<int> db_indice_2_image_lut; //match descriptor indice to its image
  74. db_images_indice_range.push_back(0);
  75. std::vector<cv::String> files;
  76. utils::fs::glob(db_path, cv::String(), files);
  77. for (std::vector<cv::String>::iterator itr = files.begin(); itr != files.end(); ++itr)
  78. {
  79. Mat tmp_img = imread( *itr, IMREAD_GRAYSCALE );
  80. if (!tmp_img.empty())
  81. {
  82. std::vector<KeyPoint> kpts;
  83. Mat descriptors;
  84. detector->detectAndCompute( tmp_img, noArray(), kpts, descriptors );
  85. db_keypoints.insert( db_keypoints.end(), kpts.begin(), kpts.end() );
  86. db_descriptors.push_back( descriptors );
  87. db_images_indice_range.push_back( db_images_indice_range.back()
  88. + static_cast<unsigned int>(kpts.size()) );
  89. }
  90. }
  91. //-- Set the LUT
  92. db_indice_2_image_lut.resize( db_images_indice_range.back() );
  93. const int nbr_of_imgs = static_cast<int>( db_images_indice_range.size()-1 );
  94. for (int i = 0; i < nbr_of_imgs; ++i)
  95. {
  96. const unsigned int first_indice = db_images_indice_range[i];
  97. const unsigned int last_indice = db_images_indice_range[i+1];
  98. std::fill( db_indice_2_image_lut.begin() + first_indice,
  99. db_indice_2_image_lut.begin() + last_indice,
  100. i );
  101. }
  102. //-- Step 2: build the structure storing the descriptors
  103. #if defined(_SIFT_)
  104. cv::Ptr<flann::GenericIndex<cvflann::L2<float> > > index;
  105. if (load_db_path != String())
  106. index = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,
  107. cvflann::SavedIndexParams(load_db_path));
  108. else
  109. index = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,
  110. cvflann::KDTreeIndexParams(4));
  111. #elif defined(_ORB_)
  112. cv::Ptr<flann::GenericIndex<cvflann::Hamming<unsigned char> > > index;
  113. if (load_db_path != String())
  114. index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >
  115. (db_descriptors, cvflann::SavedIndexParams(load_db_path));
  116. else
  117. index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >
  118. (db_descriptors, cvflann::LshIndexParams());
  119. #else
  120. cout<< "Descriptor not listed. Set the proper FLANN distance for this descriptor" <<endl;
  121. return -1;
  122. #endif
  123. if (save_db_path != String())
  124. index->save(save_db_path);
  125. // Return if no query image was set
  126. if (img_path == String())
  127. return 0;
  128. //-- Detect the keypoints and compute the descriptors for the query image
  129. std::vector<KeyPoint> img_keypoints;
  130. Mat img_descriptors;
  131. detector->detectAndCompute( img, noArray(), img_keypoints, img_descriptors );
  132. //-- Step 3: retrieve the descriptors in the dataset matching the ones of the query image
  133. // /!\ knnSearch doesn't follow OpenCV standards by not initialising empty Mat properties
  134. const int knn = 2;
  135. Mat indices(img_descriptors.rows, knn, CV_32S);
  136. #if defined(_SIFT_)
  137. #define DIST_TYPE float
  138. Mat dists(img_descriptors.rows, knn, CV_32F);
  139. #elif defined(_ORB_)
  140. #define DIST_TYPE int
  141. Mat dists(img_descriptors.rows, knn, CV_32S);
  142. #endif
  143. index->knnSearch( img_descriptors, indices, dists, knn, cvflann::SearchParams(32) );
  144. //-- Filter matches using the Lowe's ratio test
  145. const float ratio_thresh = 0.7f;
  146. std::vector<DMatch> good_matches; //contains
  147. std::vector<unsigned int> matches_per_img_histogram( nbr_of_imgs, 0 );
  148. for (int i = 0; i < dists.rows; ++i)
  149. {
  150. if (dists.at<DIST_TYPE>(i,0) < ratio_thresh * dists.at<DIST_TYPE>(i,1))
  151. {
  152. const int indice_in_db = indices.at<int>(i,0);
  153. DMatch dmatch(i, indice_in_db, db_indice_2_image_lut[indice_in_db],
  154. static_cast<float>(dists.at<DIST_TYPE>(i,0)));
  155. good_matches.push_back( dmatch );
  156. matches_per_img_histogram[ db_indice_2_image_lut[indice_in_db] ]++;
  157. }
  158. }
  159. //-- Step 4: find the dataset image with the highest proportion of matches
  160. std::multimap<float, img_info> images_infos;
  161. for (int i = 0; i < nbr_of_imgs; ++i)
  162. {
  163. const unsigned int nbr_of_matches = matches_per_img_histogram[i];
  164. if (nbr_of_matches < 4) //we need at leat 4 points for a homography
  165. continue;
  166. const unsigned int nbr_of_kpts = db_images_indice_range[i+1] - db_images_indice_range[i];
  167. const float inverse_proportion_of_retrieved_kpts =
  168. static_cast<float>(nbr_of_kpts) / static_cast<float>(nbr_of_matches);
  169. img_info info(i, nbr_of_matches);
  170. images_infos.insert( std::pair<float,img_info>(inverse_proportion_of_retrieved_kpts,
  171. info) );
  172. }
  173. if (images_infos.begin() == images_infos.end())
  174. {
  175. cout<<"No good match could be found."<<endl;
  176. return 0;
  177. }
  178. //-- if there are several images with a similar proportion of matches,
  179. // select the one with the highest number of matches weighted by the
  180. // squared ratio of proportions
  181. const float best_matches_proportion = images_infos.begin()->first;
  182. float new_matches_proportion = best_matches_proportion;
  183. img_info best_img = images_infos.begin()->second;
  184. std::multimap<float, img_info>::iterator it = images_infos.begin();
  185. ++it;
  186. while ((it!=images_infos.end()) && (it->first < 1.1*best_matches_proportion))
  187. {
  188. const float ratio = new_matches_proportion / it->first;
  189. if( it->second.nbr_of_matches * (ratio * ratio) > best_img.nbr_of_matches)
  190. {
  191. new_matches_proportion = it->first;
  192. best_img = it->second;
  193. }
  194. ++it;
  195. }
  196. //-- Step 5: filter goodmatches that belong to the best image match of the dataset
  197. std::vector<DMatch> filtered_good_matches;
  198. for (std::vector<DMatch>::iterator itr(good_matches.begin()); itr != good_matches.end(); ++itr)
  199. {
  200. if (itr->imgIdx == best_img.img_index)
  201. filtered_good_matches.push_back(*itr);
  202. }
  203. //-- Retrieve the best image match from the dataset
  204. Mat db_img = imread( files[best_img.img_index], IMREAD_GRAYSCALE );
  205. //-- Draw matches
  206. Mat img_matches;
  207. drawMatches( img, img_keypoints, db_img, db_keypoints, filtered_good_matches, img_matches, Scalar::all(-1),
  208. Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
  209. //-- Show detected matches
  210. imshow("Good Matches", img_matches );
  211. waitKey();
  212. return 0;
  213. }