13.3.1.3. Потоковая передача кадров¶
Скрипт, захватывающий кадры на камере, может передавать каждый кадр обратно на хост по USB. Шаблон состоит из двух вызовов экземпляра openmv.Camera: streaming() для включения или выключения потока и read_frame() для извлечения следующего кадра из канала.
13.3.1.3.1. Минимальный цикл потоковой передачи и отображения¶
Скрипт на стороне камеры представляет собой обычный цикл со снимками; новое здесь то, что хост открывает поток и считывает результат обратно:
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")
Камера захватывает кадры непрерывно; хост извлекает каждый из них из буфера потока по мере поступления. Камера перезаписывает буфер потока при каждом новом снимке, поэтому хост, который опрашивает медленнее, чем камера захватывает, будет молча терять кадры — и это правильное поведение для сценариев использования в стиле просмотрщика.
13.3.1.3.2. Словарь кадра¶
read_frame() возвращает либо None (ни один кадр не ожидает), либо dict с пятью записями:
Ключ |
Значение |
|---|---|
|
Ширина кадра в пикселях. |
|
Высота кадра в пикселях. |
|
Идентификатор формата пикселей, объявленный камерой (целое число из констант |
|
Для сжатых форматов (JPEG, PNG) — размер сжатого изображения в байтах. Не используется для несжатых форматов. |
|
Кадр в виде буфера |
|
Количество байтов, отправленных камерой по USB до декодирования. Полезно для фактического расчёта пропускной способности. |
Пакет преобразует собственный формат камеры (GRAYSCALE, RGB565, JPEG) в RGB888 перед возвратом, поэтому хосту никогда не приходится самостоятельно работать с упакованным по битам RGB565 или с путём декомпрессии JPEG. Кадры в оттенках серого возвращаются со значением яркости, продублированным во все три канала.
Буфер data располагается построчно, сверху вниз; его можно передать напрямую в библиотеку отображения или сохранить как файл с необработанными данными RGB без какой-либо дополнительной перестановки.
13.3.1.3.3. Режим необработанной потоковой передачи¶
По умолчанию камера сжимает каждый захваченный кадр в JPEG перед помещением его в канал потока, а read_frame() выполняет декомпрессию на хосте. На камерах без аппаратной поддержки JPEG программное сжатие является самым медленным шагом в цикле. Передача raw=True пропускает его:
cam.streaming(True, raw=True, resolution=(320, 240))
Камера в этом случае отправляет буфер пикселей без сжатия. Несжатые кадры намного больше своих эквивалентов в JPEG, поэтому камера масштабирует каждый захваченный кадр так, чтобы он поместился в канал потока, перед отправкой; аргумент resolution=(width, height) задаёт этот целевой размер. Хост по-прежнему получает RGB888 в поле data — пакет преобразует из любого формата пикселей, который камера сообщила в format.
13.3.1.3.4. Управление циклом через события¶
Цикл опроса, который вызывает read_frame() быстрее, чем камера производит кадры, большую часть времени получает обратно None. Когда у хоста есть и другая работа (обновить интерфейс, опросить другие каналы), read_status() — более дешёвая проверка: она возвращает словарь, сопоставляющий имя каждого зарегистрированного канала с булевым значением «данные готовы»:
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 ...
Именно такую форму цикла использует сам просмотрщик CLI.
13.3.1.3.5. Остановка потока¶
Вызовите streaming() с enable=False, чтобы остановить. Камера продолжает выполнять свой скрипт, но больше не заполняет буфер потока; read_frame() с этого момента просто возвращает None. Вызов stop() делает то же самое неявно, останавливая скрипт.