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