乐于分享
好东西不私藏

GeoAI语义分割

GeoAI语义分割

概述

语义分割是将遥感影像中的每个像素分配到特定语义类别的任务,如建筑物、道路、水体、植被等。在 GeoAI 中,与“文本提示式分割”关系最密切的是下面两类工具:

  1. 1. GroundedSAM - 结合 Grounding DINO 和 Segment Anything Model (SAM),先按文本提示检测目标,再由 SAM 生成实例级掩码
  2. 2. CLIPSegmentation - 使用 CLIP-Seg 模型,根据单个文本提示生成目标类别的概率图和二值掩码

需要注意:严格意义上的“多类别语义分割”通常依赖训练好的 U-Net、DeepLabV3+、SegFormer、timm/SMP 等模型;GroundedSAM 和 CLIPSegmentation 更像是开放词汇、文本提示驱动的分割工具,适合快速探索、辅助标注和特定类别提取。

主要特点

  • • 基于文本提示:通过自然语言描述指定要分割的目标
  • • 支持大影像:通过瓦片处理技术处理大幅遥感影像
  • • 地理空间输出:保留输入 GeoTIFF 的坐标参考信息,便于后续 GIS 分析
  • • 矢量化能力:GroundedSAM 可选导出边界框和多边形 GeoJSON
  • • 实例级表达:GroundedSAM 能在同一类别中保留多个目标实例,CLIPSegmentation 则主要输出单类别概率/二值掩码

语义分割流程

1. 整体流程

遥感语义分割模型结果对比

图:不同遥感语义分割模型在影像切片上的结果对比,可直观看到道路、建筑、水体、森林、农田等类别的像素级输出。该图适合理解“每个像素一个类别”的标准语义分割任务。

┌─────────────────────────────────────────────────┐│                语义分割流程                    │├─────────────────────────────────────────────────┤│ 1. 加载输入影像                              ││ 2. 切分影像为小瓦片                          ││ 3. 对每个瓦片执行分割                        ││    - 转换为RGB格式                           ││    - 检测目标(仅GroundedSAM)                ││    - 生成分割掩码                            ││    - 后处理(平滑、去噪等)                  ││ 4. 合并瓦片结果                              ││ 5. 生成输出文件                              ││    - 分割掩码(GeoTIFF)                     ││    - 边界框(GeoJSON,可选)                 ││    - 多边形(GeoJSON,可选)                 │└─────────────────────────────────────────────────┘

2. GroundedSAM 流程

GroundedSAM 文本提示分割流程

图:GroundedSAM 的核心思路是“文本提示 -> Grounding DINO 检测框 -> SAM 掩码”。它更接近开放词汇实例分割,最后可以把实例掩码合成为语义类别掩码。

┌─────────────────────────────────────────────────┐│             GroundedSAM 分割流程              │├─────────────────────────────────────────────────┤│ 1. 初始化模型                                 ││    - 加载 Grounding DINO 检测器              ││    - 加载 SAM 分割模型                       ││ 2. 处理输入影像                              ││    - 切分影像为瓦片                          ││    - 转换为RGB格式                           ││ 3. 目标检测                                  ││    - 使用 Grounding DINO 检测目标            ││    - 应用非极大值抑制(NMS)                 ││ 4. 实例分割                                  ││    - 使用 SAM 生成精确掩码                   ││    - 细化掩码(可选)                        ││ 5. 后处理                                    ││    - 高斯平滑减少块状效应                    ││    - 合并重叠掩码                            ││ 6. 输出结果                                  ││    - 保存分割掩码                            ││    - 导出边界框和多边形(可选)              │└─────────────────────────────────────────────────┘

3. CLIPSegmentation 流程

┌─────────────────────────────────────────────────┐│            CLIPSegmentation 分割流程           │├─────────────────────────────────────────────────┤│ 1. 初始化模型                                 ││    - 加载 CLIP-Seg 模型                      ││ 2. 处理输入影像                              ││    - 切分影像为瓦片                          ││    - 转换为RGB格式                           ││ 3. 文本提示分割                              ││    - 输入文本提示和影像                      ││    - 生成概率图                              ││ 4. 后处理                                    ││    - 高斯平滑                                ││    - 应用阈值生成二值掩码                    ││ 5. 输出结果                                  ││    - 保存二值分割掩码                        ││    - 保存概率分数                            │└─────────────────────────────────────────────────┘

