From 5785a6a58a869b5bac44586e15c67ae1bfcee33f Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 15:17:29 +0300 Subject: [PATCH 01/14] Add DAISY descriptor for wide-baseline / keypoints. --- modules/xfeatures2d/doc/xfeatures2d.bib | 11 + .../include/opencv2/xfeatures2d.hpp | 31 + modules/xfeatures2d/perf/perf_daisy.cpp | 33 + modules/xfeatures2d/src/daisy.cpp | 2054 +++++++++++++++++ modules/xfeatures2d/test/test_features2d.cpp | 7 + .../test_rotation_and_scale_invariance.cpp | 18 + 6 files changed, 2154 insertions(+) create mode 100644 modules/xfeatures2d/perf/perf_daisy.cpp create mode 100644 modules/xfeatures2d/src/daisy.cpp diff --git a/modules/xfeatures2d/doc/xfeatures2d.bib b/modules/xfeatures2d/doc/xfeatures2d.bib index f23c529ab..bbfaadde5 100644 --- a/modules/xfeatures2d/doc/xfeatures2d.bib +++ b/modules/xfeatures2d/doc/xfeatures2d.bib @@ -53,3 +53,14 @@ year={2012} publisher={NIPS} } + +@article{Tola10, + author = "E. Tola and V. Lepetit and P. Fua", + title = {{DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo}}, + journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence", + year = 2010, + month = "May", + pages = "815--830", + volume = "32", + number = "5" +} diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 31afe7a12..4241a1f7b 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -144,6 +144,37 @@ public: static Ptr create(const int lucid_kernel, const int blur_kernel); }; +/** @brief Class implementing DAISY descriptor, described in @cite Tola10 + +@param radius radius of the descriptor at the initial scale +@param q_radius amount of radial range division quantity +@param q_theta amount of angular range division quantity +@param q_hist amount of gradient orientations range division quantity +@param mode choose computation mode of descriptors where +DAISY::ONLY_KEYS means to compute descriptors only for keypoints in the list (default) and +DAISY::COMP_FULL will compute descriptors for all pixels in the given image +@param norm choose descriptors normalization type, where +DAISY::NRM_NONE will not do any normalization (default), +DAISY::NRM_PARTIAL mean that histograms are normalized independently for L2 norm equal to 1.0, +DAISY::NRM_FULL mean that descriptors are normalized for L2 norm equal to 1.0, +DAISY::NRM_SIFT mean that descriptors are normalized for L2 norm equal to 1.0 but no individual one is bigger than 0.154 as in SIFT +@param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image +@param interpolation switch to disable interpolation for speed improvement at minor quality loss +@param use_orientation sample patterns using keypoints orientation, disabled by default. + + */ +class CV_EXPORTS DAISY : public DescriptorExtractor +{ +public: + enum + { + ONLY_KEYS = 0, COMP_FULL = 1, + NRM_NONE = 100, NRM_PARTIAL = 101, NRM_FULL = 102, NRM_SIFT = 103, + }; + static Ptr create( float radius = 15, int q_radius = 3, int q_theta = 8, + int q_hist = 8, int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, + InputArray H = noArray() , bool interpolation = true, bool use_orientation = false ); +}; //! @} diff --git a/modules/xfeatures2d/perf/perf_daisy.cpp b/modules/xfeatures2d/perf/perf_daisy.cpp new file mode 100644 index 000000000..bb60b8e78 --- /dev/null +++ b/modules/xfeatures2d/perf/perf_daisy.cpp @@ -0,0 +1,33 @@ +#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 daisy; + +#define DAISY_IMAGES \ + "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ + "stitching/a3.png" + +PERF_TEST_P(daisy, extract, testing::Values(DAISY_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); + + // use DAISY in COMP_FULL mode (every pixel, dense baseline mode) + Ptr descriptor = DAISY::create(15, 3, 8, 8, DAISY::COMP_FULL, DAISY::NRM_NONE, noArray(), true, false); + + vector points; + vector descriptors; + TEST_CYCLE() descriptor->compute(frame, points, descriptors); + + SANITY_CHECK(descriptors, 1e-4); +} diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp new file mode 100644 index 000000000..7a71856b6 --- /dev/null +++ b/modules/xfeatures2d/src/daisy.cpp @@ -0,0 +1,2054 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009 + * Engin Tola + * web : http://www.engintola.com + * email : engin.tola+libdaisy@gmail.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 Willow Garage 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. + *********************************************************************/ + +/* + "DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo" + by Engin Tola, Vincent Lepetit and Pascal Fua. IEEE Transactions on + Pattern Analysis and achine Intelligence, 31 Mar. 2009. + IEEE computer Society Digital Library. IEEE Computer Society, + http:doi.ieeecomputersociety.org/10.1109/TPAMI.2009.77 + + "A fast local descriptor for dense matching" by Engin Tola, Vincent + Lepetit, and Pascal Fua. Intl. Conf. on Computer Vision and Pattern + Recognition, Alaska, USA, June 2008 + + OpenCV port by: Cristian Balint + */ + +#include "precomp.hpp" +#include "opencv2/imgproc/imgproc_c.h" + +#include +#include + +namespace cv +{ +namespace xfeatures2d +{ + +// constants +const double g_sigma_0 = 1; +const double g_sigma_1 = sqrt(2.0); +const double g_sigma_2 = 8; +const double g_sigma_step = std::pow(2,1.0/2); +const int g_scale_st = int( (log(g_sigma_1/g_sigma_0)) / log(g_sigma_step) ); +static int g_scale_en = 1; + +const double g_sigma_init = 1.6; +const static int g_grid_orientation_resolution = 360; + +static const int MAX_CUBE_NO = 64; +static const int MAX_NORMALIZATION_ITER = 5; + +int g_cube_number; +int g_selected_cubes[MAX_CUBE_NO]; // m_rad_q_no < MAX_CUBE_NO + +/* + !DAISY implementation + */ +class DAISY_Impl : public DAISY +{ + +public: + /** Constructor + @param radius radius of the descriptor at the initial scale + @param q_radius amount of radial range divisions + @param q_theta amount of angular range divisions + @param q_hist amount of gradient orientations range divisions + @param mode computation of descriptors + @param norm normalization type + @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image + @param interpolation switch to disable interpolation at minor costs of quality (default is true) + */ + explicit DAISY_Impl(float radius=15, int q_radius=3, int q_theta=8, int q_hist=8, + int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false); + + virtual ~DAISY_Impl(); + + /** returns the descriptor length in bytes */ + virtual int descriptorSize() const { + // +1 is for center pixel + return ( (m_rad_q_no * m_th_q_no + 1) * m_hist_th_q_no ); + }; + /** returns the descriptor type */ + virtual int descriptorType() const { return CV_32F; } + /** returns the default norm type */ + virtual int defaultNorm() const { return NORM_L2; } + + // main compute routine + virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ); + +protected: + + /* + * DAISY parameters + */ + + // operation mode + int m_mode; + + // maximum radius of the descriptor region. + float m_rad; + + // the number of quantizations of the radius. + int m_rad_q_no; + + // the number of quantizations of the angle. + int m_th_q_no; + + // the number of quantizations of the gradient orientations. + int m_hist_th_q_no; + + // holds the type of the normalization to apply; equals to NRM_PARTIAL by + // default. change the value using set_normalization() function. + int m_nrm_type; + + // holds optional H matrix + InputArray m_h_matrix; + + // input image. + float* m_image; + + // image height + int m_h; + + // image width + int m_w; + + // stores the descriptors : its size is [ m_w * m_h * m_descriptor_size ]. + float* m_dense_descriptors; + + // stores the layered gradients in successively smoothed form: layer[n] = + // m_gradient_layers * gaussian( sigma_n ); n>= 1; layer[0] is the layered_gradient + float* m_smoothed_gradient_layers; + + // if set to true, descriptors are scale invariant + bool m_scale_invariant; + + // if set to true, descriptors are rotation invariant + bool m_rotation_invariant; + + // number of bins in the histograms while computing orientation + int m_orientation_resolution; + + // hold the scales of the pixels + float* m_scale_map; + + // holds the orientaitons of the pixels + int* m_orientation_map; + + // Holds the oriented coordinates (y,x) of the grid points of the region. + double** m_oriented_grid_points; + + // holds the gaussian sigmas for radius quantizations for an incremental + // application + double* m_cube_sigmas; + + bool m_descriptor_memory; + bool m_workspace_memory; + + // the number of grid locations + int m_grid_point_number; + + // the size of the descriptor vector + int m_descriptor_size; + + // holds the amount of shift that's required for histogram computation + double m_orientation_shift_table[360]; + + // if enabled, descriptors are computed with casting non-integer locations + // to integer positions otherwise we use interpolation. + bool m_disable_interpolation; + + // switch to enable sample by keypoints orientation + bool m_use_orientation; + + // size of m_hsz layers at a single sigma: m_hsz * m_layer_size + int m_cube_size; + + // size of the layer : m_h*m_w + int m_layer_size; + + /* + * DAISY functions + */ + + // computes the histogram at yx; the size of histogram is m_hist_th_q_no + void compute_histogram( float* hcube, int y, int x, float* histogram ); + + // reorganizes the cube data so that histograms are sequential in memory. + void compute_histograms(); + + // emulates the way sift is normalized. + void normalize_sift_way( float* desc ); + + // normalizes the descriptor histogram by histogram + void normalize_partial( float* desc ); + + // normalizes the full descriptor. + void normalize_full( float* desc ); + + // initializes the class: computes gradient and structure-points + void initialize(); + + void update_selected_cubes(); + + int quantize_radius( float rad ); + + int filter_size( double sigma ); + + // computes scales for every pixel and scales the structure grid so that the + // resulting descriptors are scale invariant. you must set + // m_scale_invariant flag to 1 for the program to call this function + void compute_scales(); + + // Return a number in the range [-0.5, 0.5] that represents the location of + // the peak of a parabola passing through the 3 evenly spaced samples. The + // center value is assumed to be greater than or equal to the other values + // if positive, or less than if negative. + float interpolate_peak( float left, float center, float right ); + + // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram + // is connected in a circular buffer. + void smooth_histogram( float *hist, int bins ); + + // computes pixel orientations and rotates the structure grid so that + // resulting descriptors are rotation invariant. If the scales is also + // detected, then orientations are computed at the computed scales. you must + // set m_rotation_invariant flag to 1 for the program to call this function + void compute_orientations(); + + // the clipping threshold to use in normalization: values above this value + // are clipped to this value for normalize_sift_way() function + float m_descriptor_normalization_threshold; + + // computes the sigma's of layers from descriptor parameters if the user did + // not sets it. these define the size of the petals of the descriptor. + void compute_cube_sigmas(); + + // Computes the locations of the unscaled unrotated points where the + // histograms are going to be computed according to the given parameters. + void compute_grid_points(); + + // Computes the locations of the unscaled rotated points where the + // histograms are going to be computed according to the given parameters. + void compute_oriented_grid_points(); + + // smooths each of the layers by a Gaussian having "sigma" standart + // deviation. + void smooth_layers( float*layers, int h, int w, int layer_number, float sigma ); + + // Holds the coordinates (y,x) of the grid points of the region. + double** m_grid_points; + + int get_hq() { return m_hist_th_q_no; } + int get_thq() { return m_th_q_no; } + int get_rq() { return m_rad_q_no; } + float get_rad() { return m_rad; } + + // sets the type of the normalization to apply out of {NRM_PARTIAL, + // NRM_FULL, NRM_SIFT}. Call before using get_descriptor() if you want to + // change the default normalization type. + void set_normalization( int nrm_type ) { m_nrm_type = nrm_type; } + + // applies one of the normalizations (partial,full,sift) to the desciptors. + void normalize_descriptors(int nrm_type = DAISY::NRM_NONE); + + // normalizes histograms individually + void normalize_histograms(); + + // gets the histogram at y,x with 'orientation' from the r'th cube + float* get_histogram( int y, int x, int r ); + + // if called, I don't use interpolation in the computation of + // descriptors. + void disable_interpolation() { m_disable_interpolation = true; } + + // returns the region number. + int grid_point_number() { return m_grid_point_number; } + + // releases all the used memory; call this if you want to process + // multiple images within a loop. + void reset(); + + // releases unused memory after descriptor computation is completed. + void release_auxilary(); + + // computes the descriptors for every pixel in the image. + void compute_descriptors(); + + // returns all the descriptors. + float* get_dense_descriptors(); + + // returns oriented grid points. default is 0 orientation. + double* get_grid(int o=0); + + // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the + // scales for every pixel so that the resulting descriptors are scale + // invariant. + void scale_invariant( bool state=true ) + { + g_scale_en = (int)( (log(g_sigma_2/g_sigma_0)) / log(g_sigma_step) ) - g_scale_st; + m_scale_invariant = state; + } + + // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the + // orientations for every pixel so that the resulting descriptors are + // rotation invariant. orientation steps are 360/ori_resolution + void rotation_invariant(int ori_resolution=36, bool state=true) + { + m_rotation_invariant = state; + m_orientation_resolution = ori_resolution; + } + + // sets the gaussian variances manually. must be called before + // initialize() to be considered. must be exact sigma values -> f + // converts to incremental format. + void set_cube_gaussians( double* sigma_array, int sz ); + + int* get_orientation_map() { return m_orientation_map; } + + // call compute_descriptor_memory to find the amount of memory to allocate + void set_descriptor_memory( float* descriptor, long int d_size ); + + // call compute_workspace_memory to find the amount of memory to allocate + void set_workspace_memory( float* workspace, long int w_size ); + + // returns the amount of memory needed for the compute_descriptors() + // function. it is basically equal to imagesize x descriptor_size + int compute_descriptor_memory() { + if( m_h == 0 || m_descriptor_size == 0 ) { + CV_Error( Error::StsInternal, "Image and descriptor size is zero" ); + } + return m_w * m_h * m_descriptor_size; + } + + // returns the amount of memory needed for workspace. call before initialize() + int compute_workspace_memory() { + if( m_cube_size == 0 ) { + CV_Error( Error::StsInternal, "Cube size is zero" ); + } + return (g_cube_number+1)* m_cube_size; + } + + void normalize_descriptor(float* desc, int nrm_type = DAISY::NRM_NONE) + { + if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; + else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); + else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); + else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); + else + CV_Error( Error::StsInternal, "No such normalization" ); + } + + // transform a point via the homography + void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) + { + double kxp = H[0]*x + H[1]*y + H[2]; + double kyp = H[3]*x + H[4]*y + H[5]; + double kp = H[6]*x + H[7]*y + H[8]; + u = kxp / kp; + v = kyp / kp; + } + +private: + + // returns the descriptor vector for the point (y, x) !!! use this for + // precomputed operations meaning that you must call compute_descriptors() + // before calling this function. if you want normalized descriptors, call + // normalize_descriptors() before calling compute_descriptors() + inline void get_descriptor(int y, int x, float* &descriptor); + + // computes the descriptor and returns the result in 'descriptor' ( allocate + // 'descriptor' memory first ie: float descriptor = new + // float[m_descriptor_size]; -> the descriptor is normalized. + inline void get_descriptor(double y, double x, int orientation, float* descriptor ); + + // computes the descriptor and returns the result in 'descriptor' ( allocate + // 'descriptor' memory first ie: float descriptor = new + // float[m_descriptor_size]; -> the descriptor is NOT normalized. + inline void get_unnormalized_descriptor(double y, double x, int orientation, float* descriptor ); + + // computes the descriptor at homography-warped grid. (y,x) is not the + // coordinates of this image but the coordinates of the original grid where + // the homography will be applied. Meaning that the grid is somewhere else + // and we warp this grid with H and compute the descriptor on this warped + // grid; returns null/false if centers falls outside the image; allocate + // 'descriptor' memory first. descriptor is normalized. + inline bool get_descriptor(double y, double x, int orientation, double* H, float* descriptor ); + + // computes the descriptor at homography-warped grid. (y,x) is not the + // coordinates of this image but the coordinates of the original grid where + // the homography will be applied. Meaning that the grid is somewhere else + // and we warp this grid with H and compute the descriptor on this warped + // grid; returns null/false if centers falls outside the image; allocate + // 'descriptor' memory first. descriptor is NOT normalized. + inline bool get_unnormalized_descriptor(double y, double x, int orientation, double* H, float* descriptor ); + + // compute the smoothed gradient layers. + inline void compute_smoothed_gradient_layers(); + + // does not use interpolation while computing the histogram. + inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ); + + // returns the interpolated histogram: picks either bi_get_histogram or + // ti_get_histogram depending on 'shift' + inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ); + + // records the histogram that is computed by bilinear interpolation + // regarding the shift in the spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ); + + // records the histogram that is computed by trilinear interpolation + // regarding the shift in layers and spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ); + + // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor + inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ); + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ); + + // uses interpolation for no interpolation call ni_get_descriptor. see also get_descriptor + inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + + // creates a 1D gaussian filter with N(mean,sigma). + inline void gaussian_1d(float* fltr, int fsz, float sigma, float mean ) + { + CV_Assert(fltr != NULL); + int sz = (fsz-1)/2; + int counter=-1; + float sum = 0.0; + float v = 2*sigma*sigma; + for( int x=-sz; x<=sz; x++ ) + { + counter++; + fltr[counter] = exp((-(x-mean)*(x-mean))/v); + sum += fltr[counter]; + } + + if( sum != 0 ) + { + for( int x=0; x + class rectangle + { + public: + T lx, ux, ly, uy; + T dx, dy; + rectangle(T xl, T xu, T yl, T yu) { lx=xl; ux=xu; ly=yl; uy=yu; dx=ux-lx; dy=uy-ly; }; + rectangle() { lx = ux = ly = uy = dx = dy = 0; }; + }; + + // checks if the number x is between lx - ux interval. + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + { + if( ( ((lx lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='&') + { + switch( oper ) + { + case '|': + if( is_inside(x,lx,ux,le,ue) || is_inside(y,ly,uy,le,ue) ) + return true; + return false; + + default: + if( is_inside(x,lx,ux,le,ue) && is_inside(y,ly,uy,le,ue) ) + return true; + return false; + } + } + + // checks if the number x is between lx - ux and/or y is between ly - uy interval. + // If the number is inside, then function returns true, else it returns false. + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='&') + { + switch( oper ) + { + case '|': + if( is_inside(x,roi.lx,roi.ux,le,ue) || is_inside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + + default: + if( is_inside(x,roi.lx,roi.ux,le,ue) && is_inside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + } + } + + // checks if the number x is outside lx - ux interval + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx>x is checked else lx>=x is checked + // if ue=1 => x>ux is checked else x>=ux is checked + // by default is x is searched outside of [lx,ux) + template inline + bool is_outside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + { + return !(is_inside(x,lx,ux,le,ue)); + } + + // checks if the numbers x and y is outside their intervals. + // The equality is checked depending on the value of le and ue parameters. + // If le=1 => lx>x is checked else lx>=x is checked + // If ue=1 => x>ux is checked else x>=ux is checked + // By default is x is searched outside of [lx,ux) (Similarly for y) + // By default, 'oper' is set to OR. If one of them is outside it returns + // true otherwise false. + template inline + bool is_outside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='|') + { + switch( oper ) + { + case '&': + if( is_outside(x,lx,ux,le,ue) && is_outside(y,ly,uy,le,ue) ) + return true; + return false; + default: + if( is_outside(x,lx,ux,le,ue) || is_outside(y,ly,uy,le,ue) ) + return true; + return false; + } + } + + // checks if the numbers x and y is outside their intervals. + // The equality is checked depending on the value of le and ue parameters. + // If le=1 => lx>x is checked else lx>=x is checked + // If ue=1 => x>ux is checked else x>=ux is checked + // By default is x is searched outside of [lx,ux) (Similarly for y) + // By default, 'oper' is set to OR. If one of them is outside it returns + // true otherwise false. + template inline + bool is_outside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='|') + { + switch( oper ) + { + case '&': + if( is_outside(x,roi.lx,roi.ux,le,ue) && is_outside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + default: + if( is_outside(x,roi.lx,roi.ux,le,ue) || is_outside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + } + } + + // computes the square of a number and returns it. + template inline + T square(T a) + { + return a*a; + } + + // computes the square of an array. if in_place is enabled, the + // result is returned in the array arr. + template inline + T* square(T* arr, int sz, bool in_place=false) + { + T* out; + if( in_place ) out = arr; + else out = allocate(sz); + + for( int i=0; i inline + float l2norm( T* a, int sz) + { + float norm=0; + for( int k=0; k inline + float l2norm( T1* a, T2* b, int sz) + { + float norm=0; + for( int i=0; i inline + float l2norm( T y0, T x0, T y1, T x1 ) + { + float d0 = x0 - x1; + float d1 = y0 - y1; + + return sqrt( d0*d0 + d1*d1 ); + } + + // allocates a memory of size sz and returns a pointer to the array + template inline + T* allocate(const int sz) + { + T* array = new T[sz]; + return array; + } + + // allocates a memory of size ysz x xsz and returns a double pointer to it + template inline + T** allocate(const int ysz, const int xsz) + { + T** mat = new T*[ysz]; + int i; + + for(i=0; i(xsz); + + return mat; + } + + // deallocates the memory and sets the pointer to null. + template inline + void deallocate(T* &array) + { + delete[] array; + array = NULL; + } + + // deallocates the memory and sets the pointer to null. + template inline + void deallocate(T** &mat, int ysz) + { + if( mat == NULL ) return; + + for(int i=0; i inline + void polar2cartesian(T1 r, T1 t, T2 &y, T2 &x) + { + x = (T2)( r * cos( t ) ); + y = (T2)( r * sin( t ) ); + } + + + template inline + void convolve_sym_( T* image, int h, int w, T* kernel, int ksize ) + { + conv_horizontal( image, h, w, kernel, ksize ); + conv_vertical ( image, h, w, kernel, ksize ); + } + + template + inline void convolve_sym( T* image, int h, int w, T* kernel, int ksize, T* out=NULL ) + { + if( out == NULL ) out = image; + else memcpy( out, image, sizeof(T)*h*w ); + convolve_sym_(out, h, w, kernel, ksize); + } + + // divides the elements of the array with num + template inline + void divide(T1* arr, int sz, T2 num ) + { + float inv_num = 1.0 / num; + + for( int i=0; i inline + T* zeros(int r) + { + T* data = allocate(r); + memset( data, 0, sizeof(T)*r ); + return data; + } + + template inline + T* layered_gradient( T* data, int h, int w, int layer_no=8 ) + { + int data_size = h * w; + T* layers = zeros(layer_no * data_size); + + // smooth the data matrix + T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); + + T *dx = new T[data_size]; + T *dy = new T[data_size]; + gradient(bdata, h, w, dy, dx); + deallocate( bdata ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + deallocate(dy); + deallocate(dx); + + return layers; + } + + // computes the gradient of an image and returns the result in + // pointers to REAL. + template inline + void gradient(T* im, int h, int w, T* dy, T* dx) + { + CV_Assert( dx != NULL ); + CV_Assert( dy != NULL ); + + for( int y=0; y0 && x0 && y inline + // original T* workspace=0 was removed + void layered_gradient( T* data, int h, int w, int layer_no, T* layers, int lwork=0 ) + { + int data_size = h * w; + CV_Assert(layers!=NULL); + memset(layers,0,sizeof(T)*data_size*layer_no); + + bool empty=false; + T* work=NULL; + if( lwork < 3*data_size ) { + work = new T[3*data_size]; + empty=true; + } + + // // smooth the data matrix + // T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); + float kernel[5]; gaussian_1d(kernel, 5, 0.5, 0); + memcpy( work, data, sizeof(T)*data_size); + convolve_sym( work, h, w, kernel, 5 ); + + T *dx = work+data_size; + T *dy = work+2*data_size; + gradient( work, h, w, dy, dx ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + if( empty ) delete []work; + } + + // casts a type T2 array into a type T1 array. + template inline + T1* type_cast(T2* data, int sz) + { + T1* out = new T1[sz]; + + for( int i=0; i inline + T1* blur_gaussian_2d( T2* array, int rn, int cn, float sigma, int kernel_size=0, bool in_place=false ) + { + T1* out = NULL; + + if( in_place ) + out = (T1*)array; + else + out = type_cast(array,rn*cn); + + if( kernel_size == 0 ) + kernel_size = (int)(3*sigma); + + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size= 3; // kernel size cannot be smaller than 3 + + float* kernel = new float[kernel_size]; + gaussian_1d(kernel, kernel_size, sigma, 0); + + // !! apply the filter separately + convolve_sym( out, rn, cn, kernel, kernel_size ); + // conv_horizontal( out, rn, cn, kernel, kernel_size); + // conv_vertical ( out, rn, cn, kernel, kernel_size); + + deallocate(kernel); + return out; + } + +}; // END DAISY_Impl CLASS + + +// ------------------------------------------------- +/* DAISY computation routines */ + +float* DAISY_Impl::get_histogram( int y, int x, int r ) +{ + CV_Assert( y >= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + return m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size + (y*m_w+x)*m_hist_th_q_no; + // i_get_histogram( histogram, y, x, 0, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); +} + + +float* DAISY_Impl::get_dense_descriptors() +{ + return m_dense_descriptors; +} + +double* DAISY_Impl::get_grid(int o) +{ + CV_Assert( o >= 0 && o < 360 ); + return m_oriented_grid_points[o]; +} + +void DAISY_Impl::reset() +{ + deallocate( m_image ); + // deallocate( m_grid_points, m_grid_point_number ); + // deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + // deallocate( m_cube_sigmas ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + if( !m_descriptor_memory ) deallocate( m_dense_descriptors ); + if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); +} + +void DAISY_Impl::release_auxilary() +{ + deallocate( m_image ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + + if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); + + deallocate( m_grid_points, m_grid_point_number ); + deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + deallocate( m_cube_sigmas ); +} + +void DAISY_Impl::compute_grid_points() +{ + double r_step = m_rad / m_rad_q_no; + double t_step = 2*CV_PI/ m_th_q_no; + + if( m_grid_points ) + deallocate( m_grid_points, m_grid_point_number ); + + m_grid_points = allocate(m_grid_point_number, 2); + for( int y=0; y(m_h*m_w*m_descriptor_size); + + memset(m_dense_descriptors, 0, sizeof(float)*m_h*m_w*m_descriptor_size); + + int y, x, index, orientation; +#if defined _OPENMP +#pragma omp parallel for private(y,x,index,orientation) +#endif + for( y=0; y= 0 && orientation < g_grid_orientation_resolution ) ) orientation = 0; + get_unnormalized_descriptor( y, x, orientation, &(m_dense_descriptors[index*m_descriptor_size]) ); + } + } +} + +void DAISY_Impl::smooth_layers( float* layers, int h, int w, int layer_number, float sigma ) +{ + int fsz = filter_size(sigma); + float* filter = new float[fsz]; + gaussian_1d(filter, fsz, sigma, 0); + int i; + float* layer=0; +#if defined _OPENMP +#pragma omp parallel for private(i, layer) +#endif + for( i=0; i 1e-5 ) + divide( desc, m_descriptor_size, norm); + + for( h=0; h m_descriptor_normalization_threshold ) + { + desc[ h ] = m_descriptor_normalization_threshold; + changed = true; + } + } + } +} + +void DAISY_Impl::normalize_descriptors( int nrm_type ) +{ + int number_of_descriptors = m_h * m_w; + int d; + +#if defined _OPENMP +#pragma omp parallel for private(d) +#endif + for( d=0; d(g_cube_number); + + double r_step = double(m_rad)/m_rad_q_no; + for( int r=0; r< m_rad_q_no; r++ ) + { + m_cube_sigmas[r] = (r+1)*r_step/2; + } + } + update_selected_cubes(); +} + +void DAISY_Impl::set_cube_gaussians( double* sigma_array, int sz ) +{ + g_cube_number = sz; + + if( m_cube_sigmas ) deallocate( m_cube_sigmas ); + m_cube_sigmas = allocate(g_cube_number); + + for( int r=0; r= m_cube_sigmas[g_cube_number-1] ) return g_cube_number-1; + + float dist; + float mindist=FLT_MAX; + int mini=0; + for( int c=0; c(g_grid_orientation_resolution, m_grid_point_number*2 ); + + for( int i=0; i= left && center >= right); + + float den = (left - 2.0 * center + right); + + if( den == 0 ) return 0; + else return 0.5*(left -right)/den; +} + +int DAISY_Impl::filter_size( double sigma ) +{ + int fsz = (int)(5*sigma); + + // kernel size must be odd + if( fsz%2 == 0 ) fsz++; + + // kernel size cannot be smaller than 3 + if( fsz < 3 ) fsz = 3; + + return fsz; +} + +void DAISY_Impl::compute_scales() +{ + //############################################################################### + //# scale detection is work-in-progress! do not use it if you're not Engin Tola # + //############################################################################### + + int imsz = m_w * m_h; + + float sigma = pow( g_sigma_step, g_scale_st)*g_sigma_0; + + float* sim = blur_gaussian_2d( m_image, m_h, m_w, sigma, filter_size(sigma), false); + + float* next_sim = NULL; + + float* max_dog = allocate(imsz); + + m_scale_map = allocate(imsz); + + memset( max_dog, 0, imsz*sizeof(float) ); + memset( m_scale_map, 0, imsz*sizeof(float) ); + + int i; + float sigma_prev; + float sigma_new; + float sigma_inc; + + sigma_prev = g_sigma_0; + for( i=0; i( sim, m_h, m_w, sigma_inc, filter_size( sigma_inc ) , false); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int p=0; p max_dog[p] ) + { + max_dog[p] = dog; + m_scale_map[p] = i; + } + } + deallocate( sim ); + + sim = next_sim; + } + + blur_gaussian_2d( m_scale_map, m_h, m_w, 10.0, filter_size(10), true); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int q=0; q(m_orientation_resolution); + + for( x=0; x max_val ) + { + max_val = hist[ori]; + max_ind = ori; + } + } + + prev = max_ind-1; + if( prev < 0 ) + prev += m_orientation_resolution; + + next = max_ind+1; + if( next >= m_orientation_resolution ) + next -= m_orientation_resolution; + + peak = interpolate_peak(hist[prev], hist[max_ind], hist[next]); + angle = (max_ind + peak)*360.0/m_orientation_resolution; + + int iangle = int(angle); + + if( iangle < 0 ) iangle += 360; + if( iangle >= 360 ) iangle -= 360; + + + if( !(iangle >= 0.0 && iangle < 360.0) ) + { + angle = 0; + } + + m_orientation_map[ ind ] = iangle; + } + deallocate(hist); + } + } + + deallocate( rotation_layers ); + + compute_oriented_grid_points(); +} + +void DAISY_Impl::set_descriptor_memory( float* descriptor, long int d_size ) +{ + CV_Assert( m_descriptor_memory == false ); + CV_Assert( m_h*m_w != 0 ); + CV_Assert( d_size >= compute_descriptor_memory() ); + + m_dense_descriptors = descriptor; + m_descriptor_memory = true; +} + +void DAISY_Impl::set_workspace_memory( float* workspace, long int w_size ) +{ + CV_Assert( m_workspace_memory == false ); + CV_Assert( m_h*m_w != 0 ); + CV_Assert( w_size >= compute_workspace_memory() ); + + m_smoothed_gradient_layers = workspace; + m_workspace_memory = true; +} + +// ------------------------------------------------- +/* DAISY helper routines */ + +inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* histogram ) +{ + if( is_outside(x, 0, m_w-1, y, 0, m_h-1) ) return; + + float* spatial_shift = hcube + y * m_w + x; + int data_size = m_w * m_h; + + for( int h=0; h 0.99 ) bi_get_histogram( histogram, y, x, ishift+1, cube ); + else ti_get_histogram( histogram, y, x, shift , cube ); +} + +inline void DAISY_Impl::bi_get_histogram( float* histogram, double y, double x, int shift, float* hcube ) +{ + int mnx = int( x ); + int mny = int( y ); + + if( mnx >= m_w-2 || mny >= m_h-2 ) + { + memset(histogram, 0, sizeof(float)*m_hist_th_q_no); + return; + } + + int ind = mny*m_w+mnx; + // A C --> pixel positions + // B D + float* A = hcube+ind*m_hist_th_q_no; + float* B = A+m_w*m_hist_th_q_no; + float* C = A+m_hist_th_q_no; + float* D = A+(m_w+1)*m_hist_th_q_no; + + double alpha = mnx+1-x; + double beta = mny+1-y; + + float w0 = alpha*beta; + float w1 = beta-w0; // (1-alpha)*beta; + float w2 = alpha-w0; // (1-beta)*alpha; + float w3 = 1+w0-alpha-beta; // (1-beta)*(1-alpha); + + int h; + + for( h=0; h= m_hist_th_q_no ) hi -= m_hist_th_q_no; + histogram[h] = hptr[hi]; + } +} + +inline void DAISY_Impl::get_descriptor(int y, int x, float* &descriptor) +{ + CV_Assert( m_dense_descriptors != NULL ); + CV_Assert( y=0 && x>=0 ); + descriptor = &(m_dense_descriptors[(y*m_w+x)*m_descriptor_size]); +} + +inline void DAISY_Impl::get_descriptor(double y, double x, int orientation, float* descriptor ) +{ + get_unnormalized_descriptor(y, x, orientation, descriptor ); + normalize_descriptor(descriptor, m_nrm_type); +} + +inline void DAISY_Impl::get_unnormalized_descriptor(double y, double x, int orientation, float* descriptor ) +{ + if( m_disable_interpolation ) ni_get_descriptor(y,x,orientation,descriptor); + else i_get_descriptor(y,x,orientation,descriptor); +} + +inline void DAISY_Impl::i_get_descriptor(double y, double x, int orientation, float* descriptor ) +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + // + CV_Assert( y >= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + + i_get_histogram( descriptor, y, x, shift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + + int r, rdt, region; + double yy, xx; + float* histogram = 0; + double* grid = m_oriented_grid_points[orientation]; + + // petals of the flower + for( r=0; r= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; + if( shift - ishift > 0.5 ) ishift++; + + int iy = (int)y; if( y - iy > 0.5 ) iy++; + int ix = (int)x; if( x - ix > 0.5 ) ix++; + + // center + ni_get_histogram( descriptor, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + + double yy, xx; + float* histogram=0; + // petals of the flower + int r, rdt, region; + double* grid = m_oriented_grid_points[orientation]; + for( r=0; r 0.5 ) iy++; + ix = (int)xx; if( xx - ix > 0.5 ) ix++; + + if( is_outside(ix, 0, m_w-1, iy, 0, m_h-1) ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); + } + } +} + +// Warped get_descriptor's +inline bool DAISY_Impl::get_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +{ + bool rval = get_unnormalized_descriptor(y,x,orientation, H, descriptor); + if( rval ) normalize_descriptor(descriptor, m_nrm_type); + return rval; +} + +inline bool DAISY_Impl::get_unnormalized_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +{ + if( m_disable_interpolation ) return ni_get_descriptor(y,x,orientation,H,descriptor); + else return i_get_descriptor(y,x,orientation,H,descriptor); +} + +inline bool DAISY_Impl::i_get_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + // + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + point_transform_via_homography( H, x, y, hx, hy ); + if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; + + point_transform_via_homography( H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); + double radius = l2norm( ry, rx, hy, hx ); + hradius[0] = quantize_radius( radius ); + + double shift = m_orientation_shift_table[orientation]; + i_get_histogram( descriptor, hy, hx, shift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + + double gy, gx; + int r, rdt, th, region; + float* histogram=0; + for( r=0; r= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + double radius; + + double hy, hx, ry, rx; + + point_transform_via_homography(H, x, y, hx, hy ); + if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; + + point_transform_via_homography(H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); + radius = l2norm( ry, rx, hy, hx ); + hradius[0] = quantize_radius( radius ); + + int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + int r, rdt, th, region; + double gy, gx; + float* histogram=0; + ni_get_histogram( descriptor, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + for( r=0; r 0.5 ) ihx++; + ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + if( is_outside(ihx, 0, m_w-1, ihy, 0, m_h-1) ) continue; + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[r]*m_cube_size ); + } + } + return true; +} + +// ------------------------------------------------- +/* DAISY interface implementation */ + +void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + Mat image = _image.getMat(); + if( image.empty() ) + return; + + // get homography if supplied + Mat H = m_h_matrix.getMat(); + + // convert to float if case + if ( image.depth() != CV_64F ) + H.convertTo( H, CV_64F ); + /* + * daisy set_image() + */ + + // base size + m_h = image.rows; + m_w = image.cols; + + // clone image for conversion + if ( image.depth() != CV_32F ) { + + Mat work_image = image.clone(); + + // convert to gray inplace + if( work_image.channels() > 1 ) + cvtColor( work_image, work_image, COLOR_BGR2GRAY ); + + // convert to float if it is necessary + if ( work_image.depth() != CV_32F ) + { + // convert and normalize + work_image.convertTo( work_image, CV_32F ); + work_image /= 255.0f; + } else + CV_Error( Error::StsUnsupportedFormat, "" ); + + // use cloned work image + m_image = work_image.ptr(0); + + } else + // use original CV_32F image + m_image = image.ptr(0); + + // full mode if noArray() + // was passed to _descriptors + if ( _descriptors.needed() == false ) + m_mode = DAISY::COMP_FULL; + + /* + * daisy set_parameters() + */ + + m_grid_point_number = m_rad_q_no * m_th_q_no + 1; // +1 is for center pixel + m_descriptor_size = m_grid_point_number * m_hist_th_q_no; + + for( int i=0; i<360; i++ ) + { + m_orientation_shift_table[i] = i/360.0 * m_hist_th_q_no; + } + m_layer_size = m_h*m_w; + m_cube_size = m_layer_size*m_hist_th_q_no; + + compute_cube_sigmas(); + compute_grid_points(); + + + /* + * daisy initialize_single_descriptor_mode(); + */ + + // initializes for get_descriptor(double, double, int) mode: pre-computes + // convolutions of gradient layers in m_smoothed_gradient_layers + + initialize(); + compute_smoothed_gradient_layers(); + + /* + * daisy compute descriptors given operating mode + */ + + if ( m_mode == COMP_FULL ) + { + CV_Assert( H.empty() ); + CV_Assert( keypoints.empty() ); + CV_Assert( ! m_use_orientation ); + + compute_descriptors(); + normalize_descriptors(); + + cv::Mat descriptors; + descriptors = _descriptors.getMat(); + descriptors = Mat( m_h * m_w, m_descriptor_size, + CV_32F, &m_dense_descriptors[0] ); + } else + if ( m_mode == ONLY_KEYS ) + { + cv::Mat descriptors; + _descriptors.create( keypoints.size(), m_descriptor_size, CV_32F ); + descriptors = _descriptors.getMat(); + + if ( H.empty() ) + for (size_t k = 0; k < keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? keypoints[k].angle : 0, + &descriptors.at( k, 0 ) ); + } + else + for (size_t k = 0; k < keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? keypoints[k].angle : 0, + &H.at( 0 ), &descriptors.at( k, 0 ) ); + } + + } else + CV_Error( Error::StsInternal, "Unknown computation mode" ); +} + +// constructor +DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, + int _mode, int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) + : m_mode(_mode), m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), + m_nrm_type(_norm), m_h_matrix(_H), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) +{ + m_w = 0; + m_h = 0; + + m_image = 0; + + m_grid_point_number = 0; + m_descriptor_size = 0; + + m_smoothed_gradient_layers = NULL; + m_dense_descriptors = NULL; + m_grid_points = NULL; + m_oriented_grid_points = NULL; + + m_scale_invariant = false; + m_rotation_invariant = false; + + m_scale_map = NULL; + m_orientation_map = NULL; + m_orientation_resolution = 36; + m_scale_map = NULL; + + m_cube_sigmas = NULL; + + m_descriptor_memory = false; + m_workspace_memory = false; + m_descriptor_normalization_threshold = 0.154; // sift magical number + + m_cube_size = 0; + m_layer_size = 0; + +} + +// destructor +DAISY_Impl::~DAISY_Impl() +{ + if( !m_workspace_memory ) deallocate( m_smoothed_gradient_layers ); + deallocate( m_grid_points, m_grid_point_number ); + deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + deallocate( m_cube_sigmas ); +} + +Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, + int mode, int norm, InputArray H, bool interpolation, bool use_orientation) +{ + return makePtr(radius, q_radius, q_theta, q_hist, mode, norm, H, interpolation, use_orientation); +} + + +} // END NAMESPACE XFEATURES2D +} // END NAMESPACE CV diff --git a/modules/xfeatures2d/test/test_features2d.cpp b/modules/xfeatures2d/test/test_features2d.cpp index 303274efa..a01c0a313 100644 --- a/modules/xfeatures2d/test/test_features2d.cpp +++ b/modules/xfeatures2d/test/test_features2d.cpp @@ -1010,6 +1010,13 @@ TEST( Features2d_DescriptorExtractor_SURF, regression ) test.safe_run(); } +TEST( Features2d_DescriptorExtractor_DAISY, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-daisy", 0.05f, + DAISY::create() ); + test.safe_run(); +} + TEST( Features2d_DescriptorExtractor_FREAK, regression ) { // TODO adjust the parameters below diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 46d328205..176a342c5 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -651,6 +651,15 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) +{ + DescriptorRotationInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.79f); + test.safe_run(); +} + /* * Detector's scale invariance check */ @@ -708,3 +717,12 @@ TEST(Features2d_RotationInvariance2_Detector_SURF, regression) ASSERT_LT( fabs(keypoints[1].response - keypoints[3].response), 1e-6); ASSERT_LT( fabs(keypoints[1].response - keypoints[4].response), 1e-6); } + +TEST(Features2d_ScaleInvariance_Descriptor_DAISY, regression) +{ + DescriptorScaleInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.075f); + test.safe_run(); +} From c1b2e237752d1f8b5413e8182ce27260e848c3bc Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 16:10:48 +0300 Subject: [PATCH 02/14] Fix linux warns & more cosmetics. --- modules/xfeatures2d/src/daisy.cpp | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 7a71856b6..62f42bf6e 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -286,7 +286,7 @@ protected: void set_normalization( int nrm_type ) { m_nrm_type = nrm_type; } // applies one of the normalizations (partial,full,sift) to the desciptors. - void normalize_descriptors(int nrm_type = DAISY::NRM_NONE); + void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); // normalizes histograms individually void normalize_histograms(); @@ -320,7 +320,7 @@ protected: // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the // scales for every pixel so that the resulting descriptors are scale // invariant. - void scale_invariant( bool state=true ) + void scale_invariant( bool state = true ) { g_scale_en = (int)( (log(g_sigma_2/g_sigma_0)) / log(g_sigma_step) ) - g_scale_st; m_scale_invariant = state; @@ -329,7 +329,7 @@ protected: // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the // orientations for every pixel so that the resulting descriptors are // rotation invariant. orientation steps are 360/ori_resolution - void rotation_invariant(int ori_resolution=36, bool state=true) + void rotation_invariant( int ori_resolution = 36, bool state = true ) { m_rotation_invariant = state; m_orientation_resolution = ori_resolution; @@ -365,7 +365,7 @@ protected: return (g_cube_number+1)* m_cube_size; } - void normalize_descriptor(float* desc, int nrm_type = DAISY::NRM_NONE) + void normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) { if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); @@ -391,17 +391,17 @@ private: // precomputed operations meaning that you must call compute_descriptors() // before calling this function. if you want normalized descriptors, call // normalize_descriptors() before calling compute_descriptors() - inline void get_descriptor(int y, int x, float* &descriptor); + inline void get_descriptor( int y, int x, float* &descriptor ); // computes the descriptor and returns the result in 'descriptor' ( allocate // 'descriptor' memory first ie: float descriptor = new // float[m_descriptor_size]; -> the descriptor is normalized. - inline void get_descriptor(double y, double x, int orientation, float* descriptor ); + inline void get_descriptor( double y, double x, int orientation, float* descriptor ); // computes the descriptor and returns the result in 'descriptor' ( allocate // 'descriptor' memory first ie: float descriptor = new // float[m_descriptor_size]; -> the descriptor is NOT normalized. - inline void get_unnormalized_descriptor(double y, double x, int orientation, float* descriptor ); + inline void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ); // computes the descriptor at homography-warped grid. (y,x) is not the // coordinates of this image but the coordinates of the original grid where @@ -409,7 +409,7 @@ private: // and we warp this grid with H and compute the descriptor on this warped // grid; returns null/false if centers falls outside the image; allocate // 'descriptor' memory first. descriptor is normalized. - inline bool get_descriptor(double y, double x, int orientation, double* H, float* descriptor ); + inline bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor); // computes the descriptor at homography-warped grid. (y,x) is not the // coordinates of this image but the coordinates of the original grid where @@ -417,7 +417,7 @@ private: // and we warp this grid with H and compute the descriptor on this warped // grid; returns null/false if centers falls outside the image; allocate // 'descriptor' memory first. descriptor is NOT normalized. - inline bool get_unnormalized_descriptor(double y, double x, int orientation, double* H, float* descriptor ); + inline bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ); // compute the smoothed gradient layers. inline void compute_smoothed_gradient_layers(); @@ -452,7 +452,7 @@ private: inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); // creates a 1D gaussian filter with N(mean,sigma). - inline void gaussian_1d(float* fltr, int fsz, float sigma, float mean ) + inline void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) { CV_Assert(fltr != NULL); int sz = (fsz-1)/2; @@ -476,27 +476,27 @@ private: inline void conv_horizontal( float* image, int h, int w, float* kernel, int ksize ) { CvMat cvI; cvInitMatHeader(&cvI, h, w, CV_32FC1, (float*)image); - CvMat cvK; cvInitMatHeader(&cvK, 1, ksize, CV_32FC1, (float*)kernel ); + CvMat cvK; cvInitMatHeader(&cvK, 1, ksize, CV_32FC1, (float*)kernel); cvFilter2D( &cvI, &cvI, &cvK ); } inline void conv_horizontal( double* image, int h, int w, double* kernel, int ksize ) { CvMat cvI; cvInitMatHeader(&cvI, h, w, CV_64FC1, (double*)image); - CvMat cvK; cvInitMatHeader(&cvK, 1, ksize, CV_64FC1, (double*)kernel ); + CvMat cvK; cvInitMatHeader(&cvK, 1, ksize, CV_64FC1, (double*)kernel); cvFilter2D( &cvI, &cvI, &cvK ); } inline void conv_vertical( float* image, int h, int w, float* kernel, int ksize ) { CvMat cvI; cvInitMatHeader(&cvI, h, w, CV_32FC1, (float*)image); - CvMat cvK; cvInitMatHeader(&cvK, ksize, 1, CV_32FC1, (float*)kernel ); + CvMat cvK; cvInitMatHeader(&cvK, ksize, 1, CV_32FC1, (float*)kernel); cvFilter2D( &cvI, &cvI, &cvK ); } inline void conv_vertical( double* image, int h, int w, double* kernel, int ksize ) { CvMat cvI; cvInitMatHeader(&cvI, h, w, CV_64FC1, (double*)image); - CvMat cvK; cvInitMatHeader(&cvK, ksize, 1, CV_64FC1, (double*)kernel ); + CvMat cvK; cvInitMatHeader(&cvK, ksize, 1, CV_64FC1, (double*)kernel); cvFilter2D( &cvI, &cvI, &cvK ); } @@ -792,7 +792,7 @@ private: T* layers = zeros(layer_no * data_size); // smooth the data matrix - T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); + T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false ); T *dx = new T[data_size]; T *dy = new T[data_size]; @@ -853,23 +853,23 @@ private: // be careful, 'data' is destroyed afterwards template inline // original T* workspace=0 was removed - void layered_gradient( T* data, int h, int w, int layer_no, T* layers, int lwork=0 ) + void layered_gradient( T* data, int h, int w, int layer_no, T* layers, int lwork = 0 ) { int data_size = h * w; CV_Assert(layers!=NULL); memset(layers,0,sizeof(T)*data_size*layer_no); - bool empty=false; + bool was_empty = false; T* work=NULL; if( lwork < 3*data_size ) { work = new T[3*data_size]; - empty=true; + was_empty = true; } // // smooth the data matrix // T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); float kernel[5]; gaussian_1d(kernel, 5, 0.5, 0); - memcpy( work, data, sizeof(T)*data_size); + memcpy( work, data, sizeof(T)*data_size ); convolve_sym( work, h, w, kernel, 5 ); T *dx = work+data_size; @@ -894,7 +894,7 @@ private: else layer_l[index] = 0; } } - if( empty ) delete []work; + if( was_empty ) delete []work; } // casts a type T2 array into a type T1 array. @@ -915,7 +915,7 @@ private: // to be an odd number. if in_place=true, then T1 must be equal // to T2 naturally. template inline - T1* blur_gaussian_2d( T2* array, int rn, int cn, float sigma, int kernel_size=0, bool in_place=false ) + T1* blur_gaussian_2d( T2* array, int rn, int cn, float sigma, int kernel_size = 0, bool in_place = false ) { T1* out = NULL; @@ -1167,7 +1167,7 @@ void DAISY_Impl::set_cube_gaussians( double* sigma_array, int sz ) g_cube_number = sz; if( m_cube_sigmas ) deallocate( m_cube_sigmas ); - m_cube_sigmas = allocate(g_cube_number); + m_cube_sigmas = allocate( g_cube_number ); for( int r=0; r(g_grid_orientation_resolution, m_grid_point_number*2 ); + m_oriented_grid_points = allocate( g_grid_orientation_resolution, m_grid_point_number*2 ); for( int i=0; i=0 && x>=0 ); descriptor = &(m_dense_descriptors[(y*m_w+x)*m_descriptor_size]); } -inline void DAISY_Impl::get_descriptor(double y, double x, int orientation, float* descriptor ) +inline void DAISY_Impl::get_descriptor( double y, double x, int orientation, float* descriptor ) { get_unnormalized_descriptor(y, x, orientation, descriptor ); normalize_descriptor(descriptor, m_nrm_type); } -inline void DAISY_Impl::get_unnormalized_descriptor(double y, double x, int orientation, float* descriptor ) +inline void DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) { if( m_disable_interpolation ) ni_get_descriptor(y,x,orientation,descriptor); else i_get_descriptor(y,x,orientation,descriptor); } -inline void DAISY_Impl::i_get_descriptor(double y, double x, int orientation, float* descriptor ) +inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, float* descriptor ) { // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); // @@ -1689,7 +1689,7 @@ inline void DAISY_Impl::i_get_descriptor(double y, double x, int orientation, fl } } -inline void DAISY_Impl::ni_get_descriptor(double y, double x, int orientation, float* descriptor ) +inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, float* descriptor ) { // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); // @@ -1738,20 +1738,20 @@ inline void DAISY_Impl::ni_get_descriptor(double y, double x, int orientation, f } // Warped get_descriptor's -inline bool DAISY_Impl::get_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +inline bool DAISY_Impl::get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) { bool rval = get_unnormalized_descriptor(y,x,orientation, H, descriptor); if( rval ) normalize_descriptor(descriptor, m_nrm_type); return rval; } -inline bool DAISY_Impl::get_unnormalized_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +inline bool DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) { if( m_disable_interpolation ) return ni_get_descriptor(y,x,orientation,H,descriptor); else return i_get_descriptor(y,x,orientation,H,descriptor); } -inline bool DAISY_Impl::i_get_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) { // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); // @@ -1806,7 +1806,7 @@ inline bool DAISY_Impl::i_get_descriptor(double y, double x, int orientation, do return true; } -inline bool DAISY_Impl::ni_get_descriptor(double y, double x, int orientation, double* H, float* descriptor ) +inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) { // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); // From 01a49e7988660fa19784bcdeae04a176a3771c94 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 17:11:33 +0300 Subject: [PATCH 03/14] Fix win64 warnings. --- modules/xfeatures2d/src/daisy.cpp | 91 ++++++++++++++++--------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 62f42bf6e..2e9505e68 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -462,7 +462,7 @@ private: for( int x=-sz; x<=sz; x++ ) { counter++; - fltr[counter] = exp((-(x-mean)*(x-mean))/v); + fltr[counter] = exp((-((float)x-mean)*((float)x-mean))/v); sum += fltr[counter]; } @@ -690,8 +690,8 @@ private: template inline float l2norm( T y0, T x0, T y1, T x1 ) { - float d0 = x0 - x1; - float d1 = y0 - y1; + float d0 = (float) (x0 - x1); + float d1 = (float) (y0 - y1); return sqrt( d0*d0 + d1*d1 ); } @@ -768,7 +768,7 @@ private: template inline void divide(T1* arr, int sz, T2 num ) { - float inv_num = 1.0 / num; + float inv_num = (float) (1.0 / num); for( int i=0; i0 && x0 && x0 && y0 && y= left && center >= right); - float den = (left - 2.0 * center + right); + float den = (float) (left - 2.0 * center + right); if( den == 0 ) return 0; - else return 0.5*(left -right)/den; + else return (float) (0.5*(left -right)/den); } int DAISY_Impl::filter_size( double sigma ) @@ -1357,7 +1357,7 @@ void DAISY_Impl::compute_scales() int imsz = m_w * m_h; - float sigma = pow( g_sigma_step, g_scale_st)*g_sigma_0; + float sigma = (float) ( pow( g_sigma_step, g_scale_st)*g_sigma_0 ); float* sim = blur_gaussian_2d( m_image, m_h, m_w, sigma, filter_size(sigma), false); @@ -1375,10 +1375,10 @@ void DAISY_Impl::compute_scales() float sigma_new; float sigma_inc; - sigma_prev = g_sigma_0; + sigma_prev = (float) g_sigma_0; for( i=0; i max_dog[p] ) { max_dog[p] = dog; - m_scale_map[p] = i; + m_scale_map[p] = (float) i; } } deallocate( sim ); @@ -1408,7 +1408,7 @@ void DAISY_Impl::compute_scales() #endif for( int q=0; q 0.5 ) ihx++; int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; @@ -1855,7 +1855,7 @@ inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, { point_transform_via_homography(H, gx+m_cube_sigmas[g_selected_cubes[r]], gy, rx, ry); radius = l2norm( ry, rx, hy, hx ); - hradius[r] = quantize_radius( radius ); + hradius[r] = quantize_radius( (float) radius ); } ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; @@ -1972,21 +1972,21 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O if ( m_mode == ONLY_KEYS ) { cv::Mat descriptors; - _descriptors.create( keypoints.size(), m_descriptor_size, CV_32F ); + _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); descriptors = _descriptors.getMat(); if ( H.empty() ) - for (size_t k = 0; k < keypoints.size(); k++) + for (int k = 0; k < (int) keypoints.size(); k++) { get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, - m_use_orientation ? keypoints[k].angle : 0, + m_use_orientation ? (int) keypoints[k].angle : 0, &descriptors.at( k, 0 ) ); } else - for (size_t k = 0; k < keypoints.size(); k++) + for (int k = 0; k < (int) keypoints.size(); k++) { get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, - m_use_orientation ? keypoints[k].angle : 0, + m_use_orientation ? (int) keypoints[k].angle : 0, &H.at( 0 ), &descriptors.at( k, 0 ) ); } @@ -2025,11 +2025,12 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_descriptor_memory = false; m_workspace_memory = false; - m_descriptor_normalization_threshold = 0.154; // sift magical number m_cube_size = 0; m_layer_size = 0; + m_descriptor_normalization_threshold = 0.154f; // sift magical number + } // destructor From 6a1bf77c1ef61aeb5c1fbb565518e1a937b9a919 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 20:50:22 +0300 Subject: [PATCH 04/14] Fix win64 warnings (#2). --- modules/xfeatures2d/src/daisy.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 2e9505e68..a9264ab4f 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -84,14 +84,15 @@ class DAISY_Impl : public DAISY public: /** Constructor - @param radius radius of the descriptor at the initial scale - @param q_radius amount of radial range divisions - @param q_theta amount of angular range divisions - @param q_hist amount of gradient orientations range divisions - @param mode computation of descriptors - @param norm normalization type - @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image - @param interpolation switch to disable interpolation at minor costs of quality (default is true) + * @param radius radius of the descriptor at the initial scale + * @param q_radius amount of radial range divisions + * @param q_theta amount of angular range divisions + * @param q_hist amount of gradient orientations range divisions + * @param mode computation of descriptors + * @param norm normalization type + * @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image + * @param interpolation switch to disable interpolation at minor costs of quality (default is true) + * @param use_orientation sample patterns using keypoints orientation, disabled by default. */ explicit DAISY_Impl(float radius=15, int q_radius=3, int q_theta=8, int q_hist=8, int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, InputArray H = noArray(), @@ -138,7 +139,7 @@ protected: int m_nrm_type; // holds optional H matrix - InputArray m_h_matrix; + Mat m_h_matrix; // input image. float* m_image; @@ -1880,7 +1881,7 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O return; // get homography if supplied - Mat H = m_h_matrix.getMat(); + Mat H = m_h_matrix; // convert to float if case if ( image.depth() != CV_64F ) @@ -1998,7 +1999,7 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, int _mode, int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) : m_mode(_mode), m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), - m_nrm_type(_norm), m_h_matrix(_H), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) + m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) { m_w = 0; m_h = 0; @@ -2031,6 +2032,7 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_descriptor_normalization_threshold = 0.154f; // sift magical number + m_h_matrix = _H.getMat(); } // destructor From 96f1e2566096e3c95098b95273aef8b271a36070 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 15:17:29 +0300 Subject: [PATCH 05/14] Squash all commits into one. --- modules/xfeatures2d/doc/xfeatures2d.bib | 11 + .../include/opencv2/xfeatures2d.hpp | 31 + modules/xfeatures2d/perf/perf_daisy.cpp | 33 + modules/xfeatures2d/src/daisy.cpp | 2057 +++++++++++++++++ modules/xfeatures2d/test/test_features2d.cpp | 7 + .../test_rotation_and_scale_invariance.cpp | 18 + 6 files changed, 2157 insertions(+) create mode 100644 modules/xfeatures2d/perf/perf_daisy.cpp create mode 100644 modules/xfeatures2d/src/daisy.cpp diff --git a/modules/xfeatures2d/doc/xfeatures2d.bib b/modules/xfeatures2d/doc/xfeatures2d.bib index 0d3918923..9d2f74190 100644 --- a/modules/xfeatures2d/doc/xfeatures2d.bib +++ b/modules/xfeatures2d/doc/xfeatures2d.bib @@ -62,3 +62,14 @@ year={2012} publisher={NIPS} } + +@article{Tola10, + author = "E. Tola and V. Lepetit and P. Fua", + title = {{DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo}}, + journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence", + year = 2010, + month = "May", + pages = "815--830", + volume = "32", + number = "5" +} diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 7ac50d9ee..bb54e7ce4 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -191,6 +191,37 @@ public: static Ptr create(const int lucid_kernel, const int blur_kernel); }; +/** @brief Class implementing DAISY descriptor, described in @cite Tola10 + +@param radius radius of the descriptor at the initial scale +@param q_radius amount of radial range division quantity +@param q_theta amount of angular range division quantity +@param q_hist amount of gradient orientations range division quantity +@param mode choose computation mode of descriptors where +DAISY::ONLY_KEYS means to compute descriptors only for keypoints in the list (default) and +DAISY::COMP_FULL will compute descriptors for all pixels in the given image +@param norm choose descriptors normalization type, where +DAISY::NRM_NONE will not do any normalization (default), +DAISY::NRM_PARTIAL mean that histograms are normalized independently for L2 norm equal to 1.0, +DAISY::NRM_FULL mean that descriptors are normalized for L2 norm equal to 1.0, +DAISY::NRM_SIFT mean that descriptors are normalized for L2 norm equal to 1.0 but no individual one is bigger than 0.154 as in SIFT +@param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image +@param interpolation switch to disable interpolation for speed improvement at minor quality loss +@param use_orientation sample patterns using keypoints orientation, disabled by default. + + */ +class CV_EXPORTS DAISY : public DescriptorExtractor +{ +public: + enum + { + ONLY_KEYS = 0, COMP_FULL = 1, + NRM_NONE = 100, NRM_PARTIAL = 101, NRM_FULL = 102, NRM_SIFT = 103, + }; + static Ptr create( float radius = 15, int q_radius = 3, int q_theta = 8, + int q_hist = 8, int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, + InputArray H = noArray() , bool interpolation = true, bool use_orientation = false ); +}; //! @} diff --git a/modules/xfeatures2d/perf/perf_daisy.cpp b/modules/xfeatures2d/perf/perf_daisy.cpp new file mode 100644 index 000000000..bb60b8e78 --- /dev/null +++ b/modules/xfeatures2d/perf/perf_daisy.cpp @@ -0,0 +1,33 @@ +#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 daisy; + +#define DAISY_IMAGES \ + "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ + "stitching/a3.png" + +PERF_TEST_P(daisy, extract, testing::Values(DAISY_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); + + // use DAISY in COMP_FULL mode (every pixel, dense baseline mode) + Ptr descriptor = DAISY::create(15, 3, 8, 8, DAISY::COMP_FULL, DAISY::NRM_NONE, noArray(), true, false); + + vector points; + vector descriptors; + TEST_CYCLE() descriptor->compute(frame, points, descriptors); + + SANITY_CHECK(descriptors, 1e-4); +} diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp new file mode 100644 index 000000000..a9264ab4f --- /dev/null +++ b/modules/xfeatures2d/src/daisy.cpp @@ -0,0 +1,2057 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009 + * Engin Tola + * web : http://www.engintola.com + * email : engin.tola+libdaisy@gmail.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 Willow Garage 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. + *********************************************************************/ + +/* + "DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo" + by Engin Tola, Vincent Lepetit and Pascal Fua. IEEE Transactions on + Pattern Analysis and achine Intelligence, 31 Mar. 2009. + IEEE computer Society Digital Library. IEEE Computer Society, + http:doi.ieeecomputersociety.org/10.1109/TPAMI.2009.77 + + "A fast local descriptor for dense matching" by Engin Tola, Vincent + Lepetit, and Pascal Fua. Intl. Conf. on Computer Vision and Pattern + Recognition, Alaska, USA, June 2008 + + OpenCV port by: Cristian Balint + */ + +#include "precomp.hpp" +#include "opencv2/imgproc/imgproc_c.h" + +#include +#include + +namespace cv +{ +namespace xfeatures2d +{ + +// constants +const double g_sigma_0 = 1; +const double g_sigma_1 = sqrt(2.0); +const double g_sigma_2 = 8; +const double g_sigma_step = std::pow(2,1.0/2); +const int g_scale_st = int( (log(g_sigma_1/g_sigma_0)) / log(g_sigma_step) ); +static int g_scale_en = 1; + +const double g_sigma_init = 1.6; +const static int g_grid_orientation_resolution = 360; + +static const int MAX_CUBE_NO = 64; +static const int MAX_NORMALIZATION_ITER = 5; + +int g_cube_number; +int g_selected_cubes[MAX_CUBE_NO]; // m_rad_q_no < MAX_CUBE_NO + +/* + !DAISY implementation + */ +class DAISY_Impl : public DAISY +{ + +public: + /** Constructor + * @param radius radius of the descriptor at the initial scale + * @param q_radius amount of radial range divisions + * @param q_theta amount of angular range divisions + * @param q_hist amount of gradient orientations range divisions + * @param mode computation of descriptors + * @param norm normalization type + * @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image + * @param interpolation switch to disable interpolation at minor costs of quality (default is true) + * @param use_orientation sample patterns using keypoints orientation, disabled by default. + */ + explicit DAISY_Impl(float radius=15, int q_radius=3, int q_theta=8, int q_hist=8, + int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false); + + virtual ~DAISY_Impl(); + + /** returns the descriptor length in bytes */ + virtual int descriptorSize() const { + // +1 is for center pixel + return ( (m_rad_q_no * m_th_q_no + 1) * m_hist_th_q_no ); + }; + /** returns the descriptor type */ + virtual int descriptorType() const { return CV_32F; } + /** returns the default norm type */ + virtual int defaultNorm() const { return NORM_L2; } + + // main compute routine + virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ); + +protected: + + /* + * DAISY parameters + */ + + // operation mode + int m_mode; + + // maximum radius of the descriptor region. + float m_rad; + + // the number of quantizations of the radius. + int m_rad_q_no; + + // the number of quantizations of the angle. + int m_th_q_no; + + // the number of quantizations of the gradient orientations. + int m_hist_th_q_no; + + // holds the type of the normalization to apply; equals to NRM_PARTIAL by + // default. change the value using set_normalization() function. + int m_nrm_type; + + // holds optional H matrix + Mat m_h_matrix; + + // input image. + float* m_image; + + // image height + int m_h; + + // image width + int m_w; + + // stores the descriptors : its size is [ m_w * m_h * m_descriptor_size ]. + float* m_dense_descriptors; + + // stores the layered gradients in successively smoothed form: layer[n] = + // m_gradient_layers * gaussian( sigma_n ); n>= 1; layer[0] is the layered_gradient + float* m_smoothed_gradient_layers; + + // if set to true, descriptors are scale invariant + bool m_scale_invariant; + + // if set to true, descriptors are rotation invariant + bool m_rotation_invariant; + + // number of bins in the histograms while computing orientation + int m_orientation_resolution; + + // hold the scales of the pixels + float* m_scale_map; + + // holds the orientaitons of the pixels + int* m_orientation_map; + + // Holds the oriented coordinates (y,x) of the grid points of the region. + double** m_oriented_grid_points; + + // holds the gaussian sigmas for radius quantizations for an incremental + // application + double* m_cube_sigmas; + + bool m_descriptor_memory; + bool m_workspace_memory; + + // the number of grid locations + int m_grid_point_number; + + // the size of the descriptor vector + int m_descriptor_size; + + // holds the amount of shift that's required for histogram computation + double m_orientation_shift_table[360]; + + // if enabled, descriptors are computed with casting non-integer locations + // to integer positions otherwise we use interpolation. + bool m_disable_interpolation; + + // switch to enable sample by keypoints orientation + bool m_use_orientation; + + // size of m_hsz layers at a single sigma: m_hsz * m_layer_size + int m_cube_size; + + // size of the layer : m_h*m_w + int m_layer_size; + + /* + * DAISY functions + */ + + // computes the histogram at yx; the size of histogram is m_hist_th_q_no + void compute_histogram( float* hcube, int y, int x, float* histogram ); + + // reorganizes the cube data so that histograms are sequential in memory. + void compute_histograms(); + + // emulates the way sift is normalized. + void normalize_sift_way( float* desc ); + + // normalizes the descriptor histogram by histogram + void normalize_partial( float* desc ); + + // normalizes the full descriptor. + void normalize_full( float* desc ); + + // initializes the class: computes gradient and structure-points + void initialize(); + + void update_selected_cubes(); + + int quantize_radius( float rad ); + + int filter_size( double sigma ); + + // computes scales for every pixel and scales the structure grid so that the + // resulting descriptors are scale invariant. you must set + // m_scale_invariant flag to 1 for the program to call this function + void compute_scales(); + + // Return a number in the range [-0.5, 0.5] that represents the location of + // the peak of a parabola passing through the 3 evenly spaced samples. The + // center value is assumed to be greater than or equal to the other values + // if positive, or less than if negative. + float interpolate_peak( float left, float center, float right ); + + // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram + // is connected in a circular buffer. + void smooth_histogram( float *hist, int bins ); + + // computes pixel orientations and rotates the structure grid so that + // resulting descriptors are rotation invariant. If the scales is also + // detected, then orientations are computed at the computed scales. you must + // set m_rotation_invariant flag to 1 for the program to call this function + void compute_orientations(); + + // the clipping threshold to use in normalization: values above this value + // are clipped to this value for normalize_sift_way() function + float m_descriptor_normalization_threshold; + + // computes the sigma's of layers from descriptor parameters if the user did + // not sets it. these define the size of the petals of the descriptor. + void compute_cube_sigmas(); + + // Computes the locations of the unscaled unrotated points where the + // histograms are going to be computed according to the given parameters. + void compute_grid_points(); + + // Computes the locations of the unscaled rotated points where the + // histograms are going to be computed according to the given parameters. + void compute_oriented_grid_points(); + + // smooths each of the layers by a Gaussian having "sigma" standart + // deviation. + void smooth_layers( float*layers, int h, int w, int layer_number, float sigma ); + + // Holds the coordinates (y,x) of the grid points of the region. + double** m_grid_points; + + int get_hq() { return m_hist_th_q_no; } + int get_thq() { return m_th_q_no; } + int get_rq() { return m_rad_q_no; } + float get_rad() { return m_rad; } + + // sets the type of the normalization to apply out of {NRM_PARTIAL, + // NRM_FULL, NRM_SIFT}. Call before using get_descriptor() if you want to + // change the default normalization type. + void set_normalization( int nrm_type ) { m_nrm_type = nrm_type; } + + // applies one of the normalizations (partial,full,sift) to the desciptors. + void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); + + // normalizes histograms individually + void normalize_histograms(); + + // gets the histogram at y,x with 'orientation' from the r'th cube + float* get_histogram( int y, int x, int r ); + + // if called, I don't use interpolation in the computation of + // descriptors. + void disable_interpolation() { m_disable_interpolation = true; } + + // returns the region number. + int grid_point_number() { return m_grid_point_number; } + + // releases all the used memory; call this if you want to process + // multiple images within a loop. + void reset(); + + // releases unused memory after descriptor computation is completed. + void release_auxilary(); + + // computes the descriptors for every pixel in the image. + void compute_descriptors(); + + // returns all the descriptors. + float* get_dense_descriptors(); + + // returns oriented grid points. default is 0 orientation. + double* get_grid(int o=0); + + // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the + // scales for every pixel so that the resulting descriptors are scale + // invariant. + void scale_invariant( bool state = true ) + { + g_scale_en = (int)( (log(g_sigma_2/g_sigma_0)) / log(g_sigma_step) ) - g_scale_st; + m_scale_invariant = state; + } + + // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the + // orientations for every pixel so that the resulting descriptors are + // rotation invariant. orientation steps are 360/ori_resolution + void rotation_invariant( int ori_resolution = 36, bool state = true ) + { + m_rotation_invariant = state; + m_orientation_resolution = ori_resolution; + } + + // sets the gaussian variances manually. must be called before + // initialize() to be considered. must be exact sigma values -> f + // converts to incremental format. + void set_cube_gaussians( double* sigma_array, int sz ); + + int* get_orientation_map() { return m_orientation_map; } + + // call compute_descriptor_memory to find the amount of memory to allocate + void set_descriptor_memory( float* descriptor, long int d_size ); + + // call compute_workspace_memory to find the amount of memory to allocate + void set_workspace_memory( float* workspace, long int w_size ); + + // returns the amount of memory needed for the compute_descriptors() + // function. it is basically equal to imagesize x descriptor_size + int compute_descriptor_memory() { + if( m_h == 0 || m_descriptor_size == 0 ) { + CV_Error( Error::StsInternal, "Image and descriptor size is zero" ); + } + return m_w * m_h * m_descriptor_size; + } + + // returns the amount of memory needed for workspace. call before initialize() + int compute_workspace_memory() { + if( m_cube_size == 0 ) { + CV_Error( Error::StsInternal, "Cube size is zero" ); + } + return (g_cube_number+1)* m_cube_size; + } + + void normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) + { + if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; + else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); + else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); + else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); + else + CV_Error( Error::StsInternal, "No such normalization" ); + } + + // transform a point via the homography + void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) + { + double kxp = H[0]*x + H[1]*y + H[2]; + double kyp = H[3]*x + H[4]*y + H[5]; + double kp = H[6]*x + H[7]*y + H[8]; + u = kxp / kp; + v = kyp / kp; + } + +private: + + // returns the descriptor vector for the point (y, x) !!! use this for + // precomputed operations meaning that you must call compute_descriptors() + // before calling this function. if you want normalized descriptors, call + // normalize_descriptors() before calling compute_descriptors() + inline void get_descriptor( int y, int x, float* &descriptor ); + + // computes the descriptor and returns the result in 'descriptor' ( allocate + // 'descriptor' memory first ie: float descriptor = new + // float[m_descriptor_size]; -> the descriptor is normalized. + inline void get_descriptor( double y, double x, int orientation, float* descriptor ); + + // computes the descriptor and returns the result in 'descriptor' ( allocate + // 'descriptor' memory first ie: float descriptor = new + // float[m_descriptor_size]; -> the descriptor is NOT normalized. + inline void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ); + + // computes the descriptor at homography-warped grid. (y,x) is not the + // coordinates of this image but the coordinates of the original grid where + // the homography will be applied. Meaning that the grid is somewhere else + // and we warp this grid with H and compute the descriptor on this warped + // grid; returns null/false if centers falls outside the image; allocate + // 'descriptor' memory first. descriptor is normalized. + inline bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor); + + // computes the descriptor at homography-warped grid. (y,x) is not the + // coordinates of this image but the coordinates of the original grid where + // the homography will be applied. Meaning that the grid is somewhere else + // and we warp this grid with H and compute the descriptor on this warped + // grid; returns null/false if centers falls outside the image; allocate + // 'descriptor' memory first. descriptor is NOT normalized. + inline bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + + // compute the smoothed gradient layers. + inline void compute_smoothed_gradient_layers(); + + // does not use interpolation while computing the histogram. + inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ); + + // returns the interpolated histogram: picks either bi_get_histogram or + // ti_get_histogram depending on 'shift' + inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ); + + // records the histogram that is computed by bilinear interpolation + // regarding the shift in the spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ); + + // records the histogram that is computed by trilinear interpolation + // regarding the shift in layers and spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ); + + // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor + inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ); + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ); + + // uses interpolation for no interpolation call ni_get_descriptor. see also get_descriptor + inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + + // creates a 1D gaussian filter with N(mean,sigma). + inline void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) + { + CV_Assert(fltr != NULL); + int sz = (fsz-1)/2; + int counter=-1; + float sum = 0.0; + float v = 2*sigma*sigma; + for( int x=-sz; x<=sz; x++ ) + { + counter++; + fltr[counter] = exp((-((float)x-mean)*((float)x-mean))/v); + sum += fltr[counter]; + } + + if( sum != 0 ) + { + for( int x=0; x + class rectangle + { + public: + T lx, ux, ly, uy; + T dx, dy; + rectangle(T xl, T xu, T yl, T yu) { lx=xl; ux=xu; ly=yl; uy=yu; dx=ux-lx; dy=uy-ly; }; + rectangle() { lx = ux = ly = uy = dx = dy = 0; }; + }; + + // checks if the number x is between lx - ux interval. + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + { + if( ( ((lx lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='&') + { + switch( oper ) + { + case '|': + if( is_inside(x,lx,ux,le,ue) || is_inside(y,ly,uy,le,ue) ) + return true; + return false; + + default: + if( is_inside(x,lx,ux,le,ue) && is_inside(y,ly,uy,le,ue) ) + return true; + return false; + } + } + + // checks if the number x is between lx - ux and/or y is between ly - uy interval. + // If the number is inside, then function returns true, else it returns false. + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline + bool is_inside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='&') + { + switch( oper ) + { + case '|': + if( is_inside(x,roi.lx,roi.ux,le,ue) || is_inside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + + default: + if( is_inside(x,roi.lx,roi.ux,le,ue) && is_inside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + } + } + + // checks if the number x is outside lx - ux interval + // the equality is checked depending on the value of le and ue parameters. + // if le=1 => lx>x is checked else lx>=x is checked + // if ue=1 => x>ux is checked else x>=ux is checked + // by default is x is searched outside of [lx,ux) + template inline + bool is_outside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + { + return !(is_inside(x,lx,ux,le,ue)); + } + + // checks if the numbers x and y is outside their intervals. + // The equality is checked depending on the value of le and ue parameters. + // If le=1 => lx>x is checked else lx>=x is checked + // If ue=1 => x>ux is checked else x>=ux is checked + // By default is x is searched outside of [lx,ux) (Similarly for y) + // By default, 'oper' is set to OR. If one of them is outside it returns + // true otherwise false. + template inline + bool is_outside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='|') + { + switch( oper ) + { + case '&': + if( is_outside(x,lx,ux,le,ue) && is_outside(y,ly,uy,le,ue) ) + return true; + return false; + default: + if( is_outside(x,lx,ux,le,ue) || is_outside(y,ly,uy,le,ue) ) + return true; + return false; + } + } + + // checks if the numbers x and y is outside their intervals. + // The equality is checked depending on the value of le and ue parameters. + // If le=1 => lx>x is checked else lx>=x is checked + // If ue=1 => x>ux is checked else x>=ux is checked + // By default is x is searched outside of [lx,ux) (Similarly for y) + // By default, 'oper' is set to OR. If one of them is outside it returns + // true otherwise false. + template inline + bool is_outside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='|') + { + switch( oper ) + { + case '&': + if( is_outside(x,roi.lx,roi.ux,le,ue) && is_outside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + default: + if( is_outside(x,roi.lx,roi.ux,le,ue) || is_outside(y,roi.ly,roi.uy,le,ue) ) + return true; + return false; + } + } + + // computes the square of a number and returns it. + template inline + T square(T a) + { + return a*a; + } + + // computes the square of an array. if in_place is enabled, the + // result is returned in the array arr. + template inline + T* square(T* arr, int sz, bool in_place=false) + { + T* out; + if( in_place ) out = arr; + else out = allocate(sz); + + for( int i=0; i inline + float l2norm( T* a, int sz) + { + float norm=0; + for( int k=0; k inline + float l2norm( T1* a, T2* b, int sz) + { + float norm=0; + for( int i=0; i inline + float l2norm( T y0, T x0, T y1, T x1 ) + { + float d0 = (float) (x0 - x1); + float d1 = (float) (y0 - y1); + + return sqrt( d0*d0 + d1*d1 ); + } + + // allocates a memory of size sz and returns a pointer to the array + template inline + T* allocate(const int sz) + { + T* array = new T[sz]; + return array; + } + + // allocates a memory of size ysz x xsz and returns a double pointer to it + template inline + T** allocate(const int ysz, const int xsz) + { + T** mat = new T*[ysz]; + int i; + + for(i=0; i(xsz); + + return mat; + } + + // deallocates the memory and sets the pointer to null. + template inline + void deallocate(T* &array) + { + delete[] array; + array = NULL; + } + + // deallocates the memory and sets the pointer to null. + template inline + void deallocate(T** &mat, int ysz) + { + if( mat == NULL ) return; + + for(int i=0; i inline + void polar2cartesian(T1 r, T1 t, T2 &y, T2 &x) + { + x = (T2)( r * cos( t ) ); + y = (T2)( r * sin( t ) ); + } + + + template inline + void convolve_sym_( T* image, int h, int w, T* kernel, int ksize ) + { + conv_horizontal( image, h, w, kernel, ksize ); + conv_vertical ( image, h, w, kernel, ksize ); + } + + template + inline void convolve_sym( T* image, int h, int w, T* kernel, int ksize, T* out=NULL ) + { + if( out == NULL ) out = image; + else memcpy( out, image, sizeof(T)*h*w ); + convolve_sym_(out, h, w, kernel, ksize); + } + + // divides the elements of the array with num + template inline + void divide(T1* arr, int sz, T2 num ) + { + float inv_num = (float) (1.0 / num); + + for( int i=0; i inline + T* zeros(int r) + { + T* data = allocate(r); + memset( data, 0, sizeof(T)*r ); + return data; + } + + template inline + T* layered_gradient( T* data, int h, int w, int layer_no=8 ) + { + int data_size = h * w; + T* layers = zeros(layer_no * data_size); + + // smooth the data matrix + T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false ); + + T *dx = new T[data_size]; + T *dy = new T[data_size]; + gradient(bdata, h, w, dy, dx); + deallocate( bdata ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + deallocate(dy); + deallocate(dx); + + return layers; + } + + // computes the gradient of an image and returns the result in + // pointers to REAL. + template inline + void gradient(T* im, int h, int w, T* dy, T* dx) + { + CV_Assert( dx != NULL ); + CV_Assert( dy != NULL ); + + for( int y=0; y0 && x0 && y inline + // original T* workspace=0 was removed + void layered_gradient( T* data, int h, int w, int layer_no, T* layers, int lwork = 0 ) + { + int data_size = h * w; + CV_Assert(layers!=NULL); + memset(layers,0,sizeof(T)*data_size*layer_no); + + bool was_empty = false; + T* work=NULL; + if( lwork < 3*data_size ) { + work = new T[3*data_size]; + was_empty = true; + } + + // // smooth the data matrix + // T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); + float kernel[5]; gaussian_1d(kernel, 5, 0.5, 0); + memcpy( work, data, sizeof(T)*data_size ); + convolve_sym( work, h, w, kernel, 5 ); + + T *dx = work+data_size; + T *dy = work+2*data_size; + gradient( work, h, w, dy, dx ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + if( was_empty ) delete []work; + } + + // casts a type T2 array into a type T1 array. + template inline + T1* type_cast(T2* data, int sz) + { + T1* out = new T1[sz]; + + for( int i=0; i inline + T1* blur_gaussian_2d( T2* array, int rn, int cn, float sigma, int kernel_size = 0, bool in_place = false ) + { + T1* out = NULL; + + if( in_place ) + out = (T1*)array; + else + out = type_cast(array,rn*cn); + + if( kernel_size == 0 ) + kernel_size = (int)(3*sigma); + + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size= 3; // kernel size cannot be smaller than 3 + + float* kernel = new float[kernel_size]; + gaussian_1d(kernel, kernel_size, sigma, 0); + + // !! apply the filter separately + convolve_sym( out, rn, cn, kernel, kernel_size ); + // conv_horizontal( out, rn, cn, kernel, kernel_size); + // conv_vertical ( out, rn, cn, kernel, kernel_size); + + deallocate(kernel); + return out; + } + +}; // END DAISY_Impl CLASS + + +// ------------------------------------------------- +/* DAISY computation routines */ + +float* DAISY_Impl::get_histogram( int y, int x, int r ) +{ + CV_Assert( y >= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + return m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size + (y*m_w+x)*m_hist_th_q_no; + // i_get_histogram( histogram, y, x, 0, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); +} + + +float* DAISY_Impl::get_dense_descriptors() +{ + return m_dense_descriptors; +} + +double* DAISY_Impl::get_grid(int o) +{ + CV_Assert( o >= 0 && o < 360 ); + return m_oriented_grid_points[o]; +} + +void DAISY_Impl::reset() +{ + deallocate( m_image ); + // deallocate( m_grid_points, m_grid_point_number ); + // deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + // deallocate( m_cube_sigmas ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + if( !m_descriptor_memory ) deallocate( m_dense_descriptors ); + if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); +} + +void DAISY_Impl::release_auxilary() +{ + deallocate( m_image ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + + if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); + + deallocate( m_grid_points, m_grid_point_number ); + deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + deallocate( m_cube_sigmas ); +} + +void DAISY_Impl::compute_grid_points() +{ + double r_step = m_rad / (double)m_rad_q_no; + double t_step = 2*CV_PI/ m_th_q_no; + + if( m_grid_points ) + deallocate( m_grid_points, m_grid_point_number ); + + m_grid_points = allocate(m_grid_point_number, 2); + for( int y=0; y(m_h*m_w*m_descriptor_size); + + memset(m_dense_descriptors, 0, sizeof(float)*m_h*m_w*m_descriptor_size); + + int y, x, index, orientation; +#if defined _OPENMP +#pragma omp parallel for private(y,x,index,orientation) +#endif + for( y=0; y= 0 && orientation < g_grid_orientation_resolution ) ) orientation = 0; + get_unnormalized_descriptor( y, x, orientation, &(m_dense_descriptors[index*m_descriptor_size]) ); + } + } +} + +void DAISY_Impl::smooth_layers( float* layers, int h, int w, int layer_number, float sigma ) +{ + int fsz = filter_size(sigma); + float* filter = new float[fsz]; + gaussian_1d(filter, fsz, sigma, 0); + int i; + float* layer=0; +#if defined _OPENMP +#pragma omp parallel for private(i, layer) +#endif + for( i=0; i 1e-5 ) + divide( desc, m_descriptor_size, norm); + + for( h=0; h m_descriptor_normalization_threshold ) + { + desc[ h ] = m_descriptor_normalization_threshold; + changed = true; + } + } + } +} + +void DAISY_Impl::normalize_descriptors( int nrm_type ) +{ + int number_of_descriptors = m_h * m_w; + int d; + +#if defined _OPENMP +#pragma omp parallel for private(d) +#endif + for( d=0; d(g_cube_number); + + double r_step = double(m_rad)/m_rad_q_no; + for( int r=0; r< m_rad_q_no; r++ ) + { + m_cube_sigmas[r] = (r+1)*r_step/2; + } + } + update_selected_cubes(); +} + +void DAISY_Impl::set_cube_gaussians( double* sigma_array, int sz ) +{ + g_cube_number = sz; + + if( m_cube_sigmas ) deallocate( m_cube_sigmas ); + m_cube_sigmas = allocate( g_cube_number ); + + for( int r=0; r= m_cube_sigmas[g_cube_number-1] ) return g_cube_number-1; + + float dist; + float mindist=FLT_MAX; + int mini=0; + for( int c=0; c( g_grid_orientation_resolution, m_grid_point_number*2 ); + + for( int i=0; i= left && center >= right); + + float den = (float) (left - 2.0 * center + right); + + if( den == 0 ) return 0; + else return (float) (0.5*(left -right)/den); +} + +int DAISY_Impl::filter_size( double sigma ) +{ + int fsz = (int)(5*sigma); + + // kernel size must be odd + if( fsz%2 == 0 ) fsz++; + + // kernel size cannot be smaller than 3 + if( fsz < 3 ) fsz = 3; + + return fsz; +} + +void DAISY_Impl::compute_scales() +{ + //############################################################################### + //# scale detection is work-in-progress! do not use it if you're not Engin Tola # + //############################################################################### + + int imsz = m_w * m_h; + + float sigma = (float) ( pow( g_sigma_step, g_scale_st)*g_sigma_0 ); + + float* sim = blur_gaussian_2d( m_image, m_h, m_w, sigma, filter_size(sigma), false); + + float* next_sim = NULL; + + float* max_dog = allocate(imsz); + + m_scale_map = allocate(imsz); + + memset( max_dog, 0, imsz*sizeof(float) ); + memset( m_scale_map, 0, imsz*sizeof(float) ); + + int i; + float sigma_prev; + float sigma_new; + float sigma_inc; + + sigma_prev = (float) g_sigma_0; + for( i=0; i( sim, m_h, m_w, sigma_inc, filter_size( sigma_inc ) , false); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int p=0; p max_dog[p] ) + { + max_dog[p] = dog; + m_scale_map[p] = (float) i; + } + } + deallocate( sim ); + + sim = next_sim; + } + + blur_gaussian_2d( m_scale_map, m_h, m_w, 10.0, filter_size(10), true); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int q=0; q(m_orientation_resolution); + + for( x=0; x max_val ) + { + max_val = hist[ori]; + max_ind = ori; + } + } + + prev = max_ind-1; + if( prev < 0 ) + prev += m_orientation_resolution; + + next = max_ind+1; + if( next >= m_orientation_resolution ) + next -= m_orientation_resolution; + + peak = interpolate_peak(hist[prev], hist[max_ind], hist[next]); + angle = (float)( ((float)max_ind + peak)*360.0/m_orientation_resolution ); + + int iangle = int(angle); + + if( iangle < 0 ) iangle += 360; + if( iangle >= 360 ) iangle -= 360; + + + if( !(iangle >= 0.0 && iangle < 360.0) ) + { + angle = 0; + } + + m_orientation_map[ ind ] = iangle; + } + deallocate(hist); + } + } + + deallocate( rotation_layers ); + + compute_oriented_grid_points(); +} + +void DAISY_Impl::set_descriptor_memory( float* descriptor, long int d_size ) +{ + CV_Assert( m_descriptor_memory == false ); + CV_Assert( m_h*m_w != 0 ); + CV_Assert( d_size >= compute_descriptor_memory() ); + + m_dense_descriptors = descriptor; + m_descriptor_memory = true; +} + +void DAISY_Impl::set_workspace_memory( float* workspace, long int w_size ) +{ + CV_Assert( m_workspace_memory == false ); + CV_Assert( m_h*m_w != 0 ); + CV_Assert( w_size >= compute_workspace_memory() ); + + m_smoothed_gradient_layers = workspace; + m_workspace_memory = true; +} + +// ------------------------------------------------- +/* DAISY helper routines */ + +inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* histogram ) +{ + if( is_outside(x, 0, m_w-1, y, 0, m_h-1) ) return; + + float* spatial_shift = hcube + y * m_w + x; + int data_size = m_w * m_h; + + for( int h=0; h 0.99 ) bi_get_histogram( histogram, y, x, ishift+1, cube ); + else ti_get_histogram( histogram, y, x, shift , cube ); +} + +inline void DAISY_Impl::bi_get_histogram( float* histogram, double y, double x, int shift, float* hcube ) +{ + int mnx = int( x ); + int mny = int( y ); + + if( mnx >= m_w-2 || mny >= m_h-2 ) + { + memset(histogram, 0, sizeof(float)*m_hist_th_q_no); + return; + } + + int ind = mny*m_w+mnx; + // A C --> pixel positions + // B D + float* A = hcube+ind*m_hist_th_q_no; + float* B = A+m_w*m_hist_th_q_no; + float* C = A+m_hist_th_q_no; + float* D = A+(m_w+1)*m_hist_th_q_no; + + double alpha = mnx+1-x; + double beta = mny+1-y; + + float w0 = (float) (alpha*beta); + float w1 = (float) (beta-w0); // (1-alpha)*beta; + float w2 = (float) (alpha-w0); // (1-beta)*alpha; + float w3 = (float) (1+w0-alpha-beta); // (1-beta)*(1-alpha); + + int h; + + for( h=0; h= m_hist_th_q_no ) hi -= m_hist_th_q_no; + histogram[h] = hptr[hi]; + } +} + +inline void DAISY_Impl::get_descriptor( int y, int x, float* &descriptor ) +{ + CV_Assert( m_dense_descriptors != NULL ); + CV_Assert( y=0 && x>=0 ); + descriptor = &(m_dense_descriptors[(y*m_w+x)*m_descriptor_size]); +} + +inline void DAISY_Impl::get_descriptor( double y, double x, int orientation, float* descriptor ) +{ + get_unnormalized_descriptor(y, x, orientation, descriptor ); + normalize_descriptor(descriptor, m_nrm_type); +} + +inline void DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) +{ + if( m_disable_interpolation ) ni_get_descriptor(y,x,orientation,descriptor); + else i_get_descriptor(y,x,orientation,descriptor); +} + +inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, float* descriptor ) +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + // + CV_Assert( y >= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + + i_get_histogram( descriptor, y, x, shift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + + int r, rdt, region; + double yy, xx; + float* histogram = 0; + double* grid = m_oriented_grid_points[orientation]; + + // petals of the flower + for( r=0; r= 0 && y < m_h ); + CV_Assert( x >= 0 && x < m_w ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( m_oriented_grid_points ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; + if( shift - ishift > 0.5 ) ishift++; + + int iy = (int)y; if( y - iy > 0.5 ) iy++; + int ix = (int)x; if( x - ix > 0.5 ) ix++; + + // center + ni_get_histogram( descriptor, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + + double yy, xx; + float* histogram=0; + // petals of the flower + int r, rdt, region; + double* grid = m_oriented_grid_points[orientation]; + for( r=0; r 0.5 ) iy++; + ix = (int)xx; if( xx - ix > 0.5 ) ix++; + + if( is_outside(ix, 0, m_w-1, iy, 0, m_h-1) ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); + } + } +} + +// Warped get_descriptor's +inline bool DAISY_Impl::get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) +{ + bool rval = get_unnormalized_descriptor(y,x,orientation, H, descriptor); + if( rval ) normalize_descriptor(descriptor, m_nrm_type); + return rval; +} + +inline bool DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) +{ + if( m_disable_interpolation ) return ni_get_descriptor(y,x,orientation,H,descriptor); + else return i_get_descriptor(y,x,orientation,H,descriptor); +} + +inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + // + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + point_transform_via_homography( H, x, y, hx, hy ); + if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; + + point_transform_via_homography( H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); + double radius = l2norm( ry, rx, hy, hx ); + hradius[0] = quantize_radius( (float) radius ); + + double shift = m_orientation_shift_table[orientation]; + i_get_histogram( descriptor, hy, hx, shift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + + double gy, gx; + int r, rdt, th, region; + float* histogram=0; + for( r=0; r= 0 && orientation < 360 ); + CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + double radius; + + double hy, hx, ry, rx; + + point_transform_via_homography(H, x, y, hx, hy ); + if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; + + point_transform_via_homography(H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); + radius = l2norm( ry, rx, hy, hx ); + hradius[0] = quantize_radius( (float) radius ); + + int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + int r, rdt, th, region; + double gy, gx; + float* histogram=0; + ni_get_histogram( descriptor, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + for( r=0; r 0.5 ) ihx++; + ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + if( is_outside(ihx, 0, m_w-1, ihy, 0, m_h-1) ) continue; + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[r]*m_cube_size ); + } + } + return true; +} + +// ------------------------------------------------- +/* DAISY interface implementation */ + +void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + Mat image = _image.getMat(); + if( image.empty() ) + return; + + // get homography if supplied + Mat H = m_h_matrix; + + // convert to float if case + if ( image.depth() != CV_64F ) + H.convertTo( H, CV_64F ); + /* + * daisy set_image() + */ + + // base size + m_h = image.rows; + m_w = image.cols; + + // clone image for conversion + if ( image.depth() != CV_32F ) { + + Mat work_image = image.clone(); + + // convert to gray inplace + if( work_image.channels() > 1 ) + cvtColor( work_image, work_image, COLOR_BGR2GRAY ); + + // convert to float if it is necessary + if ( work_image.depth() != CV_32F ) + { + // convert and normalize + work_image.convertTo( work_image, CV_32F ); + work_image /= 255.0f; + } else + CV_Error( Error::StsUnsupportedFormat, "" ); + + // use cloned work image + m_image = work_image.ptr(0); + + } else + // use original CV_32F image + m_image = image.ptr(0); + + // full mode if noArray() + // was passed to _descriptors + if ( _descriptors.needed() == false ) + m_mode = DAISY::COMP_FULL; + + /* + * daisy set_parameters() + */ + + m_grid_point_number = m_rad_q_no * m_th_q_no + 1; // +1 is for center pixel + m_descriptor_size = m_grid_point_number * m_hist_th_q_no; + + for( int i=0; i<360; i++ ) + { + m_orientation_shift_table[i] = i/360.0 * m_hist_th_q_no; + } + m_layer_size = m_h*m_w; + m_cube_size = m_layer_size*m_hist_th_q_no; + + compute_cube_sigmas(); + compute_grid_points(); + + + /* + * daisy initialize_single_descriptor_mode(); + */ + + // initializes for get_descriptor(double, double, int) mode: pre-computes + // convolutions of gradient layers in m_smoothed_gradient_layers + + initialize(); + compute_smoothed_gradient_layers(); + + /* + * daisy compute descriptors given operating mode + */ + + if ( m_mode == COMP_FULL ) + { + CV_Assert( H.empty() ); + CV_Assert( keypoints.empty() ); + CV_Assert( ! m_use_orientation ); + + compute_descriptors(); + normalize_descriptors(); + + cv::Mat descriptors; + descriptors = _descriptors.getMat(); + descriptors = Mat( m_h * m_w, m_descriptor_size, + CV_32F, &m_dense_descriptors[0] ); + } else + if ( m_mode == ONLY_KEYS ) + { + cv::Mat descriptors; + _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); + descriptors = _descriptors.getMat(); + + if ( H.empty() ) + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &descriptors.at( k, 0 ) ); + } + else + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &H.at( 0 ), &descriptors.at( k, 0 ) ); + } + + } else + CV_Error( Error::StsInternal, "Unknown computation mode" ); +} + +// constructor +DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, + int _mode, int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) + : m_mode(_mode), m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), + m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) +{ + m_w = 0; + m_h = 0; + + m_image = 0; + + m_grid_point_number = 0; + m_descriptor_size = 0; + + m_smoothed_gradient_layers = NULL; + m_dense_descriptors = NULL; + m_grid_points = NULL; + m_oriented_grid_points = NULL; + + m_scale_invariant = false; + m_rotation_invariant = false; + + m_scale_map = NULL; + m_orientation_map = NULL; + m_orientation_resolution = 36; + m_scale_map = NULL; + + m_cube_sigmas = NULL; + + m_descriptor_memory = false; + m_workspace_memory = false; + + m_cube_size = 0; + m_layer_size = 0; + + m_descriptor_normalization_threshold = 0.154f; // sift magical number + + m_h_matrix = _H.getMat(); +} + +// destructor +DAISY_Impl::~DAISY_Impl() +{ + if( !m_workspace_memory ) deallocate( m_smoothed_gradient_layers ); + deallocate( m_grid_points, m_grid_point_number ); + deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + deallocate( m_orientation_map ); + deallocate( m_scale_map ); + deallocate( m_cube_sigmas ); +} + +Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, + int mode, int norm, InputArray H, bool interpolation, bool use_orientation) +{ + return makePtr(radius, q_radius, q_theta, q_hist, mode, norm, H, interpolation, use_orientation); +} + + +} // END NAMESPACE XFEATURES2D +} // END NAMESPACE CV diff --git a/modules/xfeatures2d/test/test_features2d.cpp b/modules/xfeatures2d/test/test_features2d.cpp index 303274efa..a01c0a313 100644 --- a/modules/xfeatures2d/test/test_features2d.cpp +++ b/modules/xfeatures2d/test/test_features2d.cpp @@ -1010,6 +1010,13 @@ TEST( Features2d_DescriptorExtractor_SURF, regression ) test.safe_run(); } +TEST( Features2d_DescriptorExtractor_DAISY, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-daisy", 0.05f, + DAISY::create() ); + test.safe_run(); +} + TEST( Features2d_DescriptorExtractor_FREAK, regression ) { // TODO adjust the parameters below diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 46d328205..176a342c5 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -651,6 +651,15 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) +{ + DescriptorRotationInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.79f); + test.safe_run(); +} + /* * Detector's scale invariance check */ @@ -708,3 +717,12 @@ TEST(Features2d_RotationInvariance2_Detector_SURF, regression) ASSERT_LT( fabs(keypoints[1].response - keypoints[3].response), 1e-6); ASSERT_LT( fabs(keypoints[1].response - keypoints[4].response), 1e-6); } + +TEST(Features2d_ScaleInvariance_Descriptor_DAISY, regression) +{ + DescriptorScaleInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.075f); + test.safe_run(); +} From 2d85137ef15d5309d26930fa82e2d0cbe911b6ee Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Sun, 17 May 2015 11:52:25 +0300 Subject: [PATCH 06/14] More fixes, reorganize, fix memleaks, more API tests. --- .../include/opencv2/xfeatures2d.hpp | 68 ++- modules/xfeatures2d/perf/perf_daisy.cpp | 6 +- modules/xfeatures2d/src/daisy.cpp | 532 +++++++++++------- .../test_rotation_and_scale_invariance.cpp | 4 +- 4 files changed, 395 insertions(+), 215 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 4241a1f7b..e5151e001 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -150,7 +150,6 @@ public: @param q_radius amount of radial range division quantity @param q_theta amount of angular range division quantity @param q_hist amount of gradient orientations range division quantity -@param mode choose computation mode of descriptors where DAISY::ONLY_KEYS means to compute descriptors only for keypoints in the list (default) and DAISY::COMP_FULL will compute descriptors for all pixels in the given image @param norm choose descriptors normalization type, where @@ -168,12 +167,73 @@ class CV_EXPORTS DAISY : public DescriptorExtractor public: enum { - ONLY_KEYS = 0, COMP_FULL = 1, NRM_NONE = 100, NRM_PARTIAL = 101, NRM_FULL = 102, NRM_SIFT = 103, }; static Ptr create( float radius = 15, int q_radius = 3, int q_theta = 8, - int q_hist = 8, int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, - InputArray H = noArray() , bool interpolation = true, bool use_orientation = false ); + int q_hist = 8, int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false ); + + /** @overload + * @param image image to extract descriptors + * @param keypoints of interest within image + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ) = 0; + + /** @overload + * @param image image to extract descriptors + * @param roi region of interest within image + * @param descriptors resulted descriptors array for roi image pixels + */ + virtual void compute( InputArray image, Rect roi, OutputArray descriptors ) = 0; + + /**@overload + * @param image image to extract descriptors + * @param descriptors resulted descriptors array for all image pixels + */ + virtual void compute( InputArray image, OutputArray descriptors ) = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_descriptor true if descriptor was computed + */ + virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_unnormalized_descriptor true if descriptor was computed + */ + virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + + /** + * @param image set image as working + */ + virtual void set_image( InputArray image ) = 0; + }; diff --git a/modules/xfeatures2d/perf/perf_daisy.cpp b/modules/xfeatures2d/perf/perf_daisy.cpp index bb60b8e78..18ade55e4 100644 --- a/modules/xfeatures2d/perf/perf_daisy.cpp +++ b/modules/xfeatures2d/perf/perf_daisy.cpp @@ -22,12 +22,12 @@ PERF_TEST_P(daisy, extract, testing::Values(DAISY_IMAGES)) Mat mask; declare.in(frame).time(90); - // use DAISY in COMP_FULL mode (every pixel, dense baseline mode) - Ptr descriptor = DAISY::create(15, 3, 8, 8, DAISY::COMP_FULL, DAISY::NRM_NONE, noArray(), true, false); + Ptr descriptor = DAISY::create(); vector points; vector descriptors; - TEST_CYCLE() descriptor->compute(frame, points, descriptors); + // compute all daisies in image + TEST_CYCLE() descriptor->compute(frame, descriptors); SANITY_CHECK(descriptors, 1e-4); } diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index a9264ab4f..8fd167511 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -88,15 +88,14 @@ public: * @param q_radius amount of radial range divisions * @param q_theta amount of angular range divisions * @param q_hist amount of gradient orientations range divisions - * @param mode computation of descriptors * @param norm normalization type * @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image * @param interpolation switch to disable interpolation at minor costs of quality (default is true) * @param use_orientation sample patterns using keypoints orientation, disabled by default. */ explicit DAISY_Impl(float radius=15, int q_radius=3, int q_theta=8, int q_hist=8, - int mode = DAISY::ONLY_KEYS, int norm = DAISY::NRM_NONE, InputArray H = noArray(), - bool interpolation = true, bool use_orientation = false); + int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false); virtual ~DAISY_Impl(); @@ -105,14 +104,75 @@ public: // +1 is for center pixel return ( (m_rad_q_no * m_th_q_no + 1) * m_hist_th_q_no ); }; + /** returns the descriptor type */ virtual int descriptorType() const { return CV_32F; } + /** returns the default norm type */ virtual int defaultNorm() const { return NORM_L2; } - // main compute routine + /** + * @param image image to extract descriptors + * @param keypoints of interest within image + * @param descriptors resulted descriptors array + */ virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ); + /** @overload + * @param image image to extract descriptors + * @param roi region of interest within image + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, Rect roi, OutputArray descriptors ); + + /** @overload + * @param image image to extract descriptors + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, OutputArray descriptors ); + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_descriptor true if descriptor was computed + */ + virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_unnormalized_descriptor true if descriptor was computed + */ + virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + /** + * @param image set image as working + */ + virtual void set_image( InputArray image ); + + protected: /* @@ -144,13 +204,19 @@ protected: // input image. float* m_image; + Mat m_work_image; + // image height int m_h; // image width int m_w; - // stores the descriptors : its size is [ m_w * m_h * m_descriptor_size ]. + // image roi + Rect m_roi; + + // stores the descriptors : + // its size is [ m_roi.width*m_roi.height*m_descriptor_size ]. float* m_dense_descriptors; // stores the layered gradients in successively smoothed form: layer[n] = @@ -201,7 +267,8 @@ protected: // size of m_hsz layers at a single sigma: m_hsz * m_layer_size int m_cube_size; - // size of the layer : m_h*m_w + // size of the layer : + // m_roi.width*m_roi.height int m_layer_size; /* @@ -215,20 +282,20 @@ protected: void compute_histograms(); // emulates the way sift is normalized. - void normalize_sift_way( float* desc ); + void normalize_sift_way( float* desc ) const; // normalizes the descriptor histogram by histogram - void normalize_partial( float* desc ); + void normalize_partial( float* desc ) const; // normalizes the full descriptor. - void normalize_full( float* desc ); + void normalize_full( float* desc ) const; // initializes the class: computes gradient and structure-points void initialize(); void update_selected_cubes(); - int quantize_radius( float rad ); + int quantize_radius( float rad ) const; int filter_size( double sigma ); @@ -307,7 +374,7 @@ protected: void reset(); // releases unused memory after descriptor computation is completed. - void release_auxilary(); + void release_auxiliary(); // computes the descriptors for every pixel in the image. void compute_descriptors(); @@ -355,7 +422,7 @@ protected: if( m_h == 0 || m_descriptor_size == 0 ) { CV_Error( Error::StsInternal, "Image and descriptor size is zero" ); } - return m_w * m_h * m_descriptor_size; + return m_roi.width*m_roi.height * m_descriptor_size; } // returns the amount of memory needed for workspace. call before initialize() @@ -366,7 +433,7 @@ protected: return (g_cube_number+1)* m_cube_size; } - void normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) + void normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) const { if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); @@ -377,7 +444,7 @@ protected: } // transform a point via the homography - void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) + void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) const { double kxp = H[0]*x + H[1]*y + H[2]; double kyp = H[3]*x + H[4]*y + H[5]; @@ -388,69 +455,56 @@ protected: private: + // two possible computational mode + // ONLY_KEYS -> (mode_1) compute descriptors on demand + // COMP_FULL -> (mode_2) compute all descriptors from image + enum { ONLY_KEYS = 0, COMP_FULL = 1 }; + + // set & precompute parameters + inline void set_parameters(); + + + // initializes for get_descriptor(double, double, int) mode: pre-computes + // convolutions of gradient layers in m_smoothed_gradient_layers + inline void initialize_single_descriptor_mode(); + // returns the descriptor vector for the point (y, x) !!! use this for // precomputed operations meaning that you must call compute_descriptors() // before calling this function. if you want normalized descriptors, call // normalize_descriptors() before calling compute_descriptors() inline void get_descriptor( int y, int x, float* &descriptor ); - // computes the descriptor and returns the result in 'descriptor' ( allocate - // 'descriptor' memory first ie: float descriptor = new - // float[m_descriptor_size]; -> the descriptor is normalized. - inline void get_descriptor( double y, double x, int orientation, float* descriptor ); - - // computes the descriptor and returns the result in 'descriptor' ( allocate - // 'descriptor' memory first ie: float descriptor = new - // float[m_descriptor_size]; -> the descriptor is NOT normalized. - inline void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ); - - // computes the descriptor at homography-warped grid. (y,x) is not the - // coordinates of this image but the coordinates of the original grid where - // the homography will be applied. Meaning that the grid is somewhere else - // and we warp this grid with H and compute the descriptor on this warped - // grid; returns null/false if centers falls outside the image; allocate - // 'descriptor' memory first. descriptor is normalized. - inline bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor); - - // computes the descriptor at homography-warped grid. (y,x) is not the - // coordinates of this image but the coordinates of the original grid where - // the homography will be applied. Meaning that the grid is somewhere else - // and we warp this grid with H and compute the descriptor on this warped - // grid; returns null/false if centers falls outside the image; allocate - // 'descriptor' memory first. descriptor is NOT normalized. - inline bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ); - // compute the smoothed gradient layers. inline void compute_smoothed_gradient_layers(); // does not use interpolation while computing the histogram. - inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ); + inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const; // returns the interpolated histogram: picks either bi_get_histogram or // ti_get_histogram depending on 'shift' - inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ); + inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ) const; // records the histogram that is computed by bilinear interpolation // regarding the shift in the spatial coordinates. hcube is the // histogram cube for a constant smoothness level. - inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ); + inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ) const; // records the histogram that is computed by trilinear interpolation // regarding the shift in layers and spatial coordinates. hcube is the // histogram cube for a constant smoothness level. - inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ); + inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ) const; // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor - inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ); + inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ) const; // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor - inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ); + inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ) const; // uses interpolation for no interpolation call ni_get_descriptor. see also get_descriptor - inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor - inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ); + inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; // creates a 1D gaussian filter with N(mean,sigma). inline void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) @@ -521,7 +575,7 @@ private: // if ue=1 => x<=ux is checked else x inline - bool is_inside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + bool is_inside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) const { if( ( ((lx inline - bool is_inside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='&') + bool is_inside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='&') const { switch( oper ) { @@ -569,7 +623,7 @@ private: // If the 'oper' is set '&' both of the numbers must be within the interval to return true // But if the 'oper' is set to '|' then only one of them being true is sufficient. template inline - bool is_inside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='&') + bool is_inside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='&') const { switch( oper ) { @@ -591,7 +645,7 @@ private: // if ue=1 => x>ux is checked else x>=ux is checked // by default is x is searched outside of [lx,ux) template inline - bool is_outside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) + bool is_outside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) const { return !(is_inside(x,lx,ux,le,ue)); } @@ -604,7 +658,7 @@ private: // By default, 'oper' is set to OR. If one of them is outside it returns // true otherwise false. template inline - bool is_outside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='|') + bool is_outside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='|') const { switch( oper ) { @@ -627,7 +681,7 @@ private: // By default, 'oper' is set to OR. If one of them is outside it returns // true otherwise false. template inline - bool is_outside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='|') + bool is_outside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='|') const { switch( oper ) { @@ -644,7 +698,7 @@ private: // computes the square of a number and returns it. template inline - T square(T a) + T square(T a) const { return a*a; } @@ -652,7 +706,7 @@ private: // computes the square of an array. if in_place is enabled, the // result is returned in the array arr. template inline - T* square(T* arr, int sz, bool in_place=false) + T* square(T* arr, int sz, bool in_place=false) const { T* out; if( in_place ) out = arr; @@ -666,7 +720,7 @@ private: // computes the l2norm of an array: [ sum_i( [a(i)]^2 ) ]^0.5 template inline - float l2norm( T* a, int sz) + float l2norm( T* a, int sz) const { float norm=0; for( int k=0; k inline - float l2norm( T1* a, T2* b, int sz) + float l2norm( T1* a, T2* b, int sz) const { float norm=0; for( int i=0; i inline - float l2norm( T y0, T x0, T y1, T x1 ) + float l2norm( T y0, T x0, T y1, T x1 ) const { float d0 = (float) (x0 - x1); float d1 = (float) (y0 - y1); @@ -767,7 +821,7 @@ private: // divides the elements of the array with num template inline - void divide(T1* arr, int sz, T2 num ) + void divide(T1* arr, int sz, T2 num ) const { float inv_num = (float) (1.0 / num); @@ -943,6 +997,7 @@ private: return out; } + }; // END DAISY_Impl CLASS @@ -973,27 +1028,31 @@ double* DAISY_Impl::get_grid(int o) void DAISY_Impl::reset() { - deallocate( m_image ); - // deallocate( m_grid_points, m_grid_point_number ); - // deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); - // deallocate( m_cube_sigmas ); - deallocate( m_orientation_map ); - deallocate( m_scale_map ); - if( !m_descriptor_memory ) deallocate( m_dense_descriptors ); - if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); + m_work_image.release(); + + if ( m_orientation_map ) deallocate( m_orientation_map ); + if ( m_scale_map ) deallocate( m_scale_map ); + + if ( !m_descriptor_memory && m_dense_descriptors ) + deallocate( m_dense_descriptors ); + if ( !m_workspace_memory && m_smoothed_gradient_layers ) + deallocate(m_smoothed_gradient_layers); } -void DAISY_Impl::release_auxilary() +void DAISY_Impl::release_auxiliary() { - deallocate( m_image ); - deallocate( m_orientation_map ); - deallocate( m_scale_map ); + if ( m_orientation_map ) deallocate( m_orientation_map ); + if ( m_scale_map ) deallocate( m_scale_map ); - if( !m_workspace_memory ) deallocate(m_smoothed_gradient_layers); + if ( !m_workspace_memory && m_smoothed_gradient_layers ) + deallocate( m_smoothed_gradient_layers ); - deallocate( m_grid_points, m_grid_point_number ); - deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); - deallocate( m_cube_sigmas ); + if ( m_grid_points ) deallocate( m_grid_points, m_grid_point_number ); + if ( m_oriented_grid_points ) + deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); + if ( m_cube_sigmas ) deallocate( m_cube_sigmas ); + + m_work_image.release(); } void DAISY_Impl::compute_grid_points() @@ -1029,22 +1088,29 @@ void DAISY_Impl::compute_grid_points() /// Computes the descriptor by sampling convoluted orientation maps. void DAISY_Impl::compute_descriptors() { + + int y_off = m_roi.y; + int x_off = m_roi.x; + int y_end = m_roi.y + m_roi.height; + int x_end = m_roi.x + m_roi.width; + if( m_scale_invariant ) compute_scales(); if( m_rotation_invariant ) compute_orientations(); - if( !m_descriptor_memory ) m_dense_descriptors = allocate (m_h*m_w*m_descriptor_size); + if( !m_descriptor_memory ) + m_dense_descriptors = allocate (m_roi.width*m_roi.height * m_descriptor_size); - memset(m_dense_descriptors, 0, sizeof(float)*m_h*m_w*m_descriptor_size); + memset(m_dense_descriptors, 0, sizeof(float)*m_roi.width*m_roi.height * m_descriptor_size); int y, x, index, orientation; #if defined _OPENMP #pragma omp parallel for private(y,x,index,orientation) #endif - for( y=0; y= 0 && orientation < g_grid_orientation_resolution ) ) orientation = 0; get_unnormalized_descriptor( y, x, orientation, &(m_dense_descriptors[index*m_descriptor_size]) ); @@ -1070,7 +1136,7 @@ void DAISY_Impl::smooth_layers( float* layers, int h, int w, int layer_number, f deallocate(filter); } -void DAISY_Impl::normalize_partial( float* desc ) +void DAISY_Impl::normalize_partial( float* desc ) const { float norm; for( int h=0; h= m_cube_sigmas[g_cube_number-1] ) return g_cube_number-1; @@ -1412,7 +1477,7 @@ void DAISY_Impl::compute_scales() m_scale_map[q] = (float) round( m_scale_map[q] ); } -// save( m_scale_map, m_h, m_w, "scales.dat"); + //save( m_scale_map, m_h, m_w, "scales.dat"); deallocate( sim ); deallocate( max_dog ); @@ -1521,6 +1586,7 @@ void DAISY_Impl::set_descriptor_memory( float* descriptor, long int d_size ) { CV_Assert( m_descriptor_memory == false ); CV_Assert( m_h*m_w != 0 ); + CV_Assert( m_roi.width*m_roi.height != 0 ); CV_Assert( d_size >= compute_descriptor_memory() ); m_dense_descriptors = descriptor; @@ -1531,6 +1597,7 @@ void DAISY_Impl::set_workspace_memory( float* workspace, long int w_size ) { CV_Assert( m_workspace_memory == false ); CV_Assert( m_h*m_w != 0 ); + CV_Assert( m_roi.width*m_roi.height != 0 ); CV_Assert( w_size >= compute_workspace_memory() ); m_smoothed_gradient_layers = workspace; @@ -1550,7 +1617,7 @@ inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* hi for( int h=0; h& keypoints, OutputArray _descriptors ) +void DAISY_Impl::initialize_single_descriptor_mode( ) { - // do nothing if no image - Mat image = _image.getMat(); - if( image.empty() ) - return; - - // get homography if supplied - Mat H = m_h_matrix; - - // convert to float if case - if ( image.depth() != CV_64F ) - H.convertTo( H, CV_64F ); - /* - * daisy set_image() - */ - - // base size - m_h = image.rows; - m_w = image.cols; - - // clone image for conversion - if ( image.depth() != CV_32F ) { - - Mat work_image = image.clone(); - - // convert to gray inplace - if( work_image.channels() > 1 ) - cvtColor( work_image, work_image, COLOR_BGR2GRAY ); - - // convert to float if it is necessary - if ( work_image.depth() != CV_32F ) - { - // convert and normalize - work_image.convertTo( work_image, CV_32F ); - work_image /= 255.0f; - } else - CV_Error( Error::StsUnsupportedFormat, "" ); - - // use cloned work image - m_image = work_image.ptr(0); - - } else - // use original CV_32F image - m_image = image.ptr(0); - - // full mode if noArray() - // was passed to _descriptors - if ( _descriptors.needed() == false ) - m_mode = DAISY::COMP_FULL; - - /* - * daisy set_parameters() - */ + initialize(); + compute_smoothed_gradient_layers(); +} +void DAISY_Impl::set_parameters( ) +{ m_grid_point_number = m_rad_q_no * m_th_q_no + 1; // +1 is for center pixel m_descriptor_size = m_grid_point_number * m_hist_th_q_no; @@ -1940,65 +1957,168 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O compute_cube_sigmas(); compute_grid_points(); +} +// set/convert image array for daisy internal routines +// daisy internals use CV_32F image with norm to 1.0f +void DAISY_Impl::set_image( InputArray _image ) +{ + // release previous image + // and previous daisies workspace + reset(); - /* - * daisy initialize_single_descriptor_mode(); - */ + // fetch new image + Mat image = _image.getMat(); - // initializes for get_descriptor(double, double, int) mode: pre-computes - // convolutions of gradient layers in m_smoothed_gradient_layers + // image cannot be empty + CV_Assert( ! image.empty() ); - initialize(); - compute_smoothed_gradient_layers(); + // base size + m_h = image.rows; + m_w = image.cols; - /* - * daisy compute descriptors given operating mode - */ + // clone image for conversion + if ( image.depth() != CV_32F ) { - if ( m_mode == COMP_FULL ) - { - CV_Assert( H.empty() ); - CV_Assert( keypoints.empty() ); - CV_Assert( ! m_use_orientation ); + m_work_image = image.clone(); - compute_descriptors(); - normalize_descriptors(); + // convert to gray inplace + if( m_work_image.channels() > 1 ) + cvtColor( m_work_image, m_work_image, COLOR_BGR2GRAY ); - cv::Mat descriptors; - descriptors = _descriptors.getMat(); - descriptors = Mat( m_h * m_w, m_descriptor_size, - CV_32F, &m_dense_descriptors[0] ); - } else - if ( m_mode == ONLY_KEYS ) - { - cv::Mat descriptors; - _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); - descriptors = _descriptors.getMat(); + // convert to float if it is necessary + if ( m_work_image.depth() != CV_32F ) + { + // convert and normalize + m_work_image.convertTo( m_work_image, CV_32F ); + m_work_image /= 255.0f; + } else + CV_Error( Error::StsUnsupportedFormat, "" ); - if ( H.empty() ) - for (int k = 0; k < (int) keypoints.size(); k++) - { - get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, - m_use_orientation ? (int) keypoints[k].angle : 0, - &descriptors.at( k, 0 ) ); - } - else - for (int k = 0; k < (int) keypoints.size(); k++) - { - get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, - m_use_orientation ? (int) keypoints[k].angle : 0, - &H.at( 0 ), &descriptors.at( k, 0 ) ); - } + // use cloned work image + m_image = m_work_image.ptr(0); } else - CV_Error( Error::StsInternal, "Unknown computation mode" ); + // use original user supplied CV_32F image + // should be a normalized one (cannot check) + m_image = image.ptr(0); + +} + + +// ------------------------------------------------- +/* DAISY interface implementation */ + +// keypoint scope +void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + set_image( _image ); + + // whole image + m_roi = Rect( 0, 0, m_w, m_h ); + + // get homography + Mat H = m_h_matrix; + + // convert to double if case + if ( H.depth() != CV_64F ) + H.convertTo( H, CV_64F ); + + set_parameters(); + + initialize_single_descriptor_mode(); + + // allocate array + _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); + + // prepare descriptors + Mat descriptors = _descriptors.getMat(); + descriptors.setTo( Scalar(0) ); + + // iterate over keypoints + // and fill computed descriptors + if ( H.empty() ) + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &descriptors.at( k, 0 ) ); + } + else + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &H.at( 0 ), &descriptors.at( k, 0 ) ); + } + +} + +// full scope with roi +void DAISY_Impl::compute( InputArray _image, Rect roi, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + CV_Assert( m_h_matrix.empty() ); + CV_Assert( ! m_use_orientation ); + + set_image( _image ); + + m_roi = roi; + + set_parameters(); + initialize_single_descriptor_mode(); + + // compute full desc + compute_descriptors(); + normalize_descriptors(); + + Mat descriptors = _descriptors.getMat(); + descriptors = Mat( m_roi.width * m_roi.height, m_descriptor_size, + CV_32F, &m_dense_descriptors[0] ); + + release_auxiliary(); +} + +// full scope +void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + CV_Assert( m_h_matrix.empty() ); + CV_Assert( ! m_use_orientation ); + + set_image( _image ); + + // whole image + m_roi = Rect( 0, 0, m_w, m_h ); + + set_parameters(); + initialize_single_descriptor_mode(); + + // compute full desc + compute_descriptors(); + normalize_descriptors(); + + Mat descriptors = _descriptors.getMat(); + descriptors = Mat( m_h * m_w, m_descriptor_size, + CV_32F, &m_dense_descriptors[0] ); + + release_auxiliary(); } // constructor DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, - int _mode, int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) - : m_mode(_mode), m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), + int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) + : m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) { m_w = 0; @@ -2008,7 +2128,6 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_grid_point_number = 0; m_descriptor_size = 0; - m_smoothed_gradient_layers = NULL; m_dense_descriptors = NULL; m_grid_points = NULL; @@ -2024,6 +2143,7 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_cube_sigmas = NULL; + // unused features m_descriptor_memory = false; m_workspace_memory = false; @@ -2038,7 +2158,7 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, // destructor DAISY_Impl::~DAISY_Impl() { - if( !m_workspace_memory ) deallocate( m_smoothed_gradient_layers ); + if ( !m_workspace_memory ) deallocate( m_smoothed_gradient_layers ); deallocate( m_grid_points, m_grid_point_number ); deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); deallocate( m_orientation_map ); @@ -2047,9 +2167,9 @@ DAISY_Impl::~DAISY_Impl() } Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, - int mode, int norm, InputArray H, bool interpolation, bool use_orientation) + int norm, InputArray H, bool interpolation, bool use_orientation) { - return makePtr(radius, q_radius, q_theta, q_hist, mode, norm, H, interpolation, use_orientation); + return makePtr(radius, q_radius, q_theta, q_hist, norm, H, interpolation, use_orientation); } diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 176a342c5..16d6656a8 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -654,7 +654,7 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) { DescriptorRotationInvarianceTest test(BRISK::create(), - DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + DAISY::create(15, 3, 8, 8, DAISY::NRM_NONE, noArray(), true, true), NORM_L1, 0.79f); test.safe_run(); @@ -721,7 +721,7 @@ TEST(Features2d_RotationInvariance2_Detector_SURF, regression) TEST(Features2d_ScaleInvariance_Descriptor_DAISY, regression) { DescriptorScaleInvarianceTest test(BRISK::create(), - DAISY::create(15, 3, 8, 8, DAISY::ONLY_KEYS, DAISY::NRM_NONE, noArray(), true, true), + DAISY::create(15, 3, 8, 8, DAISY::NRM_NONE, noArray(), true, true), NORM_L1, 0.075f); test.safe_run(); From 73bed6f3b2297c2e37e8329b5b897f3baf6ee236 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 18 May 2015 14:50:12 +0300 Subject: [PATCH 07/14] Rebase the code, massive cleanups. We still pass QA. --- .../include/opencv2/xfeatures2d.hpp | 13 +- modules/xfeatures2d/src/daisy.cpp | 1731 +++++++---------- 2 files changed, 658 insertions(+), 1086 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index e5151e001..744e78f98 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -196,7 +196,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; @@ -204,7 +204,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param H homography matrix for warped grid * @param descriptor supplied array for descriptor storage * @param get_descriptor true if descriptor was computed @@ -214,7 +214,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; @@ -222,18 +222,13 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param H homography matrix for warped grid * @param descriptor supplied array for descriptor storage * @param get_unnormalized_descriptor true if descriptor was computed */ virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; - /** - * @param image set image as working - */ - virtual void set_image( InputArray image ) = 0; - }; diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 8fd167511..7d6c7fd4c 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -49,7 +49,7 @@ */ #include "precomp.hpp" -#include "opencv2/imgproc/imgproc_c.h" + #include #include @@ -167,11 +167,6 @@ public: */ virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; - /** - * @param image set image as working - */ - virtual void set_image( InputArray image ); - protected: @@ -179,9 +174,6 @@ protected: * DAISY parameters */ - // operation mode - int m_mode; - // maximum radius of the descriptor region. float m_rad; @@ -198,72 +190,15 @@ protected: // default. change the value using set_normalization() function. int m_nrm_type; - // holds optional H matrix - Mat m_h_matrix; - - // input image. - float* m_image; - - Mat m_work_image; - - // image height - int m_h; - - // image width - int m_w; - - // image roi - Rect m_roi; - - // stores the descriptors : - // its size is [ m_roi.width*m_roi.height*m_descriptor_size ]. - float* m_dense_descriptors; - - // stores the layered gradients in successively smoothed form: layer[n] = - // m_gradient_layers * gaussian( sigma_n ); n>= 1; layer[0] is the layered_gradient - float* m_smoothed_gradient_layers; - - // if set to true, descriptors are scale invariant - bool m_scale_invariant; - - // if set to true, descriptors are rotation invariant - bool m_rotation_invariant; - // number of bins in the histograms while computing orientation int m_orientation_resolution; - // hold the scales of the pixels - float* m_scale_map; - - // holds the orientaitons of the pixels - int* m_orientation_map; - - // Holds the oriented coordinates (y,x) of the grid points of the region. - double** m_oriented_grid_points; - - // holds the gaussian sigmas for radius quantizations for an incremental - // application - double* m_cube_sigmas; - - bool m_descriptor_memory; - bool m_workspace_memory; - // the number of grid locations int m_grid_point_number; // the size of the descriptor vector int m_descriptor_size; - // holds the amount of shift that's required for histogram computation - double m_orientation_shift_table[360]; - - // if enabled, descriptors are computed with casting non-integer locations - // to integer positions otherwise we use interpolation. - bool m_disable_interpolation; - - // switch to enable sample by keypoints orientation - bool m_use_orientation; - // size of m_hsz layers at a single sigma: m_hsz * m_layer_size int m_cube_size; @@ -271,187 +206,68 @@ protected: // m_roi.width*m_roi.height int m_layer_size; - /* - * DAISY functions - */ - - // computes the histogram at yx; the size of histogram is m_hist_th_q_no - void compute_histogram( float* hcube, int y, int x, float* histogram ); - - // reorganizes the cube data so that histograms are sequential in memory. - void compute_histograms(); - - // emulates the way sift is normalized. - void normalize_sift_way( float* desc ) const; - - // normalizes the descriptor histogram by histogram - void normalize_partial( float* desc ) const; - - // normalizes the full descriptor. - void normalize_full( float* desc ) const; - - // initializes the class: computes gradient and structure-points - void initialize(); - - void update_selected_cubes(); - - int quantize_radius( float rad ) const; - - int filter_size( double sigma ); - - // computes scales for every pixel and scales the structure grid so that the - // resulting descriptors are scale invariant. you must set - // m_scale_invariant flag to 1 for the program to call this function - void compute_scales(); - - // Return a number in the range [-0.5, 0.5] that represents the location of - // the peak of a parabola passing through the 3 evenly spaced samples. The - // center value is assumed to be greater than or equal to the other values - // if positive, or less than if negative. - float interpolate_peak( float left, float center, float right ); - - // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram - // is connected in a circular buffer. - void smooth_histogram( float *hist, int bins ); - - // computes pixel orientations and rotates the structure grid so that - // resulting descriptors are rotation invariant. If the scales is also - // detected, then orientations are computed at the computed scales. you must - // set m_rotation_invariant flag to 1 for the program to call this function - void compute_orientations(); - // the clipping threshold to use in normalization: values above this value // are clipped to this value for normalize_sift_way() function float m_descriptor_normalization_threshold; - // computes the sigma's of layers from descriptor parameters if the user did - // not sets it. these define the size of the petals of the descriptor. - void compute_cube_sigmas(); + /* + * DAISY switches + */ - // Computes the locations of the unscaled unrotated points where the - // histograms are going to be computed according to the given parameters. - void compute_grid_points(); + // if set to true, descriptors are scale invariant + bool m_scale_invariant; - // Computes the locations of the unscaled rotated points where the - // histograms are going to be computed according to the given parameters. - void compute_oriented_grid_points(); + // if set to true, descriptors are rotation invariant + bool m_rotation_invariant; - // smooths each of the layers by a Gaussian having "sigma" standart - // deviation. - void smooth_layers( float*layers, int h, int w, int layer_number, float sigma ); + // if enabled, descriptors are computed with casting non-integer locations + // to integer positions otherwise we use interpolation. + bool m_disable_interpolation; + + // switch to enable sample by keypoints orientation + bool m_use_orientation; + + /* + * DAISY arrays + */ + + // holds optional H matrix + Mat m_h_matrix; + + // input image. + Mat m_image; + + // image roi + Rect m_roi; + + // stores the descriptors : + // its size is [ m_roi.width*m_roi.height*m_descriptor_size ]. + Mat m_dense_descriptors; + + // stores the layered gradients in successively smoothed form : + // layer[n] = m_gradient_layers * gaussian( sigma_n ); + // n>= 1; layer[0] is the layered_gradient + Mat m_smoothed_gradient_layers; + + // hold the scales of the pixels + Mat m_scale_map; + + // holds the orientaitons of the pixels + Mat m_orientation_map; + + // Holds the oriented coordinates (y,x) of the grid points of the region. + Mat m_oriented_grid_points; + + // holds the gaussian sigmas for radius quantizations for an incremental + // application + Mat m_cube_sigmas; // Holds the coordinates (y,x) of the grid points of the region. - double** m_grid_points; + Mat m_grid_points; - int get_hq() { return m_hist_th_q_no; } - int get_thq() { return m_th_q_no; } - int get_rq() { return m_rad_q_no; } - float get_rad() { return m_rad; } + // holds the amount of shift that's required for histogram computation + double m_orientation_shift_table[360]; - // sets the type of the normalization to apply out of {NRM_PARTIAL, - // NRM_FULL, NRM_SIFT}. Call before using get_descriptor() if you want to - // change the default normalization type. - void set_normalization( int nrm_type ) { m_nrm_type = nrm_type; } - - // applies one of the normalizations (partial,full,sift) to the desciptors. - void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); - - // normalizes histograms individually - void normalize_histograms(); - - // gets the histogram at y,x with 'orientation' from the r'th cube - float* get_histogram( int y, int x, int r ); - - // if called, I don't use interpolation in the computation of - // descriptors. - void disable_interpolation() { m_disable_interpolation = true; } - - // returns the region number. - int grid_point_number() { return m_grid_point_number; } - - // releases all the used memory; call this if you want to process - // multiple images within a loop. - void reset(); - - // releases unused memory after descriptor computation is completed. - void release_auxiliary(); - - // computes the descriptors for every pixel in the image. - void compute_descriptors(); - - // returns all the descriptors. - float* get_dense_descriptors(); - - // returns oriented grid points. default is 0 orientation. - double* get_grid(int o=0); - - // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the - // scales for every pixel so that the resulting descriptors are scale - // invariant. - void scale_invariant( bool state = true ) - { - g_scale_en = (int)( (log(g_sigma_2/g_sigma_0)) / log(g_sigma_step) ) - g_scale_st; - m_scale_invariant = state; - } - - // EXPERIMENTAL: DO NOT USE IF YOU ARE NOT ENGIN TOLA: tells to compute the - // orientations for every pixel so that the resulting descriptors are - // rotation invariant. orientation steps are 360/ori_resolution - void rotation_invariant( int ori_resolution = 36, bool state = true ) - { - m_rotation_invariant = state; - m_orientation_resolution = ori_resolution; - } - - // sets the gaussian variances manually. must be called before - // initialize() to be considered. must be exact sigma values -> f - // converts to incremental format. - void set_cube_gaussians( double* sigma_array, int sz ); - - int* get_orientation_map() { return m_orientation_map; } - - // call compute_descriptor_memory to find the amount of memory to allocate - void set_descriptor_memory( float* descriptor, long int d_size ); - - // call compute_workspace_memory to find the amount of memory to allocate - void set_workspace_memory( float* workspace, long int w_size ); - - // returns the amount of memory needed for the compute_descriptors() - // function. it is basically equal to imagesize x descriptor_size - int compute_descriptor_memory() { - if( m_h == 0 || m_descriptor_size == 0 ) { - CV_Error( Error::StsInternal, "Image and descriptor size is zero" ); - } - return m_roi.width*m_roi.height * m_descriptor_size; - } - - // returns the amount of memory needed for workspace. call before initialize() - int compute_workspace_memory() { - if( m_cube_size == 0 ) { - CV_Error( Error::StsInternal, "Cube size is zero" ); - } - return (g_cube_number+1)* m_cube_size; - } - - void normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) const - { - if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; - else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); - else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); - else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); - else - CV_Error( Error::StsInternal, "No such normalization" ); - } - - // transform a point via the homography - void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) const - { - double kxp = H[0]*x + H[1]*y + H[2]; - double kyp = H[3]*x + H[4]*y + H[5]; - double kp = H[6]*x + H[7]*y + H[8]; - u = kxp / kp; - v = kyp / kp; - } private: @@ -460,23 +276,100 @@ private: // COMP_FULL -> (mode_2) compute all descriptors from image enum { ONLY_KEYS = 0, COMP_FULL = 1 }; - // set & precompute parameters - inline void set_parameters(); + /* + * DAISY functions + */ + // initializes the class: computes gradient and structure-points + inline void initialize(); // initializes for get_descriptor(double, double, int) mode: pre-computes // convolutions of gradient layers in m_smoothed_gradient_layers inline void initialize_single_descriptor_mode(); + // set & precompute parameters + inline void set_parameters(); + + // image set image as working + inline void set_image( InputArray image ); + + // releases all the used memory; call this if you want to process + // multiple images within a loop. + inline void reset(); + + // releases unused memory after descriptor computation is completed. + inline void release_auxiliary(); + + // computes the descriptors for every pixel in the image. + inline void compute_descriptors(); + + // computes scales for every pixel and scales the structure grid so that the + // resulting descriptors are scale invariant. you must set + // m_scale_invariant flag to 1 for the program to call this function + inline void compute_scales(); + + // compute the smoothed gradient layers. + inline void compute_smoothed_gradient_layers(); + + // computes pixel orientations and rotates the structure grid so that + // resulting descriptors are rotation invariant. If the scales is also + // detected, then orientations are computed at the computed scales. you must + // set m_rotation_invariant flag to 1 for the program to call this function + inline void compute_orientations(); + + // computes the histogram at yx; the size of histogram is m_hist_th_q_no + inline void compute_histogram( float* hcube, int y, int x, float* histogram ); + + // reorganizes the cube data so that histograms are sequential in memory. + inline void compute_histograms(); + + // computes the sigma's of layers from descriptor parameters if the user did + // not sets it. these define the size of the petals of the descriptor. + inline void compute_cube_sigmas(); + + // Computes the locations of the unscaled unrotated points where the + // histograms are going to be computed according to the given parameters. + inline void compute_grid_points(); + + // Computes the locations of the unscaled rotated points where the + // histograms are going to be computed according to the given parameters. + inline void compute_oriented_grid_points(); + + // normalizes the descriptor + inline void normalize_descriptor( float* desc, int nrm_type ) const; + + // applies one of the normalizations (partial,full,sift) to the desciptors. + inline void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); + + + // emulates the way sift is normalized. + inline void normalize_sift_way( float* desc ) const; + + // normalizes the descriptor histogram by histogram + inline void normalize_partial( float* desc ) const; + + // normalizes the full descriptor. + inline void normalize_full( float* desc ) const; + + // normalizes histograms individually + inline void normalize_histograms(); + + inline void update_selected_cubes(); + + // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram + // is connected in a circular buffer. + inline void smooth_histogram( Mat hist, int bins ); + + // smooths each of the layers by a Gaussian having "sigma" standart + // deviation. + inline void smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ); + // returns the descriptor vector for the point (y, x) !!! use this for // precomputed operations meaning that you must call compute_descriptors() // before calling this function. if you want normalized descriptors, call // normalize_descriptors() before calling compute_descriptors() inline void get_descriptor( int y, int x, float* &descriptor ); - // compute the smoothed gradient layers. - inline void compute_smoothed_gradient_layers(); - // does not use interpolation while computing the histogram. inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const; @@ -506,496 +399,15 @@ private: // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; - // creates a 1D gaussian filter with N(mean,sigma). - inline void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) - { - CV_Assert(fltr != NULL); - int sz = (fsz-1)/2; - int counter=-1; - float sum = 0.0; - float v = 2*sigma*sigma; - for( int x=-sz; x<=sz; x++ ) - { - counter++; - fltr[counter] = exp((-((float)x-mean)*((float)x-mean))/v); - sum += fltr[counter]; - } + inline int quantize_radius( float rad ) const; - if( sum != 0 ) - { - for( int x=0; x - class rectangle - { - public: - T lx, ux, ly, uy; - T dx, dy; - rectangle(T xl, T xu, T yl, T yu) { lx=xl; ux=xu; ly=yl; uy=yu; dx=ux-lx; dy=uy-ly; }; - rectangle() { lx = ux = ly = uy = dx = dy = 0; }; - }; - - // checks if the number x is between lx - ux interval. - // the equality is checked depending on the value of le and ue parameters. - // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline - bool is_inside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) const - { - if( ( ((lx lx<=x is checked else lx x<=ux is checked else x inline - bool is_inside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='&') const - { - switch( oper ) - { - case '|': - if( is_inside(x,lx,ux,le,ue) || is_inside(y,ly,uy,le,ue) ) - return true; - return false; - - default: - if( is_inside(x,lx,ux,le,ue) && is_inside(y,ly,uy,le,ue) ) - return true; - return false; - } - } - - // checks if the number x is between lx - ux and/or y is between ly - uy interval. - // If the number is inside, then function returns true, else it returns false. - // the equality is checked depending on the value of le and ue parameters. - // if le=1 => lx<=x is checked else lx x<=ux is checked else x inline - bool is_inside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='&') const - { - switch( oper ) - { - case '|': - if( is_inside(x,roi.lx,roi.ux,le,ue) || is_inside(y,roi.ly,roi.uy,le,ue) ) - return true; - return false; - - default: - if( is_inside(x,roi.lx,roi.ux,le,ue) && is_inside(y,roi.ly,roi.uy,le,ue) ) - return true; - return false; - } - } - - // checks if the number x is outside lx - ux interval - // the equality is checked depending on the value of le and ue parameters. - // if le=1 => lx>x is checked else lx>=x is checked - // if ue=1 => x>ux is checked else x>=ux is checked - // by default is x is searched outside of [lx,ux) - template inline - bool is_outside(T1 x, T2 lx, T3 ux, bool le=true, bool ue=false) const - { - return !(is_inside(x,lx,ux,le,ue)); - } - - // checks if the numbers x and y is outside their intervals. - // The equality is checked depending on the value of le and ue parameters. - // If le=1 => lx>x is checked else lx>=x is checked - // If ue=1 => x>ux is checked else x>=ux is checked - // By default is x is searched outside of [lx,ux) (Similarly for y) - // By default, 'oper' is set to OR. If one of them is outside it returns - // true otherwise false. - template inline - bool is_outside(T1 x, T2 lx, T3 ux, T1 y, T2 ly, T3 uy, bool le=true, bool ue=false, char oper='|') const - { - switch( oper ) - { - case '&': - if( is_outside(x,lx,ux,le,ue) && is_outside(y,ly,uy,le,ue) ) - return true; - return false; - default: - if( is_outside(x,lx,ux,le,ue) || is_outside(y,ly,uy,le,ue) ) - return true; - return false; - } - } - - // checks if the numbers x and y is outside their intervals. - // The equality is checked depending on the value of le and ue parameters. - // If le=1 => lx>x is checked else lx>=x is checked - // If ue=1 => x>ux is checked else x>=ux is checked - // By default is x is searched outside of [lx,ux) (Similarly for y) - // By default, 'oper' is set to OR. If one of them is outside it returns - // true otherwise false. - template inline - bool is_outside(T1 x, T1 y, rectangle roi, bool le=true, bool ue=false, char oper='|') const - { - switch( oper ) - { - case '&': - if( is_outside(x,roi.lx,roi.ux,le,ue) && is_outside(y,roi.ly,roi.uy,le,ue) ) - return true; - return false; - default: - if( is_outside(x,roi.lx,roi.ux,le,ue) || is_outside(y,roi.ly,roi.uy,le,ue) ) - return true; - return false; - } - } - - // computes the square of a number and returns it. - template inline - T square(T a) const - { - return a*a; - } - - // computes the square of an array. if in_place is enabled, the - // result is returned in the array arr. - template inline - T* square(T* arr, int sz, bool in_place=false) const - { - T* out; - if( in_place ) out = arr; - else out = allocate(sz); - - for( int i=0; i inline - float l2norm( T* a, int sz) const - { - float norm=0; - for( int k=0; k inline - float l2norm( T1* a, T2* b, int sz) const - { - float norm=0; - for( int i=0; i inline - float l2norm( T y0, T x0, T y1, T x1 ) const - { - float d0 = (float) (x0 - x1); - float d1 = (float) (y0 - y1); - - return sqrt( d0*d0 + d1*d1 ); - } - - // allocates a memory of size sz and returns a pointer to the array - template inline - T* allocate(const int sz) - { - T* array = new T[sz]; - return array; - } - - // allocates a memory of size ysz x xsz and returns a double pointer to it - template inline - T** allocate(const int ysz, const int xsz) - { - T** mat = new T*[ysz]; - int i; - - for(i=0; i(xsz); - - return mat; - } - - // deallocates the memory and sets the pointer to null. - template inline - void deallocate(T* &array) - { - delete[] array; - array = NULL; - } - - // deallocates the memory and sets the pointer to null. - template inline - void deallocate(T** &mat, int ysz) - { - if( mat == NULL ) return; - - for(int i=0; i inline - void polar2cartesian(T1 r, T1 t, T2 &y, T2 &x) - { - x = (T2)( r * cos( t ) ); - y = (T2)( r * sin( t ) ); - } - - - template inline - void convolve_sym_( T* image, int h, int w, T* kernel, int ksize ) - { - conv_horizontal( image, h, w, kernel, ksize ); - conv_vertical ( image, h, w, kernel, ksize ); - } - - template - inline void convolve_sym( T* image, int h, int w, T* kernel, int ksize, T* out=NULL ) - { - if( out == NULL ) out = image; - else memcpy( out, image, sizeof(T)*h*w ); - convolve_sym_(out, h, w, kernel, ksize); - } - - // divides the elements of the array with num - template inline - void divide(T1* arr, int sz, T2 num ) const - { - float inv_num = (float) (1.0 / num); - - for( int i=0; i inline - T* zeros(int r) - { - T* data = allocate(r); - memset( data, 0, sizeof(T)*r ); - return data; - } - - template inline - T* layered_gradient( T* data, int h, int w, int layer_no=8 ) - { - int data_size = h * w; - T* layers = zeros(layer_no * data_size); - - // smooth the data matrix - T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false ); - - T *dx = new T[data_size]; - T *dy = new T[data_size]; - gradient(bdata, h, w, dy, dx); - deallocate( bdata ); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int l=0; l 0 ) layer_l[index] = value; - else layer_l[index] = 0; - } - } - deallocate(dy); - deallocate(dx); - - return layers; - } - - // computes the gradient of an image and returns the result in - // pointers to REAL. - template inline - void gradient(T* im, int h, int w, T* dy, T* dx) - { - CV_Assert( dx != NULL ); - CV_Assert( dy != NULL ); - - for( int y=0; y0 && x0 && y inline - // original T* workspace=0 was removed - void layered_gradient( T* data, int h, int w, int layer_no, T* layers, int lwork = 0 ) - { - int data_size = h * w; - CV_Assert(layers!=NULL); - memset(layers,0,sizeof(T)*data_size*layer_no); - - bool was_empty = false; - T* work=NULL; - if( lwork < 3*data_size ) { - work = new T[3*data_size]; - was_empty = true; - } - - // // smooth the data matrix - // T* bdata = blur_gaussian_2d( data, h, w, 0.5, 5, false); - float kernel[5]; gaussian_1d(kernel, 5, 0.5, 0); - memcpy( work, data, sizeof(T)*data_size ); - convolve_sym( work, h, w, kernel, 5 ); - - T *dx = work+data_size; - T *dy = work+2*data_size; - gradient( work, h, w, dy, dx ); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int l=0; l 0 ) layer_l[index] = value; - else layer_l[index] = 0; - } - } - if( was_empty ) delete []work; - } - - // casts a type T2 array into a type T1 array. - template inline - T1* type_cast(T2* data, int sz) - { - T1* out = new T1[sz]; - - for( int i=0; i inline - T1* blur_gaussian_2d( T2* array, int rn, int cn, float sigma, int kernel_size = 0, bool in_place = false ) - { - T1* out = NULL; - - if( in_place ) - out = (T1*)array; - else - out = type_cast(array,rn*cn); - - if( kernel_size == 0 ) - kernel_size = (int)(3*sigma); - - if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd - if( kernel_size < 3 ) kernel_size= 3; // kernel size cannot be smaller than 3 - - float* kernel = new float[kernel_size]; - gaussian_1d(kernel, kernel_size, sigma, 0); - - // !! apply the filter separately - convolve_sym( out, rn, cn, kernel, kernel_size ); - // conv_horizontal( out, rn, cn, kernel, kernel_size); - // conv_vertical ( out, rn, cn, kernel, kernel_size); - - deallocate(kernel); - return out; - } + // Return a number in the range [-0.5, 0.5] that represents the location of + // the peak of a parabola passing through the 3 evenly spaced samples. The + // center value is assumed to be greater than or equal to the other values + // if positive, or less than if negative. + inline float interpolate_peak( float left, float center, float right ); }; // END DAISY_Impl CLASS @@ -1004,70 +416,179 @@ private: // ------------------------------------------------- /* DAISY computation routines */ -float* DAISY_Impl::get_histogram( int y, int x, int r ) +inline void DAISY_Impl::reset() { - CV_Assert( y >= 0 && y < m_h ); - CV_Assert( x >= 0 && x < m_w ); - CV_Assert( m_smoothed_gradient_layers ); - CV_Assert( m_oriented_grid_points ); - return m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size + (y*m_w+x)*m_hist_th_q_no; - // i_get_histogram( histogram, y, x, 0, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); + m_image.release(); + + m_orientation_map.release(); + m_scale_map.release(); + + m_dense_descriptors.release(); + m_smoothed_gradient_layers.release(); } - -float* DAISY_Impl::get_dense_descriptors() +inline void DAISY_Impl::release_auxiliary() { - return m_dense_descriptors; + m_orientation_map.release(); + m_scale_map.release(); + + m_smoothed_gradient_layers.release(); + + m_grid_points.release(); + m_oriented_grid_points.release(); + m_cube_sigmas.release(); + + m_image.release(); } -double* DAISY_Impl::get_grid(int o) +// creates a 1D gaussian filter with N(mean,sigma). +static void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) { - CV_Assert( o >= 0 && o < 360 ); - return m_oriented_grid_points[o]; + CV_Assert(fltr != NULL); + + int sz = (fsz - 1) / 2; + int counter = -1; + float sum = 0.0f; + float v = 2 * sigma*sigma; + for( int x=-sz; x<=sz; x++ ) + { + counter++; + fltr[counter] = exp((-((float)x-mean)*((float)x-mean))/v); + sum += fltr[counter]; + } + + if( sum != 0 ) + for( int x=0; x0 && x(ind) = (im.at(ind+1)-im.at(ind-1)) / 2.0f; + if( x==0 ) dx.at(ind) = im.at(ind+1)-im.at(ind ); + if( x==w-1 ) dx.at(ind) = im.at(ind )-im.at(ind-1); - if ( !m_descriptor_memory && m_dense_descriptors ) - deallocate( m_dense_descriptors ); - if ( !m_workspace_memory && m_smoothed_gradient_layers ) - deallocate(m_smoothed_gradient_layers); + // dy + if( y>0 && y(ind) = (im.at(ind+w)-im.at(ind-w)) / 2.0f; + if( y==0 ) dy.at(ind) = im.at(ind+w)-im.at(ind ); + if( y==h-1 ) dy.at(ind) = im.at(ind )-im.at(ind-w); + } + } } -void DAISY_Impl::release_auxiliary() +static Mat layered_gradient( Mat data, int layer_no = 8 ) { - if ( m_orientation_map ) deallocate( m_orientation_map ); - if ( m_scale_map ) deallocate( m_scale_map ); + int data_size = data.rows * data.cols; + Mat layers( 1, layer_no*data_size, CV_32F, Scalar(0) ); - if ( !m_workspace_memory && m_smoothed_gradient_layers ) - deallocate( m_smoothed_gradient_layers ); + float kernel[5]; + gaussian_1d(kernel, 5, 0.5f, 0.0f); + Mat Kernel(1, 5, CV_32F, (float*) kernel); - if ( m_grid_points ) deallocate( m_grid_points, m_grid_point_number ); - if ( m_oriented_grid_points ) - deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); - if ( m_cube_sigmas ) deallocate( m_cube_sigmas ); + Mat cvO; + // smooth the data matrix + filter2D( data, cvO, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - m_work_image.release(); + Mat dx(1, data_size, CV_32F); + Mat dy(1, data_size, CV_32F); + + gradient(cvO, data.rows, data.cols, dy, dx); + cvO.release(); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l(0) + l*data_size; + + for( int index=0; index(index) + zin * dy.at(index); + if( value > 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + return layers; } -void DAISY_Impl::compute_grid_points() +// data is not destroyed afterwards +static void layered_gradient( Mat data, int layer_no, Mat layers ) +{ + CV_Assert( !layers.empty() ); + + Mat cvI = data.clone(); + layers.setTo( Scalar(0) ); + int data_size = data.rows * data.cols; + + float kernel[5]; + gaussian_1d(kernel, 5, 0.5f, 0.0f); + Mat Kernel(1, 5, CV_32F, (float*) kernel); + + filter2D( cvI, cvI, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + + Mat dx(1, data_size, CV_32F); + Mat dy(1, data_size, CV_32F); + gradient( cvI, data.rows, data.cols, dy, dx ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l(0) + l*data_size; + + for( int index=0; index(index) + zin * dy.at(index); + if( value > 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } +} + +// transform a point via the homography +static void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) +{ + double kxp = H[0]*x + H[1]*y + H[2]; + double kyp = H[3]*x + H[4]*y + H[5]; + double kp = H[6]*x + H[7]*y + H[8]; + u = kxp / kp; + v = kyp / kp; +} + +inline void DAISY_Impl::compute_grid_points() { double r_step = m_rad / (double)m_rad_q_no; double t_step = 2*CV_PI/ m_th_q_no; - if( m_grid_points ) - deallocate( m_grid_points, m_grid_point_number ); + m_grid_points.release(); + m_grid_points = Mat( m_grid_point_number, 2, CV_64F ); - m_grid_points = allocate(m_grid_point_number, 2); for( int y=0; y(y,0) = 0; + m_grid_points.at(y,1) = 0; } for( int r=0; r(region+t,0) = (r+1)*r_step * sin( t*t_step ); + m_grid_points.at(region+t,1) = (r+1)*r_step * cos( t*t_step ); } } compute_oriented_grid_points(); } -/// Computes the descriptor by sampling convoluted orientation maps. -void DAISY_Impl::compute_descriptors() +inline void DAISY_Impl::normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) const +{ + if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; + else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); + else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); + else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); + else + CV_Error( Error::StsInternal, "No such normalization" ); +} + +// Computes the descriptor by sampling convoluted orientation maps. +inline void DAISY_Impl::compute_descriptors() { int y_off = m_roi.y; @@ -1094,12 +623,10 @@ void DAISY_Impl::compute_descriptors() int y_end = m_roi.y + m_roi.height; int x_end = m_roi.x + m_roi.width; - if( m_scale_invariant ) compute_scales(); - if( m_rotation_invariant ) compute_orientations(); - if( !m_descriptor_memory ) - m_dense_descriptors = allocate (m_roi.width*m_roi.height * m_descriptor_size); +// if( m_scale_invariant ) compute_scales(); +// if( m_rotation_invariant ) compute_orientations(); - memset(m_dense_descriptors, 0, sizeof(float)*m_roi.width*m_roi.height * m_descriptor_size); + m_dense_descriptors = Mat( m_roi.width*m_roi.height, m_descriptor_size, CV_32F, Scalar(0) ); int y, x, index, orientation; #if defined _OPENMP @@ -1109,63 +636,106 @@ void DAISY_Impl::compute_descriptors() { for( x=x_off; x= 0 && orientation < g_grid_orientation_resolution ) ) orientation = 0; - get_unnormalized_descriptor( y, x, orientation, &(m_dense_descriptors[index*m_descriptor_size]) ); + if( !m_orientation_map.empty() ) + orientation = (int) m_orientation_map.at( x, y ); + if( !( orientation >= 0 && orientation < g_grid_orientation_resolution ) ) + orientation = 0; + get_unnormalized_descriptor( y, x, orientation, m_dense_descriptors.ptr( index ) ); } } } -void DAISY_Impl::smooth_layers( float* layers, int h, int w, int layer_number, float sigma ) +inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ) { - int fsz = filter_size(sigma); - float* filter = new float[fsz]; - gaussian_1d(filter, fsz, sigma, 0); + int i; - float* layer=0; + float *layer = NULL; + + int kernel_size = filter_size( sigma ); + float kernel[kernel_size]; + gaussian_1d( kernel, kernel_size, sigma, 0 ); + + float* ptr = layers.ptr(0); + #if defined _OPENMP #pragma omp parallel for private(i, layer) #endif + for( i=0; i 1e-5 ) - divide( desc, m_descriptor_size, norm); + // divide with norm + for( int i=0; i( d ), nrm_type ); } -void DAISY_Impl::initialize() +inline void DAISY_Impl::initialize() { - CV_Assert(m_h != 0); // no image ? - CV_Assert(m_w != 0); + // no image ? + CV_Assert(m_image.rows != 0); + CV_Assert(m_image.cols != 0); + if( m_layer_size == 0 ) { - m_layer_size = m_h*m_w; - m_cube_size = m_layer_size*m_hist_th_q_no; + m_layer_size = m_image.rows * m_image.cols; + m_cube_size = m_layer_size * m_hist_th_q_no; } - int glsz = compute_workspace_memory(); - if( !m_workspace_memory ) m_smoothed_gradient_layers = new float[glsz]; + m_smoothed_gradient_layers = Mat( g_cube_number + 1, m_cube_size, CV_32F); - float* gradient_layers = m_smoothed_gradient_layers; - - layered_gradient( m_image, m_h, m_w, m_hist_th_q_no, gradient_layers ); + layered_gradient( m_image, m_hist_th_q_no, m_smoothed_gradient_layers ); // assuming a 0.5 image smoothness, we pull this to 1.6 as in sift - smooth_layers( gradient_layers, m_h, m_w, m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25) ); + smooth_layers( m_smoothed_gradient_layers, m_image.rows, m_image.cols, + m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25) ); } -void DAISY_Impl::compute_cube_sigmas() +inline void DAISY_Impl::compute_cube_sigmas() { - if( m_cube_sigmas == NULL ) + if( m_cube_sigmas.empty() ) { - // user didn't set the sigma's; set them from the descriptor parameters + // user didn't set the sigma's; + // set them from the descriptor parameters g_cube_number = m_rad_q_no; - m_cube_sigmas = allocate(g_cube_number); + + m_cube_sigmas = Mat(1, g_cube_number, CV_64F); double r_step = double(m_rad)/m_rad_q_no; for( int r=0; r< m_rad_q_no; r++ ) { - m_cube_sigmas[r] = (r+1)*r_step/2; + m_cube_sigmas.at(r) = (r+1) * r_step/2; } } update_selected_cubes(); } -void DAISY_Impl::set_cube_gaussians( double* sigma_array, int sz ) -{ - g_cube_number = sz; - - if( m_cube_sigmas ) deallocate( m_cube_sigmas ); - m_cube_sigmas = allocate( g_cube_number ); - - for( int r=0; r= m_cube_sigmas[g_cube_number-1] ) return g_cube_number-1; + if( rad <= m_cube_sigmas.at(0) ) + return 0; + if( rad >= m_cube_sigmas.at(g_cube_number-1) ) + return g_cube_number-1; float dist; float mindist=FLT_MAX; int mini=0; for( int c=0; c(c)-rad ); if( dist < mindist ) { mindist = dist; mini=c; @@ -1269,24 +829,25 @@ int DAISY_Impl::quantize_radius( float rad ) const return mini; } -void DAISY_Impl::compute_histograms() +inline void DAISY_Impl::compute_histograms() { int r, y, x, ind; float* hist=0; for( r=0; r(0) + r * m_cube_size; + float* src = m_smoothed_gradient_layers.ptr(0) + (r+1)* m_cube_size; #if defined _OPENMP #pragma omp parallel for private(y,x,ind,hist) #endif - for( y=0; y(0) + r*m_cube_size; #if defined _OPENMP #pragma omp parallel for #endif - for( int y=0; y(0); float* cube = NULL; double sigma; for( int r=0; r(0) + (r+1)*m_cube_size; // incremental smoothing - if( r == 0 ) sigma = m_cube_sigmas[0]; - else sigma = sqrt( m_cube_sigmas[r]*m_cube_sigmas[r] - m_cube_sigmas[r-1]*m_cube_sigmas[r-1] ); + if( r == 0 ) + sigma = m_cube_sigmas.at(0); + else + sigma = sqrt( m_cube_sigmas.at(r ) * m_cube_sigmas.at(r ) + - m_cube_sigmas.at(r-1) * m_cube_sigmas.at(r-1) ); - int fsz = filter_size(sigma); - float* filter = new float[fsz]; - gaussian_1d(filter, fsz, (float)sigma, 0); + int kernel_size = filter_size( sigma ); + float kernel[kernel_size]; + gaussian_1d(kernel, kernel_size, (float)sigma, 0); #if defined _OPENMP #pragma omp parallel for #endif for( int th=0; th( g_grid_orientation_resolution, m_grid_point_number*2 ); + m_oriented_grid_points = + Mat( g_grid_orientation_resolution, m_grid_point_number*2, CV_64F ); for( int i=0; i(k,0); + double x = m_grid_points.at(k,1); - point_list[2*k+1] = x*kos + y*zin; // x - point_list[2*k ] = -x*zin + y*kos; // y + point_list.at(2*k+1) = x*kos + y*zin; // x + point_list.at(2*k ) = -x*zin + y*kos; // y } } } -void DAISY_Impl::smooth_histogram(float *hist, int hsz) +inline void DAISY_Impl::smooth_histogram(Mat hist, int hsz) { int i; float prev, temp; - prev = hist[hsz - 1]; + prev = hist.at(hsz - 1); for (i = 0; i < hsz; i++) { - temp = hist[i]; - hist[i] = (float) ((prev + hist[i] + hist[(i + 1 == hsz) ? 0 : i + 1]) / 3.0); + temp = hist.at(i); + hist.at(i) = (prev + hist.at(i) + hist.at( (i + 1 == hsz) ? 0 : i + 1) ) / 3.0f; prev = temp; } } -float DAISY_Impl::interpolate_peak(float left, float center, float right) +inline float DAISY_Impl::interpolate_peak(float left, float center, float right) { if( center < 0.0 ) { @@ -1402,7 +975,7 @@ float DAISY_Impl::interpolate_peak(float left, float center, float right) else return (float) (0.5*(left -right)/den); } -int DAISY_Impl::filter_size( double sigma ) +inline int DAISY_Impl::filter_size( double sigma ) { int fsz = (int)(5*sigma); @@ -1415,26 +988,32 @@ int DAISY_Impl::filter_size( double sigma ) return fsz; } -void DAISY_Impl::compute_scales() +inline void DAISY_Impl::compute_scales() { //############################################################################### //# scale detection is work-in-progress! do not use it if you're not Engin Tola # //############################################################################### - int imsz = m_w * m_h; - + int kernel_size = 0; float sigma = (float) ( pow( g_sigma_step, g_scale_st)*g_sigma_0 ); - float* sim = blur_gaussian_2d( m_image, m_h, m_w, sigma, filter_size(sigma), false); + if( kernel_size == 0 ) kernel_size = (int)(3*sigma); + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 - float* next_sim = NULL; + float kernel[kernel_size]; + gaussian_1d( kernel, kernel_size, sigma, 0 ); + Mat Kernel( 1, kernel_size, CV_32F, (float*) kernel ); - float* max_dog = allocate(imsz); + Mat sim, next_sim; - m_scale_map = allocate(imsz); + // output gaussian image + filter2D( m_image, sim, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( sim, sim, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + + Mat max_dog( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); + m_scale_map = Mat( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); - memset( max_dog, 0, imsz*sizeof(float) ); - memset( m_scale_map, 0, imsz*sizeof(float) ); int i; float sigma_prev; @@ -1448,54 +1027,79 @@ void DAISY_Impl::compute_scales() sigma_inc = sqrt( sigma_new*sigma_new - sigma_prev*sigma_prev ); sigma_prev = sigma_new; - next_sim = blur_gaussian_2d( sim, m_h, m_w, sigma_inc, filter_size( sigma_inc ) , false); + kernel_size = filter_size( sigma_inc ); + if( kernel_size == 0 ) kernel_size = (int)(3 * sigma_inc); + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size= 3; // kernel size cannot be smaller than 3 + + float next_kernel[kernel_size]; + gaussian_1d( next_kernel, kernel_size, sigma_inc, 0 ); + Mat NextKernel( 1, kernel_size, CV_32F, (float*) next_kernel ); + + // output gaussian image + filter2D( sim, next_sim, CV_32F, NextKernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( next_sim, next_sim, CV_32F, NextKernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + #if defined _OPENMP #pragma omp parallel for #endif - for( int p=0; p max_dog[p] ) - { - max_dog[p] = dog; - m_scale_map[p] = (float) i; - } + for( int c=0; c(r,c) - sim.at(r,c) ); + if( dog > max_dog.at(r,c) ) + { + max_dog.at(r,c) = dog; + m_scale_map.at(r,c) = (float) i; + } + } } - deallocate( sim ); - + sim.release(); sim = next_sim; } - blur_gaussian_2d( m_scale_map, m_h, m_w, 10.0, filter_size(10), true); + kernel_size = filter_size( 10.0f ); + if( kernel_size == 0 ) kernel_size = (int)(3 * 10.0f); + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 + + + float filter_kernel[kernel_size]; + gaussian_1d( filter_kernel, kernel_size, 10.0f, 0 ); + Mat FilterKernel( 1, kernel_size, CV_32F, (float*) filter_kernel ); + + // output gaussian image + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); #if defined _OPENMP #pragma omp parallel for #endif - for( int q=0; q(r,c) = (float) round( m_scale_map.at(r,c) ); + } + } + //save( m_scale_map, m_image.rows, m_image.cols, "scales.dat"); } -void DAISY_Impl::compute_orientations() + +inline void DAISY_Impl::compute_orientations() { //##################################################################################### //# orientation detection is work-in-progress! do not use it if you're not Engin Tola # //##################################################################################### - CV_Assert( m_image != NULL ); + CV_Assert( !m_image.empty() ); - int data_size = m_w*m_h; - float* rotation_layers = layered_gradient( m_image, m_h, m_w, m_orientation_resolution ); + int data_size = m_image.cols * m_image.rows; + Mat rotation_layers = layered_gradient( m_image, m_orientation_resolution ); - m_orientation_map = new int[data_size]; - memset( m_orientation_map, 0, sizeof(int)*data_size ); + m_orientation_map = Mat(m_image.cols, m_image.rows, CV_16U, Scalar(0)); int ori, max_ind; int ind; @@ -1506,33 +1110,34 @@ void DAISY_Impl::compute_orientations() int x, y, kk; - float* hist=NULL; + Mat hist; float sigma_inc; - float sigma_prev = 0; + float sigma_prev = 0.0f; float sigma_new; for( int scale=0; scale(m_orientation_resolution); + hist = Mat(1, m_orientation_resolution, CV_32F); - for( x=0; x(y,x) != scale ) continue; for( ori=0; ori(ori) = rotation_layers.at(ori*data_size+ind); } for( kk=0; kk<6; kk++ ) @@ -1542,9 +1147,9 @@ void DAISY_Impl::compute_orientations() max_ind = 0; for( ori=0; ori max_val ) + if( hist.at(ori) > max_val ) { - max_val = hist[ori]; + max_val = hist.at(ori); max_ind = ori; } } @@ -1557,7 +1162,7 @@ void DAISY_Impl::compute_orientations() if( next >= m_orientation_resolution ) next -= m_orientation_resolution; - peak = interpolate_peak(hist[prev], hist[max_ind], hist[next]); + peak = interpolate_peak(hist.at(prev), hist.at(max_ind), hist.at(next)); angle = (float)( ((float)max_ind + peak)*360.0/m_orientation_resolution ); int iangle = int(angle); @@ -1565,54 +1170,26 @@ void DAISY_Impl::compute_orientations() if( iangle < 0 ) iangle += 360; if( iangle >= 360 ) iangle -= 360; - if( !(iangle >= 0.0 && iangle < 360.0) ) { angle = 0; } - - m_orientation_map[ ind ] = iangle; + m_orientation_map.at(y,x) = iangle; } - deallocate(hist); + hist.release(); } } - - deallocate( rotation_layers ); - compute_oriented_grid_points(); } -void DAISY_Impl::set_descriptor_memory( float* descriptor, long int d_size ) -{ - CV_Assert( m_descriptor_memory == false ); - CV_Assert( m_h*m_w != 0 ); - CV_Assert( m_roi.width*m_roi.height != 0 ); - CV_Assert( d_size >= compute_descriptor_memory() ); - - m_dense_descriptors = descriptor; - m_descriptor_memory = true; -} - -void DAISY_Impl::set_workspace_memory( float* workspace, long int w_size ) -{ - CV_Assert( m_workspace_memory == false ); - CV_Assert( m_h*m_w != 0 ); - CV_Assert( m_roi.width*m_roi.height != 0 ); - CV_Assert( w_size >= compute_workspace_memory() ); - - m_smoothed_gradient_layers = workspace; - m_workspace_memory = true; -} - -// ------------------------------------------------- -/* DAISY helper routines */ - inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* histogram ) { - if( is_outside(x, 0, m_w-1, y, 0, m_h-1) ) return; + if ( ! Point( x, y ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return; - float* spatial_shift = hcube + y * m_w + x; - int data_size = m_w * m_h; + float* spatial_shift = hcube + y * m_image.cols + x; + int data_size = m_image.cols * m_image.rows; for( int h=0; h= m_w-2 || mny >= m_h-2 ) + if( mnx >= m_image.cols-2 || mny >= m_image.rows-2 ) { memset(histogram, 0, sizeof(float)*m_hist_th_q_no); return; } - int ind = mny*m_w+mnx; + int ind = mny*m_image.cols+mnx; // A C --> pixel positions // B D float* A = hcube+ind*m_hist_th_q_no; - float* B = A+m_w*m_hist_th_q_no; + float* B = A+m_image.cols*m_hist_th_q_no; float* C = A+m_hist_th_q_no; - float* D = A+(m_w+1)*m_hist_th_q_no; + float* D = A+(m_image.cols+1)*m_hist_th_q_no; double alpha = mnx+1-x; double beta = mny+1-y; @@ -1688,8 +1265,11 @@ inline void DAISY_Impl::ti_get_histogram( float* histogram, double y, double x, inline void DAISY_Impl::ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const { - if( is_outside(x, 0, m_w-1, y, 0, m_h-1) ) return; - float* hptr = hcube + (y*m_w+x)*m_hist_th_q_no; + if ( ! Point( x, y ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return; + + float* hptr = hcube + (y*m_image.cols+x)*m_hist_th_q_no; for( int h=0; h=0 && x>=0 ); - descriptor = &(m_dense_descriptors[(y*m_w+x)*m_descriptor_size]); + CV_Assert( !m_dense_descriptors.empty() ); + CV_Assert( y=0 && x>=0 ); + descriptor = m_dense_descriptors.ptr( y*m_image.cols+x ); } inline void DAISY_Impl::get_descriptor( double y, double x, int orientation, float* descriptor ) const @@ -1725,22 +1305,24 @@ inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, f // i'm not changing the descriptor[] values if the gridpoint is outside // the image. you should memset the descriptor array to 0 if you don't // want to have stupid values there. - // - CV_Assert( y >= 0 && y < m_h ); - CV_Assert( x >= 0 && x < m_w ); + + CV_Assert( y >= 0 && y < m_image.rows ); + CV_Assert( x >= 0 && x < m_image.cols ); CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( m_smoothed_gradient_layers ); - CV_Assert( m_oriented_grid_points ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( !m_oriented_grid_points.empty() ); CV_Assert( descriptor != NULL ); double shift = m_orientation_shift_table[orientation]; - i_get_histogram( descriptor, y, x, shift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + i_get_histogram( descriptor, y, x, shift, ptr + g_selected_cubes[0]*m_cube_size ); int r, rdt, region; double yy, xx; float* histogram = 0; - double* grid = m_oriented_grid_points[orientation]; + + Mat grid = m_oriented_grid_points.row( orientation ); // petals of the flower for( r=0; r(2*region ); + xx = x + grid.at(2*region + 1); + + if ( ! Point( xx, yy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + histogram = descriptor+region*m_hist_th_q_no; - i_get_histogram( histogram, yy, xx, shift, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); + i_get_histogram( histogram, yy, xx, shift, ptr + g_selected_cubes[r]*m_cube_size ); } } } @@ -1764,12 +1350,12 @@ inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, // i'm not changing the descriptor[] values if the gridpoint is outside // the image. you should memset the descriptor array to 0 if you don't // want to have stupid values there. - // - CV_Assert( y >= 0 && y < m_h ); - CV_Assert( x >= 0 && x < m_w ); + + CV_Assert( y >= 0 && y < m_image.rows ); + CV_Assert( x >= 0 && x < m_image.cols ); CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( m_smoothed_gradient_layers ); - CV_Assert( m_oriented_grid_points ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( !m_oriented_grid_points.empty() ); CV_Assert( descriptor != NULL ); double shift = m_orientation_shift_table[orientation]; @@ -1780,27 +1366,30 @@ inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, int ix = (int)x; if( x - ix > 0.5 ) ix++; // center - ni_get_histogram( descriptor, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[0]*m_cube_size ); + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + ni_get_histogram( descriptor, iy, ix, ishift, ptr + g_selected_cubes[0]*m_cube_size ); double yy, xx; float* histogram=0; // petals of the flower int r, rdt, region; - double* grid = m_oriented_grid_points[orientation]; + Mat grid = m_oriented_grid_points.row( orientation ); for( r=0; r(2*region ); + xx = x + grid.at(2*region+1); iy = (int)yy; if( yy - iy > 0.5 ) iy++; ix = (int)xx; if( xx - ix > 0.5 ) ix++; - if( is_outside(ix, 0, m_w-1, iy, 0, m_h-1) ) continue; + if ( ! Point( xx, yy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; histogram = descriptor+region*m_hist_th_q_no; - ni_get_histogram( histogram, iy, ix, ishift, m_smoothed_gradient_layers+g_selected_cubes[r]*m_cube_size ); + ni_get_histogram( histogram, iy, ix, ishift, ptr + g_selected_cubes[r]*m_cube_size ); } } } @@ -1826,23 +1415,28 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d // i'm not changing the descriptor[] values if the gridpoint is outside // the image. you should memset the descriptor array to 0 if you don't // want to have stupid values there. - // + CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); CV_Assert( descriptor != NULL ); int hradius[MAX_CUBE_NO]; double hy, hx, ry, rx; point_transform_via_homography( H, x, y, hx, hy ); - if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; - point_transform_via_homography( H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); - double radius = l2norm( ry, rx, hy, hx ); + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return false; + + point_transform_via_homography( H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); hradius[0] = quantize_radius( (float) radius ); double shift = m_orientation_shift_table[orientation]; - i_get_histogram( descriptor, hy, hx, shift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + i_get_histogram( descriptor, hy, hx, shift, ptr + hradius[0]*m_cube_size ); double gy, gx; int r, rdt, th, region; @@ -1854,21 +1448,24 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d { region = rdt + th; - gy = y + m_grid_points[region][0]; - gx = x + m_grid_points[region][1]; + gy = y + m_grid_points.at(region,0); + gx = x + m_grid_points.at(region,1); point_transform_via_homography(H, gx, gy, hx, hy); if( th == 0 ) { - point_transform_via_homography(H, gx+m_cube_sigmas[g_selected_cubes[r]], gy, rx, ry); - radius = l2norm( ry, rx, hy, hx ); + point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1+d1 ); hradius[r] = quantize_radius( (float) radius ); } - if( is_outside(hx, 0, m_w-1, hy, 0, m_h-1) ) continue; + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; histogram = descriptor+region*m_hist_th_q_no; - i_get_histogram( histogram, hy, hx, shift, m_smoothed_gradient_layers+hradius[r]*m_cube_size ); + i_get_histogram( histogram, hy, hx, shift, ptr + hradius[r]*m_cube_size ); } } return true; @@ -1881,24 +1478,27 @@ inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, // i'm not changing the descriptor[] values if the gridpoint is outside // the image. you should memset the descriptor array to 0 if you don't // want to have stupid values there. - // + CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( m_smoothed_gradient_layers ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); CV_Assert( descriptor != NULL ); int hradius[MAX_CUBE_NO]; - double radius; double hy, hx, ry, rx; point_transform_via_homography(H, x, y, hx, hy ); - if( is_outside( hx, 0, m_w, hy, 0, m_h ) ) return false; + + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return false; double shift = m_orientation_shift_table[orientation]; int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; - point_transform_via_homography(H, x+m_cube_sigmas[g_selected_cubes[0]], y, rx, ry); - radius = l2norm( ry, rx, hy, hx ); + point_transform_via_homography(H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); hradius[0] = quantize_radius( (float) radius ); int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; @@ -1907,7 +1507,8 @@ inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, int r, rdt, th, region; double gy, gx; float* histogram=0; - ni_get_histogram( descriptor, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[0]*m_cube_size ); + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + ni_get_histogram( descriptor, ihy, ihx, ishift, ptr + hradius[0]*m_cube_size ); for( r=0; r(region,0); + gx = x + m_grid_points.at(region,1); point_transform_via_homography(H, gx, gy, hx, hy); if( th == 0 ) { - point_transform_via_homography(H, gx+m_cube_sigmas[g_selected_cubes[r]], gy, rx, ry); - radius = l2norm( ry, rx, hy, hx ); + point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1*d1 ); hradius[r] = quantize_radius( (float) radius ); } ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; - if( is_outside(ihx, 0, m_w-1, ihy, 0, m_h-1) ) continue; + if ( ! Point( ihx, ihy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + histogram = descriptor+region*m_hist_th_q_no; - ni_get_histogram( histogram, ihy, ihx, ishift, m_smoothed_gradient_layers+hradius[r]*m_cube_size ); + ni_get_histogram( histogram, ihy, ihx, ishift, ptr + hradius[r]*m_cube_size ); } } return true; } -void DAISY_Impl::initialize_single_descriptor_mode( ) +inline void DAISY_Impl::initialize_single_descriptor_mode( ) { initialize(); compute_smoothed_gradient_layers(); } -void DAISY_Impl::set_parameters( ) +inline void DAISY_Impl::set_parameters( ) { m_grid_point_number = m_rad_q_no * m_th_q_no + 1; // +1 is for center pixel m_descriptor_size = m_grid_point_number * m_hist_th_q_no; @@ -1952,7 +1557,7 @@ void DAISY_Impl::set_parameters( ) { m_orientation_shift_table[i] = i/360.0 * m_hist_th_q_no; } - m_layer_size = m_h*m_w; + m_layer_size = m_image.rows*m_image.cols; m_cube_size = m_layer_size*m_hist_th_q_no; compute_cube_sigmas(); @@ -1961,48 +1566,29 @@ void DAISY_Impl::set_parameters( ) // set/convert image array for daisy internal routines // daisy internals use CV_32F image with norm to 1.0f -void DAISY_Impl::set_image( InputArray _image ) +inline void DAISY_Impl::set_image( InputArray _image ) { // release previous image - // and previous daisies workspace + // and previous workspace reset(); - // fetch new image Mat image = _image.getMat(); - // image cannot be empty CV_Assert( ! image.empty() ); - - // base size - m_h = image.rows; - m_w = image.cols; - // clone image for conversion if ( image.depth() != CV_32F ) { - m_work_image = image.clone(); - + m_image = image.clone(); // convert to gray inplace - if( m_work_image.channels() > 1 ) - cvtColor( m_work_image, m_work_image, COLOR_BGR2GRAY ); - - // convert to float if it is necessary - if ( m_work_image.depth() != CV_32F ) - { - // convert and normalize - m_work_image.convertTo( m_work_image, CV_32F ); - m_work_image /= 255.0f; - } else - CV_Error( Error::StsUnsupportedFormat, "" ); - - // use cloned work image - m_image = m_work_image.ptr(0); - + if( m_image.channels() > 1 ) + cvtColor( m_image, m_image, COLOR_BGR2GRAY ); + // convert and normalize + m_image.convertTo( m_image, CV_32F ); + m_image /= 255.0f; } else // use original user supplied CV_32F image // should be a normalized one (cannot check) - m_image = image.ptr(0); - + m_image = image; } @@ -2019,7 +1605,7 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O set_image( _image ); // whole image - m_roi = Rect( 0, 0, m_w, m_h ); + m_roi = Rect( 0, 0, m_image.cols, m_image.rows ); // get homography Mat H = m_h_matrix; @@ -2080,8 +1666,7 @@ void DAISY_Impl::compute( InputArray _image, Rect roi, OutputArray _descriptors normalize_descriptors(); Mat descriptors = _descriptors.getMat(); - descriptors = Mat( m_roi.width * m_roi.height, m_descriptor_size, - CV_32F, &m_dense_descriptors[0] ); + descriptors = m_dense_descriptors; release_auxiliary(); } @@ -2099,7 +1684,7 @@ void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) set_image( _image ); // whole image - m_roi = Rect( 0, 0, m_w, m_h ); + m_roi = Rect( 0, 0, m_image.cols, m_image.rows ); set_parameters(); initialize_single_descriptor_mode(); @@ -2109,8 +1694,7 @@ void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) normalize_descriptors(); Mat descriptors = _descriptors.getMat(); - descriptors = Mat( m_h * m_w, m_descriptor_size, - CV_32F, &m_dense_descriptors[0] ); + descriptors = m_dense_descriptors; release_auxiliary(); } @@ -2121,31 +1705,25 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, : m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) { - m_w = 0; - m_h = 0; m_image = 0; - m_grid_point_number = 0; m_descriptor_size = 0; - m_smoothed_gradient_layers = NULL; - m_dense_descriptors = NULL; - m_grid_points = NULL; - m_oriented_grid_points = NULL; + m_grid_point_number = 0; + + m_grid_points.release(); + m_dense_descriptors.release(); + m_smoothed_gradient_layers.release(); + m_oriented_grid_points.release(); m_scale_invariant = false; m_rotation_invariant = false; - m_scale_map = NULL; - m_orientation_map = NULL; + m_scale_map.release(); + m_orientation_map.release(); m_orientation_resolution = 36; - m_scale_map = NULL; - m_cube_sigmas = NULL; - - // unused features - m_descriptor_memory = false; - m_workspace_memory = false; + m_cube_sigmas.release(); m_cube_size = 0; m_layer_size = 0; @@ -2158,12 +1736,11 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, // destructor DAISY_Impl::~DAISY_Impl() { - if ( !m_workspace_memory ) deallocate( m_smoothed_gradient_layers ); - deallocate( m_grid_points, m_grid_point_number ); - deallocate( m_oriented_grid_points, g_grid_orientation_resolution ); - deallocate( m_orientation_map ); - deallocate( m_scale_map ); - deallocate( m_cube_sigmas ); + m_scale_map.release(); + m_grid_points.release(); + m_orientation_map.release(); + m_oriented_grid_points.release(); + m_smoothed_gradient_layers.release(); } Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, From fe38c9ef469d079568cd287a1f9c1771fda50bc5 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 11 May 2015 15:17:29 +0300 Subject: [PATCH 08/14] Squash all commits into single one. --- modules/xfeatures2d/doc/xfeatures2d.bib | 20 + .../include/opencv2/xfeatures2d.hpp | 105 +- modules/xfeatures2d/perf/perf_daisy.cpp | 33 + modules/xfeatures2d/src/daisy.cpp | 1754 +++++++++++++++++ modules/xfeatures2d/test/test_features2d.cpp | 7 + .../test_rotation_and_scale_invariance.cpp | 18 + 6 files changed, 1936 insertions(+), 1 deletion(-) create mode 100644 modules/xfeatures2d/perf/perf_daisy.cpp create mode 100644 modules/xfeatures2d/src/daisy.cpp diff --git a/modules/xfeatures2d/doc/xfeatures2d.bib b/modules/xfeatures2d/doc/xfeatures2d.bib index 0fa73c8cd..bbfaadde5 100644 --- a/modules/xfeatures2d/doc/xfeatures2d.bib +++ b/modules/xfeatures2d/doc/xfeatures2d.bib @@ -44,3 +44,23 @@ year={2012}, organization={Ieee} } + +@incollection{LUCID, + title={Locally uniform comparison image descriptor}, + author={Ziegler, Andrew, Eric Christiansen, David Kriegman, and Serge J. Belongie} + booktitle={Advances in Neural Information Processing Systems} + pages={1--9} + year={2012} + publisher={NIPS} +} + +@article{Tola10, + author = "E. Tola and V. Lepetit and P. Fua", + title = {{DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo}}, + journal = "IEEE Transactions on Pattern Analysis and Machine Intelligence", + year = 2010, + month = "May", + pages = "815--830", + volume = "32", + number = "5" +} diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 5f0125776..744e78f98 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -128,7 +128,110 @@ class CV_EXPORTS BriefDescriptorExtractor : public DescriptorExtractor public: static Ptr create( int bytes = 32 ); }; - + +/** @brief Class implementing the locally uniform comparison image descriptor, described in @cite LUCID + +An image descriptor that can be computed very fast, while being +about as robust as, for example, SURF or BRIEF. + */ +class CV_EXPORTS LUCID : public DescriptorExtractor +{ +public: + /** + * @param lucid_kernel kernel for descriptor construction, where 1=3x3, 2=5x5, 3=7x7 and so forth + * @param blur_kernel kernel for blurring image prior to descriptor construction, where 1=3x3, 2=5x5, 3=7x7 and so forth + */ + static Ptr create(const int lucid_kernel, const int blur_kernel); +}; + +/** @brief Class implementing DAISY descriptor, described in @cite Tola10 + +@param radius radius of the descriptor at the initial scale +@param q_radius amount of radial range division quantity +@param q_theta amount of angular range division quantity +@param q_hist amount of gradient orientations range division quantity +DAISY::ONLY_KEYS means to compute descriptors only for keypoints in the list (default) and +DAISY::COMP_FULL will compute descriptors for all pixels in the given image +@param norm choose descriptors normalization type, where +DAISY::NRM_NONE will not do any normalization (default), +DAISY::NRM_PARTIAL mean that histograms are normalized independently for L2 norm equal to 1.0, +DAISY::NRM_FULL mean that descriptors are normalized for L2 norm equal to 1.0, +DAISY::NRM_SIFT mean that descriptors are normalized for L2 norm equal to 1.0 but no individual one is bigger than 0.154 as in SIFT +@param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image +@param interpolation switch to disable interpolation for speed improvement at minor quality loss +@param use_orientation sample patterns using keypoints orientation, disabled by default. + + */ +class CV_EXPORTS DAISY : public DescriptorExtractor +{ +public: + enum + { + NRM_NONE = 100, NRM_PARTIAL = 101, NRM_FULL = 102, NRM_SIFT = 103, + }; + static Ptr create( float radius = 15, int q_radius = 3, int q_theta = 8, + int q_hist = 8, int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false ); + + /** @overload + * @param image image to extract descriptors + * @param keypoints of interest within image + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ) = 0; + + /** @overload + * @param image image to extract descriptors + * @param roi region of interest within image + * @param descriptors resulted descriptors array for roi image pixels + */ + virtual void compute( InputArray image, Rect roi, OutputArray descriptors ) = 0; + + /**@overload + * @param image image to extract descriptors + * @param descriptors resulted descriptors array for all image pixels + */ + virtual void compute( InputArray image, OutputArray descriptors ) = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param orientation orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param orientation orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_descriptor true if descriptor was computed + */ + virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param orientation orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param orientation orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_unnormalized_descriptor true if descriptor was computed + */ + virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + +}; + + //! @} } diff --git a/modules/xfeatures2d/perf/perf_daisy.cpp b/modules/xfeatures2d/perf/perf_daisy.cpp new file mode 100644 index 000000000..18ade55e4 --- /dev/null +++ b/modules/xfeatures2d/perf/perf_daisy.cpp @@ -0,0 +1,33 @@ +#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 daisy; + +#define DAISY_IMAGES \ + "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\ + "stitching/a3.png" + +PERF_TEST_P(daisy, extract, testing::Values(DAISY_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 descriptor = DAISY::create(); + + vector points; + vector descriptors; + // compute all daisies in image + TEST_CYCLE() descriptor->compute(frame, descriptors); + + SANITY_CHECK(descriptors, 1e-4); +} diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp new file mode 100644 index 000000000..7d6c7fd4c --- /dev/null +++ b/modules/xfeatures2d/src/daisy.cpp @@ -0,0 +1,1754 @@ +/********************************************************************* + * Software License Agreement (BSD License) + * + * Copyright (c) 2009 + * Engin Tola + * web : http://www.engintola.com + * email : engin.tola+libdaisy@gmail.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 Willow Garage 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. + *********************************************************************/ + +/* + "DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo" + by Engin Tola, Vincent Lepetit and Pascal Fua. IEEE Transactions on + Pattern Analysis and achine Intelligence, 31 Mar. 2009. + IEEE computer Society Digital Library. IEEE Computer Society, + http:doi.ieeecomputersociety.org/10.1109/TPAMI.2009.77 + + "A fast local descriptor for dense matching" by Engin Tola, Vincent + Lepetit, and Pascal Fua. Intl. Conf. on Computer Vision and Pattern + Recognition, Alaska, USA, June 2008 + + OpenCV port by: Cristian Balint + */ + +#include "precomp.hpp" + + +#include +#include + +namespace cv +{ +namespace xfeatures2d +{ + +// constants +const double g_sigma_0 = 1; +const double g_sigma_1 = sqrt(2.0); +const double g_sigma_2 = 8; +const double g_sigma_step = std::pow(2,1.0/2); +const int g_scale_st = int( (log(g_sigma_1/g_sigma_0)) / log(g_sigma_step) ); +static int g_scale_en = 1; + +const double g_sigma_init = 1.6; +const static int g_grid_orientation_resolution = 360; + +static const int MAX_CUBE_NO = 64; +static const int MAX_NORMALIZATION_ITER = 5; + +int g_cube_number; +int g_selected_cubes[MAX_CUBE_NO]; // m_rad_q_no < MAX_CUBE_NO + +/* + !DAISY implementation + */ +class DAISY_Impl : public DAISY +{ + +public: + /** Constructor + * @param radius radius of the descriptor at the initial scale + * @param q_radius amount of radial range divisions + * @param q_theta amount of angular range divisions + * @param q_hist amount of gradient orientations range divisions + * @param norm normalization type + * @param H optional 3x3 homography matrix used to warp the grid of daisy but sampling keypoints remains unwarped on image + * @param interpolation switch to disable interpolation at minor costs of quality (default is true) + * @param use_orientation sample patterns using keypoints orientation, disabled by default. + */ + explicit DAISY_Impl(float radius=15, int q_radius=3, int q_theta=8, int q_hist=8, + int norm = DAISY::NRM_NONE, InputArray H = noArray(), + bool interpolation = true, bool use_orientation = false); + + virtual ~DAISY_Impl(); + + /** returns the descriptor length in bytes */ + virtual int descriptorSize() const { + // +1 is for center pixel + return ( (m_rad_q_no * m_th_q_no + 1) * m_hist_th_q_no ); + }; + + /** returns the descriptor type */ + virtual int descriptorType() const { return CV_32F; } + + /** returns the default norm type */ + virtual int defaultNorm() const { return NORM_L2; } + + /** + * @param image image to extract descriptors + * @param keypoints of interest within image + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, std::vector& keypoints, OutputArray descriptors ); + + /** @overload + * @param image image to extract descriptors + * @param roi region of interest within image + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, Rect roi, OutputArray descriptors ); + + /** @overload + * @param image image to extract descriptors + * @param descriptors resulted descriptors array + */ + virtual void compute( InputArray image, OutputArray descriptors ); + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_descriptor true if descriptor was computed + */ + virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage + */ + virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) + * @param H homography matrix for warped grid + * @param descriptor supplied array for descriptor storage + * @param get_unnormalized_descriptor true if descriptor was computed + */ + virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + +protected: + + /* + * DAISY parameters + */ + + // maximum radius of the descriptor region. + float m_rad; + + // the number of quantizations of the radius. + int m_rad_q_no; + + // the number of quantizations of the angle. + int m_th_q_no; + + // the number of quantizations of the gradient orientations. + int m_hist_th_q_no; + + // holds the type of the normalization to apply; equals to NRM_PARTIAL by + // default. change the value using set_normalization() function. + int m_nrm_type; + + // number of bins in the histograms while computing orientation + int m_orientation_resolution; + + // the number of grid locations + int m_grid_point_number; + + // the size of the descriptor vector + int m_descriptor_size; + + // size of m_hsz layers at a single sigma: m_hsz * m_layer_size + int m_cube_size; + + // size of the layer : + // m_roi.width*m_roi.height + int m_layer_size; + + // the clipping threshold to use in normalization: values above this value + // are clipped to this value for normalize_sift_way() function + float m_descriptor_normalization_threshold; + + /* + * DAISY switches + */ + + // if set to true, descriptors are scale invariant + bool m_scale_invariant; + + // if set to true, descriptors are rotation invariant + bool m_rotation_invariant; + + // if enabled, descriptors are computed with casting non-integer locations + // to integer positions otherwise we use interpolation. + bool m_disable_interpolation; + + // switch to enable sample by keypoints orientation + bool m_use_orientation; + + /* + * DAISY arrays + */ + + // holds optional H matrix + Mat m_h_matrix; + + // input image. + Mat m_image; + + // image roi + Rect m_roi; + + // stores the descriptors : + // its size is [ m_roi.width*m_roi.height*m_descriptor_size ]. + Mat m_dense_descriptors; + + // stores the layered gradients in successively smoothed form : + // layer[n] = m_gradient_layers * gaussian( sigma_n ); + // n>= 1; layer[0] is the layered_gradient + Mat m_smoothed_gradient_layers; + + // hold the scales of the pixels + Mat m_scale_map; + + // holds the orientaitons of the pixels + Mat m_orientation_map; + + // Holds the oriented coordinates (y,x) of the grid points of the region. + Mat m_oriented_grid_points; + + // holds the gaussian sigmas for radius quantizations for an incremental + // application + Mat m_cube_sigmas; + + // Holds the coordinates (y,x) of the grid points of the region. + Mat m_grid_points; + + // holds the amount of shift that's required for histogram computation + double m_orientation_shift_table[360]; + + +private: + + // two possible computational mode + // ONLY_KEYS -> (mode_1) compute descriptors on demand + // COMP_FULL -> (mode_2) compute all descriptors from image + enum { ONLY_KEYS = 0, COMP_FULL = 1 }; + + /* + * DAISY functions + */ + + // initializes the class: computes gradient and structure-points + inline void initialize(); + + // initializes for get_descriptor(double, double, int) mode: pre-computes + // convolutions of gradient layers in m_smoothed_gradient_layers + inline void initialize_single_descriptor_mode(); + + // set & precompute parameters + inline void set_parameters(); + + // image set image as working + inline void set_image( InputArray image ); + + // releases all the used memory; call this if you want to process + // multiple images within a loop. + inline void reset(); + + // releases unused memory after descriptor computation is completed. + inline void release_auxiliary(); + + // computes the descriptors for every pixel in the image. + inline void compute_descriptors(); + + // computes scales for every pixel and scales the structure grid so that the + // resulting descriptors are scale invariant. you must set + // m_scale_invariant flag to 1 for the program to call this function + inline void compute_scales(); + + // compute the smoothed gradient layers. + inline void compute_smoothed_gradient_layers(); + + // computes pixel orientations and rotates the structure grid so that + // resulting descriptors are rotation invariant. If the scales is also + // detected, then orientations are computed at the computed scales. you must + // set m_rotation_invariant flag to 1 for the program to call this function + inline void compute_orientations(); + + // computes the histogram at yx; the size of histogram is m_hist_th_q_no + inline void compute_histogram( float* hcube, int y, int x, float* histogram ); + + // reorganizes the cube data so that histograms are sequential in memory. + inline void compute_histograms(); + + // computes the sigma's of layers from descriptor parameters if the user did + // not sets it. these define the size of the petals of the descriptor. + inline void compute_cube_sigmas(); + + // Computes the locations of the unscaled unrotated points where the + // histograms are going to be computed according to the given parameters. + inline void compute_grid_points(); + + // Computes the locations of the unscaled rotated points where the + // histograms are going to be computed according to the given parameters. + inline void compute_oriented_grid_points(); + + // normalizes the descriptor + inline void normalize_descriptor( float* desc, int nrm_type ) const; + + // applies one of the normalizations (partial,full,sift) to the desciptors. + inline void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); + + + // emulates the way sift is normalized. + inline void normalize_sift_way( float* desc ) const; + + // normalizes the descriptor histogram by histogram + inline void normalize_partial( float* desc ) const; + + // normalizes the full descriptor. + inline void normalize_full( float* desc ) const; + + // normalizes histograms individually + inline void normalize_histograms(); + + inline void update_selected_cubes(); + + // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram + // is connected in a circular buffer. + inline void smooth_histogram( Mat hist, int bins ); + + // smooths each of the layers by a Gaussian having "sigma" standart + // deviation. + inline void smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ); + + // returns the descriptor vector for the point (y, x) !!! use this for + // precomputed operations meaning that you must call compute_descriptors() + // before calling this function. if you want normalized descriptors, call + // normalize_descriptors() before calling compute_descriptors() + inline void get_descriptor( int y, int x, float* &descriptor ); + + // does not use interpolation while computing the histogram. + inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const; + + // returns the interpolated histogram: picks either bi_get_histogram or + // ti_get_histogram depending on 'shift' + inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ) const; + + // records the histogram that is computed by bilinear interpolation + // regarding the shift in the spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ) const; + + // records the histogram that is computed by trilinear interpolation + // regarding the shift in layers and spatial coordinates. hcube is the + // histogram cube for a constant smoothness level. + inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ) const; + + // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor + inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ) const; + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ) const; + + // uses interpolation for no interpolation call ni_get_descriptor. see also get_descriptor + inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor + inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + + inline int quantize_radius( float rad ) const; + + inline int filter_size( double sigma ); + + // Return a number in the range [-0.5, 0.5] that represents the location of + // the peak of a parabola passing through the 3 evenly spaced samples. The + // center value is assumed to be greater than or equal to the other values + // if positive, or less than if negative. + inline float interpolate_peak( float left, float center, float right ); + + +}; // END DAISY_Impl CLASS + + +// ------------------------------------------------- +/* DAISY computation routines */ + +inline void DAISY_Impl::reset() +{ + m_image.release(); + + m_orientation_map.release(); + m_scale_map.release(); + + m_dense_descriptors.release(); + m_smoothed_gradient_layers.release(); +} + +inline void DAISY_Impl::release_auxiliary() +{ + m_orientation_map.release(); + m_scale_map.release(); + + m_smoothed_gradient_layers.release(); + + m_grid_points.release(); + m_oriented_grid_points.release(); + m_cube_sigmas.release(); + + m_image.release(); +} + +// creates a 1D gaussian filter with N(mean,sigma). +static void gaussian_1d( float* fltr, int fsz, float sigma, float mean ) +{ + CV_Assert(fltr != NULL); + + int sz = (fsz - 1) / 2; + int counter = -1; + float sum = 0.0f; + float v = 2 * sigma*sigma; + for( int x=-sz; x<=sz; x++ ) + { + counter++; + fltr[counter] = exp((-((float)x-mean)*((float)x-mean))/v); + sum += fltr[counter]; + } + + if( sum != 0 ) + for( int x=0; x0 && x(ind) = (im.at(ind+1)-im.at(ind-1)) / 2.0f; + if( x==0 ) dx.at(ind) = im.at(ind+1)-im.at(ind ); + if( x==w-1 ) dx.at(ind) = im.at(ind )-im.at(ind-1); + + // dy + if( y>0 && y(ind) = (im.at(ind+w)-im.at(ind-w)) / 2.0f; + if( y==0 ) dy.at(ind) = im.at(ind+w)-im.at(ind ); + if( y==h-1 ) dy.at(ind) = im.at(ind )-im.at(ind-w); + } + } +} + +static Mat layered_gradient( Mat data, int layer_no = 8 ) +{ + int data_size = data.rows * data.cols; + Mat layers( 1, layer_no*data_size, CV_32F, Scalar(0) ); + + float kernel[5]; + gaussian_1d(kernel, 5, 0.5f, 0.0f); + Mat Kernel(1, 5, CV_32F, (float*) kernel); + + Mat cvO; + // smooth the data matrix + filter2D( data, cvO, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + + Mat dx(1, data_size, CV_32F); + Mat dy(1, data_size, CV_32F); + + gradient(cvO, data.rows, data.cols, dy, dx); + cvO.release(); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l(0) + l*data_size; + + for( int index=0; index(index) + zin * dy.at(index); + if( value > 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } + return layers; +} + +// data is not destroyed afterwards +static void layered_gradient( Mat data, int layer_no, Mat layers ) +{ + CV_Assert( !layers.empty() ); + + Mat cvI = data.clone(); + layers.setTo( Scalar(0) ); + int data_size = data.rows * data.cols; + + float kernel[5]; + gaussian_1d(kernel, 5, 0.5f, 0.0f); + Mat Kernel(1, 5, CV_32F, (float*) kernel); + + filter2D( cvI, cvI, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + + Mat dx(1, data_size, CV_32F); + Mat dy(1, data_size, CV_32F); + gradient( cvI, data.rows, data.cols, dy, dx ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int l=0; l(0) + l*data_size; + + for( int index=0; index(index) + zin * dy.at(index); + if( value > 0 ) layer_l[index] = value; + else layer_l[index] = 0; + } + } +} + +// transform a point via the homography +static void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) +{ + double kxp = H[0]*x + H[1]*y + H[2]; + double kyp = H[3]*x + H[4]*y + H[5]; + double kp = H[6]*x + H[7]*y + H[8]; + u = kxp / kp; + v = kyp / kp; +} + +inline void DAISY_Impl::compute_grid_points() +{ + double r_step = m_rad / (double)m_rad_q_no; + double t_step = 2*CV_PI/ m_th_q_no; + + m_grid_points.release(); + m_grid_points = Mat( m_grid_point_number, 2, CV_64F ); + + for( int y=0; y(y,0) = 0; + m_grid_points.at(y,1) = 0; + } + + for( int r=0; r(region+t,0) = (r+1)*r_step * sin( t*t_step ); + m_grid_points.at(region+t,1) = (r+1)*r_step * cos( t*t_step ); + } + } + + compute_oriented_grid_points(); +} + +inline void DAISY_Impl::normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) const +{ + if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; + else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); + else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); + else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); + else + CV_Error( Error::StsInternal, "No such normalization" ); +} + +// Computes the descriptor by sampling convoluted orientation maps. +inline void DAISY_Impl::compute_descriptors() +{ + + int y_off = m_roi.y; + int x_off = m_roi.x; + int y_end = m_roi.y + m_roi.height; + int x_end = m_roi.x + m_roi.width; + +// if( m_scale_invariant ) compute_scales(); +// if( m_rotation_invariant ) compute_orientations(); + + m_dense_descriptors = Mat( m_roi.width*m_roi.height, m_descriptor_size, CV_32F, Scalar(0) ); + + int y, x, index, orientation; +#if defined _OPENMP +#pragma omp parallel for private(y,x,index,orientation) +#endif + for( y=y_off; y( x, y ); + if( !( orientation >= 0 && orientation < g_grid_orientation_resolution ) ) + orientation = 0; + get_unnormalized_descriptor( y, x, orientation, m_dense_descriptors.ptr( index ) ); + } + } +} + +inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ) +{ + + int i; + float *layer = NULL; + + int kernel_size = filter_size( sigma ); + float kernel[kernel_size]; + gaussian_1d( kernel, kernel_size, sigma, 0 ); + + float* ptr = layers.ptr(0); + +#if defined _OPENMP +#pragma omp parallel for private(i, layer) +#endif + + for( i=0; i 1e-5 ) + // divide with norm + for( int i=0; i m_descriptor_normalization_threshold ) + { + desc[ h ] = m_descriptor_normalization_threshold; + changed = true; + } + } + } +} + +inline void DAISY_Impl::normalize_descriptors( int nrm_type ) +{ + int d; + int number_of_descriptors = m_roi.width * m_roi.height; + +#if defined _OPENMP +#pragma omp parallel for private(d) +#endif + for( d=0; d( d ), nrm_type ); +} + +inline void DAISY_Impl::initialize() +{ + // no image ? + CV_Assert(m_image.rows != 0); + CV_Assert(m_image.cols != 0); + + if( m_layer_size == 0 ) { + m_layer_size = m_image.rows * m_image.cols; + m_cube_size = m_layer_size * m_hist_th_q_no; + } + + m_smoothed_gradient_layers = Mat( g_cube_number + 1, m_cube_size, CV_32F); + + layered_gradient( m_image, m_hist_th_q_no, m_smoothed_gradient_layers ); + + // assuming a 0.5 image smoothness, we pull this to 1.6 as in sift + smooth_layers( m_smoothed_gradient_layers, m_image.rows, m_image.cols, + m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25) ); + +} + +inline void DAISY_Impl::compute_cube_sigmas() +{ + if( m_cube_sigmas.empty() ) + { + // user didn't set the sigma's; + // set them from the descriptor parameters + g_cube_number = m_rad_q_no; + + m_cube_sigmas = Mat(1, g_cube_number, CV_64F); + + double r_step = double(m_rad)/m_rad_q_no; + for( int r=0; r< m_rad_q_no; r++ ) + { + m_cube_sigmas.at(r) = (r+1) * r_step/2; + } + } + update_selected_cubes(); +} + +inline void DAISY_Impl::update_selected_cubes() +{ + for( int r=0; r(0) ) + return 0; + if( rad >= m_cube_sigmas.at(g_cube_number-1) ) + return g_cube_number-1; + + float dist; + float mindist=FLT_MAX; + int mini=0; + for( int c=0; c(c)-rad ); + if( dist < mindist ) { + mindist = dist; + mini=c; + } + } + return mini; +} + +inline void DAISY_Impl::compute_histograms() +{ + int r, y, x, ind; + float* hist=0; + + for( r=0; r(0) + r * m_cube_size; + float* src = m_smoothed_gradient_layers.ptr(0) + (r+1)* m_cube_size; + +#if defined _OPENMP +#pragma omp parallel for private(y,x,ind,hist) +#endif + for( y=0; y(0) + r*m_cube_size; + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int y=0; y(0); + float* cube = NULL; + + double sigma; + for( int r=0; r(0) + (r+1)*m_cube_size; + + // incremental smoothing + if( r == 0 ) + sigma = m_cube_sigmas.at(0); + else + sigma = sqrt( m_cube_sigmas.at(r ) * m_cube_sigmas.at(r ) + - m_cube_sigmas.at(r-1) * m_cube_sigmas.at(r-1) ); + + int kernel_size = filter_size( sigma ); + float kernel[kernel_size]; + gaussian_1d(kernel, kernel_size, (float)sigma, 0); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int th=0; th(k,0); + double x = m_grid_points.at(k,1); + + point_list.at(2*k+1) = x*kos + y*zin; // x + point_list.at(2*k ) = -x*zin + y*kos; // y + } + } +} + +inline void DAISY_Impl::smooth_histogram(Mat hist, int hsz) +{ + int i; + float prev, temp; + + prev = hist.at(hsz - 1); + for (i = 0; i < hsz; i++) + { + temp = hist.at(i); + hist.at(i) = (prev + hist.at(i) + hist.at( (i + 1 == hsz) ? 0 : i + 1) ) / 3.0f; + prev = temp; + } +} + +inline float DAISY_Impl::interpolate_peak(float left, float center, float right) +{ + if( center < 0.0 ) + { + left = -left; + center = -center; + right = -right; + } + CV_Assert(center >= left && center >= right); + + float den = (float) (left - 2.0 * center + right); + + if( den == 0 ) return 0; + else return (float) (0.5*(left -right)/den); +} + +inline int DAISY_Impl::filter_size( double sigma ) +{ + int fsz = (int)(5*sigma); + + // kernel size must be odd + if( fsz%2 == 0 ) fsz++; + + // kernel size cannot be smaller than 3 + if( fsz < 3 ) fsz = 3; + + return fsz; +} + +inline void DAISY_Impl::compute_scales() +{ + //############################################################################### + //# scale detection is work-in-progress! do not use it if you're not Engin Tola # + //############################################################################### + + int kernel_size = 0; + float sigma = (float) ( pow( g_sigma_step, g_scale_st)*g_sigma_0 ); + + if( kernel_size == 0 ) kernel_size = (int)(3*sigma); + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 + + float kernel[kernel_size]; + gaussian_1d( kernel, kernel_size, sigma, 0 ); + Mat Kernel( 1, kernel_size, CV_32F, (float*) kernel ); + + Mat sim, next_sim; + + // output gaussian image + filter2D( m_image, sim, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( sim, sim, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + + Mat max_dog( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); + m_scale_map = Mat( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); + + + int i; + float sigma_prev; + float sigma_new; + float sigma_inc; + + sigma_prev = (float) g_sigma_0; + for( i=0; i(r,c) - sim.at(r,c) ); + if( dog > max_dog.at(r,c) ) + { + max_dog.at(r,c) = dog; + m_scale_map.at(r,c) = (float) i; + } + } + } + sim.release(); + sim = next_sim; + } + + kernel_size = filter_size( 10.0f ); + if( kernel_size == 0 ) kernel_size = (int)(3 * 10.0f); + if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd + if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 + + + float filter_kernel[kernel_size]; + gaussian_1d( filter_kernel, kernel_size, 10.0f, 0 ); + Mat FilterKernel( 1, kernel_size, CV_32F, (float*) filter_kernel ); + + // output gaussian image + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + +#if defined _OPENMP +#pragma omp parallel for +#endif + for( int r=0; r(r,c) = (float) round( m_scale_map.at(r,c) ); + } + } + //save( m_scale_map, m_image.rows, m_image.cols, "scales.dat"); +} + + +inline void DAISY_Impl::compute_orientations() +{ + //##################################################################################### + //# orientation detection is work-in-progress! do not use it if you're not Engin Tola # + //##################################################################################### + + CV_Assert( !m_image.empty() ); + + int data_size = m_image.cols * m_image.rows; + Mat rotation_layers = layered_gradient( m_image, m_orientation_resolution ); + + m_orientation_map = Mat(m_image.cols, m_image.rows, CV_16U, Scalar(0)); + + int ori, max_ind; + int ind; + float max_val; + + int next, prev; + float peak, angle; + + int x, y, kk; + + Mat hist; + + float sigma_inc; + float sigma_prev = 0.0f; + float sigma_new; + + for( int scale=0; scale(y,x) != scale ) continue; + + for( ori=0; ori(ori) = rotation_layers.at(ori*data_size+ind); + } + + for( kk=0; kk<6; kk++ ) + smooth_histogram( hist, m_orientation_resolution ); + + max_val = -1; + max_ind = 0; + for( ori=0; ori(ori) > max_val ) + { + max_val = hist.at(ori); + max_ind = ori; + } + } + + prev = max_ind-1; + if( prev < 0 ) + prev += m_orientation_resolution; + + next = max_ind+1; + if( next >= m_orientation_resolution ) + next -= m_orientation_resolution; + + peak = interpolate_peak(hist.at(prev), hist.at(max_ind), hist.at(next)); + angle = (float)( ((float)max_ind + peak)*360.0/m_orientation_resolution ); + + int iangle = int(angle); + + if( iangle < 0 ) iangle += 360; + if( iangle >= 360 ) iangle -= 360; + + if( !(iangle >= 0.0 && iangle < 360.0) ) + { + angle = 0; + } + m_orientation_map.at(y,x) = iangle; + } + hist.release(); + } + } + compute_oriented_grid_points(); +} + +inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* histogram ) +{ + if ( ! Point( x, y ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return; + + float* spatial_shift = hcube + y * m_image.cols + x; + int data_size = m_image.cols * m_image.rows; + + for( int h=0; h 0.99 ) bi_get_histogram( histogram, y, x, ishift+1, cube ); + else ti_get_histogram( histogram, y, x, shift , cube ); +} + +inline void DAISY_Impl::bi_get_histogram( float* histogram, double y, double x, int shift, float* hcube ) const +{ + int mnx = int( x ); + int mny = int( y ); + + if( mnx >= m_image.cols-2 || mny >= m_image.rows-2 ) + { + memset(histogram, 0, sizeof(float)*m_hist_th_q_no); + return; + } + + int ind = mny*m_image.cols+mnx; + // A C --> pixel positions + // B D + float* A = hcube+ind*m_hist_th_q_no; + float* B = A+m_image.cols*m_hist_th_q_no; + float* C = A+m_hist_th_q_no; + float* D = A+(m_image.cols+1)*m_hist_th_q_no; + + double alpha = mnx+1-x; + double beta = mny+1-y; + + float w0 = (float) (alpha*beta); + float w1 = (float) (beta-w0); // (1-alpha)*beta; + float w2 = (float) (alpha-w0); // (1-beta)*alpha; + float w3 = (float) (1+w0-alpha-beta); // (1-beta)*(1-alpha); + + int h; + + for( h=0; h= m_hist_th_q_no ) hi -= m_hist_th_q_no; + histogram[h] = hptr[hi]; + } +} + +inline void DAISY_Impl::get_descriptor( int y, int x, float* &descriptor ) +{ + CV_Assert( !m_dense_descriptors.empty() ); + CV_Assert( y=0 && x>=0 ); + descriptor = m_dense_descriptors.ptr( y*m_image.cols+x ); +} + +inline void DAISY_Impl::get_descriptor( double y, double x, int orientation, float* descriptor ) const +{ + get_unnormalized_descriptor(y, x, orientation, descriptor ); + normalize_descriptor(descriptor, m_nrm_type); +} + +inline void DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const +{ + if( m_disable_interpolation ) ni_get_descriptor(y,x,orientation,descriptor); + else i_get_descriptor(y,x,orientation,descriptor); +} + +inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, float* descriptor ) const +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + + CV_Assert( y >= 0 && y < m_image.rows ); + CV_Assert( x >= 0 && x < m_image.cols ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( !m_oriented_grid_points.empty() ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + i_get_histogram( descriptor, y, x, shift, ptr + g_selected_cubes[0]*m_cube_size ); + + int r, rdt, region; + double yy, xx; + float* histogram = 0; + + Mat grid = m_oriented_grid_points.row( orientation ); + + // petals of the flower + for( r=0; r(2*region ); + xx = x + grid.at(2*region + 1); + + if ( ! Point( xx, yy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + i_get_histogram( histogram, yy, xx, shift, ptr + g_selected_cubes[r]*m_cube_size ); + } + } +} + +inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, float* descriptor ) const +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + + CV_Assert( y >= 0 && y < m_image.rows ); + CV_Assert( x >= 0 && x < m_image.cols ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( !m_oriented_grid_points.empty() ); + CV_Assert( descriptor != NULL ); + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; + if( shift - ishift > 0.5 ) ishift++; + + int iy = (int)y; if( y - iy > 0.5 ) iy++; + int ix = (int)x; if( x - ix > 0.5 ) ix++; + + // center + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + ni_get_histogram( descriptor, iy, ix, ishift, ptr + g_selected_cubes[0]*m_cube_size ); + + double yy, xx; + float* histogram=0; + // petals of the flower + int r, rdt, region; + Mat grid = m_oriented_grid_points.row( orientation ); + for( r=0; r(2*region ); + xx = x + grid.at(2*region+1); + iy = (int)yy; if( yy - iy > 0.5 ) iy++; + ix = (int)xx; if( xx - ix > 0.5 ) ix++; + + if ( ! Point( xx, yy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, iy, ix, ishift, ptr + g_selected_cubes[r]*m_cube_size ); + } + } +} + +// Warped get_descriptor's +inline bool DAISY_Impl::get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const +{ + bool rval = get_unnormalized_descriptor(y,x,orientation, H, descriptor); + if( rval ) normalize_descriptor(descriptor, m_nrm_type); + return rval; +} + +inline bool DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const +{ + if( m_disable_interpolation ) return ni_get_descriptor(y,x,orientation,H,descriptor); + else return i_get_descriptor(y,x,orientation,H,descriptor); +} + +inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + point_transform_via_homography( H, x, y, hx, hy ); + + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return false; + + point_transform_via_homography( H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); + hradius[0] = quantize_radius( (float) radius ); + + double shift = m_orientation_shift_table[orientation]; + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + i_get_histogram( descriptor, hy, hx, shift, ptr + hradius[0]*m_cube_size ); + + double gy, gx; + int r, rdt, th, region; + float* histogram=0; + for( r=0; r(region,0); + gx = x + m_grid_points.at(region,1); + + point_transform_via_homography(H, gx, gy, hx, hy); + if( th == 0 ) + { + point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1+d1 ); + hradius[r] = quantize_radius( (float) radius ); + } + + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + i_get_histogram( histogram, hy, hx, shift, ptr + hradius[r]*m_cube_size ); + } + } + return true; +} + +inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const +{ + // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); + // + // i'm not changing the descriptor[] values if the gridpoint is outside + // the image. you should memset the descriptor array to 0 if you don't + // want to have stupid values there. + + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !m_smoothed_gradient_layers.empty() ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + + point_transform_via_homography(H, x, y, hx, hy ); + + if ( ! Point( hx, hy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) return false; + + double shift = m_orientation_shift_table[orientation]; + int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; + + point_transform_via_homography(H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); + hradius[0] = quantize_radius( (float) radius ); + + int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + int r, rdt, th, region; + double gy, gx; + float* histogram=0; + float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); + ni_get_histogram( descriptor, ihy, ihx, ishift, ptr + hradius[0]*m_cube_size ); + for( r=0; r(region,0); + gx = x + m_grid_points.at(region,1); + + point_transform_via_homography(H, gx, gy, hx, hy); + if( th == 0 ) + { + point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1*d1 ); + hradius[r] = quantize_radius( (float) radius ); + } + + ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + if ( ! Point( ihx, ihy ).inside( + Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) + ) continue; + + histogram = descriptor+region*m_hist_th_q_no; + ni_get_histogram( histogram, ihy, ihx, ishift, ptr + hradius[r]*m_cube_size ); + } + } + return true; +} + +inline void DAISY_Impl::initialize_single_descriptor_mode( ) +{ + initialize(); + compute_smoothed_gradient_layers(); +} + +inline void DAISY_Impl::set_parameters( ) +{ + m_grid_point_number = m_rad_q_no * m_th_q_no + 1; // +1 is for center pixel + m_descriptor_size = m_grid_point_number * m_hist_th_q_no; + + for( int i=0; i<360; i++ ) + { + m_orientation_shift_table[i] = i/360.0 * m_hist_th_q_no; + } + m_layer_size = m_image.rows*m_image.cols; + m_cube_size = m_layer_size*m_hist_th_q_no; + + compute_cube_sigmas(); + compute_grid_points(); +} + +// set/convert image array for daisy internal routines +// daisy internals use CV_32F image with norm to 1.0f +inline void DAISY_Impl::set_image( InputArray _image ) +{ + // release previous image + // and previous workspace + reset(); + // fetch new image + Mat image = _image.getMat(); + // image cannot be empty + CV_Assert( ! image.empty() ); + // clone image for conversion + if ( image.depth() != CV_32F ) { + + m_image = image.clone(); + // convert to gray inplace + if( m_image.channels() > 1 ) + cvtColor( m_image, m_image, COLOR_BGR2GRAY ); + // convert and normalize + m_image.convertTo( m_image, CV_32F ); + m_image /= 255.0f; + } else + // use original user supplied CV_32F image + // should be a normalized one (cannot check) + m_image = image; +} + + +// ------------------------------------------------- +/* DAISY interface implementation */ + +// keypoint scope +void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + set_image( _image ); + + // whole image + m_roi = Rect( 0, 0, m_image.cols, m_image.rows ); + + // get homography + Mat H = m_h_matrix; + + // convert to double if case + if ( H.depth() != CV_64F ) + H.convertTo( H, CV_64F ); + + set_parameters(); + + initialize_single_descriptor_mode(); + + // allocate array + _descriptors.create( (int) keypoints.size(), m_descriptor_size, CV_32F ); + + // prepare descriptors + Mat descriptors = _descriptors.getMat(); + descriptors.setTo( Scalar(0) ); + + // iterate over keypoints + // and fill computed descriptors + if ( H.empty() ) + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &descriptors.at( k, 0 ) ); + } + else + for (int k = 0; k < (int) keypoints.size(); k++) + { + get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + m_use_orientation ? (int) keypoints[k].angle : 0, + &H.at( 0 ), &descriptors.at( k, 0 ) ); + } + +} + +// full scope with roi +void DAISY_Impl::compute( InputArray _image, Rect roi, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + CV_Assert( m_h_matrix.empty() ); + CV_Assert( ! m_use_orientation ); + + set_image( _image ); + + m_roi = roi; + + set_parameters(); + initialize_single_descriptor_mode(); + + // compute full desc + compute_descriptors(); + normalize_descriptors(); + + Mat descriptors = _descriptors.getMat(); + descriptors = m_dense_descriptors; + + release_auxiliary(); +} + +// full scope +void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) +{ + // do nothing if no image + if( _image.getMat().empty() ) + return; + + CV_Assert( m_h_matrix.empty() ); + CV_Assert( ! m_use_orientation ); + + set_image( _image ); + + // whole image + m_roi = Rect( 0, 0, m_image.cols, m_image.rows ); + + set_parameters(); + initialize_single_descriptor_mode(); + + // compute full desc + compute_descriptors(); + normalize_descriptors(); + + Mat descriptors = _descriptors.getMat(); + descriptors = m_dense_descriptors; + + release_auxiliary(); +} + +// constructor +DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, + int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) + : m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), + m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) +{ + + m_image = 0; + + m_descriptor_size = 0; + m_grid_point_number = 0; + + m_grid_points.release(); + m_dense_descriptors.release(); + m_smoothed_gradient_layers.release(); + m_oriented_grid_points.release(); + + m_scale_invariant = false; + m_rotation_invariant = false; + + m_scale_map.release(); + m_orientation_map.release(); + m_orientation_resolution = 36; + + m_cube_sigmas.release(); + + m_cube_size = 0; + m_layer_size = 0; + + m_descriptor_normalization_threshold = 0.154f; // sift magical number + + m_h_matrix = _H.getMat(); +} + +// destructor +DAISY_Impl::~DAISY_Impl() +{ + m_scale_map.release(); + m_grid_points.release(); + m_orientation_map.release(); + m_oriented_grid_points.release(); + m_smoothed_gradient_layers.release(); +} + +Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, + int norm, InputArray H, bool interpolation, bool use_orientation) +{ + return makePtr(radius, q_radius, q_theta, q_hist, norm, H, interpolation, use_orientation); +} + + +} // END NAMESPACE XFEATURES2D +} // END NAMESPACE CV diff --git a/modules/xfeatures2d/test/test_features2d.cpp b/modules/xfeatures2d/test/test_features2d.cpp index d5f001ce1..f1007c451 100644 --- a/modules/xfeatures2d/test/test_features2d.cpp +++ b/modules/xfeatures2d/test/test_features2d.cpp @@ -1010,6 +1010,13 @@ TEST( Features2d_DescriptorExtractor_SURF, regression ) test.safe_run(); } +TEST( Features2d_DescriptorExtractor_DAISY, regression ) +{ + CV_DescriptorExtractorTest > test( "descriptor-daisy", 0.05f, + DAISY::create() ); + test.safe_run(); +} + TEST( Features2d_DescriptorExtractor_FREAK, regression ) { // TODO adjust the parameters below diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 46d328205..16d6656a8 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -651,6 +651,15 @@ TEST(Features2d_RotationInvariance_Descriptor_SIFT, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) +{ + DescriptorRotationInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.79f); + test.safe_run(); +} + /* * Detector's scale invariance check */ @@ -708,3 +717,12 @@ TEST(Features2d_RotationInvariance2_Detector_SURF, regression) ASSERT_LT( fabs(keypoints[1].response - keypoints[3].response), 1e-6); ASSERT_LT( fabs(keypoints[1].response - keypoints[4].response), 1e-6); } + +TEST(Features2d_ScaleInvariance_Descriptor_DAISY, regression) +{ + DescriptorScaleInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.075f); + test.safe_run(); +} From dec629a69fcffc813fa33e57a348bd90cebc20a6 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 18 May 2015 15:08:54 +0300 Subject: [PATCH 09/14] Fix docs warns. --- modules/xfeatures2d/include/opencv2/xfeatures2d.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 744e78f98..53329c352 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -207,7 +207,6 @@ public: * @param orientation orientation on image (0->360) * @param H homography matrix for warped grid * @param descriptor supplied array for descriptor storage - * @param get_descriptor true if descriptor was computed */ virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; @@ -225,7 +224,6 @@ public: * @param orientation orientation on image (0->360) * @param H homography matrix for warped grid * @param descriptor supplied array for descriptor storage - * @param get_unnormalized_descriptor true if descriptor was computed */ virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; From 1a9eee393aa588fdf192c440274fc047cff8ca8f Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Mon, 18 May 2015 21:05:19 +0300 Subject: [PATCH 10/14] Fixx win64 compilation. --- modules/xfeatures2d/src/daisy.cpp | 70 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 7d6c7fd4c..5bb3e2be8 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -497,8 +497,8 @@ static Mat layered_gradient( Mat data, int layer_no = 8 ) Mat cvO; // smooth the data matrix - filter2D( data, cvO, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( data, cvO, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); Mat dx(1, data_size, CV_32F); Mat dy(1, data_size, CV_32F); @@ -540,8 +540,8 @@ static void layered_gradient( Mat data, int layer_no, Mat layers ) gaussian_1d(kernel, 5, 0.5f, 0.0f); Mat Kernel(1, 5, CV_32F, (float*) kernel); - filter2D( cvI, cvI, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); Mat dx(1, data_size, CV_32F); Mat dy(1, data_size, CV_32F); @@ -654,8 +654,8 @@ inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_numbe float *layer = NULL; int kernel_size = filter_size( sigma ); - float kernel[kernel_size]; - gaussian_1d( kernel, kernel_size, sigma, 0 ); + std::vector kernel(kernel_size); + gaussian_1d( &kernel[0], kernel_size, sigma, 0 ); float* ptr = layers.ptr(0); @@ -669,9 +669,9 @@ inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_numbe Mat cvI( h, w, CV_32FC1, (float*) layer ); - Mat Kernel( 1, kernel_size, CV_32FC1, (float*) kernel ); - filter2D( cvI, cvI, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); + filter2D( cvI, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); } } @@ -900,8 +900,8 @@ inline void DAISY_Impl::compute_smoothed_gradient_layers() - m_cube_sigmas.at(r-1) * m_cube_sigmas.at(r-1) ); int kernel_size = filter_size( sigma ); - float kernel[kernel_size]; - gaussian_1d(kernel, kernel_size, (float)sigma, 0); + std::vector kernel(kernel_size); + gaussian_1d(&kernel[0], kernel_size, (float)sigma, 0); #if defined _OPENMP #pragma omp parallel for @@ -911,9 +911,9 @@ inline void DAISY_Impl::compute_smoothed_gradient_layers() Mat cvI( m_image.rows, m_image.cols, CV_32FC1, (float*) prev_cube + th*m_layer_size ); Mat cvO( m_image.rows, m_image.cols, CV_32FC1, (float*) cube + th*m_layer_size ); - Mat Kernel( 1, kernel_size, CV_32FC1, (float*) kernel ); - filter2D( cvI, cvO, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); + filter2D( cvI, cvO, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); } prev_cube = cube; } @@ -1001,15 +1001,15 @@ inline void DAISY_Impl::compute_scales() if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 - float kernel[kernel_size]; - gaussian_1d( kernel, kernel_size, sigma, 0 ); - Mat Kernel( 1, kernel_size, CV_32F, (float*) kernel ); + std::vector kernel(kernel_size); + gaussian_1d( &kernel[0], kernel_size, sigma, 0 ); + Mat Kernel( 1, kernel_size, CV_32F, &kernel[0] ); Mat sim, next_sim; // output gaussian image - filter2D( m_image, sim, CV_32F, Kernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( sim, sim, CV_32F, Kernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( m_image, sim, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( sim, sim, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); Mat max_dog( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); m_scale_map = Mat( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); @@ -1032,13 +1032,13 @@ inline void DAISY_Impl::compute_scales() if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd if( kernel_size < 3 ) kernel_size= 3; // kernel size cannot be smaller than 3 - float next_kernel[kernel_size]; - gaussian_1d( next_kernel, kernel_size, sigma_inc, 0 ); - Mat NextKernel( 1, kernel_size, CV_32F, (float*) next_kernel ); + kernel.resize(kernel_size); + gaussian_1d( &kernel[0], kernel_size, sigma_inc, 0 ); + Mat NextKernel( 1, kernel_size, CV_32F, &kernel[0] ); // output gaussian image - filter2D( sim, next_sim, CV_32F, NextKernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( next_sim, next_sim, CV_32F, NextKernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( sim, next_sim, CV_32F, NextKernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( next_sim, next_sim, CV_32F, NextKernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); #if defined _OPENMP @@ -1066,13 +1066,13 @@ inline void DAISY_Impl::compute_scales() if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 - float filter_kernel[kernel_size]; - gaussian_1d( filter_kernel, kernel_size, 10.0f, 0 ); - Mat FilterKernel( 1, kernel_size, CV_32F, (float*) filter_kernel ); + kernel.resize(kernel_size); + gaussian_1d( &kernel[0], kernel_size, 10.0f, 0 ); + Mat FilterKernel( 1, kernel_size, CV_32F, &kernel[0] ); // output gaussian image - filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel, Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); - filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel.t(), Point( -1.0f, -1.0f ), 0, BORDER_REPLICATE ); + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); #if defined _OPENMP #pragma omp parallel for @@ -1174,7 +1174,7 @@ inline void DAISY_Impl::compute_orientations() { angle = 0; } - m_orientation_map.at(y,x) = iangle; + m_orientation_map.at(y,x) = (float)iangle; } hist.release(); } @@ -1333,7 +1333,7 @@ inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, f yy = y + grid.at(2*region ); xx = x + grid.at(2*region + 1); - if ( ! Point( xx, yy ).inside( + if ( ! Point2f( (float)xx, (float)yy ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) continue; @@ -1384,7 +1384,7 @@ inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, iy = (int)yy; if( yy - iy > 0.5 ) iy++; ix = (int)xx; if( xx - ix > 0.5 ) ix++; - if ( ! Point( xx, yy ).inside( + if ( ! Point2f( (float)xx, (float)yy ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) continue; @@ -1425,7 +1425,7 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d double hy, hx, ry, rx; point_transform_via_homography( H, x, y, hx, hy ); - if ( ! Point( hx, hy ).inside( + if ( ! Point2f( (float)hx, (float)hy ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) return false; @@ -1460,7 +1460,7 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d hradius[r] = quantize_radius( (float) radius ); } - if ( ! Point( hx, hy ).inside( + if ( ! Point2f( (float)hx, (float)hy ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) continue; @@ -1489,7 +1489,7 @@ inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, point_transform_via_homography(H, x, y, hx, hy ); - if ( ! Point( hx, hy ).inside( + if ( ! Point2f( (float)hx, (float)hy ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) return false; From 309274a4ae6ceb1a4b727e4b5afacf6af0445aa4 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Sun, 24 May 2015 13:19:23 +0300 Subject: [PATCH 11/14] Fix doc in header. --- .../include/opencv2/xfeatures2d.hpp | 2 - modules/xfeatures2d/src/daisy.cpp | 254 +++++++----------- .../test_rotation_and_scale_invariance.cpp | 9 + 3 files changed, 101 insertions(+), 164 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 4eba4af6e..739b443eb 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -178,8 +178,6 @@ public: @param q_radius amount of radial range division quantity @param q_theta amount of angular range division quantity @param q_hist amount of gradient orientations range division quantity -DAISY::ONLY_KEYS means to compute descriptors only for keypoints in the list (default) and -DAISY::COMP_FULL will compute descriptors for all pixels in the given image @param norm choose descriptors normalization type, where DAISY::NRM_NONE will not do any normalization (default), DAISY::NRM_PARTIAL mean that histograms are normalized independently for L2 norm equal to 1.0, diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 5bb3e2be8..573ca4ff3 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -50,7 +50,6 @@ #include "precomp.hpp" - #include #include @@ -371,21 +370,21 @@ private: inline void get_descriptor( int y, int x, float* &descriptor ); // does not use interpolation while computing the histogram. - inline void ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const; + inline void ni_get_histogram( float* histogram, int y, int x, int shift, const float* hcube ) const; // returns the interpolated histogram: picks either bi_get_histogram or // ti_get_histogram depending on 'shift' - inline void i_get_histogram( float* histogram, double y, double x, double shift, float* cube ) const; + inline void i_get_histogram( float* histogram, double y, double x, double shift, const float* cube ) const; // records the histogram that is computed by bilinear interpolation // regarding the shift in the spatial coordinates. hcube is the // histogram cube for a constant smoothness level. - inline void bi_get_histogram( float* descriptor, double y, double x, int shift, float* hcube ) const; + inline void bi_get_histogram( float* descriptor, double y, double x, int shift, const float* hcube ) const; // records the histogram that is computed by trilinear interpolation // regarding the shift in layers and spatial coordinates. hcube is the // histogram cube for a constant smoothness level. - inline void ti_get_histogram( float* descriptor, double y, double x, double shift, float* hcube ) const; + inline void ti_get_histogram( float* descriptor, double y, double x, double shift, const float* hcube ) const; // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ) const; @@ -401,8 +400,6 @@ private: inline int quantize_radius( float rad ) const; - inline int filter_size( double sigma ); - // Return a number in the range [-0.5, 0.5] that represents the location of // the peak of a parabola passing through the 3 evenly spaced samples. The // center value is assumed to be greater than or equal to the other values @@ -486,66 +483,30 @@ static void gradient( Mat im, int h, int w, Mat dy, Mat dx ) } } -static Mat layered_gradient( Mat data, int layer_no = 8 ) -{ - int data_size = data.rows * data.cols; - Mat layers( 1, layer_no*data_size, CV_32F, Scalar(0) ); - - float kernel[5]; - gaussian_1d(kernel, 5, 0.5f, 0.0f); - Mat Kernel(1, 5, CV_32F, (float*) kernel); - - Mat cvO; - // smooth the data matrix - filter2D( data, cvO, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); - - Mat dx(1, data_size, CV_32F); - Mat dy(1, data_size, CV_32F); - - gradient(cvO, data.rows, data.cols, dy, dx); - cvO.release(); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int l=0; l(0) + l*data_size; - - for( int index=0; index(index) + zin * dy.at(index); - if( value > 0 ) layer_l[index] = value; - else layer_l[index] = 0; - } - } - return layers; -} - // data is not destroyed afterwards -static void layered_gradient( Mat data, int layer_no, Mat layers ) +static void layered_gradient( Mat data, Mat& layers ) { - CV_Assert( !layers.empty() ); + CV_Assert( !layers.empty() ); + CV_Assert( layers.dims == 4 ); + CV_Assert( data.rows == layers.size[2] ); + CV_Assert( data.cols == layers.size[3] ); - Mat cvI = data.clone(); - layers.setTo( Scalar(0) ); - int data_size = data.rows * data.cols; + int layer_no = layers.size[1]; - float kernel[5]; - gaussian_1d(kernel, 5, 0.5f, 0.0f); - Mat Kernel(1, 5, CV_32F, (float*) kernel); + Mat cvI; + layers.setTo( Scalar(0) ); + int data_size = data.rows * data.cols; - filter2D( cvI, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + float kernel[5]; + gaussian_1d(kernel, 5, 0.5f, 0.0f); + Mat Kernel(1, 5, CV_32F, (float*) kernel); - Mat dx(1, data_size, CV_32F); - Mat dy(1, data_size, CV_32F); - gradient( cvI, data.rows, data.cols, dy, dx ); + filter2D( data, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); + filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + + Mat dx(1, data_size, CV_32F); + Mat dy(1, data_size, CV_32F); + gradient( cvI, data.rows, data.cols, dy, dx ); #if defined _OPENMP #pragma omp parallel for @@ -556,13 +517,13 @@ static void layered_gradient( Mat data, int layer_no, Mat layers ) float kos = (float) cos( angle ); float zin = (float) sin( angle ); - float* layer_l = layers.ptr(0) + l*data_size; + float* layer_l = layers.ptr(0, l); - for( int index=0; index(index) + zin * dy.at(index); - if( value > 0 ) layer_l[index] = value; - else layer_l[index] = 0; + float value = kos * dx.at(i) + zin * dy.at(i); + if( value > 0 ) layer_l[i] = value; + else layer_l[i] = 0; } } } @@ -577,6 +538,19 @@ static void point_transform_via_homography( double* H, double x, double y, doubl v = kyp / kp; } +static int filter_size( double sigma ) +{ + int fsz = (int)(5*sigma); + + // kernel size must be odd + if( fsz%2 == 0 ) fsz++; + + // kernel size cannot be smaller than 3 + if( fsz < 3 ) fsz = 3; + + return fsz; +} + inline void DAISY_Impl::compute_grid_points() { double r_step = m_rad / (double)m_rad_q_no; @@ -651,28 +625,23 @@ inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_numbe { int i; - float *layer = NULL; int kernel_size = filter_size( sigma ); std::vector kernel(kernel_size); gaussian_1d( &kernel[0], kernel_size, sigma, 0 ); - float* ptr = layers.ptr(0); - #if defined _OPENMP #pragma omp parallel for private(i, layer) #endif for( i=0; i(0, i) ); Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); filter2D( cvI, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); - } } @@ -771,13 +740,15 @@ inline void DAISY_Impl::initialize() m_cube_size = m_layer_size * m_hist_th_q_no; } - m_smoothed_gradient_layers = Mat( g_cube_number + 1, m_cube_size, CV_32F); + // 4 dims matrix (idcube, idhist, img_y, img_x); + int dims[4] = { g_cube_number + 1, m_hist_th_q_no, m_image.rows , m_image.cols }; + m_smoothed_gradient_layers = Mat( 4, dims, CV_32F ); - layered_gradient( m_image, m_hist_th_q_no, m_smoothed_gradient_layers ); + layered_gradient( m_image, m_smoothed_gradient_layers ); // assuming a 0.5 image smoothness, we pull this to 1.6 as in sift smooth_layers( m_smoothed_gradient_layers, m_image.rows, m_image.cols, - m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25) ); + m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25f) ); } @@ -791,10 +762,10 @@ inline void DAISY_Impl::compute_cube_sigmas() m_cube_sigmas = Mat(1, g_cube_number, CV_64F); - double r_step = double(m_rad)/m_rad_q_no; - for( int r=0; r< m_rad_q_no; r++ ) + double r_step = (double)m_rad / m_rad_q_no / 2; + for( int r=0; r(r) = (r+1) * r_step/2; + m_cube_sigmas.at(r) = (r+1) * r_step; } } update_selected_cubes(); @@ -802,10 +773,11 @@ inline void DAISY_Impl::compute_cube_sigmas() inline void DAISY_Impl::update_selected_cubes() { + double scale = m_rad/m_rad_q_no/2.0; for( int r=0; r= m_cube_sigmas.at(g_cube_number-1) ) return g_cube_number-1; - float dist; - float mindist=FLT_MAX; - int mini=0; - for( int c=0; c(c)-rad ); - if( dist < mindist ) { - mindist = dist; - mini=c; - } - } - return mini; + int idx_min[2]; + minMaxIdx( abs( m_cube_sigmas - rad ), NULL, NULL, idx_min ); + + return idx_min[1]; } inline void DAISY_Impl::compute_histograms() @@ -836,21 +801,20 @@ inline void DAISY_Impl::compute_histograms() for( r=0; r(0) + r * m_cube_size; - float* src = m_smoothed_gradient_layers.ptr(0) + (r+1)* m_cube_size; + float* dst = m_smoothed_gradient_layers.ptr(r ); + float* src = m_smoothed_gradient_layers.ptr(r+1); #if defined _OPENMP #pragma omp parallel for private(y,x,ind,hist) #endif for( y=0; y(0) + r*m_cube_size; + float* dst = m_smoothed_gradient_layers.ptr(r); #if defined _OPENMP #pragma omp parallel for @@ -883,14 +847,9 @@ inline void DAISY_Impl::normalize_histograms() inline void DAISY_Impl::compute_smoothed_gradient_layers() { - - float* prev_cube = m_smoothed_gradient_layers.ptr(0); - float* cube = NULL; - double sigma; for( int r=0; r(0) + (r+1)*m_cube_size; // incremental smoothing if( r == 0 ) @@ -903,19 +862,19 @@ inline void DAISY_Impl::compute_smoothed_gradient_layers() std::vector kernel(kernel_size); gaussian_1d(&kernel[0], kernel_size, (float)sigma, 0); + #if defined _OPENMP #pragma omp parallel for #endif for( int th=0; th(r , th) ); + Mat cvO( m_image.rows, m_image.cols, CV_32FC1, m_smoothed_gradient_layers.ptr(r+1, th) ); Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); filter2D( cvI, cvO, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); } - prev_cube = cube; } compute_histograms(); } @@ -975,18 +934,6 @@ inline float DAISY_Impl::interpolate_peak(float left, float center, float right) else return (float) (0.5*(left -right)/den); } -inline int DAISY_Impl::filter_size( double sigma ) -{ - int fsz = (int)(5*sigma); - - // kernel size must be odd - if( fsz%2 == 0 ) fsz++; - - // kernel size cannot be smaller than 3 - if( fsz < 3 ) fsz = 3; - - return fsz; -} inline void DAISY_Impl::compute_scales() { @@ -1096,13 +1043,14 @@ inline void DAISY_Impl::compute_orientations() CV_Assert( !m_image.empty() ); - int data_size = m_image.cols * m_image.rows; - Mat rotation_layers = layered_gradient( m_image, m_orientation_resolution ); + //int data_size = m_image.cols * m_image.rows; + int dims[4] = { 1, m_orientation_resolution, m_image.rows, m_image.cols }; + Mat rotation_layers(4, dims, CV_32F); + layered_gradient( m_image, rotation_layers ); m_orientation_map = Mat(m_image.cols, m_image.rows, CV_16U, Scalar(0)); int ori, max_ind; - int ind; float max_val; int next, prev; @@ -1119,7 +1067,7 @@ inline void DAISY_Impl::compute_orientations() for( int scale=0; scale(y,x) != scale ) continue; - for( ori=0; ori(ori) = rotation_layers.at(ori*data_size+ind); - } + //hist.at(ori) = rotation_layers.ptr(0, ori, y, x); + //hist.at(ori) = rotation_layers.at(ori*data_size+ind); for( kk=0; kk<6; kk++ ) smooth_histogram( hist, m_orientation_resolution ); @@ -1194,7 +1140,7 @@ inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* hi for( int h=0; h pixel positions // B D - float* A = hcube+ind*m_hist_th_q_no; - float* B = A+m_image.cols*m_hist_th_q_no; - float* C = A+m_hist_th_q_no; - float* D = A+(m_image.cols+1)*m_hist_th_q_no; + const float* A = hcube + ind*m_hist_th_q_no; + const float* B = A + m_image.cols*m_hist_th_q_no; + const float* C = A + m_hist_th_q_no; + const float* D = A + (m_image.cols+1)*m_hist_th_q_no; double alpha = mnx+1-x; double beta = mny+1-y; @@ -1250,7 +1196,7 @@ inline void DAISY_Impl::bi_get_histogram( float* histogram, double y, double x, } } -inline void DAISY_Impl::ti_get_histogram( float* histogram, double y, double x, double shift, float* hcube ) const +inline void DAISY_Impl::ti_get_histogram( float* histogram, double y, double x, double shift, const float* hcube ) const { int ishift = int( shift ); double layer_alpha = shift - ishift; @@ -1263,13 +1209,13 @@ inline void DAISY_Impl::ti_get_histogram( float* histogram, double y, double x, histogram[m_hist_th_q_no-1] = (float) ((1-layer_alpha)*thist[m_hist_th_q_no-1]+layer_alpha*thist[0]); } -inline void DAISY_Impl::ni_get_histogram( float* histogram, int y, int x, int shift, float* hcube ) const +inline void DAISY_Impl::ni_get_histogram( float* histogram, int y, int x, int shift, const float* hcube ) const { if ( ! Point( x, y ).inside( Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) return; - float* hptr = hcube + (y*m_image.cols+x)*m_hist_th_q_no; + const float* hptr = hcube + (y*m_image.cols+x)*m_hist_th_q_no; for( int h=0; h(0); - i_get_histogram( descriptor, y, x, shift, ptr + g_selected_cubes[0]*m_cube_size ); + i_get_histogram( descriptor, y, x, shift, m_smoothed_gradient_layers.ptr( g_selected_cubes[0] ) ); int r, rdt, region; double yy, xx; @@ -1337,8 +1282,8 @@ inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, f Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) ) continue; - histogram = descriptor+region*m_hist_th_q_no; - i_get_histogram( histogram, yy, xx, shift, ptr + g_selected_cubes[r]*m_cube_size ); + histogram = descriptor + region*m_hist_th_q_no; + i_get_histogram( histogram, yy, xx, shift, (float *) m_smoothed_gradient_layers.ptr( r ) ); } } } @@ -1366,8 +1311,7 @@ inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, int ix = (int)x; if( x - ix > 0.5 ) ix++; // center - float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); - ni_get_histogram( descriptor, iy, ix, ishift, ptr + g_selected_cubes[0]*m_cube_size ); + ni_get_histogram( descriptor, iy, ix, ishift, m_smoothed_gradient_layers.ptr(g_selected_cubes[0]) ); double yy, xx; float* histogram=0; @@ -1389,7 +1333,7 @@ inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, ) continue; histogram = descriptor+region*m_hist_th_q_no; - ni_get_histogram( histogram, iy, ix, ishift, ptr + g_selected_cubes[r]*m_cube_size ); + ni_get_histogram( histogram, iy, ix, ishift, m_smoothed_gradient_layers.ptr(g_selected_cubes[r]) ); } } } @@ -1435,8 +1379,7 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d hradius[0] = quantize_radius( (float) radius ); double shift = m_orientation_shift_table[orientation]; - float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); - i_get_histogram( descriptor, hy, hx, shift, ptr + hradius[0]*m_cube_size ); + i_get_histogram( descriptor, hy, hx, shift, m_smoothed_gradient_layers.ptr(hradius[0]) ); double gy, gx; int r, rdt, th, region; @@ -1465,7 +1408,7 @@ inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, d ) continue; histogram = descriptor+region*m_hist_th_q_no; - i_get_histogram( histogram, hy, hx, shift, ptr + hradius[r]*m_cube_size ); + i_get_histogram( histogram, hy, hx, shift, m_smoothed_gradient_layers.ptr(hradius[r]) ); } } return true; @@ -1507,8 +1450,7 @@ inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, int r, rdt, th, region; double gy, gx; float* histogram=0; - float *ptr = (float *) m_smoothed_gradient_layers.ptr(0); - ni_get_histogram( descriptor, ihy, ihx, ishift, ptr + hradius[0]*m_cube_size ); + ni_get_histogram( descriptor, ihy, ihx, ishift, m_smoothed_gradient_layers.ptr(hradius[0]) ); for( r=0; r(hradius[r]) ); } } return true; @@ -1706,25 +1648,13 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) { - m_image = 0; - m_descriptor_size = 0; m_grid_point_number = 0; - m_grid_points.release(); - m_dense_descriptors.release(); - m_smoothed_gradient_layers.release(); - m_oriented_grid_points.release(); - m_scale_invariant = false; m_rotation_invariant = false; - - m_scale_map.release(); - m_orientation_map.release(); m_orientation_resolution = 36; - m_cube_sigmas.release(); - m_cube_size = 0; m_layer_size = 0; diff --git a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp index 06213cea1..b51c84d96 100644 --- a/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/xfeatures2d/test/test_rotation_and_scale_invariance.cpp @@ -660,6 +660,15 @@ TEST(Features2d_RotationInvariance_Descriptor_LATCH, regression) test.safe_run(); } +TEST(Features2d_RotationInvariance_Descriptor_DAISY, regression) +{ + DescriptorRotationInvarianceTest test(BRISK::create(), + DAISY::create(15, 3, 8, 8, DAISY::NRM_NONE, noArray(), true, true), + NORM_L1, + 0.79f); + test.safe_run(); +} + /* * Detector's scale invariance check From a324c6c658e69f525cd3b668604f743ffc795fde Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Thu, 28 May 2015 23:40:18 +0300 Subject: [PATCH 12/14] Refactor DAISY for more opencv style. --- .../include/opencv2/xfeatures2d.hpp | 24 +- modules/xfeatures2d/src/daisy.cpp | 1722 ++++++++--------- 2 files changed, 831 insertions(+), 915 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 739b443eb..354d6146b 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -222,36 +222,36 @@ public: /** * @param y position y on image * @param x position x on image - * @param orientation orientation on image (0->360) + * @param ori orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ - virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; + virtual void GetDescriptor( double y, double x, int orientation, float* descriptor ) const = 0; /** * @param y position y on image * @param x position x on image * @param orientation orientation on image (0->360) + * @param descriptor supplied array for descriptor storage * @param H homography matrix for warped grid + */ + virtual bool GetDescriptor( double y, double x, int orientation, float* descriptor, double* H ) const = 0; + + /** + * @param y position y on image + * @param x position x on image + * @param ori orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ - virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + virtual void GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor ) const = 0; /** * @param y position y on image * @param x position x on image * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage - */ - virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const = 0; - - /** - * @param y position y on image - * @param x position x on image - * @param orientation orientation on image (0->360) * @param H homography matrix for warped grid - * @param descriptor supplied array for descriptor storage */ - virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const = 0; + virtual bool GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor , double *H ) const = 0; }; diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index 573ca4ff3..b21399fbe 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -72,7 +72,6 @@ const static int g_grid_orientation_resolution = 360; static const int MAX_CUBE_NO = 64; static const int MAX_NORMALIZATION_ITER = 5; -int g_cube_number; int g_selected_cubes[MAX_CUBE_NO]; // m_rad_q_no < MAX_CUBE_NO /* @@ -136,17 +135,16 @@ public: * @param ori orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ - virtual void get_descriptor( double y, double x, int orientation, float* descriptor ) const; + virtual void GetDescriptor( double y, double x, int orientation, float* descriptor ) const; /** * @param y position y on image * @param x position x on image * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage * @param H homography matrix for warped grid - * @param descriptor supplied array for descriptor storage - * @param get_descriptor true if descriptor was computed */ - virtual bool get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; + virtual bool GetDescriptor( double y, double x, int orientation, float* descriptor, double* H ) const; /** * @param y position y on image @@ -154,18 +152,16 @@ public: * @param ori orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ - virtual void get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const; + virtual void GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor ) const; /** * @param y position y on image * @param x position x on image * @param ori orientation on image (0->360) + * @param descriptor supplied array for descriptor storage * @param H homography matrix for warped grid - * @param descriptor supplied array for descriptor storage - * @param get_unnormalized_descriptor true if descriptor was computed */ - virtual bool get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; - + virtual bool GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor, double* H ) const; protected: @@ -189,25 +185,15 @@ protected: // default. change the value using set_normalization() function. int m_nrm_type; - // number of bins in the histograms while computing orientation - int m_orientation_resolution; + // the size of the descriptor vector + int m_descriptor_size; // the number of grid locations int m_grid_point_number; - // the size of the descriptor vector - int m_descriptor_size; + // number of bins in the histograms while computing orientation + int m_orientation_resolution; - // size of m_hsz layers at a single sigma: m_hsz * m_layer_size - int m_cube_size; - - // size of the layer : - // m_roi.width*m_roi.height - int m_layer_size; - - // the clipping threshold to use in normalization: values above this value - // are clipped to this value for normalize_sift_way() function - float m_descriptor_normalization_threshold; /* * DAISY switches @@ -221,7 +207,7 @@ protected: // if enabled, descriptors are computed with casting non-integer locations // to integer positions otherwise we use interpolation. - bool m_disable_interpolation; + bool m_enable_interpolation; // switch to enable sample by keypoints orientation bool m_use_orientation; @@ -233,20 +219,16 @@ protected: // holds optional H matrix Mat m_h_matrix; - // input image. + // internal float image. Mat m_image; // image roi Rect m_roi; - // stores the descriptors : - // its size is [ m_roi.width*m_roi.height*m_descriptor_size ]. - Mat m_dense_descriptors; - // stores the layered gradients in successively smoothed form : // layer[n] = m_gradient_layers * gaussian( sigma_n ); // n>= 1; layer[0] is the layered_gradient - Mat m_smoothed_gradient_layers; + std::vector m_smoothed_gradient_layers; // hold the scales of the pixels Mat m_scale_map; @@ -270,11 +252,6 @@ protected: private: - // two possible computational mode - // ONLY_KEYS -> (mode_1) compute descriptors on demand - // COMP_FULL -> (mode_2) compute all descriptors from image - enum { ONLY_KEYS = 0, COMP_FULL = 1 }; - /* * DAISY functions */ @@ -300,7 +277,7 @@ private: inline void release_auxiliary(); // computes the descriptors for every pixel in the image. - inline void compute_descriptors(); + inline void compute_descriptors( Mat* m_dense_descriptors ); // computes scales for every pixel and scales the structure grid so that the // resulting descriptors are scale invariant. you must set @@ -334,79 +311,11 @@ private: // histograms are going to be computed according to the given parameters. inline void compute_oriented_grid_points(); - // normalizes the descriptor - inline void normalize_descriptor( float* desc, int nrm_type ) const; - // applies one of the normalizations (partial,full,sift) to the desciptors. - inline void normalize_descriptors( int nrm_type = DAISY::NRM_NONE ); - - - // emulates the way sift is normalized. - inline void normalize_sift_way( float* desc ) const; - - // normalizes the descriptor histogram by histogram - inline void normalize_partial( float* desc ) const; - - // normalizes the full descriptor. - inline void normalize_full( float* desc ) const; - - // normalizes histograms individually - inline void normalize_histograms(); + inline void normalize_descriptors( Mat* m_dense_descriptors ); inline void update_selected_cubes(); - // Smooth a histogram by using a [1/3 1/3 1/3] kernel. Assume the histogram - // is connected in a circular buffer. - inline void smooth_histogram( Mat hist, int bins ); - - // smooths each of the layers by a Gaussian having "sigma" standart - // deviation. - inline void smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ); - - // returns the descriptor vector for the point (y, x) !!! use this for - // precomputed operations meaning that you must call compute_descriptors() - // before calling this function. if you want normalized descriptors, call - // normalize_descriptors() before calling compute_descriptors() - inline void get_descriptor( int y, int x, float* &descriptor ); - - // does not use interpolation while computing the histogram. - inline void ni_get_histogram( float* histogram, int y, int x, int shift, const float* hcube ) const; - - // returns the interpolated histogram: picks either bi_get_histogram or - // ti_get_histogram depending on 'shift' - inline void i_get_histogram( float* histogram, double y, double x, double shift, const float* cube ) const; - - // records the histogram that is computed by bilinear interpolation - // regarding the shift in the spatial coordinates. hcube is the - // histogram cube for a constant smoothness level. - inline void bi_get_histogram( float* descriptor, double y, double x, int shift, const float* hcube ) const; - - // records the histogram that is computed by trilinear interpolation - // regarding the shift in layers and spatial coordinates. hcube is the - // histogram cube for a constant smoothness level. - inline void ti_get_histogram( float* descriptor, double y, double x, double shift, const float* hcube ) const; - - // uses interpolation, for no interpolation call ni_get_descriptor. see also get_descriptor - inline void i_get_descriptor( double y, double x, int orientation, float* descriptor ) const; - - // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor - inline void ni_get_descriptor( double y, double x, int orientation, float* descriptor ) const; - - // uses interpolation for no interpolation call ni_get_descriptor. see also get_descriptor - inline bool i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; - - // does not use interpolation. for w/interpolation, call i_get_descriptor. see also get_descriptor - inline bool ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const; - - inline int quantize_radius( float rad ) const; - - // Return a number in the range [-0.5, 0.5] that represents the location of - // the peak of a parabola passing through the 3 evenly spaced samples. The - // center value is assumed to be greater than or equal to the other values - // if positive, or less than if negative. - inline float interpolate_peak( float left, float center, float right ); - - }; // END DAISY_Impl CLASS @@ -417,144 +326,629 @@ inline void DAISY_Impl::reset() { m_image.release(); - m_orientation_map.release(); m_scale_map.release(); + m_orientation_map.release(); - m_dense_descriptors.release(); - m_smoothed_gradient_layers.release(); + for (size_t i=0; i0 && x(ind) = (im.at(ind+1)-im.at(ind-1)) / 2.0f; - if( x==0 ) dx.at(ind) = im.at(ind+1)-im.at(ind ); - if( x==w-1 ) dx.at(ind) = im.at(ind )-im.at(ind-1); - - // dy - if( y>0 && y(ind) = (im.at(ind+w)-im.at(ind-w)) / 2.0f; - if( y==0 ) dy.at(ind) = im.at(ind+w)-im.at(ind ); - if( y==h-1 ) dy.at(ind) = im.at(ind )-im.at(ind-w); - } - } -} - -// data is not destroyed afterwards -static void layered_gradient( Mat data, Mat& layers ) -{ - CV_Assert( !layers.empty() ); - CV_Assert( layers.dims == 4 ); - CV_Assert( data.rows == layers.size[2] ); - CV_Assert( data.cols == layers.size[3] ); - - int layer_no = layers.size[1]; - - Mat cvI; - layers.setTo( Scalar(0) ); - int data_size = data.rows * data.cols; - - float kernel[5]; - gaussian_1d(kernel, 5, 0.5f, 0.0f); - Mat Kernel(1, 5, CV_32F, (float*) kernel); - - filter2D( data, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); - - Mat dx(1, data_size, CV_32F); - Mat dy(1, data_size, CV_32F); - gradient( cvI, data.rows, data.cols, dy, dx ); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int l=0; l(0, l); - - for( int i=0; i(i) + zin * dy.at(i); - if( value > 0 ) layer_l[i] = value; - else layer_l[i] = 0; - } - } -} - -// transform a point via the homography -static void point_transform_via_homography( double* H, double x, double y, double &u, double &v ) -{ - double kxp = H[0]*x + H[1]*y + H[2]; - double kyp = H[3]*x + H[4]*y + H[5]; - double kp = H[6]*x + H[7]*y + H[8]; - u = kxp / kp; - v = kyp / kp; -} - -static int filter_size( double sigma ) -{ - int fsz = (int)(5*sigma); - + int fsz = (int)( factor * sigma ); // kernel size must be odd if( fsz%2 == 0 ) fsz++; - // kernel size cannot be smaller than 3 if( fsz < 3 ) fsz = 3; return fsz; } +// transform a point via the homography +static void pt_H( double* H, double x, double y, double &u, double &v ) +{ + double kxp = H[0]*x + H[1]*y + H[2]; + double kyp = H[3]*x + H[4]*y + H[5]; + double kp = H[6]*x + H[7]*y + H[8]; + u = kxp / kp; v = kyp / kp; +} + + +static float interpolate_peak( float left, float center, float right ) +{ + if( center < 0.0 ) + { + left = -left; + center = -center; + right = -right; + } + CV_Assert(center >= left && center >= right); + + float den = (float) (left - 2.0 * center + right); + + if( den == 0 ) return 0; + else return (float) (0.5*(left -right)/den); +} + +static void smooth_histogram( Mat* hist, int hsz ) +{ + int i; + float prev, temp; + + prev = hist->at(hsz - 1); + for (i = 0; i < hsz; i++) + { + temp = hist->at(i); + hist->at(i) = (prev + hist->at(i) + hist->at( (i + 1 == hsz) ? 0 : i + 1) ) / 3.0f; + prev = temp; + } +} + +struct LayeredGradientInvoker : ParallelLoopBody +{ + LayeredGradientInvoker( Mat* _layers, Mat& _dy, Mat& _dx ) + { + dy = _dy; + dx = _dx; + layers = _layers; + layer_no = layers->size[0]; + } + + void operator ()(const cv::Range& range) const + { + for (int l = range.start; l < range.end; ++l) + { + double angle = l * 2*CV_PI / layer_no; + Mat layer( dx.rows, dx.cols, CV_32F, layers->ptr(l,0,0) ); + addWeighted( dx, cos( angle ), dy, sin( angle ), 0.0f, layer, CV_32F ); + max( layer, 0.0f, layer ); + } + } + + Mat dy, dx; + Mat *layers; + int layer_no; +}; + +static void layered_gradient( Mat& data, Mat* layers ) +{ + Mat cvO, dx, dy; + int layer_no = layers->size[0]; + + GaussianBlur( data, cvO, Size(5, 5), 0.5f, 0.5f, BORDER_REPLICATE ); + Sobel( cvO, dx, CV_32F, 1, 0, 1, 0.5f, 0.0f, BORDER_REPLICATE ); + Sobel( cvO, dy, CV_32F, 0, 1, 1, 0.5f, 0.0f, BORDER_REPLICATE ); + + parallel_for_( Range(0, layer_no), LayeredGradientInvoker( layers, dy, dx ) ); +} + +struct SmoothLayersInvoker : ParallelLoopBody +{ + SmoothLayersInvoker( Mat* _layers, const float _sigma ) + { + layers = _layers; + sigma = _sigma; + + h = layers->size[1]; + w = layers->size[2]; + ks = filter_size( sigma, 5.0f ); + } + + void operator ()(const cv::Range& range) const + { + for (int l = range.start; l < range.end; ++l) + { + Mat layer( h, w, CV_32FC1, layers->ptr(l,0,0) ); + GaussianBlur( layer, layer, Size(ks, ks), sigma, sigma, BORDER_REPLICATE ); + } + } + + float sigma; + int ks, h, w; + Mat *layers; +}; + +static void smooth_layers( Mat* layers, float sigma ) +{ + int layer_no = layers->size[0]; + parallel_for_( Range(0, layer_no), SmoothLayersInvoker( layers, sigma ) ); +} + +static int quantize_radius( float rad, const int _rad_q_no, const Mat& _cube_sigmas ) +{ + if( rad <= _cube_sigmas.at(0) ) + return 0; + if( rad >= _cube_sigmas.at(_rad_q_no-1) ) + return _rad_q_no-1; + + int idx_min[2]; + minMaxIdx( abs( _cube_sigmas - rad ), NULL, NULL, idx_min ); + + return idx_min[1]; +} + +static void normalize_partial( float* desc, const int _grid_point_number, const int _hist_th_q_no ) +{ + float norm = 0.0f; + for( int h=0; h<_grid_point_number; h++ ) + { + // l2 norm + for( int i=0; i<_hist_th_q_no; i++ ) + { + norm += sqrt(desc[h*_hist_th_q_no + i] + * desc[h*_hist_th_q_no + i]); + } + if( norm != 0.0 ) + // divide with norm + for( int i=0; i<_hist_th_q_no; i++ ) + { + desc[h*_hist_th_q_no + i] /= norm; + } + } +} + +static void normalize_sift_way( float* desc, const int _descriptor_size ) +{ + int h; + int iter = 0; + bool changed = true; + while( changed && iter < MAX_NORMALIZATION_ITER ) + { + iter++; + changed = false; + + float norm = 0.0f; + for( int i=0; i<_descriptor_size; i++ ) + { + norm += sqrt(desc[_descriptor_size + i] + * desc[_descriptor_size + i]); + } + + if( norm > 1e-5 ) + // divide with norm + for( int i=0; i<_descriptor_size; i++ ) + { + desc[_descriptor_size + i] /= norm; + } + + for( h=0; h<_descriptor_size; h++ ) + { // sift magical number + if( desc[ h ] > 0.154f ) + { + desc[ h ] = 0.154f; + changed = true; + } + } + } +} + +static void normalize_full( float* desc, const int _descriptor_size ) +{ + // l2 norm + float norm = 0.0f; + for( int i=0; i<_descriptor_size; i++ ) + { + norm += sqrt(desc[_descriptor_size + i] + * desc[_descriptor_size + i]); + } + if( norm != 0.0 ) + // divide with norm + for( int i=0; i<_descriptor_size; i++ ) + { + desc[_descriptor_size + i] /= norm; + } +} + +static void normalize_descriptor( float* desc, const int nrm_type, const int _grid_point_number, + const int _hist_th_q_no, const int _descriptor_size ) +{ + if( nrm_type == DAISY::NRM_NONE ) return; + else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc,_grid_point_number,_hist_th_q_no); + else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc,_descriptor_size); + else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc,_descriptor_size); + else + CV_Error( Error::StsInternal, "No such normalization" ); +} + +static void ni_get_histogram( float* histogram, const int y, const int x, const int shift, const Mat* hcube ) +{ + + if ( ! Point( x, y ).inside( + Rect( 0, 0, hcube->size[2]-1, hcube->size[1]-1 ) ) + ) return; + + int _hist_th_q_no = hcube->size[0]; + const float* hptr = hcube->ptr(0,y*_hist_th_q_no,x*_hist_th_q_no); + for( int h=0; h<_hist_th_q_no; h++ ) + { + int hi = h+shift; + if( hi >= _hist_th_q_no ) hi -= _hist_th_q_no; + histogram[h] = hptr[hi]; + } +} + +static void bi_get_histogram( float* histogram, const double y, const double x, const int shift, const Mat* hcube ) +{ + int mnx = int( x ); + int mny = int( y ); + int _hist_th_q_no = hcube->size[0]; + if( mnx >= hcube->size[2]-2 || mny >= hcube->size[1]-2 ) + { + memset(histogram, 0, sizeof(float)*_hist_th_q_no); + return; + } + + // A C --> pixel positions + // B D + const float* A = hcube->ptr(0, mny *_hist_th_q_no, mnx *_hist_th_q_no); + const float* B = hcube->ptr(0, (mny+1)*_hist_th_q_no, mnx *_hist_th_q_no); + const float* C = hcube->ptr(0, mny *_hist_th_q_no, (mnx+1)*_hist_th_q_no); + const float* D = hcube->ptr(0, (mny+1)*_hist_th_q_no, (mnx+1)*_hist_th_q_no); + + double alpha = mnx+1-x; + double beta = mny+1-y; + + float w0 = (float) ( alpha * beta ); + float w1 = (float) ( beta - w0 ); // (1-alpha)*beta; + float w2 = (float) ( alpha - w0 ); // (1-beta)*alpha; + float w3 = (float) ( 1 + w0 - alpha - beta); // (1-beta)*(1-alpha); + + int h; + + for( h=0; h<_hist_th_q_no; h++ ) { + if( h+shift < _hist_th_q_no ) histogram[h] = w0 * A[h+shift]; + else histogram[h] = w0 * A[h+shift-_hist_th_q_no]; + } + for( h=0; h<_hist_th_q_no; h++ ) { + if( h+shift < _hist_th_q_no ) histogram[h] += w1 * C[h+shift]; + else histogram[h] += w1 * C[h+shift-_hist_th_q_no]; + } + for( h=0; h<_hist_th_q_no; h++ ) { + if( h+shift < _hist_th_q_no ) histogram[h] += w2 * B[h+shift]; + else histogram[h] += w2 * B[h+shift-_hist_th_q_no]; + } + for( h=0; h<_hist_th_q_no; h++ ) { + if( h+shift < _hist_th_q_no ) histogram[h] += w3 * D[h+shift]; + else histogram[h] += w3 * D[h+shift-_hist_th_q_no]; + } +} + +static void ti_get_histogram( float* histogram, const double y, const double x, const double shift, const Mat* hcube ) +{ + int ishift = int( shift ); + double layer_alpha = shift - ishift; + + float thist[MAX_CUBE_NO]; + bi_get_histogram( thist, y, x, ishift, hcube ); + + int _hist_th_q_no = hcube->size[0]; + for( int h=0; h<_hist_th_q_no-1; h++ ) + histogram[h] = (float) ((1-layer_alpha)*thist[h]+layer_alpha*thist[h+1]); + histogram[_hist_th_q_no-1] = (float) ((1-layer_alpha)*thist[_hist_th_q_no-1]+layer_alpha*thist[0]); +} + +static void i_get_histogram( float* histogram, const double y, const double x, const double shift, const Mat* hcube ) +{ + int ishift = (int)shift; + double fshift = shift-ishift; + if ( fshift < 0.01 ) bi_get_histogram( histogram, y, x, ishift , hcube ); + else if( fshift > 0.99 ) bi_get_histogram( histogram, y, x, ishift+1, hcube ); + else ti_get_histogram( histogram, y, x, shift , hcube ); +} + +static void ni_get_descriptor( const double y, const double x, const int orientation, float* descriptor, const std::vector* layers, + const Mat* _oriented_grid_points, const double* _orientation_shift_table, const int _th_q_no ) +{ + CV_Assert( y >= 0 && y < layers->at(0).size[1] ); + CV_Assert( x >= 0 && x < layers->at(0).size[2] ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !layers->empty() ); + CV_Assert( !_oriented_grid_points->empty() ); + CV_Assert( descriptor != NULL ); + + int _rad_q_no = (int) layers->size() - 1; + int _hist_th_q_no = layers->at(0).size[0]; + double shift = _orientation_shift_table[orientation]; + int ishift = (int)shift; + if( shift - ishift > 0.5 ) ishift++; + + int iy = (int)y; if( y - iy > 0.5 ) iy++; + int ix = (int)x; if( x - ix > 0.5 ) ix++; + + // center + ni_get_histogram( descriptor, iy, ix, ishift, &layers->at(g_selected_cubes[0]) ); + + double yy, xx; + float* histogram=0; + // petals of the flower + int r, rdt, region; + Mat grid = _oriented_grid_points->row( orientation ); + for( r=0; r<_rad_q_no; r++ ) + { + rdt = r*_th_q_no+1; + for( region=rdt; region(2*region ); + xx = x + grid.at(2*region+1); + iy = (int)yy; if( yy - iy > 0.5 ) iy++; + ix = (int)xx; if( xx - ix > 0.5 ) ix++; + + if ( ! Point2f( (float)xx, (float)yy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) continue; + + histogram = descriptor + region*_hist_th_q_no; + ni_get_histogram( histogram, iy, ix, ishift, &layers->at(g_selected_cubes[r]) ); + } + } +} + +static void i_get_descriptor( const double y, const double x, const int orientation, float* descriptor, const std::vector* layers, + const Mat* _oriented_grid_points, const double *_orientation_shift_table, const int _th_q_no ) +{ + CV_Assert( y >= 0 && y < layers->at(0).size[1] ); + CV_Assert( x >= 0 && x < layers->at(0).size[2] ); + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !layers->empty() ); + CV_Assert( !_oriented_grid_points->empty() ); + CV_Assert( descriptor != NULL ); + + int _rad_q_no = (int) layers->size() - 1; + int _hist_th_q_no = layers->at(0).size[0]; + double shift = _orientation_shift_table[orientation]; + + i_get_histogram( descriptor, y, x, shift, &layers->at(g_selected_cubes[0]) ); + + int r, rdt, region; + double yy, xx; + float* histogram = 0; + + Mat grid = _oriented_grid_points->row( orientation ); + + // petals of the flower + for( r=0; r<_rad_q_no; r++ ) + { + rdt = r*_th_q_no+1; + for( region=rdt; region(2*region ); + xx = x + grid.at(2*region + 1); + + if ( ! Point2f( (float)xx, (float)yy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) continue; + + histogram = descriptor + region*_hist_th_q_no; + i_get_histogram( histogram, yy, xx, shift, &layers->at(r) ); + } + } +} + +static bool ni_get_descriptor_h( const double y, const double x, const int orientation, double* H, float* descriptor, const std::vector* layers, + const Mat& _cube_sigmas, const Mat* _grid_points, const double* _orientation_shift_table, const int _th_q_no ) +{ + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !layers->empty() ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + + pt_H(H, x, y, hx, hy ); + + if ( ! Point2f( (float)hx, (float)hy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) return false; + + int _rad_q_no = (int) layers->size() - 1; + int _hist_th_q_no = layers->at(0).size[0]; + double shift = _orientation_shift_table[orientation]; + int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; + + pt_H(H, x+_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); + hradius[0] = quantize_radius( (float) radius, _rad_q_no, _cube_sigmas ); + + int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + int r, rdt, th, region; + double gy, gx; + float* histogram=0; + ni_get_histogram( descriptor, ihy, ihx, ishift, &layers->at(hradius[0]) ); + for( r=0; r<_rad_q_no; r++) + { + rdt = r*_th_q_no + 1; + for( th=0; th<_th_q_no; th++ ) + { + region = rdt + th; + + gy = y + _grid_points->at(region,0); + gx = x + _grid_points->at(region,1); + + pt_H(H, gx, gy, hx, hy); + if( th == 0 ) + { + pt_H(H, gx+_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1*d1 ); + hradius[r] = quantize_radius( (float) radius, _rad_q_no, _cube_sigmas ); + } + + ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; + ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; + + if ( ! Point( ihx, ihy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) continue; + + histogram = descriptor + region*_hist_th_q_no; + ni_get_histogram( histogram, ihy, ihx, ishift, &layers->at(hradius[r]) ); + } + } + return true; +} + +static bool i_get_descriptor_h( const double y, const double x, const int orientation, double* H, float* descriptor, const std::vector* layers, + const Mat _cube_sigmas, const Mat* _grid_points, const double* _orientation_shift_table, const int _th_q_no ) +{ + CV_Assert( orientation >= 0 && orientation < 360 ); + CV_Assert( !layers->empty() ); + CV_Assert( descriptor != NULL ); + + int hradius[MAX_CUBE_NO]; + + double hy, hx, ry, rx; + pt_H( H, x, y, hx, hy ); + + if ( ! Point2f( (float)hx, (float)hy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) return false; + + int _rad_q_no = (int) layers->size() - 1; + int _hist_th_q_no = layers->at(0).size[0]; + pt_H( H, x+_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); + double d0 = rx - hx; double d1 = ry - hy; + double radius = sqrt( d0*d0 + d1*d1 ); + hradius[0] = quantize_radius( (float) radius, _rad_q_no, _cube_sigmas ); + + double shift = _orientation_shift_table[orientation]; + i_get_histogram( descriptor, hy, hx, shift, &layers->at(hradius[0]) ); + + double gy, gx; + int r, rdt, th, region; + float* histogram=0; + for( r=0; r<_rad_q_no; r++) + { + rdt = r*_th_q_no + 1; + for( th=0; th<_th_q_no; th++ ) + { + region = rdt + th; + + gy = y + _grid_points->at(region,0); + gx = x + _grid_points->at(region,1); + + pt_H(H, gx, gy, hx, hy); + if( th == 0 ) + { + pt_H(H, gx+_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); + d0 = rx - hx; d1 = ry - hy; + radius = sqrt( d0*d0 + d1*d1 ); + hradius[r] = quantize_radius( (float) radius, _rad_q_no, _cube_sigmas ); + } + + if ( ! Point2f( (float)hx, (float)hy ).inside( + Rect( 0, 0, layers->at(0).size[2]-1, layers->at(0).size[1]-1 ) ) + ) continue; + + histogram = descriptor + region*_hist_th_q_no; + i_get_histogram( histogram, hy, hx, shift, &layers->at(hradius[r]) ); + } + } + return true; +} + +static void get_unnormalized_descriptor( const double y, const double x, const int orientation, float* descriptor, + const std::vector* m_smoothed_gradient_layers, const Mat* m_oriented_grid_points, + const double* m_orientation_shift_table, const int m_th_q_no, const bool m_enable_interpolation ) +{ + if( m_enable_interpolation ) + i_get_descriptor( y, x, orientation, descriptor, m_smoothed_gradient_layers, + m_oriented_grid_points, m_orientation_shift_table, m_th_q_no ); + else + ni_get_descriptor( y, x, orientation, descriptor, m_smoothed_gradient_layers, + m_oriented_grid_points, m_orientation_shift_table, m_th_q_no); +} + +static void get_descriptor( const double y, const double x, const int orientation, float* descriptor, + const std::vector* m_smoothed_gradient_layers, const Mat* m_oriented_grid_points, + const double* m_orientation_shift_table, const int m_th_q_no, const int m_hist_th_q_no, + const int m_grid_point_number, const int m_descriptor_size, const bool m_enable_interpolation, + const int m_nrm_type ) +{ + get_unnormalized_descriptor( y, x, orientation, descriptor, m_smoothed_gradient_layers, + m_oriented_grid_points, m_orientation_shift_table, m_th_q_no, m_enable_interpolation ); + normalize_descriptor( descriptor, m_nrm_type, m_grid_point_number, m_hist_th_q_no, m_descriptor_size ); +} + +static bool get_unnormalized_descriptor_h( const double y, const double x, const int orientation, float* descriptor, double* H, + const std::vector* m_smoothed_gradient_layers, const Mat& m_cube_sigmas, + const Mat* m_grid_points, const double* m_orientation_shift_table, const int m_th_q_no, const bool m_enable_interpolation ) + +{ + if( m_enable_interpolation ) + return i_get_descriptor_h( y, x, orientation, H, descriptor, m_smoothed_gradient_layers, m_cube_sigmas, + m_grid_points, m_orientation_shift_table, m_th_q_no ); + else + return ni_get_descriptor_h( y, x, orientation, H, descriptor, m_smoothed_gradient_layers, m_cube_sigmas, + m_grid_points, m_orientation_shift_table, m_th_q_no ); +} + +static bool get_descriptor_h( const double y, const double x, const int orientation, float* descriptor, double* H, + const std::vector* m_smoothed_gradient_layers, const Mat& m_cube_sigmas, + const Mat* m_grid_points, const double* m_orientation_shift_table, const int m_th_q_no, + const int m_hist_th_q_no, const int m_grid_point_number, const int m_descriptor_size, + const bool m_enable_interpolation, const int m_nrm_type ) + +{ + bool rval = + get_unnormalized_descriptor_h( y, x, orientation, descriptor, H, m_smoothed_gradient_layers, m_cube_sigmas, + m_grid_points, m_orientation_shift_table, m_th_q_no, m_enable_interpolation ); + + if( rval ) + normalize_descriptor( descriptor, m_nrm_type, m_grid_point_number, m_hist_th_q_no, m_descriptor_size ); + + return rval; +} + +void DAISY_Impl::GetDescriptor( double y, double x, int orientation, float* descriptor ) const +{ + get_descriptor( y, x, orientation, descriptor, &m_smoothed_gradient_layers, + &m_oriented_grid_points, m_orientation_shift_table, m_th_q_no, + m_hist_th_q_no, m_grid_point_number, m_descriptor_size, m_enable_interpolation, + m_nrm_type ); +} + +bool DAISY_Impl::GetDescriptor( double y, double x, int orientation, float* descriptor, double* H ) const +{ + return + get_descriptor_h( y, x, orientation, descriptor, H, &m_smoothed_gradient_layers, + m_cube_sigmas, &m_grid_points, m_orientation_shift_table, m_th_q_no, + m_hist_th_q_no, m_grid_point_number, m_descriptor_size, m_enable_interpolation, + m_nrm_type ); +} + +void DAISY_Impl::GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor ) const +{ + get_unnormalized_descriptor( y, x, orientation, descriptor, &m_smoothed_gradient_layers, + &m_oriented_grid_points, m_orientation_shift_table, m_th_q_no, + m_enable_interpolation ); +} + +bool DAISY_Impl::GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor, double* H ) const +{ + return + get_unnormalized_descriptor_h( y, x, orientation, descriptor, H, &m_smoothed_gradient_layers, + m_cube_sigmas, &m_grid_points, m_orientation_shift_table, m_th_q_no, + m_enable_interpolation ); +} + inline void DAISY_Impl::compute_grid_points() { double r_step = m_rad / (double)m_rad_q_no; - double t_step = 2*CV_PI/ m_th_q_no; + double t_step = 2*CV_PI / m_th_q_no; m_grid_points.release(); m_grid_points = Mat( m_grid_point_number, 2, CV_64F ); @@ -578,155 +972,110 @@ inline void DAISY_Impl::compute_grid_points() compute_oriented_grid_points(); } -inline void DAISY_Impl::normalize_descriptor( float* desc, int nrm_type = DAISY::NRM_NONE ) const +struct ComputeDescriptorsInvoker : ParallelLoopBody { - if( nrm_type == DAISY::NRM_NONE ) nrm_type = m_nrm_type; - else if( nrm_type == DAISY::NRM_PARTIAL ) normalize_partial(desc); - else if( nrm_type == DAISY::NRM_FULL ) normalize_full(desc); - else if( nrm_type == DAISY::NRM_SIFT ) normalize_sift_way(desc); - else - CV_Error( Error::StsInternal, "No such normalization" ); -} + ComputeDescriptorsInvoker( Mat* _descriptors, Mat* _image, Rect* _roi, + std::vector* _layers, Mat* _orientation_map, + Mat* _oriented_grid_points, double* _orientation_shift_table, + int _th_q_no, bool _enable_interpolation ) + { + x_off = _roi->x; + x_end = _roi->x + _roi->width; + image = _image; + layers = _layers; + th_q_no = _th_q_no; + descriptors = _descriptors; + orientation_map = _orientation_map; + enable_interpolation = _enable_interpolation; + oriented_grid_points = _oriented_grid_points; + orientation_shift_table = _orientation_shift_table; + } + + void operator ()(const cv::Range& range) const + { + int index, orientation; + for (int y = range.start; y < range.end; ++y) + { + for( int x = x_off; x < x_end; x++ ) + { + index = y*image->cols + x; + orientation = 0; + if( !orientation_map->empty() ) + orientation = (int) orientation_map->at( y, x ); + if( !( orientation >= 0 && orientation < g_grid_orientation_resolution ) ) + orientation = 0; + get_unnormalized_descriptor( y, x, orientation, descriptors->ptr( index ), + layers, oriented_grid_points, orientation_shift_table, + th_q_no, enable_interpolation ); + } + } + } + + int th_q_no; + int x_off, x_end; + std::vector* layers; + Mat *descriptors; + Mat *orientation_map; + bool enable_interpolation; + double* orientation_shift_table; + Mat *image, *oriented_grid_points; +}; // Computes the descriptor by sampling convoluted orientation maps. -inline void DAISY_Impl::compute_descriptors() +inline void DAISY_Impl::compute_descriptors( Mat* m_dense_descriptors ) { - int y_off = m_roi.y; - int x_off = m_roi.x; int y_end = m_roi.y + m_roi.height; - int x_end = m_roi.x + m_roi.width; -// if( m_scale_invariant ) compute_scales(); -// if( m_rotation_invariant ) compute_orientations(); + if( m_scale_invariant ) compute_scales(); + if( m_rotation_invariant ) compute_orientations(); - m_dense_descriptors = Mat( m_roi.width*m_roi.height, m_descriptor_size, CV_32F, Scalar(0) ); + m_dense_descriptors->setTo( Scalar(0) ); + + parallel_for_( Range(y_off, y_end), + ComputeDescriptorsInvoker( m_dense_descriptors, &m_image, &m_roi, &m_smoothed_gradient_layers, + &m_orientation_map, &m_oriented_grid_points, m_orientation_shift_table, + m_th_q_no, m_enable_interpolation ) + ); - int y, x, index, orientation; -#if defined _OPENMP -#pragma omp parallel for private(y,x,index,orientation) -#endif - for( y=y_off; y( x, y ); - if( !( orientation >= 0 && orientation < g_grid_orientation_resolution ) ) - orientation = 0; - get_unnormalized_descriptor( y, x, orientation, m_dense_descriptors.ptr( index ) ); - } - } } -inline void DAISY_Impl::smooth_layers( Mat layers, int h, int w, int layer_number, float sigma ) +struct NormalizeDescriptorsInvoker : ParallelLoopBody { - - int i; - - int kernel_size = filter_size( sigma ); - std::vector kernel(kernel_size); - gaussian_1d( &kernel[0], kernel_size, sigma, 0 ); - -#if defined _OPENMP -#pragma omp parallel for private(i, layer) -#endif - - for( i=0; i(0, i) ); - - Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); - filter2D( cvI, cvI, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( cvI, cvI, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + descriptors = _descriptors; + nrm_type = _nrm_type; + grid_point_number = _grid_point_number; + hist_th_q_no = _hist_th_q_no; + descriptor_size = _descriptor_size; } -} -inline void DAISY_Impl::normalize_partial( float* desc ) const -{ - float norm = 0.0f; - for( int h=0; hptr(d), nrm_type, + grid_point_number, hist_th_q_no, descriptor_size ); } } -} -inline void DAISY_Impl::normalize_full( float* desc ) const + Mat *descriptors; + int nrm_type; + int grid_point_number; + int hist_th_q_no; + int descriptor_size; +}; + +inline void DAISY_Impl::normalize_descriptors( Mat* m_dense_descriptors ) { - // l2 norm - float norm = 0.0f; - for( int i=0; i 1e-5 ) - // divide with norm - for( int i=0; i m_descriptor_normalization_threshold ) - { - desc[ h ] = m_descriptor_normalization_threshold; - changed = true; - } - } - } -} - -inline void DAISY_Impl::normalize_descriptors( int nrm_type ) -{ - int d; + CV_Assert( !m_dense_descriptors->empty() ); int number_of_descriptors = m_roi.width * m_roi.height; -#if defined _OPENMP -#pragma omp parallel for private(d) -#endif - for( d=0; d( d ), nrm_type ); + parallel_for_( Range(0, number_of_descriptors), + NormalizeDescriptorsInvoker( m_dense_descriptors, m_nrm_type, m_grid_point_number, m_hist_th_q_no, m_descriptor_size ) + ); } inline void DAISY_Impl::initialize() @@ -735,20 +1084,18 @@ inline void DAISY_Impl::initialize() CV_Assert(m_image.rows != 0); CV_Assert(m_image.cols != 0); - if( m_layer_size == 0 ) { - m_layer_size = m_image.rows * m_image.cols; - m_cube_size = m_layer_size * m_hist_th_q_no; - } + // (m_rad_q_no + 1) matrices + // 3 dims matrix (idhist, img_y, img_x); + m_smoothed_gradient_layers.resize( m_rad_q_no + 1 ); - // 4 dims matrix (idcube, idhist, img_y, img_x); - int dims[4] = { g_cube_number + 1, m_hist_th_q_no, m_image.rows , m_image.cols }; - m_smoothed_gradient_layers = Mat( 4, dims, CV_32F ); + int dims[3] = { m_hist_th_q_no, m_image.rows, m_image.cols }; + for ( int c=0; c<=m_rad_q_no; c++) + m_smoothed_gradient_layers[c] = Mat( 3, dims, CV_32F ); - layered_gradient( m_image, m_smoothed_gradient_layers ); + layered_gradient( m_image, &m_smoothed_gradient_layers[0] ); // assuming a 0.5 image smoothness, we pull this to 1.6 as in sift - smooth_layers( m_smoothed_gradient_layers, m_image.rows, m_image.cols, - m_hist_th_q_no, (float)sqrt(g_sigma_init*g_sigma_init-0.25f) ); + smooth_layers( &m_smoothed_gradient_layers[0], (float)sqrt(g_sigma_init*g_sigma_init-0.25f) ); } @@ -756,11 +1103,8 @@ inline void DAISY_Impl::compute_cube_sigmas() { if( m_cube_sigmas.empty() ) { - // user didn't set the sigma's; - // set them from the descriptor parameters - g_cube_number = m_rad_q_no; - m_cube_sigmas = Mat(1, g_cube_number, CV_64F); + m_cube_sigmas = Mat(1, m_rad_q_no, CV_64F); double r_step = (double)m_rad / m_rad_q_no / 2; for( int r=0; r(0) ) - return 0; - if( rad >= m_cube_sigmas.at(g_cube_number-1) ) - return g_cube_number-1; - - int idx_min[2]; - minMaxIdx( abs( m_cube_sigmas - rad ), NULL, NULL, idx_min ); - - return idx_min[1]; -} - -inline void DAISY_Impl::compute_histograms() -{ - int r, y, x, ind; - float* hist=0; - - for( r=0; r* _layers, int _r ) { - float* dst = m_smoothed_gradient_layers.ptr(r ); - float* src = m_smoothed_gradient_layers.ptr(r+1); + r = _r; + layers = _layers; + _hist_th_q_no = layers->at(r).size[0]; + } -#if defined _OPENMP -#pragma omp parallel for private(y,x,ind,hist) -#endif - for( y=0; yat(r).size[2]; x++ ) { - ind = y*m_image.cols+x; - hist = dst + m_hist_th_q_no * ind; - compute_histogram( src, y, x, hist ); + if ( ! Point( x, y ).inside( + Rect( 0, 0, layers->at(r).size[2]-1, layers->at(r).size[1]-1 ) ) + ) continue; + + float* hist = layers->at(r).ptr(0,y*_hist_th_q_no,x*_hist_th_q_no); + + for( int h = 0; h < _hist_th_q_no; h++ ) + hist[h] = layers->at(r+1).at(h,y,x); } } } -} -inline void DAISY_Impl::normalize_histograms() + int r, _hist_th_q_no; + std::vector *layers; +}; + +inline void DAISY_Impl::compute_histograms() { - for( int r=0; r(r); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int y=0; y(0); @@ -858,22 +1176,13 @@ inline void DAISY_Impl::compute_smoothed_gradient_layers() sigma = sqrt( m_cube_sigmas.at(r ) * m_cube_sigmas.at(r ) - m_cube_sigmas.at(r-1) * m_cube_sigmas.at(r-1) ); - int kernel_size = filter_size( sigma ); - std::vector kernel(kernel_size); - gaussian_1d(&kernel[0], kernel_size, (float)sigma, 0); + int ks = filter_size( sigma, 5.0f ); - -#if defined _OPENMP -#pragma omp parallel for -#endif for( int th=0; th(r , th) ); - Mat cvO( m_image.rows, m_image.cols, CV_32FC1, m_smoothed_gradient_layers.ptr(r+1, th) ); - - Mat Kernel( 1, kernel_size, CV_32FC1, &kernel[0] ); - filter2D( cvI, cvO, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( cvO, cvO, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + Mat cvI( m_image.rows, m_image.cols, CV_32F, m_smoothed_gradient_layers[r ].ptr(th,0,0) ); + Mat cvO( m_image.rows, m_image.cols, CV_32F, m_smoothed_gradient_layers[r+1].ptr(th,0,0) ); + GaussianBlur( cvI, cvO, Size(ks, ks), sigma, sigma, BORDER_REPLICATE ); } } compute_histograms(); @@ -904,36 +1213,54 @@ inline void DAISY_Impl::compute_oriented_grid_points() } } -inline void DAISY_Impl::smooth_histogram(Mat hist, int hsz) +struct MaxDoGInvoker : ParallelLoopBody { - int i; - float prev, temp; - - prev = hist.at(hsz - 1); - for (i = 0; i < hsz; i++) + MaxDoGInvoker( Mat* _next_sim, Mat* _sim, Mat* _max_dog, Mat* _scale_map, int _i, int _r ) { - temp = hist.at(i); - hist.at(i) = (prev + hist.at(i) + hist.at( (i + 1 == hsz) ? 0 : i + 1) ) / 3.0f; - prev = temp; + i = _i; + r = _r; + sim = _sim; + max_dog = _max_dog; + next_sim = _next_sim; + scale_map = _scale_map; } -} -inline float DAISY_Impl::interpolate_peak(float left, float center, float right) + void operator ()(const cv::Range& range) const + { + for (int c = range.start; c < range.end; ++c) + { + float dog = (float) fabs( next_sim->at(r,c) - sim->at(r,c) ); + if( dog > max_dog->at(r,c) ) + { + max_dog->at(r,c) = dog; + scale_map->at(r,c) = (float) i; + } + } + } + int i, r; + Mat* max_dog; + Mat* scale_map; + Mat *sim, *next_sim; +}; + +struct RoundingInvoker : ParallelLoopBody { - if( center < 0.0 ) + RoundingInvoker( Mat* _scale_map, int _r ) { - left = -left; - center = -center; - right = -right; + r = _r; + scale_map = _scale_map; } - CV_Assert(center >= left && center >= right); - - float den = (float) (left - 2.0 * center + right); - - if( den == 0 ) return 0; - else return (float) (0.5*(left -right)/den); -} + void operator ()(const cv::Range& range) const + { + for (int c = range.start; c < range.end; ++c) + { + scale_map->at(r,c) = (float) round( scale_map->at(r,c) ); + } + } + int r; + Mat* scale_map; +}; inline void DAISY_Impl::compute_scales() { @@ -941,97 +1268,46 @@ inline void DAISY_Impl::compute_scales() //# scale detection is work-in-progress! do not use it if you're not Engin Tola # //############################################################################### - int kernel_size = 0; + Mat sim, next_sim; float sigma = (float) ( pow( g_sigma_step, g_scale_st)*g_sigma_0 ); - if( kernel_size == 0 ) kernel_size = (int)(3*sigma); - if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd - if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 - - std::vector kernel(kernel_size); - gaussian_1d( &kernel[0], kernel_size, sigma, 0 ); - Mat Kernel( 1, kernel_size, CV_32F, &kernel[0] ); - - Mat sim, next_sim; - - // output gaussian image - filter2D( m_image, sim, CV_32F, Kernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( sim, sim, CV_32F, Kernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); + int ks = filter_size( sigma, 3.0f ); + GaussianBlur( m_image, sim, Size(ks, ks), sigma, sigma, BORDER_REPLICATE ); Mat max_dog( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); m_scale_map = Mat( m_image.rows, m_image.cols, CV_32F, Scalar(0) ); - int i; float sigma_prev; float sigma_new; float sigma_inc; sigma_prev = (float) g_sigma_0; - for( i=0; i(r,c) - sim.at(r,c) ); - if( dog > max_dog.at(r,c) ) - { - max_dog.at(r,c) = dog; - m_scale_map.at(r,c) = (float) i; - } - } + parallel_for_( Range(0, m_image.cols), MaxDoGInvoker( &next_sim, &sim, &max_dog, &m_scale_map, i, r ) ); } sim.release(); sim = next_sim; } - kernel_size = filter_size( 10.0f ); - if( kernel_size == 0 ) kernel_size = (int)(3 * 10.0f); - if( kernel_size%2 == 0 ) kernel_size++; // kernel size must be odd - if( kernel_size < 3 ) kernel_size = 3; // kernel size cannot be smaller than 3 + ks = filter_size( 10.0f, 3.0f ); + GaussianBlur( m_scale_map, m_scale_map, Size(ks, ks), 10.0f, 10.0f, BORDER_REPLICATE ); - - kernel.resize(kernel_size); - gaussian_1d( &kernel[0], kernel_size, 10.0f, 0 ); - Mat FilterKernel( 1, kernel_size, CV_32F, &kernel[0] ); - - // output gaussian image - filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel, Point( -1, -1 ), 0, BORDER_REPLICATE ); - filter2D( m_scale_map, m_scale_map, CV_32F, FilterKernel.t(), Point( -1, -1 ), 0, BORDER_REPLICATE ); - -#if defined _OPENMP -#pragma omp parallel for -#endif - for( int r=0; r(r,c) = (float) round( m_scale_map.at(r,c) ); - } - } - //save( m_scale_map, m_image.rows, m_image.cols, "scales.dat"); + for( int r=0; r(y,x) != scale ) continue; - //hist.at(ori) = rotation_layers.ptr(0, ori, y, x); - //hist.at(ori) = rotation_layers.at(ori*data_size+ind); - + for (ori = 0; ori < m_orientation_resolution; ori++) + { + hist.at(ori) = rotation_layers.at(ori, y, x); + } for( kk=0; kk<6; kk++ ) - smooth_histogram( hist, m_orientation_resolution ); + smooth_histogram( &hist, m_orientation_resolution ); max_val = -1; max_ind = 0; @@ -1128,361 +1402,6 @@ inline void DAISY_Impl::compute_orientations() compute_oriented_grid_points(); } -inline void DAISY_Impl::compute_histogram( float* hcube, int y, int x, float* histogram ) -{ - if ( ! Point( x, y ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) return; - - float* spatial_shift = hcube + y * m_image.cols + x; - int data_size = m_image.cols * m_image.rows; - - for( int h=0; h 0.99 ) bi_get_histogram( histogram, y, x, ishift+1, cube ); - else ti_get_histogram( histogram, y, x, shift , cube ); -} - -inline void DAISY_Impl::bi_get_histogram( float* histogram, double y, double x, int shift, const float* hcube ) const -{ - int mnx = int( x ); - int mny = int( y ); - - if( mnx >= m_image.cols-2 || mny >= m_image.rows-2 ) - { - memset(histogram, 0, sizeof(float)*m_hist_th_q_no); - return; - } - - int ind = mny*m_image.cols+mnx; - // A C --> pixel positions - // B D - const float* A = hcube + ind*m_hist_th_q_no; - const float* B = A + m_image.cols*m_hist_th_q_no; - const float* C = A + m_hist_th_q_no; - const float* D = A + (m_image.cols+1)*m_hist_th_q_no; - - double alpha = mnx+1-x; - double beta = mny+1-y; - - float w0 = (float) (alpha*beta); - float w1 = (float) (beta-w0); // (1-alpha)*beta; - float w2 = (float) (alpha-w0); // (1-beta)*alpha; - float w3 = (float) (1+w0-alpha-beta); // (1-beta)*(1-alpha); - - int h; - - for( h=0; h= m_hist_th_q_no ) hi -= m_hist_th_q_no; - histogram[h] = hptr[hi]; - } -} - -inline void DAISY_Impl::get_descriptor( int y, int x, float* &descriptor ) -{ - CV_Assert( !m_dense_descriptors.empty() ); - CV_Assert( y=0 && x>=0 ); - descriptor = m_dense_descriptors.ptr( y*m_image.cols+x ); -} - -inline void DAISY_Impl::get_descriptor( double y, double x, int orientation, float* descriptor ) const -{ - get_unnormalized_descriptor(y, x, orientation, descriptor ); - normalize_descriptor(descriptor, m_nrm_type); -} - -inline void DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, float* descriptor ) const -{ - if( m_disable_interpolation ) ni_get_descriptor(y,x,orientation,descriptor); - else i_get_descriptor(y,x,orientation,descriptor); -} - -inline void DAISY_Impl::i_get_descriptor( double y, double x, int orientation, float* descriptor ) const -{ - // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); - // - // i'm not changing the descriptor[] values if the gridpoint is outside - // the image. you should memset the descriptor array to 0 if you don't - // want to have stupid values there. - - CV_Assert( y >= 0 && y < m_image.rows ); - CV_Assert( x >= 0 && x < m_image.cols ); - CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( !m_smoothed_gradient_layers.empty() ); - CV_Assert( !m_oriented_grid_points.empty() ); - CV_Assert( descriptor != NULL ); - - double shift = m_orientation_shift_table[orientation]; - - i_get_histogram( descriptor, y, x, shift, m_smoothed_gradient_layers.ptr( g_selected_cubes[0] ) ); - - int r, rdt, region; - double yy, xx; - float* histogram = 0; - - Mat grid = m_oriented_grid_points.row( orientation ); - - // petals of the flower - for( r=0; r(2*region ); - xx = x + grid.at(2*region + 1); - - if ( ! Point2f( (float)xx, (float)yy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) continue; - - histogram = descriptor + region*m_hist_th_q_no; - i_get_histogram( histogram, yy, xx, shift, (float *) m_smoothed_gradient_layers.ptr( r ) ); - } - } -} - -inline void DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, float* descriptor ) const -{ - // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); - // - // i'm not changing the descriptor[] values if the gridpoint is outside - // the image. you should memset the descriptor array to 0 if you don't - // want to have stupid values there. - - CV_Assert( y >= 0 && y < m_image.rows ); - CV_Assert( x >= 0 && x < m_image.cols ); - CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( !m_smoothed_gradient_layers.empty() ); - CV_Assert( !m_oriented_grid_points.empty() ); - CV_Assert( descriptor != NULL ); - - double shift = m_orientation_shift_table[orientation]; - int ishift = (int)shift; - if( shift - ishift > 0.5 ) ishift++; - - int iy = (int)y; if( y - iy > 0.5 ) iy++; - int ix = (int)x; if( x - ix > 0.5 ) ix++; - - // center - ni_get_histogram( descriptor, iy, ix, ishift, m_smoothed_gradient_layers.ptr(g_selected_cubes[0]) ); - - double yy, xx; - float* histogram=0; - // petals of the flower - int r, rdt, region; - Mat grid = m_oriented_grid_points.row( orientation ); - for( r=0; r(2*region ); - xx = x + grid.at(2*region+1); - iy = (int)yy; if( yy - iy > 0.5 ) iy++; - ix = (int)xx; if( xx - ix > 0.5 ) ix++; - - if ( ! Point2f( (float)xx, (float)yy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) continue; - - histogram = descriptor+region*m_hist_th_q_no; - ni_get_histogram( histogram, iy, ix, ishift, m_smoothed_gradient_layers.ptr(g_selected_cubes[r]) ); - } - } -} - -// Warped get_descriptor's -inline bool DAISY_Impl::get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const -{ - bool rval = get_unnormalized_descriptor(y,x,orientation, H, descriptor); - if( rval ) normalize_descriptor(descriptor, m_nrm_type); - return rval; -} - -inline bool DAISY_Impl::get_unnormalized_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const -{ - if( m_disable_interpolation ) return ni_get_descriptor(y,x,orientation,H,descriptor); - else return i_get_descriptor(y,x,orientation,H,descriptor); -} - -inline bool DAISY_Impl::i_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const -{ - // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); - // - // i'm not changing the descriptor[] values if the gridpoint is outside - // the image. you should memset the descriptor array to 0 if you don't - // want to have stupid values there. - - CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( !m_smoothed_gradient_layers.empty() ); - CV_Assert( descriptor != NULL ); - - int hradius[MAX_CUBE_NO]; - - double hy, hx, ry, rx; - point_transform_via_homography( H, x, y, hx, hy ); - - if ( ! Point2f( (float)hx, (float)hy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) return false; - - point_transform_via_homography( H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); - double d0 = rx - hx; double d1 = ry - hy; - double radius = sqrt( d0*d0 + d1*d1 ); - hradius[0] = quantize_radius( (float) radius ); - - double shift = m_orientation_shift_table[orientation]; - i_get_histogram( descriptor, hy, hx, shift, m_smoothed_gradient_layers.ptr(hradius[0]) ); - - double gy, gx; - int r, rdt, th, region; - float* histogram=0; - for( r=0; r(region,0); - gx = x + m_grid_points.at(region,1); - - point_transform_via_homography(H, gx, gy, hx, hy); - if( th == 0 ) - { - point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); - d0 = rx - hx; d1 = ry - hy; - radius = sqrt( d0*d0 + d1+d1 ); - hradius[r] = quantize_radius( (float) radius ); - } - - if ( ! Point2f( (float)hx, (float)hy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) continue; - - histogram = descriptor+region*m_hist_th_q_no; - i_get_histogram( histogram, hy, hx, shift, m_smoothed_gradient_layers.ptr(hradius[r]) ); - } - } - return true; -} - -inline bool DAISY_Impl::ni_get_descriptor( double y, double x, int orientation, double* H, float* descriptor ) const -{ - // memset( descriptor, 0, sizeof(float)*m_descriptor_size ); - // - // i'm not changing the descriptor[] values if the gridpoint is outside - // the image. you should memset the descriptor array to 0 if you don't - // want to have stupid values there. - - CV_Assert( orientation >= 0 && orientation < 360 ); - CV_Assert( !m_smoothed_gradient_layers.empty() ); - CV_Assert( descriptor != NULL ); - - int hradius[MAX_CUBE_NO]; - - double hy, hx, ry, rx; - - point_transform_via_homography(H, x, y, hx, hy ); - - if ( ! Point2f( (float)hx, (float)hy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) return false; - - double shift = m_orientation_shift_table[orientation]; - int ishift = (int)shift; if( shift - ishift > 0.5 ) ishift++; - - point_transform_via_homography(H, x+m_cube_sigmas.at(g_selected_cubes[0]), y, rx, ry); - double d0 = rx - hx; double d1 = ry - hy; - double radius = sqrt( d0*d0 + d1*d1 ); - hradius[0] = quantize_radius( (float) radius ); - - int ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; - int ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; - - int r, rdt, th, region; - double gy, gx; - float* histogram=0; - ni_get_histogram( descriptor, ihy, ihx, ishift, m_smoothed_gradient_layers.ptr(hradius[0]) ); - for( r=0; r(region,0); - gx = x + m_grid_points.at(region,1); - - point_transform_via_homography(H, gx, gy, hx, hy); - if( th == 0 ) - { - point_transform_via_homography(H, gx+m_cube_sigmas.at(g_selected_cubes[r]), gy, rx, ry); - d0 = rx - hx; d1 = ry - hy; - radius = sqrt( d0*d0 + d1*d1 ); - hradius[r] = quantize_radius( (float) radius ); - } - - ihx = (int)hx; if( hx - ihx > 0.5 ) ihx++; - ihy = (int)hy; if( hy - ihy > 0.5 ) ihy++; - - if ( ! Point( ihx, ihy ).inside( - Rect( 0, 0, m_image.cols-1, m_image.rows-1 ) ) - ) continue; - - histogram = descriptor+region*m_hist_th_q_no; - ni_get_histogram( histogram, ihy, ihx, ishift, m_smoothed_gradient_layers.ptr(hradius[r]) ); - } - } - return true; -} inline void DAISY_Impl::initialize_single_descriptor_mode( ) { @@ -1499,8 +1418,6 @@ inline void DAISY_Impl::set_parameters( ) { m_orientation_shift_table[i] = i/360.0 * m_hist_th_q_no; } - m_layer_size = m_image.rows*m_image.cols; - m_cube_size = m_layer_size*m_hist_th_q_no; compute_cube_sigmas(); compute_grid_points(); @@ -1574,14 +1491,20 @@ void DAISY_Impl::compute( InputArray _image, std::vector& keypoints, O { get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, m_use_orientation ? (int) keypoints[k].angle : 0, - &descriptors.at( k, 0 ) ); + &descriptors.at( k, 0 ), &m_smoothed_gradient_layers, + &m_oriented_grid_points, m_orientation_shift_table, m_th_q_no, + m_hist_th_q_no, m_grid_point_number, m_descriptor_size, m_enable_interpolation, + m_nrm_type ); } else for (int k = 0; k < (int) keypoints.size(); k++) { - get_descriptor( keypoints[k].pt.y, keypoints[k].pt.x, + get_descriptor_h( keypoints[k].pt.y, keypoints[k].pt.x, m_use_orientation ? (int) keypoints[k].angle : 0, - &H.at( 0 ), &descriptors.at( k, 0 ) ); + &descriptors.at( k, 0 ), &H.at( 0 ), &m_smoothed_gradient_layers, + m_cube_sigmas, &m_grid_points, m_orientation_shift_table, m_th_q_no, + m_hist_th_q_no, m_grid_point_number, m_descriptor_size, m_enable_interpolation, + m_nrm_type ); } } @@ -1603,12 +1526,13 @@ void DAISY_Impl::compute( InputArray _image, Rect roi, OutputArray _descriptors set_parameters(); initialize_single_descriptor_mode(); - // compute full desc - compute_descriptors(); - normalize_descriptors(); + _descriptors.create( m_roi.width*m_roi.height, m_descriptor_size, CV_32F ); Mat descriptors = _descriptors.getMat(); - descriptors = m_dense_descriptors; + + // compute full desc + compute_descriptors( &descriptors ); + normalize_descriptors( &descriptors ); release_auxiliary(); } @@ -1631,12 +1555,13 @@ void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) set_parameters(); initialize_single_descriptor_mode(); - // compute full desc - compute_descriptors(); - normalize_descriptors(); + _descriptors.create( m_roi.width*m_roi.height, m_descriptor_size, CV_32F ); Mat descriptors = _descriptors.getMat(); - descriptors = m_dense_descriptors; + + // compute full desc + compute_descriptors( &descriptors ); + normalize_descriptors( &descriptors ); release_auxiliary(); } @@ -1645,7 +1570,7 @@ void DAISY_Impl::compute( InputArray _image, OutputArray _descriptors ) DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, int _norm, InputArray _H, bool _interpolation, bool _use_orientation ) : m_rad(_radius), m_rad_q_no(_q_radius), m_th_q_no(_q_theta), m_hist_th_q_no(_q_hist), - m_nrm_type(_norm), m_disable_interpolation(_interpolation), m_use_orientation(_use_orientation) + m_nrm_type(_norm), m_enable_interpolation(_interpolation), m_use_orientation(_use_orientation) { m_descriptor_size = 0; @@ -1655,22 +1580,13 @@ DAISY_Impl::DAISY_Impl( float _radius, int _q_radius, int _q_theta, int _q_hist, m_rotation_invariant = false; m_orientation_resolution = 36; - m_cube_size = 0; - m_layer_size = 0; - - m_descriptor_normalization_threshold = 0.154f; // sift magical number - m_h_matrix = _H.getMat(); } // destructor DAISY_Impl::~DAISY_Impl() { - m_scale_map.release(); - m_grid_points.release(); - m_orientation_map.release(); - m_oriented_grid_points.release(); - m_smoothed_gradient_layers.release(); + release_auxiliary(); } Ptr DAISY::create( float radius, int q_radius, int q_theta, int q_hist, From 4c0278f387ace6755ffd5926b39d2785dda13d61 Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Fri, 29 May 2015 00:01:26 +0300 Subject: [PATCH 13/14] Remove few tabs due to vs2013. --- modules/xfeatures2d/src/daisy.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index b21399fbe..bfc30bcda 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -1356,10 +1356,10 @@ inline void DAISY_Impl::compute_orientations() { if( m_scale_invariant && m_scale_map.at(y,x) != scale ) continue; - for (ori = 0; ori < m_orientation_resolution; ori++) - { - hist.at(ori) = rotation_layers.at(ori, y, x); - } + for (ori = 0; ori < m_orientation_resolution; ori++) + { + hist.at(ori) = rotation_layers.at(ori, y, x); + } for( kk=0; kk<6; kk++ ) smooth_histogram( &hist, m_orientation_resolution ); From 7ec4559509e4f75760f94ba400f37121fa33221b Mon Sep 17 00:00:00 2001 From: cbalint13 Date: Fri, 29 May 2015 03:07:22 +0300 Subject: [PATCH 14/14] Fix docs once again. --- modules/xfeatures2d/include/opencv2/xfeatures2d.hpp | 4 ++-- modules/xfeatures2d/src/daisy.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp index 354d6146b..58d8218f5 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d.hpp @@ -222,7 +222,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void GetDescriptor( double y, double x, int orientation, float* descriptor ) const = 0; @@ -239,7 +239,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor ) const = 0; diff --git a/modules/xfeatures2d/src/daisy.cpp b/modules/xfeatures2d/src/daisy.cpp index bfc30bcda..d4a03f86e 100644 --- a/modules/xfeatures2d/src/daisy.cpp +++ b/modules/xfeatures2d/src/daisy.cpp @@ -132,7 +132,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void GetDescriptor( double y, double x, int orientation, float* descriptor ) const; @@ -140,7 +140,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage * @param H homography matrix for warped grid */ @@ -149,7 +149,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage */ virtual void GetUnnormalizedDescriptor( double y, double x, int orientation, float* descriptor ) const; @@ -157,7 +157,7 @@ public: /** * @param y position y on image * @param x position x on image - * @param ori orientation on image (0->360) + * @param orientation orientation on image (0->360) * @param descriptor supplied array for descriptor storage * @param H homography matrix for warped grid */