U-net模型的实现和完整代码

这篇具有很好参考价值的文章主要介绍了U-net模型的实现和完整代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        U-net是非常经典的图像分割模型,在整个结构中没有全连接层,使用上采样层代替。详细的可以阅读U-net的原论文详细了解,原文链接:https://arxiv.org/pdf/1505.04597.pdf

 

        同时在网上也有许多优秀的文章、博客做出了非常好的解读大家可以搜索查看。

        代码主要分为数据的预处理、网络结构的搭建、训练、测试下面是详细的代码

1、数据的预处理代码保存在datesetpretrain.py文件中,本人是参考了https://blog.csdn.net/qq_44886601/article/details/127886731这篇文章大家可以去看一下,作者也做出了详尽的讲解

import os
from torch.utils.data import Dataset
from torchvision import transforms
from PIL import Image
import matplotlib.pyplot as plt
transform = transforms.Compose([
    transforms.Resize((572, 572)),  # 缩放图像与原论中输入图像大小一致
    transforms.ToTensor(),  
])


# 数据处理文件
class Data_Loader(Dataset):  # 加载数据
    def __init__(self, root, transforms=transform):  # 指定路径、预处理等等
        imgs = os.listdir(root)  # 获取root文件下的文件
        self.imgs = [os.path.join(root, img) for img in imgs]  # 获取每个文件的路径
        self.transforms = transforms  # 预处理

    def __getitdem__(self, index):  # 读取图片,返回一条样本
        image_path = self.imgs[index]  # 根据index读取图片
        label_path = image_path.replace('image', 'label')  # 把路径中的image替换成label,就找到对应数据的label

        image = Image.open(image_path)  # 读取图片和对应的label图
        label = Image.open(label_path)

        if self.transforms:  # 判断是否预处理
            image = self.transforms(image)

            label = self.transforms(label)
            label[label >= 0.5] = 1  # 这里转为二值图片
            label[label < 0.5] = 0

        return image, label

    def __len__(self):  # 返回图像个数
        return len(self.imgs)

2、U-net结构搭建与训练。我这个把网络的搭建与训练写到一个文件中了,这点可能做的不好,我也看了许多作者都是分开写的,我当时就是图方便。在网络结构是参考https://blog.csdn.net/weixin_41857483/article/details/120768804同样,也是非常详细的介绍了网络结构,代码如下:

import torch.nn as nn
import torch.nn.functional as F
import torch
import torch.optim as optim
import datesetpretrain #导入数据预处理的部分

class DoubleConv(nn.Module):
    """
    1. DoubleConv 模块
    (convolution => [BN] => ReLU) * 2
    连续两次的卷积操作:U-net网络中,下采样和上采样过程,每一层都会连续进行两次卷积操作
    """
    def __init__(self, in_channels, out_channels):
        super().__init__()
        # torch.nn.Sequential是一个时序容器,Modules 会以它们传入的顺序被添加到容器中。
        # 此处:卷积->BN->ReLU->卷积->BN->ReLU
        self.double_conv = nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=0),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=0),
            nn.BatchNorm2d(out_channels),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        return self.double_conv(x)


class Down(nn.Module):
    """
    2. Down(下采样)模块
    Downscaling with maxpool then double conv
    maxpool池化层,进行下采样,再接DoubleConv模块
    """
    def __init__(self, in_channels, out_channels):
        super().__init__()
        self.maxpool_conv = nn.Sequential(
            nn.MaxPool2d(2),  # 池化层
            DoubleConv(in_channels, out_channels)  # DoubleConv模块
        )

    def forward(self, x):
        return self.maxpool_conv(x)


class Up(nn.Module):
    """
    3. Up(上采样)模块
    Upscaling then double conv
    """
    """
      __init__初始化函数定义了上采样方法以及卷积采用DoubleConv
      上采样,定义了两种方法:Upsample和ConvTranspose2d,也就是双线性插值和反卷积。
    """
    def __init__(self, in_channels, out_channels, bilinear=True):
        super().__init__()

        # if bilinear, use the normal convolutions to reduce the number of channels
        if bilinear:
            self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
        else:
            self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)  # 反卷积(2*2 => 4*4)

        self.conv = DoubleConv(in_channels, out_channels)

    def forward(self, x1, x2):
        """
        x1接收的是上采样的数据,x2接收的是特征融合的数据
        特征融合方法就是,先对小的feature map进行padding,再进行concat(通道叠加)
        :param x1:
        :param x2:
        :return:
        """
        x1 = self.up(x1)
        # input is CHW
        diffY = x2.size()[2] - x1.size()[2]
        diffX = x2.size()[3] - x1.size()[3]

        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,
                        diffY // 2, diffY - diffY // 2])
        print(diffX - diffX // 2)
        x = torch.cat([x2, x1], dim=1)
        return self.conv(x)


class OutConv(nn.Module):
    """
    4. OutConv模块
    UNet网络的输出需要根据分割数量,整合输出通道(若最后的通道为2,即分类为2的情况)
    """
    def __init__(self, in_channels, out_channels):
        super(OutConv, self).__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)

    def forward(self, x):
        return self.conv(x)


