keras深度学习实战(18)——语义分割详解(代码片段)

盼小辉丶 盼小辉丶     2022-12-07     250

关键词:

Keras深度学习实战(18)——语义分割详解

0. 前言

《使用 U-Net 架构进行图像分割》一节中,我们学习图像分割的基本概念,并且构建了基于 U-Net 网络模型的图像分割模型,在图像中仅包含一个对象时实现图像分割的方法。在本节中,我们将更进一步学习更复杂的语义分割,以便我们能够区分图像中的多个对象。

1. 语义分割基本概念

语义分割 (Semantic Segmentation) 是为了便于图像分析而为图像中的每个像素分配标签的过程,可以将语义分割认为是一种为场景理解提供支持的高层任务,场景理解不仅是语义分割领域需要解决的重难点问题,更是计算机视觉领域的核心问题之一。
语义分割在包括自动驾驶、行人检测和医学影像分析等领域显示出良好的应用前景。语义分割技术主要包括两类:第一类是传统的语义分割算法,首先需要使用传统的图像处理技术提取图像特征,然后通过为每个像素分类来实现图像分割,此类方法的优点在于操作简单,计算难度较小,但其适用性较低,在有外界因素(例如光照、旋转等)的干扰下分割效果不佳;第二类是基于深度学习的语义分割算法,通过使用深度卷积神经网络提取图像特征,然后对每个像素进行分类,借助卷积神经网络强大的拟合和泛化能力,提取到的图像特征能够适应复杂的外部环境变化,能够实现很好的分割效果。
本节中,我们继续使用 U-Net 架构实现交通图像语义分割模型,区分图像中多中不同类别的对象。

2. 模型与数据集分析

2.1 模型训练流程

在本节中,我们将在道路图像数据集中执行语义分割,在构建模型进行训练之前,我们首先对模型训练流程进行简要介绍:

  • 使用与上节相同的数据集,该数据集中包含原始图像以及对应的带有多种类别对象所在位置的标签模板图像
    • 语义图像数据样本示例如下:

  • 将输出蒙版转换为多维数组,其中通道维度与对象所有可能的类别数相同
  • 在此数据集,由于有 12 个不同的对象,因此需要将输出图像转换成尺寸为 224 x 224 x 12 的图像:
    • 通道的值表示在该图像对应位置中是否存在与该通道相对应的对象,例如存在汽车对象,假设其对应通道索引为 0,则在通道 0 上汽车对应的位置上像素值为 1,其它位置像素值为 0,每个通道对应 12 个不同的对象之一,类似于标签的独热编码
  • 构建模型架构训练具有 12 个通道输出的模型,与构建的真实标记相对应
  • 最后,调整输出值,将其转换为可用于可视化的单通道图像(灰度图像):
    • 使用 np.argmax 提取 12 个通道中最大值所在的索引作为单通道图像对应位置的值,例如通道 2 中对应位置像素值为 1 表示该位置存在树木,则在单通道图像中对应位置像素值为 2,这一步骤是上述获取 12 通道标签输出的逆过程

2.2 模型输出

为了更加直观的了解上述模型输出,我们对比原始带有蒙版的标签图像与处理后的 12 通道图像。可以看到,原始图像为 0 的位置,在 12 通道图像中的第 0 个通道相应位置处的值为 1;原始图像为 3 的位置,在 12 通道图像中的第 3 个通道相应位置处的值为 1

原始图像: [[0 0 0 ... 5 5 5]
 [0 0 0 ... 5 5 5]
 [0 0 0 ... 5 5 5]
 ...
 [3 3 3 ... 3 3 3]
 [3 3 3 ... 3 3 3]
 [3 3 3 ... 3 3 3]]
第0通道: [[1. 1. 1. ... 0. 0. 0.]
 [1. 1. 1. ... 0. 0. 0.]
 [1. 1. 1. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]]
