test_descriptors_regression.impl.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level directory
  3. // of this distribution and at http://opencv.org/license.html
  4. namespace opencv_test { namespace {
  5. /****************************************************************************************\
  6. * Regression tests for descriptor extractors. *
  7. \****************************************************************************************/
  8. static void double_image(Mat& src, Mat& dst) {
  9. dst.create(Size(src.cols*2, src.rows*2), src.type());
  10. Mat H = Mat::zeros(2, 3, CV_32F);
  11. H.at<float>(0, 0) = 0.5f;
  12. H.at<float>(1, 1) = 0.5f;
  13. cv::warpAffine(src, dst, H, dst.size(), INTER_LINEAR | WARP_INVERSE_MAP, BORDER_REFLECT);
  14. }
  15. static Mat prepare_img(bool rows_indexed) {
  16. int rows = 5;
  17. int columns = 5;
  18. Mat img(rows, columns, CV_32F);
  19. for (int i = 0; i < rows; i++) {
  20. for (int j = 0; j < columns; j++) {
  21. if (rows_indexed) {
  22. img.at<float>(i, j) = (float)i;
  23. } else {
  24. img.at<float>(i, j) = (float)j;
  25. }
  26. }
  27. }
  28. return img;
  29. }
  30. static void writeMatInBin( const Mat& mat, const string& filename )
  31. {
  32. FILE* f = fopen( filename.c_str(), "wb");
  33. if( f )
  34. {
  35. CV_Assert(4 == sizeof(int));
  36. int type = mat.type();
  37. fwrite( (void*)&mat.rows, sizeof(int), 1, f );
  38. fwrite( (void*)&mat.cols, sizeof(int), 1, f );
  39. fwrite( (void*)&type, sizeof(int), 1, f );
  40. int dataSize = (int)(mat.step * mat.rows);
  41. fwrite( (void*)&dataSize, sizeof(int), 1, f );
  42. fwrite( (void*)mat.ptr(), 1, dataSize, f );
  43. fclose(f);
  44. }
  45. }
  46. static Mat readMatFromBin( const string& filename )
  47. {
  48. FILE* f = fopen( filename.c_str(), "rb" );
  49. if( f )
  50. {
  51. CV_Assert(4 == sizeof(int));
  52. int rows, cols, type, dataSize;
  53. size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f );
  54. size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f );
  55. size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f );
  56. size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f );
  57. CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1);
  58. int step = dataSize / rows / CV_ELEM_SIZE(type);
  59. CV_Assert(step >= cols);
  60. Mat returnMat = Mat(rows, step, type).colRange(0, cols);
  61. size_t elements_read = fread( returnMat.ptr(), 1, dataSize, f );
  62. CV_Assert(elements_read == (size_t)(dataSize));
  63. fclose(f);
  64. return returnMat;
  65. }
  66. return Mat();
  67. }
  68. template<class Distance>
  69. class CV_DescriptorExtractorTest : public cvtest::BaseTest
  70. {
  71. public:
  72. typedef typename Distance::ValueType ValueType;
  73. typedef typename Distance::ResultType DistanceType;
  74. CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr<DescriptorExtractor>& _dextractor,
  75. Distance d = Distance(), Ptr<FeatureDetector> _detector = Ptr<FeatureDetector>()):
  76. name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {}
  77. ~CV_DescriptorExtractorTest()
  78. {
  79. }
  80. protected:
  81. virtual void createDescriptorExtractor() {}
  82. void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors )
  83. {
  84. if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() )
  85. {
  86. ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n");
  87. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  88. return;
  89. }
  90. CV_Assert( DataType<ValueType>::type == validDescriptors.type() );
  91. int dimension = validDescriptors.cols;
  92. DistanceType curMaxDist = 0;
  93. size_t exact_count = 0, failed_count = 0;
  94. for( int y = 0; y < validDescriptors.rows; y++ )
  95. {
  96. DistanceType dist = distance( validDescriptors.ptr<ValueType>(y), calcDescriptors.ptr<ValueType>(y), dimension );
  97. if (dist == 0)
  98. exact_count++;
  99. if( dist > curMaxDist )
  100. {
  101. if (dist > maxDist)
  102. failed_count++;
  103. curMaxDist = dist;
  104. }
  105. #if 0
  106. if (dist > 0)
  107. {
  108. std::cout << "i=" << y << " fail_count=" << failed_count << " dist=" << dist << std::endl;
  109. std::cout << "valid: " << validDescriptors.row(y) << std::endl;
  110. std::cout << " calc: " << calcDescriptors.row(y) << std::endl;
  111. }
  112. #endif
  113. }
  114. float exact_percents = (100 * (float)exact_count / validDescriptors.rows);
  115. float failed_percents = (100 * (float)failed_count / validDescriptors.rows);
  116. std::stringstream ss;
  117. ss << "Exact count (dist == 0): " << exact_count << " (" << (int)exact_percents << "%)" << std::endl
  118. << "Failed count (dist > " << maxDist << "): " << failed_count << " (" << (int)failed_percents << "%)" << std::endl
  119. << "Max distance between valid and computed descriptors (" << validDescriptors.size() << "): " << curMaxDist;
  120. EXPECT_LE(failed_percents, 20.0f);
  121. std::cout << ss.str() << std::endl;
  122. }
  123. void emptyDataTest()
  124. {
  125. assert( dextractor );
  126. // One image.
  127. Mat image;
  128. vector<KeyPoint> keypoints;
  129. Mat descriptors;
  130. try
  131. {
  132. dextractor->compute( image, keypoints, descriptors );
  133. }
  134. catch(...)
  135. {
  136. ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n");
  137. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  138. }
  139. RNG rng;
  140. image = cvtest::randomMat(rng, Size(50, 50), CV_8UC3, 0, 255, false);
  141. try
  142. {
  143. dextractor->compute( image, keypoints, descriptors );
  144. }
  145. catch(...)
  146. {
  147. ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n");
  148. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  149. }
  150. image = prepare_img(false);
  151. Mat dbl;
  152. try
  153. {
  154. double_image(image, dbl);
  155. Mat downsized_back(dbl.rows/2, dbl.cols/2, CV_32F);
  156. resize(dbl, downsized_back, Size(dbl.cols/2, dbl.rows/2), 0, 0, INTER_NEAREST);
  157. cv::Mat diff = (image != downsized_back);
  158. ASSERT_EQ(0, cv::norm(image, downsized_back, NORM_INF));
  159. }
  160. catch(...)
  161. {
  162. ts->printf( cvtest::TS::LOG, "double_image() must not generate exception (1).\n");
  163. ts->printf( cvtest::TS::LOG, "double_image() when downsized back by NEAREST must generate the same original image (1).\n");
  164. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  165. }
  166. // Several images.
  167. vector<Mat> images;
  168. vector<vector<KeyPoint> > keypointsCollection;
  169. vector<Mat> descriptorsCollection;
  170. try
  171. {
  172. dextractor->compute( images, keypointsCollection, descriptorsCollection );
  173. }
  174. catch(...)
  175. {
  176. ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n");
  177. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  178. }
  179. }
  180. void regressionTest()
  181. {
  182. assert( dextractor );
  183. // Read the test image.
  184. string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;
  185. Mat img = imread( imgFilename );
  186. if( img.empty() )
  187. {
  188. ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() );
  189. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  190. return;
  191. }
  192. const std::string keypoints_filename = string(ts->get_data_path()) +
  193. (detector.empty()
  194. ? (FEATURES2D_DIR + "/" + std::string("keypoints.xml.gz"))
  195. : (DESCRIPTOR_DIR + "/" + name + "_keypoints.xml.gz"));
  196. FileStorage fs(keypoints_filename, FileStorage::READ);
  197. vector<KeyPoint> keypoints;
  198. EXPECT_TRUE(fs.isOpened()) << "Keypoint testdata is missing. Re-computing and re-writing keypoints testdata...";
  199. if (!fs.isOpened())
  200. {
  201. fs.open(keypoints_filename, FileStorage::WRITE);
  202. ASSERT_TRUE(fs.isOpened()) << "File for writing keypoints can not be opened.";
  203. if (detector.empty())
  204. {
  205. Ptr<ORB> fd = ORB::create();
  206. fd->detect(img, keypoints);
  207. }
  208. else
  209. {
  210. detector->detect(img, keypoints);
  211. }
  212. write(fs, "keypoints", keypoints);
  213. fs.release();
  214. }
  215. else
  216. {
  217. read(fs.getFirstTopLevelNode(), keypoints);
  218. fs.release();
  219. }
  220. if(!detector.empty())
  221. {
  222. vector<KeyPoint> calcKeypoints;
  223. detector->detect(img, calcKeypoints);
  224. // TODO validate received keypoints
  225. int diff = abs((int)calcKeypoints.size() - (int)keypoints.size());
  226. if (diff > 0)
  227. {
  228. std::cout << "Keypoints difference: " << diff << std::endl;
  229. EXPECT_LE(diff, (int)(keypoints.size() * 0.03f));
  230. }
  231. }
  232. ASSERT_FALSE(keypoints.empty());
  233. {
  234. Mat calcDescriptors;
  235. double t = (double)getTickCount();
  236. dextractor->compute(img, keypoints, calcDescriptors);
  237. t = getTickCount() - t;
  238. ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)getTickFrequency()*1000.)/calcDescriptors.rows);
  239. if (calcDescriptors.rows != (int)keypoints.size())
  240. {
  241. ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" );
  242. ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() );
  243. ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows );
  244. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  245. return;
  246. }
  247. if (calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType())
  248. {
  249. ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" );
  250. ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() );
  251. ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols );
  252. ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() );
  253. ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() );
  254. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
  255. return;
  256. }
  257. // TODO read and write descriptor extractor parameters and check them
  258. Mat validDescriptors = readDescriptors();
  259. EXPECT_FALSE(validDescriptors.empty()) << "Descriptors testdata is missing. Re-writing descriptors testdata...";
  260. if (!validDescriptors.empty())
  261. {
  262. compareDescriptors(validDescriptors, calcDescriptors);
  263. }
  264. else
  265. {
  266. ASSERT_TRUE(writeDescriptors(calcDescriptors)) << "Descriptors can not be written.";
  267. }
  268. }
  269. }
  270. void run(int)
  271. {
  272. createDescriptorExtractor();
  273. if( !dextractor )
  274. {
  275. ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n");
  276. ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
  277. return;
  278. }
  279. emptyDataTest();
  280. regressionTest();
  281. ts->set_failed_test_info( cvtest::TS::OK );
  282. }
  283. virtual Mat readDescriptors()
  284. {
  285. Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
  286. return res;
  287. }
  288. virtual bool writeDescriptors( Mat& descs )
  289. {
  290. writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
  291. return true;
  292. }
  293. string name;
  294. const DistanceType maxDist;
  295. Ptr<DescriptorExtractor> dextractor;
  296. Distance distance;
  297. Ptr<FeatureDetector> detector;
  298. private:
  299. CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; }
  300. };
  301. }} // namespace