ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Ultralytics FastSAM 推理源码解析:FastSAMPredictor 提示引导分割的实现原理与实战指南

Ultralytics FastSAM 推理源码解析:FastSAMPredictor 提示引导分割的实现原理与实战指南 Ultralytics FastSAM 推理源码解析FastSAMPredictor 提示引导分割的实现原理与实战指南【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics导读本文以仓库中的FastSAMPredictor类为切入点深入剖析 Ultralytics FastSAMFast Segment Anything Model在推理阶段的核心实现它如何在 YOLO 实例分割管线之上完成全图分割 提示Prompt筛选的两段式工作流如何分别处理边框BBox、点Point与文本Text三类提示以及 CLIP 文本-图像相似度计算背后的调用细节。读完本文你将掌握FastSAMPredictor的源码级工作原理、postprocess/prompt/set_prompts等方法的调用语义以及在实际图像上先分割一次、再多次提示查询的高效用法。一、FastSAMPredictor 在整个 FastSAM 推理链中的位置1.1 从模型入口到预测器FastSAM 不是一个常规的 YOLO 可训练模型而是一个仅支持加载预训练权重的分割模型。模型入口 中做了两个关键约束传入FastSAM.pt时会自动重定向到FastSAM-x.pt权重不支持.yaml/.yml结构文件因为 FastSAM 只能从预训练权重构建assert Path(model).suffix not in {.yaml, .yml}无法像 YOLO 系列那样从 YAML 配置实例化。模型入口通过 task_map 把segment任务显式绑定到FastSAMPredictor推理与FastSAMValidator验证{segment: {predictor: FastSAMPredictor, validator: FastSAMValidator}}也就是说当你执行FastSAM(FastSAM-s.pt)(source)时真正负责逐张图像前向与后处理的正是本文的主角FastSAMPredictor定义见 predict.py。1.2 类继承关系FastSAMPredictor继承自 SegmentationPredictor后者又继承自检测用的DetectionPredictor。这条继承链意味着 FastSAM 的推理默认复用完整的 YOLO 推理管线预处理、模型加载、批量前向、NMS、结果构建FastSAMPredictor 只负责在父类基础上做两类定制后处理修正把贴近图像边缘的目标框吸附到图像边界保证提示筛选阶段拿到完整的实例提示引导筛选根据 BBox / 点 / 文本提示在分割一切的全量结果中挑选出用户真正感兴趣的目标。类的 docstring 同时明确了三个对外属性属性说明prompts存放提示信息的字典键为bboxes、points、labels、textsdevice模型与张量计算所在的设备torch.deviceclip用于文本提示的 CLIP 模型按需惰性加载二、初始化与提示注册__init__与set_prompts2.1 构造函数__init__的逻辑非常简洁predict.pydef __init__(self, cfgDEFAULT_CFG, overridesNone, _callbacks: dict | None None): super().__init__(cfg, overrides, _callbacks) self.prompts {}cfg默认取DEFAULT_CFG即仓库默认配置见 cfg/default.yamloverrides用于覆盖默认参数如conf、imgsz、task等在参考文档的官方示例中就是以overrides字典方式构造预测器的初始化时把self.prompts置空字典等待后续注册。2.2 手动注册提示set_promptsset_prompts的作用是把一份完整的提示字典直接写入预测器predict.pydef set_prompts(self, prompts): Set prompts to be used during inference. self.prompts prompts它会在下一次前向推理的postprocess中被消费见第三节因此典型用法是先set_prompts再调用predict让提示与当前批次图像绑定。2.3 更常用的入口模型层的 predict 包装在绝大多数场景中你并不会直接操作预测器而是通过 FastSAM.predict 传入提示prompts {bboxes: bboxes, points: points, labels: labels, texts: texts} return super().predict(source, stream, promptsprompts, **kwargs)bboxes、points、labels、texts四个命名参数被统一打包成字典传给基类Model.predict最终流转到预测器内部。也就是说无论走哪条路提示最终都会汇聚到self.prompts由postprocess统一取出使用。三、后处理主流程postprocess的两次修正postprocesspredict.py承担父类后处理 边缘框吸附 提示应用三个步骤。3.1 取出并消费提示bboxes self.prompts.pop(bboxes, None) points self.prompts.pop(points, None) labels self.prompts.pop(labels, None) texts self.prompts.pop(texts, None)这里使用了pop而不是读取意味着提示是一次性消费的每处理完一批图像self.prompts中对应的键即被清空避免提示在流式/多批推理中被重复使用造成串扰。3.2 调用父类完成常规分割后处理随后调用super().postprocess(preds, img, orig_imgs)。父类 SegmentationPredictor.postprocess 会完成 NMS、按retina_masks参数选择原生或上采样的 mask 处理并依据masks.amax((-2, -1)) 0丢弃没有有效 mask 的预测框见 construct_result最终返回Results列表。3.3 边缘框吸附修正FastSAM 特有这是 FastSAMPredictor 相对普通分割预测器最显著的一处差异full_box torch.tensor([0, 0, result.orig_shape[1], result.orig_shape[0]], ...) boxes adjust_bboxes_to_image_border(result.boxes.xyxy, result.orig_shape) idx torch.nonzero(box_iou(full_box[None], boxes)[0] 0.9).flatten() if idx.numel() ! 0: result.boxes.xyxy[idx] full_box其中 adjust_bboxes_to_image_border 的实现逻辑是若框的x1 20则把x1置 0若框的y1 20则把y1置 0若框的x2 w - 20则把x2置为图像宽w若框的y2 h - 20则把y2置为图像高h。也就是说凡是 20 像素以内贴近图像边缘的框都会被拉伸到精确贴边。随后计算每个候选框与全图框[0, 0, w, h]的 IoU凡是 IoU 超过 0.9 的框直接把其坐标覆盖为全图框。这样做的目的是当一个实例几乎占满整幅图像例如镜头前的近景主体时后续依据 mask 面积/交并比做的提示筛选不会被裁掉边缘的框误导点或框提示能更可靠地命中该实例。最终返回值是self.prompt(results, bboxes..., points..., labels..., texts...)的结果即先分割再按提示筛选。四、提示筛选核心prompt方法逐行拆解promptpredict.py是 FastSAMPredictor 的灵魂方法。签名如下参数形状/类型语义bboxes(N, 4)边框提示XYXY 格式像素坐标points(N, 2)点提示像素坐标[x, y]labels(N,)点提示的标签1表示前景、0表示背景textsstr或list[str]文本提示描述4.1 快速返回路径如果bboxes、points、texts三者皆为None直接返回原始results此时只有labels而没有points是没有意义的代码也将其视为无提示。若传入单个Results而非列表会先包装成列表统一处理。对没有任何检测框的结果len(result) 0则原样保留跳过筛选。4.2 mask 缩放对齐masks result.masks.data if masks.shape[1:] ! result.orig_shape: masks (scale_masks(masks[None].float(), result.orig_shape)[0] 0.5).byte()由于提示坐标都基于原始图像尺寸像素而 mask 可能基于模型输入尺寸这里先用 scale_masks 把 mask 放大回原图分辨率再以 0.5 阈值二值化保证提示坐标与 mask 处于同一坐标系。后续所有提示的命中判断都在原图分辨率二值 mask上进行。4.3 BBox 提示按 mask-框交并比选取最优实例BBox 提示的目标是在已有的分割一切结果里选出与给定框重叠最充分的那一个实例。核心实现为boxes clip_boxes(boxes, result.orig_shape) # 坐标裁剪到图内 bbox_areas (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0]) mask_areas torch.stack([masks[:, b[1]:b[3], b[0]:b[2]].sum(dim(1, 2)) for b in boxes]) full_mask_areas torch.sum(masks, dim(1, 2)) union bbox_areas[:, None] full_mask_areas - mask_areas idx[torch.argmax(mask_areas / union, dim1)] True逐项解释clip_boxes先把提示框裁剪到图像范围内避免越界bbox_areas计算每个提示框的面积mask_areas计算每个实例 mask 落在提示框内的像素数框与 mask 的交集full_mask_areas是每个实例 mask 的总像素数union bbox_areas full_mask_areas - mask_areas即框面积 mask 面积 − 交面积 并面积对每个提示框取mask_areas / union即框内 mask 占比的 Jaccard 式度量最大的那个实例标记为选中。这个度量的巧妙之处在于它同时惩罚两种错配mask 只有一小部分落在框内框选偏了以及 mask 远大于框框只覆盖了目标的一部分。最终每个 BBox 提示选出一个最佳实例。4.4 点提示前景/背景点逐像素投票点提示实现predict.pycoords clip_coords(coords, tuple(x - 1 for x in result.orig_shape)) if labels is None: labels torch.ones(coords.shape[0]) ... point_idx (torch.ones(...) if labels.sum() 0 else torch.zeros(...)) for point, label in zip(coords, labels): point_idx[torch.nonzero(masks[:, point[1], point[0]], as_tupleTrue)[0]] bool(label) idx | point_idx语义如下若未提供labels默认所有点都是前景点1一个点是前景label 1凡是 mask 覆盖该像素即masks[m, y, x] 1的实例被选中一个点是背景label 0凡是 mask 覆盖该像素的实例被取消选中多个点按前景选中、背景排除逐点叠加最终保留的是被某个前景点命中、且不被任何背景点排除的实例若所有点都是背景点labels.sum() 0初始point_idx全为True最终结果就是去除所有被背景点碰到的实例——这种用法对应官方 FastSAM 语义中的排除negative prompt场景。当labels与points数量不一致时代码通过断言assert len(labels) len(coords)直接报错提示坐标先经 clip_coords 以(w-1, h-1)为边界做越界裁剪。4.5 文本提示CLIP 相似度筛选文本提示是 FastSAM 最独特的能力——它让用户可以用自然语言从分割结果中挑选实例。实现分为三步predict.py第一步按 mask 裁剪候选区域剔除噪声。for i, b in enumerate(result.boxes.xyxy.tolist()): x1, y1, x2, y2 (int(x) for x in b) if (masks[i].sum() ...) 100: filter_idx.append(i) continue crop result.orig_img[y1:y2, x1:x2] * masks[i, y1:y2, x1:x2, None].cpu().numpy() crop_ims.append(Image.fromarray(crop[:, :, ::-1]))先用实例的框从原图裁出局部图再乘以该实例的 mask得到只含该目标、背景透明的前景裁剪图mask 像素总数 ≤ 100 的实例被认为是碎片噪声直接进入filter_idx排除这里有一段兼容逻辑masks[i].sum() if TORCH_1_10 else masks[i].sum(0).sum()即 PyTorch 1.10 之前的版本Tensor.sum()已支持全量求和而更早版本需要显式sum(0).sum()规避 bug图像从 BGRnumpy转成 RGB 的PIL.Imagecrop[:, :, ::-1]满足 CLIP 的输入要求。第二步CLIP 计算图像-文本相似度矩阵。similarity self._clip_inference(crop_ims, texts) text_idx torch.argmax(similarity, dim-1) # (M, )_clip_inferencepredict.py返回形状为(M, N)的相似度矩阵——M为文本条数、N为裁剪图数。对每个文本取相似度最高的那个候选实例torch.argmax(similarity, dim-1)。第三步索引重映射与最终选中。if len(filter_idx): ori_idxs torch.tensor([i for i in range(len(result)) if i not in filter_idx], ...) text_idx ori_idxs[text_idx] idx[text_idx] True由于前面过滤掉了碎片实例argmax得到的下标是候选子集内的下标需要借助ori_idxs映射回原始result中的真实实例下标——注释中特别说明这步重映射是为了支持多条文本提示同时筛选。映射完成后命中的实例在idx中置为True。三种提示可以独立使用也可以通过idx | ...与point_idx等按位或关系叠加使用BBox 结果存入idx点结果idx | point_idx文本结果idx[text_idx] True。最终返回result[idx]即只保留被任一提示选中的实例。五、CLIP 惰性加载与文本-图像推理细节文本提示依赖_clip_inference方法其关键设计是惰性加载predict.pyfrom ultralytics.nn.text_model import CLIP if not hasattr(self, clip): self.clip CLIP(ViT-B/32, deviceself.device)CLIP 模型ViT-B/32只有在第一次遇到文本提示时才加载日常的 BBox/点提示推理不会引入任何 CLIP 开销CLIP 类的实现位于 text_model.py提供image_preprocess、encode_image、tokenize、encode_text四个子过程相似度定义为文本特征与图像特征的矩阵乘法text_features image_features.T得到(M, N)的相似度矩阵predict.py取余弦相似度语义下的最大值即完成匹配。5.1 并发预处理优化当运行在 CUDA 上、配置的线程数NUM_THREADS 1且待处理的裁剪图数量 2 * NUM_THREADS时代码会使用ThreadPool(NUM_THREADS)并行执行 CLIP 的图像预处理predict.py再把预处理结果统一搬到设备端。对于一张图上分割出几十个实例再逐一做 CLIP 预处理的典型文本提示场景这个多线程路径能明显压缩预处理耗时。5.2 关于文本提示的工程前提从源码可以看出文本提示的质量高度依赖两件事分割一切阶段必须已经产出足够完整的候选实例——CLIP 只在已有实例的裁剪图上做相似度匹配无法凭空发现漏检目标这正是 FastSAM 两段式先全图分割、后提示筛选架构的本质mask 阈值 100 像素的过滤会剔除过小碎片因此低分辨率或小目标场景下文本提示的召回率会受限于候选质量。该行为同时印证了参考文档中对FastSAMPredictor的定位它专为单类别分割优化把后处理定制为mask 预测 NMS 提示筛选的完整管线。六、实战指南两种调用方式的完整示例6.1 方式一模型级 API推荐日常使用参考文档与 fast-sam.md 使用章节 一致最直接的用法是通过FastSAM模型一次性传入提示from ultralytics import FastSAM source path/to/bus.jpg model FastSAM(FastSAM-s.pt) # 或 FastSAM-x.pt # 1) 分割一切全图所有实例 everything_results model(source, devicecpu, retina_masksTrue, imgsz1024, conf0.4, iou0.9) # 2) BBox 提示选取该框内的最佳实例 results model(source, bboxes[439, 437, 524, 709]) # 3) 点提示前景点 (200, 200) results model(source, points[[200, 200]], labels[1]) # 4) 文本提示找出与描述最匹配的实例 results model(source, textsa photo of a dog) # 5) 三种提示同时使用 results model(source, bboxes[439, 437, 524, 709], points[[200, 200]], labels[1], textsa photo of a dog)参数说明参数作用注意点imgsz模型输入尺寸越大实例分割细节越完整官方示例常用 640~1024conf全图分割阶段的置信度阈值过滤低置信实例官方一切分割示例用 0.4iouNMS 的 IoU 阈值官方一切分割示例用 0.9偏高以保留更多重叠实例retina_masks是否使用原图分辨率生成 maskTrue时用process_mask_native直接在高分辨率下处理供提示筛选的 mask 质量更高CLI 同样可用注意 CLI 仅支持常规分割不支持提示参数提示能力需走 Python API# 全图分割 yolo segment predict modelFastSAM-s.pt sourcepath/to/bus.jpg imgsz6406.2 方式二预测器级 API先分割一次、提示无限次参考文档重点推荐了这一模式只跑一次全图分割之后任意次提示查询都不再重复前向推理。官方示例如下from ultralytics.models.fastsam import FastSAMPredictor # 1) 构造预测器通过 overrides 覆盖默认配置 overrides {conf: 0.25, task: segment, mode: predict, model: FastSAM-s.pt, save: False, imgsz: 1024} predictor FastSAMPredictor(overridesoverrides) # 2) 先分割一次得到一切结果 everything_results predictor(ultralytics/assets/bus.jpg) # 3) 后续基于同一份结果做任意次提示筛选 bbox_results predictor.prompt(everything_results, bboxes[[200, 200, 300, 300]]) point_results predictor.prompt(everything_results, points[200, 200]) text_results predictor.prompt(everything_results, textsa photo of a dog)注意区分FastSAMPredictor(source)继承自BasePredictor.__call__走完整的前向 postprocess流程属于真正的推理predictor.prompt(results, ...)不触发任何网络前向只在已产出的Results的 mask 上做面积/交并比/CLIP 运算。正因如此交互式应用先自动分割、再由用户点选目标可以在推理一次后获得接近实时的反馈。需要提醒的是直接使用预测器时需要自行保证overrides中tasksegment、modepredict且model指向 FastSAM 权重与测试用例中的做法保持一致。6.3 测试用例印证仓库测试 test_cli.py 中的 test_fastsam 验证了上述全部能力路径先跑yolo segment predict与yolo segment val的 CLI 链路再通过 Python API 分别验证普通图像路径与PIL.Image输入路径for s in (source, Image.open(source))随后在同一份一切分割结果上同时传入 bboxes、points、labels、texts 四种提示做联合筛选。它还被注释标注为不适合树莓派等边缘设备、且受 Python 版本影响CLIP 依赖说明文本提示功能存在一定的环境前提。此外测试中用到了Predictor.remove_small_regions(..., min_area20)这类去除小碎片的辅助手段与postprocess/prompt中 100 像素碎片过滤的思路互为补充。七、总结FastSAMPredictor 的设计要点与使用边界从源码级视角归纳FastSAMPredictor的设计可以浓缩为以下要点复用而非重写通过继承 SegmentationPredictor完整复用 YOLO 分割推理与 NMS 管线只覆写postprocess增加两段定制逻辑两段式架构前向推理只做一次分割一切所有提示能力都被后置到prompt阶段基于 mask 的轻量运算BBox/点或惰性加载的 CLIP 推理文本上这是 FastSAM 相对传统 SAM 能保持实时性的关键工程原因之一一次性提示消费self.prompts在postprocess中使用pop取出避免提示在流式或多批推理中残留串扰跨批次复用提示需显式调用set_prompts明确的单类语义与边界FastSAM 面向单类别分割一切model.py明确禁止 YAML 构建、参考文档 fast-sam.md 也说明验证时需把数据集所有类别 ID 归并为 0同时它不支持训练仅支持预测/验证/导出/跟踪等模式。若想进一步探索可继续阅读 FastSAMPredictor 源码、其依赖的 边框吸附工具、SegmentationPredictor 父类 以及 FastSAM 模型接口并结合仓库内的 FastSAM 模型总览文档 了解其在 YOLO 生态中的整体定位。【免费下载链接】ultralyticsUltralytics YOLO26, YOLO11, YOLOv8 — object detection, instance segmentation, semantic segmentation, image classification, pose estimation, object tracking项目地址: https://gitcode.com/GitHub_Trending/ul/ultralytics创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表