From 09df1a286b62450f2b619ee1d4fec02000daa2e4 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 15 Oct 2014 22:49:17 +0400 Subject: [PATCH] OpenCV with the refactored features2d compiles! contrib is broken for now; the tests are not tried yet --- .../features2d/include/opencv2/features2d.hpp | 30 +- modules/features2d/perf/opencl/perf_orb.cpp | 14 +- modules/features2d/perf/perf_orb.cpp | 14 +- modules/features2d/src/akaze.cpp | 273 ++- modules/features2d/src/blobdetector.cpp | 20 +- modules/features2d/src/brisk.cpp | 129 +- modules/features2d/src/feature2d.cpp | 159 ++ modules/features2d/src/features2d_init.cpp | 199 -- .../src/{detectors.cpp => gftt.cpp} | 0 modules/features2d/src/kaze.cpp | 8 +- modules/features2d/src/kaze/AKAZEConfig.h | 4 +- modules/features2d/src/kaze/AKAZEFeatures.cpp | 203 +- modules/features2d/src/kaze/AKAZEFeatures.h | 8 +- modules/features2d/src/kaze/KAZEFeatures.cpp | 216 ++- modules/features2d/src/kaze/KAZEFeatures.h | 4 +- modules/features2d/src/kaze/TEvolution.h | 19 +- .../src/kaze/nldiffusion_functions.cpp | 898 +++++---- .../src/kaze/nldiffusion_functions.h | 48 +- modules/features2d/src/mser.cpp | 1723 +++++++---------- modules/features2d/src/orb.cpp | 46 +- .../test/test_descriptors_regression.cpp | 21 +- .../test/test_detectors_regression.cpp | 16 +- modules/features2d/test/test_keypoints.cpp | 19 +- modules/features2d/test/test_mser.cpp | 4 + modules/features2d/test/test_orb.cpp | 4 +- modules/stitching/src/matchers.cpp | 8 +- modules/videostab/src/global_motion.cpp | 2 +- 27 files changed, 1856 insertions(+), 2233 deletions(-) create mode 100644 modules/features2d/src/feature2d.cpp delete mode 100644 modules/features2d/src/features2d_init.cpp rename modules/features2d/src/{detectors.cpp => gftt.cpp} (100%) diff --git a/modules/features2d/include/opencv2/features2d.hpp b/modules/features2d/include/opencv2/features2d.hpp index 4262f1c565..e9222918bc 100644 --- a/modules/features2d/include/opencv2/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d.hpp @@ -110,6 +110,10 @@ public: CV_OUT std::vector& keypoints, InputArray mask=noArray() ); + virtual void detect( InputArrayOfArrays images, + std::vector >& keypoints, + InputArrayOfArrays masks=noArray() ); + /* * Compute the descriptors for a set of keypoints in an image. * image The image. @@ -120,6 +124,10 @@ public: CV_OUT CV_IN_OUT std::vector& keypoints, OutputArray descriptors ); + virtual void compute( InputArrayOfArrays images, + std::vector >& keypoints, + OutputArrayOfArrays descriptors ); + /* Detects keypoints and computes the descriptors */ CV_WRAP virtual void detectAndCompute( InputArray image, InputArray mask, CV_OUT std::vector& keypoints, @@ -146,7 +154,7 @@ public: CV_WRAP static Ptr create(int thresh=30, int octaves=3, float patternScale=1.0f); // custom setup CV_WRAP static Ptr create(const std::vector &radiusList, const std::vector &numberList, - float dMax=5.85f, float dMin=8.2f, const std::vector indexChange=std::vector()); + float dMax=5.85f, float dMin=8.2f, const std::vector& indexChange=std::vector()); }; /*! @@ -174,6 +182,13 @@ public: class CV_EXPORTS_W MSER : public Feature2D { public: + enum + { + DELTA=10000, MIN_AREA=10001, MAX_AREA=10002, PASS2_ONLY=10003, + MAX_EVOLUTION=10004, AREA_THRESHOLD=10005, + MIN_MARGIN=10006, EDGE_BLUR_SIZE=10007 + }; + //! the full constructor CV_WRAP static Ptr create( int _delta=5, int _min_area=60, int _max_area=14400, double _max_variation=0.25, double _min_diversity=.2, @@ -181,7 +196,7 @@ public: double _min_margin=0.003, int _edge_blur_size=5 ); CV_WRAP virtual int detectAndLabel( InputArray image, OutputArray label, - OutputArray stats=noArray() ) const = 0; + OutputArray stats=noArray() ) = 0; }; //! detects corners using FAST algorithm by E. Rosten @@ -199,13 +214,16 @@ public: TYPE_5_8 = 0, TYPE_7_12 = 1, TYPE_9_16 = 2 }; - CV_WRAP static Ptr create( int threshold=10, bool nonmaxSuppression=true, int type=TYPE_9_16 ); + CV_WRAP static Ptr create( int threshold=10, + bool nonmaxSuppression=true, + int type=FastFeatureDetector::TYPE_9_16 ); }; class CV_EXPORTS_W GFTTDetector : public Feature2D { public: + enum { USE_HARRIS_DETECTOR=10000 }; CV_WRAP static Ptr create( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1, int blockSize=3, bool useHarrisDetector=false, double k=0.04 ); }; @@ -282,7 +300,7 @@ public: DESCRIPTOR_MLDB = 5 }; - CV_WRAP static Ptr create(int descriptor_type=DESCRIPTOR_MLDB, + CV_WRAP static Ptr create(int descriptor_type=AKAZE::DESCRIPTOR_MLDB, int descriptor_size = 0, int descriptor_channels = 3, float threshold = 0.001f, int octaves = 4, int sublevels = 4, int diffusivity = KAZE::DIFF_PM_G2); @@ -535,8 +553,6 @@ public: virtual bool isMaskSupported() const { return true; } virtual Ptr clone( bool emptyTrainData=false ) const; - - AlgorithmInfo* info() const; protected: virtual void knnMatchImpl( InputArray queryDescriptors, std::vector >& matches, int k, InputArrayOfArrays masks=noArray(), bool compactResult=false ); @@ -569,8 +585,6 @@ public: virtual bool isMaskSupported() const; virtual Ptr clone( bool emptyTrainData=false ) const; - - AlgorithmInfo* info() const; protected: static void convertToDMatches( const DescriptorCollection& descriptors, const Mat& indices, const Mat& distances, diff --git a/modules/features2d/perf/opencl/perf_orb.cpp b/modules/features2d/perf/opencl/perf_orb.cpp index c551dee88a..e9aadf50f3 100644 --- a/modules/features2d/perf/opencl/perf_orb.cpp +++ b/modules/features2d/perf/opencl/perf_orb.cpp @@ -22,10 +22,10 @@ OCL_PERF_TEST_P(ORBFixture, ORB_Detect, ORB_IMAGES) mframe.copyTo(frame); declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; - OCL_TEST_CYCLE() detector(frame, mask, points); + OCL_TEST_CYCLE() detector->detect(frame, points, mask); std::sort(points.begin(), points.end(), comparators::KeypointGreater()); SANITY_CHECK_KEYPOINTS(points, 1e-5); @@ -44,14 +44,14 @@ OCL_PERF_TEST_P(ORBFixture, ORB_Extract, ORB_IMAGES) declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; - detector(frame, mask, points); + detector->detect(frame, points, mask); std::sort(points.begin(), points.end(), comparators::KeypointGreater()); UMat descriptors; - OCL_TEST_CYCLE() detector(frame, mask, points, descriptors, true); + OCL_TEST_CYCLE() detector->compute(frame, points, descriptors); SANITY_CHECK(descriptors); } @@ -68,12 +68,12 @@ OCL_PERF_TEST_P(ORBFixture, ORB_Full, ORB_IMAGES) mframe.copyTo(frame); declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; UMat descriptors; - OCL_TEST_CYCLE() detector(frame, mask, points, descriptors, false); + OCL_TEST_CYCLE() detector->detectAndCompute(frame, mask, points, descriptors, false); ::perf::sort(points, descriptors); SANITY_CHECK_KEYPOINTS(points, 1e-5); diff --git a/modules/features2d/perf/perf_orb.cpp b/modules/features2d/perf/perf_orb.cpp index 1c181c45ec..96cfc3eb38 100644 --- a/modules/features2d/perf/perf_orb.cpp +++ b/modules/features2d/perf/perf_orb.cpp @@ -22,10 +22,10 @@ PERF_TEST_P(orb, detect, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; - TEST_CYCLE() detector(frame, mask, points); + TEST_CYCLE() detector->detect(frame, points, mask); sort(points.begin(), points.end(), comparators::KeypointGreater()); SANITY_CHECK_KEYPOINTS(points, 1e-5); @@ -42,14 +42,14 @@ PERF_TEST_P(orb, extract, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; - detector(frame, mask, points); + detector->detect(frame, points, mask); sort(points.begin(), points.end(), comparators::KeypointGreater()); Mat descriptors; - TEST_CYCLE() detector(frame, mask, points, descriptors, true); + TEST_CYCLE() detector->compute(frame, points, descriptors); SANITY_CHECK(descriptors); } @@ -64,12 +64,12 @@ PERF_TEST_P(orb, full, testing::Values(ORB_IMAGES)) Mat mask; declare.in(frame); - ORB detector(1500, 1.3f, 1); + Ptr detector = ORB::create(1500, 1.3f, 1); vector points; Mat descriptors; - TEST_CYCLE() detector(frame, mask, points, descriptors, false); + TEST_CYCLE() detector->detectAndCompute(frame, mask, points, descriptors, false); perf::sort(points, descriptors); SANITY_CHECK_KEYPOINTS(points, 1e-5); diff --git a/modules/features2d/src/akaze.cpp b/modules/features2d/src/akaze.cpp index d875b46441..b72b9402a6 100644 --- a/modules/features2d/src/akaze.cpp +++ b/modules/features2d/src/akaze.cpp @@ -52,22 +52,15 @@ http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla13bmvc.pd #include "kaze/AKAZEFeatures.h" #include -using namespace std; namespace cv { - AKAZE::AKAZE() - : descriptor(DESCRIPTOR_MLDB) - , descriptor_channels(3) - , descriptor_size(0) - , threshold(0.001f) - , octaves(4) - , sublevels(4) - , diffusivity(DIFF_PM_G2) - { - } + using namespace std; - AKAZE::AKAZE(int _descriptor_type, int _descriptor_size, int _descriptor_channels, + class AKAZE_Impl : public AKAZE + { + public: + AKAZE_Impl(int _descriptor_type, int _descriptor_size, int _descriptor_channels, float _threshold, int _octaves, int _sublevels, int _diffusivity) : descriptor(_descriptor_type) , descriptor_channels(_descriptor_channels) @@ -76,181 +69,139 @@ namespace cv , octaves(_octaves) , sublevels(_sublevels) , diffusivity(_diffusivity) - { - - } - - AKAZE::~AKAZE() - { - - } - - // returns the descriptor size in bytes - int AKAZE::descriptorSize() const - { - switch (descriptor) { - case cv::DESCRIPTOR_KAZE: - case cv::DESCRIPTOR_KAZE_UPRIGHT: - return 64; + } - case cv::DESCRIPTOR_MLDB: - case cv::DESCRIPTOR_MLDB_UPRIGHT: - // We use the full length binary descriptor -> 486 bits - if (descriptor_size == 0) - { - int t = (6 + 36 + 120) * descriptor_channels; - return (int)ceil(t / 8.); - } - else - { - // We use the random bit selection length binary descriptor - return (int)ceil(descriptor_size / 8.); - } + virtual ~AKAZE_Impl() + { - default: - return -1; } - } - // returns the descriptor type - int AKAZE::descriptorType() const - { - switch (descriptor) + // returns the descriptor size in bytes + int descriptorSize() const { - case cv::DESCRIPTOR_KAZE: - case cv::DESCRIPTOR_KAZE_UPRIGHT: - return CV_32F; - - case cv::DESCRIPTOR_MLDB: - case cv::DESCRIPTOR_MLDB_UPRIGHT: - return CV_8U; + switch (descriptor) + { + case DESCRIPTOR_KAZE: + case DESCRIPTOR_KAZE_UPRIGHT: + return 64; + + case DESCRIPTOR_MLDB: + case DESCRIPTOR_MLDB_UPRIGHT: + // We use the full length binary descriptor -> 486 bits + if (descriptor_size == 0) + { + int t = (6 + 36 + 120) * descriptor_channels; + return (int)ceil(t / 8.); + } + else + { + // We use the random bit selection length binary descriptor + return (int)ceil(descriptor_size / 8.); + } default: return -1; + } } - } - // returns the default norm type - int AKAZE::defaultNorm() const - { - switch (descriptor) + // returns the descriptor type + int descriptorType() const { - case cv::DESCRIPTOR_KAZE: - case cv::DESCRIPTOR_KAZE_UPRIGHT: - return cv::NORM_L2; + switch (descriptor) + { + case DESCRIPTOR_KAZE: + case DESCRIPTOR_KAZE_UPRIGHT: + return CV_32F; - case cv::DESCRIPTOR_MLDB: - case cv::DESCRIPTOR_MLDB_UPRIGHT: - return cv::NORM_HAMMING; + case DESCRIPTOR_MLDB: + case DESCRIPTOR_MLDB_UPRIGHT: + return CV_8U; - default: - return -1; + default: + return -1; + } } - } - - void AKAZE::operator()(InputArray image, InputArray mask, - std::vector& keypoints, - OutputArray descriptors, - bool useProvidedKeypoints) const - { - cv::Mat img = image.getMat(); - if (img.type() != CV_8UC1) - cvtColor(image, img, COLOR_BGR2GRAY); - - Mat img1_32; - img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0); - - cv::Mat& desc = descriptors.getMatRef(); - - AKAZEOptions options; - options.descriptor = descriptor; - options.descriptor_channels = descriptor_channels; - options.descriptor_size = descriptor_size; - options.img_width = img.cols; - options.img_height = img.rows; - options.dthreshold = threshold; - options.omax = octaves; - options.nsublevels = sublevels; - options.diffusivity = diffusivity; + // returns the default norm type + int defaultNorm() const + { + switch (descriptor) + { + case DESCRIPTOR_KAZE: + case DESCRIPTOR_KAZE_UPRIGHT: + return NORM_L2; - AKAZEFeatures impl(options); - impl.Create_Nonlinear_Scale_Space(img1_32); + case DESCRIPTOR_MLDB: + case DESCRIPTOR_MLDB_UPRIGHT: + return NORM_HAMMING; - if (!useProvidedKeypoints) - { - impl.Feature_Detection(keypoints); + default: + return -1; + } } - if (!mask.empty()) + void detectAndCompute(InputArray image, InputArray mask, + std::vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints) { - cv::KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat()); - } - - impl.Compute_Descriptors(keypoints, desc); - - CV_Assert((!desc.rows || desc.cols == descriptorSize())); - CV_Assert((!desc.rows || (desc.type() == descriptorType()))); - } - - void AKAZE::detectImpl(InputArray image, std::vector& keypoints, InputArray mask) const - { - cv::Mat img = image.getMat(); - if (img.type() != CV_8UC1) - cvtColor(image, img, COLOR_BGR2GRAY); - - Mat img1_32; - img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0); + Mat img = image.getMat(); + if (img.type() != CV_8UC1) + cvtColor(image, img, COLOR_BGR2GRAY); + + Mat img1_32; + img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0); + + AKAZEOptions options; + options.descriptor = descriptor; + options.descriptor_channels = descriptor_channels; + options.descriptor_size = descriptor_size; + options.img_width = img.cols; + options.img_height = img.rows; + options.dthreshold = threshold; + options.omax = octaves; + options.nsublevels = sublevels; + options.diffusivity = diffusivity; + + AKAZEFeatures impl(options); + impl.Create_Nonlinear_Scale_Space(img1_32); + + if (!useProvidedKeypoints) + { + impl.Feature_Detection(keypoints); + } - AKAZEOptions options; - options.descriptor = descriptor; - options.descriptor_channels = descriptor_channels; - options.descriptor_size = descriptor_size; - options.img_width = img.cols; - options.img_height = img.rows; - options.dthreshold = threshold; - options.omax = octaves; - options.nsublevels = sublevels; - options.diffusivity = diffusivity; + if (!mask.empty()) + { + KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat()); + } - AKAZEFeatures impl(options); - impl.Create_Nonlinear_Scale_Space(img1_32); - impl.Feature_Detection(keypoints); + if( descriptors.needed() ) + { + Mat& desc = descriptors.getMatRef(); + impl.Compute_Descriptors(keypoints, desc); - if (!mask.empty()) - { - cv::KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat()); + CV_Assert((!desc.rows || desc.cols == descriptorSize())); + CV_Assert((!desc.rows || (desc.type() == descriptorType()))); + } } - } - void AKAZE::computeImpl(InputArray image, std::vector& keypoints, OutputArray descriptors) const + int descriptor; + int descriptor_channels; + int descriptor_size; + float threshold; + int octaves; + int sublevels; + int diffusivity; + }; + + Ptr AKAZE::create(int descriptor_type, + int descriptor_size, int descriptor_channels, + float threshold, int octaves, + int sublevels, int diffusivity) { - cv::Mat img = image.getMat(); - if (img.type() != CV_8UC1) - cvtColor(image, img, COLOR_BGR2GRAY); - - Mat img1_32; - img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0); - - cv::Mat& desc = descriptors.getMatRef(); - - AKAZEOptions options; - options.descriptor = descriptor; - options.descriptor_channels = descriptor_channels; - options.descriptor_size = descriptor_size; - options.img_width = img.cols; - options.img_height = img.rows; - options.dthreshold = threshold; - options.omax = octaves; - options.nsublevels = sublevels; - options.diffusivity = diffusivity; - - AKAZEFeatures impl(options); - impl.Create_Nonlinear_Scale_Space(img1_32); - impl.Compute_Descriptors(keypoints, desc); - - CV_Assert((!desc.rows || desc.cols == descriptorSize())); - CV_Assert((!desc.rows || (desc.type() == descriptorType()))); + return makePtr(descriptor_type, descriptor_size, descriptor_channels, + threshold, octaves, sublevels, diffusivity); } } diff --git a/modules/features2d/src/blobdetector.cpp b/modules/features2d/src/blobdetector.cpp index b9511e9acb..daf1d36440 100644 --- a/modules/features2d/src/blobdetector.cpp +++ b/modules/features2d/src/blobdetector.cpp @@ -75,11 +75,10 @@ protected: double confidence; }; - virtual void detectImpl( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) const; + virtual void detect( InputArray image, std::vector& keypoints, InputArray mask=noArray() ); virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector
¢ers) const; Params params; - AlgorithmInfo* info() const; }; /* @@ -173,22 +172,22 @@ void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const fs << "maxConvexity" << maxConvexity; } -SimpleBlobDetector::SimpleBlobDetector(const SimpleBlobDetector::Params ¶meters) : +SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params ¶meters) : params(parameters) { } -void SimpleBlobDetector::read( const cv::FileNode& fn ) +void SimpleBlobDetectorImpl::read( const cv::FileNode& fn ) { params.read(fn); } -void SimpleBlobDetector::write( cv::FileStorage& fs ) const +void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const { params.write(fs); } -void SimpleBlobDetector::findBlobs(InputArray _image, InputArray _binaryImage, std::vector
¢ers) const +void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector
¢ers) const { Mat image = _image.getMat(), binaryImage = _binaryImage.getMat(); (void)image; @@ -302,7 +301,7 @@ void SimpleBlobDetector::findBlobs(InputArray _image, InputArray _binaryImage, s #endif } -void SimpleBlobDetector::detectImpl(InputArray image, std::vector& keypoints, InputArray) const +void SimpleBlobDetectorImpl::detect(InputArray image, std::vector& keypoints, InputArray) { //TODO: support mask keypoints.clear(); @@ -365,3 +364,10 @@ void SimpleBlobDetector::detectImpl(InputArray image, std::vector& keypoints.push_back(kpt); } } + +Ptr SimpleBlobDetector::create(const SimpleBlobDetector::Params& params) +{ + return makePtr(params); +} + +} diff --git a/modules/features2d/src/brisk.cpp b/modules/features2d/src/brisk.cpp index 3163de70e0..1facb3eacb 100644 --- a/modules/features2d/src/brisk.cpp +++ b/modules/features2d/src/brisk.cpp @@ -42,9 +42,7 @@ the IEEE International Conference on Computer Vision (ICCV2011). */ -#include -#include -#include +#include "precomp.hpp" #include #include @@ -53,7 +51,6 @@ namespace cv { - class BRISK_Impl : public BRISK { public: @@ -62,17 +59,36 @@ public: explicit BRISK_Impl(const std::vector &radiusList, const std::vector &numberList, float dMax=5.85f, float dMin=8.2f, const std::vector indexChange=std::vector()); + virtual ~BRISK_Impl(); + + int descriptorSize() const + { + return strings_; + } + + int descriptorType() const + { + return CV_8U; + } + + int defaultNorm() const + { + return NORM_HAMMING; + } + // call this to generate the kernel: // circle of radius r (pixels), with n points; // short pairings with dMax, long pairings with dMin - void generateKernel(std::vector &radiusList, - std::vector &numberList, float dMax=5.85f, float dMin=8.2f, - std::vector indexChange=std::vector()); + void generateKernel(const std::vector &radiusList, + const std::vector &numberList, float dMax=5.85f, float dMin=8.2f, + const std::vector &indexChange=std::vector()); -protected: + void detectAndCompute( InputArray image, InputArray mask, + CV_OUT std::vector& keypoints, + OutputArray descriptors, + bool useProvidedKeypoints ); - void computeImpl( InputArray image, std::vector& keypoints, OutputArray descriptors ) const; - void detectImpl( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) const; +protected: void computeKeypointsNoOrientation(InputArray image, InputArray mask, std::vector& keypoints) const; void computeDescriptorsAndOrOrientation(InputArray image, InputArray mask, std::vector& keypoints, @@ -256,16 +272,16 @@ protected: static const float basicSize_; }; -const float BRISK::basicSize_ = 12.0f; -const unsigned int BRISK::scales_ = 64; -const float BRISK::scalerange_ = 30.f; // 40->4 Octaves - else, this needs to be adjusted... -const unsigned int BRISK::n_rot_ = 1024; // discretization of the rotation look-up +const float BRISK_Impl::basicSize_ = 12.0f; +const unsigned int BRISK_Impl::scales_ = 64; +const float BRISK_Impl::scalerange_ = 30.f; // 40->4 Octaves - else, this needs to be adjusted... +const unsigned int BRISK_Impl::n_rot_ = 1024; // discretization of the rotation look-up const float BriskScaleSpace::safetyFactor_ = 1.0f; const float BriskScaleSpace::basicSize_ = 12.0f; // constructors -BRISK::BRISK(int thresh, int octaves_in, float patternScale) +BRISK_Impl::BRISK_Impl(int thresh, int octaves_in, float patternScale) { threshold = thresh; octaves = octaves_in; @@ -291,10 +307,12 @@ BRISK::BRISK(int thresh, int octaves_in, float patternScale) nList[4] = 20; generateKernel(rList, nList, (float)(5.85 * patternScale), (float)(8.2 * patternScale)); - } -BRISK::BRISK(std::vector &radiusList, std::vector &numberList, float dMax, float dMin, - std::vector indexChange) + +BRISK_Impl::BRISK_Impl(const std::vector &radiusList, + const std::vector &numberList, + float dMax, float dMin, + const std::vector indexChange) { generateKernel(radiusList, numberList, dMax, dMin, indexChange); threshold = 20; @@ -302,10 +320,12 @@ BRISK::BRISK(std::vector &radiusList, std::vector &numberList, float } void -BRISK::generateKernel(std::vector &radiusList, std::vector &numberList, float dMax, - float dMin, std::vector indexChange) +BRISK_Impl::generateKernel(const std::vector &radiusList, + const std::vector &numberList, + float dMax, float dMin, + const std::vector& _indexChange) { - + std::vector indexChange = _indexChange; dMax_ = dMax; dMin_ = dMin; @@ -427,7 +447,7 @@ BRISK::generateKernel(std::vector &radiusList, std::vector &numberLi // simple alternative: inline int -BRISK::smoothedIntensity(const cv::Mat& image, const cv::Mat& integral, const float key_x, +BRISK_Impl::smoothedIntensity(const cv::Mat& image, const cv::Mat& integral, const float key_x, const float key_y, const unsigned int scale, const unsigned int rot, const unsigned int point) const { @@ -594,8 +614,8 @@ RoiPredicate(const float minX, const float minY, const float maxX, const float m // computes the descriptor void -BRISK::operator()( InputArray _image, InputArray _mask, std::vector& keypoints, - OutputArray _descriptors, bool useProvidedKeypoints) const +BRISK_Impl::detectAndCompute( InputArray _image, InputArray _mask, std::vector& keypoints, + OutputArray _descriptors, bool useProvidedKeypoints) { bool doOrientation=true; if (useProvidedKeypoints) @@ -609,7 +629,7 @@ BRISK::operator()( InputArray _image, InputArray _mask, std::vector& k } void -BRISK::computeDescriptorsAndOrOrientation(InputArray _image, InputArray _mask, std::vector& keypoints, +BRISK_Impl::computeDescriptorsAndOrOrientation(InputArray _image, InputArray _mask, std::vector& keypoints, OutputArray _descriptors, bool doDescriptors, bool doOrientation, bool useProvidedKeypoints) const { @@ -775,25 +795,8 @@ BRISK::computeDescriptorsAndOrOrientation(InputArray _image, InputArray _mask, s delete[] _values; } -int -BRISK::descriptorSize() const -{ - return strings_; -} -int -BRISK::descriptorType() const -{ - return CV_8U; -} - -int -BRISK::defaultNorm() const -{ - return NORM_HAMMING; -} - -BRISK::~BRISK() +BRISK_Impl::~BRISK_Impl() { delete[] patternPoints_; delete[] shortPairs_; @@ -803,14 +806,7 @@ BRISK::~BRISK() } void -BRISK::operator()(InputArray image, InputArray mask, std::vector& keypoints) const -{ - computeKeypointsNoOrientation(image, mask, keypoints); - computeDescriptorsAndOrOrientation(image, mask, keypoints, cv::noArray(), false, true, true); -} - -void -BRISK::computeKeypointsNoOrientation(InputArray _image, InputArray _mask, std::vector& keypoints) const +BRISK_Impl::computeKeypointsNoOrientation(InputArray _image, InputArray _mask, std::vector& keypoints) const { Mat image = _image.getMat(), mask = _mask.getMat(); if( image.type() != CV_8UC1 ) @@ -821,20 +817,7 @@ BRISK::computeKeypointsNoOrientation(InputArray _image, InputArray _mask, std::v briskScaleSpace.getKeypoints(threshold, keypoints); // remove invalid points - removeInvalidPoints(mask, keypoints); -} - - -void -BRISK::detectImpl( InputArray image, std::vector& keypoints, InputArray mask) const -{ - (*this)(image.getMat(), mask.getMat(), keypoints); -} - -void - BRISK::computeImpl( InputArray image, std::vector& keypoints, OutputArray descriptors) const -{ - (*this)(image, Mat(), keypoints, descriptors, true); + KeyPointsFilter::runByPixelsMask(keypoints, mask); } // construct telling the octaves number: @@ -2084,7 +2067,7 @@ BriskLayer::BriskLayer(const cv::Mat& img_in, float scale_in, float offset_in) scale_ = scale_in; offset_ = offset_in; // create an agast detector - fast_9_16_ = makePtr(1, true, FastFeatureDetector::TYPE_9_16); + fast_9_16_ = FastFeatureDetector::create(1, true, FastFeatureDetector::TYPE_9_16); makeOffsets(pixel_5_8_, (int)img_.step, 8); makeOffsets(pixel_9_16_, (int)img_.step, 16); } @@ -2106,7 +2089,7 @@ BriskLayer::BriskLayer(const BriskLayer& layer, int mode) offset_ = 0.5f * scale_ - 0.5f; } scores_ = cv::Mat::zeros(img_.rows, img_.cols, CV_8U); - fast_9_16_ = makePtr(1, false, FastFeatureDetector::TYPE_9_16); + fast_9_16_ = FastFeatureDetector::create(1, false, FastFeatureDetector::TYPE_9_16); makeOffsets(pixel_5_8_, (int)img_.step, 8); makeOffsets(pixel_9_16_, (int)img_.step, 16); } @@ -2318,4 +2301,16 @@ BriskLayer::twothirdsample(const cv::Mat& srcimg, cv::Mat& dstimg) resize(srcimg, dstimg, dstimg.size(), 0, 0, INTER_AREA); } +Ptr BRISK::create(int thresh, int octaves, float patternScale) +{ + return makePtr(thresh, octaves, patternScale); +} + +// custom setup +Ptr BRISK::create(const std::vector &radiusList, const std::vector &numberList, + float dMax, float dMin, const std::vector& indexChange) +{ + return makePtr(radiusList, numberList, dMax, dMin, indexChange); +} + } diff --git a/modules/features2d/src/feature2d.cpp b/modules/features2d/src/feature2d.cpp new file mode 100644 index 0000000000..039b33d351 --- /dev/null +++ b/modules/features2d/src/feature2d.cpp @@ -0,0 +1,159 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" + +namespace cv +{ + +using std::vector; + +Feature2D::~Feature2D() {} + +/* + * Detect keypoints in an image. + * image The image. + * keypoints The detected keypoints. + * mask Mask specifying where to look for keypoints (optional). Must be a char + * matrix with non-zero values in the region of interest. + */ +void Feature2D::detect( InputArray image, + std::vector& keypoints, + InputArray mask ) +{ + detectAndCompute(image, mask, keypoints, noArray(), false); +} + + +void Feature2D::detect( InputArrayOfArrays _images, + std::vector >& keypoints, + InputArrayOfArrays _masks ) +{ + vector images, masks; + + _images.getMatVector(images); + size_t i, nimages = images.size(); + + if( !_masks.empty() ) + { + _masks.getMatVector(masks); + CV_Assert(masks.size() == nimages); + } + + keypoints.resize(nimages); + + for( i = 0; i < nimages; i++ ) + { + detect(images[i], keypoints[i], masks.empty() ? Mat() : masks[i] ); + } +} + +/* + * Compute the descriptors for a set of keypoints in an image. + * image The image. + * keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed. + * descriptors Copmputed descriptors. Row i is the descriptor for keypoint i. + */ +void Feature2D::compute( InputArray image, + std::vector& keypoints, + OutputArray descriptors ) +{ + detectAndCompute(image, noArray(), keypoints, descriptors, true); +} + +void Feature2D::compute( InputArrayOfArrays _images, + std::vector >& keypoints, + OutputArrayOfArrays _descriptors ) +{ + if( !_descriptors.needed() ) + return; + + vector images; + + _images.getMatVector(images); + size_t i, nimages = images.size(); + + CV_Assert( keypoints.size() == nimages ); + CV_Assert( _descriptors.kind() == _InputArray::STD_VECTOR_MAT ); + + vector& descriptors = *(vector*)_descriptors.getObj(); + descriptors.resize(nimages); + + for( i = 0; i < nimages; i++ ) + { + compute(images[i], keypoints[i], descriptors[i]); + } +} + + +/* Detects keypoints and computes the descriptors */ +void Feature2D::detectAndCompute( InputArray, InputArray, + std::vector&, + OutputArray, + bool ) +{ + CV_Error(Error::StsNotImplemented, ""); +} + +int Feature2D::descriptorSize() const +{ + return 0; +} + +int Feature2D::descriptorType() const +{ + return CV_32F; +} + +int Feature2D::defaultNorm() const +{ + int tp = descriptorType(); + return tp == CV_8U ? NORM_HAMMING : NORM_L2; +} + +// Return true if detector object is empty +bool Feature2D::empty() const +{ + return true; +} + +} diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp deleted file mode 100644 index b23a8899b1..0000000000 --- a/modules/features2d/src/features2d_init.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2009, Willow Garage Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#include "precomp.hpp" - -#if 0 - -using namespace cv; - -Ptr Feature2D::create( const String& feature2DType ) -{ - return Algorithm::create("Feature2D." + feature2DType); -} - -/////////////////////// AlgorithmInfo for various detector & descriptors //////////////////////////// - -/* NOTE!!! - All the AlgorithmInfo-related stuff should be in the same file as initModule_features2d(). - Otherwise, linker may throw away some seemingly unused stuff. -*/ - -CV_INIT_ALGORITHM(BRISK, "Feature2D.BRISK", - obj.info()->addParam(obj, "thres", obj.threshold); - obj.info()->addParam(obj, "octaves", obj.octaves)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(FastFeatureDetector, "Feature2D.FAST", - obj.info()->addParam(obj, "threshold", obj.threshold); - obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression); - obj.info()->addParam(obj, "type", obj.type)) - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(MSER, "Feature2D.MSER", - obj.info()->addParam(obj, "delta", obj.delta); - obj.info()->addParam(obj, "minArea", obj.minArea); - obj.info()->addParam(obj, "maxArea", obj.maxArea); - obj.info()->addParam(obj, "maxVariation", obj.maxVariation); - obj.info()->addParam(obj, "minDiversity", obj.minDiversity); - obj.info()->addParam(obj, "maxEvolution", obj.maxEvolution); - obj.info()->addParam(obj, "areaThreshold", obj.areaThreshold); - obj.info()->addParam(obj, "minMargin", obj.minMargin); - obj.info()->addParam(obj, "edgeBlurSize", obj.edgeBlurSize)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(ORB, "Feature2D.ORB", - obj.info()->addParam(obj, "nFeatures", obj.nfeatures); - obj.info()->addParam(obj, "scaleFactor", obj.scaleFactor); - obj.info()->addParam(obj, "nLevels", obj.nlevels); - obj.info()->addParam(obj, "firstLevel", obj.firstLevel); - obj.info()->addParam(obj, "edgeThreshold", obj.edgeThreshold); - obj.info()->addParam(obj, "patchSize", obj.patchSize); - obj.info()->addParam(obj, "WTA_K", obj.WTA_K); - obj.info()->addParam(obj, "scoreType", obj.scoreType); - obj.info()->addParam(obj, "fastThreshold", obj.fastThreshold)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(GFTTDetector, "Feature2D.GFTT", - obj.info()->addParam(obj, "nfeatures", obj.nfeatures); - obj.info()->addParam(obj, "qualityLevel", obj.qualityLevel); - obj.info()->addParam(obj, "minDistance", obj.minDistance); - obj.info()->addParam(obj, "useHarrisDetector", obj.useHarrisDetector); - obj.info()->addParam(obj, "k", obj.k)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(KAZE, "Feature2D.KAZE", - obj.info()->addParam(obj, "upright", obj.upright); - obj.info()->addParam(obj, "extended", obj.extended); - obj.info()->addParam(obj, "threshold", obj.threshold); - obj.info()->addParam(obj, "octaves", obj.octaves); - obj.info()->addParam(obj, "sublevels", obj.sublevels); - obj.info()->addParam(obj, "diffusivity", obj.diffusivity)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(AKAZE, "Feature2D.AKAZE", - obj.info()->addParam(obj, "descriptor", obj.descriptor); - obj.info()->addParam(obj, "descriptor_channels", obj.descriptor_channels); - obj.info()->addParam(obj, "descriptor_size", obj.descriptor_size); - obj.info()->addParam(obj, "threshold", obj.threshold); - obj.info()->addParam(obj, "octaves", obj.octaves); - obj.info()->addParam(obj, "sublevels", obj.sublevels); - obj.info()->addParam(obj, "diffusivity", obj.diffusivity)) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - - - -CV_INIT_ALGORITHM(SimpleBlobDetector, "Feature2D.SimpleBlob", - obj.info()->addParam(obj, "thresholdStep", obj.params.thresholdStep); - obj.info()->addParam(obj, "minThreshold", obj.params.minThreshold); - obj.info()->addParam(obj, "maxThreshold", obj.params.maxThreshold); - obj.info()->addParam_(obj, "minRepeatability", (sizeof(size_t) == sizeof(uint64))?Param::UINT64 : Param::UNSIGNED_INT, &obj.params.minRepeatability, false, 0, 0); - obj.info()->addParam(obj, "minDistBetweenBlobs", obj.params.minDistBetweenBlobs); - obj.info()->addParam(obj, "filterByColor", obj.params.filterByColor); - obj.info()->addParam(obj, "blobColor", obj.params.blobColor); - obj.info()->addParam(obj, "filterByArea", obj.params.filterByArea); - obj.info()->addParam(obj, "maxArea", obj.params.maxArea); - obj.info()->addParam(obj, "filterByCircularity", obj.params.filterByCircularity); - obj.info()->addParam(obj, "maxCircularity", obj.params.maxCircularity); - obj.info()->addParam(obj, "filterByInertia", obj.params.filterByInertia); - obj.info()->addParam(obj, "maxInertiaRatio", obj.params.maxInertiaRatio); - obj.info()->addParam(obj, "filterByConvexity", obj.params.filterByConvexity); - obj.info()->addParam(obj, "maxConvexity", obj.params.maxConvexity); - ) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -class CV_EXPORTS HarrisDetector : public GFTTDetector -{ -public: - HarrisDetector( int maxCorners=1000, double qualityLevel=0.01, double minDistance=1, - int blockSize=3, bool useHarrisDetector=true, double k=0.04 ); - AlgorithmInfo* info() const; -}; - -inline HarrisDetector::HarrisDetector( int _maxCorners, double _qualityLevel, double _minDistance, - int _blockSize, bool _useHarrisDetector, double _k ) - : GFTTDetector( _maxCorners, _qualityLevel, _minDistance, _blockSize, _useHarrisDetector, _k ) {} - -CV_INIT_ALGORITHM(HarrisDetector, "Feature2D.HARRIS", - obj.info()->addParam(obj, "nfeatures", obj.nfeatures); - obj.info()->addParam(obj, "qualityLevel", obj.qualityLevel); - obj.info()->addParam(obj, "minDistance", obj.minDistance); - obj.info()->addParam(obj, "useHarrisDetector", obj.useHarrisDetector); - obj.info()->addParam(obj, "k", obj.k)) - -//////////////////////////////////////////////////////////////////////////////////////////////////////////// - -CV_INIT_ALGORITHM(BFMatcher, "DescriptorMatcher.BFMatcher", - obj.info()->addParam(obj, "normType", obj.normType); - obj.info()->addParam(obj, "crossCheck", obj.crossCheck)) - -CV_INIT_ALGORITHM(FlannBasedMatcher, "DescriptorMatcher.FlannBasedMatcher",) - -/////////////////////////////////////////////////////////////////////////////////////////////////////////// - -bool cv::initModule_features2d(void) -{ - bool all = true; - all &= !BRISK_info_auto.name().empty(); - all &= !FastFeatureDetector_info_auto.name().empty(); - all &= !MSER_info_auto.name().empty(); - all &= !ORB_info_auto.name().empty(); - all &= !GFTTDetector_info_auto.name().empty(); - all &= !KAZE_info_auto.name().empty(); - all &= !AKAZE_info_auto.name().empty(); - all &= !HarrisDetector_info_auto.name().empty(); - all &= !BFMatcher_info_auto.name().empty(); - all &= !FlannBasedMatcher_info_auto.name().empty(); - - return all; -} - -#endif diff --git a/modules/features2d/src/detectors.cpp b/modules/features2d/src/gftt.cpp similarity index 100% rename from modules/features2d/src/detectors.cpp rename to modules/features2d/src/gftt.cpp diff --git a/modules/features2d/src/kaze.cpp b/modules/features2d/src/kaze.cpp index d16e988f6f..83eeecc4ee 100644 --- a/modules/features2d/src/kaze.cpp +++ b/modules/features2d/src/kaze.cpp @@ -140,5 +140,11 @@ namespace cv int diffusivity; }; - + Ptr KAZE::create(bool extended, bool upright, + float threshold, + int octaves, int sublevels, + int diffusivity) + { + return makePtr(extended, upright, threshold, octaves, sublevels, diffusivity); + } } diff --git a/modules/features2d/src/kaze/AKAZEConfig.h b/modules/features2d/src/kaze/AKAZEConfig.h index 2ea21f3702..806d25c801 100644 --- a/modules/features2d/src/kaze/AKAZEConfig.h +++ b/modules/features2d/src/kaze/AKAZEConfig.h @@ -22,12 +22,12 @@ struct AKAZEOptions { , soffset(1.6f) , derivative_factor(1.5f) , sderivatives(1.0) - , diffusivity(cv::DIFF_PM_G2) + , diffusivity(KAZE::DIFF_PM_G2) , dthreshold(0.001f) , min_dthreshold(0.00001f) - , descriptor(cv::DESCRIPTOR_MLDB) + , descriptor(AKAZE::DESCRIPTOR_MLDB) , descriptor_size(0) , descriptor_channels(3) , descriptor_pattern_size(10) diff --git a/modules/features2d/src/kaze/AKAZEFeatures.cpp b/modules/features2d/src/kaze/AKAZEFeatures.cpp index 59e260fc4d..7988584030 100644 --- a/modules/features2d/src/kaze/AKAZEFeatures.cpp +++ b/modules/features2d/src/kaze/AKAZEFeatures.cpp @@ -61,14 +61,14 @@ void AKAZEFeatures::Allocate_Memory_Evolution(void) { for (int j = 0; j < options_.nsublevels; j++) { TEvolution step; - step.Lx = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Ly = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lxx = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lxy = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lyy = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lt = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Ldet = cv::Mat::zeros(level_height, level_width, CV_32F); - step.Lsmooth = cv::Mat::zeros(level_height, level_width, CV_32F); + step.Lx = Mat::zeros(level_height, level_width, CV_32F); + step.Ly = Mat::zeros(level_height, level_width, CV_32F); + step.Lxx = Mat::zeros(level_height, level_width, CV_32F); + step.Lxy = Mat::zeros(level_height, level_width, CV_32F); + step.Lyy = Mat::zeros(level_height, level_width, CV_32F); + step.Lt = Mat::zeros(level_height, level_width, CV_32F); + step.Ldet = Mat::zeros(level_height, level_width, CV_32F); + step.Lsmooth = Mat::zeros(level_height, level_width, CV_32F); step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i); step.sigma_size = fRound(step.esigma); step.etime = 0.5f*(step.esigma*step.esigma); @@ -97,7 +97,7 @@ void AKAZEFeatures::Allocate_Memory_Evolution(void) { * @param img Input image for which the nonlinear scale space needs to be created * @return 0 if the nonlinear scale space was created successfully, -1 otherwise */ -int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) +int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img) { CV_Assert(evolution_.size() > 0); @@ -107,8 +107,8 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) evolution_[0].Lt.copyTo(evolution_[0].Lsmooth); // Allocate memory for the flow and step images - cv::Mat Lflow = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); - cv::Mat Lstep = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); // First compute the kcontrast factor options_.kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0); @@ -121,8 +121,8 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) options_.kcontrast = options_.kcontrast*0.75f; // Allocate memory for the resized flow and step images - Lflow = cv::Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); - Lstep = cv::Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); + Lflow = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); + Lstep = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); } else { evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); @@ -136,16 +136,16 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) // Compute the conductivity equation switch (options_.diffusivity) { - case cv::DIFF_PM_G1: + case KAZE::DIFF_PM_G1: pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); break; - case cv::DIFF_PM_G2: + case KAZE::DIFF_PM_G2: pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); break; - case cv::DIFF_WEICKERT: + case KAZE::DIFF_WEICKERT: weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); break; - case cv::DIFF_CHARBONNIER: + case KAZE::DIFF_CHARBONNIER: charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); break; default: @@ -155,7 +155,7 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) // Perform FED n inner steps for (int j = 0; j < nsteps_[i - 1]; j++) { - cv::details::kaze::nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); + nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); } } @@ -167,7 +167,7 @@ int AKAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat& img) * @brief This method selects interesting keypoints through the nonlinear scale space * @param kpts Vector of detected keypoints */ -void AKAZEFeatures::Feature_Detection(std::vector& kpts) +void AKAZEFeatures::Feature_Detection(std::vector& kpts) { kpts.clear(); Compute_Determinant_Hessian_Response(); @@ -176,7 +176,7 @@ void AKAZEFeatures::Feature_Detection(std::vector& kpts) } /* ************************************************************************* */ -class MultiscaleDerivativesAKAZEInvoker : public cv::ParallelLoopBody +class MultiscaleDerivativesAKAZEInvoker : public ParallelLoopBody { public: explicit MultiscaleDerivativesAKAZEInvoker(std::vector& ev, const AKAZEOptions& opt) @@ -185,7 +185,7 @@ public: { } - void operator()(const cv::Range& range) const + void operator()(const Range& range) const { std::vector& evolution = *evolution_; @@ -219,7 +219,7 @@ private: */ void AKAZEFeatures::Compute_Multiscale_Derivatives(void) { - cv::parallel_for_(cv::Range(0, (int)evolution_.size()), + parallel_for_(Range(0, (int)evolution_.size()), MultiscaleDerivativesAKAZEInvoker(evolution_, options_)); } @@ -253,7 +253,7 @@ void AKAZEFeatures::Compute_Determinant_Hessian_Response(void) { * @brief This method finds extrema in the nonlinear scale space * @param kpts Vector of detected keypoints */ -void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) +void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) { float value = 0.0; @@ -261,8 +261,8 @@ void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) int npoints = 0, id_repeated = 0; int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0; bool is_extremum = false, is_repeated = false, is_out = false; - cv::KeyPoint point; - vector kpts_aux; + KeyPoint point; + vector kpts_aux; // Set maximum size if (options_.descriptor == AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_MLDB) { @@ -365,7 +365,7 @@ void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) for (size_t i = 0; i < kpts_aux.size(); i++) { is_repeated = false; - const cv::KeyPoint& pt = kpts_aux[i]; + const KeyPoint& pt = kpts_aux[i]; for (size_t j = i + 1; j < kpts_aux.size(); j++) { // Compare response with the upper scale @@ -392,7 +392,7 @@ void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector& kpts) * @brief This method performs subpixel refinement of the detected keypoints * @param kpts Vector of detected keypoints */ -void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) +void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) { float Dx = 0.0, Dy = 0.0, ratio = 0.0; float Dxx = 0.0, Dyy = 0.0, Dxy = 0.0; @@ -433,7 +433,7 @@ void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) b(0) = -Dx; b(1) = -Dy; - cv::solve(A, b, dst, DECOMP_LU); + solve(A, b, dst, DECOMP_LU); if (fabs(dst(0)) <= 1.0f && fabs(dst(1)) <= 1.0f) { kpts[i].pt.x = x + dst(0); @@ -456,10 +456,10 @@ void AKAZEFeatures::Do_Subpixel_Refinement(std::vector& kpts) /* ************************************************************************* */ -class SURF_Descriptor_Upright_64_Invoker : public cv::ParallelLoopBody +class SURF_Descriptor_Upright_64_Invoker : public ParallelLoopBody { public: - SURF_Descriptor_Upright_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + SURF_Descriptor_Upright_64_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -474,18 +474,18 @@ public: } } - void Get_SURF_Descriptor_Upright_64(const cv::KeyPoint& kpt, float* desc) const; + void Get_SURF_Descriptor_Upright_64(const KeyPoint& kpt, float* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; }; -class SURF_Descriptor_64_Invoker : public cv::ParallelLoopBody +class SURF_Descriptor_64_Invoker : public ParallelLoopBody { public: - SURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + SURF_Descriptor_64_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -501,18 +501,18 @@ public: } } - void Get_SURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + void Get_SURF_Descriptor_64(const KeyPoint& kpt, float* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; }; -class MSURF_Upright_Descriptor_64_Invoker : public cv::ParallelLoopBody +class MSURF_Upright_Descriptor_64_Invoker : public ParallelLoopBody { public: - MSURF_Upright_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + MSURF_Upright_Descriptor_64_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -527,18 +527,18 @@ public: } } - void Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + void Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; }; -class MSURF_Descriptor_64_Invoker : public cv::ParallelLoopBody +class MSURF_Descriptor_64_Invoker : public ParallelLoopBody { public: - MSURF_Descriptor_64_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution) + MSURF_Descriptor_64_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -554,18 +554,18 @@ public: } } - void Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; + void Get_MSURF_Descriptor_64(const KeyPoint& kpt, float* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; }; -class Upright_MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody +class Upright_MLDB_Full_Descriptor_Invoker : public ParallelLoopBody { public: - Upright_MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) + Upright_MLDB_Full_Descriptor_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution, AKAZEOptions& options) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -581,24 +581,24 @@ public: } } - void Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; + void Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; AKAZEOptions* options_; }; -class Upright_MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody +class Upright_MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody { public: - Upright_MLDB_Descriptor_Subset_Invoker(std::vector& kpts, - cv::Mat& desc, + Upright_MLDB_Descriptor_Subset_Invoker(std::vector& kpts, + Mat& desc, std::vector& evolution, AKAZEOptions& options, - cv::Mat descriptorSamples, - cv::Mat descriptorBits) + Mat descriptorSamples, + Mat descriptorBits) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -616,22 +616,22 @@ public: } } - void Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; + void Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; AKAZEOptions* options_; - cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. - cv::Mat descriptorBits_; + Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. + Mat descriptorBits_; }; -class MLDB_Full_Descriptor_Invoker : public cv::ParallelLoopBody +class MLDB_Full_Descriptor_Invoker : public ParallelLoopBody { public: - MLDB_Full_Descriptor_Invoker(std::vector& kpts, cv::Mat& desc, std::vector& evolution, AKAZEOptions& options) + MLDB_Full_Descriptor_Invoker(std::vector& kpts, Mat& desc, std::vector& evolution, AKAZEOptions& options) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -648,28 +648,28 @@ public: } } - void Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char* desc) const; + void Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const; void MLDB_Fill_Values(float* values, int sample_step, int level, float xf, float yf, float co, float si, float scale) const; void MLDB_Binary_Comparisons(float* values, unsigned char* desc, int count, int& dpos) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; AKAZEOptions* options_; }; -class MLDB_Descriptor_Subset_Invoker : public cv::ParallelLoopBody +class MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody { public: - MLDB_Descriptor_Subset_Invoker(std::vector& kpts, - cv::Mat& desc, + MLDB_Descriptor_Subset_Invoker(std::vector& kpts, + Mat& desc, std::vector& evolution, AKAZEOptions& options, - cv::Mat descriptorSamples, - cv::Mat descriptorBits) + Mat descriptorSamples, + Mat descriptorBits) : keypoints_(&kpts) , descriptors_(&desc) , evolution_(&evolution) @@ -688,16 +688,16 @@ public: } } - void Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char* desc) const; + void Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const; private: - std::vector* keypoints_; - cv::Mat* descriptors_; + std::vector* keypoints_; + Mat* descriptors_; std::vector* evolution_; AKAZEOptions* options_; - cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. - cv::Mat descriptorBits_; + Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. + Mat descriptorBits_; }; /** @@ -705,7 +705,7 @@ private: * @param kpts Vector of detected keypoints * @param desc Matrix to store the descriptors */ -void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat& desc) +void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, Mat& desc) { for(size_t i = 0; i < kpts.size(); i++) { @@ -714,17 +714,17 @@ void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat // Allocate memory for the matrix with the descriptors if (options_.descriptor < AKAZE::DESCRIPTOR_MLDB_UPRIGHT) { - desc = cv::Mat::zeros((int)kpts.size(), 64, CV_32FC1); + desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1); } else { // We use the full length binary descriptor -> 486 bits if (options_.descriptor_size == 0) { int t = (6 + 36 + 120)*options_.descriptor_channels; - desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1); + desc = Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1); } else { // We use the random bit selection length binary descriptor - desc = cv::Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1); + desc = Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1); } } @@ -732,28 +732,28 @@ void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat { case AKAZE::DESCRIPTOR_KAZE_UPRIGHT: // Upright descriptors, not invariant to rotation { - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_)); + parallel_for_(Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_)); } break; case AKAZE::DESCRIPTOR_KAZE: { - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_)); + parallel_for_(Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_)); } break; case AKAZE::DESCRIPTOR_MLDB_UPRIGHT: // Upright descriptors, not invariant to rotation { if (options_.descriptor_size == 0) - cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); + parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); else - cv::parallel_for_(cv::Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); + parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); } break; case AKAZE::DESCRIPTOR_MLDB: { if (options_.descriptor_size == 0) - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); + parallel_for_(Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_)); else - cv::parallel_for_(cv::Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); + parallel_for_(Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_)); } break; } @@ -766,7 +766,7 @@ void AKAZEFeatures::Compute_Descriptors(std::vector& kpts, cv::Mat * @note The orientation is computed using a similar approach as described in the * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 */ -void AKAZEFeatures::Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector& evolution_) +void AKAZEFeatures::Compute_Main_Orientation(KeyPoint& kpt, const std::vector& evolution_) { /* ************************************************************************* */ /// Lookup table for 2d gaussian (sigma = 2.5) where (0,0) is top left and (6,6) is bottom right @@ -854,7 +854,7 @@ void AKAZEFeatures::Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vecto * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { +void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float *desc) const { float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -977,7 +977,7 @@ void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const cv::KeyPoint& kpt, float *desc) const { +void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, float *desc) const { float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -1101,7 +1101,7 @@ void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const cv::KeyPoint& kp * @param kpt Input keypoint * @param desc Descriptor vector */ -void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { +void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const { float di = 0.0, dx = 0.0, dy = 0.0; float ri = 0.0, rx = 0.0, ry = 0.0, xf = 0.0, yf = 0.0; @@ -1114,9 +1114,9 @@ void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(cons const std::vector& evolution = *evolution_; // Matrices for the M-LDB descriptor - cv::Mat values_1 = cv::Mat::zeros(4, options.descriptor_channels, CV_32FC1); - cv::Mat values_2 = cv::Mat::zeros(9, options.descriptor_channels, CV_32FC1); - cv::Mat values_3 = cv::Mat::zeros(16, options.descriptor_channels, CV_32FC1); + Mat values_1 = Mat::zeros(4, options.descriptor_channels, CV_32FC1); + Mat values_2 = Mat::zeros(9, options.descriptor_channels, CV_32FC1); + Mat values_3 = Mat::zeros(16, options.descriptor_channels, CV_32FC1); // Get the information from the keypoint ratio = (float)(1 << kpt.octave); @@ -1395,7 +1395,7 @@ void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsign * @param kpt Input keypoint * @param desc Descriptor vector */ -void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const cv::KeyPoint& kpt, unsigned char *desc) const { +void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const { const int max_channels = 3; CV_Assert(options_->descriptor_channels <= max_channels); @@ -1428,7 +1428,7 @@ void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const cv::KeyPoint& * @param kpt Input keypoint * @param desc Descriptor vector */ -void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { +void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const { float di = 0.f, dx = 0.f, dy = 0.f; float rx = 0.f, ry = 0.f; @@ -1449,7 +1449,7 @@ void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const cv::KeyPoi float si = sin(angle); // Allocate memory for the matrix of values - cv::Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); + Mat values = Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); // Sample everything, but only do the comparisons vector steps(3); @@ -1522,7 +1522,7 @@ void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const cv::KeyPoi * @param kpt Input keypoint * @param desc Descriptor vector */ -void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const cv::KeyPoint& kpt, unsigned char *desc) const { +void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const { float di = 0.0f, dx = 0.0f, dy = 0.0f; float rx = 0.0f, ry = 0.0f; @@ -1540,7 +1540,7 @@ void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset( float xf = kpt.pt.x / ratio; // Allocate memory for the matrix of values - Mat values = cv::Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); + Mat values = Mat_::zeros((4 + 9 + 16)*options.descriptor_channels, 1); vector steps(3); steps.at(0) = options.descriptor_pattern_size; @@ -1614,7 +1614,7 @@ void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset( * @note The function keeps the 18 bits (3-channels by 6 comparisons) of the * coarser grid, since it provides the most robust estimations */ -void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, int nbits, +void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits, int pattern_size, int nchannels) { int ssz = 0; @@ -1718,4 +1718,3 @@ void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, int } } -} diff --git a/modules/features2d/src/kaze/AKAZEFeatures.h b/modules/features2d/src/kaze/AKAZEFeatures.h index 9119c97f2f..14800bc372 100644 --- a/modules/features2d/src/kaze/AKAZEFeatures.h +++ b/modules/features2d/src/kaze/AKAZEFeatures.h @@ -11,10 +11,12 @@ /* ************************************************************************* */ // Includes -#include "../precomp.hpp" #include "AKAZEConfig.h" #include "TEvolution.h" +namespace cv +{ + /* ************************************************************************* */ // AKAZE Class Declaration class AKAZEFeatures { @@ -22,7 +24,7 @@ class AKAZEFeatures { private: AKAZEOptions options_; ///< Configuration options for AKAZE - std::vector evolution_; ///< Vector of nonlinear diffusion evolution + std::vector evolution_; ///< Vector of nonlinear diffusion evolution /// FED parameters int ncycles_; ///< Number of cycles @@ -59,4 +61,6 @@ public: void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons, int nbits, int pattern_size, int nchannels); +} + #endif diff --git a/modules/features2d/src/kaze/KAZEFeatures.cpp b/modules/features2d/src/kaze/KAZEFeatures.cpp index 69e9a4b69f..702a8a021e 100644 --- a/modules/features2d/src/kaze/KAZEFeatures.cpp +++ b/modules/features2d/src/kaze/KAZEFeatures.cpp @@ -20,14 +20,15 @@ * @date Jan 21, 2012 * @author Pablo F. Alcantarilla */ - +#include "../precomp.hpp" #include "KAZEFeatures.h" #include "utils.h" +namespace cv +{ + // Namespaces using namespace std; -using namespace cv; -using namespace cv::details::kaze; /* ************************************************************************* */ /** @@ -52,19 +53,20 @@ KAZEFeatures::KAZEFeatures(KAZEOptions& options) void KAZEFeatures::Allocate_Memory_Evolution(void) { // Allocate the dimension of the matrices for the evolution - for (int i = 0; i <= options_.omax - 1; i++) { - for (int j = 0; j <= options_.nsublevels - 1; j++) { - + for (int i = 0; i <= options_.omax - 1; i++) + { + for (int j = 0; j <= options_.nsublevels - 1; j++) + { TEvolution aux; - aux.Lx = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Ly = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Lxx = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Lxy = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Lyy = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Lt = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Lsmooth = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.Ldet = cv::Mat::zeros(options_.img_height, options_.img_width, CV_32F); - aux.esigma = options_.soffset*pow((float)2.0f, (float)(j) / (float)(options_.nsublevels)+i); + aux.Lx = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Ly = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lxx = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lxy = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lyy = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lt = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Lsmooth = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.Ldet = Mat::zeros(options_.img_height, options_.img_width, CV_32F); + aux.esigma = options_.soffset*pow((float)2.0f, (float)(j) / (float)(options_.nsublevels)+i); aux.etime = 0.5f*(aux.esigma*aux.esigma); aux.sigma_size = fRound(aux.esigma); aux.octave = i; @@ -74,7 +76,8 @@ void KAZEFeatures::Allocate_Memory_Evolution(void) { } // Allocate memory for the FED number of cycles and time steps - for (size_t i = 1; i < evolution_.size(); i++) { + for (size_t i = 1; i < evolution_.size(); i++) + { int naux = 0; vector tau; float ttime = 0.0; @@ -92,47 +95,43 @@ void KAZEFeatures::Allocate_Memory_Evolution(void) { * @param img Input image for which the nonlinear scale space needs to be created * @return 0 if the nonlinear scale space was created successfully. -1 otherwise */ -int KAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat &img) +int KAZEFeatures::Create_Nonlinear_Scale_Space(const Mat &img) { CV_Assert(evolution_.size() > 0); // Copy the original image to the first level of the evolution img.copyTo(evolution_[0].Lt); - gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); - gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options_.sderivatives); + gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); + gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options_.sderivatives); // Firstly compute the kcontrast factor Compute_KContrast(evolution_[0].Lt, options_.kcontrast_percentille); // Allocate memory for the flow and step images - cv::Mat Lflow = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); - cv::Mat Lstep = cv::Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); + Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); // Now generate the rest of evolution levels - for (size_t i = 1; i < evolution_.size(); i++) { - + for (size_t i = 1; i < evolution_.size(); i++) + { evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); - gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options_.sderivatives); + gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options_.sderivatives); // Compute the Gaussian derivatives Lx and Ly Scharr(evolution_[i].Lsmooth, evolution_[i].Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT); Scharr(evolution_[i].Lsmooth, evolution_[i].Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT); // Compute the conductivity equation - if (options_.diffusivity == cv::DIFF_PM_G1) { - pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); - } - else if (options_.diffusivity == cv::DIFF_PM_G2) { - pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); - } - else if (options_.diffusivity == cv::DIFF_WEICKERT) { - weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); - } + if (options_.diffusivity == KAZE::DIFF_PM_G1) + pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + else if (options_.diffusivity == KAZE::DIFF_PM_G2) + pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); + else if (options_.diffusivity == KAZE::DIFF_WEICKERT) + weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); // Perform FED n inner steps - for (int j = 0; j < nsteps_[i - 1]; j++) { + for (int j = 0; j < nsteps_[i - 1]; j++) nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); - } } return 0; @@ -144,7 +143,7 @@ int KAZEFeatures::Create_Nonlinear_Scale_Space(const cv::Mat &img) * @param img Input image * @param kpercentile Percentile of the gradient histogram */ -void KAZEFeatures::Compute_KContrast(const cv::Mat &img, const float &kpercentile) +void KAZEFeatures::Compute_KContrast(const Mat &img, const float &kpercentile) { options_.kcontrast = compute_k_percentile(img, kpercentile, options_.sderivatives, options_.kcontrast_bins, 0, 0); } @@ -181,7 +180,7 @@ void KAZEFeatures::Compute_Detector_Response(void) * @brief This method selects interesting keypoints through the nonlinear scale space * @param kpts Vector of keypoints */ -void KAZEFeatures::Feature_Detection(std::vector& kpts) +void KAZEFeatures::Feature_Detection(std::vector& kpts) { kpts.clear(); Compute_Detector_Response(); @@ -190,14 +189,14 @@ void KAZEFeatures::Feature_Detection(std::vector& kpts) } /* ************************************************************************* */ -class MultiscaleDerivativesKAZEInvoker : public cv::ParallelLoopBody +class MultiscaleDerivativesKAZEInvoker : public ParallelLoopBody { public: explicit MultiscaleDerivativesKAZEInvoker(std::vector& ev) : evolution_(&ev) { } - void operator()(const cv::Range& range) const + void operator()(const Range& range) const { std::vector& evolution = *evolution_; for (int i = range.start; i < range.end; i++) @@ -226,74 +225,79 @@ private: */ void KAZEFeatures::Compute_Multiscale_Derivatives(void) { - cv::parallel_for_(cv::Range(0, (int)evolution_.size()), + parallel_for_(Range(0, (int)evolution_.size()), MultiscaleDerivativesKAZEInvoker(evolution_)); } /* ************************************************************************* */ -class FindExtremumKAZEInvoker : public cv::ParallelLoopBody +class FindExtremumKAZEInvoker : public ParallelLoopBody { public: - explicit FindExtremumKAZEInvoker(std::vector& ev, std::vector >& kpts_par, + explicit FindExtremumKAZEInvoker(std::vector& ev, std::vector >& kpts_par, const KAZEOptions& options) : evolution_(&ev), kpts_par_(&kpts_par), options_(options) { } - void operator()(const cv::Range& range) const + void operator()(const Range& range) const { std::vector& evolution = *evolution_; - std::vector >& kpts_par = *kpts_par_; + std::vector >& kpts_par = *kpts_par_; for (int i = range.start; i < range.end; i++) { float value = 0.0; bool is_extremum = false; - for (int ix = 1; ix < options_.img_height - 1; ix++) { - for (int jx = 1; jx < options_.img_width - 1; jx++) { - - is_extremum = false; - value = *(evolution[i].Ldet.ptr(ix)+jx); - - // Filter the points with the detector threshold - if (value > options_.dthreshold) { - if (value >= *(evolution[i].Ldet.ptr(ix)+jx - 1)) { - // First check on the same scale - if (check_maximum_neighbourhood(evolution[i].Ldet, 1, value, ix, jx, 1)) { - // Now check on the lower scale - if (check_maximum_neighbourhood(evolution[i - 1].Ldet, 1, value, ix, jx, 0)) { - // Now check on the upper scale - if (check_maximum_neighbourhood(evolution[i + 1].Ldet, 1, value, ix, jx, 0)) { - is_extremum = true; - } - } - } - } + for (int ix = 1; ix < options_.img_height - 1; ix++) + { + for (int jx = 1; jx < options_.img_width - 1; jx++) + { + is_extremum = false; + value = *(evolution[i].Ldet.ptr(ix)+jx); + + // Filter the points with the detector threshold + if (value > options_.dthreshold) + { + if (value >= *(evolution[i].Ldet.ptr(ix)+jx - 1)) + { + // First check on the same scale + if (check_maximum_neighbourhood(evolution[i].Ldet, 1, value, ix, jx, 1)) + { + // Now check on the lower scale + if (check_maximum_neighbourhood(evolution[i - 1].Ldet, 1, value, ix, jx, 0)) + { + // Now check on the upper scale + if (check_maximum_neighbourhood(evolution[i + 1].Ldet, 1, value, ix, jx, 0)) + is_extremum = true; + } } + } + } - // Add the point of interest!! - if (is_extremum == true) { - cv::KeyPoint point; - point.pt.x = (float)jx; - point.pt.y = (float)ix; - point.response = fabs(value); - point.size = evolution[i].esigma; - point.octave = (int)evolution[i].octave; - point.class_id = i; - - // We use the angle field for the sublevel value - // Then, we will replace this angle field with the main orientation - point.angle = static_cast(evolution[i].sublevel); - kpts_par[i - 1].push_back(point); - } + // Add the point of interest!! + if (is_extremum) + { + KeyPoint point; + point.pt.x = (float)jx; + point.pt.y = (float)ix; + point.response = fabs(value); + point.size = evolution[i].esigma; + point.octave = (int)evolution[i].octave; + point.class_id = i; + + // We use the angle field for the sublevel value + // Then, we will replace this angle field with the main orientation + point.angle = static_cast(evolution[i].sublevel); + kpts_par[i - 1].push_back(point); } + } } } } private: std::vector* evolution_; - std::vector >* kpts_par_; + std::vector >* kpts_par_; KAZEOptions options_; }; @@ -304,7 +308,7 @@ private: * @param kpts Vector of keypoints * @note We compute features for each of the nonlinear scale space level in a different processing thread */ -void KAZEFeatures::Determinant_Hessian(std::vector& kpts) +void KAZEFeatures::Determinant_Hessian(std::vector& kpts) { int level = 0; float dist = 0.0, smax = 3.0; @@ -325,12 +329,14 @@ void KAZEFeatures::Determinant_Hessian(std::vector& kpts) kpts_par_.push_back(aux); } - cv::parallel_for_(cv::Range(1, (int)evolution_.size()-1), - FindExtremumKAZEInvoker(evolution_, kpts_par_, options_)); + parallel_for_(Range(1, (int)evolution_.size()-1), + FindExtremumKAZEInvoker(evolution_, kpts_par_, options_)); // Now fill the vector of keypoints!!! - for (int i = 0; i < (int)kpts_par_.size(); i++) { - for (int j = 0; j < (int)kpts_par_[i].size(); j++) { + for (int i = 0; i < (int)kpts_par_.size(); i++) + { + for (int j = 0; j < (int)kpts_par_[i].size(); j++) + { level = i + 1; is_extremum = true; is_repeated = false; @@ -388,7 +394,7 @@ void KAZEFeatures::Determinant_Hessian(std::vector& kpts) * @brief This method performs subpixel refinement of the detected keypoints * @param kpts Vector of detected keypoints */ -void KAZEFeatures::Do_Subpixel_Refinement(std::vector &kpts) { +void KAZEFeatures::Do_Subpixel_Refinement(std::vector &kpts) { int step = 1; int x = 0, y = 0; @@ -482,10 +488,10 @@ void KAZEFeatures::Do_Subpixel_Refinement(std::vector &kpts) { } /* ************************************************************************* */ -class KAZE_Descriptor_Invoker : public cv::ParallelLoopBody +class KAZE_Descriptor_Invoker : public ParallelLoopBody { public: - KAZE_Descriptor_Invoker(std::vector &kpts, cv::Mat &desc, std::vector& evolution, const KAZEOptions& options) + KAZE_Descriptor_Invoker(std::vector &kpts, Mat &desc, std::vector& evolution, const KAZEOptions& options) : kpts_(&kpts) , desc_(&desc) , evolution_(&evolution) @@ -497,10 +503,10 @@ public: { } - void operator() (const cv::Range& range) const + void operator() (const Range& range) const { - std::vector &kpts = *kpts_; - cv::Mat &desc = *desc_; + std::vector &kpts = *kpts_; + Mat &desc = *desc_; std::vector &evolution = *evolution_; for (int i = range.start; i < range.end; i++) @@ -526,13 +532,13 @@ public: } } private: - void Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; - void Get_KAZE_Descriptor_64(const cv::KeyPoint& kpt, float* desc) const; - void Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint& kpt, float* desc) const; - void Get_KAZE_Descriptor_128(const cv::KeyPoint& kpt, float *desc) const; + void Get_KAZE_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const; + void Get_KAZE_Descriptor_64(const KeyPoint& kpt, float* desc) const; + void Get_KAZE_Upright_Descriptor_128(const KeyPoint& kpt, float* desc) const; + void Get_KAZE_Descriptor_128(const KeyPoint& kpt, float *desc) const; - std::vector * kpts_; - cv::Mat * desc_; + std::vector * kpts_; + Mat * desc_; std::vector * evolution_; KAZEOptions options_; }; @@ -543,7 +549,7 @@ private: * @param kpts Vector of keypoints * @param desc Matrix with the feature descriptors */ -void KAZEFeatures::Feature_Description(std::vector &kpts, cv::Mat &desc) +void KAZEFeatures::Feature_Description(std::vector &kpts, Mat &desc) { for(size_t i = 0; i < kpts.size(); i++) { @@ -558,7 +564,7 @@ void KAZEFeatures::Feature_Description(std::vector &kpts, cv::Mat desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1); } - cv::parallel_for_(cv::Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options_)); + parallel_for_(Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options_)); } /* ************************************************************************* */ @@ -568,7 +574,7 @@ void KAZEFeatures::Feature_Description(std::vector &kpts, cv::Mat * @note The orientation is computed using a similar approach as described in the * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 */ -void KAZEFeatures::Compute_Main_Orientation(cv::KeyPoint &kpt, const std::vector& evolution_, const KAZEOptions& options) +void KAZEFeatures::Compute_Main_Orientation(KeyPoint &kpt, const std::vector& evolution_, const KAZEOptions& options) { int ix = 0, iy = 0, idx = 0, s = 0, level = 0; float xf = 0.0, yf = 0.0, gweight = 0.0; @@ -647,7 +653,7 @@ void KAZEFeatures::Compute_Main_Orientation(cv::KeyPoint &kpt, const std::vector * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint &kpt, float *desc) const +void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const KeyPoint &kpt, float *desc) const { float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -775,7 +781,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const cv::KeyPoint * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const cv::KeyPoint &kpt, float *desc) const +void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const KeyPoint &kpt, float *desc) const { float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -904,7 +910,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const cv::KeyPoint &kpt, fl * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint &kpt, float *desc) const +void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const KeyPoint &kpt, float *desc) const { float gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -1056,7 +1062,7 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const cv::KeyPoint * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, * ECCV 2008 */ -void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const cv::KeyPoint &kpt, float *desc) const +void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const KeyPoint &kpt, float *desc) const { float gauss_s1 = 0.0, gauss_s2 = 0.0; float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; @@ -1202,3 +1208,5 @@ void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const cv::KeyPoint &kpt, f desc[i] /= len; } } + +} diff --git a/modules/features2d/src/kaze/KAZEFeatures.h b/modules/features2d/src/kaze/KAZEFeatures.h index 98c8307888..934dfff29d 100644 --- a/modules/features2d/src/kaze/KAZEFeatures.h +++ b/modules/features2d/src/kaze/KAZEFeatures.h @@ -22,8 +22,8 @@ namespace cv /* ************************************************************************* */ // KAZE Class Declaration -class KAZEFeatures { - +class KAZEFeatures +{ private: /// Parameters of the Nonlinear diffusion class diff --git a/modules/features2d/src/kaze/TEvolution.h b/modules/features2d/src/kaze/TEvolution.h index bc7654ef05..b033bce275 100644 --- a/modules/features2d/src/kaze/TEvolution.h +++ b/modules/features2d/src/kaze/TEvolution.h @@ -8,10 +8,13 @@ #ifndef __OPENCV_FEATURES_2D_TEVOLUTION_H__ #define __OPENCV_FEATURES_2D_TEVOLUTION_H__ +namespace cv +{ + /* ************************************************************************* */ /// KAZE/A-KAZE nonlinear diffusion filtering evolution -struct TEvolution { - +struct TEvolution +{ TEvolution() { etime = 0.0f; esigma = 0.0f; @@ -20,11 +23,11 @@ struct TEvolution { sigma_size = 0; } - cv::Mat Lx, Ly; ///< First order spatial derivatives - cv::Mat Lxx, Lxy, Lyy; ///< Second order spatial derivatives - cv::Mat Lt; ///< Evolution image - cv::Mat Lsmooth; ///< Smoothed image - cv::Mat Ldet; ///< Detector response + Mat Lx, Ly; ///< First order spatial derivatives + Mat Lxx, Lxy, Lyy; ///< Second order spatial derivatives + Mat Lt; ///< Evolution image + Mat Lsmooth; ///< Smoothed image + Mat Ldet; ///< Detector response float etime; ///< Evolution time float esigma; ///< Evolution sigma. For linear diffusion t = sigma^2 / 2 int octave; ///< Image octave @@ -32,4 +35,6 @@ struct TEvolution { int sigma_size; ///< Integer esigma. For computing the feature detector responses }; +} + #endif diff --git a/modules/features2d/src/kaze/nldiffusion_functions.cpp b/modules/features2d/src/kaze/nldiffusion_functions.cpp index 1c0d70a75a..85ed5de91d 100644 --- a/modules/features2d/src/kaze/nldiffusion_functions.cpp +++ b/modules/features2d/src/kaze/nldiffusion_functions.cpp @@ -22,502 +22,500 @@ * @author Pablo F. Alcantarilla */ +#include "../precomp.hpp" #include "nldiffusion_functions.h" #include // Namespaces + +/* ************************************************************************* */ + +namespace cv +{ using namespace std; -using namespace cv; /* ************************************************************************* */ +/** + * @brief This function smoothes an image with a Gaussian kernel + * @param src Input image + * @param dst Output image + * @param ksize_x Kernel size in X-direction (horizontal) + * @param ksize_y Kernel size in Y-direction (vertical) + * @param sigma Kernel standard deviation + */ +void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) { -namespace cv { - namespace details { - namespace kaze { - - /* ************************************************************************* */ - /** - * @brief This function smoothes an image with a Gaussian kernel - * @param src Input image - * @param dst Output image - * @param ksize_x Kernel size in X-direction (horizontal) - * @param ksize_y Kernel size in Y-direction (vertical) - * @param sigma Kernel standard deviation - */ - void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) { - - int ksize_x_ = 0, ksize_y_ = 0; - - // Compute an appropriate kernel size according to the specified sigma - if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) { - ksize_x_ = (int)ceil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f))); - ksize_y_ = ksize_x_; - } + int ksize_x_ = 0, ksize_y_ = 0; - // The kernel size must be and odd number - if ((ksize_x_ % 2) == 0) { - ksize_x_ += 1; - } + // Compute an appropriate kernel size according to the specified sigma + if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) { + ksize_x_ = (int)ceil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f))); + ksize_y_ = ksize_x_; + } - if ((ksize_y_ % 2) == 0) { - ksize_y_ += 1; - } + // The kernel size must be and odd number + if ((ksize_x_ % 2) == 0) { + ksize_x_ += 1; + } - // Perform the Gaussian Smoothing with border replication - GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE); - } + if ((ksize_y_ % 2) == 0) { + ksize_y_ += 1; + } - /* ************************************************************************* */ - /** - * @brief This function computes image derivatives with Scharr kernel - * @param src Input image - * @param dst Output image - * @param xorder Derivative order in X-direction (horizontal) - * @param yorder Derivative order in Y-direction (vertical) - * @note Scharr operator approximates better rotation invariance than - * other stencils such as Sobel. See Weickert and Scharr, - * A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance, - * Journal of Visual Communication and Image Representation 2002 - */ - void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) { - Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT); - } + // Perform the Gaussian Smoothing with border replication + GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE); +} - /* ************************************************************************* */ - /** - * @brief This function computes the Perona and Malik conductivity coefficient g1 - * g1 = exp(-|dL|^2/k^2) - * @param Lx First order image derivative in X-direction (horizontal) - * @param Ly First order image derivative in Y-direction (vertical) - * @param dst Output image - * @param k Contrast factor parameter - */ - void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - - Size sz = Lx.size(); - float inv_k = 1.0f / (k*k); - for (int y = 0; y < sz.height; y++) { - - const float* Lx_row = Lx.ptr(y); - const float* Ly_row = Ly.ptr(y); - float* dst_row = dst.ptr(y); - - for (int x = 0; x < sz.width; x++) { - dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); - } - } +/* ************************************************************************* */ +/** + * @brief This function computes image derivatives with Scharr kernel + * @param src Input image + * @param dst Output image + * @param xorder Derivative order in X-direction (horizontal) + * @param yorder Derivative order in Y-direction (vertical) + * @note Scharr operator approximates better rotation invariance than + * other stencils such as Sobel. See Weickert and Scharr, + * A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance, + * Journal of Visual Communication and Image Representation 2002 + */ +void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) { + Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT); +} - exp(dst, dst); - } +/* ************************************************************************* */ +/** + * @brief This function computes the Perona and Malik conductivity coefficient g1 + * g1 = exp(-|dL|^2/k^2) + * @param Lx First order image derivative in X-direction (horizontal) + * @param Ly First order image derivative in Y-direction (vertical) + * @param dst Output image + * @param k Contrast factor parameter + */ +void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - /* ************************************************************************* */ - /** - * @brief This function computes the Perona and Malik conductivity coefficient g2 - * g2 = 1 / (1 + dL^2 / k^2) - * @param Lx First order image derivative in X-direction (horizontal) - * @param Ly First order image derivative in Y-direction (vertical) - * @param dst Output image - * @param k Contrast factor parameter - */ - void pm_g2(const cv::Mat &Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - - Size sz = Lx.size(); - dst.create(sz, Lx.type()); - float k2inv = 1.0f / (k * k); - - for(int y = 0; y < sz.height; y++) { - const float *Lx_row = Lx.ptr(y); - const float *Ly_row = Ly.ptr(y); - float* dst_row = dst.ptr(y); - for(int x = 0; x < sz.width; x++) { - dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv)); - } - } - } - /* ************************************************************************* */ - /** - * @brief This function computes Weickert conductivity coefficient gw - * @param Lx First order image derivative in X-direction (horizontal) - * @param Ly First order image derivative in Y-direction (vertical) - * @param dst Output image - * @param k Contrast factor parameter - * @note For more information check the following paper: J. Weickert - * Applications of nonlinear diffusion in image processing and computer vision, - * Proceedings of Algorithmy 2000 - */ - void weickert_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - - Size sz = Lx.size(); - float inv_k = 1.0f / (k*k); - for (int y = 0; y < sz.height; y++) { - - const float* Lx_row = Lx.ptr(y); - const float* Ly_row = Ly.ptr(y); - float* dst_row = dst.ptr(y); - - for (int x = 0; x < sz.width; x++) { - float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]); - dst_row[x] = -3.315f/(dL*dL*dL*dL); - } - } + Size sz = Lx.size(); + float inv_k = 1.0f / (k*k); + for (int y = 0; y < sz.height; y++) { - exp(dst, dst); - dst = 1.0 - dst; - } + const float* Lx_row = Lx.ptr(y); + const float* Ly_row = Ly.ptr(y); + float* dst_row = dst.ptr(y); + for (int x = 0; x < sz.width; x++) { + dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); + } + } - /* ************************************************************************* */ - /** - * @brief This function computes Charbonnier conductivity coefficient gc - * gc = 1 / sqrt(1 + dL^2 / k^2) - * @param Lx First order image derivative in X-direction (horizontal) - * @param Ly First order image derivative in Y-direction (vertical) - * @param dst Output image - * @param k Contrast factor parameter - * @note For more information check the following paper: J. Weickert - * Applications of nonlinear diffusion in image processing and computer vision, - * Proceedings of Algorithmy 2000 - */ - void charbonnier_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - - Size sz = Lx.size(); - float inv_k = 1.0f / (k*k); - for (int y = 0; y < sz.height; y++) { - - const float* Lx_row = Lx.ptr(y); - const float* Ly_row = Ly.ptr(y); - float* dst_row = dst.ptr(y); - - for (int x = 0; x < sz.width; x++) { - float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); - dst_row[x] = 1.0f / den; - } - } - } + exp(dst, dst); +} +/* ************************************************************************* */ +/** + * @brief This function computes the Perona and Malik conductivity coefficient g2 + * g2 = 1 / (1 + dL^2 / k^2) + * @param Lx First order image derivative in X-direction (horizontal) + * @param Ly First order image derivative in Y-direction (vertical) + * @param dst Output image + * @param k Contrast factor parameter + */ +void pm_g2(const cv::Mat &Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { + + Size sz = Lx.size(); + dst.create(sz, Lx.type()); + float k2inv = 1.0f / (k * k); + + for(int y = 0; y < sz.height; y++) { + const float *Lx_row = Lx.ptr(y); + const float *Ly_row = Ly.ptr(y); + float* dst_row = dst.ptr(y); + for(int x = 0; x < sz.width; x++) { + dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv)); + } + } +} +/* ************************************************************************* */ +/** + * @brief This function computes Weickert conductivity coefficient gw + * @param Lx First order image derivative in X-direction (horizontal) + * @param Ly First order image derivative in Y-direction (vertical) + * @param dst Output image + * @param k Contrast factor parameter + * @note For more information check the following paper: J. Weickert + * Applications of nonlinear diffusion in image processing and computer vision, + * Proceedings of Algorithmy 2000 + */ +void weickert_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { - /* ************************************************************************* */ - /** - * @brief This function computes a good empirical value for the k contrast factor - * given an input image, the percentile (0-1), the gradient scale and the number of - * bins in the histogram - * @param img Input image - * @param perc Percentile of the image gradient histogram (0-1) - * @param gscale Scale for computing the image gradient histogram - * @param nbins Number of histogram bins - * @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel - * @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel - * @return k contrast factor - */ - float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) { - - int nbin = 0, nelements = 0, nthreshold = 0, k = 0; - float kperc = 0.0, modg = 0.0; - float npoints = 0.0; - float hmax = 0.0; - - // Create the array for the histogram - std::vector hist(nbins, 0); - - // Create the matrices - Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F); - Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F); - Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F); - - // Perform the Gaussian convolution - gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale); - - // Compute the Gaussian derivatives Lx and Ly - Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT); - Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT); - - // Skip the borders for computing the histogram - for (int i = 1; i < gaussian.rows - 1; i++) { - const float *lx = Lx.ptr(i); - const float *ly = Ly.ptr(i); - for (int j = 1; j < gaussian.cols - 1; j++) { - modg = lx[j]*lx[j] + ly[j]*ly[j]; - - // Get the maximum - if (modg > hmax) { - hmax = modg; - } - } - } - hmax = sqrt(hmax); - // Skip the borders for computing the histogram - for (int i = 1; i < gaussian.rows - 1; i++) { - const float *lx = Lx.ptr(i); - const float *ly = Ly.ptr(i); - for (int j = 1; j < gaussian.cols - 1; j++) { - modg = lx[j]*lx[j] + ly[j]*ly[j]; - - // Find the correspondent bin - if (modg != 0.0) { - nbin = (int)floor(nbins*(sqrt(modg) / hmax)); - - if (nbin == nbins) { - nbin--; - } - - hist[nbin]++; - npoints++; - } - } - } + Size sz = Lx.size(); + float inv_k = 1.0f / (k*k); + for (int y = 0; y < sz.height; y++) { - // Now find the perc of the histogram percentile - nthreshold = (int)(npoints*perc); + const float* Lx_row = Lx.ptr(y); + const float* Ly_row = Ly.ptr(y); + float* dst_row = dst.ptr(y); - for (k = 0; nelements < nthreshold && k < nbins; k++) { - nelements = nelements + hist[k]; - } + for (int x = 0; x < sz.width; x++) { + float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]); + dst_row[x] = -3.315f/(dL*dL*dL*dL); + } + } - if (nelements < nthreshold) { - kperc = 0.03f; - } - else { - kperc = hmax*((float)(k) / (float)nbins); - } + exp(dst, dst); + dst = 1.0 - dst; +} + + +/* ************************************************************************* */ +/** +* @brief This function computes Charbonnier conductivity coefficient gc +* gc = 1 / sqrt(1 + dL^2 / k^2) +* @param Lx First order image derivative in X-direction (horizontal) +* @param Ly First order image derivative in Y-direction (vertical) +* @param dst Output image +* @param k Contrast factor parameter +* @note For more information check the following paper: J. Weickert +* Applications of nonlinear diffusion in image processing and computer vision, +* Proceedings of Algorithmy 2000 +*/ +void charbonnier_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) { + + Size sz = Lx.size(); + float inv_k = 1.0f / (k*k); + for (int y = 0; y < sz.height; y++) { + + const float* Lx_row = Lx.ptr(y); + const float* Ly_row = Ly.ptr(y); + float* dst_row = dst.ptr(y); + + for (int x = 0; x < sz.width; x++) { + float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x])); + dst_row[x] = 1.0f / den; + } + } +} - return kperc; + +/* ************************************************************************* */ +/** + * @brief This function computes a good empirical value for the k contrast factor + * given an input image, the percentile (0-1), the gradient scale and the number of + * bins in the histogram + * @param img Input image + * @param perc Percentile of the image gradient histogram (0-1) + * @param gscale Scale for computing the image gradient histogram + * @param nbins Number of histogram bins + * @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel + * @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel + * @return k contrast factor + */ +float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) { + + int nbin = 0, nelements = 0, nthreshold = 0, k = 0; + float kperc = 0.0, modg = 0.0; + float npoints = 0.0; + float hmax = 0.0; + + // Create the array for the histogram + std::vector hist(nbins, 0); + + // Create the matrices + Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F); + Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F); + Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F); + + // Perform the Gaussian convolution + gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale); + + // Compute the Gaussian derivatives Lx and Ly + Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT); + Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT); + + // Skip the borders for computing the histogram + for (int i = 1; i < gaussian.rows - 1; i++) { + const float *lx = Lx.ptr(i); + const float *ly = Ly.ptr(i); + for (int j = 1; j < gaussian.cols - 1; j++) { + modg = lx[j]*lx[j] + ly[j]*ly[j]; + + // Get the maximum + if (modg > hmax) { + hmax = modg; } + } + } + hmax = sqrt(hmax); + // Skip the borders for computing the histogram + for (int i = 1; i < gaussian.rows - 1; i++) { + const float *lx = Lx.ptr(i); + const float *ly = Ly.ptr(i); + for (int j = 1; j < gaussian.cols - 1; j++) { + modg = lx[j]*lx[j] + ly[j]*ly[j]; + + // Find the correspondent bin + if (modg != 0.0) { + nbin = (int)floor(nbins*(sqrt(modg) / hmax)); + + if (nbin == nbins) { + nbin--; + } - /* ************************************************************************* */ - /** - * @brief This function computes Scharr image derivatives - * @param src Input image - * @param dst Output image - * @param xorder Derivative order in X-direction (horizontal) - * @param yorder Derivative order in Y-direction (vertical) - * @param scale Scale factor for the derivative size - */ - void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) { - Mat kx, ky; - compute_derivative_kernels(kx, ky, xorder, yorder, scale); - sepFilter2D(src, dst, CV_32F, kx, ky); + hist[nbin]++; + npoints++; } + } + } - /* ************************************************************************* */ - /** - * @brief Compute derivative kernels for sizes different than 3 - * @param _kx Horizontal kernel ues - * @param _ky Vertical kernel values - * @param dx Derivative order in X-direction (horizontal) - * @param dy Derivative order in Y-direction (vertical) - * @param scale_ Scale factor or derivative size - */ - void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) { - - int ksize = 3 + 2 * (scale - 1); - - // The standard Scharr kernel - if (scale == 1) { - getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F); - return; - } + // Now find the perc of the histogram percentile + nthreshold = (int)(npoints*perc); - _kx.create(ksize, 1, CV_32F, -1, true); - _ky.create(ksize, 1, CV_32F, -1, true); - Mat kx = _kx.getMat(); - Mat ky = _ky.getMat(); + for (k = 0; nelements < nthreshold && k < nbins; k++) { + nelements = nelements + hist[k]; + } - float w = 10.0f / 3.0f; - float norm = 1.0f / (2.0f*scale*(w + 2.0f)); + if (nelements < nthreshold) { + kperc = 0.03f; + } + else { + kperc = hmax*((float)(k) / (float)nbins); + } - for (int k = 0; k < 2; k++) { - Mat* kernel = k == 0 ? &kx : &ky; - int order = k == 0 ? dx : dy; - std::vector kerI(ksize, 0.0f); + return kperc; +} - if (order == 0) { - kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm; - } - else if (order == 1) { - kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1; - } +/* ************************************************************************* */ +/** + * @brief This function computes Scharr image derivatives + * @param src Input image + * @param dst Output image + * @param xorder Derivative order in X-direction (horizontal) + * @param yorder Derivative order in Y-direction (vertical) + * @param scale Scale factor for the derivative size + */ +void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) { + Mat kx, ky; + compute_derivative_kernels(kx, ky, xorder, yorder, scale); + sepFilter2D(src, dst, CV_32F, kx, ky); +} - Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]); - temp.copyTo(*kernel); - } - } +/* ************************************************************************* */ +/** + * @brief Compute derivative kernels for sizes different than 3 + * @param _kx Horizontal kernel ues + * @param _ky Vertical kernel values + * @param dx Derivative order in X-direction (horizontal) + * @param dy Derivative order in Y-direction (vertical) + * @param scale_ Scale factor or derivative size + */ +void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) { - class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody - { - public: - Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize) - : _Ld(&Ld) - , _c(&c) - , _Lstep(&Lstep) - , stepsize(_stepsize) - { - } + int ksize = 3 + 2 * (scale - 1); - virtual ~Nld_Step_Scalar_Invoker() - { + // The standard Scharr kernel + if (scale == 1) { + getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F); + return; + } - } + _kx.create(ksize, 1, CV_32F, -1, true); + _ky.create(ksize, 1, CV_32F, -1, true); + Mat kx = _kx.getMat(); + Mat ky = _ky.getMat(); - void operator()(const cv::Range& range) const - { - cv::Mat& Ld = *_Ld; - const cv::Mat& c = *_c; - cv::Mat& Lstep = *_Lstep; - - for (int i = range.start; i < range.end; i++) - { - const float *c_prev = c.ptr(i - 1); - const float *c_curr = c.ptr(i); - const float *c_next = c.ptr(i + 1); - const float *ld_prev = Ld.ptr(i - 1); - const float *ld_curr = Ld.ptr(i); - const float *ld_next = Ld.ptr(i + 1); - - float *dst = Lstep.ptr(i); - - for (int j = 1; j < Lstep.cols - 1; j++) - { - float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]); - float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]); - float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]); - float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]); - dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg); - } - } - } - private: - cv::Mat * _Ld; - const cv::Mat * _c; - cv::Mat * _Lstep; - float stepsize; - }; - - /* ************************************************************************* */ - /** - * @brief This function performs a scalar non-linear diffusion step - * @param Ld2 Output image in the evolution - * @param c Conductivity image - * @param Lstep Previous image in the evolution - * @param stepsize The step size in time units - * @note Forward Euler Scheme 3x3 stencil - * The function c is a scalar value that depends on the gradient norm - * dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy - */ - void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) { - - cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16)); - - float xneg, xpos, yneg, ypos; - float* dst = Lstep.ptr(0); - const float* cprv = NULL; - const float* ccur = c.ptr(0); - const float* cnxt = c.ptr(1); - const float* ldprv = NULL; - const float* ldcur = Ld.ptr(0); - const float* ldnxt = Ld.ptr(1); - for (int j = 1; j < Lstep.cols - 1; j++) { - xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]); - xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]); - ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]); - dst[j] = 0.5f*stepsize*(xpos - xneg + ypos); - } + float w = 10.0f / 3.0f; + float norm = 1.0f / (2.0f*scale*(w + 2.0f)); - dst = Lstep.ptr(Lstep.rows - 1); - ccur = c.ptr(Lstep.rows - 1); - cprv = c.ptr(Lstep.rows - 2); - ldcur = Ld.ptr(Lstep.rows - 1); - ldprv = Ld.ptr(Lstep.rows - 2); - - for (int j = 1; j < Lstep.cols - 1; j++) { - xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]); - xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]); - yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]); - dst[j] = 0.5f*stepsize*(xpos - xneg - yneg); - } + for (int k = 0; k < 2; k++) { + Mat* kernel = k == 0 ? &kx : &ky; + int order = k == 0 ? dx : dy; + std::vector kerI(ksize, 0.0f); - ccur = c.ptr(1); - ldcur = Ld.ptr(1); - cprv = c.ptr(0); - ldprv = Ld.ptr(0); - - int r0 = Lstep.cols - 1; - int r1 = Lstep.cols - 2; - - for (int i = 1; i < Lstep.rows - 1; i++) { - cnxt = c.ptr(i + 1); - ldnxt = Ld.ptr(i + 1); - dst = Lstep.ptr(i); - - xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]); - ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]); - yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]); - dst[0] = 0.5f*stepsize*(xpos + ypos - yneg); - - xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]); - ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]); - yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]); - dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg); - - cprv = ccur; - ccur = cnxt; - ldprv = ldcur; - ldcur = ldnxt; - } - Ld += Lstep; - } + if (order == 0) { + kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm; + } + else if (order == 1) { + kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1; + } - /* ************************************************************************* */ - /** - * @brief This function downsamples the input image using OpenCV resize - * @param img Input image to be downsampled - * @param dst Output image with half of the resolution of the input image - */ - void halfsample_image(const cv::Mat& src, cv::Mat& dst) { - - // Make sure the destination image is of the right size - CV_Assert(src.cols / 2 == dst.cols); - CV_Assert(src.rows / 2 == dst.rows); - resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA); + Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]); + temp.copyTo(*kernel); + } +} + +class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody +{ +public: + Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize) + : _Ld(&Ld) + , _c(&c) + , _Lstep(&Lstep) + , stepsize(_stepsize) + { + } + + virtual ~Nld_Step_Scalar_Invoker() + { + + } + + void operator()(const cv::Range& range) const + { + cv::Mat& Ld = *_Ld; + const cv::Mat& c = *_c; + cv::Mat& Lstep = *_Lstep; + + for (int i = range.start; i < range.end; i++) + { + const float *c_prev = c.ptr(i - 1); + const float *c_curr = c.ptr(i); + const float *c_next = c.ptr(i + 1); + const float *ld_prev = Ld.ptr(i - 1); + const float *ld_curr = Ld.ptr(i); + const float *ld_next = Ld.ptr(i + 1); + + float *dst = Lstep.ptr(i); + + for (int j = 1; j < Lstep.cols - 1; j++) + { + float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]); + float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]); + float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]); + float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]); + dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg); } + } + } +private: + cv::Mat * _Ld; + const cv::Mat * _c; + cv::Mat * _Lstep; + float stepsize; +}; + +/* ************************************************************************* */ +/** +* @brief This function performs a scalar non-linear diffusion step +* @param Ld2 Output image in the evolution +* @param c Conductivity image +* @param Lstep Previous image in the evolution +* @param stepsize The step size in time units +* @note Forward Euler Scheme 3x3 stencil +* The function c is a scalar value that depends on the gradient norm +* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy +*/ +void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) { + + cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16)); + + float xneg, xpos, yneg, ypos; + float* dst = Lstep.ptr(0); + const float* cprv = NULL; + const float* ccur = c.ptr(0); + const float* cnxt = c.ptr(1); + const float* ldprv = NULL; + const float* ldcur = Ld.ptr(0); + const float* ldnxt = Ld.ptr(1); + for (int j = 1; j < Lstep.cols - 1; j++) { + xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]); + xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]); + ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]); + dst[j] = 0.5f*stepsize*(xpos - xneg + ypos); + } + + dst = Lstep.ptr(Lstep.rows - 1); + ccur = c.ptr(Lstep.rows - 1); + cprv = c.ptr(Lstep.rows - 2); + ldcur = Ld.ptr(Lstep.rows - 1); + ldprv = Ld.ptr(Lstep.rows - 2); + + for (int j = 1; j < Lstep.cols - 1; j++) { + xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]); + xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]); + yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]); + dst[j] = 0.5f*stepsize*(xpos - xneg - yneg); + } - /* ************************************************************************* */ - /** - * @brief This function checks if a given pixel is a maximum in a local neighbourhood - * @param img Input image where we will perform the maximum search - * @param dsize Half size of the neighbourhood - * @param value Response value at (x,y) position - * @param row Image row coordinate - * @param col Image column coordinate - * @param same_img Flag to indicate if the image value at (x,y) is in the input image - * @return 1->is maximum, 0->otherwise - */ - bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) { - - bool response = true; - - for (int i = row - dsize; i <= row + dsize; i++) { - for (int j = col - dsize; j <= col + dsize; j++) { - if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) { - if (same_img == true) { - if (i != row || j != col) { - if ((*(img.ptr(i)+j)) > value) { - response = false; - return response; - } - } - } - else { - if ((*(img.ptr(i)+j)) > value) { - response = false; - return response; - } - } + ccur = c.ptr(1); + ldcur = Ld.ptr(1); + cprv = c.ptr(0); + ldprv = Ld.ptr(0); + + int r0 = Lstep.cols - 1; + int r1 = Lstep.cols - 2; + + for (int i = 1; i < Lstep.rows - 1; i++) { + cnxt = c.ptr(i + 1); + ldnxt = Ld.ptr(i + 1); + dst = Lstep.ptr(i); + + xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]); + ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]); + yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]); + dst[0] = 0.5f*stepsize*(xpos + ypos - yneg); + + xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]); + ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]); + yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]); + dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg); + + cprv = ccur; + ccur = cnxt; + ldprv = ldcur; + ldcur = ldnxt; + } + Ld += Lstep; +} + +/* ************************************************************************* */ +/** +* @brief This function downsamples the input image using OpenCV resize +* @param img Input image to be downsampled +* @param dst Output image with half of the resolution of the input image +*/ +void halfsample_image(const cv::Mat& src, cv::Mat& dst) { + + // Make sure the destination image is of the right size + CV_Assert(src.cols / 2 == dst.cols); + CV_Assert(src.rows / 2 == dst.rows); + resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA); +} + +/* ************************************************************************* */ +/** + * @brief This function checks if a given pixel is a maximum in a local neighbourhood + * @param img Input image where we will perform the maximum search + * @param dsize Half size of the neighbourhood + * @param value Response value at (x,y) position + * @param row Image row coordinate + * @param col Image column coordinate + * @param same_img Flag to indicate if the image value at (x,y) is in the input image + * @return 1->is maximum, 0->otherwise + */ +bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) { + + bool response = true; + + for (int i = row - dsize; i <= row + dsize; i++) { + for (int j = col - dsize; j <= col + dsize; j++) { + if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) { + if (same_img == true) { + if (i != row || j != col) { + if ((*(img.ptr(i)+j)) > value) { + response = false; + return response; } } } - - return response; + else { + if ((*(img.ptr(i)+j)) > value) { + response = false; + return response; + } + } } } } + + return response; +} + } diff --git a/modules/features2d/src/kaze/nldiffusion_functions.h b/modules/features2d/src/kaze/nldiffusion_functions.h index 6665e54270..9dc9fed198 100644 --- a/modules/features2d/src/kaze/nldiffusion_functions.h +++ b/modules/features2d/src/kaze/nldiffusion_functions.h @@ -11,43 +11,37 @@ #ifndef __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__ #define __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__ -/* ************************************************************************* */ -// Includes -#include "../precomp.hpp" - /* ************************************************************************* */ // Declaration of functions -namespace cv { - namespace details { - namespace kaze { +namespace cv +{ + +// Gaussian 2D convolution +void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma); - // Gaussian 2D convolution - void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma); +// Diffusivity functions +void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); +void pm_g2(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); +void weickert_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); +void charbonnier_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); - // Diffusivity functions - void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); - void pm_g2(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); - void weickert_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); - void charbonnier_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k); +float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y); - float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y); +// Image derivatives +void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale); +void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale); +void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder); - // Image derivatives - void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale); - void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale); - void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder); +// Nonlinear diffusion filtering scalar step +void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize); - // Nonlinear diffusion filtering scalar step - void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize); +// For non-maxima suppresion +bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img); - // For non-maxima suppresion - bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img); +// Image downsampling +void halfsample_image(const cv::Mat& src, cv::Mat& dst); - // Image downsampling - void halfsample_image(const cv::Mat& src, cv::Mat& dst); - } - } } #endif diff --git a/modules/features2d/src/mser.cpp b/modules/features2d/src/mser.cpp index 40c32d0a9e..0deaa26f3c 100644 --- a/modules/features2d/src/mser.cpp +++ b/modules/features2d/src/mser.cpp @@ -46,638 +46,564 @@ namespace cv { -const int TABLE_SIZE = 400; - -static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227, - 0.0383427, 0.0446605, 0.0506115, 0.0562786, - 0.0617174, 0.0669672, 0.0720573, 0.0770099, - 0.081843, 0.0865705, 0.0912043, 0.0957541, - 0.100228, 0.104633, 0.108976, 0.113261, - 0.117493, 0.121676, 0.125814, 0.12991, - 0.133967, 0.137987, 0.141974, 0.145929, - 0.149853, 0.15375, 0.15762, 0.161466, - 0.165287, 0.169087, 0.172866, 0.176625, - 0.180365, 0.184088, 0.187794, 0.191483, - 0.195158, 0.198819, 0.202466, 0.2061, - 0.209722, 0.213332, 0.216932, 0.220521, - 0.2241, 0.22767, 0.231231, 0.234783, - 0.238328, 0.241865, 0.245395, 0.248918, - 0.252435, 0.255947, 0.259452, 0.262952, - 0.266448, 0.269939, 0.273425, 0.276908, - 0.280386, 0.283862, 0.287334, 0.290803, - 0.29427, 0.297734, 0.301197, 0.304657, - 0.308115, 0.311573, 0.315028, 0.318483, - 0.321937, 0.32539, 0.328843, 0.332296, - 0.335749, 0.339201, 0.342654, 0.346108, - 0.349562, 0.353017, 0.356473, 0.35993, - 0.363389, 0.366849, 0.37031, 0.373774, - 0.377239, 0.380706, 0.384176, 0.387648, - 0.391123, 0.3946, 0.39808, 0.401563, - 0.405049, 0.408539, 0.412032, 0.415528, - 0.419028, 0.422531, 0.426039, 0.429551, - 0.433066, 0.436586, 0.440111, 0.44364, - 0.447173, 0.450712, 0.454255, 0.457803, - 0.461356, 0.464915, 0.468479, 0.472049, - 0.475624, 0.479205, 0.482792, 0.486384, - 0.489983, 0.493588, 0.4972, 0.500818, - 0.504442, 0.508073, 0.511711, 0.515356, - 0.519008, 0.522667, 0.526334, 0.530008, - 0.533689, 0.537378, 0.541075, 0.54478, - 0.548492, 0.552213, 0.555942, 0.55968, - 0.563425, 0.56718, 0.570943, 0.574715, - 0.578497, 0.582287, 0.586086, 0.589895, - 0.593713, 0.597541, 0.601379, 0.605227, - 0.609084, 0.612952, 0.61683, 0.620718, - 0.624617, 0.628526, 0.632447, 0.636378, - 0.64032, 0.644274, 0.648239, 0.652215, - 0.656203, 0.660203, 0.664215, 0.668238, - 0.672274, 0.676323, 0.680384, 0.684457, - 0.688543, 0.692643, 0.696755, 0.700881, - 0.70502, 0.709172, 0.713339, 0.717519, - 0.721714, 0.725922, 0.730145, 0.734383, - 0.738636, 0.742903, 0.747185, 0.751483, - 0.755796, 0.760125, 0.76447, 0.768831, - 0.773208, 0.777601, 0.782011, 0.786438, - 0.790882, 0.795343, 0.799821, 0.804318, - 0.808831, 0.813363, 0.817913, 0.822482, - 0.827069, 0.831676, 0.836301, 0.840946, - 0.84561, 0.850295, 0.854999, 0.859724, - 0.864469, 0.869235, 0.874022, 0.878831, - 0.883661, 0.888513, 0.893387, 0.898284, - 0.903204, 0.908146, 0.913112, 0.918101, - 0.923114, 0.928152, 0.933214, 0.938301, - 0.943413, 0.94855, 0.953713, 0.958903, - 0.964119, 0.969361, 0.974631, 0.979929, - 0.985254, 0.990608, 0.99599, 1.0014, - 1.00684, 1.01231, 1.01781, 1.02335, - 1.02891, 1.0345, 1.04013, 1.04579, - 1.05148, 1.05721, 1.06296, 1.06876, - 1.07459, 1.08045, 1.08635, 1.09228, - 1.09826, 1.10427, 1.11032, 1.1164, - 1.12253, 1.1287, 1.1349, 1.14115, - 1.14744, 1.15377, 1.16015, 1.16656, - 1.17303, 1.17954, 1.18609, 1.19269, - 1.19934, 1.20603, 1.21278, 1.21958, - 1.22642, 1.23332, 1.24027, 1.24727, - 1.25433, 1.26144, 1.26861, 1.27584, - 1.28312, 1.29047, 1.29787, 1.30534, - 1.31287, 1.32046, 1.32812, 1.33585, - 1.34364, 1.3515, 1.35943, 1.36744, - 1.37551, 1.38367, 1.39189, 1.4002, - 1.40859, 1.41705, 1.42561, 1.43424, - 1.44296, 1.45177, 1.46068, 1.46967, - 1.47876, 1.48795, 1.49723, 1.50662, - 1.51611, 1.52571, 1.53541, 1.54523, - 1.55517, 1.56522, 1.57539, 1.58568, - 1.59611, 1.60666, 1.61735, 1.62817, - 1.63914, 1.65025, 1.66152, 1.67293, - 1.68451, 1.69625, 1.70815, 1.72023, - 1.73249, 1.74494, 1.75757, 1.77041, - 1.78344, 1.79669, 1.81016, 1.82385, - 1.83777, 1.85194, 1.86635, 1.88103, - 1.89598, 1.91121, 1.92674, 1.94257, - 1.95871, 1.97519, 1.99201, 2.0092, - 2.02676, 2.04471, 2.06309, 2.08189, - 2.10115, 2.12089, 2.14114, 2.16192, - 2.18326, 2.2052, 2.22777, 2.25101, - 2.27496, 2.29966, 2.32518, 2.35156, - 2.37886, 2.40717, 2.43655, 2.46709, - 2.49889, 2.53206, 2.56673, 2.60305, - 2.64117, 2.6813, 2.72367, 2.76854, - 2.81623, 2.86714, 2.92173, 2.98059, - 3.04446, 3.1143, 3.19135, 3.27731, - 3.37455, 3.48653, 3.61862, 3.77982, - 3.98692, 4.2776, 4.77167, 133.333 }; - -typedef struct LinkedPoint -{ - struct LinkedPoint* prev; - struct LinkedPoint* next; - Point pt; -} -LinkedPoint; +using std::vector; -// the history of region grown -typedef struct MSERGrowHistory +class MSER_Impl : public MSER { - struct MSERGrowHistory* shortcut; - struct MSERGrowHistory* child; - int stable; // when it ever stabled before, record the size - int val; - int size; -} -MSERGrowHistory; +public: + struct Params + { + explicit Params( int _delta=5, double _maxVariation=0.25, + int _minArea=60, int _maxArea=14400, + double _minDiversity=.2, int _maxEvolution=200, + double _areaThreshold=1.01, + double _minMargin=0.003, int _edgeBlurSize=5 ) + { + delta = _delta; + minArea = _minArea; + maxArea = _maxArea; + maxVariation = _maxVariation; + minDiversity = _minDiversity; + pass2Only = false; + maxEvolution = _maxEvolution; + areaThreshold = _areaThreshold; + minMargin = _minMargin; + edgeBlurSize = _edgeBlurSize; + } + int delta; + int minArea; + int maxArea; + double maxVariation; + double minDiversity; + bool pass2Only; -typedef struct MSERConnectedComp -{ - LinkedPoint* head; - LinkedPoint* tail; - MSERGrowHistory* history; - unsigned long grey_level; - int size; - int dvar; // the derivative of last var - float var; // the current variation (most time is the variation of one-step back) -} -MSERConnectedComp; + int maxEvolution; + double areaThreshold; + double minMargin; + int edgeBlurSize; + }; -// Linear Time MSER claims by using bsf can get performance gain, here is the implementation -// however it seems that will not do any good in real world test -inline void _bitset(unsigned long * a, unsigned long b) -{ - *a |= 1<size = 0; - comp->var = 0; - comp->dvar = 1; - comp->history = NULL; -} + void set(int propId, double value) + { + if( propId == DELTA ) + params.delta = cvRound(value); + else if( propId == MIN_AREA ) + params.minArea = cvRound(value); + else if( propId == MAX_AREA ) + params.maxArea = cvRound(value); + else if( propId == PASS2_ONLY ) + params.pass2Only = value != 0; + else + CV_Error(CV_StsBadArg, "Unknown parameter id"); + } -// add history of size to a connected component -static void -MSERNewHistory( MSERConnectedComp* comp, MSERGrowHistory* history ) -{ - history->child = history; - if ( NULL == comp->history ) + double get(int propId) const { - history->shortcut = history; - history->stable = 0; - } else { - comp->history->child = history; - history->shortcut = comp->history->shortcut; - history->stable = comp->history->stable; + double value = 0; + if( propId == DELTA ) + value = params.delta; + else if( propId == MIN_AREA ) + value = params.minArea; + else if( propId == MAX_AREA ) + value = params.maxArea; + else if( propId == PASS2_ONLY ) + value = params.pass2Only; + else + CV_Error(CV_StsBadArg, "Unknown parameter id"); + return value; } - history->val = comp->grey_level; - history->size = comp->size; - comp->history = history; -} -// merging two connected component -static void -MSERMergeComp( MSERConnectedComp* comp1, - MSERConnectedComp* comp2, - MSERConnectedComp* comp, - MSERGrowHistory* history ) -{ - LinkedPoint* head; - LinkedPoint* tail; - comp->grey_level = comp2->grey_level; - history->child = history; - // select the winner by size - if ( comp1->size >= comp2->size ) + enum { DIR_SHIFT = 29, NEXT_MASK = ((1<history ) + Pixel() : val(0) {} + Pixel(int _val) : val(_val) {} + + int getGray(const Pixel* ptr0, const uchar* imgptr0, int mask) const { - history->shortcut = history; - history->stable = 0; - } else { - comp1->history->child = history; - history->shortcut = comp1->history->shortcut; - history->stable = comp1->history->stable; + return imgptr0[this - ptr0] ^ mask; } - if ( NULL != comp2->history && comp2->history->stable > history->stable ) - history->stable = comp2->history->stable; - history->val = comp1->grey_level; - history->size = comp1->size; - // put comp1 to history - comp->var = comp1->var; - comp->dvar = comp1->dvar; - if ( comp1->size > 0 && comp2->size > 0 ) + int getNext() const { return (val & NEXT_MASK); } + void setNext(int next) { val = (val & ~NEXT_MASK) | next; } + + int getDir() const { return (int)((unsigned)val >> DIR_SHIFT); } + void setDir(int dir) { val = (val & NEXT_MASK) | (dir << DIR_SHIFT); } + bool isVisited() const { return (val & ~NEXT_MASK) != 0; } + + int val; + }; + typedef int PPixel; + + // the history of region grown + struct CompHistory + { + CompHistory() { shortcut = child = 0; stable = val = size = 0; } + CompHistory* shortcut; + CompHistory* child; + int stable; // when it ever stabled before, record the size + int val; + int size; + }; + + struct ConnectedComp + { + ConnectedComp() { - comp1->tail->next = comp2->head; - comp2->head->prev = comp1->tail; + init(0); } - head = ( comp1->size > 0 ) ? comp1->head : comp2->head; - tail = ( comp2->size > 0 ) ? comp2->tail : comp1->tail; - // always made the newly added in the last of the pixel list (comp1 ... comp2) - } else { - if ( NULL == comp2->history ) + + void init(int gray) { - history->shortcut = history; - history->stable = 0; - } else { - comp2->history->child = history; - history->shortcut = comp2->history->shortcut; - history->stable = comp2->history->stable; + head = tail = 0; + history = 0; + size = 0; + grey_level = gray; + dvar = false; + var = 0; } - if ( NULL != comp1->history && comp1->history->stable > history->stable ) - history->stable = comp1->history->stable; - history->val = comp2->grey_level; - history->size = comp2->size; - // put comp2 to history - comp->var = comp2->var; - comp->dvar = comp2->dvar; - if ( comp1->size > 0 && comp2->size > 0 ) + + // add history chunk to a connected component + void growHistory( CompHistory* h ) { - comp2->tail->next = comp1->head; - comp1->head->prev = comp2->tail; + h->child = h; + if( !history ) + { + h->shortcut = h; + h->stable = 0; + } + else + { + history->child = h; + h->shortcut = history->shortcut; + h->stable = history->stable; + } + h->val = grey_level; + h->size = size; + history = h; } - head = ( comp2->size > 0 ) ? comp2->head : comp1->head; - tail = ( comp1->size > 0 ) ? comp1->tail : comp2->tail; - // always made the newly added in the last of the pixel list (comp2 ... comp1) - } - comp->head = head; - comp->tail = tail; - comp->history = history; - comp->size = comp1->size + comp2->size; -} -static float -MSERVariationCalc( MSERConnectedComp* comp, int delta ) -{ - MSERGrowHistory* history = comp->history; - int val = comp->grey_level; - if ( NULL != history ) - { - MSERGrowHistory* shortcut = history->shortcut; - while ( shortcut != shortcut->shortcut && shortcut->val + delta > val ) - shortcut = shortcut->shortcut; - MSERGrowHistory* child = shortcut->child; - while ( child != child->child && child->val + delta <= val ) + // merging two connected components + static void + merge( const ConnectedComp* comp1, + const ConnectedComp* comp2, + ConnectedComp* comp, + CompHistory* h, + Pixel* pix0 ) { - shortcut = child; - child = child->child; + comp->grey_level = comp2->grey_level; + h->child = h; + // select the winner by size + if ( comp1->size < comp2->size ) + std::swap(comp1, comp2); + + if( !comp1->history ) + { + h->shortcut = h; + h->stable = 0; + } + else + { + comp1->history->child = h; + h->shortcut = comp1->history->shortcut; + h->stable = comp1->history->stable; + } + if( comp2->history && comp2->history->stable > h->stable ) + h->stable = comp2->history->stable; + h->val = comp1->grey_level; + h->size = comp1->size; + // put comp1 to history + comp->var = comp1->var; + comp->dvar = comp1->dvar; + if( comp1->size > 0 && comp2->size > 0 ) + pix0[comp1->tail].setNext(comp2->head); + PPixel head = comp1->size > 0 ? comp1->head : comp2->head; + PPixel tail = comp2->size > 0 ? comp2->tail : comp1->tail; + // always made the newly added in the last of the pixel list (comp1 ... comp2) + comp->head = head; + comp->tail = tail; + comp->history = h; + comp->size = comp1->size + comp2->size; } - // get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val - history->shortcut = shortcut; - return (float)(comp->size-shortcut->size)/(float)shortcut->size; - // here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}|| - // in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}|| - // my calculation is simpler and much easier to implement - } - return 1.; -} -static bool MSERStableCheck( MSERConnectedComp* comp, MSERParams params ) -{ - // tricky part: it actually check the stablity of one-step back - if ( comp->history == NULL || comp->history->size <= params.minArea || comp->history->size >= params.maxArea ) - return 0; - float div = (float)(comp->history->size-comp->history->stable)/(float)comp->history->size; - float var = MSERVariationCalc( comp, params.delta ); - int dvar = ( comp->var < var || (unsigned long)(comp->history->val + 1) < comp->grey_level ); - int stable = ( dvar && !comp->dvar && comp->var < params.maxVariation && div > params.minDiversity ); - comp->var = var; - comp->dvar = dvar; - if ( stable ) - comp->history->stable = comp->history->size; - return stable != 0; -} + float calcVariation( int delta ) const + { + if( !history ) + return 1.f; + int val = grey_level; + CompHistory* shortcut = history->shortcut; + while( shortcut != shortcut->shortcut && shortcut->val + delta > val ) + shortcut = shortcut->shortcut; + CompHistory* child = shortcut->child; + while( child != child->child && child->val + delta <= val ) + { + shortcut = child; + child = child->child; + } + // get the position of history where the shortcut->val <= delta+val and shortcut->child->val >= delta+val + history->shortcut = shortcut; + return (float)(size - shortcut->size)/(float)shortcut->size; + // here is a small modification of MSER where cal ||R_{i}-R_{i-delta}||/||R_{i-delta}|| + // in standard MSER, cal ||R_{i+delta}-R_{i-delta}||/||R_{i}|| + // my calculation is simpler and much easier to implement + } -// add a pixel to the pixel list -static void accumulateMSERComp( MSERConnectedComp* comp, LinkedPoint* point ) -{ - if ( comp->size > 0 ) - { - point->prev = comp->tail; - comp->tail->next = point; - point->next = NULL; - } else { - point->prev = NULL; - point->next = NULL; - comp->head = point; - } - comp->tail = point; - comp->size++; -} + bool isStable(const Params& p) + { + // tricky part: it actually check the stablity of one-step back + if( !history || history->size <= p.minArea || history->size >= p.maxArea ) + return false; + float div = (float)(history->size - history->stable)/(float)history->size; + float _var = calcVariation( p.delta ); + bool _dvar = (var < _var) || (history->val + 1 < grey_level); + bool stable = _dvar && !dvar && _var < p.maxVariation && div > p.minDiversity; + var = _var; + dvar = _dvar; + if( stable ) + history->stable = history->size; + return stable; + } -// convert the point set to CvSeq -static CvContour* MSERToContour( MSERConnectedComp* comp, CvMemStorage* storage ) -{ - CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage ); - CvContour* contour = (CvContour*)_contour; - cvSeqPushMulti( _contour, 0, comp->history->size ); - LinkedPoint* lpt = comp->head; - for ( int i = 0; i < comp->history->size; i++ ) - { - CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i ); - pt->x = lpt->pt.x; - pt->y = lpt->pt.y; - lpt = lpt->next; - } - cvBoundingRect( contour ); - return contour; -} + // convert the point set to CvSeq + Rect label( Mat& labels, int lval, const Pixel* pix0, int step ) const + { + int* lptr = labels.ptr(); + int lstep = labels.step/sizeof(lptr[0]); + int xmin = INT_MAX, ymin = INT_MAX, xmax = INT_MIN, ymax = INT_MIN; -// to preprocess src image to following format -// 32-bit image -// > 0 is available, < 0 is visited -// 17~19 bits is the direction -// 8~11 bits is the bucket it falls to (for BitScanForward) -// 0~8 bits is the color -static int* preprocessMSER_8UC1( CvMat* img, - int*** heap_cur, - CvMat* src, - CvMat* mask ) -{ - int srccpt = src->step-src->cols; - int cpt_1 = img->cols-src->cols-1; - int* imgptr = img->data.i; - int* startptr; + for( PPixel pix = head; pix != 0; pix = pix0[pix].getNext() ) + { + int y = pix/step; + int x = pix - y*step; - int level_size[256]; - for ( int i = 0; i < 256; i++ ) - level_size[i] = 0; + xmin = std::min(xmin, x); + xmax = std::max(xmax, x); + ymin = std::min(ymin, y); + ymax = std::max(ymax, y); - for ( int i = 0; i < src->cols+2; i++ ) - { - *imgptr = -1; - imgptr++; - } - imgptr += cpt_1-1; - uchar* srcptr = src->data.ptr; - if ( mask ) + lptr[lstep*y + x] = lval; + } + + return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1); + } + + PPixel head; + PPixel tail; + CompHistory* history; + int grey_level; + int size; + float var; // the current variation (most time is the variation of one-step back) + bool dvar; // the derivative of last var + }; + + int detectAndLabel( InputArray _src, OutputArray _labels, OutputArray _bboxes ); + void detect( InputArray _src, vector& keypoints, InputArray _mask ); + + void preprocess1( const Mat& img, int* level_size ) { - startptr = 0; - uchar* maskptr = mask->data.ptr; - for ( int i = 0; i < src->rows; i++ ) + memset(level_size, 0, 256*sizeof(level_size[0])); + + int i, j, cols = img.cols, rows = img.rows; + int step = cols; + pixbuf.resize(step*rows); + heapbuf.resize(cols*rows + 256); + histbuf.resize(cols*rows); + Pixel borderpix; + borderpix.setDir(4); + + for( j = 0; j < step; j++ ) { - *imgptr = -1; - imgptr++; - for ( int j = 0; j < src->cols; j++ ) - { - if ( *maskptr ) - { - if ( !startptr ) - startptr = imgptr; - *srcptr = 0xff-*srcptr; - level_size[*srcptr]++; - *imgptr = ((*srcptr>>5)<<8)|(*srcptr); - } else { - *imgptr = -1; - } - imgptr++; - srcptr++; - maskptr++; - } - *imgptr = -1; - imgptr += cpt_1; - srcptr += srccpt; - maskptr += srccpt; + pixbuf[j] = pixbuf[j + (rows-1)*step] = borderpix; } - } else { - startptr = imgptr+img->cols+1; - for ( int i = 0; i < src->rows; i++ ) + + for( i = 1; i < rows-1; i++ ) { - *imgptr = -1; - imgptr++; - for ( int j = 0; j < src->cols; j++ ) + const uchar* imgptr = img.ptr(i); + Pixel* pptr = &pixbuf[i*step]; + pptr[0] = pptr[cols-1] = borderpix; + for( j = 1; j < cols-1; j++ ) { - *srcptr = 0xff-*srcptr; - level_size[*srcptr]++; - *imgptr = ((*srcptr>>5)<<8)|(*srcptr); - imgptr++; - srcptr++; + int val = imgptr[j]; + level_size[val]++; + pptr[j].val = 0; } - *imgptr = -1; - imgptr += cpt_1; - srcptr += srccpt; } } - for ( int i = 0; i < src->cols+2; i++ ) - { - *imgptr = -1; - imgptr++; - } - heap_cur[0][0] = 0; - for ( int i = 1; i < 256; i++ ) + void preprocess2( const Mat& img, int* level_size ) { - heap_cur[i] = heap_cur[i-1]+level_size[i-1]+1; - heap_cur[i][0] = 0; - } - return startptr; -} + int i; -static void extractMSER_8UC1_Pass( int* ioptr, - int* imgptr, - int*** heap_cur, - LinkedPoint* ptsptr, - MSERGrowHistory* histptr, - MSERConnectedComp* comptr, - int step, - int stepmask, - int stepgap, - MSERParams params, - int color, - CvSeq* contours, - CvMemStorage* storage ) -{ - comptr->grey_level = 256; - comptr++; - comptr->grey_level = (*imgptr)&0xff; - initMSERComp( comptr ); - *imgptr |= 0x80000000; - heap_cur += (*imgptr)&0xff; - int dir[] = { 1, step, -1, -step }; -#ifdef __INTRIN_ENABLED__ - unsigned long heapbit[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - unsigned long* bit_cur = heapbit+(((*imgptr)&0x700)>>8); -#endif - for ( ; ; ) - { - // take tour of all the 4 directions - while ( ((*imgptr)&0x70000) < 0x40000 ) + for( i = 0; i < 128; i++ ) + std::swap(level_size[i], level_size[255-i]); + + if( !params.pass2Only ) { - // get the neighbor - int* imgptr_nbr = imgptr+dir[((*imgptr)&0x70000)>>16]; - if ( *imgptr_nbr >= 0 ) // if the neighbor is not visited yet + int j, cols = img.cols, rows = img.rows; + int step = cols; + for( i = 1; i < rows-1; i++ ) { - *imgptr_nbr |= 0x80000000; // mark it as visited - if ( ((*imgptr_nbr)&0xff) < ((*imgptr)&0xff) ) + Pixel* pptr = &pixbuf[i*step + 1]; + for( j = 1; j < cols-1; j++ ) { - // when the value of neighbor smaller than current - // push current to boundary heap and make the neighbor to be the current one - // create an empty comp - (*heap_cur)++; - **heap_cur = imgptr; - *imgptr += 0x10000; - heap_cur += ((*imgptr_nbr)&0xff)-((*imgptr)&0xff); -#ifdef __INTRIN_ENABLED__ - _bitset( bit_cur, (*imgptr)&0x1f ); - bit_cur += (((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8; -#endif - imgptr = imgptr_nbr; - comptr++; - initMSERComp( comptr ); - comptr->grey_level = (*imgptr)&0xff; - continue; - } else { - // otherwise, push the neighbor to boundary heap - heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)]++; - *heap_cur[((*imgptr_nbr)&0xff)-((*imgptr)&0xff)] = imgptr_nbr; -#ifdef __INTRIN_ENABLED__ - _bitset( bit_cur+((((*imgptr_nbr)&0x700)-((*imgptr)&0x700))>>8), (*imgptr_nbr)&0x1f ); -#endif + pptr[j].val = 0; } } - *imgptr += 0x10000; } - int imsk = (int)(imgptr-ioptr); - ptsptr->pt = cvPoint( imsk&stepmask, imsk>>stepgap ); - // get the current location - accumulateMSERComp( comptr, ptsptr ); - ptsptr++; - // get the next pixel from boundary heap - if ( **heap_cur ) + } + + void pass( const Mat& img, Mat& labels, int& lval, vector& bboxvec, + Size size, const int* level_size, int mask ) + { + CompHistory* histptr = &histbuf[0]; + int step = size.width; + Pixel *ptr0 = &pixbuf[0], *ptr = &ptr0[step+1]; + const uchar* imgptr0 = img.ptr(); + Pixel** heap[256]; + ConnectedComp comp[257]; + ConnectedComp* comptr = &comp[0]; + + heap[0] = &heapbuf[0]; + heap[0][0] = 0; + + for( int i = 1; i < 256; i++ ) + { + heap[i] = heap[i-1] + level_size[i-1] + 1; + heap[i][0] = 0; + } + + comptr->grey_level = 256; + comptr++; + comptr->grey_level = ptr->getGray(ptr0, imgptr0, mask); + ptr->setDir(1); + int dir[] = { 0, 1, step, -1, -step }; + for( ;; ) { - imgptr = **heap_cur; - (*heap_cur)--; -#ifdef __INTRIN_ENABLED__ - if ( !**heap_cur ) - _bitreset( bit_cur, (*imgptr)&0x1f ); -#endif - } else { -#ifdef __INTRIN_ENABLED__ - bool found_pixel = 0; - unsigned long pixel_val; - for ( int i = ((*imgptr)&0x700)>>8; i < 8; i++ ) + int curr_gray = ptr->getGray(ptr0, imgptr0, mask); + int nbr_idx = ptr->getDir(); + // take tour of all the 4 directions + for( ; nbr_idx <= 4; nbr_idx++ ) { - if ( _BitScanForward( &pixel_val, *bit_cur ) ) + // get the neighbor + Pixel* ptr_nbr = ptr + dir[nbr_idx]; + if( !ptr_nbr->isVisited() ) { - found_pixel = 1; - pixel_val += i<<5; - heap_cur += pixel_val-((*imgptr)&0xff); - break; + // set dir=1, next=0 + ptr_nbr->val = 1 << DIR_SHIFT; + int nbr_gray = ptr_nbr->getGray(ptr0, imgptr0, mask); + if( nbr_gray < curr_gray ) + { + // when the value of neighbor smaller than current + // push current to boundary heap and make the neighbor to be the current one + // create an empty comp + *(++heap[curr_gray]) = ptr; + ptr->val = (nbr_idx+1) << DIR_SHIFT; + ptr = ptr_nbr; + comptr++; + comptr->init(nbr_gray); + curr_gray = nbr_gray; + nbr_idx = 0; + continue; + } + // otherwise, push the neighbor to boundary heap + *(++heap[nbr_gray]) = ptr_nbr; } - bit_cur++; } - if ( found_pixel ) -#else - heap_cur++; - unsigned long pixel_val = 0; - for ( unsigned long i = ((*imgptr)&0xff)+1; i < 256; i++ ) + + // set dir = nbr_idx, next = 0 + ptr->val = nbr_idx << DIR_SHIFT; + int ptrofs = (int)(ptr - ptr0); + CV_Assert(ptrofs != 0); + + // add a pixel to the pixel list + if( comptr->tail ) + ptr0[comptr->tail].setNext(ptrofs); + else + comptr->head = ptrofs; + comptr->tail = ptrofs; + comptr->size++; + // get the next pixel from boundary heap + if( *heap[curr_gray] ) { - if ( **heap_cur ) - { - pixel_val = i; - break; - } - heap_cur++; + ptr = *heap[curr_gray]; + heap[curr_gray]--; } - if ( pixel_val ) -#endif + else { - imgptr = **heap_cur; - (*heap_cur)--; -#ifdef __INTRIN_ENABLED__ - if ( !**heap_cur ) - _bitreset( bit_cur, pixel_val&0x1f ); -#endif - if ( pixel_val < comptr[-1].grey_level ) + for( curr_gray++; curr_gray < 256; curr_gray++ ) + { + if( *heap[curr_gray] ) + break; + } + if( curr_gray >= 256 ) + break; + + ptr = *heap[curr_gray]; + heap[curr_gray]--; + if( curr_gray < comptr[-1].grey_level ) { // check the stablity and push a new history, increase the grey level - if ( MSERStableCheck( comptr, params ) ) + if( comptr->isStable(params) ) { - CvContour* contour = MSERToContour( comptr, storage ); - contour->color = color; - cvSeqPush( contours, &contour ); + Rect box = comptr->label( labels, lval++, ptr0, step ); + bboxvec.push_back(box); } - MSERNewHistory( comptr, histptr ); - comptr[0].grey_level = pixel_val; - histptr++; - } else { + comptr->growHistory( histptr++ ); + comptr[0].grey_level = curr_gray; + } + else + { // keep merging top two comp in stack until the grey level >= pixel_val - for ( ; ; ) + for(;;) { comptr--; - MSERMergeComp( comptr+1, comptr, comptr, histptr ); - histptr++; - if ( pixel_val <= comptr[0].grey_level ) + ConnectedComp::merge(comptr+1, comptr, comptr, histptr++, ptr0); + if( curr_gray <= comptr[0].grey_level ) break; - if ( pixel_val < comptr[-1].grey_level ) + if( curr_gray < comptr[-1].grey_level ) { // check the stablity here otherwise it wouldn't be an ER - if ( MSERStableCheck( comptr, params ) ) + if( comptr->isStable(params) ) { - CvContour* contour = MSERToContour( comptr, storage ); - contour->color = color; - cvSeqPush( contours, &contour ); + Rect box = comptr->label( labels, lval++, ptr0, step ); + bboxvec.push_back(box); } - MSERNewHistory( comptr, histptr ); - comptr[0].grey_level = pixel_val; - histptr++; + comptr->growHistory( histptr++ ); + comptr[0].grey_level = curr_gray; break; } } } - } else - break; + } } } -} -static void extractMSER_8UC1( CvMat* src, - CvMat* mask, - CvSeq* contours, - CvMemStorage* storage, - MSERParams params ) + Mat tempsrc; + vector pixbuf; + vector heapbuf; + vector histbuf; + + Params params; +}; + +/* + +TODO: +the color MSER has not been completely refactored yet. We leave it mostly as-is, +with just enough changes to convert C structures to C++ ones and +add support for color images into MSER_Impl::detectAndLabel. +*/ + +const int TABLE_SIZE = 400; + +static const float chitab3[]= { - int step = 8; - int stepgap = 3; - while ( step < src->step+2 ) - { - step <<= 1; - stepgap++; - } - int stepmask = step-1; - - // to speedup the process, make the width to be 2^N - CvMat* img = cvCreateMat( src->rows+2, step, CV_32SC1 ); - int* ioptr = img->data.i+step+1; - int* imgptr; - - // pre-allocate boundary heap - int** heap = (int**)cvAlloc( (src->rows*src->cols+256)*sizeof(heap[0]) ); - int** heap_start[256]; - heap_start[0] = heap; - - // pre-allocate linked point and grow history - LinkedPoint* pts = (LinkedPoint*)cvAlloc( src->rows*src->cols*sizeof(pts[0]) ); - MSERGrowHistory* history = (MSERGrowHistory*)cvAlloc( src->rows*src->cols*sizeof(history[0]) ); - MSERConnectedComp comp[257]; - - // darker to brighter (MSER-) - imgptr = preprocessMSER_8UC1( img, heap_start, src, mask ); - extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, -1, contours, storage ); - // brighter to darker (MSER+) - imgptr = preprocessMSER_8UC1( img, heap_start, src, mask ); - extractMSER_8UC1_Pass( ioptr, imgptr, heap_start, pts, history, comp, step, stepmask, stepgap, params, 1, contours, storage ); - - // clean up - cvFree( &history ); - cvFree( &heap ); - cvFree( &pts ); - cvReleaseMat( &img ); -} + 0.f, 0.0150057f, 0.0239478f, 0.0315227f, + 0.0383427f, 0.0446605f, 0.0506115f, 0.0562786f, + 0.0617174f, 0.0669672f, 0.0720573f, 0.0770099f, + 0.081843f, 0.0865705f, 0.0912043f, 0.0957541f, + 0.100228f, 0.104633f, 0.108976f, 0.113261f, + 0.117493f, 0.121676f, 0.125814f, 0.12991f, + 0.133967f, 0.137987f, 0.141974f, 0.145929f, + 0.149853f, 0.15375f, 0.15762f, 0.161466f, + 0.165287f, 0.169087f, 0.172866f, 0.176625f, + 0.180365f, 0.184088f, 0.187794f, 0.191483f, + 0.195158f, 0.198819f, 0.202466f, 0.2061f, + 0.209722f, 0.213332f, 0.216932f, 0.220521f, + 0.2241f, 0.22767f, 0.231231f, 0.234783f, + 0.238328f, 0.241865f, 0.245395f, 0.248918f, + 0.252435f, 0.255947f, 0.259452f, 0.262952f, + 0.266448f, 0.269939f, 0.273425f, 0.276908f, + 0.280386f, 0.283862f, 0.287334f, 0.290803f, + 0.29427f, 0.297734f, 0.301197f, 0.304657f, + 0.308115f, 0.311573f, 0.315028f, 0.318483f, + 0.321937f, 0.32539f, 0.328843f, 0.332296f, + 0.335749f, 0.339201f, 0.342654f, 0.346108f, + 0.349562f, 0.353017f, 0.356473f, 0.35993f, + 0.363389f, 0.366849f, 0.37031f, 0.373774f, + 0.377239f, 0.380706f, 0.384176f, 0.387648f, + 0.391123f, 0.3946f, 0.39808f, 0.401563f, + 0.405049f, 0.408539f, 0.412032f, 0.415528f, + 0.419028f, 0.422531f, 0.426039f, 0.429551f, + 0.433066f, 0.436586f, 0.440111f, 0.44364f, + 0.447173f, 0.450712f, 0.454255f, 0.457803f, + 0.461356f, 0.464915f, 0.468479f, 0.472049f, + 0.475624f, 0.479205f, 0.482792f, 0.486384f, + 0.489983f, 0.493588f, 0.4972f, 0.500818f, + 0.504442f, 0.508073f, 0.511711f, 0.515356f, + 0.519008f, 0.522667f, 0.526334f, 0.530008f, + 0.533689f, 0.537378f, 0.541075f, 0.54478f, + 0.548492f, 0.552213f, 0.555942f, 0.55968f, + 0.563425f, 0.56718f, 0.570943f, 0.574715f, + 0.578497f, 0.582287f, 0.586086f, 0.589895f, + 0.593713f, 0.597541f, 0.601379f, 0.605227f, + 0.609084f, 0.612952f, 0.61683f, 0.620718f, + 0.624617f, 0.628526f, 0.632447f, 0.636378f, + 0.64032f, 0.644274f, 0.648239f, 0.652215f, + 0.656203f, 0.660203f, 0.664215f, 0.668238f, + 0.672274f, 0.676323f, 0.680384f, 0.684457f, + 0.688543f, 0.692643f, 0.696755f, 0.700881f, + 0.70502f, 0.709172f, 0.713339f, 0.717519f, + 0.721714f, 0.725922f, 0.730145f, 0.734383f, + 0.738636f, 0.742903f, 0.747185f, 0.751483f, + 0.755796f, 0.760125f, 0.76447f, 0.768831f, + 0.773208f, 0.777601f, 0.782011f, 0.786438f, + 0.790882f, 0.795343f, 0.799821f, 0.804318f, + 0.808831f, 0.813363f, 0.817913f, 0.822482f, + 0.827069f, 0.831676f, 0.836301f, 0.840946f, + 0.84561f, 0.850295f, 0.854999f, 0.859724f, + 0.864469f, 0.869235f, 0.874022f, 0.878831f, + 0.883661f, 0.888513f, 0.893387f, 0.898284f, + 0.903204f, 0.908146f, 0.913112f, 0.918101f, + 0.923114f, 0.928152f, 0.933214f, 0.938301f, + 0.943413f, 0.94855f, 0.953713f, 0.958903f, + 0.964119f, 0.969361f, 0.974631f, 0.979929f, + 0.985254f, 0.990608f, 0.99599f, 1.0014f, + 1.00684f, 1.01231f, 1.01781f, 1.02335f, + 1.02891f, 1.0345f, 1.04013f, 1.04579f, + 1.05148f, 1.05721f, 1.06296f, 1.06876f, + 1.07459f, 1.08045f, 1.08635f, 1.09228f, + 1.09826f, 1.10427f, 1.11032f, 1.1164f, + 1.12253f, 1.1287f, 1.1349f, 1.14115f, + 1.14744f, 1.15377f, 1.16015f, 1.16656f, + 1.17303f, 1.17954f, 1.18609f, 1.19269f, + 1.19934f, 1.20603f, 1.21278f, 1.21958f, + 1.22642f, 1.23332f, 1.24027f, 1.24727f, + 1.25433f, 1.26144f, 1.26861f, 1.27584f, + 1.28312f, 1.29047f, 1.29787f, 1.30534f, + 1.31287f, 1.32046f, 1.32812f, 1.33585f, + 1.34364f, 1.3515f, 1.35943f, 1.36744f, + 1.37551f, 1.38367f, 1.39189f, 1.4002f, + 1.40859f, 1.41705f, 1.42561f, 1.43424f, + 1.44296f, 1.45177f, 1.46068f, 1.46967f, + 1.47876f, 1.48795f, 1.49723f, 1.50662f, + 1.51611f, 1.52571f, 1.53541f, 1.54523f, + 1.55517f, 1.56522f, 1.57539f, 1.58568f, + 1.59611f, 1.60666f, 1.61735f, 1.62817f, + 1.63914f, 1.65025f, 1.66152f, 1.67293f, + 1.68451f, 1.69625f, 1.70815f, 1.72023f, + 1.73249f, 1.74494f, 1.75757f, 1.77041f, + 1.78344f, 1.79669f, 1.81016f, 1.82385f, + 1.83777f, 1.85194f, 1.86635f, 1.88103f, + 1.89598f, 1.91121f, 1.92674f, 1.94257f, + 1.95871f, 1.97519f, 1.99201f, 2.0092f, + 2.02676f, 2.04471f, 2.06309f, 2.08189f, + 2.10115f, 2.12089f, 2.14114f, 2.16192f, + 2.18326f, 2.2052f, 2.22777f, 2.25101f, + 2.27496f, 2.29966f, 2.32518f, 2.35156f, + 2.37886f, 2.40717f, 2.43655f, 2.46709f, + 2.49889f, 2.53206f, 2.56673f, 2.60305f, + 2.64117f, 2.6813f, 2.72367f, 2.76854f, + 2.81623f, 2.86714f, 2.92173f, 2.98059f, + 3.04446f, 3.1143f, 3.19135f, 3.27731f, + 3.37455f, 3.48653f, 3.61862f, 3.77982f, + 3.98692f, 4.2776f, 4.77167f, 133.333f +}; + struct MSCRNode; @@ -691,6 +617,56 @@ struct TempMSCR struct MSCRNode { + // the stable mscr should be: + // bigger than minArea and smaller than maxArea + // differ from its ancestor more than minDiversity + bool isStable( const MSER_Impl::Params& params ) const + { + if( size <= params.minArea || size >= params.maxArea ) + return 0; + if( gmsr == NULL ) + return 1; + double div = (double)(size - gmsr->size)/(double)size; + return div > params.minDiversity; + } + + void init( int _index ) + { + gmsr = tmsr = NULL; + reinit = 0xffff; + rank = 0; + sizei = size = 1; + prev = next = shortcut = this; + index = _index; + } + + // to find the root of one region + MSCRNode* findRoot() + { + MSCRNode* x = this; + MSCRNode* _prev = x; + MSCRNode* _next; + for(;;) + { + _next = x->shortcut; + x->shortcut = _prev; + if( _next == x ) + break; + _prev = x; + x = _next; + } + MSCRNode* root = x; + for(;;) + { + _prev = x->shortcut; + x->shortcut = root; + if( _prev == x ) + break; + x = _prev; + } + return root; + } + MSCRNode* shortcut; // to make the finding of root less painful MSCRNode* prev; @@ -711,348 +687,86 @@ struct MSCRNode struct MSCREdge { - double chi; + double init(float _chi, MSCRNode* _left, MSCRNode* _right) + { + chi = _chi; + left = _left; + right = _right; + return chi; + } + float chi; MSCRNode* left; MSCRNode* right; }; -static double ChiSquaredDistance( uchar* x, uchar* y ) +static float ChiSquaredDistance( const uchar* x, const uchar* y ) { - return (double)((x[0]-y[0])*(x[0]-y[0]))/(double)(x[0]+y[0]+1e-10)+ - (double)((x[1]-y[1])*(x[1]-y[1]))/(double)(x[1]+y[1]+1e-10)+ - (double)((x[2]-y[2])*(x[2]-y[2]))/(double)(x[2]+y[2]+1e-10); + return (float)((x[0]-y[0])*(x[0]-y[0]))/(float)(x[0]+y[0]+FLT_EPSILON)+ + (float)((x[1]-y[1])*(x[1]-y[1]))/(float)(x[1]+y[1]+FLT_EPSILON)+ + (float)((x[2]-y[2])*(x[2]-y[2]))/(float)(x[2]+y[2]+FLT_EPSILON); } -static void initMSCRNode( MSCRNode* node ) -{ - node->gmsr = node->tmsr = NULL; - node->reinit = 0xffff; - node->rank = 0; - node->sizei = node->size = 1; - node->prev = node->next = node->shortcut = node; -} // the preprocess to get the edge list with proper gaussian blur -static int preprocessMSER_8UC3( MSCRNode* node, - MSCREdge* edge, - double* total, - CvMat* src, - CvMat* mask, - CvMat* dx, - CvMat* dy, - int Ne, - int edgeBlurSize ) +static int preprocessMSER_8UC3( MSCRNode* node, MSCREdge* edge, + double& total, const Mat& src, + Mat& dx, Mat& dy, int Ne, int edgeBlurSize ) { - int srccpt = src->step-src->cols*3; - uchar* srcptr = src->data.ptr; - uchar* lastptr = src->data.ptr+3; - double* dxptr = dx->data.db; - for ( int i = 0; i < src->rows; i++ ) - { - for ( int j = 0; j < src->cols-1; j++ ) - { - *dxptr = ChiSquaredDistance( srcptr, lastptr ); - dxptr++; - srcptr += 3; - lastptr += 3; - } - srcptr += srccpt+3; - lastptr += srccpt+3; - } - srcptr = src->data.ptr; - lastptr = src->data.ptr+src->step; - double* dyptr = dy->data.db; - for ( int i = 0; i < src->rows-1; i++ ) + int nch = src.channels(); + int i, j, nrows = src.rows, ncols = src.cols; + float* dxptr = 0; + float* dyptr = 0; + for( i = 0; i < nrows; i++ ) { - for ( int j = 0; j < src->cols; j++ ) + const uchar* srcptr = src.ptr(i); + const uchar* nextsrc = src.ptr(std::min(i+1, nrows-1)); + dxptr = dx.ptr(i); + dyptr = dy.ptr(i); + + for( j = 0; j < ncols-1; j++ ) { - *dyptr = ChiSquaredDistance( srcptr, lastptr ); - dyptr++; - srcptr += 3; - lastptr += 3; + dxptr[j] = ChiSquaredDistance( srcptr + j*nch, srcptr + (j+1)*nch ); + dyptr[j] = ChiSquaredDistance( srcptr + j*nch, nextsrc + j*nch ); } - srcptr += srccpt; - lastptr += srccpt; + dyptr[ncols-1] = ChiSquaredDistance( srcptr + (ncols-1)*nch, nextsrc + (ncols-1)*nch ); } + // get dx and dy and blur it - if ( edgeBlurSize >= 1 ) + if( edgeBlurSize >= 1 ) { - cvSmooth( dx, dx, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize ); - cvSmooth( dy, dy, CV_GAUSSIAN, edgeBlurSize, edgeBlurSize ); + GaussianBlur(dx, dx, Size(edgeBlurSize, edgeBlurSize), 0); + GaussianBlur(dy, dy, Size(edgeBlurSize, edgeBlurSize), 0); } - dxptr = dx->data.db; - dyptr = dy->data.db; + dxptr = dx.ptr(); + dyptr = dy.ptr(); // assian dx, dy to proper edge list and initialize mscr node // the nasty code here intended to avoid extra loops - if ( mask ) + MSCRNode* nodeptr = node; + for( j = 0; j < ncols-1; j++ ) { - Ne = 0; - int maskcpt = mask->step-mask->cols+1; - uchar* maskptr = mask->data.ptr; - MSCRNode* nodeptr = node; - initMSCRNode( nodeptr ); - nodeptr->index = 0; - *total += edge->chi = *dxptr; - if ( maskptr[0] && maskptr[1] ) - { - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - dxptr++; - nodeptr++; - maskptr++; - for ( int i = 1; i < src->cols-1; i++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = i; - if ( maskptr[0] && maskptr[1] ) - { - *total += edge->chi = *dxptr; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - dxptr++; - nodeptr++; - maskptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = src->cols-1; - nodeptr++; - maskptr += maskcpt; - for ( int i = 1; i < src->rows-1; i++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = i<<16; - if ( maskptr[0] ) - { - if ( maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - Ne++; - } - if ( maskptr[1] ) - { - *total += edge->chi = *dxptr; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - } - dyptr++; - dxptr++; - nodeptr++; - maskptr++; - for ( int j = 1; j < src->cols-1; j++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = (i<<16)|j; - if ( maskptr[0] ) - { - if ( maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - Ne++; - } - if ( maskptr[1] ) - { - *total += edge->chi = *dxptr; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - } - dyptr++; - dxptr++; - nodeptr++; - maskptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = (i<<16)|(src->cols-1); - if ( maskptr[0] && maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - Ne++; - } - dyptr++; - nodeptr++; - maskptr += maskcpt; - } - initMSCRNode( nodeptr ); - nodeptr->index = (src->rows-1)<<16; - if ( maskptr[0] ) - { - if ( maskptr[1] ) - { - *total += edge->chi = *dxptr; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - if ( maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - Ne++; - } - } - dxptr++; - dyptr++; - nodeptr++; - maskptr++; - for ( int i = 1; i < src->cols-1; i++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = ((src->rows-1)<<16)|i; - if ( maskptr[0] ) - { - if ( maskptr[1] ) - { - *total += edge->chi = *dxptr; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - Ne++; - } - if ( maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - Ne++; - } - } - dxptr++; - dyptr++; - nodeptr++; - maskptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); - if ( maskptr[0] && maskptr[-mask->step] ) - { - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - Ne++; - } - } else { - MSCRNode* nodeptr = node; - initMSCRNode( nodeptr ); - nodeptr->index = 0; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - nodeptr++; - for ( int i = 1; i < src->cols-1; i++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = i; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - nodeptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = src->cols-1; - nodeptr++; - for ( int i = 1; i < src->rows-1; i++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = i<<16; - *total += edge->chi = *dyptr; - dyptr++; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - nodeptr++; - for ( int j = 1; j < src->cols-1; j++ ) - { - initMSCRNode( nodeptr ); - nodeptr->index = (i<<16)|j; - *total += edge->chi = *dyptr; - dyptr++; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - nodeptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = (i<<16)|(src->cols-1); - *total += edge->chi = *dyptr; - dyptr++; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - nodeptr++; - } - initMSCRNode( nodeptr ); - nodeptr->index = (src->rows-1)<<16; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - *total += edge->chi = *dyptr; - dyptr++; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - nodeptr++; - for ( int i = 1; i < src->cols-1; i++ ) + nodeptr[j].init(j); + total += edge[j].init(dxptr[j], nodeptr+j, nodeptr+j+1); + } + dxptr += ncols - 1; + edge += ncols - 1; + nodeptr[ncols-1].init(ncols - 1); + nodeptr += ncols; + for( i = 1; i < nrows; i++ ) + { + for( j = 0; j < ncols-1; j++ ) { - initMSCRNode( nodeptr ); - nodeptr->index = ((src->rows-1)<<16)|i; - *total += edge->chi = *dxptr; - dxptr++; - edge->left = nodeptr; - edge->right = nodeptr+1; - edge++; - *total += edge->chi = *dyptr; - dyptr++; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; - edge++; - nodeptr++; + nodeptr[j].init( (i<<16)|j ); + total += edge[j*2].init(dyptr[j], nodeptr + j - ncols, nodeptr + j); + total += edge[j*2+1].init(dxptr[j], nodeptr + j, nodeptr + j + 1); } - initMSCRNode( nodeptr ); - nodeptr->index = ((src->rows-1)<<16)|(src->cols-1); - *total += edge->chi = *dyptr; - edge->left = nodeptr-src->cols; - edge->right = nodeptr; + nodeptr[ncols-1].init((i<<16)|(ncols - 1)); + total += edge[(ncols-1)*2].init(dyptr[ncols-1], nodeptr - 1, nodeptr + ncols-1); + dxptr += ncols-1; + dyptr += ncols; + edge += 2*ncols - 1; + nodeptr += ncols; } + return Ne; } @@ -1062,63 +776,34 @@ public: bool operator()(const MSCREdge& a, const MSCREdge& b) const { return a.chi < b.chi; } }; -// to find the root of one region -static MSCRNode* findMSCR( MSCRNode* x ) -{ - MSCRNode* prev = x; - MSCRNode* next; - for ( ; ; ) - { - next = x->shortcut; - x->shortcut = prev; - if ( next == x ) break; - prev= x; - x = next; - } - MSCRNode* root = x; - for ( ; ; ) - { - prev = x->shortcut; - x->shortcut = root; - if ( prev == x ) break; - x = prev; - } - return root; -} - -// the stable mscr should be: -// bigger than minArea and smaller than maxArea -// differ from its ancestor more than minDiversity -static bool MSCRStableCheck( MSCRNode* x, MSERParams params ) -{ - if ( x->size <= params.minArea || x->size >= params.maxArea ) - return 0; - if ( x->gmsr == NULL ) - return 1; - double div = (double)(x->size-x->gmsr->size)/(double)x->size; - return div > params.minDiversity; -} static void -extractMSER_8UC3( CvMat* src, - CvMat* mask, - CvSeq* contours, - CvMemStorage* storage, - MSERParams params ) +extractMSER_8uC3( const Mat& src, Mat& labels, + vector& bboxvec, + const MSER_Impl::Params& params ) { - MSCRNode* map = (MSCRNode*)cvAlloc( src->cols*src->rows*sizeof(map[0]) ); - int Ne = src->cols*src->rows*2-src->cols-src->rows; - MSCREdge* edge = (MSCREdge*)cvAlloc( Ne*sizeof(edge[0]) ); - TempMSCR* mscr = (TempMSCR*)cvAlloc( src->cols*src->rows*sizeof(mscr[0]) ); + int npixels = src.cols*src.rows; + int currlabel = 0; + int* lptr = labels.ptr(); + int lstep = (int)(labels.step/sizeof(int)); + + vector mapvec(npixels); + MSCRNode* map = &mapvec[0]; + int Ne = npixels*2 - src.cols - src.rows; + vector edgevec(Ne+1); + MSCREdge* edge = &edgevec[0]; + vector mscrvec(npixels); + TempMSCR* mscr = &mscrvec[0]; double emean = 0; - CvMat* dx = cvCreateMat( src->rows, src->cols-1, CV_64FC1 ); - CvMat* dy = cvCreateMat( src->rows-1, src->cols, CV_64FC1 ); - Ne = preprocessMSER_8UC3( map, edge, &emean, src, mask, dx, dy, Ne, params.edgeBlurSize ); + Mat dx( src.rows, src.cols-1, CV_32FC1 ); + Mat dy( src.rows, src.cols, CV_32FC1 ); + Ne = preprocessMSER_8UC3( map, edge, emean, src, dx, dy, Ne, params.edgeBlurSize ); emean = emean / (double)Ne; std::sort(edge, edge + Ne, LessThanEdge()); MSCREdge* edge_ub = edge+Ne; MSCREdge* edgeptr = edge; TempMSCR* mscrptr = mscr; + // the evolution process for ( int i = 0; i < params.maxEvolution; i++ ) { @@ -1127,23 +812,24 @@ extractMSER_8UC3( CvMat* src, double reminder = k-ti; double thres = emean*(chitab3[ti]*(1-reminder)+chitab3[ti+1]*reminder); // to process all the edges in the list that chi < thres - while ( edgeptr < edge_ub && edgeptr->chi < thres ) + while( edgeptr < edge_ub && edgeptr->chi < thres ) { - MSCRNode* lr = findMSCR( edgeptr->left ); - MSCRNode* rr = findMSCR( edgeptr->right ); + MSCRNode* lr = edgeptr->left->findRoot(); + MSCRNode* rr = edgeptr->right->findRoot(); // get the region root (who is responsible) if ( lr != rr ) { + MSCRNode* tmp; // rank idea take from: N-tree Disjoint-Set Forests for Maximally Stable Extremal Regions if ( rr->rank > lr->rank ) { - MSCRNode* tmp; CV_SWAP( lr, rr, tmp ); - } else if ( lr->rank == rr->rank ) { + } + else if ( lr->rank == rr->rank ) + { // at the same rank, we will compare the size - if ( lr->size > rr->size ) + if( lr->size > rr->size ) { - MSCRNode* tmp; CV_SWAP( lr, rr, tmp ); } lr->rank++; @@ -1175,7 +861,7 @@ extractMSER_8UC3( CvMat* src, if ( s < lr->s ) { // skip the first one and check stablity - if ( i > lr->reinit+1 && MSCRStableCheck( lr, params ) ) + if ( i > lr->reinit+1 && lr->isStable( params ) ) { if ( lr->tmsr == NULL ) { @@ -1196,113 +882,124 @@ extractMSER_8UC3( CvMat* src, if ( edgeptr >= edge_ub ) break; } - for ( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ ) + + for( TempMSCR* ptr = mscr; ptr < mscrptr; ptr++ ) + { // to prune area with margin less than minMargin - if ( ptr->m > params.minMargin ) + if( ptr->m > params.minMargin ) { - CvSeq* _contour = cvCreateSeq( CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvContour), sizeof(CvPoint), storage ); - cvSeqPushMulti( _contour, 0, ptr->size ); + int xmin = INT_MAX, ymin = INT_MAX, xmax = INT_MIN, ymax = INT_MIN; + currlabel++; MSCRNode* lpt = ptr->head; - for ( int i = 0; i < ptr->size; i++ ) + for( int i = 0; i < ptr->size; i++ ) { - CvPoint* pt = CV_GET_SEQ_ELEM( CvPoint, _contour, i ); - pt->x = (lpt->index)&0xffff; - pt->y = (lpt->index)>>16; + int x = (lpt->index)&0xffff; + int y = (lpt->index)>>16; lpt = lpt->next; + + xmin = std::min(xmin, x); + xmax = std::max(xmax, x); + ymin = std::min(ymin, y); + ymax = std::max(ymax, y); + + lptr[lstep*y + x] = currlabel; } - CvContour* contour = (CvContour*)_contour; - cvBoundingRect( contour ); - contour->color = 0; - cvSeqPush( contours, &contour ); + bboxvec.push_back(Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1)); } - cvReleaseMat( &dx ); - cvReleaseMat( &dy ); - cvFree( &mscr ); - cvFree( &edge ); - cvFree( &map ); + } } -static void -extractMSER( CvArr* _img, - CvArr* _mask, - CvSeq** _contours, - CvMemStorage* storage, - MSERParams params ) +int MSER_Impl::detectAndLabel( InputArray _src, OutputArray _labels, OutputArray _bboxes ) { - CvMat srchdr, *src = cvGetMat( _img, &srchdr ); - CvMat maskhdr, *mask = _mask ? cvGetMat( _mask, &maskhdr ) : 0; - CvSeq* contours = 0; - - CV_Assert(src != 0); - CV_Assert(CV_MAT_TYPE(src->type) == CV_8UC1 || CV_MAT_TYPE(src->type) == CV_8UC3); - CV_Assert(mask == 0 || (CV_ARE_SIZES_EQ(src, mask) && CV_MAT_TYPE(mask->type) == CV_8UC1)); - CV_Assert(storage != 0); + Mat src = _src.getMat(); + size_t npix = src.total(); + vector bboxvec; - contours = *_contours = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), storage ); - - // choose different method for different image type - // for grey image, it is: Linear Time Maximally Stable Extremal Regions - // for color image, it is: Maximally Stable Colour Regions for Recognition and Matching - switch ( CV_MAT_TYPE(src->type) ) + if( npix == 0 ) { - case CV_8UC1: - extractMSER_8UC1( src, mask, contours, storage, params ); - break; - case CV_8UC3: - extractMSER_8UC3( src, mask, contours, storage, params ); - break; + _labels.release(); + return 0; } -} + Size size = src.size(); + _labels.create( size, CV_32S ); + Mat labels = _labels.getMat(); + labels.setTo(Scalar::all(0)); -MSER::MSER( int _delta, int _min_area, int _max_area, - double _max_variation, double _min_diversity, - int _max_evolution, double _area_threshold, - double _min_margin, int _edge_blur_size ) - : delta(_delta), minArea(_min_area), maxArea(_max_area), - maxVariation(_max_variation), minDiversity(_min_diversity), - maxEvolution(_max_evolution), areaThreshold(_area_threshold), - minMargin(_min_margin), edgeBlurSize(_edge_blur_size) -{ -} + if( src.type() == CV_8U ) + { + int level_size[256]; + int lval = 1; -void MSER::operator()( InputArray image, std::vector >& dstcontours, InputArray mask ) const -{ - CvMat _image = image.getMat(), _mask, *pmask = 0; - if( !mask.empty() ) - pmask = &(_mask = mask.getMat()); - MemStorage storage(cvCreateMemStorage(0)); - Seq contours; - extractMSER( &_image, pmask, &contours.seq, storage, - MSERParams(delta, minArea, maxArea, maxVariation, minDiversity, - maxEvolution, areaThreshold, minMargin, edgeBlurSize)); - SeqIterator it = contours.begin(); - size_t i, ncontours = contours.size(); - dstcontours.resize(ncontours); - for( i = 0; i < ncontours; i++, ++it ) - Seq(*it).copyTo(dstcontours[i]); -} + if( !src.isContinuous() ) + { + src.copyTo(tempsrc); + src = tempsrc; + } + // darker to brighter (MSER+) + preprocess1( src, level_size ); + if( !params.pass2Only ) + pass( src, labels, lval, bboxvec, size, level_size, 0 ); + // brighter to darker (MSER-) + preprocess2( src, level_size ); + pass( src, labels, lval, bboxvec, size, level_size, 255 ); + } + else + { + CV_Assert( src.type() == CV_8UC3 || src.type() == CV_8UC4 ); + extractMSER_8uC3( src, labels, bboxvec, params ); + } + if( _bboxes.needed() ) + Mat(bboxvec).copyTo(_bboxes); + return (int)bboxvec.size(); +} -void MserFeatureDetector::detectImpl( InputArray _image, std::vector& keypoints, InputArray _mask ) const +void MSER_Impl::detect( InputArray _image, vector& keypoints, InputArray _mask ) { - Mat image = _image.getMat(), mask = _mask.getMat(); - std::vector > msers; + vector bboxes; + vector reg; + Mat labels, mask = _mask.getMat(); - (*this)(image, msers, mask); + int i, x, y, ncomps = detectAndLabel(_image, labels, bboxes); + CV_Assert( ncomps == (int)bboxes.size() ); - std::vector >::const_iterator contour_it = msers.begin(); - Rect r(0, 0, image.cols, image.rows); - for( ; contour_it != msers.end(); ++contour_it ) + keypoints.clear(); + for( i = 0; i < ncomps; i++ ) { + Rect r = bboxes[i]; + reg.reserve(r.area()); + reg.clear(); + + for( y = r.y; y < r.y + r.height; y++ ) + { + const int* lptr = labels.ptr(y); + for( x = r.x; x < r.x + r.width; x++ ) + { + if( lptr[x] == i+1 ) + reg.push_back(Point(x, y)); + } + } // TODO check transformation from MSER region to KeyPoint - RotatedRect rect = fitEllipse(Mat(*contour_it)); + RotatedRect rect = fitEllipse(Mat(reg)); float diam = std::sqrt(rect.size.height*rect.size.width); - if( diam > std::numeric_limits::epsilon() && r.contains(rect.center) ) + if( diam > std::numeric_limits::epsilon() && r.contains(rect.center) && + (mask.empty() || mask.at(cvRound(rect.center.y), cvRound(rect.center.x)) != 0) ) keypoints.push_back( KeyPoint(rect.center, diam) ); } +} +Ptr MSER::create( int _delta, int _min_area, int _max_area, + double _max_variation, double _min_diversity, + int _max_evolution, double _area_threshold, + double _min_margin, int _edge_blur_size ) +{ + return makePtr( + MSER_Impl::Params(_delta, _min_area, _max_area, + _max_variation, _min_diversity, + _max_evolution, _area_threshold, + _min_margin, _edge_blur_size)); } } diff --git a/modules/features2d/src/orb.cpp b/modules/features2d/src/orb.cpp index 85b4318b85..b57ab4dea2 100644 --- a/modules/features2d/src/orb.cpp +++ b/modules/features2d/src/orb.cpp @@ -664,19 +664,11 @@ public: int defaultNorm() const; // Compute the ORB_Impl features and descriptors on an image - void operator()(InputArray image, InputArray mask, std::vector& keypoints) const; - - // Compute the ORB_Impl features and descriptors on an image - void operator()( InputArray image, InputArray mask, std::vector& keypoints, - OutputArray descriptors, bool useProvidedKeypoints=false ) const; - - AlgorithmInfo* info() const; + void detectAndCompute( InputArray image, InputArray mask, std::vector& keypoints, + OutputArray descriptors, bool useProvidedKeypoints=false ); protected: - void computeImpl( InputArray image, std::vector& keypoints, OutputArray descriptors ) const; - void detectImpl( InputArray image, std::vector& keypoints, InputArray mask=noArray() ) const; - int nfeatures; double scaleFactor; int nlevels; @@ -703,17 +695,6 @@ int ORB_Impl::defaultNorm() const return NORM_HAMMING; } -/** Compute the ORB_Impl features and descriptors on an image - * @param img the image to compute the features and descriptors on - * @param mask the mask to apply - * @param keypoints the resulting keypoints - */ -void ORB_Impl::operator()(InputArray image, InputArray mask, std::vector& keypoints) const -{ - (*this)(image, mask, keypoints, noArray(), false); -} - - static void uploadORBKeypoints(const std::vector& src, std::vector& buf, OutputArray dst) { size_t i, n = src.size(); @@ -813,8 +794,10 @@ static void computeKeyPoints(const Mat& imagePyramid, Mat mask = maskPyramid.empty() ? Mat() : maskPyramid(layerInfo[level]); // Detect FAST features, 20 is a good threshold - FastFeatureDetector fd(fastThreshold, true); - fd.detect(img, keypoints, mask); + { + Ptr fd = FastFeatureDetector::create(fastThreshold, true); + fd->detect(img, keypoints, mask); + } // Remove keypoints very close to the border KeyPointsFilter::runByImageBorder(keypoints, img.size(), edgeThreshold); @@ -928,8 +911,9 @@ static void computeKeyPoints(const Mat& imagePyramid, * @param do_keypoints if true, the keypoints are computed, otherwise used as an input * @param do_descriptors if true, also computes the descriptors */ -void ORB_Impl::operator()( InputArray _image, InputArray _mask, std::vector& keypoints, - OutputArray _descriptors, bool useProvidedKeypoints ) const +void ORB_Impl::detectAndCompute( InputArray _image, InputArray _mask, + std::vector& keypoints, + OutputArray _descriptors, bool useProvidedKeypoints ) { CV_Assert(patchSize >= 2); @@ -1159,15 +1143,11 @@ void ORB_Impl::operator()( InputArray _image, InputArray _mask, std::vector& keypoints, InputArray mask) const +Ptr ORB::create(int nfeatures, float scaleFactor, int nlevels, int edgeThreshold, + int firstLevel, int WTA_K, int scoreType, int patchSize, int fastThreshold) { - (*this)(image.getMat(), mask.getMat(), keypoints, noArray(), false); + return makePtr(nfeatures, scaleFactor, nlevels, edgeThreshold, + firstLevel, WTA_K, scoreType, patchSize, fastThreshold); } -void ORB_Impl::computeImpl( InputArray image, std::vector& keypoints, OutputArray descriptors) const -{ - (*this)(image, Mat(), keypoints, descriptors, true); -} - - } diff --git a/modules/features2d/test/test_descriptors_regression.cpp b/modules/features2d/test/test_descriptors_regression.cpp index c09a466b8a..baffeeb3f8 100644 --- a/modules/features2d/test/test_descriptors_regression.cpp +++ b/modules/features2d/test/test_descriptors_regression.cpp @@ -314,31 +314,34 @@ private: TEST( Features2d_DescriptorExtractor_BRISK, regression ) { - CV_DescriptorExtractorTest test( "descriptor-brisk", (CV_DescriptorExtractorTest::DistanceType)2.f, - DescriptorExtractor::create("BRISK") ); + CV_DescriptorExtractorTest test( "descriptor-brisk", + (CV_DescriptorExtractorTest::DistanceType)2.f, + BRISK::create() ); test.safe_run(); } TEST( Features2d_DescriptorExtractor_ORB, regression ) { // TODO adjust the parameters below - CV_DescriptorExtractorTest test( "descriptor-orb", (CV_DescriptorExtractorTest::DistanceType)12.f, - DescriptorExtractor::create("ORB") ); + CV_DescriptorExtractorTest test( "descriptor-orb", + (CV_DescriptorExtractorTest::DistanceType)12.f, + ORB::create() ); test.safe_run(); } TEST( Features2d_DescriptorExtractor_KAZE, regression ) { CV_DescriptorExtractorTest< L2 > test( "descriptor-kaze", 0.03f, - DescriptorExtractor::create("KAZE"), - L2(), FeatureDetector::create("KAZE")); + KAZE::create(), + L2() ); test.safe_run(); } TEST( Features2d_DescriptorExtractor_AKAZE, regression ) { - CV_DescriptorExtractorTest test( "descriptor-akaze", (CV_DescriptorExtractorTest::DistanceType)12.f, - DescriptorExtractor::create("AKAZE"), - Hamming(), FeatureDetector::create("AKAZE")); + CV_DescriptorExtractorTest test( "descriptor-akaze", + (CV_DescriptorExtractorTest::DistanceType)12.f, + AKAZE::create(), + Hamming(), AKAZE::create()); test.safe_run(); } diff --git a/modules/features2d/test/test_detectors_regression.cpp b/modules/features2d/test/test_detectors_regression.cpp index 1a7d09a853..a3ca0a9f38 100644 --- a/modules/features2d/test/test_detectors_regression.cpp +++ b/modules/features2d/test/test_detectors_regression.cpp @@ -249,48 +249,48 @@ void CV_FeatureDetectorTest::run( int /*start_from*/ ) TEST( Features2d_Detector_BRISK, regression ) { - CV_FeatureDetectorTest test( "detector-brisk", FeatureDetector::create("BRISK") ); + CV_FeatureDetectorTest test( "detector-brisk", BRISK::create() ); test.safe_run(); } TEST( Features2d_Detector_FAST, regression ) { - CV_FeatureDetectorTest test( "detector-fast", FeatureDetector::create("FAST") ); + CV_FeatureDetectorTest test( "detector-fast", FastFeatureDetector::create() ); test.safe_run(); } TEST( Features2d_Detector_GFTT, regression ) { - CV_FeatureDetectorTest test( "detector-gftt", FeatureDetector::create("GFTT") ); + CV_FeatureDetectorTest test( "detector-gftt", GFTTDetector::create() ); test.safe_run(); } TEST( Features2d_Detector_Harris, regression ) { - CV_FeatureDetectorTest test( "detector-harris", FeatureDetector::create("HARRIS") ); + CV_FeatureDetectorTest test( "detector-harris", GFTTDetector::create(1000, 0.01, 1, 3, true, 0.04)); test.safe_run(); } TEST( Features2d_Detector_MSER, DISABLED_regression ) { - CV_FeatureDetectorTest test( "detector-mser", FeatureDetector::create("MSER") ); + CV_FeatureDetectorTest test( "detector-mser", MSER::create() ); test.safe_run(); } TEST( Features2d_Detector_ORB, regression ) { - CV_FeatureDetectorTest test( "detector-orb", FeatureDetector::create("ORB") ); + CV_FeatureDetectorTest test( "detector-orb", ORB::create() ); test.safe_run(); } TEST( Features2d_Detector_KAZE, regression ) { - CV_FeatureDetectorTest test( "detector-kaze", FeatureDetector::create("KAZE") ); + CV_FeatureDetectorTest test( "detector-kaze", KAZE::create() ); test.safe_run(); } TEST( Features2d_Detector_AKAZE, regression ) { - CV_FeatureDetectorTest test( "detector-akaze", FeatureDetector::create("AKAZE") ); + CV_FeatureDetectorTest test( "detector-akaze", AKAZE::create() ); test.safe_run(); } diff --git a/modules/features2d/test/test_keypoints.cpp b/modules/features2d/test/test_keypoints.cpp index 349c03ed49..561c3cc6ee 100644 --- a/modules/features2d/test/test_keypoints.cpp +++ b/modules/features2d/test/test_keypoints.cpp @@ -61,7 +61,6 @@ public: protected: virtual void run(int) { - cv::initModule_features2d(); CV_Assert(detector); string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME; @@ -121,51 +120,51 @@ protected: TEST(Features2d_Detector_Keypoints_BRISK, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.BRISK")); + CV_FeatureDetectorKeypointsTest test(BRISK::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_FAST, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.FAST")); + CV_FeatureDetectorKeypointsTest test(FastFeatureDetector::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_HARRIS, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.HARRIS")); + CV_FeatureDetectorKeypointsTest test(GFTTDetector::create(1000, 0.01, 1, 3, true, 0.04)); test.safe_run(); } TEST(Features2d_Detector_Keypoints_GFTT, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.GFTT")); + CV_FeatureDetectorKeypointsTest test(GFTTDetector::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_MSER, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.MSER")); + CV_FeatureDetectorKeypointsTest test(MSER::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_ORB, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.ORB")); + CV_FeatureDetectorKeypointsTest test(ORB::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_KAZE, validation) { - CV_FeatureDetectorKeypointsTest test(Algorithm::create("Feature2D.KAZE")); + CV_FeatureDetectorKeypointsTest test(KAZE::create()); test.safe_run(); } TEST(Features2d_Detector_Keypoints_AKAZE, validation) { - CV_FeatureDetectorKeypointsTest test_kaze(cv::Ptr(new cv::AKAZE(cv::DESCRIPTOR_KAZE))); + CV_FeatureDetectorKeypointsTest test_kaze(AKAZE::create(AKAZE::DESCRIPTOR_KAZE)); test_kaze.safe_run(); - CV_FeatureDetectorKeypointsTest test_mldb(cv::Ptr(new cv::AKAZE(cv::DESCRIPTOR_MLDB))); + CV_FeatureDetectorKeypointsTest test_mldb(AKAZE::create(AKAZE::DESCRIPTOR_MLDB)); test_mldb.safe_run(); } diff --git a/modules/features2d/test/test_mser.cpp b/modules/features2d/test/test_mser.cpp index 6b1b4ef639..29572870ae 100644 --- a/modules/features2d/test/test_mser.cpp +++ b/modules/features2d/test/test_mser.cpp @@ -43,6 +43,8 @@ #include "test_precomp.hpp" #include "opencv2/imgproc/imgproc_c.h" +#if 0 + #include #include using namespace std; @@ -205,3 +207,5 @@ void CV_MserTest::run(int) } TEST(Features2d_MSER, DISABLED_regression) { CV_MserTest test; test.safe_run(); } + +#endif diff --git a/modules/features2d/test/test_orb.cpp b/modules/features2d/test/test_orb.cpp index 45e76906d1..47335b4741 100644 --- a/modules/features2d/test/test_orb.cpp +++ b/modules/features2d/test/test_orb.cpp @@ -47,10 +47,10 @@ using namespace cv; TEST(Features2D_ORB, _1996) { - Ptr fd = FeatureDetector::create("ORB"); + Ptr fd = ORB::create(); fd->set("nFeatures", 10000);//setting a higher maximum to make effect of threshold visible fd->set("fastThreshold", 20);//more features than the default - Ptr de = DescriptorExtractor::create("ORB"); + Ptr de = fd; Mat image = imread(string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png"); ASSERT_FALSE(image.empty()); diff --git a/modules/stitching/src/matchers.cpp b/modules/stitching/src/matchers.cpp index adb6fab409..f1dec76c4b 100644 --- a/modules/stitching/src/matchers.cpp +++ b/modules/stitching/src/matchers.cpp @@ -367,7 +367,7 @@ void SurfFeaturesFinder::find(InputArray image, ImageFeatures &features) else { UMat descriptors; - (*surf)(gray_image, Mat(), features.keypoints, descriptors); + surf->detectAndCompute(gray_image, Mat(), features.keypoints, descriptors); features.descriptors = descriptors.reshape(1, (int)features.keypoints.size()); } } @@ -375,7 +375,7 @@ void SurfFeaturesFinder::find(InputArray image, ImageFeatures &features) OrbFeaturesFinder::OrbFeaturesFinder(Size _grid_size, int n_features, float scaleFactor, int nlevels) { grid_size = _grid_size; - orb = makePtr(n_features * (99 + grid_size.area())/100/grid_size.area(), scaleFactor, nlevels); + orb = ORB::create(n_features * (99 + grid_size.area())/100/grid_size.area(), scaleFactor, nlevels); } void OrbFeaturesFinder::find(InputArray image, ImageFeatures &features) @@ -395,7 +395,7 @@ void OrbFeaturesFinder::find(InputArray image, ImageFeatures &features) } if (grid_size.area() == 1) - (*orb)(gray_image, Mat(), features.keypoints, features.descriptors); + orb->detectAndCompute(gray_image, Mat(), features.keypoints, features.descriptors); else { features.keypoints.clear(); @@ -425,7 +425,7 @@ void OrbFeaturesFinder::find(InputArray image, ImageFeatures &features) // << " gray_image_part.dims=" << gray_image_part.dims << ", " // << " gray_image_part.data=" << ((size_t)gray_image_part.data) << "\n"); - (*orb)(gray_image_part, UMat(), points, descriptors); + orb->detectAndCompute(gray_image_part, UMat(), points, descriptors); features.keypoints.reserve(features.keypoints.size() + points.size()); for (std::vector::iterator kp = points.begin(); kp != points.end(); ++kp) diff --git a/modules/videostab/src/global_motion.cpp b/modules/videostab/src/global_motion.cpp index 560d7b9474..4875bef23d 100644 --- a/modules/videostab/src/global_motion.cpp +++ b/modules/videostab/src/global_motion.cpp @@ -671,7 +671,7 @@ Mat ToFileMotionWriter::estimate(const Mat &frame0, const Mat &frame1, bool *ok) KeypointBasedMotionEstimator::KeypointBasedMotionEstimator(Ptr estimator) : ImageMotionEstimatorBase(estimator->motionModel()), motionEstimator_(estimator) { - setDetector(makePtr()); + setDetector(GFTTDetector::create()); setOpticalFlowEstimator(makePtr()); setOutlierRejector(makePtr()); } -- GitLab