数据预处理步骤
根据nnUNet框架,三维医学图像分割的通用预处理可以分为四步,分别是数据格式的转换,裁剪crop,重采样resample以及标准化normalization。
1.数据格式的转化
常见的医学图像格式有DICOM(后缀名为.dcm),MHD(后缀名为.mhd和.raw)以及NIFTY(后缀名为.nii或.nii.gz)。
这几种格式都不太方便直接进行操作,一般都使用对应的Python库将数据进行读取后,转换成numpy数组后再进行后续处理。
nnUNet中给出了一种建议的目标数据格式,将每一个病例的数据,都存成一个四维numpy数组(npz)以及与之对应的pickle文件(pkl)。
numpy的文件存储.npy .npz 文件详解
四维数组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步。
第一步
根据四维图像数据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)
在对原始数据裁剪完毕之后,使用同样的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/
转载文章受原作者版权保护。转载请注明原作者出处!