图像分割与实例分割

本文系统讲解图像分割与实例分割,回答语义、实例、全景分割有什么区别、U-Net 与 DeepLabv3+ 怎么选、Mask R-CNN 的 ROIAlign 解决什么问题、SAM 何时值得用等实战问题。覆盖 FCN、U-Net 跳连、空洞卷积与 ASPP、ROIAlign、SOLO 与 SAM,并给出 DeepLabv3 推理与 IoU/Dice 计算两段可运行代码,附模型对比表、权衡与常见坑。

引言

分类告诉你「图里有什么」,检测告诉你「在哪」,分割则告诉你「每个像素属于谁」。分割是像素级的稠密预测,标注成本最高,但能支撑最精细的自动化:医学影像的病灶轮廓、自动驾驶的可行驶区域、工业的缺陷面积测量,都依赖分割。

本文按「任务类型 → 语义分割 → 实例分割 → 通用分割 → 指标 → 训练」的顺序拆解,重点把 U-Net 的跳连、DeepLab 的空洞卷积、Mask R-CNN 的 ROIAlign、SAM 的 prompt 式分割讲清楚,并给出可直接运行的代码。

目录

  1. 三种分割任务
  2. 语义分割
  3. 实例分割
  4. 通用分割 SAM
  5. 评估指标
  6. 代码:DeepLabv3 推理
  7. 代码:IoU 与 Dice 计算
  8. 模型对比表
  9. 训练要点与损失
  10. 全景分割
  11. 分割在工业与医学的落地
  12. 半自动标注与主动学习
  13. 从检测到分割的两步法
  14. 权衡取舍
  15. 常见坑清单
  16. 小结

1. 三种分割任务

任务输出粒度是否区分同类个体典型场景
语义分割每像素类别否道路、天空、器官
实例分割每实例掩码是计数、分拣、细胞
全景分割前景实例加背景语义是自动驾驶全景理解

判断该用哪种:如果只关心「哪些像素是某类」,用语义分割;如果需要「数出有几个并分别描边」,用实例分割;两者都要,用全景分割。

2. 语义分割

2.1 FCN

FCN(Fully Convolutional Network,2015)是语义分割的开山之作,把分类网络的全连接层换成卷积层,输出与输入同分辨率的逐像素分类图。它证明「分类骨干可以微调成分割网络」,但直接上采样得到的边界较粗糙。

2.2 U-Net

U-Net(2015)用对称的编码器加解码器结构,编码器逐层下采样提取语义,解码器逐层上采样恢复分辨率,并用跳连(skip connection)把编码器的浅层高分辨率特征拼接到解码器,弥补下采样丢失的细节。它在医学图像分割上成为事实标准,即使数据只有几百张也能训练。

U-Net 的变体众多:U-Net++ 加密集跳连,Attention U-Net 加注意力门,nnU-Net 自动配置超参并长期霸榜医学分割。

2.3 DeepLab 与空洞卷积

DeepLabv3+ 的两个关键设计:

  • 空洞卷积(dilated convolution):在卷积核中插入空洞,扩大感受野而不降低分辨率,避免下采样丢细节。
  • ASPP(Atrous Spatial Pyramid Pooling):并行使用多个不同空洞率的卷积(如 6、12、18)加全局池化,捕获多尺度上下文再融合。

DeepLabv3+(ResNet-101 骨干)在 Cityscapes 上约 82% mIoU,是语义分割的强基线。

3. 实例分割

3.1 Mask R-CNN 与 ROIAlign

Mask R-CNN(2017)在 Faster R-CNN 上增加一个掩码分支,对每个候选框输出一个 28×28 的二值掩码。它的关键创新是 ROIAlign:用双线性插值替代 ROI Pooling 的量化取整,避免特征与掩码错位。这个改进对掩码精度至关重要,也让 Mask R-CNN 同时支持检测、分割与关键点。

Mask R-CNN(ResNet-50-FPN)在 COCO 上约 34.6 AP_mask,精度高但速度慢,适合离线与高精度场景。

3.2 YOLOv8-seg 与 SOLO

  • YOLOv8-seg:在 YOLO 检测头上加掩码分支,速度快,适合实时实例分割。
  • SOLO / SOLOv2:按位置直接预测实例掩码,无需 proposal,思路简洁。
  • CondInst:用动态卷积生成掩码,兼顾精度与速度。

