
简介本资源面向计算机、人工智能、遥感与测绘等方向的在校学生、教师及企业研发人员提供一套基于SAM、Grounding DINO与CLIP组合模型的高分辨率全景图地物分类与实例分割完整实现。项目通过自定义文本提示驱动实现影像中任意类别的自动分割适合作为毕业设计、课程设计、项目立项演示或进阶学习素材。压缩包共68个文件约3.86MB以54个Python源码为核心辅以C与CUDA加速文件、模型头文件、示例图片及README说明覆盖模型加载、推理、数据工具与Grounding DINO模块结构清晰便于二次开发。目前已有146人学习关注。代码经测试可运行下载后可按README指引快速复现分割效果并在此基础上修改文本提示或替换数据拓展至其他遥感与全景场景任务。1. 从一张全景图说起SAM-DINO-CLIP 组合模型到底在做什么手里有一张无人机拍的全景图或者卫星遥感拼接出来的大图想让它自动把建筑、道路、植被、水体分出来还要给出每个目标的像素级轮廓——这件事单独用 SAM 做不了因为 SAM 只认点、框、掩码提示它不知道建筑是什么单独用 CLIP 也做不了因为 CLIP 只会给整张图打标签给不出位置。这个资源干的事情就是把 Grounding DINO 的开放词汇检测能力、CLIP 的语义对齐能力、SAM 的精细分割能力串成一条流水线先用文本提示让 DINO 把目标框出来再用 CLIP 对框内区域做类别确认和过滤最后把框喂给 SAM 拿到掩码。整条链路跑通之后你改一句文本提示就能切换地物类别不用重新训练任何模型。源码包里带了segment_anyting、groundingdino两个核心模块和main.py推理入口适合做遥感、测绘、GIS 方向的课程设计或毕设起步也适合已经会写 Python、想快速验证开放词汇分割效果的人。2. 组合模型的三个齿轮DINO 出框、CLIP 判类、SAM 抠图2.1 为什么不能只用 SAM必须引入 DINO 和 CLIPSAM 的设计定位是可提示分割它的输入必须是点、矩形框或者已有掩码输出是对应区域的精细边界。它本身没有语义概念你给它一个框它不知道框里是楼还是树。Grounding DINO 解决的是框从哪来的问题——它是一个开放词汇检测器输入一张图和一句英文文本提示比如building . road . vegetation .输出若干带置信度的矩形框。但 DINO 在小目标密集场景下会出一些语义漂移的框比如把光伏板认成水体这时候 CLIP 就派上用场把每个框裁剪出来送进 CLIP算它和各个类别文本的相似度低于阈值的框直接丢掉。三者串起来才形成文本进、掩码出的闭环。这个组合的价值在于零样本迁移——换一个场景、换一批地物类别只需要改提示词不需要标注数据重训。2.2 环境搭建与依赖安装的实操步骤这个项目对版本比较敏感尤其是torch和groundingdino的编译依赖。我一般会先建一个干净的虚拟环境避免和系统里的其他包打架。Python 建议 3.8 到 3.103.11 以上部分依赖轮子还不全。# 创建并激活虚拟环境python 版本建议 3.8-3.10 conda create -n samdino python3.9 -y conda activate samdino # 安装 pytorch按自己 CUDA 版本选这里以 cu118 为例 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 安装基础依赖 pip install opencv-python pillow numpy matplotlib scipy pip install segment-anything pip install githttps://github.com/IDEA-Research/GroundingDINO.git pip install githttps://github.com/openai/CLIP.git这里几个点要说明segment-anything是 Meta 官方包装完还要单独下载sam_vit_h_4b8939.pth权重放到项目根目录GroundingDINO用 git 装是因为 pip 源上的版本经常和权重不匹配CLIP 同理。装完之后跑一句python -c import torch; print(torch.cuda.is_available())返回 True 才算环境通了。如果返回 False先别急着往下走检查 CUDA 驱动和 torch 版本是否对得上这是后面所有推理的前提。2.3 模型加载与权重文件的组织方式项目里utils/load_models.py负责统一加载三个模型我建议把权重文件集中放在一个weights/目录下路径写进配置别硬编码在代码里。常见做法是这样组织# utils/load_models.py 核心加载逻辑示意 import torch from segment_anything import sam_model_registry, SamPredictor from groundingdino.util.inference import load_model import clip def load_all_models(devicecuda): # 1. 加载 SAMvit_h 是精度最高的版本显存不够可换 vit_b sam sam_model_registry[vit_h](checkpointweights/sam_vit_h_4b8939.pth) sam.to(device) sam_predictor SamPredictor(sam) # 2. 加载 Grounding DINO配置和权重必须成对 dino load_model( groundingdino/config/GroundingDINO_SwinT_OGC.py, weights/groundingdino_swint_ogc.pth ) dino.to(device) # 3. 加载 CLIPViT-B/32 速度快ViT-L/14 精度高但吃显存 clip_model, clip_preprocess clip.load(ViT-B/32, devicedevice) return sam_predictor, dino, clip_model, clip_preprocess参数上要留意SAM 的vit_h权重约 2.4G推理时显存占用在 6G 以上如果显卡只有 6G换成vit_b权重约 375M能跑但边缘会糙一些。DINO 的配置文件和权重必须版本对应用 SwinT 的配置配 SwinB 的权重会直接报维度错误。CLIP 的ViT-B/32和ViT-L/14在类别相似度排序上差异不大但后者显存翻倍全景大图场景下我一般先用 B/32 验证流程效果不够再换。3. 从文本提示到掩码输出推理主流程拆解3.1 文本提示的构造规则与类别词写法DINO 的文本提示有固定格式类别词之间用英文句点加空格分隔末尾也要加一个句点。比如要分建筑、道路、植被、水体四类提示词写成building . road . vegetation . water .。这里有几个血泪经验类别词用英文CLIP 的文本编码器对英文的语义空间更稳类别之间语义别太近比如tree和vegetation同时出现DINO 会出大量重叠框后面 NMS 也压不干净提示词里不要加数量词和形容词red building这种写法 DINO 基本不认。如果场景里地物类别多建议分批跑一次提示不超过 6 个类别否则召回率会明显下降。3.2 推理脚本的完整调用链main.py是入口核心逻辑在utils/inference.py里。下面这段是主流程的骨架我按实际跑通的顺序整理# main.py 推理主流程 import cv2 from utils.load_models import load_all_models from utils.inference import run_grounding_dino, run_clip_filter, run_sam_segment # 1. 读图全景图建议先缩放到长边 2000 以内否则 DINO 显存扛不住 image cv2.imread(photos/test1.jpg) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 2. 加载三个模型 sam_predictor, dino, clip_model, clip_preprocess load_all_models() # 3. DINO 出框TEXT_PROMPT 按场景改 TEXT_PROMPT building . road . vegetation . water . boxes, logits, phrases run_grounding_dino( dino, image, TEXT_PROMPT, box_threshold0.3, # 框置信度阈值调低召回高但误检多 text_threshold0.25 # 文本匹配阈值低于此值的类别词被忽略 ) # 4. CLIP 过滤把语义漂移的框丢掉 keep_idx run_clip_filter( clip_model, clip_preprocess, image, boxes, phrases, clip_threshold0.22 # 相似度阈值全景图场景建议 0.2-0.25 ) boxes boxes[keep_idx] phrases phrases[keep_idx] # 5. SAM 分割把每个框转成精细掩码 masks run_sam_segment(sam_predictor, image, boxes) # 6. 可视化叠加掩码和类别标签逻辑上分四步DINO 负责召回box_threshold控制框的多少CLIP 负责精排clip_threshold控制语义一致性SAM 负责像素级边界。三个阈值是联动的——DINO 阈值调低进 CLIP 的框变多CLIP 阈值就得相应调高来压误检否则 SAM 会被大量垃圾框拖慢。我一般先用box_threshold0.3、clip_threshold0.22跑一遍看效果再针对性微调。3.3 阈值参数对结果的影响与调参方法这三个阈值不是拍脑袋定的得看场景。下面这张表是我在几组全景图上实测出来的经验区间参数作用偏低时现象偏高时现象建议区间box_thresholdDINO 框置信度误检多背景出框漏检小目标丢失0.25-0.35text_threshold类别词匹配度无关类别被激活部分类别完全不响应0.2-0.3clip_thresholdCLIP 语义相似度语义漂移框保留正确框被误杀0.2-0.28调参顺序建议先固定 CLIP 阈值不动调 DINO 的两个阈值让召回合理再固定 DINO调 CLIP 阈值压误检。全景图里小目标比如零星车辆容易被 DINO 漏掉这时候把box_threshold降到 0.2 能捞回来一些但代价是背景框暴增必须靠 CLIP 兜住。如果发现某一类地物完全不出框先检查提示词拼写和句点格式再降text_threshold别一上来就怀疑模型。4. 避坑与排查跑不通时先看这几条4.1 报错 No module named groundingdino现象是import groundingdino直接失败或者load_model找不到配置路径。原因通常是 git 安装没成功或者装到了另一个 Python 环境里。解决方式是先pip show groundingdino确认装没装、装在哪如果没装重新执行pip install githttps://github.com/IDEA-Research/GroundingDINO.git装完再python -c import groundingdino; print(groundingdino.__file__)确认路径。如果装了还报错多半是虚拟环境没激活检查which python指向的是不是当前环境。4.2 显存溢出 CUDA out of memory现象是跑到 SAM 分割阶段直接崩或者 DINO 推理时显存爆掉。原因是全景图分辨率太高加上 SAM 的vit_h权重本身就吃显存。解决办法分两步先把输入图长边缩到 1500 到 2000 之间别拿原图硬跑再把 SAM 换成vit_b权重显存占用能降到 2G 左右。如果还不行把 DINO 的 batch 设为 1并且每处理完一张图手动torch.cuda.empty_cache()。全景图场景下我习惯先缩图跑通流程确认逻辑没问题再考虑分块处理原图。4.3 掩码结果全是背景或者边界糊成一团现象是 SAM 输出的掩码要么覆盖整张图要么边缘像狗啃的。原因通常是喂给 SAM 的框质量太差——框太大或者框重叠严重SAM 会把它当成一个整体去分割。解决方式是回头检查 DINO 出的框把box_threshold适当调高并且在送 SAM 之前加一步 NMS 去重叠框。另外 SAM 的predict接口默认multimask_outputTrue会返回三个候选掩码选分数最高的那个别直接取第一个。4.4 CLIP 过滤后正确类别被误杀现象是明明图里有建筑DINO 也出了框但 CLIP 过滤之后建筑框全没了。原因是 CLIP 对遥感视角的语义理解和自然图像有偏差building这个词在俯视图上和 CLIP 训练分布里的建筑差异较大。解决办法是把clip_threshold从 0.22 降到 0.18 试试或者换ViT-L/14模型提升语义区分度。还有一个技巧是在提示词里加同义词比如building . rooftop .让 CLIP 有更多匹配机会但注意别加太多否则 DINO 那边又会出重复框。5. 进阶玩法分块推理与自定义类别扩展全景图动辄 8000 像素以上直接缩图会丢小目标直接原图跑又爆显存我现在的做法是滑窗分块加坐标回填。把大图切成 1024×1024 的块块之间留 128 像素重叠每块单独跑完整流水线最后把掩码按全局坐标拼回去。重叠区域用 NMS 去重避免同一个目标被切出两个掩码。这套逻辑不复杂但坐标换算容易翻车我一般会先拿一张小图模拟分块验证回填坐标准确了再上大图。# 滑窗分块推理的核心坐标换算 import numpy as np def sliding_window_inference(image, patch_size1024, overlap128): h, w image.shape[:2] stride patch_size - overlap all_masks, all_boxes, all_phrases [], [], [] for y in range(0, h, stride): for x in range(0, w, stride): # 边界处理保证块不越界 y1, x1 min(y, h - patch_size), min(x, w - patch_size) patch image[y1:y1patch_size, x1:x1patch_size] # 在块上跑完整流水线拿到局部掩码和框 masks, boxes, phrases run_full_pipeline(patch) # 坐标回填到全局 for m, b, p in zip(masks, boxes, phrases): full_mask np.zeros((h, w), dtypebool) full_mask[y1:y1patch_size, x1:x1patch_size] m all_masks.append(full_mask) all_boxes.append(b np.array([x1, y1, x1, y1])) all_phrases.append(p) # 全局 NMS 去重IoU 阈值 0.5 keep global_nms(all_boxes, iou_threshold0.5) return [all_masks[i] for i in keep], [all_phrases[i] for i in keep]参数上patch_size设 1024 是因为 DINO 和 SAM 在这个尺寸下显存和精度平衡最好overlap设 128 是为了让跨块目标至少在一个块里完整出现。global_nms的 IoU 阈值我一般用 0.5太低会把相邻同类目标误合并太高去重不干净。这套方案跑一张 8000×6000 的全景图在 8G 显存的卡上大概 3 到 5 分钟比缩图方案慢但小目标召回明显更好。自定义类别扩展也很直接改TEXT_PROMPT就行。比如要加光伏板和裸土提示词改成building . road . vegetation . water . solar panel . bare soil .然后重新跑一遍。但要注意CLIP 对solar panel这种复合词的语义编码不如单词稳如果发现光伏板识别率低可以拆成solar . panel .分别试或者用photovoltaic替代。我一般新加类别时会先用几张典型图单独测这一类确认 CLIP 能区分开再合进主提示词避免一个类别拖垮整条流水线的精度。从那以后我每次改提示词或者换场景都会先拿三张典型图跑一遍全流程把 DINO 的框、CLIP 的分数、SAM 的掩码逐张看一遍再批量跑这个习惯帮我省了很多返工。希望帮到你。本文还有配套的精品资源点击获取