Tome imágenes en poco tiempo con el módulo de cámara Raspberry Pi

13

Estoy trabajando en un proyecto en el que necesito tomar alrededor de 30 imágenes por segundo (sin película) usando el módulo de cámara Raspberry Pi.

Estoy usando la biblioteca Picamera ( http://picamera.readthedocs.org/en/latest/api.html ) para eso, pero el problema es que tomar una foto toma alrededor de 0.2 - 0.4 segundos, lo que es demasiado largo. Ya configuré la use_video_portpropiedad True, lo que ayudó un poco, pero el tiempo aún es demasiado largo.

¿Alguien de ustedes sabe cómo tomar fotos en poco tiempo (aproximadamente 0.025s) usando Python y el módulo de cámara Raspberry Pi?

Timo Denk
fuente

Respuestas:

18

Para tomar fotos en 0.025s con picamera, necesitará una velocidad de fotogramas mayor o igual a 80 fps. La razón para requerir 80 en lugar de 40 fps (dado que 1 / 0.025 = 40) es que actualmente hay algún problema que hace que se salte cualquier otro cuadro en el codificador de múltiples imágenes, por lo que la tasa de captura efectiva termina como la mitad de la velocidad de fotogramas de la cámara.

El módulo de cámara de Pi es capaz de 80 fps en firmwares posteriores (vea los modos de cámara en los documentos de picamera), pero solo a una resolución VGA (las solicitudes de resoluciones más altas con velocidades de cuadro> 30 fps resultarán en un aumento de VGA a la resolución solicitada, por lo que esto es una limitación que enfrentarías incluso a 40 fps). El otro problema que probablemente encuentres son las limitaciones de velocidad de la tarjeta SD. En otras palabras, probablemente necesite capturar algo más rápido como un puerto de red o transmisiones en memoria (suponiendo que todas las imágenes que necesita capturar quepan en la RAM).

El siguiente script me da una velocidad de captura de ~ 38 fps (es decir, justo por encima de 0.025s por foto) en un Pi con overclocking establecido en 900Mhz:

import io
import time
import picamera

with picamera.PiCamera() as camera:
    # Set the camera's resolution to VGA @40fps and give it a couple
    # of seconds to measure exposure etc.
    camera.resolution = (640, 480)
    camera.framerate = 80
    time.sleep(2)
    # Set up 40 in-memory streams
    outputs = [io.BytesIO() for i in range(40)]
    start = time.time()
    camera.capture_sequence(outputs, 'jpeg', use_video_port=True)
    finish = time.time()
    # How fast were we?
    print('Captured 40 images at %.2ffps' % (40 / (finish - start)))

Si desea hacer algo entre cada cuadro, esto es posible incluso capture_sequenceproporcionando una función de generador en lugar de una lista de salidas:

import io
import time
import picamera
#from PIL import Image

def outputs():
    stream = io.BytesIO()
    for i in range(40):
        # This returns the stream for the camera to capture to
        yield stream
        # Once the capture is complete, the loop continues here
        # (read up on generator functions in Python to understand
        # the yield statement). Here you could do some processing
        # on the image...
        #stream.seek(0)
        #img = Image.open(stream)
        # Finally, reset the stream for the next capture
        stream.seek(0)
        stream.truncate()

with picamera.PiCamera() as camera:
    camera.resolution = (640, 480)
    camera.framerate = 80
    time.sleep(2)
    start = time.time()
    camera.capture_sequence(outputs(), 'jpeg', use_video_port=True)
    finish = time.time()
    print('Captured 40 images at %.2ffps' % (40 / (finish - start)))

Tenga en cuenta que en el ejemplo anterior, el procesamiento se produce en serie antes de la siguiente captura (es decir, cualquier procesamiento que realice retrasará necesariamente la próxima captura). Es posible reducir esta latencia con trucos de subprocesos, pero hacerlo implica una cierta cantidad de complejidad.

También es posible que desee buscar capturas no codificadas para el procesamiento (que eliminan la sobrecarga de la codificación y luego la decodificación de archivos JPEG). Sin embargo, tenga en cuenta que la CPU de Pi es pequeña (especialmente en comparación con la GPU VideoCore). Si bien es posible que pueda capturar a 40 fps, no hay forma de que pueda realizar un procesamiento serio de esos cuadros a 40 fps, incluso con todos los trucos mencionados anteriormente. La única forma realista de realizar el procesamiento de cuadros a esa velocidad es enviar los cuadros a través de una red a una máquina más rápida, o realizar el procesamiento en la GPU.

Dave Jones
fuente
¡Gracias por tu rápida respuesta! Pero en su programa no podré procesar las imágenes individuales mientras se ejecuta .capture_sequence, ¿verdad? ¿Hay alguna forma de hacer esto? Porque necesito trabajar con cada imagen individual antes de que la siguiente sea un token.
Timo Denk
1
Se modificó la respuesta para incluir un método para realizar el procesamiento entre cuadros con una función de generador.
Dave Jones
.capture_sequence parece ignorar KeyboardInterrupts. ¿Sabes cómo solucionar esto?
Cerin
@Cerin, ¿cuál sería el consumo de energía en algo como esto?
Ted Taylor of Life
El fps es rápido para esta solución, pero ¿cómo guardar imágenes en archivos de la transmisión?
bakalolo
4

De acuerdo con esta respuesta de StackOverflow , puede usar gstreamer y el siguiente comando para lograr lo que desea:

raspivid -n -t 1000000 -vf -b 2000000 -fps 25 -o - | gst-launch-1.0 fdsrc ! video/x-h264,framerate=25/1,stream-format=byte-stream ! decodebin ! videorate ! video/x-raw,framerate=10/1 ! videoconvert ! jpegenc ! multifilesink location=img_%04d.jpg

Este comando parece tomar la salida de video de raspivid para generar una transmisión de video con 25 cuadros por segundo y luego usar gstreamer para convertir el video a imágenes jpeg individuales.

Este artículo da instrucciones sobre cómo instalar gstreamer1.0 desde un repositorio alternativo.

HeatfanJohn
fuente