☰
全景图地物分类实战:SAM-DINO-CLIP三模型串行流水线搭建与优化
2026/10/1 16:41:15 网站建设 项目流程

简介:这份资源面向计算机视觉方向的学习者与开发者,提供一套基于SAM、DINO与CLIP组合模型的全景图智能图像处理系统,用于解决复杂大视野场景下的地物分类、实例分割与语义识别问题,并融入零样本学习与提示工程思路,适合具备一定深度学习基础、希望研究多模型协同分割方案的中高级读者。压缩包共70个文件,约3.89MB,以54个Python源码为主体,涵盖模型加载、推理与工具函数等模块,另含C++与CUDA源码、头文件、示例图片及说明文档,便于理解工程结构与运行流程。目前已有204人学习下载。资源包含主程序入口、模型构建与自动掩码生成等核心脚本,并附有使用说明与补充文档,可帮助读者快速梳理SAM-DINO-CLIP的集成方式、推理链路与评估思路,为全景图像分割、遥感分析与地物识别等任务提供可复用的工程参考。

1. 全景图地物分类为什么需要 SAM-DINO-CLIP 三件套:从一张 360 度照片说起

你手里有一张全景图,可能是无人机航拍拼接出来的,也可能是车载环视相机拍的。它和普通照片最大的区别是:视野覆盖 360 度,地物种类杂,近处有路面、车辆、行人,远处有建筑、植被、天空,而且因为全景投影的关系,同一个物体在不同位置会发生拉伸和畸变。传统做法是拿一个在 COCO 或 VOC 上训好的检测模型直接推理,结果往往是近处还行,边缘区域一塌糊涂,遇到训练集里没出现过的地物类别直接哑火。

SAM-DINO-CLIP 这套组合模型,本质上是把三个各自领域最强的模型串起来用:DINO 负责开放词汇的目标检测,给出候选框和类别语义;SAM 负责把这些框变成像素级精确的实例掩码;CLIP 负责在零样本条件下对候选区域做语义匹配和分类校准。全景图像分割、地物分类、实例分割、语义识别、目标检测、零样本学习、提示工程这几个词,在这套系统里不是并列关系,而是流水线关系。你不需要重新训练一个端到端的大模型,而是用提示工程把三个模型的能力拼起来,快速搭出一个能跑、能调、能落地的智能图像处理系统。适合谁?适合手里有标注数据但不够多、想快速验证地物分类方案、又不想从零训检测器的工程师。

2. 三模型串行流水线怎么搭:从 DINO 出框到 SAM 出掩码再到 CLIP 定类

2.1 为什么不是端到端训练,而是串行组合

端到端训练一个全景图地物分类模型,你需要大量像素级标注,而且全景畸变会让数据增强变得很麻烦。串行组合的好处是每个模型只做自己最擅长的事:DINO 的开放词汇检测能力让你不用为每个地物类别单独训检测头;SAM 的提示分割能力让你不用为每个类别训分割头;CLIP 的图文对齐能力让你在类别名称变化时只需要改文本提示,不用改模型权重。代价是推理速度比单模型慢,但换来的是零样本泛化和快速迭代。

常见做法是先用 DINO 对全景图做一次推理,拿到一批候选框和初始类别分数。然后把每个框作为 SAM 的 box prompt,让 SAM 输出对应的实例掩码。最后把每个掩码区域裁剪出来,送进 CLIP 做图像编码,和一组文本提示做相似度匹配,得到最终类别。整个流程里,DINO 的框质量决定召回率,SAM 的掩码质量决定实例分割精度,CLIP 的提示词质量决定分类准确率。

2.2 环境准备与模型加载的最小命令

我一般会先建一个干净的 Python 环境,把三个模型的依赖分开装,避免版本冲突。DINO 用 detectron2 或官方仓库,SAM 用 segment-anything,CLIP 用 openai/clip。下面是一个最小可跑的加载脚本:

