diff --git a/.gitignore b/.gitignore index 741704db8..bb42d2a49 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ Thumbs.db tags tegra/ +*.i +.download* diff --git a/modules/xfeatures2d/CMakeLists.txt b/modules/xfeatures2d/CMakeLists.txt index 891bf9b79..cabb8a68e 100644 --- a/modules/xfeatures2d/CMakeLists.txt +++ b/modules/xfeatures2d/CMakeLists.txt @@ -1,3 +1,4 @@ set(the_description "Contributed/Experimental Algorithms for Salient 2D Features Detection") ocv_define_module(xfeatures2d opencv_core opencv_imgproc opencv_features2d opencv_calib3d opencv_shape opencv_highgui opencv_videoio opencv_ml OPTIONAL opencv_cudaarithm WRAP python java) +include(cmake/download_vgg.cmake) \ No newline at end of file diff --git a/modules/xfeatures2d/cmake/download_vgg.cmake b/modules/xfeatures2d/cmake/download_vgg.cmake new file mode 100644 index 000000000..92c25d727 --- /dev/null +++ b/modules/xfeatures2d/cmake/download_vgg.cmake @@ -0,0 +1,23 @@ + +set(OPENCV_3RDPARTY_COMMIT "fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d") +set(FILE_HASH_VGG_48 "e8d0dcd54d1bcfdc29203d011a797179") +set(FILE_HASH_VGG_64 "7126a5d9a8884ebca5aea5d63d677225") +set(FILE_HASH_VGG_80 "7cd47228edec52b6d82f46511af325c5") +set(FILE_HASH_VGG_120 "151805e03568c9f490a5e3a872777b75") + + +set(VGG_DOWNLOAD_URL ${OPENCV_CONTRIB_VGG_URL};$ENV{OPENCV_CONTRIB_VGG_URL};https://raw.githubusercontent.com/Itseez/opencv_3rdparty/${OPENCV_3RDPARTY_COMMIT}/) + +function(vgg_download file id) + message(STATUS "Check contents of ${file} ...") + ocv_download(PACKAGE ${file} + HASH ${FILE_HASH_${id}} + URL ${VGG_DOWNLOAD_URL} + DESTINATION_DIR ${CMAKE_CURRENT_LIST_DIR}/../src + DOWNLOAD_DIR ${CMAKE_CURRENT_LIST_DIR}/.download) +endfunction() + +vgg_download(vgg_generated_48.i VGG_48) +vgg_download(vgg_generated_64.i VGG_64) +vgg_download(vgg_generated_80.i VGG_80) +vgg_download(vgg_generated_120.i VGG_120) diff --git a/modules/xfeatures2d/doc/xfeatures2d.bib b/modules/xfeatures2d/doc/xfeatures2d.bib index 43ba4b102..6e1d38840 100644 --- a/modules/xfeatures2d/doc/xfeatures2d.bib +++ b/modules/xfeatures2d/doc/xfeatures2d.bib @@ -71,3 +71,10 @@ booktitle={Asian Conference on Computer Vision -- ACCV 2014}, year={2014} } + +@article{Simonyan14, + author = "Simonyan, K. and Vedaldi, A. and Zisserman, A.", + title = "Learning Local Feature Descriptors Using Convex Optimisation", + journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence", + year = "2014" +} diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index b8f62b829..4e8e69d1b 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -168,7 +168,7 @@ Note: a complete example can be found under /samples/cpp/tutorial_code/xfeatures class CV_EXPORTS_W LATCH : public Feature2D { public: - CV_WRAP static Ptr create(int bytes = 32, bool rotationInvariance = true, int half_ssd_size=3); + CV_WRAP static Ptr create(int bytes = 32, bool rotationInvariance = true, int half_ssd_size=3); }; /** @brief Class implementing DAISY descriptor, described in @cite Tola10 @@ -280,6 +280,44 @@ public: float m_scale_factor = 1.25f, int m_n_scales = -1, bool m_compute_orientation = false); }; +/** @brief Class implementing VGG (Oxford Visual Geometry Group) descriptor trained end to end +using "Descriptor Learning Using Convex Optimisation" (DLCO) aparatus described in @cite Simonyan14. + +@param desc type of descriptor to use, VGG::VGG_120 is default (120 dimensions float) +Available types are VGG::VGG_120, VGG::VGG_80, VGG::VGG_64, VGG::VGG_48 +@param isigma gaussian kernel value for image blur (default is 1.4f) +@param img_normalize use image sample intensity normalization (enabled by default) +@param use_orientation sample patterns using keypoints orientation, enabled by default +@param scale_factor adjust the sampling window of detected keypoints to 64.0f (VGG sampling window) +6.25f is default and fits for KAZE, SURF detected keypoints window ratio +6.75f should be the scale for SIFT detected keypoints window ratio +5.00f should be the scale for AKAZE, MSD, AGAST, FAST, BRISK keypoints window ratio +0.75f should be the scale for ORB keypoints ratio + +@param dsc_normalize clamp descriptors to 255 and convert to uchar CV_8UC1 (disabled by default) + + */ +class CV_EXPORTS_W VGG : public Feature2D +{ +public: + + CV_WRAP enum + { + VGG_120 = 100, VGG_80 = 101, VGG_64 = 102, VGG_48 = 103, + }; + + CV_WRAP static Ptr create( int desc = VGG::VGG_120, float isigma = 1.4f, + bool img_normalize = true, bool use_scale_orientation = true, + float scale_factor = 6.25f, bool dsc_normalize = false ); + /** + * @param image image to extract descriptors + * @param keypoints of interest within image + * @param descriptors resulted descriptors array + */ + CV_WRAP virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ) = 0; + +}; + //! @} } diff --git a/modules/xfeatures2d/perf/perf_vgg.cpp b/modules/xfeatures2d/perf/perf_vgg.cpp new file mode 100644 index 000000000..eb6c4f69d --- /dev/null +++ b/modules/xfeatures2d/perf/perf_vgg.cpp @@ -0,0 +1,35 @@ +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::xfeatures2d; +using namespace perf; +using std::tr1::make_tuple; +using std::tr1::get; + +typedef perf::TestBaseWithParam vgg; + +#define VGG_IMAGES \ + "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ + "stitching/a3.png" + +PERF_TEST_P(vgg, extract, testing::Values(VGG_IMAGES)) +{ + string filename = getDataPath(GetParam()); + Mat frame = imread(filename, IMREAD_GRAYSCALE); + ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename; + + Mat mask; + declare.in(frame).time(90); + + Ptr detector = KAZE::create(); + vector points; + detector->detect(frame, points, mask); + + Ptr descriptor = VGG::create(); + Mat_ descriptors; + // compute keypoints descriptor + TEST_CYCLE() descriptor->compute(frame, points, descriptors); + + SANITY_CHECK_NOTHING(); +} diff --git a/modules/xfeatures2d/src/vgg.cpp b/modules/xfeatures2d/src/vgg.cpp new file mode 100644 index 000000000..6f91be45e --- /dev/null +++ b/modules/xfeatures2d/src/vgg.cpp @@ -0,0 +1,522 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2014, 2015 + * + * Karen Simonyan + * Andrea Vedaldi + * Andrew Zisserman + * + * Visual Geometry Group + * Department of Engineering Science, University of Oxford + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions 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. + * * Neither the name of the copyright holders nor the names of its + * contributors may 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 + * COPYRIGHT OWNER 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. + *********************************************************************/ + +/* + + "Learning Local Feature Descriptors Using Convex Optimisation", + Simonyan, K. and Vedaldi, A. and Zisserman, A., + IEEE Transactions on Pattern Analysis and Machine Intelligence, 2014 + + "Discriminative Learning of Local Image Descriptors", + Matthew A. Brown, Gang Hua, Simon A. J. Winder, + IEEE Transactions on Pattern Analysis and Machine Intelligence, 2011 + + OpenCV port by: Cristian Balint + + */ + +#include "precomp.hpp" + + + +using namespace cv; +using namespace std; + + +namespace cv +{ +namespace xfeatures2d +{ + +/* + !VGG implementation + */ +class VGG_Impl : public VGG +{ + +public: + + // constructor + explicit VGG_Impl( int desc = VGG::VGG_80, float isigma = 1.4f, + bool img_normalize = true, bool use_scale_orientation = true, + float scale_factor = 6.25f, bool dsc_normalize = false ); + + // destructor + virtual ~VGG_Impl(); + + // returns the descriptor length in bytes + virtual int descriptorSize() const { return m_descriptor_size; }; + + // returns the descriptor type + virtual int descriptorType() const { return CV_32F; } + + // returns the default norm type + virtual int defaultNorm() const { return NORM_L2; } + + // compute descriptors given keypoints + virtual void compute( InputArray image, vector& keypoints, OutputArray descriptors ); + +protected: + + /* + * VGG parameters + */ + + int m_descriptor_size; + + // gauss sigma + float m_isigma; + + // angle bins + int m_anglebins; + + // sample window + float m_scale_factor; + + /* + * VGG switches + */ + + // normalize image + bool m_img_normalize; + + // switch to enable sample by keypoints orientation + bool m_use_scale_orientation; + + // normalize desc + bool m_dsc_normalize; + + /* + * VGG arrays + */ + + // image + Mat m_image; + + // pool regions & proj + Mat m_PRFilters, m_Proj; + +private: + + /* + * VGG functions + */ + + // initialize parameters + inline void ini_params( const int PRrows, const int PRcols, + const unsigned int PRidx[], const unsigned int PRidxSize, const unsigned int PR[], + const int PJrows, const int PJcols, + const unsigned int PJidx[], const unsigned int PJidxSize, const unsigned int PJ[] ); + +}; // END VGG_Impl CLASS + +// ------------------------------------------------- +/* VGG internal routines */ + +// sample 64x64 patch from image given keypoint +static inline void get_patch( const KeyPoint kp, Mat& Patch, const Mat& image, + const bool use_scale_orientation, const float scale_factor ) +{ + // scale & radians + float scale = kp.size / 64.0f * scale_factor; + const float angle = (kp.angle == -1) + ? 0 : ( (kp.angle)*(float)CV_PI ) / 180.f; + + // transforms + const float tsin = sin(angle) * scale; + const float tcos = cos(angle) * scale; + + const float half_cols = (float)Patch.cols / 2.0f; + const float half_rows = (float)Patch.rows / 2.0f; + + // sample form original image + for ( int x = 0; x < Patch.cols; x++ ) + { + for ( int y = 0; y < Patch.rows; y++ ) + { + if ( use_scale_orientation ) + { + const float xoff = x - half_cols; + const float yoff = y - half_rows; + // the rotation shifts & scale + int img_x = int( (kp.pt.x + 0.5f) + xoff*tcos - yoff*tsin ); + int img_y = int( (kp.pt.y + 0.5f) + xoff*tsin + yoff*tcos ); + // sample only within image + if ( ( img_x < image.cols ) && ( img_x >= 0 ) + && ( img_y < image.rows ) && ( img_y >= 0 ) ) + Patch.at( y, x ) = image.at( img_y, img_x ); + else + Patch.at( y, x ) = 0.0f; + } + else + { + const float xoff = x - half_cols; + const float yoff = y - half_rows; + // the samples from image + int img_x = int( kp.pt.x + 0.5f + xoff ); + int img_y = int( kp.pt.y + 0.5f + yoff ); + // sample only within image + if ( ( img_x < image.cols ) && ( img_x >= 0 ) + && ( img_y < image.rows ) && ( img_y >= 0 ) ) + Patch.at( y, x ) = image.at( img_y, img_x ); + else + Patch.at( y, x ) = 0.0f; + } + } + } +} + +// get descriptor given 64x64 image patch +static void get_desc( const Mat Patch, Mat& PatchTrans, int anglebins, bool img_normalize ) +{ + Mat Ix, Iy; + // % compute gradient + float kparam[3] = { -1, 0, 1 }; + Mat Kernel( 1, 3, CV_32F, &kparam ); + filter2D( Patch, Ix, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( Patch, Iy, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + + Mat GMag, GAngle; + + // % gradient magnitude + // % GMag = sqrt(Ix .^ 2 + Iy .^ 2); + magnitude( Ix, Iy, GMag ); + + // % gradient orientation: [0; 2 * pi] + // % GAngle = atan2(Iy, Ix) + pi; + //phase( Ix, Iy, GAngle, false ); //<- opencv is buggy + GAngle = Mat( GMag.rows, GMag.cols, CV_32F ); + for ( int i = 0; i < (int)GAngle.total(); i++ ) + GAngle.at(i) = atan2( Iy.at(i), Ix.at(i) ) + (float)CV_PI; + + // % soft-assignment of gradients to the orientation histogram + float AngleStep = 2.0f * (float) CV_PI / (float) anglebins; + Mat GAngleRatio = GAngle / AngleStep - 0.5f; + + // % Offset1 = mod(GAngleRatio, 1); + Mat Offset1( GAngleRatio.rows, GAngleRatio.cols, CV_32F ); + for ( int i = 0; i < (int)GAngleRatio.total(); i++ ) + Offset1.at(i) = GAngleRatio.at(i) - floor( GAngleRatio.at(i) ); + + Mat w1 = 1.0f - Offset1.t(); + Mat w2 = Offset1.t(); + + Mat Bin1( GAngleRatio.rows, GAngleRatio.cols, CV_8U ); + Mat Bin2( GAngleRatio.rows, GAngleRatio.cols, CV_8U ); + + // % Bin1 = ceil(GAngleRatio); + // % Bin1(Bin1 == 0) = Params.nAngleBins; + for ( int i = 0; i < (int)GAngleRatio.total(); i++ ) + { + if ( ceil( GAngleRatio.at(i) - 1.0f) == -1.0f ) + Bin1.at(i) = (uchar) anglebins - 1; + else + Bin1.at(i) = (uchar) ceil( GAngleRatio.at(i) - 1.0f ); + } + + // % Bin2 = Bin1 + 1; + // % Bin2(Bin2 > Params.nAngleBins) = 1; + for ( int i = 0; i < (int)GAngleRatio.total(); i++ ) + { + if ( ( Bin1.at(i) + 1 ) > anglebins - 1 ) + Bin2.at(i) = 0; + else + Bin2.at(i) = Bin1.at(i) + 1; + } + + // normalize + if ( img_normalize ) + { + // % Quantile = 0.8; + float q = 0.8f; + + // % T = quantile(GMag(:), Quantile); + Mat GMagSorted; + sort( GMag.reshape( 0, 1 ), GMagSorted, SORT_ASCENDING ); + + int n = GMagSorted.cols; + // scipy/stats/mstats_basic.py#L1718 mquantiles() + // m = alphap + p*(1.-alphap-betap) + // alphap = 0.5 betap = 0.5 => (m = 0.5) + // aleph = (n*p + m) + float aleph = ( n * q + 0.5f ); + int k = cvFloor( aleph ); + if ( k >= n - 1 ) k = n - 1; + if ( k <= 1 ) k = 1; + + float gamma = aleph - k; + if ( gamma >= 1.0f ) gamma = 1.0f; + if ( gamma <= 0.0f ) gamma = 0.0f; + // quantile out from distribution + float T = ( 1.0f - gamma ) * GMagSorted.at( k - 1 ) + + gamma * GMagSorted.at( k ); + + // avoid NaN + if ( T != 0.0f ) GMag /= ( T / anglebins ); + } + + Mat Bin1T = Bin1.t(); + Mat Bin2T = Bin2.t(); + Mat GMagT = GMag.t(); + + // % feature channels + PatchTrans = Mat( (int)Patch.total(), anglebins, CV_32F, Scalar::all(0) ); + + for ( int i = 0; i < anglebins; i++ ) + { + for ( int p = 0; p < (int)Patch.total(); p++ ) + { + if ( Bin1T.at(p) == i ) + PatchTrans.at(p,i) = w1.at(p) * GMagT.at(p); + if ( Bin2T.at(p) == i ) + PatchTrans.at(p,i) = w2.at(p) * GMagT.at(p); + } + } +} + +// ------------------------------------------------- +/* VGG interface implementation */ + +struct ComputeDescInvoker : ParallelLoopBody +{ + ComputeDescInvoker( const Mat& _image, Mat* _descriptors, + const vector& _keypoints, + const Mat& _PRFilters, const Mat& _Proj, + const int _anglebins, const bool _img_normalize, + const bool _use_scale_orientation, const float _scale_factor ) + { + image = _image; + keypoints = _keypoints; + descriptors = _descriptors; + + Proj = _Proj; + PRFilters = _PRFilters; + + anglebins = _anglebins; + scale_factor = _scale_factor; + img_normalize = _img_normalize; + use_scale_orientation = _use_scale_orientation; + } + + void operator ()(const cv::Range& range) const + { + Mat Desc, PatchTrans; + Mat Patch( 64, 64, CV_32F ); + for (int k = range.start; k < range.end; k++) + { + // sample patch from image + get_patch( keypoints[k], Patch, image, use_scale_orientation, scale_factor ); + // compute transform + get_desc( Patch, PatchTrans, anglebins, img_normalize ); + // pool features + Desc = PRFilters * PatchTrans; + // crop + min( Desc, 1.0f, Desc ); + // reshape + Desc = Desc.reshape( 1, (int)Desc.total() ); + // project + descriptors->row( k ) = Desc.t() * Proj.t(); + } + } + + Mat image; + Mat *descriptors; + vector keypoints; + + Mat Proj; + Mat PRFilters; + + int anglebins; + float scale_factor; + bool img_normalize; + bool use_scale_orientation; +}; + +// descriptor computation using keypoints +void VGG_Impl::compute( InputArray _image, vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + m_image = _image.getMat().clone(); + + // Only 8bit images + CV_Assert( m_image.depth() == CV_8U ); + + // convert to gray inplace + if( m_image.channels() > 1 ) + cvtColor( m_image, m_image, COLOR_BGR2GRAY ); + + //convert + Mat image; + m_image.convertTo( image, CV_32F ); + m_image = image; + image.release(); + + // smooth whole image + GaussianBlur( m_image, m_image, Size( 0, 0 ), m_isigma, m_isigma, BORDER_REPLICATE ); + + // allocate array + _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); + + // prepare descriptors + Mat descriptors = _descriptors.getMat(); + descriptors.setTo( Scalar(0) ); + + parallel_for_( Range( 0, (int) keypoints.size() ), + ComputeDescInvoker( m_image, &descriptors, keypoints, m_PRFilters, m_Proj, + m_anglebins, m_img_normalize, m_use_scale_orientation, + m_scale_factor ) + ); + + // normalize desc + if ( m_dsc_normalize ) + { + normalize( descriptors, descriptors, 0.0f, 255.0f, NORM_MINMAX, CV_32F ); + descriptors.convertTo( _descriptors, CV_8U ); + } +} + +void VGG_Impl::ini_params( const int PRrows, const int PRcols, + const unsigned int PRidx[], const unsigned int PRidxSize, + const unsigned int PR[], + const int PJrows, const int PJcols, + const unsigned int PJidx[], const unsigned int PJidxSize, + const unsigned int PJ[] ) +{ + int idx; + + // initialize pool-region matrix + m_PRFilters = Mat::zeros( PRrows, PRcols, CV_32F ); + // initialize projection matrix + m_Proj = Mat::zeros( PJrows, PJcols, CV_32F ); + + idx = 0; + // fill sparse pool-region matrix + for ( size_t i = 0; i < PRidxSize; i=i+2 ) + { + for ( size_t k = 0; k < PRidx[i+1]; k++ ) + { + // expand floats from hex blobs + m_PRFilters.at( PRidx[i] + (int)k ) = *(float *)&PR[idx]; + idx++; + } + } + + idx = 0; + // fill sparse projection matrix + for ( size_t i = 0; i < PJidxSize; i=i+2 ) + { + for ( size_t k = 0; k < PJidx[i+1]; k++ ) + { + // expand floats from hex blobs + m_Proj.at( PJidx[i] + (int)k ) = *(float *)&PJ[idx]; + idx++; + } + } +} + +// constructor +VGG_Impl::VGG_Impl( int _desc, float _isigma, bool _img_normalize, + bool _use_scale_orientation, float _scale_factor, bool _dsc_normalize ) + : m_isigma( _isigma ), m_scale_factor( _scale_factor ), + m_img_normalize( _img_normalize ), + m_use_scale_orientation( _use_scale_orientation ), + m_dsc_normalize( _dsc_normalize ) +{ + // constant + m_anglebins = 8; + + // desc type + switch ( _desc ) + { + case VGG::VGG_120: + { + #include "vgg_generated_120.i" + ini_params( PRrows, PRcols, PRidx, sizeof(PRidx)/sizeof(PRidx[0]), PR, + PJrows, PJcols, PJidx, sizeof(PJidx)/sizeof(PJidx[0]), PJ ); + } + break; + case VGG::VGG_80: + { + #include "vgg_generated_80.i" + ini_params( PRrows, PRcols, PRidx, sizeof(PRidx)/sizeof(PRidx[0]), PR, + PJrows, PJcols, PJidx, sizeof(PJidx)/sizeof(PJidx[0]), PJ ); + } + break; + case VGG::VGG_64: + { + #include "vgg_generated_64.i" + ini_params( PRrows, PRcols, PRidx, sizeof(PRidx)/sizeof(PRidx[0]), PR, + PJrows, PJcols, PJidx, sizeof(PJidx)/sizeof(PJidx[0]), PJ ); + } + break; + case VGG::VGG_48: + { + + #include "vgg_generated_48.i" + ini_params( PRrows, PRcols, PRidx, sizeof(PRidx)/sizeof(PRidx[0]), PR, + PJrows, PJcols, PJidx, sizeof(PJidx)/sizeof(PJidx[0]), PJ ); + } + break; + default: + CV_Error( Error::StsInternal, "Unknown Descriptor Type." ); + } + + // set desc size + m_descriptor_size = m_Proj.rows; +} + +// destructor +VGG_Impl::~VGG_Impl() +{ +} + +Ptr VGG::create( int desc, float isigma, bool img_normalize, bool use_scale_orientation, + float scale_factor, bool dsc_normalize ) +{ + return makePtr( desc, isigma, img_normalize, use_scale_orientation, scale_factor, dsc_normalize ); +} + + +} // END NAMESPACE XFEATURES2D +} // END NAMESPACE CV diff --git a/modules/xfeatures2d/test/test_features2d.cpp b/modules/xfeatures2d/test/test_features2d.cpp index 8ff50f0a8..7efb47d23 100644 --- a/modules/xfeatures2d/test/test_features2d.cpp +++ b/modules/xfeatures2d/test/test_features2d.cpp @@ -1050,6 +1050,12 @@ TEST( Features2d_DescriptorExtractor_LATCH, regression ) test.safe_run(); } +TEST( Features2d_DescriptorExtractor_VGG, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-vgg", 0.03f, + VGG::create() ); + test.safe_run(); +} /*#if CV_SSE2 diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 9cb5fc514..a0dfb9501 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -671,6 +671,42 @@ TEST(DISABLED_Features2d_RotationInvariance_Descriptor_DAISY, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_VGG120, regression) +{ + DescriptorRotationInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_120, 1.4f, true, true, 48.0f, false), + NORM_L1, + 1.00f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_VGG80, regression) +{ + DescriptorRotationInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_80, 1.4f, true, true, 48.0f, false), + NORM_L1, + 1.00f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_VGG64, regression) +{ + DescriptorRotationInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_64, 1.4f, true, true, 48.0f, false), + NORM_L1, + 1.00f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_VGG48, regression) +{ + DescriptorRotationInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_48, 1.4f, true, true, 48.0f, false), + NORM_L1, + 1.00f); + test.safe_run(); +} + TEST(Features2d_RotationInvariance_Descriptor_BRIEF_64, regression) { DescriptorRotationInvarianceTest test(SURF::create(), @@ -774,3 +810,39 @@ TEST(DISABLED_Features2d_ScaleInvariance_Descriptor_DAISY, regression) 0.075f); test.safe_run(); } + +TEST(Features2d_ScaleInvariance_Descriptor_VGG120, regression) +{ + DescriptorScaleInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_120, 1.4f, true, true, 48.0f, false), + NORM_L1, + 0.99f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_VGG80, regression) +{ + DescriptorScaleInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_80, 1.4f, true, true, 48.0f, false), + NORM_L1, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_VGG64, regression) +{ + DescriptorScaleInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_64, 1.4f, true, true, 48.0f, false), + NORM_L1, + 0.97f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_VGG48, regression) +{ + DescriptorScaleInvarianceTest test(KAZE::create(), + VGG::create(VGG::VGG_48, 1.4f, true, true, 48.0f, false), + NORM_L1, + 0.93f); + test.safe_run(); +}