Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/features2d/src/kaze/AKAZEFeatures.cpp
16337 views
1
/**
2
* @file AKAZEFeatures.cpp
3
* @brief Main class for detecting and describing binary features in an
4
* accelerated nonlinear scale space
5
* @date Sep 15, 2013
6
* @author Pablo F. Alcantarilla, Jesus Nuevo
7
*/
8
9
#include "../precomp.hpp"
10
#include "AKAZEFeatures.h"
11
#include "fed.h"
12
#include "nldiffusion_functions.h"
13
#include "utils.h"
14
#include "opencl_kernels_features2d.hpp"
15
16
#include <iostream>
17
18
// Namespaces
19
namespace cv
20
{
21
using namespace std;
22
23
/* ************************************************************************* */
24
/**
25
* @brief AKAZEFeatures constructor with input options
26
* @param options AKAZEFeatures configuration options
27
* @note This constructor allocates memory for the nonlinear scale space
28
*/
29
AKAZEFeatures::AKAZEFeatures(const AKAZEOptions& options) : options_(options) {
30
31
ncycles_ = 0;
32
reordering_ = true;
33
34
if (options_.descriptor_size > 0 && options_.descriptor >= AKAZE::DESCRIPTOR_MLDB_UPRIGHT) {
35
generateDescriptorSubsample(descriptorSamples_, descriptorBits_, options_.descriptor_size,
36
options_.descriptor_pattern_size, options_.descriptor_channels);
37
}
38
39
Allocate_Memory_Evolution();
40
}
41
42
/* ************************************************************************* */
43
/**
44
* @brief This method allocates the memory for the nonlinear diffusion evolution
45
*/
46
void AKAZEFeatures::Allocate_Memory_Evolution(void) {
47
CV_INSTRUMENT_REGION();
48
49
float rfactor = 0.0f;
50
int level_height = 0, level_width = 0;
51
52
// maximum size of the area for the descriptor computation
53
float smax = 0.0;
54
if (options_.descriptor == AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_MLDB) {
55
smax = 10.0f*sqrtf(2.0f);
56
}
57
else if (options_.descriptor == AKAZE::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_KAZE) {
58
smax = 12.0f*sqrtf(2.0f);
59
}
60
61
// Allocate the dimension of the matrices for the evolution
62
for (int i = 0, power = 1; i <= options_.omax - 1; i++, power *= 2) {
63
rfactor = 1.0f / power;
64
level_height = (int)(options_.img_height*rfactor);
65
level_width = (int)(options_.img_width*rfactor);
66
67
// Smallest possible octave and allow one scale if the image is small
68
if ((level_width < 80 || level_height < 40) && i != 0) {
69
options_.omax = i;
70
break;
71
}
72
73
for (int j = 0; j < options_.nsublevels; j++) {
74
MEvolution step;
75
step.size = Size(level_width, level_height);
76
step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i);
77
step.sigma_size = cvRound(step.esigma * options_.derivative_factor / power); // In fact sigma_size only depends on j
78
step.etime = 0.5f * (step.esigma * step.esigma);
79
step.octave = i;
80
step.sublevel = j;
81
step.octave_ratio = (float)power;
82
step.border = cvRound(smax * step.sigma_size) + 1;
83
84
evolution_.push_back(step);
85
}
86
}
87
88
// Allocate memory for the number of cycles and time steps
89
for (size_t i = 1; i < evolution_.size(); i++) {
90
int naux = 0;
91
vector<float> tau;
92
float ttime = 0.0f;
93
ttime = evolution_[i].etime - evolution_[i - 1].etime;
94
naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau);
95
nsteps_.push_back(naux);
96
tsteps_.push_back(tau);
97
ncycles_++;
98
}
99
}
100
101
/* ************************************************************************* */
102
/**
103
* @brief Computes kernel size for Gaussian smoothing if the image
104
* @param sigma Kernel standard deviation
105
* @returns kernel size
106
*/
107
static inline int getGaussianKernelSize(float sigma) {
108
// Compute an appropriate kernel size according to the specified sigma
109
int ksize = (int)cvCeil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f)));
110
ksize |= 1; // kernel should be odd
111
return ksize;
112
}
113
114
/* ************************************************************************* */
115
/**
116
* @brief This function computes a scalar non-linear diffusion step
117
* @param Lt Base image in the evolution
118
* @param Lf Conductivity image
119
* @param Lstep Output image that gives the difference between the current
120
* Ld and the next Ld being evolved
121
* @param row_begin row where to start
122
* @param row_end last row to fill exclusive. the range is [row_begin, row_end).
123
* @note Forward Euler Scheme 3x3 stencil
124
* The function c is a scalar value that depends on the gradient norm
125
* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy
126
*/
127
static inline void
128
nld_step_scalar_one_lane(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size, int row_begin, int row_end)
129
{
130
CV_INSTRUMENT_REGION();
131
/* The labeling scheme for this five star stencil:
132
[ a ]
133
[ -1 c +1 ]
134
[ b ]
135
*/
136
137
Lstep.create(Lt.size(), Lt.type());
138
const int cols = Lt.cols - 2;
139
int row = row_begin;
140
141
const float *lt_a, *lt_c, *lt_b;
142
const float *lf_a, *lf_c, *lf_b;
143
float *dst;
144
float step_r = 0.f;
145
146
// Process the top row
147
if (row == 0) {
148
lt_c = Lt.ptr<float>(0) + 1; /* Skip the left-most column by +1 */
149
lf_c = Lf.ptr<float>(0) + 1;
150
lt_b = Lt.ptr<float>(1) + 1;
151
lf_b = Lf.ptr<float>(1) + 1;
152
153
// fill the corner to prevent uninitialized values
154
dst = Lstep.ptr<float>(0);
155
dst[0] = 0.0f;
156
++dst;
157
158
for (int j = 0; j < cols; j++) {
159
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
160
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
161
(lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]);
162
dst[j] = step_r * step_size;
163
}
164
165
// fill the corner to prevent uninitialized values
166
dst[cols] = 0.0f;
167
++row;
168
}
169
170
// Process the middle rows
171
int middle_end = std::min(Lt.rows - 1, row_end);
172
for (; row < middle_end; ++row)
173
{
174
lt_a = Lt.ptr<float>(row - 1);
175
lf_a = Lf.ptr<float>(row - 1);
176
lt_c = Lt.ptr<float>(row );
177
lf_c = Lf.ptr<float>(row );
178
lt_b = Lt.ptr<float>(row + 1);
179
lf_b = Lf.ptr<float>(row + 1);
180
dst = Lstep.ptr<float>(row);
181
182
// The left-most column
183
step_r = (lf_c[0] + lf_c[1])*(lt_c[1] - lt_c[0]) +
184
(lf_c[0] + lf_b[0])*(lt_b[0] - lt_c[0]) +
185
(lf_c[0] + lf_a[0])*(lt_a[0] - lt_c[0]);
186
dst[0] = step_r * step_size;
187
188
lt_a++; lt_c++; lt_b++;
189
lf_a++; lf_c++; lf_b++;
190
dst++;
191
192
// The middle columns
193
for (int j = 0; j < cols; j++)
194
{
195
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
196
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
197
(lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]) +
198
(lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]);
199
dst[j] = step_r * step_size;
200
}
201
202
// The right-most column
203
step_r = (lf_c[cols] + lf_c[cols - 1])*(lt_c[cols - 1] - lt_c[cols]) +
204
(lf_c[cols] + lf_b[cols ])*(lt_b[cols ] - lt_c[cols]) +
205
(lf_c[cols] + lf_a[cols ])*(lt_a[cols ] - lt_c[cols]);
206
dst[cols] = step_r * step_size;
207
}
208
209
// Process the bottom row (row == Lt.rows - 1)
210
if (row_end == Lt.rows) {
211
lt_a = Lt.ptr<float>(row - 1) + 1; /* Skip the left-most column by +1 */
212
lf_a = Lf.ptr<float>(row - 1) + 1;
213
lt_c = Lt.ptr<float>(row ) + 1;
214
lf_c = Lf.ptr<float>(row ) + 1;
215
216
// fill the corner to prevent uninitialized values
217
dst = Lstep.ptr<float>(row);
218
dst[0] = 0.0f;
219
++dst;
220
221
for (int j = 0; j < cols; j++) {
222
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
223
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
224
(lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]);
225
dst[j] = step_r * step_size;
226
}
227
228
// fill the corner to prevent uninitialized values
229
dst[cols] = 0.0f;
230
}
231
}
232
233
class NonLinearScalarDiffusionStep : public ParallelLoopBody
234
{
235
public:
236
NonLinearScalarDiffusionStep(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size)
237
: Lt_(&Lt), Lf_(&Lf), Lstep_(&Lstep), step_size_(step_size)
238
{}
239
240
void operator()(const Range& range) const CV_OVERRIDE
241
{
242
nld_step_scalar_one_lane(*Lt_, *Lf_, *Lstep_, step_size_, range.start, range.end);
243
}
244
245
private:
246
const Mat* Lt_;
247
const Mat* Lf_;
248
Mat* Lstep_;
249
float step_size_;
250
};
251
252
#ifdef HAVE_OPENCL
253
static inline bool
254
ocl_non_linear_diffusion_step(InputArray Lt_, InputArray Lf_, OutputArray Lstep_, float step_size)
255
{
256
if(!Lt_.isContinuous())
257
return false;
258
259
UMat Lt = Lt_.getUMat();
260
UMat Lf = Lf_.getUMat();
261
UMat Lstep = Lstep_.getUMat();
262
263
size_t globalSize[] = {(size_t)Lt.cols, (size_t)Lt.rows};
264
265
ocl::Kernel ker("AKAZE_nld_step_scalar", ocl::features2d::akaze_oclsrc);
266
if( ker.empty() )
267
return false;
268
269
return ker.args(
270
ocl::KernelArg::ReadOnly(Lt),
271
ocl::KernelArg::PtrReadOnly(Lf),
272
ocl::KernelArg::PtrWriteOnly(Lstep),
273
step_size).run(2, globalSize, 0, true);
274
}
275
#endif // HAVE_OPENCL
276
277
static inline void
278
non_linear_diffusion_step(InputArray Lt_, InputArray Lf_, OutputArray Lstep_, float step_size)
279
{
280
CV_INSTRUMENT_REGION();
281
282
Lstep_.create(Lt_.size(), Lt_.type());
283
284
CV_OCL_RUN(Lt_.isUMat() && Lf_.isUMat() && Lstep_.isUMat(),
285
ocl_non_linear_diffusion_step(Lt_, Lf_, Lstep_, step_size));
286
287
Mat Lt = Lt_.getMat();
288
Mat Lf = Lf_.getMat();
289
Mat Lstep = Lstep_.getMat();
290
parallel_for_(Range(0, Lt.rows), NonLinearScalarDiffusionStep(Lt, Lf, Lstep, step_size));
291
}
292
293
/**
294
* @brief This function computes a good empirical value for the k contrast factor
295
* given two gradient images, the percentile (0-1), the temporal storage to hold
296
* gradient norms and the histogram bins
297
* @param Lx Horizontal gradient of the input image
298
* @param Ly Vertical gradient of the input image
299
* @param nbins Number of histogram bins
300
* @return k contrast factor
301
*/
302
static inline float
303
compute_kcontrast(InputArray Lx_, InputArray Ly_, float perc, int nbins)
304
{
305
CV_INSTRUMENT_REGION();
306
307
CV_Assert(nbins > 2);
308
CV_Assert(!Lx_.empty());
309
310
Mat Lx = Lx_.getMat();
311
Mat Ly = Ly_.getMat();
312
313
// temporary square roots of dot product
314
Mat modgs (Lx.rows - 2, Lx.cols - 2, CV_32F);
315
const int total = modgs.cols * modgs.rows;
316
float *modg = modgs.ptr<float>();
317
float hmax = 0.0f;
318
319
for (int i = 1; i < Lx.rows - 1; i++) {
320
const float *lx = Lx.ptr<float>(i) + 1;
321
const float *ly = Ly.ptr<float>(i) + 1;
322
const int cols = Lx.cols - 2;
323
324
for (int j = 0; j < cols; j++) {
325
float dist = sqrtf(lx[j] * lx[j] + ly[j] * ly[j]);
326
*modg++ = dist;
327
hmax = std::max(hmax, dist);
328
}
329
}
330
modg = modgs.ptr<float>();
331
332
if (hmax == 0.0f)
333
return 0.03f; // e.g. a blank image
334
335
// Compute the bin numbers: the value range [0, hmax] -> [0, nbins-1]
336
modgs *= (nbins - 1) / hmax;
337
338
// Count up histogram
339
std::vector<int> hist(nbins, 0);
340
for (int i = 0; i < total; i++)
341
hist[(int)modg[i]]++;
342
343
// Now find the perc of the histogram percentile
344
const int nthreshold = (int)((total - hist[0]) * perc); // Exclude hist[0] as background
345
int nelements = 0;
346
for (int k = 1; k < nbins; k++) {
347
if (nelements >= nthreshold)
348
return (float)hmax * k / nbins;
349
350
nelements += hist[k];
351
}
352
353
return 0.03f;
354
}
355
356
#ifdef HAVE_OPENCL
357
static inline bool
358
ocl_pm_g2(InputArray Lx_, InputArray Ly_, OutputArray Lflow_, float kcontrast)
359
{
360
UMat Lx = Lx_.getUMat();
361
UMat Ly = Ly_.getUMat();
362
UMat Lflow = Lflow_.getUMat();
363
364
int total = Lx.rows * Lx.cols;
365
size_t globalSize[] = {(size_t)total};
366
367
ocl::Kernel ker("AKAZE_pm_g2", ocl::features2d::akaze_oclsrc);
368
if( ker.empty() )
369
return false;
370
371
return ker.args(
372
ocl::KernelArg::PtrReadOnly(Lx),
373
ocl::KernelArg::PtrReadOnly(Ly),
374
ocl::KernelArg::PtrWriteOnly(Lflow),
375
kcontrast, total).run(1, globalSize, 0, true);
376
}
377
#endif // HAVE_OPENCL
378
379
static inline void
380
compute_diffusivity(InputArray Lx, InputArray Ly, OutputArray Lflow, float kcontrast, KAZE::DiffusivityType diffusivity)
381
{
382
CV_INSTRUMENT_REGION();
383
384
Lflow.create(Lx.size(), Lx.type());
385
386
switch (diffusivity) {
387
case KAZE::DIFF_PM_G1:
388
pm_g1(Lx, Ly, Lflow, kcontrast);
389
break;
390
case KAZE::DIFF_PM_G2:
391
CV_OCL_RUN(Lx.isUMat() && Ly.isUMat() && Lflow.isUMat(), ocl_pm_g2(Lx, Ly, Lflow, kcontrast));
392
pm_g2(Lx, Ly, Lflow, kcontrast);
393
break;
394
case KAZE::DIFF_WEICKERT:
395
weickert_diffusivity(Lx, Ly, Lflow, kcontrast);
396
break;
397
case KAZE::DIFF_CHARBONNIER:
398
charbonnier_diffusivity(Lx, Ly, Lflow, kcontrast);
399
break;
400
default:
401
CV_Error_(Error::StsError, ("Diffusivity is not supported: %d", static_cast<int>(diffusivity)));
402
break;
403
}
404
}
405
406
/**
407
* @brief Converts input image to grayscale float image
408
*
409
* @param image any image
410
* @param dst grayscale float image
411
*/
412
static inline void prepareInputImage(InputArray image, OutputArray dst)
413
{
414
Mat img = image.getMat();
415
if (img.channels() > 1)
416
cvtColor(image, img, COLOR_BGR2GRAY);
417
418
if ( img.depth() == CV_32F )
419
dst.assign(img);
420
else if ( img.depth() == CV_8U )
421
img.convertTo(dst, CV_32F, 1.0 / 255.0, 0);
422
else if ( img.depth() == CV_16U )
423
img.convertTo(dst, CV_32F, 1.0 / 65535.0, 0);
424
}
425
426
/**
427
* @brief This method creates the nonlinear scale space for a given image
428
* @param image Input image for which the nonlinear scale space needs to be created
429
*/
430
template<typename MatType>
431
static inline void
432
create_nonlinear_scale_space(InputArray image, const AKAZEOptions &options,
433
const std::vector<std::vector<float > > &tsteps_evolution, std::vector<Evolution<MatType> > &evolution)
434
{
435
CV_INSTRUMENT_REGION();
436
CV_Assert(evolution.size() > 0);
437
438
// convert input to grayscale float image if needed
439
MatType img;
440
prepareInputImage(image, img);
441
442
// create first level of the evolution
443
int ksize = getGaussianKernelSize(options.soffset);
444
GaussianBlur(img, evolution[0].Lsmooth, Size(ksize, ksize), options.soffset, options.soffset, BORDER_REPLICATE);
445
evolution[0].Lsmooth.copyTo(evolution[0].Lt);
446
447
if (evolution.size() == 1) {
448
// we don't need to compute kcontrast factor
449
Compute_Determinant_Hessian_Response(evolution);
450
return;
451
}
452
453
// derivatives, flow and diffusion step
454
MatType Lx, Ly, Lsmooth, Lflow, Lstep;
455
456
// compute derivatives for computing k contrast
457
GaussianBlur(img, Lsmooth, Size(5, 5), 1.0f, 1.0f, BORDER_REPLICATE);
458
Scharr(Lsmooth, Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT);
459
Scharr(Lsmooth, Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT);
460
Lsmooth.release();
461
// compute the kcontrast factor
462
float kcontrast = compute_kcontrast(Lx, Ly, options.kcontrast_percentile, options.kcontrast_nbins);
463
464
// Now generate the rest of evolution levels
465
for (size_t i = 1; i < evolution.size(); i++) {
466
Evolution<MatType> &e = evolution[i];
467
468
if (e.octave > evolution[i - 1].octave) {
469
// new octave will be half the size
470
resize(evolution[i - 1].Lt, e.Lt, e.size, 0, 0, INTER_AREA);
471
kcontrast *= 0.75f;
472
}
473
else {
474
evolution[i - 1].Lt.copyTo(e.Lt);
475
}
476
477
GaussianBlur(e.Lt, e.Lsmooth, Size(5, 5), 1.0f, 1.0f, BORDER_REPLICATE);
478
479
// Compute the Gaussian derivatives Lx and Ly
480
Scharr(e.Lsmooth, Lx, CV_32F, 1, 0, 1.0, 0, BORDER_DEFAULT);
481
Scharr(e.Lsmooth, Ly, CV_32F, 0, 1, 1.0, 0, BORDER_DEFAULT);
482
483
// Compute the conductivity equation
484
compute_diffusivity(Lx, Ly, Lflow, kcontrast, options.diffusivity);
485
486
// Perform Fast Explicit Diffusion on Lt
487
const std::vector<float> &tsteps = tsteps_evolution[i - 1];
488
for (size_t j = 0; j < tsteps.size(); j++) {
489
const float step_size = tsteps[j] * 0.5f;
490
non_linear_diffusion_step(e.Lt, Lflow, Lstep, step_size);
491
add(e.Lt, Lstep, e.Lt);
492
}
493
}
494
495
Compute_Determinant_Hessian_Response(evolution);
496
497
return;
498
}
499
500
/**
501
* @brief Converts between UMatPyramid and Pyramid and vice versa
502
* @details Matrices in evolution levels will be copied
503
*
504
* @param src source pyramid
505
* @param dst destination pyramid
506
*/
507
template<typename MatTypeSrc, typename MatTypeDst>
508
static inline void
509
convertScalePyramid(const std::vector<Evolution<MatTypeSrc> >& src, std::vector<Evolution<MatTypeDst> > &dst)
510
{
511
dst.resize(src.size());
512
for (size_t i = 0; i < src.size(); ++i) {
513
dst[i] = Evolution<MatTypeDst>(src[i]);
514
}
515
}
516
517
/**
518
* @brief This method creates the nonlinear scale space for a given image
519
* @param image Input image for which the nonlinear scale space needs to be created
520
*/
521
void AKAZEFeatures::Create_Nonlinear_Scale_Space(InputArray image)
522
{
523
if (ocl::isOpenCLActivated() && image.isUMat()) {
524
// will run OCL version of scale space pyramid
525
UMatPyramid uPyr;
526
// init UMat pyramid with sizes
527
convertScalePyramid(evolution_, uPyr);
528
create_nonlinear_scale_space(image, options_, tsteps_, uPyr);
529
// download pyramid from GPU
530
convertScalePyramid(uPyr, evolution_);
531
} else {
532
// CPU version
533
create_nonlinear_scale_space(image, options_, tsteps_, evolution_);
534
}
535
}
536
537
/* ************************************************************************* */
538
539
#ifdef HAVE_OPENCL
540
static inline bool
541
ocl_compute_determinant(InputArray Lxx_, InputArray Lxy_, InputArray Lyy_,
542
OutputArray Ldet_, float sigma)
543
{
544
UMat Lxx = Lxx_.getUMat();
545
UMat Lxy = Lxy_.getUMat();
546
UMat Lyy = Lyy_.getUMat();
547
UMat Ldet = Ldet_.getUMat();
548
549
const int total = Lxx.rows * Lxx.cols;
550
size_t globalSize[] = {(size_t)total};
551
552
ocl::Kernel ker("AKAZE_compute_determinant", ocl::features2d::akaze_oclsrc);
553
if( ker.empty() )
554
return false;
555
556
return ker.args(
557
ocl::KernelArg::PtrReadOnly(Lxx),
558
ocl::KernelArg::PtrReadOnly(Lxy),
559
ocl::KernelArg::PtrReadOnly(Lyy),
560
ocl::KernelArg::PtrWriteOnly(Ldet),
561
sigma, total).run(1, globalSize, 0, true);
562
}
563
#endif // HAVE_OPENCL
564
565
/**
566
* @brief Compute determinant from hessians
567
* @details Compute Ldet by (Lxx.mul(Lyy) - Lxy.mul(Lxy)) * sigma
568
*
569
* @param Lxx spatial derivates
570
* @param Lxy spatial derivates
571
* @param Lyy spatial derivates
572
* @param Ldet output determinant
573
* @param sigma determinant will be scaled by this sigma
574
*/
575
static inline void compute_determinant(InputArray Lxx_, InputArray Lxy_, InputArray Lyy_,
576
OutputArray Ldet_, float sigma)
577
{
578
CV_INSTRUMENT_REGION();
579
580
Ldet_.create(Lxx_.size(), Lxx_.type());
581
582
CV_OCL_RUN(Lxx_.isUMat() && Ldet_.isUMat(), ocl_compute_determinant(Lxx_, Lxy_, Lyy_, Ldet_, sigma));
583
584
// output determinant
585
Mat Lxx = Lxx_.getMat(), Lxy = Lxy_.getMat(), Lyy = Lyy_.getMat(), Ldet = Ldet_.getMat();
586
float *lxx = Lxx.ptr<float>();
587
float *lxy = Lxy.ptr<float>();
588
float *lyy = Lyy.ptr<float>();
589
float *ldet = Ldet.ptr<float>();
590
const int total = Lxx.cols * Lxx.rows;
591
for (int j = 0; j < total; j++) {
592
ldet[j] = (lxx[j] * lyy[j] - lxy[j] * lxy[j]) * sigma;
593
}
594
595
}
596
597
template <typename MatType>
598
class DeterminantHessianResponse : public ParallelLoopBody
599
{
600
public:
601
explicit DeterminantHessianResponse(std::vector<Evolution<MatType> >& ev)
602
: evolution_(&ev)
603
{
604
}
605
606
void operator()(const Range& range) const CV_OVERRIDE
607
{
608
MatType Lxx, Lxy, Lyy;
609
610
for (int i = range.start; i < range.end; i++)
611
{
612
Evolution<MatType> &e = (*evolution_)[i];
613
614
// we cannot use cv:Scharr here, because we need to handle also
615
// kernel sizes other than 3, by default we are using 9x9, 5x5 and 7x7
616
617
// compute kernels
618
Mat DxKx, DxKy, DyKx, DyKy;
619
compute_derivative_kernels(DxKx, DxKy, 1, 0, e.sigma_size);
620
compute_derivative_kernels(DyKx, DyKy, 0, 1, e.sigma_size);
621
622
// compute the multiscale derivatives
623
sepFilter2D(e.Lsmooth, e.Lx, CV_32F, DxKx, DxKy);
624
sepFilter2D(e.Lx, Lxx, CV_32F, DxKx, DxKy);
625
sepFilter2D(e.Lx, Lxy, CV_32F, DyKx, DyKy);
626
sepFilter2D(e.Lsmooth, e.Ly, CV_32F, DyKx, DyKy);
627
sepFilter2D(e.Ly, Lyy, CV_32F, DyKx, DyKy);
628
629
// free Lsmooth to same some space in the pyramid, it is not needed anymore
630
e.Lsmooth.release();
631
632
// compute determinant scaled by sigma
633
float sigma_size_quat = (float)(e.sigma_size * e.sigma_size * e.sigma_size * e.sigma_size);
634
compute_determinant(Lxx, Lxy, Lyy, e.Ldet, sigma_size_quat);
635
}
636
}
637
638
private:
639
std::vector<Evolution<MatType> >* evolution_;
640
};
641
642
643
/**
644
* @brief This method computes the feature detector response for the nonlinear scale space
645
* @details OCL version
646
* @note We use the Hessian determinant as the feature detector response
647
*/
648
static inline void
649
Compute_Determinant_Hessian_Response(UMatPyramid &evolution) {
650
CV_INSTRUMENT_REGION();
651
652
DeterminantHessianResponse<UMat> body (evolution);
653
body(Range(0, (int)evolution.size()));
654
}
655
656
/**
657
* @brief This method computes the feature detector response for the nonlinear scale space
658
* @details CPU version
659
* @note We use the Hessian determinant as the feature detector response
660
*/
661
static inline void
662
Compute_Determinant_Hessian_Response(Pyramid &evolution) {
663
CV_INSTRUMENT_REGION();
664
665
parallel_for_(Range(0, (int)evolution.size()), DeterminantHessianResponse<Mat>(evolution));
666
}
667
668
/* ************************************************************************* */
669
670
/**
671
* @brief This method selects interesting keypoints through the nonlinear scale space
672
* @param kpts Vector of detected keypoints
673
*/
674
void AKAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts)
675
{
676
CV_INSTRUMENT_REGION();
677
678
kpts.clear();
679
std::vector<Mat> keypoints_by_layers;
680
Find_Scale_Space_Extrema(keypoints_by_layers);
681
Do_Subpixel_Refinement(keypoints_by_layers, kpts);
682
Compute_Keypoints_Orientation(kpts);
683
}
684
685
/**
686
* @brief This method searches v for a neighbor point of the point candidate p
687
* @param x Coordinates of the keypoint candidate to search a neighbor
688
* @param y Coordinates of the keypoint candidate to search a neighbor
689
* @param mask Matrix holding keypoints positions
690
* @param search_radius neighbour radius for searching keypoints
691
* @param idx The index to mask, pointing to keypoint found.
692
* @return true if a neighbor point is found; false otherwise
693
*/
694
static inline bool
695
find_neighbor_point(const int x, const int y, const Mat &mask, const int search_radius, int &idx)
696
{
697
// search neighborhood for keypoints
698
for (int i = y - search_radius; i < y + search_radius; ++i) {
699
const uchar *curr = mask.ptr<uchar>(i);
700
for (int j = x - search_radius; j < x + search_radius; ++j) {
701
if (curr[j] == 0) {
702
continue; // skip non-keypoint
703
}
704
// fine-compare with L2 metric (L2 is smaller than our search window)
705
int dx = j - x;
706
int dy = i - y;
707
if (dx * dx + dy * dy <= search_radius * search_radius) {
708
idx = i * mask.cols + j;
709
return true;
710
}
711
}
712
}
713
714
return false;
715
}
716
717
/**
718
* @brief Find keypoints in parallel for each pyramid layer
719
*/
720
class FindKeypointsSameScale : public ParallelLoopBody
721
{
722
public:
723
explicit FindKeypointsSameScale(const Pyramid& ev,
724
std::vector<Mat>& kpts, float dthreshold)
725
: evolution_(&ev), keypoints_by_layers_(&kpts), dthreshold_(dthreshold)
726
{}
727
728
void operator()(const Range& range) const CV_OVERRIDE
729
{
730
for (int i = range.start; i < range.end; i++)
731
{
732
const MEvolution &e = (*evolution_)[i];
733
Mat &kpts = (*keypoints_by_layers_)[i];
734
// this mask will hold positions of keypoints in this level
735
kpts = Mat::zeros(e.Ldet.size(), CV_8UC1);
736
737
// if border is too big we shouldn't search any keypoints
738
if (e.border + 1 >= e.Ldet.rows)
739
continue;
740
741
const float * prev = e.Ldet.ptr<float>(e.border - 1);
742
const float * curr = e.Ldet.ptr<float>(e.border );
743
const float * next = e.Ldet.ptr<float>(e.border + 1);
744
const float * ldet = e.Ldet.ptr<float>();
745
uchar *mask = kpts.ptr<uchar>();
746
const int search_radius = e.sigma_size; // size of keypoint in this level
747
748
for (int y = e.border; y < e.Ldet.rows - e.border; y++) {
749
for (int x = e.border; x < e.Ldet.cols - e.border; x++) {
750
const float value = curr[x];
751
752
// Filter the points with the detector threshold
753
if (value <= dthreshold_)
754
continue;
755
if (value <= curr[x-1] || value <= curr[x+1])
756
continue;
757
if (value <= prev[x-1] || value <= prev[x ] || value <= prev[x+1])
758
continue;
759
if (value <= next[x-1] || value <= next[x ] || value <= next[x+1])
760
continue;
761
762
int idx = 0;
763
// Compare response with the same scale
764
if (find_neighbor_point(x, y, kpts, search_radius, idx)) {
765
if (value > ldet[idx]) {
766
mask[idx] = 0; // clear old point - we have better candidate now
767
} else {
768
continue; // there already is a better keypoint
769
}
770
}
771
772
kpts.at<uchar>(y, x) = 1; // we have a new keypoint
773
}
774
775
prev = curr;
776
curr = next;
777
next += e.Ldet.cols;
778
}
779
}
780
}
781
782
private:
783
const Pyramid* evolution_;
784
std::vector<Mat>* keypoints_by_layers_;
785
float dthreshold_; ///< Detector response threshold to accept point
786
};
787
788
/**
789
* @brief This method finds extrema in the nonlinear scale space
790
* @param keypoints_by_layers Output vectors of detected keypoints; one vector for each evolution level
791
*/
792
void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector<Mat>& keypoints_by_layers)
793
{
794
CV_INSTRUMENT_REGION();
795
796
keypoints_by_layers.resize(evolution_.size());
797
798
// find points in the same level
799
parallel_for_(Range(0, (int)evolution_.size()),
800
FindKeypointsSameScale(evolution_, keypoints_by_layers, options_.dthreshold));
801
802
// Filter points with the lower scale level
803
for (size_t i = 1; i < keypoints_by_layers.size(); i++) {
804
// constants for this level
805
const Mat &keypoints = keypoints_by_layers[i];
806
const uchar *const kpts = keypoints_by_layers[i].ptr<uchar>();
807
uchar *const kpts_prev = keypoints_by_layers[i-1].ptr<uchar>();
808
const float *const ldet = evolution_[i].Ldet.ptr<float>();
809
const float *const ldet_prev = evolution_[i-1].Ldet.ptr<float>();
810
// ratios are just powers of 2
811
const int diff_ratio = (int)evolution_[i].octave_ratio / (int)evolution_[i-1].octave_ratio;
812
const int search_radius = evolution_[i].sigma_size * diff_ratio; // size of keypoint in this level
813
814
size_t j = 0;
815
for (int y = 0; y < keypoints.rows; y++) {
816
for (int x = 0; x < keypoints.cols; x++, j++) {
817
if (kpts[j] == 0) {
818
continue; // skip non-keypoints
819
}
820
int idx = 0;
821
// project point to lower scale layer
822
const int p_x = x * diff_ratio;
823
const int p_y = y * diff_ratio;
824
if (find_neighbor_point(p_x, p_y, keypoints_by_layers[i-1], search_radius, idx)) {
825
if (ldet[j] > ldet_prev[idx]) {
826
kpts_prev[idx] = 0; // clear keypoint in lower layer
827
}
828
// else this pt may be pruned by the upper scale
829
}
830
}
831
}
832
}
833
834
// Now filter points with the upper scale level (the other direction)
835
for (int i = (int)keypoints_by_layers.size() - 2; i >= 0; i--) {
836
// constants for this level
837
const Mat &keypoints = keypoints_by_layers[i];
838
const uchar *const kpts = keypoints_by_layers[i].ptr<uchar>();
839
uchar *const kpts_next = keypoints_by_layers[i+1].ptr<uchar>();
840
const float *const ldet = evolution_[i].Ldet.ptr<float>();
841
const float *const ldet_next = evolution_[i+1].Ldet.ptr<float>();
842
// ratios are just powers of 2, i+1 ratio is always greater or equal to i
843
const int diff_ratio = (int)evolution_[i+1].octave_ratio / (int)evolution_[i].octave_ratio;
844
const int search_radius = evolution_[i+1].sigma_size; // size of keypoints in upper level
845
846
size_t j = 0;
847
for (int y = 0; y < keypoints.rows; y++) {
848
for (int x = 0; x < keypoints.cols; x++, j++) {
849
if (kpts[j] == 0) {
850
continue; // skip non-keypoints
851
}
852
int idx = 0;
853
// project point to upper scale layer
854
const int p_x = x / diff_ratio;
855
const int p_y = y / diff_ratio;
856
if (find_neighbor_point(p_x, p_y, keypoints_by_layers[i+1], search_radius, idx)) {
857
if (ldet[j] > ldet_next[idx]) {
858
kpts_next[idx] = 0; // clear keypoint in upper layer
859
}
860
}
861
}
862
}
863
}
864
}
865
866
/* ************************************************************************* */
867
/**
868
* @brief This method performs subpixel refinement of the detected keypoints
869
* @param keypoints_by_layers Input vectors of detected keypoints, sorted by evolution levels
870
* @param kpts Output vector of the final refined keypoints
871
*/
872
void AKAZEFeatures::Do_Subpixel_Refinement(
873
std::vector<Mat>& keypoints_by_layers, std::vector<KeyPoint>& output_keypoints)
874
{
875
CV_INSTRUMENT_REGION();
876
877
for (size_t i = 0; i < keypoints_by_layers.size(); i++) {
878
const MEvolution &e = evolution_[i];
879
const float * const ldet = e.Ldet.ptr<float>();
880
const float ratio = e.octave_ratio;
881
const int cols = e.Ldet.cols;
882
const Mat& keypoints = keypoints_by_layers[i];
883
const uchar *const kpts = keypoints.ptr<uchar>();
884
885
size_t j = 0;
886
for (int y = 0; y < keypoints.rows; y++) {
887
for (int x = 0; x < keypoints.cols; x++, j++) {
888
if (kpts[j] == 0) {
889
continue; // skip non-keypoints
890
}
891
892
// create a new keypoint
893
KeyPoint kp;
894
kp.pt.x = x * e.octave_ratio;
895
kp.pt.y = y * e.octave_ratio;
896
kp.size = e.esigma * options_.derivative_factor;
897
kp.angle = -1;
898
kp.response = ldet[j];
899
kp.octave = e.octave;
900
kp.class_id = static_cast<int>(i);
901
902
// Compute the gradient
903
float Dx = 0.5f * (ldet[ y *cols + x + 1] - ldet[ y *cols + x - 1]);
904
float Dy = 0.5f * (ldet[(y + 1)*cols + x ] - ldet[(y - 1)*cols + x ]);
905
906
// Compute the Hessian
907
float Dxx = ldet[ y *cols + x + 1] + ldet[ y *cols + x - 1] - 2.0f * ldet[y*cols + x];
908
float Dyy = ldet[(y + 1)*cols + x ] + ldet[(y - 1)*cols + x ] - 2.0f * ldet[y*cols + x];
909
float Dxy = 0.25f * (ldet[(y + 1)*cols + x + 1] + ldet[(y - 1)*cols + x - 1] -
910
ldet[(y - 1)*cols + x + 1] - ldet[(y + 1)*cols + x - 1]);
911
912
// Solve the linear system
913
Matx22f A( Dxx, Dxy,
914
Dxy, Dyy );
915
Vec2f b( -Dx, -Dy );
916
Vec2f dst( 0.0f, 0.0f );
917
solve(A, b, dst, DECOMP_LU);
918
919
float dx = dst(0);
920
float dy = dst(1);
921
922
if (fabs(dx) > 1.0f || fabs(dy) > 1.0f)
923
continue; // Ignore the point that is not stable
924
925
// Refine the coordinates
926
kp.pt.x += dx * ratio + .5f*(ratio-1.f);
927
kp.pt.y += dy * ratio + .5f*(ratio-1.f);
928
929
kp.angle = 0.0;
930
kp.size *= 2.0f; // In OpenCV the size of a keypoint is the diameter
931
932
// Push the refined keypoint to the final storage
933
output_keypoints.push_back(kp);
934
}
935
}
936
}
937
}
938
939
/* ************************************************************************* */
940
941
class SURF_Descriptor_Upright_64_Invoker : public ParallelLoopBody
942
{
943
public:
944
SURF_Descriptor_Upright_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, const Pyramid& evolution)
945
: keypoints_(&kpts)
946
, descriptors_(&desc)
947
, evolution_(&evolution)
948
{
949
}
950
951
void operator() (const Range& range) const CV_OVERRIDE
952
{
953
for (int i = range.start; i < range.end; i++)
954
{
955
Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols);
956
}
957
}
958
959
void Get_SURF_Descriptor_Upright_64(const KeyPoint& kpt, float* desc, int desc_size) const;
960
961
private:
962
std::vector<KeyPoint>* keypoints_;
963
Mat* descriptors_;
964
const Pyramid* evolution_;
965
};
966
967
class SURF_Descriptor_64_Invoker : public ParallelLoopBody
968
{
969
public:
970
SURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, Pyramid& evolution)
971
: keypoints_(&kpts)
972
, descriptors_(&desc)
973
, evolution_(&evolution)
974
{
975
}
976
977
void operator()(const Range& range) const CV_OVERRIDE
978
{
979
for (int i = range.start; i < range.end; i++)
980
{
981
Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols);
982
}
983
}
984
985
void Get_SURF_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const;
986
987
private:
988
std::vector<KeyPoint>* keypoints_;
989
Mat* descriptors_;
990
Pyramid* evolution_;
991
};
992
993
class MSURF_Upright_Descriptor_64_Invoker : public ParallelLoopBody
994
{
995
public:
996
MSURF_Upright_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, Pyramid& evolution)
997
: keypoints_(&kpts)
998
, descriptors_(&desc)
999
, evolution_(&evolution)
1000
{
1001
}
1002
1003
void operator()(const Range& range) const CV_OVERRIDE
1004
{
1005
for (int i = range.start; i < range.end; i++)
1006
{
1007
Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols);
1008
}
1009
}
1010
1011
void Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const;
1012
1013
private:
1014
std::vector<KeyPoint>* keypoints_;
1015
Mat* descriptors_;
1016
Pyramid* evolution_;
1017
};
1018
1019
class MSURF_Descriptor_64_Invoker : public ParallelLoopBody
1020
{
1021
public:
1022
MSURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, Pyramid& evolution)
1023
: keypoints_(&kpts)
1024
, descriptors_(&desc)
1025
, evolution_(&evolution)
1026
{
1027
}
1028
1029
void operator() (const Range& range) const CV_OVERRIDE
1030
{
1031
for (int i = range.start; i < range.end; i++)
1032
{
1033
Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols);
1034
}
1035
}
1036
1037
void Get_MSURF_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const;
1038
1039
private:
1040
std::vector<KeyPoint>* keypoints_;
1041
Mat* descriptors_;
1042
Pyramid* evolution_;
1043
};
1044
1045
class Upright_MLDB_Full_Descriptor_Invoker : public ParallelLoopBody
1046
{
1047
public:
1048
Upright_MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, Pyramid& evolution, AKAZEOptions& options)
1049
: keypoints_(&kpts)
1050
, descriptors_(&desc)
1051
, evolution_(&evolution)
1052
, options_(&options)
1053
{
1054
}
1055
1056
void operator() (const Range& range) const CV_OVERRIDE
1057
{
1058
for (int i = range.start; i < range.end; i++)
1059
{
1060
Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols);
1061
}
1062
}
1063
1064
void Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc, int desc_size) const;
1065
1066
private:
1067
std::vector<KeyPoint>* keypoints_;
1068
Mat* descriptors_;
1069
Pyramid* evolution_;
1070
AKAZEOptions* options_;
1071
};
1072
1073
class Upright_MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody
1074
{
1075
public:
1076
Upright_MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts,
1077
Mat& desc,
1078
Pyramid& evolution,
1079
AKAZEOptions& options,
1080
Mat descriptorSamples,
1081
Mat descriptorBits)
1082
: keypoints_(&kpts)
1083
, descriptors_(&desc)
1084
, evolution_(&evolution)
1085
, options_(&options)
1086
, descriptorSamples_(descriptorSamples)
1087
, descriptorBits_(descriptorBits)
1088
{
1089
}
1090
1091
void operator() (const Range& range) const CV_OVERRIDE
1092
{
1093
for (int i = range.start; i < range.end; i++)
1094
{
1095
Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols);
1096
}
1097
}
1098
1099
void Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc, int desc_size) const;
1100
1101
private:
1102
std::vector<KeyPoint>* keypoints_;
1103
Mat* descriptors_;
1104
Pyramid* evolution_;
1105
AKAZEOptions* options_;
1106
1107
Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from.
1108
Mat descriptorBits_;
1109
};
1110
1111
class MLDB_Full_Descriptor_Invoker : public ParallelLoopBody
1112
{
1113
public:
1114
MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, Pyramid& evolution, AKAZEOptions& options)
1115
: keypoints_(&kpts)
1116
, descriptors_(&desc)
1117
, evolution_(&evolution)
1118
, options_(&options)
1119
{
1120
}
1121
1122
void operator() (const Range& range) const CV_OVERRIDE
1123
{
1124
for (int i = range.start; i < range.end; i++)
1125
{
1126
Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols);
1127
}
1128
}
1129
1130
void Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc, int desc_size) const;
1131
void MLDB_Fill_Values(float* values, int sample_step, int level,
1132
float xf, float yf, float co, float si, float scale) const;
1133
void MLDB_Binary_Comparisons(float* values, unsigned char* desc,
1134
int count, int& dpos) const;
1135
1136
private:
1137
std::vector<KeyPoint>* keypoints_;
1138
Mat* descriptors_;
1139
Pyramid* evolution_;
1140
AKAZEOptions* options_;
1141
};
1142
1143
class MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody
1144
{
1145
public:
1146
MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts,
1147
Mat& desc,
1148
Pyramid& evolution,
1149
AKAZEOptions& options,
1150
Mat descriptorSamples,
1151
Mat descriptorBits)
1152
: keypoints_(&kpts)
1153
, descriptors_(&desc)
1154
, evolution_(&evolution)
1155
, options_(&options)
1156
, descriptorSamples_(descriptorSamples)
1157
, descriptorBits_(descriptorBits)
1158
{
1159
}
1160
1161
void operator() (const Range& range) const CV_OVERRIDE
1162
{
1163
for (int i = range.start; i < range.end; i++)
1164
{
1165
Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols);
1166
}
1167
}
1168
1169
void Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc, int desc_size) const;
1170
1171
private:
1172
std::vector<KeyPoint>* keypoints_;
1173
Mat* descriptors_;
1174
Pyramid* evolution_;
1175
AKAZEOptions* options_;
1176
1177
Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from.
1178
Mat descriptorBits_;
1179
};
1180
1181
/**
1182
* @brief This method computes the set of descriptors through the nonlinear scale space
1183
* @param kpts Vector of detected keypoints
1184
* @param desc Matrix to store the descriptors
1185
*/
1186
void AKAZEFeatures::Compute_Descriptors(std::vector<KeyPoint>& kpts, OutputArray descriptors)
1187
{
1188
CV_INSTRUMENT_REGION();
1189
1190
for(size_t i = 0; i < kpts.size(); i++)
1191
{
1192
CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast<int>(evolution_.size()));
1193
}
1194
1195
// Allocate memory for the matrix with the descriptors
1196
int descriptor_size = 64;
1197
int descriptor_type = CV_32FC1;
1198
if (options_.descriptor >= AKAZE::DESCRIPTOR_MLDB_UPRIGHT)
1199
{
1200
int descriptor_bits = (options_.descriptor_size == 0)
1201
? (6 + 36 + 120)*options_.descriptor_channels // the full length binary descriptor -> 486 bits
1202
: options_.descriptor_size; // the random bit selection length binary descriptor
1203
descriptor_size = divUp(descriptor_bits, 8);
1204
descriptor_type = CV_8UC1;
1205
}
1206
descriptors.create((int)kpts.size(), descriptor_size, descriptor_type);
1207
1208
Mat desc = descriptors.getMat();
1209
1210
switch (options_.descriptor)
1211
{
1212
case AKAZE::DESCRIPTOR_KAZE_UPRIGHT: // Upright descriptors, not invariant to rotation
1213
{
1214
parallel_for_(Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_));
1215
}
1216
break;
1217
case AKAZE::DESCRIPTOR_KAZE:
1218
{
1219
parallel_for_(Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_));
1220
}
1221
break;
1222
case AKAZE::DESCRIPTOR_MLDB_UPRIGHT: // Upright descriptors, not invariant to rotation
1223
{
1224
if (options_.descriptor_size == 0)
1225
parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_));
1226
else
1227
parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_));
1228
}
1229
break;
1230
case AKAZE::DESCRIPTOR_MLDB:
1231
{
1232
if (options_.descriptor_size == 0)
1233
parallel_for_(Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_));
1234
else
1235
parallel_for_(Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_));
1236
}
1237
break;
1238
}
1239
}
1240
1241
/* ************************************************************************* */
1242
/**
1243
* @brief This function samples the derivative responses Lx and Ly for the points
1244
* within the radius of 6*scale from (x0, y0), then multiply 2D Gaussian weight
1245
* @param Lx Horizontal derivative
1246
* @param Ly Vertical derivative
1247
* @param x0 X-coordinate of the center point
1248
* @param y0 Y-coordinate of the center point
1249
* @param scale The sampling step
1250
* @param resX Output array of the weighted horizontal derivative responses
1251
* @param resY Output array of the weighted vertical derivative responses
1252
*/
1253
static inline
1254
void Sample_Derivative_Response_Radius6(const Mat &Lx, const Mat &Ly,
1255
const int x0, const int y0, const int scale,
1256
float *resX, float *resY)
1257
{
1258
/* ************************************************************************* */
1259
/// Lookup table for 2d gaussian (sigma = 2.5) where (0,0) is top left and (6,6) is bottom right
1260
static const float gauss25[7][7] =
1261
{
1262
{ 0.02546481f, 0.02350698f, 0.01849125f, 0.01239505f, 0.00708017f, 0.00344629f, 0.00142946f },
1263
{ 0.02350698f, 0.02169968f, 0.01706957f, 0.01144208f, 0.00653582f, 0.00318132f, 0.00131956f },
1264
{ 0.01849125f, 0.01706957f, 0.01342740f, 0.00900066f, 0.00514126f, 0.00250252f, 0.00103800f },
1265
{ 0.01239505f, 0.01144208f, 0.00900066f, 0.00603332f, 0.00344629f, 0.00167749f, 0.00069579f },
1266
{ 0.00708017f, 0.00653582f, 0.00514126f, 0.00344629f, 0.00196855f, 0.00095820f, 0.00039744f },
1267
{ 0.00344629f, 0.00318132f, 0.00250252f, 0.00167749f, 0.00095820f, 0.00046640f, 0.00019346f },
1268
{ 0.00142946f, 0.00131956f, 0.00103800f, 0.00069579f, 0.00039744f, 0.00019346f, 0.00008024f }
1269
};
1270
static const struct gtable
1271
{
1272
float weight[109];
1273
int xidx[109];
1274
int yidx[109];
1275
1276
explicit gtable(void)
1277
{
1278
// Generate the weight and indices by one-time initialization
1279
int k = 0;
1280
for (int i = -6; i <= 6; ++i) {
1281
for (int j = -6; j <= 6; ++j) {
1282
if (i*i + j*j < 36) {
1283
CV_Assert(k < 109);
1284
weight[k] = gauss25[abs(i)][abs(j)];
1285
yidx[k] = i;
1286
xidx[k] = j;
1287
++k;
1288
}
1289
}
1290
}
1291
}
1292
} g;
1293
1294
CV_Assert(x0 - 6 * scale >= 0 && x0 + 6 * scale < Lx.cols);
1295
CV_Assert(y0 - 6 * scale >= 0 && y0 + 6 * scale < Lx.rows);
1296
1297
for (int i = 0; i < 109; i++)
1298
{
1299
int y = y0 + g.yidx[i] * scale;
1300
int x = x0 + g.xidx[i] * scale;
1301
1302
float w = g.weight[i];
1303
resX[i] = w * Lx.at<float>(y, x);
1304
resY[i] = w * Ly.at<float>(y, x);
1305
}
1306
}
1307
1308
/**
1309
* @brief This function sorts a[] by quantized float values
1310
* @param a[] Input floating point array to sort
1311
* @param n The length of a[]
1312
* @param quantum The interval to convert a[i]'s float values to integers
1313
* @param nkeys a[i] < nkeys * quantum
1314
* @param idx[] Output array of the indices: a[idx[i]] forms a sorted array
1315
* @param cum[] Output array of the starting indices of quantized floats
1316
* @note The values of a[] in [k*quantum, (k + 1)*quantum) is labeled by
1317
* the integer k, which is calculated by floor(a[i]/quantum). After sorting,
1318
* the values from a[idx[cum[k]]] to a[idx[cum[k+1]-1]] are all labeled by k.
1319
* This sorting is unstable to reduce the memory access.
1320
*/
1321
static inline
1322
void quantized_counting_sort(const float a[], const int n,
1323
const float quantum, const int nkeys,
1324
int idx[/*n*/], int cum[/*nkeys + 1*/])
1325
{
1326
memset(cum, 0, sizeof(cum[0]) * (nkeys + 1));
1327
1328
// Count up the quantized values
1329
for (int i = 0; i < n; i++)
1330
{
1331
int b = (int)(a[i] / quantum);
1332
if (b < 0 || b >= nkeys)
1333
b = 0;
1334
cum[b]++;
1335
}
1336
1337
// Compute the inclusive prefix sum i.e. the end indices; cum[nkeys] is the total
1338
for (int i = 1; i <= nkeys; i++)
1339
{
1340
cum[i] += cum[i - 1];
1341
}
1342
CV_Assert(cum[nkeys] == n);
1343
1344
// Generate the sorted indices; cum[] becomes the exclusive prefix sum i.e. the start indices of keys
1345
for (int i = 0; i < n; i++)
1346
{
1347
int b = (int)(a[i] / quantum);
1348
if (b < 0 || b >= nkeys)
1349
b = 0;
1350
idx[--cum[b]] = i;
1351
}
1352
}
1353
1354
/**
1355
* @brief This function computes the main orientation for a given keypoint
1356
* @param kpt Input keypoint
1357
* @note The orientation is computed using a similar approach as described in the
1358
* original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006
1359
*/
1360
static inline
1361
void Compute_Main_Orientation(KeyPoint& kpt, const Pyramid& evolution)
1362
{
1363
// get the right evolution level for this keypoint
1364
const MEvolution& e = evolution[kpt.class_id];
1365
// Get the information from the keypoint
1366
int scale = cvRound(0.5f * kpt.size / e.octave_ratio);
1367
int x0 = cvRound(kpt.pt.x / e.octave_ratio);
1368
int y0 = cvRound(kpt.pt.y / e.octave_ratio);
1369
1370
// Sample derivatives responses for the points within radius of 6*scale
1371
const int ang_size = 109;
1372
float resX[ang_size], resY[ang_size];
1373
Sample_Derivative_Response_Radius6(e.Lx, e.Ly, x0, y0, scale, resX, resY);
1374
1375
// Compute the angle of each gradient vector
1376
float Ang[ang_size];
1377
hal::fastAtan2(resY, resX, Ang, ang_size, false);
1378
1379
// Sort by the angles; angles are labeled by slices of 0.15 radian
1380
const int slices = 42;
1381
const float ang_step = (float)(2.0 * CV_PI / slices);
1382
int slice[slices + 1];
1383
int sorted_idx[ang_size];
1384
quantized_counting_sort(Ang, ang_size, ang_step, slices, sorted_idx, slice);
1385
1386
// Find the main angle by sliding a window of 7-slice size(=PI/3) around the keypoint
1387
const int win = 7;
1388
1389
float maxX = 0.0f, maxY = 0.0f;
1390
for (int i = slice[0]; i < slice[win]; i++) {
1391
const int idx = sorted_idx[i];
1392
maxX += resX[idx];
1393
maxY += resY[idx];
1394
}
1395
float maxNorm = maxX * maxX + maxY * maxY;
1396
1397
for (int sn = 1; sn <= slices - win; sn++) {
1398
1399
if (slice[sn] == slice[sn - 1] && slice[sn + win] == slice[sn + win - 1])
1400
continue; // The contents of the window didn't change; don't repeat the computation
1401
1402
float sumX = 0.0f, sumY = 0.0f;
1403
for (int i = slice[sn]; i < slice[sn + win]; i++) {
1404
const int idx = sorted_idx[i];
1405
sumX += resX[idx];
1406
sumY += resY[idx];
1407
}
1408
1409
float norm = sumX * sumX + sumY * sumY;
1410
if (norm > maxNorm)
1411
maxNorm = norm, maxX = sumX, maxY = sumY; // Found bigger one; update
1412
}
1413
1414
for (int sn = slices - win + 1; sn < slices; sn++) {
1415
int remain = sn + win - slices;
1416
1417
if (slice[sn] == slice[sn - 1] && slice[remain] == slice[remain - 1])
1418
continue;
1419
1420
float sumX = 0.0f, sumY = 0.0f;
1421
for (int i = slice[sn]; i < slice[slices]; i++) {
1422
const int idx = sorted_idx[i];
1423
sumX += resX[idx];
1424
sumY += resY[idx];
1425
}
1426
for (int i = slice[0]; i < slice[remain]; i++) {
1427
const int idx = sorted_idx[i];
1428
sumX += resX[idx];
1429
sumY += resY[idx];
1430
}
1431
1432
float norm = sumX * sumX + sumY * sumY;
1433
if (norm > maxNorm)
1434
maxNorm = norm, maxX = sumX, maxY = sumY;
1435
}
1436
1437
// Store the final result
1438
kpt.angle = fastAtan2(maxY, maxX);
1439
}
1440
1441
class ComputeKeypointOrientation : public ParallelLoopBody
1442
{
1443
public:
1444
ComputeKeypointOrientation(std::vector<KeyPoint>& kpts,
1445
const Pyramid& evolution)
1446
: keypoints_(&kpts)
1447
, evolution_(&evolution)
1448
{
1449
}
1450
1451
void operator() (const Range& range) const CV_OVERRIDE
1452
{
1453
for (int i = range.start; i < range.end; i++)
1454
{
1455
Compute_Main_Orientation((*keypoints_)[i], *evolution_);
1456
}
1457
}
1458
private:
1459
std::vector<KeyPoint>* keypoints_;
1460
const Pyramid* evolution_;
1461
};
1462
1463
/**
1464
* @brief This method computes the main orientation for a given keypoints
1465
* @param kpts Input keypoints
1466
*/
1467
void AKAZEFeatures::Compute_Keypoints_Orientation(std::vector<KeyPoint>& kpts) const
1468
{
1469
CV_INSTRUMENT_REGION();
1470
1471
parallel_for_(Range(0, (int)kpts.size()), ComputeKeypointOrientation(kpts, evolution_));
1472
}
1473
1474
/* ************************************************************************* */
1475
/**
1476
* @brief This method computes the upright descriptor (not rotation invariant) of
1477
* the provided keypoint
1478
* @param kpt Input keypoint
1479
* @param desc Descriptor vector
1480
* @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
1481
* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
1482
* ECCV 2008
1483
*/
1484
void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float *desc, int desc_size) const {
1485
1486
const int dsize = 64;
1487
CV_Assert(desc_size == dsize);
1488
1489
float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
1490
float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
1491
float sample_x = 0.0, sample_y = 0.0;
1492
int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;
1493
int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
1494
float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
1495
int scale = 0;
1496
1497
// Subregion centers for the 4x4 gaussian weighting
1498
float cx = -0.5f, cy = 0.5f;
1499
1500
const Pyramid& evolution = *evolution_;
1501
1502
// Set the descriptor size and the sample and pattern sizes
1503
sample_step = 5;
1504
pattern_size = 12;
1505
1506
// Get the information from the keypoint
1507
ratio = (float)(1 << kpt.octave);
1508
scale = cvRound(0.5f*kpt.size / ratio);
1509
const int level = kpt.class_id;
1510
const Mat Lx = evolution[level].Lx;
1511
const Mat Ly = evolution[level].Ly;
1512
yf = kpt.pt.y / ratio;
1513
xf = kpt.pt.x / ratio;
1514
1515
i = -8;
1516
1517
// Calculate descriptor for this interest point
1518
// Area of size 24 s x 24 s
1519
while (i < pattern_size) {
1520
j = -8;
1521
i = i - 4;
1522
1523
cx += 1.0f;
1524
cy = -0.5f;
1525
1526
while (j < pattern_size) {
1527
dx = dy = mdx = mdy = 0.0;
1528
cy += 1.0f;
1529
j = j - 4;
1530
1531
ky = i + sample_step;
1532
kx = j + sample_step;
1533
1534
ys = yf + (ky*scale);
1535
xs = xf + (kx*scale);
1536
1537
for (int k = i; k < i + 9; k++) {
1538
for (int l = j; l < j + 9; l++) {
1539
sample_y = k*scale + yf;
1540
sample_x = l*scale + xf;
1541
1542
//Get the gaussian weighted x and y responses
1543
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.50f*scale);
1544
1545
y1 = cvFloor(sample_y);
1546
x1 = cvFloor(sample_x);
1547
1548
y2 = y1 + 1;
1549
x2 = x1 + 1;
1550
1551
if (x1 < 0 || y1 < 0 || x2 >= Lx.cols || y2 >= Lx.rows)
1552
continue; // FIXIT Boundaries
1553
1554
fx = sample_x - x1;
1555
fy = sample_y - y1;
1556
1557
res1 = Lx.at<float>(y1, x1);
1558
res2 = Lx.at<float>(y1, x2);
1559
res3 = Lx.at<float>(y2, x1);
1560
res4 = Lx.at<float>(y2, x2);
1561
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1562
1563
res1 = Ly.at<float>(y1, x1);
1564
res2 = Ly.at<float>(y1, x2);
1565
res3 = Ly.at<float>(y2, x1);
1566
res4 = Ly.at<float>(y2, x2);
1567
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1568
1569
rx = gauss_s1*rx;
1570
ry = gauss_s1*ry;
1571
1572
// Sum the derivatives to the cumulative descriptor
1573
dx += rx;
1574
dy += ry;
1575
mdx += fabs(rx);
1576
mdy += fabs(ry);
1577
}
1578
}
1579
1580
// Add the values to the descriptor vector
1581
gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
1582
1583
desc[dcount++] = dx*gauss_s2;
1584
desc[dcount++] = dy*gauss_s2;
1585
desc[dcount++] = mdx*gauss_s2;
1586
desc[dcount++] = mdy*gauss_s2;
1587
1588
len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
1589
1590
j += 9;
1591
}
1592
1593
i += 9;
1594
}
1595
1596
CV_Assert(dcount == desc_size);
1597
1598
// convert to unit vector
1599
len = sqrt(len);
1600
1601
const float len_inv = 1.0f / len;
1602
for (i = 0; i < dsize; i++) {
1603
desc[i] *= len_inv;
1604
}
1605
}
1606
1607
/* ************************************************************************* */
1608
/**
1609
* @brief This method computes the descriptor of the provided keypoint given the
1610
* main orientation of the keypoint
1611
* @param kpt Input keypoint
1612
* @param desc Descriptor vector
1613
* @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
1614
* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
1615
* ECCV 2008
1616
*/
1617
void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, float *desc, int desc_size) const {
1618
1619
const int dsize = 64;
1620
CV_Assert(desc_size == dsize);
1621
1622
float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
1623
float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
1624
float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;
1625
float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
1626
int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;
1627
int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
1628
int scale = 0;
1629
1630
// Subregion centers for the 4x4 gaussian weighting
1631
float cx = -0.5f, cy = 0.5f;
1632
1633
const Pyramid& evolution = *evolution_;
1634
1635
// Set the descriptor size and the sample and pattern sizes
1636
sample_step = 5;
1637
pattern_size = 12;
1638
1639
// Get the information from the keypoint
1640
ratio = (float)(1 << kpt.octave);
1641
scale = cvRound(0.5f*kpt.size / ratio);
1642
angle = kpt.angle * static_cast<float>(CV_PI / 180.f);
1643
const int level = kpt.class_id;
1644
const Mat Lx = evolution[level].Lx;
1645
const Mat Ly = evolution[level].Ly;
1646
yf = kpt.pt.y / ratio;
1647
xf = kpt.pt.x / ratio;
1648
co = cos(angle);
1649
si = sin(angle);
1650
1651
i = -8;
1652
1653
// Calculate descriptor for this interest point
1654
// Area of size 24 s x 24 s
1655
while (i < pattern_size) {
1656
j = -8;
1657
i = i - 4;
1658
1659
cx += 1.0f;
1660
cy = -0.5f;
1661
1662
while (j < pattern_size) {
1663
dx = dy = mdx = mdy = 0.0;
1664
cy += 1.0f;
1665
j = j - 4;
1666
1667
ky = i + sample_step;
1668
kx = j + sample_step;
1669
1670
xs = xf + (-kx*scale*si + ky*scale*co);
1671
ys = yf + (kx*scale*co + ky*scale*si);
1672
1673
for (int k = i; k < i + 9; ++k) {
1674
for (int l = j; l < j + 9; ++l) {
1675
// Get coords of sample point on the rotated axis
1676
sample_y = yf + (l*scale*co + k*scale*si);
1677
sample_x = xf + (-l*scale*si + k*scale*co);
1678
1679
// Get the gaussian weighted x and y responses
1680
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
1681
1682
y1 = cvFloor(sample_y);
1683
x1 = cvFloor(sample_x);
1684
1685
y2 = y1 + 1;
1686
x2 = x1 + 1;
1687
1688
if (x1 < 0 || y1 < 0 || x2 >= Lx.cols || y2 >= Lx.rows)
1689
continue; // FIXIT Boundaries
1690
1691
fx = sample_x - x1;
1692
fy = sample_y - y1;
1693
1694
res1 = Lx.at<float>(y1, x1);
1695
res2 = Lx.at<float>(y1, x2);
1696
res3 = Lx.at<float>(y2, x1);
1697
res4 = Lx.at<float>(y2, x2);
1698
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1699
1700
res1 = Ly.at<float>(y1, x1);
1701
res2 = Ly.at<float>(y1, x2);
1702
res3 = Ly.at<float>(y2, x1);
1703
res4 = Ly.at<float>(y2, x2);
1704
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1705
1706
// Get the x and y derivatives on the rotated axis
1707
rry = gauss_s1*(rx*co + ry*si);
1708
rrx = gauss_s1*(-rx*si + ry*co);
1709
1710
// Sum the derivatives to the cumulative descriptor
1711
dx += rrx;
1712
dy += rry;
1713
mdx += fabs(rrx);
1714
mdy += fabs(rry);
1715
}
1716
}
1717
1718
// Add the values to the descriptor vector
1719
gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
1720
desc[dcount++] = dx*gauss_s2;
1721
desc[dcount++] = dy*gauss_s2;
1722
desc[dcount++] = mdx*gauss_s2;
1723
desc[dcount++] = mdy*gauss_s2;
1724
1725
len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
1726
1727
j += 9;
1728
}
1729
1730
i += 9;
1731
}
1732
1733
CV_Assert(dcount == desc_size);
1734
1735
// convert to unit vector
1736
len = sqrt(len);
1737
1738
const float len_inv = 1.0f / len;
1739
for (i = 0; i < dsize; i++) {
1740
desc[i] *= len_inv;
1741
}
1742
}
1743
1744
/* ************************************************************************* */
1745
/**
1746
* @brief This method computes the rupright descriptor (not rotation invariant) of
1747
* the provided keypoint
1748
* @param kpt Input keypoint
1749
* @param desc Descriptor vector
1750
*/
1751
void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc, int desc_size) const {
1752
1753
const AKAZEOptions & options = *options_;
1754
const Pyramid& evolution = *evolution_;
1755
1756
// Buffer for the M-LDB descriptor
1757
const int max_channels = 3;
1758
CV_Assert(options.descriptor_channels <= max_channels);
1759
float values[16*max_channels];
1760
1761
// Get the information from the keypoint
1762
const float ratio = (float)(1 << kpt.octave);
1763
const int scale = cvRound(0.5f*kpt.size / ratio);
1764
const int level = kpt.class_id;
1765
const Mat Lx = evolution[level].Lx;
1766
const Mat Ly = evolution[level].Ly;
1767
const Mat Lt = evolution[level].Lt;
1768
const float yf = kpt.pt.y / ratio;
1769
const float xf = kpt.pt.x / ratio;
1770
1771
// For 2x2 grid, 3x3 grid and 4x4 grid
1772
const int pattern_size = options_->descriptor_pattern_size;
1773
CV_Assert((pattern_size & 1) == 0);
1774
const int sample_step[3] = {
1775
pattern_size,
1776
divUp(pattern_size * 2, 3),
1777
divUp(pattern_size, 2)
1778
};
1779
1780
memset(desc, 0, desc_size);
1781
1782
// For the three grids
1783
int dcount1 = 0;
1784
for (int z = 0; z < 3; z++) {
1785
int dcount2 = 0;
1786
const int step = sample_step[z];
1787
for (int i = -pattern_size; i < pattern_size; i += step) {
1788
for (int j = -pattern_size; j < pattern_size; j += step) {
1789
float di = 0.0, dx = 0.0, dy = 0.0;
1790
1791
int nsamples = 0;
1792
for (int k = 0; k < step; k++) {
1793
for (int l = 0; l < step; l++) {
1794
1795
// Get the coordinates of the sample point
1796
const float sample_y = yf + (l+j)*scale;
1797
const float sample_x = xf + (k+i)*scale;
1798
1799
const int y1 = cvRound(sample_y);
1800
const int x1 = cvRound(sample_x);
1801
1802
if (y1 < 0 || y1 >= Lt.rows || x1 < 0 || x1 >= Lt.cols)
1803
continue; // Boundaries
1804
1805
const float ri = Lt.at<float>(y1, x1);
1806
const float rx = Lx.at<float>(y1, x1);
1807
const float ry = Ly.at<float>(y1, x1);
1808
1809
di += ri;
1810
dx += rx;
1811
dy += ry;
1812
nsamples++;
1813
}
1814
}
1815
1816
if (nsamples > 0)
1817
{
1818
const float nsamples_inv = 1.0f / nsamples;
1819
di *= nsamples_inv;
1820
dx *= nsamples_inv;
1821
dy *= nsamples_inv;
1822
}
1823
1824
float *val = &values[dcount2*max_channels];
1825
*(val) = di;
1826
*(val+1) = dx;
1827
*(val+2) = dy;
1828
dcount2++;
1829
}
1830
}
1831
1832
// Do binary comparison
1833
const int num = (z + 2) * (z + 2);
1834
for (int i = 0; i < num; i++) {
1835
for (int j = i + 1; j < num; j++) {
1836
const float * valI = &values[i*max_channels];
1837
const float * valJ = &values[j*max_channels];
1838
for (int k = 0; k < 3; ++k) {
1839
if (*(valI + k) > *(valJ + k)) {
1840
desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1841
}
1842
dcount1++;
1843
}
1844
}
1845
}
1846
1847
} // for (int z = 0; z < 3; z++)
1848
1849
CV_Assert(dcount1 <= desc_size*8);
1850
CV_Assert(divUp(dcount1, 8) == desc_size);
1851
}
1852
1853
void MLDB_Full_Descriptor_Invoker::MLDB_Fill_Values(float* values, int sample_step, const int level,
1854
float xf, float yf, float co, float si, float scale) const
1855
{
1856
const Pyramid& evolution = *evolution_;
1857
int pattern_size = options_->descriptor_pattern_size;
1858
int chan = options_->descriptor_channels;
1859
const Mat Lx = evolution[level].Lx;
1860
const Mat Ly = evolution[level].Ly;
1861
const Mat Lt = evolution[level].Lt;
1862
1863
const Size size = Lt.size();
1864
CV_Assert(size == Lx.size());
1865
CV_Assert(size == Ly.size());
1866
1867
int valpos = 0;
1868
for (int i = -pattern_size; i < pattern_size; i += sample_step) {
1869
for (int j = -pattern_size; j < pattern_size; j += sample_step) {
1870
float di = 0.0f, dx = 0.0f, dy = 0.0f;
1871
1872
int nsamples = 0;
1873
for (int k = i; k < i + sample_step; k++) {
1874
for (int l = j; l < j + sample_step; l++) {
1875
float sample_y = yf + (l*co * scale + k*si*scale);
1876
float sample_x = xf + (-l*si * scale + k*co*scale);
1877
1878
int y1 = cvRound(sample_y);
1879
int x1 = cvRound(sample_x);
1880
1881
if (y1 < 0 || y1 >= Lt.rows || x1 < 0 || x1 >= Lt.cols)
1882
continue; // Boundaries
1883
1884
float ri = Lt.at<float>(y1, x1);
1885
di += ri;
1886
1887
if(chan > 1) {
1888
float rx = Lx.at<float>(y1, x1);
1889
float ry = Ly.at<float>(y1, x1);
1890
if (chan == 2) {
1891
dx += sqrtf(rx*rx + ry*ry);
1892
}
1893
else {
1894
float rry = rx*co + ry*si;
1895
float rrx = -rx*si + ry*co;
1896
dx += rrx;
1897
dy += rry;
1898
}
1899
}
1900
nsamples++;
1901
}
1902
}
1903
1904
if (nsamples > 0)
1905
{
1906
const float nsamples_inv = 1.0f / nsamples;
1907
di *= nsamples_inv;
1908
dx *= nsamples_inv;
1909
dy *= nsamples_inv;
1910
}
1911
1912
values[valpos] = di;
1913
if (chan > 1) {
1914
values[valpos + 1] = dx;
1915
}
1916
if (chan > 2) {
1917
values[valpos + 2] = dy;
1918
}
1919
valpos += chan;
1920
}
1921
}
1922
}
1923
1924
void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsigned char* desc,
1925
int count, int& dpos) const {
1926
int chan = options_->descriptor_channels;
1927
int* ivalues = (int*) values;
1928
for(int i = 0; i < count * chan; i++) {
1929
ivalues[i] = CV_TOGGLE_FLT(ivalues[i]);
1930
}
1931
1932
for(int pos = 0; pos < chan; pos++) {
1933
for (int i = 0; i < count; i++) {
1934
int ival = ivalues[chan * i + pos];
1935
for (int j = i + 1; j < count; j++) {
1936
if (ival > ivalues[chan * j + pos]) {
1937
desc[dpos >> 3] |= (1 << (dpos & 7));
1938
}
1939
dpos++;
1940
}
1941
}
1942
}
1943
}
1944
1945
/* ************************************************************************* */
1946
/**
1947
* @brief This method computes the descriptor of the provided keypoint given the
1948
* main orientation of the keypoint
1949
* @param kpt Input keypoint
1950
* @param desc Descriptor vector
1951
*/
1952
void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc, int desc_size) const {
1953
1954
const int max_channels = 3;
1955
CV_Assert(options_->descriptor_channels <= max_channels);
1956
const int pattern_size = options_->descriptor_pattern_size;
1957
1958
float values[16*max_channels];
1959
CV_Assert((pattern_size & 1) == 0);
1960
//const double size_mult[3] = {1, 2.0/3.0, 1.0/2.0};
1961
const int sample_step[3] = { // static_cast<int>(ceil(pattern_size * size_mult[lvl]))
1962
pattern_size,
1963
divUp(pattern_size * 2, 3),
1964
divUp(pattern_size, 2)
1965
};
1966
1967
float ratio = (float)(1 << kpt.octave);
1968
float scale = (float)cvRound(0.5f*kpt.size / ratio);
1969
float xf = kpt.pt.x / ratio;
1970
float yf = kpt.pt.y / ratio;
1971
float angle = kpt.angle * static_cast<float>(CV_PI / 180.f);
1972
float co = cos(angle);
1973
float si = sin(angle);
1974
1975
memset(desc, 0, desc_size);
1976
1977
int dpos = 0;
1978
for(int lvl = 0; lvl < 3; lvl++)
1979
{
1980
int val_count = (lvl + 2) * (lvl + 2);
1981
MLDB_Fill_Values(values, sample_step[lvl], kpt.class_id, xf, yf, co, si, scale);
1982
MLDB_Binary_Comparisons(values, desc, val_count, dpos);
1983
}
1984
1985
CV_Assert(dpos == 486);
1986
CV_Assert(divUp(dpos, 8) == desc_size);
1987
}
1988
1989
/* ************************************************************************* */
1990
/**
1991
* @brief This method computes the M-LDB descriptor of the provided keypoint given the
1992
* main orientation of the keypoint. The descriptor is computed based on a subset of
1993
* the bits of the whole descriptor
1994
* @param kpt Input keypoint
1995
* @param desc Descriptor vector
1996
*/
1997
void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc, int desc_size) const {
1998
1999
float rx = 0.f, ry = 0.f;
2000
float sample_x = 0.f, sample_y = 0.f;
2001
2002
const AKAZEOptions & options = *options_;
2003
const Pyramid& evolution = *evolution_;
2004
2005
// Get the information from the keypoint
2006
float ratio = (float)(1 << kpt.octave);
2007
int scale = cvRound(0.5f*kpt.size / ratio);
2008
float angle = kpt.angle * static_cast<float>(CV_PI / 180.f);
2009
const int level = kpt.class_id;
2010
const Mat Lx = evolution[level].Lx;
2011
const Mat Ly = evolution[level].Ly;
2012
const Mat Lt = evolution[level].Lt;
2013
float yf = kpt.pt.y / ratio;
2014
float xf = kpt.pt.x / ratio;
2015
float co = cos(angle);
2016
float si = sin(angle);
2017
2018
// Allocate memory for the matrix of values
2019
// Buffer for the M-LDB descriptor
2020
const int max_channels = 3;
2021
const int channels = options.descriptor_channels;
2022
CV_Assert(channels <= max_channels);
2023
float values[(4 + 9 + 16)*max_channels] = { 0 };
2024
2025
// Sample everything, but only do the comparisons
2026
const int pattern_size = options.descriptor_pattern_size;
2027
CV_Assert((pattern_size & 1) == 0);
2028
const int sample_steps[3] = {
2029
pattern_size,
2030
divUp(pattern_size * 2, 3),
2031
divUp(pattern_size, 2)
2032
};
2033
2034
for (int i = 0; i < descriptorSamples_.rows; i++) {
2035
const int *coords = descriptorSamples_.ptr<int>(i);
2036
CV_Assert(coords[0] >= 0 && coords[0] < 3);
2037
const int sample_step = sample_steps[coords[0]];
2038
float di = 0.f, dx = 0.f, dy = 0.f;
2039
2040
for (int k = coords[1]; k < coords[1] + sample_step; k++) {
2041
for (int l = coords[2]; l < coords[2] + sample_step; l++) {
2042
2043
// Get the coordinates of the sample point
2044
sample_y = yf + (l*scale*co + k*scale*si);
2045
sample_x = xf + (-l*scale*si + k*scale*co);
2046
2047
const int y1 = cvRound(sample_y);
2048
const int x1 = cvRound(sample_x);
2049
2050
if (x1 < 0 || y1 < 0 || x1 >= Lt.cols || y1 >= Lt.rows)
2051
continue; // Boundaries
2052
2053
di += Lt.at<float>(y1, x1);
2054
2055
if (options.descriptor_channels > 1) {
2056
rx = Lx.at<float>(y1, x1);
2057
ry = Ly.at<float>(y1, x1);
2058
2059
if (options.descriptor_channels == 2) {
2060
dx += sqrtf(rx*rx + ry*ry);
2061
}
2062
else if (options.descriptor_channels == 3) {
2063
// Get the x and y derivatives on the rotated axis
2064
dx += rx*co + ry*si;
2065
dy += -rx*si + ry*co;
2066
}
2067
}
2068
}
2069
}
2070
2071
float* pValues = &values[channels * i];
2072
pValues[0] = di;
2073
2074
if (channels == 2) {
2075
pValues[1] = dx;
2076
}
2077
else if (channels == 3) {
2078
pValues[1] = dx;
2079
pValues[2] = dy;
2080
}
2081
}
2082
2083
// Do the comparisons
2084
const int *comps = descriptorBits_.ptr<int>(0);
2085
2086
CV_Assert(divUp(descriptorBits_.rows, 8) == desc_size);
2087
memset(desc, 0, desc_size);
2088
2089
for (int i = 0; i<descriptorBits_.rows; i++) {
2090
if (values[comps[2 * i]] > values[comps[2 * i + 1]]) {
2091
desc[i / 8] |= (1 << (i % 8));
2092
}
2093
}
2094
}
2095
2096
/* ************************************************************************* */
2097
/**
2098
* @brief This method computes the upright (not rotation invariant) M-LDB descriptor
2099
* of the provided keypoint given the main orientation of the keypoint.
2100
* The descriptor is computed based on a subset of the bits of the whole descriptor
2101
* @param kpt Input keypoint
2102
* @param desc Descriptor vector
2103
*/
2104
void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc, int desc_size) const {
2105
2106
float di = 0.0f, dx = 0.0f, dy = 0.0f;
2107
float rx = 0.0f, ry = 0.0f;
2108
float sample_x = 0.0f, sample_y = 0.0f;
2109
int x1 = 0, y1 = 0;
2110
2111
const AKAZEOptions & options = *options_;
2112
const Pyramid& evolution = *evolution_;
2113
2114
// Get the information from the keypoint
2115
float ratio = (float)(1 << kpt.octave);
2116
int scale = cvRound(0.5f*kpt.size / ratio);
2117
const int level = kpt.class_id;
2118
const Mat Lx = evolution[level].Lx;
2119
const Mat Ly = evolution[level].Ly;
2120
const Mat Lt = evolution[level].Lt;
2121
float yf = kpt.pt.y / ratio;
2122
float xf = kpt.pt.x / ratio;
2123
2124
// Allocate memory for the matrix of values
2125
const int max_channels = 3;
2126
const int channels = options.descriptor_channels;
2127
CV_Assert(channels <= max_channels);
2128
float values[(4 + 9 + 16)*max_channels] = { 0 };
2129
2130
const int pattern_size = options.descriptor_pattern_size;
2131
CV_Assert((pattern_size & 1) == 0);
2132
const int sample_steps[3] = {
2133
pattern_size,
2134
divUp(pattern_size * 2, 3),
2135
divUp(pattern_size, 2)
2136
};
2137
2138
for (int i = 0; i < descriptorSamples_.rows; i++) {
2139
const int *coords = descriptorSamples_.ptr<int>(i);
2140
CV_Assert(coords[0] >= 0 && coords[0] < 3);
2141
int sample_step = sample_steps[coords[0]];
2142
di = 0.0f, dx = 0.0f, dy = 0.0f;
2143
2144
for (int k = coords[1]; k < coords[1] + sample_step; k++) {
2145
for (int l = coords[2]; l < coords[2] + sample_step; l++) {
2146
2147
// Get the coordinates of the sample point
2148
sample_y = yf + l*scale;
2149
sample_x = xf + k*scale;
2150
2151
y1 = cvRound(sample_y);
2152
x1 = cvRound(sample_x);
2153
2154
if (x1 < 0 || y1 < 0 || x1 >= Lt.cols || y1 >= Lt.rows)
2155
continue; // Boundaries
2156
2157
di += Lt.at<float>(y1, x1);
2158
2159
if (options.descriptor_channels > 1) {
2160
rx = Lx.at<float>(y1, x1);
2161
ry = Ly.at<float>(y1, x1);
2162
2163
if (options.descriptor_channels == 2) {
2164
dx += sqrtf(rx*rx + ry*ry);
2165
}
2166
else if (options.descriptor_channels == 3) {
2167
dx += rx;
2168
dy += ry;
2169
}
2170
}
2171
}
2172
}
2173
2174
float* pValues = &values[channels * i];
2175
pValues[0] = di;
2176
2177
if (options.descriptor_channels == 2) {
2178
pValues[1] = dx;
2179
}
2180
else if (options.descriptor_channels == 3) {
2181
pValues[1] = dx;
2182
pValues[2] = dy;
2183
}
2184
}
2185
2186
// Do the comparisons
2187
const int *comps = descriptorBits_.ptr<int>(0);
2188
2189
CV_Assert(divUp(descriptorBits_.rows, 8) == desc_size);
2190
memset(desc, 0, desc_size);
2191
2192
for (int i = 0; i<descriptorBits_.rows; i++) {
2193
if (values[comps[2 * i]] > values[comps[2 * i + 1]]) {
2194
desc[i / 8] |= (1 << (i % 8));
2195
}
2196
}
2197
}
2198
2199
/* ************************************************************************* */
2200
/**
2201
* @brief This function computes a (quasi-random) list of bits to be taken
2202
* from the full descriptor. To speed the extraction, the function creates
2203
* a list of the samples that are involved in generating at least a bit (sampleList)
2204
* and a list of the comparisons between those samples (comparisons)
2205
* @param sampleList
2206
* @param comparisons The matrix with the binary comparisons
2207
* @param nbits The number of bits of the descriptor
2208
* @param pattern_size The pattern size for the binary descriptor
2209
* @param nchannels Number of channels to consider in the descriptor (1-3)
2210
* @note The function keeps the 18 bits (3-channels by 6 comparisons) of the
2211
* coarser grid, since it provides the most robust estimations
2212
*/
2213
void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits,
2214
int pattern_size, int nchannels) {
2215
2216
int ssz = 0;
2217
for (int i = 0; i < 3; i++) {
2218
int gz = (i + 2)*(i + 2);
2219
ssz += gz*(gz - 1) / 2;
2220
}
2221
ssz *= nchannels;
2222
2223
CV_Assert(ssz == 162*nchannels);
2224
CV_Assert(nbits <= ssz && "Descriptor size can't be bigger than full descriptor (486 = 162*3 - 3 channels)");
2225
2226
// Since the full descriptor is usually under 10k elements, we pick
2227
// the selection from the full matrix. We take as many samples per
2228
// pick as the number of channels. For every pick, we
2229
// take the two samples involved and put them in the sampling list
2230
2231
Mat_<int> fullM(ssz / nchannels, 5);
2232
for (int i = 0, c = 0; i < 3; i++) {
2233
int gdiv = i + 2; //grid divisions, per row
2234
int gsz = gdiv*gdiv;
2235
int psz = divUp(2*pattern_size, gdiv);
2236
2237
for (int j = 0; j < gsz; j++) {
2238
for (int k = j + 1; k < gsz; k++, c++) {
2239
fullM(c, 0) = i;
2240
fullM(c, 1) = psz*(j % gdiv) - pattern_size;
2241
fullM(c, 2) = psz*(j / gdiv) - pattern_size;
2242
fullM(c, 3) = psz*(k % gdiv) - pattern_size;
2243
fullM(c, 4) = psz*(k / gdiv) - pattern_size;
2244
}
2245
}
2246
}
2247
2248
RNG rng(1024);
2249
const int npicks = divUp(nbits, nchannels);
2250
Mat_<int> comps = Mat_<int>(nchannels * npicks, 2);
2251
comps = 1000;
2252
2253
// Select some samples. A sample includes all channels
2254
int count = 0;
2255
Mat_<int> samples(29, 3);
2256
Mat_<int> fullcopy = fullM.clone();
2257
samples = -1;
2258
2259
for (int i = 0; i < npicks; i++) {
2260
int k = rng(fullM.rows - i);
2261
if (i < 6) {
2262
// Force use of the coarser grid values and comparisons
2263
k = i;
2264
}
2265
2266
bool n = true;
2267
2268
for (int j = 0; j < count; j++) {
2269
if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 1) && samples(j, 2) == fullcopy(k, 2)) {
2270
n = false;
2271
comps(i*nchannels, 0) = nchannels*j;
2272
comps(i*nchannels + 1, 0) = nchannels*j + 1;
2273
comps(i*nchannels + 2, 0) = nchannels*j + 2;
2274
break;
2275
}
2276
}
2277
2278
if (n) {
2279
samples(count, 0) = fullcopy(k, 0);
2280
samples(count, 1) = fullcopy(k, 1);
2281
samples(count, 2) = fullcopy(k, 2);
2282
comps(i*nchannels, 0) = nchannels*count;
2283
comps(i*nchannels + 1, 0) = nchannels*count + 1;
2284
comps(i*nchannels + 2, 0) = nchannels*count + 2;
2285
count++;
2286
}
2287
2288
n = true;
2289
for (int j = 0; j < count; j++) {
2290
if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 3) && samples(j, 2) == fullcopy(k, 4)) {
2291
n = false;
2292
comps(i*nchannels, 1) = nchannels*j;
2293
comps(i*nchannels + 1, 1) = nchannels*j + 1;
2294
comps(i*nchannels + 2, 1) = nchannels*j + 2;
2295
break;
2296
}
2297
}
2298
2299
if (n) {
2300
samples(count, 0) = fullcopy(k, 0);
2301
samples(count, 1) = fullcopy(k, 3);
2302
samples(count, 2) = fullcopy(k, 4);
2303
comps(i*nchannels, 1) = nchannels*count;
2304
comps(i*nchannels + 1, 1) = nchannels*count + 1;
2305
comps(i*nchannels + 2, 1) = nchannels*count + 2;
2306
count++;
2307
}
2308
2309
Mat tmp = fullcopy.row(k);
2310
fullcopy.row(fullcopy.rows - i - 1).copyTo(tmp);
2311
}
2312
2313
sampleList = samples.rowRange(0, count).clone();
2314
comparisons = comps.rowRange(0, nbits).clone();
2315
}
2316
2317
}
2318
2319