nnunt预处理之cropping

文章详细介绍了nnUNet1代码中的预处理步骤,包括图像的非零区域裁剪、获取boundingbox和正常化。首先,通过创建非零掩码并填充空洞找到图像的活跃区域;接着,根据掩码确定裁剪的boundingbox;最后,根据bbox对图像和分割标签进行裁剪,将非零区域外的标签设为-1。整个过程旨在优化三维医学图像数据,适应于后续的深度学习任务。
摘要由CSDN通过智能技术生成

我看的代码是nnunet1的代码,预处理对应的文件:

 预处理主要分为三部分:cropping,resample和normalization

  • cropping

图像裁剪就是将三维的医学图像裁剪到它的非零区域,首先在图片中寻找一个最小的bounding box,之后根据改bounding box对于图像进行裁剪。

1. 首先生成 非零掩码区域,并且对非零掩码区域进行空洞填充

#找到不同模态的数据的非零区域并并集
def create_nonzero_mask(data): #创建非零掩码区域
    from scipy.ndimage import binary_fill_holes
    #判断四维还是三维
    #c是模态数量
    assert len(data.shape) == 4 or len(data.shape) == 3, "data must have shape (C, X, Y, Z) or shape (C, X, Y)"
    #创建一个与第二维度相同的布尔类型的数组
    nonzero_mask = np.zeros(data.shape[1:], dtype=bool)
    for c in range(data.shape[0]):#不同模态创建非零掩码区域
        this_mask = data[c] != 0
        nonzero_mask = nonzero_mask | this_mask #非零区域的并集
    #填充空洞部分
    nonzero_mask = binary_fill_holes(nonzero_mask)
    return nonzero_mask #返回图片的非零掩码区域,作为mask,传递到下边的函数

2. 根据生成的非零模板,确定用于裁剪的bounding box的大小和位置

#在代码中就是要找到nonzero_mask在x,y,z三个坐标轴上值为1的最小坐标值以及最大坐标值
def get_bbox_from_mask(mask, outside_value=0):
    mask_voxel_coords = np.where(mask != outside_value) #找到mask中不为零的坐标值并返回
    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]]
    #找到x,y,z三个坐标轴上值为1的最小坐标和最大坐标,作为bbox向下面传递

3. 根据bounding box对图像和分割标注seg进行裁剪,之后组合在一起

#第三步就根据bounding_box对该张图像的每个方向依次进行裁剪,然后重新组合在一起。
#根据bbox提取ROI
def crop_to_bbox(image, bbox):
    assert len(image.shape) == 3, "only supports 3d images"
    #x,y,z三个方向对应非零掩码区域
    resizer = (slice(bbox[0][0], bbox[0][1]), slice(bbox[1][0], bbox[1][1]), slice(bbox[2][0], bbox[2][1]))
    return image[resizer]

#以case为单位读取data和seg
def load_case_from_list_of_files(data_files, seg_file=None):
    assert isinstance(data_files, list) or isinstance(data_files, tuple), "case must be either a list or a tuple"
    #用asert语句判断数据类型,不是的话则返回后面的报错信息'case...'
    properties = OrderedDict()
    data_itk = [sitk.ReadImage(f) for f in data_files]

    properties["original_size_of_raw_data"] = np.array(data_itk[0].GetSize())[[2, 1, 0]]
    properties["original_spacing"] = np.array(data_itk[0].GetSpacing())[[2, 1, 0]]
    properties["list_of_data_files"] = data_files
    properties["seg_file"] = seg_file

    properties["itk_origin"] = data_itk[0].GetOrigin()
    properties["itk_spacing"] = data_itk[0].GetSpacing()
    properties["itk_direction"] = data_itk[0].GetDirection()

    #对数据进行拼接
    data_npy = np.vstack([sitk.GetArrayFromImage(d)[None] for d in data_itk])
    if seg_file is not None:
        seg_itk = sitk.ReadImage(seg_file)
        seg_npy = sitk.GetArrayFromImage(seg_itk)[None].astype(np.float32)
    else:
        seg_npy = None
    return data_npy.astype(np.float32), seg_npy, properties


def crop_to_nonzero(data, seg=None, nonzero_label=-1):
    """

    :param data:
    :param seg:
    :param nonzero_label: this will be written into the segmentation map
    :return:
    """
    nonzero_mask = create_nonzero_mask(data) # 生成非零掩码区域
    bbox = get_bbox_from_mask(nonzero_mask, 0) #获得bounding——box的坐标

    cropped_data = []
    for c in range(data.shape[0]):#不同模态
        #对每个模态的数据根据生成的box进行crop
        cropped = crop_to_bbox(data[c], bbox)
        #将不同模态的数据进行拼接
        cropped_data.append(cropped[None])
    data = np.vstack(cropped_data) #竖直堆叠后的数组

    if seg is not None:
        cropped_seg = []
        for c in range(seg.shape[0]):#对seg进行分割
            cropped = crop_to_bbox(seg[c], bbox)
            cropped_seg.append(cropped[None])
        seg = np.vstack(cropped_seg)
    #对mask也进行crop
    nonzero_mask = crop_to_bbox(nonzero_mask, bbox)[None]
    if seg is not None:
        #将非零区域以外的信息都标注成-1
        #这样值为0的代表图像中之不为0的背景,值为-1的代表图像中值为0的背景
        seg[(seg == 0) & (nonzero_mask == 0)] = nonzero_label
    else:
        nonzero_mask = nonzero_mask.astype(int)
        nonzero_mask[nonzero_mask == 0] = nonzero_label
        nonzero_mask[nonzero_mask > 0] = 0
        seg = nonzero_mask
    return data, seg, bbox

 注意:在对seg进行裁剪之后最后将非零区域之外的信息都标注成-1。对于这一步我的理解是:

这时图像共有两块区域:背景区域和非背景区域

背景区域又分为两部分:值为0的背景(在bounding box之内的背景)和值为-1的背景(不在bounding box之内的背景)搭配下图理解

借鉴大神的,大神博客链接:知乎:如何针对三维医学图像分割任务进行通用数据预处理:nnUNet中预处理流程总结及代码分析

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值