4.15. フレームバッファ¶
カメラセンサーが初期化されると、フレームレートに従ってフレームを連続的に出力します。アプリケーションがそれを受け取る準備ができているかどうかに関わらず、フレーム周期ごとに新しいフレームが1枚生成されます。各フレームはRAM上のどこかに収まる必要があり、そうでなければ失われてしまいます。フレームバッファプールは、フレームがDMAを離れてからユーザーコードで処理されるまでの間に格納される場所であり、カメラがそのプールに保持するフレームバッファの数によって、DMAとアプリケーションがそれらをどのように共有するかが決まります。この選択は framebuffers() を通じて公開されており、バッファ数によって選択される4つのモードが利用できます。
4.15.1. シングルバッファ(count = 1)¶
RAM上に1つのフレームバッファがあります。DMAがそれを埋め、アプリケーションがそこから読み取ります。同じバッファが両方に必要なため、アプリケーションがバッファを解放するまで、次の snapshot() の呼び出しは開始できません。
カメラとアプリケーションはロックステップで動作します。DMAはアプリケーションの完了を待たなければならず、アプリケーションはDMAの完了を待たなければなりません。つまり、達成可能なフレームレートはよくてもセンサーのフレームレートの 半分 になります。センサーが出力するフレームのうち1枚おきにバッファがビジー状態のときに到着し、失われてしまうからです。
このモードはRAM使用量が最も小さく、スループットが最も遅くなります。2つ目のバッファを割り当てるにはRAMが厳しすぎる場合にのみ使用してください。
4.15.2. ダブルバッファ(count = 2)¶
RAM上に2つのフレームバッファがあります。DMAが埋める バック バッファと、アプリケーションが読み取る フロント バッファです。アプリケーションがフロントバッファの処理を終えると2つの役割が入れ替わり、DMAは解放されたばかりのバッファを埋め始め、アプリケーションは埋められたばかりのバッファから読み取ります。
アプリケーションが各フレームをカメラのフレーム周期1つ分未満で処理できる限り、アプリケーションはセンサーのフルフレームレートを得られます。アプリケーションが再び snapshot() を呼び出すとき、DMAの次のフレームはすでにバックバッファで待機しているからです。しかし、処理時間がフレーム周期1つ分を超えた瞬間、レートは半分になります。アプリケーションが1枚を処理する間にカメラは2枚のフレームを生成し、そのうち2枚目だけが配信されるからです。
その点を超えると、レートは処理時間に応じてなめらかに低下します。アプリケーションがまだフロントバッファを処理している間にDMAが新しいバックバッファのフレームを完成させるたびに、新しいフレームは破棄されるのではなく、その場で前のキャプチャを上書きします。アプリケーションは次の snapshot() で常にカメラが生成した最新のフレームを得ることができ、達成可能なアプリケーションのレートはその処理時間の逆数になります。
4.15.3. トリプルバッファ(count = 3)¶
RAM上に3つのフレームバッファがあります。DMAが巡回する2つの バック バッファと、アプリケーションが現在処理している1つの フロント バッファです。これは余裕のあるRAMがある場合にOpenMV Camが選択するデフォルトのモードであり、余裕がない場合は自動的にダブルバッファまたはシングルバッファにフォールバックします。
3つ目のバッファによって、カメラのフレームレートとアプリケーションのフレームレートが完全に切り離されます。DMAは常に書き込むバッファを持ち、アプリケーションは常に読み取るバッファを持ちます。各 snapshot() で、準備が整った最新のバックバッファが新しいフロントバッファになり、前のフロントバッファがDMA用に解放されます。アプリケーションのフレームレートは、各フレームの処理に実際にかかる時間に一致します。処理時間がフレーム周期1つ分をわずかに超えたときにダブルバッファが陥る1/2ステップに陥ることはありません。
4.15.4. ビデオFIFO(count = 4以上)¶
RAM上に4つ以上のフレームバッファがあり、連続してキャプチャされたフレームのリングとして配置されます。カメラが配信するすべてのフレームはFIFOにキューイングされ、 snapshot() は最新のフレームではなく、キューイングされた 最も古い フレームを返します。アプリケーションはキャプチャされたフレームを、各フレームに実際に費やせる時間でキャプチャ順に処理していきます。
このモードは、すべてのフレームが重要で、短時間の処理停止が予想される場合に適切な選択です。消去中にストレージスタックが数十ミリ秒ブロックする可能性のあるSDカードへのビデオ書き込み、一時的に読み取りを停止するホストへのUSB経由のストリーミング、コードで検査するために高速なイベントの短いバーストをバッファリングする場合などです。
アプリケーションがFIFOを排出する前にFIFOが満杯になる場合を処理する2つのポリシーがあります。
古いフレームを破棄する(デフォルト)。 FIFOが満杯になると、アクティブなフレームを除くキューイングされたすべてのフレームが破棄されるため、次の
snapshot()は古いフレームではなく最近のフレームを返します。DMAは全体を通してキャプチャを続けるため、アプリケーションは停止後も常に新鮮なデータを得られます。これは、キャプチャされたストリームを最新に保つことが目的の場合、つまりビデオ録画やライブストリーミングに適切なポリシーです。オーバーフロー時にキャプチャを停止する。
CSIコンストラクタにfflush=Falseを渡すと、FIFOが満杯になったときにDMAがFIFOへの書き込みを停止し、キューイングされたフレームをそのまま残します。snapshot()はアプリケーションがそれらを排出するまでキャプチャ順にフレームを返し続け、その後DMAが再開します。これは、短いバーストのすべてのフレームを保持することが目的の場合、つまり高速な動きをキャプチャして後でコードでフレームごとに検査する場合に適切なポリシーです。
完全なAPIについては csi.CSI.framebuffers() を参照してください。
4.15.5. トリガーモード¶
上記の常時実行モードの代替として トリガー キャプチャがあります。これは、 snapshot() が要求したときにのみセンサーがフレームを出力するものです。カメラはスナップショットの合間はアイドル状態となり、アプリケーションが呼び出すたびに新しい露出を開始します。
そのコストはスループットです。トリガーキャプチャは前回のキャプチャと重ね合わせることができないため、達成可能な最大フレームレートはセンサーの通常レートの半分になります。その利点は露出のタイミングです。スナップショットは露出が始まるタイミングを正確に制御します。これは、フリーランニングのセンサーのローリングフレームがアプリケーションの読み取り準備ができたときにたまたま到達した位置ではなく、ストロボフラッシュ、コンベア位置センサー、GPIOラインのパルスといった外部イベントに露出を合わせる必要がある場合にアプリケーションが求めるものです。
トリガーモードはセンサー固有です。対応するセンサーでは、 csi0.ioctl(csi.IOCTL_SET_TRIGGERED_MODE, True) を呼び出すことで有効になり、 False を渡すことで無効になります。