mirror of
https://github.com/opencv/opencv.git
synced 2025-06-11 11:45:30 +08:00
Merge pull request #12269 from cv3d:improvements/binding_python
This commit is contained in:
commit
335e61dc47
@ -241,6 +241,9 @@ PREDEFINED = __cplusplus=1 \
|
||||
CV_PROP_RW= \
|
||||
CV_WRAP= \
|
||||
CV_WRAP_AS(x)= \
|
||||
CV_WRAP_MAPPABLE(x)= \
|
||||
CV_WRAP_PHANTOM(x)= \
|
||||
CV_WRAP_DEFAULT(x)= \
|
||||
CV_CDECL= \
|
||||
CV_Func = \
|
||||
CV_DO_PRAGMA(x)= \
|
||||
|
@ -20,20 +20,20 @@ A simple example on extending C++ functions to Python can be found in official P
|
||||
documentation[1]. So extending all functions in OpenCV to Python by writing their wrapper functions
|
||||
manually is a time-consuming task. So OpenCV does it in a more intelligent way. OpenCV generates
|
||||
these wrapper functions automatically from the C++ headers using some Python scripts which are
|
||||
located in modules/python/src2. We will look into what they do.
|
||||
located in `modules/python/src2`. We will look into what they do.
|
||||
|
||||
First, modules/python/CMakeFiles.txt is a CMake script which checks the modules to be extended to
|
||||
First, `modules/python/CMakeFiles.txt` is a CMake script which checks the modules to be extended to
|
||||
Python. It will automatically check all the modules to be extended and grab their header files.
|
||||
These header files contain list of all classes, functions, constants etc. for that particular
|
||||
modules.
|
||||
|
||||
Second, these header files are passed to a Python script, modules/python/src2/gen2.py. This is the
|
||||
Python bindings generator script. It calls another Python script modules/python/src2/hdr_parser.py.
|
||||
Second, these header files are passed to a Python script, `modules/python/src2/gen2.py`. This is the
|
||||
Python bindings generator script. It calls another Python script `modules/python/src2/hdr_parser.py`.
|
||||
This is the header parser script. This header parser splits the complete header file into small
|
||||
Python lists. So these lists contain all details about a particular function, class etc. For
|
||||
example, a function will be parsed to get a list containing function name, return type, input
|
||||
arguments, argument types etc. Final list contains details of all the functions, structs, classes
|
||||
etc. in that header file.
|
||||
arguments, argument types etc. Final list contains details of all the functions, enums, structs,
|
||||
classes etc. in that header file.
|
||||
|
||||
But header parser doesn't parse all the functions/classes in the header file. The developer has to
|
||||
specify which functions should be exported to Python. For that, there are certain macros added to
|
||||
@ -44,15 +44,15 @@ macros will be given in next session.
|
||||
|
||||
So header parser returns a final big list of parsed functions. Our generator script (gen2.py) will
|
||||
create wrapper functions for all the functions/classes/enums/structs parsed by header parser (You
|
||||
can find these header files during compilation in the build/modules/python/ folder as
|
||||
can find these header files during compilation in the `build/modules/python/` folder as
|
||||
pyopencv_generated_\*.h files). But there may be some basic OpenCV datatypes like Mat, Vec4i,
|
||||
Size. They need to be extended manually. For example, a Mat type should be extended to Numpy array,
|
||||
Size should be extended to a tuple of two integers etc. Similarly, there may be some complex
|
||||
structs/classes/functions etc. which need to be extended manually. All such manual wrapper functions
|
||||
are placed in modules/python/src2/cv2.cpp.
|
||||
are placed in `modules/python/src2/cv2.cpp`.
|
||||
|
||||
So now only thing left is the compilation of these wrapper files which gives us **cv2** module. So
|
||||
when you call a function, say res = equalizeHist(img1,img2) in Python, you pass two numpy arrays and
|
||||
when you call a function, say `res = equalizeHist(img1,img2)` in Python, you pass two numpy arrays and
|
||||
you expect another numpy array as the output. So these numpy arrays are converted to cv::Mat and
|
||||
then calls the equalizeHist() function in C++. Final result, res will be converted back into a Numpy
|
||||
array. So in short, almost all operations are done in C++ which gives us almost same speed as that
|
||||
@ -67,19 +67,19 @@ Header parser parse the header files based on some wrapper macros added to funct
|
||||
Enumeration constants don't need any wrapper macros. They are automatically wrapped. But remaining
|
||||
functions, classes etc. need wrapper macros.
|
||||
|
||||
Functions are extended using CV_EXPORTS_W macro. An example is shown below.
|
||||
Functions are extended using `CV_EXPORTS_W` macro. An example is shown below.
|
||||
@code{.cpp}
|
||||
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
|
||||
@endcode
|
||||
Header parser can understand the input and output arguments from keywords like
|
||||
InputArray, OutputArray etc. But sometimes, we may need to hardcode inputs and outputs. For that,
|
||||
macros like CV_OUT, CV_IN_OUT etc. are used.
|
||||
macros like `CV_OUT`, `CV_IN_OUT` etc. are used.
|
||||
@code{.cpp}
|
||||
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
|
||||
CV_OUT Point2f& center, CV_OUT float& radius );
|
||||
@endcode
|
||||
For large classes also, CV_EXPORTS_W is used. To extend class methods, CV_WRAP is used.
|
||||
Similarly, CV_PROP is used for class fields.
|
||||
For large classes also, `CV_EXPORTS_W` is used. To extend class methods, `CV_WRAP` is used.
|
||||
Similarly, `CV_PROP` is used for class fields.
|
||||
@code{.cpp}
|
||||
class CV_EXPORTS_W CLAHE : public Algorithm
|
||||
{
|
||||
@ -90,9 +90,9 @@ public:
|
||||
CV_WRAP virtual double getClipLimit() const = 0;
|
||||
}
|
||||
@endcode
|
||||
Overloaded functions can be extended using CV_EXPORTS_AS. But we need to pass a new name so that
|
||||
Overloaded functions can be extended using `CV_EXPORTS_AS`. But we need to pass a new name so that
|
||||
each function will be called by that name in Python. Take the case of integral function below. Three
|
||||
functions are available, so each one is named with a suffix in Python. Similarly CV_WRAP_AS can be
|
||||
functions are available, so each one is named with a suffix in Python. Similarly `CV_WRAP_AS` can be
|
||||
used to wrap overloaded methods.
|
||||
@code{.cpp}
|
||||
//! computes the integral image
|
||||
@ -107,9 +107,9 @@ CV_EXPORTS_AS(integral3) void integral( InputArray src, OutputArray sum,
|
||||
OutputArray sqsum, OutputArray tilted,
|
||||
int sdepth = -1, int sqdepth = -1 );
|
||||
@endcode
|
||||
Small classes/structs are extended using CV_EXPORTS_W_SIMPLE. These structs are passed by value
|
||||
to C++ functions. Examples are KeyPoint, Match etc. Their methods are extended by CV_WRAP and
|
||||
fields are extended by CV_PROP_RW.
|
||||
Small classes/structs are extended using `CV_EXPORTS_W_SIMPLE`. These structs are passed by value
|
||||
to C++ functions. Examples are `KeyPoint`, `Match` etc. Their methods are extended by `CV_WRAP` and
|
||||
fields are extended by `CV_PROP_RW`.
|
||||
@code{.cpp}
|
||||
class CV_EXPORTS_W_SIMPLE DMatch
|
||||
{
|
||||
@ -125,8 +125,8 @@ public:
|
||||
CV_PROP_RW float distance;
|
||||
};
|
||||
@endcode
|
||||
Some other small classes/structs can be exported using CV_EXPORTS_W_MAP where it is exported to a
|
||||
Python native dictionary. Moments() is an example of it.
|
||||
Some other small classes/structs can be exported using `CV_EXPORTS_W_MAP` where it is exported to a
|
||||
Python native dictionary. `Moments()` is an example of it.
|
||||
@code{.cpp}
|
||||
class CV_EXPORTS_W_MAP Moments
|
||||
{
|
||||
@ -142,6 +142,41 @@ public:
|
||||
So these are the major extension macros available in OpenCV. Typically, a developer has to put
|
||||
proper macros in their appropriate positions. Rest is done by generator scripts. Sometimes, there
|
||||
may be an exceptional cases where generator scripts cannot create the wrappers. Such functions need
|
||||
to be handled manually, to do this write your own pyopencv_*.hpp extending headers and put them into
|
||||
to be handled manually, to do this write your own `pyopencv_*.hpp` extending headers and put them into
|
||||
misc/python subdirectory of your module. But most of the time, a code written according to OpenCV
|
||||
coding guidelines will be automatically wrapped by generator scripts.
|
||||
coding guidelines will be automatically wrapped by generator scripts.
|
||||
|
||||
More advanced cases involves providing Python with additional features that does not exist
|
||||
in the C++ interface such as extra methods, type mappings, or to provide default arguments.
|
||||
We will take `UMat` datatype as an example of such cases later on.
|
||||
First, to provide Python-specific methods, `CV_WRAP_PHANTOM` is utilized in a similar manner to
|
||||
`CV_WRAP`, except that it takes the method header as its argument, and you would need to provide
|
||||
the method body in your own `pyopencv_*.hpp` extension. `UMat::queue()` and `UMat::context()` are
|
||||
an example of such phantom methods that does not exist in C++ interface, but are needed to handle
|
||||
OpenCL functionalities at the Python side.
|
||||
Second, if an already-existing datatype(s) is mappable to your class, it is highly preferable to
|
||||
indicate such capacity using `CV_WRAP_MAPPABLE` with the source type as its argument,
|
||||
rather than crafting your own binding function(s). This is the case of `UMat` which maps from `Mat`.
|
||||
Finally, if a default argument is needed, but it is not provided in the native C++ interface,
|
||||
you can provide it for Python side as the argument of `CV_WRAP_DEFAULT`. As per the `UMat::getMat`
|
||||
example below:
|
||||
@code{.cpp}
|
||||
class CV_EXPORTS_W UMat
|
||||
{
|
||||
public:
|
||||
//! Mat is mappable to UMat.
|
||||
// You would need to provide `static bool cv_mappable_to(const Ptr<Mat>& src, Ptr<UMat>& dst)`
|
||||
CV_WRAP_MAPPABLE(Ptr<Mat>);
|
||||
|
||||
/! returns the OpenCL queue used by OpenCV UMat.
|
||||
// You would need to provide the method body in the binder code
|
||||
CV_WRAP_PHANTOM(static void* queue());
|
||||
|
||||
//! returns the OpenCL context used by OpenCV UMat
|
||||
// You would need to provide the method body in the binder code
|
||||
CV_WRAP_PHANTOM(static void* context());
|
||||
|
||||
//! The wrapped method become equvalent to `get(int flags = ACCESS_RW)`
|
||||
CV_WRAP_AS(get) Mat getMat(int flags CV_WRAP_DEFAULT(ACCESS_RW)) const;
|
||||
};
|
||||
@endcode
|
||||
|
@ -307,6 +307,9 @@ Cv64suf;
|
||||
#define CV_PROP_RW
|
||||
#define CV_WRAP
|
||||
#define CV_WRAP_AS(synonym)
|
||||
#define CV_WRAP_MAPPABLE(mappable)
|
||||
#define CV_WRAP_PHANTOM(phantom_header)
|
||||
#define CV_WRAP_DEFAULT(val)
|
||||
|
||||
/****************************************************************************************\
|
||||
* Matrix type (Mat) *
|
||||
|
@ -341,6 +341,7 @@ class JavaWrapperGenerator(object):
|
||||
self.classes = { "Mat" : ClassInfo([ 'class Mat', '', [], [] ], self.namespaces) }
|
||||
self.module = ""
|
||||
self.Module = ""
|
||||
self.enum_types = []
|
||||
self.ported_func_list = []
|
||||
self.skipped_func_list = []
|
||||
self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
|
||||
@ -421,6 +422,10 @@ class JavaWrapperGenerator(object):
|
||||
ci.addConst(constinfo)
|
||||
logging.info('ok: %s', constinfo)
|
||||
|
||||
def add_enum(self, decl): # [ "enum cname", "", [], [] ]
|
||||
enumname = decl[0].replace("enum ", "").strip()
|
||||
self.enum_types.append(enumname)
|
||||
|
||||
def add_func(self, decl):
|
||||
fi = FuncInfo(decl, namespaces=self.namespaces)
|
||||
classname = fi.classname or self.Module
|
||||
@ -479,6 +484,9 @@ class JavaWrapperGenerator(object):
|
||||
self.add_class(decl)
|
||||
elif name.startswith("const"):
|
||||
self.add_const(decl)
|
||||
elif name.startswith("enum"):
|
||||
# enum
|
||||
self.add_enum(decl)
|
||||
else: # function
|
||||
self.add_func(decl)
|
||||
|
||||
|
@ -27,6 +27,80 @@
|
||||
# define CV_PYTHON_TYPE_HEAD_INIT() PyObject_HEAD_INIT(&PyType_Type) 0,
|
||||
#endif
|
||||
|
||||
#define CV_PY_TO_CLASS(TYPE) \
|
||||
template<> bool pyopencv_to(PyObject* dst, Ptr<TYPE>& src, const char* name); \
|
||||
\
|
||||
template<> \
|
||||
bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \
|
||||
{ \
|
||||
if (!dst || dst == Py_None) \
|
||||
return true; \
|
||||
Ptr<TYPE> ptr; \
|
||||
\
|
||||
if (!pyopencv_to(dst, ptr, name)) return false; \
|
||||
src = *ptr; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define CV_PY_FROM_CLASS(TYPE) \
|
||||
template<> PyObject* pyopencv_from(const Ptr<TYPE>& src); \
|
||||
\
|
||||
template<> \
|
||||
PyObject* pyopencv_from(const TYPE& src) \
|
||||
{ \
|
||||
Ptr<TYPE> ptr(new TYPE()); \
|
||||
\
|
||||
*ptr = src; \
|
||||
return pyopencv_from(ptr); \
|
||||
}
|
||||
|
||||
#define CV_PY_TO_CLASS_PTR(TYPE) \
|
||||
template<> bool pyopencv_to(PyObject* dst, Ptr<TYPE>& src, const char* name); \
|
||||
\
|
||||
template<> \
|
||||
bool pyopencv_to(PyObject* dst, TYPE*& src, const char* name) \
|
||||
{ \
|
||||
if (!dst || dst == Py_None) \
|
||||
return true; \
|
||||
Ptr<TYPE> ptr; \
|
||||
\
|
||||
if (!pyopencv_to(dst, ptr, name)) return false; \
|
||||
src = ptr; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define CV_PY_FROM_CLASS_PTR(TYPE) \
|
||||
template<> PyObject* pyopencv_from(const Ptr<TYPE>& src); \
|
||||
\
|
||||
static PyObject* pyopencv_from(TYPE*& src) \
|
||||
{ \
|
||||
return pyopencv_from(Ptr<TYPE>(src)); \
|
||||
}
|
||||
|
||||
#define CV_PY_TO_ENUM(TYPE) \
|
||||
template<> bool pyopencv_to(PyObject* dst, std::underlying_type<TYPE>::type& src, const char* name); \
|
||||
\
|
||||
template<> \
|
||||
bool pyopencv_to(PyObject* dst, TYPE& src, const char* name) \
|
||||
{ \
|
||||
if (!dst || dst == Py_None) \
|
||||
return true; \
|
||||
std::underlying_type<TYPE>::type underlying; \
|
||||
\
|
||||
if (!pyopencv_to(dst, underlying, name)) return false; \
|
||||
src = static_cast<TYPE>(underlying); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define CV_PY_FROM_ENUM(TYPE) \
|
||||
template<> PyObject* pyopencv_from(const std::underlying_type<TYPE>::type& src); \
|
||||
\
|
||||
template<> \
|
||||
PyObject* pyopencv_from(const TYPE& src) \
|
||||
{ \
|
||||
return pyopencv_from(static_cast<std::underlying_type<TYPE>::type>(src)); \
|
||||
}
|
||||
|
||||
#include "pyopencv_generated_include.h"
|
||||
#include "opencv2/core/types_c.h"
|
||||
|
||||
@ -36,7 +110,7 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
static PyObject* opencv_error = 0;
|
||||
static PyObject* opencv_error = NULL;
|
||||
|
||||
static int failmsg(const char *fmt, ...)
|
||||
{
|
||||
@ -97,6 +171,12 @@ try \
|
||||
} \
|
||||
catch (const cv::Exception &e) \
|
||||
{ \
|
||||
PyObject_SetAttrString(opencv_error, "file", PyString_FromString(e.file.c_str())); \
|
||||
PyObject_SetAttrString(opencv_error, "func", PyString_FromString(e.func.c_str())); \
|
||||
PyObject_SetAttrString(opencv_error, "line", PyInt_FromLong(e.line)); \
|
||||
PyObject_SetAttrString(opencv_error, "code", PyInt_FromLong(e.code)); \
|
||||
PyObject_SetAttrString(opencv_error, "msg", PyString_FromString(e.msg.c_str())); \
|
||||
PyObject_SetAttrString(opencv_error, "err", PyString_FromString(e.err.c_str())); \
|
||||
PyErr_SetString(opencv_error, e.what()); \
|
||||
return 0; \
|
||||
}
|
||||
@ -735,12 +815,31 @@ bool pyopencv_to(PyObject* o, UMat& um, const char* name)
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const UMat& m) {
|
||||
PyObject* pyopencv_from(const UMat& m)
|
||||
{
|
||||
PyObject *o = PyObject_CallObject((PyObject *) &cv2_UMatWrapperType, NULL);
|
||||
*((cv2_UMatWrapperObject *) o)->um = m;
|
||||
return o;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool pyopencv_to(PyObject* obj, void*& ptr, const char* name)
|
||||
{
|
||||
(void)name;
|
||||
if (!obj || obj == Py_None)
|
||||
return true;
|
||||
|
||||
if (!PyLong_Check(obj))
|
||||
return false;
|
||||
ptr = PyLong_AsVoidPtr(obj);
|
||||
return ptr != NULL && !PyErr_Occurred();
|
||||
}
|
||||
|
||||
static PyObject* pyopencv_from(void*& ptr)
|
||||
{
|
||||
return PyLong_FromVoidPtr(ptr);
|
||||
}
|
||||
|
||||
static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info)
|
||||
{
|
||||
if(!o || o == Py_None)
|
||||
@ -843,6 +942,30 @@ bool pyopencv_to(PyObject* obj, int& value, const char* name)
|
||||
return value != -1 || !PyErr_Occurred();
|
||||
}
|
||||
|
||||
#if defined (_M_AMD64) || defined (__x86_64__)
|
||||
template<>
|
||||
PyObject* pyopencv_from(const unsigned int& value)
|
||||
{
|
||||
return PyLong_FromUnsignedLong(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
bool pyopencv_to(PyObject* obj, unsigned int& value, const char* name)
|
||||
{
|
||||
(void)name;
|
||||
if(!obj || obj == Py_None)
|
||||
return true;
|
||||
if(PyInt_Check(obj))
|
||||
value = (unsigned int)PyInt_AsLong(obj);
|
||||
else if(PyLong_Check(obj))
|
||||
value = (unsigned int)PyLong_AsLong(obj);
|
||||
else
|
||||
return false;
|
||||
return value != (unsigned int)-1 || !PyErr_Occurred();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const uchar& value)
|
||||
{
|
||||
@ -1913,7 +2036,15 @@ void initcv2()
|
||||
|
||||
PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION));
|
||||
|
||||
opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, NULL);
|
||||
PyObject *opencv_error_dict = PyDict_New();
|
||||
PyDict_SetItemString(opencv_error_dict, "file", Py_None);
|
||||
PyDict_SetItemString(opencv_error_dict, "func", Py_None);
|
||||
PyDict_SetItemString(opencv_error_dict, "line", Py_None);
|
||||
PyDict_SetItemString(opencv_error_dict, "code", Py_None);
|
||||
PyDict_SetItemString(opencv_error_dict, "msg", Py_None);
|
||||
PyDict_SetItemString(opencv_error_dict, "err", Py_None);
|
||||
opencv_error = PyErr_NewException((char*)MODULESTR".error", NULL, opencv_error_dict);
|
||||
Py_DECREF(opencv_error_dict);
|
||||
PyDict_SetItemString(d, "error", opencv_error);
|
||||
|
||||
//Registering UMatWrapper python class in cv2 module:
|
||||
|
@ -81,16 +81,25 @@ template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name)
|
||||
{
|
||||
if(!src || src == Py_None)
|
||||
return true;
|
||||
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||
if(PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||
{
|
||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||
return false;
|
||||
dst = ((pyopencv_${name}_t*)src)->v;
|
||||
return true;
|
||||
}
|
||||
dst = ((pyopencv_${name}_t*)src)->v;
|
||||
return true;
|
||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||
return false;
|
||||
}
|
||||
""" % head_init_str)
|
||||
|
||||
gen_template_mappable = Template("""
|
||||
{
|
||||
${mappable} _src;
|
||||
if (pyopencv_to(src, _src, name))
|
||||
{
|
||||
return cv_mappable_to(_src, dst);
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
gen_template_type_decl = Template("""
|
||||
struct pyopencv_${name}_t
|
||||
@ -124,13 +133,14 @@ template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name)
|
||||
{
|
||||
if(!src || src == Py_None)
|
||||
return true;
|
||||
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||
if(PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||
{
|
||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||
return false;
|
||||
dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
|
||||
return true;
|
||||
}
|
||||
dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
|
||||
return true;
|
||||
${mappable_code}
|
||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
""" % head_init_str)
|
||||
@ -267,6 +277,7 @@ class ClassInfo(object):
|
||||
self.isalgorithm = False
|
||||
self.methods = {}
|
||||
self.props = []
|
||||
self.mappables = []
|
||||
self.consts = {}
|
||||
self.base = None
|
||||
self.constructor = None
|
||||
@ -412,10 +423,11 @@ class ArgInfo(object):
|
||||
|
||||
|
||||
class FuncVariant(object):
|
||||
def __init__(self, classname, name, decl, isconstructor):
|
||||
def __init__(self, classname, name, decl, isconstructor, isphantom=False):
|
||||
self.classname = classname
|
||||
self.name = self.wname = name
|
||||
self.isconstructor = isconstructor
|
||||
self.isphantom = isphantom
|
||||
|
||||
self.docstring = decl[5]
|
||||
|
||||
@ -531,8 +543,8 @@ class FuncInfo(object):
|
||||
self.isclassmethod = isclassmethod
|
||||
self.variants = []
|
||||
|
||||
def add_variant(self, decl):
|
||||
self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor))
|
||||
def add_variant(self, decl, isphantom=False):
|
||||
self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor, isphantom))
|
||||
|
||||
def get_wrapper_name(self):
|
||||
name = self.name
|
||||
@ -640,6 +652,9 @@ class FuncInfo(object):
|
||||
all_cargs = []
|
||||
parse_arglist = []
|
||||
|
||||
if v.isphantom and ismethod and not self.isclassmethod:
|
||||
code_args += "_self_"
|
||||
|
||||
# declare all the C function arguments,
|
||||
# add necessary conversions from Python objects to code_cvt_list,
|
||||
# form the function/method call,
|
||||
@ -664,6 +679,9 @@ class FuncInfo(object):
|
||||
if tp.endswith("*"):
|
||||
defval0 = "0"
|
||||
tp1 = tp.replace("*", "_ptr")
|
||||
tp_candidates = [a.tp, normalize_class_name(self.namespace + "." + a.tp)]
|
||||
if any(tp in codegen.enum_types for tp in tp_candidates):
|
||||
defval0 = "static_cast<%s>(%d)" % (a.tp, 0)
|
||||
|
||||
amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
|
||||
parse_name = a.name
|
||||
@ -714,6 +732,8 @@ class FuncInfo(object):
|
||||
|
||||
code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname)
|
||||
code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args)
|
||||
if v.isphantom:
|
||||
code_fcall = code_fcall.replace("new " + selfinfo.cname, self.cname.replace("::", "_"))
|
||||
else:
|
||||
code_prelude = ""
|
||||
code_fcall = ""
|
||||
@ -835,6 +855,7 @@ class PythonWrapperGenerator(object):
|
||||
self.classes = {}
|
||||
self.namespaces = {}
|
||||
self.consts = {}
|
||||
self.enum_types = []
|
||||
self.code_include = StringIO()
|
||||
self.code_types = StringIO()
|
||||
self.code_funcs = StringIO()
|
||||
@ -892,6 +913,10 @@ class PythonWrapperGenerator(object):
|
||||
py_signatures.append(dict(name=py_name, value=value))
|
||||
#print(cname + ' => ' + str(py_name) + ' (value=' + value + ')')
|
||||
|
||||
def add_enum(self, name, decl):
|
||||
enumname = normalize_class_name(name)
|
||||
self.enum_types.append(enumname)
|
||||
|
||||
def add_func(self, decl):
|
||||
namespace, classes, barename = self.split_decl_name(decl[0])
|
||||
cname = "::".join(namespace+classes+[barename])
|
||||
@ -905,11 +930,21 @@ class PythonWrapperGenerator(object):
|
||||
|
||||
isconstructor = name == bareclassname
|
||||
isclassmethod = False
|
||||
isphantom = False
|
||||
mappable = None
|
||||
for m in decl[2]:
|
||||
if m == "/S":
|
||||
isclassmethod = True
|
||||
elif m == "/phantom":
|
||||
isphantom = True
|
||||
cname = cname.replace("::", "_")
|
||||
elif m.startswith("="):
|
||||
name = m[1:]
|
||||
elif m.startswith("/mappable="):
|
||||
mappable = m[10:]
|
||||
self.classes[classname].mappables.append(mappable)
|
||||
return
|
||||
|
||||
if isconstructor:
|
||||
name = "_".join(classes[:-1]+[name])
|
||||
|
||||
@ -917,13 +952,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, isclassmethod))
|
||||
func.add_variant(decl)
|
||||
func.add_variant(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)
|
||||
func.add_variant(decl, isphantom)
|
||||
else:
|
||||
if classname and not isconstructor:
|
||||
cname = barename
|
||||
@ -932,7 +967,7 @@ class PythonWrapperGenerator(object):
|
||||
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
|
||||
|
||||
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod))
|
||||
func.add_variant(decl)
|
||||
func.add_variant(decl, isphantom)
|
||||
|
||||
if classname and isconstructor:
|
||||
self.classes[classname].constructor = func
|
||||
@ -996,6 +1031,9 @@ class PythonWrapperGenerator(object):
|
||||
elif name.startswith("const"):
|
||||
# constant
|
||||
self.add_const(name.replace("const ", "").strip(), decl)
|
||||
elif name.startswith("enum"):
|
||||
# enum
|
||||
self.add_enum(name.replace("enum ", "").strip(), decl)
|
||||
else:
|
||||
# function
|
||||
self.add_func(decl)
|
||||
@ -1045,8 +1083,11 @@ class PythonWrapperGenerator(object):
|
||||
templ = gen_template_simple_type_decl
|
||||
else:
|
||||
templ = gen_template_type_decl
|
||||
mappable_code = "\n".join([
|
||||
gen_template_mappable.substitute(cname=classinfo.cname, mappable=mappable)
|
||||
for mappable in classinfo.mappables])
|
||||
self.code_types.write(templ.substitute(name=name, wname=classinfo.wname, cname=classinfo.cname, sname=classinfo.sname,
|
||||
cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname)))
|
||||
cname1=("cv::Algorithm" if classinfo.isalgorithm else classinfo.cname), mappable_code=mappable_code))
|
||||
|
||||
# register classes in the same order as they have been declared.
|
||||
# this way, base classes will be registered in Python before their derivatives.
|
||||
|
@ -6,6 +6,7 @@ import os, sys, re, string, io
|
||||
# the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt
|
||||
opencv_hdr_list = [
|
||||
"../../core/include/opencv2/core.hpp",
|
||||
"../../core/include/opencv2/core/mat.hpp",
|
||||
"../../core/include/opencv2/core/ocl.hpp",
|
||||
"../../flann/include/opencv2/flann/miniflann.hpp",
|
||||
"../../ml/include/opencv2/ml.hpp",
|
||||
@ -376,8 +377,6 @@ class CppHeaderParser(object):
|
||||
decl[2].append("/A")
|
||||
if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
|
||||
decl[2].append("/C")
|
||||
if "virtual" in decl_str:
|
||||
print(decl_str)
|
||||
return decl
|
||||
|
||||
def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
|
||||
@ -393,8 +392,7 @@ class CppHeaderParser(object):
|
||||
"""
|
||||
|
||||
if self.wrap_mode:
|
||||
if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
|
||||
("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
|
||||
if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or ("CV_WRAP" in decl_str)):
|
||||
return []
|
||||
|
||||
# ignore old API in the documentation check (for now)
|
||||
@ -414,6 +412,16 @@ class CppHeaderParser(object):
|
||||
arg, npos3 = self.get_macro_arg(decl_str, npos)
|
||||
func_modlist.append("="+arg)
|
||||
decl_str = decl_str[:npos] + decl_str[npos3+1:]
|
||||
npos = decl_str.find("CV_WRAP_PHANTOM")
|
||||
if npos >= 0:
|
||||
decl_str, _ = self.get_macro_arg(decl_str, npos)
|
||||
func_modlist.append("/phantom")
|
||||
npos = decl_str.find("CV_WRAP_MAPPABLE")
|
||||
if npos >= 0:
|
||||
mappable, npos3 = self.get_macro_arg(decl_str, npos)
|
||||
func_modlist.append("/mappable="+mappable)
|
||||
classname = top[1]
|
||||
return ['.'.join([classname, classname]), None, func_modlist, [], None, None]
|
||||
|
||||
virtual_method = False
|
||||
pure_virtual_method = False
|
||||
@ -527,8 +535,6 @@ class CppHeaderParser(object):
|
||||
t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
|
||||
if not t:
|
||||
print("Error: no closing ')' at %d" % (self.lineno,))
|
||||
print(decl_str)
|
||||
print(decl_str[arg_start:])
|
||||
sys.exit(-1)
|
||||
if t == "<":
|
||||
angle_balance += 1
|
||||
@ -705,20 +711,19 @@ class CppHeaderParser(object):
|
||||
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
||||
return stmt_type, classname, True, decl
|
||||
|
||||
if stmt.startswith("enum"):
|
||||
return "enum", "", True, None
|
||||
|
||||
if stmt.startswith("namespace"):
|
||||
if stmt.startswith("enum") or stmt.startswith("namespace"):
|
||||
stmt_list = stmt.split()
|
||||
if len(stmt_list) < 2:
|
||||
stmt_list.append("<unnamed>")
|
||||
return stmt_list[0], stmt_list[1], True, None
|
||||
|
||||
if stmt.startswith("extern") and "\"C\"" in stmt:
|
||||
return "namespace", "", True, None
|
||||
|
||||
if end_token == "}" and context == "enum":
|
||||
decl = self.parse_enum(stmt)
|
||||
return "enum", "", False, decl
|
||||
name = stack_top[self.BLOCK_NAME]
|
||||
return "enum", name, False, decl
|
||||
|
||||
if end_token == ";" and stmt.startswith("typedef"):
|
||||
# TODO: handle typedef's more intelligently
|
||||
@ -896,8 +901,9 @@ class CppHeaderParser(object):
|
||||
stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring)
|
||||
if decl:
|
||||
if stmt_type == "enum":
|
||||
for d in decl:
|
||||
decls.append(d)
|
||||
if name != "<unnamed>":
|
||||
decls.append(["enum " + self.get_dotted_name(name), "", [], [], None, ""])
|
||||
decls.extend(decl)
|
||||
else:
|
||||
decls.append(decl)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user