Updated test_features2d.cpp with latest API change
[profile/ivi/opencv.git] / modules / cudafeatures2d / test / test_features2d.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                           License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 //   * Redistribution's of source code must retain the above copyright notice,
21 //     this list of conditions and the following disclaimer.
22 //
23 //   * Redistribution's in binary form must reproduce the above copyright notice,
24 //     this list of conditions and the following disclaimer in the documentation
25 //     and/or other materials provided with the distribution.
26 //
27 //   * The name of the copyright holders may not be used to endorse or promote products
28 //     derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "test_precomp.hpp"
44
45 #ifdef HAVE_CUDA
46
47 using namespace cvtest;
48
49 /////////////////////////////////////////////////////////////////////////////////////////////////
50 // FAST
51
52 namespace
53 {
54     IMPLEMENT_PARAM_CLASS(FAST_Threshold, int)
55     IMPLEMENT_PARAM_CLASS(FAST_NonmaxSuppression, bool)
56 }
57
58 PARAM_TEST_CASE(FAST, cv::cuda::DeviceInfo, FAST_Threshold, FAST_NonmaxSuppression)
59 {
60     cv::cuda::DeviceInfo devInfo;
61     int threshold;
62     bool nonmaxSuppression;
63
64     virtual void SetUp()
65     {
66         devInfo = GET_PARAM(0);
67         threshold = GET_PARAM(1);
68         nonmaxSuppression = GET_PARAM(2);
69
70         cv::cuda::setDevice(devInfo.deviceID());
71     }
72 };
73
74 CUDA_TEST_P(FAST, Accuracy)
75 {
76     cv::Mat image = readImage("features2d/aloe.png", cv::IMREAD_GRAYSCALE);
77     ASSERT_FALSE(image.empty());
78
79     cv::cuda::FAST_CUDA fast(threshold);
80     fast.nonmaxSuppression = nonmaxSuppression;
81
82     if (!supportFeature(devInfo, cv::cuda::GLOBAL_ATOMICS))
83     {
84         try
85         {
86             std::vector<cv::KeyPoint> keypoints;
87             fast(loadMat(image), cv::cuda::GpuMat(), keypoints);
88         }
89         catch (const cv::Exception& e)
90         {
91             ASSERT_EQ(cv::Error::StsNotImplemented, e.code);
92         }
93     }
94     else
95     {
96         std::vector<cv::KeyPoint> keypoints;
97         fast(loadMat(image), cv::cuda::GpuMat(), keypoints);
98
99         std::vector<cv::KeyPoint> keypoints_gold;
100         cv::FAST(image, keypoints_gold, threshold, nonmaxSuppression);
101
102         ASSERT_KEYPOINTS_EQ(keypoints_gold, keypoints);
103     }
104 }
105
106 INSTANTIATE_TEST_CASE_P(CUDA_Features2D, FAST, testing::Combine(
107     ALL_DEVICES,
108     testing::Values(FAST_Threshold(25), FAST_Threshold(50)),
109     testing::Values(FAST_NonmaxSuppression(false), FAST_NonmaxSuppression(true))));
110
111 /////////////////////////////////////////////////////////////////////////////////////////////////
112 // ORB
113
114 namespace
115 {
116     IMPLEMENT_PARAM_CLASS(ORB_FeaturesCount, int)
117     IMPLEMENT_PARAM_CLASS(ORB_ScaleFactor, float)
118     IMPLEMENT_PARAM_CLASS(ORB_LevelsCount, int)
119     IMPLEMENT_PARAM_CLASS(ORB_EdgeThreshold, int)
120     IMPLEMENT_PARAM_CLASS(ORB_firstLevel, int)
121     IMPLEMENT_PARAM_CLASS(ORB_WTA_K, int)
122     IMPLEMENT_PARAM_CLASS(ORB_PatchSize, int)
123     IMPLEMENT_PARAM_CLASS(ORB_BlurForDescriptor, bool)
124 }
125
126 CV_ENUM(ORB_ScoreType, ORB::HARRIS_SCORE, ORB::FAST_SCORE)
127
128 PARAM_TEST_CASE(ORB, cv::cuda::DeviceInfo, ORB_FeaturesCount, ORB_ScaleFactor, ORB_LevelsCount, ORB_EdgeThreshold, ORB_firstLevel, ORB_WTA_K, ORB_ScoreType, ORB_PatchSize, ORB_BlurForDescriptor)
129 {
130     cv::cuda::DeviceInfo devInfo;
131     int nFeatures;
132     float scaleFactor;
133     int nLevels;
134     int edgeThreshold;
135     int firstLevel;
136     int WTA_K;
137     int scoreType;
138     int patchSize;
139     bool blurForDescriptor;
140
141     virtual void SetUp()
142     {
143         devInfo = GET_PARAM(0);
144         nFeatures = GET_PARAM(1);
145         scaleFactor = GET_PARAM(2);
146         nLevels = GET_PARAM(3);
147         edgeThreshold = GET_PARAM(4);
148         firstLevel = GET_PARAM(5);
149         WTA_K = GET_PARAM(6);
150         scoreType = GET_PARAM(7);
151         patchSize = GET_PARAM(8);
152         blurForDescriptor = GET_PARAM(9);
153
154         cv::cuda::setDevice(devInfo.deviceID());
155     }
156 };
157
158 CUDA_TEST_P(ORB, Accuracy)
159 {
160     cv::Mat image = readImage("features2d/aloe.png", cv::IMREAD_GRAYSCALE);
161     ASSERT_FALSE(image.empty());
162
163     cv::Mat mask(image.size(), CV_8UC1, cv::Scalar::all(1));
164     mask(cv::Range(0, image.rows / 2), cv::Range(0, image.cols / 2)).setTo(cv::Scalar::all(0));
165
166     cv::cuda::ORB_CUDA orb(nFeatures, scaleFactor, nLevels, edgeThreshold, firstLevel, WTA_K, scoreType, patchSize);
167     orb.blurForDescriptor = blurForDescriptor;
168
169     if (!supportFeature(devInfo, cv::cuda::GLOBAL_ATOMICS))
170     {
171         try
172         {
173             std::vector<cv::KeyPoint> keypoints;
174             cv::cuda::GpuMat descriptors;
175             orb(loadMat(image), loadMat(mask), keypoints, descriptors);
176         }
177         catch (const cv::Exception& e)
178         {
179             ASSERT_EQ(cv::Error::StsNotImplemented, e.code);
180         }
181     }
182     else
183     {
184         std::vector<cv::KeyPoint> keypoints;
185         cv::cuda::GpuMat descriptors;
186         orb(loadMat(image), loadMat(mask), keypoints, descriptors);
187
188         cv::Ptr<cv::ORB> orb_gold = cv::ORB::create(nFeatures, scaleFactor, nLevels, edgeThreshold, firstLevel, WTA_K, scoreType, patchSize);
189
190         std::vector<cv::KeyPoint> keypoints_gold;
191         cv::Mat descriptors_gold;
192         orb_gold->detectAndCompute(image, mask, keypoints_gold, descriptors_gold);
193
194         cv::BFMatcher matcher(cv::NORM_HAMMING);
195         std::vector<cv::DMatch> matches;
196         matcher.match(descriptors_gold, cv::Mat(descriptors), matches);
197
198         int matchedCount = getMatchedPointsCount(keypoints_gold, keypoints, matches);
199         double matchedRatio = static_cast<double>(matchedCount) / keypoints.size();
200
201         EXPECT_GT(matchedRatio, 0.35);
202     }
203 }
204
205 INSTANTIATE_TEST_CASE_P(CUDA_Features2D, ORB,  testing::Combine(
206     ALL_DEVICES,
207     testing::Values(ORB_FeaturesCount(1000)),
208     testing::Values(ORB_ScaleFactor(1.2f)),
209     testing::Values(ORB_LevelsCount(4), ORB_LevelsCount(8)),
210     testing::Values(ORB_EdgeThreshold(31)),
211     testing::Values(ORB_firstLevel(0), ORB_firstLevel(2)),
212     testing::Values(ORB_WTA_K(2), ORB_WTA_K(3), ORB_WTA_K(4)),
213     testing::Values(ORB_ScoreType(cv::ORB::HARRIS_SCORE)),
214     testing::Values(ORB_PatchSize(31), ORB_PatchSize(29)),
215     testing::Values(ORB_BlurForDescriptor(false), ORB_BlurForDescriptor(true))));
216
217 /////////////////////////////////////////////////////////////////////////////////////////////////
218 // BruteForceMatcher
219
220 namespace
221 {
222     IMPLEMENT_PARAM_CLASS(DescriptorSize, int)
223     IMPLEMENT_PARAM_CLASS(UseMask, bool)
224 }
225
226 PARAM_TEST_CASE(BruteForceMatcher, cv::cuda::DeviceInfo, NormCode, DescriptorSize, UseMask)
227 {
228     cv::cuda::DeviceInfo devInfo;
229     int normCode;
230     int dim;
231     bool useMask;
232
233     int queryDescCount;
234     int countFactor;
235
236     cv::Mat query, train;
237
238     virtual void SetUp()
239     {
240         devInfo = GET_PARAM(0);
241         normCode = GET_PARAM(1);
242         dim = GET_PARAM(2);
243         useMask = GET_PARAM(3);
244
245         cv::cuda::setDevice(devInfo.deviceID());
246
247         queryDescCount = 300; // must be even number because we split train data in some cases in two
248         countFactor = 4; // do not change it
249
250         cv::RNG& rng = cvtest::TS::ptr()->get_rng();
251
252         cv::Mat queryBuf, trainBuf;
253
254         // Generate query descriptors randomly.
255         // Descriptor vector elements are integer values.
256         queryBuf.create(queryDescCount, dim, CV_32SC1);
257         rng.fill(queryBuf, cv::RNG::UNIFORM, cv::Scalar::all(0), cv::Scalar::all(3));
258         queryBuf.convertTo(queryBuf, CV_32FC1);
259
260         // Generate train decriptors as follows:
261         // copy each query descriptor to train set countFactor times
262         // and perturb some one element of the copied descriptors in
263         // in ascending order. General boundaries of the perturbation
264         // are (0.f, 1.f).
265         trainBuf.create(queryDescCount * countFactor, dim, CV_32FC1);
266         float step = 1.f / countFactor;
267         for (int qIdx = 0; qIdx < queryDescCount; qIdx++)
268         {
269             cv::Mat queryDescriptor = queryBuf.row(qIdx);
270             for (int c = 0; c < countFactor; c++)
271             {
272                 int tIdx = qIdx * countFactor + c;
273                 cv::Mat trainDescriptor = trainBuf.row(tIdx);
274                 queryDescriptor.copyTo(trainDescriptor);
275                 int elem = rng(dim);
276                 float diff = rng.uniform(step * c, step * (c + 1));
277                 trainDescriptor.at<float>(0, elem) += diff;
278             }
279         }
280
281         queryBuf.convertTo(query, CV_32F);
282         trainBuf.convertTo(train, CV_32F);
283     }
284 };
285
286 CUDA_TEST_P(BruteForceMatcher, Match_Single)
287 {
288     cv::cuda::BFMatcher_CUDA matcher(normCode);
289
290     cv::cuda::GpuMat mask;
291     if (useMask)
292     {
293         mask.create(query.rows, train.rows, CV_8UC1);
294         mask.setTo(cv::Scalar::all(1));
295     }
296
297     std::vector<cv::DMatch> matches;
298     matcher.match(loadMat(query), loadMat(train), matches, mask);
299
300     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
301
302     int badCount = 0;
303     for (size_t i = 0; i < matches.size(); i++)
304     {
305         cv::DMatch match = matches[i];
306         if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor) || (match.imgIdx != 0))
307             badCount++;
308     }
309
310     ASSERT_EQ(0, badCount);
311 }
312
313 CUDA_TEST_P(BruteForceMatcher, Match_Collection)
314 {
315     cv::cuda::BFMatcher_CUDA matcher(normCode);
316
317     cv::cuda::GpuMat d_train(train);
318
319     // make add() twice to test such case
320     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(0, train.rows / 2)));
321     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(train.rows / 2, train.rows)));
322
323     // prepare masks (make first nearest match illegal)
324     std::vector<cv::cuda::GpuMat> masks(2);
325     for (int mi = 0; mi < 2; mi++)
326     {
327         masks[mi] = cv::cuda::GpuMat(query.rows, train.rows/2, CV_8UC1, cv::Scalar::all(1));
328         for (int di = 0; di < queryDescCount/2; di++)
329             masks[mi].col(di * countFactor).setTo(cv::Scalar::all(0));
330     }
331
332     std::vector<cv::DMatch> matches;
333     if (useMask)
334         matcher.match(cv::cuda::GpuMat(query), matches, masks);
335     else
336         matcher.match(cv::cuda::GpuMat(query), matches);
337
338     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
339
340     int badCount = 0;
341     int shift = useMask ? 1 : 0;
342     for (size_t i = 0; i < matches.size(); i++)
343     {
344         cv::DMatch match = matches[i];
345
346         if ((int)i < queryDescCount / 2)
347         {
348             bool validQueryIdx = (match.queryIdx == (int)i);
349             bool validTrainIdx = (match.trainIdx == (int)i * countFactor + shift);
350             bool validImgIdx = (match.imgIdx == 0);
351             if (!validQueryIdx || !validTrainIdx || !validImgIdx)
352                 badCount++;
353         }
354         else
355         {
356             bool validQueryIdx = (match.queryIdx == (int)i);
357             bool validTrainIdx = (match.trainIdx == ((int)i - queryDescCount / 2) * countFactor + shift);
358             bool validImgIdx = (match.imgIdx == 1);
359             if (!validQueryIdx || !validTrainIdx || !validImgIdx)
360                 badCount++;
361         }
362     }
363
364     ASSERT_EQ(0, badCount);
365 }
366
367 CUDA_TEST_P(BruteForceMatcher, KnnMatch_2_Single)
368 {
369     cv::cuda::BFMatcher_CUDA matcher(normCode);
370
371     const int knn = 2;
372
373     cv::cuda::GpuMat mask;
374     if (useMask)
375     {
376         mask.create(query.rows, train.rows, CV_8UC1);
377         mask.setTo(cv::Scalar::all(1));
378     }
379
380     std::vector< std::vector<cv::DMatch> > matches;
381     matcher.knnMatch(loadMat(query), loadMat(train), matches, knn, mask);
382
383     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
384
385     int badCount = 0;
386     for (size_t i = 0; i < matches.size(); i++)
387     {
388         if ((int)matches[i].size() != knn)
389             badCount++;
390         else
391         {
392             int localBadCount = 0;
393             for (int k = 0; k < knn; k++)
394             {
395                 cv::DMatch match = matches[i][k];
396                 if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k) || (match.imgIdx != 0))
397                     localBadCount++;
398             }
399             badCount += localBadCount > 0 ? 1 : 0;
400         }
401     }
402
403     ASSERT_EQ(0, badCount);
404 }
405
406 CUDA_TEST_P(BruteForceMatcher, KnnMatch_3_Single)
407 {
408     cv::cuda::BFMatcher_CUDA matcher(normCode);
409
410     const int knn = 3;
411
412     cv::cuda::GpuMat mask;
413     if (useMask)
414     {
415         mask.create(query.rows, train.rows, CV_8UC1);
416         mask.setTo(cv::Scalar::all(1));
417     }
418
419     std::vector< std::vector<cv::DMatch> > matches;
420     matcher.knnMatch(loadMat(query), loadMat(train), matches, knn, mask);
421
422     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
423
424     int badCount = 0;
425     for (size_t i = 0; i < matches.size(); i++)
426     {
427         if ((int)matches[i].size() != knn)
428             badCount++;
429         else
430         {
431             int localBadCount = 0;
432             for (int k = 0; k < knn; k++)
433             {
434                 cv::DMatch match = matches[i][k];
435                 if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k) || (match.imgIdx != 0))
436                     localBadCount++;
437             }
438             badCount += localBadCount > 0 ? 1 : 0;
439         }
440     }
441
442     ASSERT_EQ(0, badCount);
443 }
444
445 CUDA_TEST_P(BruteForceMatcher, KnnMatch_2_Collection)
446 {
447     cv::cuda::BFMatcher_CUDA matcher(normCode);
448
449     const int knn = 2;
450
451     cv::cuda::GpuMat d_train(train);
452
453     // make add() twice to test such case
454     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(0, train.rows / 2)));
455     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(train.rows / 2, train.rows)));
456
457     // prepare masks (make first nearest match illegal)
458     std::vector<cv::cuda::GpuMat> masks(2);
459     for (int mi = 0; mi < 2; mi++ )
460     {
461         masks[mi] = cv::cuda::GpuMat(query.rows, train.rows / 2, CV_8UC1, cv::Scalar::all(1));
462         for (int di = 0; di < queryDescCount / 2; di++)
463             masks[mi].col(di * countFactor).setTo(cv::Scalar::all(0));
464     }
465
466     std::vector< std::vector<cv::DMatch> > matches;
467
468     if (useMask)
469         matcher.knnMatch(cv::cuda::GpuMat(query), matches, knn, masks);
470     else
471         matcher.knnMatch(cv::cuda::GpuMat(query), matches, knn);
472
473     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
474
475     int badCount = 0;
476     int shift = useMask ? 1 : 0;
477     for (size_t i = 0; i < matches.size(); i++)
478     {
479         if ((int)matches[i].size() != knn)
480             badCount++;
481         else
482         {
483             int localBadCount = 0;
484             for (int k = 0; k < knn; k++)
485             {
486                 cv::DMatch match = matches[i][k];
487                 {
488                     if ((int)i < queryDescCount / 2)
489                     {
490                         if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k + shift) || (match.imgIdx != 0) )
491                             localBadCount++;
492                     }
493                     else
494                     {
495                         if ((match.queryIdx != (int)i) || (match.trainIdx != ((int)i - queryDescCount / 2) * countFactor + k + shift) || (match.imgIdx != 1) )
496                             localBadCount++;
497                     }
498                 }
499             }
500             badCount += localBadCount > 0 ? 1 : 0;
501         }
502     }
503
504     ASSERT_EQ(0, badCount);
505 }
506
507 CUDA_TEST_P(BruteForceMatcher, KnnMatch_3_Collection)
508 {
509     cv::cuda::BFMatcher_CUDA matcher(normCode);
510
511     const int knn = 3;
512
513     cv::cuda::GpuMat d_train(train);
514
515     // make add() twice to test such case
516     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(0, train.rows / 2)));
517     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(train.rows / 2, train.rows)));
518
519     // prepare masks (make first nearest match illegal)
520     std::vector<cv::cuda::GpuMat> masks(2);
521     for (int mi = 0; mi < 2; mi++ )
522     {
523         masks[mi] = cv::cuda::GpuMat(query.rows, train.rows / 2, CV_8UC1, cv::Scalar::all(1));
524         for (int di = 0; di < queryDescCount / 2; di++)
525             masks[mi].col(di * countFactor).setTo(cv::Scalar::all(0));
526     }
527
528     std::vector< std::vector<cv::DMatch> > matches;
529
530     if (useMask)
531         matcher.knnMatch(cv::cuda::GpuMat(query), matches, knn, masks);
532     else
533         matcher.knnMatch(cv::cuda::GpuMat(query), matches, knn);
534
535     ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
536
537     int badCount = 0;
538     int shift = useMask ? 1 : 0;
539     for (size_t i = 0; i < matches.size(); i++)
540     {
541         if ((int)matches[i].size() != knn)
542             badCount++;
543         else
544         {
545             int localBadCount = 0;
546             for (int k = 0; k < knn; k++)
547             {
548                 cv::DMatch match = matches[i][k];
549                 {
550                     if ((int)i < queryDescCount / 2)
551                     {
552                         if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k + shift) || (match.imgIdx != 0) )
553                             localBadCount++;
554                     }
555                     else
556                     {
557                         if ((match.queryIdx != (int)i) || (match.trainIdx != ((int)i - queryDescCount / 2) * countFactor + k + shift) || (match.imgIdx != 1) )
558                             localBadCount++;
559                     }
560                 }
561             }
562             badCount += localBadCount > 0 ? 1 : 0;
563         }
564     }
565
566     ASSERT_EQ(0, badCount);
567 }
568
569 CUDA_TEST_P(BruteForceMatcher, RadiusMatch_Single)
570 {
571     cv::cuda::BFMatcher_CUDA matcher(normCode);
572
573     const float radius = 1.f / countFactor;
574
575     if (!supportFeature(devInfo, cv::cuda::GLOBAL_ATOMICS))
576     {
577         try
578         {
579             std::vector< std::vector<cv::DMatch> > matches;
580             matcher.radiusMatch(loadMat(query), loadMat(train), matches, radius);
581         }
582         catch (const cv::Exception& e)
583         {
584             ASSERT_EQ(cv::Error::StsNotImplemented, e.code);
585         }
586     }
587     else
588     {
589         cv::cuda::GpuMat mask;
590         if (useMask)
591         {
592             mask.create(query.rows, train.rows, CV_8UC1);
593             mask.setTo(cv::Scalar::all(1));
594         }
595
596         std::vector< std::vector<cv::DMatch> > matches;
597         matcher.radiusMatch(loadMat(query), loadMat(train), matches, radius, mask);
598
599         ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
600
601         int badCount = 0;
602         for (size_t i = 0; i < matches.size(); i++)
603         {
604             if ((int)matches[i].size() != 1)
605                 badCount++;
606             else
607             {
608                 cv::DMatch match = matches[i][0];
609                 if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0))
610                     badCount++;
611             }
612         }
613
614         ASSERT_EQ(0, badCount);
615     }
616 }
617
618 CUDA_TEST_P(BruteForceMatcher, RadiusMatch_Collection)
619 {
620     cv::cuda::BFMatcher_CUDA matcher(normCode);
621
622     const int n = 3;
623     const float radius = 1.f / countFactor * n;
624
625     cv::cuda::GpuMat d_train(train);
626
627     // make add() twice to test such case
628     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(0, train.rows / 2)));
629     matcher.add(std::vector<cv::cuda::GpuMat>(1, d_train.rowRange(train.rows / 2, train.rows)));
630
631     // prepare masks (make first nearest match illegal)
632     std::vector<cv::cuda::GpuMat> masks(2);
633     for (int mi = 0; mi < 2; mi++)
634     {
635         masks[mi] = cv::cuda::GpuMat(query.rows, train.rows / 2, CV_8UC1, cv::Scalar::all(1));
636         for (int di = 0; di < queryDescCount / 2; di++)
637             masks[mi].col(di * countFactor).setTo(cv::Scalar::all(0));
638     }
639
640     if (!supportFeature(devInfo, cv::cuda::GLOBAL_ATOMICS))
641     {
642         try
643         {
644             std::vector< std::vector<cv::DMatch> > matches;
645             matcher.radiusMatch(cv::cuda::GpuMat(query), matches, radius, masks);
646         }
647         catch (const cv::Exception& e)
648         {
649             ASSERT_EQ(cv::Error::StsNotImplemented, e.code);
650         }
651     }
652     else
653     {
654         std::vector< std::vector<cv::DMatch> > matches;
655
656         if (useMask)
657             matcher.radiusMatch(cv::cuda::GpuMat(query), matches, radius, masks);
658         else
659             matcher.radiusMatch(cv::cuda::GpuMat(query), matches, radius);
660
661         ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
662
663         int badCount = 0;
664         int shift = useMask ? 1 : 0;
665         int needMatchCount = useMask ? n-1 : n;
666         for (size_t i = 0; i < matches.size(); i++)
667         {
668             if ((int)matches[i].size() != needMatchCount)
669                 badCount++;
670             else
671             {
672                 int localBadCount = 0;
673                 for (int k = 0; k < needMatchCount; k++)
674                 {
675                     cv::DMatch match = matches[i][k];
676                     {
677                         if ((int)i < queryDescCount / 2)
678                         {
679                             if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k + shift) || (match.imgIdx != 0) )
680                                 localBadCount++;
681                         }
682                         else
683                         {
684                             if ((match.queryIdx != (int)i) || (match.trainIdx != ((int)i - queryDescCount / 2) * countFactor + k + shift) || (match.imgIdx != 1) )
685                                 localBadCount++;
686                         }
687                     }
688                 }
689                 badCount += localBadCount > 0 ? 1 : 0;
690             }
691         }
692
693         ASSERT_EQ(0, badCount);
694     }
695 }
696
697 INSTANTIATE_TEST_CASE_P(CUDA_Features2D, BruteForceMatcher, testing::Combine(
698     ALL_DEVICES,
699     testing::Values(NormCode(cv::NORM_L1), NormCode(cv::NORM_L2)),
700     testing::Values(DescriptorSize(57), DescriptorSize(64), DescriptorSize(83), DescriptorSize(128), DescriptorSize(179), DescriptorSize(256), DescriptorSize(304)),
701     testing::Values(UseMask(false), UseMask(true))));
702
703 #endif // HAVE_CUDA