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