4.16. 画像プレビュー

フレームバッファプールは、アプリケーションがフレームを読み取る場所です。アプリケーションがそれらのフレームを処理している間、フレームをプレビューするためにカメラに接続されているものも、各フレームのコピーを必要とします。カメラはその目的のために2つ目の専用バッファを備えており、それが埋められるタイミングについて1つの単純なルールがあります。 アプリケーションが snapshot() を呼び出すたびに、前回キャプチャされたフレームが 新しいフレームが返される前に プレビューバッファにコピーされます

アプリケーションとプレビューアーが同じメモリを取り合うことは決してありません。アプリケーションはプールから自分のフレームを読み取り、プレビューアーはプレビューバッファから自分のフレームを読み取ります。両方が並行して行われます。

4.16.1. ストリームフレームバッファ

プレビューバッファ、すなわち ストリームフレームバッファ は、フレームバッファプールとは別の、固定サイズのRAM領域1つです。そのサイズはファームウェアのビルド時に設定され、 framesize()pixformat() では変化しません。最近のOpenMV Camでは約1メガバイトが一般的で、これは中程度の解像度のプレビューを保持するのに十分な大きさですが、最大のセンサーサイズでのフル解像度フレームよりはるかに小さいものです。

アプリケーションコードはこのバッファを直接読み書きしません。カメラドライバが snapshot() の副作用としてそれを埋めます。

4.16.2. snapshotがプレビューのために行うこと

snapshot() の各呼び出しで、ドライバはアプリケーションの前回のフレームバッファをプールに戻して新しいものを渡す前に、前回のフレームをプレビューバッファへコピーします。このとき、処理中にアプリケーションがその上に描画したものは画像上に残ったままです。2つの分岐が考えられます。どちらが実行されるかは、カメラではなく プレビューアーによって選択されます 。プレビューを開いたコンシューマーが、raw画像が欲しいのかJPEGが欲しいのか、そして受け入れ可能なrawウィンドウサイズはどれくらいかをドライバに伝えます。

  • rawダウンスケールコピー。 プレビューアーがrawフレームを要求した場合、ドライバは前回のフレームをそのネイティブのピクセル形式(RGB565、グレースケールなど)でコピーします。フレームがプレビューアーの要求したrawウィンドウより大きい場合、ドライバはそれが収まるまでバイリニアフィルタリングで縮小します。そうでなければピクセルはそのまま通過します。圧縮アーティファクトはなく、プレビューアーはアプリケーションが処理していたのと同じピクセルを見ます。

  • JPEG圧縮。 プレビューアーがJPEGを要求した場合、またはrawコピーがストリームバッファにまったく収まらない場合、ドライバは前回のフレームをフル解像度でJPEG圧縮してストリームバッファに格納します。品質はフレームごとに 適応的に 調整され、圧縮出力がストリームバッファの容量内に収まるようにします。フレームが収まると、ドライバは品質を1ステップずつ上限に向かって徐々に引き上げます。この上限はキャプチャされたフレームのピクセルサイズに依存します(小さいフレームほど高い品質が許可され、大きいフレームは小さな内容変化でオーバーフローしないように低めに制限されます)。フレームが収まら ない 場合、ドライバは現在の品質を半分にし、新しい設定が落ち着く時間を確保するためにその低い水準を次の数十フレームの間維持し、オーバーフローしたフレームをプレビューから破棄します。アプリケーションのループは影響を受けずに実行を続け、プレビューアーだけが破棄されたフレームを見逃します。

すでに圧縮された形式でカメラが生成するフレーム(JPEGを直接出力するセンサーでのJPEGピクセル形式)は、両方の分岐をスキップします。エンコードされたビットストリームはそのままプレビューバッファに直接コピーされます。

プレビューアーは独自のスケジュールでポーリングしますが、これは一般にカメラのキャプチャよりはるかに遅いため、rawキャプチャレートを サブサンプリング します。つまり、間に合って読み出されたスナップショットだけが表示されます。プレビューアーが前のフレームを読み出す前に新しい snapshot() がプレビューバッファに到着した場合、バッファはまだプレビューアーによってロックされているため、新しいプレビュー更新はスキップされます。そのキャプチャはプレビューストリームから失われます。アプリケーション自身のフレームバッファプールは影響を受けず、キャプチャされたフレームは通常どおりアプリケーションに渡されます。

4.16.3. 最後のフレームを手動でプッシュする

プレビューは snapshot() の副作用として更新されるため、再びsnapshotを呼び出すことなく終了するスクリプトは、最後にプレビューに送ったものをプレビューアー上に無期限に残します。最初のsnapshotの前に処理を行ってから終了するスクリプトの場合、これは空のプレビューになります。 image.Image.flush() (または CSI オブジェクト上の同等の flush() )は、新しいフレームをキャプチャすることなく、アプリケーションのフレームバッファの現在の内容を必要に応じてストリームバッファにコピーします:

img = csi0.snapshot()
# process the image and draw on it
img.flush()                               # previewer sees the annotated frame

同じ呼び出しは、長時間実行される処理がスナップショットの合間にあり、そうしなければプレビューアーがその間ずっと古いプレビューを表示してしまう場合にも便利です。

注釈

プレビューアプリケーションは、スクリプトが終了する前にストリームバッファからフレームを読み出さなければなりません。短いスクリプトの最後でのflushはフレームをステージングするだけです。スクリプトがその後プレビューアーがポーリングする前にカメラへ制御を返すと、バッファは次の実行で再利用され、その最後のフレームは失われます。スクリプト終了時のプレビューでは、スクリプトが終わる前にプレビューアーがフレームを取得する時間を与えてください(flushの後に短いスリープを入れる、または単にすぐには終了しないなど)。