"""
UNet网络用到的模块即以上4个模块
根据UNet网络结构,设置每个模块的输入输出通道个数以及调用顺序
"""


class UNet(nn.Module):
    def __init__(self, n_channels, n_classes, bilinear = False):
        super(UNet, self).__init__()
        self.n_channels = n_channels
        self.n_classes = n_classes
        self.bilinear = bilinear

        self.inc = DoubleConv(n_channels, 64)
        self.down1 = Down(64, 128)
        self.down2 = Down(128, 256)
        self.down3 = Down(256, 512)
        self.down4 = Down(512, 1024)
        self.up1 = Up(1024, 512, bilinear)
        self.up2 = Up(512, 256, bilinear)
        self.up3 = Up(256, 128, bilinear)
        self.up4 = Up(128, 64, bilinear)
        self.outc = OutConv(64, n_classes)
    def _initialize_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 = self.inc(x)
        x2 = self.down1(x1)
        x3 = self.down2(x2)
        x4 = self.down3(x3)
        x5 = self.down4(x4)
        x = self.up1(x5, x4)
        x = self.up2(x, x3)
        x = self.up3(x, x2)
        x = self.up4(x, x1)
        logits = self.outc(x)
        return logits

#下面是训练的部分
'''if __name__ == '__main__':
    net = UNet(n_channels=1, n_classes=1)
    trainset=datesetpretrain.Data_Loader("./data/train/image")
    train_loader = torch.utils.data.DataLoader(dataset=trainset,batch_size=4,shuffle=True)
    optimizer=optim.RMSprop(net.parameters(),lr=0.00001,weight_decay=1e-8,momentum=0.9)
    criterion=nn.BCEWithLogitsLoss()

    save_path = './UNet.pth'
    print(net)
    for epoch in range(20):

        net.train()  # 训练模式
        running_loss = 0.0

        for image, label in train_loader:  # 读取数据和label
            #print(image.shape)
            #print(label.shape)
            optimizer.zero_grad()  # 梯度清零
            pred = net(image)  # 前向传播
            pred=F.pad(pred,[4,4,4,4])
            #print(pred.shape)
            #print(label.shape)在这里我遇到了问题在下文解释
            loss = criterion(pred, label)  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 梯度下降
            running_loss += loss.item()  # 计算损失和

            print("train_loss:%0.3f" % (loss.item()))

    torch.save(net.state_dict(), save_path)'''

在代码中我做出了标记就是在倒数第9行的位置,我输出了一下pred与label的形状,pred的做完向前传播的矩阵是[4,1,564,564],而我们在输入的时候是572*572的图像大小经过向前传播却变小了,我估计是在网络结构中出现了问题,本人目前还不清楚蛋初步估计是在上采样中出现了问题也就是在


    def forward(self, x1, x2):
        """
        x1接收的是上采样的数据,x2接收的是特征融合的数据
        特征融合方法就是,先对小的feature map进行padding,再进行concat(通道叠加)
        :param x1:
        :param x2:
        :return:
        """
        x1 = self.up(x1)
        # input is CHW
        diffY = x2.size()[2] - x1.size()[2]
        diffX = x2.size()[3] - x1.size()[3]

        x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2,
                        diffY // 2, diffY - diffY // 2])
        print(diffX - diffX // 2)
        x = torch.cat([x2, x1], dim=1)
        return self.conv(x)

F.pad()中,这是我的猜想。于是我在向前传播完成后对生成的pred结果使用了补上了4圈0的办法解决了问题,因为在计算损失函数时需要pred与label的形状相同。训练完成将数据保存在UNet.pth文件中

3、效果测试,参考文件如下

import numpy as np
import torch
from torchvision import transforms
from PIL import Image
import Unet
import matplotlib.pyplot as plt

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5))
])#对测试集的图像做预处理

# 加载模型和在UNet.pth中的参数
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
net = Unet.UNet(n_channels=1, n_classes=1)
net.load_state_dict(torch.load('UNet.pth', map_location=device))
net.to(device)

net.eval()
with torch.no_grad():
    img = Image.open('./data/test/1.png')  # 读取预测的图片
    img = transform(img)  # 预处理
    img = torch.unsqueeze(img, dim=0)
    pred = net(img.to(device))  # 网络预测
    pred = torch.squeeze(pred)  # 将(batch、channel)维度去掉
    pred = np.array(pred.data.cpu())  # 保存图片需要转为cpu处理
    pred[pred >= 0] = 255  # 转为二值图片
    pred[pred < 0] = 0
    pred = np.uint8(pred)  # 转为图片的形式
    plt.imshow(pred)
    plt.show()

最后是结果对比个人感觉还不错,只是我没有做精度的测算,对比图如下:

u-net代码,U-net图像分割,pytorch,图像处理,计算机视觉

        大家可以下载数据集来尝试,从而更好的理解,数据集的下载链接是https://github.com/Jack-Cherish/Deep-Learning

        最后,也是最重要的是感谢上述我所参考文章的作者!!!文章来源地址https://www.toymoban.com/news/detail-725670.html