import torch from segment_anything import sam_model_registry, SamPredictor from groundingdino.util.inference import load_model, load_image, predict import clip # 加载 DINO 模型,配置文件路径按你实际下载的权重调整 dino_model = load_model( "groundingdino/config/GroundingDINO_SwinT_OGC.py", "weights/groundingdino_swint_ogc.pth" ) # 加载 SAM 模型,vit_h 精度最高但显存占用大,vit_b 适合快速验证 sam = sam_model_registry["vit_h"](checkpoint="weights/sam_vit_h_4b8939.pth") sam.to(device="cuda") sam_predictor = SamPredictor(sam) # 加载 CLIP 模型,ViT-B/32 速度快,ViT-L/14 精度高 clip_model, clip_preprocess = clip.load("ViT-B/32", device="cuda")

逻辑说明:DINO 的 load_model 需要配置文件和权重文件两个参数,配置文件决定了网络结构和输入尺寸。SAM 的 sam_model_registry 支持 vit_b、vit_l、vit_h 三种规格,显存不够就降级。CLIP 的 clip.load 返回模型和预处理函数,预处理函数负责把裁剪区域 resize 到 224x224 并归一化。参数方面,DINO 的输入尺寸默认 800x1333,全景图建议先 resize 到长边 1333 再推理,否则显存容易爆。

2.3 用文本提示驱动 DINO 检测地物

DINO 的核心用法是给一段文本提示,它会把提示里的每个名词当作潜在类别去检测。全景图地物分类的提示词不能只写“building”,要写得更具体,比如“building. road. tree. car. person. sky.”。注意 DINO 的提示词用句点分隔,末尾也要加句点。

from groundingdino.util.inference import load_image, predict import torchvision.transforms as T image_source, image = load_image("panorama.jpg") # 文本提示:地物类别用句点分隔,末尾加句点 TEXT_PROMPT = "building. road. tree. car. person. sky. grass. water." boxes, logits, phrases = predict( model=dino_model, image=image, caption=TEXT_PROMPT, box_threshold=0.35, # 框置信度阈值,全景图建议 0.3-0.4 text_threshold=0.25 # 文本匹配阈值,太低会引入误检 ) print(f"检测到 {len(boxes)} 个候选框") for box, logit, phrase in zip(boxes, logits, phrases): print(f"{phrase}: {logit:.3f}, box={box.tolist()}")

逻辑说明:predict 返回三个东西——boxes 是归一化坐标的框,logits 是置信度,phrases 是匹配到的类别名。box_threshold 控制哪些框保留,text_threshold 控制文本和区域的匹配严格程度。全景图因为畸变,边缘区域的框置信度普遍偏低,我一般会把 box_threshold 降到 0.3 左右,宁可多留一些候选框,后面用 CLIP 再筛。注意 DINO 输出的框是归一化的 cxcywh 格式,要转成 xyxy 才能给 SAM 用。

2.4 把 DINO 的框喂给 SAM 做实例分割

SAM 支持三种提示:点、框、掩码。DINO 给的是框,所以直接用 box prompt。SAM 的 SamPredictor 需要先 set_image,然后对每个框调用 predict。

import numpy as np import cv2 # 把全景图转成 SAM 需要的 RGB numpy 数组 image_bgr = cv2.imread("panorama.jpg") image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) sam_predictor.set_image(image_rgb) # 把 DINO 的归一化 cxcywh 转成像素 xyxy h, w = image_rgb.shape[:2] boxes_xyxy = [] for box in boxes: cx, cy, bw, bh = box.tolist() x1 = (cx - bw / 2) * w y1 = (cy - bh / 2) * h x2 = (cx + bw / 2) * w y2 = (cy + bh / 2) * h boxes_xyxy.append([x1, y1, x2, y2]) # 对每个框生成掩码 masks = [] for box in boxes_xyxy: mask, score, _ = sam_predictor.predict( box=np.array(box), multimask_output=False # 单掩码模式,速度快 ) masks.append(mask[0]) print(f"生成 {len(masks)} 个实例掩码")

逻辑说明:set_image 只做一次,SAM 会把图像编码缓存起来,后续每个框的 predict 都复用这个编码,所以速度比重新编码快很多。multimask_output=False 表示每个框只输出一个掩码,适合实例分割场景;如果要做语义分割,可以设 True 然后取分数最高的。参数方面,SAM 的输入图像长边建议不超过 1024,全景图太大就先 resize,否则显存和速度都吃不消。掩码是布尔数组,True 表示该像素属于这个实例。

2.5 用 CLIP 做零样本分类校准

