SAM3 笔记1:从环境配置到成功运行

项目需求

最近接了个活,需要自动检测答题卡上的答题区域和分数框。听起来不复杂对吧?扫描卡片,框出答题区域,再标注个分数框,然后匹配一下。本来想着用传统的OpenCV做边缘检测就完事了,但答题卡的样式千奇百怪,模板匹配根本搞不定, OpenCV边缘检测的方法在扫描件上效果良好,但在手机照片上识别准确性显著下降, 如手机拍照引起的透视畸变和光照不均。正好看到Meta刚出了SAM3,号称支持"文本提示检测"——你直接告诉它"找答题区域",它就能找出来。听起来很美好,对吧?
于是我就开始了这趟"踩坑之旅"。

环境搭建

基础环境

我的环境:

  • Windows 10/11
  • Python 3.12
  • CUDA 12.6
  • Miniconda

创建虚拟环境:

conda create -n sam3 python=3.12 -y
d:\miniconda3\Scripts\activate sam3

安装PyTorch

SAM3需要PyTorch 2.7+和CUDA 12.6+:

pip install --upgrade pip
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126

验证安装:

python -c "import torch; print(f'PyTorch: {torch.__version__}'); print(f'CUDA: {torch.cuda.is_available()}')"

应该输出类似:

PyTorch: 2.9.1+cu126
CUDA: True

安装SAM3

克隆仓库并安装:

cd D:\workspace_py\mark
git clone https://github.com/facebookresearch/sam3.git
cd sam3
pip install -e ".[train,dev]"

依赖问题解决

问题1:numpy版本冲突

安装后可能遇到:

ERROR: opencv-python 4.12.0.88 requires numpy>=2, but you have numpy 1.26.0

这是因为SAM3锁定了numpy==1.26,但新版OpenCV要求numpy>=2

解决方案:降级OpenCV

pip install opencv-python==4.8.1.78

问题2:Triton在Windows上的问题(重要)

运行时会遇到:

ModuleNotFoundError: No module named 'triton'

Triton是NVIDIA的GPU加速库,但在Windows上支持很差。直接安装pip install triton通常会失败。