第3通道: [[0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 [0. 0. 0. ... 0. 0. 0.]
 ...
 [1. 1. 1. ... 1. 1. 1.]
 [1. 1. 1. ... 1. 1. 1.]
 [1. 1. 1. ... 1. 1. 1.]]

接下来,我们实现上述语义分割模型,所用的数据集与在《使用 U-Net 架构进行图像分割》一节中使用的数据集相同。

3. 实现语义分割模型

3.1 加载数据集

(1) 导入相关库,并读取数据集:

from glob import glob
import os
import numpy as np
import matplotlib.pyplot as plt

dir_data = 'dataset1'
dir_seg = dir_data + '/annotations_prepped_train/'
dir_img = dir_data + '/images_prepped_train/'
all_img_paths = glob(os.path.join(dir_img, '*.png'))
all_mask_paths = glob(os.path.join(dir_seg, '*.png'))

(2) 将图像及其相应的标签存储到列表中,并查看加载的数据集:

import cv2
x = []
y = []
for i in range(len(all_img_paths)):
    img = cv2.imread(all_img_paths[i])
    img = cv2.resize(img, (224, 224))
    mask_path = dir_seg + all_img_paths[i].split('/')[-1]
    img_mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
    img_mask = cv2.resize(img_mask, (224, 224))
    x.append(img)
y.append(img_mask)
plt.subplot(221)
plt.imshow(x[0, :, :, ::-1])
plt.title('Original image')
plt.subplot(222)
plt.imshow(255-y[0], cmap='gray')
plt.title('Masked image')
plt.subplot(223)
plt.imshow(x[2, :, :, ::-1])
plt.subplot(224)
plt.imshow(255-y[2], cmap='gray')
plt.show()

(3) 定义函数 get_segmentation_arr,将单通道的灰度输出图像转换成 12 个通道(由于图像中有 12 个不同类别)。
首先,提取输出中存在的不同对象数:

n_classes = len(set(np.array(y).flatten()))

将蒙版图像转换为 12 通道的独热编码输出,其通道数与数据集中的对象数量相同,原始图像中存在对象的位置在相应通道的位置像素值为 1。可以看到,不同通道对应不同的对象类别:

def get_segmentation_arr(img):
    seg_labels = np.zeros((224, 224, n_classes))
    for c in range(n_classes):
        seg_labels[:, :, c] = (img == c).astype(int)
    return seg_labels

y2 = np.array(y2)
x = np.array(x) / 255
plt.subplot(241)
plt.imshow(x[4, :, :, ::-1])
plt.title('Original image')
for i in range(7):
    plt.subplot(2,4,i+2)
    plt.imshow(y2[4,:,:,i], cmap='gray')
    plt.title('Channle '.format(i))
# plt.imshow(255-y[2], cmap='gray')
plt.show()

3.2 模型构建与训练

(1) 构建模型,将图像输入预训练的 VGG16 模型提取图像特征:

from keras.applications.vgg16 import VGG16
from keras.layers import Input, Conv2D, UpSampling2D, BatchNormalization
from keras.applications.vgg16 import preprocess_input
from keras.layers import concatenate, Dropout
from keras.models import Model
from keras.optimizers import Adam

base_model = VGG16(input_shape=(224, 224, 3), include_top=False, weights='imagenet')
base_model.trainable = False

conv1 = Model(inputs=base_model.input,outputs=base_model.get_layer('block1_conv2').output).output
conv2 = Model(inputs=base_model.input,outputs=base_model.get_layer('block2_conv2').output).output
conv3 = Model(inputs=base_model.input,outputs=base_model.get_layer('block3_conv3').output).output
conv4 = Model(inputs=base_model.input,outputs=base_model.get_layer('block4_conv3').output).output
drop4 = Dropout(0.5)(conv4)
conv5 = Model(inputs=base_model.input,outputs=base_model.get_layer('block5_conv3').output).output
drop5 = Dropout(0.5)(conv5)

(2) 将卷积特征与上采样层相同尺寸的特征串联起来,使用在《使用 U-Net 架构进行图像分割》一节中介绍 U-Net 架构:

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)
conv6 = BatchNormalization()(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)
conv7 = BatchNormalization()(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)
conv8 = BatchNormalization()(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(64, 3, activation = 'relu', padding = 'same',kernel_initializer = 'he_normal')(conv9)
conv9 = BatchNormalization()(conv9)

conv10 = Conv2D(n_classes, 1, activation = 'sigmoid')(conv9)

model = Model(inputs=base_model.input, outputs=conv10)
model.summary()

(3) 冻结预训练的VGG16卷积层参数,然后编译并拟合模型:

for layer in model.layers[:18]:
    layer.trainable = False
    
adam=Adam(1e-3, decay = 1e-6)
model.compile(optimizer=adam, loss='binary_crossentropy', metrics = ['acc'])

history = model.fit(x, y2,
                    epochs=20,
                    batch_size=2,
                    validation_split=0.1)

模型训练过程中,在训练集和测试集上的损失值和准确率变化情况如下所示:

(4) 使用测试图像测试训练完成的语义分割模型性能:

y_pred = model.predict(x[-2:].reshape(2,224,224,3))
y_predi = np.argmax(y_pred, axis=3)
y_testi = np.argmax(y2[-2:].reshape(2,224,224,12), axis=3)

plt.subplot(231)
plt.imshow(x[-1, :, :, ::-1])
plt.title('Original image')
plt.subplot(232)
plt.imshow(255-y[-1], cmap='gray')
plt.title('Masked image')
plt.subplot(233)
plt.imshow(255-y_predi[-1], cmap='gray')
plt.title('Predicted masked image')
plt.subplot(234)
plt.imshow(x[-2, :, :, ::-1])
plt.subplot(235)
plt.imshow(255-y[-2], cmap='gray')
plt.subplot(236)
plt.imshow(255-y_predi[-2], cmap='gray')
plt.show()

模型预测的语义分割结果图像和实际的语义分割图像如下:

结合上图和模型训练过程准确率变化图,我们可以看出训练完成的语义分割模型能够以较高的准确率(约为 90% )识别图像中的语义结构。 ·

小结

语义分割是使计算机理解复杂场景过程中的重要一步,该任务是为了便于图像分析而为图像中的每个像素分配标签的过程。本节首先对语义分割的基本概念进行了简要介绍,阐述了语义分割领域主流的技术现状,并基于 U-Net 架构实现了一个交通图像语义分割模型,用于区分图像中的多种不同类别的对象。

系列链接

Keras深度学习实战(1)——神经网络基础与模型训练过程详解
Keras深度学习实战(2)——使用Keras构建神经网络
Keras深度学习实战(3)——神经网络性能优化技术
Keras深度学习实战(4)——深度学习中常用激活函数和损失函数详解
Keras深度学习实战(5)——批归一化详解
Keras深度学习实战(6)——深度学习过拟合问题及解决方法
Keras深度学习实战(7)——卷积神经网络详解与实现
Keras深度学习实战(8)——使用数据增强提高神经网络性能
Keras深度学习实战(9)——卷积神经网络的局限性
Keras深度学习实战(10)——迁移学习详解
Keras深度学习实战(11)——可视化神经网络中间层输出
Keras深度学习实战(12)——面部特征点检测
Keras深度学习实战(13)——目标检测基础详解
Keras深度学习实战(14)——从零开始实现R-CNN目标检测
Keras深度学习实战(15)——从零开始实现YOLO目标检测
Keras深度学习实战(16)——自编码器详解
Keras深度学习实战(17)——使用U-Net架构进行图像分割

tensorflow2深度学习实战(十三):语义分割算法segnet实战(代码片段)

...开源!这博客是我对学习的一点总结与记录。如果您也对深度学习、机器视觉、算法、Python、C++感兴趣,可以关注我的动态,我们一起学习,一起进步~我的博客地址为:【AI菌】的博客我的Github项目地址是:【AI菌】的Github本教... 查看详情

keras深度学习实战(17)——使用u-net架构进行图像分割(代码片段)

Keras深度学习实战(17)——使用U-Net架构进行图像分割0.前言1.图像分割相关研究1.1图像分割简介1.2图像分割分类1.3U-Net架构在图像分割中的应用2.模型与数据集分析2.1数据集分析2.2模型分析3.使用U-Net架构进行图像分割3.1... 查看详情

keras深度学习实战(23)——dcgan详解与实现(代码片段)

Keras深度学习实战(23)——DCGAN详解与实现0.前言1.使用DCGAN生成手写数字图像2.使用DCGAN生成面部图像2.1模型分析2.2从零开始实现DCGAN生成面部图像小结系列链接0.前言在生成对抗网络(GenerativeAdversarialNetworks,GAN)一节中,我们使用... 查看详情

keras深度学习实战(20)——神经风格迁移详解(代码片段)

Keras深度学习实战(20)——神经风格迁移详解0.前言1.神经风格迁移原理2.模型分析3.使用Keras实现神经风格迁移小结系列链接0.前言在DeepDream图像生成算法的学习中,我们通过修改像素值试图使神经网络中卷积核的激... 查看详情

keras深度学习实战(23)——dcgan详解与实现(代码片段)

Keras深度学习实战(23)——DCGAN详解与实现0.前言1.使用DCGAN生成手写数字图像2.使用DCGAN生成面部图像2.1模型分析2.2从零开始实现DCGAN生成面部图像小结系列链接0.前言在生成对抗网络(GenerativeAdversarialNetworks,GAN)一节中,... 查看详情

keras深度学习实战(20)——deepdream模型详解(代码片段)

Keras深度学习实战(20)——DeepDream模型详解0.前言1.DeepDream的技术原理2.DeepDream模型分析3.DeepDream算法实现3.1数据加载与预处理3.2DeepDream生成模型小结系列链接0.前言在《对抗样本生成》一节中,我们通过略微修改输入... 查看详情

keras深度学习实战(13)——目标检测基础详解(代码片段)

Keras深度学习实战(13)——目标检测基础详解0.前言1.目标检测概念2.创建自定义目标检测数据集2.1windows2.2Ubuntu2.3MacOS3.使用选择性搜索在图像内生成候选区域3.1候选区域3.2选择性搜索3.3使用选择性搜索生成候选区域4.交并... 查看详情

keras深度学习实战(22)——生成对抗网络详解与实现(代码片段)

Keras深度学习实战(22)——生成对抗网络详解与实现0.前言1.生成对抗网络原理2.模型分析3.利用生成对抗网络生成手写数字图像小结系列链接0.前言生成对抗网络(GenerativeAdversarialNetworks,GAN)使用神经网络生成与原始图像集... 查看详情

keras深度学习实战——卷积神经网络详解与实现(代码片段)

Keras深度学习实战(7)——卷积神经网络详解与实现0.前言1.传统神经网络的缺陷1.1构建传统神经网络1.2传统神经网络的缺陷2.使用Python从零开始构建CNN2.1卷积神经网络的基本概念2.2卷积和池化相比全连接网络的优势3.使用... 查看详情

keras深度学习实战(29)——长短时记忆网络详解与实现(代码片段)

Keras深度学习实战(29)——长短时记忆网络详解与实现0.前言1.RNN的局限性2.LSTM模型架构详解2.1LSTM架构2.2LSTM各组成部分与计算流程3.从零开始实现LSTM3.1LSTM模型实现3.2验证输出小结系列链接0.前言长短时记忆网络(LongShortTer... 查看详情

keras深度学习实战(29)——长短时记忆网络详解与实现(代码片段)

Keras深度学习实战(29)——长短时记忆网络详解与实现0.前言1.RNN的局限性2.LSTM模型架构详解2.1LSTM架构2.2LSTM各组成部分与计算流程3.从零开始实现LSTM3.1LSTM模型实现3.2验证输出小结系列链接0.前言长短时记忆网络(LongShortTer... 查看详情

keras深度学习实战——批归一化详解(代码片段)

Keras深度学习实战(5)——批归一化详解0.前言1.批归一化基本概念2.在神经网络训练过程中使用批归一化小结系列链接0.前言批归一化是神经网络中关键性技术之一,使用批归一化可以大幅加快网络的收敛,同时... 查看详情

openmmlabai实战营打卡笔记——06语义分割算法基础

...ff1a;语义分割算法基础目录语义分割1.语义分割基本思路2.深度学习下的语义分割模型3.分割模型的评估方法4.语义分割算法总结语义分割这节课对语义分割算法做了详细的介绍,先介绍了什么是语义分割,接着从语音分割... 查看详情

keras深度学习实战(26)——文档向量详解(代码片段)

Keras深度学习实战(26)——文档向量详解0.前言1.文档向量基本概念2.神经网络模型与数据集分析2.1模型分析2.2数据集介绍3.利用Keras构建神经网络模型生成文档向量小结系列链接0.前言在《从零开始构建单词向量》一节中&... 查看详情

「深度学习一遍过」必修18:基于pytorch的语义分割模型实现(代码片段)

本专栏用于记录关于深度学习的笔记,不光方便自己复习与查阅,同时也希望能给您解决一些关于深度学习的相关问题,并提供一些微不足道的人工神经网络模型设计思路。专栏地址:「深度学习一遍过」必修篇... 查看详情

keras深度学习实战(22)——生成对抗网络详解与实现(代码片段)

Keras深度学习实战(22)——生成对抗网络详解与实现0.前言1.生成对抗网络原理2.模型分析3.利用生成对抗网络生成手写数字图像小结系列链接0.前言生成对抗网络(GenerativeAdversarialNetworks,GAN)使用神经网络生成与原始图像集非常相... 查看详情

opencv-python实战(18)——深度学习简介与入门示例(快来一起推开深度学习的大门吧)(代码片段)

OpenCV-Python实战(18)——深度学习简介与入门示例(快来一起推开深度学习的大门吧)0.前言1.计算机视觉中的深度学习简介1.1深度学习的特点1.2深度学习大爆发2.用于图像分类的深度学习简介3.用于目标检测的深... 查看详情

keras深度学习实战——推荐系统数据编码(代码片段)

Keras深度学习实战——推荐系统数据编码0.前言1.推荐系统数据编码的必要性2.推荐系统数据编码2.1在推荐系统中对用户和电影进行编码2.2数据集介绍2.3用于推荐系统的编码策略2.4实现推荐系统编码模型相关链接0.前言在《自编码... 查看详情