DINO 给出的类别有时候会错,尤其是全景图边缘区域。CLIP 的作用是把每个掩码区域裁剪出来,和一组文本提示做相似度匹配,得到更稳的类别。

import torch from PIL import Image # 准备文本提示,每个类别可以写多个同义词 class_names = ["building", "road", "tree", "car", "person", "sky", "grass", "water"] text_prompts = [f"a photo of a {name}" for name in class_names] text_tokens = clip.tokenize(text_prompts).to("cuda") with torch.no_grad(): text_features = clip_model.encode_text(text_tokens) text_features /= text_features.norm(dim=-1, keepdim=True) # 对每个掩码区域做 CLIP 分类 final_labels = [] for mask in masks: # 用掩码裁剪区域,背景置黑 masked_image = image_rgb.copy() masked_image[~mask] = 0 pil_image = Image.fromarray(masked_image) image_input = clip_preprocess(pil_image).unsqueeze(0).to("cuda") with torch.no_grad(): image_features = clip_model.encode_image(image_input) image_features /= image_features.norm(dim=-1, keepdim=True) similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1) pred_idx = similarity.argmax().item() final_labels.append(class_names[pred_idx]) print("最终分类结果:", final_labels)

逻辑说明:CLIP 的文本编码和图像编码要分别归一化,然后做点积得到相似度。乘以 100 是温度系数,让 softmax 更尖锐。掩码区域裁剪时把背景置黑,可以减少背景干扰。参数方面,CLIP 的输入是 224x224,clip_preprocess 会自动做 resize 和归一化。如果某个类别在 CLIP 里表现不好,可以换更具体的提示词,比如“aerial view of a building”比“building”更适合航拍全景图。

3. 全景图畸变和提示工程踩过的坑:地物分类准确率上不去的排查清单

3.1 边缘区域框置信度低导致漏检

现象:全景图左右边缘的建筑和树木经常检测不到,DINO 输出的框集中在画面中央。原因:全景图在拼接时边缘区域会有拉伸和畸变,DINO 的训练数据以普通透视图像为主,对畸变区域的响应偏弱。解决:先把全景图切成多个透视子图分别检测,再把结果映射回全景坐标;或者把 box_threshold 降到 0.25,配合 CLIP 二次筛选。我一般用第一种,切 6 到 8 个子图,重叠 20%,效果比直接降阈值稳。

3.2 SAM 掩码在天空和路面区域出现空洞

现象:SAM 对天空、路面这种大面积纹理单一的区域,掩码经常出现内部空洞或者边界锯齿。原因:SAM 的提示编码器对低对比度区域的边界不敏感,box prompt 只给了外接矩形,没有给内部细节。解决:在 box prompt 基础上加几个正样本点,点选在区域内部;或者对掩码做形态学闭运算,填掉小空洞。代码上可以用 cv2.morphologyEx 做闭运算,kernel 大小 5x5 到 15x15 之间调。

3.3 CLIP 把相似类别搞混

现象:草地和树木、路面和建筑经常被 CLIP 分错。原因:CLIP 的图文对齐是在通用图像上训的,对航拍和全景视角的语义区分不够细。解决:提示词里加视角描述,比如“aerial view of grass”和“aerial view of tree”;或者用 DINO 的初始类别做约束,只在 DINO 给出的 top-3 类别里让 CLIP 选,而不是全类别里选。我一般两个方法一起用,准确率能提 10 到 15 个百分点。

3.4 显存不够导致推理中断

现象:跑全景图时 CUDA out of memory,尤其是 SAM 的 vit_h 模型。原因:全景图分辨率高,DINO 和 SAM 同时加载在显存里,加上中间特征图,很容易超过 12G。解决:DINO 和 SAM 分时加载,先跑 DINO 存下框,释放 DINO 显存,再加载 SAM;或者把 SAM 换成 vit_b,精度损失不大但显存省一半。CLIP 的 ViT-B/32 比 ViT-L/14 省显存,验证阶段用 B/32 就够了。

3.5 提示词里的类别顺序影响结果

现象:同样的类别集合,换个顺序写,DINO 的检测结果会变。原因:DINO 的文本编码器对提示词的顺序敏感,尤其是类别数量多的时候。解决:把出现频率高的类别写在前面,比如“building. road. tree. car. person. sky.”;类别数量控制在 10 个以内,太多会互相干扰。如果类别确实多,分批检测,每批 5 到 6 个类别。