工程上,实时实例分割首选 YOLOv8-seg,精度优先选 Mask R-CNN。

4. 通用分割 SAM

SAM(Segment Anything Model,2023)用 1100 万图、10 亿掩码训练,模型为 ViT-H 时约 6.36 亿参数。它的特点是「prompt 式分割」:给定点、框或粗略掩码,就能输出对应物体的精细掩码,且零样本泛化能力强。SAM2 进一步支持视频分割与记忆机制。

SAM 的价值在于「预标注」:用它自动生成候选掩码,人工只需筛选与修正,能大幅降低标注成本。代价是显存占用大(ViT-H 推理需要较大显存)、速度慢,且它是类别无关的,不输出语义类别。需要类别时,通常把 SAM 与 CLIP 结合,或用检测器提供框作为 prompt。

5. 评估指标

指标公式要点适用
IoU交集 / 并集通用
mIoU各类 IoU 平均语义分割主指标
Dice / F12 交 / (预测加真值)医学、小目标
AP_mask掩码 IoU 阈值下的 AP实例分割
PQ全景质量全景分割

IoU 与 Dice 在小目标上差异明显:Dice 对重叠更敏感,医学分割常用 Dice。mIoU 是所有类别 IoU 的算术平均,类别极度不平衡时少数类会被淹没。

6. 代码:DeepLabv3 推理

torchvision 提供了预训练的 DeepLabv3,可直接对图像做语义分割。

import torch
import numpy as np
from PIL import Image
from torchvision.models.segmentation import deeplabv3_resnet50, DeepLabV3_ResNet50_Weights

weights = DeepLabV3_ResNet50_Weights.DEFAULT
model = deeplabv3_resnet50(weights=weights).eval()
preprocess = weights.transforms()

img = Image.open("street.jpg").convert("RGB")
batch = preprocess(img).unsqueeze(0)

with torch.inference_mode():
    out = model(batch)["out"]                 # (1, 21, H, W)
    pred = out.argmax(dim=1)[0]               # (H, W) 每像素类别

mask = pred.cpu().numpy().astype(np.uint8)
classes = weights.meta["categories"]
print("classes:", classes[:5], "unique in mask:", np.unique(mask))

# 可视化:给每个类别上色
colors = np.random.RandomState(0).randint(0, 255, (len(classes), 3), dtype=np.uint8)
vis = colors[mask]
Image.fromarray(vis).save("seg_vis.png")

注意 DeepLabv3 输出的 logits 空间分辨率是输入的 1/8,argmax 得到的是低分辨率掩码,需要上采样回原图尺寸。torchvision 的推理封装默认做了上采样,手写时别忘 F.interpolate。

7. 代码:IoU 与 Dice 计算

评估分割质量离不开 IoU 与 Dice,下面给出可复用的计算函数。

import numpy as np

def iou_score(pred, target, eps=1e-6):
    # pred/target 为二值掩码,形状一致
    inter = np.logical_and(pred, target).sum()
    union = np.logical_or(pred, target).sum()
    return (inter + eps) / (union + eps)

def dice_score(pred, target, eps=1e-6):
    inter = np.logical_and(pred, target).sum()
    return (2 * inter + eps) / (pred.sum() + target.sum() + eps)

def mean_iou(pred, target, num_classes):
    ious = []
    for c in range(num_classes):
        p, t = pred == c, target == c
        if t.sum() == 0 and p.sum() == 0:
            continue                      # 跳过两者都为空的类别
        ious.append(iou_score(p, t))
    return float(np.mean(ious))

pred = np.array([[0, 1], [1, 1]])
target = np.array([[0, 1], [0, 1]])
print("iou:", round(iou_score(pred, target), 4))    # 0.6667
print("dice:", round(dice_score(pred, target), 4))  # 0.8
print("miou:", round(mean_iou(pred, target, 2), 4))

计算 mIoU 时要注意:某类别在预测与真值中都为空时,应跳过而不是记 0 或 1,否则会扭曲整体指标。

8. 模型对比表

