一、语义分割
语义分割是一种为图像中的每个像素分配单个标签的技术,允许识别包含相同目标类的图像区域以及区域之间的边界。
该图像已被手动注释。在此图像中,每种颜色代表一个类,图像中的所有像素都被分配一个类。
图像分割常见用例:
- 在图像中标记土地使用和土地覆盖【多类标记】
- 在背景下识别单个类别【eg.分割洪水或火灾】
图像分割的板块主要有几大类:
- 道路和建筑物
- 植被,通常是农作物或树木
- 水、海岸线和洪水
- 火灾、烟雾和燃烧区域
- 山崩
- 冰川
- 大型人造结构,包括太阳能板和游泳池
二、两种提供注释的方式
1.像素级别蒙版(Masks)
掩码文件与所注释的图像大小相同,每个像素值都是 0 到 255 范围内的整数。如果任务是区分背景和洪水区域,则掩码图像将使用像素值 0 来表示背景,并使用另一个值(例如 255)来表示洪水区域。掩码图像附带一个文本文件,其中列出了像素值到类值的映射 — 这通常是转储到 json 文件的 python 字典。
2.多边形边界(Geometries)
使用多边形或边界框来表示图像中的对象区域。这些多边形通常是由多个点(顶点)构成的。
假设我们有一张包含汽车的图像,并且我们想要用一个多边形来标记汽车的轮廓。
-
输入图像: 一张包含汽车的图像。
-
多边形边界: 我们用汽车轮廓上的顶点来表示汽车区域,例如用四个点组成一个多边形来标记汽车的区域。
三、数据库
1.Awesome_Satellite_Benchmark_Datasets 存储库
2.torchgeo
torchgeo
支持的数据集包括 BigEarthNet、Landsat、Sentinel-2、ChesapeakeCVPR 等。
from torchgeo.datasets import Landsat8
dataset = Landsat8(root="path_to_dataset")
3.mmsegmentation
open-mmlab/mmsegmentation: OpenMMLab Semantic Segmentation Toolbox and Benchmark. (github.com)
- 集成数据集:
mmsegmentation
支持自定义和公开的分割数据集,用户可以根据需求将卫星数据集加载进来进行分割任务。 - 使用场景:适合需要快速集成模型和数据集的研究者,特别是已经在使用 MMLab 系列工具的用户。
4.Dubai 数据集
Semantic segmentation of aerial imagery (kaggle.com)
Dubai 数据集是一个适用于学习和研究的小型数据集,通常包含城市区域的遥感图像。它特别适合初学者和小型研究项目。
四、选择和训练模型
1.fastai 中的 unet_learner
Unet 有很多实现方式,我们来看一下fastai 中的 unet_learner【
执行迁移学习和实验不同的预训练编码器变得非常简单,但在分割指标上却非常有限(仅支持 Dice 和 Jaccard 指标)】
unet_learner (dls, arch, normalize=True, n_out=None, pretrained=True,
weights=None, config=None, loss_func=None,
opt_func=<function Adam>, lr=0.001, splitter=None,
cbs=None, metrics=None, path=None, model_dir='models',
wd=None, wd_bn_bias=False, train_bn=True, moms=(0.95, 0.85,
0.95), cut=None, n_in=3, blur=False, blur_final=True,
self_attention=False, y_range=None, last_cross=True,
bottle=False, act_cls=<class
'torch.nn.modules.activation.ReLU'>, init=<function
kaiming_normal_>, norm_type=None)
(1)dls(DataLoaders)
- 说明:传入
dls
是必需的,它包含了训练和验证集的数据加载器。通常可以通过DataBlock
或ImageDataLoaders
创建。
(2)arch(Architecture)
- 说明:表示用于 Unet 的编码器部分的神经网络架构(例如
resnet34
或resnet50
)。这将作为 Unet 的编码器,负责特征提取。
(3)normalize
- 类型:
bool
- 默认值:
True
- 说明:是否对输入数据进行标准化处理。通常情况下,在使用预训练模型时需要进行标准化。
(4)n_out
- 类型:
NoneType
- 默认值:
None
- 说明:输出通道的数量,通常与分割任务的类别数相匹配。如果设置为
None
,fastai 会自动推断输出的大小。
(5)pretrained
- 类型:
bool
- 默认值:
True
- 说明:是否加载预训练的编码器权重。设置为
True
时,编码器将从 ImageNet 等数据集预训练的模型中加载权重。
(6)weights
- 类型:
NoneType
- 默认值:
None
- 说明:你可以提供自定义的预训练权重。如果没有指定,则使用默认的权重(如 ImageNet 权重)。
(7)config
- 类型:
NoneType
- 默认值:
None
- 说明:用于自定义模型配置。如果需要对模型的结构进行更复杂的修改,可以传入自定义的配置字典。
(8)loss_func
- 类型:
NoneType
- 默认值:
None
- 说明:指定损失函数。如果不提供,fastai 会根据任务自动选择合适的损失函数(例如,分类问题会使用交叉熵损失)。
(9)opt_func
- 类型:
function
- 默认值:
Adam
- 说明:指定优化器。默认为
Adam
优化器,但可以替换为其他优化器,如SGD
。
(10)lr(Learning Rate)
- 类型:
float
- 默认值:
0.001
- 说明:初始学习率,控制参数更新的步长。
(11)splitter
- 类型:
NoneType
- 默认值:
None
- 说明:指定如何分割模型的参数以应用不同的学习率。例如,可以为编码器和解码器部分应用不同的学习率。
(12)cbs(Callbacks)
- 类型:
NoneType
- 默认值:
None
- 说明:回调函数列表,用于在训练过程中执行一些额外操作,如早停或学习率调度器。
(13)metrics
- 类型:
NoneType
- 默认值:
None
- 说明:评估模型的指标列表,例如
Dice
或IoU
,用于分割任务。
(14)path
- 类型:
NoneType
- 默认值:
None
- 说明:指定模型保存路径。
(15)model_dir
- 类型:
str
- 默认值:
'models'
- 说明:模型保存目录。
(16)wd(Weight Decay)
- 类型:
NoneType
- 默认值:
None
- 说明:权重衰减,用于正则化,防止过拟合。
(17)wd_bn_bias
- 类型:
bool
- 默认值:
False
- 说明:是否对 BatchNorm 和偏置参数应用权重衰减。
(18)train_bn
- 类型:
bool
- 默认值:
True
- 说明:是否在冻结模型时继续训练 BatchNorm 层。通常在微调时 BatchNorm 层需要继续更新。
(19)moms(Momentum)
- 类型:
tuple
- 默认值:
(0.95, 0.85, 0.95)
- 说明:用于一周期学习率调度的动量值。
(20)cut
- 类型:
NoneType
- 默认值:
None
- 说明:控制模型编码器的截断点,可以指定在哪一层切断编码器并连接到 Unet 的解码器部分。
(21)n_in
- 类型:
int
- 默认值:
3
- 说明:输入图像的通道数,通常 RGB 图像为 3 通道。
(22)blur
- 类型:
bool
- 默认值:
False
- 说明:是否在上采样过程中对解码器输出应用模糊操作。
(23)blur_final
- 类型:
bool
- 默认值:
True
- 说明:是否在最后一层上采样过程中应用模糊操作。
(24)self_attention
- 类型:
bool
- 默认值:
False
- 说明:是否在模型中使用自注意力机制。
(25)y_range
- 类型:
NoneType
- 默认值:
None
- 说明:输出值的范围,用于回归任务中约束模型的输出。
(26)last_cross
- 类型:
bool
- 默认值:
True
- 说明:是否在 Unet 的最后一层使用横向连接,以增强信息流。
(27)bottle
- 类型:
bool
- 默认值:
False
- 说明:是否在编码器中加入瓶颈层,用于减少模型参数数量。
(28)act_cls
- 类型:
type
- 默认值:
ReLU
- 说明:指定激活函数类型,默认为
ReLU
。
(29)init
- 类型:
function
- 默认值:
kaiming_normal_
- 说明:指定权重初始化方法,默认为
kaiming_normal_
。
(30)norm_type
- 类型:
NoneType
- 默认值:
None
- 说明:指定归一化类型,可以选择
BatchNorm
或LayerNorm
。
代码示例:
1) 路径与数据准备
image_P.jpg
是 image.jpg
的语义分割标签图像,其中 _P
后缀表示该图像的分割掩码
path = untar_data(URLs.CAMVID_TINY)
fnames = get_image_files(path/'images')
def label_func(x): return path/'labels'/f'{x.stem}_P{x.suffix}'
codes = np.loadtxt(path/'codes.txt', dtype=str)
untar_data(URLs.CAMVID_TINY)
:从 fastai 的URLs
中下载并解压缩CAMVID_TINY
数据集,这是一个小型的用于语义分割的城市场景数据集。get_image_files(path/'images')
:获取数据集中所有图像的文件路径。label_func(x)
:定义一个函数来获取图像对应的标签文件路径。例如,假设图像名为image.jpg
,标签文件名则为image_P.jpg
。codes
:从codes.txt
文件中读取类别标签,通常是用于表示分割任务中每个类的名称。
2)构建 DataLoaders
dls = SegmentationDataLoaders.from_label_func(path, fnames, label_func, codes=codes)
SegmentationDataLoaders.from_label_func
:构建用于分割任务的数据加载器。通过文件路径和标签函数将图像与其对应的标签进行配对,并进行批量化处理。codes=codes
:指定分割任务的类别标签。
3) 创建 Unet 学习器
learn = unet_learner(dls, models.resnet34, loss_func=CrossEntropyLossFlat(axis=1), y_range=(0,1))
2.功能更完善的分割库:segmentation_models.pytorch。
该库包含了一系列现代模型,并且受益于一个简单的 API,使得快速实验变得容易。Unet 的一个局限是它的区域边界往往比较粗糙,因此通过单一 API 来实验广泛的模型组合是非常有用的。
安装依赖(AI生成):
pip install segmentation-models-pytorch
pip install torch torchvision
代码示例(AI生成):
import torch
import segmentation_models_pytorch as smp
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import VOCSegmentation
# 1. 加载数据集 (以Pascal VOC数据集为例)
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Resize((256, 256))
])
train_dataset = VOCSegmentation(root='./data', year='2012', image_set='train', download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)
# 2. 创建Unet模型,使用ResNet34作为编码器
model = smp.Unet(
encoder_name="resnet34", # 使用ResNet34作为编码器
encoder_weights="imagenet", # 使用ImageNet预训练权重
in_channels=3, # 输入的通道数 (RGB图像)
classes=21 # 输出类别数(与数据集一致)
)
# 3. 定义损失函数和优化器
loss_fn = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
# 4. 训练模型(简单的训练循环)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
for epoch in range(5): # 训练5个epoch
model.train()
for images, masks in train_loader:
images, masks = images.to(device), masks['mask'].to(device)
optimizer.zero_grad() # 清除上次的梯度
outputs = model(images) # 前向传播
loss = loss_fn(outputs, masks) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新模型参数
print(f"Epoch [{epoch+1}/5], Loss: {loss.item()}")
print("训练完成")
3.TorchGeo 中的 SemanticSegmentationTask
安装依赖(AI生成):
pip install torchgeo
代码示例(AI生成):
import torch
from torchgeo.datasets import LandCoverAI
from torchgeo.models import SemanticSegmentationTask
from torch.utils.data import DataLoader
import pytorch_lightning as pl
# 1. 加载遥感数据集(例如 LandCover.ai)
train_dataset = LandCoverAI(split="train", root="data/")
val_dataset = LandCoverAI(split="val", root="data/")
# 2. 创建数据加载器
train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=8, shuffle=False)
# 3. 定义分割任务
task = SemanticSegmentationTask(
model="unet", # 使用 UNet 模型
in_channels=3, # 输入通道数 (RGB)
num_classes=5, # 输出类别数
)
# 4. 使用 PyTorch Lightning 进行训练
trainer = pl.Trainer(max_epochs=10, gpus=1 if torch.cuda.is_available() else 0)
trainer.fit(task, train_dataloaders=train_loader, val_dataloaders=val_loader)
print("训练完成")
4.现代模型SegNeXt
这篇论文提供了在 iSAID 遥感数据集上的模型性能对比。
SegNeXt: Rethinking Convolutional Attention Design for Semantic Segmentation (nips.cc)
五、实例分割
定义:实例分割不仅要区分不同的类别,还需要识别每个类别中的具体实例。
六、全景分割
全景分割结合了语义分割和实例分割的思想,它同时需要为图像中的所有像素进行分类,并区分对象实例。
(A) 原始图像、(B) 语义分割、(C)实例分割、 (D) 全景分割