加快图像标注
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_np
的 700 * 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
调用。
我有一堆 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_np
的 700 * 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
调用。