mirror of
https://github.com/opencv/opencv.git
synced 2025-06-12 20:42:53 +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_PROP_RW= \
|
||||||
CV_WRAP= \
|
CV_WRAP= \
|
||||||
CV_WRAP_AS(x)= \
|
CV_WRAP_AS(x)= \
|
||||||
|
CV_WRAP_MAPPABLE(x)= \
|
||||||
|
CV_WRAP_PHANTOM(x)= \
|
||||||
|
CV_WRAP_DEFAULT(x)= \
|
||||||
CV_CDECL= \
|
CV_CDECL= \
|
||||||
CV_Func = \
|
CV_Func = \
|
||||||
CV_DO_PRAGMA(x)= \
|
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
|
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
|
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
|
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.
|
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
|
These header files contain list of all classes, functions, constants etc. for that particular
|
||||||
modules.
|
modules.
|
||||||
|
|
||||||
Second, these header files are passed to a Python script, modules/python/src2/gen2.py. This is the
|
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.
|
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
|
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
|
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
|
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
|
arguments, argument types etc. Final list contains details of all the functions, enums, structs,
|
||||||
etc. in that header file.
|
classes etc. in that header file.
|
||||||
|
|
||||||
But header parser doesn't parse all the functions/classes in the header file. The developer has to
|
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
|
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
|
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
|
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,
|
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. 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
|
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
|
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
|
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
|
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
|
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
|
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
|
Enumeration constants don't need any wrapper macros. They are automatically wrapped. But remaining
|
||||||
functions, classes etc. need wrapper macros.
|
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}
|
@code{.cpp}
|
||||||
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
|
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
|
||||||
@endcode
|
@endcode
|
||||||
Header parser can understand the input and output arguments from keywords like
|
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,
|
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}
|
@code{.cpp}
|
||||||
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
|
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
|
||||||
CV_OUT Point2f& center, CV_OUT float& radius );
|
CV_OUT Point2f& center, CV_OUT float& radius );
|
||||||
@endcode
|
@endcode
|
||||||
For large classes also, CV_EXPORTS_W is used. To extend class methods, CV_WRAP is used.
|
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.
|
Similarly, `CV_PROP` is used for class fields.
|
||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
class CV_EXPORTS_W CLAHE : public Algorithm
|
class CV_EXPORTS_W CLAHE : public Algorithm
|
||||||
{
|
{
|
||||||
@ -90,9 +90,9 @@ public:
|
|||||||
CV_WRAP virtual double getClipLimit() const = 0;
|
CV_WRAP virtual double getClipLimit() const = 0;
|
||||||
}
|
}
|
||||||
@endcode
|
@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
|
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.
|
used to wrap overloaded methods.
|
||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
//! computes the integral image
|
//! computes the integral image
|
||||||
@ -107,9 +107,9 @@ CV_EXPORTS_AS(integral3) void integral( InputArray src, OutputArray sum,
|
|||||||
OutputArray sqsum, OutputArray tilted,
|
OutputArray sqsum, OutputArray tilted,
|
||||||
int sdepth = -1, int sqdepth = -1 );
|
int sdepth = -1, int sqdepth = -1 );
|
||||||
@endcode
|
@endcode
|
||||||
Small classes/structs are extended using CV_EXPORTS_W_SIMPLE. These structs are passed by value
|
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
|
to C++ functions. Examples are `KeyPoint`, `Match` etc. Their methods are extended by `CV_WRAP` and
|
||||||
fields are extended by CV_PROP_RW.
|
fields are extended by `CV_PROP_RW`.
|
||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
class CV_EXPORTS_W_SIMPLE DMatch
|
class CV_EXPORTS_W_SIMPLE DMatch
|
||||||
{
|
{
|
||||||
@ -125,8 +125,8 @@ public:
|
|||||||
CV_PROP_RW float distance;
|
CV_PROP_RW float distance;
|
||||||
};
|
};
|
||||||
@endcode
|
@endcode
|
||||||
Some other small classes/structs can be exported using CV_EXPORTS_W_MAP where it is exported to a
|
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.
|
Python native dictionary. `Moments()` is an example of it.
|
||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
class CV_EXPORTS_W_MAP Moments
|
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
|
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
|
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
|
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
|
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_PROP_RW
|
||||||
#define CV_WRAP
|
#define CV_WRAP
|
||||||
#define CV_WRAP_AS(synonym)
|
#define CV_WRAP_AS(synonym)
|
||||||
|
#define CV_WRAP_MAPPABLE(mappable)
|
||||||
|
#define CV_WRAP_PHANTOM(phantom_header)
|
||||||
|
#define CV_WRAP_DEFAULT(val)
|
||||||
|
|
||||||
/****************************************************************************************\
|
/****************************************************************************************\
|
||||||
* Matrix type (Mat) *
|
* Matrix type (Mat) *
|
||||||
|
@ -341,6 +341,7 @@ class JavaWrapperGenerator(object):
|
|||||||
self.classes = { "Mat" : ClassInfo([ 'class Mat', '', [], [] ], self.namespaces) }
|
self.classes = { "Mat" : ClassInfo([ 'class Mat', '', [], [] ], self.namespaces) }
|
||||||
self.module = ""
|
self.module = ""
|
||||||
self.Module = ""
|
self.Module = ""
|
||||||
|
self.enum_types = []
|
||||||
self.ported_func_list = []
|
self.ported_func_list = []
|
||||||
self.skipped_func_list = []
|
self.skipped_func_list = []
|
||||||
self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
|
self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
|
||||||
@ -421,6 +422,10 @@ class JavaWrapperGenerator(object):
|
|||||||
ci.addConst(constinfo)
|
ci.addConst(constinfo)
|
||||||
logging.info('ok: %s', 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):
|
def add_func(self, decl):
|
||||||
fi = FuncInfo(decl, namespaces=self.namespaces)
|
fi = FuncInfo(decl, namespaces=self.namespaces)
|
||||||
classname = fi.classname or self.Module
|
classname = fi.classname or self.Module
|
||||||
@ -479,6 +484,9 @@ class JavaWrapperGenerator(object):
|
|||||||
self.add_class(decl)
|
self.add_class(decl)
|
||||||
elif name.startswith("const"):
|
elif name.startswith("const"):
|
||||||
self.add_const(decl)
|
self.add_const(decl)
|
||||||
|
elif name.startswith("enum"):
|
||||||
|
# enum
|
||||||
|
self.add_enum(decl)
|
||||||
else: # function
|
else: # function
|
||||||
self.add_func(decl)
|
self.add_func(decl)
|
||||||
|
|
||||||
|
@ -27,6 +27,80 @@
|
|||||||
# define CV_PYTHON_TYPE_HEAD_INIT() PyObject_HEAD_INIT(&PyType_Type) 0,
|
# define CV_PYTHON_TYPE_HEAD_INIT() PyObject_HEAD_INIT(&PyType_Type) 0,
|
||||||
#endif
|
#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 "pyopencv_generated_include.h"
|
||||||
#include "opencv2/core/types_c.h"
|
#include "opencv2/core/types_c.h"
|
||||||
|
|
||||||
@ -36,7 +110,7 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
static PyObject* opencv_error = 0;
|
static PyObject* opencv_error = NULL;
|
||||||
|
|
||||||
static int failmsg(const char *fmt, ...)
|
static int failmsg(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
@ -97,6 +171,12 @@ try \
|
|||||||
} \
|
} \
|
||||||
catch (const cv::Exception &e) \
|
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()); \
|
PyErr_SetString(opencv_error, e.what()); \
|
||||||
return 0; \
|
return 0; \
|
||||||
}
|
}
|
||||||
@ -735,12 +815,31 @@ bool pyopencv_to(PyObject* o, UMat& um, const char* name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
PyObject* pyopencv_from(const UMat& m) {
|
PyObject* pyopencv_from(const UMat& m)
|
||||||
|
{
|
||||||
PyObject *o = PyObject_CallObject((PyObject *) &cv2_UMatWrapperType, NULL);
|
PyObject *o = PyObject_CallObject((PyObject *) &cv2_UMatWrapperType, NULL);
|
||||||
*((cv2_UMatWrapperObject *) o)->um = m;
|
*((cv2_UMatWrapperObject *) o)->um = m;
|
||||||
return o;
|
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)
|
static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo info)
|
||||||
{
|
{
|
||||||
if(!o || o == Py_None)
|
if(!o || o == Py_None)
|
||||||
@ -843,6 +942,30 @@ bool pyopencv_to(PyObject* obj, int& value, const char* name)
|
|||||||
return value != -1 || !PyErr_Occurred();
|
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<>
|
template<>
|
||||||
PyObject* pyopencv_from(const uchar& value)
|
PyObject* pyopencv_from(const uchar& value)
|
||||||
{
|
{
|
||||||
@ -1913,7 +2036,15 @@ void initcv2()
|
|||||||
|
|
||||||
PyDict_SetItemString(d, "__version__", PyString_FromString(CV_VERSION));
|
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);
|
PyDict_SetItemString(d, "error", opencv_error);
|
||||||
|
|
||||||
//Registering UMatWrapper python class in cv2 module:
|
//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)
|
if(!src || src == Py_None)
|
||||||
return true;
|
return true;
|
||||||
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
if(PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||||
{
|
{
|
||||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
dst = ((pyopencv_${name}_t*)src)->v;
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
dst = ((pyopencv_${name}_t*)src)->v;
|
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
""" % head_init_str)
|
""" % 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("""
|
gen_template_type_decl = Template("""
|
||||||
struct pyopencv_${name}_t
|
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)
|
if(!src || src == Py_None)
|
||||||
return true;
|
return true;
|
||||||
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
if(PyObject_TypeCheck(src, &pyopencv_${name}_Type))
|
||||||
{
|
{
|
||||||
failmsg("Expected ${cname} for argument '%%s'", name);
|
dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
dst = ((pyopencv_${name}_t*)src)->v.dynamicCast<${cname}>();
|
${mappable_code}
|
||||||
return true;
|
failmsg("Expected ${cname} for argument '%%s'", name);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
""" % head_init_str)
|
""" % head_init_str)
|
||||||
@ -267,6 +277,7 @@ class ClassInfo(object):
|
|||||||
self.isalgorithm = False
|
self.isalgorithm = False
|
||||||
self.methods = {}
|
self.methods = {}
|
||||||
self.props = []
|
self.props = []
|
||||||
|
self.mappables = []
|
||||||
self.consts = {}
|
self.consts = {}
|
||||||
self.base = None
|
self.base = None
|
||||||
self.constructor = None
|
self.constructor = None
|
||||||
@ -412,10 +423,11 @@ class ArgInfo(object):
|
|||||||
|
|
||||||
|
|
||||||
class FuncVariant(object):
|
class FuncVariant(object):
|
||||||
def __init__(self, classname, name, decl, isconstructor):
|
def __init__(self, classname, name, decl, isconstructor, isphantom=False):
|
||||||
self.classname = classname
|
self.classname = classname
|
||||||
self.name = self.wname = name
|
self.name = self.wname = name
|
||||||
self.isconstructor = isconstructor
|
self.isconstructor = isconstructor
|
||||||
|
self.isphantom = isphantom
|
||||||
|
|
||||||
self.docstring = decl[5]
|
self.docstring = decl[5]
|
||||||
|
|
||||||
@ -531,8 +543,8 @@ class FuncInfo(object):
|
|||||||
self.isclassmethod = isclassmethod
|
self.isclassmethod = isclassmethod
|
||||||
self.variants = []
|
self.variants = []
|
||||||
|
|
||||||
def add_variant(self, decl):
|
def add_variant(self, decl, isphantom=False):
|
||||||
self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor))
|
self.variants.append(FuncVariant(self.classname, self.name, decl, self.isconstructor, isphantom))
|
||||||
|
|
||||||
def get_wrapper_name(self):
|
def get_wrapper_name(self):
|
||||||
name = self.name
|
name = self.name
|
||||||
@ -640,6 +652,9 @@ class FuncInfo(object):
|
|||||||
all_cargs = []
|
all_cargs = []
|
||||||
parse_arglist = []
|
parse_arglist = []
|
||||||
|
|
||||||
|
if v.isphantom and ismethod and not self.isclassmethod:
|
||||||
|
code_args += "_self_"
|
||||||
|
|
||||||
# declare all the C function arguments,
|
# declare all the C function arguments,
|
||||||
# add necessary conversions from Python objects to code_cvt_list,
|
# add necessary conversions from Python objects to code_cvt_list,
|
||||||
# form the function/method call,
|
# form the function/method call,
|
||||||
@ -664,6 +679,9 @@ class FuncInfo(object):
|
|||||||
if tp.endswith("*"):
|
if tp.endswith("*"):
|
||||||
defval0 = "0"
|
defval0 = "0"
|
||||||
tp1 = tp.replace("*", "_ptr")
|
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))
|
amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
|
||||||
parse_name = a.name
|
parse_name = a.name
|
||||||
@ -714,6 +732,8 @@ class FuncInfo(object):
|
|||||||
|
|
||||||
code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname)
|
code_prelude = templ_prelude.substitute(name=selfinfo.name, cname=selfinfo.cname)
|
||||||
code_fcall = templ.substitute(name=selfinfo.name, cname=selfinfo.cname, args=code_args)
|
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:
|
else:
|
||||||
code_prelude = ""
|
code_prelude = ""
|
||||||
code_fcall = ""
|
code_fcall = ""
|
||||||
@ -835,6 +855,7 @@ class PythonWrapperGenerator(object):
|
|||||||
self.classes = {}
|
self.classes = {}
|
||||||
self.namespaces = {}
|
self.namespaces = {}
|
||||||
self.consts = {}
|
self.consts = {}
|
||||||
|
self.enum_types = []
|
||||||
self.code_include = StringIO()
|
self.code_include = StringIO()
|
||||||
self.code_types = StringIO()
|
self.code_types = StringIO()
|
||||||
self.code_funcs = StringIO()
|
self.code_funcs = StringIO()
|
||||||
@ -892,6 +913,10 @@ class PythonWrapperGenerator(object):
|
|||||||
py_signatures.append(dict(name=py_name, value=value))
|
py_signatures.append(dict(name=py_name, value=value))
|
||||||
#print(cname + ' => ' + str(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):
|
def add_func(self, decl):
|
||||||
namespace, classes, barename = self.split_decl_name(decl[0])
|
namespace, classes, barename = self.split_decl_name(decl[0])
|
||||||
cname = "::".join(namespace+classes+[barename])
|
cname = "::".join(namespace+classes+[barename])
|
||||||
@ -905,11 +930,21 @@ class PythonWrapperGenerator(object):
|
|||||||
|
|
||||||
isconstructor = name == bareclassname
|
isconstructor = name == bareclassname
|
||||||
isclassmethod = False
|
isclassmethod = False
|
||||||
|
isphantom = False
|
||||||
|
mappable = None
|
||||||
for m in decl[2]:
|
for m in decl[2]:
|
||||||
if m == "/S":
|
if m == "/S":
|
||||||
isclassmethod = True
|
isclassmethod = True
|
||||||
|
elif m == "/phantom":
|
||||||
|
isphantom = True
|
||||||
|
cname = cname.replace("::", "_")
|
||||||
elif m.startswith("="):
|
elif m.startswith("="):
|
||||||
name = m[1:]
|
name = m[1:]
|
||||||
|
elif m.startswith("/mappable="):
|
||||||
|
mappable = m[10:]
|
||||||
|
self.classes[classname].mappables.append(mappable)
|
||||||
|
return
|
||||||
|
|
||||||
if isconstructor:
|
if isconstructor:
|
||||||
name = "_".join(classes[:-1]+[name])
|
name = "_".join(classes[:-1]+[name])
|
||||||
|
|
||||||
@ -917,13 +952,13 @@ class PythonWrapperGenerator(object):
|
|||||||
# Add it as a method to the class
|
# Add it as a method to the class
|
||||||
func_map = self.classes[classname].methods
|
func_map = self.classes[classname].methods
|
||||||
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod))
|
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
|
# Add it as global function
|
||||||
g_name = "_".join(classes+[name])
|
g_name = "_".join(classes+[name])
|
||||||
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
|
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
|
||||||
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False))
|
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace, False))
|
||||||
func.add_variant(decl)
|
func.add_variant(decl, isphantom)
|
||||||
else:
|
else:
|
||||||
if classname and not isconstructor:
|
if classname and not isconstructor:
|
||||||
cname = barename
|
cname = barename
|
||||||
@ -932,7 +967,7 @@ class PythonWrapperGenerator(object):
|
|||||||
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
|
func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
|
||||||
|
|
||||||
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace, isclassmethod))
|
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:
|
if classname and isconstructor:
|
||||||
self.classes[classname].constructor = func
|
self.classes[classname].constructor = func
|
||||||
@ -996,6 +1031,9 @@ class PythonWrapperGenerator(object):
|
|||||||
elif name.startswith("const"):
|
elif name.startswith("const"):
|
||||||
# constant
|
# constant
|
||||||
self.add_const(name.replace("const ", "").strip(), decl)
|
self.add_const(name.replace("const ", "").strip(), decl)
|
||||||
|
elif name.startswith("enum"):
|
||||||
|
# enum
|
||||||
|
self.add_enum(name.replace("enum ", "").strip(), decl)
|
||||||
else:
|
else:
|
||||||
# function
|
# function
|
||||||
self.add_func(decl)
|
self.add_func(decl)
|
||||||
@ -1045,8 +1083,11 @@ class PythonWrapperGenerator(object):
|
|||||||
templ = gen_template_simple_type_decl
|
templ = gen_template_simple_type_decl
|
||||||
else:
|
else:
|
||||||
templ = gen_template_type_decl
|
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,
|
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.
|
# register classes in the same order as they have been declared.
|
||||||
# this way, base classes will be registered in Python before their derivatives.
|
# 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
|
# the list only for debugging. The real list, used in the real OpenCV build, is specified in CMakeLists.txt
|
||||||
opencv_hdr_list = [
|
opencv_hdr_list = [
|
||||||
"../../core/include/opencv2/core.hpp",
|
"../../core/include/opencv2/core.hpp",
|
||||||
|
"../../core/include/opencv2/core/mat.hpp",
|
||||||
"../../core/include/opencv2/core/ocl.hpp",
|
"../../core/include/opencv2/core/ocl.hpp",
|
||||||
"../../flann/include/opencv2/flann/miniflann.hpp",
|
"../../flann/include/opencv2/flann/miniflann.hpp",
|
||||||
"../../ml/include/opencv2/ml.hpp",
|
"../../ml/include/opencv2/ml.hpp",
|
||||||
@ -376,8 +377,6 @@ class CppHeaderParser(object):
|
|||||||
decl[2].append("/A")
|
decl[2].append("/A")
|
||||||
if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
|
if bool(re.match(r".*\)\s*const(\s*=\s*0)?", decl_str)):
|
||||||
decl[2].append("/C")
|
decl[2].append("/C")
|
||||||
if "virtual" in decl_str:
|
|
||||||
print(decl_str)
|
|
||||||
return decl
|
return decl
|
||||||
|
|
||||||
def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
|
def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
|
||||||
@ -393,8 +392,7 @@ class CppHeaderParser(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if self.wrap_mode:
|
if self.wrap_mode:
|
||||||
if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or \
|
if not (("CV_EXPORTS_AS" in decl_str) or ("CV_EXPORTS_W" in decl_str) or ("CV_WRAP" in decl_str)):
|
||||||
("CV_WRAP" in decl_str) or ("CV_WRAP_AS" in decl_str)):
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# ignore old API in the documentation check (for now)
|
# 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)
|
arg, npos3 = self.get_macro_arg(decl_str, npos)
|
||||||
func_modlist.append("="+arg)
|
func_modlist.append("="+arg)
|
||||||
decl_str = decl_str[:npos] + decl_str[npos3+1:]
|
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
|
virtual_method = False
|
||||||
pure_virtual_method = False
|
pure_virtual_method = False
|
||||||
@ -527,8 +535,6 @@ class CppHeaderParser(object):
|
|||||||
t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
|
t, npos = self.find_next_token(decl_str, ["(", ")", ",", "<", ">"], npos)
|
||||||
if not t:
|
if not t:
|
||||||
print("Error: no closing ')' at %d" % (self.lineno,))
|
print("Error: no closing ')' at %d" % (self.lineno,))
|
||||||
print(decl_str)
|
|
||||||
print(decl_str[arg_start:])
|
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
if t == "<":
|
if t == "<":
|
||||||
angle_balance += 1
|
angle_balance += 1
|
||||||
@ -705,20 +711,19 @@ class CppHeaderParser(object):
|
|||||||
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
decl[1] = ": " + ", ".join([self.get_dotted_name(b).replace(".","::") for b in bases])
|
||||||
return stmt_type, classname, True, decl
|
return stmt_type, classname, True, decl
|
||||||
|
|
||||||
if stmt.startswith("enum"):
|
if stmt.startswith("enum") or stmt.startswith("namespace"):
|
||||||
return "enum", "", True, None
|
|
||||||
|
|
||||||
if stmt.startswith("namespace"):
|
|
||||||
stmt_list = stmt.split()
|
stmt_list = stmt.split()
|
||||||
if len(stmt_list) < 2:
|
if len(stmt_list) < 2:
|
||||||
stmt_list.append("<unnamed>")
|
stmt_list.append("<unnamed>")
|
||||||
return stmt_list[0], stmt_list[1], True, None
|
return stmt_list[0], stmt_list[1], True, None
|
||||||
|
|
||||||
if stmt.startswith("extern") and "\"C\"" in stmt:
|
if stmt.startswith("extern") and "\"C\"" in stmt:
|
||||||
return "namespace", "", True, None
|
return "namespace", "", True, None
|
||||||
|
|
||||||
if end_token == "}" and context == "enum":
|
if end_token == "}" and context == "enum":
|
||||||
decl = self.parse_enum(stmt)
|
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"):
|
if end_token == ";" and stmt.startswith("typedef"):
|
||||||
# TODO: handle typedef's more intelligently
|
# 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)
|
stmt_type, name, parse_flag, decl = self.parse_stmt(stmt, token, docstring=docstring)
|
||||||
if decl:
|
if decl:
|
||||||
if stmt_type == "enum":
|
if stmt_type == "enum":
|
||||||
for d in decl:
|
if name != "<unnamed>":
|
||||||
decls.append(d)
|
decls.append(["enum " + self.get_dotted_name(name), "", [], [], None, ""])
|
||||||
|
decls.extend(decl)
|
||||||
else:
|
else:
|
||||||
decls.append(decl)
|
decls.append(decl)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user