• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3from __future__ import print_function
4import hdr_parser, sys, re, os
5from string import Template
6
7if sys.version_info[0] >= 3:
8    from io import StringIO
9else:
10    from cStringIO import StringIO
11
12ignored_arg_types = ["RNG*"]
13
14gen_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
19gen_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
24gen_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
28gen_template_call_constructor = Template("""self->v.reset(new ${cname}${args})""")
29
30gen_template_simple_call_constructor_prelude = Template("""self = PyObject_NEW(pyopencv_${name}_t, &pyopencv_${name}_Type);
31        if(self) """)
32
33gen_template_simple_call_constructor = Template("""self->v = ${cname}${args}""")
34
35gen_template_parse_args = Template("""const char* keywords[] = { $kw_list, NULL };
36    if( PyArg_ParseTupleAndKeywords(args, kw, "$fmtspec", (char**)keywords, $parse_arglist)$code_cvt )""")
37
38gen_template_func_body = Template("""$code_decl
39    $code_parse
40    {
41        ${code_prelude}ERRWRAP2($code_fcall);
42        $code_ret;
43    }
44""")
45
46py_major_version = sys.version_info[0]
47if py_major_version >= 3:
48    head_init_str = "PyVarObject_HEAD_INIT(&PyType_Type, 0)"
49else:
50    head_init_str = """PyObject_HEAD_INIT(&PyType_Type)
510,"""
52
53gen_template_simple_type_decl = Template("""
54struct pyopencv_${name}_t
55{
56    PyObject_HEAD
57    ${cname} v;
58};
59
60static PyTypeObject pyopencv_${name}_Type =
61{
62    %s
63    MODULESTR".$wname",
64    sizeof(pyopencv_${name}_t),
65};
66
67static void pyopencv_${name}_dealloc(PyObject* self)
68{
69    PyObject_Del(self);
70}
71
72template<> 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
79template<> 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
94gen_template_type_decl = Template("""
95struct pyopencv_${name}_t
96{
97    PyObject_HEAD
98    Ptr<${cname1}> v;
99};
100
101static PyTypeObject pyopencv_${name}_Type =
102{
103    %s
104    MODULESTR".$wname",
105    sizeof(pyopencv_${name}_t),
106};
107
108static void pyopencv_${name}_dealloc(PyObject* self)
109{
110    ((pyopencv_${name}_t*)self)->v.release();
111    PyObject_Del(self);
112}
113
114template<> 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
122template<> 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
137gen_template_map_type_cvt = Template("""
138template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name);
139""")
140
141gen_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
150gen_template_type_impl = Template("""
151static 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
160static PyGetSetDef pyopencv_${name}_getseters[] =
161{${getset_inits}
162    {NULL}  /* Sentinel */
163};
164
165${methods_code}
166
167static PyMethodDef pyopencv_${name}_methods[] =
168{
169${methods_inits}
170    {NULL,          NULL}
171};
172
173static 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
184gen_template_get_prop = Template("""
185static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
186{
187    return pyopencv_from(p->v${access}${member});
188}
189""")
190
191gen_template_get_prop_algo = Template("""
192static 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
198gen_template_set_prop = Template("""
199static 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
210gen_template_set_prop_algo = Template("""
211static 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
223gen_template_prop_init = Template("""
224    {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, NULL, (char*)"${member}", NULL},""")
225
226gen_template_rw_prop_init = Template("""
227    {(char*)"${member}", (getter)pyopencv_${name}_get_${member}, (setter)pyopencv_${name}_set_${member}, (char*)"${member}", NULL},""")
228
229simple_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
237def normalize_class_name(name):
238    return re.sub(r"^cv\.", "", name).replace(".", "_")
239
240class 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
248class 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
348def handle_ptr(tp):
349    if tp.startswith('Ptr_'):
350        tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
351    return tp
352
353
354class 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
390class 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
496class 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
728class Namespace(object):
729    def __init__(self):
730        self.funcs = {}
731        self.consts = {}
732
733
734class 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
932if __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