核心算法

GroundedSAM 算法

GroundedSAM 结合了两个强大的模型:

  • • Grounding DINO:一个开放词汇目标检测器,能够根据文本提示检测图像中的目标
  • • Segment Anything Model (SAM):一个通用的分割模型,能够生成精确的分割掩码

工作原理

  1. 1. 使用 Grounding DINO 根据文本提示检测目标,生成边界框
  2. 2. 将检测到的边界框作为输入传递给 SAM
  3. 3. SAM 根据边界框生成精确的分割掩码
  4. 4. 对掩码进行后处理,如高斯平滑、多边形细化等
  5. 5. 合并瓦片结果,生成综合掩码、按提示分离的掩码,以及可选的边界框/多边形矢量数据

因此,GroundedSAM 的输出可以服务于语义分割任务,但它本身的技术路线是“检测 + 实例分割”,不是传统的端到端多类别语义分割网络。

CLIPSegmentation 算法

CLIPSegmentation 使用 CLIP-Seg 模型。CLIP-Seg 在冻结的 CLIP 视觉/文本骨干上增加轻量解码器,可根据文本或图像提示生成二值分割结果。在 GeoAI 的封装中,它接收一个文本提示,输出两波段 GeoTIFF:第一波段是二值掩码,第二波段是概率分数。

工作原理

  1. 1. 将输入影像切分为瓦片,并转换为 RGB 图像
  2. 2. 将文本提示和瓦片影像输入 CLIP-Seg 处理器和模型
  3. 3. 通过模型 logits 经 sigmoid 得到像素级概率图
  4. 4. 根据 threshold 生成二值分割掩码
  5. 5. 对概率图进行平滑处理,减少瓦片边界和块状效应

核心代码分析

GroundedSAM 类

初始化方法

def__init__(    self,    detector_id: str = "IDEA-Research/grounding-dino-tiny",    segmenter_id: str = "facebook/sam-vit-base",    device: Optional[str] = None,    tile_size: int = 1024,    overlap: int = 128,    threshold: float = 0.3,) -> None:# 设置模型和参数self.detector_id = detector_idself.segmenter_id = segmenter_idself.tile_size = tile_sizeself.overlap = overlapself.threshold = threshold# 设置设备if device isNone:self.device = "cuda"if torch.cuda.is_available() else"cpu"else:self.device = device# 加载模型self._load_models()

代码说明

  • • 初始化 GroundedSAM 类,设置模型参数和设备
  • • 自动检测可用设备,优先使用 CUDA
  • • 加载 Grounding DINO 和 SAM 模型

核心分割方法

defsegment_image(    self,    input_path: str,    output_path: str,    text_prompts: Union[strList[str]],    polygon_refinement: bool = False,    export_boxes: bool = False,    export_polygons: bool = True,    smoothing_sigma: float = 1.0,    nms_threshold: float = 0.5,    min_polygon_area: int = 50,    simplify_tolerance: float = 2.0,) -> Dict[strstr]:# 处理文本提示ifisinstance(text_prompts, str):        text_prompts = [text_prompts]# 打开输入影像with rasterio.open(input_path) as src:# 获取元数据        meta = src.meta        height = src.height        width = src.width        transform = src.transform        crs = src.crs# 创建输出元数据        out_meta = meta.copy()        out_meta.update(            {"count"len(text_prompts) + 1"dtype""uint8""nodata"0}        )# 创建结果数组        all_masks = np.zeros((len(text_prompts), height, width), dtype=np.uint8)        all_boxes = []        all_polygons = []# 计算有效瓦片大小        effective_tile_size = self.tile_size - 2 * self.overlap# 计算瓦片数量        n_tiles_x = max(1int(np.ceil(width / effective_tile_size)))        n_tiles_y = max(1int(np.ceil(height / effective_tile_size)))# 处理瓦片for y inrange(n_tiles_y):for x inrange(n_tiles_x):# 计算瓦片坐标                x_start = max(0, x * effective_tile_size - self.overlap)                y_start = max(0, y * effective_tile_size - self.overlap)                x_end = min(width, (x + 1) * effective_tile_size + self.overlap)                y_end = min(height, (y + 1) * effective_tile_size + self.overlap)# 读取瓦片                window = Window(x_start, y_start, x_end - x_start, y_end - y_start)                tile_data = src.read(window=window)# 处理瓦片# 1. 转换为RGB格式# 2. 检测目标# 3. 分割目标# 4. 处理结果# 生成合并掩码        combined_mask = np.any(all_masks, axis=0).astype(np.uint8)# 保存输出with rasterio.open(output_path, "w", **out_meta) as dst:            dst.write(combined_mask, 1)for i, mask inenumerate(all_masks):                dst.write(mask, i + 2)# 导出边界框和多边形# ...return result_files

