肺炎CT影像分析模型(Pneumonia-CT-LKM-PP)能夠高效地完成對患者CT影像的病竈檢測識別、病竈輪廓勾畫,經過必定的後處理代碼,能夠分析輸出肺部病竈的數量、體積、病竈佔比等全套定量指標。值得強調的是,該系統採用的深度學習算法模型充分訓練了所收集到的高分辨率和低分辨率的CT影像數據,能極好地適應不一樣等級CT影像設備採集的檢查數據,有望爲醫療資源受限和醫療水平偏低的基層醫院提供有效的肺炎輔助診斷工具。python
NOTE: 若是您在本地運行該項目示例,須要首先安裝PaddleHub。若是您在線運行,須要首先fork該項目示例。以後按照該示例操做便可。git
!pip install paddlehub==1.6.0 -i https://pypi.tuna.tsinghua.edu.cn/simple !pip install pydicom -i https://pypi.tuna.tsinghua.edu.cn/simple !pip install nibabel -i https://pypi.tuna.tsinghua.edu.cn/simple !pip install scikit-image==0.15.0 -i https://pypi.tuna.tsinghua.edu.cn/simple
以本示例中dcm_data文件夾下demo.dcm醫學影像爲例。github
關於dcm醫學影像參考:https://baike.baidu.com/item/DICOM/2171358?fr=aladdin算法
# 讀取數據 import os import json import numpy as np from mate.load_input_data import load_input_data from mate.preprocess_lung_part import preprocess_lung_part from lib.threshold_function_module import windowlize_image from lib.png_rw import npy_to_png from lib.judge_mkdir import judge_mkdir import cv2 image_raw, info_dict = load_input_data('./dcm_data')
Begin loading data searching ./dcm_data LKM 3 contains 1 slices Valid imaging slices: 1 Done loading data, runtime: 0.008
# 1. 展現醫學圖像 import matplotlib.pyplot as plt import matplotlib.image as mpimg image = windowlize_image(image_raw, 1500, -500)[0] image = npy_to_png(image) image = (image - float(np.min(image))) / float(np.max(image)) * 255. image = image[np.newaxis, :, :] image = image.transpose((1, 2, 0)).astype('float32') image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) cv2.imwrite('demo.png', image) img = mpimg.imread('demo.png') plt.figure(figsize=(10,10)) plt.imshow(img) plt.axis('off') plt.show()
# 2. 進行肺部分割須要的前處理
lung_part, info_dict = preprocess_lung_part(image_raw, info_dict)
Begin to preprocess data.min(), data.max() -1024 250 data.min(), data.max() 0.0 0.4212963 ---------- begin crop2.5D -------- ---------- end crop2.5D --------
# 3. 進行病竈分割須要的前處理 ww, wc = (1500, -500) lesion_part = windowlize_image(image_raw.copy(), ww, wc) lesion_part = np.squeeze(lesion_part, 0)
lesion_np_path = "lesion_part.npy" lung_np_path = "lung_part.npy" np.save(lung_np_path, lung_part) np.save(lesion_np_path, lesion_part) print('肺部分割輸入:', lung_part.shape) print('病竈分割輸入:', lesion_part.shape)
肺部分割輸入: (1, 320, 320, 3) 病竈分割輸入: (512, 512)
PaddleHub提供了病竈分析和肺部分割的Module,即Pneumonia_CT_LKM_PP,包含病竈分割和肺部分割2個模塊,都是基於UNet進行一系列優化。json
import paddlehub as hub pneumonia = hub.Module(name="Pneumonia_CT_LKM_PP")
[2020-03-23 11:49:36,360] [ INFO] - Installing Pneumonia_CT_LKM_PP module
Downloading Pneumonia_CT_LKM_PP [==================================================] 100.00% Uncompress /home/aistudio/.paddlehub/tmp/tmpiad_bch0/Pneumonia_CT_LKM_PP [==================================================] 100.00%
[2020-03-23 11:49:40,364] [ INFO] - Successfully installed Pneumonia_CT_LKM_PP-1.0.0
PaddleHub對於支持一鍵預測的module,能夠調用module的相應預測API,完成預測功能。數組
input_dict = {"image_np_path": [[lesion_np_path, lung_np_path]] }
results = pneumonia.segmentation(data=input_dict)
[2020-03-23 11:49:45,049] [ INFO] - 0 pretrained paramaters loaded by PaddleHub [2020-03-23 11:49:45,055] [ INFO] - Installing Pneumonia_CT_LKM_PP_lung module
Downloading Pneumonia_CT_LKM_PP_lung [==================================================] 100.00% Uncompress /home/aistudio/.paddlehub/tmp/tmp3pohjqws/Pneumonia_CT_LKM_PP_lung [==================================================] 100.00%
[2020-03-23 11:49:48,510] [ INFO] - Successfully installed Pneumonia_CT_LKM_PP_lung-1.0.0 [2020-03-23 11:49:48,723] [ INFO] - 0 pretrained paramaters loaded by PaddleHub
# 輸出結果包含input_lesion_np_path與output_lesion_np print(results[0])
{'input_lesion_np_path': 'lesion_part.npy', 'output_lesion_np': array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]], dtype=int64), 'output_lung_np': array([[0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], ..., [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0], [0, 0, 0, ..., 0, 0, 0]]), 'input_lung_np_path': 'lung_part.npy'}
以上運行結果中:babel
input_lesion_np_path:存放用於病竈分析的numpy數組路徑工具
output_lesion_np:存放病竈分析結果post
input_lesion_np_path:存放用於肺部分割的numpy數組路徑學習
output_lung_np:存放肺部分割結果
經過必定的後處理,將肺部分割結果映射到原圖上,再將病竈分割和肺部分割融合到一張圖上可視化。
from PIL import Image as PILImage from mate.postprocess_lung_part import postprocess_lung_part from mate.merge_process import merge_process from lib.remove_small_obj_module import remove_small_obj # 將類別轉換爲可視化的像素點值 def get_color_map_list(num_classes): """ Returns the color map for visualizing the segmentation mask, which can support arbitrary number of classes. Args: num_classes: Number of classes Returns: The color map """ color_map = num_classes * [0, 0, 0] for i in range(0, num_classes): j = 0 lab = i while lab: color_map[i * 3] |= (((lab >> 0) & 1) << (7 - j)) color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j)) color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j)) j += 1 lab >>= 3 return color_map color_map_lesion = get_color_map_list(num_classes=2) color_map_lung = get_color_map_list(num_classes=3) lung_part = postprocess_lung_part(results[0]['output_lung_np'], info_dict) lesion_part = results[0]['output_lesion_np'].astype(np.uint8) for i in range(len(lesion_part)): lesion_part[i] = remove_small_obj(lesion_part[i], 10) # 對肺部分割結果和病竈分割結果進行後處理 lung_part, lesion_part = merge_process(image_raw, lung_part, lesion_part)
process lung part post process: 0%| | 0/1 [00:00<?, ?it/s]/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/skimage/morphology/misc.py:211: UserWarning: the min_size argument is deprecated and will be removed in 0.16. Use area_threshold instead. warn("the min_size argument is deprecated and will be removed in " + process lung part post process: 100%|██████████| 1/1 [00:00<00:00, 68.80it/s]
Begin to postprocess Done to postprocess
# 展現肺部分割的圖片,黃色表示右肺,綠色表示左肺 pred_mask = PILImage.fromarray(np.argmax(lung_part, -1)[0].astype(np.uint8), mode='P') pred_mask.putpalette(color_map_lung) pred_mask = pred_mask.convert('RGB') lung_merge_img = np.where(pred_mask, pred_mask, img) fig, axarr = plt.subplots(1, 1, figsize=(10, 10)) axarr.axis('off') axarr.imshow(lung_merge_img)
2020-03-23 11:50:09,206-WARNING: Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage at 0x7f73a0086210>
# 展現病竈分割的圖片,能夠看到左下角非肺部部分存在誤檢。 # 後續咱們與肺部分割相結合,去除誤檢 resmap = results[0]['output_lesion_np'] pred_mask = PILImage.fromarray(resmap.astype(np.uint8), mode='P') pred_mask.putpalette(color_map_lesion) pred_mask = pred_mask.convert('RGB') lesion_merge_img = np.where(pred_mask, pred_mask, img) fig, axarr = plt.subplots(1, 1, figsize=(10, 10)) axarr.axis('off') axarr.imshow(lesion_merge_img)
2020-03-23 11:50:12,670-WARNING: Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage at 0x7f73a0063a90>
#將兩個結果合併,排除非肺部的病竈分割 import json import numpy as np from lib.info_dict_module import InfoDict from mate.save_merged_png_cv2 import save_merged_png_cv2 from lib.judge_mkdir import judge_mkdir
# 融合肺部分割結果和病竈分割結果 image = windowlize_image(image_raw, 1500, -500)[0] image = npy_to_png(image) image = (image - float(np.min(image))) / float(np.max(image)) * 255. lung = lung_part[0,..., 1] + lung_part[0,..., 2] binary = lung * 255 binary = binary.astype(np.uint8) try: _, lung_contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) except: lung_contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) binary = lesion_part[0] * 255 binary = binary.astype(np.uint8) try: _, lesion_contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) except: lesion_contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) image = image[np.newaxis, :, :] image = image.transpose((1, 2, 0)).astype('float32') image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) cv2.drawContours(image, lesion_contours, -1, (0, 0, 255), 2) cv2.drawContours(image, lung_contours, -1, (0, 255, 0), 2) cv2.imwrite('merged.png', image)
True
# 能夠看到此時誤檢的非肺部分已經被去除 img = mpimg.imread('merged.png') plt.figure(figsize=(10,10)) plt.imshow(img) plt.axis('off') plt.show()
# 最後咱們根據預測計算一下病竈佔比,病竈體積,病竈個數 from lib.c1_cal_lesion_percent import cal_lesion_percent from lib.c2_cal_lesion_volume import cal_lesion_volume from lib.c3_cal_lesion_num import cal_lesion_num from lib.c4_cal_histogram import cal_histogram from lib.c5_normal_statistics import normal_statistics def cal_metrics(image_raw, lung_part, lesion_part, spacing_list): """ 進行指標計算 總體流程: 1. 分別獲得左右肺和左右病竈 2. 計算病竈佔比 3. 計算病竈體積 4. 計算病竈個數 5. 計算直方圖 """ print('cal the statistics metrics') # 1. 分別獲得左右肺和左右病竈 lung_l = lung_part[..., 1] lung_r = lung_part[..., 2] lesion_l = lesion_part.copy() * lung_l lesion_r = lesion_part.copy() * lung_r lung_tuple = (lung_l, lung_r, lung_part) lesion_tuple = (lesion_l, lesion_r, lesion_part) # 2. 計算病竈佔比 lesion_percent_dict = cal_lesion_percent(lung_tuple, lesion_tuple) # 3. 計算病竈體積 lesion_volume_dict = cal_lesion_volume(lesion_tuple, spacing_list) # 4. 計算病竈個數 lesion_num_dict = cal_lesion_num(lesion_tuple) # 5. 計算直方圖 hu_statistics_dict = cal_histogram(image_raw, lung_tuple) metrics_dict = { 'lesion_num': lesion_num_dict, 'lesion_volume': lesion_volume_dict, 'lesion_percent': lesion_percent_dict, 'hu_statistics': hu_statistics_dict, 'normal_statistics': normal_statistics } return metrics_dict # 進行指標計算 metrics_dict = cal_metrics(image_raw, lung_part, lesion_part, info_dict.spacing_list)
cal the statistics metrics
# 打印一下各項指標, 'lung_l'爲左肺,'lung_r'爲右肺, 'lung_all'爲兩個肺。 print('病竈個數', metrics_dict['lesion_num']) print('病竈體積', metrics_dict['lesion_volume']) print('病竈佔比', metrics_dict['lesion_percent'])
病竈個數 {'lung_l': 0, 'lung_r': 0, 'lung_all': 0} 病竈體積 {'lung_l': 0.0, 'lung_r': 0.0, 'lung_all': 0.0} 病竈佔比 {'lung_l': 0.0, 'lung_r': 0.0, 'lung_all': 0.0}
如您在運行本教程有任何疑問,能夠經過如下兩種方式提問:
使用AI Studio一鍵上手實踐項目吧:https://aistudio.baidu.com/aistudio/projectdetail/312508