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