4. 从单张全景图到批量处理:把流水线封装成可复用的推理脚本

4.1 用配置文件管理类别和阈值

每次改类别和阈值都去改代码很麻烦,我一般用一个 YAML 配置文件管理这些参数。这样换数据集或者换场景时,只改配置不改代码。

# config.yaml dino: box_threshold: 0.35 text_threshold: 0.25 prompt: "building. road. tree. car. person. sky. grass. water." sam: model_type: "vit_h" checkpoint: "weights/sam_vit_h_4b8939.pth" clip: model_name: "ViT-B/32" class_names: ["building", "road", "tree", "car", "person", "sky", "grass", "water"] prompt_template: "aerial view of a {}" output: save_mask: true save_visualization: true output_dir: "results/"

逻辑说明:prompt_template 里的 {} 会被 class_names 里的每个类别替换,生成 CLIP 的文本提示。save_mask 控制是否保存每个实例的掩码 PNG,save_visualization 控制是否保存叠加了掩码和标签的可视化图。output_dir 是结果保存目录,批量处理时每个输入图对应一个子目录。

4.2 批量推理的主循环

批量处理的核心是把单张图的流程包成一个函数,然后遍历输入目录。注意每张图处理完后要清理中间变量,避免显存累积。

import os import yaml import cv2 import numpy as np import torch from PIL import Image def process_single_image(image_path, config, dino_model, sam_predictor, clip_model, clip_preprocess): image_bgr = cv2.imread(image_path) image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) h, w = image_rgb.shape[:2] # DINO 检测 _, image_tensor = load_image(image_path) boxes, logits, phrases = predict( model=dino_model, image=image_tensor, caption=config["dino"]["prompt"], box_threshold=config["dino"]["box_threshold"], text_threshold=config["dino"]["text_threshold"] ) if len(boxes) == 0: return None # 转 xyxy boxes_xyxy = [] for box in boxes: cx, cy, bw, bh = box.tolist() boxes_xyxy.append([ (cx - bw/2) * w, (cy - bh/2) * h, (cx + bw/2) * w, (cy + bh/2) * h ]) # SAM 分割 sam_predictor.set_image(image_rgb) masks = [] for box in boxes_xyxy: mask, _, _ = sam_predictor.predict( box=np.array(box), multimask_output=False ) masks.append(mask[0]) # CLIP 分类 class_names = config["clip"]["class_names"] text_prompts = [ config["clip"]["prompt_template"].format(name) for name in class_names ] text_tokens = clip.tokenize(text_prompts).to("cuda") with torch.no_grad(): text_features = clip_model.encode_text(text_tokens) text_features /= text_features.norm(dim=-1, keepdim=True) final_labels = [] for mask in masks: masked_image = image_rgb.copy() masked_image[~mask] = 0 pil_image = Image.fromarray(masked_image) image_input = clip_preprocess(pil_image).unsqueeze(0).to("cuda") with torch.no_grad(): image_features = clip_model.encode_image(image_input) image_features /= image_features.norm(dim=-1, keepdim=True) similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1) pred_idx = similarity.argmax().item() final_labels.append(class_names[pred_idx]) return { "boxes": boxes_xyxy, "masks": masks, "labels": final_labels, "phrases": phrases } # 主循环 with open("config.yaml", "r") as f: config = yaml.safe_load(f) input_dir = "panoramas/" output_dir = config["output"]["output_dir"] os.makedirs(output_dir, exist_ok=True) for fname in os.listdir(input_dir): if not fname.lower().endswith((".jpg", ".png", ".jpeg")): continue image_path = os.path.join(input_dir, fname) result = process_single_image( image_path, config, dino_model, sam_predictor, clip_model, clip_preprocess ) if result is None: print(f"{fname}: 未检测到目标") continue # 保存可视化结果 vis_image = cv2.imread(image_path) for mask, label in zip(result["masks"], result["labels"]): color = np.random.randint(0, 255, 3).tolist() vis_image[mask] = vis_image[mask] * 0.5 + np.array(color) * 0.5 ys, xs = np.where(mask) if len(xs) > 0: cv2.putText( vis_image, label, (int(xs.mean()), int(ys.mean())), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2 ) cv2.imwrite(os.path.join(output_dir, fname), vis_image) print(f"{fname}: 检测到 {len(result['masks'])} 个实例")

