From ddca47040b01ae7fff0f6028fbc1acb3f3f1e0d4 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Thu, 16 Aug 2012 16:23:27 +0400 Subject: [PATCH 01/58] updated gpu accuracy tests added posibility to specify device on which tests will be executed --- modules/gpu/test/main.cpp | 93 ++++++++++++------- modules/gpu/test/precomp.hpp | 1 + modules/gpu/test/utility.cpp | 167 ++++++++++++++++++++++++++++------- modules/gpu/test/utility.hpp | 21 +++-- 4 files changed, 212 insertions(+), 70 deletions(-) diff --git a/modules/gpu/test/main.cpp b/modules/gpu/test/main.cpp index 6a8c67d79f..6df7db0a1b 100644 --- a/modules/gpu/test/main.cpp +++ b/modules/gpu/test/main.cpp @@ -49,35 +49,39 @@ using namespace cv::gpu; using namespace cvtest; using namespace testing; -void print_info() +void printInfo() { - printf("\n"); #if defined _WIN32 # if defined _WIN64 - puts("OS: Windows 64"); + puts("OS: Windows x64"); # else - puts("OS: Windows 32"); + puts("OS: Windows x32"); # endif #elif defined linux # if defined _LP64 - puts("OS: Linux 64"); + puts("OS: Linux x64"); # else - puts("OS: Linux 32"); + puts("OS: Linux x32"); # endif #elif defined __APPLE__ # if defined _LP64 - puts("OS: Apple 64"); + puts("OS: Apple x64"); # else - puts("OS: Apple 32"); + puts("OS: Apple x32"); # endif #endif - int deviceCount = getCudaEnabledDeviceCount(); int driver; cudaDriverGetVersion(&driver); printf("CUDA Driver version: %d\n", driver); printf("CUDA Runtime version: %d\n", CUDART_VERSION); + + puts("GPU module was compiled for the following GPU archs:"); + printf(" BIN: %s\n", CUDA_ARCH_BIN); + printf(" PTX: %s\n\n", CUDA_ARCH_PTX); + + int deviceCount = getCudaEnabledDeviceCount(); printf("CUDA device count: %d\n\n", deviceCount); for (int i = 0; i < deviceCount; ++i) @@ -87,17 +91,13 @@ void print_info() printf("Device %d:\n", i); printf(" Name: %s\n", info.name().c_str()); printf(" Compute capability version: %d.%d\n", info.majorVersion(), info.minorVersion()); + printf(" Multi Processor Count: %d\n", info.multiProcessorCount()); printf(" Total memory: %d Mb\n", static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0)); printf(" Free memory: %d Mb\n", static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0)); - if (info.isCompatible()) - puts(" This device is compatible with current GPU module build\n"); - else - puts(" This device is NOT compatible with current GPU module build\n"); + if (!info.isCompatible()) + puts(" !!! This device is NOT compatible with current GPU module build\n"); + printf("\n"); } - - puts("GPU module was compiled for the following GPU archs:"); - printf(" BIN: %s\n", CUDA_ARCH_BIN); - printf(" PTX: %s\n\n", CUDA_ARCH_PTX); } enum OutputLevel @@ -111,25 +111,56 @@ extern OutputLevel nvidiaTestOutputLevel; int main(int argc, char** argv) { - TS::ptr()->init("gpu"); - InitGoogleTest(&argc, argv); - - const char* keys ="{ nvtest_output_level | nvtest_output_level | compact | NVidia test verbosity level }"; + try + { + CommandLineParser parser(argc, (const char**)argv, + "{ print_info_only | print_info_only | false | Print information about system and exit }" + "{ device | device | -1 | Device on which tests will be executed (-1 means all devices) }" + "{ nvtest_output_level | nvtest_output_level | compact | NVidia test verbosity level }"); + + printInfo(); + + if (parser.get("print_info_only")) + return 0; + + int device = parser.get("device"); + if (device < 0) + { + DeviceManager::instance().loadAll(); + std::cout << "Run tests on all supported devices\n" << std::endl; + } + else + { + DeviceManager::instance().load(device); + std::cout << "Run tests on device " << device << '\n' << std::endl; + } - CommandLineParser parser(argc, (const char**)argv, keys); + string outputLevel = parser.get("nvtest_output_level"); - string outputLevel = parser.get("nvtest_output_level", "none"); + if (outputLevel == "none") + nvidiaTestOutputLevel = OutputLevelNone; + else if (outputLevel == "compact") + nvidiaTestOutputLevel = OutputLevelCompact; + else if (outputLevel == "full") + nvidiaTestOutputLevel = OutputLevelFull; - if (outputLevel == "none") - nvidiaTestOutputLevel = OutputLevelNone; - else if (outputLevel == "compact") - nvidiaTestOutputLevel = OutputLevelCompact; - else if (outputLevel == "full") - nvidiaTestOutputLevel = OutputLevelFull; + TS::ptr()->init("gpu"); + InitGoogleTest(&argc, argv); - print_info(); + return RUN_ALL_TESTS(); + } + catch (const exception& e) + { + cerr << e.what() << endl; + return -1; + } + catch (...) + { + cerr << "Unknown error" << endl; + return -1; + } - return RUN_ALL_TESTS(); + return 0; } #else // HAVE_CUDA diff --git a/modules/gpu/test/precomp.hpp b/modules/gpu/test/precomp.hpp index afc3be8559..753367cce8 100644 --- a/modules/gpu/test/precomp.hpp +++ b/modules/gpu/test/precomp.hpp @@ -56,6 +56,7 @@ #include #include #include +#include #include "cvconfig.h" #include "opencv2/core/core.hpp" diff --git a/modules/gpu/test/utility.cpp b/modules/gpu/test/utility.cpp index bc73d30038..148c9d202b 100644 --- a/modules/gpu/test/utility.cpp +++ b/modules/gpu/test/utility.cpp @@ -46,6 +46,7 @@ using namespace cv; using namespace cv::gpu; using namespace cvtest; using namespace testing; +using namespace testing::internal; ////////////////////////////////////////////////////////////////////// // random generators @@ -108,12 +109,12 @@ GpuMat loadMat(const Mat& m, bool useRoi) ////////////////////////////////////////////////////////////////////// // Image load -Mat readImage(const string& fileName, int flags) +Mat readImage(const std::string& fileName, int flags) { - return imread(string(cvtest::TS::ptr()->get_data_path()) + fileName, flags); + return imread(TS::ptr()->get_data_path() + fileName, flags); } -Mat readImageType(const string& fname, int type) +Mat readImageType(const std::string& fname, int type) { Mat src = readImage(fname, CV_MAT_CN(type) == 1 ? IMREAD_GRAYSCALE : IMREAD_COLOR); if (CV_MAT_CN(type) == 4) @@ -134,50 +135,150 @@ bool supportFeature(const DeviceInfo& info, FeatureSet feature) return TargetArchs::builtWith(feature) && info.supports(feature); } -const vector& devices() +DeviceManager& DeviceManager::instance() { - static vector devs; - static bool first = true; + static DeviceManager obj; + return obj; +} - if (first) - { - int deviceCount = getCudaEnabledDeviceCount(); +void DeviceManager::load(int i) +{ + devices_.clear(); + devices_.reserve(1); - devs.reserve(deviceCount); + ostringstream msg; - for (int i = 0; i < deviceCount; ++i) - { - DeviceInfo info(i); - if (info.isCompatible()) - devs.push_back(info); - } + if (i < 0 || i >= getCudaEnabledDeviceCount()) + { + msg << "Incorrect device number - " << i; + throw runtime_error(msg.str()); + } + + DeviceInfo info(i); - first = false; + if (!info.isCompatible()) + { + msg << "Device " << i << " [" << info.name() << "] is NOT compatible with current GPU module build"; + throw runtime_error(msg.str()); } - return devs; + devices_.push_back(info); } -vector devices(FeatureSet feature) +void DeviceManager::loadAll() { - const vector& d = devices(); + int deviceCount = getCudaEnabledDeviceCount(); - vector devs_filtered; + devices_.clear(); + devices_.reserve(deviceCount); - if (TargetArchs::builtWith(feature)) + for (int i = 0; i < deviceCount; ++i) { - devs_filtered.reserve(d.size()); - - for (size_t i = 0, size = d.size(); i < size; ++i) + DeviceInfo info(i); + if (info.isCompatible()) { - const DeviceInfo& info = d[i]; - - if (info.supports(feature)) - devs_filtered.push_back(info); + devices_.push_back(info); } } +} - return devs_filtered; +class DevicesGenerator : public ParamGeneratorInterface +{ +public: + ~DevicesGenerator(); + + ParamIteratorInterface* Begin() const; + ParamIteratorInterface* End() const; + +private: + class Iterator : public ParamIteratorInterface + { + public: + Iterator(const ParamGeneratorInterface* base, vector::const_iterator iterator); + + virtual ~Iterator(); + + virtual const ParamGeneratorInterface* BaseGenerator() const; + + virtual void Advance(); + + virtual ParamIteratorInterface* Clone() const; + + virtual const DeviceInfo* Current() const; + + virtual bool Equals(const ParamIteratorInterface& other) const; + + private: + Iterator(const Iterator& other); + + const ParamGeneratorInterface* const base_; + vector::const_iterator iterator_; + + mutable DeviceInfo value_; + }; +}; + +DevicesGenerator::~DevicesGenerator() +{ +} + +ParamIteratorInterface* DevicesGenerator::Begin() const +{ + return new Iterator(this, DeviceManager::instance().values().begin()); +} + +ParamIteratorInterface* DevicesGenerator::End() const +{ + return new Iterator(this, DeviceManager::instance().values().end()); +} + +DevicesGenerator::Iterator::Iterator(const ParamGeneratorInterface* base, vector::const_iterator iterator) + : base_(base), iterator_(iterator) +{ +} + +DevicesGenerator::Iterator::~Iterator() +{ +} + +const ParamGeneratorInterface* DevicesGenerator::Iterator::BaseGenerator() const +{ + return base_; +} + +void DevicesGenerator::Iterator::Advance() +{ + ++iterator_; +} + +ParamIteratorInterface* DevicesGenerator::Iterator::Clone() const +{ + return new Iterator(*this); +} + +const DeviceInfo* DevicesGenerator::Iterator::Current() const +{ + value_ = *iterator_; + return &value_; +} + +bool DevicesGenerator::Iterator::Equals(const ParamIteratorInterface& other) const +{ + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << endl; + + return iterator_ == CheckedDowncastToActualType(&other)->iterator_; +} + +DevicesGenerator::Iterator::Iterator(const Iterator& other) : + ParamIteratorInterface(), base_(other.base_), iterator_(other.iterator_) +{ +} + +ParamGenerator DevicesGenerator_() +{ + return ParamGenerator(new DevicesGenerator); } ////////////////////////////////////////////////////////////////////// @@ -250,7 +351,7 @@ void minMaxLocGold(const Mat& src, double* minVal_, double* maxVal_, Point* minL namespace { - template string printMatValImpl(const Mat& m, Point p) + template std::string printMatValImpl(const Mat& m, Point p) { const int cn = m.channels(); @@ -269,9 +370,9 @@ namespace return ostr.str(); } - string printMatVal(const Mat& m, Point p) + std::string printMatVal(const Mat& m, Point p) { - typedef string (*func_t)(const Mat& m, Point p); + typedef std::string (*func_t)(const Mat& m, Point p); static const func_t funcs[] = { diff --git a/modules/gpu/test/utility.hpp b/modules/gpu/test/utility.hpp index 3ad02decbb..b36f177f6e 100644 --- a/modules/gpu/test/utility.hpp +++ b/modules/gpu/test/utility.hpp @@ -80,14 +80,23 @@ cv::Mat readImageType(const std::string& fname, int type); //! return true if device supports specified feature and gpu module was built with support the feature. bool supportFeature(const cv::gpu::DeviceInfo& info, cv::gpu::FeatureSet feature); -//! return all devices compatible with current gpu module build. -const std::vector& devices(); +class DeviceManager +{ +public: + static DeviceManager& instance(); + + void load(int i); + void loadAll(); + + const std::vector& values() const { return devices_; } + +private: + std::vector devices_; +}; -//! return all devices compatible with current gpu module build which support specified feature. -std::vector devices(cv::gpu::FeatureSet feature); +testing::internal::ParamGenerator DevicesGenerator_(); -#define ALL_DEVICES testing::ValuesIn(devices()) -#define DEVICES(feature) testing::ValuesIn(devices(feature)) +#define ALL_DEVICES DevicesGenerator_() ////////////////////////////////////////////////////////////////////// // Additional assertion From 6e4eb722a6e426960f2e515cf35b28482fc4a8bc Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 15:14:14 +0400 Subject: [PATCH 02/58] updated gpu performance tests now it executes only on one device added posibility to specify device on which tests will be executed --- modules/gpu/perf/perf_calib3d.cpp | 250 ++-- modules/gpu/perf/perf_core.cpp | 1818 ++++++++++---------------- modules/gpu/perf/perf_features2d.cpp | 196 ++- modules/gpu/perf/perf_filters.cpp | 281 ++-- modules/gpu/perf/perf_imgproc.cpp | 1350 +++++++++---------- modules/gpu/perf/perf_labeling.cpp | 18 +- modules/gpu/perf/perf_main.cpp | 87 ++ modules/gpu/perf/perf_matop.cpp | 134 +- modules/gpu/perf/perf_objdetect.cpp | 83 +- modules/gpu/perf/perf_precomp.hpp | 8 + modules/gpu/perf/perf_utility.cpp | 41 +- modules/gpu/perf/perf_utility.hpp | 70 +- modules/gpu/perf/perf_video.cpp | 472 +++---- 13 files changed, 2017 insertions(+), 2791 deletions(-) diff --git a/modules/gpu/perf/perf_calib3d.cpp b/modules/gpu/perf/perf_calib3d.cpp index 7a9c6c3950..343a4e9fad 100644 --- a/modules/gpu/perf/perf_calib3d.cpp +++ b/modules/gpu/perf/perf_calib3d.cpp @@ -1,207 +1,180 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////////////////////// // StereoBM -GPU_PERF_TEST_1(StereoBM, cv::gpu::DeviceInfo) -{ - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); +typedef pair pair_string; +DEF_PARAM_TEST_1(ImagePair, pair_string); - cv::Mat img_l_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_l_host.empty()); +PERF_TEST_P(ImagePair, Calib3D_StereoBM, Values(make_pair("gpu/perf/aloe.jpg", "gpu/perf/aloeR.jpg"))) +{ + declare.time(5.0); - cv::Mat img_r_host = readImage("gpu/perf/aloeR.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_r_host.empty()); + cv::Mat imgLeft = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(imgLeft.empty()); - cv::gpu::StereoBM_GPU bm(0, 256); - cv::gpu::GpuMat img_l(img_l_host); - cv::gpu::GpuMat img_r(img_r_host); - cv::gpu::GpuMat dst; + cv::Mat imgRight = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(imgRight.empty()); - bm(img_l, img_r, dst); + cv::gpu::StereoBM_GPU d_bm(0, 256); + cv::gpu::GpuMat d_imgLeft(imgLeft); + cv::gpu::GpuMat d_imgRight(imgRight); + cv::gpu::GpuMat d_dst; - declare.time(5.0); + d_bm(d_imgLeft, d_imgRight, d_dst); TEST_CYCLE() { - bm(img_l, img_r, dst); + d_bm(d_imgLeft, d_imgRight, d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, StereoBM, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // StereoBeliefPropagation -GPU_PERF_TEST_1(StereoBeliefPropagation, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Calib3D_StereoBeliefPropagation, Values(make_pair("gpu/stereobp/aloe-L.png", "gpu/stereobp/aloe-R.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Mat img_l_host = readImage("gpu/stereobp/aloe-L.png"); - ASSERT_FALSE(img_l_host.empty()); + declare.time(10.0); - cv::Mat img_r_host = readImage("gpu/stereobp/aloe-R.png"); - ASSERT_FALSE(img_r_host.empty()); + cv::Mat imgLeft = readImage(GetParam().first); + ASSERT_FALSE(imgLeft.empty()); - cv::gpu::StereoBeliefPropagation bp(64); - cv::gpu::GpuMat img_l(img_l_host); - cv::gpu::GpuMat img_r(img_r_host); - cv::gpu::GpuMat dst; + cv::Mat imgRight = readImage(GetParam().second); + ASSERT_FALSE(imgRight.empty()); - bp(img_l, img_r, dst); + cv::gpu::StereoBeliefPropagation d_bp(64); + cv::gpu::GpuMat d_imgLeft(imgLeft); + cv::gpu::GpuMat d_imgRight(imgRight); + cv::gpu::GpuMat d_dst; - declare.time(10.0); + d_bp(d_imgLeft, d_imgRight, d_dst); TEST_CYCLE() { - bp(img_l, img_r, dst); + d_bp(d_imgLeft, d_imgRight, d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, StereoBeliefPropagation, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // StereoConstantSpaceBP -GPU_PERF_TEST_1(StereoConstantSpaceBP, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Calib3D_StereoConstantSpaceBP, Values(make_pair("gpu/stereobm/aloe-L.png", "gpu/stereobm/aloe-R.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Mat img_l_host = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_l_host.empty()); + declare.time(10.0); - cv::Mat img_r_host = readImage("gpu/stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_r_host.empty()); + cv::Mat imgLeft = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(imgLeft.empty()); - cv::gpu::StereoConstantSpaceBP csbp(128); - cv::gpu::GpuMat img_l(img_l_host); - cv::gpu::GpuMat img_r(img_r_host); - cv::gpu::GpuMat dst; + cv::Mat imgRight = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(imgRight.empty()); - csbp(img_l, img_r, dst); + cv::gpu::StereoConstantSpaceBP d_csbp(128); + cv::gpu::GpuMat d_imgLeft(imgLeft); + cv::gpu::GpuMat d_imgRight(imgRight); + cv::gpu::GpuMat d_dst; - declare.time(10.0); + d_csbp(d_imgLeft, d_imgRight, d_dst); TEST_CYCLE() { - csbp(img_l, img_r, dst); + d_csbp(d_imgLeft, d_imgRight, d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, StereoConstantSpaceBP, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // DisparityBilateralFilter -GPU_PERF_TEST_1(DisparityBilateralFilter, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Calib3D_DisparityBilateralFilter, Values(make_pair("gpu/stereobm/aloe-L.png", "gpu/stereobm/aloe-disp.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::Mat img_host = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); + cv::Mat disp = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(disp.empty()); - cv::Mat disp_host = readImage("gpu/stereobm/aloe-disp.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(disp_host.empty()); + cv::gpu::DisparityBilateralFilter d_filter(128); + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_disp(disp); + cv::gpu::GpuMat d_dst; - cv::gpu::DisparityBilateralFilter f(128); - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat disp(disp_host); - cv::gpu::GpuMat dst; - - f(disp, img, dst); + d_filter(d_disp, d_img, d_dst); TEST_CYCLE() { - f(disp, img, dst); + d_filter(d_disp, d_img, d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, DisparityBilateralFilter, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // TransformPoints -IMPLEMENT_PARAM_CLASS(Count, int) +DEF_PARAM_TEST_1(Count, int); -GPU_PERF_TEST(TransformPoints, cv::gpu::DeviceInfo, Count) +PERF_TEST_P(Count, Calib3D_TransformPoints, Values(5000, 10000, 20000)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + int count = GetParam(); - int count = GET_PARAM(1); + cv::Mat src(1, count, CV_32FC3); + fillRandom(src, -100, 100); - cv::Mat src_host(1, count, CV_32FC3); - fill(src_host, -100, 100); - - cv::gpu::GpuMat src(src_host); cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::gpu::GpuMat dst; - cv::gpu::transformPoints(src, rvec, tvec, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); TEST_CYCLE() { - cv::gpu::transformPoints(src, rvec, tvec, dst); + cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, TransformPoints, testing::Combine( - ALL_DEVICES, - testing::Values(5000, 10000, 20000))); - ////////////////////////////////////////////////////////////////////// // ProjectPoints -GPU_PERF_TEST(ProjectPoints, cv::gpu::DeviceInfo, Count) +PERF_TEST_P(Count, Calib3D_ProjectPoints, Values(5000, 10000, 20000)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - int count = GET_PARAM(1); + int count = GetParam(); - cv::Mat src_host(1, count, CV_32FC3); - fill(src_host, -100, 100); + cv::Mat src(1, count, CV_32FC3); + fillRandom(src, -100, 100); - cv::gpu::GpuMat src(src_host); cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); cv::Mat camera_mat = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat dst; - cv::gpu::projectPoints(src, rvec, tvec, camera_mat, cv::Mat(), dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); TEST_CYCLE() { - cv::gpu::projectPoints(src, rvec, tvec, camera_mat, cv::Mat(), dst); + cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); } } -INSTANTIATE_TEST_CASE_P(Calib3D, ProjectPoints, testing::Combine( - ALL_DEVICES, - testing::Values(5000, 10000, 20000))); - ////////////////////////////////////////////////////////////////////// // SolvePnPRansac -GPU_PERF_TEST(SolvePnPRansac, cv::gpu::DeviceInfo, Count) +PERF_TEST_P(Count, Calib3D_SolvePnPRansac, Values(5000, 10000, 20000)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(3.0); - int count = GET_PARAM(1); + int count = GetParam(); cv::Mat object(1, count, CV_32FC3); - fill(object, -100, 100); + fillRandom(object, -100, 100); cv::Mat camera_mat(3, 3, CV_32FC1); - fill(camera_mat, 0.5, 1); + fillRandom(camera_mat, 0.5, 1); camera_mat.at(0, 1) = 0.f; camera_mat.at(1, 0) = 0.f; camera_mat.at(2, 0) = 0.f; @@ -211,9 +184,9 @@ GPU_PERF_TEST(SolvePnPRansac, cv::gpu::DeviceInfo, Count) std::vector image_vec; cv::Mat rvec_gold(1, 3, CV_32FC1); - fill(rvec_gold, 0, 1); + fillRandom(rvec_gold, 0, 1); cv::Mat tvec_gold(1, 3, CV_32FC1); - fill(tvec_gold, 0, 1); + fillRandom(tvec_gold, 0, 1); cv::projectPoints(object, rvec_gold, tvec_gold, camera_mat, dist_coef, image_vec); cv::Mat image(1, count, CV_32FC2, &image_vec[0]); @@ -223,80 +196,57 @@ GPU_PERF_TEST(SolvePnPRansac, cv::gpu::DeviceInfo, Count) cv::gpu::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); - declare.time(3.0); - TEST_CYCLE() { cv::gpu::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); } } -INSTANTIATE_TEST_CASE_P(Calib3D, SolvePnPRansac, testing::Combine( - ALL_DEVICES, - testing::Values(5000, 10000, 20000))); - ////////////////////////////////////////////////////////////////////// // ReprojectImageTo3D -GPU_PERF_TEST(ReprojectImageTo3D, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Calib3D_ReprojectImageTo3D, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src_host(size, depth); - fill(src_host, 5.0, 30.0); + cv::Mat src(size, depth); + fillRandom(src, 5.0, 30.0); cv::Mat Q(4, 4, CV_32FC1); - fill(Q, 0.1, 1.0); + fillRandom(Q, 0.1, 1.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::reprojectImageTo3D(src, dst, Q); + cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); TEST_CYCLE() { - cv::gpu::reprojectImageTo3D(src, dst, Q); + cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); } } -INSTANTIATE_TEST_CASE_P(Calib3D, ReprojectImageTo3D, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S))); - ////////////////////////////////////////////////////////////////////// // DrawColorDisp -GPU_PERF_TEST(DrawColorDisp, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Calib3D_DrawColorDisp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Mat src(size, type); + fillRandom(src, 0, 255); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::drawColorDisp(src, dst, 255); + cv::gpu::drawColorDisp(d_src, d_dst, 255); TEST_CYCLE() { - cv::gpu::drawColorDisp(src, dst, 255); + cv::gpu::drawColorDisp(d_src, d_dst, 255); } } -INSTANTIATE_TEST_CASE_P(Calib3D, DrawColorDisp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16S)))); - -#endif - +} // namespace diff --git a/modules/gpu/perf/perf_core.cpp b/modules/gpu/perf/perf_core.cpp index f413432041..b56713a70e 100644 --- a/modules/gpu/perf/perf_core.cpp +++ b/modules/gpu/perf/perf_core.cpp @@ -1,1883 +1,1513 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { + +#define ARITHM_MAT_DEPTH Values(CV_8U, CV_16U, CV_32F, CV_64F) ////////////////////////////////////////////////////////////////////// // Merge -GPU_PERF_TEST(Merge, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_Merge, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, Values(2, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - std::vector src(channels); + std::vector d_src(channels); for (int i = 0; i < channels; ++i) - src[i] = cv::gpu::GpuMat(size, depth, cv::Scalar::all(i)); + d_src[i] = cv::gpu::GpuMat(size, depth, cv::Scalar::all(i)); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_dst; - cv::gpu::merge(src, dst); + cv::gpu::merge(d_src, d_dst); TEST_CYCLE() { - cv::gpu::merge(src, dst); + cv::gpu::merge(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Merge, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(2, 3, 4))); - ////////////////////////////////////////////////////////////////////// // Split -GPU_PERF_TEST(Split, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_Split, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, Values(2, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::gpu::GpuMat src(size, CV_MAKE_TYPE(depth, channels), cv::Scalar(1, 2, 3, 4)); + cv::gpu::GpuMat d_src(size, CV_MAKE_TYPE(depth, channels), cv::Scalar(1, 2, 3, 4)); - std::vector dst; + std::vector d_dst; - cv::gpu::split(src, dst); + cv::gpu::split(d_src, d_dst); TEST_CYCLE() { - cv::gpu::split(src, dst); + cv::gpu::split(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Split, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(2, 3, 4))); - ////////////////////////////////////////////////////////////////////// -// Add_Mat +// AddMat -GPU_PERF_TEST(Add_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_AddMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0.0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0.0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::add(src1, src2, dst); + cv::gpu::add(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::add(src1, src2, dst); + cv::gpu::add(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Add_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Add_Scalar +// AddScalar -GPU_PERF_TEST(Add_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_AddScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); - - cv::gpu::GpuMat src(src_host); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat dst; - cv::gpu::add(src, s, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::add(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::add(src, s, dst); + cv::gpu::add(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Add_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Subtract_Mat +// SubtractMat -GPU_PERF_TEST(Subtract_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_SubtractMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0.0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0.0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::subtract(src1, src2, dst); + cv::gpu::subtract(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::subtract(src1, src2, dst); + cv::gpu::subtract(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Subtract_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Subtract_Scalar +// SubtractScalar -GPU_PERF_TEST(Subtract_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_SubtractScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); - - cv::gpu::GpuMat src(src_host); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat dst; - cv::gpu::subtract(src, s, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::subtract(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::subtract(src, s, dst); + cv::gpu::subtract(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Subtract_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Multiply_Mat +// MultiplyMat -GPU_PERF_TEST(Multiply_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MultiplyMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0.0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0.0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::multiply(src1, src2, dst); + cv::gpu::multiply(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::multiply(src1, src2, dst); + cv::gpu::multiply(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Multiply_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Multiply_Scalar +// MultiplyScalar -GPU_PERF_TEST(Multiply_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MultiplyScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat dst; - cv::gpu::multiply(src, s, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::multiply(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::multiply(src, s, dst); + cv::gpu::multiply(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Multiply_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Divide_Mat +// DivideMat -GPU_PERF_TEST(Divide_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_DivideMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0.0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::Mat src2_host(size, depth); - fill(src2_host, 0.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::divide(src1, src2, dst); + cv::gpu::divide(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::divide(src1, src2, dst); + cv::gpu::divide(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Divide_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Divide_Scalar +// DivideScalar -GPU_PERF_TEST(Divide_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_DivideScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat dst; - cv::gpu::divide(src, s, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::divide(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::divide(src, s, dst); + cv::gpu::divide(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Divide_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// Divide_Scalar_Inv +// DivideScalarInv -GPU_PERF_TEST(Divide_Scalar_Inv, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_DivideScalarInv, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + double s = 100.0; - cv::gpu::GpuMat src(src_host); - double scale = 100.0; - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::divide(scale, src, dst); + cv::gpu::divide(s, d_src, d_dst); TEST_CYCLE() { - cv::gpu::divide(scale, src, dst); + cv::gpu::divide(s, d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Divide_Scalar_Inv, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// AbsDiff_Mat +// AbsDiffMat -GPU_PERF_TEST(AbsDiff_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_AbsDiffMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0.0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::Mat src2_host(size, depth); - fill(src2_host, 0.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::absdiff(src1, src2, dst); + cv::gpu::absdiff(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::absdiff(src1, src2, dst); + cv::gpu::absdiff(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, AbsDiff_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// -// AbsDiff_Scalar +// AbsDiffScalar -GPU_PERF_TEST(AbsDiff_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_AbsDiffScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); - - cv::gpu::GpuMat src(src_host); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat dst; - cv::gpu::absdiff(src, s, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::absdiff(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::absdiff(src, s, dst); + cv::gpu::absdiff(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, AbsDiff_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// // Abs -GPU_PERF_TEST(Abs, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_Abs, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_16S, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::abs(src, dst); + cv::gpu::abs(d_src, d_dst); TEST_CYCLE() { - cv::gpu::abs(src, dst); + cv::gpu::abs(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Abs, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// // Sqr -GPU_PERF_TEST(Sqr, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_Sqr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::sqr(src, dst); + cv::gpu::sqr(d_src, d_dst); TEST_CYCLE() { - cv::gpu::sqr(src, dst); + cv::gpu::sqr(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Sqr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// // Sqrt -GPU_PERF_TEST(Sqrt, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_Sqrt, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::sqrt(src, dst); + cv::gpu::sqrt(d_src, d_dst); TEST_CYCLE() { - cv::gpu::sqrt(src, dst); + cv::gpu::sqrt(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Sqrt, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// // Log -GPU_PERF_TEST(Log, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_Log, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src, 1.0, 255.0); - cv::Mat src_host(size, depth); - fill(src_host, 1.0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::log(src, dst); + cv::gpu::log(d_src, d_dst); TEST_CYCLE() { - cv::gpu::log(src, dst); + cv::gpu::log(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Log, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// // Exp -GPU_PERF_TEST(Exp, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_Exp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, depth); - fill(src_host, 1.0, 10.0); + cv::Mat src(size, depth); + fillRandom(src, 1.0, 10.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::exp(src, dst); + cv::gpu::exp(d_src, d_dst); TEST_CYCLE() { - cv::gpu::exp(src, dst); + cv::gpu::exp(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Exp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// // Pow -GPU_PERF_TEST(Pow, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Power, cv::Size, MatDepth, double); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); +PERF_TEST_P(Sz_Depth_Power, Core_Pow, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F), Values(0.3, 2.0, 2.4))) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + double power = GET_PARAM(2); - cv::Mat src_host(size, depth); - fill(src_host, 1.0, 10.0); + cv::Mat src(size, depth); + fillRandom(src, 1.0, 10.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pow(src, 2.3, dst); + cv::gpu::pow(d_src, power, d_dst); TEST_CYCLE() { - cv::gpu::pow(src, 2.3, dst); + cv::gpu::pow(d_src, power, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Pow, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - ////////////////////////////////////////////////////////////////////// -// Compare_Mat +// CompareMat CV_ENUM(CmpCode, cv::CMP_EQ, cv::CMP_GT, cv::CMP_GE, cv::CMP_LT, cv::CMP_LE, cv::CMP_NE) -#define ALL_CMP_CODES testing::Values(CmpCode(cv::CMP_EQ), CmpCode(cv::CMP_NE), CmpCode(cv::CMP_GT), CmpCode(cv::CMP_GE), CmpCode(cv::CMP_LT), CmpCode(cv::CMP_LE)) +#define ALL_CMP_CODES ValuesIn(CmpCode::all()) -GPU_PERF_TEST(Compare_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth, CmpCode) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Code, cv::Size, MatDepth, CmpCode); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int cmp_code = GET_PARAM(3); +PERF_TEST_P(Sz_Depth_Code, Core_CompareMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, ALL_CMP_CODES)) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int cmp_code = GET_PARAM(2); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::compare(src1, src2, dst, cmp_code); + cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); TEST_CYCLE() { - cv::gpu::compare(src1, src2, dst, cmp_code); + cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); } } -INSTANTIATE_TEST_CASE_P(Core, Compare_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - ALL_CMP_CODES)); - ////////////////////////////////////////////////////////////////////// -// Compare_Scalar +// CompareScalar -GPU_PERF_TEST(Compare_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, CmpCode) +PERF_TEST_P(Sz_Depth_Code, Core_CompareScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, ALL_CMP_CODES)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int cmp_code = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int cmp_code = GET_PARAM(3); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0, 100.0); + cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat src(src_host); - cv::Scalar s = cv::Scalar::all(50); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::compare(src, s, dst, cmp_code); + cv::gpu::compare(d_src, s, d_dst, cmp_code); TEST_CYCLE() { - cv::gpu::compare(src, s, dst, cmp_code); + cv::gpu::compare(d_src, s, d_dst, cmp_code); } } -INSTANTIATE_TEST_CASE_P(Core, Compare_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - ALL_CMP_CODES)); - ////////////////////////////////////////////////////////////////////// -// Bitwise_Not +// BitwiseNot -GPU_PERF_TEST(Bitwise_Not, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_BitwiseNot, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::bitwise_not(src, dst); + cv::gpu::bitwise_not(d_src, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_not(src, dst); + cv::gpu::bitwise_not(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Not, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_And_Mat +// BitwiseAndMat -GPU_PERF_TEST(Bitwise_And_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_BitwiseAndMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_and(src1, src2, dst); + cv::gpu::bitwise_and(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_and(src1, src2, dst); + cv::gpu::bitwise_and(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_And_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_And_Scalar +// BitwiseAndScalar -GPU_PERF_TEST(Bitwise_And_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseAndScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 100.0); + cv::Mat src(size, type); + fillRandom(src); + + cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat src(src_host); - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_and(src, s, dst); + cv::gpu::bitwise_and(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_and(src, s, dst); + cv::gpu::bitwise_and(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_And_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_Or_Mat +// BitwiseOrMat -GPU_PERF_TEST(Bitwise_Or_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_BitwiseOrMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_or(src1, src2, dst); + cv::gpu::bitwise_or(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_or(src1, src2, dst); + cv::gpu::bitwise_or(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Or_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_Or_Scalar +// BitwiseOrScalar -GPU_PERF_TEST(Bitwise_Or_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseOrScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 100.0); + cv::Mat src(size, type); + fillRandom(src); + + cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat src(src_host); - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_or(src, s, dst); + cv::gpu::bitwise_or(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_or(src, s, dst); + cv::gpu::bitwise_or(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Or_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_Xor_Mat +// BitwiseXorMat -GPU_PERF_TEST(Bitwise_Xor_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_BitwiseXorMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 100.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 100.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_xor(src1, src2, dst); + cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_xor(src1, src2, dst); + cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Xor_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - ////////////////////////////////////////////////////////////////////// -// Bitwise_Xor_Scalar +// BitwiseXorScalar -GPU_PERF_TEST(Bitwise_Xor_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseXorScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 100.0); + cv::Mat src(size, type); + fillRandom(src); + + cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat src(src_host); - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_xor(src, s, dst); + cv::gpu::bitwise_xor(d_src, s, d_dst); TEST_CYCLE() { - cv::gpu::bitwise_xor(src, s, dst); + cv::gpu::bitwise_xor(d_src, s, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Xor_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - ////////////////////////////////////////////////////////////////////// // RShift -GPU_PERF_TEST(RShift, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_RShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar_ val = cv::Scalar_::all(4); - cv::gpu::GpuMat dst; - cv::gpu::rshift(src, val, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::rshift(d_src, val, d_dst); TEST_CYCLE() { - cv::gpu::rshift(src, val, dst); + cv::gpu::rshift(d_src, val, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, RShift, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - ////////////////////////////////////////////////////////////////////// // LShift -GPU_PERF_TEST(LShift, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) +PERF_TEST_P(Sz_Depth_Cn, Core_LShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar_ val = cv::Scalar_::all(4); - cv::gpu::GpuMat dst; - cv::gpu::lshift(src, val, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::lshift(d_src, val, d_dst); TEST_CYCLE() { - cv::gpu::lshift(src, val, dst); + cv::gpu::lshift(d_src, val, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, LShift, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - ////////////////////////////////////////////////////////////////////// -// Min_Mat +// MinMat -GPU_PERF_TEST(Min_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MinMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 255.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 255.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::min(src1, src2, dst); + cv::gpu::min(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::min(src1, src2, dst); + cv::gpu::min(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Min_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - ////////////////////////////////////////////////////////////////////// -// Min_Scalar +// MinScalar -GPU_PERF_TEST(Min_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MinScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0, 255.0); - - cv::gpu::GpuMat src(src_host); double val = 50.0; - cv::gpu::GpuMat dst; - cv::gpu::min(src, val, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::min(d_src, val, d_dst); TEST_CYCLE() { - cv::gpu::min(src, val, dst); + cv::gpu::min(d_src, val, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Min_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - ////////////////////////////////////////////////////////////////////// -// Max_Mat +// MaxMat -GPU_PERF_TEST(Max_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MaxMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src1_host(size, depth); - fill(src1_host, 0, 255.0); + cv::Mat src1(size, depth); + fillRandom(src1); - cv::Mat src2_host(size, depth); - fill(src2_host, 0, 255.0); + cv::Mat src2(size, depth); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::max(src1, src2, dst); + cv::gpu::max(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::max(src1, src2, dst); + cv::gpu::max(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Max_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - ////////////////////////////////////////////////////////////////////// -// Max_Scalar +// MaxScalar -GPU_PERF_TEST(Max_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MaxScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0, 255.0); - - cv::gpu::GpuMat src(src_host); double val = 50.0; - cv::gpu::GpuMat dst; - cv::gpu::max(src, val, dst); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::max(d_src, val, d_dst); TEST_CYCLE() { - cv::gpu::max(src, val, dst); + cv::gpu::max(d_src, val, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Max_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - ////////////////////////////////////////////////////////////////////// // AddWeighted -GPU_PERF_TEST(AddWeighted, cv::gpu::DeviceInfo, cv::Size, MatDepth, MatDepth, MatDepth) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_3Depth, cv::Size, MatDepth, MatDepth, MatDepth); - cv::Size size = GET_PARAM(1); - int depth1 = GET_PARAM(2); - int depth2 = GET_PARAM(3); - int dst_depth = GET_PARAM(4); +PERF_TEST_P(Sz_3Depth, Core_AddWeighted, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(CV_8U, CV_16U, CV_32F, CV_64F), + Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + cv::Size size = GET_PARAM(0); + int depth1 = GET_PARAM(1); + int depth2 = GET_PARAM(2); + int dst_depth = GET_PARAM(3); - cv::Mat src1_host(size, depth1); - fill(src1_host, 0, 100.0); + cv::Mat src1(size, depth1); + fillRandom(src1); - cv::Mat src2_host(size, depth2); - fill(src2_host, 0, 100.0); + cv::Mat src2(size, depth2); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); + cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); TEST_CYCLE() { - cv::gpu::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); + cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); } } -INSTANTIATE_TEST_CASE_P(Core, AddWeighted, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// // GEMM -#ifdef HAVE_CUBLAS CV_FLAGS(GemmFlags, 0, cv::GEMM_1_T, cv::GEMM_2_T, cv::GEMM_3_T) -#define ALL_GEMM_FLAGS testing::Values(GemmFlags(0), GemmFlags(cv::GEMM_1_T), GemmFlags(cv::GEMM_2_T), GemmFlags(cv::GEMM_3_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_3_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T | cv::GEMM_3_T)) +#define ALL_GEMM_FLAGS Values(0, CV_GEMM_A_T, CV_GEMM_B_T, CV_GEMM_C_T, CV_GEMM_A_T | CV_GEMM_B_T, CV_GEMM_A_T | CV_GEMM_C_T, CV_GEMM_A_T | CV_GEMM_B_T | CV_GEMM_C_T) -GPU_PERF_TEST(GEMM, cv::gpu::DeviceInfo, cv::Size, MatType, GemmFlags) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Type_Flags, cv::Size, MatType, GemmFlags); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int flags = GET_PARAM(3); +PERF_TEST_P(Sz_Type_Flags, Core_GEMM, Combine( + Values(cv::Size(512, 512), cv::Size(1024, 1024)), + Values(CV_32FC1, CV_32FC2, CV_64FC1, CV_64FC2), + ALL_GEMM_FLAGS)) +{ + declare.time(5.0); - cv::Mat src1_host(size, type); - fill(src1_host, 0.0, 10.0); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int flags = GET_PARAM(2); - cv::Mat src2_host(size, type); - fill(src2_host, 0.0, 10.0); + cv::Mat src1(size, type); + fillRandom(src1); - cv::Mat src3_host(size, type); - fill(src3_host, 0.0, 10.0); + cv::Mat src2(size, type); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat src3(src3_host); - cv::gpu::GpuMat dst; + cv::Mat src3(size, type); + fillRandom(src3); - cv::gpu::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_src3(src3); + cv::gpu::GpuMat d_dst; - declare.time(5.0); + cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); TEST_CYCLE() { - cv::gpu::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); + cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); } } -INSTANTIATE_TEST_CASE_P(Core, GEMM, testing::Combine( - ALL_DEVICES, - testing::Values(cv::Size(512, 512), cv::Size(1024, 1024)), - testing::Values(CV_32FC1, CV_32FC2, CV_64FC1, CV_64FC2), - ALL_GEMM_FLAGS)); - -#endif ////////////////////////////////////////////////////////////////////// // Transpose -GPU_PERF_TEST(Transpose, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Core_Transpose, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8UC1, CV_8UC4, CV_16UC2, CV_16SC2, CV_32SC1, CV_32SC2, CV_64FC1))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 100.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::transpose(src, dst); + cv::gpu::transpose(d_src, d_dst); TEST_CYCLE() { - cv::gpu::transpose(src, dst); + cv::gpu::transpose(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Transpose, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC4, CV_16UC2, CV_16SC2, CV_32SC1, CV_32SC2, CV_64FC1))); - ////////////////////////////////////////////////////////////////////// // Flip enum {FLIP_BOTH = 0, FLIP_X = 1, FLIP_Y = -1}; CV_ENUM(FlipCode, FLIP_BOTH, FLIP_X, FLIP_Y) -#define ALL_FLIP_CODES testing::Values(FlipCode(FLIP_BOTH), FlipCode(FLIP_X), FlipCode(FLIP_Y)) +#define ALL_FLIP_CODES ValuesIn(FlipCode::all()) -GPU_PERF_TEST(Flip, cv::gpu::DeviceInfo, cv::Size, MatType, FlipCode) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Cn_Code, cv::Size, MatDepth, int, FlipCode); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); +PERF_TEST_P(Sz_Depth_Cn_Code, Core_Flip, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + ALL_FLIP_CODES)) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int flipCode = GET_PARAM(3); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 100.0); + int type = CV_MAKE_TYPE(depth, channels); + + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::flip(src, dst, flipCode); + cv::gpu::flip(d_src, d_dst, flipCode); TEST_CYCLE() { - cv::gpu::flip(src, dst, flipCode); + cv::gpu::flip(d_src, d_dst, flipCode); } } -INSTANTIATE_TEST_CASE_P(Core, Flip, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4), - ALL_FLIP_CODES)); - ////////////////////////////////////////////////////////////////////// -// LUT_OneChannel +// LutOneChannel -GPU_PERF_TEST(LUT_OneChannel, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Core_LutOneChannel, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8UC1, CV_8UC3))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 100.0); + cv::Mat src(size, type); + fillRandom(src); cv::Mat lut(1, 256, CV_8UC1); - fill(lut, 0.0, 100.0); + fillRandom(lut); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::LUT(src, lut, dst); + cv::gpu::LUT(d_src, lut, d_dst); TEST_CYCLE() { - cv::gpu::LUT(src, lut, dst); + cv::gpu::LUT(d_src, lut, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, LUT_OneChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3))); - ////////////////////////////////////////////////////////////////////// -// LUT_MultiChannel +// LutMultiChannel -GPU_PERF_TEST(LUT_MultiChannel, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Core_LutMultiChannel, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8UC3))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 100.0); + cv::Mat lut(1, 256, CV_MAKE_TYPE(CV_8U, src.channels())); + fillRandom(lut); - cv::Mat lut(1, 256, CV_MAKE_TYPE(CV_8U, src_host.channels())); - fill(lut, 0.0, 100.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - - cv::gpu::LUT(src, lut, dst); + cv::gpu::LUT(d_src, lut, d_dst); TEST_CYCLE() { - cv::gpu::LUT(src, lut, dst); + cv::gpu::LUT(d_src, lut, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, LUT_MultiChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC3))); - ////////////////////////////////////////////////////////////////////// -// Magnitude_Complex +// MagnitudeComplex -GPU_PERF_TEST(Magnitude_Complex, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, Core_MagnitudeComplex, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_32FC2); - fill(src_host, -100.0, 100.0); + cv::Mat src(size, CV_32FC2); + fillRandom(src, -100.0, 100.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitude(src, dst); + cv::gpu::magnitude(d_src, d_dst); TEST_CYCLE() { - cv::gpu::magnitude(src, dst); + cv::gpu::magnitude(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Magnitude_Complex, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// -// Magnitude_Sqr_Complex +// MagnitudeSqrComplex -GPU_PERF_TEST(Magnitude_Sqr_Complex, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, Core_MagnitudeSqrComplex, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_32FC2); - fill(src_host, -100.0, 100.0); + cv::Mat src(size, CV_32FC2); + fillRandom(src, -100.0, 100.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitudeSqr(src, dst); + cv::gpu::magnitudeSqr(d_src, d_dst); TEST_CYCLE() { - cv::gpu::magnitudeSqr(src, dst); + cv::gpu::magnitudeSqr(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Magnitude_Sqr_Complex, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Magnitude -GPU_PERF_TEST(Magnitude, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, Core_Magnitude, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GetParam(); - cv::Size size = GET_PARAM(1); + cv::Mat src1(size, CV_32FC1); + fillRandom(src1, -100.0, 100.0); - cv::Mat src1_host(size, CV_32FC1); - fill(src1_host, -100.0, 100.0); + cv::Mat src2(size, CV_32FC1); + fillRandom(src2, -100.0, 100.0); - cv::Mat src2_host(size, CV_32FC1); - fill(src2_host, -100.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::magnitude(src1, src2, dst); + cv::gpu::magnitude(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::magnitude(src1, src2, dst); + cv::gpu::magnitude(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Magnitude, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// -// Magnitude_Sqr +// MagnitudeSqr -GPU_PERF_TEST(Magnitude_Sqr, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, Core_MagnitudeSqr, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GetParam(); - cv::Size size = GET_PARAM(1); + cv::Mat src1(size, CV_32FC1); + fillRandom(src1, -100.0, 100.0); - cv::Mat src1_host(size, CV_32FC1); - fill(src1_host, -100.0, 100.0); + cv::Mat src2(size, CV_32FC1); + fillRandom(src2, -100.0, 100.0); - cv::Mat src2_host(size, CV_32FC1); - fill(src2_host, -100.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::magnitudeSqr(src1, src2, dst); + cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); TEST_CYCLE() { - cv::gpu::magnitudeSqr(src1, src2, dst); + cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); } } -INSTANTIATE_TEST_CASE_P(Core, Magnitude_Sqr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Phase -IMPLEMENT_PARAM_CLASS(AngleInDegrees, bool) +DEF_PARAM_TEST(Sz_AngleInDegrees, cv::Size, bool); -GPU_PERF_TEST(Phase, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) +PERF_TEST_P(Sz_AngleInDegrees, Core_Phase, Combine(GPU_TYPICAL_MAT_SIZES, Bool())) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + bool angleInDegrees = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); + cv::Mat src1(size, CV_32FC1); + fillRandom(src1, -100.0, 100.0); - cv::Mat src1_host(size, CV_32FC1); - fill(src1_host, -100.0, 100.0); + cv::Mat src2(size, CV_32FC1); + fillRandom(src2, -100.0, 100.0); - cv::Mat src2_host(size, CV_32FC1); - fill(src2_host, -100.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; - - cv::gpu::phase(src1, src2, dst, angleInDegrees); + cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); TEST_CYCLE() { - cv::gpu::phase(src1, src2, dst, angleInDegrees); + cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); } } -INSTANTIATE_TEST_CASE_P(Core, Phase, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - ////////////////////////////////////////////////////////////////////// // CartToPolar -GPU_PERF_TEST(CartToPolar, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) +PERF_TEST_P(Sz_AngleInDegrees, Core_CartToPolar, Combine(GPU_TYPICAL_MAT_SIZES, Bool())) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + bool angleInDegrees = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); + cv::Mat src1(size, CV_32FC1); + fillRandom(src1, -100.0, 100.0); - cv::Mat src1_host(size, CV_32FC1); - fill(src1_host, -100.0, 100.0); + cv::Mat src2(size, CV_32FC1); + fillRandom(src2, -100.0, 100.0); - cv::Mat src2_host(size, CV_32FC1); - fill(src2_host, -100.0, 100.0); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_magnitude; + cv::gpu::GpuMat d_angle; - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat magnitude; - cv::gpu::GpuMat angle; - - cv::gpu::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); + cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); TEST_CYCLE() { - cv::gpu::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); + cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); } } -INSTANTIATE_TEST_CASE_P(Core, CartToPolar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - ////////////////////////////////////////////////////////////////////// // PolarToCart -GPU_PERF_TEST(PolarToCart, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) +PERF_TEST_P(Sz_AngleInDegrees, Core_PolarToCart, Combine(GPU_TYPICAL_MAT_SIZES, Bool())) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + bool angleInDegrees = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); + cv::Mat magnitude(size, CV_32FC1); + fillRandom(magnitude, 0.0, 100.0); - cv::Mat magnitude_host(size, CV_32FC1); - fill(magnitude_host, 0.0, 100.0); + cv::Mat angle(size, CV_32FC1); + fillRandom(angle, 0.0, angleInDegrees ? 360.0 : 2 * CV_PI); - cv::Mat angle_host(size, CV_32FC1); - fill(angle_host, 0.0, angleInDegrees ? 360.0 : 2 * CV_PI); + cv::gpu::GpuMat d_magnitude(magnitude); + cv::gpu::GpuMat d_angle(angle); + cv::gpu::GpuMat d_x; + cv::gpu::GpuMat d_y; - cv::gpu::GpuMat magnitude(magnitude_host); - cv::gpu::GpuMat angle(angle_host); - cv::gpu::GpuMat x; - cv::gpu::GpuMat y; - - cv::gpu::polarToCart(magnitude, angle, x, y, angleInDegrees); + cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); TEST_CYCLE() { - cv::gpu::polarToCart(magnitude, angle, x, y, angleInDegrees); + cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); } } -INSTANTIATE_TEST_CASE_P(Core, PolarToCart, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - ////////////////////////////////////////////////////////////////////// // MeanStdDev -GPU_PERF_TEST(MeanStdDev, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, Core_MeanStdDev, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GetParam(); - cv::Size size = GET_PARAM(1); + cv::Mat src(size, CV_8UC1); + fillRandom(src); - cv::Mat src_host(size, CV_8UC1); - fill(src_host, 0.0, 255.0); - - cv::gpu::GpuMat src(src_host); cv::Scalar mean; cv::Scalar stddev; - cv::gpu::GpuMat buf; - cv::gpu::meanStdDev(src, mean, stddev, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); TEST_CYCLE() { - cv::gpu::meanStdDev(src, mean, stddev, buf); + cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, MeanStdDev, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Norm -GPU_PERF_TEST(Norm, cv::gpu::DeviceInfo, cv::Size, MatDepth, NormType) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Norm, cv::Size, MatDepth, NormType); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int normType = GET_PARAM(3); +PERF_TEST_P(Sz_Depth_Norm, Core_Norm, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32S, CV_32F), + Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int normType = GET_PARAM(2); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); double dst; - cv::gpu::GpuMat buf; - dst = cv::gpu::norm(src, normType, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + dst = cv::gpu::norm(d_src, normType, d_buf); TEST_CYCLE() { - dst = cv::gpu::norm(src, normType, buf); + dst = cv::gpu::norm(d_src, normType, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, Norm, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S, CV_32F), - testing::Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))); - ////////////////////////////////////////////////////////////////////// // NormDiff -GPU_PERF_TEST(NormDiff, cv::gpu::DeviceInfo, cv::Size, NormType) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Norm, cv::Size, NormType); - cv::Size size = GET_PARAM(1); - int normType = GET_PARAM(2); +PERF_TEST_P(Sz_Norm, Core_NormDiff, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))) +{ + cv::Size size = GET_PARAM(0); + int normType = GET_PARAM(1); - cv::Mat src1_host(size, CV_8UC1); - fill(src1_host, 0.0, 255.0); + cv::Mat src1(size, CV_8UC1); + fillRandom(src1); - cv::Mat src2_host(size, CV_8UC1); - fill(src2_host, 0.0, 255.0); + cv::Mat src2(size, CV_8UC1); + fillRandom(src2); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); double dst; - dst = cv::gpu::norm(src1, src2, normType); + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + + dst = cv::gpu::norm(d_src1, d_src2, normType); TEST_CYCLE() { - dst = cv::gpu::norm(src1, src2, normType); + dst = cv::gpu::norm(d_src1, d_src2, normType); } } -INSTANTIATE_TEST_CASE_P(Core, NormDiff, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))); - ////////////////////////////////////////////////////////////////////// // Sum -GPU_PERF_TEST(Sum, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, Core_Sum, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar dst; - cv::gpu::GpuMat buf; - dst = cv::gpu::sum(src, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + dst = cv::gpu::sum(d_src, d_buf); TEST_CYCLE() { - dst = cv::gpu::sum(src, buf); + dst = cv::gpu::sum(d_src, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, Sum, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4))); - ////////////////////////////////////////////////////////////////////// -// Sum_Abs +// SumAbs -GPU_PERF_TEST(Sum_Abs, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, Core_SumAbs, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar dst; - cv::gpu::GpuMat buf; - dst = cv::gpu::absSum(src, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + dst = cv::gpu::absSum(d_src, d_buf); TEST_CYCLE() { - dst = cv::gpu::absSum(src, buf); + dst = cv::gpu::absSum(d_src, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, Sum_Abs, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4))); - ////////////////////////////////////////////////////////////////////// -// Sum_Sqr +// SumSqr -GPU_PERF_TEST(Sum_Sqr, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, Core_SumSqr, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); cv::Scalar dst; - cv::gpu::GpuMat buf; - dst = cv::gpu::sqrSum(src, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + dst = cv::gpu::sqrSum(d_src, d_buf); TEST_CYCLE() { - dst = cv::gpu::sqrSum(src, buf); + dst = cv::gpu::sqrSum(d_src, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, Sum_Sqr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4))); - ////////////////////////////////////////////////////////////////////// // MinMax -GPU_PERF_TEST(MinMax, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MinMax, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); double minVal, maxVal; - cv::gpu::GpuMat buf; - cv::gpu::minMax(src, &minVal, &maxVal, cv::gpu::GpuMat(), buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); TEST_CYCLE() { - cv::gpu::minMax(src, &minVal, &maxVal, cv::gpu::GpuMat(), buf); + cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, MinMax, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// // MinMaxLoc -GPU_PERF_TEST(MinMaxLoc, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_MinMaxLoc, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 255.0); - - cv::gpu::GpuMat src(src_host); double minVal, maxVal; cv::Point minLoc, maxLoc; - cv::gpu::GpuMat valbuf, locbuf; - cv::gpu::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), valbuf, locbuf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_valbuf, d_locbuf; + + cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); TEST_CYCLE() { - cv::gpu::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), valbuf, locbuf); + cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); } } -INSTANTIATE_TEST_CASE_P(Core, MinMaxLoc, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// // CountNonZero -GPU_PERF_TEST(CountNonZero, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, Core_CountNonZero, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Mat src(size, depth); + fillRandom(src); - cv::Mat src_host(size, depth); - fill(src_host, 0.0, 1.5); - - cv::gpu::GpuMat src(src_host); int dst; - cv::gpu::GpuMat buf; - dst = cv::gpu::countNonZero(src, buf); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; + + dst = cv::gpu::countNonZero(d_src, d_buf); TEST_CYCLE() { - dst = cv::gpu::countNonZero(src, buf); + dst = cv::gpu::countNonZero(d_src, d_buf); } } -INSTANTIATE_TEST_CASE_P(Core, CountNonZero, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - ////////////////////////////////////////////////////////////////////// // Reduce CV_ENUM(ReduceCode, CV_REDUCE_SUM, CV_REDUCE_AVG, CV_REDUCE_MAX, CV_REDUCE_MIN) -#define ALL_REDUCE_CODES testing::Values(CV_REDUCE_SUM, CV_REDUCE_AVG, CV_REDUCE_MAX, CV_REDUCE_MIN) +#define ALL_REDUCE_CODES ValuesIn(ReduceCode::all()) enum {Rows = 0, Cols = 1}; CV_ENUM(ReduceDim, Rows, Cols) +#define ALL_REDUCE_DIMS ValuesIn(ReduceDim::all()) -GPU_PERF_TEST(Reduce, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels, ReduceCode, ReduceDim) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Cn_Code_Dim, cv::Size, MatDepth, int, ReduceCode, ReduceDim); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - int reduceOp = GET_PARAM(4); - int dim = GET_PARAM(5); +PERF_TEST_P(Sz_Depth_Cn_Code_Dim, Core_Reduce, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_16S, CV_32F), + Values(1, 2, 3, 4), + ALL_REDUCE_CODES, + ALL_REDUCE_DIMS)) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); + int reduceOp = GET_PARAM(3); + int dim = GET_PARAM(4); int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 10.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::reduce(src, dst, dim, reduceOp); + cv::gpu::reduce(d_src, d_dst, dim, reduceOp); TEST_CYCLE() { - cv::gpu::reduce(src, dst, dim, reduceOp); + cv::gpu::reduce(d_src, d_dst, dim, reduceOp); } } -INSTANTIATE_TEST_CASE_P(Core, Reduce, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_16S, CV_32F), - testing::Values(1, 2, 3, 4), - ALL_REDUCE_CODES, - testing::Values(ReduceDim(Rows), ReduceDim(Cols)))); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_features2d.cpp b/modules/gpu/perf/perf_features2d.cpp index 8a81860176..ef9612b549 100644 --- a/modules/gpu/perf/perf_features2d.cpp +++ b/modules/gpu/perf/perf_features2d.cpp @@ -1,209 +1,179 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////////////////////// // SURF -GPU_PERF_TEST_1(SURF, cv::gpu::DeviceInfo) -{ - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST_1(Image, string); - cv::Mat img_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); +PERF_TEST_P(Image, Features2D_SURF, Values("gpu/perf/aloe.jpg")) +{ + declare.time(2.0); - cv::gpu::SURF_GPU surf; + cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat keypoints, descriptors; + cv::gpu::SURF_GPU d_surf; - surf(img, cv::gpu::GpuMat(), keypoints, descriptors); + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints, d_descriptors; - declare.time(2.0); + d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); TEST_CYCLE() { - surf(img, cv::gpu::GpuMat(), keypoints, descriptors); + d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); } } -INSTANTIATE_TEST_CASE_P(Features2D, SURF, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // FAST -GPU_PERF_TEST_1(FAST, cv::gpu::DeviceInfo) +PERF_TEST_P(Image, Features2D_FAST, Values("gpu/perf/aloe.jpg")) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::Mat img_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); + cv::gpu::FAST_GPU d_fast(20); - cv::gpu::FAST_GPU fast(20); + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints; - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat keypoints; - - fast(img, cv::gpu::GpuMat(), keypoints); + d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); TEST_CYCLE() { - fast(img, cv::gpu::GpuMat(), keypoints); + d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); } } -INSTANTIATE_TEST_CASE_P(Features2D, FAST, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // ORB -GPU_PERF_TEST_1(ORB, cv::gpu::DeviceInfo) +PERF_TEST_P(Image, Features2D_ORB, Values("gpu/perf/aloe.jpg")) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::Mat img_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); + cv::gpu::ORB_GPU d_orb(4000); - cv::gpu::ORB_GPU orb(4000); + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints, d_descriptors; - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat keypoints, descriptors; + d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); TEST_CYCLE() { - orb(img, cv::gpu::GpuMat(), keypoints, descriptors); + d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); } } -INSTANTIATE_TEST_CASE_P(Features2D, ORB, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_match +// BFMatch -IMPLEMENT_PARAM_CLASS(DescriptorSize, int) +DEF_PARAM_TEST(DescSize_Norm, int, NormType); -GPU_PERF_TEST(BruteForceMatcher_match, cv::gpu::DeviceInfo, DescriptorSize, NormType) +PERF_TEST_P(DescSize_Norm, Features2D_BFMatch, Combine(Values(64, 128, 256), Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(3.0); - int desc_size = GET_PARAM(1); - int normType = GET_PARAM(2); + int desc_size = GET_PARAM(0); + int normType = GET_PARAM(1); int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - cv::Mat query_host(3000, desc_size, type); - fill(query_host, 0.0, 10.0); - - cv::Mat train_host(3000, desc_size, type); - fill(train_host, 0.0, 10.0); + cv::Mat query(3000, desc_size, type); + fillRandom(query); - cv::gpu::BFMatcher_GPU matcher(normType); + cv::Mat train(3000, desc_size, type); + fillRandom(train); - cv::gpu::GpuMat query(query_host); - cv::gpu::GpuMat train(train_host); - cv::gpu::GpuMat trainIdx, distance; + cv::gpu::BFMatcher_GPU d_matcher(normType); - matcher.matchSingle(query, train, trainIdx, distance); + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_distance; - declare.time(3.0); + d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); TEST_CYCLE() { - matcher.matchSingle(query, train, trainIdx, distance); + d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); } } -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_match, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - ////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_knnMatch +// BFKnnMatch -IMPLEMENT_PARAM_CLASS(K, int) +DEF_PARAM_TEST(DescSize_K_Norm, int, int, NormType); -GPU_PERF_TEST(BruteForceMatcher_knnMatch, cv::gpu::DeviceInfo, DescriptorSize, K, NormType) +PERF_TEST_P(DescSize_K_Norm, Features2D_BFKnnMatch, Combine( + Values(64, 128, 256), + Values(2, 3), + Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(3.0); - int desc_size = GET_PARAM(1); - int k = GET_PARAM(2); - int normType = GET_PARAM(3); + int desc_size = GET_PARAM(0); + int k = GET_PARAM(1); + int normType = GET_PARAM(2); int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - cv::Mat query_host(3000, desc_size, type); - fill(query_host, 0.0, 10.0); - - cv::Mat train_host(3000, desc_size, type); - fill(train_host, 0.0, 10.0); + cv::Mat query(3000, desc_size, type); + fillRandom(query); - cv::gpu::BFMatcher_GPU matcher(normType); + cv::Mat train(3000, desc_size, type); + fillRandom(train); - cv::gpu::GpuMat query(query_host); - cv::gpu::GpuMat train(train_host); - cv::gpu::GpuMat trainIdx, distance, allDist; + cv::gpu::BFMatcher_GPU d_matcher(normType); - matcher.knnMatchSingle(query, train, trainIdx, distance, allDist, k); + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_distance, d_allDist; - declare.time(3.0); + d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); TEST_CYCLE() { - matcher.knnMatchSingle(query, train, trainIdx, distance, allDist, k); + d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); } } -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_knnMatch, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(K(2), K(3)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - ////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_radiusMatch +// BFRadiusMatch -GPU_PERF_TEST(BruteForceMatcher_radiusMatch, cv::gpu::DeviceInfo, DescriptorSize, NormType) +PERF_TEST_P(DescSize_Norm, Features2D_BFRadiusMatch, Combine(Values(64, 128, 256), Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(3.0); - int desc_size = GET_PARAM(1); - int normType = GET_PARAM(2); + int desc_size = GET_PARAM(0); + int normType = GET_PARAM(1); int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - cv::Mat query_host(3000, desc_size, type); - fill(query_host, 0.0, 1.0); + cv::Mat query(3000, desc_size, type); + fillRandom(query, 0.0, 1.0); - cv::Mat train_host(3000, desc_size, type); - fill(train_host, 0.0, 1.0); + cv::Mat train(3000, desc_size, type); + fillRandom(train, 0.0, 1.0); - cv::gpu::BFMatcher_GPU matcher(normType); + cv::gpu::BFMatcher_GPU d_matcher(normType); - cv::gpu::GpuMat query(query_host); - cv::gpu::GpuMat train(train_host); - cv::gpu::GpuMat trainIdx, nMatches, distance; + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_nMatches, d_distance; - matcher.radiusMatchSingle(query, train, trainIdx, distance, nMatches, 2.0); - - declare.time(3.0); + d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); TEST_CYCLE() { - matcher.radiusMatchSingle(query, train, trainIdx, distance, nMatches, 2.0); + d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); } } -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_radiusMatch, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_filters.cpp b/modules/gpu/perf/perf_filters.cpp index 0263e8063f..71dcd49e2b 100644 --- a/modules/gpu/perf/perf_filters.cpp +++ b/modules/gpu/perf/perf_filters.cpp @@ -1,308 +1,235 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////////////////////// // Blur -IMPLEMENT_PARAM_CLASS(KernelSize, int) +DEF_PARAM_TEST(Sz_Type_KernelSz, cv::Size, MatType, int); -GPU_PERF_TEST(Blur, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) +PERF_TEST_P(Sz_Type_KernelSz, Filters_Blur, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4), Values(3, 5, 7))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int ksize = GET_PARAM(2); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::blur(src, dst, cv::Size(ksize, ksize)); + cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); TEST_CYCLE() { - cv::gpu::blur(src, dst, cv::Size(ksize, ksize)); + cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); } } -INSTANTIATE_TEST_CASE_P(Filters, Blur, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7)))); - ////////////////////////////////////////////////////////////////////// // Sobel -GPU_PERF_TEST(Sobel, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) +PERF_TEST_P(Sz_Type_KernelSz, Filters_Sobel, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1), Values(3, 5, 7, 9, 11, 13, 15))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int ksize = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; - - cv::gpu::Sobel(src, dst, -1, 1, 1, buf, ksize); + cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); TEST_CYCLE() { - cv::gpu::Sobel(src, dst, -1, 1, 1, buf, ksize); + cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); } } -INSTANTIATE_TEST_CASE_P(Filters, Sobel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - ////////////////////////////////////////////////////////////////////// // Scharr -GPU_PERF_TEST(Scharr, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Filters_Scharr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; - - cv::gpu::Scharr(src, dst, -1, 1, 0, buf); + cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); TEST_CYCLE() { - cv::gpu::Scharr(src, dst, -1, 1, 0, buf); + cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); } } -INSTANTIATE_TEST_CASE_P(Filters, Scharr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)))); - ////////////////////////////////////////////////////////////////////// // GaussianBlur -GPU_PERF_TEST(GaussianBlur, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) +PERF_TEST_P(Sz_Type_KernelSz, Filters_GaussianBlur, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1), Values(3, 5, 7, 9, 11, 13, 15))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int ksize = GET_PARAM(2); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::GaussianBlur(src, dst, cv::Size(ksize, ksize), buf, 0.5); + cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); TEST_CYCLE() { - cv::gpu::GaussianBlur(src, dst, cv::Size(ksize, ksize), buf, 0.5); + cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); } } -INSTANTIATE_TEST_CASE_P(Filters, GaussianBlur, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - ////////////////////////////////////////////////////////////////////// // Laplacian -GPU_PERF_TEST(Laplacian, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) +PERF_TEST_P(Sz_Type_KernelSz, Filters_Laplacian, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4), Values(1, 3))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int ksize = GET_PARAM(2); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::Laplacian(src, dst, -1, ksize); + cv::gpu::Laplacian(d_src, d_dst, -1, ksize); TEST_CYCLE() { - cv::gpu::Laplacian(src, dst, -1, ksize); + cv::gpu::Laplacian(d_src, d_dst, -1, ksize); } } -INSTANTIATE_TEST_CASE_P(Filters, Laplacian, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1), MatType(CV_32FC4)), - testing::Values(KernelSize(1), KernelSize(3)))); - ////////////////////////////////////////////////////////////////////// // Erode -GPU_PERF_TEST(Erode, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Filters_Erode, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::erode(src, dst, ker, buf); + cv::gpu::erode(d_src, d_dst, ker, d_buf); TEST_CYCLE() { - cv::gpu::erode(src, dst, ker, buf); + cv::gpu::erode(d_src, d_dst, ker, d_buf); } } -INSTANTIATE_TEST_CASE_P(Filters, Erode, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)))); - ////////////////////////////////////////////////////////////////////// // Dilate -GPU_PERF_TEST(Dilate, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Type, Filters_Dilate, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::dilate(src, dst, ker, buf); + cv::gpu::dilate(d_src, d_dst, ker, d_buf); TEST_CYCLE() { - cv::gpu::dilate(src, dst, ker, buf); + cv::gpu::dilate(d_src, d_dst, ker, d_buf); } } -INSTANTIATE_TEST_CASE_P(Filters, Dilate, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)))); - ////////////////////////////////////////////////////////////////////// // MorphologyEx CV_ENUM(MorphOp, cv::MORPH_OPEN, cv::MORPH_CLOSE, cv::MORPH_GRADIENT, cv::MORPH_TOPHAT, cv::MORPH_BLACKHAT) -#define ALL_MORPH_OPS testing::Values(MorphOp(cv::MORPH_OPEN), MorphOp(cv::MORPH_CLOSE), MorphOp(cv::MORPH_GRADIENT), MorphOp(cv::MORPH_TOPHAT), MorphOp(cv::MORPH_BLACKHAT)) +#define ALL_MORPH_OPS ValuesIn(MorphOp::all()) -GPU_PERF_TEST(MorphologyEx, cv::gpu::DeviceInfo, cv::Size, MatType, MorphOp) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Type_Op, cv::Size, MatType, MorphOp); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int morphOp = GET_PARAM(3); +PERF_TEST_P(Sz_Type_Op, Filters_MorphologyEx, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4), ALL_MORPH_OPS)) +{ + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int morphOp = GET_PARAM(2); - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf1; - cv::gpu::GpuMat buf2; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf1; + cv::gpu::GpuMat d_buf2; - cv::gpu::morphologyEx(src, dst, morphOp, ker, buf1, buf2); + cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); TEST_CYCLE() { - cv::gpu::morphologyEx(src, dst, morphOp, ker, buf1, buf2); + cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); } } -INSTANTIATE_TEST_CASE_P(Filters, MorphologyEx, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)), - ALL_MORPH_OPS)); - ////////////////////////////////////////////////////////////////////// // Filter2D -GPU_PERF_TEST(Filter2D, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) +PERF_TEST_P(Sz_Type_KernelSz, Filters_Filter2D, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4), Values(3, 5, 7, 9, 11, 13, 15))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int ksize = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src_host(size, type); - fill(src_host, 0.0, 255.0); + cv::Mat src(size, type); + fillRandom(src); cv::Mat kernel(ksize, ksize, CV_32FC1); - fill(kernel, 0.0, 1.0); + fillRandom(kernel, 0.0, 1.0); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::filter2D(src, dst, -1, kernel); + cv::gpu::filter2D(d_src, d_dst, -1, kernel); TEST_CYCLE() { - cv::gpu::filter2D(src, dst, -1, kernel); + cv::gpu::filter2D(d_src, d_dst, -1, kernel); } } -INSTANTIATE_TEST_CASE_P(Filters, Filter2D, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1), MatType(CV_32FC4)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 0dbcd34c65..a1614a79f3 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1,188 +1,226 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////////////////////// // Remap -GPU_PERF_TEST(Remap, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) +enum{HALF_SIZE=0, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH}; +CV_ENUM(RemapMode, HALF_SIZE, UPSIDE_DOWN, REFLECTION_X, REFLECTION_BOTH); +#define ALL_REMAP_MODES ValuesIn(RemapMode::all()) + +void generateMap(cv::Mat& map_x, cv::Mat& map_y, int remapMode) +{ + for (int j = 0; j < map_x.rows; ++j) + { + for (int i = 0; i < map_x.cols; ++i) + { + switch (remapMode) + { + case HALF_SIZE: + if (i > map_x.cols*0.25 && i < map_x.cols*0.75 && j > map_x.rows*0.25 && j < map_x.rows*0.75) + { + map_x.at(j,i) = 2 * (i - map_x.cols * 0.25) + 0.5; + map_y.at(j,i) = 2 * (j - map_x.rows * 0.25) + 0.5; + } + else + { + map_x.at(j,i) = 0; + map_y.at(j,i) = 0; + } + break; + case UPSIDE_DOWN: + map_x.at(j,i) = i; + map_y.at(j,i) = map_x.rows - j; + break; + case REFLECTION_X: + map_x.at(j,i) = map_x.cols - i; + map_y.at(j,i) = j; + break; + case REFLECTION_BOTH: + map_x.at(j,i) = map_x.cols - i; + map_y.at(j,i) = map_x.rows - j; + break; + } // end of switch + } + } +} + +DEF_PARAM_TEST(Sz_Depth_Cn_Inter_Border_Mode, cv::Size, MatDepth, int, Interpolation, BorderMode, RemapMode); + +PERF_TEST_P(Sz_Depth_Cn_Inter_Border_Mode, ImgProc_Remap, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), + ALL_BORDER_MODES, + ALL_REMAP_MODES)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(3.0); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = GET_PARAM(3); int borderMode = GET_PARAM(4); + int remapMode = GET_PARAM(5); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat xmap_host(size, CV_32FC1); - fill(xmap_host, 0, size.width); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat ymap_host(size, CV_32FC1); - fill(ymap_host, 0, size.height); + cv::Mat xmap(size, CV_32FC1); + cv::Mat ymap(size, CV_32FC1); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat xmap(xmap_host); - cv::gpu::GpuMat ymap(ymap_host); - cv::gpu::GpuMat dst; + generateMap(xmap, ymap, remapMode); - cv::gpu::remap(src, dst, xmap, ymap, interpolation, borderMode); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_xmap(xmap); + cv::gpu::GpuMat d_ymap(ymap); + cv::gpu::GpuMat d_dst; - declare.time(3.0); + cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); TEST_CYCLE() { - cv::gpu::remap(src, dst, xmap, ymap, interpolation, borderMode); + cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Remap, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - - ////////////////////////////////////////////////////////////////////// // Resize -IMPLEMENT_PARAM_CLASS(Scale, double) +DEF_PARAM_TEST(Sz_Depth_Cn_Inter_Scale, cv::Size, MatDepth, int, Interpolation, double); -GPU_PERF_TEST(Resize, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, Scale) +PERF_TEST_P(Sz_Depth_Cn_Inter_Scale, ImgProc_Resize, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + ALL_INTERPOLATIONS, + Values(0.5, 0.3, 2.0))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(1.0); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = GET_PARAM(3); double f = GET_PARAM(4); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::resize(src, dst, cv::Size(), f, f, interpolation); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - declare.time(1.0); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); TEST_CYCLE() { - cv::gpu::resize(src, dst, cv::Size(), f, f, interpolation); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Resize, testing::Combine( - ALL_DEVICES, +////////////////////////////////////////////////////////////////////// +// ResizeArea + +DEF_PARAM_TEST(Sz_Depth_Cn_Scale, cv::Size, MatDepth, int, double); + +PERF_TEST_P(Sz_Depth_Cn_Scale, ImgProc_ResizeArea, Combine( GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), - Interpolation(cv::INTER_CUBIC), Interpolation(cv::INTER_AREA)), - testing::Values(Scale(0.5), Scale(0.3), Scale(2.0)))); - -GPU_PERF_TEST(ResizeArea, cv::gpu::DeviceInfo, cv::Size, MatType, Scale) + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + Values(0.2, 0.1, 0.05))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(1.0); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = cv::INTER_AREA; double f = GET_PARAM(3); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::resize(src, dst, cv::Size(), f, f, interpolation); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - declare.time(1.0); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); TEST_CYCLE() { - cv::gpu::resize(src, dst, cv::Size(), f, f, interpolation); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); } } -INSTANTIATE_TEST_CASE_P(ImgProc, ResizeArea, testing::Combine( - ALL_DEVICES, - testing::Values(perf::sz1080p/*, cv::Size(4096, 2048)*/), - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Scale(0.2),Scale(0.1),Scale(0.05)))); - ////////////////////////////////////////////////////////////////////// // WarpAffine -GPU_PERF_TEST(WarpAffine, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Cn_Inter_Border, cv::Size, MatDepth, int, Interpolation, BorderMode); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); +PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpAffine, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), + ALL_BORDER_MODES)) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = GET_PARAM(3); int borderMode = GET_PARAM(4); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, type); + fillRandom(src); const double aplha = CV_PI / 4; double mat[2][3] = { {std::cos(aplha), -std::sin(aplha), src.cols / 2}, {std::sin(aplha), std::cos(aplha), 0}}; cv::Mat M(2, 3, CV_64F, (void*) mat); - cv::gpu::warpAffine(src, dst, M, size, interpolation, borderMode); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); TEST_CYCLE() { - cv::gpu::warpAffine(src, dst, M, size, interpolation, borderMode); + cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, WarpAffine, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - ////////////////////////////////////////////////////////////////////// // WarpPerspective -GPU_PERF_TEST(WarpPerspective, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) +PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpPerspective, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), + ALL_BORDER_MODES)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = GET_PARAM(3); int borderMode = GET_PARAM(4); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, type); + fillRandom(src); const double aplha = CV_PI / 4; double mat[3][3] = { {std::cos(aplha), -std::sin(aplha), src.cols / 2}, @@ -190,1160 +228,958 @@ GPU_PERF_TEST(WarpPerspective, cv::gpu::DeviceInfo, cv::Size, MatType, Interpola {0.0, 0.0, 1.0}}; cv::Mat M(3, 3, CV_64F, (void*) mat); - cv::gpu::warpPerspective(src, dst, M, size, interpolation, borderMode); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); TEST_CYCLE() { - cv::gpu::warpPerspective(src, dst, M, size, interpolation, borderMode); + cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, WarpPerspective, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - ////////////////////////////////////////////////////////////////////// // CopyMakeBorder -GPU_PERF_TEST(CopyMakeBorder, cv::gpu::DeviceInfo, cv::Size, MatType, BorderMode) +DEF_PARAM_TEST(Sz_Depth_Cn_Border, cv::Size, MatDepth, int, BorderMode); + +PERF_TEST_P(Sz_Depth_Cn_Border, ImgProc_CopyMakeBorder, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + ALL_BORDER_MODES)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); + int borderMode = GET_PARAM(3); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int borderType = GET_PARAM(3); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::copyMakeBorder(src, dst, 5, 5, 5, 5, borderType); + cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); TEST_CYCLE() { - cv::gpu::copyMakeBorder(src, dst, 5, 5, 5, 5, borderType); + cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, CopyMakeBorder, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - ////////////////////////////////////////////////////////////////////// // Threshold CV_ENUM(ThreshOp, cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV) -#define ALL_THRESH_OPS testing::Values(ThreshOp(cv::THRESH_BINARY), ThreshOp(cv::THRESH_BINARY_INV), ThreshOp(cv::THRESH_TRUNC), ThreshOp(cv::THRESH_TOZERO), ThreshOp(cv::THRESH_TOZERO_INV)) +#define ALL_THRESH_OPS ValuesIn(ThreshOp::all()) -GPU_PERF_TEST(Threshold, cv::gpu::DeviceInfo, cv::Size, MatDepth, ThreshOp) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Op, cv::Size, MatDepth, ThreshOp); - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int threshOp = GET_PARAM(3); +PERF_TEST_P(Sz_Depth_Op, ImgProc_Threshold, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F, CV_64F), + ALL_THRESH_OPS)) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int threshOp = GET_PARAM(2); - cv::Mat src_host(size, depth); - fill(src_host, 0, 255); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::threshold(src, dst, 100.0, 255.0, threshOp); + cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); TEST_CYCLE() { - cv::gpu::threshold(src, dst, 100.0, 255.0, threshOp); + cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Threshold, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)), - ALL_THRESH_OPS)); - ////////////////////////////////////////////////////////////////////// // Integral -GPU_PERF_TEST(Integral, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_Integral, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_8UC1); - fill(src_host, 0, 255); + cv::Mat src(size, CV_8UC1); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::integralBuffered(src, dst, buf); + cv::gpu::integralBuffered(d_src, d_dst, d_buf); TEST_CYCLE() { - cv::gpu::integralBuffered(src, dst, buf); + cv::gpu::integralBuffered(d_src, d_dst, d_buf); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Integral, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// -// Integral_Sqr +// IntegralSqr -GPU_PERF_TEST(Integral_Sqr, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_IntegralSqr, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_8UC1); - fill(src_host, 0, 255); + cv::Mat src(size, CV_8UC1); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::sqrIntegral(src, dst); + cv::gpu::sqrIntegral(d_src, d_dst); TEST_CYCLE() { - cv::gpu::sqrIntegral(src, dst); + cv::gpu::sqrIntegral(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Integral_Sqr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// -// HistEven_OneChannel +// HistEvenC1 -GPU_PERF_TEST(HistEven_OneChannel, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, ImgProc_HistEvenC1, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_16S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, depth); - fill(src_host, 0, 255); + cv::Mat src(size, depth); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat hist; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::histEven(src, hist, buf, 30, 0, 180); + cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); TEST_CYCLE() { - cv::gpu::histEven(src, hist, buf, 30, 0, 180); + cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); } } -INSTANTIATE_TEST_CASE_P(ImgProc, HistEven_OneChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_16S)))); - ////////////////////////////////////////////////////////////////////// -// HistEven_FourChannel +// HistEvenC4 -GPU_PERF_TEST(HistEven_FourChannel, cv::gpu::DeviceInfo, cv::Size, MatDepth) +PERF_TEST_P(Sz_Depth, ImgProc_HistEvenC4, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_16S))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); - cv::Mat src_host(size, CV_MAKE_TYPE(depth, 4)); - fill(src_host, 0, 255); + cv::Mat src(size, CV_MAKE_TYPE(depth, 4)); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat hist[4]; - cv::gpu::GpuMat buf; int histSize[] = {30, 30, 30, 30}; int lowerLevel[] = {0, 0, 0, 0}; int upperLevel[] = {180, 180, 180, 180}; - cv::gpu::histEven(src, hist, buf, histSize, lowerLevel, upperLevel); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist[4]; + cv::gpu::GpuMat d_buf; + + cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); TEST_CYCLE() { - cv::gpu::histEven(src, hist, buf, histSize, lowerLevel, upperLevel); + cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); } } -INSTANTIATE_TEST_CASE_P(ImgProc, HistEven_FourChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_16S)))); - ////////////////////////////////////////////////////////////////////// // CalcHist -GPU_PERF_TEST(CalcHist, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_CalcHist, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_8UC1); - fill(src_host, 0, 255); + cv::Mat src(size, CV_8UC1); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat hist; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::calcHist(src, hist, buf); + cv::gpu::calcHist(d_src, d_hist, d_buf); TEST_CYCLE() { - cv::gpu::calcHist(src, hist, buf); + cv::gpu::calcHist(d_src, d_hist, d_buf); } } -INSTANTIATE_TEST_CASE_P(ImgProc, CalcHist, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // EqualizeHist -GPU_PERF_TEST(EqualizeHist, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_EqualizeHist, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GetParam(); - cv::Size size = GET_PARAM(1); + cv::Mat src(size, CV_8UC1); + fillRandom(src); - cv::Mat src_host(size, CV_8UC1); - fill(src_host, 0, 255); - - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat hist; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::equalizeHist(src, dst, hist, buf); + cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); TEST_CYCLE() { - cv::gpu::equalizeHist(src, dst, hist, buf); + cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); } } -INSTANTIATE_TEST_CASE_P(ImgProc, EqualizeHist, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // ColumnSum -GPU_PERF_TEST(ColumnSum, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_ColumnSum, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_32FC1); - fill(src_host, 0, 255); + cv::Mat src(size, CV_32FC1); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::columnSum(src, dst); + cv::gpu::columnSum(d_src, d_dst); TEST_CYCLE() { - cv::gpu::columnSum(src, dst); + cv::gpu::columnSum(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(ImgProc, ColumnSum, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Canny -IMPLEMENT_PARAM_CLASS(AppertureSize, int) -IMPLEMENT_PARAM_CLASS(L2gradient, bool) +DEF_PARAM_TEST(Image_AppertureSz_L2gradient, string, int, bool); -GPU_PERF_TEST(Canny, cv::gpu::DeviceInfo, AppertureSize, L2gradient) +PERF_TEST_P(Image_AppertureSz_L2gradient, ImgProc_Canny, Combine( + Values("perf/800x600.jpg", "perf/1280x1024.jpg", "perf/1680x1050.jpg"), + Values(3, 5), + Bool())) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - + string fileName = GET_PARAM(0); int apperture_size = GET_PARAM(1); bool useL2gradient = GET_PARAM(2); - cv::Mat image_host = readImage("perf/1280x1024.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(image_host.empty()); + cv::Mat image = readImage(fileName, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(image.empty()); - cv::gpu::GpuMat image(image_host); - cv::gpu::GpuMat dst; - cv::gpu::CannyBuf buf; + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_dst; + cv::gpu::CannyBuf d_buf; - cv::gpu::Canny(image, buf, dst, 50.0, 100.0, apperture_size, useL2gradient); + cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); TEST_CYCLE() { - cv::gpu::Canny(image, buf, dst, 50.0, 100.0, apperture_size, useL2gradient); + cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine( - ALL_DEVICES, - testing::Values(AppertureSize(3), AppertureSize(5)), - testing::Values(L2gradient(false), L2gradient(true)))); - ////////////////////////////////////////////////////////////////////// // MeanShiftFiltering -GPU_PERF_TEST_1(MeanShiftFiltering, cv::gpu::DeviceInfo) +DEF_PARAM_TEST_1(Image, string); + +PERF_TEST_P(Image, ImgProc_MeanShiftFiltering, Values("gpu/meanshift/cones.png")) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(5.0); - cv::Mat img = readImage("gpu/meanshift/cones.png"); + cv::Mat img = readImage(GetParam()); ASSERT_FALSE(img.empty()); cv::Mat rgba; cv::cvtColor(img, rgba, cv::COLOR_BGR2BGRA); - cv::gpu::GpuMat src(rgba); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(rgba); + cv::gpu::GpuMat d_dst; - cv::gpu::meanShiftFiltering(src, dst, 50, 50); - - declare.time(5.0); + cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); TEST_CYCLE() { - cv::gpu::meanShiftFiltering(src, dst, 50, 50); + cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); } } -INSTANTIATE_TEST_CASE_P(ImgProc, MeanShiftFiltering, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // MeanShiftProc -GPU_PERF_TEST_1(MeanShiftProc, cv::gpu::DeviceInfo) +PERF_TEST_P(Image, ImgProc_MeanShiftProc, Values("gpu/meanshift/cones.png")) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(5.0); - cv::Mat img = readImage("gpu/meanshift/cones.png"); + cv::Mat img = readImage(GetParam()); ASSERT_FALSE(img.empty()); cv::Mat rgba; cv::cvtColor(img, rgba, cv::COLOR_BGR2BGRA); - cv::gpu::GpuMat src(rgba); - cv::gpu::GpuMat dstr; - cv::gpu::GpuMat dstsp; - - cv::gpu::meanShiftProc(src, dstr, dstsp, 50, 50); + cv::gpu::GpuMat d_src(rgba); + cv::gpu::GpuMat d_dstr; + cv::gpu::GpuMat d_dstsp; - declare.time(5.0); + cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); TEST_CYCLE() { - cv::gpu::meanShiftProc(src, dstr, dstsp, 50, 50); + cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); } } -INSTANTIATE_TEST_CASE_P(ImgProc, MeanShiftProc, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // MeanShiftSegmentation -GPU_PERF_TEST_1(MeanShiftSegmentation, cv::gpu::DeviceInfo) +PERF_TEST_P(Image, ImgProc_MeanShiftSegmentation, Values("gpu/meanshift/cones.png")) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(5.0); - cv::Mat img = readImage("gpu/meanshift/cones.png"); + cv::Mat img = readImage(GetParam()); ASSERT_FALSE(img.empty()); cv::Mat rgba; cv::cvtColor(img, rgba, cv::COLOR_BGR2BGRA); - cv::gpu::GpuMat src(rgba); cv::Mat dst; - meanShiftSegmentation(src, dst, 10, 10, 20); + cv::gpu::GpuMat d_src(rgba); - declare.time(5.0); + cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); TEST_CYCLE() { - meanShiftSegmentation(src, dst, 10, 10, 20); + cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); } } -INSTANTIATE_TEST_CASE_P(ImgProc, MeanShiftSegmentation, ALL_DEVICES); - ////////////////////////////////////////////////////////////////////// // BlendLinear -GPU_PERF_TEST(BlendLinear, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, ImgProc_BlendLinear, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_32F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat img1_host(size, type); - fill(img1_host, 0, 255); + cv::Mat img1(size, type); + fillRandom(img1); - cv::Mat img2_host(size, type); - fill(img2_host, 0, 255); + cv::Mat img2(size, type); + fillRandom(img2); - cv::gpu::GpuMat img1(img1_host); - cv::gpu::GpuMat img2(img2_host); - cv::gpu::GpuMat weights1(size, CV_32FC1, cv::Scalar::all(0.5)); - cv::gpu::GpuMat weights2(size, CV_32FC1, cv::Scalar::all(0.5)); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_img1(img1); + cv::gpu::GpuMat d_img2(img2); + cv::gpu::GpuMat d_weights1(size, CV_32FC1, cv::Scalar::all(0.5)); + cv::gpu::GpuMat d_weights2(size, CV_32FC1, cv::Scalar::all(0.5)); + cv::gpu::GpuMat d_dst; - cv::gpu::blendLinear(img1, img2, weights1, weights2, dst); + cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); TEST_CYCLE() { - cv::gpu::blendLinear(img1, img2, weights1, weights2, dst); + cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); } } -INSTANTIATE_TEST_CASE_P(ImgProc, BlendLinear, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - ////////////////////////////////////////////////////////////////////// // Convolve -IMPLEMENT_PARAM_CLASS(KSize, int) -IMPLEMENT_PARAM_CLASS(Ccorr, bool) +DEF_PARAM_TEST(Sz_KernelSz_Ccorr, cv::Size, int, bool); -GPU_PERF_TEST(Convolve, cv::gpu::DeviceInfo, cv::Size, KSize, Ccorr) +PERF_TEST_P(Sz_KernelSz_Ccorr, ImgProc_Convolve, Combine(GPU_TYPICAL_MAT_SIZES, Values(17, 27, 32, 64), Bool())) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int templ_size = GET_PARAM(2); - bool ccorr = GET_PARAM(3); + declare.time(2.0); - cv::gpu::GpuMat image = cv::gpu::createContinuous(size, CV_32FC1); - image.setTo(cv::Scalar(1.0)); + cv::Size size = GET_PARAM(0); + int templ_size = GET_PARAM(1); + bool ccorr = GET_PARAM(2); - cv::gpu::GpuMat templ = cv::gpu::createContinuous(templ_size, templ_size, CV_32FC1); - templ.setTo(cv::Scalar(1.0)); + cv::gpu::GpuMat d_image = cv::gpu::createContinuous(size, CV_32FC1); + d_image.setTo(cv::Scalar(1.0)); - cv::gpu::GpuMat dst; - cv::gpu::ConvolveBuf buf; + cv::gpu::GpuMat d_templ = cv::gpu::createContinuous(templ_size, templ_size, CV_32FC1); + d_templ.setTo(cv::Scalar(1.0)); - cv::gpu::convolve(image, templ, dst, ccorr, buf); + cv::gpu::GpuMat d_dst; + cv::gpu::ConvolveBuf d_buf; - declare.time(2.0); + cv::gpu::convolve(d_image, d_templ, d_dst, ccorr, d_buf); TEST_CYCLE() { - cv::gpu::convolve(image, templ, dst, ccorr, buf); + cv::gpu::convolve(d_image, d_templ, d_dst, ccorr, d_buf); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Convolve, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(KSize(3), KSize(9), KSize(17), KSize(27), KSize(32), KSize(64)), - testing::Values(Ccorr(false), Ccorr(true)))); - //////////////////////////////////////////////////////////////////////////////// -// MatchTemplate_8U +// MatchTemplate8U CV_ENUM(TemplateMethod, cv::TM_SQDIFF, cv::TM_SQDIFF_NORMED, cv::TM_CCORR, cv::TM_CCORR_NORMED, cv::TM_CCOEFF, cv::TM_CCOEFF_NORMED) -#define ALL_TEMPLATE_METHODS testing::Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_SQDIFF_NORMED), TemplateMethod(cv::TM_CCORR), TemplateMethod(cv::TM_CCORR_NORMED), TemplateMethod(cv::TM_CCOEFF), TemplateMethod(cv::TM_CCOEFF_NORMED)) +#define ALL_TEMPLATE_METHODS ValuesIn(TemplateMethod::all()) -IMPLEMENT_PARAM_CLASS(TemplateSize, cv::Size) +DEF_PARAM_TEST(Sz_TemplateSz_Cn_Method, cv::Size, cv::Size, int, TemplateMethod); -GPU_PERF_TEST(MatchTemplate_8U, cv::gpu::DeviceInfo, cv::Size, TemplateSize, Channels, TemplateMethod) +PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate8U, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(cv::Size(5, 5), cv::Size(16, 16), cv::Size(30, 30)), + Values(1, 3, 4), + ALL_TEMPLATE_METHODS)) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - cv::Size templ_size = GET_PARAM(2); - int cn = GET_PARAM(3); - int method = GET_PARAM(4); + cv::Size size = GET_PARAM(0); + cv::Size templ_size = GET_PARAM(1); + int cn = GET_PARAM(2); + int method = GET_PARAM(3); - cv::Mat image_host(size, CV_MAKE_TYPE(CV_8U, cn)); - fill(image_host, 0, 255); + cv::Mat image(size, CV_MAKE_TYPE(CV_8U, cn)); + fillRandom(image); - cv::Mat templ_host(templ_size, CV_MAKE_TYPE(CV_8U, cn)); - fill(templ_host, 0, 255); + cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_8U, cn)); + fillRandom(templ); - cv::gpu::GpuMat image(image_host); - cv::gpu::GpuMat templ(templ_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_templ(templ); + cv::gpu::GpuMat d_dst; - cv::gpu::matchTemplate(image, templ, dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); TEST_CYCLE() { - cv::gpu::matchTemplate(image, templ, dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); } }; -INSTANTIATE_TEST_CASE_P(ImgProc, MatchTemplate_8U, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(TemplateSize(cv::Size(5, 5)), TemplateSize(cv::Size(16, 16)), TemplateSize(cv::Size(30, 30))), - testing::Values(Channels(1), Channels(3), Channels(4)), - ALL_TEMPLATE_METHODS)); - //////////////////////////////////////////////////////////////////////////////// -// MatchTemplate_32F +// MatchTemplate32F -GPU_PERF_TEST(MatchTemplate_32F, cv::gpu::DeviceInfo, cv::Size, TemplateSize, Channels, TemplateMethod) +PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate32F, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(cv::Size(5, 5), cv::Size(16, 16), cv::Size(30, 30)), + Values(1, 3, 4), + Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_CCORR)))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - cv::Size templ_size = GET_PARAM(2); - int cn = GET_PARAM(3); - int method = GET_PARAM(4); + cv::Size size = GET_PARAM(0); + cv::Size templ_size = GET_PARAM(1); + int cn = GET_PARAM(2); + int method = GET_PARAM(3); - cv::Mat image_host(size, CV_MAKE_TYPE(CV_32F, cn)); - fill(image_host, 0, 255); + cv::Mat image(size, CV_MAKE_TYPE(CV_32F, cn)); + fillRandom(image); - cv::Mat templ_host(templ_size, CV_MAKE_TYPE(CV_32F, cn)); - fill(templ_host, 0, 255); + cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_32F, cn)); + fillRandom(templ); - cv::gpu::GpuMat image(image_host); - cv::gpu::GpuMat templ(templ_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_templ(templ); + cv::gpu::GpuMat d_dst; - cv::gpu::matchTemplate(image, templ, dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); TEST_CYCLE() { - cv::gpu::matchTemplate(image, templ, dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); } }; -INSTANTIATE_TEST_CASE_P(ImgProc, MatchTemplate_32F, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(TemplateSize(cv::Size(5, 5)), TemplateSize(cv::Size(16, 16)), TemplateSize(cv::Size(30, 30))), - testing::Values(Channels(1), Channels(3), Channels(4)), - testing::Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_CCORR)))); - ////////////////////////////////////////////////////////////////////// // MulSpectrums CV_FLAGS(DftFlags, 0, cv::DFT_INVERSE, cv::DFT_SCALE, cv::DFT_ROWS, cv::DFT_COMPLEX_OUTPUT, cv::DFT_REAL_OUTPUT) -GPU_PERF_TEST(MulSpectrums, cv::gpu::DeviceInfo, cv::Size, DftFlags) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Flags, cv::Size, DftFlags); - cv::Size size = GET_PARAM(1); - int flag = GET_PARAM(2); +PERF_TEST_P(Sz_Flags, ImgProc_MulSpectrums, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(0, DftFlags(cv::DFT_ROWS)))) +{ + cv::Size size = GET_PARAM(0); + int flag = GET_PARAM(1); - cv::Mat a_host(size, CV_32FC2); - fill(a_host, 0, 100); + cv::Mat a(size, CV_32FC2); + fillRandom(a, 0, 100); - cv::Mat b_host(size, CV_32FC2); - fill(b_host, 0, 100); + cv::Mat b(size, CV_32FC2); + fillRandom(b, 0, 100); - cv::gpu::GpuMat a(a_host); - cv::gpu::GpuMat b(b_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_a(a); + cv::gpu::GpuMat d_b(b); + cv::gpu::GpuMat d_dst; - cv::gpu::mulSpectrums(a, b, dst, flag); + cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); TEST_CYCLE() { - cv::gpu::mulSpectrums(a, b, dst, flag); + cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); } } -INSTANTIATE_TEST_CASE_P(ImgProc, MulSpectrums, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DftFlags(0), DftFlags(cv::DFT_ROWS)))); - ////////////////////////////////////////////////////////////////////// // MulAndScaleSpectrums -GPU_PERF_TEST(MulAndScaleSpectrums, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_MulAndScaleSpectrums, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); float scale = 1.f / size.area(); - cv::Mat src1_host(size, CV_32FC2); - fill(src1_host, 0, 100); + cv::Mat src1(size, CV_32FC2); + fillRandom(src1, 0, 100); - cv::Mat src2_host(size, CV_32FC2); - fill(src2_host, 0, 100); + cv::Mat src2(size, CV_32FC2); + fillRandom(src2, 0, 100); - cv::gpu::GpuMat src1(src1_host); - cv::gpu::GpuMat src2(src2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::mulAndScaleSpectrums(src1, src2, dst, cv::DFT_ROWS, scale, false); + cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); TEST_CYCLE() { - cv::gpu::mulAndScaleSpectrums(src1, src2, dst, cv::DFT_ROWS, scale, false); + cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); } } -INSTANTIATE_TEST_CASE_P(ImgProc, MulAndScaleSpectrums, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Dft -GPU_PERF_TEST(Dft, cv::gpu::DeviceInfo, cv::Size, DftFlags) +PERF_TEST_P(Sz_Flags, ImgProc_Dft, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(0, DftFlags(cv::DFT_ROWS), DftFlags(cv::DFT_INVERSE)))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int flag = GET_PARAM(2); + declare.time(2.0); - cv::Mat src_host(size, CV_32FC2); - fill(src_host, 0, 100); + cv::Size size = GET_PARAM(0); + int flag = GET_PARAM(1); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, CV_32FC2); + fillRandom(src, 0, 100); - cv::gpu::dft(src, dst, size, flag); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - declare.time(2.0); + cv::gpu::dft(d_src, d_dst, size, flag); TEST_CYCLE() { - cv::gpu::dft(src, dst, size, flag); + cv::gpu::dft(d_src, d_dst, size, flag); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Dft, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DftFlags(0), DftFlags(cv::DFT_ROWS), DftFlags(cv::DFT_INVERSE)))); - ////////////////////////////////////////////////////////////////////// // CornerHarris -IMPLEMENT_PARAM_CLASS(BlockSize, int) -IMPLEMENT_PARAM_CLASS(ApertureSize, int) +DEF_PARAM_TEST(Image_Type_Border_BlockSz_ApertureSz, string, MatType, BorderMode, int, int); -GPU_PERF_TEST(CornerHarris, cv::gpu::DeviceInfo, MatType, BorderMode, BlockSize, ApertureSize) +PERF_TEST_P(Image_Type_Border_BlockSz_ApertureSz, ImgProc_CornerHarris, Combine( + Values("gpu/stereobm/aloe-L.png"), + Values(CV_8UC1, CV_32FC1), + Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), + Values(3, 5, 7), + Values(0, 3, 5, 7))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + double k = 0.5; + string fileName = GET_PARAM(0); int type = GET_PARAM(1); - int borderType = GET_PARAM(2); + int borderMode = GET_PARAM(2); int blockSize = GET_PARAM(3); int apertureSize = GET_PARAM(4); - cv::Mat img = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); + cv::Mat img = readImage(fileName, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - cv::gpu::GpuMat src(img); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat Dx; - cv::gpu::GpuMat Dy; - cv::gpu::GpuMat buf; - - double k = 0.5; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_Dx; + cv::gpu::GpuMat d_Dy; + cv::gpu::GpuMat d_buf; - cv::gpu::cornerHarris(src, dst, Dx, Dy, buf, blockSize, apertureSize, k, borderType); + cv::gpu::cornerHarris(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, k, borderMode); TEST_CYCLE() { - cv::gpu::cornerHarris(src, dst, Dx, Dy, buf, blockSize, apertureSize, k, borderType); + cv::gpu::cornerHarris(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, k, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, CornerHarris, testing::Combine( - ALL_DEVICES, - testing::Values(MatType(CV_8UC1), MatType(CV_32FC1)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), - testing::Values(BlockSize(3), BlockSize(5), BlockSize(7)), - testing::Values(ApertureSize(0), ApertureSize(3), ApertureSize(5), ApertureSize(7)))); - ////////////////////////////////////////////////////////////////////// // CornerMinEigenVal -GPU_PERF_TEST(CornerMinEigenVal, cv::gpu::DeviceInfo, MatType, BorderMode, BlockSize, ApertureSize) +PERF_TEST_P(Image_Type_Border_BlockSz_ApertureSz, ImgProc_CornerMinEigenVal, Combine( + Values("gpu/stereobm/aloe-L.png"), + Values(CV_8UC1, CV_32FC1), + Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), + Values(3, 5, 7), + Values(0, 3, 5, 7))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - + string fileName = GET_PARAM(0); int type = GET_PARAM(1); - int borderType = GET_PARAM(2); + int borderMode = GET_PARAM(2); int blockSize = GET_PARAM(3); int apertureSize = GET_PARAM(4); - cv::Mat img = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); + cv::Mat img = readImage(fileName, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - cv::gpu::GpuMat src(img); - cv::gpu::GpuMat dst; - cv::gpu::GpuMat Dx; - cv::gpu::GpuMat Dy; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_Dx; + cv::gpu::GpuMat d_Dy; + cv::gpu::GpuMat d_buf; - cv::gpu::cornerMinEigenVal(src, dst, Dx, Dy, buf, blockSize, apertureSize, borderType); + cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); TEST_CYCLE() { - cv::gpu::cornerMinEigenVal(src, dst, Dx, Dy, buf, blockSize, apertureSize, borderType); + cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); } } -INSTANTIATE_TEST_CASE_P(ImgProc, CornerMinEigenVal, testing::Combine( - ALL_DEVICES, - testing::Values(MatType(CV_8UC1), MatType(CV_32FC1)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), - testing::Values(BlockSize(3), BlockSize(5), BlockSize(7)), - testing::Values(ApertureSize(0), ApertureSize(3), ApertureSize(5), ApertureSize(7)))); - ////////////////////////////////////////////////////////////////////// // BuildWarpPlaneMaps -GPU_PERF_TEST(BuildWarpPlaneMaps, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_BuildWarpPlaneMaps, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); cv::Mat T = cv::Mat::zeros(1, 3, CV_32F); - cv::gpu::GpuMat map_x; - cv::gpu::GpuMat map_y; - cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, map_x, map_y); + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; + + cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); TEST_CYCLE() { - cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, map_x, map_y); + cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); } } -INSTANTIATE_TEST_CASE_P(ImgProc, BuildWarpPlaneMaps, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // BuildWarpCylindricalMaps -GPU_PERF_TEST(BuildWarpCylindricalMaps, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_BuildWarpCylindricalMaps, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat map_x; - cv::gpu::GpuMat map_y; - cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, map_x, map_y); + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; + + cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); TEST_CYCLE() { - cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, map_x, map_y); + cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); } } -INSTANTIATE_TEST_CASE_P(ImgProc, BuildWarpCylindricalMaps, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // BuildWarpSphericalMaps -GPU_PERF_TEST(BuildWarpSphericalMaps, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_BuildWarpSphericalMaps, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat map_x; - cv::gpu::GpuMat map_y; - cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, map_x, map_y); + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; + + cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); TEST_CYCLE() { - cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, map_x, map_y); + cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); } } -INSTANTIATE_TEST_CASE_P(ImgProc, BuildWarpSphericalMaps, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // Rotate -GPU_PERF_TEST(Rotate, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Depth_Cn_Inter, cv::Size, MatDepth, int, Interpolation); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); +PERF_TEST_P(Sz_Depth_Cn_Inter, ImgProc_Rotate, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4), + Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)))) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); int interpolation = GET_PARAM(3); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::rotate(src, dst, size, 30.0, 0, 0, interpolation); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + + cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); TEST_CYCLE() { - cv::gpu::rotate(src, dst, size, 30.0, 0, 0, interpolation); + cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); } } -INSTANTIATE_TEST_CASE_P(ImgProc, Rotate, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)))); - ////////////////////////////////////////////////////////////////////// // PyrDown -GPU_PERF_TEST(PyrDown, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, ImgProc_PyrDown, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pyrDown(src, dst); + cv::gpu::pyrDown(d_src, d_dst); TEST_CYCLE() { - cv::gpu::pyrDown(src, dst); + cv::gpu::pyrDown(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(ImgProc, PyrDown, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - ////////////////////////////////////////////////////////////////////// // PyrUp -GPU_PERF_TEST(PyrUp, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, ImgProc_PyrUp, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pyrUp(src, dst); + cv::gpu::pyrUp(d_src, d_dst); TEST_CYCLE() { - cv::gpu::pyrUp(src, dst); + cv::gpu::pyrUp(d_src, d_dst); } } -INSTANTIATE_TEST_CASE_P(ImgProc, PyrUp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - ////////////////////////////////////////////////////////////////////// // CvtColor -GPU_PERF_TEST(CvtColor, cv::gpu::DeviceInfo, cv::Size, MatDepth, CvtColorInfo) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - CvtColorInfo info = GET_PARAM(3); +DEF_PARAM_TEST(Sz_Depth_Code, cv::Size, MatDepth, CvtColorInfo); - cv::Mat src_host(size, CV_MAKETYPE(depth, info.scn)); - fill(src_host, 0, 255); +PERF_TEST_P(Sz_Depth_Code, ImgProc_CvtColor, Combine( + GPU_TYPICAL_MAT_SIZES, + Values(CV_8U, CV_16U, CV_32F), + Values(CvtColorInfo(4, 4, cv::COLOR_RGBA2BGRA), + CvtColorInfo(4, 1, cv::COLOR_BGRA2GRAY), + CvtColorInfo(1, 4, cv::COLOR_GRAY2BGRA), + CvtColorInfo(3, 3, cv::COLOR_BGR2XYZ), + CvtColorInfo(3, 3, cv::COLOR_XYZ2BGR), + CvtColorInfo(3, 3, cv::COLOR_BGR2YCrCb), + CvtColorInfo(3, 3, cv::COLOR_YCrCb2BGR), + CvtColorInfo(3, 3, cv::COLOR_BGR2YUV), + CvtColorInfo(3, 3, cv::COLOR_YUV2BGR), + CvtColorInfo(3, 3, cv::COLOR_BGR2HSV), + CvtColorInfo(3, 3, cv::COLOR_HSV2BGR), + CvtColorInfo(3, 3, cv::COLOR_BGR2HLS), + CvtColorInfo(3, 3, cv::COLOR_HLS2BGR), + CvtColorInfo(3, 3, cv::COLOR_BGR2Lab), + CvtColorInfo(3, 3, cv::COLOR_RGB2Lab), + CvtColorInfo(3, 3, cv::COLOR_BGR2Luv), + CvtColorInfo(3, 3, cv::COLOR_RGB2Luv), + CvtColorInfo(3, 3, cv::COLOR_Lab2BGR), + CvtColorInfo(3, 3, cv::COLOR_Lab2RGB), + CvtColorInfo(3, 3, cv::COLOR_Luv2BGR), + CvtColorInfo(3, 3, cv::COLOR_Luv2RGB), + CvtColorInfo(1, 3, cv::COLOR_BayerBG2BGR), + CvtColorInfo(1, 3, cv::COLOR_BayerGB2BGR), + CvtColorInfo(1, 3, cv::COLOR_BayerRG2BGR), + CvtColorInfo(1, 3, cv::COLOR_BayerGR2BGR), + CvtColorInfo(4, 4, cv::COLOR_RGBA2mRGBA)))) +{ + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + CvtColorInfo info = GET_PARAM(2); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::Mat src(size, CV_MAKETYPE(depth, info.scn)); + fillRandom(src); - if (info.code >= cv::COLOR_BayerBG2BGR && info.code <= cv::COLOR_BayerGR2BGR) - info.dcn = 4; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::cvtColor(src, dst, info.code, info.dcn); + cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); TEST_CYCLE() { - cv::gpu::cvtColor(src, dst, info.code, info.dcn); + cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); } } -INSTANTIATE_TEST_CASE_P(ImgProc, CvtColor, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F)), - testing::Values(CvtColorInfo(4, 4, cv::COLOR_RGBA2BGRA), - CvtColorInfo(4, 1, cv::COLOR_BGRA2GRAY), - CvtColorInfo(1, 4, cv::COLOR_GRAY2BGRA), - CvtColorInfo(3, 3, cv::COLOR_BGR2XYZ), - CvtColorInfo(3, 3, cv::COLOR_XYZ2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2YCrCb), - CvtColorInfo(3, 3, cv::COLOR_YCrCb2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2YUV), - CvtColorInfo(3, 3, cv::COLOR_YUV2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2HSV), - CvtColorInfo(3, 3, cv::COLOR_HSV2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2HLS), - CvtColorInfo(3, 3, cv::COLOR_HLS2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2Lab), - CvtColorInfo(3, 3, cv::COLOR_RGB2Lab), - CvtColorInfo(3, 3, cv::COLOR_BGR2Luv), - CvtColorInfo(3, 3, cv::COLOR_RGB2Luv), - CvtColorInfo(3, 3, cv::COLOR_Lab2BGR), - CvtColorInfo(3, 3, cv::COLOR_Lab2RGB), - CvtColorInfo(3, 3, cv::COLOR_Luv2BGR), - CvtColorInfo(3, 3, cv::COLOR_Luv2RGB), - CvtColorInfo(1, 3, cv::COLOR_BayerBG2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerGB2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerRG2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerGR2BGR), - CvtColorInfo(4, 4, cv::COLOR_RGBA2mRGBA)))); - ////////////////////////////////////////////////////////////////////// // SwapChannels -GPU_PERF_TEST(SwapChannels, cv::gpu::DeviceInfo, cv::Size) +PERF_TEST_P(Sz, ImgProc_SwapChannels, GPU_TYPICAL_MAT_SIZES) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Size size = GET_PARAM(1); + cv::Size size = GetParam(); - cv::Mat src_host(size, CV_8UC4); - fill(src_host, 0, 255); - - cv::gpu::GpuMat src(src_host); + cv::Mat src(size, CV_8UC4); + fillRandom(src); const int dstOrder[] = {2, 1, 0, 3}; - cv::gpu::swapChannels(src, dstOrder); + cv::gpu::GpuMat d_src(src); + + cv::gpu::swapChannels(d_src, dstOrder); TEST_CYCLE() { - cv::gpu::swapChannels(src, dstOrder); + cv::gpu::swapChannels(d_src, dstOrder); } } -INSTANTIATE_TEST_CASE_P(ImgProc, SwapChannels, testing::Combine(ALL_DEVICES, GPU_TYPICAL_MAT_SIZES)); - ////////////////////////////////////////////////////////////////////// // AlphaComp CV_ENUM(AlphaOp, cv::gpu::ALPHA_OVER, cv::gpu::ALPHA_IN, cv::gpu::ALPHA_OUT, cv::gpu::ALPHA_ATOP, cv::gpu::ALPHA_XOR, cv::gpu::ALPHA_PLUS, cv::gpu::ALPHA_OVER_PREMUL, cv::gpu::ALPHA_IN_PREMUL, cv::gpu::ALPHA_OUT_PREMUL, cv::gpu::ALPHA_ATOP_PREMUL, cv::gpu::ALPHA_XOR_PREMUL, cv::gpu::ALPHA_PLUS_PREMUL, cv::gpu::ALPHA_PREMUL) +#define ALL_ALPHA_OPS ValuesIn(AlphaOp::all()) -GPU_PERF_TEST(AlphaComp, cv::gpu::DeviceInfo, cv::Size, MatType, AlphaOp) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_Type_Op, cv::Size, MatType, AlphaOp); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int alpha_op = GET_PARAM(3); +PERF_TEST_P(Sz_Type_Op, ImgProc_AlphaComp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC4, CV_16UC4, CV_32SC4, CV_32FC4), ALL_ALPHA_OPS)) +{ + cv::Size size = GET_PARAM(0); + int type = GET_PARAM(1); + int alpha_op = GET_PARAM(2); - cv::Mat img1_host(size, type); - fill(img1_host, 0, 255); + cv::Mat img1(size, type); + fillRandom(img1); - cv::Mat img2_host(size, type); - fill(img2_host, 0, 255); + cv::Mat img2(size, type); + fillRandom(img2); - cv::gpu::GpuMat img1(img1_host); - cv::gpu::GpuMat img2(img2_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_img1(img1); + cv::gpu::GpuMat d_img2(img2); + cv::gpu::GpuMat d_dst; - cv::gpu::alphaComp(img1, img2, dst, alpha_op); + cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); TEST_CYCLE() { - cv::gpu::alphaComp(img1, img2, dst, alpha_op); + cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); } } -INSTANTIATE_TEST_CASE_P(ImgProc, AlphaComp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC4), MatType(CV_16UC4), MatType(CV_32SC4), MatType(CV_32FC4)), - testing::Values(AlphaOp(cv::gpu::ALPHA_OVER), - AlphaOp(cv::gpu::ALPHA_IN), - AlphaOp(cv::gpu::ALPHA_OUT), - AlphaOp(cv::gpu::ALPHA_ATOP), - AlphaOp(cv::gpu::ALPHA_XOR), - AlphaOp(cv::gpu::ALPHA_PLUS), - AlphaOp(cv::gpu::ALPHA_OVER_PREMUL), - AlphaOp(cv::gpu::ALPHA_IN_PREMUL), - AlphaOp(cv::gpu::ALPHA_OUT_PREMUL), - AlphaOp(cv::gpu::ALPHA_ATOP_PREMUL), - AlphaOp(cv::gpu::ALPHA_XOR_PREMUL), - AlphaOp(cv::gpu::ALPHA_PLUS_PREMUL), - AlphaOp(cv::gpu::ALPHA_PREMUL)))); - ////////////////////////////////////////////////////////////////////// -// ImagePyramid +// ImagePyramidBuild -GPU_PERF_TEST(ImagePyramid_build, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, ImgProc_ImagePyramidBuild, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::gpu::GpuMat src(src_host); + cv::gpu::GpuMat d_src(src); - cv::gpu::ImagePyramid pyr; + cv::gpu::ImagePyramid d_pyr; - pyr.build(src, 5); + d_pyr.build(d_src, 5); TEST_CYCLE() { - pyr.build(src, 5); + d_pyr.build(d_src, 5); } } -INSTANTIATE_TEST_CASE_P(ImgProc, ImagePyramid_build, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); +////////////////////////////////////////////////////////////////////// +// ImagePyramidGetLayer -GPU_PERF_TEST(ImagePyramid_getLayer, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, ImgProc_ImagePyramidGetLayer, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); + + int type = CV_MAKE_TYPE(depth, channels); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Size dstSize(size.width / 2 + 10, size.height / 2 + 10); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::ImagePyramid pyr(src, 3); + cv::gpu::ImagePyramid d_pyr(d_src, 3); - pyr.getLayer(dst, cv::Size(size.width / 2 + 10, size.height / 2 + 10)); + d_pyr.getLayer(d_dst, dstSize); TEST_CYCLE() { - pyr.getLayer(dst, cv::Size(size.width / 2 + 10, size.height / 2 + 10)); + d_pyr.getLayer(d_dst, dstSize); } } -INSTANTIATE_TEST_CASE_P(ImgProc, ImagePyramid_getLayer, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - ////////////////////////////////////////////////////////////////////// // HoughLines -IMPLEMENT_PARAM_CLASS(DoSort, bool) +DEF_PARAM_TEST(Sz_DoSort, cv::Size, bool); -GPU_PERF_TEST(HoughLines, cv::gpu::DeviceInfo, cv::Size, DoSort) +PERF_TEST_P(Sz_DoSort, ImgProc_HoughLines, Combine(GPU_TYPICAL_MAT_SIZES, Bool())) { declare.time(30.0); - const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - const cv::Size size = GET_PARAM(1); - const bool doSort = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const bool doSort = GET_PARAM(1); const float rho = 1.0f; const float theta = CV_PI / 180.0f; @@ -1365,6 +1201,7 @@ GPU_PERF_TEST(HoughLines, cv::gpu::DeviceInfo, cv::Size, DoSort) cv::gpu::GpuMat d_lines; cv::gpu::GpuMat d_accum; cv::gpu::GpuMat d_buf; + cv::gpu::HoughLines(d_src, d_lines, d_accum, d_buf, rho, theta, threshold, doSort); TEST_CYCLE() @@ -1373,9 +1210,4 @@ GPU_PERF_TEST(HoughLines, cv::gpu::DeviceInfo, cv::Size, DoSort) } } -INSTANTIATE_TEST_CASE_P(ImgProc, HoughLines, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DoSort(false), DoSort(true)))); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_labeling.cpp b/modules/gpu/perf/perf_labeling.cpp index 2c537ebbeb..86d67ff93b 100644 --- a/modules/gpu/perf/perf_labeling.cpp +++ b/modules/gpu/perf/perf_labeling.cpp @@ -41,14 +41,16 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; -GPU_PERF_TEST(ConnectedComponents, cv::gpu::DeviceInfo, cv::Size) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +namespace { + +DEF_PARAM_TEST_1(Image, string); - cv::Mat image = readImage("gpu/labeling/aloe-disp.png", cv::IMREAD_GRAYSCALE); +PERF_TEST_P(Image, Labeling_ConnectedComponents, Values("gpu/labeling/aloe-disp.png")) +{ + cv::Mat image = readImage(GetParam(), cv::IMREAD_GRAYSCALE); // cv::threshold(image, image, 150, 255, CV_THRESH_BINARY); @@ -70,6 +72,4 @@ GPU_PERF_TEST(ConnectedComponents, cv::gpu::DeviceInfo, cv::Size) } } -INSTANTIATE_TEST_CASE_P(Labeling, ConnectedComponents, testing::Combine(ALL_DEVICES, testing::Values(cv::Size(261, 262)))); - -#endif \ No newline at end of file +} // namespace diff --git a/modules/gpu/perf/perf_main.cpp b/modules/gpu/perf/perf_main.cpp index 0cd4002775..988c95ae0e 100644 --- a/modules/gpu/perf/perf_main.cpp +++ b/modules/gpu/perf/perf_main.cpp @@ -2,11 +2,98 @@ #ifdef HAVE_CUDA +using namespace std; +using namespace cv; +using namespace cv::gpu; +using namespace cvtest; +using namespace testing; + +void printInfo() +{ +#if defined _WIN32 +# if defined _WIN64 + puts("OS: Windows x64"); +# else + puts("OS: Windows x32"); +# endif +#elif defined linux +# if defined _LP64 + puts("OS: Linux x64"); +# else + puts("OS: Linux x32"); +# endif +#elif defined __APPLE__ +# if defined _LP64 + puts("OS: Apple x64"); +# else + puts("OS: Apple x32"); +# endif +#endif + + int driver; + cudaDriverGetVersion(&driver); + + printf("CUDA Driver version: %d\n", driver); + printf("CUDA Runtime version: %d\n", CUDART_VERSION); + + puts("GPU module was compiled for the following GPU archs:"); + printf(" BIN: %s\n", CUDA_ARCH_BIN); + printf(" PTX: %s\n\n", CUDA_ARCH_PTX); + + int deviceCount = getCudaEnabledDeviceCount(); + printf("CUDA device count: %d\n\n", deviceCount); + + for (int i = 0; i < deviceCount; ++i) + { + DeviceInfo info(i); + + printf("Device %d:\n", i); + printf(" Name: %s\n", info.name().c_str()); + printf(" Compute capability version: %d.%d\n", info.majorVersion(), info.minorVersion()); + printf(" Multi Processor Count: %d\n", info.multiProcessorCount()); + printf(" Total memory: %d Mb\n", static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0)); + printf(" Free memory: %d Mb\n", static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0)); + if (!info.isCompatible()) + puts(" !!! This device is NOT compatible with current GPU module build\n"); + printf("\n"); + } +} + int main(int argc, char **argv) { + CommandLineParser parser(argc, (const char**)argv, + "{ print_info_only | print_info_only | false | Print information about system and exit }" + "{ device | device | 0 | Device on which tests will be executed }"); + + printInfo(); + + if (parser.get("print_info_only")) + return 0; + + int device = parser.get("device"); + + if (device < 0 || device >= getCudaEnabledDeviceCount()) + { + cerr << "Incorrect device number - " << device << endl; + return -1; + } + + DeviceInfo info(device); + if (!info.isCompatible()) + { + cerr << "Device " << device << " [" << info.name() << "] is NOT compatible with current GPU module build" << endl; + return -1; + } + + std::cout << "Run tests on device " << device << '\n' << std::endl; + + setDevice(device); + testing::InitGoogleTest(&argc, argv); perf::TestBase::Init(argc, argv); return RUN_ALL_TESTS(); + + return 0; } #else diff --git a/modules/gpu/perf/perf_matop.cpp b/modules/gpu/perf/perf_matop.cpp index 5cc24402c0..c014b19da8 100644 --- a/modules/gpu/perf/perf_matop.cpp +++ b/modules/gpu/perf/perf_matop.cpp @@ -1,141 +1,115 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////////////////////// // SetTo -GPU_PERF_TEST(SetTo, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, MatOp_SetTo, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F, CV_64F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::gpu::GpuMat src(size, type); cv::Scalar val(1, 2, 3, 4); - src.setTo(val); + cv::gpu::GpuMat d_src(size, type); + + d_src.setTo(val); TEST_CYCLE() { - src.setTo(val); + d_src.setTo(val); } } -INSTANTIATE_TEST_CASE_P(MatOp, SetTo, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - ////////////////////////////////////////////////////////////////////// // SetToMasked -GPU_PERF_TEST(SetToMasked, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, MatOp_SetToMasked, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F, CV_64F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat mask_host(size, CV_8UC1); - fill(mask_host, 0, 2); + cv::Mat mask(size, CV_8UC1); + fillRandom(mask, 0, 2); - cv::gpu::GpuMat src(src_host); cv::Scalar val(1, 2, 3, 4); - cv::gpu::GpuMat mask(mask_host); - src.setTo(val, mask); + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_mask(mask); + + d_src.setTo(val, d_mask); TEST_CYCLE() { - src.setTo(val, mask); + d_src.setTo(val, d_mask); } } -INSTANTIATE_TEST_CASE_P(MatOp, SetToMasked, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - ////////////////////////////////////////////////////////////////////// // CopyToMasked -GPU_PERF_TEST(CopyToMasked, cv::gpu::DeviceInfo, cv::Size, MatType) +PERF_TEST_P(Sz_Depth_Cn, MatOp_CopyToMasked, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F, CV_64F), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Size size = GET_PARAM(0); + int depth = GET_PARAM(1); + int channels = GET_PARAM(2); - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); + int type = CV_MAKE_TYPE(depth, channels); - cv::Mat src_host(size, type); - fill(src_host, 0, 255); + cv::Mat src(size, type); + fillRandom(src); - cv::Mat mask_host(size, CV_8UC1); - fill(mask_host, 0, 2); + cv::Mat mask(size, CV_8UC1); + fillRandom(mask, 0, 2); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat mask(mask_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_mask(mask); + cv::gpu::GpuMat d_dst; - src.copyTo(dst, mask); + d_src.copyTo(d_dst, d_mask); TEST_CYCLE() { - src.copyTo(dst, mask); + d_src.copyTo(d_dst, d_mask); } } -INSTANTIATE_TEST_CASE_P(MatOp, CopyToMasked, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - ////////////////////////////////////////////////////////////////////// // ConvertTo -GPU_PERF_TEST(ConvertTo, cv::gpu::DeviceInfo, cv::Size, MatDepth, MatDepth) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Sz_2Depth, cv::Size, MatDepth, MatDepth); - cv::Size size = GET_PARAM(1); - int depth1 = GET_PARAM(2); - int depth2 = GET_PARAM(3); +PERF_TEST_P(Sz_2Depth, MatOp_ConvertTo, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F, CV_64F), Values(CV_8U, CV_16U, CV_32F, CV_64F))) +{ + cv::Size size = GET_PARAM(0); + int depth1 = GET_PARAM(1); + int depth2 = GET_PARAM(2); - cv::Mat src_host(size, depth1); - fill(src_host, 0, 255); + cv::Mat src(size, depth1); + fillRandom(src); - cv::gpu::GpuMat src(src_host); - cv::gpu::GpuMat dst; + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - src.convertTo(dst, depth2, 0.5, 1.0); + d_src.convertTo(d_dst, depth2, 0.5, 1.0); TEST_CYCLE() { - src.convertTo(dst, depth2, 0.5, 1.0); + d_src.convertTo(d_dst, depth2, 0.5, 1.0); } } -INSTANTIATE_TEST_CASE_P(MatOp, ConvertTo, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)), - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)))); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_objdetect.cpp b/modules/gpu/perf/perf_objdetect.cpp index c376586c9b..9c1f7919f9 100644 --- a/modules/gpu/perf/perf_objdetect.cpp +++ b/modules/gpu/perf/perf_objdetect.cpp @@ -1,85 +1,84 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { /////////////////////////////////////////////////////////////// // HOG -GPU_PERF_TEST_1(HOG, cv::gpu::DeviceInfo) -{ - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST_1(Image, string); - cv::Mat img_host = readImage("gpu/hog/road.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); +PERF_TEST_P(Image, ObjDetect_HOG, Values("gpu/hog/road.png")) +{ + cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::gpu::GpuMat img(img_host); std::vector found_locations; - cv::gpu::HOGDescriptor hog; - hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); + cv::gpu::GpuMat d_img(img); - hog.detectMultiScale(img, found_locations); + cv::gpu::HOGDescriptor d_hog; + d_hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); + + d_hog.detectMultiScale(d_img, found_locations); TEST_CYCLE() { - hog.detectMultiScale(img, found_locations); + d_hog.detectMultiScale(d_img, found_locations); } } -INSTANTIATE_TEST_CASE_P(ObjDetect, HOG, ALL_DEVICES); - /////////////////////////////////////////////////////////////// // HaarClassifier -GPU_PERF_TEST_1(HaarClassifier, cv::gpu::DeviceInfo) -{ - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); +typedef pair pair_string; +DEF_PARAM_TEST_1(ImageAndCascade, pair_string); - cv::Mat img_host = readImage("gpu/haarcascade/group_1_640x480_VGA.pgm", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); +PERF_TEST_P(ImageAndCascade, ObjDetect_HaarClassifier, + Values(make_pair("gpu/haarcascade/group_1_640x480_VGA.pgm", "gpu/perf/haarcascade_frontalface_alt.xml"))) +{ + cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::gpu::CascadeClassifier_GPU cascade; + cv::gpu::CascadeClassifier_GPU d_cascade; - ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/perf/haarcascade_frontalface_alt.xml"))); + ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat objects_buffer; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_objects_buffer; - cascade.detectMultiScale(img, objects_buffer); + d_cascade.detectMultiScale(d_img, d_objects_buffer); TEST_CYCLE() { - cascade.detectMultiScale(img, objects_buffer); + d_cascade.detectMultiScale(d_img, d_objects_buffer); } } -INSTANTIATE_TEST_CASE_P(ObjDetect, HaarClassifier, ALL_DEVICES); +/////////////////////////////////////////////////////////////// +// LBP cascade -//===================== LBP cascade ==========================// -GPU_PERF_TEST_1(LBPClassifier, cv::gpu::DeviceInfo) +PERF_TEST_P(ImageAndCascade, ObjDetect_LBPClassifier, + Values(make_pair("gpu/haarcascade/group_1_640x480_VGA.pgm", "gpu/lbpcascade/lbpcascade_frontalface.xml"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(img.empty()); - cv::Mat img_host = readImage("gpu/haarcascade/group_1_640x480_VGA.pgm", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_host.empty()); + cv::gpu::CascadeClassifier_GPU d_cascade; + ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_gpu_rects; - cv::gpu::GpuMat img(img_host); - cv::gpu::GpuMat gpu_rects; - cv::gpu::CascadeClassifier_GPU cascade; - ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/lbpcascade/lbpcascade_frontalface.xml"))); + d_cascade.detectMultiScale(d_img, d_gpu_rects); - cascade.detectMultiScale(img, gpu_rects); TEST_CYCLE() { - cascade.detectMultiScale(img, gpu_rects); + d_cascade.detectMultiScale(d_img, d_gpu_rects); } } -INSTANTIATE_TEST_CASE_P(ObjDetect, LBPClassifier, ALL_DEVICES); - -#endif +} // namespace diff --git a/modules/gpu/perf/perf_precomp.hpp b/modules/gpu/perf/perf_precomp.hpp index 19608bde10..be3f234a35 100644 --- a/modules/gpu/perf/perf_precomp.hpp +++ b/modules/gpu/perf/perf_precomp.hpp @@ -11,6 +11,10 @@ #include "cvconfig.h" +#ifdef HAVE_CUDA +#include +#endif + #include "opencv2/ts/ts.hpp" #include "opencv2/ts/ts_perf.hpp" @@ -18,6 +22,10 @@ #include "opencv2/highgui/highgui.hpp" #include "opencv2/gpu/gpu.hpp" #include "opencv2/calib3d/calib3d.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/video/video.hpp" +#include "opencv2/nonfree/nonfree.hpp" +#include "opencv2/legacy/legacy.hpp" #include "perf_utility.hpp" diff --git a/modules/gpu/perf/perf_utility.cpp b/modules/gpu/perf/perf_utility.cpp index bf6bdfe739..42862f49d1 100644 --- a/modules/gpu/perf/perf_utility.cpp +++ b/modules/gpu/perf/perf_utility.cpp @@ -4,12 +4,17 @@ using namespace std; using namespace cv; using namespace cv::gpu; -void fill(Mat& m, double a, double b) +void fillRandom(Mat& m, double a, double b) { RNG rng(123456789); rng.fill(m, RNG::UNIFORM, Scalar::all(a), Scalar::all(b)); } +Mat readImage(const string& fileName, int flags) +{ + return imread(perf::TestBase::getDataPath(fileName), flags); +} + void PrintTo(const CvtColorInfo& info, ostream* os) { static const char* str[] = @@ -184,37 +189,3 @@ void PrintTo(const CvtColorInfo& info, ostream* os) *os << str[info.code]; } - -void cv::gpu::PrintTo(const DeviceInfo& info, ostream* os) -{ - *os << info.name(); -} - -Mat readImage(const string& fileName, int flags) -{ - return imread(perf::TestBase::getDataPath(fileName), flags); -} - -const vector& devices() -{ - static vector devs; - static bool first = true; - - if (first) - { - int deviceCount = getCudaEnabledDeviceCount(); - - devs.reserve(deviceCount); - - for (int i = 0; i < deviceCount; ++i) - { - DeviceInfo info(i); - if (info.isCompatible()) - devs.push_back(info); - } - - first = false; - } - - return devs; -} diff --git a/modules/gpu/perf/perf_utility.hpp b/modules/gpu/perf/perf_utility.hpp index 8693cfc3c0..b717030ff3 100644 --- a/modules/gpu/perf/perf_utility.hpp +++ b/modules/gpu/perf/perf_utility.hpp @@ -1,13 +1,21 @@ #ifndef __OPENCV_PERF_GPU_UTILITY_HPP__ #define __OPENCV_PERF_GPU_UTILITY_HPP__ -void fill(cv::Mat& m, double a, double b); +#include "opencv2/core/core.hpp" +#include "opencv2/core/gpumat.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "opencv2/ts/ts_perf.hpp" + +void fillRandom(cv::Mat& m, double a = 0.0, double b = 255.0); +cv::Mat readImage(const std::string& fileName, int flags = cv::IMREAD_COLOR); using perf::MatType; using perf::MatDepth; CV_ENUM(BorderMode, cv::BORDER_REFLECT101, cv::BORDER_REPLICATE, cv::BORDER_CONSTANT, cv::BORDER_REFLECT, cv::BORDER_WRAP) - CV_ENUM(Interpolation, cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_CUBIC, cv::INTER_AREA) +#define ALL_BORDER_MODES testing::ValuesIn(BorderMode::all()) +CV_ENUM(Interpolation, cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_CUBIC, cv::INTER_AREA) +#define ALL_INTERPOLATIONS testing::ValuesIn(Interpolation::all()) CV_ENUM(NormType, cv::NORM_INF, cv::NORM_L1, cv::NORM_L2, cv::NORM_HAMMING) struct CvtColorInfo @@ -18,60 +26,18 @@ struct CvtColorInfo explicit CvtColorInfo(int scn_=0, int dcn_=0, int code_=0) : scn(scn_), dcn(dcn_), code(code_) {} }; - void PrintTo(const CvtColorInfo& info, std::ostream* os); -#define IMPLEMENT_PARAM_CLASS(name, type) \ - class name \ - { \ - public: \ - name ( type arg = type ()) : val_(arg) {} \ - operator type () const {return val_;} \ - private: \ - type val_; \ - }; \ - inline void PrintTo( name param, std::ostream* os) \ - { \ - *os << #name << " = " << testing::PrintToString(static_cast< type >(param)); \ - } - -IMPLEMENT_PARAM_CLASS(Channels, int) - -namespace cv { namespace gpu -{ - void PrintTo(const cv::gpu::DeviceInfo& info, std::ostream* os); -}} - -#define GPU_PERF_TEST(name, ...) \ - struct name : perf::TestBaseWithParam< std::tr1::tuple< __VA_ARGS__ > > \ - { \ - public: \ - name() {} \ - protected: \ - void PerfTestBody(); \ - }; \ - TEST_P(name, perf){ RunPerfTestBody(); } \ - void name :: PerfTestBody() - -#define GPU_PERF_TEST_1(name, param_type) \ - struct name : perf::TestBaseWithParam< param_type > \ - { \ - public: \ - name() {} \ - protected: \ - void PerfTestBody(); \ - }; \ - TEST_P(name, perf){ RunPerfTestBody(); } \ - void name :: PerfTestBody() - -#define GPU_TYPICAL_MAT_SIZES testing::Values(perf::szSXGA, perf::sz1080p, cv::Size(1800, 1500)) - -cv::Mat readImage(const std::string& fileName, int flags = cv::IMREAD_COLOR); +#define GET_PARAM(k) std::tr1::get< k >(GetParam()) -const std::vector& devices(); +#define DEF_PARAM_TEST(name, ...) typedef ::perf::TestBaseWithParam< std::tr1::tuple< __VA_ARGS__ > > name +#define DEF_PARAM_TEST_1(name, param_type) typedef ::perf::TestBaseWithParam< param_type > name -#define ALL_DEVICES testing::ValuesIn(devices()) +DEF_PARAM_TEST_1(Sz, cv::Size); +typedef perf::Size_MatType Sz_Type; +DEF_PARAM_TEST(Sz_Depth, cv::Size, MatDepth); +DEF_PARAM_TEST(Sz_Depth_Cn, cv::Size, MatDepth, int); -#define GET_PARAM(k) std::tr1::get< k >(GetParam()) +#define GPU_TYPICAL_MAT_SIZES testing::Values(perf::szSXGA, perf::sz720p, perf::sz1080p) #endif // __OPENCV_PERF_GPU_UTILITY_HPP__ diff --git a/modules/gpu/perf/perf_video.cpp b/modules/gpu/perf/perf_video.cpp index 6e577a4a40..a5a1e4da57 100644 --- a/modules/gpu/perf/perf_video.cpp +++ b/modules/gpu/perf/perf_video.cpp @@ -1,305 +1,277 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA +using namespace std; +using namespace testing; + +namespace { ////////////////////////////////////////////////////// // BroxOpticalFlow -GPU_PERF_TEST_1(BroxOpticalFlow, cv::gpu::DeviceInfo) +typedef pair pair_string; + +DEF_PARAM_TEST_1(ImagePair, pair_string); + +PERF_TEST_P(ImagePair, Video_BroxOpticalFlow, Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(10); - cv::Mat frame0_host = readImage("gpu/opticalflow/frame0.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0_host.empty()); + cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame0.empty()); - cv::Mat frame1_host = readImage("gpu/opticalflow/frame1.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1_host.empty()); + cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame1.empty()); - frame0_host.convertTo(frame0_host, CV_32FC1, 1.0 / 255.0); - frame1_host.convertTo(frame1_host, CV_32FC1, 1.0 / 255.0); + frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); + frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat u; - cv::gpu::GpuMat v; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(frame0, frame1, u, v); - - declare.time(10); + d_flow(d_frame0, d_frame1, d_u, d_v); TEST_CYCLE() { - d_flow(frame0, frame1, u, v); + d_flow(d_frame0, d_frame1, d_u, d_v); } } -INSTANTIATE_TEST_CASE_P(Video, BroxOpticalFlow, ALL_DEVICES); - ////////////////////////////////////////////////////// // InterpolateFrames -GPU_PERF_TEST_1(InterpolateFrames, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Video_InterpolateFrames, Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); + cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame0.empty()); - cv::Mat frame0_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0_host.empty()); + cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame1.empty()); - cv::Mat frame1_host = readImage("gpu/perf/aloeR.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1_host.empty()); + frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); + frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - frame0_host.convertTo(frame0_host, CV_32FC1, 1.0 / 255.0); - frame1_host.convertTo(frame1_host, CV_32FC1, 1.0 / 255.0); - - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat fu, fv; - cv::gpu::GpuMat bu, bv; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_fu, d_fv; + cv::gpu::GpuMat d_bu, d_bv; cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(frame0, frame1, fu, fv); - d_flow(frame1, frame0, bu, bv); + d_flow(d_frame0, d_frame1, d_fu, d_fv); + d_flow(d_frame1, d_frame0, d_bu, d_bv); - cv::gpu::GpuMat newFrame; - cv::gpu::GpuMat buf; + cv::gpu::GpuMat d_newFrame; + cv::gpu::GpuMat d_buf; - cv::gpu::interpolateFrames(frame0, frame1, fu, fv, bu, bv, 0.5f, newFrame, buf); + cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); TEST_CYCLE() { - cv::gpu::interpolateFrames(frame0, frame1, fu, fv, bu, bv, 0.5f, newFrame, buf); + cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); } } -INSTANTIATE_TEST_CASE_P(Video, InterpolateFrames, ALL_DEVICES); - ////////////////////////////////////////////////////// // CreateOpticalFlowNeedleMap -GPU_PERF_TEST_1(CreateOpticalFlowNeedleMap, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Video_CreateOpticalFlowNeedleMap, Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Mat frame0_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0_host.empty()); + cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame0.empty()); - cv::Mat frame1_host = readImage("gpu/perf/aloeR.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1_host.empty()); + cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame1.empty()); - frame0_host.convertTo(frame0_host, CV_32FC1, 1.0 / 255.0); - frame1_host.convertTo(frame1_host, CV_32FC1, 1.0 / 255.0); + frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); + frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat u, v; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(frame0, frame1, u, v); + d_flow(d_frame0, d_frame1, d_u, d_v); - cv::gpu::GpuMat vertex, colors; + cv::gpu::GpuMat d_vertex, d_colors; - cv::gpu::createOpticalFlowNeedleMap(u, v, vertex, colors); + cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); TEST_CYCLE() { - cv::gpu::createOpticalFlowNeedleMap(u, v, vertex, colors); + cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); } } -INSTANTIATE_TEST_CASE_P(Video, CreateOpticalFlowNeedleMap, ALL_DEVICES); - ////////////////////////////////////////////////////// // GoodFeaturesToTrack -IMPLEMENT_PARAM_CLASS(MinDistance, double) +DEF_PARAM_TEST(Image_MinDistance, string, double); -GPU_PERF_TEST(GoodFeaturesToTrack, cv::gpu::DeviceInfo, MinDistance) +PERF_TEST_P(Image_MinDistance, Video_GoodFeaturesToTrack, Combine(Values("gpu/perf/aloe.jpg"), Values(0.0, 3.0))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - + string fileName = GET_PARAM(0); double minDistance = GET_PARAM(1); - cv::Mat image_host = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(image_host.empty()); + cv::Mat image = readImage(fileName, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(image.empty()); - cv::gpu::GoodFeaturesToTrackDetector_GPU detector(8000, 0.01, minDistance); + cv::gpu::GoodFeaturesToTrackDetector_GPU d_detector(8000, 0.01, minDistance); - cv::gpu::GpuMat image(image_host); - cv::gpu::GpuMat pts; + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_pts; - detector(image, pts); + d_detector(d_image, d_pts); TEST_CYCLE() { - detector(image, pts); + d_detector(d_image, d_pts); } } -INSTANTIATE_TEST_CASE_P(Video, GoodFeaturesToTrack, testing::Combine( - ALL_DEVICES, - testing::Values(MinDistance(0.0), MinDistance(3.0)))); - ////////////////////////////////////////////////////// // PyrLKOpticalFlowSparse -IMPLEMENT_PARAM_CLASS(GraySource, bool) -IMPLEMENT_PARAM_CLASS(Points, int) -IMPLEMENT_PARAM_CLASS(WinSize, int) -IMPLEMENT_PARAM_CLASS(Levels, int) -IMPLEMENT_PARAM_CLASS(Iters, int) +DEF_PARAM_TEST(ImagePair_Gray_NPts_WinSz_Levels_Iters, pair_string, bool, int, int, int, int); -GPU_PERF_TEST(PyrLKOpticalFlowSparse, cv::gpu::DeviceInfo, GraySource, Points, WinSize, Levels, Iters) +PERF_TEST_P(ImagePair_Gray_NPts_WinSz_Levels_Iters, Video_PyrLKOpticalFlowSparse, Combine( + Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")), + Bool(), + Values(1000, 2000, 4000, 8000), + Values(9, 13, 17, 21), + Values(1, 2, 3), + Values(1, 10, 30))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - + pair_string imagePair = GET_PARAM(0); bool useGray = GET_PARAM(1); int points = GET_PARAM(2); int winSize = GET_PARAM(3); int levels = GET_PARAM(4); int iters = GET_PARAM(5); - cv::Mat frame0_host = readImage("gpu/opticalflow/frame0.png", useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); - ASSERT_FALSE(frame0_host.empty()); + cv::Mat frame0 = readImage(imagePair.first, useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); + ASSERT_FALSE(frame0.empty()); - cv::Mat frame1_host = readImage("gpu/opticalflow/frame1.png", useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); - ASSERT_FALSE(frame1_host.empty()); + cv::Mat frame1 = readImage(imagePair.second, useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); + ASSERT_FALSE(frame1.empty()); cv::Mat gray_frame; if (useGray) - gray_frame = frame0_host; + gray_frame = frame0; else - cv::cvtColor(frame0_host, gray_frame, cv::COLOR_BGR2GRAY); + cv::cvtColor(frame0, gray_frame, cv::COLOR_BGR2GRAY); - cv::gpu::GpuMat pts; + cv::gpu::GpuMat d_pts; - cv::gpu::GoodFeaturesToTrackDetector_GPU detector(points, 0.01, 0.0); - detector(cv::gpu::GpuMat(gray_frame), pts); + cv::gpu::GoodFeaturesToTrackDetector_GPU d_detector(points, 0.01, 0.0); + d_detector(cv::gpu::GpuMat(gray_frame), d_pts); - cv::gpu::PyrLKOpticalFlow pyrLK; - pyrLK.winSize = cv::Size(winSize, winSize); - pyrLK.maxLevel = levels - 1; - pyrLK.iters = iters; + cv::gpu::PyrLKOpticalFlow d_pyrLK; + d_pyrLK.winSize = cv::Size(winSize, winSize); + d_pyrLK.maxLevel = levels - 1; + d_pyrLK.iters = iters; - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat nextPts; - cv::gpu::GpuMat status; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_nextPts; + cv::gpu::GpuMat d_status; - pyrLK.sparse(frame0, frame1, pts, nextPts, status); + d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); TEST_CYCLE() { - pyrLK.sparse(frame0, frame1, pts, nextPts, status); + d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); } } -INSTANTIATE_TEST_CASE_P(Video, PyrLKOpticalFlowSparse, testing::Combine( - ALL_DEVICES, - testing::Values(GraySource(true), GraySource(false)), - testing::Values(Points(1000), Points(2000), Points(4000), Points(8000)), - testing::Values(WinSize(9), WinSize(13), WinSize(17), WinSize(21)), - testing::Values(Levels(1), Levels(2), Levels(3)), - testing::Values(Iters(1), Iters(10), Iters(30)))); - ////////////////////////////////////////////////////// // PyrLKOpticalFlowDense -GPU_PERF_TEST(PyrLKOpticalFlowDense, cv::gpu::DeviceInfo, WinSize, Levels, Iters) +DEF_PARAM_TEST(ImagePair_WinSz_Levels_Iters, pair_string, int, int, int); + +PERF_TEST_P(ImagePair_WinSz_Levels_Iters, Video_PyrLKOpticalFlowDense, Combine( + Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png")), + Values(3, 5, 7, 9, 13, 17, 21), + Values(1, 2, 3), + Values(1, 10))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(30); + pair_string imagePair = GET_PARAM(0); int winSize = GET_PARAM(1); int levels = GET_PARAM(2); int iters = GET_PARAM(3); - cv::Mat frame0_host = readImage("gpu/opticalflow/frame0.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0_host.empty()); + cv::Mat frame0 = readImage(imagePair.first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame0.empty()); - cv::Mat frame1_host = readImage("gpu/opticalflow/frame1.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1_host.empty()); + cv::Mat frame1 = readImage(imagePair.second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame1.empty()); - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat u; - cv::gpu::GpuMat v; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; - cv::gpu::PyrLKOpticalFlow pyrLK; + cv::gpu::PyrLKOpticalFlow d_pyrLK; + d_pyrLK.winSize = cv::Size(winSize, winSize); + d_pyrLK.maxLevel = levels - 1; + d_pyrLK.iters = iters; - pyrLK.winSize = cv::Size(winSize, winSize); - pyrLK.maxLevel = levels - 1; - pyrLK.iters = iters; - - pyrLK.dense(frame0, frame1, u, v); - - declare.time(30); + d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); TEST_CYCLE() { - pyrLK.dense(frame0, frame1, u, v); + d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); } } -INSTANTIATE_TEST_CASE_P(Video, PyrLKOpticalFlowDense, testing::Combine( - ALL_DEVICES, - testing::Values(WinSize(3), WinSize(5), WinSize(7), WinSize(9), WinSize(13), WinSize(17), WinSize(21)), - testing::Values(Levels(1), Levels(2), Levels(3)), - testing::Values(Iters(1), Iters(10)))); - ////////////////////////////////////////////////////// -// FarnebackOpticalFlowTest +// FarnebackOpticalFlow -GPU_PERF_TEST_1(FarnebackOpticalFlowTest, cv::gpu::DeviceInfo) +PERF_TEST_P(ImagePair, Video_FarnebackOpticalFlow, Values(make_pair("gpu/opticalflow/frame0.png", "gpu/opticalflow/frame1.png"))) { - cv::gpu::DeviceInfo devInfo = GetParam(); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Mat frame0_host = readImage("gpu/opticalflow/frame0.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0_host.empty()); + declare.time(10); - cv::Mat frame1_host = readImage("gpu/opticalflow/frame1.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1_host.empty()); + cv::Mat frame0 = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame0.empty()); - cv::gpu::GpuMat frame0(frame0_host); - cv::gpu::GpuMat frame1(frame1_host); - cv::gpu::GpuMat u; - cv::gpu::GpuMat v; + cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + ASSERT_FALSE(frame1.empty()); - cv::gpu::FarnebackOpticalFlow farneback; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; - farneback(frame0, frame1, u, v); + cv::gpu::FarnebackOpticalFlow d_farneback; - declare.time(10); + d_farneback(d_frame0, d_frame1, d_u, d_v); TEST_CYCLE() { - farneback(frame0, frame1, u, v); + d_farneback(d_frame0, d_frame1, d_u, d_v); } } -INSTANTIATE_TEST_CASE_P(Video, FarnebackOpticalFlowTest, ALL_DEVICES); - ////////////////////////////////////////////////////// // FGDStatModel -GPU_PERF_TEST(FGDStatModel, cv::gpu::DeviceInfo, std::string) +DEF_PARAM_TEST_1(Video, string); + +PERF_TEST_P(Video, Video_FGDStatModel, Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi")) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + declare.time(10); - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); + string inputFile = perf::TestBase::getDataPath(GetParam()); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -312,8 +284,6 @@ GPU_PERF_TEST(FGDStatModel, cv::gpu::DeviceInfo, std::string) cv::gpu::FGDStatModel d_model(4); d_model.create(d_frame); - declare.time(10); - for (int i = 0; i < 10; ++i) { cap >> frame; @@ -327,23 +297,16 @@ GPU_PERF_TEST(FGDStatModel, cv::gpu::DeviceInfo, std::string) } } -INSTANTIATE_TEST_CASE_P(Video, FGDStatModel, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - ////////////////////////////////////////////////////// // MOG -IMPLEMENT_PARAM_CLASS(LearningRate, double) +DEF_PARAM_TEST(Video_Cn_LearningRate, string, int, double); -GPU_PERF_TEST(MOG, cv::gpu::DeviceInfo, std::string, Channels, LearningRate) +PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1, 3, 4), Values(0.0, 0.01))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - double learningRate = GET_PARAM(3); + string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + int cn = GET_PARAM(1); + double learningRate = GET_PARAM(2); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -351,8 +314,8 @@ GPU_PERF_TEST(MOG, cv::gpu::DeviceInfo, std::string, Channels, LearningRate) cv::Mat frame; cv::gpu::GpuMat d_frame; - cv::gpu::MOG_GPU mog; - cv::gpu::GpuMat foreground; + cv::gpu::MOG_GPU d_mog; + cv::gpu::GpuMat d_foreground; cap >> frame; ASSERT_FALSE(frame.empty()); @@ -369,7 +332,7 @@ GPU_PERF_TEST(MOG, cv::gpu::DeviceInfo, std::string, Channels, LearningRate) d_frame.upload(frame); - mog(d_frame, foreground, learningRate); + d_mog(d_frame, d_foreground, learningRate); for (int i = 0; i < 10; ++i) { @@ -389,27 +352,20 @@ GPU_PERF_TEST(MOG, cv::gpu::DeviceInfo, std::string, Channels, LearningRate) d_frame.upload(frame); startTimer(); next(); - mog(d_frame, foreground, learningRate); + d_mog(d_frame, d_foreground, learningRate); stopTimer(); } } -INSTANTIATE_TEST_CASE_P(Video, MOG, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)), - testing::Values(LearningRate(0.0), LearningRate(0.01)))); - ////////////////////////////////////////////////////// // MOG2 -GPU_PERF_TEST(MOG2_update, cv::gpu::DeviceInfo, std::string, Channels) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); +DEF_PARAM_TEST(Video_Cn, string, int); - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); +PERF_TEST_P(Video_Cn, Video_MOG2, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1, 3, 4))) +{ + string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + int cn = GET_PARAM(1); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -417,8 +373,8 @@ GPU_PERF_TEST(MOG2_update, cv::gpu::DeviceInfo, std::string, Channels) cv::Mat frame; cv::gpu::GpuMat d_frame; - cv::gpu::MOG2_GPU mog2; - cv::gpu::GpuMat foreground; + cv::gpu::MOG2_GPU d_mog2; + cv::gpu::GpuMat d_foreground; cap >> frame; ASSERT_FALSE(frame.empty()); @@ -435,7 +391,7 @@ GPU_PERF_TEST(MOG2_update, cv::gpu::DeviceInfo, std::string, Channels) d_frame.upload(frame); - mog2(d_frame, foreground); + d_mog2(d_frame, d_foreground); for (int i = 0; i < 10; ++i) { @@ -455,23 +411,18 @@ GPU_PERF_TEST(MOG2_update, cv::gpu::DeviceInfo, std::string, Channels) d_frame.upload(frame); startTimer(); next(); - mog2(d_frame, foreground); + d_mog2(d_frame, d_foreground); stopTimer(); } } -INSTANTIATE_TEST_CASE_P(Video, MOG2_update, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)))); +////////////////////////////////////////////////////// +// MOG2GetBackgroundImage -GPU_PERF_TEST(MOG2_getBackgroundImage, cv::gpu::DeviceInfo, std::string, Channels) +PERF_TEST_P(Video_Cn, Video_MOG2GetBackgroundImage, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); + string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + int cn = GET_PARAM(1); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -479,8 +430,8 @@ GPU_PERF_TEST(MOG2_getBackgroundImage, cv::gpu::DeviceInfo, std::string, Channel cv::Mat frame; cv::gpu::GpuMat d_frame; - cv::gpu::MOG2_GPU mog2; - cv::gpu::GpuMat foreground; + cv::gpu::MOG2_GPU d_mog2; + cv::gpu::GpuMat d_foreground; for (int i = 0; i < 10; ++i) { @@ -499,33 +450,25 @@ GPU_PERF_TEST(MOG2_getBackgroundImage, cv::gpu::DeviceInfo, std::string, Channel d_frame.upload(frame); - mog2(d_frame, foreground); + d_mog2(d_frame, d_foreground); } - cv::gpu::GpuMat background; - mog2.getBackgroundImage(background); + cv::gpu::GpuMat d_background; + d_mog2.getBackgroundImage(d_background); TEST_CYCLE() { - mog2.getBackgroundImage(background); + d_mog2.getBackgroundImage(d_background); } } -INSTANTIATE_TEST_CASE_P(Video, MOG2_getBackgroundImage, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)))); - ////////////////////////////////////////////////////// // VIBE -GPU_PERF_TEST(VIBE, cv::gpu::DeviceInfo, std::string, Channels) +PERF_TEST_P(Video_Cn, Video_VIBE, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1, 3, 4))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); + string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + int cn = GET_PARAM(1); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -545,10 +488,10 @@ GPU_PERF_TEST(VIBE, cv::gpu::DeviceInfo, std::string, Channels) } cv::gpu::GpuMat d_frame(frame); - cv::gpu::VIBE_GPU vibe; - cv::gpu::GpuMat foreground; + cv::gpu::VIBE_GPU d_vibe; + cv::gpu::GpuMat d_foreground; - vibe(d_frame, foreground); + d_vibe(d_frame, d_foreground); for (int i = 0; i < 10; ++i) { @@ -568,28 +511,21 @@ GPU_PERF_TEST(VIBE, cv::gpu::DeviceInfo, std::string, Channels) d_frame.upload(frame); startTimer(); next(); - vibe(d_frame, foreground); + d_vibe(d_frame, d_foreground); stopTimer(); } } -INSTANTIATE_TEST_CASE_P(Video, VIBE, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)))); - ////////////////////////////////////////////////////// // GMG -IMPLEMENT_PARAM_CLASS(MaxFeatures, int) +DEF_PARAM_TEST(Video_Cn_MaxFeatures, string, int, int); -GPU_PERF_TEST(GMG, cv::gpu::DeviceInfo, std::string, Channels, MaxFeatures) +PERF_TEST_P(Video_Cn_MaxFeatures, Video_GMG, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1, 3, 4), Values(20, 40, 60))) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - int maxFeatures = GET_PARAM(3); + std::string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); + int cn = GET_PARAM(1); + int maxFeatures = GET_PARAM(2); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); @@ -611,10 +547,10 @@ GPU_PERF_TEST(GMG, cv::gpu::DeviceInfo, std::string, Channels, MaxFeatures) cv::gpu::GpuMat d_frame(frame); cv::gpu::GpuMat d_fgmask; - cv::gpu::GMG_GPU gmg; - gmg.maxFeatures = maxFeatures; + cv::gpu::GMG_GPU d_gmg; + d_gmg.maxFeatures = maxFeatures; - gmg(d_frame, d_fgmask); + d_gmg(d_frame, d_fgmask); for (int i = 0; i < 150; ++i) { @@ -638,31 +574,20 @@ GPU_PERF_TEST(GMG, cv::gpu::DeviceInfo, std::string, Channels, MaxFeatures) d_frame.upload(frame); startTimer(); next(); - gmg(d_frame, d_fgmask); + d_gmg(d_frame, d_fgmask); stopTimer(); } } -INSTANTIATE_TEST_CASE_P(Video, GMG, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)), - testing::Values(MaxFeatures(20), MaxFeatures(40), MaxFeatures(60)))); - ////////////////////////////////////////////////////// // VideoWriter -#ifdef WIN32 - -GPU_PERF_TEST(VideoWriter, cv::gpu::DeviceInfo, std::string) +PERF_TEST_P(Video, Video_VideoWriter, Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi")) { - const double FPS = 25.0; - - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); + string inputFile = perf::TestBase::getDataPath(GetParam()); + string outputFile = cv::tempfile(".avi"); - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - std::string outputFile = cv::tempfile(".avi"); + const double FPS = 25.0; cv::VideoCapture reader(inputFile); ASSERT_TRUE( reader.isOpened() ); @@ -690,39 +615,26 @@ GPU_PERF_TEST(VideoWriter, cv::gpu::DeviceInfo, std::string) } } -INSTANTIATE_TEST_CASE_P(Video, VideoWriter, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - -#endif // WIN32 - ////////////////////////////////////////////////////// // VideoReader -GPU_PERF_TEST(VideoReader, cv::gpu::DeviceInfo, std::string) +PERF_TEST_P(Video, Video_VideoReader, Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi")) { - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); + declare.time(20); - cv::gpu::VideoReader_GPU reader(inputFile); - ASSERT_TRUE( reader.isOpened() ); + string inputFile = perf::TestBase::getDataPath(GetParam()); - cv::gpu::GpuMat frame; + cv::gpu::VideoReader_GPU d_reader(inputFile); + ASSERT_TRUE( d_reader.isOpened() ); - reader.read(frame); + cv::gpu::GpuMat d_frame; - declare.time(20); + d_reader.read(d_frame); TEST_CYCLE_N(10) { - reader.read(frame); + d_reader.read(d_frame); } } -INSTANTIATE_TEST_CASE_P(Video, VideoReader, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - -#endif +} // namespace From 362df96cb9670c1757f1aae77541010da6bbe0a4 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 15:16:45 +0400 Subject: [PATCH 03/58] renamed perf_main.cpp to main.cpp --- modules/gpu/perf/{perf_main.cpp => main.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/gpu/perf/{perf_main.cpp => main.cpp} (100%) diff --git a/modules/gpu/perf/perf_main.cpp b/modules/gpu/perf/main.cpp similarity index 100% rename from modules/gpu/perf/perf_main.cpp rename to modules/gpu/perf/main.cpp From 2634dc6cce264af42379961c64c0ac84a9ba0074 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 15:22:29 +0400 Subject: [PATCH 04/58] removed license comments --- modules/gpu/perf/perf_labeling.cpp | 41 ------------------------------ 1 file changed, 41 deletions(-) diff --git a/modules/gpu/perf/perf_labeling.cpp b/modules/gpu/perf/perf_labeling.cpp index 86d67ff93b..bd1bcf144d 100644 --- a/modules/gpu/perf/perf_labeling.cpp +++ b/modules/gpu/perf/perf_labeling.cpp @@ -1,44 +1,3 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -//M*/ - #include "perf_precomp.hpp" using namespace std; From 00546f24e006c0454dd138f8faec81891543dabe Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 15:50:35 +0400 Subject: [PATCH 05/58] updated mark_nvidia.py script --- modules/gpu/misc/mark_nvidia.py | 359 +++++++++++++++----------------- 1 file changed, 169 insertions(+), 190 deletions(-) diff --git a/modules/gpu/misc/mark_nvidia.py b/modules/gpu/misc/mark_nvidia.py index fd543e9f5f..88070117d9 100644 --- a/modules/gpu/misc/mark_nvidia.py +++ b/modules/gpu/misc/mark_nvidia.py @@ -1,255 +1,234 @@ import sys, re spaces = '[\s]*' -symbols = '[\s\w\d,.=:|]*' +symbols = '[\s\w\d,.:|]*' def pattern1(prefix, test): - return re.compile(spaces + 'perf::' + prefix + '/' + test + '::' + '\(' + symbols + '\)' + spaces) + return re.compile(spaces + prefix + '_' + test + '::' + symbols + '::' + '\(' + symbols + '\)' + spaces) -def pattern2(prefix, test, cvtype): - return re.compile(spaces + 'perf::' + prefix + '/' + test + '::' + '\(' + symbols + cvtype + symbols + '\)' + spaces) +def pattern2(prefix, test, param1): + return re.compile(spaces + prefix + '_' + test + '::' + symbols + '::' + '\(' + symbols + param1 + symbols + '\)' + spaces) -def pattern3(prefix, test, cvtype, param1): - return re.compile(spaces + 'perf::' + prefix + '/' + test + '::' + '\(' + symbols + cvtype + symbols + param1 + symbols + '\)' + spaces) +def pattern3(prefix, test, param1, param2): + return re.compile(spaces + prefix + '_' + test + '::' + symbols + '::' + '\(' + symbols + param1 + symbols + param2 + symbols + '\)' + spaces) -def pattern4(prefix, test, cvtype, param1, param2): - return re.compile(spaces + 'perf::' + prefix + '/' + test + '::' + '\(' + symbols + cvtype + symbols + param1 + symbols + param2 + symbols + '\)' + spaces) +def pattern4(prefix, test, param1, param2, param3): + return re.compile(spaces + prefix + '_' + test + '::' + symbols + '::' + '\(' + symbols + param1 + symbols + param2 + symbols + param3 + symbols + '\)' + spaces) + +def pattern5(prefix, test, param1, param2, param3, param5): + return re.compile(spaces + prefix + '_' + test + '::' + symbols + '::' + '\(' + symbols + param1 + symbols + param2 + symbols + param3 + symbols + param4 + symbols + '\)' + spaces) npp_patterns = [ ############################################################## # Core - - # Core/Add_Mat (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Add_Mat', '8U'), - pattern2('Core', 'Add_Mat', '16U'), - pattern2('Core', 'Add_Mat', '32F'), - - # Core/Add_Scalar (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Add_Scalar', '8U'), - pattern2('Core', 'Add_Scalar', '16U'), - pattern2('Core', 'Add_Scalar', '32F'), - - # Core/Subtract_Mat (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Subtract_Mat', '8U'), - pattern2('Core', 'Subtract_Mat', '16U'), - pattern2('Core', 'Subtract_Mat', '32F'), - - # Core/Subtract_Scalar (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Subtract_Scalar', '8U'), - pattern2('Core', 'Subtract_Scalar', '16U'), - pattern2('Core', 'Subtract_Scalar', '32F'), - - # Core/Multiply_Mat (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Multiply_Mat', '8U'), - pattern2('Core', 'Multiply_Mat', '16U'), - pattern2('Core', 'Multiply_Mat', '32F'), - - # Core/Multiply_Scalar (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Multiply_Scalar', '8U'), - pattern2('Core', 'Multiply_Scalar', '16U'), - pattern2('Core', 'Multiply_Scalar', '32F'), - - # Core/Divide_Mat (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Divide_Mat', '8U'), - pattern2('Core', 'Divide_Mat', '16U'), - pattern2('Core', 'Divide_Mat', '32F'), - - # Core/Divide_Scalar (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'Divide_Scalar', '8U'), - pattern2('Core', 'Divide_Scalar', '16U'), - pattern2('Core', 'Divide_Scalar', '32F'), - - # Core/AbsDiff_Mat (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'AbsDiff_Mat', '8U'), - pattern2('Core', 'AbsDiff_Mat', '16U'), - pattern2('Core', 'AbsDiff_Mat', '32F'), - - # Core/AbsDiff_Scalar (CV_8U | CV_16U | CV_32F) - pattern2('Core', 'AbsDiff_Scalar', '8U'), - pattern2('Core', 'AbsDiff_Scalar', '16U'), - pattern2('Core', 'AbsDiff_Scalar', '32F'), - - # Core/Abs + + # Core_AddMat (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'AddMat', '8U'), + pattern2('Core', 'AddMat', '16U'), + pattern2('Core', 'AddMat', '32F'), + + # Core_AddScalar (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'AddScalar', '8U'), + pattern2('Core', 'AddScalar', '16U'), + pattern2('Core', 'AddScalar', '32F'), + + # Core_SubtractMat (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'SubtractMat', '8U'), + pattern2('Core', 'SubtractMat', '16U'), + pattern2('Core', 'SubtractMat', '32F'), + + # Core_SubtractScalar (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'SubtractScalar', '8U'), + pattern2('Core', 'SubtractScalar', '16U'), + pattern2('Core', 'SubtractScalar', '32F'), + + # Core_MultiplyMat (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'MultiplyMat', '8U'), + pattern2('Core', 'MultiplyMat', '16U'), + pattern2('Core', 'MultiplyMat', '32F'), + + # Core_MultiplyScalar (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'MultiplyScalar', '8U'), + pattern2('Core', 'MultiplyScalar', '16U'), + pattern2('Core', 'MultiplyScalar', '32F'), + + # Core_DivideMat (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'DivideMat', '8U'), + pattern2('Core', 'DivideMat', '16U'), + pattern2('Core', 'DivideMat', '32F'), + + # Core_Divide_Scalar (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'DivideScalar', '8U'), + pattern2('Core', 'DivideScalar', '16U'), + pattern2('Core', 'DivideScalar', '32F'), + + # Core_AbsDiff_Mat (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'AbsDiffMat', '8U'), + pattern2('Core', 'AbsDiffMat', '16U'), + pattern2('Core', 'AbsDiffMat', '32F'), + + # Core_AbsDiffScalar (CV_8U | CV_16U | CV_32F) + pattern2('Core', 'AbsDiffScalar', '8U'), + pattern2('Core', 'AbsDiffScalar', '16U'), + pattern2('Core', 'AbsDiffScalar', '32F'), + + # Core_Abs pattern1('Core', 'Abs'), - # Core/Sqr + # Core_Sqr pattern1('Core', 'Sqr'), - # Core/Sqrt + # Core_Sqrt pattern1('Core', 'Sqrt'), - # Core/Log + # Core_Log pattern1('Core', 'Log'), - # Core/Exp + # Core_Exp pattern1('Core', 'Exp'), - # Core/Bitwise_And_Scalar - pattern1('Core', 'Bitwise_And_Scalar'), + # Core_BitwiseAndScalar + pattern1('Core', 'BitwiseAndScalar'), - # Core/Bitwise_Or_Scalar - pattern1('Core', 'Bitwise_Or_Scalar'), + # Core_BitwiseOrScalar + pattern1('Core', 'BitwiseOrScalar'), - # Core/Bitwise_Xor_Scalar - pattern1('Core', 'Bitwise_Xor_Scalar'), + # Core_BitwiseXorScalar + pattern1('Core', 'BitwiseXorScalar'), - # Core/RShift + # Core_RShift pattern1('Core', 'RShift'), - # Core/LShift + # Core_LShift pattern1('Core', 'LShift'), - # Core/Transpose + # Core_Transpose pattern1('Core', 'Transpose'), - # Core/Flip + # Core_Flip pattern1('Core', 'Flip'), - # Core/LUT_OneChannel - pattern1('Core', 'LUT_OneChannel'), + # Core_LutOneChannel + pattern1('Core', 'LutOneChannel'), - # Core/LUT_MultiChannel - pattern1('Core', 'LUT_MultiChannel'), + # Core_LutMultiChannel + pattern1('Core', 'LutMultiChannel'), - # Core/Magnitude_Complex - pattern1('Core', 'Magnitude_Complex'), + # Core_MagnitudeComplex + pattern1('Core', 'MagnitudeComplex'), - # Core/Magnitude_Sqr_Complex - pattern1('Core', 'Magnitude_Sqr_Complex'), + # Core_MagnitudeSqrComplex + pattern1('Core', 'MagnitudeSqrComplex'), - # Core/MeanStdDev + # Core_MeanStdDev pattern1('Core', 'MeanStdDev'), - # Core/NormDiff + # Core_NormDiff pattern1('Core', 'NormDiff'), - + ############################################################## # Filters - # Filters/Blur + # Filters_Blur pattern1('Filters', 'Blur'), - - # Filters/Erode + + # Filters_Erode pattern1('Filters', 'Erode'), - - # Filters/Dilate + + # Filters_Dilate pattern1('Filters', 'Dilate'), - - # Filters/MorphologyEx + + # Filters_MorphologyEx pattern1('Filters', 'MorphologyEx'), - + ############################################################## # ImgProc - - # ImgProc/Resize (8UC1 | 8UC4, INTER_NEAREST | INTER_LINEAR) - pattern3('ImgProc', 'Resize', '8UC1', 'INTER_NEAREST'), - pattern3('ImgProc', 'Resize', '8UC4', 'INTER_NEAREST'), - pattern3('ImgProc', 'Resize', '8UC1', 'INTER_LINEAR'), - pattern3('ImgProc', 'Resize', '8UC4', 'INTER_LINEAR'), - - # ImgProc/Resize (8UC4, INTER_CUBIC) - pattern3('ImgProc', 'Resize', '8UC4', 'INTER_CUBIC'), - - # ImgProc/WarpAffine (8UC1 | 8UC3 | 8UC4 | 32FC1 | 32FC3 | 32FC4, INTER_NEAREST | INTER_LINEAR | INTER_CUBIC, BORDER_CONSTANT) - pattern4('ImgProc', 'WarpAffine', '8UC1', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC1', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC1', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC3', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC3', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC3', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC4', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC4', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '8UC4', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC1', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC1', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC1', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC3', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC3', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC3', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC4', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC4', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpAffine', '32FC4', 'INTER_CUBIC', 'BORDER_CONSTANT'), - - # ImgProc/WarpPerspective (8UC1 | 8UC3 | 8UC4 | 32FC1 | 32FC3 | 32FC4, INTER_NEAREST | INTER_LINEAR | INTER_CUBIC, BORDER_CONSTANT) - pattern4('ImgProc', 'WarpPerspective', '8UC1', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC1', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC1', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC3', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC3', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC3', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC4', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC4', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '8UC4', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC1', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC1', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC1', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC3', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC3', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC3', 'INTER_CUBIC', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC4', 'INTER_NEAREST', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC4', 'INTER_LINEAR', 'BORDER_CONSTANT'), - pattern4('ImgProc', 'WarpPerspective', '32FC4', 'INTER_CUBIC', 'BORDER_CONSTANT'), - - # ImgProc/CopyMakeBorder (8UC1 | 8UC4 | 32SC1 | 32FC1, BORDER_CONSTANT) - pattern3('ImgProc', 'CopyMakeBorder', '8UC1', 'BORDER_CONSTANT'), - pattern3('ImgProc', 'CopyMakeBorder', '8UC4', 'BORDER_CONSTANT'), - pattern3('ImgProc', 'CopyMakeBorder', '32SC1', 'BORDER_CONSTANT'), - pattern3('ImgProc', 'CopyMakeBorder', '32FC1', 'BORDER_CONSTANT'), - - # ImgProc/Threshold (32F, THRESH_TRUNC) + + # ImgProc_Resize (8U, 1 | 4, INTER_NEAREST | INTER_LINEAR) + pattern4('ImgProc', 'Resize', '8U', '1', 'INTER_NEAREST'), + pattern4('ImgProc', 'Resize', '8U', '4', 'INTER_NEAREST'), + pattern4('ImgProc', 'Resize', '8U', '1', 'INTER_LINEAR'), + pattern4('ImgProc', 'Resize', '8U', '4', 'INTER_LINEAR'), + + # ImgProc_Resize (8U, 4, INTER_CUBIC) + pattern4('ImgProc', 'Resize', '8U', '4', 'INTER_CUBIC'), + + # ImgProc_WarpAffine (8U | 32F, INTER_NEAREST | INTER_LINEAR | INTER_CUBIC, BORDER_CONSTANT) + pattern4('ImgProc', 'WarpAffine', '8U' , 'INTER_NEAREST', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpAffine', '8U' , 'INTER_LINEAR', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpAffine', '8U' , 'INTER_CUBIC', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpAffine', '32F', 'INTER_NEAREST', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpAffine', '32F', 'INTER_LINEAR', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpAffine', '32F', 'INTER_CUBIC', 'BORDER_CONSTANT'), + + # ImgProc_WarpPerspective (8U | 32F, INTER_NEAREST | INTER_LINEAR | INTER_CUBIC, BORDER_CONSTANT) + pattern4('ImgProc', 'WarpPerspective', '8U' , 'INTER_NEAREST', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpPerspective', '8U' , 'INTER_LINEAR', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpPerspective', '8U' , 'INTER_CUBIC', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpPerspective', '32F', 'INTER_NEAREST', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpPerspective', '32F', 'INTER_LINEAR', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'WarpPerspective', '32F', 'INTER_CUBIC', 'BORDER_CONSTANT'), + + # ImgProc_CopyMakeBorder (8UC1 | 8UC4 | 32SC1 | 32FC1, BORDER_CONSTANT) + pattern4('ImgProc', 'CopyMakeBorder', '8U' , '1', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'CopyMakeBorder', '8U' , '4', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'CopyMakeBorder', '32S', '1', 'BORDER_CONSTANT'), + pattern4('ImgProc', 'CopyMakeBorder', '32F', '1', 'BORDER_CONSTANT'), + + # ImgProc_Threshold (32F, THRESH_TRUNC) pattern3('ImgProc', 'Threshold', '32F', 'THRESH_TRUNC'), - # ImgProc/Integral_Sqr - pattern1('ImgProc', 'Integral_Sqr'), + # ImgProc_IntegralSqr + pattern1('ImgProc', 'IntegralSqr'), - # ImgProc/HistEven_OneChannel - pattern1('ImgProc', 'HistEven_OneChannel'), + # ImgProc_HistEven_OneChannel + pattern1('ImgProc', 'HistEvenOneChannel'), - # ImgProc/HistEven_FourChannel - pattern1('ImgProc', 'HistEven_FourChannel'), + # ImgProc_HistEven_FourChannel + pattern1('ImgProc', 'HistEvenFourChannel'), - # ImgProc/Rotate + # ImgProc_Rotate pattern1('ImgProc', 'Rotate'), - # ImgProc/SwapChannels + # ImgProc_SwapChannels pattern1('ImgProc', 'SwapChannels'), - # ImgProc/AlphaComp + # ImgProc_AlphaComp pattern1('ImgProc', 'AlphaComp'), - # ImgProc/ImagePyramid_build - pattern1('ImgProc', 'ImagePyramid_build'), + # ImgProc_ImagePyramidBuild + pattern1('ImgProc', 'ImagePyramidBuild'), + + # ImgProc_ImagePyramid_getLayer + pattern1('ImgProc', 'ImagePyramidGetLayer'), - # ImgProc/ImagePyramid_getLayer - pattern1('ImgProc', 'ImagePyramid_getLayer'), - ############################################################## # MatOp - - # MatOp/SetTo (8UC4 | 16UC1 | 16UC4 | 32FC1 | 32FC4) - pattern2('MatOp', 'SetTo', '8UC4'), - pattern2('MatOp', 'SetTo', '16UC1'), - pattern2('MatOp', 'SetTo', '16UC4'), - pattern2('MatOp', 'SetTo', '32FC1'), - pattern2('MatOp', 'SetTo', '32FC4'), - - # MatOp/SetToMasked (8UC4 | 16UC1 | 16UC4 | 32FC1 | 32FC4) - pattern2('MatOp', 'SetToMasked', '8UC4'), - pattern2('MatOp', 'SetToMasked', '16UC1'), - pattern2('MatOp', 'SetToMasked', '16UC4'), - pattern2('MatOp', 'SetToMasked', '32FC1'), - pattern2('MatOp', 'SetToMasked', '32FC4'), - - # MatOp/CopyToMasked (8UC1 | 8UC3 |8UC4 | 16UC1 | 16UC3 | 16UC4 | 32FC1 | 32FC3 | 32FC4) - pattern2('MatOp', 'CopyToMasked', '8UC1'), - pattern2('MatOp', 'CopyToMasked', '8UC3'), - pattern2('MatOp', 'CopyToMasked', '8UC4'), - pattern2('MatOp', 'CopyToMasked', '16UC1'), - pattern2('MatOp', 'CopyToMasked', '16UC3'), - pattern2('MatOp', 'CopyToMasked', '16UC4'), - pattern2('MatOp', 'CopyToMasked', '32FC1'), - pattern2('MatOp', 'CopyToMasked', '32FC3'), - pattern2('MatOp', 'CopyToMasked', '32FC4'), + + # MatOp_SetTo (8UC4 | 16UC1 | 16UC4 | 32FC1 | 32FC4) + pattern3('MatOp', 'SetTo', '8U' , '4'), + pattern3('MatOp', 'SetTo', '16U', '1'), + pattern3('MatOp', 'SetTo', '16U', '4'), + pattern3('MatOp', 'SetTo', '32F', '1'), + pattern3('MatOp', 'SetTo', '32F', '4'), + + # MatOp_SetToMasked (8UC4 | 16UC1 | 16UC4 | 32FC1 | 32FC4) + pattern3('MatOp', 'SetToMasked', '8U' , '4'), + pattern3('MatOp', 'SetToMasked', '16U', '1'), + pattern3('MatOp', 'SetToMasked', '16U', '4'), + pattern3('MatOp', 'SetToMasked', '32F', '1'), + pattern3('MatOp', 'SetToMasked', '32F', '4'), + + # MatOp_CopyToMasked (8UC1 | 8UC3 |8UC4 | 16UC1 | 16UC3 | 16UC4 | 32FC1 | 32FC3 | 32FC4) + pattern3('MatOp', 'CopyToMasked', '8U' , '1'), + pattern3('MatOp', 'CopyToMasked', '8U' , '3'), + pattern3('MatOp', 'CopyToMasked', '8U' , '4'), + pattern3('MatOp', 'CopyToMasked', '16U', '1'), + pattern3('MatOp', 'CopyToMasked', '16U', '3'), + pattern3('MatOp', 'CopyToMasked', '16U', '4'), + pattern3('MatOp', 'CopyToMasked', '32F', '1'), + pattern3('MatOp', 'CopyToMasked', '32F', '3'), + pattern3('MatOp', 'CopyToMasked', '32F', '4'), ] cublasPattern = pattern1('Core', 'GEMM') @@ -260,7 +239,7 @@ if __name__ == "__main__": inputFile = open(sys.argv[1], 'r') lines = inputFile.readlines() inputFile.close() - + for i in range(len(lines)): if cublasPattern.match(lines[i]): From 93172bab87c2c9bab596b964a16256a2f479b303 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 16:12:32 +0400 Subject: [PATCH 06/58] added --cpu flag (run tests on CPU) --- modules/gpu/perf/main.cpp | 122 +++++++++++++++++------------- modules/gpu/perf/perf_utility.cpp | 2 + modules/gpu/perf/perf_utility.hpp | 2 + 3 files changed, 74 insertions(+), 52 deletions(-) diff --git a/modules/gpu/perf/main.cpp b/modules/gpu/perf/main.cpp index 988c95ae0e..865362f880 100644 --- a/modules/gpu/perf/main.cpp +++ b/modules/gpu/perf/main.cpp @@ -1,107 +1,125 @@ #include "perf_precomp.hpp" -#ifdef HAVE_CUDA - using namespace std; using namespace cv; using namespace cv::gpu; using namespace cvtest; using namespace testing; -void printInfo() +void printOsInfo() { #if defined _WIN32 # if defined _WIN64 - puts("OS: Windows x64"); + cout << "OS: Windows x64 \n" << endl; # else - puts("OS: Windows x32"); + cout << "OS: Windows x32 \n" << endl; # endif #elif defined linux # if defined _LP64 - puts("OS: Linux x64"); + cout << "OS: Linux x64 \n" << endl; # else - puts("OS: Linux x32"); + cout << "OS: Linux x32 \n" << endl; # endif #elif defined __APPLE__ # if defined _LP64 - puts("OS: Apple x64"); + cout << "OS: Apple x64 \n" << endl; # else - puts("OS: Apple x32"); + cout << "OS: Apple x32 \n" << endl; # endif #endif +} +void printCudaInfo() +{ +#ifndef HAVE_CUDA + cout << "OpenCV was built without CUDA support \n" << endl; +#else int driver; cudaDriverGetVersion(&driver); - printf("CUDA Driver version: %d\n", driver); - printf("CUDA Runtime version: %d\n", CUDART_VERSION); + cout << "CUDA Driver version: " << driver << '\n'; + cout << "CUDA Runtime version: " << CUDART_VERSION << '\n'; - puts("GPU module was compiled for the following GPU archs:"); - printf(" BIN: %s\n", CUDA_ARCH_BIN); - printf(" PTX: %s\n\n", CUDA_ARCH_PTX); + cout << endl; + + cout << "GPU module was compiled for the following GPU archs:" << endl; + cout << " BIN: " << CUDA_ARCH_BIN << '\n'; + cout << " PTX: " << CUDA_ARCH_PTX << '\n'; + + cout << endl; int deviceCount = getCudaEnabledDeviceCount(); - printf("CUDA device count: %d\n\n", deviceCount); + cout << "CUDA device count: " << deviceCount << '\n'; + + cout << endl; for (int i = 0; i < deviceCount; ++i) { DeviceInfo info(i); - printf("Device %d:\n", i); - printf(" Name: %s\n", info.name().c_str()); - printf(" Compute capability version: %d.%d\n", info.majorVersion(), info.minorVersion()); - printf(" Multi Processor Count: %d\n", info.multiProcessorCount()); - printf(" Total memory: %d Mb\n", static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0)); - printf(" Free memory: %d Mb\n", static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0)); + cout << "Device [" << i << "] \n"; + cout << "\t Name: " << info.name() << '\n'; + cout << "\t Compute capability: " << info.majorVersion() << '.' << info.minorVersion()<< '\n'; + cout << "\t Multi Processor Count: " << info.multiProcessorCount() << '\n'; + cout << "\t Total memory: " << static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0) << " Mb \n"; + cout << "\t Free memory: " << static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0) << " Mb \n"; if (!info.isCompatible()) - puts(" !!! This device is NOT compatible with current GPU module build\n"); - printf("\n"); + cout << "\t !!! This device is NOT compatible with current GPU module build \n"; + + cout << endl; } +#endif } int main(int argc, char **argv) { - CommandLineParser parser(argc, (const char**)argv, - "{ print_info_only | print_info_only | false | Print information about system and exit }" - "{ device | device | 0 | Device on which tests will be executed }"); + CommandLineParser cmd(argc, argv, + "{ print_info_only | print_info_only | false | Print information about system and exit }" + "{ device | device | 0 | Device on which tests will be executed }" + "{ cpu | cpu | false | Run tests on cpu }" + ); - printInfo(); + printOsInfo(); + printCudaInfo(); - if (parser.get("print_info_only")) + if (cmd.get("print_info_only")) return 0; - int device = parser.get("device"); + int device = cmd.get("device"); + bool cpu = cmd.get("cpu"); +#ifndef HAVE_CUDA + cpu = true; +#endif - if (device < 0 || device >= getCudaEnabledDeviceCount()) + if (cpu) { - cerr << "Incorrect device number - " << device << endl; - return -1; - } + runOnGpu = false; - DeviceInfo info(device); - if (!info.isCompatible()) - { - cerr << "Device " << device << " [" << info.name() << "] is NOT compatible with current GPU module build" << endl; - return -1; + cout << "Run tests on CPU \n" << endl; } + else + { + runOnGpu = true; - std::cout << "Run tests on device " << device << '\n' << std::endl; + if (device < 0 || device >= getCudaEnabledDeviceCount()) + { + cerr << "Incorrect device index - " << device << endl; + return -1; + } - setDevice(device); + DeviceInfo info(device); + if (!info.isCompatible()) + { + cerr << "Device " << device << " [" << info.name() << "] is NOT compatible with current GPU module build" << endl; + return -1; + } + + setDevice(device); + + cout << "Run tests on device " << device << " [" << info.name() << "] \n" << endl; + } testing::InitGoogleTest(&argc, argv); perf::TestBase::Init(argc, argv); return RUN_ALL_TESTS(); - - return 0; -} - -#else - -int main() -{ - printf("OpenCV was built without CUDA support\n"); - return 0; } - -#endif diff --git a/modules/gpu/perf/perf_utility.cpp b/modules/gpu/perf/perf_utility.cpp index 42862f49d1..3be162b933 100644 --- a/modules/gpu/perf/perf_utility.cpp +++ b/modules/gpu/perf/perf_utility.cpp @@ -4,6 +4,8 @@ using namespace std; using namespace cv; using namespace cv::gpu; +bool runOnGpu = true; + void fillRandom(Mat& m, double a, double b) { RNG rng(123456789); diff --git a/modules/gpu/perf/perf_utility.hpp b/modules/gpu/perf/perf_utility.hpp index b717030ff3..2d21fffb57 100644 --- a/modules/gpu/perf/perf_utility.hpp +++ b/modules/gpu/perf/perf_utility.hpp @@ -6,6 +6,8 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/ts/ts_perf.hpp" +extern bool runOnGpu; + void fillRandom(cv::Mat& m, double a = 0.0, double b = 255.0); cv::Mat readImage(const std::string& fileName, int flags = cv::IMREAD_COLOR); From 6da9b9f13739994e5a291b9106eb353b66b8a2b4 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 17 Aug 2012 01:23:25 +0400 Subject: [PATCH 07/58] Android toolchain: libstdc++ and libsupc are removed from explicit link libraries # Please enter the commit message for your changes. Lines starting # with '#' will be kept; you may remove them yourself if you want to. # An empty message aborts the commit. # On branch master # Your branch is ahead of 'origin/master' by 1 commit. # # Changes to be committed: # (use "git reset HEAD^1 ..." to unstage) # # modified: android/android.toolchain.cmake # --- android/android.toolchain.cmake | 131 ++++++++++++++++---------------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake index 3c89806ac0..37c9ad15ff 100644 --- a/android/android.toolchain.cmake +++ b/android/android.toolchain.cmake @@ -1,6 +1,6 @@ # ------------------------------------------------------------------------------ # Android CMake toolchain file, for use with the Android NDK r5-r8 -# Requires cmake 2.6.3 or newer (2.8.3 or newer is recommended). +# Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). # See home page: http://code.google.com/p/android-cmake/ # # The file is mantained by the OpenCV project. And also can be found at @@ -44,7 +44,8 @@ # ANDROID_ABI=armeabi-v7a - specifies the target Application Binary # Interface (ABI). This option nearly matches to the APP_ABI variable # used by ndk-build tool from Android NDK. -# Possible values are: +# +# Possible targets are: # "armeabi" - matches to the NDK ABI with the same name. # See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. # "armeabi-v7a" - matches to the NDK ABI with the same name. @@ -55,7 +56,9 @@ # sets VFPV3 as floating-point unit (has 32 registers instead of 16). # "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP. # "x86" - matches to the NDK ABI with the same name. -# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "mips" - matches to the NDK ABI with the same name +# (not testes on real devices) # # ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. # Option is read-only when standalone toolchain used. @@ -183,12 +186,13 @@ # - modified August 2012 # [+] updated for NDK r8b # [~] all intermediate files generated by toolchain are moved into CMakeFiles +# [~] libstdc++ and libsupc are removed from explicit link libraries # ------------------------------------------------------------------------------ cmake_minimum_required( VERSION 2.6.3 ) if( DEFINED CMAKE_CROSSCOMPILING ) - #subsequent toolchain loading is not really needed + # subsequent toolchain loading is not really needed return() endif() @@ -199,7 +203,7 @@ endif() # this one is important set( CMAKE_SYSTEM_NAME Linux ) -#this one not so much +# this one not so much set( CMAKE_SYSTEM_VERSION 1 ) set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) @@ -331,11 +335,11 @@ macro( __COPY_IF_DIFFERENT _source _destination ) endmacro() -#stl version: by default gnustl_static will be used +# stl version: by default gnustl_static will be used set( ANDROID_USE_STLPORT FALSE CACHE BOOL "Experimental: use stlport_static instead of gnustl_static") mark_as_advanced( ANDROID_USE_STLPORT ) -#fight against cygwin +# fight against cygwin set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") mark_as_advanced( ANDROID_FORBID_SYGWIN ) if( ANDROID_FORBID_SYGWIN ) @@ -344,7 +348,7 @@ if( ANDROID_FORBID_SYGWIN ) endif() if( CMAKE_HOST_WIN32 ) - #remove cygwin from PATH + # remove cygwin from PATH set( __new_path "$ENV{PATH}") __LIST_FILTER( __new_path "cygwin" ) set(ENV{PATH} "${__new_path}") @@ -352,7 +356,7 @@ if( ANDROID_FORBID_SYGWIN ) endif() endif() -#detect current host platform +# detect current host platform set( TOOL_OS_SUFFIX "" ) if( CMAKE_HOST_APPLE ) set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86" ) @@ -365,10 +369,10 @@ else() message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) endif() -#see if we have path to Android NDK +# see if we have path to Android NDK __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) if( NOT ANDROID_NDK ) - #see if we have path to Android standalone toolchain + # see if we have path to Android standalone toolchain __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN OBSOLETE_ANDROID_NDK_TOOLCHAIN_ROOT OBSOLETE_ENV_ANDROID_NDK_TOOLCHAIN_ROOT ) if( NOT ANDROID_STANDALONE_TOOLCHAIN ) @@ -397,10 +401,10 @@ if( NOT ANDROID_NDK ) endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) endif( NOT ANDROID_NDK ) -#remember found paths +# remember found paths if( ANDROID_NDK ) get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) - #try to detect change + # try to detect change if( CMAKE_AR ) string( LENGTH "${ANDROID_NDK}" __length ) string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) @@ -414,7 +418,7 @@ if( ANDROID_NDK ) set( BUILD_WITH_ANDROID_NDK True ) elseif( ANDROID_STANDALONE_TOOLCHAIN ) get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) - #try to detect change + # try to detect change if( CMAKE_AR ) string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) @@ -438,7 +442,7 @@ else() sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) endif() -#get all the details about standalone toolchain +# get all the details about standalone toolchain if( BUILD_WITH_STANDALONE_TOOLCHAIN ) __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) @@ -455,7 +459,7 @@ if( BUILD_WITH_STANDALONE_TOOLCHAIN ) set( __availableToolchainArchs "mipsel" ) endif() if( ANDROID_COMPILER_VERSION ) - #do not run gcc every time because it is relatevely expencive + # do not run gcc every time because it is relatevely expencive set( __availableToolchainCompilerVersions "${ANDROID_COMPILER_VERSION}" ) else() execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" --version @@ -464,7 +468,7 @@ if( BUILD_WITH_STANDALONE_TOOLCHAIN ) endif() endif() -#get all the details about NDK +# get all the details about NDK if( BUILD_WITH_ANDROID_NDK ) file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) @@ -490,7 +494,7 @@ if( BUILD_WITH_ANDROID_NDK ) endif() endif() -#build list of available ABIs +# build list of available ABIs if( NOT ANDROID_SUPPORTED_ABIS ) set( ANDROID_SUPPORTED_ABIS "" ) set( __uniqToolchainArchNames ${__availableToolchainArchs} ) @@ -505,9 +509,9 @@ if( NOT ANDROID_SUPPORTED_ABIS ) endif() endif() -#choose target ABI +# choose target ABI __INIT_VARIABLE( ANDROID_ABI OBSOLETE_ARM_TARGET OBSOLETE_ARM_TARGETS VALUES ${ANDROID_SUPPORTED_ABIS} ) -#verify that target ABI is supported +# verify that target ABI is supported list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) if( __androidAbiIdx EQUAL -1 ) string( REPLACE ";" "\", \"", PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) @@ -517,10 +521,10 @@ if( __androidAbiIdx EQUAL -1 ) endif() unset( __androidAbiIdx ) -#remember target ABI +# remember target ABI set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) -#set target ABI options +# set target ABI options if( ANDROID_ABI STREQUAL "x86" ) set( X86 true ) set( ANDROID_NDK_ABI_NAME "x86" ) @@ -545,7 +549,7 @@ elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) set( ANDROID_ARCH_NAME "arm" ) set( ANDROID_ARCH_FULLNAME "arm" ) set( CMAKE_SYSTEM_PROCESSOR "armv6" ) - #need always fallback to older platform + # need always fallback to older platform set( ARMEABI true ) elseif( ANDROID_ABI STREQUAL "armeabi-v7a") set( ARMEABI_V7A true ) @@ -573,8 +577,8 @@ else() endif() if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) - #really dirty hack - #it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... + # really dirty hack + # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) endif() @@ -592,7 +596,7 @@ else() unset( ANDROID_FORCE_ARM_BUILD CACHE ) endif() -#choose toolchain +# choose toolchain if( ANDROID_TOOLCHAIN_NAME ) list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) if( __toolchainIdx EQUAL -1 ) @@ -637,10 +641,10 @@ unset( __availableToolchainMachines ) unset( __availableToolchainArchs ) unset( __availableToolchainCompilerVersions ) -#choose native API level +# choose native API level __INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) string( REGEX MATCH "[0-9]+" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) -#validate +# validate list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) if( __levelIdx EQUAL -1 ) message( SEND_ERROR "Specified Android native API level (${ANDROID_NATIVE_API_LEVEL}) is not supported by your NDK/toolchain." ) @@ -659,7 +663,7 @@ if( CMAKE_VERSION VERSION_GREATER "2.8" ) set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) endif() -#setup paths +# setup paths if( BUILD_WITH_STANDALONE_TOOLCHAIN ) set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) @@ -689,7 +693,7 @@ set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHI if( CMAKE_VERSION VERSION_LESS 2.8.5 ) set( CMAKE_ASM_COMPILER_ARG1 "-c" ) endif() -#there may be a way to make cmake deduce these TODO deduce the rest of the tools +# there may be a way to make cmake deduce these TODO deduce the rest of the tools set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) @@ -705,11 +709,12 @@ if( APPLE ) endif() mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) endif() -#export directories + +# export directories set( ANDROID_SYSTEM_INCLUDE_DIRS "" ) set( ANDROID_SYSTEM_LIB_DIRS "" ) -#setup output directories +# setup output directories set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) @@ -722,13 +727,13 @@ if(NOT _CMAKE_IN_TRY_COMPILE) set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) endif() -#includes +# includes list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_SYSROOT}/usr/include" ) if( __stlIncludePath AND EXISTS "${__stlIncludePath}" ) list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${__stlIncludePath}" ) endif() -#STL bits includes +# c++ bits includes if( __stlLibPath AND EXISTS "${__stlLibPath}/include" ) list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${__stlLibPath}/include" ) endif() @@ -742,7 +747,7 @@ elseif( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/incl list( APPEND ANDROID_SYSTEM_INCLUDE_DIRS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) endif() -#flags and definitions +# flags and definitions if(ANDROID_SYSROOT MATCHES "[ ;\"]") set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) # quotes will break try_compile and compiler identification @@ -766,7 +771,7 @@ set( CMAKE_CXX_PLATFORM_ID Linux ) set( CMAKE_CXX_SIZEOF_DATA_PTR 4 ) set( CMAKE_CXX_HAS_ISYSROOT 1 ) set( CMAKE_CXX_COMPILER_ABI ELF ) -#force ASM compiler (required for CMake < 2.8.5) +# force ASM compiler (required for CMake < 2.8.5) set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) set( CMAKE_ASM_COMPILER_ID GNU ) set( CMAKE_ASM_COMPILER_WORKS TRUE ) @@ -796,17 +801,17 @@ endif() if( ANDROID_USE_STLPORT ) set( _CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions" ) - set( _CMAKE_C_FLAGS "${_CMAKE_C_FLAGS} -fno-rtti -fno-exceptions" ) + set( _CMAKE_C_FLAGS "${_CMAKE_C_FLAGS} -fno-exceptions" ) else() set( _CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS} -frtti -fexceptions" ) set( _CMAKE_C_FLAGS "${_CMAKE_C_FLAGS} -fexceptions" ) endif() -#release and debug flags +# release and debug flags if( ARMEABI OR ARMEABI_V7A ) if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) - #It is recommended to use the -mthumb compiler flag to force the generation - #of 16-bit Thumb-1 instructions (the default being 32-bit ARM ones). + # It is recommended to use the -mthumb compiler flag to force the generation + # of 16-bit Thumb-1 instructions (the default being 32-bit ARM ones). # O3 instead of O2/Os in release mode - like cmake sets for desktop gcc set( _CMAKE_CXX_FLAGS_RELEASE "-mthumb -O3" ) set( _CMAKE_C_FLAGS_RELEASE "-mthumb -O3" ) @@ -836,7 +841,7 @@ set( _CMAKE_C_FLAGS_RELEASE "${_CMAKE_C_FLAGS_RELEASE} -fomit-frame-pointer set( _CMAKE_CXX_FLAGS_DEBUG "${_CMAKE_CXX_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) set( _CMAKE_C_FLAGS_DEBUG "${_CMAKE_C_FLAGS_DEBUG} -fno-strict-aliasing -fno-omit-frame-pointer -DDEBUG -D_DEBUG" ) -#ABI-specific flags +# ABI-specific flags if( ARMEABI_V7A ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) if( NEON ) @@ -854,19 +859,18 @@ elseif( X86 ) set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" )#sse? endif() -#linker flags +# linker flags if( NOT DEFINED __ndklibspath ) set( __ndklibspath "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/ndklibs/${ANDROID_NDK_ABI_NAME}" ) endif() -list( APPEND ANDROID_SYSTEM_LIB_DIRS "${__ndklibspath}" "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) +list( APPEND ANDROID_SYSTEM_LIB_DIRS "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) set( ANDROID_LINKER_FLAGS "" ) -#STL + +# STL if( ANDROID_USE_STLPORT ) if( EXISTS "${__stlLibPath}/libstlport_static.a" ) - __COPY_IF_DIFFERENT( "${__stlLibPath}/libstlport_static.a" "${__ndklibspath}/libstlport_static.a" ) - endif() - if( EXISTS "${__ndklibspath}/libstlport_static.a" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--start-group -lstlport_static" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o \"${__stlLibPath}/libstlport_static.a\"") + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o \"${__stlLibPath}/libstlport_static.a\"") endif() else( ANDROID_USE_STLPORT ) if( EXISTS "${__stlLibPath}/libgnustl_static.a" ) @@ -880,11 +884,6 @@ else( ANDROID_USE_STLPORT ) elseif( EXISTS "${__stlLibPath}/libstdc++.a" ) __COPY_IF_DIFFERENT( "${__stlLibPath}/libstdc++.a" "${__ndklibspath}/libstdc++.a" ) endif() - if( EXISTS "${__ndklibspath}/libstdc++.a" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -lstdc++" ) - endif() - - #gcc exception & rtti support if( EXISTS "${__stlLibPath}/libsupc++.a" ) __COPY_IF_DIFFERENT( "${__stlLibPath}/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) elseif( ANDROID_ARCH_NAME STREQUAL "arm" AND EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) @@ -896,16 +895,14 @@ else( ANDROID_USE_STLPORT ) elseif( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) __COPY_IF_DIFFERENT( "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" "${__ndklibspath}/libsupc++.a" ) endif() - if( EXISTS "${__ndklibspath}/libsupc++.a" ) - set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -lsupc++" ) - endif() + list( APPEND ANDROID_SYSTEM_LIB_DIRS "${__ndklibspath}" ) endif( ANDROID_USE_STLPORT ) -#cleanup for STL search +# cleanup for STL search unset( __stlIncludePath ) unset( __stlLibPath ) -#other linker flags +# other linker flags __INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" FORCE ) mark_as_advanced( ANDROID_NO_UNDEFINED ) @@ -914,7 +911,7 @@ if( ANDROID_NO_UNDEFINED ) endif() if (ANDROID_NDK MATCHES "-r[56].?$") - #libGLESv2.so in NDK's prior to r7 refers to exteranal symbols. So this flag option is required for all projects using OpenGL from native. + # libGLESv2.so in NDK's prior to r7 refers to exteranal symbols. So this flag option is required for all projects using OpenGL from native. __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) else() __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) @@ -940,7 +937,7 @@ if( ARMEABI_V7A ) set( ANDROID_LINKER_FLAGS "-Wl,--fix-cortex-a8 ${ANDROID_LINKER_FLAGS}" ) endif() -#cache flags +# cache flags set( CMAKE_CXX_FLAGS "${_CMAKE_CXX_FLAGS}" CACHE STRING "c++ flags" ) set( CMAKE_C_FLAGS "${_CMAKE_C_FLAGS}" CACHE STRING "c flags" ) set( CMAKE_CXX_FLAGS_RELEASE "${_CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "c++ Release flags" ) @@ -954,7 +951,7 @@ set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "linker flags" ) include_directories( SYSTEM ${ANDROID_SYSTEM_INCLUDE_DIRS} ) link_directories( ${ANDROID_SYSTEM_LIB_DIRS} ) -#finish flags +# finish flags set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Extra Android compiler flags") set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Extra Android linker flags") set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) @@ -969,7 +966,7 @@ else() set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) endif() -#set these global flags for cmake client scripts to change behavior +# set these global flags for cmake client scripts to change behavior set( ANDROID True ) set( BUILD_ANDROID True ) @@ -982,7 +979,7 @@ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) -#macro to find packages on the host OS +# macro to find packages on the host OS macro( find_host_package ) set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) @@ -1004,7 +1001,7 @@ macro( find_host_package ) endmacro() -#macro to find programs on the host OS +# macro to find programs on the host OS macro( find_host_program ) set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) @@ -1044,7 +1041,11 @@ if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) set( __toolchain_config "") foreach( __var ANDROID_ABI ANDROID_FORCE_ARM_BUILD ANDROID_NATIVE_API_LEVEL ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_SET_OBSOLETE_VARIABLES LIBRARY_OUTPUT_PATH_ROOT ANDROID_USE_STLPORT ANDROID_FORBID_SYGWIN ANDROID_NDK ANDROID_STANDALONE_TOOLCHAIN ANDROID_FUNCTION_LEVEL_LINKING __ndklibspath ) if( DEFINED ${__var} ) - set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" )\n" ) + if( "${__var}" MATCHES " ") + set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) + else() + set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) + endif() endif() endforeach() file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) From 41b6d25bdd78ae684d8630fdb20a8752d6d7dee6 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 17 Aug 2012 17:32:06 +0400 Subject: [PATCH 08/58] added cross-platform Mutex implementation; enable platform-native (GDC/Concurrency) parallel_for_ implementation when TBB is not installed. --- modules/core/include/opencv2/core/core.hpp | 28 ++++++ modules/core/src/parallel.cpp | 10 +++ modules/core/src/system.cpp | 100 +++++++++++++++++++++ 3 files changed, 138 insertions(+) diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 8cf7b7e277..fbaf13721f 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -4620,6 +4620,34 @@ public: CV_EXPORTS void parallel_for_(const Range& range, const ParallelLoopBody& body); +/////////////////////////// Synchronization Primitives /////////////////////////////// + +class CV_EXPORTS Mutex +{ +public: + Mutex(); + ~Mutex(); + Mutex(const Mutex& m); + Mutex& operator = (const Mutex& m); + + void lock(); + bool trylock(); + void unlock(); + + struct Impl; +protected: + Impl* impl; +}; + +class CV_EXPORTS AutoLock +{ +public: + AutoLock(Mutex& m) : mutex(&m) { mutex->lock(); } + ~AutoLock() { mutex->unlock(); } +protected: + Mutex* mutex; +}; + } #endif // __cplusplus diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 4274caf340..3e21417f74 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -42,6 +42,16 @@ #include "precomp.hpp" +#if !defined HAVE_TBB && !defined HAVE_OPENMP && !defined HAVE_GCD && !defined HAVE_CONCURRENCY + +#ifdef __APPLE__ +#define HAVE_GCD +#elif defined __MSC_VER && __MSC_VER >= 1600 +#define HAVE_CONCURRENCY +#endif + +#endif + #ifdef HAVE_CONCURRENCY # include #elif defined HAVE_OPENMP diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 42bf6593db..e1d57ef8cb 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -930,4 +930,104 @@ BOOL WINAPI DllMain( HINSTANCE, DWORD fdwReason, LPVOID ) } #endif +namespace cv +{ + +#if defined WIN32 || defined _WIN32 || defined WINCE + +struct Mutex::Impl +{ + Impl() { InitializeCriticalSection(&cs); refcount = 1; } + ~Impl() { DeleteCriticalSection(&cs); } + + void lock() { EnterCriticalSection(&cs); } + bool trylock() { return TryEnterCriticalSection(&cs) != 0; } + void unlock() { LeaveCriticalSection(&cs); } + + CRITICAL_SECTION cs; + int refcount; +}; + +#elif defined __APPLE__ + +#include + +struct Mutex::Impl +{ + Impl() { sl = OS_SPINLOCK_INIT; refcount = 1; } + ~Impl() {} + + void lock() { OSSpinLockLock(&sl); } + bool trylock() { return OSSpinLockTry(&sl); } + void unlock() { OSSpinLockUnlock(&sl); } + + OSSpinLock sl; + int refcount; +}; + +#elif defined __linux__ + +struct Mutex::Impl +{ + Impl() { pthread_spin_init(&sl, 0); refcount = 1; } + ~Impl() { pthread_spin_destroy(&sl); } + + void lock() { pthread_spin_lock(&sl); } + bool trylock() { return pthread_spin_trylock(&sl) == 0; } + void unlock() { pthread_spin_unlock(&sl); } + + pthread_spinlock_t sl; + int refcount; +}; + +#else + +struct Mutex::Impl +{ + Impl() { pthread_mutex_init(&sl, 0); refcount = 1; } + ~Impl() { pthread_mutex_destroy(&sl); } + + void lock() { pthread_mutex_lock(&sl); } + bool trylock() { return pthread_mutex_trylock(&sl) == 0; } + void unlock() { pthread_mutex_unlock(&sl); } + + pthread_mutex_t sl; + int refcount; +}; + +#endif + +Mutex::Mutex() +{ + impl = new Mutex::Impl; +} + +Mutex::~Mutex() +{ + if( CV_XADD(&impl->refcount, -1) == 1 ) + delete impl; + impl = 0; +} + +Mutex::Mutex(const Mutex& m) +{ + impl = m.impl; + CV_XADD(&impl->refcount, 1); +} + +Mutex& Mutex::operator = (const Mutex& m) +{ + CV_XADD(&m.impl->refcount, 1); + if( CV_XADD(&impl->refcount, -1) == 1 ) + delete impl; + impl = m.impl; + return *this; +} + +void Mutex::lock() { impl->lock(); } +void Mutex::unlock() { impl->unlock(); } +bool Mutex::trylock() { return impl->trylock(); } + +} + /* End of file. */ \ No newline at end of file From ec8f926686457d4b5f4c7c8d9d78b975ac846812 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Fri, 17 Aug 2012 17:34:51 +0400 Subject: [PATCH 09/58] small correction for the previous patch in parallel.cpp --- modules/core/src/parallel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 3e21417f74..c238f992b0 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -46,7 +46,7 @@ #ifdef __APPLE__ #define HAVE_GCD -#elif defined __MSC_VER && __MSC_VER >= 1600 +#elif defined _MSC_VER && _MSC_VER >= 1600 #define HAVE_CONCURRENCY #endif From a507d564a4aaa33cb1fa8bac144a3dacbddbf103 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Fri, 17 Aug 2012 17:34:58 +0400 Subject: [PATCH 10/58] merged gpu_perf_cpu into gpu_perf --- modules/gpu/perf/perf_calib3d.cpp | 250 +++- modules/gpu/perf/perf_core.cpp | 1538 +++++++++++++++------ modules/gpu/perf/perf_features2d.cpp | 185 ++- modules/gpu/perf/perf_filters.cpp | 248 +++- modules/gpu/perf/perf_imgproc.cpp | 914 ++++++++---- modules/gpu/perf/perf_labeling.cpp | 124 +- modules/gpu/perf/perf_matop.cpp | 94 +- modules/gpu/perf/perf_objdetect.cpp | 91 +- modules/gpu/perf/perf_video.cpp | 718 +++++++--- modules/gpu/perf_cpu/perf_calib3d.cpp | 136 -- modules/gpu/perf_cpu/perf_core.cpp | 1388 ------------------- modules/gpu/perf_cpu/perf_cpu_precomp.cpp | 1 - modules/gpu/perf_cpu/perf_cpu_precomp.hpp | 32 - modules/gpu/perf_cpu/perf_features2d.cpp | 187 --- modules/gpu/perf_cpu/perf_filters.cpp | 283 ---- modules/gpu/perf_cpu/perf_imgproc.cpp | 771 ----------- modules/gpu/perf_cpu/perf_labeling.cpp | 158 --- modules/gpu/perf_cpu/perf_main.cpp | 20 - modules/gpu/perf_cpu/perf_matop.cpp | 124 -- modules/gpu/perf_cpu/perf_objdetect.cpp | 74 - modules/gpu/perf_cpu/perf_utility.cpp | 220 --- modules/gpu/perf_cpu/perf_utility.hpp | 77 -- modules/gpu/perf_cpu/perf_video.cpp | 466 ------- 23 files changed, 3096 insertions(+), 5003 deletions(-) delete mode 100644 modules/gpu/perf_cpu/perf_calib3d.cpp delete mode 100644 modules/gpu/perf_cpu/perf_core.cpp delete mode 100644 modules/gpu/perf_cpu/perf_cpu_precomp.cpp delete mode 100644 modules/gpu/perf_cpu/perf_cpu_precomp.hpp delete mode 100644 modules/gpu/perf_cpu/perf_features2d.cpp delete mode 100644 modules/gpu/perf_cpu/perf_filters.cpp delete mode 100644 modules/gpu/perf_cpu/perf_imgproc.cpp delete mode 100644 modules/gpu/perf_cpu/perf_labeling.cpp delete mode 100644 modules/gpu/perf_cpu/perf_main.cpp delete mode 100644 modules/gpu/perf_cpu/perf_matop.cpp delete mode 100644 modules/gpu/perf_cpu/perf_objdetect.cpp delete mode 100644 modules/gpu/perf_cpu/perf_utility.cpp delete mode 100644 modules/gpu/perf_cpu/perf_utility.hpp delete mode 100644 modules/gpu/perf_cpu/perf_video.cpp diff --git a/modules/gpu/perf/perf_calib3d.cpp b/modules/gpu/perf/perf_calib3d.cpp index 343a4e9fad..f62185e007 100644 --- a/modules/gpu/perf/perf_calib3d.cpp +++ b/modules/gpu/perf/perf_calib3d.cpp @@ -15,22 +15,42 @@ PERF_TEST_P(ImagePair, Calib3D_StereoBM, Values(make_pair("gpu/p { declare.time(5.0); - cv::Mat imgLeft = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + const cv::Mat imgLeft = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(imgLeft.empty()); - cv::Mat imgRight = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + const cv::Mat imgRight = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(imgRight.empty()); - cv::gpu::StereoBM_GPU d_bm(0, 256); - cv::gpu::GpuMat d_imgLeft(imgLeft); - cv::gpu::GpuMat d_imgRight(imgRight); - cv::gpu::GpuMat d_dst; + const int preset = 0; + const int ndisp = 256; - d_bm(d_imgLeft, d_imgRight, d_dst); - - TEST_CYCLE() + if (runOnGpu) { + cv::gpu::StereoBM_GPU d_bm(preset, ndisp); + + cv::gpu::GpuMat d_imgLeft(imgLeft); + cv::gpu::GpuMat d_imgRight(imgRight); + cv::gpu::GpuMat d_dst; + d_bm(d_imgLeft, d_imgRight, d_dst); + + TEST_CYCLE() + { + d_bm(d_imgLeft, d_imgRight, d_dst); + } + } + else + { + cv::StereoBM bm(preset, ndisp); + + cv::Mat dst; + + bm(imgLeft, imgRight, dst); + + TEST_CYCLE() + { + bm(imgLeft, imgRight, dst); + } } } @@ -41,22 +61,32 @@ PERF_TEST_P(ImagePair, Calib3D_StereoBeliefPropagation, Values(make_pair("gpu/stereobm/aloe-L.png", "gpu/stereobm/aloe-disp.png"))) { - cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); + const cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::Mat disp = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); + const cv::Mat disp = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(disp.empty()); - cv::gpu::DisparityBilateralFilter d_filter(128); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_disp(disp); - cv::gpu::GpuMat d_dst; + const int ndisp = 128; - d_filter(d_disp, d_img, d_dst); - - TEST_CYCLE() + if (runOnGpu) { + cv::gpu::DisparityBilateralFilter d_filter(ndisp); + + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_disp(disp); + cv::gpu::GpuMat d_dst; + d_filter(d_disp, d_img, d_dst); + + TEST_CYCLE() + { + d_filter(d_disp, d_img, d_dst); + } + } + else + { + FAIL(); } } @@ -117,22 +167,29 @@ DEF_PARAM_TEST_1(Count, int); PERF_TEST_P(Count, Calib3D_TransformPoints, Values(5000, 10000, 20000)) { - int count = GetParam(); + const int count = GetParam(); cv::Mat src(1, count, CV_32FC3); fillRandom(src, -100, 100); - cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); + const cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); + const cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); + cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); + } + } + else { - cv::gpu::transformPoints(d_src, rvec, tvec, d_dst); + FAIL(); } } @@ -141,23 +198,37 @@ PERF_TEST_P(Count, Calib3D_TransformPoints, Values(5000, 10000, 20000)) PERF_TEST_P(Count, Calib3D_ProjectPoints, Values(5000, 10000, 20000)) { - int count = GetParam(); + const int count = GetParam(); cv::Mat src(1, count, CV_32FC3); fillRandom(src, -100, 100); - cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::Mat camera_mat = cv::Mat::ones(3, 3, CV_32FC1); + const cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); + const cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); + const cv::Mat camera_mat = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); + cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); + } + } + else { - cv::gpu::projectPoints(d_src, rvec, tvec, camera_mat, cv::Mat(), d_dst); + cv::Mat dst; + + cv::projectPoints(src, rvec, tvec, camera_mat, cv::noArray(), dst); + + TEST_CYCLE() + { + cv::projectPoints(src, rvec, tvec, camera_mat, cv::noArray(), dst); + } } } @@ -166,9 +237,9 @@ PERF_TEST_P(Count, Calib3D_ProjectPoints, Values(5000, 10000, 20000)) PERF_TEST_P(Count, Calib3D_SolvePnPRansac, Values(5000, 10000, 20000)) { - declare.time(3.0); + declare.time(10.0); - int count = GetParam(); + const int count = GetParam(); cv::Mat object(1, count, CV_32FC3); fillRandom(object, -100, 100); @@ -180,7 +251,7 @@ PERF_TEST_P(Count, Calib3D_SolvePnPRansac, Values(5000, 10000, 20000)) camera_mat.at(2, 0) = 0.f; camera_mat.at(2, 1) = 0.f; - cv::Mat dist_coef(1, 8, CV_32F, cv::Scalar::all(0)); + const cv::Mat dist_coef(1, 8, CV_32F, cv::Scalar::all(0)); std::vector image_vec; cv::Mat rvec_gold(1, 3, CV_32FC1); @@ -194,11 +265,23 @@ PERF_TEST_P(Count, Calib3D_SolvePnPRansac, Values(5000, 10000, 20000)) cv::Mat rvec; cv::Mat tvec; - cv::gpu::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); - - TEST_CYCLE() + if (runOnGpu) { cv::gpu::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); + + TEST_CYCLE() + { + cv::gpu::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); + } + } + else + { + cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); + + TEST_CYCLE() + { + cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); + } } } @@ -207,8 +290,8 @@ PERF_TEST_P(Count, Calib3D_SolvePnPRansac, Values(5000, 10000, 20000)) PERF_TEST_P(Sz_Depth, Calib3D_ReprojectImageTo3D, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src, 5.0, 30.0); @@ -216,14 +299,28 @@ PERF_TEST_P(Sz_Depth, Calib3D_ReprojectImageTo3D, Combine(GPU_TYPICAL_MAT_SIZES, cv::Mat Q(4, 4, CV_32FC1); fillRandom(Q, 0.1, 1.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); + cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); + } + } + else { - cv::gpu::reprojectImageTo3D(d_src, d_dst, Q); + cv::Mat dst; + + cv::reprojectImageTo3D(src, dst, Q); + + TEST_CYCLE() + { + cv::reprojectImageTo3D(src, dst, Q); + } } } @@ -232,20 +329,27 @@ PERF_TEST_P(Sz_Depth, Calib3D_ReprojectImageTo3D, Combine(GPU_TYPICAL_MAT_SIZES, PERF_TEST_P(Sz_Depth, Calib3D_DrawColorDisp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S))) { - cv::Size size = GET_PARAM(0); - int type = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); cv::Mat src(size, type); fillRandom(src, 0, 255); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::drawColorDisp(d_src, d_dst, 255); + cv::gpu::drawColorDisp(d_src, d_dst, 255); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::drawColorDisp(d_src, d_dst, 255); + } + } + else { - cv::gpu::drawColorDisp(d_src, d_dst, 255); + FAIL(); } } diff --git a/modules/gpu/perf/perf_core.cpp b/modules/gpu/perf/perf_core.cpp index b56713a70e..90d0d831c9 100644 --- a/modules/gpu/perf/perf_core.cpp +++ b/modules/gpu/perf/perf_core.cpp @@ -12,21 +12,39 @@ namespace { PERF_TEST_P(Sz_Depth_Cn, Core_Merge, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, Values(2, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - std::vector d_src(channels); + std::vector src(channels); for (int i = 0; i < channels; ++i) - d_src[i] = cv::gpu::GpuMat(size, depth, cv::Scalar::all(i)); + src[i] = cv::Mat(size, depth, cv::Scalar::all(i)); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + std::vector d_src(channels); + for (int i = 0; i < channels; ++i) + d_src[i].upload(src[i]); - cv::gpu::merge(d_src, d_dst); + cv::gpu::GpuMat d_dst; - TEST_CYCLE() - { cv::gpu::merge(d_src, d_dst); + + TEST_CYCLE() + { + cv::gpu::merge(d_src, d_dst); + } + } + else + { + cv::Mat dst; + + cv::merge(src, dst); + + TEST_CYCLE() + { + cv::merge(src, dst); + } } } @@ -35,19 +53,35 @@ PERF_TEST_P(Sz_Depth_Cn, Core_Merge, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_D PERF_TEST_P(Sz_Depth_Cn, Core_Split, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, Values(2, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - cv::gpu::GpuMat d_src(size, CV_MAKE_TYPE(depth, channels), cv::Scalar(1, 2, 3, 4)); + cv::Mat src(size, CV_MAKE_TYPE(depth, channels), cv::Scalar(1, 2, 3, 4)); - std::vector d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); - cv::gpu::split(d_src, d_dst); + std::vector d_dst; - TEST_CYCLE() - { cv::gpu::split(d_src, d_dst); + + TEST_CYCLE() + { + cv::gpu::split(d_src, d_dst); + } + } + else + { + std::vector dst; + + cv::split(src, dst); + + TEST_CYCLE() + { + cv::split(src, dst); + } } } @@ -56,8 +90,8 @@ PERF_TEST_P(Sz_Depth_Cn, Core_Split, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_D PERF_TEST_P(Sz_Depth, Core_AddMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -65,15 +99,29 @@ PERF_TEST_P(Sz_Depth, Core_AddMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEP cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::add(d_src1, d_src2, d_dst); + cv::gpu::add(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::add(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::add(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::add(src1, src2, dst); + + TEST_CYCLE() + { + cv::add(src1, src2, dst); + } } } @@ -82,22 +130,36 @@ PERF_TEST_P(Sz_Depth, Core_AddMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEP PERF_TEST_P(Sz_Depth, Core_AddScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::add(d_src, s, d_dst); + cv::gpu::add(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::add(d_src, s, d_dst); + } + } + else { - cv::gpu::add(d_src, s, d_dst); + cv::Mat dst; + + cv::add(src, s, dst); + + TEST_CYCLE() + { + cv::add(src, s, dst); + } } } @@ -106,8 +168,8 @@ PERF_TEST_P(Sz_Depth, Core_AddScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_ PERF_TEST_P(Sz_Depth, Core_SubtractMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -115,15 +177,29 @@ PERF_TEST_P(Sz_Depth, Core_SubtractMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MA cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::subtract(d_src1, d_src2, d_dst); + cv::gpu::subtract(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::subtract(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::subtract(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::subtract(src1, src2, dst); + + TEST_CYCLE() + { + cv::subtract(src1, src2, dst); + } } } @@ -132,22 +208,36 @@ PERF_TEST_P(Sz_Depth, Core_SubtractMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MA PERF_TEST_P(Sz_Depth, Core_SubtractScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::subtract(d_src, s, d_dst); + cv::gpu::subtract(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::subtract(d_src, s, d_dst); + } + } + else { - cv::gpu::subtract(d_src, s, d_dst); + cv::Mat dst; + + cv::subtract(src, s, dst); + + TEST_CYCLE() + { + cv::subtract(src, s, dst); + } } } @@ -156,8 +246,8 @@ PERF_TEST_P(Sz_Depth, Core_SubtractScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM PERF_TEST_P(Sz_Depth, Core_MultiplyMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -165,15 +255,29 @@ PERF_TEST_P(Sz_Depth, Core_MultiplyMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MA cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::multiply(d_src1, d_src2, d_dst); + cv::gpu::multiply(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::multiply(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::multiply(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::multiply(src1, src2, dst); + + TEST_CYCLE() + { + cv::multiply(src1, src2, dst); + } } } @@ -182,22 +286,36 @@ PERF_TEST_P(Sz_Depth, Core_MultiplyMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MA PERF_TEST_P(Sz_Depth, Core_MultiplyScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::multiply(d_src, s, d_dst); + cv::gpu::multiply(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::multiply(d_src, s, d_dst); + } + } + else { - cv::gpu::multiply(d_src, s, d_dst); + cv::Mat dst; + + cv::multiply(src, s, dst); + + TEST_CYCLE() + { + cv::multiply(src, s, dst); + } } } @@ -206,8 +324,8 @@ PERF_TEST_P(Sz_Depth, Core_MultiplyScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM PERF_TEST_P(Sz_Depth, Core_DivideMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -215,15 +333,29 @@ PERF_TEST_P(Sz_Depth, Core_DivideMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_ cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::divide(d_src1, d_src2, d_dst); + cv::gpu::divide(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::divide(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::divide(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::divide(src1, src2, dst); + + TEST_CYCLE() + { + cv::divide(src1, src2, dst); + } } } @@ -232,22 +364,36 @@ PERF_TEST_P(Sz_Depth, Core_DivideMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_ PERF_TEST_P(Sz_Depth, Core_DivideScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::divide(d_src, s, d_dst); + cv::gpu::divide(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::divide(d_src, s, d_dst); + } + } + else { - cv::gpu::divide(d_src, s, d_dst); + cv::Mat dst; + + cv::divide(src, s, dst); + + TEST_CYCLE() + { + cv::divide(src, s, dst); + } } } @@ -256,22 +402,36 @@ PERF_TEST_P(Sz_Depth, Core_DivideScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_M PERF_TEST_P(Sz_Depth, Core_DivideScalarInv, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); double s = 100.0; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::divide(s, d_src, d_dst); + cv::gpu::divide(s, d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::divide(s, d_src, d_dst); + } + } + else { - cv::gpu::divide(s, d_src, d_dst); + cv::Mat dst; + + cv::divide(s, src, dst); + + TEST_CYCLE() + { + cv::divide(s, src, dst); + } } } @@ -280,8 +440,8 @@ PERF_TEST_P(Sz_Depth, Core_DivideScalarInv, Combine(GPU_TYPICAL_MAT_SIZES, ARITH PERF_TEST_P(Sz_Depth, Core_AbsDiffMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -289,15 +449,29 @@ PERF_TEST_P(Sz_Depth, Core_AbsDiffMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::absdiff(d_src1, d_src2, d_dst); + cv::gpu::absdiff(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::absdiff(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::absdiff(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::absdiff(src1, src2, dst); + + TEST_CYCLE() + { + cv::absdiff(src1, src2, dst); + } } } @@ -306,22 +480,36 @@ PERF_TEST_P(Sz_Depth, Core_AbsDiffMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT PERF_TEST_P(Sz_Depth, Core_AbsDiffScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::absdiff(d_src, s, d_dst); + cv::gpu::absdiff(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::absdiff(d_src, s, d_dst); + } + } + else { - cv::gpu::absdiff(d_src, s, d_dst); + cv::Mat dst; + + cv::absdiff(src, s, dst); + + TEST_CYCLE() + { + cv::absdiff(src, s, dst); + } } } @@ -330,20 +518,27 @@ PERF_TEST_P(Sz_Depth, Core_AbsDiffScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_ PERF_TEST_P(Sz_Depth, Core_Abs, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_16S, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::abs(d_src, d_dst); + cv::gpu::abs(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::abs(d_src, d_dst); + } + } + else { - cv::gpu::abs(d_src, d_dst); + FAIL(); } } @@ -352,20 +547,27 @@ PERF_TEST_P(Sz_Depth, Core_Abs, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_16S, CV PERF_TEST_P(Sz_Depth, Core_Sqr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::sqr(d_src, d_dst); + cv::gpu::sqr(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::sqr(d_src, d_dst); + } + } + else { - cv::gpu::sqr(d_src, d_dst); + FAIL(); } } @@ -374,20 +576,34 @@ PERF_TEST_P(Sz_Depth, Core_Sqr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_ PERF_TEST_P(Sz_Depth, Core_Sqrt, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::sqrt(d_src, d_dst); + cv::gpu::sqrt(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::sqrt(d_src, d_dst); + } + } + else { - cv::gpu::sqrt(d_src, d_dst); + cv::Mat dst; + + cv::sqrt(src, dst); + + TEST_CYCLE() + { + cv::sqrt(src, dst); + } } } @@ -396,20 +612,34 @@ PERF_TEST_P(Sz_Depth, Core_Sqrt, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV PERF_TEST_P(Sz_Depth, Core_Log, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src, 1.0, 255.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::log(d_src, d_dst); + cv::gpu::log(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::log(d_src, d_dst); + } + } + else { - cv::gpu::log(d_src, d_dst); + cv::Mat dst; + + cv::log(src, dst); + + TEST_CYCLE() + { + cv::log(src, dst); + } } } @@ -418,20 +648,34 @@ PERF_TEST_P(Sz_Depth, Core_Log, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_ PERF_TEST_P(Sz_Depth, Core_Exp, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src, 1.0, 10.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::exp(d_src, d_dst); + cv::gpu::exp(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::exp(d_src, d_dst); + } + } + else { - cv::gpu::exp(d_src, d_dst); + cv::Mat dst; + + cv::exp(src, dst); + + TEST_CYCLE() + { + cv::exp(src, dst); + } } } @@ -442,21 +686,35 @@ DEF_PARAM_TEST(Sz_Depth_Power, cv::Size, MatDepth, double); PERF_TEST_P(Sz_Depth_Power, Core_Pow, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16S, CV_32F), Values(0.3, 2.0, 2.4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - double power = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const double power = GET_PARAM(2); cv::Mat src(size, depth); fillRandom(src, 1.0, 10.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pow(d_src, power, d_dst); + cv::gpu::pow(d_src, power, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::pow(d_src, power, d_dst); + } + } + else { - cv::gpu::pow(d_src, power, d_dst); + cv::Mat dst; + + cv::pow(src, power, dst); + + TEST_CYCLE() + { + cv::pow(src, power, dst); + } } } @@ -470,9 +728,9 @@ DEF_PARAM_TEST(Sz_Depth_Code, cv::Size, MatDepth, CmpCode); PERF_TEST_P(Sz_Depth_Code, Core_CompareMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, ALL_CMP_CODES)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int cmp_code = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int cmp_code = GET_PARAM(2); cv::Mat src1(size, depth); fillRandom(src1); @@ -480,15 +738,29 @@ PERF_TEST_P(Sz_Depth_Code, Core_CompareMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITH cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); + cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); + } + } + else { - cv::gpu::compare(d_src1, d_src2, d_dst, cmp_code); + cv::Mat dst; + + cv::compare(src1, src2, dst, cmp_code); + + TEST_CYCLE() + { + cv::compare(src1, src2, dst, cmp_code); + } } } @@ -497,23 +769,37 @@ PERF_TEST_P(Sz_Depth_Code, Core_CompareMat, Combine(GPU_TYPICAL_MAT_SIZES, ARITH PERF_TEST_P(Sz_Depth_Code, Core_CompareScalar, Combine(GPU_TYPICAL_MAT_SIZES, ARITHM_MAT_DEPTH, ALL_CMP_CODES)) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int cmp_code = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int cmp_code = GET_PARAM(2); cv::Mat src(size, depth); fillRandom(src); cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::compare(d_src, s, d_dst, cmp_code); + cv::gpu::compare(d_src, s, d_dst, cmp_code); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::compare(d_src, s, d_dst, cmp_code); + } + } + else { - cv::gpu::compare(d_src, s, d_dst, cmp_code); + cv::Mat dst; + + cv::compare(src, s, dst, cmp_code); + + TEST_CYCLE() + { + cv::compare(src, s, dst, cmp_code); + } } } @@ -522,20 +808,34 @@ PERF_TEST_P(Sz_Depth_Code, Core_CompareScalar, Combine(GPU_TYPICAL_MAT_SIZES, AR PERF_TEST_P(Sz_Depth, Core_BitwiseNot, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_not(d_src, d_dst); + cv::gpu::bitwise_not(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_not(d_src, d_dst); + } + } + else { - cv::gpu::bitwise_not(d_src, d_dst); + cv::Mat dst; + + cv::bitwise_not(src, dst); + + TEST_CYCLE() + { + cv::bitwise_not(src, dst); + } } } @@ -544,8 +844,8 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseNot, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_ PERF_TEST_P(Sz_Depth, Core_BitwiseAndMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -553,15 +853,29 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseAndMat, Combine(GPU_TYPICAL_MAT_SIZES, Values( cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_and(d_src1, d_src2, d_dst); + cv::gpu::bitwise_and(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_and(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::bitwise_and(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::bitwise_and(src1, src2, dst); + + TEST_CYCLE() + { + cv::bitwise_and(src1, src2, dst); + } } } @@ -570,25 +884,39 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseAndMat, Combine(GPU_TYPICAL_MAT_SIZES, Values( PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseAndScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - int type = CV_MAKE_TYPE(depth, channels); + const int type = CV_MAKE_TYPE(depth, channels); cv::Mat src(size, type); fillRandom(src); cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_and(d_src, s, d_dst); + cv::gpu::bitwise_and(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_and(d_src, s, d_dst); + } + } + else { - cv::gpu::bitwise_and(d_src, s, d_dst); + cv::Mat dst; + + cv::bitwise_and(src, s, dst); + + TEST_CYCLE() + { + cv::bitwise_and(src, s, dst); + } } } @@ -597,8 +925,8 @@ PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseAndScalar, Combine(GPU_TYPICAL_MAT_SIZES, V PERF_TEST_P(Sz_Depth, Core_BitwiseOrMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -606,15 +934,29 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseOrMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(C cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_or(d_src1, d_src2, d_dst); + cv::gpu::bitwise_or(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_or(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::bitwise_or(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::bitwise_or(src1, src2, dst); + + TEST_CYCLE() + { + cv::bitwise_or(src1, src2, dst); + } } } @@ -623,25 +965,39 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseOrMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(C PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseOrScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - int type = CV_MAKE_TYPE(depth, channels); + const int type = CV_MAKE_TYPE(depth, channels); cv::Mat src(size, type); fillRandom(src); cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_or(d_src, s, d_dst); + cv::gpu::bitwise_or(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_or(d_src, s, d_dst); + } + } + else { - cv::gpu::bitwise_or(d_src, s, d_dst); + cv::Mat dst; + + cv::bitwise_or(src, s, dst); + + TEST_CYCLE() + { + cv::bitwise_or(src, s, dst); + } } } @@ -650,8 +1006,8 @@ PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseOrScalar, Combine(GPU_TYPICAL_MAT_SIZES, Va PERF_TEST_P(Sz_Depth, Core_BitwiseXorMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -659,15 +1015,29 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseXorMat, Combine(GPU_TYPICAL_MAT_SIZES, Values( cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); + cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::bitwise_xor(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::bitwise_xor(src1, src2, dst); + + TEST_CYCLE() + { + cv::bitwise_xor(src1, src2, dst); + } } } @@ -676,25 +1046,39 @@ PERF_TEST_P(Sz_Depth, Core_BitwiseXorMat, Combine(GPU_TYPICAL_MAT_SIZES, Values( PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseXorScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - int type = CV_MAKE_TYPE(depth, channels); + const int type = CV_MAKE_TYPE(depth, channels); cv::Mat src(size, type); fillRandom(src); cv::Scalar s = cv::Scalar::all(100); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::bitwise_xor(d_src, s, d_dst); + cv::gpu::bitwise_xor(d_src, s, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::bitwise_xor(d_src, s, d_dst); + } + } + else { - cv::gpu::bitwise_xor(d_src, s, d_dst); + cv::Mat dst; + + cv::bitwise_xor(src, s, dst); + + TEST_CYCLE() + { + cv::bitwise_xor(src, s, dst); + } } } @@ -703,25 +1087,32 @@ PERF_TEST_P(Sz_Depth_Cn, Core_BitwiseXorScalar, Combine(GPU_TYPICAL_MAT_SIZES, V PERF_TEST_P(Sz_Depth_Cn, Core_RShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - int type = CV_MAKE_TYPE(depth, channels); + const int type = CV_MAKE_TYPE(depth, channels); cv::Mat src(size, type); fillRandom(src); - cv::Scalar_ val = cv::Scalar_::all(4); + const cv::Scalar_ val = cv::Scalar_::all(4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::rshift(d_src, val, d_dst); + cv::gpu::rshift(d_src, val, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::rshift(d_src, val, d_dst); + } + } + else { - cv::gpu::rshift(d_src, val, d_dst); + FAIL(); } } @@ -730,25 +1121,32 @@ PERF_TEST_P(Sz_Depth_Cn, Core_RShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8 PERF_TEST_P(Sz_Depth_Cn, Core_LShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32S), Values(1, 3, 4))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); - int channels = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); + const int channels = GET_PARAM(2); - int type = CV_MAKE_TYPE(depth, channels); + const int type = CV_MAKE_TYPE(depth, channels); cv::Mat src(size, type); fillRandom(src); - cv::Scalar_ val = cv::Scalar_::all(4); + const cv::Scalar_ val = cv::Scalar_::all(4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::lshift(d_src, val, d_dst); + cv::gpu::lshift(d_src, val, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::lshift(d_src, val, d_dst); + } + } + else { - cv::gpu::lshift(d_src, val, d_dst); + FAIL(); } } @@ -757,8 +1155,8 @@ PERF_TEST_P(Sz_Depth_Cn, Core_LShift, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8 PERF_TEST_P(Sz_Depth, Core_MinMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -766,15 +1164,29 @@ PERF_TEST_P(Sz_Depth, Core_MinMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::min(d_src1, d_src2, d_dst); + cv::gpu::min(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::min(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::min(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::min(src1, src2, dst); + + TEST_CYCLE() + { + cv::min(src1, src2, dst); + } } } @@ -783,22 +1195,36 @@ PERF_TEST_P(Sz_Depth, Core_MinMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, PERF_TEST_P(Sz_Depth, Core_MinScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - double val = 50.0; + const double val = 50.0; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::min(d_src, val, d_dst); + cv::gpu::min(d_src, val, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::min(d_src, val, d_dst); + } + } + else { - cv::gpu::min(d_src, val, d_dst); + cv::Mat dst; + + cv::min(src, val, dst); + + TEST_CYCLE() + { + cv::min(src, val, dst); + } } } @@ -807,8 +1233,8 @@ PERF_TEST_P(Sz_Depth, Core_MinScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8 PERF_TEST_P(Sz_Depth, Core_MaxMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src1(size, depth); fillRandom(src1); @@ -816,15 +1242,29 @@ PERF_TEST_P(Sz_Depth, Core_MaxMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, cv::Mat src2(size, depth); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::max(d_src1, d_src2, d_dst); + cv::gpu::max(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::max(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::max(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::max(src1, src2, dst); + + TEST_CYCLE() + { + cv::max(src1, src2, dst); + } } } @@ -833,22 +1273,36 @@ PERF_TEST_P(Sz_Depth, Core_MaxMat, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, PERF_TEST_P(Sz_Depth, Core_MaxScalar, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U, CV_16U, CV_32F))) { - cv::Size size = GET_PARAM(0); - int depth = GET_PARAM(1); + const cv::Size size = GET_PARAM(0); + const int depth = GET_PARAM(1); cv::Mat src(size, depth); fillRandom(src); - double val = 50.0; + const double val = 50.0; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::max(d_src, val, d_dst); + cv::gpu::max(d_src, val, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::max(d_src, val, d_dst); + } + } + else { - cv::gpu::max(d_src, val, d_dst); + cv::Mat dst; + + cv::max(src, val, dst); + + TEST_CYCLE() + { + cv::max(src, val, dst); + } } } @@ -863,10 +1317,10 @@ PERF_TEST_P(Sz_3Depth, Core_AddWeighted, Combine( Values(CV_8U, CV_16U, CV_32F, CV_64F), Values(CV_8U, CV_16U, CV_32F, CV_64F))) { - cv::Size size = GET_PARAM(0); - int depth1 = GET_PARAM(1); - int depth2 = GET_PARAM(2); - int dst_depth = GET_PARAM(3); + const cv::Size size = GET_PARAM(0); + const int depth1 = GET_PARAM(1); + const int depth2 = GET_PARAM(2); + const int dst_depth = GET_PARAM(3); cv::Mat src1(size, depth1); fillRandom(src1); @@ -874,15 +1328,29 @@ PERF_TEST_P(Sz_3Depth, Core_AddWeighted, Combine( cv::Mat src2(size, depth2); fillRandom(src2); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); + cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); + } + } + else { - cv::gpu::addWeighted(d_src1, 0.5, d_src2, 0.5, 10.0, d_dst, dst_depth); + cv::Mat dst; + + cv::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); + + TEST_CYCLE() + { + cv::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); + } } } @@ -901,9 +1369,9 @@ PERF_TEST_P(Sz_Type_Flags, Core_GEMM, Combine( { declare.time(5.0); - cv::Size size = GET_PARAM(0); - int type = GET_PARAM(1); - int flags = GET_PARAM(2); + const cv::Size size = GET_PARAM(0); + const int type = GET_PARAM(1); + const int flags = GET_PARAM(2); cv::Mat src1(size, type); fillRandom(src1); @@ -914,16 +1382,32 @@ PERF_TEST_P(Sz_Type_Flags, Core_GEMM, Combine( cv::Mat src3(size, type); fillRandom(src3); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_src3(src3); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_src3(src3); + cv::gpu::GpuMat d_dst; - cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); + cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); + } + } + else { - cv::gpu::gemm(d_src1, d_src2, 1.0, d_src3, 1.0, d_dst, flags); + cv::Mat dst; + + cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); + + declare.time(50.0); + + TEST_CYCLE() + { + cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); + } } } @@ -940,14 +1424,28 @@ PERF_TEST_P(Sz_Type, Core_Transpose, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::transpose(d_src, d_dst); + cv::gpu::transpose(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::transpose(d_src, d_dst); + } + } + else { - cv::gpu::transpose(d_src, d_dst); + cv::Mat dst; + + cv::transpose(src, dst); + + TEST_CYCLE() + { + cv::transpose(src, dst); + } } } @@ -976,14 +1474,28 @@ PERF_TEST_P(Sz_Depth_Cn_Code, Core_Flip, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::flip(d_src, d_dst, flipCode); + cv::gpu::flip(d_src, d_dst, flipCode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::flip(d_src, d_dst, flipCode); + } + } + else { - cv::gpu::flip(d_src, d_dst, flipCode); + cv::Mat dst; + + cv::flip(src, dst, flipCode); + + TEST_CYCLE() + { + cv::flip(src, dst, flipCode); + } } } @@ -1003,14 +1515,28 @@ PERF_TEST_P(Sz_Type, Core_LutOneChannel, Combine( cv::Mat lut(1, 256, CV_8UC1); fillRandom(lut); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::LUT(d_src, lut, d_dst); + cv::gpu::LUT(d_src, lut, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::LUT(d_src, lut, d_dst); + } + } + else { - cv::gpu::LUT(d_src, lut, d_dst); + cv::Mat dst; + + cv::LUT(src, lut, dst); + + TEST_CYCLE() + { + cv::LUT(src, lut, dst); + } } } @@ -1030,14 +1556,28 @@ PERF_TEST_P(Sz_Type, Core_LutMultiChannel, Combine( cv::Mat lut(1, 256, CV_MAKE_TYPE(CV_8U, src.channels())); fillRandom(lut); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::LUT(d_src, lut, d_dst); + cv::gpu::LUT(d_src, lut, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::LUT(d_src, lut, d_dst); + } + } + else { - cv::gpu::LUT(d_src, lut, d_dst); + cv::Mat dst; + + cv::LUT(src, lut, dst); + + TEST_CYCLE() + { + cv::LUT(src, lut, dst); + } } } @@ -1051,14 +1591,31 @@ PERF_TEST_P(Sz, Core_MagnitudeComplex, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_32FC2); fillRandom(src, -100.0, 100.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitude(d_src, d_dst); + cv::gpu::magnitude(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::magnitude(d_src, d_dst); + } + } + else { - cv::gpu::magnitude(d_src, d_dst); + cv::Mat xy[2]; + cv::split(src, xy); + + cv::Mat dst; + + cv::magnitude(xy[0], xy[1], dst); + + TEST_CYCLE() + { + cv::magnitude(xy[0], xy[1], dst); + } } } @@ -1072,14 +1629,21 @@ PERF_TEST_P(Sz, Core_MagnitudeSqrComplex, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_32FC2); fillRandom(src, -100.0, 100.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitudeSqr(d_src, d_dst); + cv::gpu::magnitudeSqr(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::magnitudeSqr(d_src, d_dst); + } + } + else { - cv::gpu::magnitudeSqr(d_src, d_dst); + FAIL(); } } @@ -1096,15 +1660,29 @@ PERF_TEST_P(Sz, Core_Magnitude, GPU_TYPICAL_MAT_SIZES) cv::Mat src2(size, CV_32FC1); fillRandom(src2, -100.0, 100.0); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitude(d_src1, d_src2, d_dst); + cv::gpu::magnitude(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::magnitude(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::magnitude(d_src1, d_src2, d_dst); + cv::Mat dst; + + cv::magnitude(src1, src2, dst); + + TEST_CYCLE() + { + cv::magnitude(src1, src2, dst); + } } } @@ -1121,15 +1699,22 @@ PERF_TEST_P(Sz, Core_MagnitudeSqr, GPU_TYPICAL_MAT_SIZES) cv::Mat src2(size, CV_32FC1); fillRandom(src2, -100.0, 100.0); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); + cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); + } + } + else { - cv::gpu::magnitudeSqr(d_src1, d_src2, d_dst); + FAIL(); } } @@ -1149,15 +1734,29 @@ PERF_TEST_P(Sz_AngleInDegrees, Core_Phase, Combine(GPU_TYPICAL_MAT_SIZES, Bool() cv::Mat src2(size, CV_32FC1); fillRandom(src2, -100.0, 100.0); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); + cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); + } + } + else { - cv::gpu::phase(d_src1, d_src2, d_dst, angleInDegrees); + cv::Mat dst; + + cv::phase(src1, src2, dst, angleInDegrees); + + TEST_CYCLE() + { + cv::phase(src1, src2, dst, angleInDegrees); + } } } @@ -1175,16 +1774,31 @@ PERF_TEST_P(Sz_AngleInDegrees, Core_CartToPolar, Combine(GPU_TYPICAL_MAT_SIZES, cv::Mat src2(size, CV_32FC1); fillRandom(src2, -100.0, 100.0); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_magnitude; - cv::gpu::GpuMat d_angle; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_magnitude; + cv::gpu::GpuMat d_angle; - cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); + cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); + } + } + else { - cv::gpu::cartToPolar(d_src1, d_src2, d_magnitude, d_angle, angleInDegrees); + cv::Mat magnitude; + cv::Mat angle; + + cv::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); + + TEST_CYCLE() + { + cv::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); + } } } @@ -1202,16 +1816,31 @@ PERF_TEST_P(Sz_AngleInDegrees, Core_PolarToCart, Combine(GPU_TYPICAL_MAT_SIZES, cv::Mat angle(size, CV_32FC1); fillRandom(angle, 0.0, angleInDegrees ? 360.0 : 2 * CV_PI); - cv::gpu::GpuMat d_magnitude(magnitude); - cv::gpu::GpuMat d_angle(angle); - cv::gpu::GpuMat d_x; - cv::gpu::GpuMat d_y; + if (runOnGpu) + { + cv::gpu::GpuMat d_magnitude(magnitude); + cv::gpu::GpuMat d_angle(angle); + cv::gpu::GpuMat d_x; + cv::gpu::GpuMat d_y; - cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); + cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); + } + } + else { - cv::gpu::polarToCart(d_magnitude, d_angle, d_x, d_y, angleInDegrees); + cv::Mat x; + cv::Mat y; + + cv::polarToCart(magnitude, angle, x, y, angleInDegrees); + + TEST_CYCLE() + { + cv::polarToCart(magnitude, angle, x, y, angleInDegrees); + } } } @@ -1228,14 +1857,26 @@ PERF_TEST_P(Sz, Core_MeanStdDev, GPU_TYPICAL_MAT_SIZES) cv::Scalar mean; cv::Scalar stddev; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); + cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); + } + } + else { - cv::gpu::meanStdDev(d_src, mean, stddev, d_buf); + cv::meanStdDev(src, mean, stddev); + + TEST_CYCLE() + { + cv::meanStdDev(src, mean, stddev); + } } } @@ -1258,14 +1899,26 @@ PERF_TEST_P(Sz_Depth_Norm, Core_Norm, Combine( double dst; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - dst = cv::gpu::norm(d_src, normType, d_buf); + dst = cv::gpu::norm(d_src, normType, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::norm(d_src, normType, d_buf); + } + } + else { - dst = cv::gpu::norm(d_src, normType, d_buf); + dst = cv::norm(src, normType); + + TEST_CYCLE() + { + dst = cv::norm(src, normType); + } } } @@ -1289,14 +1942,26 @@ PERF_TEST_P(Sz_Norm, Core_NormDiff, Combine( double dst; - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); - dst = cv::gpu::norm(d_src1, d_src2, normType); + dst = cv::gpu::norm(d_src1, d_src2, normType); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::norm(d_src1, d_src2, normType); + } + } + else { - dst = cv::gpu::norm(d_src1, d_src2, normType); + dst = cv::norm(src1, src2, normType); + + TEST_CYCLE() + { + dst = cv::norm(src1, src2, normType); + } } } @@ -1319,14 +1984,26 @@ PERF_TEST_P(Sz_Depth_Cn, Core_Sum, Combine( cv::Scalar dst; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - dst = cv::gpu::sum(d_src, d_buf); + dst = cv::gpu::sum(d_src, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::sum(d_src, d_buf); + } + } + else { - dst = cv::gpu::sum(d_src, d_buf); + dst = cv::sum(src); + + TEST_CYCLE() + { + dst = cv::sum(src); + } } } @@ -1349,14 +2026,21 @@ PERF_TEST_P(Sz_Depth_Cn, Core_SumAbs, Combine( cv::Scalar dst; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - dst = cv::gpu::absSum(d_src, d_buf); + dst = cv::gpu::absSum(d_src, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::absSum(d_src, d_buf); + } + } + else { - dst = cv::gpu::absSum(d_src, d_buf); + FAIL(); } } @@ -1379,14 +2063,21 @@ PERF_TEST_P(Sz_Depth_Cn, Core_SumSqr, Combine( cv::Scalar dst; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - dst = cv::gpu::sqrSum(d_src, d_buf); + dst = cv::gpu::sqrSum(d_src, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::sqrSum(d_src, d_buf); + } + } + else { - dst = cv::gpu::sqrSum(d_src, d_buf); + FAIL(); } } @@ -1405,14 +2096,21 @@ PERF_TEST_P(Sz_Depth, Core_MinMax, Combine( double minVal, maxVal; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); + cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); + } + } + else { - cv::gpu::minMax(d_src, &minVal, &maxVal, cv::gpu::GpuMat(), d_buf); + FAIL(); } } @@ -1432,14 +2130,26 @@ PERF_TEST_P(Sz_Depth, Core_MinMaxLoc, Combine( double minVal, maxVal; cv::Point minLoc, maxLoc; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_valbuf, d_locbuf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_valbuf, d_locbuf; - cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); + cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); + } + } + else { - cv::gpu::minMaxLoc(d_src, &minVal, &maxVal, &minLoc, &maxLoc, cv::gpu::GpuMat(), d_valbuf, d_locbuf); + cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); + + TEST_CYCLE() + { + cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); + } } } @@ -1458,14 +2168,26 @@ PERF_TEST_P(Sz_Depth, Core_CountNonZero, Combine( int dst; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_buf; - dst = cv::gpu::countNonZero(d_src, d_buf); + dst = cv::gpu::countNonZero(d_src, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + dst = cv::gpu::countNonZero(d_src, d_buf); + } + } + else { - dst = cv::gpu::countNonZero(d_src, d_buf); + dst = cv::countNonZero(src); + + TEST_CYCLE() + { + dst = cv::countNonZero(src); + } } } @@ -1499,14 +2221,28 @@ PERF_TEST_P(Sz_Depth_Cn_Code_Dim, Core_Reduce, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::reduce(d_src, d_dst, dim, reduceOp); + cv::gpu::reduce(d_src, d_dst, dim, reduceOp); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::reduce(d_src, d_dst, dim, reduceOp); + } + } + else { - cv::gpu::reduce(d_src, d_dst, dim, reduceOp); + cv::Mat dst; + + cv::reduce(src, dst, dim, reduceOp); + + TEST_CYCLE() + { + cv::reduce(src, dst, dim, reduceOp); + } } } diff --git a/modules/gpu/perf/perf_features2d.cpp b/modules/gpu/perf/perf_features2d.cpp index ef9612b549..53b98d4628 100644 --- a/modules/gpu/perf/perf_features2d.cpp +++ b/modules/gpu/perf/perf_features2d.cpp @@ -12,21 +12,39 @@ DEF_PARAM_TEST_1(Image, string); PERF_TEST_P(Image, Features2D_SURF, Values("gpu/perf/aloe.jpg")) { - declare.time(2.0); + declare.time(50.0); cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::gpu::SURF_GPU d_surf; + if (runOnGpu) + { + cv::gpu::SURF_GPU d_surf; - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_keypoints, d_descriptors; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints, d_descriptors; - d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); - TEST_CYCLE() + TEST_CYCLE() + { + d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + } + } + else { - d_surf(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + cv::SURF surf; + + std::vector keypoints; + cv::Mat descriptors; + + surf(img, cv::noArray(), keypoints, descriptors); + + TEST_CYCLE() + { + keypoints.clear(); + surf(img, cv::noArray(), keypoints, descriptors); + } } } @@ -38,16 +56,31 @@ PERF_TEST_P(Image, Features2D_FAST, Values("gpu/perf/aloe.jpg")) cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::gpu::FAST_GPU d_fast(20); + if (runOnGpu) + { + cv::gpu::FAST_GPU d_fast(20); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_keypoints; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints; - d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); + d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); - TEST_CYCLE() + TEST_CYCLE() + { + d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); + } + } + else { - d_fast(d_img, cv::gpu::GpuMat(), d_keypoints); + std::vector keypoints; + + cv::FAST(img, keypoints, 20); + + TEST_CYCLE() + { + keypoints.clear(); + cv::FAST(img, keypoints, 20); + } } } @@ -59,16 +92,34 @@ PERF_TEST_P(Image, Features2D_ORB, Values("gpu/perf/aloe.jpg")) cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::gpu::ORB_GPU d_orb(4000); + if (runOnGpu) + { + cv::gpu::ORB_GPU d_orb(4000); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_keypoints, d_descriptors; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_keypoints, d_descriptors; - d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); - TEST_CYCLE() + TEST_CYCLE() + { + d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + } + } + else { - d_orb(d_img, cv::gpu::GpuMat(), d_keypoints, d_descriptors); + cv::ORB orb(4000); + + std::vector keypoints; + cv::Mat descriptors; + + orb(img, cv::noArray(), keypoints, descriptors); + + TEST_CYCLE() + { + keypoints.clear(); + orb(img, cv::noArray(), keypoints, descriptors); + } } } @@ -79,7 +130,7 @@ DEF_PARAM_TEST(DescSize_Norm, int, NormType); PERF_TEST_P(DescSize_Norm, Features2D_BFMatch, Combine(Values(64, 128, 256), Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - declare.time(3.0); + declare.time(20.0); int desc_size = GET_PARAM(0); int normType = GET_PARAM(1); @@ -92,17 +143,33 @@ PERF_TEST_P(DescSize_Norm, Features2D_BFMatch, Combine(Values(64, 128, 256), Val cv::Mat train(3000, desc_size, type); fillRandom(train); - cv::gpu::BFMatcher_GPU d_matcher(normType); + if (runOnGpu) + { + cv::gpu::BFMatcher_GPU d_matcher(normType); - cv::gpu::GpuMat d_query(query); - cv::gpu::GpuMat d_train(train); - cv::gpu::GpuMat d_trainIdx, d_distance; + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_distance; - d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); + d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); - TEST_CYCLE() + TEST_CYCLE() + { + d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); + } + } + else { - d_matcher.matchSingle(d_query, d_train, d_trainIdx, d_distance); + cv::BFMatcher matcher(normType); + + std::vector matches; + + matcher.match(query, train, matches); + + TEST_CYCLE() + { + matcher.match(query, train, matches); + } } } @@ -116,7 +183,7 @@ PERF_TEST_P(DescSize_K_Norm, Features2D_BFKnnMatch, Combine( Values(2, 3), Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - declare.time(3.0); + declare.time(30.0); int desc_size = GET_PARAM(0); int k = GET_PARAM(1); @@ -130,17 +197,33 @@ PERF_TEST_P(DescSize_K_Norm, Features2D_BFKnnMatch, Combine( cv::Mat train(3000, desc_size, type); fillRandom(train); - cv::gpu::BFMatcher_GPU d_matcher(normType); + if (runOnGpu) + { + cv::gpu::BFMatcher_GPU d_matcher(normType); - cv::gpu::GpuMat d_query(query); - cv::gpu::GpuMat d_train(train); - cv::gpu::GpuMat d_trainIdx, d_distance, d_allDist; + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_distance, d_allDist; - d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); + d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); - TEST_CYCLE() + TEST_CYCLE() + { + d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); + } + } + else { - d_matcher.knnMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_allDist, k); + cv::BFMatcher matcher(normType); + + std::vector< std::vector > matches; + + matcher.knnMatch(query, train, matches, k); + + TEST_CYCLE() + { + matcher.knnMatch(query, train, matches, k); + } } } @@ -149,7 +232,7 @@ PERF_TEST_P(DescSize_K_Norm, Features2D_BFKnnMatch, Combine( PERF_TEST_P(DescSize_Norm, Features2D_BFRadiusMatch, Combine(Values(64, 128, 256), Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))) { - declare.time(3.0); + declare.time(30.0); int desc_size = GET_PARAM(0); int normType = GET_PARAM(1); @@ -162,17 +245,33 @@ PERF_TEST_P(DescSize_Norm, Features2D_BFRadiusMatch, Combine(Values(64, 128, 256 cv::Mat train(3000, desc_size, type); fillRandom(train, 0.0, 1.0); - cv::gpu::BFMatcher_GPU d_matcher(normType); + if (runOnGpu) + { + cv::gpu::BFMatcher_GPU d_matcher(normType); - cv::gpu::GpuMat d_query(query); - cv::gpu::GpuMat d_train(train); - cv::gpu::GpuMat d_trainIdx, d_nMatches, d_distance; + cv::gpu::GpuMat d_query(query); + cv::gpu::GpuMat d_train(train); + cv::gpu::GpuMat d_trainIdx, d_nMatches, d_distance; - d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); + d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); - TEST_CYCLE() + TEST_CYCLE() + { + d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); + } + } + else { - d_matcher.radiusMatchSingle(d_query, d_train, d_trainIdx, d_distance, d_nMatches, 2.0); + cv::BFMatcher matcher(normType); + + std::vector< std::vector > matches; + + matcher.radiusMatch(query, train, matches, 2.0); + + TEST_CYCLE() + { + matcher.radiusMatch(query, train, matches, 2.0); + } } } diff --git a/modules/gpu/perf/perf_filters.cpp b/modules/gpu/perf/perf_filters.cpp index 71dcd49e2b..64ab829f8e 100644 --- a/modules/gpu/perf/perf_filters.cpp +++ b/modules/gpu/perf/perf_filters.cpp @@ -12,6 +12,8 @@ DEF_PARAM_TEST(Sz_Type_KernelSz, cv::Size, MatType, int); PERF_TEST_P(Sz_Type_KernelSz, Filters_Blur, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4), Values(3, 5, 7))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int ksize = GET_PARAM(2); @@ -19,14 +21,28 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Blur, Combine(GPU_TYPICAL_MAT_SIZES, Value cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); + cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); + } + } + else { - cv::gpu::blur(d_src, d_dst, cv::Size(ksize, ksize)); + cv::Mat dst; + + cv::blur(src, dst, cv::Size(ksize, ksize)); + + TEST_CYCLE() + { + cv::blur(src, dst, cv::Size(ksize, ksize)); + } } } @@ -35,6 +51,8 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Blur, Combine(GPU_TYPICAL_MAT_SIZES, Value PERF_TEST_P(Sz_Type_KernelSz, Filters_Sobel, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1), Values(3, 5, 7, 9, 11, 13, 15))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int ksize = GET_PARAM(2); @@ -42,15 +60,29 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Sobel, Combine(GPU_TYPICAL_MAT_SIZES, Valu cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); + cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); + } + } + else { - cv::gpu::Sobel(d_src, d_dst, -1, 1, 1, d_buf, ksize); + cv::Mat dst; + + cv::Sobel(src, dst, -1, 1, 1, ksize); + + TEST_CYCLE() + { + cv::Sobel(src, dst, -1, 1, 1, ksize); + } } } @@ -59,21 +91,37 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Sobel, Combine(GPU_TYPICAL_MAT_SIZES, Valu PERF_TEST_P(Sz_Type, Filters_Scharr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); + cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); + } + } + else { - cv::gpu::Scharr(d_src, d_dst, -1, 1, 0, d_buf); + cv::Mat dst; + + cv::Scharr(src, dst, -1, 1, 0); + + TEST_CYCLE() + { + cv::Scharr(src, dst, -1, 1, 0); + } } } @@ -82,6 +130,8 @@ PERF_TEST_P(Sz_Type, Filters_Scharr, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U PERF_TEST_P(Sz_Type_KernelSz, Filters_GaussianBlur, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1), Values(3, 5, 7, 9, 11, 13, 15))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int ksize = GET_PARAM(2); @@ -89,15 +139,29 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_GaussianBlur, Combine(GPU_TYPICAL_MAT_SIZE cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); + cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); + } + } + else { - cv::gpu::GaussianBlur(d_src, d_dst, cv::Size(ksize, ksize), d_buf, 0.5); + cv::Mat dst; + + cv::GaussianBlur(src, dst, cv::Size(ksize, ksize), 0.5); + + TEST_CYCLE() + { + cv::GaussianBlur(src, dst, cv::Size(ksize, ksize), 0.5); + } } } @@ -106,6 +170,8 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_GaussianBlur, Combine(GPU_TYPICAL_MAT_SIZE PERF_TEST_P(Sz_Type_KernelSz, Filters_Laplacian, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4), Values(1, 3))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int ksize = GET_PARAM(2); @@ -113,14 +179,28 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Laplacian, Combine(GPU_TYPICAL_MAT_SIZES, cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::Laplacian(d_src, d_dst, -1, ksize); + cv::gpu::Laplacian(d_src, d_dst, -1, ksize); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::Laplacian(d_src, d_dst, -1, ksize); + } + } + else { - cv::gpu::Laplacian(d_src, d_dst, -1, ksize); + cv::Mat dst; + + cv::Laplacian(src, dst, -1, ksize); + + TEST_CYCLE() + { + cv::Laplacian(src, dst, -1, ksize); + } } } @@ -129,6 +209,8 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Laplacian, Combine(GPU_TYPICAL_MAT_SIZES, PERF_TEST_P(Sz_Type, Filters_Erode, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); @@ -137,15 +219,29 @@ PERF_TEST_P(Sz_Type, Filters_Erode, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::erode(d_src, d_dst, ker, d_buf); + cv::gpu::erode(d_src, d_dst, ker, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::erode(d_src, d_dst, ker, d_buf); + } + } + else { - cv::gpu::erode(d_src, d_dst, ker, d_buf); + cv::Mat dst; + + cv::erode(src, dst, ker); + + TEST_CYCLE() + { + cv::erode(src, dst, ker); + } } } @@ -154,6 +250,8 @@ PERF_TEST_P(Sz_Type, Filters_Erode, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC PERF_TEST_P(Sz_Type, Filters_Dilate, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); @@ -162,15 +260,29 @@ PERF_TEST_P(Sz_Type, Filters_Dilate, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8U cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::dilate(d_src, d_dst, ker, d_buf); + cv::gpu::dilate(d_src, d_dst, ker, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::dilate(d_src, d_dst, ker, d_buf); + } + } + else { - cv::gpu::dilate(d_src, d_dst, ker, d_buf); + cv::Mat dst; + + cv::dilate(src, dst, ker); + + TEST_CYCLE() + { + cv::dilate(src, dst, ker); + } } } @@ -184,6 +296,8 @@ DEF_PARAM_TEST(Sz_Type_Op, cv::Size, MatType, MorphOp); PERF_TEST_P(Sz_Type_Op, Filters_MorphologyEx, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4), ALL_MORPH_OPS)) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int morphOp = GET_PARAM(2); @@ -193,16 +307,30 @@ PERF_TEST_P(Sz_Type_Op, Filters_MorphologyEx, Combine(GPU_TYPICAL_MAT_SIZES, Val cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf1; - cv::gpu::GpuMat d_buf2; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf1; + cv::gpu::GpuMat d_buf2; - cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); + cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); + } + } + else { - cv::gpu::morphologyEx(d_src, d_dst, morphOp, ker, d_buf1, d_buf2); + cv::Mat dst; + + cv::morphologyEx(src, dst, morphOp, ker); + + TEST_CYCLE() + { + cv::morphologyEx(src, dst, morphOp, ker); + } } } @@ -211,6 +339,8 @@ PERF_TEST_P(Sz_Type_Op, Filters_MorphologyEx, Combine(GPU_TYPICAL_MAT_SIZES, Val PERF_TEST_P(Sz_Type_KernelSz, Filters_Filter2D, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8UC1, CV_8UC4, CV_32FC1, CV_32FC4), Values(3, 5, 7, 9, 11, 13, 15))) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int type = GET_PARAM(1); int ksize = GET_PARAM(2); @@ -221,14 +351,28 @@ PERF_TEST_P(Sz_Type_KernelSz, Filters_Filter2D, Combine(GPU_TYPICAL_MAT_SIZES, V cv::Mat kernel(ksize, ksize, CV_32FC1); fillRandom(kernel, 0.0, 1.0); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::filter2D(d_src, d_dst, -1, kernel); + cv::gpu::filter2D(d_src, d_dst, -1, kernel); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::filter2D(d_src, d_dst, -1, kernel); + } + } + else { - cv::gpu::filter2D(d_src, d_dst, -1, kernel); + cv::Mat dst; + + cv::filter2D(src, dst, -1, kernel); + + TEST_CYCLE() + { + cv::filter2D(src, dst, -1, kernel); + } } } diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index a1614a79f3..9104892db4 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -59,7 +59,7 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border_Mode, ImgProc_Remap, Combine( ALL_BORDER_MODES, ALL_REMAP_MODES)) { - declare.time(3.0); + declare.time(20.0); cv::Size size = GET_PARAM(0); int depth = GET_PARAM(1); @@ -78,16 +78,30 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border_Mode, ImgProc_Remap, Combine( generateMap(xmap, ymap, remapMode); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_xmap(xmap); - cv::gpu::GpuMat d_ymap(ymap); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_xmap(xmap); + cv::gpu::GpuMat d_ymap(ymap); + cv::gpu::GpuMat d_dst; - cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); + cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); + } + } + else { - cv::gpu::remap(d_src, d_dst, d_xmap, d_ymap, interpolation, borderMode); + cv::Mat dst; + + cv::remap(src, dst, xmap, ymap, interpolation, borderMode); + + TEST_CYCLE() + { + cv::remap(src, dst, xmap, ymap, interpolation, borderMode); + } } } @@ -103,7 +117,7 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Scale, ImgProc_Resize, Combine( ALL_INTERPOLATIONS, Values(0.5, 0.3, 2.0))) { - declare.time(1.0); + declare.time(20.0); cv::Size size = GET_PARAM(0); int depth = GET_PARAM(1); @@ -116,14 +130,28 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Scale, ImgProc_Resize, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + } + } + else { - cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + cv::Mat dst; + + cv::resize(src, dst, cv::Size(), f, f, interpolation); + + TEST_CYCLE() + { + cv::resize(src, dst, cv::Size(), f, f, interpolation); + } } } @@ -151,14 +179,28 @@ PERF_TEST_P(Sz_Depth_Cn_Scale, ImgProc_ResizeArea, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + } + } + else { - cv::gpu::resize(d_src, d_dst, cv::Size(), f, f, interpolation); + cv::Mat dst; + + cv::resize(src, dst, cv::Size(), f, f, interpolation); + + TEST_CYCLE() + { + cv::resize(src, dst, cv::Size(), f, f, interpolation); + } } } @@ -174,6 +216,8 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpAffine, Combine( Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), ALL_BORDER_MODES)) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int depth = GET_PARAM(1); int channels = GET_PARAM(2); @@ -190,14 +234,28 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpAffine, Combine( {std::sin(aplha), std::cos(aplha), 0}}; cv::Mat M(2, 3, CV_64F, (void*) mat); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); + cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); + } + } + else { - cv::gpu::warpAffine(d_src, d_dst, M, size, interpolation, borderMode); + cv::Mat dst; + + cv::warpAffine(src, dst, M, size, interpolation, borderMode); + + TEST_CYCLE() + { + cv::warpAffine(src, dst, M, size, interpolation, borderMode); + } } } @@ -211,6 +269,8 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpPerspective, Combine( Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), ALL_BORDER_MODES)) { + declare.time(20.0); + cv::Size size = GET_PARAM(0); int depth = GET_PARAM(1); int channels = GET_PARAM(2); @@ -228,14 +288,28 @@ PERF_TEST_P(Sz_Depth_Cn_Inter_Border, ImgProc_WarpPerspective, Combine( {0.0, 0.0, 1.0}}; cv::Mat M(3, 3, CV_64F, (void*) mat); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); + cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); + } + } + else { - cv::gpu::warpPerspective(d_src, d_dst, M, size, interpolation, borderMode); + cv::Mat dst; + + cv::warpPerspective(src, dst, M, size, interpolation, borderMode); + + TEST_CYCLE() + { + cv::warpPerspective(src, dst, M, size, interpolation, borderMode); + } } } @@ -260,14 +334,28 @@ PERF_TEST_P(Sz_Depth_Cn_Border, ImgProc_CopyMakeBorder, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); + cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); + } + } + else { - cv::gpu::copyMakeBorder(d_src, d_dst, 5, 5, 5, 5, borderMode); + cv::Mat dst; + + cv::copyMakeBorder(src, dst, 5, 5, 5, 5, borderMode); + + TEST_CYCLE() + { + cv::copyMakeBorder(src, dst, 5, 5, 5, 5, borderMode); + } } } @@ -291,14 +379,28 @@ PERF_TEST_P(Sz_Depth_Op, ImgProc_Threshold, Combine( cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); + cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); + } + } + else { - cv::gpu::threshold(d_src, d_dst, 100.0, 255.0, threshOp); + cv::Mat dst; + + cv::threshold(src, dst, 100.0, 255.0, threshOp); + + TEST_CYCLE() + { + cv::threshold(src, dst, 100.0, 255.0, threshOp); + } } } @@ -312,15 +414,29 @@ PERF_TEST_P(Sz, ImgProc_Integral, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_8UC1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_buf; - cv::gpu::integralBuffered(d_src, d_dst, d_buf); + cv::gpu::integralBuffered(d_src, d_dst, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::integralBuffered(d_src, d_dst, d_buf); + } + } + else { - cv::gpu::integralBuffered(d_src, d_dst, d_buf); + cv::Mat dst; + + cv::integral(src, dst); + + TEST_CYCLE() + { + cv::integral(src, dst); + } } } @@ -334,14 +450,21 @@ PERF_TEST_P(Sz, ImgProc_IntegralSqr, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_8UC1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::sqrIntegral(d_src, d_dst); + cv::gpu::sqrIntegral(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::sqrIntegral(d_src, d_dst); + } + } + else { - cv::gpu::sqrIntegral(d_src, d_dst); + FAIL(); } } @@ -356,15 +479,35 @@ PERF_TEST_P(Sz_Depth, ImgProc_HistEvenC1, Combine(GPU_TYPICAL_MAT_SIZES, Values( cv::Mat src(size, depth); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_hist; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); + cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); + } + } + else { - cv::gpu::histEven(d_src, d_hist, d_buf, 30, 0, 180); + int hbins = 30; + float hranges[] = {0.0f, 180.0f}; + int histSize[] = {hbins}; + const float* ranges[] = {hranges}; + int channels[] = {0}; + + cv::Mat hist; + + cv::calcHist(&src, 1, channels, cv::Mat(), hist, 1, histSize, ranges); + + TEST_CYCLE() + { + cv::calcHist(&src, 1, channels, cv::Mat(), hist, 1, histSize, ranges); + } } } @@ -383,15 +526,22 @@ PERF_TEST_P(Sz_Depth, ImgProc_HistEvenC4, Combine(GPU_TYPICAL_MAT_SIZES, Values( int lowerLevel[] = {0, 0, 0, 0}; int upperLevel[] = {180, 180, 180, 180}; - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_hist[4]; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist[4]; + cv::gpu::GpuMat d_buf; - cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); + cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); + } + } + else { - cv::gpu::histEven(d_src, d_hist, d_buf, histSize, lowerLevel, upperLevel); + FAIL(); } } @@ -405,15 +555,22 @@ PERF_TEST_P(Sz, ImgProc_CalcHist, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_8UC1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_hist; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::calcHist(d_src, d_hist, d_buf); + cv::gpu::calcHist(d_src, d_hist, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::calcHist(d_src, d_hist, d_buf); + } + } + else { - cv::gpu::calcHist(d_src, d_hist, d_buf); + FAIL(); } } @@ -427,16 +584,30 @@ PERF_TEST_P(Sz, ImgProc_EqualizeHist, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_8UC1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_hist; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_hist; + cv::gpu::GpuMat d_buf; - cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); + cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); + } + } + else { - cv::gpu::equalizeHist(d_src, d_dst, d_hist, d_buf); + cv::Mat dst; + + cv::equalizeHist(src, dst); + + TEST_CYCLE() + { + cv::equalizeHist(src, dst); + } } } @@ -450,14 +621,21 @@ PERF_TEST_P(Sz, ImgProc_ColumnSum, GPU_TYPICAL_MAT_SIZES) cv::Mat src(size, CV_32FC1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::columnSum(d_src, d_dst); + cv::gpu::columnSum(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::columnSum(d_src, d_dst); + } + } + else { - cv::gpu::columnSum(d_src, d_dst); + FAIL(); } } @@ -478,15 +656,29 @@ PERF_TEST_P(Image_AppertureSz_L2gradient, ImgProc_Canny, Combine( cv::Mat image = readImage(fileName, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(image.empty()); - cv::gpu::GpuMat d_image(image); - cv::gpu::GpuMat d_dst; - cv::gpu::CannyBuf d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_dst; + cv::gpu::CannyBuf d_buf; - cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); + cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); + } + } + else { - cv::gpu::Canny(d_image, d_buf, d_dst, 50.0, 100.0, apperture_size, useL2gradient); + cv::Mat dst; + + cv::Canny(image, dst, 50.0, 100.0, apperture_size, useL2gradient); + + TEST_CYCLE() + { + cv::Canny(image, dst, 50.0, 100.0, apperture_size, useL2gradient); + } } } @@ -497,7 +689,7 @@ DEF_PARAM_TEST_1(Image, string); PERF_TEST_P(Image, ImgProc_MeanShiftFiltering, Values("gpu/meanshift/cones.png")) { - declare.time(5.0); + declare.time(15.0); cv::Mat img = readImage(GetParam()); ASSERT_FALSE(img.empty()); @@ -505,14 +697,28 @@ PERF_TEST_P(Image, ImgProc_MeanShiftFiltering, Values("gpu/meanshift/con cv::Mat rgba; cv::cvtColor(img, rgba, cv::COLOR_BGR2BGRA); - cv::gpu::GpuMat d_src(rgba); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(rgba); + cv::gpu::GpuMat d_dst; - cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); + cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); + } + } + else { - cv::gpu::meanShiftFiltering(d_src, d_dst, 50, 50); + cv::Mat dst; + + cv::pyrMeanShiftFiltering(img, dst, 50, 50); + + TEST_CYCLE() + { + cv::pyrMeanShiftFiltering(img, dst, 50, 50); + } } } @@ -529,15 +735,22 @@ PERF_TEST_P(Image, ImgProc_MeanShiftProc, Values("gpu/meanshift/cones.pn cv::Mat rgba; cv::cvtColor(img, rgba, cv::COLOR_BGR2BGRA); - cv::gpu::GpuMat d_src(rgba); - cv::gpu::GpuMat d_dstr; - cv::gpu::GpuMat d_dstsp; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(rgba); + cv::gpu::GpuMat d_dstr; + cv::gpu::GpuMat d_dstsp; - cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); + cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); + } + } + else { - cv::gpu::meanShiftProc(d_src, d_dstr, d_dstsp, 50, 50); + FAIL(); } } @@ -556,13 +769,20 @@ PERF_TEST_P(Image, ImgProc_MeanShiftSegmentation, Values("gpu/meanshift/ cv::Mat dst; - cv::gpu::GpuMat d_src(rgba); + if (runOnGpu) + { + cv::gpu::GpuMat d_src(rgba); - cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); + cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); + } + } + else { - cv::gpu::meanShiftSegmentation(d_src, dst, 10, 10, 20); + FAIL(); } } @@ -583,17 +803,24 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_BlendLinear, Combine(GPU_TYPICAL_MAT_SIZES, Val cv::Mat img2(size, type); fillRandom(img2); - cv::gpu::GpuMat d_img1(img1); - cv::gpu::GpuMat d_img2(img2); - cv::gpu::GpuMat d_weights1(size, CV_32FC1, cv::Scalar::all(0.5)); - cv::gpu::GpuMat d_weights2(size, CV_32FC1, cv::Scalar::all(0.5)); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_img1(img1); + cv::gpu::GpuMat d_img2(img2); + cv::gpu::GpuMat d_weights1(size, CV_32FC1, cv::Scalar::all(0.5)); + cv::gpu::GpuMat d_weights2(size, CV_32FC1, cv::Scalar::all(0.5)); + cv::gpu::GpuMat d_dst; - cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); + cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); + } + } + else { - cv::gpu::blendLinear(d_img1, d_img2, d_weights1, d_weights2, d_dst); + FAIL(); } } @@ -604,26 +831,48 @@ DEF_PARAM_TEST(Sz_KernelSz_Ccorr, cv::Size, int, bool); PERF_TEST_P(Sz_KernelSz_Ccorr, ImgProc_Convolve, Combine(GPU_TYPICAL_MAT_SIZES, Values(17, 27, 32, 64), Bool())) { - declare.time(2.0); + declare.time(10.0); cv::Size size = GET_PARAM(0); int templ_size = GET_PARAM(1); bool ccorr = GET_PARAM(2); - cv::gpu::GpuMat d_image = cv::gpu::createContinuous(size, CV_32FC1); - d_image.setTo(cv::Scalar(1.0)); + cv::Mat image(size, CV_32FC1); + image.setTo(1.0); - cv::gpu::GpuMat d_templ = cv::gpu::createContinuous(templ_size, templ_size, CV_32FC1); - d_templ.setTo(cv::Scalar(1.0)); + cv::Mat templ(templ_size, templ_size, CV_32FC1); + templ.setTo(1.0); - cv::gpu::GpuMat d_dst; - cv::gpu::ConvolveBuf d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_image = cv::gpu::createContinuous(size, CV_32FC1); + d_image.upload(image); - cv::gpu::convolve(d_image, d_templ, d_dst, ccorr, d_buf); + cv::gpu::GpuMat d_templ = cv::gpu::createContinuous(templ_size, templ_size, CV_32FC1); + d_templ.upload(templ); + + cv::gpu::GpuMat d_dst; + cv::gpu::ConvolveBuf d_buf; - TEST_CYCLE() - { cv::gpu::convolve(d_image, d_templ, d_dst, ccorr, d_buf); + + TEST_CYCLE() + { + cv::gpu::convolve(d_image, d_templ, d_dst, ccorr, d_buf); + } + } + else + { + ASSERT_FALSE(ccorr); + + cv::Mat dst; + + cv::filter2D(image, dst, image.depth(), templ); + + TEST_CYCLE() + { + cv::filter2D(image, dst, image.depth(), templ); + } } } @@ -652,15 +901,29 @@ PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate8U, Combine( cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_8U, cn)); fillRandom(templ); - cv::gpu::GpuMat d_image(image); - cv::gpu::GpuMat d_templ(templ); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_templ(templ); + cv::gpu::GpuMat d_dst; - cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + } + } + else { - cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + cv::Mat dst; + + cv::matchTemplate(image, templ, dst, method); + + TEST_CYCLE() + { + cv::matchTemplate(image, templ, dst, method); + } } }; @@ -684,15 +947,29 @@ PERF_TEST_P(Sz_TemplateSz_Cn_Method, ImgProc_MatchTemplate32F, Combine( cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_32F, cn)); fillRandom(templ); - cv::gpu::GpuMat d_image(image); - cv::gpu::GpuMat d_templ(templ); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_templ(templ); + cv::gpu::GpuMat d_dst; - cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + } + } + else { - cv::gpu::matchTemplate(d_image, d_templ, d_dst, method); + cv::Mat dst; + + cv::matchTemplate(image, templ, dst, method); + + TEST_CYCLE() + { + cv::matchTemplate(image, templ, dst, method); + } } }; @@ -716,15 +993,29 @@ PERF_TEST_P(Sz_Flags, ImgProc_MulSpectrums, Combine( cv::Mat b(size, CV_32FC2); fillRandom(b, 0, 100); - cv::gpu::GpuMat d_a(a); - cv::gpu::GpuMat d_b(b); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_a(a); + cv::gpu::GpuMat d_b(b); + cv::gpu::GpuMat d_dst; - cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); + cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); + } + } + else { - cv::gpu::mulSpectrums(d_a, d_b, d_dst, flag); + cv::Mat dst; + + cv::mulSpectrums(a, b, dst, flag); + + TEST_CYCLE() + { + cv::mulSpectrums(a, b, dst, flag); + } } } @@ -743,15 +1034,22 @@ PERF_TEST_P(Sz, ImgProc_MulAndScaleSpectrums, GPU_TYPICAL_MAT_SIZES) cv::Mat src2(size, CV_32FC2); fillRandom(src2, 0, 100); - cv::gpu::GpuMat d_src1(src1); - cv::gpu::GpuMat d_src2(src2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src1(src1); + cv::gpu::GpuMat d_src2(src2); + cv::gpu::GpuMat d_dst; - cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); + cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); + } + } + else { - cv::gpu::mulAndScaleSpectrums(d_src1, d_src2, d_dst, cv::DFT_ROWS, scale, false); + FAIL(); } } @@ -762,7 +1060,7 @@ PERF_TEST_P(Sz_Flags, ImgProc_Dft, Combine( GPU_TYPICAL_MAT_SIZES, Values(0, DftFlags(cv::DFT_ROWS), DftFlags(cv::DFT_INVERSE)))) { - declare.time(2.0); + declare.time(10.0); cv::Size size = GET_PARAM(0); int flag = GET_PARAM(1); @@ -770,14 +1068,28 @@ PERF_TEST_P(Sz_Flags, ImgProc_Dft, Combine( cv::Mat src(size, CV_32FC2); fillRandom(src, 0, 100); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::dft(d_src, d_dst, size, flag); + cv::gpu::dft(d_src, d_dst, size, flag); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::dft(d_src, d_dst, size, flag); + } + } + else { - cv::gpu::dft(d_src, d_dst, size, flag); + cv::Mat dst; + + cv::dft(src, dst, flag); + + TEST_CYCLE() + { + cv::dft(src, dst, flag); + } } } @@ -793,8 +1105,6 @@ PERF_TEST_P(Image_Type_Border_BlockSz_ApertureSz, ImgProc_CornerHarris, Combine( Values(3, 5, 7), Values(0, 3, 5, 7))) { - double k = 0.5; - string fileName = GET_PARAM(0); int type = GET_PARAM(1); int borderMode = GET_PARAM(2); @@ -803,20 +1113,35 @@ PERF_TEST_P(Image_Type_Border_BlockSz_ApertureSz, ImgProc_CornerHarris, Combine( cv::Mat img = readImage(fileName, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_Dx; - cv::gpu::GpuMat d_Dy; - cv::gpu::GpuMat d_buf; - - cv::gpu::cornerHarris(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, k, borderMode); + double k = 0.5; - TEST_CYCLE() + if (runOnGpu) { + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_Dx; + cv::gpu::GpuMat d_Dy; + cv::gpu::GpuMat d_buf; + cv::gpu::cornerHarris(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, k, borderMode); + + TEST_CYCLE() + { + cv::gpu::cornerHarris(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, k, borderMode); + } + } + else + { + cv::Mat dst; + + cv::cornerHarris(img, dst, blockSize, apertureSize, k, borderMode); + + TEST_CYCLE() + { + cv::cornerHarris(img, dst, blockSize, apertureSize, k, borderMode); + } } } @@ -841,17 +1166,31 @@ PERF_TEST_P(Image_Type_Border_BlockSz_ApertureSz, ImgProc_CornerMinEigenVal, Com img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_dst; - cv::gpu::GpuMat d_Dx; - cv::gpu::GpuMat d_Dy; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_dst; + cv::gpu::GpuMat d_Dx; + cv::gpu::GpuMat d_Dy; + cv::gpu::GpuMat d_buf; - cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); + cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); + } + } + else { - cv::gpu::cornerMinEigenVal(d_img, d_dst, d_Dx, d_Dy, d_buf, blockSize, apertureSize, borderMode); + cv::Mat dst; + + cv::cornerMinEigenVal(img, dst, blockSize, apertureSize, borderMode); + + TEST_CYCLE() + { + cv::cornerMinEigenVal(img, dst, blockSize, apertureSize, borderMode); + } } } @@ -866,14 +1205,21 @@ PERF_TEST_P(Sz, ImgProc_BuildWarpPlaneMaps, GPU_TYPICAL_MAT_SIZES) cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); cv::Mat T = cv::Mat::zeros(1, 3, CV_32F); - cv::gpu::GpuMat d_map_x; - cv::gpu::GpuMat d_map_y; + if (runOnGpu) + { + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; - cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); + cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); + } + } + else { - cv::gpu::buildWarpPlaneMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, T, 1.0, d_map_x, d_map_y); + FAIL(); } } @@ -887,14 +1233,21 @@ PERF_TEST_P(Sz, ImgProc_BuildWarpCylindricalMaps, GPU_TYPICAL_MAT_SIZES) cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat d_map_x; - cv::gpu::GpuMat d_map_y; + if (runOnGpu) + { + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; - cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + } + } + else { - cv::gpu::buildWarpCylindricalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + FAIL(); } } @@ -908,14 +1261,21 @@ PERF_TEST_P(Sz, ImgProc_BuildWarpSphericalMaps, GPU_TYPICAL_MAT_SIZES) cv::Mat K = cv::Mat::eye(3, 3, CV_32FC1); cv::Mat R = cv::Mat::ones(3, 3, CV_32FC1); - cv::gpu::GpuMat d_map_x; - cv::gpu::GpuMat d_map_y; + if (runOnGpu) + { + cv::gpu::GpuMat d_map_x; + cv::gpu::GpuMat d_map_y; - cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + } + } + else { - cv::gpu::buildWarpSphericalMaps(size, cv::Rect(0, 0, size.width, size.height), K, R, 1.0, d_map_x, d_map_y); + FAIL(); } } @@ -940,14 +1300,21 @@ PERF_TEST_P(Sz_Depth_Cn_Inter, ImgProc_Rotate, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); + cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); + } + } + else { - cv::gpu::rotate(d_src, d_dst, size, 30.0, 0, 0, interpolation); + FAIL(); } } @@ -968,14 +1335,28 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_PyrDown, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pyrDown(d_src, d_dst); + cv::gpu::pyrDown(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::pyrDown(d_src, d_dst); + } + } + else { - cv::gpu::pyrDown(d_src, d_dst); + cv::Mat dst; + + cv::pyrDown(src, dst); + + TEST_CYCLE() + { + cv::pyrDown(src, dst); + } } } @@ -996,14 +1377,28 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_PyrUp, Combine( cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::pyrUp(d_src, d_dst); + cv::gpu::pyrUp(d_src, d_dst); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::pyrUp(d_src, d_dst); + } + } + else { - cv::gpu::pyrUp(d_src, d_dst); + cv::Mat dst; + + cv::pyrUp(src, dst); + + TEST_CYCLE() + { + cv::pyrUp(src, dst); + } } } @@ -1049,14 +1444,28 @@ PERF_TEST_P(Sz_Depth_Code, ImgProc_CvtColor, Combine( cv::Mat src(size, CV_MAKETYPE(depth, info.scn)); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); + cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); + } + } + else { - cv::gpu::cvtColor(d_src, d_dst, info.code, info.dcn); + cv::Mat dst; + + cv::cvtColor(src, dst, info.code, info.dcn); + + TEST_CYCLE() + { + cv::cvtColor(src, dst, info.code, info.dcn); + } } } @@ -1072,13 +1481,20 @@ PERF_TEST_P(Sz, ImgProc_SwapChannels, GPU_TYPICAL_MAT_SIZES) const int dstOrder[] = {2, 1, 0, 3}; - cv::gpu::GpuMat d_src(src); + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); - cv::gpu::swapChannels(d_src, dstOrder); + cv::gpu::swapChannels(d_src, dstOrder); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::swapChannels(d_src, dstOrder); + } + } + else { - cv::gpu::swapChannels(d_src, dstOrder); + FAIL(); } } @@ -1102,15 +1518,22 @@ PERF_TEST_P(Sz_Type_Op, ImgProc_AlphaComp, Combine(GPU_TYPICAL_MAT_SIZES, Values cv::Mat img2(size, type); fillRandom(img2); - cv::gpu::GpuMat d_img1(img1); - cv::gpu::GpuMat d_img2(img2); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_img1(img1); + cv::gpu::GpuMat d_img2(img2); + cv::gpu::GpuMat d_dst; - cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); + cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); + } + } + else { - cv::gpu::alphaComp(d_img1, d_img2, d_dst, alpha_op); + FAIL(); } } @@ -1128,15 +1551,22 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_ImagePyramidBuild, Combine(GPU_TYPICAL_MAT_SIZE cv::Mat src(size, type); fillRandom(src); - cv::gpu::GpuMat d_src(src); + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); - cv::gpu::ImagePyramid d_pyr; + cv::gpu::ImagePyramid d_pyr; - d_pyr.build(d_src, 5); + d_pyr.build(d_src, 5); - TEST_CYCLE() + TEST_CYCLE() + { + d_pyr.build(d_src, 5); + } + } + else { - d_pyr.build(d_src, 5); + FAIL(); } } @@ -1156,16 +1586,23 @@ PERF_TEST_P(Sz_Depth_Cn, ImgProc_ImagePyramidGetLayer, Combine(GPU_TYPICAL_MAT_S cv::Size dstSize(size.width / 2 + 10, size.height / 2 + 10); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - cv::gpu::ImagePyramid d_pyr(d_src, 3); + cv::gpu::ImagePyramid d_pyr(d_src, 3); - d_pyr.getLayer(d_dst, dstSize); + d_pyr.getLayer(d_dst, dstSize); - TEST_CYCLE() + TEST_CYCLE() + { + d_pyr.getLayer(d_dst, dstSize); + } + } + else { - d_pyr.getLayer(d_dst, dstSize); + FAIL(); } } @@ -1197,16 +1634,29 @@ PERF_TEST_P(Sz_DoSort, ImgProc_HoughLines, Combine(GPU_TYPICAL_MAT_SIZES, Bool() cv::line(src, p1, p2, cv::Scalar::all(255), 2); } - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_lines; - cv::gpu::GpuMat d_accum; - cv::gpu::GpuMat d_buf; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_lines; + cv::gpu::GpuMat d_accum; + cv::gpu::GpuMat d_buf; - cv::gpu::HoughLines(d_src, d_lines, d_accum, d_buf, rho, theta, threshold, doSort); + cv::gpu::HoughLines(d_src, d_lines, d_accum, d_buf, rho, theta, threshold, doSort); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::HoughLines(d_src, d_lines, d_accum, d_buf, rho, theta, threshold, doSort); + } + } + else { - cv::gpu::HoughLines(d_src, d_lines, d_accum, d_buf, rho, theta, threshold, doSort); + std::vector lines; + cv::HoughLines(src, lines, rho, theta, threshold); + + TEST_CYCLE() + { + cv::HoughLines(src, lines, rho, theta, threshold); + } } } diff --git a/modules/gpu/perf/perf_labeling.cpp b/modules/gpu/perf/perf_labeling.cpp index bd1bcf144d..f17dd7d0af 100644 --- a/modules/gpu/perf/perf_labeling.cpp +++ b/modules/gpu/perf/perf_labeling.cpp @@ -7,27 +7,129 @@ namespace { DEF_PARAM_TEST_1(Image, string); -PERF_TEST_P(Image, Labeling_ConnectedComponents, Values("gpu/labeling/aloe-disp.png")) +struct GreedyLabeling { - cv::Mat image = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + struct dot + { + int x; + int y; + + static dot make(int i, int j) + { + dot d; d.x = i; d.y = j; + return d; + } + }; + + struct InInterval + { + InInterval(const int& _lo, const int& _hi) : lo(-_lo), hi(_hi) {}; + const int lo, hi; + + bool operator() (const unsigned char a, const unsigned char b) const + { + int d = a - b; + return lo <= d && d <= hi; + } + }; + + GreedyLabeling(cv::Mat img) + : image(img), _labels(image.size(), CV_32SC1, cv::Scalar::all(-1)) {stack = new dot[image.cols * image.rows];} + + ~GreedyLabeling(){delete[] stack;} + + void operator() (cv::Mat labels) const + { + labels.setTo(cv::Scalar::all(-1)); + InInterval inInt(0, 2); + int cc = -1; + + int* dist_labels = (int*)labels.data; + int pitch = labels.step1(); + + unsigned char* source = (unsigned char*)image.data; + int width = image.cols; + int height = image.rows; + + for (int j = 0; j < image.rows; ++j) + for (int i = 0; i < image.cols; ++i) + { + if (dist_labels[j * pitch + i] != -1) continue; + + dot* top = stack; + dot p = dot::make(i, j); + cc++; + + dist_labels[j * pitch + i] = cc; + + while (top >= stack) + { + int* dl = &dist_labels[p.y * pitch + p.x]; + unsigned char* sp = &source[p.y * image.step1() + p.x]; - // cv::threshold(image, image, 150, 255, CV_THRESH_BINARY); + dl[0] = cc; - cv::gpu::GpuMat mask; - mask.create(image.rows, image.cols, CV_8UC1); + //right + if( p.x < (width - 1) && dl[ +1] == -1 && inInt(sp[0], sp[+1])) + *top++ = dot::make(p.x + 1, p.y); - cv::gpu::GpuMat components; - components.create(image.rows, image.cols, CV_32SC1); + //left + if( p.x > 0 && dl[-1] == -1 && inInt(sp[0], sp[-1])) + *top++ = dot::make(p.x - 1, p.y); - cv::gpu::connectivityMask(cv::gpu::GpuMat(image), mask, cv::Scalar::all(0), cv::Scalar::all(2)); + //bottom + if( p.y < (height - 1) && dl[+pitch] == -1 && inInt(sp[0], sp[+image.step1()])) + *top++ = dot::make(p.x, p.y + 1); - ASSERT_NO_THROW(cv::gpu::labelComponents(mask, components)); + //top + if( p.y > 0 && dl[-pitch] == -1 && inInt(sp[0], sp[-image.step1()])) + *top++ = dot::make(p.x, p.y - 1); + p = *--top; + } + } + } + + cv::Mat image; + cv::Mat _labels; + dot* stack; +}; + +PERF_TEST_P(Image, Labeling_ConnectedComponents, Values("gpu/labeling/aloe-disp.png")) +{ declare.time(1.0); - TEST_CYCLE() + cv::Mat image = readImage(GetParam(), cv::IMREAD_GRAYSCALE); + + if (runOnGpu) + { + cv::gpu::GpuMat mask; + mask.create(image.rows, image.cols, CV_8UC1); + + cv::gpu::GpuMat components; + components.create(image.rows, image.cols, CV_32SC1); + + cv::gpu::connectivityMask(cv::gpu::GpuMat(image), mask, cv::Scalar::all(0), cv::Scalar::all(2)); + + ASSERT_NO_THROW(cv::gpu::labelComponents(mask, components)); + + TEST_CYCLE() + { + cv::gpu::labelComponents(mask, components); + } + } + else { - cv::gpu::labelComponents(mask, components); + GreedyLabeling host(image); + + host(host._labels); + + declare.time(1.0); + + TEST_CYCLE() + { + host(host._labels); + } } } diff --git a/modules/gpu/perf/perf_matop.cpp b/modules/gpu/perf/perf_matop.cpp index c014b19da8..cdae962f23 100644 --- a/modules/gpu/perf/perf_matop.cpp +++ b/modules/gpu/perf/perf_matop.cpp @@ -18,13 +18,27 @@ PERF_TEST_P(Sz_Depth_Cn, MatOp_SetTo, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV_8 cv::Scalar val(1, 2, 3, 4); - cv::gpu::GpuMat d_src(size, type); + if (runOnGpu) + { + cv::gpu::GpuMat d_src(size, type); - d_src.setTo(val); + d_src.setTo(val); - TEST_CYCLE() + TEST_CYCLE() + { + d_src.setTo(val); + } + } + else { - d_src.setTo(val); + cv::Mat src(size, type); + + src.setTo(val); + + TEST_CYCLE() + { + src.setTo(val); + } } } @@ -47,14 +61,26 @@ PERF_TEST_P(Sz_Depth_Cn, MatOp_SetToMasked, Combine(GPU_TYPICAL_MAT_SIZES, Value cv::Scalar val(1, 2, 3, 4); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_mask(mask); + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_mask(mask); - d_src.setTo(val, d_mask); + d_src.setTo(val, d_mask); - TEST_CYCLE() + TEST_CYCLE() + { + d_src.setTo(val, d_mask); + } + } + else { - d_src.setTo(val, d_mask); + src.setTo(val, mask); + + TEST_CYCLE() + { + src.setTo(val, mask); + } } } @@ -75,15 +101,29 @@ PERF_TEST_P(Sz_Depth_Cn, MatOp_CopyToMasked, Combine(GPU_TYPICAL_MAT_SIZES, Valu cv::Mat mask(size, CV_8UC1); fillRandom(mask, 0, 2); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_mask(mask); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_mask(mask); + cv::gpu::GpuMat d_dst; - d_src.copyTo(d_dst, d_mask); + d_src.copyTo(d_dst, d_mask); - TEST_CYCLE() + TEST_CYCLE() + { + d_src.copyTo(d_dst, d_mask); + } + } + else { - d_src.copyTo(d_dst, d_mask); + cv::Mat dst; + + src.copyTo(dst, mask); + + TEST_CYCLE() + { + src.copyTo(dst, mask); + } } } @@ -101,14 +141,28 @@ PERF_TEST_P(Sz_2Depth, MatOp_ConvertTo, Combine(GPU_TYPICAL_MAT_SIZES, Values(CV cv::Mat src(size, depth1); fillRandom(src); - cv::gpu::GpuMat d_src(src); - cv::gpu::GpuMat d_dst; + if (runOnGpu) + { + cv::gpu::GpuMat d_src(src); + cv::gpu::GpuMat d_dst; - d_src.convertTo(d_dst, depth2, 0.5, 1.0); + d_src.convertTo(d_dst, depth2, 0.5, 1.0); - TEST_CYCLE() + TEST_CYCLE() + { + d_src.convertTo(d_dst, depth2, 0.5, 1.0); + } + } + else { - d_src.convertTo(d_dst, depth2, 0.5, 1.0); + cv::Mat dst; + + src.convertTo(dst, depth2, 0.5, 1.0); + + TEST_CYCLE() + { + src.convertTo(dst, depth2, 0.5, 1.0); + } } } diff --git a/modules/gpu/perf/perf_objdetect.cpp b/modules/gpu/perf/perf_objdetect.cpp index 9c1f7919f9..0c4cd5e4c0 100644 --- a/modules/gpu/perf/perf_objdetect.cpp +++ b/modules/gpu/perf/perf_objdetect.cpp @@ -17,16 +17,31 @@ PERF_TEST_P(Image, ObjDetect_HOG, Values("gpu/hog/road.png")) std::vector found_locations; - cv::gpu::GpuMat d_img(img); + if (runOnGpu) + { + cv::gpu::GpuMat d_img(img); - cv::gpu::HOGDescriptor d_hog; - d_hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); + cv::gpu::HOGDescriptor d_hog; + d_hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); - d_hog.detectMultiScale(d_img, found_locations); + d_hog.detectMultiScale(d_img, found_locations); - TEST_CYCLE() + TEST_CYCLE() + { + d_hog.detectMultiScale(d_img, found_locations); + } + } + else { - d_hog.detectMultiScale(d_img, found_locations); + cv::HOGDescriptor hog; + hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); + + hog.detectMultiScale(img, found_locations); + + TEST_CYCLE() + { + hog.detectMultiScale(img, found_locations); + } } } @@ -42,18 +57,34 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_HaarClassifier, cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::gpu::CascadeClassifier_GPU d_cascade; - - ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); + if (runOnGpu) + { + cv::gpu::CascadeClassifier_GPU d_cascade; + ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_objects_buffer; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_objects_buffer; - d_cascade.detectMultiScale(d_img, d_objects_buffer); + d_cascade.detectMultiScale(d_img, d_objects_buffer); - TEST_CYCLE() + TEST_CYCLE() + { + d_cascade.detectMultiScale(d_img, d_objects_buffer); + } + } + else { - d_cascade.detectMultiScale(d_img, d_objects_buffer); + cv::CascadeClassifier cascade; + ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/perf/haarcascade_frontalface_alt.xml"))); + + std::vector rects; + + cascade.detectMultiScale(img, rects); + + TEST_CYCLE() + { + cascade.detectMultiScale(img, rects); + } } } @@ -66,18 +97,34 @@ PERF_TEST_P(ImageAndCascade, ObjDetect_LBPClassifier, cv::Mat img = readImage(GetParam().first, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(img.empty()); - cv::gpu::CascadeClassifier_GPU d_cascade; - - ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); + if (runOnGpu) + { + cv::gpu::CascadeClassifier_GPU d_cascade; + ASSERT_TRUE(d_cascade.load(perf::TestBase::getDataPath(GetParam().second))); - cv::gpu::GpuMat d_img(img); - cv::gpu::GpuMat d_gpu_rects; + cv::gpu::GpuMat d_img(img); + cv::gpu::GpuMat d_gpu_rects; - d_cascade.detectMultiScale(d_img, d_gpu_rects); + d_cascade.detectMultiScale(d_img, d_gpu_rects); - TEST_CYCLE() + TEST_CYCLE() + { + d_cascade.detectMultiScale(d_img, d_gpu_rects); + } + } + else { - d_cascade.detectMultiScale(d_img, d_gpu_rects); + cv::CascadeClassifier cascade; + ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/lbpcascade/lbpcascade_frontalface.xml"))); + + std::vector rects; + + cascade.detectMultiScale(img, rects); + + TEST_CYCLE() + { + cascade.detectMultiScale(img, rects); + } } } diff --git a/modules/gpu/perf/perf_video.cpp b/modules/gpu/perf/perf_video.cpp index a5a1e4da57..7faea0b880 100644 --- a/modules/gpu/perf/perf_video.cpp +++ b/modules/gpu/perf/perf_video.cpp @@ -3,6 +3,14 @@ using namespace std; using namespace testing; +namespace cv +{ + template<> void Ptr::delete_obj() + { + cvReleaseBGStatModel(&obj); + } +} + namespace { ////////////////////////////////////////////////////// @@ -25,19 +33,26 @@ PERF_TEST_P(ImagePair, Video_BroxOpticalFlow, Values(make_pair("gpu frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_u; - cv::gpu::GpuMat d_v; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; - cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, - 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); + cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, + 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(d_frame0, d_frame1, d_u, d_v); + d_flow(d_frame0, d_frame1, d_u, d_v); - TEST_CYCLE() + TEST_CYCLE() + { + d_flow(d_frame0, d_frame1, d_u, d_v); + } + } + else { - d_flow(d_frame0, d_frame1, d_u, d_v); + FAIL(); } } @@ -55,25 +70,32 @@ PERF_TEST_P(ImagePair, Video_InterpolateFrames, Values(make_pair("g frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_fu, d_fv; - cv::gpu::GpuMat d_bu, d_bv; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_fu, d_fv; + cv::gpu::GpuMat d_bu, d_bv; - cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, - 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); + cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, + 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(d_frame0, d_frame1, d_fu, d_fv); - d_flow(d_frame1, d_frame0, d_bu, d_bv); + d_flow(d_frame0, d_frame1, d_fu, d_fv); + d_flow(d_frame1, d_frame0, d_bu, d_bv); - cv::gpu::GpuMat d_newFrame; - cv::gpu::GpuMat d_buf; + cv::gpu::GpuMat d_newFrame; + cv::gpu::GpuMat d_buf; - cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); + cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); + } + } + else { - cv::gpu::interpolateFrames(d_frame0, d_frame1, d_fu, d_fv, d_bu, d_bv, 0.5f, d_newFrame, d_buf); + FAIL(); } } @@ -91,23 +113,30 @@ PERF_TEST_P(ImagePair, Video_CreateOpticalFlowNeedleMap, Values(mak frame0.convertTo(frame0, CV_32FC1, 1.0 / 255.0); frame1.convertTo(frame1, CV_32FC1, 1.0 / 255.0); - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_u; - cv::gpu::GpuMat d_v; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; - cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, - 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); + cv::gpu::BroxOpticalFlow d_flow(0.197f /*alpha*/, 50.0f /*gamma*/, 0.8f /*scale_factor*/, + 10 /*inner_iterations*/, 77 /*outer_iterations*/, 10 /*solver_iterations*/); - d_flow(d_frame0, d_frame1, d_u, d_v); + d_flow(d_frame0, d_frame1, d_u, d_v); - cv::gpu::GpuMat d_vertex, d_colors; + cv::gpu::GpuMat d_vertex, d_colors; - cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); + cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); - TEST_CYCLE() + TEST_CYCLE() + { + cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); + } + } + else { - cv::gpu::createOpticalFlowNeedleMap(d_u, d_v, d_vertex, d_colors); + FAIL(); } } @@ -124,16 +153,30 @@ PERF_TEST_P(Image_MinDistance, Video_GoodFeaturesToTrack, Combine(Values cv::Mat image = readImage(fileName, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(image.empty()); - cv::gpu::GoodFeaturesToTrackDetector_GPU d_detector(8000, 0.01, minDistance); + if (runOnGpu) + { + cv::gpu::GoodFeaturesToTrackDetector_GPU d_detector(8000, 0.01, minDistance); - cv::gpu::GpuMat d_image(image); - cv::gpu::GpuMat d_pts; + cv::gpu::GpuMat d_image(image); + cv::gpu::GpuMat d_pts; - d_detector(d_image, d_pts); + d_detector(d_image, d_pts); - TEST_CYCLE() + TEST_CYCLE() + { + d_detector(d_image, d_pts); + } + } + else { - d_detector(d_image, d_pts); + cv::Mat pts; + + cv::goodFeaturesToTrack(image, pts, 8000, 0.01, minDistance); + + TEST_CYCLE() + { + cv::goodFeaturesToTrack(image, pts, 8000, 0.01, minDistance); + } } } @@ -150,6 +193,8 @@ PERF_TEST_P(ImagePair_Gray_NPts_WinSz_Levels_Iters, Video_PyrLKOpticalFlowSparse Values(1, 2, 3), Values(1, 10, 30))) { + declare.time(20.0); + pair_string imagePair = GET_PARAM(0); bool useGray = GET_PARAM(1); int points = GET_PARAM(2); @@ -169,26 +214,45 @@ PERF_TEST_P(ImagePair_Gray_NPts_WinSz_Levels_Iters, Video_PyrLKOpticalFlowSparse else cv::cvtColor(frame0, gray_frame, cv::COLOR_BGR2GRAY); - cv::gpu::GpuMat d_pts; + cv::Mat pts; + cv::goodFeaturesToTrack(gray_frame, pts, points, 0.01, 0.0); - cv::gpu::GoodFeaturesToTrackDetector_GPU d_detector(points, 0.01, 0.0); - d_detector(cv::gpu::GpuMat(gray_frame), d_pts); + if (runOnGpu) + { + cv::gpu::GpuMat d_pts(pts); - cv::gpu::PyrLKOpticalFlow d_pyrLK; - d_pyrLK.winSize = cv::Size(winSize, winSize); - d_pyrLK.maxLevel = levels - 1; - d_pyrLK.iters = iters; + cv::gpu::PyrLKOpticalFlow d_pyrLK; + d_pyrLK.winSize = cv::Size(winSize, winSize); + d_pyrLK.maxLevel = levels - 1; + d_pyrLK.iters = iters; - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_nextPts; - cv::gpu::GpuMat d_status; + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_nextPts; + cv::gpu::GpuMat d_status; - d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); + d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); - TEST_CYCLE() + TEST_CYCLE() + { + d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); + } + } + else { - d_pyrLK.sparse(d_frame0, d_frame1, d_pts, d_nextPts, d_status); + cv::Mat nextPts; + cv::Mat status; + + cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts, status, cv::noArray(), + cv::Size(winSize, winSize), levels - 1, + cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, iters, 0.01)); + + TEST_CYCLE() + { + cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts, status, cv::noArray(), + cv::Size(winSize, winSize), levels - 1, + cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, iters, 0.01)); + } } } @@ -216,21 +280,28 @@ PERF_TEST_P(ImagePair_WinSz_Levels_Iters, Video_PyrLKOpticalFlowDense, Combine( cv::Mat frame1 = readImage(imagePair.second, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(frame1.empty()); - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_u; - cv::gpu::GpuMat d_v; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; - cv::gpu::PyrLKOpticalFlow d_pyrLK; - d_pyrLK.winSize = cv::Size(winSize, winSize); - d_pyrLK.maxLevel = levels - 1; - d_pyrLK.iters = iters; + cv::gpu::PyrLKOpticalFlow d_pyrLK; + d_pyrLK.winSize = cv::Size(winSize, winSize); + d_pyrLK.maxLevel = levels - 1; + d_pyrLK.iters = iters; - d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); + d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); - TEST_CYCLE() + TEST_CYCLE() + { + d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); + } + } + else { - d_pyrLK.dense(d_frame0, d_frame1, d_u, d_v); + FAIL(); } } @@ -247,18 +318,47 @@ PERF_TEST_P(ImagePair, Video_FarnebackOpticalFlow, Values(make_pair cv::Mat frame1 = readImage(GetParam().second, cv::IMREAD_GRAYSCALE); ASSERT_FALSE(frame1.empty()); - cv::gpu::GpuMat d_frame0(frame0); - cv::gpu::GpuMat d_frame1(frame1); - cv::gpu::GpuMat d_u; - cv::gpu::GpuMat d_v; + int numLevels = 5; + double pyrScale = 0.5; + int winSize = 13; + int numIters = 10; + int polyN = 5; + double polySigma = 1.1; + int flags = 0; - cv::gpu::FarnebackOpticalFlow d_farneback; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame0(frame0); + cv::gpu::GpuMat d_frame1(frame1); + cv::gpu::GpuMat d_u; + cv::gpu::GpuMat d_v; + + cv::gpu::FarnebackOpticalFlow d_farneback; + d_farneback.numLevels = numLevels; + d_farneback.pyrScale = pyrScale; + d_farneback.winSize = winSize; + d_farneback.numIters = numIters; + d_farneback.polyN = polyN; + d_farneback.polySigma = polySigma; + d_farneback.flags = flags; - d_farneback(d_frame0, d_frame1, d_u, d_v); + d_farneback(d_frame0, d_frame1, d_u, d_v); - TEST_CYCLE() + TEST_CYCLE() + { + d_farneback(d_frame0, d_frame1, d_u, d_v); + } + } + else { - d_farneback(d_frame0, d_frame1, d_u, d_v); + cv::Mat flow; + + cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags); + + TEST_CYCLE() + { + cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags); + } } } @@ -269,7 +369,7 @@ DEF_PARAM_TEST_1(Video, string); PERF_TEST_P(Video, Video_FGDStatModel, Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi")) { - declare.time(10); + declare.time(60); string inputFile = perf::TestBase::getDataPath(GetParam()); @@ -280,20 +380,41 @@ PERF_TEST_P(Video, Video_FGDStatModel, Values("gpu/video/768x576.avi", "gpu/vide cap >> frame; ASSERT_FALSE(frame.empty()); - cv::gpu::GpuMat d_frame(frame); - cv::gpu::FGDStatModel d_model(4); - d_model.create(d_frame); + if (runOnGpu) + { + cv::gpu::GpuMat d_frame(frame); + + cv::gpu::FGDStatModel d_model(4); + d_model.create(d_frame); + + for (int i = 0; i < 10; ++i) + { + cap >> frame; + ASSERT_FALSE(frame.empty()); - for (int i = 0; i < 10; ++i) + d_frame.upload(frame); + + startTimer(); next(); + d_model.update(d_frame); + stopTimer(); + } + } + else { - cap >> frame; - ASSERT_FALSE(frame.empty()); + IplImage ipl_frame = frame; + cv::Ptr model(cvCreateFGDStatModel(&ipl_frame)); + + for (int i = 0; i < 10; ++i) + { + cap >> frame; + ASSERT_FALSE(frame.empty()); - d_frame.upload(frame); + ipl_frame = frame; - startTimer(); next(); - d_model.update(d_frame); - stopTimer(); + startTimer(); next(); + cvUpdateBGStatModel(&ipl_frame, model); + stopTimer(); + } } } @@ -313,10 +434,6 @@ PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, Combine(Values("gpu/video/768x576. cv::Mat frame; - cv::gpu::GpuMat d_frame; - cv::gpu::MOG_GPU d_mog; - cv::gpu::GpuMat d_foreground; - cap >> frame; ASSERT_FALSE(frame.empty()); @@ -330,30 +447,62 @@ PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, Combine(Values("gpu/video/768x576. cv::swap(temp, frame); } - d_frame.upload(frame); - - d_mog(d_frame, d_foreground, learningRate); - - for (int i = 0; i < 10; ++i) + if (runOnGpu) { - cap >> frame; - ASSERT_FALSE(frame.empty()); + cv::gpu::GpuMat d_frame(frame); + cv::gpu::MOG_GPU d_mog; + cv::gpu::GpuMat d_foreground; - if (cn != 3) + d_mog(d_frame, d_foreground, learningRate); + + for (int i = 0; i < 10; ++i) { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + d_frame.upload(frame); + + startTimer(); next(); + d_mog(d_frame, d_foreground, learningRate); + stopTimer(); } + } + else + { + cv::BackgroundSubtractorMOG mog; + cv::Mat foreground; - d_frame.upload(frame); + mog(frame, foreground, learningRate); - startTimer(); next(); - d_mog(d_frame, d_foreground, learningRate); - stopTimer(); + for (int i = 0; i < 10; ++i) + { + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + startTimer(); next(); + mog(frame, foreground, learningRate); + stopTimer(); + } } } @@ -372,10 +521,6 @@ PERF_TEST_P(Video_Cn, Video_MOG2, Combine(Values("gpu/video/768x576.avi", "gpu/v cv::Mat frame; - cv::gpu::GpuMat d_frame; - cv::gpu::MOG2_GPU d_mog2; - cv::gpu::GpuMat d_foreground; - cap >> frame; ASSERT_FALSE(frame.empty()); @@ -389,30 +534,62 @@ PERF_TEST_P(Video_Cn, Video_MOG2, Combine(Values("gpu/video/768x576.avi", "gpu/v cv::swap(temp, frame); } - d_frame.upload(frame); - - d_mog2(d_frame, d_foreground); - - for (int i = 0; i < 10; ++i) + if (runOnGpu) { - cap >> frame; - ASSERT_FALSE(frame.empty()); + cv::gpu::GpuMat d_frame(frame); + cv::gpu::MOG2_GPU d_mog2; + cv::gpu::GpuMat d_foreground; + + d_mog2(d_frame, d_foreground); - if (cn != 3) + for (int i = 0; i < 10; ++i) { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + d_frame.upload(frame); + + startTimer(); next(); + d_mog2(d_frame, d_foreground); + stopTimer(); } + } + else + { + cv::BackgroundSubtractorMOG2 mog2; + cv::Mat foreground; - d_frame.upload(frame); + mog2(frame, foreground); - startTimer(); next(); - d_mog2(d_frame, d_foreground); - stopTimer(); + for (int i = 0; i < 10; ++i) + { + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + startTimer(); next(); + mog2(frame, foreground); + stopTimer(); + } } } @@ -429,36 +606,70 @@ PERF_TEST_P(Video_Cn, Video_MOG2GetBackgroundImage, Combine(Values("gpu/video/76 cv::Mat frame; - cv::gpu::GpuMat d_frame; - cv::gpu::MOG2_GPU d_mog2; - cv::gpu::GpuMat d_foreground; - - for (int i = 0; i < 10; ++i) + if (runOnGpu) { - cap >> frame; - ASSERT_FALSE(frame.empty()); + cv::gpu::GpuMat d_frame; + cv::gpu::MOG2_GPU d_mog2; + cv::gpu::GpuMat d_foreground; - if (cn != 3) + for (int i = 0; i < 10; ++i) { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + d_frame.upload(frame); + + d_mog2(d_frame, d_foreground); } - d_frame.upload(frame); + cv::gpu::GpuMat d_background; + d_mog2.getBackgroundImage(d_background); - d_mog2(d_frame, d_foreground); + TEST_CYCLE() + { + d_mog2.getBackgroundImage(d_background); + } } + else + { + cv::BackgroundSubtractorMOG2 mog2; + cv::Mat foreground; - cv::gpu::GpuMat d_background; - d_mog2.getBackgroundImage(d_background); + for (int i = 0; i < 10; ++i) + { + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + mog2(frame, foreground); + } - TEST_CYCLE() - { - d_mog2.getBackgroundImage(d_background); + cv::Mat background; + mog2.getBackgroundImage(background); + + TEST_CYCLE() + { + mog2.getBackgroundImage(background); + } } } @@ -487,32 +698,39 @@ PERF_TEST_P(Video_Cn, Video_VIBE, Combine(Values("gpu/video/768x576.avi", "gpu/v cv::swap(temp, frame); } - cv::gpu::GpuMat d_frame(frame); - cv::gpu::VIBE_GPU d_vibe; - cv::gpu::GpuMat d_foreground; - - d_vibe(d_frame, d_foreground); - - for (int i = 0; i < 10; ++i) + if (runOnGpu) { - cap >> frame; - ASSERT_FALSE(frame.empty()); + cv::gpu::GpuMat d_frame(frame); + cv::gpu::VIBE_GPU d_vibe; + cv::gpu::GpuMat d_foreground; - if (cn != 3) + d_vibe(d_frame, d_foreground); + + for (int i = 0; i < 10; ++i) { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); + cap >> frame; + ASSERT_FALSE(frame.empty()); + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + d_frame.upload(frame); + + startTimer(); next(); + d_vibe(d_frame, d_foreground); + stopTimer(); } - - d_frame.upload(frame); - - startTimer(); next(); - d_vibe(d_frame, d_foreground); - stopTimer(); + } + else + { + FAIL(); } } @@ -544,38 +762,76 @@ PERF_TEST_P(Video_Cn_MaxFeatures, Video_GMG, Combine(Values("gpu/video/768x576.a cv::swap(temp, frame); } - cv::gpu::GpuMat d_frame(frame); - cv::gpu::GpuMat d_fgmask; + if (runOnGpu) + { + cv::gpu::GpuMat d_frame(frame); + cv::gpu::GpuMat d_fgmask; - cv::gpu::GMG_GPU d_gmg; - d_gmg.maxFeatures = maxFeatures; + cv::gpu::GMG_GPU d_gmg; + d_gmg.maxFeatures = maxFeatures; - d_gmg(d_frame, d_fgmask); + d_gmg(d_frame, d_fgmask); - for (int i = 0; i < 150; ++i) - { - cap >> frame; - if (frame.empty()) + for (int i = 0; i < 150; ++i) { - cap.open(inputFile); cap >> frame; + if (frame.empty()) + { + cap.open(inputFile); + cap >> frame; + } + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + d_frame.upload(frame); + + startTimer(); next(); + d_gmg(d_frame, d_fgmask); + stopTimer(); } + } + else + { + cv::Mat fgmask; + cv::Mat zeros(frame.size(), CV_8UC1, cv::Scalar::all(0)); - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } + cv::BackgroundSubtractorGMG gmg; + gmg.set("maxFeatures", maxFeatures); + gmg.initialize(frame.size(), 0.0, 255.0); - d_frame.upload(frame); + gmg(frame, fgmask); - startTimer(); next(); - d_gmg(d_frame, d_fgmask); - stopTimer(); + for (int i = 0; i < 150; ++i) + { + cap >> frame; + if (frame.empty()) + { + cap.open(inputFile); + cap >> frame; + } + + if (cn != 3) + { + cv::Mat temp; + if (cn == 1) + cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); + else + cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); + cv::swap(temp, frame); + } + + startTimer(); next(); + gmg(frame, fgmask); + stopTimer(); + } } } @@ -584,6 +840,8 @@ PERF_TEST_P(Video_Cn_MaxFeatures, Video_GMG, Combine(Values("gpu/video/768x576.a PERF_TEST_P(Video, Video_VideoWriter, Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi")) { + declare.time(30); + string inputFile = perf::TestBase::getDataPath(GetParam()); string outputFile = cv::tempfile(".avi"); @@ -592,26 +850,45 @@ PERF_TEST_P(Video, Video_VideoWriter, Values("gpu/video/768x576.avi", "gpu/video cv::VideoCapture reader(inputFile); ASSERT_TRUE( reader.isOpened() ); - cv::gpu::VideoWriter_GPU d_writer; - cv::Mat frame; - cv::gpu::GpuMat d_frame; - declare.time(10); + if (runOnGpu) + { + cv::gpu::VideoWriter_GPU d_writer; + + cv::gpu::GpuMat d_frame; + + for (int i = 0; i < 10; ++i) + { + reader >> frame; + ASSERT_FALSE(frame.empty()); + + d_frame.upload(frame); + + if (!d_writer.isOpened()) + d_writer.open(outputFile, frame.size(), FPS); - for (int i = 0; i < 10; ++i) + startTimer(); next(); + d_writer.write(d_frame); + stopTimer(); + } + } + else { - reader >> frame; - ASSERT_FALSE(frame.empty()); + cv::VideoWriter writer; - d_frame.upload(frame); + for (int i = 0; i < 10; ++i) + { + reader >> frame; + ASSERT_FALSE(frame.empty()); - if (!d_writer.isOpened()) - d_writer.open(outputFile, frame.size(), FPS); + if (!writer.isOpened()) + writer.open(outputFile, CV_FOURCC('X', 'V', 'I', 'D'), FPS, frame.size()); - startTimer(); next(); - d_writer.write(d_frame); - stopTimer(); + startTimer(); next(); + writer.write(frame); + stopTimer(); + } } } @@ -624,16 +901,33 @@ PERF_TEST_P(Video, Video_VideoReader, Values("gpu/video/768x576.avi", "gpu/video string inputFile = perf::TestBase::getDataPath(GetParam()); - cv::gpu::VideoReader_GPU d_reader(inputFile); - ASSERT_TRUE( d_reader.isOpened() ); + if (runOnGpu) + { + cv::gpu::VideoReader_GPU d_reader(inputFile); + ASSERT_TRUE( d_reader.isOpened() ); - cv::gpu::GpuMat d_frame; + cv::gpu::GpuMat d_frame; - d_reader.read(d_frame); + d_reader.read(d_frame); - TEST_CYCLE_N(10) + TEST_CYCLE_N(10) + { + d_reader.read(d_frame); + } + } + else { - d_reader.read(d_frame); + cv::VideoCapture reader(inputFile); + ASSERT_TRUE( reader.isOpened() ); + + cv::Mat frame; + + reader >> frame; + + TEST_CYCLE_N(10) + { + reader >> frame; + } } } diff --git a/modules/gpu/perf_cpu/perf_calib3d.cpp b/modules/gpu/perf_cpu/perf_calib3d.cpp deleted file mode 100644 index 8124b808af..0000000000 --- a/modules/gpu/perf_cpu/perf_calib3d.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////////////////////// -// StereoBM - -GPU_PERF_TEST_1(StereoBM, cv::gpu::DeviceInfo) -{ - cv::Mat img_l = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_l.empty()); - - cv::Mat img_r = readImage("gpu/perf/aloeR.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img_r.empty()); - - cv::StereoBM bm(0, 256); - - cv::Mat dst; - - bm(img_l, img_r, dst); - - declare.time(5.0); - - TEST_CYCLE() - { - bm(img_l, img_r, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Calib3D, StereoBM, ALL_DEVICES); - -////////////////////////////////////////////////////////////////////// -// ProjectPoints - -IMPLEMENT_PARAM_CLASS(Count, int) - -GPU_PERF_TEST(ProjectPoints, cv::gpu::DeviceInfo, Count) -{ - int count = GET_PARAM(1); - - cv::Mat src(1, count, CV_32FC3); - fill(src, -100, 100); - - cv::Mat rvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::Mat tvec = cv::Mat::ones(1, 3, CV_32FC1); - cv::Mat camera_mat = cv::Mat::ones(3, 3, CV_32FC1); - cv::Mat dst; - - cv::projectPoints(src, rvec, tvec, camera_mat, cv::noArray(), dst); - - TEST_CYCLE() - { - cv::projectPoints(src, rvec, tvec, camera_mat, cv::noArray(), dst); - } -} - -INSTANTIATE_TEST_CASE_P(Calib3D, ProjectPoints, testing::Combine( - ALL_DEVICES, - testing::Values(5000, 10000, 20000))); - -////////////////////////////////////////////////////////////////////// -// SolvePnPRansac - -GPU_PERF_TEST(SolvePnPRansac, cv::gpu::DeviceInfo, Count) -{ - int count = GET_PARAM(1); - - cv::Mat object(1, count, CV_32FC3); - fill(object, -100, 100); - - cv::Mat camera_mat(3, 3, CV_32FC1); - fill(camera_mat, 0.5, 1); - camera_mat.at(0, 1) = 0.f; - camera_mat.at(1, 0) = 0.f; - camera_mat.at(2, 0) = 0.f; - camera_mat.at(2, 1) = 0.f; - - cv::Mat dist_coef(1, 8, CV_32F, cv::Scalar::all(0)); - - std::vector image_vec; - cv::Mat rvec_gold(1, 3, CV_32FC1); - fill(rvec_gold, 0, 1); - cv::Mat tvec_gold(1, 3, CV_32FC1); - fill(tvec_gold, 0, 1); - cv::projectPoints(object, rvec_gold, tvec_gold, camera_mat, dist_coef, image_vec); - - cv::Mat image(1, count, CV_32FC2, &image_vec[0]); - - cv::Mat rvec; - cv::Mat tvec; - - cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); - - declare.time(10.0); - - TEST_CYCLE() - { - cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec); - } -} - -INSTANTIATE_TEST_CASE_P(Calib3D, SolvePnPRansac, testing::Combine( - ALL_DEVICES, - testing::Values(5000, 10000, 20000))); - -////////////////////////////////////////////////////////////////////// -// ReprojectImageTo3D - -GPU_PERF_TEST(ReprojectImageTo3D, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 5.0, 30.0); - - cv::Mat Q(4, 4, CV_32FC1); - fill(Q, 0.1, 1.0); - - cv::Mat dst; - - cv::reprojectImageTo3D(src, dst, Q); - - TEST_CYCLE() - { - cv::reprojectImageTo3D(src, dst, Q); - } -} - -INSTANTIATE_TEST_CASE_P(Calib3D, ReprojectImageTo3D, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S))); - -#endif - diff --git a/modules/gpu/perf_cpu/perf_core.cpp b/modules/gpu/perf_cpu/perf_core.cpp deleted file mode 100644 index fb87009584..0000000000 --- a/modules/gpu/perf_cpu/perf_core.cpp +++ /dev/null @@ -1,1388 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////////////////////// -// Merge - -GPU_PERF_TEST(Merge, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - std::vector src(channels); - for (int i = 0; i < channels; ++i) - src[i] = cv::Mat(size, depth, cv::Scalar::all(i)); - - cv::Mat dst; - - cv::merge(src, dst); - - TEST_CYCLE() - { - cv::merge(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Merge, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(2, 3, 4))); - -////////////////////////////////////////////////////////////////////// -// Split - -GPU_PERF_TEST(Split, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - cv::Mat src(size, CV_MAKE_TYPE(depth, channels), cv::Scalar(1, 2, 3, 4)); - - std::vector dst; - - cv::split(src, dst); - - TEST_CYCLE() - { - cv::split(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Split, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(2, 3, 4))); - -////////////////////////////////////////////////////////////////////// -// Add_Mat - -GPU_PERF_TEST(Add_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0.0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0.0, 100.0); - - cv::Mat dst; - - cv::add(src1, src2, dst); - - TEST_CYCLE() - { - cv::add(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Add_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Add_Scalar - -GPU_PERF_TEST(Add_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Scalar s(1, 2, 3, 4); - cv::Mat dst; - - cv::add(src, s, dst); - - TEST_CYCLE() - { - cv::add(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Add_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Subtract_Mat - -GPU_PERF_TEST(Subtract_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0.0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0.0, 100.0); - - cv::Mat dst; - - cv::subtract(src1, src2, dst); - - TEST_CYCLE() - { - cv::subtract(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Subtract_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Subtract_Scalar - -GPU_PERF_TEST(Subtract_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Scalar s(1, 2, 3, 4); - cv::Mat dst; - - cv::subtract(src, s, dst); - - TEST_CYCLE() - { - cv::subtract(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Subtract_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Multiply_Mat - -GPU_PERF_TEST(Multiply_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0.0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0.0, 100.0); - - cv::Mat dst; - - cv::multiply(src1, src2, dst); - - TEST_CYCLE() - { - cv::multiply(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Multiply_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Multiply_Scalar - -GPU_PERF_TEST(Multiply_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Scalar s(1, 2, 3, 4); - cv::Mat dst; - - cv::multiply(src, s, dst); - - TEST_CYCLE() - { - cv::multiply(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Multiply_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Divide_Mat - -GPU_PERF_TEST(Divide_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0.0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0.0, 100.0); - - cv::Mat dst; - - cv::divide(src1, src2, dst); - - TEST_CYCLE() - { - cv::divide(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Divide_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Divide_Scalar - -GPU_PERF_TEST(Divide_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Scalar s(1, 2, 3, 4); - cv::Mat dst; - - cv::divide(src, s, dst); - - TEST_CYCLE() - { - cv::divide(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Divide_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Divide_Scalar_Inv - -GPU_PERF_TEST(Divide_Scalar_Inv, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - double scale = 100.0; - cv::Mat dst; - - cv::divide(scale, src, dst); - - TEST_CYCLE() - { - cv::divide(scale, src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Divide_Scalar_Inv, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// AbsDiff_Mat - -GPU_PERF_TEST(AbsDiff_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0.0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0.0, 100.0); - - cv::Mat dst; - - cv::absdiff(src1, src2, dst); - - TEST_CYCLE() - { - cv::absdiff(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, AbsDiff_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// AbsDiff_Scalar - -GPU_PERF_TEST(AbsDiff_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Scalar s(1, 2, 3, 4); - cv::Mat dst; - - cv::absdiff(src, s, dst); - - TEST_CYCLE() - { - cv::absdiff(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, AbsDiff_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Sqrt - -GPU_PERF_TEST(Sqrt, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 100.0); - - cv::Mat dst; - - cv::sqrt(src, dst); - - TEST_CYCLE() - { - cv::sqrt(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Sqrt, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Log - -GPU_PERF_TEST(Log, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 1.0, 100.0); - - cv::Mat dst; - - cv::log(src, dst); - - TEST_CYCLE() - { - cv::log(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Log, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Exp - -GPU_PERF_TEST(Exp, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 1.0, 10.0); - - cv::Mat dst; - - cv::exp(src, dst); - - TEST_CYCLE() - { - cv::exp(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Exp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Pow - -GPU_PERF_TEST(Pow, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 1.0, 10.0); - - cv::Mat dst; - - cv::pow(src, 2.3, dst); - - TEST_CYCLE() - { - cv::pow(src, 2.3, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Pow, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16S, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Compare_Mat - -CV_ENUM(CmpCode, cv::CMP_EQ, cv::CMP_GT, cv::CMP_GE, cv::CMP_LT, cv::CMP_LE, cv::CMP_NE) -#define ALL_CMP_CODES testing::Values(CmpCode(cv::CMP_EQ), CmpCode(cv::CMP_NE), CmpCode(cv::CMP_GT), CmpCode(cv::CMP_GE), CmpCode(cv::CMP_LT), CmpCode(cv::CMP_LE)) - -GPU_PERF_TEST(Compare_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth, CmpCode) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int cmp_code = GET_PARAM(3); - - cv::Mat src1(size, depth); - fill(src1, 0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 100.0); - - cv::Mat dst; - - cv::compare(src1, src2, dst, cmp_code); - - TEST_CYCLE() - { - cv::compare(src1, src2, dst, cmp_code); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Compare_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - ALL_CMP_CODES)); - -////////////////////////////////////////////////////////////////////// -// Compare_Scalar - -GPU_PERF_TEST(Compare_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, CmpCode) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int cmp_code = GET_PARAM(3); - - cv::Mat src(size, depth); - fill(src, 0, 100.0); - - cv::Scalar s = cv::Scalar::all(50); - cv::Mat dst; - - cv::compare(src, s, dst, cmp_code); - - TEST_CYCLE() - { - cv::compare(src, s, dst, cmp_code); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Compare_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - ALL_CMP_CODES)); - -////////////////////////////////////////////////////////////////////// -// Bitwise_Not - -GPU_PERF_TEST(Bitwise_Not, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0, 100.0); - - cv::Mat dst; - - cv::bitwise_not(src, dst); - - TEST_CYCLE() - { - cv::bitwise_not(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Not, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_And_Mat - -GPU_PERF_TEST(Bitwise_And_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 100.0); - - cv::Mat dst; - - cv::bitwise_and(src1, src2, dst); - - TEST_CYCLE() - { - cv::bitwise_and(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_And_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_And_Scalar - -GPU_PERF_TEST(Bitwise_And_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - int type = CV_MAKE_TYPE(depth, channels); - - cv::Mat src(size, type); - fill(src, 0, 100.0); - - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::Mat dst; - - cv::bitwise_and(src, s, dst); - - TEST_CYCLE() - { - cv::bitwise_and(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_And_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_Or_Mat - -GPU_PERF_TEST(Bitwise_Or_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 100.0); - - cv::Mat dst; - - cv::bitwise_or(src1, src2, dst); - - TEST_CYCLE() - { - cv::bitwise_or(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Or_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_Or_Scalar - -GPU_PERF_TEST(Bitwise_Or_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - int type = CV_MAKE_TYPE(depth, channels); - - cv::Mat src(size, type); - fill(src, 0, 100.0); - - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::Mat dst; - - cv::bitwise_or(src, s, dst); - - TEST_CYCLE() - { - cv::bitwise_or(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Or_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_Xor_Mat - -GPU_PERF_TEST(Bitwise_Xor_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0, 100.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 100.0); - - cv::Mat dst; - - cv::bitwise_xor(src1, src2, dst); - - TEST_CYCLE() - { - cv::bitwise_xor(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Xor_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S))); - -////////////////////////////////////////////////////////////////////// -// Bitwise_Xor_Scalar - -GPU_PERF_TEST(Bitwise_Xor_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - - int type = CV_MAKE_TYPE(depth, channels); - - cv::Mat src(size, type); - fill(src, 0, 100.0); - - cv::Scalar s = cv::Scalar(50, 50, 50, 50); - cv::Mat dst; - - cv::bitwise_xor(src, s, dst); - - TEST_CYCLE() - { - cv::bitwise_xor(src, s, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Bitwise_Xor_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S), - testing::Values(1, 3, 4))); - -////////////////////////////////////////////////////////////////////// -// Min_Mat - -GPU_PERF_TEST(Min_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0, 255.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 255.0); - - cv::Mat dst; - - cv::min(src1, src2, dst); - - TEST_CYCLE() - { - cv::min(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Min_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Min_Scalar - -GPU_PERF_TEST(Min_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0, 255.0); - - double val = 50.0; - cv::Mat dst; - - cv::min(src, val, dst); - - TEST_CYCLE() - { - cv::min(src, val, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Min_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Max_Mat - -GPU_PERF_TEST(Max_Mat, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src1(size, depth); - fill(src1, 0, 255.0); - - cv::Mat src2(size, depth); - fill(src2, 0, 255.0); - - cv::Mat dst; - - cv::max(src1, src2, dst); - - TEST_CYCLE() - { - cv::max(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Max_Mat, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// Max_Scalar - -GPU_PERF_TEST(Max_Scalar, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0, 255.0); - - double val = 50.0; - cv::Mat dst; - - cv::max(src, val, dst); - - TEST_CYCLE() - { - cv::max(src, val, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Max_Scalar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F))); - -////////////////////////////////////////////////////////////////////// -// AddWeighted - -GPU_PERF_TEST(AddWeighted, cv::gpu::DeviceInfo, cv::Size, MatDepth, MatDepth, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth1 = GET_PARAM(2); - int depth2 = GET_PARAM(3); - int dst_depth = GET_PARAM(4); - - cv::Mat src1(size, depth1); - fill(src1, 0, 100.0); - - cv::Mat src2(size, depth2); - fill(src2, 0, 100.0); - - cv::Mat dst; - - cv::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); - - TEST_CYCLE() - { - cv::addWeighted(src1, 0.5, src2, 0.5, 10.0, dst, dst_depth); - } -} - -INSTANTIATE_TEST_CASE_P(Core, AddWeighted, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F), - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// GEMM - -CV_FLAGS(GemmFlags, 0, cv::GEMM_1_T, cv::GEMM_2_T, cv::GEMM_3_T) -#define ALL_GEMM_FLAGS testing::Values(GemmFlags(0), GemmFlags(cv::GEMM_1_T), GemmFlags(cv::GEMM_2_T), GemmFlags(cv::GEMM_3_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_3_T), GemmFlags(cv::GEMM_1_T | cv::GEMM_2_T | cv::GEMM_3_T)) - -GPU_PERF_TEST(GEMM, cv::gpu::DeviceInfo, cv::Size, MatType, GemmFlags) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int flags = GET_PARAM(3); - - cv::Mat src1(size, type); - fill(src1, 0.0, 10.0); - - cv::Mat src2(size, type); - fill(src2, 0.0, 10.0); - - cv::Mat src3(size, type); - fill(src3, 0.0, 10.0); - - cv::Mat dst; - - cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); - - declare.time(50.0); - - TEST_CYCLE() - { - cv::gemm(src1, src2, 1.0, src3, 1.0, dst, flags); - } -} - -INSTANTIATE_TEST_CASE_P(Core, GEMM, testing::Combine( - ALL_DEVICES, - testing::Values(cv::Size(512, 512), cv::Size(1024, 1024)), - testing::Values(CV_32FC1, CV_32FC2, CV_64FC1, CV_64FC2), - ALL_GEMM_FLAGS)); - -////////////////////////////////////////////////////////////////////// -// Transpose - -GPU_PERF_TEST(Transpose, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 100.0); - - cv::Mat dst; - - cv::transpose(src, dst); - - TEST_CYCLE() - { - cv::transpose(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Transpose, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC4, CV_16UC2, CV_16SC2, CV_32SC1, CV_32SC2, CV_64FC1))); - -////////////////////////////////////////////////////////////////////// -// Flip - -enum {FLIP_BOTH = 0, FLIP_X = 1, FLIP_Y = -1}; -CV_ENUM(FlipCode, FLIP_BOTH, FLIP_X, FLIP_Y) -#define ALL_FLIP_CODES testing::Values(FlipCode(FLIP_BOTH), FlipCode(FLIP_X), FlipCode(FLIP_Y)) - -GPU_PERF_TEST(Flip, cv::gpu::DeviceInfo, cv::Size, MatType, FlipCode) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int flipCode = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 100.0); - - cv::Mat dst; - - cv::flip(src, dst, flipCode); - - TEST_CYCLE() - { - cv::flip(src, dst, flipCode); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Flip, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4), - ALL_FLIP_CODES)); - -////////////////////////////////////////////////////////////////////// -// LUT_OneChannel - -GPU_PERF_TEST(LUT_OneChannel, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 100.0); - - cv::Mat lut(1, 256, CV_8UC1); - fill(lut, 0.0, 100.0); - - cv::Mat dst; - - cv::LUT(src, lut, dst); - - TEST_CYCLE() - { - cv::LUT(src, lut, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, LUT_OneChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3))); - -////////////////////////////////////////////////////////////////////// -// LUT_MultiChannel - -GPU_PERF_TEST(LUT_MultiChannel, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 100.0); - - cv::Mat lut(1, 256, CV_MAKE_TYPE(CV_8U, src.channels())); - fill(lut, 0.0, 100.0); - - cv::Mat dst; - - cv::LUT(src, lut, dst); - - TEST_CYCLE() - { - cv::LUT(src, lut, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, LUT_MultiChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC3))); - -////////////////////////////////////////////////////////////////////// -// Magnitude_Complex - -GPU_PERF_TEST(Magnitude_Complex, cv::gpu::DeviceInfo, cv::Size) -{ - cv::Size size = GET_PARAM(1); - - cv::Mat src(size, CV_32FC2); - fill(src, -100.0, 100.0); - - cv::Mat srcs[2]; - cv::split(src, srcs); - - cv::Mat dst; - - cv::magnitude(srcs[0], srcs[1], dst); - - TEST_CYCLE() - { - cv::magnitude(srcs[0], srcs[1], dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Magnitude_Complex, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - -////////////////////////////////////////////////////////////////////// -// Magnitude - -GPU_PERF_TEST(Magnitude, cv::gpu::DeviceInfo, cv::Size) -{ - cv::Size size = GET_PARAM(1); - - cv::Mat src1(size, CV_32FC1); - fill(src1, -100.0, 100.0); - - cv::Mat src2(size, CV_32FC1); - fill(src2, -100.0, 100.0); - - cv::Mat dst; - - cv::magnitude(src1, src2, dst); - - TEST_CYCLE() - { - cv::magnitude(src1, src2, dst); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Magnitude, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - -////////////////////////////////////////////////////////////////////// -// Phase - -IMPLEMENT_PARAM_CLASS(AngleInDegrees, bool) - -GPU_PERF_TEST(Phase, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) -{ - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); - - cv::Mat src1(size, CV_32FC1); - fill(src1, -100.0, 100.0); - - cv::Mat src2(size, CV_32FC1); - fill(src2, -100.0, 100.0); - - cv::Mat dst; - - cv::phase(src1, src2, dst, angleInDegrees); - - TEST_CYCLE() - { - cv::phase(src1, src2, dst, angleInDegrees); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Phase, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - -////////////////////////////////////////////////////////////////////// -// CartToPolar - -GPU_PERF_TEST(CartToPolar, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) -{ - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); - - cv::Mat src1(size, CV_32FC1); - fill(src1, -100.0, 100.0); - - cv::Mat src2(size, CV_32FC1); - fill(src2, -100.0, 100.0); - - cv::Mat magnitude; - cv::Mat angle; - - cv::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); - - TEST_CYCLE() - { - cv::cartToPolar(src1, src2, magnitude, angle, angleInDegrees); - } -} - -INSTANTIATE_TEST_CASE_P(Core, CartToPolar, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - -////////////////////////////////////////////////////////////////////// -// PolarToCart - -GPU_PERF_TEST(PolarToCart, cv::gpu::DeviceInfo, cv::Size, AngleInDegrees) -{ - cv::Size size = GET_PARAM(1); - bool angleInDegrees = GET_PARAM(2); - - cv::Mat magnitude(size, CV_32FC1); - fill(magnitude, 0.0, 100.0); - - cv::Mat angle(size, CV_32FC1); - fill(angle, 0.0, angleInDegrees ? 360.0 : 2 * CV_PI); - - cv::Mat x; - cv::Mat y; - - cv::polarToCart(magnitude, angle, x, y, angleInDegrees); - - TEST_CYCLE() - { - cv::polarToCart(magnitude, angle, x, y, angleInDegrees); - } -} - -INSTANTIATE_TEST_CASE_P(Core, PolarToCart, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(false, true))); - -////////////////////////////////////////////////////////////////////// -// MeanStdDev - -GPU_PERF_TEST(MeanStdDev, cv::gpu::DeviceInfo, cv::Size) -{ - cv::Size size = GET_PARAM(1); - - cv::Mat src(size, CV_8UC1); - fill(src, 0.0, 255.0); - - cv::Scalar mean; - cv::Scalar stddev; - - cv::meanStdDev(src, mean, stddev); - - TEST_CYCLE() - { - cv::meanStdDev(src, mean, stddev); - } -} - -INSTANTIATE_TEST_CASE_P(Core, MeanStdDev, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - -////////////////////////////////////////////////////////////////////// -// Norm - -GPU_PERF_TEST(Norm, cv::gpu::DeviceInfo, cv::Size, MatDepth, NormType) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int normType = GET_PARAM(3); - - cv::Mat src(size, depth); - fill(src, 0.0, 255.0); - - double dst; - cv::Mat buf; - - dst = cv::norm(src, normType); - - TEST_CYCLE() - { - dst = cv::norm(src, normType); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Norm, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32S, CV_32F), - testing::Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))); - -////////////////////////////////////////////////////////////////////// -// NormDiff - -GPU_PERF_TEST(NormDiff, cv::gpu::DeviceInfo, cv::Size, NormType) -{ - cv::Size size = GET_PARAM(1); - int normType = GET_PARAM(2); - - cv::Mat src1(size, CV_8UC1); - fill(src1, 0.0, 255.0); - - cv::Mat src2(size, CV_8UC1); - fill(src2, 0.0, 255.0); - - double dst; - - dst = cv::norm(src1, src2, normType); - - TEST_CYCLE() - { - dst = cv::norm(src1, src2, normType); - } -} - -INSTANTIATE_TEST_CASE_P(Core, NormDiff, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(NormType(cv::NORM_INF), NormType(cv::NORM_L1), NormType(cv::NORM_L2)))); - -////////////////////////////////////////////////////////////////////// -// Sum - -GPU_PERF_TEST(Sum, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Scalar dst; - - dst = cv::sum(src); - - TEST_CYCLE() - { - dst = cv::sum(src); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Sum, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8UC1, CV_8UC3, CV_8UC4, CV_16UC1, CV_16UC3, CV_16UC4, CV_32FC1, CV_32FC3, CV_32FC4))); - -////////////////////////////////////////////////////////////////////// -// MinMaxLoc - -GPU_PERF_TEST(MinMaxLoc, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 255.0); - - double minVal, maxVal; - cv::Point minLoc, maxLoc; - - cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); - - TEST_CYCLE() - { - cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc); - } -} - -INSTANTIATE_TEST_CASE_P(Core, MinMaxLoc, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// CountNonZero - -GPU_PERF_TEST(CountNonZero, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0.0, 1.5); - - int dst; - - dst = cv::countNonZero(src); - - TEST_CYCLE() - { - dst = cv::countNonZero(src); - } -} - -INSTANTIATE_TEST_CASE_P(Core, CountNonZero, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_32F, CV_64F))); - -////////////////////////////////////////////////////////////////////// -// Reduce - -CV_ENUM(ReduceCode, CV_REDUCE_SUM, CV_REDUCE_AVG, CV_REDUCE_MAX, CV_REDUCE_MIN) -#define ALL_REDUCE_CODES testing::Values(CV_REDUCE_SUM, CV_REDUCE_AVG, CV_REDUCE_MAX, CV_REDUCE_MIN) - -enum {Rows = 0, Cols = 1}; -CV_ENUM(ReduceDim, Rows, Cols) - -GPU_PERF_TEST(Reduce, cv::gpu::DeviceInfo, cv::Size, MatDepth, Channels, ReduceCode, ReduceDim) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int channels = GET_PARAM(3); - int reduceOp = GET_PARAM(4); - int dim = GET_PARAM(5); - - int type = CV_MAKE_TYPE(depth, channels); - - cv::Mat src(size, type); - fill(src, 0.0, 10.0); - - cv::Mat dst; - - cv::reduce(src, dst, dim, reduceOp); - - TEST_CYCLE() - { - cv::reduce(src, dst, dim, reduceOp); - } -} - -INSTANTIATE_TEST_CASE_P(Core, Reduce, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(CV_8U, CV_16U, CV_16S, CV_32F), - testing::Values(1, 2, 3, 4), - ALL_REDUCE_CODES, - testing::Values(ReduceDim(Rows), ReduceDim(Cols)))); - -#endif diff --git a/modules/gpu/perf_cpu/perf_cpu_precomp.cpp b/modules/gpu/perf_cpu/perf_cpu_precomp.cpp deleted file mode 100644 index d947dd0258..0000000000 --- a/modules/gpu/perf_cpu/perf_cpu_precomp.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "perf_cpu_precomp.hpp" diff --git a/modules/gpu/perf_cpu/perf_cpu_precomp.hpp b/modules/gpu/perf_cpu/perf_cpu_precomp.hpp deleted file mode 100644 index 12680c7a02..0000000000 --- a/modules/gpu/perf_cpu/perf_cpu_precomp.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifdef __GNUC__ -# pragma GCC diagnostic ignored "-Wmissing-declarations" -# pragma GCC diagnostic ignored "-Wmissing-prototypes" //OSX -#endif - -#ifndef __OPENCV_PERF_CPU_PRECOMP_HPP__ -#define __OPENCV_PERF_CPU_PRECOMP_HPP__ - -#include -#include - -#include "cvconfig.h" - -#include "opencv2/ts/ts.hpp" -#include "opencv2/ts/ts_perf.hpp" - -#include "opencv2/core/core.hpp" -#include "opencv2/highgui/highgui.hpp" -#include "opencv2/gpu/gpu.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include "opencv2/video/video.hpp" -#include "opencv2/calib3d/calib3d.hpp" -#include "opencv2/nonfree/nonfree.hpp" -#include "opencv2/legacy/legacy.hpp" - -#include "perf_utility.hpp" - -#ifdef GTEST_CREATE_SHARED_LIBRARY -#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined -#endif - -#endif diff --git a/modules/gpu/perf_cpu/perf_features2d.cpp b/modules/gpu/perf_cpu/perf_features2d.cpp deleted file mode 100644 index 74579a64a2..0000000000 --- a/modules/gpu/perf_cpu/perf_features2d.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////////////////////// -// SURF - -GPU_PERF_TEST_1(SURF, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - cv::SURF surf; - - std::vector keypoints; - cv::Mat descriptors; - - surf(img, cv::noArray(), keypoints, descriptors); - - declare.time(50.0); - - TEST_CYCLE() - { - keypoints.clear(); - surf(img, cv::noArray(), keypoints, descriptors); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, SURF, ALL_DEVICES); - -////////////////////////////////////////////////////////////////////// -// FAST - -GPU_PERF_TEST_1(FAST, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - std::vector keypoints; - - cv::FAST(img, keypoints, 20); - - TEST_CYCLE() - { - keypoints.clear(); - cv::FAST(img, keypoints, 20); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, FAST, ALL_DEVICES); - -////////////////////////////////////////////////////////////////////// -// ORB - -GPU_PERF_TEST_1(ORB, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - cv::ORB orb(4000); - - std::vector keypoints; - cv::Mat descriptors; - - orb(img, cv::noArray(), keypoints, descriptors); - - TEST_CYCLE() - { - keypoints.clear(); - orb(img, cv::noArray(), keypoints, descriptors); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, ORB, ALL_DEVICES); - -////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_match - -IMPLEMENT_PARAM_CLASS(DescriptorSize, int) - -GPU_PERF_TEST(BruteForceMatcher_match, cv::gpu::DeviceInfo, DescriptorSize, NormType) -{ - int desc_size = GET_PARAM(1); - int normType = GET_PARAM(2); - - int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - - cv::Mat query(3000, desc_size, type); - fill(query, 0.0, 10.0); - - cv::Mat train(3000, desc_size, type); - fill(train, 0.0, 10.0); - - cv::BFMatcher matcher(normType); - - std::vector matches; - - matcher.match(query, train, matches); - - declare.time(20.0); - - TEST_CYCLE() - { - matcher.match(query, train, matches); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_match, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - -////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_knnMatch - -IMPLEMENT_PARAM_CLASS(K, int) - -GPU_PERF_TEST(BruteForceMatcher_knnMatch, cv::gpu::DeviceInfo, DescriptorSize, K, NormType) -{ - int desc_size = GET_PARAM(1); - int k = GET_PARAM(2); - int normType = GET_PARAM(3); - - int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - - cv::Mat query(3000, desc_size, type); - fill(query, 0.0, 10.0); - - cv::Mat train(3000, desc_size, type); - fill(train, 0.0, 10.0); - - cv::BFMatcher matcher(normType); - - std::vector< std::vector > matches; - - matcher.knnMatch(query, train, matches, k); - - declare.time(30.0); - - TEST_CYCLE() - { - matcher.knnMatch(query, train, matches, k); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_knnMatch, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(K(2), K(3)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - -////////////////////////////////////////////////////////////////////// -// BruteForceMatcher_radiusMatch - -GPU_PERF_TEST(BruteForceMatcher_radiusMatch, cv::gpu::DeviceInfo, DescriptorSize, NormType) -{ - int desc_size = GET_PARAM(1); - int normType = GET_PARAM(2); - - int type = normType == cv::NORM_HAMMING ? CV_8U : CV_32F; - - cv::Mat query(3000, desc_size, type); - fill(query, 0.0, 1.0); - - cv::Mat train(3000, desc_size, type); - fill(train, 0.0, 1.0); - - cv::BFMatcher matcher(normType); - - std::vector< std::vector > matches; - - matcher.radiusMatch(query, train, matches, 2.0); - - declare.time(30.0); - - TEST_CYCLE() - { - matcher.radiusMatch(query, train, matches, 2.0); - } -} - -INSTANTIATE_TEST_CASE_P(Features2D, BruteForceMatcher_radiusMatch, testing::Combine( - ALL_DEVICES, - testing::Values(DescriptorSize(64), DescriptorSize(128), DescriptorSize(256)), - testing::Values(NormType(cv::NORM_L1), NormType(cv::NORM_L2), NormType(cv::NORM_HAMMING)))); - -#endif diff --git a/modules/gpu/perf_cpu/perf_filters.cpp b/modules/gpu/perf_cpu/perf_filters.cpp deleted file mode 100644 index ab0be3bad9..0000000000 --- a/modules/gpu/perf_cpu/perf_filters.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -IMPLEMENT_PARAM_CLASS(KernelSize, int) - -////////////////////////////////////////////////////////////////////// -// Blur - -GPU_PERF_TEST(Blur, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::blur(src, dst, cv::Size(ksize, ksize)); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::blur(src, dst, cv::Size(ksize, ksize)); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Blur, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7)))); - -////////////////////////////////////////////////////////////////////// -// Sobel - -GPU_PERF_TEST(Sobel, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::Sobel(src, dst, -1, 1, 1, ksize); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::Sobel(src, dst, -1, 1, 1, ksize); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Sobel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - -////////////////////////////////////////////////////////////////////// -// Scharr - -GPU_PERF_TEST(Scharr, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::Scharr(src, dst, -1, 1, 0); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::Scharr(src, dst, -1, 1, 0); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Scharr, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)))); - -////////////////////////////////////////////////////////////////////// -// GaussianBlur - -GPU_PERF_TEST(GaussianBlur, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::GaussianBlur(src, dst, cv::Size(ksize, ksize), 0.5); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::GaussianBlur(src, dst, cv::Size(ksize, ksize), 0.5); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, GaussianBlur, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - -////////////////////////////////////////////////////////////////////// -// Laplacian - -GPU_PERF_TEST(Laplacian, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::Laplacian(src, dst, -1, ksize); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::Laplacian(src, dst, -1, ksize); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Laplacian, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1), MatType(CV_32FC4)), - testing::Values(KernelSize(1), KernelSize(3)))); - -////////////////////////////////////////////////////////////////////// -// Erode - -GPU_PERF_TEST(Erode, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - - cv::Mat dst; - - cv::erode(src, dst, ker); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::erode(src, dst, ker); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Erode, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)))); - -////////////////////////////////////////////////////////////////////// -// Dilate - -GPU_PERF_TEST(Dilate, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - - cv::Mat dst; - - cv::dilate(src, dst, ker); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::dilate(src, dst, ker); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Dilate, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)))); - -////////////////////////////////////////////////////////////////////// -// MorphologyEx - -CV_ENUM(MorphOp, cv::MORPH_OPEN, cv::MORPH_CLOSE, cv::MORPH_GRADIENT, cv::MORPH_TOPHAT, cv::MORPH_BLACKHAT) -#define ALL_MORPH_OPS testing::Values(MorphOp(cv::MORPH_OPEN), MorphOp(cv::MORPH_CLOSE), MorphOp(cv::MORPH_GRADIENT), MorphOp(cv::MORPH_TOPHAT), MorphOp(cv::MORPH_BLACKHAT)) - -GPU_PERF_TEST(MorphologyEx, cv::gpu::DeviceInfo, cv::Size, MatType, MorphOp) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int morphOp = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat dst; - - cv::Mat ker = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - - cv::morphologyEx(src, dst, morphOp, ker); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::morphologyEx(src, dst, morphOp, ker); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, MorphologyEx, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4)), - ALL_MORPH_OPS)); - -////////////////////////////////////////////////////////////////////// -// Filter2D - -GPU_PERF_TEST(Filter2D, cv::gpu::DeviceInfo, cv::Size, MatType, KernelSize) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int ksize = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0.0, 255.0); - - cv::Mat kernel(ksize, ksize, CV_32FC1); - fill(kernel, 0.0, 1.0); - - cv::Mat dst; - - cv::filter2D(src, dst, -1, kernel); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::filter2D(src, dst, -1, kernel); - } -} - -INSTANTIATE_TEST_CASE_P(Filters, Filter2D, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC4), MatType(CV_32FC1), MatType(CV_32FC4)), - testing::Values(KernelSize(3), KernelSize(5), KernelSize(7), KernelSize(9), KernelSize(11), KernelSize(13), KernelSize(15)))); - -#endif diff --git a/modules/gpu/perf_cpu/perf_imgproc.cpp b/modules/gpu/perf_cpu/perf_imgproc.cpp deleted file mode 100644 index 1b3c0951c6..0000000000 --- a/modules/gpu/perf_cpu/perf_imgproc.cpp +++ /dev/null @@ -1,771 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////////////////////// -// Remap - -GPU_PERF_TEST(Remap, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int interpolation = GET_PARAM(3); - int borderMode = GET_PARAM(4); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat xmap(size, CV_32FC1); - fill(xmap, 0, size.width); - - cv::Mat ymap(size, CV_32FC1); - fill(ymap, 0, size.height); - - cv::Mat dst; - - cv::remap(src, dst, xmap, ymap, interpolation, borderMode); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::remap(src, dst, xmap, ymap, interpolation, borderMode); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Remap, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - - -////////////////////////////////////////////////////////////////////// -// Resize - -IMPLEMENT_PARAM_CLASS(Scale, double) - -GPU_PERF_TEST(Resize, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, Scale) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int interpolation = GET_PARAM(3); - double f = GET_PARAM(4); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - cv::resize(src, dst, cv::Size(), f, f, interpolation); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::resize(src, dst, cv::Size(), f, f, interpolation); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Resize, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), - Interpolation(cv::INTER_CUBIC), Interpolation(cv::INTER_AREA)), - testing::Values(Scale(0.5), Scale(0.3), Scale(2.0)))); - -GPU_PERF_TEST(ResizeArea, cv::gpu::DeviceInfo, cv::Size, MatType, Scale) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int interpolation = cv::INTER_AREA; - double f = GET_PARAM(3); - - cv::Mat src_host(size, type); - fill(src_host, 0, 255); - - cv::Mat src(src_host); - cv::Mat dst; - - cv::resize(src, dst, cv::Size(), f, f, interpolation); - - declare.time(1.0); - - TEST_CYCLE() - { - cv::resize(src, dst, cv::Size(), f, f, interpolation); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, ResizeArea, testing::Combine( - ALL_DEVICES, - testing::Values(perf::sz1080p, cv::Size(4096, 2048)), - testing::Values(MatType(CV_8UC1)/*, MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)*/), - testing::Values(Scale(0.2),Scale(0.1),Scale(0.05)))); - -////////////////////////////////////////////////////////////////////// -// WarpAffine - -GPU_PERF_TEST(WarpAffine, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int interpolation = GET_PARAM(3); - int borderMode = GET_PARAM(4); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - const double aplha = CV_PI / 4; - double mat[2][3] = { {std::cos(aplha), -std::sin(aplha), src.cols / 2}, - {std::sin(aplha), std::cos(aplha), 0}}; - cv::Mat M(2, 3, CV_64F, (void*) mat); - - cv::warpAffine(src, dst, M, size, interpolation, borderMode); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::warpAffine(src, dst, M, size, interpolation, borderMode); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, WarpAffine, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - -////////////////////////////////////////////////////////////////////// -// WarpPerspective - -GPU_PERF_TEST(WarpPerspective, cv::gpu::DeviceInfo, cv::Size, MatType, Interpolation, BorderMode) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int interpolation = GET_PARAM(3); - int borderMode = GET_PARAM(4); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - const double aplha = CV_PI / 4; - double mat[3][3] = { {std::cos(aplha), -std::sin(aplha), src.cols / 2}, - {std::sin(aplha), std::cos(aplha), 0}, - {0.0, 0.0, 1.0}}; - cv::Mat M(3, 3, CV_64F, (void*) mat); - - cv::warpPerspective(src, dst, M, size, interpolation, borderMode); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::warpPerspective(src, dst, M, size, interpolation, borderMode); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, WarpPerspective, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(Interpolation(cv::INTER_NEAREST), Interpolation(cv::INTER_LINEAR), Interpolation(cv::INTER_CUBIC)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - -////////////////////////////////////////////////////////////////////// -// CopyMakeBorder - -GPU_PERF_TEST(CopyMakeBorder, cv::gpu::DeviceInfo, cv::Size, MatType, BorderMode) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - int borderType = GET_PARAM(3); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - cv::copyMakeBorder(src, dst, 5, 5, 5, 5, borderType); - - TEST_CYCLE() - { - cv::copyMakeBorder(src, dst, 5, 5, 5, 5, borderType); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, CopyMakeBorder, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_CONSTANT), BorderMode(cv::BORDER_REFLECT), BorderMode(cv::BORDER_WRAP)))); - -////////////////////////////////////////////////////////////////////// -// Threshold - -CV_ENUM(ThreshOp, cv::THRESH_BINARY, cv::THRESH_BINARY_INV, cv::THRESH_TRUNC, cv::THRESH_TOZERO, cv::THRESH_TOZERO_INV) -#define ALL_THRESH_OPS testing::Values(ThreshOp(cv::THRESH_BINARY), ThreshOp(cv::THRESH_BINARY_INV), ThreshOp(cv::THRESH_TRUNC), ThreshOp(cv::THRESH_TOZERO), ThreshOp(cv::THRESH_TOZERO_INV)) - -GPU_PERF_TEST(Threshold, cv::gpu::DeviceInfo, cv::Size, MatDepth, ThreshOp) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - int threshOp = GET_PARAM(3); - - cv::Mat src(size, depth); - fill(src, 0, 255); - - cv::Mat dst; - - cv::threshold(src, dst, 100.0, 255.0, threshOp); - - TEST_CYCLE() - { - cv::threshold(src, dst, 100.0, 255.0, threshOp); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Threshold, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)), - ALL_THRESH_OPS)); - -////////////////////////////////////////////////////////////////////// -// Integral - -GPU_PERF_TEST(Integral, cv::gpu::DeviceInfo, cv::Size) -{ - cv::Size size = GET_PARAM(1); - - cv::Mat src(size, CV_8UC1); - fill(src, 0, 255); - - cv::Mat dst; - - cv::integral(src, dst); - - TEST_CYCLE() - { - cv::integral(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Integral, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - -////////////////////////////////////////////////////////////////////// -// HistEven_OneChannel - -GPU_PERF_TEST(HistEven_OneChannel, cv::gpu::DeviceInfo, cv::Size, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - - cv::Mat src(size, depth); - fill(src, 0, 255); - - int hbins = 30; - float hranges[] = {0.0f, 180.0f}; - cv::Mat hist; - int histSize[] = {hbins}; - const float* ranges[] = {hranges}; - int channels[] = {0}; - - cv::calcHist(&src, 1, channels, cv::Mat(), hist, 1, histSize, ranges); - - TEST_CYCLE() - { - cv::calcHist(&src, 1, channels, cv::Mat(), hist, 1, histSize, ranges); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, HistEven_OneChannel, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_16S)))); - -////////////////////////////////////////////////////////////////////// -// EqualizeHist - -GPU_PERF_TEST(EqualizeHist, cv::gpu::DeviceInfo, cv::Size) -{ - cv::Size size = GET_PARAM(1); - - cv::Mat src(size, CV_8UC1); - fill(src, 0, 255); - - cv::Mat dst; - - cv::equalizeHist(src, dst); - - TEST_CYCLE() - { - cv::equalizeHist(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, EqualizeHist, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES)); - -////////////////////////////////////////////////////////////////////// -// Canny - -IMPLEMENT_PARAM_CLASS(AppertureSize, int) -IMPLEMENT_PARAM_CLASS(L2gradient, bool) - -GPU_PERF_TEST(Canny, cv::gpu::DeviceInfo, AppertureSize, L2gradient) -{ - int apperture_size = GET_PARAM(1); - bool useL2gradient = GET_PARAM(2); - - cv::Mat image = readImage("perf/1280x1024.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(image.empty()); - - cv::Mat dst; - - cv::Canny(image, dst, 50.0, 100.0, apperture_size, useL2gradient); - - TEST_CYCLE() - { - cv::Canny(image, dst, 50.0, 100.0, apperture_size, useL2gradient); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Canny, testing::Combine( - ALL_DEVICES, - testing::Values(AppertureSize(3), AppertureSize(5)), - testing::Values(L2gradient(false), L2gradient(true)))); - -////////////////////////////////////////////////////////////////////// -// MeanShiftFiltering - -GPU_PERF_TEST_1(MeanShiftFiltering, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/meanshift/cones.png"); - ASSERT_FALSE(img.empty()); - - cv::Mat dst; - - cv::pyrMeanShiftFiltering(img, dst, 50, 50); - - declare.time(15.0); - - TEST_CYCLE() - { - cv::pyrMeanShiftFiltering(img, dst, 50, 50); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, MeanShiftFiltering, ALL_DEVICES); - -////////////////////////////////////////////////////////////////////// -// Convolve - -IMPLEMENT_PARAM_CLASS(KSize, int) -IMPLEMENT_PARAM_CLASS(Ccorr, bool) - -GPU_PERF_TEST(Convolve, cv::gpu::DeviceInfo, cv::Size, KSize, Ccorr) -{ - cv::Size size = GET_PARAM(1); - int templ_size = GET_PARAM(2); - bool ccorr = GET_PARAM(3); - - ASSERT_FALSE(ccorr); - - cv::Mat image(size, CV_32FC1); - image.setTo(1.0); - - cv::Mat templ(templ_size, templ_size, CV_32FC1); - templ.setTo(1.0); - - cv::Mat dst; - - cv::filter2D(image, dst, image.depth(), templ); - - declare.time(10.0); - - TEST_CYCLE() - { - cv::filter2D(image, dst, image.depth(), templ); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Convolve, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(KSize(3), KSize(9), KSize(17), KSize(27), KSize(32), KSize(64)), - testing::Values(Ccorr(false), Ccorr(true)))); - -//////////////////////////////////////////////////////////////////////////////// -// MatchTemplate_8U - -CV_ENUM(TemplateMethod, cv::TM_SQDIFF, cv::TM_SQDIFF_NORMED, cv::TM_CCORR, cv::TM_CCORR_NORMED, cv::TM_CCOEFF, cv::TM_CCOEFF_NORMED) -#define ALL_TEMPLATE_METHODS testing::Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_SQDIFF_NORMED), TemplateMethod(cv::TM_CCORR), TemplateMethod(cv::TM_CCORR_NORMED), TemplateMethod(cv::TM_CCOEFF), TemplateMethod(cv::TM_CCOEFF_NORMED)) - -IMPLEMENT_PARAM_CLASS(TemplateSize, cv::Size) - -GPU_PERF_TEST(MatchTemplate_8U, cv::gpu::DeviceInfo, cv::Size, TemplateSize, Channels, TemplateMethod) -{ - cv::Size size = GET_PARAM(1); - cv::Size templ_size = GET_PARAM(2); - int cn = GET_PARAM(3); - int method = GET_PARAM(4); - - cv::Mat image(size, CV_MAKE_TYPE(CV_8U, cn)); - fill(image, 0, 255); - - cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_8U, cn)); - fill(templ, 0, 255); - - cv::Mat dst; - - cv::matchTemplate(image, templ, dst, method); - - TEST_CYCLE() - { - cv::matchTemplate(image, templ, dst, method); - } -}; - -INSTANTIATE_TEST_CASE_P(ImgProc, MatchTemplate_8U, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(TemplateSize(cv::Size(5, 5)), TemplateSize(cv::Size(16, 16)), TemplateSize(cv::Size(30, 30))), - testing::Values(Channels(1), Channels(3), Channels(4)), - ALL_TEMPLATE_METHODS)); - -//////////////////////////////////////////////////////////////////////////////// -// MatchTemplate_32F - -GPU_PERF_TEST(MatchTemplate_32F, cv::gpu::DeviceInfo, cv::Size, TemplateSize, Channels, TemplateMethod) -{ - cv::Size size = GET_PARAM(1); - cv::Size templ_size = GET_PARAM(2); - int cn = GET_PARAM(3); - int method = GET_PARAM(4); - - cv::Mat image(size, CV_MAKE_TYPE(CV_32F, cn)); - fill(image, 0, 255); - - cv::Mat templ(templ_size, CV_MAKE_TYPE(CV_32F, cn)); - fill(templ, 0, 255); - - cv::Mat dst; - - cv::matchTemplate(image, templ, dst, method); - - TEST_CYCLE() - { - cv::matchTemplate(image, templ, dst, method); - } -}; - -INSTANTIATE_TEST_CASE_P(ImgProc, MatchTemplate_32F, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(TemplateSize(cv::Size(5, 5)), TemplateSize(cv::Size(16, 16)), TemplateSize(cv::Size(30, 30))), - testing::Values(Channels(1), Channels(3), Channels(4)), - testing::Values(TemplateMethod(cv::TM_SQDIFF), TemplateMethod(cv::TM_CCORR)))); - -////////////////////////////////////////////////////////////////////// -// MulSpectrums - -CV_FLAGS(DftFlags, 0, cv::DFT_INVERSE, cv::DFT_SCALE, cv::DFT_ROWS, cv::DFT_COMPLEX_OUTPUT, cv::DFT_REAL_OUTPUT) - -GPU_PERF_TEST(MulSpectrums, cv::gpu::DeviceInfo, cv::Size, DftFlags) -{ - cv::Size size = GET_PARAM(1); - int flag = GET_PARAM(2); - - cv::Mat a(size, CV_32FC2); - fill(a, 0, 100); - - cv::Mat b(size, CV_32FC2); - fill(b, 0, 100); - - cv::Mat dst; - - cv::mulSpectrums(a, b, dst, flag); - - TEST_CYCLE() - { - cv::mulSpectrums(a, b, dst, flag); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, MulSpectrums, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DftFlags(0), DftFlags(cv::DFT_ROWS)))); - -////////////////////////////////////////////////////////////////////// -// Dft - -GPU_PERF_TEST(Dft, cv::gpu::DeviceInfo, cv::Size, DftFlags) -{ - cv::Size size = GET_PARAM(1); - int flag = GET_PARAM(2); - - cv::Mat src(size, CV_32FC2); - fill(src, 0, 100); - - cv::Mat dst; - - cv::dft(src, dst, flag); - - declare.time(10.0); - - TEST_CYCLE() - { - cv::dft(src, dst, flag); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, Dft, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DftFlags(0), DftFlags(cv::DFT_ROWS), DftFlags(cv::DFT_INVERSE)))); - -////////////////////////////////////////////////////////////////////// -// CornerHarris - -IMPLEMENT_PARAM_CLASS(BlockSize, int) -IMPLEMENT_PARAM_CLASS(ApertureSize, int) - -GPU_PERF_TEST(CornerHarris, cv::gpu::DeviceInfo, MatType, BorderMode, BlockSize, ApertureSize) -{ - int type = GET_PARAM(1); - int borderType = GET_PARAM(2); - int blockSize = GET_PARAM(3); - int apertureSize = GET_PARAM(4); - - cv::Mat img = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - - cv::Mat dst; - - double k = 0.5; - - cv::cornerHarris(img, dst, blockSize, apertureSize, k, borderType); - - TEST_CYCLE() - { - cv::cornerHarris(img, dst, blockSize, apertureSize, k, borderType); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, CornerHarris, testing::Combine( - ALL_DEVICES, - testing::Values(MatType(CV_8UC1), MatType(CV_32FC1)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), - testing::Values(BlockSize(3), BlockSize(5), BlockSize(7)), - testing::Values(ApertureSize(0), ApertureSize(3), ApertureSize(5), ApertureSize(7)))); - -////////////////////////////////////////////////////////////////////// -// CornerMinEigenVal - -GPU_PERF_TEST(CornerMinEigenVal, cv::gpu::DeviceInfo, MatType, BorderMode, BlockSize, ApertureSize) -{ - int type = GET_PARAM(1); - int borderType = GET_PARAM(2); - int blockSize = GET_PARAM(3); - int apertureSize = GET_PARAM(4); - - cv::Mat img = readImage("gpu/stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - img.convertTo(img, type, type == CV_32F ? 1.0 / 255.0 : 1.0); - - cv::Mat dst; - - cv::cornerMinEigenVal(img, dst, blockSize, apertureSize, borderType); - - TEST_CYCLE() - { - cv::cornerMinEigenVal(img, dst, blockSize, apertureSize, borderType); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, CornerMinEigenVal, testing::Combine( - ALL_DEVICES, - testing::Values(MatType(CV_8UC1), MatType(CV_32FC1)), - testing::Values(BorderMode(cv::BORDER_REFLECT101), BorderMode(cv::BORDER_REPLICATE), BorderMode(cv::BORDER_REFLECT)), - testing::Values(BlockSize(3), BlockSize(5), BlockSize(7)), - testing::Values(ApertureSize(0), ApertureSize(3), ApertureSize(5), ApertureSize(7)))); - -////////////////////////////////////////////////////////////////////// -// PyrDown - -GPU_PERF_TEST(PyrDown, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - cv::pyrDown(src, dst); - - TEST_CYCLE() - { - cv::pyrDown(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, PyrDown, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - -////////////////////////////////////////////////////////////////////// -// PyrUp - -GPU_PERF_TEST(PyrUp, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat dst; - - cv::pyrUp(src, dst); - - TEST_CYCLE() - { - cv::pyrUp(src, dst); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, PyrUp, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4)))); - -////////////////////////////////////////////////////////////////////// -// CvtColor - -GPU_PERF_TEST(CvtColor, cv::gpu::DeviceInfo, cv::Size, MatDepth, CvtColorInfo) -{ - cv::Size size = GET_PARAM(1); - int depth = GET_PARAM(2); - CvtColorInfo info = GET_PARAM(3); - - cv::Mat src(size, CV_MAKETYPE(depth, info.scn)); - fill(src, 0, 255); - - cv::Mat dst; - - cv::cvtColor(src, dst, info.code, info.dcn); - - TEST_CYCLE() - { - cv::cvtColor(src, dst, info.code, info.dcn); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, CvtColor, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F)), - testing::Values(CvtColorInfo(4, 4, cv::COLOR_RGBA2BGRA), - CvtColorInfo(4, 1, cv::COLOR_BGRA2GRAY), - CvtColorInfo(1, 4, cv::COLOR_GRAY2BGRA), - CvtColorInfo(3, 3, cv::COLOR_BGR2XYZ), - CvtColorInfo(3, 3, cv::COLOR_XYZ2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2YCrCb), - CvtColorInfo(3, 3, cv::COLOR_YCrCb2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2YUV), - CvtColorInfo(3, 3, cv::COLOR_YUV2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2HSV), - CvtColorInfo(3, 3, cv::COLOR_HSV2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2HLS), - CvtColorInfo(3, 3, cv::COLOR_HLS2BGR), - CvtColorInfo(3, 3, cv::COLOR_BGR2Lab), - CvtColorInfo(3, 3, cv::COLOR_RGB2Lab), - CvtColorInfo(3, 3, cv::COLOR_BGR2Luv), - CvtColorInfo(3, 3, cv::COLOR_RGB2Luv), - CvtColorInfo(3, 3, cv::COLOR_Lab2BGR), - CvtColorInfo(3, 3, cv::COLOR_Lab2RGB), - CvtColorInfo(3, 3, cv::COLOR_Luv2BGR), - CvtColorInfo(3, 3, cv::COLOR_Luv2RGB), - CvtColorInfo(1, 3, cv::COLOR_BayerBG2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerGB2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerRG2BGR), - CvtColorInfo(1, 3, cv::COLOR_BayerGR2BGR), - CvtColorInfo(4, 4, cv::COLOR_RGBA2mRGBA)))); - -////////////////////////////////////////////////////////////////////// -// HoughLines - -IMPLEMENT_PARAM_CLASS(DoSort, bool) - -GPU_PERF_TEST(HoughLines, cv::gpu::DeviceInfo, cv::Size, DoSort) -{ - declare.time(30.0); - - const cv::Size size = GET_PARAM(1); - - const float rho = 1.0f; - const float theta = CV_PI / 180.0f; - const int threshold = 300; - - cv::RNG rng(123456789); - - cv::Mat src(size, CV_8UC1, cv::Scalar::all(0)); - - const int numLines = rng.uniform(500, 2000); - for (int i = 0; i < numLines; ++i) - { - cv::Point p1(rng.uniform(0, src.cols), rng.uniform(0, src.rows)); - cv::Point p2(rng.uniform(0, src.cols), rng.uniform(0, src.rows)); - cv::line(src, p1, p2, cv::Scalar::all(255), 2); - } - - std::vector lines; - cv::HoughLines(src, lines, rho, theta, threshold); - - TEST_CYCLE() - { - cv::HoughLines(src, lines, rho, theta, threshold); - } -} - -INSTANTIATE_TEST_CASE_P(ImgProc, HoughLines, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(DoSort(false), DoSort(true)))); - -#endif diff --git a/modules/gpu/perf_cpu/perf_labeling.cpp b/modules/gpu/perf_cpu/perf_labeling.cpp deleted file mode 100644 index ddf9c3d9bb..0000000000 --- a/modules/gpu/perf_cpu/perf_labeling.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -//M*/ - -#include "perf_precomp.hpp" - -#ifdef HAVE_CUDA - -namespace { - - struct GreedyLabeling - { - struct dot - { - int x; - int y; - - static dot make(int i, int j) - { - dot d; d.x = i; d.y = j; - return d; - } - }; - - struct InInterval - { - InInterval(const int& _lo, const int& _hi) : lo(-_lo), hi(_hi) {}; - const int lo, hi; - - bool operator() (const unsigned char a, const unsigned char b) const - { - int d = a - b; - return lo <= d && d <= hi; - } - }; - - GreedyLabeling(cv::Mat img) - : image(img), _labels(image.size(), CV_32SC1, cv::Scalar::all(-1)) {stack = new dot[image.cols * image.rows];} - - ~GreedyLabeling(){delete[] stack;} - - void operator() (cv::Mat labels) const - { - labels.setTo(cv::Scalar::all(-1)); - InInterval inInt(0, 2); - int cc = -1; - - int* dist_labels = (int*)labels.data; - int pitch = labels.step1(); - - unsigned char* source = (unsigned char*)image.data; - int width = image.cols; - int height = image.rows; - - for (int j = 0; j < image.rows; ++j) - for (int i = 0; i < image.cols; ++i) - { - if (dist_labels[j * pitch + i] != -1) continue; - - dot* top = stack; - dot p = dot::make(i, j); - cc++; - - dist_labels[j * pitch + i] = cc; - - while (top >= stack) - { - int* dl = &dist_labels[p.y * pitch + p.x]; - unsigned char* sp = &source[p.y * image.step1() + p.x]; - - dl[0] = cc; - - //right - if( p.x < (width - 1) && dl[ +1] == -1 && inInt(sp[0], sp[+1])) - *top++ = dot::make(p.x + 1, p.y); - - //left - if( p.x > 0 && dl[-1] == -1 && inInt(sp[0], sp[-1])) - *top++ = dot::make(p.x - 1, p.y); - - //bottom - if( p.y < (height - 1) && dl[+pitch] == -1 && inInt(sp[0], sp[+image.step1()])) - *top++ = dot::make(p.x, p.y + 1); - - //top - if( p.y > 0 && dl[-pitch] == -1 && inInt(sp[0], sp[-image.step1()])) - *top++ = dot::make(p.x, p.y - 1); - - p = *--top; - } - } - } - - cv::Mat image; - cv::Mat _labels; - dot* stack; - }; -} - -GPU_PERF_TEST(ConnectedComponents, cv::gpu::DeviceInfo, cv::Size) -{ - cv::gpu::DeviceInfo devInfo = GET_PARAM(0); - cv::gpu::setDevice(devInfo.deviceID()); - - cv::Mat image = readImage("gpu/labeling/aloe-disp.png", cv::IMREAD_GRAYSCALE); - - GreedyLabeling host(image); - - host(host._labels); - - declare.time(1.0); - - TEST_CYCLE() - { - host(host._labels); - } -} - -INSTANTIATE_TEST_CASE_P(Labeling, ConnectedComponents, testing::Combine(ALL_DEVICES, testing::Values(cv::Size(261, 262)))); - -#endif \ No newline at end of file diff --git a/modules/gpu/perf_cpu/perf_main.cpp b/modules/gpu/perf_cpu/perf_main.cpp deleted file mode 100644 index 6fcf56d0b0..0000000000 --- a/modules/gpu/perf_cpu/perf_main.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - perf::TestBase::Init(argc, argv); - return RUN_ALL_TESTS(); -} - -#else - -int main() -{ - printf("OpenCV was built without CUDA support\n"); - return 0; -} - -#endif diff --git a/modules/gpu/perf_cpu/perf_matop.cpp b/modules/gpu/perf_cpu/perf_matop.cpp deleted file mode 100644 index 7c46eee80e..0000000000 --- a/modules/gpu/perf_cpu/perf_matop.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////////////////////// -// SetTo - -GPU_PERF_TEST(SetTo, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - cv::Scalar val(1, 2, 3, 4); - - src.setTo(val); - - TEST_CYCLE() - { - src.setTo(val); - } -} - -INSTANTIATE_TEST_CASE_P(MatOp, SetTo, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - -////////////////////////////////////////////////////////////////////// -// SetToMasked - -GPU_PERF_TEST(SetToMasked, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat mask(size, CV_8UC1); - fill(mask, 0, 2); - - cv::Scalar val(1, 2, 3, 4); - - src.setTo(val, mask); - - TEST_CYCLE() - { - src.setTo(val, mask); - } -} - -INSTANTIATE_TEST_CASE_P(MatOp, SetToMasked, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - -////////////////////////////////////////////////////////////////////// -// CopyToMasked - -GPU_PERF_TEST(CopyToMasked, cv::gpu::DeviceInfo, cv::Size, MatType) -{ - cv::Size size = GET_PARAM(1); - int type = GET_PARAM(2); - - cv::Mat src(size, type); - fill(src, 0, 255); - - cv::Mat mask(size, CV_8UC1); - fill(mask, 0, 2); - - cv::Mat dst; - - src.copyTo(dst, mask); - - TEST_CYCLE() - { - src.copyTo(dst, mask); - } -} - -INSTANTIATE_TEST_CASE_P(MatOp, CopyToMasked, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatType(CV_8UC1), MatType(CV_8UC3), MatType(CV_8UC4), - MatType(CV_16UC1), MatType(CV_16UC3), MatType(CV_16UC4), - MatType(CV_32FC1), MatType(CV_32FC3), MatType(CV_32FC4), - MatType(CV_64FC1), MatType(CV_64FC3), MatType(CV_64FC4)))); - -////////////////////////////////////////////////////////////////////// -// ConvertTo - -GPU_PERF_TEST(ConvertTo, cv::gpu::DeviceInfo, cv::Size, MatDepth, MatDepth) -{ - cv::Size size = GET_PARAM(1); - int depth1 = GET_PARAM(2); - int depth2 = GET_PARAM(3); - - cv::Mat src(size, depth1); - fill(src, 0, 255); - - cv::Mat dst; - - src.convertTo(dst, depth2, 0.5, 1.0); - - TEST_CYCLE() - { - src.convertTo(dst, depth2, 0.5, 1.0); - } -} - -INSTANTIATE_TEST_CASE_P(MatOp, ConvertTo, testing::Combine( - ALL_DEVICES, - GPU_TYPICAL_MAT_SIZES, - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)), - testing::Values(MatDepth(CV_8U), MatDepth(CV_16U), MatDepth(CV_32F), MatDepth(CV_64F)))); - -#endif diff --git a/modules/gpu/perf_cpu/perf_objdetect.cpp b/modules/gpu/perf_cpu/perf_objdetect.cpp deleted file mode 100644 index d9ae2b7c8f..0000000000 --- a/modules/gpu/perf_cpu/perf_objdetect.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -/////////////////////////////////////////////////////////////// -// HOG - -GPU_PERF_TEST_1(HOG, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/hog/road.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - std::vector found_locations; - - cv::HOGDescriptor hog; - hog.setSVMDetector(cv::gpu::HOGDescriptor::getDefaultPeopleDetector()); - - hog.detectMultiScale(img, found_locations); - - TEST_CYCLE() - { - hog.detectMultiScale(img, found_locations); - } -} - -INSTANTIATE_TEST_CASE_P(ObjDetect, HOG, ALL_DEVICES); - -/////////////////////////////////////////////////////////////// -// HaarClassifier - -GPU_PERF_TEST_1(HaarClassifier, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/haarcascade/group_1_640x480_VGA.pgm", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - cv::CascadeClassifier cascade; - - ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/perf/haarcascade_frontalface_alt.xml"))); - - std::vector rects; - - cascade.detectMultiScale(img, rects); - - TEST_CYCLE() - { - cascade.detectMultiScale(img, rects); - } -} - -INSTANTIATE_TEST_CASE_P(ObjDetect, HaarClassifier, ALL_DEVICES); - -//===================== LBP cascade ==========================// -GPU_PERF_TEST_1(LBPClassifier, cv::gpu::DeviceInfo) -{ - cv::Mat img = readImage("gpu/haarcascade/group_1_640x480_VGA.pgm", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); - - cv::CascadeClassifier cascade; - - ASSERT_TRUE(cascade.load(perf::TestBase::getDataPath("gpu/lbpcascade/lbpcascade_frontalface.xml"))); - - std::vector rects; - - cascade.detectMultiScale(img, rects); - - TEST_CYCLE() - { - cascade.detectMultiScale(img, rects); - } -} - -INSTANTIATE_TEST_CASE_P(ObjDetect, LBPClassifier, ALL_DEVICES); - -#endif diff --git a/modules/gpu/perf_cpu/perf_utility.cpp b/modules/gpu/perf_cpu/perf_utility.cpp deleted file mode 100644 index 541e6fdc7b..0000000000 --- a/modules/gpu/perf_cpu/perf_utility.cpp +++ /dev/null @@ -1,220 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -using namespace std; -using namespace cv; -using namespace cv::gpu; - -void fill(Mat& m, double a, double b) -{ - RNG rng(123456789); - rng.fill(m, RNG::UNIFORM, a, b); -} - -void PrintTo(const CvtColorInfo& info, ostream* os) -{ - static const char* str[] = - { - "BGR2BGRA", - "BGRA2BGR", - "BGR2RGBA", - "RGBA2BGR", - "BGR2RGB", - "BGRA2RGBA", - - "BGR2GRAY", - "RGB2GRAY", - "GRAY2BGR", - "GRAY2BGRA", - "BGRA2GRAY", - "RGBA2GRAY", - - "BGR2BGR565", - "RGB2BGR565", - "BGR5652BGR", - "BGR5652RGB", - "BGRA2BGR565", - "RGBA2BGR565", - "BGR5652BGRA", - "BGR5652RGBA", - - "GRAY2BGR565", - "BGR5652GRAY", - - "BGR2BGR555", - "RGB2BGR555", - "BGR5552BGR", - "BGR5552RGB", - "BGRA2BGR555", - "RGBA2BGR555", - "BGR5552BGRA", - "BGR5552RGBA", - - "GRAY2BGR555", - "BGR5552GRAY", - - "BGR2XYZ", - "RGB2XYZ", - "XYZ2BGR", - "XYZ2RGB", - - "BGR2YCrCb", - "RGB2YCrCb", - "YCrCb2BGR", - "YCrCb2RGB", - - "BGR2HSV", - "RGB2HSV", - - "", - "", - - "BGR2Lab", - "RGB2Lab", - - "BayerBG2BGR", - "BayerGB2BGR", - "BayerRG2BGR", - "BayerGR2BGR", - - "BGR2Luv", - "RGB2Luv", - - "BGR2HLS", - "RGB2HLS", - - "HSV2BGR", - "HSV2RGB", - - "Lab2BGR", - "Lab2RGB", - "Luv2BGR", - "Luv2RGB", - - "HLS2BGR", - "HLS2RGB", - - "BayerBG2BGR_VNG", - "BayerGB2BGR_VNG", - "BayerRG2BGR_VNG", - "BayerGR2BGR_VNG", - - "BGR2HSV_FULL", - "RGB2HSV_FULL", - "BGR2HLS_FULL", - "RGB2HLS_FULL", - - "HSV2BGR_FULL", - "HSV2RGB_FULL", - "HLS2BGR_FULL", - "HLS2RGB_FULL", - - "LBGR2Lab", - "LRGB2Lab", - "LBGR2Luv", - "LRGB2Luv", - - "Lab2LBGR", - "Lab2LRGB", - "Luv2LBGR", - "Luv2LRGB", - - "BGR2YUV", - "RGB2YUV", - "YUV2BGR", - "YUV2RGB", - - "BayerBG2GRAY", - "BayerGB2GRAY", - "BayerRG2GRAY", - "BayerGR2GRAY", - - //YUV 4:2:0 formats family - "YUV2RGB_NV12", - "YUV2BGR_NV12", - "YUV2RGB_NV21", - "YUV2BGR_NV21", - - "YUV2RGBA_NV12", - "YUV2BGRA_NV12", - "YUV2RGBA_NV21", - "YUV2BGRA_NV21", - - "YUV2RGB_YV12", - "YUV2BGR_YV12", - "YUV2RGB_IYUV", - "YUV2BGR_IYUV", - - "YUV2RGBA_YV12", - "YUV2BGRA_YV12", - "YUV2RGBA_IYUV", - "YUV2BGRA_IYUV", - - "YUV2GRAY_420", - - //YUV 4:2:2 formats family - "YUV2RGB_UYVY", - "YUV2BGR_UYVY", - "YUV2RGB_VYUY", - "YUV2BGR_VYUY", - - "YUV2RGBA_UYVY", - "YUV2BGRA_UYVY", - "YUV2RGBA_VYUY", - "YUV2BGRA_VYUY", - - "YUV2RGB_YUY2", - "YUV2BGR_YUY2", - "YUV2RGB_YVYU", - "YUV2BGR_YVYU", - - "YUV2RGBA_YUY2", - "YUV2BGRA_YUY2", - "YUV2RGBA_YVYU", - "YUV2BGRA_YVYU", - - "YUV2GRAY_UYVY", - "YUV2GRAY_YUY2", - - // alpha premultiplication - "RGBA2mRGBA", - "mRGBA2RGBA", - - "COLORCVT_MAX" - }; - - *os << str[info.code]; -} - -void cv::gpu::PrintTo(const DeviceInfo& info, ostream* os) -{ - *os << info.name(); -} - -Mat readImage(const string& fileName, int flags) -{ - return imread(perf::TestBase::getDataPath(fileName), flags); -} - -const vector& devices() -{ - static vector devs; - static bool first = true; - - if (first) - { - int deviceCount = getCudaEnabledDeviceCount(); - - devs.reserve(deviceCount); - - for (int i = 0; i < deviceCount; ++i) - { - DeviceInfo info(i); - if (info.isCompatible()) - devs.push_back(info); - } - - first = false; - } - - return devs; -} diff --git a/modules/gpu/perf_cpu/perf_utility.hpp b/modules/gpu/perf_cpu/perf_utility.hpp deleted file mode 100644 index 8693cfc3c0..0000000000 --- a/modules/gpu/perf_cpu/perf_utility.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef __OPENCV_PERF_GPU_UTILITY_HPP__ -#define __OPENCV_PERF_GPU_UTILITY_HPP__ - -void fill(cv::Mat& m, double a, double b); - -using perf::MatType; -using perf::MatDepth; - -CV_ENUM(BorderMode, cv::BORDER_REFLECT101, cv::BORDER_REPLICATE, cv::BORDER_CONSTANT, cv::BORDER_REFLECT, cv::BORDER_WRAP) - CV_ENUM(Interpolation, cv::INTER_NEAREST, cv::INTER_LINEAR, cv::INTER_CUBIC, cv::INTER_AREA) -CV_ENUM(NormType, cv::NORM_INF, cv::NORM_L1, cv::NORM_L2, cv::NORM_HAMMING) - -struct CvtColorInfo -{ - int scn; - int dcn; - int code; - - explicit CvtColorInfo(int scn_=0, int dcn_=0, int code_=0) : scn(scn_), dcn(dcn_), code(code_) {} -}; - -void PrintTo(const CvtColorInfo& info, std::ostream* os); - -#define IMPLEMENT_PARAM_CLASS(name, type) \ - class name \ - { \ - public: \ - name ( type arg = type ()) : val_(arg) {} \ - operator type () const {return val_;} \ - private: \ - type val_; \ - }; \ - inline void PrintTo( name param, std::ostream* os) \ - { \ - *os << #name << " = " << testing::PrintToString(static_cast< type >(param)); \ - } - -IMPLEMENT_PARAM_CLASS(Channels, int) - -namespace cv { namespace gpu -{ - void PrintTo(const cv::gpu::DeviceInfo& info, std::ostream* os); -}} - -#define GPU_PERF_TEST(name, ...) \ - struct name : perf::TestBaseWithParam< std::tr1::tuple< __VA_ARGS__ > > \ - { \ - public: \ - name() {} \ - protected: \ - void PerfTestBody(); \ - }; \ - TEST_P(name, perf){ RunPerfTestBody(); } \ - void name :: PerfTestBody() - -#define GPU_PERF_TEST_1(name, param_type) \ - struct name : perf::TestBaseWithParam< param_type > \ - { \ - public: \ - name() {} \ - protected: \ - void PerfTestBody(); \ - }; \ - TEST_P(name, perf){ RunPerfTestBody(); } \ - void name :: PerfTestBody() - -#define GPU_TYPICAL_MAT_SIZES testing::Values(perf::szSXGA, perf::sz1080p, cv::Size(1800, 1500)) - -cv::Mat readImage(const std::string& fileName, int flags = cv::IMREAD_COLOR); - -const std::vector& devices(); - -#define ALL_DEVICES testing::ValuesIn(devices()) - -#define GET_PARAM(k) std::tr1::get< k >(GetParam()) - -#endif // __OPENCV_PERF_GPU_UTILITY_HPP__ diff --git a/modules/gpu/perf_cpu/perf_video.cpp b/modules/gpu/perf_cpu/perf_video.cpp deleted file mode 100644 index 2c3aeb31ce..0000000000 --- a/modules/gpu/perf_cpu/perf_video.cpp +++ /dev/null @@ -1,466 +0,0 @@ -#include "perf_cpu_precomp.hpp" - -#ifdef HAVE_CUDA - -////////////////////////////////////////////////////// -// GoodFeaturesToTrack - -IMPLEMENT_PARAM_CLASS(MinDistance, double) - -GPU_PERF_TEST(GoodFeaturesToTrack, cv::gpu::DeviceInfo, MinDistance) -{ - double minDistance = GET_PARAM(1); - - cv::Mat image = readImage("gpu/perf/aloe.jpg", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(image.empty()); - - cv::Mat corners; - - cv::goodFeaturesToTrack(image, corners, 8000, 0.01, minDistance); - - TEST_CYCLE() - { - cv::goodFeaturesToTrack(image, corners, 8000, 0.01, minDistance); - } -} - -INSTANTIATE_TEST_CASE_P(Video, GoodFeaturesToTrack, testing::Combine( - ALL_DEVICES, - testing::Values(MinDistance(0.0), MinDistance(3.0)))); - -////////////////////////////////////////////////////// -// PyrLKOpticalFlowSparse - -IMPLEMENT_PARAM_CLASS(GraySource, bool) -IMPLEMENT_PARAM_CLASS(Points, int) -IMPLEMENT_PARAM_CLASS(WinSize, int) -IMPLEMENT_PARAM_CLASS(Levels, int) -IMPLEMENT_PARAM_CLASS(Iters, int) - -GPU_PERF_TEST(PyrLKOpticalFlowSparse, cv::gpu::DeviceInfo, GraySource, Points, WinSize, Levels, Iters) -{ - bool useGray = GET_PARAM(1); - int points = GET_PARAM(2); - int win_size = GET_PARAM(3); - int levels = GET_PARAM(4); - int iters = GET_PARAM(5); - - cv::Mat frame0 = readImage("gpu/opticalflow/frame0.png", useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); - ASSERT_FALSE(frame0.empty()); - - cv::Mat frame1 = readImage("gpu/opticalflow/frame1.png", useGray ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR); - ASSERT_FALSE(frame1.empty()); - - cv::Mat gray_frame; - if (useGray) - gray_frame = frame0; - else - cv::cvtColor(frame0, gray_frame, cv::COLOR_BGR2GRAY); - - cv::Mat pts; - cv::goodFeaturesToTrack(gray_frame, pts, points, 0.01, 0.0); - - cv::Mat nextPts; - cv::Mat status; - - cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts, status, cv::noArray(), - cv::Size(win_size, win_size), levels - 1, - cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, iters, 0.01)); - - declare.time(20.0); - - TEST_CYCLE() - { - cv::calcOpticalFlowPyrLK(frame0, frame1, pts, nextPts, status, cv::noArray(), - cv::Size(win_size, win_size), levels - 1, - cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, iters, 0.01)); - } -} - -INSTANTIATE_TEST_CASE_P(Video, PyrLKOpticalFlowSparse, testing::Combine( - ALL_DEVICES, - testing::Values(GraySource(true), GraySource(false)), - testing::Values(Points(1000), Points(2000), Points(4000), Points(8000)), - testing::Values(WinSize(9), WinSize(13), WinSize(17), WinSize(21)), - testing::Values(Levels(1), Levels(2), Levels(3)), - testing::Values(Iters(1), Iters(10), Iters(30)))); - -////////////////////////////////////////////////////// -// FarnebackOpticalFlowTest - -GPU_PERF_TEST_1(FarnebackOpticalFlowTest, cv::gpu::DeviceInfo) -{ - cv::Mat frame0 = readImage("gpu/opticalflow/frame0.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame0.empty()); - - cv::Mat frame1 = readImage("gpu/opticalflow/frame1.png", cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(frame1.empty()); - - cv::Mat flow; - - int numLevels = 5; - double pyrScale = 0.5; - int winSize = 13; - int numIters = 10; - int polyN = 5; - double polySigma = 1.1; - int flags = 0; - - cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags); - - declare.time(10); - - TEST_CYCLE() - { - cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags); - } -} - -INSTANTIATE_TEST_CASE_P(Video, FarnebackOpticalFlowTest, ALL_DEVICES); - -////////////////////////////////////////////////////// -// FGDStatModel - -namespace cv -{ - template<> void Ptr::delete_obj() - { - cvReleaseBGStatModel(&obj); - } -} - -GPU_PERF_TEST(FGDStatModel, cv::gpu::DeviceInfo, std::string) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - - cv::Mat frame; - cap >> frame; - ASSERT_FALSE(frame.empty()); - - IplImage ipl_frame = frame; - cv::Ptr model(cvCreateFGDStatModel(&ipl_frame)); - - declare.time(60); - - for (int i = 0; i < 10; ++i) - { - cap >> frame; - ASSERT_FALSE(frame.empty()); - - ipl_frame = frame; - - startTimer(); - next(); - - cvUpdateBGStatModel(&ipl_frame, model); - - stopTimer(); - } -} - -INSTANTIATE_TEST_CASE_P(Video, FGDStatModel, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - -////////////////////////////////////////////////////// -// MOG - -IMPLEMENT_PARAM_CLASS(LearningRate, double) - -GPU_PERF_TEST(MOG, cv::gpu::DeviceInfo, std::string, Channels, LearningRate) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - double learningRate = GET_PARAM(3); - - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - - cv::Mat frame; - - cv::BackgroundSubtractorMOG mog; - cv::Mat foreground; - - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - mog(frame, foreground, learningRate); - - for (int i = 0; i < 10; ++i) - { - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - startTimer(); next(); - mog(frame, foreground, learningRate); - stopTimer(); - } -} - -INSTANTIATE_TEST_CASE_P(Video, MOG, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3)/*, Channels(4)*/), - testing::Values(LearningRate(0.0), LearningRate(0.01)))); - -////////////////////////////////////////////////////// -// MOG2 - -GPU_PERF_TEST(MOG2_update, cv::gpu::DeviceInfo, std::string, Channels) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - - cv::Mat frame; - - cv::BackgroundSubtractorMOG2 mog2; - cv::Mat foreground; - - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - mog2(frame, foreground); - - for (int i = 0; i < 10; ++i) - { - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - startTimer(); next(); - mog2(frame, foreground); - stopTimer(); - } -} - -INSTANTIATE_TEST_CASE_P(Video, MOG2_update, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3)/*, Channels(4)*/))); - -GPU_PERF_TEST(MOG2_getBackgroundImage, cv::gpu::DeviceInfo, std::string, Channels) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - - cv::Mat frame; - - cv::BackgroundSubtractorMOG2 mog2; - cv::Mat foreground; - - for (int i = 0; i < 10; ++i) - { - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - mog2(frame, foreground); - } - - cv::Mat background; - mog2.getBackgroundImage(background); - - TEST_CYCLE() - { - mog2.getBackgroundImage(background); - } -} - -INSTANTIATE_TEST_CASE_P(Video, MOG2_getBackgroundImage, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(/*Channels(1),*/ Channels(3)/*, Channels(4)*/))); - -////////////////////////////////////////////////////// -// GMG - -IMPLEMENT_PARAM_CLASS(MaxFeatures, int) - -GPU_PERF_TEST(GMG, cv::gpu::DeviceInfo, std::string, Channels, MaxFeatures) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - int cn = GET_PARAM(2); - int maxFeatures = GET_PARAM(3); - - cv::VideoCapture cap(inputFile); - ASSERT_TRUE(cap.isOpened()); - - cv::Mat frame; - cap >> frame; - ASSERT_FALSE(frame.empty()); - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - cv::Mat fgmask; - cv::Mat zeros(frame.size(), CV_8UC1, cv::Scalar::all(0)); - - cv::BackgroundSubtractorGMG gmg; - gmg.set("maxFeatures", maxFeatures); - gmg.initialize(frame.size(), 0.0, 255.0); - - gmg(frame, fgmask); - - for (int i = 0; i < 150; ++i) - { - cap >> frame; - if (frame.empty()) - { - cap.open(inputFile); - cap >> frame; - } - - if (cn != 3) - { - cv::Mat temp; - if (cn == 1) - cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY); - else - cv::cvtColor(frame, temp, cv::COLOR_BGR2BGRA); - cv::swap(temp, frame); - } - - startTimer(); next(); - gmg(frame, fgmask); - stopTimer(); - } -} - -INSTANTIATE_TEST_CASE_P(Video, GMG, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")), - testing::Values(Channels(1), Channels(3), Channels(4)), - testing::Values(MaxFeatures(20), MaxFeatures(40), MaxFeatures(60)))); - -////////////////////////////////////////////////////// -// VideoWriter - -#ifdef WIN32 - -GPU_PERF_TEST(VideoWriter, cv::gpu::DeviceInfo, std::string) -{ - const double FPS = 25.0; - - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - std::string outputFile = cv::tempfile(".avi"); - - cv::VideoCapture reader(inputFile); - ASSERT_TRUE( reader.isOpened() ); - - cv::VideoWriter writer; - - cv::Mat frame; - - declare.time(30); - - for (int i = 0; i < 10; ++i) - { - reader >> frame; - ASSERT_FALSE(frame.empty()); - - if (!writer.isOpened()) - writer.open(outputFile, CV_FOURCC('X', 'V', 'I', 'D'), FPS, frame.size()); - - startTimer(); next(); - writer.write(frame); - stopTimer(); - } -} - -INSTANTIATE_TEST_CASE_P(Video, VideoWriter, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - -#endif // WIN32 - -////////////////////////////////////////////////////// -// VideoReader - -GPU_PERF_TEST(VideoReader, cv::gpu::DeviceInfo, std::string) -{ - std::string inputFile = perf::TestBase::getDataPath(std::string("gpu/video/") + GET_PARAM(1)); - - cv::VideoCapture reader(inputFile); - ASSERT_TRUE( reader.isOpened() ); - - cv::Mat frame; - - reader >> frame; - - declare.time(20); - - TEST_CYCLE_N(10) - { - reader >> frame; - } -} - -INSTANTIATE_TEST_CASE_P(Video, VideoReader, testing::Combine( - ALL_DEVICES, - testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); - -#endif From 915169e8b7b4d3293e375eb18e59a07bd4ae2d55 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 17 Aug 2012 17:09:26 +0400 Subject: [PATCH 11/58] Fix documentation build warnings in gpu module reference --- modules/gpu/doc/video.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/gpu/doc/video.rst b/modules/gpu/doc/video.rst index 378cca71ab..c4f84484a1 100644 --- a/modules/gpu/doc/video.rst +++ b/modules/gpu/doc/video.rst @@ -653,7 +653,7 @@ gpu::GMG_GPU ------------ .. ocv:class:: gpu::GMG_GPU -Class used for background/foreground segmentation. :: + Class used for background/foreground segmentation. :: class GMG_GPU_GPU { @@ -677,9 +677,9 @@ Class used for background/foreground segmentation. :: ... }; -The class discriminates between foreground and background pixels by building and maintaining a model of the background. Any pixel which does not fit this model is then deemed to be foreground. The class implements algorithm described in [GMG2012]_. + The class discriminates between foreground and background pixels by building and maintaining a model of the background. Any pixel which does not fit this model is then deemed to be foreground. The class implements algorithm described in [GMG2012]_. -Here are important members of the class that control the algorithm, which you can set after constructing the class instance: + Here are important members of the class that control the algorithm, which you can set after constructing the class instance: .. ocv:member:: int maxFeatures From 9d6ccecfddd55dd6c72a8dfc372a57a394ef3abd Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 17 Aug 2012 18:12:40 +0400 Subject: [PATCH 12/58] Fixed layout in third Android tutorial --- doc/_themes/blue/static/default.css_t | 29 +- .../dev_with_OCV_on_Android.rst | 449 +++++++++--------- 2 files changed, 247 insertions(+), 231 deletions(-) diff --git a/doc/_themes/blue/static/default.css_t b/doc/_themes/blue/static/default.css_t index 648e106b50..50a544218a 100644 --- a/doc/_themes/blue/static/default.css_t +++ b/doc/_themes/blue/static/default.css_t @@ -175,6 +175,8 @@ a:hover { div.body p, div.body dd, div.body li { text-align: justify; line-height: 130%; + margin-top: 1em; + margin-bottom: 1em; } div.body h1, @@ -327,16 +329,16 @@ table.field-list { margin-top: 20px; } -ul.simple { +/*ul.simple { list-style: none; -} +}*/ em.menuselection, em.guilabel { font-family: {{ theme_guifont }}; } .enumeratevisibleitemswithsquare ul { -list-style: square; +list-style: square; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; @@ -349,25 +351,25 @@ margin-left: 0px; margin-right: 0px; margin-top: 0.2em; } - + .enumeratevisibleitemswithsquare p { margin-bottom: 0pt; margin-top: 1pt; } - + .enumeratevisibleitemswithsquare dl{ margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; } - + .toctableopencv { - width: 100% ; + width: 100% ; table-layout: fixed; } - + .toctableopencv colgroup col:first-child { @@ -375,12 +377,17 @@ margin-top: 0px; max-width: 100pt !important; min-width: 100pt !important; } - - .toctableopencv colgroup col:nth-child(2) + + .toctableopencv colgroup col:nth-child(2) { width: 100% !important; } - + div.body ul.search li { text-align: left; } + +div.linenodiv { + min-width: 1em; + text-align: right; +} \ No newline at end of file diff --git a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst index ca0211a881..d56bd1bc4e 100644 --- a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst +++ b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst @@ -57,12 +57,12 @@ Using async initialization is a **recommended** way for application development. To run OpenCV Manager-based application the first time you need to install packages with the `OpenCV Manager` and `OpenCV binary pack` for you platform. You can do it using Google Play Market or manually with ``adb`` tool: - .. code-block:: sh +.. code-block:: sh :linenos: /platform-tools/adb install /apk/OpenCV_2.4.2_Manager.apk /platform-tools/adb install /apk/OpenCV_2.4.2_binary_pack_armv7a.apk - + There is a very base code snippet implementing the async initialization. It shows basic principles. See the "15-puzzle" OpenCV sample for details. .. code-block:: java @@ -107,7 +107,7 @@ There is a very base code snippet implementing the async initialization. It show } It this case application works with OpenCV Manager in asynchronous fashion. ``OnManagerConnected`` callback will be called in UI thread, when initialization finishes. -Please note, that it is not allowed to use OpenCV calls or load OpenCV-dependent native libs before invoking this callback. +Please note, that it is not allowed to use OpenCV calls or load OpenCV-dependent native libs before invoking this callback. Load your own native libraries that depend on OpenCV after the successful OpenCV initialization. Application development with static initialization @@ -130,27 +130,27 @@ This approach is deprecated for the production code, release package is recommen :align: center #. If your application project **doesn't have a JNI part**, just copy the corresponding OpenCV native libs from :file:`/sdk/native/libs/` to your project directory to folder :file:`libs/`. - - In case of the application project **with a JNI part**, instead of manual libraries copying you need to modify your ``Android.mk`` file: + + In case of the application project **with a JNI part**, instead of manual libraries copying you need to modify your ``Android.mk`` file: add the following two code lines after the ``"include $(CLEAR_VARS)"`` and before ``"include path_to_OpenCV-2.4.2-android-sdk/sdk/native/jni/OpenCV.mk"`` .. code-block:: make - :linenos: + :linenos: + + OPENCV_CAMERA_MODULES:=on + OPENCV_INSTALL_MODULES:=on - OPENCV_CAMERA_MODULES:=on - OPENCV_INSTALL_MODULES:=on - The result should look like the following: - + .. code-block:: make - :linenos: + :linenos: - include $(CLEAR_VARS) + include $(CLEAR_VARS) - # OpenCV - OPENCV_CAMERA_MODULES:=on - OPENCV_INSTALL_MODULES:=on - include ../../sdk/native/jni/OpenCV.mk + # OpenCV + OPENCV_CAMERA_MODULES:=on + OPENCV_INSTALL_MODULES:=on + include ../../sdk/native/jni/OpenCV.mk After that the OpenCV libraries will be copied to your application :file:`libs` folder during the JNI part build. @@ -159,28 +159,28 @@ This approach is deprecated for the production code, release package is recommen #. The last step of enabling OpenCV in your application is Java initialization code before call to OpenCV API. It can be done, for example, in the static section of the ``Activity`` class: - .. code-block:: java - :linenos: + .. code-block:: java + :linenos: - static { - if (!OpenCVLoader.initDebug()) { - // Handle initialization error - } - } + static { + if (!OpenCVLoader.initDebug()) { + // Handle initialization error + } + } - If you application includes other OpenCV-dependent native libraries you should load them **after** OpenCV initialization: + If you application includes other OpenCV-dependent native libraries you should load them **after** OpenCV initialization: - .. code-block:: java - :linenos: + .. code-block:: java + :linenos: - static { - if (!OpenCVLoader.initDebug()) { - // Handle initialization error - } else { - System.loadLibrary("my_jni_lib1"); - System.loadLibrary("my_jni_lib2"); - } - } + static { + if (!OpenCVLoader.initDebug()) { + // Handle initialization error + } else { + System.loadLibrary("my_jni_lib1"); + System.loadLibrary("my_jni_lib2"); + } + } Native/C++ ---------- @@ -198,33 +198,33 @@ To build your own Android application, which uses OpenCV from native part, the f .. code-block:: make - include C:\Work\OpenCV4Android\OpenCV-2.4.2-android-sdk\sdk\native\jni\OpenCV.mk + include C:\Work\OpenCV4Android\OpenCV-2.4.2-android-sdk\sdk\native\jni\OpenCV.mk should be inserted into the :file:`jni/Android.mk` file **after** the line .. code-block:: make - include $(CLEAR_VARS) + include $(CLEAR_VARS) #. Several variables can be used to customize OpenCV stuff, but you **don't need** to use them when your application uses the `async initialization` via the `OpenCV Manager` API. - + Note: these variables should be set **before** the ``"include .../OpenCV.mk"`` line: .. code-block:: make - OPENCV_INSTALL_MODULES:=on + OPENCV_INSTALL_MODULES:=on Copies necessary OpenCV dynamic libs to the project ``libs`` folder in order to include them into the APK. .. code-block:: make - OPENCV_CAMERA_MODULES:=off + OPENCV_CAMERA_MODULES:=off Skip native OpenCV camera related libs copying to the project ``libs`` folder. .. code-block:: make - OPENCV_LIB_TYPE:=STATIC + OPENCV_LIB_TYPE:=STATIC Perform static link with OpenCV. By default dynamic link is used and the project JNI lib depends on ``libopencv_java.so``. @@ -232,14 +232,14 @@ To build your own Android application, which uses OpenCV from native part, the f .. code-block:: make - APP_STL := gnustl_static - APP_CPPFLAGS := -frtti -fexceptions + APP_STL := gnustl_static + APP_CPPFLAGS := -frtti -fexceptions Also the line like this one: .. code-block:: make - APP_ABI := armeabi-v7a + APP_ABI := armeabi-v7a should specify the application target platforms. @@ -249,11 +249,11 @@ To build your own Android application, which uses OpenCV from native part, the f .. code-block:: make - APP_PLATFORM := android-9 + APP_PLATFORM := android-9 #. Either use :ref:`manual ` ``ndk-build`` invocation or :ref:`setup Eclipse CDT Builder ` to build native JNI lib before Java part [re]build and APK creation. - + Hello OpenCV Sample =================== @@ -262,208 +262,217 @@ Here are basic steps to guide you trough the process of creating a simple OpenCV It will be capable of accessing camera output, processing it and displaying the result. #. Open Eclipse IDE, create a new clean workspace, create a new Android project (*File -> New -> Android Project*). - + #. Set name, target, package and minSDKVersion accordingly. - + #. Create a new class (*File -> New -> Class*). Name it for example: *HelloOpenCVView*. - .. image:: images/dev_OCV_new_class.png - :alt: Add a new class. - :align: center - * It should extend *SurfaceView* class. + .. image:: images/dev_OCV_new_class.png + :alt: Add a new class. + :align: center - * It also should implement *SurfaceHolder.Callback*, *Runnable*. + * It should extend *SurfaceView* class. + * It also should implement *SurfaceHolder.Callback*, *Runnable*. #. Edit *HelloOpenCVView* class. - * Add an *import* line for *android.content.context*. + * Add an *import* line for *android.content.context*. + + * Modify autogenerated stubs: *HelloOpenCVView*, *surfaceCreated*, *surfaceDestroyed* and *surfaceChanged*. - * Modify autogenerated stubs: *HelloOpenCVView*, *surfaceCreated*, *surfaceDestroyed* and *surfaceChanged*. - .. code-block:: java + .. code-block:: java + :linenos: - package com.hello.opencv.test; + package com.hello.opencv.test; - import android.content.Context; + import android.content.Context; - public class HelloOpenCVView extends SurfaceView implements Callback, Runnable { + public class HelloOpenCVView extends SurfaceView implements Callback, Runnable { + + public HelloOpenCVView(Context context) { + super(context); + getHolder().addCallback(this); + } - public HelloOpenCVView(Context context) { - super(context); - getHolder().addCallback(this); - } - - public void surfaceCreated(SurfaceHolder holder) { - (new Thread(this)).start(); - } - - public void surfaceDestroyed(SurfaceHolder holder) { - cameraRelease(); - } - - public void surfaceChanged(SurfaceHolder holder, int format, int width, - int height) { - cameraSetup(width, height); - } + public void surfaceCreated(SurfaceHolder holder) { + (new Thread(this)).start(); + } - * Add *cameraOpen*, *cameraRelease* and *cameraSetup* voids as shown below. + public void surfaceDestroyed(SurfaceHolder holder) { + cameraRelease(); + } - * Also, don't forget to add the public void *run()* as follows: - - .. code-block:: java + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + cameraSetup(width, height); + } - public void run() { - // TODO: loop { getFrame(), processFrame(), drawFrame() } - } + //... - public boolean cameraOpen() { - return false; //TODO: open camera - } - - private void cameraRelease() { - // TODO release camera - } + * Add *cameraOpen*, *cameraRelease* and *cameraSetup* voids as shown below. - private void cameraSetup(int width, int height) { - // TODO setup camera - } - + * Also, don't forget to add the public void *run()* as follows: - .. + .. code-block:: java + :linenos: + + public void run() { + // TODO: loop { getFrame(), processFrame(), drawFrame() } + } + + public boolean cameraOpen() { + return false; //TODO: open camera + } + + private void cameraRelease() { + // TODO release camera + } + + private void cameraSetup(int width, int height) { + // TODO setup camera + } #. Create a new *Activity* (*New -> Other -> Android -> Android Activity*) and name it, for example: *HelloOpenCVActivity*. For this activity define *onCreate*, *onResume* and *onPause* voids. - .. code-block:: java - - public void onCreate (Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mView = new HelloOpenCVView(this); - setContentView (mView); - } - - protected void onPause() { - super.onPause(); - mView.cameraRelease(); - } - - protected void onResume() { - super.onResume(); - if( !mView.cameraOpen() ) { - // MessageBox and exit app - AlertDialog ad = new AlertDialog.Builder(this).create(); - ad.setCancelable(false); // This blocks the "BACK" button - ad.setMessage("Fatal error: can't open camera!"); - ad.setButton("OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - finish(); - } - }); - ad.show(); - } - - } + + .. code-block:: java + :linenos: + + public void onCreate (Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mView = new HelloOpenCVView(this); + setContentView (mView); + } + + protected void onPause() { + super.onPause(); + mView.cameraRelease(); + } + + protected void onResume() { + super.onResume(); + if( !mView.cameraOpen() ) { + // MessageBox and exit app + AlertDialog ad = new AlertDialog.Builder(this).create(); + ad.setCancelable(false); // This blocks the "BACK" button + ad.setMessage("Fatal error: can't open camera!"); + ad.setButton("OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + finish(); + } + }); + ad.show(); + } + } #. Add the following permissions to the AndroidManifest.xml file: - .. code-block:: xml - + .. code-block:: xml + :linenos: + + + + + + - - - - #. Reference OpenCV library within your project properties. - .. image:: images/dev_OCV_reference.png - :alt: Reference OpenCV library. - :align: center + + .. image:: images/dev_OCV_reference.png + :alt: Reference OpenCV library. + :align: center #. We now need some code to handle the camera. Update the *HelloOpenCVView* class as follows: - .. code-block:: java - - private VideoCapture mCamera; - - public boolean cameraOpen() { - synchronized (this) { - cameraRelease(); - mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); - if (!mCamera.isOpened()) { - mCamera.release(); - mCamera = null; - Log.e("HelloOpenCVView", "Failed to open native camera"); - return false; - } - } - return true; - } - public void cameraRelease() { - synchronized(this) { - if (mCamera != null) { - mCamera.release(); - mCamera = null; - } - } - } - private void cameraSetup(int width, int height) { - synchronized (this) { - if (mCamera != null && mCamera.isOpened()) { - List sizes = mCamera.getSupportedPreviewSizes(); - int mFrameWidth = width; - int mFrameHeight = height; - { // selecting optimal camera preview size - double minDiff = Double.MAX_VALUE; - for (Size size : sizes) { - if (Math.abs(size.height - height) < minDiff) { - mFrameWidth = (int) size.width; - mFrameHeight = (int) size.height; - minDiff = Math.abs(size.height - height); - } - } - } - mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mFrameWidth); - mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mFrameHeight); - } - } - } -#. The last step would be to update the *run()* void in *HelloOpenCVView* class as follows: - .. code-block:: java - - public void run() { - while (true) { - Bitmap bmp = null; - synchronized (this) { - if (mCamera == null) - break; - if (!mCamera.grab()) - break; - - bmp = processFrame(mCamera); - } - if (bmp != null) { - Canvas canvas = getHolder().lockCanvas(); - if (canvas != null) { - canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, - (canvas.getHeight() - bmp.getHeight()) / 2, null); - getHolder().unlockCanvasAndPost(canvas); - - } - bmp.recycle(); - } - } - } - - protected Bitmap processFrame(VideoCapture capture) { - Mat mRgba = new Mat(); - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - //process mRgba - Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); - try { - Utils.matToBitmap(mRgba, bmp); - } catch(Exception e) { - Log.e("processFrame", "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - bmp = null; - } - return bmp; - } + .. code-block:: java + :linenos: + + private VideoCapture mCamera; + + public boolean cameraOpen() { + synchronized (this) { + cameraRelease(); + mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); + if (!mCamera.isOpened()) { + mCamera.release(); + mCamera = null; + Log.e("HelloOpenCVView", "Failed to open native camera"); + return false; + } + } + return true; + } + + public void cameraRelease() { + synchronized(this) { + if (mCamera != null) { + mCamera.release(); + mCamera = null; + } + } + } + + private void cameraSetup(int width, int height) { + synchronized (this) { + if (mCamera != null && mCamera.isOpened()) { + List sizes = mCamera.getSupportedPreviewSizes(); + int mFrameWidth = width; + int mFrameHeight = height; + { // selecting optimal camera preview size + double minDiff = Double.MAX_VALUE; + for (Size size : sizes) { + if (Math.abs(size.height - height) < minDiff) { + mFrameWidth = (int) size.width; + mFrameHeight = (int) size.height; + minDiff = Math.abs(size.height - height); + } + } + } + mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mFrameWidth); + mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mFrameHeight); + } + } + } +#. The last step would be to update the *run()* void in *HelloOpenCVView* class as follows: + .. code-block:: java + :linenos: + + public void run() { + while (true) { + Bitmap bmp = null; + synchronized (this) { + if (mCamera == null) + break; + if (!mCamera.grab()) + break; + + bmp = processFrame(mCamera); + } + if (bmp != null) { + Canvas canvas = getHolder().lockCanvas(); + if (canvas != null) { + canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, + (canvas.getHeight() - bmp.getHeight()) / 2, null); + getHolder().unlockCanvasAndPost(canvas); + + } + bmp.recycle(); + } + } + } + + protected Bitmap processFrame(VideoCapture capture) { + Mat mRgba = new Mat(); + capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); + //process mRgba + Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); + try { + Utils.matToBitmap(mRgba, bmp); + } catch(Exception e) { + Log.e("processFrame", "Utils.matToBitmap() throws an exception: " + e.getMessage()); + bmp.recycle(); + bmp = null; + } + return bmp; + } From ff3aa6cbe1fcd5abd07d6d9637ece1f17cdd1d33 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 17 Aug 2012 18:28:50 +0400 Subject: [PATCH 13/58] Fix Android build after commit:41b6d25 --- modules/core/src/system.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index e1d57ef8cb..253d840248 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -939,11 +939,11 @@ struct Mutex::Impl { Impl() { InitializeCriticalSection(&cs); refcount = 1; } ~Impl() { DeleteCriticalSection(&cs); } - + void lock() { EnterCriticalSection(&cs); } bool trylock() { return TryEnterCriticalSection(&cs) != 0; } void unlock() { LeaveCriticalSection(&cs); } - + CRITICAL_SECTION cs; int refcount; }; @@ -956,26 +956,26 @@ struct Mutex::Impl { Impl() { sl = OS_SPINLOCK_INIT; refcount = 1; } ~Impl() {} - + void lock() { OSSpinLockLock(&sl); } bool trylock() { return OSSpinLockTry(&sl); } void unlock() { OSSpinLockUnlock(&sl); } - + OSSpinLock sl; int refcount; }; -#elif defined __linux__ +#elif defined __linux__ && !defined ANDROID struct Mutex::Impl { Impl() { pthread_spin_init(&sl, 0); refcount = 1; } ~Impl() { pthread_spin_destroy(&sl); } - + void lock() { pthread_spin_lock(&sl); } bool trylock() { return pthread_spin_trylock(&sl) == 0; } void unlock() { pthread_spin_unlock(&sl); } - + pthread_spinlock_t sl; int refcount; }; @@ -986,11 +986,11 @@ struct Mutex::Impl { Impl() { pthread_mutex_init(&sl, 0); refcount = 1; } ~Impl() { pthread_mutex_destroy(&sl); } - + void lock() { pthread_mutex_lock(&sl); } bool trylock() { return pthread_mutex_trylock(&sl) == 0; } void unlock() { pthread_mutex_unlock(&sl); } - + pthread_mutex_t sl; int refcount; }; @@ -1001,14 +1001,14 @@ Mutex::Mutex() { impl = new Mutex::Impl; } - + Mutex::~Mutex() { if( CV_XADD(&impl->refcount, -1) == 1 ) delete impl; impl = 0; } - + Mutex::Mutex(const Mutex& m) { impl = m.impl; @@ -1023,10 +1023,10 @@ Mutex& Mutex::operator = (const Mutex& m) impl = m.impl; return *this; } - + void Mutex::lock() { impl->lock(); } void Mutex::unlock() { impl->unlock(); } -bool Mutex::trylock() { return impl->trylock(); } +bool Mutex::trylock() { return impl->trylock(); } } From bf4c1df0d0edafab8a225590b0996a077fce1144 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Fri, 17 Aug 2012 19:23:45 +0400 Subject: [PATCH 14/58] Fix formatting in Android tutorials --- .../android_binary_package/O4A_SDK.rst | 68 ++--- .../android_dev_intro.rst | 266 ++++++++++-------- 2 files changed, 183 insertions(+), 151 deletions(-) diff --git a/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst b/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst index 56069eb307..824a9730e3 100644 --- a/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst +++ b/doc/tutorials/introduction/android_binary_package/O4A_SDK.rst @@ -36,21 +36,21 @@ The structure of package contents looks as follows: OpenCV-2.4.2-android-sdk |_ apk - | |_ OpenCV_2.4.2_binary_pack_XXX.apk - | |_ OpenCV_2.4.2_Manager.apk + | |_ OpenCV_2.4.2_binary_pack_XXX.apk + | |_ OpenCV_2.4.2_Manager.apk | |_ doc |_ samples |_ sdk - | |_ etc - | |_ java - | |_ native - | |_ 3rdparty - | |_ jni - | |_ libs - | |_ armeabi - | |_ armeabi-v7a - | |_ x86 + | |_ etc + | |_ java + | |_ native + | |_ 3rdparty + | |_ jni + | |_ libs + | |_ armeabi + | |_ armeabi-v7a + | |_ x86 | |_ license.txt |_ README.android @@ -64,11 +64,11 @@ The structure of package contents looks as follows: * :file:`sdk/etc` folder contains Haar and LBP cascades distributed with OpenCV. * :file:`apk` folder contains Android packages that should be installed on the target Android device to enable OpenCV library access via OpenCV Manager API (see details below). - + On production devices that have access to Google Play Market (and internet) these packages will be installed from Market on the first start of an application using OpenCV Manager API. But dev kits without Market or internet require this packages to be installed manually. (Install the `Manager.apk` and the corresponding `binary_pack.apk` depending on the device CPU, the Manager GUI provides this info). - + **Note**: installation from internet is the preferable way since we may publish updated versions of this packages on the Market. * :file:`samples` folder contains sample applications projects and their prebuilt packages (APK). @@ -76,7 +76,7 @@ The structure of package contents looks as follows: * :file:`doc` folder contains various OpenCV documentation in PDF format. It's also available online at http://docs.opencv.org. - + **Note**: the most recent docs (nightly build) are at http://docs.opencv.org/trunk/. Generally, it's more up-to-date, but can refer to not-yet-released functionality. @@ -94,10 +94,10 @@ Starting version 2.4.2 `OpenCV4Android SDK` uses `OpenCV Manager` API for librar For additional information on OpenCV Manager see the: - -* |OpenCV4Android_Slides|_ - -* |OpenCV4Android_Reference|_ + +* |OpenCV4Android_Slides|_ + +* |OpenCV4Android_Reference|_ .. @@ -196,15 +196,15 @@ Open OpenCV library and samples in Eclipse However, **all these errors are only false-alarms**! Just give a minute to Eclipse to complete initialization. - + In some cases these errors disappear after :menuselection:`Project --> Clean... --> Clean all --> OK` or after pressing :kbd:`F5` (for Refresh action) when selecting error-label-marked projects in :guilabel:`Package Explorer`. Sometimes more advanced manipulations are required: - * The provided projects are configured for ``API 11`` target (and ``API 9`` for the library) that can be missing platform in your Android SDK. - After right click on any project select :guilabel:`Properties` and then :guilabel:`Android` on the left pane. - Click some target with `API Level` 11 or higher: + The provided projects are configured for ``API 11`` target (and ``API 9`` for the library) that can be missing platform in your Android SDK. + After right click on any project select :guilabel:`Properties` and then :guilabel:`Android` on the left pane. + Click some target with `API Level` 11 or higher: .. image:: images/eclipse_8a_target.png :alt: Updating target @@ -239,10 +239,10 @@ Well, running samples from Eclipse is very simple: * Connect your device with :command:`adb` tool from Android SDK or create an emulator with camera support. - * See `Managing Virtual Devices - `_ document for help with Android Emulator. - * See `Using Hardware Devices - `_ for help with real devices (not emulators). + * See `Managing Virtual Devices + `_ document for help with Android Emulator. + * See `Using Hardware Devices + `_ for help with real devices (not emulators). * Select project you want to start in :guilabel:`Package Explorer` and just press :kbd:`Ctrl + F11` or select option :menuselection:`Run --> Run` from the main menu, or click :guilabel:`Run` button on the toolbar. @@ -263,33 +263,33 @@ Well, running samples from Eclipse is very simple: .. image:: images/android_emulator_opencv_manager_fail.png :alt: You will see this message if you have no OpenCV Manager installed :align: center - + To get rid of the message you will need to install `OpenCV Manager` and the appropriate `OpenCV binary pack`. Simply tap :menuselection:`Yes` if you have *Google Play Market* installed on your device/emulator. It will redirect you to the corresponding page on *Google Play Market*. - + If you have no access to the *Market*, which is often the case with emulators - you will need to install the packages from OpenCV4Android SDK folder manually. Open the console/terminal and type in the following two commands: - + .. code-block:: sh :linenos: /platform-tools/adb install /apk/OpenCV_2.4.2_Manager.apk /platform-tools/adb install /apk/OpenCV_2.4.2_binary_pack_armv7a.apk - + If you're running Windows, that will probably look like this: - + .. image:: images/install_opencv_manager_with_adb.png :alt: Run these commands in the console to install OpenCV Manager :align: center - + When done, you will be able to run OpenCV samples on your device/emulator seamlessly. - + * Here is ``Tutorial 2 - Use OpenCV Camera`` sample, running on top of stock camera-preview of the emulator. .. image:: images/emulator_canny.png :height: 600px :alt: Tutorial 1 Basic - 1. Add OpenCV - running Canny :align: center - + What's next =========== diff --git a/doc/tutorials/introduction/android_binary_package/android_dev_intro.rst b/doc/tutorials/introduction/android_binary_package/android_dev_intro.rst index dc95403ffd..db109adae1 100644 --- a/doc/tutorials/introduction/android_binary_package/android_dev_intro.rst +++ b/doc/tutorials/introduction/android_binary_package/android_dev_intro.rst @@ -75,7 +75,7 @@ You need the following software to be installed in order to develop for Android sudo update-java-alternatives --set java-6-sun - **TODO:** add a note on Sun/Oracle Java installation on Ubuntu 12. +.. **TODO:** add a note on Sun/Oracle Java installation on Ubuntu 12. #. **Android SDK** @@ -241,27 +241,29 @@ where: The script :file:`Android.mk` usually has the following structure: .. code-block:: make + :linenos: - LOCAL_PATH := $(call my-dir) + LOCAL_PATH := $(call my-dir) - include $(CLEAR_VARS) - LOCAL_MODULE := - LOCAL_SRC_FILES := - := - ... - := + include $(CLEAR_VARS) + LOCAL_MODULE := + LOCAL_SRC_FILES := + := + ... + := - include $(BUILD_SHARED_LIBRARY) + include $(BUILD_SHARED_LIBRARY) This is the minimal file :file:`Android.mk`, which builds C++ source code of an Android application. Note that the first two lines and the last line are mandatory for any :file:`Android.mk`. Usually the file :file:`Application.mk` is optional, but in case of project using OpenCV, when STL and exceptions are used in C++, it also should be created. Example of the file :file:`Application.mk`: .. code-block:: make + :linenos: - APP_STL := gnustl_static - APP_CPPFLAGS := -frtti -fexceptions - APP_ABI := armeabi-v7a + APP_STL := gnustl_static + APP_CPPFLAGS := -frtti -fexceptions + APP_ABI := armeabi-v7a .. _NDK_build_cli: @@ -332,75 +334,76 @@ We recommend the approach based on Eclipse :abbr:`CDT(C/C++ Development Tooling) #. Open Eclipse and load the Android app project to configure. #. Add C/C++ Nature to the project via Eclipse menu :guilabel:`New -> Other -> C/C++ -> Convert to a C/C++ Project`. - - .. image:: images/eclipse_cdt_cfg1.png - :alt: Configure CDT - :align: center - ` ` + .. image:: images/eclipse_cdt_cfg1.png + :alt: Configure CDT + :align: center - .. image:: images/eclipse_cdt_cfg2.png - :alt: Configure CDT - :align: center + And: + + .. image:: images/eclipse_cdt_cfg2.png + :alt: Configure CDT + :align: center #. Select the project(s) to convert. Specify "Project type" = ``Makefile project``, "Toolchains" = ``Other Toolchain``. - + .. image:: images/eclipse_cdt_cfg3.png :alt: Configure CDT :align: center -#. Open :guilabel:`Project Properties -> C/C++ Build`, unckeck ``Use default build command``, replace "Build command" text from ``"make"`` to - ``"${NDKROOT}/ndk-build.cmd"`` on Windows, +#. Open :guilabel:`Project Properties -> C/C++ Build`, unckeck ``Use default build command``, replace "Build command" text from ``"make"`` to - ``"${NDKROOT}/ndk-build"`` on Linux and MacOS. - - .. image:: images/eclipse_cdt_cfg4.png - :alt: Configure CDT - :align: center + ``"${NDKROOT}/ndk-build.cmd"`` on Windows, + + ``"${NDKROOT}/ndk-build"`` on Linux and MacOS. + + .. image:: images/eclipse_cdt_cfg4.png + :alt: Configure CDT + :align: center #. Go to :guilabel:`Behaviour` tab and change "Workbench build type" section like shown below: - - .. image:: images/eclipse_cdt_cfg5.png - :alt: Configure CDT - :align: center + + .. image:: images/eclipse_cdt_cfg5.png + :alt: Configure CDT + :align: center #. Press :guilabel:`OK` and make sure the ``ndk-build`` is successfully invoked when building the project. - - .. image:: images/eclipse_cdt_cfg6.png - :alt: Configure CDT - :align: center + + .. image:: images/eclipse_cdt_cfg6.png + :alt: Configure CDT + :align: center #. If you open your C++ source file in Eclipse editor, you'll see syntax error notifications. They are not real errors, but additional CDT configuring is required. - - .. image:: images/eclipse_cdt_cfg7.png - :alt: Configure CDT - :align: center + + .. image:: images/eclipse_cdt_cfg7.png + :alt: Configure CDT + :align: center #. Open :guilabel:`Project Properties -> C/C++ General -> Paths and Symbols` and add the following **Include** paths for **C++**: - :: + :: ${NDKROOT}/platforms/android-9/arch-arm/usr/include ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/include ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/include ${ProjDirPath}/../../sdk/native/jni/include - The last path should be changed to the correct absolute or relative path to OpenCV4Android SDK location. - - This should clear the syntax error notifications in Eclipse C++ editor. - - .. image:: images/eclipse_cdt_cfg8.png - :alt: Configure CDT - :align: center + The last path should be changed to the correct absolute or relative path to OpenCV4Android SDK location. + + This should clear the syntax error notifications in Eclipse C++ editor. - .. note:: The latest Android NDK **r8b** has a bit different STL headers path. So if you use this NDK version please use the following modified **Include** paths list: + .. image:: images/eclipse_cdt_cfg8.png + :alt: Configure CDT + :align: center + + .. note:: The latest Android NDK **r8b** uses different STL headers path. So if you use this NDK release add the following **Include** paths list instead: - :: + :: - ${NDKROOT}/platforms/android-9/arch-arm/usr/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include - ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include - ${ProjDirPath}/../../sdk/native/jni/include + ${NDKROOT}/platforms/android-9/arch-arm/usr/include + ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/include + ${NDKROOT}/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include + ${ProjDirPath}/../../sdk/native/jni/include Debugging and Testing @@ -412,12 +415,16 @@ AVD AVD (*Android Virtual Device*) is not probably the most convenient way to test an OpenCV-dependent application, but sure the most uncomplicated one to configure. #. Assuming you already have *Android SDK* and *Eclipse IDE* installed, in Eclipse go :guilabel:`Window -> AVD Manager`. - **TBD:** how to start AVD Manager without Eclipse... + + .. **TBD:** how to start AVD Manager without Eclipse... + #. Press the :guilabel:`New` button in :guilabel:`AVD Manager` window. #. :guilabel:`Create new Android Virtual Device` window will let you select some properties for your new device, like target API level, size of SD-card and other. - .. image:: images/AVD_create.png - :alt: Configure builders - :align: center + + .. image:: images/AVD_create.png + :alt: Configure builders + :align: center + #. When you click the :guilabel:`Create AVD` button, your new AVD will be availible in :guilabel:`AVD Manager`. #. Press :guilabel:`Start` to launch the device. Be aware that any AVD (a.k.a. Emulator) is usually much slower than a hardware Android device, so it may take up to several minutes to start. #. Go :guilabel:`Run -> Run/Debug` in Eclipse IDE to run your application in regular or debugging mode. :guilabel:`Device Chooser` will let you choose among the running devices or to start a new one. @@ -435,81 +442,106 @@ Windows host computer #. Attach the Android device to your PC with a USB cable. #. Go to :guilabel:`Start Menu` and **right-click** on :guilabel:`Computer`. Select :guilabel:`Manage` in the context menu. You may be asked for Administrative permissions. #. Select :guilabel:`Device Manager` in the left pane and find an unknown device in the list. You may try unplugging it and then plugging back in order to check whether it's your exact equipment appears in the list. - .. image:: images/usb_device_connect_01.png - :alt: Unknown device - :align: center + + .. image:: images/usb_device_connect_01.png + :alt: Unknown device + :align: center + #. Try your luck installing `Google USB drivers` without any modifications: **right-click** on the unknown device, select :guilabel:`Properties` menu item --> :guilabel:`Details` tab --> :guilabel:`Update Driver` button. - .. image:: images/usb_device_connect_05.png - :alt: Device properties - :align: center + + .. image:: images/usb_device_connect_05.png + :alt: Device properties + :align: center + #. Select :guilabel:`Browse computer for driver software`. - .. image:: images/usb_device_connect_06.png - :alt: Browse for driver - :align: center + + .. image:: images/usb_device_connect_06.png + :alt: Browse for driver + :align: center + #. Specify the path to :file:`/extras/google/usb_driver/` folder. - .. image:: images/usb_device_connect_07.png - :alt: Browse for driver - :align: center + + .. image:: images/usb_device_connect_07.png + :alt: Browse for driver + :align: center + #. If you get the prompt to install unverified drivers and report about success - you've finished with USB driver installation. - .. image:: images/usb_device_connect_08.png - :alt: Install prompt - :align: center - ` ` + .. image:: images/usb_device_connect_08.png + :alt: Install prompt + :align: center + + ` ` + + .. image:: images/usb_device_connect_09.png + :alt: Installed OK + :align: center - .. image:: images/usb_device_connect_09.png - :alt: Installed OK - :align: center #. Otherwise (getting the failure like shown below) follow the next steps. - .. image:: images/usb_device_connect_12.png - :alt: No driver - :align: center + + .. image:: images/usb_device_connect_12.png + :alt: No driver + :align: center + #. Again **right-click** on the unknown device, select :guilabel:`Properties --> Details --> Hardware Ids` and copy the line like ``USB\VID_XXXX&PID_XXXX&MI_XX``. - .. image:: images/usb_device_connect_02.png - :alt: Device properties details - :align: center + + .. image:: images/usb_device_connect_02.png + :alt: Device properties details + :align: center + #. Now open file :file:`/extras/google/usb_driver/android_winusb.inf`. Select either ``Google.NTx86`` or ``Google.NTamd64`` section depending on your host system architecture. - .. image:: images/usb_device_connect_03.png - :alt: "android_winusb.inf" - :align: center + + .. image:: images/usb_device_connect_03.png + :alt: "android_winusb.inf" + :align: center + #. There should be a record like existing ones for your device and you need to add one manually. - .. image:: images/usb_device_connect_04.png - :alt: "android_winusb.inf" - :align: center + + .. image:: images/usb_device_connect_04.png + :alt: "android_winusb.inf" + :align: center + #. Save the :file:`android_winusb.inf` file and try to install the USB driver again. - .. image:: images/usb_device_connect_05.png - :alt: Device properties - :align: center - ` ` + .. image:: images/usb_device_connect_05.png + :alt: Device properties + :align: center - .. image:: images/usb_device_connect_06.png - :alt: Browse for driver - :align: center + ` ` - ` ` + .. image:: images/usb_device_connect_06.png + :alt: Browse for driver + :align: center + + ` ` + + .. image:: images/usb_device_connect_07.png + :alt: Browse for driver + :align: center - .. image:: images/usb_device_connect_07.png - :alt: Browse for driver - :align: center #. This time installation should go successfully. - .. image:: images/usb_device_connect_08.png - :alt: Install prompt - :align: center - ` ` + .. image:: images/usb_device_connect_08.png + :alt: Install prompt + :align: center + + ` ` + + .. image:: images/usb_device_connect_09.png + :alt: Installed OK + :align: center - .. image:: images/usb_device_connect_09.png - :alt: Installed OK - :align: center #. And an unknown device is now recognized as an Android phone. - .. image:: images/usb_device_connect_10.png - :alt: "Known" device - :align: center + + .. image:: images/usb_device_connect_10.png + :alt: "Known" device + :align: center + #. Successful device USB connection can be verified in console via ``adb devices`` command. - .. image:: images/usb_device_connect_11.png - :alt: "adb devices" - :align: center + + .. image:: images/usb_device_connect_11.png + :alt: "adb devices" + :align: center #. Now, in Eclipse go :guilabel:`Run -> Run/Debug` to run your application in regular or debugging mode. :guilabel:`Device Chooser` will let you choose among the devices. @@ -519,13 +551,13 @@ By default Linux doesn't recognize Android devices, but it's easy to fix this is .. code-block:: guess - SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666", GROUP="plugdev" + SUBSYSTEM=="usb", ATTR{idVendor}=="1004", MODE="0666", GROUP="plugdev" Then restart your adb server (even better to restart the system), plug in your Android device and execute :command:`adb devices` command. You will see the list of attached devices: - .. image:: images/usb_device_connect_ubuntu.png - :alt: List of attached devices - :align: center +.. image:: images/usb_device_connect_ubuntu.png + :alt: List of attached devices + :align: center MacOS host computer ^^^^^^^^^^^^^^^^^^^ From a4bffd96c470be50eb21880beb4e1e895c39ae84 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintesv Date: Sat, 18 Aug 2012 19:43:32 +0300 Subject: [PATCH 15/58] added deconvolution.py sample and example images --- samples/python2/data/licenseplate_motion.jpg | Bin 0 -> 58364 bytes samples/python2/data/text_defocus.jpg | Bin 0 -> 32176 bytes samples/python2/data/text_motion.jpg | Bin 0 -> 26637 bytes samples/python2/deconvolution.py | 118 +++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 samples/python2/data/licenseplate_motion.jpg create mode 100644 samples/python2/data/text_defocus.jpg create mode 100644 samples/python2/data/text_motion.jpg create mode 100644 samples/python2/deconvolution.py diff --git a/samples/python2/data/licenseplate_motion.jpg b/samples/python2/data/licenseplate_motion.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9723ea148b0d91ee8651d91b336ec1073f5e001f GIT binary patch literal 58364 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 literal 0 HcmV?d00001 diff --git a/samples/python2/data/text_motion.jpg b/samples/python2/data/text_motion.jpg new file mode 100644 index 0000000000000000000000000000000000000000..781f095c2d4f63c2648175434bf6874c980813fc GIT binary patch literal 26637 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�k&R37a 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 literal 0 HcmV?d00001 diff --git a/samples/python2/deconvolution.py b/samples/python2/deconvolution.py new file mode 100644 index 0000000000..cbce8a2c50 --- /dev/null +++ b/samples/python2/deconvolution.py @@ -0,0 +1,118 @@ +''' +Wiener deconvolution. + +Sample shows how DFT can be used to perform Weiner deconvolution [1] +of an image with user-defined point spread function (PSF) + +Usage: + deconvolution.py [--circle] + [--angle ] + [--d ] + [--snr ] + [] + + Use sliders to adjust PSF paramitiers. + Keys: + SPACE - switch btw linear/cirular PSF + ESC - exit + +Examples: + deconvolution.py --angle 135 --d 22 data/licenseplate_motion.jpg + (image source: http://www.topazlabs.com/infocus/_images/licenseplate_compare.jpg) + + deconvolution.py --angle 86 --d 31 data/text_motion.jpg + deconvolution.py --circle --d 19 data/text_defocus.jpg + (image source: compact digital photo camera, no artificial distortion) + + +[1] http://en.wikipedia.org/wiki/Wiener_deconvolution +''' + +import numpy as np +import cv2 +from common import nothing + + +def blur_edge(img, d=31): + h, w = img.shape[:2] + img_pad = cv2.copyMakeBorder(img, d, d, d, d, cv2.BORDER_WRAP) + img_blur = cv2.GaussianBlur(img_pad, (2*d+1, 2*d+1), -1)[d:-d,d:-d] + y, x = np.indices((h, w)) + dist = np.dstack([x, w-x-1, y, h-y-1]).min(-1) + w = np.minimum(np.float32(dist)/d, 1.0) + return img*w + img_blur*(1-w) + +def motion_kernel(angle, d, sz=65): + kern = np.ones((1, d), np.float32) + c, s = np.cos(angle), np.sin(angle) + A = np.float32([[c, -s, 0], [s, c, 0]]) + sz2 = sz // 2 + A[:,2] = (sz2, sz2) - np.dot(A[:,:2], ((d-1)*0.5, 0)) + kern = cv2.warpAffine(kern, A, (sz, sz), flags=cv2.INTER_CUBIC) + return kern + +def defocus_kernel(d, sz=65): + kern = np.zeros((sz, sz), np.uint8) + cv2.circle(kern, (sz, sz), d, 255, -1, cv2.CV_AA, shift=1) + kern = np.float32(kern) / 255.0 + return kern + + +if __name__ == '__main__': + print __doc__ + import sys, getopt + opts, args = getopt.getopt(sys.argv[1:], '', ['circle', 'angle=', 'd=', 'snr=']) + opts = dict(opts) + try: fn = args[0] + except: fn = 'data/licenseplate_motion.jpg' + + win = 'deconvolution' + + img = cv2.imread(fn, 0) + img = np.float32(img)/255.0 + cv2.imshow('input', img) + + img = blur_edge(img) + IMG = cv2.dft(img, flags=cv2.DFT_COMPLEX_OUTPUT) + + defocus = '--circle' in opts + + def update(_): + ang = np.deg2rad( cv2.getTrackbarPos('angle', win) ) + d = cv2.getTrackbarPos('d', win) + noise = 10**(-0.1*cv2.getTrackbarPos('SNR (db)', win)) + + if defocus: + psf = defocus_kernel(d) + else: + psf = motion_kernel(ang, d) + cv2.imshow('psf', psf) + + psf /= psf.sum() + psf_pad = np.zeros_like(img) + kh, kw = psf.shape + psf_pad[:kh, :kw] = psf + PSF = cv2.dft(psf_pad, flags=cv2.DFT_COMPLEX_OUTPUT, nonzeroRows = kh) + PSF2 = (PSF**2).sum(-1) + iPSF = PSF / (PSF2 + noise)[...,np.newaxis] + RES = cv2.mulSpectrums(IMG, iPSF, 0) + res = cv2.idft(RES, flags=cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT ) + res = np.roll(res, -kh//2, 0) + res = np.roll(res, -kw//2, 1) + cv2.imshow(win, res) + + cv2.namedWindow(win) + cv2.namedWindow('psf', 0) + cv2.createTrackbar('angle', win, int(opts.get('--angle', 135)), 180, update) + cv2.createTrackbar('d', win, int(opts.get('--d', 22)), 50, update) + cv2.createTrackbar('SNR (db)', win, int(opts.get('--snr', 25)), 50, update) + update(None) + + while True: + ch = cv2.waitKey() + if ch == 27: + break + if ch == ord(' '): + defocus = not defocus + update(None) + From 8e2258c494db6bc701aceab443278f1f10e6d867 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Sun, 19 Aug 2012 00:20:08 +0400 Subject: [PATCH 16/58] Fix Windows build after commit:41b6d25 --- modules/core/src/parallel.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index c238f992b0..8072a11589 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -116,7 +116,22 @@ namespace cv #elif defined HAVE_CONCURRENCY - Concurrency::parallel_for(range.start, range.end, body); + class ConcurrencyProxyLoopBody + { + public: + ConcurrencyProxyLoopBody(const ParallelLoopBody& body) : _body(body) {} + + void operator ()(int i) const + { + _body(Range(i, i + 1)); + } + + private: + const ParallelLoopBody& _body; + ConcurrencyProxyLoopBody& operator=(const ConcurrencyProxyLoopBody&) {return *this;} + } proxy(body); + + Concurrency::parallel_for(range.start, range.end, proxy); #elif defined HAVE_OPENMP From 1f42de39a33a3a5e2f9f21d4ea36219b42d6a865 Mon Sep 17 00:00:00 2001 From: Victor Passichenko Date: Sun, 19 Aug 2012 13:13:58 +0400 Subject: [PATCH 17/58] Add non-local means denoising algorithm implementatation into photo module --- modules/photo/doc/denoising.rst | 91 ++++ modules/photo/doc/photo.rst | 1 + .../photo/include/opencv2/photo/denoising.hpp | 79 ++++ modules/photo/src/arrays.hpp | 161 +++++++ modules/photo/src/denoising.cpp | 220 ++++++++++ .../src/fast_nlmeans_denoising_invoker.hpp | 342 +++++++++++++++ ...fast_nlmeans_denoising_invoker_commons.hpp | 120 ++++++ .../fast_nlmeans_multi_denoising_invoker.hpp | 394 ++++++++++++++++++ modules/photo/test/test_denoising.cpp | 213 ++++++++++ 9 files changed, 1621 insertions(+) create mode 100644 modules/photo/doc/denoising.rst create mode 100644 modules/photo/include/opencv2/photo/denoising.hpp create mode 100644 modules/photo/src/arrays.hpp create mode 100644 modules/photo/src/denoising.cpp create mode 100644 modules/photo/src/fast_nlmeans_denoising_invoker.hpp create mode 100644 modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp create mode 100644 modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp create mode 100644 modules/photo/test/test_denoising.cpp diff --git a/modules/photo/doc/denoising.rst b/modules/photo/doc/denoising.rst new file mode 100644 index 0000000000..957ec88006 --- /dev/null +++ b/modules/photo/doc/denoising.rst @@ -0,0 +1,91 @@ +Denoising +========== + +.. highlight:: cpp + +fastNlMeansDenoising +----------- +Perform image denoising using Non-local Means Denoising algorithm http://www.ipol.im/pub/algo/bcm_non_local_means_denoising/ +with several computational optimizations. Noise expected to be a gaussian white noise + +.. ocv:function:: void fastNlMeansDenoising( Mat& src, Mat& dst, int templateWindowSize, int searchWindowSize, int h ) + + :param src: Input 8-bit 1-channel, 2-channel or 3-channel image. + + :param dst: Output image with the same size and type as ``src`` . + + :param templateWindowSize: Size in pixels of the template patch that is used to compute weights. Should be odd. Recommended value 7 pixels + + :param searchWindowSize: Size in pixels of the window that is used to compute weighted average for given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater denoising time. Recommended value 21 pixels + + :param h: Parameter regulating filter strength. Big h value perfectly removes noise but also removes image details, smaller h value preserves details but also preserves some noise + +This function expected to be applied to grayscale images. For colored images look at ``fastNlMeansDenoisingColored``. +Advanced usage of this functions can be manual denoising of colored image in different colorspaces. +Such approach is used in ``fastNlMeansDenoisingColored`` by converting image to CIELAB colorspace and then separately denoise L and AB components with different h parameter. + +fastNlMeansDenoisingColored +----------- +Modification of ``fastNlMeansDenoising`` function for colored images + +.. ocv:function:: void fastNlMeansDenoisingColored( Mat& src, Mat& dst, int templateWindowSize, int searchWindowSize, int h, int hForColorComponents ) + + :param src: Input 8-bit 3-channel image. + + :param dst: Output image with the same size and type as ``src`` . + + :param templateWindowSize: Size in pixels of the template patch that is used to compute weights. Should be odd. Recommended value 7 pixels + + :param searchWindowSize: Size in pixels of the window that is used to compute weighted average for given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater denoising time. Recommended value 21 pixels + + :param h: Parameter regulating filter strength for luminance component. Bigger h value perfectly removes noise but also removes image details, smaller h value preserves details but also preserves some noise + + :param hForColorComponents: The same as h but for color components. For most images value equals 10 will be enought to remove colored noise and do not distort colors + +The function converts image to CIELAB colorspace and then separately denoise L and AB components with given h parameters using ``fastNlMeansDenoising`` function. + +fastNlMeansDenoisingMulti +----------- +Modification of ``fastNlMeansDenoising`` function for images sequence where consequtive images have been captured in small period of time. For example video. This version of the function is for grayscale images or for manual manipulation with colorspaces. +For more details see http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.131.6394 + +.. ocv:function:: void fastNlMeansDenoisingMulti( const std::vector& srcImgs, int imgToDenoiseIndex, int temporalWindowSize, Mat& dst, int templateWindowSize, int searchWindowSize, int h) + + :param srcImgs: Input 8-bit 1-channel, 2-channel or 3-channel images sequence. All images should have the same type and size. + + :param imgToDenoiseIndex: Target image to denoise index in ``srcImgs`` sequence + + :param temporalWindowSize: Number of surrounding images to use for target image denoising. Should be odd. Images from ``imgToDenoiseIndex - temporalWindowSize / 2`` to ``imgToDenoiseIndex - temporalWindowSize / 2`` from ``srcImgs`` will be used to denoise ``srcImgs[imgToDenoiseIndex]`` image. + + :param dst: Output image with the same size and type as ``srcImgs`` images. + + :param templateWindowSize: Size in pixels of the template patch that is used to compute weights. Should be odd. Recommended value 7 pixels + + :param searchWindowSize: Size in pixels of the window that is used to compute weighted average for given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater denoising time. Recommended value 21 pixels + + :param h: Parameter regulating filter strength for luminance component. Bigger h value perfectly removes noise but also removes image details, smaller h value preserves details but also preserves some noise + +fastNlMeansDenoisingColoredMulti +----------- +Modification of ``fastNlMeansDenoisingMulti`` function for colored images sequences + +.. ocv:function:: void fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, int imgToDenoiseIndex, int temporalWindowSize, Mat& dst, int templateWindowSize, int searchWindowSize, int h, int hForColorComponents) + + :param srcImgs: Input 8-bit 3-channel images sequence. All images should have the same type and size. + + :param imgToDenoiseIndex: Target image to denoise index in ``srcImgs`` sequence + + :param temporalWindowSize: Number of surrounding images to use for target image denoising. Should be odd. Images from ``imgToDenoiseIndex - temporalWindowSize / 2`` to ``imgToDenoiseIndex - temporalWindowSize / 2`` from ``srcImgs`` will be used to denoise ``srcImgs[imgToDenoiseIndex]`` image. + + :param dst: Output image with the same size and type as ``srcImgs`` images. + + :param templateWindowSize: Size in pixels of the template patch that is used to compute weights. Should be odd. Recommended value 7 pixels + + :param searchWindowSize: Size in pixels of the window that is used to compute weighted average for given pixel. Should be odd. Affect performance linearly: greater searchWindowsSize - greater denoising time. Recommended value 21 pixels + + :param h: Parameter regulating filter strength for luminance component. Bigger h value perfectly removes noise but also removes image details, smaller h value preserves details but also preserves some noise. + + :param hForColorComponents: The same as h but for color components. + +The function converts images to CIELAB colorspace and then separately denoise L and AB components with given h parameters using ``fastNlMeansDenoisingMulti`` function. + diff --git a/modules/photo/doc/photo.rst b/modules/photo/doc/photo.rst index 9d8636ee73..6f05239120 100644 --- a/modules/photo/doc/photo.rst +++ b/modules/photo/doc/photo.rst @@ -8,3 +8,4 @@ photo. Computational Photography :maxdepth: 2 inpainting + denoising diff --git a/modules/photo/include/opencv2/photo/denoising.hpp b/modules/photo/include/opencv2/photo/denoising.hpp new file mode 100644 index 0000000000..b322c31755 --- /dev/null +++ b/modules/photo/include/opencv2/photo/denoising.hpp @@ -0,0 +1,79 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2008-2012, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_DENOISING_HPP__ +#define __OPENCV_DENOISING_HPP__ + +#include "opencv2/core/core.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include + +#ifdef __cplusplus + +/*! \namespace cv + Namespace where all the C++ OpenCV functionality resides + */ +namespace cv +{ + +CV_EXPORTS void fastNlMeansDenoising( const Mat& src, Mat& dst, + int templateWindowSize, int searchWindowSize, int h); + +CV_EXPORTS void fastNlMeansDenoisingColored( const Mat& src, Mat& dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); + +CV_EXPORTS void fastNlMeansDenoisingMulti( const std::vector& srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + Mat& dst, + int templateWindowSize, int searchWindowSize, int h); + +CV_EXPORTS void fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + Mat& dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); + +} +#endif + +#endif diff --git a/modules/photo/src/arrays.hpp b/modules/photo/src/arrays.hpp new file mode 100644 index 0000000000..c1c4e5f971 --- /dev/null +++ b/modules/photo/src/arrays.hpp @@ -0,0 +1,161 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective icvers. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_DENOISING_ARRAYS_HPP__ +#define __OPENCV_DENOISING_ARRAYS_HPP__ + +template struct Array2d { + T* a; + int n1,n2; + bool needToDeallocArray; + + Array2d(const Array2d& array2d): + a(array2d.a), n1(array2d.n1), n2(array2d.n2), needToDeallocArray(false) + { + if (array2d.needToDeallocArray) { + // copy constructor for self allocating arrays not supported + throw new exception(); + } + } + + Array2d(T* _a, int _n1, int _n2): + a(_a), n1(_n1), n2(_n2), needToDeallocArray(false) {} + + Array2d(int _n1, int _n2): + n1(_n1), n2(_n2), needToDeallocArray(true) + { + a = new T[n1*n2]; + } + + ~Array2d() { + if (needToDeallocArray) { + delete a; + } + } + + T* operator [] (int i) { + return a + i*n2; + } + + inline T* row_ptr(int i) { + return (*this)[i]; + } +}; + +template struct Array3d { + T* a; + int n1,n2,n3; + bool needToDeallocArray; + + Array3d(T* _a, int _n1, int _n2, int _n3): + a(_a), n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(false) {} + + Array3d(int _n1, int _n2, int _n3): + n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(true) + { + a = new T[n1*n2*n3]; + } + + ~Array3d() { + if (needToDeallocArray) { + delete a; + } + } + + Array2d operator [] (int i) { + Array2d array2d(a + i*n2*n3, n2, n3); + return array2d; + } + + inline T* row_ptr(int i1, int i2) { + return a + i1*n2*n3 + i2*n3; + } +}; + +template struct Array4d { + T* a; + int n1,n2,n3,n4; + bool needToDeallocArray; + int steps[4]; + + void init_steps() { + steps[0] = n2*n3*n4; + steps[1] = n3*n4; + steps[2] = n4; + steps[3] = 1; + } + + Array4d(T* _a, int _n1, int _n2, int _n3, int _n4): + a(_a), n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(false) + { + init_steps(); + } + + Array4d(int _n1, int _n2, int _n3, int _n4): + n1(_n1), n2(_n2), n3(_n3), n4(_n4), needToDeallocArray(true) + { + a = new T[n1*n2*n3*n4]; + init_steps(); + } + + ~Array4d() { + if (needToDeallocArray) { + delete a; + } + } + + Array3d operator [] (int i) { + Array3d array3d(a + i*n2*n3*n4, n2, n3, n4); + return array3d; + } + + inline T* row_ptr(int i1, int i2, int i3) { + return a + i1*n2*n3*n4 + i2*n3*n4 + i3*n4; + } + + inline int step_size(int dimension) { + return steps[dimension]; + } +}; + +#endif + + diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp new file mode 100644 index 0000000000..39643e33dc --- /dev/null +++ b/modules/photo/src/denoising.cpp @@ -0,0 +1,220 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective icvers. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "precomp.hpp" +#include "opencv2/photo/denoising.hpp" +#include "opencv2/imgproc/imgproc.hpp" +#include "fast_nlmeans_denoising_invoker.hpp" +#include "fast_nlmeans_multi_denoising_invoker.hpp" + +void cv::fastNlMeansDenoising( const cv::Mat& src, cv::Mat& dst, + int templateWindowSize, int searchWindowSize, int h) +{ + switch (src.type()) { + case CV_8U: + parallel_for(cv::BlockedRange(0, src.rows), + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC2: + parallel_for(cv::BlockedRange(0, src.rows), + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC3: + parallel_for(cv::BlockedRange(0, src.rows), + FastNlMeansDenoisingInvoker( + src, dst, templateWindowSize, searchWindowSize, h)); + break; + default: + CV_Error(CV_StsBadArg, + "Unsupported matrix format! Only uchar, Vec2b, Vec3b are supported"); + } +} + +void cv::fastNlMeansDenoisingColored( const cv::Mat& src, cv::Mat& dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents) +{ + if (src.type() != CV_8UC3) { + CV_Error(CV_StsBadArg, "Type of input image should be CV_8UC3!"); + return; + } + + Mat src_lab; + cvtColor(src, src_lab, CV_LBGR2Lab); + + Mat l(src.size(), CV_8U); + Mat ab(src.size(), CV_8UC2); + Mat l_ab[] = { l, ab }; + int from_to[] = { 0,0, 1,1, 2,2 }; + mixChannels(&src_lab, 1, l_ab, 2, from_to, 3); + + fastNlMeansDenoising(l, l, templateWindowSize, searchWindowSize, h); + fastNlMeansDenoising(ab, ab, templateWindowSize, searchWindowSize, hForColorComponents); + + Mat l_ab_denoised[] = { l, ab }; + Mat dst_lab(src.size(), src.type()); + mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3); + + cvtColor(dst_lab, dst, CV_Lab2LBGR); +} + +static void fastNlMeansDenoisingMultiCheckPreconditions( + const std::vector& srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + int templateWindowSize, int searchWindowSize) +{ + int src_imgs_size = srcImgs.size(); + if (src_imgs_size == 0) { + CV_Error(CV_StsBadArg, "Input images vector should not be empty!"); + } + + if (temporalWindowSize % 2 == 0 || + searchWindowSize % 2 == 0 || + templateWindowSize % 2 == 0) { + CV_Error(CV_StsBadArg, "All windows sizes should be odd!"); + } + + int temporalWindowHalfSize = temporalWindowSize / 2; + if (imgToDenoiseIndex - temporalWindowHalfSize < 0 || + imgToDenoiseIndex + temporalWindowHalfSize >= src_imgs_size) + { + CV_Error(CV_StsBadArg, + "imgToDenoiseIndex and temporalWindowSize " + "should be choosen corresponding srcImgs size!"); + } + + for (int i = 1; i < src_imgs_size; i++) { + if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type()) { + CV_Error(CV_StsBadArg, "Input images should have the same size and type!"); + } + } +} + +void cv::fastNlMeansDenoisingMulti( const std::vector& srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + cv::Mat& dst, + int templateWindowSize, int searchWindowSize, int h) +{ + fastNlMeansDenoisingMultiCheckPreconditions( + srcImgs, imgToDenoiseIndex, + temporalWindowSize, templateWindowSize, searchWindowSize + ); + + switch (srcImgs[0].type()) { + case CV_8U: + parallel_for(cv::BlockedRange(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC2: + parallel_for(cv::BlockedRange(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + case CV_8UC3: + parallel_for(cv::BlockedRange(0, srcImgs[0].rows), + FastNlMeansMultiDenoisingInvoker( + srcImgs, imgToDenoiseIndex, temporalWindowSize, + dst, templateWindowSize, searchWindowSize, h)); + break; + default: + CV_Error(CV_StsBadArg, + "Unsupported matrix format! Only uchar, Vec2b, Vec3b are supported"); + } +} + +void cv::fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + cv::Mat& dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents) +{ + fastNlMeansDenoisingMultiCheckPreconditions( + srcImgs, imgToDenoiseIndex, + temporalWindowSize, templateWindowSize, searchWindowSize + ); + + int src_imgs_size = srcImgs.size(); + + if (srcImgs[0].type() != CV_8UC3) { + CV_Error(CV_StsBadArg, "Type of input images should be CV_8UC3!"); + return; + } + + int from_to[] = { 0,0, 1,1, 2,2 }; + + // TODO convert only required images + vector src_lab(src_imgs_size); + vector l(src_imgs_size); + vector ab(src_imgs_size); + for (int i = 0; i < src_imgs_size; i++) { + src_lab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC3); + l[i] = Mat::zeros(srcImgs[0].size(), CV_8UC1); + ab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC2); + cvtColor(srcImgs[i], src_lab[i], CV_LBGR2Lab); + + Mat l_ab[] = { l[i], ab[i] }; + mixChannels(&src_lab[i], 1, l_ab, 2, from_to, 3); + } + + Mat dst_l; + Mat dst_ab; + + fastNlMeansDenoisingMulti( + l, imgToDenoiseIndex, temporalWindowSize, + dst_l, templateWindowSize, searchWindowSize, h); + + fastNlMeansDenoisingMulti( + ab, imgToDenoiseIndex, temporalWindowSize, + dst_ab, templateWindowSize, searchWindowSize, hForColorComponents); + + Mat l_ab_denoised[] = { dst_l, dst_ab }; + Mat dst_lab(srcImgs[0].size(), srcImgs[0].type()); + mixChannels(l_ab_denoised, 2, &dst_lab, 1, from_to, 3); + + cvtColor(dst_lab, dst, CV_Lab2LBGR); +} + + diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp new file mode 100644 index 0000000000..58e4a45e17 --- /dev/null +++ b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp @@ -0,0 +1,342 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective icvers. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_HPP__ +#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_HPP__ + +#include "precomp.hpp" +#include +#include +#include +#include + +#include "fast_nlmeans_denoising_invoker_commons.hpp" +#include "arrays.hpp" + +using namespace std; +using namespace cv; + +template +struct FastNlMeansDenoisingInvoker { + public: + FastNlMeansDenoisingInvoker(const Mat& src, Mat& dst, + int template_window_size, int search_window_size, const double h); + + void operator() (const BlockedRange& range) const; + + private: + const Mat& src_; + Mat& dst_; + + Mat extended_src_; + int border_size_; + + int template_window_size_; + int search_window_size_; + + int template_window_half_size_; + int search_window_half_size_; + + int fixed_point_mult_; + int almost_template_window_size_sq_bin_shift; + vector almost_dist2weight; + + void calcDistSumsForFirstElementInRow( + int i, + Array2d& dist_sums, + Array3d& col_dist_sums, + Array3d& up_col_dist_sums) const; + + void calcDistSumsForElementInFirstRow( + int i, + int j, + int first_col_num, + Array2d& dist_sums, + Array3d& col_dist_sums, + Array3d& up_col_dist_sums) const; +}; + +template +FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( + const cv::Mat& src, + cv::Mat& dst, + int template_window_size, + int search_window_size, + const double h) : src_(src), dst_(dst) +{ + template_window_half_size_ = template_window_size / 2; + search_window_half_size_ = search_window_size / 2; + template_window_size_ = template_window_half_size_ * 2 + 1; + search_window_size_ = search_window_half_size_ * 2 + 1; + + border_size_ = search_window_half_size_ + template_window_half_size_; + copyMakeBorder(src_, extended_src_, + border_size_, border_size_, border_size_, border_size_, cv::BORDER_DEFAULT); + + const int max_estimate_sum_value = search_window_size_ * search_window_size_ * 255; + fixed_point_mult_ = numeric_limits::max() / max_estimate_sum_value; + + // precalc weight for every possible l2 dist between blocks + // additional optimization of precalced weights to replace division(averaging) by binary shift + int template_window_size_sq = template_window_size_ * template_window_size_; + almost_template_window_size_sq_bin_shift = 0; + while (1 << almost_template_window_size_sq_bin_shift < template_window_size_sq) { + almost_template_window_size_sq_bin_shift++; + } + + int almost_template_window_size_sq = 1 << almost_template_window_size_sq_bin_shift; + double almost_dist2actual_dist_multiplier = + ((double) almost_template_window_size_sq) / template_window_size_sq; + + int max_dist = 256 * 256 * src_.channels(); + int almost_max_dist = (int) (max_dist / almost_dist2actual_dist_multiplier + 1); + almost_dist2weight.resize(almost_max_dist); + + const double WEIGHT_THRESHOLD = 0.001; + for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++) { + double dist = almost_dist * almost_dist2actual_dist_multiplier; + int weight = cvRound(fixed_point_mult_ * std::exp(- dist / (h * h * src_.channels()))); + + if (weight < WEIGHT_THRESHOLD * fixed_point_mult_) { + weight = 0; + } + + almost_dist2weight[almost_dist] = weight; + } + // additional optimization init end + + if (dst_.empty()) { + dst_ = Mat::zeros(src_.size(), src_.type()); + } +} + +template +void FastNlMeansDenoisingInvoker::operator() (const BlockedRange& range) const { + int row_from = range.begin(); + int row_to = range.end() - 1; + + int dist_sums_array[search_window_size_ * search_window_size_]; + Array2d dist_sums(dist_sums_array, search_window_size_, search_window_size_); + + // for lazy calc optimization + int col_dist_sums_array[template_window_size_ * search_window_size_ * search_window_size_]; + Array3d col_dist_sums(&col_dist_sums_array[0], + template_window_size_, search_window_size_, search_window_size_); + + int first_col_num = -1; + + Array3d up_col_dist_sums(src_.cols, search_window_size_, search_window_size_); + + for (int i = row_from; i <= row_to; i++) { + for (int j = 0; j < src_.cols; j++) { + int search_window_y = i - search_window_half_size_; + int search_window_x = j - search_window_half_size_; + + // calc dist_sums + if (j == 0) { + calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums); + first_col_num = 0; + + } else { // calc cur dist_sums using previous dist_sums + if (i == row_from) { + calcDistSumsForElementInFirstRow(i, j, first_col_num, + dist_sums, col_dist_sums, up_col_dist_sums); + + } else { + int ay = border_size_ + i; + int ax = border_size_ + j + template_window_half_size_; + + int start_by = + border_size_ + i - search_window_half_size_; + + int start_bx = + border_size_ + j - search_window_half_size_ + template_window_half_size_; + + T a_up = extended_src_.at(ay - template_window_half_size_ - 1, ax); + T a_down = extended_src_.at(ay + template_window_half_size_, ax); + + // copy class member to local variable for optimization + int search_window_size = search_window_size_; + + for (int y = 0; y < search_window_size; y++) { + int* dist_sums_row = dist_sums.row_ptr(y); + + int* col_dist_sums_row = col_dist_sums.row_ptr(first_col_num,y); + + int* up_col_dist_sums_row = up_col_dist_sums.row_ptr(j, y); + + const T* b_up_ptr = + extended_src_.ptr(start_by - template_window_half_size_ - 1 + y); + + const T* b_down_ptr = + extended_src_.ptr(start_by + template_window_half_size_ + y); + + for (int x = 0; x < search_window_size; x++) { + dist_sums_row[x] -= col_dist_sums_row[x]; + + col_dist_sums_row[x] = + up_col_dist_sums_row[x] + + calcUpDownDist( + a_up, a_down, + b_up_ptr[start_bx + x], b_down_ptr[start_bx + x] + ); + + dist_sums_row[x] += col_dist_sums_row[x]; + + up_col_dist_sums_row[x] = col_dist_sums_row[x]; + + } + } + } + + first_col_num = (first_col_num + 1) % template_window_size_; + } + + // calc weights + int weights_sum = 0; + + int estimation[src_.channels()]; + for (int channel_num = 0; channel_num < src_.channels(); channel_num++) { + estimation[channel_num] = 0; + } + + for (int y = 0; y < search_window_size_; y++) { + const T* cur_row_ptr = extended_src_.ptr(border_size_ + search_window_y + y); + int* dist_sums_row = dist_sums.row_ptr(y); + for (int x = 0; x < search_window_size_; x++) { + int almostAvgDist = + dist_sums_row[x] >> almost_template_window_size_sq_bin_shift; + + int weight = almost_dist2weight[almostAvgDist]; + weights_sum += weight; + + T p = cur_row_ptr[border_size_ + search_window_x + x]; + incWithWeight(estimation, weight, p); + } + } + + if (weights_sum > 0) { + for (int channel_num = 0; channel_num < src_.channels(); channel_num++) { + estimation[channel_num] = + cvRound(((double)estimation[channel_num]) / weights_sum); + } + + dst_.at(i,j) = saturateCastFromArray(estimation); + + } else { // weights_sum == 0 + dst_.at(i,j) = src_.at(i,j); + } + } + } +} + +template +inline void FastNlMeansDenoisingInvoker::calcDistSumsForFirstElementInRow( + int i, + Array2d& dist_sums, + Array3d& col_dist_sums, + Array3d& up_col_dist_sums) const +{ + int j = 0; + + for (int y = 0; y < search_window_size_; y++) { + for (int x = 0; x < search_window_size_; x++) { + dist_sums[y][x] = 0; + for (int tx = 0; tx < template_window_size_; tx++) { + col_dist_sums[tx][y][x] = 0; + } + + int start_y = i + y - search_window_half_size_; + int start_x = j + x - search_window_half_size_; + + for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { + for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++) { + int dist = calcDist(extended_src_, + border_size_ + i + ty, border_size_ + j + tx, + border_size_ + start_y + ty, border_size_ + start_x + tx); + + dist_sums[y][x] += dist; + col_dist_sums[tx + template_window_half_size_][y][x] += dist; + } + } + + up_col_dist_sums[j][y][x] = col_dist_sums[template_window_size_ - 1][y][x]; + } + } +} + +template +inline void FastNlMeansDenoisingInvoker::calcDistSumsForElementInFirstRow( + int i, + int j, + int first_col_num, + Array2d& dist_sums, + Array3d& col_dist_sums, + Array3d& up_col_dist_sums) const +{ + int ay = border_size_ + i; + int ax = border_size_ + j + template_window_half_size_; + + int start_by = border_size_ + i - search_window_half_size_; + int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_; + + int new_last_col_num = first_col_num; + + for (int y = 0; y < search_window_size_; y++) { + for (int x = 0; x < search_window_size_; x++) { + dist_sums[y][x] -= col_dist_sums[first_col_num][y][x]; + + col_dist_sums[new_last_col_num][y][x] = 0; + int by = start_by + y; + int bx = start_bx + x; + for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { + col_dist_sums[new_last_col_num][y][x] += + calcDist(extended_src_, ay + ty, ax, by + ty, bx); + } + + dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x]; + + up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x]; + } + } +} + +#endif diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp new file mode 100644 index 0000000000..c1084f15e9 --- /dev/null +++ b/modules/photo/src/fast_nlmeans_denoising_invoker_commons.hpp @@ -0,0 +1,120 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective icvers. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__ +#define __OPENCV_FAST_NLMEANS_DENOISING_INVOKER_COMMONS_HPP__ + +#include +#include +#include + +using namespace std; +using namespace cv; + +template static inline int calcDist(const T a, const T b); + +template <> inline int calcDist(const uchar a, const uchar b) { + return (a-b) * (a-b); +} + +template <> inline int calcDist(const Vec2b a, const Vec2b b) { + return (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]); +} + +template <> inline int calcDist(const Vec3b a, const Vec3b b) { + return (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]); +} + +template static inline int calcDist(const Mat& m, int i1, int j1, int i2, int j2) { + const T a = m.at(i1, j1); + const T b = m.at(i2, j2); + return calcDist(a,b); +} + +template static inline int calcUpDownDist(T a_up, T a_down, T b_up, T b_down) { + return calcDist(a_down,b_down) - calcDist(a_up, b_up); +} + +template <> inline int calcUpDownDist(uchar a_up, uchar a_down, uchar b_up, uchar b_down) { + int A = a_down - b_down; + int B = a_up - b_up; + return (A-B)*(A+B); +} + +template static inline void incWithWeight(int* estimation, int weight, T p); + +template <> inline void incWithWeight(int* estimation, int weight, uchar p) { + estimation[0] += weight * p; +} + +template <> inline void incWithWeight(int* estimation, int weight, Vec2b p) { + estimation[0] += weight * p[0]; + estimation[1] += weight * p[1]; +} + +template <> inline void incWithWeight(int* estimation, int weight, Vec3b p) { + estimation[0] += weight * p[0]; + estimation[1] += weight * p[1]; + estimation[2] += weight * p[2]; +} + +template static inline T saturateCastFromArray(int* estimation); + +template <> inline uchar saturateCastFromArray(int* estimation) { + return saturate_cast(estimation[0]); +} + +template <> inline Vec2b saturateCastFromArray(int* estimation) { + Vec2b res; + res[0] = saturate_cast(estimation[0]); + res[1] = saturate_cast(estimation[1]); + return res; +} + +template <> inline Vec3b saturateCastFromArray(int* estimation) { + Vec3b res; + res[0] = saturate_cast(estimation[0]); + res[1] = saturate_cast(estimation[1]); + res[2] = saturate_cast(estimation[2]); + return res; +} + +#endif diff --git a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp new file mode 100644 index 0000000000..cb08c7e434 --- /dev/null +++ b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp @@ -0,0 +1,394 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective icvers. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef __OPENCV_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__ +#define __OPENCV_FAST_NLMEANS_MULTI_DENOISING_INVOKER_HPP__ + +#include "precomp.hpp" +#include +#include +#include +#include + +#include "fast_nlmeans_denoising_invoker_commons.hpp" +#include "arrays.hpp" + +using namespace std; +using namespace cv; + +template +struct FastNlMeansMultiDenoisingInvoker { + public: + FastNlMeansMultiDenoisingInvoker( + const std::vector& srcImgs, int imgToDenoiseIndex, int temporalWindowSize, + Mat& dst, int template_window_size, int search_window_size, const double h); + + void operator() (const BlockedRange& range) const; + + private: + int rows_; + int cols_; + int channels_count_; + + Mat& dst_; + + vector extended_srcs_; + Mat main_extended_src_; + int border_size_; + + int template_window_size_; + int search_window_size_; + int temporal_window_size_; + + int template_window_half_size_; + int search_window_half_size_; + int temporal_window_half_size_; + + int fixed_point_mult_; + int almost_template_window_size_sq_bin_shift; + vector almost_dist2weight; + + void calcDistSumsForFirstElementInRow( + int i, + Array3d& dist_sums, + Array4d& col_dist_sums, + Array4d& up_col_dist_sums) const; + + void calcDistSumsForElementInFirstRow( + int i, + int j, + int first_col_num, + Array3d& dist_sums, + Array4d& col_dist_sums, + Array4d& up_col_dist_sums) const; +}; + +template +FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( + const vector& srcImgs, + int imgToDenoiseIndex, + int temporalWindowSize, + cv::Mat& dst, + int template_window_size, + int search_window_size, + const double h) : dst_(dst), extended_srcs_(srcImgs.size()) +{ + rows_ = srcImgs[0].rows; + cols_ = srcImgs[0].cols; + channels_count_ = srcImgs[0].channels(); + + template_window_half_size_ = template_window_size / 2; + search_window_half_size_ = search_window_size / 2; + temporal_window_half_size_ = temporalWindowSize / 2; + + template_window_size_ = template_window_half_size_ * 2 + 1; + search_window_size_ = search_window_half_size_ * 2 + 1; + temporal_window_size_ = temporal_window_half_size_ * 2 + 1; + + border_size_ = search_window_half_size_ + template_window_half_size_; + for (int i = 0; i < temporal_window_size_; i++) { + copyMakeBorder( + srcImgs[imgToDenoiseIndex - temporal_window_half_size_ + i], extended_srcs_[i], + border_size_, border_size_, border_size_, border_size_, cv::BORDER_DEFAULT); + } + main_extended_src_ = extended_srcs_[temporal_window_half_size_]; + + const int max_estimate_sum_value = + temporal_window_size_ * search_window_size_ * search_window_size_ * 255; + + fixed_point_mult_ = numeric_limits::max() / max_estimate_sum_value; + + // precalc weight for every possible l2 dist between blocks + // additional optimization of precalced weights to replace division(averaging) by binary shift + int template_window_size_sq = template_window_size_ * template_window_size_; + almost_template_window_size_sq_bin_shift = 0; + while (1 << almost_template_window_size_sq_bin_shift < template_window_size_sq) { + almost_template_window_size_sq_bin_shift++; + } + + int almost_template_window_size_sq = 1 << almost_template_window_size_sq_bin_shift; + double almost_dist2actual_dist_multiplier = + ((double) almost_template_window_size_sq) / template_window_size_sq; + + int max_dist = 256 * 256 * channels_count_; + int almost_max_dist = (int) (max_dist / almost_dist2actual_dist_multiplier + 1); + almost_dist2weight.resize(almost_max_dist); + + const double WEIGHT_THRESHOLD = 0.001; + for (int almost_dist = 0; almost_dist < almost_max_dist; almost_dist++) { + double dist = almost_dist * almost_dist2actual_dist_multiplier; + int weight = cvRound(fixed_point_mult_ * std::exp(- dist / (h * h * channels_count_))); + + if (weight < WEIGHT_THRESHOLD * fixed_point_mult_) { + weight = 0; + } + + almost_dist2weight[almost_dist] = weight; + } + // additional optimization init end + + if (dst_.empty()) { + dst_ = Mat::zeros(srcImgs[0].size(), srcImgs[0].type()); + } +} + +template +void FastNlMeansMultiDenoisingInvoker::operator() (const BlockedRange& range) const { + int row_from = range.begin(); + int row_to = range.end() - 1; + + int dist_sums_array[temporal_window_size_ * search_window_size_ * search_window_size_]; + Array3d dist_sums(dist_sums_array, + temporal_window_size_, search_window_size_, search_window_size_); + + // for lazy calc optimization + int col_dist_sums_array[ + template_window_size_ * temporal_window_size_ * search_window_size_ * search_window_size_]; + + Array4d col_dist_sums(col_dist_sums_array, + template_window_size_, temporal_window_size_, search_window_size_, search_window_size_); + + int first_col_num = -1; + + Array4d up_col_dist_sums( + cols_, temporal_window_size_, search_window_size_, search_window_size_); + + for (int i = row_from; i <= row_to; i++) { + for (int j = 0; j < cols_; j++) { + int search_window_y = i - search_window_half_size_; + int search_window_x = j - search_window_half_size_; + + // calc dist_sums + if (j == 0) { + calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums); + first_col_num = 0; + + } else { // calc cur dist_sums using previous dist_sums + if (i == row_from) { + calcDistSumsForElementInFirstRow(i, j, first_col_num, + dist_sums, col_dist_sums, up_col_dist_sums); + + } else { + int ay = border_size_ + i; + int ax = border_size_ + j + template_window_half_size_; + + int start_by = + border_size_ + i - search_window_half_size_; + + int start_bx = + border_size_ + j - search_window_half_size_ + template_window_half_size_; + + T a_up = main_extended_src_.at(ay - template_window_half_size_ - 1, ax); + T a_down = main_extended_src_.at(ay + template_window_half_size_, ax); + + // copy class member to local variable for optimization + int search_window_size = search_window_size_; + + for (int d = 0; d < temporal_window_size_; d++) { + Mat cur_extended_src = extended_srcs_[d]; + Array2d cur_dist_sums = dist_sums[d]; + Array2d cur_col_dist_sums = col_dist_sums[first_col_num][d]; + Array2d cur_up_col_dist_sums = up_col_dist_sums[j][d]; + for (int y = 0; y < search_window_size; y++) { + int* dist_sums_row = cur_dist_sums.row_ptr(y); + + int* col_dist_sums_row = cur_col_dist_sums.row_ptr(y); + + int* up_col_dist_sums_row = cur_up_col_dist_sums.row_ptr(y); + + const T* b_up_ptr = + cur_extended_src.ptr(start_by - template_window_half_size_ - 1 + y); + const T* b_down_ptr = + cur_extended_src.ptr(start_by + template_window_half_size_ + y); + + for (int x = 0; x < search_window_size; x++) { + dist_sums_row[x] -= col_dist_sums_row[x]; + + col_dist_sums_row[x] = up_col_dist_sums_row[x] + + calcUpDownDist( + a_up, a_down, + b_up_ptr[start_bx + x], b_down_ptr[start_bx + x] + ); + + dist_sums_row[x] += col_dist_sums_row[x]; + + up_col_dist_sums_row[x] = col_dist_sums_row[x]; + + } + } + } + } + + first_col_num = (first_col_num + 1) % template_window_size_; + } + + // calc weights + int weights_sum = 0; + + int estimation[channels_count_]; + for (int channel_num = 0; channel_num < channels_count_; channel_num++) { + estimation[channel_num] = 0; + } + for (int d = 0; d < temporal_window_size_; d++) { + for (int y = 0; y < search_window_size_; y++) { + const T* cur_row_ptr = + extended_srcs_[d].ptr(border_size_ + search_window_y + y); + + int* dist_sums_row = dist_sums.row_ptr(d, y); + + for (int x = 0; x < search_window_size_; x++) { + int almostAvgDist = + dist_sums_row[x] >> almost_template_window_size_sq_bin_shift; + + int weight = almost_dist2weight[almostAvgDist]; + weights_sum += weight; + + T p = cur_row_ptr[border_size_ + search_window_x + x]; + incWithWeight(estimation, weight, p); + } + } + } + + if (weights_sum > 0) { + for (int channel_num = 0; channel_num < channels_count_; channel_num++) { + estimation[channel_num] = + cvRound(((double)estimation[channel_num]) / weights_sum); + } + + dst_.at(i,j) = saturateCastFromArray(estimation); + + } else { // weights_sum == 0 + dst_.at(i,j) = extended_srcs_[temporal_window_half_size_].at(i,j); + } + } + } +} + +template +inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForFirstElementInRow( + int i, + Array3d& dist_sums, + Array4d& col_dist_sums, + Array4d& up_col_dist_sums) const +{ + int j = 0; + + for (int d = 0; d < temporal_window_size_; d++) { + Mat cur_extended_src = extended_srcs_[d]; + for (int y = 0; y < search_window_size_; y++) { + for (int x = 0; x < search_window_size_; x++) { + dist_sums[d][y][x] = 0; + for (int tx = 0; tx < template_window_size_; tx++) { + col_dist_sums[tx][d][y][x] = 0; + } + + int start_y = i + y - search_window_half_size_; + int start_x = j + x - search_window_half_size_; + + int* dist_sums_ptr = &dist_sums[d][y][x]; + int* col_dist_sums_ptr = &col_dist_sums[0][d][y][x]; + int col_dist_sums_step = col_dist_sums.step_size(0); + for (int tx = -template_window_half_size_; tx <= template_window_half_size_; tx++) { + for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { + int dist = calcDist( + main_extended_src_.at( + border_size_ + i + ty, border_size_ + j + tx), + cur_extended_src.at( + border_size_ + start_y + ty, border_size_ + start_x + tx) + ); + + *dist_sums_ptr += dist; + *col_dist_sums_ptr += dist; + } + col_dist_sums_ptr += col_dist_sums_step; + } + + up_col_dist_sums[j][d][y][x] = col_dist_sums[template_window_size_ - 1][d][y][x]; + } + } + } +} + +template +inline void FastNlMeansMultiDenoisingInvoker::calcDistSumsForElementInFirstRow( + int i, + int j, + int first_col_num, + Array3d& dist_sums, + Array4d& col_dist_sums, + Array4d& up_col_dist_sums) const +{ + int ay = border_size_ + i; + int ax = border_size_ + j + template_window_half_size_; + + int start_by = border_size_ + i - search_window_half_size_; + int start_bx = border_size_ + j - search_window_half_size_ + template_window_half_size_; + + int new_last_col_num = first_col_num; + + for (int d = 0; d < temporal_window_size_; d++) { + Mat cur_extended_src = extended_srcs_[d]; + for (int y = 0; y < search_window_size_; y++) { + for (int x = 0; x < search_window_size_; x++) { + dist_sums[d][y][x] -= col_dist_sums[first_col_num][d][y][x]; + + col_dist_sums[new_last_col_num][d][y][x] = 0; + int by = start_by + y; + int bx = start_bx + x; + + int* col_dist_sums_ptr = &col_dist_sums[new_last_col_num][d][y][x]; + for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++) { + *col_dist_sums_ptr += + calcDist( + main_extended_src_.at(ay + ty, ax), + cur_extended_src.at(by + ty, bx) + ); + } + + dist_sums[d][y][x] += col_dist_sums[new_last_col_num][d][y][x]; + + up_col_dist_sums[j][d][y][x] = col_dist_sums[new_last_col_num][d][y][x]; + } + } + } +} + +#endif diff --git a/modules/photo/test/test_denoising.cpp b/modules/photo/test/test_denoising.cpp new file mode 100644 index 0000000000..39aa699811 --- /dev/null +++ b/modules/photo/test/test_denoising.cpp @@ -0,0 +1,213 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#include "opencv2/photo/denoising.hpp" +#include + +using namespace cv; +using namespace std; + +class CV_DenoisingGrayscaleTest : public cvtest::BaseTest +{ +public: + CV_DenoisingGrayscaleTest(); + ~CV_DenoisingGrayscaleTest(); +protected: + void run(int); +}; + +CV_DenoisingGrayscaleTest::CV_DenoisingGrayscaleTest() {} +CV_DenoisingGrayscaleTest::~CV_DenoisingGrayscaleTest() {} + +void CV_DenoisingGrayscaleTest::run( int ) +{ + string folder = string(ts->get_data_path()) + "denoising/"; + Mat orig = imread(folder + "lena_noised_gaussian_sigma=10.png", 0); + Mat exp = imread(folder + "lena_noised_denoised_grayscale_tw=7_sw=21_h=10.png", 0); + + if (orig.empty() || exp.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat res; + fastNlMeansDenoising(orig, res, 7, 21, 10); + + if (norm(res - exp) > 0) { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + } else { + ts->set_failed_test_info(cvtest::TS::OK); + } +} + +class CV_DenoisingColoredTest : public cvtest::BaseTest +{ +public: + CV_DenoisingColoredTest(); + ~CV_DenoisingColoredTest(); +protected: + void run(int); +}; + +CV_DenoisingColoredTest::CV_DenoisingColoredTest() {} +CV_DenoisingColoredTest::~CV_DenoisingColoredTest() {} + +void CV_DenoisingColoredTest::run( int ) +{ + string folder = string(ts->get_data_path()) + "denoising/"; + Mat orig = imread(folder + "lena_noised_gaussian_sigma=10.png", 1); + Mat exp = imread(folder + "lena_noised_denoised_lab12_tw=7_sw=21_h=10_h2=10.png", 1); + + if (orig.empty() || exp.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat res; + fastNlMeansDenoisingColored(orig, res, 7, 21, 10, 10); + + if (norm(res - exp) > 0) { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + } else { + ts->set_failed_test_info(cvtest::TS::OK); + } +} + +class CV_DenoisingGrayscaleMultiTest : public cvtest::BaseTest +{ +public: + CV_DenoisingGrayscaleMultiTest(); + ~CV_DenoisingGrayscaleMultiTest(); +protected: + void run(int); +}; + +CV_DenoisingGrayscaleMultiTest::CV_DenoisingGrayscaleMultiTest() {} +CV_DenoisingGrayscaleMultiTest::~CV_DenoisingGrayscaleMultiTest() {} + +void CV_DenoisingGrayscaleMultiTest::run( int ) +{ + string folder = string(ts->get_data_path()) + "denoising/"; + + const int imgs_count = 3; + vector src_imgs(imgs_count); + src_imgs[0] = imread(folder + "lena_noised_gaussian_sigma=20_multi_0.png", 0); + src_imgs[1] = imread(folder + "lena_noised_gaussian_sigma=20_multi_1.png", 0); + src_imgs[2] = imread(folder + "lena_noised_gaussian_sigma=20_multi_2.png", 0); + + Mat exp = imread(folder + "lena_noised_denoised_multi_tw=7_sw=21_h=15.png", 0); + + bool have_empty_src = false; + for (int i = 0; i < imgs_count; i++) { + have_empty_src |= src_imgs[i].empty(); + } + + if (have_empty_src || exp.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat res; + fastNlMeansDenoisingMulti(src_imgs, imgs_count / 2, imgs_count, res, 7, 21, 15); + + if (norm(res - exp) > 0) { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + } else { + ts->set_failed_test_info(cvtest::TS::OK); + } +} + +class CV_DenoisingColoredMultiTest : public cvtest::BaseTest +{ +public: + CV_DenoisingColoredMultiTest(); + ~CV_DenoisingColoredMultiTest(); +protected: + void run(int); +}; + +CV_DenoisingColoredMultiTest::CV_DenoisingColoredMultiTest() {} +CV_DenoisingColoredMultiTest::~CV_DenoisingColoredMultiTest() {} + +void CV_DenoisingColoredMultiTest::run( int ) +{ + string folder = string(ts->get_data_path()) + "denoising/"; + + const int imgs_count = 3; + vector src_imgs(imgs_count); + src_imgs[0] = imread(folder + "lena_noised_gaussian_sigma=20_multi_0.png", 1); + src_imgs[1] = imread(folder + "lena_noised_gaussian_sigma=20_multi_1.png", 1); + src_imgs[2] = imread(folder + "lena_noised_gaussian_sigma=20_multi_2.png", 1); + + Mat exp = imread(folder + "lena_noised_denoised_multi_lab12_tw=7_sw=21_h=10_h2=15.png", 1); + + bool have_empty_src = false; + for (int i = 0; i < imgs_count; i++) { + have_empty_src |= src_imgs[i].empty(); + } + + if (have_empty_src || exp.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat res; + fastNlMeansDenoisingColoredMulti(src_imgs, imgs_count / 2, imgs_count, res, 7, 21, 10, 15); + + if (norm(res - exp) > 0) { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + } else { + ts->set_failed_test_info(cvtest::TS::OK); + } +} + + +TEST(Imgproc_DenoisingGrayscale, regression) { CV_DenoisingGrayscaleTest test; test.safe_run(); } +TEST(Imgproc_DenoisingColored, regression) { CV_DenoisingColoredTest test; test.safe_run(); } +TEST(Imgproc_DenoisingGrayscaleMulti, regression) { CV_DenoisingGrayscaleMultiTest test; test.safe_run(); } +TEST(Imgproc_DenoisingColoredMulti, regression) { CV_DenoisingColoredMultiTest test; test.safe_run(); } + From 4993748c91ef222cd79b05e2c4d8437581b0caef Mon Sep 17 00:00:00 2001 From: Sergiu Dotenco Date: Sun, 19 Aug 2012 15:25:07 +0200 Subject: [PATCH 18/58] added missing includes --- modules/calib3d/test/test_affine3d_estimator.cpp | 1 + modules/calib3d/test/test_chesscorners.cpp | 1 + modules/gpu/test/precomp.hpp | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/calib3d/test/test_affine3d_estimator.cpp b/modules/calib3d/test/test_affine3d_estimator.cpp index 95742a919a..9eed8e97d7 100644 --- a/modules/calib3d/test/test_affine3d_estimator.cpp +++ b/modules/calib3d/test/test_affine3d_estimator.cpp @@ -48,6 +48,7 @@ using namespace std; #include #include #include +#include #include #include #include diff --git a/modules/calib3d/test/test_chesscorners.cpp b/modules/calib3d/test/test_chesscorners.cpp index f6d513d299..d69212121d 100644 --- a/modules/calib3d/test/test_chesscorners.cpp +++ b/modules/calib3d/test/test_chesscorners.cpp @@ -42,6 +42,7 @@ #include "test_precomp.hpp" #include "test_chessboardgenerator.hpp" +#include #include #include diff --git a/modules/gpu/test/precomp.hpp b/modules/gpu/test/precomp.hpp index afc3be8559..5c1c4c1f49 100644 --- a/modules/gpu/test/precomp.hpp +++ b/modules/gpu/test/precomp.hpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include From ddc7bd76591d42f264b21f1773a786c241eed576 Mon Sep 17 00:00:00 2001 From: Sergiu Dotenco Date: Sun, 19 Aug 2012 15:30:41 +0200 Subject: [PATCH 19/58] fixed compilation errors caused by std::tuple --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cce10bacd..80cf798ee6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -336,6 +336,7 @@ include(cmake/OpenCVCompilerOptions.cmake REQUIRED) # ---------------------------------------------------------------------------- if(MSVC) include(cmake/OpenCVCRTLinkage.cmake REQUIRED) + add_definitions(-D_VARIADIC_MAX=10) endif(MSVC) From 1a621c2916e0793a3380d5121be9470e2dac882b Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Sun, 19 Aug 2012 19:36:43 +0400 Subject: [PATCH 20/58] Fix build warnings --- modules/calib3d/src/triangulate.cpp | 4 +-- modules/gpu/src/graphcuts.cpp | 2 +- modules/gpu/test/test_imgproc.cpp | 2 +- modules/imgproc/perf/perf_warp.cpp | 16 +++++----- modules/ml/src/em.cpp | 2 +- .../test_rotation_and_scale_invariance.cpp | 30 +++++++++---------- modules/objdetect/src/hog.cpp | 2 +- .../opencv2/stitching/detail/seam_finders.hpp | 2 +- modules/stitching/src/seam_finders.cpp | 8 ++--- modules/video/src/bgfg_gmg.cpp | 2 +- 10 files changed, 35 insertions(+), 35 deletions(-) diff --git a/modules/calib3d/src/triangulate.cpp b/modules/calib3d/src/triangulate.cpp index c86a7834c1..5d6037a05f 100644 --- a/modules/calib3d/src/triangulate.cpp +++ b/modules/calib3d/src/triangulate.cpp @@ -416,10 +416,10 @@ void cv::triangulatePoints( InputArray _projMatr1, InputArray _projMatr2, Mat points1 = _projPoints1.getMat(), points2 = _projPoints2.getMat(); if((points1.rows == 1 || points1.cols == 1) && points1.channels() == 2) - points1 = points1.reshape(1, points1.total()).t(); + points1 = points1.reshape(1, static_cast(points1.total())).t(); if((points2.rows == 1 || points2.cols == 1) && points2.channels() == 2) - points2 = points2.reshape(1, points2.total()).t(); + points2 = points2.reshape(1, static_cast(points2.total())).t(); CvMat cvMatr1 = matr1, cvMatr2 = matr2; CvMat cvPoints1 = points1, cvPoints2 = points2; diff --git a/modules/gpu/src/graphcuts.cpp b/modules/gpu/src/graphcuts.cpp index 58fcde8f09..fc3ac1edf0 100644 --- a/modules/gpu/src/graphcuts.cpp +++ b/modules/gpu/src/graphcuts.cpp @@ -48,7 +48,7 @@ void cv::gpu::graphcut(GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, Gpu void cv::gpu::graphcut(GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, GpuMat&, Stream&) { throw_nogpu(); } void cv::gpu::connectivityMask(const GpuMat&, GpuMat&, const cv::Scalar&, const cv::Scalar&, Stream&) { throw_nogpu(); } -void cv::gpu::labelComponents(const GpuMat& mask, GpuMat& components, int, Stream& stream) { throw_nogpu(); } +void cv::gpu::labelComponents(const GpuMat&, GpuMat&, int, Stream&) { throw_nogpu(); } #else /* !defined (HAVE_CUDA) */ diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index 4d67de59d3..b3c9dfdfa1 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -1154,7 +1154,7 @@ TEST_P(HoughLines, Accuracy) const std::string fileName = GET_PARAM(1); const float rho = 1.0f; - const float theta = CV_PI / 180.0f; + const float theta = static_cast(CV_PI / 180); const int threshold = 50; cv::Mat img = readImage(fileName, cv::IMREAD_GRAYSCALE); diff --git a/modules/imgproc/perf/perf_warp.cpp b/modules/imgproc/perf/perf_warp.cpp index 823ff53f18..8f309a21d5 100644 --- a/modules/imgproc/perf/perf_warp.cpp +++ b/modules/imgproc/perf/perf_warp.cpp @@ -125,8 +125,8 @@ void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode ) case HALF_SIZE: if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 ) { - map_x.at(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ; - map_y.at(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ; + map_x.at(j,i) = 2*( i - src.cols*0.25f ) + 0.5f ; + map_y.at(j,i) = 2*( j - src.rows*0.25f ) + 0.5f ; } else { @@ -135,16 +135,16 @@ void update_map(const Mat& src, Mat& map_x, Mat& map_y, const int remapMode ) } break; case UPSIDE_DOWN: - map_x.at(j,i) = i ; - map_y.at(j,i) = src.rows - j ; + map_x.at(j,i) = static_cast(i) ; + map_y.at(j,i) = static_cast(src.rows - j) ; break; case REFLECTION_X: - map_x.at(j,i) = src.cols - i ; - map_y.at(j,i) = j ; + map_x.at(j,i) = static_cast(src.cols - i) ; + map_y.at(j,i) = static_cast(j) ; break; case REFLECTION_BOTH: - map_x.at(j,i) = src.cols - i ; - map_y.at(j,i) = src.rows - j ; + map_x.at(j,i) = static_cast(src.cols - i) ; + map_y.at(j,i) = static_cast(src.rows - j) ; break; } // end of switch } diff --git a/modules/ml/src/em.cpp b/modules/ml/src/em.cpp index 39b58e0d49..20f365e1de 100644 --- a/modules/ml/src/em.cpp +++ b/modules/ml/src/em.cpp @@ -386,7 +386,7 @@ void EM::computeLogWeightDivDet() for(int clusterIndex = 0; clusterIndex < nclusters; clusterIndex++) { double logDetCov = 0.; - const int evalCount = covsEigenValues[clusterIndex].total(); + const int evalCount = static_cast(covsEigenValues[clusterIndex].total()); for(int di = 0; di < evalCount; di++) logDetCov += std::log(covsEigenValues[clusterIndex].at(covMatType != EM::COV_MAT_SPHERICAL ? di : 0)); diff --git a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp index 9c099f8927..b5cb62bbdc 100644 --- a/modules/nonfree/test/test_rotation_and_scale_invariance.cpp +++ b/modules/nonfree/test/test_rotation_and_scale_invariance.cpp @@ -54,7 +54,7 @@ static Mat generateHomography(float angle) { // angle - rotation around Oz in degrees - float angleRadian = angle * CV_PI / 180.; + float angleRadian = static_cast(angle * CV_PI / 180); Mat H = Mat::eye(3, 3, CV_32FC1); H.at(0,0) = H.at(1,1) = std::cos(angleRadian); H.at(0,1) = -std::sin(angleRadian); @@ -69,8 +69,8 @@ Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) // angle - rotation around Oz in degrees float diag = std::sqrt(static_cast(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows)); Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up - LUShift.at(0,2) = -srcImage.cols/2; - LUShift.at(1,2) = -srcImage.rows/2; + LUShift.at(0,2) = static_cast(-srcImage.cols/2); + LUShift.at(1,2) = static_cast(-srcImage.rows/2); Mat RDShift = Mat::eye(3, 3, CV_32FC1); // right down RDShift.at(0,2) = diag/2; RDShift.at(1,2) = diag/2; @@ -114,7 +114,7 @@ void scaleKeyPoints(const vector& src, vector& dst, float sc static float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1) { - float c = norm(p0 - p1), sqr_c = c * c; + float c = static_cast(norm(p0 - p1)), sqr_c = c * c; float sqr_r0 = r0 * r0; float sqr_r1 = r1 * r1; @@ -125,7 +125,7 @@ float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, f float minR = std::min(r0, r1); float maxR = std::max(r0, r1); if(c + minR <= maxR) - return CV_PI * minR * minR; + return static_cast(CV_PI * minR * minR); float cos_halfA0 = (sqr_r0 + sqr_c - sqr_r1) / (2 * r0 * c); float cos_halfA1 = (sqr_r1 + sqr_c - sqr_r0) / (2 * r1 * c); @@ -133,15 +133,15 @@ float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, f float A0 = 2 * acos(cos_halfA0); float A1 = 2 * acos(cos_halfA1); - return 0.5 * sqr_r0 * (A0 - sin(A0)) + - 0.5 * sqr_r1 * (A1 - sin(A1)); + return 0.5f * sqr_r0 * (A0 - sin(A0)) + + 0.5f * sqr_r1 * (A1 - sin(A1)); } static float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r1) { float intersectArea = calcCirclesIntersectArea(p0, r0, p1, r1); - float unionArea = CV_PI * (r0 * r0 + r1 * r1) - intersectArea; + float unionArea = static_cast(CV_PI) * (r0 * r0 + r1 * r1) - intersectArea; return intersectArea / unionArea; } @@ -160,7 +160,7 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, matches.clear(); vector usedMask(keypoints1.size(), 0); - for(size_t i0 = 0; i0 < keypoints0.size(); i0++) + for(int i0 = 0; i0 < static_cast(keypoints0.size()); i0++) { int nearestPointIndex = -1; float maxIntersectRatio = 0.f; @@ -176,7 +176,7 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, if(intersectRatio > maxIntersectRatio) { maxIntersectRatio = intersectRatio; - nearestPointIndex = i1; + nearestPointIndex = static_cast(i1); } } @@ -222,7 +222,7 @@ protected: const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) { - Mat H = rotateImage(image0, angle, image1, mask1); + Mat H = rotateImage(image0, static_cast(angle), image1, mask1); vector keypoints1; featureDetector->detect(image1, keypoints1, mask1); @@ -339,10 +339,10 @@ protected: const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) { - Mat H = rotateImage(image0, angle, image1, mask1); + Mat H = rotateImage(image0, static_cast(angle), image1, mask1); vector keypoints1; - rotateKeyPoints(keypoints0, H, angle, keypoints1); + rotateKeyPoints(keypoints0, H, static_cast(angle), keypoints1); Mat descriptors1; descriptorExtractor->compute(image1, keypoints1, descriptors1); @@ -457,7 +457,7 @@ protected: keyPointMatchesCount++; // Check does this inlier have consistent sizes - const float maxSizeDiff = 0.8;//0.9f; // grad + const float maxSizeDiff = 0.8f;//0.9f; // grad float size0 = keypoints0[matches[m].trainIdx].size; float size1 = osiKeypoints1[matches[m].queryIdx].size; CV_Assert(size0 > 0 && size1 > 0); @@ -545,7 +545,7 @@ protected: resize(image0, image1, Size(), 1./scale, 1./scale); vector keypoints1; - scaleKeyPoints(keypoints0, keypoints1, 1./scale); + scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale); Mat descriptors1; descriptorExtractor->compute(image1, keypoints1, descriptors1); diff --git a/modules/objdetect/src/hog.cpp b/modules/objdetect/src/hog.cpp index 485334cc36..19decfb536 100644 --- a/modules/objdetect/src/hog.cpp +++ b/modules/objdetect/src/hog.cpp @@ -2563,7 +2563,7 @@ void HOGDescriptor::readALTModel(std::string modelfile) throw Exception(); } int kernel_type; - int nread; + size_t nread; nread=fread(&(kernel_type),sizeof(int),1,modelfl); {// ignore these diff --git a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp index c700a169c0..fc1fcc7b75 100644 --- a/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp +++ b/modules/stitching/include/opencv2/stitching/detail/seam_finders.hpp @@ -120,7 +120,7 @@ private: ImagePairLess(const std::vector &images, const std::vector &corners) : src_(&images[0]), corners_(&corners[0]) {} - bool operator() (const std::pair &l, const std::pair &r) const + bool operator() (const std::pair &l, const std::pair &r) const { Point c1 = corners_[l.first] + Point(src_[l.first].cols / 2, src_[l.first].rows / 2); Point c2 = corners_[l.second] + Point(src_[l.second].cols / 2, src_[l.second].rows / 2); diff --git a/modules/stitching/src/seam_finders.cpp b/modules/stitching/src/seam_finders.cpp index 723a087034..3e10398ee7 100644 --- a/modules/stitching/src/seam_finders.cpp +++ b/modules/stitching/src/seam_finders.cpp @@ -166,7 +166,7 @@ void DpSeamFinder::find(const vector &src, const vector &corners, ve if (src.size() == 0) return; - vector > pairs; + vector > pairs; for (size_t i = 0; i+1 < src.size(); ++i) for (size_t j = i+1; j < src.size(); ++j) @@ -177,7 +177,7 @@ void DpSeamFinder::find(const vector &src, const vector &corners, ve for (size_t i = 0; i < pairs.size(); ++i) { - int i0 = pairs[i].first, i1 = pairs[i].second; + size_t i0 = pairs[i].first, i1 = pairs[i].second; process(src[i0], src[i1], corners[i0], corners[i1], masks[i0], masks[i1]); } @@ -393,7 +393,7 @@ void DpSeamFinder::resolveConflicts( bool hasConflict = true; while (hasConflict) { - int c1, c2; + int c1 = 0, c2 = 0; hasConflict = false; for (set >::iterator itr = edges_.begin(); itr != edges_.end(); ++itr) @@ -635,7 +635,7 @@ bool DpSeamFinder::getSeamTips(int comp1, int comp2, Point &p1, Point &p2) double cx = cvRound(sum[idx[i]].x / size); double cy = cvRound(sum[idx[i]].y / size); - int closest = -1; + size_t closest = points[idx[i]].size(); double minDist = numeric_limits::max(); for (size_t j = 0; j < points[idx[i]].size(); ++j) diff --git a/modules/video/src/bgfg_gmg.cpp b/modules/video/src/bgfg_gmg.cpp index f2e52b5930..056da3826e 100644 --- a/modules/video/src/bgfg_gmg.cpp +++ b/modules/video/src/bgfg_gmg.cpp @@ -290,7 +290,7 @@ namespace normalizeHistogram(weights, nfeatures); } - fgmask_row[x] = (uchar)(-isForeground); + fgmask_row[x] = (uchar)(-(schar)isForeground); } } } From c9f2ff44876349a3164025b0919d51378e3002ff Mon Sep 17 00:00:00 2001 From: Alexander Mordvintesv Date: Sun, 19 Aug 2012 20:36:50 +0300 Subject: [PATCH 21/58] added texture_flow.py sample --- samples/python2/data/starry_night.jpg | Bin 0 -> 302901 bytes samples/python2/texture_flow.py | 36 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 samples/python2/data/starry_night.jpg create mode 100644 samples/python2/texture_flow.py diff --git a/samples/python2/data/starry_night.jpg b/samples/python2/data/starry_night.jpg new file mode 100644 index 0000000000000000000000000000000000000000..824e0e5a27f100926314a9efaa11a518e2877fb1 GIT binary patch literal 302901 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%0DgdZs%fm#?^cFZhrlu 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 LKJovo{ag7j`lc5- literal 0 HcmV?d00001 diff --git a/samples/python2/texture_flow.py b/samples/python2/texture_flow.py new file mode 100644 index 0000000000..7de5dff6e2 --- /dev/null +++ b/samples/python2/texture_flow.py @@ -0,0 +1,36 @@ +''' +Texture flow direction estimation. + +Sample shows how cv2.cornerEigenValsAndVecs function can be used +to estimate image texture flow direction. + +Usage: + texture_flow.py [] +''' + +import numpy as np +import cv2 + +if __name__ == '__main__': + import sys + try: fn = sys.argv[1] + except: fn = 'data/starry_night.jpg' + + img = cv2.imread(fn) + gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + h, w = img.shape[:2] + + eigen = cv2.cornerEigenValsAndVecs(gray, 15, 3) + eigen = eigen.reshape(h, w, 3, 2) # [[e1, e2], v1, v2] + flow = eigen[:,:,2] + + vis = img.copy() + vis[:] = (192 + np.uint32(vis)) / 2 + d = 12 + points = np.dstack( np.mgrid[d/2:w:d, d/2:h:d] ).reshape(-1, 2) + for x, y in points: + vx, vy = np.int32(flow[y, x]*d) + cv2.line(vis, (x-vx, y-vy), (x+vx, y+vy), (0, 0, 0), 1, cv2.CV_AA) + cv2.imshow('input', img) + cv2.imshow('flow', vis) + cv2.waitKey() From 66eb96d719309ed8f3252ef0ea27632005d65ec6 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Sun, 19 Aug 2012 22:43:27 +0400 Subject: [PATCH 22/58] minor warning fix --- modules/core/src/cuda/matrix_operations.cu | 13 +++++++++++-- modules/core/src/gpumat.cpp | 4 ---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/core/src/cuda/matrix_operations.cu b/modules/core/src/cuda/matrix_operations.cu index 6bf1549dd5..671fa5fc31 100644 --- a/modules/core/src/cuda/matrix_operations.cu +++ b/modules/core/src/cuda/matrix_operations.cu @@ -44,7 +44,7 @@ #include "opencv2/gpu/device/transform.hpp" #include "opencv2/gpu/device/functional.hpp" -namespace cv { namespace gpu { namespace device +namespace cv { namespace gpu { namespace device { template struct shift_and_sizeof; template <> struct shift_and_sizeof { enum { shift = 0 }; }; @@ -272,7 +272,7 @@ namespace cv { namespace gpu { namespace device template struct TransformFunctorTraits< Convertor > : detail::ConvertTraits< Convertor > { }; - + template void cvt_(DevMem2Db src, DevMem2Db dst, double alpha, double beta, cudaStream_t stream) { @@ -282,6 +282,11 @@ namespace cv { namespace gpu { namespace device cv::gpu::device::transform((DevMem2D_)src, (DevMem2D_)dst, op, WithOutMask(), stream); } +#if defined __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-declarations" +#endif + void convert_gpu(DevMem2Db src, int sdepth, DevMem2Db dst, int ddepth, double alpha, double beta, cudaStream_t stream) { typedef void (*caller_t)(DevMem2Db src, DevMem2Db dst, double alpha, double beta, cudaStream_t stream); @@ -318,4 +323,8 @@ namespace cv { namespace gpu { namespace device func(src, dst, alpha, beta, stream); } + +#if defined __clang__ +# pragma clang diagnostic pop +#endif }}} // namespace cv { namespace gpu { namespace device diff --git a/modules/core/src/gpumat.cpp b/modules/core/src/gpumat.cpp index c901bf4925..105a5ff15f 100644 --- a/modules/core/src/gpumat.cpp +++ b/modules/core/src/gpumat.cpp @@ -1199,10 +1199,6 @@ namespace void setTo(GpuMat& m, Scalar s, const GpuMat& mask) const { - NppiSize sz; - sz.width = m.cols; - sz.height = m.rows; - if (mask.empty()) { if (s[0] == 0.0 && s[1] == 0.0 && s[2] == 0.0 && s[3] == 0.0) From 9c13b84e35b8108dd958416451e404eeccb9a37b Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 00:06:12 +0400 Subject: [PATCH 23/58] fixed unused warnings --- modules/gpu/perf/perf_core.cpp | 3 ++ modules/gpu/perf_cpu/perf_core.cpp | 3 ++ modules/gpu/src/calib3d.cpp | 21 ++++----- modules/gpu/src/cascadeclassifier.cpp | 8 ++-- modules/gpu/src/match_template.cpp | 65 ++++++++++++++------------- modules/gpu/src/split_merge.cpp | 1 - modules/gpu/test/test_objdetect.cpp | 21 ++++----- 7 files changed, 65 insertions(+), 57 deletions(-) diff --git a/modules/gpu/perf/perf_core.cpp b/modules/gpu/perf/perf_core.cpp index f413432041..588bae59d8 100644 --- a/modules/gpu/perf/perf_core.cpp +++ b/modules/gpu/perf/perf_core.cpp @@ -1607,6 +1607,7 @@ GPU_PERF_TEST(Norm, cv::gpu::DeviceInfo, cv::Size, MatDepth, NormType) { dst = cv::gpu::norm(src, normType, buf); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, Norm, testing::Combine( @@ -1642,6 +1643,7 @@ GPU_PERF_TEST(NormDiff, cv::gpu::DeviceInfo, cv::Size, NormType) { dst = cv::gpu::norm(src1, src2, normType); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, NormDiff, testing::Combine( @@ -1829,6 +1831,7 @@ GPU_PERF_TEST(CountNonZero, cv::gpu::DeviceInfo, cv::Size, MatDepth) { dst = cv::gpu::countNonZero(src, buf); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, CountNonZero, testing::Combine( diff --git a/modules/gpu/perf_cpu/perf_core.cpp b/modules/gpu/perf_cpu/perf_core.cpp index fb87009584..24e19a8abe 100644 --- a/modules/gpu/perf_cpu/perf_core.cpp +++ b/modules/gpu/perf_cpu/perf_core.cpp @@ -1229,6 +1229,7 @@ GPU_PERF_TEST(Norm, cv::gpu::DeviceInfo, cv::Size, MatDepth, NormType) { dst = cv::norm(src, normType); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, Norm, testing::Combine( @@ -1259,6 +1260,7 @@ GPU_PERF_TEST(NormDiff, cv::gpu::DeviceInfo, cv::Size, NormType) { dst = cv::norm(src1, src2, normType); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, NormDiff, testing::Combine( @@ -1338,6 +1340,7 @@ GPU_PERF_TEST(CountNonZero, cv::gpu::DeviceInfo, cv::Size, MatDepth) { dst = cv::countNonZero(src); } + (void)dst; } INSTANTIATE_TEST_CASE_P(Core, CountNonZero, testing::Combine( diff --git a/modules/gpu/src/calib3d.cpp b/modules/gpu/src/calib3d.cpp index bc522f372c..8897996269 100644 --- a/modules/gpu/src/calib3d.cpp +++ b/modules/gpu/src/calib3d.cpp @@ -56,14 +56,14 @@ void cv::gpu::solvePnPRansac(const Mat&, const Mat&, const Mat&, const Mat&, Mat #else -namespace cv { namespace gpu { namespace device +namespace cv { namespace gpu { namespace device { - namespace transform_points + namespace transform_points { void call(const DevMem2D_ src, const float* rot, const float* transl, DevMem2D_ dst, cudaStream_t stream); } - namespace project_points + namespace project_points { void call(const DevMem2D_ src, const float* rot, const float* transl, const float* proj, DevMem2D_ dst, cudaStream_t stream); } @@ -154,11 +154,11 @@ namespace class TransformHypothesesGenerator { public: - TransformHypothesesGenerator(const Mat& object_, const Mat& image_, const Mat& dist_coef_, - const Mat& camera_mat_, int num_points_, int subset_size_, + TransformHypothesesGenerator(const Mat& object_, const Mat& image_, const Mat& dist_coef_, + const Mat& camera_mat_, int num_points_, int subset_size_, Mat rot_matrices_, Mat transl_vectors_) - : object(&object_), image(&image_), dist_coef(&dist_coef_), camera_mat(&camera_mat_), - num_points(num_points_), subset_size(subset_size_), rot_matrices(rot_matrices_), + : object(&object_), image(&image_), dist_coef(&dist_coef_), camera_mat(&camera_mat_), + num_points(num_points_), subset_size(subset_size_), rot_matrices(rot_matrices_), transl_vectors(transl_vectors_) {} void operator()(const BlockedRange& range) const @@ -211,9 +211,10 @@ namespace void cv::gpu::solvePnPRansac(const Mat& object, const Mat& image, const Mat& camera_mat, const Mat& dist_coef, Mat& rvec, Mat& tvec, bool use_extrinsic_guess, - int num_iters, float max_dist, int min_inlier_count, + int num_iters, float max_dist, int min_inlier_count, vector* inliers) { + (void)min_inlier_count; CV_Assert(object.rows == 1 && object.cols > 0 && object.type() == CV_32FC3); CV_Assert(image.rows == 1 && image.cols > 0 && image.type() == CV_32FC2); CV_Assert(object.cols == image.cols); @@ -236,7 +237,7 @@ void cv::gpu::solvePnPRansac(const Mat& object, const Mat& image, const Mat& cam Mat transl_vectors(1, num_iters * 3, CV_32F); // Generate set of hypotheses using small subsets of the input data - TransformHypothesesGenerator body(object, image_normalized, empty_dist_coef, eye_camera_mat, + TransformHypothesesGenerator body(object, image_normalized, empty_dist_coef, eye_camera_mat, num_points, subset_size, rot_matrices, transl_vectors); parallel_for(BlockedRange(0, num_iters), body); @@ -246,7 +247,7 @@ void cv::gpu::solvePnPRansac(const Mat& object, const Mat& image, const Mat& cam GpuMat d_hypothesis_scores(1, num_iters, CV_32S); solve_pnp_ransac::computeHypothesisScores( num_iters, num_points, rot_matrices.ptr(), transl_vectors.ptr(), - d_object.ptr(), d_image_normalized.ptr(), max_dist * max_dist, + d_object.ptr(), d_image_normalized.ptr(), max_dist * max_dist, d_hypothesis_scores.ptr()); // Find the best hypothesis index diff --git a/modules/gpu/src/cascadeclassifier.cpp b/modules/gpu/src/cascadeclassifier.cpp index 1f277f0cf7..570cb7a806 100644 --- a/modules/gpu/src/cascadeclassifier.cpp +++ b/modules/gpu/src/cascadeclassifier.cpp @@ -143,7 +143,7 @@ public: } unsigned int process(const GpuMat& image, GpuMat& objectsBuf, float scaleFactor, int minNeighbors, - bool findLargestObject, bool visualizeInPlace, cv::Size minSize, cv::Size maxObjectSize) + bool findLargestObject, bool visualizeInPlace, cv::Size minSize, cv::Size /*maxObjectSize*/) { CV_Assert( scaleFactor > 1 && image.depth() == CV_8U); @@ -380,12 +380,12 @@ public: LbpCascade(){} virtual ~LbpCascade(){} - virtual unsigned int process(const GpuMat& image, GpuMat& objects, float scaleFactor, int groupThreshold, bool findLargestObject, - bool visualizeInPlace, cv::Size minObjectSize, cv::Size maxObjectSize) + virtual unsigned int process(const GpuMat& image, GpuMat& objects, float scaleFactor, int groupThreshold, bool /*findLargestObject*/, + bool /*visualizeInPlace*/, cv::Size minObjectSize, cv::Size maxObjectSize) { CV_Assert(scaleFactor > 1 && image.depth() == CV_8U); - const int defaultObjSearchNum = 100; + // const int defaultObjSearchNum = 100; const float grouping_eps = 0.2f; if( !objects.empty() && objects.depth() == CV_32S) diff --git a/modules/gpu/src/match_template.cpp b/modules/gpu/src/match_template.cpp index c4a8180682..c988000a57 100644 --- a/modules/gpu/src/match_template.cpp +++ b/modules/gpu/src/match_template.cpp @@ -52,9 +52,9 @@ void cv::gpu::matchTemplate(const GpuMat&, const GpuMat&, GpuMat&, int, Stream&) #else -namespace cv { namespace gpu { namespace device +namespace cv { namespace gpu { namespace device { - namespace match_template + namespace match_template { void matchTemplateNaive_CCORR_8U(const DevMem2Db image, const DevMem2Db templ, DevMem2Df result, int cn, cudaStream_t stream); void matchTemplateNaive_CCORR_32F(const DevMem2Db image, const DevMem2Db templ, DevMem2Df result, int cn, cudaStream_t stream); @@ -71,47 +71,47 @@ namespace cv { namespace gpu { namespace device void matchTemplatePrepared_CCOFF_8U(int w, int h, const DevMem2D_ image_sum, unsigned int templ_sum, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_8UC2( int w, int h, - const DevMem2D_ image_sum_r, - const DevMem2D_ image_sum_g, + const DevMem2D_ image_sum_r, + const DevMem2D_ image_sum_g, unsigned int templ_sum_r, - unsigned int templ_sum_g, + unsigned int templ_sum_g, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_8UC3( - int w, int h, - const DevMem2D_ image_sum_r, + int w, int h, + const DevMem2D_ image_sum_r, const DevMem2D_ image_sum_g, const DevMem2D_ image_sum_b, - unsigned int templ_sum_r, - unsigned int templ_sum_g, - unsigned int templ_sum_b, + unsigned int templ_sum_r, + unsigned int templ_sum_g, + unsigned int templ_sum_b, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_8UC4( - int w, int h, - const DevMem2D_ image_sum_r, + int w, int h, + const DevMem2D_ image_sum_r, const DevMem2D_ image_sum_g, const DevMem2D_ image_sum_b, const DevMem2D_ image_sum_a, - unsigned int templ_sum_r, - unsigned int templ_sum_g, - unsigned int templ_sum_b, - unsigned int templ_sum_a, + unsigned int templ_sum_r, + unsigned int templ_sum_g, + unsigned int templ_sum_b, + unsigned int templ_sum_a, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_NORMED_8U( - int w, int h, const DevMem2D_ image_sum, + int w, int h, const DevMem2D_ image_sum, const DevMem2D_ image_sqsum, unsigned int templ_sum, unsigned long long templ_sqsum, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_NORMED_8UC2( - int w, int h, + int w, int h, const DevMem2D_ image_sum_r, const DevMem2D_ image_sqsum_r, const DevMem2D_ image_sum_g, const DevMem2D_ image_sqsum_g, unsigned int templ_sum_r, unsigned long long templ_sqsum_r, unsigned int templ_sum_g, unsigned long long templ_sqsum_g, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_NORMED_8UC3( - int w, int h, + int w, int h, const DevMem2D_ image_sum_r, const DevMem2D_ image_sqsum_r, const DevMem2D_ image_sum_g, const DevMem2D_ image_sqsum_g, const DevMem2D_ image_sum_b, const DevMem2D_ image_sqsum_b, @@ -120,7 +120,7 @@ namespace cv { namespace gpu { namespace device unsigned int templ_sum_b, unsigned long long templ_sqsum_b, DevMem2Df result, cudaStream_t stream); void matchTemplatePrepared_CCOFF_NORMED_8UC4( - int w, int h, + int w, int h, const DevMem2D_ image_sum_r, const DevMem2D_ image_sqsum_r, const DevMem2D_ image_sum_g, const DevMem2D_ image_sqsum_g, const DevMem2D_ image_sum_b, const DevMem2D_ image_sqsum_b, @@ -131,7 +131,7 @@ namespace cv { namespace gpu { namespace device unsigned int templ_sum_a, unsigned long long templ_sqsum_a, DevMem2Df result, cudaStream_t stream); - void normalize_8U(int w, int h, const DevMem2D_ image_sqsum, + void normalize_8U(int w, int h, const DevMem2D_ image_sqsum, unsigned long long templ_sqsum, DevMem2Df result, int cn, cudaStream_t stream); void extractFirstChannel_32F(const DevMem2Db image, DevMem2Df result, int cn, cudaStream_t stream); @@ -140,17 +140,17 @@ namespace cv { namespace gpu { namespace device using namespace ::cv::gpu::device::match_template; -namespace +namespace { - // Evaluates optimal template's area threshold. If - // template's area is less than the threshold, we use naive match + // Evaluates optimal template's area threshold. If + // template's area is less than the threshold, we use naive match // template version, otherwise FFT-based (if available) int getTemplateThreshold(int method, int depth) { switch (method) { - case CV_TM_CCORR: + case CV_TM_CCORR: if (depth == CV_32F) return 250; if (depth == CV_8U) return 300; break; @@ -162,10 +162,10 @@ namespace return 0; } - + void matchTemplate_CCORR_32F( const GpuMat& image, const GpuMat& templ, GpuMat& result, MatchTemplateBuf &buf, Stream& stream) - { + { result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32F); if (templ.size().area() < getTemplateThreshold(CV_TM_CCORR, CV_32F)) { @@ -223,10 +223,11 @@ namespace normalize_8U(templ.cols, templ.rows, buf.image_sqsums[0], templ_sqsum, result, image.channels(), StreamAccessor::getStream(stream)); } - + void matchTemplate_SQDIFF_32F( const GpuMat& image, const GpuMat& templ, GpuMat& result, MatchTemplateBuf &buf, Stream& stream) { + (void)buf; result.create(image.rows - templ.rows + 1, image.cols - templ.cols + 1, CV_32F); matchTemplateNaive_SQDIFF_32F(image, templ, result, image.channels(), StreamAccessor::getStream(stream)); } @@ -362,7 +363,7 @@ namespace { case 2: matchTemplatePrepared_CCOFF_NORMED_8UC2( - templ.cols, templ.rows, + templ.cols, templ.rows, buf.image_sums[0], buf.image_sqsums[0], buf.image_sums[1], buf.image_sqsums[1], (unsigned int)templ_sum[0], (unsigned long long)templ_sqsum[0], @@ -371,7 +372,7 @@ namespace break; case 3: matchTemplatePrepared_CCOFF_NORMED_8UC3( - templ.cols, templ.rows, + templ.cols, templ.rows, buf.image_sums[0], buf.image_sqsums[0], buf.image_sums[1], buf.image_sqsums[1], buf.image_sums[2], buf.image_sqsums[2], @@ -382,7 +383,7 @@ namespace break; case 4: matchTemplatePrepared_CCOFF_NORMED_8UC4( - templ.cols, templ.rows, + templ.cols, templ.rows, buf.image_sums[0], buf.image_sqsums[0], buf.image_sums[1], buf.image_sqsums[1], buf.image_sums[2], buf.image_sqsums[2], @@ -391,7 +392,7 @@ namespace (unsigned int)templ_sum[1], (unsigned long long)templ_sqsum[1], (unsigned int)templ_sum[2], (unsigned long long)templ_sqsum[2], (unsigned int)templ_sum[3], (unsigned long long)templ_sqsum[3], - result, StreamAccessor::getStream(stream)); + result, StreamAccessor::getStream(stream)); break; default: CV_Error(CV_StsBadArg, "matchTemplate: unsupported number of channels"); diff --git a/modules/gpu/src/split_merge.cpp b/modules/gpu/src/split_merge.cpp index 2d5b1b13c1..3153c5f339 100644 --- a/modules/gpu/src/split_merge.cpp +++ b/modules/gpu/src/split_merge.cpp @@ -119,7 +119,6 @@ namespace int depth = src.depth(); int num_channels = src.channels(); - Size size = src.size(); if (depth == CV_64F) { diff --git a/modules/gpu/test/test_objdetect.cpp b/modules/gpu/test/test_objdetect.cpp index 927762b2b0..f867533c80 100644 --- a/modules/gpu/test/test_objdetect.cpp +++ b/modules/gpu/test/test_objdetect.cpp @@ -302,13 +302,13 @@ PARAM_TEST_CASE(LBP_Read_classifier, cv::gpu::DeviceInfo, int) TEST_P(LBP_Read_classifier, Accuracy) { - cv::gpu::CascadeClassifier_GPU classifier; + cv::gpu::CascadeClassifier_GPU classifier; std::string classifierXmlPath = std::string(cvtest::TS::ptr()->get_data_path()) + "lbpcascade/lbpcascade_frontalface.xml"; ASSERT_TRUE(classifier.load(classifierXmlPath)); } -INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_Read_classifier, - testing::Combine(ALL_DEVICES, testing::Values(0))); +INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_Read_classifier, + testing::Combine(ALL_DEVICES, testing::Values(0))); PARAM_TEST_CASE(LBP_classify, cv::gpu::DeviceInfo, int) @@ -344,31 +344,32 @@ TEST_P(LBP_classify, Accuracy) for (; it != rects.end(); ++it) cv::rectangle(markedImage, *it, CV_RGB(0, 0, 255)); - cv::gpu::CascadeClassifier_GPU gpuClassifier; + cv::gpu::CascadeClassifier_GPU gpuClassifier; ASSERT_TRUE(gpuClassifier.load(classifierXmlPath)); cv::gpu::GpuMat gpu_rects; cv::gpu::GpuMat tested(grey); int count = gpuClassifier.detectMultiScale(tested, gpu_rects); +#if defined (LOG_CASCADE_STATISTIC) cv::Mat downloaded(gpu_rects); - const cv::Rect* faces = downloaded.ptr(); + const cv::Rect* faces = downloaded.ptr(); for (int i = 0; i < count; i++) { cv::Rect r = faces[i]; -#if defined (LOG_CASCADE_STATISTIC) - std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl; + std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl; cv::rectangle(markedImage, r , CV_RGB(255, 0, 0)); -#endif } +#endif #if defined (LOG_CASCADE_STATISTIC) - cv::imshow("Res", markedImage); cv::waitKey(); + cv::imshow("Res", markedImage); cv::waitKey(); #endif + (void)count; } INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_classify, - testing::Combine(ALL_DEVICES, testing::Values(0))); + testing::Combine(ALL_DEVICES, testing::Values(0))); } // namespace From aeaf1a6f6d724552f3b7dd4c5dbcd1d0652a6e78 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 02:26:23 +0400 Subject: [PATCH 24/58] refactoring in Emulation --- modules/gpu/src/cuda/ccomponetns.cu | 4 ++-- modules/gpu/src/cuda/lbp.cu | 2 +- modules/gpu/src/opencv2/gpu/device/emulation.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/gpu/src/cuda/ccomponetns.cu b/modules/gpu/src/cuda/ccomponetns.cu index 275066a2ab..07f2410caa 100644 --- a/modules/gpu/src/cuda/ccomponetns.cu +++ b/modules/gpu/src/cuda/ccomponetns.cu @@ -316,7 +316,7 @@ namespace cv { namespace gpu { namespace device } } - changed = Emulation::sycthOr(changed); + changed = Emulation::syncthreadsOr(changed); if (!changed) break; @@ -474,7 +474,7 @@ namespace cv { namespace gpu { namespace device } } } - } while (Emulation::sycthOr(changed)); + } while (Emulation::syncthreadsOr(changed)); } __global__ void flatten(const DevMem2D edges, DevMem2Di comps) diff --git a/modules/gpu/src/cuda/lbp.cu b/modules/gpu/src/cuda/lbp.cu index e96692cefb..edfbf6ea59 100644 --- a/modules/gpu/src/cuda/lbp.cu +++ b/modules/gpu/src/cuda/lbp.cu @@ -279,7 +279,7 @@ namespace cv { namespace gpu { namespace device rect.z = __float2int_rn(windowW * scale); rect.w = __float2int_rn(windowH * scale); - int res = Emulation::smem::atomicInc(classified, (unsigned int)objects.cols); + int res = atomicInc(classified, (unsigned int)objects.cols); objects(0, res) = rect; } } diff --git a/modules/gpu/src/opencv2/gpu/device/emulation.hpp b/modules/gpu/src/opencv2/gpu/device/emulation.hpp index 1a6f5794cf..074e911275 100644 --- a/modules/gpu/src/opencv2/gpu/device/emulation.hpp +++ b/modules/gpu/src/opencv2/gpu/device/emulation.hpp @@ -51,11 +51,11 @@ namespace cv { namespace gpu { namespace device struct Emulation { - static __device__ __forceinline__ int sycthOr(int pred) + static __device__ __forceinline__ int syncthreadsOr(int pred) { #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ < 200) // just campilation stab - return false; + return 0; #else return __syncthreads_or(pred); #endif From ede3781e3efdcf7e5333638c0808ea80190aa4dd Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 02:27:54 +0400 Subject: [PATCH 25/58] fixed -Wstrict_alliasing warning for GCC --- modules/gpu/src/imgproc.cpp | 8 ++++- .../gpu/src/nvidia/NCVHaarObjectDetection.hpp | 32 ++++++++++++------- .../gpu/src/nvidia/NPP_staging/NPP_staging.cu | 18 +++++++---- .../nvidia/TestHaarCascadeApplication.cpp | 6 ++-- modules/gpu/test/nvidia/main_nvidia.cpp | 4 ++- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index 31ab44cb8d..fe1ad7bd89 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -261,6 +261,12 @@ namespace } } +#if defined __GNUC__ && __GNUC__ > 2 && __GNUC_MINOR__ > 4 +typedef Npp32s __attribute__((__may_alias__)) Npp32s_a; +#else +typedef Npp32s Npp32s_a; +#endif + void cv::gpu::copyMakeBorder(const GpuMat& src, GpuMat& dst, int top, int bottom, int left, int right, int borderType, const Scalar& value, Stream& s) { CV_Assert(src.depth() <= CV_32F && src.channels() <= 4); @@ -308,7 +314,7 @@ void cv::gpu::copyMakeBorder(const GpuMat& src, GpuMat& dst, int top, int bottom case CV_32FC1: { Npp32f val = saturate_cast(value[0]); - Npp32s nVal = *(reinterpret_cast(&val)); + Npp32s nVal = *(reinterpret_cast(&val)); nppSafeCall( nppiCopyConstBorder_32s_C1R(src.ptr(), static_cast(src.step), srcsz, dst.ptr(), static_cast(dst.step), dstsz, top, left, nVal) ); break; diff --git a/modules/gpu/src/nvidia/NCVHaarObjectDetection.hpp b/modules/gpu/src/nvidia/NCVHaarObjectDetection.hpp index 79872cbfa4..fd0e53e904 100644 --- a/modules/gpu/src/nvidia/NCVHaarObjectDetection.hpp +++ b/modules/gpu/src/nvidia/NCVHaarObjectDetection.hpp @@ -67,7 +67,11 @@ // Guaranteed size cross-platform classifier structures // //============================================================================== - +#if defined __GNUC__ && __GNUC__ > 2 && __GNUC_MINOR__ > 4 +typedef Ncv32f __attribute__((__may_alias__)) Ncv32f_a; +#else +typedef Ncv32f Ncv32f_a; +#endif struct HaarFeature64 { @@ -87,7 +91,7 @@ struct HaarFeature64 __host__ NCVStatus setWeight(Ncv32f weight) { - ((Ncv32f*)&(this->_ui2.y))[0] = weight; + ((Ncv32f_a*)&(this->_ui2.y))[0] = weight; return NCV_SUCCESS; } @@ -102,7 +106,7 @@ struct HaarFeature64 __device__ __host__ Ncv32f getWeight(void) { - return *(Ncv32f*)(&this->_ui2.y); + return *(Ncv32f_a*)(&this->_ui2.y); } }; @@ -168,14 +172,13 @@ public: } }; - struct HaarClassifierNodeDescriptor32 { uint1 _ui1; __host__ NCVStatus create(Ncv32f leafValue) { - *(Ncv32f *)&this->_ui1 = leafValue; + *(Ncv32f_a *)&this->_ui1 = leafValue; return NCV_SUCCESS; } @@ -187,7 +190,7 @@ struct HaarClassifierNodeDescriptor32 __host__ Ncv32f getLeafValueHost(void) { - return *(Ncv32f *)&this->_ui1.x; + return *(Ncv32f_a *)&this->_ui1.x; } #ifdef __CUDACC__ @@ -203,6 +206,11 @@ struct HaarClassifierNodeDescriptor32 } }; +#if defined __GNUC__ && __GNUC__ > 2 && __GNUC_MINOR__ > 4 +typedef Ncv32u __attribute__((__may_alias__)) Ncv32u_a; +#else +typedef Ncv32u Ncv32u_a; +#endif struct HaarClassifierNode128 { @@ -216,19 +224,19 @@ struct HaarClassifierNode128 __host__ NCVStatus setThreshold(Ncv32f t) { - this->_ui4.y = *(Ncv32u *)&t; + this->_ui4.y = *(Ncv32u_a *)&t; return NCV_SUCCESS; } __host__ NCVStatus setLeftNodeDesc(HaarClassifierNodeDescriptor32 nl) { - this->_ui4.z = *(Ncv32u *)&nl; + this->_ui4.z = *(Ncv32u_a *)&nl; return NCV_SUCCESS; } __host__ NCVStatus setRightNodeDesc(HaarClassifierNodeDescriptor32 nr) { - this->_ui4.w = *(Ncv32u *)&nr; + this->_ui4.w = *(Ncv32u_a *)&nr; return NCV_SUCCESS; } @@ -239,7 +247,7 @@ struct HaarClassifierNode128 __host__ __device__ Ncv32f getThreshold(void) { - return *(Ncv32f*)&this->_ui4.y; + return *(Ncv32f_a*)&this->_ui4.y; } __host__ __device__ HaarClassifierNodeDescriptor32 getLeftNodeDesc(void) @@ -264,7 +272,7 @@ struct HaarStage64 __host__ NCVStatus setStageThreshold(Ncv32f t) { - this->_ui2.x = *(Ncv32u *)&t; + this->_ui2.x = *(Ncv32u_a *)&t; return NCV_SUCCESS; } @@ -290,7 +298,7 @@ struct HaarStage64 __host__ __device__ Ncv32f getStageThreshold(void) { - return *(Ncv32f*)&this->_ui2.x; + return *(Ncv32f_a*)&this->_ui2.x; } __host__ __device__ Ncv32u getStartClassifierRootNodeOffset(void) diff --git a/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu b/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu index 6e0f5eb26a..f75453930b 100644 --- a/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu +++ b/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu @@ -1423,7 +1423,7 @@ NCVStatus compactVector_32u_device(Ncv32u *d_src, Ncv32u srcLen, (d_hierSums.ptr() + partSumOffsets[i], partSumNums[i], NULL, d_hierSums.ptr() + partSumOffsets[i+1], - NULL); + 0); } else { @@ -1433,7 +1433,7 @@ NCVStatus compactVector_32u_device(Ncv32u *d_src, Ncv32u srcLen, (d_hierSums.ptr() + partSumOffsets[i], partSumNums[i], NULL, NULL, - NULL); + 0); } ncvAssertCUDALastErrorReturn(NPPST_CUDA_KERNEL_EXECUTION_ERROR); @@ -1557,16 +1557,21 @@ NCVStatus nppsStCompact_32s(Ncv32s *d_src, Ncv32u srcLen, } +#if defined __GNUC__ && __GNUC__ > 2 && __GNUC_MINOR__ > 4 +typedef Ncv32u __attribute__((__may_alias__)) Ncv32u_a; +#else +typedef Ncv32u Ncv32u_a; +#endif + NCVStatus nppsStCompact_32f(Ncv32f *d_src, Ncv32u srcLen, Ncv32f *d_dst, Ncv32u *p_dstLen, Ncv32f elemRemove, Ncv8u *pBuffer, Ncv32u bufSize, cudaDeviceProp &devProp) { return nppsStCompact_32u((Ncv32u *)d_src, srcLen, (Ncv32u *)d_dst, p_dstLen, - *(Ncv32u *)&elemRemove, pBuffer, bufSize, devProp); + *(Ncv32u_a *)&elemRemove, pBuffer, bufSize, devProp); } - NCVStatus nppsStCompact_32u_host(Ncv32u *h_src, Ncv32u srcLen, Ncv32u *h_dst, Ncv32u *dstLen, Ncv32u elemRemove) { @@ -1602,17 +1607,16 @@ NCVStatus nppsStCompact_32u_host(Ncv32u *h_src, Ncv32u srcLen, NCVStatus nppsStCompact_32s_host(Ncv32s *h_src, Ncv32u srcLen, Ncv32s *h_dst, Ncv32u *dstLen, Ncv32s elemRemove) { - return nppsStCompact_32u_host((Ncv32u *)h_src, srcLen, (Ncv32u *)h_dst, dstLen, *(Ncv32u *)&elemRemove); + return nppsStCompact_32u_host((Ncv32u *)h_src, srcLen, (Ncv32u *)h_dst, dstLen, *(Ncv32u_a *)&elemRemove); } NCVStatus nppsStCompact_32f_host(Ncv32f *h_src, Ncv32u srcLen, Ncv32f *h_dst, Ncv32u *dstLen, Ncv32f elemRemove) { - return nppsStCompact_32u_host((Ncv32u *)h_src, srcLen, (Ncv32u *)h_dst, dstLen, *(Ncv32u *)&elemRemove); + return nppsStCompact_32u_host((Ncv32u *)h_src, srcLen, (Ncv32u *)h_dst, dstLen, *(Ncv32u_a *)&elemRemove); } - //============================================================================== // // Filter.cu diff --git a/modules/gpu/test/nvidia/TestHaarCascadeApplication.cpp b/modules/gpu/test/nvidia/TestHaarCascadeApplication.cpp index e53f7f3a4f..fe4f5d6a3e 100644 --- a/modules/gpu/test/nvidia/TestHaarCascadeApplication.cpp +++ b/modules/gpu/test/nvidia/TestHaarCascadeApplication.cpp @@ -245,8 +245,8 @@ bool TestHaarCascadeApplication::process() int devId; ncvAssertCUDAReturn(cudaGetDevice(&devId), false); - cudaDeviceProp devProp; - ncvAssertCUDAReturn(cudaGetDeviceProperties(&devProp, devId), false); + cudaDeviceProp _devProp; + ncvAssertCUDAReturn(cudaGetDeviceProperties(&_devProp, devId), false); ncvStat = ncvApplyHaarClassifierCascade_device( d_integralImage, d_rectStdDev, d_pixelMask, @@ -254,7 +254,7 @@ bool TestHaarCascadeApplication::process() haar, h_HaarStages, d_HaarStages, d_HaarNodes, d_HaarFeatures, false, searchRoiU, 1, 1.0f, *this->allocatorGPU.get(), *this->allocatorCPU.get(), - devProp, 0); + _devProp, 0); ncvAssertReturn(ncvStat == NCV_SUCCESS, false); NCVMatrixAlloc h_pixelMask_d(*this->allocatorCPU.get(), this->width, this->height); diff --git a/modules/gpu/test/nvidia/main_nvidia.cpp b/modules/gpu/test/nvidia/main_nvidia.cpp index bb61608946..15759e10fb 100644 --- a/modules/gpu/test/nvidia/main_nvidia.cpp +++ b/modules/gpu/test/nvidia/main_nvidia.cpp @@ -1,4 +1,6 @@ -#pragma warning (disable : 4408 4201 4100) +#if defined _MSC_VER && _MSC_VER >= 1200 +# pragma warning (disable : 4408 4201 4100) +#endif #include From 15213401529574ba4f4ae81d04dd01ec61f83991 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 03:25:52 +0400 Subject: [PATCH 26/58] updated CascadeClassifier_GPU documentation --- modules/gpu/doc/object_detection.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/gpu/doc/object_detection.rst b/modules/gpu/doc/object_detection.rst index 1f522f3441..133660236a 100644 --- a/modules/gpu/doc/object_detection.rst +++ b/modules/gpu/doc/object_detection.rst @@ -204,7 +204,7 @@ gpu::CascadeClassifier_GPU -------------------------- .. ocv:class:: gpu::CascadeClassifier_GPU -Cascade classifier class used for object detection. :: +Cascade classifier class used for object detection. Supports HAAR and LBP cascades. :: class CV_EXPORTS CascadeClassifier_GPU { @@ -219,6 +219,7 @@ Cascade classifier class used for object detection. :: /* Returns number of detected objects */ int detectMultiScale( const GpuMat& image, GpuMat& objectsBuf, double scaleFactor=1.2, int minNeighbors=4, Size minSize=Size()); + int detectMultiScale( const GpuMat& image, GpuMat& objectsBuf, Size maxObjectSize, Size minSize = Size(), double scaleFactor = 1.1, int minNeighbors = 4); /* Finds only the largest object. Special mode if training is required.*/ bool findLargestObject; @@ -233,11 +234,11 @@ Cascade classifier class used for object detection. :: gpu::CascadeClassifier_GPU::CascadeClassifier_GPU ----------------------------------------------------- -Loads the classifier from a file. +Loads the classifier from a file. Cascade type is detected automatically by constructor parameter. .. ocv:function:: gpu::CascadeClassifier_GPU::CascadeClassifier_GPU(const string& filename) - :param filename: Name of the file from which the classifier is loaded. Only the old ``haar`` classifier (trained by the ``haar`` training application) and NVIDIA's ``nvbin`` are supported. + :param filename: Name of the file from which the classifier is loaded. Only the old ``haar`` classifier (trained by the ``haar`` training application) and NVIDIA's ``nvbin`` are supported for HAAR and only new type of OpenCV XML cascade supported for LBP. @@ -255,8 +256,7 @@ Loads the classifier from a file. The previous content is destroyed. .. ocv:function:: bool gpu::CascadeClassifier_GPU::load(const string& filename) - :param filename: Name of the file from which the classifier is loaded. Only the old ``haar`` classifier (trained by the ``haar`` training application) and NVIDIA's ``nvbin`` are supported. - + :param filename: Name of the file from which the classifier is loaded. Only the old ``haar`` classifier (trained by the ``haar`` training application) and NVIDIA's ``nvbin`` are supported for HAAR and only new type of OpenCV XML cascade supported for LBP. gpu::CascadeClassifier_GPU::release @@ -273,13 +273,17 @@ Detects objects of different sizes in the input image. .. ocv:function:: int gpu::CascadeClassifier_GPU::detectMultiScale(const GpuMat& image, GpuMat& objectsBuf, double scaleFactor=1.2, int minNeighbors=4, Size minSize=Size()) +.. ocv:function:: int gpu::CascadeClassifier_GPU::detectMultiScale(const GpuMat& image, GpuMat& objectsBuf, Size maxObjectSize, Size minSize = Size(), double scaleFactor = 1.1, int minNeighbors = 4) + :param image: Matrix of type ``CV_8U`` containing an image where objects should be detected. :param objectsBuf: Buffer to store detected objects (rectangles). If it is empty, it is allocated with the default size. If not empty, the function searches not more than N objects, where ``N = sizeof(objectsBufer's data)/sizeof(cv::Rect)``. - :param scaleFactor: Value to specify how much the image size is reduced at each image scale. + :param maxObjectSize: Maximum possible object size. Objects larger than that are ignored. Used for second signature and supported only for LBP cascades. + + :param scaleFactor: Parameter specifying how much the image size is reduced at each image scale. - :param minNeighbors: Value to specify how many neighbours each candidate rectangle has to retain. + :param minNeighbors: Parameter specifying how many neighbors each candidate rectangle should have to retain it. :param minSize: Minimum possible object size. Objects smaller than that are ignored. From f17f4bda6026ea02668c1a6ccf8f0726b1ed8682 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 03:26:53 +0400 Subject: [PATCH 27/58] more warning fixes for GCC --- modules/gpu/src/brute_force_matcher.cpp | 50 ++++++++++++------------- modules/gpu/src/hog.cpp | 6 +-- modules/gpu/src/video_decoder.cpp | 42 ++++++++++----------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/modules/gpu/src/brute_force_matcher.cpp b/modules/gpu/src/brute_force_matcher.cpp index d70926068c..a1fe066136 100644 --- a/modules/gpu/src/brute_force_matcher.cpp +++ b/modules/gpu/src/brute_force_matcher.cpp @@ -420,16 +420,16 @@ void cv::gpu::BFMatcher_GPU::matchConvert(const Mat& trainIdx, const Mat& imgIdx const float* distance_ptr = distance.ptr(); for (int queryIdx = 0; queryIdx < nQuery; ++queryIdx, ++trainIdx_ptr, ++imgIdx_ptr, ++distance_ptr) { - int trainIdx = *trainIdx_ptr; + int _trainIdx = *trainIdx_ptr; - if (trainIdx == -1) + if (_trainIdx == -1) continue; - int imgIdx = *imgIdx_ptr; + int _imgIdx = *imgIdx_ptr; - float distance = *distance_ptr; + float _distance = *distance_ptr; - DMatch m(queryIdx, trainIdx, imgIdx, distance); + DMatch m(queryIdx, _trainIdx, _imgIdx, _distance); matches.push_back(m); } @@ -558,13 +558,13 @@ void cv::gpu::BFMatcher_GPU::knnMatchConvert(const Mat& trainIdx, const Mat& dis for (int i = 0; i < k; ++i, ++trainIdx_ptr, ++distance_ptr) { - int trainIdx = *trainIdx_ptr; + int _trainIdx = *trainIdx_ptr; - if (trainIdx != -1) + if (_trainIdx != -1) { - float distance = *distance_ptr; + float _distance = *distance_ptr; - DMatch m(queryIdx, trainIdx, 0, distance); + DMatch m(queryIdx, _trainIdx, 0, _distance); curMatches.push_back(m); } @@ -680,15 +680,15 @@ void cv::gpu::BFMatcher_GPU::knnMatch2Convert(const Mat& trainIdx, const Mat& im for (int i = 0; i < 2; ++i, ++trainIdx_ptr, ++imgIdx_ptr, ++distance_ptr) { - int trainIdx = *trainIdx_ptr; + int _trainIdx = *trainIdx_ptr; - if (trainIdx != -1) + if (_trainIdx != -1) { - int imgIdx = *imgIdx_ptr; + int _imgIdx = *imgIdx_ptr; - float distance = *distance_ptr; + float _distance = *distance_ptr; - DMatch m(queryIdx, trainIdx, imgIdx, distance); + DMatch m(queryIdx, _trainIdx, _imgIdx, _distance); curMatches.push_back(m); } @@ -868,25 +868,25 @@ void cv::gpu::BFMatcher_GPU::radiusMatchConvert(const Mat& trainIdx, const Mat& const int* trainIdx_ptr = trainIdx.ptr(queryIdx); const float* distance_ptr = distance.ptr(queryIdx); - const int nMatches = std::min(nMatches_ptr[queryIdx], trainIdx.cols); + const int nMatched = std::min(nMatches_ptr[queryIdx], trainIdx.cols); - if (nMatches == 0) + if (nMatched == 0) { if (!compactResult) matches.push_back(vector()); continue; } - matches.push_back(vector(nMatches)); + matches.push_back(vector(nMatched)); vector& curMatches = matches.back(); - for (int i = 0; i < nMatches; ++i, ++trainIdx_ptr, ++distance_ptr) + for (int i = 0; i < nMatched; ++i, ++trainIdx_ptr, ++distance_ptr) { - int trainIdx = *trainIdx_ptr; + int _trainIdx = *trainIdx_ptr; - float distance = *distance_ptr; + float _distance = *distance_ptr; - DMatch m(queryIdx, trainIdx, 0, distance); + DMatch m(queryIdx, _trainIdx, 0, _distance); curMatches[i] = m; } @@ -1009,9 +1009,9 @@ void cv::gpu::BFMatcher_GPU::radiusMatchConvert(const Mat& trainIdx, const Mat& const int* imgIdx_ptr = imgIdx.ptr(queryIdx); const float* distance_ptr = distance.ptr(queryIdx); - const int nMatches = std::min(nMatches_ptr[queryIdx], trainIdx.cols); + const int nMatched = std::min(nMatches_ptr[queryIdx], trainIdx.cols); - if (nMatches == 0) + if (nMatched == 0) { if (!compactResult) matches.push_back(vector()); @@ -1020,9 +1020,9 @@ void cv::gpu::BFMatcher_GPU::radiusMatchConvert(const Mat& trainIdx, const Mat& matches.push_back(vector()); vector& curMatches = matches.back(); - curMatches.reserve(nMatches); + curMatches.reserve(nMatched); - for (int i = 0; i < nMatches; ++i, ++trainIdx_ptr, ++imgIdx_ptr, ++distance_ptr) + for (int i = 0; i < nMatched; ++i, ++trainIdx_ptr, ++imgIdx_ptr, ++distance_ptr) { int _trainIdx = *trainIdx_ptr; int _imgIdx = *imgIdx_ptr; diff --git a/modules/gpu/src/hog.cpp b/modules/gpu/src/hog.cpp index fafcce784b..5db3be9781 100644 --- a/modules/gpu/src/hog.cpp +++ b/modules/gpu/src/hog.cpp @@ -315,7 +315,7 @@ void cv::gpu::HOGDescriptor::computeConfidenceMultiScale(const GpuMat& img, vect double scale = 1.; int levels = 0; - for (levels = 0; levels < conf_out.size(); levels++) + for (levels = 0; levels < (int)conf_out.size(); levels++) { scale = conf_out[levels].scale; level_scale.push_back(scale); @@ -332,8 +332,8 @@ void cv::gpu::HOGDescriptor::computeConfidenceMultiScale(const GpuMat& img, vect for (size_t i = 0; i < level_scale.size(); i++) { - double scale = level_scale[i]; - Size sz(cvRound(img.cols / scale), cvRound(img.rows / scale)); + double _scale = level_scale[i]; + Size sz(cvRound(img.cols / _scale), cvRound(img.rows / _scale)); GpuMat smaller_img; if (sz == img.size()) diff --git a/modules/gpu/src/video_decoder.cpp b/modules/gpu/src/video_decoder.cpp index 9f06adc9ce..bee5002a88 100644 --- a/modules/gpu/src/video_decoder.cpp +++ b/modules/gpu/src/video_decoder.cpp @@ -49,36 +49,36 @@ void cv::gpu::detail::VideoDecoder::create(const VideoReader_GPU::FormatInfo& vi { release(); - cudaVideoCodec codec = static_cast(videoFormat.codec); - cudaVideoChromaFormat chromaFormat = static_cast(videoFormat.chromaFormat); + cudaVideoCodec _codec = static_cast(videoFormat.codec); + cudaVideoChromaFormat _chromaFormat = static_cast(videoFormat.chromaFormat); - cudaVideoCreateFlags videoCreateFlags = (codec == cudaVideoCodec_JPEG || codec == cudaVideoCodec_MPEG2) ? + cudaVideoCreateFlags videoCreateFlags = (_codec == cudaVideoCodec_JPEG || _codec == cudaVideoCodec_MPEG2) ? cudaVideoCreate_PreferCUDA : cudaVideoCreate_PreferCUVID; // Validate video format. These are the currently supported formats via NVCUVID - CV_Assert(cudaVideoCodec_MPEG1 == codec || - cudaVideoCodec_MPEG2 == codec || - cudaVideoCodec_MPEG4 == codec || - cudaVideoCodec_VC1 == codec || - cudaVideoCodec_H264 == codec || - cudaVideoCodec_JPEG == codec || - cudaVideoCodec_YUV420== codec || - cudaVideoCodec_YV12 == codec || - cudaVideoCodec_NV12 == codec || - cudaVideoCodec_YUYV == codec || - cudaVideoCodec_UYVY == codec ); - - CV_Assert(cudaVideoChromaFormat_Monochrome == chromaFormat || - cudaVideoChromaFormat_420 == chromaFormat || - cudaVideoChromaFormat_422 == chromaFormat || - cudaVideoChromaFormat_444 == chromaFormat); + CV_Assert(cudaVideoCodec_MPEG1 == _codec || + cudaVideoCodec_MPEG2 == _codec || + cudaVideoCodec_MPEG4 == _codec || + cudaVideoCodec_VC1 == _codec || + cudaVideoCodec_H264 == _codec || + cudaVideoCodec_JPEG == _codec || + cudaVideoCodec_YUV420== _codec || + cudaVideoCodec_YV12 == _codec || + cudaVideoCodec_NV12 == _codec || + cudaVideoCodec_YUYV == _codec || + cudaVideoCodec_UYVY == _codec ); + + CV_Assert(cudaVideoChromaFormat_Monochrome == _chromaFormat || + cudaVideoChromaFormat_420 == _chromaFormat || + cudaVideoChromaFormat_422 == _chromaFormat || + cudaVideoChromaFormat_444 == _chromaFormat); // Fill the decoder-create-info struct from the given video-format struct. std::memset(&createInfo_, 0, sizeof(CUVIDDECODECREATEINFO)); // Create video decoder - createInfo_.CodecType = codec; + createInfo_.CodecType = _codec; createInfo_.ulWidth = videoFormat.width; createInfo_.ulHeight = videoFormat.height; createInfo_.ulNumDecodeSurfaces = FrameQueue::MaximumSize; @@ -87,7 +87,7 @@ void cv::gpu::detail::VideoDecoder::create(const VideoReader_GPU::FormatInfo& vi while (createInfo_.ulNumDecodeSurfaces * videoFormat.width * videoFormat.height > 16 * 1024 * 1024) createInfo_.ulNumDecodeSurfaces--; - createInfo_.ChromaFormat = chromaFormat; + createInfo_.ChromaFormat = _chromaFormat; createInfo_.OutputFormat = cudaVideoSurfaceFormat_NV12; createInfo_.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive; From e7f4dd1a2e5d648c242af0151b0a183eb7dcd950 Mon Sep 17 00:00:00 2001 From: niko Date: Mon, 20 Aug 2012 10:06:41 +0800 Subject: [PATCH 28/58] temp build fix because the new core.hpp impact on this project --- modules/ocl/src/initialization.cpp | 6 +++--- modules/ocl/src/threadsafe.cpp | 4 ++-- modules/ocl/src/threadsafe.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/ocl/src/initialization.cpp b/modules/ocl/src/initialization.cpp index fab81fd46d..096b9790de 100644 --- a/modules/ocl/src/initialization.cpp +++ b/modules/ocl/src/initialization.cpp @@ -44,9 +44,9 @@ //M*/ #include "precomp.hpp" -#include "Threadsafe.h" +#include "threadsafe.h" #include -#include "binaryCaching.hpp" +#include "binarycaching.hpp" using namespace cv; using namespace cv::ocl; @@ -775,7 +775,7 @@ namespace cv { if(val == 0) { - AutoLock al(&cs); + myAutoLock al(&cs); if( NULL == clCxt.get()) clCxt.reset(new Context); diff --git a/modules/ocl/src/threadsafe.cpp b/modules/ocl/src/threadsafe.cpp index 9d952d328c..25b3773e04 100644 --- a/modules/ocl/src/threadsafe.cpp +++ b/modules/ocl/src/threadsafe.cpp @@ -43,8 +43,8 @@ // //M*/ -#include "precomp.hpp" -#include "Threadsafe.h" +//#include "precomp.hpp" +#include "threadsafe.h" CriticalSection::CriticalSection() { diff --git a/modules/ocl/src/threadsafe.h b/modules/ocl/src/threadsafe.h index b7954fdcdd..5905a55172 100644 --- a/modules/ocl/src/threadsafe.h +++ b/modules/ocl/src/threadsafe.h @@ -65,15 +65,15 @@ protected: #endif }; -class AutoLock +class myAutoLock { public: - explicit AutoLock(CriticalSection *lock) + explicit myAutoLock(CriticalSection *lock) { m_lock = lock; m_lock->Lock(); }; - ~AutoLock() + ~myAutoLock() { m_lock->Unlock(); }; From a0aef244d675afc515397e2df3bd7a5d04312b78 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 20 Aug 2012 09:58:37 +0400 Subject: [PATCH 29/58] removed perf_gpu_cpu from CMake scripts --- cmake/OpenCVModule.cmake | 2 - modules/gpu/CMakeLists.txt | 40 ------------------- modules/gpu/perf/perf_precomp.hpp | 2 +- .../perf/{perf_utility.cpp => utility.cpp} | 0 .../perf/{perf_utility.hpp => utility.hpp} | 0 .../test/{precomp.cpp => test_precomp.cpp} | 0 .../test/{precomp.hpp => test_precomp.hpp} | 0 7 files changed, 1 insertion(+), 43 deletions(-) rename modules/gpu/perf/{perf_utility.cpp => utility.cpp} (100%) rename modules/gpu/perf/{perf_utility.hpp => utility.hpp} (100%) rename modules/gpu/test/{precomp.cpp => test_precomp.cpp} (100%) rename modules/gpu/test/{precomp.hpp => test_precomp.hpp} (100%) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 543f9970b6..018a467308 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -509,8 +509,6 @@ endmacro() macro(ocv_add_precompiled_headers the_target) if("${the_target}" MATCHES "^opencv_test_.*$") SET(pch_path "test/test_") - elseif("${the_target}" MATCHES "opencv_perf_gpu_cpu") - SET(pch_path "perf_cpu/perf_cpu_") elseif("${the_target}" MATCHES "^opencv_perf_.*$") SET(pch_path "perf/perf_") else() diff --git a/modules/gpu/CMakeLists.txt b/modules/gpu/CMakeLists.txt index e24b73a77d..5d2c440fe5 100644 --- a/modules/gpu/CMakeLists.txt +++ b/modules/gpu/CMakeLists.txt @@ -111,43 +111,3 @@ ocv_add_accuracy_tests(FILES "Include" ${test_hdrs} FILES "Src" ${test_srcs} ${nvidia}) ocv_add_perf_tests() - - - -set(perf_cpu_path "${CMAKE_CURRENT_SOURCE_DIR}/perf_cpu") -if(BUILD_PERF_TESTS AND EXISTS "${perf_cpu_path}") - # opencv_highgui is required for imread/imwrite - set(perf_deps ${the_module} opencv_ts opencv_highgui opencv_imgproc opencv_calib3d opencv_objdetect opencv_video opencv_nonfree) - ocv_check_dependencies(${perf_deps}) - - if(OCV_DEPENDENCIES_FOUND) - set(the_target "opencv_perf_gpu_cpu") - - ocv_module_include_directories(${perf_deps} "${perf_cpu_path}") - - if(NOT OPENCV_PERF_${the_module}_CPU_SOURCES) - file(GLOB perf_srcs "${perf_cpu_path}/*.cpp") - file(GLOB perf_hdrs "${perf_cpu_path}/*.hpp" "${perf_cpu_path}/*.h") - source_group("Src" FILES ${perf_srcs}) - source_group("Include" FILES ${perf_hdrs}) - set(OPENCV_PERF_${the_module}_CPU_SOURCES ${perf_srcs} ${perf_hdrs}) - endif() - - add_executable(${the_target} ${OPENCV_PERF_${the_module}_CPU_SOURCES}) - target_link_libraries(${the_target} ${OPENCV_MODULE_${the_module}_DEPS} ${perf_deps} ${OPENCV_LINKER_LIBS}) - - # Additional target properties - set_target_properties(${the_target} PROPERTIES - DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" - RUNTIME_OUTPUT_DIRECTORY "${EXECUTABLE_OUTPUT_PATH}" - ) - - if(ENABLE_SOLUTION_FOLDERS) - set_target_properties(${the_target} PROPERTIES FOLDER "tests performance") - endif() - - ocv_add_precompiled_headers(${the_target}) - else(OCV_DEPENDENCIES_FOUND) - #TODO: warn about unsatisfied dependencies - endif(OCV_DEPENDENCIES_FOUND) - endif() diff --git a/modules/gpu/perf/perf_precomp.hpp b/modules/gpu/perf/perf_precomp.hpp index be3f234a35..8378599475 100644 --- a/modules/gpu/perf/perf_precomp.hpp +++ b/modules/gpu/perf/perf_precomp.hpp @@ -27,7 +27,7 @@ #include "opencv2/nonfree/nonfree.hpp" #include "opencv2/legacy/legacy.hpp" -#include "perf_utility.hpp" +#include "utility.hpp" #ifdef GTEST_CREATE_SHARED_LIBRARY #error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined diff --git a/modules/gpu/perf/perf_utility.cpp b/modules/gpu/perf/utility.cpp similarity index 100% rename from modules/gpu/perf/perf_utility.cpp rename to modules/gpu/perf/utility.cpp diff --git a/modules/gpu/perf/perf_utility.hpp b/modules/gpu/perf/utility.hpp similarity index 100% rename from modules/gpu/perf/perf_utility.hpp rename to modules/gpu/perf/utility.hpp diff --git a/modules/gpu/test/precomp.cpp b/modules/gpu/test/test_precomp.cpp similarity index 100% rename from modules/gpu/test/precomp.cpp rename to modules/gpu/test/test_precomp.cpp diff --git a/modules/gpu/test/precomp.hpp b/modules/gpu/test/test_precomp.hpp similarity index 100% rename from modules/gpu/test/precomp.hpp rename to modules/gpu/test/test_precomp.hpp From ec7f9566e0df2d535d7ac5123026720cb7e80d2d Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 20 Aug 2012 10:15:36 +0400 Subject: [PATCH 30/58] minor gpu tests fix --- modules/gpu/perf/main.cpp | 6 +- modules/gpu/test/main.cpp | 92 ++++++++++--------- modules/gpu/test/main_test_nvidia.h | 4 +- modules/gpu/test/test_calib3d.cpp | 8 +- modules/gpu/test/test_color.cpp | 2 +- modules/gpu/test/test_copy_make_border.cpp | 6 +- modules/gpu/test/test_core.cpp | 6 +- modules/gpu/test/test_features2d.cpp | 6 +- modules/gpu/test/test_filters.cpp | 6 +- modules/gpu/test/test_global_motion.cpp | 8 +- modules/gpu/test/test_gpumat.cpp | 6 +- modules/gpu/test/test_imgproc.cpp | 6 +- modules/gpu/test/test_labeling.cpp | 6 +- modules/gpu/test/test_nvidia.cpp | 14 +-- modules/gpu/test/test_objdetect.cpp | 25 ++--- modules/gpu/test/test_precomp.cpp | 2 +- modules/gpu/test/test_precomp.hpp | 2 + modules/gpu/test/test_pyramids.cpp | 2 +- modules/gpu/test/test_remap.cpp | 2 +- modules/gpu/test/test_resize.cpp | 3 +- modules/gpu/test/test_threshold.cpp | 2 +- modules/gpu/test/test_video.cpp | 6 +- modules/gpu/test/test_warp_affine.cpp | 2 +- modules/gpu/test/test_warp_perspective.cpp | 2 +- modules/gpu/test/utility.cpp | 101 +-------------------- modules/gpu/test/utility.hpp | 4 +- 26 files changed, 134 insertions(+), 195 deletions(-) diff --git a/modules/gpu/perf/main.cpp b/modules/gpu/perf/main.cpp index 865362f880..aadeee955b 100644 --- a/modules/gpu/perf/main.cpp +++ b/modules/gpu/perf/main.cpp @@ -71,9 +71,9 @@ void printCudaInfo() #endif } -int main(int argc, char **argv) +int main(int argc, char** argv) { - CommandLineParser cmd(argc, argv, + CommandLineParser cmd(argc, (const char**) argv, "{ print_info_only | print_info_only | false | Print information about system and exit }" "{ device | device | 0 | Device on which tests will be executed }" "{ cpu | cpu | false | Run tests on cpu }" @@ -119,7 +119,7 @@ int main(int argc, char **argv) cout << "Run tests on device " << device << " [" << info.name() << "] \n" << endl; } - testing::InitGoogleTest(&argc, argv); + InitGoogleTest(&argc, argv); perf::TestBase::Init(argc, argv); return RUN_ALL_TESTS(); } diff --git a/modules/gpu/test/main.cpp b/modules/gpu/test/main.cpp index 6df7db0a1b..8f216c9fea 100644 --- a/modules/gpu/test/main.cpp +++ b/modules/gpu/test/main.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA @@ -49,93 +49,103 @@ using namespace cv::gpu; using namespace cvtest; using namespace testing; -void printInfo() +void printOsInfo() { #if defined _WIN32 # if defined _WIN64 - puts("OS: Windows x64"); + cout << "OS: Windows x64 \n" << endl; # else - puts("OS: Windows x32"); + cout << "OS: Windows x32 \n" << endl; # endif #elif defined linux # if defined _LP64 - puts("OS: Linux x64"); + cout << "OS: Linux x64 \n" << endl; # else - puts("OS: Linux x32"); + cout << "OS: Linux x32 \n" << endl; # endif #elif defined __APPLE__ # if defined _LP64 - puts("OS: Apple x64"); + cout << "OS: Apple x64 \n" << endl; # else - puts("OS: Apple x32"); + cout << "OS: Apple x32 \n" << endl; # endif #endif +} +void printCudaInfo() +{ +#ifndef HAVE_CUDA + cout << "OpenCV was built without CUDA support \n" << endl; +#else int driver; cudaDriverGetVersion(&driver); - printf("CUDA Driver version: %d\n", driver); - printf("CUDA Runtime version: %d\n", CUDART_VERSION); + cout << "CUDA Driver version: " << driver << '\n'; + cout << "CUDA Runtime version: " << CUDART_VERSION << '\n'; + + cout << endl; - puts("GPU module was compiled for the following GPU archs:"); - printf(" BIN: %s\n", CUDA_ARCH_BIN); - printf(" PTX: %s\n\n", CUDA_ARCH_PTX); + cout << "GPU module was compiled for the following GPU archs:" << endl; + cout << " BIN: " << CUDA_ARCH_BIN << '\n'; + cout << " PTX: " << CUDA_ARCH_PTX << '\n'; + + cout << endl; int deviceCount = getCudaEnabledDeviceCount(); - printf("CUDA device count: %d\n\n", deviceCount); + cout << "CUDA device count: " << deviceCount << '\n'; + + cout << endl; for (int i = 0; i < deviceCount; ++i) { DeviceInfo info(i); - printf("Device %d:\n", i); - printf(" Name: %s\n", info.name().c_str()); - printf(" Compute capability version: %d.%d\n", info.majorVersion(), info.minorVersion()); - printf(" Multi Processor Count: %d\n", info.multiProcessorCount()); - printf(" Total memory: %d Mb\n", static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0)); - printf(" Free memory: %d Mb\n", static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0)); + cout << "Device [" << i << "] \n"; + cout << "\t Name: " << info.name() << '\n'; + cout << "\t Compute capability: " << info.majorVersion() << '.' << info.minorVersion()<< '\n'; + cout << "\t Multi Processor Count: " << info.multiProcessorCount() << '\n'; + cout << "\t Total memory: " << static_cast(static_cast(info.totalMemory() / 1024.0) / 1024.0) << " Mb \n"; + cout << "\t Free memory: " << static_cast(static_cast(info.freeMemory() / 1024.0) / 1024.0) << " Mb \n"; if (!info.isCompatible()) - puts(" !!! This device is NOT compatible with current GPU module build\n"); - printf("\n"); + cout << "\t !!! This device is NOT compatible with current GPU module build \n"; + + cout << endl; } +#endif } -enum OutputLevel -{ - OutputLevelNone, - OutputLevelCompact, - OutputLevelFull -}; - -extern OutputLevel nvidiaTestOutputLevel; - int main(int argc, char** argv) { try { - CommandLineParser parser(argc, (const char**)argv, - "{ print_info_only | print_info_only | false | Print information about system and exit }" - "{ device | device | -1 | Device on which tests will be executed (-1 means all devices) }" - "{ nvtest_output_level | nvtest_output_level | compact | NVidia test verbosity level }"); + CommandLineParser cmd(argc, (const char**)argv, + "{ print_info_only | print_info_only | false | Print information about system and exit }" + "{ device | device | -1 | Device on which tests will be executed (-1 means all devices) }" + "{ nvtest_output_level | nvtest_output_level | compact | NVidia test verbosity level }" + ); - printInfo(); + printOsInfo(); + printCudaInfo(); - if (parser.get("print_info_only")) + if (cmd.get("print_info_only")) return 0; - int device = parser.get("device"); + int device = cmd.get("device"); if (device < 0) { DeviceManager::instance().loadAll(); - std::cout << "Run tests on all supported devices\n" << std::endl; + + cout << "Run tests on all supported devices \n" << endl; } else { DeviceManager::instance().load(device); - std::cout << "Run tests on device " << device << '\n' << std::endl; + + DeviceInfo info(device); + cout << "Run tests on device " << device << " [" << info.name() << "] \n" << endl; } - string outputLevel = parser.get("nvtest_output_level"); + string outputLevel = cmd.get("nvtest_output_level"); if (outputLevel == "none") nvidiaTestOutputLevel = OutputLevelNone; diff --git a/modules/gpu/test/main_test_nvidia.h b/modules/gpu/test/main_test_nvidia.h index d1c3620128..15016ca64c 100644 --- a/modules/gpu/test/main_test_nvidia.h +++ b/modules/gpu/test/main_test_nvidia.h @@ -1,7 +1,7 @@ #ifndef __main_test_nvidia_h__ #define __main_test_nvidia_h__ -#include +#include enum OutputLevel { @@ -10,6 +10,8 @@ enum OutputLevel OutputLevelFull }; +extern OutputLevel nvidiaTestOutputLevel; + bool nvidia_NPPST_Integral_Image(const std::string& test_data_path, OutputLevel outputLevel); bool nvidia_NPPST_Squared_Integral_Image(const std::string& test_data_path, OutputLevel outputLevel); bool nvidia_NPPST_RectStdDev(const std::string& test_data_path, OutputLevel outputLevel); diff --git a/modules/gpu/test/test_calib3d.cpp b/modules/gpu/test/test_calib3d.cpp index bdc043fc58..5fa1ebb41d 100644 --- a/modules/gpu/test/test_calib3d.cpp +++ b/modules/gpu/test/test_calib3d.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -329,7 +331,7 @@ TEST_P(ReprojectImageTo3D, Accuracy) cv::gpu::GpuMat dst; cv::gpu::reprojectImageTo3D(loadMat(disp, useRoi), dst, Q, 3); - + cv::Mat dst_gold; cv::reprojectImageTo3D(disp, dst_gold, Q, false); @@ -343,3 +345,5 @@ INSTANTIATE_TEST_CASE_P(GPU_Calib3D, ReprojectImageTo3D, testing::Combine( WHOLE_SUBMAT)); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_color.cpp b/modules/gpu/test/test_color.cpp index 645967ef27..c2929fbe71 100644 --- a/modules/gpu/test/test_color.cpp +++ b/modules/gpu/test/test_color.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_copy_make_border.cpp b/modules/gpu/test/test_copy_make_border.cpp index 45b73d302d..8bd5a66f90 100644 --- a/modules/gpu/test/test_copy_make_border.cpp +++ b/modules/gpu/test/test_copy_make_border.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -98,3 +100,5 @@ INSTANTIATE_TEST_CASE_P(GPU_ImgProc, CopyMakeBorder, testing::Combine( WHOLE_SUBMAT)); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_core.cpp b/modules/gpu/test/test_core.cpp index 6502c4a054..09c6be1ac3 100644 --- a/modules/gpu/test/test_core.cpp +++ b/modules/gpu/test/test_core.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -3396,3 +3398,5 @@ INSTANTIATE_TEST_CASE_P(GPU_Core, Reduce, testing::Combine( WHOLE_SUBMAT)); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_features2d.cpp b/modules/gpu/test/test_features2d.cpp index d8ed864eb6..b461d4b441 100644 --- a/modules/gpu/test/test_features2d.cpp +++ b/modules/gpu/test/test_features2d.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -984,3 +986,5 @@ INSTANTIATE_TEST_CASE_P(GPU_Features2D, BruteForceMatcher, testing::Combine( testing::Values(UseMask(false), UseMask(true)))); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_filters.cpp b/modules/gpu/test/test_filters.cpp index 9df6ee27ca..0781970e49 100644 --- a/modules/gpu/test/test_filters.cpp +++ b/modules/gpu/test/test_filters.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -552,3 +554,5 @@ INSTANTIATE_TEST_CASE_P(GPU_Filter, Filter2D, testing::Combine( WHOLE_SUBMAT)); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_global_motion.cpp b/modules/gpu/test/test_global_motion.cpp index eb8b2f7c11..b37d080684 100644 --- a/modules/gpu/test/test_global_motion.cpp +++ b/modules/gpu/test/test_global_motion.cpp @@ -39,11 +39,11 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" -#include -using namespace std; +#ifdef HAVE_CUDA +using namespace std; using namespace cv; struct CompactPoints : testing::TestWithParam @@ -85,3 +85,5 @@ TEST_P(CompactPoints, CanCompactizeSmallInput) } INSTANTIATE_TEST_CASE_P(GPU_GlobalMotion, CompactPoints, ALL_DEVICES); + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_gpumat.cpp b/modules/gpu/test/test_gpumat.cpp index 4dd419a181..8627957598 100644 --- a/modules/gpu/test/test_gpumat.cpp +++ b/modules/gpu/test/test_gpumat.cpp @@ -40,7 +40,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -323,3 +325,5 @@ INSTANTIATE_TEST_CASE_P(GPU_GpuMat, ConvertTo, testing::Combine( WHOLE_SUBMAT)); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index 4d67de59d3..7a616a2e4b 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -1186,3 +1188,5 @@ INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughLines, testing::Combine( std::string("../cv/shared/pic6.png")))); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_labeling.cpp b/modules/gpu/test/test_labeling.cpp index c88109af19..46f6f4eeba 100644 --- a/modules/gpu/test/test_labeling.cpp +++ b/modules/gpu/test/test_labeling.cpp @@ -39,9 +39,7 @@ // the use of this software, even if advised of the possibility of such damage. //M*/ -#include "precomp.hpp" -#include -#include +#include "test_precomp.hpp" #ifdef HAVE_CUDA @@ -199,4 +197,4 @@ TEST_P(Labeling, ConnectedComponents) INSTANTIATE_TEST_CASE_P(ConnectedComponents, Labeling, ALL_DEVICES); -#endif \ No newline at end of file +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_nvidia.cpp b/modules/gpu/test/test_nvidia.cpp index 386a66cfd7..3de355744c 100644 --- a/modules/gpu/test/test_nvidia.cpp +++ b/modules/gpu/test/test_nvidia.cpp @@ -39,21 +39,15 @@ // //M*/ -#include -#include "precomp.hpp" +#include "test_precomp.hpp" + +OutputLevel nvidiaTestOutputLevel = OutputLevelCompact; #ifdef HAVE_CUDA using namespace cvtest; using namespace testing; -//enum OutputLevel -//{ -// OutputLevelNone, -// OutputLevelCompact, -// OutputLevelFull -//}; - struct NVidiaTest : TestWithParam { cv::gpu::DeviceInfo devInfo; @@ -73,8 +67,6 @@ struct NVidiaTest : TestWithParam struct NPPST : NVidiaTest {}; struct NCV : NVidiaTest {}; -OutputLevel nvidiaTestOutputLevel = OutputLevelCompact; - //TEST_P(NPPST, Integral) //{ // bool res = nvidia_NPPST_Integral_Image(path, nvidiaTestOutputLevel); diff --git a/modules/gpu/test/test_objdetect.cpp b/modules/gpu/test/test_objdetect.cpp index 927762b2b0..957ee11656 100644 --- a/modules/gpu/test/test_objdetect.cpp +++ b/modules/gpu/test/test_objdetect.cpp @@ -39,8 +39,9 @@ // //M*/ -#include "precomp.hpp" -#include +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA namespace { @@ -302,13 +303,13 @@ PARAM_TEST_CASE(LBP_Read_classifier, cv::gpu::DeviceInfo, int) TEST_P(LBP_Read_classifier, Accuracy) { - cv::gpu::CascadeClassifier_GPU classifier; + cv::gpu::CascadeClassifier_GPU classifier; std::string classifierXmlPath = std::string(cvtest::TS::ptr()->get_data_path()) + "lbpcascade/lbpcascade_frontalface.xml"; ASSERT_TRUE(classifier.load(classifierXmlPath)); } -INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_Read_classifier, - testing::Combine(ALL_DEVICES, testing::Values(0))); +INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_Read_classifier, + testing::Combine(ALL_DEVICES, testing::Values(0))); PARAM_TEST_CASE(LBP_classify, cv::gpu::DeviceInfo, int) @@ -344,7 +345,7 @@ TEST_P(LBP_classify, Accuracy) for (; it != rects.end(); ++it) cv::rectangle(markedImage, *it, CV_RGB(0, 0, 255)); - cv::gpu::CascadeClassifier_GPU gpuClassifier; + cv::gpu::CascadeClassifier_GPU gpuClassifier; ASSERT_TRUE(gpuClassifier.load(classifierXmlPath)); cv::gpu::GpuMat gpu_rects; @@ -352,23 +353,25 @@ TEST_P(LBP_classify, Accuracy) int count = gpuClassifier.detectMultiScale(tested, gpu_rects); cv::Mat downloaded(gpu_rects); - const cv::Rect* faces = downloaded.ptr(); + const cv::Rect* faces = downloaded.ptr(); for (int i = 0; i < count; i++) { cv::Rect r = faces[i]; #if defined (LOG_CASCADE_STATISTIC) - std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl; + std::cout << r.x << " " << r.y << " " << r.width << " " << r.height << std::endl; cv::rectangle(markedImage, r , CV_RGB(255, 0, 0)); -#endif +#endif } #if defined (LOG_CASCADE_STATISTIC) - cv::imshow("Res", markedImage); cv::waitKey(); + cv::imshow("Res", markedImage); cv::waitKey(); #endif } INSTANTIATE_TEST_CASE_P(GPU_ObjDetect, LBP_classify, - testing::Combine(ALL_DEVICES, testing::Values(0))); + testing::Combine(ALL_DEVICES, testing::Values(0))); } // namespace + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_precomp.cpp b/modules/gpu/test/test_precomp.cpp index dfa7246129..34acf2ae91 100644 --- a/modules/gpu/test/test_precomp.cpp +++ b/modules/gpu/test/test_precomp.cpp @@ -39,4 +39,4 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" diff --git a/modules/gpu/test/test_precomp.hpp b/modules/gpu/test/test_precomp.hpp index 753367cce8..f6933d51c6 100644 --- a/modules/gpu/test/test_precomp.hpp +++ b/modules/gpu/test/test_precomp.hpp @@ -59,6 +59,7 @@ #include #include "cvconfig.h" + #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/calib3d/calib3d.hpp" @@ -72,6 +73,7 @@ #include "utility.hpp" #include "interpolation.hpp" +#include "main_test_nvidia.h" #ifdef HAVE_CUDA #include diff --git a/modules/gpu/test/test_pyramids.cpp b/modules/gpu/test/test_pyramids.cpp index d0bf37b376..1abd7841ef 100644 --- a/modules/gpu/test/test_pyramids.cpp +++ b/modules/gpu/test/test_pyramids.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_remap.cpp b/modules/gpu/test/test_remap.cpp index c61a899142..b83e5db070 100644 --- a/modules/gpu/test/test_remap.cpp +++ b/modules/gpu/test/test_remap.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_resize.cpp b/modules/gpu/test/test_resize.cpp index 7a7aacab17..73b8d9fab0 100644 --- a/modules/gpu/test/test_resize.cpp +++ b/modules/gpu/test/test_resize.cpp @@ -39,8 +39,7 @@ // //M*/ -#include "precomp.hpp" -#include +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_threshold.cpp b/modules/gpu/test/test_threshold.cpp index cb31c643b0..e878e6dfa5 100644 --- a/modules/gpu/test/test_threshold.cpp +++ b/modules/gpu/test/test_threshold.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_video.cpp b/modules/gpu/test/test_video.cpp index 0ee66ba522..ca9442d697 100644 --- a/modules/gpu/test/test_video.cpp +++ b/modules/gpu/test/test_video.cpp @@ -39,7 +39,9 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" + +#ifdef HAVE_CUDA //#define DUMP @@ -865,3 +867,5 @@ TEST_P(VideoReader, Regression) INSTANTIATE_TEST_CASE_P(GPU_Video, VideoReader, testing::Combine( ALL_DEVICES, testing::Values(std::string("768x576.avi"), std::string("1920x1080.avi")))); + +#endif // HAVE_CUDA diff --git a/modules/gpu/test/test_warp_affine.cpp b/modules/gpu/test/test_warp_affine.cpp index 262937fbeb..c81fef354e 100644 --- a/modules/gpu/test/test_warp_affine.cpp +++ b/modules/gpu/test/test_warp_affine.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/test_warp_perspective.cpp b/modules/gpu/test/test_warp_perspective.cpp index f143170150..83c170f2ef 100644 --- a/modules/gpu/test/test_warp_perspective.cpp +++ b/modules/gpu/test/test_warp_perspective.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" #ifdef HAVE_CUDA diff --git a/modules/gpu/test/utility.cpp b/modules/gpu/test/utility.cpp index 148c9d202b..cf3b0fc8cb 100644 --- a/modules/gpu/test/utility.cpp +++ b/modules/gpu/test/utility.cpp @@ -39,7 +39,7 @@ // //M*/ -#include "precomp.hpp" +#include "test_precomp.hpp" using namespace std; using namespace cv; @@ -182,105 +182,6 @@ void DeviceManager::loadAll() } } -class DevicesGenerator : public ParamGeneratorInterface -{ -public: - ~DevicesGenerator(); - - ParamIteratorInterface* Begin() const; - ParamIteratorInterface* End() const; - -private: - class Iterator : public ParamIteratorInterface - { - public: - Iterator(const ParamGeneratorInterface* base, vector::const_iterator iterator); - - virtual ~Iterator(); - - virtual const ParamGeneratorInterface* BaseGenerator() const; - - virtual void Advance(); - - virtual ParamIteratorInterface* Clone() const; - - virtual const DeviceInfo* Current() const; - - virtual bool Equals(const ParamIteratorInterface& other) const; - - private: - Iterator(const Iterator& other); - - const ParamGeneratorInterface* const base_; - vector::const_iterator iterator_; - - mutable DeviceInfo value_; - }; -}; - -DevicesGenerator::~DevicesGenerator() -{ -} - -ParamIteratorInterface* DevicesGenerator::Begin() const -{ - return new Iterator(this, DeviceManager::instance().values().begin()); -} - -ParamIteratorInterface* DevicesGenerator::End() const -{ - return new Iterator(this, DeviceManager::instance().values().end()); -} - -DevicesGenerator::Iterator::Iterator(const ParamGeneratorInterface* base, vector::const_iterator iterator) - : base_(base), iterator_(iterator) -{ -} - -DevicesGenerator::Iterator::~Iterator() -{ -} - -const ParamGeneratorInterface* DevicesGenerator::Iterator::BaseGenerator() const -{ - return base_; -} - -void DevicesGenerator::Iterator::Advance() -{ - ++iterator_; -} - -ParamIteratorInterface* DevicesGenerator::Iterator::Clone() const -{ - return new Iterator(*this); -} - -const DeviceInfo* DevicesGenerator::Iterator::Current() const -{ - value_ = *iterator_; - return &value_; -} - -bool DevicesGenerator::Iterator::Equals(const ParamIteratorInterface& other) const -{ - GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) - << "The program attempted to compare iterators " - << "from different generators." << endl; - - return iterator_ == CheckedDowncastToActualType(&other)->iterator_; -} - -DevicesGenerator::Iterator::Iterator(const Iterator& other) : - ParamIteratorInterface(), base_(other.base_), iterator_(other.iterator_) -{ -} - -ParamGenerator DevicesGenerator_() -{ - return ParamGenerator(new DevicesGenerator); -} - ////////////////////////////////////////////////////////////////////// // Additional assertion diff --git a/modules/gpu/test/utility.hpp b/modules/gpu/test/utility.hpp index b36f177f6e..f509b786ad 100644 --- a/modules/gpu/test/utility.hpp +++ b/modules/gpu/test/utility.hpp @@ -94,9 +94,7 @@ private: std::vector devices_; }; -testing::internal::ParamGenerator DevicesGenerator_(); - -#define ALL_DEVICES DevicesGenerator_() +#define ALL_DEVICES testing::ValuesIn(DeviceManager::instance().values()) ////////////////////////////////////////////////////////////////////// // Additional assertion From 1e4012079d84b5f1742f539f2eae3a85254a1409 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 13:26:51 +0400 Subject: [PATCH 31/58] removed logging from test --- modules/gpu/test/test_labeling.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/modules/gpu/test/test_labeling.cpp b/modules/gpu/test/test_labeling.cpp index 46f6f4eeba..d32dbc1a2f 100644 --- a/modules/gpu/test/test_labeling.cpp +++ b/modules/gpu/test/test_labeling.cpp @@ -139,10 +139,9 @@ namespace { if ( (_labels.at(j,i) == gpu.at(j,i + 1)) && (diff.at(j, i) != diff.at(j,i + 1))) { outliers++; - // std::cout << j << " " << i << " " << _labels.at(j,i) << " " << gpu.at(j,i + 1) << " " << diff.at(j, i) << " " << diff.at(j,i + 1) << std::endl; } } - ASSERT_FALSE(outliers); + ASSERT_TRUE(outliers < gpu.cols + gpu.rows); } cv::Mat image; @@ -162,7 +161,7 @@ struct Labeling : testing::TestWithParam cv::Mat loat_image() { - return cv::imread(std::string( cvtest::TS::ptr()->get_data_path() ) + "labeling/IMG_0727.JPG"); + return cv::imread(std::string( cvtest::TS::ptr()->get_data_path() ) + "labeling/label.png"); } }; @@ -189,10 +188,6 @@ TEST_P(Labeling, ConnectedComponents) ASSERT_NO_THROW(cv::gpu::labelComponents(mask, components)); host.checkCorrectness(cv::Mat(components)); - cv::imshow("test", image); - cv::waitKey(0); - cv::imshow("test", host._labels); - cv::waitKey(0); } INSTANTIATE_TEST_CASE_P(ConnectedComponents, Labeling, ALL_DEVICES); From c26d543e1e5007e5566f9fd8d4d3a121c97005d3 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Mon, 20 Aug 2012 16:03:01 +0400 Subject: [PATCH 32/58] gpu::HoughLines : minor code improvements --- modules/gpu/include/opencv2/gpu/gpu.hpp | 1 + modules/gpu/perf/perf_imgproc.cpp | 2 +- modules/gpu/src/cuda/hough.cu | 123 ++++++++++++------------ modules/gpu/src/hough.cpp | 44 +++++---- modules/gpu/test/test_imgproc.cpp | 74 +++++++------- 5 files changed, 128 insertions(+), 116 deletions(-) diff --git a/modules/gpu/include/opencv2/gpu/gpu.hpp b/modules/gpu/include/opencv2/gpu/gpu.hpp index a7f0ab32d9..c6f2e1141e 100644 --- a/modules/gpu/include/opencv2/gpu/gpu.hpp +++ b/modules/gpu/include/opencv2/gpu/gpu.hpp @@ -820,6 +820,7 @@ private: int nLayers_; }; +//! HoughLines CV_EXPORTS void HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort = false, int maxLines = 4096); CV_EXPORTS void HoughLines(const GpuMat& src, GpuMat& lines, GpuMat& accum, GpuMat& buf, float rho, float theta, int threshold, bool doSort = false, int maxLines = 4096); CV_EXPORTS void HoughLinesTransform(const GpuMat& src, GpuMat& accum, GpuMat& buf, float rho, float theta); diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 9104892db4..979aaa3723 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -1626,7 +1626,7 @@ PERF_TEST_P(Sz_DoSort, ImgProc_HoughLines, Combine(GPU_TYPICAL_MAT_SIZES, Bool() cv::Mat src(size, CV_8UC1, cv::Scalar::all(0)); - const int numLines = rng.uniform(500, 2000); + const int numLines = rng.uniform(100, 300); for (int i = 0; i < numLines; ++i) { cv::Point p1(rng.uniform(0, src.cols), rng.uniform(0, src.rows)); diff --git a/modules/gpu/src/cuda/hough.cu b/modules/gpu/src/cuda/hough.cu index 82bd04caad..66433aba7e 100644 --- a/modules/gpu/src/cuda/hough.cu +++ b/modules/gpu/src/cuda/hough.cu @@ -59,7 +59,7 @@ namespace cv { namespace gpu { namespace device { __shared__ int s_queues[4][32 * PIXELS_PER_THREAD]; __shared__ int s_qsize[4]; - __shared__ int s_start[4]; + __shared__ int s_globStart[4]; const int x = blockIdx.x * blockDim.x * PIXELS_PER_THREAD + threadIdx.x; const int y = blockIdx.y * blockDim.y + threadIdx.y; @@ -73,9 +73,10 @@ namespace cv { namespace gpu { namespace device __syncthreads(); // fill the queue + const uchar* srcRow = src.ptr(y); for (int i = 0, xx = x; i < PIXELS_PER_THREAD && xx < src.cols; ++i, xx += blockDim.x) { - if (src(y, xx)) + if (srcRow[xx]) { const unsigned int val = (y << 16) | xx; const int qidx = Emulation::smem::atomicAdd(&s_qsize[threadIdx.y], 1); @@ -89,36 +90,34 @@ namespace cv { namespace gpu { namespace device if (threadIdx.x == 0 && threadIdx.y == 0) { // find how many items are stored in each list - int total_size = 0; + int totalSize = 0; for (int i = 0; i < blockDim.y; ++i) { - s_start[i] = total_size; - total_size += s_qsize[i]; + s_globStart[i] = totalSize; + totalSize += s_qsize[i]; } // calculate the offset in the global list - const int global_offset = atomicAdd(&g_counter, total_size); + const int globalOffset = atomicAdd(&g_counter, totalSize); for (int i = 0; i < blockDim.y; ++i) - s_start[i] += global_offset; + s_globStart[i] += globalOffset; } __syncthreads(); // copy local queues to global queue const int qsize = s_qsize[threadIdx.y]; - for(int i = threadIdx.x; i < qsize; i += blockDim.x) - { - const unsigned int val = s_queues[threadIdx.y][i]; - list[s_start[threadIdx.y] + i] = val; - } + int gidx = s_globStart[threadIdx.y] + threadIdx.x; + for(int i = threadIdx.x; i < qsize; i += blockDim.x, gidx += blockDim.x) + list[gidx] = s_queues[threadIdx.y][i]; } int buildPointList_gpu(DevMem2Db src, unsigned int* list) { - void* counter_ptr; - cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, g_counter) ); + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(int)) ); + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); const dim3 block(32, 4); const dim3 grid(divUp(src.cols, block.x * PIXELS_PER_THREAD), divUp(src.rows, block.y)); @@ -130,10 +129,10 @@ namespace cv { namespace gpu { namespace device cudaSafeCall( cudaDeviceSynchronize() ); - int total_count; - cudaSafeCall( cudaMemcpy(&total_count, counter_ptr, sizeof(int), cudaMemcpyDeviceToHost) ); + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - return total_count; + return totalCount; } //////////////////////////////////////////////////////////////////////// @@ -144,24 +143,26 @@ namespace cv { namespace gpu { namespace device const int n = blockIdx.x; const float ang = n * theta; - float sin_ang; - float cos_ang; - sincosf(ang, &sin_ang, &cos_ang); + float sinVal; + float cosVal; + sincosf(ang, &sinVal, &cosVal); + sinVal *= irho; + cosVal *= irho; - const float tabSin = sin_ang * irho; - const float tabCos = cos_ang * irho; + const int shift = (numrho - 1) / 2; + int* accumRow = accum.ptr(n + 1); for (int i = threadIdx.x; i < count; i += blockDim.x) { - const unsigned int qvalue = list[i]; + const unsigned int val = list[i]; - const int x = (qvalue & 0x0000FFFF); - const int y = (qvalue >> 16) & 0x0000FFFF; + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; - int r = __float2int_rn(x * tabCos + y * tabSin); - r += (numrho - 1) / 2; + int r = __float2int_rn(x * cosVal + y * sinVal); + r += shift; - ::atomicAdd(accum.ptr(n + 1) + r + 1, 1); + ::atomicAdd(accumRow + r + 1, 1); } } @@ -177,30 +178,32 @@ namespace cv { namespace gpu { namespace device const int n = blockIdx.x; const float ang = n * theta; - float sin_ang; - float cos_ang; - sincosf(ang, &sin_ang, &cos_ang); + float sinVal; + float cosVal; + sincosf(ang, &sinVal, &cosVal); + sinVal *= irho; + cosVal *= irho; - const float tabSin = sin_ang * irho; - const float tabCos = cos_ang * irho; + const int shift = (numrho - 1) / 2; for (int i = threadIdx.x; i < count; i += blockDim.x) { - const unsigned int qvalue = list[i]; + const unsigned int val = list[i]; - const int x = (qvalue & 0x0000FFFF); - const int y = (qvalue >> 16) & 0x0000FFFF; + const int x = (val & 0xFFFF); + const int y = (val >> 16) & 0xFFFF; - int r = __float2int_rn(x * tabCos + y * tabSin); - r += (numrho - 1) / 2; + int r = __float2int_rn(x * cosVal + y * sinVal); + r += shift; Emulation::smem::atomicAdd(&smem[r + 1], 1); } __syncthreads(); - for (int i = threadIdx.x; i < numrho; i += blockDim.x) - accum(n + 1, i) = smem[i]; + int* accumRow = accum.ptr(n + 1); + for (int i = threadIdx.x; i < numrho + 1; i += blockDim.x) + accumRow[i] = smem[i]; } void linesAccum_gpu(const unsigned int* list, int count, DevMem2Di accum, float rho, float theta, size_t sharedMemPerBlock, bool has20) @@ -225,21 +228,21 @@ namespace cv { namespace gpu { namespace device //////////////////////////////////////////////////////////////////////// // linesGetResult - __global__ void linesGetResult(const DevMem2Di accum, float2* out, int* votes, const int maxSize, const float threshold, const float theta, const float rho, const int numrho) + __global__ void linesGetResult(const DevMem2Di accum, float2* out, int* votes, const int maxSize, const float rho, const float theta, const float threshold, const int numrho) { __shared__ int smem[8][32]; - int r = blockIdx.x * (blockDim.x - 2) + threadIdx.x; - int n = blockIdx.y * (blockDim.y - 2) + threadIdx.y; + const int x = blockIdx.x * (blockDim.x - 2) + threadIdx.x; + const int y = blockIdx.y * (blockDim.y - 2) + threadIdx.y; - if (r >= accum.cols || n >= accum.rows) + if (x >= accum.cols || y >= accum.rows) return; - smem[threadIdx.y][threadIdx.x] = accum(n, r); + smem[threadIdx.y][threadIdx.x] = accum(y, x); __syncthreads(); - r -= 1; - n -= 1; + const int r = x - 1; + const int n = y - 1; if (threadIdx.x == 0 || threadIdx.x == blockDim.x - 1 || threadIdx.y == 0 || threadIdx.y == blockDim.y - 1 || r >= accum.cols - 2 || n >= accum.rows - 2) return; @@ -264,32 +267,32 @@ namespace cv { namespace gpu { namespace device int linesGetResult_gpu(DevMem2Di accum, float2* out, int* votes, int maxSize, float rho, float theta, float threshold, bool doSort) { - void* counter_ptr; - cudaSafeCall( cudaGetSymbolAddress(&counter_ptr, g_counter) ); + void* counterPtr; + cudaSafeCall( cudaGetSymbolAddress(&counterPtr, g_counter) ); - cudaSafeCall( cudaMemset(counter_ptr, 0, sizeof(int)) ); + cudaSafeCall( cudaMemset(counterPtr, 0, sizeof(int)) ); const dim3 block(32, 8); const dim3 grid(divUp(accum.cols, block.x - 2), divUp(accum.rows, block.y - 2)); - linesGetResult<<>>(accum, out, votes, maxSize, threshold, theta, rho, accum.cols - 2); + linesGetResult<<>>(accum, out, votes, maxSize, rho, theta, threshold, accum.cols - 2); cudaSafeCall( cudaGetLastError() ); cudaSafeCall( cudaDeviceSynchronize() ); - int total_count; - cudaSafeCall( cudaMemcpy(&total_count, counter_ptr, sizeof(int), cudaMemcpyDeviceToHost) ); + int totalCount; + cudaSafeCall( cudaMemcpy(&totalCount, counterPtr, sizeof(int), cudaMemcpyDeviceToHost) ); - total_count = ::min(total_count, maxSize); + totalCount = ::min(totalCount, maxSize); - if (doSort && total_count > 0) + if (doSort && totalCount > 0) { - thrust::device_ptr out_ptr(out); - thrust::device_ptr votes_ptr(votes); - thrust::sort_by_key(votes_ptr, votes_ptr + total_count, out_ptr, thrust::greater()); + thrust::device_ptr outPtr(out); + thrust::device_ptr votesPtr(votes); + thrust::sort_by_key(votesPtr, votesPtr + totalCount, outPtr, thrust::greater()); } - return total_count; + return totalCount; } } }}} diff --git a/modules/gpu/src/hough.cpp b/modules/gpu/src/hough.cpp index ba61ad78c8..3b683ff40c 100644 --- a/modules/gpu/src/hough.cpp +++ b/modules/gpu/src/hough.cpp @@ -57,11 +57,27 @@ namespace cv { namespace gpu { namespace device namespace hough { int buildPointList_gpu(DevMem2Db src, unsigned int* list); + void linesAccum_gpu(const unsigned int* list, int count, DevMem2Di accum, float rho, float theta, size_t sharedMemPerBlock, bool has20); int linesGetResult_gpu(DevMem2Di accum, float2* out, int* votes, int maxSize, float rho, float theta, float threshold, bool doSort); } }}} +////////////////////////////////////////////////////////// +// HoughLines + +void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) +{ + GpuMat accum, buf; + HoughLines(src, lines, accum, buf, rho, theta, threshold, doSort, maxLines); +} + +void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, GpuMat& accum, GpuMat& buf, float rho, float theta, int threshold, bool doSort, int maxLines) +{ + HoughLinesTransform(src, accum, buf, rho, theta); + HoughLinesGet(accum, lines, rho, theta, threshold, doSort, maxLines); +} + void cv::gpu::HoughLinesTransform(const GpuMat& src, GpuMat& accum, GpuMat& buf, float rho, float theta) { using namespace cv::gpu::device::hough; @@ -80,23 +96,23 @@ void cv::gpu::HoughLinesTransform(const GpuMat& src, GpuMat& accum, GpuMat& buf, CV_Assert(numangle > 0 && numrho > 0); ensureSizeIsEnough(numangle + 2, numrho + 2, CV_32SC1, accum); - accum.setTo(cv::Scalar::all(0)); + accum.setTo(Scalar::all(0)); - cv::gpu::DeviceInfo devInfo; + DeviceInfo devInfo; if (count > 0) - linesAccum_gpu(buf.ptr(), count, accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(cv::gpu::FEATURE_SET_COMPUTE_20)); + linesAccum_gpu(buf.ptr(), count, accum, rho, theta, devInfo.sharedMemPerBlock(), devInfo.supports(FEATURE_SET_COMPUTE_20)); } void cv::gpu::HoughLinesGet(const GpuMat& accum, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) { - using namespace cv::gpu::device; + using namespace cv::gpu::device::hough; CV_Assert(accum.type() == CV_32SC1); ensureSizeIsEnough(2, maxLines, CV_32FC2, lines); - int count = hough::linesGetResult_gpu(accum, lines.ptr(0), lines.ptr(1), maxLines, rho, theta, threshold, doSort); + int count = linesGetResult_gpu(accum, lines.ptr(0), lines.ptr(1), maxLines, rho, theta, threshold, doSort); if (count > 0) lines.cols = count; @@ -104,18 +120,6 @@ void cv::gpu::HoughLinesGet(const GpuMat& accum, GpuMat& lines, float rho, float lines.release(); } -void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, float rho, float theta, int threshold, bool doSort, int maxLines) -{ - cv::gpu::GpuMat accum, buf; - HoughLines(src, lines, accum, buf, rho, theta, threshold, doSort, maxLines); -} - -void cv::gpu::HoughLines(const GpuMat& src, GpuMat& lines, GpuMat& accum, GpuMat& buf, float rho, float theta, int threshold, bool doSort, int maxLines) -{ - HoughLinesTransform(src, accum, buf, rho, theta); - HoughLinesGet(accum, lines, rho, theta, threshold, doSort, maxLines); -} - void cv::gpu::HoughLinesDownload(const GpuMat& d_lines, OutputArray h_lines_, OutputArray h_votes_) { if (d_lines.empty()) @@ -129,14 +133,14 @@ void cv::gpu::HoughLinesDownload(const GpuMat& d_lines, OutputArray h_lines_, Ou CV_Assert(d_lines.rows == 2 && d_lines.type() == CV_32FC2); h_lines_.create(1, d_lines.cols, CV_32FC2); - cv::Mat h_lines = h_lines_.getMat(); + Mat h_lines = h_lines_.getMat(); d_lines.row(0).download(h_lines); if (h_votes_.needed()) { h_votes_.create(1, d_lines.cols, CV_32SC1); - cv::Mat h_votes = h_votes_.getMat(); - cv::gpu::GpuMat d_votes(1, d_lines.cols, CV_32SC1, const_cast(d_lines.ptr(1))); + Mat h_votes = h_votes_.getMat(); + GpuMat d_votes(1, d_lines.cols, CV_32SC1, const_cast(d_lines.ptr(1))); d_votes.download(h_votes); } } diff --git a/modules/gpu/test/test_imgproc.cpp b/modules/gpu/test/test_imgproc.cpp index 4f402da16d..06662d8d2e 100644 --- a/modules/gpu/test/test_imgproc.cpp +++ b/modules/gpu/test/test_imgproc.cpp @@ -1129,63 +1129,67 @@ INSTANTIATE_TEST_CASE_P(GPU_ImgProc, CornerMinEigen, testing::Combine( /////////////////////////////////////////////////////////////////////////////////////////////////////// // HoughLines -PARAM_TEST_CASE(HoughLines, cv::gpu::DeviceInfo, std::string) +PARAM_TEST_CASE(HoughLines, cv::gpu::DeviceInfo, cv::Size, UseRoi) { -}; + void generateLines(cv::Mat& img) + { + img.setTo(cv::Scalar::all(0)); -void drawLines(cv::Mat& dst, const std::vector& lines) -{ - for (size_t i = 0; i < lines.size(); ++i) + cv::line(img, cv::Point(20, 0), cv::Point(20, img.rows), cv::Scalar::all(255)); + cv::line(img, cv::Point(0, 50), cv::Point(img.cols, 50), cv::Scalar::all(255)); + cv::line(img, cv::Point(0, 0), cv::Point(img.cols, img.rows), cv::Scalar::all(255)); + cv::line(img, cv::Point(img.cols, 0), cv::Point(0, img.rows), cv::Scalar::all(255)); + } + + void drawLines(cv::Mat& dst, const std::vector& lines) { - float rho = lines[i][0], theta = lines[i][1]; - cv::Point pt1, pt2; - double a = std::cos(theta), b = std::sin(theta); - double x0 = a*rho, y0 = b*rho; - pt1.x = cvRound(x0 + 1000*(-b)); - pt1.y = cvRound(y0 + 1000*(a)); - pt2.x = cvRound(x0 - 1000*(-b)); - pt2.y = cvRound(y0 - 1000*(a)); - cv::line(dst, pt1, pt2, cv::Scalar::all(255)); + dst.setTo(cv::Scalar::all(0)); + + for (size_t i = 0; i < lines.size(); ++i) + { + float rho = lines[i][0], theta = lines[i][1]; + cv::Point pt1, pt2; + double a = std::cos(theta), b = std::sin(theta); + double x0 = a*rho, y0 = b*rho; + pt1.x = cvRound(x0 + 1000*(-b)); + pt1.y = cvRound(y0 + 1000*(a)); + pt2.x = cvRound(x0 - 1000*(-b)); + pt2.y = cvRound(y0 - 1000*(a)); + cv::line(dst, pt1, pt2, cv::Scalar::all(255)); + } } -} +}; TEST_P(HoughLines, Accuracy) { const cv::gpu::DeviceInfo devInfo = GET_PARAM(0); cv::gpu::setDevice(devInfo.deviceID()); - const std::string fileName = GET_PARAM(1); + const cv::Size size = GET_PARAM(1); + const bool useRoi = GET_PARAM(2); const float rho = 1.0f; - const float theta = static_cast(CV_PI / 180); - const int threshold = 50; - - cv::Mat img = readImage(fileName, cv::IMREAD_GRAYSCALE); - ASSERT_FALSE(img.empty()); + const float theta = 1.5f * CV_PI / 180.0f; + const int threshold = 100; - cv::Mat edges; - cv::Canny(img, edges, 50, 200); + cv::Mat src(size, CV_8UC1); + generateLines(src); cv::gpu::GpuMat d_lines; - cv::gpu::HoughLines(loadMat(edges), d_lines, rho, theta, threshold); + cv::gpu::HoughLines(loadMat(src, useRoi), d_lines, rho, theta, threshold); + std::vector lines; cv::gpu::HoughLinesDownload(d_lines, lines); - cv::Mat dst(img.size(), CV_8UC1, cv::Scalar::all(0)); - drawLines(dst, lines); - std::vector lines_gold; - cv::HoughLines(edges, lines_gold, rho, theta, threshold); - cv::Mat dst_gold(img.size(), CV_8UC1, cv::Scalar::all(0)); - drawLines(dst_gold, lines_gold); + cv::Mat dst(size, CV_8UC1); + drawLines(dst, lines); - ASSERT_MAT_NEAR(dst_gold, dst, 0.0); + ASSERT_MAT_NEAR(src, dst, 0.0); } INSTANTIATE_TEST_CASE_P(GPU_ImgProc, HoughLines, testing::Combine( ALL_DEVICES, - testing::Values(std::string("../cv/shared/pic1.png"), - std::string("../cv/shared/pic3.png"), - std::string("../cv/shared/pic5.png"), - std::string("../cv/shared/pic6.png")))); + DIFFERENT_SIZES, + WHOLE_SUBMAT)); } // namespace From 8efb84b5a4cc98c6e3b1266923e744e6032ed52f Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Mon, 20 Aug 2012 19:36:36 +0400 Subject: [PATCH 33/58] added ios camera support in highgui. turned on optimization in opencv2.framework (ticket #2232) --- ios/cmake/Modules/Platform/iOS.cmake | 2 + modules/core/include/opencv2/core/core.hpp | 2 +- modules/highgui/CMakeLists.txt | 6 + .../highgui/include/opencv2/highgui/cap_ios.h | 160 +++++ .../highgui/src/cap_ios_abstract_camera.mm | 408 +++++++++++ modules/highgui/src/cap_ios_photo_camera.mm | 165 +++++ modules/highgui/src/cap_ios_video_camera.mm | 656 ++++++++++++++++++ 7 files changed, 1398 insertions(+), 1 deletion(-) create mode 100644 modules/highgui/include/opencv2/highgui/cap_ios.h create mode 100644 modules/highgui/src/cap_ios_abstract_camera.mm create mode 100644 modules/highgui/src/cap_ios_photo_camera.mm create mode 100644 modules/highgui/src/cap_ios_video_camera.mm diff --git a/ios/cmake/Modules/Platform/iOS.cmake b/ios/cmake/Modules/Platform/iOS.cmake index c657b800fb..e8389798bb 100644 --- a/ios/cmake/Modules/Platform/iOS.cmake +++ b/ios/cmake/Modules/Platform/iOS.cmake @@ -42,6 +42,8 @@ set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") set (CMAKE_C_FLAGS "") set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden") +set (CMAKE_CXX_FLAGS_RELEASE "-O3 -fomit-frame-pointer") + if (HAVE_FLAG_SEARCH_PATHS_FIRST) set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index fbaf13721f..5fd9272135 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -440,7 +440,7 @@ template class CV_EXPORTS Matx { public: typedef _Tp value_type; - typedef Matx<_Tp, MIN(m, n), 1> diag_type; + typedef Matx<_Tp, (m < n ? m : n), 1> diag_type; typedef Matx<_Tp, m, n> mat_type; enum { depth = DataDepth<_Tp>::value, rows = m, cols = n, channels = rows*cols, type = CV_MAKETYPE(depth, channels) }; diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 6b9341b32e..d53c17c40f 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -193,6 +193,12 @@ elseif(APPLE) endif() endif() +if(IOS) + add_definitions(-DHAVE_IOS=1) + list(APPEND highgui_srcs src/cap_ios_abstract_camera.mm src/cap_ios_photo_camera.mm src/cap_ios_video_camera.mm) + list(APPEND HIGHGUI_LIBRARIES "-framework Accelerate" "-framework AVFoundation" "-framework CoreGraphics" "-framework CoreImage" "-framework CoreMedia" "-framework CoreVideo" "-framework QuartzCore" "-framework AssetsLibrary") +endif() + if(WIN32) link_directories("${OpenCV_SOURCE_DIR}/3rdparty/lib") # for ffmpeg wrapper only include_directories(AFTER SYSTEM "${OpenCV_SOURCE_DIR}/3rdparty/include") # for directshow in VS2005 and multi-monitor support on MinGW diff --git a/modules/highgui/include/opencv2/highgui/cap_ios.h b/modules/highgui/include/opencv2/highgui/cap_ios.h new file mode 100644 index 0000000000..cc6668ace0 --- /dev/null +++ b/modules/highgui/include/opencv2/highgui/cap_ios.h @@ -0,0 +1,160 @@ +/* + * cap_ios.h + * For iOS video I/O + * by Eduard Feicho on 29/07/12 + * Copyright 2012. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import +#import +#import +#import +#include "opencv2/core/core.hpp" + +/////////////////////////////////////// CvAbstractCamera ///////////////////////////////////// + +@class CvAbstractCamera; + +@interface CvAbstractCamera : NSObject +{ + AVCaptureSession* captureSession; + AVCaptureConnection* videoCaptureConnection; + AVCaptureVideoPreviewLayer *captureVideoPreviewLayer; + + UIDeviceOrientation currentDeviceOrientation; + + BOOL cameraAvailable; + BOOL captureSessionLoaded; + BOOL running; + BOOL useAVCaptureVideoPreviewLayer; + + AVCaptureDevicePosition defaultAVCaptureDevicePosition; + AVCaptureVideoOrientation defaultAVCaptureVideoOrientation; + NSString *const defaultAVCaptureSessionPreset; + + int defaultFPS; + + UIView* parentView; + + int imageWidth; + int imageHeight; +} + +@property (nonatomic, retain) AVCaptureSession* captureSession; +@property (nonatomic, retain) AVCaptureConnection* videoCaptureConnection; + +@property (nonatomic, readonly) BOOL running; +@property (nonatomic, readonly) BOOL captureSessionLoaded; + +@property (nonatomic, assign) int defaultFPS; +@property (nonatomic, assign) AVCaptureDevicePosition defaultAVCaptureDevicePosition; +@property (nonatomic, assign) AVCaptureVideoOrientation defaultAVCaptureVideoOrientation; +@property (nonatomic, assign) BOOL useAVCaptureVideoPreviewLayer; +@property (nonatomic, strong) NSString *const defaultAVCaptureSessionPreset; + +@property (nonatomic, assign) int imageWidth; +@property (nonatomic, assign) int imageHeight; + +@property (nonatomic, retain) UIView* parentView; + +- (void)pause; +- (void)start; +- (void)stop; +- (void)switchCameras; + +- (id)initWithParentView:(UIView*)parent; + +- (void)createCaptureOutput; +- (void)createVideoPreviewLayer; +- (void)updateOrientation; + + +@end + +///////////////////////////////// CvVideoCamera /////////////////////////////////////////// + +@class CvVideoCamera; + +@protocol CvVideoCameraDelegate + +#ifdef __cplusplus +// delegate method for processing image frames +- (void)processImage:(cv::Mat&)image; +#endif + +@end + +@interface CvVideoCamera : CvAbstractCamera +{ + AVCaptureVideoDataOutput *videoDataOutput; + + dispatch_queue_t videoDataOutputQueue; + CALayer *customPreviewLayer; + + BOOL grayscaleMode; + + BOOL recordVideo; + AVAssetWriterInput* recordAssetWriterInput; + AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor; + AVAssetWriter* recordAssetWriter; + +} + +@property (nonatomic, assign) id delegate; +@property (nonatomic, assign) BOOL grayscaleMode; + +@property (nonatomic, assign) BOOL recordVideo; +@property (nonatomic, retain) AVAssetWriterInput* recordAssetWriterInput; +@property (nonatomic, retain) AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor; +@property (nonatomic, retain) AVAssetWriter* recordAssetWriter; + +- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +- (void)layoutPreviewLayer; +- (void)saveVideo; + +@end + +///////////////////////////////// CvPhotoCamera /////////////////////////////////////////// + +@class CvPhotoCamera; + +@protocol CvPhotoCameraDelegate + +- (void)photoCamera:(CvPhotoCamera*)photoCamera capturedImage:(UIImage *)image; +- (void)photoCameraCancel:(CvPhotoCamera*)photoCamera; + +@end + +@interface CvPhotoCamera : CvAbstractCamera +{ + AVCaptureStillImageOutput *stillImageOutput; +} + +@property (nonatomic, assign) id delegate; + +- (void)takePicture; + +@end diff --git a/modules/highgui/src/cap_ios_abstract_camera.mm b/modules/highgui/src/cap_ios_abstract_camera.mm new file mode 100644 index 0000000000..45b53b018a --- /dev/null +++ b/modules/highgui/src/cap_ios_abstract_camera.mm @@ -0,0 +1,408 @@ +/* + * cap_ios_abstract_camera.mm + * For iOS video I/O + * by Eduard Feicho on 29/07/12 + * Copyright 2012. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#import "opencv2/highgui/cap_ios.h" +#include "precomp.hpp" + +#pragma mark - Private Interface + +@interface CvAbstractCamera () + +@property (nonatomic, retain) AVCaptureVideoPreviewLayer* captureVideoPreviewLayer; + +- (void)deviceOrientationDidChange:(NSNotification*)notification; +- (void)startCaptureSession; + +- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; + +- (void)updateSize; + +@end + + +#pragma mark - Implementation + + +@implementation CvAbstractCamera + + + +#pragma mark Public + +@synthesize imageWidth; +@synthesize imageHeight; + + +@synthesize defaultFPS; +@synthesize defaultAVCaptureDevicePosition; +@synthesize defaultAVCaptureVideoOrientation; +@synthesize defaultAVCaptureSessionPreset; + + + +@synthesize captureSession; +@synthesize captureVideoPreviewLayer; +@synthesize videoCaptureConnection; +@synthesize running; +@synthesize captureSessionLoaded; +@synthesize useAVCaptureVideoPreviewLayer; + +@synthesize parentView; + +#pragma mark - Constructors + +- (id)init; +{ + self = [super init]; + if (self) { + // react to device orientation notifications + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + currentDeviceOrientation = [[UIDevice currentDevice] orientation]; + + + // check if camera available + cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; + NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") ); + + running = NO; + + // set camera default configuration + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft; + self.defaultFPS = 15; + self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288; + + self.parentView = nil; + self.useAVCaptureVideoPreviewLayer = NO; + } + return self; +} + + + +- (id)initWithParentView:(UIView*)parent; +{ + self = [super init]; + if (self) { + // react to device orientation notifications + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deviceOrientationDidChange:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + currentDeviceOrientation = [[UIDevice currentDevice] orientation]; + + + // check if camera available + cameraAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]; + NSLog(@"camera available: %@", (cameraAvailable == YES ? @"YES" : @"NO") ); + + running = NO; + + // set camera default configuration + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + self.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationLandscapeLeft; + self.defaultFPS = 15; + self.defaultAVCaptureSessionPreset = AVCaptureSessionPreset640x480; + + self.parentView = parent; + self.useAVCaptureVideoPreviewLayer = YES; + } + return self; +} + + + +- (void)dealloc; +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; +} + + +#pragma mark - Public interface + + +- (void)start; +{ + if (![NSThread isMainThread]) { + NSLog(@"[Camera] Warning: Call start only from main thread"); + [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; + return; + } + + if (running == YES) { + return; + } + running = YES; + + // TOOD update image size data before actually starting (needed for recording) + [self updateSize]; + + if (cameraAvailable) { + [self startCaptureSession]; + } +} + + +- (void)pause; +{ + running = NO; + [self.captureSession stopRunning]; +} + + + +- (void)stop; +{ + running = NO; + + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; + + [self.captureSession stopRunning]; + self.captureSession = nil; + self.captureVideoPreviewLayer = nil; + self.videoCaptureConnection = nil; + captureSessionLoaded = NO; +} + + + +// use front/back camera +- (void)switchCameras; +{ + BOOL was_running = self.running; + if (was_running) { + [self stop]; + } + if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) { + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack; + } else { + self.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront; + } + if (was_running) { + [self start]; + } +} + + + +#pragma mark - Device Orientation Changes + + +- (void)deviceOrientationDidChange:(NSNotification*)notification +{ + UIDeviceOrientation orientation = [UIDevice currentDevice].orientation; + + switch (orientation) + { + case UIDeviceOrientationPortrait: + case UIDeviceOrientationPortraitUpsideDown: + case UIDeviceOrientationLandscapeLeft: + case UIDeviceOrientationLandscapeRight: + currentDeviceOrientation = orientation; + break; + + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + default: + break; + } + NSLog(@"deviceOrientationDidChange: %d", orientation); + + [self updateOrientation]; +} + + + +#pragma mark - Private Interface + +- (void)createCaptureSession; +{ + // set a av capture session preset + self.captureSession = [[AVCaptureSession alloc] init]; + if ([self.captureSession canSetSessionPreset:self.defaultAVCaptureSessionPreset]) { + [self.captureSession setSessionPreset:self.defaultAVCaptureSessionPreset]; + } else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPresetLow]) { + [self.captureSession setSessionPreset:AVCaptureSessionPresetLow]; + } else { + NSLog(@"[Camera] Error: could not set session preset"); + } +} + +- (void)createCaptureDevice; +{ + // setup the device + AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + [self setDesiredCameraPosition:self.defaultAVCaptureDevicePosition]; + NSLog(@"[Camera] device connected? %@", device.connected ? @"YES" : @"NO"); + NSLog(@"[Camera] device position %@", (device.position == AVCaptureDevicePositionBack) ? @"back" : @"front"); +} + + +- (void)createVideoPreviewLayer; +{ + self.captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession]; + + if ([self.captureVideoPreviewLayer isOrientationSupported]) { + [self.captureVideoPreviewLayer setOrientation:self.defaultAVCaptureVideoOrientation]; + } + + if (parentView != nil) { + self.captureVideoPreviewLayer.frame = self.parentView.bounds; + self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + [self.parentView.layer addSublayer:self.captureVideoPreviewLayer]; + } + NSLog(@"[Camera] created AVCaptureVideoPreviewLayer"); +} + + + + +- (void)setDesiredCameraPosition:(AVCaptureDevicePosition)desiredPosition; +{ + for (AVCaptureDevice *device in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) { + if ([device position] == desiredPosition) { + [self.captureSession beginConfiguration]; + + NSError* error; + AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; + if (!input) { + NSLog(@"error creating input %@", [error localizedDescription]); + } + + // support for autofocus + if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { + NSError *error = nil; + if ([device lockForConfiguration:&error]) { + device.focusMode = AVCaptureFocusModeContinuousAutoFocus; + [device unlockForConfiguration]; + } else { + NSLog(@"unable to lock device for autofocos configuration %@", [error localizedDescription]); + } + } + [self.captureSession addInput:input]; + + for (AVCaptureInput *oldInput in self.captureSession.inputs) { + [self.captureSession removeInput:oldInput]; + } + [self.captureSession addInput:input]; + [self.captureSession commitConfiguration]; + + break; + } + } +} + + + +- (void)startCaptureSession +{ + if (!cameraAvailable) { + return; + } + + if (self.captureSessionLoaded == NO) { + [self createCaptureSession]; + [self createCaptureDevice]; + [self createCaptureOutput]; + + // setup preview layer + if (self.useAVCaptureVideoPreviewLayer) { + [self createVideoPreviewLayer]; + } else { + [self createCustomVideoPreview]; + } + + captureSessionLoaded = YES; + } + + [self.captureSession startRunning]; +} + + +- (void)createCaptureOutput; +{ + [NSException raise:NSInternalInconsistencyException + format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]; +} + +- (void)createCustomVideoPreview; +{ + [NSException raise:NSInternalInconsistencyException + format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]; +} + +- (void)updateOrientation; +{ + // nothing to do here +} + + +- (void)updateSize; +{ + if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetPhoto]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetHigh]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetMedium]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPresetLow]) { + //TODO: find the correct resolution + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset352x288]) { + self.imageWidth = 352; + self.imageHeight = 288; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset640x480]) { + self.imageWidth = 640; + self.imageHeight = 480; + } else if ([self.defaultAVCaptureSessionPreset isEqualToString:AVCaptureSessionPreset1280x720]) { + self.imageWidth = 1280; + self.imageHeight = 720; + } else { + self.imageWidth = 640; + self.imageHeight = 480; + } +} + +@end diff --git a/modules/highgui/src/cap_ios_photo_camera.mm b/modules/highgui/src/cap_ios_photo_camera.mm new file mode 100644 index 0000000000..51e5ce2299 --- /dev/null +++ b/modules/highgui/src/cap_ios_photo_camera.mm @@ -0,0 +1,165 @@ +/* + * cap_ios_photo_camera.mm + * For iOS video I/O + * by Eduard Feicho on 29/07/12 + * Copyright 2012. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#import "opencv2/highgui/cap_ios.h" +#include "precomp.hpp" + +#pragma mark - Private Interface + + +@interface CvPhotoCamera () + +@property (nonatomic, retain) AVCaptureStillImageOutput* stillImageOutput; + +@end + + + +#pragma mark - Implementation + + +@implementation CvPhotoCamera + + + +#pragma mark Public + +@synthesize stillImageOutput; +@synthesize delegate; + + +#pragma mark - Public interface + + +- (void)takePicture +{ + if (cameraAvailable == NO) { + return; + } + cameraAvailable = NO; + + + [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:self.videoCaptureConnection + completionHandler: + ^(CMSampleBufferRef imageSampleBuffer, NSError *error) + { + if (error == nil && imageSampleBuffer != NULL) + { + // TODO check + // NSNumber* imageOrientation = [UIImage cgImageOrientationForUIDeviceOrientation:currentDeviceOrientation]; + // CMSetAttachment(imageSampleBuffer, kCGImagePropertyOrientation, imageOrientation, 1); + + NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [self.captureSession stopRunning]; + + // Make sure we create objects on the main thread in the main context + UIImage* newImage = [UIImage imageWithData:jpegData]; + + //UIImageOrientation orientation = [newImage imageOrientation]; + + // TODO: only apply rotation, don't scale, since we can set this directly in the camera + /* + switch (orientation) { + case UIImageOrientationUp: + case UIImageOrientationDown: + newImage = [newImage imageWithAppliedRotationAndMaxSize:CGSizeMake(640.0, 480.0)]; + break; + case UIImageOrientationLeft: + case UIImageOrientationRight: + newImage = [newImage imageWithMaxSize:CGSizeMake(640.0, 480.0)]; + default: + break; + } + */ + + // We have captured the image, we can allow the user to take another picture + cameraAvailable = YES; + + NSLog(@"CvPhotoCamera captured image"); + if (self.delegate) { + [self.delegate photoCamera:self capturedImage:newImage]; + } + + [self.captureSession startRunning]; + }); + } + }]; + + +} + +- (void)stop; +{ + [super stop]; + self.stillImageOutput = nil; +} + + +#pragma mark - Private Interface + + +- (void)createStillImageOutput; +{ + // setup still image output with jpeg codec + self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; + NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil]; + [self.stillImageOutput setOutputSettings:outputSettings]; + [self.captureSession addOutput:self.stillImageOutput]; + + for (AVCaptureConnection *connection in self.stillImageOutput.connections) { + for (AVCaptureInputPort *port in [connection inputPorts]) { + if ([port.mediaType isEqual:AVMediaTypeVideo]) { + self.videoCaptureConnection = connection; + break; + } + } + if (self.videoCaptureConnection) { + break; + } + } + NSLog(@"[Camera] still image output created"); +} + + +- (void)createCaptureOutput; +{ + [self createStillImageOutput]; +} + +- (void)createCustomVideoPreview; +{ + //do nothing, always use AVCaptureVideoPreviewLayer +} + + +@end diff --git a/modules/highgui/src/cap_ios_video_camera.mm b/modules/highgui/src/cap_ios_video_camera.mm new file mode 100644 index 0000000000..bed17e1882 --- /dev/null +++ b/modules/highgui/src/cap_ios_video_camera.mm @@ -0,0 +1,656 @@ +/* + * cap_ios_video_camera.mm + * For iOS video I/O + * by Eduard Feicho on 29/07/12 + * Copyright 2012. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#import "opencv2/highgui/cap_ios.h" +#include "precomp.hpp" + +#import + + +static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; + +#pragma mark - Private Interface + + + + +@interface CvVideoCamera () + +- (void)createVideoDataOutput; +- (void)createVideoFileOutput; + + +@property (nonatomic, retain) CALayer *customPreviewLayer; +@property (nonatomic, retain) AVCaptureVideoDataOutput *videoDataOutput; + +@end + + + +#pragma mark - Implementation + + + +@implementation CvVideoCamera + + + + +@synthesize delegate; +@synthesize grayscaleMode; + +@synthesize customPreviewLayer; +@synthesize videoDataOutput; + +@synthesize recordVideo; +//@synthesize videoFileOutput; +@synthesize recordAssetWriterInput; +@synthesize recordPixelBufferAdaptor; +@synthesize recordAssetWriter; + + + +#pragma mark - Constructors + +- (id)initWithParentView:(UIView*)parent; +{ + self = [super initWithParentView:parent]; + if (self) { + self.useAVCaptureVideoPreviewLayer = NO; + self.recordVideo = NO; + } + return self; +} + + + +#pragma mark - Public interface + + +- (void)start; +{ + [super start]; + + if (self.recordVideo == YES) { +// [self.videoFileOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self]; + + NSError* error; + if ([[NSFileManager defaultManager] fileExistsAtPath:[self tempFileString]]) [[NSFileManager defaultManager] removeItemAtPath:[self tempFileString] error:&error]; + if (error == nil) { + NSLog(@"[Camera] Delete file %@", [self tempFileString]); + } + + + BOOL started = [self.recordAssetWriter startWriting]; + [self.recordAssetWriter startSessionAtSourceTime:kCMTimeZero]; + + NSLog(@"[Camera] Session started? %d", started); + + if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { + NSLog(@"AVAssetWriter status: unknown"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + NSLog(@"AVAssetWriter status: writing"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { + NSLog(@"AVAssetWriter status: completed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { + NSLog(@"AVAssetWriter status: failed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { + NSLog(@"AVAssetWriter status: cancelled"); + } + + if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { + NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error); + } else { + NSLog(@"[Camera] Recording started"); + } + } +} + + + +- (void)pause; +{ + [super pause]; + if (self.recordVideo == YES) { +// [self.videoFileOutput stopRecording]; + + + if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { + NSLog(@"AVAssetWriter status: unknown"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + NSLog(@"AVAssetWriter status: writing"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { + NSLog(@"AVAssetWriter status: completed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { + NSLog(@"AVAssetWriter status: failed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { + NSLog(@"AVAssetWriter status: cancelled"); + } + + if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + [self.recordAssetWriter finishWriting]; + NSLog(@"[Camera] recording stopped"); + } else { + NSLog(@"[Camera] Recording Error: asset writer status is not writing"); + } + } +} + + +- (void)stop; +{ + [super stop]; + + if (self.recordVideo == YES) { + NSLog(@"recording stop"); + if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { + NSLog(@"AVAssetWriter status: unknown"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + NSLog(@"AVAssetWriter status: writing"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { + NSLog(@"AVAssetWriter status: completed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { + NSLog(@"AVAssetWriter status: failed"); + } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { + NSLog(@"AVAssetWriter status: cancelled"); + } + + + if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { + [self.recordAssetWriter finishWriting]; + NSLog(@"[Camera] recording stopped"); + } else { + NSLog(@"[Camera] Recording Error: asset writer status is not writing"); + } + + self.recordAssetWriter = nil; + self.recordAssetWriterInput = nil; + self.recordPixelBufferAdaptor = nil; + } + + self.videoDataOutput = nil; + if (videoDataOutputQueue) { + dispatch_release(videoDataOutputQueue); + } + + [self.customPreviewLayer removeFromSuperlayer]; + self.customPreviewLayer = nil; +} + +// TODO fix +- (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; +{ + + NSLog(@"layout preview layer"); + if (self.parentView != nil) { + + CALayer* layer = self.customPreviewLayer; + CGRect bounds = self.customPreviewLayer.bounds; + int rotation_angle = 0; + bool flip_bounds = false; + + switch (interfaceOrientation) { + case UIInterfaceOrientationPortrait: + NSLog(@"to Portrait"); + rotation_angle = 270; + break; + case UIInterfaceOrientationPortraitUpsideDown: + rotation_angle = 90; + NSLog(@"to UpsideDown"); + break; + case UIInterfaceOrientationLandscapeLeft: + rotation_angle = 0; + NSLog(@"to LandscapeLeft"); + break; + case UIInterfaceOrientationLandscapeRight: + rotation_angle = 180; + NSLog(@"to LandscapeRight"); + break; + default: + break; // leave the layer in its last known orientation + } + + switch (defaultAVCaptureVideoOrientation) { + case AVCaptureVideoOrientationLandscapeRight: + rotation_angle += 180; + break; + case AVCaptureVideoOrientationPortraitUpsideDown: + rotation_angle += 270; + break; + case AVCaptureVideoOrientationPortrait: + rotation_angle += 90; + case AVCaptureVideoOrientationLandscapeLeft: + break; + default: + break; + } + rotation_angle = rotation_angle % 360; + + if (rotation_angle == 90 || rotation_angle == 270) { + flip_bounds = true; + } + + if (flip_bounds) { + NSLog(@"flip bounds"); + bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width); + } + + layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.); + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + + layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) ); + layer.bounds = bounds; + } + +} + +// TODO fix +- (void)layoutPreviewLayer; +{ + NSLog(@"layout preview layer"); + if (self.parentView != nil) { + + CALayer* layer = self.customPreviewLayer; + CGRect bounds = self.customPreviewLayer.bounds; + int rotation_angle = 0; + bool flip_bounds = false; + + switch (currentDeviceOrientation) { + case UIDeviceOrientationPortrait: + rotation_angle = 270; + break; + case UIDeviceOrientationPortraitUpsideDown: + rotation_angle = 90; + break; + case UIDeviceOrientationLandscapeLeft: + NSLog(@"left"); + rotation_angle = 180; + break; + case UIDeviceOrientationLandscapeRight: + NSLog(@"right"); + rotation_angle = 0; + break; + case UIDeviceOrientationFaceUp: + case UIDeviceOrientationFaceDown: + default: + break; // leave the layer in its last known orientation + } + + switch (defaultAVCaptureVideoOrientation) { + case AVCaptureVideoOrientationLandscapeRight: + rotation_angle += 180; + break; + case AVCaptureVideoOrientationPortraitUpsideDown: + rotation_angle += 270; + break; + case AVCaptureVideoOrientationPortrait: + rotation_angle += 90; + case AVCaptureVideoOrientationLandscapeLeft: + break; + default: + break; + } + rotation_angle = rotation_angle % 360; + + if (rotation_angle == 90 || rotation_angle == 270) { + flip_bounds = true; + } + + if (flip_bounds) { + NSLog(@"flip bounds"); + bounds = CGRectMake(0, 0, bounds.size.height, bounds.size.width); + } + + layer.position = CGPointMake(self.parentView.frame.size.width/2., self.parentView.frame.size.height/2.); + layer.affineTransform = CGAffineTransformMakeRotation( DegreesToRadians(rotation_angle) ); + layer.bounds = bounds; + } + +} + + + + +#pragma mark - Private Interface + + + +- (void)createVideoDataOutput; +{ + // Make a video data output + self.videoDataOutput = [AVCaptureVideoDataOutput new]; + + // In grayscale mode we want YUV (YpCbCr 4:2:0) so we can directly access the graylevel intensity values (Y component) + // In color mode we, BGRA format is used + OSType format = self.grayscaleMode ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; + + self.videoDataOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format] + forKey:(id)kCVPixelBufferPixelFormatTypeKey]; + + // discard if the data output queue is blocked (as we process the still image) + [self.videoDataOutput setAlwaysDiscardsLateVideoFrames:YES]; + + if ( [self.captureSession canAddOutput:self.videoDataOutput] ) { + [self.captureSession addOutput:self.videoDataOutput]; + } + [[self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo] setEnabled:YES]; + + + // set default FPS + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMinFrameDuration) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMinFrameDuration = CMTimeMake(1, self.defaultFPS); + } + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMaxFrameDuration) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMaxFrameDuration = CMTimeMake(1, self.defaultFPS); + } + + // set video mirroring for front camera (more intuitive) + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoMirroring) { + if (self.defaultAVCaptureDevicePosition == AVCaptureDevicePositionFront) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = YES; + } else { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO; + } + } + + // set default video orientation + if ([self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].supportsVideoOrientation) { + [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation = self.defaultAVCaptureVideoOrientation; + } + + + // create a custom preview layer + self.customPreviewLayer = [CALayer layer]; + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + [self layoutPreviewLayer]; + + // create a serial dispatch queue used for the sample buffer delegate as well as when a still image is captured + // a serial dispatch queue must be used to guarantee that video frames will be delivered in order + // see the header doc for setSampleBufferDelegate:queue: for more information + videoDataOutputQueue = dispatch_queue_create("VideoDataOutputQueue", DISPATCH_QUEUE_SERIAL); + [self.videoDataOutput setSampleBufferDelegate:self queue:videoDataOutputQueue]; + + + NSLog(@"[Camera] created AVCaptureVideoDataOutput at %d FPS", self.defaultFPS); +} + + + +- (void)createVideoFileOutput; +{ + /* + if (self.recordVideo == YES) { + self.videoFileOutput = [[AVCaptureMovieFileOutput alloc] init]; + if ( [self.captureSession canAddOutput:self.videoFileOutput] ) { + [self.captureSession addOutput:self.videoFileOutput]; + } + } + */ + + /* Video File Output in H.264, via AVAsserWriter */ + NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight); + + NSDictionary *outputSettings + = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.imageWidth], AVVideoWidthKey, + [NSNumber numberWithInt:self.imageHeight], AVVideoHeightKey, + AVVideoCodecH264, AVVideoCodecKey, + nil + ]; + + + self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings]; + + + /* I'm going to push pixel buffers to it, so will need a + AVAssetWriterPixelBufferAdaptor, to expect the same 32BGRA input as I've + asked the AVCaptureVideDataOutput to supply */ + int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; + + self.recordPixelBufferAdaptor = + [[AVAssetWriterInputPixelBufferAdaptor alloc] + initWithAssetWriterInput:self.recordAssetWriterInput + sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey,nil]]; + + NSError* error = nil; + NSLog(@"Create AVAssetWriter with url: %@", [self tempFileURL]); + self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self tempFileURL] + fileType:AVFileTypeMPEG4 + error:&error]; + if (error != nil) { + NSLog(@"[Camera] Unable to create AVAssetWriter: %@", error); + } + + [self.recordAssetWriter addInput:self.recordAssetWriterInput]; + self.recordAssetWriterInput.expectsMediaDataInRealTime = NO; + + NSLog(@"[Camera] created AVAssetWriter"); + +} + + +- (void)createCaptureOutput; +{ + [self createVideoDataOutput]; + if (self.recordVideo == YES) { + [self createVideoFileOutput]; + } +} + +- (void)createCustomVideoPreview; +{ + [self.parentView.layer addSublayer:self.customPreviewLayer]; +} + + +#pragma mark - Protocol AVCaptureVideoDataOutputSampleBufferDelegate + + +- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection +{ + if (self.delegate) { + + // convert from Core Media to Core Video + CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); + CVPixelBufferLockBaseAddress(imageBuffer, 0); + + void* bufferAddress; + size_t width; + size_t height; + size_t bytesPerRow; + + CGColorSpaceRef colorSpace; + CGContextRef context; + + int format_opencv; + + OSType format = CVPixelBufferGetPixelFormatType(imageBuffer); + if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) { + + format_opencv = CV_8UC1; + + bufferAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); + width = CVPixelBufferGetWidthOfPlane(imageBuffer, 0); + height = CVPixelBufferGetHeightOfPlane(imageBuffer, 0); + bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0); + + } else { // expect kCVPixelFormatType_32BGRA + + format_opencv = CV_8UC4; + + bufferAddress = CVPixelBufferGetBaseAddress(imageBuffer); + width = CVPixelBufferGetWidth(imageBuffer); + height = CVPixelBufferGetHeight(imageBuffer); + bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); + + } + + // delegate image processing to the delegate + cv::Mat image(height, width, format_opencv, bufferAddress, bytesPerRow); + + cv::Mat* result = NULL; + CGImage* dstImage; + + if ([self.delegate respondsToSelector:@selector(processImage:)]) { + [self.delegate processImage:image]; + } + + // check if matrix data pointer or dimensions were changed by the delegate + bool iOSimage = true; + if (height == image.rows && width == image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) { + iOSimage = false; + } + + + // (create color space, create graphics context, render buffer) + CGBitmapInfo bitmapInfo; + + // basically we decide if it's a grayscale, rgb or rgba image + if (image.channels() == 1) { + colorSpace = CGColorSpaceCreateDeviceGray(); + bitmapInfo = kCGImageAlphaNone; + } else if (image.channels() == 3) { + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitmapInfo = kCGImageAlphaNone; + if (iOSimage) { + bitmapInfo |= kCGBitmapByteOrder32Little; + } else { + bitmapInfo |= kCGBitmapByteOrder32Big; + } + } else { + colorSpace = CGColorSpaceCreateDeviceRGB(); + bitmapInfo = kCGImageAlphaPremultipliedFirst; + if (iOSimage) { + bitmapInfo |= kCGBitmapByteOrder32Little; + } else { + bitmapInfo |= kCGBitmapByteOrder32Big; + } + } + + if (iOSimage) { + context = CGBitmapContextCreate(bufferAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo); + dstImage = CGBitmapContextCreateImage(context); + CGContextRelease(context); + } else { + + NSData *data = [NSData dataWithBytes:image.data length:image.elemSize()*image.total()]; + CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data); + + // Creating CGImage from cv::Mat + dstImage = CGImageCreate(image.cols, // width + image.rows, // height + 8, // bits per component + 8 * image.elemSize(), // bits per pixel + image.step, // bytesPerRow + colorSpace, // colorspace + bitmapInfo, // bitmap info + provider, // CGDataProviderRef + NULL, // decode + false, // should interpolate + kCGRenderingIntentDefault // intent + ); + + CGDataProviderRelease(provider); + } + + + + + // render buffer + dispatch_sync(dispatch_get_main_queue(), ^{ + self.customPreviewLayer.contents = (__bridge id)dstImage; + }); + + + if (self.recordVideo == YES) { + // a very dense way to keep track of the time at which this frame + // occurs relative to the output stream, but it's just an example! + + // TODO reset frame number + static int64_t frameNumber = 0; + if (self.recordAssetWriterInput.readyForMoreMediaData) { + [self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer + withPresentationTime:CMTimeMake(frameNumber, self.defaultFPS)]; + } + frameNumber++; + } + + + // cleanup + CGImageRelease(dstImage); + + CGColorSpaceRelease(colorSpace); + + CVPixelBufferUnlockBaseAddress(imageBuffer, 0); + } +} + + +- (void)updateOrientation; +{ + NSLog(@"rotate.."); + self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height); + [self layoutPreviewLayer]; +} + + +- (void)saveVideo; +{ + if (self.recordVideo == NO) { + return; + } + + ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; + if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self tempFileURL]]) { + [library writeVideoAtPathToSavedPhotosAlbum:[self tempFileURL] + completionBlock:^(NSURL *assetURL, NSError *error){}]; + } +} + + +- (NSURL *)tempFileURL; +{ + NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; + NSURL *outputURL = [NSURL fileURLWithPath:outputPath]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:outputPath]) { + NSLog(@"file exists"); + } + return outputURL; +} + + + +- (NSString *)tempFileString; +{ + NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; + return outputPath; +} + +@end From d8b69d750df06ddbb1c4de1b884f8d3938b86079 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Mon, 20 Aug 2012 19:46:52 +0400 Subject: [PATCH 34/58] some tweaks in optimization flags for opencv2.framework --- ios/cmake/Modules/Platform/iOS.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/cmake/Modules/Platform/iOS.cmake b/ios/cmake/Modules/Platform/iOS.cmake index e8389798bb..4ef75e7f15 100644 --- a/ios/cmake/Modules/Platform/iOS.cmake +++ b/ios/cmake/Modules/Platform/iOS.cmake @@ -42,7 +42,7 @@ set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") set (CMAKE_C_FLAGS "") set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden") -set (CMAKE_CXX_FLAGS_RELEASE "-O3 -fomit-frame-pointer") +set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -fomit-frame-pointer -ffast-math") if (HAVE_FLAG_SEARCH_PATHS_FIRST) set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") From 4a1ef53deb6f64ae88ec25efd8ac262553eecf07 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 20 Aug 2012 19:51:19 +0400 Subject: [PATCH 35/58] Fixed gcc build warning --- modules/objdetect/src/hog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/objdetect/src/hog.cpp b/modules/objdetect/src/hog.cpp index 19decfb536..21bda2411c 100644 --- a/modules/objdetect/src/hog.cpp +++ b/modules/objdetect/src/hog.cpp @@ -2601,7 +2601,7 @@ void HOGDescriptor::readALTModel(std::string modelfile) double *linearwt = new double[totwords+1]; int length = totwords; nread = fread(linearwt, sizeof(double), totwords + 1, modelfl); - if(nread != length + 1) + if(nread != static_cast(length) + 1) throw Exception(); for(int i = 0; i < length; i++) From 682dede87e9b2d52ef3725608e1438a4ab8f202f Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Mon, 20 Aug 2012 19:52:24 +0400 Subject: [PATCH 36/58] #2215 Fix dependencies escaping in generated OpenCVConfig.cmake --- cmake/OpenCVGenConfig.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVGenConfig.cmake b/cmake/OpenCVGenConfig.cmake index 7a38e4b4e1..fcca310ceb 100644 --- a/cmake/OpenCVGenConfig.cmake +++ b/cmake/OpenCVGenConfig.cmake @@ -64,9 +64,14 @@ macro(ocv_generate_dependencies_map_configcmake suffix configuration) string(REGEX REPLACE "${CMAKE_SHARED_LIBRARY_SUFFIX}$" "${OPENCV_LINK_LIBRARY_SUFFIX}" __libname "${__libname}") endif() + string(REPLACE " " "\\ " __mod_deps "${${__ocv_lib}_MODULE_DEPS_${suffix}}") + string(REPLACE " " "\\ " __ext_deps "${${__ocv_lib}_EXTRA_DEPS_${suffix}}") + string(REPLACE "\"" "\\\"" __mod_deps "${__mod_deps}") + string(REPLACE "\"" "\\\"" __ext_deps "${__ext_deps}") + set(OPENCV_DEPENDENCIES_MAP_${suffix} "${OPENCV_DEPENDENCIES_MAP_${suffix}}set(OpenCV_${__ocv_lib}_LIBNAME_${suffix} \"${__libname}\")\n") - set(OPENCV_DEPENDENCIES_MAP_${suffix} "${OPENCV_DEPENDENCIES_MAP_${suffix}}set(OpenCV_${__ocv_lib}_DEPS_${suffix} ${${__ocv_lib}_MODULE_DEPS_${suffix}})\n") - set(OPENCV_DEPENDENCIES_MAP_${suffix} "${OPENCV_DEPENDENCIES_MAP_${suffix}}set(OpenCV_${__ocv_lib}_EXTRA_DEPS_${suffix} ${${__ocv_lib}_EXTRA_DEPS_${suffix}})\n") + set(OPENCV_DEPENDENCIES_MAP_${suffix} "${OPENCV_DEPENDENCIES_MAP_${suffix}}set(OpenCV_${__ocv_lib}_DEPS_${suffix} ${__mod_deps})\n") + set(OPENCV_DEPENDENCIES_MAP_${suffix} "${OPENCV_DEPENDENCIES_MAP_${suffix}}set(OpenCV_${__ocv_lib}_EXTRA_DEPS_${suffix} ${__ext_deps})\n") list(APPEND OPENCV_PROCESSED_LIBS ${__ocv_lib}) list(APPEND OPENCV_LIBS_TO_PROCESS ${${__ocv_lib}_MODULE_DEPS_${suffix}}) From ee0dd4d4967082c6bf3e4ba8d8bb3871720a7ec8 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Mon, 20 Aug 2012 23:35:29 +0400 Subject: [PATCH 37/58] disabled -Wunused-but-set-variable because of multiple warnings during building nvcc auto generated files (GCC 4.6) --- cmake/OpenCVDetectCUDA.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/OpenCVDetectCUDA.cmake b/cmake/OpenCVDetectCUDA.cmake index 60d800547d..43e9c819af 100644 --- a/cmake/OpenCVDetectCUDA.cmake +++ b/cmake/OpenCVDetectCUDA.cmake @@ -88,7 +88,11 @@ if(CUDA_FOUND) if(APPLE) set (CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -Xcompiler -fno-finite-math-only) endif() - string(REPLACE "-Wsign-promo" "" CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}") + + # disabled because of multiple warnings during building nvcc auto generated files + if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_GCC_REGEX_VERSION VERSION_GREATER "4.6.0") + ocv_warnings_disable(CMAKE_CXX_FLAGS -Wunused-but-set-variable) + endif() # we remove -ggdb3 flag as it leads to preprocessor errors when compiling CUDA files (CUDA 4.1) set(CMAKE_CXX_FLAGS_DEBUG_ ${CMAKE_CXX_FLAGS_DEBUG}) From 94f7c3c5923888caf14acb4ea86355888e3040fc Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Tue, 21 Aug 2012 01:03:52 +0400 Subject: [PATCH 38/58] Fixed FAST algorithm initializer --- modules/features2d/src/features2d_init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp index 6ecffebd40..e21b8d8021 100644 --- a/modules/features2d/src/features2d_init.cpp +++ b/modules/features2d/src/features2d_init.cpp @@ -59,7 +59,7 @@ CV_INIT_ALGORITHM(BriefDescriptorExtractor, "Feature2D.BRIEF", CV_INIT_ALGORITHM(FastFeatureDetector, "Feature2D.FAST", obj.info()->addParam(obj, "threshold", obj.threshold); obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression); - obj.info()->addParam(obj, "type", obj.type, FastFeatureDetector::TYPE_9_16)); + obj.info()->addParam(obj, "type", obj.type, static_cast(FastFeatureDetector::TYPE_9_16))); /////////////////////////////////////////////////////////////////////////////////////////////////////////// From 81d6400f45796f701c4f01057ee8216af01fba56 Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Tue, 21 Aug 2012 14:28:40 +0400 Subject: [PATCH 39/58] fixed compilation under windows --- modules/ts/include/opencv2/ts/ts_perf.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ts/include/opencv2/ts/ts_perf.hpp b/modules/ts/include/opencv2/ts/ts_perf.hpp index d2a0b996e9..f730b2ce7a 100644 --- a/modules/ts/include/opencv2/ts/ts_perf.hpp +++ b/modules/ts/include/opencv2/ts/ts_perf.hpp @@ -442,7 +442,7 @@ CV_EXPORTS void PrintTo(const Size& sz, ::std::ostream* os); // SANITY_CHECK(c); // } #define PERF_TEST_P(fixture, name, params) \ - class fixture##_##name : public ::fixture {\ + class fixture##_##name : public fixture {\ public:\ fixture##_##name() {}\ protected:\ From 017ab51bf4edc45d0da2ccb0ca889c206f6735a1 Mon Sep 17 00:00:00 2001 From: Victor Passichenko Date: Tue, 21 Aug 2012 15:38:38 +0400 Subject: [PATCH 40/58] Fix wrong memory deallocation --- modules/photo/src/arrays.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/photo/src/arrays.hpp b/modules/photo/src/arrays.hpp index c1c4e5f971..0d86cb6e3a 100644 --- a/modules/photo/src/arrays.hpp +++ b/modules/photo/src/arrays.hpp @@ -67,7 +67,7 @@ template struct Array2d { ~Array2d() { if (needToDeallocArray) { - delete a; + delete[] a; } } @@ -96,7 +96,7 @@ template struct Array3d { ~Array3d() { if (needToDeallocArray) { - delete a; + delete[] a; } } @@ -138,7 +138,7 @@ template struct Array4d { ~Array4d() { if (needToDeallocArray) { - delete a; + delete[] a; } } From 0d2b8f4ba11651306cf59dfaf4c0c29a0822b881 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 21 Aug 2012 15:41:05 +0400 Subject: [PATCH 41/58] fixed the channel ordering of rgba images (by Eduard) --- modules/highgui/src/cap_ios_video_camera.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/highgui/src/cap_ios_video_camera.mm b/modules/highgui/src/cap_ios_video_camera.mm index bed17e1882..6f7bfa20fc 100644 --- a/modules/highgui/src/cap_ios_video_camera.mm +++ b/modules/highgui/src/cap_ios_video_camera.mm @@ -522,9 +522,9 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; } // check if matrix data pointer or dimensions were changed by the delegate - bool iOSimage = true; + bool iOSimage = false; if (height == image.rows && width == image.cols && format_opencv == image.type() && bufferAddress == image.data && bytesPerRow == image.step) { - iOSimage = false; + iOSimage = true; } From 92edd4fc6e15bc98f6cd8903bd49ecd06a736253 Mon Sep 17 00:00:00 2001 From: Victor Passichenko Date: Tue, 21 Aug 2012 15:41:51 +0400 Subject: [PATCH 42/58] Fix building for MS VS C++: remove allocation of arrays of dynamic size in stack --- modules/photo/src/denoising.cpp | 4 ++-- .../src/fast_nlmeans_denoising_invoker.hpp | 14 +++++++------- .../fast_nlmeans_multi_denoising_invoker.hpp | 17 ++++++++--------- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index 39643e33dc..b980b407b7 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -103,7 +103,7 @@ static void fastNlMeansDenoisingMultiCheckPreconditions( int imgToDenoiseIndex, int temporalWindowSize, int templateWindowSize, int searchWindowSize) { - int src_imgs_size = srcImgs.size(); + int src_imgs_size = (int)srcImgs.size(); if (src_imgs_size == 0) { CV_Error(CV_StsBadArg, "Input images vector should not be empty!"); } @@ -176,7 +176,7 @@ void cv::fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, temporalWindowSize, templateWindowSize, searchWindowSize ); - int src_imgs_size = srcImgs.size(); + int src_imgs_size = (int)srcImgs.size(); if (srcImgs[0].type() != CV_8UC3) { CV_Error(CV_StsBadArg, "Type of input images should be CV_8UC3!"); diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp index 58e4a45e17..a45cb15d8a 100644 --- a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp @@ -62,6 +62,10 @@ struct FastNlMeansDenoisingInvoker { void operator() (const BlockedRange& range) const; + void operator= (const FastNlMeansDenoisingInvoker& invoker) { + CV_Error(CV_StsNotImplemented, "Assigment operator is not implemented"); + } + private: const Mat& src_; Mat& dst_; @@ -153,16 +157,12 @@ void FastNlMeansDenoisingInvoker::operator() (const BlockedRange& range) cons int row_from = range.begin(); int row_to = range.end() - 1; - int dist_sums_array[search_window_size_ * search_window_size_]; - Array2d dist_sums(dist_sums_array, search_window_size_, search_window_size_); + Array2d dist_sums(search_window_size_, search_window_size_); // for lazy calc optimization - int col_dist_sums_array[template_window_size_ * search_window_size_ * search_window_size_]; - Array3d col_dist_sums(&col_dist_sums_array[0], - template_window_size_, search_window_size_, search_window_size_); + Array3d col_dist_sums(template_window_size_, search_window_size_, search_window_size_); int first_col_num = -1; - Array3d up_col_dist_sums(src_.cols, search_window_size_, search_window_size_); for (int i = row_from; i <= row_to; i++) { @@ -233,7 +233,7 @@ void FastNlMeansDenoisingInvoker::operator() (const BlockedRange& range) cons // calc weights int weights_sum = 0; - int estimation[src_.channels()]; + int estimation[3]; for (int channel_num = 0; channel_num < src_.channels(); channel_num++) { estimation[channel_num] = 0; } diff --git a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp index cb08c7e434..8a6a81979b 100644 --- a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp @@ -63,6 +63,10 @@ struct FastNlMeansMultiDenoisingInvoker { void operator() (const BlockedRange& range) const; + void operator= (const FastNlMeansMultiDenoisingInvoker& invoker) { + CV_Error(CV_StsNotImplemented, "Assigment operator is not implemented"); + } + private: int rows_; int cols_; @@ -175,16 +179,11 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const BlockedRange& range) int row_from = range.begin(); int row_to = range.end() - 1; - int dist_sums_array[temporal_window_size_ * search_window_size_ * search_window_size_]; - Array3d dist_sums(dist_sums_array, - temporal_window_size_, search_window_size_, search_window_size_); + Array3d dist_sums(temporal_window_size_, search_window_size_, search_window_size_); // for lazy calc optimization - int col_dist_sums_array[ - template_window_size_ * temporal_window_size_ * search_window_size_ * search_window_size_]; - - Array4d col_dist_sums(col_dist_sums_array, - template_window_size_, temporal_window_size_, search_window_size_, search_window_size_); + Array4d col_dist_sums( + template_window_size_, temporal_window_size_, search_window_size_, search_window_size_); int first_col_num = -1; @@ -263,7 +262,7 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const BlockedRange& range) // calc weights int weights_sum = 0; - int estimation[channels_count_]; + int estimation[3]; for (int channel_num = 0; channel_num < channels_count_; channel_num++) { estimation[channel_num] = 0; } From 18de45f7bcda86e453156f3d18e1cdb8ded5ec42 Mon Sep 17 00:00:00 2001 From: Victor Passichenko Date: Tue, 21 Aug 2012 16:05:18 +0400 Subject: [PATCH 43/58] Add CV_Asserts for channels count --- modules/photo/src/fast_nlmeans_denoising_invoker.hpp | 2 ++ modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp index a45cb15d8a..6724e8272f 100644 --- a/modules/photo/src/fast_nlmeans_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_denoising_invoker.hpp @@ -106,6 +106,8 @@ FastNlMeansDenoisingInvoker::FastNlMeansDenoisingInvoker( int search_window_size, const double h) : src_(src), dst_(dst) { + CV_Assert(src.channels() <= 3); + template_window_half_size_ = template_window_size / 2; search_window_half_size_ = search_window_size / 2; template_window_size_ = template_window_half_size_ * 2 + 1; diff --git a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp index 8a6a81979b..02185d13d2 100644 --- a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp @@ -115,6 +115,9 @@ FastNlMeansMultiDenoisingInvoker::FastNlMeansMultiDenoisingInvoker( int search_window_size, const double h) : dst_(dst), extended_srcs_(srcImgs.size()) { + CV_Assert(srcImgs.size() > 0); + CV_Assert(srcImgs[0].channels() <= 3); + rows_ = srcImgs[0].rows; cols_ = srcImgs[0].cols; channels_count_ = srcImgs[0].channels(); From 2f1cc018c9be148ebe55976e41ab70faafeee45d Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 21 Aug 2012 17:16:06 +0400 Subject: [PATCH 44/58] enabled SSE3 by default; integrated SSE3-optimized bilateral filter (by Grigoriy Frolov); modified API of non-local means (use Input/OutputArrays) --- CMakeLists.txt | 2 +- modules/imgproc/src/smooth.cpp | 237 ++++++++++++++++-- .../photo/include/opencv2/photo/denoising.hpp | 28 +-- modules/photo/src/denoising.cpp | 35 ++- .../fast_nlmeans_multi_denoising_invoker.hpp | 7 +- 5 files changed, 261 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80cf798ee6..c81cbd9dee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,7 +191,7 @@ OCV_OPTION(ENABLE_POWERPC "Enable PowerPC for GCC" OCV_OPTION(ENABLE_FAST_MATH "Enable -ffast-math (not recommended for GCC 4.6.x)" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSE "Enable SSE instructions" ON IF (MSVC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSE2 "Enable SSE2 instructions" ON IF (MSVC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) -OCV_OPTION(ENABLE_SSE3 "Enable SSE3 instructions" OFF IF (CV_ICC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) +OCV_OPTION(ENABLE_SSE3 "Enable SSE3 instructions" ON IF (MSVC OR CV_ICC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSSE3 "Enable SSSE3 instructions" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSE41 "Enable SSE4.1 instructions" OFF IF (CV_ICC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSE42 "Enable SSE4.2 instructions" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) diff --git a/modules/imgproc/src/smooth.cpp b/modules/imgproc/src/smooth.cpp index 058768551e..0fe1b6be44 100644 --- a/modules/imgproc/src/smooth.cpp +++ b/modules/imgproc/src/smooth.cpp @@ -1294,28 +1294,64 @@ class BilateralFilter_8u_Invoker : public: BilateralFilter_8u_Invoker(Mat& _dest, const Mat& _temp, int _radius, int _maxk, int* _space_ofs, float *_space_weight, float *_color_weight) : - ParallelLoopBody(), dest(&_dest), temp(&_temp), radius(_radius), + temp(&_temp), dest(&_dest), radius(_radius), maxk(_maxk), space_ofs(_space_ofs), space_weight(_space_weight), color_weight(_color_weight) { } - + virtual void operator() (const Range& range) const { int i, j, cn = dest->channels(), k; Size size = dest->size(); - + #if CV_SSE3 + int CV_DECL_ALIGNED(16) buf[4]; + float CV_DECL_ALIGNED(16) bufSum[4]; + static const int CV_DECL_ALIGNED(16) bufSignMask[] = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + bool haveSSE3 = checkHardwareSupport(CV_CPU_SSE3); + #endif + for( i = range.start; i < range.end; i++ ) { const uchar* sptr = temp->ptr(i+radius) + radius*cn; uchar* dptr = dest->ptr(i); - + if( cn == 1 ) { for( j = 0; j < size.width; j++ ) { float sum = 0, wsum = 0; int val0 = sptr[j]; - for( k = 0; k < maxk; k++ ) + k = 0; + #if CV_SSE3 + if( haveSSE3 ) + { + __m128 _val0 = _mm_set1_ps(val0); + const __m128 _signMask = _mm_load_ps((const float*)bufSignMask); + + for( ; k <= maxk - 4; k += 4 ) + { + __m128 _valF = _mm_set_ps(sptr[j + space_ofs[k+3]], sptr[j + space_ofs[k+2]], + sptr[j + space_ofs[k+1]], sptr[j + space_ofs[k]]); + + __m128 _val = _mm_andnot_ps(_signMask, _mm_sub_ps(_valF, _val0)); + _mm_store_si128((__m128i*)buf, _mm_cvtps_epi32(_val)); + + __m128 _cw = _mm_set_ps(color_weight[buf[3]],color_weight[buf[2]], + color_weight[buf[1]],color_weight[buf[0]]); + __m128 _sw = _mm_loadu_ps(space_weight+k); + __m128 _w = _mm_mul_ps(_cw, _sw); + _cw = _mm_mul_ps(_w, _valF); + + _sw = _mm_hadd_ps(_w, _cw); + _sw = _mm_hadd_ps(_sw, _sw); + _mm_storel_pi((__m64*)bufSum, _sw); + + sum += bufSum[1]; + wsum += bufSum[0]; + } + } + #endif + for( ; k < maxk; k++ ) { int val = sptr[j + space_ofs[k]]; float w = space_weight[k]*color_weight[std::abs(val - val0)]; @@ -1333,7 +1369,57 @@ public: { float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0; int b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2]; - for( k = 0; k < maxk; k++ ) + k = 0; + #if CV_SSE3 + if( haveSSE3 ) + { + const __m128 _b0 = _mm_set1_ps(b0); + const __m128 _g0 = _mm_set1_ps(g0); + const __m128 _r0 = _mm_set1_ps(r0); + const __m128 _signMask = _mm_load_ps((const float*)bufSignMask); + + for( ; k <= maxk - 4; k += 4 ) + { + const uchar* sptr_k = sptr + j + space_ofs[k]; + const uchar* sptr_k1 = sptr + j + space_ofs[k+1]; + const uchar* sptr_k2 = sptr + j + space_ofs[k+2]; + const uchar* sptr_k3 = sptr + j + space_ofs[k+3]; + + __m128 _b = _mm_set_ps(sptr_k3[0],sptr_k2[0],sptr_k1[0],sptr_k[0]); + __m128 _g = _mm_set_ps(sptr_k3[1],sptr_k2[1],sptr_k1[1],sptr_k[1]); + __m128 _r = _mm_set_ps(sptr_k3[2],sptr_k2[2],sptr_k1[2],sptr_k[2]); + + __m128 bt = _mm_andnot_ps(_signMask, _mm_sub_ps(_b,_b0)); + __m128 gt = _mm_andnot_ps(_signMask, _mm_sub_ps(_g,_g0)); + __m128 rt = _mm_andnot_ps(_signMask, _mm_sub_ps(_r,_r0)); + + bt =_mm_add_ps(rt, _mm_add_ps(bt, gt)); + _mm_store_si128((__m128i*)buf, _mm_cvtps_epi32(bt)); + + __m128 _w = _mm_set_ps(color_weight[buf[3]],color_weight[buf[2]], + color_weight[buf[1]],color_weight[buf[0]]); + __m128 _sw = _mm_loadu_ps(space_weight+k); + + _w = _mm_mul_ps(_w,_sw); + _b = _mm_mul_ps(_b, _w); + _g = _mm_mul_ps(_g, _w); + _r = _mm_mul_ps(_r, _w); + + _w = _mm_hadd_ps(_w, _b); + _g = _mm_hadd_ps(_g, _r); + + _w = _mm_hadd_ps(_w, _g); + _mm_store_ps(bufSum, _w); + + wsum += bufSum[0]; + sum_b += bufSum[1]; + sum_g += bufSum[2]; + sum_r += bufSum[3]; + } + } + #endif + + for( ; k < maxk; k++ ) { const uchar* sptr_k = sptr + j + space_ofs[k]; int b = sptr_k[0], g = sptr_k[1], r = sptr_k[2]; @@ -1351,10 +1437,10 @@ public: } } } - + private: - Mat *dest; const Mat *temp; + Mat *dest; int radius, maxk, *space_ofs; float *space_weight, *color_weight; }; @@ -1364,46 +1450,51 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, double sigma_color, double sigma_space, int borderType ) { + int cn = src.channels(); int i, j, maxk, radius; Size size = src.size(); - + CV_Assert( (src.type() == CV_8UC1 || src.type() == CV_8UC3) && src.type() == dst.type() && src.size() == dst.size() && src.data != dst.data ); - + if( sigma_color <= 0 ) sigma_color = 1; if( sigma_space <= 0 ) sigma_space = 1; - + double gauss_color_coeff = -0.5/(sigma_color*sigma_color); double gauss_space_coeff = -0.5/(sigma_space*sigma_space); - + if( d <= 0 ) radius = cvRound(sigma_space*1.5); else radius = d/2; radius = MAX(radius, 1); d = radius*2 + 1; - + Mat temp; copyMakeBorder( src, temp, radius, radius, radius, radius, borderType ); - + vector _color_weight(cn*256); vector _space_weight(d*d); vector _space_ofs(d*d); float* color_weight = &_color_weight[0]; float* space_weight = &_space_weight[0]; int* space_ofs = &_space_ofs[0]; - + // initialize color-related bilateral filter coefficients + for( i = 0; i < 256*cn; i++ ) color_weight[i] = (float)std::exp(i*i*gauss_color_coeff); - + // initialize space-related bilateral filter coefficients for( i = -radius, maxk = 0; i <= radius; i++ ) - for( j = -radius; j <= radius; j++ ) + { + j = -radius; + + for( ;j <= radius; j++ ) { double r = std::sqrt((double)i*i + (double)j*j); if( r > radius ) @@ -1411,7 +1502,8 @@ bilateralFilter_8u( const Mat& src, Mat& dst, int d, space_weight[maxk] = (float)std::exp(r*r*gauss_space_coeff); space_ofs[maxk++] = (int)(i*temp.step + j*cn); } - + } + BilateralFilter_8u_Invoker body(dst, temp, radius, maxk, space_ofs, space_weight, color_weight); parallel_for_(Range(0, size.height), body); } @@ -1424,7 +1516,7 @@ public: BilateralFilter_32f_Invoker(int _cn, int _radius, int _maxk, int *_space_ofs, const Mat& _temp, Mat& _dest, float _scale_index, float *_space_weight, float *_expLUT) : - ParallelLoopBody(), cn(_cn), radius(_radius), maxk(_maxk), space_ofs(_space_ofs), + cn(_cn), radius(_radius), maxk(_maxk), space_ofs(_space_ofs), temp(&_temp), dest(&_dest), scale_index(_scale_index), space_weight(_space_weight), expLUT(_expLUT) { } @@ -1433,6 +1525,12 @@ public: { int i, j, k; Size size = dest->size(); + #if CV_SSE3 + int CV_DECL_ALIGNED(16) idxBuf[4]; + float CV_DECL_ALIGNED(16) bufSum32[4]; + static const int CV_DECL_ALIGNED(16) bufSignMask[] = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + bool haveSSE3 = checkHardwareSupport(CV_CPU_SSE3); + #endif for( i = range.start; i < range.end; i++ ) { @@ -1445,7 +1543,44 @@ public: { float sum = 0, wsum = 0; float val0 = sptr[j]; - for( k = 0; k < maxk; k++ ) + k = 0; + #if CV_SSE3 + if( haveSSE3 ) + { + const __m128 _val0 = _mm_set1_ps(sptr[j]); + const __m128 _scale_index = _mm_set1_ps(scale_index); + const __m128 _signMask = _mm_load_ps((const float*)bufSignMask); + + for( ; k <= maxk - 4 ; k += 4 ) + { + __m128 _sw = _mm_loadu_ps(space_weight + k); + __m128 _val = _mm_set_ps(sptr[j + space_ofs[k+3]], sptr[j + space_ofs[k+2]], + sptr[j + space_ofs[k+1]], sptr[j + space_ofs[k]]); + __m128 _alpha = _mm_mul_ps(_mm_andnot_ps( _signMask, _mm_sub_ps(_val,_val0)), _scale_index); + + __m128i _idx = _mm_cvtps_epi32(_alpha); + _mm_store_si128((__m128i*)idxBuf, _idx); + _alpha = _mm_sub_ps(_alpha, _mm_cvtepi32_ps(_idx)); + + __m128 _explut = _mm_set_ps(expLUT[idxBuf[3]], expLUT[idxBuf[2]], + expLUT[idxBuf[1]], expLUT[idxBuf[0]]); + __m128 _explut1 = _mm_set_ps(expLUT[idxBuf[3]+1], expLUT[idxBuf[2]+1], + expLUT[idxBuf[1]+1], expLUT[idxBuf[0]+1]); + + __m128 _w = _mm_mul_ps(_sw, _mm_add_ps(_explut, _mm_mul_ps(_alpha, _mm_sub_ps(_explut1, _explut)))); + _val = _mm_mul_ps(_w, _val); + + _sw = _mm_hadd_ps(_w, _val); + _sw = _mm_hadd_ps(_sw, _sw); + _mm_storel_pi((__m64*)bufSum32, _sw); + + sum += bufSum32[1]; + wsum += bufSum32[0]; + } + } + #endif + + for( ; k < maxk; k++ ) { float val = sptr[j + space_ofs[k]]; float alpha = (float)(std::abs(val - val0)*scale_index); @@ -1465,7 +1600,64 @@ public: { float sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0; float b0 = sptr[j], g0 = sptr[j+1], r0 = sptr[j+2]; - for( k = 0; k < maxk; k++ ) + k = 0; + #if CV_SSE3 + if( haveSSE3 ) + { + const __m128 _b0 = _mm_set1_ps(b0); + const __m128 _g0 = _mm_set1_ps(g0); + const __m128 _r0 = _mm_set1_ps(r0); + const __m128 _scale_index = _mm_set1_ps(scale_index); + const __m128 _signMask = _mm_load_ps((const float*)bufSignMask); + + for( ; k <= maxk-4; k += 4 ) + { + __m128 _sw = _mm_loadu_ps(space_weight + k); + + const float* sptr_k = sptr + j + space_ofs[k]; + const float* sptr_k1 = sptr + j + space_ofs[k+1]; + const float* sptr_k2 = sptr + j + space_ofs[k+2]; + const float* sptr_k3 = sptr + j + space_ofs[k+3]; + + __m128 _b = _mm_set_ps(sptr_k3[0], sptr_k2[0], sptr_k1[0], sptr_k[0]); + __m128 _g = _mm_set_ps(sptr_k3[1], sptr_k2[1], sptr_k1[1], sptr_k[1]); + __m128 _r = _mm_set_ps(sptr_k3[2], sptr_k2[2], sptr_k1[2], sptr_k[2]); + + __m128 _bt = _mm_andnot_ps(_signMask,_mm_sub_ps(_b,_b0)); + __m128 _gt = _mm_andnot_ps(_signMask,_mm_sub_ps(_g,_g0)); + __m128 _rt = _mm_andnot_ps(_signMask,_mm_sub_ps(_r,_r0)); + + __m128 _alpha = _mm_mul_ps(_scale_index, _mm_add_ps(_rt,_mm_add_ps(_bt, _gt))); + + __m128i _idx = _mm_cvtps_epi32(_alpha); + _mm_store_si128((__m128i*)idxBuf, _idx); + _alpha = _mm_sub_ps(_alpha, _mm_cvtepi32_ps(_idx)); + + __m128 _explut = _mm_set_ps(expLUT[idxBuf[3]], expLUT[idxBuf[2]], expLUT[idxBuf[1]], expLUT[idxBuf[0]]); + __m128 _explut1 = _mm_set_ps(expLUT[idxBuf[3]+1], expLUT[idxBuf[2]+1], expLUT[idxBuf[1]+1], expLUT[idxBuf[0]+1]); + + __m128 _w = _mm_mul_ps(_sw, _mm_add_ps(_explut, _mm_mul_ps(_alpha, _mm_sub_ps(_explut1, _explut)))); + + _b = _mm_mul_ps(_b, _w); + _g = _mm_mul_ps(_g, _w); + _r = _mm_mul_ps(_r, _w); + + _w = _mm_hadd_ps(_w, _b); + _g = _mm_hadd_ps(_g, _r); + + _w = _mm_hadd_ps(_w, _g); + _mm_store_ps(bufSum32, _w); + + wsum += bufSum32[0]; + sum_b += bufSum32[1]; + sum_g += bufSum32[2]; + sum_r += bufSum32[3]; + } + + } + #endif + + for(; k < maxk; k++ ) { const float* sptr_k = sptr + j + space_ofs[k]; float b = sptr_k[0], g = sptr_k[1], r = sptr_k[2]; @@ -1493,6 +1685,7 @@ private: Mat *dest; float scale_index, *space_weight, *expLUT; }; + static void bilateralFilter_32f( const Mat& src, Mat& dst, int d, @@ -1569,7 +1762,7 @@ bilateralFilter_32f( const Mat& src, Mat& dst, int d, } // initialize space-related bilateral filter coefficients - for( i = -radius, maxk = 0; i <= radius; i++ ) + for( i = -radius, maxk = 0; i <= radius; i++ ) for( j = -radius; j <= radius; j++ ) { double r = std::sqrt((double)i*i + (double)j*j); diff --git a/modules/photo/include/opencv2/photo/denoising.hpp b/modules/photo/include/opencv2/photo/denoising.hpp index b322c31755..1fc35a674b 100644 --- a/modules/photo/include/opencv2/photo/denoising.hpp +++ b/modules/photo/include/opencv2/photo/denoising.hpp @@ -55,23 +55,23 @@ namespace cv { -CV_EXPORTS void fastNlMeansDenoising( const Mat& src, Mat& dst, - int templateWindowSize, int searchWindowSize, int h); +CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, + int templateWindowSize, int searchWindowSize, int h); -CV_EXPORTS void fastNlMeansDenoisingColored( const Mat& src, Mat& dst, - int templateWindowSize, int searchWindowSize, - int h, int hForColorComponents); +CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); -CV_EXPORTS void fastNlMeansDenoisingMulti( const std::vector& srcImgs, - int imgToDenoiseIndex, int temporalWindowSize, - Mat& dst, - int templateWindowSize, int searchWindowSize, int h); +CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + OutputArray dst, + int templateWindowSize, int searchWindowSize, int h); -CV_EXPORTS void fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, - int imgToDenoiseIndex, int temporalWindowSize, - Mat& dst, - int templateWindowSize, int searchWindowSize, - int h, int hForColorComponents); +CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + OutputArray dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); } #endif diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index b980b407b7..7452d0d3ce 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -45,9 +45,13 @@ #include "fast_nlmeans_denoising_invoker.hpp" #include "fast_nlmeans_multi_denoising_invoker.hpp" -void cv::fastNlMeansDenoising( const cv::Mat& src, cv::Mat& dst, +void cv::fastNlMeansDenoising( InputArray _src, OutputArray _dst, int templateWindowSize, int searchWindowSize, int h) -{ +{ + Mat src = _src.getMat(); + _dst.create(src.size(), src.type()); + Mat dst = _dst.getMat(); + switch (src.type()) { case CV_8U: parallel_for(cv::BlockedRange(0, src.rows), @@ -70,10 +74,14 @@ void cv::fastNlMeansDenoising( const cv::Mat& src, cv::Mat& dst, } } -void cv::fastNlMeansDenoisingColored( const cv::Mat& src, cv::Mat& dst, +void cv::fastNlMeansDenoisingColored( InputArray _src, OutputArray _dst, int templateWindowSize, int searchWindowSize, int h, int hForColorComponents) { + Mat src = _src.getMat(); + _dst.create(src.size(), src.type()); + Mat dst = _dst.getMat(); + if (src.type() != CV_8UC3) { CV_Error(CV_StsBadArg, "Type of input image should be CV_8UC3!"); return; @@ -130,15 +138,20 @@ static void fastNlMeansDenoisingMultiCheckPreconditions( } } -void cv::fastNlMeansDenoisingMulti( const std::vector& srcImgs, +void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, int imgToDenoiseIndex, int temporalWindowSize, - cv::Mat& dst, + OutputArray _dst, int templateWindowSize, int searchWindowSize, int h) -{ +{ + vector srcImgs; + _srcImgs.getMatVector(srcImgs); + fastNlMeansDenoisingMultiCheckPreconditions( srcImgs, imgToDenoiseIndex, temporalWindowSize, templateWindowSize, searchWindowSize ); + _dst.create(srcImgs[0].size(), srcImgs[0].type()); + Mat dst = _dst.getMat(); switch (srcImgs[0].type()) { case CV_8U: @@ -165,16 +178,22 @@ void cv::fastNlMeansDenoisingMulti( const std::vector& srcImgs, } } -void cv::fastNlMeansDenoisingColoredMulti( const std::vector& srcImgs, +void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, int imgToDenoiseIndex, int temporalWindowSize, - cv::Mat& dst, + OutputArray _dst, int templateWindowSize, int searchWindowSize, int h, int hForColorComponents) { + vector srcImgs; + _srcImgs.getMatVector(srcImgs); + fastNlMeansDenoisingMultiCheckPreconditions( srcImgs, imgToDenoiseIndex, temporalWindowSize, templateWindowSize, searchWindowSize ); + + _dst.create(srcImgs[0].size(), srcImgs[0].type()); + Mat dst = _dst.getMat(); int src_imgs_size = (int)srcImgs.size(); diff --git a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp index 02185d13d2..602007e326 100644 --- a/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp +++ b/modules/photo/src/fast_nlmeans_multi_denoising_invoker.hpp @@ -270,9 +270,9 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const BlockedRange& range) estimation[channel_num] = 0; } for (int d = 0; d < temporal_window_size_; d++) { + const Mat& esrc_d = extended_srcs_[d]; for (int y = 0; y < search_window_size_; y++) { - const T* cur_row_ptr = - extended_srcs_[d].ptr(border_size_ + search_window_y + y); + const T* cur_row_ptr = esrc_d.ptr(border_size_ + search_window_y + y); int* dist_sums_row = dist_sums.row_ptr(d, y); @@ -298,7 +298,8 @@ void FastNlMeansMultiDenoisingInvoker::operator() (const BlockedRange& range) dst_.at(i,j) = saturateCastFromArray(estimation); } else { // weights_sum == 0 - dst_.at(i,j) = extended_srcs_[temporal_window_half_size_].at(i,j); + const Mat& esrc = extended_srcs_[temporal_window_half_size_]; + dst_.at(i,j) = esrc.at(i,j); } } } From 10dcf9ea39999a26f5b1d49cf7f742be9899f53d Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Tue, 21 Aug 2012 18:34:02 +0400 Subject: [PATCH 45/58] Fix Java API build --- modules/photo/include/opencv2/photo/denoising.hpp | 8 ++++---- modules/photo/include/opencv2/photo/photo.hpp | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/photo/include/opencv2/photo/denoising.hpp b/modules/photo/include/opencv2/photo/denoising.hpp index 1fc35a674b..718c3b73e8 100644 --- a/modules/photo/include/opencv2/photo/denoising.hpp +++ b/modules/photo/include/opencv2/photo/denoising.hpp @@ -59,18 +59,18 @@ CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, int templateWindowSize, int searchWindowSize, int h); CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst, - int templateWindowSize, int searchWindowSize, + int templateWindowSize, int searchWindowSize, int h, int hForColorComponents); -CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, +CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, int imgToDenoiseIndex, int temporalWindowSize, OutputArray dst, int templateWindowSize, int searchWindowSize, int h); -CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, +CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, int imgToDenoiseIndex, int temporalWindowSize, OutputArray dst, - int templateWindowSize, int searchWindowSize, + int templateWindowSize, int searchWindowSize, int h, int hForColorComponents); } diff --git a/modules/photo/include/opencv2/photo/photo.hpp b/modules/photo/include/opencv2/photo/photo.hpp index 40a0924327..f771b8fbd9 100644 --- a/modules/photo/include/opencv2/photo/photo.hpp +++ b/modules/photo/include/opencv2/photo/photo.hpp @@ -47,6 +47,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/photo/photo_c.h" +#include "opencv2/photo/denoising.hpp" #ifdef __cplusplus From a840d929aca2ffc6429882e92570b69850e88784 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Tue, 21 Aug 2012 18:59:51 +0400 Subject: [PATCH 46/58] #2170 fix warning of undefined GTEST_HAS_DEATH_TEST --- modules/ts/include/opencv2/ts/ts_gtest.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ts/include/opencv2/ts/ts_gtest.h b/modules/ts/include/opencv2/ts/ts_gtest.h index 1300ff2d6f..f98f71b28c 100644 --- a/modules/ts/include/opencv2/ts/ts_gtest.h +++ b/modules/ts/include/opencv2/ts/ts_gtest.h @@ -1702,6 +1702,8 @@ inline bool operator!=(const GTEST_10_TUPLE_(T)& t, GTEST_OS_OPENBSD || GTEST_OS_QNX) # define GTEST_HAS_DEATH_TEST 1 # include // NOLINT +#else +# define GTEST_HAS_DEATH_TEST 0 #endif // We don't support MSVC 7.1 with exceptions disabled now. Therefore From 64661ea9cd2cdd97b43740f07565a6f64abbb810 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 21 Aug 2012 23:00:50 +0400 Subject: [PATCH 47/58] added SSE2-optimized resizeAreaFast for 8-bit images by Grigoriy Frolov --- modules/imgproc/src/imgwarp.cpp | 117 ++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index a311d5be67..b09588879c 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1328,6 +1328,123 @@ static void resizeArea_( const Mat& src, Mat& dst, const DecimateAlpha* xofs, in } } } + + +template <> static void +resizeAreaFast_( const Mat& src, Mat& dst, const int* ofs, const int* xofs, + int scale_x, int scale_y ) +{ +#if CV_SSE2 + bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2); +#endif + + Size ssize = src.size(), dsize = dst.size(); + int cn = src.channels(); + int dy, dx, k = 0; + int area = scale_x*scale_y; + float scale = 1.f/(scale_x*scale_y); + int dwidth1 = (ssize.width/scale_x)*cn; + dsize.width *= cn; + ssize.width *= cn; + //avg values + for( dy = 0; dy < dsize.height; dy++ ) + { + uchar* D = (uchar*)(dst.data + dst.step*dy); + int sy0 = dy*scale_y, w = sy0 + scale_y <= ssize.height ? dwidth1 : 0; + if( sy0 >= ssize.height ) + { + for( dx = 0; dx < dsize.width; dx++ ) //memset(D,0, dsize.width);//warning, never executed -> not tested + D[dx] = 0; + continue; + } + dx = 0; + + #if CV_SSE2 + if( haveSSE2 ) + { + const __m128 _scale = _mm_set1_ps(scale); + const __m128i _ucMAXs = _mm_set1_epi16(UCHAR_MAX); + const uchar* _S[8]; + + for(; dx < w-8; dx+=8 ) + { + __m128i _sum = _mm_setzero_si128(); + __m128i _sum1 = _mm_setzero_si128(); + _S[0] = (const uchar*)(src.data + src.step*sy0) + xofs[dx]; + _S[1] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+1]; + _S[2] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+2]; + _S[3] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+3]; + + _S[4] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+4]; + _S[5] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+5]; + _S[6] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+6]; + _S[7] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+7]; + + for( k = 0; k < area; k++ ) + { + int ofsk = ofs[k]; + __m128i _temp = _mm_set_epi32(_S[3][ofsk],_S[2][ofsk],_S[1][ofsk],_S[0][ofsk]); + _sum = _mm_add_epi32(_sum, _temp); + + __m128i _temp1 = _mm_set_epi32(_S[7][ofsk],_S[6][ofsk],_S[5][ofsk],_S[4][ofsk]); + _sum1 = _mm_add_epi32(_sum1, _temp1); + } + + __m128i _tempSum = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(_sum), _scale)); + __m128i _tempSum1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(_sum1), _scale)); + + _tempSum = _mm_packs_epi32(_tempSum, _tempSum1); + _tempSum = _mm_min_epi16(_ucMAXs, _tempSum); + _tempSum = _mm_packus_epi16(_tempSum, _tempSum); + _mm_storel_epi64((__m128i*)(D+dx),_tempSum); + } + } + #endif + + for(; dx < w; dx++ ) + { + const uchar* S = (const uchar*)(src.data + src.step*sy0) + xofs[dx]; + int sum = 0; + k=0; + + #if CV_ENABLE_UNROLLED + for( ; k <= area - 4; k += 4 ) + sum += S[ofs[k]] + S[ofs[k+1]] + S[ofs[k+2]] + S[ofs[k+3]]; + #endif + + for( ; k < area; k++ ) + sum += S[ofs[k]]; + + + D[dx] = saturate_cast(sum*scale); + } + + for( ; dx < dsize.width; dx++ ) + { + int sum = 0; + int count = 0, sx0 = xofs[dx]; + if( sx0 >= ssize.width ) + D[dx] = 0; + + for( int sy = 0; sy < scale_y; sy++ ) + { + if( sy0 + sy >= ssize.height ) + break; + const uchar* S = (const uchar*)(src.data + src.step*(sy0 + sy)) + sx0; + int sx = 0; + for( ; sx < scale_x*cn; sx += cn ) + { + if( sx0 + sx >= ssize.width ) + break; + sum += S[sx]; + count++; + } + } + + D[dx] = saturate_cast((float)sum/count); + } + } +} typedef void (*ResizeFunc)( const Mat& src, Mat& dst, From 793e8b546d060a6ad032a794afd4cbd351ed87ed Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Tue, 21 Aug 2012 23:11:49 +0400 Subject: [PATCH 48/58] fixed build on Ubuntu --- modules/imgproc/src/imgwarp.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index b09588879c..e5a47e1a2d 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -1330,9 +1330,9 @@ static void resizeArea_( const Mat& src, Mat& dst, const DecimateAlpha* xofs, in } -template <> static void -resizeAreaFast_( const Mat& src, Mat& dst, const int* ofs, const int* xofs, - int scale_x, int scale_y ) +static void resizeAreaFast_8u( const Mat& src, Mat& dst, + const int* ofs, const int* xofs, + int scale_x, int scale_y ) { #if CV_SSE2 bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2); @@ -1553,7 +1553,7 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, static ResizeAreaFastFunc areafast_tab[] = { - resizeAreaFast_, 0, + resizeAreaFast_8u, 0, resizeAreaFast_, resizeAreaFast_, 0, From f2a02feffa582a850800174e9a520e2a4d730777 Mon Sep 17 00:00:00 2001 From: Andrey Kamaev Date: Wed, 22 Aug 2012 03:59:05 +0400 Subject: [PATCH 49/58] Fix Windows build issues * No /arch:SSE3 in MSVC * Warnings fixed or suppressed --- 3rdparty/libtiff/CMakeLists.txt | 2 +- CMakeLists.txt | 8 ++--- modules/features2d/src/features2d_init.cpp | 2 +- modules/features2d/test/test_fast.cpp | 4 +-- .../features2d/test/test_nearestneighbors.cpp | 6 ++-- .../test_rotation_and_scale_invariance.cpp | 30 +++++++++---------- modules/gpu/perf/perf_imgproc.cpp | 18 +++++------ modules/gpu/perf/perf_labeling.cpp | 9 ++++-- modules/gpu/perf/perf_video.cpp | 2 +- samples/cpp/bagofwords_classification.cpp | 1 + samples/gpu/bgfg_segm.cpp | 6 ++-- samples/gpu/performance/tests.cpp | 4 +-- 12 files changed, 49 insertions(+), 43 deletions(-) diff --git a/3rdparty/libtiff/CMakeLists.txt b/3rdparty/libtiff/CMakeLists.txt index ed9d1c86b7..6dd7956f58 100644 --- a/3rdparty/libtiff/CMakeLists.txt +++ b/3rdparty/libtiff/CMakeLists.txt @@ -89,7 +89,7 @@ endif(WIN32) ocv_warnings_disable(CMAKE_C_FLAGS -Wno-unused-but-set-variable -Wmissing-prototypes -Wmissing-declarations -Wundef -Wunused -Wsign-compare -Wcast-align -Wshadow -Wno-maybe-uninitialized -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast) -ocv_warnings_disable(CMAKE_CXX_FLAGS -Wmissing-declarations -Wunused-parameter /wd4100 /wd4244 /wd4706 /wd4127 /wd4701 /wd4018 /wd4267 /wd4306 /wd4305 /wd4312 /wd4311) +ocv_warnings_disable(CMAKE_CXX_FLAGS -Wmissing-declarations -Wunused-parameter /wd4100 /wd4244 /wd4706 /wd4127 /wd4701 /wd4018 /wd4267 /wd4306 /wd4305 /wd4312 /wd4311 /wd4703) if(UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR CV_ICC)) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") diff --git a/CMakeLists.txt b/CMakeLists.txt index c81cbd9dee..d893f1c74d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,11 +189,11 @@ OCV_OPTION(ENABLE_PROFILING "Enable profiling in the GCC compiler (Add OCV_OPTION(ENABLE_OMIT_FRAME_POINTER "Enable -fomit-frame-pointer for GCC" ON IF CMAKE_COMPILER_IS_GNUCXX ) OCV_OPTION(ENABLE_POWERPC "Enable PowerPC for GCC" ON IF (CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_PROCESSOR MATCHES powerpc.*) ) OCV_OPTION(ENABLE_FAST_MATH "Enable -ffast-math (not recommended for GCC 4.6.x)" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) -OCV_OPTION(ENABLE_SSE "Enable SSE instructions" ON IF (MSVC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) -OCV_OPTION(ENABLE_SSE2 "Enable SSE2 instructions" ON IF (MSVC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) -OCV_OPTION(ENABLE_SSE3 "Enable SSE3 instructions" ON IF (MSVC OR CV_ICC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) +OCV_OPTION(ENABLE_SSE "Enable SSE instructions" ON IF ((MSVC OR CMAKE_COMPILER_IS_GNUCXX) AND (X86 OR X86_64)) ) +OCV_OPTION(ENABLE_SSE2 "Enable SSE2 instructions" ON IF ((MSVC OR CMAKE_COMPILER_IS_GNUCXX) AND (X86 OR X86_64)) ) +OCV_OPTION(ENABLE_SSE3 "Enable SSE3 instructions" ON IF ((CV_ICC OR CMAKE_COMPILER_IS_GNUCXX) AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSSE3 "Enable SSSE3 instructions" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) -OCV_OPTION(ENABLE_SSE41 "Enable SSE4.1 instructions" OFF IF (CV_ICC OR CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) +OCV_OPTION(ENABLE_SSE41 "Enable SSE4.1 instructions" OFF IF ((CV_ICC OR CMAKE_COMPILER_IS_GNUCXX) AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_SSE42 "Enable SSE4.2 instructions" OFF IF (CMAKE_COMPILER_IS_GNUCXX AND (X86 OR X86_64)) ) OCV_OPTION(ENABLE_NOISY_WARNINGS "Show all warnings even if they are too noisy" OFF ) OCV_OPTION(OPENCV_WARNINGS_ARE_ERRORS "Treat warnings as errors" OFF ) diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp index e21b8d8021..c9abfefa9f 100644 --- a/modules/features2d/src/features2d_init.cpp +++ b/modules/features2d/src/features2d_init.cpp @@ -59,7 +59,7 @@ CV_INIT_ALGORITHM(BriefDescriptorExtractor, "Feature2D.BRIEF", CV_INIT_ALGORITHM(FastFeatureDetector, "Feature2D.FAST", obj.info()->addParam(obj, "threshold", obj.threshold); obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression); - obj.info()->addParam(obj, "type", obj.type, static_cast(FastFeatureDetector::TYPE_9_16))); + obj.info()->addParam(obj, "type", obj.type)); /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/modules/features2d/test/test_fast.cpp b/modules/features2d/test/test_fast.cpp index 671e66d5f4..761abfcca3 100644 --- a/modules/features2d/test/test_fast.cpp +++ b/modules/features2d/test/test_fast.cpp @@ -75,8 +75,8 @@ void CV_FastTest::run( int ) vector keypoints1; vector keypoints2; - FAST(gray1, keypoints1, 30, type); - FAST(gray2, keypoints2, 30, type); + FAST(gray1, keypoints1, 30, true, type); + FAST(gray2, keypoints2, 30, true, type); for(size_t i = 0; i < keypoints1.size(); ++i) { diff --git a/modules/features2d/test/test_nearestneighbors.cpp b/modules/features2d/test/test_nearestneighbors.cpp index 8a0482218f..45131eff2e 100644 --- a/modules/features2d/test/test_nearestneighbors.cpp +++ b/modules/features2d/test/test_nearestneighbors.cpp @@ -200,7 +200,7 @@ int CV_KDTreeTest_CPP::checkGetPoins( const Mat& data ) int CV_KDTreeTest_CPP::checkFindBoxed() { - vector min( dims, minValue), max(dims, maxValue); + vector min( dims, static_cast(minValue)), max(dims, static_cast(maxValue)); vector indices; tr->findOrthoRange( min, max, indices ); // TODO check indices @@ -214,8 +214,8 @@ int CV_KDTreeTest_CPP::findNeighbors( Mat& points, Mat& neighbors ) const int emax = 20; Mat neighbors2( neighbors.size(), CV_32SC1 ); int j; - vector min(points.cols, minValue); - vector max(points.cols, maxValue); + vector min(points.cols, static_cast(minValue)); + vector max(points.cols, static_cast(maxValue)); for( int pi = 0; pi < points.rows; pi++ ) { // 1st way diff --git a/modules/features2d/test/test_rotation_and_scale_invariance.cpp b/modules/features2d/test/test_rotation_and_scale_invariance.cpp index d98431ba57..48dddd3eee 100644 --- a/modules/features2d/test/test_rotation_and_scale_invariance.cpp +++ b/modules/features2d/test/test_rotation_and_scale_invariance.cpp @@ -54,7 +54,7 @@ static Mat generateHomography(float angle) { // angle - rotation around Oz in degrees - float angleRadian = angle * CV_PI / 180.; + float angleRadian = static_cast(angle * CV_PI / 180); Mat H = Mat::eye(3, 3, CV_32FC1); H.at(0,0) = H.at(1,1) = std::cos(angleRadian); H.at(0,1) = -std::sin(angleRadian); @@ -69,8 +69,8 @@ Mat rotateImage(const Mat& srcImage, float angle, Mat& dstImage, Mat& dstMask) // angle - rotation around Oz in degrees float diag = std::sqrt(static_cast(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows)); Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up - LUShift.at(0,2) = -srcImage.cols/2; - LUShift.at(1,2) = -srcImage.rows/2; + LUShift.at(0,2) = static_cast(-srcImage.cols/2); + LUShift.at(1,2) = static_cast(-srcImage.rows/2); Mat RDShift = Mat::eye(3, 3, CV_32FC1); // right down RDShift.at(0,2) = diag/2; RDShift.at(1,2) = diag/2; @@ -114,7 +114,7 @@ void scaleKeyPoints(const vector& src, vector& dst, float sc static float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1) { - float c = norm(p0 - p1), sqr_c = c * c; + float c = static_cast(norm(p0 - p1)), sqr_c = c * c; float sqr_r0 = r0 * r0; float sqr_r1 = r1 * r1; @@ -125,7 +125,7 @@ float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, f float minR = std::min(r0, r1); float maxR = std::max(r0, r1); if(c + minR <= maxR) - return CV_PI * minR * minR; + return static_cast(CV_PI * minR * minR); float cos_halfA0 = (sqr_r0 + sqr_c - sqr_r1) / (2 * r0 * c); float cos_halfA1 = (sqr_r1 + sqr_c - sqr_r0) / (2 * r1 * c); @@ -133,15 +133,15 @@ float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, f float A0 = 2 * acos(cos_halfA0); float A1 = 2 * acos(cos_halfA1); - return 0.5 * sqr_r0 * (A0 - sin(A0)) + - 0.5 * sqr_r1 * (A1 - sin(A1)); + return 0.5f * sqr_r0 * (A0 - sin(A0)) + + 0.5f * sqr_r1 * (A1 - sin(A1)); } static float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r1) { float intersectArea = calcCirclesIntersectArea(p0, r0, p1, r1); - float unionArea = CV_PI * (r0 * r0 + r1 * r1) - intersectArea; + float unionArea = static_cast(CV_PI) * (r0 * r0 + r1 * r1) - intersectArea; return intersectArea / unionArea; } @@ -160,7 +160,7 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, matches.clear(); vector usedMask(keypoints1.size(), 0); - for(size_t i0 = 0; i0 < keypoints0.size(); i0++) + for(int i0 = 0; i0 < static_cast(keypoints0.size()); i0++) { int nearestPointIndex = -1; float maxIntersectRatio = 0.f; @@ -176,7 +176,7 @@ void matchKeyPoints(const vector& keypoints0, const Mat& H, if(intersectRatio > maxIntersectRatio) { maxIntersectRatio = intersectRatio; - nearestPointIndex = i1; + nearestPointIndex = static_cast(i1); } } @@ -222,7 +222,7 @@ protected: const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) { - Mat H = rotateImage(image0, angle, image1, mask1); + Mat H = rotateImage(image0, static_cast(angle), image1, mask1); vector keypoints1; featureDetector->detect(image1, keypoints1, mask1); @@ -339,10 +339,10 @@ protected: const int maxAngle = 360, angleStep = 15; for(int angle = 0; angle < maxAngle; angle += angleStep) { - Mat H = rotateImage(image0, angle, image1, mask1); + Mat H = rotateImage(image0, static_cast(angle), image1, mask1); vector keypoints1; - rotateKeyPoints(keypoints0, H, angle, keypoints1); + rotateKeyPoints(keypoints0, H, static_cast(angle), keypoints1); Mat descriptors1; descriptorExtractor->compute(image1, keypoints1, descriptors1); @@ -457,7 +457,7 @@ protected: keyPointMatchesCount++; // Check does this inlier have consistent sizes - const float maxSizeDiff = 0.8;//0.9f; // grad + const float maxSizeDiff = 0.8f;//0.9f; // grad float size0 = keypoints0[matches[m].trainIdx].size; float size1 = osiKeypoints1[matches[m].queryIdx].size; CV_Assert(size0 > 0 && size1 > 0); @@ -545,7 +545,7 @@ protected: resize(image0, image1, Size(), 1./scale, 1./scale); vector keypoints1; - scaleKeyPoints(keypoints0, keypoints1, 1./scale); + scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale); Mat descriptors1; descriptorExtractor->compute(image1, keypoints1, descriptors1); diff --git a/modules/gpu/perf/perf_imgproc.cpp b/modules/gpu/perf/perf_imgproc.cpp index 979aaa3723..f938ca239e 100644 --- a/modules/gpu/perf/perf_imgproc.cpp +++ b/modules/gpu/perf/perf_imgproc.cpp @@ -23,8 +23,8 @@ void generateMap(cv::Mat& map_x, cv::Mat& map_y, int remapMode) case HALF_SIZE: if (i > map_x.cols*0.25 && i < map_x.cols*0.75 && j > map_x.rows*0.25 && j < map_x.rows*0.75) { - map_x.at(j,i) = 2 * (i - map_x.cols * 0.25) + 0.5; - map_y.at(j,i) = 2 * (j - map_x.rows * 0.25) + 0.5; + map_x.at(j,i) = 2 * (i - map_x.cols * 0.25f) + 0.5f; + map_y.at(j,i) = 2 * (j - map_x.rows * 0.25f) + 0.5f; } else { @@ -33,16 +33,16 @@ void generateMap(cv::Mat& map_x, cv::Mat& map_y, int remapMode) } break; case UPSIDE_DOWN: - map_x.at(j,i) = i; - map_y.at(j,i) = map_x.rows - j; + map_x.at(j,i) = static_cast(i); + map_y.at(j,i) = static_cast(map_x.rows - j); break; case REFLECTION_X: - map_x.at(j,i) = map_x.cols - i; - map_y.at(j,i) = j; + map_x.at(j,i) = static_cast(map_x.cols - i); + map_y.at(j,i) = static_cast(j); break; case REFLECTION_BOTH: - map_x.at(j,i) = map_x.cols - i; - map_y.at(j,i) = map_x.rows - j; + map_x.at(j,i) = static_cast(map_x.cols - i); + map_y.at(j,i) = static_cast(map_x.rows - j); break; } // end of switch } @@ -1619,7 +1619,7 @@ PERF_TEST_P(Sz_DoSort, ImgProc_HoughLines, Combine(GPU_TYPICAL_MAT_SIZES, Bool() const bool doSort = GET_PARAM(1); const float rho = 1.0f; - const float theta = CV_PI / 180.0f; + const float theta = static_cast(CV_PI / 180.0); const int threshold = 300; cv::RNG rng(123456789); diff --git a/modules/gpu/perf/perf_labeling.cpp b/modules/gpu/perf/perf_labeling.cpp index f17dd7d0af..bbab5ecfa4 100644 --- a/modules/gpu/perf/perf_labeling.cpp +++ b/modules/gpu/perf/perf_labeling.cpp @@ -31,6 +31,11 @@ struct GreedyLabeling int d = a - b; return lo <= d && d <= hi; } + + private: + InInterval& operator=(const InInterval&); + + }; GreedyLabeling(cv::Mat img) @@ -45,7 +50,7 @@ struct GreedyLabeling int cc = -1; int* dist_labels = (int*)labels.data; - int pitch = labels.step1(); + int pitch = static_cast(labels.step1()); unsigned char* source = (unsigned char*)image.data; int width = image.cols; @@ -82,7 +87,7 @@ struct GreedyLabeling *top++ = dot::make(p.x, p.y + 1); //top - if( p.y > 0 && dl[-pitch] == -1 && inInt(sp[0], sp[-image.step1()])) + if( p.y > 0 && dl[-pitch] == -1 && inInt(sp[0], sp[-static_cast(image.step1())])) *top++ = dot::make(p.x, p.y - 1); p = *--top; diff --git a/modules/gpu/perf/perf_video.cpp b/modules/gpu/perf/perf_video.cpp index 7faea0b880..8346d25f42 100644 --- a/modules/gpu/perf/perf_video.cpp +++ b/modules/gpu/perf/perf_video.cpp @@ -427,7 +427,7 @@ PERF_TEST_P(Video_Cn_LearningRate, Video_MOG, Combine(Values("gpu/video/768x576. { string inputFile = perf::TestBase::getDataPath(GET_PARAM(0)); int cn = GET_PARAM(1); - double learningRate = GET_PARAM(2); + float learningRate = static_cast(GET_PARAM(2)); cv::VideoCapture cap(inputFile); ASSERT_TRUE(cap.isOpened()); diff --git a/samples/cpp/bagofwords_classification.cpp b/samples/cpp/bagofwords_classification.cpp index 559e77bfcf..071c2a1733 100644 --- a/samples/cpp/bagofwords_classification.cpp +++ b/samples/cpp/bagofwords_classification.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #if defined WIN32 || defined _WIN32 #define WIN32_LEAN_AND_MEAN diff --git a/samples/gpu/bgfg_segm.cpp b/samples/gpu/bgfg_segm.cpp index 7c5e148f77..4e4c52096c 100644 --- a/samples/gpu/bgfg_segm.cpp +++ b/samples/gpu/bgfg_segm.cpp @@ -86,7 +86,7 @@ int main(int argc, const char** argv) break; case MOG: - mog(d_frame, d_fgmask, 0.01); + mog(d_frame, d_fgmask, 0.01f); break; case MOG2: @@ -127,7 +127,7 @@ int main(int argc, const char** argv) break; case MOG: - mog(d_frame, d_fgmask, 0.01); + mog(d_frame, d_fgmask, 0.01f); mog.getBackgroundImage(d_bgimg); break; @@ -162,7 +162,7 @@ int main(int argc, const char** argv) if (!bgimg.empty()) imshow("mean background image", bgimg); - char key = waitKey(30); + int key = waitKey(30); if (key == 27) break; } diff --git a/samples/gpu/performance/tests.cpp b/samples/gpu/performance/tests.cpp index aefd9572f6..e117202838 100644 --- a/samples/gpu/performance/tests.cpp +++ b/samples/gpu/performance/tests.cpp @@ -1341,7 +1341,7 @@ TEST(MOG) cv::gpu::MOG_GPU d_mog; cv::gpu::GpuMat d_foreground; - d_mog(d_frame, d_foreground, 0.01); + d_mog(d_frame, d_foreground, 0.01f); while (!TestSystem::instance().stop()) { @@ -1350,7 +1350,7 @@ TEST(MOG) TestSystem::instance().gpuOn(); - d_mog(d_frame, d_foreground, 0.01); + d_mog(d_frame, d_foreground, 0.01f); TestSystem::instance().gpuOff(); } From 92795ba4769e77a9833a06fcd67c444f3c9c2122 Mon Sep 17 00:00:00 2001 From: Ilya Lavrenov Date: Wed, 22 Aug 2012 11:49:21 +0400 Subject: [PATCH 50/58] parallel version of remap, resize, warpaffine, warpPerspective. Some optimization for 2x decimation in resize algorithm --- modules/imgproc/perf/perf_remap.cpp | 68 + modules/imgproc/perf/perf_resize.cpp | 32 +- modules/imgproc/src/imgwarp.cpp | 1803 +++++++++++++++----------- 3 files changed, 1157 insertions(+), 746 deletions(-) create mode 100644 modules/imgproc/perf/perf_remap.cpp diff --git a/modules/imgproc/perf/perf_remap.cpp b/modules/imgproc/perf/perf_remap.cpp new file mode 100644 index 0000000000..e789296735 --- /dev/null +++ b/modules/imgproc/perf/perf_remap.cpp @@ -0,0 +1,68 @@ +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace perf; +using namespace testing; +using std::tr1::make_tuple; +using std::tr1::get; + +CV_ENUM(MatrixType, CV_16UC1, CV_16SC1, CV_32FC1) +CV_ENUM(MapType, CV_16SC2, CV_32FC1, CV_32FC2) +CV_ENUM(InterType, INTER_LINEAR, INTER_CUBIC, INTER_LANCZOS4, INTER_NEAREST) + +typedef TestBaseWithParam< tr1::tuple > TestRemap; + +PERF_TEST_P( TestRemap, Remap, + Combine( + Values( szVGA, sz1080p ), + ValuesIn( MatrixType::all() ), + ValuesIn( MapType::all() ), + ValuesIn( InterType::all() ) + ) +) +{ + Size sz; + int src_type, map1_type, inter_type; + + sz = get<0>(GetParam()); + src_type = get<1>(GetParam()); + map1_type = get<2>(GetParam()); + inter_type = get<3>(GetParam()); + + Mat src(sz, src_type); + Mat map1(sz, map1_type); + Mat dst(sz, src_type); + + Mat map2(map1_type == CV_32FC1 ? sz : Size(), CV_32FC1); + + RNG rng; + rng.fill(src, RNG::UNIFORM, 0, 256); + + for (int j = 0; j < map1.rows; ++j) + for (int i = 0; i < map1.cols; ++i) + switch (map1_type) + { + case CV_32FC1: + map1.at(j, i) = src.cols - i; + map2.at(j, i) = j; + break; + case CV_32FC2: + map1.at(j, i)[0] = src.cols - i; + map1.at(j, i)[1] = j; + break; + case CV_16SC2: + map1.at(j, i)[0] = src.cols - i; + map1.at(j, i)[1] = j; + break; + default: + CV_Assert(0); + } + + + declare.in(src, WARMUP_RNG).out(dst).time(20); + + TEST_CYCLE() remap(src, dst, map1, map2, inter_type); + + SANITY_CHECK(dst); +} diff --git a/modules/imgproc/perf/perf_resize.cpp b/modules/imgproc/perf/perf_resize.cpp index fba12942f2..15be0eae17 100644 --- a/modules/imgproc/perf/perf_resize.cpp +++ b/modules/imgproc/perf/perf_resize.cpp @@ -59,11 +59,11 @@ PERF_TEST_P(MatInfo_Size_Size, resizeDownLinear, typedef tr1::tuple MatInfo_Size_Scale_t; typedef TestBaseWithParam MatInfo_Size_Scale; -PERF_TEST_P(MatInfo_Size_Scale, resizeAreaFast, +PERF_TEST_P(MatInfo_Size_Scale, ResizeAreaFast, testing::Combine( testing::Values(CV_8UC1, CV_8UC4), testing::Values(szVGA, szqHD, sz720p, sz1080p), - testing::Values(2, 4) + testing::Values(2) ) ) { @@ -84,3 +84,31 @@ PERF_TEST_P(MatInfo_Size_Scale, resizeAreaFast, //difference equal to 1 is allowed because of different possible rounding modes: round-to-nearest vs bankers' rounding SANITY_CHECK(dst, 1); } + + +typedef TestBaseWithParam > MatInfo_Size_Scale_Area; + +PERF_TEST_P(MatInfo_Size_Scale_Area, ResizeArea, + testing::Combine( + testing::Values(CV_8UC1, CV_8UC4), + testing::Values(szVGA, szqHD, sz720p, sz1080p), + testing::Values(2.4, 3.4, 1.3) + ) + ) +{ + int matType = get<0>(GetParam()); + Size from = get<1>(GetParam()); + double scale = get<2>(GetParam()); + + cv::Mat src(from, matType); + + Size to(cvRound(from.width * scale), cvRound(from.height * scale)); + cv::Mat dst(to, matType); + + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() resize(src, dst, dst.size(), 0, 0, INTER_AREA); + + //difference equal to 1 is allowed because of different possible rounding modes: round-to-nearest vs bankers' rounding + SANITY_CHECK(dst, 1); +} diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index e5a47e1a2d..1e45fadf26 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -240,6 +240,99 @@ template struct FixedPtCast * Resize * \****************************************************************************************/ +class resizeNNInvoker : + public ParallelLoopBody +{ +public: + resizeNNInvoker(const Mat& _src, Mat &_dst, int *_x_ofs, int _pix_size4, double _ify) : + ParallelLoopBody(), src(_src), dst(_dst), x_ofs(_x_ofs), pix_size4(_pix_size4), + ify(_ify) + { + } + + virtual void operator() (const Range& range) const + { + Size ssize = src.size(), dsize = dst.size(); + int y, x, pix_size = (int)src.elemSize(); + + for( y = range.start; y < range.end; y++ ) + { + uchar* D = dst.data + dst.step*y; + int sy = std::min(cvFloor(y*ify), ssize.height-1); + const uchar* S = src.data + src.step*sy; + + switch( pix_size ) + { + case 1: + for( x = 0; x <= dsize.width - 2; x += 2 ) + { + uchar t0 = S[x_ofs[x]]; + uchar t1 = S[x_ofs[x+1]]; + D[x] = t0; + D[x+1] = t1; + } + + for( ; x < dsize.width; x++ ) + D[x] = S[x_ofs[x]]; + break; + case 2: + for( x = 0; x < dsize.width; x++ ) + *(ushort*)(D + x*2) = *(ushort*)(S + x_ofs[x]); + break; + case 3: + for( x = 0; x < dsize.width; x++, D += 3 ) + { + const uchar* _tS = S + x_ofs[x]; + D[0] = _tS[0]; D[1] = _tS[1]; D[2] = _tS[2]; + } + break; + case 4: + for( x = 0; x < dsize.width; x++ ) + *(int*)(D + x*4) = *(int*)(S + x_ofs[x]); + break; + case 6: + for( x = 0; x < dsize.width; x++, D += 6 ) + { + const ushort* _tS = (const ushort*)(S + x_ofs[x]); + ushort* _tD = (ushort*)D; + _tD[0] = _tS[0]; _tD[1] = _tS[1]; _tD[2] = _tS[2]; + } + break; + case 8: + for( x = 0; x < dsize.width; x++, D += 8 ) + { + const int* _tS = (const int*)(S + x_ofs[x]); + int* _tD = (int*)D; + _tD[0] = _tS[0]; _tD[1] = _tS[1]; + } + break; + case 12: + for( x = 0; x < dsize.width; x++, D += 12 ) + { + const int* _tS = (const int*)(S + x_ofs[x]); + int* _tD = (int*)D; + _tD[0] = _tS[0]; _tD[1] = _tS[1]; _tD[2] = _tS[2]; + } + break; + default: + for( x = 0; x < dsize.width; x++, D += pix_size ) + { + const int* _tS = (const int*)(S + x_ofs[x]); + int* _tD = (int*)D; + for( int k = 0; k < pix_size4; k++ ) + _tD[k] = _tS[k]; + } + } + } + } + +private: + const Mat src; + Mat dst; + int* x_ofs, pix_size4; + double ify; +}; + static void resizeNN( const Mat& src, Mat& dst, double fx, double fy ) { @@ -249,83 +342,17 @@ resizeNN( const Mat& src, Mat& dst, double fx, double fy ) int pix_size = (int)src.elemSize(); int pix_size4 = (int)(pix_size / sizeof(int)); double ifx = 1./fx, ify = 1./fy; - int x, y; + int x; for( x = 0; x < dsize.width; x++ ) { int sx = cvFloor(x*ifx); x_ofs[x] = std::min(sx, ssize.width-1)*pix_size; } - - for( y = 0; y < dsize.height; y++ ) - { - uchar* D = dst.data + dst.step*y; - int sy = std::min(cvFloor(y*ify), ssize.height-1); - const uchar* S = src.data + src.step*sy; - - switch( pix_size ) - { - case 1: - for( x = 0; x <= dsize.width - 2; x += 2 ) - { - uchar t0 = S[x_ofs[x]]; - uchar t1 = S[x_ofs[x+1]]; - D[x] = t0; - D[x+1] = t1; - } - - for( ; x < dsize.width; x++ ) - D[x] = S[x_ofs[x]]; - break; - case 2: - for( x = 0; x < dsize.width; x++ ) - *(ushort*)(D + x*2) = *(ushort*)(S + x_ofs[x]); - break; - case 3: - for( x = 0; x < dsize.width; x++, D += 3 ) - { - const uchar* _tS = S + x_ofs[x]; - D[0] = _tS[0]; D[1] = _tS[1]; D[2] = _tS[2]; - } - break; - case 4: - for( x = 0; x < dsize.width; x++ ) - *(int*)(D + x*4) = *(int*)(S + x_ofs[x]); - break; - case 6: - for( x = 0; x < dsize.width; x++, D += 6 ) - { - const ushort* _tS = (const ushort*)(S + x_ofs[x]); - ushort* _tD = (ushort*)D; - _tD[0] = _tS[0]; _tD[1] = _tS[1]; _tD[2] = _tS[2]; - } - break; - case 8: - for( x = 0; x < dsize.width; x++, D += 8 ) - { - const int* _tS = (const int*)(S + x_ofs[x]); - int* _tD = (int*)D; - _tD[0] = _tS[0]; _tD[1] = _tS[1]; - } - break; - case 12: - for( x = 0; x < dsize.width; x++, D += 12 ) - { - const int* _tS = (const int*)(S + x_ofs[x]); - int* _tD = (int*)D; - _tD[0] = _tS[0]; _tD[1] = _tS[1]; _tD[2] = _tS[2]; - } - break; - default: - for( x = 0; x < dsize.width; x++, D += pix_size ) - { - const int* _tS = (const int*)(S + x_ofs[x]); - int* _tD = (int*)D; - for( int k = 0; k < pix_size4; k++ ) - _tD[k] = _tS[k]; - } - } - } + + Range range(0, dsize.height); + resizeNNInvoker invoker(src, dst, x_ofs, pix_size4, ify); + parallel_for_(range, invoker); } @@ -1092,6 +1119,82 @@ static inline int clip(int x, int a, int b) static const int MAX_ESIZE=16; +template +class resizeGeneric_Invoker : + public ParallelLoopBody +{ +public: + typedef typename HResize::value_type T; + typedef typename HResize::buf_type WT; + typedef typename HResize::alpha_type AT; + + resizeGeneric_Invoker(const Mat& _src, Mat &_dst, const int *_xofs, const int *_yofs, + const AT* _alpha, const AT* __beta, const Size& _ssize, const Size &_dsize, + int _ksize, int _xmin, int _xmax) : + ParallelLoopBody(), src(_src), dst(_dst), xofs(_xofs), yofs(_yofs), + alpha(_alpha), _beta(__beta), ssize(_ssize), dsize(_dsize), + ksize(_ksize), xmin(_xmin), xmax(_xmax) + { + } + + virtual void operator() (const Range& range) const + { + int dy, cn = src.channels(); + HResize hresize; + VResize vresize; + + int bufstep = (int)alignSize(dsize.width, 16); + AutoBuffer _buffer(bufstep*ksize); + const T* srows[MAX_ESIZE]={0}; + WT* rows[MAX_ESIZE]={0}; + int prev_sy[MAX_ESIZE]; + + for(int k = 0; k < ksize; k++ ) + { + prev_sy[k] = -1; + rows[k] = (WT*)_buffer + bufstep*k; + } + + const AT* beta = _beta + ksize * range.start; + + for( dy = range.start; dy < range.end; dy++, beta += ksize ) + { + int sy0 = yofs[dy], k0=ksize, k1=0, ksize2 = ksize/2; + + for(int k = 0; k < ksize; k++ ) + { + int sy = clip(sy0 - ksize2 + 1 + k, 0, ssize.height); + for( k1 = std::max(k1, k); k1 < ksize; k1++ ) + { + if( sy == prev_sy[k1] ) // if the sy-th row has been computed already, reuse it. + { + if( k1 > k ) + memcpy( rows[k], rows[k1], bufstep*sizeof(rows[0][0]) ); + break; + } + } + if( k1 == ksize ) + k0 = std::min(k0, k); // remember the first row that needs to be computed + srows[k] = (T*)(src.data + src.step*sy); + prev_sy[k] = sy; + } + + if( k0 < ksize ) + hresize( (const T**)(srows + k0), (WT**)(rows + k0), ksize - k0, xofs, (const AT*)(alpha), + ssize.width, dsize.width, cn, xmin, xmax ); + vresize( (const WT**)rows, (T*)(dst.data + dst.step*dy), beta, dsize.width ); + } + } + +private: + const Mat src; + Mat dst; + const int* xofs, *yofs; + const AT* alpha, *_beta; + const Size ssize, dsize; + const int ksize, xmin, xmax; +}; + template static void resizeGeneric_( const Mat& src, Mat& dst, const int* xofs, const void* _alpha, @@ -1102,124 +1205,178 @@ static void resizeGeneric_( const Mat& src, Mat& dst, typedef typename HResize::buf_type WT; typedef typename HResize::alpha_type AT; - const AT* alpha = (const AT*)_alpha; const AT* beta = (const AT*)_beta; Size ssize = src.size(), dsize = dst.size(); int cn = src.channels(); ssize.width *= cn; dsize.width *= cn; - int bufstep = (int)alignSize(dsize.width, 16); - AutoBuffer _buffer(bufstep*ksize); - const T* srows[MAX_ESIZE]={0}; - WT* rows[MAX_ESIZE]={0}; - int prev_sy[MAX_ESIZE]; - int dy; xmin *= cn; xmax *= cn; + // image resize is a separable operation. In case of not too strong + + Range range(0, dsize.height); + resizeGeneric_Invoker invoker(src, dst, xofs, yofs, (const AT*)_alpha, beta, + ssize, dsize, ksize, xmin, xmax); + parallel_for_(range, invoker); +} - HResize hresize; - VResize vresize; +template +struct ResizeAreaFastNoVec +{ + ResizeAreaFastNoVec(int /*_scale_x*/, int /*_scale_y*/, + int /*_cn*/, int /*_step*//*, const int**/ /*_ofs*/) { } + int operator() (const T* /*S*/, T* /*D*/, int /*w*/) const { return 0; } +}; - for(int k = 0; k < ksize; k++ ) - { - prev_sy[k] = -1; - rows[k] = (WT*)_buffer + bufstep*k; +template +struct ResizeAreaFast_2x2_8u +{ + ResizeAreaFast_2x2_8u(int _scale_x, int _scale_y, int _cn, int _step/*, const int* _ofs*/) : + scale_x(_scale_x), scale_y(_scale_y), cn(_cn), step(_step)/*, ofs(_ofs)*/ + { + fast_mode = scale_x == 2 && scale_y == 2 && (cn == 1 || cn == 3 || cn == 4); } - - // image resize is a separable operation. In case of not too strong - for( dy = 0; dy < dsize.height; dy++, beta += ksize ) + + int operator() (const T* S, T* D, int w) const { - int sy0 = yofs[dy], k0=ksize, k1=0, ksize2 = ksize/2; - - for(int k = 0; k < ksize; k++ ) + if( !fast_mode ) + return 0; + + const T* nextS = S + step; + int dx = 0; + + if (cn == 1) + for( ; dx < w; ++dx ) + { + int index = dx*2; + D[dx] = (S[index] + S[index+1] + nextS[index] + nextS[index+1] + 2) >> 2; + } + else if (cn == 3) + for( ; dx < w; dx += 3 ) + { + int index = dx*2; + D[dx] = (S[index] + S[index+3] + nextS[index] + nextS[index+3] + 2) >> 2; + D[dx+1] = (S[index+1] + S[index+4] + nextS[index+1] + nextS[index+4] + 2) >> 2; + D[dx+2] = (S[index+2] + S[index+5] + nextS[index+2] + nextS[index+5] + 2) >> 2; + } + else { - int sy = clip(sy0 - ksize2 + 1 + k, 0, ssize.height); - for( k1 = std::max(k1, k); k1 < ksize; k1++ ) + assert(cn == 4); + for( ; dx < w; dx += 4 ) { - if( sy == prev_sy[k1] ) // if the sy-th row has been computed already, reuse it. - { - if( k1 > k ) - memcpy( rows[k], rows[k1], bufstep*sizeof(rows[0][0]) ); - break; - } + int index = dx*2; + D[dx] = (S[index] + S[index+3] + nextS[index] + nextS[index+3] + 2) >> 2; + D[dx+1] = (S[index+1] + S[index+4] + nextS[index+1] + nextS[index+4] + 2) >> 2; + D[dx+2] = (S[index+2] + S[index+5] + nextS[index+2] + nextS[index+5] + 2) >> 2; + D[dx+3] = (S[index+3] + S[index+6] + nextS[index+3] + nextS[index+6] + 2) >> 2; } - if( k1 == ksize ) - k0 = std::min(k0, k); // remember the first row that needs to be computed - srows[k] = (const T*)(src.data + src.step*sy); - prev_sy[k] = sy; } - - if( k0 < ksize ) - hresize( srows + k0, rows + k0, ksize - k0, xofs, alpha, - ssize.width, dsize.width, cn, xmin, xmax ); - vresize( (const WT**)rows, (T*)(dst.data + dst.step*dy), beta, dsize.width ); + + return dx; } -} - + +private: + const int scale_x, scale_y; + const int cn; + bool fast_mode; + const int step; +}; -template -static void resizeAreaFast_( const Mat& src, Mat& dst, const int* ofs, const int* xofs, - int scale_x, int scale_y ) +template +class resizeAreaFast_Invoker : + public ParallelLoopBody { - Size ssize = src.size(), dsize = dst.size(); - int cn = src.channels(); - int dy, dx, k = 0; - int area = scale_x*scale_y; - float scale = 1.f/(scale_x*scale_y); - int dwidth1 = (ssize.width/scale_x)*cn; - dsize.width *= cn; - ssize.width *= cn; - - for( dy = 0; dy < dsize.height; dy++ ) +public: + resizeAreaFast_Invoker(const Mat &_src, Mat &_dst, + int _scale_x, int _scale_y, const int* _ofs, const int* _xofs) : + ParallelLoopBody(), src(_src), dst(_dst), scale_x(_scale_x), + scale_y(_scale_y), ofs(_ofs), xofs(_xofs) { - T* D = (T*)(dst.data + dst.step*dy); - int sy0 = dy*scale_y, w = sy0 + scale_y <= ssize.height ? dwidth1 : 0; - if( sy0 >= ssize.height ) + } + + virtual void operator() (const Range& range) const + { + Size ssize = src.size(), dsize = dst.size(); + int cn = src.channels(); + int area = scale_x*scale_y; + float scale = 1.f/(area); + int dwidth1 = (ssize.width/scale_x)*cn; + dsize.width *= cn; + ssize.width *= cn; + int dy, dx, k = 0; + + VecOp vop(scale_x, scale_y, src.channels(), src.step/*, area_ofs*/); + + for( dy = range.start; dy < range.end; dy++ ) { - for( dx = 0; dx < dsize.width; dx++ ) - D[dx] = 0; - continue; - } + T* D = (T*)(dst.data + dst.step*dy); + int sy0 = dy*scale_y; + int w = sy0 + scale_y <= ssize.height ? dwidth1 : 0; + + if( sy0 >= ssize.height ) + { + for( dx = 0; dx < dsize.width; dx++ ) + D[dx] = 0; + continue; + } - for( dx = 0; dx < w; dx++ ) - { - const T* S = (const T*)(src.data + src.step*sy0) + xofs[dx]; - WT sum = 0; - k=0; - #if CV_ENABLE_UNROLLED - for( ; k <= area - 4; k += 4 ) - sum += S[ofs[k]] + S[ofs[k+1]] + S[ofs[k+2]] + S[ofs[k+3]]; - #endif - for( ; k < area; k++ ) - sum += S[ofs[k]]; - - D[dx] = saturate_cast(sum*scale); - } + dx = vop((const T*)(src.data + src.step * sy0), D, w); + for( ; dx < w; dx++ ) + { + const T* S = (const T*)(src.data + src.step * sy0) + xofs[dx]; + WT sum = 0; + k = 0; + #if CV_ENABLE_UNROLLED + for( ; k <= area - 4; k += 4 ) + sum += S[ofs[k]] + S[ofs[k+1]] + S[ofs[k+2]] + S[ofs[k+3]]; + #endif + for( ; k < area; k++ ) + sum += S[ofs[k]]; - for( ; dx < dsize.width; dx++ ) - { - WT sum = 0; - int count = 0, sx0 = xofs[dx]; - if( sx0 >= ssize.width ) - D[dx] = 0; + D[dx] = saturate_cast(sum * scale); + } - for( int sy = 0; sy < scale_y; sy++ ) + for( ; dx < dsize.width; dx++ ) { - if( sy0 + sy >= ssize.height ) - break; - const T* S = (const T*)(src.data + src.step*(sy0 + sy)) + sx0; - for( int sx = 0; sx < scale_x*cn; sx += cn ) + WT sum = 0; + int count = 0, sx0 = xofs[dx]; + if( sx0 >= ssize.width ) + D[dx] = 0; + + for( int sy = 0; sy < scale_y; sy++ ) { - if( sx0 + sx >= ssize.width ) + if( sy0 + sy >= ssize.height ) break; - sum += S[sx]; - count++; + const T* S = (const T*)(src.data + src.step*(sy0 + sy)) + sx0; + for( int sx = 0; sx < scale_x*cn; sx += cn ) + { + if( sx0 + sx >= ssize.width ) + break; + sum += S[sx]; + count++; + } } - } - D[dx] = saturate_cast((float)sum/count); + D[dx] = saturate_cast((float)sum/count); + } } } + +private: + const Mat src; + Mat dst; + const int scale_x, scale_y; + const int *ofs, *xofs; +}; + +template +static void resizeAreaFast_( const Mat& src, Mat& dst, const int* ofs, const int* xofs, + int scale_x, int scale_y ) +{ + Range range(0, dst.rows); + resizeAreaFast_Invoker invoker(src, dst, scale_x, + scale_y, ofs, xofs); + parallel_for_(range, invoker); } struct DecimateAlpha @@ -1228,222 +1385,260 @@ struct DecimateAlpha float alpha; }; -template -static void resizeArea_( const Mat& src, Mat& dst, const DecimateAlpha* xofs, int xofs_count, double scale_y_) +template +class resizeArea_Invoker : + public ParallelLoopBody { - Size ssize = src.size(), dsize = dst.size(); - int cn = src.channels(); - dsize.width *= cn; - AutoBuffer _buffer(dsize.width*2); - WT *buf = _buffer, *sum = buf + dsize.width; - int k, sy, dx, cur_dy = 0; - WT scale_y = (WT)scale_y_; - - CV_Assert( cn <= 4 ); - for( dx = 0; dx < dsize.width; dx++ ) - buf[dx] = sum[dx] = 0; - - for( sy = 0; sy < ssize.height; sy++ ) +public: + resizeArea_Invoker(const Mat& _src, Mat& _dst, const DecimateAlpha* _xofs, + int _xofs_count, double _scale_y_ +#ifdef HAVE_TBB + , const int* _yofs, const int* _cur_dy_ofs +#endif + ) : + ParallelLoopBody(), src(_src), dst(_dst), xofs(_xofs), + xofs_count(_xofs_count), scale_y_(_scale_y_) +#ifdef HAVE_TBB + , yofs(_yofs), cur_dy_ofs(_cur_dy_ofs) +#endif { - const T* S = (const T*)(src.data + src.step*sy); - if( cn == 1 ) - for( k = 0; k < xofs_count; k++ ) - { - int dxn = xofs[k].di; - WT alpha = xofs[k].alpha; - buf[dxn] += S[xofs[k].si]*alpha; - } - else if( cn == 2 ) - for( k = 0; k < xofs_count; k++ ) - { - int sxn = xofs[k].si; - int dxn = xofs[k].di; - WT alpha = xofs[k].alpha; - WT t0 = buf[dxn] + S[sxn]*alpha; - WT t1 = buf[dxn+1] + S[sxn+1]*alpha; - buf[dxn] = t0; buf[dxn+1] = t1; - } - else if( cn == 3 ) - for( k = 0; k < xofs_count; k++ ) - { - int sxn = xofs[k].si; - int dxn = xofs[k].di; - WT alpha = xofs[k].alpha; - WT t0 = buf[dxn] + S[sxn]*alpha; - WT t1 = buf[dxn+1] + S[sxn+1]*alpha; - WT t2 = buf[dxn+2] + S[sxn+2]*alpha; - buf[dxn] = t0; buf[dxn+1] = t1; buf[dxn+2] = t2; - } - else - for( k = 0; k < xofs_count; k++ ) - { - int sxn = xofs[k].si; - int dxn = xofs[k].di; - WT alpha = xofs[k].alpha; - WT t0 = buf[dxn] + S[sxn]*alpha; - WT t1 = buf[dxn+1] + S[sxn+1]*alpha; - buf[dxn] = t0; buf[dxn+1] = t1; - t0 = buf[dxn+2] + S[sxn+2]*alpha; - t1 = buf[dxn+3] + S[sxn+3]*alpha; - buf[dxn+2] = t0; buf[dxn+3] = t1; - } - - if( (cur_dy + 1)*scale_y <= sy + 1 || sy == ssize.height - 1 ) + } + + virtual void operator() (const Range& range) const + { + Size ssize = src.size(), dsize = dst.size(); + int cn = src.channels(); + dsize.width *= cn; + AutoBuffer _buffer(dsize.width*2); + WT *buf = _buffer, *sum = buf + dsize.width; + int k, sy, dx, cur_dy = 0, num = sizeof(WT) * dsize.width; + WT scale_y = (WT)scale_y_; + + CV_Assert( cn <= 4 ); + memset(buf, 0, num * 2); + +#ifdef HAVE_TBB + sy = yofs[range.start]; + cur_dy = cur_dy_ofs[sy]; + for( ; sy < range.start; sy++ ) { - WT beta = std::max(sy + 1 - (cur_dy+1)*scale_y, (WT)0); - WT beta1 = 1 - beta; - T* D = (T*)(dst.data + dst.step*cur_dy); - if( fabs(beta) < 1e-3 ) + const T* S = (const T*)(src.data + src.step * sy); + memset(buf, 0, num); + + if( cn == 1 ) + for( k = 0; k < xofs_count; k++ ) + { + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + buf[dxn] += S[xofs[k].si]*alpha; + } + else if( cn == 2 ) + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + buf[dxn] = t0; buf[dxn+1] = t1; + } + else if( cn == 3 ) + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + WT t2 = buf[dxn+2] + S[sxn+2]*alpha; + + buf[dxn] = t0; buf[dxn+1] = t1; buf[dxn+2] = t2; + } + else + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + + buf[dxn] = t0; buf[dxn+1] = t1; + + t0 = buf[dxn+2] + S[sxn+2]*alpha; + t1 = buf[dxn+3] + S[sxn+3]*alpha; + + buf[dxn+2] = t0; buf[dxn+3] = t1; + } + + if( (cur_dy + 1)*scale_y <= sy + 1 || sy == ssize.height - 1 ) { - if(cur_dy >= dsize.height) return; - for( dx = 0; dx < dsize.width; dx++ ) + WT beta = std::max(sy + 1 - (cur_dy + 1) * scale_y, (WT)0); + if( fabs(beta) < 1e-3 ) { - D[dx] = saturate_cast((sum[dx] + buf[dx]) / min(scale_y, src.rows - cur_dy * scale_y)); - sum[dx] = buf[dx] = 0; + if(cur_dy >= dsize.height) + break; + memset(sum, 0, num); } + else + for( dx = 0; dx < dsize.width; dx++ ) + sum[dx] = buf[dx] * beta; + cur_dy++; } else - for( dx = 0; dx < dsize.width; dx++ ) + { + for( dx = 0; dx <= dsize.width - 2; dx += 2 ) { - D[dx] = saturate_cast((sum[dx] + buf[dx]* beta1)/ min(scale_y, src.rows - cur_dy*scale_y)); - sum[dx] = buf[dx]*beta; - buf[dx] = 0; + WT t0 = sum[dx] + buf[dx]; + WT t1 = sum[dx+1] + buf[dx+1]; + sum[dx] = t0; sum[dx+1] = t1; } - cur_dy++; + for( ; dx < dsize.width; dx++ ) + sum[dx] += buf[dx]; + } } - else +#endif + + for( sy = range.start; sy < range.end; sy++ ) { - for( dx = 0; dx <= dsize.width - 2; dx += 2 ) + const T* S = (const T*)(src.data + src.step * sy); + memset(buf, 0, num); + + if( cn == 1 ) + for( k = 0; k < xofs_count; k++ ) + { + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + buf[dxn] += S[xofs[k].si]*alpha; + } + else if( cn == 2 ) + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + buf[dxn] = t0; buf[dxn+1] = t1; + } + else if( cn == 3 ) + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + WT t2 = buf[dxn+2] + S[sxn+2]*alpha; + + buf[dxn] = t0; buf[dxn+1] = t1; buf[dxn+2] = t2; + } + else + for( k = 0; k < xofs_count; k++ ) + { + int sxn = xofs[k].si; + int dxn = xofs[k].di; + WT alpha = xofs[k].alpha; + + WT t0 = buf[dxn] + S[sxn]*alpha; + WT t1 = buf[dxn+1] + S[sxn+1]*alpha; + + buf[dxn] = t0; buf[dxn+1] = t1; + + t0 = buf[dxn+2] + S[sxn+2]*alpha; + t1 = buf[dxn+3] + S[sxn+3]*alpha; + + buf[dxn+2] = t0; buf[dxn+3] = t1; + } + + if( (cur_dy + 1)*scale_y <= sy + 1 || sy == ssize.height - 1 ) { - WT t0 = sum[dx] + buf[dx]; - WT t1 = sum[dx+1] + buf[dx+1]; - sum[dx] = t0; sum[dx+1] = t1; - buf[dx] = buf[dx+1] = 0; + WT beta = std::max(sy + 1 - (cur_dy + 1) * scale_y, (WT)0); + T* D = (T*)(dst.data + dst.step*cur_dy); + if( fabs(beta) < 1e-3 ) + { + if(cur_dy >= dsize.height) + return; + for( dx = 0; dx < dsize.width; dx++ ) + D[dx] = saturate_cast((sum[dx] + buf[dx]) / min(scale_y, src.rows - cur_dy * scale_y)); + memset(sum, 0, num); + } + else + { + WT beta1 = 1 - beta; + for( dx = 0; dx < dsize.width; dx++ ) + { + D[dx] = saturate_cast((sum[dx] + buf[dx] * beta1)/ min(scale_y, src.rows - cur_dy * scale_y)); + sum[dx] = buf[dx] * beta; + } + } + cur_dy++; } - for( ; dx < dsize.width; dx++ ) + else { - sum[dx] += buf[dx]; - buf[dx] = 0; + for( dx = 0; dx <= dsize.width - 2; dx += 2 ) + { + WT t0 = sum[dx] + buf[dx]; + WT t1 = sum[dx+1] + buf[dx+1]; + sum[dx] = t0; sum[dx+1] = t1; + } + for( ; dx < dsize.width; dx++ ) + sum[dx] += buf[dx]; } } } -} - -static void resizeAreaFast_8u( const Mat& src, Mat& dst, - const int* ofs, const int* xofs, - int scale_x, int scale_y ) -{ -#if CV_SSE2 - bool haveSSE2 = checkHardwareSupport(CV_CPU_SSE2); +private: + const Mat src; + Mat dst; + const DecimateAlpha* xofs; + const int xofs_count; + const double scale_y_; +#ifdef HAVE_TBB + const int *yofs, *cur_dy_ofs; #endif - +}; + +template +static void resizeArea_( const Mat& src, Mat& dst, const DecimateAlpha* xofs, int xofs_count, double scale_y_) +{ +#ifdef HAVE_TBB Size ssize = src.size(), dsize = dst.size(); - int cn = src.channels(); - int dy, dx, k = 0; - int area = scale_x*scale_y; - float scale = 1.f/(scale_x*scale_y); - int dwidth1 = (ssize.width/scale_x)*cn; - dsize.width *= cn; - ssize.width *= cn; - //avg values - for( dy = 0; dy < dsize.height; dy++ ) + AutoBuffer _yofs(2 * ssize.height); + int *yofs = _yofs, *cur_dy_ofs = _yofs + ssize.height; + int index = 0, cur_dy = 0, sy; + + for( sy = 0; sy < ssize.height; sy++ ) { - uchar* D = (uchar*)(dst.data + dst.step*dy); - int sy0 = dy*scale_y, w = sy0 + scale_y <= ssize.height ? dwidth1 : 0; - if( sy0 >= ssize.height ) - { - for( dx = 0; dx < dsize.width; dx++ ) //memset(D,0, dsize.width);//warning, never executed -> not tested - D[dx] = 0; - continue; - } - dx = 0; - - #if CV_SSE2 - if( haveSSE2 ) + bool reset = false; + cur_dy_ofs[sy] = cur_dy; + if( (cur_dy + 1)*scale_y_ <= sy + 1 || sy == ssize.height - 1 ) { - const __m128 _scale = _mm_set1_ps(scale); - const __m128i _ucMAXs = _mm_set1_epi16(UCHAR_MAX); - const uchar* _S[8]; - - for(; dx < w-8; dx+=8 ) + WT beta = std::max(sy + 1 - (cur_dy+1)*scale_y_, 0.); + if( fabs(beta) < 1e-3 ) { - __m128i _sum = _mm_setzero_si128(); - __m128i _sum1 = _mm_setzero_si128(); - _S[0] = (const uchar*)(src.data + src.step*sy0) + xofs[dx]; - _S[1] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+1]; - _S[2] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+2]; - _S[3] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+3]; - - _S[4] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+4]; - _S[5] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+5]; - _S[6] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+6]; - _S[7] = (const uchar*)(src.data + src.step*sy0) + xofs[dx+7]; - - for( k = 0; k < area; k++ ) - { - int ofsk = ofs[k]; - __m128i _temp = _mm_set_epi32(_S[3][ofsk],_S[2][ofsk],_S[1][ofsk],_S[0][ofsk]); - _sum = _mm_add_epi32(_sum, _temp); - - __m128i _temp1 = _mm_set_epi32(_S[7][ofsk],_S[6][ofsk],_S[5][ofsk],_S[4][ofsk]); - _sum1 = _mm_add_epi32(_sum1, _temp1); - } - - __m128i _tempSum = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(_sum), _scale)); - __m128i _tempSum1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_cvtepi32_ps(_sum1), _scale)); - - _tempSum = _mm_packs_epi32(_tempSum, _tempSum1); - _tempSum = _mm_min_epi16(_ucMAXs, _tempSum); - _tempSum = _mm_packus_epi16(_tempSum, _tempSum); - _mm_storel_epi64((__m128i*)(D+dx),_tempSum); - } - } - #endif - - for(; dx < w; dx++ ) - { - const uchar* S = (const uchar*)(src.data + src.step*sy0) + xofs[dx]; - int sum = 0; - k=0; - - #if CV_ENABLE_UNROLLED - for( ; k <= area - 4; k += 4 ) - sum += S[ofs[k]] + S[ofs[k+1]] + S[ofs[k+2]] + S[ofs[k+3]]; - #endif - - for( ; k < area; k++ ) - sum += S[ofs[k]]; - - - D[dx] = saturate_cast(sum*scale); - } - - for( ; dx < dsize.width; dx++ ) - { - int sum = 0; - int count = 0, sx0 = xofs[dx]; - if( sx0 >= ssize.width ) - D[dx] = 0; - - for( int sy = 0; sy < scale_y; sy++ ) - { - if( sy0 + sy >= ssize.height ) + if(cur_dy >= dsize.height) break; - const uchar* S = (const uchar*)(src.data + src.step*(sy0 + sy)) + sx0; - int sx = 0; - for( ; sx < scale_x*cn; sx += cn ) - { - if( sx0 + sx >= ssize.width ) - break; - sum += S[sx]; - count++; - } + reset = true; } - - D[dx] = saturate_cast((float)sum/count); + cur_dy++; } + yofs[sy] = index; + if (reset) + index = sy + 1; } +#endif + + Range range(0, src.rows); + resizeArea_Invoker invoker(src, dst, xofs, xofs_count, scale_y_ +#ifdef HAVE_TBB + , yofs, cur_dy_ofs +#endif + ); + parallel_for_(range, invoker); } @@ -1457,10 +1652,12 @@ typedef void (*ResizeAreaFastFunc)( const Mat& src, Mat& dst, int scale_x, int scale_y ); typedef void (*ResizeAreaFunc)( const Mat& src, Mat& dst, - const DecimateAlpha* xofs, int xofs_count, double scale_y_); + const DecimateAlpha* xofs, int xofs_count, + double scale_y_); } + ////////////////////////////////////////////////////////////////////////////////////////// void cv::resize( InputArray _src, OutputArray _dst, Size dsize, @@ -1553,30 +1750,33 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, static ResizeAreaFastFunc areafast_tab[] = { - resizeAreaFast_8u, 0, - resizeAreaFast_, - resizeAreaFast_, + resizeAreaFast_ >, + 0, + resizeAreaFast_ >, + resizeAreaFast_ >, 0, - resizeAreaFast_, - resizeAreaFast_, + resizeAreaFast_ >, + resizeAreaFast_ >, 0 }; static ResizeAreaFunc area_tab[] = { - resizeArea_, 0, resizeArea_, resizeArea_, - 0, resizeArea_, resizeArea_, 0 + resizeArea_, 0, resizeArea_, + resizeArea_, 0, resizeArea_, + resizeArea_, 0 }; Mat src = _src.getMat(); Size ssize = src.size(); CV_Assert( ssize.area() > 0 ); - CV_Assert( !(dsize == Size()) || (inv_scale_x > 0 && inv_scale_y > 0) ); - if( dsize == Size() ) + CV_Assert( dsize.area() || (inv_scale_x > 0 && inv_scale_y > 0) ); + if( !dsize.area() ) { dsize = Size(saturate_cast(src.cols*inv_scale_x), saturate_cast(src.rows*inv_scale_y)); + CV_Assert( dsize.area() ); } else { @@ -1601,79 +1801,92 @@ void cv::resize( InputArray _src, OutputArray _dst, Size dsize, resizeNN( src, dst, inv_scale_x, inv_scale_y ); return; } - - // true "area" interpolation is only implemented for the case (scale_x <= 1 && scale_y <= 1). - // In other cases it is emulated using some variant of bilinear interpolation - if( interpolation == INTER_AREA && scale_x >= 1 && scale_y >= 1 ) + { int iscale_x = saturate_cast(scale_x); int iscale_y = saturate_cast(scale_y); - - if( std::abs(scale_x - iscale_x) < DBL_EPSILON && - std::abs(scale_y - iscale_y) < DBL_EPSILON ) + + bool is_area_fast = std::abs(scale_x - iscale_x) < DBL_EPSILON && + std::abs(scale_y - iscale_y) < DBL_EPSILON; + + // in case of scale_x && scale_y is equal to 2 + // INTER_AREA (fast) also is equal to INTER_LINEAR + if ( interpolation == INTER_LINEAR && + scale_x >= 1 && scale_y >= 1 && is_area_fast) + interpolation = INTER_AREA; + + // true "area" interpolation is only implemented for the case (scale_x <= 1 && scale_y <= 1). + // In other cases it is emulated using some variant of bilinear interpolation + if( interpolation == INTER_AREA && scale_x >= 1 && scale_y >= 1 ) { - int area = iscale_x*iscale_y; - size_t srcstep = src.step / src.elemSize1(); - AutoBuffer _ofs(area + dsize.width*cn); - int* ofs = _ofs; - int* xofs = ofs + area; - ResizeAreaFastFunc func = areafast_tab[depth]; - CV_Assert( func != 0 ); - - for( sy = 0, k = 0; sy < iscale_y; sy++ ) - for( sx = 0; sx < iscale_x; sx++ ) - ofs[k++] = (int)(sy*srcstep + sx*cn); - - for( dx = 0; dx < dsize.width; dx++ ) + if( is_area_fast ) { - sx = dx*iscale_x*cn; - for( k = 0; k < cn; k++ ) - xofs[dx*cn + k] = sx + k; - } + int area = iscale_x*iscale_y; + size_t srcstep = src.step / src.elemSize1(); + AutoBuffer _ofs(area + dsize.width*cn); + int* ofs = _ofs; + int* xofs = ofs + area; + ResizeAreaFastFunc func = areafast_tab[depth]; + CV_Assert( func != 0 ); + + for( sy = 0, k = 0; sy < iscale_y; sy++ ) + for( sx = 0; sx < iscale_x; sx++ ) + ofs[k++] = (int)(sy*srcstep + sx*cn); - func( src, dst, ofs, xofs, iscale_x, iscale_y ); - return; - } + for( dx = 0; dx < dsize.width; dx++ ) + { + int j = dx * cn; + sx = iscale_x * j; + for( k = 0; k < cn; k++ ) + xofs[j + k] = sx + k; + } - ResizeAreaFunc func = area_tab[depth]; - CV_Assert( func != 0 && cn <= 4 ); + func( src, dst, ofs, xofs, iscale_x, iscale_y ); + return; + } - AutoBuffer _xofs(ssize.width*2); - DecimateAlpha* xofs = _xofs; + ResizeAreaFunc func = area_tab[depth]; + CV_Assert( func != 0 && cn <= 4 ); - for( dx = 0, k = 0; dx < dsize.width; dx++ ) - { - double fsx1 = dx*scale_x, fsx2 = fsx1 + scale_x; - int sx1 = cvCeil(fsx1), sx2 = cvFloor(fsx2); - sx1 = std::min(sx1, ssize.width-1); - sx2 = std::min(sx2, ssize.width-1); + AutoBuffer _xofs(ssize.width*2); + DecimateAlpha* xofs = _xofs; - if( sx1 > fsx1 ) + for( dx = 0, k = 0; dx < dsize.width; dx++ ) { - assert( k < ssize.width*2 ); - xofs[k].di = dx*cn; - xofs[k].si = (sx1-1)*cn; - xofs[k++].alpha = (float)((sx1 - fsx1) / min(scale_x, src.cols - fsx1)); - } + double fsx1 = dx*scale_x; + double fsx2 = fsx1 + scale_x; + int sx1 = cvCeil(fsx1), sx2 = cvFloor(fsx2); + sx1 = std::min(sx1, ssize.width-1); + sx2 = std::min(sx2, ssize.width-1); - for( sx = sx1; sx < sx2; sx++ ) - { - assert( k < ssize.width*2 ); - xofs[k].di = dx*cn; - xofs[k].si = sx*cn; - xofs[k++].alpha = float(1.0 / min(scale_x, src.cols - fsx1)); - } + if( sx1 > fsx1 ) + { + assert( k < ssize.width*2 ); + xofs[k].di = dx*cn; + xofs[k].si = (sx1-1)*cn; + xofs[k++].alpha = (float)((sx1 - fsx1) / min(scale_x, src.cols - fsx1)); + } - if( fsx2 - sx2 > 1e-3 ) - { - assert( k < ssize.width*2 ); - xofs[k].di = dx*cn; - xofs[k].si = sx2*cn; - xofs[k++].alpha = (float)(min(fsx2 - sx2, 1.) / min(scale_x, src.cols - fsx1)); + for( sx = sx1; sx < sx2; sx++ ) + { + assert( k < ssize.width*2 ); + xofs[k].di = dx*cn; + xofs[k].si = sx*cn; + xofs[k++].alpha = float(1.0 / min(scale_x, src.cols - fsx1)); + } + + if( fsx2 - sx2 > 1e-3 ) + { + assert( k < ssize.width*2 ); + xofs[k].di = dx*cn; + xofs[k].si = sx2*cn; + xofs[k++].alpha = (float)(min(fsx2 - sx2, 1.) / min(scale_x, src.cols - fsx1)); + } } + + func( src, dst, xofs, k, scale_y); + return; } - func( src, dst, xofs, k ,scale_y); - return; } int xmin = 0, xmax = dsize.width, width = dsize.width*cn; @@ -2549,6 +2762,206 @@ typedef void (*RemapFunc)(const Mat& _src, Mat& _dst, const Mat& _xy, const Mat& _fxy, const void* _wtab, int borderType, const Scalar& _borderValue); +class remapInvoker : + public ParallelLoopBody +{ +public: + remapInvoker(const Mat& _src, Mat _dst, const Mat& _map1, const Mat& _map2, const Mat *_m1, + const Mat *_m2, int _interpolation, int _borderType, const Scalar &_borderValue, + int _planar_input, RemapNNFunc _nnfunc, RemapFunc _ifunc, const void *_ctab) : + ParallelLoopBody(), src(_src), dst(_dst), map1(_map1), map2(_map2), m1(_m1), m2(_m2), + interpolation(_interpolation), borderType(_borderType), borderValue(_borderValue), + planar_input(_planar_input), nnfunc(_nnfunc), ifunc(_ifunc), ctab(_ctab) + { + } + + virtual void operator() (const Range& range) const + { + int x, y, x1, y1; + const int buf_size = 1 << 14; + int brows0 = std::min(128, dst.rows), map_depth = map1.depth(); + int bcols0 = std::min(buf_size/brows0, dst.cols); + brows0 = std::min(buf_size/bcols0, dst.rows); + #if CV_SSE2 + bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); + #endif + + Mat _bufxy(brows0, bcols0, CV_16SC2), _bufa; + if( !nnfunc ) + _bufa.create(brows0, bcols0, CV_16UC1); + + for( y = range.start; y < range.end; y += brows0 ) + { + for( x = 0; x < dst.cols; x += bcols0 ) + { + int brows = std::min(brows0, range.end - y); + int bcols = std::min(bcols0, dst.cols - x); + Mat dpart(dst, Rect(x, y, bcols, brows)); + Mat bufxy(_bufxy, Rect(0, 0, bcols, brows)); + + if( nnfunc ) + { + if( map1.type() == CV_16SC2 && !map2.data ) // the data is already in the right format + bufxy = map1(Rect(x, y, bcols, brows)); + else if( map_depth != CV_32F ) + { + for( y1 = 0; y1 < brows; y1++ ) + { + short* XY = (short*)(bufxy.data + bufxy.step*y1); + const short* sXY = (const short*)(m1->data + m1->step*(y+y1)) + x*2; + const ushort* sA = (const ushort*)(m2->data + m2->step*(y+y1)) + x; + + for( x1 = 0; x1 < bcols; x1++ ) + { + int a = sA[x1] & (INTER_TAB_SIZE2-1); + XY[x1*2] = sXY[x1*2] + NNDeltaTab_i[a][0]; + XY[x1*2+1] = sXY[x1*2+1] + NNDeltaTab_i[a][1]; + } + } + } + else if( !planar_input ) + map1(Rect(x, y, bcols, brows)).convertTo(bufxy, bufxy.depth()); + else + { + for( y1 = 0; y1 < brows; y1++ ) + { + short* XY = (short*)(bufxy.data + bufxy.step*y1); + const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; + const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; + x1 = 0; + + #if CV_SSE2 + if( useSIMD ) + { + for( ; x1 <= bcols - 8; x1 += 8 ) + { + __m128 fx0 = _mm_loadu_ps(sX + x1); + __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); + __m128 fy0 = _mm_loadu_ps(sY + x1); + __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); + __m128i ix0 = _mm_cvtps_epi32(fx0); + __m128i ix1 = _mm_cvtps_epi32(fx1); + __m128i iy0 = _mm_cvtps_epi32(fy0); + __m128i iy1 = _mm_cvtps_epi32(fy1); + ix0 = _mm_packs_epi32(ix0, ix1); + iy0 = _mm_packs_epi32(iy0, iy1); + ix1 = _mm_unpacklo_epi16(ix0, iy0); + iy1 = _mm_unpackhi_epi16(ix0, iy0); + _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); + _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); + } + } + #endif + + for( ; x1 < bcols; x1++ ) + { + XY[x1*2] = saturate_cast(sX[x1]); + XY[x1*2+1] = saturate_cast(sY[x1]); + } + } + } + nnfunc( src, dpart, bufxy, borderType, borderValue ); + continue; + } + + Mat bufa(_bufa, Rect(0, 0, bcols, brows)); + for( y1 = 0; y1 < brows; y1++ ) + { + short* XY = (short*)(bufxy.data + bufxy.step*y1); + ushort* A = (ushort*)(bufa.data + bufa.step*y1); + + if( (map1.type() == CV_16SC2 && (map2.type() == CV_16UC1 || map2.type() == CV_16SC1)) || + (map2.type() == CV_16SC2 && (map1.type() == CV_16UC1 || map1.type() == CV_16SC1)) ) + { + bufxy = m1->operator()(Rect(x, y, bcols, brows)); + bufa = m2->operator()(Rect(x, y, bcols, brows)); + } + else if( planar_input ) + { + const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; + const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; + + x1 = 0; + #if CV_SSE2 + if( useSIMD ) + { + __m128 scale = _mm_set1_ps((float)INTER_TAB_SIZE); + __m128i mask = _mm_set1_epi32(INTER_TAB_SIZE-1); + for( ; x1 <= bcols - 8; x1 += 8 ) + { + __m128 fx0 = _mm_loadu_ps(sX + x1); + __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); + __m128 fy0 = _mm_loadu_ps(sY + x1); + __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); + __m128i ix0 = _mm_cvtps_epi32(_mm_mul_ps(fx0, scale)); + __m128i ix1 = _mm_cvtps_epi32(_mm_mul_ps(fx1, scale)); + __m128i iy0 = _mm_cvtps_epi32(_mm_mul_ps(fy0, scale)); + __m128i iy1 = _mm_cvtps_epi32(_mm_mul_ps(fy1, scale)); + __m128i mx0 = _mm_and_si128(ix0, mask); + __m128i mx1 = _mm_and_si128(ix1, mask); + __m128i my0 = _mm_and_si128(iy0, mask); + __m128i my1 = _mm_and_si128(iy1, mask); + mx0 = _mm_packs_epi32(mx0, mx1); + my0 = _mm_packs_epi32(my0, my1); + my0 = _mm_slli_epi16(my0, INTER_BITS); + mx0 = _mm_or_si128(mx0, my0); + _mm_storeu_si128((__m128i*)(A + x1), mx0); + ix0 = _mm_srai_epi32(ix0, INTER_BITS); + ix1 = _mm_srai_epi32(ix1, INTER_BITS); + iy0 = _mm_srai_epi32(iy0, INTER_BITS); + iy1 = _mm_srai_epi32(iy1, INTER_BITS); + ix0 = _mm_packs_epi32(ix0, ix1); + iy0 = _mm_packs_epi32(iy0, iy1); + ix1 = _mm_unpacklo_epi16(ix0, iy0); + iy1 = _mm_unpackhi_epi16(ix0, iy0); + _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); + _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); + } + } + #endif + + for( ; x1 < bcols; x1++ ) + { + int sx = cvRound(sX[x1]*INTER_TAB_SIZE); + int sy = cvRound(sY[x1]*INTER_TAB_SIZE); + int v = (sy & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE-1)); + XY[x1*2] = (short)(sx >> INTER_BITS); + XY[x1*2+1] = (short)(sy >> INTER_BITS); + A[x1] = (ushort)v; + } + } + else + { + const float* sXY = (const float*)(map1.data + map1.step*(y+y1)) + x*2; + + for( x1 = 0; x1 < bcols; x1++ ) + { + int sx = cvRound(sXY[x1*2]*INTER_TAB_SIZE); + int sy = cvRound(sXY[x1*2+1]*INTER_TAB_SIZE); + int v = (sy & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE-1)); + XY[x1*2] = (short)(sx >> INTER_BITS); + XY[x1*2+1] = (short)(sy >> INTER_BITS); + A[x1] = (ushort)v; + } + } + } + ifunc(src, dpart, bufxy, bufa, ctab, borderType, borderValue); + } + } + } + +private: + const Mat src; + Mat dst; + const Mat map1, map2, *m1, *m2; + int interpolation, borderType; + const Scalar borderValue; + int planar_input; + RemapNNFunc nnfunc; + RemapFunc ifunc; + const void *ctab; +}; + } void cv::remap( InputArray _src, OutputArray _dst, @@ -2590,14 +3003,15 @@ void cv::remap( InputArray _src, OutputArray _dst, Mat src = _src.getMat(), map1 = _map1.getMat(), map2 = _map2.getMat(); - CV_Assert( (!map2.data || map2.size() == map1.size())); + CV_Assert( map1.size().area() > 0 ); + CV_Assert( !map2.data || (map2.size() == map1.size())); _dst.create( map1.size(), src.type() ); Mat dst = _dst.getMat(); if( dst.data == src.data ) src = src.clone(); - int depth = src.depth(), map_depth = map1.depth(); + int depth = src.depth(); RemapNNFunc nnfunc = 0; RemapFunc ifunc = 0; const void* ctab = 0; @@ -2608,12 +3022,6 @@ void cv::remap( InputArray _src, OutputArray _dst, { nnfunc = nn_tab[depth]; CV_Assert( nnfunc != 0 ); - - if( map1.type() == CV_16SC2 && !map2.data ) // the data is already in the right format - { - nnfunc( src, dst, map1, borderType, borderValue ); - return; - } } else { @@ -2639,182 +3047,19 @@ void cv::remap( InputArray _src, OutputArray _dst, { if( map1.type() != CV_16SC2 ) std::swap(m1, m2); - if( ifunc ) - { - ifunc( src, dst, *m1, *m2, ctab, borderType, borderValue ); - return; - } } else { - CV_Assert( (map1.type() == CV_32FC2 && !map2.data) || + CV_Assert( ((map1.type() == CV_32FC2 || map1.type() == CV_16SC2) && !map2.data) || (map1.type() == CV_32FC1 && map2.type() == CV_32FC1) ); planar_input = map1.channels() == 1; } - int x, y, x1, y1; - const int buf_size = 1 << 14; - int brows0 = std::min(128, dst.rows); - int bcols0 = std::min(buf_size/brows0, dst.cols); - brows0 = std::min(buf_size/bcols0, dst.rows); -#if CV_SSE2 - bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); -#endif - - Mat _bufxy(brows0, bcols0, CV_16SC2), _bufa; - if( !nnfunc ) - _bufa.create(brows0, bcols0, CV_16UC1); - - for( y = 0; y < dst.rows; y += brows0 ) - { - for( x = 0; x < dst.cols; x += bcols0 ) - { - int brows = std::min(brows0, dst.rows - y); - int bcols = std::min(bcols0, dst.cols - x); - Mat dpart(dst, Rect(x, y, bcols, brows)); - Mat bufxy(_bufxy, Rect(0, 0, bcols, brows)); - - if( nnfunc ) - { - if( map_depth != CV_32F ) - { - for( y1 = 0; y1 < brows; y1++ ) - { - short* XY = (short*)(bufxy.data + bufxy.step*y1); - const short* sXY = (const short*)(m1->data + m1->step*(y+y1)) + x*2; - const ushort* sA = (const ushort*)(m2->data + m2->step*(y+y1)) + x; - - for( x1 = 0; x1 < bcols; x1++ ) - { - int a = sA[x1] & (INTER_TAB_SIZE2-1); - XY[x1*2] = sXY[x1*2] + NNDeltaTab_i[a][0]; - XY[x1*2+1] = sXY[x1*2+1] + NNDeltaTab_i[a][1]; - } - } - } - else if( !planar_input ) - map1(Rect(0,0,bcols,brows)).convertTo(bufxy, bufxy.depth()); - else - { - for( y1 = 0; y1 < brows; y1++ ) - { - short* XY = (short*)(bufxy.data + bufxy.step*y1); - const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; - const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; - x1 = 0; - - #if CV_SSE2 - if( useSIMD ) - { - for( ; x1 <= bcols - 8; x1 += 8 ) - { - __m128 fx0 = _mm_loadu_ps(sX + x1); - __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); - __m128 fy0 = _mm_loadu_ps(sY + x1); - __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); - __m128i ix0 = _mm_cvtps_epi32(fx0); - __m128i ix1 = _mm_cvtps_epi32(fx1); - __m128i iy0 = _mm_cvtps_epi32(fy0); - __m128i iy1 = _mm_cvtps_epi32(fy1); - ix0 = _mm_packs_epi32(ix0, ix1); - iy0 = _mm_packs_epi32(iy0, iy1); - ix1 = _mm_unpacklo_epi16(ix0, iy0); - iy1 = _mm_unpackhi_epi16(ix0, iy0); - _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); - _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); - } - } - #endif - - for( ; x1 < bcols; x1++ ) - { - XY[x1*2] = saturate_cast(sX[x1]); - XY[x1*2+1] = saturate_cast(sY[x1]); - } - } - } - nnfunc( src, dpart, bufxy, borderType, borderValue ); - continue; - } - - Mat bufa(_bufa, Rect(0,0,bcols, brows)); - for( y1 = 0; y1 < brows; y1++ ) - { - short* XY = (short*)(bufxy.data + bufxy.step*y1); - ushort* A = (ushort*)(bufa.data + bufa.step*y1); - - if( planar_input ) - { - const float* sX = (const float*)(map1.data + map1.step*(y+y1)) + x; - const float* sY = (const float*)(map2.data + map2.step*(y+y1)) + x; - - x1 = 0; - #if CV_SSE2 - if( useSIMD ) - { - __m128 scale = _mm_set1_ps((float)INTER_TAB_SIZE); - __m128i mask = _mm_set1_epi32(INTER_TAB_SIZE-1); - for( ; x1 <= bcols - 8; x1 += 8 ) - { - __m128 fx0 = _mm_loadu_ps(sX + x1); - __m128 fx1 = _mm_loadu_ps(sX + x1 + 4); - __m128 fy0 = _mm_loadu_ps(sY + x1); - __m128 fy1 = _mm_loadu_ps(sY + x1 + 4); - __m128i ix0 = _mm_cvtps_epi32(_mm_mul_ps(fx0, scale)); - __m128i ix1 = _mm_cvtps_epi32(_mm_mul_ps(fx1, scale)); - __m128i iy0 = _mm_cvtps_epi32(_mm_mul_ps(fy0, scale)); - __m128i iy1 = _mm_cvtps_epi32(_mm_mul_ps(fy1, scale)); - __m128i mx0 = _mm_and_si128(ix0, mask); - __m128i mx1 = _mm_and_si128(ix1, mask); - __m128i my0 = _mm_and_si128(iy0, mask); - __m128i my1 = _mm_and_si128(iy1, mask); - mx0 = _mm_packs_epi32(mx0, mx1); - my0 = _mm_packs_epi32(my0, my1); - my0 = _mm_slli_epi16(my0, INTER_BITS); - mx0 = _mm_or_si128(mx0, my0); - _mm_storeu_si128((__m128i*)(A + x1), mx0); - ix0 = _mm_srai_epi32(ix0, INTER_BITS); - ix1 = _mm_srai_epi32(ix1, INTER_BITS); - iy0 = _mm_srai_epi32(iy0, INTER_BITS); - iy1 = _mm_srai_epi32(iy1, INTER_BITS); - ix0 = _mm_packs_epi32(ix0, ix1); - iy0 = _mm_packs_epi32(iy0, iy1); - ix1 = _mm_unpacklo_epi16(ix0, iy0); - iy1 = _mm_unpackhi_epi16(ix0, iy0); - _mm_storeu_si128((__m128i*)(XY + x1*2), ix1); - _mm_storeu_si128((__m128i*)(XY + x1*2 + 8), iy1); - } - } - #endif - - for( ; x1 < bcols; x1++ ) - { - int sx = cvRound(sX[x1]*INTER_TAB_SIZE); - int sy = cvRound(sY[x1]*INTER_TAB_SIZE); - int v = (sy & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE-1)); - XY[x1*2] = (short)(sx >> INTER_BITS); - XY[x1*2+1] = (short)(sy >> INTER_BITS); - A[x1] = (ushort)v; - } - } - else - { - const float* sXY = (const float*)(map1.data + map1.step*(y+y1)) + x*2; - - for( x1 = 0; x1 < bcols; x1++ ) - { - int sx = cvRound(sXY[x1*2]*INTER_TAB_SIZE); - int sy = cvRound(sXY[x1*2+1]*INTER_TAB_SIZE); - int v = (sy & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (sx & (INTER_TAB_SIZE-1)); - XY[x1*2] = (short)(sx >> INTER_BITS); - XY[x1*2+1] = (short)(sy >> INTER_BITS); - A[x1] = (ushort)v; - } - } - } - ifunc(src, dpart, bufxy, bufa, ctab, borderType, borderValue); - } - } + Range range(0, dst.rows); + remapInvoker invoker(src, dst, map1, map2, m1, m2, interpolation, + borderType, borderValue, planar_input, nnfunc, ifunc, + ctab); + parallel_for_(range, invoker); } @@ -2956,7 +3201,134 @@ void cv::convertMaps( InputArray _map1, InputArray _map2, } } + +namespace cv +{ + +class warpAffineInvoker : + public ParallelLoopBody +{ +public: + warpAffineInvoker(const Mat &_src, Mat &_dst, int _interpolation, int _borderType, + const Scalar &_borderValue, int *_adelta, int *_bdelta, double *_M) : + ParallelLoopBody(), src(_src), dst(_dst), interpolation(_interpolation), + borderType(_borderType), borderValue(_borderValue), adelta(_adelta), bdelta(_bdelta), + M(_M) + { + } + + virtual void operator() (const Range& range) const + { + const int BLOCK_SZ = 64; + short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ]; + const int AB_BITS = MAX(10, (int)INTER_BITS); + const int AB_SCALE = 1 << AB_BITS; + int round_delta = interpolation == INTER_NEAREST ? AB_SCALE/2 : AB_SCALE/INTER_TAB_SIZE/2, x, y, x1, y1; + #if CV_SSE2 + bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); + #endif + + int bh0 = std::min(BLOCK_SZ/2, dst.rows); + int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, dst.cols); + bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, dst.rows); + for( y = range.start; y < range.end; y += bh0 ) + { + for( x = 0; x < dst.cols; x += bw0 ) + { + int bw = std::min( bw0, dst.cols - x); + int bh = std::min( bh0, range.end - y); + + Mat _XY(bh, bw, CV_16SC2, XY), matA; + Mat dpart(dst, Rect(x, y, bw, bh)); + + for( y1 = 0; y1 < bh; y1++ ) + { + short* xy = XY + y1*bw*2; + int X0 = saturate_cast((M[1]*(y + y1) + M[2])*AB_SCALE) + round_delta; + int Y0 = saturate_cast((M[4]*(y + y1) + M[5])*AB_SCALE) + round_delta; + + if( interpolation == INTER_NEAREST ) + for( x1 = 0; x1 < bw; x1++ ) + { + int X = (X0 + adelta[x+x1]) >> AB_BITS; + int Y = (Y0 + bdelta[x+x1]) >> AB_BITS; + xy[x1*2] = saturate_cast(X); + xy[x1*2+1] = saturate_cast(Y); + } + else + { + short* alpha = A + y1*bw; + x1 = 0; + #if CV_SSE2 + if( useSIMD ) + { + __m128i fxy_mask = _mm_set1_epi32(INTER_TAB_SIZE - 1); + __m128i XX = _mm_set1_epi32(X0), YY = _mm_set1_epi32(Y0); + for( ; x1 <= bw - 8; x1 += 8 ) + { + __m128i tx0, tx1, ty0, ty1; + tx0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1)), XX); + ty0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1)), YY); + tx1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1 + 4)), XX); + ty1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1 + 4)), YY); + + tx0 = _mm_srai_epi32(tx0, AB_BITS - INTER_BITS); + ty0 = _mm_srai_epi32(ty0, AB_BITS - INTER_BITS); + tx1 = _mm_srai_epi32(tx1, AB_BITS - INTER_BITS); + ty1 = _mm_srai_epi32(ty1, AB_BITS - INTER_BITS); + + __m128i fx_ = _mm_packs_epi32(_mm_and_si128(tx0, fxy_mask), + _mm_and_si128(tx1, fxy_mask)); + __m128i fy_ = _mm_packs_epi32(_mm_and_si128(ty0, fxy_mask), + _mm_and_si128(ty1, fxy_mask)); + tx0 = _mm_packs_epi32(_mm_srai_epi32(tx0, INTER_BITS), + _mm_srai_epi32(tx1, INTER_BITS)); + ty0 = _mm_packs_epi32(_mm_srai_epi32(ty0, INTER_BITS), + _mm_srai_epi32(ty1, INTER_BITS)); + fx_ = _mm_adds_epi16(fx_, _mm_slli_epi16(fy_, INTER_BITS)); + + _mm_storeu_si128((__m128i*)(xy + x1*2), _mm_unpacklo_epi16(tx0, ty0)); + _mm_storeu_si128((__m128i*)(xy + x1*2 + 8), _mm_unpackhi_epi16(tx0, ty0)); + _mm_storeu_si128((__m128i*)(alpha + x1), fx_); + } + } + #endif + for( ; x1 < bw; x1++ ) + { + int X = (X0 + adelta[x+x1]) >> (AB_BITS - INTER_BITS); + int Y = (Y0 + bdelta[x+x1]) >> (AB_BITS - INTER_BITS); + xy[x1*2] = saturate_cast(X >> INTER_BITS); + xy[x1*2+1] = saturate_cast(Y >> INTER_BITS); + alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + + (X & (INTER_TAB_SIZE-1))); + } + } + } + + if( interpolation == INTER_NEAREST ) + remap( src, dpart, _XY, Mat(), interpolation, borderType, borderValue ); + else + { + Mat _matA(bh, bw, CV_16U, A); + remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue ); + } + } + } + } + +private: + const Mat src; + Mat dst; + int interpolation, borderType; + const Scalar borderValue; + int *adelta, *bdelta; + double *M; +}; + +} + + void cv::warpAffine( InputArray _src, OutputArray _dst, InputArray _M0, Size dsize, int flags, int borderType, const Scalar& borderValue ) @@ -2968,8 +3340,6 @@ void cv::warpAffine( InputArray _src, OutputArray _dst, if( dst.data == src.data ) src = src.clone(); - const int BLOCK_SZ = 64; - short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ]; double M[6]; Mat matM(2, 3, CV_64F, M); int interpolation = flags & INTER_MAX; @@ -2996,112 +3366,121 @@ void cv::warpAffine( InputArray _src, OutputArray _dst, M[2] = b1; M[5] = b2; } - int x, y, x1, y1, width = dst.cols, height = dst.rows; - AutoBuffer _abdelta(width*2); - int* adelta = &_abdelta[0], *bdelta = adelta + width; + int x; + AutoBuffer _abdelta(dst.cols*2); + int* adelta = &_abdelta[0], *bdelta = adelta + dst.cols; const int AB_BITS = MAX(10, (int)INTER_BITS); const int AB_SCALE = 1 << AB_BITS; - int round_delta = interpolation == INTER_NEAREST ? AB_SCALE/2 : AB_SCALE/INTER_TAB_SIZE/2; -#if CV_SSE2 - bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); -#endif - for( x = 0; x < width; x++ ) + for( x = 0; x < dst.cols; x++ ) { adelta[x] = saturate_cast(M[0]*x*AB_SCALE); bdelta[x] = saturate_cast(M[3]*x*AB_SCALE); } - int bh0 = std::min(BLOCK_SZ/2, height); - int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, width); - bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, height); + Range range(0, dst.rows); + warpAffineInvoker invoker(src, dst, interpolation, borderType, + borderValue, adelta, bdelta, M); + parallel_for_(range, invoker); +} - for( y = 0; y < height; y += bh0 ) - { - for( x = 0; x < width; x += bw0 ) - { - int bw = std::min( bw0, width - x); - int bh = std::min( bh0, height - y); - Mat _XY(bh, bw, CV_16SC2, XY), matA; - Mat dpart(dst, Rect(x, y, bw, bh)); +namespace cv +{ - for( y1 = 0; y1 < bh; y1++ ) +class warpPerspectiveInvoker : + public ParallelLoopBody +{ +public: + + warpPerspectiveInvoker(const Mat &_src, Mat &_dst, double *_M, int _interpolation, + int _borderType, const Scalar &_borderValue) : + ParallelLoopBody(), src(_src), dst(_dst), M(_M), interpolation(_interpolation), + borderType(_borderType), borderValue(_borderValue) + { + } + + virtual void operator() (const Range& range) const + { + const int BLOCK_SZ = 32; + short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ]; + int x, y, x1, y1, width = dst.cols, height = dst.rows; + + int bh0 = std::min(BLOCK_SZ/2, height); + int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, width); + bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, height); + + for( y = range.start; y < range.end; y += bh0 ) + { + for( x = 0; x < width; x += bw0 ) { - short* xy = XY + y1*bw*2; - int X0 = saturate_cast((M[1]*(y + y1) + M[2])*AB_SCALE) + round_delta; - int Y0 = saturate_cast((M[4]*(y + y1) + M[5])*AB_SCALE) + round_delta; - - if( interpolation == INTER_NEAREST ) - for( x1 = 0; x1 < bw; x1++ ) - { - int X = (X0 + adelta[x+x1]) >> AB_BITS; - int Y = (Y0 + bdelta[x+x1]) >> AB_BITS; - xy[x1*2] = saturate_cast(X); - xy[x1*2+1] = saturate_cast(Y); - } - else + int bw = std::min( bw0, width - x); + int bh = std::min( bh0, range.end - y); // height + + Mat _XY(bh, bw, CV_16SC2, XY), matA; + Mat dpart(dst, Rect(x, y, bw, bh)); + + for( y1 = 0; y1 < bh; y1++ ) { - short* alpha = A + y1*bw; - x1 = 0; - #if CV_SSE2 - if( useSIMD ) - { - __m128i fxy_mask = _mm_set1_epi32(INTER_TAB_SIZE - 1); - __m128i XX = _mm_set1_epi32(X0), YY = _mm_set1_epi32(Y0); - for( ; x1 <= bw - 8; x1 += 8 ) + short* xy = XY + y1*bw*2; + double X0 = M[0]*x + M[1]*(y + y1) + M[2]; + double Y0 = M[3]*x + M[4]*(y + y1) + M[5]; + double W0 = M[6]*x + M[7]*(y + y1) + M[8]; + + if( interpolation == INTER_NEAREST ) + for( x1 = 0; x1 < bw; x1++ ) { - __m128i tx0, tx1, ty0, ty1; - tx0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1)), XX); - ty0 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1)), YY); - tx1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(adelta + x + x1 + 4)), XX); - ty1 = _mm_add_epi32(_mm_loadu_si128((const __m128i*)(bdelta + x + x1 + 4)), YY); - - tx0 = _mm_srai_epi32(tx0, AB_BITS - INTER_BITS); - ty0 = _mm_srai_epi32(ty0, AB_BITS - INTER_BITS); - tx1 = _mm_srai_epi32(tx1, AB_BITS - INTER_BITS); - ty1 = _mm_srai_epi32(ty1, AB_BITS - INTER_BITS); - - __m128i fx_ = _mm_packs_epi32(_mm_and_si128(tx0, fxy_mask), - _mm_and_si128(tx1, fxy_mask)); - __m128i fy_ = _mm_packs_epi32(_mm_and_si128(ty0, fxy_mask), - _mm_and_si128(ty1, fxy_mask)); - tx0 = _mm_packs_epi32(_mm_srai_epi32(tx0, INTER_BITS), - _mm_srai_epi32(tx1, INTER_BITS)); - ty0 = _mm_packs_epi32(_mm_srai_epi32(ty0, INTER_BITS), - _mm_srai_epi32(ty1, INTER_BITS)); - fx_ = _mm_adds_epi16(fx_, _mm_slli_epi16(fy_, INTER_BITS)); - - _mm_storeu_si128((__m128i*)(xy + x1*2), _mm_unpacklo_epi16(tx0, ty0)); - _mm_storeu_si128((__m128i*)(xy + x1*2 + 8), _mm_unpackhi_epi16(tx0, ty0)); - _mm_storeu_si128((__m128i*)(alpha + x1), fx_); + double W = W0 + M[6]*x1; + W = W ? 1./W : 0; + double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0]*x1)*W)); + double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3]*x1)*W)); + int X = saturate_cast(fX); + int Y = saturate_cast(fY); + + xy[x1*2] = saturate_cast(X); + xy[x1*2+1] = saturate_cast(Y); } - } - #endif - for( ; x1 < bw; x1++ ) + else { - int X = (X0 + adelta[x+x1]) >> (AB_BITS - INTER_BITS); - int Y = (Y0 + bdelta[x+x1]) >> (AB_BITS - INTER_BITS); - xy[x1*2] = saturate_cast(X >> INTER_BITS); - xy[x1*2+1] = saturate_cast(Y >> INTER_BITS); - alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + - (X & (INTER_TAB_SIZE-1))); + short* alpha = A + y1*bw; + for( x1 = 0; x1 < bw; x1++ ) + { + double W = W0 + M[6]*x1; + W = W ? INTER_TAB_SIZE/W : 0; + double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0]*x1)*W)); + double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3]*x1)*W)); + int X = saturate_cast(fX); + int Y = saturate_cast(fY); + + xy[x1*2] = saturate_cast(X >> INTER_BITS); + xy[x1*2+1] = saturate_cast(Y >> INTER_BITS); + alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + + (X & (INTER_TAB_SIZE-1))); + } } } - } - - if( interpolation == INTER_NEAREST ) - remap( src, dpart, _XY, Mat(), interpolation, borderType, borderValue ); - else - { - Mat _matA(bh, bw, CV_16U, A); - remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue ); + + if( interpolation == INTER_NEAREST ) + remap( src, dpart, _XY, Mat(), interpolation, borderType, borderValue ); + else + { + Mat _matA(bh, bw, CV_16U, A); + remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue ); + } } } } + +private: + const Mat src; + Mat dst; + double* M; + int interpolation, borderType; + const Scalar borderValue; +}; + } - void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0, Size dsize, int flags, int borderType, const Scalar& borderValue ) { @@ -3113,8 +3492,6 @@ void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0, if( dst.data == src.data ) src = src.clone(); - const int BLOCK_SZ = 32; - short XY[BLOCK_SZ*BLOCK_SZ*2], A[BLOCK_SZ*BLOCK_SZ]; double M[9]; Mat matM(3, 3, CV_64F, M); int interpolation = flags & INTER_MAX; @@ -3132,71 +3509,9 @@ void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0, if( !(flags & WARP_INVERSE_MAP) ) invert(matM, matM); - int x, y, x1, y1, width = dst.cols, height = dst.rows; - - int bh0 = std::min(BLOCK_SZ/2, height); - int bw0 = std::min(BLOCK_SZ*BLOCK_SZ/bh0, width); - bh0 = std::min(BLOCK_SZ*BLOCK_SZ/bw0, height); - - for( y = 0; y < height; y += bh0 ) - { - for( x = 0; x < width; x += bw0 ) - { - int bw = std::min( bw0, width - x); - int bh = std::min( bh0, height - y); - - Mat _XY(bh, bw, CV_16SC2, XY), matA; - Mat dpart(dst, Rect(x, y, bw, bh)); - - for( y1 = 0; y1 < bh; y1++ ) - { - short* xy = XY + y1*bw*2; - double X0 = M[0]*x + M[1]*(y + y1) + M[2]; - double Y0 = M[3]*x + M[4]*(y + y1) + M[5]; - double W0 = M[6]*x + M[7]*(y + y1) + M[8]; - - if( interpolation == INTER_NEAREST ) - for( x1 = 0; x1 < bw; x1++ ) - { - double W = W0 + M[6]*x1; - W = W ? 1./W : 0; - double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0]*x1)*W)); - double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3]*x1)*W)); - int X = saturate_cast(fX); - int Y = saturate_cast(fY); - - xy[x1*2] = saturate_cast(X); - xy[x1*2+1] = saturate_cast(Y); - } - else - { - short* alpha = A + y1*bw; - for( x1 = 0; x1 < bw; x1++ ) - { - double W = W0 + M[6]*x1; - W = W ? INTER_TAB_SIZE/W : 0; - double fX = std::max((double)INT_MIN, std::min((double)INT_MAX, (X0 + M[0]*x1)*W)); - double fY = std::max((double)INT_MIN, std::min((double)INT_MAX, (Y0 + M[3]*x1)*W)); - int X = saturate_cast(fX); - int Y = saturate_cast(fY); - - xy[x1*2] = saturate_cast(X >> INTER_BITS); - xy[x1*2+1] = saturate_cast(Y >> INTER_BITS); - alpha[x1] = (short)((Y & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + - (X & (INTER_TAB_SIZE-1))); - } - } - } - - if( interpolation == INTER_NEAREST ) - remap( src, dpart, _XY, Mat(), interpolation, borderType, borderValue ); - else - { - Mat _matA(bh, bw, CV_16U, A); - remap( src, dpart, _XY, _matA, interpolation, borderType, borderValue ); - } - } - } + Range range(0, dst.rows); + warpPerspectiveInvoker invoker(src, dst, M, interpolation, borderType, borderValue); + parallel_for_(range, invoker); } From da5aaab27788d32a9f4118b4a90667000c8282aa Mon Sep 17 00:00:00 2001 From: Vladislav Vinogradov Date: Wed, 22 Aug 2012 12:11:11 +0400 Subject: [PATCH 51/58] optimized gpu::integral for Kepler --- modules/core/include/opencv2/core/gpumat.hpp | 4 +- modules/gpu/src/cuda/integral_image.cu | 385 +++++++++++++++++++ modules/gpu/src/imgproc.cpp | 92 ++++- 3 files changed, 461 insertions(+), 20 deletions(-) create mode 100644 modules/gpu/src/cuda/integral_image.cu diff --git a/modules/core/include/opencv2/core/gpumat.hpp b/modules/core/include/opencv2/core/gpumat.hpp index 9893359253..e09f1bc4c3 100644 --- a/modules/core/include/opencv2/core/gpumat.hpp +++ b/modules/core/include/opencv2/core/gpumat.hpp @@ -72,9 +72,11 @@ namespace cv { namespace gpu FEATURE_SET_COMPUTE_13 = 13, FEATURE_SET_COMPUTE_20 = 20, FEATURE_SET_COMPUTE_21 = 21, + FEATURE_SET_COMPUTE_30 = 30, GLOBAL_ATOMICS = FEATURE_SET_COMPUTE_11, SHARED_ATOMICS = FEATURE_SET_COMPUTE_12, - NATIVE_DOUBLE = FEATURE_SET_COMPUTE_13 + NATIVE_DOUBLE = FEATURE_SET_COMPUTE_13, + WARP_SHUFFLE_FUNCTIONS = FEATURE_SET_COMPUTE_30 }; // Gives information about what GPU archs this OpenCV GPU module was diff --git a/modules/gpu/src/cuda/integral_image.cu b/modules/gpu/src/cuda/integral_image.cu new file mode 100644 index 0000000000..ead0ddefec --- /dev/null +++ b/modules/gpu/src/cuda/integral_image.cu @@ -0,0 +1,385 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or bpied warranties, including, but not limited to, the bpied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "opencv2/gpu/device/common.hpp" + +namespace cv { namespace gpu { namespace device +{ + namespace imgproc + { + // Utility function to extract unsigned chars from an unsigned integer + __device__ uchar4 int_to_uchar4(unsigned int in) + { + uchar4 bytes; + bytes.x = (in && 0x000000ff) >> 0; + bytes.y = (in && 0x0000ff00) >> 8; + bytes.z = (in && 0x00ff0000) >> 16; + bytes.w = (in && 0xff000000) >> 24; + return bytes; + } + + __global__ void shfl_integral_horizontal(const PtrStep_ img, PtrStep_ integral) + { + #if __CUDA_ARCH__ >= 300 + __shared__ int sums[128]; + + const int id = threadIdx.x; + const int lane_id = id % warpSize; + const int warp_id = id / warpSize; + + const uint4 data = img(blockIdx.x, id); + + const uchar4 a = int_to_uchar4(data.x); + const uchar4 b = int_to_uchar4(data.y); + const uchar4 c = int_to_uchar4(data.z); + const uchar4 d = int_to_uchar4(data.w); + + int result[16]; + + result[0] = a.x; + result[1] = result[0] + a.y; + result[2] = result[1] + a.z; + result[3] = result[2] + a.w; + + result[4] = result[3] + b.x; + result[5] = result[4] + b.y; + result[6] = result[5] + b.z; + result[7] = result[6] + b.w; + + result[8] = result[7] + c.x; + result[9] = result[8] + c.y; + result[10] = result[9] + c.z; + result[11] = result[10] + c.w; + + result[12] = result[11] + d.x; + result[13] = result[12] + d.y; + result[14] = result[13] + d.z; + result[15] = result[14] + d.w; + + int sum = result[15]; + + // the prefix sum for each thread's 16 value is computed, + // now the final sums (result[15]) need to be shared + // with the other threads and add. To do this, + // the __shfl_up() instruction is used and a shuffle scan + // operation is performed to distribute the sums to the correct + // threads + #pragma unroll + for (int i = 1; i < 32; i *= 2) + { + const int n = __shfl_up(sum, i, 32); + + if (lane_id >= i) + { + #pragma unroll + for (int i = 0; i < 16; ++i) + result[i] += n; + + sum += n; + } + } + + // Now the final sum for the warp must be shared + // between warps. This is done by each warp + // having a thread store to shared memory, then + // having some other warp load the values and + // compute a prefix sum, again by using __shfl_up. + // The results are uniformly added back to the warps. + // last thread in the warp holding sum of the warp + // places that in shared + if (threadIdx.x % warpSize == warpSize - 1) + sums[warp_id] = result[15]; + + __syncthreads(); + + if (warp_id == 0) + { + int warp_sum = sums[lane_id]; + + #pragma unroll + for (int i = 1; i <= 32; i *= 2) + { + const int n = __shfl_up(warp_sum, i, 32); + + if (lane_id >= i) + warp_sum += n; + } + + sums[lane_id] = warp_sum; + } + + __syncthreads(); + + int blockSum = 0; + + // fold in unused warp + if (warp_id > 0) + { + blockSum = sums[warp_id - 1]; + + #pragma unroll + for (int i = 0; i < 16; ++i) + result[i] += blockSum; + } + + // assemble result + // Each thread has 16 values to write, which are + // now integer data (to avoid overflow). Instead of + // each thread writing consecutive uint4s, the + // approach shown here experiments using + // the shuffle command to reformat the data + // inside the registers so that each thread holds + // consecutive data to be written so larger contiguous + // segments can be assembled for writing. + + /* + For example data that needs to be written as + + GMEM[16] <- x0 x1 x2 x3 y0 y1 y2 y3 z0 z1 z2 z3 w0 w1 w2 w3 + but is stored in registers (r0..r3), in four threads (0..3) as: + + threadId 0 1 2 3 + r0 x0 y0 z0 w0 + r1 x1 y1 z1 w1 + r2 x2 y2 z2 w2 + r3 x3 y3 z3 w3 + + after apply __shfl_xor operations to move data between registers r1..r3: + + threadId 00 01 10 11 + x0 y0 z0 w0 + xor(01)->y1 x1 w1 z1 + xor(10)->z2 w2 x2 y2 + xor(11)->w3 z3 y3 x3 + + and now x0..x3, and z0..z3 can be written out in order by all threads. + + In the current code, each register above is actually representing + four integers to be written as uint4's to GMEM. + */ + + result[4] = __shfl_xor(result[4] , 1, 32); + result[5] = __shfl_xor(result[5] , 1, 32); + result[6] = __shfl_xor(result[6] , 1, 32); + result[7] = __shfl_xor(result[7] , 1, 32); + + result[8] = __shfl_xor(result[8] , 2, 32); + result[9] = __shfl_xor(result[9] , 2, 32); + result[10] = __shfl_xor(result[10], 2, 32); + result[11] = __shfl_xor(result[11], 2, 32); + + result[12] = __shfl_xor(result[12], 3, 32); + result[13] = __shfl_xor(result[13], 3, 32); + result[14] = __shfl_xor(result[14], 3, 32); + result[15] = __shfl_xor(result[15], 3, 32); + + uint4* integral_row = integral.ptr(blockIdx.x); + uint4 output; + + /////// + + if (threadIdx.x % 4 == 0) + output = make_uint4(result[0], result[1], result[2], result[3]); + + if (threadIdx.x % 4 == 1) + output = make_uint4(result[4], result[5], result[6], result[7]); + + if (threadIdx.x % 4 == 2) + output = make_uint4(result[8], result[9], result[10], result[11]); + + if (threadIdx.x % 4 == 3) + output = make_uint4(result[12], result[13], result[14], result[15]); + + integral_row[threadIdx.x % 4 + (threadIdx.x / 4) * 16] = output; + + /////// + + if (threadIdx.x % 4 == 2) + output = make_uint4(result[0], result[1], result[2], result[3]); + + if (threadIdx.x % 4 == 3) + output = make_uint4(result[4], result[5], result[6], result[7]); + + if (threadIdx.x % 4 == 0) + output = make_uint4(result[8], result[9], result[10], result[11]); + + if (threadIdx.x % 4 == 1) + output = make_uint4(result[12], result[13], result[14], result[15]); + + integral_row[(threadIdx.x + 2) % 4 + (threadIdx.x / 4) * 16 + 8] = output; + + // continuning from the above example, + // this use of __shfl_xor() places the y0..y3 and w0..w3 data + // in order. + + #pragma unroll + for (int i = 0; i < 16; ++i) + result[i] = __shfl_xor(result[i], 1, 32); + + if (threadIdx.x % 4 == 0) + output = make_uint4(result[0], result[1], result[2], result[3]); + + if (threadIdx.x % 4 == 1) + output = make_uint4(result[4], result[5], result[6], result[7]); + + if (threadIdx.x % 4 == 2) + output = make_uint4(result[8], result[9], result[10], result[11]); + + if (threadIdx.x % 4 == 3) + output = make_uint4(result[12], result[13], result[14], result[15]); + + integral_row[threadIdx.x % 4 + (threadIdx.x / 4) * 16 + 4] = output; + + /////// + + if (threadIdx.x % 4 == 2) + output = make_uint4(result[0], result[1], result[2], result[3]); + + if (threadIdx.x % 4 == 3) + output = make_uint4(result[4], result[5], result[6], result[7]); + + if (threadIdx.x % 4 == 0) + output = make_uint4(result[8], result[9], result[10], result[11]); + + if (threadIdx.x % 4 == 1) + output = make_uint4(result[12], result[13], result[14], result[15]); + + integral_row[(threadIdx.x + 2) % 4 + (threadIdx.x / 4) * 16 + 12] = output; + #endif + } + + // This kernel computes columnwise prefix sums. When the data input is + // the row sums from above, this completes the integral image. + // The approach here is to have each block compute a local set of sums. + // First , the data covered by the block is loaded into shared memory, + // then instead of performing a sum in shared memory using __syncthreads + // between stages, the data is reformatted so that the necessary sums + // occur inside warps and the shuffle scan operation is used. + // The final set of sums from the block is then propgated, with the block + // computing "down" the image and adding the running sum to the local + // block sums. + __global__ void shfl_integral_vertical(DevMem2D_ integral) + { + #if __CUDA_ARCH__ >= 300 + __shared__ unsigned int sums[32][9]; + + const int tidx = blockIdx.x * blockDim.x + threadIdx.x; + const int lane_id = tidx % 8; + + if (tidx >= integral.cols) + return; + + sums[threadIdx.x][threadIdx.y] = 0; + __syncthreads(); + + unsigned int stepSum = 0; + + for (int y = threadIdx.y; y < integral.rows; y += blockDim.y) + { + unsigned int* p = integral.ptr(y) + tidx; + + unsigned int sum = *p; + + sums[threadIdx.x][threadIdx.y] = sum; + __syncthreads(); + + // place into SMEM + // shfl scan reduce the SMEM, reformating so the column + // sums are computed in a warp + // then read out properly + const int j = threadIdx.x % 8; + const int k = threadIdx.x / 8 + threadIdx.y * 4; + + int partial_sum = sums[k][j]; + + for (int i = 1; i <= 8; i *= 2) + { + int n = __shfl_up(partial_sum, i, 32); + + if (lane_id >= i) + partial_sum += n; + } + + sums[k][j] = partial_sum; + __syncthreads(); + + if (threadIdx.y > 0) + sum += sums[threadIdx.x][threadIdx.y - 1]; + + sum += stepSum; + stepSum += sums[threadIdx.x][blockDim.y - 1]; + + __syncthreads(); + + *p = sum; + } + #endif + } + + void shfl_integral_gpu(DevMem2Db img, DevMem2D_ integral, cudaStream_t stream) + { + { + // each thread handles 16 values, use 1 block/row + const int block = img.cols / 16; + + // launch 1 block / row + const int grid = img.rows; + + cudaSafeCall( cudaFuncSetCacheConfig(shfl_integral_horizontal, cudaFuncCachePreferL1) ); + + shfl_integral_horizontal<<>>((DevMem2D_) img, (DevMem2D_) integral); + cudaSafeCall( cudaGetLastError() ); + } + + { + const dim3 block(32, 8); + const dim3 grid(divUp(integral.cols, block.x), 1); + + shfl_integral_vertical<<>>(integral); + cudaSafeCall( cudaGetLastError() ); + } + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + } +}}} diff --git a/modules/gpu/src/imgproc.cpp b/modules/gpu/src/imgproc.cpp index fe1ad7bd89..5b8fb9c057 100644 --- a/modules/gpu/src/imgproc.cpp +++ b/modules/gpu/src/imgproc.cpp @@ -223,7 +223,7 @@ void cv::gpu::reprojectImageTo3D(const GpuMat& disp, GpuMat& xyz, const Mat& Q, using namespace cv::gpu::device::imgproc; typedef void (*func_t)(const DevMem2Db disp, DevMem2Db xyz, const float* q, cudaStream_t stream); - static const func_t funcs[2][4] = + static const func_t funcs[2][4] = { {reprojectImageTo3D_gpu, 0, 0, reprojectImageTo3D_gpu}, {reprojectImageTo3D_gpu, 0, 0, reprojectImageTo3D_gpu} @@ -533,32 +533,86 @@ void cv::gpu::integral(const GpuMat& src, GpuMat& sum, Stream& s) integralBuffered(src, sum, buffer, s); } +namespace cv { namespace gpu { namespace device +{ + namespace imgproc + { + void shfl_integral_gpu(DevMem2Db img, DevMem2D_ integral, cudaStream_t stream); + } +}}} + void cv::gpu::integralBuffered(const GpuMat& src, GpuMat& sum, GpuMat& buffer, Stream& s) { CV_Assert(src.type() == CV_8UC1); - if (sum.cols != src.cols + 1 && sum.rows != src.rows + 1) - sum.create(src.rows + 1, src.cols + 1, CV_32S); - NcvSize32u roiSize; - roiSize.width = src.cols; - roiSize.height = src.rows; + cudaStream_t stream = StreamAccessor::getStream(s); - cudaDeviceProp prop; - cudaSafeCall( cudaGetDeviceProperties(&prop, cv::gpu::getDevice()) ); + DeviceInfo info; - Ncv32u bufSize; - ncvSafeCall( nppiStIntegralGetSize_8u32u(roiSize, &bufSize, prop) ); - ensureSizeIsEnough(1, bufSize, CV_8UC1, buffer); + if (info.supports(WARP_SHUFFLE_FUNCTIONS)) + { + GpuMat src16; - cudaStream_t stream = StreamAccessor::getStream(s); + if (src.cols % 16 == 0) + src16 = src; + else + { + ensureSizeIsEnough(src.rows, ((src.cols + 15) / 16) * 16, src.type(), buffer); - NppStStreamHandler h(stream); + GpuMat inner = buffer(Rect(0, 0, src.cols, src.rows)); + + if (s) + { + s.enqueueMemSet(buffer, Scalar::all(0)); + s.enqueueCopy(src, inner); + } + else + { + buffer.setTo(Scalar::all(0)); + src.copyTo(inner); + } + + src16 = buffer; + } - ncvSafeCall( nppiStIntegral_8u32u_C1R(const_cast(src.ptr()), static_cast(src.step), - sum.ptr(), static_cast(sum.step), roiSize, buffer.ptr(), bufSize, prop) ); + sum.create(src16.rows + 1, src16.cols + 1, CV_32SC1); - if (stream == 0) - cudaSafeCall( cudaDeviceSynchronize() ); + if (s) + s.enqueueMemSet(sum, Scalar::all(0)); + else + sum.setTo(Scalar::all(0)); + + GpuMat inner = sum(Rect(1, 1, src16.cols, src16.rows)); + + cv::gpu::device::imgproc::shfl_integral_gpu(src16, inner, stream); + + if (src16.cols != src.cols) + sum = sum(Rect(0, 0, src.cols + 1, src.rows + 1)); + } + else + { + sum.create(src.rows + 1, src.cols + 1, CV_32SC1); + + NcvSize32u roiSize; + roiSize.width = src.cols; + roiSize.height = src.rows; + + cudaDeviceProp prop; + cudaSafeCall( cudaGetDeviceProperties(&prop, cv::gpu::getDevice()) ); + + Ncv32u bufSize; + ncvSafeCall( nppiStIntegralGetSize_8u32u(roiSize, &bufSize, prop) ); + ensureSizeIsEnough(1, bufSize, CV_8UC1, buffer); + + + NppStStreamHandler h(stream); + + ncvSafeCall( nppiStIntegral_8u32u_C1R(const_cast(src.ptr()), static_cast(src.step), + sum.ptr(), static_cast(sum.step), roiSize, buffer.ptr(), bufSize, prop) ); + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } } ////////////////////////////////////////////////////////////////////////////// @@ -1340,7 +1394,7 @@ Size cv::gpu::ConvolveBuf::estimateBlockSize(Size result_size, Size /*templ_size int width = (result_size.width + 2) / 3; int height = (result_size.height + 2) / 3; width = std::min(width, result_size.width); - height = std::min(height, result_size.height); + height = std::min(height, result_size.height); return Size(width, height); } @@ -1380,7 +1434,7 @@ void cv::gpu::convolve(const GpuMat& image, const GpuMat& templ, GpuMat& result, cufftHandle planR2C, planC2R; cufftSafeCall(cufftPlan2d(&planC2R, dft_size.height, dft_size.width, CUFFT_C2R)); - cufftSafeCall(cufftPlan2d(&planR2C, dft_size.height, dft_size.width, CUFFT_R2C)); + cufftSafeCall(cufftPlan2d(&planR2C, dft_size.height, dft_size.width, CUFFT_R2C)); cufftSafeCall( cufftSetStream(planR2C, StreamAccessor::getStream(stream)) ); cufftSafeCall( cufftSetStream(planC2R, StreamAccessor::getStream(stream)) ); From e95bc7d502ce74748cde30c48bc54e360e039fd1 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 22 Aug 2012 13:33:13 +0400 Subject: [PATCH 52/58] fixed ios camera timing (patch by Eduard Feicho) --- .../highgui/include/opencv2/highgui/cap_ios.h | 5 +- modules/highgui/src/cap_ios_video_camera.mm | 145 +++++------------- 2 files changed, 41 insertions(+), 109 deletions(-) diff --git a/modules/highgui/include/opencv2/highgui/cap_ios.h b/modules/highgui/include/opencv2/highgui/cap_ios.h index cc6668ace0..26a28c077b 100644 --- a/modules/highgui/include/opencv2/highgui/cap_ios.h +++ b/modules/highgui/include/opencv2/highgui/cap_ios.h @@ -80,7 +80,6 @@ @property (nonatomic, retain) UIView* parentView; -- (void)pause; - (void)start; - (void)stop; - (void)switchCameras; @@ -120,6 +119,8 @@ AVAssetWriterInput* recordAssetWriterInput; AVAssetWriterInputPixelBufferAdaptor* recordPixelBufferAdaptor; AVAssetWriter* recordAssetWriter; + + CMTime lastSampleTime; } @@ -134,6 +135,8 @@ - (void)adjustLayoutToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation; - (void)layoutPreviewLayer; - (void)saveVideo; +- (NSURL *)videoFileURL; + @end diff --git a/modules/highgui/src/cap_ios_video_camera.mm b/modules/highgui/src/cap_ios_video_camera.mm index 6f7bfa20fc..0a162ebea0 100644 --- a/modules/highgui/src/cap_ios_video_camera.mm +++ b/modules/highgui/src/cap_ios_video_camera.mm @@ -98,90 +98,29 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; { [super start]; - if (self.recordVideo == YES) { -// [self.videoFileOutput startRecordingToOutputFileURL:[self tempFileURL] recordingDelegate:self]; - + if (self.recordVideo == YES) { NSError* error; - if ([[NSFileManager defaultManager] fileExistsAtPath:[self tempFileString]]) [[NSFileManager defaultManager] removeItemAtPath:[self tempFileString] error:&error]; + if ([[NSFileManager defaultManager] fileExistsAtPath:[self videoFileString]]) { + [[NSFileManager defaultManager] removeItemAtPath:[self videoFileString] error:&error]; + } if (error == nil) { - NSLog(@"[Camera] Delete file %@", [self tempFileString]); - } - - - BOOL started = [self.recordAssetWriter startWriting]; - [self.recordAssetWriter startSessionAtSourceTime:kCMTimeZero]; - - NSLog(@"[Camera] Session started? %d", started); - - if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { - NSLog(@"AVAssetWriter status: unknown"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { - NSLog(@"AVAssetWriter status: writing"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { - NSLog(@"AVAssetWriter status: completed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { - NSLog(@"AVAssetWriter status: failed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { - NSLog(@"AVAssetWriter status: cancelled"); - } - - if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { - NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error); - } else { - NSLog(@"[Camera] Recording started"); + NSLog(@"[Camera] Delete file %@", [self videoFileString]); } } } -- (void)pause; -{ - [super pause]; - if (self.recordVideo == YES) { -// [self.videoFileOutput stopRecording]; - - - if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { - NSLog(@"AVAssetWriter status: unknown"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { - NSLog(@"AVAssetWriter status: writing"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { - NSLog(@"AVAssetWriter status: completed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { - NSLog(@"AVAssetWriter status: failed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { - NSLog(@"AVAssetWriter status: cancelled"); - } - - if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { - [self.recordAssetWriter finishWriting]; - NSLog(@"[Camera] recording stopped"); - } else { - NSLog(@"[Camera] Recording Error: asset writer status is not writing"); - } - } -} - - - (void)stop; { [super stop]; - if (self.recordVideo == YES) { - NSLog(@"recording stop"); - if (self.recordAssetWriter.status == AVAssetWriterStatusUnknown) { - NSLog(@"AVAssetWriter status: unknown"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { - NSLog(@"AVAssetWriter status: writing"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCompleted) { - NSLog(@"AVAssetWriter status: completed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusFailed) { - NSLog(@"AVAssetWriter status: failed"); - } else if (self.recordAssetWriter.status == AVAssetWriterStatusCancelled) { - NSLog(@"AVAssetWriter status: cancelled"); - } - + self.videoDataOutput = nil; + if (videoDataOutputQueue) { + dispatch_release(videoDataOutputQueue); + } + + if (self.recordVideo == YES) { if (self.recordAssetWriter.status == AVAssetWriterStatusWriting) { [self.recordAssetWriter finishWriting]; @@ -194,11 +133,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; self.recordAssetWriterInput = nil; self.recordPixelBufferAdaptor = nil; } - - self.videoDataOutput = nil; - if (videoDataOutputQueue) { - dispatch_release(videoDataOutputQueue); - } [self.customPreviewLayer removeFromSuperlayer]; self.customPreviewLayer = nil; @@ -405,15 +339,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; - (void)createVideoFileOutput; { - /* - if (self.recordVideo == YES) { - self.videoFileOutput = [[AVCaptureMovieFileOutput alloc] init]; - if ( [self.captureSession canAddOutput:self.videoFileOutput] ) { - [self.captureSession addOutput:self.videoFileOutput]; - } - } - */ - /* Video File Output in H.264, via AVAsserWriter */ NSLog(@"Create Video with dimensions %dx%d", self.imageWidth, self.imageHeight); @@ -426,21 +351,18 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; self.recordAssetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings]; - - /* I'm going to push pixel buffers to it, so will need a - AVAssetWriterPixelBufferAdaptor, to expect the same 32BGRA input as I've - asked the AVCaptureVideDataOutput to supply */ + int pixelBufferFormat = (self.grayscaleMode == YES) ? kCVPixelFormatType_420YpCbCr8BiPlanarFullRange : kCVPixelFormatType_32BGRA; self.recordPixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:self.recordAssetWriterInput - sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey,nil]]; + sourcePixelBufferAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:pixelBufferFormat], kCVPixelBufferPixelFormatTypeKey, nil]]; NSError* error = nil; - NSLog(@"Create AVAssetWriter with url: %@", [self tempFileURL]); - self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self tempFileURL] + NSLog(@"Create AVAssetWriter with url: %@", [self videoFileURL]); + self.recordAssetWriter = [AVAssetWriter assetWriterWithURL:[self videoFileURL] fileType:AVFileTypeMPEG4 error:&error]; if (error != nil) { @@ -448,10 +370,9 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; } [self.recordAssetWriter addInput:self.recordAssetWriterInput]; - self.recordAssetWriterInput.expectsMediaDataInRealTime = NO; + self.recordAssetWriterInput.expectsMediaDataInRealTime = YES; NSLog(@"[Camera] created AVAssetWriter"); - } @@ -580,8 +501,6 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; } - - // render buffer dispatch_sync(dispatch_get_main_queue(), ^{ self.customPreviewLayer.contents = (__bridge id)dstImage; @@ -589,16 +508,26 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; if (self.recordVideo == YES) { - // a very dense way to keep track of the time at which this frame - // occurs relative to the output stream, but it's just an example! + lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); +// CMTimeShow(lastSampleTime); + if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { + [self.recordAssetWriter startWriting]; + [self.recordAssetWriter startSessionAtSourceTime:lastSampleTime]; + if (self.recordAssetWriter.status != AVAssetWriterStatusWriting) { + NSLog(@"[Camera] Recording Error: asset writer status is not writing: %@", self.recordAssetWriter.error); + return; + } else { + NSLog(@"[Camera] Video recording started"); + } + } - // TODO reset frame number - static int64_t frameNumber = 0; if (self.recordAssetWriterInput.readyForMoreMediaData) { - [self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer - withPresentationTime:CMTimeMake(frameNumber, self.defaultFPS)]; + if (! [self.recordPixelBufferAdaptor appendPixelBuffer:imageBuffer + withPresentationTime:lastSampleTime] ) { + NSLog(@"Video Writing Error"); + } } - frameNumber++; + } @@ -627,14 +556,14 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; } ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; - if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self tempFileURL]]) { - [library writeVideoAtPathToSavedPhotosAlbum:[self tempFileURL] + if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:[self videoFileURL]]) { + [library writeVideoAtPathToSavedPhotosAlbum:[self videoFileURL] completionBlock:^(NSURL *assetURL, NSError *error){}]; } } -- (NSURL *)tempFileURL; +- (NSURL *)videoFileURL; { NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; NSURL *outputURL = [NSURL fileURLWithPath:outputPath]; @@ -647,7 +576,7 @@ static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; -- (NSString *)tempFileString; +- (NSString *)videoFileString; { NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"]; return outputPath; From bbf679267ae1ffdd4bdedb5497a8e03ed059678b Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Wed, 22 Aug 2012 15:48:57 +0400 Subject: [PATCH 53/58] modified facedetect to properly display very oblong objects and optionally flip image horizontally (for profile face detection). Added LBP cascades by Attila Novak for profile face detection and silverware detection (those are results of GSoC 2012) --- data/lbpcascades/lbpcascade_profileface.xml | 1275 ++++++++++++++++++ data/lbpcascades/lbpcascade_silverware.xml | 1279 +++++++++++++++++++ samples/c/facedetect.cpp | 90 +- 3 files changed, 2615 insertions(+), 29 deletions(-) create mode 100755 data/lbpcascades/lbpcascade_profileface.xml create mode 100755 data/lbpcascades/lbpcascade_silverware.xml diff --git a/data/lbpcascades/lbpcascade_profileface.xml b/data/lbpcascades/lbpcascade_profileface.xml new file mode 100755 index 0000000000..a9e8437306 --- /dev/null +++ b/data/lbpcascades/lbpcascade_profileface.xml @@ -0,0 +1,1275 @@ + + + + + BOOST + LBP + 34 + 20 + + GAB + 9.9500000476837158e-001 + 3.0000001192092896e-001 + 9.4999999999999996e-001 + 1 + 100 + + 256 + 1 + 16 + + + <_> + 4 + -5.9480339288711548e-001 + + <_> + + 0 -1 114 -2360321 -82228595 -771518211 -713436773 + -1060447799 -810385271 -2004135683 -2566104 + + -8.0942183732986450e-001 5.9530025720596313e-001 + <_> + + 0 -1 54 -649134608 -1060077114 1375916272 -719981432 + 1073801352 33024 281198795 -5246465 + + -7.7979278564453125e-001 5.4052764177322388e-001 + <_> + + 0 -1 12 -960266913 -495857599 -1068498864 -867970987 + 457398579 -1174173695 1749041235 1849162079 + + -8.0028575658798218e-001 5.0435048341751099e-001 + <_> + + 0 -1 120 -1228145793 -807247727 18059735 -138644520 + 998980043 -41250583 673112549 -1930366540 + + -7.7902388572692871e-001 4.9006074666976929e-001 + + <_> + 6 + -5.4879629611968994e-001 + + <_> + + 0 -1 6 -254346881 -746143606 -1039596583 1963430479 + -263790449 -1073545213 698505999 -1349357 + + -6.6315788030624390e-001 6.0000002384185791e-001 + <_> + + 0 -1 112 -134225985 -684228389 -988213089 -684716007 + -1966960899 -896630615 152815840 -864497420 + + -7.0195454359054565e-001 5.8843690156936646e-001 + <_> + + 0 -1 53 -35923461 520818827 -1862167847 856916291 68141197 + 2072530978 304306417 526079163 + + -6.4593964815139771e-001 5.7274609804153442e-001 + <_> + + 0 -1 101 -2097665 -1781432163 588321018 -1677405808 + -1968469982 -1450147831 -1467632684 -593693808 + + -7.2959578037261963e-001 4.9470889568328857e-001 + <_> + + 0 -1 79 -205847273 -1088716541 285266431 1393693056 + 293931101 -1634205688 -452263692 -111136684 + + -7.0331865549087524e-001 5.2564400434494019e-001 + <_> + + 0 -1 126 579801457 -670613495 -1065269989 -117095565 + -1295163359 -779534335 -1744220101 -1355860 + + -7.5121974945068359e-001 4.5217981934547424e-001 + + <_> + 4 + -4.3886357545852661e-001 + + <_> + + 0 -1 20 -346563793 1217040543 -1324639677 206303367 + -260894653 1165249072 1359168335 1652518863 + + -8.3054625988006592e-001 5.5417186021804810e-001 + <_> + + 0 -1 69 -925898078 -917290147 -2147368790 -1995968378 + 1203961890 1765910571 789128481 -4201473 + + -7.5220447778701782e-001 6.1290657520294189e-001 + <_> + + 0 -1 7 -425790473 -368916470 -1065172848 -1877712894 + -1067360254 -847191997 1342400518 -680037517 + + -7.8469508886337280e-001 5.9731280803680420e-001 + <_> + + 0 -1 5 -260315918 -1567751150 -805289977 1721229843 + 1644296976 1954742530 824530213 -8392601 + + -7.3686408996582031e-001 5.6347119808197021e-001 + + <_> + 6 + -4.6629825234413147e-001 + + <_> + + 0 -1 111 -67634177 -72175593 -246181185 -144772036 + -1465917455 -1426934837 -345249307 -539041852 + + -7.1692305803298950e-001 5.5034482479095459e-001 + <_> + + 0 -1 47 -1048705 -96415158 -1996126927 67301684 -659873481 + 1800863745 -402143413 1647570815 + + -7.6134461164474487e-001 4.7370144724845886e-001 + <_> + + 0 -1 119 1905247351 -1111526689 1426654203 -116427277 + 1731664419 -81052249 1051905317 -1628448513 + + -5.9460461139678955e-001 6.1952447891235352e-001 + <_> + + 0 -1 2 578486263 -2115313530 -788268733 -1122507629 + -343408719 2127242147 -85406399 -37295 + + -6.0801470279693604e-001 5.8719038963317871e-001 + <_> + + 0 -1 127 -1147176065 52139167 21156225 -540503783 -771529299 + -33325024 -671045243 -1913073360 + + -7.4383884668350220e-001 5.1643568277359009e-001 + <_> + + 0 -1 93 -319091633 -58633529 1166906391 1854443149 + 1267403009 -1198817246 1208634960 -35661669 + + -6.8595260381698608e-001 5.5931246280670166e-001 + + <_> + 8 + -6.0948312282562256e-001 + + <_> + + 0 -1 102 -747899393 -543522675 545333467 -34230241 + -1572626245 -17790840 -1182162691 -1078427420 + + -6.0826772451400757e-001 4.6491229534149170e-001 + <_> + + 0 -1 38 -103812609 503024467 -2121908081 722834075 + 1375757518 2022089353 197321677 2077719203 + + -6.2948691844940186e-001 4.8044654726982117e-001 + <_> + + 0 -1 19 -774429826 -607461158 1158791644 -971587409 + -1732167611 2015560010 -1278549257 -159911361 + + -5.9694272279739380e-001 4.7999730706214905e-001 + <_> + + 0 -1 122 735837495 -875325281 152208339 -741020481 + -1471817477 -1165246433 -1450830159 -1696546384 + + -6.4947181940078735e-001 4.2661586403846741e-001 + <_> + + 0 -1 104 -629063145 -49708711 50692231 1973945160 157637120 + 2056259593 1771350547 -78911181 + + -6.2496536970138550e-001 4.4524449110031128e-001 + <_> + + 0 -1 67 -74189973 -803307502 688005268 1600057378 -131870050 + -1600503318 571446250 -386668002 + + -5.5046343803405762e-001 5.6090569496154785e-001 + <_> + + 0 -1 81 586347861 -2071051852 -250078020 -1455374076 + 546287843 1216708619 -1853707673 -35130912 + + -6.3877129554748535e-001 4.7911971807479858e-001 + <_> + + 0 -1 22 -1436568057 1555188001 164315 2084672259 1809869105 + 1132626050 1223430266 -596124761 + + -6.4428490400314331e-001 4.7921949625015259e-001 + + <_> + 8 + -5.4387503862380981e-001 + + <_> + + 0 -1 44 -783680003 -771883143 -302055943 -5898247 -253370375 + -1996628131 1625947386 -2004157446 + + -5.2870607376098633e-001 5.9474670886993408e-001 + <_> + + 0 -1 49 -586034977 -41205679 352424062 -163145456 151126042 + -1171652503 1208036058 -9019322 + + -5.6763833761215210e-001 4.8789894580841064e-001 + <_> + + 0 -1 39 1402589836 1363509256 103583 823365787 -1861443377 + 412131360 539718283 1002160350 + + -5.9899079799652100e-001 4.9562713503837585e-001 + <_> + + 0 -1 113 -783429121 -1559215981 286355953 -794820602 + 461510679 -611662910 -2136237584 -96429424 + + -6.3842493295669556e-001 4.3330931663513184e-001 + <_> + + 0 -1 99 -1365839532 -1291265163 1091604493 965968977 + 147472779 -1466925055 -2013090821 -1410703205 + + -5.8633142709732056e-001 5.0152444839477539e-001 + <_> + + 0 -1 26 1846469631 -788479850 268796195 -754872317 + 1630603451 -896532480 1208092751 -72652777 + + -5.9243172407150269e-001 4.7917708754539490e-001 + <_> + + 0 -1 85 -715395062 -113037167 1342198133 -552594287 + 411123713 11059209 -2012512153 -877809205 + + -6.9079184532165527e-001 4.2610234022140503e-001 + <_> + + 0 -1 100 -526391817 -921022135 -1593630697 671093393 + -2004270453 -1962835840 -1870413655 -1597095644 + + -6.5030521154403687e-001 4.4748127460479736e-001 + + <_> + 8 + -6.3195121288299561e-001 + + <_> + + 0 -1 109 -674761315 -581726065 352407899 -83717423 + -660870145 -1165915966 -326837763 -927182608 + + -7.3185729980468750e-001 3.3258172869682312e-001 + <_> + + 0 -1 97 860755579 -707063662 1361264863 1065505299 + -1022866435 -1776123776 -1865661700 -1615196136 + + -6.1147916316986084e-001 3.7205791473388672e-001 + <_> + + 0 -1 15 -678435969 -106962866 268652561 -826396597 + -802066313 1931092070 1208025439 1211582847 + + -6.8679082393646240e-001 3.6285603046417236e-001 + <_> + + 0 -1 86 -1573074550 -2080337595 299991 110482176 268552379 + -310373944 596185787 -1428952165 + + -6.4654982089996338e-001 4.1456297039985657e-001 + <_> + + 0 -1 30 -72637790 -1258143612 1342937104 -544352374 + -1046875163 -121076606 -786059128 -71702400 + + -5.2772462368011475e-001 4.9787566065788269e-001 + <_> + + 0 -1 89 -683288417 -218031996 33734999 -16115386 -2013259561 + -2008907509 -1978533232 -352342880 + + -5.2718847990036011e-001 5.2839303016662598e-001 + <_> + + 0 -1 10 -268764033 -1078984772 -65537 -281182212 -524291 -1 + -8489090 -4227265 + + -5.0513482093811035e-001 5.8522778749465942e-001 + <_> + + 0 -1 82 -570445845 784662143 -268435661 -1292701712 + -436263043 -1367507075 -671091243 -751108132 + + -5.2438414096832275e-001 5.4709094762802124e-001 + + <_> + 8 + -5.9874147176742554e-001 + + <_> + + 0 -1 27 -721421649 -1001940437 2300046 -720004829 -792686333 + 1908900882 -160055232 -134763633 + + -5.7692307233810425e-001 3.7921348214149475e-001 + <_> + + 0 -1 78 -1764279809 -1755824061 1937871313 -42069793 + -1241158993 -1196293937 -1576828673 -70371296 + + -4.7039109468460083e-001 4.8607903718948364e-001 + <_> + + 0 -1 29 -795875130 432079111 285457049 -620658641 -780072971 + 1158283432 -226254016 1839935243 + + -6.2938809394836426e-001 4.1353255510330200e-001 + <_> + + 0 -1 33 -37236389 1654493543 202129823 1788182787 + -1186162321 1912913933 -122942838 1968176815 + + -5.9031385183334351e-001 4.1488575935363770e-001 + <_> + + 0 -1 88 1903888863 -286828472 -2125248034 -623115882 + -268301806 -894826357 -2046633148 -696873056 + + -6.3875061273574829e-001 4.0209171175956726e-001 + <_> + + 0 -1 123 -87223501 -1873424249 -1878929092 -586710990 + -643825151 -1039040192 -285122488 -264093 + + -5.4196298122406006e-001 4.5856228470802307e-001 + <_> + + 0 -1 52 -780030833 1363755203 -385150929 25502018 1214818435 + -1020786271 -1870036478 1200354241 + + -5.2826374769210815e-001 5.3351372480392456e-001 + <_> + + 0 -1 84 -1724706499 -184429355 620844509 -179010317 + -1610327896 -341801844 -1190328066 1755915264 + + -5.7672232389450073e-001 4.4138705730438232e-001 + + <_> + 9 + -5.4533123970031738e-001 + + <_> + + 0 -1 48 -254347649 -565919658 1079050328 1090502875 + 1895985446 2013437961 -916419445 -53481573 + + -5.8105266094207764e-001 3.3599999547004700e-001 + <_> + + 0 -1 65 2030928895 1438877010 1124143121 258207763 + 1361199276 1527410834 2072519624 1004267991 + + -5.9629368782043457e-001 3.6112698912620544e-001 + <_> + + 0 -1 45 -247204964 -242712316 54544644 892459288 1888023456 + -2138044280 -802615208 13199500 + + -6.5467655658721924e-001 3.0486112833023071e-001 + <_> + + 0 -1 3 -430509345 -1865653973 554091143 -1069121312 + 1091180718 50577994 -1031731181 -211321225 + + -5.8759629726409912e-001 3.9526104927062988e-001 + <_> + + 0 -1 106 -741412064 -255623164 1090945848 -1687760764 + 42428760 -1064762741 -1861683196 -81029101 + + -6.5875691175460815e-001 3.4154877066612244e-001 + <_> + + 0 -1 128 -464010241 762112 285299147 -589082223 1373135017 + -2138955645 1057005712 -526876236 + + -6.5968728065490723e-001 3.3614772558212280e-001 + <_> + + 0 -1 80 -666744719 -635780797 33637339 -887860848 + -1073532217 -108904320 440608996 -1100753973 + + -5.0520354509353638e-001 4.4810971617698669e-001 + <_> + + 0 -1 28 -1580738774 -1506653838 302055688 -721223615 + 1427604224 -1566332144 1078565791 -558431977 + + -5.5560898780822754e-001 4.3426483869552612e-001 + <_> + + 0 -1 103 957796629 538644536 352997725 80838797 453085387 + -1165492198 285346042 1487077737 + + -5.5915868282318115e-001 4.0778505802154541e-001 + + <_> + 9 + -6.7299038171768188e-001 + + <_> + + 0 -1 0 -882973185 -620584737 279035921 -673986422 + -1568464349 -2105466877 1468391879 -38825 + + -5.7544225454330444e-001 3.4235453605651855e-001 + <_> + + 0 -1 90 -1820101795 -1336770299 285245717 -57216724 + -502134548 -1425341984 -1475618680 -1195896480 + + -6.6810834407806396e-001 2.7653357386589050e-001 + <_> + + 0 -1 9 -100197449 -457893579 200991 1964749325 -754875920 + 1897044675 1669843618 -70792821 + + -4.9064287543296814e-001 4.3120625615119934e-001 + <_> + + 0 -1 117 -792114173 -544111547 537001999 2034569362 + -1065213888 1630052634 -1450583484 -532405661 + + -6.4218991994857788e-001 3.6113587021827698e-001 + <_> + + 0 -1 107 -1564241697 -1429683702 -2062974587 -1900539448 + -1040078205 -394262006 -188628336 -390485984 + + -5.9181970357894897e-001 3.5756480693817139e-001 + <_> + + 0 -1 4 1893434787 -1945108258 82458 -318734161 -939347837 + 684196040 1078496869 2133023515 + + -6.1955446004867554e-001 3.4674292802810669e-001 + <_> + + 0 -1 31 -196247204 1964277780 -1810886012 21827851 + -364280891 -1062338560 -536741128 -362562814 + + -5.2849757671356201e-001 4.1380330920219421e-001 + <_> + + 0 -1 61 -1929140897 353472529 -721412674 -1228123782 + -392951233 -1442693096 672800826 -232914898 + + -5.7934975624084473e-001 3.9208874106407166e-001 + <_> + + 0 -1 72 -1004361296 -1069243858 268710018 1393598601 + 213956864 417530145 -912735606 1327495627 + + -7.5585323572158813e-001 2.6728668808937073e-001 + + <_> + 9 + -7.1303337812423706e-001 + + <_> + + 0 -1 23 -557797393 1524138462 277074064 -737259367 + -1878818960 -81600384 -1740109301 -59267505 + + -6.7397260665893555e-001 1.9793814420700073e-001 + <_> + + 0 -1 42 -1222377543 960610456 -2013138684 -989277927 + -1010064731 -802979830 -645806439 -885143219 + + -4.5935314893722534e-001 4.1904711723327637e-001 + <_> + + 0 -1 124 -783292542 -728791016 1342570700 1481418249 + 1258825942 -1580563964 -1178136688 -272306640 + + -6.3012123107910156e-001 2.9463621973991394e-001 + <_> + + 0 -1 46 1369396573 -188563225 22085642 -1005861886 + 2023260232 -1123842045 -2146991925 1245170171 + + -5.2092707157135010e-001 3.9743596315383911e-001 + <_> + + 0 -1 64 1540188400 1976259599 -805025279 864127692 544944 + 1484935304 -2147056504 1002584738 + + -6.5315401554107666e-001 3.1758561730384827e-001 + <_> + + 0 -1 77 -188606981 -1873391210 16842830 -117157654 + -1576842600 -1454767992 -518835576 -1625272280 + + -5.8580338954925537e-001 3.4936144948005676e-001 + <_> + + 0 -1 18 -473497030 -477572088 16842905 -12164860 184698994 + 1350566019 -2143169323 1405313030 + + -6.0962837934494019e-001 3.0044576525688171e-001 + <_> + + 0 -1 92 -528022006 -611028904 1075937757 -577660920 + 1073809492 -1341620207 -1475846395 -162412743 + + -6.6547930240631104e-001 3.1993752717971802e-001 + <_> + + 0 -1 116 -2062347245 35311783 406966429 -640155632 + -1904205761 -2012610494 399245455 -937752211 + + -4.8515367507934570e-001 4.3642494082450867e-001 + + <_> + 10 + -1.1831332445144653e+000 + + <_> + + 0 -1 115 -912525479 -2146793066 247327 -554139184 320582141 + -1442774971 1552517769 -1464330096 + + -7.2892564535140991e-001 1.2876711785793304e-001 + <_> + + 0 -1 41 -182757566 -683667118 268566545 -540408959 + 1547915506 2014497074 1817806103 -549486525 + + -5.6024330854415894e-001 2.8734233975410461e-001 + <_> + + 0 -1 13 -1396013057 -175218480 536903951 -35946104 -92067077 + 956498056 -200474487 1331907188 + + -5.5237007141113281e-001 3.2844060659408569e-001 + <_> + + 0 -1 17 2110443855 1547702666 -1874853670 1083212172 + -2004008413 -498614008 572624451 1179093527 + + -7.2481799125671387e-001 2.6627025008201599e-001 + <_> + + 0 -1 43 -1751428966 -1626324992 -1073540847 -783806124 + -2146909454 -913440767 -2138941303 -558233160 + + -4.4304186105728149e-001 4.1505634784698486e-001 + <_> + + 0 -1 37 -576405461 -1625709950 1627439763 1116373274 + 1622902452 1107834529 975868423 2074176171 + + -5.6509882211685181e-001 3.5433205962181091e-001 + <_> + + 0 -1 118 1171205664 1426522307 49281 563122240 -791985520 + -930869245 -364148081 -590624140 + + -5.6250953674316406e-001 3.3341854810714722e-001 + <_> + + 0 -1 76 1162033968 1180991656 16859165 230787289 -2104786299 + -1819967351 1118240928 -343561865 + + -4.7331553697586060e-001 4.1576251387596130e-001 + <_> + + 0 -1 110 -2147085315 -1228897088 -2146839339 -1751314339 + -531605907 -393183232 1804153563 -1399324416 + + -5.8979070186614990e-001 3.7525305151939392e-001 + <_> + + 0 -1 55 1581887865 999817729 151311688 331546624 -991625824 + -938834941 1837335184 852075394 + + -5.4071021080017090e-001 4.0077716112136841e-001 + + <_> + 10 + -6.4480733871459961e-001 + + <_> + + 0 -1 16 -510660401 -884555766 272896026 -12189566 + -1685363509 -662568805 1073840823 -545105785 + + -5.3361344337463379e-001 2.7807486057281494e-001 + <_> + + 0 -1 48 -557408354 2115155922 -2130669353 1616707591 + 693193240 -1569554175 -1743918878 1983596555 + + -5.3364741802215576e-001 3.1411096453666687e-001 + <_> + + 0 -1 108 -413278733 83935516 536961502 1452278484 + -2004277212 -391683967 -1426466672 -85395040 + + -7.4530494213104248e-001 2.3025059700012207e-001 + <_> + + 0 -1 32 -938623022 1469386887 822151432 421593370 + -1433793568 -1602191360 -527916919 680112651 + + -4.6078306436538696e-001 4.0021440386772156e-001 + <_> + + 0 -1 50 1619785226 -1004367410 1417725137 126732357 + 148062614 -625983352 -712398335 -412918226 + + -4.9818846583366394e-001 3.6678382754325867e-001 + <_> + + 0 -1 24 -1064322531 1351938204 196691 -561840073 -1978859471 + -649944954 -2003664885 -1172094197 + + -4.7309580445289612e-001 4.2868506908416748e-001 + <_> + + 0 -1 96 -1878961904 1360035888 -1073721317 -1051487863 + -431841087 1628112896 -2112640640 -1829440828 + + -6.9250243902206421e-001 2.8783574700355530e-001 + <_> + + 0 -1 62 67496095 391741589 -2146154237 96245592 -893992548 + 982687872 571488264 278906307 + + -6.4613574743270874e-001 3.0145862698554993e-001 + <_> + + 0 -1 73 -415771792 1208487966 339825796 1792117580 + 1128517807 144965669 -536376816 732856538 + + -6.9449120759963989e-001 3.0338683724403381e-001 + <_> + + 0 -1 40 -1991530440 324215457 -2080275930 -1857940798 + 1342685625 721420800 1250592988 1493903457 + + -7.0043331384658813e-001 2.5916099548339844e-001 + + <_> + 10 + -6.0248321294784546e-001 + + <_> + + 0 -1 21 -16537745 2114438797 1409323561 1691064397 + -207434939 822260754 -384857461 2031088579 + + -6.1256545782089233e-001 1.7948718369007111e-001 + <_> + + 0 -1 1 -95427858 67117166 -1308426467 -1962693439 601886855 + 924320187 1661215701 2078945158 + + -6.8756872415542603e-001 2.2317354381084442e-001 + <_> + + 0 -1 121 -1853361185 -619857007 16793601 -184516476 + -1422775873 -488996831 1476610285 -926297672 + + -5.2260422706604004e-001 3.2479336857795715e-001 + <_> + + 0 -1 105 -267171326 1436635177 1937772829 -2092859315 + -769638067 -2122268534 1502103583 -18894227 + + -5.2588832378387451e-001 3.4061828255653381e-001 + <_> + + 0 -1 83 1880187281 -1862250368 303299 960921986 -2002701917 + -1593343958 -334888263 1058018448 + + -6.9037044048309326e-001 2.7262538671493530e-001 + <_> + + 0 -1 34 -2125487365 1347551377 -1861970752 1368654274 + -1064675233 436275211 327448684 2068015115 + + -5.3338903188705444e-001 3.2425448298454285e-001 + <_> + + 0 -1 36 1192659162 235536712 1078002258 428089414 + -2138651204 -1937242101 507742421 1932739127 + + -6.4654779434204102e-001 3.0722403526306152e-001 + <_> + + 0 -1 14 -805047416 -1962622822 -2013265442 2030239751 + 1082134810 1744963592 -1836871485 -249326965 + + -5.7250964641571045e-001 3.1499111652374268e-001 + <_> + + 0 -1 75 -650653297 170234379 -2063527695 448823424 + -2139088862 319586315 -2067685344 -1347692410 + + -5.4618871212005615e-001 3.8171616196632385e-001 + <_> + + 0 -1 56 -168821125 -1107300354 -536871052 -1125515426 + -1795721360 -1672085508 1845358040 -2114327569 + + -4.2669427394866943e-001 5.0532561540603638e-001 + + <_> + 11 + -1.1912760734558105e+000 + + <_> + + 0 -1 11 -1043414305 -1735900650 268517385 -1137929054 + -1048411462 -2011152253 -1957405841 -497557425 + + -5.7042253017425537e-001 2.1933962404727936e-001 + <_> + + 0 -1 71 -233469310 1360073157 376971 626087057 -1180588024 + -1191067261 -1474310132 830601690 + + -5.3927713632583618e-001 2.9026004672050476e-001 + <_> + + 0 -1 35 -1599643389 42074270 -1811918838 -949960625 + 1564707361 289538187 1204527649 -112006873 + + -6.0980087518692017e-001 2.8851604461669922e-001 + <_> + + 0 -1 59 585529126 -1100070936 -1342177537 833961983 + 1306961797 1986559992 -810088568 -1082149201 + + -3.2345715165138245e-001 5.5635309219360352e-001 + <_> + + 0 -1 95 1107806555 2030223765 17039707 -1224163308 + -1073053535 -1291837432 822618633 -121972608 + + -6.5054124593734741e-001 3.1912675499916077e-001 + <_> + + 0 -1 51 -171583461 -1660890605 268504396 453157697 + -1065215606 -1740602879 1824636801 1940062923 + + -4.7275745868682861e-001 4.2362514138221741e-001 + <_> + + 0 -1 87 -799546379 -2097769968 293605405 -21571376 285294733 + 136347650 -930405536 -69420863 + + -5.5549502372741699e-001 3.3842340111732483e-001 + <_> + + 0 -1 60 -594509036 -267114166 35413 -1052598126 545325639 + -1207959408 -1073643381 682827807 + + -5.4805672168731689e-001 3.7224516272544861e-001 + <_> + + 0 -1 63 1513710022 194882313 1109000450 28010496 -601835264 + -645791614 -1041880446 1561822180 + + -5.3384119272232056e-001 3.7635508179664612e-001 + <_> + + 0 -1 125 -754581391 -246595569 -2113336948 -1855323709 + 1090531337 -931133310 950984 -3971805 + + -5.2334308624267578e-001 4.0167775750160217e-001 + <_> + + 0 -1 58 -361268680 662383988 2147483638 -209756289 + -1375932428 -1895890954 -1744855042 -1142215109 + + -3.4343415498733521e-001 6.1590969562530518e-001 + + <_> + 10 + -7.7425497770309448e-001 + + <_> + + 0 -1 66 -716447302 -602037376 1090519043 -150261760 + 342934202 -2034138749 1141152394 -351301493 + + -4.8867926001548767e-001 3.4062498807907104e-001 + <_> + + 0 -1 98 -2071985592 -700120831 1078417460 672719121 + 1082264136 -209075063 -1438988203 -1465205245 + + -7.1539443731307983e-001 2.4058867990970612e-001 + <_> + + 0 -1 74 872558624 331821072 1610649929 -1181384552 + -2130081587 -92209146 -612134248 -1199562344 + + -4.4142067432403564e-001 3.7935256958007813e-001 + <_> + + 0 -1 68 -791554721 -737771072 2425605 740044819 1208549387 + 973897998 1124108962 802102203 + + -4.6558478474617004e-001 4.2193859815597534e-001 + <_> + + 0 -1 8 1893114270 -1013792636 360523 -586362838 -1073151001 + -2146917824 -2104934391 -875596965 + + -5.0676107406616211e-001 3.5864940285682678e-001 + <_> + + 0 -1 91 574816266 -2011773950 1476495634 580227538 + -2146781128 -2147448830 1901535891 -692616573 + + -6.1020326614379883e-001 3.0061775445938110e-001 + <_> + + 0 -1 70 2125429880 2080309246 -285282561 2142961407 + -1259516274 1073741823 754945025 867497448 + + -4.3854746222496033e-001 4.7815895080566406e-001 + <_> + + 0 -1 94 -1727736509 -1979678624 285229334 1115689064 + 537927788 -1207402368 1098914016 -91503488 + + -6.8697202205657959e-001 3.5183742642402649e-001 + <_> + + 0 -1 57 -528465144 -707035113 -1048575869 1372127361 8651416 + -526909310 -1845360374 -1451016182 + + -4.5901125669479370e-001 4.5875525474548340e-001 + <_> + + 0 -1 25 -2076984798 -533130869 -1060954112 1639977472 + 828440586 1792508680 -1693988801 -13285232 + + -4.8493441939353943e-001 4.3403539061546326e-001 + + <_> + + 0 1 1 9 + <_> + + 0 1 4 7 + <_> + + 0 2 2 6 + <_> + + 0 2 2 10 + <_> + + 0 2 3 4 + <_> + + 0 3 3 8 + <_> + + 0 4 1 8 + <_> + + 0 5 2 9 + <_> + + 0 7 1 8 + <_> + + 0 7 5 7 + <_> + + 0 9 1 5 + <_> + + 0 9 2 6 + <_> + + 0 10 3 7 + <_> + + 0 11 1 3 + <_> + + 0 12 2 1 + <_> + + 0 13 3 7 + <_> + + 0 14 1 1 + <_> + + 0 14 3 4 + <_> + + 0 16 1 1 + <_> + + 0 19 3 5 + <_> + + 0 20 3 4 + <_> + + 0 21 3 4 + <_> + + 0 22 2 4 + <_> + + 0 25 3 3 + <_> + + 0 25 4 3 + <_> + + 1 0 5 10 + <_> + + 1 2 1 9 + <_> + + 1 4 4 8 + <_> + + 1 4 5 9 + <_> + + 1 6 3 5 + <_> + + 1 9 2 3 + <_> + + 1 11 2 4 + <_> + + 1 15 3 2 + <_> + + 1 20 3 3 + <_> + + 1 28 2 2 + <_> + + 2 0 2 3 + <_> + + 2 0 3 5 + <_> + + 2 0 4 8 + <_> + + 2 3 4 5 + <_> + + 2 4 5 5 + <_> + + 2 5 2 5 + <_> + + 2 7 5 9 + <_> + + 2 8 1 3 + <_> + + 2 12 1 2 + <_> + + 2 13 3 3 + <_> + + 2 14 2 2 + <_> + + 2 16 3 5 + <_> + + 2 18 3 5 + <_> + + 2 22 2 4 + <_> + + 2 31 3 1 + <_> + + 3 0 2 3 + <_> + + 3 1 3 5 + <_> + + 3 1 3 8 + <_> + + 3 2 3 6 + <_> + + 3 8 4 6 + <_> + + 3 10 2 4 + <_> + + 3 14 2 2 + <_> + + 3 16 1 1 + <_> + + 3 18 1 1 + <_> + + 3 19 1 1 + <_> + + 3 19 1 2 + <_> + + 3 31 2 1 + <_> + + 4 4 4 4 + <_> + + 4 5 2 7 + <_> + + 4 6 2 4 + <_> + + 4 6 3 4 + <_> + + 4 7 2 8 + <_> + + 4 12 3 5 + <_> + + 4 19 2 3 + <_> + + 5 0 5 7 + <_> + + 5 3 4 4 + <_> + + 5 3 5 4 + <_> + + 5 5 2 8 + <_> + + 5 12 4 4 + <_> + + 5 22 1 1 + <_> + + 6 21 3 3 + <_> + + 6 26 2 2 + <_> + + 6 30 1 1 + <_> + + 6 31 1 1 + <_> + + 6 31 2 1 + <_> + + 7 0 2 3 + <_> + + 7 9 3 7 + <_> + + 7 17 1 1 + <_> + + 7 31 1 1 + <_> + + 7 31 2 1 + <_> + + 8 0 4 1 + <_> + + 8 5 2 4 + <_> + + 8 10 3 6 + <_> + + 8 16 2 1 + <_> + + 8 25 3 2 + <_> + + 8 30 1 1 + <_> + + 9 0 1 1 + <_> + + 9 0 3 2 + <_> + + 9 0 3 4 + <_> + + 9 15 2 1 + <_> + + 9 24 3 3 + <_> + + 9 29 1 1 + <_> + + 9 31 1 1 + <_> + + 10 4 2 2 + <_> + + 10 8 1 3 + <_> + + 10 15 1 3 + <_> + + 10 26 2 1 + <_> + + 10 30 1 1 + <_> + + 10 31 3 1 + <_> + + 11 0 3 2 + <_> + + 11 1 3 4 + <_> + + 11 5 3 8 + <_> + + 11 14 1 1 + <_> + + 11 23 2 2 + <_> + + 11 27 2 2 + <_> + + 11 31 1 1 + <_> + + 12 22 2 3 + <_> + + 12 29 1 1 + <_> + + 13 23 2 1 + <_> + + 13 24 1 3 + <_> + + 13 29 1 1 + <_> + + 13 31 2 1 + <_> + + 14 1 2 2 + <_> + + 14 1 2 6 + <_> + + 14 2 2 1 + <_> + + 14 24 2 2 + <_> + + 14 26 2 2 + <_> + + 14 28 1 1 + <_> + + 15 4 1 1 + <_> + + 15 24 1 1 + <_> + + 17 0 1 3 + <_> + + 17 3 1 4 + <_> + + 17 23 1 2 + <_> + + 17 27 1 1 + diff --git a/data/lbpcascades/lbpcascade_silverware.xml b/data/lbpcascades/lbpcascade_silverware.xml new file mode 100755 index 0000000000..33aa443c24 --- /dev/null +++ b/data/lbpcascades/lbpcascade_silverware.xml @@ -0,0 +1,1279 @@ + + + + + BOOST + LBP + 80 + 12 + + GAB + 9.9500000476837158e-001 + 3.0000001192092896e-001 + 9.4999999999999996e-001 + 1 + 100 + + 256 + 1 + 16 + + + <_> + 4 + -8.2867860794067383e-002 + + <_> + + 0 -1 99 -268435521 -486543361 -258 1659633406 -134217857 + 1702887279 -134217929 -184549377 + + -7.5000000000000000e-001 8.6380833387374878e-001 + <_> + + 0 -1 39 -540541017 -1060113913 -781245688 -477121697 + -1818664155 1105186857 -505961467 -152575569 + + -7.9976779222488403e-001 7.5056612491607666e-001 + <_> + + 0 -1 101 -479208497 -353380921 -855254781 -1566689761 + -454302869 1893310787 -271591561 -134222965 + + -7.1062028408050537e-001 7.7380746603012085e-001 + <_> + + 0 -1 41 -338958865 -925383977 -1438297681 -981777969 + -882901177 1913369038 -135286729 1995959223 + + -7.8616768121719360e-001 6.9309240579605103e-001 + + <_> + 5 + -7.7058833837509155e-001 + + <_> + + 0 -1 14 -34089161 -2245 1878980471 -8687769 -134316045 + 1744797563 -8388737 1795146607 + + -6.1089491844177246e-001 7.3594772815704346e-001 + <_> + + 0 -1 32 -707274321 1896302609 1132560802 -183140351 17019099 + 830472347 -1993621429 1440074510 + + -6.4869755506515503e-001 5.6941097974777222e-001 + <_> + + 0 -1 4 -1055898237 -104492975 -1795141251 1464975384 + -1602043461 -914358144 1111543953 -2067496448 + + -6.0432785749435425e-001 5.5685383081436157e-001 + <_> + + 0 -1 96 -520160401 2063466495 -65665 -134217729 -50462805 + 1761476478 1693969709 1910503031 + + -5.6237226724624634e-001 6.2263637781143188e-001 + <_> + + 0 -1 6 -1479564374 -954482597 16859161 -799804534 268468874 + 713187329 1108033665 -714619755 + + -6.9048601388931274e-001 5.3264212608337402e-001 + + <_> + 5 + -7.1249550580978394e-001 + + <_> + + 0 -1 21 -34638473 -553976197 -134217865 -159715533 + -142901385 -272629761 -8421377 -956303361 + + -6.4170038700103760e-001 7.0683228969573975e-001 + <_> + + 0 -1 100 -8389777 -185860353 -277 -2097152001 -161 + -209780865 -1 -529006609 + + -5.5270516872406006e-001 6.9983023405075073e-001 + <_> + + 0 -1 118 -545259537 -276857217 -1258291302 1652358910 + -134236308 1735819126 -16812809 -221249673 + + -5.6243920326232910e-001 6.2150186300277710e-001 + <_> + + 0 -1 19 -342885713 -1369882213 -2079215310 -765214587 + -2113207945 1074365452 1393631959 1409022707 + + -6.8943935632705688e-001 5.3469669818878174e-001 + <_> + + 0 -1 23 -506991005 1360417115 -1844809365 -821575604 + 21178499 986120459 1347943419 -969541850 + + -6.7428857088088989e-001 5.5008578300476074e-001 + + <_> + 6 + -3.0183684825897217e-001 + + <_> + + 0 -1 31 -144703505 -143130625 -17 -134381841 -143130625 + 2012741567 -134218802 -134217841 + + -5.3079712390899658e-001 7.5616836547851563e-001 + <_> + + 0 -1 35 -137887809 -1924805943 1363218446 -817782134 + 1099022547 1082327168 -1279204784 1128784467 + + -6.4090979099273682e-001 5.3444361686706543e-001 + <_> + + 0 -1 15 -786433589 -515129128 277173650 -132673121 + -884037451 1137229866 1938662135 -676336865 + + -5.2920126914978027e-001 5.9623366594314575e-001 + <_> + + 0 -1 92 -1897400451 -1627924747 -335548553 -1 1257762559 + -2113929417 -419433067 -235309193 + + -5.5294114351272583e-001 5.8814722299575806e-001 + <_> + + 0 -1 112 -187176146 1743897116 -1878957040 542033563 + 1372582934 823282242 -158609727 -779295046 + + -6.8665105104446411e-001 4.4378995895385742e-001 + <_> + + 0 -1 9 1676637640 1887961346 16875658 1977614736 1682145753 + 813744265 -842338550 1930548135 + + -7.5830078125000000e-001 3.9562159776687622e-001 + + <_> + 8 + -3.9228534698486328e-001 + + <_> + + 0 -1 25 -167774345 -6689161 -2097153 -4194541 -282329093 -1 + -1 -352323601 + + -4.7727271914482117e-001 7.4114018678665161e-001 + <_> + + 0 -1 2 -1051598753 -1005571964 1900827102 2065404120 + -1207262247 -120553331 -1725955392 -494812414 + + -5.2365595102310181e-001 5.3981113433837891e-001 + <_> + + 0 -1 116 -2142770433 -1601462143 16842760 -804892128 1032369 + 268763273 1091011104 -1142957585 + + -4.7790464758872986e-001 5.4881525039672852e-001 + <_> + + 0 -1 87 -532155537 1351188929 1073823759 -1253637875 + -721321497 -662691837 -955278809 1623500836 + + -6.8072116374969482e-001 3.7135115265846252e-001 + <_> + + 0 -1 113 -1996457508 -2146282492 -1728016135 -578347007 + -1609004859 193626505 1153570968 -1920333632 + + -5.7289212942123413e-001 4.6210876107215881e-001 + <_> + + 0 -1 56 -972008109 -691003372 -2147413749 2098355010 + 143009971 -1744174583 -1073051430 617488921 + + -5.9549087285995483e-001 4.8842963576316833e-001 + <_> + + 0 -1 48 26 1971388449 419479901 2080931848 -1140292918 + -1719074813 -2130476842 -268398592 + + -5.8355164527893066e-001 4.7890499234199524e-001 + <_> + + 0 -1 57 -1052266874 167813132 -2130690045 -703061621 + -131874777 -662142838 -1064730555 1119947703 + + -6.9379311800003052e-001 3.9936643838882446e-001 + + <_> + 9 + -6.6581231355667114e-001 + + <_> + + 0 -1 29 2080314175 -112910205 805323551 1024016674 + 1073891387 -2137847805 1653140111 -7676933 + + -5.5957448482513428e-001 5.4044550657272339e-001 + <_> + + 0 -1 94 -1358956801 -100880986 -1887436809 1073741823 + -1896350220 -838860811 268434686 -1912602633 + + -4.3124794960021973e-001 5.6135851144790649e-001 + <_> + + 0 -1 76 -26230993 1357905647 -1358958674 -135266305 -524434 + -176291841 -142622837 -1005125829 + + -4.6799373626708984e-001 5.1660954952239990e-001 + <_> + + 0 -1 30 -313836176 -742240245 16818511 -1391787262 + 1632363443 -156630911 -83631445 248984215 + + -6.2023061513900757e-001 3.9792594313621521e-001 + <_> + + 0 -1 91 -612895966 591778561 1073812490 369347088 + -1870223303 556335107 553910792 1907094058 + + -6.2148678302764893e-001 4.1758581995964050e-001 + <_> + + 0 -1 46 -1430257749 -672663689 -218104082 -135266322 + -1493174275 -873463809 -276826113 -690006715 + + -5.1617449522018433e-001 5.2012032270431519e-001 + <_> + + 0 -1 123 1088746207 1489289603 16781456 -443461355 + -762795606 -670564192 -1465814774 -101527550 + + -5.0202989578247070e-001 5.0987190008163452e-001 + <_> + + 0 -1 53 -1001679641 -955695103 25248080 -738078457 671123502 + 193003713 -1836523327 -216026117 + + -5.2692401409149170e-001 5.3243070840835571e-001 + <_> + + 0 -1 89 2147417937 -1048642 -1039 -1766457361 -134236382 + -1922646177 -16777473 -1534591162 + + -4.6150138974189758e-001 5.6634509563446045e-001 + + <_> + 8 + -1.2349532842636108e+000 + + <_> + + 0 -1 67 -142902409 -67142273 1878982639 -1182802113 -75841 + -274219146 -88604929 -31817921 + + -4.5625588297843933e-001 5.7534247636795044e-001 + <_> + + 0 -1 128 -808330661 1390004234 1107406871 -2098932967 + -767440829 1208655939 -1971196977 1351600587 + + -5.7236993312835693e-001 4.1942635178565979e-001 + <_> + + 0 -1 0 -805307409 -1052697 -65684 -4233 -134217745 -4194453 + -696778831 -708062879 + + -4.5485407114028931e-001 5.5909335613250732e-001 + <_> + + 0 -1 119 -169888509 1150652435 1074791064 541757442 + -645182635 989929472 1262741126 1963976639 + + -6.4869618415832520e-001 3.9796143770217896e-001 + <_> + + 0 -1 38 -912524801 811171970 33644801 -717151469 -2108956437 + 294158344 1109713681 1900266000 + + -5.0387507677078247e-001 5.1329559087753296e-001 + <_> + + 0 -1 20 -746687625 -200802301 1073872962 285491202 + 1208512717 -2138664446 -1837102693 1174835902 + + -5.9465301036834717e-001 4.4057011604309082e-001 + <_> + + 0 -1 16 -442903927 -988184502 -717209211 1443168395 + -1465793521 1252524168 1107337938 -1050414557 + + -5.9043467044830322e-001 4.3687704205513000e-001 + <_> + + 0 -1 104 -1692667790 -612286452 -1056931520 437452806 + -2136309078 -401536992 -1987928929 -1033981310 + + -5.0495445728302002e-001 4.9910807609558105e-001 + + <_> + 9 + -5.4583048820495605e-001 + + <_> + + 0 -1 97 -419954689 -570949699 2147417599 -1 -872415749 + -301989897 -872433670 -268443689 + + -4.0734556317329407e-001 7.1092438697814941e-001 + <_> + + 0 -1 3 -1062674253 1929486475 197402 1841550219 135268235 + -1165491808 956369290 1258896162 + + -5.4886269569396973e-001 4.1644170880317688e-001 + <_> + + 0 -1 37 -620271105 -901300206 1359008346 -603537150 + 1355455189 596312193 -247999129 -728767550 + + -5.1914668083190918e-001 3.9419922232627869e-001 + <_> + + 0 -1 17 -1072700149 546031429 12798103 1881656595 35238042 + 682232321 176931799 1148695251 + + -5.4100900888442993e-001 4.0588796138763428e-001 + <_> + + 0 -1 71 -522857685 1350893957 17339597 1999601732 -779974469 + -359071607 1879296642 -1236927697 + + -4.9249285459518433e-001 4.4877073168754578e-001 + <_> + + 0 -1 93 2037497904 492944831 -2013291075 -754983169 + 1837104414 -671812233 -1660989976 -973105033 + + -4.6483671665191650e-001 4.8267844319343567e-001 + <_> + + 0 -1 33 -553943182 -100663369 -1327169 -181207174 -805896236 + -16777225 -32770 -344459717 + + -3.9679497480392456e-001 5.6408804655075073e-001 + <_> + + 0 -1 44 -8439301 -9502850 2147412095 2134171367 1467968283 + -555876513 1719612907 -959121 + + -3.7275579571723938e-001 6.2219065427780151e-001 + <_> + + 0 -1 62 -2086686357 -2143072184 1073745988 -1878839231 + 1221503177 -2113732606 1133091218 1470880455 + + -5.5160778760910034e-001 4.4197219610214233e-001 + + <_> + 8 + -4.9482953548431396e-001 + + <_> + + 0 -1 124 803987455 -1207959557 -1073747969 -3 -1879048193 + -1720221705 -1073744641 -1212159499 + + -4.2883211374282837e-001 5.8106172084808350e-001 + <_> + + 0 -1 1 -1520569905 -125497088 1360134399 -49444069 + -1065189105 -612134877 -1497194288 -1006112575 + + -4.8296096920967102e-001 4.3431344628334045e-001 + <_> + + 0 -1 108 -67112229 -797503462 268623881 1083056391 + -1874187198 1879638016 -804355463 1985162053 + + -6.1597704887390137e-001 3.4508374333381653e-001 + <_> + + 0 -1 26 -686760009 1468434576 1140918535 -880733942 12599987 + -1304752000 -1593784081 115557220 + + -5.7973521947860718e-001 4.0324980020523071e-001 + <_> + + 0 -1 115 -753405796 4259842 -872415136 85172613 154534824 + 8454145 -2147292968 1094185899 + + -4.7171372175216675e-001 4.6018373966217041e-001 + <_> + + 0 -1 64 -737160572 2107229470 1478238399 386729999 46739708 + -1717532540 134302191 1502456202 + + -4.7625115513801575e-001 4.6307522058486938e-001 + <_> + + 0 -1 63 574973114 1079378118 151608 -1089433600 683881170 + 1234370560 25761968 1305471639 + + -5.4804503917694092e-001 4.2817059159278870e-001 + <_> + + 0 -1 126 -913048353 -1333444591 303141015 1107341569 + -1727960821 1644167297 -1190753878 1418524891 + + -6.3843786716461182e-001 3.2018747925758362e-001 + + <_> + 10 + -4.7552201151847839e-001 + + <_> + + 0 -1 54 -17825929 -8718489 -34111631 -135004289 -1358954497 + -16814213 -151556225 -285220369 + + -4.1965106129646301e-001 5.5681818723678589e-001 + <_> + + 0 -1 88 -1856526326 -645691871 337711324 1464176998 + -1602581814 -1710751608 168420078 -1341468062 + + -4.0517404675483704e-001 4.9981650710105896e-001 + <_> + + 0 -1 45 -741223945 -1627185101 822169913 407916675 + -897447857 589300224 540099855 -1156899883 + + -4.4794428348541260e-001 4.3524059653282166e-001 + <_> + + 0 -1 66 258608606 -1120993285 -419517441 -578240642 + -1879056401 -1101037569 -13383 -28301584 + + -3.9371734857559204e-001 5.2872020006179810e-001 + <_> + + 0 -1 117 -350280689 -829730738 -1073461695 38377489 + -645158785 839057410 -1249137694 1882566387 + + -5.7474929094314575e-001 3.8859930634498596e-001 + <_> + + 0 -1 34 1536523031 -952168281 -1855975139 -854621937 + -939095838 -1744699368 -796270511 1582955555 + + -5.4318642616271973e-001 4.1631007194519043e-001 + <_> + + 0 -1 51 1393782562 319525363 8471383 1368384004 889651722 + 1921550554 -1836930098 1660195204 + + -7.2387772798538208e-001 2.8236424922943115e-001 + <_> + + 0 -1 78 1675075922 637567168 -2130116204 -1890844654 + 34255055 167907336 1091555477 -2142773065 + + -5.3113341331481934e-001 3.7920853495597839e-001 + <_> + + 0 -1 7 1164149387 1433912608 16876979 1595080980 1275865262 + -1446313974 1241665562 173580528 + + -5.0643980503082275e-001 4.4159597158432007e-001 + <_> + + 0 -1 129 -111949961 -783789413 268583504 -923765997 + -1073657336 -1340440574 -394149886 1216081042 + + -5.0880813598632813e-001 4.1170257329940796e-001 + + <_> + 11 + -6.9445723295211792e-001 + + <_> + + 0 -1 106 -487588613 -118095873 -1 2109472735 -1258291202 + -101712129 -33832963 -67652237 + + -4.0311419963836670e-001 6.2951332330703735e-001 + <_> + + 0 -1 49 -268435473 -353372166 2138045906 -4121 -276824105 + 1317007308 -41945099 -134484017 + + -3.5493713617324829e-001 5.5815106630325317e-001 + <_> + + 0 -1 5 1460877355 -15613689 558207061 -1623109371 + -1926723379 244908044 -113047169 1414649856 + + -5.8201593160629272e-001 3.5618588328361511e-001 + <_> + + 0 -1 103 -669296387 189940185 -1860046723 -1760460773 + -1740078915 -931100536 276828352 -1917868015 + + -4.2647001147270203e-001 4.6035429835319519e-001 + <_> + + 0 -1 107 -2109233498 -602287230 -1054785005 1360101827 + 1099137177 -318504822 -1341497202 232232049 + + -4.9850422143936157e-001 4.4256457686424255e-001 + <_> + + 0 -1 40 -54286241 -1608934766 286327519 -1270398764 + 1267376258 1636335746 542720627 1966594122 + + -5.5573022365570068e-001 3.9825862646102905e-001 + <_> + + 0 -1 18 -904213325 1133543618 67508251 -714997735 1094779186 + 160088201 872654991 -903019733 + + -5.2738076448440552e-001 3.8662704825401306e-001 + <_> + + 0 -1 70 1275766299 1347454976 150995380 -217382907 + 1661501627 -788494333 1259046051 -1006600122 + + -4.6260216832160950e-001 4.6852749586105347e-001 + <_> + + 0 -1 121 -367803633 420562962 36765796 -502050533 1380984391 + 268601345 536897573 -995624251 + + -5.2821987867355347e-001 4.4226339459419250e-001 + <_> + + 0 -1 68 -470086117 1069514507 -268472471 1936420849 + -1904232854 1475346303 -160432647 -258802070 + + -4.5063796639442444e-001 5.2728754281997681e-001 + <_> + + 0 -1 85 -698610339 -1504477166 1267372697 822280328 + -909606742 -561903583 -1658732533 962675013 + + -5.5067950487136841e-001 3.9346820116043091e-001 + + <_> + 9 + -7.5511032342910767e-001 + + <_> + + 0 -1 27 -485801045 -1031585761 285212749 -1013038975 + 427848842 -1006632832 -1039468406 -162905189 + + -4.8945146799087524e-001 4.7218933701515198e-001 + <_> + + 0 -1 114 -962887670 1547862275 -1827077881 1140871689 + -536829941 -763363328 -264142181 1112595267 + + -6.1379230022430420e-001 3.4447920322418213e-001 + <_> + + 0 -1 111 -784109321 320069633 1073811463 1074292770 + -2138957664 -2130001880 -2147252214 315289683 + + -5.6861025094985962e-001 3.7049382925033569e-001 + <_> + + 0 -1 80 -679857295 -17928596 -328961 991442748 1064728144 + -357040523 -1082493190 -1368229638 + + -3.9095887541770935e-001 6.0248941183090210e-001 + <_> + + 0 -1 82 175736687 -17072405 2130705262 -218107907 + -1358978530 1692925804 787824558 -672137257 + + -4.0445902943611145e-001 6.0857713222503662e-001 + <_> + + 0 -1 47 -985116365 -553647839 420626839 1968635918 + -1576924981 -360119808 142606465 -795508656 + + -4.8094493150711060e-001 5.1770961284637451e-001 + <_> + + 0 -1 50 -1459109750 33792144 21514342 1343230978 1124110539 + 50364672 441024643 -202393597 + + -5.2261912822723389e-001 4.6680617332458496e-001 + <_> + + 0 -1 98 -259008926 1378975745 -1476362162 1888485505 + 1082744897 571146241 1367392642 -1073229683 + + -6.1712646484375000e-001 3.8970091938972473e-001 + <_> + + 0 -1 125 34318799 1090695442 25199491 1342177299 -2060943181 + 143360000 -2097010032 -907873592 + + -5.3400212526321411e-001 4.4268184900283813e-001 + + <_> + 10 + -4.8388049006462097e-001 + + <_> + + 0 -1 120 -1477443585 -1140940929 -1342185476 1308588029 + -1376256001 218070525 1073741181 -41951875 + + -5.0602412223815918e-001 5.5081558227539063e-001 + <_> + + 0 -1 36 -73936261 -2137816955 -1073659749 -553533419 + -1073706765 -30799693 -972443088 1998113303 + + -4.8420175909996033e-001 4.5527526736259460e-001 + <_> + + 0 -1 77 454566983 420696071 16777221 -2130608117 -1719576352 + -644874174 -2111166071 577795078 + + -6.1467814445495605e-001 3.4610831737518311e-001 + <_> + + 0 -1 60 -1592753970 -251404269 570458176 486621571 + -2130476982 -1207431030 25803086 -2029039551 + + -5.2004736661911011e-001 4.5498979091644287e-001 + <_> + + 0 -1 72 694105913 1907355278 -37129 821280759 931135417 + -923336907 1073716718 -68419540 + + -4.1492795944213867e-001 5.7309722900390625e-001 + <_> + + 0 -1 79 1393265851 -1032732526 264196 -920530793 754211 + 169623560 1149456611 1135983235 + + -5.1638025045394897e-001 4.7242832183837891e-001 + <_> + + 0 -1 73 706130001 -1708251305 1056944760 1006373626 + -1303178409 -813991949 -1183128387 -604048669 + + -4.1649991273880005e-001 5.9589266777038574e-001 + <_> + + 0 -1 95 -904859491 -134017015 1090589192 -587038719 + -167673709 -897449815 152141841 886696449 + + -6.4827072620391846e-001 3.5843926668167114e-001 + <_> + + 0 -1 90 -717057392 690163912 822149263 65803 -1706982525 + -1736400884 534537 -1630082545 + + -5.0309199094772339e-001 5.1634097099304199e-001 + <_> + + 0 -1 12 -1366843350 -2126376671 1041 -566034432 142770176 + 12583104 51712 1116198165 + + -7.9860860109329224e-001 3.1541401147842407e-001 + + <_> + 10 + -5.6616169214248657e-001 + + <_> + + 0 -1 28 -143395977 2004844407 -32897 1840447419 -852257 + -4097 -272630497 -1165502065 + + -4.4186046719551086e-001 5.1379764080047607e-001 + <_> + + 0 -1 8 -519577109 -427718635 -1862262703 -65943231 9163380 + 1112064264 553714225 1157599521 + + -6.9529622793197632e-001 2.9373377561569214e-001 + <_> + + 0 -1 109 990036221 -1392408495 85 -1455423472 537079956 + -1451032448 -2121658180 -1917118335 + + -4.6548900008201599e-001 4.4904062151908875e-001 + <_> + + 0 -1 83 -307263958 1726969598 602799716 -587284627 + -2110304757 -1500547078 1400237979 -194002951 + + -4.4492045044898987e-001 5.2867370843887329e-001 + <_> + + 0 -1 84 -696132137 331497536 -1868546039 -1859480056 + 1753940107 -1029504896 -1341584891 937520647 + + -4.9129620194435120e-001 4.4696673750877380e-001 + <_> + + 0 -1 61 -1056718371 -912911872 67113021 1498447874 134777514 + -1412955989 -2138406733 1082270464 + + -5.8106380701065063e-001 4.1291686892509460e-001 + <_> + + 0 -1 43 -648808770 -703963135 -2147401712 -1858043831 + 1073823883 1074266248 159924795 1879588907 + + -5.2166140079498291e-001 4.6159252524375916e-001 + <_> + + 0 -1 65 538123210 285607041 -2122121208 -1651965941 + -1047953261 1661077920 591915 1689841382 + + -7.4180144071578979e-001 3.0022916197776794e-001 + <_> + + 0 -1 55 805390529 407044123 285213203 211421255 -1702852378 + -1919942528 -2134294375 2066729839 + + -4.8658525943756104e-001 5.4231238365173340e-001 + <_> + + 0 -1 69 -490280822 -1274937328 268439820 1359003776 + -931126870 1220674050 268681287 1997226373 + + -5.6268626451492310e-001 4.5061412453651428e-001 + + <_> + 10 + -9.9649858474731445e-001 + + <_> + + 0 -1 122 -1745100805 -1209164803 -1073770531 -436207891 + -1090560009 234354687 -1610664449 -1082138881 + + -4.0143370628356934e-001 5.6573116779327393e-001 + <_> + + 0 -1 11 -644493203 -1021149047 16847288 -804977263 + 1074438223 1375879170 1099505907 -233072125 + + -4.9022576212882996e-001 4.1356840729713440e-001 + <_> + + 0 -1 110 -1092637138 -1127253650 -604013462 309325799 + 511047567 -562074754 -700452946 -763371997 + + -4.2038223147392273e-001 5.0647193193435669e-001 + <_> + + 0 -1 24 1223739637 -1419051417 1043595135 -215335105 + 376670206 -167870465 -4194306 -222771398 + + -4.0432786941528320e-001 5.9335744380950928e-001 + <_> + + 0 -1 75 -1761937577 -1076383745 -286361737 -9060559 + 2013197781 2013265783 -98370 -1002109842 + + -4.4517979025840759e-001 5.2503407001495361e-001 + <_> + + 0 -1 102 1359075611 -233766656 65681 -1878048735 -1610570746 + 1379991688 -1073689784 -221669373 + + -4.9918147921562195e-001 4.6203434467315674e-001 + <_> + + 0 -1 52 1186053495 -36241670 -268451888 519745529 175382495 + 788381687 2147319804 1327036346 + + -4.6265572309494019e-001 5.1841813325881958e-001 + <_> + + 0 -1 59 -1040035797 1946189894 50247 -1862266624 1090519113 + 268961800 679544907 757613389 + + -5.5006593465805054e-001 4.4656375050544739e-001 + <_> + + 0 -1 10 1610993732 -939524096 1073877397 -267910919 + 151167146 537427968 -769096510 -181428117 + + -5.6329357624053955e-001 4.2267900705337524e-001 + <_> + + 0 -1 86 -1596021624 2047393801 -2130673584 -1856700352 + 327207619 272728192 -2004808112 491069440 + + -6.3942277431488037e-001 3.8081073760986328e-001 + + <_> + 8 + -5.5261385440826416e-001 + + <_> + + 0 -1 13 -648185009 -1315897313 -2139077632 1367998985 + 1744840211 -1005502457 -935198613 -74777841 + + -5.3191488981246948e-001 4.0654698014259338e-001 + <_> + + 0 -1 105 1699432742 -1890377581 1343232064 -1039957887 + -2142687167 637566976 -2122282989 -460871217 + + -5.4315727949142456e-001 3.6683899164199829e-001 + <_> + + 0 -1 81 -67160267 2105388843 -1619001345 1937768302 + -1359003974 -1098989786 -805322771 -1874678652 + + -3.9974156022071838e-001 5.5645257234573364e-001 + <_> + + 0 -1 58 -1072656189 1095241792 16777487 -352059374 4718723 + 1109393544 1074438486 -1848987381 + + -5.0869542360305786e-001 4.9633875489234924e-001 + <_> + + 0 -1 22 226493774 -1911816127 1091108968 26214662 26222970 + -1123287032 -1987040599 -882898875 + + -6.0312920808792114e-001 3.5752627253532410e-001 + <_> + + 0 -1 127 -259153461 -805273578 50364730 -1060208632 + -1708161014 947912705 -2147450710 80388754 + + -6.9576680660247803e-001 3.3376914262771606e-001 + <_> + + 0 -1 42 -800800303 1368954882 75795 2031108096 -2013069281 + 212336778 538680 2064105488 + + -5.6596046686172485e-001 4.3809539079666138e-001 + <_> + + 0 -1 74 -2108215089 1260109955 -1207926768 268812673 + -2146893693 167788680 55189712 -140564306 + + -5.1393473148345947e-001 4.8148322105407715e-001 + + <_> + + 0 0 1 2 + <_> + + 0 0 1 3 + <_> + + 0 0 2 1 + <_> + + 0 0 2 11 + <_> + + 0 0 3 1 + <_> + + 0 0 4 4 + <_> + + 0 1 1 3 + <_> + + 0 1 3 5 + <_> + + 0 2 1 2 + <_> + + 0 2 4 17 + <_> + + 0 3 1 1 + <_> + + 0 4 1 1 + <_> + + 0 4 1 4 + <_> + + 0 4 1 18 + <_> + + 0 4 2 21 + <_> + + 0 5 1 1 + <_> + + 0 5 1 2 + <_> + + 0 5 1 4 + <_> + + 0 5 2 11 + <_> + + 0 6 1 2 + <_> + + 0 7 1 15 + <_> + + 0 7 2 18 + <_> + + 0 13 3 3 + <_> + + 0 13 3 19 + <_> + + 0 14 2 5 + <_> + + 0 14 2 14 + <_> + + 0 16 3 17 + <_> + + 0 17 1 6 + <_> + + 0 17 2 9 + <_> + + 0 18 1 6 + <_> + + 0 19 2 17 + <_> + + 0 21 4 13 + <_> + + 0 21 4 16 + <_> + + 0 22 2 8 + <_> + + 0 36 1 5 + <_> + + 0 40 2 12 + <_> + + 0 43 1 7 + <_> + + 0 46 2 10 + <_> + + 0 48 1 9 + <_> + + 0 48 2 4 + <_> + + 0 50 1 2 + <_> + + 0 56 2 3 + <_> + + 0 71 1 3 + <_> + + 0 74 1 2 + <_> + + 0 77 1 1 + <_> + + 0 77 2 1 + <_> + + 1 0 1 3 + <_> + + 1 0 2 1 + <_> + + 1 0 3 1 + <_> + + 1 2 1 1 + <_> + + 1 4 1 2 + <_> + + 1 4 3 23 + <_> + + 1 5 2 7 + <_> + + 1 9 1 1 + <_> + + 1 10 2 15 + <_> + + 1 12 2 7 + <_> + + 1 14 2 9 + <_> + + 1 25 2 18 + <_> + + 1 39 2 10 + <_> + + 1 71 1 3 + <_> + + 2 0 1 3 + <_> + + 2 0 2 1 + <_> + + 2 3 1 2 + <_> + + 2 4 1 5 + <_> + + 2 16 3 8 + <_> + + 2 18 3 14 + <_> + + 2 21 2 2 + <_> + + 2 22 1 4 + <_> + + 2 24 1 2 + <_> + + 2 64 1 5 + <_> + + 3 0 2 1 + <_> + + 3 1 3 25 + <_> + + 3 2 3 6 + <_> + + 3 3 2 11 + <_> + + 3 6 1 3 + <_> + + 3 17 1 11 + <_> + + 3 22 3 17 + <_> + + 3 23 1 4 + <_> + + 3 42 1 10 + <_> + + 3 52 1 6 + <_> + + 3 77 1 1 + <_> + + 4 0 2 2 + <_> + + 4 1 1 2 + <_> + + 4 2 1 1 + <_> + + 5 7 2 20 + <_> + + 5 12 2 19 + <_> + + 5 14 1 3 + <_> + + 5 19 2 15 + <_> + + 6 0 1 1 + <_> + + 6 0 2 1 + <_> + + 6 1 2 13 + <_> + + 6 5 2 5 + <_> + + 6 7 2 17 + <_> + + 6 10 2 7 + <_> + + 6 13 2 10 + <_> + + 6 14 2 13 + <_> + + 6 16 2 14 + <_> + + 6 19 2 7 + <_> + + 6 36 1 8 + <_> + + 6 39 2 7 + <_> + + 6 41 2 9 + <_> + + 6 44 2 2 + <_> + + 6 51 2 6 + <_> + + 6 77 2 1 + <_> + + 7 0 1 1 + <_> + + 7 9 1 2 + <_> + + 7 20 1 9 + <_> + + 7 23 1 4 + <_> + + 7 45 1 7 + <_> + + 7 77 1 1 + <_> + + 8 0 1 1 + <_> + + 8 47 1 11 + <_> + + 8 53 1 4 + <_> + + 8 77 1 1 + <_> + + 9 0 1 2 + <_> + + 9 0 1 15 + <_> + + 9 0 1 20 + <_> + + 9 2 1 3 + <_> + + 9 3 1 2 + <_> + + 9 6 1 3 + <_> + + 9 9 1 13 + <_> + + 9 13 1 2 + <_> + + 9 13 1 8 + <_> + + 9 19 1 16 + <_> + + 9 20 1 4 + <_> + + 9 25 1 4 + <_> + + 9 43 1 5 + <_> + + 9 48 1 4 + <_> + + 9 59 1 3 + <_> + + 9 61 1 5 + diff --git a/samples/c/facedetect.cpp b/samples/c/facedetect.cpp index a641a74a8a..69b9e9983a 100644 --- a/samples/c/facedetect.cpp +++ b/samples/c/facedetect.cpp @@ -3,6 +3,7 @@ #include "opencv2/imgproc/imgproc.hpp" #include +#include #include using namespace std; @@ -11,36 +12,41 @@ using namespace cv; static void help() { cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n" - "This classifier can recognize many ~rigid objects, it's most known use is for faces.\n" + "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n" + "It's most known use is for faces.\n" "Usage:\n" "./facedetect [--cascade= this is the primary trained classifier such as frontal face]\n" " [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n" - " [--scale=\n" + " [--scale=]\n" + " [--try-flip]\n" " [filename|camera_index]\n\n" "see facedetect.cmd for one call:\n" - "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3 \n" - "Hit any key to quit.\n" - "Using OpenCV version " << CV_VERSION << "\n" << endl; + "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3\n\n" + "During execution:\n\tHit any key to quit.\n" + "\tUsing OpenCV version " << CV_VERSION << "\n" << endl; } -void detectAndDraw( Mat& img, - CascadeClassifier& cascade, CascadeClassifier& nestedCascade, - double scale); +void detectAndDraw( Mat& img, CascadeClassifier& cascade, + CascadeClassifier& nestedCascade, + double scale, bool tryflip ); -String cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; -String nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; +string cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; +string nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; int main( int argc, const char** argv ) { CvCapture* capture = 0; Mat frame, frameCopy, image; - const String scaleOpt = "--scale="; + const string scaleOpt = "--scale="; size_t scaleOptLen = scaleOpt.length(); - const String cascadeOpt = "--cascade="; + const string cascadeOpt = "--cascade="; size_t cascadeOptLen = cascadeOpt.length(); - const String nestedCascadeOpt = "--nested-cascade"; + const string nestedCascadeOpt = "--nested-cascade"; size_t nestedCascadeOptLen = nestedCascadeOpt.length(); - String inputName; + const string tryFlipOpt = "--try-flip"; + size_t tryFlipOptLen = tryFlipOpt.length(); + string inputName; + bool tryflip = false; help(); @@ -68,6 +74,11 @@ int main( int argc, const char** argv ) scale = 1; cout << " from which we read scale = " << scale << endl; } + else if( tryFlipOpt.compare( 0, tryFlipOptLen, argv[i], tryFlipOptLen ) == 0 ) + { + tryflip = true; + cout << " will try to flip image horizontally to detect assymetric objects\n"; + } else if( argv[i][0] == '-' ) { cerr << "WARNING: Unknown option %s" << argv[i] << endl; @@ -79,10 +90,7 @@ int main( int argc, const char** argv ) if( !cascade.load( cascadeName ) ) { cerr << "ERROR: Could not load classifier cascade" << endl; - cerr << "Usage: facedetect [--cascade=]\n" - " [--nested-cascade[=nested_cascade_path]]\n" - " [--scale[=\n" - " [filename|camera_index]\n" << endl ; + help(); return -1; } @@ -123,7 +131,7 @@ int main( int argc, const char** argv ) else flip( frame, frameCopy, 0 ); - detectAndDraw( frameCopy, cascade, nestedCascade, scale ); + detectAndDraw( frameCopy, cascade, nestedCascade, scale, tryflip ); if( waitKey( 10 ) >= 0 ) goto _cleanup_; @@ -139,7 +147,7 @@ _cleanup_: cout << "In image read" << endl; if( !image.empty() ) { - detectAndDraw( image, cascade, nestedCascade, scale ); + detectAndDraw( image, cascade, nestedCascade, scale, tryflip ); waitKey(0); } else if( !inputName.empty() ) @@ -160,7 +168,7 @@ _cleanup_: image = imread( buf, 1 ); if( !image.empty() ) { - detectAndDraw( image, cascade, nestedCascade, scale ); + detectAndDraw( image, cascade, nestedCascade, scale, tryflip ); c = waitKey(0); if( c == 27 || c == 'q' || c == 'Q' ) break; @@ -180,13 +188,13 @@ _cleanup_: return 0; } -void detectAndDraw( Mat& img, - CascadeClassifier& cascade, CascadeClassifier& nestedCascade, - double scale) +void detectAndDraw( Mat& img, CascadeClassifier& cascade, + CascadeClassifier& nestedCascade, + double scale, bool tryflip ) { int i = 0; double t = 0; - vector faces; + vector faces, faces2; const static Scalar colors[] = { CV_RGB(0,0,255), CV_RGB(0,128,255), CV_RGB(0,255,255), @@ -209,6 +217,21 @@ void detectAndDraw( Mat& img, |CV_HAAR_SCALE_IMAGE , Size(30, 30) ); + if( tryflip ) + { + flip(smallImg, smallImg, 1); + cascade.detectMultiScale( smallImg, faces2, + 1.1, 2, 0 + //|CV_HAAR_FIND_BIGGEST_OBJECT + //|CV_HAAR_DO_ROUGH_SEARCH + |CV_HAAR_SCALE_IMAGE + , + Size(30, 30) ); + for( vector::const_iterator r = faces2.begin(); r != faces2.end(); r++ ) + { + faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height)); + } + } t = (double)cvGetTickCount() - t; printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) ); for( vector::const_iterator r = faces.begin(); r != faces.end(); r++, i++ ) @@ -218,10 +241,19 @@ void detectAndDraw( Mat& img, Point center; Scalar color = colors[i%8]; int radius; - center.x = cvRound((r->x + r->width*0.5)*scale); - center.y = cvRound((r->y + r->height*0.5)*scale); - radius = cvRound((r->width + r->height)*0.25*scale); - circle( img, center, radius, color, 3, 8, 0 ); + + double aspect_ratio = (double)r->width/r->height; + if( 0.75 < aspect_ratio && aspect_ratio < 1.3 ) + { + center.x = cvRound((r->x + r->width*0.5)*scale); + center.y = cvRound((r->y + r->height*0.5)*scale); + radius = cvRound((r->width + r->height)*0.25*scale); + circle( img, center, radius, color, 3, 8, 0 ); + } + else + rectangle( img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)), + cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)), + color, 3, 8, 0); if( nestedCascade.empty() ) continue; smallImgROI = smallImg(*r); From d9185ec21b42198b55b97bef119a9621da34258a Mon Sep 17 00:00:00 2001 From: Alexander Mordvintesv Date: Wed, 22 Aug 2012 16:42:19 +0300 Subject: [PATCH 54/58] added _doc.py -- doc-string ckecking utility added some sample description --- samples/python2/_coverage.py | 37 ++++++++++++++++++++--------------- samples/python2/_doc.py | 14 +++++++++++++ samples/python2/common.py | 4 ++++ samples/python2/demo.py | 4 ++++ samples/python2/distrans.py | 17 ++++++++++------ samples/python2/floodfill.py | 14 ++++++++----- samples/python2/inpaint.py | 17 +++++++++++----- samples/python2/morphology.py | 18 +++++++++++++---- samples/python2/squares.py | 6 ++++++ 9 files changed, 95 insertions(+), 36 deletions(-) create mode 100644 samples/python2/_doc.py diff --git a/samples/python2/_coverage.py b/samples/python2/_coverage.py index e64c7c9bcb..3a365dc589 100644 --- a/samples/python2/_coverage.py +++ b/samples/python2/_coverage.py @@ -1,24 +1,29 @@ +''' +Utility for measuring python opencv API coverage by samples. +''' + from glob import glob import cv2 import re -cv2_callable = set(['cv2.'+name for name in dir(cv2) if callable( getattr(cv2, name) )]) +if __name__ == '__main__': + cv2_callable = set(['cv2.'+name for name in dir(cv2) if callable( getattr(cv2, name) )]) -found = set() -for fn in glob('*.py'): - print ' --- ', fn - code = open(fn).read() - found |= set(re.findall('cv2?\.\w+', code)) + found = set() + for fn in glob('*.py'): + print ' --- ', fn + code = open(fn).read() + found |= set(re.findall('cv2?\.\w+', code)) -cv2_used = found & cv2_callable -cv2_unused = cv2_callable - cv2_used -with open('unused_api.txt', 'w') as f: - f.write('\n'.join(sorted(cv2_unused))) + cv2_used = found & cv2_callable + cv2_unused = cv2_callable - cv2_used + with open('unused_api.txt', 'w') as f: + f.write('\n'.join(sorted(cv2_unused))) -r = 1.0 * len(cv2_used) / len(cv2_callable) -print '\ncv2 api coverage: %d / %d (%.1f%%)' % ( len(cv2_used), len(cv2_callable), r*100 ) + r = 1.0 * len(cv2_used) / len(cv2_callable) + print '\ncv2 api coverage: %d / %d (%.1f%%)' % ( len(cv2_used), len(cv2_callable), r*100 ) -print '\nold (cv) symbols:' -for s in found: - if s.startswith('cv.'): - print s + print '\nold (cv) symbols:' + for s in found: + if s.startswith('cv.'): + print s diff --git a/samples/python2/_doc.py b/samples/python2/_doc.py new file mode 100644 index 0000000000..1204ae65a5 --- /dev/null +++ b/samples/python2/_doc.py @@ -0,0 +1,14 @@ +''' +Scans current directory for *.py files and reports +ones with missing __doc__ string. +''' + +from glob import glob + +if __name__ == '__main__': + print '--- undocumented files:' + for fn in glob('*.py'): + loc = {} + execfile(fn, loc) + if '__doc__' not in loc: + print fn diff --git a/samples/python2/common.py b/samples/python2/common.py index 883aa9ae23..5415942d5a 100644 --- a/samples/python2/common.py +++ b/samples/python2/common.py @@ -1,3 +1,7 @@ +''' +This module contais some common routines used by other samples. +''' + import numpy as np import cv2 import os diff --git a/samples/python2/demo.py b/samples/python2/demo.py index 3f75703303..9dd7779e19 100644 --- a/samples/python2/demo.py +++ b/samples/python2/demo.py @@ -1,3 +1,7 @@ +''' +Sample-launcher application. +''' + import Tkinter as tk from ScrolledText import ScrolledText from glob import glob diff --git a/samples/python2/distrans.py b/samples/python2/distrans.py index 2cbca0ca20..15b1fac425 100644 --- a/samples/python2/distrans.py +++ b/samples/python2/distrans.py @@ -1,20 +1,25 @@ -import numpy as np -import cv2 -import cv2.cv as cv -from common import make_cmap +''' +Distance transform sample. -help_message = '''USAGE: distrans.py [] +Usage: + distrans.py [] Keys: ESC - exit v - toggle voronoi mode ''' + +import numpy as np +import cv2 +import cv2.cv as cv +from common import make_cmap + if __name__ == '__main__': import sys try: fn = sys.argv[1] except: fn = '../cpp/fruits.jpg' - print help_message + print __doc__ img = cv2.imread(fn, 0) cm = make_cmap('jet') diff --git a/samples/python2/floodfill.py b/samples/python2/floodfill.py index c2d0d70578..7ddc4c01df 100644 --- a/samples/python2/floodfill.py +++ b/samples/python2/floodfill.py @@ -1,9 +1,10 @@ -import numpy as np -import cv2 +''' +Floodfill sample. -help_message = '''USAGE: floodfill.py [] +Usage: + floodfill.py [] -Click on the image to set seed point + Click on the image to set seed point Keys: f - toggle floating range @@ -11,11 +12,14 @@ Keys: ESC - exit ''' +import numpy as np +import cv2 + if __name__ == '__main__': import sys try: fn = sys.argv[1] except: fn = '../cpp/fruits.jpg' - print help_message + print __doc__ img = cv2.imread(fn, True) h, w = img.shape[:2] diff --git a/samples/python2/inpaint.py b/samples/python2/inpaint.py index 6a52d01627..9ed6bab101 100644 --- a/samples/python2/inpaint.py +++ b/samples/python2/inpaint.py @@ -1,8 +1,11 @@ -import numpy as np -import cv2 -from common import Sketcher +''' +Inpainting sample. + +Inpainting repairs damage to images by floodfilling +the damage with surrounding image areas. -help_message = '''USAGE: inpaint.py [] +Usage: + inpaint.py [] Keys: SPACE - inpaint @@ -10,11 +13,15 @@ Keys: ESC - exit ''' +import numpy as np +import cv2 +from common import Sketcher + if __name__ == '__main__': import sys try: fn = sys.argv[1] except: fn = '../cpp/fruits.jpg' - print help_message + print __doc__ img = cv2.imread(fn) img_mark = img.copy() diff --git a/samples/python2/morphology.py b/samples/python2/morphology.py index b7f84fbb28..b2f6e0ebfd 100644 --- a/samples/python2/morphology.py +++ b/samples/python2/morphology.py @@ -1,8 +1,22 @@ +''' +Morphology operations. + +Usage: + morphology.py [] + +Keys: + 1 - change operation + 2 - change structure element shape + ESC - exit +''' + import numpy as np import cv2 if __name__ == '__main__': + print __doc__ + import sys from itertools import cycle from common import draw_str @@ -44,10 +58,6 @@ if __name__ == '__main__': cv2.createTrackbar('op/size', 'morphology', 12, 20, update) cv2.createTrackbar('iters', 'morphology', 1, 10, update) update() - print "Controls:" - print " 1 - change operation" - print " 2 - change structure element shape" - print while True: ch = 0xFF & cv2.waitKey() if ch == 27: diff --git a/samples/python2/squares.py b/samples/python2/squares.py index 6c6b899d83..60b04f2882 100644 --- a/samples/python2/squares.py +++ b/samples/python2/squares.py @@ -1,3 +1,9 @@ +''' +Simple "Square Detector" program. + +Loads several images sequentially and tries to find squares in each image. +''' + import numpy as np import cv2 From 6a5d996ca88387bb9f9b8ae03b6b46bb0a874df4 Mon Sep 17 00:00:00 2001 From: Leonid Beynenson Date: Wed, 22 Aug 2012 17:51:52 +0400 Subject: [PATCH 55/58] Removed the header opencv2/photo/denoising.hpp All the functions from it are moved to the header opencv2/photo/photo.hpp --- .../photo/include/opencv2/photo/denoising.hpp | 79 ------------------- modules/photo/include/opencv2/photo/photo.hpp | 20 ++++- modules/photo/src/denoising.cpp | 2 +- modules/photo/test/test_denoising.cpp | 2 +- 4 files changed, 21 insertions(+), 82 deletions(-) delete mode 100644 modules/photo/include/opencv2/photo/denoising.hpp diff --git a/modules/photo/include/opencv2/photo/denoising.hpp b/modules/photo/include/opencv2/photo/denoising.hpp deleted file mode 100644 index 718c3b73e8..0000000000 --- a/modules/photo/include/opencv2/photo/denoising.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/*M/////////////////////////////////////////////////////////////////////////////////////// -// -// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. -// -// By downloading, copying, installing or using the software you agree to this license. -// If you do not agree to this license, do not download, install, -// copy or use the software. -// -// -// License Agreement -// For Open Source Computer Vision Library -// -// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. -// Copyright (C) 2008-2012, Willow Garage Inc., all rights reserved. -// Third party copyrights are property of their respective owners. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistribution's of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// * Redistribution's in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// -// * The name of the copyright holders may not be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// This software is provided by the copyright holders and contributors "as is" and -// any express or implied warranties, including, but not limited to, the implied -// warranties of merchantability and fitness for a particular purpose are disclaimed. -// In no event shall the Intel Corporation or contributors be liable for any direct, -// indirect, incidental, special, exemplary, or consequential damages -// (including, but not limited to, procurement of substitute goods or services; -// loss of use, data, or profits; or business interruption) however caused -// and on any theory of liability, whether in contract, strict liability, -// or tort (including negligence or otherwise) arising in any way out of -// the use of this software, even if advised of the possibility of such damage. -// -//M*/ - -#ifndef __OPENCV_DENOISING_HPP__ -#define __OPENCV_DENOISING_HPP__ - -#include "opencv2/core/core.hpp" -#include "opencv2/imgproc/imgproc.hpp" -#include - -#ifdef __cplusplus - -/*! \namespace cv - Namespace where all the C++ OpenCV functionality resides - */ -namespace cv -{ - -CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, - int templateWindowSize, int searchWindowSize, int h); - -CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst, - int templateWindowSize, int searchWindowSize, - int h, int hForColorComponents); - -CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, - int imgToDenoiseIndex, int temporalWindowSize, - OutputArray dst, - int templateWindowSize, int searchWindowSize, int h); - -CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, - int imgToDenoiseIndex, int temporalWindowSize, - OutputArray dst, - int templateWindowSize, int searchWindowSize, - int h, int hForColorComponents); - -} -#endif - -#endif diff --git a/modules/photo/include/opencv2/photo/photo.hpp b/modules/photo/include/opencv2/photo/photo.hpp index f771b8fbd9..bae83bbb0e 100644 --- a/modules/photo/include/opencv2/photo/photo.hpp +++ b/modules/photo/include/opencv2/photo/photo.hpp @@ -47,7 +47,6 @@ #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/photo/photo_c.h" -#include "opencv2/photo/denoising.hpp" #ifdef __cplusplus @@ -68,6 +67,25 @@ enum CV_EXPORTS_W void inpaint( InputArray src, InputArray inpaintMask, OutputArray dst, double inpaintRadius, int flags ); + +CV_EXPORTS_W void fastNlMeansDenoising( InputArray src, OutputArray dst, + int templateWindowSize, int searchWindowSize, int h); + +CV_EXPORTS_W void fastNlMeansDenoisingColored( InputArray src, OutputArray dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); + +CV_EXPORTS_W void fastNlMeansDenoisingMulti( InputArrayOfArrays srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + OutputArray dst, + int templateWindowSize, int searchWindowSize, int h); + +CV_EXPORTS_W void fastNlMeansDenoisingColoredMulti( InputArrayOfArrays srcImgs, + int imgToDenoiseIndex, int temporalWindowSize, + OutputArray dst, + int templateWindowSize, int searchWindowSize, + int h, int hForColorComponents); + } #endif diff --git a/modules/photo/src/denoising.cpp b/modules/photo/src/denoising.cpp index 7452d0d3ce..42620575bc 100644 --- a/modules/photo/src/denoising.cpp +++ b/modules/photo/src/denoising.cpp @@ -40,7 +40,7 @@ //M*/ #include "precomp.hpp" -#include "opencv2/photo/denoising.hpp" +#include "opencv2/photo/photo.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "fast_nlmeans_denoising_invoker.hpp" #include "fast_nlmeans_multi_denoising_invoker.hpp" diff --git a/modules/photo/test/test_denoising.cpp b/modules/photo/test/test_denoising.cpp index 39aa699811..6b202f133a 100644 --- a/modules/photo/test/test_denoising.cpp +++ b/modules/photo/test/test_denoising.cpp @@ -41,7 +41,7 @@ //M*/ #include "test_precomp.hpp" -#include "opencv2/photo/denoising.hpp" +#include "opencv2/photo/photo.hpp" #include using namespace cv; From c8b7a8c80c9d290bf9264a73b344d2cea4dbd233 Mon Sep 17 00:00:00 2001 From: Alexey Spizhevoy Date: Thu, 23 Aug 2012 10:47:15 +0400 Subject: [PATCH 56/58] added median-based version of global motion estimation (videostab) --- .../opencv2/videostab/global_motion.hpp | 5 +- modules/videostab/src/global_motion.cpp | 70 ++++++++++++++++++- modules/videostab/src/precomp.hpp | 1 + 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/modules/videostab/include/opencv2/videostab/global_motion.hpp b/modules/videostab/include/opencv2/videostab/global_motion.hpp index 19fcfefaca..9616eae5c4 100644 --- a/modules/videostab/include/opencv2/videostab/global_motion.hpp +++ b/modules/videostab/include/opencv2/videostab/global_motion.hpp @@ -66,7 +66,10 @@ CV_EXPORTS Mat estimateGlobalMotionLeastSquares( InputOutputArray points0, InputOutputArray points1, int model = MM_AFFINE, float *rmse = 0); -CV_EXPORTS Mat estimateGlobalMotionRobust( +CV_EXPORTS Mat estimateGlobalMotionMedian( + InputArray points0, InputArray points1, int model, int size, int niters); + +CV_EXPORTS Mat estimateGlobalMotionRansac( InputArray points0, InputArray points1, int model = MM_AFFINE, const RansacParams ¶ms = RansacParams::default2dMotion(MM_AFFINE), float *rmse = 0, int *ninliers = 0); diff --git a/modules/videostab/src/global_motion.cpp b/modules/videostab/src/global_motion.cpp index 6a40eac8b7..1eb2f0f46b 100644 --- a/modules/videostab/src/global_motion.cpp +++ b/modules/videostab/src/global_motion.cpp @@ -305,7 +305,73 @@ Mat estimateGlobalMotionLeastSquares( } -Mat estimateGlobalMotionRobust( +Mat estimateGlobalMotionMedian( + InputArray points0, InputArray points1, int model, int size, int niters) +{ + // perform 'niters' iterations over points subsets ('size' elements each) estimating + // motions, after that select median motion parameters from the distribution + + CV_Assert(model <= MM_AFFINE); + CV_Assert(points0.type() == points1.type()); + const int npoints = points0.getMat().checkVector(2); + CV_Assert(points1.getMat().checkVector(2) == npoints); + + const Point2f *points0_ = points0.getMat().ptr(); + const Point2f *points1_ = points1.getMat().ptr(); + + // all estimated motions + vector Ms[3][3]; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + Ms[i][j].resize(niters); + + // current hypothesis + vector indices(size); + vector subset0(size); + vector subset1(size); + + RNG rng(0); + + for (int iter = 0; iter < niters; ++iter) + { + for (int i = 0; i < size; ++i) + { + bool ok = false; + while (!ok) + { + ok = true; + indices[i] = static_cast(rng) % npoints; + for (int j = 0; j < i; ++j) + if (indices[i] == indices[j]) + { ok = false; break; } + } + } + for (int i = 0; i < size; ++i) + { + subset0[i] = points0_[indices[i]]; + subset1[i] = points1_[indices[i]]; + } + + Mat_ M = estimateGlobalMotionLeastSquares(subset0, subset1, model, 0); + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + Ms[i][j][iter] = M(i, j); + } + + Mat_ medianM(3, 3); + + for (int i = 0; i < 3; ++i) + for (int j = 0; j < 3; ++j) + { + nth_element(Ms[i][j].begin(), Ms[i][j].begin() + niters/2, Ms[i][j].end()); + medianM(i, j) = Ms[i][j][niters/2]; + } + + return medianM; +} + + +Mat estimateGlobalMotionRansac( InputArray points0, InputArray points1, int model, const RansacParams ¶ms, float *rmse, int *ninliers) { @@ -424,7 +490,7 @@ Mat MotionEstimatorRansacL2::estimate(InputArray points0, InputArray points1, bo Mat_ M; if (motionModel() != MM_HOMOGRAPHY) - M = estimateGlobalMotionRobust( + M = estimateGlobalMotionRansac( points0, points1, motionModel(), ransacParams_, 0, &ninliers); else { diff --git a/modules/videostab/src/precomp.hpp b/modules/videostab/src/precomp.hpp index 6a1fa7c46a..848c54836a 100644 --- a/modules/videostab/src/precomp.hpp +++ b/modules/videostab/src/precomp.hpp @@ -50,6 +50,7 @@ #include #include #include +#include #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/video/video.hpp" From d395bf1522163cceb59b100bab8c01d9b8be1136 Mon Sep 17 00:00:00 2001 From: Anatoly Baksheev Date: Tue, 21 Aug 2012 13:59:27 +0400 Subject: [PATCH 57/58] minor fix in FindTBB added texture_binder --- cmake/CMakeParseArguments.cmake | 138 ++++++++++++++++++++++++ cmake/OpenCVDetectTBB.cmake | 7 +- cmake/OpenCVUtils.cmake | 9 ++ modules/core/CMakeLists.txt | 7 +- modules/gpu/src/cuda/texture_binder.hpp | 92 ++++++++++++++++ 5 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 cmake/CMakeParseArguments.cmake create mode 100644 modules/gpu/src/cuda/texture_binder.hpp diff --git a/cmake/CMakeParseArguments.cmake b/cmake/CMakeParseArguments.cmake new file mode 100644 index 0000000000..7ce4c49ae5 --- /dev/null +++ b/cmake/CMakeParseArguments.cmake @@ -0,0 +1,138 @@ +# CMAKE_PARSE_ARGUMENTS( args...) +# +# CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for +# parsing the arguments given to that macro or function. +# It processes the arguments and defines a set of variables which hold the +# values of the respective options. +# +# The argument contains all options for the respective macro, +# i.e. keywords which can be used when calling the macro without any value +# following, like e.g. the OPTIONAL keyword of the install() command. +# +# The argument contains all keywords for this macro +# which are followed by one value, like e.g. DESTINATION keyword of the +# install() command. +# +# The argument contains all keywords for this macro +# which can be followed by more than one value, like e.g. the TARGETS or +# FILES keywords of the install() command. +# +# When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the +# keywords listed in , and +# a variable composed of the given +# followed by "_" and the name of the respective keyword. +# These variables will then hold the respective value from the argument list. +# For the keywords this will be TRUE or FALSE. +# +# All remaining arguments are collected in a variable +# _UNPARSED_ARGUMENTS, this can be checked afterwards to see whether +# your macro was called with unrecognized parameters. +# +# As an example here a my_install() macro, which takes similar arguments as the +# real install() command: +# +# function(MY_INSTALL) +# set(options OPTIONAL FAST) +# set(oneValueArgs DESTINATION RENAME) +# set(multiValueArgs TARGETS CONFIGURATIONS) +# cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) +# ... +# +# Assume my_install() has been called like this: +# my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) +# +# After the cmake_parse_arguments() call the macro will have set the following +# variables: +# MY_INSTALL_OPTIONAL = TRUE +# MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() +# MY_INSTALL_DESTINATION = "bin" +# MY_INSTALL_RENAME = "" (was not used) +# MY_INSTALL_TARGETS = "foo;bar" +# MY_INSTALL_CONFIGURATIONS = "" (was not used) +# MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" +# +# You can the continue and process these variables. +# +# Keywords terminate lists of values, e.g. if directly after a one_value_keyword +# another recognized keyword follows, this is interpreted as the beginning of +# the new option. +# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in +# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would +# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. + +#============================================================================= +# Copyright 2010 Alexander Neundorf +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + + +if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) + return() +endif() +set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) + + +function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) + # first set all result variables to empty/FALSE + foreach(arg_name ${_singleArgNames} ${_multiArgNames}) + set(${prefix}_${arg_name}) + endforeach(arg_name) + + foreach(option ${_optionNames}) + set(${prefix}_${option} FALSE) + endforeach(option) + + set(${prefix}_UNPARSED_ARGUMENTS) + + set(insideValues FALSE) + set(currentArgName) + + # now iterate over all arguments and fill the result variables + foreach(currentArg ${ARGN}) + list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword + list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword + + if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) + if(insideValues) + if("${insideValues}" STREQUAL "SINGLE") + set(${prefix}_${currentArgName} ${currentArg}) + set(insideValues FALSE) + elseif("${insideValues}" STREQUAL "MULTI") + list(APPEND ${prefix}_${currentArgName} ${currentArg}) + endif() + else(insideValues) + list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) + endif(insideValues) + else() + if(NOT ${optionIndex} EQUAL -1) + set(${prefix}_${currentArg} TRUE) + set(insideValues FALSE) + elseif(NOT ${singleArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "SINGLE") + elseif(NOT ${multiArgIndex} EQUAL -1) + set(currentArgName ${currentArg}) + set(${prefix}_${currentArgName}) + set(insideValues "MULTI") + endif() + endif() + + endforeach(currentArg) + + # propagate the result variables to the caller: + foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) + set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) + endforeach(arg_name) + set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) + +endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs) diff --git a/cmake/OpenCVDetectTBB.cmake b/cmake/OpenCVDetectTBB.cmake index c37a8c34ec..3dba759f9f 100644 --- a/cmake/OpenCVDetectTBB.cmake +++ b/cmake/OpenCVDetectTBB.cmake @@ -21,7 +21,12 @@ elseif(UNIX AND NOT APPLE) endif() if(NOT HAVE_TBB) - set(TBB_DEFAULT_INCLUDE_DIRS "/opt/intel/tbb" "/usr/local/include" "/usr/include" "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" "C:/Program Files (x86)/TBB" "${CMAKE_INSTALL_PREFIX}/include") + set(TBB_DEFAULT_INCLUDE_DIRS + "/opt/intel/tbb" "/usr/local/include" "/usr/include" + "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" + "C:/Program Files (x86)/tbb/include" + "C:/Program Files (x86)/tbb/include" + "${CMAKE_INSTALL_PREFIX}/include") find_path(TBB_INCLUDE_DIRS "tbb/tbb.h" PATHS ${TBB_INCLUDE_DIR} ${TBB_DEFAULT_INCLUDE_DIRS} DOC "The path to TBB headers") if(TBB_INCLUDE_DIRS) diff --git a/cmake/OpenCVUtils.cmake b/cmake/OpenCVUtils.cmake index 7fa56781f8..879d332532 100644 --- a/cmake/OpenCVUtils.cmake +++ b/cmake/OpenCVUtils.cmake @@ -501,3 +501,12 @@ macro(ocv_parse_header2 LIBNAME HDR_PATH VARNAME) endif() endif() endmacro() + + +################################################################################################ +# short command to setup source group +function(ocv_source_group group) + cmake_parse_arguments(OCV_SOURCE_GROUP "" "" "GLOB" ${ARGN}) + file(GLOB srcs ${OCV_SOURCE_GROUP_GLOB}) + source_group(${group} FILES ${srcs}) +endfunction() \ No newline at end of file diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 644e10e5eb..db673c7a2e 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -3,13 +3,14 @@ ocv_add_module(core ${ZLIB_LIBRARIES}) ocv_module_include_directories(${ZLIB_INCLUDE_DIR}) if(HAVE_CUDA) - file(GLOB lib_cuda "src/cuda/*.cu") - source_group("Cuda" FILES "${lib_cuda}") - + ocv_source_group("Src\\Cuda" GLOB "src/cuda/*.cu") ocv_include_directories("${OpenCV_SOURCE_DIR}/modules/gpu/src" "${OpenCV_SOURCE_DIR}/modules/gpu/src/cuda" ${CUDA_INCLUDE_DIRS}) ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef) + + file(GLOB lib_cuda "src/cuda/*.cu") ocv_cuda_compile(cuda_objs ${lib_cuda}) + set(cuda_link_libs ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY}) else() set(lib_cuda "") diff --git a/modules/gpu/src/cuda/texture_binder.hpp b/modules/gpu/src/cuda/texture_binder.hpp new file mode 100644 index 0000000000..812062da80 --- /dev/null +++ b/modules/gpu/src/cuda/texture_binder.hpp @@ -0,0 +1,92 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#ifndef OPENCV_GPU_TEXTURE_BINDER_HPP_ +#define OPENCV_GPU_TEXTURE_BINDER_HPP_ + +#include "opencv2/gpu/devmem2d.hpp" +#include + +namespace cv +{ + namespace gpu + { + class TextureBinder + { + public: + template + TextureBinder(const PtrStepSz& arr, const struct texture& tex) : texref(&tex) + { + cudaChannelFormatDesc desc = cudaCreateChannelDesc(); + cudaSafeCall( cudaBindTexture2D(0, tex, arr.data, desc, arr.cols, arr.rows, arr.step) ); + } + + template + TextureBinder(const PtrSz& arr, const struct texture &tex) : texref(&tex) + { + cudaChannelFormatDesc desc = cudaCreateChannelDesc(); + cudaSafeCall( cudaBindTexture(0, tex, arr.data, desc, arr.size * arr.elemSize()) ); + } + + template + TextureBinder(const A& arr, const struct texture& tex, const cudaChannelFormatDesc& desc) : texref(&tex) + { + cudaSafeCall( cudaBindTexture2D(0, tex, arr.data, desc, arr.cols, arr.rows, arr.step) ); + } + + + ~TextureBinder() + { + cudaSafeCall( cudaUnbindTexture(texref) ); + } + private: + const struct textureReference *texref; + }; + } + + namespace device + { + using pcl::gpu::TextureBinder; + } +} + +#endif /* OPENCV_GPU_TEXTURE_BINDER_HPP_*/ \ No newline at end of file From c8a54f67d4df0e64d44914b4ca37d6d7d5ce3824 Mon Sep 17 00:00:00 2001 From: "marina.kolpakova" Date: Wed, 22 Aug 2012 20:26:32 +0400 Subject: [PATCH 58/58] minor warning fix --- modules/gpu/src/cuda/bf_radius_match.cu | 4 ++-- modules/gpu/src/cuda/canny.cu | 4 ++-- modules/gpu/src/cuda/column_filter.cu | 2 +- modules/gpu/src/cuda/fast.cu | 4 ++-- modules/gpu/src/cuda/hist.cu | 2 +- modules/gpu/src/cuda/integral_image.cu | 4 ++-- modules/gpu/src/cuda/matrix_reductions.cu | 16 ++++++++-------- modules/gpu/src/cuda/pyrlk.cu | 8 ++++---- modules/gpu/src/cuda/row_filter.cu | 2 +- .../gpu/src/nvidia/NPP_staging/NPP_staging.cu | 2 +- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/modules/gpu/src/cuda/bf_radius_match.cu b/modules/gpu/src/cuda/bf_radius_match.cu index 0aa71913b2..c6cba928c1 100644 --- a/modules/gpu/src/cuda/bf_radius_match.cu +++ b/modules/gpu/src/cuda/bf_radius_match.cu @@ -56,7 +56,7 @@ namespace cv { namespace gpu { namespace device __global__ void matchUnrolled(const DevMem2D_ query, int imgIdx, const DevMem2D_ train, float maxDistance, const Mask mask, PtrStepi bestTrainIdx, PtrStepi bestImgIdx, PtrStepf bestDistance, unsigned int* nMatches, int maxCount) { - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) extern __shared__ int smem[]; @@ -168,7 +168,7 @@ namespace cv { namespace gpu { namespace device __global__ void match(const DevMem2D_ query, int imgIdx, const DevMem2D_ train, float maxDistance, const Mask mask, PtrStepi bestTrainIdx, PtrStepi bestImgIdx, PtrStepf bestDistance, unsigned int* nMatches, int maxCount) { - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) extern __shared__ int smem[]; diff --git a/modules/gpu/src/cuda/canny.cu b/modules/gpu/src/cuda/canny.cu index f9dd490bf6..95028c2eaf 100644 --- a/modules/gpu/src/cuda/canny.cu +++ b/modules/gpu/src/cuda/canny.cu @@ -261,7 +261,7 @@ namespace cv { namespace gpu { namespace device __global__ void edgesHysteresisLocal(PtrStepi map, ushort2* st, int rows, int cols) { - #if __CUDA_ARCH__ >= 120 + #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 120) __shared__ int smem[18][18]; @@ -358,7 +358,7 @@ namespace cv { namespace gpu { namespace device __global__ void edgesHysteresisGlobal(PtrStepi map, ushort2* st1, ushort2* st2, int rows, int cols, int count) { - #if __CUDA_ARCH__ >= 120 + #if defined (__CUDA_ARCH__) && __CUDA_ARCH__ >= 120 const int stack_size = 512; diff --git a/modules/gpu/src/cuda/column_filter.cu b/modules/gpu/src/cuda/column_filter.cu index 624bd3fbc0..fb52d6e989 100644 --- a/modules/gpu/src/cuda/column_filter.cu +++ b/modules/gpu/src/cuda/column_filter.cu @@ -64,7 +64,7 @@ namespace cv { namespace gpu { namespace device template __global__ void linearColumnFilter(const DevMem2D_ src, PtrStep dst, const int anchor, const B brd) { - #if __CUDA_ARCH__ >= 200 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 200) const int BLOCK_DIM_X = 16; const int BLOCK_DIM_Y = 16; const int PATCH_PER_BLOCK = 4; diff --git a/modules/gpu/src/cuda/fast.cu b/modules/gpu/src/cuda/fast.cu index 8f904cd985..a511e8f35f 100644 --- a/modules/gpu/src/cuda/fast.cu +++ b/modules/gpu/src/cuda/fast.cu @@ -223,7 +223,7 @@ namespace cv { namespace gpu { namespace device template __global__ void calcKeypoints(const DevMem2Db img, const Mask mask, short2* kpLoc, const unsigned int maxKeypoints, PtrStepi score, const int threshold) { - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) const int j = threadIdx.x + blockIdx.x * blockDim.x + 3; const int i = threadIdx.y + blockIdx.y * blockDim.y + 3; @@ -325,7 +325,7 @@ namespace cv { namespace gpu { namespace device __global__ void nonmaxSupression(const short2* kpLoc, int count, const DevMem2Di scoreMat, short2* locFinal, float* responseFinal) { - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) const int kpIdx = threadIdx.x + blockIdx.x * blockDim.x; diff --git a/modules/gpu/src/cuda/hist.cu b/modules/gpu/src/cuda/hist.cu index a7f45719ea..0a08f82f41 100644 --- a/modules/gpu/src/cuda/hist.cu +++ b/modules/gpu/src/cuda/hist.cu @@ -63,7 +63,7 @@ namespace cv { namespace gpu { namespace device #define MERGE_THREADBLOCK_SIZE 256 - #define USE_SMEM_ATOMICS (__CUDA_ARCH__ >= 120) + #define USE_SMEM_ATOMICS (defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 120)) namespace hist { diff --git a/modules/gpu/src/cuda/integral_image.cu b/modules/gpu/src/cuda/integral_image.cu index ead0ddefec..5cb777dd92 100644 --- a/modules/gpu/src/cuda/integral_image.cu +++ b/modules/gpu/src/cuda/integral_image.cu @@ -59,7 +59,7 @@ namespace cv { namespace gpu { namespace device __global__ void shfl_integral_horizontal(const PtrStep_ img, PtrStep_ integral) { - #if __CUDA_ARCH__ >= 300 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 300) __shared__ int sums[128]; const int id = threadIdx.x; @@ -299,7 +299,7 @@ namespace cv { namespace gpu { namespace device // block sums. __global__ void shfl_integral_vertical(DevMem2D_ integral) { - #if __CUDA_ARCH__ >= 300 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 300) __shared__ unsigned int sums[32][9]; const int tidx = blockIdx.x * blockDim.x + threadIdx.x; diff --git a/modules/gpu/src/cuda/matrix_reductions.cu b/modules/gpu/src/cuda/matrix_reductions.cu index a0be65c3cd..d9b1b5cba4 100644 --- a/modules/gpu/src/cuda/matrix_reductions.cu +++ b/modules/gpu/src/cuda/matrix_reductions.cu @@ -215,7 +215,7 @@ namespace cv { namespace gpu { namespace device maxval[blockIdx.y * gridDim.x + blockIdx.x] = (T)smaxval[0]; } - #if __CUDA_ARCH__ >= 110 + #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -535,7 +535,7 @@ namespace cv { namespace gpu { namespace device findMinMaxLocInSmem(sminval, smaxval, sminloc, smaxloc, tid); - #if __CUDA_ARCH__ >= 110 + #if defined (__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -841,7 +841,7 @@ namespace cv { namespace gpu { namespace device sumInSmem(scount, tid); - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -1034,7 +1034,7 @@ namespace cv { namespace gpu { namespace device sumInSmem(smem, tid); - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -1115,7 +1115,7 @@ namespace cv { namespace gpu { namespace device sumInSmem(smem, tid); sumInSmem(smem + nthreads, tid); - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -1222,7 +1222,7 @@ namespace cv { namespace gpu { namespace device sumInSmem(smem + nthreads, tid); sumInSmem(smem + 2 * nthreads, tid); - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 110 __shared__ bool is_last; if (tid == 0) @@ -1339,7 +1339,7 @@ namespace cv { namespace gpu { namespace device sumInSmem(smem + 2 * nthreads, tid); sumInSmem(smem + 3 * nthreads, tid); - #if __CUDA_ARCH__ >= 110 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 110) __shared__ bool is_last; if (tid == 0) @@ -1975,7 +1975,7 @@ namespace cv { namespace gpu { namespace device for (int c = 0; c < cn; ++c) myVal[c] = op.startValue(); - #if __CUDA_ARCH__ >= 200 + #if defined (__CUDA_ARCH__) && __CUDA_ARCH__ >= 200 // For cc >= 2.0 prefer L1 cache for (int x = threadIdx.x; x < src.cols; x += 256) diff --git a/modules/gpu/src/cuda/pyrlk.cu b/modules/gpu/src/cuda/pyrlk.cu index b06d607687..271660515b 100644 --- a/modules/gpu/src/cuda/pyrlk.cu +++ b/modules/gpu/src/cuda/pyrlk.cu @@ -82,7 +82,7 @@ namespace cv { namespace gpu { namespace device smem3[tid] = val3; __syncthreads(); -#if __CUDA_ARCH__ > 110 +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) if (tid < 128) { smem1[tid] = val1 += smem1[tid + 128]; @@ -138,7 +138,7 @@ namespace cv { namespace gpu { namespace device smem2[tid] = val2; __syncthreads(); -#if __CUDA_ARCH__ > 110 +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) if (tid < 128) { smem1[tid] = val1 += smem1[tid + 128]; @@ -184,7 +184,7 @@ namespace cv { namespace gpu { namespace device smem1[tid] = val1; __syncthreads(); -#if __CUDA_ARCH__ > 110 +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ > 110) if (tid < 128) { smem1[tid] = val1 += smem1[tid + 128]; @@ -271,7 +271,7 @@ namespace cv { namespace gpu { namespace device template __global__ void lkSparse(const float2* prevPts, float2* nextPts, uchar* status, float* err, const int level, const int rows, const int cols) { -#if __CUDA_ARCH__ <= 110 +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ <= 110) __shared__ float smem1[128]; __shared__ float smem2[128]; __shared__ float smem3[128]; diff --git a/modules/gpu/src/cuda/row_filter.cu b/modules/gpu/src/cuda/row_filter.cu index 8963212a23..d40af71496 100644 --- a/modules/gpu/src/cuda/row_filter.cu +++ b/modules/gpu/src/cuda/row_filter.cu @@ -64,7 +64,7 @@ namespace cv { namespace gpu { namespace device template __global__ void linearRowFilter(const DevMem2D_ src, PtrStep dst, const int anchor, const B brd) { - #if __CUDA_ARCH__ >= 200 + #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 200) const int BLOCK_DIM_X = 32; const int BLOCK_DIM_Y = 8; const int PATCH_PER_BLOCK = 4; diff --git a/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu b/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu index f75453930b..0e6aca0889 100644 --- a/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu +++ b/modules/gpu/src/nvidia/NPP_staging/NPP_staging.cu @@ -2070,7 +2070,7 @@ NCVStatus nppiStInterpolateFrames(const NppStInterpolationState *pState) //============================================================================== -#if __CUDA_ARCH__ < 200 +#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 200) // FP32 atomic add static __forceinline__ __device__ float _atomicAdd(float *addr, float val)