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, 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() diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 42e1fe5164..792f59e7e8 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)) @@ -118,9 +122,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 @@ -641,7 +647,11 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType) # 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 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/core/src/parallel_impl.cpp b/modules/core/src/parallel_impl.cpp index cf73619425..e732830d22 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) @@ -264,6 +266,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 b415bde4c3..104a8c7c9b 100644 --- a/modules/core/src/system.cpp +++ b/modules/core/src/system.cpp @@ -1815,6 +1815,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: @@ -1823,7 +1832,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 } }; diff --git a/modules/dnn/CMakeLists.txt b/modules/dnn/CMakeLists.txt index f2bc91021d..3fa7fd69c3 100644 --- a/modules/dnn/CMakeLists.txt +++ b/modules/dnn/CMakeLists.txt @@ -64,6 +64,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 b63c966b40..4dcca623bf 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_INLINE_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/misc/python/test/test_dnn.py b/modules/dnn/misc/python/test/test_dnn.py index 1b37bc431b..2d757b87d4 100644 --- a/modules/dnn/misc/python/test/test_dnn.py +++ b/modules/dnn/misc/python/test/test_dnn.py @@ -275,6 +275,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() 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/dnn.cpp b/modules/dnn/src/dnn.cpp index 8a01efc0c0..2de02732d4 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -3040,8 +3040,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); diff --git a/modules/dnn/src/layers/convolution_layer.cpp b/modules/dnn/src/layers/convolution_layer.cpp index 5019f27d85..632165ae4a 100644 --- a/modules/dnn/src/layers/convolution_layer.cpp +++ b/modules/dnn/src/layers/convolution_layer.cpp @@ -634,6 +634,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) { @@ -643,10 +649,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); } } @@ -656,14 +662,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( @@ -2254,41 +2258,33 @@ 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}; 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); diff --git a/modules/dnn/src/layers/detection_output_layer.cpp b/modules/dnn/src/layers/detection_output_layer.cpp index f3ad73bf6b..1730be7f1f 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 diff --git a/modules/dnn/src/layers/elementwise_layers.cpp b/modules/dnn/src/layers/elementwise_layers.cpp index 15312995f2..fa34993843 100644 --- a/modules/dnn/src/layers/elementwise_layers.cpp +++ b/modules/dnn/src/layers/elementwise_layers.cpp @@ -659,7 +659,7 @@ struct SwishFunctor { return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - 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 @@ -727,7 +727,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 @@ -755,7 +756,7 @@ struct MishFunctor { return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || - 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 @@ -823,7 +824,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 diff --git a/modules/dnn/src/layers/eltwise_layer.cpp b/modules/dnn/src/layers/eltwise_layer.cpp index 52d1849cef..6925863773 100644 --- a/modules/dnn/src/layers/eltwise_layer.cpp +++ b/modules/dnn/src/layers/eltwise_layer.cpp @@ -72,9 +72,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; @@ -103,6 +122,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 @@ -111,7 +159,7 @@ public: backendId == DNN_BACKEND_CUDA || (backendId == DNN_BACKEND_HALIDE && op != DIV) || // TODO: not implemented, see PR #15811 ((((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, @@ -126,212 +174,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, ""); } } @@ -350,7 +506,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); @@ -453,8 +609,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); } #ifdef HAVE_CUDA @@ -588,6 +745,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/src/layers/prior_box_layer.cpp b/modules/dnn/src/layers/prior_box_layer.cpp index 029e5bee7d..aaedd24b0a 100644 --- a/modules/dnn/src/layers/prior_box_layer.cpp +++ b/modules/dnn/src/layers/prior_box_layer.cpp @@ -630,8 +630,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 9cb47c2e45..8d035fbeb8 100644 --- a/modules/dnn/src/layers/slice_layer.cpp +++ b/modules/dnn/src/layers/slice_layer.cpp @@ -370,8 +370,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 diff --git a/modules/dnn/src/op_inf_engine.cpp b/modules/dnn/src/op_inf_engine.cpp index 19d74baa43..2f8ec41843 100644 --- a/modules/dnn/src/op_inf_engine.cpp +++ b/modules/dnn/src/op_inf_engine.cpp @@ -751,11 +751,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) 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 61b1ad0444..e4ac578033 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -1587,30 +1587,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())); } @@ -1618,27 +1616,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/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 3045215717..e5af81a060 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1113,11 +1113,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, 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/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/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: diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index bb0b2e6e80..35923b1e47 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/src/resize.cpp b/modules/imgproc/src/resize.cpp index b0283e5ca4..df127d1c43 100644 --- a/modules/imgproc/src/resize.cpp +++ b/modules/imgproc/src/resize.cpp @@ -1481,10 +1481,330 @@ 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 smax, 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) + { + 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]; + int *D0 = dst[k]; + const uchar *S1 = src[k+1]; + int *D1 = dst[k+1]; + + 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)); + 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 += 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)); + } + } + } + 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 +1825,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 +1849,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]; diff --git a/modules/imgproc/src/thresh.cpp b/modules/imgproc/src/thresh.cpp index 6574f557fa..1fca78026a 100644 --- a/modules/imgproc/src/thresh.cpp +++ b/modules/imgproc/src/thresh.cpp @@ -757,6 +757,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)) { @@ -765,6 +766,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_approxpoly.cpp b/modules/imgproc/test/test_approxpoly.cpp index 7a3df9ff0f..77be6bbf39 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 diff --git a/modules/imgproc/test/test_histograms.cpp b/modules/imgproc/test/test_histograms.cpp index f2955d137c..b21acd5f03 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 */ 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 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: diff --git a/modules/python/package/setup.py b/modules/python/package/setup.py index a9097cc39b..996709a42d 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/modules/ts/src/ts_func.cpp b/modules/ts/src/ts_func.cpp index 257c50cbb1..c35755043a 100644 --- a/modules/ts/src/ts_func.cpp +++ b/modules/ts/src/ts_func.cpp @@ -1383,7 +1383,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}; diff --git a/samples/_winpack_run_python_sample.cmd b/samples/_winpack_run_python_sample.cmd index b7075e322a..9bec8ef7e2 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