diff --git a/cmake/OpenCVCompilerOptions.cmake b/cmake/OpenCVCompilerOptions.cmake index 2917dd33d5..037c7fb5ba 100644 --- a/cmake/OpenCVCompilerOptions.cmake +++ b/cmake/OpenCVCompilerOptions.cmake @@ -407,6 +407,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/bindings_utils.hpp b/modules/core/include/opencv2/core/bindings_utils.hpp index e5c60631eb..a3f45eb0c7 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/include/opencv2/core/cvdef.h b/modules/core/include/opencv2/core/cvdef.h index 7f510748af..ada460334c 100644 --- a/modules/core/include/opencv2/core/cvdef.h +++ b/modules/core/include/opencv2/core/cvdef.h @@ -705,14 +705,47 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType) # 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 6768be7683..1ef0eb5a02 100644 --- a/modules/core/include/opencv2/core/mat.hpp +++ b/modules/core/include/opencv2/core/mat.hpp @@ -1188,14 +1188,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. @@ -1359,20 +1359,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. @@ -1390,20 +1390,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. @@ -1419,13 +1419,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. @@ -2288,7 +2288,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; @@ -2301,14 +2301,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 ); @@ -2451,11 +2451,11 @@ 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, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat diag(const UMat& d) { return diag(d, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat diag(const UMat& d, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat diag(const UMat& d) { return diag(d, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload //! 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; @@ -2486,22 +2486,22 @@ public: double dot(InputArray m) const; //! Matlab-style matrix initialization - static UMat zeros(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat zeros(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat zeros(int ndims, const int* sz, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat zeros(int rows, int cols, int type) { return zeros(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat zeros(Size size, int type) { return zeros(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat zeros(int ndims, const int* sz, int type) { return zeros(ndims, sz, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat ones(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat ones(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat ones(int ndims, const int* sz, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat ones(int rows, int cols, int type) { return ones(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat ones(Size size, int type) { return ones(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat ones(int ndims, const int* sz, int type) { return ones(ndims, sz, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat eye(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat eye(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); - static UMat eye(int rows, int cols, int type) { return eye(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload - static UMat eye(Size size, int type) { return eye(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat zeros(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat zeros(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat zeros(int ndims, const int* sz, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat zeros(int rows, int cols, int type) { return zeros(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat zeros(Size size, int type) { return zeros(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat zeros(int ndims, const int* sz, int type) { return zeros(ndims, sz, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat ones(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat ones(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat ones(int ndims, const int* sz, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat ones(int rows, int cols, int type) { return ones(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat ones(Size size, int type) { return ones(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat ones(int ndims, const int* sz, int type) { return ones(ndims, sz, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat eye(int rows, int cols, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat eye(Size size, int type, UMatUsageFlags usageFlags /*= USAGE_DEFAULT*/); + CV_NODISCARD_STD static UMat eye(int rows, int cols, int type) { return eye(rows, cols, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload + CV_NODISCARD_STD static UMat eye(Size size, int type) { return eye(size, type, USAGE_DEFAULT); } // OpenCV 5.0: remove abi compatibility overload //! 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 45d8a97f34..3c92e3a21d 100644 --- a/modules/core/include/opencv2/core/matx.hpp +++ b/modules/core/include/opencv2/core/matx.hpp @@ -142,22 +142,22 @@ public: Matx(std::initializer_list<_Tp>); //!< initialize from an initializer list - 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; diff --git a/modules/core/src/bindings_utils.cpp b/modules/core/src/bindings_utils.cpp index 78716c21f6..78093002f7 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 #include #include @@ -210,6 +211,53 @@ 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 fs { cv::String getCacheDirectoryForDownloads() { diff --git a/modules/core/src/ocl.cpp b/modules/core/src/ocl.cpp index be7c30c2d5..7125f88be0 100644 --- a/modules/core/src/ocl.cpp +++ b/modules/core/src/ocl.cpp @@ -3677,6 +3677,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) diff --git a/modules/dnn/misc/python/test/test_dnn.py b/modules/dnn/misc/python/test/test_dnn.py index 31ee70b212..df997eb7fa 100644 --- a/modules/dnn/misc/python/test/test_dnn.py +++ b/modules/dnn/misc/python/test/test_dnn.py @@ -218,7 +218,7 @@ class dnn_test(NewOpenCVTests): model.setInputParams(scale, size, mean) out, _ = model.detect(frame) - self.assertTrue(type(out) == list) + self.assertTrue(type(out) == tuple, msg='actual type {}'.format(str(type(out)))) self.assertTrue(np.array(out).shape == (2, 4, 2)) diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index cff2bdc0f4..26aa89e3ac 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; diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 91880cba6a..33fdeb79de 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -534,7 +534,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) @@ -1540,6 +1541,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); @@ -2185,7 +2196,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 5dfe9ca30c..84ac8b6ec0 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -301,6 +301,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) @@ -586,6 +587,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"); @@ -881,6 +887,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) diff --git a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py index a10d63f09e..34a07b535b 100644 --- a/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py +++ b/modules/gapi/misc/python/test/test_gapi_sample_pipelines.py @@ -517,8 +517,8 @@ try: comp = cv.GComputation(cv.GIn(g_arr0, g_arr1), cv.GOut(g_out)) - arr0 = [(2, 2), 2.0] - arr1 = [3, 'str'] + arr0 = ((2, 2), 2.0) + arr1 = (3, 'str') out = comp.apply(cv.gin(arr0, arr1), args=cv.gapi.compile_args(cv.gapi.kernels(GConcatImpl))) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 55f512291b..e97f17470c 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -496,6 +496,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; @@ -1072,6 +1099,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) { @@ -1484,357 +1535,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; - } - } - if (i != n) - { - failmsg("Can't convert vector element for '%s', index=%d", info.name, i); - } - return i == n; - } - failmsg("Can't convert object to vector for '%s', unsupported type", info.name); - 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<> inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector& 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); - bool elem{}; - if(!pyopencv_to(item_wrap.item, elem, info)) - return false; - value[i] = elem; - } - 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++ ) - { - _Tp elem = value[i]; - PyObject* item = pyopencv_from(elem); - if(!item) - break; - PyList_SetItem(seq, i, item); - } - if( i < n ) - { - Py_DECREF(seq); - return 0; - } - return seq; -} - -template<> inline PyObject* pyopencv_from_generic_vec(const std::vector& value) -{ - int i, n = (int)value.size(); - PyObject* seq = PyList_New(n); - for( i = 0; i < n; i++ ) - { - bool elem = value[i]; - PyObject* item = pyopencv_from(elem); - if(!item) - break; - PyList_SetItem(seq, i, item); - } - if( i < n ) - { - Py_DECREF(seq); - return 0; - } - return seq; -} - -template -inline typename std::enable_if::type -convert_to_python_tuple(const std::tuple&, PyObject*) { } - -template -inline typename std::enable_if::type -convert_to_python_tuple(const std::tuple& cpp_tuple, PyObject* py_tuple) -{ - PyObject* item = pyopencv_from(std::get(cpp_tuple)); - - if (!item) - return; - - PyTuple_SetItem(py_tuple, I, item); - convert_to_python_tuple(cpp_tuple, py_tuple); -} - - -template -PyObject* pyopencv_from(const std::tuple& cpp_tuple) -{ - size_t size = sizeof...(Ts); - PyObject* py_tuple = PyTuple_New(size); - convert_to_python_tuple(cpp_tuple, py_tuple); - size_t actual_size = PyTuple_Size(py_tuple); - - if (actual_size < size) - { - Py_DECREF(py_tuple); - return NULL; - } - - return py_tuple; -} - 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) { @@ -1962,6 +1668,266 @@ 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<> inline 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); + bool elem{}; + if (!pyopencv_to(item_wrap.item, elem, info)) + { + failmsg("Can't parse '%s'. Sequence item with index %lu has a wrong type", info.name, i); + return false; + } + value[i] = elem; + } + 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<> inline 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++) + { + bool elem = value[i]; + PyObject* item = pyopencv_from(elem); + // 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 +inline typename std::enable_if::type +convert_to_python_tuple(const std::tuple&, PyObject*) { } + +template +inline typename std::enable_if::type +convert_to_python_tuple(const std::tuple& cpp_tuple, PyObject* py_tuple) +{ + PyObject* item = pyopencv_from(std::get(cpp_tuple)); + + if (!item) + return; + + PyTuple_SetItem(py_tuple, I, item); + convert_to_python_tuple(cpp_tuple, py_tuple); +} + + +template +PyObject* pyopencv_from(const std::tuple& cpp_tuple) +{ + size_t size = sizeof...(Ts); + PyObject* py_tuple = PyTuple_New(size); + convert_to_python_tuple(cpp_tuple, py_tuple); + size_t actual_size = PyTuple_Size(py_tuple); + + if (actual_size < size) + { + Py_DECREF(py_tuple); + return NULL; + } + + return py_tuple; +} + +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 = NULL; + 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 = format("(%d x %d)", static_cast(value.size()), cols); + } + else + { + shape = format("(%d)", static_cast(value.size())); + } + 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); + 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 d3a9f35766..111b1427b4 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -481,6 +481,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):