在三维视觉任务里,对点云或网格进行划分是基础且关键的一步。当我们拿到一份室内扫描得到的3D模型时,常常需要让计算机理解哪些部分属于墙面、哪些属于家具、哪些属于人体。这个过程衍生出两条技术路线:一条只告诉模型“这是什么”,另一条还要告诉模型“这是哪一个”。前者称为语义分割,后者称为实例分割。二者在目标定义、网络结构、标注方式和应用场景上都有本质不同,理解这些差异能帮助团队在立项阶段就选对技术栈。

语义分割的核心原理与典型实现
语义分割的目标是为3D模型中每一个点(或面片)分配一个类别标签。它并不关心场景里有几把椅子,只关心某个点是否属于椅子类。以PointNet系列为例,网络先通过对称函数聚合全局特征,再与点级特征拼接,最终用全连接层输出每点所属类别的概率分布。这种逐点分类的思路让模型具备置换不变性,非常适合无序点云输入。
在代码层面,一个简化的逐点分类头可以写作如下形式。该示例假设我们已经提取了每点特征point_feats,需要映射为类别分数:
import torch
import torch.nn as nn
class SemanticHead(nn.Module):
def __init__(self, in_dim, num_classes):
super(SemanticHead, self).__init__()
# 两层MLP提升非线性表达能力
self.mlp = nn.Sequential(
nn.Linear(in_dim, 128),
nn.ReLU(),
nn.Linear(128, num_classes)
)
def forward(self, point_feats):
# point_feats形状: [B, N, in_dim]
logits = self.mlp(point_feats)
return logits
# 假设批次为2,点数4096,特征维度64,类别数5
feats = torch.randn(2, 4096, 64)
head = SemanticHead(64, 5)
out = head(feats)
print(out.shape) # [2, 4096, 5]
语义分割的优势在于标注相对便宜。标注员只需把点云染成不同颜色或填类别属性,不需要区分单个物体边界。它的局限也同样明显:在机器人抓取任务中,如果场景里有两杯同样的水,语义分割只能给出“杯子类”掩码,无法告诉机械臂该抓左边还是右边那一只。此外,当业务需要计数时,纯语义结果必须配合连通域分析,且对接触遮挡极其敏感。
实例分割如何突破个体区分瓶颈
实例分割在语义信息之上增加了个体identity的维度。主流做法分为两类:基于候选框(类似2D的Mask R-CNN三维扩展)和基于嵌入聚类(如SGPN、ASIS)。嵌入聚类方法让网络为每个点预测一个高维向量,设计损失使同一实例的点向量相互接近、不同实例相互远离,之后用欧氏距离阈值做均值漂移聚类。这样网络输出不仅是类别,还有可聚类的特征空间。
下面给出一个嵌入向量的简化训练目标伪代码,展示如何构造同一实例吸引、不同实例排斥的损失:
import torch
import torch.nn.functional as F
def instance_loss(emb, labels, margin=0.5):
# emb: [N, D] 每点嵌入向量
# labels: [N] 实例编号,背景为-1
loss = 0.0
unique_ids = [i for i in torch.unique(labels) if i >= 0]
for i in unique_ids:
mask_i = (labels == i)
emb_i = emb[mask_i]
mean_i = emb_i.mean(0)
# 同类紧凑度
loss += ((emb_i - mean_i) ** 2).sum()
for j in unique_ids:
if i == j:
continue
mask_j = (labels == j)
mean_j = emb[mask_j].mean(0)
# 异类分离度
loss += F.relu(margin - torch.norm(mean_i - mean_j))
return loss / len(unique_ids)
# 模拟4个点,2个实例
emb = torch.randn(4, 3, requires_grad=True)
labels = torch.tensor([0, 0, 1, 1])
l = instance_loss(emb, labels)
print(l.item())
实例分割的代价是标注昂贵且推理复杂。标注者必须沿物体边界逐点标记属于第几个物体,这在稀疏雷达点云上尤为费力。推理时聚类参数敏感,点多时耗时陡增。但在增强现实里把用户手指从背景手腕中单独分割出来、或仓储中统计同款纸箱数量,这类任务离开实例分割几乎无法完成。架构选型时要权衡是否真需要个体级输出。
从业务场景看二者选型与混合策略
在自动驾驶静态地图构建中,路面、植被、建筑大类划分用语义分割就够了,因为没人关心“第几棵树”。但在工厂零件分拣,传送带上同型号齿轮彼此挨着,就必须实例分割才能逐个抓取。很多团队一开始盲目上实例模型,结果发现标注团队两周只标了总数据量的百分之三,项目直接停摆。反向案例是做室内布局分析却只训了语义模型,交付后客户问“这间办公室有几把椅子”时系统哑火。
实际工业方案常采用两阶段混合:先用语义分割滤除无关类,再对关键类(如“椅”“杯”)跑轻量实例头。这种级联结构降低全场景实例标注压力,也减少聚类搜索空间。下面示意级联推理流程:
def cascade_infer(semantic_model, instance_model, cloud):
# cloud: [N, 3+C]
sem_logits = semantic_model(cloud)
sem_pred = sem_logits.argmax(-1)
# 仅保留椅子类索引假设为2
chair_mask = (sem_pred == 2)
if chair_mask.sum() == 0:
return sem_pred, None
chair_cloud = cloud[chair_mask]
inst_id = instance_model(chair_cloud)
return sem_pred, inst_id
# 伪调用
# sem, inst = cascade_infer(sem_net, inst_net, raw_cloud)
混合策略还要求评测指标分开看。语义用mIoU,实例用mAP或覆盖率。若只报整体准确率会掩盖实例召回塌陷的问题。建议立项时先小样本验证标注可行性与模型精度,再决定纯语义、纯实例还是级联。这样能把算力和人力都花在真实瓶颈上,避免架构过度设计。
3D_model_segmentationsemantic_segmentationinstance_segmentation修改时间:2026-08-13 22:03:21