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
descriptorSize() const64 int descriptorSize() const
65 {
66 return strings_;
67 }
68
descriptorType() const69 int descriptorType() const
70 {
71 return CV_8U;
72 }
73
defaultNorm() const74 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&
img() const174 img() const
175 {
176 return img_;
177 }
178 inline const cv::Mat&
scores() const179 scores() const
180 {
181 return scores_;
182 }
183 inline float
scale() const184 scale() const
185 {
186 return scale_;
187 }
188 inline float
offset() const189 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
BRISK_Impl(int thresh,int octaves_in,float patternScale)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
BRISK_Impl(const std::vector<float> & radiusList,const std::vector<int> & numberList,float dMax,float dMin,const std::vector<int> indexChange)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
generateKernel(const std::vector<float> & radiusList,const std::vector<int> & numberList,float dMax,float dMin,const std::vector<int> & _indexChange)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
smoothedIntensity(const cv::Mat & image,const cv::Mat & integral,const float key_x,const float key_y,const unsigned int scale,const unsigned int rot,const unsigned int point) const450 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
RoiPredicate(const float minX,const float minY,const float maxX,const float maxY,const KeyPoint & keyPt)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
detectAndCompute(InputArray _image,InputArray _mask,std::vector<KeyPoint> & keypoints,OutputArray _descriptors,bool useProvidedKeypoints)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
computeDescriptorsAndOrOrientation(InputArray _image,InputArray _mask,std::vector<KeyPoint> & keypoints,OutputArray _descriptors,bool doDescriptors,bool doOrientation,bool useProvidedKeypoints) const630 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
~BRISK_Impl()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
computeKeypointsNoOrientation(InputArray _image,InputArray _mask,std::vector<KeyPoint> & keypoints) const814 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:
BriskScaleSpace(int _octaves)829 BriskScaleSpace::BriskScaleSpace(int _octaves)
830 {
831 if (_octaves == 0)
832 layers_ = 1;
833 else
834 layers_ = 2 * _octaves;
835 }
~BriskScaleSpace()836 BriskScaleSpace::~BriskScaleSpace()
837 {
838
839 }
840 // construct the image pyramids
841 void
constructPyramid(const cv::Mat & image)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
getKeypoints(const int threshold_,std::vector<cv::KeyPoint> & keypoints)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
getScoreAbove(const int layer,const int x_layer,const int y_layer) const986 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
getScoreBelow(const int layer,const int x_layer,const int y_layer) const1029 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
isMax2D(const int layer,const int x_layer,const int y_layer)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
refine3D(const int layer,const int x_layer,const int y_layer,float & x,float & y,float & scale,bool & ismax) const1249 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
getScoreMaxAbove(const int layer,const int x_layer,const int y_layer,const int threshold,bool & ismax,float & dx,float & dy) const1407 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
getScoreMaxBelow(const int layer,const int x_layer,const int y_layer,const int threshold,bool & ismax,float & dx,float & dy) const1587 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
refine1D(const float s_05,const float s0,const float s05,float & max) const1786 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
refine1D_1(const float s_05,const float s0,const float s05,float & max) const1832 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
refine1D_2(const float s_05,const float s0,const float s05,float & max) const1878 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
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,const int s_2_0,const int s_2_1,const int s_2_2,float & delta_x,float & delta_y) const1924 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
BriskLayer(const cv::Mat & img_in,float scale_in,float offset_in)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
BriskLayer(const BriskLayer & layer,int mode)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
getAgastPoints(int threshold,std::vector<KeyPoint> & keypoints)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
getAgastScore(int x,int y,int threshold) const2118 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
getAgastScore_5_8(int x,int y,int threshold) const2136 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
getAgastScore(float xf,float yf,int threshold_in,float scale_in) const2149 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
value(const cv::Mat & mat,float xf,float yf,float scale_in) const2183 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
halfsample(const cv::Mat & srcimg,cv::Mat & dstimg)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
twothirdsample(const cv::Mat & srcimg,cv::Mat & dstimg)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
create(int thresh,int octaves,float patternScale)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
create(const std::vector<float> & radiusList,const std::vector<int> & numberList,float dMax,float dMin,const std::vector<int> & indexChange)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