nnUNet使用指南(三):nnUNet对数据的预处理

数据预处理步骤

根据nnUNet框架,三维医学图像分割的通用预处理可以分为四步,分别是数据格式的转换,裁剪crop,重采样resample以及标准化normalization。

1.数据格式的转化

常见的医学图像格式有DICOM(后缀名为.dcm),MHD(后缀名为.mhd和.raw)以及NIFTY(后缀名为.nii或.nii.gz)。
这几种格式都不太方便直接进行操作,一般都使用对应的Python库将数据进行读取后,转换成numpy数组后再进行后续处理。

nnUNet中给出了一种建议的目标数据格式,将每一个病例的数据,都存成一个四维numpy数组(npz)以及与之对应的pickle文件(pkl)。
numpy的文件存储.npy .npz 文件详解

nnUNet使用指南(三):nnUNet对数据的预处理

四维数组array(C、X、Y、Z)中,C维度的最后一个array[-1,:,:,:]存储的是分割标注结果。
而C维度的前面存储不同模态的数据,如MRI数据中有FLAIR, T1w, t1gd, T2w等四种模态,
array[0,:,:,:]表示FLAIR序列成像的强度数据,array[1,:,:,:]表示T1加权的强度数据,以此类推。
[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:d777983d-437e-4c1a-be88-a6c282e1d07b

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:b036efe4-be30-43b4-97fc-ec287817a0b3

四维数组array的后三个维度代表x,y,z三个坐标表示的三维数据,对于原始影像数据,值大小代表强度,
[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:44be2184-04c6-4a20-80ba-9db370265f9b

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:24cd61f9-3227-4398-8dca-cf0acc614345

在后续的代码中,为了简便,将不同模态的原始图像与分割标注分开,使用data(CXYZ)代表四维图像数据,使用seg(XYZ)代表三维标注数据。

而pickle文件中存储该医学影像中其它的重要信息,是对numpy数组提供信息的补充。包含spacing,direction,origin等信息。

2.图像裁剪Crop

图像裁剪就是将三维的医学图像裁剪到它的非零区域,具体方法就是在图像中寻找一个最小的三维bounding box,
该bounding box区域以外的值为0,使用这个bounding box对图像进行裁剪。
[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:b1009006-77fa-4a16-8851-8f311bafbf4d

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:802907af-c166-4a3f-be1c-78ec4c5c0b6f

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:e3959761-16a1-4732-8c0f-5bafeef5e88d

[En]

[TencentCloudSDKException] code:FailedOperation.ServiceIsolate message:service is stopped due to arrears, please recharge your account in Tencent Cloud requestId:50f1de99-0821-48dd-b84b-d25f2a75c558

裁剪在nnUNet的实现中可以分为3步。

nnUNet使用指南(三):nnUNet对数据的预处理

第一步

根据四维图像数据data(C,X,Y,Z)生成三维的非零模板nonzero_mask,标示图像中哪些区域是非零的 。
不同的模态都有对应的三维数据,产生不同的三维nonzero_mask,而整个四维图像的非零模板为各个模态非零模板的并集。
最后调用scipy库的binary_fill_holes函数对生成的nonzero_mask进行填充。

from scipy.ndimage import binary_fill_holes
data.shape[1:]取x,y,z三维,bool型,初始全为false的三维框nonzero_mask
nonzero_mask = np.zeros(data.shape[1:], dtype=bool)
取data中每一个channel
for c in range(data.shape[0]):
    # this mask 表示当前这个channel的三维图像框,将data中该channel的图像
    # 不等于0的地方标记为True,等于0的地方标记为False,并赋给this mask
    this_mask = data[c] != 0
    # 对三维框和当前框取并集,只要有True的地方都标记为True
    # nparray的并集(|):True + True = True,True + False = True,False + False = False
    # nparray的交集(&):True + True = True,True + False = False,False + False = False
    nonzero_mask = nonzero_mask | this_mask
最后得到的总的三维框包括了在所有channel中有true的地方,就可以包括所有非0区域
用binary_fill_holes对该非零区域去洞填充
nonzero_mask = binary_fill_holes(nonzero_mask)

第二步

根据生成的非零模板,确定用于裁剪的bounding_box大小和位置,在代码中就是要找到nonzero_mask在x,y,z三个坐标轴上值为1的最小坐标值以及最大坐标值。

def get_bbox_from_mask(nonzero_mask, outside_value=0):
    mask_voxel_coords = np.where(nonzero_mask != outside_value)
    minzidx = int(np.min(mask_voxel_coords[0]))
    maxzidx = int(np.max(mask_voxel_coords[0])) + 1
    minxidx = int(np.min(mask_voxel_coords[1]))
    maxxidx = int(np.max(mask_voxel_coords[1])) + 1
    minyidx = int(np.min(mask_voxel_coords[2]))
    maxyidx = int(np.max(mask_voxel_coords[2])) + 1

    return [[minzidx, maxzidx], [minxidx, maxxidx], [minyidx, maxyidx]]

第三步

根据bounding_box对该张图像的每个模态依次进行裁剪,然后重新组合在一起。

bbox = [[minzidx, maxzidx], [minxidx, maxxidx], [minyidx, maxyidx]]
resizer = (slice(bbox[0][0], bbox[0][1]),
            slice(bbox[1][0], bbox[1][1]),
              slice(bbox[2][0], bbox[2][1]))
cropped_data = []
for c in range(data.shape[0]):
    cropped = data[c][resizer]
    cropped_data.append(cropped[None])
data = np.vstack(cropped_data)

nnUNet使用指南(三):nnUNet对数据的预处理

在对原始数据裁剪完毕之后,使用同样的bounding box对分割标注seg进行裁剪,具体步骤与上述代码的第三步一致。

注意到,nnUNet在对标注图像seg进行裁剪之后,还额外利用了nonzero_mask的信息,将nonzero_mask区域以外的背景标签值,从0修改为-1。

non_zero_label = -1
seg[(seg == 0) & (nonzero_mask == 0)] = nonzero_label

这样一来, seg中值为0和-1的都代表背景, 只是值为0的代表图像中值不为0的背景, 值为-1的代表图像中值为0的背景.

这样做可在后续的处理中,用seg

重采样Resample

未完待续

如何针对三维医学图像分割任务进行通用数据预处理:nnUNet中预处理流程总结及代码分析

Original: https://www.cnblogs.com/xyf9474/p/16305556.html
Author: 梅雨明夏
Title: nnUNet使用指南(三):nnUNet对数据的预处理

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/563397/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球