From 10d61a2b44b2fc1e2ae655987b3672c4053e3534 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Thu, 21 Mar 2019 10:45:02 +0300 Subject: [PATCH] Limited API support for Python3 --- modules/dnn/misc/python/pyopencv_dnn.hpp | 19 +- modules/flann/misc/python/pyopencv_flann.hpp | 29 +- modules/python/bindings/CMakeLists.txt | 8 +- modules/python/common.cmake | 26 +- modules/python/python3/CMakeLists.txt | 8 + modules/python/src2/cv2.cpp | 561 ++++++++----------- modules/python/src2/gen2.py | 216 +++---- modules/python/src2/pycompat.hpp | 265 ++++++++- 8 files changed, 635 insertions(+), 497 deletions(-) diff --git a/modules/dnn/misc/python/pyopencv_dnn.hpp b/modules/dnn/misc/python/pyopencv_dnn.hpp index 03728f6440..b1cc7c72d3 100644 --- a/modules/dnn/misc/python/pyopencv_dnn.hpp +++ b/modules/dnn/misc/python/pyopencv_dnn.hpp @@ -21,16 +21,19 @@ bool pyopencv_to(PyObject *o, dnn::DictValue &dv, const char *name) } else if (PyFloat_Check(o)) { - dv = dnn::DictValue(PyFloat_AS_DOUBLE(o)); - return true; - } - else if (PyString_Check(o)) - { - dv = dnn::DictValue(String(PyString_AsString(o))); + dv = dnn::DictValue(PyFloat_AsDouble(o)); return true; } else - return false; + { + std::string str; + if (getUnicodeString(o, str)) + { + dv = dnn::DictValue(str); + return true; + } + } + return false; } template<> @@ -134,7 +137,7 @@ public: PyObject* args = PyList_New(inputs.size()); for(size_t i = 0; i < inputs.size(); ++i) - PyList_SET_ITEM(args, i, pyopencv_from_generic_vec(inputs[i])); + PyList_SetItem(args, i, pyopencv_from_generic_vec(inputs[i])); PyObject* res = PyObject_CallMethodObjArgs(o, PyString_FromString("getMemoryShapes"), args, NULL); Py_DECREF(args); diff --git a/modules/flann/misc/python/pyopencv_flann.hpp b/modules/flann/misc/python/pyopencv_flann.hpp index 91e0564d84..2f56afb398 100644 --- a/modules/flann/misc/python/pyopencv_flann.hpp +++ b/modules/flann/misc/python/pyopencv_flann.hpp @@ -27,20 +27,20 @@ bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) return true; if(PyDict_Check(o)) { - while(PyDict_Next(o, &pos, &key, &item)) { - if( !PyString_Check(key) ) { + while(PyDict_Next(o, &pos, &key, &item)) + { + // get key + std::string k; + if (!getUnicodeString(key, k)) + { ok = false; break; } - - String k = PyString_AsString(key); - if( PyString_Check(item) ) + // get value + if( !!PyBool_Check(item) ) { - const char* value = PyString_AsString(item); - p.setString(k, value); - } - else if( !!PyBool_Check(item) ) p.setBool(k, item == Py_True); + } else if( PyInt_Check(item) ) { int value = (int)PyInt_AsLong(item); @@ -56,8 +56,13 @@ bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) } else { - ok = false; - break; + std::string val_str; + if (!getUnicodeString(item, val_str)) + { + ok = false; + break; + } + p.setString(k, val_str); } } } @@ -79,4 +84,4 @@ bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const char *name) dist = (cvflann::flann_distance_t)d; return ok; } -#endif \ No newline at end of file +#endif diff --git a/modules/python/bindings/CMakeLists.txt b/modules/python/bindings/CMakeLists.txt index d96d7e88ff..4ad3d0c8d9 100644 --- a/modules/python/bindings/CMakeLists.txt +++ b/modules/python/bindings/CMakeLists.txt @@ -51,11 +51,13 @@ if(NOT HAVE_CUDA) endif() set(cv2_generated_files - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_enums.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_funcs.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_modules.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_modules_content.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_ns_reg.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types_content.h" "${OPENCV_PYTHON_SIGNATURES_FILE}" ) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 17503c03f9..6a9d32d99a 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -24,6 +24,22 @@ if(TARGET gen_opencv_python_source) add_dependencies(${the_module} gen_opencv_python_source) endif() +ocv_assert(${PYTHON}_VERSION_MAJOR) +ocv_assert(${PYTHON}_VERSION_MINOR) + +if(${PYTHON}_LIMITED_API) + # support only python3.3+ + ocv_assert(${PYTHON}_VERSION_MAJOR EQUAL 3 AND ${PYTHON}_VERSION_MINOR GREATER 2) + target_compile_definitions(${the_module} PRIVATE CVPY_DYNAMIC_INIT) + if(WIN32) + string(REPLACE + "python${${PYTHON}_VERSION_MAJOR}${${PYTHON}_VERSION_MINOR}.lib" + "python${${PYTHON}_VERSION_MAJOR}.lib" + ${PYTHON}_LIBRARIES + "${${PYTHON}_LIBRARIES}") + endif() +endif() + if(APPLE) set_target_properties(${the_module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") elseif(WIN32 OR OPENCV_FORCE_PYTHON_LIBS) @@ -54,6 +70,13 @@ else() if(NOT PYTHON_CVPY_PROCESS EQUAL 0) set(CVPY_SUFFIX ".so") endif() + if(${PYTHON}_LIMITED_API) + if(WIN32) + string(REGEX REPLACE "\\.[^\\.]*\\." "." CVPY_SUFFIX "${CVPY_SUFFIX}") + else() + string(REGEX REPLACE "\\.[^\\.]*\\." ".abi${${PYTHON}_VERSION_MAJOR}." CVPY_SUFFIX "${CVPY_SUFFIX}") + endif() + endif() endif() ocv_update(OPENCV_PYTHON_EXTENSION_BUILD_PATH "${LIBRARY_OUTPUT_PATH}/${MODULE_INSTALL_SUBDIR}") @@ -111,9 +134,6 @@ else() set(PYTHON_INSTALL_ARCHIVE ARCHIVE DESTINATION ${${PYTHON}_PACKAGES_PATH} COMPONENT python) endif() -ocv_assert(${PYTHON}_VERSION_MAJOR) -ocv_assert(${PYTHON}_VERSION_MINOR) - set(__python_loader_subdir "") if(NOT OPENCV_SKIP_PYTHON_LOADER) set(__python_loader_subdir "cv2/") diff --git a/modules/python/python3/CMakeLists.txt b/modules/python/python3/CMakeLists.txt index da86ba5c5e..ae1f989d3a 100644 --- a/modules/python/python3/CMakeLists.txt +++ b/modules/python/python3/CMakeLists.txt @@ -2,6 +2,14 @@ if(NOT PYTHON3_INCLUDE_PATH OR NOT PYTHON3_NUMPY_INCLUDE_DIRS) ocv_module_disable(python3) endif() +# Problem in numpy >=1.15 <1.17 +if(PYTHON3_LIMITED_API + AND NOT PYTHON3_NUMPY_VERSION VERSION_LESS "1.15" + AND PYTHON3_NUMPY_VERSION VERSION_LESS "1.17" + ) + set(PYTHON3_LIMITED_API OFF) +endif() + set(the_description "The python3 bindings") set(MODULE_NAME python3) set(MODULE_INSTALL_SUBDIR python3) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 4b2801fca9..88683c146c 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -5,12 +5,35 @@ #pragma warning(push) #pragma warning(disable:5033) // 'register' is no longer a supported storage class #endif + +// #define CVPY_DYNAMIC_INIT +// #define Py_DEBUG + +#if defined(CVPY_DYNAMIC_INIT) && !defined(Py_DEBUG) +# define Py_LIMITED_API 0x03030000 +#endif + #include #include + +#if PY_MAJOR_VERSION < 3 +#undef CVPY_DYNAMIC_INIT +#endif + #if defined(_MSC_VER) && (_MSC_VER > 1800) #pragma warning(pop) #endif +#define MODULESTR "cv2" +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include + +#include "pyopencv_generated_include.h" +#include "opencv2/core/types_c.h" +#include "opencv2/opencv_modules.hpp" +#include "pycompat.hpp" +#include + template // TEnable is used for SFINAE checks struct PyOpenCV_Converter { @@ -24,95 +47,6 @@ bool pyopencv_to(PyObject* obj, T& p, const char* name = "") { return P template static PyObject* pyopencv_from(const T& src) { return PyOpenCV_Converter::from(src); } - -#define CV_PY_FN_WITH_KW_(fn, flags) (PyCFunction)(void*)(PyCFunctionWithKeywords)(fn), (flags) | METH_VARARGS | METH_KEYWORDS -#define CV_PY_FN_NOARGS_(fn, flags) (PyCFunction)(fn), (flags) | METH_NOARGS - -#define CV_PY_FN_WITH_KW(fn) CV_PY_FN_WITH_KW_(fn, 0) -#define CV_PY_FN_NOARGS(fn) CV_PY_FN_NOARGS_(fn, 0) - - -#define MODULESTR "cv2" -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include - -#if PY_MAJOR_VERSION >= 3 -# define CV_PYTHON_TYPE_HEAD_INIT() PyVarObject_HEAD_INIT(&PyType_Type, 0) -#else -# define CV_PYTHON_TYPE_HEAD_INIT() PyObject_HEAD_INIT(&PyType_Type) 0, -#endif - -#define CV_PY_TO_CLASS(TYPE) \ -template<> \ -bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ -{ \ - if (!dst || dst == Py_None) \ - return true; \ - Ptr ptr; \ - \ - if (!pyopencv_to(dst, ptr, name)) return false; \ - src = *ptr; \ - return true; \ -} - -#define CV_PY_FROM_CLASS(TYPE) \ -template<> \ -PyObject* pyopencv_from(const TYPE& src) \ -{ \ - Ptr ptr(new TYPE()); \ - \ - *ptr = src; \ - return pyopencv_from(ptr); \ -} - -#define CV_PY_TO_CLASS_PTR(TYPE) \ -template<> \ -bool pyopencv_to(PyObject* dst, TYPE*& src, const char* name) \ -{ \ - if (!dst || dst == Py_None) \ - return true; \ - Ptr ptr; \ - \ - if (!pyopencv_to(dst, ptr, name)) return false; \ - src = ptr; \ - return true; \ -} - -#define CV_PY_FROM_CLASS_PTR(TYPE) \ -static PyObject* pyopencv_from(TYPE*& src) \ -{ \ - return pyopencv_from(Ptr(src)); \ -} - -#define CV_PY_TO_ENUM(TYPE) \ -template<> \ -bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ -{ \ - if (!dst || dst == Py_None) \ - return true; \ - int underlying = 0; \ - \ - if (!pyopencv_to(dst, underlying, name)) return false; \ - src = static_cast(underlying); \ - return true; \ -} - -#define CV_PY_FROM_ENUM(TYPE) \ -template<> \ -PyObject* pyopencv_from(const TYPE& src) \ -{ \ - return pyopencv_from(static_cast(src)); \ -} - -#include "pyopencv_generated_include.h" -#include "opencv2/core/types_c.h" - -#include "opencv2/opencv_modules.hpp" - -#include "pycompat.hpp" - -#include - static PyObject* opencv_error = NULL; static int failmsg(const char *fmt, ...) @@ -337,7 +271,7 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo info) m = Mat(sz, 1, CV_64F); for( i = 0; i < sz; i++ ) { - PyObject* oi = PyTuple_GET_ITEM(o, i); + PyObject* oi = PyTuple_GetItem(o, i); if( PyInt_Check(oi) ) m.at(i) = (double)PyInt_AsLong(oi); else if( PyFloat_Check(oi) ) @@ -571,21 +505,26 @@ static PyObject* pyopencv_from(void*& ptr) return PyLong_FromVoidPtr(ptr); } +struct SafeSeqItem +{ + PyObject * item; + SafeSeqItem(PyObject *obj, size_t idx) { item = PySequence_GetItem(obj, idx); } + ~SafeSeqItem() { Py_XDECREF(item); } +}; + static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info) { if(!o || o == Py_None) return true; if (PySequence_Check(o)) { - PyObject *fi = PySequence_Fast(o, info.name); - if (fi == NULL) - return false; - if (4 < PySequence_Fast_GET_SIZE(fi)) + if (4 < PySequence_Size(o)) { failmsg("Scalar value for argument '%s' is longer than 4", info.name); return false; } - for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(fi); i++) { - PyObject *item = PySequence_Fast_GET_ITEM(fi, i); + for (Py_ssize_t i = 0; i < PySequence_Size(o); i++) { + SafeSeqItem item_wrap(o, i); + PyObject *item = item_wrap.item; if (PyFloat_Check(item) || PyInt_Check(item)) { s[(int)i] = PyFloat_AsDouble(item); } else { @@ -593,7 +532,6 @@ static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info) return false; } } - Py_DECREF(fi); } else { if (PyFloat_Check(o) || PyInt_Check(o)) { s[0] = PyFloat_AsDouble(o); @@ -741,16 +679,18 @@ PyObject* pyopencv_from(const String& value) } template<> -bool pyopencv_to(PyObject* obj, String& value, const char* name) +bool pyopencv_to(PyObject* obj, String &value, const char* name) { CV_UNUSED(name); if(!obj || obj == Py_None) return true; - const char* str = PyString_AsString(obj); - if(!str) - return false; - value = String(str); - return true; + std::string str; + if (getUnicodeString(obj, str)) + { + value = str; + return true; + } + return false; } template<> @@ -821,36 +761,31 @@ bool pyopencv_to(PyObject* obj, Range& r, const char* name) return true; while (PySequence_Check(obj)) { - PyObject *fi = PySequence_Fast(obj, name); - if (fi == NULL) - break; - if (2 != PySequence_Fast_GET_SIZE(fi)) + if (2 != PySequence_Size(obj)) { failmsg("Range value for argument '%s' is longer than 2", name); - Py_DECREF(fi); return false; } { - PyObject *item = PySequence_Fast_GET_ITEM(fi, 0); + SafeSeqItem item_wrap(obj, 0); + PyObject *item = item_wrap.item; if (PyInt_Check(item)) { r.start = (int)PyInt_AsLong(item); } else { failmsg("Range.start value for argument '%s' is not integer", name); - Py_DECREF(fi); break; } } { - PyObject *item = PySequence_Fast_GET_ITEM(fi, 1); + SafeSeqItem item_wrap(obj, 1); + PyObject *item = item_wrap.item; if (PyInt_Check(item)) { r.end = (int)PyInt_AsLong(item); } else { failmsg("Range.end value for argument '%s' is not integer", name); - Py_DECREF(fi); break; } } - Py_DECREF(fi); return true; } if(PyObject_Size(obj) == 0) @@ -873,11 +808,10 @@ bool pyopencv_to(PyObject* obj, Point& p, const char* name) CV_UNUSED(name); if(!obj || obj == Py_None) return true; - if(!!PyComplex_CheckExact(obj)) + if(PyComplex_Check(obj)) { - Py_complex c = PyComplex_AsCComplex(obj); - p.x = saturate_cast(c.real); - p.y = saturate_cast(c.imag); + p.x = saturate_cast(PyComplex_RealAsDouble(obj)); + p.y = saturate_cast(PyComplex_ImagAsDouble(obj)); return true; } return PyArg_ParseTuple(obj, "ii", &p.x, &p.y) > 0; @@ -889,11 +823,10 @@ bool pyopencv_to(PyObject* obj, Point2f& p, const char* name) CV_UNUSED(name); if(!obj || obj == Py_None) return true; - if(!!PyComplex_CheckExact(obj)) + if (PyComplex_Check(obj)) { - Py_complex c = PyComplex_AsCComplex(obj); - p.x = saturate_cast(c.real); - p.y = saturate_cast(c.imag); + p.x = saturate_cast(PyComplex_RealAsDouble(obj)); + p.y = saturate_cast(PyComplex_ImagAsDouble(obj)); return true; } return PyArg_ParseTuple(obj, "ff", &p.x, &p.y) > 0; @@ -905,11 +838,10 @@ bool pyopencv_to(PyObject* obj, Point2d& p, const char* name) CV_UNUSED(name); if(!obj || obj == Py_None) return true; - if(!!PyComplex_CheckExact(obj)) + if(PyComplex_Check(obj)) { - Py_complex c = PyComplex_AsCComplex(obj); - p.x = saturate_cast(c.real); - p.y = saturate_cast(c.imag); + p.x = PyComplex_RealAsDouble(obj); + p.y = PyComplex_ImagAsDouble(obj); return true; } return PyArg_ParseTuple(obj, "dd", &p.x, &p.y) > 0; @@ -1136,9 +1068,41 @@ PyObject* pyopencv_from(const Point3d& p) 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) { - typedef typename DataType<_Tp>::channel_type _Cp; if(!obj || obj == Py_None) return true; if (PyArray_Check(obj)) @@ -1146,92 +1110,63 @@ template struct pyopencvVecConverter Mat m; pyopencv_to(obj, m, info); m.copyTo(value); + return true; } - if (!PySequence_Check(obj)) - return false; - PyObject *seq = PySequence_Fast(obj, info.name); - if (seq == NULL) - return false; - int i, j, n = (int)PySequence_Fast_GET_SIZE(seq); - value.resize(n); - - int type = traits::Type<_Tp>::value; - int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type); - PyObject** items = PySequence_Fast_ITEMS(seq); - - for( i = 0; i < n; i++ ) + else if (PySequence_Check(obj)) { - PyObject* item = items[i]; - PyObject* seq_i = 0; - PyObject** items_i = &item; - _Cp* data = (_Cp*)&value[i]; - - if( channels == 2 && PyComplex_CheckExact(item) ) + 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++ ) { - Py_complex c = PyComplex_AsCComplex(obj); - data[0] = saturate_cast<_Cp>(c.real); - data[1] = saturate_cast<_Cp>(c.imag); - continue; - } - 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; - continue; - } + SafeSeqItem item_wrap(obj, i); + PyObject* item = item_wrap.item; + _Cp* data = (_Cp*)&value[i]; - seq_i = PySequence_Fast(item, info.name); - if( !seq_i || (int)PySequence_Fast_GET_SIZE(seq_i) != channels ) + if( channels == 2 && PyComplex_Check(item) ) { - Py_XDECREF(seq_i); - break; + data[0] = saturate_cast<_Cp>(PyComplex_RealAsDouble(item)); + data[1] = saturate_cast<_Cp>(PyComplex_ImagAsDouble(item)); } - items_i = PySequence_Fast_ITEMS(seq_i); - } - - for( j = 0; j < channels; j++ ) - { - PyObject* item_ij = items_i[j]; - if( PyInt_Check(item_ij)) + else if( channels > 1 ) { - int v = (int)PyInt_AsLong(item_ij); - if( v == -1 && PyErr_Occurred() ) + 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; - data[j] = saturate_cast<_Cp>(v); + } } - else if( PyLong_Check(item_ij)) + else if (channels == 1) { - int v = (int)PyLong_AsLong(item_ij); - if( v == -1 && PyErr_Occurred() ) + if (!copyOneItem(obj, i, channels, data)) break; - data[j] = saturate_cast<_Cp>(v); - } - else if( PyFloat_Check(item_ij)) - { - double v = PyFloat_AsDouble(item_ij); - if( PyErr_Occurred() ) - break; - data[j] = saturate_cast<_Cp>(v); } else + { break; + } } - Py_XDECREF(seq_i); - if( j < channels ) - break; + return i == n; } - Py_DECREF(seq); - return i == n; + return false; } static PyObject* from(const std::vector<_Tp>& value) @@ -1263,22 +1198,15 @@ template static inline bool pyopencv_to_generic_vec(PyObject* obj, return true; if (!PySequence_Check(obj)) return false; - PyObject *seq = PySequence_Fast(obj, info.name); - if (seq == NULL) - return false; - int i, n = (int)PySequence_Fast_GET_SIZE(seq); + size_t n = PySequence_Size(obj); value.resize(n); - - PyObject** items = PySequence_Fast_ITEMS(seq); - - for( i = 0; i < n; i++ ) + for(size_t i = 0; i < n; i++ ) { - PyObject* item = items[i]; - if(!pyopencv_to(item, value[i], info)) - break; + SafeSeqItem item_wrap(obj, i); + if(!pyopencv_to(item_wrap.item, value[i], info)) + return false; } - Py_DECREF(seq); - return i == n; + return true; } template static inline PyObject* pyopencv_from_generic_vec(const std::vector<_Tp>& value) @@ -1290,7 +1218,7 @@ template static inline PyObject* pyopencv_from_generic_vec(const s PyObject* item = pyopencv_from(value[i]); if(!item) break; - PyList_SET_ITEM(seq, i, item); + PyList_SetItem(seq, i, item); } if( i < n ) { @@ -1669,31 +1597,37 @@ static PyObject *pycvCreateButton(PyObject*, PyObject *args, PyObject *kw) static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") { - if (PyString_Check(o) && PyString_Size(o) == 1) { - *dst = PyString_AsString(o)[0]; - return 1; - } else { + std::string str; + if (getUnicodeString(o, str)) + { + *dst = str[0]; + return 1; + } (*dst) = 0; return failmsg("Expected single character string for argument '%s'", name); - } } -#if PY_MAJOR_VERSION >= 3 -#define MKTYPE2(NAME) pyopencv_##NAME##_specials(); if (!to_ok(&pyopencv_##NAME##_Type)) return NULL; -#else -#define MKTYPE2(NAME) pyopencv_##NAME##_specials(); if (!to_ok(&pyopencv_##NAME##_Type)) return -#endif - #ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wunused-parameter" # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif + #include "pyopencv_generated_enums.h" #include "pyopencv_custom_headers.h" + +#ifdef CVPY_DYNAMIC_INIT +#define CVPY_TYPE(NAME, STORAGE, SNAME, _1, _2) CVPY_TYPE_DECLARE_DYNAMIC(NAME, STORAGE, SNAME) +#else +#define CVPY_TYPE(NAME, STORAGE, SNAME, _1, _2) CVPY_TYPE_DECLARE(NAME, STORAGE, SNAME) +#endif #include "pyopencv_generated_types.h" +#undef CVPY_TYPE + +#include "pyopencv_generated_types_content.h" #include "pyopencv_generated_funcs.h" + static PyMethodDef special_methods[] = { {"redirectError", CV_PY_FN_WITH_KW(pycvRedirectError), "redirectError(onError) -> None"}, #ifdef HAVE_OPENCV_HIGHGUI @@ -1758,23 +1692,90 @@ static void init_submodule(PyObject * root, const char * name, PyMethodDef * met } -#include "pyopencv_generated_ns_reg.h" +#include "pyopencv_generated_modules_content.h" -static int to_ok(PyTypeObject *to) +static bool init_body(PyObject * m) { - to->tp_alloc = PyType_GenericAlloc; - to->tp_new = PyType_GenericNew; - to->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - return (PyType_Ready(to) == 0); -} +#define CVPY_MODULE(NAMESTR, NAME) \ + init_submodule(m, MODULESTR NAMESTR, methods_##NAME, consts_##NAME) + #include "pyopencv_generated_modules.h" +#undef CVPY_MODULE +#ifdef CVPY_DYNAMIC_INIT +#define CVPY_TYPE(NAME, _1, _2, BASE, CONSTRUCTOR) CVPY_TYPE_INIT_DYNAMIC(NAME, return false, BASE, CONSTRUCTOR) + PyObject * pyopencv_NoBase_TypePtr = NULL; +#else +#define CVPY_TYPE(NAME, _1, _2, BASE, CONSTRUCTOR) CVPY_TYPE_INIT_STATIC(NAME, return false, BASE, CONSTRUCTOR) + PyTypeObject * pyopencv_NoBase_TypePtr = NULL; +#endif + #include "pyopencv_generated_types.h" +#undef CVPY_TYPE + + PyObject* d = PyModule_GetDict(m); + + + PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION)); + + PyObject *opencv_error_dict = PyDict_New(); + PyDict_SetItemString(opencv_error_dict, "file", Py_None); + PyDict_SetItemString(opencv_error_dict, "func", Py_None); + PyDict_SetItemString(opencv_error_dict, "line", Py_None); + PyDict_SetItemString(opencv_error_dict, "code", Py_None); + PyDict_SetItemString(opencv_error_dict, "msg", Py_None); + PyDict_SetItemString(opencv_error_dict, "err", Py_None); + opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, opencv_error_dict); + Py_DECREF(opencv_error_dict); + PyDict_SetItemString(d, "error", opencv_error); + + +#define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I)) + PUBLISH(CV_8U); + PUBLISH(CV_8UC1); + PUBLISH(CV_8UC2); + PUBLISH(CV_8UC3); + PUBLISH(CV_8UC4); + PUBLISH(CV_8S); + PUBLISH(CV_8SC1); + PUBLISH(CV_8SC2); + PUBLISH(CV_8SC3); + PUBLISH(CV_8SC4); + PUBLISH(CV_16U); + PUBLISH(CV_16UC1); + PUBLISH(CV_16UC2); + PUBLISH(CV_16UC3); + PUBLISH(CV_16UC4); + PUBLISH(CV_16S); + PUBLISH(CV_16SC1); + PUBLISH(CV_16SC2); + PUBLISH(CV_16SC3); + PUBLISH(CV_16SC4); + PUBLISH(CV_32S); + PUBLISH(CV_32SC1); + PUBLISH(CV_32SC2); + PUBLISH(CV_32SC3); + PUBLISH(CV_32SC4); + PUBLISH(CV_32F); + PUBLISH(CV_32FC1); + PUBLISH(CV_32FC2); + PUBLISH(CV_32FC3); + PUBLISH(CV_32FC4); + PUBLISH(CV_64F); + PUBLISH(CV_64FC1); + PUBLISH(CV_64FC2); + PUBLISH(CV_64FC3); + PUBLISH(CV_64FC4); +#undef PUBLISH + + return true; +} #if defined(__GNUC__) #pragma GCC visibility push(default) #endif #if PY_MAJOR_VERSION >= 3 -PyMODINIT_FUNC PyInit_cv2(); +// === Python 3 + static struct PyModuleDef cv2_moduledef = { PyModuleDef_HEAD_INIT, @@ -1785,92 +1786,24 @@ static struct PyModuleDef cv2_moduledef = special_methods }; +PyMODINIT_FUNC PyInit_cv2(); PyObject* PyInit_cv2() -#else -PyMODINIT_FUNC initcv2(); - -void initcv2() -#endif { - import_array(); - -#include "pyopencv_generated_type_reg.h" - -#if PY_MAJOR_VERSION >= 3 - PyObject* m = PyModule_Create(&cv2_moduledef); -#else - PyObject* m = Py_InitModule(MODULESTR, special_methods); -#endif - init_submodules(m); // from "pyopencv_generated_ns_reg.h" - - PyObject* d = PyModule_GetDict(m); - - PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION)); - - PyObject *opencv_error_dict = PyDict_New(); - PyDict_SetItemString(opencv_error_dict, "file", Py_None); - PyDict_SetItemString(opencv_error_dict, "func", Py_None); - PyDict_SetItemString(opencv_error_dict, "line", Py_None); - PyDict_SetItemString(opencv_error_dict, "code", Py_None); - PyDict_SetItemString(opencv_error_dict, "msg", Py_None); - PyDict_SetItemString(opencv_error_dict, "err", Py_None); - opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, opencv_error_dict); - Py_DECREF(opencv_error_dict); - PyDict_SetItemString(d, "error", opencv_error); - -#if PY_MAJOR_VERSION >= 3 -#define PUBLISH_OBJECT(name, type) Py_INCREF(&type);\ - PyModule_AddObject(m, name, (PyObject *)&type); -#else -// Unrolled Py_INCREF(&type) without (PyObject*) cast -// due to "warning: dereferencing type-punned pointer will break strict-aliasing rules" -#define PUBLISH_OBJECT(name, type) _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (&type)->ob_refcnt++;\ - PyModule_AddObject(m, name, (PyObject *)&type); -#endif - -#include "pyopencv_generated_type_publish.h" - -#define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I)) -//#define PUBLISHU(I) PyDict_SetItemString(d, #I, PyLong_FromUnsignedLong(I)) -#define PUBLISH2(I, value) PyDict_SetItemString(d, #I, PyLong_FromLong(value)) - - PUBLISH(CV_8U); - PUBLISH(CV_8UC1); - PUBLISH(CV_8UC2); - PUBLISH(CV_8UC3); - PUBLISH(CV_8UC4); - PUBLISH(CV_8S); - PUBLISH(CV_8SC1); - PUBLISH(CV_8SC2); - PUBLISH(CV_8SC3); - PUBLISH(CV_8SC4); - PUBLISH(CV_16U); - PUBLISH(CV_16UC1); - PUBLISH(CV_16UC2); - PUBLISH(CV_16UC3); - PUBLISH(CV_16UC4); - PUBLISH(CV_16S); - PUBLISH(CV_16SC1); - PUBLISH(CV_16SC2); - PUBLISH(CV_16SC3); - PUBLISH(CV_16SC4); - PUBLISH(CV_32S); - PUBLISH(CV_32SC1); - PUBLISH(CV_32SC2); - PUBLISH(CV_32SC3); - PUBLISH(CV_32SC4); - PUBLISH(CV_32F); - PUBLISH(CV_32FC1); - PUBLISH(CV_32FC2); - PUBLISH(CV_32FC3); - PUBLISH(CV_32FC4); - PUBLISH(CV_64F); - PUBLISH(CV_64FC1); - PUBLISH(CV_64FC2); - PUBLISH(CV_64FC3); - PUBLISH(CV_64FC4); - -#if PY_MAJOR_VERSION >= 3 + import_array(); // from numpy + PyObject* m = PyModule_Create(&cv2_moduledef); + if (!init_body(m)) + return NULL; return m; -#endif } + +#else +// === Python 2 +PyMODINIT_FUNC initcv2(); +void initcv2() +{ + import_array(); // from numpy + PyObject* m = Py_InitModule(MODULESTR, special_methods); + init_body(m); +} + +#endif diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 747f507934..188f793ea2 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -16,20 +16,12 @@ ignored_arg_types = ["RNG*"] pass_by_val_types = ["Point*", "Point2f*", "Rect*", "String*", "double*", "float*", "int*"] -gen_template_check_self = Template(""" $cname* _self_ = NULL; - if(PyObject_TypeCheck(self, &pyopencv_${name}_Type)) - _self_ = ${amp}((pyopencv_${name}_t*)self)->v${get}; - if (!_self_) +gen_template_check_self = Template(""" + ${cname} * self1 = 0; + if (!pyopencv_${name}_getp(self, self1)) return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); + ${pname} _self_ = ${cvt}(self1); """) - -gen_template_check_self_algo = Template(""" $cname* _self_ = NULL; - if(PyObject_TypeCheck(self, &pyopencv_${name}_Type)) - _self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.get()); - if (!_self_) - return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); -""") - gen_template_call_constructor_prelude = Template("""new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new if(self) """) @@ -50,53 +42,6 @@ gen_template_func_body = Template("""$code_decl } """) -head_init_str = "CV_PYTHON_TYPE_HEAD_INIT()" - -gen_template_simple_type_decl = Template(""" -struct pyopencv_${name}_t -{ - PyObject_HEAD - ${cname} v; -}; - -static PyTypeObject pyopencv_${name}_Type = -{ - %s - MODULESTR".$wname", - sizeof(pyopencv_${name}_t), -}; - -static void pyopencv_${name}_dealloc(PyObject* self) -{ - ((pyopencv_${name}_t*)self)->v.${cname}::~${sname}(); - PyObject_Del(self); -} - -template<> -struct PyOpenCV_Converter< ${cname} > -{ - static PyObject* from(const ${cname}& r) - { - pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - new (&m->v) ${cname}(r); //Copy constructor - return (PyObject*)m; - } - - static bool to(PyObject* src, ${cname}& dst, const char* name) - { - if(!src || src == Py_None) - return true; - if(PyObject_TypeCheck(src, &pyopencv_${name}_Type)) - { - dst = ((pyopencv_${name}_t*)src)->v; - return true; - } - failmsg("Expected ${cname} for argument '%%s'", name); - return false; - } -}; -""" % head_init_str) - gen_template_mappable = Template(""" { ${mappable} _src; @@ -108,43 +53,23 @@ gen_template_mappable = Template(""" """) gen_template_type_decl = Template(""" -struct pyopencv_${name}_t -{ - PyObject_HEAD - Ptr<${cname1}> v; -}; - -static PyTypeObject pyopencv_${name}_Type = -{ - %s - MODULESTR".$wname", - sizeof(pyopencv_${name}_t), -}; - -static void pyopencv_${name}_dealloc(PyObject* self) -{ - ((pyopencv_${name}_t*)self)->v.release(); - PyObject_Del(self); -} +// Converter (${name}) template<> -struct PyOpenCV_Converter< Ptr<${cname}> > +struct PyOpenCV_Converter< ${cname} > { - static PyObject* from(const Ptr<${cname}>& r) + static PyObject* from(const ${cname}& r) { - pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - new (&(m->v)) Ptr<$cname1>(); // init Ptr with placement new - m->v = r; - return (PyObject*)m; + return pyopencv_${name}_Instance(r); } - - static bool to(PyObject* src, Ptr<${cname}>& dst, const char* name) + static bool to(PyObject* src, ${cname}& dst, const char* name) { if(!src || src == Py_None) return true; - if(PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + ${cname} * dst_; + if (pyopencv_${name}_getp(src, dst_)) { - dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>(); + dst = *dst_; return true; } ${mappable_code} @@ -153,10 +78,11 @@ struct PyOpenCV_Converter< Ptr<${cname}> > } }; -""" % head_init_str) +""") gen_template_map_type_cvt = Template(""" template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name); + """) gen_template_set_prop_from_map = Template(""" @@ -169,37 +95,26 @@ gen_template_set_prop_from_map = Template(""" }""") gen_template_type_impl = Template(""" -static PyObject* pyopencv_${name}_repr(PyObject* self) -{ - char str[1000]; - sprintf(str, "<$wname %p>", self); - return PyString_FromString(str); -} +// GetSet (${name}) ${getset_code} +// Methods (${name}) + +${methods_code} + +// Tables (${name}) + static PyGetSetDef pyopencv_${name}_getseters[] = {${getset_inits} {NULL} /* Sentinel */ }; -${methods_code} - static PyMethodDef pyopencv_${name}_methods[] = { ${methods_inits} {NULL, NULL} }; - -static void pyopencv_${name}_specials(void) -{ - pyopencv_${name}_Type.tp_base = ${baseptr}; - pyopencv_${name}_Type.tp_dealloc = pyopencv_${name}_dealloc; - pyopencv_${name}_Type.tp_repr = pyopencv_${name}_repr; - pyopencv_${name}_Type.tp_getset = pyopencv_${name}_getseters; - pyopencv_${name}_Type.tp_init = (initproc)${constructor}; - pyopencv_${name}_Type.tp_methods = pyopencv_${name}_methods;${extra_specials} -} """) @@ -373,20 +288,29 @@ class ClassInfo(object): methods_code.write(m.gen_code(codegen)) methods_inits.write(m.get_tab_entry()) - baseptr = "NULL" + code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname, + getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(), + methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue()) + + return code + + def gen_def(self, codegen): + all_classes = codegen.classes + baseptr = "NoBase" if self.base and self.base in all_classes: - baseptr = "&pyopencv_" + all_classes[self.base].name + "_Type" + baseptr = all_classes[self.base].name constructor_name = "0" if self.constructor is not None: constructor_name = self.constructor.get_wrapper_name() - code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname, - getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(), - methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue(), - baseptr=baseptr, constructor=constructor_name, extra_specials="") - - return code + return "CVPY_TYPE({}, {}, {}, {}, {});\n".format( + self.name, + self.cname if self.issimple else "Ptr<{}>".format(self.cname), + self.sname if self.issimple else "Ptr", + baseptr, + constructor_name + ) def handle_ptr(tp): @@ -634,7 +558,7 @@ class FuncInfo(object): code = "%s\n{\n" % (proto,) code += " using namespace %s;\n\n" % self.namespace.replace('.', '::') - selfinfo = ClassInfo("") + selfinfo = None ismethod = self.classname != "" and not self.isconstructor # full name is needed for error diagnostic in PyArg_ParseTupleAndKeywords fullname = self.name @@ -642,14 +566,13 @@ class FuncInfo(object): if self.classname: selfinfo = all_classes[self.classname] if not self.isconstructor: - amp = "&" if selfinfo.issimple else "" - if self.is_static: - pass - elif selfinfo.isalgorithm: - code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) - else: - get = "" if selfinfo.issimple else ".get()" - code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp, get=get) + if not self.is_static: + code += gen_template_check_self.substitute( + name=selfinfo.name, + cname=selfinfo.cname if selfinfo.issimple else "Ptr<{}>".format(selfinfo.cname), + pname=(selfinfo.cname + '*') if selfinfo.issimple else "Ptr<{}>".format(selfinfo.cname), + cvt='' if selfinfo.issimple else '*' + ) fullname = selfinfo.wname + "." + fullname all_code_variants = [] @@ -871,8 +794,8 @@ class PythonWrapperGenerator(object): self.code_enums = StringIO() self.code_types = StringIO() self.code_funcs = StringIO() - self.code_type_reg = StringIO() self.code_ns_reg = StringIO() + self.code_ns_init = StringIO() self.code_type_publish = StringIO() self.py_signatures = dict() self.class_idx = 0 @@ -1013,14 +936,6 @@ class PythonWrapperGenerator(object): self.code_ns_reg.write(' {"%s", static_cast(%s)},\n'%(compat_name, cname)) self.code_ns_reg.write(' {NULL, 0}\n};\n\n') - def gen_namespaces_reg(self): - self.code_ns_reg.write('static void init_submodules(PyObject * root) \n{\n') - for ns_name in sorted(self.namespaces): - if ns_name.split('.')[0] == 'cv': - wname = normalize_class_name(ns_name) - self.code_ns_reg.write(' init_submodule(root, MODULESTR"%s", methods_%s, consts_%s);\n' % (ns_name[2:], wname, wname)) - self.code_ns_reg.write('};\n') - def gen_enum_reg(self, enum_name): name_seg = enum_name.split(".") is_enum_class = False @@ -1113,18 +1028,22 @@ class PythonWrapperGenerator(object): classlist = list(self.classes.items()) classlist.sort() for name, classinfo in classlist: + self.code_types.write("//{}\n".format(80*"=")) + self.code_types.write("// {} ({})\n".format(name, 'Map' if classinfo.ismap else 'Generic')) + self.code_types.write("//{}\n".format(80*"=")) + self.code_types.write(classinfo.gen_code(self)) if classinfo.ismap: - self.code_types.write(gen_template_map_type_cvt.substitute(name=name, cname=classinfo.cname)) + self.code_types.write(gen_template_map_type_cvt.substitute(name=classinfo.name, cname=classinfo.cname)) else: - if classinfo.issimple: - templ = gen_template_simple_type_decl - else: - templ = gen_template_type_decl mappable_code = "\n".join([ gen_template_mappable.substitute(cname=classinfo.cname, mappable=mappable) for mappable in classinfo.mappables]) - self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname, sname=classinfo.sname, - cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname), mappable_code=mappable_code)) + code = gen_template_type_decl.substitute( + name=classinfo.name, + cname=classinfo.cname if classinfo.issimple else "Ptr<{}>".format(classinfo.cname), + mappable_code=mappable_code + ) + self.code_types.write(code) # register classes in the same order as they have been declared. # this way, base classes will be registered in Python before their derivatives. @@ -1132,11 +1051,10 @@ class PythonWrapperGenerator(object): classlist1.sort() for decl_idx, name, classinfo in classlist1: - code = classinfo.gen_code(self) - self.code_types.write(code) - if not classinfo.ismap: - self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) - self.code_type_publish.write("PUBLISH_OBJECT(\"{name}\", pyopencv_{name}_Type);\n".format(name=classinfo.name)) + if classinfo.ismap: + continue + self.code_type_publish.write(classinfo.gen_def(self)) + # step 3: generate the code for all the global functions for ns_name, ns in sorted(self.namespaces.items()): @@ -1148,7 +1066,7 @@ class PythonWrapperGenerator(object): code = func.gen_code(self) self.code_funcs.write(code) self.gen_namespace(ns_name) - self.gen_namespaces_reg() + self.code_ns_init.write('CVPY_MODULE("{}", {});\n'.format(ns_name[2:], normalize_class_name(ns_name))) # step 4: generate the code for enum types enumlist = list(self.enums.values()) @@ -1166,10 +1084,10 @@ class PythonWrapperGenerator(object): self.save(output_path, "pyopencv_generated_include.h", self.code_include) self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs) self.save(output_path, "pyopencv_generated_enums.h", self.code_enums) - self.save(output_path, "pyopencv_generated_types.h", self.code_types) - self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg) - self.save(output_path, "pyopencv_generated_ns_reg.h", self.code_ns_reg) - self.save(output_path, "pyopencv_generated_type_publish.h", self.code_type_publish) + self.save(output_path, "pyopencv_generated_types.h", self.code_type_publish) + self.save(output_path, "pyopencv_generated_types_content.h", self.code_types) + self.save(output_path, "pyopencv_generated_modules.h", self.code_ns_init) + self.save(output_path, "pyopencv_generated_modules_content.h", self.code_ns_reg) self.save_json(output_path, "pyopencv_signatures.json", self.py_signatures) if __name__ == "__main__": diff --git a/modules/python/src2/pycompat.hpp b/modules/python/src2/pycompat.hpp index f4ebea6be9..8ba0500863 100644 --- a/modules/python/src2/pycompat.hpp +++ b/modules/python/src2/pycompat.hpp @@ -45,6 +45,7 @@ #define __PYCOMPAT_HPP__ #if PY_MAJOR_VERSION >= 3 + // Python3 treats all ints as longs, PyInt_X functions have been removed. #define PyInt_Check PyLong_Check #define PyInt_CheckExact PyLong_CheckExact @@ -53,18 +54,266 @@ #define PyInt_FromLong PyLong_FromLong #define PyNumber_Int PyNumber_Long -// Python3 strings are unicode, these defines mimic the Python2 functionality. -#define PyString_Check PyUnicode_Check + #define PyString_FromString PyUnicode_FromString #define PyString_FromStringAndSize PyUnicode_FromStringAndSize -#define PyString_Size PyUnicode_GET_SIZE -// PyUnicode_AsUTF8 isn't available until Python 3.3 -#if (PY_VERSION_HEX < 0x03030000) -#define PyString_AsString _PyUnicode_AsString +#endif // PY_MAJOR >=3 + +static inline bool getUnicodeString(PyObject * obj, std::string &str) +{ + bool res = false; + if (PyUnicode_Check(obj)) + { + PyObject * bytes = PyUnicode_AsUTF8String(obj); + if (PyBytes_Check(bytes)) + { + const char * raw = PyBytes_AsString(bytes); + if (raw) + { + str = std::string(raw); + res = true; + } + } + Py_XDECREF(bytes); + } +#if PY_MAJOR_VERSION < 3 + else if (PyString_Check(obj)) + { + const char * raw = PyString_AsString(obj); + if (raw) + { + str = std::string(raw); + res = true; + } + } +#endif + return res; +} + +//================================================================================================== + +#define CV_PY_FN_WITH_KW_(fn, flags) (PyCFunction)(void*)(PyCFunctionWithKeywords)(fn), (flags) | METH_VARARGS | METH_KEYWORDS +#define CV_PY_FN_NOARGS_(fn, flags) (PyCFunction)(fn), (flags) | METH_NOARGS + +#define CV_PY_FN_WITH_KW(fn) CV_PY_FN_WITH_KW_(fn, 0) +#define CV_PY_FN_NOARGS(fn) CV_PY_FN_NOARGS_(fn, 0) + +#define CV_PY_TO_CLASS(TYPE) \ +template<> \ +bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ +{ \ + if (!dst || dst == Py_None) \ + return true; \ + Ptr ptr; \ + \ + if (!pyopencv_to(dst, ptr, name)) return false; \ + src = *ptr; \ + return true; \ +} + +#define CV_PY_FROM_CLASS(TYPE) \ +template<> \ +PyObject* pyopencv_from(const TYPE& src) \ +{ \ + Ptr ptr(new TYPE()); \ + \ + *ptr = src; \ + return pyopencv_from(ptr); \ +} + +#define CV_PY_TO_CLASS_PTR(TYPE) \ +template<> \ +bool pyopencv_to(PyObject* dst, TYPE*& src, const char* name) \ +{ \ + if (!dst || dst == Py_None) \ + return true; \ + Ptr ptr; \ + \ + if (!pyopencv_to(dst, ptr, name)) return false; \ + src = ptr; \ + return true; \ +} + +#define CV_PY_FROM_CLASS_PTR(TYPE) \ +static PyObject* pyopencv_from(TYPE*& src) \ +{ \ + return pyopencv_from(Ptr(src)); \ +} + +#define CV_PY_TO_ENUM(TYPE) \ +template<> \ +bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \ +{ \ + if (!dst || dst == Py_None) \ + return true; \ + int underlying = 0; \ + \ + if (!pyopencv_to(dst, underlying, name)) return false; \ + src = static_cast(underlying); \ + return true; \ +} + +#define CV_PY_FROM_ENUM(TYPE) \ +template<> \ +PyObject* pyopencv_from(const TYPE& src) \ +{ \ + return pyopencv_from(static_cast(src)); \ +} + +//================================================================================================== + +#if PY_MAJOR_VERSION >= 3 +#define CVPY_TYPE_HEAD PyVarObject_HEAD_INIT(&PyType_Type, 0) +#define CVPY_TYPE_INCREF(T) Py_INCREF(T) #else -#define PyString_AsString PyUnicode_AsUTF8 -#endif +#define CVPY_TYPE_HEAD PyObject_HEAD_INIT(&PyType_Type) 0, +#define CVPY_TYPE_INCREF(T) _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA (T)->ob_refcnt++ #endif + +#define CVPY_TYPE_DECLARE(NAME, STORAGE, SNAME) \ + struct pyopencv_##NAME##_t \ + { \ + PyObject_HEAD \ + STORAGE v; \ + }; \ + static PyTypeObject pyopencv_##NAME##_TypeXXX = \ + { \ + CVPY_TYPE_HEAD \ + MODULESTR"."#NAME, \ + sizeof(pyopencv_##NAME##_t), \ + }; \ + static PyTypeObject * pyopencv_##NAME##_TypePtr = &pyopencv_##NAME##_TypeXXX; \ + static bool pyopencv_##NAME##_getp(PyObject * self, STORAGE * & dst) \ + { \ + if (PyObject_TypeCheck(self, pyopencv_##NAME##_TypePtr)) \ + { \ + dst = &(((pyopencv_##NAME##_t*)self)->v); \ + return true; \ + } \ + return false; \ + } \ + static PyObject * pyopencv_##NAME##_Instance(const STORAGE &r) \ + { \ + pyopencv_##NAME##_t *m = PyObject_NEW(pyopencv_##NAME##_t, pyopencv_##NAME##_TypePtr); \ + new (&(m->v)) STORAGE(r); \ + return (PyObject*)m; \ + } \ + static void pyopencv_##NAME##_dealloc(PyObject* self) \ + { \ + ((pyopencv_##NAME##_t*)self)->v.STORAGE::~SNAME(); \ + PyObject_Del(self); \ + } \ + static PyObject* pyopencv_##NAME##_repr(PyObject* self) \ + { \ + char str[1000]; \ + sprintf(str, "<"#NAME" %p>", self); \ + return PyString_FromString(str); \ + } + + +#define CVPY_TYPE_INIT_STATIC(NAME, ERROR_HANDLER, BASE, CONSTRUCTOR) \ + { \ + pyopencv_##NAME##_TypePtr->tp_base = pyopencv_##BASE##_TypePtr; \ + pyopencv_##NAME##_TypePtr->tp_dealloc = pyopencv_##NAME##_dealloc; \ + pyopencv_##NAME##_TypePtr->tp_repr = pyopencv_##NAME##_repr; \ + pyopencv_##NAME##_TypePtr->tp_getset = pyopencv_##NAME##_getseters; \ + pyopencv_##NAME##_TypePtr->tp_init = (initproc) CONSTRUCTOR; \ + pyopencv_##NAME##_TypePtr->tp_methods = pyopencv_##NAME##_methods; \ + pyopencv_##NAME##_TypePtr->tp_alloc = PyType_GenericAlloc; \ + pyopencv_##NAME##_TypePtr->tp_new = PyType_GenericNew; \ + pyopencv_##NAME##_TypePtr->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; \ + if (PyType_Ready(pyopencv_##NAME##_TypePtr) != 0) \ + { \ + ERROR_HANDLER; \ + } \ + CVPY_TYPE_INCREF(pyopencv_##NAME##_TypePtr); \ + PyModule_AddObject(m, #NAME, (PyObject *)pyopencv_##NAME##_TypePtr); \ + } + +//================================================================================================== + +#define CVPY_TYPE_DECLARE_DYNAMIC(NAME, STORAGE, SNAME) \ + struct pyopencv_##NAME##_t \ + { \ + PyObject_HEAD \ + STORAGE v; \ + }; \ + static PyObject * pyopencv_##NAME##_TypePtr = 0; \ + static bool pyopencv_##NAME##_getp(PyObject * self, STORAGE * & dst) \ + { \ + if (PyObject_TypeCheck(self, (PyTypeObject*)pyopencv_##NAME##_TypePtr)) \ + { \ + dst = &(((pyopencv_##NAME##_t*)self)->v); \ + return true; \ + } \ + return false; \ + } \ + static PyObject * pyopencv_##NAME##_Instance(const STORAGE &r) \ + { \ + pyopencv_##NAME##_t *m = PyObject_New(pyopencv_##NAME##_t, (PyTypeObject*)pyopencv_##NAME##_TypePtr); \ + new (&(m->v)) STORAGE(r); \ + return (PyObject*)m; \ + } \ + static void pyopencv_##NAME##_dealloc(PyObject* self) \ + { \ + ((pyopencv_##NAME##_t*)self)->v.STORAGE::~SNAME(); \ + PyObject_Del(self); \ + } \ + static PyObject* pyopencv_##NAME##_repr(PyObject* self) \ + { \ + char str[1000]; \ + sprintf(str, "<"#NAME" %p>", self); \ + return PyString_FromString(str); \ + } \ + static PyType_Slot pyopencv_##NAME##_Slots[] = \ + { \ + {Py_tp_dealloc, 0}, \ + {Py_tp_repr, 0}, \ + {Py_tp_getset, 0}, \ + {Py_tp_init, 0}, \ + {Py_tp_methods, 0}, \ + {Py_tp_alloc, 0}, \ + {Py_tp_new, 0}, \ + {0, 0} \ + }; \ + static PyType_Spec pyopencv_##NAME##_Spec = \ + { \ + MODULESTR"."#NAME, \ + sizeof(pyopencv_##NAME##_t), \ + 0, \ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, \ + pyopencv_##NAME##_Slots \ + }; + +#define CVPY_TYPE_INIT_DYNAMIC(NAME, ERROR_HANDLER, BASE, CONSTRUCTOR) \ + { \ + pyopencv_##NAME##_Slots[0].pfunc /*tp_dealloc*/ = (void*)pyopencv_##NAME##_dealloc; \ + pyopencv_##NAME##_Slots[1].pfunc /*tp_repr*/ = (void*)pyopencv_##NAME##_repr; \ + pyopencv_##NAME##_Slots[2].pfunc /*tp_getset*/ = (void*)pyopencv_##NAME##_getseters; \ + pyopencv_##NAME##_Slots[3].pfunc /*tp_init*/ = (void*) CONSTRUCTOR; \ + pyopencv_##NAME##_Slots[4].pfunc /*tp_methods*/ = pyopencv_##NAME##_methods; \ + pyopencv_##NAME##_Slots[5].pfunc /*tp_alloc*/ = (void*)PyType_GenericAlloc; \ + pyopencv_##NAME##_Slots[6].pfunc /*tp_new*/ = (void*)PyType_GenericNew; \ + PyObject * bases = 0; \ + if (pyopencv_##BASE##_TypePtr) \ + bases = PyTuple_Pack(1, pyopencv_##BASE##_TypePtr); \ + pyopencv_##NAME##_TypePtr = PyType_FromSpecWithBases(&pyopencv_##NAME##_Spec, bases); \ + if (!pyopencv_##NAME##_TypePtr) \ + { \ + printf("Failed to init: " #NAME ", base (" #BASE ")" "\n"); \ + ERROR_HANDLER; \ + } \ + PyModule_AddObject(m, #NAME, (PyObject *)pyopencv_##NAME##_TypePtr); \ + } + +// Debug module load: +// +// else \ +// { \ +// printf("Init: " #NAME ", base (" #BASE ") -> %p" "\n", pyopencv_##NAME##_TypePtr); \ +// } \ + + #endif // END HEADER GUARD