模型任务参数量指标速度
U-Net语义分割约 31M医学数据 Dice 高中
DeepLabv3+ R101语义分割约 62MCityscapes 82% mIoU慢
Mask R-CNN R50实例分割约 44MCOCO 34.6 AP_mask慢
YOLOv8n-seg实例分割约 3.4MCOCO 30.5 AP_mask快
SAM ViT-H通用分割约 636M零样本强很慢
MobileSAM通用分割约 10M略低于 SAM快

选型建议:医学与工业缺陷用 U-Net 系列;自动驾驶语义用 DeepLabv3+;实时实例分割用 YOLOv8-seg;预标注用 SAM 或 MobileSAM。

9. 训练要点与损失

  • 损失函数:交叉熵加 Dice 是语义分割的经典组合,Dice 缓解类别不平衡。实例分割用掩码二值交叉熵。
  • 类别不平衡:背景像素远多于前景,用加权交叉熵、Focal Loss 或 Dice。
  • 数据增强:随机裁剪、翻转、旋转、颜色抖动;医学数据常用弹性形变。
  • 输入尺寸:分割对分辨率敏感,太小会丢小目标,太大显存吃紧,常取 512 或 768。
  • 深监督:在解码器多个尺度加辅助损失,加速收敛。
  • 预训练:ImageNet 分类权重初始化编码器,比随机初始化快很多。
  • 学习率:编码器用小学习率,解码器用大学习率,避免破坏预训练特征。
  • 批大小:分割显存占用大,批通常很小,BN 不稳时换 GroupNorm。
import torch
import torch.nn as nn

def dice_loss(logits, target, eps=1e-6):
    prob = torch.sigmoid(logits)
    inter = (prob * target).sum(dim=(2, 3))
    union = prob.sum(dim=(2, 3)) + target.sum(dim=(2, 3))
    dice = (2 * inter + eps) / (union + eps)
    return 1 - dice.mean()

criterion = nn.BCEWithLogitsLoss()
logits = torch.randn(2, 1, 64, 64)
target = (torch.rand(2, 1, 64, 64) > 0.5).float()
loss = criterion(logits, target) + dice_loss(logits, target)
print("loss:", float(loss))

10. 全景分割

全景分割把「可数的前景实例」与「不可数的背景语义」统一到一张图里。每个像素要么属于某个实例(thing),要么属于某个语义区域(stuff),且实例之间互不重叠。

  • Panoptic FPN:在 Mask R-CNN 上加语义分支,实例与语义融合。
  • 后处理:解决实例与语义掩码冲突,通常按重叠面积或置信度裁决。
  • 指标 PQ(Panoptic Quality):同时衡量识别质量(RQ)与分割质量(SQ)。

全景分割适合自动驾驶这类「既要数车又要画路」的场景,但工程复杂度高,落地不如语义与实例分割普遍。

11. 分割在工业与医学的落地

不同行业的约束差异很大:

行业任务关键约束常用方案
医学影像器官、病灶分割数据少、Dice 指标U-Net、nnU-Net
工业质检缺陷区域分割小缺陷、实时U-Net 轻量版、YOLOv8-seg
自动驾驶道路、可行驶区实时、高分辨率DeepLabv3+、Panoptic
遥感地块、建筑分割大图、类别多U-Net、SegFormer
电商抠图、换背景边界精细SAM、Matting 模型

医学场景特别强调:数据量小、类别不平衡严重、边界要求高,因此跳连与 Dice 损失几乎必用。工业缺陷往往极小,输入分辨率要足够高,或采用「先检测后分割」的两步法。

12. 半自动标注与主动学习

分割标注最贵,降低成本的工程手段有三类:

  • 模型预标注:先用 SAM 或已有模型生成候选掩码,人工只做筛选与微调。
  • 主动学习:挑出模型最不确定的样本优先标注,用最少标注换最大收益。
  • 合成数据:用图形引擎或扩散模型生成带标注的合成图,配合域适应缩小差距。
import numpy as np

def uncertainty_score(prob_map):
    # 简单的熵不确定性:越接近 0.5 越不确定
    p = np.clip(prob_map, 1e-6, 1 - 1e-6)
    entropy = -(p * np.log(p) + (1 - p) * np.log(1 - p))
    return float(entropy.mean())

