资讯动态

一行 curl 接入 SAM3:把图像分割封装成 RESTful API 的生产级实战

发布时间:2026/10/20 2:51:11 来源:尧图企业网站定制
一行 curl 接入 SAM3把图像分割封装成 RESTful API 的生产级实战【免费下载链接】sam3项目地址: https://ai.gitcode.com/hf_mirrors/facebook/sam3从 SAM 到 SAM 2Meta 用两代模型重新定义了可提示分割Promptable Segmentation而 2025 年末亮相、以 ICLR 2026 论文身份引发热议的 SAM 3则把这场变革从分割一切推进到了理解世界——它不再满足于跟随你点的点、画的框而是提出了可提示概念分割Promptable Concept Segmentation, PCS任务给定一句文本短语或几张示例图就能穷尽式地分割出图像中所有匹配该概念的实例。官方在 SA-CO 基准上收录了 27 万个独特概念是既有评测基准的 50 倍以上SAM 3 在这上面做到了人类表现的 75%–80%。社区里万字硬核解读无需画框、文本提示即分割的教程持续刷屏智源社区的在线演示甚至展示了 30 毫秒量级处理上百个检测对象掩码的能力。但模型能力再强如果只能躺在 Python Notebook 里交互就与生产环境无缘。本文以本仓库hf_mirrors/facebook/sam3即 facebook/sam3 权重仓库的镜像为蓝本从config.json、processor_config.json到README.md逐层拆解 SAM3 的输入输出契约手把手把它封装成一个 FastAPI 服务——最终做到一行 curl 传图、拿回掩码并给出与多模态大模型、检测器串联的三种生产级集成方案。一、先看清仓库里有什么从权重到可调用的模型仓库根目录的文件构成非常典型是一个完整的 HF 权重仓库model.safetensors/sam3.pt模型权重safetensors 与 PyTorch 双格式经 Git LFS 托管实际体积约 3.4 GB / 3.45 GBconfig.json模型结构配置architectures声明为Sam3VideoModelprocessor_config.json图像/视频预处理参数tokenizer_config.json、vocab.json、merges.txtCLIP 风格 BPE 分词器三件套configuration.json{framework: pytorch, task: mask-generation}声明了推理任务类型README.md官方用法文档也是本文所有代码示例的事实来源。真正决定怎么传参、怎么拿结果的是config.json与processor_config.json。打开 config.json 可以看到 SAM3 的五脏六腑视觉主干sam3_vit_model32 层 Transformerpatch 尺寸 14窗口注意力window_size24输入分辨率 1008×1008全局注意力层位于第 7/15/23/31 层文本编码器clip_text_model24 层、hidden size 1024词表 49408max_position_embeddings32——这正是文本提示能直达语义空间的根基检测器DETR 式 encoder-decoder各 6 层num_queries200且use_presence_tokentrue——SAM3 用 200 个查询位点做全实例穷举检测并显式输出概念是否存在的判定几何编码器sam3_geometry_encoder3 层roi_size7负责把点、框等空间提示编码进模型视频追踪器score_threshold_detection0.5、new_det_thresh0.7、high_conf_thresh0.8、recondition_every_nth_frame16等一系列阈值支撑跨帧的检测-关联-追踪流水线。再看 processor_config.json图像统一 resize 到 1008×1008掩码输出分辨率 288×288mask_size归一化采用 mean/std 0.5。而 tokenizer_config.json 显示分词器是CLIPTokenizermax_length32——也就是说文本提示最长 32 个 token生产环境中对超长自然语言描述要先做截断或改写。这些文件共同定义了 SAM3 的接口契约图像 文本/视觉提示 → 掩码 边界框 置信度实例分割之外还附带单通道语义分割输出outputs.semantic_seg。README 中的核心调用范式如下from transformers import Sam3Processor, Sam3Model model Sam3Model.from_pretrained(facebook/sam3).to(device) processor Sam3Processor.from_pretrained(facebook/sam3) inputs processor(imagesimage, textear, return_tensorspt).to(device) outputs model(**inputs) results processor.post_process_instance_segmentation( outputs, threshold0.5, mask_threshold0.5, target_sizesinputs.get(original_sizes).tolist(), )[0] # results 包含: masks / boxes(xyxy 绝对像素坐标) / scores二、SAM3 服务化架构与接口设计模型本身无法响应 HTTP一行 curl 直接打过来更不可能——中间必须有一层服务化封装。生产级的封装要考虑四件事模型常驻3.4 GB 的权重加载一次、常驻显存避免每次请求重新from_pretrained这通常需要数十秒还会反复吃显存预热warm-up服务启动后先跑一次空推理把 CUDA kernel 与 cuDNN autotune 的缓存激活否则第一个真实请求会包含冷启动开销输入输出契约化把 PIL Image、torch.Tensor 这类内部类型收敛为 HTTP 友好形态——输入走multipart/form-data输出走 JSON后处理统一post_process_instance_segmentation负责把模型输出的低分辨率掩码上采样回原图尺寸并二值化这段逻辑必须收敛在服务端客户端只负责传图与收结果。下面是一个可直接落地的 FastAPI 服务骨架完整代码见 README.md 中 API 用法对应的部分此处按生产形态重排import base64 import io import numpy as np import torch from fastapi import FastAPI, File, Form, UploadFile from PIL import Image from transformers import Sam3Model, Sam3Processor app FastAPI(titleSAM3 Segment API, version1.0.0) device cuda if torch.cuda.is_available() else cpu model Sam3Model.from_pretrained(facebook/sam3).to(device).eval() processor Sam3Processor.from_pretrained(facebook/sam3) def mask_to_png(mask: np.ndarray) - str: 把二值掩码转成 PNG 并做 base64便于 JSON 传输 buf io.BytesIO() Image.fromarray(mask.astype(np.uint8) * 255).save(buf, formatPNG) return base64.b64encode(buf.getvalue()).decode() app.post(/segment) async def segment( image: UploadFile File(...), text: str Form(...), threshold: float Form(0.5), mask_threshold: float Form(0.5), ): raw Image.open(io.BytesIO(await image.read())).convert(RGB) inputs processor(imagesraw, texttext, return_tensorspt).to(device) with torch.no_grad(): outputs model(**inputs) results processor.post_process_instance_segmentation( outputs, thresholdthreshold, mask_thresholdmask_threshold, target_sizesinputs.get(original_sizes).tolist(), )[0] masks results[masks].numpy() # [N, H, W] 二值掩码已还原到原图尺寸 boxes results[boxes].numpy().tolist() # xyxy 绝对像素坐标 scores results[scores].numpy().tolist() return { num_objects: len(scores), boxes: boxes, scores: scores, masks_png_base64: [mask_to_png(m) for m in masks], }接口设计上有一个容易被忽略的细节掩码的序列化方式。二进制掩码直接塞 JSON 会爆炸一张 1080p 图的掩码就是 200 万字节这里采用 PNG 压缩 base64单张掩码通常可压缩到几 KB 到几十 KB是图像分割服务最实用的折中方案。如果需要框提示视觉 prompt可以再暴露一个/segment_box端点直接把 README 里的input_boxes/input_boxes_labels参数化app.post(/segment_box) async def segment_box( image: UploadFile File(...), box: str Form(...), # x1,y1,x2,y2 positive: int Form(1), # 1正框(要分割), 0负框(要排除) ): raw Image.open(io.BytesIO(await image.read())).convert(RGB) x1, y1, x2, y2 map(int, box.split(,)) inputs processor( imagesraw, input_boxes[[[x1, y1, x2, y2]]], input_boxes_labels[[positive]], return_tensorspt, ).to(device) with torch.no_grad(): outputs model(**inputs) results processor.post_process_instance_segmentation( outputs, threshold0.5, mask_threshold0.5, target_sizesinputs.get(original_sizes).tolist(), )[0] return {num_objects: len(results[scores]), boxes: results[boxes].tolist(), scores: results[scores].tolist()}对应视频场景README 展示了另一套会话化sessionAPISam3VideoProcessor.init_video_session建会话 →add_text_prompt注入文本概念 →Sam3VideoModel.propagate_in_video_iterator逐帧传播。服务化时需要用字典维护 session 句柄视频特征缓存在 session 里并将上传视频转成帧序列。一个最小实现如下sessions: dict[str, object] {} app.post(/video/segment) async def video_segment( video: UploadFile File(...), text: str Form(...), max_frames: int Form(50), ): video_bytes await video.read() frames, _ load_video_from_bytes(video_bytes) # 将 MP4 解码为帧列表 session processor.init_video_session( videoframes, inference_devicedevice, processing_devicecpu, video_storage_devicecpu, dtypetorch.bfloat16, ) session processor.add_text_prompt(inference_sessionsession, texttext) outputs_per_frame {} for model_outputs in model.propagate_in_video_iterator( inference_sessionsession, max_frame_num_to_trackmax_frames ): outputs_per_frame[model_outputs.frame_idx] processor.postprocess_outputs( session, model_outputs ) return {frames: sorted(outputs_per_frame.keys()), objects_frame_0: len(outputs_per_frame[0][object_ids])}注意视频分支用dtypetorch.bfloat16加载——半精度推理是官方示例的标准姿势服务端应默认开启。三、一行 curl从传图到拿掩码服务启动后客户端的接入成本被压缩到极致。启动与调用# 1. 启动服务模型加载 预热在启动阶段完成 uvicorn main:app --host 0.0.0.0 --port 8000 # 2. 一行 curl传图 文本概念拿回掩码 curl -s -X POST http://127.0.0.1:8000/segment \ -F imagestreet.jpg \ -F textcar \ -F threshold0.5 | jq返回示例{ num_objects: 3, boxes: [[12, 38, 210, 140], [260, 90, 402, 188], [455, 300, 520, 340]], scores: [0.96, 0.91, 0.87], masks_png_base64: [iVBORw0KGgo..., iVBORw0KGgo..., iVBORw0KGgo...] }一次请求同时拿到三样东西掩码PNG base64可解码后叠加到原图、边界框xyxy 绝对像素坐标、置信度scores。这正是 SAM3 与 SAM/SAM 2 最本质的差异在 API 层的体现——textcar一句提示返回的是图上所有汽车实例而不是一个待你多点几次的交互式分割结果。这也让全实例分割真正变得可编程、可批处理。框提示同样是一行的事# 用正框锁定区域分割框内目标 curl -s -X POST http://127.0.0.1:8000/segment_box \ -F imagekitchen.jpg \ -F box59,144,76,163 \ -F positive1 | jq # 文本 负框分割 handle但排除烤箱把手所在区域 curl -s -X POST http://127.0.0.1:8000/segment_box \ -F imagekitchen.jpg \ -F texthandle \ -F box40,183,318,204 \ -F positive0 | jq第二个例子对应 README 中的 Combined Prompts (Text Negative Box) 模式文本提示负责定义概念负框负责在概念内排除干扰区域——这是视觉 prompt 与文本 prompt 联合服务化的最典型场景也是 SAM3 多模态提示能力的直接体现。四、与多模态大模型串联的三种集成方案服务化之后SAM3 就从一个模型变成了一个可编排的视觉能力单元。社区实践中被反复验证的三条串联路线如下方案一VLM 生成概念词 → SAM3 文本分割SAM3 的文本提示接口天然可编程与视觉语言模型VLM是绝配。用 VLM 对画面做开放式理解把输出的物体概念喂给分割服务形成理解-分割两级流水线# VLM 输出开放式概念清单伪代码 concepts vlm.generate_concepts(frame, question画面里有哪些值得分割的物体) # [car, traffic light, pedestrian] import requests for concept in concepts: resp requests.post( http://127.0.0.1:8000/segment, files{image: open(frame.jpg, rb)}, data{text: concept}, ) masks resp.json()[masks_png_base64] # 逐概念取掩码这条路线的价值在于SAM3 的开放词汇能力把分割什么的决定权完全交给了上游语言模型无需任何类别清单或标注。方案二检测器出框 → SAM3 框提示精分割工业落地中最常见的是检测 分割的组合拳社区部署实践里 YOLOv8 与 SAM3 的多模型集成正是这一路线。检测器负责快速定位SAM3 负责像素级精分割# 步骤 1YOLO 类检测器给出候选框 boxes detector.predict(frame) # [[x1, y1, x2, y2], ...] # 步骤 2逐框调用 SAM3 框提示端点 for box in boxes: resp requests.post( http://127.0.0.1:8000/segment_box, files{image: open(frame.jpg, rb)}, data{box: ,.join(map(str, box)), positive: 1}, ) # 掩码比检测框精细得多可用于计数、抠图、质检等下游任务对比纯检测这条路线用可控的推理成本换来了像素级边界而 SAM3 的负框能力还能在框内排除误检区域进一步净化分割结果。方案三文本概念 → SAM3 Video 全实例追踪对视频场景直接用文本提示启动全实例追踪让 SAM3 Video 的检测-关联-追踪流水线接管时序维度# 对视频帧序列只做一次文本提示所有匹配实例跨帧追踪 session processor.init_video_session(videoframes, ...) session processor.add_text_prompt(inference_sessionsession, textperson) for model_outputs in model.propagate_in_video_iterator(inference_sessionsession): outputs_per_frame[model_outputs.frame_idx] \ processor.postprocess_outputs(session, model_outputs) # 每个 frame 的 object_ids / boxes / masks 即为追踪结果配合大模型做上层编排例如 LLM 根据用户指令选择提示词、汇总各帧的追踪轨迹就可以把一句话描述目标 → 视频中全程追踪该目标做成标准能力。README 同时提示流式推理会关闭 hotstart 启发式去重可能引入更多误检与重复轨迹——全帧可得的场景优先用预加载推理这个工程权衡值得在服务化时写进接口文档。五、生产化清单从能跑到大流量最后把社区部署实践中反复出现的坑收敛成一份清单供服务上线前逐项核对半精度与显存图像分支用 fp32 约 3.4 GB视频分支务必torch.bfloat16加载README 官方示例即为 bf16显存紧张时可考虑分块推理与更小的 batch预热与冷启动启动后执行一次空推理否则首请求会叠加 CUDA kernel 编译延迟输入约束tokenizer_config.json规定文本提示最长 32 token超长需截断上传图片需限制尺寸与格式防止恶意大图拖垮预处理掩码传输优先 RLE 或 PNG base64 压缩避免原始数组直传并发模型Transformer 类模型的推理天然支持 batch/segment端点可接收多图、多文本README 的 Batched Inference 示例展示了images[...], text[...]一次前向处理多图服务端按 GPU 余量动态组 batch 是提升吞吐的关键手段加速探索社区实践普遍指向 ONNX 导出、TensorRT 与 8bit 量化三个方向配合 CUDA 内存优化与 Guided Filter 后处理可在精度可接受的范围内换取明显延迟收益追踪阈值调参config.json中的score_threshold_detection、new_det_thresh、high_conf_thresh等阈值直接影响视频场景的检出率与轨迹稳定性生产环境应按数据分布单独标定。结语SAM3 的核心贡献是把分割从点一点、画一画的交互式工具升级成了说一句就全给你的概念级理解能力。而本文要传递的工程判断是当模型接口变成可编程的输入输出契约时它的价值才真正开始指数级放大。从config.json里的 200 个查询位点到processor_config.json里 1008×1008 的输入契约再到一行 curl 拿回掩码——这条路线的每一步都不需要魔法只需要把官方 API 老老实实地服务化。至于接下来 SAM3 的视频追踪、Tracker 的 SAM2 兼容替换README 明确它是 SAM2 工作流的 drop-in replacement都是同一套思路在不同任务上的自然延伸。【免费下载链接】sam3项目地址: https://ai.gitcode.com/hf_mirrors/facebook/sam3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