From e85e83f49103f4b1419c8038d4919de309652beb Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Thu, 14 Aug 2014 19:01:49 +0400 Subject: [PATCH 01/11] handling namespaces on parsing functions submodule method tables generation --- modules/python/src2/gen2.py | 72 +++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 3dc2329c5..4ddcb8eff 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -740,7 +740,7 @@ class PythonWrapperGenerator(object): def clear(self): self.classes = {} - self.funcs = {} + self.ns_funcs = {} self.consts = {} self.code_include = StringIO() self.code_types = StringIO() @@ -748,6 +748,7 @@ class PythonWrapperGenerator(object): self.code_func_tab = StringIO() self.code_type_reg = StringIO() self.code_const_reg = StringIO() + self.code_ns_reg = StringIO() self.class_idx = 0 def add_class(self, stype, name, decl): @@ -773,53 +774,48 @@ class PythonWrapperGenerator(object): self.consts[constinfo.name] = constinfo def add_func(self, decl): - classname = bareclassname = "" - name = decl[0] - dpos = name.rfind(".") - if dpos >= 0 and name[:dpos] not in ["cv", "cv.ocl"]: - classname = bareclassname = re.sub(r"^cv\.", "", name[:dpos]) - name = name[dpos+1:] - dpos = classname.rfind(".") - if dpos >= 0: - bareclassname = classname[dpos+1:] - classname = classname.replace(".", "_") - cname = name - name = re.sub(r"^cv\.", "", name) - name = name.replace(".", "_") - isconstructor = cname == bareclassname - cname = cname.replace(".", "::") + chunks = decl[0].split('.') + name = chunks[-1] + cname = '::'.join(chunks) + namespace = '.'.join(chunks[:-1]) + classname = normalize_class_name(namespace) + if classname in self.classes: + bareclassname = chunks[-2] + namespace = '.'.join(chunks[:-2]) + else: + classname = '' + bareclassname = '' + + isconstructor = name == bareclassname isclassmethod = False - customname = False for m in decl[2]: if m == "/S": isclassmethod = True elif m.startswith("="): name = m[1:] - customname = True - func_map = self.funcs + if isclassmethod: + name = bareclassname+"_"+name + classname = bareclassname = '' - if not classname or isconstructor: - pass - elif isclassmethod: - if not customname: - name = classname + "_" + name - cname = classname + "::" + cname - classname = "" + if classname: + func_map = classinfo = self.classes[classname].methods else: - classinfo = self.classes.get(classname, ClassInfo("")) - if not classinfo.name: - print("Generator error: the class for method %s is missing" % (name,)) - sys.exit(-1) - func_map = classinfo.methods + func_map = self.ns_funcs.setdefault(namespace, {}) - func = func_map.get(name, FuncInfo(classname, name, cname, isconstructor)) + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor)) func.add_variant(decl) - if len(func.variants) == 1: - func_map[name] = func def gen_const_reg(self, constinfo): self.code_const_reg.write("PUBLISH2(%s,%s);\n" % (constinfo.name, constinfo.cname)) + def gen_namespace(self, ns_name): + wname = normalize_class_name(ns_name) + self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname) + funclist = sorted(self.ns_funcs[ns_name].items()) + for name, func in funclist: + self.code_ns_reg.write(func.get_tab_entry()) + self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') + def save(self, path, name, buf): f = open(path + "/" + name, "wt") f.write(buf.getvalue()) @@ -876,7 +872,7 @@ class PythonWrapperGenerator(object): self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) # step 3: generate the code for all the global functions - funclist = list(self.funcs.items()) + funclist = list(self.ns_funcs['cv'].items()) funclist.sort() for name, func in funclist: code = func.gen_code(self.classes) @@ -889,6 +885,11 @@ class PythonWrapperGenerator(object): for name, constinfo in constlist: self.gen_const_reg(constinfo) + # step 5: generate the code for namespaces + for ns in sorted(self.ns_funcs): + if ns != 'cv': + self.gen_namespace(ns) + # 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) @@ -896,6 +897,7 @@ class PythonWrapperGenerator(object): self.save(output_path, "pyopencv_generated_const_reg.h", self.code_const_reg) 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) if __name__ == "__main__": srcfiles = hdr_parser.opencv_hdr_list From 5ad7f9910f23d1d6248c3142b1b65849051767bf Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Fri, 15 Aug 2014 20:44:32 +0400 Subject: [PATCH 02/11] function namespaces partially work --- modules/python/common.cmake | 3 ++- modules/python/src2/cv2.cpp | 36 +++++++++++++++++++++++++ modules/python/src2/gen2.py | 53 ++++++++++++++++++++++--------------- 3 files changed, 70 insertions(+), 22 deletions(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index b9b3377a2..05cff74b4 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -51,7 +51,8 @@ set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_func_tab.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_const_reg.h") + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_const_reg.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_ns_reg.h") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs}") add_custom_command( diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 0bd914a24..f5df8836e 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1205,6 +1205,40 @@ static PyMethodDef methods[] = { /************************************************************************/ /* Module init */ +static void init_submodule(PyObject * root, const char * name, PyMethodDef * methods) +{ + std::string s = name; + size_t i = s.find('.')+1; // assume, that name is cv2.... + while (i < s.length()) + { + size_t j = s.find('.', i); + if (j == std::string::npos) + j = s.length(); + std::string short_name = s.substr(i, j-i); + std::string full_name = s.substr(0, j); + i = j+1; + + PyObject * d = PyModule_GetDict(root); + PyObject * submod = PyDict_GetItemString(d, short_name.c_str()); + if (submod == NULL) + { + submod = PyImport_AddModule(full_name.c_str()); + PyDict_SetItemString(d, short_name.c_str(), submod); + } + root = submod; + } + + PyObject * d = PyModule_GetDict(root); + for (PyMethodDef * m = methods; m->ml_name != NULL; ++m) + { + PyObject * method_obj = PyCFunction_NewEx(m, NULL, NULL); + PyDict_SetItemString(d, m->ml_name, method_obj); + Py_DECREF(method_obj); + } +} + +#include "pyopencv_generated_ns_reg.h" + static int to_ok(PyTypeObject *to) { to->tp_alloc = PyType_GenericAlloc; @@ -1242,6 +1276,8 @@ void initcv2() #else PyObject* m = Py_InitModule(MODULESTR, methods); #endif + init_submodules(m); // from "pyopencv_generated_ns_reg.h" + PyObject* d = PyModule_GetDict(m); PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION)); diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 4ddcb8eff..5926cece1 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -398,11 +398,6 @@ class FuncVariant(object): self.classname = classname self.name = self.wname = name self.isconstructor = isconstructor - if self.isconstructor: - if self.wname.startswith("Cv"): - self.wname = self.wname[2:] - else: - self.wname = self.classname self.rettype = handle_ptr(decl[1]) if self.rettype == "void": @@ -505,11 +500,12 @@ class FuncVariant(object): class FuncInfo(object): - def __init__(self, classname, name, cname, isconstructor): + def __init__(self, classname, name, cname, isconstructor, namespace): self.classname = classname self.name = name self.cname = cname self.isconstructor = isconstructor + self.namespace = namespace self.variants = [] def add_variant(self, decl): @@ -560,6 +556,7 @@ class FuncInfo(object): def gen_code(self, all_classes): proto = self.get_wrapper_prototype() code = "%s\n{\n" % (proto,) + code += " using namespace %s;\n\n" % self.namespace.replace('.', '::') selfinfo = ClassInfo("") ismethod = self.classname != "" and not self.isconstructor @@ -782,6 +779,9 @@ class PythonWrapperGenerator(object): if classname in self.classes: bareclassname = chunks[-2] namespace = '.'.join(chunks[:-2]) + if normalize_class_name(namespace) in self.classes: + print('Note: skipping "%s" (nested classes are currently not supported)'%decl[0]) + return else: classname = '' bareclassname = '' @@ -795,14 +795,15 @@ class PythonWrapperGenerator(object): name = m[1:] if isclassmethod: name = bareclassname+"_"+name - classname = bareclassname = '' + classname = '' - if classname: - func_map = classinfo = self.classes[classname].methods + if classname and not isconstructor: + cname = chunks[-1] + func_map = self.classes[classname].methods else: func_map = self.ns_funcs.setdefault(namespace, {}) - func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor)) + func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace)) func.add_variant(decl) def gen_const_reg(self, constinfo): @@ -816,6 +817,16 @@ class PythonWrapperGenerator(object): self.code_ns_reg.write(func.get_tab_entry()) self.code_ns_reg.write(' {NULL, NULL}\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.ns_funcs): + if ns_name == 'cv': + continue + wname = normalize_class_name(ns_name) + self.code_ns_reg.write(' init_submodule(root, MODULESTR"%s", methods_%s);\n' % (ns_name[2:], wname)) + self.code_ns_reg.write('};\n') + + def save(self, path, name, buf): f = open(path + "/" + name, "wt") f.write(buf.getvalue()) @@ -872,12 +883,17 @@ class PythonWrapperGenerator(object): self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) # step 3: generate the code for all the global functions - funclist = list(self.ns_funcs['cv'].items()) - funclist.sort() - for name, func in funclist: - code = func.gen_code(self.classes) - self.code_funcs.write(code) - self.code_func_tab.write(func.get_tab_entry()) + for ns in self.ns_funcs: + funclist = self.ns_funcs[ns].items() + funclist.sort() + for name, func in funclist: + code = func.gen_code(self.classes) + self.code_funcs.write(code) + if ns == 'cv': + self.code_func_tab.write(func.get_tab_entry()) + if ns != 'cv': + self.gen_namespace(ns) + self.gen_namespaces_reg() # step 4: generate the code for constants constlist = list(self.consts.items()) @@ -885,11 +901,6 @@ class PythonWrapperGenerator(object): for name, constinfo in constlist: self.gen_const_reg(constinfo) - # step 5: generate the code for namespaces - for ns in sorted(self.ns_funcs): - if ns != 'cv': - self.gen_namespace(ns) - # 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) From 4a519a25f395784cfbf0a0d7eaddc2e52d6ff70a Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Mon, 18 Aug 2014 16:15:30 +0400 Subject: [PATCH 03/11] nested classes support --- modules/python/src2/cv2.cpp | 2 -- modules/python/src2/gen2.py | 27 ++++++++++++++------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index f5df8836e..fe37c8c50 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -83,8 +83,6 @@ catch (const cv::Exception &e) \ } using namespace cv; -using cv::flann::IndexParams; -using cv::flann::SearchParams; typedef std::vector vector_uchar; typedef std::vector vector_char; diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 5926cece1..cdf4431e6 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -774,18 +774,17 @@ class PythonWrapperGenerator(object): chunks = decl[0].split('.') name = chunks[-1] cname = '::'.join(chunks) - namespace = '.'.join(chunks[:-1]) - classname = normalize_class_name(namespace) - if classname in self.classes: - bareclassname = chunks[-2] - namespace = '.'.join(chunks[:-2]) - if normalize_class_name(namespace) in self.classes: - print('Note: skipping "%s" (nested classes are currently not supported)'%decl[0]) - return - else: - classname = '' - bareclassname = '' - + namespace = chunks[:-1] + classes = [] + while normalize_class_name('.'.join(namespace)) in self.classes: + classes.insert(0, namespace.pop()) + classname = '' + bareclassname = '' + if classes: + classname = normalize_class_name('.'.join(namespace+classes)) + bareclassname = classes[-1] + namespace = '.'.join(namespace) + isconstructor = name == bareclassname isclassmethod = False for m in decl[2]: @@ -794,8 +793,10 @@ class PythonWrapperGenerator(object): elif m.startswith("="): name = m[1:] if isclassmethod: - name = bareclassname+"_"+name + name = "_".join(classes+[name]) classname = '' + elif isconstructor: + name = "_".join(classes[:-1]+[name]) if classname and not isconstructor: cname = chunks[-1] From 6d1c02ffedfbcdc8e5a16fa4286eb8940efd43df Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Mon, 18 Aug 2014 16:52:01 +0400 Subject: [PATCH 04/11] unified namespace population with 'init_submodule' --- modules/python/common.cmake | 1 - modules/python/src2/cv2.cpp | 8 ++++---- modules/python/src2/gen2.py | 29 +++++++++++++---------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 05cff74b4..f19474ef0 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -48,7 +48,6 @@ ocv_list_filterout(opencv_hdrs "opencv2/optim.hpp") set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_funcs.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_func_tab.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_const_reg.h" diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index fe37c8c50..3948d04ce 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1193,8 +1193,6 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") #include "pyopencv_generated_funcs.h" static PyMethodDef methods[] = { - -#include "pyopencv_generated_func_tab.h" {"createTrackbar", pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"}, {"setMouseCallback", (PyCFunction)pycvSetMouseCallback, METH_VARARGS | METH_KEYWORDS, "setMouseCallback(windowName, onMouse [, param]) -> None"}, {NULL, NULL}, @@ -1205,9 +1203,10 @@ static PyMethodDef methods[] = { static void init_submodule(PyObject * root, const char * name, PyMethodDef * methods) { + // traverse and create nested submodules std::string s = name; - size_t i = s.find('.')+1; // assume, that name is cv2.... - while (i < s.length()) + size_t i = s.find('.'); + while (i < s.length() && i != std::string::npos) { size_t j = s.find('.', i); if (j == std::string::npos) @@ -1226,6 +1225,7 @@ static void init_submodule(PyObject * root, const char * name, PyMethodDef * met root = submod; } + // populate module's dict PyObject * d = PyModule_GetDict(root); for (PyMethodDef * m = methods; m->ml_name != NULL; ++m) { diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index cdf4431e6..ca4e1fb6f 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -731,18 +731,23 @@ class FuncInfo(object): return code +class Namespace(object): + def __init__(self): + self.funcs = {} + self.consts = {} + + class PythonWrapperGenerator(object): def __init__(self): self.clear() def clear(self): self.classes = {} - self.ns_funcs = {} + self.namespaces = {} self.consts = {} self.code_include = StringIO() self.code_types = StringIO() self.code_funcs = StringIO() - self.code_func_tab = StringIO() self.code_type_reg = StringIO() self.code_const_reg = StringIO() self.code_ns_reg = StringIO() @@ -802,7 +807,7 @@ class PythonWrapperGenerator(object): cname = chunks[-1] func_map = self.classes[classname].methods else: - func_map = self.ns_funcs.setdefault(namespace, {}) + func_map = self.namespaces.setdefault(namespace, Namespace()).funcs func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace)) func.add_variant(decl) @@ -813,16 +818,14 @@ class PythonWrapperGenerator(object): def gen_namespace(self, ns_name): wname = normalize_class_name(ns_name) self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname) - funclist = sorted(self.ns_funcs[ns_name].items()) + funclist = sorted(self.namespaces[ns_name].funcs.items()) for name, func in funclist: self.code_ns_reg.write(func.get_tab_entry()) self.code_ns_reg.write(' {NULL, NULL}\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.ns_funcs): - if ns_name == 'cv': - continue + for ns_name in sorted(self.namespaces): wname = normalize_class_name(ns_name) self.code_ns_reg.write(' init_submodule(root, MODULESTR"%s", methods_%s);\n' % (ns_name[2:], wname)) self.code_ns_reg.write('};\n') @@ -884,16 +887,11 @@ class PythonWrapperGenerator(object): self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) ) # step 3: generate the code for all the global functions - for ns in self.ns_funcs: - funclist = self.ns_funcs[ns].items() - funclist.sort() - for name, func in funclist: + for ns_name, ns in sorted(self.namespaces.items()): + for name, func in sorted(ns.funcs.items()): code = func.gen_code(self.classes) self.code_funcs.write(code) - if ns == 'cv': - self.code_func_tab.write(func.get_tab_entry()) - if ns != 'cv': - self.gen_namespace(ns) + self.gen_namespace(ns_name) self.gen_namespaces_reg() # step 4: generate the code for constants @@ -905,7 +903,6 @@ 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_func_tab.h", self.code_func_tab) self.save(output_path, "pyopencv_generated_const_reg.h", self.code_const_reg) self.save(output_path, "pyopencv_generated_types.h", self.code_types) self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg) From e74cddfbd18a4b870eb561660abd5fae1a609feb Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Tue, 19 Aug 2014 13:35:46 +0400 Subject: [PATCH 05/11] put consts into python submodules maintain set of visited namespaces in CppHeaderParser WARNING: REMOVED const name conversion (SomeConstName -> SOME_CONST_NAME), discussion needed --- modules/python/common.cmake | 1 - modules/python/src2/cv2.cpp | 14 +++++- modules/python/src2/gen2.py | 73 ++++++++++++++++--------------- modules/python/src2/hdr_parser.py | 7 +++ 4 files changed, 57 insertions(+), 38 deletions(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index f19474ef0..ea8d0c832 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -50,7 +50,6 @@ set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_funcs.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_const_reg.h" "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_ns_reg.h") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs}") diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 3948d04ce..d71abba9f 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1201,7 +1201,13 @@ static PyMethodDef methods[] = { /************************************************************************/ /* Module init */ -static void init_submodule(PyObject * root, const char * name, PyMethodDef * methods) +struct ConstDef +{ + const char * name; + long val; +}; + +static void init_submodule(PyObject * root, const char * name, PyMethodDef * methods, ConstDef * consts) { // traverse and create nested submodules std::string s = name; @@ -1233,6 +1239,11 @@ static void init_submodule(PyObject * root, const char * name, PyMethodDef * met PyDict_SetItemString(d, m->ml_name, method_obj); Py_DECREF(method_obj); } + for (ConstDef * c = consts; c->name != NULL; ++c) + { + PyDict_SetItemString(d, c->name, PyInt_FromLong(c->val)); + } + } #include "pyopencv_generated_ns_reg.h" @@ -1323,7 +1334,6 @@ void initcv2() PUBLISH(CV_64FC3); PUBLISH(CV_64FC4); -#include "pyopencv_generated_const_reg.h" #if PY_MAJOR_VERSION >= 3 return m; #endif diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index ca4e1fb6f..0c1ff6449 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -341,16 +341,6 @@ class ClassInfo(object): return code -class ConstInfo(object): - def __init__(self, name, val): - self.cname = name.replace(".", "::") - self.name = re.sub(r"^cv\.", "", name).replace(".", "_") - if self.name.startswith("Cv"): - self.name = self.name[2:] - self.name = re.sub(r"([a-z])([A-Z])", r"\1_\2", self.name) - self.name = self.name.upper() - self.value = val - def handle_ptr(tp): if tp.startswith('Ptr_'): tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>' @@ -749,7 +739,6 @@ class PythonWrapperGenerator(object): self.code_types = StringIO() self.code_funcs = StringIO() self.code_type_reg = StringIO() - self.code_const_reg = StringIO() self.code_ns_reg = StringIO() self.class_idx = 0 @@ -766,23 +755,31 @@ class PythonWrapperGenerator(object): if classinfo.bases and not classinfo.isalgorithm: classinfo.isalgorithm = self.classes[classinfo.bases[0].replace("::", "_")].isalgorithm - def add_const(self, name, decl): - constinfo = ConstInfo(name, decl[1]) - - if constinfo.name in self.consts: - print("Generator error: constant %s (cname=%s) already exists" \ - % (constinfo.name, constinfo.cname)) - sys.exit(-1) - self.consts[constinfo.name] = constinfo - - def add_func(self, decl): - chunks = decl[0].split('.') - name = chunks[-1] - cname = '::'.join(chunks) + def split_decl_name(self, name): + chunks = name.split('.') namespace = chunks[:-1] classes = [] - while normalize_class_name('.'.join(namespace)) in self.classes: + while namespace and '.'.join(namespace) not in self.parser.namespaces: classes.insert(0, namespace.pop()) + return namespace, classes, chunks[-1] + + + def add_const(self, name, decl): + cname = name.replace('.','::') + namespace, classes, name = self.split_decl_name(name) + namespace = '.'.join(namespace) + name = '_'.join(classes+[name]) + ns = self.namespaces.setdefault(namespace, Namespace()) + if name in ns.consts: + print("Generator error: constant %s (cname=%s) already exists" \ + % (name, cname)) + sys.exit(-1) + ns.consts[name] = cname + + def add_func(self, decl): + namespace, classes, barename = self.split_decl_name(decl[0]) + cname = "::".join(namespace+classes+[barename]) + name = barename classname = '' bareclassname = '' if classes: @@ -804,7 +801,7 @@ class PythonWrapperGenerator(object): name = "_".join(classes[:-1]+[name]) if classname and not isconstructor: - cname = chunks[-1] + cname = barename func_map = self.classes[classname].methods else: func_map = self.namespaces.setdefault(namespace, Namespace()).funcs @@ -812,22 +809,27 @@ class PythonWrapperGenerator(object): func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace)) func.add_variant(decl) - def gen_const_reg(self, constinfo): - self.code_const_reg.write("PUBLISH2(%s,%s);\n" % (constinfo.name, constinfo.cname)) def gen_namespace(self, ns_name): + ns = self.namespaces[ns_name] wname = normalize_class_name(ns_name) + self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname) - funclist = sorted(self.namespaces[ns_name].funcs.items()) - for name, func in funclist: + for name, func in sorted(ns.funcs.items()): self.code_ns_reg.write(func.get_tab_entry()) self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') + 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(' {NULL, NULL}\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): - wname = normalize_class_name(ns_name) - self.code_ns_reg.write(' init_submodule(root, MODULESTR"%s", methods_%s);\n' % (ns_name[2:], wname)) + 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') @@ -838,11 +840,11 @@ class PythonWrapperGenerator(object): def gen(self, srcfiles, output_path): self.clear() - parser = hdr_parser.CppHeaderParser() + self.parser = hdr_parser.CppHeaderParser() # step 1: scan the headers and build more descriptive maps of classes, consts, functions for hdr in srcfiles: - decls = parser.parse(hdr) + decls = self.parser.parse(hdr) if len(decls) == 0: continue self.code_include.write( '#include "{}"\n'.format(hdr[hdr.rindex('opencv2/'):]) ) @@ -888,6 +890,8 @@ class PythonWrapperGenerator(object): # step 3: generate the code for all the global functions for ns_name, ns in sorted(self.namespaces.items()): + if ns_name.split('.')[0] != 'cv': + continue for name, func in sorted(ns.funcs.items()): code = func.gen_code(self.classes) self.code_funcs.write(code) @@ -903,7 +907,6 @@ 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_const_reg.h", self.code_const_reg) 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 de33aeb91..5f989d63a 100755 --- a/modules/python/src2/hdr_parser.py +++ b/modules/python/src2/hdr_parser.py @@ -38,6 +38,8 @@ class CppHeaderParser(object): self.PUBLIC_SECTION = 3 self.CLASS_DECL = 4 + self.namespaces = set() + def batch_replace(self, s, pairs): for before, after in pairs: s = s.replace(before, after) @@ -833,6 +835,9 @@ class CppHeaderParser(object): decls.append(d) else: decls.append(decl) + if stmt_type == "namespace": + chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name] + self.namespaces.add('.'.join(chunks)) else: stmt_type, name, parse_flag = "block", "", False @@ -877,3 +882,5 @@ if __name__ == '__main__': #decls += parser.parse(hname, wmode=False) parser.print_decls(decls) print(len(decls)) + print("namespaces:", " ".join(sorted(parser.namespaces))) + From c22dcb04af1793c94e399b1432a19e805ed751a4 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Tue, 19 Aug 2014 14:57:47 +0400 Subject: [PATCH 06/11] fixing warnings --- modules/python/src2/cv2.cpp | 6 +++--- modules/python/src2/gen2.py | 2 +- modules/python/src2/hdr_parser.py | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index d71abba9f..f02ba5a12 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -1192,7 +1192,7 @@ static int convert_to_char(PyObject *o, char *dst, const char *name = "no_name") #include "pyopencv_generated_types.h" #include "pyopencv_generated_funcs.h" -static PyMethodDef methods[] = { +static PyMethodDef special_methods[] = { {"createTrackbar", pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"}, {"setMouseCallback", (PyCFunction)pycvSetMouseCallback, METH_VARARGS | METH_KEYWORDS, "setMouseCallback(windowName, onMouse [, param]) -> None"}, {NULL, NULL}, @@ -1266,7 +1266,7 @@ static struct PyModuleDef cv2_moduledef = "Python wrapper for OpenCV.", -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ - methods + special_methods }; PyObject* PyInit_cv2() @@ -1283,7 +1283,7 @@ void initcv2() #if PY_MAJOR_VERSION >= 3 PyObject* m = PyModule_Create(&cv2_moduledef); #else - PyObject* m = Py_InitModule(MODULESTR, methods); + PyObject* m = Py_InitModule(MODULESTR, special_methods); #endif init_submodules(m); // from "pyopencv_generated_ns_reg.h" diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 0c1ff6449..e94d584bc 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -821,7 +821,7 @@ 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", (long)(%s)},\n'%(name, cname)) self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') def gen_namespaces_reg(self): diff --git a/modules/python/src2/hdr_parser.py b/modules/python/src2/hdr_parser.py index 5f989d63a..e0f8ecfcd 100755 --- a/modules/python/src2/hdr_parser.py +++ b/modules/python/src2/hdr_parser.py @@ -883,4 +883,3 @@ if __name__ == '__main__': parser.print_decls(decls) print(len(decls)) print("namespaces:", " ".join(sorted(parser.namespaces))) - From c4c731c0aa79e3587a08b713e796c26c839455f5 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Tue, 19 Aug 2014 16:57:37 +0400 Subject: [PATCH 07/11] fixing warnings keep old const names for compatibility --- modules/python/src2/gen2.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index e94d584bc..fc1bb7004 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -821,8 +821,11 @@ 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", (long)(%s)},\n'%(name, cname)) - self.code_ns_reg.write(' {NULL, NULL}\n};\n\n') + self.code_ns_reg.write(' {"%s", %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(' {NULL, 0}\n};\n\n') def gen_namespaces_reg(self): self.code_ns_reg.write('static void init_submodules(PyObject * root) \n{\n') From 40d0f853d6d27dc7e2fe7f9653df3767e64b8669 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Tue, 19 Aug 2014 19:19:11 +0400 Subject: [PATCH 08/11] removing modules from blacklist --- modules/python/common.cmake | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index ea8d0c832..c79f5400e 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -14,15 +14,7 @@ endforeach(mp) # module blacklist ocv_list_filterout(candidate_deps "^opencv_cud(a|ev)") -ocv_list_filterout(candidate_deps "^opencv_adas$") -ocv_list_filterout(candidate_deps "^opencv_face$") ocv_list_filterout(candidate_deps "^opencv_matlab$") -ocv_list_filterout(candidate_deps "^opencv_tracking$") -ocv_list_filterout(candidate_deps "^opencv_optflow$") -ocv_list_filterout(candidate_deps "^opencv_bgsegm$") -ocv_list_filterout(candidate_deps "^opencv_xfeatures2d$") -ocv_list_filterout(candidate_deps "^opencv_ximgproc$") -ocv_list_filterout(candidate_deps "^opencv_xphoto$") ocv_list_filterout(candidate_deps "^opencv_ts$") ocv_add_module(${MODULE_NAME} BINDINGS OPTIONAL ${candidate_deps}) @@ -43,7 +35,6 @@ ocv_list_filterout(opencv_hdrs ".h$") ocv_list_filterout(opencv_hdrs "cuda") ocv_list_filterout(opencv_hdrs "cudev") ocv_list_filterout(opencv_hdrs "opencv2/objdetect/detection_based_tracker.hpp") -ocv_list_filterout(opencv_hdrs "opencv2/optim.hpp") set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" From 74c77d826f2f30918a59eccd64c612d22f2f3f01 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Thu, 21 Aug 2014 14:51:03 +0400 Subject: [PATCH 09/11] base class name resolution fixed find_obj.py --- modules/python/common.cmake | 2 ++ modules/python/src2/gen2.py | 17 ++++++++++++++--- samples/python2/find_obj.py | 13 ++++++++----- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index c79f5400e..92b9c0863 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -16,6 +16,7 @@ endforeach(mp) ocv_list_filterout(candidate_deps "^opencv_cud(a|ev)") ocv_list_filterout(candidate_deps "^opencv_matlab$") ocv_list_filterout(candidate_deps "^opencv_ts$") +ocv_list_filterout(candidate_deps "^opencv_adas$") ocv_add_module(${MODULE_NAME} BINDINGS OPTIONAL ${candidate_deps}) @@ -35,6 +36,7 @@ ocv_list_filterout(opencv_hdrs ".h$") ocv_list_filterout(opencv_hdrs "cuda") ocv_list_filterout(opencv_hdrs "cudev") ocv_list_filterout(opencv_hdrs "opencv2/objdetect/detection_based_tracker.hpp") +ocv_list_filterout(opencv_hdrs "opencv2/ximgproc/structured_edge_detection.hpp") set(cv2_generated_hdrs "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index fc1bb7004..bacd8582a 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -267,7 +267,7 @@ class ClassInfo(object): #return sys.exit(-1) if self.bases and self.bases[0].startswith("cv::"): self.bases[0] = self.bases[0][4:] - if self.bases and self.bases[0] == "cv::Algorithm": + if self.bases and self.bases[0] == "Algorithm": self.isalgorithm = True for m in decl[2]: if m.startswith("="): @@ -752,8 +752,19 @@ class PythonWrapperGenerator(object): % (classinfo.name, classinfo.cname)) sys.exit(-1) self.classes[classinfo.name] = classinfo - if classinfo.bases and not classinfo.isalgorithm: - classinfo.isalgorithm = self.classes[classinfo.bases[0].replace("::", "_")].isalgorithm + + if classinfo.bases: + chunks = classinfo.bases[0].split('::') + base = '_'.join(chunks) + while base not in self.classes and len(chunks)>1: + del chunks[-2] + base = '_'.join(chunks) + if base not in self.classes: + print("Generator error: unable to resolve base %s for %s" + % (classinfo.bases[0], classinfo.name)) + sys.exit(-1) + classinfo.bases[0] = "::".join(chunks) + classinfo.isalgorithm |= self.classes[base].isalgorithm def split_decl_name(self, name): chunks = name.split('.') diff --git a/samples/python2/find_obj.py b/samples/python2/find_obj.py index ca4ee4daf..718a14fa5 100755 --- a/samples/python2/find_obj.py +++ b/samples/python2/find_obj.py @@ -4,7 +4,7 @@ Feature-based image matching sample. USAGE - find_obj.py [--feature=[-flann]] [ ] + find_obj.py [--feature=[-flann]] [ ] --feature - Feature to use. Can be sift, surf, orb or brisk. Append '-flann' to feature name to use Flann-based matcher instead bruteforce. @@ -23,14 +23,17 @@ FLANN_INDEX_LSH = 6 def init_feature(name): chunks = name.split('-') if chunks[0] == 'sift': - detector = cv2.SIFT() + detector = cv2.xfeatures2d.SIFT() norm = cv2.NORM_L2 elif chunks[0] == 'surf': - detector = cv2.SURF(800) + detector = cv2.xfeatures2d.SURF(800) norm = cv2.NORM_L2 elif chunks[0] == 'orb': detector = cv2.ORB(400) norm = cv2.NORM_HAMMING + elif chunks[0] == 'akaze': + detector = cv2.AKAZE() + norm = cv2.NORM_HAMMING elif chunks[0] == 'brisk': detector = cv2.BRISK() norm = cv2.NORM_HAMMING @@ -136,8 +139,8 @@ if __name__ == '__main__': try: fn1, fn2 = args except: - fn1 = '../c/box.png' - fn2 = '../c/box_in_scene.png' + fn1 = '../cpp/box.png' + fn2 = '../cpp/box_in_scene.png' img1 = cv2.imread(fn1, 0) img2 = cv2.imread(fn2, 0) From 2a9e252f777823ef732dc18f685ffa58152fa601 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Thu, 21 Aug 2014 17:31:01 +0400 Subject: [PATCH 10/11] fix namespace conflicts (like cv::inpaint and cv::xphoto::inpaint) whitespace fix --- modules/python/src2/gen2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index bacd8582a..cf60ea978 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -509,7 +509,7 @@ class FuncInfo(object): name = "getelem" else: classname = "" - return "pyopencv_" + classname + name + return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name def get_wrapper_prototype(self): full_fname = self.get_wrapper_name() @@ -760,7 +760,7 @@ class PythonWrapperGenerator(object): del chunks[-2] base = '_'.join(chunks) if base not in self.classes: - print("Generator error: unable to resolve base %s for %s" + print("Generator error: unable to resolve base %s for %s" % (classinfo.bases[0], classinfo.name)) sys.exit(-1) classinfo.bases[0] = "::".join(chunks) From c23d6b67ee68ad8835878c062be10a9004a189e8 Mon Sep 17 00:00:00 2001 From: Alexander Mordvintsev Date: Thu, 21 Aug 2014 18:45:50 +0400 Subject: [PATCH 11/11] blacklisted 'tracking' due to some linking problem --- modules/python/common.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 92b9c0863..7f17ad99a 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -17,6 +17,7 @@ ocv_list_filterout(candidate_deps "^opencv_cud(a|ev)") ocv_list_filterout(candidate_deps "^opencv_matlab$") ocv_list_filterout(candidate_deps "^opencv_ts$") ocv_list_filterout(candidate_deps "^opencv_adas$") +ocv_list_filterout(candidate_deps "^opencv_tracking$") ocv_add_module(${MODULE_NAME} BINDINGS OPTIONAL ${candidate_deps})