Home | History | Annotate | Download | only in kaze
      1 
      2 //=============================================================================
      3 //
      4 // KAZE.cpp
      5 // Author: Pablo F. Alcantarilla
      6 // Institution: University d'Auvergne
      7 // Address: Clermont Ferrand, France
      8 // Date: 21/01/2012
      9 // Email: pablofdezalc (at) gmail.com
     10 //
     11 // KAZE Features Copyright 2012, Pablo F. Alcantarilla
     12 // All Rights Reserved
     13 // See LICENSE for the license information
     14 //=============================================================================
     15 
     16 /**
     17  * @file KAZEFeatures.cpp
     18  * @brief Main class for detecting and describing features in a nonlinear
     19  * scale space
     20  * @date Jan 21, 2012
     21  * @author Pablo F. Alcantarilla
     22  */
     23 #include "../precomp.hpp"
     24 #include "KAZEFeatures.h"
     25 #include "utils.h"
     26 
     27 namespace cv
     28 {
     29 
     30 // Namespaces
     31 using namespace std;
     32 
     33 /* ************************************************************************* */
     34 /**
     35  * @brief KAZE constructor with input options
     36  * @param options KAZE configuration options
     37  * @note The constructor allocates memory for the nonlinear scale space
     38  */
     39 KAZEFeatures::KAZEFeatures(KAZEOptions& options)
     40         : options_(options)
     41 {
     42     ncycles_ = 0;
     43     reordering_ = true;
     44 
     45     // Now allocate memory for the evolution
     46     Allocate_Memory_Evolution();
     47 }
     48 
     49 /* ************************************************************************* */
     50 /**
     51  * @brief This method allocates the memory for the nonlinear diffusion evolution
     52  */
     53 void KAZEFeatures::Allocate_Memory_Evolution(void) {
     54 
     55     // Allocate the dimension of the matrices for the evolution
     56     for (int i = 0; i <= options_.omax - 1; i++)
     57     {
     58         for (int j = 0; j <= options_.nsublevels - 1; j++)
     59         {
     60             TEvolution aux;
     61             aux.Lx = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     62             aux.Ly = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     63             aux.Lxx = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     64             aux.Lxy = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     65             aux.Lyy = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     66             aux.Lt = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     67             aux.Lsmooth = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     68             aux.Ldet = Mat::zeros(options_.img_height, options_.img_width, CV_32F);
     69             aux.esigma = options_.soffset*pow((float)2.0f, (float)(j) / (float)(options_.nsublevels)+i);
     70             aux.etime = 0.5f*(aux.esigma*aux.esigma);
     71             aux.sigma_size = fRound(aux.esigma);
     72             aux.octave = i;
     73             aux.sublevel = j;
     74             evolution_.push_back(aux);
     75         }
     76     }
     77 
     78     // Allocate memory for the FED number of cycles and time steps
     79     for (size_t i = 1; i < evolution_.size(); i++)
     80     {
     81         int naux = 0;
     82         vector<float> tau;
     83         float ttime = 0.0;
     84         ttime = evolution_[i].etime - evolution_[i - 1].etime;
     85         naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau);
     86         nsteps_.push_back(naux);
     87         tsteps_.push_back(tau);
     88         ncycles_++;
     89     }
     90 }
     91 
     92 /* ************************************************************************* */
     93 /**
     94  * @brief This method creates the nonlinear scale space for a given image
     95  * @param img Input image for which the nonlinear scale space needs to be created
     96  * @return 0 if the nonlinear scale space was created successfully. -1 otherwise
     97  */
     98 int KAZEFeatures::Create_Nonlinear_Scale_Space(const Mat &img)
     99 {
    100     CV_Assert(evolution_.size() > 0);
    101 
    102     // Copy the original image to the first level of the evolution
    103     img.copyTo(evolution_[0].Lt);
    104     gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset);
    105     gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options_.sderivatives);
    106 
    107     // Firstly compute the kcontrast factor
    108         Compute_KContrast(evolution_[0].Lt, options_.kcontrast_percentille);
    109 
    110     // Allocate memory for the flow and step images
    111     Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);
    112     Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);
    113 
    114     // Now generate the rest of evolution levels
    115     for (size_t i = 1; i < evolution_.size(); i++)
    116     {
    117         evolution_[i - 1].Lt.copyTo(evolution_[i].Lt);
    118         gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options_.sderivatives);
    119 
    120         // Compute the Gaussian derivatives Lx and Ly
    121         Scharr(evolution_[i].Lsmooth, evolution_[i].Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT);
    122         Scharr(evolution_[i].Lsmooth, evolution_[i].Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT);
    123 
    124         // Compute the conductivity equation
    125         if (options_.diffusivity == KAZE::DIFF_PM_G1)
    126             pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
    127         else if (options_.diffusivity == KAZE::DIFF_PM_G2)
    128             pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
    129         else if (options_.diffusivity == KAZE::DIFF_WEICKERT)
    130             weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
    131 
    132         // Perform FED n inner steps
    133         for (int j = 0; j < nsteps_[i - 1]; j++)
    134             nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]);
    135     }
    136 
    137     return 0;
    138 }
    139 
    140 /* ************************************************************************* */
    141 /**
    142  * @brief This method computes the k contrast factor
    143  * @param img Input image
    144  * @param kpercentile Percentile of the gradient histogram
    145  */
    146 void KAZEFeatures::Compute_KContrast(const Mat &img, const float &kpercentile)
    147 {
    148     options_.kcontrast = compute_k_percentile(img, kpercentile, options_.sderivatives, options_.kcontrast_bins, 0, 0);
    149 }
    150 
    151 /* ************************************************************************* */
    152 /**
    153  * @brief This method computes the feature detector response for the nonlinear scale space
    154  * @note We use the Hessian determinant as feature detector
    155  */
    156 void KAZEFeatures::Compute_Detector_Response(void)
    157 {
    158     float lxx = 0.0, lxy = 0.0, lyy = 0.0;
    159 
    160     // Firstly compute the multiscale derivatives
    161     Compute_Multiscale_Derivatives();
    162 
    163     for (size_t i = 0; i < evolution_.size(); i++)
    164     {
    165                 for (int ix = 0; ix < options_.img_height; ix++)
    166         {
    167                         for (int jx = 0; jx < options_.img_width; jx++)
    168             {
    169                 lxx = *(evolution_[i].Lxx.ptr<float>(ix)+jx);
    170                 lxy = *(evolution_[i].Lxy.ptr<float>(ix)+jx);
    171                 lyy = *(evolution_[i].Lyy.ptr<float>(ix)+jx);
    172                 *(evolution_[i].Ldet.ptr<float>(ix)+jx) = (lxx*lyy - lxy*lxy);
    173             }
    174         }
    175     }
    176 }
    177 
    178 /* ************************************************************************* */
    179 /**
    180  * @brief This method selects interesting keypoints through the nonlinear scale space
    181  * @param kpts Vector of keypoints
    182  */
    183 void KAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts)
    184 {
    185     kpts.clear();
    186         Compute_Detector_Response();
    187         Determinant_Hessian(kpts);
    188     Do_Subpixel_Refinement(kpts);
    189 }
    190 
    191 /* ************************************************************************* */
    192 class MultiscaleDerivativesKAZEInvoker : public ParallelLoopBody
    193 {
    194 public:
    195     explicit MultiscaleDerivativesKAZEInvoker(std::vector<TEvolution>& ev) : evolution_(&ev)
    196     {
    197     }
    198 
    199     void operator()(const Range& range) const
    200     {
    201         std::vector<TEvolution>& evolution = *evolution_;
    202         for (int i = range.start; i < range.end; i++)
    203         {
    204             compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, evolution[i].sigma_size);
    205             compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, evolution[i].sigma_size);
    206             compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, evolution[i].sigma_size);
    207             compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, evolution[i].sigma_size);
    208             compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, evolution[i].sigma_size);
    209 
    210             evolution[i].Lx = evolution[i].Lx*((evolution[i].sigma_size));
    211             evolution[i].Ly = evolution[i].Ly*((evolution[i].sigma_size));
    212             evolution[i].Lxx = evolution[i].Lxx*((evolution[i].sigma_size)*(evolution[i].sigma_size));
    213             evolution[i].Lxy = evolution[i].Lxy*((evolution[i].sigma_size)*(evolution[i].sigma_size));
    214             evolution[i].Lyy = evolution[i].Lyy*((evolution[i].sigma_size)*(evolution[i].sigma_size));
    215         }
    216     }
    217 
    218 private:
    219     std::vector<TEvolution>*  evolution_;
    220 };
    221 
    222 /* ************************************************************************* */
    223 /**
    224  * @brief This method computes the multiscale derivatives for the nonlinear scale space
    225  */
    226 void KAZEFeatures::Compute_Multiscale_Derivatives(void)
    227 {
    228     parallel_for_(Range(0, (int)evolution_.size()),
    229                                         MultiscaleDerivativesKAZEInvoker(evolution_));
    230 }
    231 
    232 
    233 /* ************************************************************************* */
    234 class FindExtremumKAZEInvoker : public ParallelLoopBody
    235 {
    236 public:
    237     explicit FindExtremumKAZEInvoker(std::vector<TEvolution>& ev, std::vector<std::vector<KeyPoint> >& kpts_par,
    238                                                                      const KAZEOptions& options) : evolution_(&ev), kpts_par_(&kpts_par), options_(options)
    239     {
    240     }
    241 
    242     void operator()(const Range& range) const
    243     {
    244         std::vector<TEvolution>& evolution = *evolution_;
    245         std::vector<std::vector<KeyPoint> >& kpts_par = *kpts_par_;
    246         for (int i = range.start; i < range.end; i++)
    247         {
    248             float value = 0.0;
    249             bool is_extremum = false;
    250 
    251             for (int ix = 1; ix < options_.img_height - 1; ix++)
    252             {
    253                 for (int jx = 1; jx < options_.img_width - 1; jx++)
    254                 {
    255                     is_extremum = false;
    256                     value = *(evolution[i].Ldet.ptr<float>(ix)+jx);
    257 
    258                     // Filter the points with the detector threshold
    259                     if (value > options_.dthreshold)
    260                     {
    261                         if (value >= *(evolution[i].Ldet.ptr<float>(ix)+jx - 1))
    262                         {
    263                             // First check on the same scale
    264                             if (check_maximum_neighbourhood(evolution[i].Ldet, 1, value, ix, jx, 1))
    265                             {
    266                                 // Now check on the lower scale
    267                                 if (check_maximum_neighbourhood(evolution[i - 1].Ldet, 1, value, ix, jx, 0))
    268                                 {
    269                                     // Now check on the upper scale
    270                                     if (check_maximum_neighbourhood(evolution[i + 1].Ldet, 1, value, ix, jx, 0))
    271                                         is_extremum = true;
    272                                 }
    273                             }
    274                         }
    275                     }
    276 
    277                     // Add the point of interest!!
    278                     if (is_extremum)
    279                     {
    280                         KeyPoint point;
    281                         point.pt.x = (float)jx;
    282                         point.pt.y = (float)ix;
    283                         point.response = fabs(value);
    284                         point.size = evolution[i].esigma;
    285                         point.octave = (int)evolution[i].octave;
    286                         point.class_id = i;
    287 
    288                         // We use the angle field for the sublevel value
    289                         // Then, we will replace this angle field with the main orientation
    290                         point.angle = static_cast<float>(evolution[i].sublevel);
    291                         kpts_par[i - 1].push_back(point);
    292                     }
    293                 }
    294             }
    295         }
    296     }
    297 
    298 private:
    299     std::vector<TEvolution>*  evolution_;
    300     std::vector<std::vector<KeyPoint> >* kpts_par_;
    301     KAZEOptions options_;
    302 };
    303 
    304 /* ************************************************************************* */
    305 /**
    306  * @brief This method performs the detection of keypoints by using the normalized
    307  * score of the Hessian determinant through the nonlinear scale space
    308  * @param kpts Vector of keypoints
    309  * @note We compute features for each of the nonlinear scale space level in a different processing thread
    310  */
    311 void KAZEFeatures::Determinant_Hessian(std::vector<KeyPoint>& kpts)
    312 {
    313     int level = 0;
    314     float dist = 0.0, smax = 3.0;
    315     int npoints = 0, id_repeated = 0;
    316     int left_x = 0, right_x = 0, up_y = 0, down_y = 0;
    317     bool is_extremum = false, is_repeated = false, is_out = false;
    318 
    319     // Delete the memory of the vector of keypoints vectors
    320     // In case we use the same kaze object for multiple images
    321     for (size_t i = 0; i < kpts_par_.size(); i++) {
    322         vector<KeyPoint>().swap(kpts_par_[i]);
    323     }
    324     kpts_par_.clear();
    325     vector<KeyPoint> aux;
    326 
    327     // Allocate memory for the vector of vectors
    328     for (size_t i = 1; i < evolution_.size() - 1; i++) {
    329         kpts_par_.push_back(aux);
    330     }
    331 
    332     parallel_for_(Range(1, (int)evolution_.size()-1),
    333                 FindExtremumKAZEInvoker(evolution_, kpts_par_, options_));
    334 
    335     // Now fill the vector of keypoints!!!
    336     for (int i = 0; i < (int)kpts_par_.size(); i++)
    337     {
    338         for (int j = 0; j < (int)kpts_par_[i].size(); j++)
    339         {
    340             level = i + 1;
    341             is_extremum = true;
    342             is_repeated = false;
    343             is_out = false;
    344 
    345             // Check in case we have the same point as maxima in previous evolution levels
    346             for (int ik = 0; ik < (int)kpts.size(); ik++) {
    347                 if (kpts[ik].class_id == level || kpts[ik].class_id == level + 1 || kpts[ik].class_id == level - 1) {
    348                     dist = pow(kpts_par_[i][j].pt.x - kpts[ik].pt.x, 2) + pow(kpts_par_[i][j].pt.y - kpts[ik].pt.y, 2);
    349 
    350                     if (dist < evolution_[level].sigma_size*evolution_[level].sigma_size) {
    351                         if (kpts_par_[i][j].response > kpts[ik].response) {
    352                             id_repeated = ik;
    353                             is_repeated = true;
    354                         }
    355                         else {
    356                             is_extremum = false;
    357                         }
    358 
    359                         break;
    360                     }
    361                 }
    362             }
    363 
    364             if (is_extremum == true) {
    365                 // Check that the point is under the image limits for the descriptor computation
    366                 left_x = fRound(kpts_par_[i][j].pt.x - smax*kpts_par_[i][j].size);
    367                 right_x = fRound(kpts_par_[i][j].pt.x + smax*kpts_par_[i][j].size);
    368                 up_y = fRound(kpts_par_[i][j].pt.y - smax*kpts_par_[i][j].size);
    369                 down_y = fRound(kpts_par_[i][j].pt.y + smax*kpts_par_[i][j].size);
    370 
    371                 if (left_x < 0 || right_x >= evolution_[level].Ldet.cols ||
    372                     up_y < 0 || down_y >= evolution_[level].Ldet.rows) {
    373                     is_out = true;
    374                 }
    375 
    376                 is_out = false;
    377 
    378                 if (is_out == false) {
    379                     if (is_repeated == false) {
    380                         kpts.push_back(kpts_par_[i][j]);
    381                         npoints++;
    382                     }
    383                     else {
    384                         kpts[id_repeated] = kpts_par_[i][j];
    385                     }
    386                 }
    387             }
    388         }
    389     }
    390 }
    391 
    392 /* ************************************************************************* */
    393 /**
    394  * @brief This method performs subpixel refinement of the detected keypoints
    395  * @param kpts Vector of detected keypoints
    396  */
    397 void KAZEFeatures::Do_Subpixel_Refinement(std::vector<KeyPoint> &kpts) {
    398 
    399     int step = 1;
    400     int x = 0, y = 0;
    401     float Dx = 0.0, Dy = 0.0, Ds = 0.0, dsc = 0.0;
    402     float Dxx = 0.0, Dyy = 0.0, Dss = 0.0, Dxy = 0.0, Dxs = 0.0, Dys = 0.0;
    403     Mat A = Mat::zeros(3, 3, CV_32F);
    404     Mat b = Mat::zeros(3, 1, CV_32F);
    405     Mat dst = Mat::zeros(3, 1, CV_32F);
    406 
    407     vector<KeyPoint> kpts_(kpts);
    408 
    409     for (size_t i = 0; i < kpts_.size(); i++) {
    410 
    411         x = static_cast<int>(kpts_[i].pt.x);
    412         y = static_cast<int>(kpts_[i].pt.y);
    413 
    414         // Compute the gradient
    415         Dx = (1.0f / (2.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x + step)
    416             - *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x - step));
    417         Dy = (1.0f / (2.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x)
    418             - *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x));
    419         Ds = 0.5f*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x)
    420             - *(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x));
    421 
    422         // Compute the Hessian
    423         Dxx = (1.0f / (step*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x + step)
    424             + *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x - step)
    425             - 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x)));
    426 
    427         Dyy = (1.0f / (step*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x)
    428             + *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x)
    429             - 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x)));
    430 
    431         Dss = *(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x)
    432             + *(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x)
    433             - 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x));
    434 
    435         Dxy = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x + step)
    436             + (*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x - step)))
    437             - (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x + step)
    438             + (*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x - step)));
    439 
    440         Dxs = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x + step)
    441             + (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x - step)))
    442             - (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x - step)
    443             + (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x + step)));
    444 
    445         Dys = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y + step) + x)
    446             + (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y - step) + x)))
    447             - (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y - step) + x)
    448             + (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y + step) + x)));
    449 
    450         // Solve the linear system
    451         *(A.ptr<float>(0)) = Dxx;
    452         *(A.ptr<float>(1) + 1) = Dyy;
    453         *(A.ptr<float>(2) + 2) = Dss;
    454 
    455         *(A.ptr<float>(0) + 1) = *(A.ptr<float>(1)) = Dxy;
    456         *(A.ptr<float>(0) + 2) = *(A.ptr<float>(2)) = Dxs;
    457         *(A.ptr<float>(1) + 2) = *(A.ptr<float>(2) + 1) = Dys;
    458 
    459         *(b.ptr<float>(0)) = -Dx;
    460         *(b.ptr<float>(1)) = -Dy;
    461         *(b.ptr<float>(2)) = -Ds;
    462 
    463         solve(A, b, dst, DECOMP_LU);
    464 
    465         if (fabs(*(dst.ptr<float>(0))) <= 1.0f && fabs(*(dst.ptr<float>(1))) <= 1.0f && fabs(*(dst.ptr<float>(2))) <= 1.0f) {
    466             kpts_[i].pt.x += *(dst.ptr<float>(0));
    467             kpts_[i].pt.y += *(dst.ptr<float>(1));
    468                         dsc = kpts_[i].octave + (kpts_[i].angle + *(dst.ptr<float>(2))) / ((float)(options_.nsublevels));
    469 
    470             // In OpenCV the size of a keypoint is the diameter!!
    471                         kpts_[i].size = 2.0f*options_.soffset*pow((float)2.0f, dsc);
    472             kpts_[i].angle = 0.0;
    473         }
    474         // Set the points to be deleted after the for loop
    475         else {
    476             kpts_[i].response = -1;
    477         }
    478     }
    479 
    480     // Clear the vector of keypoints
    481     kpts.clear();
    482 
    483     for (size_t i = 0; i < kpts_.size(); i++) {
    484         if (kpts_[i].response != -1) {
    485             kpts.push_back(kpts_[i]);
    486         }
    487     }
    488 }
    489 
    490 /* ************************************************************************* */
    491 class KAZE_Descriptor_Invoker : public ParallelLoopBody
    492 {
    493 public:
    494         KAZE_Descriptor_Invoker(std::vector<KeyPoint> &kpts, Mat &desc, std::vector<TEvolution>& evolution, const KAZEOptions& options)
    495                 : kpts_(&kpts)
    496                 , desc_(&desc)
    497                 , evolution_(&evolution)
    498                 , options_(options)
    499     {
    500     }
    501 
    502     virtual ~KAZE_Descriptor_Invoker()
    503     {
    504     }
    505 
    506     void operator() (const Range& range) const
    507     {
    508                 std::vector<KeyPoint> &kpts      = *kpts_;
    509                 Mat                   &desc      = *desc_;
    510                 std::vector<TEvolution>   &evolution = *evolution_;
    511 
    512         for (int i = range.start; i < range.end; i++)
    513         {
    514             kpts[i].angle = 0.0;
    515                         if (options_.upright)
    516             {
    517                 kpts[i].angle = 0.0;
    518                                 if (options_.extended)
    519                     Get_KAZE_Upright_Descriptor_128(kpts[i], desc.ptr<float>((int)i));
    520                 else
    521                     Get_KAZE_Upright_Descriptor_64(kpts[i], desc.ptr<float>((int)i));
    522             }
    523             else
    524             {
    525                                 KAZEFeatures::Compute_Main_Orientation(kpts[i], evolution, options_);
    526 
    527                                 if (options_.extended)
    528                     Get_KAZE_Descriptor_128(kpts[i], desc.ptr<float>((int)i));
    529                 else
    530                     Get_KAZE_Descriptor_64(kpts[i], desc.ptr<float>((int)i));
    531             }
    532         }
    533     }
    534 private:
    535     void Get_KAZE_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const;
    536     void Get_KAZE_Descriptor_64(const KeyPoint& kpt, float* desc) const;
    537     void Get_KAZE_Upright_Descriptor_128(const KeyPoint& kpt, float* desc) const;
    538     void Get_KAZE_Descriptor_128(const KeyPoint& kpt, float *desc) const;
    539 
    540         std::vector<KeyPoint> * kpts_;
    541         Mat                   * desc_;
    542         std::vector<TEvolution>   * evolution_;
    543         KAZEOptions                 options_;
    544 };
    545 
    546 /* ************************************************************************* */
    547 /**
    548  * @brief This method  computes the set of descriptors through the nonlinear scale space
    549  * @param kpts Vector of keypoints
    550  * @param desc Matrix with the feature descriptors
    551  */
    552 void KAZEFeatures::Feature_Description(std::vector<KeyPoint> &kpts, Mat &desc)
    553 {
    554     for(size_t i = 0; i < kpts.size(); i++)
    555     {
    556         CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast<int>(evolution_.size()));
    557     }
    558 
    559     // Allocate memory for the matrix of descriptors
    560         if (options_.extended == true) {
    561         desc = Mat::zeros((int)kpts.size(), 128, CV_32FC1);
    562     }
    563     else {
    564         desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1);
    565     }
    566 
    567         parallel_for_(Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options_));
    568 }
    569 
    570 /* ************************************************************************* */
    571 /**
    572  * @brief This method computes the main orientation for a given keypoint
    573  * @param kpt Input keypoint
    574  * @note The orientation is computed using a similar approach as described in the
    575  * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006
    576  */
    577 void KAZEFeatures::Compute_Main_Orientation(KeyPoint &kpt, const std::vector<TEvolution>& evolution_, const KAZEOptions& options)
    578 {
    579     int ix = 0, iy = 0, idx = 0, s = 0, level = 0;
    580     float xf = 0.0, yf = 0.0, gweight = 0.0;
    581     vector<float> resX(109), resY(109), Ang(109);
    582 
    583     // Variables for computing the dominant direction
    584     float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0;
    585 
    586     // Get the information from the keypoint
    587     xf = kpt.pt.x;
    588     yf = kpt.pt.y;
    589     level = kpt.class_id;
    590     s = fRound(kpt.size / 2.0f);
    591 
    592     // Calculate derivatives responses for points within radius of 6*scale
    593     for (int i = -6; i <= 6; ++i) {
    594         for (int j = -6; j <= 6; ++j) {
    595             if (i*i + j*j < 36) {
    596                 iy = fRound(yf + j*s);
    597                 ix = fRound(xf + i*s);
    598 
    599                 if (iy >= 0 && iy < options.img_height && ix >= 0 && ix < options.img_width) {
    600                     gweight = gaussian(iy - yf, ix - xf, 2.5f*s);
    601                     resX[idx] = gweight*(*(evolution_[level].Lx.ptr<float>(iy)+ix));
    602                     resY[idx] = gweight*(*(evolution_[level].Ly.ptr<float>(iy)+ix));
    603                 }
    604                 else {
    605                     resX[idx] = 0.0;
    606                     resY[idx] = 0.0;
    607                 }
    608 
    609                 Ang[idx] = getAngle(resX[idx], resY[idx]);
    610                 ++idx;
    611             }
    612         }
    613     }
    614 
    615     // Loop slides pi/3 window around feature point
    616     for (ang1 = 0; ang1 < 2.0f*CV_PI; ang1 += 0.15f) {
    617         ang2 = (ang1 + (float)(CV_PI / 3.0) > (float)(2.0*CV_PI) ? ang1 - (float)(5.0*CV_PI / 3.0) : ang1 + (float)(CV_PI / 3.0));
    618         sumX = sumY = 0.f;
    619 
    620         for (size_t k = 0; k < Ang.size(); ++k) {
    621             // Get angle from the x-axis of the sample point
    622             const float & ang = Ang[k];
    623 
    624             // Determine whether the point is within the window
    625             if (ang1 < ang2 && ang1 < ang && ang < ang2) {
    626                 sumX += resX[k];
    627                 sumY += resY[k];
    628             }
    629             else if (ang2 < ang1 &&
    630                 ((ang > 0 && ang < ang2) || (ang > ang1 && ang < (float)(2.0*CV_PI)))) {
    631                 sumX += resX[k];
    632                 sumY += resY[k];
    633             }
    634         }
    635 
    636         // if the vector produced from this window is longer than all
    637         // previous vectors then this forms the new dominant direction
    638         if (sumX*sumX + sumY*sumY > max) {
    639             // store largest orientation
    640             max = sumX*sumX + sumY*sumY;
    641             kpt.angle = getAngle(sumX, sumY);
    642         }
    643     }
    644 }
    645 
    646 /* ************************************************************************* */
    647 /**
    648  * @brief This method computes the upright descriptor (not rotation invariant) of
    649  * the provided keypoint
    650  * @param kpt Input keypoint
    651  * @param desc Descriptor vector
    652  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
    653  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
    654  * ECCV 2008
    655  */
    656 void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const KeyPoint &kpt, float *desc) const
    657 {
    658     float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
    659     float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
    660     float sample_x = 0.0, sample_y = 0.0;
    661     int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;
    662     int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
    663     float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
    664     int dsize = 0, scale = 0, level = 0;
    665 
    666         std::vector<TEvolution>& evolution = *evolution_;
    667 
    668     // Subregion centers for the 4x4 gaussian weighting
    669     float cx = -0.5f, cy = 0.5f;
    670 
    671     // Set the descriptor size and the sample and pattern sizes
    672     dsize = 64;
    673     sample_step = 5;
    674     pattern_size = 12;
    675 
    676     // Get the information from the keypoint
    677     yf = kpt.pt.y;
    678     xf = kpt.pt.x;
    679     scale = fRound(kpt.size / 2.0f);
    680     level = kpt.class_id;
    681 
    682     i = -8;
    683 
    684     // Calculate descriptor for this interest point
    685     // Area of size 24 s x 24 s
    686     while (i < pattern_size) {
    687         j = -8;
    688         i = i - 4;
    689 
    690         cx += 1.0f;
    691         cy = -0.5f;
    692 
    693         while (j < pattern_size) {
    694 
    695             dx = dy = mdx = mdy = 0.0;
    696             cy += 1.0f;
    697             j = j - 4;
    698 
    699             ky = i + sample_step;
    700             kx = j + sample_step;
    701 
    702             ys = yf + (ky*scale);
    703             xs = xf + (kx*scale);
    704 
    705             for (int k = i; k < i + 9; k++) {
    706                 for (int l = j; l < j + 9; l++) {
    707 
    708                     sample_y = k*scale + yf;
    709                     sample_x = l*scale + xf;
    710 
    711                     //Get the gaussian weighted x and y responses
    712                     gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
    713 
    714                     y1 = (int)(sample_y - 0.5f);
    715                     x1 = (int)(sample_x - 0.5f);
    716 
    717                                         checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);
    718 
    719                     y2 = (int)(sample_y + 0.5f);
    720                     x2 = (int)(sample_x + 0.5f);
    721 
    722                                         checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);
    723 
    724                     fx = sample_x - x1;
    725                     fy = sample_y - y1;
    726 
    727                                         res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
    728                                         res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
    729                                         res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
    730                                         res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
    731                     rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
    732 
    733                                         res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
    734                                         res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
    735                                         res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
    736                                         res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
    737                     ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
    738 
    739                     rx = gauss_s1*rx;
    740                     ry = gauss_s1*ry;
    741 
    742                     // Sum the derivatives to the cumulative descriptor
    743                     dx += rx;
    744                     dy += ry;
    745                     mdx += fabs(rx);
    746                     mdy += fabs(ry);
    747                 }
    748             }
    749 
    750             // Add the values to the descriptor vector
    751             gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
    752 
    753             desc[dcount++] = dx*gauss_s2;
    754             desc[dcount++] = dy*gauss_s2;
    755             desc[dcount++] = mdx*gauss_s2;
    756             desc[dcount++] = mdy*gauss_s2;
    757 
    758             len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
    759 
    760             j += 9;
    761         }
    762 
    763         i += 9;
    764     }
    765 
    766     // convert to unit vector
    767     len = sqrt(len);
    768 
    769     for (i = 0; i < dsize; i++) {
    770         desc[i] /= len;
    771     }
    772 }
    773 
    774 /* ************************************************************************* */
    775 /**
    776  * @brief This method computes the descriptor of the provided keypoint given the
    777  * main orientation of the keypoint
    778  * @param kpt Input keypoint
    779  * @param desc Descriptor vector
    780  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
    781  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
    782  * ECCV 2008
    783  */
    784 void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const KeyPoint &kpt, float *desc) const
    785 {
    786     float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
    787     float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
    788     float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;
    789     float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
    790     int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;
    791     int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
    792     int dsize = 0, scale = 0, level = 0;
    793 
    794         std::vector<TEvolution>& evolution = *evolution_;
    795 
    796     // Subregion centers for the 4x4 gaussian weighting
    797     float cx = -0.5f, cy = 0.5f;
    798 
    799     // Set the descriptor size and the sample and pattern sizes
    800     dsize = 64;
    801     sample_step = 5;
    802     pattern_size = 12;
    803 
    804     // Get the information from the keypoint
    805     yf = kpt.pt.y;
    806     xf = kpt.pt.x;
    807     scale = fRound(kpt.size / 2.0f);
    808     angle = kpt.angle;
    809     level = kpt.class_id;
    810     co = cos(angle);
    811     si = sin(angle);
    812 
    813     i = -8;
    814 
    815     // Calculate descriptor for this interest point
    816     // Area of size 24 s x 24 s
    817     while (i < pattern_size) {
    818 
    819         j = -8;
    820         i = i - 4;
    821 
    822         cx += 1.0f;
    823         cy = -0.5f;
    824 
    825         while (j < pattern_size) {
    826 
    827             dx = dy = mdx = mdy = 0.0;
    828             cy += 1.0f;
    829             j = j - 4;
    830 
    831             ky = i + sample_step;
    832             kx = j + sample_step;
    833 
    834             xs = xf + (-kx*scale*si + ky*scale*co);
    835             ys = yf + (kx*scale*co + ky*scale*si);
    836 
    837             for (int k = i; k < i + 9; ++k) {
    838                 for (int l = j; l < j + 9; ++l) {
    839 
    840                     // Get coords of sample point on the rotated axis
    841                     sample_y = yf + (l*scale*co + k*scale*si);
    842                     sample_x = xf + (-l*scale*si + k*scale*co);
    843 
    844                     // Get the gaussian weighted x and y responses
    845                     gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
    846                     y1 = fRound(sample_y - 0.5f);
    847                     x1 = fRound(sample_x - 0.5f);
    848 
    849                                         checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);
    850 
    851                     y2 = (int)(sample_y + 0.5f);
    852                     x2 = (int)(sample_x + 0.5f);
    853 
    854                                         checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);
    855 
    856                     fx = sample_x - x1;
    857                     fy = sample_y - y1;
    858 
    859                                         res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
    860                                         res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
    861                                         res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
    862                                         res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
    863                     rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
    864 
    865                                         res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
    866                                         res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
    867                                         res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
    868                                         res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
    869                     ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
    870 
    871                     // Get the x and y derivatives on the rotated axis
    872                     rry = gauss_s1*(rx*co + ry*si);
    873                     rrx = gauss_s1*(-rx*si + ry*co);
    874 
    875                     // Sum the derivatives to the cumulative descriptor
    876                     dx += rrx;
    877                     dy += rry;
    878                     mdx += fabs(rrx);
    879                     mdy += fabs(rry);
    880                 }
    881             }
    882 
    883             // Add the values to the descriptor vector
    884             gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
    885             desc[dcount++] = dx*gauss_s2;
    886             desc[dcount++] = dy*gauss_s2;
    887             desc[dcount++] = mdx*gauss_s2;
    888             desc[dcount++] = mdy*gauss_s2;
    889             len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
    890             j += 9;
    891         }
    892         i += 9;
    893     }
    894 
    895     // convert to unit vector
    896     len = sqrt(len);
    897 
    898     for (i = 0; i < dsize; i++) {
    899         desc[i] /= len;
    900     }
    901 }
    902 
    903 /* ************************************************************************* */
    904 /**
    905  * @brief This method computes the extended upright descriptor (not rotation invariant) of
    906  * the provided keypoint
    907  * @param kpt Input keypoint
    908  * @param desc Descriptor vector
    909  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 128. The descriptor is inspired
    910  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
    911  * ECCV 2008
    912  */
    913 void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const KeyPoint &kpt, float *desc) const
    914 {
    915     float gauss_s1 = 0.0, gauss_s2 = 0.0;
    916     float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
    917     float sample_x = 0.0, sample_y = 0.0;
    918     int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;
    919     int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
    920     float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
    921     float dxp = 0.0, dyp = 0.0, mdxp = 0.0, mdyp = 0.0;
    922     float dxn = 0.0, dyn = 0.0, mdxn = 0.0, mdyn = 0.0;
    923     int dsize = 0, scale = 0, level = 0;
    924 
    925     // Subregion centers for the 4x4 gaussian weighting
    926     float cx = -0.5f, cy = 0.5f;
    927 
    928         std::vector<TEvolution>& evolution = *evolution_;
    929 
    930     // Set the descriptor size and the sample and pattern sizes
    931     dsize = 128;
    932     sample_step = 5;
    933     pattern_size = 12;
    934 
    935     // Get the information from the keypoint
    936     yf = kpt.pt.y;
    937     xf = kpt.pt.x;
    938     scale = fRound(kpt.size / 2.0f);
    939     level = kpt.class_id;
    940 
    941     i = -8;
    942 
    943     // Calculate descriptor for this interest point
    944     // Area of size 24 s x 24 s
    945     while (i < pattern_size) {
    946 
    947         j = -8;
    948         i = i - 4;
    949 
    950         cx += 1.0f;
    951         cy = -0.5f;
    952 
    953         while (j < pattern_size) {
    954 
    955             dxp = dxn = mdxp = mdxn = 0.0;
    956             dyp = dyn = mdyp = mdyn = 0.0;
    957 
    958             cy += 1.0f;
    959             j = j - 4;
    960 
    961             ky = i + sample_step;
    962             kx = j + sample_step;
    963 
    964             ys = yf + (ky*scale);
    965             xs = xf + (kx*scale);
    966 
    967             for (int k = i; k < i + 9; k++) {
    968                 for (int l = j; l < j + 9; l++) {
    969 
    970                     sample_y = k*scale + yf;
    971                     sample_x = l*scale + xf;
    972 
    973                     //Get the gaussian weighted x and y responses
    974                     gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
    975 
    976                     y1 = (int)(sample_y - 0.5f);
    977                     x1 = (int)(sample_x - 0.5f);
    978 
    979                                         checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);
    980 
    981                     y2 = (int)(sample_y + 0.5f);
    982                     x2 = (int)(sample_x + 0.5f);
    983 
    984                                         checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);
    985 
    986                     fx = sample_x - x1;
    987                     fy = sample_y - y1;
    988 
    989                                         res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
    990                                         res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
    991                                         res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
    992                                         res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
    993                     rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
    994 
    995                                         res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
    996                                         res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
    997                                         res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
    998                                         res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
    999                     ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
   1000 
   1001                     rx = gauss_s1*rx;
   1002                     ry = gauss_s1*ry;
   1003 
   1004                     // Sum the derivatives to the cumulative descriptor
   1005                     if (ry >= 0.0) {
   1006                         dxp += rx;
   1007                         mdxp += fabs(rx);
   1008                     }
   1009                     else {
   1010                         dxn += rx;
   1011                         mdxn += fabs(rx);
   1012                     }
   1013 
   1014                     if (rx >= 0.0) {
   1015                         dyp += ry;
   1016                         mdyp += fabs(ry);
   1017                     }
   1018                     else {
   1019                         dyn += ry;
   1020                         mdyn += fabs(ry);
   1021                     }
   1022                 }
   1023             }
   1024 
   1025             // Add the values to the descriptor vector
   1026             gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
   1027 
   1028             desc[dcount++] = dxp*gauss_s2;
   1029             desc[dcount++] = dxn*gauss_s2;
   1030             desc[dcount++] = mdxp*gauss_s2;
   1031             desc[dcount++] = mdxn*gauss_s2;
   1032             desc[dcount++] = dyp*gauss_s2;
   1033             desc[dcount++] = dyn*gauss_s2;
   1034             desc[dcount++] = mdyp*gauss_s2;
   1035             desc[dcount++] = mdyn*gauss_s2;
   1036 
   1037             // Store the current length^2 of the vector
   1038             len += (dxp*dxp + dxn*dxn + mdxp*mdxp + mdxn*mdxn +
   1039                 dyp*dyp + dyn*dyn + mdyp*mdyp + mdyn*mdyn)*gauss_s2*gauss_s2;
   1040 
   1041             j += 9;
   1042         }
   1043 
   1044         i += 9;
   1045     }
   1046 
   1047     // convert to unit vector
   1048     len = sqrt(len);
   1049 
   1050     for (i = 0; i < dsize; i++) {
   1051         desc[i] /= len;
   1052     }
   1053 }
   1054 
   1055 /* ************************************************************************* */
   1056 /**
   1057  * @brief This method computes the extended G-SURF descriptor of the provided keypoint
   1058  * given the main orientation of the keypoint
   1059  * @param kpt Input keypoint
   1060  * @param desc Descriptor vector
   1061  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 128. The descriptor is inspired
   1062  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
   1063  * ECCV 2008
   1064  */
   1065 void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const KeyPoint &kpt, float *desc) const
   1066 {
   1067     float gauss_s1 = 0.0, gauss_s2 = 0.0;
   1068     float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
   1069     float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;
   1070     float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
   1071     float dxp = 0.0, dyp = 0.0, mdxp = 0.0, mdyp = 0.0;
   1072     float dxn = 0.0, dyn = 0.0, mdxn = 0.0, mdyn = 0.0;
   1073     int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;
   1074     int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
   1075     int dsize = 0, scale = 0, level = 0;
   1076 
   1077         std::vector<TEvolution>& evolution = *evolution_;
   1078 
   1079     // Subregion centers for the 4x4 gaussian weighting
   1080     float cx = -0.5f, cy = 0.5f;
   1081 
   1082     // Set the descriptor size and the sample and pattern sizes
   1083     dsize = 128;
   1084     sample_step = 5;
   1085     pattern_size = 12;
   1086 
   1087     // Get the information from the keypoint
   1088     yf = kpt.pt.y;
   1089     xf = kpt.pt.x;
   1090     scale = fRound(kpt.size / 2.0f);
   1091     angle = kpt.angle;
   1092     level = kpt.class_id;
   1093     co = cos(angle);
   1094     si = sin(angle);
   1095 
   1096     i = -8;
   1097 
   1098     // Calculate descriptor for this interest point
   1099     // Area of size 24 s x 24 s
   1100     while (i < pattern_size) {
   1101 
   1102         j = -8;
   1103         i = i - 4;
   1104 
   1105         cx += 1.0f;
   1106         cy = -0.5f;
   1107 
   1108         while (j < pattern_size) {
   1109 
   1110             dxp = dxn = mdxp = mdxn = 0.0;
   1111             dyp = dyn = mdyp = mdyn = 0.0;
   1112 
   1113             cy += 1.0f;
   1114             j = j - 4;
   1115 
   1116             ky = i + sample_step;
   1117             kx = j + sample_step;
   1118 
   1119             xs = xf + (-kx*scale*si + ky*scale*co);
   1120             ys = yf + (kx*scale*co + ky*scale*si);
   1121 
   1122             for (int k = i; k < i + 9; ++k) {
   1123                 for (int l = j; l < j + 9; ++l) {
   1124 
   1125                     // Get coords of sample point on the rotated axis
   1126                     sample_y = yf + (l*scale*co + k*scale*si);
   1127                     sample_x = xf + (-l*scale*si + k*scale*co);
   1128 
   1129                     // Get the gaussian weighted x and y responses
   1130                     gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
   1131 
   1132                     y1 = fRound(sample_y - 0.5f);
   1133                     x1 = fRound(sample_x - 0.5f);
   1134 
   1135                                         checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);
   1136 
   1137                     y2 = (int)(sample_y + 0.5f);
   1138                     x2 = (int)(sample_x + 0.5f);
   1139 
   1140                                         checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);
   1141 
   1142                     fx = sample_x - x1;
   1143                     fy = sample_y - y1;
   1144 
   1145                                         res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
   1146                                         res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
   1147                                         res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
   1148                                         res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
   1149                     rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
   1150 
   1151                                         res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
   1152                                         res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
   1153                                         res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
   1154                                         res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
   1155                     ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
   1156 
   1157                     // Get the x and y derivatives on the rotated axis
   1158                     rry = gauss_s1*(rx*co + ry*si);
   1159                     rrx = gauss_s1*(-rx*si + ry*co);
   1160 
   1161                     // Sum the derivatives to the cumulative descriptor
   1162                     if (rry >= 0.0) {
   1163                         dxp += rrx;
   1164                         mdxp += fabs(rrx);
   1165                     }
   1166                     else {
   1167                         dxn += rrx;
   1168                         mdxn += fabs(rrx);
   1169                     }
   1170 
   1171                     if (rrx >= 0.0) {
   1172                         dyp += rry;
   1173                         mdyp += fabs(rry);
   1174                     }
   1175                     else {
   1176                         dyn += rry;
   1177                         mdyn += fabs(rry);
   1178                     }
   1179                 }
   1180             }
   1181 
   1182             // Add the values to the descriptor vector
   1183             gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
   1184 
   1185             desc[dcount++] = dxp*gauss_s2;
   1186             desc[dcount++] = dxn*gauss_s2;
   1187             desc[dcount++] = mdxp*gauss_s2;
   1188             desc[dcount++] = mdxn*gauss_s2;
   1189             desc[dcount++] = dyp*gauss_s2;
   1190             desc[dcount++] = dyn*gauss_s2;
   1191             desc[dcount++] = mdyp*gauss_s2;
   1192             desc[dcount++] = mdyn*gauss_s2;
   1193 
   1194             // Store the current length^2 of the vector
   1195             len += (dxp*dxp + dxn*dxn + mdxp*mdxp + mdxn*mdxn +
   1196                 dyp*dyp + dyn*dyn + mdyp*mdyp + mdyn*mdyn)*gauss_s2*gauss_s2;
   1197 
   1198             j += 9;
   1199         }
   1200 
   1201         i += 9;
   1202     }
   1203 
   1204     // convert to unit vector
   1205     len = sqrt(len);
   1206 
   1207     for (i = 0; i < dsize; i++) {
   1208         desc[i] /= len;
   1209     }
   1210 }
   1211 
   1212 }
   1213