I've received a void pointer from a foreign function via ctypes, containing an array of c_double arrays:
[[12.0, 13.0], [14.0, 15.0], …]
I'm accessing it via the restype parameter:
from ctypes import Structure, POINTER, c_void_p, c_size_t, c_double, c_uint32, c_char_p, cast, cdll
class _CoordResult(Structure):
""" Container for returned FFI coordinate data """
_fields_ = [("coords", _FFIArray)]
class _FFIArray(Structure):
"""
Convert sequence of float lists to a C-compatible void array
example: [[1.0, 2.0], [3.0, 4.0]]
"""
_fields_ = [("data", c_void_p),
("len", c_size_t)]
def _void_array_to_nested_list(res, _func, _args):
""" Dereference the FFI result to a list of coordinates """
shape = (res.coords.len, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = string_at(res.coords.data, mem_size)
array = [list(pair) for pair in ((POINTER(c_double * 2).from_buffer_copy(res.coords)[:res.coords.len]))]
drop_array(res.coords)
return array
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _CoordResult
decode_polyline.errcheck = _void_array_to_nested_list
However, this gives me back nonsense values, because the pointer dereference in _void_array_to_nested_list is wrong.
The solution doesn't have to use NumPy, but that seems like the best approach.
I can't test this right now, but this is what I would try:
import numpy as np
result = ...
shape = (10, 2)
array_size = np.prod(shape)
mem_size = 8 * array_size
array_str = ctypes.string_at(result, mem_size)
array = np.frombuffer(array_str, float, array_size).reshape(shape)
array will be read only, copy it if you need a writable array.
Here is a solution that uses ctypes.cast or numpy.ctypeslib.as_array, and no ctypes.string_at just in case if it makes an extra copy of memory region.
class _FFIArray(Structure):
_fields_ = [("data", c_void_p), ("len", c_size_t)]
class Coordinate(Structure):
_fields_ = [("latitude", c_double), ("longitude", c_double)]
class Coordinates(Structure):
_fields_ = [("data", POINTER(Coordinate)), ("len", c_size_t)]
decode_polyline = lib.decode_polyline_ffi
decode_polyline.argtypes = (c_char_p, c_uint32)
decode_polyline.restype = _FFIArray
# assuming that the second argument is the length of polyline,
# although it should not be needed for `\0` terminated string
res = decode_polyline(polyline, len(polyline))
nres = Coordinates(cast(res.data, POINTER(Coordinate)), res.len)
for i in range(nres.len):
print(nres.data[i].latitude, nres.data[i].longitude)
# if just a numpy (np) array is needed
xs = np.ctypeslib.as_array((c_double * res.len * 2).from_address(res.data))
# "New view of array with the same data."
xs = xs.view(dtype=[('a', np.float64), ('b', np.float64)], type=np.ndarray)
xs.shape = res.len
Related
I don't know much about what is actually going on with _fields_ and __init__, so I'm not sure if it's possible or not, but I'm trying to do the something like the following:
class Matrix(Structure):
def __init__(self, dtype, nrow, ncol, data = []):
self._fields_ = [("nrow", c_int), ("ncol", c_int)]
self.dtype = dtype
self.nrow = nrow
self.ncol = ncol
if dtype == float:
self._fields_.append(("data", POINTER(c_double)))
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
The goal is to call functions from a shared library I wrote in C that has the structure:
typedef struct DoubleMatrix DoubleMatrix;
struct DoubleMatrix {
int nrow;
int ncol;
double *data;
};
void func1(DoubleMatrix *mat) {
// stuff
}
but it's not working. When I do:
m = DoubleMatrix(float, 2, 2, [1, 2, 3, 4])
I get
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-28-4e74b99b208f> in <module>
----> 1 m = DoubleMatrix(float, 2, 2, [1, 2, 3, 4])
TypeError: __init__() takes from 3 to 4 positional arguments but 5 were given
The reason I want to do this is because I will have matrices whose entries are of type other than double
Edit
I want to share what has worked but does not have my desired functionality:
from ctypes import *
lib = CDLL("/usr/local/lib/ccalc/libccalc.so")
class DoubleMatrix(Structure):
_fields_ = [("nrow", c_int),
("ncol", c_int),
("data", POINTER(c_double))]
def __init__(self, nrow, ncol, data=[]):
self.nrow = nrow
self.ncol = ncol
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
return
Second Edit
I am sorry everyone, I think I didn't restart kernel of my jupyter notebook properly when probing this. Now the case is that python crashes with the following code:
class DoubleMatrix(Structure):
_fields_ = [("nrow", c_int), ("ncol", c_int)]
def __init__(self, dtype, nrow, ncol, data=[]):
self.dtype = dtype
self.nrow = nrow
self.ncol = ncol
if dtype == float:
self._fields_.append(("data", POINTER(c_double)))
arr_type = c_double * (nrow * ncol)
arr_inst = arr_type(*data)
self.data = arr_inst
return
Maybe This question should be closed/deleted so I can ask it properly? The heart of my question is still the same, how do I take my working code in the first edit, and add the functionality I mentioned. Again I am very sorry.
The win32 function NetShareDel takes three arguments, LPCWSTR LPCWSTR and DWORD.
So I use the following list for argtypes:
import ctypes as C
C.windll.Netapi32.NetShareDel.argtypes = [LPCWSTR, LPCWSTR, c_int]
C.windll.Netapi32.NetShareDel.restype = c_int
C.windll.Netapi32.NetShareDel(server, shareName, 0)
That works fine, but I can't figure out what to use for NetShareAdd, especialle the byte array for NET_SHARE_INFO struct and the last byref(c_int) argument.
Here's the code:
def Share(server, shareName, dir):
info = SHARE_INFO_2()
STYPE_DISKTREE = 0
info.shi2_netname = shareName
info.shi2_path = dir
info.shi2_type = STYPE_DISKTREE
info.shi2_remark = "Shared: " + time.strftime("%Y%m%d-%H:%M")
info.shi2_max_uses = -1
info.shi2_passwd = ""
info.shi2_current_uses = 0
info.shi2_permissions = 0xFFFFFFFF
i = c_int()
bytearray = buffer(info)[:]
windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, ????, ????]
windll.Netapi32.NetShareAdd(server, 2, bytearray, C.byref(i))
What would be the correct argtypes list for NetShareAdd?
Got it working finally
First the line
bytearray = buffer(info)[:]
was changed into byte pointer type
byteptr = C.POINTER(C.wintypes.BYTE)(info)
and then the argtypes and call will become POINTER(BYTE) too of course:
C.windll.Netapi32.NetShareAdd.argtypes = [LPCWSTR, c_int, C.POINTER(C.wintypes.BYTE), C.POINTER(c_int)]
C.windll.Netapi32.NetShareAdd.restype = c_int
windll.Netapi32.NetShareAdd(server, 2, byteptr, C.byref(i))
I am trying to capture the screen using only the ctypes modules. Unfortunately I cannot retrieve raw pixel from CGDataProviderCopyData. I need to get an access to raw data:
#!/usr/bin/env python
# coding: utf-8
from sys import maxsize
from ctypes import POINTER, Structure, c_double, byref, c_void_p, c_int32, c_uint32, c_float, cdll
from ctypes.util import find_library
CGFloat = c_double if maxsize > 2 ** 32 else c_float
class CGPoint(Structure):
_fields_ = [('x', CGFloat), ('y', CGFloat)]
class CGSize(Structure):
_fields_ = [('width', CGFloat), ('height', CGFloat)]
class CGRect(Structure):
_fields_ = [('origin', CGPoint), ('size', CGSize)]
# Library
cgs = cdll.LoadLibrary(find_library('CoreGraphics'))
# Argtypes
cgs.CGGetActiveDisplayList.argtypes = [c_uint32, POINTER(c_uint32), POINTER(c_uint32)]
cgs.CGDisplayBounds.argtypes = [c_uint32]
cgs.CGRectStandardize.argtypes = [CGRect]
cgs.CGDisplayRotation.argtypes = [c_uint32]
cgs.CGWindowListCreateImage.argtypes = [CGRect, c_uint32, c_uint32, c_uint32]
cgs.CGImageGetWidth.argtypes = [c_void_p]
cgs.CGImageGetHeight.argtypes = [c_void_p]
cgs.CGImageGetDataProvider.argtypes = [c_void_p]
cgs.CGDataProviderCopyData.argtypes = [c_void_p]
cgs.CGDataProviderRelease.argtypes = [c_void_p]
# Restypes
cgs.CGGetActiveDisplayList.restype = c_int32
cgs.CGDisplayBounds.restype = CGRect
cgs.CGRectStandardize.restype = CGRect
cgs.CGDisplayRotation.restype = c_float
cgs.CGWindowListCreateImage.restype = c_void_p
cgs.CGImageGetWidth.restype = c_uint32
cgs.CGImageGetHeight.restype = c_uint32
cgs.CGImageGetDataProvider.restype = c_void_p
cgs.CGDataProviderCopyData.restype = c_void_p
cgs.CGDataProviderRelease.restype = c_void_p
# Monitors
max_displays = 32
display_count = c_uint32(0)
active_displays = (c_uint32 * max_displays)()
cgs.CGGetActiveDisplayList(max_displays, active_displays, byref(display_count))
for idx in range(display_count.value):
display = active_displays[idx]
rect = cgs.CGDisplayBounds(display)
rect = cgs.CGRectStandardize(rect)
image_ref = cgs.CGWindowListCreateImage(rect, 1, 0, 0)
width = int(cgs.CGImageGetWidth(image_ref))
height = int(cgs.CGImageGetHeight(image_ref))
prov = cgs.CGImageGetDataProvider(image_ref)
data = cgs.CGDataProviderCopyData(prov)
# How to get raw pixels from data`?
MacOS X version 10.11.3.
Python versions 2.7.10 and 2.6.9.
I have no experience with the CoreGraphics API, but looking at the documentation it looks like CGDataProviderCopyData returns a CFDataRef object. The documentat for CFDataRef has a section on "Examining a CFData Object" which describes a CFDataGetLength function and a CFDataGetBytePtr function which return the length of the data and a UInt8*.
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGDataProvider/#//apple_ref/c/func/CGDataProviderCopyData
https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFDataRef/index.html
Thanks to Snorfalorpagus, I finally succeed:
#...
data = cgs.CGDataProviderCopyData(prov)
data_ref = cgs.CFDataGetBytePtr(data)
buf_len = cgs.CFDataGetLength()
image_data = cast(data_ref, POINTER(c_ubyte * buf_len))
cgs.CGDataProviderRelease(prov)
# Raw pixels are in image_data.contents
I am trying to make a pointer of a struct and then de-reference it. But it's crashing. I have mimic'ed the behvior here with this simple code.
from ctypes import *
import ctypes
class File(Structure):
_fields_ = [("fileSize", c_uint),
("fileName", c_byte * 32)]
f = File()
f.fileSize = 2
print(f.fileSize)
P = ctypes.POINTER(File)
p = P.from_address(addressof(f))
print(p.contents.fileSize)
Can someone point out what's the issue with this code?
Thanks in advance.
this works (I just tried):
p = pointer(f)
no need to instantiate P at all.
To be clearer, given the p and P look quite similar on screen:
from ctypes import *
class File(Structure):
_fields_ = [("fileSize", c_uint),
("fileName", c_byte * 32)]
f = File()
f.fileSize = 2
print(f.fileSize)
p = pointer(f)
print(p.contents.fileSize)
I am using the following code to read an svg:
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path, byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
(from Error with Python ctypes and librsvg)
I call img = Handle(path) and this leaks memory. I am pretty sure this is due to incorrectly using ctypes & pointers, but I cannot find a way to fix this.
According to the documentation of rsvg_handle_new, free the handle with g_object_unref. Also, if a failed call allocates a GError, after you get the code and message you have to free the error with g_error_free.
from ctypes import *
from ctypes.util import find_library
_gobj = CDLL(find_library("gobject-2.0"))
_glib = CDLL(find_library("glib-2.0"))
class _GError(Structure):
_fields_ = [("domain", c_uint32),
("code", c_int),
("message", c_char_p)]
_GErrorP = POINTER(_GError)
_glib.g_error_free.restype = None
_glib.g_error_free.argtypes = [_GErrorP]
_gobj.g_object_unref.restype = None
_gobj.g_object_unref.argtypes = [c_void_p]
You can free the handle in the __del__ method of the Handle class:
class Handle(object):
_gobj = _gobj # keep a valid ref for module teardown
# ...
def __del__(self):
if self.handle:
self._gobj.g_object_unref(self.handle)
self.handle = None