Tire fotos em pouco tempo usando o módulo de câmera Raspberry Pi

13

Estou trabalhando em um projeto no qual preciso tirar cerca de 30 imagens por segundo (sem filme) usando o módulo de câmera Raspberry Pi.

Estou usando a biblioteca Picamera ( http://picamera.readthedocs.org/en/latest/api.html ) para isso, mas o problema é que tirar uma foto leva de 0,2 a 0,4 segundos, o que é muito demorado. Já defini a use_video_portpropriedade como True, o que ajudou um pouco, mas o tempo ainda é longo.

Alguém sabe como tirar fotos em pouco tempo (cerca de 0,025s) usando Python e o módulo de câmera Raspberry Pi?

Timo Denk
fonte

Respostas:

18

Para tirar fotos em 0,025s com a câmera, você precisará de uma taxa de quadros maior ou igual a 80fps. A razão para exigir 80 fps a 40 fps (considerando que 1 / 0,025 = 40) é que atualmente há algum problema que faz com que todos os outros quadros sejam pulados no codificador de imagem múltipla, para que a taxa de captura efetiva seja metade da taxa de quadros da câmera.

O módulo de câmera do Pi é capaz de 80fps em firmwares posteriores (consulte os modos de câmera nos documentos da picamera), mas apenas em uma resolução VGA (solicitações de resoluções mais altas com taxas de quadros> 30fps resultarão no aumento do VGA para a resolução solicitada, portanto, isso é uma limitação que você enfrentaria mesmo a 40fps). O outro problema que você provavelmente encontrará são as limitações de velocidade do cartão SD. Em outras palavras, você provavelmente precisará capturar algo mais rápido, como uma porta de rede ou fluxos na memória (assumindo que todas as imagens que você precisa capturar caberão na RAM).

O script a seguir me dá uma taxa de captura de ~ 38fps (ou seja, logo acima de 0,025s por foto) em um Pi com overclock definido em 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)))

Se você deseja fazer algo entre cada quadro, isso é possível mesmo capture_sequencefornecendo uma função de gerador em vez de uma lista de saídas:

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

Lembre-se de que, no exemplo acima, o processamento ocorre em série antes da próxima captura (ou seja, qualquer processamento que você faça necessariamente atrasará a próxima captura). É possível reduzir essa latência com truques de segmentação, mas isso envolve uma certa complexidade.

Você também pode procurar capturas não codificadas para processamento (que removem a sobrecarga de codificação e, em seguida, decodificam JPEGs). No entanto, lembre-se de que a CPU do Pi é pequena (especialmente em comparação com a GPU VideoCore). Embora você possa capturar a 40fps, não há como executar qualquer processamento sério desses quadros a 40fps, mesmo com todos os truques mencionados acima. A única maneira realista de executar o processamento de quadros nessa taxa é enviá-los por uma rede para uma máquina mais rápida ou executar o processamento na GPU.

Dave Jones
fonte
Obrigado pela sua resposta rápida! Mas no seu programa não poderei processar as imagens individuais enquanto o .capture_sequence é executado, certo? Existe uma maneira de fazer isso? Porque eu preciso trabalhar com cada imagem individual antes que a próxima seja um token.
Timo Denk
1
A resposta foi alterada para incluir um método de processamento entre quadros com uma função de gerador.
Dave Jones
.capture_sequence parece ignorar KeyboardInterrupts. Você sabe como resolver isso?
Cerin
@Cerin, qual seria o consumo de energia em algo assim?
Ted Taylor da vida
O fps é rápido para esta solução, mas como salvar imagens em arquivos do stream?
bakalolo
4

De acordo com esta resposta do StackOverflow, você pode usar o gstreamer e o seguinte comando para realizar o que deseja:

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 capturar a saída de vídeo do raspivid para gerar um fluxo de vídeo com 25 quadros por segundo e, em seguida, usar o gstreamer para converter o vídeo em imagens jpeg individuais.

Este artigo fornece instruções sobre como instalar o gstreamer1.0 a partir de um repositório alternativo.

HeatfanJohn
fonte