OpenCV:C++ と C のパフォーマンス比較



現在、OpenCV API (C++) を使用していくつかのアプリケーションを開発しています。 )。このアプリケーションは、ビデオで処理を行います。


PC では、すべてが非常に高速に動作します。そして今日、このアプリケーションを Android に移植することにしました (カメラをビデオ入力として使用するため)。幸いなことに、Android 用の OpenCV があるので、サンプルの Android アプリケーションにネイティブ コードを追加しました。パフォーマンス以外はすべて正常に動作します。アプリケーションのベンチマークを行ったところ、アプリケーションは 4 ~ 5 fps で動作することがわかりましたが、これは実際には受け入れられません (私のデバイスにはシングルコア 1 GHz プロセッサが搭載されています) - 約 10 fps で動作するようにしたいと考えています。


C でアプリケーションを完全に書き直すことは理にかなっていますか? ? std::vector などを使用することを知っています 開発者にとっては非常に快適ですが、私は気にしません。


OpenCV's C のようです インターフェイスは C++ と同じ関数/メソッドを持っています インターフェース。


この質問をグーグル検索しましたが、何も見つかりませんでした.


アドバイスありがとうございます。


答え:


私は Android と最適化 (4 ミリ秒でフレームを処理するビデオ処理アプリを作成しました) でかなり多くの作業を行ってきたので、適切な回答を提供できることを願っています.


OpenCV では、C と C++ のインターフェイスに大きな違いはありません。コードの一部は C で書かれており、C++ ラッパーを持っています。この 2 つの大きな違い (Shervin Emami によって測定) は、リグレッション、バグ修正、または品質の向上のいずれかです。最新の OpenCV バージョンを使用する必要があります。


なぜ書き直さないのですか?


かなりの時間を費やすことになりますが、その時間をもっと有効に使うことができます。 C インターフェイスは扱いにくく、バグやメモリ リークが発生する可能性が高くなります。私の意見では、あなたはそれを避けるべきです。


最適化のアドバイス


A. 最適化をオンにします。


コンパイラの最適化とデバッグ アサーションの欠如の両方が、実行時間に大きな違いをもたらす可能性があります。


B. アプリをプロファイリングします。


はるかに簡単なので、最初にコンピューターで実行してください。 Visual Studio プロファイラーを使用して、遅い部分を特定します。それらを最適化します。遅いと思うからではなく、測定するから最適化しないでください。最も遅い関数から始めて、可能な限り最適化してから、2 番目に遅い関数を使用します。変更を測定して、実際に高速であることを確認してください。


C. アルゴリズムに焦点を当てます。


より高速なアルゴリズムは、桁違い (100 倍) にパフォーマンスを向上させることができます。 C++ のトリックを使用すると、おそらく 2 倍のパフォーマンスが向上します。


古典的なテクニック:



  • ビデオ フレームのサイズを小さくします。多くの場合、1024x768 の代わりに 200x300px の画像から情報を抽出できます。最初の面積は 10 分の 1 です。


  • 複雑な操作ではなく、より単純な操作を使用してください。浮動小数点の代わりに整数を使用してください。 double は絶対に使用しないでください マトリックスまたは for で 何千回も実行されるループ。


  • できるだけ少ない計算を行います。すべてのフレームですべてのオブジェクトを処理するのではなく、画像の特定の領域でのみオブジェクトを追跡できますか?非常に小さな画像で大まかな/おおよその検出を行い、フル フレームの ROI でそれを調整できますか?



D. 必要に応じて C を使用


ループでは、C++ の代わりに C スタイルを使用することが理にかなっている場合があります。データ行列または float 配列へのポインターは、mat.at または std::vector<> よりもはるかに高速です。多くの場合、ボトルネックはネストされたループです。それに集中してください。 vector<> を至る所で置き換えて、コードをスパゲティ化するのは意味がありません。


E. 隠れたコストを回避


一部の OpenCV 関数は、データを double に変換し、処理してから、入力形式に変換します。それらに注意してください。モバイル デバイスのパフォーマンスが低下します。例:ワーピング、スケーリング、型変換。また、色空間の変換は遅延することが知られています。ネイティブ YUV から直接取得したグレースケールを優先します。


F. ベクトル化を使用


ARM プロセッサは、NEON と呼ばれるテクノロジを使用してベクトル化を実装します。それを使用することを学びます。パワフルです!


小さな例:


float* a, *b, *c;
// init a and b to 1000001 elements
for(int i=0;i<1000001;i++)
c[i] = a[i]*b[i];

は次のように書き直すことができます。より冗長ですが、はるかに高速です。


float* a, *b, *c;
// init a and b to 1000001 elements
float32x4_t _a, _b, _c;
int i;
for(i=0;i<1000001;i+=4)
{
a_ = vld1q_f32( &a[i] ); // load 4 floats from a in a NEON register
b_ = vld1q_f32( &b[i] );
c_ = vmulq_f32(a_, b_); // perform 4 float multiplies in parrallel
vst1q_f32( &c[i], c_); // store the four results in c
}
// the vector size is not always multiple of 4 or 8 or 16.
// Process the remaining elements
for(;i<1000001;i++)
c[i] = a[i]*b[i];

純粋主義者は、アセンブラーで書かなければならないと言いますが、通常のプログラマーにとっては少し気が遠くなります。上記の例のように、gcc 組み込み関数を使用して良い結果が得られました。


すぐに開始する別の方法は、OpenCV でハンドコーディングされた SSE 最適化コードを NEON に変換することです。 SSE は Intel プロセッサの NEON に相当するものであり、ここのように多くの OpenCV 関数で使用されています。これは、uchar 行列 (通常の画像形式) の画像フィルタリング コードです。やみくもに命令を 1 つずつ変換するのではなく、まず例として取り上げてください。


NEON の詳細については、このブログと次の投稿をご覧ください。


G. 画像キャプチャに注意


モバイルデバイスでは驚くほど遅くなる可能性があります。最適化はデバイスと OS 固有です。


いくつかのコードの回答