From fe625a558e0c9ab56f781c47bb0db25079368681 Mon Sep 17 00:00:00 2001 From: Smirnov Egor Date: Wed, 18 Aug 2021 18:37:35 +0300 Subject: [PATCH 1/7] fix hasDynamicShapes for batch_size and fix axis selection in Scale layer --- modules/dnn/src/onnx/onnx_importer.cpp | 13 ++++++++++++- modules/dnn/test/test_onnx_importer.cpp | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 32b56278bd..dd84e3b7cd 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -475,7 +475,8 @@ void ONNXImporter::populateNet() for (int j = 0; j < inpShape.size(); ++j) { inpShape[j] = tensorShape.dim(j).dim_value(); - if (!tensorShape.dim(j).dim_param().empty()) + // NHW, NCHW(NHWC), NCDHW(NDHWC); do not set this flag if only N is dynamic + if (!tensorShape.dim(j).dim_param().empty() && !(j == 0 && inpShape.size() >= 3)) hasDynamicShapes = true; } if (!inpShape.empty() && !hasDynamicShapes) @@ -1407,6 +1408,16 @@ void ONNXImporter::parseMul(LayerParams& layerParams, const opencv_onnx::NodePro //Replace input to Power node_proto.set_input(1, powerParams.name); } + + const MatShape& broadShape = outShapes[node_proto.input(1)]; + const size_t outShapeSize = outShapes[node_proto.input(0)].size(); + const size_t diff = outShapeSize - broadShape.size(); + + size_t axis; + for (axis = diff; axis < broadShape.size() && broadShape[axis - diff] == 1; ++axis) {} + + CV_Assert(axis != outShapeSize); + layerParams.set("axis", static_cast(axis)); layerParams.type = "Scale"; } addLayer(layerParams, node_proto); diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index a446a37c79..d13b39064e 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -284,6 +284,7 @@ TEST_P(Test_ONNX_layers, Scale) if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER); testONNXModels("scale"); + testONNXModels("scale_broadcast", npy, 0, 0, false, true, 3); } TEST_P(Test_ONNX_layers, ReduceMean3D) @@ -825,6 +826,7 @@ TEST_P(Test_ONNX_layers, DynamicAxes) testONNXModels("resize_opset11_torch1.6_dynamic_axes"); testONNXModels("average_pooling_dynamic_axes"); testONNXModels("maxpooling_sigmoid_dynamic_axes"); + testONNXModels("dynamic_batch"); } TEST_P(Test_ONNX_layers, MaxPool1d) From 3995deaf76d0c988d315630ace46eb01dba37174 Mon Sep 17 00:00:00 2001 From: Dale Phurrough Date: Sat, 28 Aug 2021 01:12:44 +0200 Subject: [PATCH 2/7] fix opencv/opencv#20544 nodiscard for msvc/gcc - includes workaround for preprocessor non-compliance - enable attribute syntax checking in msvc --- cmake/OpenCVCompilerOptions.cmake | 3 ++ modules/core/include/opencv2/core/cvdef.h | 39 ++++++++++++-- modules/core/include/opencv2/core/mat.hpp | 62 +++++++++++----------- modules/core/include/opencv2/core/matx.hpp | 14 ++--- 4 files changed, 77 insertions(+), 41 deletions(-) diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index 303d4f451e..d444085ea3 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -400,6 +400,9 @@ if(MSVC) endif() endif() + # Enable [[attribute]] syntax checking to prevent silent failure: "attribute is ignored in this syntactic position" + add_extra_compiler_option("/w15240") + if(NOT ENABLE_NOISY_WARNINGS) ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4127) # conditional expression is constant ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4251) # class 'std::XXX' needs to have dll-interface to be used by clients of YYY diff --git a/modules/core/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index a4b560b4e1..6011b2a931 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -575,14 +575,47 @@ Cv64suf; # endif #endif +/****************************************************************************************\ +* CV_NODISCARD_STD attribute (C++17) * +* encourages the compiler to issue a warning if the return value is discarded * +\****************************************************************************************/ +#ifndef CV_NODISCARD_STD +# ifndef __has_cpp_attribute +// workaround preprocessor non-compliance https://reviews.llvm.org/D57851 +# define __has_cpp_attribute(__x) 0 +# endif +# if __has_cpp_attribute(nodiscard) +# define CV_NODISCARD_STD [[nodiscard]] +# elif __cplusplus >= 201703L +// available when compiler is C++17 compliant +# define CV_NODISCARD_STD [[nodiscard]] +# elif defined(_MSC_VER) && _MSC_VER >= 1911 && _MSVC_LANG >= 201703L +// available with VS2017 v15.3+ with /std:c++17 or higher; works on functions and classes +# define CV_NODISCARD_STD [[nodiscard]] +# elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 700) && (__cplusplus >= 201103L) +// available with GCC 7.0+; works on functions, works or silently fails on classes +# define CV_NODISCARD_STD [[nodiscard]] +# elif defined(__GNUC__) && (((__GNUC__ * 100) + __GNUC_MINOR__) >= 408) && (__cplusplus >= 201103L) +// available with GCC 4.8+ but it usually does nothing and can fail noisily -- therefore not used +// define CV_NODISCARD_STD [[gnu::warn_unused_result]] +# endif +#endif +#ifndef CV_NODISCARD_STD +# define CV_NODISCARD_STD /* nothing by default */ +#endif + /****************************************************************************************\ -* CV_NODISCARD attribute * -* encourages the compiler to issue a warning if the return value is discarded (C++17) * +* CV_NODISCARD attribute (deprecated, GCC only) * +* DONT USE: use instead the standard CV_NODISCARD_STD macro above * +* this legacy method silently fails to issue warning until some version * +* after gcc 6.3.0. Yet with gcc 7+ you can use the above standard method * +* which makes this method useless. Don't use it. * +* @deprecated use instead CV_NODISCARD_STD * \****************************************************************************************/ #ifndef CV_NODISCARD # if defined(__GNUC__) -# define CV_NODISCARD __attribute__((__warn_unused_result__)) // at least available with GCC 3.4 +# define CV_NODISCARD __attribute__((__warn_unused_result__)) # elif defined(__clang__) && defined(__has_attribute) # if __has_attribute(__warn_unused_result__) # define CV_NODISCARD __attribute__((__warn_unused_result__)) diff --git a/modules/core/include/opencv2/core/mat.hpp b/modules/core/include/opencv2/core/mat.hpp index d0ce61e123..fdcb8fc817 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1204,14 +1204,14 @@ public: The method creates a square diagonal matrix from specified main diagonal. @param d One-dimensional matrix that represents the main diagonal. */ - static Mat diag(const Mat& d); + CV_NODISCARD_STD static Mat diag(const Mat& d); /** @brief Creates a full copy of the array and the underlying data. The method creates a full copy of the array. The original step[] is not taken into account. So, the array copy is a continuous array occupying total()*elemSize() bytes. */ - Mat clone() const CV_NODISCARD; + CV_NODISCARD_STD Mat clone() const; /** @brief Copies the matrix to another one. @@ -1375,20 +1375,20 @@ public: @param cols Number of columns. @param type Created matrix type. */ - static MatExpr zeros(int rows, int cols, int type); + CV_NODISCARD_STD static MatExpr zeros(int rows, int cols, int type); /** @overload @param size Alternative to the matrix size specification Size(cols, rows) . @param type Created matrix type. */ - static MatExpr zeros(Size size, int type); + CV_NODISCARD_STD static MatExpr zeros(Size size, int type); /** @overload @param ndims Array dimensionality. @param sz Array of integers specifying the array shape. @param type Created matrix type. */ - static MatExpr zeros(int ndims, const int* sz, int type); + CV_NODISCARD_STD static MatExpr zeros(int ndims, const int* sz, int type); /** @brief Returns an array of all 1's of the specified size and type. @@ -1406,20 +1406,20 @@ public: @param cols Number of columns. @param type Created matrix type. */ - static MatExpr ones(int rows, int cols, int type); + CV_NODISCARD_STD static MatExpr ones(int rows, int cols, int type); /** @overload @param size Alternative to the matrix size specification Size(cols, rows) . @param type Created matrix type. */ - static MatExpr ones(Size size, int type); + CV_NODISCARD_STD static MatExpr ones(Size size, int type); /** @overload @param ndims Array dimensionality. @param sz Array of integers specifying the array shape. @param type Created matrix type. */ - static MatExpr ones(int ndims, const int* sz, int type); + CV_NODISCARD_STD static MatExpr ones(int ndims, const int* sz, int type); /** @brief Returns an identity matrix of the specified size and type. @@ -1435,13 +1435,13 @@ public: @param cols Number of columns. @param type Created matrix type. */ - static MatExpr eye(int rows, int cols, int type); + CV_NODISCARD_STD static MatExpr eye(int rows, int cols, int type); /** @overload @param size Alternative matrix size specification as Size(cols, rows) . @param type Created matrix type. */ - static MatExpr eye(Size size, int type); + CV_NODISCARD_STD static MatExpr eye(Size size, int type); /** @brief Allocates new array data if needed. @@ -2302,7 +2302,7 @@ public: Mat_ row(int y) const; Mat_ col(int x) const; Mat_ diag(int d=0) const; - Mat_ clone() const CV_NODISCARD; + CV_NODISCARD_STD Mat_ clone() const; //! overridden forms of Mat::elemSize() etc. size_t elemSize() const; @@ -2315,14 +2315,14 @@ public: size_t stepT(int i=0) const; //! overridden forms of Mat::zeros() etc. Data type is omitted, of course - static MatExpr zeros(int rows, int cols); - static MatExpr zeros(Size size); - static MatExpr zeros(int _ndims, const int* _sizes); - static MatExpr ones(int rows, int cols); - static MatExpr ones(Size size); - static MatExpr ones(int _ndims, const int* _sizes); - static MatExpr eye(int rows, int cols); - static MatExpr eye(Size size); + CV_NODISCARD_STD static MatExpr zeros(int rows, int cols); + CV_NODISCARD_STD static MatExpr zeros(Size size); + CV_NODISCARD_STD static MatExpr zeros(int _ndims, const int* _sizes); + CV_NODISCARD_STD static MatExpr ones(int rows, int cols); + CV_NODISCARD_STD static MatExpr ones(Size size); + CV_NODISCARD_STD static MatExpr ones(int _ndims, const int* _sizes); + CV_NODISCARD_STD static MatExpr eye(int rows, int cols); + CV_NODISCARD_STD static MatExpr eye(Size size); //! some more overridden methods Mat_& adjustROI( int dtop, int dbottom, int dleft, int dright ); @@ -2469,10 +2469,10 @@ public: //! <0 - a diagonal from the lower half) UMat diag(int d=0) const; //! constructs a square diagonal matrix which main diagonal is vector "d" - static UMat diag(const UMat& d); + CV_NODISCARD_STD static UMat diag(const UMat& d); //! returns deep copy of the matrix, i.e. the data is copied - UMat clone() const CV_NODISCARD; + CV_NODISCARD_STD UMat clone() const; //! copies the matrix content to "m". // It calls m.create(this->size(), this->type()). void copyTo( OutputArray m ) const; @@ -2503,14 +2503,14 @@ public: double dot(InputArray m) const; //! Matlab-style matrix initialization - static UMat zeros(int rows, int cols, int type); - static UMat zeros(Size size, int type); - static UMat zeros(int ndims, const int* sz, int type); - static UMat ones(int rows, int cols, int type); - static UMat ones(Size size, int type); - static UMat ones(int ndims, const int* sz, int type); - static UMat eye(int rows, int cols, int type); - static UMat eye(Size size, int type); + CV_NODISCARD_STD static UMat zeros(int rows, int cols, int type); + CV_NODISCARD_STD static UMat zeros(Size size, int type); + CV_NODISCARD_STD static UMat zeros(int ndims, const int* sz, int type); + CV_NODISCARD_STD static UMat ones(int rows, int cols, int type); + CV_NODISCARD_STD static UMat ones(Size size, int type); + CV_NODISCARD_STD static UMat ones(int ndims, const int* sz, int type); + CV_NODISCARD_STD static UMat eye(int rows, int cols, int type); + CV_NODISCARD_STD static UMat eye(Size size, int type); //! allocates new matrix data unless the matrix already has specified size and type. // previous data is unreferenced if needed. @@ -2767,7 +2767,7 @@ public: SparseMat& operator = (const Mat& m); //! creates full copy of the matrix - SparseMat clone() const CV_NODISCARD; + CV_NODISCARD_STD SparseMat clone() const; //! copies all the data to the destination matrix. All the previous content of m is erased void copyTo( SparseMat& m ) const; @@ -3004,7 +3004,7 @@ public: SparseMat_& operator = (const Mat& m); //! makes full copy of the matrix. All the elements are duplicated - SparseMat_ clone() const CV_NODISCARD; + CV_NODISCARD_STD SparseMat_ clone() const; //! equivalent to cv::SparseMat::create(dims, _sizes, DataType<_Tp>::type) void create(int dims, const int* _sizes); //! converts sparse matrix to the old-style CvSparseMat. All the elements are copied diff --git a/modules/core/include/opencv2/core/matx.hpp b/modules/core/include/opencv2/core/matx.hpp index 733f675192..f25c8bce57 100644 --- a/modules/core/include/opencv2/core/matx.hpp +++ b/modules/core/include/opencv2/core/matx.hpp @@ -146,22 +146,22 @@ public: Matx(std::initializer_list<_Tp>); //!< initialize from an initializer list #endif - static Matx all(_Tp alpha); - static Matx zeros(); - static Matx ones(); - static Matx eye(); - static Matx diag(const diag_type& d); + CV_NODISCARD_STD static Matx all(_Tp alpha); + CV_NODISCARD_STD static Matx zeros(); + CV_NODISCARD_STD static Matx ones(); + CV_NODISCARD_STD static Matx eye(); + CV_NODISCARD_STD static Matx diag(const diag_type& d); /** @brief Generates uniformly distributed random numbers @param a Range boundary. @param b The other range boundary (boundaries don't have to be ordered, the lower boundary is inclusive, the upper one is exclusive). */ - static Matx randu(_Tp a, _Tp b); + CV_NODISCARD_STD static Matx randu(_Tp a, _Tp b); /** @brief Generates normally distributed random numbers @param a Mean value. @param b Standard deviation. */ - static Matx randn(_Tp a, _Tp b); + CV_NODISCARD_STD static Matx randn(_Tp a, _Tp b); //! dot product computed with the default precision _Tp dot(const Matx<_Tp, m, n>& v) const; From f25951c412dec1329292136a171bf876b9cb7fbd Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 30 Aug 2021 11:46:14 +0000 Subject: [PATCH 3/7] core(ocl): handle NULL in dumpValue() debug call - NULL is used for allocation of workgroup local variables --- modules/core/src/ocl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index 2d85a32366..0c14e7f3e0 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -2981,6 +2981,8 @@ bool Kernel::empty() const static cv::String dumpValue(size_t sz, const void* p) { + if (!p) + return "NULL"; if (sz == 4) return cv::format("%d / %uu / 0x%08x / %g", *(int*)p, *(int*)p, *(int*)p, *(float*)p); if (sz == 8) From ae6fabc6fe7e372808d3b8aab08398c87074fb7d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Mon, 30 Aug 2021 20:40:14 +0000 Subject: [PATCH 4/7] dnn(ocl): drop CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE check - it is a hint and it should not block kernel execution --- .../src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index ef7c380c1b..517a663e46 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -1437,26 +1437,13 @@ bool OCL4DNNConvSpatial::createGEMMLikeConvKernel(int32_t blockM, ocl::Program program = compileKernel(); if (program.ptr()) { - size_t workgroupSize_used; ocl::Kernel kernel(kernel_name_.c_str(), program); if (kernel.empty()) return false; - workgroupSize_used = kernel.preferedWorkGroupSizeMultiple(); - if (workgroupSize_used != simd_size) - { - std::cerr << "OpenCV(ocl4dnn): The OpenCL compiler chose a simd size (" << workgroupSize_used << ") that " << std::endl; - std::cerr << " does not equal the size (" << simd_size << ") kernel source required." << std::endl; - std::cerr << " Skip this kernel " << kernel_name_ << std::endl; - unloadProgram(kernel_name_); - return false; - } - else - { - kernelQueue.push_back(makePtr(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0], - true, KERNEL_TYPE_GEMM_LIKE)); - return true; - } + kernelQueue.push_back(makePtr(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0], + true, KERNEL_TYPE_GEMM_LIKE)); + return true; } else return false; @@ -1502,26 +1489,13 @@ bool OCL4DNNConvSpatial::createIDLFKernel(int32_t blockWidth, ocl::Program program = compileKernel(); if (program.ptr()) { - size_t workgroupSize_used; ocl::Kernel kernel(kernel_name_.c_str(), program); if (kernel.empty()) return false; - workgroupSize_used = kernel.preferedWorkGroupSizeMultiple(); - if (workgroupSize_used != simd_size) - { - std::cerr << "OpenCV(ocl4dnn): The OpenCL compiler chose a simd size (" << workgroupSize_used << ") that " << std::endl; - std::cerr << " does not equal the size (" << simd_size << ") kernel source required." << std::endl; - std::cerr << " Skip this kernel " << kernel_name_ << std::endl; - unloadProgram(kernel_name_); - return false; - } - else - { - kernelQueue.push_back(makePtr(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0], - true, KERNEL_TYPE_INTEL_IDLF)); - return true; - } + kernelQueue.push_back(makePtr(kernel_name_, &global_size[0], &local_size[0], &workItemOutput[0], + true, KERNEL_TYPE_INTEL_IDLF)); + return true; } else return false; From 16b95145439c509b9adb8600e0ff6dd83845f208 Mon Sep 17 00:00:00 2001 From: Vadim Levin Date: Fri, 27 Aug 2021 15:01:09 +0300 Subject: [PATCH 5/7] feat: update conversion logic for `std::vector` in Python bindings `PyObject*` to `std::vector` conversion logic: - If user passed Numpy Array - If array is planar and T is a primitive type (doesn't require constructor call) that matches with the element type of array, then copy element one by one with the respect of the step between array elements. If compiler is lucky (or brave enough) copy loop can be vectorized. For classes that require constructor calls this path is not possible, because we can't begin an object lifetime without hacks. - Otherwise fall-back to general case - Otherwise - execute the general case: If PyObject* corresponds to Sequence protocol - iterate over the sequence elements and invoke the appropriate `pyopencv_to` function. `std::vector` to `PyObject*` conversion logic: - If `std::vector` is empty - return empty tuple. - If `T` has a corresponding `Mat` `DataType` than return Numpy array instance of the matching `dtype` e.g. `std::vector` is returned as `np.ndarray` of shape `Nx4` and `dtype=int`. This branch helps to optimize further evaluations in user code. - Otherwise - execute the general case: Construct a tuple of length N = `std::vector::size` and insert elements one by one. Unnecessary functions were removed and code was rearranged to allow compiler select the appropriate conversion function specialization. --- .../include/opencv2/core/bindings_utils.hpp | 47 ++ modules/core/src/bindings_utils.cpp | 47 ++ modules/python/src2/cv2.cpp | 493 ++++++++---------- modules/python/test/test_legacy.py | 9 +- modules/python/test/test_misc.py | 103 ++++ 5 files changed, 432 insertions(+), 267 deletions(-) diff --git a/modules/core/include/opencv2/core/bindings_utils.hpp b/modules/core/include/opencv2/core/bindings_utils.hpp index a3f83d9c2c..c53511f88f 100644 --- a/modules/core/include/opencv2/core/bindings_utils.hpp +++ b/modules/core/include/opencv2/core/bindings_utils.hpp @@ -122,6 +122,53 @@ String testReservedKeywordConversion(int positional_argument, int lambda = 2, in return format("arg=%d, lambda=%d, from=%d", positional_argument, lambda, from); } +CV_EXPORTS_W String dumpVectorOfInt(const std::vector& vec); + +CV_EXPORTS_W String dumpVectorOfDouble(const std::vector& vec); + +CV_EXPORTS_W String dumpVectorOfRect(const std::vector& vec); + +CV_WRAP static inline +void generateVectorOfRect(size_t len, CV_OUT std::vector& vec) +{ + vec.resize(len); + if (len > 0) + { + RNG rng(12345); + Mat tmp(static_cast(len), 1, CV_32SC4); + rng.fill(tmp, RNG::UNIFORM, 10, 20); + tmp.copyTo(vec); + } +} + +CV_WRAP static inline +void generateVectorOfInt(size_t len, CV_OUT std::vector& vec) +{ + vec.resize(len); + if (len > 0) + { + RNG rng(554433); + Mat tmp(static_cast(len), 1, CV_32SC1); + rng.fill(tmp, RNG::UNIFORM, -10, 10); + tmp.copyTo(vec); + } +} + +CV_WRAP static inline +void generateVectorOfMat(size_t len, int rows, int cols, int dtype, CV_OUT std::vector& vec) +{ + vec.resize(len); + if (len > 0) + { + RNG rng(65431); + for (size_t i = 0; i < len; ++i) + { + vec[i].create(rows, cols, dtype); + rng.fill(vec[i], RNG::UNIFORM, 0, 10); + } + } +} + CV_WRAP static inline void testRaiseGeneralException() { diff --git a/modules/core/src/bindings_utils.cpp b/modules/core/src/bindings_utils.cpp index 050b7247f8..ea5b82ac7d 100644 --- a/modules/core/src/bindings_utils.cpp +++ b/modules/core/src/bindings_utils.cpp @@ -5,6 +5,7 @@ #include "precomp.hpp" #include "opencv2/core/bindings_utils.hpp" #include +#include namespace cv { namespace utils { @@ -208,4 +209,50 @@ CV_EXPORTS_W String dumpInputOutputArrayOfArrays(InputOutputArrayOfArrays argume return ss.str(); } +static inline std::ostream& operator<<(std::ostream& os, const cv::Rect& rect) +{ + return os << "[x=" << rect.x << ", y=" << rect.y << ", w=" << rect.width << ", h=" << rect.height << ']'; +} + +template +static inline String dumpVector(const std::vector& vec, Formatter format) +{ + std::ostringstream oss("[", std::ios::ate); + if (!vec.empty()) + { + oss << format << vec[0]; + for (std::size_t i = 1; i < vec.size(); ++i) + { + oss << ", " << format << vec[i]; + } + } + oss << "]"; + return oss.str(); +} + +static inline std::ostream& noFormat(std::ostream& os) +{ + return os; +} + +static inline std::ostream& floatFormat(std::ostream& os) +{ + return os << std::fixed << std::setprecision(2); +} + +String dumpVectorOfInt(const std::vector& vec) +{ + return dumpVector(vec, &noFormat); +} + +String dumpVectorOfDouble(const std::vector& vec) +{ + return dumpVector(vec, &floatFormat); +} + +String dumpVectorOfRect(const std::vector& vec) +{ + return dumpVector(vec, &noFormat); +} + }} // namespace diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 6c5e6463d2..ff57978dc5 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -493,6 +493,33 @@ bool parseSequence(PyObject* obj, RefWrapper (&value)[N], const ArgInfo& info } } // namespace +namespace traits { +template +struct BooleanConstant +{ + static const bool value = Value; + typedef BooleanConstant type; +}; + +typedef BooleanConstant TrueType; +typedef BooleanConstant FalseType; + +template +struct VoidType { + typedef void type; +}; + +template +struct IsRepresentableAsMatDataType : FalseType +{ +}; + +template +struct IsRepresentableAsMatDataType::channel_type>::type> : TrueType +{ +}; +} // namespace traits + typedef std::vector vector_uchar; typedef std::vector vector_char; typedef std::vector vector_int; @@ -1042,6 +1069,30 @@ bool pyopencv_to(PyObject* obj, uchar& value, const ArgInfo& info) return ivalue != -1 || !PyErr_Occurred(); } +template<> +bool pyopencv_to(PyObject* obj, char& value, const ArgInfo& info) +{ + if (!obj || obj == Py_None) + { + return true; + } + if (isBool(obj)) + { + failmsg("Argument '%s' must be an integer, not bool", info.name); + return false; + } + if (PyArray_IsIntegerScalar(obj)) + { + value = saturate_cast(PyArray_PyIntAsInt(obj)); + } + else + { + failmsg("Argument '%s' is required to be an integer", info.name); + return false; + } + return !CV_HAS_CONVERSION_ERROR(value); +} + template<> PyObject* pyopencv_from(const double& value) { @@ -1454,277 +1505,12 @@ PyObject* pyopencv_from(const Point3d& p) return Py_BuildValue("(ddd)", p.x, p.y, p.z); } -template struct pyopencvVecConverter -{ - typedef typename DataType<_Tp>::channel_type _Cp; - static inline bool copyOneItem(PyObject *obj, size_t start, int channels, _Cp * data) - { - for(size_t j = 0; (int)j < channels; j++ ) - { - SafeSeqItem sub_item_wrap(obj, start + j); - PyObject* item_ij = sub_item_wrap.item; - if( PyInt_Check(item_ij)) - { - int v = (int)PyInt_AsLong(item_ij); - if( v == -1 && PyErr_Occurred() ) - return false; - data[j] = saturate_cast<_Cp>(v); - } - else if( PyLong_Check(item_ij)) - { - int v = (int)PyLong_AsLong(item_ij); - if( v == -1 && PyErr_Occurred() ) - return false; - data[j] = saturate_cast<_Cp>(v); - } - else if( PyFloat_Check(item_ij)) - { - double v = PyFloat_AsDouble(item_ij); - if( PyErr_Occurred() ) - return false; - data[j] = saturate_cast<_Cp>(v); - } - else - return false; - } - return true; - } - static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) - { - if(!obj || obj == Py_None) - return true; - if (PyArray_Check(obj)) - { - Mat m; - pyopencv_to(obj, m, info); - m.copyTo(value); - return true; - } - else if (PySequence_Check(obj)) - { - const int type = traits::Type<_Tp>::value; - const int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type); - size_t i, n = PySequence_Size(obj); - value.resize(n); - for (i = 0; i < n; i++ ) - { - SafeSeqItem item_wrap(obj, i); - PyObject* item = item_wrap.item; - _Cp* data = (_Cp*)&value[i]; - - if( channels == 2 && PyComplex_Check(item) ) - { - data[0] = saturate_cast<_Cp>(PyComplex_RealAsDouble(item)); - data[1] = saturate_cast<_Cp>(PyComplex_ImagAsDouble(item)); - } - else if( channels > 1 ) - { - if( PyArray_Check(item)) - { - Mat src; - pyopencv_to(item, src, info); - if( src.dims != 2 || src.channels() != 1 || - ((src.cols != 1 || src.rows != channels) && - (src.cols != channels || src.rows != 1))) - break; - Mat dst(src.rows, src.cols, depth, data); - src.convertTo(dst, type); - if( dst.data != (uchar*)data ) - break; - } - else if (PySequence_Check(item)) - { - if (!copyOneItem(item, 0, channels, data)) - break; - } - else - { - break; - } - } - else if (channels == 1) - { - if (!copyOneItem(obj, i, channels, data)) - break; - } - else - { - break; - } - } - return i == n; - } - return false; - } - - static PyObject* from(const std::vector<_Tp>& value) - { - if(value.empty()) - return PyTuple_New(0); - int type = traits::Type<_Tp>::value; - int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type); - Mat src((int)value.size(), channels, depth, (uchar*)&value[0]); - return pyopencv_from(src); - } -}; - -template -bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) -{ - return pyopencvVecConverter<_Tp>::to(obj, value, info); -} - -template -PyObject* pyopencv_from(const std::vector<_Tp>& value) -{ - return pyopencvVecConverter<_Tp>::from(value); -} - -template static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info) -{ - if(!obj || obj == Py_None) - return true; - if (!PySequence_Check(obj)) - return false; - size_t n = PySequence_Size(obj); - value.resize(n); - for(size_t i = 0; i < n; i++ ) - { - SafeSeqItem item_wrap(obj, i); - if(!pyopencv_to(item_wrap.item, value[i], info)) - return false; - } - return true; -} - -template static inline PyObject* pyopencv_from_generic_vec(const std::vector<_Tp>& value) -{ - int i, n = (int)value.size(); - PyObject* seq = PyList_New(n); - for( i = 0; i < n; i++ ) - { - PyObject* item = pyopencv_from(value[i]); - if(!item) - break; - PyList_SetItem(seq, i, item); - } - if( i < n ) - { - Py_DECREF(seq); - return 0; - } - return seq; -} - template<> PyObject* pyopencv_from(const std::pair& src) { return Py_BuildValue("(id)", src.first, src.second); } -template struct pyopencvVecConverter > -{ - static bool to(PyObject* obj, std::vector >& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector >& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template struct pyopencvVecConverter > -{ - static bool to(PyObject* obj, std::vector >& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector >& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - static PyObject* from(const std::vector& value) - { - return pyopencv_from_generic_vec(value); - } -}; - template<> bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info) { @@ -1852,6 +1638,183 @@ PyObject* pyopencv_from(const Moments& m) "nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03); } +template +struct pyopencvVecConverter; + +template +bool pyopencv_to(PyObject* obj, std::vector& value, const ArgInfo& info) +{ + if (!obj || obj == Py_None) + { + return true; + } + return pyopencvVecConverter::to(obj, value, info); +} + +template +PyObject* pyopencv_from(const std::vector& value) +{ + return pyopencvVecConverter::from(value); +} + +template +static bool pyopencv_to_generic_vec(PyObject* obj, std::vector& value, const ArgInfo& info) +{ + if (!obj || obj == Py_None) + { + return true; + } + if (!PySequence_Check(obj)) + { + failmsg("Can't parse '%s'. Input argument doesn't provide sequence protocol", info.name); + return false; + } + const size_t n = static_cast(PySequence_Size(obj)); + value.resize(n); + for (size_t i = 0; i < n; i++) + { + SafeSeqItem item_wrap(obj, i); + if (!pyopencv_to(item_wrap.item, value[i], info)) + { + failmsg("Can't parse '%s'. Sequence item with index %lu has a wrong type", info.name, i); + return false; + } + } + return true; +} + +template +static PyObject* pyopencv_from_generic_vec(const std::vector& value) +{ + Py_ssize_t n = static_cast(value.size()); + PySafeObject seq(PyTuple_New(n)); + for (Py_ssize_t i = 0; i < n; i++) + { + PyObject* item = pyopencv_from(value[i]); + // If item can't be assigned - PyTuple_SetItem raises exception and returns -1. + if (!item || PyTuple_SetItem(seq, i, item) == -1) + { + return NULL; + } + } + return seq.release(); +} + +template +struct pyopencvVecConverter +{ + typedef typename std::vector::iterator VecIt; + + static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) + { + if (!PyArray_Check(obj)) + { + return pyopencv_to_generic_vec(obj, value, info); + } + // If user passed an array it is possible to make faster conversions in several cases + PyArrayObject* array_obj = reinterpret_cast(obj); + const NPY_TYPES target_type = asNumpyType(); + const NPY_TYPES source_type = static_cast(PyArray_TYPE(array_obj)); + if (target_type == NPY_OBJECT) + { + // Non-planar arrays representing objects (e.g. array of N Rect is an array of shape Nx4) have NPY_OBJECT + // as their target type. + return pyopencv_to_generic_vec(obj, value, info); + } + if (PyArray_NDIM(array_obj) > 1) + { + failmsg("Can't parse %dD array as '%s' vector argument", PyArray_NDIM(array_obj), info.name); + return false; + } + if (target_type != source_type) + { + // Source type requires conversion + // Allowed conversions for target type is handled in the corresponding pyopencv_to function + return pyopencv_to_generic_vec(obj, value, info); + } + // For all other cases, all array data can be directly copied to std::vector data + // Simple `memcpy` is not possible because NumPy array can reference a slice of the bigger array: + // ``` + // arr = np.ones((8, 4, 5), dtype=np.int32) + // convertible_to_vector_of_int = arr[:, 0, 1] + // ``` + value.resize(static_cast(PyArray_SIZE(array_obj))); + const npy_intp item_step = PyArray_STRIDE(array_obj, 0) / PyArray_ITEMSIZE(array_obj); + const Tp* data_ptr = static_cast(PyArray_DATA(array_obj)); + for (VecIt it = value.begin(); it != value.end(); ++it, data_ptr += item_step) { + *it = *data_ptr; + } + return true; + } + + static PyObject* from(const std::vector& value) + { + if (value.empty()) + { + return PyTuple_New(0); + } + return from(value, ::traits::IsRepresentableAsMatDataType()); + } + +private: + static PyObject* from(const std::vector& value, ::traits::FalseType) + { + // Underlying type is not representable as Mat Data Type + return pyopencv_from_generic_vec(value); + } + + static PyObject* from(const std::vector& value, ::traits::TrueType) + { + // Underlying type is representable as Mat Data Type, so faster return type is available + typedef DataType DType; + typedef typename DType::channel_type UnderlyingArrayType; + + // If Mat is always exposed as NumPy array this code path can be reduced to the following snipped: + // Mat src(value); + // PyObject* array = pyopencv_from(src); + // return PyArray_Squeeze(reinterpret_cast(array)); + // This puts unnecessary restrictions on Mat object those might be avoided without losing the performance. + // Moreover, this version is a bit faster, because it doesn't create temporary objects with reference counting. + + const NPY_TYPES target_type = asNumpyType(); + const int cols = DType::channels; + PyObject* array; + if (cols == 1) + { + npy_intp dims = static_cast(value.size()); + array = PyArray_SimpleNew(1, &dims, target_type); + } + else + { + npy_intp dims[2] = {static_cast(value.size()), cols}; + array = PyArray_SimpleNew(2, dims, target_type); + } + if(!array) + { + // NumPy arrays with shape (N, 1) and (N) are not equal, so correct error message should distinguish + // them too. + String shape; + if (cols > 1) + { + shape = cv::format("(%d x %d)", static_cast(value.size()), cols); + } + else + { + shape = cv::format("(%d)", static_cast(value.size())); + } + CV_Error_(Error::StsError, ("Can't allocate NumPy array for vector with dtype=%d shape=%s", + static_cast(target_type), static_cast(value.size()), shape.c_str())); + } + // Fill the array + PyArrayObject* array_obj = reinterpret_cast(array); + UnderlyingArrayType* array_data = static_cast(PyArray_DATA(array_obj)); + // if Tp is representable as Mat DataType, so the following cast is pretty safe... + const UnderlyingArrayType* value_data = reinterpret_cast(value.data()); + memcpy(array_data, value_data, sizeof(UnderlyingArrayType) * value.size() * static_cast(cols)); + return array; + } +}; + static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata) { PyGILState_STATE gstate; diff --git a/modules/python/test/test_legacy.py b/modules/python/test/test_legacy.py index ab0a8bdc35..e550ab73c8 100644 --- a/modules/python/test/test_legacy.py +++ b/modules/python/test/test_legacy.py @@ -20,8 +20,13 @@ class Hackathon244Tests(NewOpenCVTests): flag, ajpg = cv.imencode("img_q90.jpg", a, [cv.IMWRITE_JPEG_QUALITY, 90]) self.assertEqual(flag, True) self.assertEqual(ajpg.dtype, np.uint8) - self.assertGreater(ajpg.shape[0], 1) - self.assertEqual(ajpg.shape[1], 1) + self.assertTrue(isinstance(ajpg, np.ndarray), "imencode returned buffer of wrong type: {}".format(type(ajpg))) + self.assertEqual(len(ajpg.shape), 1, "imencode returned buffer with wrong shape: {}".format(ajpg.shape)) + self.assertGreaterEqual(len(ajpg), 1, "imencode length of the returned buffer should be at least 1") + self.assertLessEqual( + len(ajpg), a.size, + "imencode length of the returned buffer shouldn't exceed number of elements in original image" + ) def test_projectPoints(self): objpt = np.float64([[1,2,3]]) diff --git a/modules/python/test/test_misc.py b/modules/python/test/test_misc.py index 4d435a46b6..c992c9450d 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -480,6 +480,109 @@ class Arguments(NewOpenCVTests): cv.utils.testReservedKeywordConversion(20, lambda_=-4, from_=12), format_str.format(20, -4, 12) ) + def test_parse_vector_int_convertible(self): + np.random.seed(123098765) + try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfInt) + arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2) + int_min, int_max = get_limits(ctypes.c_int) + for convertible in ((int_min, 1, 2, 3, int_max), [40, 50], tuple(), + np.array([int_min, -10, 24, int_max], dtype=np.int32), + np.array([10, 230, 12], dtype=np.uint8), arr[:, 0, 1],): + expected = "[" + ", ".join(map(str, convertible)) + "]" + actual = try_to_convert(convertible) + self.assertEqual(expected, actual, + msg=get_conversion_error_msg(convertible, expected, actual)) + + def test_parse_vector_int_not_convertible(self): + np.random.seed(123098765) + arr = np.random.randint(-20, 20, 40).astype(np.float).reshape(10, 2, 2) + int_min, int_max = get_limits(ctypes.c_int) + test_dict = {1: 2, 3: 10, 10: 20} + for not_convertible in ((int_min, 1, 2.5, 3, int_max), [True, 50], 'test', test_dict, + reversed([1, 2, 3]), + np.array([int_min, -10, 24, [1, 2]], dtype=np.object), + np.array([[1, 2], [3, 4]]), arr[:, 0, 1],): + with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): + _ = cv.utils.dumpVectorOfInt(not_convertible) + + def test_parse_vector_double_convertible(self): + np.random.seed(1230965) + try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfDouble) + arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2) + for convertible in ((1, 2.12, 3.5), [40, 50], tuple(), + np.array([-10, 24], dtype=np.int32), + np.array([-12.5, 1.4], dtype=np.double), + np.array([10, 230, 12], dtype=np.float), arr[:, 0, 1], ): + expected = "[" + ", ".join(map(lambda v: "{:.2f}".format(v), convertible)) + "]" + actual = try_to_convert(convertible) + self.assertEqual(expected, actual, + msg=get_conversion_error_msg(convertible, expected, actual)) + + def test_parse_vector_double_not_convertible(self): + test_dict = {1: 2, 3: 10, 10: 20} + for not_convertible in (('t', 'e', 's', 't'), [True, 50.55], 'test', test_dict, + np.array([-10.1, 24.5, [1, 2]], dtype=np.object), + np.array([[1, 2], [3, 4]]),): + with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): + _ = cv.utils.dumpVectorOfDouble(not_convertible) + + def test_parse_vector_rect_convertible(self): + np.random.seed(1238765) + try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfRect) + arr_of_rect_int32 = np.random.randint(5, 20, 4 * 3).astype(np.int32).reshape(3, 4) + arr_of_rect_cast = np.random.randint(10, 40, 4 * 5).astype(np.uint8).reshape(5, 4) + for convertible in (((1, 2, 3, 4), (10, -20, 30, 10)), arr_of_rect_int32, arr_of_rect_cast, + arr_of_rect_int32.astype(np.int8), [[5, 3, 1, 4]], + ((np.int8(4), np.uint8(10), np.int(32), np.int16(55)),)): + expected = "[" + ", ".join(map(lambda v: "[x={}, y={}, w={}, h={}]".format(*v), convertible)) + "]" + actual = try_to_convert(convertible) + self.assertEqual(expected, actual, + msg=get_conversion_error_msg(convertible, expected, actual)) + + def test_parse_vector_rect_not_convertible(self): + np.random.seed(1238765) + arr = np.random.randint(5, 20, 4 * 3).astype(np.float).reshape(3, 4) + for not_convertible in (((1, 2, 3, 4), (10.5, -20, 30.1, 10)), arr, + [[5, 3, 1, 4], []], + ((np.float(4), np.uint8(10), np.int(32), np.int16(55)),)): + with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)): + _ = cv.utils.dumpVectorOfRect(not_convertible) + + def test_vector_general_return(self): + expected_number_of_mats = 5 + expected_shape = (10, 10, 3) + expected_type = np.uint8 + mats = cv.utils.generateVectorOfMat(5, 10, 10, cv.CV_8UC3) + self.assertTrue(isinstance(mats, tuple), + "Vector of Mats objects should be returned as tuple. Got: {}".format(type(mats))) + self.assertEqual(len(mats), expected_number_of_mats, "Returned array has wrong length") + for mat in mats: + self.assertEqual(mat.shape, expected_shape, "Returned Mat has wrong shape") + self.assertEqual(mat.dtype, expected_type, "Returned Mat has wrong elements type") + empty_mats = cv.utils.generateVectorOfMat(0, 10, 10, cv.CV_32FC1) + self.assertTrue(isinstance(empty_mats, tuple), + "Empty vector should be returned as empty tuple. Got: {}".format(type(mats))) + self.assertEqual(len(empty_mats), 0, "Vector of size 0 should be returned as tuple of length 0") + + def test_vector_fast_return(self): + expected_shape = (5, 4) + rects = cv.utils.generateVectorOfRect(expected_shape[0]) + self.assertTrue(isinstance(rects, np.ndarray), + "Vector of rectangles should be returned as numpy array. Got: {}".format(type(rects))) + self.assertEqual(rects.dtype, np.int32, "Vector of rectangles has wrong elements type") + self.assertEqual(rects.shape, expected_shape, "Vector of rectangles has wrong shape") + empty_rects = cv.utils.generateVectorOfRect(0) + self.assertTrue(isinstance(empty_rects, tuple), + "Empty vector should be returned as empty tuple. Got: {}".format(type(empty_rects))) + self.assertEqual(len(empty_rects), 0, "Vector of size 0 should be returned as tuple of length 0") + + expected_shape = (10,) + ints = cv.utils.generateVectorOfInt(expected_shape[0]) + self.assertTrue(isinstance(ints, np.ndarray), + "Vector of integers should be returned as numpy array. Got: {}".format(type(ints))) + self.assertEqual(ints.dtype, np.int32, "Vector of integers has wrong elements type") + self.assertEqual(ints.shape, expected_shape, "Vector of integers has wrong shape.") + class SamplesFindFile(NewOpenCVTests): From edc442afdb559c7a8d731b86637839b5f224de35 Mon Sep 17 00:00:00 2001 From: WJJ1995 Date: Wed, 1 Sep 2021 18:10:05 +0800 Subject: [PATCH 6/7] Merge pull request #20511 from wjj19950828:add_humanseg_support_0806 * support PPSeg model for dnn module * fixed README for CI * add test case * fixed bug * deal with comments * rm dnn_model_runner * update test case * fixed bug for testcase * update testcase --- modules/dnn/src/onnx/onnx_importer.cpp | 2 +- modules/dnn/test/test_onnx_importer.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 6da2c5edf6..41ff6c9b1e 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -2054,7 +2054,7 @@ void ONNXImporter::parseResize(LayerParams& layerParams, const opencv_onnx::Node layerParams.set("align_corners", interp_mode == "align_corners"); if (layerParams.get("mode") == "linear") { - layerParams.set("mode", interp_mode == "pytorch_half_pixel" ? + layerParams.set("mode", interp_mode == "pytorch_half_pixel" || interp_mode == "half_pixel" ? "opencv_linear" : "bilinear"); } } diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 983f72d6d6..07a0290b9b 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -552,6 +552,11 @@ TEST_P(Test_ONNX_layers, DynamicResize) testONNXModels("dynamic_resize_scale_11", npy, 0, 0, false, true, 2); } +TEST_P(Test_ONNX_layers, Resize_HumanSeg) +{ + testONNXModels("resize_humanseg"); +} + TEST_P(Test_ONNX_layers, Div) { const String model = _tf("models/div.onnx"); From 390957fec4601b58982c3d15130485f531d22ba5 Mon Sep 17 00:00:00 2001 From: Vadim Levin Date: Thu, 2 Sep 2021 10:32:17 +0300 Subject: [PATCH 7/7] fix: NumPy array allocation error message in vector conversion --- modules/python/src2/cv2.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index ff57978dc5..67c4166799 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1778,7 +1778,7 @@ private: const NPY_TYPES target_type = asNumpyType(); const int cols = DType::channels; - PyObject* array; + PyObject* array = NULL; if (cols == 1) { npy_intp dims = static_cast(value.size()); @@ -1796,14 +1796,16 @@ private: String shape; if (cols > 1) { - shape = cv::format("(%d x %d)", static_cast(value.size()), cols); + shape = format("(%d x %d)", static_cast(value.size()), cols); } else { - shape = cv::format("(%d)", static_cast(value.size())); + shape = format("(%d)", static_cast(value.size())); } - CV_Error_(Error::StsError, ("Can't allocate NumPy array for vector with dtype=%d shape=%s", - static_cast(target_type), static_cast(value.size()), shape.c_str())); + const String error_message = format("Can't allocate NumPy array for vector with dtype=%d and shape=%s", + static_cast(target_type), shape.c_str()); + emit_failmsg(PyExc_MemoryError, error_message.c_str()); + return array; } // Fill the array PyArrayObject* array_obj = reinterpret_cast(array);