有效地找到标记图像区域的质心

时间:2017-10-20 00:21:51

标签: numpy image-processing gpu image-segmentation pytorch

我将分割图像作为唯一标签1 ... k的二维矩阵。例如:

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循环(或者在我的例子中,1到6),找到每个标签的点的索引,并平均它们的坐标通过索引图像的网格网格。

但是,我希望以针对GPU计算优化的方式执行此操作。因此,for循环的使用不太理想(对于几百个标签,在一个漂亮的GPU上每个图像大约需要1秒)。我正在使用PyTorch,但实际上任何numpy解决方案都应该足够了。

是否有针对此任务的GPU高效解决方案?

3 个答案:

答案 0 :(得分: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是不值得的,除非它已经存在。

答案 1 :(得分: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]

答案 2 :(得分:1)

一个想法是使用bincount来累积每个区域的行和列索引,使用输入数组中的数字作为bin,因此有一个矢量化解决方案,如下所示 -

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

示例运行 -

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]])