【深度学习】一个应用—肝脏CT图像自动分割(术前评估)

这把我C 2021-04-28 16:21:52 15570

【深度学习】一个应用—肝脏CT图像自动分割(术前评估)

文章目录
1 目标
2 数据集
3 LITS2017
    3.1 LiTS数据的预处理
    3.2 LiTS数据的读取
    3.3 数据增强
    3.4 数据存储
4 U-Net3d搭建
5 结果

1 目标

分割出CT腹部图像的肝脏区域。

2 数据集

肝脏和肿瘤分割数据集下载链接
LiTS2017:https://competitions.codalab.org/competitions/17094#participate

3D-IRCADb 01:https://www.ircad.fr/research/3d-ircadb-01/

SLIVER07:https://sliver07.grand-challenge.org/Download/#signin

LiTS2017和SLIVER07需要账号才能下载。

肝脏分割数据集,训练集一共有400张肝脏CT图像以及对应的分割模板,验证集一共有20张肝脏CT图像以及对应的分割模板,如下图所示
在这里插入图片描述
在这里插入图片描述

PATIENT_DICOM利用软件展示效果如下:一个dcm文件包含129张切片。
在这里插入图片描述
MASKS_DICOM下的liver分割图效果如下:
在这里插入图片描述

3 LITS2017

数据集的train集合一共130个样例,都为nii格式,原始CT数据为volume-*.nii,分割的ground truth为segmentation-0.nii,其中0为背景,1为肝脏,2为肿瘤,但是并不是每个样例里边都含有肿瘤

3.1 LiTS数据的预处理

在这里使用了这个源代码进行,找到包含肝脏或者肿瘤的slice,然后上下取n片,作为训练集合

    def fix_data(self):
        upper = 200
        lower = -200
        expand_slice = 20  # 轴向上向外扩张的slice数量
        size = 48  # 取样的slice数量
        stride = 3  # 取样的步长
        down_scale = 0.5
        slice_thickness = 2

        for ct_file in os.listdir(self.row_root_path + 'data/'):
            print(ct_file)
            # 将CT和金标准入读内存
            ct = sitk.ReadImage(os.path.join(self.row_root_path + 'data/', ct_file), sitk.sitkInt16)
            ct_array = sitk.GetArrayFromImage(ct)

            seg = sitk.ReadImage(os.path.join(self.row_root_path + 'label/', ct_file.replace('volume', 'segmentation')),
                                 sitk.sitkInt8)
            seg_array = sitk.GetArrayFromImage(seg)

            print(ct_array.shape, seg_array.shape)

            # 将金标准中肝脏和肝肿瘤的标签融合为一个
            seg_array[seg_array > 0] = 1

            # 将灰度值在阈值之外的截断掉
            ct_array[ct_array > upper] = upper
            ct_array[ct_array < lower] = lower

            # 找到肝脏区域开始和结束的slice,并各向外扩张
            z = np.any(seg_array, axis=(1, 2))
            start_slice, end_slice = np.where(z)[0][[0, -1]]

            # 两个方向上各扩张个slice
            if start_slice - expand_slice < 0:
                start_slice = 0
            else:
                start_slice -= expand_slice

            if end_slice + expand_slice >= seg_array.shape[0]:
                end_slice = seg_array.shape[0] - 1
            else:
                end_slice += expand_slice

            print(str(start_slice) + '--' + str(end_slice))
            # 如果这时候剩下的slice数量不足size,直接放弃,这样的数据很少
            if end_slice - start_slice + 1 < size:
                print('!!!!!!!!!!!!!!!!')
                print(ct_file, 'too little slice')
                print('!!!!!!!!!!!!!!!!')
                continue

            ct_array = ct_array[start_slice:end_slice + 1, :, :]
            seg_array = sitk.GetArrayFromImage(seg)
            seg_array = seg_array[start_slice:end_slice + 1, :, :]

            new_ct = sitk.GetImageFromArray(ct_array)
            new_seg = sitk.GetImageFromArray(seg_array)

            sitk.WriteImage(new_ct, os.path.join(self.data_root_path + 'data/', ct_file))
            sitk.WriteImage(new_seg,
                            os.path.join(self.data_root_path + 'label/', ct_file.replace('volume', 'segmentation')))

将ct值转化为标准的hu值
直方图均衡化
窗口化操作
归一化
仅提取腹部所有切片中包含了肝脏的那些切片,其余的不要

#part2
    # 接part1
   images = get_pixels_hu(image_slices)

   images = transform_ctdata(images,500,150)

   start,end = getRangImageDepth(livers)
   images = clahe_equalized(images,start,end)

   images /= 255.
   # 仅提取腹部所有切片中包含了肝脏的那些切片,其余的不要

   total = (end - 4) - (start+4) +1
   print("%d person, total slices %d"%(i,total))
   # 首和尾目标区域都太小,舍弃
   images = images[start+5:end-5]
   print("%d person, images.shape:(%d,)"%(i,images.shape[0]))

   livers[livers>0] = 1

   livers = livers[start+5:end-5]
