深度学习落地实战:基于UNet实现血管瘤超声图像分割

news2024/12/23 14:48:07

前言

大家好,我是机长

本专栏将持续收集整理市场上深度学习的相关项目,旨在为准备从事深度学习工作或相关科研活动的伙伴,储备、提升更多的实际开发经验,每个项目实例都可作为实际开发项目写入简历,且都附带完整的代码与数据集。可通过百度云盘进行获取,实现开箱即用

正在跟新中~

项目背景

(基于UNet实现血管瘤超声图像分割)

血管瘤是一种常见的血管异常疾病,尤其在婴幼儿中具有较高的发病率。超声检查作为一种无创的诊断手段,能够为临床提供血管瘤的位置、形状及累及范围等重要信息,对指导医生进一步治疗至关重要。然而,目前血管瘤病灶的分割主要依赖于专家的人工勾画,这不仅耗时耗力,还容易受到临床经验水平的影响,导致分割结果存在人为误差。因此,利用深度学习技术实现血管瘤超声图像的自动精准分割,成为了一个医学应用与研究的热点方向方向。

项目环境

  • 平台:windows 10
  • 语言环境:python 3.8
  • 编辑器:PyCharm
  • PyThorch版本:1.8

1.创建并跳转到虚拟环境

python -m venv myenv

myenv\Scripts\activate.bat

2. 虚拟环境pip命令安装其他工具包

pip install torch torchvision torchaudio

注:此处只示范安装pytorch,其他工具包安装类似,可通过运行代码查看所确实包提示进行安装

3.pycharm 运行环境配置

进入pytcharm =》点击file =》点击settings=》点击Project:...=》点击 Python Interpreter,进入如下界面

点击add =》点击Existing environment  =》 点击 ... =》选择第一步1创建虚拟环境目录myenv\Scripts\下的python.exe文件点击ok完成环境配置

数据集介绍

数据集分分训练数据集与标签数据集

                

       训练数据样式                                                               标注数据样式

训练数据获取:

私信博主获取

UNet网络介绍

U-Net网络的以其独特的U型结构著称,这种结构由编码器(Encoder)和解码器(Decoder)两大部分组成,非常适合于医学图像分割等任务。下面我将进一步解释您提到的关键点,并补充一些细节。

Encoder(编码器)
  • 结构:编码器的左半部分主要负责特征提取。它通过多个卷积层(通常是3x3的卷积核)和ReLU激活函数来逐层提取图像的特征。在每个卷积层组合之后,通常会添加一个2x2的最大池化层(Max Pooling)来降低特征图的分辨率,并增加感受野。这种下采样操作有助于捕获图像中的全局信息。
  • 作用:编码器通过逐层提取和抽象化图像特征,为后续的分割任务提供丰富的信息。
Decoder(解码器)
  • 结构:解码器的右半部分则负责将编码器提取的特征图恢复到原始图像的分辨率,以便进行像素级别的分类。它首先通过上采样(通常是转置卷积或双线性插值)来增加特征图的尺寸,然后通过特征拼接(Concatenation)将上采样后的特征图与编码器对应层级的特征图进行融合。之后,再通过卷积层和ReLU激活函数进一步处理融合后的特征图。
  • 特征拼接:与FCN(全卷积网络)的逐点相加不同,U-Net使用特征拼接来融合不同层级的特征图。这种拼接方式能够保留更多的特征信息,形成更“厚”的特征图,但同时也需要更多的显存来存储这些特征。
  • 作用:解码器通过逐步上采样和特征融合,将编码器提取的高级特征与低级特征相结合,恢复出精细的图像分割结果。
优点与挑战
  • 优点:U-Net网络结构简洁,但性能强大,特别适用于医学图像分割等任务。其独特的U型结构和特征拼接方式使得网络能够同时捕获全局和局部特征,从而实现高精度的分割。
  • 挑战:尽管U-Net在性能上表现出色,但其对显存的需求也相对较高。特别是当处理高分辨率图像或进行大规模训练时,显存消耗可能会成为限制因素。此外,网络的设计和训练也需要大量的专业知识和经验。

综上所述,U-Net网络通过其独特的编码器-解码器结构和特征拼接方式,在医学图像分割等领域取得了显著成效。然而,在实际应用中仍需注意显存消耗等挑战,并不断探索更高效的优化方法。

