From 134094a442f2944ccb3605e2f836f3747295acbc Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Thu, 5 Dec 2019 19:25:51 +0300 Subject: [PATCH 01/26] Backport fix for autodetection of input shapes --- modules/dnn/src/dnn.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 83b6b9f470..cd51282626 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -2772,17 +2772,25 @@ struct Net::Impl { std::vector& inputLayerIds = layers[id].inputBlobsId; - if (inOutShapes[0].in[0].empty() && !layers[0].outputBlobs.empty()) + if (id == 0 && inOutShapes[id].in[0].empty()) { - ShapesVec shapes; - for (int i = 0; i < layers[0].outputBlobs.size(); i++) + if (!layers[0].outputBlobs.empty()) { - Mat& inp = layers[0].outputBlobs[i]; - CV_Assert(inp.total()); - shapes.push_back(shape(inp)); + ShapesVec shapes; + for (int i = 0; i < layers[0].outputBlobs.size(); i++) + { + Mat& inp = layers[0].outputBlobs[i]; + CV_Assert(inp.total()); + shapes.push_back(shape(inp)); + } + inOutShapes[0].in = shapes; } - inOutShapes[0].in = shapes; - } + else + { + inOutShapes[0].out.clear(); + return; + } + } if (inOutShapes[id].in.empty()) { From 76a27e3399c1d3ab43a7812deba03b7c0694c540 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 5 Dec 2019 18:28:38 +0000 Subject: [PATCH 02/26] pre: OpenCV 3.4.9 (version++) --- doc/tutorials/dnn/dnn_android/dnn_android.markdown | 2 +- .../cross_referencing/tutorial_cross_referencing.markdown | 4 ++-- modules/core/include/opencv2/core/version.hpp | 4 ++-- modules/python/package/setup.py | 2 +- platforms/android/build_sdk.py | 2 +- platforms/android/service/engine/AndroidManifest.xml | 4 ++-- .../engine/src/org/opencv/engine/OpenCVEngineService.java | 2 +- platforms/android/service/readme.txt | 2 +- platforms/maven/opencv-it/pom.xml | 2 +- platforms/maven/opencv/pom.xml | 2 +- platforms/maven/pom.xml | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/tutorials/dnn/dnn_android/dnn_android.markdown b/doc/tutorials/dnn/dnn_android/dnn_android.markdown index 30022d4cc9..a88a72f2e4 100644 --- a/doc/tutorials/dnn/dnn_android/dnn_android.markdown +++ b/doc/tutorials/dnn/dnn_android/dnn_android.markdown @@ -12,7 +12,7 @@ Tutorial was written for the following versions of corresponding software: - Download and install Android Studio from https://developer.android.com/studio. -- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it (for example, `opencv-3.4.8-android-sdk.zip`). +- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it (for example, `opencv-3.4.9-android-sdk.zip`). - Download MobileNet object detection model from https://github.com/chuanqi305/MobileNet-SSD. We need a configuration file `MobileNetSSD_deploy.prototxt` and weights `MobileNetSSD_deploy.caffemodel`. diff --git a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown index 0e282c6960..0c164b03b9 100644 --- a/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown +++ b/doc/tutorials/introduction/cross_referencing/tutorial_cross_referencing.markdown @@ -36,14 +36,14 @@ Open your Doxyfile using your favorite text editor and search for the key `TAGFILES`. Change it as follows: @code -TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.8 +TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.9 @endcode If you had other definitions already, you can append the line using a `\`: @code TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \ - ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.8 + ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/3.4.9 @endcode Doxygen can now use the information from the tag file to link to the OpenCV diff --git a/modules/core/include/opencv2/core/version.hpp b/modules/core/include/opencv2/core/version.hpp index f5350662dc..2a41194a47 100644 --- a/modules/core/include/opencv2/core/version.hpp +++ b/modules/core/include/opencv2/core/version.hpp @@ -7,8 +7,8 @@ #define CV_VERSION_MAJOR 3 #define CV_VERSION_MINOR 4 -#define CV_VERSION_REVISION 8 -#define CV_VERSION_STATUS "-dev" +#define CV_VERSION_REVISION 9 +#define CV_VERSION_STATUS "-pre" #define CVAUX_STR_EXP(__A) #__A #define CVAUX_STR(__A) CVAUX_STR_EXP(__A) diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index cdf1093a6f..33a3208225 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -9,7 +9,7 @@ def main(): os.chdir(SCRIPT_DIR) package_name = 'opencv' - package_version = os.environ.get('OPENCV_VERSION', '3.4.4') # TODO + package_version = os.environ.get('OPENCV_VERSION', '3.4.9') # TODO long_description = 'Open Source Computer Vision Library Python bindings' # TODO diff --git a/platforms/android/build_sdk.py b/platforms/android/build_sdk.py index 17bdd93f80..f9b4c1a8f3 100755 --- a/platforms/android/build_sdk.py +++ b/platforms/android/build_sdk.py @@ -264,7 +264,7 @@ class Builder: # Add extra data apkxmldest = check_dir(os.path.join(apkdest, "res", "xml"), create=True) apklibdest = check_dir(os.path.join(apkdest, "libs", abi.name), create=True) - for ver, d in self.extra_packs + [("3.4.8", os.path.join(self.libdest, "lib"))]: + for ver, d in self.extra_packs + [("3.4.9", os.path.join(self.libdest, "lib"))]: r = ET.Element("library", attrib={"version": ver}) log.info("Adding libraries from %s", d) diff --git a/platforms/android/service/engine/AndroidManifest.xml b/platforms/android/service/engine/AndroidManifest.xml index 651f487fa9..d3792f2a08 100644 --- a/platforms/android/service/engine/AndroidManifest.xml +++ b/platforms/android/service/engine/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="349@ANDROID_PLATFORM_ID@" + android:versionName="3.49"> diff --git a/platforms/android/service/engine/src/org/opencv/engine/OpenCVEngineService.java b/platforms/android/service/engine/src/org/opencv/engine/OpenCVEngineService.java index c069448103..3527934b23 100644 --- a/platforms/android/service/engine/src/org/opencv/engine/OpenCVEngineService.java +++ b/platforms/android/service/engine/src/org/opencv/engine/OpenCVEngineService.java @@ -137,7 +137,7 @@ public class OpenCVEngineService extends Service { @Override public int getEngineVersion() throws RemoteException { - int version = 3480; + int version = 3490; try { version = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; } catch (NameNotFoundException e) { diff --git a/platforms/android/service/readme.txt b/platforms/android/service/readme.txt index 6453248cad..7720ab37db 100644 --- a/platforms/android/service/readme.txt +++ b/platforms/android/service/readme.txt @@ -12,7 +12,7 @@ manually using adb tool: adb install /apk/OpenCV__Manager__.apk -Example: OpenCV_3.4.8-dev_Manager_3.48_armeabi-v7a.apk +Example: OpenCV_3.4.9-dev_Manager_3.49_armeabi-v7a.apk Use the list of platforms below to determine proper OpenCV Manager package for your device: diff --git a/platforms/maven/opencv-it/pom.xml b/platforms/maven/opencv-it/pom.xml index cfee2ff3a6..84109391f5 100644 --- a/platforms/maven/opencv-it/pom.xml +++ b/platforms/maven/opencv-it/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 3.4.8 + 3.4.9 org.opencv opencv-it diff --git a/platforms/maven/opencv/pom.xml b/platforms/maven/opencv/pom.xml index 7dd4e40492..f0a4103bcf 100644 --- a/platforms/maven/opencv/pom.xml +++ b/platforms/maven/opencv/pom.xml @@ -4,7 +4,7 @@ org.opencv opencv-parent - 3.4.8 + 3.4.9 org.opencv opencv diff --git a/platforms/maven/pom.xml b/platforms/maven/pom.xml index 909f7e143b..4f02f32fbd 100644 --- a/platforms/maven/pom.xml +++ b/platforms/maven/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.opencv opencv-parent - 3.4.8 + 3.4.9 pom OpenCV Parent POM From d99d18304a21282d09ebf5d327047024169b8c49 Mon Sep 17 00:00:00 2001 From: Liubov Batanina Date: Fri, 6 Dec 2019 09:56:21 +0300 Subject: [PATCH 03/26] Slice v1 op --- modules/dnn/src/layers/prior_box_layer.cpp | 6 ++++-- modules/dnn/src/layers/slice_layer.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index fd37004da3..c38e61329b 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -569,8 +569,10 @@ public: auto upper_bounds = std::make_shared(ngraph::element::i64, ngraph::Shape{1}, std::vector{4}); auto strides = std::make_shared(ngraph::element::i64, ngraph::Shape{1}, std::vector{1}); - auto slice_layer = std::make_shared(layer_shape, lower_bounds, upper_bounds, strides); - auto slice_image = std::make_shared(image_shape, lower_bounds, upper_bounds, strides); + auto slice_layer = std::make_shared(layer_shape, + lower_bounds, upper_bounds, strides, std::vector{}, std::vector{}); + auto slice_image = std::make_shared(image_shape, + lower_bounds, upper_bounds, strides, std::vector{}, std::vector{}); if (_explicitSizes) { diff --git a/modules/dnn/src/layers/slice_layer.cpp b/modules/dnn/src/layers/slice_layer.cpp index ea2562380f..d7cafd7f93 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -341,8 +341,9 @@ public: auto strides = std::make_shared(ngraph::element::i64, ngraph::Shape{dims.size()}, std::vector((int64_t)dims.size(), 1)); - auto slice = std::make_shared(ieInpNode, lower_bounds, upper_bounds, - strides, ngraph::AxisSet{}, ngraph::AxisSet{}); + auto slice = std::make_shared(ieInpNode, + lower_bounds, upper_bounds, strides, std::vector{}, std::vector{}); + return Ptr(new InfEngineNgraphNode(slice)); } #endif // HAVE_DNN_NGRAPH From 660a7098402f745ccd47e355dbfcbd40c1c906b9 Mon Sep 17 00:00:00 2001 From: Liubov Batanina Date: Fri, 6 Dec 2019 11:27:59 +0300 Subject: [PATCH 04/26] Support Swish and Mish activations --- modules/dnn/src/layers/elementwise_layers.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 8a0ddcdd75..3459734a08 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -579,7 +579,7 @@ struct SwishFunctor bool supportBackend(int backendId, int) { return backendId == DNN_BACKEND_OPENCV || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH;; } void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const @@ -640,7 +640,8 @@ struct SwishFunctor #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { - CV_Error(Error::StsNotImplemented, ""); + auto sigmoid = std::make_shared(node); + return std::make_shared(node, sigmoid); } #endif // HAVE_DNN_NGRAPH @@ -659,7 +660,7 @@ struct MishFunctor bool supportBackend(int backendId, int) { return backendId == DNN_BACKEND_OPENCV || - backendId == DNN_BACKEND_HALIDE; + backendId == DNN_BACKEND_HALIDE || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; } void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const @@ -720,7 +721,13 @@ struct MishFunctor #ifdef HAVE_DNN_NGRAPH std::shared_ptr initNgraphAPI(const std::shared_ptr& node) { - CV_Error(Error::StsNotImplemented, ""); + float one = 1.0f; + auto constant = std::make_shared(ngraph::element::f32, ngraph::Shape{1}, &one); + auto exp_node = std::make_shared(node); + auto sum = std::make_shared(constant, exp_node, ngraph::op::AutoBroadcastType::NUMPY); + auto log_node = std::make_shared(sum); + auto tanh_node = std::make_shared(log_node); + return std::make_shared(node, tanh_node); } #endif // HAVE_DNN_NGRAPH From b369c456f2dc2cc4ca9addbd182cafa7cdd6a70f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 6 Dec 2019 13:25:51 +0300 Subject: [PATCH 05/26] imgproc(color): clarify error message --- modules/imgproc/src/color.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/imgproc/src/color.hpp b/modules/imgproc/src/color.hpp index 8c1f19fa8a..e4a5052892 100644 --- a/modules/imgproc/src/color.hpp +++ b/modules/imgproc/src/color.hpp @@ -224,7 +224,10 @@ struct OclHelper int scn = src.channels(); int depth = src.depth(); - CV_Assert( VScn::contains(scn) && VDcn::contains(dcn) && VDepth::contains(depth) ); + CV_Check(scn, VScn::contains(scn), "Invalid number of channels in input image"); + CV_Check(dcn, VDcn::contains(dcn), "Invalid number of channels in output image"); + CV_CheckDepth(depth, VDepth::contains(depth), "Unsupported depth of input image"); + switch (sizePolicy) { case TO_YUV: From beb5b291b96960c3cdab389d31bc76d50cd94fd8 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Fri, 6 Dec 2019 19:34:54 +0300 Subject: [PATCH 06/26] Fix HETERO:FPGA,CPU plugin for IE backend --- modules/dnn/src/op_inf_engine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index 0a35194995..c6f741f3c9 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -750,11 +750,16 @@ void InfEngineBackendNet::initPlugin(InferenceEngine::CNNNetwork& net) { if (layer->type == kOpenCVLayersType) { - layer->affinity = "CPU"; isHetero = true; +#if INF_ENGINE_VER_MAJOR_LT(INF_ENGINE_RELEASE_2019R3) + // Not sure about lower versions but in 2019R3 we do not need this + layer->affinity = "CPU"; } else + { layer->affinity = device_name; +#endif + } } } if (isHetero) From 629d47fcd85f7a6b63d0d519b08acfcfb653a0ec Mon Sep 17 00:00:00 2001 From: Lubov Batanina Date: Fri, 6 Dec 2019 21:29:57 +0300 Subject: [PATCH 07/26] Merge pull request #15988 from l-bat:custom_layer Test create custom layer in python * check is contiguos * Add custom layer test * Fix test * Remove assert * Move assert to pyopencv dnn * remove assert * Add unregister * Fix python2 * proto to bytearray * Fix data type --- modules/dnn/misc/python/test/test_dnn.py | 68 ++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/modules/dnn/misc/python/test/test_dnn.py b/modules/dnn/misc/python/test/test_dnn.py index 8346297c39..24a23d6ace 100644 --- a/modules/dnn/misc/python/test/test_dnn.py +++ b/modules/dnn/misc/python/test/test_dnn.py @@ -226,6 +226,74 @@ class dnn_test(NewOpenCVTests): self.assertTrue(ret) normAssert(self, refs[i], result, 'Index: %d' % i, 1e-10) + def test_custom_layer(self): + class CropLayer(object): + def __init__(self, params, blobs): + self.xstart = 0 + self.xend = 0 + self.ystart = 0 + self.yend = 0 + # Our layer receives two inputs. We need to crop the first input blob + # to match a shape of the second one (keeping batch size and number of channels) + def getMemoryShapes(self, inputs): + inputShape, targetShape = inputs[0], inputs[1] + batchSize, numChannels = inputShape[0], inputShape[1] + height, width = targetShape[2], targetShape[3] + self.ystart = (inputShape[2] - targetShape[2]) // 2 + self.xstart = (inputShape[3] - targetShape[3]) // 2 + self.yend = self.ystart + height + self.xend = self.xstart + width + return [[batchSize, numChannels, height, width]] + def forward(self, inputs): + return [inputs[0][:,:,self.ystart:self.yend,self.xstart:self.xend]] + + cv.dnn_registerLayer('CropCaffe', CropLayer) + proto = ''' + name: "TestCrop" + input: "input" + input_shape + { + dim: 1 + dim: 2 + dim: 5 + dim: 5 + } + input: "roi" + input_shape + { + dim: 1 + dim: 2 + dim: 3 + dim: 3 + } + layer { + name: "Crop" + type: "CropCaffe" + bottom: "input" + bottom: "roi" + top: "Crop" + }''' + + net = cv.dnn.readNetFromCaffe(bytearray(proto.encode())) + for backend, target in self.dnnBackendsAndTargets: + if backend != cv.dnn.DNN_BACKEND_OPENCV: + continue + + printParams(backend, target) + + net.setPreferableBackend(backend) + net.setPreferableTarget(target) + src_shape = [1, 2, 5, 5] + dst_shape = [1, 2, 3, 3] + inp = np.arange(0, np.prod(src_shape), dtype=np.float32).reshape(src_shape) + roi = np.empty(dst_shape, dtype=np.float32) + net.setInput(inp, "input") + net.setInput(roi, "roi") + out = net.forward() + ref = inp[:, :, 1:4, 1:4] + normAssert(self, out, ref) + + cv.dnn_unregisterLayer('CropCaffe') if __name__ == '__main__': NewOpenCVTests.bootstrap() From 816f82682b05205b86fe06659088eaa6d4b5cbea Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sat, 7 Dec 2019 21:41:15 +0000 Subject: [PATCH 08/26] core(trace/itt): avoid calling __itt_thread_set_name() by default - don't override current application thread names - set name for own threads only --- modules/core/src/parallel_impl.cpp | 5 +++++ modules/core/src/system.cpp | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index adf2bd3948..90424a6990 100644 --- a/modules/core/src/parallel_impl.cpp +++ b/modules/core/src/parallel_impl.cpp @@ -16,6 +16,8 @@ //#define CV_LOG_STRIP_LEVEL CV_LOG_LEVEL_VERBOSE + 1 #include +#include + //#define CV_PROFILE_THREADS 64 //#define getTickCount getCPUTickCount // use this if getTickCount() calls are expensive (and getCPUTickCount() is accurate) @@ -276,6 +278,9 @@ public: void thread_body(); static void* thread_loop_wrapper(void* thread_object) { +#ifdef OPENCV_WITH_ITT + __itt_thread_set_name(cv::format("OpenCVThread-%03d", cv::utils::getThreadID()).c_str()); +#endif ((WorkerThread*)thread_object)->thread_body(); return 0; } diff --git a/modules/core/src/system.cpp b/modules/core/src/system.cpp index 699fecccfc..2ed05f12ee 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -1888,6 +1888,15 @@ BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID lpReserved) namespace { + +#ifdef OPENCV_WITH_ITT +bool overrideThreadName() +{ + static bool param = utils::getConfigurationParameterBool("OPENCV_TRACE_ITT_SET_THREAD_NAME", false); + return param; +} +#endif + static int g_threadNum = 0; class ThreadID { public: @@ -1896,7 +1905,8 @@ public: id(CV_XADD(&g_threadNum, 1)) { #ifdef OPENCV_WITH_ITT - __itt_thread_set_name(cv::format("OpenCVThread-%03d", id).c_str()); + if (overrideThreadName()) + __itt_thread_set_name(cv::format("OpenCVThread-%03d", id).c_str()); #endif } }; From b1b505f7833eb1a3317613c91f1d8dc5267f7668 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Sun, 8 Dec 2019 22:11:55 +0000 Subject: [PATCH 09/26] dnn: clarify error message from getMemoryShapes() --- modules/dnn/src/dnn.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index cd51282626..eb87d2c348 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -2812,8 +2812,29 @@ struct Net::Impl ShapesVec& os = inOutShapes[id].out; ShapesVec& ints = inOutShapes[id].internal; int requiredOutputs = layers[id].requiredOutputs.size(); - inOutShapes[id].supportInPlace = - layers[id].getLayerInstance()->getMemoryShapes(is, requiredOutputs, os, ints); + Ptr l = layers[id].getLayerInstance(); + CV_Assert(l); + bool layerSupportInPlace = false; + try + { + layerSupportInPlace = l->getMemoryShapes(is, requiredOutputs, os, ints); + } + catch (const cv::Exception& e) + { + CV_LOG_ERROR(NULL, "OPENCV/DNN: [" << l->type << "]:(" << l->name << "): getMemoryShapes() throws exception." << + " inputs=" << is.size() << " outputs=" << os.size() << "/" << requiredOutputs); + for (size_t i = 0; i < is.size(); ++i) + { + CV_LOG_ERROR(NULL, " input[" << i << "] = " << toString(is[i])); + } + for (size_t i = 0; i < os.size(); ++i) + { + CV_LOG_ERROR(NULL, " output[" << i << "] = " << toString(os[i])); + } + CV_LOG_ERROR(NULL, "Exception message: " << e.what()); + throw; + } + inOutShapes[id].supportInPlace = layerSupportInPlace; for (int i = 0; i < ints.size(); i++) CV_Assert(total(ints[i]) > 0); From 883c4c60c366f778f41773a3d00c0a71a9ceea1b Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 9 Dec 2019 12:49:47 +0300 Subject: [PATCH 10/26] Remove Dummy layer --- .../dnn/src/layers/detection_output_layer.cpp | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index d629a71384..d391e01e4e 100644 --- a/modules/dnn/src/layers/detection_output_layer.cpp +++ b/modules/dnn/src/layers/detection_output_layer.cpp @@ -55,36 +55,6 @@ #ifdef HAVE_DNN_NGRAPH #include "../ie_ngraph.hpp" #include - -namespace ngraph { -namespace op { - -class Dummy : public Op { -public: - Dummy() : Op("Dummy", {}) { - constructor_validate_and_infer_types(); - } - - void validate_and_infer_types() override { - set_output_type(0, ngraph::element::Type(), {}); - } - - std::shared_ptr copy_with_new_args(const NodeVector& new_args) const override { - if (!new_args.empty()) - throw ngraph_error("Incorrect number of new arguments"); - return std::make_shared(); - } - - static constexpr NodeTypeInfo type_info{"Dummy", 1}; - const NodeTypeInfo& get_type_info() const override { - return type_info; - } -}; - -constexpr NodeTypeInfo Dummy::type_info; - -} // namespace op -} // namespace ngraph #endif namespace cv @@ -1000,10 +970,8 @@ public: attrs.code_type = std::string{"caffe.PriorBoxParameter." + _codeType}; attrs.normalized = true; - auto aux_class_preds = std::make_shared(); - auto aux_box_preds = std::make_shared(); auto det_out = std::make_shared(box_logits, class_preds, - proposals, aux_class_preds, aux_box_preds, attrs); + proposals, attrs); return Ptr(new InfEngineNgraphNode(det_out)); } #endif // HAVE_DNN_NGRAPH From 734de34b7a9d42d79efff3dd186115f74eec3f9b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 9 Dec 2019 14:51:02 +0300 Subject: [PATCH 11/26] Merge pull request #16085 from alalek:imgproc_threshold_to_zero_ipp_bug * imgproc(IPP): wrong result from threshold(THRESH_TOZERO) * imgproc(IPP): disable IPP code to pass THRESH_TOZERO test --- modules/imgproc/src/thresh.cpp | 2 ++ modules/imgproc/test/test_thresh.cpp | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 466b0a8b6f..2e6690e55b 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -774,6 +774,7 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) } setIppErrorStatus(); break; +#if 0 // details: https://github.com/opencv/opencv/pull/16085 case THRESH_TOZERO: if (0 <= CV_INSTRUMENT_FUN_IPP(ippiThreshold_LTVal_32f_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh + FLT_EPSILON, 0)) { @@ -782,6 +783,7 @@ thresh_32f( const Mat& _src, Mat& _dst, float thresh, float maxval, int type ) } setIppErrorStatus(); break; +#endif case THRESH_TOZERO_INV: if (0 <= CV_INSTRUMENT_FUN_IPP(ippiThreshold_GTVal_32f_C1R, src, (int)src_step*sizeof(src[0]), dst, (int)dst_step*sizeof(dst[0]), sz, thresh, 0)) { diff --git a/modules/imgproc/test/test_thresh.cpp b/modules/imgproc/test/test_thresh.cpp index e9bed8c72e..a61095d5cc 100644 --- a/modules/imgproc/test/test_thresh.cpp +++ b/modules/imgproc/test/test_thresh.cpp @@ -434,4 +434,13 @@ BIGDATA_TEST(Imgproc_Threshold, huge) ASSERT_EQ((uint64)nz, n / 2); } +TEST(Imgproc_Threshold, regression_THRESH_TOZERO_IPP_16085) +{ + Size sz(16, 16); + Mat input(sz, CV_32F, Scalar::all(2)); + Mat result; + cv::threshold(input, result, 2.0, 0.0, THRESH_TOZERO); + EXPECT_EQ(0, cv::norm(result, NORM_INF)); +} + }} // namespace From a011035ed6c823b555df82662cc0eadd12d68443 Mon Sep 17 00:00:00 2001 From: Paul Murphy <12972156+pmur@users.noreply.github.com> Date: Mon, 9 Dec 2019 05:54:06 -0600 Subject: [PATCH 12/26] Merge pull request #15257 from pmur:resize * resize: HResizeLinear reduce duplicate work There appears to be a 2x unroll of the HResizeLinear against k, however the k value is only incremented by 1 during the unroll. This results in k - 1 duplicate passes when k > 1. Likewise, the final pass may not respect the work done by the vector loop. Start it with the offset returned by the vector op if implemented. Note, no vector ops are implemented today. The performance is most noticable on a linear downscale. A set of performance tests are added to characterize this. The performance improvement is 10-50% depending on the scaling. * imgproc: vectorize HResizeLinear Performance is mostly gated by the gather operations for x inputs. Likewise, provide a 2x unroll against k, this reduces the number of alpha gathers by 1/2 for larger k. While not a 4x improvement, it still performs substantially better under P9 for a 1.4x improvement. P8 baseline is 1.05-1.10x due to reduced VSX instruction set. For float types, this results in a more modest 1.2x improvement. * Update U8 processing for non-bitexact linear resize * core: hal: vsx: improve v_load_expand_q With a little help, we can do this quickly without gprs on all VSX enabled targets. * resize: Fix cn == 3 step per feedback Per feedback, ensure we don't overrun. This was caught via the failure observed in Test_TensorFlow.inception_accuracy. --- .../include/opencv2/core/hal/intrin_vsx.hpp | 30 +- modules/imgproc/perf/perf_resize.cpp | 86 +++++ modules/imgproc/src/resize.cpp | 314 +++++++++++++++++- 3 files changed, 426 insertions(+), 4 deletions(-) diff --git a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp index e4d13af1a2..bda1d8558f 100644 --- a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp @@ -346,11 +346,37 @@ OPENCV_HAL_IMPL_VSX_EXPAND(v_int16x8, v_int32x4, short, vec_unpackl, vec_unpackh OPENCV_HAL_IMPL_VSX_EXPAND(v_uint32x4, v_uint64x2, uint, vec_unpacklu, vec_unpackhu) OPENCV_HAL_IMPL_VSX_EXPAND(v_int32x4, v_int64x2, int, vec_unpackl, vec_unpackh) +/* Load and zero expand a 4 byte value into the second dword, first is don't care. */ +#if !defined(CV_COMPILER_VSX_BROKEN_ASM) + #define _LXSIWZX(out, ptr, T) __asm__ ("lxsiwzx %x0, 0, %1\r\n" : "=wa"(out) : "r" (ptr) : "memory"); +#else + /* This is compiler-agnostic, but will introduce an unneeded splat on the critical path. */ + #define _LXSIWZX(out, ptr, T) out = (T)vec_udword2_sp(*(uint32_t*)(ptr)); +#endif + inline v_uint32x4 v_load_expand_q(const uchar* ptr) -{ return v_uint32x4(vec_uint4_set(ptr[0], ptr[1], ptr[2], ptr[3])); } +{ + // Zero-extend the extra 24B instead of unpacking. Usually faster in small kernel + // Likewise note, value is zero extended and upper 4 bytes are zero'ed. + vec_uchar16 pmu = {8, 12, 12, 12, 9, 12, 12, 12, 10, 12, 12, 12, 11, 12, 12, 12}; + vec_uchar16 out; + + _LXSIWZX(out, ptr, vec_uchar16); + out = vec_perm(out, out, pmu); + return v_uint32x4((vec_uint4)out); +} inline v_int32x4 v_load_expand_q(const schar* ptr) -{ return v_int32x4(vec_int4_set(ptr[0], ptr[1], ptr[2], ptr[3])); } +{ + vec_char16 out; + vec_short8 outs; + vec_int4 outw; + + _LXSIWZX(out, ptr, vec_char16); + outs = vec_unpackl(out); + outw = vec_unpackh(outs); + return v_int32x4(outw); +} /* pack */ #define OPENCV_HAL_IMPL_VSX_PACK(_Tpvec, _Tp, _Tpwvec, _Tpvn, _Tpdel, sfnc, pkfnc, addfnc, pack) \ diff --git a/modules/imgproc/perf/perf_resize.cpp b/modules/imgproc/perf/perf_resize.cpp index 0705108503..236955bd66 100644 --- a/modules/imgproc/perf/perf_resize.cpp +++ b/modules/imgproc/perf/perf_resize.cpp @@ -7,6 +7,31 @@ namespace opencv_test { typedef tuple MatInfo_Size_Size_t; typedef TestBaseWithParam MatInfo_Size_Size; +typedef tuple Size_Size_t; +typedef tuple MatInfo_SizePair_t; +typedef TestBaseWithParam MatInfo_SizePair; + +#define MATTYPE_NE_VALUES CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4, \ + CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4, \ + CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4 + +// For gradient-ish testing of the other matrix formats +template +static void fillFPGradient(Mat& img) +{ + const int ch = img.channels(); + + int r, c, i; + for(r=0; r(GetParam()); + Size_Size_t sizes = get<1>(GetParam()); + Size from = get<0>(sizes); + Size to = get<1>(sizes); + + cv::Mat src(from, matType), dst(to, matType); + switch(src.depth()) + { + case CV_8U: cvtest::fillGradient(src); break; + case CV_16U: fillFPGradient(src); break; + case CV_32F: fillFPGradient(src); break; + } + declare.in(src).out(dst); + + TEST_CYCLE_MULTIRUN(10) resize(src, dst, to, 0, 0, INTER_LINEAR); + + SANITY_CHECK_NOTHING(); +} + PERF_TEST_P(MatInfo_Size_Size, resizeDownLinear, testing::Values( MatInfo_Size_Size_t(CV_8UC1, szVGA, szQVGA), @@ -80,6 +132,40 @@ PERF_TEST_P(MatInfo_Size_Size, resizeDownLinear, #endif } +PERF_TEST_P(MatInfo_SizePair, resizeDownLinearNonExact, + testing::Combine + ( + testing::Values( MATTYPE_NE_VALUES ), + testing::Values + ( + Size_Size_t(szVGA, szQVGA), + Size_Size_t(szqHD, szVGA), + Size_Size_t(sz720p, Size(120 * sz720p.width / sz720p.height, 120)), + Size_Size_t(sz720p, szVGA), + Size_Size_t(sz720p, szQVGA) + ) + ) + ) +{ + int matType = get<0>(GetParam()); + Size_Size_t sizes = get<1>(GetParam()); + Size from = get<0>(sizes); + Size to = get<1>(sizes); + + cv::Mat src(from, matType), dst(to, matType); + switch(src.depth()) + { + case CV_8U: cvtest::fillGradient(src); break; + case CV_16U: fillFPGradient(src); break; + case CV_32F: fillFPGradient(src); break; + } + declare.in(src).out(dst); + + TEST_CYCLE_MULTIRUN(10) resize(src, dst, to, 0, 0, INTER_LINEAR); + + SANITY_CHECK_NOTHING(); +} + typedef tuple MatInfo_Size_Scale_t; typedef TestBaseWithParam MatInfo_Size_Scale; diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index b0283e5ca4..56f06ac4b7 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1481,10 +1481,320 @@ typedef VResizeNoVec VResizeLanczos4Vec_32f; #endif +#if CV_SIMD128 + +template +struct HResizeLinearVec_X4 +{ + int operator()(const uchar** _src, uchar** _dst, int count, const int* xofs, + const uchar* _alpha, int, int, int cn, int, int xmax) const + { + const ST **src = (const ST**)_src; + const AT *alpha = (const AT*)_alpha; + DT **dst = (DT**)_dst; + const int nlanes = 4; + const int len0 = xmax & -nlanes; + int dx = 0, k = 0; + + for( ; k <= (count - 2); k+=2 ) + { + const ST *S0 = src[k]; + DT *D0 = dst[k]; + const ST *S1 = src[k+1]; + DT *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += nlanes ) + { + int sx0 = xofs[dx+0]; + int sx1 = xofs[dx+1]; + int sx2 = xofs[dx+2]; + int sx3 = xofs[dx+3]; + DVT a_even; + DVT a_odd; + + v_load_deinterleave(&alpha[dx*2], a_even, a_odd); + DVT s0(S0[sx0], S0[sx1], S0[sx2], S0[sx3]); + DVT s1(S0[sx0+cn], S0[sx1+cn], S0[sx2+cn], S0[sx3+cn]); + DVT s0_u(S1[sx0], S1[sx1], S1[sx2], S1[sx3]); + DVT s1_u(S1[sx0+cn], S1[sx1+cn], S1[sx2+cn], S1[sx3+cn]); + v_store(&D1[dx], s0_u * a_even + s1_u * a_odd); + v_store(&D0[dx], s0 * a_even + s1 * a_odd); + } + } + for( ; k < count; k++ ) + { + const ST *S = src[k]; + DT *D = dst[k]; + for( dx = 0; dx < len0; dx += nlanes ) + { + int sx0 = xofs[dx+0]; + int sx1 = xofs[dx+1]; + int sx2 = xofs[dx+2]; + int sx3 = xofs[dx+3]; + DVT a_even; + DVT a_odd; + + v_load_deinterleave(&alpha[dx*2], a_even, a_odd); + DVT s0(S[sx0], S[sx1], S[sx2], S[sx3]); + DVT s1(S[sx0+cn], S[sx1+cn], S[sx2+cn], S[sx3+cn]); + v_store(&D[dx], s0 * a_even + s1 * a_odd); + } + } + return dx; + } +}; + +struct HResizeLinearVecU8_X4 +{ + int operator()(const uchar** src, uchar** _dst, int count, const int* xofs, + const uchar* _alpha, int, int, int cn, int, int xmax) const + { + const short *alpha = (const short*)_alpha; + int **dst = (int**)_dst; + int dx = 0, k = 0; + + if(cn == 1) + { + const int step = 8; + const int len0 = xmax & -step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 al = v_load(alpha+dx*2); + v_int16x8 ah = v_load(alpha+dx*2+8); + v_uint16x8 sl, sh; + v_expand(v_lut_pairs(S0, xofs+dx), sl, sh); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + v_expand(v_lut_pairs(S1, xofs+dx), sl, sh); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 al = v_load(alpha+dx*2); + v_int16x8 ah = v_load(alpha+dx*2+8); + v_uint16x8 sl, sh; + v_expand(v_lut_pairs(S, xofs+dx), sl, sh); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + } + } + } + else if(cn == 2) + { + const int step = 8; + const int len0 = xmax & -step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 al = v_load(alpha+dx*2); + v_int16x8 ah = v_load(alpha+dx*2+8); + v_uint16x8 sl, sh; + v_expand(v_interleave_pairs(v_lut_quads(S0, xofs+dx)), sl, sh); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + v_expand(v_interleave_pairs(v_lut_pairs(S1, xofs+dx)), sl, sh); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 al = v_load(alpha+dx*2); + v_int16x8 ah = v_load(alpha+dx*2+8); + v_uint16x8 sl, sh; + v_expand(v_interleave_pairs(v_lut_quads(S, xofs+dx)), sl, sh); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(sl), al)); + v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(sh), ah)); + } + } + } + else if(cn == 3) + { + const int step = 4; + const int len0 = xmax - step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += 3*step/4 ) + { + v_int16x8 a = v_load(alpha+dx*2); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(v_load_expand_q(S0+xofs[dx]) | (v_load_expand_q(S0+xofs[dx]+cn)<<16)), a)); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(v_load_expand_q(S1+xofs[dx]) | (v_load_expand_q(S1+xofs[dx]+cn)<<16)), a)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += 3*step/4 ) + { + v_int16x8 a = v_load(alpha+dx*2); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(v_load_expand_q(S+xofs[dx]) | (v_load_expand_q(S+xofs[dx]+cn)<<16)), a)); + } + } + } + else if(cn == 4) + { + const int step = 4; + const int len0 = xmax & -step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 a = v_load(alpha+dx*2); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(v_interleave_quads(v_load_expand(S0+xofs[dx]))), a)); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(v_interleave_quads(v_load_expand(S1+xofs[dx]))), a)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 a = v_load(alpha+dx*2); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(v_interleave_quads(v_load_expand(S+xofs[dx]))), a)); + } + } + } + else if(cn < 9) + { + const int step = 8; + const int len0 = xmax & -step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += cn ) + { + v_int16x8 a0 = v_load(alpha+dx*2); + v_int16x8 a1 = v_load(alpha+dx*2 + 8); + v_uint16x8 s0, s1; + v_zip(v_load_expand(S0+xofs[dx]), v_load_expand(S0+xofs[dx]+cn), s0, s1); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); + v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); + v_zip(v_load_expand(S1+xofs[dx]), v_load_expand(S1+xofs[dx]+cn), s0, s1); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); + v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += cn ) + { + v_int16x8 a0 = v_load(alpha+dx*2); + v_int16x8 a1 = v_load(alpha+dx*2 + 8); + v_uint16x8 s0, s1; + v_zip(v_load_expand(S+xofs[dx]), v_load_expand(S+xofs[dx]+cn), s0, s1); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(s0), a0)); + v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(s1), a1)); + } + } + } + else + { + const int step = 16; + const int len0 = (xmax - cn) & -step; + for( ; k <= (count - 2); k+=2 ) + { + const uchar *S0 = src[k]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 a0 = v_load(alpha+dx*2); + v_int16x8 a1 = v_load(alpha+dx*2 + 8); + v_int16x8 a2 = v_load(alpha+dx*2 + 16); + v_int16x8 a3 = v_load(alpha+dx*2 + 24); + v_uint8x16 s01, s23; + v_zip(v_lut(S0, xofs+dx), v_lut(S0+cn, xofs+dx), s01, s23); + v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); + v_store(&D0[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); + v_store(&D0[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); + v_store(&D0[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); + v_zip(v_lut(S1, xofs+dx), v_lut(S1+cn, xofs+dx), s01, s23); + v_store(&D1[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); + v_store(&D1[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); + v_store(&D1[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); + v_store(&D1[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); + } + } + for( ; k < count; k++ ) + { + const uchar *S = src[k]; + int *D = dst[k]; + for( dx = 0; dx < len0; dx += step ) + { + v_int16x8 a0 = v_load(alpha+dx*2); + v_int16x8 a1 = v_load(alpha+dx*2 + 8); + v_int16x8 a2 = v_load(alpha+dx*2 + 16); + v_int16x8 a3 = v_load(alpha+dx*2 + 24); + v_uint8x16 s01, s23; + v_zip(v_lut(S, xofs+dx), v_lut(S+cn, xofs+dx), s01, s23); + v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(v_expand_low(s01)), a0)); + v_store(&D[dx+4], v_dotprod(v_reinterpret_as_s16(v_expand_high(s01)), a1)); + v_store(&D[dx+8], v_dotprod(v_reinterpret_as_s16(v_expand_low(s23)), a2)); + v_store(&D[dx+12], v_dotprod(v_reinterpret_as_s16(v_expand_high(s23)), a3)); + } + } + } + return dx; + } +}; + +typedef HResizeLinearVec_X4 HResizeLinearVec_32f; +typedef HResizeLinearVec_X4 HResizeLinearVec_16u32f; +typedef HResizeLinearVec_X4 HResizeLinearVec_16s32f; +typedef HResizeLinearVecU8_X4 HResizeLinearVec_8u32s; + +#else + typedef HResizeNoVec HResizeLinearVec_8u32s; typedef HResizeNoVec HResizeLinearVec_16u32f; typedef HResizeNoVec HResizeLinearVec_16s32f; typedef HResizeNoVec HResizeLinearVec_32f; + +#endif + typedef HResizeNoVec HResizeLinearVec_64f; @@ -1505,7 +1815,7 @@ struct HResizeLinear int dx0 = vecOp((const uchar**)src, (uchar**)dst, count, xofs, (const uchar*)alpha, swidth, dwidth, cn, xmin, xmax ); - for( k = 0; k <= count - 2; k++ ) + for( k = 0; k <= count - 2; k+=2 ) { const T *S0 = src[k], *S1 = src[k+1]; WT *D0 = dst[k], *D1 = dst[k+1]; @@ -1529,7 +1839,7 @@ struct HResizeLinear { const T *S = src[k]; WT *D = dst[k]; - for( dx = 0; dx < xmax; dx++ ) + for( dx = dx0; dx < xmax; dx++ ) { int sx = xofs[dx]; D[dx] = S[sx]*alpha[dx*2] + S[sx+cn]*alpha[dx*2+1]; From 766465ce9483c20d54bfce422d285c077f6502bd Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 9 Dec 2019 13:32:16 +0300 Subject: [PATCH 13/26] Added Xperience.AI to copyright file. --- LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/LICENSE b/LICENSE index be5767149f..aeb19a4908 100644 --- a/LICENSE +++ b/LICENSE @@ -13,6 +13,7 @@ Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +Copyright (C) 2019, Xperience AI, all rights reserved. Third party copyrights are property of their respective owners. Redistribution and use in source and binary forms, with or without modification, From c2ca3ee2faa52fd5bbbddeb8edc31a6edfedb207 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 9 Dec 2019 19:06:47 +0300 Subject: [PATCH 14/26] Fix weights fusion for Convolution and Deconvolution layers in nGraph --- modules/dnn/src/layers/convolution_layer.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 4d1fe5f5ae..a4406a6466 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -553,10 +553,10 @@ public: } else { - Mat newWeights = blobs[0].reshape(1, outCn); - Mat cvWeights = weightsMat.colRange(0, newWeights.cols); + Mat newWeights; + Mat cvWeights = weightsMat.colRange(0, blobs[0].total() / outCn); cvWeights.copyTo(newWeights); - ieWeights = std::make_shared(ngraph::element::f32, kernel_shape, blobs[0].data); + ieWeights = std::make_shared(ngraph::element::f32, kernel_shape, newWeights.data); } } @@ -2033,9 +2033,9 @@ public: if (fusedWeights) { - int inpCn = blobs[0].size[0]; - Mat newWeights = blobs[0].reshape(1, inpCn); + Mat newWeights; transpose(weightsMat, newWeights); + ieWeights = std::make_shared(ngraph::element::f32, kernel_shape, newWeights.data); } size_t batch = ieInpNode->get_shape()[0]; std::vector out_shape = {batch, (size_t)numOutput}; From b9435b9e38dfaa707623879158bcb92952010fe0 Mon Sep 17 00:00:00 2001 From: RAJKIRAN NATARAJAN Date: Mon, 9 Dec 2019 19:24:35 +0000 Subject: [PATCH 15/26] Merge pull request #16094 from saskatchewancatch:issue-16053 * Add eps error checking for approxPolyDP to allow sensible values only for epsilon value of Douglas-Peucker algorithm. * Review changes for PR --- modules/imgproc/src/approx.cpp | 7 +++++++ modules/imgproc/test/test_approxpoly.cpp | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index 8491e81016..195cd16399 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -677,6 +677,13 @@ void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve, { CV_INSTRUMENT_REGION(); + //Prevent unreasonable error values (Douglas-Peucker algorithm) + //from being used. + if (epsilon < 0.0 || !(epsilon < 1e30)) + { + CV_Error(CV_StsOutOfRange, "Epsilon not valid."); + } + Mat curve = _curve.getMat(); int npoints = curve.checkVector(2), depth = curve.depth(); CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F)); diff --git a/modules/imgproc/test/test_approxpoly.cpp b/modules/imgproc/test/test_approxpoly.cpp index 69511a6f8c..845d7bb4ea 100644 --- a/modules/imgproc/test/test_approxpoly.cpp +++ b/modules/imgproc/test/test_approxpoly.cpp @@ -355,4 +355,25 @@ _exit_: TEST(Imgproc_ApproxPoly, accuracy) { CV_ApproxPolyTest test; test.safe_run(); } +//Tests to make sure that unreasonable epsilon (error) +//values never get passed to the Douglas-Peucker algorithm. +TEST(Imgproc_ApproxPoly, bad_epsilon) +{ + std::vector inputPoints; + inputPoints.push_back(Point2f(0.0f, 0.0f)); + std::vector outputPoints; + + double eps = std::numeric_limits::infinity(); + ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false)); + + eps = 9e99; + ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false)); + + eps = -1e-6; + ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false)); + + eps = NAN; + ASSERT_ANY_THROW(approxPolyDP(inputPoints, outputPoints, eps, false)); +} + }} // namespace From fe77223dee6e7bd43f2ec011627e3dce83c07bb4 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Mon, 9 Dec 2019 21:47:13 +0300 Subject: [PATCH 16/26] Modify nGraph's ConvolutionBackpropData and GroupConvolution --- modules/dnn/src/layers/convolution_layer.cpp | 26 +++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 4d1fe5f5ae..3b5a7d8ea5 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -544,6 +544,12 @@ public: const int group = inpCn / inpGroupCn; std::vector kernel_shape = getShape(blobs[0]); + if (group != 1) + { + kernel_shape[0] /= group; + kernel_shape.insert(kernel_shape.begin(), group); + } + auto ieWeights = std::make_shared(ngraph::element::f32, kernel_shape, blobs[0].data); if (fusedWeights) { @@ -566,14 +572,12 @@ public: std::shared_ptr conv_node; if (group != 1) { - conv_node = std::make_shared( + conv_node = std::make_shared( ieInpNode, ieWeights, ngraph::Strides(strides), - ngraph::Strides(dilations), ngraph::CoordinateDiff(std::vector(pads_begin.begin(), pads_begin.end())), ngraph::CoordinateDiff(std::vector(pads_end.begin(), pads_end.end())), - ngraph::Strides{}, - group, + ngraph::Strides(dilations), pad_type); } else { conv_node = std::make_shared( @@ -2037,37 +2041,29 @@ public: Mat newWeights = blobs[0].reshape(1, inpCn); transpose(weightsMat, newWeights); } - size_t batch = ieInpNode->get_shape()[0]; - std::vector out_shape = {batch, (size_t)numOutput}; std::vector paddings_end; - std::vector inpShape = ieInpNode->get_shape(); if (padMode.empty()) { for (int i = 0; i < pads_end.size(); i++) { - out_shape.push_back(strides[i] * (inpShape[2 + i] - 1) + - kernel_size[i] - pads_begin[i] - pads_end[i] + adjust_pads[i]); paddings_end.push_back(pads_end[i] - adjust_pads[i]); } } else if (padMode == "SAME") { for (int i = 0; i < pads_begin.size(); i++) { - out_shape.push_back(strides[i] * (inpShape[2 + i] - 1) + 1 + adjust_pads[i]); paddings_end.push_back(kernel_size[i] - pads_begin[i] - 1 - adjust_pads[i]); } } else { paddings_end = pads_end; } - auto deconv = std::make_shared( - ngraph::Shape{out_shape}, - ieWeights, + auto deconv = std::make_shared( ieInpNode, + ieWeights, ngraph::Strides(strides), - ngraph::Strides(dilations), ngraph::CoordinateDiff(std::vector(pads_begin.begin(), pads_begin.end())), ngraph::CoordinateDiff(std::vector(paddings_end.begin(), paddings_end.end())), - (strides.size() == 2 ? ngraph::Strides{1, 1} : ngraph::Strides{1, 1, 1})); + ngraph::Strides(dilations)); if (hasBias() || fusedBias) { std::vector shape(deconv->get_shape().size(), 1); From 435c97c7a2ca7d88dd1e54b2ce360a754002c130 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 10 Dec 2019 15:13:38 +0300 Subject: [PATCH 17/26] imgproc: add parameter checks in calcHist and calcBackProj --- modules/imgproc/src/histogram.cpp | 14 +++++++-- modules/imgproc/test/test_histograms.cpp | 37 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index 64a5c61afe..e66e2e2d23 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -75,8 +75,8 @@ calcHistLookupTables_8u( const Mat& hist, const SparseMat& shist, int sz = !issparse ? hist.size[i] : shist.size(i); size_t step = !issparse ? hist.step[i] : 1; - double v_lo = ranges[i][0]; - double v_hi = ranges[i][1]; + double v_lo = ranges ? ranges[i][0] : 0; + double v_hi = ranges ? ranges[i][1] : 256; for( j = low; j < high; j++ ) { @@ -183,7 +183,7 @@ static void histPrepareImages( const Mat* images, int nimages, const int* channe imsize.height = 1; } - if( !ranges ) + if( !ranges ) // implicit uniform ranges for 8U { CV_Assert( depth == CV_8U ); @@ -951,6 +951,8 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels, { CV_INSTRUMENT_REGION(); + CV_Assert(images && nimages > 0); + CV_OVX_RUN( images && histSize && nimages == 1 && images[0].type() == CV_8UC1 && dims == 1 && _mask.getMat().empty() && @@ -1261,6 +1263,8 @@ void cv::calcHist( const Mat* images, int nimages, const int* channels, { CV_INSTRUMENT_REGION(); + CV_Assert(images && nimages > 0); + Mat mask = _mask.getMat(); calcHist( images, nimages, channels, mask, hist, dims, histSize, ranges, uniform, accumulate, false ); @@ -1608,6 +1612,8 @@ void cv::calcBackProject( const Mat* images, int nimages, const int* channels, { CV_INSTRUMENT_REGION(); + CV_Assert(images && nimages > 0); + Mat hist = _hist.getMat(); std::vector ptrs; std::vector deltas; @@ -1777,6 +1783,8 @@ void cv::calcBackProject( const Mat* images, int nimages, const int* channels, { CV_INSTRUMENT_REGION(); + CV_Assert(images && nimages > 0); + std::vector ptrs; std::vector deltas; std::vector uniranges; diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index e9c7575ca4..fdf31fe771 100644 --- a/modules/imgproc/test/test_histograms.cpp +++ b/modules/imgproc/test/test_histograms.cpp @@ -1957,5 +1957,42 @@ TEST(Imgproc_Hist_Calc, calcHist_regression_11544) } } +TEST(Imgproc_Hist_Calc, badarg) +{ + const int channels[] = {0}; + float range1[] = {0, 10}; + float range2[] = {10, 20}; + const float * ranges[] = {range1, range2}; + Mat img = cv::Mat::zeros(10, 10, CV_8UC1); + Mat imgInt = cv::Mat::zeros(10, 10, CV_32SC1); + Mat hist; + const int hist_size[] = { 100 }; + // base run + EXPECT_NO_THROW(cv::calcHist(&img, 1, channels, noArray(), hist, 1, hist_size, ranges, true)); + // bad parameters + EXPECT_THROW(cv::calcHist(NULL, 1, channels, noArray(), hist, 1, hist_size, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&img, 0, channels, noArray(), hist, 1, hist_size, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&img, 1, NULL, noArray(), hist, 2, hist_size, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&img, 1, channels, noArray(), noArray(), 1, hist_size, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&img, 1, channels, noArray(), hist, -1, hist_size, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&img, 1, channels, noArray(), hist, 1, NULL, ranges, true), cv::Exception); + EXPECT_THROW(cv::calcHist(&imgInt, 1, channels, noArray(), hist, 1, hist_size, NULL, true), cv::Exception); + // special case + EXPECT_NO_THROW(cv::calcHist(&img, 1, channels, noArray(), hist, 1, hist_size, NULL, true)); + + Mat backProj; + // base run + EXPECT_NO_THROW(cv::calcBackProject(&img, 1, channels, hist, backProj, ranges, 1, true)); + // bad parameters + EXPECT_THROW(cv::calcBackProject(NULL, 1, channels, hist, backProj, ranges, 1, true), cv::Exception); + EXPECT_THROW(cv::calcBackProject(&img, 0, channels, hist, backProj, ranges, 1, true), cv::Exception); + EXPECT_THROW(cv::calcBackProject(&img, 1, channels, noArray(), backProj, ranges, 1, true), cv::Exception); + EXPECT_THROW(cv::calcBackProject(&img, 1, channels, hist, noArray(), ranges, 1, true), cv::Exception); + EXPECT_THROW(cv::calcBackProject(&imgInt, 1, channels, hist, backProj, NULL, 1, true), cv::Exception); + // special case + EXPECT_NO_THROW(cv::calcBackProject(&img, 1, channels, hist, backProj, NULL, 1, true)); +} + + }} // namespace /* End Of File */ From 7b298b09952f8a48a4674c43d1c64ea3c70b91dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= Date: Mon, 9 Dec 2019 20:25:40 +0000 Subject: [PATCH 18/26] Fix pch generation when linker flags used with GCC -c is required to avoid linking (and the associated missing "main" message) when linker flags like "-Wl,-z,relro" are passed to GCC --- cmake/OpenCVPCHSupport.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/OpenCVPCHSupport.cmake b/cmake/OpenCVPCHSupport.cmake index a796ae9faa..08cd06def4 100644 --- a/cmake/OpenCVPCHSupport.cmake +++ b/cmake/OpenCVPCHSupport.cmake @@ -125,11 +125,11 @@ MACRO(_PCH_GET_COMPILE_COMMAND out_command _input _output) STRING(REGEX REPLACE "^ +" "" pchsupport_compiler_cxx_arg1 ${CMAKE_CXX_COMPILER_ARG1}) SET(${out_command} - ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input} + ${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} -x c++-header -o ${_output} -c ${_input} ) ELSE(CMAKE_CXX_COMPILER_ARG1) SET(${out_command} - ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input} + ${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} -c ${_input} ) ENDIF(CMAKE_CXX_COMPILER_ARG1) ELSE() From 4ec4ec844f71a746a67f98f2ecd2679c44f60610 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Tue, 10 Dec 2019 19:00:10 +0000 Subject: [PATCH 19/26] python: fix issue with bindings loading on Python 3.8 --- modules/python/package/cv2/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/python/package/cv2/__init__.py b/modules/python/package/cv2/__init__.py index 5220e38553..d367998b0b 100644 --- a/modules/python/package/cv2/__init__.py +++ b/modules/python/package/cv2/__init__.py @@ -68,6 +68,13 @@ def bootstrap(): sys.path.insert(1, p) if os.name == 'nt': + if sys.version_info[:2] >= (3, 8): # https://github.com/python/cpython/pull/12302 + for p in l_vars['BINARIES_PATHS']: + try: + os.add_dll_directory(p) + except Exception as e: + if DEBUG: print('Failed os.add_dll_directory(): '+ str(e)) + pass os.environ['PATH'] = ';'.join(l_vars['BINARIES_PATHS']) + ';' + os.environ.get('PATH', '') if DEBUG: print('OpenCV loader: PATH={}'.format(str(os.environ['PATH']))) else: From b89581960c945293a3a1af50b6a7ec8b98b4b3ed Mon Sep 17 00:00:00 2001 From: shimat Date: Wed, 11 Dec 2019 09:13:58 +0900 Subject: [PATCH 20/26] s/Voroni/Voronoi/g --- modules/imgproc/include/opencv2/imgproc.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 42c08d13de..769caef14f 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1076,11 +1076,11 @@ public: */ CV_WRAP void getTriangleList(CV_OUT std::vector& triangleList) const; - /** @brief Returns a list of all Voroni facets. + /** @brief Returns a list of all Voronoi facets. @param idx Vector of vertices IDs to consider. For all vertices you can pass empty vector. - @param facetList Output vector of the Voroni facets. - @param facetCenters Output vector of the Voroni facets center points. + @param facetList Output vector of the Voronoi facets. + @param facetCenters Output vector of the Voronoi facets center points. */ CV_WRAP void getVoronoiFacetList(const std::vector& idx, CV_OUT std::vector >& facetList, From a675c4937ad0e668379edebe0aeee70026332a4f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 11 Dec 2019 00:27:13 +0000 Subject: [PATCH 21/26] core: OPENCV_INCLUDE_PORT_FILE for custom platform configuration --- modules/core/include/opencv2/core/cvdef.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index ea446b3fcb..468deaba5f 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -48,6 +48,10 @@ //! @addtogroup core_utils //! @{ +#ifdef OPENCV_INCLUDE_PORT_FILE // User-provided header file with custom platform configuration +#include OPENCV_INCLUDE_PORT_FILE +#endif + #if !defined CV_DOXYGEN && !defined CV_IGNORE_DEBUG_BUILD_GUARD #if (defined(_MSC_VER) && (defined(DEBUG) || defined(_DEBUG))) || \ (defined(_GLIBCXX_DEBUG) || defined(_GLIBCXX_DEBUG_PEDANTIC)) From 76b5e19eb3a1e343f6ca5d9c0ab25d643665b760 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 11 Dec 2019 00:35:13 +0000 Subject: [PATCH 22/26] core: add "namespace cv" in CV_StaticAssert fallback implementation --- modules/core/include/opencv2/core/cvdef.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index ea446b3fcb..c0c6867768 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -118,9 +118,11 @@ namespace cv { namespace debug_build_guard { } using namespace debug_build_guard # if !defined(__clang__) && defined(__GNUC__) && (__GNUC__*100 + __GNUC_MINOR__ > 302) # define CV_StaticAssert(condition, reason) ({ extern int __attribute__((error("CV_StaticAssert: " reason " " #condition))) CV_StaticAssert(); ((condition) ? 0 : CV_StaticAssert()); }) # else +namespace cv { template struct CV_StaticAssert_failed; template <> struct CV_StaticAssert_failed { enum { val = 1 }; }; template struct CV_StaticAssert_test {}; +} # define CV_StaticAssert(condition, reason)\ typedef cv::CV_StaticAssert_test< sizeof(cv::CV_StaticAssert_failed< static_cast(condition) >) > CVAUX_CONCAT(CV_StaticAssert_failed_at_, __LINE__) # endif From 416848066c7ed3398bee51cf0f4731a095f4d63b Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 11 Dec 2019 00:45:10 +0000 Subject: [PATCH 23/26] core: provide safe implementations of CV_XADD() only --- modules/core/include/opencv2/core/cvdef.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index ea446b3fcb..47bc15482d 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -519,7 +519,11 @@ Cv64suf; # include # define CV_XADD(addr, delta) (int)_InterlockedExchangeAdd((long volatile*)addr, delta) #else - CV_INLINE CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; } + #ifdef OPENCV_FORCE_UNSAFE_XADD + CV_INLINE CV_XADD(int* addr, int delta) { int tmp = *addr; *addr += delta; return tmp; } + #else + #error "OpenCV: can't define safe CV_XADD macro for current platform (unsupported). Define CV_XADD macro through custom port header (see OPENCV_INCLUDE_PORT_FILE)" + #endif #endif From 15612ebd39ea072c9af13b5fde68a810d7f44b3a Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 11 Dec 2019 16:52:38 +0300 Subject: [PATCH 24/26] python: enable Python 3.8 --- modules/python/package/setup.py | 1 + samples/_winpack_run_python_sample.cmd | 2 ++ 2 files changed, 3 insertions(+) diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index 33a3208225..6bd06c65b7 100644 --- a/modules/python/package/setup.py +++ b/modules/python/package/setup.py @@ -44,6 +44,7 @@ def main(): 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: C++', 'Programming Language :: Python :: Implementation :: CPython', 'Topic :: Scientific/Engineering', diff --git a/samples/_winpack_run_python_sample.cmd b/samples/_winpack_run_python_sample.cmd index 5e84e2e954..a42c0c61cf 100644 --- a/samples/_winpack_run_python_sample.cmd +++ b/samples/_winpack_run_python_sample.cmd @@ -15,6 +15,8 @@ IF %ERRORLEVEL% EQU 0 ( GOTO :PYTHON_FOUND ) +CALL :QUERY_PYTHON 3.8 +IF %ERRORLEVEL% EQU 0 GOTO :PYTHON_FOUND CALL :QUERY_PYTHON 3.7 IF %ERRORLEVEL% EQU 0 GOTO :PYTHON_FOUND CALL :QUERY_PYTHON 3.6 From 5ee7abbe3c279a1b4f260b641fd2aeac322819cb Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 11 Dec 2019 20:16:58 +0300 Subject: [PATCH 25/26] Merge pull request #16088 from alalek:dnn_eltwise_layer_different_src_channels dnn(eltwise): fix handling of different number of channels * dnn(test): reproducer for Eltwise layer issue from PR16063 * dnn(eltwise): rework support for inputs with different channels * dnn(eltwise): get rid of finalize(), variableChannels * dnn(eltwise): update input sorting by number of channels - do not swap inputs if number of channels are same after truncation * dnn(test): skip "shortcut" with batch size 2 on MYRIAD targets --- modules/dnn/CMakeLists.txt | 1 + .../dnn/include/opencv2/dnn/all_layers.hpp | 7 + modules/dnn/src/darknet/darknet_io.cpp | 1 + modules/dnn/src/layers/eltwise_layer.cpp | 376 +++++++++++++----- modules/dnn/test/test_darknet_importer.cpp | 42 ++ modules/dnn/test/test_layers.cpp | 100 ++++- modules/ts/src/ts_func.cpp | 3 +- 7 files changed, 407 insertions(+), 123 deletions(-) diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index 7ed4f27cd6..4fb5e6c8e7 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -58,6 +58,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4456 /wd4510 /wd4610 /wd4800 /wd4701 /wd4703 # potentially uninitialized local/pointer variable 'value' used /wd4505 # unreferenced local function has been removed + /wd4458 # declaration of 'x' hides class member. GCC still works, MSVC bug is here: https://developercommunity.visualstudio.com/content/problem/219311/c-c4458-declaration-hides-class-member-warning-iss.html -wd858 -wd2196 -Winvalid-offsetof # Apple Clang (attr_value.pb.cc) ) diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 363dcc01cb..b251b4adb3 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -508,6 +508,13 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN static Ptr create(const LayerParams ¶ms); }; + /** @brief Element wise operation on inputs + + Extra optional parameters: + - "operation" as string. Values are "sum" (default), "prod", "max", "div" + - "coeff" as float array. Specify weights of inputs for SUM operation + - "output_channels_mode" as string. Values are "same" (default, all input must have the same layout), "input_0", "input_0_truncate", "max_input_channels" + */ class CV_EXPORTS EltwiseLayer : public Layer { public: diff --git a/modules/dnn/src/darknet/darknet_io.cpp b/modules/dnn/src/darknet/darknet_io.cpp index 3a90081e17..e9938ecbb9 100644 --- a/modules/dnn/src/darknet/darknet_io.cpp +++ b/modules/dnn/src/darknet/darknet_io.cpp @@ -425,6 +425,7 @@ namespace cv { } shortcut_param.set("op", "sum"); + shortcut_param.set("output_channels_mode", "input_0_truncate"); darknet::LayerParameter lp; std::string layer_name = cv::format("shortcut_%d", layer_id); diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index 978b1b6f4a..c7c28e8fb8 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -66,9 +66,28 @@ public: DIV = 3 } op; std::vector coeffs; - bool variableChannels; + + enum OutputChannelsMode + { + ELTWISE_CHANNNELS_SAME = 0, //!< number of channels from inputs must be the same and equal to output's number of channels + ELTWISE_CHANNNELS_INPUT_0, //!< number of channels from inputs may be different, + //!< output's number of channels is equal to number of channels of first input + //!< number of channels of other inputs should not be greater than number of channels of first input + ELTWISE_CHANNNELS_INPUT_0_TRUNCATE, //!< number of channels from inputs may be different, + //!< output's number of channels is equal to number of channels of first input + //!< there is restriction on number of channels of other inputs + //!< extra channels of other inputs is ignored + ELTWISE_CHANNNELS_USE_MAX, //!< number of channels from inputs may be different, + //!< output's number of channels is equal to maximal number of input channels + //!< @note supported operation: `SUM` + } channelsModeInput; + + + mutable OutputChannelsMode channelsMode; //!< "optimized" channels mode (switch to ELTWISE_CHANNNELS_SAME if number of input channels are equal) + mutable /*size_t*/int outputChannels; EltwiseLayerImpl(const LayerParams& params) + : outputChannels(0) { setParamsFrom(params); op = SUM; @@ -97,6 +116,35 @@ public: coeffs[i] = paramCoeff.get(i); } } + + channelsModeInput = ELTWISE_CHANNNELS_SAME; + if (params.has("output_channels_mode")) + { + String v = toLowerCase(params.get("output_channels_mode")); + if (v == "same") + { + channelsModeInput = ELTWISE_CHANNNELS_SAME; + } + else if (v == "input_0") + { + channelsModeInput = ELTWISE_CHANNNELS_INPUT_0; + } + else if (v == "input_0_truncate") + { + channelsModeInput = ELTWISE_CHANNNELS_INPUT_0_TRUNCATE; + } + else if (v == "max_input_channels") + { + channelsModeInput = ELTWISE_CHANNNELS_USE_MAX; + if (op != SUM) + CV_Error(cv::Error::StsBadArg, "[" + type + "]:(" + name + ") 'max' channels mode is limited to SUM operation only"); + } + else + CV_Error(cv::Error::StsBadArg, "[" + type + "]:(" + name + ") unknown channels mode: \"" + v + "\""); + } + channelsMode = channelsModeInput; + + // TODO Must have checks for other unknown options } virtual bool supportBackend(int backendId) CV_OVERRIDE @@ -104,7 +152,7 @@ public: return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE || ((((backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019 && (preferableTarget != DNN_TARGET_OPENCL || coeffs.empty())) - || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && !variableChannels)); + || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) && channelsMode == ELTWISE_CHANNNELS_SAME)); } bool getMemoryShapes(const std::vector &inputs, @@ -119,212 +167,320 @@ public: int dims = inputs[0].size(); // Number of channels in output shape is determined by the first input tensor. + bool variableChannels = false; int numChannels = inputs[0][1]; - for (int i = 1; i < inputs.size(); i++) + for (size_t i = 1; i < inputs.size(); i++) { - CV_Assert(inputs[0][0] == inputs[i][0]); + CV_Assert(inputs[0][0] == inputs[i][0]); // batch sizes are equal - // It's allowed for channels axis to be different. - for (int j = 2; j < dims; j++) + int input_channels = inputs[i][1]; + if (numChannels != input_channels) + variableChannels = true; + + if (channelsModeInput == ELTWISE_CHANNNELS_SAME) + { + CV_Assert(numChannels == input_channels); + } + else if (channelsModeInput == ELTWISE_CHANNNELS_INPUT_0) + { + CV_Assert(numChannels >= input_channels); + } + else if (channelsModeInput == ELTWISE_CHANNNELS_INPUT_0_TRUNCATE) + { + // nothing to check + } + else if (channelsModeInput == ELTWISE_CHANNNELS_USE_MAX) + { + numChannels = std::max(numChannels, input_channels); + } + else + { + CV_Assert(0 && "Internal error"); + } + + for (size_t j = 2; j < dims; j++) CV_Assert(inputs[0][j] == inputs[i][j]); } + channelsMode = variableChannels ? channelsModeInput : ELTWISE_CHANNNELS_SAME; + outputChannels = numChannels; + outputs.assign(1, inputs[0]); outputs[0][1] = numChannels; return false; } - void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays) CV_OVERRIDE - { - std::vector inputs; - inputs_arr.getMatVector(inputs); - variableChannels = false; - for (int i = 1; i < inputs.size(); ++i) - { - if (inputs[i].size[1] != inputs[0].size[1]) - { - variableChannels = true; - break; - } - } - } - class EltwiseInvoker : public ParallelLoopBody { - public: + EltwiseLayerImpl& self; std::vector srcs; + std::vector srcNumChannels; int nsrcs; Mat* dst; std::vector coeffs; - EltwiseOp op; int nstripes; const ActivationLayer* activ; int channels; size_t planeSize; - EltwiseInvoker() : nsrcs(0), dst(0), op(PROD), nstripes(0), activ(0), channels(0), planeSize(0) {} + EltwiseInvoker(EltwiseLayerImpl& self_) + : self(self_) + , nsrcs(0), dst(0), nstripes(0), activ(0), channels(0) + , planeSize(0) + {} - static void run(const Mat* srcs, int nsrcs, Mat& dst, - const std::vector& coeffs, EltwiseOp op, - const ActivationLayer* activ, int nstripes) + public: + static void run(EltwiseLayerImpl& self, + const Mat* srcs, int nsrcs, Mat& dst, + int nstripes) { + const EltwiseOp op = self.op; CV_Check(dst.dims, 1 < dst.dims && dst.dims <= 5, ""); CV_CheckTypeEQ(dst.type(), CV_32FC1, ""); CV_Assert(dst.isContinuous()); - CV_Assert(coeffs.empty() || coeffs.size() == (size_t)nsrcs); + CV_Assert(self.coeffs.empty() || self.coeffs.size() == (size_t)nsrcs); + CV_CheckGE(nsrcs, 2, ""); - EltwiseInvoker p; + CV_Assert(self.outputChannels == dst.size[1]); + + EltwiseInvoker p(self); p.srcs.resize(nsrcs); - p.coeffs = coeffs; + p.srcNumChannels.resize(nsrcs); + p.coeffs = self.coeffs; // can be sorted + + bool sortInputs = false; for( int i = 0; i < nsrcs; i++ ) { - p.srcs[i] = srcs + i; - CV_Assert(srcs[i].type() == dst.type() && - srcs[i].isContinuous()); - // Sort srcs and coefficients in the order by number of channels - for( int j = i; j >= 1 && p.srcs[j - 1]->size[1] < p.srcs[j]->size[1]; j-- ) + p.srcs[i] = &srcs[i]; + CV_CheckEQ(srcs[i].dims, dst.dims, ""); + CV_Assert(srcs[i].isContinuous()); + CV_Assert(srcs[i].type() == dst.type()); + p.srcNumChannels[i] = (srcs[i].dims >= 4) ? srcs[i].size[1] : 1; + + if (self.channelsMode == ELTWISE_CHANNNELS_SAME) { - std::swap(p.srcs[j - 1], p.srcs[j]); - if (!p.coeffs.empty()) - std::swap(p.coeffs[j - 1], p.coeffs[j]); + CV_Assert(srcs[i].size == dst.size); + } + else if (self.channelsMode == ELTWISE_CHANNNELS_INPUT_0) + { + if (i == 0) + CV_Assert(srcs[0].size == dst.size); + CV_Assert(self.outputChannels >= p.srcNumChannels[i]); + sortInputs = true; + } + else if (self.channelsMode == ELTWISE_CHANNNELS_INPUT_0_TRUNCATE) + { + if (i == 0) + CV_Assert(srcs[0].size == dst.size); + sortInputs = true; + } + else if (self.channelsMode == ELTWISE_CHANNNELS_USE_MAX) + { + CV_Assert(op == SUM); + CV_Assert(self.outputChannels >= p.srcNumChannels[i]); + sortInputs = true; + } + else + { + CV_Assert(0 && "Internal error"); + } + + if (sortInputs) + { + // Sort srcs and coefficients in the desc order by number of channels + for (int j = i; j >= 1; j--) + { + if (std::min(self.outputChannels, p.srcs[j - 1]->size[1]) < std::min(self.outputChannels, p.srcs[j]->size[1])) + { + std::swap(p.srcs[j - 1], p.srcs[j]); + std::swap(p.srcNumChannels[j - 1], p.srcNumChannels[j]); + if (!p.coeffs.empty()) + std::swap(p.coeffs[j - 1], p.coeffs[j]); + } + else + break; + } } } p.nsrcs = nsrcs; p.dst = &dst; - p.op = op; p.nstripes = nstripes; p.channels = (dst.dims >= 4 ? dst.size[1] : 1); p.planeSize = dst.total(dst.dims >= 4 ? 2 : 1); - CV_Assert(dst.total() == dst.size[0] * p.channels * p.planeSize); + CV_CheckEQ(dst.total(), dst.size[0] * p.channels * p.planeSize, ""); bool simpleCoeffs = true; - if( op == SUM && !coeffs.empty() ) + if (op == SUM && !p.coeffs.empty()) { - CV_Assert( coeffs.size() == (size_t)nsrcs ); + CV_CheckEQ(p.coeffs.size(), (size_t)nsrcs, ""); - for( size_t i = 0; i < coeffs.size(); i++ ) - if( coeffs[i] != 1 ) + for (size_t i = 0; i < p.coeffs.size(); i++) + { + if (p.coeffs[i] != 1) { simpleCoeffs = false; break; } + } } if (simpleCoeffs) p.coeffs.clear(); - p.activ = activ; + p.activ = self.activ.get(); parallel_for_(Range(0, nstripes), p, nstripes); } void operator()(const Range& r) const CV_OVERRIDE { + const EltwiseOp op = self.op; size_t total = dst->size[0]*planeSize; size_t stripeSize = (total + nstripes - 1)/nstripes; size_t stripeStart = r.start*stripeSize; size_t stripeEnd = std::min(r.end*stripeSize, total); - int c, j, k, n; const float* coeffsptr = !coeffs.empty() ? &coeffs[0] : 0; float* dstptr0 = dst->ptr(); - int blockSize0 = 1 << 12, blockSize; + int blockSize0 = 1 << 12; - for( size_t ofs = stripeStart; ofs < stripeEnd; ofs += blockSize ) + for (size_t ofs = stripeStart; ofs < stripeEnd; ) { int sampleIdx = (int)(ofs / planeSize); int delta = (int)ofs - sampleIdx * planeSize; - blockSize = std::min(blockSize0, std::min((int)(stripeEnd - ofs), (int)planeSize - delta)); + int blockSize = std::min(blockSize0, std::min((int)(stripeEnd - ofs), (int)planeSize - delta)); if( blockSize <= 0 ) break; + ofs += blockSize; - for( c = 0; c < channels; c++ ) + for (int c = 0; c < channels; c++) { - size_t globalDelta = delta + (sampleIdx*channels + c)*planeSize; - const float* srcptr0 = srcs[0]->ptr() + globalDelta; - float* dstptr = dstptr0 + globalDelta; + size_t dstIdx = delta + (sampleIdx*channels + c)*planeSize; + float* dstptr = dstptr0 + dstIdx; - // This code assumes that srcs are sorted in descending order by channels. - for (n = 1; n < nsrcs && c < srcs[n]->size[1]; ++n) {} - - if (n == 1) + // process first two inputs { - if( !coeffsptr ) + const float* srcptr0 = srcs[0]->ptr() + dstIdx; + + const int inputIdx = 1; + int src1_channels = srcNumChannels[inputIdx]; + if (c >= src1_channels) { - for( j = 0; j < blockSize; j++ ) + // no data from second input + if (!coeffsptr || coeffsptr[0] == 1.0f) { - dstptr[j] = srcptr0[j]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = srcptr0[j]; + } + } + else + { + float c0 = coeffsptr[0]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = c0*srcptr0[j]; + } } } else { - float c0 = coeffsptr[0]; - for( j = 0; j < blockSize; j++ ) + size_t srcIdx = delta + (sampleIdx * src1_channels + c) * planeSize; + const float* srcptrI = srcs[inputIdx]->ptr() + srcIdx; + + if (op == PROD) { - dstptr[j] = c0*srcptr0[j]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = srcptr0[j] * srcptrI[j]; + } } + else if (op == DIV) + { + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = srcptr0[j] / srcptrI[j]; + } + } + else if (op == MAX) + { + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = std::max(srcptr0[j], srcptrI[j]); + } + } + else if (op == SUM) + { + if (!coeffsptr || (coeffsptr[0] == 1.0f && coeffsptr[1] == 1.0f)) + { + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = srcptr0[j] + srcptrI[j]; + } + } + else + { + float c0 = coeffsptr[0]; + float c1 = coeffsptr[1]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] = c0*srcptr0[j] + c1*srcptrI[j]; + } + } + } + else + CV_Error(Error::StsInternal, ""); } } - else if( op == PROD ) + + // aggregate other inputs (3+) + for (size_t inputIdx = 2; inputIdx < nsrcs; inputIdx++) { - for( k = 1; k < n; k++ ) + int srcI_channels = srcNumChannels[inputIdx]; + if (c >= srcI_channels) + continue; // no data from second input + size_t srcIdx = delta + (sampleIdx * srcI_channels + c) * planeSize; + const float* srcptrI = srcs[inputIdx]->ptr() + srcIdx; + + if (op == PROD) { - const float* srcptr1 = srcs[k]->ptr() + globalDelta; - for( j = 0; j < blockSize; j++ ) + for (int j = 0; j < blockSize; j++) { - dstptr[j] = srcptr0[j]*srcptr1[j]; + dstptr[j] *= srcptrI[j]; } - srcptr0 = (const float*)dstptr; } - } - else if( op == DIV ) - { - for( k = 1; k < n; k++ ) + else if (op == DIV) { - const float* srcptr1 = srcs[k]->ptr() + globalDelta; - for( j = 0; j < blockSize; j++ ) + for (int j = 0; j < blockSize; j++) { - dstptr[j] = srcptr0[j]/srcptr1[j]; + dstptr[j] /= srcptrI[j]; } - srcptr0 = (const float*)dstptr; } - } - else if( op == MAX ) - { - for( k = 1; k < n; k++ ) + else if (op == MAX) { - const float* srcptr1 = srcs[k]->ptr() + globalDelta; - for( j = 0; j < blockSize; j++ ) + for (int j = 0; j < blockSize; j++) { - dstptr[j] = std::max(srcptr0[j], srcptr1[j]); + dstptr[j] = std::max(dstptr[j], srcptrI[j]); } - srcptr0 = (const float*)dstptr; } - } - else if( !coeffsptr ) - { - for( k = 1; k < n; k++ ) + else if (op == SUM) { - const float* srcptr1 = srcs[k]->ptr() + globalDelta; - for( j = 0; j < blockSize; j++ ) + if (!coeffsptr || coeffsptr[inputIdx] == 1.0f) { - dstptr[j] = srcptr0[j] + srcptr1[j]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] += srcptrI[j]; + } } - srcptr0 = (const float*)dstptr; - } - } - else - { - float c0 = coeffsptr[0]; - for( k = 1; k < n; k++ ) - { - const float* srcptr1 = srcs[k]->ptr() + globalDelta; - float c1 = coeffsptr[k]; - for( j = 0; j < blockSize; j++ ) + else { - dstptr[j] = c0*srcptr0[j] + c1*srcptr1[j]; + float cI = coeffsptr[inputIdx]; + for (int j = 0; j < blockSize; j++) + { + dstptr[j] += cI * srcptrI[j]; + } } - srcptr0 = (const float*)dstptr; - c0 = 1; } + else + CV_Error(Error::StsInternal, ""); } } @@ -343,7 +499,7 @@ public: std::vector inputs; std::vector outputs; - if ((inputs_.depth() == CV_16S && op != SUM) || variableChannels) + if ((inputs_.depth() == CV_16S && op != SUM) || (channelsMode != ELTWISE_CHANNNELS_SAME)) return false; inputs_.getUMatVector(inputs); @@ -446,8 +602,9 @@ public: CV_Assert(outputs.size() == 1); const int nstripes = getNumThreads(); - EltwiseInvoker::run(&inputs[0], (int)inputs.size(), outputs[0], - coeffs, op, activ.get(), nstripes); + EltwiseInvoker::run(*this, + &inputs[0], (int)inputs.size(), outputs[0], + nstripes); } virtual Ptr initHalide(const std::vector > &input) CV_OVERRIDE @@ -558,6 +715,7 @@ public: CV_UNUSED(outputs); // suppress unused variable warning CV_Assert(inputs.size()); + // FIXIT: handle inputs with different number of channels long flops = inputs.size() * total(inputs[0]); return flops; diff --git a/modules/dnn/test/test_darknet_importer.cpp b/modules/dnn/test/test_darknet_importer.cpp index fecc5aaa8e..eced69555e 100644 --- a/modules/dnn/test/test_darknet_importer.cpp +++ b/modules/dnn/test/test_darknet_importer.cpp @@ -99,6 +99,7 @@ class Test_Darknet_layers : public DNNTestLayer public: void testDarknetLayer(const std::string& name, bool hasWeights = false) { + SCOPED_TRACE(name); Mat inp = blobFromNPY(findDataFile("dnn/darknet/" + name + "_in.npy")); Mat ref = blobFromNPY(findDataFile("dnn/darknet/" + name + "_out.npy")); @@ -115,6 +116,47 @@ public: net.setInput(inp); Mat out = net.forward(); normAssert(out, ref, "", default_l1, default_lInf); + + if (inp.size[0] == 1) // test handling of batch size + { + SCOPED_TRACE("batch size 2"); + +#if defined(INF_ENGINE_RELEASE) + if (target == DNN_TARGET_MYRIAD && name == "shortcut") + applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_MYRIAD); +#endif + + std::vector sz2 = shape(inp); + sz2[0] = 2; + + Net net2 = readNet(cfg, model); + net2.setPreferableBackend(backend); + net2.setPreferableTarget(target); + Range ranges0[4] = { Range(0, 1), Range::all(), Range::all(), Range::all() }; + Range ranges1[4] = { Range(1, 2), Range::all(), Range::all(), Range::all() }; + Mat inp2(sz2, inp.type(), Scalar::all(0)); + inp.copyTo(inp2(ranges0)); + inp.copyTo(inp2(ranges1)); + net2.setInput(inp2); + Mat out2 = net2.forward(); + EXPECT_EQ(0, cv::norm(out2(ranges0), out2(ranges1), NORM_INF)) << "Batch result is not equal: " << name; + + Mat ref2 = ref; + if (ref.dims == 2 && out2.dims == 3) + { + int ref_3d_sizes[3] = {1, ref.rows, ref.cols}; + ref2 = Mat(3, ref_3d_sizes, ref.type(), (void*)ref.data); + } + /*else if (ref.dims == 3 && out2.dims == 4) + { + int ref_4d_sizes[4] = {1, ref.size[0], ref.size[1], ref.size[2]}; + ref2 = Mat(4, ref_4d_sizes, ref.type(), (void*)ref.data); + }*/ + ASSERT_EQ(out2.dims, ref2.dims) << ref.dims; + + normAssert(out2(ranges0), ref2, "", default_l1, default_lInf); + normAssert(out2(ranges1), ref2, "", default_l1, default_lInf); + } } }; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 7c5c491fad..f9ff4ed883 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1582,30 +1582,28 @@ TEST(Layer_Test_Convolution, relu_fusion) } typedef testing::TestWithParam > > Layer_Test_Eltwise_unequal; -TEST_P(Layer_Test_Eltwise_unequal, Accuracy) +TEST_P(Layer_Test_Eltwise_unequal, accuracy_input_0_truncate) { bool weighted = get<0>(GetParam()); int backendId = get<0>(get<1>(GetParam())); int targetId = get<1>(get<1>(GetParam())); - if (backendId == DNN_BACKEND_OPENCV && targetId == DNN_TARGET_OPENCL_FP16) - applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16); - Net net; LayerParams lp; lp.type = "Eltwise"; lp.name = "testLayer"; + lp.set("output_channels_mode", "input_0_truncate"); const int inpShapes[][4] = {{1, 4, 2, 2}, {1, 5, 2, 2}, {1, 3, 2, 2}}; + const int out_channels = inpShapes[0][1]; std::vector inpNames(3); std::vector inputs(3); - size_t numOutValues = 1*4*2*2; // By the first input std::vector weights(3, 1); if (weighted) { for (int i = 0; i < inputs.size(); ++i) - randu(Mat(1, 1, CV_32F, &weights[i]), -1, 1); + weights[i] = -0.125f + i * 0.25f; lp.set("coeff", DictValue::arrayReal(&weights[0], weights.size())); } @@ -1613,27 +1611,103 @@ TEST_P(Layer_Test_Eltwise_unequal, Accuracy) for (int i = 0; i < inputs.size(); ++i) { inputs[i].create(4, inpShapes[i], CV_32F); - randu(inputs[i], 0, 255); + size_t total = inputs[i].total(); + for (size_t j = 0; j < total; j++) + inputs[i].ptr()[j] = j + i * 100; inpNames[i] = format("input_%d", i); net.connect(0, i, eltwiseId, i); } - Mat ref(1, numOutValues, CV_32F, Scalar(0)); + Mat ref(4, inpShapes[0], CV_32F, Scalar(0)); net.setInputsNames(inpNames); for (int i = 0; i < inputs.size(); ++i) { + //std::cout << ref.reshape(1,1) << endl; net.setInput(inputs[i], inpNames[i]); - if (numOutValues >= inputs[i].total()) - ref.colRange(0, inputs[i].total()) += weights[i] * inputs[i].reshape(1, 1); - else - ref += weights[i] * inputs[i].reshape(1, 1).colRange(0, numOutValues); + for (size_t batchId = 0; batchId < ref.size[0]; batchId++) + { + int input_channels = inputs[i].size[1]; + Range ranges[4] = { Range(batchId, batchId + 1), Range(0, std::min(out_channels, input_channels)), Range::all(), Range::all() }; + Mat ref_slice = ref(ranges); + Mat input_slice = inputs[i](ranges); + ref_slice += weights[i] * input_slice; + } } net.setPreferableBackend(backendId); net.setPreferableTarget(targetId); Mat out = net.forward(); - normAssert(out.reshape(1, 1), ref); + normAssert(out, ref); + if (testing::Test::HasFailure()) + { + std::cout << out.reshape(1,1) << endl; + std::cout << ref.reshape(1,1) << endl; + } } + +TEST_P(Layer_Test_Eltwise_unequal, accuracy_input_0) +{ + bool weighted = get<0>(GetParam()); + int backendId = get<0>(get<1>(GetParam())); + int targetId = get<1>(get<1>(GetParam())); + + Net net; + LayerParams lp; + lp.type = "Eltwise"; + lp.name = "testLayer"; + lp.set("output_channels_mode", "input_0"); + + const int inpShapes[][4] = {{1, 4, 2, 2}, {1, 2, 2, 2}, {1, 3, 2, 2}}; + const int out_channels = inpShapes[0][1]; + std::vector inpNames(3); + std::vector inputs(3); + + std::vector weights(3, 1); + if (weighted) + { + for (int i = 0; i < inputs.size(); ++i) + weights[i] = -0.125f + i * 0.25f; + lp.set("coeff", DictValue::arrayReal(&weights[0], weights.size())); + } + + int eltwiseId = net.addLayer(lp.name, lp.type, lp); + for (int i = 0; i < inputs.size(); ++i) + { + inputs[i].create(4, inpShapes[i], CV_32F); + size_t total = inputs[i].total(); + for (size_t j = 0; j < total; j++) + inputs[i].ptr()[j] = j + i * 100; + inpNames[i] = format("input_%d", i); + net.connect(0, i, eltwiseId, i); + } + Mat ref(4, inpShapes[0], CV_32F, Scalar(0)); + + net.setInputsNames(inpNames); + for (int i = 0; i < inputs.size(); ++i) + { + //std::cout << ref.reshape(1,1) << endl; + net.setInput(inputs[i], inpNames[i]); + for (size_t batchId = 0; batchId < ref.size[0]; batchId++) + { + int input_channels = inputs[i].size[1]; + Range ranges[4] = { Range(batchId, batchId + 1), Range(0, std::min(out_channels, input_channels)), Range::all(), Range::all() }; + Mat ref_slice = ref(ranges); + Mat input_slice = inputs[i](ranges); + ref_slice += weights[i] * input_slice; + } + } + + net.setPreferableBackend(backendId); + net.setPreferableTarget(targetId); + Mat out = net.forward(); + normAssert(out, ref); + if (testing::Test::HasFailure()) + { + std::cout << out.reshape(1,1) << endl; + std::cout << ref.reshape(1,1) << endl; + } +} + INSTANTIATE_TEST_CASE_P(/**/, Layer_Test_Eltwise_unequal, Combine( testing::Bool(), dnnBackendsAndTargets() diff --git a/modules/ts/src/ts_func.cpp b/modules/ts/src/ts_func.cpp index 928411d996..6f1c389307 100644 --- a/modules/ts/src/ts_func.cpp +++ b/modules/ts/src/ts_func.cpp @@ -1368,7 +1368,8 @@ double norm(InputArray _src1, InputArray _src2, int normType, InputArray _mask) int normType0 = normType; normType = normType == NORM_L2SQR ? NORM_L2 : normType; - CV_Assert( src1.type() == src2.type() && src1.size == src2.size ); + CV_CheckTypeEQ(src1.type(), src2.type(), ""); + CV_Assert(src1.size == src2.size); CV_Assert( mask.empty() || (src1.size == mask.size && mask.type() == CV_8U) ); CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); const Mat *arrays[]={&src1, &src2, &mask, 0}; From 1c4a64f0a1ce410ef08273d703aa640980cb05fa Mon Sep 17 00:00:00 2001 From: Paul Murphy <12972156+pmur@users.noreply.github.com> Date: Thu, 12 Dec 2019 04:00:44 -0600 Subject: [PATCH 26/26] Merge pull request #16138 from pmur:reg_16137 * imgproc: Prevent 1B overrun of 8C3 SIMD optimization The fourth value read via v_load_q is essentially ignored, but can cause trouble if it happens to cross page boundaries. The final few iterations may attempt to read the most extreme elements of S, which will read 1B beyond the array in most aligment cases. Dynamically compute the stop. This could be hoised from the loop, but will require a more extensive change. Likewise, cleanup the iteration increment statements to make it more obvious they do channel count (3) elements per pass. This should resolve #16137 * imgproc(resize): extra check --- modules/imgproc/src/resize.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/imgproc/src/resize.cpp b/modules/imgproc/src/resize.cpp index 56f06ac4b7..df127d1c43 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1547,7 +1547,7 @@ struct HResizeLinearVec_X4 struct HResizeLinearVecU8_X4 { int operator()(const uchar** src, uchar** _dst, int count, const int* xofs, - const uchar* _alpha, int, int, int cn, int, int xmax) const + const uchar* _alpha, int smax, int, int cn, int, int xmax) const { const short *alpha = (const short*)_alpha; int **dst = (int**)_dst; @@ -1633,8 +1633,18 @@ struct HResizeLinearVecU8_X4 } else if(cn == 3) { - const int step = 4; - const int len0 = xmax - step; + int len0 = xmax - cn; + + /* This may need to trim 1 or more extra units depending on the amount of + scaling. Test until we find the first value which we know cannot overrun. */ + while (len0 >= cn && + xofs[len0 - cn] + cn >= smax - cn // check access: v_load_expand_q(S+xofs[dx]+cn) + ) + { + len0 -= cn; + } + CV_DbgAssert(len0 <= 0 || len0 >= cn); + for( ; k <= (count - 2); k+=2 ) { const uchar *S0 = src[k]; @@ -1642,7 +1652,7 @@ struct HResizeLinearVecU8_X4 const uchar *S1 = src[k+1]; int *D1 = dst[k+1]; - for( dx = 0; dx < len0; dx += 3*step/4 ) + for( dx = 0; dx < len0; dx += cn ) { v_int16x8 a = v_load(alpha+dx*2); v_store(&D0[dx], v_dotprod(v_reinterpret_as_s16(v_load_expand_q(S0+xofs[dx]) | (v_load_expand_q(S0+xofs[dx]+cn)<<16)), a)); @@ -1653,7 +1663,7 @@ struct HResizeLinearVecU8_X4 { const uchar *S = src[k]; int *D = dst[k]; - for( dx = 0; dx < len0; dx += 3*step/4 ) + for( dx = 0; dx < len0; dx += cn ) { v_int16x8 a = v_load(alpha+dx*2); v_store(&D[dx], v_dotprod(v_reinterpret_as_s16(v_load_expand_q(S+xofs[dx]) | (v_load_expand_q(S+xofs[dx]+cn)<<16)), a));