13.3.1.3. Transmitindo quadros

Um script que captura quadros na câmera pode transmitir cada quadro de volta para o host pela USB. O padrão são duas chamadas na instância openmv.Camera: streaming() para ligar ou desligar a transmissão, e read_frame() para retirar o próximo quadro do canal.

13.3.1.3.1. Um laço mínimo de transmissão e exibição

O script do lado da câmera é o laço de snapshot habitual; o que há de novo é que o host abre a transmissão e lê o resultado de volta:

from openmv import Camera

script = """
import csi
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
while True:
    csi0.snapshot()
"""

with Camera('/dev/ttyACM0') as cam:
    cam.stop()
    cam.exec(script)
    cam.streaming(True)

    while True:
        if frame := cam.read_frame():
            print(f"{frame['width']}x{frame['height']}, "
                  f"{frame['raw_size']} bytes")

A câmera captura quadros continuamente; o host retira cada um do buffer de transmissão à medida que chega. A câmera sobrescreve o buffer de transmissão a cada novo snapshot, de modo que um host que faz polling mais devagar do que a câmera captura vai descartar quadros silenciosamente – esse é o comportamento correto para casos de uso no estilo de visualização.

13.3.1.3.2. O dicionário do quadro

read_frame() retorna None (nenhum quadro aguardando) ou um dict com cinco entradas:

Chave

Significado

width

Largura do quadro em pixels.

height

Altura do quadro em pixels.

format

Identificador do formato de pixel que a câmera declarou (um inteiro vindo das constantes csi da câmera).

depth

Para formatos compactados (JPEG, PNG), o tamanho da imagem compactada em bytes. Não utilizado para formatos não compactados.

data

O quadro como um buffer bytes em RGB888. Cada pixel ocupa três bytes (R, G, B); o comprimento total é width * height * 3.

raw_size

Bytes que a câmera enviou pela USB antes da decodificação. Útil para o cálculo real de throughput.

O pacote converte o formato nativo da câmera (GRAYSCALE, RGB565, JPEG) para RGB888 antes de retornar, de modo que o host nunca precisa lidar sozinho com o RGB565 empacotado em bits nem com o caminho de descompressão de JPEG. Quadros em escala de cinza retornam com o valor de luminância replicado nos três canais.

O buffer data é organizado linha por linha, de cima para baixo; passá-lo diretamente para uma biblioteca de exibição ou salvá-lo como um arquivo RGB bruto funciona sem qualquer reorganização adicional.

13.3.1.3.3. Modo de transmissão bruta (raw)

Por padrão, a câmera comprime em JPEG cada quadro capturado antes de colocá-lo no canal de transmissão, e read_frame() descomprime no host. Em câmeras sem suporte a JPEG por hardware, a compressão por software é o passo mais lento do laço. Passar raw=True o ignora:

cam.streaming(True, raw=True, resolution=(320, 240))

A câmera então envia o buffer de pixels sem compressão. Quadros não compactados são muito maiores que seus equivalentes em JPEG, então a câmera reduz a escala de cada quadro capturado para caber no canal de transmissão antes de enviá-lo; o argumento resolution=(width, height) define esse alvo. O host ainda recebe RGB888 no campo data – o pacote converte a partir de qualquer formato de pixel que a câmera tenha informado em format.

13.3.1.3.4. Deixando os eventos conduzirem o laço

Um laço de polling que chama read_frame() mais rápido do que a câmera produz quadros passa a maior parte do tempo recebendo None de volta. Quando o host também tem outras tarefas a fazer (uma interface a atualizar, outros canais para consultar), read_status() é a verificação mais barata: ela retorna um dicionário que mapeia cada nome de canal registrado para um booleano de “dados prontos”:

while True:
    status = cam.read_status()

    if status.get('stream'):
        frame = cam.read_frame()
        # ... process the frame ...

    if status.get('stdout'):
        text = cam.read_stdout()
        print(text, end='')

    if status.get('my_channel'):
        data = cam.channel_read('my_channel')
        # ... process custom-channel data ...

Esse é o formato de laço que o próprio visualizador de CLI utiliza.

13.3.1.3.5. Interrompendo a transmissão

Chame streaming() com enable=False para parar. A câmera continua executando seu script, mas não preenche mais o buffer de transmissão; read_frame() simplesmente retorna None a partir desse ponto. Chamar stop() faz o mesmo de forma implícita ao interromper o script.