Merge pull request #19156 from vpisarev:named_args1

This commit is contained in:
Vadim Pisarevsky 2020-12-18 17:35:19 +00:00
commit 71be47e04a
6 changed files with 138 additions and 22 deletions

View File

@ -445,6 +445,7 @@ Cv64suf;
#define CV_EXPORTS_W_SIMPLE CV_EXPORTS
#define CV_EXPORTS_AS(synonym) CV_EXPORTS
#define CV_EXPORTS_W_MAP CV_EXPORTS
#define CV_EXPORTS_W_PARAMS CV_EXPORTS
#define CV_IN_OUT
#define CV_OUT
#define CV_PROP

View File

@ -1508,6 +1508,21 @@ CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth,
InputArray kernel, Point anchor = Point(-1,-1),
double delta = 0, int borderType = BORDER_DEFAULT );
class CV_EXPORTS_W_PARAMS Filter2DParams
{
public:
CV_PROP_RW int anchorX = -1;
CV_PROP_RW int anchorY = -1;
CV_PROP_RW int borderType = BORDER_DEFAULT;
CV_PROP_RW Scalar borderValue = Scalar();
CV_PROP_RW int ddepth = -1;
CV_PROP_RW double scale = 1.;
CV_PROP_RW double shift = 0.;
};
CV_EXPORTS_AS(filter2Dp) void filter2D( InputArray src, OutputArray dst, InputArray kernel,
const Filter2DParams& params=Filter2DParams());
/** @brief Applies a separable linear filter to an image.
The function applies a separable linear filter to the image. That is, first, every row of src is

View File

@ -1555,6 +1555,22 @@ void filter2D(InputArray _src, OutputArray _dst, int ddepth,
delta, borderType, src.isSubmatrix());
}
void filter2D( InputArray src, OutputArray dst, InputArray kernel,
const Filter2DParams& params)
{
Mat K = kernel.getMat(), tempK;
if (params.scale != 1) {
int kdepth = K.depth();
K.convertTo(tempK,
kdepth == CV_32F || kdepth == CV_64F ? kdepth : CV_32F,
params.scale, 0);
K = tempK;
}
CV_Assert(params.borderValue == Scalar());
filter2D(src, dst, params.ddepth, K, Point(params.anchorX, params.anchorY),
params.shift, params.borderType);
}
void sepFilter2D(InputArray _src, OutputArray _dst, int ddepth,
InputArray _kernelX, InputArray _kernelY, Point anchor,
double delta, int borderType)

View File

@ -221,6 +221,7 @@ class ClassProp(object):
def __init__(self, decl):
self.tp = decl[0].replace("*", "_ptr")
self.name = decl[1]
self.defval = decl[2]
self.readonly = True
if "/RW" in decl[3]:
self.readonly = False
@ -233,6 +234,7 @@ class ClassInfo(object):
self.ismap = False
self.issimple = False
self.isalgorithm = False
self.isparams = False
self.methods = {}
self.props = []
self.mappables = []
@ -264,6 +266,8 @@ class ClassInfo(object):
self.ismap = True
elif m == "/Simple":
self.issimple = True
elif m == "/Params":
self.isparams = True
self.props = [ClassProp(p) for p in decl[3]]
if not customname and self.wname.startswith("Cv"):
@ -352,6 +356,18 @@ def handle_ptr(tp):
return tp
def get_named_params_info(all_classes, args):
extra_named_params = []
params_arg_name = ""
if args:
last_arg = args[-1]
if ("Params" in last_arg.tp) and (last_arg.tp in all_classes):
arg_classinfo = all_classes[last_arg.tp]
if arg_classinfo.isparams:
params_arg_name = last_arg.name
extra_named_params = arg_classinfo.props
return (params_arg_name, extra_named_params)
class ArgInfo(object):
def __init__(self, arg_tuple):
self.tp = handle_ptr(arg_tuple[0])
@ -392,7 +408,7 @@ class ArgInfo(object):
class FuncVariant(object):
def __init__(self, classname, name, decl, isconstructor, isphantom=False):
def __init__(self, all_classes, classname, name, decl, isconstructor, isphantom=False):
self.classname = classname
self.name = self.wname = name
self.isconstructor = isconstructor
@ -415,9 +431,9 @@ class FuncVariant(object):
else:
self.array_counters[c] = [ainfo.name]
self.args.append(ainfo)
self.init_pyproto()
self.init_pyproto(all_classes)
def init_pyproto(self):
def init_pyproto(self, all_classes):
# string representation of argument list, with '[', ']' symbols denoting optional arguments, e.g.
# "src1, src2[, dst[, mask]]" for cv.add
argstr = ""
@ -430,6 +446,7 @@ class FuncVariant(object):
# become the first optional input parameters of the Python function, and thus they are placed right after
# non-optional input parameters)
arglist = []
proto_arglist = []
# the list of "heavy" output parameters. Heavy parameters are the parameters
# that can be expensive to allocate each time, such as vectors and matrices (see isbig).
@ -456,25 +473,39 @@ class FuncVariant(object):
continue
if not a.defval:
arglist.append((a.name, argno))
proto_arglist.append((a.name, argno))
else:
firstoptarg = min(firstoptarg, len(arglist))
# if there are some array output parameters before the first default parameter, they
# are added as optional parameters before the first optional parameter
if outarr_list:
arglist += outarr_list
proto_arglist += [(aname_+"=None",argno_) for (aname_, argno_) in outarr_list]
outarr_list = []
arglist.append((a.name, argno))
proto_arglist.append((a.name+"="+a.defval, argno))
# exclude "params" from Python func parameters ==>
params_arg_name, extra_named_params = get_named_params_info(all_classes, self.args)
if params_arg_name:
arglist = arglist[:-1]
proto_arglist = proto_arglist[:-1]
if outarr_list:
firstoptarg = min(firstoptarg, len(arglist))
proto_arglist += [(aname+"=None",argno) for (aname, argno) in outarr_list]
arglist += outarr_list
firstoptarg = min(firstoptarg, len(arglist))
noptargs = len(arglist) - firstoptarg
argnamelist = [aname for aname, argno in arglist]
argnamelist = [aname for aname, argno in proto_arglist]
noptargs = len(argnamelist) - firstoptarg
if params_arg_name:
# ==> instead, add the individual parameters one by one
argnamelist += ["%s=%s" % (a.name, a.defval) for a in extra_named_params]
argstr = ", ".join(argnamelist[:firstoptarg])
argstr = "[, ".join([argstr] + argnamelist[firstoptarg:])
argstr += "]" * noptargs
argstr += "[, " + (", ".join(argnamelist[firstoptarg:]))
argstr += "]"
if self.rettype:
outlist = [("retval", -1)] + outlist
elif self.isconstructor:
@ -513,8 +544,8 @@ class FuncInfo(object):
self.is_static = is_static
self.variants = []
def add_variant(self, decl, isphantom=False):
self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor, isphantom))
def add_variant(self, all_classes, decl, isphantom=False):
self.variants.append(FuncVariant(all_classes, self.classname, self.name, decl, self.isconstructor, isphantom))
def get_wrapper_name(self):
name = self.name
@ -570,20 +601,20 @@ class FuncInfo(object):
# their relevant doxygen comment
full_docstring = ""
for prototype, body in zip(prototype_list, docstring_list):
full_docstring += Template("$prototype\n$docstring\n\n\n\n").substitute(
prototype=prototype,
docstring='\n'.join(
['. ' + line
full_docstring += Template("$prototype\n$docstring").substitute(
prototype="\"" + prototype.replace("\\", "\\\\").replace("\"", "\\\"") + "\\n\\n\"",
docstring="\n".join(
[" \"" + line.replace("\\", "\\\\").replace("\"", "\\\"") + "\\n\""
for line in body.split('\n')]
)
)
# Escape backslashes, newlines, and double quotes
full_docstring = full_docstring.strip().replace("\\", "\\\\").replace('\n', '\\n').replace("\"", "\\\"")
#full_docstring = full_docstring.strip().replace("\\", "\\\\").replace("\"", "\\\"")
# 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), "$py_docstring"},\n'
return Template(' {"$py_funcname", CV_PY_FN_WITH_KW_($wrap_funcname, $flags),\n $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)
@ -623,6 +654,8 @@ class FuncInfo(object):
if v.isphantom and ismethod and not self.is_static:
code_args += "_self_"
params_arg_name, extra_named_params = get_named_params_info(all_classes, v.args)
# declare all the C function arguments,
# add necessary conversions from Python objects to code_cvt_list,
# form the function/method call,
@ -692,6 +725,15 @@ class FuncInfo(object):
code_args += amp + a.name
if params_arg_name:
for a in extra_named_params:
code_decl += " PyObject* pyobj_kw_%s = NULL;\n" % (a.name,)
ainfo = "ArgInfo(\"params.%s\", %d)" % (a.name, 0)
if a.tp == 'char':
code_cvt_list.append("convert_to_char(pyobj_kw_%s, &params.%s, %s)" % (a.name, a.name, ainfo))
else:
code_cvt_list.append("pyopencv_to(pyobj_kw_%s, params.%s, %s)" % (a.name, a.name, ainfo))
code_args += ")"
if self.isconstructor:
@ -740,16 +782,22 @@ class FuncInfo(object):
])
if v.py_noptargs > 0:
fmtspec = fmtspec[:-v.py_noptargs] + "|" + fmtspec[-v.py_noptargs:]
fmtspec += "O" * len(extra_named_params)
fmtspec += ":" + fullname
# form the argument parse code that:
# - declares the list of keyword parameters
# - calls PyArg_ParseTupleAndKeywords
# - converts complex arguments from PyObject's to native OpenCV types
kw_list = ['"' + aname + '"' for aname, argno in v.py_arglist]
kw_list += ['"' + a.name + '"' for a in extra_named_params]
parse_arglist = ["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]
parse_arglist += ["&pyobj_kw_" + a.name for a in extra_named_params]
code_parse = gen_template_parse_args.substitute(
kw_list = ", ".join(['"' + aname + '"' for aname, argno in v.py_arglist]),
kw_list = ", ".join(kw_list),
fmtspec = fmtspec,
parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]),
parse_arglist = ", ".join(parse_arglist),
code_cvt = " &&\n ".join(code_cvt_list))
else:
code_parse = "if(PyObject_Size(py_args) == 0 && (!kw || PyObject_Size(kw) == 0))"
@ -933,13 +981,13 @@ class PythonWrapperGenerator(object):
# Add it as a method to the class
func_map = self.classes[classname].methods
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, is_static))
func.add_variant(decl, isphantom)
func.add_variant(self.classes, decl, isphantom)
# Add it as global function
g_name = "_".join(classes+[name])
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False))
func.add_variant(decl, isphantom)
func.add_variant(self.classes, decl, isphantom)
else:
if classname and not isconstructor:
if not isphantom:
@ -949,7 +997,7 @@ class PythonWrapperGenerator(object):
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, is_static))
func.add_variant(decl, isphantom)
func.add_variant(self.classes, decl, isphantom)
if classname and isconstructor:
self.classes[classname].constructor = func
@ -1130,7 +1178,7 @@ class PythonWrapperGenerator(object):
if __name__ == "__main__":
srcfiles = hdr_parser.opencv_hdr_list
dstdir = "/Users/vp/tmp"
dstdir = "."
if len(sys.argv) > 1:
dstdir = sys.argv[1]
if len(sys.argv) > 2:

View File

@ -258,6 +258,10 @@ class CppHeaderParser(object):
if "CV_EXPORTS_W_MAP" in l:
l = l.replace("CV_EXPORTS_W_MAP", "")
modlist.append("/Map")
if "CV_EXPORTS_W_PARAMS" in l:
l = l.replace("CV_EXPORTS_W_PARAMS", "")
modlist.append("/Map")
modlist.append("/Params")
if "CV_EXPORTS_W_SIMPLE" in l:
l = l.replace("CV_EXPORTS_W_SIMPLE", "")
modlist.append("/Simple")
@ -769,7 +773,12 @@ class CppHeaderParser(object):
var_list = [var_name1] + [i.strip() for i in var_list[1:]]
for v in var_list:
class_decl[3].append([var_type, v, "", var_modlist])
vv = v.split("=")
vname = vv[0].strip()
vdefval = ""
if len(vv) > 1:
vdefval = vv[1].strip()
class_decl[3].append([var_type, vname, vdefval, var_modlist])
return stmt_type, "", False, None
# something unknown

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python
'''
Test for disctrete fourier transform (dft)
'''
# Python 2/3 compatibility
from __future__ import print_function
import cv2 as cv
import numpy as np
import sys
from tests_common import NewOpenCVTests
class imgproc_test(NewOpenCVTests):
def test_filter2d(self):
img = self.get_sample('samples/data/lena.jpg', 1)
eps = 0.001
# compare 2 ways of computing 3x3 blur using the same function
kernel = np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]], dtype='float32')
img_blur0 = cv.filter2D(img, cv.CV_32F, kernel*(1./9))
img_blur1 = cv.filter2Dp(img, kernel, ddepth=cv.CV_32F, scale=1./9)
self.assertLess(cv.norm(img_blur0 - img_blur1, cv.NORM_INF), eps)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()