pytorch实现UNet网络

class double_conv2d_bn(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, strides=1, padding=1):
        super(double_conv2d_bn, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels,
                               kernel_size=kernel_size,
                               stride=strides, padding=padding, bias=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels,
                               kernel_size=kernel_size,
                               stride=strides, padding=padding, bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.relu(self.bn2(self.conv2(out)))
        return out


class deconv2d_bn(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=2, strides=2):
        super(deconv2d_bn, self).__init__()
        self.conv1 = nn.ConvTranspose2d(in_channels, out_channels,
                                        kernel_size=kernel_size,
                                        stride=strides, bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        return out


class Unet(nn.Module):
    def __init__(self):
        super(Unet, self).__init__()
        self.layer1_conv = double_conv2d_bn(1, 8)
        self.layer2_conv = double_conv2d_bn(8, 16)
        self.layer3_conv = double_conv2d_bn(16, 32)
        self.layer4_conv = double_conv2d_bn(32, 64)
        self.layer5_conv = double_conv2d_bn(64, 128)
        self.layer6_conv = double_conv2d_bn(128, 64)
        self.layer7_conv = double_conv2d_bn(64, 32)
        self.layer8_conv = double_conv2d_bn(32, 16)
        self.layer9_conv = double_conv2d_bn(16, 8)
        self.layer10_conv = nn.Conv2d(8, 1, kernel_size=3,
                                      stride=1, padding=1, bias=True)

        self.deconv1 = deconv2d_bn(128, 64)
        self.deconv2 = deconv2d_bn(64, 32)
        self.deconv3 = deconv2d_bn(32, 16)
        self.deconv4 = deconv2d_bn(16, 8)

        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        conv1 = self.layer1_conv(x)
        pool1 = F.max_pool2d(conv1, 2)

        conv2 = self.layer2_conv(pool1)
        pool2 = F.max_pool2d(conv2, 2)

        conv3 = self.layer3_conv(pool2)
        pool3 = F.max_pool2d(conv3, 2)

        conv4 = self.layer4_conv(pool3)
        pool4 = F.max_pool2d(conv4, 2)

        conv5 = self.layer5_conv(pool4)

        convt1 = self.deconv1(conv5)
        concat1 = torch.cat([convt1, conv4], dim=1)
        conv6 = self.layer6_conv(concat1)

        convt2 = self.deconv2(conv6)
        concat2 = torch.cat([convt2, conv3], dim=1)
        conv7 = self.layer7_conv(concat2)

        convt3 = self.deconv3(conv7)
        concat3 = torch.cat([convt3, conv2], dim=1)
        conv8 = self.layer8_conv(concat3)

        convt4 = self.deconv4(conv8)
        concat4 = torch.cat([convt4, conv1], dim=1)
        conv9 = self.layer9_conv(concat4)
        outp = self.layer10_conv(conv9)
        outp = self.sigmoid(outp)
        return outp

自定义加载数据集

class LiverDataset(data.Dataset):
    def __init__(self, root, transform=None, target_transform=None, mode='train'):
        n = len(os.listdir(root + '/images'))

        imgs = []

        if mode == 'train':
            for i in range(n):
                img = os.path.join(root, 'images',
                                   "%d.png" % (i + 1))
                mask = os.path.join(root, 'mask', "%d.png" % (i + 1))
                imgs.append([img, mask])
        else:
            for i in range(n):
                img = os.path.join(root, 'images',
                                   "%d.png" % (i + 1))
                mask = os.path.join(root, 'mask', "%d.png" % (i + 1))
                imgs.append([img, mask])

        self.imgs = imgs
        self.transform = transform
        self.target_transform = target_transform

    def __getitem__(self, index):
        x_path, y_path = self.imgs[index]
        img_x = Image.open(x_path)
        img_y = Image.open(y_path)
        if self.transform is not None:
            img_x = self.transform(img_x)
        if self.target_transform is not None:
            img_y = self.target_transform(img_y)
        return img_x, img_y

    def __len__(self):
        return len(self.imgs)

完整代码

import os

import PIL.Image as Image
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')
#module 'backend_interagg' has no attribute 'FigureCanvas',报错执行pip install matplotlib==3.5.0
import numpy as np
#关于numpy 报错时执行 pip install numpy==1.23.5
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as data
from torchvision import transforms
from tqdm import tqdm

epochs = 1
batch_size = 32
device = 'cpu'
best_model = None
best_loss = 999
save_path = './checkpoints/best_model.pkl'
directory_path = './checkpoints'

# 检查目录是否存在
if not os.path.exists(directory_path):
    # 如果目录不存在,则创建它
    os.makedirs(directory_path)


class double_conv2d_bn(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, strides=1, padding=1):
        super(double_conv2d_bn, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels,
                               kernel_size=kernel_size,
                               stride=strides, padding=padding, bias=True)
        self.conv2 = nn.Conv2d(out_channels, out_channels,
                               kernel_size=kernel_size,
                               stride=strides, padding=padding, bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = F.relu(self.bn2(self.conv2(out)))
        return out


class deconv2d_bn(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=2, strides=2):
        super(deconv2d_bn, self).__init__()
        self.conv1 = nn.ConvTranspose2d(in_channels, out_channels,
                                        kernel_size=kernel_size,
                                        stride=strides, bias=True)
        self.bn1 = nn.BatchNorm2d(out_channels)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        return out


class Unet(nn.Module):
    def __init__(self):
        super(Unet, self).__init__()
        self.layer1_conv = double_conv2d_bn(1, 8)
        self.layer2_conv = double_conv2d_bn(8, 16)
        self.layer3_conv = double_conv2d_bn(16, 32)
        self.layer4_conv = double_conv2d_bn(32, 64)
        self.layer5_conv = double_conv2d_bn(64, 128)
        self.layer6_conv = double_conv2d_bn(128, 64)
        self.layer7_conv = double_conv2d_bn(64, 32)
        self.layer8_conv = double_conv2d_bn(32, 16)
        self.layer9_conv = double_conv2d_bn(16, 8)
        self.layer10_conv = nn.Conv2d(8, 1, kernel_size=3,
                                      stride=1, padding=1, bias=True)

        self.deconv1 = deconv2d_bn(128, 64)
        self.deconv2 = deconv2d_bn(64, 32)
        self.deconv3 = deconv2d_bn(32, 16)
        self.deconv4 = deconv2d_bn(16, 8)

        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        conv1 = self.layer1_conv(x)
        pool1 = F.max_pool2d(conv1, 2)

        conv2 = self.layer2_conv(pool1)
        pool2 = F.max_pool2d(conv2, 2)

        conv3 = self.layer3_conv(pool2)
        pool3 = F.max_pool2d(conv3, 2)

        conv4 = self.layer4_conv(pool3)
        pool4 = F.max_pool2d(conv4, 2)

        conv5 = self.layer5_conv(pool4)

        convt1 = self.deconv1(conv5)
        concat1 = torch.cat([convt1, conv4], dim=1)
        conv6 = self.layer6_conv(concat1)

        convt2 = self.deconv2(conv6)
        concat2 = torch.cat([convt2, conv3], dim=1)
        conv7 = self.layer7_conv(concat2)

        convt3 = self.deconv3(conv7)
        concat3 = torch.cat([convt3, conv2], dim=1)
        conv8 = self.layer8_conv(concat3)

        convt4 = self.deconv4(conv8)
        concat4 = torch.cat([convt4, conv1], dim=1)
        conv9 = self.layer9_conv(concat4)
        outp = self.layer10_conv(conv9)
        outp = self.sigmoid(outp)
        return outp


class LiverDataset(data.Dataset):
    def __init__(self, root, transform=None, target_transform=None, mode='train'):
        n = len(os.listdir(root + '/images'))

        imgs = []

        if mode == 'train':
            for i in range(n):
                img = os.path.join(root, 'images',
                                   "%d.png" % (i + 1))
                mask = os.path.join(root, 'mask', "%d.png" % (i + 1))
                imgs.append([img, mask])
        else:
            for i in range(n):
                img = os.path.join(root, 'images',
                                   "%d.png" % (i + 1))
                mask = os.path.join(root, 'mask', "%d.png" % (i + 1))
                imgs.append([img, mask])

        self.imgs = imgs
        self.transform = transform
        self.target_transform = target_transform

    def __getitem__(self, index):
        x_path, y_path = self.imgs[index]
        img_x = Image.open(x_path)
        img_y = Image.open(y_path)
        if self.transform is not None:
            img_x = self.transform(img_x)
        if self.target_transform is not None:
            img_y = self.target_transform(img_y)
        return img_x, img_y

    def __len__(self):
        return len(self.imgs)


x_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomResizedCrop(224),
    transforms.Grayscale(num_output_channels=1)
])

y_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomResizedCrop(224)
])

