13.3.1.3. 串流影格

在相機上擷取影格的指令碼可以透過 USB 將每個影格串流回主機。其模式是在 openmv.Camera 實例上呼叫兩個方法:以 streaming() 開啟或關閉串流,並以 read_frame() 從通道中取出下一個影格。

13.3.1.3.1. 最簡單的串流與顯示迴圈

相機端的指令碼就是一般的快照迴圈;新增的部分是主機開啟串流並將結果讀回:

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

相機持續擷取影格;主機在每個影格抵達時將其從串流緩衝區中取出。相機在每次新的快照時都會覆寫串流緩衝區,因此輪詢速度比相機擷取速度慢的主機會默默地丟棄影格——對於檢視器類型的使用情境而言,這正是正確的行為。

13.3.1.3.2. 影格字典

read_frame() 會回傳 None(沒有影格在等待)或一個包含五個項目的 dict

意義

width

影格寬度(以像素為單位)。

height

影格高度(以像素為單位)。

format

相機所宣告的像素格式識別碼(來自相機 csi 常數的整數)。

depth

對於壓縮格式(JPEG、PNG),這是壓縮影像的位元組大小。未壓縮格式不使用此項。

data

影格以 RGB888bytes 緩衝區形式呈現。每個像素為三個位元組 (R, G, B);總長度為 width * height * 3

raw_size

相機在解碼前透過 USB 傳送的位元組數。用於實際的吞吐量計算。

套件會在回傳前將相機的原生格式(GRAYSCALERGB565JPEG)轉換為 RGB888,因此主機永遠不必自行處理位元封裝的 RGB565 或 JPEG 解壓縮流程。灰階影格回傳時,會將其亮度值複製到全部三個通道。

data 緩衝區是逐列由上而下排列的;將它直接餵給顯示函式庫或將其儲存為原始 RGB 檔案皆可運作,無需任何額外的重排。

13.3.1.3.3. 原始串流模式

在預設情況下,相機會先將每個擷取的影格進行 JPEG 壓縮,再放入串流通道,而 read_frame() 則在主機端解壓縮。在沒有硬體 JPEG 支援的相機上,軟體壓縮是迴圈中最慢的步驟。傳入 raw=True 可略過此步驟:

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

相機接著會以未壓縮的方式傳送像素緩衝區。未壓縮的影格比其 JPEG 對應版本大得多,因此相機會在傳送前將每個擷取的影格縮小以符合串流通道;resolution=(width, height) 引數設定該目標。主機端仍會在 data 欄位中收到 RGB888——套件會從相機在 format 中所回報的任何像素格式進行轉換。

13.3.1.3.4. 讓事件驅動迴圈

若輪詢迴圈呼叫 read_frame() 的速度比相機產生影格的速度還快,那麼它大部分的時間都在取回 None。當主機還有其他工作要做(要更新的 UI、要輪詢的其他通道)時,read_status() 是更省成本的檢查方式:它會回傳一個字典,將每個已註冊的通道名稱對應到一個布林值,表示「資料是否就緒」:

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

這正是 CLI 檢視器本身所使用的迴圈形式。

13.3.1.3.5. 停止串流

enable=False 呼叫 streaming() 即可停止。相機會繼續執行其指令碼,但不再填入串流緩衝區;從那一刻起,read_frame() 只會回傳 None。呼叫 stop() 會藉由停止指令碼而隱含地達到相同的效果。