13.3.1.3. Streaming dei frame¶
Uno script che cattura frame sulla cam può inviarne ciascuno in streaming all’host tramite USB. Il pattern consiste in due chiamate sull’istanza openmv.Camera: streaming() per attivare o disattivare lo stream e read_frame() per estrarre il frame successivo dal canale.
13.3.1.3.1. Un loop minimale di streaming e visualizzazione¶
Lo script lato cam è il consueto loop di snapshot; la novità è che l’host apre lo streaming e legge indietro il risultato:
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")
La cam cattura i frame in modo continuo; l’host estrae ciascuno dal buffer dello stream non appena arriva. La cam sovrascrive il buffer dello stream a ogni nuovo snapshot, quindi un host che effettua il polling più lentamente di quanto la cam catturi scarterà silenziosamente dei frame – ed è il comportamento corretto per i casi d’uso di tipo viewer.
13.3.1.3.2. Il dict del frame¶
read_frame() restituisce None (nessun frame in attesa) oppure un dict con cinque voci:
Chiave |
Significato |
|---|---|
|
Larghezza del frame in pixel. |
|
Altezza del frame in pixel. |
|
Identificatore del formato pixel dichiarato dalla cam (un intero tra le costanti |
|
Per i formati compressi (JPEG, PNG), la dimensione dell’immagine compressa in byte. Inutilizzato per i formati non compressi. |
|
Il frame come buffer di |
|
Byte inviati dalla cam tramite USB prima della decodifica. Utile per il calcolo effettivo del throughput. |
Il pacchetto converte il formato nativo della cam (GRAYSCALE, RGB565, JPEG) in RGB888 prima di restituirlo, così l’host non deve mai gestire l’RGB565 a bit compressi o il percorso di decompressione JPEG. I frame in scala di grigi vengono restituiti con il valore di luma replicato su tutti e tre i canali.
Il buffer data è disposto riga per riga, dall’alto verso il basso; passarlo direttamente a una libreria di visualizzazione o salvarlo come file RGB grezzo funziona senza ulteriori riordinamenti.
13.3.1.3.3. Modalità streaming grezzo¶
Per impostazione predefinita la cam comprime in JPEG ogni frame catturato prima di inserirlo nel canale dello stream, e read_frame() lo decomprime sull’host. Sulle cam senza supporto JPEG hardware, la compressione software è il passaggio più lento del loop. Passare raw=True la salta:
cam.streaming(True, raw=True, resolution=(320, 240))
La cam invia quindi il buffer di pixel non compresso. I frame non compressi sono molto più grandi dei loro equivalenti JPEG, quindi la cam riduce la scala di ogni frame catturato affinché entri nel canale dello stream prima di inviarlo; l’argomento resolution=(width, height) imposta tale destinazione. L’host riceve comunque RGB888 nel campo data – il pacchetto converte da qualunque formato pixel la cam abbia riportato in format.
13.3.1.3.4. Lasciare che gli eventi guidino il loop¶
Un loop di polling che chiama read_frame() più velocemente di quanto la cam produca i frame trascorre la maggior parte del tempo ottenendo None. Quando l’host ha anche altro lavoro da svolgere (una UI da aggiornare, altri canali su cui effettuare il polling), read_status() è il controllo più economico: restituisce un dict che associa a ogni nome di canale registrato un booleano che indica «i dati sono pronti»:
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 ...
Questa è la forma di loop che usa il viewer della CLI stesso.
13.3.1.3.5. Arrestare lo stream¶
Chiama streaming() con enable=False per arrestare. La cam continua a eseguire il suo script ma non riempie più il buffer dello stream; read_frame() restituisce semplicemente None da quel punto in poi. Chiamare stop() fa la stessa cosa in modo implicito, fermando lo script.