我正在使用 3D“图像”,并试图找出如何更有效地绘制独特连接区域(在本例中为血管)。 我有 1 和 2 以及 0 的空格。 我当前创建这些船只所在位置的地图的解决方案是创建一个数组,其中
[1 2 0 2
1 2 0 2
1 1 2 2
0 1 0 2] (但是在3D中)
将变成:
[1 2 0 3
1 2 0 3
1 1 3 3
0 1 0 3]
我使用 np.nonzero(arr )收集所有非零元素的 x,y,z 坐标,然后遍历并找到所有具有相同值的相邻元素(前、后、左、右、上、下;无对角线),将这些元素添加到 np.zeroeslike(arr) 数组中。 在迭代此数组时,我希望能够从 np.nonzero 创建的数组中删除项目,因为我在“按数字绘制”解决方案中遇到它们(我使用该解决方案是因为该软件将按数字绘制解决方案作为输入)。
这是我当前的代码:
def vesselFinder(self, inputVolumeAsArray, minVesselSize):
import numpy as np
nonZeroes = np.nonzero(inputVolumeAsArray != inputVolumeAsArray[0][0][0]) # Assume the origin corner is a null value
visited = np.zeros_like(inputVolumeAsArray, dtype=int)
nzLen = len(nonZeroes[0])
v = 1
for t in range(nzLen-1):
if not visited[nonZeroes[0][t]][nonZeroes[1][t]][nonZeroes[2][t]]:
currentVal = inputVolumeAsArray[nonZeroes[0][t]][nonZeroes[1][t]][nonZeroes[2][t]]
currentPoint = [(nonZeroes[0][t], nonZeroes[1][t], nonZeroes[2][t])]
while currentPoint:
depth, row, col = currentPoint.pop()
if (0 <= depth < len(inputVolumeAsArray) and
0 <= row < len(inputVolumeAsArray[0]) and
0 <= col < len(inputVolumeAsArray[0][0]) and
not (visited[depth][row][col] > 0) and
(inputVolumeAsArray[depth][row][col] == currentVal)):
visited[depth][row][col] = v
currentPoint.extend([(depth + dd, row + dr, col + dc) for dd, dr, dc in [(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]])
cou = np.count_nonzero(visited == v)
if (cou < minVesselSize):
visited[visited == v] = 0
else:
v += 1
return visited
现在,我只是检查“visited”数组中的值是否不是零,如果是,那么我只是跳过该项目。 我想避免一些等待时间,因为我注意到,对于更密集的患者扫描,我的程序需要更长的时间来运行(稀疏图像需要 2 分钟,密集图像需要 30 分钟)。
理解了。希望优化血管分割代码,该代码在 3D 图像中查找相连的组件(血管),并通过给每个血管分配唯一的 ID 来标记它们。当前的基于 flood-fill 算法的解决方案按顺序处理每个体素,这在处理大型密集图像时可能会变慢。
让我们探索一种使用 NumPy 更有效地实现的目标的方法:
使用
np.unique
和集合操作进行优化
-
标记唯一值:
不需要手动为每个相邻区域分配唯一的 ID。
np.unique
函数(使用return_inverse=True
)可以有效地为完成这项工作:
import numpy as np
def optimized_vessel_finder(input_volume, min_vessel_size):
labeled_volume = np.zeros_like(input_volume, dtype=int)
unique_vals, inverse_indices = np.unique(input_volume, return_inverse=True)
labeled_volume = inverse_indices.reshape(input_volume.shape) + 1 # +1 以避免 0 冲突
# ...(稍后处理小血管)...
return labeled_volume
- 高效计算邻域: 仍然可以遍历非零体素,但不必手动检查所有 6 个邻域,可以使用 NumPy 的索引技巧来实现:
# ...(在上面的代码中)...
for current_label in range(1, len(unique_vals) + 1):
vessel_coords = np.argwhere(labeled_volume == current_label)
for coord in vessel_coords:
neighbors = [(coord[0] + dd, coord[1] + dr, coord[2] + dc) for dd, dr, dc in
[(1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)]]
# ...(检查此血管标签的邻居,请参阅下面的说明)...
- 删除小血管: 在标记体素并识别连接的组件后,可以有效地删除低于大小阈值的血管:
# ...(在上面的代码中)...
for current_label in range(1, len(unique_vals) + 1):
# ...(计算邻居)...
vessel_size = np.count_nonzero(labeled_volume == current_label)
if vessel_size < min_vessel_size:
labeled_volume[labeled_volume == current_label] = 0
优化邻域检查
- 可以通过维护“已访问”集来进一步优化邻域检查。对于每个唯一的血管标签,创建一个包含其坐标的集合。在检查邻居时,如果该邻居的坐标已经在集合中,则可以跳过它。
完整的优化代码:
import numpy as np
def optimized_vessel_finder(input_volume, min_vessel_size):
labeled_volume = np.zeros_like(input_volume, dtype=int)
unique_vals, inverse_indices = np.unique(input_volume, return_inverse=True)
labeled_volume = inverse_indices.reshape(input_volume.shape) + 1
for current_label in range(1, len(unique_vals) + 1):
vessel_coords = set(map(tuple, np.argwhere(labeled_volume == current_label))) # 使用集合进行更快的成员资格检查
vessel_size = len(vessel_coords)
if vessel_size < min_vessel_size:
labeled_volume[labeled_volume == current_label] = 0
continue # 跳过进一步的邻居检查
# ...(可以根据需要在此处添加邻域检查逻辑)...
return labeled_volume
其他提示
-
并行化:
研究使用
joblib
或multiprocessing
等库并行化唯一值循环。 -
形态学操作:
如果需要更高级的连接组件分析(例如,考虑对角邻居),请探索 Scikit-image 库(
skimage.measure.label
)中的形态学操作和连接组件标记。
通过应用这些优化,应该可以显著减少血管分割代码的处理时间,尤其是在处理大型密集图像时。
标签:python,numpy From: 78802649