liver_dataset = LiverDataset("./data/train", transform=x_transform, target_transform=y_transform)
dataloader = torch.utils.data.DataLoader(liver_dataset, batch_size=batch_size, shuffle=True)

model = Unet()
criterion = torch.nn.BCELoss()
#criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())

for epoch in range(epochs):
    model.train()
    epoch_loss = 0
    train_bar = tqdm(dataloader)
    for x, y in train_bar:
        optimizer.zero_grad()
        inputs = x.to(device)
        labels = y.to(device)
        outputs = model(inputs)
        labels=labels.bool().float()
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()

    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % str(epoch_loss))

    if epoch_loss < best_loss:
        best_loss = epoch_loss
        best_model = model.state_dict()

    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, save_path)

print('Finished Training')

plt.figure('测试一张图片')
pil_img = Image.open('./data/train/images/1.png')
np_img = np.array(pil_img)
plt.imshow(np_img)
plt.show()

plt.figure('测试一张蒙版图片')
pil_img = Image.open('./data/train/mask/1.png')
np_img = np.array(pil_img)
plt.imshow(np_img)
plt.show()

开启训练

训练结果展示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1931158.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无人机技术优势及发展详解

一、技术优势 无人机&#xff08;Unmanned Aerial Vehicle&#xff0c;UAV&#xff09;作为一种新兴的空中智能平台&#xff0c;凭借其独特的技术优势&#xff0c;已经在众多领域中展现出强大的应用潜力和实用价值。以下是无人机的主要技术优势&#xff1a; 1. 自主导航与远程…

