2017-10-20 5 views
1

私は、セグメント化された画像を一意のラベル1 ... kの2次元マトリックスとして持っています。例:ラベル付き画像領域の重心を効率的に見つける

img = 
    [1 1 2 2 2 2 2 3 3] 
    [1 1 1 2 2 2 2 3 3] 
    [1 1 2 2 2 2 3 3 3] 
    [1 4 4 4 2 2 2 2 3] 
    [4 4 4 5 5 5 2 3 3] 
    [4 4 4 5 5 6 6 6 6] 
    [4 4 5 5 5 5 6 6 6] 

リージョンの重心を特定しようとしています。つまり、ラベルごとに、重心のX、Y座標は?例えば、ラベル1の重心は(1.25,0.625)である。行番号((0 + 0 + 1 + 1 + 1 + 2 + 2 + 3)/8 = 1.25)と列番号((0 + 0 + 0 + 0 + 1 + 1 + 1 + 2)/8 = 0.625)を平均してください。

これを行う方法を知る唯一の方法は、1からkまでのforループを使用することです。 6)、各ラベルの点のインデックスを見つけ、画像のメッシュグリッドにインデックスを付けることによってそれらの座標を平均する。

しかし、私はGPUの計算に最適化された方法でこれを行うことを検討しています。したがって、forループの使用は理想的ではありません(数百のラベルのためにすばらしいGPUでイメージあたり約1秒かかる)。私はPyTorchを使用していますが、本当にどんなnumpyソリューションでも問題ありません。

このタスクのGPU効率的なソリューションはありますか?

答えて

1

この計算には累積が必要ですが、GPU上での効率はわかりません。これは、擬似コードでシーケンシャルなアルゴリズムです:

int n[k] = 0 
int sx[k] = 0 
int sy[k] = 0 
loop over y: 
    loop over x: 
     i = img[x,y] 
     ++n[i] 
     sx[i] += x 
     sy[i] += y 
for i = 1 to k 
    sx[i] /= n[i] 
    sy[i] /= n[i] 

そしてもちろん、(sx[i],sy[i])は、オブジェクトiの重心です。

CPUが本当に高速ですが、GPUにデータを送信するだけの価値はありません。

+0

これは私があまりにも思い付く最高の解決策です。データはすでにGPU上にあるので、私はそこでそれをやろうと思ったのです。 CPUの動きを見ていきます。これは確かに効率的です - すべてのピクセルが一度触れましたが、私はGPUでこれを並列化する方法があるのだろうかと思っています... – marcman

+0

上記のアルゴリズムの純粋なGPU実装が遅すぎると、イメージ列ごとに配列 'n'、' sx'と 'sy'を作成し、後でそれらを一緒に追加することです。これにより、配列値のアトミック更新を行うのを待っているコアの数を減らすことができます。 –

+0

グローバル配列内の単一の値を原子的に更新すると効率的ですか?あなたはGPU上で誤った共有の問題を抱えていますか? –

1

scikit-imageを使用するか、またはcode(numpy/scipyに基づく)を再利用することを検討してください。ここ

デモである:

import numpy as np 
from skimage import measure 
from time import perf_counter as pc 

img = np.array([[1, 1, 2, 2, 2, 2, 2, 3, 3], 
       [1, 1, 1, 2, 2, 2, 2, 3, 3], 
       [1, 1, 2, 2, 2, 2, 3, 3, 3], 
       [1, 4, 4, 4, 2, 2, 2, 2, 3], 
       [4, 4, 4, 5, 5, 5, 2, 3, 3], 
       [4, 4, 4, 5, 5, 6, 6, 6, 6], 
       [4, 4, 5, 5, 5, 5, 6, 6, 6]]) 

# assuming already labels of 1, 2, ... n 
times = [pc()] 
props = measure.regionprops(img) 
times.append(pc()) 
for i in range(np.unique(img).shape[0]): 
    print(props[i].centroid) 
    times.append(pc()) 

print(np.diff(times)) 

出力:

(1.25, 0.625) 
(1.5555555555555556, 4.4444444444444446) 
(1.8999999999999999, 7.4000000000000004) 
(4.3636363636363633, 1.1818181818181819) 
(5.1111111111111107, 3.6666666666666665) 
(5.4285714285714288, 6.7142857142857144) 
[ 9.05569615e-05 8.51235438e-04 2.48126075e-04 2.59294767e-04 
    2.42692657e-04 2.00734598e-04 2.34542530e-04] 
1

一つのアイデアは、入力アレイ内の番号を使用して、各領域の行と列のインデックスを蓄積するbincountを使用することであろうビンはベクトル化されたソリューションを持っています。 -

m,n = a.shape 
r,c = np.mgrid[:m,:n] 
count = np.bincount(a.ravel()) 
centroid_row = np.bincount(a.ravel(),r.ravel())/count 
centroid_col = np.bincount(a.ravel(),c.ravel())/count 

Sa何度も実行する -

In [77]: a 
Out[77]: 
array([[1, 1, 2, 2, 2, 2, 2, 3, 3], 
     [1, 1, 1, 2, 2, 2, 2, 3, 3], 
     [1, 1, 2, 2, 2, 2, 3, 3, 3], 
     [1, 4, 4, 4, 2, 2, 2, 2, 3], 
     [4, 4, 4, 5, 5, 5, 2, 3, 3], 
     [4, 4, 4, 5, 5, 6, 6, 6, 6], 
     [4, 4, 5, 5, 5, 5, 6, 6, 6]]) 

In [78]: np.c_[centroid_row, centroid_col] 
Out[78]: 
array([[ nan, nan], 
     [ 1.25, 0.62], # centroid for region-1 
     [ 1.56, 4.44], # centroid for region-2 
     [ 1.9 , 7.4 ], # centroid for region-3 and so on. 
     [ 4.36, 1.18], 
     [ 5.11, 3.67], 
     [ 5.43, 6.71]]) 
関連する問題