13.3.1.3. Strumieniowanie ramek

Skrypt przechwytujący ramki w kamerze może strumieniować każdą ramkę z powrotem do hosta przez USB. Wzorzec opiera się na dwóch wywołaniach na instancji openmv.Camera: streaming() włącza lub wyłącza strumień, a read_frame() pobiera kolejną ramkę z kanału.

13.3.1.3.1. Minimalna pętla strumieniowania i wyświetlania

Skrypt po stronie kamery to zwykła pętla zrzutu obrazu; nowością jest to, że host otwiera strumień i odczytuje wynik z powrotem:

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")

Kamera przechwytuje ramki w sposób ciągły; host pobiera każdą z nich z bufora strumienia w miarę jak się pojawiają. Kamera nadpisuje bufor strumienia przy każdym nowym zrzucie obrazu, więc host odpytujący wolniej niż kamera przechwytuje ramki będzie po cichu gubił ramki – co jest prawidłowym zachowaniem w przypadku zastosowań typu podgląd.

13.3.1.3.2. Słownik ramki

read_frame() zwraca albo None (żadna ramka nie czeka), albo dict z pięcioma wpisami:

Klucz

Znaczenie

width

Szerokość ramki w pikselach.

height

Wysokość ramki w pikselach.

format

Identyfikator formatu piksela zadeklarowany przez kamerę (liczba całkowita ze stałych csi kamery).

depth

Dla formatów skompresowanych (JPEG, PNG) rozmiar skompresowanego obrazu w bajtach. Nieużywane w przypadku formatów nieskompresowanych.

data

Ramka jako bufor bytes w formacie RGB888. Każdy piksel to trzy bajty (R, G, B); całkowita długość to width * height * 3.

raw_size

Liczba bajtów wysłanych przez kamerę przez USB przed dekodowaniem. Przydatne do rzeczywistego obliczenia przepustowości.

Pakiet konwertuje natywny format kamery (GRAYSCALE, RGB565, JPEG) na RGB888 przed zwróceniem, dzięki czemu host nigdy nie musi sam zajmować się upakowanym bitowo RGB565 ani dekompresją JPEG. Ramki w skali szarości są zwracane z wartością luminancji powieloną na wszystkie trzy kanały.

Bufor data jest ułożony wiersz po wierszu, od góry do dołu; przekazanie go bezpośrednio do biblioteki wyświetlania lub zapisanie jako surowy plik RGB działa bez żadnego dalszego przetasowania.

13.3.1.3.3. Tryb surowego strumieniowania

Domyślnie kamera kompresuje każdą przechwyconą ramkę do formatu JPEG przed umieszczeniem jej w kanale strumienia, a read_frame() dekompresuje ją na hoście. W kamerach bez sprzętowej obsługi JPEG programowa kompresja jest najwolniejszym krokiem w pętli. Przekazanie raw=True pomija ją:

cam.streaming(True, raw=True, resolution=(320, 240))

Kamera wysyła wówczas bufor pikseli bez kompresji. Nieskompresowane ramki są znacznie większe niż ich odpowiedniki w formacie JPEG, więc kamera skaluje w dół każdą przechwyconą ramkę, aby zmieściła się w kanale strumienia przed wysłaniem; argument resolution=(width, height) ustawia ten cel. Host nadal otrzymuje RGB888 w polu data – pakiet konwertuje z dowolnego formatu piksela zgłoszonego przez kamerę w polu format.

13.3.1.3.4. Pozwól zdarzeniom sterować pętlą

Pętla odpytująca, która wywołuje read_frame() szybciej, niż kamera produkuje ramki, spędza większość czasu na otrzymywaniu z powrotem None. Gdy host ma także inne zadania do wykonania (interfejs do aktualizacji, inne kanały do odpytania), read_status() jest tańszym sprawdzeniem: zwraca słownik mapujący każdą zarejestrowaną nazwę kanału na wartość logiczną oznaczającą „dane są gotowe”:

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 ...

Taki kształt pętli wykorzystuje sam podgląd CLI.

13.3.1.3.5. Zatrzymywanie strumienia

Wywołaj streaming() z enable=False, aby zatrzymać strumień. Kamera nadal wykonuje swój skrypt, ale nie wypełnia już bufora strumienia; read_frame() od tego momentu zwraca po prostu None. Wywołanie stop() robi to samo niejawnie, zatrzymując skrypt.