逻辑说明:process_single_image 把单张图的完整流程封装起来,返回框、掩码、标签和 DINO 的原始类别。主循环遍历输入目录,对每张图调用这个函数,保存可视化结果。参数方面,可视化时用随机颜色叠加掩码,透明度 0.5,标签写在掩码区域的质心位置。注意每次循环结束后 Python 的垃圾回收会自动清理中间变量,但如果显存还是涨,可以手动加 torch.cuda.empty_cache()。

4.3 结果后处理:合并重叠掩码和过滤小区域

SAM 对同一个物体会输出多个重叠掩码,尤其是 DINO 给了多个框的时候。后处理要做两件事:合并 IoU 高的掩码,过滤面积过小的掩码。

def merge_masks(masks, labels, iou_threshold=0.5, min_area=100): """合并重叠掩码并过滤小区域""" keep = [] for i, mask_i in enumerate(masks): if mask_i.sum() < min_area: continue merged = False for j in keep: mask_j = masks[j] intersection = np.logical_and(mask_i, mask_j).sum() union = np.logical_or(mask_i, mask_j).sum() iou = intersection / union if union > 0 else 0 if iou > iou_threshold: # 合并到已有掩码 masks[j] = np.logical_or(masks[j], mask_i) merged = True break if not merged: keep.append(i) return [masks[i] for i in keep], [labels[i] for i in keep]

逻辑说明:merge_masks 遍历所有掩码,如果当前掩码和已保留的某个掩码 IoU 超过阈值,就合并进去;否则作为新掩码保留。min_area 过滤掉面积小于 100 像素的噪声掩码。参数方面,iou_threshold 设 0.5 比较稳,太高会漏合并,太低会把不同物体合并。min_area 根据全景图分辨率调,1080p 的全景图设 100 到 200 之间。

5. 零样本地物分类的进阶技巧:用提示集成和掩码精修把准确率再提一档

5.1 提示集成:同一个类别写多个提示词取平均

CLIP 对提示词很敏感,同一个类别换种说法,分类结果可能就变了。提示集成(prompt ensemble)的做法是每个类别写多个提示词,分别编码后取平均,再和图像特征做相似度。

# 每个类别多个提示词 class_prompts = { "building": ["aerial view of a building", "a photo of a building", "building from above"], "road": ["aerial view of a road", "a photo of a road", "road surface"], "tree": ["aerial view of a tree", "a photo of a tree", "tree canopy"], "car": ["aerial view of a car", "a photo of a car", "vehicle"], "person": ["aerial view of a person", "a photo of a person", "pedestrian"], "sky": ["aerial view of sky", "a photo of sky", "clear sky"], "grass": ["aerial view of grass", "a photo of grass", "grassland"], "water": ["aerial view of water", "a photo of water", "water body"] } # 编码所有提示词并取平均 class_features = {} for class_name, prompts in class_prompts.items(): tokens = clip.tokenize(prompts).to("cuda") with torch.no_grad(): features = clip_model.encode_text(tokens) features /= features.norm(dim=-1, keepdim=True) class_features[class_name] = features.mean(dim=0) class_features[class_name] /= class_features[class_name].norm() # 分类时和每个类别的平均特征做相似度 text_features = torch.stack(list(class_features.values())) class_names = list(class_features.keys())

逻辑说明:每个类别的多个提示词编码后取平均,得到一个更鲁棒的类别特征。分类时图像特征和这个平均特征做相似度,比单提示词稳。参数方面,每个类别 3 到 5 个提示词就够了,太多会引入噪声。提示词要覆盖不同视角和说法,比如“aerial view of”和“a photo of”搭配使用。

5.2 掩码精修:用 SAM 的 multimask 输出做边界优化

SAM 的 multimask_output=True 会输出三个掩码,分别对应不同粒度。我一般取分数最高的那个,但如果边界不理想,可以把三个掩码做加权平均再二值化。

