diff --git a/modules/python/src2/cv2_convert.cpp b/modules/python/src2/cv2_convert.cpp index 2e69586f47..20cd957753 100644 --- a/modules/python/src2/cv2_convert.cpp +++ b/modules/python/src2/cv2_convert.cpp @@ -5,6 +5,7 @@ #include "cv2_convert.hpp" #include "cv2_numpy.hpp" +#include "cv2_util.hpp" #include "opencv2/core/utils/logger.hpp" PyTypeObject* pyopencv_Mat_TypePtr = nullptr; @@ -24,6 +25,26 @@ static std::string pycv_dumpArray(const T* arr, int n) return out.str(); } +static inline std::string getArrayTypeName(PyArrayObject* arr) +{ + PyArray_Descr* dtype = PyArray_DESCR(arr); + PySafeObject dtype_str(PyObject_Str(reinterpret_cast(dtype))); + if (!dtype_str) + { + // Fallback to typenum value + return cv::format("%d", PyArray_TYPE(arr)); + } + std::string type_name; + if (!getUnicodeString(dtype_str, type_name)) + { + // Failed to get string from bytes object - clear set TypeError and + // fallback to typenum value + PyErr_Clear(); + return cv::format("%d", PyArray_TYPE(arr)); + } + return type_name; +} + //====================================================================================================================== // --- Mat @@ -102,7 +123,9 @@ bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info) } else { - failmsg("%s data type = %d is not supported", info.name, typenum); + const std::string dtype_name = getArrayTypeName(oarr); + failmsg("%s data type = %s is not supported", info.name, + dtype_name.c_str()); return false; } } diff --git a/modules/python/src2/cv2_util.hpp b/modules/python/src2/cv2_util.hpp index 0d27e98825..c15fcac486 100644 --- a/modules/python/src2/cv2_util.hpp +++ b/modules/python/src2/cv2_util.hpp @@ -42,7 +42,7 @@ private: /** * Light weight RAII wrapper for `PyObject*` owning references. - * In comparisson to C++11 `std::unique_ptr` with custom deleter, it provides + * In comparison to C++11 `std::unique_ptr` with custom deleter, it provides * implicit conversion functions that might be useful to initialize it with * Python functions those returns owning references through the `PyObject**` * e.g. `PyErr_Fetch` or directly pass it to functions those want to borrow @@ -70,6 +70,10 @@ public: return &obj_; } + operator bool() { + return static_cast(obj_); + } + PyObject* release() { PyObject* obj = obj_; diff --git a/modules/python/test/test_misc.py b/modules/python/test/test_misc.py index ec7f44de0f..ed13d66b93 100644 --- a/modules/python/test/test_misc.py +++ b/modules/python/test/test_misc.py @@ -219,6 +219,14 @@ class Arguments(NewOpenCVTests): #res6 = cv.utils.dumpInputArray([a, b]) #self.assertEqual(res6, "InputArrayOfArrays: empty()=false kind=0x00050000 flags=0x01050000 total(-1)=2 dims(-1)=1 size(-1)=2x1 type(0)=CV_32FC1 dims(0)=4 size(0)=[2 3 4 5]") + def test_unsupported_numpy_data_types_string_description(self): + for dtype in (object, str, np.complex128): + test_array = np.zeros((4, 4, 3), dtype=dtype) + msg = ".*type = {} is not supported".format(test_array.dtype) + self.assertRaisesRegex( + Exception, msg, cv.utils.dumpInputArray, test_array + ) + def test_20968(self): pixel = np.uint8([[[40, 50, 200]]]) _ = cv.cvtColor(pixel, cv.COLOR_RGB2BGR) # should not raise exception