1

私が直面している問題は、大規模なデータセット (最大 2048x2048x40x10000 x、y、z、t == 解凍された数テラバイト、ギブまたはテイク) をセグメント化することです。プラス面として、このデータセットの機能はかなり小さいです。最大20x20x20x20程度。

私が見る限り、これに対するすぐに使えるソリューションはありません(間違っている場合は修正してください)。これに対処する方法についてはいくつかの計画がありますが、フィードバックをお願いします。

1 つのタイムスライスは最大 600 MB です。典型的な状況下では少ない; 私は 4 GB メモリに一連のそのようなスライスを保持できます。

私の特徴のサイズが小さいことを考えると、私の直感では、あらゆる形式のセグメンテーションの巧妙さを避け、ラベルに対してローカルで反復的なフラッドフィルのような更新を行うのが最善であると考えています。隣人がより高いラベルを持っている場合は、それをコピーします。収束するまで繰り返します。反復回数は、任意の次元の最大クラスター サイズによって制限される必要がありますが、これも小さくする必要があります。

CUDA は自然に 3D を好むため、これを 2 段階のプロセスとして行うことができます。まだ収束していないすべての 3D ボリューム スライスを繰り返します。次に、連続するすべてのタイム スライスに対して単純に要素ごとのループを実行し、同じフラッディング ロジックを実行します。

単純なインクリメント ユニーク カウンターで反復を初期化するか、最初に極大値を見つけてそこにラベルをシードすることができます。後者が優先されるため、ラベルでインデックス付けされた配列を保持して、すべての領域の x、y、z、t の最小/最大範囲を格納できます (後処理としても実行できます)。リージョンが最新のタイムスライスまで拡張されていない場合、そのリージョンはデータから消去され、その場所がデータベースに書き込まれます。末尾のタイムスライスがそのように完全に使い果たされた (合計がゼロになる) 場合は、メモリから削除します。(または、メモリがオーバーフローした場合は、最新のものも削除します。このようにして作成された近似は我慢する必要があります)

それはうまくいくようです。z 次元のサイズが限られていることを考えると、x、y、z スレッドブロックを起動するか、x、y ブロックを起動して各スレッドを z 次元でループさせる方がよいと思いますか? それは「試してみる」ようなものですか、それともそれに対するストックアンサーはありますか?

私が思いついた別の最適化。x、y、z のブロックを共有メモリにロードした場合、とにかくそこにメモリを取得している間に、いくつかのフラッドフィル更新を実行する方が高速ではないでしょうか? おそらく、ローカルメモリを収束まで反復させてから先に進むのが最善です...それは、私が推測する上記の問題に関連しています。単一の近隣最大ルックアップは、おそらく次善の計算強度であるため、z をループするか、数回反復することでそれを相殺する必要があります。私は後者の方が好きだと思います。

別の質問です。このようなものはまだ存在していないようですが、同様のことを行うテンプレート コードを含むプロジェクトへのリンクは高く評価されます (最適化された 3D フラッドフィル コード?)。私の CUDA の知識はまだむらがあります。

ご意見やフィードバックをお寄せいただきありがとうございます。

4

3 に答える 3

1

記録のために; 私は満足している実用的な解決策を手に入れました。もう少し成熟したら、どこかにオンラインで公開するかもしれません。説明は次のとおりです。

私が今していることは次のとおりです。

  • 最大値を見つけます。
  • アトミックを使用して段階的に最大値にラベルを付ける
  • 各ピクセルが自分自身を指すポインター画像を作成する
  • ポインター画像の塗りつぶし: 隣人がより大きな値を指している場合は、ポインターをコピーします

これにより、各ポインターが一意のラベルを指している分水界のポインター イメージが得られます。

次に、マージ手順を実行します。隣接するピクセルが別のラベルを指し、それらの値がしきい値を超えている場合、「十分に接続された」最大値が独自の領域を形成しないように、それらのラベルをマージします。

次に、ポインターを逆参照すると、素敵なラベル イメージが得られます。最大値にのみラベルをシードしたため、最大ラベルは小さい数値です。そのサイズの 6xN 配列を割り当て、アトミックを使用して各フィーチャの最小/最大範囲を計算します。

現時点では共有メモリを使用していませんが、すでにかなり高速です。私はうっかりやや賢いことをしてしまいました。最初に集水域を作成し、次に単一のスイープを使用して隣接する集水域をマージするため、一種のマルチレベル アルゴリズムを効果的に実行しています。パフォーマンスは、この一握りのフラッドフィル コールと、2 つまたは 3 つのマージ スイープによって支配されます。共有メモリを正しく使用すると、メモリ帯域幅の要件を 5 分の 1 に減らすことができますが、それがボトルネックとして明示的に現れるまで待つ必要はありません。私はすでに、scipy からラングリングできるものよりも、私が望むものに似たセグメンテーションを取得しており、より高速で、メモリの使用量が桁違いに少ないので、今のところ満足しています。

于 2012-08-10T09:27:41.653 に答える
0

最も簡単な方法は、1D ストレージを使用し、その上に 4D インデックス スキーマをオーバーレイすることです。

アドレス = x + y * 幅 + z * 高さ * 幅 + t * 長さ * 高さ * 幅;

これは次のことを意味します。

data( 0 ,0, 0, 0 ) と data( 1, 0, 0, 0 ) は連続したアドレスですが、

data( 0 ,0, 0, 0 ) と data( 0 ,0, 0, 1 ) は width * height * length アドレス部分です。

ただし、アクセス パターンが最近傍を処理している場合は、インデックス作成に空間的局所性が必要です。

これは、インデックスの Morton Z (Peano Key) 順序付けを使用して実現できます。最近隣を近い線形メモリ アドレスに配置します。この Z オーダー リニア アドレスは、x、y、z、t インデックスの交互のビットをインターリーブすることによって取得されます。2D の例については、

http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious

于 2012-08-06T21:42:14.543 に答える
0

ピラミッド型の処理スキームをお勧めします。組み合わせたボクセルの最大値/最小値を保存するミップマップのような詳細レベルをすばやく計算できます。これにより、octree に似た結果が得られます。次に、関心のある領域でのみセグメンテーションを開始できます。これにより、かなりのスピードアップが得られます。少数の非常に小さなセグメントしかない場合、これはかなりうまくいくはずです。

レベル セット (GPU で実装できる) も使用できると思いますが、これらは実装が少し難しいと思います (「CUDA レベル セット」をググってください)。

時間の経過とともにセグメントを追跡する必要がありますか? それを行う方法はありますか?

于 2012-08-07T09:37:49.730 に答える