diff --git a/modules/features2d/test/test_rotation_and_scale_invariance.cpp b/modules/features2d/test/test_rotation_and_scale_invariance.cpp index f1f8ff490..c5aa1f7b6 100644 --- a/modules/features2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/features2d/test/test_rotation_and_scale_invariance.cpp @@ -53,7 +53,7 @@ const string IMAGE_BIKES = "/detectors_descriptors_evaluation/images_datasets/bi static Mat generateHomography(float angle) { - // angle - rotation around Oz in degrees + // angle - rotation around Oz in degrees float angleRadian = angle * CV_PI / 180.; Mat H = Mat::eye(3, 3, CV_32FC1); H.at(0,0) = H.at(1,1) = std::cos(angleRadian); @@ -66,7 +66,7 @@ Mat generateHomography(float angle) static Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) { - // angle - rotation around Oz in degrees + // angle - rotation around Oz in degrees float diag = std::sqrt(static_cast(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows)); Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up LUShift.at(0,2) = -srcImage.cols/2; @@ -85,6 +85,32 @@ Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) return H; } +void rotateKeyPoints(const vector& src, const Mat& H, float angle, vector& dst) +{ + // suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage() + vector srcCenters, dstCenters; + KeyPoint::convert(src, srcCenters); + + perspectiveTransform(srcCenters, dstCenters, H); + + dst = src; + for(size_t i = 0; i < dst.size(); i++) + { + dst[i].pt = dstCenters[i]; + float dstAngle = src[i].angle + angle; + if(dstAngle >= 360.f) + dstAngle -= 360.f; + dst[i].angle = dstAngle; + } +} + +void scaleKeyPoints(const vector& src, vector& dst, float scale) +{ + dst.resize(src.size()); + for(size_t i = 0; i < src.size(); i++) + dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); +} + static float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1) { @@ -119,45 +145,45 @@ float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r return intersectArea / unionArea; } -static +static void matchKeyPoints(const vector& keypoints0, const Mat& H, - const vector& keypoints1, - vector& matches) + const vector& keypoints1, + vector& matches) { - vector points0; + vector points0; KeyPoint::convert(keypoints0, points0); Mat points0t; - if(H.empty()) - points0t = Mat(points0); - else - perspectiveTransform(Mat(points0), points0t, H); + if(H.empty()) + points0t = Mat(points0); + else + perspectiveTransform(Mat(points0), points0t, H); - matches.clear(); - vector usedMask(keypoints1.size(), 0); - for(size_t i0 = 0; i0 < keypoints0.size(); i0++) - { - int nearestPointIndex = -1; + matches.clear(); + vector usedMask(keypoints1.size(), 0); + for(size_t i0 = 0; i0 < keypoints0.size(); i0++) + { + int nearestPointIndex = -1; float maxIntersectRatio = 0.f; const float r0 = 0.5f * keypoints0[i0].size; for(size_t i1 = 0; i1 < keypoints1.size(); i1++) { - if(nearestPointIndex >= 0 && usedMask[i1]) - continue; + if(nearestPointIndex >= 0 && usedMask[i1]) + continue; - float r1 = 0.5f * keypoints1[i1].size; + float r1 = 0.5f * keypoints1[i1].size; float intersectRatio = calcIntersectRatio(points0t.at(i0), r0, keypoints1[i1].pt, r1); if(intersectRatio > maxIntersectRatio) { - maxIntersectRatio = intersectRatio; - nearestPointIndex = i1; + maxIntersectRatio = intersectRatio; + nearestPointIndex = i1; } } - matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); - if(nearestPointIndex >= 0) - usedMask[nearestPointIndex] = 1; - } + matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); + if(nearestPointIndex >= 0) + usedMask[nearestPointIndex] = 1; + } } class DetectorRotationInvarianceTest : public cvtest::BaseTest @@ -166,134 +192,13 @@ public: DetectorRotationInvarianceTest(const Ptr& _featureDetector, float _minKeyPointMatchesRatio, float _minAngleInliersRatio) : - featureDetector(_featureDetector), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minAngleInliersRatio(_minAngleInliersRatio) + featureDetector(_featureDetector), + minKeyPointMatchesRatio(_minKeyPointMatchesRatio), + minAngleInliersRatio(_minAngleInliersRatio) { CV_Assert(!featureDetector.empty()); } -protected: - - void run(int) - { - const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; - - // Read test data - Mat image0 = imread(imageFilename), image1, mask1; - if(image0.empty()) - { - ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); - return; - } - - vector keypoints0; - featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - - const int maxAngle = 360, angleStep = 15; - for(int angle = 0; angle < maxAngle; angle += angleStep) - { - Mat H = rotateImage(image0, angle, image1, mask1); - - vector keypoints1; - featureDetector->detect(image1, keypoints1, mask1); - - vector matches; - matchKeyPoints(keypoints0, H, keypoints1, matches); - - int angleInliersCount = 0; - - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < matches.size(); m++) - { - if(matches[m].distance < minIntersectRatio) - continue; - - keyPointMatchesCount++; - - // Check does this inlier have consistent angles - const float maxAngleDiff = 15.f; // grad - float angle0 = keypoints0[matches[m].queryIdx].angle; - float angle1 = keypoints1[matches[m].trainIdx].angle; - if(angle0 == -1 || angle1 == -1) - CV_Error(CV_StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n"); - CV_Assert(angle0 >= 0.f && angle0 < 360.f); - CV_Assert(angle1 >= 0.f && angle1 < 360.f); - - float rotAngle0 = angle0 + angle; - if(rotAngle0 >= 360.f) - rotAngle0 -= 360.f; - - float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1); - angleDiff = std::min(angleDiff, static_cast(360.f - angleDiff)); - CV_Assert(angleDiff >= 0.f); - bool isAngleCorrect = angleDiff < maxAngleDiff; - if(isAngleCorrect) - angleInliersCount++; - } - - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints0.size(); - if(keyPointMatchesRatio < minKeyPointMatchesRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", - keyPointMatchesRatio, minKeyPointMatchesRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - - if(keyPointMatchesCount) - { - float angleInliersRatio = static_cast(angleInliersCount) / keyPointMatchesCount; - if(angleInliersRatio < minAngleInliersRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n", - angleInliersRatio, minAngleInliersRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } -#if SHOW_DEBUG_LOG - std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - angleInliersRatio " << static_cast(angleInliersCount) / keyPointMatchesCount << std::endl; -#endif - } - ts->set_failed_test_info( cvtest::TS::OK ); - } - - Ptr featureDetector; - float minKeyPointMatchesRatio; - float minAngleInliersRatio; -}; - -void scaleKeyPoints(const vector& src, vector& dst, float scale) -{ - dst.resize(src.size()); - for(size_t i = 0; i < src.size(); i++) - dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); -} - -class DescriptorRotationInvarianceTest : public cvtest::BaseTest -{ -public: - DescriptorRotationInvarianceTest(const Ptr& _featureDetector, - const Ptr& _descriptorExtractor, - int _normType, - float _minKeyPointMatchesRatio, - float _minDescInliersRatio) : - featureDetector(_featureDetector), - descriptorExtractor(_descriptorExtractor), - normType(_normType), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minDescInliersRatio(_minDescInliersRatio) - { - CV_Assert(!featureDetector.empty()); - CV_Assert(!descriptorExtractor.empty()); - } - protected: void run(int) @@ -310,13 +215,9 @@ protected: } vector keypoints0; - Mat descriptors0; featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - descriptorExtractor->compute(image0, keypoints0, descriptors0); - - BFMatcher bfmatcher(normType); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) @@ -324,31 +225,42 @@ protected: Mat H = rotateImage(image0, angle, image1, mask1); vector keypoints1; - Mat descriptors1; featureDetector->detect(image1, keypoints1, mask1); - descriptorExtractor->compute(image1, keypoints1, descriptors1); - vector descMatches; - bfmatcher.match(descriptors0, descriptors1, descMatches); + vector matches; + matchKeyPoints(keypoints0, H, keypoints1, matches); - vector keyPointMatches; - matchKeyPoints(keypoints0, H, keypoints1, keyPointMatches); + int angleInliersCount = 0; - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < keyPointMatches.size(); m++) - { - if(keyPointMatches[m].distance >= minIntersectRatio) - keyPointMatchesCount++; - } - int descInliersCount = 0; - for(size_t m = 0; m < descMatches.size(); m++) + const float minIntersectRatio = 0.5f; + int keyPointMatchesCount = 0; + for(size_t m = 0; m < matches.size(); m++) { - int queryIdx = descMatches[m].queryIdx; - if(keyPointMatches[queryIdx].distance >= minIntersectRatio && - descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) - descInliersCount++; - } + if(matches[m].distance < minIntersectRatio) + continue; + + keyPointMatchesCount++; + + // Check does this inlier have consistent angles + const float maxAngleDiff = 15.f; // grad + float angle0 = keypoints0[matches[m].queryIdx].angle; + float angle1 = keypoints1[matches[m].trainIdx].angle; + if(angle0 == -1 || angle1 == -1) + CV_Error(CV_StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n"); + CV_Assert(angle0 >= 0.f && angle0 < 360.f); + CV_Assert(angle1 >= 0.f && angle1 < 360.f); + + float rotAngle0 = angle0 + angle; + if(rotAngle0 >= 360.f) + rotAngle0 -= 360.f; + + float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1); + angleDiff = std::min(angleDiff, static_cast(360.f - angleDiff)); + CV_Assert(angleDiff >= 0.f); + bool isAngleCorrect = angleDiff < maxAngleDiff; + if(isAngleCorrect) + angleInliersCount++; + } float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints0.size(); if(keyPointMatchesRatio < minKeyPointMatchesRatio) @@ -359,31 +271,115 @@ protected: return; } - if(keyPointMatchesCount) + if(keyPointMatchesCount) { - float descInliersRatio = static_cast(descInliersCount) / keyPointMatchesCount; - if(descInliersRatio < minDescInliersRatio) + float angleInliersRatio = static_cast(angleInliersCount) / keyPointMatchesCount; + if(angleInliersRatio < minAngleInliersRatio) { - ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", - descInliersRatio, minDescInliersRatio); + ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n", + angleInliersRatio, minAngleInliersRatio); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } } #if SHOW_DEBUG_LOG std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - descInliersRatio " << static_cast(descInliersCount) / keyPointMatchesCount << std::endl; + << " - angleInliersRatio " << static_cast(angleInliersCount) / keyPointMatchesCount << std::endl; #endif } ts->set_failed_test_info( cvtest::TS::OK ); } Ptr featureDetector; - Ptr descriptorExtractor; + float minKeyPointMatchesRatio; + float minAngleInliersRatio; +}; + +class DescriptorRotationInvarianceTest : public cvtest::BaseTest +{ +public: + DescriptorRotationInvarianceTest(const Ptr& _featureDetector, + const Ptr& _descriptorExtractor, + int _normType, + float _minDescInliersRatio) : + featureDetector(_featureDetector), + descriptorExtractor(_descriptorExtractor), + normType(_normType), + minDescInliersRatio(_minDescInliersRatio) + { + CV_Assert(!featureDetector.empty()); + CV_Assert(!descriptorExtractor.empty()); + } + +protected: + + void run(int) + { + const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; + + // Read test data + Mat image0 = imread(imageFilename), image1, mask1; + if(image0.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + vector keypoints0; + Mat descriptors0; + featureDetector->detect(image0, keypoints0); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + descriptorExtractor->compute(image0, keypoints0, descriptors0); + + BFMatcher bfmatcher(normType); + + const float minIntersectRatio = 0.5f; + const int maxAngle = 360, angleStep = 15; + for(int angle = 0; angle < maxAngle; angle += angleStep) + { + Mat H = rotateImage(image0, angle, image1, mask1); + + vector keypoints1; + rotateKeyPoints(keypoints0, H, angle, keypoints1); + Mat descriptors1; + descriptorExtractor->compute(image1, keypoints1, descriptors1); + + vector descMatches; + bfmatcher.match(descriptors0, descriptors1, descMatches); + + int descInliersCount = 0; + for(size_t m = 0; m < descMatches.size(); m++) + { + const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx]; + const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx]; + if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, + p1.pt, 0.5f * p1.size) >= minIntersectRatio) + { + descInliersCount++; + } + } + + float descInliersRatio = static_cast(descInliersCount) / keypoints0.size(); + if(descInliersRatio < minDescInliersRatio) + { + ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", + descInliersRatio, minDescInliersRatio); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +#if SHOW_DEBUG_LOG + std::cout << "descInliersRatio " << static_cast(descInliersCount) / keypoints0.size() << std::endl; +#endif + } + ts->set_failed_test_info( cvtest::TS::OK ); + } + + Ptr featureDetector; + Ptr descriptorExtractor; int normType; - float minKeyPointMatchesRatio; - float minDescInliersRatio; - + float minDescInliersRatio; }; class DetectorScaleInvarianceTest : public cvtest::BaseTest @@ -392,9 +388,9 @@ public: DetectorScaleInvarianceTest(const Ptr& _featureDetector, float _minKeyPointMatchesRatio, float _minScaleInliersRatio) : - featureDetector(_featureDetector), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minScaleInliersRatio(_minScaleInliersRatio) + featureDetector(_featureDetector), + minKeyPointMatchesRatio(_minKeyPointMatchesRatio), + minScaleInliersRatio(_minScaleInliersRatio) { CV_Assert(!featureDetector.empty()); } @@ -403,7 +399,7 @@ protected: void run(int) { - const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; + const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; // Read test data Mat image0 = imread(imageFilename); @@ -417,58 +413,59 @@ protected: vector keypoints0; featureDetector->detect(image0, keypoints0); if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - for(int scale = 2; scale <= 4; scale++) + for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) { - Mat image1; - resize(image0, image1, Size(), 1./scale, 1./scale); + float scale = 1.f + scaleIdx * 0.5f; + Mat image1; + resize(image0, image1, Size(), 1./scale, 1./scale); vector keypoints1, osiKeypoints1; // osi - original size image featureDetector->detect(image1, keypoints1); - if(keypoints1.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + if(keypoints1.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - if(keypoints1.size() > keypoints0.size()) - { + if(keypoints1.size() > keypoints0.size()) + { ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " - "It gives more points count in an image of the smaller size.\n" - "original size (%d, %d), keypoints count = %d\n" - "reduced size (%d, %d), keypoints count = %d\n", - image0.cols, image0.rows, keypoints0.size(), - image1.cols, image1.rows, keypoints1.size()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + "It gives more points count in an image of the smaller size.\n" + "original size (%d, %d), keypoints count = %d\n" + "reduced size (%d, %d), keypoints count = %d\n", + image0.cols, image0.rows, keypoints0.size(), + image1.cols, image1.rows, keypoints1.size()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); return; } - scaleKeyPoints(keypoints1, osiKeypoints1, scale); + scaleKeyPoints(keypoints1, osiKeypoints1, scale); - vector matches; - // image1 is query image (it's reduced image0) - // image0 is train image - matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); + vector matches; + // image1 is query image (it's reduced image0) + // image0 is train image + matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - int scaleInliersCount = 0; + const float minIntersectRatio = 0.5f; + int keyPointMatchesCount = 0; + int scaleInliersCount = 0; - for(size_t m = 0; m < matches.size(); m++) + for(size_t m = 0; m < matches.size(); m++) { - if(matches[m].distance < minIntersectRatio) - continue; + if(matches[m].distance < minIntersectRatio) + continue; - keyPointMatchesCount++; + keyPointMatchesCount++; - // Check does this inlier have consistent sizes + // Check does this inlier have consistent sizes const float maxSizeDiff = 0.8;//0.9f; // grad - float size0 = keypoints0[matches[m].trainIdx].size; + float size0 = keypoints0[matches[m].trainIdx].size; float size1 = osiKeypoints1[matches[m].queryIdx].size; - CV_Assert(size0 > 0 && size1 > 0); - if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) + CV_Assert(size0 > 0 && size1 > 0); + if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) scaleInliersCount++; } - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); + float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); if(keyPointMatchesRatio < minKeyPointMatchesRatio) { ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", @@ -477,9 +474,9 @@ protected: return; } - if(keyPointMatchesCount) + if(keyPointMatchesCount) { - float scaleInliersRatio = static_cast(scaleInliersCount) / keyPointMatchesCount; + float scaleInliersRatio = static_cast(scaleInliersCount) / keyPointMatchesCount; if(scaleInliersRatio < minScaleInliersRatio) { ts->printf(cvtest::TS::LOG, "Incorrect scaleInliersRatio: curr = %f, min = %f.\n", @@ -490,21 +487,8 @@ protected: } #if SHOW_DEBUG_LOG std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - scaleInliersRatio " << static_cast(scaleInliersCount) / keyPointMatchesCount << std::endl; + << " - scaleInliersRatio " << static_cast(scaleInliersCount) / keyPointMatchesCount << std::endl; #endif - /*vector filteredMatches; - for(size_t i = 0; i < matches.size(); i++) - { - if(matches[i].distance >= minIntersectRatio) - filteredMatches.push_back(matches[i]); - } - - Mat out; - namedWindow("out", CV_WINDOW_NORMAL); - drawMatches(image1, keypoints1, image0, keypoints0, filteredMatches, out, - Scalar::all(-1), Scalar(-1), vector(), DrawMatchesFlags::DEFAULT + DrawMatchesFlags::DRAW_RICH_KEYPOINTS); - imshow("out", out); - waitKey();*/ } ts->set_failed_test_info( cvtest::TS::OK ); } @@ -518,25 +502,23 @@ class DescriptorScaleInvarianceTest : public cvtest::BaseTest { public: DescriptorScaleInvarianceTest(const Ptr& _featureDetector, - const Ptr& _descriptorExtractor, - int _normType, - float _minKeyPointMatchesRatio, + const Ptr& _descriptorExtractor, + int _normType, float _minDescInliersRatio) : - featureDetector(_featureDetector), - descriptorExtractor(_descriptorExtractor), - normType(_normType), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minDescInliersRatio(_minDescInliersRatio) + featureDetector(_featureDetector), + descriptorExtractor(_descriptorExtractor), + normType(_normType), + minDescInliersRatio(_minDescInliersRatio) { CV_Assert(!featureDetector.empty()); - CV_Assert(!descriptorExtractor.empty()); + CV_Assert(!descriptorExtractor.empty()); } protected: void run(int) { - const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; + const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; // Read test data Mat image0 = imread(imageFilename); @@ -549,145 +531,126 @@ protected: vector keypoints0; featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - Mat descriptors0; - descriptorExtractor->compute(image0, keypoints0, descriptors0); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + Mat descriptors0; + descriptorExtractor->compute(image0, keypoints0, descriptors0); - BFMatcher bfmatcher(normType); - for(int scale = 2; scale <= 4; scale++) + BFMatcher bfmatcher(normType); + for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) { - Mat image1; - resize(image0, image1, Size(), 1./scale, 1./scale); + float scale = 1.f + scaleIdx * 0.5f; - vector keypoints1, osiKeypoints1; // osi - original size image - featureDetector->detect(image1, keypoints1); - if(keypoints1.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - if(keypoints1.size() > keypoints0.size() ) - { - ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " - "It gives more points count in an image of the smaller size.\n" - "original size (%d, %d), keypoints count = %d\n" - "reduced size (%d, %d), keypoints count = %d\n", - image0.cols, image0.rows, keypoints0.size(), - image1.cols, image1.rows, keypoints1.size()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); - return; + Mat image1; + resize(image0, image1, Size(), 1./scale, 1./scale); + + vector keypoints1; + scaleKeyPoints(keypoints0, keypoints1, 1./scale); + Mat descriptors1; + descriptorExtractor->compute(image1, keypoints1, descriptors1); + + vector descMatches; + bfmatcher.match(descriptors0, descriptors1, descMatches); + + const float minIntersectRatio = 0.5f; + int descInliersCount = 0; + for(size_t m = 0; m < descMatches.size(); m++) + { + const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx]; + const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx]; + if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, + p1.pt, 0.5f * p1.size) >= minIntersectRatio) + { + descInliersCount++; + } } - Mat descriptors1; - descriptorExtractor->compute(image1, keypoints1, descriptors1); - - vector keyPointMatches, descMatches; - // image1 is query image (it's reduced image0) - // image0 is train image - bfmatcher.match(descriptors1, descriptors0, descMatches); - - scaleKeyPoints(keypoints1, osiKeypoints1, scale); - matchKeyPoints(osiKeypoints1, Mat(), keypoints0, keyPointMatches); - - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < keyPointMatches.size(); m++) - { - if(keyPointMatches[m].distance >= minIntersectRatio) - keyPointMatchesCount++; - } - int descInliersCount = 0; - for(size_t m = 0; m < descMatches.size(); m++) + float descInliersRatio = static_cast(descInliersCount) / keypoints0.size(); + if(descInliersRatio < minDescInliersRatio) { - int queryIdx = descMatches[m].queryIdx; - if(keyPointMatches[queryIdx].distance >= minIntersectRatio && - descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) - descInliersCount++; - } - - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); - if(keyPointMatchesRatio < minKeyPointMatchesRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", - keyPointMatchesRatio, minKeyPointMatchesRatio); + ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", + descInliersRatio, minDescInliersRatio); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } - - if(keyPointMatchesCount) - { - float descInliersRatio = static_cast(descInliersCount) / keyPointMatchesCount; - if(descInliersRatio < minDescInliersRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", - descInliersRatio, minDescInliersRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } #if SHOW_DEBUG_LOG - std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - descInliersRatio " << static_cast(descInliersCount) / keyPointMatchesCount << std::endl; + std::cout << "descInliersRatio " << static_cast(descInliersCount) / keypoints0.size() << std::endl; #endif } ts->set_failed_test_info( cvtest::TS::OK ); } Ptr featureDetector; - Ptr descriptorExtractor; - int normType; + Ptr descriptorExtractor; + int normType; float minKeyPointMatchesRatio; float minDescInliersRatio; }; // Tests registration -// Detector's rotation invariance check +/* + * Detector's rotation invariance check + */ TEST(Features2d_RotationInvariance_Detector_ORB, regression) { DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.ORB"), - 0.45f, - 0.75f); + 0.47f, + 0.77f); test.safe_run(); } -// Descriptors's rotation invariance check +/* + * Descriptors's rotation invariance check + */ TEST(Features2d_RotationInvariance_Descriptor_ORB, regression) { DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.ORB"), Algorithm::create("Feature2D.ORB"), NORM_HAMMING, - 0.45f, - 0.53f); + 0.99f); test.safe_run(); } -// TODO: Uncomment test for FREAK when it will work; add test for scale invariance for FREAK //TEST(Features2d_RotationInvariance_Descriptor_FREAK, regression) //{ -// DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.ORB"), -// Algorithm::create("Feature2D.FREAK"), -// NORM_HAMMING(?), -// 0.45f, -// 0.?f); +// DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.ORB"), +// Algorithm::create("Feature2D.FREAK"), +// NORM_HAMMING, +// 0.f); // test.safe_run(); //} -/* TODO: Why ORB has bad scale invariance in this tests? -// Detector's scale invariance check -TEST(Features2d_ScaleInvariance_Detector_ORB, regression) -{ - DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.ORB"), - 0.13f, - 0.0f); - test.safe_run(); -} +/* + * Detector's scale invariance check + */ -// Descriptor's scale invariance check -TEST(Features2d_ScaleInvariance_Descriptor_ORB, regression) -{ - DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.ORB"), - Algorithm::create("Feature2D.ORB"), - NORM_HAMMING, - 0.13f, - 0.36f); - test.safe_run(); -}*/ \ No newline at end of file +//TEST(Features2d_ScaleInvariance_Detector_ORB, regression) +//{ +// DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.ORB"), +// 0.22f, +// 0.83f); +// test.safe_run(); +//} + +/* + * Descriptor's scale invariance check + */ + +//TEST(Features2d_ScaleInvariance_Descriptor_ORB, regression) +//{ +// DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.ORB"), +// Algorithm::create("Feature2D.ORB"), +// NORM_HAMMING, +// 0.01f); +// test.safe_run(); +//} + +//TEST(Features2d_ScaleInvariance_Descriptor_FREAK, regression) +//{ +// DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.ORB"), +// Algorithm::create("Feature2D.FREAK"), +// NORM_HAMMING, +// 0.01f); +// test.safe_run(); +//} diff --git a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp index ccc93c269..4563d0513 100644 --- a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp +++ b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp @@ -53,7 +53,7 @@ const string IMAGE_BIKES = "/detectors_descriptors_evaluation/images_datasets/bi static Mat generateHomography(float angle) { - // angle - rotation around Oz in degrees + // angle - rotation around Oz in degrees float angleRadian = angle * CV_PI / 180.; Mat H = Mat::eye(3, 3, CV_32FC1); H.at(0,0) = H.at(1,1) = std::cos(angleRadian); @@ -66,7 +66,7 @@ Mat generateHomography(float angle) static Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) { - // angle - rotation around Oz in degrees + // angle - rotation around Oz in degrees float diag = std::sqrt(static_cast(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows)); Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up LUShift.at(0,2) = -srcImage.cols/2; @@ -85,6 +85,32 @@ Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) return H; } +void rotateKeyPoints(const vector& src, const Mat& H, float angle, vector& dst) +{ + // suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage() + vector srcCenters, dstCenters; + KeyPoint::convert(src, srcCenters); + + perspectiveTransform(srcCenters, dstCenters, H); + + dst = src; + for(size_t i = 0; i < dst.size(); i++) + { + dst[i].pt = dstCenters[i]; + float dstAngle = src[i].angle + angle; + if(dstAngle >= 360.f) + dstAngle -= 360.f; + dst[i].angle = dstAngle; + } +} + +void scaleKeyPoints(const vector& src, vector& dst, float scale) +{ + dst.resize(src.size()); + for(size_t i = 0; i < src.size(); i++) + dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); +} + static float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1) { @@ -119,45 +145,45 @@ float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r return intersectArea / unionArea; } -static +static void matchKeyPoints(const vector& keypoints0, const Mat& H, - const vector& keypoints1, - vector& matches) + const vector& keypoints1, + vector& matches) { - vector points0; + vector points0; KeyPoint::convert(keypoints0, points0); Mat points0t; - if(H.empty()) - points0t = Mat(points0); - else - perspectiveTransform(Mat(points0), points0t, H); + if(H.empty()) + points0t = Mat(points0); + else + perspectiveTransform(Mat(points0), points0t, H); - matches.clear(); - vector usedMask(keypoints1.size(), 0); - for(size_t i0 = 0; i0 < keypoints0.size(); i0++) - { - int nearestPointIndex = -1; + matches.clear(); + vector usedMask(keypoints1.size(), 0); + for(size_t i0 = 0; i0 < keypoints0.size(); i0++) + { + int nearestPointIndex = -1; float maxIntersectRatio = 0.f; const float r0 = 0.5f * keypoints0[i0].size; for(size_t i1 = 0; i1 < keypoints1.size(); i1++) { - if(nearestPointIndex >= 0 && usedMask[i1]) - continue; + if(nearestPointIndex >= 0 && usedMask[i1]) + continue; - float r1 = 0.5f * keypoints1[i1].size; + float r1 = 0.5f * keypoints1[i1].size; float intersectRatio = calcIntersectRatio(points0t.at(i0), r0, keypoints1[i1].pt, r1); if(intersectRatio > maxIntersectRatio) { - maxIntersectRatio = intersectRatio; - nearestPointIndex = i1; + maxIntersectRatio = intersectRatio; + nearestPointIndex = i1; } } - matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); - if(nearestPointIndex >= 0) - usedMask[nearestPointIndex] = 1; - } + matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); + if(nearestPointIndex >= 0) + usedMask[nearestPointIndex] = 1; + } } class DetectorRotationInvarianceTest : public cvtest::BaseTest @@ -166,134 +192,13 @@ public: DetectorRotationInvarianceTest(const Ptr& _featureDetector, float _minKeyPointMatchesRatio, float _minAngleInliersRatio) : - featureDetector(_featureDetector), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minAngleInliersRatio(_minAngleInliersRatio) + featureDetector(_featureDetector), + minKeyPointMatchesRatio(_minKeyPointMatchesRatio), + minAngleInliersRatio(_minAngleInliersRatio) { CV_Assert(!featureDetector.empty()); } -protected: - - void run(int) - { - const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; - - // Read test data - Mat image0 = imread(imageFilename), image1, mask1; - if(image0.empty()) - { - ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); - return; - } - - vector keypoints0; - featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - - const int maxAngle = 360, angleStep = 15; - for(int angle = 0; angle < maxAngle; angle += angleStep) - { - Mat H = rotateImage(image0, angle, image1, mask1); - - vector keypoints1; - featureDetector->detect(image1, keypoints1, mask1); - - vector matches; - matchKeyPoints(keypoints0, H, keypoints1, matches); - - int angleInliersCount = 0; - - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < matches.size(); m++) - { - if(matches[m].distance < minIntersectRatio) - continue; - - keyPointMatchesCount++; - - // Check does this inlier have consistent angles - const float maxAngleDiff = 15.f; // grad - float angle0 = keypoints0[matches[m].queryIdx].angle; - float angle1 = keypoints1[matches[m].trainIdx].angle; - if(angle0 == -1 || angle1 == -1) - CV_Error(CV_StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n"); - CV_Assert(angle0 >= 0.f && angle0 < 360.f); - CV_Assert(angle1 >= 0.f && angle1 < 360.f); - - float rotAngle0 = angle0 + angle; - if(rotAngle0 >= 360.f) - rotAngle0 -= 360.f; - - float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1); - angleDiff = std::min(angleDiff, static_cast(360.f - angleDiff)); - CV_Assert(angleDiff >= 0.f); - bool isAngleCorrect = angleDiff < maxAngleDiff; - if(isAngleCorrect) - angleInliersCount++; - } - - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints0.size(); - if(keyPointMatchesRatio < minKeyPointMatchesRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", - keyPointMatchesRatio, minKeyPointMatchesRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - - if(keyPointMatchesCount) - { - float angleInliersRatio = static_cast(angleInliersCount) / keyPointMatchesCount; - if(angleInliersRatio < minAngleInliersRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n", - angleInliersRatio, minAngleInliersRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } -#if SHOW_DEBUG_LOG - std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - angleInliersRatio " << static_cast(angleInliersCount) / keyPointMatchesCount << std::endl; -#endif - } - ts->set_failed_test_info( cvtest::TS::OK ); - } - - Ptr featureDetector; - float minKeyPointMatchesRatio; - float minAngleInliersRatio; -}; - -void scaleKeyPoints(const vector& src, vector& dst, float scale) -{ - dst.resize(src.size()); - for(size_t i = 0; i < src.size(); i++) - dst[i] = KeyPoint(src[i].pt.x * scale, src[i].pt.y * scale, src[i].size * scale); -} - -class DescriptorRotationInvarianceTest : public cvtest::BaseTest -{ -public: - DescriptorRotationInvarianceTest(const Ptr& _featureDetector, - const Ptr& _descriptorExtractor, - int _normType, - float _minKeyPointMatchesRatio, - float _minDescInliersRatio) : - featureDetector(_featureDetector), - descriptorExtractor(_descriptorExtractor), - normType(_normType), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minDescInliersRatio(_minDescInliersRatio) - { - CV_Assert(!featureDetector.empty()); - CV_Assert(!descriptorExtractor.empty()); - } - protected: void run(int) @@ -310,13 +215,9 @@ protected: } vector keypoints0; - Mat descriptors0; featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - descriptorExtractor->compute(image0, keypoints0, descriptors0); - - BFMatcher bfmatcher(normType); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) @@ -324,31 +225,42 @@ protected: Mat H = rotateImage(image0, angle, image1, mask1); vector keypoints1; - Mat descriptors1; featureDetector->detect(image1, keypoints1, mask1); - descriptorExtractor->compute(image1, keypoints1, descriptors1); - vector descMatches; - bfmatcher.match(descriptors0, descriptors1, descMatches); + vector matches; + matchKeyPoints(keypoints0, H, keypoints1, matches); - vector keyPointMatches; - matchKeyPoints(keypoints0, H, keypoints1, keyPointMatches); + int angleInliersCount = 0; - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < keyPointMatches.size(); m++) - { - if(keyPointMatches[m].distance >= minIntersectRatio) - keyPointMatchesCount++; - } - int descInliersCount = 0; - for(size_t m = 0; m < descMatches.size(); m++) + const float minIntersectRatio = 0.5f; + int keyPointMatchesCount = 0; + for(size_t m = 0; m < matches.size(); m++) { - int queryIdx = descMatches[m].queryIdx; - if(keyPointMatches[queryIdx].distance >= minIntersectRatio && - descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) - descInliersCount++; - } + if(matches[m].distance < minIntersectRatio) + continue; + + keyPointMatchesCount++; + + // Check does this inlier have consistent angles + const float maxAngleDiff = 15.f; // grad + float angle0 = keypoints0[matches[m].queryIdx].angle; + float angle1 = keypoints1[matches[m].trainIdx].angle; + if(angle0 == -1 || angle1 == -1) + CV_Error(CV_StsBadArg, "Given FeatureDetector is not rotation invariant, it can not be tested here.\n"); + CV_Assert(angle0 >= 0.f && angle0 < 360.f); + CV_Assert(angle1 >= 0.f && angle1 < 360.f); + + float rotAngle0 = angle0 + angle; + if(rotAngle0 >= 360.f) + rotAngle0 -= 360.f; + + float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1); + angleDiff = std::min(angleDiff, static_cast(360.f - angleDiff)); + CV_Assert(angleDiff >= 0.f); + bool isAngleCorrect = angleDiff < maxAngleDiff; + if(isAngleCorrect) + angleInliersCount++; + } float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints0.size(); if(keyPointMatchesRatio < minKeyPointMatchesRatio) @@ -359,31 +271,115 @@ protected: return; } - if(keyPointMatchesCount) + if(keyPointMatchesCount) { - float descInliersRatio = static_cast(descInliersCount) / keyPointMatchesCount; - if(descInliersRatio < minDescInliersRatio) + float angleInliersRatio = static_cast(angleInliersCount) / keyPointMatchesCount; + if(angleInliersRatio < minAngleInliersRatio) { - ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", - descInliersRatio, minDescInliersRatio); + ts->printf(cvtest::TS::LOG, "Incorrect angleInliersRatio: curr = %f, min = %f.\n", + angleInliersRatio, minAngleInliersRatio); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } } #if SHOW_DEBUG_LOG std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - descInliersRatio " << static_cast(descInliersCount) / keyPointMatchesCount << std::endl; + << " - angleInliersRatio " << static_cast(angleInliersCount) / keyPointMatchesCount << std::endl; #endif } ts->set_failed_test_info( cvtest::TS::OK ); } Ptr featureDetector; - Ptr descriptorExtractor; + float minKeyPointMatchesRatio; + float minAngleInliersRatio; +}; + +class DescriptorRotationInvarianceTest : public cvtest::BaseTest +{ +public: + DescriptorRotationInvarianceTest(const Ptr& _featureDetector, + const Ptr& _descriptorExtractor, + int _normType, + float _minDescInliersRatio) : + featureDetector(_featureDetector), + descriptorExtractor(_descriptorExtractor), + normType(_normType), + minDescInliersRatio(_minDescInliersRatio) + { + CV_Assert(!featureDetector.empty()); + CV_Assert(!descriptorExtractor.empty()); + } + +protected: + + void run(int) + { + const string imageFilename = string(ts->get_data_path()) + IMAGE_TSUKUBA; + + // Read test data + Mat image0 = imread(imageFilename), image1, mask1; + if(image0.empty()) + { + ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imageFilename.c_str()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + vector keypoints0; + Mat descriptors0; + featureDetector->detect(image0, keypoints0); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + descriptorExtractor->compute(image0, keypoints0, descriptors0); + + BFMatcher bfmatcher(normType); + + const float minIntersectRatio = 0.5f; + const int maxAngle = 360, angleStep = 15; + for(int angle = 0; angle < maxAngle; angle += angleStep) + { + Mat H = rotateImage(image0, angle, image1, mask1); + + vector keypoints1; + rotateKeyPoints(keypoints0, H, angle, keypoints1); + Mat descriptors1; + descriptorExtractor->compute(image1, keypoints1, descriptors1); + + vector descMatches; + bfmatcher.match(descriptors0, descriptors1, descMatches); + + int descInliersCount = 0; + for(size_t m = 0; m < descMatches.size(); m++) + { + const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx]; + const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx]; + if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, + p1.pt, 0.5f * p1.size) >= minIntersectRatio) + { + descInliersCount++; + } + } + + float descInliersRatio = static_cast(descInliersCount) / keypoints0.size(); + if(descInliersRatio < minDescInliersRatio) + { + ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", + descInliersRatio, minDescInliersRatio); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +#if SHOW_DEBUG_LOG + std::cout << "descInliersRatio " << static_cast(descInliersCount) / keypoints0.size() << std::endl; +#endif + } + ts->set_failed_test_info( cvtest::TS::OK ); + } + + Ptr featureDetector; + Ptr descriptorExtractor; int normType; - float minKeyPointMatchesRatio; - float minDescInliersRatio; - + float minDescInliersRatio; }; class DetectorScaleInvarianceTest : public cvtest::BaseTest @@ -392,9 +388,9 @@ public: DetectorScaleInvarianceTest(const Ptr& _featureDetector, float _minKeyPointMatchesRatio, float _minScaleInliersRatio) : - featureDetector(_featureDetector), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minScaleInliersRatio(_minScaleInliersRatio) + featureDetector(_featureDetector), + minKeyPointMatchesRatio(_minKeyPointMatchesRatio), + minScaleInliersRatio(_minScaleInliersRatio) { CV_Assert(!featureDetector.empty()); } @@ -403,7 +399,7 @@ protected: void run(int) { - const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; + const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; // Read test data Mat image0 = imread(imageFilename); @@ -417,58 +413,59 @@ protected: vector keypoints0; featureDetector->detect(image0, keypoints0); if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - for(int scale = 2; scale <= 4; scale++) + for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) { - Mat image1; - resize(image0, image1, Size(), 1./scale, 1./scale); + float scale = 1.f + scaleIdx * 0.5f; + Mat image1; + resize(image0, image1, Size(), 1./scale, 1./scale); vector keypoints1, osiKeypoints1; // osi - original size image featureDetector->detect(image1, keypoints1); - if(keypoints1.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + if(keypoints1.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - if(keypoints1.size() > keypoints0.size()) - { + if(keypoints1.size() > keypoints0.size()) + { ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " - "It gives more points count in an image of the smaller size.\n" - "original size (%d, %d), keypoints count = %d\n" - "reduced size (%d, %d), keypoints count = %d\n", - image0.cols, image0.rows, keypoints0.size(), - image1.cols, image1.rows, keypoints1.size()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + "It gives more points count in an image of the smaller size.\n" + "original size (%d, %d), keypoints count = %d\n" + "reduced size (%d, %d), keypoints count = %d\n", + image0.cols, image0.rows, keypoints0.size(), + image1.cols, image1.rows, keypoints1.size()); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); return; } - scaleKeyPoints(keypoints1, osiKeypoints1, scale); + scaleKeyPoints(keypoints1, osiKeypoints1, scale); - vector matches; - // image1 is query image (it's reduced image0) - // image0 is train image - matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); + vector matches; + // image1 is query image (it's reduced image0) + // image0 is train image + matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches); - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - int scaleInliersCount = 0; + const float minIntersectRatio = 0.5f; + int keyPointMatchesCount = 0; + int scaleInliersCount = 0; - for(size_t m = 0; m < matches.size(); m++) + for(size_t m = 0; m < matches.size(); m++) { - if(matches[m].distance < minIntersectRatio) - continue; + if(matches[m].distance < minIntersectRatio) + continue; - keyPointMatchesCount++; + keyPointMatchesCount++; - // Check does this inlier have consistent sizes + // Check does this inlier have consistent sizes const float maxSizeDiff = 0.8;//0.9f; // grad - float size0 = keypoints0[matches[m].trainIdx].size; + float size0 = keypoints0[matches[m].trainIdx].size; float size1 = osiKeypoints1[matches[m].queryIdx].size; - CV_Assert(size0 > 0 && size1 > 0); - if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) + CV_Assert(size0 > 0 && size1 > 0); + if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1)) scaleInliersCount++; } - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); + float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); if(keyPointMatchesRatio < minKeyPointMatchesRatio) { ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", @@ -477,9 +474,9 @@ protected: return; } - if(keyPointMatchesCount) + if(keyPointMatchesCount) { - float scaleInliersRatio = static_cast(scaleInliersCount) / keyPointMatchesCount; + float scaleInliersRatio = static_cast(scaleInliersCount) / keyPointMatchesCount; if(scaleInliersRatio < minScaleInliersRatio) { ts->printf(cvtest::TS::LOG, "Incorrect scaleInliersRatio: curr = %f, min = %f.\n", @@ -490,7 +487,7 @@ protected: } #if SHOW_DEBUG_LOG std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - scaleInliersRatio " << static_cast(scaleInliersCount) / keyPointMatchesCount << std::endl; + << " - scaleInliersRatio " << static_cast(scaleInliersCount) / keyPointMatchesCount << std::endl; #endif } ts->set_failed_test_info( cvtest::TS::OK ); @@ -505,25 +502,23 @@ class DescriptorScaleInvarianceTest : public cvtest::BaseTest { public: DescriptorScaleInvarianceTest(const Ptr& _featureDetector, - const Ptr& _descriptorExtractor, - int _normType, - float _minKeyPointMatchesRatio, + const Ptr& _descriptorExtractor, + int _normType, float _minDescInliersRatio) : - featureDetector(_featureDetector), - descriptorExtractor(_descriptorExtractor), - normType(_normType), - minKeyPointMatchesRatio(_minKeyPointMatchesRatio), - minDescInliersRatio(_minDescInliersRatio) + featureDetector(_featureDetector), + descriptorExtractor(_descriptorExtractor), + normType(_normType), + minDescInliersRatio(_minDescInliersRatio) { CV_Assert(!featureDetector.empty()); - CV_Assert(!descriptorExtractor.empty()); + CV_Assert(!descriptorExtractor.empty()); } protected: void run(int) { - const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; + const string imageFilename = string(ts->get_data_path()) + IMAGE_BIKES; // Read test data Mat image0 = imread(imageFilename); @@ -536,102 +531,71 @@ protected: vector keypoints0; featureDetector->detect(image0, keypoints0); - if(keypoints0.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - Mat descriptors0; - descriptorExtractor->compute(image0, keypoints0, descriptors0); + if(keypoints0.size() < 15) + CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); + Mat descriptors0; + descriptorExtractor->compute(image0, keypoints0, descriptors0); - BFMatcher bfmatcher(normType); - for(int scale = 2; scale <= 4; scale++) + BFMatcher bfmatcher(normType); + for(float scaleIdx = 1; scaleIdx <= 3; scaleIdx++) { - Mat image1; - resize(image0, image1, Size(), 1./scale, 1./scale); + float scale = 1.f + scaleIdx * 0.5f; - vector keypoints1, osiKeypoints1; // osi - original size image - featureDetector->detect(image1, keypoints1); - if(keypoints1.size() < 15) - CV_Error(CV_StsAssert, "Detector gives too few points in a test image\n"); - if(keypoints1.size() > keypoints0.size() ) - { - ts->printf(cvtest::TS::LOG, "Strange behavior of the detector. " - "It gives more points count in an image of the smaller size.\n" - "original size (%d, %d), keypoints count = %d\n" - "reduced size (%d, %d), keypoints count = %d\n", - image0.cols, image0.rows, keypoints0.size(), - image1.cols, image1.rows, keypoints1.size()); - ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); - return; + Mat image1; + resize(image0, image1, Size(), 1./scale, 1./scale); + + vector keypoints1; + scaleKeyPoints(keypoints0, keypoints1, 1./scale); + Mat descriptors1; + descriptorExtractor->compute(image1, keypoints1, descriptors1); + + vector descMatches; + bfmatcher.match(descriptors0, descriptors1, descMatches); + + const float minIntersectRatio = 0.5f; + int descInliersCount = 0; + for(size_t m = 0; m < descMatches.size(); m++) + { + const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx]; + const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx]; + if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size, + p1.pt, 0.5f * p1.size) >= minIntersectRatio) + { + descInliersCount++; + } } - Mat descriptors1; - descriptorExtractor->compute(image1, keypoints1, descriptors1); - - vector keyPointMatches, descMatches; - // image1 is query image (it's reduced image0) - // image0 is train image - bfmatcher.match(descriptors1, descriptors0, descMatches); - - scaleKeyPoints(keypoints1, osiKeypoints1, scale); - matchKeyPoints(osiKeypoints1, Mat(), keypoints0, keyPointMatches); - - const float minIntersectRatio = 0.5f; - int keyPointMatchesCount = 0; - for(size_t m = 0; m < keyPointMatches.size(); m++) - { - if(keyPointMatches[m].distance >= minIntersectRatio) - keyPointMatchesCount++; - } - int descInliersCount = 0; - for(size_t m = 0; m < descMatches.size(); m++) + float descInliersRatio = static_cast(descInliersCount) / keypoints0.size(); + if(descInliersRatio < minDescInliersRatio) { - int queryIdx = descMatches[m].queryIdx; - if(keyPointMatches[queryIdx].distance >= minIntersectRatio && - descMatches[m].trainIdx == keyPointMatches[queryIdx].trainIdx) - descInliersCount++; - } - - float keyPointMatchesRatio = static_cast(keyPointMatchesCount) / keypoints1.size(); - if(keyPointMatchesRatio < minKeyPointMatchesRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect keyPointMatchesRatio: curr = %f, min = %f.\n", - keyPointMatchesRatio, minKeyPointMatchesRatio); + ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", + descInliersRatio, minDescInliersRatio); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } - - if(keyPointMatchesCount) - { - float descInliersRatio = static_cast(descInliersCount) / keyPointMatchesCount; - if(descInliersRatio < minDescInliersRatio) - { - ts->printf(cvtest::TS::LOG, "Incorrect descInliersRatio: curr = %f, min = %f.\n", - descInliersRatio, minDescInliersRatio); - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - return; - } - } #if SHOW_DEBUG_LOG - std::cout << "keyPointMatchesRatio - " << keyPointMatchesRatio - << " - descInliersRatio " << static_cast(descInliersCount) / keyPointMatchesCount << std::endl; + std::cout << "descInliersRatio " << static_cast(descInliersCount) / keypoints0.size() << std::endl; #endif } ts->set_failed_test_info( cvtest::TS::OK ); } Ptr featureDetector; - Ptr descriptorExtractor; - int normType; + Ptr descriptorExtractor; + int normType; float minKeyPointMatchesRatio; float minDescInliersRatio; }; // Tests registration -// Detector's rotation invariance check +/* + * Detector's rotation invariance check + */ TEST(Features2d_RotationInvariance_Detector_SURF, regression) { DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.SURF"), - 0.44f, + 0.45f, 0.76f); test.safe_run(); } @@ -639,19 +603,20 @@ TEST(Features2d_RotationInvariance_Detector_SURF, regression) TEST(Features2d_RotationInvariance_Detector_SIFT, regression) { DetectorRotationInvarianceTest test(Algorithm::create("Feature2D.SIFT"), - 0.64f, - 0.74f); + 0.75f, + 0.76f); test.safe_run(); } -// Descriptors's rotation invariance check +/* + * Descriptors's rotation invariance check + */ TEST(Features2d_RotationInvariance_Descriptor_SURF, regression) { DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.SURF"), Algorithm::create("Feature2D.SURF"), NORM_L1, - 0.44f, - 0.63f); + 0.83f); test.safe_run(); } @@ -660,45 +625,46 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) DescriptorRotationInvarianceTest test(Algorithm::create("Feature2D.SIFT"), Algorithm::create("Feature2D.SIFT"), NORM_L1, - 0.64f, - 0.72f); + 0.98f); test.safe_run(); } -// Detector's scale invariance check +/* + * Detector's scale invariance check + */ TEST(Features2d_ScaleInvariance_Detector_SURF, regression) { DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.SURF"), - 0.62f, - 0.68f); + 0.64f, + 0.84f); test.safe_run(); } TEST(Features2d_ScaleInvariance_Detector_SIFT, regression) { DetectorScaleInvarianceTest test(Algorithm::create("Feature2D.SIFT"), - 0.59f, - 0.94f); + 0.69f, + 0.99f); test.safe_run(); } -// Descriptor's scale invariance check +/* + * Descriptor's scale invariance check + */ TEST(Features2d_ScaleInvariance_Descriptor_SURF, regression) { DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.SURF"), Algorithm::create("Feature2D.SURF"), NORM_L1, - 0.62f, - 0.68f); + 0.61f); test.safe_run(); } -TEST(Features2d_ScaleInvariance_Descriptor_SIFT, regression) -{ - DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.SIFT"), - Algorithm::create("Feature2D.SIFT"), - NORM_L1, - 0.59f, - 0.78f); - test.safe_run(); -} \ No newline at end of file +//TEST(Features2d_ScaleInvariance_Descriptor_SIFT, regression) +//{ +// DescriptorScaleInvarianceTest test(Algorithm::create("Feature2D.SIFT"), +// Algorithm::create("Feature2D.SIFT"), +// NORM_L1, +// 0.14f); +// test.safe_run(); +//}