Python中的用户指针
*我正在尝试从使用v4l捕获的网络摄像头显示预览。
这里是代码大概长什么样:
from ctypes import *
from v4l2 import *
from Image import fromstring
from Tkinter import Tk, Label
from ImageTk import PhotoImage
from ctypes.util import find_library
libc = CDLL(find_library('c'))
posix_memalign = libc.posix_memalign
getpagesize = libc.getpagesize
device_name = '/dev/video0'
ps = preview_settings = {
'width': 320,
'height': 240,
'pixformat': 'RGB',
}
PIX_FMT = V4L2_PIX_FMT_RGB555
preview = Tk()
image = PhotoImage(ps['pixformat'], (ps['width'], ps['height']))
label = Label(preview, text='Preview', image=image, width=ps['width'], height=ps['height'])
label.pack()
capability = v4l2_capability()
size = v4l2_frmsizeenum()
format = v4l2_format()
request = v4l2_requestbuffers()
buffer = v4l2_buffer()
b_address = c_void_p()
frame_name_count = '0'
type = V4L2_BUF_TYPE_VIDEO_CAPTURE
device = open(device_name, 'rw')
ioctl(device, VIDIOC_QUERYCAP, addr(capability))
size.pixel_format = PIX_FMT
size.index = 0
format.type = type
format.fmt.pix.pixelformat = PIX_FMT
format.fmt.pix.width = size.discrete.width
format.fmt.pix.height = size.discrete.height
format.fmt.pix.field = V4L2_FIELD_NONE
format.fmt.pix.bytesperline = 0
format.fmt.pix.sizeimage = 0
request.type = type
request.memory = V4L2_MEMORY_USERPTR
request.count = 1
ioctl(device, VIDIOC_S_FMT, addr(format))
ioctl(device, VIDIOC_G_FMT, addr(format))
ioctl(device, VIDIOC_REQBUFS, addr(request))
posix_memalign(addressof(b_address), getpagesize(), format.fmt.pix.sizeimage)
buffer.type = request.type
buffer.memory = request.memory
buffer.index = 0
buffer.m.userptr = b_address.value
buffer.length = format.fmt.pix.sizeimage
while True:
ioctl(device, VIDIOC_QBUF, addr(buffer))
ioctl(device, VIDIOC_STREAMON, cast(type, c_void_p))
ioctl(device, VIDIOC_DQBUF, addr(buffer))
preview_data = string_at(buffer.m.userptr, buffer.length)
im = fromstring(ps['pixformat'], (ps['width'], ps['height']), preview_data)
image.paste(im)
preview.update()
然后我遇到了 ValueError: not enough image data
好吧,我导入了
c_lib = CDLL(find_library('c'))
posix_memalign = c_lib.posix_memalign
getpagesize = c_lib.getpagesize
然后在之后
ioctl(device, VIDIOC_S_FMT, addr(format))
ioctl(device, VIDIOC_G_FMT, addr(format))
还有一些其他的操作,我尝试获取内存
posix_memalign(addressof(b_address), getpagesize(), format.fmt.pix.sizeimage)
现在 b_address 不再是 None
b_address 看起来像 c_void_p(145014784)
然后我开始循环,QBUF,DQBUF,等等……
问题是,当我调用 pygame.image.frombuffer
pg_img = pygame.image.frombuffer(
buffer.m.userptr,
(format.fmt.pix.width, format.fmt.pix.height),
preview_settings['pixformat']
)
我得到的错误是 TypeError: expected a character buffer object
5 个回答
好的,我做的事情是从pygame转到Tkinter和PIL
现在在同样的分配之后,我把 buffer.m.userptr
传给Image的fromstring方法
首先,我当然有以下内容:
import Image
import Tkinter
tk = Tkinter.Tk()
preview = ImageTk.PhotoImage(ps['pixformat'], (ps['width'], ps['height']))
label = Tkinter.Label(tk, text='Preview', image=preview, width=ps['width'], height=ps['height'])
label.pack()
现在预览一下:
im = Image.fromstring(ps['pixformat'], (format.fmt.pix.width, format.fmt.pix.height), '\0'*buffer.m.userptr)
preview.paste(im)
tk.update()
我按照@sipickles说的做了,使用了'\0'来看看整个过程是否能正常工作 结果是可以的 :)
问题是如何正确传递这个userptr,并且里面的数据是否真的适合传给预览
我在这里真的很迷茫。有人知道v4l2吗?
你有没有试过直接把 buffer
作为第一个参数传进去?如果这样不行,而且你想用 ctypes
创建一个可以写入的字符缓冲区,那么我知道的唯一方法就是使用 create_string_buffer。我不太明白你是从哪里得到 b_address.value
这个东西的。
看起来你需要的是 ctypes.string_at(address, size)
这个东西。它可以让你从你指针指向的内存地址获取内容,并把这些内容放到一个Python字符串缓冲区里。这样你就可以把这个字符串传给 Image.fromstring
或者 pygame.image.frombuffer
使用了。