Advertisement

深度学习实战肺部医疗图像分割【U-Net模型+肺部CT数据集+PyQt5界面】

阅读量:
在这里插入图片描述

U-Net肺医疗图像分割

文章目录

  • 研究背景

  • 一、效果演示

    • 环境配置安装教程与资源说明
  • 二、整体流程

    • 2.1 数据准备
    • 2.2 模型设计
    • 2.3 模型训练
    • 2.4 模型评估
    • 2.5 模型推理
    • 2.6 部署和应用
  • 第三章 U-Net 图像分割原理

    • 第三章第一节 基本概念
      • 第三章第二节 U-Net 原理

        • 第三章第二节第一小节 编码器(压缩模块)
          • 第三章第二节第一小节 编码器(压缩模块)
      • 第三章第二节第二小节 解码器(重建模块)

      • 输出层

        • 3.2.2 工作原理
          • 编码阶段
      • 解码阶段

        • 3.2.3 跳跃连接的作用
        • 3.2.4 损失函数和优化
      • 3.3 U-Net具体实现

        • 3.3.1 编码器
        • 3.3.2 解码器
        • 3.3.3 U-Net
      • 3.4 肺部CT数据集

      • 3.5 U-Net模型评估

        • 3.5.1 定量评估
        • 3.5.2 计算评估指标
      • 3.6 U-Net分割界面设计

      • 3.7 U-Net分割预测

      • 参考文献

      • 结束语


研究背景

随着计算机的发展与硬件技术的进步,在人们处理实际生活与工作中的问题时也逐渐形成了不同的思维方式,在过去需要用巨额人力财力才能完成的工作如今可以直接由软件来完成。机器学习以及计算机视觉则是当前最为重要的研究前沿领域之一

目前采用的主流治疗方法包括但不限于手术治疗、介入治疗以及中药治疗等多种方法,在这之中, 肺切除术和肝移植术仍然是当前最为标准的根治手段

医学影像技术主要包含计算机断层扫描(Computer tomography, CT)、核磁共振成像(Magnetic resonance imaging, MRI)、放射性核素成像(Radionuclide Imaging, RNI)、超声成像(Ultrasound)以及正电子发射断层扫描(Positron emission tomography, PET)等多个分支领域。这些技术和方法已成为现代医学领域的重要诊疗手段之一,在医疗实践中用于疾病筛查、诊断以及治疗等方面发挥着重要作用。其中CT成像技术因其具备密度分辨率高、空间分辨率高以及组织结构影像无重叠等多种优势特性,在肺部疾病的诊断中得到了广泛应用。

在完成腹部CT成像后,则必须先在CT图中标注出肺部区域。
通过精确分析腹部CT影像确定并分离出肺脏区域。
这一过程作为开展胸部影像学诊断的重要步骤之一,在实际应用中,则必须由具备相关专业知识和丰富临床经验的专业医师对各个部位(如心脏、肝脏、脾脏等)的具体位置进行标注。
不同医师的经验和主观偏好会导致同一标得分存在较大差异。
因此为了提高效率并减少工作负担的同时还能确保一致性结果,
利用当前先进的人工智能技术实现自动化医学图像分割具有重要意义。


一、效果演示

本文构建的肺部图像分割采用U-Net模型,PyQt5构建界面交互。

在这里插入图片描述

环境配置安装教程与资源说明

离线安装配置文件说明

二、整体流程

U-Net是一种被广泛应用在医学影像分割领域的深度学习模型,在该领域的应用效果尤为突出。具体而言,在进行肺部影像分割时展现出了显著的优势。以下将详细阐述其工作原理和实现细节

2.1 数据准备

  • 数据采集:采集涵盖肺部影像的医学影像数据库(如CT扫描或X射线照片),确保所收集的所有样本均包含完整的标注信息。
    • 标准化处理:对获取的所有样本进行标准化处理(Standardization),使每个样本的最大值不超过1且最小值不低于0。
    • 尺寸优化:对原始图片进行尺寸优化(Resizing),使其符合特定算法的需求。
    • 多样性增强:通过旋转(Rotation)、平移(Translation)和翻转(Flipping)等方式进行多样化处理(Diversification),从而提升模型泛化能力。

