Deeplab是图像分割领域非常强大的模型,在前面的博文中我们也进行过很多相应项目的开发实践,感兴趣的话可以自行移步阅读即可:
《基于DeepLabv3Plus开发构建人脸人像分割系统》
《基于DeepLabV3实践路面、桥梁、基建裂缝裂痕分割》
《基于DeepLabV3Plus实现质检划痕图像分割识别系统》
《基于DeepLabV3Plus实现无人机航拍目标分割识别系统》
《python基于DeepLabv3+开发构建河道分割识别系统》
《基于DeepLabV3Plus实现焊缝分割识别系统》
《python基于DeeplabV3Plus开发构建裂缝分割识别系统,并实现裂缝宽度计算测量》
《AI助力隧道等洞体类场景下水泥基建缺陷检测,基于DeeplabV3Plus开发构建洞体场景下壁体建筑缺陷分割系统》
本文的核心目的就是想要基于DeepLabV3Plus来开发构建手机屏幕表面缺陷图像智能分割识别系统,助力工业生产流程上的智能化,首先看下实例效果:
在图像分割领域中有不少优秀出色的网络,DeepLab系列就是其中非常经典的分支之一,在之前的很多项目中陆续都已经有接触到了,在处理图像分割中表现出色。
DeepLabV3Plus是一种用于语义分割任务的深度学习模型,它是DeepLab系列模型的一种改进版本。下面详细解释DeepLabV3Plus的原理:
引入空洞卷积(Dilated Convolution):DeepLabV3Plus利用空洞卷积来扩大感受野,以更好地捕捉图像中的上下文信息。传统的卷积操作只关注局部区域,而空洞卷积通过在卷积核中引入间隔(或称为膨胀率),使得卷积核能够跳过一些像素点,从而扩大感受野。
多尺度金字塔池化(Multi-scale Atrous Spatial Pyramid Pooling, ASPP):DeepLabV3Plus使用ASPP模块来处理不同尺度的信息。ASPP模块使用多个并行的空洞卷积分支,每个分支具有不同的膨胀率,以捕捉来自不同感受野的特征。最后,将这些特征进行汇总并进行融合,以生成更丰富的特征表示。
融合低级特征:为了结合低层次的细节特征,DeepLabV3Plus引入了一个编码器-解码器结构。在编码器部分,通过堆叠多个残差块和降采样操作,提取高层次的语义特征。然而,这会导致空间信息的丢失。因此,在解码器部分,使用反卷积(或上采样)操作来恢复特征图的分辨率,并与对应的低级特征进行融合。
融合注意力机制:为了进一步提升融合的效果,DeepLabV3Plus引入了注意力机制。该机制利用辅助监督信号和空间注意力模块,自适应地对不同的特征图进行加权融合。这样可以使网络更加关注重要的特征区域,提升语义分割的准确性。
DeepLabV3Plus通过引入空洞卷积、多尺度金字塔池化、融合低级特征和注意力机制等改进,提升了语义分割任务的性能。它能够准确地标记图像中每个像素的类别,从而在许多计算机视觉领域(如图像分割、自动驾驶等)中发挥着重要作用。
整体网络结构图如下所示:
接下来看下数据集:
共包含:油污、划痕和斑点这三种常见的手机屏幕缺损类型。
DeepLabV3Plus核心实现如下:
import tensorflow as tf
from keras import backend as K
from keras.layers import (Activation,BatchNormalization,Concatenate,Conv2D,DepthwiseConv2D,Dropout,GlobalAveragePooling2D,Input,Lambda,Softmax,ZeroPadding2D,
)
from keras.models import Model
from modules.mobilenet import mobilenetV2
from modules.Xception import Xceptiondef SepConv_BN(x,filters,prefix,stride=1,kernel_size=3,rate=1,depth_activation=False,epsilon=1e-3,
):if stride == 1:depth_padding = "same"else:kernel_size_effective = kernel_size + (kernel_size - 1) * (rate - 1)pad_total = kernel_size_effective - 1pad_beg = pad_total // 2pad_end = pad_total - pad_begx = ZeroPadding2D((pad_beg, pad_end))(x)depth_padding = "valid"if not depth_activation:x = Activation("relu")(x)x = DepthwiseConv2D((kernel_size, kernel_size),strides=(stride, stride),dilation_rate=(rate, rate),padding=depth_padding,use_bias=False,)(x)x = BatchNormalization(epsilon=epsilon)(x)if depth_activation:x = Activation("relu")(x)x = Conv2D(filters, (1, 1), padding="same", use_bias=False)(x)x = BatchNormalization(epsilon=epsilon)(x)if depth_activation:x = Activation("relu")(x)return xdef Deeplabv3(input_shape, num_classes, alpha=1.0, backbone="mobilenet", downsample_factor=16
):img_input = Input(shape=input_shape)x, atrous_rates, skip1 = MobileNet(img_input, alpha, downsample_factor=downsample_factor)size_before = tf.keras.backend.int_shape(x)b0 = Conv2D(256, (1, 1), padding="same", use_bias=False)(x)b0 = BatchNormalization(epsilon=1e-5)(b0)b0 = Activation("relu")(b0)b1 = SepConv_BN(x, 256, "A!", rate=atrous_rates[0], depth_activation=True, epsilon=1e-5)b2 = SepConv_BN(x, 256, "A2", rate=atrous_rates[1], depth_activation=True, epsilon=1e-5)b3 = SepConv_BN(x, 256, "A3", rate=atrous_rates[2], depth_activation=True, epsilon=1e-5)b4 = GlobalAveragePooling2D()(x)b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4)b4 = Lambda(lambda x: K.expand_dims(x, 1))(b4)b4 = Conv2D(256, (1, 1), padding="same", use_bias=False)(b4)b4 = BatchNormalization(epsilon=1e-5)(b4)b4 = Activation("relu")(b4)b4 = Lambda(lambda x: tf.image.resize_images(x, size_before[1:3], align_corners=True))(b4)x = Concatenate()([b4, b0, b1, b2, b3])x = Conv2D(256, (1, 1), padding="same", use_bias=False)(x)x = BatchNormalization(epsilon=1e-5)(x)x = Activation("relu")(x)x = Dropout(0.1)(x)skip_size = tf.keras.backend.int_shape(skip1)x = Lambda(lambda xx: tf.image.resize_images(xx, skip_size[1:3], align_corners=True))(x)dec_skip1 = Conv2D(48, (1, 1), padding="same", use_bias=False)(skip1)dec_skip1 = BatchNormalization(epsilon=1e-5)(dec_skip1)dec_skip1 = Activation(tf.nn.relu)(dec_skip1)x = Concatenate()([x, dec_skip1])x = SepConv_BN(x, 256, "DC0", depth_activation=True, epsilon=1e-5)x = SepConv_BN(x, 256, "DC1", depth_activation=True, epsilon=1e-5)size_before3 = tf.keras.backend.int_shape(img_input)x = Conv2D(num_classes, (1, 1), padding="same")(x)x = Lambda(lambda xx: tf.image.resize_images(xx, size_before3[1:3], align_corners=True))(x)x = Softmax()(x)model = Model(img_input, x)return model
基于轻量级的MobileNet作为骨干网络来实现特征的提取,即使是在算力较弱的设备下也可以完成训练。在自己的项目中也可以直接整合集成使用。
训练完成后对整体进行可视化,核心实现如下:
from matplotlib import pyplot as pltwith open("train.txt") as f:train_list = [float(one) for one in f.readlines() if one.strip()]
with open("val.txt") as f:val_list = [float(one) for one in f.readlines() if one.strip()]
print("train_list_length: ", len(train_list))
print("val_list_length: ", len(val_list))plt.clf()
plt.figure(figsize=(8, 6))
plt.plot(train_list, label="Train Loss Cruve", c="g")
plt.plot(val_list, label="Val Loss Cruve", c="b")
plt.title("Model Loss Cruve")
plt.savefig("loss.png")
结果如下所示:
这里分为两个阶段进行训练,首先是冷冻训练,主要是基于预训练权重来进行训练,如下:
这一阶段大部分参数被冻结,模型的整体损失比较高,完成预热之后解冻模型参数开启全量训练,如下所示:
这里为了方便使用模型,开发了专用的可视化系统界面,实例推理计算效果如下所示:
直观来看效果还是不错的,有兴趣的话都是可以自己动手实践一下的。