10.2. Возврат снимка¶
Конечная точка состояния – это хорошо, но камера существует ради объектива. Добавьте конечную точку, которая возвращает JPEG того, на что датчик смотрит прямо сейчас.
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'},
)
Откройте http://<cam-ip>/snapshot.jpg в браузере, и JPEG текущего вида заполнит вкладку. Обновите страницу – и получите свежий снимок.
10.2.1. Объект Response¶
Обработчик, возвращающий словарь, позволяет microdot сделать всё остальное. Для байтов JPEG нужна развёрнутая форма: явно сконструированный microdot.Response. Аргумент body принимает любое байтоподобное значение – буфер image.Image камеры доступен через bytearray(), так что тот же буфер, в который писал датчик, идёт прямо в сокет.
Content-Type: image/jpeg – это то, что говорит браузеру отрисовать тело как изображение. Без него браузер попытался бы отобразить байты JPEG как текст, и вы увидели бы полный экран мусора.
image.Image.compress() выполняет JPEG-кодирование существующего буфера изображения на месте и возвращает то же изображение (теперь в формате JPEG), чтобы его байты можно было отправить как есть. quality=85 – обычное значение по умолчанию: достаточно высокое, чтобы картинка была чёткой, и достаточно низкое, чтобы файл прошёл по медленному каналу.
10.2.2. Захват блокирует цикл¶
csi.CSI.snapshot() ждёт, пока датчик завершит экспонирование и передачу кадра по DMA, прежде чем вернуть управление. Внутри асинхронного обработчика это означает, что цикл событий замирает на время экспозиции – десять, двадцать, пятьдесят миллисекунд в зависимости от освещения. Когда один клиент запрашивает один маршрут за раз, это незаметно; при нескольких клиентах или при работающей рядом корутине захвата это блокировало бы всё остальное.
Для случая с несколькими корутинами существует неблокирующий вариант snapshot() (blocking=False возвращает следующий готовый кадр или None). Для одного снимка на запрос блокирующий вызов по умолчанию вполне подходит.
Теперь владелец может ткнуть в URL и получить свежий кадр.