def refine_mask(sam_predictor, box, image_rgb): """用 multimask 输出做掩码精修""" masks, scores, _ = sam_predictor.predict( box=np.array(box), multimask_output=True ) # 按分数加权平均 weights = scores / scores.sum() weighted_mask = np.zeros_like(masks[0], dtype=np.float32) for mask, weight in zip(masks, weights): weighted_mask += mask.astype(np.float32) * weight # 二值化 refined = weighted_mask > 0.5 return refined

逻辑说明:multimask_output=True 时 SAM 输出三个掩码和对应的分数,分数越高表示 SAM 越自信。按分数加权平均后二值化,边界比单掩码更平滑。参数方面,阈值 0.5 是经验值,如果掩码偏小可以降到 0.4,偏大就升到 0.6。这个方法对建筑和道路的边界提升明显,对天空和草地提升不大。

5.3 用 DINO 的类别分数做 CLIP 的候选约束

DINO 输出的 phrases 里已经包含了初始类别,虽然不一定准,但可以作为 CLIP 的候选约束。具体做法是:对每个掩码,只取 DINO 给出的 top-3 类别,让 CLIP 在这三个里选,而不是全类别里选。

def constrained_clip_classify(image_features, text_features, class_names, dino_phrase, top_k=3): """用 DINO 的类别做 CLIP 候选约束""" # 找到 DINO 类别在 class_names 里的索引 if dino_phrase in class_names: dino_idx = class_names.index(dino_phrase) else: # 模糊匹配,取相似度最高的 dino_idx = 0 # 取 DINO 类别附近的 top_k 个类别作为候选 candidate_indices = list(range(max(0, dino_idx - top_k), min(len(class_names), dino_idx + top_k + 1))) candidate_features = text_features[candidate_indices] similarity = (100.0 * image_features @ candidate_features.T).softmax(dim=-1) pred_idx = similarity.argmax().item() return class_names[candidate_indices[pred_idx]]

逻辑说明:这个函数先找到 DINO 给出的类别在类别列表里的位置,然后取前后 top_k 个类别作为候选,CLIP 只在候选里选。这样既利用了 DINO 的检测能力,又用 CLIP 做了精细分类。参数方面,top_k 设 3 比较稳,设 1 就完全依赖 DINO,设太大就退化成全类别分类。

5.4 验证方法:用混淆矩阵和 IoU 评估分割质量

做完推理不能只看可视化图,要用量化指标评估。分类用混淆矩阵,分割用 IoU。

from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt def evaluate_classification(true_labels, pred_labels, class_names): """分类评估:混淆矩阵""" cm = confusion_matrix(true_labels, pred_labels, labels=class_names) plt.figure(figsize=(10, 8)) sns.heatmap(cm, annot=True, fmt="d", xticklabels=class_names, yticklabels=class_names) plt.xlabel("Predicted") plt.ylabel("True") plt.title("Confusion Matrix") plt.savefig("confusion_matrix.png") plt.close() def evaluate_segmentation(pred_masks, gt_masks, iou_threshold=0.5): """分割评估:IoU 和 mAP""" ious = [] for pred, gt in zip(pred_masks, gt_masks): intersection = np.logical_and(pred, gt).sum() union = np.logical_or(pred, gt).sum() iou = intersection / union if union > 0 else 0 ious.append(iou) return np.mean(ious), np.mean([1 if iou > iou_threshold else 0 for iou in ious])

逻辑说明:混淆矩阵看分类错在哪,IoU 看分割边界准不准。参数方面,iou_threshold 设 0.5 是常用标准,0.75 更严格。如果 IoU 低于 0.5,优先检查 SAM 的掩码质量;如果分类准确率低于 0.7,优先检查 CLIP 的提示词。

5.5 我踩过的最大的坑:别在全景图上直接跑 SAM

最后说一个血泪经验。我一开始图省事,把整张 4K 全景图直接塞给 SAM,结果显存爆了不说,掩码质量还特别差,边缘全是锯齿。后来改成先切子图再跑 SAM,最后把掩码拼回全景坐标,效果好了不止一个档次。具体做法是:把全景图按经度切成 8 个 90 度子图,每个子图重叠 10 度,分别跑 DINO+SAM+CLIP,然后把子图的掩码按坐标映射回全景图,重叠区域用投票法合并。这个方案显存占用降了 60%,掩码 IoU 提了 15 个百分点。如果你也在做全景图分割,别偷懒,切图这一步省不得。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询