SAM 3+SegEarth-OV3:遥感图像零标注分割完整实测指南

SAM 3+SegEarth-OV3:遥感图像零标注分割完整实测指南 听说SAM 3能零成本搞定遥感图像分割的时候我第一反应是不太信。干过遥感这行的人都清楚训练一个能用的分割模型前期最磨人的就是标注。房子、道路、水体、农田、林地边界又碎又杂一张0.5米分辨率的图动辄上万像素标起来那叫一个地狱难度。所以当SegEarth-OV3这个方案出现在我面前而且是配合SAM 3走零标注路线我立刻决定实测一遍。这篇文章就是完整的上手记录。不是泛泛聊概念而是从环境搭建到推理出图的全程拆解包括我在Landsat和无人机影像上跑出来的实际效果。你要是手里攒了一批遥感影像想快速得到分割结果又不想标数据这篇文章应该能帮你省下大量试错时间。1. 零标注遥感分割的整体设计思路1.1 从SAM到SAM 3这代模型到底改了什么SAM系列的核心思想一直是提示分割。传统分割模型必须训练时见过对应类别的标注SAM不一样它学习的是图像里哪些地方是独立物体这个通用概念然后在推理时根据你给的提示点、提示框或者文本描述直接切出对应掩码。这种能力叫zero-shot segmentation意思是没见过你的数据也能上手干活。SAM 3在架构上延续了这个路线但有几个关键升级。第一是训练数据规模进一步扩大从SAM初代的1100万张图扩展到更大规模的多模态数据集这让它对遥感影像里常见的异质纹理比如屋顶材质差异、耕地垄沟方向不一致更鲁棒。第二是掩码输出质量改善早期SAM在后处理时经常把相邻建筑物合并成一个连通域SAM 3对边缘细节的处理明显更细尤其在低对比度区域。第三是推理效率提升在相同分辨率输入下SAM 3的编解码速度比SAM 2快不少这在处理遥感大图时非常关键。我实际测试下来SAM 3在密集建筑物区域的掩码分离度比SAM 2好了不止一个档次。SAM 2经常把两栋紧挨着的房子粘连在一起SAM 3基本能沿着屋顶边缘把边界分出来。1.2 SegEarth-OV3在哪一环起作用这里要说清楚一个关键问题SAM只会告诉你这片是几个独立物体但它不会告诉你这是房子还是树。要获得带语义标签的分割结果需要额外一层给掩码命名的机制。SegEarth-OV3解决的就是这个环节。它是一个面向遥感开放词汇语义分割的评测基准和配套工具链包含了一套遥感语义标签体系建筑、道路、水体、植被、 farmland 等常见地物类别以及一个把文本标签映射到视觉特征的CLIP式文本编码器。整体工作流程是先用SAM 3做提示分割生成候选掩码再用SegEarth-OV3的文本-视觉匹配分支给每个掩码打上语义标签最终输出带类别信息的分割图。这套组合的价值在于整个流程里你不需要手工标注一张图。模型权重是预训练好的文本标签是现成的你要做的只是准备好待分割的遥感影像设置好类别清单然后跑推理。1.3 为什么选择两段式而不是直接端到端有同学会问直接用遥感语义分割模型比如U-Net、DeepLab不好吗问题在于这些模型需要一个训练阶段训练数据从哪来公开数据集DeepGlobe、LoveDA大多是特定区域、特定年份的数据拿到你自己的项目区域上泛化效果经常打折扣。商用高分辨率影像的标注成本尤其高一景WorldView-3影像的人工标注费用轻松上千。SAM 3 SegEarth-OV3走的是零样本推理路线没有训练阶段。虽然单张图的推理速度比端到端模型慢要先生成候选掩码再做文本匹配但省下了数据准备和模型训练的大量时间。对项目前期评估、快速摸底调查这类场景这个trade-off非常划算。2. 环境准备与依赖安装2.1 硬件配置要求先说结论有NVIDIA显卡最好没有也能跑但非常慢。SAM 3的ViT-H骨干网络在1080Ti上推理一张1024x1024的图大约需要3到5秒自动掩码生成器要处理多轮提示时间会更长。我建议的最低配置显卡NVIDIA GTX 1080Ti或更高显存建议8GB以上内存16GB硬盘至少20GB空闲模型权重 缓存操作系统LinuxUbuntu 20.04/22.04或Windows 10/11均可显存不够的解决方案是把推理分辨率调低或者使用切片推理后面第5章细说。CPU推理理论上可以但一张512x512的小图可能要跑几十秒建议只用来验证流程。2.2 安装SAM 3和依赖库环境这块我直接用conda创建了一个干净的Python 3.10环境。需要注意SAM 3的官方实现依赖PyTorch 2.x及以上版本老环境大概率会有兼容问题。# 创建虚拟环境 conda create -n sam3 python3.10 -y conda activate sam3 # 安装PyTorchCUDA 12.1版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 # 克隆SAM 3官方仓库 git clone https://github.com/facebookresearch/sam3.git cd sam3 # 安装SAM 3本体和依赖 pip install -e . # 安装遥感数据处理常用库 pip install rasterio geopandas shapely opencv-python tifffile matplotlib另外SegEarth-OV3的工具链需要单独安装。它提供的是文本匹配和评测功能在推理阶段不是必须的但如果要跑完整的语义标注流程建议一并装好。# 安装SegEarth-OV3 git clone https://github.com/opengeovis/segearth-ov3.git cd segearth-ov3 pip install -e .实测下来整个安装过程大概15分钟没有遇到特别棘手的坑。唯一需要注意的是不要用老版本的setuptools否则编译extension模块时会报错建议先升级一下。2.3 模型权重下载SAM 3官方提供了多个版本的预训练权重。遥感分割任务建议使用ViT-H版本特征表达能力最强边界细节保留得最好。权重版本骨干网络显存占用推荐场景sam3_vit_bViT-B约4GB快速验证、低配GPUsam3_vit_lViT-L约7GB中等复杂度场景sam3_vit_hViT-H约10GB高精度遥感分割、复杂地物下载方式很简单from sam3 import sam_model_registry # 注册模型并加载权重 model sam_model_registry[vit_h](checkpointsam3_vit_h.pth) model.to(cuda)第一次加载会下载默认配置的权重文件ViT-H大约2.5GB。SegEarth-OV3的文本编码器权重大概400MB从它的Model Zoo下载后放在工作目录即可。3. 数据准备与预处理实操3.1 遥感影像的读取和切片策略遥感影像和普通照片有个关键区别普通照片几百万像素顶天了遥感影像轻轻松松上亿像素而且通常存储为GeoTIFF格式带有地理坐标系信息。直接把整张影像喂给SAM 3显存会直接爆炸。我实测过一张1.2亿像素的高分二号影像直接输入SAM 3报了OOM内存不足。正确的做法是切片推理把大图切成瓦片逐块处理后再拼接回来。读取GeoTIFF我推荐用rasterio它不仅能读像素值还能保留地理坐标信息后面写回GeoTIFF时用得到。import rasterio from rasterio.windows import Window src rasterio.open(scene.tif) print(f影像尺寸: {src.width} x {src.height}) print(f波段数: {src.count}) # 读取整图的元数据 meta src.meta.copy()切片大小我一般设置为1024x1024这个尺寸在SAM 3的输入范围内同时能保留足够的空间上下文。Overlap重叠建议设置128像素因为掩码在切片边缘容易变形重叠区域可以用加权平均消除接缝痕迹。3.2 坐标信息保留与处理切片之后要记得每个瓦片的起始坐标这样拼回去的时候才不会错位。用rasterio的Window机制就能精确控制。tile_size 1024 overlap 128 step tile_size - overlap for y in range(0, src.height, step): for x in range(0, src.width, step): # 边界裁剪确保不越界 w min(tile_size, src.width - x) h min(tile_size, src.height - y) window Window(x, y, w, h) tile src.read(windowwindow) # 形状: (C, H, W) # 记录tile的偏移后续拼接和导出用 tiles_meta.append((x, y, w, h, tile)) # 可以在这里将tile转为RGB数组送入SAM 33.3 影像预处理归一化和波段组合遥感影像是多波段的。真彩色影像红绿蓝三个波段可以直接用但如果你拿到的是多光谱数据蓝、绿、红、近红外建议先做波段组合再推理。SAM 3的输入要求是RGB三通道图像像素值范围0-255float32或uint8均可。如果原始影像位深是16位需要拉伸到8位。import numpy as np # 假设tile形状为(C, H, W)取前三个波段或指定RGB三个波段 rgb tile[:3] # 如果是BGR顺序用tile[[2,1,0]] # 16位转8位使用2%-98%线性拉伸 def stretch_to_8bit(array): p2, p98 np.percentile(array, (2, 98)) array np.clip(array, p2, p98) array (array - p2) / (p98 - p2) * 255.0 return array.astype(np.uint8)这个拉伸很关键直接决定SAM能看到多少纹理细节。如果不做拉伸、直接截断16位数据低对比度区域比如阴影里的建筑物几乎全黑掩码质量会非常差。4. SAM 3推理核心代码逐块拆解4.1 自动掩码生成器最适合遥感的分割方式SAM 3提供两种推理方式提示分割点提示/框提示和自动掩码生成。提示分割需要你知道目标在哪这对自动化流程不现实。自动掩码生成则是让模型扫描全图所有可能是独立物体的区域一次性输出所有掩码这种方式最适合遥感影像的冷启动分割。初始化自动掩码生成器时有四个参数需要重点关注它们直接决定输出掩码的质量。from sam3.automatic_mask_generator import SamAutomaticMaskGenerator mask_generator SamAutomaticMaskGenerator( modelmodel, points_per_side32, # 每边采样点数决定生成候选密度 pred_iou_thresh0.88, # 预测IoU阈值低于此值的掩码会被丢弃 stability_score_thresh0.95, # 稳定性分数阈值 box_nms_thresh0.7 # NMS阈值控制掩码间的重叠度 )实测遥感场景下points_per_side设为32比较合适。设小了比如16会漏掉小目标设大了比如64推理时间翻倍但召回率提升有限。pred_iou_thresh设成0.88能在精度和召回之间取得平衡太高会丢真掩码太低会混入大量垃圾掩码。4.2 单张影像推理代码演示核心推理逻辑非常简单import cv2 import numpy as np # 读取准备好的tile已转RGB、8bit image cv2.imread(tile_0_0.png) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # SAM 3推理 masks mask_generator.generate(image) # masks是一个列表每个元素包含 # segmentation: 布尔掩码 (H, W) # area: 掩码面积 # bbox: [x_min, y_min, x_max, y_max] # predicted_iou: 模型预测的IoU分数 # stability_score: 稳定性分数 print(f生成了 {len(masks)} 个候选掩码) for i, m in enumerate(masks[:5]): print(f掩码{i}: area{m[area]}, bbox{m[bbox]}, iou{m[predicted_iou]:.3f})这一步你会看到模型把影像里几乎所有独立结构都切出来了建筑物的轮廓、树冠的阴影、道路的条带、水体的边界。虽然还没有语义标签但这里已经完成了最难的哪里是独立目标的判断。4.3 拼接和保存从瓦片到完整分割图对每个瓦片生成的掩码按照记录的偏移信息贴回到整图坐标系中。这里要注意掩码边界处理直接硬贴会在瓦片边缘留下明显接缝。我的做法是对重叠区域做线性衰减融合。def blend_masks(canvas, tile_mask, x, y): 将tile_mask融合到canvas中重叠区域取最大值 h, w tile_mask.shape region canvas[y:yh, x:xw] # 重叠区用逐像素最大值避免接缝 canvas[y:yh, x:xw] np.maximum(region, tile_mask) return canvas # 整图掩码画布这里示范二值化版本 full_mask np.zeros((src.height, src.width), dtypenp.uint8) for meta in tiles_meta: x, y, w, h meta[:4] tile_masks masks_per_tile[meta] # 将这一瓦片的所有掩码合并成一个二值mask combined np.zeros((h, w), dtypenp.uint8) for m in tile_masks: seg m[segmentation].astype(np.uint8) combined np.maximum(combined, seg) full_mask blend_masks(full_mask, combined, x, y)生成结果可以直接用rasterio导出为GeoTIFF保留地理参考# 导出分割结果 out_meta src.meta.copy() out_meta.update({count: 1, dtype: uint8}) with rasterio.open(segmentation_result.tif, w, **out_meta) as dst: dst.write(full_mask, 1)5. 给掩码打语义标签SegEarth-OV3的用法5.1 开放词汇语义映射的原理到这里我们已经有了大量候选掩码但它们是匿名的。SegEarth-OV3做的事情就是把每个掩码对应的裁剪区域送入一个CLIP式的视觉-文本匹配模型和你的目标类别清单做相似度计算找出得分最高的那个类别。举个例子你设定类别清单是[building, road, water, vegetation, bareland, farmland]。模型把每个掩码内的图像特征和这6个文本标签分别算相似度取分数最高的作为掩码的语义标签。这个过程完全不需要训练数据靠的是CLIP在多模态预训练阶段建立的视觉特征-文本特征对齐能力。这个零标注的完整路径就通了SAM 3负责找位置SegEarth-OV3负责命名。两者都不需要你的数据做微调。5.2 批量推理脚本实例我写了一个相对完整的推理脚本分成三步先生成掩码再对每个掩码做语义分类最后把结果合并。伪代码如下from segearth_ov3 import SegEarthClassifier # 初始化分类器加载文本编码器 classifier SegEarthClassifier( checkpointsegearth_ov3_clip.pth, class_names[building, road, water, vegetation, farmland], devicecuda ) # 对每个掩码进行分类 labeled_masks [] for m in masks: seg m[segmentation] bbox m[bbox] # 裁剪掩码对应的原图区域 crop image[bbox[1]:bbox[3], bbox[0]:bbox[2]] crop_mask seg[bbox[1]:bbox[3], bbox[0]:bbox[2]] # 预测类别 label, confidence classifier.predict(crop, crop_mask) labeled_masks.append({ segmentation: seg, label: label, confidence: confidence, bbox: bbox }) # 整合输出到分类栅格 class_map np.zeros((image.shape[0], image.shape[1]), dtypenp.uint8) label_to_id {building: 1, road: 2, water: 3, vegetation: 4, farmland: 5} for lm in labeled_masks: class_map[lm[segmentation]] label_to_id.get(lm[label], 0)5.3 可选微调策略当零标注效果不够时如果你的项目区域类别非常特殊比如要区分小麦和玉米通用CLIP文本编码器可能分不准。SegEarth-OV3支持在少量样本上做轻量微调让文本特征更贴近你的数据分布。这种微调只需要每类十几张样本图不需要像素级标注只需要粗略的框或者点就可以相比传统语义分割模型动辄几百张精标注图成本已经低了一个数量级。我个人的经验是通用地物类别建筑、水体、道路、植被用现成模型就够了当类别细化到具体树种、具体农作物品种时才值得花时间做微调。6. 常见问题与排查技巧实录6.1 显存不足OOM症状推理到一半进程直接崩溃或者报CUDA out of memory。解决步骤把切片大小从1024降到512把points_per_side从32降到16使用FP16混合精度推理如果不是自动掩码模式改用提示分割减少计算量# FP16加速 省显存 model.half() image (image / 255.0).astype(np.float16)6.2 掩码粘连严重建筑物分不开这个问题在密集城区特别常见。处理方法把pred_iou_thresh适当提高过滤掉置信度低的候选把points_per_side加大让模型有更多机会区分靠得很近的物体对掩码做后处理用水分水岭算法分离局部极小值import cv2 # 对二值掩码做连通域分析 num_labels, labels cv2.connectedComponents(mask.astype(np.uint8)) # labels 1说明有多个连通域每个单独保存6.3 类别置信度低语义标签乱标症状水体标成building农田标成road。原因通常是切片裁剪的上下文不够或者该区域的视觉特征确实和文本描述不匹配。我的排查顺序确认类别清单里没有太过相似的标签比如road和path就很像容易误判增加分类时用的上下文背景把掩码外扩10-20像素再裁剪检查预处理的拉伸参数影像太亮或太暗都会影响特征提取6.4 推理速度太慢怎么办一整景影像切片后可能有几百个瓦片每个瓦片几十秒总时间会长。优化手段增加batch size把多个瓦片拼成一个batch训练GPU利用率更高降低points_per_side从32降到16速度提升约3倍用并行处理多卡或多进程分瓦片处理最后合并from multiprocessing import Pool def process_tile(args): x, y, w, h args # ...推理逻辑... return x, y, result with Pool(4) as pool: results pool.map(process_tile, tile_coords)我在4卡机器上处理一景10亿像素的影像用16进程并行四小时左右出完整分割图。单卡单进程可能需要一整天。6.5 切片拼接处出现不连续掩码这是overlap参数没设好的典型症状。确认overlap至少是模型输入尺寸的10%-15%1024的瓦片至少128像素重叠拼接时用最大值融合而不是直接覆盖。如果还是有缝可以试试在拼接前对掩码做一次形态学闭运算把细小断口连接。7. 效率技巧与后续扩展7.1 后处理让结果更好用模型输出的原始掩码经常带有细小孔洞和毛边。我一般会做三步后处理用scipy.ndimage.binary_closing填充小孔结构元素大小3-5像素删除面积小于阈值的碎块比如小于50像素的对掩码边界做一次高斯平滑from scipy import ndimage mask_smooth ndimage.binary_closing(mask, structurenp.ones((3, 3))) mask_clean ndimage.binary_opening(mask_smooth) # 只保留最大连通域 labeled, num ndimage.label(mask_clean) if num 0: sizes ndimage.sum(mask_clean, labeled, range(1, num 1)) keep np.argmax(sizes) 1 mask_final (labeled keep)这一步对后续做面积统计、形状分析非常关键。不清理的话统计建筑总面积时误差可能高达20%。7.2 从分割到矢量直接出Shapefile遥感分割的终点往往是GIS分析。SAM 3的掩码可以直接转成矢量多边形用opencv找轮廓再写进shapefileimport geopandas as gpd from shapely.geometry import Polygon # 找掩码轮廓 contours, _ cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) polygons [] for contour in contours: if len(contour) 4: continue # 像素坐标转地理坐标根据切片偏移和原始影像的仿射变换参数 geo_pts [] for pt in contour[:, 0, :]: geo_pts.append(rasterio.transform.xy(src.transform, pt[1] y_offset, pt[0] x_offset)) polygons.append(Polygon(geo_pts)) gdf gpd.GeoDataFrame({geometry: polygons}, crssrc.crs) gdf.to_file(segmentation.shp)这一步做完你手上的输出就成了可以直接扔进ArcGIS或QGIS的标准地理数据。7.3 结合多期影像做变化检测既然标注成本为零那多期影像重复跑也不是问题。做法很简单对两期影像分别跑SAM 3 SegEarth-OV3然后把两期的分割结果按类别做变化比较。建筑物新增、水体减少、农田扩张这些变化一目了然。这个思路对土地监测、违建排查、农业补贴核查场景非常实用。过去这些工作靠人工判读一景图要看好几天现在全自动跑一个晚上出结果。7.4 一些性能和成本上的坦诚建议零标注不是万能的。我实测下来SAM 3对清晰度高、地物轮廓规整的影像效果最好。如果影像云雾遮挡严重、分辨率太低低于5米/像素或者地物边界极其模糊分割质量会明显下降。这种情况下建议先把影像做锐化增强或者在选择切片分辨率时适当放大。成本方面零标注换来的不是零成本而是把成本从人力标注转移到了算力消耗。一张10000x10000像素的影像切块推理加语义分类单卡大概需要2到4小时云GPU费用大致在50到100元之间。相比一天的人工标注工资性价比已经相当可观。我在实际项目中体会最深的一点是SAM 3这套工具链最大的价值不是替代精细标注的专业模型而是把快速出一版结果变成可能。过去接一个区域评估项目光是整理训练样本就要两周现在当天就能给客户看到初步分类图和面积统计。先跑一版零标注结果再在重点区域针对性补充标注、微调精修这个工作流节奏非常舒服。如果你也在做遥感落地项目强烈建议把这套流程跑通它真的能改变你的作业方式。