Commit 9c1d01e2 authored by Balint Cristian's avatar Balint Cristian

Add BoostDesc Descriptor.

parent 2a637461
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)
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)
......@@ -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} ...")
......
......@@ -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}
}
......@@ -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<BoostDesc> create( int desc = BoostDesc::BINBOOST_256,
bool use_scale_orientation = true, float scale_factor = 6.25f );
};
//! @}
}
......
#!/usr/bin/python
"""
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2016
*
* Balint Cristian <cristian dot balint at gmail dot com>
*
*
* 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( '<I', struct.pack('<f', f) )[0]
def main():
# usage
if ( len(sys.argv) < 3 ):
print( "Usage: %s <binary-type (BGM, LBGM, BINBOOST)> <boostdesc-binary-filename>" % 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( '<i', f.read(4) )[0]
print
print "// dimensionality of learner"
print "static const int nDim = %i;" % nDim
# week learners (where is the case)
if ( sys.argv[1] != "BINBOOST" ):
nWLs = struct.unpack( '<i', f.read(4) )[0]
# common header
orientQuant = struct.unpack( '<i', f.read(4) )[0]
patchSize = struct.unpack( '<i', f.read(4) )[0]
iGradAssignType = struct.unpack( '<i', f.read(4) )[0]
print
print "// orientations"
print "static const int orientQuant = %i;" % orientQuant
print
print "// patch size"
print "static const int patchSize = %i;" % patchSize
print
print "// gradient assignment type"
print "static const int iGradAssignType = %s;" % Assign[iGradAssignType]
arr_thresh = ""
arr_orient = ""
arr__y_min = ""
arr__y_max = ""
arr__x_min = ""
arr__x_max = ""
arr__alpha = ""
arr___beta = ""
dims = nDim
if ( sys.argv[1] == "LBGM" ):
dims = 1
# iterate each dimension
for d in range( 0, dims ):
if ( sys.argv[1] == "BINBOOST" ):
nWLs = struct.unpack( '<i', f.read(4) )[0]
if ( d == 0 ):
print
print "// number of weak learners"
print "static const int nWLs = %i;" % nWLs
# iterate each members
for i in range( 0, nWLs ):
# unpack structure array
thresh = struct.unpack( '<f', f.read(4) )[0]
orient = struct.unpack( '<i', f.read(4) )[0]
y_min = struct.unpack( '<i', f.read(4) )[0]
y_max = struct.unpack( '<i', f.read(4) )[0]
x_min = struct.unpack( '<i', f.read(4) )[0]
x_max = struct.unpack( '<i', f.read(4) )[0]
alpha = struct.unpack( '<f', f.read(4) )[0]
beta = 0
if ( sys.argv[1] == "BINBOOST" ):
beta = struct.unpack( '<f', f.read(4) )[0]
# first entry
if ( d*dims + i == 0 ):
arr_thresh += "\n"
arr_thresh += "// threshold array (%s x %s)\n" % (dims,nWLs)
arr_thresh += "static const unsigned int thresh[] =\n{\n"
arr_orient += "\n"
arr_orient += "// orientation array (%s x %s)\n" % (dims,nWLs)
arr_orient += "static const int orient[] =\n{\n"
arr__y_min += "\n"
arr__y_min += "// Y min array (%s x %s)\n" % (dims,nWLs)
arr__y_min += "static const int y_min[] =\n{\n"
arr__y_max += "\n"
arr__y_max += "// Y max array (%s x %s)\n" % (dims,nWLs)
arr__y_max += "static const int y_max[] =\n{\n"
arr__x_min += "\n"
arr__x_min += "// X min array (%s x %s)\n" % (dims,nWLs)
arr__x_min += "static const int x_min[] =\n{\n"
arr__x_max += "\n"
arr__x_max += "// X max array (%s x %s)\n" % (dims,nWLs)
arr__x_max += "static const int x_max[] =\n{\n"
arr__alpha += "\n"
arr__alpha += "// alpha array (%s x %s)\n" % (dims,nWLs)
arr__alpha += "static const unsigned int alpha[] =\n{\n"
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += "\n"
arr___beta += "// beta array (%s x %s)\n" % (dims,nWLs)
arr___beta += "static const unsigned int beta[] =\n{\n"
# last entry
if ( i == nWLs - 1 ) and ( d == dims - 1):
arr_thresh += " 0x%08x\n};" % float_to_hex(thresh)
arr_orient += " 0x%02x\n};" % orient
arr__y_min += " 0x%02x\n};" % y_min
arr__y_max += " 0x%02x\n};" % y_max
arr__x_min += " 0x%02x\n};" % x_min
arr__x_max += " 0x%02x\n};" % x_max
arr__alpha += " 0x%08x\n};" % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x\n};" % float_to_hex(beta)
break
# align entries
if ( (d*dims + i + 1) % 8 ):
arr_thresh += " 0x%08x," % float_to_hex(thresh)
arr_orient += " 0x%02x," % orient
arr__y_min += " 0x%02x," % y_min
arr__y_max += " 0x%02x," % y_max
arr__x_min += " 0x%02x," % x_min
arr__x_max += " 0x%02x," % x_max
arr__alpha += " 0x%08x," % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x," % float_to_hex(beta)
else:
arr_thresh += " 0x%08x,\n" % float_to_hex(thresh)
arr_orient += " 0x%02x,\n" % orient
arr__y_min += " 0x%02x,\n" % y_min
arr__y_max += " 0x%02x,\n" % y_max
arr__x_min += " 0x%02x,\n" % x_min
arr__x_max += " 0x%02x,\n" % x_max
arr__alpha += " 0x%08x,\n" % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x,\n" % float_to_hex(beta)
# extra array (when LBGM)
if ( sys.argv[1] == "LBGM" ):
arr___beta += "\n"
arr___beta += "// beta array (%s x %s)\n" % (nWLs,nDim)
arr___beta += "static const unsigned int beta[] =\n{\n"
for i in range( 0, nWLs ):
for d in range( 0, nDim ):
beta = struct.unpack( '<f', f.read(4) )[0]
# last entry
if ( i == nWLs-1 ) and ( d == nDim-1 ):
arr___beta += " 0x%08x\n};" % float_to_hex(beta)
break
# align entries
if ( (i*nDim + d + 1) % 8 ):
arr___beta += " 0x%08x," % float_to_hex(beta)
else:
arr___beta += " 0x%08x,\n" % float_to_hex(beta)
# release
f.close()
# dump on screen
print arr_thresh
print arr_orient
print arr__y_min
print arr__y_max
print arr__x_min
print arr__x_max
print arr__alpha
if ( ( sys.argv[1] == "LBGM" ) or
( sys.argv[1] == "BINBOOST" ) ):
print arr___beta
if __name__ == "__main__":
main()
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2013, 2016
*
* Tomasz Trzcinski <t dot trzcinski at ii dot pw dot edu dot pl>
* Mario Christoudias <mariochristoudias at gmail dot com>
* Vincent Lepetit <lepetit at icg dot tugraz dot at>
*
*
* 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 <t dot trzcinski at ii dot pw dot edu dot pl>
OpenCV port by: Cristian Balint <cristian dot balint at gmail dot com>
*/
#include <bitset>
#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<KeyPoint>& 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<Mat>& 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<float>();
const float* pDerivy = derivy.ptr<float>();
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<uchar>(i,j) = 1;
break;
case ASSIGN_HARD_MAGN:
gradMap[index].at<uchar>(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<uchar>(i,j) = (uchar) round( 255 * weight );
gradMap[index2].at<uchar>(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<uchar>(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<uchar>(i,j) = (uchar) round( gradMagnitude * weight );
}
break;
} // end switch
}
++pDerivy;
++pDerivx;
}
}
}
static void computeIntegrals( const vector<Mat>& gradMap,
const int orientQuant,
vector<Mat>& 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<int>();
ptrSum = (int*) integralMap[orientQuant].ptr<int>();
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<Mat>& 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>();
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<int>();
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<KeyPoint>& _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<Mat> 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<uchar>(i);
for ( int j = 0; j < nWLs; j++ )
{
WLR = computeWLResponse( wl_x_min.at<int>(0,j), wl_x_max.at<int>(0,j),
wl_y_min.at<int>(0,j), wl_y_max.at<int>(0,j),
wl_orient.at<int>(0,j), wl_thresh.at<float>(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<int>(0,j), wl_x_max.at<int>(0,j),
wl_y_min.at<int>(0,j), wl_y_max.at<int>(0,j),
wl_orient.at<int>(0,j), wl_thresh.at<float>(0,j),
orient_q, integralMap );
wlResponses[j] = ( WLR >= 0 ) ? 1 : 0;
}
float* desc = descriptors->ptr<float>(i);
for ( int d = 0; d < Dims; d++ )
{
for ( int wl = 0; wl < nWLs; wl++ )
{
desc[d] += ( wlResponses[wl] ) ? wl_beta.at<float>(wl,d) : -wl_beta.at<float>(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<uchar>(i);
for ( int wl = 0; wl < nWLs; wl++ )
{
WLR = computeWLResponse( wl_x_min.at<int>(d,wl), wl_x_max.at<int>(d,wl),
wl_y_min.at<int>(d,wl), wl_y_max.at<int>(d,wl),
wl_orient.at<int>(d,wl), wl_thresh.at<float>(d,wl),
orient_q, integralMap );
resp += ( WLR >= 0 ) ? wl_beta.at<float>(d,wl) : -wl_beta.at<float>(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<KeyPoint> 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<KeyPoint>& 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<float *>(const_cast<unsigned int *>(thresh)) );
m_wl_alpha = Mat( dim0, dim1, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(alpha )) );
// cast into opencv Mat type as integer
m_wl_orient = Mat( dim0, dim1, CV_32S, const_cast<int *>(orient) );
m_wl_x_min = Mat( dim0, dim1, CV_32S, const_cast<int *>(x_min ) );
m_wl_x_max = Mat( dim0, dim1, CV_32S, const_cast<int *>(x_max ) );
m_wl_y_min = Mat( dim0, dim1, CV_32S, const_cast<int *>(y_min ) );
m_wl_y_max = Mat( dim0, dim1, CV_32S, const_cast<int *>(y_max ) );
// no beta
if ( beta == NULL ) return;
if ( m_desc_type == LBGM )
m_wl_beta = Mat( dim1, nDim, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(beta)) );
else
m_wl_beta = Mat( dim0, dim1, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(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> BoostDesc::create( int desc, bool use_scale_orientation, float scale_factor )
{
return makePtr<BoostDesc_Impl>( desc, use_scale_orientation, scale_factor );
}
} // END NAMESPACE XFEATURES2D
} // END NAMESPACE CV
......@@ -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<KeyPoint>& _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<KeyPoint>& 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 )
);
......
......@@ -1057,6 +1057,62 @@ TEST( Features2d_DescriptorExtractor_VGG, regression )
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BGM, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-bgm",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
BoostDesc::create(BoostDesc::BGM) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BGM_HARD, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-bgm_hard",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
BoostDesc::create(BoostDesc::BGM_HARD) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BGM_BILINEAR, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-bgm_bilinear",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)15.f,
BoostDesc::create(BoostDesc::BGM_BILINEAR) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_LBGM, regression )
{
CV_DescriptorExtractorTest<L2<float> > test( "descriptor-boostdesc-lbgm",
1.0f,
BoostDesc::create(BoostDesc::LBGM) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BINBOOST_64, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-binboost_64",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
BoostDesc::create(BoostDesc::BINBOOST_64) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BINBOOST_128, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-binboost_128",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
BoostDesc::create(BoostDesc::BINBOOST_128) );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_BINBOOST_256, regression )
{
CV_DescriptorExtractorTest<Hamming> test( "descriptor-boostdesc-binboost_256",
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
BoostDesc::create(BoostDesc::BINBOOST_256) );
test.safe_run();
}
/*#if CV_SSE2
TEST( Features2d_DescriptorExtractor_Calonder_uchar, regression )
......
......@@ -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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment