1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 07:13:02 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2023-06-01 09:37:38 +03:00
565 changed files with 84396 additions and 17589 deletions
+101 -32
View File
@@ -1,16 +1,34 @@
#!/usr/bin/env python
from __future__ import print_function
import hdr_parser, sys, re, os
import hdr_parser, sys, re
from string import Template
from pprint import pprint
from collections import namedtuple
from itertools import chain
from typing_stubs_generator import TypingStubsGenerator
if sys.version_info[0] >= 3:
from io import StringIO
else:
from cStringIO import StringIO
if sys.version_info >= (3, 6):
from typing_stubs_generation import SymbolName
else:
SymbolName = namedtuple('SymbolName', ('namespaces', 'classes', 'name'))
def parse_symbol_name(cls, full_symbol_name, known_namespaces):
chunks = full_symbol_name.split('.')
namespaces, name = chunks[:-1], chunks[-1]
classes = []
while len(namespaces) > 0 and '.'.join(namespaces) not in known_namespaces:
classes.insert(0, namespaces.pop())
return cls(tuple(namespaces), tuple(classes), name)
setattr(SymbolName, "parse", classmethod(parse_symbol_name))
forbidden_arg_types = ["void*"]
@@ -182,6 +200,7 @@ ${variant}
}
""")
class FormatStrings:
string = 's'
unsigned_char = 'b'
@@ -197,9 +216,9 @@ class FormatStrings:
double = 'd'
object = 'O'
ArgTypeInfo = namedtuple('ArgTypeInfo',
['atype', 'format_str', 'default_value',
'strict_conversion'])
['atype', 'format_str', 'default_value', 'strict_conversion'])
# strict_conversion is False by default
ArgTypeInfo.__new__.__defaults__ = (False,)
@@ -256,12 +275,16 @@ class ClassProp(object):
class ClassInfo(object):
def __init__(self, name, decl=None, codegen=None):
# Scope name can be a module or other class e.g. cv::SimpleBlobDetector::Params
scope_name, self.original_name = name.rsplit(".", 1)
self.original_scope_name, self.original_name = name.rsplit(".", 1)
# In case scope refer the outer class exported with different name
if codegen:
scope_name = codegen.get_export_scope_name(scope_name)
self.scope_name = re.sub(r"^cv\.?", "", scope_name)
self.export_scope_name = codegen.get_export_scope_name(
self.original_scope_name
)
else:
self.export_scope_name = self.original_scope_name
self.export_scope_name = re.sub(r"^cv\.?", "", self.export_scope_name)
self.export_name = self.original_name
@@ -312,8 +335,8 @@ class ClassInfo(object):
@property
def wname(self):
if len(self.scope_name) > 0:
return self.scope_name.replace(".", "_") + "_" + self.export_name
if len(self.export_scope_name) > 0:
return self.export_scope_name.replace(".", "_") + "_" + self.export_name
return self.export_name
@@ -322,16 +345,16 @@ class ClassInfo(object):
return self.class_id
@property
def full_scope_name(self):
return "cv." + self.scope_name if len(self.scope_name) else "cv"
def full_export_scope_name(self):
return "cv." + self.export_scope_name if len(self.export_scope_name) else "cv"
@property
def full_export_name(self):
return self.full_scope_name + "." + self.export_name
return self.full_export_scope_name + "." + self.export_name
@property
def full_original_name(self):
return self.full_scope_name + "." + self.original_name
return self.original_scope_name + "." + self.original_name
@property
def has_export_alias(self):
@@ -415,7 +438,7 @@ class ClassInfo(object):
baseptr,
constructor_name,
# Leading dot is required to provide correct class naming
"." + self.scope_name if len(self.scope_name) > 0 else self.scope_name
"." + self.export_scope_name if len(self.export_scope_name) > 0 else self.export_scope_name
)
@@ -448,6 +471,15 @@ class ArgInfo(object):
self.py_outputarg = False
self.enclosing_arg = enclosing_arg
def __str__(self):
return 'ArgInfo("{}", tp="{}", default="{}", in={}, out={})'.format(
self.name, self.tp, self.defval, self.inputarg,
self.outputarg
)
def __repr__(self):
return str(self)
@property
def export_name(self):
if self.name in python_reserved_keywords:
@@ -568,6 +600,10 @@ class FuncVariant(object):
self.args.append(ainfo)
self.init_pyproto(namespace, classname, known_classes)
def is_arg_optional(self, py_arg_index):
# type: (FuncVariant, int) -> bool
return py_arg_index >= len(self.py_arglist) - self.py_noptargs
def init_pyproto(self, namespace, classname, known_classes):
# string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g.
# "src1, src2[, dst[, mask]]" for cv.add
@@ -768,7 +804,7 @@ class FuncInfo(object):
# Convert unicode chars to xml representation, but keep as string instead of bytes
full_docstring = full_docstring.encode('ascii', errors='xmlcharrefreplace').decode()
return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags),\n "$py_docstring"},\n'
return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags), "$py_docstring"},\n'
).substitute(py_funcname = self.variants[0].wname, wrap_funcname=self.get_wrapper_name(),
flags = 'METH_STATIC' if self.is_static else '0', py_docstring = full_docstring)
@@ -1045,6 +1081,7 @@ class PythonWrapperGenerator(object):
self.namespaces = {}
self.consts = {}
self.enums = {}
self.typing_stubs_generator = TypingStubsGenerator()
self.code_include = StringIO()
self.code_enums = StringIO()
self.code_types = StringIO()
@@ -1091,19 +1128,13 @@ class PythonWrapperGenerator(object):
return original_scope_name
def split_decl_name(self, name):
chunks = name.split('.')
namespace = chunks[:-1]
classes = []
while namespace and '.'.join(namespace) not in self.parser.namespaces:
classes.insert(0, namespace.pop())
return namespace, classes, chunks[-1]
return SymbolName.parse(name, self.parser.namespaces)
def add_const(self, name, decl):
cname = name.replace('.','::')
namespace, classes, name = self.split_decl_name(name)
namespace = '.'.join(namespace)
name = '_'.join(classes+[name])
name = '_'.join(chain(classes, (name, )))
ns = self.namespaces.setdefault(namespace, Namespace())
if name in ns.consts:
print("Generator error: constant %s (cname=%s) already exists" \
@@ -1118,20 +1149,31 @@ class PythonWrapperGenerator(object):
#print(cname + ' => ' + str(py_name) + ' (value=' + value + ')')
def add_enum(self, name, decl):
enumeration_name = SymbolName.parse(name, self.parser.namespaces)
is_scoped_enum = decl[0].startswith("enum class") \
or decl[0].startswith("enum struct")
wname = normalize_class_name(name)
if wname.endswith("<unnamed>"):
wname = None
else:
self.enums[wname] = name
const_decls = decl[3]
enum_entries = {}
for decl in const_decls:
name = decl[0]
self.add_const(name.replace("const ", "").strip(), decl)
enum_entries[decl[0].split(".")[-1]] = decl[1]
self.add_const(decl[0].replace("const ", "").strip(), decl)
# Extra enumerations tracking is required to generate stubs for
# all enumerations, including <unnamed> once, otherwise they
# will be forgiven
self.typing_stubs_generator.add_enum(enumeration_name, is_scoped_enum,
enum_entries)
def add_func(self, decl):
namespace, classes, barename = self.split_decl_name(decl[0])
cname = "::".join(namespace+classes+[barename])
cname = "::".join(chain(namespace, classes, (barename, )))
name = barename
classname = ''
bareclassname = ''
@@ -1158,7 +1200,7 @@ class PythonWrapperGenerator(object):
return
if isconstructor:
name = "_".join(classes[:-1]+[name])
name = "_".join(chain(classes[:-1], (name, )))
if is_static:
# Add it as a method to the class
@@ -1167,7 +1209,7 @@ class PythonWrapperGenerator(object):
func.add_variant(decl, self.classes, isphantom)
# Add it as global function
g_name = "_".join(classes+[name])
g_name = "_".join(chain(classes, (name, )))
w_classes = []
for i in range(0, len(classes)):
classes_i = classes[:i+1]
@@ -1179,10 +1221,15 @@ class PythonWrapperGenerator(object):
w_classes.append(w_classname)
g_wname = "_".join(w_classes+[name])
func_map = self.namespaces.setdefault(namespace_str, Namespace()).funcs
# Static functions should be called using class names, not like
# module-level functions, so first step is to remove them from
# type hints.
self.typing_stubs_generator.add_ignored_function_name(g_name)
# Exports static function with internal name (backward compatibility)
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace_str, False))
func.add_variant(decl, self.classes, isphantom)
if g_wname != g_name: # TODO OpenCV 5.0
self.typing_stubs_generator.add_ignored_function_name(g_wname)
wfunc = func_map.setdefault(g_wname, FuncInfo("", g_wname, cname, isconstructor, namespace_str, False))
wfunc.add_variant(decl, self.classes, isphantom)
else:
@@ -1347,19 +1394,33 @@ class PythonWrapperGenerator(object):
for decl_idx, name, classinfo in classlist1:
if classinfo.ismap:
continue
def _registerType(classinfo):
if classinfo.decl_idx in published_types:
#print(classinfo.decl_idx, classinfo.name, ' - already published')
return
# If class already registered it means that there is
# a correponding node in the AST. This check is partically
# useful for base classes.
return self.typing_stubs_generator.find_class_node(
classinfo, self.parser.namespaces
)
published_types.add(classinfo.decl_idx)
# Registering a class means creation of the AST node from the
# given class information
class_node = self.typing_stubs_generator.create_class_node(
classinfo, self.parser.namespaces
)
if classinfo.base and classinfo.base in self.classes:
base_classinfo = self.classes[classinfo.base]
#print(classinfo.decl_idx, classinfo.name, ' - request publishing of base type ', base_classinfo.decl_idx, base_classinfo.name)
_registerType(base_classinfo)
# print(classinfo.decl_idx, classinfo.name, ' - request publishing of base type ', base_classinfo.decl_idx, base_classinfo.name)
base_node = _registerType(base_classinfo)
class_node.add_base(base_node)
#print(classinfo.decl_idx, classinfo.name, ' - published!')
# print(classinfo.decl_idx, classinfo.name, ' - published!')
self.code_type_publish.write(classinfo.gen_def(self))
return class_node
_registerType(classinfo)
@@ -1373,6 +1434,10 @@ class PythonWrapperGenerator(object):
continue
code = func.gen_code(self)
self.code_funcs.write(code)
# If function is not ignored - create an AST node for it
if name not in self.typing_stubs_generator.type_hints_ignored_functions:
self.typing_stubs_generator.create_function_node(func)
self.gen_namespace(ns_name)
self.code_ns_init.write('CVPY_MODULE("{}", {});\n'.format(ns_name[2:], normalize_class_name(ns_name)))
@@ -1388,6 +1453,10 @@ class PythonWrapperGenerator(object):
for name, constinfo in constlist:
self.gen_const_reg(constinfo)
# All symbols are collected and AST is reconstructed, generating
# typing stubs...
self.typing_stubs_generator.generate(output_path)
# That's it. Now save all the files
self.save(output_path, "pyopencv_generated_include.h", self.code_include)
self.save(output_path, "pyopencv_generated_funcs.h", self.code_funcs)