13.3.1.3. フレームのストリーミング¶
カメラ上でフレームを取得するスクリプトは、各フレームをUSB経由でホストにストリーミングして返すことができます。そのパターンは openmv.Camera インスタンスに対する2つの呼び出しです。ストリームのオンとオフを切り替える 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 (フレームが待機していない)か、5つのエントリを持つ dict のいずれかを返します。
キー |
意味 |
|---|---|
|
フレームの幅(ピクセル単位)。 |
|
フレームの高さ(ピクセル単位)。 |
|
カメラが宣言したピクセルフォーマット識別子(カメラの |
|
圧縮フォーマット(JPEG、PNG)の場合、圧縮された画像のサイズ(バイト単位)。非圧縮フォーマットでは使用されません。 |
|
RGB888 形式の |
|
デコード前にカメラがUSB経由で送信したバイト数。実際のスループット計算に役立ちます。 |
パッケージは返す前にカメラのネイティブフォーマット(GRAYSCALE、RGB565、JPEG)をRGB888に変換するため、ホストはビットパックされたRGB565やJPEGの伸張処理を自分で扱う必要がありません。グレースケールフレームは、輝度(luma)値が3つのチャンネルすべてに複製された状態で返されます。
data バッファは上から下へ行ごとにレイアウトされています。これをそのまま表示ライブラリに渡したり、RAW RGBファイルとして保存したりしても、それ以上の並べ替えなしで動作します。
13.3.1.3.3. RAWストリーミングモード¶
デフォルトでは、カメラは取得した各フレームをストリームチャンネルに置く前に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() を呼び出すと、スクリプトを停止することで暗黙的に同じ動作をします。