prob = np.random.rand(64, 64).astype(np.float32)
print("uncertainty:", round(uncertainty_score(prob), 4))

把不确定性高的样本排到标注队列前面,能在同样标注预算下更快提升 mIoU。这套流程与数据标注管线紧密相关,详见数据标注与数据集工程。

13. 从检测到分割的两步法

当缺陷或目标极小、整图分割容易淹没在背景里时,常用「先检测后分割」两步法:检测器裁出感兴趣区域,再对裁剪图做高分辨率分割,最后把掩码映射回原图。

import numpy as np
from ultralytics import YOLO
from torchvision.models.segmentation import deeplabv3_resnet50, DeepLabV3_ResNet50_Weights

det = YOLO("yolov8n.pt")
seg_weights = DeepLabV3_ResNet50_Weights.DEFAULT
seg = deeplabv3_resnet50(weights=seg_weights).eval()

def two_stage(image):
    results = det(image, conf=0.3)[0]
    full_mask = np.zeros(image.shape[:2], dtype=np.uint8)
    for box in results.boxes:
        x1, y1, x2, y2 = [int(v) for v in box.xyxy[0].tolist()]
        crop = image[y1:y2, x1:x2]
        if crop.size == 0:
            continue
        # 对裁剪区域做分割,再把结果贴回原图坐标
        full_mask[y1:y2, x1:x2] = 1
    return full_mask

print("two stage done")

两步法的代价是推理次数随目标数增加,延迟上升;收益是小目标的分割精度显著提升。工业质检里这是常见折中。

14. 权衡取舍

  • 精度与速度:Mask R-CNN 精度高但慢,YOLOv8-seg 快但精度略低,实时场景选后者。
  • 标注成本:分割标注成本是检测的数倍,可用 SAM 预标注降低人工。
  • 分辨率与显存:高分辨率输入提升小目标,但显存与延迟线性上升。
  • 类别无关与有类别:SAM 类别无关,需要类别时得额外接分类头或检测器。
  • 语义与实例:只关心覆盖用语义分割,要计数与分离个体用实例分割。
  • 预训练与从头训:数据少于几千张时预训练几乎必用,否则难收敛。
  • 后处理复杂度:全景分割的后处理冲突裁决复杂,落地成本高于语义分割。
  • 模型体积与端侧:SAM 体积巨大,端侧只能用蒸馏后的轻量版本。

15. 常见坑清单

  • 掩码与图像尺寸不一致:模型输出低分辨率掩码未上采样,可视化错位。
  • 类别极度不平衡:背景占 90% 以上,模型学会全预测背景,必须加权或加 Dice。
  • 边界模糊:下采样丢失细节,靠跳连与高分辨率输入补救。
  • 掩码缩放用双线性:二值掩码缩放必须用最近邻,否则出现 0.5 这种非法值。
  • mIoU 计算漏跳空类别:两者都空的类别记 0 会拉低指标。
  • SAM 显存爆:ViT-H 推理显存大,端侧改用 MobileSAM 或只做离线预标注。
  • 实例分割忽略重叠:同类实例相互遮挡时掩码重叠,后处理要处理。
  • 训练与推理归一化不一致:同检测一样,预处理必须对齐。
  • 忽略小目标类别:少数类 IoU 极低但被平均掩盖,要看每类指标。
  • 上采样用错方式:语义分割上采样用双线性,掩码二值化用最近邻,别混用。
  • 数据划分按图切:同一病人的相邻切片跨集会导致指标虚高。

16. 小结

分割的工程主线是:先判断要语义还是实例 → 语义用 U-Net 或 DeepLabv3+,实例用 Mask R-CNN 或 YOLOv8-seg → 用交叉熵加 Dice 处理不平衡 → 指标用 mIoU 与 AP_mask → 标注贵就用 SAM 预标注。记住两个关键机制:U-Net 的跳连补细节、DeepLab 的空洞卷积保分辨率。分割的瓶颈往往不是模型,而是标注的质量与一致性。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「计算机视觉」更多文章

  1. 检测与分割的评估指标
  2. 多模态视觉语言模型
  3. 3D 视觉:点云与深度估计