From f5b58e5fc99c63362d73df1835b5d265d76f2d9d Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 1 Mar 2019 18:45:33 +0300 Subject: [PATCH] bindings: backport generator from OpenCV 4.x - better handling of enum arguments - less merge conflicts --- modules/core/misc/python/pyopencv_umat.hpp | 36 ++ modules/core/misc/python/shadow_umat.hpp | 59 +++ modules/java/generator/gen_java.py | 104 ++++- modules/python/bindings/CMakeLists.txt | 11 +- modules/python/src2/cv2.cpp | 415 ++++++------------ modules/python/src2/gen2.py | 219 ++++++--- modules/python/src2/hdr_parser.py | 79 ++-- .../misc/python/pyopencv_stitching.hpp | 8 +- .../videoio/misc/python/pyopencv_videoio.hpp | 27 -- 9 files changed, 517 insertions(+), 441 deletions(-) create mode 100644 modules/core/misc/python/pyopencv_umat.hpp create mode 100644 modules/core/misc/python/shadow_umat.hpp diff --git a/modules/core/misc/python/pyopencv_umat.hpp b/modules/core/misc/python/pyopencv_umat.hpp new file mode 100644 index 0000000000..697adaf202 --- /dev/null +++ b/modules/core/misc/python/pyopencv_umat.hpp @@ -0,0 +1,36 @@ +#ifdef HAVE_OPENCV_CORE + +#include "opencv2/core/mat.hpp" + +typedef std::vector vector_Range; + +CV_PY_TO_CLASS(UMat); +CV_PY_FROM_CLASS(UMat); + +static bool cv_mappable_to(const Ptr& src, Ptr& dst) +{ + //dst.reset(new UMat(src->getUMat(ACCESS_RW))); + dst.reset(new UMat()); + src->copyTo(*dst); + return true; +} + +static void* cv_UMat_queue() +{ + return cv::ocl::Queue::getDefault().ptr(); +} + +static void* cv_UMat_context() +{ + return cv::ocl::Context::getDefault().ptr(); +} + +static Mat cv_UMat_get(const UMat* _self) +{ + Mat m; + m.allocator = &g_numpyAllocator; + _self->copyTo(m); + return m; +} + +#endif diff --git a/modules/core/misc/python/shadow_umat.hpp b/modules/core/misc/python/shadow_umat.hpp new file mode 100644 index 0000000000..dd83b9cc1d --- /dev/null +++ b/modules/core/misc/python/shadow_umat.hpp @@ -0,0 +1,59 @@ +#error This is a shadow header file, which is not intended for processing by any compiler. \ + Only bindings parser should handle this file. + +namespace cv +{ + +class CV_EXPORTS_W UMat +{ +public: + //! default constructor + CV_WRAP UMat(UMatUsageFlags usageFlags = USAGE_DEFAULT); + //! constructs 2D matrix of the specified size and type + // (_type is CV_8UC1, CV_64FC3, CV_32SC(12) etc.) + CV_WRAP UMat(int rows, int cols, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + CV_WRAP UMat(Size size, int type, UMatUsageFlags usageFlags = USAGE_DEFAULT); + //! constucts 2D matrix and fills it with the specified value _s. + CV_WRAP UMat(int rows, int cols, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + CV_WRAP UMat(Size size, int type, const Scalar& s, UMatUsageFlags usageFlags = USAGE_DEFAULT); + + //! Mat is mappable to UMat + CV_WRAP_MAPPABLE(Ptr); + + //! returns the OpenCL queue used by OpenCV UMat + CV_WRAP_PHANTOM(static void* queue()); + + //! returns the OpenCL context used by OpenCV UMat + CV_WRAP_PHANTOM(static void* context()); + + //! copy constructor + CV_WRAP UMat(const UMat& m); + + //! creates a matrix header for a part of the bigger matrix + CV_WRAP UMat(const UMat& m, const Range& rowRange, const Range& colRange = Range::all()); + CV_WRAP UMat(const UMat& m, const Rect& roi); + CV_WRAP UMat(const UMat& m, const std::vector& ranges); + + //CV_WRAP_AS(get) Mat getMat(int flags CV_WRAP_DEFAULT(ACCESS_RW)) const; + //! returns a numpy matrix + CV_WRAP_PHANTOM(Mat get() const); + + //! returns true iff the matrix data is continuous + // (i.e. when there are no gaps between successive rows). + // similar to CV_IS_MAT_CONT(cvmat->type) + CV_WRAP bool isContinuous() const; + + //! returns true if the matrix is a submatrix of another matrix + CV_WRAP bool isSubmatrix() const; + + /*! Returns the OpenCL buffer handle on which UMat operates on. + The UMat instance should be kept alive during the use of the handle to prevent the buffer to be + returned to the OpenCV buffer pool. + */ + CV_WRAP void* handle(int/*AccessFlag*/ accessFlags) const; + + // offset of the submatrix (or 0) + CV_PROP_RW size_t offset; +}; + +} // namespace cv diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py index d3a4664d38..b1ea133edf 100755 --- a/modules/java/generator/gen_java.py +++ b/modules/java/generator/gen_java.py @@ -140,17 +140,18 @@ class GeneralInfo(): def fullName(self, isCPP=False): result = ".".join([self.fullClass(), self.name]) - return result if not isCPP else result.replace(".", "::") + return result if not isCPP else get_cname(result) def fullClass(self, isCPP=False): result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0]) - return result if not isCPP else result.replace(".", "::") + return result if not isCPP else get_cname(result) class ConstInfo(GeneralInfo): - def __init__(self, decl, addedManually=False, namespaces=[]): + def __init__(self, decl, addedManually=False, namespaces=[], enumType=None): GeneralInfo.__init__(self, "const", decl, namespaces) - self.cname = self.name.replace(".", "::") + self.cname = get_cname(self.name) self.value = decl[1] + self.enumType = enumType self.addedManually = addedManually if self.namespace in namespaces_dict: self.name = '%s_%s' % (namespaces_dict[self.namespace], self.name) @@ -166,6 +167,25 @@ class ConstInfo(GeneralInfo): return True return False +def normalize_field_name(name): + return name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj") + +def normalize_class_name(name): + return re.sub(r"^cv\.", "", name).replace(".", "_") + +def get_cname(name): + return name.replace(".", "::") + +def cast_from(t): + if t in type_dict and "cast_from" in type_dict[t]: + return type_dict[t]["cast_from"] + return t + +def cast_to(t): + if t in type_dict and "cast_to" in type_dict[t]: + return type_dict[t]["cast_to"] + return t + class ClassPropInfo(): def __init__(self, decl): # [f_ctype, f_name, '', '/RW'] self.ctype = decl[0] @@ -178,7 +198,7 @@ class ClassPropInfo(): class ClassInfo(GeneralInfo): def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ] GeneralInfo.__init__(self, "class", decl, namespaces) - self.cname = self.name.replace(".", "::") + self.cname = get_cname(self.name) self.methods = [] self.methods_suffixes = {} self.consts = [] # using a list to save the occurrence order @@ -303,7 +323,7 @@ class ArgInfo(): class FuncInfo(GeneralInfo): def __init__(self, decl, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ] GeneralInfo.__init__(self, "func", decl, namespaces) - self.cname = decl[0].replace(".", "::") + self.cname = get_cname(decl[0]) self.jname = self.name self.isconstructor = self.name == self.classname if "[" in self.name: @@ -403,13 +423,16 @@ class JavaWrapperGenerator(object): ) logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base) - def add_const(self, decl): # [ "const cname", val, [], [] ] - constinfo = ConstInfo(decl, namespaces=self.namespaces) + def add_const(self, decl, enumType=None): # [ "const cname", val, [], [] ] + constinfo = ConstInfo(decl, namespaces=self.namespaces, enumType=enumType) if constinfo.isIgnored(): logging.info('ignored: %s', constinfo) - elif not self.isWrapped(constinfo.classname): - logging.info('class not found: %s', constinfo) else: + if not self.isWrapped(constinfo.classname): + logging.info('class not found: %s', constinfo) + constinfo.name = constinfo.classname + '_' + constinfo.name + constinfo.classname = '' + ci = self.getClass(constinfo.classname) duplicate = ci.getConst(constinfo.name) if duplicate: @@ -421,6 +444,18 @@ class JavaWrapperGenerator(object): ci.addConst(constinfo) logging.info('ok: %s', constinfo) + def add_enum(self, decl): # [ "enum cname", "", [], [] ] + enumType = decl[0].rsplit(" ", 1)[1] + if enumType.endswith(""): + enumType = None + else: + ctype = normalize_class_name(enumType) + type_dict[ctype] = { "cast_from" : "int", "cast_to" : get_cname(enumType), "j_type" : "int", "jn_type" : "int", "jni_type" : "jint", "suffix" : "I" } + const_decls = decl[3] + + for decl in const_decls: + self.add_const(decl, enumType) + def add_func(self, decl): fi = FuncInfo(decl, namespaces=self.namespaces) classname = fi.classname or self.Module @@ -479,6 +514,9 @@ class JavaWrapperGenerator(object): self.add_class(decl) elif name.startswith("const"): self.add_const(decl) + elif name.startswith("enum"): + # enum + self.add_enum(decl) else: # function self.add_func(decl) @@ -518,7 +556,7 @@ class JavaWrapperGenerator(object): if self.isWrapped(t): return self.getClass(t).fullName(isCPP=True) else: - return t + return cast_from(t) def gen_func(self, ci, fi, prop_name=''): logging.info("%s", fi) @@ -551,7 +589,7 @@ class JavaWrapperGenerator(object): msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype self.skipped_func_list.append(c_decl + "\n" + msg) j_code.write( " "*4 + msg ) - logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type" + fi.ctype) + logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type " + fi.ctype) return for a in fi.args: if a.ctype not in type_dict: @@ -563,7 +601,7 @@ class JavaWrapperGenerator(object): msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I") self.skipped_func_list.append(c_decl + "\n" + msg) j_code.write( " "*4 + msg ) - logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type" + a.ctype + "/" + (a.out or "I")) + logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type " + a.ctype + "/" + (a.out or "I")) return self.ported_func_list.append(c_decl) @@ -642,7 +680,7 @@ class JavaWrapperGenerator(object): if "I" in a.out or not a.out or self.isWrapped(a.ctype): # input arg, pass by primitive fields for f in fields: jn_args.append ( ArgInfo([ f[0], a.name + f[1], "", [], "" ]) ) - jni_args.append( ArgInfo([ f[0], a.name + f[1].replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj"), "", [], "" ]) ) + jni_args.append( ArgInfo([ f[0], a.name + normalize_field_name(f[1]), "", [], "" ]) ) if "O" in a.out and not self.isWrapped(a.ctype): # out arg, pass as double[] jn_args.append ( ArgInfo([ "double[]", "%s_out" % a.name, "", [], "" ]) ) jni_args.append ( ArgInfo([ "double[]", "%s_out" % a.name, "", [], "" ]) ) @@ -690,7 +728,7 @@ class JavaWrapperGenerator(object): " private static native $type $name($args);\n").substitute(\ type = type_dict[fi.ctype].get("jn_type", "double[]"), \ name = fi.jname + '_' + str(suffix_counter), \ - args = ", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"], a.name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj")) for a in jn_args]) + args = ", ".join(["%s %s" % (type_dict[a.ctype]["jn_type"], normalize_field_name(a.name)) for a in jn_args]) ) ); # java part: @@ -848,7 +886,7 @@ class JavaWrapperGenerator(object): if not a.out and not "jni_var" in type_dict[a.ctype]: # explicit cast to C type to avoid ambiguous call error on platforms (mingw) # where jni types are different from native types (e.g. jint is not the same as int) - jni_name = "(%s)%s" % (a.ctype, jni_name) + jni_name = "(%s)%s" % (cast_to(a.ctype), jni_name) if not a.ctype: # hidden jni_name = a.defval cvargs.append( type_dict[a.ctype].get("jni_name", jni_name) % {"n" : a.name}) @@ -922,11 +960,35 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname %s;\n\n""" % (",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in ci.private_consts]) ) if ci.consts: - logging.info("%s", ci.consts) - ci.j_code.write(""" + enumTypes = set(map(lambda c: c.enumType, ci.consts)) + grouped_consts = {enumType: [c for c in ci.consts if c.enumType == enumType] for enumType in enumTypes} + for typeName, consts in grouped_consts.items(): + logging.info("%s", consts) + if typeName: + typeName = typeName.rsplit(".", 1)[-1] +###################### Utilize Java enums ###################### +# ci.j_code.write(""" +# public enum {1} {{ +# {0}; +# +# private final int id; +# {1}(int id) {{ this.id = id; }} +# {1}({1} _this) {{ this.id = _this.id; }} +# public int getValue() {{ return id; }} +# }}\n\n""".format((",\n"+" "*8).join(["%s(%s)" % (c.name, c.value) for c in consts]), typeName) +# ) +################################################################ + ci.j_code.write(""" + // C++: enum {1} public static final int - %s;\n\n""" % (",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in ci.consts]) - ) + {0};\n\n""".format((",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in consts]), typeName) + ) + else: + ci.j_code.write(""" + // C++: enum + public static final int + {0};\n\n""".format((",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in consts])) + ) # methods for fi in ci.getAllMethods(): self.gen_func(ci, fi) @@ -1123,7 +1185,7 @@ if __name__ == "__main__": with open(srcfiles_fname) as f: srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()] else: - re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/)') + re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.details.hpp$|_winrt.hpp$|/cuda/|/legacy/)') # .h files before .hpp h_files = [] hpp_files = [] diff --git a/modules/python/bindings/CMakeLists.txt b/modules/python/bindings/CMakeLists.txt index 7b2d5c5ed6..d96d7e88ff 100644 --- a/modules/python/bindings/CMakeLists.txt +++ b/modules/python/bindings/CMakeLists.txt @@ -26,15 +26,15 @@ foreach(m ${OPENCV_PYTHON_MODULES}) list(APPEND opencv_hdrs "${hdr}") endif() endforeach() + file(GLOB hdr ${OPENCV_MODULE_${m}_LOCATION}/misc/python/shadow*.hpp) + list(APPEND opencv_hdrs ${hdr}) file(GLOB userdef_hdrs ${OPENCV_MODULE_${m}_LOCATION}/misc/python/pyopencv*.hpp) list(APPEND opencv_userdef_hdrs ${userdef_hdrs}) endforeach(m) # header blacklist ocv_list_filterout(opencv_hdrs "modules/.*\\\\.h$") -ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda") -ocv_list_filterout(opencv_hdrs "modules/cuda.*") -ocv_list_filterout(opencv_hdrs "modules/cudev") +ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda/") ocv_list_filterout(opencv_hdrs "modules/core/.*/hal/") ocv_list_filterout(opencv_hdrs "modules/core/.*/opencl/") ocv_list_filterout(opencv_hdrs "modules/.+/utils/.*") @@ -43,7 +43,12 @@ ocv_list_filterout(opencv_hdrs "modules/.*_inl\\\\.h*") ocv_list_filterout(opencv_hdrs "modules/.*\\\\.details\\\\.h*") ocv_list_filterout(opencv_hdrs "modules/.*\\\\.private\\\\.h*") ocv_list_filterout(opencv_hdrs "modules/.*/private\\\\.h*") +ocv_list_filterout(opencv_hdrs "modules/.*/legacy/.*") ocv_list_filterout(opencv_hdrs "modules/.*/detection_based_tracker\\\\.hpp") # Conditional compilation +if(NOT HAVE_CUDA) + ocv_list_filterout(opencv_hdrs "modules/cuda.*") + ocv_list_filterout(opencv_hdrs "modules/cudev") +endif() set(cv2_generated_files "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 17abbc5b18..c9adbbda23 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -11,6 +11,20 @@ #pragma warning(pop) #endif +template // TEnable is used for SFINAE checks +struct PyOpenCV_Converter +{ + //static inline bool to(PyObject* obj, T& p, const char* name); + //static inline PyObject* from(const T& src); +}; + +template static +bool pyopencv_to(PyObject* obj, T& p, const char* name = "") { return PyOpenCV_Converter::to(obj, p, name); } + +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 @@ -28,6 +42,68 @@ # 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" @@ -37,7 +113,7 @@ #include -static PyObject* opencv_error = 0; +static PyObject* opencv_error = NULL; static int failmsg(const char *fmt, ...) { @@ -98,6 +174,12 @@ try \ } \ catch (const cv::Exception &e) \ { \ + PyObject_SetAttrString(opencv_error, "file", PyString_FromString(e.file.c_str())); \ + PyObject_SetAttrString(opencv_error, "func", PyString_FromString(e.func.c_str())); \ + PyObject_SetAttrString(opencv_error, "line", PyInt_FromLong(e.line)); \ + PyObject_SetAttrString(opencv_error, "code", PyInt_FromLong(e.code)); \ + PyObject_SetAttrString(opencv_error, "msg", PyString_FromString(e.msg.c_str())); \ + PyObject_SetAttrString(opencv_error, "err", PyString_FromString(e.err.c_str())); \ PyErr_SetString(opencv_error, e.what()); \ return 0; \ } @@ -113,6 +195,7 @@ typedef std::vector vector_size_t; typedef std::vector vector_Point; typedef std::vector vector_Point2f; typedef std::vector vector_Point3f; +typedef std::vector vector_Size; typedef std::vector vector_Vec2f; typedef std::vector vector_Vec3f; typedef std::vector vector_Vec4f; @@ -223,12 +306,6 @@ public: NumpyAllocator g_numpyAllocator; -template static -bool pyopencv_to(PyObject* obj, T& p, const char* name = ""); - -template static -PyObject* pyopencv_from(const T& src); - enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 }; // special case, when the converter needs full ArgInfo structure @@ -435,15 +512,6 @@ bool pyopencv_to(PyObject* o, Matx<_Tp, m, n>& mx, const char* name) return pyopencv_to(o, mx, ArgInfo(name, 0)); } -template -bool pyopencv_to(PyObject *o, Ptr& p, const char *name) -{ - if (!o || o == Py_None) - return true; - p = makePtr(); - return pyopencv_to(o, *p, name); -} - template<> PyObject* pyopencv_from(const Mat& m) { @@ -468,279 +536,39 @@ PyObject* pyopencv_from(const Matx<_Tp, m, n>& matx) } template -PyObject* pyopencv_from(const cv::Ptr& p) -{ - if (!p) - Py_RETURN_NONE; - return pyopencv_from(*p); -} - -typedef struct { - PyObject_HEAD - UMat* um; -} cv2_UMatWrapperObject; - -static bool PyObject_IsUMat(PyObject *o); - -// UMatWrapper init - try to map arguments from python to UMat constructors -static int UMatWrapper_init(PyObject* self_, PyObject *args, PyObject *kwds) +struct PyOpenCV_Converter< cv::Ptr > { - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) + static PyObject* from(const cv::Ptr& p) { - PyErr_SetString(PyExc_TypeError, "Internal error"); - return -1; + if (!p) + Py_RETURN_NONE; + return pyopencv_from(*p); } - self->um = NULL; + static bool to(PyObject *o, Ptr& p, const char *name) { - // constructor () - const char *kwlist[] = {NULL}; - if (PyArg_ParseTupleAndKeywords(args, kwds, "", (char**) kwlist)) { - self->um = new UMat(); - return 0; - } - PyErr_Clear(); - } - { - // constructor (rows, cols, type) - const char *kwlist[] = {"rows", "cols", "type", NULL}; - int rows, cols, type; - if (PyArg_ParseTupleAndKeywords(args, kwds, "iii", (char**) kwlist, &rows, &cols, &type)) { - self->um = new UMat(rows, cols, type); - return 0; - } - PyErr_Clear(); - } - { - // constructor (m, rowRange, colRange) - const char *kwlist[] = {"m", "rowRange", "colRange", NULL}; - PyObject *obj = NULL; - int y0 = -1, y1 = -1, x0 = -1, x1 = -1; - if (PyArg_ParseTupleAndKeywords(args, kwds, "O(ii)|(ii)", (char**) kwlist, &obj, &y0, &y1, &x0, &x1) && PyObject_IsUMat(obj)) { - UMat *um_other = ((cv2_UMatWrapperObject *) obj)->um; - Range rowRange(y0, y1); - Range colRange = (x0 >= 0 && x1 >= 0) ? Range(x0, x1) : Range::all(); - self->um = new UMat(*um_other, rowRange, colRange); - return 0; - } - PyErr_Clear(); - } - { - // constructor (m) - const char *kwlist[] = {"m", NULL}; - PyObject *obj = NULL; - if (PyArg_ParseTupleAndKeywords(args, kwds, "O", (char**) kwlist, &obj)) { - // constructor (UMat m) - if (PyObject_IsUMat(obj)) { - UMat *um_other = ((cv2_UMatWrapperObject *) obj)->um; - self->um = new UMat(*um_other); - return 0; - } - // python specific constructor from array like object - Mat m; - if (pyopencv_to(obj, m, ArgInfo("UMatWrapper.np_mat", 0))) { - self->um = new UMat(); - m.copyTo(*self->um); - return 0; - } - } - PyErr_Clear(); + if (!o || o == Py_None) + return true; + p = makePtr(); + return pyopencv_to(o, *p, name); } - PyErr_SetString(PyExc_TypeError, "no matching UMat constructor found/supported"); - return -1; -} - -static void UMatWrapper_dealloc(cv2_UMatWrapperObject* self) -{ - if (self->um) - delete self->um; -#if PY_MAJOR_VERSION >= 3 - Py_TYPE(self)->tp_free((PyObject*)self); -#else - self->ob_type->tp_free((PyObject*)self); -#endif -} - -// UMatWrapper.get() - returns numpy array by transferring UMat data to Mat and than wrapping it to numpy array -// (using numpy allocator - and so without unnecessary copy) -static PyObject * UMatWrapper_get(PyObject* self_, PyObject * /*args*/) -{ - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) - return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); - Mat m; - m.allocator = &g_numpyAllocator; - self->um->copyTo(m); - - return pyopencv_from(m); -} - -// UMatWrapper.handle() - returns the OpenCL handle of the UMat object -static PyObject * UMatWrapper_handle(PyObject* self_, PyObject *args, PyObject *kwds) -{ - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) - return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); - const char *kwlist[] = {"accessFlags", NULL}; - int accessFlags; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", (char**) kwlist, &accessFlags)) - return 0; - return PyLong_FromVoidPtr(self->um->handle(accessFlags)); -} - -// UMatWrapper.isContinuous() - returns true if the matrix data is continuous -static PyObject * UMatWrapper_isContinuous(PyObject* self_, PyObject * /*args*/) -{ - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) - return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); - return PyBool_FromLong(self->um->isContinuous()); -} - -// UMatWrapper.isContinuous() - returns true if the matrix is a submatrix of another matrix -static PyObject * UMatWrapper_isSubmatrix(PyObject* self_, PyObject * /*args*/) -{ - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) - return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); - return PyBool_FromLong(self->um->isSubmatrix()); -} - -// UMatWrapper.context() - returns the OpenCL context used by OpenCV UMat -static PyObject * UMatWrapper_context(PyObject* /*self_*/, PyObject * /*args*/) -{ - return PyLong_FromVoidPtr(cv::ocl::Context::getDefault().ptr()); -} - -// UMatWrapper.context() - returns the OpenCL queue used by OpenCV UMat -static PyObject * UMatWrapper_queue(PyObject* /*self_*/, PyObject * /*args*/) -{ - return PyLong_FromVoidPtr(cv::ocl::Queue::getDefault().ptr()); -} - -static PyObject * UMatWrapper_offset_getter(PyObject* self_, void*) -{ - cv2_UMatWrapperObject* self = (cv2_UMatWrapperObject*)self_; - if (self == NULL) - return failmsgp("Incorrect type of self (must be 'cv2_UMatWrapperObject')"); - return PyLong_FromSsize_t(self->um->offset); -} - -static PyMethodDef UMatWrapper_methods[] = { - {"get", CV_PY_FN_NOARGS(UMatWrapper_get), - "Returns numpy array" - }, - {"handle", CV_PY_FN_WITH_KW(UMatWrapper_handle), - "Returns UMat native handle" - }, - {"isContinuous", CV_PY_FN_NOARGS(UMatWrapper_isContinuous), - "Returns true if the matrix data is continuous" - }, - {"isSubmatrix", CV_PY_FN_NOARGS(UMatWrapper_isSubmatrix), - "Returns true if the matrix is a submatrix of another matrix" - }, - {"context", CV_PY_FN_NOARGS_(UMatWrapper_context, METH_STATIC), - "Returns OpenCL context handle" - }, - {"queue", CV_PY_FN_NOARGS_(UMatWrapper_queue, METH_STATIC), - "Returns OpenCL queue handle" - }, - {NULL, NULL, 0, NULL} /* Sentinel */ -}; - -static PyGetSetDef UMatWrapper_getset[] = { - {(char*) "offset", (getter) UMatWrapper_offset_getter, NULL, NULL, NULL}, - {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; -static PyTypeObject cv2_UMatWrapperType = { -#if PY_MAJOR_VERSION >= 3 - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ -#endif - "cv2.UMat", /* tp_name */ - sizeof(cv2_UMatWrapperObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)UMatWrapper_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_reserved */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - "OpenCV 3 UMat wrapper. Used for T-API support.", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - UMatWrapper_methods, /* tp_methods */ - 0, /* tp_members */ - UMatWrapper_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)UMatWrapper_init, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0, /* tp_del */ - 0, /* tp_version_tag */ -#if PY_MAJOR_VERSION >= 3 - 0, /* tp_finalize */ -#endif -}; - -static bool PyObject_IsUMat(PyObject *o) { - return (o != NULL) && PyObject_TypeCheck(o, &cv2_UMatWrapperType); -} - -static bool pyopencv_to(PyObject* o, UMat& um, const ArgInfo info) { - if (PyObject_IsUMat(o)) { - um = *((cv2_UMatWrapperObject *) o)->um; +template<> +bool pyopencv_to(PyObject* obj, void*& ptr, const char* name) +{ + CV_UNUSED(name); + if (!obj || obj == Py_None) return true; - } - Mat m; - if (!pyopencv_to(o, m, info)) { + if (!PyLong_Check(obj)) return false; - } - - m.copyTo(um); - return true; + ptr = PyLong_AsVoidPtr(obj); + return ptr != NULL && !PyErr_Occurred(); } -template<> -bool pyopencv_to(PyObject* o, UMat& um, const char* name) +static PyObject* pyopencv_from(void*& ptr) { - return pyopencv_to(o, um, ArgInfo(name, 0)); -} - -template<> -PyObject* pyopencv_from(const UMat& m) { - PyObject *o = PyObject_CallObject((PyObject *) &cv2_UMatWrapperType, NULL); - *((cv2_UMatWrapperObject *) o)->um = m; - return o; + return PyLong_FromVoidPtr(ptr); } static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info) @@ -1483,6 +1311,19 @@ template<> struct pyopencvVecConverter } }; +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) @@ -1814,6 +1655,7 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +#include "pyopencv_generated_enums.h" #include "pyopencv_custom_headers.h" #include "pyopencv_generated_types.h" #include "pyopencv_generated_funcs.h" @@ -1927,18 +1769,17 @@ void initcv2() PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION)); - opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, NULL); + 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); -//Registering UMatWrapper python class in cv2 module: - if (PyType_Ready(&cv2_UMatWrapperType) < 0) -#if PY_MAJOR_VERSION >= 3 - return NULL; -#else - return; -#endif - - #if PY_MAJOR_VERSION >= 3 #define PUBLISH_OBJECT(name, type) Py_INCREF(&type);\ PyModule_AddObject(m, name, (PyObject *)&type); @@ -1949,8 +1790,6 @@ void initcv2() PyModule_AddObject(m, name, (PyObject *)&type); #endif - PUBLISH_OBJECT("UMat", cv2_UMatWrapperType); - #include "pyopencv_generated_type_publish.h" #define PUBLISH(I) PyDict_SetItemString(d, #I, PyInt_FromLong(I)) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index d8bce41ff3..747f507934 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -10,19 +10,23 @@ if sys.version_info[0] >= 3: else: from cStringIO import StringIO +forbidden_arg_types = ["void*"] + 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_ == NULL) + if (!_self_) return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); """) 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_ == NULL) + if (!_self_) return failmsgp("Incorrect type of self (must be '${name}' or its derivative)"); """) @@ -68,27 +72,40 @@ static void pyopencv_${name}_dealloc(PyObject* self) PyObject_Del(self); } -template<> PyObject* pyopencv_from(const ${cname}& r) +template<> +struct PyOpenCV_Converter< ${cname} > { - pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type); - new (&m->v) ${cname}(r); //Copy constructor - return (PyObject*)m; -} + 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; + } -template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name) -{ - if( src == NULL || src == Py_None ) - return true; - if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + 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; } - dst = ((pyopencv_${name}_t*)src)->v; - return true; -} +}; """ % head_init_str) +gen_template_mappable = Template(""" + { + ${mappable} _src; + if (pyopencv_to(src, _src, name)) + { + return cv_mappable_to(_src, dst); + } + } +""") gen_template_type_decl = Template(""" struct pyopencv_${name}_t @@ -110,26 +127,31 @@ static void pyopencv_${name}_dealloc(PyObject* self) PyObject_Del(self); } -template<> PyObject* pyopencv_from(const Ptr<${cname}>& r) +template<> +struct PyOpenCV_Converter< Ptr<${cname}> > { - 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; -} + static PyObject* from(const Ptr<${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; + } -template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name) -{ - if( src == NULL || src == Py_None ) - return true; - if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type)) + static bool to(PyObject* src, Ptr<${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.dynamicCast<${cname}>(); + return true; + } + ${mappable_code} failmsg("Expected ${cname} for argument '%%s'", name); return false; } - dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>(); - return true; -} +}; """ % head_init_str) @@ -192,7 +214,7 @@ gen_template_get_prop_algo = Template(""" static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure) { $cname* _self_ = dynamic_cast<$cname*>(p->v.get()); - if (_self_ == NULL) + if (!_self_) return failmsgp("Incorrect type of object (must be '${name}' or its derivative)"); return pyopencv_from(_self_${access}${member}); } @@ -201,7 +223,7 @@ static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *clo gen_template_set_prop = Template(""" static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure) { - if (value == NULL) + if (!value) { PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute"); return -1; @@ -213,13 +235,13 @@ static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value gen_template_set_prop_algo = Template(""" static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure) { - if (value == NULL) + if (!value) { PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute"); return -1; } $cname* _self_ = dynamic_cast<$cname*>(p->v.get()); - if (_self_ == NULL) + if (!_self_) { failmsgp("Incorrect type of object (must be '${name}' or its derivative)"); return -1; @@ -265,6 +287,7 @@ class ClassInfo(object): self.isalgorithm = False self.methods = {} self.props = [] + self.mappables = [] self.consts = {} self.base = None self.constructor = None @@ -402,7 +425,7 @@ class ArgInfo(object): self.py_outputarg = False def isbig(self): - return self.tp == "Mat" or self.tp == "vector_Mat"\ + return self.tp == "Mat" or self.tp == "vector_Mat" or self.tp == "cuda::GpuMat"\ or self.tp == "UMat" or self.tp == "vector_UMat" # or self.tp.startswith("vector") def crepr(self): @@ -410,10 +433,11 @@ class ArgInfo(object): class FuncVariant(object): - def __init__(self, classname, name, decl, isconstructor): + def __init__(self, classname, name, decl, isconstructor, isphantom=False): self.classname = classname self.name = self.wname = name self.isconstructor = isconstructor + self.isphantom = isphantom self.docstring = decl[5] @@ -461,6 +485,7 @@ class FuncVariant(object): argno += 1 if a.name in self.array_counters: continue + assert not a.tp in forbidden_arg_types, 'Forbidden type "{}" for argument "{}" in "{}" ("{}")'.format(a.tp, a.name, self.name, self.classname) if a.tp in ignored_arg_types: continue if a.returnarg: @@ -520,17 +545,17 @@ class FuncVariant(object): class FuncInfo(object): - def __init__(self, classname, name, cname, isconstructor, namespace, isclassmethod): + def __init__(self, classname, name, cname, isconstructor, namespace, is_static): self.classname = classname self.name = name self.cname = cname self.isconstructor = isconstructor self.namespace = namespace - self.isclassmethod = isclassmethod + self.is_static = is_static self.variants = [] - def add_variant(self, decl): - self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor)) + def add_variant(self, decl, isphantom=False): + self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor, isphantom)) def get_wrapper_name(self): name = self.name @@ -541,8 +566,8 @@ class FuncInfo(object): else: classname = "" - if self.isclassmethod: - name += "_cls" + if self.is_static: + name += "_static" return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name @@ -601,7 +626,7 @@ class FuncInfo(object): return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags), "$py_docstring"},\n' ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(), - flags = 'METH_CLASS' if self.isclassmethod else '0', py_docstring = full_docstring) + flags = 'METH_STATIC' if self.is_static else '0', py_docstring = full_docstring) def gen_code(self, codegen): all_classes = codegen.classes @@ -618,7 +643,7 @@ class FuncInfo(object): selfinfo = all_classes[self.classname] if not self.isconstructor: amp = "&" if selfinfo.issimple else "" - if self.isclassmethod: + if self.is_static: pass elif selfinfo.isalgorithm: code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp) @@ -638,6 +663,9 @@ class FuncInfo(object): all_cargs = [] parse_arglist = [] + if v.isphantom and ismethod and not self.is_static: + code_args += "_self_" + # declare all the C function arguments, # add necessary conversions from Python objects to code_cvt_list, # form the function/method call, @@ -646,7 +674,7 @@ class FuncInfo(object): if a.tp in ignored_arg_types: defval = a.defval if not defval and a.tp.endswith("*"): - defval = 0 + defval = "0" assert defval if not code_args.endswith("("): code_args += ", " @@ -656,15 +684,15 @@ class FuncInfo(object): tp1 = tp = a.tp amp = "" defval0 = "" - if tp.endswith("*"): + if tp in pass_by_val_types: tp = tp1 = tp[:-1] amp = "&" if tp.endswith("*"): defval0 = "0" tp1 = tp.replace("*", "_ptr") - if tp1.endswith("*"): - print("Error: type with star: a.tp=%s, tp=%s, tp1=%s" % (a.tp, tp, tp1)) - sys.exit(-1) + tp_candidates = [a.tp, normalize_class_name(self.namespace + "." + a.tp)] + if any(tp in codegen.enums.keys() for tp in tp_candidates): + defval0 = "static_cast<%s>(%d)" % (a.tp, 0) amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0)) parse_name = a.name @@ -686,6 +714,9 @@ class FuncInfo(object): if "UMat" in tp: if "Mat" in defval and "UMat" not in defval: defval = defval.replace("Mat", "UMat") + if "cuda::GpuMat" in tp: + if "Mat" in defval and "GpuMat" not in defval: + defval = defval.replace("Mat", "cuda::GpuMat") # "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types if defval == tp + "()" and amapping[1] == "O": defval = "" @@ -712,13 +743,15 @@ class FuncInfo(object): code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname) code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args) + if v.isphantom: + code_fcall = code_fcall.replace("new " + selfinfo.cname, self.cname.replace("::", "_")) else: code_prelude = "" code_fcall = "" if v.rettype: code_decl += " " + v.rettype + " retval;\n" code_fcall += "retval = " - if ismethod and not self.isclassmethod: + if not v.isphantom and ismethod and not self.is_static: code_fcall += "_self_->" + self.cname else: code_fcall += self.cname @@ -754,7 +787,7 @@ class FuncInfo(object): parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]), code_cvt = " &&\n ".join(code_cvt_list)) else: - code_parse = "if(PyObject_Size(args) == 0 && (kw == NULL || PyObject_Size(kw) == 0))" + code_parse = "if(PyObject_Size(args) == 0 && (!kw || PyObject_Size(kw) == 0))" if len(v.py_outlist) == 0: code_ret = "Py_RETURN_NONE" @@ -799,7 +832,7 @@ class FuncInfo(object): #if dump: pprint(vars(classinfo)) if self.isconstructor: py_name = 'cv.' + classinfo.wname - elif self.isclassmethod: + elif self.is_static: py_name = '.'.join([self.namespace, classinfo.sname + '_' + self.variants[0].wname]) else: cname = classinfo.cname + '::' + cname @@ -833,7 +866,9 @@ class PythonWrapperGenerator(object): self.classes = {} self.namespaces = {} self.consts = {} + self.enums = {} self.code_include = StringIO() + self.code_enums = StringIO() self.code_types = StringIO() self.code_funcs = StringIO() self.code_type_reg = StringIO() @@ -890,6 +925,18 @@ class PythonWrapperGenerator(object): py_signatures.append(dict(name=py_name, value=value)) #print(cname + ' => ' + str(py_name) + ' (value=' + value + ')') + def add_enum(self, name, decl): + wname = normalize_class_name(name) + if wname.endswith(""): + wname = None + else: + self.enums[wname] = name + const_decls = decl[3] + + for decl in const_decls: + name = decl[0] + self.add_const(name.replace("const ", "").strip(), decl) + def add_func(self, decl): namespace, classes, barename = self.split_decl_name(decl[0]) cname = "::".join(namespace+classes+[barename]) @@ -902,35 +949,46 @@ class PythonWrapperGenerator(object): namespace = '.'.join(namespace) isconstructor = name == bareclassname - isclassmethod = False + is_static = False + isphantom = False + mappable = None for m in decl[2]: if m == "/S": - isclassmethod = True + is_static = True + elif m == "/phantom": + isphantom = True + cname = cname.replace("::", "_") elif m.startswith("="): name = m[1:] + elif m.startswith("/mappable="): + mappable = m[10:] + self.classes[classname].mappables.append(mappable) + return + if isconstructor: name = "_".join(classes[:-1]+[name]) - if isclassmethod: + if is_static: # Add it as a method to the class func_map = self.classes[classname].methods - func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod)) - func.add_variant(decl) + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, is_static)) + func.add_variant(decl, isphantom) # Add it as global function g_name = "_".join(classes+[name]) func_map = self.namespaces.setdefault(namespace, Namespace()).funcs func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False)) - func.add_variant(decl) + func.add_variant(decl, isphantom) else: if classname and not isconstructor: - cname = barename + if not isphantom: + cname = barename func_map = self.classes[classname].methods else: func_map = self.namespaces.setdefault(namespace, Namespace()).funcs - func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod)) - func.add_variant(decl) + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, is_static)) + func.add_variant(decl, isphantom) if classname and isconstructor: self.classes[classname].constructor = func @@ -949,10 +1007,10 @@ class PythonWrapperGenerator(object): self.code_ns_reg.write('static ConstDef consts_%s[] = {\n'%wname) for name, cname in sorted(ns.consts.items()): - self.code_ns_reg.write(' {"%s", %s},\n'%(name, cname)) + self.code_ns_reg.write(' {"%s", static_cast(%s)},\n'%(name, cname)) compat_name = re.sub(r"([a-z])([A-Z])", r"\1_\2", name).upper() if name != compat_name: - self.code_ns_reg.write(' {"%s", %s},\n'%(compat_name, cname)) + 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): @@ -963,6 +1021,21 @@ class PythonWrapperGenerator(object): 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 + if len(name_seg) >= 2 and name_seg[-1] == name_seg[-2]: + enum_name = ".".join(name_seg[:-1]) + is_enum_class = True + + wname = normalize_class_name(enum_name) + cname = enum_name.replace(".", "::") + + code = "" + if re.sub(r"^cv\.", "", enum_name) != wname: + code += "typedef enum {0} {1};\n".format(cname, wname) + code += "CV_PY_FROM_ENUM({0});\nCV_PY_TO_ENUM({0});\n\n".format(wname) + self.code_enums.write(code) def save(self, path, name, buf): with open(path + "/" + name, "wt") as f: @@ -975,14 +1048,15 @@ class PythonWrapperGenerator(object): def gen(self, srcfiles, output_path): self.clear() - self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True) + self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=False) # step 1: scan the headers and build more descriptive maps of classes, consts, functions for hdr in srcfiles: decls = self.parser.parse(hdr) if len(decls) == 0: continue - self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) ) + if hdr.find('opencv2/') >= 0: #Avoid including the shadow files + self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) ) for decl in decls: name = decl[0] if name.startswith("struct") or name.startswith("class"): @@ -994,6 +1068,9 @@ class PythonWrapperGenerator(object): elif name.startswith("const"): # constant self.add_const(name.replace("const ", "").strip(), decl) + elif name.startswith("enum"): + # enum + self.add_enum(name.rsplit(" ", 1)[1], decl) else: # function self.add_func(decl) @@ -1043,8 +1120,11 @@ class PythonWrapperGenerator(object): 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))) + cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname), mappable_code=mappable_code)) # register classes in the same order as they have been declared. # this way, base classes will be registered in Python before their derivatives. @@ -1070,7 +1150,13 @@ class PythonWrapperGenerator(object): self.gen_namespace(ns_name) self.gen_namespaces_reg() - # step 4: generate the code for constants + # step 4: generate the code for enum types + enumlist = list(self.enums.values()) + enumlist.sort() + for name in enumlist: + self.gen_enum_reg(name) + + # step 5: generate the code for constants constlist = list(self.consts.items()) constlist.sort() for name, constinfo in constlist: @@ -1079,6 +1165,7 @@ class PythonWrapperGenerator(object): # That's it. Now save all the files 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) diff --git a/modules/python/src2/hdr_parser.py b/modules/python/src2/hdr_parser.py index 21faf1bacd..73b5ef4ed6 100755 --- a/modules/python/src2/hdr_parser.py +++ b/modules/python/src2/hdr_parser.py @@ -6,6 +6,7 @@ import os, sys, re, string, io # the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt opencv_hdr_list = [ "../../core/include/opencv2/core.hpp", +"../../core/include/opencv2/core/mat.hpp", "../../core/include/opencv2/core/ocl.hpp", "../../flann/include/opencv2/flann/miniflann.hpp", "../../ml/include/opencv2/ml.hpp", @@ -32,8 +33,9 @@ original_return_type is None if the original_return_type is the same as return_v class CppHeaderParser(object): - def __init__(self, generate_umat_decls=False): + def __init__(self, generate_umat_decls=False, generate_gpumat_decls=False): self._generate_umat_decls = generate_umat_decls + self._generate_gpumat_decls = generate_gpumat_decls self.BLOCK_TYPE = 0 self.BLOCK_NAME = 1 @@ -375,11 +377,9 @@ class CppHeaderParser(object): decl[2].append("/A") if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)): decl[2].append("/C") - if "virtual" in decl_str: - print(decl_str) return decl - def parse_func_decl(self, decl_str, use_umat=False, docstring=""): + def parse_func_decl(self, decl_str, mat="Mat", docstring=""): """ Parses the function or method declaration in the form: [([CV_EXPORTS] ) | CVAPI(rettype)] @@ -392,8 +392,7 @@ class CppHeaderParser(object): """ if self.wrap_mode: - if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \ - ("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)): + if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or ("CV_WRAP" in decl_str)): return [] # ignore old API in the documentation check (for now) @@ -413,6 +412,16 @@ class CppHeaderParser(object): arg, npos3 = self.get_macro_arg(decl_str, npos) func_modlist.append("="+arg) decl_str = decl_str[:npos] + decl_str[npos3+1:] + npos = decl_str.find("CV_WRAP_PHANTOM") + if npos >= 0: + decl_str, _ = self.get_macro_arg(decl_str, npos) + func_modlist.append("/phantom") + npos = decl_str.find("CV_WRAP_MAPPABLE") + if npos >= 0: + mappable, npos3 = self.get_macro_arg(decl_str, npos) + func_modlist.append("/mappable="+mappable) + classname = top[1] + return ['.'.join([classname, classname]), None, func_modlist, [], None, None] virtual_method = False pure_virtual_method = False @@ -526,8 +535,6 @@ class CppHeaderParser(object): t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos) if not t: print("Error: no closing ')' at %d" % (self.lineno,)) - print(decl_str) - print(decl_str[arg_start:]) sys.exit(-1) if t == "<": angle_balance += 1 @@ -563,8 +570,6 @@ class CppHeaderParser(object): a = a[:eqpos].strip() arg_type, arg_name, modlist, argno = self.parse_arg(a, argno) if self.wrap_mode: - mat = "UMat" if use_umat else "Mat" - # TODO: Vectors should contain UMat, but this is not very easy to support and not very needed vector_mat = "vector_{}".format("Mat") vector_mat_template = "vector<{}>".format("Mat") @@ -629,8 +634,10 @@ class CppHeaderParser(object): block_type, block_name = b[self.BLOCK_TYPE], b[self.BLOCK_NAME] if block_type in ["file", "enum"]: continue - if block_type not in ["struct", "class", "namespace"]: - print("Error at %d: there are non-valid entries in the current block stack " % (self.lineno, self.block_stack)) + if block_type in ["enum struct", "enum class"] and block_name == name: + continue + if block_type not in ["struct", "class", "namespace", "enum struct", "enum class"]: + print("Error at %d: there are non-valid entries in the current block stack %s" % (self.lineno, self.block_stack)) sys.exit(-1) if block_name and (block_type == "namespace" or not qualified_name): n += block_name + "." @@ -639,7 +646,7 @@ class CppHeaderParser(object): n = "cv.Algorithm" return n - def parse_stmt(self, stmt, end_token, use_umat=False, docstring=""): + def parse_stmt(self, stmt, end_token, mat="Mat", docstring=""): """ parses the statement (ending with ';' or '}') or a block head (ending with '{') @@ -706,20 +713,19 @@ class CppHeaderParser(object): decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases]) return stmt_type, classname, True, decl - if stmt.startswith("enum"): - return "enum", "", True, None - - if stmt.startswith("namespace"): - stmt_list = stmt.split() + if stmt.startswith("enum") or stmt.startswith("namespace"): + stmt_list = stmt.rsplit(" ", 1) if len(stmt_list) < 2: stmt_list.append("") return stmt_list[0], stmt_list[1], True, None + if stmt.startswith("extern") and "\"C\"" in stmt: return "namespace", "", True, None - if end_token == "}" and context == "enum": + if end_token == "}" and context.startswith("enum"): decl = self.parse_enum(stmt) - return "enum", "", False, decl + name = stack_top[self.BLOCK_NAME] + return context, name, False, decl if end_token == ";" and stmt.startswith("typedef"): # TODO: handle typedef's more intelligently @@ -731,7 +737,7 @@ class CppHeaderParser(object): # since we filtered off the other places where '(' can normally occur: # - code blocks # - function pointer typedef's - decl = self.parse_func_decl(stmt, use_umat=use_umat, docstring=docstring) + decl = self.parse_func_decl(stmt, mat=mat, docstring=docstring) # we return parse_flag == False to prevent the parser to look inside function/method bodies # (except for tracking the nested blocks) return stmt_type, "", False, decl @@ -827,7 +833,7 @@ class CppHeaderParser(object): l = l[pos+2:] state = SCAN - if l.startswith('CV__'): # just ignore this lines + if l.startswith('CV__') or l.startswith('__CV_'): # just ignore these lines #print('IGNORE: ' + l) state = SCAN continue @@ -841,11 +847,17 @@ class CppHeaderParser(object): if not token: block_head += " " + l - break + block_head = block_head.strip() + if len(block_head) > 0 and block_head[-1] == ')' and block_head.startswith('CV_ENUM_FLAGS('): + l = '' + token = ';' + else: + break if token == "//": block_head += " " + l[:pos] - break + l = '' + continue if token == "/*": block_head += " " + l[:pos] @@ -896,20 +908,29 @@ class CppHeaderParser(object): docstring = docstring.strip() stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring) if decl: - if stmt_type == "enum": - for d in decl: - decls.append(d) + if stmt_type.startswith("enum"): + decls.append([stmt_type + " " + self.get_dotted_name(name), "", [], decl, None, ""]) else: decls.append(decl) + if self._generate_gpumat_decls and "cv.cuda." in decl[0]: + # If function takes as one of arguments Mat or vector - we want to create the + # same declaration working with GpuMat (this is important for T-Api access) + args = decl[3] + has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0 + if has_mat: + _, _, _, gpumat_decl = self.parse_stmt(stmt, token, mat="cuda::GpuMat", docstring=docstring) + decls.append(gpumat_decl) + if self._generate_umat_decls: # If function takes as one of arguments Mat or vector - we want to create the # same declaration working with UMat (this is important for T-Api access) args = decl[3] has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0 if has_mat: - _, _, _, umat_decl = self.parse_stmt(stmt, token, use_umat=True, docstring=docstring) + _, _, _, umat_decl = self.parse_stmt(stmt, token, mat="UMat", docstring=docstring) decls.append(umat_decl) + docstring = "" if stmt_type == "namespace": chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name] @@ -952,7 +973,7 @@ class CppHeaderParser(object): print() if __name__ == '__main__': - parser = CppHeaderParser(generate_umat_decls=True) + parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=False) decls = [] for hname in opencv_hdr_list: decls += parser.parse(hname) diff --git a/modules/stitching/misc/python/pyopencv_stitching.hpp b/modules/stitching/misc/python/pyopencv_stitching.hpp index e5d0cd2481..8456ad7b91 100644 --- a/modules/stitching/misc/python/pyopencv_stitching.hpp +++ b/modules/stitching/misc/python/pyopencv_stitching.hpp @@ -1,9 +1,3 @@ #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 +#endif diff --git a/modules/videoio/misc/python/pyopencv_videoio.hpp b/modules/videoio/misc/python/pyopencv_videoio.hpp index 453a57a126..8ea62ebd73 100644 --- a/modules/videoio/misc/python/pyopencv_videoio.hpp +++ b/modules/videoio/misc/python/pyopencv_videoio.hpp @@ -1,33 +1,6 @@ #ifdef HAVE_OPENCV_VIDEOIO typedef std::vector vector_VideoCaptureAPIs; -template<> -bool pyopencv_to(PyObject *o, cv::VideoCaptureAPIs &v, const char *name) -{ - (void)name; - v = CAP_ANY; - if (!o || o == Py_None) - return false; - else if (PyLong_Check(o)) - { - v = VideoCaptureAPIs((int64)PyLong_AsLongLong(o)); - return true; - } - else if (PyInt_Check(o)) - { - v = VideoCaptureAPIs((int64)PyInt_AS_LONG(o)); - return true; - } - else - return false; -} - -template<> -PyObject* pyopencv_from(const cv::VideoCaptureAPIs &v) -{ - return pyopencv_from((int)(v)); -} - template<> struct pyopencvVecConverter { static bool to(PyObject* obj, std::vector& value, const ArgInfo info) -- GitLab