快速将标记图像转换为{label:[coordinates]}

时间:2015-09-23 20:33:17

标签: python numpy image-processing scipy

说我用scipy.ndimage.measurements.label标记了一张图片,如下所示:

[[0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 0, 0, 3, 0],
 [2, 2, 0, 0, 0, 0],
 [2, 2, 0, 0, 0, 0]]

收集属于每个标签的坐标的快速方法是什么?即类似的东西:

{ 1: [[0, 1], [1, 1], [2, 1]],
  2: [[4, 0], [4, 1], [5, 0], [5, 1]],
  3: [[3, 4]] }

我正在处理大小约为15,000 x 5000像素的图像,并且每个图像的大约一半像素都被标记(即非零)。

不是使用nditer遍历整个图像,而是为每个标签执行np.where(img == label)之类的操作会更快吗?

编辑:

哪种算法最快取决于标记图像与标记图像的数量相比有多大。 Warren Weckesser和Salvador Dali / BHAT IRSHAD的方法(基于np.nonzeronp.where)似乎都与标签的数量成线性比例,而用{{迭代每个图像元素1}}显然与标记图像的大小成线性比例。

小测试的结果:

nditer

所以问题变得更加具体:

对于标签数量大约为size: 1000 x 1000, num_labels: 10 weckesser ... 0.214357852936s dali ... 0.650229930878s nditer ... 6.53645992279s size: 1000 x 1000, num_labels: 100 weckesser ... 0.936990022659s dali ... 1.33582305908s nditer ... 6.81486487389s size: 1000 x 1000, num_labels: 1000 weckesser ... 8.43906402588s dali ... 9.81333303452s nditer ... 7.47897100449s size: 1000 x 1000, num_labels: 10000 weckesser ... 100.405524015s dali ... 118.17239809s nditer ... 9.14583897591s 的标签图像,有一种算法来收集标签坐标,这比迭代每个图像元素(即使用sqrt(size(image)))更快? / p>

4 个答案:

答案 0 :(得分:4)

这是一种可能性:

get_label_indices.py

我调用了这个脚本In [97]: import pprint In [98]: run get_label_indices.py In [99]: pprint.pprint(res) {1: array([[0, 1], [1, 1], [2, 1]]), 2: array([[4, 0], [4, 1], [5, 0], [5, 1]]), 3: array([[3, 4]])} 。这是一个示例运行:

alldata.txt

答案 1 :(得分:1)

你可以做这样的事情(让img是你原来的nd.array)

res = {}
for i in np.unique(img)[1:]:
  x, y = np.where(a == i)
  res[i] = zip(list(x), list(y))

会给你你想要的东西:

{
 1: [(0, 1), (1, 1), (2, 1)],
 2: [(4, 0), (4, 1), (5, 0), (5, 1)],
 3: [(3, 4)]
}

是否会更快 - 取决于基准确定。

根据Warren的建议,我不需要使用独特的,只能做

res = {}
for i in range(1, num_labels + 1)
    x, y = np.where(a == i)
    res[i] = zip(list(x), list(y))

答案 2 :(得分:0)

试试这个:

>>> z
array([[0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0],
       [0, 0, 0, 0, 3, 0],
       [2, 2, 0, 0, 0, 0],
       [2, 2, 0, 0, 0, 0]])
>>> {i:zip(*np.where(z==i)) for i in np.unique(z) if i}
{1: [(0, 1), (1, 1), (2, 1)], 2: [(4, 0), (4, 1), (5, 0), (5, 1)], 3: [(3, 4)]}

答案 3 :(得分:0)

这基本上是一个argsort操作,还有一些额外的工作来获得所需的格式:

def sorting_based(img, nlabels):
    img_flat = img.ravel()

    label_counts = np.bincount(img_flat)
    lin_idx = np.argsort(img_flat)[label_counts[0]:]
    coor = np.column_stack(np.unravel_index(lin_idx, img.shape))

    ptr = np.cumsum(label_counts[1:-1])
    out = dict(enumerate(np.split(coor, ptr), start=1))

    return out

正如您所发现的,对每个标签执行np.where(img == label)会产生二次运行时O(m*n)m=n_pixelsn=n_labels。基于排序的方法将复杂性降低到O(m*log(m) + n)

可以在线性时间内执行此操作,但我不认为可以使用Numpy进行矢量化。你可以滥用类似于this answerscipy.sparse.csr_matrix,但此时你可能最好还是编写真正有意义的代码,在Numba,Cython等中。