到了这里,关于U-net模型的实现和完整代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 医学图像分割之Attention U-Net

    目录 一、背景 二、问题 三、解决问题 四、Attention U-Net网络结构         简单总结Attention U-Net的操作:增强目标区域的特征值,抑制背景区域的目标值。抑制也就是设为了0。         为了捕获到足够大的、可接受的范围和语义上下文信息,在标准的CNN结构中,特征图被逐步

    2024年02月02日
    浏览(27)
  • 【图像处理】经营您的第一个U-Net以进行图像分割

            AI厨师们,今天您将学习如何准备计算机视觉中最重要的食谱之一:U-Net。本文将叙述:1 语义与实例分割,2 图像分割中还使用了其他损失,例如Jaccard损失,焦点损失;3 如果2D图像分割对您来说太容易了,您可以查看3D图像分割,因为模型要大得多,因此要困难得

    2024年02月15日
    浏览(43)
  • U-Net Transformer:用于医学图像分割的自我和交叉注意力模块

    对于复杂和低对比度的解剖结构,医学图像分割仍然特别具有挑战性。本文提出的一种U-Transformer网络,它将Transformer中的self-attention和Cross attention融合进了UNet,这样克服了UNet无法建模长程关系和空间依赖的缺点,从而提升对关键上下文的分割。本文集合了两种注意力机制:自

    2024年02月06日
    浏览(28)
  • 计算机视觉-02-基于U-Net的肝脏肿瘤分割(包含数据和代码)

    关注公众号:『 AI学习星球 』 回复: 肝脏肿瘤分割 即可获取数据下载。 需要 论文辅导 、 4对1辅导 、 算法学习 ,可以通过 CSDN 或 公众号 滴滴我 1.1 简介 该任务分为三个阶段,这是第一个阶段,三个阶段分别是: 第一阶段分割出腹部图像中的肝脏,作为第二阶段的ROI(r

    2024年02月03日
    浏览(31)
  • 论文阅读_图形图像_U-NET

    name_en: U-Net: Convolutional Networks for Biomedical Image Segmentation name_ch: U-Net:用于生物医学图像分割的卷积网络 addr: http://link.springer.com/10.1007/978-3-319-24574-4_28 doi: 10.1007/978-3-319-24574-4_28 date_read: 2023-02-08 date_publish: 2015-01-01 tags: [‘图形图像’\\\'] journal: Medical Image Computing and Computer-Assisted

    2024年02月11日
    浏览(31)
  • DeU-Net: 用于三维心脏mri视频分割的可变形(Deformable)U-Net

    论文链接:https://arxiv.org/abs/2007.06341 代码链接:文章都看完了实在找不到代码!好崩溃!好崩溃!已经发邮件联系作者! 心脏磁共振成像(MRI)的自动分割促进了临床应用中高效、准确的体积测量。然而,由于分辨率各向异性和边界模糊(如右心室心内膜),现有方法在心脏MRI三

    2024年02月09日
    浏览(32)
  • 论文阅读笔记——SMU-Net:面向缺失模态脑肿瘤分割的样式匹配U-Net

    论文地址:https://arxiv.org/abs/2204.02961v1 脑胶质瘤:https://baike.baidu.com/item/%E8%84%91%E8%83%B6%E8%B4%A8%E7%98%A4/7242862 互信息:https://zhuanlan.zhihu.com/p/240676850 Gram矩阵:https://zhuanlan.zhihu.com/p/187345192 背景: 绝大多数脑肿瘤都可以通过磁共振成像进行唯一的鉴别。 多模态MRI的好处: 每一种模态

    2024年01月25日
    浏览(36)
  • U-Net网络结构解析和代码解析

    在语义分割领域,基于深度学习的语义分割算法开山之作是FCN(Fully Convolutional Networks for Semantic Segmentation),而U-Net是遵循FCN的原理,并进行了相应的改进,使其适应小样本的简单分割问题。U-Net网络在医疗影像领域的应用十分广泛,成为了大多数医疗影像语义分割任务的ba

    2024年02月05日
    浏览(26)
  • U-Net 模型改进和应用场景研究性综述

    闲来无事,被封宿舍,代码不好码,正好有几篇综述,拿来看看,这篇文章主要从U-Net模型的结构性和非结构性改进两个方向做出综述,同时还介绍了视网膜血管,肺结节,肝脏肝脏肿瘤,脑肿瘤四种典型任务为例,阐述不同数据的分割特点和难点; 参考之前的一篇文章:

    2024年01月19日
    浏览(24)
  • U-Net网络模型改进(添加通道与空间注意力机制)---亲测有效,指标提升

    U-Net网络模型(注意力改进版本) 这一段时间做项目用到了U-Net网络模型,但是原始的U-Net网络还有很大的改良空间,在卷积下采样的过程中加入了通道注意力和空间注意力 。 常规的U-net模型如下图: 红色箭头为可以添加的地方:即下采样之间。 通道空间注意力是一个即插即

    2024年03月15日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包