《昇思25天学习打卡营第19天|Diffusion扩散模型》

什么是Diffusion Model&#xff1f; 什么是Diffusion Model? 如果将Diffusion与其他生成模型&#xff08;如Normalizing Flows、GAN或VAE&#xff09;进行比较&#xff0c;它并没有那么复杂&#xff0c;它们都将噪声从一些简单分布转换为数据样本&#xff0c;Diffusion也是从…

传统墙面装饰已成过去?创意投影互动墙引领新潮流?

你是否曾遐想过&#xff0c;那些日常中屡见不鲜的平凡墙面&#xff0c;能够摇身一变&#xff0c;成为既炫酷又高度互动的奇迹之地&#xff1f;事实上&#xff0c;这并非遥不可及的梦想&#xff0c;只需巧妙融合前沿的投影技术、灵敏的传感器与智能软件系统&#xff0c;便能瞬间…

01 机器学习概述

目录 1. 基本概念 2. 机器学习三要素 3. 参数估计的四个方法 3.1 经验风险最小化 3.2 结构风险最小化 3.3 最大似然估计 3.4 最大后验估计 4. 偏差-方差分解 5. 机器学习算法的类型 6. 数据的特征表示 7. 评价指标 1. 基本概念 机器学习&#xff08;Machine Le…

AdobeInDesign ID软件三网下载+Id教程

简介&#xff1a; InDesign还可以结合其他产品发布适合平板设备的内容。平面设计师和生产艺术家是主要用户&#xff0c;创作和布局期刊出版物、海报和印刷媒体。它还支持导出到EPUB和SWF格式&#xff0c;以创建电子书和数字出版物&#xff0c;包括数字杂志&#xff0c;以及适合…

【linux高级IO(三)】初识epoll

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux高级IO 1. 前言2. 初识e…

【python】PyQt5的窗口界面的各种交互逻辑实现,轻松掌控图形化界面程序

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

H3C Intelligent Management Center无线认证新增设备如何配置

目录 前提条件 一、IPsec VPN配置 二、IMC平台的配置 1.组网 ​编辑 2.核心设备配置 3.AAA服务器侧配置 4.创建认证的用户 5.登录测试 三、AC无线控制器图形界面配置 1.认证配置 1.1 新增ISP域 ​编辑​编辑 1.2新增 RADIUS 1.3 Portal认证配置​编辑​编辑​编…

Rust编程-crates.io

