大きな画像で(おそらく重い)ピクセル処理を行う場合、マルチスレッド化が必須になります。標準的な方法は、インデックスがスレッドプール内の複数のスレッドに分割されるループを開始することです。パフォーマンスの利点は、結果の正確性を保証するために適切なスレッドセーフ対策を講じるとすぐに明らかになります。
ただし、インデックスを分割する方法は複数あります。最も一般的な方法は、行またはピクセルによるパーティション化です。ここでは、それぞれの長所と短所についての私の解釈を示します。
行ごと:
スレッド作成のオーバーヘッドが少ない
行の数がスレッドの数で割り切れない可能性があるため、スレッドのロードが均一にならない可能性があります。これにより、幅は広いが高さが高くないイメージが複数のコアにわたって非効率的に処理される可能性があります
ピクセル別:
スレッド作成オーバーヘッドの増加
スレッドの数で割り切れないインデックスの処理にかかる時間が比較的短いため、スレッドの負荷をより均等に分散できます。
私の解釈は正しいですか、それともストーリーにもっとありますか?常にどちらか一方を選択する必要がありますか?
参考までに、C#ではParallel.For()関数を使用しています。
私は、各タスクがピクセルとともにltrb
rectを取得するアプローチを使用して、処理する画像の長方形の領域を表します。
これにより、画像が高さよりもはるかに広い場合でも、長方形のチャンクに分割して、たとえばスレッドごとに1024ピクセルで処理できます。合計1024ピクセル未満の小さな画像の場合は、並列forループを適用することさえありません。これらのケースでは、シングルスレッドのforループを使用する方が一般的に安価だからです。
通常、タスクごとに1ピクセルを割り当てようとしても、このような優れたパフォーマンスは得られません。少なくともOMPやTBBのようなライブラリでは、各タスクで実行する十分な量の作業が必要です。そうしないと、タスクのスケジュールのオーバーヘッドが、マルチスレッドの利点を上回り、シングルスレッドのパフォーマンスよりも簡単に悪化する可能性があります。
また、画像アルゴリズムが処理するピクセルの位置を気にしない限り、ピクセルごとにピクセル座標を渡す必要があるという別のオーバーヘッドが生じます。
したがって、私が行うように長方形のチャンクで処理するか、各スレッドにスキャンラインを処理させることも悪くはなく、一般的なケースでは一般的に十分です。
この作業をGPUに移行することを検討する必要があります。超並列処理用に設計されており、多くの画像処理タスクがこのカテゴリに分類されます。また、最新のCPUのような数十のコアではなく、多くの場合、数千のコアを備えています。カードへのアップロードとメモリへのダウンロードのオーバーヘッドは、処理が非常に単純なものでない限り、処理速度によってしばしば小さくなります。多くの場合、GPUコアは一度にフラグメントで動作します。これは通常、2x2ピクセル領域のようなものです。私の経験では、HD(1920 x 1080)ビデオ映像で30 fpsを維持するのは簡単で、60を打つことはそれほど難しくありません。多くの場合、4Kフッテージのリアルタイム処理も可能です。
画像処理にGPUを非常に効率的に使用する例として、AppleのCoreImage、特にそれらのCIFilterクラスを確認することをお勧めします。 macOSを使用していない場合でも、アイデアは他のシステムに適用できます。フラグメントシェーダーとして表現される小さなカーネルがあります。これらのシェーダを連結して、関連する中間バッファの数を減らすことができます。