2.2 模型设计

  • U-Net架构
  • 编码器模块(下采样路径):包含多个卷积层和池化操作序列,用于从输入图像中提取多层次的特征表示,并逐步减少图像的空间分辨率。
  • 解码器模块(上采样路径):通过反卷积层或插值操作重建图像的空间分辨率,并结合卷积层进行特征融合。
  • 跳跃连接机制:在编码与解码模块之间建立直接联系,在目标检测等任务中有效提升模型性能。

2.3 模型训练

  • 损失函数

    • 常用的损失函数包括二值交叉熵损失(Binary Cross-Entropy Loss)和Dice损失(Dice Loss),用于衡量预测分割与真实分割之间的差异。
  • 训练工具

  • 常见的训练工具包括Adam和SGD等算法,这些方法旨在通过更新模型参数来最小化损失函数。

  • 训练流程

  • 将数据集划分为训练集与验证集。

  • 经过多轮循环迭代的训练过程,在每一轮中通过正向传播和反向传播的方式更新模型参数。

  • 每隔一个epoch周期后对模型在验证集上的性能进行评估,并根据测试结果调整超参数以实现最佳模型性能。

2.4 模型评估

  • 评估标准
  • Dice指数用于评估预测结果与实际结果之间的重叠情况。
  • **交集率(IoU)**则反映了预测区域与实际区域在重叠部分所占的比例。
  • 精确率和召回率分别从不同的维度量化模型的表现效果。

2.5 模型推理

  • 图像输入:将待分割的肺部影像输入经过训练的U-Net架构。
  • 图像分割:模型输出概率图谱,其中每个像素对应属于目标区域(肺部)或背景区域的可能性。
  • 后处理:首先对概率图进行阈值化处理以获得二值化分割结果;随后根据实际需求可进一步执行连通域分析以去除噪声和伪影。

2.6 部署和应用

  • 模型部署 :向各个应用场景投用训练好的模型,并主要适用于服务器端运行、云端部署以及嵌入式设备集成使用。
    • 实时应用 :即时接收并分析患者的胸部X光片,并完成图像分割处理以便辅助医生进行疾病判断和医学研究。

三、U-Net图像分割原理

3.1 概述

作为一种专门用于图像分割的技术方案,
U_{\text{net}} 以其显著的特点使其在医学影像领域展现出极高的实用性,
从而使得其成为医学成像领域中最常用的分割工具之一。
这一技术不仅广泛应用于CT扫描、MRI以及X光检查等常规成像手段,
同时也拓展到了显微镜等新兴应用场景。
尽管它主要用于细分领域的操作,
但也有实例展示了其在其他应用场景中的应用。
鉴于该技术仍具备巨大的发展潜力,
对此进行了全面而深入的研究分析。
研究不仅探讨了 U_{\text{net}} 在深度学习框架下的体系结构发展,
还对其所面临的挑战与突破进行了详尽阐述。

得益于过去十年来计算机视觉与深度学习领域的最新突破,在医学图像分析方面得到了越来越多的关注。与此同时,在多个领域快速推进基于计算机视觉与深度学习的技术发展仍面临诸多挑战,在医学影像领域

U-Net是一种主要用于图像分割的关键神经网络架构[1]。其基本架构包含两条核心路径:第一条(收缩部分)类似于传统卷积神经网络(CNN),负责提取并传递分类信息;第二条(扩张部分)则通过上采样和与收缩部分特征融合来增强细节捕捉能力,并提升输出分辨率直至生成完整分割结果。这种扩张机制使得模型能够学习到局部化的分类信息,并在每个层次上构建多尺度特征表示。此外,在生成模型时会保留这一对称性特点使其呈现出类似字母U的结构特征主流的任务就是将整个图像归类到单一标签中然而,在医学影像分析中这类简单的全局分类方法往往无法满足对像素级上下文信息的需求因此存在明显的局限性

