10.2. Retornando um snapshot

Um endpoint de status é útil, mas a razão pela qual a câmera existe é a lente. Adicione um endpoint que retorne o JPEG do que o sensor estiver olhando agora.

import csi
from microdot import Response

csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)

@app.get('/snapshot.jpg')
async def snapshot(request):
    img = csi0.snapshot().compress(quality=85)
    return Response(
        body=img.bytearray(),
        headers={'Content-Type': 'image/jpeg'},
    )

Acesse http://<cam-ip>/snapshot.jpg a partir de um navegador e um JPEG da visão atual preenche a aba. Atualize e você obtém um novo.

10.2.1. O objeto Response

Um handler que retorna um dict deixa o microdot fazer o resto. Bytes de JPEG precisam da forma longa: um microdot.Response construído explicitamente. O argumento body aceita qualquer valor do tipo bytes – o buffer image.Image da câmera é exposto via bytearray(), de modo que o mesmo buffer no qual o sensor gravou vai direto para o socket.

Content-Type: image/jpeg é o que informa ao navegador que ele deve renderizar o corpo como uma imagem. Sem isso, o navegador tentaria exibir os bytes do JPEG como texto e você veria uma tela cheia de lixo.

image.Image.compress() executa a codificação JPEG no buffer de imagem existente, no lugar, e retorna a mesma imagem (agora no formato JPEG) para que seus bytes possam ser enviados como estão. quality=85 é o padrão usual – alto o suficiente para que a imagem fique nítida, baixo o suficiente para que o arquivo passe por um link lento.

10.2.2. A captura bloqueia o loop

csi.CSI.snapshot() espera o sensor terminar de expor e de fazer o DMA de um quadro antes de retornar. Dentro de um handler assíncrono, isso significa que o loop de eventos trava durante a exposição – dez, vinte, cinquenta milissegundos, dependendo da iluminação. Com um cliente solicitando uma rota por vez, isso é invisível; com vários clientes, ou uma corrotina de captura rodando em paralelo, isso bloquearia todo o resto.

Existe uma variante não bloqueante de snapshot() para o caso de múltiplas corrotinas (blocking=False retorna o próximo quadro pronto ou None). Para um snapshot por requisição, a chamada bloqueante padrão é adequada.

O proprietário agora pode acessar uma URL e obter um quadro novo.