test_affine_feature.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. #include "test_precomp.hpp"
  5. // #define GENERATE_DATA // generate data in debug mode
  6. namespace opencv_test { namespace {
  7. #ifndef GENERATE_DATA
  8. static bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 )
  9. {
  10. const float maxPtDif = 1.f;
  11. const float maxSizeDif = 1.f;
  12. const float maxAngleDif = 2.f;
  13. const float maxResponseDif = 0.1f;
  14. float dist = (float)cv::norm( p1.pt - p2.pt );
  15. return (dist < maxPtDif &&
  16. fabs(p1.size - p2.size) < maxSizeDif &&
  17. abs(p1.angle - p2.angle) < maxAngleDif &&
  18. abs(p1.response - p2.response) < maxResponseDif &&
  19. (p1.octave & 0xffff) == (p2.octave & 0xffff) // do not care about sublayers and class_id
  20. );
  21. }
  22. #endif
  23. TEST(Features2d_AFFINE_FEATURE, regression)
  24. {
  25. Mat image = imread(cvtest::findDataFile("features2d/tsukuba.png"));
  26. string xml = cvtest::TS::ptr()->get_data_path() + "asift/regression_cpp.xml.gz";
  27. ASSERT_FALSE(image.empty());
  28. Mat gray;
  29. cvtColor(image, gray, COLOR_BGR2GRAY);
  30. // Default ASIFT generates too large descriptors. This test uses small maxTilt to suppress the size of testdata.
  31. Ptr<AffineFeature> ext = AffineFeature::create(SIFT::create(), 2, 0, 1.4142135623730951f, 144.0f);
  32. Mat mpt, msize, mangle, mresponse, moctave, mclass_id;
  33. #ifdef GENERATE_DATA
  34. // calculate
  35. vector<KeyPoint> calcKeypoints;
  36. Mat calcDescriptors;
  37. ext->detectAndCompute(gray, Mat(), calcKeypoints, calcDescriptors, false);
  38. // create keypoints XML
  39. FileStorage fs(xml, FileStorage::WRITE);
  40. ASSERT_TRUE(fs.isOpened()) << xml;
  41. std::cout << "Creating keypoints XML..." << std::endl;
  42. mpt = Mat(calcKeypoints.size(), 2, CV_32F);
  43. msize = Mat(calcKeypoints.size(), 1, CV_32F);
  44. mangle = Mat(calcKeypoints.size(), 1, CV_32F);
  45. mresponse = Mat(calcKeypoints.size(), 1, CV_32F);
  46. moctave = Mat(calcKeypoints.size(), 1, CV_32S);
  47. mclass_id = Mat(calcKeypoints.size(), 1, CV_32S);
  48. for( size_t i = 0; i < calcKeypoints.size(); i++ )
  49. {
  50. const KeyPoint& key = calcKeypoints[i];
  51. mpt.at<float>(i, 0) = key.pt.x;
  52. mpt.at<float>(i, 1) = key.pt.y;
  53. msize.at<float>(i, 0) = key.size;
  54. mangle.at<float>(i, 0) = key.angle;
  55. mresponse.at<float>(i, 0) = key.response;
  56. moctave.at<int>(i, 0) = key.octave;
  57. mclass_id.at<int>(i, 0) = key.class_id;
  58. }
  59. fs << "keypoints_pt" << mpt;
  60. fs << "keypoints_size" << msize;
  61. fs << "keypoints_angle" << mangle;
  62. fs << "keypoints_response" << mresponse;
  63. fs << "keypoints_octave" << moctave;
  64. fs << "keypoints_class_id" << mclass_id;
  65. // create descriptor XML
  66. fs << "descriptors" << calcDescriptors;
  67. fs.release();
  68. #else
  69. const float badCountsRatio = 0.01f;
  70. const float badDescriptorDist = 1.0f;
  71. const float maxBadKeypointsRatio = 0.15f;
  72. const float maxBadDescriptorRatio = 0.15f;
  73. // read keypoints
  74. vector<KeyPoint> validKeypoints;
  75. Mat validDescriptors;
  76. FileStorage fs(xml, FileStorage::READ);
  77. ASSERT_TRUE(fs.isOpened()) << xml;
  78. fs["keypoints_pt"] >> mpt;
  79. ASSERT_EQ(mpt.type(), CV_32F);
  80. fs["keypoints_size"] >> msize;
  81. ASSERT_EQ(msize.type(), CV_32F);
  82. fs["keypoints_angle"] >> mangle;
  83. ASSERT_EQ(mangle.type(), CV_32F);
  84. fs["keypoints_response"] >> mresponse;
  85. ASSERT_EQ(mresponse.type(), CV_32F);
  86. fs["keypoints_octave"] >> moctave;
  87. ASSERT_EQ(moctave.type(), CV_32S);
  88. fs["keypoints_class_id"] >> mclass_id;
  89. ASSERT_EQ(mclass_id.type(), CV_32S);
  90. validKeypoints.resize(mpt.rows);
  91. for( int i = 0; i < (int)validKeypoints.size(); i++ )
  92. {
  93. validKeypoints[i].pt.x = mpt.at<float>(i, 0);
  94. validKeypoints[i].pt.y = mpt.at<float>(i, 1);
  95. validKeypoints[i].size = msize.at<float>(i, 0);
  96. validKeypoints[i].angle = mangle.at<float>(i, 0);
  97. validKeypoints[i].response = mresponse.at<float>(i, 0);
  98. validKeypoints[i].octave = moctave.at<int>(i, 0);
  99. validKeypoints[i].class_id = mclass_id.at<int>(i, 0);
  100. }
  101. // read descriptors
  102. fs["descriptors"] >> validDescriptors;
  103. fs.release();
  104. // calc and compare keypoints
  105. vector<KeyPoint> calcKeypoints;
  106. ext->detectAndCompute(gray, Mat(), calcKeypoints, noArray(), false);
  107. float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size();
  108. ASSERT_LT(countRatio, 1 + badCountsRatio) << "Bad keypoints count ratio.";
  109. ASSERT_GT(countRatio, 1 - badCountsRatio) << "Bad keypoints count ratio.";
  110. int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)calcKeypoints.size());
  111. for( size_t v = 0; v < validKeypoints.size(); v++ )
  112. {
  113. int nearestIdx = -1;
  114. float minDist = std::numeric_limits<float>::max();
  115. float angleDistOfNearest = std::numeric_limits<float>::max();
  116. for( size_t c = 0; c < calcKeypoints.size(); c++ )
  117. {
  118. if( validKeypoints[v].class_id != calcKeypoints[c].class_id )
  119. continue;
  120. float curDist = (float)cv::norm( calcKeypoints[c].pt - validKeypoints[v].pt );
  121. if( curDist < minDist )
  122. {
  123. minDist = curDist;
  124. nearestIdx = (int)c;
  125. angleDistOfNearest = abs( calcKeypoints[c].angle - validKeypoints[v].angle );
  126. }
  127. else if( curDist == minDist ) // the keypoints whose positions are same but angles are different
  128. {
  129. float angleDist = abs( calcKeypoints[c].angle - validKeypoints[v].angle );
  130. if( angleDist < angleDistOfNearest )
  131. {
  132. nearestIdx = (int)c;
  133. angleDistOfNearest = angleDist;
  134. }
  135. }
  136. }
  137. if( nearestIdx == -1 || !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) )
  138. badPointCount++;
  139. }
  140. float badKeypointsRatio = (float)badPointCount / (float)commonPointCount;
  141. std::cout << "badKeypointsRatio: " << badKeypointsRatio << std::endl;
  142. ASSERT_LT( badKeypointsRatio , maxBadKeypointsRatio ) << "Bad accuracy!";
  143. // Calc and compare descriptors. This uses validKeypoints for extraction.
  144. Mat calcDescriptors;
  145. ext->detectAndCompute(gray, Mat(), validKeypoints, calcDescriptors, true);
  146. int dim = validDescriptors.cols;
  147. int badDescriptorCount = 0;
  148. L1<float> distance;
  149. for( int i = 0; i < (int)validKeypoints.size(); i++ )
  150. {
  151. float dist = distance( validDescriptors.ptr<float>(i), calcDescriptors.ptr<float>(i), dim );
  152. if( dist > badDescriptorDist )
  153. badDescriptorCount++;
  154. }
  155. float badDescriptorRatio = (float)badDescriptorCount / (float)validKeypoints.size();
  156. std::cout << "badDescriptorRatio: " << badDescriptorRatio << std::endl;
  157. ASSERT_LT( badDescriptorRatio, maxBadDescriptorRatio ) << "Too many descriptors mismatched.";
  158. #endif
  159. }
  160. }} // namespace