发布配置和开发配置&#xff1a; [profile.dev]: > cargo build opt-level0 [profile.release]: > cargo build --release opt-level3 发布到crates.io 文档注释&#xff1a; 三斜线&#xff08;///&#xff09;&#xff0c;使用markdown语法来格式化内容 可以为函数…

fatal: read error: Connection reset by peer

参考文章&#xff1a;https://www.cnblogs.com/sisimi/p/7910272.html 问题&#xff1a; 首先确认是否可以访问外网&#xff1a; ping www.baidu.com如果可以访问外网&#xff0c;把 git: 修改为 http: 即可&#xff1a;

高职院校人工智能人才培养成果导向系统构建、实施要点与评量方法

一、引言 近年来&#xff0c;人工智能技术在全球范围内迅速发展&#xff0c;对各行各业产生了深远的影响。高职院校作为培养高技能人才的重要基地&#xff0c;肩负着培养人工智能领域专业人才的重任。为了适应社会对人工智能人才的需求&#xff0c;高职院校需要构建一套科学、…

Java学习 - spring Bean 详解

Bean 的别名配置 接着上一篇文章中的 <bean> 配置&#xff0c;其中配置了 id 属性&#xff0c;通过 id 属性我们就可以获取到对象。其实 <bean> 配置也提供了 name 属性&#xff0c;它是用于定义 Bean 的别名&#xff0c;一个 Bean 的别名是可以有多个的&#xff…

[C++]——同步异步日志系统(7)

同步异步日志系统 一、日志器管理模块&#xff08;单例模式&#xff09;1.1 对日志器管理器进行设计1.2 实现日志器管理类的各个功能1.3. 设计一个全局的日志器建造者1.4 测试日志器管理器的接口和全局建造者类 二、宏函数和全局接口设计2.1 新建一个.h,文件,文件里面放我们写的…

视图库对接系列(GA-T 1400)十九、视图库对接系列(级联)注册

背景 在上一章视图库对接系列(GA-T 1400)十八、视图库对接系列(级联)代码生成中我们已经把代码生成了,那怎么实现级联? 我们可以抓包看设备是怎么注册到我们平台的, 那我们就怎么实现就可以了。 实现 先看设备注册到我们服务端的包 步骤 注册我们可以参考视图库对接系列(…

Data类中的常用方法

Calender类 java.util.Calendar是一个抽象的基类&#xff0c;创建对象需要使用静态方法Calendar.getInstance()完成。通过Calendar对象可以获得详细的日历信息&#xff0c;例如年、月、日、小时、分和秒&#xff0c;Calendar的子类可以实现特定的日历系统。 当前时间 Calenda…

CV每日论文--2024.7.16

1、Radiance Fields from Photons 中文标题&#xff1a;光子的辐射场 简介&#xff1a;神经辐射场(NeRFs)已成为从多个视角捕获的图像进行高质量视图合成的事实标准方法。然而,在野外环境下捕获图像时,仍存在许多挑战,例如低光、高动态范围或快速运动导致的模糊重建和明显的伪…

【MySQL进阶篇】索引

1、索引概述 索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c…

高职院校专业群的生成机制研究

摘要&#xff1a;本研究针对我国产业转型升级背景下高职院校专业群的生成机制进行了深入探讨。运用案例分析法&#xff0c;从生成决策、生成目标、生成规则三个维度出发&#xff0c;对专业群的生成机制进行了系统分析。研究发现&#xff0c;高职院校专业群的生成是一个与产业环…

容器之Harbor

Harbor 是一个开源的企业级 Docker 镜像仓库&#xff0c;用于存储、签名和扫描 Docker 镜像。它由 VMware 开发&#xff0c;旨在增强用户在容器化环境中的安全性和效率。以下是对 Harbor 的详细介绍&#xff1a; 主要功能 1. 镜像管理 镜像存储&#xff1a;提供高效的存储管理…

誉天教育与武汉晴川学院携手开展鸿蒙实训营,共筑鸿蒙生态新篇章!

在数字经济蓬勃发展的今天&#xff0c;鸿蒙系统作为华为自主研发的操作系统&#xff0c;正逐步构建起一个开放、协同、共赢的生态体系。为了进一步推动鸿蒙生态的繁荣发展&#xff0c;培养更多具备鸿蒙原生应用开发能力的专业人才&#xff0c;誉天教育与武汉晴川学院强强联合&a…