今天帶來的是飛槳開發者技術專家馮嘉駿利用飛槳3D醫療影像分割方案MedicalSeg自主診斷脂肪肝的案例分享,歡迎大家關注~
項目背景
現在人們的日常生活方式和飲食結構發生了巨大的變化,大概就是喫好了,動少了。體內的過量的甘油三酯無法代謝,最終聚集在肝細胞內,導致人體中正常肝臟逐步變成脂肪肝。長期患有脂肪肝可能會導致肝硬化,並最終增加患慢性肝病的風險。醫學影像學領域可以通過CT或者B超技術來判斷患者是否患有脂肪肝,其中CT的脂肪肝檢出比例高於B超,其用於診斷脂肪肝時準確率更高、特異性更強。在CT檢查中,主要是通過計算肝臟與脾臟的CT值的比值來確定患者是否患有脂肪肝以及嚴重程度。根據中華醫學會肝病學分會制定的標準,肝、脾CT比值大於1爲正常肝臟,CT比值在[0.7,1.0]之間爲輕度脂肪肝,[0.5,0.7]之間爲中度脂肪肝,小於0.5爲重度脂肪肝。在日常工作中,放射醫師需要手工選擇肝臟和脾臟的最大層面,在一定範圍內進行 ROI 的選取,之後計算 ROI 範圍內肝臟和脾臟的CT總值以及計算兩者 ROI 範圍內CT總值的比值,從而確定患者是否患有脂肪肝。這個過程需要投入較多的工作量。目前,深度學習技術中的語義分割正被廣泛應用於醫學領域。該技術可以通過訓練模型來預測出影像中的不同組織類型的精確邊界、位置和區域,在腹部CT上自動獲得肝臟和脾臟的分割結果,對肝臟和脾臟多次隨機取出一定體積的立方體來計算CT總值的比值,從而評估被檢測者是否有脂肪肝以及脂肪肝的嚴重程度。這種方式減輕醫生的工作強度,也避免人爲的主觀性帶來的偏差。如下圖展示人工測量和基於語義分割自動測量之間的優劣。關注 AI Studio 項目和我一起討論️
- 項目鏈接
https://aistudio.baidu.com/aistudio/projectdetail/5574909
醫學臨牀上進行診斷時會人爲選擇CT平掃肝、脾顯示最大層面,各選取邊長爲1.0cm以上的正方形 ROI 對肝、脾取CT值。本項目爲了降低隨機選取 ROI 時可能納入肝內血管和僞影部分,影響計算肝脾比值結果的真實性,提出如下解決方法:1.增加隨機取出立方體 ROI 的個數;2.兩兩配對,增加肝脾比值的樣本。
自動分割與評估脂肪肝操作步驟
環境版本要求
數據集介紹
用醫療軟件 itk-snap 軟件讀取原始數據和對應分割標籤,展示效果如下圖:
VNet 模型和醫療分割套件 MedicalSeg
相對於二維語義分割,三維語義分割利用體素的三維結構信息來分割醫學影像,具有更強的抓取空間信息的能力。因爲三維分割利用了周圍鄰近切片及沿着z軸的軸向信息,具有更好的上下文信息,進一步提高了模型的泛化能力。此外,三維語義分割相較二維語義分割也有利於消除噪聲和僞影的影響。當前使用的醫療影像數據具有較高的空間分辨率且爲三維影像數據,因此使用三維語義分割模型進行分割通常是更適合的處理方式。這次使用的三維語義分割模型 VNet 採用了一種自下而上的方法,並且使用了 U 形連接搭建網絡結構,以捕捉到影像數據的不同尺度的細節信息。由於相鄰的體素往往具有密切的相關性,因此 VNet 引入了 3D 卷積、轉置卷積操作以及殘差 U 形連接來捕獲和應用融合上下文信息,從而提高模型的精度和泛化能力。分割醫療影像數據時,由於病變區域可能會佔整個影像的很小一部分,導致標註數據的分佈不均衡,VNet 提出 Dice 損失函數,來減少不平衡分佈對模型訓練的影響,使模型更加穩定。VNet 整體結構如下。
MedicalSeg 是一個簡易、強大、全流程的3D醫學圖像分割工具,作爲 PaddleSeg 分割套件中的分割工具,繼承了 PaddleSeg 配置化訓練的模式,一行代碼實現對醫療數據的處理和模型的訓練,本項目就是基於 MedicalSeg 分割套件實現在腹部CT上對肝臟和脾臟的3D分割。
模型訓練和推理
數據處理
因爲醫療數據較爲特殊,需要進行預處理操作,例如重採樣、像素裁剪,再轉換成 NumPy 格式。需要自定義數據預處理腳本 prepare_SpleenAndLiver.py 。腳本主要設置數據的路徑和像素裁剪等參數設置。設置如下:
1. self.preprocess = {
2. "images": [
3. wrapped_partial(
4. HUnorm, HU_min=-100, HU_max=300),#設置窗寬窗位的裁剪範圍
5. wrapped_partial(
6. resample, new_shape=[128,128,128], order=1)#設置輸入網絡的數據形狀,順序是[z,y,x]
7. ],
8. "labels": [
9. wrapped_partial(
10. resample, new_shape=[128,128,128], order=0),
11. ],
12. "images_test":[
13. wrapped_partial(
14. HUnorm, HU_min=-100, HU_max=300),
15. wrapped_partial(
16. resample, new_shape=[128, 128, 128], order=1)
17. ]
18. }
然後通過一行代碼轉換數據,並按一定比例分割訓練集和驗證集。
1.#運行預處理文件,把SimpleITK文件轉換成numpy文件,生成對應的train.txt和val.txt,和數據參數有關的json文件
2.!python tools/prepare_SpleenAndLiver.py
模型訓練
MedicalSeg 採用配置化訓練,需要新建一個配置化 Yaml 文件,然後再以代碼進行訓練。配置文件主要設置數據的路徑、數據增強方式、優化器、學習率和分割模型等主要參數。不過一般情況下主要設置數據的路徑,其它設置只需要保持默認即可。當訓練效果不好的時候,可以根據經驗對數據增強、學習率等參數進行修改。Yaml 配置如下:
1.data_root: /home/aistudio/work/
2.batch_size: 2 #32GB顯存,shape=256x128x128,batchsize可以設置2
3.iters: 10000 #訓練輪次
4.train_dataset:
5. type: MedicalDataset
6. dataset_root: /home/aistudio/work/SpleenAndLiver_Np #轉換後的Numpy文件路徑
7. result_dir: /home/aistudio/result
8.#設置數據增強
9. transforms:
10. - type: RandomRotation3D #3d選擇
11. degrees: 90
12. - type: RandomFlip3D #水平翻轉
13. mode: train
14. num_classes: 3 #分割類別數
15.val_dataset:
16. type: MedicalDataset
17. dataset_root: /home/aistudio/work/SpleenAndLiver_Np
18. result_dir: /home/aistudio/result
19. num_classes: 3
20. transforms: []
21. mode: val
22. dataset_json_path: "/home/aistudio/work/dataset.json"
23.#設置優化器
24.optimizer:
25. type: sgd
26. momentum: 0.9
27. weight_decay: 1.0e-4
28.#設置學習率
29.lr_scheduler:
30. type: PolynomialDecay
31. decay_steps: 10000
32. learning_rate: 0.05
33. end_lr: 0
34. power: 0.9
35.#設置損失函數
36.loss:
37. types:
38. - type: MixedLoss
39. losses:
40. - type: CrossEntropyLoss
41. - type: DiceLoss
42. coef: [0.3, 0.7]
43. coef: [1]
44.#設置VNet模型參數
45.model:
46. type: VNet
47. elu: False
48. in_channels: 1
49. num_classes: 3
50. pretrained: null
51. kernel_size: [[2,2,4], [2,2,2], [2,2,2], [2,2,2]]
52. stride_size: [[2,2,1], [2,2,1], [2,2,2], [2,2,2]]
配置好 Yaml 文件之後,就可以實現一行代碼進行訓練。
1.!python3 train.py --config /home/aistudio/SpleenAndLiver.yml \
2. --save_dir "/home/aistudio/output/SpleenAndLiver_vent_128" \
3. --save_interval 70 --log_iters 20 \
4. --keep_checkpoint_max 3 \
5. --num_workers 1 --do_eval --use_vdl
模型驗證
訓練完之後,模型驗證也只需要一行代碼即可完成。
1.!python3 val.py --config /home/aistudio/SpleenAndLiver.yml \
2. --model_path /home/aistudio/output/SpleenAndLiver_vent_128/best_model/model.pdparams \
3. --save_dir /home/aistudio/output/SpleenAndLiver_vent_128/best_model
最終通過10000 iters 的訓練,驗證集達到平均 Dice 0.9531,肝臟 Dice0.944 ,脾臟 Dice0.918。整體分割效果達到不錯的精度。
1.2023-03-22 04:57:21 [INFO] [EVAL] #Images: 18, Dice: 0.9531, Loss: 0.097088
2.2023-03-22 04:57:21 [INFO] [EVAL] Class dice:
3.[0.9964 0.9444 0.9183]
模型導出
爲了模型推理,需要導出訓練模型,實現代碼如下。
1.!python export.py --config /home/aistudio/SpleenAndLiver.yml \
2.--model_path /home/aistudio/output/SpleenAndLiver_vent_128/best_model/model.pdparams \
3.--save_dir /home/aistudio/export_model
判斷是否脂肪肝以及脂肪肝的嚴重程度
實現自動判斷脂肪肝,需要對預測的分割結果進行處理。處理步驟如下:
1)對分割結果的每一類保留最大連通域;
2)根據原始圖像的圖像參數,把分割結果轉換成 Nifit 格式;
3)在分割結果中分別訓練肝臟、脾臟的最小邊界範圍;
4)分別在肝臟、脾臟的範圍裏,切割n個立方體;
5)對兩組立方體,兩兩配對,計算兩者的CT比值;
6)對所有兩兩配對的CT比值,求均值,即爲肝臟與脾臟的CT比值;7)根據CT比值對應診斷標準判斷是否脂肪肝以及嚴重程度。部分關鍵代碼如下:
- 保留最大連通域
1.def GetLargestConnectedCompont(binarysitk_image):
2. # 最大連通域提取,binarysitk_image 是掩膜
3. cc = sitk.ConnectedComponent(binarysitk_image)
4. stats = sitk.LabelIntensityStatisticsImageFilter()
5. stats.SetGlobalDefaultNumberOfThreads(8)
6. stats.Execute(cc, binarysitk_image)#根據掩膜計算統計量
7. maxlabel = 0
8. maxsize = 0
9. for l in stats.GetLabels():#掩膜中存在的標籤類別
10. size = stats.GetPhysicalSize(l)
11. if maxsize < size:#只保留最大的標籤類別
12. maxlabel = l
13. maxsize = size
14. labelmaskimage = sitk.GetArrayFromImage(cc)
15. outmask = labelmaskimage.copy()
16. if len(stats.GetLabels()):
17. outmask[labelmaskimage == maxlabel] = 255
18. outmask[labelmaskimage != maxlabel] = 0
19. return outmask
- 縮小肝臟、脾臟的範圍
1.def maskcroppingbox(mask):
2. #尋找mask範圍的3D最大邊界範圍
3. mask_2 = np.argwhere(mask)
4. (zstart, ystart, xstart), (zstop, ystop, xstop) = mask_2.min(axis=0), mask_2.max(axis=0) + 1
5. if zstart- zstop < 0 : zmax,zmin = zstop,zstart
6. if ystart- ystop < 0 : ymax,ymin = ystop,ystart
7. if xstart- xstop < 0 : xmax,xmin = xstop,xstart
8. return zmax,zmin,ymax,ymin,xmax,xmin
- 兩兩配對計算肝臟和脾臟的CT比值
1.#現在有5個肝臟的立方體和5個脾臟的立方體,找出兩個的所有組合,然後計算它們比值
2.tupleNums = list(itertools.product(label[1], label[2]))
3.cts = [tupleNum[0]/tupleNum[1] for tupleNum in tupleNums]
結論
分割預測效果
需要把預測結果轉換成和原始數據的空間、維度、尺度等參數一致的 Nifit 格式醫療數據文件。對預測的分割結果處理後得到的位置映射到原始數據上才能保證取 ROI 計算CT值時,沒有額外的插值。因爲肝臟和脾臟都是一個整體連續的器官,因此預測結果轉換之前,還需對結果只保留最大連通域,去掉小目標。對88例帶標籤的數據,經過以上的處理,再重新計算 Dice 。最終反饋肝臟有0.954mDice,脾臟有0.911mDice。
在臨牀中的現實效果
爲了檢驗“基於3D分割實現自動判斷脂肪肝和嚴重程度”在實際臨牀上的效果,隨機選擇10名病例,分別使用手動測量和自動測量的方法對10名病例進行肝、脾CT值的測量,並使用非參數檢驗方法對結果進行檢驗。測量數據如下:
使用 Mann-Whitney U 檢驗判斷手動測量與自動測量水平是否有差異。根據直方圖判斷兩組測量結果分佈的形狀基本一致。人工測量的中位數爲0.714,自動測量的中位數爲0.740。Mann-Whitney U 檢驗結果顯示,人工測量與自動測量差異無統計學意義(U=48.000,P=0.912)。
項目意義
目前自動判斷脂肪肝的方法大多使用閾值或者直方圖等傳統分割方式。本項目是使用深度學習中三維語義分割來實現肝臟和脾臟的精準分割,再採用多次隨機取三維 ROI 來計算比值。從而減輕了醫生的工作量和主觀性偏差,提高醫療質量和效率。以上就是基於3D分割實現自動判斷脂肪肝和其嚴重程度的相關內容。歡迎大家來我的 AI Studio 頁面互關交流,與我共同體驗飛槳的樂趣!
https://aistudio.baidu.com/aistudio/personalcenter/thirdview/181096