ARTICLE DETAIL

资讯详情

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

零标注遥感分割保姆级教程:SAM 3与SegEarth-OV3实战

零标注遥感分割保姆级教程:SAM 3与SegEarth-OV3实战 搞遥感分割的朋友应该都体会过这个痛标注一张高分影像的成本基本可以劝退大多数个人开发者和中小团队。建筑物轮廓、农田地块、水体边界这些都得有人拿着标注工具一点一点抠一个几千像素的目标抠下来半小时起步。正因如此我一直很关注“不用标注怎么做分割”这个方向。前阵子折腾完 SegEarth-OV3 和 SAM 3 这套组合之后我最大的感受是零标注做遥感分割这件事真的已经从实验室概念变成了可以上手的操作流程。SAM 3 是分割基础模型的新版本官方在语义分割和实例分割上表现又往前推了一截SegEarth-OV3 则是一套专门针对遥感场景的开放性评估协议把“能不能不打标签直接分割”这件事拆成了可量化的步骤。这篇文章我不打算讲太多理论就把我这两天完整跑通的流程整理成保姆级教程从环境配置到数据准备从提示词采样到后处理导出每个环节的坑我都踩了一遍顺手记录下来了。不管你是刚开始接触遥感分割还是已经用过 SAM 系列想迁移到遥感场景这篇应该都能帮你省下不少弯路。1. 整体思路拆解为什么零标注遥感分割能成立先把这个事儿的底层逻辑说清楚不然你照着步骤跑完也不知道自己在调什么。1.1 传统遥感分割为何“贵”以前做遥感影像分割路径基本是收集影像、人工标注、训练模型、推理预测。问题出在前两步影像可以批量下载标注却完全依赖人力。遥感图像的标注和自然图像不一样自然图里一个“人”就是一个框或一条边遥感图里一栋房子、一块田地边缘往往模糊有阴影遮挡有光谱混淆。标注一个类别东一块西一块质量还要经过二次审核。五个类别的标注数据集投入两三个人力做一个月是常有的事。这份成本摆在这里大量细分场景根本凑不出像样的训练集。这也是为什么我一直留意零样本分割方向。如果模型本身已经具备“什么东西都能分”的能力我们为什么还要为每个新场景重新造一套标注数据1.2 SAM 3 做了什么升级SAM 系列的核心思路是用海量掩膜数据预训练一个通用分割模型。输入一张图再输入一些提示点、框或者文本模型输出对应目标的掩膜。SAM 3 在同一条路线上继续升级模型的架构做了优化提示的理解能力更强对边界模糊的目标分割结果更干净。放在遥感场景里这意味着很多以前需要针对性训练的类别——建筑物、道路、裸地、林地——现在可以直接被 SAM 3 “一次到位”地区分出来。你不需要为它准备任何训练样本它本身就已经是“训练好了的”通用分割器。1.3 SegEarth-OV3 补上最后一块拼图但通用分割模型有一个实际问题它会分但它不知道你想分什么。一张影像丢进去模型能输出成千上万个潜在目标的掩膜到底哪个是你要的房子哪个是背景里的空地如果靠人工在图上点来点去提示在大范围遥感影像上效率还是太低。SegEarth-OV3 解决的正是这个问题。它提供了一套在遥感场景下的提示采样、推理编排和结果评估流程。简单来说就是让 SAM 3 在大幅遥感影像上自动生成候选分割掩膜再用类别信息或者规则过滤最终把“模型能力”转成“实际可用结果”。这套协议是从遥感影像和地物特性出发设计的对大幅面的拼接处理、小目标密集地物的提示密度都做了适配。所以这套组合的逻辑闭环是SAM 3 负责通用分割能力SegEarth-OV3 负责遥感场景编排两边一接标注成本归零。2. 环境准备硬件要求与依赖清单先把环境搞定跑模型最烦的就是环境问题。2.1 显存和算力的实测参考先泼一盆冷水零标注不等于零算力。SAM 3 这类基础模型参数量在那里摆着不可能像以前训练个小 Unet 一样随便找块显卡都能跑。我这边的实测环境是一张 RTX 409024GB 显存在 1024×1024 的影像块上推理单块耗时约 1.5 到 2.5 秒具体取决于目标数量。如果你手上是 16GB 显存的显卡比如 4080、V100 16G也能跑但建议把影像切成 512×512 的块防止显存溢出。8GB 显存也不是完全没戏可以用 CPU 推理做兜底但速度会让人比较着急体验差很多。如果你只是想在缺少显卡的机器上快速验证效果可以先下载官方提供的小权重版本占用的显存大约能降到 6GB 左右适合跑通流程正式处理再用完整权重。2.2 依赖库安装与权重下载我是在 Linux 服务器上跑的Windows 下流程基本一致只是路径写法注意区分。核心依赖如下# Python 3.10 以上版本 conda create -n sam3 python3.10 -y conda activate sam3 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install opencv-python pillow numpy rasterio geopandas fiona shapelyrasterio 和 geopandas 这两个是为了读遥感影像和导出矢量结果实际处理矢量导出时还要用到 fiona。如果安装 geopandas 遇到依赖冲突建议用 conda 统一安装conda install -c conda-forge geopandas rasterioSAM 3 的代码和权重需要从官方仓库获取。不同版本的名称略有差异你只需要在仓库的 Release 页面里找到对应版本的权重文件下载即可。以我这次用的版本为例模型权重文件名大致是 sam3_vit_h.pth体积在 2.5GB 左右。下载后放在项目目录下的 weights 文件夹里后边调用直接指定路径。提示下载权重时一定注意检查文件大小是否和页面标注一致。我之前遇到过下载到一半中断但系统没报错的情况结果加载权重时报错“unexpected key in state_dict”排查了很久才发现是文件不完整。环境准备好之后建议先跑一个最小测试加载模型 推理一张普通照片确认整个链路没问题再进遥感数据。这一步能帮你区分后续报错是环境问题还是遥感数据处理的问题。3. 保姆级实操从影像到矢量结果的完整流程这个环节我按完整的处理链路来说每一步都附上代码和思路解释。整体链路是读影像 → 切块 → 生成提示 → SAM 3 推理 → 掩膜清洗 → 拼接导出。3.1 读取大幅遥感影像遥感影像和普通照片的区别在于尺寸大、通道多、带地理坐标。普通的 OpenCV 直接读会丢掉地理信息后边导出的时候坐标就对不上了。我习惯用 rasterio 读影像它能把像素坐标和地理坐标绑定在一起import rasterio from rasterio.windows import from_bounds src rasterio.open(sample_area.tif) print(src.crs) # 坐标系信息比如 EPSG:32650 print(src.transform) # 仿射变换参数 print(src.width, src.height) # 影像宽高 img src.read([1, 2, 3]) # 取 RGB 三个波段 # 转成 HWC 顺序并归一化到 0-255 img img.transpose(1, 2, 0) img img[:, :, ::-1] # BGR 转 RGB根据你的源数据情况决定是否需要如果你的影像是多光谱的不要直接把所有波段喂给模型。SAM 3 预训练时用的是三通道自然图像多出来的波段对模型来说反而是干扰。建议通过主成分分析或者直接选 R、G、B 三个波段合成真彩色图。3.2 影像切块与重叠处理SAM 3 虽然能处理大图但遥感影像动辄上万像素宽直接整张塞进去显存必然爆掉。而且大图上的目标太小模型容易漏检。我的做法是切块处理同时设置重叠区域。def split_image_into_tiles(img, tile_size1024, overlap128): h, w img.shape[:2] tiles [] step tile_size - overlap for y in range(0, h, step): for x in range(0, w, step): y_end min(y tile_size, h) x_end min(x tile_size, w) tile img[y:y_end, x:x_end] # 边缘块可能不足尺寸用 padding 补足 pad_y tile_size - tile.shape[0] pad_x tile_size - tile.shape[1] if pad_y 0 or pad_x 0: tile cv2.copyMakeBorder( tile, 0, pad_y, 0, pad_x, cv2.BORDER_CONSTANT, value(0, 0, 0) ) tiles.append((x, y, tile)) return tiles切块的时候重叠区域设置多大我建议是 128 到 256 像素。重叠的意义在于如果目标恰好跨在切块边界上模型可能在两个块里各分割出一半后边拼接就要做去重合并。没有重叠的话跨边界的目标可能直接在接缝处断开后期很难补。3.3 自动提示采样让模型知道该分谁这是零标注流程里最核心的一步。提示采样的质量直接决定了最后分割结果的完整性。最基本的思路是网格采样。在影像上均匀撒点每个点都作为一个提示送入模型SAM 3 会为每个点返回最有可能包含该点的目标掩膜。网格密度越高小目标越不容易漏但推理耗时会线性增加。我这边的经验值中高分辨率影像0.3m-1m建筑物为主网格间距 32-48 像素效果比较稳。大范围低分辨率影像10m 以上地物以大块为主网格间距可以放到 80-120 像素。目标密集的城区建议 24-32 像素宁多勿漏。def build_grid_prompt(img_shape, step32): h, w img_shape[:2] points [] # 从 step//2 开始避免所有点都落在边缘 for y in range(step // 2, h, step): for x in range(step // 2, w, step): points.append([x, y]) labels [1] * len(points) # 1 表示前景点0 表示背景点 return points, labels这里有一个我自己踩过的坑网格点不能从 0 开始而是从 step//2 开始。如果从 0 开始每行每列的第一个点都贴在图块边缘SAM 3 对边缘点的处理不太稳定有时候会返回空的预测结果。用半偏移采样可以让点均匀分布在网格中心采样覆盖更合理。还有一种增强方式是把点都映射到梯度较大的位置也就是影像变化剧烈的区域。用 OpenCV 的 Canny 边缘检测或 Sobel 梯度幅度做引导选择在边缘附近的点作为提示。这样做的好处是提示集中在地物边界区域掩膜边界质量更高。不过这种采样方式对参数敏感实际操作时我建议先用均匀网格跑一版发现问题再考虑梯度引导。3.4 推理批量调用模型SAM 3 的推理接口接收图像、提示点和标签输出目标掩膜及置信度。以我实现的流程为例from sam3 import Sam3 model Sam3(weights/sam3_vit_h.pth) model.to(cuda) def predict_tile(model, tile, points, labels): masks, scores model.predict( imagetile, point_coordspoints, point_labelslabels, multimask_outputTrue ) return masks, scoresmultimask_output 这个参数值得展开说一下。开启后模型为每个提示点返回多个候选掩膜比如一个完整的目标、一个只包含核心区域的目标还有一个包含周边扩展区域的目标。三个候选里哪个是对的模型会给出置信度分数。SegEarth-OV3 的默认策略是选择置信度最高的那个掩膜如果后续做类别过滤可以把三个都保留让后续筛选自己选。处理大幅面时建议每跑完一类地物的提示后立即把掩膜存成临时文件不要把所有结果都堆在内存里。我处理一个 2 万乘 2 万的区域光掩膜文件就占了好几 GB不落地迟早把内存打满。3.5 掩膜清洗与拼接模型输出的原始掩膜是“像素级布尔数组”是单块范围的。在拼接前必须先做几步清洗去掉面积过小的连通域这些大多是噪声。面积阈值根据地物类型设定建筑物一般小于 20 个像素的可以直接干掉。去掉置信度过低的掩膜。SegEarth-OV3 里一般把置信度阈值设在 0.8 左右低于这个值的掩膜可靠性差。对同一目标产生多个掩膜的情况用 IoU 去重。两个掩膜的 IoU 大于 0.7就认为它们指向同一个目标保留置信度高的那个。清洗完的掩膜按切块时的坐标位置拼回整幅影像范围。重叠区域的掩膜通过取并集或者取置信度较高的结果这里我建议用置信度加权融合过渡更自然。def merge_masks_tile(masks, scores, x_offset, y_offset, full_size, min_iou0.7): # 先做连通域分析提取每个掩膜中的独立目标 # 再按坐标偏移写入全图掩膜数组 pass这一步的函数实现很简单我就不贴完整代码了说下几个容易出错的位置坐标偏移算错、边界同步不一致、Mask 的尺寸和原图没对上。每次拼接完后务必可视化叠加检查一遍。3.6 导出矢量结果并配准坐标最后一步是把掩膜转成矢量并写入地理坐标系这样 GIS 软件里才能直接用。import geopandas as gpd from shapely.geometry import shape from rasterio.features import shapes as rio_shapes import numpy as np def mask_to_vector(mask, transform, crs): results [] for geom, value in rio_shapes(mask.astype(np.uint8), maskmask, transformtransform): if value 1: results.append(shape(geom)) gdf gpd.GeoDataFrame({geometry: results}, crscrs) return gdf gdf mask_to_vector(binary_mask, src.transform, src.crs) gdf.to_file(output_water.shp)这里必须强调 transform 和 crs 一定要用 rasterio 打开原始影像时获取的那份不能自己推断。我见过不少人在这步直接给了默认坐标系导出的 Shapefile 在 ArcGIS 里叠到卫星影像上全部错位浪费了大半天排查。4. 提示词设计与精度调优技巧零标注分割看起来是“点一下就跑”实际要得到好的效果需要在提示和参数层面反复打磨。下面这几个点是影响遥感分割精度最直接的因素。4.1 提示点密度不是越多越好提示点密度和分割精度的关系并不完全是线性的。点太密一是耗时成倍增长二是相邻点会让模型产生大量重叠掩膜去重阶段处理复杂度飙升。我在农田地块分割场景测试过一组对比实验在同一个测试区域使用 16 像素、32 像素、64 像素三种网格密度。16 像素网格耗时最高但和多类别场景里的小地块分割相比效果不一定优于 32 像素64 像素网格虽然快但小块田之间的窄边界经常被跳过导致地块合并。最后我采取的是分块变密度策略用边缘检测生成梯度图梯度强的区域用 24 像素密度梯度弱的区域用 64 像素密度。效果和均匀 24 像素基本接近但推理时间缩短了约 40%。这套思路很实用你在自己的数据上也可以试一试。4.2 置信度阈值调整策略掩膜的置信度阈值直接影响输出结果的干净程度。阈值设高了漏掉一部分目标阈值设低了会混入大量背景碎片。SegEarth-OV3 里默认建议是 0.8但实际应用中要根据地物类型灵活调整建筑物轮廓清晰与背景差异大置信度通常很高阈值 0.85 以上没压力。水体边缘过渡缓慢模型给出的置信度偏低可以把阈值降到 0.75。林地与草地边界模糊阈值建议 0.7宁可多保留在后续矢量化阶段再人工筛选。这里我建议先跑一个小候选区域用不同阈值分别导出在 GIS 里目视比较一遍确定最佳值再全量跑避免浪费算力。4.3 提示框与文本提示的互补用法虽然 SegEarth-OV3 主打的是点提示自动采样但在实际使用中有些场景确实需要手工干预。比如一个区域内只想要某一类目标自动网格采样会分出所有目标这时候可以画一个框限定范围把框坐标交给模型只对框内做密集采样。又比如某些非常特殊的场景比如识别某个特定形状的目标可以用文本提示试试SAM 3 的文本理解能力比前代更强虽然不保证每个遥感类别都认识但值得一试。我用下来觉得最合理的策略是先用网格点提示跑全图得到所有候选掩膜再用一个简单的分类器或者人工抽样确认筛选类别最后对不确定的区域人工撒点补漏。有选择地干预效率最高。4.4 大幅面场景的内存调度大幅面影像处理时最怕的不是分割效果差而是跑到一半内存溢出进程被杀前功尽弃。建议按以下方式控制资源切块尺寸不要超过 1024×1024。每个批次处理 1-2 个块不要一次性把所有块载入。用 numpy 数组保存临时结果不要用 Python 列表存图片数据内存占用差距巨大。每处理完几个块主动调用 gc.collect() 释放内存。5. 常见问题与排查技巧实录这一节把我遇到的高频问题按“现象 - 原因 - 解法”整理成表格你如果正好撞上了直接对照着查。5.1 问题速查表现象可能原因解决办法模型加载报“unexpected key”权重文件下载不完整或版本不匹配重新下载权重并核对文件大小推理时显存溢出CUDA out of memory切块尺寸过大或批处理太多缩小切块到 512一次只处理一个块输出掩膜全是空的提示点全部落在无目标区域或采样步长过大检查采样点的分布情况缩小 step掩膜边缘锯齿严重后处理没有进行平滑在矢量化前用 scipy 的 gaussian_filter 或形态学闭运算做平滑拼接处出现裂缝切块重叠区域设置过小增大 overlap 到 256并对重叠区做置信度融合导出的 Shapefile 与影像错位crs 或 transform 没有取对确保使用 rasterio 读取原始影像时的参数大图跑几天没结果没有合理切块或内存泄漏检查脚本是否在循环中不断累积变量5.2 踩坑实录几个典型的排查故事第一个坑是有一次大范围水体提取跑出来的结果在水面中央出现大量孤立的小碎片。我一开始以为阈值设低了调高后碎片更多排查了很久才发现问题出在提示采样水体区域的光谱特征非常均匀梯度很低模型对均匀区域容易生成破碎的候选掩膜。解决办法是把置信度阈值提高同时在后处理里加了一个最小面积过滤把小于 100 像素的连通域全部去掉结果才干净。第二个坑是拼接边界错位。我处理一个 2 万像素宽的影像时顶部几行和底部几行的掩膜对不齐中间出现明显的水平错位线。排查后发现是 padding 补边时没有对齐坐标边缘块的实际内容偏移了导致拼接时位置错了一半。这个问题的教训是padding 的信息一定要跟着切块记录走不能只用坐标算回原图位置还得考虑 padding 带来的偏移。第三个坑是显存优化。最开始我一次处理 4 个 1024 的块结果经常跑到一半就 OOM。后来改成一次 1 个块并用 torch.no_grad() 包裹推理过程显存占用直接降了三分之二速度反而因为不用频繁清理内存变得更快了。5.3 效率优化经验处理超大幅面时有几个优化策略可以组合使用按地物类型分类处理。先提取所有候选掩膜再根据掩膜的平均光谱值做快速聚类同类目标一次性后处理避免反复读全图。用多进程并行处理不同切块。SAM 3 推理是典型的 CPU 预处理 GPU 推理模式多进程能有效利用多核 CPU 做数据读取和预处理。推荐进程数设为 CPU 核数一半左右太高反而会有调度开销。推理阶段关闭所有日志输出。一句话控制台打印 I/O 在高频推理循环里会显著拖慢速度。6. 个人经验与后续扩展建议这次实测最直观的体会是零标注遥感分割真正落地的时间节点已经到了。以前遇到一个新场景先想能不能找到公开数据集找不到就只能发动大家手工标注。现在用这套组合第一天拿到影像当天就能出第一版结果对项目预研和方案验证的价值非常大。当然也要说句公道话零标注不代表在所有场景下都能直接超过有监督模型的效果。在类别高度雷同、边界极不清晰的场景里有监督训练的算法仍然可能更优。但零标注流程可以作为一个快速预筛工具把大量精力从标注中解放出来集中处理模型解决不了的那部分难题。后续值得折腾的方向我列几个自己正在看的一是把分割掩膜接一个小分类器做成完整的“分割 分类”自动解译链路二是针对自己的地物类别积累一套提示采样参数模板覆盖不同分辨率和地物密度三是把分割结果接进变化检测流程用不同时期的影像对比判断地物变化情况。整个流程的核心就是提示设计建议你拿到自己的数据后先切几个代表性区域实验把提示密度、阈值、后处理顺序跑通再全量处理这个习惯能帮你避免很多浪费。希望这篇教程能帮你跳过我已经踩过的坑少一点头秃时刻。
返回列表