def clahe_equalized(imgs,start,end):
   assert (len(imgs.shape)==3)  #3D arrays
   #create a CLAHE object (Arguments are optional).
   clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
   imgs_equalized = np.empty(imgs.shape)
   for i in range(start, end+1):
       imgs_equalized[i,:,:] = clahe.apply(np.array(imgs[i,:,:], dtype = np.uint8))
   return imgs_equalized

3.2 LiTS数据的读取

首先是将130个数据随机分为训练集(0.8)和验证集(0.1)和测试集(0.1)

1、读取volume和segmentation

2、进行scale,将分辨率压缩

3、每个样例随机截取n个(depth,height,width)大小的3维块作为一个输入的batch

4、数据归一化到0-1

5、将读取函数包装为dataset、dataloader

使用的时候主要使用了以下函数

def next_train_batch_3d_sub_by_index(self, train_batch_size, crop_size, index,resize_scale=1):
        train_imgs = np.zeros([train_batch_size, crop_size[0], crop_size[1], crop_size[2], 1])
        train_labels = np.zeros([train_batch_size, crop_size[0], crop_size[1], crop_size[2], self.n_labels])
        img, label = self.get_np_data_3d(self.train_name_list[index],resize_scale=resize_scale)
        for i in range(train_batch_size):
            sub_img, sub_label = util.random_crop_3d(img, label, crop_size)

            sub_img = sub_img[:, :, :, np.newaxis]
            sub_label_onehot = make_one_hot_3d(sub_label, self.n_labels)

            train_imgs[i] = sub_img
            train_labels[i] = sub_label_onehot

        return train_imgs, train_labels

3.3 数据增强

利用keras的数据增强接口,可以实现分割问题的数据增强。一般的增强是分类问题,这种情况,只需要对image变形,label保持不变。但分割问题,就需要image和mask进行同样的变形处理。具体怎么实现,参考下面代码,注意种子设定成一样的。

3.4 数据存储

一般而言,数据量较大的话,都会先将原始数据库的东西转化为np或者h5格式的文件,我感觉这样有两个好处,一是真正输入网络训练的时候io量会大大减少(特别是h5很适用于大的数据库),二是数据分享或者上传至服务器时也方便一点。

实验中会出现两个类,分别是写h5和读h5文件的辅助类:
这读文件的类写成了generator,这样可以结合训练网络时,keras的fit_generator来使用,降低内存开销。

4 U-Net3d搭建

这里其实没什么好讲的,主要使用几个模块,resblock,seblock,RecombinationBlock、denseBlock等,然后上采样方式可以选是线性插值或者是deconv

class UNet(nn.Module):
    def __init__(self, in_channels, filter_num_list, class_num, conv_block=RecombinationBlock, net_mode='2d'):
        super(UNet, self).__init__()

        if net_mode == '2d':
            conv = nn.Conv2d
        elif net_mode == '3d':
            conv = nn.Conv3d
        else:
            conv = None

        self.inc = conv(in_channels, 16, 1)

        # down
        self.down1 = Down(16, filter_num_list[0], conv_block=conv_block, net_mode=net_mode)
        self.down2 = Down(filter_num_list[0], filter_num_list[1], conv_block=conv_block, net_mode=net_mode)
        self.down3 = Down(filter_num_list[1], filter_num_list[2], conv_block=conv_block, net_mode=net_mode)
        self.down4 = Down(filter_num_list[2], filter_num_list[3], conv_block=conv_block, net_mode=net_mode)

        self.bridge = conv_block(filter_num_list[3], filter_num_list[4], net_mode=net_mode)

        # up
        self.up1 = Up(filter_num_list[4], filter_num_list[3], filter_num_list[3], conv_block=conv_block,
                      net_mode=net_mode)
        self.up2 = Up(filter_num_list[3], filter_num_list[2], filter_num_list[2], conv_block=conv_block,
                      net_mode=net_mode)
        self.up3 = Up(filter_num_list[2], filter_num_list[1], filter_num_list[1], conv_block=conv_block,
                      net_mode=net_mode)
        self.up4 = Up(filter_num_list[1], filter_num_list[0], filter_num_list[0], conv_block=conv_block,
                      net_mode=net_mode)

        self.class_conv = conv(filter_num_list[0], class_num, 1)

    def forward(self, input):

        x = input

        x = self.inc(x)

        conv1, x = self.down1(x)

        conv2, x = self.down2(x)

        conv3, x = self.down3(x)

        conv4, x = self.down4(x)

        x = self.bridge(x)

        x = self.up1(x, conv4)

        x = self.up2(x, conv3)

        x = self.up3(x, conv2)

        x = self.up4(x, conv1)

        x = self.class_conv(x)

        x = nn.Softmax(1)(x)

        return x

5 结果

绿色轮廓为真实分割结果,红色轮廓为预测分割结果

在这里插入图片描述

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 97 4 评论 打赏
评论
1个
内容存在敏感词
手气红包
  • 空鸣 2021-04-30 17:21:41
    回复

    1

相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
这把我C
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区