加快图像标注

Speed up image labelling

我有一堆 1000 张图像,其中包含超过 120k 个对象。在第三方软件中运行一个连通分量算法后,我得到了一张彩色图像,其中每个连通对象都有一个标签,一个颜色和一个分配的ID值。

我需要提取构成每个对象的体素坐标列表。

我已经编写了一个 Python 代码来提取此信息,但它非常慢,因为它处理 ~30 labels/min。因此,在最好的情况下处理整个扫描需要2天以上的时间。

将堆栈转换成3D Numpy数组后(img_stack_numpy),下面是代码的主要部分:

# Store the labels and its associated voxels in a dictionary
labels, counts = np.unique(img_stack_np, return_counts=True)
labels_list = list(labels)
dict_labels_and_voxels = {}

for label in labels_list:
    
    if label==0:  # We don't want to store the label 0 (background)
        continue
    
    index = np.where(img_stack_np == label)
    dict_labels_and_voxels[label] = [index]
    

我怎样才能提高它的速度?

当前代码的主要问题在于行 np.where(img_stack_np == label)。实际上,它会针对 label_list 的 120,000 个值迭代 img_stack_np700 * 700 * 1000 = 490,000,000 值,从而导致要检查 490,000,000 * 120,000 = 58,800,000,000,000 个值。

您不需要为每个标签遍历 img_stack_np。您需要的是根据值(即标签)对“体素”进行分类。您可以使用 自定义排序 :

  • 首先,将每个体素的位置与标签存储在一个数组中;
  • 然后,做一个键值排序,其中标签是键,体素位置是值;
  • 然后,遍历已排序的项目以按标签对它们进行分组(或为简单起见使用效率较低的 np.unique);
  • 最后把每组的位置存入最终的dict中

为了简单和限制内存使用,也可以使用基于索引的排序来代替键值排序。这可以用 argsort 来完成。这是一个示例代码:

s1, s2, s3 = img_stack_np.shape

# Classification by label.
# Remove the "kind='stable'" argument if you do not care about the ordering 
# of the voxel positions for a given label in the resulting dict (much faster).
index = np.argsort(img_stack_np, axis=None, kind='stable')
labels = img_stack_np.reshape(img_stack_np.size)[index]

# Generate the associated position
i1 = np.arange(s1).repeat(s2*s3)[index]
i2 = np.tile(np.arange(s2), s1).repeat(s3)[index]
i3 = np.tile(np.arange(s3), s1*s2)[index]

groupLabels, groupSizes = np.unique(img_stack_np, return_counts=True)
groupOffsets = np.concatenate(([0], counts.cumsum()))

dict_labels_and_voxels = {}

for i,label in enumerate(groupLabels):
    if label==0:
        continue
    start, end = groupOffsets[i], groupOffsets[i] + groupSizes[i]
    index = (i1[start:end], i2[start:end], i3[start:end])
    dict_labels_and_voxels[label] = [index]

以下是在我的机器上使用 100x100x1000 整数随机输入和 12000 个标签的结果:

Reference algorithm:                   363.29 s
Proposed algorithm (strict ordering):    3.91 s
Proposed algorithm (relaxed ordering):   2.02 s

因此,在这种情况下,建议的实施速度 180 倍
对于您指定的输入,它应该快几千倍

补充说明:

img_stack_np 中使用 float32 值会占用大量内存:700 * 700 * 1000 * 4 ~= 2 GB。如果可以,请使用 float16 甚至 in8/int16 类型。存储每个体素的位置也需要大量内存:从 3 GB(int16)到 12 GB(int64)。考虑使用更紧凑的数据表示或更小的数据集。如需进一步改进,请查看此 以替换相当慢的 np.unique 调用。