图像分割的定义
图像分割是计算机视觉领域的任务,旨在将图像划分为不同的区域或物体,使得每个区域具有特定的语义或特征。图像分割的目标是通过将图像划分成有意义的部分,从而更好地理解图像的内容。这有助于识别和分析图像中的对象、场景或结构。
图像分割的分类
-
语义分割(Semantic Segmentation): 将图像中的每个像素标记为特定类别,从而实现对图像的详细语义理解。这对于图像理解、自动驾驶等应用非常有用。
-
实例分割(Instance Segmentation): 与语义分割类似,但不仅标记像素所属的类别,还标记属于不同物体实例的像素。这对于多物体检测和跟踪非常有用。
-
全景分割(panoptic segmentation): 综合语义分割中stuff(不可数物体如天空、草坪等)和实例分割中things(可数物体如人、车等),并给出像素点的唯一标签。
-
边缘检测(Edge Detection): 通过标记图像中的边缘来分割物体。这可以通过应用边缘检测算法,如Canny边缘检测器等来实现。
-
区域生长(Region Growing): 从种子像素开始,根据一定的相似性准则逐渐将相邻像素合并,形成一个区域。
-
图割(Graph Cut): 将图像分割问题转化为图论中的图割问题,通过最小割最大流算法来解决。
-
语义分割方法
-
在图像分割任务中目前,基于深度学习的2D图像语义分割方法有 很多种,从方法特点上可分为3类:基于候选区域的 图像语义分割方法、全监督学习图像语义分割方法 和弱监督学习图像语义分割方法。
-
1.基于候选区域的图像语义分割方法:
-
该方法先是利用区域生成算法在图像中生成一系列自由格式的候选区域(其中的每个候选区域都有可能包含潜在的目标物体),并利 用卷积神经网络(convolutional neural network,CNN) 对候选区域的图像特征和语义信息进行提取,再对这些区域进行分类,之后把关于分类区域的预测转 化成关于像素的预测,像素得分最高的区域即可进行标签。最具代表性的是RCNN算法。
-
2.全监督学习图像语义分割方法
- 基于深度学习的语义分割方法大多是全监督学习模型。全监督学习图像语义分割方法即采用人工提前标注过的像素作为训练样本,语义分割过程为: 1)人工标注数据,即给图像的每个像素预先设定一 个语义标签;2)运用已标注的数据训练神经网络;3) 语义分割。人工标注的像素可以提供大量的细节语 义信息和局部特征,以便高效精准地训练网络。
-
1)FCN算法
- 网络结构图如上图 所 示 ,FCN 将 VGG-16(Visual Geometry Group 16- layer network)算法的全连接层替换为卷积层。一幅 RGB 图像输入卷积神经网络之后,进行一系列的卷 积和池化操作提取特征图,再通过反卷积层对特征 图进行上采样处理,最后进行像素分类并把粗粒度的分割结果转换成细粒度分割结果。FCN成功地将图像分类网络拓展为语义分割网络,可以在较抽象的特征中标记像素的类别,对图像语义分割领域做 出了卓越贡献,但是仍面临着3方面的挑战:池化层 会使得特征图的分辨率下降,也会导致某些像素的 位置信息损失;上采样处理会使得结果模糊,不能很 好地理解图像的细节信息;分割过程离散,不能充分地考虑像素上下文语义信息,故无论是局部特征还 是全局特征利用率均不高。
-
2)基于全卷积的扩张语义分割算法
- 由于全卷 积网络存在上述问题,Google在2014年提出了扩张语 义分割算法,其能够扩大感受野并且不增加参数量, 代 表 算 法 有 DeepLab-V1、DeepLab-V2等。
- Chen和Kokkinos(2014)把卷积神经网络与概率 图级联而成了DeepLab-V1网络,在全卷积网络的末 端加入了 FCCRF(fully connected conditional random field),FCCRF 可以对粗粒度分割图进行边界优化, 同时加入了带孔卷积来增大特征图的感受野。上图 为 DeepLab-V1 的 处 理 流 程 。
-
3)基于全卷积的对称语义分割算法
- 在图像语 义分割领域,对称结构的语义分割网络是解决“池化 处理会使得特征图分辨率会下降、部分像素空间位 置语义信息缺失”问题的一类重要方法。对称结构 的语义分割网络也叫做基于编码器—解码器的网 络,该方法的原理是通过深度学习中的卷积、池化等 步骤组成编码器来提取图像特征,然后通过反卷积、 上池化等步骤组成解码器来恢复图像的一系列像素 特征。
- SegNet 网络的编码器部分与VGG16相同,由13个卷积层和 5个池化层构成,解码器部分由 9个上采样层、13个 卷积层和1个softmax分类器组成, 如上图 5所示。SegNet 网络运算简便,涉及的参数数量和占用的存储空间均较小,但是该网络是通过先验概率来进行像素点的分类,无法预测分割结果的置信度。
- 基于全卷积的对称语义分割网络主 要具有以下优缺点,优点为:还原图像的空间维度和 像素的位置信息,解决池化操作后特征图分辨率降 低的问题;缺点为:网络训练参数过多,计算量大,无 法实现实时分割。
-
4)基于注意力机制的算法。
-
注意力(attention) 机制主要用在自然语言处理领域(natural language processing,NLP),但有研究者开始尝试将注意力机 制用在语义分割上。把注意力机制融入语义分割算 法,突出的贡献就是可以在大量的语义信息中捕获 最关键的部分,更加高效的训练分割网络。自注意 力机制模型的分割效果远远优于通道注意力机制模型。
-
DANet(dual attention network)(Fu 等 ,2019)将 ResNet(带有空洞卷积)作为主干网络,卷积后的特 征图送入两个并行的自注意力网络(位置注意力网 络和通道注意力网络)。位置注意力网络能够获取 特征图上任意两点之间的空间依赖关系,通道注意 力网络能够获取特征图上任意两个通道之间的通道 依赖关系。再将经过注意力机制处理过的两幅特征 图融合,最后进行分割。
-
3弱监督学习图像语义分割方法
-
目前,主流的弱 监督学习标注方法可分为以下 4类:边界框标签、简笔标签、图像级标签和点标签
-
附录
- pytorch模型下的segnet网络图像分割代码示例
-
import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import transforms from torchvision.datasets import YourDataset # 请替换成你的数据集类 # 定义SegNet模型 class SegNet(nn.Module): def __init__(self, num_classes=2): super(SegNet, self).__init__() # 编码器 self.encoder = nn.Sequential( nn.Conv2d(3, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(64, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True) ) self.encoder2 = nn.Sequential( nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(128, 128, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True) ) self.encoder3 = nn.Sequential( nn.Conv2d(128, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2, return_indices=True) ) # 解码器 self.decoder3 = nn.Sequential( nn.MaxUnpool2d(kernel_size=2, stride=2), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(inplace=True) ) self.decoder2 = nn.Sequential( nn.MaxUnpool2d(kernel_size=2, stride=2), nn.Conv2d(256, 128, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(128, 128, kernel_size=3, padding=1), nn.ReLU(inplace=True) ) self.decoder = nn.Sequential( nn.MaxUnpool2d(kernel_size=2, stride=2), nn.Conv2d(128, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(64, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(64, num_classes, kernel_size=1) ) def forward(self, x): size_1 = x.size() x, indices_1 = self.encoder(x) size_2 = x.size() x, indices_2 = self.encoder2(x) size_3 = x.size() x, indices_3 = self.encoder3(x) x = self.decoder3(x, indices_3, output_size=size_3) x = self.decoder2(x, indices_2, output_size=size_2) x = self.decoder(x, indices_1, output_size=size_1) return x # 设置一些超参数 num_classes = 2 batch_size = 32 learning_rate = 0.001 num_epochs = 10 # 准备数据集,你需要替换成你的数据集加载逻辑 # transform = transforms.Compose([transforms.ToTensor(), ...]) # 添加适当的数据转换 # dataset = YourDataset(root='path/to/dataset', transform=transform) # dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True) # 初始化模型、损失函数和优化器 model = SegNet(num_classes=num_classes) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 训练模型 for epoch in range(num_epochs): for images, labels in dataloader: optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() print(f'Epoch {epoch+1}/{num_epochs}, Loss: {loss.item()}')