Merge remote-tracking branch 'origin/2.4' into merge-2.4
[profile/ivi/opencv.git] / modules / ts / src / ts_perf.cpp
1 #include "precomp.hpp"
2
3 #ifdef HAVE_CUDA
4 #include "opencv2/core/cuda.hpp"
5 #endif
6
7 #ifdef ANDROID
8 # include <sys/time.h>
9 #endif
10
11 using namespace perf;
12
13 int64 TestBase::timeLimitDefault = 0;
14 unsigned int TestBase::iterationsLimitDefault = (unsigned int)(-1);
15 int64 TestBase::_timeadjustment = 0;
16
17 // Item [0] will be considered the default implementation.
18 static std::vector<std::string> available_impls;
19
20 static std::string  param_impl;
21
22 static enum PERF_STRATEGY param_strategy = PERF_STRATEGY_BASE;
23
24 static double       param_max_outliers;
25 static double       param_max_deviation;
26 static unsigned int param_min_samples;
27 static unsigned int param_force_samples;
28 static uint64       param_seed;
29 static double       param_time_limit;
30 static int          param_threads;
31 static bool         param_write_sanity;
32 static bool         param_verify_sanity;
33 #ifdef HAVE_CUDA
34 static int          param_cuda_device;
35 #endif
36
37
38 #ifdef ANDROID
39 static int          param_affinity_mask;
40 static bool         log_power_checkpoints;
41
42 #include <sys/syscall.h>
43 #include <pthread.h>
44 static void setCurrentThreadAffinityMask(int mask)
45 {
46     pid_t pid=gettid();
47     int syscallres=syscall(__NR_sched_setaffinity, pid, sizeof(mask), &mask);
48     if (syscallres)
49     {
50         int err=errno;
51         err=err;//to avoid warnings about unused variables
52         LOGE("Error in the syscall setaffinity: mask=%d=0x%x err=%d=0x%x", mask, mask, err, err);
53     }
54 }
55 #endif
56
57 namespace {
58
59 class PerfEnvironment: public ::testing::Environment
60 {
61 public:
62     void TearDown()
63     {
64         cv::setNumThreads(-1);
65     }
66 };
67
68 } // namespace
69
70 static void randu(cv::Mat& m)
71 {
72     const int bigValue = 0x00000FFF;
73     if (m.depth() < CV_32F)
74     {
75         int minmax[] = {0, 256};
76         cv::Mat mr = cv::Mat(m.rows, (int)(m.cols * m.elemSize()), CV_8U, m.ptr(), m.step[0]);
77         cv::randu(mr, cv::Mat(1, 1, CV_32S, minmax), cv::Mat(1, 1, CV_32S, minmax + 1));
78     }
79     else if (m.depth() == CV_32F)
80     {
81         //float minmax[] = {-FLT_MAX, FLT_MAX};
82         float minmax[] = {-bigValue, bigValue};
83         cv::Mat mr = m.reshape(1);
84         cv::randu(mr, cv::Mat(1, 1, CV_32F, minmax), cv::Mat(1, 1, CV_32F, minmax + 1));
85     }
86     else
87     {
88         //double minmax[] = {-DBL_MAX, DBL_MAX};
89         double minmax[] = {-bigValue, bigValue};
90         cv::Mat mr = m.reshape(1);
91         cv::randu(mr, cv::Mat(1, 1, CV_64F, minmax), cv::Mat(1, 1, CV_64F, minmax + 1));
92     }
93 }
94
95 /*****************************************************************************************\
96 *                       inner exception class for early termination
97 \*****************************************************************************************/
98
99 class PerfEarlyExitException: public cv::Exception {};
100
101 /*****************************************************************************************\
102 *                                   ::perf::Regression
103 \*****************************************************************************************/
104
105 Regression& Regression::instance()
106 {
107     static Regression single;
108     return single;
109 }
110
111 Regression& Regression::add(TestBase* test, const std::string& name, cv::InputArray array, double eps, ERROR_TYPE err)
112 {
113     if(test) test->setVerified();
114     return instance()(name, array, eps, err);
115 }
116
117 Regression& Regression::addKeypoints(TestBase* test, const std::string& name, const std::vector<cv::KeyPoint>& array, double eps, ERROR_TYPE err)
118 {
119     int len = (int)array.size();
120     cv::Mat pt      (len, 1, CV_32FC2, len ? (void*)&array[0].pt : 0,       sizeof(cv::KeyPoint));
121     cv::Mat size    (len, 1, CV_32FC1, len ? (void*)&array[0].size : 0,     sizeof(cv::KeyPoint));
122     cv::Mat angle   (len, 1, CV_32FC1, len ? (void*)&array[0].angle : 0,    sizeof(cv::KeyPoint));
123     cv::Mat response(len, 1, CV_32FC1, len ? (void*)&array[0].response : 0, sizeof(cv::KeyPoint));
124     cv::Mat octave  (len, 1, CV_32SC1, len ? (void*)&array[0].octave : 0,   sizeof(cv::KeyPoint));
125     cv::Mat class_id(len, 1, CV_32SC1, len ? (void*)&array[0].class_id : 0, sizeof(cv::KeyPoint));
126
127     return Regression::add(test, name + "-pt",       pt,       eps, ERROR_ABSOLUTE)
128                                 (name + "-size",     size,     eps, ERROR_ABSOLUTE)
129                                 (name + "-angle",    angle,    eps, ERROR_ABSOLUTE)
130                                 (name + "-response", response, eps, err)
131                                 (name + "-octave",   octave,   eps, ERROR_ABSOLUTE)
132                                 (name + "-class_id", class_id, eps, ERROR_ABSOLUTE);
133 }
134
135 Regression& Regression::addMatches(TestBase* test, const std::string& name, const std::vector<cv::DMatch>& array, double eps, ERROR_TYPE err)
136 {
137     int len = (int)array.size();
138     cv::Mat queryIdx(len, 1, CV_32SC1, len ? (void*)&array[0].queryIdx : 0, sizeof(cv::DMatch));
139     cv::Mat trainIdx(len, 1, CV_32SC1, len ? (void*)&array[0].trainIdx : 0, sizeof(cv::DMatch));
140     cv::Mat imgIdx  (len, 1, CV_32SC1, len ? (void*)&array[0].imgIdx : 0,   sizeof(cv::DMatch));
141     cv::Mat distance(len, 1, CV_32FC1, len ? (void*)&array[0].distance : 0, sizeof(cv::DMatch));
142
143     return Regression::add(test, name + "-queryIdx", queryIdx, DBL_EPSILON, ERROR_ABSOLUTE)
144                                 (name + "-trainIdx", trainIdx, DBL_EPSILON, ERROR_ABSOLUTE)
145                                 (name + "-imgIdx",   imgIdx,   DBL_EPSILON, ERROR_ABSOLUTE)
146                                 (name + "-distance", distance, eps, err);
147 }
148
149 void Regression::Init(const std::string& testSuitName, const std::string& ext)
150 {
151     instance().init(testSuitName, ext);
152 }
153
154 void Regression::init(const std::string& testSuitName, const std::string& ext)
155 {
156     if (!storageInPath.empty())
157     {
158         LOGE("Subsequent initialization of Regression utility is not allowed.");
159         return;
160     }
161
162     const char *data_path_dir = getenv("OPENCV_TEST_DATA_PATH");
163     const char *path_separator = "/";
164
165     if (data_path_dir)
166     {
167         int len = (int)strlen(data_path_dir)-1;
168         if (len < 0) len = 0;
169         std::string path_base = (data_path_dir[0] == 0 ? std::string(".") : std::string(data_path_dir))
170                 + (data_path_dir[len] == '/' || data_path_dir[len] == '\\' ? "" : path_separator)
171                 + "perf"
172                 + path_separator;
173
174         storageInPath = path_base + testSuitName + ext;
175         storageOutPath = path_base + testSuitName;
176     }
177     else
178     {
179         storageInPath = testSuitName + ext;
180         storageOutPath = testSuitName;
181     }
182
183     suiteName = testSuitName;
184
185     try
186     {
187         if (storageIn.open(storageInPath, cv::FileStorage::READ))
188         {
189             rootIn = storageIn.root();
190             if (storageInPath.length() > 3 && storageInPath.substr(storageInPath.length()-3) == ".gz")
191                 storageOutPath += "_new";
192             storageOutPath += ext;
193         }
194     }
195     catch(cv::Exception&)
196     {
197         LOGE("Failed to open sanity data for reading: %s", storageInPath.c_str());
198     }
199
200     if(!storageIn.isOpened())
201         storageOutPath = storageInPath;
202 }
203
204 Regression::Regression() : regRNG(cv::getTickCount())//this rng should be really random
205 {
206 }
207
208 Regression::~Regression()
209 {
210     if (storageIn.isOpened())
211         storageIn.release();
212     if (storageOut.isOpened())
213     {
214         if (!currentTestNodeName.empty())
215             storageOut << "}";
216         storageOut.release();
217     }
218 }
219
220 cv::FileStorage& Regression::write()
221 {
222     if (!storageOut.isOpened() && !storageOutPath.empty())
223     {
224         int mode = (storageIn.isOpened() && storageInPath == storageOutPath)
225                 ? cv::FileStorage::APPEND : cv::FileStorage::WRITE;
226         storageOut.open(storageOutPath, mode);
227         if (!storageOut.isOpened())
228         {
229             LOGE("Could not open \"%s\" file for writing", storageOutPath.c_str());
230             storageOutPath.clear();
231         }
232         else if (mode == cv::FileStorage::WRITE && !rootIn.empty())
233         {
234             //TODO: write content of rootIn node into the storageOut
235         }
236     }
237     return storageOut;
238 }
239
240 std::string Regression::getCurrentTestNodeName()
241 {
242     const ::testing::TestInfo* const test_info =
243       ::testing::UnitTest::GetInstance()->current_test_info();
244
245     if (test_info == 0)
246         return "undefined";
247
248     std::string nodename = std::string(test_info->test_case_name()) + "--" + test_info->name();
249     size_t idx = nodename.find_first_of('/');
250     if (idx != std::string::npos)
251         nodename.erase(idx);
252
253     const char* type_param = test_info->type_param();
254     if (type_param != 0)
255         (nodename += "--") += type_param;
256
257     const char* value_param = test_info->value_param();
258     if (value_param != 0)
259         (nodename += "--") += value_param;
260
261     for(size_t i = 0; i < nodename.length(); ++i)
262         if (!isalnum(nodename[i]) && '_' != nodename[i])
263             nodename[i] = '-';
264
265     return nodename;
266 }
267
268 bool Regression::isVector(cv::InputArray a)
269 {
270     return a.kind() == cv::_InputArray::STD_VECTOR_MAT || a.kind() == cv::_InputArray::STD_VECTOR_VECTOR;
271 }
272
273 double Regression::getElem(cv::Mat& m, int y, int x, int cn)
274 {
275     switch (m.depth())
276     {
277     case CV_8U: return *(m.ptr<unsigned char>(y, x) + cn);
278     case CV_8S: return *(m.ptr<signed char>(y, x) + cn);
279     case CV_16U: return *(m.ptr<unsigned short>(y, x) + cn);
280     case CV_16S: return *(m.ptr<signed short>(y, x) + cn);
281     case CV_32S: return *(m.ptr<signed int>(y, x) + cn);
282     case CV_32F: return *(m.ptr<float>(y, x) + cn);
283     case CV_64F: return *(m.ptr<double>(y, x) + cn);
284     default: return 0;
285     }
286 }
287
288 void Regression::write(cv::Mat m)
289 {
290     if (!m.empty() && m.dims < 2) return;
291
292     double min, max;
293     cv::minMaxIdx(m, &min, &max);
294     write() << "min" << min << "max" << max;
295
296     write() << "last" << "{" << "x" << m.size.p[1] - 1 << "y" << m.size.p[0] - 1
297         << "val" << getElem(m, m.size.p[0] - 1, m.size.p[1] - 1, m.channels() - 1) << "}";
298
299     int x, y, cn;
300     x = regRNG.uniform(0, m.size.p[1]);
301     y = regRNG.uniform(0, m.size.p[0]);
302     cn = regRNG.uniform(0, m.channels());
303     write() << "rng1" << "{" << "x" << x << "y" << y;
304     if(cn > 0) write() << "cn" << cn;
305     write() << "val" << getElem(m, y, x, cn) << "}";
306
307     x = regRNG.uniform(0, m.size.p[1]);
308     y = regRNG.uniform(0, m.size.p[0]);
309     cn = regRNG.uniform(0, m.channels());
310     write() << "rng2" << "{" << "x" << x << "y" << y;
311     if (cn > 0) write() << "cn" << cn;
312     write() << "val" << getElem(m, y, x, cn) << "}";
313 }
314
315 void Regression::verify(cv::FileNode node, cv::Mat actual, double eps, std::string argname, ERROR_TYPE err)
316 {
317     if (!actual.empty() && actual.dims < 2) return;
318
319     double expect_min = (double)node["min"];
320     double expect_max = (double)node["max"];
321
322     if (err == ERROR_RELATIVE)
323         eps *= std::max(std::abs(expect_min), std::abs(expect_max));
324
325     double actual_min, actual_max;
326     cv::minMaxIdx(actual, &actual_min, &actual_max);
327
328     ASSERT_NEAR(expect_min, actual_min, eps)
329             << argname << " has unexpected minimal value" << std::endl;
330     ASSERT_NEAR(expect_max, actual_max, eps)
331             << argname << " has unexpected maximal value" << std::endl;
332
333     cv::FileNode last = node["last"];
334     double actual_last = getElem(actual, actual.size.p[0] - 1, actual.size.p[1] - 1, actual.channels() - 1);
335     int expect_cols = (int)last["x"] + 1;
336     int expect_rows = (int)last["y"] + 1;
337     ASSERT_EQ(expect_cols, actual.size.p[1])
338             << argname << " has unexpected number of columns" << std::endl;
339     ASSERT_EQ(expect_rows, actual.size.p[0])
340             << argname << " has unexpected number of rows" << std::endl;
341
342     double expect_last = (double)last["val"];
343     ASSERT_NEAR(expect_last, actual_last, eps)
344             << argname << " has unexpected value of the last element" << std::endl;
345
346     cv::FileNode rng1 = node["rng1"];
347     int x1 = rng1["x"];
348     int y1 = rng1["y"];
349     int cn1 = rng1["cn"];
350
351     double expect_rng1 = (double)rng1["val"];
352     // it is safe to use x1 and y1 without checks here because we have already
353     // verified that mat size is the same as recorded
354     double actual_rng1 = getElem(actual, y1, x1, cn1);
355
356     ASSERT_NEAR(expect_rng1, actual_rng1, eps)
357             << argname << " has unexpected value of the ["<< x1 << ":" << y1 << ":" << cn1 <<"] element" << std::endl;
358
359     cv::FileNode rng2 = node["rng2"];
360     int x2 = rng2["x"];
361     int y2 = rng2["y"];
362     int cn2 = rng2["cn"];
363
364     double expect_rng2 = (double)rng2["val"];
365     double actual_rng2 = getElem(actual, y2, x2, cn2);
366
367     ASSERT_NEAR(expect_rng2, actual_rng2, eps)
368             << argname << " has unexpected value of the ["<< x2 << ":" << y2 << ":" << cn2 <<"] element" << std::endl;
369 }
370
371 void Regression::write(cv::InputArray array)
372 {
373     write() << "kind" << array.kind();
374     write() << "type" << array.type();
375     if (isVector(array))
376     {
377         int total = (int)array.total();
378         int idx = regRNG.uniform(0, total);
379         write() << "len" << total;
380         write() << "idx" << idx;
381
382         cv::Mat m = array.getMat(idx);
383
384         if (m.total() * m.channels() < 26) //5x5 or smaller
385             write() << "val" << m;
386         else
387             write(m);
388     }
389     else
390     {
391         if (array.total() * array.channels() < 26) //5x5 or smaller
392             write() << "val" << array.getMat();
393         else
394             write(array.getMat());
395     }
396 }
397
398 static int countViolations(const cv::Mat& expected, const cv::Mat& actual, const cv::Mat& diff, double eps, double* max_violation = 0, double* max_allowed = 0)
399 {
400     cv::Mat diff64f;
401     diff.reshape(1).convertTo(diff64f, CV_64F);
402
403     cv::Mat expected_abs = cv::abs(expected.reshape(1));
404     cv::Mat actual_abs = cv::abs(actual.reshape(1));
405     cv::Mat maximum, mask;
406     cv::max(expected_abs, actual_abs, maximum);
407     cv::multiply(maximum, cv::Vec<double, 1>(eps), maximum, CV_64F);
408     cv::compare(diff64f, maximum, mask, cv::CMP_GT);
409
410     int v = cv::countNonZero(mask);
411
412     if (v > 0 && max_violation != 0 && max_allowed != 0)
413     {
414         int loc[10];
415         cv::minMaxIdx(maximum, 0, max_allowed, 0, loc, mask);
416         *max_violation = diff64f.at<double>(loc[1], loc[0]);
417     }
418
419     return v;
420 }
421
422 void Regression::verify(cv::FileNode node, cv::InputArray array, double eps, ERROR_TYPE err)
423 {
424     int expected_kind = (int)node["kind"];
425     int expected_type = (int)node["type"];
426     ASSERT_EQ(expected_kind, array.kind()) << "  Argument \"" << node.name() << "\" has unexpected kind";
427     ASSERT_EQ(expected_type, array.type()) << "  Argument \"" << node.name() << "\" has unexpected type";
428
429     cv::FileNode valnode = node["val"];
430     if (isVector(array))
431     {
432         int expected_length = (int)node["len"];
433         ASSERT_EQ(expected_length, (int)array.total()) << "  Vector \"" << node.name() << "\" has unexpected length";
434         int idx = node["idx"];
435
436         cv::Mat actual = array.getMat(idx);
437
438         if (valnode.isNone())
439         {
440             ASSERT_LE((size_t)26, actual.total() * (size_t)actual.channels())
441                     << "  \"" << node.name() << "[" <<  idx << "]\" has unexpected number of elements";
442             verify(node, actual, eps, cv::format("%s[%d]", node.name().c_str(), idx), err);
443         }
444         else
445         {
446             cv::Mat expected;
447             valnode >> expected;
448
449             if(expected.empty())
450             {
451                 ASSERT_TRUE(actual.empty())
452                     << "  expected empty " << node.name() << "[" <<  idx<< "]";
453             }
454             else
455             {
456                 ASSERT_EQ(expected.size(), actual.size())
457                         << "  " << node.name() << "[" <<  idx<< "] has unexpected size";
458
459                 cv::Mat diff;
460                 cv::absdiff(expected, actual, diff);
461
462                 if (err == ERROR_ABSOLUTE)
463                 {
464                     if (!cv::checkRange(diff, true, 0, 0, eps))
465                     {
466                         if(expected.total() * expected.channels() < 12)
467                             std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
468
469                         double max;
470                         cv::minMaxIdx(diff.reshape(1), 0, &max);
471
472                         FAIL() << "  Absolute difference (=" << max << ") between argument \""
473                                << node.name() << "[" <<  idx << "]\" and expected value is greater than " << eps;
474                     }
475                 }
476                 else if (err == ERROR_RELATIVE)
477                 {
478                     double maxv, maxa;
479                     int violations = countViolations(expected, actual, diff, eps, &maxv, &maxa);
480                     if (violations > 0)
481                     {
482                         if(expected.total() * expected.channels() < 12)
483                             std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
484
485                         FAIL() << "  Relative difference (" << maxv << " of " << maxa << " allowed) between argument \""
486                                << node.name() << "[" <<  idx << "]\" and expected value is greater than " << eps << " in " << violations << " points";
487                     }
488                 }
489             }
490         }
491     }
492     else
493     {
494         if (valnode.isNone())
495         {
496             ASSERT_LE((size_t)26, array.total() * (size_t)array.channels())
497                     << "  Argument \"" << node.name() << "\" has unexpected number of elements";
498             verify(node, array.getMat(), eps, "Argument \"" + node.name() + "\"", err);
499         }
500         else
501         {
502             cv::Mat expected;
503             valnode >> expected;
504             cv::Mat actual = array.getMat();
505
506             if(expected.empty())
507             {
508                 ASSERT_TRUE(actual.empty())
509                     << "  expected empty " << node.name();
510             }
511             else
512             {
513                 ASSERT_EQ(expected.size(), actual.size())
514                         << "  Argument \"" << node.name() << "\" has unexpected size";
515
516                 cv::Mat diff;
517                 cv::absdiff(expected, actual, diff);
518
519                 if (err == ERROR_ABSOLUTE)
520                 {
521                     if (!cv::checkRange(diff, true, 0, 0, eps))
522                     {
523                         if(expected.total() * expected.channels() < 12)
524                             std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
525
526                         double max;
527                         cv::minMaxIdx(diff.reshape(1), 0, &max);
528
529                         FAIL() << "  Difference (=" << max << ") between argument1 \"" << node.name()
530                                << "\" and expected value is greater than " << eps;
531                     }
532                 }
533                 else if (err == ERROR_RELATIVE)
534                 {
535                     double maxv, maxa;
536                     int violations = countViolations(expected, actual, diff, eps, &maxv, &maxa);
537                     if (violations > 0)
538                     {
539                         if(expected.total() * expected.channels() < 12)
540                             std::cout << " Expected: " << std::endl << expected << std::endl << " Actual:" << std::endl << actual << std::endl;
541
542                         FAIL() << "  Relative difference (" << maxv << " of " << maxa << " allowed) between argument \"" << node.name()
543                                << "\" and expected value is greater than " << eps << " in " << violations << " points";
544                     }
545                 }
546             }
547         }
548     }
549 }
550
551 Regression& Regression::operator() (const std::string& name, cv::InputArray array, double eps, ERROR_TYPE err)
552 {
553     // exit if current test is already failed
554     if(::testing::UnitTest::GetInstance()->current_test_info()->result()->Failed()) return *this;
555
556     if(!array.empty() && array.depth() == CV_USRTYPE1)
557     {
558         ADD_FAILURE() << "  Can not check regression for CV_USRTYPE1 data type for " << name;
559         return *this;
560     }
561
562     std::string nodename = getCurrentTestNodeName();
563
564     cv::FileNode n = rootIn[nodename];
565     if(n.isNone())
566     {
567         if(param_write_sanity)
568         {
569             if (nodename != currentTestNodeName)
570             {
571                 if (!currentTestNodeName.empty())
572                     write() << "}";
573                 currentTestNodeName = nodename;
574
575                 write() << nodename << "{";
576             }
577             // TODO: verify that name is alphanumeric, current error message is useless
578             write() << name << "{";
579             write(array);
580             write() << "}";
581         }
582         else if(param_verify_sanity)
583         {
584             ADD_FAILURE() << "  No regression data for " << name << " argument";
585         }
586     }
587     else
588     {
589         cv::FileNode this_arg = n[name];
590         if (!this_arg.isMap())
591             ADD_FAILURE() << "  No regression data for " << name << " argument";
592         else
593             verify(this_arg, array, eps, err);
594     }
595
596     return *this;
597 }
598
599
600 /*****************************************************************************************\
601 *                                ::perf::performance_metrics
602 \*****************************************************************************************/
603 performance_metrics::performance_metrics()
604 {
605     clear();
606 }
607
608 void performance_metrics::clear()
609 {
610     bytesIn = 0;
611     bytesOut = 0;
612     samples = 0;
613     outliers = 0;
614     gmean = 0;
615     gstddev = 0;
616     mean = 0;
617     stddev = 0;
618     median = 0;
619     min = 0;
620     frequency = 0;
621     terminationReason = TERM_UNKNOWN;
622 }
623
624
625 /*****************************************************************************************\
626 *                                   ::perf::TestBase
627 \*****************************************************************************************/
628
629
630 void TestBase::Init(int argc, const char* const argv[])
631 {
632     std::vector<std::string> plain_only;
633     plain_only.push_back("plain");
634     TestBase::Init(plain_only, argc, argv);
635 }
636
637 void TestBase::Init(const std::vector<std::string> & availableImpls,
638                  int argc, const char* const argv[])
639 {
640     available_impls = availableImpls;
641
642     const std::string command_line_keys =
643         "{   perf_max_outliers           |8        |percent of allowed outliers}"
644         "{   perf_min_samples            |10       |minimal required numer of samples}"
645         "{   perf_force_samples          |100      |force set maximum number of samples for all tests}"
646         "{   perf_seed                   |809564   |seed for random numbers generator}"
647         "{   perf_threads                |-1       |the number of worker threads, if parallel execution is enabled}"
648         "{   perf_write_sanity           |false    |create new records for sanity checks}"
649         "{   perf_verify_sanity          |false    |fail tests having no regression data for sanity checks}"
650         "{   perf_impl                   |" + available_impls[0] +
651                                                   "|the implementation variant of functions under test}"
652         "{   perf_list_impls             |false    |list available implementation variants and exit}"
653         "{   perf_run_cpu                |false    |deprecated, equivalent to --perf_impl=plain}"
654         "{   perf_strategy               |default  |specifies performance measuring strategy: default, base or simple (weak restrictions)}"
655 #ifdef ANDROID
656         "{   perf_time_limit             |6.0      |default time limit for a single test (in seconds)}"
657         "{   perf_affinity_mask          |0        |set affinity mask for the main thread}"
658         "{   perf_log_power_checkpoints  |         |additional xml logging for power measurement}"
659 #else
660         "{   perf_time_limit             |3.0      |default time limit for a single test (in seconds)}"
661 #endif
662         "{   perf_max_deviation          |1.0      |}"
663         "{   help h                      |false    |print help info}"
664 #ifdef HAVE_CUDA
665         "{   perf_cuda_device            |0        |run CUDA test suite onto specific CUDA capable device}"
666         "{   perf_cuda_info_only         |false    |print an information about system and an available CUDA devices and then exit.}"
667 #endif
668     ;
669
670     cv::CommandLineParser args(argc, argv, command_line_keys);
671     if (args.has("help"))
672     {
673         args.printMessage();
674         return;
675     }
676
677     ::testing::AddGlobalTestEnvironment(new PerfEnvironment);
678
679     param_impl          = args.has("perf_run_cpu") ? "plain" : args.get<std::string>("perf_impl");
680     std::string perf_strategy = args.get<std::string>("perf_strategy");
681     if (perf_strategy == "default")
682     {
683         // nothing
684     }
685     else if (perf_strategy == "base")
686     {
687         param_strategy = PERF_STRATEGY_BASE;
688     }
689     else if (perf_strategy == "simple")
690     {
691         param_strategy = PERF_STRATEGY_SIMPLE;
692     }
693     else
694     {
695         printf("No such strategy: %s\n", perf_strategy.c_str());
696         exit(1);
697     }
698     param_max_outliers  = std::min(100., std::max(0., args.get<double>("perf_max_outliers")));
699     param_min_samples   = std::max(1u, args.get<unsigned int>("perf_min_samples"));
700     param_max_deviation = std::max(0., args.get<double>("perf_max_deviation"));
701     param_seed          = args.get<unsigned int>("perf_seed");
702     param_time_limit    = std::max(0., args.get<double>("perf_time_limit"));
703     param_force_samples = args.get<unsigned int>("perf_force_samples");
704     param_write_sanity  = args.has("perf_write_sanity");
705     param_verify_sanity = args.has("perf_verify_sanity");
706     param_threads  = args.get<int>("perf_threads");
707 #ifdef ANDROID
708     param_affinity_mask   = args.get<int>("perf_affinity_mask");
709     log_power_checkpoints = args.has("perf_log_power_checkpoints");
710 #endif
711
712     bool param_list_impls = args.has("perf_list_impls");
713
714     if (param_list_impls)
715     {
716         fputs("Available implementation variants:", stdout);
717         for (size_t i = 0; i < available_impls.size(); ++i) {
718             putchar(' ');
719             fputs(available_impls[i].c_str(), stdout);
720         }
721         putchar('\n');
722         exit(0);
723     }
724
725     if (std::find(available_impls.begin(), available_impls.end(), param_impl) == available_impls.end())
726     {
727         printf("No such implementation: %s\n", param_impl.c_str());
728         exit(1);
729     }
730
731 #ifdef HAVE_CUDA
732
733     bool printOnly        = args.has("perf_cuda_info_only");
734
735     if (printOnly)
736         exit(0);
737 #endif
738
739     if (available_impls.size() > 1)
740         printf("[----------]\n[   INFO   ] \tImplementation variant: %s.\n[----------]\n", param_impl.c_str()), fflush(stdout);
741
742 #ifdef HAVE_CUDA
743
744     param_cuda_device      = std::max(0, std::min(cv::cuda::getCudaEnabledDeviceCount(), args.get<int>("perf_cuda_device")));
745
746     if (param_impl == "cuda")
747     {
748         cv::cuda::DeviceInfo info(param_cuda_device);
749         if (!info.isCompatible())
750         {
751             printf("[----------]\n[ FAILURE  ] \tDevice %s is NOT compatible with current CUDA module build.\n[----------]\n", info.name()), fflush(stdout);
752             exit(-1);
753         }
754
755         cv::cuda::setDevice(param_cuda_device);
756
757         printf("[----------]\n[ GPU INFO ] \tRun test suite on %s GPU.\n[----------]\n", info.name()), fflush(stdout);
758     }
759 #endif
760
761     if (!args.check())
762     {
763         args.printErrors();
764         return;
765     }
766
767     timeLimitDefault = param_time_limit == 0.0 ? 1 : (int64)(param_time_limit * cv::getTickFrequency());
768     iterationsLimitDefault = param_force_samples == 0 ? (unsigned)(-1) : param_force_samples;
769     _timeadjustment = _calibrate();
770 }
771
772 void TestBase::RecordRunParameters()
773 {
774     ::testing::Test::RecordProperty("cv_implementation", param_impl);
775     ::testing::Test::RecordProperty("cv_num_threads", param_threads);
776
777 #ifdef HAVE_CUDA
778     if (param_impl == "cuda")
779     {
780         cv::cuda::DeviceInfo info(param_cuda_device);
781         ::testing::Test::RecordProperty("cv_cuda_gpu", info.name());
782     }
783 #endif
784 }
785
786 std::string TestBase::getSelectedImpl()
787 {
788     return param_impl;
789 }
790
791 enum PERF_STRATEGY TestBase::getPerformanceStrategy()
792 {
793     return param_strategy;
794 }
795
796 enum PERF_STRATEGY TestBase::setPerformanceStrategy(enum PERF_STRATEGY strategy)
797 {
798     enum PERF_STRATEGY ret = param_strategy;
799     param_strategy = strategy;
800     return ret;
801 }
802
803
804 int64 TestBase::_calibrate()
805 {
806     class _helper : public ::perf::TestBase
807     {
808         public:
809         performance_metrics& getMetrics() { return calcMetrics(); }
810         virtual void TestBody() {}
811         virtual void PerfTestBody()
812         {
813             //the whole system warmup
814             SetUp();
815             cv::Mat a(2048, 2048, CV_32S, cv::Scalar(1));
816             cv::Mat b(2048, 2048, CV_32S, cv::Scalar(2));
817             declare.time(30);
818             double s = 0;
819             for(declare.iterations(20); startTimer(), next(); stopTimer())
820                 s+=a.dot(b);
821             declare.time(s);
822
823             //self calibration
824             SetUp();
825             for(declare.iterations(1000); startTimer(), next(); stopTimer()){}
826         }
827     };
828
829     _timeadjustment = 0;
830     _helper h;
831     h.PerfTestBody();
832     double compensation = h.getMetrics().min;
833     if (param_strategy == PERF_STRATEGY_SIMPLE)
834     {
835         CV_Assert(compensation < 0.01 * cv::getTickFrequency());
836         compensation = 0.0f; // simple strategy doesn't require any compensation
837     }
838     LOGD("Time compensation is %.0f", compensation);
839     return (int64)compensation;
840 }
841
842 #ifdef _MSC_VER
843 # pragma warning(push)
844 # pragma warning(disable:4355)  // 'this' : used in base member initializer list
845 #endif
846 TestBase::TestBase(): declare(this)
847 {
848 }
849 #ifdef _MSC_VER
850 # pragma warning(pop)
851 #endif
852
853
854 void TestBase::declareArray(SizeVector& sizes, cv::InputOutputArray a, int wtype)
855 {
856     if (!a.empty())
857     {
858         sizes.push_back(std::pair<int, cv::Size>(getSizeInBytes(a), getSize(a)));
859         warmup(a, wtype);
860     }
861     else if (a.kind() != cv::_InputArray::NONE)
862         ADD_FAILURE() << "  Uninitialized input/output parameters are not allowed for performance tests";
863 }
864
865 void TestBase::warmup(cv::InputOutputArray a, int wtype)
866 {
867     if (a.empty()) return;
868     if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
869         warmup_impl(a.getMat(), wtype);
870     else
871     {
872         size_t total = a.total();
873         for (size_t i = 0; i < total; ++i)
874             warmup_impl(a.getMat((int)i), wtype);
875     }
876 }
877
878 int TestBase::getSizeInBytes(cv::InputArray a)
879 {
880     if (a.empty()) return 0;
881     int total = (int)a.total();
882     if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
883         return total * CV_ELEM_SIZE(a.type());
884
885     int size = 0;
886     for (int i = 0; i < total; ++i)
887         size += (int)a.total(i) * CV_ELEM_SIZE(a.type(i));
888
889     return size;
890 }
891
892 cv::Size TestBase::getSize(cv::InputArray a)
893 {
894     if (a.kind() != cv::_InputArray::STD_VECTOR_MAT && a.kind() != cv::_InputArray::STD_VECTOR_VECTOR)
895         return a.size();
896     return cv::Size();
897 }
898
899 bool TestBase::next()
900 {
901     static int64 lastActivityPrintTime = 0;
902
903     if (currentIter != (unsigned int)-1)
904     {
905         if (currentIter + 1 != times.size())
906             ADD_FAILURE() << "  next() is called before stopTimer()";
907     }
908     else
909     {
910         lastActivityPrintTime = 0;
911         metrics.clear();
912     }
913
914     cv::theRNG().state = param_seed; //this rng should generate same numbers for each run
915     ++currentIter;
916
917     bool has_next = false;
918
919     do {
920         assert(currentIter == times.size());
921         if (currentIter == 0)
922         {
923             has_next = true;
924             break;
925         }
926
927         if (param_strategy == PERF_STRATEGY_BASE)
928         {
929             has_next = currentIter < nIters && totalTime < timeLimit;
930         }
931         else
932         {
933             assert(param_strategy == PERF_STRATEGY_SIMPLE);
934             if (totalTime - lastActivityPrintTime >= cv::getTickFrequency() * 10)
935             {
936                 std::cout << '.' << std::endl;
937                 lastActivityPrintTime = totalTime;
938             }
939             if (currentIter >= nIters)
940             {
941                 has_next = false;
942                 break;
943             }
944             if (currentIter < param_min_samples)
945             {
946                 has_next = true;
947                 break;
948             }
949
950             calcMetrics();
951
952             double criteria = 0.03;  // 3%
953             if (fabs(metrics.mean) > 1e-6)
954                 has_next = metrics.stddev > criteria * fabs(metrics.mean);
955             else
956                 has_next = true;
957         }
958     } while (false);
959
960 #ifdef ANDROID
961     if (log_power_checkpoints)
962     {
963         timeval tim;
964         gettimeofday(&tim, NULL);
965         unsigned long long t1 = tim.tv_sec * 1000LLU + (unsigned long long)(tim.tv_usec / 1000.f);
966
967         if (currentIter == 1) RecordProperty("test_start", cv::format("%llu",t1).c_str());
968         if (!has_next) RecordProperty("test_complete", cv::format("%llu",t1).c_str());
969     }
970 #endif
971
972     if (has_next)
973         startTimer(); // really we should measure activity from this moment, so reset start time
974     return has_next;
975 }
976
977 void TestBase::warmup_impl(cv::Mat m, int wtype)
978 {
979     switch(wtype)
980     {
981     case WARMUP_READ:
982         cv::sum(m.reshape(1));
983         return;
984     case WARMUP_WRITE:
985         m.reshape(1).setTo(cv::Scalar::all(0));
986         return;
987     case WARMUP_RNG:
988         randu(m);
989         return;
990     default:
991         return;
992     }
993 }
994
995 unsigned int TestBase::getTotalInputSize() const
996 {
997     unsigned int res = 0;
998     for (SizeVector::const_iterator i = inputData.begin(); i != inputData.end(); ++i)
999         res += i->first;
1000     return res;
1001 }
1002
1003 unsigned int TestBase::getTotalOutputSize() const
1004 {
1005     unsigned int res = 0;
1006     for (SizeVector::const_iterator i = outputData.begin(); i != outputData.end(); ++i)
1007         res += i->first;
1008     return res;
1009 }
1010
1011 void TestBase::startTimer()
1012 {
1013     lastTime = cv::getTickCount();
1014 }
1015
1016 void TestBase::stopTimer()
1017 {
1018     int64 time = cv::getTickCount();
1019     if (lastTime == 0)
1020         ADD_FAILURE() << "  stopTimer() is called before startTimer()/next()";
1021     lastTime = time - lastTime;
1022     totalTime += lastTime;
1023     lastTime -= _timeadjustment;
1024     if (lastTime < 0) lastTime = 0;
1025     times.push_back(lastTime);
1026     lastTime = 0;
1027 }
1028
1029 performance_metrics& TestBase::calcMetrics()
1030 {
1031     CV_Assert(metrics.samples <= (unsigned int)currentIter);
1032     if ((metrics.samples == (unsigned int)currentIter) || times.size() == 0)
1033         return metrics;
1034
1035     metrics.bytesIn = getTotalInputSize();
1036     metrics.bytesOut = getTotalOutputSize();
1037     metrics.frequency = cv::getTickFrequency();
1038     metrics.samples = (unsigned int)times.size();
1039     metrics.outliers = 0;
1040
1041     if (metrics.terminationReason != performance_metrics::TERM_INTERRUPT && metrics.terminationReason != performance_metrics::TERM_EXCEPTION)
1042     {
1043         if (currentIter == nIters)
1044             metrics.terminationReason = performance_metrics::TERM_ITERATIONS;
1045         else if (totalTime >= timeLimit)
1046             metrics.terminationReason = performance_metrics::TERM_TIME;
1047         else
1048             metrics.terminationReason = performance_metrics::TERM_UNKNOWN;
1049     }
1050
1051     std::sort(times.begin(), times.end());
1052
1053     TimeVector::const_iterator start = times.begin();
1054     TimeVector::const_iterator end = times.end();
1055
1056     if (param_strategy == PERF_STRATEGY_BASE)
1057     {
1058         //estimate mean and stddev for log(time)
1059         double gmean = 0;
1060         double gstddev = 0;
1061         int n = 0;
1062         for(TimeVector::const_iterator i = times.begin(); i != times.end(); ++i)
1063         {
1064             double x = static_cast<double>(*i)/runsPerIteration;
1065             if (x < DBL_EPSILON) continue;
1066             double lx = log(x);
1067
1068             ++n;
1069             double delta = lx - gmean;
1070             gmean += delta / n;
1071             gstddev += delta * (lx - gmean);
1072         }
1073
1074         gstddev = n > 1 ? sqrt(gstddev / (n - 1)) : 0;
1075
1076         //filter outliers assuming log-normal distribution
1077         //http://stackoverflow.com/questions/1867426/modeling-distribution-of-performance-measurements
1078         if (gstddev > DBL_EPSILON)
1079         {
1080             double minout = exp(gmean - 3 * gstddev) * runsPerIteration;
1081             double maxout = exp(gmean + 3 * gstddev) * runsPerIteration;
1082             while(*start < minout) ++start, ++metrics.outliers;
1083             do --end, ++metrics.outliers; while(*end > maxout);
1084             ++end, --metrics.outliers;
1085         }
1086     }
1087     else if (param_strategy == PERF_STRATEGY_SIMPLE)
1088     {
1089         metrics.outliers = static_cast<int>(times.size() * param_max_outliers / 100);
1090         for (unsigned int i = 0; i < metrics.outliers; i++)
1091             --end;
1092     }
1093     else
1094     {
1095         assert(false);
1096     }
1097
1098     int offset = static_cast<int>(start - times.begin());
1099
1100     metrics.min = static_cast<double>(*start)/runsPerIteration;
1101     //calc final metrics
1102     unsigned int n = 0;
1103     double gmean = 0;
1104     double gstddev = 0;
1105     double mean = 0;
1106     double stddev = 0;
1107     unsigned int m = 0;
1108     for(; start != end; ++start)
1109     {
1110         double x = static_cast<double>(*start)/runsPerIteration;
1111         if (x > DBL_EPSILON)
1112         {
1113             double lx = log(x);
1114             ++m;
1115             double gdelta = lx - gmean;
1116             gmean += gdelta / m;
1117             gstddev += gdelta * (lx - gmean);
1118         }
1119         ++n;
1120         double delta = x - mean;
1121         mean += delta / n;
1122         stddev += delta * (x - mean);
1123     }
1124
1125     metrics.mean = mean;
1126     metrics.gmean = exp(gmean);
1127     metrics.gstddev = m > 1 ? sqrt(gstddev / (m - 1)) : 0;
1128     metrics.stddev = n > 1 ? sqrt(stddev / (n - 1)) : 0;
1129     metrics.median = (n % 2
1130             ? (double)times[offset + n / 2]
1131             : 0.5 * (times[offset + n / 2] + times[offset + n / 2 - 1])
1132             ) / runsPerIteration;
1133
1134     return metrics;
1135 }
1136
1137 void TestBase::validateMetrics()
1138 {
1139     performance_metrics& m = calcMetrics();
1140
1141     if (HasFailure()) return;
1142
1143     ASSERT_GE(m.samples, 1u)
1144       << "  No time measurements was performed.\nstartTimer() and stopTimer() commands are required for performance tests.";
1145
1146     if (param_strategy == PERF_STRATEGY_BASE)
1147     {
1148         EXPECT_GE(m.samples, param_min_samples)
1149           << "  Only a few samples are collected.\nPlease increase number of iterations or/and time limit to get reliable performance measurements.";
1150
1151         if (m.gstddev > DBL_EPSILON)
1152         {
1153             EXPECT_GT(/*m.gmean * */1., /*m.gmean * */ 2 * sinh(m.gstddev * param_max_deviation))
1154               << "  Test results are not reliable ((mean-sigma,mean+sigma) deviation interval is greater than measured time interval).";
1155         }
1156
1157         EXPECT_LE(m.outliers, std::max((unsigned int)cvCeil(m.samples * param_max_outliers / 100.), 1u))
1158           << "  Test results are not reliable (too many outliers).";
1159     }
1160     else if (param_strategy == PERF_STRATEGY_SIMPLE)
1161     {
1162         double mean = metrics.mean * 1000.0f / metrics.frequency;
1163         double stddev = metrics.stddev * 1000.0f / metrics.frequency;
1164         double percents = stddev / mean * 100.f;
1165         printf("    samples = %d, mean = %.2f, stddev = %.2f (%.1f%%)\n", (int)metrics.samples, mean, stddev, percents);
1166     }
1167     else
1168     {
1169         assert(false);
1170     }
1171 }
1172
1173 void TestBase::reportMetrics(bool toJUnitXML)
1174 {
1175     performance_metrics& m = calcMetrics();
1176
1177     if (m.terminationReason == performance_metrics::TERM_SKIP_TEST)
1178     {
1179         if (toJUnitXML)
1180         {
1181             RecordProperty("custom_status", "skipped");
1182         }
1183     }
1184     else if (toJUnitXML)
1185     {
1186         RecordProperty("bytesIn", (int)m.bytesIn);
1187         RecordProperty("bytesOut", (int)m.bytesOut);
1188         RecordProperty("term", m.terminationReason);
1189         RecordProperty("samples", (int)m.samples);
1190         RecordProperty("outliers", (int)m.outliers);
1191         RecordProperty("frequency", cv::format("%.0f", m.frequency).c_str());
1192         RecordProperty("min", cv::format("%.0f", m.min).c_str());
1193         RecordProperty("median", cv::format("%.0f", m.median).c_str());
1194         RecordProperty("gmean", cv::format("%.0f", m.gmean).c_str());
1195         RecordProperty("gstddev", cv::format("%.6f", m.gstddev).c_str());
1196         RecordProperty("mean", cv::format("%.0f", m.mean).c_str());
1197         RecordProperty("stddev", cv::format("%.0f", m.stddev).c_str());
1198     }
1199     else
1200     {
1201         const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
1202         const char* type_param = test_info->type_param();
1203         const char* value_param = test_info->value_param();
1204
1205 #if defined(ANDROID) && defined(USE_ANDROID_LOGGING)
1206         LOGD("[ FAILED   ] %s.%s", test_info->test_case_name(), test_info->name());
1207 #endif
1208
1209         if (type_param)  LOGD("type      = %11s", type_param);
1210         if (value_param) LOGD("params    = %11s", value_param);
1211
1212         switch (m.terminationReason)
1213         {
1214         case performance_metrics::TERM_ITERATIONS:
1215             LOGD("termination reason:  reached maximum number of iterations");
1216             break;
1217         case performance_metrics::TERM_TIME:
1218             LOGD("termination reason:  reached time limit");
1219             break;
1220         case performance_metrics::TERM_INTERRUPT:
1221             LOGD("termination reason:  aborted by the performance testing framework");
1222             break;
1223         case performance_metrics::TERM_EXCEPTION:
1224             LOGD("termination reason:  unhandled exception");
1225             break;
1226         case performance_metrics::TERM_UNKNOWN:
1227         default:
1228             LOGD("termination reason:  unknown");
1229             break;
1230         };
1231
1232         LOGD("bytesIn   =%11lu", (unsigned long)m.bytesIn);
1233         LOGD("bytesOut  =%11lu", (unsigned long)m.bytesOut);
1234         if (nIters == (unsigned int)-1 || m.terminationReason == performance_metrics::TERM_ITERATIONS)
1235             LOGD("samples   =%11u",  m.samples);
1236         else
1237             LOGD("samples   =%11u of %u", m.samples, nIters);
1238         LOGD("outliers  =%11u", m.outliers);
1239         LOGD("frequency =%11.0f", m.frequency);
1240         if (m.samples > 0)
1241         {
1242             LOGD("min       =%11.0f = %.2fms", m.min, m.min * 1e3 / m.frequency);
1243             LOGD("median    =%11.0f = %.2fms", m.median, m.median * 1e3 / m.frequency);
1244             LOGD("gmean     =%11.0f = %.2fms", m.gmean, m.gmean * 1e3 / m.frequency);
1245             LOGD("gstddev   =%11.8f = %.2fms for 97%% dispersion interval", m.gstddev, m.gmean * 2 * sinh(m.gstddev * 3) * 1e3 / m.frequency);
1246             LOGD("mean      =%11.0f = %.2fms", m.mean, m.mean * 1e3 / m.frequency);
1247             LOGD("stddev    =%11.0f = %.2fms", m.stddev, m.stddev * 1e3 / m.frequency);
1248         }
1249     }
1250 }
1251
1252 void TestBase::SetUp()
1253 {
1254     cv::theRNG().state = param_seed; // this rng should generate same numbers for each run
1255
1256     if (param_threads >= 0)
1257         cv::setNumThreads(param_threads);
1258
1259 #ifdef ANDROID
1260     if (param_affinity_mask)
1261         setCurrentThreadAffinityMask(param_affinity_mask);
1262 #endif
1263
1264     verified = false;
1265     lastTime = 0;
1266     totalTime = 0;
1267     runsPerIteration = 1;
1268     nIters = iterationsLimitDefault;
1269     currentIter = (unsigned int)-1;
1270     timeLimit = timeLimitDefault;
1271     times.clear();
1272 }
1273
1274 void TestBase::TearDown()
1275 {
1276     if (metrics.terminationReason == performance_metrics::TERM_SKIP_TEST)
1277     {
1278         LOGI("\tTest was skipped");
1279         GTEST_SUCCEED() << "Test was skipped";
1280     }
1281     else
1282     {
1283         if (!HasFailure() && !verified)
1284             ADD_FAILURE() << "The test has no sanity checks. There should be at least one check at the end of performance test.";
1285
1286         validateMetrics();
1287         if (HasFailure())
1288         {
1289             reportMetrics(false);
1290             return;
1291         }
1292     }
1293
1294     const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
1295     const char* type_param = test_info->type_param();
1296     const char* value_param = test_info->value_param();
1297     if (value_param) printf("[ VALUE    ] \t%s\n", value_param), fflush(stdout);
1298     if (type_param)  printf("[ TYPE     ] \t%s\n", type_param), fflush(stdout);
1299     reportMetrics(true);
1300 }
1301
1302 std::string TestBase::getDataPath(const std::string& relativePath)
1303 {
1304     if (relativePath.empty())
1305     {
1306         ADD_FAILURE() << "  Bad path to test resource";
1307         throw PerfEarlyExitException();
1308     }
1309
1310     const char *data_path_dir = getenv("OPENCV_TEST_DATA_PATH");
1311     const char *path_separator = "/";
1312
1313     std::string path;
1314     if (data_path_dir)
1315     {
1316         int len = (int)strlen(data_path_dir) - 1;
1317         if (len < 0) len = 0;
1318         path = (data_path_dir[0] == 0 ? std::string(".") : std::string(data_path_dir))
1319                 + (data_path_dir[len] == '/' || data_path_dir[len] == '\\' ? "" : path_separator);
1320     }
1321     else
1322     {
1323         path = ".";
1324         path += path_separator;
1325     }
1326
1327     if (relativePath[0] == '/' || relativePath[0] == '\\')
1328         path += relativePath.substr(1);
1329     else
1330         path += relativePath;
1331
1332     FILE* fp = fopen(path.c_str(), "r");
1333     if (fp)
1334         fclose(fp);
1335     else
1336     {
1337         ADD_FAILURE() << "  Requested file \"" << path << "\" does not exist.";
1338         throw PerfEarlyExitException();
1339     }
1340     return path;
1341 }
1342
1343 void TestBase::RunPerfTestBody()
1344 {
1345     try
1346     {
1347         this->PerfTestBody();
1348     }
1349     catch(PerfSkipTestException&)
1350     {
1351         metrics.terminationReason = performance_metrics::TERM_SKIP_TEST;
1352         return;
1353     }
1354     catch(PerfEarlyExitException&)
1355     {
1356         metrics.terminationReason = performance_metrics::TERM_INTERRUPT;
1357         return;//no additional failure logging
1358     }
1359     catch(cv::Exception& e)
1360     {
1361         metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
1362         #ifdef HAVE_CUDA
1363             if (e.code == cv::Error::GpuApiCallError)
1364                 cv::cuda::resetDevice();
1365         #endif
1366         FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n  Actual: it throws cv::Exception:\n  " << e.what();
1367     }
1368     catch(std::exception& e)
1369     {
1370         metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
1371         FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n  Actual: it throws std::exception:\n  " << e.what();
1372     }
1373     catch(...)
1374     {
1375         metrics.terminationReason = performance_metrics::TERM_EXCEPTION;
1376         FAIL() << "Expected: PerfTestBody() doesn't throw an exception.\n  Actual: it throws...";
1377     }
1378 }
1379
1380 /*****************************************************************************************\
1381 *                          ::perf::TestBase::_declareHelper
1382 \*****************************************************************************************/
1383 TestBase::_declareHelper& TestBase::_declareHelper::iterations(unsigned int n)
1384 {
1385     test->times.clear();
1386     test->times.reserve(n);
1387     test->nIters = std::min(n, TestBase::iterationsLimitDefault);
1388     test->currentIter = (unsigned int)-1;
1389     test->metrics.clear();
1390     return *this;
1391 }
1392
1393 TestBase::_declareHelper& TestBase::_declareHelper::time(double timeLimitSecs)
1394 {
1395     test->times.clear();
1396     test->currentIter = (unsigned int)-1;
1397     test->timeLimit = (int64)(timeLimitSecs * cv::getTickFrequency());
1398     test->metrics.clear();
1399     return *this;
1400 }
1401
1402 TestBase::_declareHelper& TestBase::_declareHelper::tbb_threads(int n)
1403 {
1404     cv::setNumThreads(n);
1405     return *this;
1406 }
1407
1408 TestBase::_declareHelper& TestBase::_declareHelper::runs(unsigned int runsNumber)
1409 {
1410     test->runsPerIteration = runsNumber;
1411     return *this;
1412 }
1413
1414 TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, int wtype)
1415 {
1416     if (!test->times.empty()) return *this;
1417     TestBase::declareArray(test->inputData, a1, wtype);
1418     return *this;
1419 }
1420
1421 TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, int wtype)
1422 {
1423     if (!test->times.empty()) return *this;
1424     TestBase::declareArray(test->inputData, a1, wtype);
1425     TestBase::declareArray(test->inputData, a2, wtype);
1426     return *this;
1427 }
1428
1429 TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, int wtype)
1430 {
1431     if (!test->times.empty()) return *this;
1432     TestBase::declareArray(test->inputData, a1, wtype);
1433     TestBase::declareArray(test->inputData, a2, wtype);
1434     TestBase::declareArray(test->inputData, a3, wtype);
1435     return *this;
1436 }
1437
1438 TestBase::_declareHelper& TestBase::_declareHelper::in(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, cv::InputOutputArray a4, int wtype)
1439 {
1440     if (!test->times.empty()) return *this;
1441     TestBase::declareArray(test->inputData, a1, wtype);
1442     TestBase::declareArray(test->inputData, a2, wtype);
1443     TestBase::declareArray(test->inputData, a3, wtype);
1444     TestBase::declareArray(test->inputData, a4, wtype);
1445     return *this;
1446 }
1447
1448 TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, int wtype)
1449 {
1450     if (!test->times.empty()) return *this;
1451     TestBase::declareArray(test->outputData, a1, wtype);
1452     return *this;
1453 }
1454
1455 TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, int wtype)
1456 {
1457     if (!test->times.empty()) return *this;
1458     TestBase::declareArray(test->outputData, a1, wtype);
1459     TestBase::declareArray(test->outputData, a2, wtype);
1460     return *this;
1461 }
1462
1463 TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, int wtype)
1464 {
1465     if (!test->times.empty()) return *this;
1466     TestBase::declareArray(test->outputData, a1, wtype);
1467     TestBase::declareArray(test->outputData, a2, wtype);
1468     TestBase::declareArray(test->outputData, a3, wtype);
1469     return *this;
1470 }
1471
1472 TestBase::_declareHelper& TestBase::_declareHelper::out(cv::InputOutputArray a1, cv::InputOutputArray a2, cv::InputOutputArray a3, cv::InputOutputArray a4, int wtype)
1473 {
1474     if (!test->times.empty()) return *this;
1475     TestBase::declareArray(test->outputData, a1, wtype);
1476     TestBase::declareArray(test->outputData, a2, wtype);
1477     TestBase::declareArray(test->outputData, a3, wtype);
1478     TestBase::declareArray(test->outputData, a4, wtype);
1479     return *this;
1480 }
1481
1482 TestBase::_declareHelper::_declareHelper(TestBase* t) : test(t)
1483 {
1484 }
1485
1486 /*****************************************************************************************\
1487 *                                  miscellaneous
1488 \*****************************************************************************************/
1489
1490 namespace {
1491 struct KeypointComparator
1492 {
1493     std::vector<cv::KeyPoint>& pts_;
1494     comparators::KeypointGreater cmp;
1495
1496     KeypointComparator(std::vector<cv::KeyPoint>& pts) : pts_(pts), cmp() {}
1497
1498     bool operator()(int idx1, int idx2) const
1499     {
1500         return cmp(pts_[idx1], pts_[idx2]);
1501     }
1502 private:
1503     const KeypointComparator& operator=(const KeypointComparator&); // quiet MSVC
1504 };
1505 }//namespace
1506
1507 void perf::sort(std::vector<cv::KeyPoint>& pts, cv::InputOutputArray descriptors)
1508 {
1509     cv::Mat desc = descriptors.getMat();
1510
1511     CV_Assert(pts.size() == (size_t)desc.rows);
1512     cv::AutoBuffer<int> idxs(desc.rows);
1513
1514     for (int i = 0; i < desc.rows; ++i)
1515         idxs[i] = i;
1516
1517     std::sort((int*)idxs, (int*)idxs + desc.rows, KeypointComparator(pts));
1518
1519     std::vector<cv::KeyPoint> spts(pts.size());
1520     cv::Mat sdesc(desc.size(), desc.type());
1521
1522     for(int j = 0; j < desc.rows; ++j)
1523     {
1524         spts[j] = pts[idxs[j]];
1525         cv::Mat row = sdesc.row(j);
1526         desc.row(idxs[j]).copyTo(row);
1527     }
1528
1529     spts.swap(pts);
1530     sdesc.copyTo(desc);
1531 }
1532
1533 /*****************************************************************************************\
1534 *                                  ::perf::GpuPerf
1535 \*****************************************************************************************/
1536 bool perf::GpuPerf::targetDevice()
1537 {
1538     return param_impl == "cuda";
1539 }
1540
1541 /*****************************************************************************************\
1542 *                                  ::perf::PrintTo
1543 \*****************************************************************************************/
1544 namespace perf
1545 {
1546
1547 void PrintTo(const MatType& t, ::std::ostream* os)
1548 {
1549     switch( CV_MAT_DEPTH((int)t) )
1550     {
1551         case CV_8U:  *os << "8U";  break;
1552         case CV_8S:  *os << "8S";  break;
1553         case CV_16U: *os << "16U"; break;
1554         case CV_16S: *os << "16S"; break;
1555         case CV_32S: *os << "32S"; break;
1556         case CV_32F: *os << "32F"; break;
1557         case CV_64F: *os << "64F"; break;
1558         case CV_USRTYPE1: *os << "USRTYPE1"; break;
1559         default: *os << "INVALID_TYPE"; break;
1560     }
1561     *os << 'C' << CV_MAT_CN((int)t);
1562 }
1563
1564 } //namespace perf
1565
1566 /*****************************************************************************************\
1567 *                                  ::cv::PrintTo
1568 \*****************************************************************************************/
1569 namespace cv {
1570
1571 void PrintTo(const Size& sz, ::std::ostream* os)
1572 {
1573     *os << /*"Size:" << */sz.width << "x" << sz.height;
1574 }
1575
1576 }  // namespace cv