13.1.10. スタンドアロンのターミナルウィンドウ¶
Tools → Open Terminal は独立したターミナルウィンドウを開きます。各ウィンドウは、選択したトランスポート経由で接続された、フレームバッファビューア、ヒストグラム、対話型ターミナルを備えた、それ自体が小型の OpenMV IDE セッションです。メインウィンドウの接続には影響しないため、スタンドアロンターミナルを使えば、最初のカメラを接続したまま2台目のカメラを監視したり、ネットワークの向こう側にあるカメラをデバッグしたりできます。
シリアルポート経由のスタンドアロンターミナルウィンドウ。左側に実行 / 停止 / リセットのツールバーを備えた対話型ターミナル、右側にフレームバッファとヒストグラムがあります。カメラがインバンドでフレームをストリーミングすると、これらが表示されます。¶
New Terminal では、3つのトランスポートのいずれかを選択します。
Serial port -- 任意のボーレート(デフォルト 115,200)の任意のシリアルポートです。2台目のカメラの USB ポート、USB-to-UART ブリッジ経由で接続されたカメラ、Bluetooth シリアルリンク(通常のシリアルポートとして表示されます)、あるいはターミナルが必要な OpenMV 以外の任意のシリアルデバイスに対応します。USB ポートでは、ボーレートが速度を制限することはありません。データは常に USB リンクの速度で転送されます。ただしカメラの USB ポートでは、921,600 と 12,000,000 は避けてください。これらはカメラを REPL から IDE のデバッグプロトコルへ切り替えてしまいます。
TCP -- 指定したホストとポートのサーバーに接続するか、指定したポートでサーバーとして待ち受けます。
UDP -- 同じ2つの役割を UDP 経由で行います。
IDE は直近10個の構成を記憶し、Open Terminal サブメニューに一覧表示してワンクリックで再オープンできるようにします。Clear Menu でそれらを消去できます。
メインウィンドウの出力専用ペインとは異なり、スタンドアロンターミナルは完全に対話型です。これは REPL です。プロンプトで入力すると、接続されたカメラ上で Python が1行ずつ実行され、履歴やタブ補完は MicroPython 自体によって提供されます。ツールバーには、一般的な制御シーケンスのワンクリック相当機能が追加されており、現在のエディタスクリプトの実行、実行中のスクリプトの停止、ソフトリセットのほか、メインターミナルペインと同じクリア、保存、折り返しのコントロールがあります。
ターミナル上部のフレームバッファもライブです。接続されたカメラが圧縮フレームをインバンドで、つまり print 出力と同じストリームに埋め込んでストリーミングすると、ターミナルがそれをデコードして表示し、Record ボタンと Zoom ボタンはメインウィンドウとまったく同じように動作します。この組み合わせが IDE のネットワークデバッグの仕組みです。Wi-Fi 経由で REPL を公開するカメラは、USB ケーブルなしで完全な編集・実行・プレビューのループを利用できます。
13.1.10.1. フレームをインバンドでストリーミングする¶
ターミナルが解釈するエンコーディングはシンプルです。0xFE バイトがフレームを開始し、2つ目の 0xFE がそれを閉じます。その間のすべてのペイロードバイトは最上位ビットが立てられ、圧縮画像の6ビットを保持します。つまり画像3バイトがペイロード4バイトになります。プレーンテキストはこれらのバイト値を決して使わないため、フレームと print() 出力は衝突せずにストリームを共有します。ターミナルはテキストを表示し、フレームを表示します。
以下のスクリプトはキャプチャを行い、各フレームを JPEG に変換し、その形式で出力します。ビットパッキングは ulab を通して実行され(シフト演算子がないため、シフトは乗算と除算で記述されています)、カメラに追従できる十分な速度です:
import csi
import sys
import time
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
def encode_for_ide(data):
n = len(data)
n3 = n - (n % 3)
m = (n3 // 3) * 4
out = bytearray(((n * 8) + 5) // 6 + 2)
out[0] = 0xFE
out[-1] = 0xFE
if n3:
src = np.frombuffer(data, dtype=np.uint8)
dst = np.frombuffer(out, dtype=np.uint8)
b0 = src[0:n3:3]
b1 = src[1:n3:3]
b2 = src[2:n3:3]
dst[1:m + 1:4] = (b0 & 0x3F) | 0x80
dst[2:m + 2:4] = (b0 // 64) | ((b1 & 0x0F) * 4) | 0x80
dst[3:m + 3:4] = (b1 // 16) | ((b2 & 0x03) * 16) | 0x80
dst[4:m + 4:4] = (b2 // 4) | 0x80
if n % 3 == 2:
x = data[n - 2] | (data[n - 1] << 8)
out[m + 1] = 0x80 | (x & 0x3F)
out[m + 2] = 0x80 | ((x >> 6) & 0x3F)
out[m + 3] = 0x80 | ((x >> 12) & 0x3F)
elif n % 3 == 1:
out[m + 1] = 0x80 | (data[n - 1] & 0x3F)
out[m + 2] = 0x80 | (data[n - 1] >> 6)
return out
while True:
clock.tick()
img = csi0.snapshot().to_jpeg(quality=80)
sys.stdout.write(encode_for_ide(img.bytearray()))
print(clock.fps())
これがスタンドアロンターミナルで機能するのは、REPL の出力が待機するためです。カメラはターミナルがデータを取得するまでブロックするので、フレームのすべてのバイトが届き、JPEG は USB のフルスピードまたはハイスピードリンク上で素早く転送されます。同じスクリプトは TCP ターミナル上でもそのまま機能し、これが上記のネットワークデバッグの経路です。唯一機能しないのは IDE のメインデバッグ接続で、その出力チャネルはオーバーフロー時に待機する代わりに自身をリセットします。そこではフレームバッファビューアがすでにカメラのフレームをネイティブに表示しているため、見落としは発生しません。