结合PaddleSeg和PyQt制作新冠肺部CT磨玻璃样病灶检测小工具

编程入门 行业动态 更新时间:2024-10-25 12:29:28

结合PaddleSeg和PyQt制作新冠肺部CT磨玻璃样<a href=https://www.elefans.com/category/jswz/34/965749.html style=病灶检测小工具"/>

结合PaddleSeg和PyQt制作新冠肺部CT磨玻璃样病灶检测小工具

PaddleSeg

PaddleSeg是基于飞桨PaddlePaddle开发的端到端图像分割开发套件,涵盖了高精度和轻量级等不同方向的大量高质量分割模型。通过模块化的设计,提供了配置化驱动和API调用两种应用方式,帮助开发者更便捷地完成从训练到部署的全流程图像分割应用。github地址

分割在医疗上广泛应用,例如病灶分割,血管分割等。

背景

新型冠状病毒肺炎(COVID-19)早期可表现磨玻璃样密度影,边界不清,以胸膜下或支气管血管束周围分布为主;病理提示为肺泡腔内和肺泡间隔的炎性渗出。影像表现主要分为结节状、斑片状或片状磨玻璃样密度影,病灶可相对局限或弥漫,随着病情进一步发展,局部合并实变影。


可见快速在肺部CT中检测和定位患者是否有磨玻璃样病灶对鉴别患者是否患有新型冠状病毒起到重要的作用。

现在使用PaddleSeg进行对磨玻璃样病灶进行分割,快速并在原图上找到对应的病灶位置并绘制轮廓,并告诉医生病灶在胸部CT中那一层,大大减轻影像医生的负担。

参考书籍《COVID-19 影像与临床诊断》

最终结果 结合PyQt制作病灶检测小工具

模型检测效果如下图:

结合PyQT制作小工具效果如下图:

小工具使用介绍视频如下

# 解压数据
#数据来源:/,如有版权侵犯,请联系,马上删除
#原本数据是NiFit格式数据,然后转换成png格式,输入PaddleSeg进行训练
!unzip data/data114821/MosMedSegPNG.zip -d /home/aistudio/work
#支持配置化训练和API方式训练
#这里采用API方式进行训练,所以通过pip install paddleseg 安装
!pip install paddleseg SimpleITK
#加载常用库
import os
import random
import numpy as np
import matplotlib.pyplot as plt
from random import shuffle
import cv2
import paddle
import paddleseg

划分数据,生成txt文档

每一行由两个文件路径+空格组成。第一个路径是原始图片的路径,第二路径是mask路径。这里的路径采用绝对路径,这样做会避免在搭建Dataset时填写参数"dataset_root" 与"train_path"的搭配问题。例如采用绝对路径后,就不用关心"dataset_root"的设置。

### 划分数据集
random.seed(1000)
path_origin = '/home/aistudio/work/MosMedSegPNG/origin'
files = list(filter(lambda x: x.endswith('.png'), os.listdir(path_origin)))
random.shuffle(files)
rate = int(len(files) * 0.8)#训练集和测试集8:2
train_txt = open('/home/aistudio/work/MosMedSegPNG/train_list.txt','w')
val_txt = open('/home/aistudio/work/MosMedSegPNG/val_list.txt','w')
for i,f in enumerate(files):image_path = os.path.join(path_origin, f)label_path = image_path.replace("origin", "mask")if i < rate:train_txt.write(image_path + ' ' + label_path+ '\n')else:val_txt.write(image_path + ' ' + label_path+ '\n')
train_txt.close()
val_txt.close()print('完成')
完成

搭建transforms

采用一些简单的数据增强,水平翻转,随机旋转,对比度、明度、随机模糊,随机裁剪等。

import paddleseg.transforms as T
from paddleseg.datasets import Datasettrain_transforms = [T.Resize(target_size=(550, 550)),T.RandomHorizontalFlip(),T.RandomDistort(brightness_range = 0.2,contrast_range = 0.2,saturation_range = 0.2,hue_prob = 0),T.RandomRotation(max_rotation = 10,im_padding_value =(0,0,0),label_padding_value = 0),#随机旋转T.RandomBlur(),T.RandomPaddingCrop(crop_size = (512, 512),im_padding_value = (0,0,0),label_padding_value = 0),T.Normalize()
]
val_transforms = [T.Resize(target_size=(512, 512)),T.Normalize()
]dataset_root = '/home/aistudio/work/MosMedSegPNG'
train_path  = '/home/aistudio/work/MosMedSegPNG/train_list.txt'
val_path  = '/home/aistudio/work/MosMedSegPNG/val_list.txt'
# 构建训练集
train_dataset = Dataset(transforms = train_transforms,dataset_root = dataset_root,num_classes = 2,train_path  = train_path,mode = 'train')
#验证集
val_dataset = Dataset(transforms = val_transforms,dataset_root = dataset_root,num_classes = 2,val_path = val_path,mode = 'val')
#预览经过数据增强后的数据。
plt.figure(figsize=(16,16))
for i in range(1,6,2):img, label = train_dataset[50]img = np.transpose(img, (1,2,0))img = img*0.5 + 0.5plt.subplot(3,2,i),plt.imshow(img,'gray'),plt.title('img'),plt.xticks([]),plt.yticks([])plt.subplot(3,2,i+1),plt.imshow(label,'gray'),plt.title('label'),plt.xticks([]),plt.yticks([])plt.show

