之前有解释预处理部分的函数,不过觉得还不够详细,同时文字解释还不够直观,所以现在想一步步运行下,打印输出
首先读取原始数据,包括相应的注释(即结节标签)【注意】注释文件中的标签是按x,y,z的顺序给的,但是origin以及spacing都是按照z,y,x的顺序,所以要逆序处理一下([:,::-1])
raw_data,origin,spacing,isflip = load_itk_image("/home/dataset/LUNA16/subset3/1.3.6.1.4.1.14519.5.2.1.6279.6001.126631670596873065041988320084.mhd")
annos = np.array(pandas.read_csv("/home/dataset/LUNA16/CSVFILES/annotations.csv"))
this_annos = np.copy(annos[annos[:,0]==("1.3.6.1.4.1.14519.5.2.1.6279.6001.126631670596873065041988320084")])
raw_label = (this_annos[:,1:-1][:,::-1]-origin)/spacing
然后输出下原始数据的shape,以及标签的内容
print(raw_data.shape)
print(raw_label)
输出
(272, 512, 512)
[[83.84986792 232.12420469969365 348.18196764820215] [229.169256536 308.7302064585643 158.2345410262112]]
然后可视化其中一张切片
plt.imshow(raw_data[229])
效果
现在开始进入预处理流程
之前读取过原始数据,此处不再读取,除原始数据及标签外,还需要读取LUNA16为每个CT提供的掩码,用于剔除肺部以外区域
Mask,origin,spacing,isflip = load_itk_image("/home/dataset/LUNA16/seg-lungs-LUNA16/1.3.6.1.4.1.14519.5.2.1.6279.6001.126631670596873065041988320084.mhd")
掩码是与CT图像同样大小的三维图像,区别在于掩码只有0,1两种值,这张CT的大小是(272,512,512),掩码也是(272,512,512)
值得注意的是此处的掩码却并不是0,1二值,而是0,3,4三种值,0依然代表肺部以外区域,3代表左肺,4代表右肺,为便于将肺部一起处理,还需要将左右两个肺合并一下
m1 = Mask==3 #LUNA16的掩码有两种值,3和4
m2 = Mask==4
Mask = m1+m2
然后这里做了一个我认为没那么重要但是给处理带来很多麻烦的环节,那就是对掩码求取边界(肺部边界),只保留边界内的数据
不妨想象一个正方形,里面有一个小圆,小圆就是掩码,那么此处做的就是求小圆的最小外接矩形,将矩形外的部分砍掉
xx,yy,zz= np.where(Mask)
box = np.array([[np.min(xx),np.max(xx)],[np.min(yy),np.max(yy)],[np.min(zz),np.max(zz)]])
打印输出看一下box
array([[ 21, 264], [130, 407], [ 62, 441]])
做到这,预处理已经差不多了,再加几步
box = box*np.expand_dims(spacing,1)/np.expand_dims(resolution,1) #对边界即掩码的最小外部长方体应用新分辨率 box = np.floor(box).astype('int')
打印输出
array([[ 26, 330], [ 96, 302], [ 46, 327]])
对这个边界向外扩展一点,为了处理边缘的像素
margin = 5 extendbox = np.vstack([np.max([[0,0,0],box[:,0]-margin],0),np.min([newshape,box[:,1]+2*margin],axis=0).T]).T
打印输出
array([[ 21, 340], [ 91, 312], [ 41, 337]])
然后对掩码进行一点处理
convex_mask = m1
dm1 = process_mask(m1) #对掩码采取膨胀操作,去除肺部黑洞
dm2 = process_mask(m2)
dilatedMask = dm1+dm2
Mask = m1+m2
extramask = dilatedMask ^ Mask #异或操作,求出相比于原始掩码膨胀后多出来的区域
这里mask的大小没有变过,仍然是(272,512,512)
bone_thresh = 210
pad_value = 170
sliceim = lumTrans(sliceim) #对原始数据阈值化,并归一化
sliceim = sliceim*dilatedMask+pad_value*(1-dilatedMask).astype('uint8') #170对应归一化话后的水,掩码(膨胀过后)外的区域补充为水
bones = (sliceim*extramask)>bone_thresh #210对应归一化后的骨头,凡是大于骨头的区域都填充为水
sliceim[bones] = pad_value
此时CT数据即sliceim的大小也没有变过,现在要变化一下,进行重采样
sliceim1,_ = resample(sliceim,spacing,resolution,order=1)
查看下此时的大小
(340, 380, 380)
最后还记得之前求的box吗,我们只需要box内的数据即可
sliceim2 = sliceim1[extendbox[0,0]:extendbox[0,1], #将extendbox内数据取出作为最后结果
extendbox[1,0]:extendbox[1,1],
extendbox[2,0]:extendbox[2,1]]
处理完数据,还需要处理标签
之前已经将世界坐标转换为体素坐标,现在要对其应用新的分辨率(这里取[1,1,1])
raw_label = raw_label*spacing/resolution
输出
array([[104.8123349, 172.279793861, 258.41647013999994], [286.46157067, 229.13584731999998, 117.43977386999997]], dtype=object)
最后的最后,减去box的下界
得到
[[83.84986792 232.12420469969365 348.18196764820215] [229.169256536 308.7302064585643 158.2345410262112]]
上面处理过的数据和标签与完整预处理后的clean.npy和label.npy是一样的,证明这个分解的过程没什么纰漏
完结,撒花