web-dev-qa-db-ja.com

疎行列で各島のピークを見つける

未知のサイズのいくつかの島を含む疎行列があります。各島の最高峰を探したいのですが。例としてこのマトリックスを考えてみましょう:

0 0 1 0 0 0 0
0 0 1 2 1 0 0
0 0 0 3 2 1 0
0 1 0 0 0 0 0
0 2 3 4 0 1 0
0 0 1 1 0 1 0
0 0 0 0 0 1 0

この場合、(3、2)で3の位置、(3、4)で4の位置、(5、4)、(5、5)または(5、で1の位置を取得します。 6)。注:私は4接続の隣人を考慮します。

今まで、私は各ピクセルをスキャンするソリューションを思い付きました、そしてそれがゼロでなければそれはそのピクセルからフラッドフィルを開始します。塗りつぶしは、訪問した最大値を追跡しながらピクセルをゼロにペイントします。このアルゴリズムはうまく機能しますが、もっと速い解決策があるかどうか疑問に思っていましたか?

ターゲットプラットフォーム(iOS)では、BLAS、LAPACK、vDSPなどのさまざまなベクター処理フレームワークにアクセスして、ベクター(または行列)の最大値をすばやく見つけるためのいくつかの機能を提供します。たぶん、これらの機能を利用して、より高速なソリューションを実装できますか?

6
MrTJ

かつては、商用の画像処理ソフトウェアの一部として、まったく同じ問題を解決する必要がありました。マトリックスを1回しか読み取らないため、結局、スキャンラインの実装を選択しました。

基本的には、行列の単一の行を考慮し、このスキャンライン上のすべての「イベント」を追跡します。イベントは島の左端と右端です。同じ島の同じ走査線上に複数の左端と右端がある場合があることに注意してください。また、スキャンライン上の2つの独立した島のように見えるものは、後で同じ島になる可能性があることに注意してください。

また、最も高い値を持つ既知の各島の記録も保持します。

最初の行から始めて、イベントを初期化します。次に、一度に1行ずつ下に移動します。そのたびに、マトリックスの行がさらに1行表示され、イベントが更新されます。既存のイベントの位置を更新することに加えて、次のトポロジ変更も考慮する必要があります。

  1. 新しいランドの開始(上):新しいアイランドレコード、および新しい左と右のエッジがスキャンラインに挿入されます。
  2. 土地の終わり(下):左右のイベントがスキャンラインから削除されます。
  3. 湖/海の上:同じ島の2つの独立した半島に分かれる島(2つの新しいイベント)
  4. 湖/海の底:2つの半島が1つに合流します。それらが同じアイランドレコードにまだ属していない場合は、これらの2つのアイランドレコードをマージする必要があります。
3
Kris Van Bael

私が正しく理解していれば、あなたはそれを最も効率的な方法で行っているように思えます。あなたのアルゴリズムを要約してみましょう:

Main:
For each pixel in image:
  If pixel is non-zero:
    Call flood subroutine with pixel.
  End
End

Flood subroutine:
Acquire all neighboring pixels of passed pixel of the same height.
Peak = true
For each pixel 
  If pixel has neighboring pixel with higher height
    Peak = false
    Call flood subroutine with pixel with higher height
  End
End
If Not Peak
  For each pixel
    Set pixel height to 0
  End
Else
  Add peak to list
End

つまり、技術的には画像内のすべてのピクセルを渡してこの塗りつぶしサブルーチンを実行しますが、多くのピクセルが同じ高さを共有している場合は、後でサブルーチンによって設定された高さ0のピクセルが多数ヒットします。

これを少し最適化して、同じ高さのすべてのピクセルを選択するだけでなく、同じアルゴリズムで高さの高い隣接ピクセルがあるかどうかを確認して、後でそのチェックを実行する必要をなくすこともできます。

このアルゴリズムの唯一の問題は、再帰を使用して同じ色のピクセルを保持するため、メモリがかなり大きくなる可能性があることです。高さの高い隣接ピクセルを選択し、現在選択されているピクセルが見つかった場合は0に設定し、リソースを解放してから、高さの高い各ピクセルのフラッドサブルーチンを呼び出すことで、これを少し改善できるでしょう。

お役に立てば幸いです。

2
Neil

私があなたの現在のアルゴリズムを正しく理解しているなら、それは:

loop through all n values in your matrix looking for non 0 ones in O(n)
for each of island, flood fill over the m non zero elements and set them to 0 in O(m)

行列がスパース、つまりn >> mの場合、このアルゴリズムはO(n)複雑さになります。

0を格納しないデータ構造を使用することにより、複雑さを改善できます。たとえば、データのインデックス(i、j)をキーとして、0以外のデータをディクショナリに追加した場合、O(1)キーが存在しないため、値は0です。

we can then for each non zero element we haven't looked at yet, run the flood fill 
and remove every key we look at from the dictionary in a complexity of O(m).

今、あなたの質問はより低いアルゴリズムの複雑さの解決を要求しませんが、より速い解決を要求します、この新しいアルゴリズムは助けになるでしょうか?

まあ、それは依存します:

  • マトリックスが十分に大きく、スパースである場合、これは現在の実装よりも優れているはずです。小さい行列の場合、元のアルゴリズムは定数係数が小さいため、結果的に高速になる可能性があります。
  • データ構造を変更することもできますか? (ここでの回答は「いいえ」のようです)
2
jk.

ブロブのカラーリングのバリエーションのように聞こえます。 (Googleはあなたの友達です!)

最初は空のblobのリストを作成します。ゼロ以外の値をスキャンします。 1つが見つかったら、4方向接続を使用してブロブピクセルを見つけ、「色」を最高値と呼びます。 4方向に接続されたピクセルが不足したら、スキャンを再開して新しいblobを見つけます。

0
John R. Strohm

これはDisjoints Setsの例だと思います。
:セット
ピーク:セット(または「セットの親」)の数が多い

私は「Disjoint Sets Forest」を使用します。各セットは、島の「ピーク」へのポインタを保持します。

https://en.wikipedia.org/wiki/Disjoint-set_data_structure

試してみる。幸運を。

0
Guillaume G