I am trying to use python to capture an image from a camera.
I have been looking at the capture example code, which is in c, on linuxtv.org, and I've got to a bit of an impasse.
I'm using the USERPTR IO method, because mmap seems too complicated and I can't use the read method.
In the original c there is a struct like so:
struct buffer {
void *start;
size_t length;
};
Which I have pythonised into:
class buffer_location():
def __init__(self):
self.start = v4l2.c_int(0)
self.length = 0
Where v4l2 is a python module that wraps up linux/videodev2.h
In order to initialise the buffers for the video capture I have done this:
buflocs = []
buffers = []
buffer_size = ...#size is taken from another ioctl call
for i in range(4):
bl = buffer_location()
buflocs.append(bl)
bl.length = buffer_size
buff = create_string_buffer(buffer_size)
buffers.append(buff)
bl.start = pointer(buff)
This is an attempt to replicate what happens in init_userp in the original c. Then I tried to replicate start_capturing like so:
for i in range(4):
v4l2buf = v4l2.v4l2_buffer()
v4l2buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
v4l2buf.memory = v4l2.V4L2_MEMORY_USERPTR
v4l2buf.index = i
v4l2buf.m.usrptr = buflocs[i].start
v4l2buf.length = buffer_size
if -1 == ioctl(cam, v4l2.VIDIOC_QBUF, v4l2buf):
print('cannot qbuf')
However this gives the error
OSError: [Errno 14] Bad address
I assume this is coming from v4l2buf.m.usrptr.
How can I correctly assign the address to the start of the buffer?
Related
In Python, I am trying to use the J1939 filtering as mentionned in the linux kernel docs: https://www.kernel.org/doc/html/latest/networking/j1939.html
The following code fails at the setsockopt() line (setting up filters):
import socket
import struct
def pack_J1939_filters(can_filters):
can_filter_fmt = "=" + "2Q2B2I" * len(can_filters)
filter_data = []
for can_filter in can_filters:
name = can_filter['name']
name_mask = can_filter['name_mask']
addr = can_filter['addr']
addr_mask = can_filter['addr_mask']
pgn = can_filter['pgn']
pgn_mask = can_filter['pgn_mask']
filter_data.append(name)
filter_data.append(name_mask)
filter_data.append(addr)
filter_data.append(addr_mask)
filter_data.append(pgn)
filter_data.append(pgn_mask)
return struct.pack(can_filter_fmt, *filter_data)
s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939)
interface = "vcan0"
src_name = socket.J1939_NO_NAME
src_pgn = socket.J1939_NO_PGN
src_addr = 0x81
src_sck_addr = (interface, src_name, src_pgn, src_addr)
s.bind(src_sck_addr)
filters = [{"name": 0, "name_mask":0, "addr":0, "addr_mask":0, "pgn": 0, "pgn_mask": 0}]
packed_filters = pack_J1939_filters(filters)
# socket.SOL_CAN_J1939 does not seem to exist
SOL_CAN_BASE = 100
CAN_J1939 = 7
SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939
s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters)
s.recvfrom(128)
s.close()
First, the kernel documentation mentions to use SOL_CAN_J1939 as the first argument. However socket.SOL_CAN_J1939 does not exist in the socket package. So looking at the code at this location I was able to understand that this int value should be 107: http://socket-can.996257.n3.nabble.com/RFC-v3-0-6-CAN-add-SAE-J1939-protocol-td7571.html
As for the setsockopt() third argument, I packed the filters to match the j1939_filter structure (26 bytes as described in the code from the previous link). This is similar to what is done in can.interfaces.socketcan.utils for raw CAN.
What am I doing wrong to cause setsockopt() to fail?
The first issue was with the struct.pack format (can_filter_fmt) being wrong. I first assumed that the kernel j1939_filter structure size was the sum of the members. This is wrong since the compiler adds padding. This can be added to the struct.pack format as x such as 2Q2I2B6x. Please see Why isn't sizeof for a struct equal to the sum of sizeof of each member?
The second issue was that can_filter_fmt is not packed as 2Q2B2I but as 2Q2I2B6x (the addr member is in the middle).
As for SOL_CAN_J1939 I was correct and needs to be created in file because it is not yet in the package.
The final code is the following:
#!/usr/bin/env python3
import socket
import struct
def pack_J1939_filters(can_filters=None):
if can_filters is None:
# Pass all messages
can_filters = [{}]
can_filter_fmt = "=" + "2Q2I2B6x" * len(can_filters)
filter_data = []
for can_filter in can_filters:
if 'name' in can_filter:
name = can_filter['name']
else:
name = 0
if 'name_mask' in can_filter:
name_mask = can_filter['name_mask']
else:
name_mask = 0
if 'pgn' in can_filter:
pgn = can_filter['pgn']
else:
pgn = 0
if 'pgn_mask' in can_filter:
pgn_mask = can_filter['pgn_mask']
else:
pgn_mask = 0
if 'addr' in can_filter:
addr = can_filter['addr']
else:
addr = 0
if 'addr_mask' in can_filter:
addr_mask = can_filter['addr_mask']
else:
addr_mask = 0
filter_data.append(name)
filter_data.append(name_mask)
filter_data.append(pgn)
filter_data.append(pgn_mask)
filter_data.append(addr)
filter_data.append(addr_mask)
return struct.pack(can_filter_fmt, *filter_data)
def print_msg(data, sck_addr):
print(f"SA:{hex(sck_addr[3])} PGN:{hex(sck_addr[2])}")
for j in range(len(data)):
if j % 8 == 0 and j != 0:
print()
if j % 8 == 0:
print(f"bytes {j} to {j+7}: ", end="")
print(f"{hex(data[j])} ", end="")
print()
print()
def main():
s = socket.socket(socket.PF_CAN, socket.SOCK_DGRAM, socket.CAN_J1939)
# allows to receive broadcast messages
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
interface = "vcan0"
src_name = socket.J1939_NO_NAME
src_pgn = socket.J1939_NO_PGN # always no PGN for source, unless filtering is needed
src_addr = 0x81 # recvfrom() will not return destination specific messages for other addresses
src_sck_addr = (interface, src_name, src_pgn, src_addr)
s.bind(src_sck_addr)
packed_filters = pack_J1939_filters()
SOL_CAN_BASE = 100
CAN_J1939 = 7
SOL_CAN_J1939 = SOL_CAN_BASE + CAN_J1939
s.setsockopt(SOL_CAN_J1939, socket.SO_J1939_FILTER , packed_filters)
(recv_data, recv_sck_addr) = s.recvfrom(128)
print_msg(recv_data, recv_sck_addr)
s.close()
if __name__ == "__main__":
main()
Thank you.
For J1939 to work with SocketCAN you need two things:
kernel 5.4+
can-j1939 kernel module enabled
Testing for can-1939:
If you install can-utils and after sudo modprobe can-j1939 all you get is fatal error, or if you start testj1939 from can-utils and you get error that protocol is not supported, then it means that can-j1939 was not enabled in your kernel and you need to compile it manually.
Here are my instructions for enabling can-j1939 in Debian 10 kernel:
https://github.com/linux-can/can-utils/blob/master/can-j1939-install-kernel-module.md
I have been following along with a tutorial online using OpenCL, where I am doing everything using python and pyOpenCL. As a stripped down example of my problem, I need to pass an array of C structs as an argument to an OpenCL kernel.
Here is an artificial example of OpenCL code:
typedef struct Test{
float a;
float3 b;
} Test;
__kernel void render_kernel(__constant Test *tests, const int width, const int height, const int num_structs, __global float3* output)
{
unsigned int work_item_id = get_global_id(0);
unsigned int x_coord = work_item_id % width;
unsigned int y_coord = work_item_id / width;
Test test = tests[0];
output[work_item_id] = test.b;
}
This will do something silly which is just give one of the float3 arrays as the output, but I just need to know I am actually getting the data through to the kernel properly.
I am trying to mimic an array of these structures on the python side with the following code:
class Test(ctypes.Structure):
_fields_ = [
("a", ctypes.c_float),
("b", (ctypes.c_float * 4))
]
class Test_Array(ctypes.Structure):
_fields_ = [("TEST_ARRAY", ctypes.POINTER(Test))]
def __init__(self, num_structs):
elems = (Test * num_structs)()
self.TEST_ARRAY = ctypes.cast(elems, ctypes.POINTER(Test))
self.elements = num_structs
for num in range(0, num_structs):
self.TEST_ARRAY[num].a = 1.0
self.TEST_ARRAY[num].b = (1.0, 0.0, 0.0, 1.0)
num_structs = 2
test_arr = Test_Array(num_structs)
#host buffer
color_out = np.empty((win.width * win.height, 4), dtype=np.float32)
cl_prog = CL()
cl_prog.load_program("shaders/struct.cl")
#device buffers
cl_structs = cl_prog.create_input_buffer(num_structs * ctypes.sizeof(Test))
cl_output = cl_prog.create_output_buffer(color_out.nbytes)
cl.enqueue_fill_buffer(cl_prog.queue, cl_structs, test_arr.TEST_ARRAY,
0, num_structs * ctypes.sizeof(Test))
global_work_size = (win.width * win.height,)
cl_prog.program.render_kernel(cl_prog.queue, global_work_size, None,
cl_structs, np.int32(win.width), np.int32(win.height),
np.int32(num_structs), cl_output)
cl_prog.retrieve_data(color_out, cl_output)
print(color_out)
This isn't really relevant as the functions in this class are just wrappers around pyOpenCL functions, but here is the CL class which is instantiated.
class CL:
def __init__(self):
self.platform = cl.get_platforms()[0]
self.device = self.platform.get_devices()[0]
self.ctx = cl.Context([self.device])
self.queue = cl.CommandQueue(self.ctx)
def load_program(self, file_path):
with open(file_path) as f:
src = f.read()
self.program = cl.Program(self.ctx, src).build()
def create_output_buffer(self, size):
"""
creates and returns a write only cl.Buffer of size bytes.
"""
mf = cl.mem_flags
return cl.Buffer(self.ctx, mf.WRITE_ONLY, size)
def create_input_buffer(self, size):
"""
returns a read only cl.Buffer of size bytes.
"""
mf = cl.mem_flags
return cl.Buffer(self.ctx, mf.READ_ONLY, size)
def retrieve_data(self, host_buffer, device_buffer):
"""
retrieves data from a buffer on the device, device_buffer, and copies it
over to host_buffer
"""
cl.enqueue_copy(self.queue, host_buffer, device_buffer)
def fill_buffer(self, memory, pattern, offset, size, wait_for=None):
"""
A wrapper around cl.enqueue_fill_buffer which uses self.queue
"""
cl.enqueue_fill_buffer(self.queue, memory, pattern, offset, size, wait_for)
def enqueue_copy(self, device_buffer, host_buffer):
cl.enqueue_copy(self.queue, device_buffer, host_buffer)
When I run the above code, it compiles and runs fine, but the information I get back from the buffer is just garbage that was already in memory. I can't tell if my problem is with alignment of the data, the way I am creating the array of ctypes structs, or what?
I am not attached to using a C array of C structs. I suspect there is a way to do this with numpy arrays, but I can't figure it out. Any way to properly get the data from the host to the device would be greatly appreciated.
Some suggest back in 2014, this could perhaps be done like so:
__kernel void render_kernel(struct Params Test, ...){
}
You can see this post.
Otherwise, something called Attributes of Variables may be an option?
Hope you got this sorted out and share the experience. I would love to see how this is done as I may want to try pass SQL query over to kernel to process.
The setup is as such: An Arduino (Mega2560) receiving GPS and telemetry data, which is connected to a Raspberry Pi via I2C (Pi = Master). I got some code from Github which accurately transmits and unpacks floating point with no issues. However, I am not sending a homogeneous byte stream. The C struct I created is: (Float, Float, Int, Int, Int, Char, Char). Here is a bit of my code:
C:
// callback for sending data
void sendData(){
struct transferData data;
data.latitude = GPS.latitude;
data.longitude = GPS.longitude;
data.hour = GPS.hour;
data.minute = GPS.minute;
data.second = GPS.seconds;
data.lat = GPS.lat;
data.lon = GPS.lon;
uint8_t *ptr = (uint8_t *)&data;
Wire.write(ptr,sizeof(data));
Python:
data=get_data()
print"Latitude",(get_float(data,0)),(get_char(data,5))
Where the functions are:
def get_data():
return bus.read_i2c_block_data(address,0);
def get_float(data,index):
bytes = data[4*index:(index+1)*4]
return struct.unpack('f', "".join(map(chr, bytes)))[0]
def get_char(data,index):
bytes = data[(index)*4]
return struct.unpack('c', "".join(map(chr, bytes)))[0]
My python code won't run through get_char() function.
I also tried:
buff = bus.read_i2c_block_data(address, 0)
buff = struct.unpack('f f i i i c c', buff)
print buff
But that didn't work either. Does anyone have some guidance for me?
I am using a DLL library to call functions to operate a camera in python, and i'm able to retrieve the image using ctypes but it's formatted incorrectly. The returned image is duplicated and half of it is blank. what do i need to do to fix this?
I have a labview program that correctly takes images from the camera, so that is how they are supposed to look like.
Correct image retrieved using Labview
Image retrieved using Python:
the image is duplicated and also sideways in python.
python code:
from ctypes import *
import numpy as np
import matplotlib.pyplot as plt
mydll = windll.LoadLibrary('StTrgApi.dll')
hCamera = mydll.StTrg_Open()
print(hCamera)
im_height = 1200
im_width = 1600
dwBufferSize = im_height * im_width
pbyteraw = np.zeros((im_height, im_width), dtype=np.uint16)
dwNumberOfByteTrans = 0
dwNumberOfByteTrans = (c_ubyte * dwNumberOfByteTrans)()
dwFrameNo = 0
dwFrameNo = (c_ubyte * dwFrameNo)()
dwMilliseconds = 3000
mydll.StTrg_TakeRawSnapShot(hCamera,
pbyteraw.ctypes.data_as(POINTER(c_int16)), dwBufferSize*2,
dwNumberOfByteTrans, dwFrameNo, dwMilliseconds)
print(pbyteraw)
plt.matshow(pbyteraw)
plt.show()
C++ code for taking the image:
DWORD dwBufferSize = 0;
if(!StTrg_GetRawDataSize(hCamera, &dwBufferSize))
{
_tprintf(TEXT("Get Raw Data Size Failed.\n"));
return(-1);
}
PBYTE pbyteRaw = new BYTE[dwBufferSize];
if(NULL != pbyteRaw)
{
DWORD dwNumberOfByteTrans = 0;
DWORD dwFrameNo = 0;
DWORD dwMilliseconds = 3000;
for(DWORD dwPos = 0; dwPos < 10; dwPos++)
{
if(StTrg_TakeRawSnapShot(hCamera, pbyteRaw, dwBufferSize,
&dwNumberOfByteTrans, &dwFrameNo, dwMilliseconds))
{
TCHAR szFileName[MAX_PATH];
if(is2BytesMode)
{
_stprintf_s(szFileName, _countof(szFileName), TEXT("%s\\%u.tif"), szBitmapFilePath, dwFrameNo);
StTrg_SaveImage(dwWidth, dwHeight, STCAM_PIXEL_FORMAT_16_MONO_OR_RAW, pbyteRaw, szFileName, 0);
}
else
{
_stprintf_s(szFileName, _countof(szFileName), TEXT("%s\\%u.bmp"), szBitmapFilePath, dwFrameNo);
StTrg_SaveImage(dwWidth, dwHeight, STCAM_PIXEL_FORMAT_08_MONO_OR_RAW, pbyteRaw, szFileName, 0);
}
_tprintf(TEXT("Save Image:%s\n"), szFileName);
}
else
{
_tprintf(TEXT("Fail:StTrg_TakeRawSnapShot\n"));
break;
}
}
delete[] pbyteRaw;
}
Based on your C code, something like this should work, but it is untested since I don't have your camera library. If you are using 32-bit Python, make sure the library calls are __stdcall to use WinDLL, else use CDLL. 64-bit Python it doesn't matter. Defining the argument types and return type helps catch errors. For output parameters, create instances of the correct ctype, then pass byref(). The way you were currently doing the output parameters was likely the cause of your crash. Setting argtypes would have detected that the values weren't pointers to DWORDs.
from ctypes import *
from ctypes import wintypes as w
mydll = WinDLL('StTrgApi')
mydll.StTrg_Open.argtypes = None
mydll.StTrg_Open.restype = w.HANDLE
mydll.StTrg_GetRawDataSize.argtypes = w.HANDLE,w.PDWORD
mydll.StTrg_GetRawDataSize.restype = None
mydll.StTrg_TakeRawSnapShot.argtypes = w.HANDLE,w.PBYTE,w.DWORD,w.PDWORD,w.PDWORD,w.DWORD
mydll.StTrg_TakeRawSnapShot.restype = None
hCamera = mydll.StTrg_Open()
print(hCamera)
dwBufferSize = w.DWORD()
mydll.StTrg_GetRawDataSize(hCamera,byref(dwBufferSize))
pbyteraw = (w.BYTE * dwbufferSize)()
dwNumberOfByteTrans = w.DWORD() # output parameters. Pass byref()
dwFrameNo = w.DWORD() # output parameters. Pass byref()
dwMilliseconds = 3000
mydll.StTrg_TakeRawSnapShot(hCamera,
pbyteraw,
dwbufferSize,
byref(dwNumberOfByteTrans),
byref(dwFrameNo),
dwMilliseconds)
I am trying to use the ctypes module to make calls to Windows' Common Item Dialog API. The code shown below is roughly based on the steps outlined in the MSDN documentation. Its only dependency is the comtypes.GUID module.
import ctypes
from ctypes import byref, POINTER, c_int, c_long
from ctypes.wintypes import HWND, HRESULT
from comtypes import GUID
CLSID_FileOpenDialog = '{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}'
IID_IFileDialog = '{42F85136-DB7E-439C-85F1-E4075D135FC8}'
#IID_IFileOpenDialog = '{D57C7288-D4AD-4768-BE02-9D969532D960}'
CLSCTX_SERVER = 5
COINIT_APARTMENTTHREADED = 2
FOS_PICKFOLDERS = 32
FOS_FORCEFILESYSTEM = 64
ole32 = ctypes.windll.ole32
CoCreateInstance = ole32.CoCreateInstance
CoInitializeEx = ole32.CoInitializeEx
CoInitializeEx(None, COINIT_APARTMENTTHREADED)
ptr = c_int()
error = CoCreateInstance(
byref(GUID(CLSID_FileOpenDialog)), None, CLSCTX_SERVER,
byref(GUID(IID_IFileDialog)), byref(ptr))
assert error == 0
ptr = ptr.value
c_long_p = ctypes.POINTER(ctypes.c_int)
print('Pointer to COM object: %s' % ptr)
vtable = ctypes.cast(ptr, c_long_p).contents.value
print('Pointer to vtable: %s' % vtable)
func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)
# Calculating function pointer offset: 3rd entry in vtable; 32-bit => 4 bytes
show_p = ctypes.cast(vtable + 3*4, c_long_p).contents.value
print('Pointer to show(): %s' % show_p)
show = func_proto(show_p)
show(0)
Everything works as intended until the first call to show(0):
WindowsError: exception: access violation reading 0xXXXXXXXX
(Output may vary.) For comparison, I have carried out the same steps in AutoHotkey_L, which has direct access to COM.
CLSID := "{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}"
IID := "{42F85136-DB7E-439C-85F1-E4075D135FC8}"
ptr := ComObjCreate(CLSID, IID)
vtable := NumGet(ptr + 0, 0, "Ptr")
show := NumGet(vtbl + 0, 3 * A_PtrSize, "Ptr")
MsgBox ptr: %ptr% vtable: %vtable% show: %A_PtrSize%
DllCall(show, "Ptr", ptr, "Ptr", 44)
The resulting macro pops up an Open File dialog, as expected. The vtable pointer offsets are the same in both cases, but only the Python version throws up an access violation.
Can anyone shed some light on this?
[I apologize for not adding more hyperlinks where appropriate, but as a new user I am limited to two at a time.]
Background:
I am putting together a lightweight module which provides a native save/open file dialog for use in Python scripts. So far I have been unable to find an implementation in pure Python. Those that exist rely on UI toolkits such as Tkinter or wxPython.
Here is the solution:
COM methods take an additional parameter: The 'this' pointer. It is implicit when you call the method from C++, in C (and in ctypes) you must supply it yourself.
Change the line
func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)
into
func_proto = ctypes.WINFUNCTYPE(HRESULT, c_long, HWND)
and this line
show(0)
into
show(ptr, 0)
and your code will work.