代码说明

  • • 处理输入影像和文本提示
  • • 计算瓦片数量和位置
  • • 遍历处理每个瓦片
  • • 对每个瓦片执行目标检测和分割
  • • 合并瓦片结果,生成最终分割掩码
  • • 导出分割结果和可选的边界框、多边形

目标检测方法

def_detect(self, image: Image.Image, labels: List[str]) -> List[DetectionResult]:# 确保标签以句号结尾    labels = [label if label.endswith("."else label + "."for label in labels]    results = self.object_detector(        image, candidate_labels=labels, threshold=self.threshold    )    results = [DetectionResult.from_dict(result) for result in results]return results

代码说明

  • • 使用 Grounding DINO 检测目标
  • • 确保标签格式正确(以句号结尾)
  • • 将检测结果转换为 DetectionResult 对象

实例分割方法

def_segment(    self,    image: Image.Image,    detection_results: List[DetectionResult],    polygon_refinement: bool = False,) -> List[DetectionResult]:ifnot detection_results:return detection_results    boxes = self._get_boxes(detection_results)    inputs = self.processor(        images=image, input_boxes=boxes, return_tensors="pt"    ).to(self.device)    outputs = self.segmentator(**inputs)    masks = self.processor.post_process_masks(        masks=outputs.pred_masks,        original_sizes=inputs.original_sizes,        reshaped_input_sizes=inputs.reshaped_input_sizes,    )[0]    masks = self._refine_masks(masks, polygon_refinement)for detection_result, mask inzip(detection_results, masks):        detection_result.mask = maskreturn detection_results

代码说明

  • • 从检测结果中提取边界框
  • • 使用 SAM 生成分割掩码
  • • 后处理掩码,如多边形细化
  • • 将掩码添加到检测结果中

CLIPSegmentation 类

初始化方法

def__init__(    self,    model_name: str = "CIDAS/clipseg-rd64-refined",    device: Optional[str] = None,    tile_size: int = 512,    overlap: int = 32,) -> None:self.tile_size = tile_sizeself.overlap = overlap# 自动选择设备from .utils.device import get_deviceself.device = str(device or get_device())# 加载模型和处理器self.processor = CLIPSegProcessor.from_pretrained(model_name)self.model = CLIPSegForImageSegmentation.from_pretrained(model_name).to(self.device    )

代码说明

  • • 初始化 CLIPSegmentation 类,设置模型参数和设备
  • • 自动检测可用设备
  • • 加载 CLIP-Seg 模型和处理器

核心分割方法

defsegment_image(    self,    input_path: str,    output_path: str,    text_prompt: str,    threshold: float = 0.5,    smoothing_sigma: float = 1.0,) -> str:# 打开输入影像with rasterio.open(input_path) as src:# 获取元数据        meta = src.meta        height = src.height        width = src.width# 创建输出元数据        out_meta = meta.copy()        out_meta.update({"count"2"dtype""float32""nodata"None})# 创建结果数组        segmentation = np.zeros((height, width), dtype=np.float32)        probabilities = np.zeros((height, width), dtype=np.float32)# 计算有效瓦片大小        effective_tile_size = self.tile_size - 2 * self.overlap# 计算瓦片数量        n_tiles_x = max(1int(np.ceil(width / effective_tile_size)))        n_tiles_y = max(1int(np.ceil(height / effective_tile_size)))# 处理瓦片for y inrange(n_tiles_y):for x inrange(n_tiles_x):# 计算瓦片坐标                x_start = max(0, x * effective_tile_size - self.overlap)                y_start = max(0, y * effective_tile_size - self.overlap)                x_end = min(width, (x + 1) * effective_tile_size + self.overlap)                y_end = min(height, (y + 1) * effective_tile_size + self.overlap)# 读取瓦片                window = Window(x_start, y_start, x_end - x_start, y_end - y_start)                tile_data = src.read(window=window)# 处理瓦片# 1. 转换为RGB格式# 2. 处理CLIP-Seg# 3. 生成概率图# 4. 后处理# 生成二值分割掩码        segmentation = (probabilities >= threshold).astype(np.float32)# 保存输出with rasterio.open(output_path, "w", **out_meta) as dst:            dst.write(segmentation, 1)            dst.write(probabilities, 2)return output_path

