From ab8de8f506dcc799e8426c5d04da7f9157f805bd Mon Sep 17 00:00:00 2001 From: Vitaliy Lyudvichenko Date: Sat, 6 Aug 2016 19:46:17 +0300 Subject: [PATCH] Adding of user-defined type conversions for python bindings inside module directories Adding of destructor and placement new constructors for classes wrapped with CV_EXPORTS_W_SIMPLE macro --- .../py_bindings_basics.markdown | 5 +- .../misc/python/pyopencv_features2d.hpp | 3 + modules/flann/misc/python/pyopencv_flann.hpp | 79 +++++++++++ modules/ml/misc/python/pyopencv_ml.hpp | 22 +++ modules/python/common.cmake | 11 +- modules/python/src2/cv2.cpp | 131 +----------------- modules/python/src2/gen2.py | 8 +- .../misc/python/pyopencv_stitching.hpp | 9 ++ 8 files changed, 132 insertions(+), 136 deletions(-) create mode 100644 modules/features2d/misc/python/pyopencv_features2d.hpp create mode 100644 modules/flann/misc/python/pyopencv_flann.hpp create mode 100644 modules/ml/misc/python/pyopencv_ml.hpp create mode 100644 modules/stitching/misc/python/pyopencv_stitching.hpp diff --git a/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown b/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown index e256bcea03..4f48ae7799 100644 --- a/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown +++ b/doc/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.markdown @@ -142,5 +142,6 @@ public: So these are the major extension macros available in OpenCV. Typically, a developer has to put proper macros in their appropriate positions. Rest is done by generator scripts. Sometimes, there may be an exceptional cases where generator scripts cannot create the wrappers. Such functions need -to be handled manually. But most of the time, a code written according to OpenCV coding guidelines -will be automatically wrapped by generator scripts. +to be handled manually, to do this write your own pyopencv_*.hpp extending headers and put them into +misc/python subdirectory of your module. But most of the time, a code written according to OpenCV +coding guidelines will be automatically wrapped by generator scripts. \ No newline at end of file diff --git a/modules/features2d/misc/python/pyopencv_features2d.hpp b/modules/features2d/misc/python/pyopencv_features2d.hpp new file mode 100644 index 0000000000..b865e361b6 --- /dev/null +++ b/modules/features2d/misc/python/pyopencv_features2d.hpp @@ -0,0 +1,3 @@ +#ifdef HAVE_OPENCV_FEATURES2D +typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; +#endif \ No newline at end of file diff --git a/modules/flann/misc/python/pyopencv_flann.hpp b/modules/flann/misc/python/pyopencv_flann.hpp new file mode 100644 index 0000000000..a9da8d0f00 --- /dev/null +++ b/modules/flann/misc/python/pyopencv_flann.hpp @@ -0,0 +1,79 @@ +#ifdef HAVE_OPENCV_FLANN +typedef cvflann::flann_distance_t cvflann_flann_distance_t; +typedef cvflann::flann_algorithm_t cvflann_flann_algorithm_t; + +template<> +PyObject* pyopencv_from(const cvflann_flann_algorithm_t& value) +{ + return PyInt_FromLong(int(value)); +} + +template<> +PyObject* pyopencv_from(const cvflann_flann_distance_t& value) +{ + return PyInt_FromLong(int(value)); +} + +template<> +bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) +{ + (void)name; + bool ok = true; + PyObject* key = NULL; + PyObject* item = NULL; + Py_ssize_t pos = 0; + + if(PyDict_Check(o)) { + while(PyDict_Next(o, &pos, &key, &item)) { + if( !PyString_Check(key) ) { + ok = false; + break; + } + + String k = PyString_AsString(key); + if( PyString_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); + if( strcmp(k.c_str(), "algorithm") == 0 ) + p.setAlgorithm(value); + else + p.setInt(k, value); + } + else if( PyFloat_Check(item) ) + { + double value = PyFloat_AsDouble(item); + p.setDouble(k, value); + } + else + { + ok = false; + break; + } + } + } + + return ok && !PyErr_Occurred(); +} + +template<> +bool pyopencv_to(PyObject* obj, cv::flann::SearchParams & value, const char * name) +{ + return pyopencv_to(obj, value, name); +} + +template<> +bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const char *name) +{ + int d = (int)dist; + bool ok = pyopencv_to(o, d, name); + dist = (cvflann::flann_distance_t)d; + return ok; +} +#endif \ No newline at end of file diff --git a/modules/ml/misc/python/pyopencv_ml.hpp b/modules/ml/misc/python/pyopencv_ml.hpp new file mode 100644 index 0000000000..6a86c46c2e --- /dev/null +++ b/modules/ml/misc/python/pyopencv_ml.hpp @@ -0,0 +1,22 @@ +template<> +bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name) +{ + (void)name; + if(!obj) + return true; + return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.max_iter, &dst.epsilon) > 0; +} + +template<> +bool pyopencv_to(PyObject* obj, CvSlice& r, const char* name) +{ + (void)name; + if(!obj || obj == Py_None) + return true; + if(PyObject_Size(obj) == 0) + { + r = CV_WHOLE_SEQ; + return true; + } + return PyArg_ParseTuple(obj, "ii", &r.start_index, &r.end_index) > 0; +} \ No newline at end of file diff --git a/modules/python/common.cmake b/modules/python/common.cmake index ba65100fb7..ffe709f3dc 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -24,8 +24,11 @@ foreach(m ${OPENCV_MODULES_BUILD}) endforeach() set(opencv_hdrs "") +set(opencv_userdef_hdrs "") foreach(m ${OPENCV_PYTHON_MODULES}) list(APPEND opencv_hdrs ${OPENCV_MODULE_${m}_HEADERS}) + file(GLOB userdef_hdrs ${OPENCV_MODULE_${m}_LOCATION}/misc/python/pyopencv*.hpp) + list(APPEND opencv_userdef_hdrs ${userdef_hdrs}) endforeach(m) # header blacklist @@ -52,7 +55,13 @@ add_custom_command( DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt DEPENDS ${opencv_hdrs}) -ocv_add_library(${the_module} MODULE ${PYTHON_SOURCE_DIR}/src2/cv2.cpp ${cv2_generated_hdrs}) +set(cv2_custom_hdr "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_custom_headers.h") +file(WRITE ${cv2_custom_hdr} "//user-defined headers\n") +foreach(uh ${opencv_userdef_hdrs}) + file(APPEND ${cv2_custom_hdr} "#include \"${uh}\"\n") +endforeach(uh) + +ocv_add_library(${the_module} MODULE ${PYTHON_SOURCE_DIR}/src2/cv2.cpp ${cv2_generated_hdrs} ${opencv_userdef_hdrs} ${cv2_custom_hdr}) if(APPLE) set_target_properties(${the_module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 4b07e60fa4..1c189e7b67 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -111,19 +111,6 @@ typedef std::vector > vector_vector_Point2f; typedef std::vector > vector_vector_Point3f; typedef std::vector > vector_vector_DMatch; -#ifdef HAVE_OPENCV_FEATURES2D -typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; -#endif - -#ifdef HAVE_OPENCV_FLANN -typedef cvflann::flann_distance_t cvflann_flann_distance_t; -typedef cvflann::flann_algorithm_t cvflann_flann_algorithm_t; -#endif - -#ifdef HAVE_OPENCV_STITCHING -typedef Stitcher::Status Status; -#endif - static PyObject* failmsgp(const char *fmt, ...) { char str[1000]; @@ -469,14 +456,6 @@ PyObject* pyopencv_from(const bool& value) return PyBool_FromLong(value); } -#ifdef HAVE_OPENCV_STITCHING -template<> -PyObject* pyopencv_from(const Status& value) -{ - return PyInt_FromLong(value); -} -#endif - template<> bool pyopencv_to(PyObject* obj, bool& value, const char* name) { @@ -512,20 +491,6 @@ PyObject* pyopencv_from(const int& value) return PyInt_FromLong(value); } -#ifdef HAVE_OPENCV_FLANN -template<> -PyObject* pyopencv_from(const cvflann_flann_algorithm_t& value) -{ - return PyInt_FromLong(int(value)); -} - -template<> -PyObject* pyopencv_from(const cvflann_flann_distance_t& value) -{ - return PyInt_FromLong(int(value)); -} -#endif - template<> bool pyopencv_to(PyObject* obj, int& value, const char* name) { @@ -1094,62 +1059,6 @@ PyObject* pyopencv_from(const Moments& m) "nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03); } -#ifdef HAVE_OPENCV_FLANN -template<> -bool pyopencv_to(PyObject *o, cv::flann::IndexParams& p, const char *name) -{ - (void)name; - bool ok = true; - PyObject* key = NULL; - PyObject* item = NULL; - Py_ssize_t pos = 0; - - if(PyDict_Check(o)) { - while(PyDict_Next(o, &pos, &key, &item)) { - if( !PyString_Check(key) ) { - ok = false; - break; - } - - String k = PyString_AsString(key); - if( PyString_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); - if( strcmp(k.c_str(), "algorithm") == 0 ) - p.setAlgorithm(value); - else - p.setInt(k, value); - } - else if( PyFloat_Check(item) ) - { - double value = PyFloat_AsDouble(item); - p.setDouble(k, value); - } - else - { - ok = false; - break; - } - } - } - - return ok && !PyErr_Occurred(); -} - -template<> -bool pyopencv_to(PyObject* obj, cv::flann::SearchParams & value, const char * name) -{ - return pyopencv_to(obj, value, name); -} -#endif - template bool pyopencv_to(PyObject *o, Ptr& p, const char *name) { @@ -1157,45 +1066,7 @@ bool pyopencv_to(PyObject *o, Ptr& p, const char *name) return pyopencv_to(o, *p, name); } -#ifdef HAVE_OPENCV_FLANN -template<> -bool pyopencv_to(PyObject *o, cvflann::flann_distance_t& dist, const char *name) -{ - int d = (int)dist; - bool ok = pyopencv_to(o, d, name); - dist = (cvflann::flann_distance_t)d; - return ok; -} -#endif - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// TODO: REMOVE used only by ml wrapper - -template<> -bool pyopencv_to(PyObject *obj, CvTermCriteria& dst, const char *name) -{ - (void)name; - if(!obj) - return true; - return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.max_iter, &dst.epsilon) > 0; -} - -template<> -bool pyopencv_to(PyObject* obj, CvSlice& r, const char* name) -{ - (void)name; - if(!obj || obj == Py_None) - return true; - if(PyObject_Size(obj) == 0) - { - r = CV_WHOLE_SEQ; - return true; - } - return PyArg_ParseTuple(obj, "ii", &r.start_index, &r.end_index) > 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// +#include "pyopencv_custom_headers.h" static void OnMouse(int event, int x, int y, int flags, void* param) { diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 0175f1e6db..86cde882b7 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -30,7 +30,7 @@ gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})"" gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); if(self) """) -gen_template_simple_call_constructor = Template("""self->v = ${cname}${args}""") +gen_template_simple_call_constructor = Template("""new (&(self->v)) ${cname}${args}""") gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL }; if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""") @@ -74,13 +74,14 @@ static PyTypeObject pyopencv_${name}_Type = static void pyopencv_${name}_dealloc(PyObject* self) { + ((pyopencv_${name}_t*)self)->v.${cname}::~${sname}(); PyObject_Del(self); } template<> PyObject* pyopencv_from(const ${cname}& r) { pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - m->v = r; + new (&m->v) ${cname}(r); //Copy constructor return (PyObject*)m; } @@ -258,6 +259,7 @@ class ClassInfo(object): def __init__(self, name, decl=None): self.cname = name.replace(".", "::") self.name = self.wname = normalize_class_name(name) + self.sname = name[name.rfind('.') + 1:] self.ismap = False self.issimple = False self.isalgorithm = False @@ -904,7 +906,7 @@ class PythonWrapperGenerator(object): templ = gen_template_simple_type_decl else: templ = gen_template_type_decl - self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname, + 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))) # register classes in the same order as they have been declared. diff --git a/modules/stitching/misc/python/pyopencv_stitching.hpp b/modules/stitching/misc/python/pyopencv_stitching.hpp new file mode 100644 index 0000000000..e5d0cd2481 --- /dev/null +++ b/modules/stitching/misc/python/pyopencv_stitching.hpp @@ -0,0 +1,9 @@ +#ifdef HAVE_OPENCV_STITCHING +typedef Stitcher::Status Status; + +template<> +PyObject* pyopencv_from(const Status& value) +{ + return PyInt_FromLong(value); +} +#endif \ No newline at end of file