在之前的一篇博客里:医学图像分割 unet实现(一),是学习并复现别人的实验。这篇将记录下自己毕设第一阶段的实验。毕设题目为:基于深度学习的肝脏肿瘤分割。
经过几番调整,最终确定:第一阶段分割出腹部图像中的肝脏,作为第二阶段的ROI(region of interest),第二阶段利用ROI对腹部图像进行裁剪,裁剪后的非ROI区域变成黑色,作为该阶段输入,分割出肝脏中的肿瘤(更新2019-4-2,已做完实验,医学图像分割 基于深度学习的肝脏肿瘤分割 实战(二)。第三阶段用随机场的后处理方法进行优化。
此外,觉得当时的那篇文章写得很不用心,没费多大功夫,复制粘贴为主。我记录的主要原因之一就是觉得这方面入门实战的文章很少,希望让情况类似的同学少走一点弯路。可现在自己写的东西过几天再去看,都会一头雾水。没有从主干思路到细节的分层讲解,没有关键点的介绍。这篇文章开始,将尽我所能,写得利于接受一点。
(不过,自己才开始学习一个多月,python和框架都是临时学的,并不能保证博客里的东西是对的,有错误的话,望指出)
正文:
目标
分割出CT腹部图像的肝脏区域。
原始数据介绍
实验用到的数据为3Dircadb,即腹部的CT图。一个病人为一个文件夹。
例如3Dircadb1.1(一共20位),该文件夹下会使用到的子文件夹为PATIENT_DICOM(病人原始腹部3D CT图),MASKS_DICOM(该文件夹下具有不同部位的分割结果,比如liver和liver tumor等等)。如下图所示:
PATIENT_DICOM利用软件展示效果如下:一个dcm文件包含129张切片。
MASKS_DICOM下的liver分割图效果如下:
整体思路
1、数据提取
数据读取:
从原始dcm格式读入成我们需要的数组格式
#part1
import numpy as np
import pydicom
import os
import matplotlib.pyplot as plt
import cv2
from keras.preprocessing.image import ImageDataGenerator
from HDF5DatasetWriter import HDF5DatasetWriter
from HDF5DatasetGenerator import HDF5DatasetGenerator
for i in range(1,18): # 前17个人作为测试集
full_images = [] # 后面用来存储目标切片的列表
full_livers = [] #功能同上
# 注意不同的系统,文件分割符的区别
label_path = '~/3Dircadb/3Dircadb1.%d/MASKS_DICOM/liver'%i
data_path = '~/3Dircadb/3Dircadb1.%d/PATIENT_DICOM'%i
liver_slices = [pydicom.dcmread(label_path + '/' + s) for s in os.listdir(label_path)]
# 注意需要排序,即使文件夹中显示的是有序的,读进来后就是随机的了
liver_slices.sort(key = lambda x: int(x.InstanceNumber))
# s.pixel_array 获取dicom格式中的像素值
livers = np.stack([s.pixel_array for s in liver_slices])
image_slices = [pydicom.dcmread(data_path + '/' + s) for s in os.listdir(data_path)]
image_slices.sort(key = lambda x: int(x.InstanceNumber))
""" 省略进行的预处理操作,具体见part2"""
full_images.append(images)
full_livers.append(livers)
full_images = np.vstack(full_images)
full_images = np.expand_dims(full_images,axis=-1)
full_livers = np.vstack(full_livers)
full_livers = np.expand_dims(full_livers,axis=-1)
2、数据的预处理
1、将ct值转化为标准的hu值
至于为什么要将值进行转化,这儿就不详细说明,具体可以参考医学图像预处理(三)——windowing(ct对比增强),这篇博文中有一样的get_pixels_hu
函数
2、窗口化操作
医学图像预处理(三)——windowing(ct对比增强)
3、直方图均衡化
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
4、归一化
5、仅提取腹部所有切片中包含了肝脏的那些切片,其余的不要
医学图像预处理(四)—— 提取包含目标的切片
#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]
3、数据增强
利用keras的数据增强接口,可以实现分割问题的数据增强。一般的增强是分类问题,这种情况,只需要对image变形,label保持不变。但分割问题,就需要image和mask进行同样的变形处理。具体怎么实现,参考下面代码,注意种子设定成一样的。
# 可以在part1之前设定好(即循环外)
seed=1
data_gen_args = dict(rotation_range=3,
width_shift_range=0.01,
height_shift_range=0.01,
shear_range=0.01,
zoom_range=0.01,
fill_mode='nearest')
image_datagen = ImageDataGenerator(**data_gen_args)
mask_datagen = ImageDataGenerator(**data_gen_args)
#part3 接part2
image_datagen.fit(full_images, augment