test_descriptors_invariance.impl.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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_invariance_utils.hpp"
  5. #include <functional>
  6. namespace opencv_test { namespace {
  7. #define SHOW_DEBUG_LOG 1
  8. // NOTE: using factory function (function<Ptr<Type>()>) instead of object instance (Ptr<Type>) as a
  9. // test parameter, because parameters exist during whole test program run and consume a lot of memory
  10. typedef std::function<cv::Ptr<cv::FeatureDetector>()> DetectorFactory;
  11. typedef std::function<cv::Ptr<cv::DescriptorExtractor>()> ExtractorFactory;
  12. typedef tuple<std::string, DetectorFactory, ExtractorFactory, float>
  13. String_FeatureDetector_DescriptorExtractor_Float_t;
  14. static
  15. void SetSuitableSIFTOctave(vector<KeyPoint>& keypoints,
  16. int firstOctave = -1, int nOctaveLayers = 3, double sigma = 1.6)
  17. {
  18. for (size_t i = 0; i < keypoints.size(); i++ )
  19. {
  20. int octv, layer;
  21. KeyPoint& kpt = keypoints[i];
  22. double octv_layer = std::log(kpt.size / sigma) / std::log(2.) - 1;
  23. octv = cvFloor(octv_layer);
  24. layer = cvRound( (octv_layer - octv) * nOctaveLayers );
  25. if (octv < firstOctave)
  26. {
  27. octv = firstOctave;
  28. layer = 0;
  29. }
  30. kpt.octave = (layer << 8) | (octv & 255);
  31. }
  32. }
  33. static
  34. void rotateKeyPoints(const vector<KeyPoint>& src, const Mat& H, float angle, vector<KeyPoint>& dst)
  35. {
  36. // suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage()
  37. vector<Point2f> srcCenters, dstCenters;
  38. KeyPoint::convert(src, srcCenters);
  39. perspectiveTransform(srcCenters, dstCenters, H);
  40. dst = src;
  41. for(size_t i = 0; i < dst.size(); i++)
  42. {
  43. dst[i].pt = dstCenters[i];
  44. float dstAngle = src[i].angle + angle;
  45. if(dstAngle >= 360.f)
  46. dstAngle -= 360.f;
  47. dst[i].angle = dstAngle;
  48. }
  49. }
  50. class DescriptorInvariance : public TestWithParam<String_FeatureDetector_DescriptorExtractor_Float_t>
  51. {
  52. protected:
  53. virtual void SetUp() {
  54. // Read test data
  55. const std::string filename = cvtest::TS::ptr()->get_data_path() + get<0>(GetParam());
  56. image0 = imread(filename);
  57. ASSERT_FALSE(image0.empty()) << "couldn't read input image";
  58. featureDetector = get<1>(GetParam())();
  59. descriptorExtractor = get<2>(GetParam())();
  60. minInliersRatio = get<3>(GetParam());
  61. }
  62. Ptr<FeatureDetector> featureDetector;
  63. Ptr<DescriptorExtractor> descriptorExtractor;
  64. float minInliersRatio;
  65. Mat image0;
  66. };
  67. typedef DescriptorInvariance DescriptorScaleInvariance;
  68. typedef DescriptorInvariance DescriptorRotationInvariance;
  69. TEST_P(DescriptorRotationInvariance, rotation)
  70. {
  71. Mat image1, mask1;
  72. const int borderSize = 16;
  73. Mat mask0(image0.size(), CV_8UC1, Scalar(0));
  74. mask0(Rect(borderSize, borderSize, mask0.cols - 2*borderSize, mask0.rows - 2*borderSize)).setTo(Scalar(255));
  75. vector<KeyPoint> keypoints0;
  76. Mat descriptors0;
  77. featureDetector->detect(image0, keypoints0, mask0);
  78. std::cout << "Keypoints: " << keypoints0.size() << std::endl;
  79. EXPECT_GE(keypoints0.size(), 15u);
  80. descriptorExtractor->compute(image0, keypoints0, descriptors0);
  81. BFMatcher bfmatcher(descriptorExtractor->defaultNorm());
  82. const float minIntersectRatio = 0.5f;
  83. const int maxAngle = 360, angleStep = 15;
  84. for(int angle = 0; angle < maxAngle; angle += angleStep)
  85. {
  86. Mat H = rotateImage(image0, mask0, static_cast<float>(angle), image1, mask1);
  87. vector<KeyPoint> keypoints1;
  88. rotateKeyPoints(keypoints0, H, static_cast<float>(angle), keypoints1);
  89. Mat descriptors1;
  90. descriptorExtractor->compute(image1, keypoints1, descriptors1);
  91. vector<DMatch> descMatches;
  92. bfmatcher.match(descriptors0, descriptors1, descMatches);
  93. int descInliersCount = 0;
  94. for(size_t m = 0; m < descMatches.size(); m++)
  95. {
  96. const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx];
  97. const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx];
  98. if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
  99. p1.pt, 0.5f * p1.size) >= minIntersectRatio)
  100. {
  101. descInliersCount++;
  102. }
  103. }
  104. float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
  105. EXPECT_GE(descInliersRatio, minInliersRatio);
  106. #if SHOW_DEBUG_LOG
  107. std::cout
  108. << "angle = " << angle
  109. << ", inliers = " << descInliersCount
  110. << ", descInliersRatio = " << static_cast<float>(descInliersCount) / keypoints0.size()
  111. << std::endl;
  112. #endif
  113. }
  114. }
  115. TEST_P(DescriptorScaleInvariance, scale)
  116. {
  117. vector<KeyPoint> keypoints0;
  118. featureDetector->detect(image0, keypoints0);
  119. std::cout << "Keypoints: " << keypoints0.size() << std::endl;
  120. EXPECT_GE(keypoints0.size(), 15u);
  121. Mat descriptors0;
  122. descriptorExtractor->compute(image0, keypoints0, descriptors0);
  123. BFMatcher bfmatcher(descriptorExtractor->defaultNorm());
  124. for(int scaleIdx = 1; scaleIdx <= 3; scaleIdx++)
  125. {
  126. float scale = 1.f + scaleIdx * 0.5f;
  127. Mat image1;
  128. resize(image0, image1, Size(), 1./scale, 1./scale, INTER_LINEAR_EXACT);
  129. vector<KeyPoint> keypoints1;
  130. scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale);
  131. if (featureDetector->getDefaultName() == "Feature2D.SIFT")
  132. {
  133. SetSuitableSIFTOctave(keypoints1);
  134. }
  135. Mat descriptors1;
  136. descriptorExtractor->compute(image1, keypoints1, descriptors1);
  137. vector<DMatch> descMatches;
  138. bfmatcher.match(descriptors0, descriptors1, descMatches);
  139. const float minIntersectRatio = 0.5f;
  140. int descInliersCount = 0;
  141. for(size_t m = 0; m < descMatches.size(); m++)
  142. {
  143. const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx];
  144. const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx];
  145. if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
  146. p1.pt, 0.5f * p1.size) >= minIntersectRatio)
  147. {
  148. descInliersCount++;
  149. }
  150. }
  151. float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
  152. EXPECT_GE(descInliersRatio, minInliersRatio);
  153. #if SHOW_DEBUG_LOG
  154. std::cout
  155. << "scale = " << scale
  156. << ", inliers = " << descInliersCount
  157. << ", descInliersRatio = " << static_cast<float>(descInliersCount) / keypoints0.size()
  158. << std::endl;
  159. #endif
  160. }
  161. }
  162. #undef SHOW_DEBUG_LOG
  163. }} // namespace
  164. namespace std {
  165. using namespace opencv_test;
  166. static inline void PrintTo(const String_FeatureDetector_DescriptorExtractor_Float_t& v, std::ostream* os)
  167. {
  168. *os << "(\"" << get<0>(v)
  169. << "\", " << get<3>(v)
  170. << ")";
  171. }
  172. } // namespace