引言:SAM模型概述与上海交大课程背景
Segment Anything Model (SAM) 是由 Meta AI (Facebook Research) 在 2023 年推出的一个革命性基础模型,旨在实现“万物皆可分割”的目标。它能够根据提示(如点、框或文本)对图像中的任意对象进行零样本(Zero-shot)分割,无需针对特定任务进行微调。
上海交通大学(SJTU)作为国内顶尖高校,其计算机视觉或深度学习相关课程(如 ACM 班、电子信息与电气工程学院的研究生课程)往往会将 SAM 作为前沿模型进行深入剖析。本指南旨在模拟上海交大 SAM 课程的实战教学路径,从零基础出发,逐步深入到核心代码实现、微调技巧及高级应用,帮助你真正掌握这一核心技能。
第一阶段:环境搭建与基础认知
在开始编写代码之前,必须确保你的开发环境配置正确。SAM 模型对 PyTorch 和 GPU 有较高的要求。
1.1 硬件与软件要求
- 硬件:推荐使用 NVIDIA GPU(显存 >= 8GB,推荐 RTX 3090⁄4090 或 A100)。
- 操作系统:Linux (Ubuntu) 或 Windows (WSL2)。
- Python 版本:Python 3.8 - 3.10。
1.2 创建 Conda 环境
为了避免依赖冲突,建议使用 Conda 创建独立环境。
# 创建环境
conda create -n sam_course python=3.9 -y
# 激活环境
conda activate sam_course
# 安装 PyTorch (请根据 CUDA 版本选择,此处以 CUDA 11.8 为例)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 安装基础依赖
pip install opencv-python pycocotools matplotlib onnxruntime onnx
1.3 获取 SAM 源码与模型权重
上海交大课程通常会要求阅读源码,因此我们直接从 GitHub 克隆官方仓库。
git clone https://github.com/facebookresearch/segment-anything.git
cd segment-anything
# 安装 SAM 依赖
pip install -e .
# 下载预训练权重 (官方提供了几种大小的模型,这里下载最常用的 ViT-H)
# 如果下载慢,可以使用国内镜像或手动下载后放入 checkpoints 文件夹
wget https://dl.fbaipublicfiles.com/segment_anything/sam_vit_h_4b8939.pth
第二阶段:核心代码实战——零样本推理
这一阶段是课程的入门部分,重点在于理解 SAM 的推理流程:图像编码 -> 提示编码 -> 掩码解码。
2.1 基础单点/多点分割
SAM 最直观的用法是通过点击(Click)来指定要分割的对象。
代码示例:
import sys
sys.path.append("..") # 确保能找到 segment_anything 模块
from segment_anything import SamPredictor, sam_model_registry
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1. 加载模型
# 这里的 'sam_vit_h_4b8939.pth' 是我们下载的权重路径
# 'vit_h' 是模型类型
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth")
predictor = SamPredictor(sam)
# 2. 加载并处理图像
# 假设你有一张名为 'demo.jpg' 的图片
image = cv2.imread('demo.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 3. 图像编码
# 这一步是 SAM 的核心耗时操作,它将图像转换为嵌入向量(Embedding)
# 一旦编码,可以对不同的提示重复使用
predictor.set_image(image)
# 4. 定义提示(Prompts)
# 假设我们要分割图像中心的一个物体,坐标为 (x, y)
# 注意:坐标是 (H, W) 格式,即 (行, 列)
input_point = np.array([[500, 300]])
input_label = np.array([1]) # 1 表示前景点,0 表示背景点
# 5. 预测掩码
masks, scores, logits = predictor.predict(
point_coords=input_point,
point_labels=input_label,
multimask_output=True # True 返回 3 个候选掩码,False 返回 1 个
)
# 6. 可视化结果
def show_mask(mask, ax, random_color=False):
if random_color:
color = np.concatenate([np.random.random(3), np.array([0.6])], axis=0)
else:
color = np.array([30/255, 144/255, 255/255, 0.6])
h, w = mask.shape[-2:]
mask_image = mask.reshape(h, w, 1) * color.reshape(1, 1, -1)
ax.imshow(mask_image)
def show_points(coords, labels, ax, marker_size=375):
pos_points = coords[labels == 1]
neg_points = coords[labels == 0]
ax.scatter(pos_points[:, 0], pos_points[:, 1], color='green', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)
ax.scatter(neg_points[:, 0], neg_points[:, 1], color='red', marker='*', s=marker_size, edgecolor='white', linewidth=1.25)
plt.figure(figsize=(10, 10))
plt.imshow(image)
show_mask(masks[0], plt.gca()) # 显示得分最高的掩码
show_points(input_point, input_label, plt.gca())
plt.title(f"Score: {scores[0]:.3f}", fontsize=18)
plt.axis('off')
plt.show()
代码解析:
SamPredictor: 这是用于推理的类,它封装了图像编码和提示处理的逻辑。set_image: 执行 ViT (Vision Transformer) 编码,生成图像特征图。这是计算量最大的一步。multimask_output: SAM 会预测 3 个掩码(对应粗略、中等、精细),并给出置信度分数。通常选择masks[0](分数最高的)。
2.2 框(Bounding Box)提示
除了点,SAM 也支持框输入,这在目标检测任务中非常有用。
# 接上面的代码,predictor 已经 set_image 了
# 定义框:[x_min, y_min, x_max, y_max]
input_box = np.array([400, 200, 600, 400])
# 预测
masks, _, _ = predictor.predict(
point_coords=None,
point_labels=None,
box=input_box[None, :], # 需要增加一维变成 (1, 4)
multimask_output=False
)
# 可视化框
import matplotlib.patches as patches
plt.figure(figsize=(10, 10))
plt.imshow(image)
show_mask(masks[0], plt.gca())
# 画框
rect = patches.Rectangle((input_box[0], input_box[1]), input_box[2]-input_box[0], input_box[3]-input_box[1],
linewidth=2, edgecolor='r', facecolor='none')
plt.gca().add_patch(rect)
plt.axis('off')
plt.show()
第三阶段:深入架构——理解 SAM 的三大组件
上海交大课程的深度在于不仅会用,还要懂原理。SAM 由三个主要部分组成:
图像编码器 (Image Encoder):
- 通常是一个 ViT (Vision Transformer)。
- 作用:处理高分辨率图像,提取全局和局部特征。
- 特点:计算量大,但只需运行一次(Once)。
提示编码器 (Prompt Encoder):
- 处理点(Positional Encoding)和框(Embedding)。
- 处理稀疏提示(点/框)和稠密提示(文本/掩码)。
- 特点:轻量级,计算非常快。
掩码解码器 (Mask Decoder):
- 基于 Transformer 的解码器。
- 输入:图像嵌入 + 提示嵌入。
- 输出:掩码 + 置信度分数。
- 特点:非常快,可以在几毫秒内生成掩码。
实战思考:为什么 SAM 能做到“实时”? 因为图像编码可以离线完成,而解码器极快。在实际应用(如视频编辑)中,我们通常先对视频帧进行离线编码,然后在用户交互时实时解码。
第四阶段:进阶应用——自动化与批量处理
在实际工程中,手动点击是不现实的。我们需要结合其他算法(如边缘检测、显著性检测)来自动化生成提示。
4.1 结合 OpenCV 自动化分割
我们可以使用 OpenCV 的阈值处理或边缘检测来获取初始点,然后喂给 SAM。
策略:
- 使用 OpenCV 找到图像中最大的轮廓。
- 计算轮廓的中心点作为 SAM 的正样本点。
- 调用 SAM 获取高质量掩码。
代码示例:
def auto_segment_sam(image_path, predictor):
# 读图
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 预处理:灰度化 + 二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 寻找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
print("未找到轮廓")
return
# 找到最大的轮廓
c = max(contours, key=cv2.contourArea)
# 计算轮廓中心
M = cv2.moments(c)
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
else:
cX, cY = 0, 0
# SAM 推理
predictor.set_image(img_rgb)
input_point = np.array([[cX, cY]])
input_label = np.array([1])
masks, scores, _ = predictor.predict(point_coords=input_point, point_labels=input_label)
# 选择最佳掩码
best_mask = masks[np.argmax(scores)]
return best_mask
# 使用示例
# mask = auto_segment_sam('demo.jpg', predictor)
第五阶段:模型微调 (Fine-tuning) 与 自定义训练
虽然 SAM 是基础模型,但在特定领域(如医疗影像、卫星图)可能表现不佳。上海交大课程通常会涉及如何利用 LoRA (Low-Rank Adaptation) 或全量微调来适配特定数据集。
5.1 数据集准备
你需要准备 COCO 格式或自定义格式的数据。关键在于提示生成策略。
- 正样本:掩码内部的随机点。
- 负样本:掩码外部的随机点。
- 框:掩码的最小外接矩形。
5.2 微调代码逻辑 (PyTorch)
微调的核心是定义 Loss。SAM 的原始训练 Loss 包含:
- Mask Loss: 掩码的 Dice Loss 或 Focal Loss。
- Iou Loss: 预测 IoU 与真实 IoU 的 MSE Loss。
微调伪代码结构:
import torch
from segment_anything.utils.transforms import ResizeLongestSide
# 1. 准备模型
sam = sam_model_registry["vit_h"](checkpoint="sam_vit_h_4b8939.pth")
# 2. 冻结图像编码器 (通常只微调解码器,或者使用 LoRA)
for param in sam.image_encoder.parameters():
param.requires_grad = False
# 3. 定义优化器 (针对解码器)
optimizer = torch.optim.AdamW(sam.mask_decoder.parameters(), lr=1e-4, weight_decay=0.01)
# 4. 数据加载器 (假设已实现)
# train_loader 返回: image, ground_truth_mask, prompts (points/boxes)
resize_transform = ResizeLongestSide(sam.image_encoder.img_size)
def train_step(image, gt_mask, input_points, input_labels):
# 预处理图像
input_image = resize_transform.apply_image_torch(image)
# 图像编码 (如果图像编码器未冻结,这里会更新梯度)
image_embedding = sam.image_encoder(input_image)
# 提示编码
sparse_embeddings, dense_embeddings = sam.prompt_encoder(
points=(input_points, input_labels),
boxes=None,
masks=None
)
# 掩码解码
low_res_masks, iou_predictions = sam.mask_decoder(
image_embeddings=image_embedding,
image_pe=sam.prompt_encoder.get_dense_pe(),
sparse_prompt_embeddings=sparse_embeddings,
dense_prompt_embeddings=dense_embeddings,
multimask_output=True
)
# 计算 Loss (示例:Focal Loss + Dice Loss)
# 这里需要将 low_res_masks 上采样回原尺寸与 gt_mask 比较
# 实际实现中通常使用官方提供的 calculate_loss 函数
# 反向传播
loss = ... # 计算 Loss
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss
微调技巧 (Tips):
- LoRA: 使用
peft库对qkv层添加低秩适配器,显存占用极低,效果接近全量微调。 - 数据增强: 对输入图像进行翻转、旋转,但要注意提示点(Point)也要随之变换。
第六阶段:高级应用与部署
掌握了核心技能后,我们需要考虑如何将 SAM 应用到实际项目中。
6.1 视频对象分割 (Video Object Segmentation)
利用 SAM 处理视频的关键是时序一致性。
- 第一帧:用户手动点击或使用检测器生成掩码。
- 后续帧:使用光流(Optical Flow)或目标追踪算法(如 Tracktor)将第一帧的点坐标传播到后续帧。
- 修正:如果追踪失败,使用 SAM 重新生成。
6.2 3D 医学影像分割
SAM 在医学领域(CT/MRI)应用广泛。
- 切片处理:将 3D Volume 视为一系列 2D 图像。
- 提示策略:在中心切片点击一次,生成掩码,然后传播到相邻切片作为初始提示。
6.3 部署优化
- ONNX 导出:将 PyTorch 模型转换为 ONNX 格式,便于在 C++ 或 TensorRT 环境部署。
- 量化:使用 INT8 量化减小模型体积,加速推理。
# 导出 ONNX 示例
import torch.onnx
# 假设 dummy_input 是符合模型输入的张量
dummy_input = (dummy_image_embedding, dummy_pe, dummy_sparse, dummy_dense)
torch.onnx.export(
sam.mask_decoder,
dummy_input,
"sam_mask_decoder.onnx",
opset_version=11,
input_names=['image_embedding', 'image_pe', 'sparse_prompt_embeddings', 'dense_prompt_embeddings'],
output_names=['masks', 'iou_predictions']
)
总结:从零基础到精通的学习路线
通过上海交大 SAM 课程实战指南的学习,你应该掌握了以下路径:
- 基础层:环境配置、API 调用、点/框推理。
- 原理层:理解 ViT 编码器、提示器、解码器的交互机制。
- 工程层:结合 OpenCV 实现自动化提示生成,处理批量数据。
- 进阶层:掌握 LoRA 微调技术,适配垂直领域数据。
- 应用层:探索视频、3D 医疗及 ONNX 部署。
SAM 不仅仅是一个工具,它是提示工程(Prompt Engineering)在视觉领域的集大成者。精通 SAM,意味着你掌握了如何通过“交互”来引导 AI 理解视觉世界的边界。继续深入阅读源码,尝试复现 segment-anything-2 的最新进展,你的计算机视觉技能将迈上一个新的台阶。
