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 |
|---|---|
|
Bildrutans bredd i pixlar. |
|
Bildrutans höjd i pixlar. |
|
Identifierare för pixelformatet som kameran deklarerade (ett heltal från kamerans |
|
För komprimerade format (JPEG, PNG), storleken på den komprimerade bilden i byte. Används inte för okomprimerade format. |
|
Bildrutan som en |
|
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.