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 
15 #include <iostream>
16 
17 // Namespaces
18 namespace cv
19 {
20 using namespace std;
21 
22 /* ************************************************************************* */
23 /**
24  * @brief AKAZEFeatures constructor with input options
25  * @param options AKAZEFeatures configuration options
26  * @note This constructor allocates memory for the nonlinear scale space
27  */
AKAZEFeatures(const AKAZEOptions & options)28 AKAZEFeatures::AKAZEFeatures(const AKAZEOptions& options) : options_(options) {
29 
30   ncycles_ = 0;
31   reordering_ = true;
32 
33   if (options_.descriptor_size > 0 && options_.descriptor >= AKAZE::DESCRIPTOR_MLDB_UPRIGHT) {
34     generateDescriptorSubsample(descriptorSamples_, descriptorBits_, options_.descriptor_size,
35                                 options_.descriptor_pattern_size, options_.descriptor_channels);
36   }
37 
38   Allocate_Memory_Evolution();
39 }
40 
41 /* ************************************************************************* */
42 /**
43  * @brief This method allocates the memory for the nonlinear diffusion evolution
44  */
Allocate_Memory_Evolution(void)45 void AKAZEFeatures::Allocate_Memory_Evolution(void) {
46 
47   float rfactor = 0.0f;
48   int level_height = 0, level_width = 0;
49 
50   // Allocate the dimension of the matrices for the evolution
51   for (int i = 0, power = 1; i <= options_.omax - 1; i++, power *= 2) {
52     rfactor = 1.0f / power;
53     level_height = (int)(options_.img_height*rfactor);
54     level_width = (int)(options_.img_width*rfactor);
55 
56     // Smallest possible octave and allow one scale if the image is small
57     if ((level_width < 80 || level_height < 40) && i != 0) {
58       options_.omax = i;
59       break;
60     }
61 
62     for (int j = 0; j < options_.nsublevels; j++) {
63       TEvolution step;
64       step.Lx = Mat::zeros(level_height, level_width, CV_32F);
65       step.Ly = Mat::zeros(level_height, level_width, CV_32F);
66       step.Lxx = Mat::zeros(level_height, level_width, CV_32F);
67       step.Lxy = Mat::zeros(level_height, level_width, CV_32F);
68       step.Lyy = Mat::zeros(level_height, level_width, CV_32F);
69       step.Lt = Mat::zeros(level_height, level_width, CV_32F);
70       step.Ldet = Mat::zeros(level_height, level_width, CV_32F);
71       step.Lsmooth = Mat::zeros(level_height, level_width, CV_32F);
72       step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i);
73       step.sigma_size = fRound(step.esigma);
74       step.etime = 0.5f*(step.esigma*step.esigma);
75       step.octave = i;
76       step.sublevel = j;
77       evolution_.push_back(step);
78     }
79   }
80 
81   // Allocate memory for the number of cycles and time steps
82   for (size_t i = 1; i < evolution_.size(); i++) {
83     int naux = 0;
84     vector<float> tau;
85     float ttime = 0.0f;
86     ttime = evolution_[i].etime - evolution_[i - 1].etime;
87     naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau);
88     nsteps_.push_back(naux);
89     tsteps_.push_back(tau);
90     ncycles_++;
91   }
92 }
93 
94 /* ************************************************************************* */
95 /**
96  * @brief This method creates the nonlinear scale space for a given image
97  * @param img Input image for which the nonlinear scale space needs to be created
98  * @return 0 if the nonlinear scale space was created successfully, -1 otherwise
99  */
Create_Nonlinear_Scale_Space(const Mat & img)100 int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img)
101 {
102   CV_Assert(evolution_.size() > 0);
103 
104   // Copy the original image to the first level of the evolution
105   img.copyTo(evolution_[0].Lt);
106   gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset);
107   evolution_[0].Lt.copyTo(evolution_[0].Lsmooth);
108 
109   // Allocate memory for the flow and step images
110   Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);
111   Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);
112 
113   // First compute the kcontrast factor
114   options_.kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0);
115 
116   // Now generate the rest of evolution levels
117   for (size_t i = 1; i < evolution_.size(); i++) {
118 
119     if (evolution_[i].octave > evolution_[i - 1].octave) {
120       halfsample_image(evolution_[i - 1].Lt, evolution_[i].Lt);
121       options_.kcontrast = options_.kcontrast*0.75f;
122 
123       // Allocate memory for the resized flow and step images
124       Lflow = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F);
125       Lstep = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F);
126     }
127     else {
128       evolution_[i - 1].Lt.copyTo(evolution_[i].Lt);
129     }
130 
131     gaussian_2D_convolution(evolution_[i].Lt, evolution_[i].Lsmooth, 0, 0, 1.0f);
132 
133     // Compute the Gaussian derivatives Lx and Ly
134     image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Lx, 1, 0);
135     image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Ly, 0, 1);
136 
137     // Compute the conductivity equation
138     switch (options_.diffusivity) {
139       case KAZE::DIFF_PM_G1:
140         pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
141       break;
142       case KAZE::DIFF_PM_G2:
143         pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
144       break;
145       case KAZE::DIFF_WEICKERT:
146         weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
147       break;
148       case KAZE::DIFF_CHARBONNIER:
149         charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);
150       break;
151       default:
152         CV_Error(options_.diffusivity, "Diffusivity is not supported");
153       break;
154     }
155 
156     // Perform FED n inner steps
157     for (int j = 0; j < nsteps_[i - 1]; j++) {
158       nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]);
159     }
160   }
161 
162   return 0;
163 }
164 
165 /* ************************************************************************* */
166 /**
167  * @brief This method selects interesting keypoints through the nonlinear scale space
168  * @param kpts Vector of detected keypoints
169  */
Feature_Detection(std::vector<KeyPoint> & kpts)170 void AKAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts)
171 {
172   kpts.clear();
173   Compute_Determinant_Hessian_Response();
174   Find_Scale_Space_Extrema(kpts);
175   Do_Subpixel_Refinement(kpts);
176 }
177 
178 /* ************************************************************************* */
179 class MultiscaleDerivativesAKAZEInvoker : public ParallelLoopBody
180 {
181 public:
MultiscaleDerivativesAKAZEInvoker(std::vector<TEvolution> & ev,const AKAZEOptions & opt)182     explicit MultiscaleDerivativesAKAZEInvoker(std::vector<TEvolution>& ev, const AKAZEOptions& opt)
183     : evolution_(&ev)
184     , options_(opt)
185   {
186   }
187 
operator ()(const Range & range) const188   void operator()(const Range& range) const
189   {
190     std::vector<TEvolution>& evolution = *evolution_;
191 
192     for (int i = range.start; i < range.end; i++)
193     {
194         float ratio = (float)fastpow(2, evolution[i].octave);
195       int sigma_size_ = fRound(evolution[i].esigma * options_.derivative_factor / ratio);
196 
197       compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, sigma_size_);
198       compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, sigma_size_);
199       compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, sigma_size_);
200       compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, sigma_size_);
201       compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, sigma_size_);
202 
203       evolution[i].Lx = evolution[i].Lx*((sigma_size_));
204       evolution[i].Ly = evolution[i].Ly*((sigma_size_));
205       evolution[i].Lxx = evolution[i].Lxx*((sigma_size_)*(sigma_size_));
206       evolution[i].Lxy = evolution[i].Lxy*((sigma_size_)*(sigma_size_));
207       evolution[i].Lyy = evolution[i].Lyy*((sigma_size_)*(sigma_size_));
208     }
209   }
210 
211 private:
212   std::vector<TEvolution>*  evolution_;
213   AKAZEOptions              options_;
214 };
215 
216 /* ************************************************************************* */
217 /**
218  * @brief This method computes the multiscale derivatives for the nonlinear scale space
219  */
Compute_Multiscale_Derivatives(void)220 void AKAZEFeatures::Compute_Multiscale_Derivatives(void)
221 {
222   parallel_for_(Range(0, (int)evolution_.size()),
223                                         MultiscaleDerivativesAKAZEInvoker(evolution_, options_));
224 }
225 
226 /* ************************************************************************* */
227 /**
228  * @brief This method computes the feature detector response for the nonlinear scale space
229  * @note We use the Hessian determinant as the feature detector response
230  */
Compute_Determinant_Hessian_Response(void)231 void AKAZEFeatures::Compute_Determinant_Hessian_Response(void) {
232 
233   // Firstly compute the multiscale derivatives
234   Compute_Multiscale_Derivatives();
235 
236   for (size_t i = 0; i < evolution_.size(); i++)
237   {
238     for (int ix = 0; ix < evolution_[i].Ldet.rows; ix++)
239     {
240       for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++)
241       {
242         float lxx = *(evolution_[i].Lxx.ptr<float>(ix)+jx);
243         float lxy = *(evolution_[i].Lxy.ptr<float>(ix)+jx);
244         float lyy = *(evolution_[i].Lyy.ptr<float>(ix)+jx);
245         *(evolution_[i].Ldet.ptr<float>(ix)+jx) = (lxx*lyy - lxy*lxy);
246       }
247     }
248   }
249 }
250 
251 /* ************************************************************************* */
252 /**
253  * @brief This method finds extrema in the nonlinear scale space
254  * @param kpts Vector of detected keypoints
255  */
Find_Scale_Space_Extrema(std::vector<KeyPoint> & kpts)256 void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector<KeyPoint>& kpts)
257 {
258 
259   float value = 0.0;
260   float dist = 0.0, ratio = 0.0, smax = 0.0;
261   int npoints = 0, id_repeated = 0;
262   int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0;
263   bool is_extremum = false, is_repeated = false, is_out = false;
264   KeyPoint point;
265   vector<KeyPoint> kpts_aux;
266 
267   // Set maximum size
268   if (options_.descriptor == AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_MLDB) {
269     smax = 10.0f*sqrtf(2.0f);
270   }
271   else if (options_.descriptor == AKAZE::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_KAZE) {
272     smax = 12.0f*sqrtf(2.0f);
273   }
274 
275   for (size_t i = 0; i < evolution_.size(); i++) {
276     float* prev = evolution_[i].Ldet.ptr<float>(0);
277     float* curr = evolution_[i].Ldet.ptr<float>(1);
278     for (int ix = 1; ix < evolution_[i].Ldet.rows - 1; ix++) {
279       float* next = evolution_[i].Ldet.ptr<float>(ix + 1);
280 
281       for (int jx = 1; jx < evolution_[i].Ldet.cols - 1; jx++) {
282         is_extremum = false;
283         is_repeated = false;
284         is_out = false;
285         value = *(evolution_[i].Ldet.ptr<float>(ix)+jx);
286 
287         // Filter the points with the detector threshold
288         if (value > options_.dthreshold && value >= options_.min_dthreshold &&
289             value > curr[jx-1] &&
290             value > curr[jx+1] &&
291             value > prev[jx-1] &&
292             value > prev[jx] &&
293             value > prev[jx+1] &&
294             value > next[jx-1] &&
295             value > next[jx] &&
296             value > next[jx+1]) {
297 
298           is_extremum = true;
299           point.response = fabs(value);
300           point.size = evolution_[i].esigma*options_.derivative_factor;
301           point.octave = (int)evolution_[i].octave;
302           point.class_id = (int)i;
303           ratio = (float)fastpow(2, point.octave);
304           sigma_size_ = fRound(point.size / ratio);
305           point.pt.x = static_cast<float>(jx);
306           point.pt.y = static_cast<float>(ix);
307 
308           // Compare response with the same and lower scale
309           for (size_t ik = 0; ik < kpts_aux.size(); ik++) {
310 
311             if ((point.class_id - 1) == kpts_aux[ik].class_id ||
312                 point.class_id == kpts_aux[ik].class_id) {
313               float distx = point.pt.x*ratio - kpts_aux[ik].pt.x;
314               float disty = point.pt.y*ratio - kpts_aux[ik].pt.y;
315               dist = distx * distx + disty * disty;
316               if (dist <= point.size * point.size) {
317                 if (point.response > kpts_aux[ik].response) {
318                   id_repeated = (int)ik;
319                   is_repeated = true;
320                 }
321                 else {
322                   is_extremum = false;
323                 }
324                 break;
325               }
326             }
327           }
328 
329           // Check out of bounds
330           if (is_extremum == true) {
331 
332             // Check that the point is under the image limits for the descriptor computation
333             left_x = fRound(point.pt.x - smax*sigma_size_) - 1;
334             right_x = fRound(point.pt.x + smax*sigma_size_) + 1;
335             up_y = fRound(point.pt.y - smax*sigma_size_) - 1;
336             down_y = fRound(point.pt.y + smax*sigma_size_) + 1;
337 
338             if (left_x < 0 || right_x >= evolution_[i].Ldet.cols ||
339                 up_y < 0 || down_y >= evolution_[i].Ldet.rows) {
340               is_out = true;
341             }
342 
343             if (is_out == false) {
344               if (is_repeated == false) {
345                 point.pt.x *= ratio;
346                 point.pt.y *= ratio;
347                 kpts_aux.push_back(point);
348                 npoints++;
349               }
350               else {
351                 point.pt.x *= ratio;
352                 point.pt.y *= ratio;
353                 kpts_aux[id_repeated] = point;
354               }
355             } // if is_out
356           } //if is_extremum
357         }
358       } // for jx
359       prev = curr;
360       curr = next;
361     } // for ix
362   } // for i
363 
364   // Now filter points with the upper scale level
365   for (size_t i = 0; i < kpts_aux.size(); i++) {
366 
367     is_repeated = false;
368     const KeyPoint& pt = kpts_aux[i];
369     for (size_t j = i + 1; j < kpts_aux.size(); j++) {
370 
371       // Compare response with the upper scale
372       if ((pt.class_id + 1) == kpts_aux[j].class_id) {
373         float distx = pt.pt.x - kpts_aux[j].pt.x;
374         float disty = pt.pt.y - kpts_aux[j].pt.y;
375         dist = distx * distx + disty * disty;
376         if (dist <= pt.size * pt.size) {
377           if (pt.response < kpts_aux[j].response) {
378             is_repeated = true;
379             break;
380           }
381         }
382       }
383     }
384 
385     if (is_repeated == false)
386       kpts.push_back(pt);
387   }
388 }
389 
390 /* ************************************************************************* */
391 /**
392  * @brief This method performs subpixel refinement of the detected keypoints
393  * @param kpts Vector of detected keypoints
394  */
Do_Subpixel_Refinement(std::vector<KeyPoint> & kpts)395 void AKAZEFeatures::Do_Subpixel_Refinement(std::vector<KeyPoint>& kpts)
396 {
397   float Dx = 0.0, Dy = 0.0, ratio = 0.0;
398   float Dxx = 0.0, Dyy = 0.0, Dxy = 0.0;
399   int x = 0, y = 0;
400   Matx22f A(0, 0, 0, 0);
401   Vec2f b(0, 0);
402   Vec2f dst(0, 0);
403 
404   for (size_t i = 0; i < kpts.size(); i++) {
405     ratio = (float)fastpow(2, kpts[i].octave);
406     x = fRound(kpts[i].pt.x / ratio);
407     y = fRound(kpts[i].pt.y / ratio);
408 
409     // Compute the gradient
410     Dx = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x + 1)
411         - *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x - 1));
412     Dy = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x)
413         - *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x));
414 
415     // Compute the Hessian
416     Dxx = (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x + 1)
417         + *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x - 1)
418         - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x)));
419 
420     Dyy = (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x)
421         + *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x)
422         - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x)));
423 
424     Dxy = (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x + 1)
425         + (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x - 1)))
426         - (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x + 1)
427         + (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x - 1)));
428 
429     // Solve the linear system
430     A(0, 0) = Dxx;
431     A(1, 1) = Dyy;
432     A(0, 1) = A(1, 0) = Dxy;
433     b(0) = -Dx;
434     b(1) = -Dy;
435 
436     solve(A, b, dst, DECOMP_LU);
437 
438     if (fabs(dst(0)) <= 1.0f && fabs(dst(1)) <= 1.0f) {
439         kpts[i].pt.x = x + dst(0);
440       kpts[i].pt.y = y + dst(1);
441       int power = fastpow(2, evolution_[kpts[i].class_id].octave);
442       kpts[i].pt.x *= power;
443       kpts[i].pt.y *= power;
444       kpts[i].angle = 0.0;
445 
446       // In OpenCV the size of a keypoint its the diameter
447       kpts[i].size *= 2.0f;
448     }
449     // Delete the point since its not stable
450     else {
451       kpts.erase(kpts.begin() + i);
452       i--;
453     }
454   }
455 }
456 
457 /* ************************************************************************* */
458 
459 class SURF_Descriptor_Upright_64_Invoker : public ParallelLoopBody
460 {
461 public:
SURF_Descriptor_Upright_64_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution)462   SURF_Descriptor_Upright_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution)
463     : keypoints_(&kpts)
464     , descriptors_(&desc)
465     , evolution_(&evolution)
466   {
467   }
468 
operator ()(const Range & range) const469   void operator() (const Range& range) const
470   {
471     for (int i = range.start; i < range.end; i++)
472     {
473       Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr<float>(i));
474     }
475   }
476 
477   void Get_SURF_Descriptor_Upright_64(const KeyPoint& kpt, float* desc) const;
478 
479 private:
480   std::vector<KeyPoint>* keypoints_;
481   Mat*                   descriptors_;
482   std::vector<TEvolution>*   evolution_;
483 };
484 
485 class SURF_Descriptor_64_Invoker : public ParallelLoopBody
486 {
487 public:
SURF_Descriptor_64_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution)488   SURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution)
489     : keypoints_(&kpts)
490     , descriptors_(&desc)
491     , evolution_(&evolution)
492   {
493   }
494 
operator ()(const Range & range) const495   void operator()(const Range& range) const
496   {
497     for (int i = range.start; i < range.end; i++)
498     {
499       AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_);
500       Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i));
501     }
502   }
503 
504   void Get_SURF_Descriptor_64(const KeyPoint& kpt, float* desc) const;
505 
506 private:
507   std::vector<KeyPoint>* keypoints_;
508   Mat*                   descriptors_;
509   std::vector<TEvolution>*   evolution_;
510 };
511 
512 class MSURF_Upright_Descriptor_64_Invoker : public ParallelLoopBody
513 {
514 public:
MSURF_Upright_Descriptor_64_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution)515   MSURF_Upright_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution)
516     : keypoints_(&kpts)
517     , descriptors_(&desc)
518     , evolution_(&evolution)
519   {
520   }
521 
operator ()(const Range & range) const522   void operator()(const Range& range) const
523   {
524     for (int i = range.start; i < range.end; i++)
525     {
526       Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i));
527     }
528   }
529 
530   void Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const;
531 
532 private:
533   std::vector<KeyPoint>* keypoints_;
534   Mat*                   descriptors_;
535   std::vector<TEvolution>*   evolution_;
536 };
537 
538 class MSURF_Descriptor_64_Invoker : public ParallelLoopBody
539 {
540 public:
MSURF_Descriptor_64_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution)541   MSURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution)
542     : keypoints_(&kpts)
543     , descriptors_(&desc)
544     , evolution_(&evolution)
545   {
546   }
547 
operator ()(const Range & range) const548   void operator() (const Range& range) const
549   {
550     for (int i = range.start; i < range.end; i++)
551     {
552       AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_);
553       Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i));
554     }
555   }
556 
557   void Get_MSURF_Descriptor_64(const KeyPoint& kpt, float* desc) const;
558 
559 private:
560   std::vector<KeyPoint>* keypoints_;
561   Mat*                   descriptors_;
562   std::vector<TEvolution>*   evolution_;
563 };
564 
565 class Upright_MLDB_Full_Descriptor_Invoker : public ParallelLoopBody
566 {
567 public:
Upright_MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution,AKAZEOptions & options)568   Upright_MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution, AKAZEOptions& options)
569     : keypoints_(&kpts)
570     , descriptors_(&desc)
571     , evolution_(&evolution)
572     , options_(&options)
573   {
574   }
575 
operator ()(const Range & range) const576   void operator() (const Range& range) const
577   {
578     for (int i = range.start; i < range.end; i++)
579     {
580       Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i));
581     }
582   }
583 
584   void Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const;
585 
586 private:
587   std::vector<KeyPoint>* keypoints_;
588   Mat*                   descriptors_;
589   std::vector<TEvolution>*   evolution_;
590   AKAZEOptions*              options_;
591 };
592 
593 class Upright_MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody
594 {
595 public:
Upright_MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution,AKAZEOptions & options,Mat descriptorSamples,Mat descriptorBits)596   Upright_MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts,
597                                          Mat& desc,
598                                          std::vector<TEvolution>& evolution,
599                                          AKAZEOptions& options,
600                                          Mat descriptorSamples,
601                                          Mat descriptorBits)
602     : keypoints_(&kpts)
603     , descriptors_(&desc)
604     , evolution_(&evolution)
605     , options_(&options)
606     , descriptorSamples_(descriptorSamples)
607     , descriptorBits_(descriptorBits)
608   {
609   }
610 
operator ()(const Range & range) const611   void operator() (const Range& range) const
612   {
613     for (int i = range.start; i < range.end; i++)
614     {
615       Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i));
616     }
617   }
618 
619   void Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const;
620 
621 private:
622   std::vector<KeyPoint>* keypoints_;
623   Mat*                   descriptors_;
624   std::vector<TEvolution>*   evolution_;
625   AKAZEOptions*              options_;
626 
627   Mat descriptorSamples_;  // List of positions in the grids to sample LDB bits from.
628   Mat descriptorBits_;
629 };
630 
631 class MLDB_Full_Descriptor_Invoker : public ParallelLoopBody
632 {
633 public:
MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution,AKAZEOptions & options)634   MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution, AKAZEOptions& options)
635     : keypoints_(&kpts)
636     , descriptors_(&desc)
637     , evolution_(&evolution)
638     , options_(&options)
639   {
640   }
641 
operator ()(const Range & range) const642   void operator() (const Range& range) const
643   {
644     for (int i = range.start; i < range.end; i++)
645     {
646       AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_);
647       Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i));
648     }
649   }
650 
651   void Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const;
652   void MLDB_Fill_Values(float* values, int sample_step, int level,
653                         float xf, float yf, float co, float si, float scale) const;
654   void MLDB_Binary_Comparisons(float* values, unsigned char* desc,
655                                int count, int& dpos) const;
656 
657 private:
658   std::vector<KeyPoint>* keypoints_;
659   Mat*                   descriptors_;
660   std::vector<TEvolution>*   evolution_;
661   AKAZEOptions*              options_;
662 };
663 
664 class MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody
665 {
666 public:
MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint> & kpts,Mat & desc,std::vector<TEvolution> & evolution,AKAZEOptions & options,Mat descriptorSamples,Mat descriptorBits)667   MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts,
668                                  Mat& desc,
669                                  std::vector<TEvolution>& evolution,
670                                  AKAZEOptions& options,
671                                  Mat descriptorSamples,
672                                  Mat descriptorBits)
673     : keypoints_(&kpts)
674     , descriptors_(&desc)
675     , evolution_(&evolution)
676     , options_(&options)
677     , descriptorSamples_(descriptorSamples)
678     , descriptorBits_(descriptorBits)
679   {
680   }
681 
operator ()(const Range & range) const682   void operator() (const Range& range) const
683   {
684     for (int i = range.start; i < range.end; i++)
685     {
686       AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_);
687       Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i));
688     }
689   }
690 
691   void Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const;
692 
693 private:
694   std::vector<KeyPoint>* keypoints_;
695   Mat*                   descriptors_;
696   std::vector<TEvolution>*   evolution_;
697   AKAZEOptions*              options_;
698 
699   Mat descriptorSamples_;  // List of positions in the grids to sample LDB bits from.
700   Mat descriptorBits_;
701 };
702 
703 /**
704  * @brief This method  computes the set of descriptors through the nonlinear scale space
705  * @param kpts Vector of detected keypoints
706  * @param desc Matrix to store the descriptors
707  */
Compute_Descriptors(std::vector<KeyPoint> & kpts,Mat & desc)708 void AKAZEFeatures::Compute_Descriptors(std::vector<KeyPoint>& kpts, Mat& desc)
709 {
710   for(size_t i = 0; i < kpts.size(); i++)
711   {
712       CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast<int>(evolution_.size()));
713   }
714 
715   // Allocate memory for the matrix with the descriptors
716   if (options_.descriptor < AKAZE::DESCRIPTOR_MLDB_UPRIGHT) {
717     desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1);
718   }
719   else {
720     // We use the full length binary descriptor -> 486 bits
721     if (options_.descriptor_size == 0) {
722       int t = (6 + 36 + 120)*options_.descriptor_channels;
723       desc = Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1);
724     }
725     else {
726       // We use the random bit selection length binary descriptor
727       desc = Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1);
728     }
729   }
730 
731   switch (options_.descriptor)
732   {
733     case AKAZE::DESCRIPTOR_KAZE_UPRIGHT: // Upright descriptors, not invariant to rotation
734     {
735       parallel_for_(Range(0, (int)kpts.size()), MSURF_Upright_Descriptor_64_Invoker(kpts, desc, evolution_));
736     }
737     break;
738     case AKAZE::DESCRIPTOR_KAZE:
739     {
740       parallel_for_(Range(0, (int)kpts.size()), MSURF_Descriptor_64_Invoker(kpts, desc, evolution_));
741     }
742     break;
743     case AKAZE::DESCRIPTOR_MLDB_UPRIGHT: // Upright descriptors, not invariant to rotation
744     {
745       if (options_.descriptor_size == 0)
746         parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_));
747       else
748         parallel_for_(Range(0, (int)kpts.size()), Upright_MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_));
749     }
750     break;
751     case AKAZE::DESCRIPTOR_MLDB:
752     {
753       if (options_.descriptor_size == 0)
754         parallel_for_(Range(0, (int)kpts.size()), MLDB_Full_Descriptor_Invoker(kpts, desc, evolution_, options_));
755       else
756         parallel_for_(Range(0, (int)kpts.size()), MLDB_Descriptor_Subset_Invoker(kpts, desc, evolution_, options_, descriptorSamples_, descriptorBits_));
757     }
758     break;
759   }
760 }
761 
762 /* ************************************************************************* */
763 /**
764  * @brief This method computes the main orientation for a given keypoint
765  * @param kpt Input keypoint
766  * @note The orientation is computed using a similar approach as described in the
767  * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006
768  */
Compute_Main_Orientation(KeyPoint & kpt,const std::vector<TEvolution> & evolution_)769 void AKAZEFeatures::Compute_Main_Orientation(KeyPoint& kpt, const std::vector<TEvolution>& evolution_)
770 {
771     /* ************************************************************************* */
772     /// Lookup table for 2d gaussian (sigma = 2.5) where (0,0) is top left and (6,6) is bottom right
773     static const float gauss25[7][7] =
774     {
775         { 0.02546481f, 0.02350698f, 0.01849125f, 0.01239505f, 0.00708017f, 0.00344629f, 0.00142946f },
776         { 0.02350698f, 0.02169968f, 0.01706957f, 0.01144208f, 0.00653582f, 0.00318132f, 0.00131956f },
777         { 0.01849125f, 0.01706957f, 0.01342740f, 0.00900066f, 0.00514126f, 0.00250252f, 0.00103800f },
778         { 0.01239505f, 0.01144208f, 0.00900066f, 0.00603332f, 0.00344629f, 0.00167749f, 0.00069579f },
779         { 0.00708017f, 0.00653582f, 0.00514126f, 0.00344629f, 0.00196855f, 0.00095820f, 0.00039744f },
780         { 0.00344629f, 0.00318132f, 0.00250252f, 0.00167749f, 0.00095820f, 0.00046640f, 0.00019346f },
781         { 0.00142946f, 0.00131956f, 0.00103800f, 0.00069579f, 0.00039744f, 0.00019346f, 0.00008024f }
782     };
783 
784   int ix = 0, iy = 0, idx = 0, s = 0, level = 0;
785   float xf = 0.0, yf = 0.0, gweight = 0.0, ratio = 0.0;
786   const int ang_size = 109;
787   float resX[ang_size], resY[ang_size], Ang[ang_size];
788   const int id[] = { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 };
789 
790   // Variables for computing the dominant direction
791   float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0;
792 
793   // Get the information from the keypoint
794   level = kpt.class_id;
795   ratio = (float)(1 << evolution_[level].octave);
796   s = fRound(0.5f*kpt.size / ratio);
797   xf = kpt.pt.x / ratio;
798   yf = kpt.pt.y / ratio;
799 
800   // Calculate derivatives responses for points within radius of 6*scale
801   for (int i = -6; i <= 6; ++i) {
802     for (int j = -6; j <= 6; ++j) {
803       if (i*i + j*j < 36) {
804         iy = fRound(yf + j*s);
805         ix = fRound(xf + i*s);
806 
807         gweight = gauss25[id[i + 6]][id[j + 6]];
808         resX[idx] = gweight*(*(evolution_[level].Lx.ptr<float>(iy)+ix));
809         resY[idx] = gweight*(*(evolution_[level].Ly.ptr<float>(iy)+ix));
810 
811         ++idx;
812       }
813     }
814   }
815   hal::fastAtan2(resY, resX, Ang, ang_size, false);
816   // Loop slides pi/3 window around feature point
817   for (ang1 = 0; ang1 < (float)(2.0 * CV_PI); ang1 += 0.15f) {
818     ang2 = (ang1 + (float)(CV_PI / 3.0) >(float)(2.0*CV_PI) ? ang1 - (float)(5.0*CV_PI / 3.0) : ang1 + (float)(CV_PI / 3.0));
819     sumX = sumY = 0.f;
820 
821     for (int k = 0; k < ang_size; ++k) {
822       // Get angle from the x-axis of the sample point
823       const float & ang = Ang[k];
824 
825       // Determine whether the point is within the window
826       if (ang1 < ang2 && ang1 < ang && ang < ang2) {
827         sumX += resX[k];
828         sumY += resY[k];
829       }
830       else if (ang2 < ang1 &&
831                ((ang > 0 && ang < ang2) || (ang > ang1 && ang < 2.0f*CV_PI))) {
832         sumX += resX[k];
833         sumY += resY[k];
834       }
835     }
836 
837     // if the vector produced from this window is longer than all
838     // previous vectors then this forms the new dominant direction
839     if (sumX*sumX + sumY*sumY > max) {
840       // store largest orientation
841       max = sumX*sumX + sumY*sumY;
842       kpt.angle = getAngle(sumX, sumY);
843     }
844   }
845 }
846 
847 /* ************************************************************************* */
848 /**
849  * @brief This method computes the upright descriptor (not rotation invariant) of
850  * the provided keypoint
851  * @param kpt Input keypoint
852  * @param desc Descriptor vector
853  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
854  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
855  * ECCV 2008
856  */
Get_MSURF_Upright_Descriptor_64(const KeyPoint & kpt,float * desc) const857 void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float *desc) const {
858 
859   float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
860   float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;
861   float sample_x = 0.0, sample_y = 0.0;
862   int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;
863   int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
864   float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
865   int scale = 0, dsize = 0, level = 0;
866 
867   // Subregion centers for the 4x4 gaussian weighting
868   float cx = -0.5f, cy = 0.5f;
869 
870   const std::vector<TEvolution>& evolution = *evolution_;
871 
872   // Set the descriptor size and the sample and pattern sizes
873   dsize = 64;
874   sample_step = 5;
875   pattern_size = 12;
876 
877   // Get the information from the keypoint
878   ratio = (float)(1 << kpt.octave);
879   scale = fRound(0.5f*kpt.size / ratio);
880   level = kpt.class_id;
881   yf = kpt.pt.y / ratio;
882   xf = kpt.pt.x / ratio;
883 
884   i = -8;
885 
886   // Calculate descriptor for this interest point
887   // Area of size 24 s x 24 s
888   while (i < pattern_size) {
889     j = -8;
890     i = i - 4;
891 
892     cx += 1.0f;
893     cy = -0.5f;
894 
895     while (j < pattern_size) {
896       dx = dy = mdx = mdy = 0.0;
897       cy += 1.0f;
898       j = j - 4;
899 
900       ky = i + sample_step;
901       kx = j + sample_step;
902 
903       ys = yf + (ky*scale);
904       xs = xf + (kx*scale);
905 
906       for (int k = i; k < i + 9; k++) {
907         for (int l = j; l < j + 9; l++) {
908           sample_y = k*scale + yf;
909           sample_x = l*scale + xf;
910 
911           //Get the gaussian weighted x and y responses
912           gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.50f*scale);
913 
914           y1 = (int)(sample_y - .5);
915           x1 = (int)(sample_x - .5);
916 
917           y2 = (int)(sample_y + .5);
918           x2 = (int)(sample_x + .5);
919 
920           fx = sample_x - x1;
921           fy = sample_y - y1;
922 
923           res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
924           res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
925           res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
926           res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
927           rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
928 
929           res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
930           res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
931           res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
932           res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
933           ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
934 
935           rx = gauss_s1*rx;
936           ry = gauss_s1*ry;
937 
938           // Sum the derivatives to the cumulative descriptor
939           dx += rx;
940           dy += ry;
941           mdx += fabs(rx);
942           mdy += fabs(ry);
943         }
944       }
945 
946       // Add the values to the descriptor vector
947       gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
948 
949       desc[dcount++] = dx*gauss_s2;
950       desc[dcount++] = dy*gauss_s2;
951       desc[dcount++] = mdx*gauss_s2;
952       desc[dcount++] = mdy*gauss_s2;
953 
954       len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
955 
956       j += 9;
957     }
958 
959     i += 9;
960   }
961 
962   // convert to unit vector
963   len = sqrt(len);
964 
965   for (i = 0; i < dsize; i++) {
966     desc[i] /= len;
967   }
968 }
969 
970 /* ************************************************************************* */
971 /**
972  * @brief This method computes the descriptor of the provided keypoint given the
973  * main orientation of the keypoint
974  * @param kpt Input keypoint
975  * @param desc Descriptor vector
976  * @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired
977  * from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,
978  * ECCV 2008
979  */
Get_MSURF_Descriptor_64(const KeyPoint & kpt,float * desc) const980 void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, float *desc) const {
981 
982   float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;
983   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;
984   float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;
985   float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;
986   int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;
987   int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;
988   int scale = 0, dsize = 0, level = 0;
989 
990   // Subregion centers for the 4x4 gaussian weighting
991   float cx = -0.5f, cy = 0.5f;
992 
993   const std::vector<TEvolution>& evolution = *evolution_;
994 
995   // Set the descriptor size and the sample and pattern sizes
996   dsize = 64;
997   sample_step = 5;
998   pattern_size = 12;
999 
1000   // Get the information from the keypoint
1001   ratio = (float)(1 << kpt.octave);
1002   scale = fRound(0.5f*kpt.size / ratio);
1003   angle = kpt.angle;
1004   level = kpt.class_id;
1005   yf = kpt.pt.y / ratio;
1006   xf = kpt.pt.x / ratio;
1007   co = cos(angle);
1008   si = sin(angle);
1009 
1010   i = -8;
1011 
1012   // Calculate descriptor for this interest point
1013   // Area of size 24 s x 24 s
1014   while (i < pattern_size) {
1015     j = -8;
1016     i = i - 4;
1017 
1018     cx += 1.0f;
1019     cy = -0.5f;
1020 
1021     while (j < pattern_size) {
1022       dx = dy = mdx = mdy = 0.0;
1023       cy += 1.0f;
1024       j = j - 4;
1025 
1026       ky = i + sample_step;
1027       kx = j + sample_step;
1028 
1029       xs = xf + (-kx*scale*si + ky*scale*co);
1030       ys = yf + (kx*scale*co + ky*scale*si);
1031 
1032       for (int k = i; k < i + 9; ++k) {
1033         for (int l = j; l < j + 9; ++l) {
1034           // Get coords of sample point on the rotated axis
1035           sample_y = yf + (l*scale*co + k*scale*si);
1036           sample_x = xf + (-l*scale*si + k*scale*co);
1037 
1038           // Get the gaussian weighted x and y responses
1039           gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);
1040 
1041           y1 = fRound(sample_y - 0.5f);
1042           x1 = fRound(sample_x - 0.5f);
1043 
1044           y2 = fRound(sample_y + 0.5f);
1045           x2 = fRound(sample_x + 0.5f);
1046 
1047           fx = sample_x - x1;
1048           fy = sample_y - y1;
1049 
1050           res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);
1051           res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);
1052           res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);
1053           res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);
1054           rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1055 
1056           res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);
1057           res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);
1058           res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);
1059           res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);
1060           ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;
1061 
1062           // Get the x and y derivatives on the rotated axis
1063           rry = gauss_s1*(rx*co + ry*si);
1064           rrx = gauss_s1*(-rx*si + ry*co);
1065 
1066           // Sum the derivatives to the cumulative descriptor
1067           dx += rrx;
1068           dy += rry;
1069           mdx += fabs(rrx);
1070           mdy += fabs(rry);
1071         }
1072       }
1073 
1074       // Add the values to the descriptor vector
1075       gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
1076       desc[dcount++] = dx*gauss_s2;
1077       desc[dcount++] = dy*gauss_s2;
1078       desc[dcount++] = mdx*gauss_s2;
1079       desc[dcount++] = mdy*gauss_s2;
1080 
1081       len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;
1082 
1083       j += 9;
1084     }
1085 
1086     i += 9;
1087   }
1088 
1089   // convert to unit vector
1090   len = sqrt(len);
1091 
1092   for (i = 0; i < dsize; i++) {
1093     desc[i] /= len;
1094   }
1095 }
1096 
1097 /* ************************************************************************* */
1098 /**
1099  * @brief This method computes the rupright descriptor (not rotation invariant) of
1100  * the provided keypoint
1101  * @param kpt Input keypoint
1102  * @param desc Descriptor vector
1103  */
Get_Upright_MLDB_Full_Descriptor(const KeyPoint & kpt,unsigned char * desc) const1104 void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const {
1105 
1106   float di = 0.0, dx = 0.0, dy = 0.0;
1107   float ri = 0.0, rx = 0.0, ry = 0.0, xf = 0.0, yf = 0.0;
1108   float sample_x = 0.0, sample_y = 0.0, ratio = 0.0;
1109   int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;
1110   int level = 0, nsamples = 0, scale = 0;
1111   int dcount1 = 0, dcount2 = 0;
1112 
1113   const AKAZEOptions & options = *options_;
1114   const std::vector<TEvolution>& evolution = *evolution_;
1115 
1116   // Matrices for the M-LDB descriptor
1117   Mat values_1 = Mat::zeros(4, options.descriptor_channels, CV_32FC1);
1118   Mat values_2 = Mat::zeros(9, options.descriptor_channels, CV_32FC1);
1119   Mat values_3 = Mat::zeros(16, options.descriptor_channels, CV_32FC1);
1120 
1121   // Get the information from the keypoint
1122   ratio = (float)(1 << kpt.octave);
1123   scale = fRound(0.5f*kpt.size / ratio);
1124   level = kpt.class_id;
1125   yf = kpt.pt.y / ratio;
1126   xf = kpt.pt.x / ratio;
1127 
1128   // First 2x2 grid
1129   pattern_size = options_->descriptor_pattern_size;
1130   sample_step = pattern_size;
1131 
1132   for (int i = -pattern_size; i < pattern_size; i += sample_step) {
1133     for (int j = -pattern_size; j < pattern_size; j += sample_step) {
1134       di = dx = dy = 0.0;
1135       nsamples = 0;
1136 
1137       for (int k = i; k < i + sample_step; k++) {
1138         for (int l = j; l < j + sample_step; l++) {
1139 
1140           // Get the coordinates of the sample point
1141           sample_y = yf + l*scale;
1142           sample_x = xf + k*scale;
1143 
1144           y1 = fRound(sample_y);
1145           x1 = fRound(sample_x);
1146 
1147           ri = *(evolution[level].Lt.ptr<float>(y1)+x1);
1148           rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1149           ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1150 
1151           di += ri;
1152           dx += rx;
1153           dy += ry;
1154           nsamples++;
1155         }
1156       }
1157 
1158       di /= nsamples;
1159       dx /= nsamples;
1160       dy /= nsamples;
1161 
1162       *(values_1.ptr<float>(dcount2)) = di;
1163       *(values_1.ptr<float>(dcount2)+1) = dx;
1164       *(values_1.ptr<float>(dcount2)+2) = dy;
1165       dcount2++;
1166     }
1167   }
1168 
1169   // Do binary comparison first level
1170   for (int i = 0; i < 4; i++) {
1171     for (int j = i + 1; j < 4; j++) {
1172       if (*(values_1.ptr<float>(i)) > *(values_1.ptr<float>(j))) {
1173         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1174       }
1175       dcount1++;
1176 
1177       if (*(values_1.ptr<float>(i)+1) > *(values_1.ptr<float>(j)+1)) {
1178         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1179       }
1180       dcount1++;
1181 
1182       if (*(values_1.ptr<float>(i)+2) > *(values_1.ptr<float>(j)+2)) {
1183         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1184       }
1185       dcount1++;
1186     }
1187   }
1188 
1189   // Second 3x3 grid
1190   sample_step = static_cast<int>(ceil(pattern_size*2. / 3.));
1191   dcount2 = 0;
1192 
1193   for (int i = -pattern_size; i < pattern_size; i += sample_step) {
1194     for (int j = -pattern_size; j < pattern_size; j += sample_step) {
1195       di = dx = dy = 0.0;
1196       nsamples = 0;
1197 
1198       for (int k = i; k < i + sample_step; k++) {
1199         for (int l = j; l < j + sample_step; l++) {
1200 
1201           // Get the coordinates of the sample point
1202           sample_y = yf + l*scale;
1203           sample_x = xf + k*scale;
1204 
1205           y1 = fRound(sample_y);
1206           x1 = fRound(sample_x);
1207 
1208           ri = *(evolution[level].Lt.ptr<float>(y1)+x1);
1209           rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1210           ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1211 
1212           di += ri;
1213           dx += rx;
1214           dy += ry;
1215           nsamples++;
1216         }
1217       }
1218 
1219       di /= nsamples;
1220       dx /= nsamples;
1221       dy /= nsamples;
1222 
1223       *(values_2.ptr<float>(dcount2)) = di;
1224       *(values_2.ptr<float>(dcount2)+1) = dx;
1225       *(values_2.ptr<float>(dcount2)+2) = dy;
1226       dcount2++;
1227     }
1228   }
1229 
1230   //Do binary comparison second level
1231   dcount2 = 0;
1232   for (int i = 0; i < 9; i++) {
1233     for (int j = i + 1; j < 9; j++) {
1234       if (*(values_2.ptr<float>(i)) > *(values_2.ptr<float>(j))) {
1235         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1236       }
1237       dcount1++;
1238 
1239       if (*(values_2.ptr<float>(i)+1) > *(values_2.ptr<float>(j)+1)) {
1240         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1241       }
1242       dcount1++;
1243 
1244       if (*(values_2.ptr<float>(i)+2) > *(values_2.ptr<float>(j)+2)) {
1245         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1246       }
1247       dcount1++;
1248     }
1249   }
1250 
1251   // Third 4x4 grid
1252   sample_step = pattern_size / 2;
1253   dcount2 = 0;
1254 
1255   for (int i = -pattern_size; i < pattern_size; i += sample_step) {
1256     for (int j = -pattern_size; j < pattern_size; j += sample_step) {
1257       di = dx = dy = 0.0;
1258       nsamples = 0;
1259 
1260       for (int k = i; k < i + sample_step; k++) {
1261         for (int l = j; l < j + sample_step; l++) {
1262 
1263           // Get the coordinates of the sample point
1264           sample_y = yf + l*scale;
1265           sample_x = xf + k*scale;
1266 
1267           y1 = fRound(sample_y);
1268           x1 = fRound(sample_x);
1269 
1270           ri = *(evolution[level].Lt.ptr<float>(y1)+x1);
1271           rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1272           ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1273 
1274           di += ri;
1275           dx += rx;
1276           dy += ry;
1277           nsamples++;
1278         }
1279       }
1280 
1281       di /= nsamples;
1282       dx /= nsamples;
1283       dy /= nsamples;
1284 
1285       *(values_3.ptr<float>(dcount2)) = di;
1286       *(values_3.ptr<float>(dcount2)+1) = dx;
1287       *(values_3.ptr<float>(dcount2)+2) = dy;
1288       dcount2++;
1289     }
1290   }
1291 
1292   //Do binary comparison third level
1293   dcount2 = 0;
1294   for (int i = 0; i < 16; i++) {
1295     for (int j = i + 1; j < 16; j++) {
1296       if (*(values_3.ptr<float>(i)) > *(values_3.ptr<float>(j))) {
1297         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1298       }
1299       dcount1++;
1300 
1301       if (*(values_3.ptr<float>(i)+1) > *(values_3.ptr<float>(j)+1)) {
1302         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1303       }
1304       dcount1++;
1305 
1306       if (*(values_3.ptr<float>(i)+2) > *(values_3.ptr<float>(j)+2)) {
1307         desc[dcount1 / 8] |= (1 << (dcount1 % 8));
1308       }
1309       dcount1++;
1310     }
1311   }
1312 }
1313 
MLDB_Fill_Values(float * values,int sample_step,int level,float xf,float yf,float co,float si,float scale) const1314 void MLDB_Full_Descriptor_Invoker::MLDB_Fill_Values(float* values, int sample_step, int level,
1315                                                     float xf, float yf, float co, float si, float scale) const
1316 {
1317     const std::vector<TEvolution>& evolution = *evolution_;
1318     int pattern_size = options_->descriptor_pattern_size;
1319     int chan = options_->descriptor_channels;
1320     int valpos = 0;
1321 
1322     for (int i = -pattern_size; i < pattern_size; i += sample_step) {
1323         for (int j = -pattern_size; j < pattern_size; j += sample_step) {
1324             float di, dx, dy;
1325             di = dx = dy = 0.0;
1326             int nsamples = 0;
1327 
1328             for (int k = i; k < i + sample_step; k++) {
1329               for (int l = j; l < j + sample_step; l++) {
1330                 float sample_y = yf + (l*co * scale + k*si*scale);
1331                 float sample_x = xf + (-l*si * scale + k*co*scale);
1332 
1333                 int y1 = fRound(sample_y);
1334                 int x1 = fRound(sample_x);
1335 
1336                 float ri = *(evolution[level].Lt.ptr<float>(y1)+x1);
1337                 di += ri;
1338 
1339                 if(chan > 1) {
1340                     float rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1341                     float ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1342                     if (chan == 2) {
1343                       dx += sqrtf(rx*rx + ry*ry);
1344                     }
1345                     else {
1346                       float rry = rx*co + ry*si;
1347                       float rrx = -rx*si + ry*co;
1348                       dx += rrx;
1349                       dy += rry;
1350                     }
1351                 }
1352                 nsamples++;
1353               }
1354             }
1355             di /= nsamples;
1356             dx /= nsamples;
1357             dy /= nsamples;
1358 
1359             values[valpos] = di;
1360             if (chan > 1) {
1361                 values[valpos + 1] = dx;
1362             }
1363             if (chan > 2) {
1364               values[valpos + 2] = dy;
1365             }
1366             valpos += chan;
1367           }
1368         }
1369 }
1370 
MLDB_Binary_Comparisons(float * values,unsigned char * desc,int count,int & dpos) const1371 void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsigned char* desc,
1372                                                            int count, int& dpos) const {
1373     int chan = options_->descriptor_channels;
1374     int* ivalues = (int*) values;
1375     for(int i = 0; i < count * chan; i++) {
1376         ivalues[i] = CV_TOGGLE_FLT(ivalues[i]);
1377     }
1378 
1379     for(int pos = 0; pos < chan; pos++) {
1380         for (int i = 0; i < count; i++) {
1381             int ival = ivalues[chan * i + pos];
1382             for (int j = i + 1; j < count; j++) {
1383                 int res = ival > ivalues[chan * j + pos];
1384                 desc[dpos >> 3] |= (res << (dpos & 7));
1385                 dpos++;
1386             }
1387         }
1388     }
1389 }
1390 
1391 /* ************************************************************************* */
1392 /**
1393  * @brief This method computes the descriptor of the provided keypoint given the
1394  * main orientation of the keypoint
1395  * @param kpt Input keypoint
1396  * @param desc Descriptor vector
1397  */
Get_MLDB_Full_Descriptor(const KeyPoint & kpt,unsigned char * desc) const1398 void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const {
1399 
1400   const int max_channels = 3;
1401   CV_Assert(options_->descriptor_channels <= max_channels);
1402   float values[16*max_channels];
1403   const double size_mult[3] = {1, 2.0/3.0, 1.0/2.0};
1404 
1405   float ratio = (float)(1 << kpt.octave);
1406   float scale = (float)fRound(0.5f*kpt.size / ratio);
1407   float xf = kpt.pt.x / ratio;
1408   float yf = kpt.pt.y / ratio;
1409   float co = cos(kpt.angle);
1410   float si = sin(kpt.angle);
1411   int pattern_size = options_->descriptor_pattern_size;
1412 
1413   int dpos = 0;
1414   for(int lvl = 0; lvl < 3; lvl++) {
1415 
1416       int val_count = (lvl + 2) * (lvl + 2);
1417       int sample_step = static_cast<int>(ceil(pattern_size * size_mult[lvl]));
1418       MLDB_Fill_Values(values, sample_step, kpt.class_id, xf, yf, co, si, scale);
1419       MLDB_Binary_Comparisons(values, desc, val_count, dpos);
1420   }
1421 }
1422 
1423 /* ************************************************************************* */
1424 /**
1425  * @brief This method computes the M-LDB descriptor of the provided keypoint given the
1426  * main orientation of the keypoint. The descriptor is computed based on a subset of
1427  * the bits of the whole descriptor
1428  * @param kpt Input keypoint
1429  * @param desc Descriptor vector
1430  */
Get_MLDB_Descriptor_Subset(const KeyPoint & kpt,unsigned char * desc) const1431 void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const {
1432 
1433   float di = 0.f, dx = 0.f, dy = 0.f;
1434   float rx = 0.f, ry = 0.f;
1435   float sample_x = 0.f, sample_y = 0.f;
1436   int x1 = 0, y1 = 0;
1437 
1438   const AKAZEOptions & options = *options_;
1439   const std::vector<TEvolution>& evolution = *evolution_;
1440 
1441   // Get the information from the keypoint
1442   float ratio = (float)(1 << kpt.octave);
1443   int scale = fRound(0.5f*kpt.size / ratio);
1444   float angle = kpt.angle;
1445   int level = kpt.class_id;
1446   float yf = kpt.pt.y / ratio;
1447   float xf = kpt.pt.x / ratio;
1448   float co = cos(angle);
1449   float si = sin(angle);
1450 
1451   // Allocate memory for the matrix of values
1452   Mat values = Mat_<float>::zeros((4 + 9 + 16)*options.descriptor_channels, 1);
1453 
1454   // Sample everything, but only do the comparisons
1455   vector<int> steps(3);
1456   steps.at(0) = options.descriptor_pattern_size;
1457   steps.at(1) = (int)ceil(2.f*options.descriptor_pattern_size / 3.f);
1458   steps.at(2) = options.descriptor_pattern_size / 2;
1459 
1460   for (int i = 0; i < descriptorSamples_.rows; i++) {
1461     const int *coords = descriptorSamples_.ptr<int>(i);
1462     int sample_step = steps.at(coords[0]);
1463     di = 0.0f;
1464     dx = 0.0f;
1465     dy = 0.0f;
1466 
1467     for (int k = coords[1]; k < coords[1] + sample_step; k++) {
1468       for (int l = coords[2]; l < coords[2] + sample_step; l++) {
1469 
1470         // Get the coordinates of the sample point
1471         sample_y = yf + (l*scale*co + k*scale*si);
1472         sample_x = xf + (-l*scale*si + k*scale*co);
1473 
1474         y1 = fRound(sample_y);
1475         x1 = fRound(sample_x);
1476 
1477         di += *(evolution[level].Lt.ptr<float>(y1)+x1);
1478 
1479         if (options.descriptor_channels > 1) {
1480           rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1481           ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1482 
1483           if (options.descriptor_channels == 2) {
1484             dx += sqrtf(rx*rx + ry*ry);
1485           }
1486           else if (options.descriptor_channels == 3) {
1487             // Get the x and y derivatives on the rotated axis
1488             dx += rx*co + ry*si;
1489             dy += -rx*si + ry*co;
1490           }
1491         }
1492       }
1493     }
1494 
1495     *(values.ptr<float>(options.descriptor_channels*i)) = di;
1496 
1497     if (options.descriptor_channels == 2) {
1498       *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx;
1499     }
1500     else if (options.descriptor_channels == 3) {
1501       *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx;
1502       *(values.ptr<float>(options.descriptor_channels*i + 2)) = dy;
1503     }
1504   }
1505 
1506   // Do the comparisons
1507   const float *vals = values.ptr<float>(0);
1508   const int *comps = descriptorBits_.ptr<int>(0);
1509 
1510   for (int i = 0; i<descriptorBits_.rows; i++) {
1511     if (vals[comps[2 * i]] > vals[comps[2 * i + 1]]) {
1512       desc[i / 8] |= (1 << (i % 8));
1513     }
1514   }
1515 }
1516 
1517 /* ************************************************************************* */
1518 /**
1519  * @brief This method computes the upright (not rotation invariant) M-LDB descriptor
1520  * of the provided keypoint given the main orientation of the keypoint.
1521  * The descriptor is computed based on a subset of the bits of the whole descriptor
1522  * @param kpt Input keypoint
1523  * @param desc Descriptor vector
1524  */
Get_Upright_MLDB_Descriptor_Subset(const KeyPoint & kpt,unsigned char * desc) const1525 void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const {
1526 
1527   float di = 0.0f, dx = 0.0f, dy = 0.0f;
1528   float rx = 0.0f, ry = 0.0f;
1529   float sample_x = 0.0f, sample_y = 0.0f;
1530   int x1 = 0, y1 = 0;
1531 
1532   const AKAZEOptions & options = *options_;
1533   const std::vector<TEvolution>& evolution = *evolution_;
1534 
1535   // Get the information from the keypoint
1536   float ratio = (float)(1 << kpt.octave);
1537   int scale = fRound(0.5f*kpt.size / ratio);
1538   int level = kpt.class_id;
1539   float yf = kpt.pt.y / ratio;
1540   float xf = kpt.pt.x / ratio;
1541 
1542   // Allocate memory for the matrix of values
1543   Mat values = Mat_<float>::zeros((4 + 9 + 16)*options.descriptor_channels, 1);
1544 
1545   vector<int> steps(3);
1546   steps.at(0) = options.descriptor_pattern_size;
1547   steps.at(1) = static_cast<int>(ceil(2.f*options.descriptor_pattern_size / 3.f));
1548   steps.at(2) = options.descriptor_pattern_size / 2;
1549 
1550   for (int i = 0; i < descriptorSamples_.rows; i++) {
1551     const int *coords = descriptorSamples_.ptr<int>(i);
1552     int sample_step = steps.at(coords[0]);
1553     di = 0.0f, dx = 0.0f, dy = 0.0f;
1554 
1555     for (int k = coords[1]; k < coords[1] + sample_step; k++) {
1556       for (int l = coords[2]; l < coords[2] + sample_step; l++) {
1557 
1558         // Get the coordinates of the sample point
1559         sample_y = yf + l*scale;
1560         sample_x = xf + k*scale;
1561 
1562         y1 = fRound(sample_y);
1563         x1 = fRound(sample_x);
1564         di += *(evolution[level].Lt.ptr<float>(y1)+x1);
1565 
1566         if (options.descriptor_channels > 1) {
1567           rx = *(evolution[level].Lx.ptr<float>(y1)+x1);
1568           ry = *(evolution[level].Ly.ptr<float>(y1)+x1);
1569 
1570           if (options.descriptor_channels == 2) {
1571             dx += sqrtf(rx*rx + ry*ry);
1572           }
1573           else if (options.descriptor_channels == 3) {
1574             dx += rx;
1575             dy += ry;
1576           }
1577         }
1578       }
1579     }
1580 
1581     *(values.ptr<float>(options.descriptor_channels*i)) = di;
1582 
1583     if (options.descriptor_channels == 2) {
1584       *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx;
1585     }
1586     else if (options.descriptor_channels == 3) {
1587       *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx;
1588       *(values.ptr<float>(options.descriptor_channels*i + 2)) = dy;
1589     }
1590   }
1591 
1592   // Do the comparisons
1593   const float *vals = values.ptr<float>(0);
1594   const int *comps = descriptorBits_.ptr<int>(0);
1595 
1596   for (int i = 0; i<descriptorBits_.rows; i++) {
1597     if (vals[comps[2 * i]] > vals[comps[2 * i + 1]]) {
1598       desc[i / 8] |= (1 << (i % 8));
1599     }
1600   }
1601 }
1602 
1603 /* ************************************************************************* */
1604 /**
1605  * @brief This function computes a (quasi-random) list of bits to be taken
1606  * from the full descriptor. To speed the extraction, the function creates
1607  * a list of the samples that are involved in generating at least a bit (sampleList)
1608  * and a list of the comparisons between those samples (comparisons)
1609  * @param sampleList
1610  * @param comparisons The matrix with the binary comparisons
1611  * @param nbits The number of bits of the descriptor
1612  * @param pattern_size The pattern size for the binary descriptor
1613  * @param nchannels Number of channels to consider in the descriptor (1-3)
1614  * @note The function keeps the 18 bits (3-channels by 6 comparisons) of the
1615  * coarser grid, since it provides the most robust estimations
1616  */
generateDescriptorSubsample(Mat & sampleList,Mat & comparisons,int nbits,int pattern_size,int nchannels)1617 void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits,
1618                                  int pattern_size, int nchannels) {
1619 
1620   int ssz = 0;
1621   for (int i = 0; i < 3; i++) {
1622     int gz = (i + 2)*(i + 2);
1623     ssz += gz*(gz - 1) / 2;
1624   }
1625   ssz *= nchannels;
1626 
1627   CV_Assert(nbits <= ssz); // Descriptor size can't be bigger than full descriptor
1628 
1629   // Since the full descriptor is usually under 10k elements, we pick
1630   // the selection from the full matrix.  We take as many samples per
1631   // pick as the number of channels. For every pick, we
1632   // take the two samples involved and put them in the sampling list
1633 
1634   Mat_<int> fullM(ssz / nchannels, 5);
1635   for (int i = 0, c = 0; i < 3; i++) {
1636     int gdiv = i + 2; //grid divisions, per row
1637     int gsz = gdiv*gdiv;
1638     int psz = (int)ceil(2.f*pattern_size / (float)gdiv);
1639 
1640     for (int j = 0; j < gsz; j++) {
1641       for (int k = j + 1; k < gsz; k++, c++) {
1642         fullM(c, 0) = i;
1643         fullM(c, 1) = psz*(j % gdiv) - pattern_size;
1644         fullM(c, 2) = psz*(j / gdiv) - pattern_size;
1645         fullM(c, 3) = psz*(k % gdiv) - pattern_size;
1646         fullM(c, 4) = psz*(k / gdiv) - pattern_size;
1647       }
1648     }
1649   }
1650 
1651   srand(1024);
1652   Mat_<int> comps = Mat_<int>(nchannels * (int)ceil(nbits / (float)nchannels), 2);
1653   comps = 1000;
1654 
1655   // Select some samples. A sample includes all channels
1656   int count = 0;
1657   int npicks = (int)ceil(nbits / (float)nchannels);
1658   Mat_<int> samples(29, 3);
1659   Mat_<int> fullcopy = fullM.clone();
1660   samples = -1;
1661 
1662   for (int i = 0; i < npicks; i++) {
1663     int k = rand() % (fullM.rows - i);
1664     if (i < 6) {
1665       // Force use of the coarser grid values and comparisons
1666       k = i;
1667     }
1668 
1669     bool n = true;
1670 
1671     for (int j = 0; j < count; j++) {
1672       if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 1) && samples(j, 2) == fullcopy(k, 2)) {
1673         n = false;
1674         comps(i*nchannels, 0) = nchannels*j;
1675         comps(i*nchannels + 1, 0) = nchannels*j + 1;
1676         comps(i*nchannels + 2, 0) = nchannels*j + 2;
1677         break;
1678       }
1679     }
1680 
1681     if (n) {
1682       samples(count, 0) = fullcopy(k, 0);
1683       samples(count, 1) = fullcopy(k, 1);
1684       samples(count, 2) = fullcopy(k, 2);
1685       comps(i*nchannels, 0) = nchannels*count;
1686       comps(i*nchannels + 1, 0) = nchannels*count + 1;
1687       comps(i*nchannels + 2, 0) = nchannels*count + 2;
1688       count++;
1689     }
1690 
1691     n = true;
1692     for (int j = 0; j < count; j++) {
1693       if (samples(j, 0) == fullcopy(k, 0) && samples(j, 1) == fullcopy(k, 3) && samples(j, 2) == fullcopy(k, 4)) {
1694         n = false;
1695         comps(i*nchannels, 1) = nchannels*j;
1696         comps(i*nchannels + 1, 1) = nchannels*j + 1;
1697         comps(i*nchannels + 2, 1) = nchannels*j + 2;
1698         break;
1699       }
1700     }
1701 
1702     if (n) {
1703       samples(count, 0) = fullcopy(k, 0);
1704       samples(count, 1) = fullcopy(k, 3);
1705       samples(count, 2) = fullcopy(k, 4);
1706       comps(i*nchannels, 1) = nchannels*count;
1707       comps(i*nchannels + 1, 1) = nchannels*count + 1;
1708       comps(i*nchannels + 2, 1) = nchannels*count + 2;
1709       count++;
1710     }
1711 
1712     Mat tmp = fullcopy.row(k);
1713     fullcopy.row(fullcopy.rows - i - 1).copyTo(tmp);
1714   }
1715 
1716   sampleList = samples.rowRange(0, count).clone();
1717   comparisons = comps.rowRange(0, nbits).clone();
1718 }
1719 
1720 }
1721