全卷积神经网络FCN模型Unet 图像分割项目实践

前言

本文基于下面链接的项目, 实践一次基于Unet模型的图像分割.

个人在实现时遇到很多问题, 许多问题在网上并没有找到解答, 所以写下本文, 记录了在实现过程中遇到的问题和大部分代码的注解, 主要从代码逻辑入手, 分析整个实践过程.

我的实现代码放在文章最后, 供大家参考

参考链接

效果展示

在这里插入图片描述

在这里插入图片描述

一些声明

  • 本文实现的代码仅考虑灰度图, 即单通道图

  • 本文中涉及到的数据增强, Unet模型部分, 均为直接调用API, 只讲解具体怎么使用, 涉及具体原理部分需要自己在网上查询

  • 我的实现写成了多个版本, 这里讲解第一个和第四个版本

  • 代码运行在Google Colab上, 这里放一下网上别人的使用教程 ( 使用Colab需要科学上网, 这样比较稳 )

    Colab使用参考链接

整体概述

整个项目需要实现的是对图像的分割, 为二分类问题, 因此我们主要的代码逻辑如下 :

  1. 读入数据集和标签, 进行数据增强(也可不进行数据增强)
  2. 预编译一个loss函数为binary_crossentropy的Unet模型
  3. 使用数据和标签对Unet进行训练, 将训练好的模型进行保存
  4. 加载模型进行预测和图片生成

读入数据

首先明确一下, 我们的图像和标签都是 512 x 512 的图片.

我们首先将图片转换成numpy数组并将多张图片合到一起, 此时变成了三维的数组 : n x 512 x 512

def load_png_files(image_path,start,end): # 加载图片成numpy的list
    im_array = []
    for i in range(start,end+1):
        im = Image.open(os.path.join(image_path,str(i)+'.png'))
        tmp_array = np.array(im) #图片转numpy数组
        tmp_array = tmp_array[np.newaxis,:,:] #numpy数组添加一维,为了把二维图片转成三维图片集
        if len(im_array) == 0:
            im_array = tmp_array
        else:
            im_array = np.concatenate((im_array,tmp_array),axis=0) #将新的图片加入到之前的图片集
    return im_array

由于我们将数据用于训练时还需要一维的通道, 所以使用以下代码增加一维

im_array = im_array[:,:,:,np.newaxis]

注意 : 此时用于训练的数据均是 0~255的 uint 型的数据

预编译模型

model = Unet('resnet34', input_shape = (512, 512, 1), encoder_weights = None)#1代表通道数  model.compile('Adam', loss = 'binary_crossentropy', metrics = ['accuracy'])

这里创建了一个Unet模型并进行了预编译, 具体参数个人感觉比较好理解, 有不懂的话这里提供中文文档供查阅

Keras-model中文文档参考链接

模型训练和保存

model.fit(
    x = im_array,
    y = la_array,
    batch_size = 10, #batch_size代表每次从im_array中的n个图片中选10个来进行训练
    epochs = 8, #操作8次
    validation_split = 0.2, #取训练集中的0.2作为验证集
    shuffle = True
)
model.save("model_v1.h5") #保存模型 

这里是进行模型的训练和保存, 编译部分的参数可从上面的文档中查询到, 模型保存的方法也可以百度到

图像预测和生成

首先我们取5张图片用上述的方法形成 5 x 512 x 512 x 1 的numpy数组测试样本, 进行预测

model = load_model("model_v1.h5") #加载模型
res=model.predict(te_array,5) #对te_array进行预测,5代表图片个数

此时获取到的res数组应该是 float 类型的 5 x 512 x 512 x 1 数组, 此时的res数组代表的是对应像素位置是 0/1 的概率, 因此res是一个概率数组. 由于我们最后生成的图片的每一位是 0~255 的, 所以此时需要转换.

这里提供两种方法进行转换 :

  • 将res数组所有元素乘以255, 再转换成 int 型,然后使用 img.save 方法进行保存

    img = Image.fromarray(im_npp) 
  • 17
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 29
    评论
UNet是一种用于图像分割的深度学习模型,由Olaf Ronneberger、Philipp Fischer、Thomas Brox在2015年提出。它是一种基于卷积神经网络CNN)的卷积网络(FCN),可以用于实现像素级别的图像分割任务。 UNet的结构主要分为两部分,即编码器和解码器。编码器部分采用了VGG16或VGG19等经典的卷积神经网络结构,用于提取图像的高级特征。解码器部分则采用了反卷积(transposed convolution)操作和跳跃式连接(skip connection)的方式,将编码器提取的特征图进行上采样和融合,得到目标的分割结果。 UNet的特点主要有以下几点: 1. 可以在较少的训练数据下获得较好的分割效果; 2. 结构简单,易于理解和实现; 3. 能够处理不同尺度的图像,适用于多种分割任务。 下面是一个简单的Python实现: ```python from keras.models import Model from keras.layers import Input, Conv2D, MaxPooling2D, Dropout, UpSampling2D, concatenate def UNet(input_shape): inputs = Input(input_shape) # 编码器部分 conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(inputs) conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1) pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1) conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2) pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2) conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3) pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool3) conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv4) drop4 = Dropout(0.5)(conv4) pool4 = MaxPooling2D(pool_size=(2, 2))(drop4) conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool4) conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv5) drop5 = Dropout(0.5)(conv5) # 解码器部分 up6 = Conv2D(512, 2, activation='relu', padding='same', kernel_initializer='he_normal')(UpSampling2D(size=(2, 2))(drop5)) merge6 = concatenate([drop4, up6], axis=3) conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge6) conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv6) up7 = Conv2D(256, 2, activation='relu', padding='same', kernel_initializer='he_normal')(UpSampling2D(size=(2, 2))(conv6)) merge7 = concatenate([conv3, up7], axis=3) conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge7) conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv7) up8 = Conv2D(128, 2, activation='relu', padding='same', kernel_initializer='he_normal')(UpSampling2D(size=(2, 2))(conv7)) merge8 = concatenate([conv2, up8], axis=3) conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge8) conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv8) up9 = Conv2D(64, 2, activation='relu', padding='same', kernel_initializer='he_normal')(UpSampling2D(size=(2, 2))(conv8)) merge9 = concatenate([conv1, up9], axis=3) conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge9) conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9) conv9 = Conv2D(2, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9) # 输出层 outputs = Conv2D(1, 1, activation='sigmoid')(conv9) model = Model(inputs=inputs, outputs=outputs) return model ``` 这里使用Keras框架实现了一个基本的UNet模型,用于进行图像分割任务。具体的使用方法可以参考Keras官方文档或者其他相关教程。
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值