3.2 U-Net原理

在这里插入图片描述

U-Net被广泛认为是一种卷积神经网络(CNN),专为生物医学图像分割任务而设计。其独特的架构使其能够在小规模数据下实现精准分割,并生成可靠的结果。以下是其框架详细原理说明:U-Net架构主要由 Contracting Path 和 Expanding Path 两个核心组件构成,在 Contracting Path模块中,通过卷积操作提取图像的空间特征并逐步降低图像的空间分辨率;Expanding Path模块则通过解码器组件将特征图逐步放大至与输入图像尺寸一致,并结合分类或分割任务进行预测;各组件内部均集成多层卷积层设计以进一步提升模型性能。

3.2.1 U-Net的基本结构

U-Net架构由两个核心模块组成:编码器(收缩路径)和解码器(扩展路径)。在连接处进行特征融合。

编码器(收缩路径)
  • Convolutional Layer:每一级的卷积模块均采用大小为3\times 3的小核矩阵执行卷积运算,并通过ReLU非线性激活机制对输出结果进行处理。
  • Pooling Layer:系统通过基于最大值的2\times 2池化模块对提取到的特征图执行降采样处理,在降低空间分辨率的同时能够有效保留关键特征信息。
  • Multi-layer Structure:编码器系统由多个逐级连接的Convolutional Layer和Pooling Layer组成,在每经过一次降采样处理后其输出的空间分辨率会减半而通道数量则会翻倍。
解码器(扩展路径)
  • 上采样层:通过转置卷积或专门的上采样操作,在不改变参数数量的前提下实现对原始空间分辨率的有效复现。
  • 卷积层:经过两个连续的3×3卷积模块并配合ReLU激活函数,在深度学习模型中逐步提升特征表示能力。
  • 跳跃连接:在模型架构设计中,在编码器各层级与解码器相应层级之间建立直接连接关系,并对编码器提取出的高分辨率特征求助于与解码器提取出的低分辨率特征求助于拼接融合处理以提升重建效果
输出层

最后一个卷积层采用1×1尺寸的卷积核进行处理,在目标类别数量(对于二分类任务即为一类)上降低了特征图的高度,并通过应用Sigmoid或Softmax激活函数计算每个像素所属类别的概率

3.2.2 工作原理

U-Net捕获图像中各层次细节特征的过程主要依赖于其多层次卷积与下采样机制。在重建图像分辨率的过程中,网络借助于上采样操作与跳跃式信息传递机制逐步修复空间细节。最终能够达到精细到像素级别的目标分类效果。

编码阶段
  • 通过多层卷积和下采样操作(如池化),输入图像能够提取出多层次的空间特征。
  • 每次下采样(池化)操作后,在空间维度上图像尺寸减半,在通道数量上则翻倍。
  • 编码器最终生成一个具有高维表示但空间分辨率较低的空间特征图。
  • 该特征图能够有效捕获整体场景中的视觉信息。
解码阶段
  • 通过系统性地进行上采样操作来逐步恢复特征图的原始尺寸。
  • 每一次上采样后,在当前特征图中融合更高分辨率的编码器相应层特征图。
  • 通过跳跃连接整合多尺度信息。
  • 该种方法能够有效结合全局上下文信息,并精准捕捉局部细节特征。
  • 该方法显著提升了分割精度。

3.2.3 跳跃连接的作用

跳跃连接(skip connections)是U-Net体系中的核心组件之一。通过直接传输编码器各层的特征图至解码器对应层级的位置,该技术有效防止了特征信息的损失,并使模型能够更加精确地重建图像细节。具体而言,这种机制不仅确保了空间分辨率的有效传递,在重建边缘和纹理等细节时表现尤为突出。

  • 信息保持 :确保在编码过程中不会遗漏任何详细细节。
  • 梯度传播机制 :能够有效缓解梯度消失的问题,并促进模型训练效率的提升。
  • 多层次特征融合 :通过整合多层次的信息来提升模型识别复杂边界和捕捉小型目标的能力。

