A few tricks I learned using ctypes to wrap OpenCV.
Easy function prototypes
ctypes allows declaration of foreign function arguments and results using a very power prototype capability. Unfortunately the parameters have to be specified in two separate lists. This little helper function cleans that up into one list:
# make function prototypes a bit easier to declare
def cfunc(name, dll, result, *args):
'''build and apply a ctypes prototype complete with parameter flags'''
atypes = []
aflags = []
for arg in args:
atypes.append(arg[1])
aflags.append((arg[2], arg[0]) + arg[3:])
return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags))
Arguments: name is a string specifying the function name in the dll, dll is the dll object, result is the type of the result, *args is a list of tuples with 3 or 4 elements each like (argname, argtype, in/out, default) where argname is the name of the argument, argtype is the type, in/out is 1 for input and 2 for output, and default is an optional default value.
For example:
cvMinMaxLoc = cfunc('cvMinMaxLoc', _cxDLL, None,
('image', POINTER(IplImage), 1),
('min_val', POINTER(double), 2),
('max_val', POINTER(double), 2),
('min_loc', POINTER(CvPoint), 2),
('max_loc', POINTER(CvPoint), 2),
('mask', POINTER(IplImage), 1, None))
means locate cvMinMaxLoc in dll _cxDLL, it returns nothing. The first argument is an input image. The next 4 arguments are output, and the last argument is input with an optional value. A typical call might look like:
min_val,max_val,min_loc,max_loc = cvMinMaxLoc(img)
Array arguments from lists
The from_param feature of ctypes is a very powerful way to insert code into the process of preparing arguments to pass to foreign functions. For example, using this class:
class ListPOINTER(object):
'''Just like a POINTER but accept a list of ctype as an argument'''
def __init__(self, etype):
self.etype = etype
def from_param(self, param):
if isinstance(param, (list,tuple)):
return (self.etype * len(param))(*param)
Suppose I have a foreign function foo that expects an array of ints. I could say:
ary = (c_int * 3)() ary[0] = 1 ary[1] = 2 ary[2] = 3 foo(ary)
I can replace an argument of type POINTER(c_int) with LIstPOINTER(c_int). Now I can call foo like:
foo([1,2,3])
Arrays of pointers to arrays from nested lists
The idea above can easily be extended to replace an int** with a nested list.
class ListPOINTER2(object):
'''Just like POINTER(POINTER(ctype)) but accept a list of lists of ctype'''
def __init__(self, etype):
self.etype = etype
def from_param(self, param):
if isinstance(param, (list,tuple)):
val = (POINTER(self.etype) * len(param))()
for i,v in enumerate(param):
if isinstance(v, (list,tuple)):
val[i] = (self.etype * len(v))(*v)
else:
raise TypeError, 'nested list or tuple required at %d' % i
return val
else:
raise TypeError, 'list or tuple required'
Implicit byref arguments
class ByRefArg(object):
'''Just like a POINTER but accept an argument and pass it byref'''
def __init__(self, atype):
self.atype = atype
def from_param(self, param):
return byref(self.atype(param))
Structures that can print themselves and magically accept tuples as arguments
# hack the ctypes.Structure class to include printing the fields
class _Structure(Structure):
def __repr__(self):
'''Print the fields'''
res = []
for field in self._fields_:
res.append('%s=%s' % (field[0], repr(getattr(self, field[0]))))
return self.__class__.__name__ + '(' + ','.join(res) + ')'
@classmethod
def from_param(cls, obj):
'''Magically construct from a tuple'''
if isinstance(obj, cls):
return obj
if isinstance(obj, tuple):
return cls(*obj)
raise TypeError
3 comments
Like your ctype tricks. Been trying out the function prototype trick.
My particular function call as a (input, output, input) argument list. When i pass the 2 for the output type I get a TypeError: c_wchar_p ‘out’ parameter must be passed as default value.
I cannot find any reference as to what that means. Curious if you have ran into this?
Thanks
rw
My example above is like that, except mine has multiple outputs followed by an optional input. I don’t recall if I’ve called it with the optional input but I think it works. Email me a snippet of code and maybe I can spot something.
[...] that you would like to use, give ctypes a try. Gary Bishop wrote about a couple of interesting ctypes tricks to make the process [...]
Leave a Comment