肺炎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