3.2.4 损失函数和优化

  • 损失函数 :U-Net主要采用交叉熵损失函数或Dice系数损失函数作为其损失计算的核心工具。具体而言,在处理二分类问题时,默认采用二值交叉熵作为目标函数;而对于多分类场景,则倾向于使用多类交叉熵作为评价标准。
    • 优化器 :在训练过程中,常用的优化器有Adam和SGD两种主要选择。其中Adam优化器因其收敛速度快且稳定的优势,在U-Net模型中得到了广泛应用。

3.3 U-Net具体实现

3.3.1 编码器

复制代码
    class encoder(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(encoder, self).__init__()
        self.down_conv = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
        # ceil_mode参数取整的时候向上取整,该参数默认为False表示取整的时候向下取整
        self.pool = nn.MaxPool2d(kernel_size=2, ceil_mode=True)
    
    def forward(self, x):
        out = self.down_conv(x)
        out_pool = self.pool(out)
        return out, out_pool
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

3.3.2 解码器

复制代码
    class decoder(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(decoder, self).__init__()
        # 反卷积
        self.up = nn.ConvTranspose2d(in_channels, out_channels, kernel_size=2, stride=2)
        self.up_conv = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )
    
    def forward(self, x_copy, x, interpolate=True):
        out = self.up(x)
        if interpolate:
            # 迭代代替填充, 取得更好的结果
            out = F.interpolate(out, size=(x_copy.size(2), x_copy.size(3)),
                                mode="bilinear", align_corners=True
                                )
        else:
            # 如果填充物体积大小不同
            diffY = x_copy.size()[2] - x.size()[2]
            diffX = x_copy.size()[3] - x.size()[3]
            out = F.pad(out, (diffX // 2, diffX - diffX // 2, diffY, diffY - diffY // 2))
        # 连接
        out = torch.cat([x_copy, out], dim=1)
        out_conv = self.up_conv(out)
        return out_conv
    
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

3.3.3 U-Net

复制代码
    class BaseModel(nn.Module):
    def __init__(self):
        super(BaseModel, self).__init__()
        self.logger = logging.getLogger(self.__class__.__name__)
    
    def forward(self):
        raise NotImplementedError
    
    def summary(self):
        model_parameters = filter(lambda p: p.requires_grad, self.parameters())
        nbr_params = sum([np.prod(p.size()) for p in model_parameters])
        self.logger.info(f'Nbr of trainable parametersL {nbr_params}')
    
    def __str__(self):
        model_parameters = filter(lambda p: p.requires_grad, self.parameters())
        nbr_params = sum([np.prod(p.size()) for p in model_parameters])
        return super(BaseModel, self).__str__() + f"\nNbr of trainable parameters: {nbr_params}"
    
    
    class UNet(BaseModel):
    def __init__(self, num_classes, in_channels=1, freeze_bn=False, **_):
        super(UNet, self).__init__()
        self.down1 = encoder(in_channels, 64)
        self.down2 = encoder(64, 128)
        self.down3 = encoder(128, 256)
        self.down4 = encoder(256, 512)
        self.middle_conv = nn.Sequential(
            nn.Conv2d(512, 1024, kernel_size=3, padding=1),
            nn.BatchNorm2d(1024),
            nn.ReLU(inplace=True),
            nn.Conv2d(1024, 1024, kernel_size=3, padding=1),
            nn.ReLU(inplace=True)
        )
    
        self.up1 = decoder(1024, 512)
        self.up2 = decoder(512, 256)
        self.up3 = decoder(256, 128)
        self.up4 = decoder(128, 64)
        self.final_conv = nn.Conv2d(64, num_classes, kernel_size=1)
        self._initalize_weights()
        if freeze_bn:
            self.freeze_bn()
    
    def _initalize_weights(self):
        for module in self.modules():
            if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear):
                nn.init.kaiming_normal_(module.weight)
                if module.bias is not None:
                    module.bias.data.zero_()
            elif isinstance(module, nn.BatchNorm2d):
                module.weight.data.fill_(1)
                module.bias.data.zero_()
    
    def forward(self, x):
        x1, x = self.down1(x)
        x2, x = self.down2(x)
        x3, x = self.down3(x)
        x4, x = self.down4(x)
        x = self.middle_conv(x)
        x = self.up1(x4, x)
        x = self.up2(x3, x)
        x = self.up3(x2, x)
        x = self.up4(x1, x)
        x = self.final_conv(x)
        return x
    
    def get_backbone_params(self):
        # There is no backbone for unet, all the parameters are trained from scratch
        return []
    
    def get_decoder_params(self):
        return self.parameters()
    
    def freeze_bn(self):
        for module in self.modules():
            if isinstance(module, nn.BatchNorm2d):
                module.eval()
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

3.4 肺部CT数据集

在这里插入图片描述

3.5 U-Net模型评估

为了验证U-Net模型在图像分割任务中的有效性与鲁棒性,其评估被视为一项关键步骤.一般而言,评估过程涉及对定量评估指标进行计算以及定性结果进行分析.以下将详细阐述U-Net模型的完整评估流程:

3.5.1 定量评估

在这里插入图片描述

3.5.2 计算评估指标

用于评估模型性能

在Keras中能够提供选项

复制代码
    import numpy as np
    from sklearn.metrics import f1_score, jaccard_score
    
    # Example function to calculate Dice coefficient
    def dice_coefficient(y_true, y_pred):
    smooth = 1e-6
    y_true_f = np.ravel(y_true)
    y_pred_f = np.ravel(y_pred)
    intersection = np.sum(y_true_f * y_pred_f)
    return (2. * intersection + smooth) / (np.sum(y_true_f) + np.sum(y_pred_f) + smooth)
    
    # Calculate Dice coefficient for validation set
    dice_scores = [dice_coefficient(true, pred) for true, pred in zip(val_masks, val_predictions)]
    average_dice = np.mean(dice_scores)
    
    # Calculate IoU for validation set
    iou_scores = [jaccard_score(true.flatten(), pred.flatten()) for true, pred in zip(val_masks, val_predictions)]
    average_iou = np.mean(iou_scores)
    
    # Calculate F1 score for validation set
    f1_scores = [f1_score(true.flatten(), pred.flatten()) for true, pred in zip(val_masks, val_predictions)]
    average_f1 = np.mean(f1_scores)
    
    print(f'Average Dice Coefficient: {average_dice}')
    print(f'Average IoU: {average_iou}')
    print(f'Average F1 Score: {average_f1}')
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

3.6 U-Net分割界面设计

在这里插入图片描述

3.7 U-Net分割预测

复制代码
    def predict(image_file_name):
    
    im = np.asarray(Image.open(image_file_name))
    im = im.reshape((Height, Width, Img_channel))
    im = transform(im).float().cuda()
    im = im.reshape((1,Img_channel,Height,Width))
    
    output = model(im)
    _, pred = output.max(1)
    pred = pred.view(Height, Width)
    mask_im = pred.cpu().numpy().astype(np.uint8)
    
    
    w,h = mask_im.shape
    print(w,h)
    
    for i in range(0, w):
        for j in range(0, h):
            if mask_im[i, j] == 1:
                # print('xxx')
                mask_im[i,j] = 255
    
    return mask_im
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    代码解读

参考文献

[1] 基于改进UNet网络的室内运动目标阴影分割[J]. 刘莹;杨硕.计算机系统应用,2022(12)
[2] 基于SegNet与三维点云聚类的大田杨树苗叶片分割方法[J]. 胡春华;刘炫;计铭杰;李羽江;李萍萍.农业机械学报,2022(06)
[3] 基于DeepLab v3+的葡萄叶片分割算法[J]. 李余康;翟长远;王秀;袁洪波;张玮;赵春江.农机化研究,2022(02)
[4] 基于U-Net的玉米叶部病斑分割算法[J]. 刘永波;胡亮;曹艳;唐江云;雷波.中国农学通报,2021(05)
[5] 针对TT100K交通标志数据集的扩增策略[J]. 龚祎垄;吴勇;陈铭峥.福建电脑,2019(11)
[6] 深度学习自适应学习率算法研究[J]. 蒋文斌;彭晶;叶阁焰.华中科技大学学报(自然科学版),2019(05)
[7] 基于Lab空间和K-Means聚类的叶片分割算法研究[J]. 邹秋霞;杨林楠;彭琳;郑强.农机化研究,2015(09)
[8] 复杂背景下植物叶片的彩色图像分割[J]. 王红君;陈伟;赵辉;岳友军.中国农机化学报,2013(02)
[9] 田间枣树叶片复杂目标图像综合分割方法[J]. 董金勇;王建仑;李道亮;何建磊;王永彬.农业机械学报,2011(01)
[10] 基于Mean-shift和提升小波变换的棉花叶片边缘的图像检测[J]. 李寒;王库;边昊一.农业工程学报,2010(S1)
[11] Image Segmentation Method for Sweetgum Leaf Spots Based on an Improved DeeplabV3+ Network[J]. Cai Maodong;Yi Xiaomei;Wang Guoying;Mo Lufeng;Wu Peng;Mwanza Christine;Kapula Kasanda Ernest.Forests,2022
[12] A MobileNetV2-SENet-based method for identifying fish school feeding behavior[J]. Zhang Lu;Wang Jianping;Li Beibei;Liu Yiran;Zhang Hongxu;Duan Qingling.Aquacultural Engineering,2022
[13] LEMOXINET: Lite ensemble MobileNetV2 and Xception models to predict plant disease[J]. Sutaji Deni;Yıldız Oktay.Ecological Informatics,2022
[14] A bone segmentation method based on Multi-scale features fuse U2Net and improved dice loss in CT image process[J]. Liu Tao;Lu Yonghua;Zhang Yu;Hu Jiahui;Gao Cheng.Biomedical Signal Processing and Control,2022
[15] A Framework Using Binary Cross Entropy - Gradient Boost Hybrid Ensemble Classifier for Imbalanced Data Classification[J]. Isabella S.J.;Srinivasan S.;Suseendran G…Webology,2021
[16] GhostNet: More features from cheap operations[J]. Han K.;Wang Y.;Tian Q.;Guo J.;Xu C.;Xu C…Proceedings of the IEEE Computer Society Conference on Computer Vision and Pattern Recognition,2020
[17] Accuracy and sensitivity of radium mass balances in assessing karstic submarine groundwater discharge in the stratified Calanque of Port-Miou (Mediterranean Sea)[J]. Christelle Claude;;Sabine Cockenpot;;Bruno Arfib;;Samuel Meulé;;Olivier Radakovitch.Journal of Hydrology,2019
[18] ImageNet classification with deep convolutional neural networks[J]. Alex Krizhevsky;;Ilya Sutskever;;Geoffrey E. Hinton.Communications of the ACM,2017
[19] Improving Text Classification Performance Using PCA and Recall-Precision Criteria[J]. M. Zahedi;;A. Ghanbari Sorkhi.Arabian journal for science and engineering,2013
[20] LabelMe: A Database and Web-Based Tool for Image Annotation[J]. Bryan C. Russell;;Antonio Torralba;;Kevin P. Murphy;;William T. Freeman.International Journal of Computer Vision,2008

结束语

鉴于博主的能力有限,在本文中所提及其方法也难免会有所欠缺与漏洞,请各位读者多多包涵,并期待您能细心指出其中的问题以便于下一次修订时能够更加完善和严谨地呈现出来

全部评论 (0)

还没有任何评论哟~