Home | History | Annotate | Download | only in src
      1 /*********************************************************************
      2  * Software License Agreement (BSD License)
      3  *
      4  *  Copyright (C) 2011  The Autonomous Systems Lab (ASL), ETH Zurich,
      5  *                Stefan Leutenegger, Simon Lynen and Margarita Chli.
      6  *  Copyright (c) 2009, Willow Garage, Inc.
      7  *  All rights reserved.
      8  *
      9  *  Redistribution and use in source and binary forms, with or without
     10  *  modification, are permitted provided that the following conditions
     11  *  are met:
     12  *
     13  *   * Redistributions of source code must retain the above copyright
     14  *     notice, this list of conditions and the following disclaimer.
     15  *   * Redistributions in binary form must reproduce the above
     16  *     copyright notice, this list of conditions and the following
     17  *     disclaimer in the documentation and/or other materials provided
     18  *     with the distribution.
     19  *   * Neither the name of the Willow Garage nor the names of its
     20  *     contributors may be used to endorse or promote products derived
     21  *     from this software without specific prior written permission.
     22  *
     23  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     26  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     27  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     28  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     29  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     30  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     31  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     33  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     34  *  POSSIBILITY OF SUCH DAMAGE.
     35  *********************************************************************/
     36 
     37 /*
     38  BRISK - Binary Robust Invariant Scalable Keypoints
     39  Reference implementation of
     40  [1] Stefan Leutenegger,Margarita Chli and Roland Siegwart, BRISK:
     41  Binary Robust Invariant Scalable Keypoints, in Proceedings of
     42  the IEEE International Conference on Computer Vision (ICCV2011).
     43  */
     44 
     45 #include "precomp.hpp"
     46 #include <fstream>
     47 #include <stdlib.h>
     48 
     49 #include "agast_score.hpp"
     50 
     51 namespace cv
     52 {
     53 
     54 class BRISK_Impl : public BRISK
     55 {
     56 public:
     57     explicit BRISK_Impl(int thresh=30, int octaves=3, float patternScale=1.0f);
     58     // custom setup
     59     explicit BRISK_Impl(const std::vector<float> &radiusList, const std::vector<int> &numberList,
     60         float dMax=5.85f, float dMin=8.2f, const std::vector<int> indexChange=std::vector<int>());
     61 
     62     virtual ~BRISK_Impl();
     63 
     64     int descriptorSize() const
     65     {
     66         return strings_;
     67     }
     68 
     69     int descriptorType() const
     70     {
     71         return CV_8U;
     72     }
     73 
     74     int defaultNorm() const
     75     {
     76         return NORM_HAMMING;
     77     }
     78 
     79     // call this to generate the kernel:
     80     // circle of radius r (pixels), with n points;
     81     // short pairings with dMax, long pairings with dMin
     82     void generateKernel(const std::vector<float> &radiusList,
     83         const std::vector<int> &numberList, float dMax=5.85f, float dMin=8.2f,
     84         const std::vector<int> &indexChange=std::vector<int>());
     85 
     86     void detectAndCompute( InputArray image, InputArray mask,
     87                      CV_OUT std::vector<KeyPoint>& keypoints,
     88                      OutputArray descriptors,
     89                      bool useProvidedKeypoints );
     90 
     91 protected:
     92 
     93     void computeKeypointsNoOrientation(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints) const;
     94     void computeDescriptorsAndOrOrientation(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints,
     95                                        OutputArray descriptors, bool doDescriptors, bool doOrientation,
     96                                        bool useProvidedKeypoints) const;
     97 
     98     // Feature parameters
     99     CV_PROP_RW int threshold;
    100     CV_PROP_RW int octaves;
    101 
    102     // some helper structures for the Brisk pattern representation
    103     struct BriskPatternPoint{
    104         float x;         // x coordinate relative to center
    105         float y;         // x coordinate relative to center
    106         float sigma;     // Gaussian smoothing sigma
    107     };
    108     struct BriskShortPair{
    109         unsigned int i;  // index of the first pattern point
    110         unsigned int j;  // index of other pattern point
    111     };
    112     struct BriskLongPair{
    113         unsigned int i;  // index of the first pattern point
    114         unsigned int j;  // index of other pattern point
    115         int weighted_dx; // 1024.0/dx
    116         int weighted_dy; // 1024.0/dy
    117     };
    118     inline int smoothedIntensity(const cv::Mat& image,
    119                 const cv::Mat& integral,const float key_x,
    120                 const float key_y, const unsigned int scale,
    121                 const unsigned int rot, const unsigned int point) const;
    122     // pattern properties
    123     BriskPatternPoint* patternPoints_;     //[i][rotation][scale]
    124     unsigned int points_;                 // total number of collocation points
    125     float* scaleList_;                     // lists the scaling per scale index [scale]
    126     unsigned int* sizeList_;             // lists the total pattern size per scale index [scale]
    127     static const unsigned int scales_;    // scales discretization
    128     static const float scalerange_;     // span of sizes 40->4 Octaves - else, this needs to be adjusted...
    129     static const unsigned int n_rot_;    // discretization of the rotation look-up
    130 
    131     // pairs
    132     int strings_;                        // number of uchars the descriptor consists of
    133     float dMax_;                         // short pair maximum distance
    134     float dMin_;                         // long pair maximum distance
    135     BriskShortPair* shortPairs_;         // d<_dMax
    136     BriskLongPair* longPairs_;             // d>_dMin
    137     unsigned int noShortPairs_;         // number of shortParis
    138     unsigned int noLongPairs_;             // number of longParis
    139 
    140     // general
    141     static const float basicSize_;
    142 };
    143 
    144 
    145 // a layer in the Brisk detector pyramid
    146 class CV_EXPORTS BriskLayer
    147 {
    148 public:
    149   // constructor arguments
    150   struct CV_EXPORTS CommonParams
    151   {
    152     static const int HALFSAMPLE = 0;
    153     static const int TWOTHIRDSAMPLE = 1;
    154   };
    155   // construct a base layer
    156   BriskLayer(const cv::Mat& img, float scale = 1.0f, float offset = 0.0f);
    157   // derive a layer
    158   BriskLayer(const BriskLayer& layer, int mode);
    159 
    160   // Agast without non-max suppression
    161   void
    162   getAgastPoints(int threshold, std::vector<cv::KeyPoint>& keypoints);
    163 
    164   // get scores - attention, this is in layer coordinates, not scale=1 coordinates!
    165   inline int
    166   getAgastScore(int x, int y, int threshold) const;
    167   inline int
    168   getAgastScore_5_8(int x, int y, int threshold) const;
    169   inline int
    170   getAgastScore(float xf, float yf, int threshold, float scale = 1.0f) const;
    171 
    172   // accessors
    173   inline const cv::Mat&
    174   img() const
    175   {
    176     return img_;
    177   }
    178   inline const cv::Mat&
    179   scores() const
    180   {
    181     return scores_;
    182   }
    183   inline float
    184   scale() const
    185   {
    186     return scale_;
    187   }
    188   inline float
    189   offset() const
    190   {
    191     return offset_;
    192   }
    193 
    194   // half sampling
    195   static inline void
    196   halfsample(const cv::Mat& srcimg, cv::Mat& dstimg);
    197   // two third sampling
    198   static inline void
    199   twothirdsample(const cv::Mat& srcimg, cv::Mat& dstimg);
    200 
    201 private:
    202   // access gray values (smoothed/interpolated)
    203   inline int
    204   value(const cv::Mat& mat, float xf, float yf, float scale) const;
    205   // the image
    206   cv::Mat img_;
    207   // its Agast scores
    208   cv::Mat_<uchar> scores_;
    209   // coordinate transformation
    210   float scale_;
    211   float offset_;
    212   // agast
    213   cv::Ptr<cv::AgastFeatureDetector> oast_9_16_;
    214   int pixel_5_8_[25];
    215   int pixel_9_16_[25];
    216 };
    217 
    218 class CV_EXPORTS BriskScaleSpace
    219 {
    220 public:
    221   // construct telling the octaves number:
    222   BriskScaleSpace(int _octaves = 3);
    223   ~BriskScaleSpace();
    224 
    225   // construct the image pyramids
    226   void
    227   constructPyramid(const cv::Mat& image);
    228 
    229   // get Keypoints
    230   void
    231   getKeypoints(const int _threshold, std::vector<cv::KeyPoint>& keypoints);
    232 
    233 protected:
    234   // nonmax suppression:
    235   inline bool
    236   isMax2D(const int layer, const int x_layer, const int y_layer);
    237   // 1D (scale axis) refinement:
    238   inline float
    239   refine1D(const float s_05, const float s0, const float s05, float& max) const; // around octave
    240   inline float
    241   refine1D_1(const float s_05, const float s0, const float s05, float& max) const; // around intra
    242   inline float
    243   refine1D_2(const float s_05, const float s0, const float s05, float& max) const; // around octave 0 only
    244   // 2D maximum refinement:
    245   inline float
    246   subpixel2D(const int s_0_0, const int s_0_1, const int s_0_2, const int s_1_0, const int s_1_1, const int s_1_2,
    247              const int s_2_0, const int s_2_1, const int s_2_2, float& delta_x, float& delta_y) const;
    248   // 3D maximum refinement centered around (x_layer,y_layer)
    249   inline float
    250   refine3D(const int layer, const int x_layer, const int y_layer, float& x, float& y, float& scale, bool& ismax) const;
    251 
    252   // interpolated score access with recalculation when needed:
    253   inline int
    254   getScoreAbove(const int layer, const int x_layer, const int y_layer) const;
    255   inline int
    256   getScoreBelow(const int layer, const int x_layer, const int y_layer) const;
    257 
    258   // return the maximum of score patches above or below
    259   inline float
    260   getScoreMaxAbove(const int layer, const int x_layer, const int y_layer, const int threshold, bool& ismax,
    261                    float& dx, float& dy) const;
    262   inline float
    263   getScoreMaxBelow(const int layer, const int x_layer, const int y_layer, const int threshold, bool& ismax,
    264                    float& dx, float& dy) const;
    265 
    266   // the image pyramids:
    267   int layers_;
    268   std::vector<BriskLayer> pyramid_;
    269 
    270   // some constant parameters:
    271   static const float safetyFactor_;
    272   static const float basicSize_;
    273 };
    274 
    275 const float BRISK_Impl::basicSize_ = 12.0f;
    276 const unsigned int BRISK_Impl::scales_ = 64;
    277 const float BRISK_Impl::scalerange_ = 30.f; // 40->4 Octaves - else, this needs to be adjusted...
    278 const unsigned int BRISK_Impl::n_rot_ = 1024; // discretization of the rotation look-up
    279 
    280 const float BriskScaleSpace::safetyFactor_ = 1.0f;
    281 const float BriskScaleSpace::basicSize_ = 12.0f;
    282 
    283 // constructors
    284 BRISK_Impl::BRISK_Impl(int thresh, int octaves_in, float patternScale)
    285 {
    286   threshold = thresh;
    287   octaves = octaves_in;
    288 
    289   std::vector<float> rList;
    290   std::vector<int> nList;
    291 
    292   // this is the standard pattern found to be suitable also
    293   rList.resize(5);
    294   nList.resize(5);
    295   const double f = 0.85 * patternScale;
    296 
    297   rList[0] = (float)(f * 0.);
    298   rList[1] = (float)(f * 2.9);
    299   rList[2] = (float)(f * 4.9);
    300   rList[3] = (float)(f * 7.4);
    301   rList[4] = (float)(f * 10.8);
    302 
    303   nList[0] = 1;
    304   nList[1] = 10;
    305   nList[2] = 14;
    306   nList[3] = 15;
    307   nList[4] = 20;
    308 
    309   generateKernel(rList, nList, (float)(5.85 * patternScale), (float)(8.2 * patternScale));
    310 }
    311 
    312 BRISK_Impl::BRISK_Impl(const std::vector<float> &radiusList,
    313                        const std::vector<int> &numberList,
    314                        float dMax, float dMin,
    315                        const std::vector<int> indexChange)
    316 {
    317   generateKernel(radiusList, numberList, dMax, dMin, indexChange);
    318   threshold = 20;
    319   octaves = 3;
    320 }
    321 
    322 void
    323 BRISK_Impl::generateKernel(const std::vector<float> &radiusList,
    324                            const std::vector<int> &numberList,
    325                            float dMax, float dMin,
    326                            const std::vector<int>& _indexChange)
    327 {
    328   std::vector<int> indexChange = _indexChange;
    329   dMax_ = dMax;
    330   dMin_ = dMin;
    331 
    332   // get the total number of points
    333   const int rings = (int)radiusList.size();
    334   CV_Assert(radiusList.size() != 0 && radiusList.size() == numberList.size());
    335   points_ = 0; // remember the total number of points
    336   for (int ring = 0; ring < rings; ring++)
    337   {
    338     points_ += numberList[ring];
    339   }
    340   // set up the patterns
    341   patternPoints_ = new BriskPatternPoint[points_ * scales_ * n_rot_];
    342   BriskPatternPoint* patternIterator = patternPoints_;
    343 
    344   // define the scale discretization:
    345   static const float lb_scale = (float)(std::log(scalerange_) / std::log(2.0));
    346   static const float lb_scale_step = lb_scale / (scales_);
    347 
    348   scaleList_ = new float[scales_];
    349   sizeList_ = new unsigned int[scales_];
    350 
    351   const float sigma_scale = 1.3f;
    352 
    353   for (unsigned int scale = 0; scale < scales_; ++scale)
    354   {
    355     scaleList_[scale] = (float)std::pow((double) 2.0, (double) (scale * lb_scale_step));
    356     sizeList_[scale] = 0;
    357 
    358     // generate the pattern points look-up
    359     double alpha, theta;
    360     for (size_t rot = 0; rot < n_rot_; ++rot)
    361     {
    362       theta = double(rot) * 2 * CV_PI / double(n_rot_); // this is the rotation of the feature
    363       for (int ring = 0; ring < rings; ++ring)
    364       {
    365         for (int num = 0; num < numberList[ring]; ++num)
    366         {
    367           // the actual coordinates on the circle
    368           alpha = (double(num)) * 2 * CV_PI / double(numberList[ring]);
    369           patternIterator->x = (float)(scaleList_[scale] * radiusList[ring] * cos(alpha + theta)); // feature rotation plus angle of the point
    370           patternIterator->y = (float)(scaleList_[scale] * radiusList[ring] * sin(alpha + theta));
    371           // and the gaussian kernel sigma
    372           if (ring == 0)
    373           {
    374             patternIterator->sigma = sigma_scale * scaleList_[scale] * 0.5f;
    375           }
    376           else
    377           {
    378             patternIterator->sigma = (float)(sigma_scale * scaleList_[scale] * (double(radiusList[ring]))
    379                                      * sin(CV_PI / numberList[ring]));
    380           }
    381           // adapt the sizeList if necessary
    382           const unsigned int size = cvCeil(((scaleList_[scale] * radiusList[ring]) + patternIterator->sigma)) + 1;
    383           if (sizeList_[scale] < size)
    384           {
    385             sizeList_[scale] = size;
    386           }
    387 
    388           // increment the iterator
    389           ++patternIterator;
    390         }
    391       }
    392     }
    393   }
    394 
    395   // now also generate pairings
    396   shortPairs_ = new BriskShortPair[points_ * (points_ - 1) / 2];
    397   longPairs_ = new BriskLongPair[points_ * (points_ - 1) / 2];
    398   noShortPairs_ = 0;
    399   noLongPairs_ = 0;
    400 
    401   // fill indexChange with 0..n if empty
    402   unsigned int indSize = (unsigned int)indexChange.size();
    403   if (indSize == 0)
    404   {
    405     indexChange.resize(points_ * (points_ - 1) / 2);
    406     indSize = (unsigned int)indexChange.size();
    407 
    408     for (unsigned int i = 0; i < indSize; i++)
    409       indexChange[i] = i;
    410   }
    411   const float dMin_sq = dMin_ * dMin_;
    412   const float dMax_sq = dMax_ * dMax_;
    413   for (unsigned int i = 1; i < points_; i++)
    414   {
    415     for (unsigned int j = 0; j < i; j++)
    416     { //(find all the pairs)
    417       // point pair distance:
    418       const float dx = patternPoints_[j].x - patternPoints_[i].x;
    419       const float dy = patternPoints_[j].y - patternPoints_[i].y;
    420       const float norm_sq = (dx * dx + dy * dy);
    421       if (norm_sq > dMin_sq)
    422       {
    423         // save to long pairs
    424         BriskLongPair& longPair = longPairs_[noLongPairs_];
    425         longPair.weighted_dx = int((dx / (norm_sq)) * 2048.0 + 0.5);
    426         longPair.weighted_dy = int((dy / (norm_sq)) * 2048.0 + 0.5);
    427         longPair.i = i;
    428         longPair.j = j;
    429         ++noLongPairs_;
    430       }
    431       else if (norm_sq < dMax_sq)
    432       {
    433         // save to short pairs
    434         CV_Assert(noShortPairs_ < indSize);
    435         // make sure the user passes something sensible
    436         BriskShortPair& shortPair = shortPairs_[indexChange[noShortPairs_]];
    437         shortPair.j = j;
    438         shortPair.i = i;
    439         ++noShortPairs_;
    440       }
    441     }
    442   }
    443 
    444   // no bits:
    445   strings_ = (int) ceil((float(noShortPairs_)) / 128.0) * 4 * 4;
    446 }
    447 
    448 // simple alternative:
    449 inline int
    450 BRISK_Impl::smoothedIntensity(const cv::Mat& image, const cv::Mat& integral, const float key_x,
    451                                             const float key_y, const unsigned int scale, const unsigned int rot,
    452                                             const unsigned int point) const
    453 {
    454 
    455   // get the float position
    456   const BriskPatternPoint& briskPoint = patternPoints_[scale * n_rot_ * points_ + rot * points_ + point];
    457   const float xf = briskPoint.x + key_x;
    458   const float yf = briskPoint.y + key_y;
    459   const int x = int(xf);
    460   const int y = int(yf);
    461   const int& imagecols = image.cols;
    462 
    463   // get the sigma:
    464   const float sigma_half = briskPoint.sigma;
    465   const float area = 4.0f * sigma_half * sigma_half;
    466 
    467   // calculate output:
    468   int ret_val;
    469   if (sigma_half < 0.5)
    470   {
    471     //interpolation multipliers:
    472     const int r_x = (int)((xf - x) * 1024);
    473     const int r_y = (int)((yf - y) * 1024);
    474     const int r_x_1 = (1024 - r_x);
    475     const int r_y_1 = (1024 - r_y);
    476     const uchar* ptr = &image.at<uchar>(y, x);
    477     size_t step = image.step;
    478     // just interpolate:
    479     ret_val = r_x_1 * r_y_1 * ptr[0] + r_x * r_y_1 * ptr[1] +
    480               r_x * r_y * ptr[step] + r_x_1 * r_y * ptr[step+1];
    481     return (ret_val + 512) / 1024;
    482   }
    483 
    484   // this is the standard case (simple, not speed optimized yet):
    485 
    486   // scaling:
    487   const int scaling = (int)(4194304.0 / area);
    488   const int scaling2 = int(float(scaling) * area / 1024.0);
    489 
    490   // the integral image is larger:
    491   const int integralcols = imagecols + 1;
    492 
    493   // calculate borders
    494   const float x_1 = xf - sigma_half;
    495   const float x1 = xf + sigma_half;
    496   const float y_1 = yf - sigma_half;
    497   const float y1 = yf + sigma_half;
    498 
    499   const int x_left = int(x_1 + 0.5);
    500   const int y_top = int(y_1 + 0.5);
    501   const int x_right = int(x1 + 0.5);
    502   const int y_bottom = int(y1 + 0.5);
    503 
    504   // overlap area - multiplication factors:
    505   const float r_x_1 = float(x_left) - x_1 + 0.5f;
    506   const float r_y_1 = float(y_top) - y_1 + 0.5f;
    507   const float r_x1 = x1 - float(x_right) + 0.5f;
    508   const float r_y1 = y1 - float(y_bottom) + 0.5f;
    509   const int dx = x_right - x_left - 1;
    510   const int dy = y_bottom - y_top - 1;
    511   const int A = (int)((r_x_1 * r_y_1) * scaling);
    512   const int B = (int)((r_x1 * r_y_1) * scaling);
    513   const int C = (int)((r_x1 * r_y1) * scaling);
    514   const int D = (int)((r_x_1 * r_y1) * scaling);
    515   const int r_x_1_i = (int)(r_x_1 * scaling);
    516   const int r_y_1_i = (int)(r_y_1 * scaling);
    517   const int r_x1_i = (int)(r_x1 * scaling);
    518   const int r_y1_i = (int)(r_y1 * scaling);
    519 
    520   if (dx + dy > 2)
    521   {
    522     // now the calculation:
    523     const uchar* ptr = image.ptr() + x_left + imagecols * y_top;
    524     // first the corners:
    525     ret_val = A * int(*ptr);
    526     ptr += dx + 1;
    527     ret_val += B * int(*ptr);
    528     ptr += dy * imagecols + 1;
    529     ret_val += C * int(*ptr);
    530     ptr -= dx + 1;
    531     ret_val += D * int(*ptr);
    532 
    533     // next the edges:
    534     const int* ptr_integral = integral.ptr<int>() + x_left + integralcols * y_top + 1;
    535     // find a simple path through the different surface corners
    536     const int tmp1 = (*ptr_integral);
    537     ptr_integral += dx;
    538     const int tmp2 = (*ptr_integral);
    539     ptr_integral += integralcols;
    540     const int tmp3 = (*ptr_integral);
    541     ptr_integral++;
    542     const int tmp4 = (*ptr_integral);
    543     ptr_integral += dy * integralcols;
    544     const int tmp5 = (*ptr_integral);
    545     ptr_integral--;
    546     const int tmp6 = (*ptr_integral);
    547     ptr_integral += integralcols;
    548     const int tmp7 = (*ptr_integral);
    549     ptr_integral -= dx;
    550     const int tmp8 = (*ptr_integral);
    551     ptr_integral -= integralcols;
    552     const int tmp9 = (*ptr_integral);
    553     ptr_integral--;
    554     const int tmp10 = (*ptr_integral);
    555     ptr_integral -= dy * integralcols;
    556     const int tmp11 = (*ptr_integral);
    557     ptr_integral++;
    558     const int tmp12 = (*ptr_integral);
    559 
    560     // assign the weighted surface integrals:
    561     const int upper = (tmp3 - tmp2 + tmp1 - tmp12) * r_y_1_i;
    562     const int middle = (tmp6 - tmp3 + tmp12 - tmp9) * scaling;
    563     const int left = (tmp9 - tmp12 + tmp11 - tmp10) * r_x_1_i;
    564     const int right = (tmp5 - tmp4 + tmp3 - tmp6) * r_x1_i;
    565     const int bottom = (tmp7 - tmp6 + tmp9 - tmp8) * r_y1_i;
    566 
    567     return (ret_val + upper + middle + left + right + bottom + scaling2 / 2) / scaling2;
    568   }
    569 
    570   // now the calculation:
    571   const uchar* ptr = image.ptr() + x_left + imagecols * y_top;
    572   // first row:
    573   ret_val = A * int(*ptr);
    574   ptr++;
    575   const uchar* end1 = ptr + dx;
    576   for (; ptr < end1; ptr++)
    577   {
    578     ret_val += r_y_1_i * int(*ptr);
    579   }
    580   ret_val += B * int(*ptr);
    581   // middle ones:
    582   ptr += imagecols - dx - 1;
    583   const uchar* end_j = ptr + dy * imagecols;
    584   for (; ptr < end_j; ptr += imagecols - dx - 1)
    585   {
    586     ret_val += r_x_1_i * int(*ptr);
    587     ptr++;
    588     const uchar* end2 = ptr + dx;
    589     for (; ptr < end2; ptr++)
    590     {
    591       ret_val += int(*ptr) * scaling;
    592     }
    593     ret_val += r_x1_i * int(*ptr);
    594   }
    595   // last row:
    596   ret_val += D * int(*ptr);
    597   ptr++;
    598   const uchar* end3 = ptr + dx;
    599   for (; ptr < end3; ptr++)
    600   {
    601     ret_val += r_y1_i * int(*ptr);
    602   }
    603   ret_val += C * int(*ptr);
    604 
    605   return (ret_val + scaling2 / 2) / scaling2;
    606 }
    607 
    608 inline bool
    609 RoiPredicate(const float minX, const float minY, const float maxX, const float maxY, const KeyPoint& keyPt)
    610 {
    611   const Point2f& pt = keyPt.pt;
    612   return (pt.x < minX) || (pt.x >= maxX) || (pt.y < minY) || (pt.y >= maxY);
    613 }
    614 
    615 // computes the descriptor
    616 void
    617 BRISK_Impl::detectAndCompute( InputArray _image, InputArray _mask, std::vector<KeyPoint>& keypoints,
    618                               OutputArray _descriptors, bool useProvidedKeypoints)
    619 {
    620   bool doOrientation=true;
    621 
    622   // If the user specified cv::noArray(), this will yield false. Otherwise it will return true.
    623   bool doDescriptors = _descriptors.needed();
    624 
    625   computeDescriptorsAndOrOrientation(_image, _mask, keypoints, _descriptors, doDescriptors, doOrientation,
    626                                        useProvidedKeypoints);
    627 }
    628 
    629 void
    630 BRISK_Impl::computeDescriptorsAndOrOrientation(InputArray _image, InputArray _mask, std::vector<KeyPoint>& keypoints,
    631                                      OutputArray _descriptors, bool doDescriptors, bool doOrientation,
    632                                      bool useProvidedKeypoints) const
    633 {
    634   Mat image = _image.getMat(), mask = _mask.getMat();
    635   if( image.type() != CV_8UC1 )
    636       cvtColor(image, image, COLOR_BGR2GRAY);
    637 
    638   if (!useProvidedKeypoints)
    639   {
    640     doOrientation = true;
    641     computeKeypointsNoOrientation(_image, _mask, keypoints);
    642   }
    643 
    644   //Remove keypoints very close to the border
    645   size_t ksize = keypoints.size();
    646   std::vector<int> kscales; // remember the scale per keypoint
    647   kscales.resize(ksize);
    648   static const float log2 = 0.693147180559945f;
    649   static const float lb_scalerange = (float)(std::log(scalerange_) / (log2));
    650   std::vector<cv::KeyPoint>::iterator beginning = keypoints.begin();
    651   std::vector<int>::iterator beginningkscales = kscales.begin();
    652   static const float basicSize06 = basicSize_ * 0.6f;
    653   for (size_t k = 0; k < ksize; k++)
    654   {
    655     unsigned int scale;
    656       scale = std::max((int) (scales_ / lb_scalerange * (std::log(keypoints[k].size / (basicSize06)) / log2) + 0.5), 0);
    657       // saturate
    658       if (scale >= scales_)
    659         scale = scales_ - 1;
    660       kscales[k] = scale;
    661     const int border = sizeList_[scale];
    662     const int border_x = image.cols - border;
    663     const int border_y = image.rows - border;
    664     if (RoiPredicate((float)border, (float)border, (float)border_x, (float)border_y, keypoints[k]))
    665     {
    666       keypoints.erase(beginning + k);
    667       kscales.erase(beginningkscales + k);
    668       if (k == 0)
    669       {
    670         beginning = keypoints.begin();
    671         beginningkscales = kscales.begin();
    672       }
    673       ksize--;
    674       k--;
    675     }
    676   }
    677 
    678   // first, calculate the integral image over the whole image:
    679   // current integral image
    680   cv::Mat _integral; // the integral image
    681   cv::integral(image, _integral);
    682 
    683   int* _values = new int[points_]; // for temporary use
    684 
    685   // resize the descriptors:
    686   cv::Mat descriptors;
    687   if (doDescriptors)
    688   {
    689     _descriptors.create((int)ksize, strings_, CV_8U);
    690     descriptors = _descriptors.getMat();
    691     descriptors.setTo(0);
    692   }
    693 
    694   // now do the extraction for all keypoints:
    695 
    696   // temporary variables containing gray values at sample points:
    697   int t1;
    698   int t2;
    699 
    700   // the feature orientation
    701   const uchar* ptr = descriptors.ptr();
    702   for (size_t k = 0; k < ksize; k++)
    703   {
    704     cv::KeyPoint& kp = keypoints[k];
    705     const int& scale = kscales[k];
    706     int* pvalues = _values;
    707     const float& x = kp.pt.x;
    708     const float& y = kp.pt.y;
    709 
    710     if (doOrientation)
    711     {
    712         // get the gray values in the unrotated pattern
    713         for (unsigned int i = 0; i < points_; i++)
    714         {
    715           *(pvalues++) = smoothedIntensity(image, _integral, x, y, scale, 0, i);
    716         }
    717 
    718         int direction0 = 0;
    719         int direction1 = 0;
    720         // now iterate through the long pairings
    721         const BriskLongPair* max = longPairs_ + noLongPairs_;
    722         for (BriskLongPair* iter = longPairs_; iter < max; ++iter)
    723         {
    724           t1 = *(_values + iter->i);
    725           t2 = *(_values + iter->j);
    726           const int delta_t = (t1 - t2);
    727           // update the direction:
    728           const int tmp0 = delta_t * (iter->weighted_dx) / 1024;
    729           const int tmp1 = delta_t * (iter->weighted_dy) / 1024;
    730           direction0 += tmp0;
    731           direction1 += tmp1;
    732         }
    733         kp.angle = (float)(atan2((float) direction1, (float) direction0) / CV_PI * 180.0);
    734 
    735         if (!doDescriptors)
    736         {
    737           if (kp.angle < 0)
    738             kp.angle += 360.f;
    739         }
    740     }
    741 
    742     if (!doDescriptors)
    743       continue;
    744 
    745     int theta;
    746     if (kp.angle==-1)
    747     {
    748         // don't compute the gradient direction, just assign a rotation of 0
    749         theta = 0;
    750     }
    751     else
    752     {
    753         theta = (int) (n_rot_ * (kp.angle / (360.0)) + 0.5);
    754         if (theta < 0)
    755           theta += n_rot_;
    756         if (theta >= int(n_rot_))
    757           theta -= n_rot_;
    758     }
    759 
    760     if (kp.angle < 0)
    761       kp.angle += 360.f;
    762 
    763     // now also extract the stuff for the actual direction:
    764     // let us compute the smoothed values
    765     int shifter = 0;
    766 
    767     //unsigned int mean=0;
    768     pvalues = _values;
    769     // get the gray values in the rotated pattern
    770     for (unsigned int i = 0; i < points_; i++)
    771     {
    772       *(pvalues++) = smoothedIntensity(image, _integral, x, y, scale, theta, i);
    773     }
    774 
    775     // now iterate through all the pairings
    776     unsigned int* ptr2 = (unsigned int*) ptr;
    777     const BriskShortPair* max = shortPairs_ + noShortPairs_;
    778     for (BriskShortPair* iter = shortPairs_; iter < max; ++iter)
    779     {
    780       t1 = *(_values + iter->i);
    781       t2 = *(_values + iter->j);
    782       if (t1 > t2)
    783       {
    784         *ptr2 |= ((1) << shifter);
    785 
    786       } // else already initialized with zero
    787       // take care of the iterators:
    788       ++shifter;
    789       if (shifter == 32)
    790       {
    791         shifter = 0;
    792         ++ptr2;
    793       }
    794     }
    795 
    796     ptr += strings_;
    797   }
    798 
    799   // clean-up
    800   delete[] _values;
    801 }
    802 
    803 
    804 BRISK_Impl::~BRISK_Impl()
    805 {
    806   delete[] patternPoints_;
    807   delete[] shortPairs_;
    808   delete[] longPairs_;
    809   delete[] scaleList_;
    810   delete[] sizeList_;
    811 }
    812 
    813 void
    814 BRISK_Impl::computeKeypointsNoOrientation(InputArray _image, InputArray _mask, std::vector<KeyPoint>& keypoints) const
    815 {
    816   Mat image = _image.getMat(), mask = _mask.getMat();
    817   if( image.type() != CV_8UC1 )
    818       cvtColor(_image, image, COLOR_BGR2GRAY);
    819 
    820   BriskScaleSpace briskScaleSpace(octaves);
    821   briskScaleSpace.constructPyramid(image);
    822   briskScaleSpace.getKeypoints(threshold, keypoints);
    823 
    824   // remove invalid points
    825   KeyPointsFilter::runByPixelsMask(keypoints, mask);
    826 }
    827 
    828 // construct telling the octaves number:
    829 BriskScaleSpace::BriskScaleSpace(int _octaves)
    830 {
    831   if (_octaves == 0)
    832     layers_ = 1;
    833   else
    834     layers_ = 2 * _octaves;
    835 }
    836 BriskScaleSpace::~BriskScaleSpace()
    837 {
    838 
    839 }
    840 // construct the image pyramids
    841 void
    842 BriskScaleSpace::constructPyramid(const cv::Mat& image)
    843 {
    844 
    845   // set correct size:
    846   pyramid_.clear();
    847 
    848   // fill the pyramid:
    849   pyramid_.push_back(BriskLayer(image.clone()));
    850   if (layers_ > 1)
    851   {
    852     pyramid_.push_back(BriskLayer(pyramid_.back(), BriskLayer::CommonParams::TWOTHIRDSAMPLE));
    853   }
    854   const int octaves2 = layers_;
    855 
    856   for (uchar i = 2; i < octaves2; i += 2)
    857   {
    858     pyramid_.push_back(BriskLayer(pyramid_[i - 2], BriskLayer::CommonParams::HALFSAMPLE));
    859     pyramid_.push_back(BriskLayer(pyramid_[i - 1], BriskLayer::CommonParams::HALFSAMPLE));
    860   }
    861 }
    862 
    863 void
    864 BriskScaleSpace::getKeypoints(const int threshold_, std::vector<cv::KeyPoint>& keypoints)
    865 {
    866   // make sure keypoints is empty
    867   keypoints.resize(0);
    868   keypoints.reserve(2000);
    869 
    870   // assign thresholds
    871   int safeThreshold_ = (int)(threshold_ * safetyFactor_);
    872   std::vector<std::vector<cv::KeyPoint> > agastPoints;
    873   agastPoints.resize(layers_);
    874 
    875   // go through the octaves and intra layers and calculate agast corner scores:
    876   for (int i = 0; i < layers_; i++)
    877   {
    878     // call OAST16_9 without nms
    879     BriskLayer& l = pyramid_[i];
    880     l.getAgastPoints(safeThreshold_, agastPoints[i]);
    881   }
    882 
    883   if (layers_ == 1)
    884   {
    885     // just do a simple 2d subpixel refinement...
    886     const size_t num = agastPoints[0].size();
    887     for (size_t n = 0; n < num; n++)
    888     {
    889       const cv::Point2f& point = agastPoints.at(0)[n].pt;
    890       // first check if it is a maximum:
    891       if (!isMax2D(0, (int)point.x, (int)point.y))
    892         continue;
    893 
    894       // let's do the subpixel and float scale refinement:
    895       BriskLayer& l = pyramid_[0];
    896       int s_0_0 = l.getAgastScore(point.x - 1, point.y - 1, 1);
    897       int s_1_0 = l.getAgastScore(point.x, point.y - 1, 1);
    898       int s_2_0 = l.getAgastScore(point.x + 1, point.y - 1, 1);
    899       int s_2_1 = l.getAgastScore(point.x + 1, point.y, 1);
    900       int s_1_1 = l.getAgastScore(point.x, point.y, 1);
    901       int s_0_1 = l.getAgastScore(point.x - 1, point.y, 1);
    902       int s_0_2 = l.getAgastScore(point.x - 1, point.y + 1, 1);
    903       int s_1_2 = l.getAgastScore(point.x, point.y + 1, 1);
    904       int s_2_2 = l.getAgastScore(point.x + 1, point.y + 1, 1);
    905       float delta_x, delta_y;
    906       float max = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, delta_x, delta_y);
    907 
    908       // store:
    909       keypoints.push_back(cv::KeyPoint(float(point.x) + delta_x, float(point.y) + delta_y, basicSize_, -1, max, 0));
    910 
    911     }
    912 
    913     return;
    914   }
    915 
    916   float x, y, scale, score;
    917   for (int i = 0; i < layers_; i++)
    918   {
    919     BriskLayer& l = pyramid_[i];
    920     const size_t num = agastPoints[i].size();
    921     if (i == layers_ - 1)
    922     {
    923       for (size_t n = 0; n < num; n++)
    924       {
    925         const cv::Point2f& point = agastPoints.at(i)[n].pt;
    926         // consider only 2D maxima...
    927         if (!isMax2D(i, (int)point.x, (int)point.y))
    928           continue;
    929 
    930         bool ismax;
    931         float dx, dy;
    932         getScoreMaxBelow(i, (int)point.x, (int)point.y, l.getAgastScore(point.x, point.y, safeThreshold_), ismax, dx, dy);
    933         if (!ismax)
    934           continue;
    935 
    936         // get the patch on this layer:
    937         int s_0_0 = l.getAgastScore(point.x - 1, point.y - 1, 1);
    938         int s_1_0 = l.getAgastScore(point.x, point.y - 1, 1);
    939         int s_2_0 = l.getAgastScore(point.x + 1, point.y - 1, 1);
    940         int s_2_1 = l.getAgastScore(point.x + 1, point.y, 1);
    941         int s_1_1 = l.getAgastScore(point.x, point.y, 1);
    942         int s_0_1 = l.getAgastScore(point.x - 1, point.y, 1);
    943         int s_0_2 = l.getAgastScore(point.x - 1, point.y + 1, 1);
    944         int s_1_2 = l.getAgastScore(point.x, point.y + 1, 1);
    945         int s_2_2 = l.getAgastScore(point.x + 1, point.y + 1, 1);
    946         float delta_x, delta_y;
    947         float max = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, delta_x, delta_y);
    948 
    949         // store:
    950         keypoints.push_back(
    951             cv::KeyPoint((float(point.x) + delta_x) * l.scale() + l.offset(),
    952                          (float(point.y) + delta_y) * l.scale() + l.offset(), basicSize_ * l.scale(), -1, max, i));
    953       }
    954     }
    955     else
    956     {
    957       // not the last layer:
    958       for (size_t n = 0; n < num; n++)
    959       {
    960         const cv::Point2f& point = agastPoints.at(i)[n].pt;
    961 
    962         // first check if it is a maximum:
    963         if (!isMax2D(i, (int)point.x, (int)point.y))
    964           continue;
    965 
    966         // let's do the subpixel and float scale refinement:
    967         bool ismax=false;
    968         score = refine3D(i, (int)point.x, (int)point.y, x, y, scale, ismax);
    969         if (!ismax)
    970         {
    971           continue;
    972         }
    973 
    974         // finally store the detected keypoint:
    975         if (score > float(threshold_))
    976         {
    977           keypoints.push_back(cv::KeyPoint(x, y, basicSize_ * scale, -1, score, i));
    978         }
    979       }
    980     }
    981   }
    982 }
    983 
    984 // interpolated score access with recalculation when needed:
    985 inline int
    986 BriskScaleSpace::getScoreAbove(const int layer, const int x_layer, const int y_layer) const
    987 {
    988   CV_Assert(layer < layers_-1);
    989   const BriskLayer& l = pyramid_[layer + 1];
    990   if (layer % 2 == 0)
    991   { // octave
    992     const int sixths_x = 4 * x_layer - 1;
    993     const int x_above = sixths_x / 6;
    994     const int sixths_y = 4 * y_layer - 1;
    995     const int y_above = sixths_y / 6;
    996     const int r_x = (sixths_x % 6);
    997     const int r_x_1 = 6 - r_x;
    998     const int r_y = (sixths_y % 6);
    999     const int r_y_1 = 6 - r_y;
   1000     uchar score = 0xFF
   1001         & ((r_x_1 * r_y_1 * l.getAgastScore(x_above, y_above, 1) + r_x * r_y_1
   1002                                                                    * l.getAgastScore(x_above + 1, y_above, 1)
   1003             + r_x_1 * r_y * l.getAgastScore(x_above, y_above + 1, 1)
   1004             + r_x * r_y * l.getAgastScore(x_above + 1, y_above + 1, 1) + 18)
   1005            / 36);
   1006 
   1007     return score;
   1008   }
   1009   else
   1010   { // intra
   1011     const int eighths_x = 6 * x_layer - 1;
   1012     const int x_above = eighths_x / 8;
   1013     const int eighths_y = 6 * y_layer - 1;
   1014     const int y_above = eighths_y / 8;
   1015     const int r_x = (eighths_x % 8);
   1016     const int r_x_1 = 8 - r_x;
   1017     const int r_y = (eighths_y % 8);
   1018     const int r_y_1 = 8 - r_y;
   1019     uchar score = 0xFF
   1020         & ((r_x_1 * r_y_1 * l.getAgastScore(x_above, y_above, 1) + r_x * r_y_1
   1021                                                                    * l.getAgastScore(x_above + 1, y_above, 1)
   1022             + r_x_1 * r_y * l.getAgastScore(x_above, y_above + 1, 1)
   1023             + r_x * r_y * l.getAgastScore(x_above + 1, y_above + 1, 1) + 32)
   1024            / 64);
   1025     return score;
   1026   }
   1027 }
   1028 inline int
   1029 BriskScaleSpace::getScoreBelow(const int layer, const int x_layer, const int y_layer) const
   1030 {
   1031   CV_Assert(layer);
   1032   const BriskLayer& l = pyramid_[layer - 1];
   1033   int sixth_x;
   1034   int quarter_x;
   1035   float xf;
   1036   int sixth_y;
   1037   int quarter_y;
   1038   float yf;
   1039 
   1040   // scaling:
   1041   float offs;
   1042   float area;
   1043   int scaling;
   1044   int scaling2;
   1045 
   1046   if (layer % 2 == 0)
   1047   { // octave
   1048     sixth_x = 8 * x_layer + 1;
   1049     xf = float(sixth_x) / 6.0f;
   1050     sixth_y = 8 * y_layer + 1;
   1051     yf = float(sixth_y) / 6.0f;
   1052 
   1053     // scaling:
   1054     offs = 2.0f / 3.0f;
   1055     area = 4.0f * offs * offs;
   1056     scaling = (int)(4194304.0 / area);
   1057     scaling2 = (int)(float(scaling) * area);
   1058   }
   1059   else
   1060   {
   1061     quarter_x = 6 * x_layer + 1;
   1062     xf = float(quarter_x) / 4.0f;
   1063     quarter_y = 6 * y_layer + 1;
   1064     yf = float(quarter_y) / 4.0f;
   1065 
   1066     // scaling:
   1067     offs = 3.0f / 4.0f;
   1068     area = 4.0f * offs * offs;
   1069     scaling = (int)(4194304.0 / area);
   1070     scaling2 = (int)(float(scaling) * area);
   1071   }
   1072 
   1073   // calculate borders
   1074   const float x_1 = xf - offs;
   1075   const float x1 = xf + offs;
   1076   const float y_1 = yf - offs;
   1077   const float y1 = yf + offs;
   1078 
   1079   const int x_left = int(x_1 + 0.5);
   1080   const int y_top = int(y_1 + 0.5);
   1081   const int x_right = int(x1 + 0.5);
   1082   const int y_bottom = int(y1 + 0.5);
   1083 
   1084   // overlap area - multiplication factors:
   1085   const float r_x_1 = float(x_left) - x_1 + 0.5f;
   1086   const float r_y_1 = float(y_top) - y_1 + 0.5f;
   1087   const float r_x1 = x1 - float(x_right) + 0.5f;
   1088   const float r_y1 = y1 - float(y_bottom) + 0.5f;
   1089   const int dx = x_right - x_left - 1;
   1090   const int dy = y_bottom - y_top - 1;
   1091   const int A = (int)((r_x_1 * r_y_1) * scaling);
   1092   const int B = (int)((r_x1 * r_y_1) * scaling);
   1093   const int C = (int)((r_x1 * r_y1) * scaling);
   1094   const int D = (int)((r_x_1 * r_y1) * scaling);
   1095   const int r_x_1_i = (int)(r_x_1 * scaling);
   1096   const int r_y_1_i = (int)(r_y_1 * scaling);
   1097   const int r_x1_i = (int)(r_x1 * scaling);
   1098   const int r_y1_i = (int)(r_y1 * scaling);
   1099 
   1100   // first row:
   1101   int ret_val = A * int(l.getAgastScore(x_left, y_top, 1));
   1102   for (int X = 1; X <= dx; X++)
   1103   {
   1104     ret_val += r_y_1_i * int(l.getAgastScore(x_left + X, y_top, 1));
   1105   }
   1106   ret_val += B * int(l.getAgastScore(x_left + dx + 1, y_top, 1));
   1107   // middle ones:
   1108   for (int Y = 1; Y <= dy; Y++)
   1109   {
   1110     ret_val += r_x_1_i * int(l.getAgastScore(x_left, y_top + Y, 1));
   1111 
   1112     for (int X = 1; X <= dx; X++)
   1113     {
   1114       ret_val += int(l.getAgastScore(x_left + X, y_top + Y, 1)) * scaling;
   1115     }
   1116     ret_val += r_x1_i * int(l.getAgastScore(x_left + dx + 1, y_top + Y, 1));
   1117   }
   1118   // last row:
   1119   ret_val += D * int(l.getAgastScore(x_left, y_top + dy + 1, 1));
   1120   for (int X = 1; X <= dx; X++)
   1121   {
   1122     ret_val += r_y1_i * int(l.getAgastScore(x_left + X, y_top + dy + 1, 1));
   1123   }
   1124   ret_val += C * int(l.getAgastScore(x_left + dx + 1, y_top + dy + 1, 1));
   1125 
   1126   return ((ret_val + scaling2 / 2) / scaling2);
   1127 }
   1128 
   1129 inline bool
   1130 BriskScaleSpace::isMax2D(const int layer, const int x_layer, const int y_layer)
   1131 {
   1132   const cv::Mat& scores = pyramid_[layer].scores();
   1133   const int scorescols = scores.cols;
   1134   const uchar* data = scores.ptr() + y_layer * scorescols + x_layer;
   1135   // decision tree:
   1136   const uchar center = (*data);
   1137   data--;
   1138   const uchar s_10 = *data;
   1139   if (center < s_10)
   1140     return false;
   1141   data += 2;
   1142   const uchar s10 = *data;
   1143   if (center < s10)
   1144     return false;
   1145   data -= (scorescols + 1);
   1146   const uchar s0_1 = *data;
   1147   if (center < s0_1)
   1148     return false;
   1149   data += 2 * scorescols;
   1150   const uchar s01 = *data;
   1151   if (center < s01)
   1152     return false;
   1153   data--;
   1154   const uchar s_11 = *data;
   1155   if (center < s_11)
   1156     return false;
   1157   data += 2;
   1158   const uchar s11 = *data;
   1159   if (center < s11)
   1160     return false;
   1161   data -= 2 * scorescols;
   1162   const uchar s1_1 = *data;
   1163   if (center < s1_1)
   1164     return false;
   1165   data -= 2;
   1166   const uchar s_1_1 = *data;
   1167   if (center < s_1_1)
   1168     return false;
   1169 
   1170   // reject neighbor maxima
   1171   std::vector<int> delta;
   1172   // put together a list of 2d-offsets to where the maximum is also reached
   1173   if (center == s_1_1)
   1174   {
   1175     delta.push_back(-1);
   1176     delta.push_back(-1);
   1177   }
   1178   if (center == s0_1)
   1179   {
   1180     delta.push_back(0);
   1181     delta.push_back(-1);
   1182   }
   1183   if (center == s1_1)
   1184   {
   1185     delta.push_back(1);
   1186     delta.push_back(-1);
   1187   }
   1188   if (center == s_10)
   1189   {
   1190     delta.push_back(-1);
   1191     delta.push_back(0);
   1192   }
   1193   if (center == s10)
   1194   {
   1195     delta.push_back(1);
   1196     delta.push_back(0);
   1197   }
   1198   if (center == s_11)
   1199   {
   1200     delta.push_back(-1);
   1201     delta.push_back(1);
   1202   }
   1203   if (center == s01)
   1204   {
   1205     delta.push_back(0);
   1206     delta.push_back(1);
   1207   }
   1208   if (center == s11)
   1209   {
   1210     delta.push_back(1);
   1211     delta.push_back(1);
   1212   }
   1213   const unsigned int deltasize = (unsigned int)delta.size();
   1214   if (deltasize != 0)
   1215   {
   1216     // in this case, we have to analyze the situation more carefully:
   1217     // the values are gaussian blurred and then we really decide
   1218     data = scores.ptr() + y_layer * scorescols + x_layer;
   1219     int smoothedcenter = 4 * center + 2 * (s_10 + s10 + s0_1 + s01) + s_1_1 + s1_1 + s_11 + s11;
   1220     for (unsigned int i = 0; i < deltasize; i += 2)
   1221     {
   1222       data = scores.ptr() + (y_layer - 1 + delta[i + 1]) * scorescols + x_layer + delta[i] - 1;
   1223       int othercenter = *data;
   1224       data++;
   1225       othercenter += 2 * (*data);
   1226       data++;
   1227       othercenter += *data;
   1228       data += scorescols;
   1229       othercenter += 2 * (*data);
   1230       data--;
   1231       othercenter += 4 * (*data);
   1232       data--;
   1233       othercenter += 2 * (*data);
   1234       data += scorescols;
   1235       othercenter += *data;
   1236       data++;
   1237       othercenter += 2 * (*data);
   1238       data++;
   1239       othercenter += *data;
   1240       if (othercenter > smoothedcenter)
   1241         return false;
   1242     }
   1243   }
   1244   return true;
   1245 }
   1246 
   1247 // 3D maximum refinement centered around (x_layer,y_layer)
   1248 inline float
   1249 BriskScaleSpace::refine3D(const int layer, const int x_layer, const int y_layer, float& x, float& y, float& scale,
   1250                           bool& ismax) const
   1251 {
   1252   ismax = true;
   1253   const BriskLayer& thisLayer = pyramid_[layer];
   1254   const int center = thisLayer.getAgastScore(x_layer, y_layer, 1);
   1255 
   1256   // check and get above maximum:
   1257   float delta_x_above = 0, delta_y_above = 0;
   1258   float max_above = getScoreMaxAbove(layer, x_layer, y_layer, center, ismax, delta_x_above, delta_y_above);
   1259 
   1260   if (!ismax)
   1261     return 0.0f;
   1262 
   1263   float max; // to be returned
   1264 
   1265   if (layer % 2 == 0)
   1266   { // on octave
   1267     // treat the patch below:
   1268     float delta_x_below, delta_y_below;
   1269     float max_below_float;
   1270     int max_below = 0;
   1271     if (layer == 0)
   1272     {
   1273       // guess the lower intra octave...
   1274       const BriskLayer& l = pyramid_[0];
   1275       int s_0_0 = l.getAgastScore_5_8(x_layer - 1, y_layer - 1, 1);
   1276       max_below = s_0_0;
   1277       int s_1_0 = l.getAgastScore_5_8(x_layer, y_layer - 1, 1);
   1278       max_below = std::max(s_1_0, max_below);
   1279       int s_2_0 = l.getAgastScore_5_8(x_layer + 1, y_layer - 1, 1);
   1280       max_below = std::max(s_2_0, max_below);
   1281       int s_2_1 = l.getAgastScore_5_8(x_layer + 1, y_layer, 1);
   1282       max_below = std::max(s_2_1, max_below);
   1283       int s_1_1 = l.getAgastScore_5_8(x_layer, y_layer, 1);
   1284       max_below = std::max(s_1_1, max_below);
   1285       int s_0_1 = l.getAgastScore_5_8(x_layer - 1, y_layer, 1);
   1286       max_below = std::max(s_0_1, max_below);
   1287       int s_0_2 = l.getAgastScore_5_8(x_layer - 1, y_layer + 1, 1);
   1288       max_below = std::max(s_0_2, max_below);
   1289       int s_1_2 = l.getAgastScore_5_8(x_layer, y_layer + 1, 1);
   1290       max_below = std::max(s_1_2, max_below);
   1291       int s_2_2 = l.getAgastScore_5_8(x_layer + 1, y_layer + 1, 1);
   1292       max_below = std::max(s_2_2, max_below);
   1293 
   1294       max_below_float = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, delta_x_below,
   1295                                    delta_y_below);
   1296       max_below_float = (float)max_below;
   1297     }
   1298     else
   1299     {
   1300       max_below_float = getScoreMaxBelow(layer, x_layer, y_layer, center, ismax, delta_x_below, delta_y_below);
   1301       if (!ismax)
   1302         return 0;
   1303     }
   1304 
   1305     // get the patch on this layer:
   1306     int s_0_0 = thisLayer.getAgastScore(x_layer - 1, y_layer - 1, 1);
   1307     int s_1_0 = thisLayer.getAgastScore(x_layer, y_layer - 1, 1);
   1308     int s_2_0 = thisLayer.getAgastScore(x_layer + 1, y_layer - 1, 1);
   1309     int s_2_1 = thisLayer.getAgastScore(x_layer + 1, y_layer, 1);
   1310     int s_1_1 = thisLayer.getAgastScore(x_layer, y_layer, 1);
   1311     int s_0_1 = thisLayer.getAgastScore(x_layer - 1, y_layer, 1);
   1312     int s_0_2 = thisLayer.getAgastScore(x_layer - 1, y_layer + 1, 1);
   1313     int s_1_2 = thisLayer.getAgastScore(x_layer, y_layer + 1, 1);
   1314     int s_2_2 = thisLayer.getAgastScore(x_layer + 1, y_layer + 1, 1);
   1315     float delta_x_layer, delta_y_layer;
   1316     float max_layer = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, delta_x_layer,
   1317                                  delta_y_layer);
   1318 
   1319     // calculate the relative scale (1D maximum):
   1320     if (layer == 0)
   1321     {
   1322       scale = refine1D_2(max_below_float, std::max(float(center), max_layer), max_above, max);
   1323     }
   1324     else
   1325       scale = refine1D(max_below_float, std::max(float(center), max_layer), max_above, max);
   1326 
   1327     if (scale > 1.0)
   1328     {
   1329       // interpolate the position:
   1330       const float r0 = (1.5f - scale) / .5f;
   1331       const float r1 = 1.0f - r0;
   1332       x = (r0 * delta_x_layer + r1 * delta_x_above + float(x_layer)) * thisLayer.scale() + thisLayer.offset();
   1333       y = (r0 * delta_y_layer + r1 * delta_y_above + float(y_layer)) * thisLayer.scale() + thisLayer.offset();
   1334     }
   1335     else
   1336     {
   1337       if (layer == 0)
   1338       {
   1339         // interpolate the position:
   1340         const float r0 = (scale - 0.5f) / 0.5f;
   1341         const float r_1 = 1.0f - r0;
   1342         x = r0 * delta_x_layer + r_1 * delta_x_below + float(x_layer);
   1343         y = r0 * delta_y_layer + r_1 * delta_y_below + float(y_layer);
   1344       }
   1345       else
   1346       {
   1347         // interpolate the position:
   1348         const float r0 = (scale - 0.75f) / 0.25f;
   1349         const float r_1 = 1.0f - r0;
   1350         x = (r0 * delta_x_layer + r_1 * delta_x_below + float(x_layer)) * thisLayer.scale() + thisLayer.offset();
   1351         y = (r0 * delta_y_layer + r_1 * delta_y_below + float(y_layer)) * thisLayer.scale() + thisLayer.offset();
   1352       }
   1353     }
   1354   }
   1355   else
   1356   {
   1357     // on intra
   1358     // check the patch below:
   1359     float delta_x_below, delta_y_below;
   1360     float max_below = getScoreMaxBelow(layer, x_layer, y_layer, center, ismax, delta_x_below, delta_y_below);
   1361     if (!ismax)
   1362       return 0.0f;
   1363 
   1364     // get the patch on this layer:
   1365     int s_0_0 = thisLayer.getAgastScore(x_layer - 1, y_layer - 1, 1);
   1366     int s_1_0 = thisLayer.getAgastScore(x_layer, y_layer - 1, 1);
   1367     int s_2_0 = thisLayer.getAgastScore(x_layer + 1, y_layer - 1, 1);
   1368     int s_2_1 = thisLayer.getAgastScore(x_layer + 1, y_layer, 1);
   1369     int s_1_1 = thisLayer.getAgastScore(x_layer, y_layer, 1);
   1370     int s_0_1 = thisLayer.getAgastScore(x_layer - 1, y_layer, 1);
   1371     int s_0_2 = thisLayer.getAgastScore(x_layer - 1, y_layer + 1, 1);
   1372     int s_1_2 = thisLayer.getAgastScore(x_layer, y_layer + 1, 1);
   1373     int s_2_2 = thisLayer.getAgastScore(x_layer + 1, y_layer + 1, 1);
   1374     float delta_x_layer, delta_y_layer;
   1375     float max_layer = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, delta_x_layer,
   1376                                  delta_y_layer);
   1377 
   1378     // calculate the relative scale (1D maximum):
   1379     scale = refine1D_1(max_below, std::max(float(center), max_layer), max_above, max);
   1380     if (scale > 1.0)
   1381     {
   1382       // interpolate the position:
   1383       const float r0 = 4.0f - scale * 3.0f;
   1384       const float r1 = 1.0f - r0;
   1385       x = (r0 * delta_x_layer + r1 * delta_x_above + float(x_layer)) * thisLayer.scale() + thisLayer.offset();
   1386       y = (r0 * delta_y_layer + r1 * delta_y_above + float(y_layer)) * thisLayer.scale() + thisLayer.offset();
   1387     }
   1388     else
   1389     {
   1390       // interpolate the position:
   1391       const float r0 = scale * 3.0f - 2.0f;
   1392       const float r_1 = 1.0f - r0;
   1393       x = (r0 * delta_x_layer + r_1 * delta_x_below + float(x_layer)) * thisLayer.scale() + thisLayer.offset();
   1394       y = (r0 * delta_y_layer + r_1 * delta_y_below + float(y_layer)) * thisLayer.scale() + thisLayer.offset();
   1395     }
   1396   }
   1397 
   1398   // calculate the absolute scale:
   1399   scale *= thisLayer.scale();
   1400 
   1401   // that's it, return the refined maximum:
   1402   return max;
   1403 }
   1404 
   1405 // return the maximum of score patches above or below
   1406 inline float
   1407 BriskScaleSpace::getScoreMaxAbove(const int layer, const int x_layer, const int y_layer, const int threshold,
   1408                                   bool& ismax, float& dx, float& dy) const
   1409 {
   1410 
   1411   ismax = false;
   1412   // relevant floating point coordinates
   1413   float x_1;
   1414   float x1;
   1415   float y_1;
   1416   float y1;
   1417 
   1418   // the layer above
   1419   CV_Assert(layer + 1 < layers_);
   1420   const BriskLayer& layerAbove = pyramid_[layer + 1];
   1421 
   1422   if (layer % 2 == 0)
   1423   {
   1424     // octave
   1425     x_1 = float(4 * (x_layer) - 1 - 2) / 6.0f;
   1426     x1 = float(4 * (x_layer) - 1 + 2) / 6.0f;
   1427     y_1 = float(4 * (y_layer) - 1 - 2) / 6.0f;
   1428     y1 = float(4 * (y_layer) - 1 + 2) / 6.0f;
   1429   }
   1430   else
   1431   {
   1432     // intra
   1433     x_1 = float(6 * (x_layer) - 1 - 3) / 8.0f;
   1434     x1 = float(6 * (x_layer) - 1 + 3) / 8.0f;
   1435     y_1 = float(6 * (y_layer) - 1 - 3) / 8.0f;
   1436     y1 = float(6 * (y_layer) - 1 + 3) / 8.0f;
   1437   }
   1438 
   1439   // check the first row
   1440   int max_x = (int)x_1 + 1;
   1441   int max_y = (int)y_1 + 1;
   1442   float tmp_max;
   1443   float maxval = (float)layerAbove.getAgastScore(x_1, y_1, 1);
   1444   if (maxval > threshold)
   1445     return 0;
   1446   for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1447   {
   1448     tmp_max = (float)layerAbove.getAgastScore(float(x), y_1, 1);
   1449     if (tmp_max > threshold)
   1450       return 0;
   1451     if (tmp_max > maxval)
   1452     {
   1453       maxval = tmp_max;
   1454       max_x = x;
   1455     }
   1456   }
   1457   tmp_max = (float)layerAbove.getAgastScore(x1, y_1, 1);
   1458   if (tmp_max > threshold)
   1459     return 0;
   1460   if (tmp_max > maxval)
   1461   {
   1462     maxval = tmp_max;
   1463     max_x = int(x1);
   1464   }
   1465 
   1466   // middle rows
   1467   for (int y = (int)y_1 + 1; y <= int(y1); y++)
   1468   {
   1469     tmp_max = (float)layerAbove.getAgastScore(x_1, float(y), 1);
   1470     if (tmp_max > threshold)
   1471       return 0;
   1472     if (tmp_max > maxval)
   1473     {
   1474       maxval = tmp_max;
   1475       max_x = int(x_1 + 1);
   1476       max_y = y;
   1477     }
   1478     for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1479     {
   1480       tmp_max = (float)layerAbove.getAgastScore(x, y, 1);
   1481       if (tmp_max > threshold)
   1482         return 0;
   1483       if (tmp_max > maxval)
   1484       {
   1485         maxval = tmp_max;
   1486         max_x = x;
   1487         max_y = y;
   1488       }
   1489     }
   1490     tmp_max = (float)layerAbove.getAgastScore(x1, float(y), 1);
   1491     if (tmp_max > threshold)
   1492       return 0;
   1493     if (tmp_max > maxval)
   1494     {
   1495       maxval = tmp_max;
   1496       max_x = int(x1);
   1497       max_y = y;
   1498     }
   1499   }
   1500 
   1501   // bottom row
   1502   tmp_max = (float)layerAbove.getAgastScore(x_1, y1, 1);
   1503   if (tmp_max > maxval)
   1504   {
   1505     maxval = tmp_max;
   1506     max_x = int(x_1 + 1);
   1507     max_y = int(y1);
   1508   }
   1509   for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1510   {
   1511     tmp_max = (float)layerAbove.getAgastScore(float(x), y1, 1);
   1512     if (tmp_max > maxval)
   1513     {
   1514       maxval = tmp_max;
   1515       max_x = x;
   1516       max_y = int(y1);
   1517     }
   1518   }
   1519   tmp_max = (float)layerAbove.getAgastScore(x1, y1, 1);
   1520   if (tmp_max > maxval)
   1521   {
   1522     maxval = tmp_max;
   1523     max_x = int(x1);
   1524     max_y = int(y1);
   1525   }
   1526 
   1527   //find dx/dy:
   1528   int s_0_0 = layerAbove.getAgastScore(max_x - 1, max_y - 1, 1);
   1529   int s_1_0 = layerAbove.getAgastScore(max_x, max_y - 1, 1);
   1530   int s_2_0 = layerAbove.getAgastScore(max_x + 1, max_y - 1, 1);
   1531   int s_2_1 = layerAbove.getAgastScore(max_x + 1, max_y, 1);
   1532   int s_1_1 = layerAbove.getAgastScore(max_x, max_y, 1);
   1533   int s_0_1 = layerAbove.getAgastScore(max_x - 1, max_y, 1);
   1534   int s_0_2 = layerAbove.getAgastScore(max_x - 1, max_y + 1, 1);
   1535   int s_1_2 = layerAbove.getAgastScore(max_x, max_y + 1, 1);
   1536   int s_2_2 = layerAbove.getAgastScore(max_x + 1, max_y + 1, 1);
   1537   float dx_1, dy_1;
   1538   float refined_max = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, dx_1, dy_1);
   1539 
   1540   // calculate dx/dy in above coordinates
   1541   float real_x = float(max_x) + dx_1;
   1542   float real_y = float(max_y) + dy_1;
   1543   bool returnrefined = true;
   1544   if (layer % 2 == 0)
   1545   {
   1546     dx = (real_x * 6.0f + 1.0f) / 4.0f - float(x_layer);
   1547     dy = (real_y * 6.0f + 1.0f) / 4.0f - float(y_layer);
   1548   }
   1549   else
   1550   {
   1551     dx = (real_x * 8.0f + 1.0f) / 6.0f - float(x_layer);
   1552     dy = (real_y * 8.0f + 1.0f) / 6.0f - float(y_layer);
   1553   }
   1554 
   1555   // saturate
   1556   if (dx > 1.0f)
   1557   {
   1558     dx = 1.0f;
   1559     returnrefined = false;
   1560   }
   1561   if (dx < -1.0f)
   1562   {
   1563     dx = -1.0f;
   1564     returnrefined = false;
   1565   }
   1566   if (dy > 1.0f)
   1567   {
   1568     dy = 1.0f;
   1569     returnrefined = false;
   1570   }
   1571   if (dy < -1.0f)
   1572   {
   1573     dy = -1.0f;
   1574     returnrefined = false;
   1575   }
   1576 
   1577   // done and ok.
   1578   ismax = true;
   1579   if (returnrefined)
   1580   {
   1581     return std::max(refined_max, maxval);
   1582   }
   1583   return maxval;
   1584 }
   1585 
   1586 inline float
   1587 BriskScaleSpace::getScoreMaxBelow(const int layer, const int x_layer, const int y_layer, const int threshold,
   1588                                   bool& ismax, float& dx, float& dy) const
   1589 {
   1590   ismax = false;
   1591 
   1592   // relevant floating point coordinates
   1593   float x_1;
   1594   float x1;
   1595   float y_1;
   1596   float y1;
   1597 
   1598   if (layer % 2 == 0)
   1599   {
   1600     // octave
   1601     x_1 = float(8 * (x_layer) + 1 - 4) / 6.0f;
   1602     x1 = float(8 * (x_layer) + 1 + 4) / 6.0f;
   1603     y_1 = float(8 * (y_layer) + 1 - 4) / 6.0f;
   1604     y1 = float(8 * (y_layer) + 1 + 4) / 6.0f;
   1605   }
   1606   else
   1607   {
   1608     x_1 = float(6 * (x_layer) + 1 - 3) / 4.0f;
   1609     x1 = float(6 * (x_layer) + 1 + 3) / 4.0f;
   1610     y_1 = float(6 * (y_layer) + 1 - 3) / 4.0f;
   1611     y1 = float(6 * (y_layer) + 1 + 3) / 4.0f;
   1612   }
   1613 
   1614   // the layer below
   1615   CV_Assert(layer > 0);
   1616   const BriskLayer& layerBelow = pyramid_[layer - 1];
   1617 
   1618   // check the first row
   1619   int max_x = (int)x_1 + 1;
   1620   int max_y = (int)y_1 + 1;
   1621   float tmp_max;
   1622   float max = (float)layerBelow.getAgastScore(x_1, y_1, 1);
   1623   if (max > threshold)
   1624     return 0;
   1625   for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1626   {
   1627     tmp_max = (float)layerBelow.getAgastScore(float(x), y_1, 1);
   1628     if (tmp_max > threshold)
   1629       return 0;
   1630     if (tmp_max > max)
   1631     {
   1632       max = tmp_max;
   1633       max_x = x;
   1634     }
   1635   }
   1636   tmp_max = (float)layerBelow.getAgastScore(x1, y_1, 1);
   1637   if (tmp_max > threshold)
   1638     return 0;
   1639   if (tmp_max > max)
   1640   {
   1641     max = tmp_max;
   1642     max_x = int(x1);
   1643   }
   1644 
   1645   // middle rows
   1646   for (int y = (int)y_1 + 1; y <= int(y1); y++)
   1647   {
   1648     tmp_max = (float)layerBelow.getAgastScore(x_1, float(y), 1);
   1649     if (tmp_max > threshold)
   1650       return 0;
   1651     if (tmp_max > max)
   1652     {
   1653       max = tmp_max;
   1654       max_x = int(x_1 + 1);
   1655       max_y = y;
   1656     }
   1657     for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1658     {
   1659       tmp_max = (float)layerBelow.getAgastScore(x, y, 1);
   1660       if (tmp_max > threshold)
   1661         return 0;
   1662       if (tmp_max == max)
   1663       {
   1664         const int t1 = 2
   1665             * (layerBelow.getAgastScore(x - 1, y, 1) + layerBelow.getAgastScore(x + 1, y, 1)
   1666                + layerBelow.getAgastScore(x, y + 1, 1) + layerBelow.getAgastScore(x, y - 1, 1))
   1667                        + (layerBelow.getAgastScore(x + 1, y + 1, 1) + layerBelow.getAgastScore(x - 1, y + 1, 1)
   1668                           + layerBelow.getAgastScore(x + 1, y - 1, 1) + layerBelow.getAgastScore(x - 1, y - 1, 1));
   1669         const int t2 = 2
   1670             * (layerBelow.getAgastScore(max_x - 1, max_y, 1) + layerBelow.getAgastScore(max_x + 1, max_y, 1)
   1671                + layerBelow.getAgastScore(max_x, max_y + 1, 1) + layerBelow.getAgastScore(max_x, max_y - 1, 1))
   1672                        + (layerBelow.getAgastScore(max_x + 1, max_y + 1, 1) + layerBelow.getAgastScore(max_x - 1,
   1673                                                                                                        max_y + 1, 1)
   1674                           + layerBelow.getAgastScore(max_x + 1, max_y - 1, 1)
   1675                           + layerBelow.getAgastScore(max_x - 1, max_y - 1, 1));
   1676         if (t1 > t2)
   1677         {
   1678           max_x = x;
   1679           max_y = y;
   1680         }
   1681       }
   1682       if (tmp_max > max)
   1683       {
   1684         max = tmp_max;
   1685         max_x = x;
   1686         max_y = y;
   1687       }
   1688     }
   1689     tmp_max = (float)layerBelow.getAgastScore(x1, float(y), 1);
   1690     if (tmp_max > threshold)
   1691       return 0;
   1692     if (tmp_max > max)
   1693     {
   1694       max = tmp_max;
   1695       max_x = int(x1);
   1696       max_y = y;
   1697     }
   1698   }
   1699 
   1700   // bottom row
   1701   tmp_max = (float)layerBelow.getAgastScore(x_1, y1, 1);
   1702   if (tmp_max > max)
   1703   {
   1704     max = tmp_max;
   1705     max_x = int(x_1 + 1);
   1706     max_y = int(y1);
   1707   }
   1708   for (int x = (int)x_1 + 1; x <= int(x1); x++)
   1709   {
   1710     tmp_max = (float)layerBelow.getAgastScore(float(x), y1, 1);
   1711     if (tmp_max > max)
   1712     {
   1713       max = tmp_max;
   1714       max_x = x;
   1715       max_y = int(y1);
   1716     }
   1717   }
   1718   tmp_max = (float)layerBelow.getAgastScore(x1, y1, 1);
   1719   if (tmp_max > max)
   1720   {
   1721     max = tmp_max;
   1722     max_x = int(x1);
   1723     max_y = int(y1);
   1724   }
   1725 
   1726   //find dx/dy:
   1727   int s_0_0 = layerBelow.getAgastScore(max_x - 1, max_y - 1, 1);
   1728   int s_1_0 = layerBelow.getAgastScore(max_x, max_y - 1, 1);
   1729   int s_2_0 = layerBelow.getAgastScore(max_x + 1, max_y - 1, 1);
   1730   int s_2_1 = layerBelow.getAgastScore(max_x + 1, max_y, 1);
   1731   int s_1_1 = layerBelow.getAgastScore(max_x, max_y, 1);
   1732   int s_0_1 = layerBelow.getAgastScore(max_x - 1, max_y, 1);
   1733   int s_0_2 = layerBelow.getAgastScore(max_x - 1, max_y + 1, 1);
   1734   int s_1_2 = layerBelow.getAgastScore(max_x, max_y + 1, 1);
   1735   int s_2_2 = layerBelow.getAgastScore(max_x + 1, max_y + 1, 1);
   1736   float dx_1, dy_1;
   1737   float refined_max = subpixel2D(s_0_0, s_0_1, s_0_2, s_1_0, s_1_1, s_1_2, s_2_0, s_2_1, s_2_2, dx_1, dy_1);
   1738 
   1739   // calculate dx/dy in above coordinates
   1740   float real_x = float(max_x) + dx_1;
   1741   float real_y = float(max_y) + dy_1;
   1742   bool returnrefined = true;
   1743   if (layer % 2 == 0)
   1744   {
   1745     dx = (float)((real_x * 6.0 + 1.0) / 8.0) - float(x_layer);
   1746     dy = (float)((real_y * 6.0 + 1.0) / 8.0) - float(y_layer);
   1747   }
   1748   else
   1749   {
   1750     dx = (float)((real_x * 4.0 - 1.0) / 6.0) - float(x_layer);
   1751     dy = (float)((real_y * 4.0 - 1.0) / 6.0) - float(y_layer);
   1752   }
   1753 
   1754   // saturate
   1755   if (dx > 1.0)
   1756   {
   1757     dx = 1.0f;
   1758     returnrefined = false;
   1759   }
   1760   if (dx < -1.0f)
   1761   {
   1762     dx = -1.0f;
   1763     returnrefined = false;
   1764   }
   1765   if (dy > 1.0f)
   1766   {
   1767     dy = 1.0f;
   1768     returnrefined = false;
   1769   }
   1770   if (dy < -1.0f)
   1771   {
   1772     dy = -1.0f;
   1773     returnrefined = false;
   1774   }
   1775 
   1776   // done and ok.
   1777   ismax = true;
   1778   if (returnrefined)
   1779   {
   1780     return std::max(refined_max, max);
   1781   }
   1782   return max;
   1783 }
   1784 
   1785 inline float
   1786 BriskScaleSpace::refine1D(const float s_05, const float s0, const float s05, float& max) const
   1787 {
   1788   int i_05 = int(1024.0 * s_05 + 0.5);
   1789   int i0 = int(1024.0 * s0 + 0.5);
   1790   int i05 = int(1024.0 * s05 + 0.5);
   1791 
   1792   //   16.0000  -24.0000    8.0000
   1793   //  -40.0000   54.0000  -14.0000
   1794   //   24.0000  -27.0000    6.0000
   1795 
   1796   int three_a = 16 * i_05 - 24 * i0 + 8 * i05;
   1797   // second derivative must be negative:
   1798   if (three_a >= 0)
   1799   {
   1800     if (s0 >= s_05 && s0 >= s05)
   1801     {
   1802       max = s0;
   1803       return 1.0f;
   1804     }
   1805     if (s_05 >= s0 && s_05 >= s05)
   1806     {
   1807       max = s_05;
   1808       return 0.75f;
   1809     }
   1810     if (s05 >= s0 && s05 >= s_05)
   1811     {
   1812       max = s05;
   1813       return 1.5f;
   1814     }
   1815   }
   1816 
   1817   int three_b = -40 * i_05 + 54 * i0 - 14 * i05;
   1818   // calculate max location:
   1819   float ret_val = -float(three_b) / float(2 * three_a);
   1820   // saturate and return
   1821   if (ret_val < 0.75)
   1822     ret_val = 0.75;
   1823   else if (ret_val > 1.5)
   1824     ret_val = 1.5; // allow to be slightly off bounds ...?
   1825   int three_c = +24 * i_05 - 27 * i0 + 6 * i05;
   1826   max = float(three_c) + float(three_a) * ret_val * ret_val + float(three_b) * ret_val;
   1827   max /= 3072.0f;
   1828   return ret_val;
   1829 }
   1830 
   1831 inline float
   1832 BriskScaleSpace::refine1D_1(const float s_05, const float s0, const float s05, float& max) const
   1833 {
   1834   int i_05 = int(1024.0 * s_05 + 0.5);
   1835   int i0 = int(1024.0 * s0 + 0.5);
   1836   int i05 = int(1024.0 * s05 + 0.5);
   1837 
   1838   //  4.5000   -9.0000    4.5000
   1839   //-10.5000   18.0000   -7.5000
   1840   //  6.0000   -8.0000    3.0000
   1841 
   1842   int two_a = 9 * i_05 - 18 * i0 + 9 * i05;
   1843   // second derivative must be negative:
   1844   if (two_a >= 0)
   1845   {
   1846     if (s0 >= s_05 && s0 >= s05)
   1847     {
   1848       max = s0;
   1849       return 1.0f;
   1850     }
   1851     if (s_05 >= s0 && s_05 >= s05)
   1852     {
   1853       max = s_05;
   1854       return 0.6666666666666666666666666667f;
   1855     }
   1856     if (s05 >= s0 && s05 >= s_05)
   1857     {
   1858       max = s05;
   1859       return 1.3333333333333333333333333333f;
   1860     }
   1861   }
   1862 
   1863   int two_b = -21 * i_05 + 36 * i0 - 15 * i05;
   1864   // calculate max location:
   1865   float ret_val = -float(two_b) / float(2 * two_a);
   1866   // saturate and return
   1867   if (ret_val < 0.6666666666666666666666666667f)
   1868     ret_val = 0.666666666666666666666666667f;
   1869   else if (ret_val > 1.33333333333333333333333333f)
   1870     ret_val = 1.333333333333333333333333333f;
   1871   int two_c = +12 * i_05 - 16 * i0 + 6 * i05;
   1872   max = float(two_c) + float(two_a) * ret_val * ret_val + float(two_b) * ret_val;
   1873   max /= 2048.0f;
   1874   return ret_val;
   1875 }
   1876 
   1877 inline float
   1878 BriskScaleSpace::refine1D_2(const float s_05, const float s0, const float s05, float& max) const
   1879 {
   1880   int i_05 = int(1024.0 * s_05 + 0.5);
   1881   int i0 = int(1024.0 * s0 + 0.5);
   1882   int i05 = int(1024.0 * s05 + 0.5);
   1883 
   1884   //   18.0000  -30.0000   12.0000
   1885   //  -45.0000   65.0000  -20.0000
   1886   //   27.0000  -30.0000    8.0000
   1887 
   1888   int a = 2 * i_05 - 4 * i0 + 2 * i05;
   1889   // second derivative must be negative:
   1890   if (a >= 0)
   1891   {
   1892     if (s0 >= s_05 && s0 >= s05)
   1893     {
   1894       max = s0;
   1895       return 1.0f;
   1896     }
   1897     if (s_05 >= s0 && s_05 >= s05)
   1898     {
   1899       max = s_05;
   1900       return 0.7f;
   1901     }
   1902     if (s05 >= s0 && s05 >= s_05)
   1903     {
   1904       max = s05;
   1905       return 1.5f;
   1906     }
   1907   }
   1908 
   1909   int b = -5 * i_05 + 8 * i0 - 3 * i05;
   1910   // calculate max location:
   1911   float ret_val = -float(b) / float(2 * a);
   1912   // saturate and return
   1913   if (ret_val < 0.7f)
   1914     ret_val = 0.7f;
   1915   else if (ret_val > 1.5f)
   1916     ret_val = 1.5f; // allow to be slightly off bounds ...?
   1917   int c = +3 * i_05 - 3 * i0 + 1 * i05;
   1918   max = float(c) + float(a) * ret_val * ret_val + float(b) * ret_val;
   1919   max /= 1024;
   1920   return ret_val;
   1921 }
   1922 
   1923 inline float
   1924 BriskScaleSpace::subpixel2D(const int s_0_0, const int s_0_1, const int s_0_2, const int s_1_0, const int s_1_1,
   1925                             const int s_1_2, const int s_2_0, const int s_2_1, const int s_2_2, float& delta_x,
   1926                             float& delta_y) const
   1927 {
   1928 
   1929   // the coefficients of the 2d quadratic function least-squares fit:
   1930   int tmp1 = s_0_0 + s_0_2 - 2 * s_1_1 + s_2_0 + s_2_2;
   1931   int coeff1 = 3 * (tmp1 + s_0_1 - ((s_1_0 + s_1_2) << 1) + s_2_1);
   1932   int coeff2 = 3 * (tmp1 - ((s_0_1 + s_2_1) << 1) + s_1_0 + s_1_2);
   1933   int tmp2 = s_0_2 - s_2_0;
   1934   int tmp3 = (s_0_0 + tmp2 - s_2_2);
   1935   int tmp4 = tmp3 - 2 * tmp2;
   1936   int coeff3 = -3 * (tmp3 + s_0_1 - s_2_1);
   1937   int coeff4 = -3 * (tmp4 + s_1_0 - s_1_2);
   1938   int coeff5 = (s_0_0 - s_0_2 - s_2_0 + s_2_2) << 2;
   1939   int coeff6 = -(s_0_0 + s_0_2 - ((s_1_0 + s_0_1 + s_1_2 + s_2_1) << 1) - 5 * s_1_1 + s_2_0 + s_2_2) << 1;
   1940 
   1941   // 2nd derivative test:
   1942   int H_det = 4 * coeff1 * coeff2 - coeff5 * coeff5;
   1943 
   1944   if (H_det == 0)
   1945   {
   1946     delta_x = 0.0f;
   1947     delta_y = 0.0f;
   1948     return float(coeff6) / 18.0f;
   1949   }
   1950 
   1951   if (!(H_det > 0 && coeff1 < 0))
   1952   {
   1953     // The maximum must be at the one of the 4 patch corners.
   1954     int tmp_max = coeff3 + coeff4 + coeff5;
   1955     delta_x = 1.0f;
   1956     delta_y = 1.0f;
   1957 
   1958     int tmp = -coeff3 + coeff4 - coeff5;
   1959     if (tmp > tmp_max)
   1960     {
   1961       tmp_max = tmp;
   1962       delta_x = -1.0f;
   1963       delta_y = 1.0f;
   1964     }
   1965     tmp = coeff3 - coeff4 - coeff5;
   1966     if (tmp > tmp_max)
   1967     {
   1968       tmp_max = tmp;
   1969       delta_x = 1.0f;
   1970       delta_y = -1.0f;
   1971     }
   1972     tmp = -coeff3 - coeff4 + coeff5;
   1973     if (tmp > tmp_max)
   1974     {
   1975       tmp_max = tmp;
   1976       delta_x = -1.0f;
   1977       delta_y = -1.0f;
   1978     }
   1979     return float(tmp_max + coeff1 + coeff2 + coeff6) / 18.0f;
   1980   }
   1981 
   1982   // this is hopefully the normal outcome of the Hessian test
   1983   delta_x = float(2 * coeff2 * coeff3 - coeff4 * coeff5) / float(-H_det);
   1984   delta_y = float(2 * coeff1 * coeff4 - coeff3 * coeff5) / float(-H_det);
   1985   // TODO: this is not correct, but easy, so perform a real boundary maximum search:
   1986   bool tx = false;
   1987   bool tx_ = false;
   1988   bool ty = false;
   1989   bool ty_ = false;
   1990   if (delta_x > 1.0)
   1991     tx = true;
   1992   else if (delta_x < -1.0)
   1993     tx_ = true;
   1994   if (delta_y > 1.0)
   1995     ty = true;
   1996   if (delta_y < -1.0)
   1997     ty_ = true;
   1998 
   1999   if (tx || tx_ || ty || ty_)
   2000   {
   2001     // get two candidates:
   2002     float delta_x1 = 0.0f, delta_x2 = 0.0f, delta_y1 = 0.0f, delta_y2 = 0.0f;
   2003     if (tx)
   2004     {
   2005       delta_x1 = 1.0f;
   2006       delta_y1 = -float(coeff4 + coeff5) / float(2 * coeff2);
   2007       if (delta_y1 > 1.0f)
   2008         delta_y1 = 1.0f;
   2009       else if (delta_y1 < -1.0f)
   2010         delta_y1 = -1.0f;
   2011     }
   2012     else if (tx_)
   2013     {
   2014       delta_x1 = -1.0f;
   2015       delta_y1 = -float(coeff4 - coeff5) / float(2 * coeff2);
   2016       if (delta_y1 > 1.0f)
   2017         delta_y1 = 1.0f;
   2018       else if (delta_y1 < -1.0)
   2019         delta_y1 = -1.0f;
   2020     }
   2021     if (ty)
   2022     {
   2023       delta_y2 = 1.0f;
   2024       delta_x2 = -float(coeff3 + coeff5) / float(2 * coeff1);
   2025       if (delta_x2 > 1.0f)
   2026         delta_x2 = 1.0f;
   2027       else if (delta_x2 < -1.0f)
   2028         delta_x2 = -1.0f;
   2029     }
   2030     else if (ty_)
   2031     {
   2032       delta_y2 = -1.0f;
   2033       delta_x2 = -float(coeff3 - coeff5) / float(2 * coeff1);
   2034       if (delta_x2 > 1.0f)
   2035         delta_x2 = 1.0f;
   2036       else if (delta_x2 < -1.0f)
   2037         delta_x2 = -1.0f;
   2038     }
   2039     // insert both options for evaluation which to pick
   2040     float max1 = (coeff1 * delta_x1 * delta_x1 + coeff2 * delta_y1 * delta_y1 + coeff3 * delta_x1 + coeff4 * delta_y1
   2041                   + coeff5 * delta_x1 * delta_y1 + coeff6)
   2042                  / 18.0f;
   2043     float max2 = (coeff1 * delta_x2 * delta_x2 + coeff2 * delta_y2 * delta_y2 + coeff3 * delta_x2 + coeff4 * delta_y2
   2044                   + coeff5 * delta_x2 * delta_y2 + coeff6)
   2045                  / 18.0f;
   2046     if (max1 > max2)
   2047     {
   2048       delta_x = delta_x1;
   2049       delta_y = delta_x1;
   2050       return max1;
   2051     }
   2052     else
   2053     {
   2054       delta_x = delta_x2;
   2055       delta_y = delta_x2;
   2056       return max2;
   2057     }
   2058   }
   2059 
   2060   // this is the case of the maximum inside the boundaries:
   2061   return (coeff1 * delta_x * delta_x + coeff2 * delta_y * delta_y + coeff3 * delta_x + coeff4 * delta_y
   2062           + coeff5 * delta_x * delta_y + coeff6)
   2063          / 18.0f;
   2064 }
   2065 
   2066 // construct a layer
   2067 BriskLayer::BriskLayer(const cv::Mat& img_in, float scale_in, float offset_in)
   2068 {
   2069   img_ = img_in;
   2070   scores_ = cv::Mat_<uchar>::zeros(img_in.rows, img_in.cols);
   2071   // attention: this means that the passed image reference must point to persistent memory
   2072   scale_ = scale_in;
   2073   offset_ = offset_in;
   2074   // create an agast detector
   2075   oast_9_16_ = AgastFeatureDetector::create(1, false, AgastFeatureDetector::OAST_9_16);
   2076   makeAgastOffsets(pixel_5_8_, (int)img_.step, AgastFeatureDetector::AGAST_5_8);
   2077   makeAgastOffsets(pixel_9_16_, (int)img_.step, AgastFeatureDetector::OAST_9_16);
   2078 }
   2079 // derive a layer
   2080 BriskLayer::BriskLayer(const BriskLayer& layer, int mode)
   2081 {
   2082   if (mode == CommonParams::HALFSAMPLE)
   2083   {
   2084     img_.create(layer.img().rows / 2, layer.img().cols / 2, CV_8U);
   2085     halfsample(layer.img(), img_);
   2086     scale_ = layer.scale() * 2;
   2087     offset_ = 0.5f * scale_ - 0.5f;
   2088   }
   2089   else
   2090   {
   2091     img_.create(2 * (layer.img().rows / 3), 2 * (layer.img().cols / 3), CV_8U);
   2092     twothirdsample(layer.img(), img_);
   2093     scale_ = layer.scale() * 1.5f;
   2094     offset_ = 0.5f * scale_ - 0.5f;
   2095   }
   2096   scores_ = cv::Mat::zeros(img_.rows, img_.cols, CV_8U);
   2097   oast_9_16_ = AgastFeatureDetector::create(1, false, AgastFeatureDetector::OAST_9_16);
   2098   makeAgastOffsets(pixel_5_8_, (int)img_.step, AgastFeatureDetector::AGAST_5_8);
   2099   makeAgastOffsets(pixel_9_16_, (int)img_.step, AgastFeatureDetector::OAST_9_16);
   2100 }
   2101 
   2102 // Agast
   2103 // wraps the agast class
   2104 void
   2105 BriskLayer::getAgastPoints(int threshold, std::vector<KeyPoint>& keypoints)
   2106 {
   2107   oast_9_16_->setThreshold(threshold);
   2108   oast_9_16_->detect(img_, keypoints);
   2109 
   2110   // also write scores
   2111   const size_t num = keypoints.size();
   2112 
   2113   for (size_t i = 0; i < num; i++)
   2114     scores_((int)keypoints[i].pt.y, (int)keypoints[i].pt.x) = saturate_cast<uchar>(keypoints[i].response);
   2115 }
   2116 
   2117 inline int
   2118 BriskLayer::getAgastScore(int x, int y, int threshold) const
   2119 {
   2120   if (x < 3 || y < 3)
   2121     return 0;
   2122   if (x >= img_.cols - 3 || y >= img_.rows - 3)
   2123     return 0;
   2124   uchar& score = (uchar&)scores_(y, x);
   2125   if (score > 2)
   2126   {
   2127     return score;
   2128   }
   2129   score = (uchar)agast_cornerScore<AgastFeatureDetector::OAST_9_16>(&img_.at<uchar>(y, x), pixel_9_16_, threshold - 1);
   2130   if (score < threshold)
   2131     score = 0;
   2132   return score;
   2133 }
   2134 
   2135 inline int
   2136 BriskLayer::getAgastScore_5_8(int x, int y, int threshold) const
   2137 {
   2138   if (x < 2 || y < 2)
   2139     return 0;
   2140   if (x >= img_.cols - 2 || y >= img_.rows - 2)
   2141     return 0;
   2142   int score = agast_cornerScore<AgastFeatureDetector::AGAST_5_8>(&img_.at<uchar>(y, x), pixel_5_8_, threshold - 1);
   2143   if (score < threshold)
   2144     score = 0;
   2145   return score;
   2146 }
   2147 
   2148 inline int
   2149 BriskLayer::getAgastScore(float xf, float yf, int threshold_in, float scale_in) const
   2150 {
   2151   if (scale_in <= 1.0f)
   2152   {
   2153     // just do an interpolation inside the layer
   2154     const int x = int(xf);
   2155     const float rx1 = xf - float(x);
   2156     const float rx = 1.0f - rx1;
   2157     const int y = int(yf);
   2158     const float ry1 = yf - float(y);
   2159     const float ry = 1.0f - ry1;
   2160 
   2161     return (uchar)(rx * ry * getAgastScore(x, y, threshold_in) + rx1 * ry * getAgastScore(x + 1, y, threshold_in)
   2162            + rx * ry1 * getAgastScore(x, y + 1, threshold_in) + rx1 * ry1 * getAgastScore(x + 1, y + 1, threshold_in));
   2163   }
   2164   else
   2165   {
   2166     // this means we overlap area smoothing
   2167     const float halfscale = scale_in / 2.0f;
   2168     // get the scores first:
   2169     for (int x = int(xf - halfscale); x <= int(xf + halfscale + 1.0f); x++)
   2170     {
   2171       for (int y = int(yf - halfscale); y <= int(yf + halfscale + 1.0f); y++)
   2172       {
   2173         getAgastScore(x, y, threshold_in);
   2174       }
   2175     }
   2176     // get the smoothed value
   2177     return value(scores_, xf, yf, scale_in);
   2178   }
   2179 }
   2180 
   2181 // access gray values (smoothed/interpolated)
   2182 inline int
   2183 BriskLayer::value(const cv::Mat& mat, float xf, float yf, float scale_in) const
   2184 {
   2185   CV_Assert(!mat.empty());
   2186   // get the position
   2187   const int x = cvFloor(xf);
   2188   const int y = cvFloor(yf);
   2189   const cv::Mat& image = mat;
   2190   const int& imagecols = image.cols;
   2191 
   2192   // get the sigma_half:
   2193   const float sigma_half = scale_in / 2;
   2194   const float area = 4.0f * sigma_half * sigma_half;
   2195   // calculate output:
   2196   int ret_val;
   2197   if (sigma_half < 0.5)
   2198   {
   2199     //interpolation multipliers:
   2200     const int r_x = (int)((xf - x) * 1024);
   2201     const int r_y = (int)((yf - y) * 1024);
   2202     const int r_x_1 = (1024 - r_x);
   2203     const int r_y_1 = (1024 - r_y);
   2204     const uchar* ptr = image.ptr() + x + y * imagecols;
   2205     // just interpolate:
   2206     ret_val = (r_x_1 * r_y_1 * int(*ptr));
   2207     ptr++;
   2208     ret_val += (r_x * r_y_1 * int(*ptr));
   2209     ptr += imagecols;
   2210     ret_val += (r_x * r_y * int(*ptr));
   2211     ptr--;
   2212     ret_val += (r_x_1 * r_y * int(*ptr));
   2213     return 0xFF & ((ret_val + 512) / 1024 / 1024);
   2214   }
   2215 
   2216   // this is the standard case (simple, not speed optimized yet):
   2217 
   2218   // scaling:
   2219   const int scaling = (int)(4194304.0f / area);
   2220   const int scaling2 = (int)(float(scaling) * area / 1024.0f);
   2221 
   2222   // calculate borders
   2223   const float x_1 = xf - sigma_half;
   2224   const float x1 = xf + sigma_half;
   2225   const float y_1 = yf - sigma_half;
   2226   const float y1 = yf + sigma_half;
   2227 
   2228   const int x_left = int(x_1 + 0.5);
   2229   const int y_top = int(y_1 + 0.5);
   2230   const int x_right = int(x1 + 0.5);
   2231   const int y_bottom = int(y1 + 0.5);
   2232 
   2233   // overlap area - multiplication factors:
   2234   const float r_x_1 = float(x_left) - x_1 + 0.5f;
   2235   const float r_y_1 = float(y_top) - y_1 + 0.5f;
   2236   const float r_x1 = x1 - float(x_right) + 0.5f;
   2237   const float r_y1 = y1 - float(y_bottom) + 0.5f;
   2238   const int dx = x_right - x_left - 1;
   2239   const int dy = y_bottom - y_top - 1;
   2240   const int A = (int)((r_x_1 * r_y_1) * scaling);
   2241   const int B = (int)((r_x1 * r_y_1) * scaling);
   2242   const int C = (int)((r_x1 * r_y1) * scaling);
   2243   const int D = (int)((r_x_1 * r_y1) * scaling);
   2244   const int r_x_1_i = (int)(r_x_1 * scaling);
   2245   const int r_y_1_i = (int)(r_y_1 * scaling);
   2246   const int r_x1_i = (int)(r_x1 * scaling);
   2247   const int r_y1_i = (int)(r_y1 * scaling);
   2248 
   2249   // now the calculation:
   2250   const uchar* ptr = image.ptr() + x_left + imagecols * y_top;
   2251   // first row:
   2252   ret_val = A * int(*ptr);
   2253   ptr++;
   2254   const uchar* end1 = ptr + dx;
   2255   for (; ptr < end1; ptr++)
   2256   {
   2257     ret_val += r_y_1_i * int(*ptr);
   2258   }
   2259   ret_val += B * int(*ptr);
   2260   // middle ones:
   2261   ptr += imagecols - dx - 1;
   2262   const uchar* end_j = ptr + dy * imagecols;
   2263   for (; ptr < end_j; ptr += imagecols - dx - 1)
   2264   {
   2265     ret_val += r_x_1_i * int(*ptr);
   2266     ptr++;
   2267     const uchar* end2 = ptr + dx;
   2268     for (; ptr < end2; ptr++)
   2269     {
   2270       ret_val += int(*ptr) * scaling;
   2271     }
   2272     ret_val += r_x1_i * int(*ptr);
   2273   }
   2274   // last row:
   2275   ret_val += D * int(*ptr);
   2276   ptr++;
   2277   const uchar* end3 = ptr + dx;
   2278   for (; ptr < end3; ptr++)
   2279   {
   2280     ret_val += r_y1_i * int(*ptr);
   2281   }
   2282   ret_val += C * int(*ptr);
   2283 
   2284   return 0xFF & ((ret_val + scaling2 / 2) / scaling2 / 1024);
   2285 }
   2286 
   2287 // half sampling
   2288 inline void
   2289 BriskLayer::halfsample(const cv::Mat& srcimg, cv::Mat& dstimg)
   2290 {
   2291   // make sure the destination image is of the right size:
   2292   CV_Assert(srcimg.cols / 2 == dstimg.cols);
   2293   CV_Assert(srcimg.rows / 2 == dstimg.rows);
   2294 
   2295   // handle non-SSE case
   2296   resize(srcimg, dstimg, dstimg.size(), 0, 0, INTER_AREA);
   2297 }
   2298 
   2299 inline void
   2300 BriskLayer::twothirdsample(const cv::Mat& srcimg, cv::Mat& dstimg)
   2301 {
   2302   // make sure the destination image is of the right size:
   2303   CV_Assert((srcimg.cols / 3) * 2 == dstimg.cols);
   2304   CV_Assert((srcimg.rows / 3) * 2 == dstimg.rows);
   2305 
   2306   resize(srcimg, dstimg, dstimg.size(), 0, 0, INTER_AREA);
   2307 }
   2308 
   2309 Ptr<BRISK> BRISK::create(int thresh, int octaves, float patternScale)
   2310 {
   2311     return makePtr<BRISK_Impl>(thresh, octaves, patternScale);
   2312 }
   2313 
   2314 // custom setup
   2315 Ptr<BRISK> BRISK::create(const std::vector<float> &radiusList, const std::vector<int> &numberList,
   2316                          float dMax, float dMin, const std::vector<int>& indexChange)
   2317 {
   2318     return makePtr<BRISK_Impl>(radiusList, numberList, dMax, dMin, indexChange);
   2319 }
   2320 
   2321 }
   2322