Support Python binding for CUDA functionalities

This commit is contained in:
Hamdi Sahloul
2018-08-06 20:46:46 +09:00
parent 053259fd92
commit 293facbae7
19 changed files with 224 additions and 112 deletions
+5 -3
View File
@@ -32,9 +32,7 @@ endforeach(m)
# header blacklist
ocv_list_filterout(opencv_hdrs "modules/.*\\\\.h$")
ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda")
ocv_list_filterout(opencv_hdrs "modules/cuda.*")
ocv_list_filterout(opencv_hdrs "modules/cudev")
ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda/")
ocv_list_filterout(opencv_hdrs "modules/core/.*/hal/")
ocv_list_filterout(opencv_hdrs "modules/core/.*/opencl/")
ocv_list_filterout(opencv_hdrs "modules/.+/utils/.*")
@@ -43,6 +41,10 @@ ocv_list_filterout(opencv_hdrs "modules/.*_inl\\\\.h*")
ocv_list_filterout(opencv_hdrs "modules/.*\\\\.details\\\\.h*")
ocv_list_filterout(opencv_hdrs "modules/.*\\\\.private\\\\.h*")
ocv_list_filterout(opencv_hdrs "modules/.*/detection_based_tracker\\\\.hpp") # Conditional compilation
if(NOT HAVE_CUDA)
ocv_list_filterout(opencv_hdrs "modules/cuda.*")
ocv_list_filterout(opencv_hdrs "modules/cudev")
endif()
set(cv2_generated_files
"${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h"
+17 -15
View File
@@ -12,17 +12,19 @@ else:
ignored_arg_types = ["RNG*"]
pass_by_val_types = ["Point*", "Point2f*", "Rect*", "String*", "double*", "float*", "int*"]
gen_template_check_self = Template(""" $cname* _self_ = NULL;
if(PyObject_TypeCheck(self, &pyopencv_${name}_Type))
_self_ = ${amp}((pyopencv_${name}_t*)self)->v${get};
if (_self_ == NULL)
if (!_self_)
return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
""")
gen_template_check_self_algo = Template(""" $cname* _self_ = NULL;
if(PyObject_TypeCheck(self, &pyopencv_${name}_Type))
_self_ = dynamic_cast<$cname*>(${amp}((pyopencv_${name}_t*)self)->v.get());
if (_self_ == NULL)
if (!_self_)
return failmsgp("Incorrect type of self (must be '${name}' or its derivative)");
""")
@@ -77,7 +79,7 @@ template<> PyObject* pyopencv_from(const ${cname}& r)
template<> bool pyopencv_to(PyObject* src, ${cname}& dst, const char* name)
{
if( src == NULL || src == Py_None )
if(!src || src == Py_None)
return true;
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
{
@@ -120,7 +122,7 @@ template<> PyObject* pyopencv_from(const Ptr<${cname}>& r)
template<> bool pyopencv_to(PyObject* src, Ptr<${cname}>& dst, const char* name)
{
if( src == NULL || src == Py_None )
if(!src || src == Py_None)
return true;
if(!PyObject_TypeCheck(src, &pyopencv_${name}_Type))
{
@@ -192,7 +194,7 @@ gen_template_get_prop_algo = Template("""
static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *closure)
{
$cname* _self_ = dynamic_cast<$cname*>(p->v.get());
if (_self_ == NULL)
if (!_self_)
return failmsgp("Incorrect type of object (must be '${name}' or its derivative)");
return pyopencv_from(_self_${access}${member});
}
@@ -201,7 +203,7 @@ static PyObject* pyopencv_${name}_get_${member}(pyopencv_${name}_t* p, void *clo
gen_template_set_prop = Template("""
static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
{
if (value == NULL)
if (!value)
{
PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
return -1;
@@ -213,13 +215,13 @@ static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value
gen_template_set_prop_algo = Template("""
static int pyopencv_${name}_set_${member}(pyopencv_${name}_t* p, PyObject *value, void *closure)
{
if (value == NULL)
if (!value)
{
PyErr_SetString(PyExc_TypeError, "Cannot delete the ${member} attribute");
return -1;
}
$cname* _self_ = dynamic_cast<$cname*>(p->v.get());
if (_self_ == NULL)
if (!_self_)
{
failmsgp("Incorrect type of object (must be '${name}' or its derivative)");
return -1;
@@ -402,7 +404,7 @@ class ArgInfo(object):
self.py_outputarg = False
def isbig(self):
return self.tp == "Mat" or self.tp == "vector_Mat"\
return self.tp == "Mat" or self.tp == "vector_Mat" or self.tp == "cuda::GpuMat"\
or self.tp == "UMat" or self.tp == "vector_UMat" # or self.tp.startswith("vector")
def crepr(self):
@@ -656,15 +658,12 @@ class FuncInfo(object):
tp1 = tp = a.tp
amp = ""
defval0 = ""
if tp.endswith("*"):
if tp in pass_by_val_types:
tp = tp1 = tp[:-1]
amp = "&"
if tp.endswith("*"):
defval0 = "0"
tp1 = tp.replace("*", "_ptr")
if tp1.endswith("*"):
print("Error: type with star: a.tp=%s, tp=%s, tp1=%s" % (a.tp, tp, tp1))
sys.exit(-1)
amapping = simple_argtype_mapping.get(tp, (tp, "O", defval0))
parse_name = a.name
@@ -686,6 +685,9 @@ class FuncInfo(object):
if "UMat" in tp:
if "Mat" in defval and "UMat" not in defval:
defval = defval.replace("Mat", "UMat")
if "cuda::GpuMat" in tp:
if "Mat" in defval and "GpuMat" not in defval:
defval = defval.replace("Mat", "cuda::GpuMat")
# "tp arg = tp();" is equivalent to "tp arg;" in the case of complex types
if defval == tp + "()" and amapping[1] == "O":
defval = ""
@@ -754,7 +756,7 @@ class FuncInfo(object):
parse_arglist = ", ".join(["&" + all_cargs[argno][1] for aname, argno in v.py_arglist]),
code_cvt = " &&\n ".join(code_cvt_list))
else:
code_parse = "if(PyObject_Size(args) == 0 && (kw == NULL || PyObject_Size(kw) == 0))"
code_parse = "if(PyObject_Size(args) == 0 && (!kw || PyObject_Size(kw) == 0))"
if len(v.py_outlist) == 0:
code_ret = "Py_RETURN_NONE"
@@ -975,7 +977,7 @@ class PythonWrapperGenerator(object):
def gen(self, srcfiles, output_path):
self.clear()
self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True)
self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
# step 1: scan the headers and build more descriptive maps of classes, consts, functions
for hdr in srcfiles:
+17 -8
View File
@@ -32,8 +32,9 @@ original_return_type is None if the original_return_type is the same as return_v
class CppHeaderParser(object):
def __init__(self, generate_umat_decls=False):
def __init__(self, generate_umat_decls=False, generate_gpumat_decls=False):
self._generate_umat_decls = generate_umat_decls
self._generate_gpumat_decls = generate_gpumat_decls
self.BLOCK_TYPE = 0
self.BLOCK_NAME = 1
@@ -379,7 +380,7 @@ class CppHeaderParser(object):
print(decl_str)
return decl
def parse_func_decl(self, decl_str, use_umat=False, docstring=""):
def parse_func_decl(self, decl_str, mat="Mat", docstring=""):
"""
Parses the function or method declaration in the form:
[([CV_EXPORTS] <rettype>) | CVAPI(rettype)]
@@ -563,8 +564,6 @@ class CppHeaderParser(object):
a = a[:eqpos].strip()
arg_type, arg_name, modlist, argno = self.parse_arg(a, argno)
if self.wrap_mode:
mat = "UMat" if use_umat else "Mat"
# TODO: Vectors should contain UMat, but this is not very easy to support and not very needed
vector_mat = "vector_{}".format("Mat")
vector_mat_template = "vector<{}>".format("Mat")
@@ -639,7 +638,7 @@ class CppHeaderParser(object):
n = "cv.Algorithm"
return n
def parse_stmt(self, stmt, end_token, use_umat=False, docstring=""):
def parse_stmt(self, stmt, end_token, mat="Mat", docstring=""):
"""
parses the statement (ending with ';' or '}') or a block head (ending with '{')
@@ -731,7 +730,7 @@ class CppHeaderParser(object):
# since we filtered off the other places where '(' can normally occur:
# - code blocks
# - function pointer typedef's
decl = self.parse_func_decl(stmt, use_umat=use_umat, docstring=docstring)
decl = self.parse_func_decl(stmt, mat=mat, docstring=docstring)
# we return parse_flag == False to prevent the parser to look inside function/method bodies
# (except for tracking the nested blocks)
return stmt_type, "", False, decl
@@ -902,14 +901,24 @@ class CppHeaderParser(object):
else:
decls.append(decl)
if self._generate_gpumat_decls and "cv.cuda." in decl[0]:
# If function takes as one of arguments Mat or vector<Mat> - we want to create the
# same declaration working with GpuMat (this is important for T-Api access)
args = decl[3]
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
if has_mat:
_, _, _, gpumat_decl = self.parse_stmt(stmt, token, mat="cuda::GpuMat", docstring=docstring)
decls.append(gpumat_decl)
if self._generate_umat_decls:
# If function takes as one of arguments Mat or vector<Mat> - we want to create the
# same declaration working with UMat (this is important for T-Api access)
args = decl[3]
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
if has_mat:
_, _, _, umat_decl = self.parse_stmt(stmt, token, use_umat=True, docstring=docstring)
_, _, _, umat_decl = self.parse_stmt(stmt, token, mat="UMat", docstring=docstring)
decls.append(umat_decl)
docstring = ""
if stmt_type == "namespace":
chunks = [block[1] for block in self.block_stack if block[0] == 'namespace'] + [name]
@@ -952,7 +961,7 @@ class CppHeaderParser(object):
print()
if __name__ == '__main__':
parser = CppHeaderParser(generate_umat_decls=True)
parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
decls = []
for hname in opencv_hdr_list:
decls += parser.parse(hname)
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python
'''
CUDA-accelerated Computer Vision functions
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
from tests_common import NewOpenCVTests
class cuda_test(NewOpenCVTests):
def setUp(self):
if not cv.cuda.getCudaEnabledDeviceCount():
self.skipTest("No CUDA-capable device is detected")
def test_cuda_upload_download(self):
npMat = (np.random.random((200, 200, 3)) * 255).astype(np.uint8)
gpuMat = cv.cuda_GpuMat()
gpuMat.upload(npMat)
self.assertTrue(np.allclose(gpuMat.download(), npMat))
def test_cuda_imgproc_cvtColor(self):
npMat = (np.random.random((200, 200, 3)) * 255).astype(np.uint8)
gpuMat = cv.cuda_GpuMat()
gpuMat.upload(npMat)
gpuMat2 = cv.cuda.cvtColor(gpuMat, cv.COLOR_BGR2HSV)
self.assertTrue(np.allclose(gpuMat2.download(), cv.cvtColor(npMat, cv.COLOR_BGR2HSV)))
def test_cuda_filter_laplacian(self):
npMat = (np.random.random((200, 200)) * 255).astype(np.uint16)
gpuMat = cv.cuda_GpuMat()
gpuMat.upload(npMat)
gpuMat = cv.cuda.createLaplacianFilter(cv.CV_16UC1, -1, ksize=3).apply(gpuMat)
self.assertTrue(np.allclose(gpuMat.download(), cv.Laplacian(npMat, cv.CV_16UC1, ksize=3)))
if __name__ == '__main__':
NewOpenCVTests.bootstrap()