6

I am using python ctypes and libc to interface with a vendor-provided DLL file. The purpose of the DLL file is to acquire an image from a camera.

The image acquisition appears to run without error; the issue I am having is accessing the data.

The image acquisition function takes a ctypes.c_void_p as an argument for the image data.

simplified as follows:

"""
typedef struct AvailableData
{
    void* initial_readout;
    int64 readout_count;
} imageData;
"""

class AvailableData(ctypes.Structure):
    _fields_ = [("initial_readout", ctypes.c_void_p), 
                ("readout_count", ctypes.c_longlong)]

"""
Prototype
Acquire(
CamHandle                 camera,
int64                       readout_count,
int                       readout_time_out,
AvailableData*         available,
AcquisitionErrorsMask* errors );
"""

>>> imageData = AvailableData()
>>> Acquire.argtypes = CamHandle, ctypes.c_longlong, ctypes.c_int, 
         ctypes.POINTER(AvailableData), ctypes.POINTER(AcquisitionErrorsMask)
>>> Acquire.restype = ctypes.c_void_p

>>> status = Acquire(camera, readout_count, readout_time_out, imageData, errors)

I do not fully understand exactly what the function is doing, because after I run the function, imageData.initial_readout appears to be a type 'long' (not even a ctypes.c_long: just 'long'). However, it also has a value associated with it. I'm assuming this is a starting address of where the data is stored.

>>> type(imageData.initial_readout)
<type 'long'>
>>> imageData.initial_readout
81002560L

My current approach for accessing the data is to use libc.fopen, libc.fwrite, libc.fclose as follows:

>>> libc = ctypes.cdll.msvcrt

>>> fopen = libc.fopen
>>> fwrite = libc.fwrite
>>> fclose = libc.fclose
>>> fopen.argtypes = ctypes.c_char_p, ctypes.c_char_p
>>> fopen.restype = ctypes.c_void_p

>>> fopen.restype = ctypes.c_void_p
>>> fwrite.argtypes = ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, ctypes.c_void_p
>>> fwrite.restype = ctypes.c_size_t

>>> fclose = libc.fclose
>>> fclose.argtypes = ctypes.c_void_p,
>>> fclose.restype = ctypes.c_int

>>> fp = fopen('output3.raw', 'wb')
>>> print 'fwrite returns: ',fwrite(ctypes.c_void_p(imageData.initial_readout), readoutstride.value, 1, fp)
fwrite returns:  0
>>> fclose(fp)

where readoutstride = 2097152 corresponding to a 1024x1024 array of 16 bit pixels.

The file "output3.raw" shows up in windows explorer, however, it has 0 kbytes and when I try to open it with (e.g. with an imag viewer) it says the file is empty.

I see that fwrite returns a value of 0 (but should return a value of 1)

If you have any ideas as to what I'm doing wrong here, I appreciate it immensely. Thank you in advance.

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
Joe
  • 355
  • 1
  • 5
  • 17
  • @eryksun I've included a prototype for acquire in my edits this morning. Also, strictly speaking imageData is a structure, which then contains a void pointer (I left out these details to simplify the initial question). However, since you've asked I've modified my code above to include the full structure. Hopefully this clarifies things. – Joe Jun 23 '13 at 15:17
  • @eryksun `readout_count` is the number of frames being collected. When I call the `Acquire` function, its 2nd argument is `readout_count`, which I specify to be 1. After running `Acquire` this value gets assigned to `imageData.readout_count`. `readoutstride` specifies the number of bytes in a stride (frame). There is an additional function that I can call (before Acquiring) that returns the `readoutsride`. The value it returns is `2097152`, which corresponds to 2 bytes per pixel at 1024x1024 pixels. – Joe Jun 23 '13 at 16:34
  • @eryksun it seems to me that there is indeed a byte string stored in memory (at the address given by available.initial_readout). Is there a way in Python to access the next`2097152` bytes beginning at a given memory address, e.g. `81002560L` or in hex `0x4d40040L`? – Joe Jun 23 '13 at 16:36
  • If the call was successful, there are lots of way to create the string. One way is to use `ctypes.string_at(address, size)` Afterward, call the library's function that frees the memory. – Eryk Sun Jun 23 '13 at 17:51

1 Answers1

6

Specify argtypes, restype of the functions.

import ctypes

libc = ctypes.windll.msvcrt

fopen = libc.fopen
fopen.argtypes = ctypes.c_char_p, ctypes.c_char_p,
fopen.restype = ctypes.c_void_p

fwrite = libc.fwrite
fwrite.argtypes = ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, ctypes.c_void_p
fwrite.restype = ctypes.c_size_t

fclose = libc.fclose
fclose.argtypes = ctypes.c_void_p,
fclose.restype = ctypes.c_int

fp = fopen('output3.raw', 'wb')
fwrite('Hello', 5, 1, fp)
fclose(fp)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
falsetru
  • 357,413
  • 63
  • 732
  • 636
  • I modified my code to include specification of `argstypes` and `restype`. I still have the same issue-- feel free to check out my updates to the question above. Thanks for looking into this. – Joe Jun 23 '13 at 15:09
  • @J.Lowney, You have to specify argtypes, restype not only for fopen, fwrite, fclose, but also acquire, .... – falsetru Jun 23 '13 at 15:15
  • @J.Lowney, Change `ctypes.c_void_p(available.initial_readout)` to `ctypes.addressof(available.initial_readout)`. – falsetru Jun 23 '13 at 15:16
  • When I modify to `ctypes.addressof(imageData.initial_readout)` I get an error:`TypeError: invalid type`. The confusing part is, before I passed the structure to the function, `available.initial_readout` was defined as `ctypes.c_void_p`. When I get it back from the function it is a type `long` -- so I think python is converting it back to a python variable. As a result of this I think I've lost the `ctypes.addressof` information. – Joe Jun 23 '13 at 15:40
  • Are `argstype`, `argtypes` and `argstypes` somehow aliases? I seem to find all on the internet (mostly `argtypes` though). – Shahbaz Mar 23 '15 at 16:59
  • @Shahbaz, According to [`ctypes` documentation](https://docs.python.org/2/library/ctypes.html#ctypes._FuncPtr.argtypes), only `argtypes` is the right one. – falsetru Mar 24 '15 at 00:10