精华内容
下载资源
问答
  • pytorch实现unet网络,专门用于进行图像分割训练。该代码打过kaggle上的 Carvana Image Masking Challenge from a high definition image.
  • Pytorch实现用于图像语义分割:U-Net,具有密集的CRF后处理
  • 细分数据集 确保使用--recurse-submodules签出,有一个子模块'tools'用于常见的点滴)它会加载一个包含图像和遮罩的文件夹,... python main.py --lr 0.1 --batch_size 4 --input /path/to/dataset --model "unet --
  • UNet / FCN PyTorch 该存储库包含U-Net和FCN的简单PyTorch实现,这是Ronneberger等人提出的深度学习细分方法。 和龙等。 用于训练的合成图像/遮罩 首先克隆存储库并cd到项目目录。 import matplotlib . pyplot as ...
  • Unet图像分割网络Pytorch实现

    万次阅读 多人点赞 2019-02-12 02:34:04
    本文的内容是我对Unet论文的总结与提炼,需要提醒的是,Unet原文发布的时候还没有提出BN(Batch Normalization). 所以在本文中我会增加这一个步骤。 如果想要安装Python和Pytorch或者获得进一步的信息可以点击pytho.....

    介绍

    最近一个月接触了一下Pytorch,个人认为Pytorch相较于Tensorflow来说好用很多。本文的内容是我对Unet论文的总结与提炼,需要提醒的是,Unet原文发布的时候还没有提出BN(Batch Normalization). 所以在本文中我会增加这一个步骤。

    如果想要安装Python和Pytorch或者获得进一步的信息可以点击Python ,Pytorch

    在图像分割这个大问题上,主要有两个流派:U-shape和dialated Conv。本文介绍的是U-shape网络中最为经典的U-Net。随着骨干网路的进化,很多相应衍生出来的网络大多都是对于Unet进行了改进但是本质上的思路还是没有太多的变化。比如结合DenseNet 和Unet的FCDenseNet, Unet++


    Unet

    Unet是一个为医学图像分割设计的auto-encoder-decoder结构的网络。行业里也把它视作一种FCN(fully connected network)。 它可以分成两个部分,down(encoder) 和 up(decoder)。down的主要结构可以看成conv后面跟maxpool。 up的主要结构是一个upsample后面跟conv。

    Unet的核心思想

    想要弄清这个问题首先要感性的理解一下卷积的作用。就拿MINIST数据集训练数字识别这个简单的CNN网络为例, 它把一个28*28的图片抽象成一个0-9的向量。卷积可以看成是特征的提取,它可以提取出输入的信息的抽象概念。但是Pool和Conv会损失空间信息。其中,空间信息在pool的过程中损失的更为严重。对于图像分割来说, 空间信息和抽象信息同样重要。既然每一个次pool的时候会严重损失空间信息,也就是说maxpool之间的空间信息多于之后的。于是Unet提出,把down的特征连接到对应的up上。

    Unet的结构

    Unet
    其中灰色箭头copy and crop中的copy就是concatenatecrop是为了让两者的长宽一致
    左半边就是down path右半边 就是up path。我们来分别介绍这两个部分。

    Down Path

    图中input image tile就是我们输入的训练数据。除了第一层是两个conv,其他层都可以看成是maxpool后面跟两个conv。在Unet中绝大部分的conv都是两个conv连用的形式存在的,为了方便,我们可以先自定义一个double_conv类。

    # 实现double conv
    class double_conv(nn.Module):
        ''' Conv => Batch_Norm => ReLU => Conv2d => Batch_Norm => ReLU
        '''
        def __init__(self, in_ch, out_ch):
            super(double_conv, self).__init__()
            self.conv = nn.Sequential(
                nn.Conv2d(in_ch, out_ch, 3, padding=1),
                nn.BatchNorm2d(out_ch),
                nn.ReLU(inplace=True),
                nn.Conv2d(out_ch, out_ch, 3, padding=1),
                nn.BatchNorm2d(out_ch),
                nn.ReLU(inplace=True)
            )
            self.conv.apply(self.init_weights)
        
        def forward(self, x):
            x = self.conv(x)
            return x
    
        @staticmethod
        def init_weights(m):
            if type(m) == nn.Conv2d:
                init.xavier_normal(m.weight)
                init.constant(m.bias,0)
    

    下面我们来实现input conv, 它实际上用一个double_conv也就完成了。

    # 实现input conv
    class inconv(nn.Module):
        ''' input conv layer
            let input 3 channels image to 64 channels
            The oly difference between `inconv` and `down` is maxpool layer 
        '''
        def __init__(self, in_ch, out_ch):
            super(inconv, self).__init__()
            self.conv = double_conv(in_ch, out_ch)
    
        def forward(self, x):
            x = self.conv(x)
            return x
    

    接下来我们来实现down类,它的结构是一个maxpool接一个double_conv

    class down(nn.Module):
        ''' normal down path 
            MaxPool2d => double_conv
        '''
        def __init__(self, in_ch, out_ch):
            super(down, self).__init__()
            self.mpconv = nn.Sequential(
                nn.MaxPool2d(2),
                double_conv(in_ch, out_ch)
            )
    
        def forward(self, x):
            x = self.mpconv(x)
            return x
    

    Up path

    Unet的up path主要的结构是upsampl加上double_conv但是也可以使用ConvTranspose2d代替upsample。下面的代码给出了两种选择。
    在up path 中,我们需要将down path 中的特征合并进来。在up.forward中crop从而让两个特征一致。

    class up(nn.Module):
        ''' up path
            conv_transpose => double_conv
        '''
        def __init__(self, in_ch, out_ch, Transpose=False):
            super(up, self).__init__()
    
            #  would be a nice idea if the upsampling could be learned too,
            #  but my machine do not have enough memory to handle all those weights
            if Transpose:
                self.up = nn.ConvTranspose2d(in_ch, in_ch//2, 2, stride=2)
            else:
                # self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
                self.up = nn.Sequential(nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True),
                                        nn.Conv2d(in_ch, in_ch//2, kernel_size=2, padding=0),
                                        nn.ReLU(inplace=True))
            self.conv = double_conv(in_ch, out_ch)
            self.up.apply(self.init_weights)
    
        def forward(self, x1, x2):
            ''' 
                conv output shape = (input_shape - Filter_shape + 2 * padding)/stride + 1
            '''
    
            x1 = self.up(x1)
    
            diffY = x2.size()[2] - x1.size()[2]
            diffX = x2.size()[3] - x1.size()[3]
    
            x1 = nn.functional.pad(x1, (diffX // 2, diffX - diffX//2,
                                        diffY // 2, diffY - diffY//2))
    
            x = torch.cat([x2,x1], dim=1)
            x = self.conv(x)
            return x
    
        @staticmethod
        def init_weights(m):
            if type(m) == nn.Conv2d:
                init.xavier_normal(m.weight)
                init.constant(m.bias,0)
    

    轮子已经造好了,那么我们来实现Unet让它跑起来

    class Unet(nn.Module):
        def __init__(self, in_ch, out_ch, gpu_ids=[]):
            super(Unet, self).__init__()
            self.loss_stack = 0
            self.matrix_iou_stack = 0
            self.stack_count = 0
            self.display_names = ['loss_stack', 'matrix_iou_stack']
            self.gpu_ids = gpu_ids
            self.bce_loss = nn.BCELoss()
            self.device = torch.device('cuda:{}'.format(self.gpu_ids[0])) if torch.cuda.is_available() else torch.device('cpu')
            self.inc = inconv(in_ch, 64)
            self.down1 = down(64, 128)
            # print(list(self.down1.parameters()))
            self.down2 = down(128, 256)
            self.down3 = down(256, 512)
            self.drop3 = nn.Dropout2d(0.5)
            self.down4 = down(512, 1024)
            self.drop4 = nn.Dropout2d(0.5)
            self.up1 = up(1024, 512, False)
            self.up2 = up(512, 256, False)
            self.up3 = up(256, 128, False)
            self.up4 = up(128, 64, False)
            self.outc = outconv(64, 1)
            self.optimizer = torch.optim.Adam(self.parameters(), lr=1e-4)
            # self.optimizer = torch.optim.SGD(self.parameters(), lr=0.1, momentum=0.9, weight_decay=0.0005)
    
        def forward(self):
            x1 = self.inc(self.x)
            x2 = self.down1(x1)
            x3 = self.down2(x2)
            x4 = self.down3(x3)
            x4 = self.drop3(x4)
            x5 = self.down4(x4)
            x5 = self.drop4(x5)
            x = self.up1(x5, x4)
            x = self.up2(x, x3)
            x = self.up3(x, x2)
            x = self.up4(x, x1)
            x = self.outc(x)
            self.pred_y = nn.functional.sigmoid(x)
    
        def set_input(self, x, y):
            self.x = x.to(self.device)
            self.y = y.to(self.device)
    
        def optimize_params(self):
            self.forward()
            self._bce_iou_loss()
            _ = self.accu_iou()
            self.stack_count += 1
            self.zero_grad()
            self.loss.backward()
            self.optimizer.step()
    
        def accu_iou(self):
            # B is the mask pred, A is the malanoma 
            y_pred = (self.pred_y > 0.5) * 1.0
            y_true = (self.y > 0.5) * 1.0
            pred_flat = y_pred.view(y_pred.numel())
            true_flat = y_true.view(y_true.numel())
    
            intersection = float(torch.sum(pred_flat * true_flat)) + 1e-7
            denominator = float(torch.sum(pred_flat + true_flat)) - intersection + 2e-7
    
            self.matrix_iou = intersection/denominator
            self.matrix_iou_stack += self.matrix_iou
            return self.matrix_iou
    
        def _bce_iou_loss(self):
            y_pred = self.pred_y
            y_true = self.y
            pred_flat = y_pred.view(y_pred.numel())
            true_flat = y_true.view(y_true.numel())
    
            intersection = torch.sum(pred_flat * true_flat) + 1e-7
            denominator = torch.sum(pred_flat + true_flat) - intersection + 1e-7
            iou = torch.div(intersection, denominator)
            bce_loss = self.bce_loss(pred_flat, true_flat)
            self.loss = bce_loss - iou + 1
            self.loss_stack += self.loss
            
        def get_current_losses(self):
            errors_ret = {}
            for name in self.display_names:
                if isinstance(name, str):
                    errors_ret[name] = float(getattr(self, name)) / self.stack_count
            self.loss_stack = 0
            self.matrix_iou_stack = 0
            self.stack_count = 0
            return errors_ret
            
        def eval_iou(self):
            with torch.no_grad():
                self.forward()
                self._bce_iou_loss()
                _ = self.accu_iou()
                self.stack_count += 1
    

    其他的代码就是很固定的pytorch模板代码了。

    代码参考自GitHub


    转载请标明出处


    展开全文
  • 此python代码是一个示例项目,说明如何使用U-Net [1]通过PyTorch( )在医学图像上进行分割。 它是由德国癌症研究中心(DKFZ)的医学图像计算部门开发的。 这也是如何使用我们的其他python包batchgenerators( )和...
  • 使用UNet进行图像分割Pytorch搭建)

    千次阅读 多人点赞 2020-05-23 23:06:49
    使用UNet进行图像分割(利用Pytorch搭建) 文章目录使用UNet进行图像分割(利用Pytorch搭建)简述环境准备代码数据集模型训练结果 简述 这里介绍一下如何使用Pytorch搭建一个UNet图像分割模型,并训练出效果,论文...

    使用UNet进行图像分割(利用Pytorch搭建)

    简述

    这里介绍一下如何使用Pytorch搭建一个UNet的图像分割模型,并训练出效果,论文中的一些trick这里没有使用。

    只包含简单的几个模块,并且大部分代码都有注释。

    环境

    平台:Windows

    python版本:3.7

    Pytorch版本:torch:1.3.0,torchvision:0.4.0

    准备

    在搭建模型之前,我们还需要做些准备工作,那就是搜集数据,这里我提供一份眼球毛细血管数据集和一份VOC2012数据集来进行训练,如果有特殊的需求还是要自己搜集数据。

    VOC数据集是 PASCAL VOC 挑战赛这个比赛使用的数据,里面包含了 目标分类、目标检测、目标分割、姿态识别、行为分类 所需要的数据与标签,我这里只使用分割的部分就可以了。

    VOC数据集下载地址

    官方: https://pjreddie.com/projects/pascal-voc-dataset-mirror/

    或者:https://pan.baidu.com/s/1yfUILB185VvlgQ8bXk536w 提取码:geir

    图像样式

    原始图片





    标签





    毛细血管数据集下载地址

    https://pan.baidu.com/s/1C06ERcImDpXlTneTrVuXPg 提取码:vmq0

    图片样式

    原始图片



    标签



    毛细血管数据集与VOC的用法差不多,就只是读取方式有点区别。

    代码

    总共三个py文件,文件名分别为:dataset(数据集)、unet(网络模型)、train(训练模块)

    数据集

    下载好数据集之后,解压完毕,可以看到这些文件:



    这里我们只使用 JPEGImages 和 SegmentationClass 下的图片来进行语义分割,总共2913张图片。

    首先是数据集部分的代码,没有使用数据增强,VOC部分:

    import os
    import cv2
    import torchvision
    
    from torch.utils.data import Dataset
    from torchvision.utils import save_image
    
    # 简单的数据集,没有进行数据增强
    class Datasets(Dataset):
    
        def __init__(self, path):
            self.path = path
            # 语义分割需要的图片的加载进来,做标签,总共2913张图片
            self.name = os.listdir(os.path.join(path, "SegmentationClass"))
            self.trans = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
    
        def __len__(self):
            return len(self.name)
    
        # 简单的正方形转换,把图片和标签转为正方形
        # 图片会置于中央,两边会填充为黑色,不会失真
        def __trans__(self, img, size):
            # 图片的宽高
            h, w = img.shape[0:2]
            # 需要的尺寸
            _w = _h = size
            # 不改变图像的宽高比例
            scale = min(_h / h, _w / w)
            h = int(h * scale)
            w = int(w * scale)
            # 缩放图像
            img = cv2.resize(img, (w, h), interpolation=cv2.INTER_CUBIC)
            # 上下左右分别要扩展的像素数
            top = (_h - h) // 2
            left = (_w - w) // 2
            bottom = _h - h - top
            right = _w - w - left
            # 生成一个新的填充过的图像,这里用纯黑色进行填充(0,0,0)
            new_img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(0, 0, 0))
            return new_img
    
        def __getitem__(self, index):
            # 拿到的图片
            name = self.name[index]
            # 把标签名的格式改成jpg,与原始图片一致
            name2jpg = name[:-3] + "jpg"
    
            # 所有的原始图片和标签
            img_path = [os.path.join(self.path, i) for i in ("JPEGImages", "SegmentationClass")]
            # 读取原始图片和标签,并转RGB
            img_o = cv2.imread(os.path.join(img_path[0], name2jpg))
            img_l = cv2.imread(os.path.join(img_path[1], name))
            img_o = cv2.cvtColor(img_o, cv2.COLOR_BGR2RGB)
            img_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2RGB)
    
            # 转成网络需要的正方形
            img_o = self.__trans__(img_o, 256)
            img_l = self.__trans__(img_l, 256)
    
            return self.trans(img_o), self.trans(img_l)
    
    
    if __name__ == '__main__':
        i = 1
        # 路径改一下
        dataset = Datasets(r"G:\datasets\VOCdevkit\VOC2012")
        for a, b in dataset:
            print(i)
            # print(a.shape)
            # print(b.shape)
            save_image(a, f"./img/{i}.jpg", nrow=1)
            save_image(b, f"./img/{i}.png", nrow=1)
            i += 1
            if i > 2:
                break
    

    如果是眼球毛细血管的数据集,使用这一份代码:

    import os
    import cv2
    import torchvision
    
    from torch.utils.data import Dataset
    from torchvision.utils import save_image
    
    
    class Datasets(Dataset):
    
        def __init__(self, path):
            self.path = path
            # 语义分割需要的图片的图片和标签
            self.name1 = os.listdir(os.path.join(path, "images"))
            self.name2 = os.listdir(os.path.join(path, "1st_manual"))
            self.trans = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
    
        def __len__(self):
            return len(self.name1)
    
        # 简单的正方形转换,把图片和标签转为正方形
        # 图片会置于中央,两边会填充为黑色,不会失真
        def __trans__(self, img, size):
            # 图片的宽高
            h, w = img.shape[0:2]
            # 需要的尺寸
            _w = _h = size
            # 不改变图像的宽高比例
            scale = min(_h / h, _w / w)
            h = int(h * scale)
            w = int(w * scale)
            # 缩放图像
            img = cv2.resize(img, (w, h), interpolation=cv2.INTER_CUBIC)
            # 上下左右分别要扩展的像素数
            top = (_h - h) // 2
            left = (_w - w) // 2
            bottom = _h - h - top
            right = _w - w - left
            # 生成一个新的填充过的图像,这里用纯黑色进行填充(0,0,0)
            new_img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(0, 0, 0))
            return new_img
    
        def __getitem__(self, index):
            # 拿到的图片和标签
            name1 = self.name1[index]
            name2 = self.name2[index]
            # 图片和标签的路径
            img_path = [os.path.join(self.path, i) for i in ("images", "1st_manual")]
            # 读取原始图片和标签,并转RGB
            img_o = cv2.imread(os.path.join(img_path[0], name1))
            _, img_l = cv2.VideoCapture(os.path.join(img_path[1], name2)).read()
            img_o = cv2.cvtColor(img_o, cv2.COLOR_BGR2RGB)
            img_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2RGB)
    
            # 转成网络需要的正方形
            img_o = self.__trans__(img_o, 256)
            img_l = self.__trans__(img_l, 256)
    
            return self.trans(img_o), self.trans(img_l)
    
    if __name__ == '__main__':
        i = 1
        dataset = Datasets(r"D:\DRIVE\training")
        for a, b in dataset:
            print(i)
            # print(a.shape)
            # print(b.shape)
            save_image(a, f"./img/{i}.jpg", nrow=1)
            save_image(b, f"./img/{i}.png", nrow=1)
            i += 1
            if i > 5:
                break
    

    模型

    根据UNet结构搭建的网络模型:

    """
    这是根据UNet模型搭建出的一个基本网络结构
    输入和输出大小是一样的,可以根据需求进行修改
    """
    import torch
    import torch.nn as nn
    from torch.nn import functional as F
    
    
    # 基本卷积块
    class Conv(nn.Module):
        def __init__(self, C_in, C_out):
            super(Conv, self).__init__()
            self.layer = nn.Sequential(
    
                nn.Conv2d(C_in, C_out, 3, 1, 1),
                nn.BatchNorm2d(C_out),
                # 防止过拟合
                nn.Dropout(0.3),
                nn.LeakyReLU(),
    
                nn.Conv2d(C_out, C_out, 3, 1, 1),
                nn.BatchNorm2d(C_out),
                # 防止过拟合
                nn.Dropout(0.4),
                nn.LeakyReLU(),
            )
    
        def forward(self, x):
            return self.layer(x)
    
    
    # 下采样模块
    class DownSampling(nn.Module):
        def __init__(self, C):
            super(DownSampling, self).__init__()
            self.Down = nn.Sequential(
                # 使用卷积进行2倍的下采样,通道数不变
                nn.Conv2d(C, C, 3, 2, 1),
                nn.LeakyReLU()
            )
    
        def forward(self, x):
            return self.Down(x)
    
    
    # 上采样模块
    class UpSampling(nn.Module):
    
        def __init__(self, C):
            super(UpSampling, self).__init__()
            # 特征图大小扩大2倍,通道数减半
            self.Up = nn.Conv2d(C, C // 2, 1, 1)
    
        def forward(self, x, r):
            # 使用邻近插值进行下采样
            up = F.interpolate(x, scale_factor=2, mode="nearest")
            x = self.Up(up)
            # 拼接,当前上采样的,和之前下采样过程中的
            return torch.cat((x, r), 1)
    
    
    # 主干网络
    class UNet(nn.Module):
    
        def __init__(self):
            super(UNet, self).__init__()
    
            # 4次下采样
            self.C1 = Conv(3, 64)
            self.D1 = DownSampling(64)
            self.C2 = Conv(64, 128)
            self.D2 = DownSampling(128)
            self.C3 = Conv(128, 256)
            self.D3 = DownSampling(256)
            self.C4 = Conv(256, 512)
            self.D4 = DownSampling(512)
            self.C5 = Conv(512, 1024)
    
            # 4次上采样
            self.U1 = UpSampling(1024)
            self.C6 = Conv(1024, 512)
            self.U2 = UpSampling(512)
            self.C7 = Conv(512, 256)
            self.U3 = UpSampling(256)
            self.C8 = Conv(256, 128)
            self.U4 = UpSampling(128)
            self.C9 = Conv(128, 64)
    
            self.Th = torch.nn.Sigmoid()
            self.pred = torch.nn.Conv2d(64, 3, 3, 1, 1)
    
        def forward(self, x):
            # 下采样部分
            R1 = self.C1(x)
            R2 = self.C2(self.D1(R1))
            R3 = self.C3(self.D2(R2))
            R4 = self.C4(self.D3(R3))
            Y1 = self.C5(self.D4(R4))
    
            # 上采样部分
            # 上采样的时候需要拼接起来
            O1 = self.C6(self.U1(Y1, R4))
            O2 = self.C7(self.U2(O1, R3))
            O3 = self.C8(self.U3(O2, R2))
            O4 = self.C9(self.U4(O3, R1))
    
            # 输出预测,这里大小跟输入是一致的
            # 可以把下采样时的中间抠出来再进行拼接,这样修改后输出就会更小
            return self.Th(self.pred(O4))
    
    
    if __name__ == '__main__':
        a = torch.randn(2, 3, 256, 256)
        net = UNet()
        print(net(a).shape)
    

    训练

    简单的训练模块,没有加一些trick:

    """
    训练器模块
    """
    import os
    import unet
    import torch
    import dataset
    import torch.nn as nn
    
    from tqdm import tqdm
    from torch.utils.data import DataLoader
    from torchvision.utils import save_image
    
    # 训练器
    class Trainer:
    
        def __init__(self, path, model, model_copy, img_save_path):
            self.path = path
            self.model = model
            self.model_copy = model_copy
            self.img_save_path = img_save_path
            # 使用的设备
            self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
            # 网络
            self.net = unet.UNet().to(self.device)
            # 优化器,这里用的Adam,跑得快点
            self.opt = torch.optim.Adam(self.net.parameters())
            # 这里直接使用二分类交叉熵来训练,效果可能不那么好
            # 可以使用其他损失,比如DiceLoss、FocalLoss之类的
            self.loss_func = nn.BCELoss()
            # 设备好,batch_size和num_workers可以给大点
            self.loader = DataLoader(dataset.Datasets(path), batch_size=4, shuffle=True, num_workers=4)
    
            # 判断是否存在模型
            if os.path.exists(self.model):
                self.net.load_state_dict(torch.load(model))
                print(f"Loaded{model}!")
            else:
                print("No Param!")
            os.makedirs(img_save_path, exist_ok=True)
    
        # 训练
        def train(self, stop_value):
            epoch = 1
            while True:
                for inputs, labels in tqdm(self.loader, desc=f"Epoch {epoch}/{stop_value}",
                                           ascii=True, total=len(self.loader)):
                    # 图片和分割标签
                    inputs, labels = inputs.to(self.device), labels.to(self.device)
                    # 输出生成的图像
                    out = self.net(inputs)
                    loss = self.loss_func(out, labels)
                    # 后向
                    self.opt.zero_grad()
                    loss.backward()
                    self.opt.step()
    
                    # 输入的图像,取第一张
                    x = inputs[0]
                    # 生成的图像,取第一张
                    x_ = out[0]
                    # 标签的图像,取第一张
                    y = labels[0]
                    # 三张图,从第0轴拼接起来,再保存
                    img = torch.stack([x, x_, y], 0)
                    save_image(img.cpu(), os.path.join(self.img_save_path, f"{epoch}.png"))
                    # print("image save successfully !")
                print(f"\nEpoch: {epoch}/{stop_value}, Loss: {loss}")
                torch.save(self.net.state_dict(), self.model)
                # print("model is saved !")
    
                # 备份
                if epoch % 50 == 0:
                    torch.save(self.net.state_dict(), self.model_copy.format(epoch, loss))
                    print("model_copy is saved !")
                if epoch > stop_value:
                    break
                epoch += 1
    
    
    if __name__ == '__main__':
    	# 路径改一下
        t = Trainer(r"D:\datasets\VOCdevkit\VOC2012", r'./model.plt', r'./model_{}_{}.plt', img_save_path=r'./train_img')
        t.train(300)
    

    结果

    三个模块准备完毕后,放到一起,直接run训练模块就行了!

    由于VOC的数据集较大,所以需要更多的算力,训练时间也比较久,而眼球毛细血管的数据集由于数据量很少,只需要训练很短的时间(1060以上的显卡跑几分钟,可能还不要那么久)就能看到效果。

    稍微训练了一下,可以看到,分割的效果还是不错的(右边的是标签,中间的是输出):

    VOC:



    眼球毛细血管



    由于数据集很少,过拟合是肯定的,所以这里也只是看了一下在训练集上的情况,如果需要真正能用的,还是要增加数据量,增加算力。

    展开全文
  • UNet:使用PyTorch进行语义分割PyTorch中针对高清晰度图像针对Kaggle的自定义实施 。 该模型是从头开始训练的,具有5000张图像(无数据增强),并且在超过100k张测试图像上获得了0.988423(735中的511)的。 ...
  • Pytorch入门——用UNet网络做图像分割

    万次阅读 多人点赞 2019-05-13 19:02:05
    最近看的paper里的pytorch代码太复杂,我之前也没接触过pytorch,遂决定先自己实现一个基础的裸代码,这样走一遍,对跑网络的基本流程和一些常用的基础函数的印象会更深刻。 本文的代码和数据主要来自...

    最近看的paper里的pytorch代码太复杂,我之前也没接触过pytorch,遂决定先自己实现一个基础的裸代码,这样走一遍,对跑网络的基本流程和一些常用的基础函数的印象会更深刻。

    本文的代码和数据主要来自pytorch笔记:05)UNet网络简单实现_Javis486的专栏-CSDN博客

    附上该博主的github地址:https://github.com/JavisPeng/u_net_liver

    并在自己的理解的基础上做了一些改动,以及加了大量注释。

    如有错误,欢迎指出。

     unet.py(实现unet网络)

    import torch.nn as nn
    import torch
    
    class DoubleConv(nn.Module):
        def __init__(self,in_ch,out_ch):
            super(DoubleConv,self).__init__()
            self.conv = nn.Sequential(
                    nn.Conv2d(in_ch,out_ch,3,padding=1),#in_ch、out_ch是通道数
                    nn.BatchNorm2d(out_ch),
                    nn.ReLU(inplace = True),
                    nn.Conv2d(out_ch,out_ch,3,padding=1),
                    nn.BatchNorm2d(out_ch),
                    nn.ReLU(inplace = True)  
                )
        def forward(self,x):
            return self.conv(x)
    
    
    class UNet(nn.Module):
        def __init__(self,in_ch,out_ch):
            super(UNet,self).__init__()
            self.conv1 = DoubleConv(in_ch,64)
            self.pool1 = nn.MaxPool2d(2)#每次把图像尺寸缩小一半
            self.conv2 = DoubleConv(64,128)
            self.pool2 = nn.MaxPool2d(2)
            self.conv3 = DoubleConv(128,256)
            self.pool3 = nn.MaxPool2d(2)
            self.conv4 = DoubleConv(256,512)
            self.pool4 = nn.MaxPool2d(2)
            self.conv5 = DoubleConv(512,1024)
            #逆卷积
            self.up6 = nn.ConvTranspose2d(1024,512,2,stride=2)
            self.conv6 = DoubleConv(1024,512)
            self.up7 = nn.ConvTranspose2d(512,256,2,stride=2)
            self.conv7 = DoubleConv(512,256)
            self.up8 = nn.ConvTranspose2d(256,128,2,stride=2)
            self.conv8 = DoubleConv(256,128)
            self.up9 = nn.ConvTranspose2d(128,64,2,stride=2)
            self.conv9 = DoubleConv(128,64)
            
            self.conv10 = nn.Conv2d(64,out_ch,1)
            
        
        def forward(self,x):
            c1 = self.conv1(x)
            p1 = self.pool1(c1)
            c2 = self.conv2(p1)
            p2 = self.pool2(c2)
            c3 = self.conv3(p2)
            p3 = self.pool3(c3)
            c4 = self.conv4(p3)
            p4 = self.pool4(c4)
            c5 = self.conv5(p4)
            up_6 = self.up6(c5)
            merge6 = torch.cat([up_6,c4],dim=1)#按维数1(列)拼接,列增加
            c6 = self.conv6(merge6)
            up_7 = self.up7(c6)
            merge7 = torch.cat([up_7,c3],dim=1)
            c7 = self.conv7(merge7)
            up_8 = self.up8(c7)
            merge8 = torch.cat([up_8,c2],dim=1)
            c8 = self.conv8(merge8)
            up_9 = self.up9(c8)
            merge9 = torch.cat([up_9,c1],dim=1)
            c9 = self.conv9(merge9)
            c10 = self.conv10(c9)
            
            out = nn.Sigmoid()(c10)#化成(0~1)区间
            return out
            
    

     dataset.py

    import torch.utils.data as data
    import os
    import PIL.Image as Image
    
    #data.Dataset:
    #所有子类应该override__len__和__getitem__,前者提供了数据集的大小,后者支持整数索引,范围从0到len(self)
    
    class LiverDataset(data.Dataset):
        #创建LiverDataset类的实例时,就是在调用init初始化
        def __init__(self,root,transform = None,target_transform = None):#root表示图片路径
            n = len(os.listdir(root))//2 #os.listdir(path)返回指定路径下的文件和文件夹列表。/是真除法,//对结果取整
            
            imgs = []
            for i in range(n):
                img = os.path.join(root,"%03d.png"%i)#os.path.join(path1[,path2[,......]]):将多个路径组合后返回
                mask = os.path.join(root,"%03d_mask.png"%i)
                imgs.append([img,mask])#append只能有一个参数,加上[]变成一个list
            
            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)#400,list[i]有两个元素,[img,mask]
    

    main.py

    import torch
    from torchvision.transforms import transforms as T
    import argparse #argparse模块的作用是用于解析命令行参数,例如python parseTest.py input.txt --port=8080
    import unet
    from torch import optim
    from dataset import LiverDataset
    from torch.utils.data import DataLoader
    
    
    # 是否使用current cuda device or torch.device('cuda:0')
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    x_transform = T.Compose([
        T.ToTensor(),
        # 标准化至[-1,1],规定均值和标准差
        T.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])#torchvision.transforms.Normalize(mean, std, inplace=False)
    ])
    # mask只需要转换为tensor
    y_transform = T.ToTensor()
    
    def train_model(model,criterion,optimizer,dataload,num_epochs=20):
        for epoch in range(num_epochs):
            print('Epoch {}/{}'.format(epoch, num_epochs - 1))
            print('-' * 10)
            dataset_size = len(dataload.dataset)
            epoch_loss = 0
            step = 0 #minibatch数
            for x, y in dataload:# 分100次遍历数据集,每次遍历batch_size=4
                optimizer.zero_grad()#每次minibatch都要将梯度(dw,db,...)清零
                inputs = x.to(device)
                labels = y.to(device)
                outputs = model(inputs)#前向传播
                loss = criterion(outputs, labels)#计算损失
                loss.backward()#梯度下降,计算出梯度
                optimizer.step()#更新参数一次:所有的优化器Optimizer都实现了step()方法来对所有的参数进行更新
                epoch_loss += loss.item()
                step += 1
                print("%d/%d,train_loss:%0.3f" % (step, dataset_size // dataload.batch_size, loss.item()))
            print("epoch %d loss:%0.3f" % (epoch, epoch_loss))
        torch.save(model.state_dict(),'weights_%d.pth' % epoch)# 返回模型的所有内容
        return model
    
    #训练模型
    def train():
        model = unet.UNet(3,1).to(device)
        batch_size = args.batch_size
        #损失函数
        criterion = torch.nn.BCELoss()
        #梯度下降
        optimizer = optim.Adam(model.parameters())#model.parameters():Returns an iterator over module parameters
        #加载数据集
        liver_dataset = LiverDataset("data/train", transform=x_transform, target_transform=y_transform)
        dataloader = DataLoader(liver_dataset, batch_size=batch_size, shuffle=True,num_workers=4)
        # DataLoader:该接口主要用来将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch size封装成Tensor
        # batch_size:how many samples per minibatch to load,这里为4,数据集大小400,所以一共有100个minibatch
        # shuffle:每个epoch将数据打乱,这里epoch=10。一般在训练数据中会采用
        # num_workers:表示通过多个进程来导入数据,可以加快数据导入速度 
        train_model(model,criterion,optimizer,dataloader)
    
    #测试
    def test():
        model = unet.UNet(3,1)
        model.load_state_dict(torch.load(args.weight,map_location='cpu'))
        liver_dataset = LiverDataset("data/val", transform=x_transform, target_transform=y_transform)
        dataloaders = DataLoader(liver_dataset)#batch_size默认为1
        model.eval()
        import matplotlib.pyplot as plt
        plt.ion()
        with torch.no_grad():
            for x, _ in dataloaders:
                y=model(x)
                img_y=torch.squeeze(y).numpy()
                plt.imshow(img_y)
                plt.pause(0.01)
            plt.show()
    
    
    if __name__ == '__main__':
        #参数解析
        parser = argparse.ArgumentParser() #创建一个ArgumentParser对象
        parser.add_argument('action', type=str, help='train or test')#添加参数
        parser.add_argument('--batch_size', type=int, default=4)
        parser.add_argument('--weight', type=str, help='the path of the mode weight file')
        args = parser.parse_args()
        
        if args.action == 'train':
            train()
        elif args.action == 'test':
            test()
    

    数据集自取:https://github.com/yudijiao/UNet

    展开全文
  • Unet实现图像分割(by pytorch

    万次阅读 2018-12-17 16:35:25
    Figure1来自CamVid database,专为目标识别(Object Dection)和图像分割(Image Segmentation)提供训练数据的网站。从图中可以看出,segmentation将图像中不同个体用不同颜色来标记,这里不同的颜色就代表不同的...

    Segmentation

    Figure 1
    Figure1来自CamVid database,专为目标识别(Object Dection)和图像分割(Image Segmentation)提供训练数据的网站。从图中可以看出,segmentation将图像中不同个体用不同颜色来标记,这里不同的颜色就代表不同的分类,例如红色就是分类1,蓝色就是分类2,可以看出,它就是像素级的图像识别(Image Identification)。

    除了自动驾驶之外,图像分割还广泛应用于医学诊断、卫星影像定位、图片合成等领域,本文就以当前kaggle上最热门的segmentation竞赛–TGS Salt Identification Challenge为例来讲解如何应用Unet来解决真实世界的图像分割问题。github: here

    TGS公司通过地震波反射技术绘制出下图所示的3D地质图像,并标记出图像中的盐矿区域,参赛者需要训练用于从岩层中分离盐矿的机器学习模型。
    Figure 2: Images & marks

    Figure 2是trainset中5组image和mark图片,每组的左图为原始地质图像,右图为该图像的分类,称为mark,黑色区域代表一般岩层,白色区域就是盐的分布。segmentation要做的就是训练一个image-to-image的模型,通过对原始图像的学习,生成其对应的mask2,mask则作为target,通过最小化mask和mask2的差距来识别哪些是盐。

    Dataset

    生成dataset的第一步是根据run length数据创建对应的mark图片,因为TGS的trainset里面已经提供了mark图片(mark图片和对应image图片同名),所以我们就不需要额外再创建。

    但要知道的是,并非所有的segmentation dataset都会提供marks,你需要根据数据run length来为images创建相应的marks,run length是如下图rle_mask所示的数据,数据间以空格分隔,两两为一组,每组的第一个数代表flatten后的image vector的起始下标,后一个数代表它所占据的长度,占据区域会填充该目标对应的分类号,如0、1、2…,通过rle_decode()可以将run length转化为mark。
    image.png

    def rle_decode(mask_rle, shape=(101, 101)):
        s = mask_rle.split()
        starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]
        starts -= 1
        ends = starts + lengths
        img = np.zeros(shape[0]*shape[1], dtype=np.uint8)
        for lo, hi in zip(starts, ends):
            img[lo:hi] = 1
        return img.mean()
    

    从Figure 2可以看到,地质图像都是低分辨画质,只有101x101大小,不仅不利于神经网络的卷积计算,也不利于图像识别,所以我们接下来一般会将其resize为128x128大小。

    def resize_img(fn, outp_dir, sz):
      Image.open(fn).resize((sz, sz)).save(outp_dir/fn.name)
    

    Data augmentation是创建dataset的核心,和object dection一样,segmentation一般不会做random crop,我在这个项目中采用水平、垂直翻转和微调光暗的方法来做augmentation。

    aug_tfms = [
        RandomFlip(tfm_y=TfmType.CLASS),
        RandomDihedral(tfm_y=TfmType.CLASS),
    #     RandomRotate(4, tfm_y=TfmType.CLASS),
        RandomLighting(0.07, 0.07, tfm_y=TfmType.CLASS)
    ]
    

    Unet

    paper
    Unet虽然是2015年诞生的模型,但它依旧是当前segmentation项目中应用最广的模型,kaggle上LB排名靠前的选手很多都是使用该模型。
    image.png
    Unet的左侧是convolution layers,右侧则是upsamping layers,convolutions layers中每个pooling layer前一刻的activation值会concatenate到对应的upsamping层的activation值中。

    因为Unet左侧部分和resnet、vgg、inception等模型一样,都是通过卷积层来提取图像特征,所以Unet可以采用resnet/vgg/inception+upsampling的形式来实现,这样做好处是可以利用pretrained的成熟模型来加速Unet的训练,要知道transfer training的效果是非常显著的,我在这个项目中采用的就是resnet34+upsampling的架构。

    class SaveFeatures():
        features=None
        def __init__(self, m): self.hook = m.register_forward_hook(self.hook_fn)
        def hook_fn(self, module, input, output): self.features = output
        def remove(self): self.hook.remove()
    
    
    class UnetBlock(nn.Module):
      def __init__(self, up_in, down_in, n_out, dp=False, ps=0.25):
        super().__init__()
        up_out = down_out = n_out // 2
        self.tr_conv = nn.ConvTranspose2d(up_in, up_out, 2, 2, bias=False)
        self.conv = nn.Conv2d(down_in, down_out, 1, bias=False)
        self.bn = nn.BatchNorm2d(n_out)
        self.dp = dp
        if dp: self.dropout = nn.Dropout(ps, inplace=True)
      
      def forward(self, up_x, down_x):
        x1 = self.tr_conv(up_x)
        x2 = self.conv(down_x)
        x = torch.cat([x1, x2], dim=1)
        x = self.bn(F.relu(x))
        return self.dropout(x) if self.dp else x
    
    
    class Unet34(nn.Module):
      def __init__(self, rn, drop_i=False, ps_i=None, drop_up=False, ps=None):
        super().__init__()
        self.rn = rn
        self.sfs = [SaveFeatures(rn[i]) for i in [2, 4, 5, 6]]
        self.drop_i = drop_i
        if drop_i:
          self.dropout = nn.Dropout(ps_i, inplace=True)
        if ps_i is None: ps_i = 0.1
        if ps is not None: assert len(ps) == 4
        if ps is None: ps = [0.1] * 4
        self.up1 = UnetBlock(512, 256, 256, drop_up, ps[0])
        self.up2 = UnetBlock(256, 128, 256, drop_up, ps[1])
        self.up3 = UnetBlock(256, 64, 256, drop_up, ps[2])
        self.up4 = UnetBlock(256, 64, 256, drop_up, ps[3])
        self.up5 = nn.ConvTranspose2d(256, 1, 2, 2)
      
      def forward(self, x):
        x = F.relu(self.rn(x))
        x = self.dropout(x) if self.drop_i else x
        x = self.up1(x, self.sfs[3].features)
        x = self.up2(x, self.sfs[2].features)
        x = self.up3(x, self.sfs[1].features)
        x = self.up4(x, self.sfs[0].features)
        x = self.up5(x)
        return x[:, 0]
      
      def close(self):
        for o in self.sfs: o.remove()
    

    通过注册nn.register_forward_hook() ,将指定resnet34指定层(2, 4, 5, 6)的activation值保存起来,在upsampling的过程中将它们concatnate到相应的upsampling layer中。upsampling layer中使用ConvTranspose2d()来做deconvolution,ConvTranspose2d()的工作机制和conv2d()正好相反,用于增加feature map的grid size,对deconvolution的计算不是很熟悉的朋友请自行阅读convolution arithmetic tutorial

    Loss

    前文也提到,segmentation本质上是像素级的图像识别,该项目只有两个分类: 盐和岩,和猫vs狗一样是binary classification问题,用binary cross entropy即可,即nn.BCEWithLogitsLoss()。除了BCE,我还尝试了focal loss,准确率提升了0.013。
    Figure 3

    从Figure 3中数学公式可以看出,focal loss就是scale版的cross entropy, − ( 1 − p t ) γ -(1 - p_t)^\gamma (1pt)γ是scale值,这里的scale不是常数而是可学习的weights。focal loss的公式虽然很简单,但在object dection中,focal loss的表现远胜于BCE,其背后的逻辑是:通过scale放大/缩小模型的输出结果,将原本模糊不清的判断确定化。Figure 3,当gamma == 0时,focal loss就相当于corss entropy(CE),如蓝色曲线所示,即使probability达到0.6,loss值还会>= 0.5,就好像是说:“我判断输出不是分类B的概率是60%,但我依旧不能确定它一定不是分类B”。当gamma == 2时,同样是probability达到0.6,loss值接近于0,就好像是说:“我判断输出不是分类B的概率是60%,我认为它一定不是分类B”,这就是scale的威力。

    #https://github.com/marvis/pytorch-yolo2/blob/master/FocalLoss.py
    #https://github.com/unsky/focal-loss
    class FocalLoss2d(nn.Module):
        def __init__(self, gamma=2, size_average=True):
            super(FocalLoss2d, self).__init__()
            self.gamma = gamma
            self.size_average = size_average
    
        def forward(self, logit, target, class_weight=None, type='softmax'):
            target = target.view(-1, 1).long()
            if type=='sigmoid':
                if class_weight is None:
                    class_weight = [1]*2 #[0.5, 0.5]
                prob   = F.sigmoid(logit)
                prob   = prob.view(-1, 1)
                prob   = torch.cat((1-prob, prob), 1)
                select = torch.FloatTensor(len(prob), 2).zero_().cuda()
                select.scatter_(1, target, 1.)
            elif  type=='softmax':
                B,C,H,W = logit.size()
                if class_weight is None:
                    class_weight =[1]*C #[1/C]*C
                logit   = logit.permute(0, 2, 3, 1).contiguous().view(-1, C)
                prob    = F.softmax(logit,1)
                select  = torch.FloatTensor(len(prob), C).zero_().cuda()
                select.scatter_(1, target, 1.)
            class_weight = torch.FloatTensor(class_weight).cuda().view(-1,1)
            class_weight = torch.gather(class_weight, 0, target)
            prob       = (prob*select).sum(1).view(-1,1)
            prob       = torch.clamp(prob,1e-8,1-1e-8)
            batch_loss = - class_weight *(torch.pow((1-prob), self.gamma))*prob.log()
            if self.size_average:
                loss = batch_loss.mean()
            else:
                loss = batch_loss
            return loss
    

    Metric

    image.png

    项目采用取超过probability超过Thresholds:[0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95]的IoU均值作为metric。

    iou_thresholds = np.array([0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95])
    
    def iou(img_true, img_pred):
        img_pred = (img_pred > 0).float()
        i = (img_true * img_pred).sum()
        u = (img_true + img_pred).sum()
        return i / u if u != 0 else u
    
    def iou_metric(imgs_pred, imgs_true):
        num_images = len(imgs_true)
        scores = np.zeros(num_images)
        for i in range(num_images):
            if imgs_true[i].sum() == imgs_pred[i].sum() == 0:
                scores[i] = 1
            else:
                scores[i] = (iou_thresholds <= iou(imgs_true[i], imgs_pred[i])).mean()
        return scores.mean()
    

    Training

    Unet模型训练大致分两步:

    wd = 4e-4
    arch = resnet34
    ps_i = 0.05
    ps = np.array([0.1, 0.1, 0.1, 0.1]) * 1
    m_base = get_base_model(arch, cut, True)
    m = to_gpu(Unet34(m_base, drop_i=True, drop_up=True, ps=ps, ps_i=ps_i))
    models = UnetModel(m)
    learn = ConvLearner(md, models)
    learn.opt_fn = optim.Adam
    learn.crit = nn.BCEWithLogitsLoss()
    learn.metrics = [accuracy_thresh(0.5), miou]
    

    当模型训练到无法通过变化学习率来减少loss值,val loss收敛且有过拟合的可能时,我停止了模型的训练。
    image.png
    image.png
    从结果来看模型需要增加正则化来对抗过拟合,Dropout在Unet的实际应用中并没有起到好的效果,所以需要从data augmentation和weight decay下功夫。

    Run length encoder

    和rle_decode()相反,在将输出提交到kaggle之前,需要通过rle_encode()根据mask生成相应的run length。当然前提是通过downsample()将mask resize回101x101大小。

    def downsample(img, shape):
      if shape == img.shape: return img
      return resize(img, shape, mode='constant', preserve_range=True)
    
    def rle_encode(im):
        pixels = im.flatten()
        pixels = np.concatenate([[0], pixels, [0]])
        runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
        runs[1::2] -= runs[::2]
        return ' '.join(str(x) for x in runs)
    

    TTA(Test Time Augmentation)

    我们可以通过对testset做data augmentation来提高在kaggle上的score。在segmentation项目中应用TTA时要特别注意的是,augmented images会带来augmented outputs,在对这些outputs求均值之前需要先根据相应的transform规则来转化outputs,例如,image1和水平翻转后的image2经模型分别生成mark1和mark2,在计算mark的均值之前需要先将mark2做水平翻转。


    小结

    到此,Unet模型的构建、训练的几个要点:dataset、model、loss和metric等都已经基本讲清了。这篇博文是我在比赛初期写下的,和我最终使用的模型稍有不同,例如新模型增加了5-folds cross validation、scSE network等, 有时间我会再写篇博文介绍排名靠前的参赛者的方案以及相关技术。我参赛的code已经上传到github: here,它可以直接在google colab上运行。

    展开全文
  • pytorchUnet实现医学图像分割

    万次阅读 多人点赞 2019-07-23 11:03:57
    GitHub - Z-XQ/unet_pytorch: using pytorch to implement unet network for liver image segmentation.using pytorch to implement unet network for liver image segmentation. - GitHub - Z-XQ/unet_pytorch: ...
  • 在右上方 ··· 设为星标 ★,第一时间获取资源 仅做学术分享,如有侵权,联系删除 转载于 :52CV 分享一位52CV粉丝Ellis开发的基于PyTorch的专注于医学图像分割的开源库,其支持模型丰富,方便易用。...
  • UNet是一种基于深度学习的图像语义分割方法,尤其在医学图像分割中表现优异。 本课程将手把手地教大家使用labelme图像标注工具制作自己的数据集,生成Mask图像,并使用PyTorchUNet训练自己的数据集,从而能开展...
  • Unet-Segmentation-Pytorch-Nest-of-Unets Implementation of different kinds of Unet Models for Image SegmentationUNet - U-Net: Convolutional Networks for Biomedical Image Segmentation https://arxi...
  • 理论理解参考:【语义分割】评价指标:PA、CPA、MPA、IoU、MIoU详细总结和代码实现(零基础从入门到精通系列!) """ refer to ...
  • Pytorch下实现Unet对自己多类别数据集的语义分割

    千次阅读 热门讨论 2021-01-09 09:23:38
    通常,Unet被普遍应用到医学图像的处理,实现病灶的分割,这里的分割一般只是针对于单类病灶和背景的分割。为此,博主使用pytorch,对含有21类的VOC2012数据集在Unet实现训练和测试,过程如下。 开发环
  • 基于pytorch编写Unet进行细胞结构分割

    千次阅读 2020-01-06 20:53:07
    Unet想必大家都听过它的大名,在医学图像分割方向非常著名的一篇论文,我也是怀着好奇心,以其为我想对分割任务有个初步认识为动机,实现了一下论文中说的那个挑战赛(训练数据少,训练时间短)...
  • 模型(一种流行的图像分割网络)的实现。 这是非常稳定和可配置的,我已经在多个数据集中使用了它,并将其作为几个项目的组成部分。 更新:还支持基于的3-D卷的分段 更新:所有批次归一化操作已被实例归一化所取代...
  • 文章目录基本概念Unet代码实现 基本概念 卷积:计算权重矩阵和扫描所得的数据矩阵的乘积,然后把结果汇总成一个输出像素。 池化:池化过程在一般卷积过程后。池化(pooling) 的本质,其实就是采样。Pooling 对于...
  • 图像分割 (1)普通分割:前景和后景分割。 (2)语义分割:每一类物体分割。 (3)实例分割:每一个实例分割。 却别于目标检测,图像分割师像素级别的分类。最早应用与医疗行业。 1. 应用: (1) 医疗行业:器官...
  • PyTorchUnet网络实现脑肿瘤图像分割

    千次阅读 热门讨论 2019-10-24 13:01:25
    U-Net是一篇基本结构非常好的论文,主要是针对生物医学图片的分割,而且,在今后的许多对医学图像分割网络中,很大一部分会采取U-Net作为网络的主干。相对于当年的,在EM segmentation challenge at ISBI 2012上...
  • Pytorch实现基于U-net的医学图像分割

    千次阅读 2020-04-10 21:26:23
    Pytorch实现基于U-net的医学图像分割 目录结构 代码 Train.py import numpy as np np.set_printoptions(threshold=np.inf) # threshold表示: Total number of array elements to be print(输出数组的元素数目) ...
  • 图像分割是什么? 模型是如何将图像分割的? 深度学习图像分割模型简介 训练Unet完成人像抠图 一.图像分割是什么? 图像分割:将图像每一个像素分类 1.超像素分割:少量超像素代替大量像素,常用于图像预处理 2. 语义...
  • segmentation_models_pytorch是一个基于PyTorch图像分割神经网络 这个新集合由俄罗斯的程序员小哥Pavel Yakubovskiy一手打造,对于图像分割而言简直就是神器般的存在。 github地址:...
  • 一、 配置lanelme数据集标注工具 1.安装labelme 二、实现数据标注后图像分割建立数据集 三、利用FCN实现数据分割
  •  Unet主要用于医学图像的很多论文中,以及Kaggle竞赛和一些其他竞赛中“少类别”的图像分割。从我做实验的经验来说,像VOC这种类别比较多的分割任务,不容易收敛,效果较为差。 2、Resnet34  我们的encode部分...
  • Unet_pytorch-master.zip

    2020-07-24 09:41:30
    Pytorch unet rgb 方便大家学习,别的就没啥了,效果不错,unet众所周知用于医学图像分割,做了一些改良,希望大家喜欢。而且另一个优点是数据集所需比较少。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 987
精华内容 394
关键字:

pytorchunet图像分割

友情链接: DSP本科讲义.rar