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 з п’ятьма записами:

Ключ

Значення

width

Ширина кадру в пікселях.

height

Висота кадру в пікселях.

format

Ідентифікатор піксельного формату, оголошений камерою (ціле число з констант csi камери).

depth

Для стиснених форматів (JPEG, PNG) — розмір стисненого зображення в байтах. Не використовується для нестиснених форматів.

data

Кадр у вигляді буфера bytes у форматі RGB888. Кожен піксель займає три байти (R, G, B); загальна довжина дорівнює width * height * 3.

raw_size

Байти, надіслані камерою через 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. Коли хост також має іншу роботу (UI для оновлення, інші канали для опитування), 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() неявно робить те саме, зупиняючи скрипт.