Home | History | Annotate | Download | only in src2
      1 #!/usr/bin/env python
      2 
      3 from __future__ import print_function
      4 import hdr_parser, sys, re, os
      5 from string import Template
      6 
      7 if sys.version_info[0] >= 3:
      8     from io import StringIO
      9 else:
     10     from cStringIO import StringIO
     11 
     12 ignored_arg_types = ["RNG*"]
     13 
     14 gen_template_check_self = Template("""    if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type))
     15         return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
     16     $cname* _self_ = ${amp}((pyopencv_${name}_t*)self)->v${get};
     17 """)
     18 
     19 gen_template_check_self_algo = Template("""    if(!PyObject_TypeCheck(self, &pyopencv_${name}_Type))
     20         return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
     21     $cname* _self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.get());
     22 """)
     23 
     24 gen_template_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
     25         new (&(self->v)) Ptr<$cname>(); // init Ptr with placement new
     26         if(self) """)
     27 
     28 gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})""")
     29 
     30 gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
     31         if(self) """)
     32 
     33 gen_template_simple_call_constructor = Template("""self->v = ${cname}${args}""")
     34 
     35 gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL };
     36     if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""")
     37 
     38 gen_template_func_body = Template("""$code_decl
     39     $code_parse
     40     {
     41         ${code_prelude}ERRWRAP2($code_fcall);
     42         $code_ret;
     43     }
     44 """)
     45 
     46 py_major_version = sys.version_info[0]
     47 if py_major_version >= 3:
     48     head_init_str = "PyVarObject_HEAD_INIT(&PyType_Type, 0)"
     49 else:
     50     head_init_str = """PyObject_HEAD_INIT(&PyType_Type)
     51 0,"""
     52 
     53 gen_template_simple_type_decl = Template("""
     54 struct pyopencv_${name}_t
     55 {
     56     PyObject_HEAD
     57     ${cname} v;
     58 };
     59 
     60 static PyTypeObject pyopencv_${name}_Type =
     61 {
     62     %s
     63     MODULESTR".$wname",
     64     sizeof(pyopencv_${name}_t),
     65 };
     66 
     67 static void pyopencv_${name}_dealloc(PyObject* self)
     68 {
     69     PyObject_Del(self);
     70 }
     71 
     72 template<> PyObject* pyopencv_from(const ${cname}& r)
     73 {
     74     pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
     75     m->v = r;
     76     return (PyObject*)m;
     77 }
     78 
     79 template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name)
     80 {
     81     if( src == NULL || src == Py_None )
     82         return true;
     83     if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
     84     {
     85         failmsg("Expected ${cname} for argument '%%s'", name);
     86         return false;
     87     }
     88     dst = ((pyopencv_${name}_t*)src)->v;
     89     return true;
     90 }
     91 """ % head_init_str)
     92 
     93 
     94 gen_template_type_decl = Template("""
     95 struct pyopencv_${name}_t
     96 {
     97     PyObject_HEAD
     98     Ptr<${cname1}> v;
     99 };
    100 
    101 static PyTypeObject pyopencv_${name}_Type =
    102 {
    103     %s
    104     MODULESTR".$wname",
    105     sizeof(pyopencv_${name}_t),
    106 };
    107 
    108 static void pyopencv_${name}_dealloc(PyObject* self)
    109 {
    110     ((pyopencv_${name}_t*)self)->v.release();
    111     PyObject_Del(self);
    112 }
    113 
    114 template<> PyObject* pyopencv_from(const Ptr<${cname}>& r)
    115 {
    116     pyopencv_${name}_t *m = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
    117     new (&(m->v)) Ptr<$cname1>(); // init Ptr with placement new
    118     m->v = r;
    119     return (PyObject*)m;
    120 }
    121 
    122 template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name)
    123 {
    124     if( src == NULL || src == Py_None )
    125         return true;
    126     if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
    127     {
    128         failmsg("Expected ${cname} for argument '%%s'", name);
    129         return false;
    130     }
    131     dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
    132     return true;
    133 }
    134 
    135 """ % head_init_str)
    136 
    137 gen_template_map_type_cvt = Template("""
    138 template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name);
    139 """)
    140 
    141 gen_template_set_prop_from_map = Template("""
    142     if( PyMapping_HasKeyString(src, (char*)"$propname") )
    143     {
    144         tmp = PyMapping_GetItemString(src, (char*)"$propname");
    145         ok = tmp && pyopencv_to(tmp, dst.$propname);
    146         Py_DECREF(tmp);
    147         if(!ok) return false;
    148     }""")
    149 
    150 gen_template_type_impl = Template("""
    151 static PyObject* pyopencv_${name}_repr(PyObject* self)
    152 {
    153     char str[1000];
    154     sprintf(str, "<$wname %p>", self);
    155     return PyString_FromString(str);
    156 }
    157 
    158 ${getset_code}
    159 
    160 static PyGetSetDef pyopencv_${name}_getseters[] =
    161 {${getset_inits}
    162     {NULL}  /* Sentinel */
    163 };
    164 
    165 ${methods_code}
    166 
    167 static PyMethodDef pyopencv_${name}_methods[] =
    168 {
    169 ${methods_inits}
    170     {NULL,          NULL}
    171 };
    172 
    173 static void pyopencv_${name}_specials(void)
    174 {
    175     pyopencv_${name}_Type.tp_base = ${baseptr};
    176     pyopencv_${name}_Type.tp_dealloc = pyopencv_${name}_dealloc;
    177     pyopencv_${name}_Type.tp_repr = pyopencv_${name}_repr;
    178     pyopencv_${name}_Type.tp_getset = pyopencv_${name}_getseters;
    179     pyopencv_${name}_Type.tp_methods = pyopencv_${name}_methods;${extra_specials}
    180 }
    181 """)
    182 
    183 
    184 gen_template_get_prop = Template("""
    185 static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
    186 {
    187     return pyopencv_from(p->v${access}${member});
    188 }
    189 """)
    190 
    191 gen_template_get_prop_algo = Template("""
    192 static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
    193 {
    194     return pyopencv_from(dynamic_cast<$cname*>(p->v.get())${access}${member});
    195 }
    196 """)
    197 
    198 gen_template_set_prop = Template("""
    199 static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
    200 {
    201     if (value == NULL)
    202     {
    203         PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
    204         return -1;
    205     }
    206     return pyopencv_to(value, p->v${access}${member}) ? 0 : -1;
    207 }
    208 """)
    209 
    210 gen_template_set_prop_algo = Template("""
    211 static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
    212 {
    213     if (value == NULL)
    214     {
    215         PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
    216         return -1;
    217     }
    218     return pyopencv_to(value, dynamic_cast<$cname*>(p->v.get())${access}${member}) ? 0 : -1;
    219 }
    220 """)
    221 
    222 
    223 gen_template_prop_init = Template("""
    224     {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, NULL, (char*)"${member}", NULL},""")
    225 
    226 gen_template_rw_prop_init = Template("""
    227     {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, (setter)pyopencv_${name}_set_${member}, (char*)"${member}", NULL},""")
    228 
    229 simple_argtype_mapping = {
    230     "bool": ("bool", "b", "0"),
    231     "int": ("int", "i", "0"),
    232     "float": ("float", "f", "0.f"),
    233     "double": ("double", "d", "0"),
    234     "c_string": ("char*", "s", '(char*)""')
    235 }
    236 
    237 def normalize_class_name(name):
    238     return re.sub(r"^cv\.", "", name).replace(".", "_")
    239 
    240 class ClassProp(object):
    241     def __init__(self, decl):
    242         self.tp = decl[0].replace("*", "_ptr")
    243         self.name = decl[1]
    244         self.readonly = True
    245         if "/RW" in decl[3]:
    246             self.readonly = False
    247 
    248 class ClassInfo(object):
    249     def __init__(self, name, decl=None):
    250         self.cname = name.replace(".", "::")
    251         self.name = self.wname = normalize_class_name(name)
    252         self.ismap = False
    253         self.issimple = False
    254         self.isalgorithm = False
    255         self.methods = {}
    256         self.props = []
    257         self.consts = {}
    258         self.base = None
    259         customname = False
    260 
    261         if decl:
    262             bases = decl[1].split()[1:]
    263             if len(bases) > 1:
    264                 print("Note: Class %s has more than 1 base class (not supported by Python C extensions)" % (self.name,))
    265                 print("      Bases: ", " ".join(bases))
    266                 print("      Only the first base class will be used")
    267                 #return sys.exit(-1)
    268             elif len(bases) == 1:
    269                 self.base = bases[0].strip(",")
    270                 if self.base.startswith("cv::"):
    271                     self.base = self.base[4:]
    272                 if self.base == "Algorithm":
    273                     self.isalgorithm = True
    274                 self.base = self.base.replace("::", "_")
    275 
    276             for m in decl[2]:
    277                 if m.startswith("="):
    278                     self.wname = m[1:]
    279                     customname = True
    280                 elif m == "/Map":
    281                     self.ismap = True
    282                 elif m == "/Simple":
    283                     self.issimple = True
    284             self.props = [ClassProp(p) for p in decl[3]]
    285 
    286         if not customname and self.wname.startswith("Cv"):
    287             self.wname = self.wname[2:]
    288 
    289     def gen_map_code(self, all_classes):
    290         code = "static bool pyopencv_to(PyObject* src, %s& dst, const char* name)\n{\n    PyObject* tmp;\n    bool ok;\n" % (self.cname)
    291         code += "".join([gen_template_set_prop_from_map.substitute(propname=p.name,proptype=p.tp) for p in self.props])
    292         if self.base:
    293             code += "\n    return pyopencv_to(src, (%s&)dst, name);\n}\n" % all_classes[self.base].cname
    294         else:
    295             code += "\n    return true;\n}\n"
    296         return code
    297 
    298     def gen_code(self, all_classes):
    299         if self.ismap:
    300             return self.gen_map_code(all_classes)
    301 
    302         getset_code = StringIO()
    303         getset_inits = StringIO()
    304 
    305         sorted_props = [(p.name, p) for p in self.props]
    306         sorted_props.sort()
    307 
    308         access_op = "->"
    309         if self.issimple:
    310             access_op = "."
    311 
    312         for pname, p in sorted_props:
    313             if self.isalgorithm:
    314                 getset_code.write(gen_template_get_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op))
    315             else:
    316                 getset_code.write(gen_template_get_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op))
    317             if p.readonly:
    318                 getset_inits.write(gen_template_prop_init.substitute(name=self.name, member=pname))
    319             else:
    320                 if self.isalgorithm:
    321                     getset_code.write(gen_template_set_prop_algo.substitute(name=self.name, cname=self.cname, member=pname, membertype=p.tp, access=access_op))
    322                 else:
    323                     getset_code.write(gen_template_set_prop.substitute(name=self.name, member=pname, membertype=p.tp, access=access_op))
    324                 getset_inits.write(gen_template_rw_prop_init.substitute(name=self.name, member=pname))
    325 
    326         methods_code = StringIO()
    327         methods_inits = StringIO()
    328 
    329         sorted_methods = list(self.methods.items())
    330         sorted_methods.sort()
    331 
    332         for mname, m in sorted_methods:
    333             methods_code.write(m.gen_code(all_classes))
    334             methods_inits.write(m.get_tab_entry())
    335 
    336         baseptr = "NULL"
    337         if self.base and self.base in all_classes:
    338             baseptr = "&pyopencv_" + all_classes[self.base].name + "_Type"
    339 
    340         code = gen_template_type_impl.substitute(name=self.name, wname=self.wname, cname=self.cname,
    341             getset_code=getset_code.getvalue(), getset_inits=getset_inits.getvalue(),
    342             methods_code=methods_code.getvalue(), methods_inits=methods_inits.getvalue(),
    343             baseptr=baseptr, extra_specials="")
    344 
    345         return code
    346 
    347 
    348 def handle_ptr(tp):
    349     if tp.startswith('Ptr_'):
    350         tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
    351     return tp
    352 
    353 
    354 class ArgInfo(object):
    355     def __init__(self, arg_tuple):
    356         self.tp = handle_ptr(arg_tuple[0])
    357         self.name = arg_tuple[1]
    358         self.defval = arg_tuple[2]
    359         self.isarray = False
    360         self.arraylen = 0
    361         self.arraycvt = None
    362         self.inputarg = True
    363         self.outputarg = False
    364         self.returnarg = False
    365         for m in arg_tuple[3]:
    366             if m == "/O":
    367                 self.inputarg = False
    368                 self.outputarg = True
    369                 self.returnarg = True
    370             elif m == "/IO":
    371                 self.inputarg = True
    372                 self.outputarg = True
    373                 self.returnarg = True
    374             elif m.startswith("/A"):
    375                 self.isarray = True
    376                 self.arraylen = m[2:].strip()
    377             elif m.startswith("/CA"):
    378                 self.isarray = True
    379                 self.arraycvt = m[2:].strip()
    380         self.py_inputarg = False
    381         self.py_outputarg = False
    382 
    383     def isbig(self):
    384         return self.tp == "Mat" or self.tp == "vector_Mat"# or self.tp.startswith("vector")
    385 
    386     def crepr(self):
    387         return "ArgInfo(\"%s\", %d)" % (self.name, self.outputarg)
    388 
    389 
    390 class FuncVariant(object):
    391     def __init__(self, classname, name, decl, isconstructor):
    392         self.classname = classname
    393         self.name = self.wname = name
    394         self.isconstructor = isconstructor
    395 
    396         self.rettype = decl[4] if len(decl) >=5 else handle_ptr(decl[1])
    397         if self.rettype == "void":
    398             self.rettype = ""
    399         self.args = []
    400         self.array_counters = {}
    401         for a in decl[3]:
    402             ainfo = ArgInfo(a)
    403             if ainfo.isarray and not ainfo.arraycvt:
    404                 c = ainfo.arraylen
    405                 c_arrlist = self.array_counters.get(c, [])
    406                 if c_arrlist:
    407                     c_arrlist.append(ainfo.name)
    408                 else:
    409                     self.array_counters[c] = [ainfo.name]
    410             self.args.append(ainfo)
    411         self.init_pyproto()
    412 
    413     def init_pyproto(self):
    414         # string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g.
    415         # "src1, src2[, dst[, mask]]" for cv.add
    416         argstr = ""
    417 
    418         # list of all input arguments of the Python function, with the argument numbers:
    419         #    [("src1", 0), ("src2", 1), ("dst", 2), ("mask", 3)]
    420         # we keep an argument number to find the respective argument quickly, because
    421         # some of the arguments of C function may not present in the Python function (such as array counters)
    422         # or even go in a different order ("heavy" output parameters of the C function
    423         # become the first optional input parameters of the Python function, and thus they are placed right after
    424         # non-optional input parameters)
    425         arglist = []
    426 
    427         # the list of "heavy" output parameters. Heavy parameters are the parameters
    428         # that can be expensive to allocate each time, such as vectors and matrices (see isbig).
    429         outarr_list = []
    430 
    431         # the list of output parameters. Also includes input/output parameters.
    432         outlist = []
    433 
    434         firstoptarg = 1000000
    435         argno = -1
    436         for a in self.args:
    437             argno += 1
    438             if a.name in self.array_counters:
    439                 continue
    440             if a.tp in ignored_arg_types:
    441                 continue
    442             if a.returnarg:
    443                 outlist.append((a.name, argno))
    444             if (not a.inputarg) and a.isbig():
    445                 outarr_list.append((a.name, argno))
    446                 continue
    447             if not a.inputarg:
    448                 continue
    449             if not a.defval:
    450                 arglist.append((a.name, argno))
    451             else:
    452                 firstoptarg = min(firstoptarg, len(arglist))
    453                 # if there are some array output parameters before the first default parameter, they
    454                 # are added as optional parameters before the first optional parameter
    455                 if outarr_list:
    456                     arglist += outarr_list
    457                     outarr_list = []
    458                 arglist.append((a.name, argno))
    459 
    460         if outarr_list:
    461             firstoptarg = min(firstoptarg, len(arglist))
    462             arglist += outarr_list
    463         firstoptarg = min(firstoptarg, len(arglist))
    464 
    465         noptargs = len(arglist) - firstoptarg
    466         argnamelist = [aname for aname, argno in arglist]
    467         argstr = ", ".join(argnamelist[:firstoptarg])
    468         argstr = "[, ".join([argstr] + argnamelist[firstoptarg:])
    469         argstr += "]" * noptargs
    470         if self.rettype:
    471             outlist = [("retval", -1)] + outlist
    472         elif self.isconstructor:
    473             assert outlist == []
    474             outlist = [("self", -1)]
    475         if self.isconstructor:
    476             classname = self.classname
    477             if classname.startswith("Cv"):
    478                 classname=classname[2:]
    479             outstr = "<%s object>" % (classname,)
    480         elif outlist:
    481             outstr = ", ".join([o[0] for o in outlist])
    482         else:
    483             outstr = "None"
    484 
    485         self.py_docstring = "%s(%s) -> %s" % (self.wname, argstr, outstr)
    486         self.py_noptargs = noptargs
    487         self.py_arglist = arglist
    488         for aname, argno in arglist:
    489             self.args[argno].py_inputarg = True
    490         for aname, argno in outlist:
    491             if argno >= 0:
    492                 self.args[argno].py_outputarg = True
    493         self.py_outlist = outlist
    494 
    495 
    496 class FuncInfo(object):
    497     def __init__(self, classname, name, cname, isconstructor, namespace):
    498         self.classname = classname
    499         self.name = name
    500         self.cname = cname
    501         self.isconstructor = isconstructor
    502         self.namespace = namespace
    503         self.variants = []
    504 
    505     def add_variant(self, decl):
    506         self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor))
    507 
    508     def get_wrapper_name(self):
    509         name = self.name
    510         if self.classname:
    511             classname = self.classname + "_"
    512             if "[" in name:
    513                 name = "getelem"
    514         else:
    515             classname = ""
    516         return "pyopencv_" + self.namespace.replace('.','_') + '_' + classname + name
    517 
    518     def get_wrapper_prototype(self):
    519         full_fname = self.get_wrapper_name()
    520         if self.classname and not self.isconstructor:
    521             self_arg = "self"
    522         else:
    523             self_arg = ""
    524         return "static PyObject* %s(PyObject* %s, PyObject* args, PyObject* kw)" % (full_fname, self_arg)
    525 
    526     def get_tab_entry(self):
    527         docstring_list = []
    528         have_empty_constructor = False
    529         for v in self.variants:
    530             s = v.py_docstring
    531             if (not v.py_arglist) and self.isconstructor:
    532                 have_empty_constructor = True
    533             if s not in docstring_list:
    534                 docstring_list.append(s)
    535         # if there are just 2 constructors: default one and some other,
    536         # we simplify the notation.
    537         # Instead of ClassName(args ...) -> object or ClassName() -> object
    538         # we write ClassName([args ...]) -> object
    539         if have_empty_constructor and len(self.variants) == 2:
    540             idx = self.variants[1].py_arglist != []
    541             s = self.variants[idx].py_docstring
    542             p1 = s.find("(")
    543             p2 = s.rfind(")")
    544             docstring_list = [s[:p1+1] + "[" + s[p1+1:p2] + "]" + s[p2:]]
    545 
    546         return Template('    {"$py_funcname", (PyCFunction)$wrap_funcname, METH_VARARGS | METH_KEYWORDS, "$py_docstring"},\n'
    547                         ).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
    548                                      py_docstring = "  or  ".join(docstring_list))
    549 
    550     def gen_code(self, all_classes):
    551         proto = self.get_wrapper_prototype()
    552         code = "%s\n{\n" % (proto,)
    553         code += "    using namespace %s;\n\n" % self.namespace.replace('.', '::')
    554 
    555         selfinfo = ClassInfo("")
    556         ismethod = self.classname != "" and not self.isconstructor
    557         # full name is needed for error diagnostic in PyArg_ParseTupleAndKeywords
    558         fullname = self.name
    559 
    560         if self.classname:
    561             selfinfo = all_classes[self.classname]
    562             if not self.isconstructor:
    563                 amp = "&" if selfinfo.issimple else ""
    564                 if selfinfo.isalgorithm:
    565                     code += gen_template_check_self_algo.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp)
    566                 else:
    567                     get = "" if selfinfo.issimple else ".get()"
    568                     code += gen_template_check_self.substitute(name=selfinfo.name, cname=selfinfo.cname, amp=amp, get=get)
    569                 fullname = selfinfo.wname + "." + fullname
    570 
    571         all_code_variants = []
    572         declno = -1
    573         for v in self.variants:
    574             code_decl = ""
    575             code_ret = ""
    576             code_cvt_list = []
    577 
    578             code_args = "("
    579             all_cargs = []
    580             parse_arglist = []
    581 
    582             # declare all the C function arguments,
    583             # add necessary conversions from Python objects to code_cvt_list,
    584             # form the function/method call,
    585             # for the list of type mappings
    586             for a in v.args:
    587                 if a.tp in ignored_arg_types:
    588                     defval = a.defval
    589                     if not defval and a.tp.endswith("*"):
    590                         defval = 0
    591                     assert defval
    592                     if not code_args.endswith("("):
    593                         code_args += ", "
    594                     code_args += defval
    595                     all_cargs.append([[None, ""], ""])
    596                     continue
    597                 tp1 = tp = a.tp
    598                 amp = ""
    599                 defval0 = ""
    600                 if tp.endswith("*"):
    601                     tp = tp1 = tp[:-1]
    602                     amp = "&"
    603                     if tp.endswith("*"):
    604                         defval0 = "0"
    605                         tp1 = tp.replace("*", "_ptr")
    606                 if tp1.endswith("*"):
    607                     print("Error: type with star: a.tp=%s, tp=%s, tp1=%s" % (a.tp, tp, tp1))
    608                     sys.exit(-1)
    609 
    610                 amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
    611                 parse_name = a.name
    612                 if a.py_inputarg:
    613                     if amapping[1] == "O":
    614                         code_decl += "    PyObject* pyobj_%s = NULL;\n" % (a.name,)
    615                         parse_name = "pyobj_" + a.name
    616                         if a.tp == 'char':
    617                             code_cvt_list.append("convert_to_char(pyobj_%s, &%s, %s)"% (a.name, a.name, a.crepr()))
    618                         else:
    619                             code_cvt_list.append("pyopencv_to(pyobj_%s, %s, %s)" % (a.name, a.name, a.crepr()))
    620 
    621                 all_cargs.append([amapping, parse_name])
    622 
    623                 defval = a.defval
    624                 if not defval:
    625                     defval = amapping[2]
    626                 # "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types
    627                 if defval == tp + "()" and amapping[1] == "O":
    628                     defval = ""
    629                 if a.outputarg and not a.inputarg:
    630                     defval = ""
    631                 if defval:
    632                     code_decl += "    %s %s=%s;\n" % (amapping[0], a.name, defval)
    633                 else:
    634                     code_decl += "    %s %s;\n" % (amapping[0], a.name)
    635 
    636                 if not code_args.endswith("("):
    637                     code_args += ", "
    638                 code_args += amp + a.name
    639 
    640             code_args += ")"
    641 
    642             if self.isconstructor:
    643                 code_decl += "    pyopencv_%s_t* self = 0;\n" % selfinfo.name
    644                 if selfinfo.issimple:
    645                     templ_prelude = gen_template_simple_call_constructor_prelude
    646                     templ = gen_template_simple_call_constructor
    647                 else:
    648                     templ_prelude = gen_template_call_constructor_prelude
    649                     templ = gen_template_call_constructor
    650 
    651                 code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname)
    652                 code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args)
    653             else:
    654                 code_prelude = ""
    655                 code_fcall = ""
    656                 if v.rettype:
    657                     code_decl += "    " + v.rettype + " retval;\n"
    658                     code_fcall += "retval = "
    659                 if ismethod:
    660                     code_fcall += "_self_->" + self.cname
    661                 else:
    662                     code_fcall += self.cname
    663                 code_fcall += code_args
    664 
    665             if code_cvt_list:
    666                 code_cvt_list = [""] + code_cvt_list
    667 
    668             # add info about return value, if any, to all_cargs. if there non-void return value,
    669             # it is encoded in v.py_outlist as ("retval", -1) pair.
    670             # As [-1] in Python accesses the last element of a list, we automatically handle the return value by
    671             # adding the necessary info to the end of all_cargs list.
    672             if v.rettype:
    673                 tp = v.rettype
    674                 tp1 = tp.replace("*", "_ptr")
    675                 amapping = simple_argtype_mapping.get(tp, (tp, "O", "0"))
    676                 all_cargs.append(amapping)
    677 
    678             if v.args and v.py_arglist:
    679                 # form the format spec for PyArg_ParseTupleAndKeywords
    680                 fmtspec = "".join([all_cargs[argno][0][1] for aname, argno in v.py_arglist])
    681                 if v.py_noptargs > 0:
    682                     fmtspec = fmtspec[:-v.py_noptargs] + "|" + fmtspec[-v.py_noptargs:]
    683                 fmtspec += ":" + fullname
    684 
    685                 # form the argument parse code that:
    686                 #   - declares the list of keyword parameters
    687                 #   - calls PyArg_ParseTupleAndKeywords
    688                 #   - converts complex arguments from PyObject's to native OpenCV types
    689                 code_parse = gen_template_parse_args.substitute(
    690                     kw_list = ", ".join(['"' + aname + '"' for aname, argno in v.py_arglist]),
    691                     fmtspec = fmtspec,
    692                     parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]),
    693                     code_cvt = " &&\n        ".join(code_cvt_list))
    694             else:
    695                 code_parse = "if(PyObject_Size(args) == 0 && (kw == NULL || PyObject_Size(kw) == 0))"
    696 
    697             if len(v.py_outlist) == 0:
    698                 code_ret = "Py_RETURN_NONE"
    699             elif len(v.py_outlist) == 1:
    700                 if self.isconstructor:
    701                     code_ret = "return (PyObject*)self"
    702                 else:
    703                     aname, argno = v.py_outlist[0]
    704                     code_ret = "return pyopencv_from(%s)" % (aname,)
    705             else:
    706                 # ther is more than 1 return parameter; form the tuple out of them
    707                 fmtspec = "N"*len(v.py_outlist)
    708                 backcvt_arg_list = []
    709                 for aname, argno in v.py_outlist:
    710                     amapping = all_cargs[argno][0]
    711                     backcvt_arg_list.append("%s(%s)" % (amapping[2], aname))
    712                 code_ret = "return Py_BuildValue(\"(%s)\", %s)" % \
    713                     (fmtspec, ", ".join(["pyopencv_from(" + aname + ")" for aname, argno in v.py_outlist]))
    714 
    715             all_code_variants.append(gen_template_func_body.substitute(code_decl=code_decl,
    716                 code_parse=code_parse, code_prelude=code_prelude, code_fcall=code_fcall, code_ret=code_ret))
    717 
    718         if len(all_code_variants)==1:
    719             # if the function/method has only 1 signature, then just put it
    720             code += all_code_variants[0]
    721         else:
    722             # try to execute each signature
    723             code += "    PyErr_Clear();\n\n".join(["    {\n" + v + "    }\n" for v in all_code_variants])
    724         code += "\n    return NULL;\n}\n\n"
    725         return code
    726 
    727 
    728 class Namespace(object):
    729     def __init__(self):
    730         self.funcs = {}
    731         self.consts = {}
    732 
    733 
    734 class PythonWrapperGenerator(object):
    735     def __init__(self):
    736         self.clear()
    737 
    738     def clear(self):
    739         self.classes = {}
    740         self.namespaces = {}
    741         self.consts = {}
    742         self.code_include = StringIO()
    743         self.code_types = StringIO()
    744         self.code_funcs = StringIO()
    745         self.code_type_reg = StringIO()
    746         self.code_ns_reg = StringIO()
    747         self.class_idx = 0
    748 
    749     def add_class(self, stype, name, decl):
    750         classinfo = ClassInfo(name, decl)
    751         classinfo.decl_idx = self.class_idx
    752         self.class_idx += 1
    753 
    754         if classinfo.name in self.classes:
    755             print("Generator error: class %s (cname=%s) already exists" \
    756                 % (classinfo.name, classinfo.cname))
    757             sys.exit(-1)
    758         self.classes[classinfo.name] = classinfo
    759 
    760         if classinfo.base:
    761             chunks = classinfo.base.split('_')
    762             base = '_'.join(chunks)
    763             while base not in self.classes and len(chunks)>1:
    764                 del chunks[-2]
    765                 base = '_'.join(chunks)
    766             if base not in self.classes:
    767                 print("Generator error: unable to resolve base %s for %s"
    768                     % (classinfo.base, classinfo.name))
    769                 sys.exit(-1)
    770             classinfo.base = base
    771             classinfo.isalgorithm |= self.classes[base].isalgorithm
    772 
    773     def split_decl_name(self, name):
    774         chunks = name.split('.')
    775         namespace = chunks[:-1]
    776         classes = []
    777         while namespace and '.'.join(namespace) not in self.parser.namespaces:
    778             classes.insert(0, namespace.pop())
    779         return namespace, classes, chunks[-1]
    780 
    781 
    782     def add_const(self, name, decl):
    783         cname = name.replace('.','::')
    784         namespace, classes, name = self.split_decl_name(name)
    785         namespace = '.'.join(namespace)
    786         name = '_'.join(classes+[name])
    787         ns = self.namespaces.setdefault(namespace, Namespace())
    788         if name in ns.consts:
    789             print("Generator error: constant %s (cname=%s) already exists" \
    790                 % (name, cname))
    791             sys.exit(-1)
    792         ns.consts[name] = cname
    793 
    794     def add_func(self, decl):
    795         namespace, classes, barename = self.split_decl_name(decl[0])
    796         cname = "::".join(namespace+classes+[barename])
    797         name = barename
    798         classname = ''
    799         bareclassname = ''
    800         if classes:
    801             classname = normalize_class_name('.'.join(namespace+classes))
    802             bareclassname = classes[-1]
    803         namespace = '.'.join(namespace)
    804 
    805         isconstructor = name == bareclassname
    806         isclassmethod = False
    807         for m in decl[2]:
    808             if m == "/S":
    809                 isclassmethod = True
    810             elif m.startswith("="):
    811                 name = m[1:]
    812         if isclassmethod:
    813             name = "_".join(classes+[name])
    814             classname = ''
    815         elif isconstructor:
    816             name = "_".join(classes[:-1]+[name])
    817 
    818         if classname and not isconstructor:
    819             cname = barename
    820             func_map = self.classes[classname].methods
    821         else:
    822             func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
    823 
    824         func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace))
    825         func.add_variant(decl)
    826 
    827 
    828     def gen_namespace(self, ns_name):
    829         ns = self.namespaces[ns_name]
    830         wname = normalize_class_name(ns_name)
    831 
    832         self.code_ns_reg.write('static PyMethodDef methods_%s[] = {\n'%wname)
    833         for name, func in sorted(ns.funcs.items()):
    834             self.code_ns_reg.write(func.get_tab_entry())
    835         self.code_ns_reg.write('    {NULL, NULL}\n};\n\n')
    836 
    837         self.code_ns_reg.write('static ConstDef consts_%s[] = {\n'%wname)
    838         for name, cname in sorted(ns.consts.items()):
    839             self.code_ns_reg.write('    {"%s", %s},\n'%(name, cname))
    840             compat_name = re.sub(r"([a-z])([A-Z])", r"\1_\2", name).upper()
    841             if name != compat_name:
    842                 self.code_ns_reg.write('    {"%s", %s},\n'%(compat_name, cname))
    843         self.code_ns_reg.write('    {NULL, 0}\n};\n\n')
    844 
    845     def gen_namespaces_reg(self):
    846         self.code_ns_reg.write('static void init_submodules(PyObject * root) \n{\n')
    847         for ns_name in sorted(self.namespaces):
    848             if ns_name.split('.')[0] == 'cv':
    849                 wname = normalize_class_name(ns_name)
    850                 self.code_ns_reg.write('  init_submodule(root, MODULESTR"%s", methods_%s, consts_%s);\n' % (ns_name[2:], wname, wname))
    851         self.code_ns_reg.write('};\n')
    852 
    853 
    854     def save(self, path, name, buf):
    855         f = open(path + "/" + name, "wt")
    856         f.write(buf.getvalue())
    857         f.close()
    858 
    859     def gen(self, srcfiles, output_path):
    860         self.clear()
    861         self.parser = hdr_parser.CppHeaderParser()
    862 
    863         # step 1: scan the headers and build more descriptive maps of classes, consts, functions
    864         for hdr in srcfiles:
    865             decls = self.parser.parse(hdr)
    866             if len(decls) == 0:
    867                 continue
    868             self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) )
    869             for decl in decls:
    870                 name = decl[0]
    871                 if name.startswith("struct") or name.startswith("class"):
    872                     # class/struct
    873                     p = name.find(" ")
    874                     stype = name[:p]
    875                     name = name[p+1:].strip()
    876                     self.add_class(stype, name, decl)
    877                 elif name.startswith("const"):
    878                     # constant
    879                     self.add_const(name.replace("const ", "").strip(), decl)
    880                 else:
    881                     # function
    882                     self.add_func(decl)
    883 
    884         # step 2: generate code for the classes and their methods
    885         classlist = list(self.classes.items())
    886         classlist.sort()
    887         for name, classinfo in classlist:
    888             if classinfo.ismap:
    889                 self.code_types.write(gen_template_map_type_cvt.substitute(name=name, cname=classinfo.cname))
    890             else:
    891                 if classinfo.issimple:
    892                     templ = gen_template_simple_type_decl
    893                 else:
    894                     templ = gen_template_type_decl
    895                 self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname,
    896                                       cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname)))
    897 
    898         # register classes in the same order as they have been declared.
    899         # this way, base classes will be registered in Python before their derivatives.
    900         classlist1 = [(classinfo.decl_idx, name, classinfo) for name, classinfo in classlist]
    901         classlist1.sort()
    902 
    903         for decl_idx, name, classinfo in classlist1:
    904             code = classinfo.gen_code(self.classes)
    905             self.code_types.write(code)
    906             if not classinfo.ismap:
    907                 self.code_type_reg.write("MKTYPE2(%s);\n" % (classinfo.name,) )
    908 
    909         # step 3: generate the code for all the global functions
    910         for ns_name, ns in sorted(self.namespaces.items()):
    911             if ns_name.split('.')[0] != 'cv':
    912                 continue
    913             for name, func in sorted(ns.funcs.items()):
    914                 code = func.gen_code(self.classes)
    915                 self.code_funcs.write(code)
    916             self.gen_namespace(ns_name)
    917         self.gen_namespaces_reg()
    918 
    919         # step 4: generate the code for constants
    920         constlist = list(self.consts.items())
    921         constlist.sort()
    922         for name, constinfo in constlist:
    923             self.gen_const_reg(constinfo)
    924 
    925         # That's it. Now save all the files
    926         self.save(output_path, "pyopencv_generated_include.h", self.code_include)
    927         self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs)
    928         self.save(output_path, "pyopencv_generated_types.h", self.code_types)
    929         self.save(output_path, "pyopencv_generated_type_reg.h", self.code_type_reg)
    930         self.save(output_path, "pyopencv_generated_ns_reg.h", self.code_ns_reg)
    931 
    932 if __name__ == "__main__":
    933     srcfiles = hdr_parser.opencv_hdr_list
    934     dstdir = "/Users/vp/tmp"
    935     if len(sys.argv) > 1:
    936         dstdir = sys.argv[1]
    937     if len(sys.argv) > 2:
    938         srcfiles = open(sys.argv[2], 'r').read().split(';')
    939     generator = PythonWrapperGenerator()
    940     generator.gen(srcfiles, dstdir)
    941