From 9c1d01e22a8ca580501c44f1e44c1c04ea0855bd Mon Sep 17 00:00:00 2001 From: Balint Cristian Date: Thu, 27 Oct 2016 14:49:29 +0300 Subject: [PATCH] Add BoostDesc Descriptor. --- modules/xfeatures2d/CMakeLists.txt | 3 +- .../cmake/download_boostdesc.cmake | 30 + modules/xfeatures2d/cmake/download_vgg.cmake | 2 +- modules/xfeatures2d/doc/xfeatures2d.bib | 14 + .../include/opencv2/xfeatures2d.hpp | 41 + .../xfeatures2d/samples/export-boostdesc.py | 293 +++++++ modules/xfeatures2d/src/boostdesc.cpp | 731 ++++++++++++++++++ modules/xfeatures2d/src/vgg.cpp | 6 +- modules/xfeatures2d/test/test_features2d.cpp | 56 ++ .../test_rotation_and_scale_invariance.cpp | 126 +++ 10 files changed, 1297 insertions(+), 5 deletions(-) create mode 100644 modules/xfeatures2d/cmake/download_boostdesc.cmake create mode 100644 modules/xfeatures2d/samples/export-boostdesc.py create mode 100644 modules/xfeatures2d/src/boostdesc.cpp diff --git a/modules/xfeatures2d/CMakeLists.txt b/modules/xfeatures2d/CMakeLists.txt index cabb8a68e..f295bddae 100644 --- a/modules/xfeatures2d/CMakeLists.txt +++ b/modules/xfeatures2d/CMakeLists.txt @@ -1,4 +1,5 @@ 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 +include(cmake/download_vgg.cmake) +include(cmake/download_boostdesc.cmake) diff --git a/modules/xfeatures2d/cmake/download_boostdesc.cmake b/modules/xfeatures2d/cmake/download_boostdesc.cmake new file mode 100644 index 000000000..600cbd6b2 --- /dev/null +++ b/modules/xfeatures2d/cmake/download_boostdesc.cmake @@ -0,0 +1,30 @@ + +set(OPENCV_3RDPARTY_COMMIT "34e4206aef44d50e6bbcd0ab06354b52e7466d26") +set(FILE_HASH_BOOSTDESC_BGM "0ea90e7a8f3f7876d450e4149c97c74f") +set(FILE_HASH_BOOSTDESC_BGM_BI "232c966b13651bd0e46a1497b0852191") +set(FILE_HASH_BOOSTDESC_BGM_HD "324426a24fa56ad9c5b8e3e0b3e5303e") +set(FILE_HASH_BOOSTDESC_BINBOOST_064 "202e1b3e9fec871b04da31f7f016679f") +set(FILE_HASH_BOOSTDESC_BINBOOST_128 "98ea99d399965c03d555cef3ea502a0b") +set(FILE_HASH_BOOSTDESC_BINBOOST_256 "e6dcfa9f647779eb1ce446a8d759b6ea") +set(FILE_HASH_BOOSTDESC_LBGM "0ae0675534aa318d9668f2a179c2a052") + + + +set(BOOSTDESC_DOWNLOAD_URL ${OPENCV_CONTRIB_BOOSTDESC_URL};$ENV{OPENCV_CONTRIB_BOOSTDESC_URL};https://raw.githubusercontent.com/opencv/opencv_3rdparty/${OPENCV_3RDPARTY_COMMIT}/) + +function(boostdesc_download file id) + message(STATUS "Check contents of ${file} ...") + ocv_download(PACKAGE ${file} + HASH ${FILE_HASH_${id}} + URL ${BOOSTDESC_DOWNLOAD_URL} + DESTINATION_DIR ${CMAKE_CURRENT_LIST_DIR}/../src + DOWNLOAD_DIR ${CMAKE_CURRENT_LIST_DIR}/.download) +endfunction() + +boostdesc_download(boostdesc_bgm.i BOOSTDESC_BGM) +boostdesc_download(boostdesc_bgm_bi.i BOOSTDESC_BGM_BI) +boostdesc_download(boostdesc_bgm_hd.i BOOSTDESC_BGM_HD) +boostdesc_download(boostdesc_binboost_064.i BOOSTDESC_BINBOOST_064) +boostdesc_download(boostdesc_binboost_128.i BOOSTDESC_BINBOOST_128) +boostdesc_download(boostdesc_binboost_256.i BOOSTDESC_BINBOOST_256) +boostdesc_download(boostdesc_lbgm.i BOOSTDESC_LBGM) diff --git a/modules/xfeatures2d/cmake/download_vgg.cmake b/modules/xfeatures2d/cmake/download_vgg.cmake index 92c25d727..09187ab92 100644 --- a/modules/xfeatures2d/cmake/download_vgg.cmake +++ b/modules/xfeatures2d/cmake/download_vgg.cmake @@ -6,7 +6,7 @@ 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}/) +set(VGG_DOWNLOAD_URL ${OPENCV_CONTRIB_VGG_URL};$ENV{OPENCV_CONTRIB_VGG_URL};https://raw.githubusercontent.com/opencv/opencv_3rdparty/${OPENCV_3RDPARTY_COMMIT}/) function(vgg_download file id) message(STATUS "Check contents of ${file} ...") diff --git a/modules/xfeatures2d/doc/xfeatures2d.bib b/modules/xfeatures2d/doc/xfeatures2d.bib index 6e1d38840..774e04eb4 100644 --- a/modules/xfeatures2d/doc/xfeatures2d.bib +++ b/modules/xfeatures2d/doc/xfeatures2d.bib @@ -78,3 +78,17 @@ journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence", year = "2014" } + +@article{Trzcinski13b, + author = {T. Trzcinski, M. Christoudias and V. Lepetit}, + title = {{Learning Image Descriptors with Boosting}}, + journal = "submitted to IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI)", + year = {2013} +} + +@inproceedings{Trzcinski13a, + author = {T. Trzcinski, M. Christoudias, V. Lepetit and P. Fua}, + title = {{Boosting Binary Keypoint Descriptors}}, + booktitle = "Computer Vision and Pattern Recognition", + year = {2013} +} diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 4e8e69d1b..98e2eb661 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -318,6 +318,47 @@ public: }; +/** @brief Class implementing BoostDesc (Learning Image Descriptors with Boosting), described in +@cite Trzcinski13a and @cite Trzcinski13b. + +@param desc type of descriptor to use, BoostDesc::BINBOOST_256 is default (256 bit long dimension) +Available types are: BoostDesc::BGM, BoostDesc::BGM_HARD, BoostDesc::BGM_BILINEAR, BoostDesc::LBGM, +BoostDesc::BINBOOST_64, BoostDesc::BINBOOST_128, BoostDesc::BINBOOST_256 +@param use_orientation sample patterns using keypoints orientation, enabled by default +@param scale_factor adjust the sampling window of detected keypoints +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 +1.50f was the default in original implementation + +@note BGM is the base descriptor where each binary dimension is computed as the output of a single weak learner. +BGM_HARD and BGM_BILINEAR refers to same BGM but use different type of gradient binning. In the BGM_HARD that +use ASSIGN_HARD binning type the gradient is assigned to the nearest orientation bin. In the BGM_BILINEAR that use +ASSIGN_BILINEAR binning type the gradient is assigned to the two neighbouring bins. In the BGM and all other modes that use +ASSIGN_SOFT binning type the gradient is assigned to 8 nearest bins according to the cosine value between the gradient +angle and the bin center. LBGM (alias FP-Boost) is the floating point extension where each dimension is computed +as a linear combination of the weak learner responses. BINBOOST and subvariants are the binary extensions of LBGM +where each bit is computed as a thresholded linear combination of a set of weak learners. +BoostDesc header files (boostdesc_*.i) was exported from original binaries with export-boostdesc.py script from +samples subfolder. + +*/ + +class CV_EXPORTS_W BoostDesc : public Feature2D +{ +public: + + CV_WRAP enum + { + BGM = 100, BGM_HARD = 101, BGM_BILINEAR = 102, LBGM = 200, + BINBOOST_64 = 300, BINBOOST_128 = 301, BINBOOST_256 = 302 + }; + + CV_WRAP static Ptr create( int desc = BoostDesc::BINBOOST_256, + bool use_scale_orientation = true, float scale_factor = 6.25f ); +}; + //! @} } diff --git a/modules/xfeatures2d/samples/export-boostdesc.py b/modules/xfeatures2d/samples/export-boostdesc.py new file mode 100644 index 000000000..0d89c8127 --- /dev/null +++ b/modules/xfeatures2d/samples/export-boostdesc.py @@ -0,0 +1,293 @@ +#!/usr/bin/python + +""" + +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2016 + * + * Balint Cristian + * + * + * 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. + *********************************************************************/ + +/* export-boostdesc.py */ +/* Export C headers from binary data */ +// [http://infoscience.epfl.ch/record/186246/files/boostDesc_1.0.tar.gz] + +""" + +import sys +import struct + + + +def float_to_hex(f): + return struct.unpack( ' " % sys.argv[0] ) + sys.exit(0) + + + if ( ( sys.argv[1] != "BGM" ) and + ( sys.argv[1] != "LBGM" ) and + ( sys.argv[1] != "BINBOOST" ) ): + print( "Invalid type [%s]" % sys.argv[1] ) + sys.exit(0) + + # enum literals + Assign = [ "ASSIGN_HARD", + "ASSIGN_BILINEAR", + "ASSIGN_SOFT", + "ASSIGN_HARD_MAGN", + "ASSIGN_SOFT_MAGN" ] + + # open binary data file + f = open( sys.argv[2], 'rb' ) + + # header + print "/*" + print " *" + print " * Header exported from binary." + print " * [%s %s %s]" % ( sys.argv[0], sys.argv[1], sys.argv[2] ) + print " *" + print " */" + + # ini + nDim = 1; + nWLs = 0; + + # dimensionality (where is the case) + if ( ( sys.argv[1] == "LBGM" ) or + ( sys.argv[1] == "BINBOOST" ) ): + nDim = struct.unpack( ' + * Mario Christoudias + * Vincent Lepetit + * + * + * 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 Image Descriptors with Boosting" + T. Trzcinski, M. Christoudias and V. Lepetit + IEEE Transactions on Pattern Analysis and Machine Intelligence, 2013 + + "Boosting Binary Keypoint Descriptors" + T. Trzcinski, M. Christoudias, V. Lepetit and P. Fua + Computer Vision and Pattern Recognition (CVPR), 2013 + + Original code: Tomasz Trzcinski + OpenCV port by: Cristian Balint + + */ + +#include +#include "precomp.hpp" + + + +using namespace cv; +using namespace std; + + +namespace cv +{ +namespace xfeatures2d +{ + +/* + !BoostDesc implementation + */ +class BoostDesc_Impl : public BoostDesc +{ + +public: + + // constructor + explicit BoostDesc_Impl( int desc = BINBOOST_256, + bool use_scale_orientation = true, + float scale_factor = 6.25f ); + + // destructor + virtual ~BoostDesc_Impl(); + + // returns the descriptor length in bytes + virtual int descriptorSize() const { return m_descriptor_size; } + + // returns the descriptor type + virtual int descriptorType() const { return m_descriptor_type; } + + // returns the default norm type + virtual int defaultNorm() const { return m_descriptor_norm; } + + // compute descriptors given keypoints + virtual void compute( InputArray image, vector& keypoints, OutputArray descriptors ); + +protected: + + /* + * BoostDesc parameters + */ + + // size, type, norm + int m_descriptor_size; + int m_descriptor_type; + int m_descriptor_norm; + + // desc type + int m_desc_type; + + // gradient + // assignment types + enum Assign + { + ASSIGN_HARD = 0, + ASSIGN_BILINEAR = 1, + ASSIGN_SOFT = 2, + ASSIGN_HARD_MAGN = 3, + ASSIGN_SOFT_MAGN = 4 + }; + + // dims + int m_Dims; + + // no. weak + // learners + int m_nWLs; + + // gradient type + int m_grad_atype; + + // patch size + int m_patch_size; + + // orient quantitiy + int m_orient_q; + + // patch scale factor + float m_scale_factor; + + /* + * BoostDesc switches + */ + + // switch to enable sample by keypoints orientation + bool m_use_scale_orientation; + + + /* + * BoostDesc arrays + */ + + // image + Mat m_image; + + // parameters + // weak learner + Mat m_wl_thresh; + Mat m_wl_orient; + Mat m_wl_x_min, m_wl_x_max; + Mat m_wl_y_min, m_wl_y_max; + Mat m_wl_alpha, m_wl_beta; + +private: + + /* + * BoostDesc functions + */ + + // initialize parameters + inline void ini_params( const int orientQuant, const int patchSize, + const int iGradAssignType, + const int nDim, const int nWLs, + const unsigned int thresh[], const int orient[], + const int x_min[], const int x_max[], + const int y_min[], const int y_max[], + const unsigned int alpha[], const unsigned int beta[] ); + +}; // END BoostDesc_Impl CLASS + +// ------------------------------------------------- +/* BoostDesc internal routines */ + +static void computeGradientMaps( const Mat& im, + const int gradAssignType, + const int orientQuant, + vector& gradMap ) +{ + enum Assign + { + ASSIGN_HARD = 0, + ASSIGN_BILINEAR = 1, + ASSIGN_SOFT = 2, + ASSIGN_HARD_MAGN = 3, + ASSIGN_SOFT_MAGN = 4 + }; + + Mat derivx( im.size(), CV_32FC1 ); + Mat derivy( im.size(), CV_32FC1 ); + + Sobel( im, derivx, derivx.depth(), 1, 0 ); + Sobel( im, derivy, derivy.depth(), 0, 1 ); + + for ( int i = 0; i < orientQuant; i++ ) + gradMap.push_back( Mat::zeros( im.size(), CV_8UC1 ) ); + + int index, index2; + double binCenter, weight; + double binSize = (2 * CV_PI) / orientQuant; + + // fill in temp matrices with + // respones to edge detection + const float* pDerivx = derivx.ptr(); + const float* pDerivy = derivy.ptr(); + + for ( int i = 0; i < im.rows; i++ ) + { + for ( int j = 0; j < im.cols; j++ ) + { + float gradMagnitude = sqrt( (*pDerivx) * (*pDerivx) + + (*pDerivy) * (*pDerivy) ); + if ( gradMagnitude > 20 ) + { + double theta = atan2( *pDerivy, *pDerivx ); + theta = ( theta < 0 ) ? theta + 2*CV_PI : theta; + index = int( theta / binSize ); + index = ( index == orientQuant ) ? 0 : index; + + switch ( gradAssignType ) + { + case ASSIGN_HARD: + gradMap[index].at(i,j) = 1; + break; + + case ASSIGN_HARD_MAGN: + gradMap[index].at(i,j) = (uchar) round( gradMagnitude ); + break; + + case ASSIGN_BILINEAR: + index2 = (int) ceil( theta / binSize ); + index2 = ( index2 == orientQuant ) ? 0 : index2; + binCenter = ( index + 0.5f ) * binSize; + weight = 1 - abs( theta - binCenter ) / binSize; + gradMap[index ].at(i,j) = (uchar) round( 255 * weight ); + gradMap[index2].at(i,j) = (uchar) round( 255 * ( 1 - weight ) ); + break; + + case ASSIGN_SOFT: + for ( int binNum = 0; binNum < orientQuant/2 + 1; binNum++ ) + { + index2 = ( binNum + index + orientQuant - orientQuant/4 ) % orientQuant; + binCenter = ( index2 + 0.5f ) * binSize; + weight = cos( theta - binCenter ); + weight = ( weight < 0 ) ? 0 : weight; + gradMap[index2].at(i,j) = (uchar) round( 255 * weight ); + } + break; + + case ASSIGN_SOFT_MAGN: + for ( int binNum = 0; binNum < orientQuant/2 + 1; binNum++ ) + { + index2 = ( binNum + index + orientQuant - orientQuant/4 ) % orientQuant; + binCenter = ( index2 + 0.5f ) * binSize; + weight = cos( theta - binCenter ); + weight = ( weight < 0 ) ? 0 : weight; + gradMap[index2].at(i,j) = (uchar) round( gradMagnitude * weight ); + } + break; + } // end switch + } + ++pDerivy; + ++pDerivx; + } + } +} + +static void computeIntegrals( const vector& gradMap, + const int orientQuant, + vector& integralMap ) +{ + // init integral images + int rows = gradMap[0].rows; + int cols = gradMap[0].cols; + + for ( int i = 0; i < orientQuant+1; i++ ) + integralMap.push_back( Mat::zeros( rows+1, cols+1, CV_8UC1 ) ); + + // generate corresponding integral images + for( int i = 0; i < orientQuant; i++ ) + integral( gradMap[i], integralMap[i] ); + + // copy the values from the first quantization bin + integralMap[0].copyTo( integralMap[orientQuant] ); + + int* ptrSum, *ptr; + for ( int k = 1; k < orientQuant; k++ ) + { + ptr = (int*) integralMap[k].ptr(); + ptrSum = (int*) integralMap[orientQuant].ptr(); + for (int i=0; i<(rows+1)*(cols+1); ++i) + { + *ptrSum += *ptr; + ++ptrSum; + ++ptr; + } + } +} + +static float computeWLResponse( const int x_min, const int x_max, + const int y_min, const int y_max, + const int orient, const float thresh, + const int orientQuant, + const vector& integralMap ) +{ + const int width = integralMap[0].cols; + + const int idx1 = (y_min ) * width + x_min; + const int idx2 = (y_min ) * width + x_max + 1; + const int idx3 = (y_max + 1) * width + x_min; + const int idx4 = (y_max + 1) * width + x_max + 1; + + const int* ptr = integralMap[orient].ptr(); + + int A, B ,C, D; + A = ptr[idx1]; B = ptr[idx2]; + C = ptr[idx3]; D = ptr[idx4]; + + const float current = float(D + A - B - C); + + ptr = integralMap[orientQuant].ptr(); + + A = ptr[idx1]; B = ptr[idx2]; + C = ptr[idx3]; D = ptr[idx4]; + + const float total = float(D + A - B - C); + + return total ? ( (current / total) - thresh ) : 0.f; +} + +static void rectifyPatch( const Mat& image, const KeyPoint& kp, + const int& patchSize, Mat& patch, + const bool use_scale_orientation, + const float scale_factor ) +{ + Mat M; + if ( use_scale_orientation ) + { + const float s = scale_factor * (float) kp.size / (float) patchSize; + + const float cosine = (kp.angle>=0) ? cos(kp.angle*(float)CV_PI/180.0f) : 1.f; + const float sine = (kp.angle>=0) ? sin(kp.angle*(float)CV_PI/180.0f) : 0.f; + + float M_[] = { + s*cosine, -s*sine, (-s*cosine + s*sine ) * patchSize/2.0f + kp.pt.x, + s*sine, s*cosine, (-s*sine - s*cosine) * patchSize/2.0f + kp.pt.y + }; + M = Mat( 2, 3, CV_32FC1, M_ ).clone(); + } + else + { + float M_[] = { + 1.f, 0.f, -1.f * patchSize/2.0f + kp.pt.x, + 0.f, 1.f, -1.f * patchSize/2.0f + kp.pt.y + }; + M = Mat( 2, 3, CV_32FC1, M_ ).clone(); + } + + warpAffine( image, patch, M, Size( patchSize, patchSize ), + WARP_INVERSE_MAP + INTER_CUBIC + WARP_FILL_OUTLIERS ); +} + +// ------------------------------------------------- +/* BoostDesc interface implementation */ + +struct ComputeBoostDescInvoker : ParallelLoopBody +{ + ComputeBoostDescInvoker( const Mat& _image, Mat* _descriptors, + const vector& _keypoints, + const int _desc_type, const int _grad_atype, + const int _orient_q, const int _patch_size, + const int _nWLs, const int _Dims, + const Mat& _wl_x_min, const Mat& _wl_x_max, + const Mat& _wl_y_min, const Mat& _wl_y_max, + const Mat& _wl_thresh, const Mat& _wl_orient, + const Mat& _wl_alpha, const Mat& _wl_beta, + const bool _use_scale_orientation, + const float _scale_factor ) + { + nWLs = _nWLs; + Dims = _Dims; + image = _image; + orient_q = _orient_q; + desc_type = _desc_type; + keypoints = _keypoints; + grad_atype = _grad_atype; + patch_size = _patch_size; + descriptors = _descriptors; + + wl_beta = _wl_beta; + wl_alpha = _wl_alpha; + wl_x_min = _wl_x_min; + wl_x_max = _wl_x_max; + wl_y_min = _wl_y_min; + wl_y_max = _wl_y_max; + wl_thresh = _wl_thresh; + wl_orient = _wl_orient; + + scale_factor = _scale_factor; + use_scale_orientation = _use_scale_orientation; + } + + void operator ()( const cv::Range& range ) const + { + // maps + vector gradMap, integralMap; + + // small binary map + uchar binLookUp[8]; + for ( unsigned int i = 0; i < 8; i++ ) + binLookUp[i] = (uchar) 1 << i; + + for ( int i = range.start; i < range.end; i++ ) + { + + Mat patch; + // rectify the patch around a given keypoint + rectifyPatch( image, keypoints[i], patch_size, + patch, use_scale_orientation, scale_factor ); + + // compute gradient maps (and integral gradient maps) + computeGradientMaps( patch, grad_atype, orient_q, gradMap ); + computeIntegrals( gradMap, orient_q, integralMap ); + + float WLR; + + /* + * BGM + */ + if ( ( desc_type == BGM ) || + ( desc_type == BGM_HARD ) || + ( desc_type == BGM_BILINEAR ) + ) + { + uchar* desc = descriptors->ptr(i); + for ( int j = 0; j < nWLs; j++ ) + { + WLR = computeWLResponse( wl_x_min.at(0,j), wl_x_max.at(0,j), + wl_y_min.at(0,j), wl_y_max.at(0,j), + wl_orient.at(0,j), wl_thresh.at(0,j), + orient_q, integralMap ); + desc[j/8] |= ( WLR >= 0 ) ? binLookUp[ j % 8 ] : 0; + } + } // end BGM + + /* + * LBGM + */ + if ( desc_type == LBGM ) + { + std::bitset<512> wlResponses; + + for ( int j = 0; j < nWLs; j++ ) + { + WLR = computeWLResponse( wl_x_min.at(0,j), wl_x_max.at(0,j), + wl_y_min.at(0,j), wl_y_max.at(0,j), + wl_orient.at(0,j), wl_thresh.at(0,j), + orient_q, integralMap ); + wlResponses[j] = ( WLR >= 0 ) ? 1 : 0; + } + + float* desc = descriptors->ptr(i); + for ( int d = 0; d < Dims; d++ ) + { + for ( int wl = 0; wl < nWLs; wl++ ) + { + desc[d] += ( wlResponses[wl] ) ? wl_beta.at(wl,d) : -wl_beta.at(wl,d); + } + } + } // end LBGM + + /* + * BINBOOST + */ + if ( ( desc_type == BINBOOST_64 ) || + ( desc_type == BINBOOST_128 ) || + ( desc_type == BINBOOST_256 ) + ) + { + float resp; + for ( int d = 0; d < Dims; d++ ) + { + resp = 0; + uchar* desc = descriptors->ptr(i); + for ( int wl = 0; wl < nWLs; wl++ ) + { + WLR = computeWLResponse( wl_x_min.at(d,wl), wl_x_max.at(d,wl), + wl_y_min.at(d,wl), wl_y_max.at(d,wl), + wl_orient.at(d,wl), wl_thresh.at(d,wl), + orient_q, integralMap ); + resp += ( WLR >= 0 ) ? wl_beta.at(d,wl) : -wl_beta.at(d,wl); + } + desc[d/8] |= ( resp >= 0 ) ? binLookUp[d%8] : 0; + } + } // end BINBOOST + + // clean-up + patch.release(); + gradMap.clear(); + integralMap.clear(); + + } // end for loop + } // end operator + + int nWLs; + int Dims; + int orient_q; + int desc_type; + int patch_size; + int grad_atype; + int patch_szie; + + Mat image; + Mat *descriptors; + vector keypoints; + + Mat wl_x_min, wl_x_max, wl_y_min, wl_y_max; + Mat wl_thresh, wl_orient, wl_alpha, wl_beta; + + float scale_factor; + bool use_scale_orientation; + + enum + { + BGM = 100, BGM_HARD = 101, BGM_BILINEAR = 102, LBGM = 200, + BINBOOST_64 = 300, BINBOOST_128 = 301, BINBOOST_256 = 302 + }; +}; + +// descriptor computation using keypoints +void BoostDesc_Impl::compute( InputArray _image, vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + if( keypoints.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 ); + + // initialize the variables + _descriptors.create( (int)keypoints.size(), descriptorSize(), descriptorType() ); + _descriptors.setTo( Scalar::all(0) ); + + // descriptor storage + Mat descriptors = _descriptors.getMat(); + + parallel_for_( Range( 0, (int) keypoints.size() ), + ComputeBoostDescInvoker( m_image, &descriptors, keypoints, + m_desc_type, m_grad_atype, m_orient_q, + m_patch_size, m_nWLs, m_Dims, + m_wl_x_min, m_wl_x_max, m_wl_y_min, m_wl_y_max, + m_wl_thresh, m_wl_orient, m_wl_alpha, m_wl_beta, + m_use_scale_orientation, m_scale_factor ) + ); +} + +void BoostDesc_Impl::ini_params( const int orientQuant, const int patchSize, + const int iGradAssignType, + const int nDim, const int nWLs, + const unsigned int thresh[], const int orient[], + const int x_min[], const int x_max[], + const int y_min[], const int y_max[], + const unsigned int alpha[], const unsigned int beta[] ) +{ + // desc type, norm, size + if ( m_desc_type == LBGM ) + { + m_descriptor_size = nDim; + m_descriptor_norm = NORM_L2; + m_descriptor_type = CV_32FC1; + } + else + { + if ( ( m_desc_type == BGM ) || + ( m_desc_type == BGM_HARD ) || + ( m_desc_type == BGM_BILINEAR ) + ) + m_descriptor_size = nWLs / 8; + else + m_descriptor_size = nDim / 8; + + m_descriptor_type = CV_8UC1; + m_descriptor_norm = NORM_HAMMING; + } + + // 2d array dim + int dim0 = nDim; + int dim1 = nWLs; + + // override beta dim0 on LBGM + if ( m_desc_type == LBGM ) dim0 = 1; + + m_Dims = nDim; + m_nWLs = nWLs; + m_orient_q = orientQuant; + m_patch_size = patchSize; + m_grad_atype = iGradAssignType; + + // cast into opencv Mat type as float + m_wl_thresh = Mat( dim0, dim1, CV_32F, reinterpret_cast(const_cast(thresh)) ); + m_wl_alpha = Mat( dim0, dim1, CV_32F, reinterpret_cast(const_cast(alpha )) ); + + // cast into opencv Mat type as integer + m_wl_orient = Mat( dim0, dim1, CV_32S, const_cast(orient) ); + m_wl_x_min = Mat( dim0, dim1, CV_32S, const_cast(x_min ) ); + m_wl_x_max = Mat( dim0, dim1, CV_32S, const_cast(x_max ) ); + m_wl_y_min = Mat( dim0, dim1, CV_32S, const_cast(y_min ) ); + m_wl_y_max = Mat( dim0, dim1, CV_32S, const_cast(y_max ) ); + + // no beta + if ( beta == NULL ) return; + + if ( m_desc_type == LBGM ) + m_wl_beta = Mat( dim1, nDim, CV_32F, reinterpret_cast(const_cast(beta)) ); + else + m_wl_beta = Mat( dim0, dim1, CV_32F, reinterpret_cast(const_cast(beta)) ); +} + +// constructor +BoostDesc_Impl::BoostDesc_Impl( int _desc, bool _use_scale_orientation, float _scale_factor ) + : m_desc_type( _desc ), m_scale_factor( _scale_factor ), + m_use_scale_orientation( _use_scale_orientation ) +{ + // desc type + switch ( m_desc_type ) + { + case BGM: + { + #include "boostdesc_bgm.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, NULL ); + } + break; + case BGM_HARD: + { + #include "boostdesc_bgm_hd.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, NULL ); + } + break; + case BGM_BILINEAR: + { + #include "boostdesc_bgm_bi.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, NULL ); + } + break; + case LBGM: + { + #include "boostdesc_lbgm.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, beta ); + } + break; + case BINBOOST_64: + { + #include "boostdesc_binboost_064.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, beta ); + } + break; + case BINBOOST_128: + { + #include "boostdesc_binboost_128.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, beta ); + } + break; + case BINBOOST_256: + { + #include "boostdesc_binboost_256.i" + ini_params( orientQuant, patchSize, + iGradAssignType, + nDim, nWLs, thresh, orient, + x_min, x_max, y_min, y_max, + alpha, beta ); + } + break; + default: + CV_Error( Error::StsInternal, "Unknown Descriptor Type." ); + } +} + +// destructor +BoostDesc_Impl::~BoostDesc_Impl() +{ +} + +Ptr BoostDesc::create( int desc, bool use_scale_orientation, float scale_factor ) +{ + return makePtr( desc, use_scale_orientation, scale_factor ); +} + + +} // END NAMESPACE XFEATURES2D +} // END NAMESPACE CV diff --git a/modules/xfeatures2d/src/vgg.cpp b/modules/xfeatures2d/src/vgg.cpp index 6f91be45e..f42bd321a 100644 --- a/modules/xfeatures2d/src/vgg.cpp +++ b/modules/xfeatures2d/src/vgg.cpp @@ -315,9 +315,9 @@ static void get_desc( const Mat Patch, Mat& PatchTrans, int anglebins, bool img_ // ------------------------------------------------- /* VGG interface implementation */ -struct ComputeDescInvoker : ParallelLoopBody +struct ComputeVGGInvoker : ParallelLoopBody { - ComputeDescInvoker( const Mat& _image, Mat* _descriptors, + ComputeVGGInvoker( const Mat& _image, Mat* _descriptors, const vector& _keypoints, const Mat& _PRFilters, const Mat& _Proj, const int _anglebins, const bool _img_normalize, @@ -403,7 +403,7 @@ void VGG_Impl::compute( InputArray _image, vector& keypoints, OutputAr descriptors.setTo( Scalar(0) ); parallel_for_( Range( 0, (int) keypoints.size() ), - ComputeDescInvoker( m_image, &descriptors, keypoints, m_PRFilters, m_Proj, + ComputeVGGInvoker( m_image, &descriptors, keypoints, m_PRFilters, m_Proj, m_anglebins, m_img_normalize, m_use_scale_orientation, m_scale_factor ) ); diff --git a/modules/xfeatures2d/test/test_features2d.cpp b/modules/xfeatures2d/test/test_features2d.cpp index 7efb47d23..d42218cc4 100644 --- a/modules/xfeatures2d/test/test_features2d.cpp +++ b/modules/xfeatures2d/test/test_features2d.cpp @@ -1057,6 +1057,62 @@ TEST( Features2d_DescriptorExtractor_VGG, regression ) test.safe_run(); } +TEST( Features2d_DescriptorExtractor_BGM, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-bgm", + (CV_DescriptorExtractorTest::DistanceType)12.f, + BoostDesc::create(BoostDesc::BGM) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BGM_HARD, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-bgm_hard", + (CV_DescriptorExtractorTest::DistanceType)12.f, + BoostDesc::create(BoostDesc::BGM_HARD) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BGM_BILINEAR, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-bgm_bilinear", + (CV_DescriptorExtractorTest::DistanceType)15.f, + BoostDesc::create(BoostDesc::BGM_BILINEAR) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_LBGM, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-boostdesc-lbgm", + 1.0f, + BoostDesc::create(BoostDesc::LBGM) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BINBOOST_64, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-binboost_64", + (CV_DescriptorExtractorTest::DistanceType)12.f, + BoostDesc::create(BoostDesc::BINBOOST_64) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BINBOOST_128, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-binboost_128", + (CV_DescriptorExtractorTest::DistanceType)12.f, + BoostDesc::create(BoostDesc::BINBOOST_128) ); + test.safe_run(); +} + +TEST( Features2d_DescriptorExtractor_BINBOOST_256, regression ) +{ + CV_DescriptorExtractorTest test( "descriptor-boostdesc-binboost_256", + (CV_DescriptorExtractorTest::DistanceType)12.f, + BoostDesc::create(BoostDesc::BINBOOST_256) ); + test.safe_run(); +} + /*#if CV_SSE2 TEST( Features2d_DescriptorExtractor_Calonder_uchar, regression ) diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index a0dfb9501..caa1de2ea 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -744,6 +744,69 @@ TEST(Features2d_RotationInvariance_Descriptor_FREAK, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BGM, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM,true,6.25f), + NORM_HAMMING, + 0.999f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BGM_HARD, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM_HARD,true,6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BGM_BILINEAR, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM_BILINEAR,true,6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_LBGM, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::LBGM,true,6.25f), + NORM_L1, + 0.999f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BINBOOST_64, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_64,true,6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BINBOOST_128, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_128,true,6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_RotationInvariance_Descriptor_BoostDesc_BINBOOST_256, regression) +{ + DescriptorRotationInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_256,true,6.25f), + NORM_HAMMING, + 0.999f); + test.safe_run(); +} + /* * Detector's scale invariance check */ @@ -846,3 +909,66 @@ TEST(Features2d_ScaleInvariance_Descriptor_VGG48, regression) 0.93f); test.safe_run(); } + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BGM, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM, true, 6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BGM_HARD, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM_HARD, true, 6.25f), + NORM_HAMMING, + 0.75f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BGM_BILINEAR, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BGM_BILINEAR, true, 6.25f), + NORM_HAMMING, + 0.95f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_LBGM, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::LBGM, true, 6.25f), + NORM_L1, + 0.98f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BINBOOST_64, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_64, true, 6.25f), + NORM_HAMMING, + 0.75f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BINBOOST_128, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_128, true, 6.25f), + NORM_HAMMING, + 0.95f); + test.safe_run(); +} + +TEST(Features2d_ScaleInvariance_Descriptor_BoostDesc_BINBOOST_256, regression) +{ + DescriptorScaleInvarianceTest test(SURF::create(), + BoostDesc::create(BoostDesc::BINBOOST_256, true, 6.25f), + NORM_HAMMING, + 0.98f); + test.safe_run(); +}