官方解决方案(来自PR #174):

SAM3官方已经在PR #174中修复了这个问题。如果你的版本还没包含这个修复,可以手动应用:

编辑 D:\workspace_py\sam3\sam3\model\edt.py,将triton导入改为可选:

# 在文件开头找到 import triton 的位置,替换为:
try:
    import triton
    import triton.language as tl
    TRITON_AVAILABLE = True
except ImportError:
    TRITON_AVAILABLE = False
    triton = None
    tl = None

# 然后在使用triton的函数前加上检查:
def some_function_using_triton():
    if not TRITON_AVAILABLE:
        raise RuntimeError("Triton is not available. Please install it or use CPU mode.")
    # ... 原有代码

或者直接更新到最新版本(如果PR已合并):

cd D:\workspace_py\mark\sam3
git pull origin main
pip install -e . --force-reinstall

这样在没有Triton的情况下,SAM3会自动降级使用PyTorch的实现,虽然慢一些,但至少能运行。

问题3:其他缺失依赖

陆续可能缺少这些包:

pip install einops transformers accelerate sentencepiece protobuf av decord pillow matplotlib tqdm pyyaml loguru

一次性安装完整依赖列表:

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install opencv-python==4.8.1.78
pip install einops transformers accelerate sentencepiece protobuf av decord pillow matplotlib numpy tqdm pyyaml loguru huggingface-hub

项目结构设计

创建工作目录:

cd D:\workspace_py\mark
mkdir sam3_test
cd sam3_test
mkdir output

最终结构:

sam3_test/
├── detect_answer_areas.py   # 主检测脚本
├── config.py                 # 配置文件
├── INSTALL.md                # 安装说明
├── README.md                 # 项目文档
└── output/                   # 输出目录
    ├── *_vis.jpg            # 可视化结果
    └── *.json               # JSON数据

核心代码实现

配置文件(config.py)

集中管理所有参数:

import os

# 模型配置
MODEL_ID = "facebook/sam3"  # 或使用本地路径
DEVICE = "cuda"  # 或 "cpu"

# 输入输出路径
DEFAULT_INPUT_DIR = r"D:\workspace_py\mark\data"
DEFAULT_OUTPUT_DIR = r"D:\workspace_py\mark\sam3_test\output"

# 检测参数
CONF_THRESHOLD_ANSWER = 0.3      # 答题区域置信度阈值
CONF_THRESHOLD_SCORE_BOX = 0.3   # 分数框置信度阈值
NMS_IOU_THRESHOLD = 0.5          # NMS去重阈值

# 文本提示(支持中英文)
TEXT_PROMPTS = {
    "answer_area": [
        "answer area", 
        "answer region", 
        "答题区域", 
        "作答区域"
    ],
    "score_box": [
        "score box", 
        "score area", 
        "分数框", 
        "得分框"
    ]
}

# 匹配参数
PAIRING_MAX_DISTANCE_RATIO = 1.0     # 最大匹配距离比例
VERTICAL_ALIGNMENT_TOLERANCE = 0.6   # 垂直对齐容差

# 可视化设置
VIS_ANSWER_COLOR = (255, 0, 0)  # 答题区域-红色
VIS_SCORE_COLOR = (0, 255, 0)   # 分数框-绿色
VIS_LINE_WIDTH = 3
VIS_FONT_SIZE = 20

主检测脚本关键部分

1. 正确的模型加载方式

重要提示:SAM3的实际API与README中的示例不完全一致!

错误方式(按README会报错):

from sam3 import Sam3Processor
processor = Sam3Processor.from_pretrained("facebook/sam3")  # ❌ 会失败

正确方式:

def load_model(device: str = "cuda"):
    """加载SAM3模型和处理器"""
    from loguru import logger
    import torch
    
    logger.info(f"加载SAM3模型: {config.MODEL_ID}")
    
    # 检查CUDA可用性
    if device == "cuda" and not torch.cuda.is_available():
        logger.warning("CUDA不可用,切换到CPU")
        device = "cpu"
    
    # 正确的导入路径
    from sam3 import build_sam3_image_model
    from sam3.model.sam3_image_processor import Sam3Processor
    
    # 加载模型
    model = build_sam3_image_model()
    model.eval()
    model = model.to(device)
    
    # 创建处理器
    processor = Sam3Processor(
        model=model,
        resolution=1008,
        device=device,
        confidence_threshold=0.3
    )
    
    logger.info("模型加载成功!")
    return model, processor, device

关键点:

  1. sam3.model.sam3_image_processor导入Sam3Processor
  2. 使用build_sam3_image_model()而不是from_pretrained
  3. 手动将模型移到设备上
  4. 传入model实例来创建processor

2. 推理流程

def run_inference(processor, image, text_prompt):
    """对单张图片进行推理"""
    # 设置图片
    inference_state = processor.set_image(image)
    
    # 使用文本提示进行推理
    output = processor.set_text_prompt(
        state=inference_state, 
        prompt=text_prompt
    )
    
    # 重置状态,准备处理下一张
    processor.reset_all_prompts(inference_state)
    
    return output  # 包含 boxes, scores, masks

3. 检测对象

def detect_objects(processor, image, prompts, conf_threshold=0.3, nms_iou=0.5):
    """检测指定类型的对象"""
    from torchvision.ops import nms
    import torch
    
    all_boxes = []
    all_scores = []
    
    # 尝试多个文本提示
    for prompt in prompts:
        output = run_inference(processor, image, prompt)
        
        if output and len(output) > 0:
            boxes = output[0] if isinstance(output, list) else output
            scores = output[1] if len(output) > 1 else None
            
            all_boxes.extend(boxes)
            all_scores.extend(scores if scores else [0.5] * len(boxes))
    
    if not all_boxes:
        return []
    
    # NMS去重
    boxes_tensor = torch.tensor(all_boxes)
    scores_tensor = torch.tensor(all_scores)
    keep_indices = nms(boxes_tensor, scores_tensor, nms_iou)
    
    # 过滤低置信度
    detections = []
    for idx in keep_indices:
        if scores_tensor[idx] >= conf_threshold:
            detections.append({
                "bbox": boxes_tensor[idx].tolist(),
                "score": float(scores_tensor[idx])
            })
    
    return detections

4. 智能匹配算法

def pair_answer_and_score_boxes(answer_areas, score_boxes):
    """根据空间位置配对答题区域和分数框"""
    import math
    
    def calculate_distance(bbox1, bbox2):
        """计算两个框的中心点距离"""
        center1 = ((bbox1[0] + bbox1[2]) / 2, (bbox1[1] + bbox1[3]) / 2)
        center2 = ((bbox2[0] + bbox2[2]) / 2, (bbox2[1] + bbox2[3]) / 2)
        return math.sqrt((center1[0] - center2[0])**2 + (center1[1] - center2[1])**2)
    
    for answer in answer_areas:
        answer["matched_score_boxes"] = []
        answer_bbox = answer["bbox"]
        
        # 计算答题区域的对角线长度(用于距离阈值)
        diagonal = math.sqrt(
            (answer_bbox[2] - answer_bbox[0])**2 + 
            (answer_bbox[3] - answer_bbox[1])**2
        )
        
        # 找所有候选分数框
        candidates = []
        for score_box in score_boxes:
            score_bbox = score_box["bbox"]
            distance = calculate_distance(answer_bbox, score_bbox)
            
            # 检查距离是否在合理范围内
            max_distance = config.PAIRING_MAX_DISTANCE_RATIO * diagonal
            if distance < max_distance:
                candidates.append({
                    "score_box": score_box,
                    "distance": distance
                })
        
        # 选择距离最近的分数框
        candidates.sort(key=lambda x: x["distance"])
        if candidates:
            matched = candidates[0]
            answer["matched_score_boxes"].append({
                "id": matched["score_box"]["id"],
                "bbox": matched["score_box"]["bbox"],
                "score": matched["score_box"]["score"],
                "distance": float(matched["distance"])
            })
    
    return answer_areas, score_boxes

5. 可视化

def visualize_and_save(image, answer_areas, score_boxes, save_path):
    """绘制检测框并保存"""
    from PIL import ImageDraw, ImageFont
    
    vis_image = image.copy()
    draw = ImageDraw.Draw(vis_image)
    
    try:
        font = ImageFont.truetype("arial.ttf", config.VIS_FONT_SIZE)
    except:
        font = ImageFont.load_default()
    
    # 绘制答题区域(红色)
    for answer in answer_areas:
        bbox = answer["bbox"]
        draw.rectangle(bbox, outline=config.VIS_ANSWER_COLOR, 
                      width=config.VIS_LINE_WIDTH)
        draw.text((bbox[0], bbox[1]-25), f"A{answer['id']}", 
                 fill=config.VIS_ANSWER_COLOR, font=font)
    
    # 绘制分数框(绿色)
    for score_box in score_boxes:
        bbox = score_box["bbox"]
        draw.rectangle(bbox, outline=config.VIS_SCORE_COLOR, 
                      width=config.VIS_LINE_WIDTH)
        draw.text((bbox[0], bbox[1]-25), f"S{score_box['id']}", 
                 fill=config.VIS_SCORE_COLOR, font=font)
    
    vis_image.save(save_path, quality=95)
    return vis_image

Hugging Face Gated Model问题

这是最大的障碍:SAM3是Gated Model(受限模型)

什么是Gated Model

当你尝试下载模型时,会看到:

You need to agree to share your contact information to access this model
Your request is awaiting review from the repository authors.

这意味着需要Meta审批才能访问模型。

申请流程

  1. 访问 https://huggingface.co/facebook/sam3
  2. 登录Hugging Face账号(没有的话先注册)
  3. 点击"Request Access"按钮
  4. 填写申请表单(通常会问使用目的)
  5. 提交后等待审批(通常几小时到几天)
  6. 收到邮件通知后即可下载

配置Hugging Face认证

审批通过后,需要配置访问令牌:

# 安装CLI
pip install "huggingface_hub[cli]"

# 方式1:命令行登录
huggingface-cli login

# 方式2:Python代码登录
python -c "from huggingface_hub import login; login()"

输入你的Access Token(在Hugging Face设置中生成)。

手动下载模型(可选)

如果网络不稳定,可以手动下载:

  1. 访问镜像站:https://hf-mirror.com/facebook/sam3/
  2. 下载文件:
    • config.json
    • sam3.pt(约3-4GB)
  3. 放到本地目录:D:\workspace_py\mark\sam3_models\facebook--sam3\

修改config.py

MODEL_ID = r"D:\workspace_py\mark\sam3_models\facebook--sam3"

脚本会自动检测是本地路径还是在线ID。

运行检测

基本用法

cd D:\workspace_py\mark\sam3_test
python detect_answer_areas.py

默认会处理data/目录下的所有图片,结果保存到output/

自定义参数

python detect_answer_areas.py \
  --input-dir D:\my_data \
  --output-dir D:\my_output \
  --conf-answer 0.3 \
  --conf-score 0.3 \
  --device cuda

可用参数:

  • --input-dir:输入图片目录
  • --output-dir:输出目录
  • --conf-answer:答题区域置信度阈值
  • --conf-score:分数框置信度阈值
  • --device:cuda或cpu
  • --model-id:模型ID或本地路径

输出结果

可视化图片(*_vis.jpg)

  • 红色框:答题区域(标记为A1, A2, A3...)
  • 绿色框:分数框(标记为S1, S2, S3...)

JSON数据(*.json)

{
  "image_name": "exam_sheet_01.jpg",
  "image_size": [2480, 3508],
  "detection_time": "2025-11-22T10:30:45",
  "answer_areas": [
    {
      "id": 1,
      "bbox": [120, 350, 580, 920],
      "score": 0.92,
      "matched_score_boxes": [
        {
          "id": 1,
          "bbox": [620, 380, 720, 450],
          "score": 0.88,
          "distance": 45.6
        }
      ]
    },
    {
      "id": 2,
      "bbox": [120, 980, 580, 1450],
      "score": 0.89,
      "matched_score_boxes": [
        {
          "id": 2,
          "bbox": [620, 1010, 720, 1080],
          "score": 0.85,
          "distance": 48.2
        }
      ]
    }
  ],
  "score_boxes": [
    {
      "id": 1,
      "bbox": [620, 380, 720, 450],
      "score": 0.88,
      "matched_answer_area_id": 1
    },
    {
      "id": 2,
      "bbox": [620, 1010, 720, 1080],
      "score": 0.85,
      "matched_answer_area_id": 2
    }
  ],
  "statistics": {
    "total_answer_areas": 2,
    "total_score_boxes": 2,
    "matched_pairs": 2
  }
}

性能与优化

性能对比

设备 单张耗时 显存/内存 适用场景
CUDA GPU (RTX 3090) 5-10秒 8GB VRAM 生产环境
CUDA GPU (RTX 4060) 8-15秒 6GB VRAM 开发测试
CPU (i7-12700) 40-80秒 16GB RAM 无GPU环境

优化建议

1. 调整置信度阈值

如果出现误检(检测到不该检测的区域):

CONF_THRESHOLD_ANSWER = 0.5  # 提高阈值

如果漏检(应该检测的区域没检测到):

CONF_THRESHOLD_ANSWER = 0.2  # 降低阈值

2. 优化匹配距离

如果分数框离答题区域较远:

PAIRING_MAX_DISTANCE_RATIO = 1.5  # 允许更远距离匹配

3. 批量处理

对于大量图片,可以考虑批量推理(需要修改代码支持):

# 批量推理可以提升30-40%的吞吐量
processor.set_image_batch(images_list)

4. 模型量化

如果显存不足,可以考虑INT8量化(会损失一些精度)。

常见问题

Q1: 导入错误 "cannot import name 'Sam3Processor'"

原因:导入路径不正确

解决:使用正确的导入路径

from sam3.model.sam3_image_processor import Sam3Processor

Q2: Triton相关错误

原因:Windows上Triton支持不好

解决:应用PR #174的修复,或更新到最新版SAM3

Q3: 网络连接失败

原因:Hugging Face连接问题或防火墙

解决

  • 使用镜像站手动下载
  • 配置代理
  • 或等待Gated Model审批后再下载

Q4: 显存不足(CUDA out of memory)

原因:GPU显存不够

解决

  • 使用更小的resolution(默认1008,可改为512)
  • 切换到CPU模式
  • 或升级GPU

Q5: 检测效果不理想

原因:文本提示不够准确或阈值设置不当

解决

  • 尝试不同的文本提示词
  • 调整置信度阈值
  • 考虑针对你的数据微调模型

替代方案

如果SAM3不适合你的场景,可以考虑:

SAM2

  • 优点:无需审批,开箱即用
  • 缺点:不支持文本提示,需要提供点或框
  • 适用:目标位置相对固定的场景

GroundingDINO

  • 优点:支持文本提示,完全开源
  • 缺点:精度可能略低
  • 适用:快速原型开发

YOLOv8 + 自定义训练

  • 优点:速度快,精度可控
  • 缺点:需要标注数据和训练
  • 适用:有标注数据且需要部署的场景

生产部署建议

如果要将系统部署到生产环境:

架构设计

前端Web界面
    ↓
FastAPI后端服务
    ↓
Redis任务队列
    ↓
Celery Worker (GPU服务器)
    ↓
MinIO对象存储 (结果保存)

关键考虑

  1. 模型服务化:用FastAPI包装推理接口
  2. 异步处理:大批量图片用任务队列
  3. GPU池管理:多GPU负载均衡
  4. 结果缓存:相同图片不重复推理
  5. 监控告警:推理时间、错误率等指标

Docker部署

创建Dockerfile

FROM nvidia/cuda:12.6.0-cudnn9-runtime-ubuntu22.04

RUN apt-get update && apt-get install -y python3.12 python3-pip git

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD ["python3", "detect_answer_areas.py"]

项目总结

技术亮点

  • 使用最新的SAM3文本提示功能
  • 完整的检测+匹配pipeline
  • 支持批量处理和可视化
  • 结构化JSON输出
  • 灵活的配置管理

踩过的坑

  1. 依赖冲突:numpy版本、Triton在Windows上的问题
  2. API变更:官方文档与实际代码不一致
  3. 网络问题:Hugging Face连接不稳定
  4. Gated Model:需要审批才能使用

经验教训

  1. 以源码为准:不要完全依赖README
  2. 环境隔离:用Conda避免依赖冲突
  3. 提前准备:模型下载到本地,避免临时卡住
  4. 关注更新:SAM3还在快速迭代,关注GitHub的PR和Issue

参考资源

官方资源

相关技术

替代方案

结语

从环境搭建到成功运行,SAM3答题卡检测系统的搭建过程确实充满挑战。依赖冲突、API变更、Gated Model审批...每一步都可能遇到问题。

但一旦跑通,文本提示的检测方式确实很强大。你不需要训练模型,不需要标注数据,只需要用自然语言描述目标,就能实现检测。这对于答题卡这种"非标准"目标的检测特别有价值。

如果你也在探索SAM3或类似的视觉大模型,希望这篇指南能帮你少走弯路。遇到问题欢迎交流讨论!


提示:由于SAM3是Gated Model,建议先申请访问权限。等待期间可以先用SAM2做原型验证。
注意:SAM3仍在快速迭代中,API可能会变化。建议关注官方GitHub获取最新信息。
更新:Triton问题已在PR #174中修复,建议更新到最新版本。