如何加速扩大布尔 numpy 数组中的 3D 区域?

How to speed up dilating a 3D region in a boolean numpy array?

我有一个 3D numpy 数组布尔掩码,它是从 MRI 脑体积中分割出来的。
脑体素 = 真。其他一切 = 假。

我想做的是扩大这个掩模,使其包含 MRI 体积中的周围组织,而不仅仅是被分割的器官,可能是大脑周围 10 毫米的非大脑外皮。

我尝试使用带有钻石滤镜的 skimage.morphology.dilation 进行二维膨胀。虽然这对于单个图像来说既好又快,但我需要在整个体积的多个切片和至少 2 个平面中重复此操作,以接近均匀扩张 3D 蒙版。

我主要从这里获取我的代码:https://scipy-lectures.org/packages/scikit-image/index.html

典型体积形状 = 512、512、270

# 1st pass in axial plane
(x, y, z) = np.shape(3dMask)
for slice_number in range(z):
    image_slice = 3dMask[:, :, slice_number]
    3dMask[:, :, slice_number] = morphology.binary_dilation(image_slice, morphology.diamond(30))
# repeat in coronal plane...

这在每个切片中都能很好地实现所需的效果,但对于 3D 来说速度很慢。 我可以通过仅扩大包含至少一个 'True' 的那些切片来加快速度,但这不可避免地会在每个平面中留下 100 多个切片。还是很慢。

希望 python 侧循环会减慢一切,我在 numpy 和 skimage 中寻找了一个 3D 等效的单一函数,但没有发现任何我认为有用的东西。

我曾想过找到几何中心并简单地将体积放大 5%,但面罩中必然会有孔(space 在大脑的两半之间)这将不再与 MRI 体积匹配,因此没有用...

我认为这意味着我做错了,因为我是 numpy 和 skimage 的新手。

有快速的方法吗?也许是 2D skimage 膨胀的 3D 替代方案?

这个问题其实有点微妙,我会试着解开。

首先要注意的是,大多数 scikit-image 函数实际上在 3D 中工作得很好,包括 binary_dilation!所以你应该在一个理想的世界里能够做到:

dilated = morphology.binary_dilation(
    mask3d, morphology.ball(radius=30)
)

我说在理想世界中是因为它在我的机器上崩溃,可能是因为 this longstanding SciPy bug 阻止 SciPy 过滤器(scikit-image 在引擎盖下使用)处理大邻域大小。

不过,对于方形和菱形邻域,您确实有一个解决方法:用半径为 30 的菱形扩大一次实际上与用半径为 1 的菱形扩大 30 次实际上是一样的!您可以在 for 循环中手动执行此操作,或者您可以使用 scipy.ndimage.binary_dilation 使用 iterations 关键字参数。 (有关此问题的一些讨论,请参阅 this issue。)

from scipy import ndimage as ndi

# make a little 3D diamond:
diamond = ndi.generate_binary_structure(rank=3, connectivity=1)
# dilate 30x with it
dilated = ndi.binary_dilation(mask3d, diamond, iterations=30)

你实际上可以通过这个策略走得更远。例如,如果您的数据集在 x、y 和 z 方向上没有相同的分辨率,您可能想要扩大更多,比如沿着 x 和 y 扩大两倍。您可以分两步完成此操作:

dilated1 = ndi.binary_dilation(mask3d, diamond, iterations=15)
flat = np.copy(diamond)
flat[:, :, 0] = 0
flat[:, :, -1] = 0
dilated2 = ndi.binary_dilation(mask3d, flat, iterations=15)

最后,请注意二元扩张相当于(非二元)卷积,然后是高于 0 的阈值。所以我发现这也有效:

from scipy import signal

b = morphology.ball(radius=30)
dilated = signal.fftconvolve(mask3d, b, mode='same') > 0

但是,对于这个图像大小和在我的机器上,这比迭代膨胀要慢。但是,请牢记这一点,因为不同数据集的性能会有所不同。

作为旁注,我建议在您的 Whosebug 问题中发布完整的工作代码,如 here 所述。在您的情况下,np.shape(3dMask) 是语法错误,因为 3dMask 不是有效的 Python 标识符! =)

希望对您有所帮助!