From 7e637c134fa3aba40f82124e4283ac8075261cd0 Mon Sep 17 00:00:00 2001 From: Eduard Trulls Date: Tue, 4 Feb 2020 07:28:07 +0000 Subject: [PATCH 01/15] Expose maxIters in findFundamentalMat Lets the user choose the maximum number of iterations the robust estimator runs for, similary to findHomography. This can significantly improve performance (at a computational cost). --- modules/calib3d/include/opencv2/calib3d.hpp | 6 ++++++ modules/calib3d/src/fundam.cpp | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 42761967db..e986c1458c 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -1905,6 +1905,7 @@ point localization, image resolution, and the image noise. @param confidence Parameter used for the RANSAC and LMedS methods only. It specifies a desirable level of confidence (probability) that the estimated matrix is correct. @param mask +@param maxIters The maximum number of robust method iterations. The epipolar geometry is described by the following equation: @@ -1938,6 +1939,11 @@ stereoRectifyUncalibrated to compute the rectification transformation. : findFundamentalMat(points1, points2, FM_RANSAC, 3, 0.99); @endcode */ +CV_EXPORTS_W Mat findFundamentalMat( InputArray points1, InputArray points2, + int method, double ransacReprojThreshold, double confidence, + int maxIters, OutputArray mask = noArray() ); + +/** @overload */ CV_EXPORTS_W Mat findFundamentalMat( InputArray points1, InputArray points2, int method = FM_RANSAC, double ransacReprojThreshold = 3., double confidence = 0.99, diff --git a/modules/calib3d/src/fundam.cpp b/modules/calib3d/src/fundam.cpp index ef09feb3ab..ebfd3af992 100644 --- a/modules/calib3d/src/fundam.cpp +++ b/modules/calib3d/src/fundam.cpp @@ -809,7 +809,7 @@ public: cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2, int method, double ransacReprojThreshold, double confidence, - OutputArray _mask ) + int maxIters, OutputArray _mask ) { CV_INSTRUMENT_REGION(); @@ -861,7 +861,7 @@ cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2, confidence = 0.99; if( (method & ~3) == FM_RANSAC && npoints >= 15 ) - result = createRANSACPointSetRegistrator(cb, 7, ransacReprojThreshold, confidence)->run(m1, m2, F, _mask); + result = createRANSACPointSetRegistrator(cb, 7, ransacReprojThreshold, confidence, maxIters)->run(m1, m2, F, _mask); else result = createLMeDSPointSetRegistrator(cb, 7, confidence)->run(m1, m2, F, _mask); } @@ -872,11 +872,17 @@ cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2, return F; } -cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2, - OutputArray _mask, int method, - double ransacReprojThreshold , double confidence) +cv::Mat cv::findFundamentalMat( cv::InputArray points1, cv::InputArray points2, + int method, double ransacReprojThreshold, double confidence, + cv::OutputArray mask ) { - return cv::findFundamentalMat(_points1, _points2, method, ransacReprojThreshold, confidence, _mask); + return cv::findFundamentalMat(points1, points2, method, ransacReprojThreshold, confidence, 1000, mask); +} + +cv::Mat cv::findFundamentalMat( cv::InputArray points1, cv::InputArray points2, cv::OutputArray mask, + int method, double ransacReprojThreshold, double confidence ) +{ + return cv::findFundamentalMat(points1, points2, method, ransacReprojThreshold, confidence, 1000, mask); } From e0a946848811e36a6620294d9d02cf216f2c7b78 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 7 Mar 2020 15:37:21 +0000 Subject: [PATCH 02/15] cmake: allow extra compile options for tests --- cmake/OpenCVModule.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 082290c710..f014ee7f5b 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -1111,6 +1111,8 @@ macro(__ocv_parse_test_sources tests_type) unset(__currentvar) endmacro() +ocv_check_environment_variables(OPENCV_TEST_EXTRA_CXX_FLAGS_Release) + # this is a command for adding OpenCV performance tests to the module # ocv_add_perf_tests() function(ocv_add_perf_tests) @@ -1257,6 +1259,10 @@ function(ocv_add_accuracy_tests) _ocv_add_precompiled_headers(${the_target}) endif() + if(OPENCV_TEST_EXTRA_CXX_FLAGS_Release) + target_compile_options(${the_target} PRIVATE "$<$:${OPENCV_TEST_EXTRA_CXX_FLAGS_Release}>") + endif() + ocv_add_test_from_target("${the_target}" "Accuracy" "${the_target}") else(OCV_DEPENDENCIES_FOUND) # TODO: warn about unsatisfied dependencies From b927ce18b26bcbcc79443a974bfe8032a915131f Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Sun, 8 Mar 2020 20:15:18 +0300 Subject: [PATCH 03/15] Support for MobileNetV3-SSD from TensorFlow --- modules/dnn/src/tensorflow/tf_importer.cpp | 59 +++++++++++++++++++++- modules/dnn/test/test_tf_importer.cpp | 7 +++ samples/dnn/tf_text_graph_ssd.py | 4 +- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/tensorflow/tf_importer.cpp b/modules/dnn/src/tensorflow/tf_importer.cpp index 53ca75007e..fe7e47f7a0 100644 --- a/modules/dnn/src/tensorflow/tf_importer.cpp +++ b/modules/dnn/src/tensorflow/tf_importer.cpp @@ -458,6 +458,7 @@ private: tensorflow::GraphDef netTxt; std::vector netInputsNames; + std::vector netInputShapes; }; TFImporter::TFImporter(const char *model, const char *config) @@ -1401,6 +1402,27 @@ void TFImporter::populateNet(Net dstNet) netInputsNames.push_back(name); layer_id[name] = 0; } + if (hasLayerAttr(layer, "shape")) + { + const tensorflow::TensorShapeProto& shape = getLayerAttr(layer, "shape").shape(); + MatShape dims(shape.dim_size()); + for (int i = 0; i < dims.size(); ++i) + dims[i] = shape.dim(i).size(); + if (dims.size() == 4 && predictedLayout == DATA_LAYOUT_NHWC) + { + std::swap(dims[1], dims[3]); // NHWC->NCWH + std::swap(dims[2], dims[3]); // NCWH->NCHW + if (dims[0] == -1) // It's OK to have undetermined batch size + dims[0] = 1; + } + bool hasNeg = false; + for (int i = 0; i < dims.size() && !hasNeg; ++i) + { + hasNeg = dims[i] < 0; + } + if (!hasNeg) + netInputShapes.push_back(dims); + } } else if (type == "Split") { // TODO: determining axis index remapping by input dimensions order of input blob @@ -1580,8 +1602,41 @@ void TFImporter::populateNet(Net dstNet) } else { - layerParams.set("operation", "prod"); - int id = dstNet.addLayer(name, "Eltwise", layerParams); + // Check if all the inputs have the same shape. + bool equalInpShapes = true; + MatShape outShape0; + for (int ii = 0; ii < layer.input_size() && !netInputShapes.empty(); ii++) + { + Pin pin = parsePin(layer.input(ii)); + int inpId = layer_id.find(pin.name)->second; + + // Get input shape + MatShape outShape; + std::vector inpShapes, outShapes; + dstNet.getLayerShapes(netInputShapes, inpId, inpShapes, outShapes); + CV_CheckGT(static_cast(outShapes.size()), pin.blobIndex, ""); + outShape = outShapes[pin.blobIndex]; + + if (ii == 0) + { + outShape0 = outShape; + } + else if (outShape != outShape0) + { + equalInpShapes = false; + break; + } + } + + int id; + if (equalInpShapes || netInputShapes.empty()) + { + layerParams.set("operation", "prod"); + id = dstNet.addLayer(name, "Eltwise", layerParams); + } + else + id = dstNet.addLayer(name, "Scale", layerParams); + layer_id[name] = id; for (int ii = 0; ii < layer.input_size(); ii++) diff --git a/modules/dnn/test/test_tf_importer.cpp b/modules/dnn/test/test_tf_importer.cpp index e8064f1c90..8cacae8ea8 100644 --- a/modules/dnn/test/test_tf_importer.cpp +++ b/modules/dnn/test/test_tf_importer.cpp @@ -181,6 +181,13 @@ TEST_P(Test_TensorFlow_layers, eltwise) runTensorFlowNet("eltwise_sub"); } +TEST_P(Test_TensorFlow_layers, channel_broadcast) +{ + if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); + runTensorFlowNet("channel_broadcast"); +} + TEST_P(Test_TensorFlow_layers, pad_and_concat) { runTensorFlowNet("pad_and_concat"); diff --git a/samples/dnn/tf_text_graph_ssd.py b/samples/dnn/tf_text_graph_ssd.py index b466548502..bf1d788d41 100644 --- a/samples/dnn/tf_text_graph_ssd.py +++ b/samples/dnn/tf_text_graph_ssd.py @@ -64,7 +64,7 @@ def createSSDGraph(modelPath, configPath, outputPath): # Nodes that should be kept. keepOps = ['Conv2D', 'BiasAdd', 'Add', 'AddV2', 'Relu', 'Relu6', 'Placeholder', 'FusedBatchNorm', 'DepthwiseConv2dNative', 'ConcatV2', 'Mul', 'MaxPool', 'AvgPool', 'Identity', - 'Sub', 'ResizeNearestNeighbor', 'Pad', 'FusedBatchNormV3'] + 'Sub', 'ResizeNearestNeighbor', 'Pad', 'FusedBatchNormV3', 'Mean'] # Node with which prefixes should be removed prefixesToRemove = ('MultipleGridAnchorGenerator/', 'Concatenate/', 'Postprocessor/', 'Preprocessor/map') @@ -235,7 +235,7 @@ def createSSDGraph(modelPath, configPath, outputPath): # Connect input node to the first layer assert(graph_def.node[0].op == 'Placeholder') # assert(graph_def.node[1].op == 'Conv2D') - weights = graph_def.node[1].input[0] + weights = graph_def.node[1].input[-1] for i in range(len(graph_def.node[1].input)): graph_def.node[1].input.pop() graph_def.node[1].input.append(graph_def.node[0].name) From 4e56c1326f4d2b3f9858bf209b5399be3953deea Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 21 Jan 2020 13:51:03 +0300 Subject: [PATCH 04/15] core: adjust type of allocator_stats counter, allow to disable --- cmake/OpenCVDetectCXXCompiler.cmake | 15 +++- modules/core/CMakeLists.txt | 17 +++++ .../core/utils/allocator_stats.impl.hpp | 75 +++++++++++++------ 3 files changed, 84 insertions(+), 23 deletions(-) diff --git a/cmake/OpenCVDetectCXXCompiler.cmake b/cmake/OpenCVDetectCXXCompiler.cmake index c27fb4f549..f9fc1189ce 100644 --- a/cmake/OpenCVDetectCXXCompiler.cmake +++ b/cmake/OpenCVDetectCXXCompiler.cmake @@ -212,7 +212,13 @@ if(NOT HAVE_CXX11) endif() endif() -if((HAVE_CXX11 +set(__OPENCV_ENABLE_ATOMIC_LONG_LONG OFF) +if(HAVE_CXX11 AND (X86 OR X86_64)) + set(__OPENCV_ENABLE_ATOMIC_LONG_LONG ON) +endif() +option(OPENCV_ENABLE_ATOMIC_LONG_LONG "Enable C++ compiler support for atomic" ${__OPENCV_ENABLE_ATOMIC_LONG_LONG}) + +if((HAVE_CXX11 AND OPENCV_ENABLE_ATOMIC_LONG_LONG AND NOT MSVC AND NOT (X86 OR X86_64) AND NOT OPENCV_SKIP_LIBATOMIC_COMPILER_CHECK) @@ -223,9 +229,14 @@ if((HAVE_CXX11 list(APPEND CMAKE_REQUIRED_LIBRARIES atomic) ocv_check_compiler_flag(CXX "" HAVE_CXX_ATOMICS_WITH_LIB "${OpenCV_SOURCE_DIR}/cmake/checks/atomic_check.cpp") if(HAVE_CXX_ATOMICS_WITH_LIB) + set(HAVE_ATOMIC_LONG_LONG ON) list(APPEND OPENCV_LINKER_LIBS atomic) else() - message(FATAL_ERROR "C++11 compiler must support std::atomic") + message(STATUS "Compiler doesn't support std::atomic") endif() + else() + set(HAVE_ATOMIC_LONG_LONG ON) endif() +else(HAVE_CXX11 AND OPENCV_ENABLE_ATOMIC_LONG_LONG) + set(HAVE_ATOMIC_LONG_LONG ${OPENCV_ENABLE_ATOMIC_LONG_LONG}) endif() diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 2e0fac6172..4cb61a1d82 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -78,6 +78,23 @@ if(HAVE_MEMALIGN) ocv_append_source_file_compile_definitions(${CMAKE_CURRENT_SOURCE_DIR}/src/alloc.cpp "HAVE_MEMALIGN=1") endif() +option(OPENCV_ENABLE_ALLOCATOR_STATS "Enable Allocator metrics" ON) + +if(NOT OPENCV_ENABLE_ALLOCATOR_STATS) + add_definitions(-DOPENCV_DISABLE_ALLOCATOR_STATS=1) +else() + if(NOT DEFINED OPENCV_ALLOCATOR_STATS_COUNTER_TYPE) + if(HAVE_ATOMIC_LONG_LONG AND OPENCV_ENABLE_ATOMIC_LONG_LONG) + set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "long long") + else() + set(OPENCV_ALLOCATOR_STATS_COUNTER_TYPE "int") + endif() + endif() + message(STATUS "Allocator metrics storage type: '${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}'") + add_definitions("-DOPENCV_ALLOCATOR_STATS_COUNTER_TYPE=${OPENCV_ALLOCATOR_STATS_COUNTER_TYPE}") +endif() + + ocv_create_module(${extra_libs}) ocv_target_link_libraries(${the_module} PRIVATE diff --git a/modules/core/include/opencv2/core/utils/allocator_stats.impl.hpp b/modules/core/include/opencv2/core/utils/allocator_stats.impl.hpp index b77493d010..61fcf15977 100644 --- a/modules/core/include/opencv2/core/utils/allocator_stats.impl.hpp +++ b/modules/core/include/opencv2/core/utils/allocator_stats.impl.hpp @@ -11,32 +11,55 @@ #include #endif +//#define OPENCV_DISABLE_ALLOCATOR_STATS + namespace cv { namespace utils { +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#if defined(__GNUC__) && (\ + (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4) || \ + (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8)) \ + ) +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE int +#endif +#endif + +#ifndef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE +#define OPENCV_ALLOCATOR_STATS_COUNTER_TYPE long long +#endif + #ifdef CV__ALLOCATOR_STATS_LOG namespace { #endif class AllocatorStatistics : public AllocatorStatisticsInterface { -protected: -#ifdef CV_CXX11 - std::atomic curr, total, total_allocs, peak; -#else - volatile long long curr, total, total_allocs, peak; // overflow is possible, CV_XADD operates with 'int' only -#endif +#ifdef OPENCV_DISABLE_ALLOCATOR_STATS public: - AllocatorStatistics() -#ifndef CV_CXX11 - : curr(0), total(0), total_allocs(0), peak(0) -#endif - {} + AllocatorStatistics() {} ~AllocatorStatistics() CV_OVERRIDE {} - // AllocatorStatisticsInterface + uint64_t getCurrentUsage() const CV_OVERRIDE { return 0; } + uint64_t getTotalUsage() const CV_OVERRIDE { return 0; } + uint64_t getNumberOfAllocations() const CV_OVERRIDE { return 0; } + uint64_t getPeakUsage() const CV_OVERRIDE { return 0; } + + /** set peak usage = current usage */ + void resetPeakUsage() CV_OVERRIDE {}; + + void onAllocate(size_t /*sz*/) {} + void onFree(size_t /*sz*/) {} + +#elif defined(CV_CXX11) + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + std::atomic curr, total, total_allocs, peak; +public: + AllocatorStatistics() {} + ~AllocatorStatistics() CV_OVERRIDE {} -#ifdef CV_CXX11 uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr.load(); } uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total.load(); } uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs.load(); } @@ -52,7 +75,7 @@ public: CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); #endif - long long new_curr = curr.fetch_add((long long)sz) + (long long)sz; + counter_t new_curr = curr.fetch_add((counter_t)sz) + (counter_t)sz; // peak = std::max((uint64_t)peak, new_curr); auto prev_peak = peak.load(); @@ -63,7 +86,7 @@ public: } // end of peak = max(...) - total += (long long)sz; + total += (counter_t)sz; total_allocs++; } void onFree(size_t sz) @@ -71,10 +94,20 @@ public: #ifdef CV__ALLOCATOR_STATS_LOG CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr.load())); #endif - curr -= (long long)sz; + curr -= (counter_t)sz; } -#else +#else // non C++11 + +protected: + typedef OPENCV_ALLOCATOR_STATS_COUNTER_TYPE counter_t; + volatile counter_t curr, total, total_allocs, peak; // overflow is possible, CV_XADD operates with 'int' only +public: + AllocatorStatistics() + : curr(0), total(0), total_allocs(0), peak(0) + {} + ~AllocatorStatistics() CV_OVERRIDE {} + uint64_t getCurrentUsage() const CV_OVERRIDE { return (uint64_t)curr; } uint64_t getTotalUsage() const CV_OVERRIDE { return (uint64_t)total; } uint64_t getNumberOfAllocations() const CV_OVERRIDE { return (uint64_t)total_allocs; } @@ -89,21 +122,21 @@ public: CV__ALLOCATOR_STATS_LOG(cv::format("allocate: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); #endif - uint64_t new_curr = (uint64_t)CV_XADD(&curr, (uint64_t)sz) + sz; + counter_t new_curr = (counter_t)CV_XADD(&curr, (counter_t)sz) + (counter_t)sz; - peak = std::max((uint64_t)peak, new_curr); // non-thread safe + peak = std::max((counter_t)peak, new_curr); // non-thread safe //CV_XADD(&total, (uint64_t)sz); // overflow with int, non-reliable... total += sz; - CV_XADD(&total_allocs, (uint64_t)1); + CV_XADD(&total_allocs, (counter_t)1); } void onFree(size_t sz) { #ifdef CV__ALLOCATOR_STATS_LOG CV__ALLOCATOR_STATS_LOG(cv::format("free: %lld (curr=%lld)", (long long int)sz, (long long int)curr)); #endif - CV_XADD(&curr, (uint64_t)-sz); + CV_XADD(&curr, (counter_t)-sz); } #endif }; From 7080c783d1465af9672e494e431276839609fda0 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 12 Mar 2020 14:29:31 +0300 Subject: [PATCH 05/15] cmake: fix missing project() warning in Python standalone builds --- modules/python/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index 616f500c95..a51acf386e 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -41,6 +41,7 @@ add_subdirectory(python3) else() # standalone build cmake_minimum_required(VERSION 2.8.12) +project(OpenCVPython CXX C) include("./standalone.cmake") endif() From 3d36f9044dfff0b53b9414d7912baa4ca73dfd86 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 12 Mar 2020 16:35:14 +0300 Subject: [PATCH 06/15] opencv_version: dump threads information --- apps/version/opencv_version.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/version/opencv_version.cpp b/apps/version/opencv_version.cpp index 8034f698c5..795c870fde 100644 --- a/apps/version/opencv_version.cpp +++ b/apps/version/opencv_version.cpp @@ -14,6 +14,11 @@ #include #endif +// defined in core/private.hpp +namespace cv { +CV_EXPORTS const char* currentParallelFramework(); +} + static void dumpHWFeatures(bool showAll = false) { std::cout << "OpenCV's HW features list:" << std::endl; @@ -34,6 +39,16 @@ static void dumpHWFeatures(bool showAll = false) std::cout << "Total available: " << count << std::endl; } +static void dumpParallelFramework() +{ + const char* parallelFramework = cv::currentParallelFramework(); + if (parallelFramework) + { + int threads = cv::getNumThreads(); + std::cout << "Parallel framework: " << parallelFramework << " (nthreads=" << threads << ")" << std::endl; + } +} + int main(int argc, const char** argv) { CV_TRACE_FUNCTION(); @@ -47,6 +62,7 @@ int main(int argc, const char** argv) "{ verbose v | | show build configuration log }" "{ opencl | | show information about OpenCL (available platforms/devices, default selected device) }" "{ hw | | show detected HW features (see cv::checkHardwareSupport() function). Use --hw=0 to show available features only }" + "{ threads | | show configured parallel framework and number of active threads }" ); if (parser.has("help")) @@ -73,10 +89,17 @@ int main(int argc, const char** argv) { dumpHWFeatures(parser.get("hw")); } + + if (parser.has("threads")) + { + dumpParallelFramework(); + } + #else std::cout << cv::getBuildInformation().c_str() << std::endl; cv::dumpOpenCLInformation(); dumpHWFeatures(); + dumpParallelFramework(); MessageBoxA(NULL, "Check console window output", "OpenCV(" CV_VERSION ")", MB_ICONINFORMATION | MB_OK); #endif From 9b64eadcb6f007f7cc17b228b87a852ca9771342 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 13 Mar 2020 18:33:27 +0300 Subject: [PATCH 07/15] dnn(ie): separate IECore for different devices - HETERO plugin doesn't switch well between devices --- modules/dnn/src/dnn.cpp | 6 ++-- modules/dnn/src/ie_ngraph.cpp | 2 +- modules/dnn/src/op_inf_engine.cpp | 46 ++++++++++++++++++++----------- modules/dnn/src/op_inf_engine.hpp | 2 +- 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 67ca61d9bb..0f8dacad35 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -108,7 +108,7 @@ public: { #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R3) // Lightweight detection - const std::vector devices = getCore().GetAvailableDevices(); + const std::vector devices = getCore("").GetAvailableDevices(); for (std::vector::const_iterator i = devices.begin(); i != devices.end(); ++i) { if (std::string::npos != i->find("MYRIAD") && target == DNN_TARGET_MYRIAD) @@ -3253,7 +3253,7 @@ Net Net::readFromModelOptimizer(const String& xml, const String& bin) InferenceEngine::CNNNetwork ieNet = reader.getNetwork(); #else - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore(""); InferenceEngine::CNNNetwork ieNet = ie.ReadNetwork(xml, bin); #endif @@ -3302,7 +3302,7 @@ Net Net::readFromModelOptimizer( InferenceEngine::CNNNetwork ieNet = reader.getNetwork(); #else - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore(""); std::string model; model.assign((char*)bufferModelConfigPtr, bufferModelConfigSize); diff --git a/modules/dnn/src/ie_ngraph.cpp b/modules/dnn/src/ie_ngraph.cpp index 55ba9377a0..f0975d05a0 100644 --- a/modules/dnn/src/ie_ngraph.cpp +++ b/modules/dnn/src/ie_ngraph.cpp @@ -524,7 +524,7 @@ void InfEngineNgraphNet::initPlugin(InferenceEngine::CNNNetwork& net) try { AutoLock lock(getInitializationMutex()); - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore(device_name); { isInit = true; std::vector candidates; diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index 9c74032a33..047292fda8 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -604,18 +604,31 @@ static bool init_IE_plugins() (void)init_core->GetAvailableDevices(); return true; } -static InferenceEngine::Core& create_IE_Core_instance() +static InferenceEngine::Core& retrieveIECore(const std::string& id, std::map >& cores) { - static InferenceEngine::Core core; - return core; + AutoLock lock(getInitializationMutex()); + std::map >::iterator i = cores.find(id); + if (i == cores.end()) + { + std::shared_ptr core = std::make_shared(); + cores[id] = core; + return *core.get(); + } + return *(i->second).get(); } -static InferenceEngine::Core& create_IE_Core_pointer() +static InferenceEngine::Core& create_IE_Core_instance(const std::string& id) +{ + static std::map > cores; + return retrieveIECore(id, cores); +} +static InferenceEngine::Core& create_IE_Core_pointer(const std::string& id) { // load and hold IE plugins - static InferenceEngine::Core* core = new InferenceEngine::Core(); // 'delete' is never called - return *core; + static std::map >* cores = + new std::map >(); + return retrieveIECore(id, *cores); } -InferenceEngine::Core& getCore() +InferenceEngine::Core& getCore(const std::string& id) { // to make happy memory leak tools use: // - OPENCV_DNN_INFERENCE_ENGINE_HOLD_PLUGINS=0 @@ -631,9 +644,10 @@ InferenceEngine::Core& getCore() false #endif ); - static InferenceEngine::Core& core = param_DNN_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND - ? create_IE_Core_pointer() - : create_IE_Core_instance(); + + InferenceEngine::Core& core = param_DNN_INFERENCE_ENGINE_CORE_LIFETIME_WORKAROUND + ? create_IE_Core_pointer(id) + : create_IE_Core_instance(id); return core; } #endif @@ -641,9 +655,10 @@ InferenceEngine::Core& getCore() #if !defined(OPENCV_DNN_IE_VPU_TYPE_DEFAULT) static bool detectMyriadX_() { + AutoLock lock(getInitializationMutex()); #if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R3) // Lightweight detection - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore("MYRIAD"); const std::vector devices = ie.GetAvailableDevices(); for (std::vector::const_iterator i = devices.begin(); i != devices.end(); ++i) { @@ -687,7 +702,6 @@ static bool detectMyriadX_() #if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2019R1) InferenceEngine::InferenceEnginePluginPtr enginePtr; { - AutoLock lock(getInitializationMutex()); auto& sharedPlugins = getSharedPlugins(); auto pluginIt = sharedPlugins.find("MYRIAD"); if (pluginIt != sharedPlugins.end()) { @@ -706,9 +720,9 @@ static bool detectMyriadX_() try { #if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2019R3) - auto netExec = getCore().LoadNetwork(cnn, "MYRIAD", {{"VPU_PLATFORM", "VPU_2480"}}); + auto netExec = getCore("MYRIAD").LoadNetwork(cnn, "MYRIAD", {{"VPU_PLATFORM", "VPU_2480"}}); #else - auto netExec = getCore().LoadNetwork(cnn, "MYRIAD", {{"VPU_MYRIAD_PLATFORM", "VPU_MYRIAD_2480"}}); + auto netExec = getCore("MYRIAD").LoadNetwork(cnn, "MYRIAD", {{"VPU_MYRIAD_PLATFORM", "VPU_MYRIAD_2480"}}); #endif #endif auto infRequest = netExec.CreateInferRequest(); @@ -739,7 +753,7 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::CNNNetwork& net) } else #else - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore(device_name); #endif { #if INF_ENGINE_VER_MAJOR_LE(INF_ENGINE_RELEASE_2019R1) @@ -1124,7 +1138,7 @@ void resetMyriadDevice() getSharedPlugins().erase("MYRIAD"); #else // Unregister both "MYRIAD" and "HETERO:MYRIAD,CPU" plugins - InferenceEngine::Core& ie = getCore(); + InferenceEngine::Core& ie = getCore("MYRIAD"); try { ie.UnregisterPlugin("MYRIAD"); diff --git a/modules/dnn/src/op_inf_engine.hpp b/modules/dnn/src/op_inf_engine.hpp index 5b37c4dbf0..54bc76941b 100644 --- a/modules/dnn/src/op_inf_engine.hpp +++ b/modules/dnn/src/op_inf_engine.hpp @@ -245,7 +245,7 @@ bool isMyriadX(); CV__DNN_EXPERIMENTAL_NS_END -InferenceEngine::Core& getCore(); +InferenceEngine::Core& getCore(const std::string& id); template static inline std::vector getShape(const Mat& mat) From 2645ee90ca4243fe6663c9b8c7f373c9b77bdf17 Mon Sep 17 00:00:00 2001 From: Liubov Batanina Date: Sat, 14 Mar 2020 14:05:49 +0300 Subject: [PATCH 08/15] Merge pull request #16735 from l-bat:flatten_const_onnx * Supported Flatten for constant nodes * Added default axis * Refactoring * Refactoring * Added cast layer * Fix comments * Add Cast for layers --- modules/dnn/src/onnx/onnx_importer.cpp | 55 ++++++++++++++++++++++++- modules/dnn/test/test_onnx_importer.cpp | 6 +++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 0ca909597e..e08f7b0e11 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -431,9 +431,20 @@ void ONNXImporter::populateNet(Net dstNet) { bool isSub = layer_type == "Sub"; CV_CheckEQ(node_proto.input_size(), 2, ""); - if (layer_id.find(node_proto.input(1)) == layer_id.end()) + bool is_const_0 = layer_id.find(node_proto.input(0)) == layer_id.end(); + bool is_const_1 = layer_id.find(node_proto.input(1)) == layer_id.end(); + if (is_const_0 && is_const_1) { - Mat blob = getBlob(node_proto, constBlobs, 1); + Mat blob_0 = getBlob(node_proto, constBlobs, 0); + Mat blob_1 = getBlob(node_proto, constBlobs, 1); + CV_Assert(blob_0.size == blob_1.size); + Mat output = isSub ? (blob_0 - blob_1) : (blob_0 + blob_1); + constBlobs.insert(std::make_pair(layerParams.name, output)); + continue; + } + else if (is_const_0 || is_const_1) + { + Mat blob = getBlob(node_proto, constBlobs, is_const_0 ? 0 : 1); blob = blob.reshape(1, 1); if (blob.total() == 1) { layerParams.type = "Power"; @@ -808,6 +819,21 @@ void ONNXImporter::populateNet(Net dstNet) layerParams.set("end_axis", axis); layerParams.type = "Flatten"; } + else if (layer_type == "Flatten") + { + CV_CheckEQ(node_proto.input_size(), 1, ""); + if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) + { + Mat input = getBlob(node_proto, constBlobs, 0); + int axis = clamp(layerParams.get("axis", 1), input.dims); + + std::vector out_size(&input.size[0], &input.size[0] + axis); + out_size.push_back(input.total(axis)); + Mat output = input.reshape(1, out_size); + constBlobs.insert(std::make_pair(layerParams.name, output)); + continue; + } + } else if (layer_type == "Unsqueeze") { CV_Assert(node_proto.input_size() == 1); @@ -896,6 +922,31 @@ void ONNXImporter::populateNet(Net dstNet) constBlobs.insert(std::make_pair(layerParams.name, shapeMat)); continue; } + else if (layer_type == "Cast") + { + if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) + { + Mat blob = getBlob(node_proto, constBlobs, 0); + int type; + switch (layerParams.get("to")) + { + case opencv_onnx::TensorProto_DataType_FLOAT: type = CV_32F; break; + case opencv_onnx::TensorProto_DataType_UINT8: type = CV_8U; break; + case opencv_onnx::TensorProto_DataType_UINT16: type = CV_16U; break; + case opencv_onnx::TensorProto_DataType_FLOAT16: type = CV_16S; break; + case opencv_onnx::TensorProto_DataType_INT8: + case opencv_onnx::TensorProto_DataType_INT16: + case opencv_onnx::TensorProto_DataType_INT32: + case opencv_onnx::TensorProto_DataType_INT64: type = CV_32S; break; + default: type = blob.type(); + } + blob.convertTo(blob, type); + constBlobs.insert(std::make_pair(layerParams.name, blob)); + continue; + } + else + layerParams.type = "Identity"; + } else if (layer_type == "Gather") { CV_Assert(node_proto.input_size() == 2); diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index f284eed45b..769862d53d 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -187,6 +187,11 @@ TEST_P(Test_ONNX_layers, MaxPooling_Sigmoid) testONNXModels("maxpooling_sigmoid"); } +TEST_P(Test_ONNX_layers, Cast) +{ + testONNXModels("cast"); +} + TEST_P(Test_ONNX_layers, Concatenation) { if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) @@ -377,6 +382,7 @@ TEST_P(Test_ONNX_layers, DynamicReshape) testONNXModels("dynamic_reshape"); testONNXModels("dynamic_reshape_opset_11"); testONNXModels("flatten_by_prod"); + testONNXModels("flatten_const"); } TEST_P(Test_ONNX_layers, Reshape) From 9ea62bfddbe6648de3d9a8cc1c89583c8ae99bba Mon Sep 17 00:00:00 2001 From: Sayed Adel Date: Sat, 14 Mar 2020 22:14:17 +0200 Subject: [PATCH 09/15] core:vsx reimplement `v_broadcast_element()` There's no need to use `vec_perm()` instead of `vec_splat()`, since instruction `vperm` is quite heavy compared to `vsplt[b,h,w]`. --- .../include/opencv2/core/hal/intrin_vsx.hpp | 77 +------------------ 1 file changed, 3 insertions(+), 74 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp index 6e8b439182..e0f6cbf635 100644 --- a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp @@ -1564,81 +1564,10 @@ OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_uint32x4, vec_uint4) OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_int32x4, vec_int4) OPENCV_HAL_IMPL_VSX_TRANSPOSE4x4(v_float32x4, vec_float4) -template -inline v_int8x16 v_broadcast_element(v_int8x16 v) -{ - return v_int8x16(vec_perm(v.val, v.val, vec_splats((unsigned char)i))); -} +template +inline Tvec v_broadcast_element(const Tvec& v) +{ return Tvec(vec_splat(v.val, i)); } -template -inline v_uint8x16 v_broadcast_element(v_uint8x16 v) -{ - return v_uint8x16(vec_perm(v.val, v.val, vec_splats((unsigned char)i))); -} - -template -inline v_int16x8 v_broadcast_element(v_int16x8 v) -{ - unsigned char t0 = 2*i, t1 = 2*i + 1; - vec_uchar16 p = {t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1}; - return v_int16x8(vec_perm(v.val, v.val, p)); -} - -template -inline v_uint16x8 v_broadcast_element(v_uint16x8 v) -{ - unsigned char t0 = 2*i, t1 = 2*i + 1; - vec_uchar16 p = {t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1, t0, t1}; - return v_uint16x8(vec_perm(v.val, v.val, p)); -} - -template -inline v_int32x4 v_broadcast_element(v_int32x4 v) -{ - unsigned char t0 = 4*i, t1 = 4*i + 1, t2 = 4*i + 2, t3 = 4*i + 3; - vec_uchar16 p = {t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3}; - return v_int32x4(vec_perm(v.val, v.val, p)); -} - -template -inline v_uint32x4 v_broadcast_element(v_uint32x4 v) -{ - unsigned char t0 = 4*i, t1 = 4*i + 1, t2 = 4*i + 2, t3 = 4*i + 3; - vec_uchar16 p = {t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3}; - return v_uint32x4(vec_perm(v.val, v.val, p)); -} - -template -inline v_int64x2 v_broadcast_element(v_int64x2 v) -{ - unsigned char t0 = 8*i, t1 = 8*i + 1, t2 = 8*i + 2, t3 = 8*i + 3, t4 = 8*i + 4, t5 = 8*i + 5, t6 = 8*i + 6, t7 = 8*i + 7; - vec_uchar16 p = {t0, t1, t2, t3, t4, t5, t6, t7, t0, t1, t2, t3, t4, t5, t6, t7}; - return v_int64x2(vec_perm(v.val, v.val, p)); -} - -template -inline v_uint64x2 v_broadcast_element(v_uint64x2 v) -{ - unsigned char t0 = 8*i, t1 = 8*i + 1, t2 = 8*i + 2, t3 = 8*i + 3, t4 = 8*i + 4, t5 = 8*i + 5, t6 = 8*i + 6, t7 = 8*i + 7; - vec_uchar16 p = {t0, t1, t2, t3, t4, t5, t6, t7, t0, t1, t2, t3, t4, t5, t6, t7}; - return v_uint64x2(vec_perm(v.val, v.val, p)); -} - -template -inline v_float32x4 v_broadcast_element(v_float32x4 v) -{ - unsigned char t0 = 4*i, t1 = 4*i + 1, t2 = 4*i + 2, t3 = 4*i + 3; - vec_uchar16 p = {t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3, t0, t1, t2, t3}; - return v_float32x4(vec_perm(v.val, v.val, p)); -} - -template -inline v_float64x2 v_broadcast_element(v_float64x2 v) -{ - unsigned char t0 = 8*i, t1 = 8*i + 1, t2 = 8*i + 2, t3 = 8*i + 3, t4 = 8*i + 4, t5 = 8*i + 5, t6 = 8*i + 6, t7 = 8*i + 7; - vec_uchar16 p = {t0, t1, t2, t3, t4, t5, t6, t7, t0, t1, t2, t3, t4, t5, t6, t7}; - return v_float64x2(vec_perm(v.val, v.val, p)); -} CV_CPU_OPTIMIZATION_HAL_NAMESPACE_END From 7f0d90a525b2da5f54859ae8dcccd0079ca9519f Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 16 Mar 2020 15:49:37 +0300 Subject: [PATCH 10/15] Fix memory leak in Python custom dnn layers --- modules/dnn/misc/python/pyopencv_dnn.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/dnn/misc/python/pyopencv_dnn.hpp b/modules/dnn/misc/python/pyopencv_dnn.hpp index 69c14240c3..d729cd8b97 100644 --- a/modules/dnn/misc/python/pyopencv_dnn.hpp +++ b/modules/dnn/misc/python/pyopencv_dnn.hpp @@ -160,12 +160,13 @@ public: PyObject* args = pyopencv_from(inputs); PyObject* res = PyObject_CallMethodObjArgs(o, PyString_FromString("forward"), args, NULL); Py_DECREF(args); - PyGILState_Release(gstate); if (!res) CV_Error(Error::StsNotImplemented, "Failed to call \"forward\" method"); std::vector pyOutputs; CV_Assert(pyopencv_to(res, pyOutputs, ArgInfo("", 0))); + Py_DECREF(res); + PyGILState_Release(gstate); CV_Assert(pyOutputs.size() == outputs.size()); for (size_t i = 0; i < outputs.size(); ++i) From 1c2ed2876f68188abea6c9805e8d30b6d9972820 Mon Sep 17 00:00:00 2001 From: Dizhenin Vlad <39303687+SimpleVlad@users.noreply.github.com> Date: Mon, 16 Mar 2020 17:10:25 +0300 Subject: [PATCH 11/15] Merge pull request #16695 from SimpleVlad:intelligent_scissors Intelligent scissors * Start * Remove whitespace * Re onMouse * replased double to float * Draw contours * CV_FILLED -> FILLED * Remove line 210 * Change 'about' * Remove M_PI * Remove warning * CP_PI * double to float * CV_PI to Float * Add struct for data * line 172, 191 whitespace * Change name * Fix Warnings * Set const * line 180 * rewrite keys * &img = param->img --- samples/cpp/intelligent_scissors.cpp | 232 +++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 samples/cpp/intelligent_scissors.cpp diff --git a/samples/cpp/intelligent_scissors.cpp b/samples/cpp/intelligent_scissors.cpp new file mode 100644 index 0000000000..6141c1f7b5 --- /dev/null +++ b/samples/cpp/intelligent_scissors.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; +struct Pix +{ + Point next_point; + double cost; + + bool operator > (const Pix &b) const + { + return cost > b.cost; + } +}; + +struct Parameters +{ + Mat img, img_pre_render, img_render; + Point end; + std::vector > contours; + std::vector tmp_contour; + Mat zero_crossing, gradient_magnitude, Ix, Iy, hit_map_x, hit_map_y; +}; + + +static float local_cost(const Point& p, const Point& q, const Mat& gradient_magnitude, const Mat& Iy, const Mat& Ix, const Mat& zero_crossing) +{ + float fG = gradient_magnitude.at(q.y, q.x); + float dp; + float dq; + const float WEIGHT_LAP_ZERO_CROSS = 0.43f; + const float WEIGHT_GRADIENT_MAGNITUDE = 0.14f; + const float WEIGHT_GRADIENT_DIRECTION = 0.43f; + bool isDiag = (p.x != q.x) && (p.y != q.y); + + if ((Iy.at(p) * (q.x - p.x) - Ix.at(p) * (q.y - p.y)) >= 0) + { + dp = Iy.at(p) * (q.x - p.x) - Ix.at(p) * (q.y - p.y); + dq = Iy.at(q) * (q.x - p.x) - Ix.at(q) * (q.y - p.y); + } + else + { + dp = Iy.at(p) * (p.x - q.x) + (-Ix.at(p)) * (p.y - q.y); + dq = Iy.at(q) * (p.x - q.x) + (-Ix.at(q)) * (p.y - q.y); + } + if (isDiag) + { + dp /= sqrtf(2); + dq /= sqrtf(2); + } + else + { + fG /= sqrtf(2); + } + return WEIGHT_LAP_ZERO_CROSS * zero_crossing.at(q) + + WEIGHT_GRADIENT_DIRECTION * (acosf(dp) + acosf(dq)) / static_cast(CV_PI) + + WEIGHT_GRADIENT_MAGNITUDE * fG; +} + +static void find_min_path(const Point& start, Parameters* param) +{ + Pix begin; + Mat &img = param->img; + Mat cost_map(img.size(), CV_32F, Scalar(FLT_MAX)); + Mat expand(img.size(), CV_8UC1, Scalar(0)); + Mat processed(img.size(), CV_8UC1, Scalar(0)); + Mat removed(img.size(), CV_8UC1, Scalar(0)); + std::priority_queue < Pix, std::vector, std::greater > L; + + cost_map.at(start) = 0; + processed.at(start) = 1; + begin.cost = 0; + begin.next_point = start; + + L.push(begin); + while (!L.empty()) + { + Pix P = L.top(); + L.pop(); + Point p = P.next_point; + processed.at(p) = 0; + if (removed.at(p) == 0) + { + expand.at(p) = 1; + for (int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + int tx = p.x + i; + int ty = p.y + j; + if (tx < 0 || tx >= img.cols || ty < 0 || ty >= img.rows) + continue; + if (expand.at(ty, tx) == 0) + { + Point q = Point(tx, ty); + float cost = cost_map.at(p) + local_cost(p, q, param->gradient_magnitude, param->Iy, param->Ix, param->zero_crossing); + if (processed.at(q) == 1 && cost < cost_map.at(q)) + { + removed.at(q) = 1; + } + if (processed.at(q) == 0) + { + cost_map.at(q) = cost; + param->hit_map_x.at(q)= p.x; + param->hit_map_y.at(q) = p.y; + processed.at(q) = 1; + Pix val; + val.cost = cost_map.at(q); + val.next_point = q; + L.push(val); + } + } + } + } + } + } +} + + +static void onMouse(int event, int x, int y, int , void* userdata) +{ + Parameters* param = reinterpret_cast(userdata); + Point &end = param->end; + std::vector > &contours = param->contours; + std::vector &tmp_contour = param->tmp_contour; + Mat &img_render = param->img_render; + Mat &img_pre_render = param->img_pre_render; + + if (event == EVENT_LBUTTONDOWN) + { + end = Point(x, y); + if (!contours.back().empty()) + { + for (int i = static_cast(tmp_contour.size()) - 1; i >= 0; i--) + { + contours.back().push_back(tmp_contour[i]); + } + tmp_contour.clear(); + } + else + { + contours.back().push_back(end); + } + find_min_path(end, param); + + img_render.copyTo(img_pre_render); + imshow("lasso", img_render); + } + else if (event == EVENT_RBUTTONDOWN) + { + img_pre_render.copyTo(img_render); + drawContours(img_pre_render, contours, static_cast(contours.size()) - 1, Scalar(0,255,0), FILLED); + addWeighted(img_pre_render, 0.3, img_render, 0.7, 0, img_render); + contours.resize(contours.size() + 1); + imshow("lasso", img_render); + } + else if (event == EVENT_MOUSEMOVE && !contours.back().empty()) + { + tmp_contour.clear(); + img_pre_render.copyTo(img_render); + Point val_point = Point(x, y); + while (val_point != end) + { + tmp_contour.push_back(val_point); + Point cur = Point(param->hit_map_x.at(val_point), param->hit_map_y.at(val_point)); + line(img_render, val_point, cur, Scalar(255, 0, 0), 2); + val_point = cur; + } + imshow("lasso", img_render); + } +} + +const char* keys = +{ + "{help h | |}" + "{@image | fruits.jpg| Path to image to process}" +}; + + +int main( int argc, const char** argv ) +{ + Parameters param; + const int EDGE_THRESHOLD_LOW = 50; + const int EDGE_THRESHOLD_HIGH = 100; + CommandLineParser parser(argc, argv, keys); + parser.about("\nThis program demonstrates implementation of 'intelligent scissors' algorithm\n" + "To start drawing a new contour select a pixel, click LEFT mouse button.\n" + "To fix a path click LEFT mouse button again.\n" + "To finish drawing a contour click RIGHT mouse button.\n"); + if (parser.has("help")) + { + parser.printMessage(); + return 1; + } + std::vector > c(1); + param.contours = c; + std::string filename = parser.get(0); + + Mat grayscale, img_canny; + param.img = imread(samples::findFile(filename)); + param.hit_map_x.create(param.img.rows, param.img.cols, CV_32SC1); + param.hit_map_y.create(param.img.rows, param.img.cols, CV_32SC1); + + cvtColor(param.img, grayscale, COLOR_BGR2GRAY); + Canny(grayscale, img_canny, EDGE_THRESHOLD_LOW, EDGE_THRESHOLD_HIGH); + + threshold(img_canny, param.zero_crossing, 254, 1, THRESH_BINARY_INV); + Sobel(grayscale, param.Ix, CV_32FC1, 1, 0, 1); + Sobel(grayscale, param.Iy, CV_32FC1, 0, 1, 1); + param.Ix.convertTo(param.Ix, CV_32F, 1.0/255); + param.Iy.convertTo(param.Iy, CV_32F, 1.0/255); + + // Compute gradients magnitude. + double max_val = 0.0; + magnitude(param.Iy, param.Ix, param.gradient_magnitude); + minMaxLoc(param.gradient_magnitude, 0, &max_val); + param.gradient_magnitude.convertTo(param.gradient_magnitude, CV_32F, -1/max_val, 1.0); + + param.img.copyTo(param.img_pre_render); + param.img.copyTo(param.img_render); + + namedWindow("lasso"); + setMouseCallback("lasso", onMouse, ¶m); + imshow("lasso", param.img); + waitKey(0); +} From 3b2e409fa7f4d6319242da85b434d9b0ff8d996f Mon Sep 17 00:00:00 2001 From: RAJKIRAN NATARAJAN Date: Mon, 16 Mar 2020 07:12:29 -0700 Subject: [PATCH 12/15] Merge pull request #16779 from saskatchewancatch:issue-16777 * Fixes issue 16777. * core: update Concurrency getNumThreads() --- modules/core/src/parallel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core/src/parallel.cpp b/modules/core/src/parallel.cpp index 1b53cfa0e3..3d02d748b9 100644 --- a/modules/core/src/parallel.cpp +++ b/modules/core/src/parallel.cpp @@ -618,9 +618,9 @@ int getNumThreads(void) #elif defined HAVE_CONCURRENCY - return 1 + (pplScheduler == 0 + return (pplScheduler == 0) ? Concurrency::CurrentScheduler::Get()->GetNumberOfVirtualProcessors() - : pplScheduler->GetNumberOfVirtualProcessors()); + : (1 + pplScheduler->GetNumberOfVirtualProcessors()); #elif defined HAVE_PTHREADS_PF From d4d95bd70d72fbf7c96d223eff837818a2f30673 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Mon, 16 Mar 2020 15:49:51 +0300 Subject: [PATCH 13/15] Fixs several problems found by static analysis --- modules/features2d/src/kaze/AKAZEFeatures.cpp | 1 + modules/objdetect/src/qrcode.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/modules/features2d/src/kaze/AKAZEFeatures.cpp b/modules/features2d/src/kaze/AKAZEFeatures.cpp index 9b0d8311b0..89cf66bc24 100644 --- a/modules/features2d/src/kaze/AKAZEFeatures.cpp +++ b/modules/features2d/src/kaze/AKAZEFeatures.cpp @@ -1323,6 +1323,7 @@ void quantized_counting_sort(const float a[], const int n, const float quantum, const int nkeys, int idx[/*n*/], int cum[/*nkeys + 1*/]) { + CV_Assert(nkeys > 0); memset(cum, 0, sizeof(cum[0]) * (nkeys + 1)); // Count up the quantized values diff --git a/modules/objdetect/src/qrcode.cpp b/modules/objdetect/src/qrcode.cpp index f99a46c922..4990dfd388 100644 --- a/modules/objdetect/src/qrcode.cpp +++ b/modules/objdetect/src/qrcode.cpp @@ -1608,6 +1608,8 @@ bool QRDetectMulti::checkPoints(const vector& quadrangle_points) li2++; } } + if (count_w == 0) + return false; double frac = double(count_b) / double(count_w); double bottom_bound = 0.76; From 05d49756195680e41e3b32f80a9dc4c2f8d0dfcc Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Mon, 16 Mar 2020 18:49:16 +0300 Subject: [PATCH 14/15] objdetect: fix QRCode tests with disabled QUIRC --- modules/objdetect/test/test_qrcode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/objdetect/test/test_qrcode.cpp b/modules/objdetect/test/test_qrcode.cpp index cda5a02311..79a060db0e 100644 --- a/modules/objdetect/test/test_qrcode.cpp +++ b/modules/objdetect/test/test_qrcode.cpp @@ -284,7 +284,7 @@ TEST_P(Objdetect_QRCode_Close, regression) ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); #else - ASSERT_TRUE(qrcode.detect(src, corners)); + ASSERT_TRUE(qrcode.detect(barcode, corners)); #endif const std::string dataset_config = findDataFile(root + "dataset_config.json"); @@ -349,7 +349,7 @@ TEST_P(Objdetect_QRCode_Monitor, regression) ASSERT_FALSE(corners.empty()); ASSERT_FALSE(decoded_info.empty()); #else - ASSERT_TRUE(qrcode.detect(src, corners)); + ASSERT_TRUE(qrcode.detect(barcode, corners)); #endif const std::string dataset_config = findDataFile(root + "dataset_config.json"); From 77d1c20fb7855c9ca74d5fb5d99001abaa09da46 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 16 Mar 2020 19:34:08 +0300 Subject: [PATCH 15/15] core(buffer_area): handle 'OPENCV_ENABLE_MEMORY_SANITIZER=ON' case --- .../core/utils/buffer_area.private.hpp | 6 ++++ modules/core/src/buffer_area.cpp | 36 +++++++++++++------ 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/modules/core/include/opencv2/core/utils/buffer_area.private.hpp b/modules/core/include/opencv2/core/utils/buffer_area.private.hpp index ab19da6416..f6bb93e088 100644 --- a/modules/core/include/opencv2/core/utils/buffer_area.private.hpp +++ b/modules/core/include/opencv2/core/utils/buffer_area.private.hpp @@ -72,6 +72,10 @@ public: CV_Assert(alignment % sizeof(T) == 0); CV_Assert((alignment & (alignment - 1)) == 0); allocate_((void**)(&ptr), static_cast(sizeof(T)), count, alignment); +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER + if (safe) +#endif + CV_Assert(ptr != NULL); } /** @brief Fill one of buffers with zeroes @@ -118,9 +122,11 @@ private: private: class Block; std::vector blocks; +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER void * oneBuf; size_t totalSize; const bool safe; +#endif }; //! @} diff --git a/modules/core/src/buffer_area.cpp b/modules/core/src/buffer_area.cpp index b6bb321bba..2fe9d782ae 100644 --- a/modules/core/src/buffer_area.cpp +++ b/modules/core/src/buffer_area.cpp @@ -5,14 +5,10 @@ #include "opencv2/core/utils/buffer_area.private.hpp" #include "opencv2/core/utils/configuration.private.hpp" -#ifdef OPENCV_ENABLE_MEMORY_SANITIZER -#define BUFFER_AREA_DEFAULT_MODE true -#else -#define BUFFER_AREA_DEFAULT_MODE false -#endif - +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER static bool CV_BUFFER_AREA_OVERRIDE_SAFE_MODE = - cv::utils::getConfigurationParameterBool("OPENCV_BUFFER_AREA_ALWAYS_SAFE", BUFFER_AREA_DEFAULT_MODE); + cv::utils::getConfigurationParameterBool("OPENCV_BUFFER_AREA_ALWAYS_SAFE", false); +#endif namespace cv { namespace utils { @@ -58,6 +54,7 @@ public: *ptr = raw_mem; } } +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER void * fast_allocate(void * buf) const { CV_Assert(ptr && *ptr == NULL); @@ -66,6 +63,7 @@ public: *ptr = buf; return static_cast(static_cast(*ptr) + type_size * count); } +#endif bool operator==(void **other) const { CV_Assert(ptr && other); @@ -86,12 +84,20 @@ private: //================================================================================================== +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER BufferArea::BufferArea(bool safe_) : oneBuf(0), totalSize(0), safe(safe_ || CV_BUFFER_AREA_OVERRIDE_SAFE_MODE) { + // nothing } +#else +BufferArea::BufferArea(bool safe_) +{ + CV_UNUSED(safe_); +} +#endif BufferArea::~BufferArea() { @@ -101,10 +107,16 @@ BufferArea::~BufferArea() void BufferArea::allocate_(void **ptr, ushort type_size, size_t count, ushort alignment) { blocks.push_back(Block(ptr, type_size, count, alignment)); - if (safe) - blocks.back().real_allocate(); - else +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER + if (!safe) + { totalSize += blocks.back().getByteCount(); + } + else +#endif + { + blocks.back().real_allocate(); + } } void BufferArea::zeroFill_(void **ptr) @@ -129,6 +141,7 @@ void BufferArea::zeroFill() void BufferArea::commit() { +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER if (!safe) { CV_Assert(totalSize > 0); @@ -141,6 +154,7 @@ void BufferArea::commit() ptr = i->fast_allocate(ptr); } } +#endif } void BufferArea::release() @@ -150,11 +164,13 @@ void BufferArea::release() i->cleanup(); } blocks.clear(); +#ifndef OPENCV_ENABLE_MEMORY_SANITIZER if (oneBuf) { fastFree(oneBuf); oneBuf = 0; } +#endif } //==================================================================================================