在医学图像分割 基于深度学习的肝脏肿瘤分割 实战(一)中,实现了对肝脏的分割,但是后续在使用相同的处理方法与模型进行肿瘤分割的时候,遇到了两次问题。
第一次,网络的dice系数,训练集上一直只能达到40%左右,测试集上只有10%左右,而且结果明显不对。我认为是数据层面有问题,随机又看了很多原始图片,发现很多肿瘤肉眼都无法分辨,于是进行了实验验证猜想,实验写在了博客:医学图像预处理(五) 器官与病灶的直方图,即验证很多病人的肝脏和肿瘤灰度值(更准确地说是hu值)几乎是重叠的。
实验验证了猜想,开始重新做实验。我于是从3Dircadb数据库换成了LiTS2017数据库,后者的数据量更大。然后根据病人的肝脏与肿瘤直方图分布,手动将130位病人划分成了三个等级:level1:肝脏与肿瘤对比度最大。level3:肝脏与肿瘤的对比度几乎没有。训练数据只选择level1&level2。
当然,数据预处理操作和ROI操作和之前一样,是都有的。
但是这回做实验,一样出了问题。即第二次问题。
第二次:先是dice系数在训练集上可以到10%,可是又会突然回到接近0的值。我百思不得其解,以为是发生了梯度消失或者梯度爆炸的问题,还差了很多资料看如何解决。
可是,后来想着想着,发现一个奇怪又严肃的问题,就是ROI操作会将真实肝脏分割结果与原图做“与”操作,这样,非感兴趣的区域就会变黑,就可以让网络集中注意力在肝脏内部的区域,可是,肝脏内部的肿瘤也是黑色(一般灰度值低于肝脏)的呀??为什么外面变成黑色就是“非感兴趣区域”,而肝脏里的就得是“目标”呢? 想了想之前肝脏分割的图片,发现做窗口值等的操作,也是将非目标区域变黑,然后可以突出肝脏(肝脏变成灰白色,当然,还有其他器官更是白色)。
于是,我做了一个大胆的实验,将肝脏变成灰色,肿瘤变成白色,肝脏外的区域为黑色。(进行了颜色翻转)
这一次实验,训练集上,dice系数90%左右,测试集上70%左右。虽然不够好,但是至少证明了猜想是可行的。也反映了,数据是王道:种瓜得瓜,种豆得豆。
下面是实验的代码,第一部分是准备数据集的过程(预处理后写成h5文件),这部分在本地环境进行。第二部分是模型构建和训练过程,这部分在服务器进行(ubuntu16.04, tensorflow-gpu)
第一部分:
(注:有关h5文件读写的工具类也放在了博客里)
# -*- coding: utf-8 -*-
"""
根据LITS_check.py,观察结果
根据肝脏与肿瘤的对比度,将病人分成 3 level
1 level:对比度最高(随机选出两个作为validation集)
2 level: 对比度中等(随机选出两个作为validation集)
3 level:对比度最低
"""
# theshold = 1e-3, total=755
# 81,125作为测试集
level_1 = [0,1,22,23,25,26,27,31,37,46,49,50,55,57,58,59,61,62,
63,64,66,78,79,82,83,90,92,95,99,109,112,124]
#level_1 = [63,64,66,78,79,81,82,83]
# theshold = 1e-3, total= 1345
# 11,110作为测试集
level_2 = [2,7,8,9,10,12,14,15,17,28,35,40,42,
53,56,69,76,93,96,101,111,113,117]
level12 = level_1 + level_2
level12.sort()
test_list = [11,81,110,125]
a = [i for i in range(130)]
level_3 =list(set(a)-set(level_1)-set(level_2))
# sort方法直接改变原列表,无返回值
level_3.sort()
"""
将level_1的其余图片观察,确定是否对比度高
观察后确定窗口值为:[-50,200]
"""
onServer = False
if onServer:
niiSegPath = './LITS17/seg/'
niiImagePath = './LITS17/ct/'
else:
niiSegPath = '~/Documents/LITS17/seg/'
niiImagePath = '~/Documents/LITS17/ct/'
import numpy as np
import SimpleITK as sitk
import matplotlib.pyplot as plt
def getRangeImageDepth(image):
z = np.any(image, axis=(1,2)) # z.shape:(depth,)
#print("all index:",np.where(z)[0])
if len(np.where(z)[0]) >0:
startposition,endposition = np.where(z)[0][[0,-1]]
else:
startposition = endposition = 0
return startposition, endposition
def sample_stack(stack, name="images.png", rows=4, cols=2, start_with=0, show_every=1):
fig,ax = plt.subplots(rows,cols,figsize=[5*cols,5*rows])
if rows==1 or cols==1 :
nums = rows*cols
for i in range(nums):
ind = start_with + i*show_every
ax[int(i % nums)].set_title('slice %d' % ind)
ax[int(i % nums)].imshow(stack[ind],cmap='gray')
ax[int(i % nums)].axis('off')
else:
for i in range(rows*cols):
ind = start_with + i*show_every
ax[int(i/cols),int(i % cols)].set_title('slice %d' % ind)
ax[int(i/cols),int(i % cols)].imshow(stack[ind],cmap='gray')
ax[int(i/cols),int(i % cols)].axis('off')
# 这句话一定要在show之前写,否则show函数之后会创建新的空白图
# plt.savefig(name)
plt.show()
"""
工具函数,左边原图,右边真实分割图
"""
def show_src_seg(srcimg, segimg,index, rows=3,start_with=0, show_every=1):
assert srcimg.shape == segimg.shape
rows = srcimg.shape[0]
plan_rows = start_with + rows*show_every - 1
print("rows=%d,planned_rows=%d"%(rows,plan_rows))
rows = plan_rows if (rows > plan_rows) else rows
cols = 2
print("final rows=%d"%rows)
fig,ax = plt.subplots(rows,cols,figsize=[5*cols,5*rows])
for i in range(rows):
ind = start_with + i*show_every
ax[i,0].set_title('src slice %d' % ind)
ax[i,0].imshow(srcimg[ind],cmap='gray')
ax[i,0].axis('off')
ax[i,1].set_title('truth seg slice %d' % ind