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