搭建训练全流程

模型->损失函数->优化器->超参数->train

模型:采用BiSeNetV2,因为这个模型很轻量,模型文件大概只有9.7MB左右。在部署的时候只有CPU的环境下运行,所以选用它。

损失函数:CrossEntropyLoss和DiceLoss的组合,7:3的比重。

优化器:采用优化器Momentum,学习率策略采用PolynomialDecay,初始化学习率为0.02

超参数:BatchSize为8.一共训练200轮

BiSeNetV2 结构图如下

论文地址:The original article refers to Yu, Changqian, et al. “BiSeNet V2: Bilateral Network with Guided Aggregation for Real-time Semantic Segmentation”

from paddleseg.models import UNet, BiSeNetV2
from paddleseg.core import train
from paddleseg.models.losses import CrossEntropyLoss,DiceLoss, MixedLoss
import paddle
num_classes = 2
model = BiSeNetV2(num_classes=num_classes)# 设置学习率  
batch_size=8
log_iters = int(len(train_dataset)/batch_size /3) #日志打印间隔
iters = int(len(train_dataset)/batch_size) * 200 #训练次数
save_interval = int(len(train_dataset)/batch_size) * 5 #保存的间隔次数
base_lr = 0.02
#优化器和损失函数
lr = paddle.optimizer.lr.PolynomialDecay(base_lr, power=0.9, decay_steps=iters, end_lr=0.0000125)
optimizer = paddle.optimizer.Momentum(lr, parameters=model.parameters(), momentum=0.9, weight_decay=4.0e-5)
mixtureLosses = [CrossEntropyLoss(),DiceLoss() ]
mixtureCoef = [0.7,0.3]
losses = {}
losses['types'] = [MixedLoss(mixtureLosses, mixtureCoef)]*5
losses['coef'] = [1]*5train(model=model,train_dataset=train_dataset,#填写训练集的datasetval_dataset=val_dataset,#填写验证集的datasetoptimizer=optimizer,#优化器save_dir='/home/aistudio/Bisnet',#保存路径iters=iters,batch_size=batch_size,save_interval=save_interval,log_iters=log_iters,losses=losses,#传入 loss函数use_vdl=True)#是否使用visualDL

评估

最后训练结果磨玻璃样病灶的mIou为0.6162,157张图在只用cup的环境下进行推理,时间需要97s。用16gGPU环境下只需要5秒。在医疗数据上,例如肺部CT,假如用薄层数据,可以达到200到300多层,意味着模型需要推理200多张图片,可见在只用CPU环境下进行推理医学图像压力是很大的。需要做很多优化工作。

import paddle
from paddleseg.core import evaluate
from paddleseg.models import BiSeNetV2
model = BiSeNetV2(num_classes=2)
#这是我的训练的模型保存结果路径
model_path = '/home/aistudio/MyBestModel/BiSeNet_model.pdparams'
para_state_dict = paddle.load(model_path)
model.set_dict(para_state_dict)
evaluate(model,val_dataset)
2021-11-09 08:47:56 [INFO]	Start evaluating (total_samples: 157, total_iters: 157)...157/157 [==============================] - 5s 30ms/step - batch_cost: 0.0299 - reader cost: 2.2512e-042021-11-09 08:48:01 [INFO]	[EVAL] #Images: 157 mIoU: 0.8069 Acc: 0.9977 Kappa: 0.9977 
2021-11-09 08:48:01 [INFO]	[EVAL] Class IoU: 
[0.9977 0.6162]
2021-11-09 08:48:01 [INFO]	[EVAL] Class Acc: 
[0.9988 0.78  ](0.8069463282580509,0.9977263189425134,array([0.99771799, 0.61617467]),array([0.99875007, 0.77998328]),0.997726317139097)

对预测结果进行展示

可见模型对磨玻璃样病灶的细节分割并不是很好。但是这个项目的目地只是需要通过预测的mask结果在原图上画出病灶的轮廓,实现对病灶的检测和定位。告诉医疗那一层有磨玻璃样病灶,或者告诉医生那一层出现的磨玻璃样病灶面积是最大的。

import cv2
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import paddle
import paddleseg.transforms as T
from paddleseg.core import infer
from paddleseg.models import UNet,BiSeNetV2def nn_infer(model, im, model_path):# 网络定义para_state_dict = paddle.load(model_path)model.set_dict(para_state_dict)# 预测结果transforms = T.Compose([T.Resize(target_size=(512, 512)),T.Normalize()])img, _ = transforms(im)img = paddle.to_tensor(img[np.newaxis, :])pre = infer.inference(model, img)pred = paddle.argmax(pre, axis=1).numpy().reshape((512, 512))return pred.astype('uint8')params = '/home/aistudio/MyBestModel/BiSeNet_model.pdparams'
model = BiSeNetV2(num_classes=2 )
img_path = '/home/aistudio/work/MosMedSegPNG/origin/study_0271_20.png'
lab_path = '/home/aistudio/work/MosMedSegPNG/mask/study_0271_20.png'img = np.asarray(Image.open(img_path))
lab = np.asarray(Image.open(lab_path))
# 还原大小
pre = cv2.resize(nn_infer(model, img, params), (lab.shape[1], lab.shape[0]), cv2.INTER_NEAREST)
plt.figure(figsize=(15, 10))
plt.subplot(131);plt.imshow(img,'gray');plt.title('image')
plt.subplot(132);plt.imshow(lab,'gray');plt.title('label')
plt.subplot(133);plt.imshow(pre,'gray');plt.title('pre')
plt.show()

对整个医疗数据图像文件Nifit文件进行推理

医学影像数据,一般是Nifit格式或者Dicom格式。是一种三维数据。因为训练过程中采用2D数据训练。最后对医疗影像数据进行推理时,需要一层层进行推理,并把最后结果再堆叠组合成三维数据。

import SimpleITK as sitk
from paddleseg.core import infer
def wwwc(sitkImage,ww=1500,wc=-550):#设置窗宽窗位min = int(wc - ww/2.0)max = int(wc + ww/2.0)intensityWindow = sitk.IntensityWindowingImageFilter()intensityWindow.SetWindowMaximum(max)intensityWindow.SetWindowMinimum(min)sitkImage = intensityWindow.Execute(sitkImage)return sitkImagedef readNii(path,isflipud=True):"""读取和加载数据"""img = wwwc(sitk.ReadImage(path))data = sitk.GetArrayFromImage(img)#图像是上下翻转的,所有把他们翻转过来if isflipud:data = np.flip(data,1)return datadef nn_infer(model, im):# 预测结果transforms = T.Compose([T.Resize(target_size=(512, 512)),T.Normalize()])img, _ = transforms(im)img = paddle.to_tensor(img[np.newaxis, :])pre = infer.inference(model, img)pred = paddle.argmax(pre, axis=1).numpy().reshape((512, 512))return pred.astype('uint8')
#原始医疗数据的文件路径,格式Nifit
origin_f_path = '/home/aistudio/study_0306.nii'
#通过SimpleITK读取,并设置窗宽窗位并缩放到0~255之间,并转换成numpy格式
origin_numpy = readNii(origin_f_path).astype(np.uint8)
d,h,w = origin_numpy.shape
#用来保存结果。
result = np.zeros((d,h,w,3)).astype(np.uint8)
mask_numpy = np.zeros_like(origin_numpy).astype(np.uint8)model_path = '/home/aistudio/MyBestModel/BiSeNet_model.pdparams'
model = BiSeNetV2(num_classes=2 )
para_state_dict = paddle.load(model_path)
model.set_dict(para_state_dict)for i in range(d):img = origin_numpy[i].copy()img = np.expand_dims(img, axis=2)img = np.concatenate((img, img, img), axis=-1).astype(np.uint8)pre = cv2.resize(nn_infer(model, origin_numpy[i]), (512,512), cv2.INTER_NEAREST)mask_numpy[i] = preret,thresh = cv2.threshold(pre,0,255,cv2.THRESH_BINARY)thresh = cv2.dilate(thresh, kernel=np.ones((5, 5), np.uint8), iterations=1)contours, hierarchy = cv2.findContours(thresh, 1, 2)# 这是画轮廓img = cv2.drawContours(img, contours, -1, (0, 255, 0), 2)result[i] =  img#把预测出来的mask保存成三维的Nifit格式。
mask_numpy = np.flip(mask_numpy, 1)
pre_sitkImage = sitk.GetImageFromArray(mask_numpy)
pre_sitkImage.CopyInformation(sitk.ReadImage(origin_f_path))
pre_sitkImage = sitk.Cast(pre_sitkImage, sitk.sitkUInt8)
save_path =origin_f_path.split('.')[0] + '_mask.nii'
sitk.WriteImage(pre_sitkImage, save_path)plt.figure(figsize=(15, 15))
plt.subplot(221);plt.imshow(result[20])
plt.subplot(222);plt.imshow(result[22])
plt.subplot(223);plt.imshow(result[24])
plt.subplot(224);plt.imshow(result[26])
plt.show()

把预测出来的maskNifit格式与原始数据格式,用itk-SNAP叠加读取打开查看

最后结合PyQT制作磨玻璃病灶检测小工具

如图

小工具的代码在项目文件游览器中,需要的可以下载试试,因为是花了一天临时写的,所以假如出现各种各样的问题,请轻喷

请点击此处查看本环境基本用法.

Please click here for more detailed instructions.

更多推荐

结合PaddleSeg和PyQt制作新冠肺部CT磨玻璃样病灶检测小工具

本文发布于:2024-03-15 07:00:42,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1738341.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:病灶   肺部   小工具   玻璃   PaddleSeg

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!