代码说明

  • • 处理输入影像和文本提示
  • • 计算瓦片数量和位置
  • • 遍历处理每个瓦片
  • • 对每个瓦片执行 CLIP-Seg 分割
  • • 生成概率图和二值分割掩码
  • • 保存输出结果

使用示例

示例 1:使用 GroundedSAM 提取建筑物

import geoai# 初始化 GroundedSAM 模型grounded_sam = geoai.GroundedSAM(    detector_id="IDEA-Research/grounding-dino-tiny",    segmenter_id="facebook/sam-vit-base",    device="cuda")# 提取建筑物result = grounded_sam.segment_image(    input_path="satellite_image.tif",    output_path="buildings_segmented.tif",    text_prompts="building",    export_polygons=True,    min_polygon_area=100,    simplify_tolerance=2.0)print("建筑物提取完成!")print(f"分割结果保存到: {result['segmentation']}")print(f"多边形结果保存到: {result['polygons']}")

示例 2:使用 GroundedSAM 提取多种地物

import geoai# 初始化 GroundedSAM 模型grounded_sam = geoai.GroundedSAM(    detector_id="IDEA-Research/grounding-dino-tiny",    segmenter_id="facebook/sam-vit-base",    device="cuda")# 提取建筑物、道路和水体result = grounded_sam.segment_image(    input_path="aerial_image.tif",    output_path="multiple_features.tif",    text_prompts=["building""road""water"],    export_polygons=True,    min_polygon_area=50,    simplify_tolerance=1.0)print("多特征提取完成!")print(f"分割结果保存到: {result['segmentation']}")print(f"多边形结果保存到: {result['polygons']}")

示例 3:使用 CLIPSegmentation 提取水体

import geoai# 初始化 CLIPSegmentation 模型clip_seg = geoai.CLIPSegmentation(    model_name="CIDAS/clipseg-rd64-refined",    device="cuda")# 提取水体output_path = clip_seg.segment_image(    input_path="satellite_image.tif",    output_path="water_segmented.tif",    text_prompt="water body",    threshold=0.5,    smoothing_sigma=1.0)print(f"水体提取完成!结果保存到: {output_path}")

示例 4:批量处理多个影像

import geoaiimport os# 初始化 CLIPSegmentation 模型clip_seg = geoai.CLIPSegmentation(    model_name="CIDAS/clipseg-rd64-refined",    device="cuda")# 批量处理多个影像input_folder = "input_images"output_folder = "output_segmented"text_prompt = "building"# 创建输出目录os.makedirs(output_folder, exist_ok=True)# 获取所有影像文件image_files = [f for f in os.listdir(input_folder) if f.endswith('.tif')]# 批量处理output_paths = clip_seg.segment_image_batch(    input_paths=[os.path.join(input_folder, f) for f in image_files],    output_dir=output_folder,    text_prompt=text_prompt,    threshold=0.5,    smoothing_sigma=1.0)print(f"批量处理完成!处理了 {len(output_paths)} 个影像")

业务应用场景

1. 城市规划

应用:提取建筑物、道路、绿地等城市要素,生成城市现状图

优势

  • • 快速获取城市建成区范围
  • • 监测城市扩张
  • • 辅助城市规划决策

示例

# 提取城市建筑物和道路grounded_sam.segment_image(    input_path="city_image.tif",    output_path="city_features.tif",    text_prompts=["building""road""park"],    export_polygons=True)

2. 环境监测

应用:提取水体、植被等环境要素,监测环境变化

优势

  • • 快速识别水体边界
  • • 监测植被覆盖变化
  • • 评估环境质量

