views:

477

answers:

7

*I am trying to display preview from webcam captured using v4l.

Here is an idea of how the code looks like:

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()

and I get ValueError: not enough image data

A: 

I don't know much about ctypes, but I am doing a similar thing (wrapped c++ webcam capture, displayed with DirectPython).

In my case I just made a buffer in python like this:

bufferSize = imageWidth * imageHeight
buf = "\0" * bufferSize

Pass buf to your image capture function for filling?

Maybe post a more complete code sample...

sipickles
I am trying to use nothing else but python and bindings for python to capture and display frames from camera using v4l.
manson54
A: 

Have you tried passing buffer directly as the first arg? If that doesn't work and you want to make a writeable character buffer with ctypes, create_string_buffer is the only way I know (I don't understand where you're getting the b_address.value from).

Alex Martelli
b_address is assigned when posix_memalign is called.I am not sure that addresof(b_address) is the correct way to pass that argument and therefore not sure if that's not the actual problem. not the frombuffer method
manson54
A: 

well, I import

c_lib = CDLL(find_library('c'))
posix_memalign = c_lib.posix_memalign
getpagesize = c_lib.getpagesize

and then after

ioctl(device, VIDIOC_S_FMT, addr(format))
ioctl(device, VIDIOC_G_FMT, addr(format))

and stuff like that, I try to acquire memory

posix_memalign(addressof(b_address), getpagesize(), format.fmt.pix.sizeimage)

*now b_address is no longer = None* *b_address is something like* c_void_p(145014784)

then I start the loop, QBUF, DQBUF, etc..

the thing is, that when I call pygame.image.frombuffer

pg_img = pygame.image.frombuffer(
         buffer.m.userptr,
         (format.fmt.pix.width, format.fmt.pix.height),
         preview_settings['pixformat']
         )

I get TypeError: expected a character buffer object

manson54
A: 

OK, what I did was to leave pygame for Tkinter and PIL

now after the same allocation I pass buffer.m.userptr *to fromstring method from Image

first I have, of course, the following:

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()

and now preview:

im = Image.fromstring(ps['pixformat'], (format.fmt.pix.width, format.fmt.pix.height), '\0'*buffer.m.userptr)
preview.paste(im)
tk.update()

I did what @sipickles said, with '\0' to see if the whole thing will work *And it did :)*

the thing is how to pas that userptr correctly and is the data in it actually what it needs to be passed to the preview

I am really lost here. Someone knows v4l2?

manson54
+1  A: 

Looks like ctypes.string_at(address, size) is what you want. That'll give you a python string buffer with the contents of the memory at your pointer's address. This should be suitable to pass to Image.fromstring or pygame.image.frombuffer.

Geoff Reedy
Thank you. Thank you very much. That really helped.I now get ValueError, though: ValueError: not enough image dataand I will have to investigate further on that
manson54
A: 

Geoff Reedy's suggestion was of great help, but now I get ValueError: not enough image data

And if however I multiply format.fmt.pix.sizeimage by 8 I don't get that error I don't understand why is the image data not enough

manson54
A: 

Ok. so for now I fixed the sizeimage problem by setting the sizeimage myself:

Now frombuffer displays something that is not the frame from the buffer.

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()
type = V4L2_BUF_TYPE_VIDEO_CAPTURE

device = open(device_name, 'rw')

ioctl(device, VIDIOC_QUERYCAP, 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

request.type = type
request.memory = V4L2_MEMORY_USERPTR
request.count = 1

format.fmt.pix.sizeimage = format.fmt.pix.width * format.fmt.pix.height * 4
buffer.length = format.fmt.pix.sizeimage

ioctl(device, VIDIOC_S_FMT, format)

posix_memalign(byref(b_address), getpagesize(), format.fmt.pix.sizeimage)

buffer.m.userptr = b_address.value

buffer.type = request.type
buffer.memory = request.memory

ioctl(device, VIDIOC_REQBUFS, request)

while True:

    ioctl(device, VIDIOC_QBUF, buffer)

    ioctl(device, VIDIOC_STREAMON, cast(type, c_void_p))

    ioctl(device, VIDIOC_DQBUF, buffer)

    **# What happens here? preview_data is wrong?**
    preview_data = string_at(buffer.m.userptr, buffer.length)

    im = frombuffer(ps['pixformat'], (ps['width'], ps['height']), preview_data)
    image.paste(im)
    preview.update()
manson54