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