10.2. Devolver uma captura de imagem

Um endpoint de estado é útil, mas a razão pela qual a câmara existe é a objetiva. Adicione um endpoint que devolve o JPEG do que quer que o sensor esteja a ver neste momento.

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'},
    )

Aceda a http://<cam-ip>/snapshot.jpg a partir de um browser e um JPEG da vista atual preenche o separador. Atualize e obterá um novo.

10.2.1. O objeto Response

Um manipulador que devolve um dicionário deixa o microdot tratar do resto. Os bytes JPEG precisam do formato longo: um microdot.Response construído explicitamente. O argumento body aceita qualquer valor semelhante a bytes – o buffer image.Image da câmara é exposto via bytearray(), pelo que o mesmo buffer em que o sensor escreveu vai diretamente para o socket.

Content-Type: image/jpeg é o que diz ao browser para renderizar o corpo como uma imagem. Sem isso, o browser tentaria mostrar os bytes JPEG como texto e veria um ecrã cheio de caracteres sem sentido.

image.Image.compress() executa a codificação JPEG no buffer de imagem existente no lugar e devolve a mesma imagem (agora em formato JPEG) para que os seus bytes possam ser enviados tal como estão. quality=85 é o valor predefinido habitual – suficientemente alto para que a imagem seja nítida, suficientemente baixo para que o ficheiro passe por uma ligação lenta.

10.2.2. A captura bloqueia o ciclo

csi.CSI.snapshot() aguarda que o sensor termine a exposição e a transferência DMA de um fotograma antes de retornar. Dentro de um manipulador assíncrono, isso significa que o ciclo de eventos fica parado durante a duração da exposição – dez, vinte, cinquenta milissegundos dependendo da iluminação. Com um cliente a pedir uma rota de cada vez, isto é invisível; com múltiplos clientes, ou uma corrotina de captura a correr em paralelo, bloquearia tudo o resto.

Existe uma variante não bloqueante de snapshot() para o caso de múltiplas corrotinas (blocking=False devolve o próximo fotograma pronto ou None). Para uma captura por pedido, a chamada bloqueante predefinida é adequada.

O proprietário pode agora aceder a um URL e obter um fotograma atualizado.