示例

# 提取水体和植被clip_seg.segment_image(    input_path="environment_image.tif",    output_path="water_vegetation.tif",    text_prompt="water and vegetation",    threshold=0.5)

3. 农业管理

应用:提取农田、作物等农业要素,监测农业生产

优势

  • • 识别农田边界
  • • 监测作物生长状况
  • • 辅助精准农业决策

示例

# 提取农田clip_seg.segment_image(    input_path="agriculture_image.tif",    output_path="farmland.tif",    text_prompt="farmland",    threshold=0.5)

4. 灾害响应

应用:提取建筑物、道路等要素,评估灾害损失

优势

  • • 快速评估建筑物受损情况
  • • 识别道路阻塞
  • • 辅助救援决策

示例

# 提取建筑物和道路grounded_sam.segment_image(    input_path="disaster_image.tif",    output_path="disaster_assessment.tif",    text_prompts=["building""road"],    export_polygons=True)

性能优化

1. 内存优化

  • • 瓦片大小调整:根据 GPU 内存调整 tile_size 参数
  • • 逐景处理:对多个影像逐一处理,避免一次性加载过多数据
  • • 模型选择:选择轻量级模型,如 "grounding-dino-tiny" 和 "sam-vit-base"

2. 速度优化

  • • GPU 加速:使用 GPU 加速模型推理
  • • 合理放大瓦片:在显存允许的前提下适当增大 tile_size,减少瓦片数量
  • • 模型缓存:缓存模型,避免重复加载
  • • 瓦片重叠:合理设置 overlap 参数;overlap 越大,边缘更稳定,但有效步长会变小、瓦片数量会增加

3. 质量优化

  • • 阈值调整:根据具体任务调整 threshold 参数
  • • 平滑处理:使用 smoothing_sigma 参数减少块状效应
  • • 多边形简化:使用 simplify_tolerance 参数控制多边形复杂度
  • • 面积过滤:使用 min_polygon_area 参数过滤小面积多边形

常见问题解决

1. 内存不足

解决方案

  • • 减小 tile_size 参数
  • • 选择更小的模型
  • • 使用 CPU 模式(速度会变慢)

2. 分割结果不准确

解决方案

  • • 调整 threshold 参数
  • • 使用更具体的文本提示
  • • 增加 min_polygon_area 参数,过滤小面积噪声
  • • GroundedSAM 场景下可调高检测阈值或 NMS 阈值,减少误检和重复框
  • • CLIPSegmentation 场景下可对同一区域尝试多个等价英文提示,如 "water body"、"river"、"lake"

3. 处理速度慢

解决方案

  • • 使用 GPU 加速
  • • 在显存允许的前提下增大 tile_size
  • • 适当减小 overlap 参数,减少重复推理区域
  • • 选择轻量级模型

4. 边缘效应

解决方案

  • • 增加 overlap 参数
  • • 使用平滑处理减少边缘差异

5. 多边形质量差

解决方案

  • • 增加 simplify_tolerance 参数
  • • 启用 polygon_refinement 参数
  • • 增加 min_polygon_area 参数

GeoAI 的 GroundedSAM 和 CLIPSegmentation 能够基于文本提示从遥感影像中提取建筑物、道路、水体等地理要素。更准确地说,GroundedSAM 是“开放词汇检测 + SAM 实例分割”的组合,CLIPSegmentation 是“单文本提示 -> 概率图/二值掩码”的分割工具;二者都可以用于语义分割工作流,但不等同于经过训练的多类别语义分割模型。

这些方法具有以下优势:

  1. 1. 基于文本提示:通过自然语言描述指定要分割的目标,无需手动标注
  2. 2. 支持大影像:通过瓦片处理技术处理大幅遥感影像
  3. 3. 地理空间输出:输出 GeoTIFF 掩码,并保留空间参考信息
  4. 4. GIS 工作流友好:结果可继续用于叠加分析、矢量化、面积统计和人工校核
  5. 5. 实例/矢量导出:GroundedSAM 可导出边界框和多边形,便于后续 GIS 编辑

通过合理配置参数和选择适当的模型,可以高效地从遥感影像中提取各种地理要素,为城市规划、环境监测、农业管理和灾害响应等应用提供有力支持。