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