test_descriptors_invariance.impl.hpp 6.6 KB

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