本文主要是介绍医疗图像切割日记 01 - 用nnUNet做Fatal Head Segmentation Challenge,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
1. nnUNet 介绍
1.1 nnUNet 特点
2. 准备工作
2.1 整理数据格式 - 用自己的数据集
2.2 设置路径
2.3 nnUNet预处理数据
3. 训练
3.1 训练代码
编辑编辑
3.2 训练结果
3.2.1 骰子系数 - 最差结果 - Worst Cases编辑编辑
3.2.2 骰子系数 - 最优结果 - Best Cases编辑编辑模型分析
4. 优化
4.1 数据预处理
4.2 优化结果 - 图片
4.2.1 骰子系数 - 最差结果 - Worst Cases编辑编辑
4.2.2 骰子系数 - 最优结果 - Best Cases编辑编辑
4.3 优化结果 - 数字
4.3.1 骰子
4.3.2 豪斯多夫
4.3.3 周长误差 (Circumference Error in mm)
5. 模型分析
5.1 索伦森-骰子系数 (Dice Distance)
5.2 豪斯多夫距离 (Hausdorff Distance)
5.3 代码实现
6. 总结
总结(节俭版)
发的第一篇文,有很多要改进的地方。求各路大神多多指引
费时5天完成的一个Segmentation挑战,之前没有做过segmentation所以走了一点点弯路
nnUNet的github链接 - GitHub - MIC-DKFZ/nnUNet
nnUNet的文章链接- -nnU-Net: a self-configuring method for deep learning-based biomedical image segmentation | Nature Methods
Fatal Head Segmentation Challenge的链接 - Home - Grand Challenge
*部分代码非原创,会标注出处以及链接*
1. nnUNet 介绍
nnUNet是一款可以自己调整数据预处理,模型架构,训练,和后处理的深度学习医疗图片分割模型。在没有人工调参的情况下,nnUNet 在23个图形切割挑战上得到了最优的分数/前沿的效果。作者把nnUNet开源,让更多人,不论背景,都能轻松的接触到医疗图形切割的领域。
1.1 nnUNet 特点
- 衡量标准:nnUNet 在很多数据集都达到了最优的结果。作者表示nnUNet可以算是行业标杆,让其他用户的模型和nnUNet做准确率比对。(咱也不敢问,咱也不敢说,大佬说什么都对)
- 开箱即食:nnUNet 是第一个“开箱即食”的先进图像分割系统。没有segmentation经验的用户也可以(相对)流畅的使用nnUNet来做图像分割的任务。
- 框架:nnUNet的模块化框架可以让用户替换/优化部分,可以有效的看到自己优化的效果。
2. 准备工作
2.1 整理数据格式 - 用自己的数据集
nnUNet 对数据的格式要求比较高,是根据Medical Segmentation Decathlon (MSD)的格式。
nnUNet_raw_data_base/nnUNet_raw_data/
├── Task001_BrainTumour
├── Task002_Heart
├── Task003_Liver
├── Task004_Hippocampus
├── Task005_Prostate
├── ...~/Task001_BrainTumour/
├── dataset.json
├── imagesTr
├── (imagesTs)
└── labelsTr
详细转换方法请借鉴作者 github:nnUNet/dataset_conversion.md at master · MIC-DKFZ/nnUNet · GitHub
2.2 设置路径
nnUNet的初衷是能让每个数据集都能用这个模型跑,所以环境设置很重要!我是在colab上面跑的,如果要本地跑的话需要修改。这里有三个环境设置:
- Raw database - 源数据路径
- Preprocessed - 处理后的数据路径
- Results Folder - 结果储存路径
os.environ['nnUNet_raw_data_base'] = "content/raw_data_base"
os.environ['nnUNet_preprocessed'] = "content/raw_data_base/nnUNet_preprocessed"
os.environ["RESULTS_FOLDER"] = "content/raw_data_base/results"
2.3 nnUNet预处理数据
在训练nnUNet之前需要用他们的代码预处理一下数据,代码如下:
nnUNet_plan_and_preprocess -t XXX --verify_dataset_integrity
这里nnUNet_plan_and_preprocess是命令,XXX是自定义的任务编号,建议设置在500+以防和现有模型冲突。如果这代码跑出来没问题就可以开始训练了。
3. 训练
3.1 训练代码
!nnUNet_train 2d nnUNetTrainerV2 555 0
!nnUNet_train 2d nnUNetTrainerV2 555 1
!nnUNet_train 2d nnUNetTrainerV2 555 2
!nnUNet_train 2d nnUNetTrainerV2 555 3
!nnUNet_train 2d nnUNetTrainerV2 555 4
#nnUNet_train 命令
#2d 跑2d nnUNet
#nnUNetTrainerV2 基础nnUNet
#555 任务编号
#0/1/2/3/4 多重编号
#!nnUNet_train 2d nnUNetTrainerV2 555 4 -c
#Add -c to continue train from the last check point
3.2 训练结果
3.2.1 骰子系数 - 最差结果 - Worst Cases
3.2.2 骰子系数 - 最优结果 - Best Cases
模型分析
第一次跑出来的结果差强人意,并没有想象中能圈出预测的范围。绝大部分的预判结果都不是一个圈。大概想了一下导致这个的的原因,因为模型读到的mask是一个圈,他不会去学习圈圈中间的内容,只会去预判那个圈。这个方法的缺点就是在模型不确定的时候会不预判任何东西,导致我们不知道哪里出了问题。
4. 优化
4.1 数据预处理
这里数据做一个简单的数据增强,就是把这个圈圈填上。
*在自己处理的时候遇到了一些小问题所以联系了nnUNet的作者,Fabian在3天内就把图像预处理的代码做出来发布在github上了(发的时候bug已解决)*
Fabian 提供的官方代码链接:nnUNet/Task218_HC18.py at master · MIC-DKFZ/nnUNet · GitHub
4.2 优化结果 - 图片
填完之后再跑一次数据格式处理,如何再训练一次。得到的结果如下。
4.2.1 骰子系数 - 最差结果 - Worst Cases
4.2.2 骰子系数 - 最优结果 - Best Cases
4.3 优化结果 - 数字
4.3.1 骰子
The 7 cases with the worst results are:
023_2HC.png - 0.34406
022_2HC.png - 0.55531
024_HC.png - 0.59306
031_HC.png - 0.63960
009_HC.png - 0.65774
094_HC.png - 0.65859
007_HC.png - 0.74304
032_2HC.png - 0.75615
====================================
The 7 cases with the best results are:
037_HC.png - 0.98398
080_HC.png - 0.98411
054_HC.png - 0.98487
039_HC.png - 0.98496
061_HC.png - 0.98595
068_HC.png - 0.98621
072_HC.png - 0.98723
040_HC.png - 0.98864
The model achieved an average accuracy score of: 0.9255711501147182
The highest Accuracy in the batch: 0.9886378848728247
The lowest Accuracy in the batch: 0.34406099681349833
4.3.2 豪斯多夫
The 7 cases with the worst results are:
052_HC.png - 53.00000
002_HC.png - 73.23933
032_2HC.png - 96.89685
046_HC.png - 151.71355
012_HC.png - 229.80426
083_2HC.png - 279.57111
023_2HC.png - 351.10254
094_HC.png - 434.88504
====================================
The 7 cases with the best results are:
037_HC.png - 0.00000
011_HC.png - 1.00000
048_HC.png - 1.00000
054_HC.png - 1.00000
066_2HC.png - 1.00000
068_HC.png - 1.00000
072_HC.png - 1.00000
076_HC.png - 1.00000
The model achieved an average accuracy score of: 21.673193653752982
The longest Haussdorf Distance in the batch: 434.8850402832031
The shortest Haussdorf Distance in the batch: 0.0
4.3.3 周长误差 (Circumference Error in mm)
周长误差是算基准周长和预判结果的周长差距(单位为mm)
The average error in mm is: 11.292108091481538
The file set with the maximum error is: 083_2HC.png, with an error of 84.41819 mm
The file set with the minimum error is: 006_HC.png, with an error of 0.18754 mm
计算下来,平均误差是11.29mm
测试集中,最大的误差是84.42mm
测试集中,最小的误差是0.19mm
5. 模型分析
5.1 索伦森-骰子系数 (Dice Distance)
筛子系数是2乘以模型结果和基准的结合除以两个图片的像素总值
两个公式是正相关的,比如如果模型A的骰子系数比模型B的骰子系数高,这么模型A的IoU系数比也会比模型B的IoU系数高
5.2 豪斯多夫距离 (Hausdorff Distance)
豪斯多夫距离是在度量空间中任意两个集合之间定义的一种距离。 设X和Y是度量空间M的两个真子集,那么豪斯多夫距离dH(X,Y)是最小的数r使得X的闭r—邻域包含Y,Y的闭r—邻域也包含X
-这段来于百度百科
5.3 代码实现
def get_Dice_list(resList, predPath, gtPath):for file in os.listdir(predPath):path = predPath + fileif os.path.isfile(path):predicted = predPath + filegroundTruth = gtPath + fileacc = diceScore(predicted, groundTruth)resList.append(acc)else:passreturn(resList)
def get_Haussdorf_list(listHaussdorf, predPath, gtPath):
"""
Calculate the Haussdorf distance between the results and the ground truthand returning them in a list.
list listHaussdorf - Haussdorf result list
str gtPath - Path to the ground truth masks
str predPath - Path to the predicted masks计算模型跑出来的结果和基准的Haussdorf值,返回一个数值列表
list listHaussdorf - Haussdorf 结果列表
str gtPath - 基准路径
str predPath - 预测结果路径
"""for file in os.listdir(predPath): #Iterate through Result Path path = gtPath + fileif os.path.isfile(path): predicted = predPath + filegroundTruth = gtPath + file# 1.Import the Photos# 1.导入图片img_cs1 = cv2.imread(predicted)img_cs2 = cv2.imread(groundTruth)# 2.Get the contours of both images# 2.获取图片连通域try:cnt_cs1 = get_contours(img_cs1)cnt_cs2 = get_contours(img_cs2)hausdorff_sd = cv2.createHausdorffDistanceExtractor()# 3.Calculate the distance between the contours# 3.计算轮廓之间的距离d3 = hausdorff_sd.computeDistance(cnt_cs2, cnt_cs1)listHaussdorf.append(d3)except IndexError:listHaussdorf.append(999999) return listHaussdorfdef get_contours(img):"""Get contours of the imageimg: Image generated from the model and ground truthReturns image of contour获取连通域:param img: 输入图片:return: 最大连通域"""img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Grayscale 灰度化ret, img_bin = cv2.threshold(img_gray, 200, 255, cv2.THRESH_BINARY)# Binary and set threshold 二值化, 连通域分析contours, hierarchy = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)return contours[0]#The contour calculation code is adapted from https://www.pythonf.cn/read/57691
#连通区域计算代码来源于: https://www.pythonf.cn/read/57691
6. 总结
nnUNet是一个非常容易调用的深度学习模型,很多初学者可以拿他入手做一些有趣的项目。例如肿瘤检测: Task 029 - nnUNet/Task029_LiverTumorSegmentationChallenge.py at master · MIC-DKFZ/nnUNet · GitHub,马路检测: Task 120 - MassGIS Data: Massachusetts Department of Transportation (MassDOT) Roads | Mass.gov
总结(节俭版)
Fabian nb
nnUNet 代码 | GitHub - MIC-DKFZ/nnUNet |
nnUNet 文献 | nnU-Net: a self-configuring method for deep learning-based biomedical image segmentation | Nature Methods |
Fatal Head Segmentation 链接 | Home - Grand Challenge |
数据链接 | Automated measurement of fetal head circumference using 2D ultrasound images | Zenodo |
豪斯多夫 - 代码 | Python+opencv2(I)豪斯多夫距离,PythonOpencv2,一,Hausdorff |
推荐nnUNet文章 | (四:2020.07.28)nnUNet最舒服的训练教程(让我的奶奶也会用nnUNet(上))(21.04.20更新)_花卷汤圆的博客-CSDN博客_nnunet |
Colab 代码链接 |
×注-Colab当时写的有点急促,这一段忙完了会加备注后发布,如果需要跑的话欢迎私信。也欢迎各路大佬评论留言多提建议!谢谢~
这篇关于医疗图像切割日记 01 - 用nnUNet做Fatal Head Segmentation Challenge的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!