From 4064d4c7ebf44b67500d393f9349aaa88e421953 Mon Sep 17 00:00:00 2001 From: Yosshi999 Date: Thu, 25 Jun 2020 20:34:31 +0900 Subject: [PATCH] Merge pull request #17618 from Yosshi999:gsoc_sift-better-test Added/Fixed testcases for SIFT * merge perf_sift into conventional perf tests * Fix disabled SIFT scale invariance tests allows trainIdx duplication in matching scaled keypoints --- .../features2d/include/opencv2/features2d.hpp | 6 ++ modules/features2d/perf/perf_feature2d.hpp | 5 +- modules/features2d/perf/perf_sift.cpp | 85 ------------------- modules/features2d/src/sift.dispatch.cpp | 5 ++ .../test/test_descriptors_invariance.cpp | 29 ++++++- .../test/test_detectors_invariance.cpp | 10 +-- 6 files changed, 43 insertions(+), 97 deletions(-) delete mode 100644 modules/features2d/perf/perf_sift.cpp diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index 316b130377..d588d75c14 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -261,6 +261,10 @@ public: @param contrastThreshold The contrast threshold used to filter out weak features in semi-uniform (low-contrast) regions. The larger the threshold, the less features are produced by the detector. + @note The contrast threshold will be divided by nOctaveLayers when the filtering is applied. When + nOctaveLayers is set to default and if you want to use the value used in D. Lowe paper, 0.03, set + this argument to 0.09. + @param edgeThreshold The threshold used to filter out edge-like features. Note that the its meaning is different from the contrastThreshold, i.e. the larger the edgeThreshold, the less features are filtered out (more features are retained). @@ -271,6 +275,8 @@ public: CV_WRAP static Ptr create(int nfeatures = 0, int nOctaveLayers = 3, double contrastThreshold = 0.04, double edgeThreshold = 10, double sigma = 1.6); + + CV_WRAP virtual String getDefaultName() const CV_OVERRIDE; }; typedef SIFT SiftFeatureDetector; diff --git a/modules/features2d/perf/perf_feature2d.hpp b/modules/features2d/perf/perf_feature2d.hpp index ad98689bf5..244de7a6ce 100644 --- a/modules/features2d/perf/perf_feature2d.hpp +++ b/modules/features2d/perf/perf_feature2d.hpp @@ -21,7 +21,8 @@ namespace opencv_test ORB_DEFAULT, ORB_1500_13_1, \ AKAZE_DEFAULT, AKAZE_DESCRIPTOR_KAZE, \ BRISK_DEFAULT, \ - KAZE_DEFAULT + KAZE_DEFAULT, \ + SIFT_DEFAULT #define CV_ENUM_EXPAND(name, ...) CV_ENUM(name, __VA_ARGS__) @@ -77,6 +78,8 @@ static inline Ptr getFeature2D(Feature2DType type) return KAZE::create(); case MSER_DEFAULT: return MSER::create(); + case SIFT_DEFAULT: + return SIFT::create(); default: return Ptr(); } diff --git a/modules/features2d/perf/perf_sift.cpp b/modules/features2d/perf/perf_sift.cpp deleted file mode 100644 index fd9579bed5..0000000000 --- a/modules/features2d/perf/perf_sift.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -#include "perf_precomp.hpp" - -namespace opencv_test { namespace { - -typedef perf::TestBaseWithParam SIFT_detect; -typedef perf::TestBaseWithParam SIFT_extract; -typedef perf::TestBaseWithParam SIFT_full; - -#define SIFT_IMAGES \ - "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ - "stitching/a3.png" - -PERF_TEST_P_(SIFT_detect, SIFT) -{ - string filename = getDataPath(GetParam()); - Mat frame = imread(filename, IMREAD_GRAYSCALE); - ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename; - - Mat mask; - declare.in(frame).time(90); - Ptr detector = SIFT::create(); - vector points; - - PERF_SAMPLE_BEGIN(); - detector->detect(frame, points, mask); - PERF_SAMPLE_END(); - - SANITY_CHECK_NOTHING(); -} - -PERF_TEST_P_(SIFT_extract, SIFT) -{ - string filename = getDataPath(GetParam()); - Mat frame = imread(filename, IMREAD_GRAYSCALE); - ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename; - - Mat mask; - declare.in(frame).time(90); - - Ptr detector = SIFT::create(); - vector points; - Mat descriptors; - detector->detect(frame, points, mask); - - PERF_SAMPLE_BEGIN(); - detector->compute(frame, points, descriptors); - PERF_SAMPLE_END(); - - SANITY_CHECK_NOTHING(); -} - -PERF_TEST_P_(SIFT_full, SIFT) -{ - string filename = getDataPath(GetParam()); - Mat frame = imread(filename, IMREAD_GRAYSCALE); - ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename; - - Mat mask; - declare.in(frame).time(90); - Ptr detector = SIFT::create(); - vector points; - Mat descriptors; - - PERF_SAMPLE_BEGIN(); - detector->detectAndCompute(frame, mask, points, descriptors, false); - PERF_SAMPLE_END(); - - SANITY_CHECK_NOTHING(); -} - - -INSTANTIATE_TEST_CASE_P(/*nothing*/, SIFT_detect, - testing::Values(SIFT_IMAGES) -); -INSTANTIATE_TEST_CASE_P(/*nothing*/, SIFT_extract, - testing::Values(SIFT_IMAGES) -); -INSTANTIATE_TEST_CASE_P(/*nothing*/, SIFT_full, - testing::Values(SIFT_IMAGES) -); - -}} // namespace diff --git a/modules/features2d/src/sift.dispatch.cpp b/modules/features2d/src/sift.dispatch.cpp index b9ab704804..d5abd9c228 100644 --- a/modules/features2d/src/sift.dispatch.cpp +++ b/modules/features2d/src/sift.dispatch.cpp @@ -126,6 +126,11 @@ Ptr SIFT::create( int _nfeatures, int _nOctaveLayers, return makePtr(_nfeatures, _nOctaveLayers, _contrastThreshold, _edgeThreshold, _sigma); } +String SIFT::getDefaultName() const +{ + return (Feature2D::getDefaultName() + ".SIFT"); +} + static inline void unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale) { diff --git a/modules/features2d/test/test_descriptors_invariance.cpp b/modules/features2d/test/test_descriptors_invariance.cpp index 0bbcad43c5..a53491fb76 100644 --- a/modules/features2d/test/test_descriptors_invariance.cpp +++ b/modules/features2d/test/test_descriptors_invariance.cpp @@ -15,6 +15,26 @@ const static std::string IMAGE_TSUKUBA = "features2d/tsukuba.png"; const static std::string IMAGE_BIKES = "detectors_descriptors_evaluation/images_datasets/bikes/img1.png"; #define Value(...) Values(String_FeatureDetector_DescriptorExtractor_Float_t(__VA_ARGS__)) +static +void SetSuitableSIFTOctave(vector& keypoints, + int firstOctave = -1, int nOctaveLayers = 3, double sigma = 1.6) +{ + for (size_t i = 0; i < keypoints.size(); i++ ) + { + int octv, layer; + KeyPoint& kpt = keypoints[i]; + double octv_layer = std::log(kpt.size / sigma) / std::log(2.) - 1; + octv = cvFloor(octv_layer); + layer = cvRound( (octv_layer - octv) * nOctaveLayers ); + if (octv < firstOctave) + { + octv = firstOctave; + layer = 0; + } + kpt.octave = (layer << 8) | (octv & 255); + } +} + static void rotateKeyPoints(const vector& src, const Mat& H, float angle, vector& dst) { @@ -132,6 +152,10 @@ TEST_P(DescriptorScaleInvariance, scale) vector keypoints1; scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale); + if (featureDetector->getDefaultName() == "Feature2D.SIFT") + { + SetSuitableSIFTOctave(keypoints1); + } Mat descriptors1; descriptorExtractor->compute(image1, keypoints1, descriptors1); @@ -186,9 +210,8 @@ INSTANTIATE_TEST_CASE_P(AKAZE_DESCRIPTOR_KAZE, DescriptorRotationInvariance, * Descriptor's scale invariance check */ -// TODO: Expected: (descInliersRatio) >= (minInliersRatio), actual: 0.330378 vs 0.78 -INSTANTIATE_TEST_CASE_P(DISABLED_SIFT, DescriptorScaleInvariance, - Value(IMAGE_BIKES, SIFT::create(), SIFT::create(), 0.78f)); +INSTANTIATE_TEST_CASE_P(SIFT, DescriptorScaleInvariance, + Value(IMAGE_BIKES, SIFT::create(0, 3, 0.09), SIFT::create(0, 3, 0.09), 0.78f)); INSTANTIATE_TEST_CASE_P(AKAZE, DescriptorScaleInvariance, Value(IMAGE_BIKES, AKAZE::create(), AKAZE::create(), 0.6f)); diff --git a/modules/features2d/test/test_detectors_invariance.cpp b/modules/features2d/test/test_detectors_invariance.cpp index 31ba12e60e..d998a4e60a 100644 --- a/modules/features2d/test/test_detectors_invariance.cpp +++ b/modules/features2d/test/test_detectors_invariance.cpp @@ -29,7 +29,6 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, perspectiveTransform(Mat(points0), points0t, H); matches.clear(); - vector usedMask(keypoints1.size(), 0); for(int i0 = 0; i0 < static_cast(keypoints0.size()); i0++) { int nearestPointIndex = -1; @@ -37,8 +36,6 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, const float r0 = 0.5f * keypoints0[i0].size; for(size_t i1 = 0; i1 < keypoints1.size(); i1++) { - if(nearestPointIndex >= 0 && usedMask[i1]) - continue; float r1 = 0.5f * keypoints1[i1].size; float intersectRatio = calcIntersectRatio(points0t.at(i0), r0, @@ -51,8 +48,6 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, } matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio)); - if(nearestPointIndex >= 0) - usedMask[nearestPointIndex] = 1; } } @@ -239,9 +234,8 @@ INSTANTIATE_TEST_CASE_P(AKAZE_DESCRIPTOR_KAZE, DetectorRotationInvariance, * Detector's scale invariance check */ -// TODO: Expected: (keyPointMatchesRatio) >= (minKeyPointMatchesRatio), actual: 0.596752 vs 0.69 -INSTANTIATE_TEST_CASE_P(DISABLED_SIFT, DetectorScaleInvariance, - Value(IMAGE_BIKES, SIFT::create(), 0.69f, 0.98f)); +INSTANTIATE_TEST_CASE_P(SIFT, DetectorScaleInvariance, + Value(IMAGE_BIKES, SIFT::create(0, 3, 0.09), 0.69f, 0.98f)); INSTANTIATE_TEST_CASE_P(BRISK, DetectorScaleInvariance, Value(IMAGE_BIKES, BRISK::create(), 0.08f, 0.49f)); -- GitLab