拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于Grounding DINO与SAM的开放词汇检测与分割实战指南

简介目标检测与图像分割是计算机视觉的两大核心任务前者负责定位物体后者则实现像素级的精细划分。传统方法通常需要为特定类别分别训练模型缺乏灵活性。随着多模态与基础模型的发展开放词汇目标检测Open-Vocabulary Object Detection和通用分割General Segmentation技术应运而生它们能理解自然语言描述并泛化到未见过的类别极大地拓宽了应用边界。其技术价值在于构建了一个灵活、通用的视觉感知流水线无需针对每个新物体重新训练模型。在工程实践中通过将专精于文本理解与定位的Grounding DINO模型与擅长零样本分割的Segment Anything Model (SAM)相结合可以高效实现“语言描述-目标定位-精细分割”的端到端流程。这一组合方案在数据标注、图像编辑、机器人视觉及内容创作等场景中展现出巨大潜力为解决开放世界下的精细视觉理解问题提供了强大工具。1. 项目缘起当目标检测遇上开放世界与精细分割最近在复现和整合一些前沿的视觉算法时我遇到了一个典型的工程挑战如何在一个项目中既实现开放词汇的通用目标检测又能对检测到的目标进行高精度的像素级分割传统的目标检测框架无论是YOLO系列还是SSD通常只输出一个边界框Bounding Box对于需要进一步分析目标形状、轮廓或者进行抠图的应用场景就显得力不从心了。而像Mask R-CNN这类实例分割模型虽然能同时输出框和掩码Mask但其检测的类别通常是预先定义好的、封闭的无法灵活应对“检测图片中那个红色的杯子”或者“找出所有金属物体”这类开放式的指令。这个矛盾点恰好是当前视觉领域两个重要方向的交汇处开放词汇目标检测Open-Vocabulary Object Detection和通用分割General Segmentation。前者旨在让模型能够理解自然语言描述并定位物体打破类别限制后者则追求对任意物体进行像素级分割的能力。于是一个很自然的想法就产生了能不能把这两个强大的工具“粘”在一起先让一个模型听懂指令找出目标再让另一个模型对这个目标进行精细分割这就是我动手实践这个项目的核心动机。我选择了两个在各自领域表现突出的模型进行组合Grounding DINO负责“听懂并定位”Segment Anything Model (SAM)负责“精细分割”。Grounding DINO 基于 Transformer 架构能够将图像特征和文本特征进行深度融合实现基于文本提示的零样本目标检测。而 SAM 则是一个拥有超强泛化能力的分割基础模型给它一个粗略的提示点或框它就能生成高质量的分割掩码。这个组合的威力在于它构建了一个非常灵活的视觉感知流水线。你不再需要为每一个特定物体训练一个检测模型和一个分割模型。你只需要用自然语言描述你想要找的东西这个流水线就能自动把它框出来并精确地“抠”出来。无论是做内容创作、图像编辑、机器人视觉引导还是数据标注辅助这个流程都能极大地提升效率和灵活性。接下来我就详细拆解一下这个项目的实现过程、核心原理以及我趟过的一些坑。2. Grounding DINO 解析如何让模型“听懂”人话并定位在组合系统中Grounding DINO 扮演着“指挥官”和“侦察兵”的角色。它的任务是理解我们输入的一段文本描述例如“一只棕色的狗”、“餐桌上的玻璃杯”然后在图像中找到所有与之匹配的区域并用边界框标出来。这听起来很像传统的目标检测但核心区别在于“开放词汇”。传统检测模型在最后一层有一个固定的分类头输出的是训练集中见过的、有限的类别概率。而 Grounding DINO 要实现的是文本到区域的匹配。2.1 核心架构与工作流程Grounding DINO 的架构可以粗略分为三个核心部分图像编码器、文本编码器和特征融合解码器。图像编码器通常是一个基于 Transformer 的骨干网络比如 Swin Transformer。它的任务是把输入图像转换成一序列的图像特征令牌Image Token。假设输入图像是 224x224经过分块和编码后我们可能得到 196 个14x14图像特征向量每个向量都编码了图像局部区域的信息。文本编码器则负责处理我们的文本提示。它同样是一个 Transformer 编码器如 BERT将输入的文本句子如 “a dog”转换成一系列文本特征令牌Text Token。最精妙的部分在于特征融合与解码。Grounding DINO 引入了“语言引导的查询选择”机制。传统的 DETR 类检测器使用一组可学习的对象查询Object Query去图像特征中“寻找”物体。而 Grounding DINO 的初始对象查询是由文本特征动态生成的模型会计算文本特征的自注意力并选择其中最具有代表性的几个特征向量作为初始的对象查询。这就好比模型先消化了你的指令“狗”然后生成几个带着“找狗”任务的特派员查询。这些“特派员”会被送入一个 Transformer 解码器。在解码器中图像特征作为 Key 和 Value文本引导的对象查询作为 Query进行交叉注意力计算。这个过程就是让这些“找狗特派员”不断地审视图像的不同区域寻找与“狗”的文本语义最匹配的视觉模式。经过多层解码最终每个对象查询会输出一个预测一个边界框坐标中心点、宽高和一个与文本的匹配度分数不是分类概率而是该区域与输入文本的相似度。2.2 关键参数调优与实战心得在实际部署 Grounding DINO 时有几个参数对结果影响巨大需要仔细调整文本提示Text Prompt这是最重要的输入。描述越精准效果通常越好。“a dog”就比“dog”好“a brown dog sitting on grass”又会更好。但也要注意过于复杂的长句有时会引入歧义。我的经验是使用名词短语并可以适当添加关键属性颜色、材质、位置。框阈值Box Threshold模型会输出很多框及其与文本的相似度分数。这个阈值决定了分数多高的框才会被保留下来。默认值如0.25可能偏保守在简单场景下可以提高到0.3-0.35以过滤掉更多噪声在复杂或小目标场景下可能需要降低到0.2甚至0.15避免漏检。# 伪代码示例过滤预测框 boxes predictions[boxes] scores predictions[scores] logits predictions[logits] # 与文本的相似度 # 应用框阈值 keep logits box_threshold filtered_boxes boxes[keep] filtered_scores scores[keep]文本阈值Text Threshold在模型内部其实会对每个检测框计算它与文本中各个单词的相似度。这个参数用于在后期处理中进一步过滤。一般情况下它和框阈值联动调整我通常先固定一个调另一个。模型尺寸选择Grounding DINO 提供了 Swin-T, Swin-B 等不同大小的骨干网络。Swin-T 速度快适合实时或对精度要求不极致的场景Swin-B 精度更高但计算量也更大。在消费级GPU如RTX 3080上处理一张1080p的图片Swin-T 版本可以在零点几秒内完成而 Swin-B 可能需要1-2秒。注意Grounding DINO 对文本非常敏感。同一个物体用“vehicle”和“car”去提示检测出的框的数量和置信度可能会有差异。这体现了其开放词汇的特性但也要求使用者在设计提示词时要多从模型的角度思考。3. Segment Anything Model (SAM) 深度拆解分割一切的“神笔马良”拿到 Grounding DINO 输出的边界框后我们的任务就交给了 SAM。SAM 的强大之处在于其“提示工程”和“零样本泛化”能力。它本身不是一个针对特定类别训练的分割模型而是一个分割基础模型。你给它一个提示点、框、掩码或文本它就能在相应的位置生成分割掩码。3.1 提示编码与掩码解码SAM 的核心引擎SAM 的流程也可以分为编码和解码两大部分。图像编码器是一个沉重的 Vision Transformer (ViT)它一次性对整个输入图像进行编码生成一个高维的图像嵌入Image Embedding。这个嵌入包含了整张图像的全局和局部信息。关键点在于这个编码过程只需要做一次。无论你后续给出多少个不同的提示比如对一张图里的多个物体分别画框都无需再次对图像进行编码极大地提高了交互式分割的效率。提示编码器则负责处理我们给的提示。对于点提示它将其位置编码为一个向量并附加一个标签是前景点还是背景点。对于框提示它通常将框的左上角和右下角两个点当作两个前景点进行编码。这正是我们的流水线所需要的将 Grounding DINO 输出的边界框作为提示输入给 SAM。轻量级掩码解码器是最后一步。它接收图像嵌入和提示嵌入通过一个改良的 Transformer 结构在提示信息的引导下逐步“描绘”出目标的轮廓。解码器最终会输出多个可能的分割掩码通常为3个并附带每个掩码的置信度分数IoU预测值。我们可以选择置信度最高的那个作为最终输出。3.2 与 Grounding DINO 对接框提示的细节处理在将 Grounding DINO 的框送给 SAM 时有几点细节决定了分割效果的上限框的坐标对齐Grounding DINO 输出的框坐标通常是归一化的值在0到1之间而 SAM 的提示编码器期望的输入是图像原始尺度下的坐标。必须进行正确的尺度变换。# 伪代码示例坐标转换 # 假设 image_shape 是 (H, W, C) boxes 是归一化的 [x1, y1, x2, y2] boxes_pixel boxes * np.array([image_shape[1], image_shape[0], image_shape[1], image_shape[0]]) # 将 boxes_pixel 输入 SAM 的提示编码器框的冗余与合并Grounding DINO 可能会对同一个物体输出多个重叠度很高的框特别是当阈值设得较低时。直接把这些框都丢给 SAM 会造成重复计算和结果冗余。一个常见的后处理步骤是使用非极大值抑制NMS基于框的置信度分数去除冗余框。多物体批量处理为了提高效率我们通常希望一次性处理一张图片里所有被检测到的物体。SAM 的编码器设计支持批量提示输入。我们可以将过滤、去重后的所有边界框组成一个列表一次性输入给 SAM 的预测函数让它并行地为每个框生成分割掩码。这比用 for 循环一个个处理要快得多。实操心得SAM 对提示框的位置非常鲁棒即使框不是特别精确比如只框住了物体的大部分它往往也能分割出完整的物体。但是如果多个物体挨得非常近或者存在严重遮挡一个粗糙的框可能会让 SAM “困惑”导致分割结果将多个物体连在一起。这时一个更精确的检测框比如来自更优的 Grounding DINO 参数或后处理就至关重要。4. 项目实战从环境搭建到完整流水线理论清楚了接下来就是动手搭建。这个项目对环境的依赖相对明确主要围绕 PyTorch、TorchVision 以及两个模型的官方仓库。4.1 环境配置与依赖安装我强烈建议使用 Anaconda 或 Miniconda 来管理 Python 环境避免包冲突。# 1. 创建并激活一个新的 conda 环境 conda create -n grounding_sam python3.9 conda activate grounding_sam # 2. 安装 PyTorch (请根据你的CUDA版本到官网选择对应命令) # 例如对于 CUDA 11.8 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 3. 克隆必要的代码仓库 git clone https://github.com/IDEA-Research/GroundingDINO.git git clone https://github.com/facebookresearch/segment-anything.git # 4. 安装 Grounding DINO 依赖 cd GroundingDINO pip install -e . # 安装额外的依赖如 transformers, timm 等通常 requirements.txt 里有 pip install -r requirements.txt cd .. # 5. 安装 Segment Anything 依赖 cd segment-anything pip install -e . pip install opencv-python pycocotools matplotlib onnxruntime onnx cd .. # 6. 下载模型权重文件 # Grounding DINO 权重 (例如 Swin-T 版本) # 从官方仓库的 Release 或提供的链接下载 groundingdino_swint_ogc.pth # SAM 权重 (例如 vit_b 版本) # 从官方仓库提供的链接下载 sam_vit_b_01ec64.pth安装过程中最常见的坑是 PyTorch 版本与 CUDA 的兼容性问题以及某些依赖包如pycocotools在 Windows 上的编译失败。如果遇到pycocotools安装失败可以尝试寻找预编译的 wheel 文件或者使用pip install pycocotools-windows仅限 Windows。4.2 核心流水线代码实现环境准备好后就可以编写核心的集成代码了。代码结构应该是模块化的主要分为加载模型、执行检测、执行分割、可视化结果几个部分。import cv2 import numpy as np import torch from PIL import Image import matplotlib.pyplot as plt # 假设 GroundingDINO 和 SAM 的模块路径已正确设置 from groundingdino.util.inference import load_model, load_image, predict from segment_anything import sam_model_registry, SamPredictor class GroundedSAM: def __init__(self, grounding_dino_config_path, grounding_dino_checkpoint_path, sam_checkpoint_path, sam_model_typevit_b, devicecuda): self.device device # 1. 加载 Grounding DINO self.grounding_dino_model load_model(grounding_dino_config_path, grounding_dino_checkpoint_path, devicedevice) # 2. 加载 SAM self.sam sam_model_registry[sam_model_type](checkpointsam_checkpoint_path) self.sam.to(devicedevice) self.sam_predictor SamPredictor(self.sam) def run(self, image_path, text_prompt, box_threshold0.25, text_threshold0.25): # 1. 读取并预处理图像 image_source, image load_image(image_path) # GroundingDINO 的预处理 H, W, _ image_source.shape # 2. 使用 Grounding DINO 进行检测 boxes, logits, phrases predict( modelself.grounding_dino_model, imageimage, captiontext_prompt, box_thresholdbox_threshold, text_thresholdtext_threshold, deviceself.device ) # boxes 是归一化的 [x1, y1, x2, y2] if boxes.numel() 0: print(未检测到任何目标。) return None, None, None # 3. 将框的坐标转换到图像原始尺度 boxes_pixel boxes * torch.Tensor([W, H, W, H]) boxes_pixel boxes_pixel.cpu().numpy().astype(int) # 4. 为 SAM 准备图像嵌入 (只做一次) self.sam_predictor.set_image(image_source) # 5. 使用 SAM 进行分割 # 将每个框作为提示输入 SAM masks_list [] for box in boxes_pixel: # 输入格式为 [x1, y1, x2, y2] input_box np.array([box[0], box[1], box[2], box[3]]) masks, scores, _ self.sam_predictor.predict( point_coordsNone, point_labelsNone, boxinput_box[None, :], # 增加 batch 维度 multimask_outputFalse # 我们通常只要最好的那个掩码 ) masks_list.append(masks[0]) # 取第一个也是唯一一个掩码 # 6. 返回结果 return boxes_pixel, masks_list, logits.cpu().numpy() def visualize(self, image_path, boxes, masks, output_pathresult.jpg): image cv2.imread(image_path) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) plt.figure(figsize(10, 10)) plt.imshow(image) # 绘制检测框 for box in boxes: x1, y1, x2, y2 box rect plt.Rectangle((x1, y1), x2-x1, y2-y1, linewidth2, edgecolorr, facecolornone) plt.gca().add_patch(rect) # 叠加分割掩码 (使用半透明颜色) for i, mask in enumerate(masks): # 为每个掩码生成随机颜色 color np.random.rand(3,) # 创建一个带透明度的彩色掩码图像 mask_image np.zeros((*mask.shape, 4), dtypenp.float32) mask_image[mask] [*color, 0.5] # RGBAA通道为0.5表示半透明 plt.imshow(mask_image) plt.axis(off) plt.savefig(output_path, bbox_inchestight, pad_inches0) plt.show() # 使用示例 if __name__ __main__: # 初始化流水线 pipeline GroundedSAM( grounding_dino_config_pathGroundingDINO/groundingdino/config/GroundingDINO_SwinT_OGC.py, grounding_dino_checkpoint_pathweights/groundingdino_swint_ogc.pth, sam_checkpoint_pathweights/sam_vit_b_01ec64.pth, devicecuda if torch.cuda.is_available() else cpu ) # 运行检测与分割 boxes, masks, scores pipeline.run( image_pathtest_image.jpg, text_prompta red car and a traffic light, box_threshold0.28, text_threshold0.25 ) # 可视化结果 if boxes is not None: pipeline.visualize(test_image.jpg, boxes, masks, output.jpg)这段代码构建了一个完整的流水线。GroundedSAM类封装了模型加载、推理和可视化的全过程。run方法接收图像路径和文本提示依次执行开放词汇检测和提示式分割。可视化部分将检测框和半透明的分割掩码叠加在原图上效果非常直观。5. 性能优化与高级技巧当基础流程跑通后我们自然会关注如何让它更快、更准、更稳定。以下是我在实践中总结的一些优化点和进阶用法。5.1 推理速度优化策略模型尺寸选择这是最直接的权衡。对于 SAMvit_b(9100万参数) 比vit_l(3.08亿) 和vit_h(6.36亿) 快得多精度损失在可接受范围内。对于原型或对实时性有要求的应用vit_b是首选。图像尺寸缩放Grounding DINO 和 SAM 的推理时间都与输入图像尺寸强相关。在保证检测和分割精度的前提下将长边缩放到一个固定值如 800 像素可以显著加速。注意缩放后需要相应调整框的坐标。SAM 图像嵌入缓存这是 SAM 设计带来的天然优化点。对于需要多次对同一张图进行不同提示分割的场景比如交互式标注工具sam_predictor.set_image()计算的图像嵌入可以缓存起来后续提示直接使用避免重复编码。批量处理如前所述利用 SAM 支持批量提示输入的特性将一张图中所有物体的框一次性输入比循环处理效率高。使用 ONNX 或 TensorRT 部署对于生产环境可以将 PyTorch 模型导出为 ONNX 格式并利用 ONNX Runtime 或 NVIDIA TensorRT 进行推理优化获得数倍的加速比。SAM 官方提供了 ONNX 导出脚本Grounding DINO 的转换需要一些额外工作。5.2 提升检测与分割精度的技巧提示词工程具体化用“a metallic sports car”代替“a car”。多词组合对于复杂场景可以尝试用“and”连接多个物体如 “person and bicycle”。Grounding DINO 能较好地处理这种并列关系。负面提示实验性有些变体或自己微调的模型支持负面提示如“a car not a truck”但原版 Grounding DINO 对此支持有限。后处理策略基于分数的过滤除了模型自带的阈值可以根据任务需求对logits文本相似度和scores检测置信度进行更复杂的过滤比如只保留logits 0.3 and scores 0.5的预测。基于面积的过滤过滤掉面积过小可能是噪声或过大可能是背景误检的检测框和分割掩码。掩码后处理SAM 输出的掩码有时会有零星的小孔洞或毛刺。可以使用 OpenCV 的形态学操作如闭运算进行平滑。import cv2 mask masks[0].astype(np.uint8) * 255 kernel np.ones((3,3), np.uint8) mask_smoothed cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) mask_smoothed mask_smoothed 128 # 转回布尔值迭代式提示如果 SAM 第一次分割效果不理想比如只分割了物体的一部分可以将第一次得到的不完整掩码作为新的提示再次输入给 SAM引导它进行修正和补全。这模拟了人工交互式分割的过程。5.3 处理复杂场景与失败案例没有哪个模型是万能的这个组合流水线在以下场景可能会遇到挑战非常小的目标Grounding DINO 和 SAM 都对极小目标如图像中几十个像素以下的物体的检测和分割能力有限。可以尝试在检测前对图像进行适当放大上采样但会增加计算成本。高度重叠或密集物体当多个同类物体紧密堆积时Grounding DINO 可能只输出一个大的包围框导致 SAM 无法将它们分开。这时可能需要更精细的文本提示如“the leftmost cup”或引入额外的点提示。抽象或非刚性物体对于“火焰”、“水流”、“阴影”这类没有固定形状的物体检测和分割的边界都会比较模糊。文本描述歧义例如提示“bank”模型可能无法区分河岸和银行。这需要更精确的上下文描述。面对这些情况一个实用的策略是设置一个置信度阈值只输出高置信度的结果对于低置信度的预测可以标记出来交由人工复核或者触发一个更复杂的备用处理流程。6. 项目扩展与应用场景展望这个 Grounding DINO SAM 的流水线是一个强大的基础工具其价值在于它的灵活性和通用性。基于此我们可以向多个方向进行扩展构建更复杂的应用。6.1 扩展方向从流水线到系统视频处理将流水线应用于视频的每一帧可以实现开放词汇的视频目标检测与分割。关键在于利用帧间连续性进行优化例如使用前一帧的检测框和分割结果作为下一帧的提示跟踪或者只在关键帧运行完整的检测中间帧使用 SAM 进行基于提示的分割大幅提升处理速度。交互式标注工具这是最直接的应用。可以开发一个图形界面用户输入文本系统自动标出所有相关物体并生成像素级掩码。用户可以对不满意的结果进行微调如添加正负点提示极大地提升数据标注效率特别是对于罕见或长尾类别。以文搜图与编辑结合图像数据库用户可以用自然语言搜索特定物体如“找到所有包含木质桌子的图片”并直接获得该物体的分割掩码进而可以进行批量移除、替换、风格化等编辑操作。机器人视觉感知让机器人理解“请把那个红色的方块拿过来”这样的指令。流水线可以定位“红色的方块”SAM 的分割结果可以用于计算物体的精确轮廓和抓取点。多模态问答基础作为大型多模态模型LMM的视觉感知前端。LMM如 GPT-4V可以分析图像生成对感兴趣区域的描述文本然后用这个流水线去精确地分割出来实现指代、推理等更复杂的任务。6.2 工程化部署考量如果要将这个项目用于实际生产还需要考虑以下工程问题服务化使用 FastAPI 或 Flask 将模型封装成 RESTful API 服务接收图像和文本返回框和掩码的坐标信息如 COCO JSON 格式。并发与资源管理模型加载占用显存较大。需要设计合理的服务架构例如使用模型预热、请求队列、动态批处理对于 SAM以及多 GPU 负载均衡来应对高并发请求。结果缓存对于常见的、重复的查询例如电商网站中频繁搜索“手机”可以将检测和分割结果缓存起来避免重复计算。监控与日志记录请求的响应时间、模型置信度分布、常见失败案例等用于持续优化模型参数和提示策略。这个项目源码的价值不仅在于提供了一个可运行的技术演示更在于它展示了一种“组合创新”的思路将两个在特定任务上达到 SOTA 的专用模型通过清晰的接口框作为提示连接起来解决了一个更复杂的复合型问题。在实际操作中从环境配置、参数调试到性能优化每一步都需要耐心和细致的实验。我最深的体会是提示词Prompt是连接人类意图与模型能力的桥梁它的质量直接决定了整个系统的上限。多花时间思考如何用模型能理解的语言描述你的需求往往比盲目调整模型参数更有效。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门