13.3.1.3. Strömma bildrutor

Ett skript som fångar bildrutor på kameran kan strömma varje bildruta tillbaka till värddatorn över USB. Mönstret består av två anrop på openmv.Camera-instansen: streaming() för att slå på eller av strömmen, och read_frame() för att hämta nästa bildruta ur kanalen.

13.3.1.3.1. En minimal ström-och-visa-loop

Skriptet på kamerasidan är den vanliga stillbildsloopen; det nya är att värddatorn öppnar strömning och läser tillbaka resultatet:

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

Kameran fångar bildrutor kontinuerligt; värddatorn hämtar var och en ur strömbufferten när den landar. Kameran skriver över strömbufferten vid varje ny stillbild, så en värddator som pollar långsammare än kameran fångar kommer tyst att tappa bildrutor – vilket är det rätta beteendet för visningsorienterade användningsfall.

13.3.1.3.2. Bildrutans dict

read_frame() returnerar antingen None (ingen bildruta väntar) eller en dict med fem poster:

Nyckel

Betydelse

width

Bildrutans bredd i pixlar.

height

Bildrutans höjd i pixlar.

format

Identifierare för pixelformatet som kameran deklarerade (ett heltal från kamerans csi-konstanter).

depth

För komprimerade format (JPEG, PNG), storleken på den komprimerade bilden i byte. Används inte för okomprimerade format.

data

Bildrutan som en bytes-buffert i RGB888. Varje pixel är tre byte (R, G, B); den totala längden är width * height * 3.

raw_size

Antal byte som kameran skickade över USB före avkodning. Användbart för att beräkna den faktiska genomströmningen.

Paketet konverterar kamerans ursprungliga format (GRAYSCALE, RGB565, JPEG) till RGB888 innan det returneras, så att värddatorn aldrig själv behöver hantera den bit-packade RGB565-banan eller JPEG-dekomprimeringen. Gråskalebildrutor kommer tillbaka med luma-värdet replikerat in i alla tre kanalerna.

Bufferten data är upplagd rad för rad, uppifrån och ned; att mata den direkt till ett visningsbibliotek eller spara den som en rå RGB-fil fungerar utan ytterligare omflyttning.

13.3.1.3.3. Rått strömningsläge

Som standard JPEG-komprimerar kameran varje fångad bildruta innan den placeras i strömkanalen, och read_frame() dekomprimerar på värddatorn. På kameror utan hårdvarustöd för JPEG är mjukvarukomprimeringen det långsammaste steget i loopen. Att skicka raw=True hoppar över det:

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

Kameran skickar då pixelbufferten okomprimerad. Okomprimerade bildrutor är mycket större än sina JPEG-motsvarigheter, så kameran skalar ned varje fångad bildruta så att den får plats i strömkanalen innan den skickas; argumentet resolution=(width, height) anger det målet. Värddatorn tar fortfarande emot RGB888 i fältet data – paketet konverterar från vilket pixelformat kameran än rapporterade i format.

13.3.1.3.4. Låt händelser driva loopen

En pollningsloop som anropar read_frame() snabbare än kameran producerar bildrutor tillbringar större delen av sin tid med att få None tillbaka. När värddatorn också har annat att göra (ett gränssnitt att uppdatera, andra kanaler att polla) är read_status() den billigare kontrollen: den returnerar en dict som mappar varje registrerat kanalnamn till ett booleskt värde för ”data är klart”:

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

Detta är den loopform som CLI-visaren själv använder.

13.3.1.3.5. Stoppa strömmen

Anropa streaming() med enable=False för att stoppa. Kameran fortsätter köra sitt skript men fyller inte längre strömbufferten; read_frame() returnerar bara None från och med då. Att anropa stop() gör samma sak implicit genom att stoppa skriptet.