第16章 - 遥感影像与影像分析

遥感影像在 GeoLibre 里既是”可以看的图”,也是”可以算的数”。这两件事的距离,取决于平台是否把像素值、波段顺序、反射率缩放这些元信息保留到了分析环节。GeoLibre 的做法是把它们全部暴露出来:Style 面板让你自由组合波段与拉伸,Spectral index 与 Raster calculator 让你在像素上做代数,AI Segmentation 与 Object-Based Analysis 把像素聚成对象再分类。

本章基于 GeoLibre 3.4,覆盖影像入口、波段可视化、光谱指数、变化检测、AI 分割、OBIA 全流程与 eCognition 迁移。工具名、参数名、环境变量与字段名全部保留英文原文,来源是 docs/user-guide/segmentation.md、docs/user-guide/obia.md、docs/user-guide/obia-ecognition.md、packages/processing/src/spectral-indices.ts、raster-tools.ts 与 obia.ts。

16.1 影像数据入口

16.1.1 七条入口

入口 路径 提供什么 前置条件
本地影像 Add Data → Raster Layer GeoTIFF / COG 文件 无
远程 COG Add Data → Raster Layer 填 URL HTTP Range 按需读块 服务端支持 Range + CORS
STAC Layer Add Data → STAC Layer 任意 STAC 目录的匹配条目 目录可达
STAC Catalogs Plugins → Web Services → STAC Catalogs STAC Index 全量目录浏览 同上
Sentinel-2 Explorer Plugins → Web Services → Sentinel-2 Explorer 2015 年至今每一景 Sentinel-2 L2A 无 API、无服务端
NASA Earthdata Plugins → Web Services → NASA Earthdata GIBS 预渲染全球影像图层(1100 多个) 无
NASA Earthaccess Plugins → Web Services → NASA Earthaccess CMR 目录检索 + 下载 granule Earthdata Login token(下载时)
Planetary Computer Processing → Planetary Computer Sentinel / Landsat / NAIP / DEM 等 STAC 数据 无(服务端签名)
Earth Engine Processing → Earth Engine GEE 数据集 认证

再加上两个专业影像入口:Vantor Open Data(灾害事件卫星影像,可按事件前后筛选、可视化 COG、下载)与 Planet Open Data(Planet 灾害数据发布,走 STAC 浏览器)。

16.1.2 Sentinel-2 Explorer:无服务端的数据检索

这个面板值得单独讲,因为它的实现方式很特别:背后没有 API,也没有服务器。

它读的是 Source Cooperative 上的 s2-stac-geoparquet 目录——一份把 Earth Search 索引的每一景都转成 STAC-GeoParquet 的静态数据集。每次检索都是对静态文件的一组 HTTP Range 读,每张影像都从它在 AWS 上的 COG 流式读取。

操作顺序:

  1. 选 collection:Collection 1(ESA 统一重处理,2015 年 10 月起,默认)或原始 L2A 索引(2016 年 11 月起)。
  2. 设 From / To 日期。地图会把每个 MGRS 瓦片按该窗口的统计上色,可选四种指标:clearest scene 的云量、scene count(景数)、median cloud(中位云量)、coverage(单景最大填充比例)。绿色好,深红差。
  3. 用 Max cloud %、Min coverage %、Min scenes per tile 过滤掉不合格的瓦片(变灰)。前两个同时过滤景列表。Show the tile grid 可以只隐藏栅格而不关面板。
  4. 点一个瓦片检索它的景。面板只读该窗口 GeoParquet 分片里可能包含这个瓦片的行组,通常几百 KB,并会报告读了多少次 Range 请求。
  5. 景列表可按云量最少、覆盖最多、最新排序;悬停会在图上描出足迹。
  6. Add scenes as 决定 Add to map 流什么:
选项 内容
true color(TCI) 真彩色
单个波段 B01–B12、B8A、AOT、WVP、SCL 场景分类;Collection 1 还有云与雪概率掩膜
composite:false color infrared B08 / B04 / B03
composite:agriculture B11 / B08 / B02
composite:short-wave infrared B12 / B8A / B04
index:NDVI B08 / B04,发散色带
index:NDWI B03 / B08,发散色带

合成与指数都是在浏览器里拼的:每个地图瓦片直接从各波段的 COG 上读对应 overview 的窗口,从 UTM 翘曲过来再绘制——所以 10 m 与 20 m 波段能在全分辨率下合成。反射率已按 04.00 及之后处理基线的 1000 偏移做了校正。

注意:composites 需要 MapLibre 渲染器。主图切到 Cesium 或 ArcGIS 时这几种显示方式不可用。

Download 列出该景的所有文件(TCI、每个波段、SCL、Collection 1 的掩膜),点击会在浏览器(桌面端用系统浏览器)打开 COG 并保存。

16.1.3 三个 “NASA” 不是一回事

这是最容易混的一处:

面板 数据形态 用途
NASA Earthdata GIBS 的预渲染瓦片(1100 多个全球图层,按日期) 看,不能算。瓦片是 8 位彩色图,没有原始波段
Earthdata GIS EOSDIS 发布的 ArcGIS image / map / feature 服务 看与分析服务端能力
NASA Earthaccess CMR 目录检索 + 原始 granule 下载 拿到可算的原始数据

关键区别:只有第三个能给你可计算的像素值。要算 NDVI,必须走 Earthaccess 下载,或者走 Sentinel-2 Explorer / STAC 拿 COG。

Earthaccess 的用法:

  • 选热门数据集(ICESat-2 ATL03 / ATL06 / ATL08、GEDI L2A / L2B / L4A、HLS、NASADEM)或按关键词搜索。热门项自动用 Earthdata Cloud 中的最新版本。
  • Download 保存 granule 文件(多文件 granule 如 HLS 场景用 Download all,桌面端会问一次目录),Zoom 缩放到它,Details 在 Earthdata Search 里打开。
  • 检索不需要账号。下载需要 Earthdata Login token:从 profile 的 Generate Token 页粘贴,或桌面端用用户名密码登录(只保留 token)。token 在桌面端存系统钥匙串,Web 端存浏览器存储。部分数据集还要求先在 Earthdata Login 站点接受一次 EULA。

16.1.4 STAC 的过滤能力

STAC Catalogs 是通用浏览器,任何 STAC API 或静态目录都能连:

  • 从 STAC Index 选目录,或粘贴目录 / API URL。静态目录按树浏览,API 走检索。
  • 按 collection(多选)、日期范围、区域过滤。区域可以是当前地图范围、手填的 bbox、或在地图上画框。
  • STAC API 还接受额外的 JSON 检索参数——这是绕开界面限制、直接用 STAC 原生语法的后门。

提示:三个目录浏览器(STAC Catalogs、Planet Open Data、Portolan)共享面板状态,激活一个会停用另一个。被顶掉的那个如果切换失败会自动回来。

16.2 波段与可视化

16.2.1 波段编号是 1 起

GeoLibre 的所有波段引用都是 1-based,与 GDAL / rasterio 一致,也与栅格计算器里的 A1、A2 对齐。这看起来是小事,但遥感数据描述里常见 0-based 的索引,抄错一位会让整张图变成”看起来像那么回事但颜色完全不对”。

16.2.2 Style 面板里的栅格显示模式

栅格图层的 Style 面板支持三种 mode:

mode 用途 关键字段
single 单波段 + 色带 1-based bands、colormap、reversed
rgb 三波段合成 三个 bands、各自或统一的 rescale
index 归一化差值预设 指数预设 + 色带

公共字段:

字段 含义
rescale 值域映射区间,形如 [min, max];按波段分别给或统一给
nodata 视为无效的像素值
opacity 图层不透明度
gamma 伽马校正,提升暗部或压暗高光
stretch 拉伸方式:linear / log / sqrt
colormap / reversed 色带名与是否反向

16.2.3 拉伸:三个选项的适用场景

stretch 公式直觉 适合
linear 线性映射 值域均匀分布的数据(反射率、温度)
log 压缩高值、展开低值 长尾分布(人口、夜间灯光、流量)
sqrt 折中 影像对比度不足、想同时看清明暗两端

拉伸与 rescale 是两件事:rescale 决定”哪一段值域参与映射”(相当于设窗宽窗位),stretch 决定”参与映射后按什么曲线分配”。常见错误是只调 stretch 不调 rescale——曲线换了但值域还是 0–65535,画面上几乎看不出变化。

提示:Sentinel-2 L2A 的原始 DN 值域大致 0–10000(反射率乘 10000)。直接显示会几乎全黑。正确起手是把 rescale 设成 [0, 3000] 左右,再用 gamma 微调。

16.2.4 三种常见的假彩色

目的 波段组合(Sentinel-2) 为什么
植被增强(假彩色红外) B08 / B04 / B03 近红外给红通道,健康植被变鲜红
农业 B11 / B08 / B02 短波红外对作物含水量敏感
短波红外 B12 / B8A / B04 地质与烧痕识别

这些组合在 Sentinel-2 Explorer 里是预设,在别的数据上要手工在 Style 面板里按 1-based 波段号填。

16.3 光谱指数

16.3.1 八个内置指数

光谱指数不是独立的栅格算法——它是既有栅格计算器的预设前端。spectral-indices.ts 的注释写得很清楚:它把一个指数加一份波段布局编译成同一条单输入波段运算表达式,交给浏览器里的 rasterCalc 或 sidecar 的 raster-calc 脚本求值。

指数 id 面板名称 用到的波段 公式 值域 默认色带
ndvi NDVI (vegetation) red, nir (nir - red) / (nir + red) −1..1 RdYlGn
gndvi GNDVI (green vegetation) green, nir (nir - green) / (nir + green) −1..1 RdYlGn
ndwi NDWI (water, McFeeters) green, nir (green - nir) / (green + nir) −1..1 Blues
ndmi NDMI (moisture) nir, swir1 (nir - swir1) / (nir + swir1) −1..1 BrBG
ndbi NDBI (built-up) nir, swir1 (swir1 - nir) / (swir1 + nir) −1..1 RdBu
nbr NBR (burn ratio) nir, swir2 (nir - swir2) / (nir + swir2) −1..1 RdYlGn
evi EVI (enhanced vegetation) blue, red, nir 2.5 * ((nir - red) / (nir + 6*red - 7.5*blue + 1)) −1..1 RdYlGn
savi SAVI (soil-adjusted vegetation) red, nir ((nir - red) / (nir + red + L)) * (1 + L) −1..1 RdYlGn

前六个都是”归一化差值”这个统一形状 (a - b) / (a + b),只是分子分母的波段不同。NDBI 的方向与直觉相反:它是 swir1 - nir,所以建成区为正值、植被为负值。

16.3.2 四个传感器预设

波段号是可以手工指定的,但预设覆盖了最常见的情况:

传感器 id 面板名 波段布局(1-based)
sentinel2 Sentinel-2 (B2, B3, B4, B8, B11, B12) blue 1、green 2、red 3、nir 4、swir1 5、swir2 6
landsat89 Landsat 8/9 (B1–B7) blue 2、green 3、red 4、nir 5、swir1 6、swir2 7
naip NAIP (R, G, B, NIR) red 1、green 2、blue 3、nir 4(无 SWIR)
custom Custom 六个波段号全部手工填

注意 Landsat 的 blue 是 2 而不是 1——因为 B1 是海岸气溶胶波段。这是个高频错误:直接按 1 起顺排会让所有指数偏掉。

16.3.3 参数与四条拒绝规则

参数 id 面板标签 默认 说明
index Index ndvi 八个指数之一
sensor Sensor / band layout sentinel2 四个预设之一
red / green / blue / nir / swir1 / swir2 对应波段号 — 仅 sensor 为 custom 时可见
L Soil factor (L) 0.5 仅 index 为 savi 时可见,0–1
scale Reflectance scale 1 乘到每个波段 ref 上的系数,必须大于 0

四条校验(源码里的抛出条件):

情形 报错
未知指数 id Unknown spectral index: "..."
预设传感器缺这个指数需要的波段 The ... preset does not include the "..." band this index needs.
波段号不是 ≥ 1 的整数 Band "..." must be a whole number ≥ 1 (got ...)
scale ≤ 0 Reflectance scale must be greater than 0.

“预设缺波段”这条在 NAIP 上必然触发:NAIP 只有 R/G/B/NIR,算 NDMI / NDBI / NBR 需要 SWIR——直接用 NAIP 预设会报错。解法是切到 Custom 手工填,或者干脆承认这些指数在 NAIP 上算不出来。

16.3.4 scale 什么时候重要

源码注释把取舍讲透了:

// When scale=1, s*A = A so we emit the bare reference. When scale != 1 we
// wrap every band uniformly — for ratio indices the scale cancels anyway;
// for EVI/SAVI the additive constants require true reflectance values.
return scale === 1 ? `A${band}` : `(${scale} * A${band})`;

翻译成实践规则:

指数类型 是否需要设 scale 理由
归一化差值(NDVI / NDWI / NDMI / NDBI / NBR / GNDVI) 不需要 分子分母同乘一个系数,系数约掉了
EVI 必须 公式里的 +1 是反射率意义上的 1,DN 值下完全不成立
SAVI 必须 L 是反射率量纲的土壤调节因子

Sentinel-2 L2A 的 scale 填 0.0001;Landsat Collection 2 的反射率系数是 0.0000275(加偏移量,需另行处理)。

警告:EVI 不设 scale 是最常见的”算出来全是怪值”的原因。DN 值在 0–10000 之间时,分母 nir + 6*red - 7.5*blue + 1 与分子量级差得离谱,结果会落在几百到几千,而 EVI 的物理范围是 −1 到 1。

16.3.5 Raster calculator

当八个内置指数不够用时,用 Processing → GeoLibre Toolbox → Raster → Raster calculator。它有客户端引擎,浏览器里也能跑。

参数 id 面板标签 必填 说明
expression Expression 是 波段代数表达式
b_path Raster B (optional) 否 第二张栅格,引用为 B / B1, B2, …;必须与 A 尺寸一致
c_path Raster C (optional) 否 第三张,引用为 C / C1, C2, …;同样要求尺寸一致

表达式语法要点:

  • 第一张栅格的波段 1 引用为 A,第 n 个波段引用为 An。
  • 可用函数:where、log、exp、sqrt、abs、clip、minimum、maximum、sin、cos、tan。
  • 面板描述给的例子就是 NDVI:(A2 - A1) / (A2 + A1)。

三件事必须自己保证,工具不会替你检查:

  1. 尺寸一致——B、C 与 A 必须同尺寸,否则直接失败。
  2. 除零——归一化差值在 a + b = 0 处会炸。稳妥写法是用 where 兜底:where(A1 + A2 == 0, 0, (A2 - A1) / (A2 + A1))。
  3. NoData 传播——表达式里没有对 NoData 的特殊处理,它会参与运算并污染邻域。

16.3.6 指数选择速查

想回答 用 阈值起点
这里有没有植被 NDVI 大于 0.2 视为有植被;大于 0.5 为密植被
植被长得好不好 NDVI / EVI EVI 在高生物量区不易饱和,优于 NDVI
有没有水 NDWI(McFeeters) 大于 0 通常为水体;大于 0.3 更稳
含水量 / 干旱 NDMI 负值为缺水
建成区 NDBI 大于 0 倾向建成
烧痕 NBR 火灾前后差值(dNBR)是关键量
裸土上的植被 SAVI L 取 0.5 为通用值

注意:阈值不是常数。同一景里阴影水体的 NDWI 可能是负的;大气校正与否会让 NDVI 整体平移 0.05–0.1。任何阈值都应该在当前这景上用样本点验证。

16.4 变化检测与时间序列

16.4.1 双期差分的四步法

变化检测最稳的做法不是”看两张图找不同”,而是把一个指数相减再定阈值:

  1. 取两期同源影像(同传感器、同处理级别、同季节)。季节不同带来的物候差异会淹没真实变化。
  2. 各自算指数。用 Spectral index 各跑一次 NDVI。
  3. 做差。用 Raster calculator,两张图分别放在 A 与 B:

    B - A
    

    或者带除零保护的归一化差:

    where(B + A == 0, 0, (B - A) / (B + A))
    
  4. 定阈值并分类。用 Reclassify 把差值映射成类别。table 参数的语法是”最小值:最大值:新值”的规则串,范围左闭右开:

    min:-0.1:1, -0.1:0.1:0, 0.1:max:2
    

    含义:显著减少 = 1、基本不变 = 0、显著增加 = 2。unmatched 参数决定没落进任何区间的值怎么办(nodata 设为 NoData,或 original 保留原值)。

提示:0.1 这个阈值只是起点。NDVI 差值的噪声水平与传感器、大气校正、配准精度都有关。先在同一时期的两景上做一次”伪差分”,量出噪声的量级,再把它当阈值下限。

16.4.2 变化检测的三个前提

前提 检查方法 不满足的后果
几何配准 两期影像叠合,看道路/建筑边缘是否对齐 未配准会在所有边缘产生”变化”,形成描边状假变化
辐射一致 两期同一稳定地物(深水体、裸岩)的像素值是否接近 整体偏移会让阈值法把所有像元判成同一方向变化
物候一致 两期是否同一季节/同一月 农田的 NDVI 随生育期剧烈变化,跨季比较毫无意义

第三条在农业区是致命的:同一块玉米地在 6 月与 9 月的 NDVI 差 0.5 以上,而这块地一年内什么都没变。

16.4.3 时间序列工具

工具 在哪 做什么
Time Slider Plugins → Time Slider 通过停靠的时间轴动画化时间序列(COG、XYZ/WMTS、WMS-Time、按时间过滤的 GeoJSON)
Layer Swipe Plugins → Layer Swipe 用滑动柄并排比较两个图层
Dynamical Plugins → Web Services → Dynamical 读 dynamical.org 的 Icechunk/Zarr 档案,按预报批次或时间切片,还能”Time series at a point”出曲线

Time Slider 对 COG 的要求是文件名或元数据里带可解析的时间;否则时间轴排不出顺序。

注意:Zarr 图层(含 Dynamical 的)不会随项目恢复——重开项目需要从面板重新加。这是渲染引擎层面的限制,不是面板的疏忽。

16.4.4 时序统计

想量化”某区域 NDVI 的年际趋势”,GeoLibre 没有现成的趋势工具,但有两条可行路径:

路径 A:SQL Workspace。 如果多期指数已经导出成表(每期一列或一行),用 SQL 算线性趋势很简单:

SELECT cell_id,
       regr_slope(ndvi, year) AS trend_per_year
FROM ndvi_series
GROUP BY cell_id;

路径 B:Python 控制台。 拿多期栅格做逐像元回归,numpy 一行就够:

import numpy as np
# stack 形状为 (期数, 行, 列)
stack = np.stack(series)
years = np.arange(len(series), dtype=float)
years -= years.mean()
slope = (stack * years[:, None, None]).sum(axis=0) / (years ** 2).sum()

两条路径的分界:已经表格化就用 SQL,还在栅格里就用 Python。

16.5 AI 分割:SamGeo 与 SAM 3

16.5.1 架构:为什么模型不在 sidecar 里

GeoLibre 的 sidecar 是一个轻量 FastAPI 服务。把 PyTorch 加 SAM 3 塞进去会让它变成一个几 GB 的怪物——而且没 GPU 的机器根本跑不动。所以设计是三进程:

SegmentationDialog ──图像+提示──▶ sidecar /ml/segment/* ──▶ samgeo-api (SAM 3) ──▶ GeoJSON

sidecar 只做一个薄反向代理(/ml 前缀)。真正的模型跑在独立的 samgeo-api 服务里,那台机器可以有 GPU,甚至可以是另一台主机。

16.5.2 安装与三个环境变量

在有可用 PyTorch(最好是 CUDA)的 Python 环境里装模型包:

pip install "segment-geospatial[api,samgeo3]"

sidecar 侧只需要它的 HTTP 客户端(模型不在这一侧):

pip install -e "backend/geolibre_server[ml]"

两种启动方式:

自动拉起(默认)。 如果 samgeo-api 在 sidecar 所在环境的 PATH 上,GeoLibre 首次使用时自动启动它。

自己跑 / 跑在另一台机器上。 启动服务并让 GeoLibre 指过去:

samgeo-api --port 8000
# 然后带环境变量启动 sidecar:
GEOLIBRE_ML_SAMGEO_URL=http://127.0.0.1:8000

三个环境变量:

变量 作用
GEOLIBRE_ML_SAMGEO_URL 已在运行的 samgeo-api 基地址。设了它,sidecar 只代理、不拉起子进程
GEOLIBRE_ML_SAMGEO_CMD 按需启动 samgeo-api 的命令(默认 samgeo-api)。--host / --port 会自动追加
GEOLIBRE_ML_DEFAULT_MODEL UI 默认模型(默认 sam3)

注意:桌面版的自动拉起不适用。桌面版把 sidecar 跑在一个受管的 uv 环境里,那个环境带 ml extra 但不带 segment-geospatial 的模型栈,所以 samgeo-api 不在它的 PATH 上。正确做法是:在一个有 PyTorch 的环境里装好并启动 samgeo-api,然后从一个已导出 GEOLIBRE_ML_SAMGEO_URL 的 shell 启动桌面应用:

# 在 PyTorch 环境里
samgeo-api --port 8000

# 带着代理目标启动桌面应用
GEOLIBRE_ML_SAMGEO_URL=http://127.0.0.1:8000 npm run tauri:dev

Tauri 进程会把它自己的环境传给 sidecar。不设这个变量时,sidecar 没有模型后端,/ml/status 会报分割后端不可用。

16.5.3 用 AI Segmentation 对话框

Processing → GeoLibre Toolbox → AI Segmentation。

  1. 后端不可达时,对话框显示提示与一个 Start server 按钮(桌面端)。点它启动 sidecar 与模型服务。
  2. Choose a GeoTIFF. 选一个有地理参考的栅格(.tif / .tiff;PNG/JPG 也能用,但得到的是像素空间结果)。返回的多边形在栅格自己的坐标系里,所以会落在正确位置。
  3. 选模式:

    模式 参数 行为
    Text prompt 提示词(如 trees、buildings、cars)+ Confidence threshold(0–1) SAM 3 找出图里每一个匹配对象
    Automatic (everything) 无提示词 分割所有可区分对象

    Confidence threshold 的方向要记牢:调低找得多,调高更严格。

  4. 点 Segment。结果加成新的 GeoJSON 多边形图层,图层名取自提示词,地图缩放到它。

16.5.4 四条限制

限制 说明
只支持 SAM 3 GeoLibre 用的是 SAM 3 后端,它覆盖文本、框/点、自动提示。(SAM 2 刻意没接)
仅桌面版 和栅格工具一样,分割需要桌面应用与 Python sidecar,纯浏览器构建没有
“No objects found” 是正常结果 换个提示词,或降低置信度阈值
这个对话框只做文本与自动 地图上画的框/点提示在交互式 SamGeo 插件里

16.5.5 交互式 SamGeo 面板

Plugins → SamGeo 打开一个可停靠面板,它直接和 samgeo-api 说话(默认 http://127.0.0.1:8000,面板里可改),不经过 sidecar——所以它在浏览器构建里也能用。

  1. 先启动模型服务(samgeo-api --port 8000),点 Check connection。
  2. 选影像。Image source 列出地图上所有 COG 或 GeoTIFF 图层,选一个就地分割;或留在 Upload a file 选本地 .tif / .png / .jpg。想快速试手,用 Add Data → Raster Layer 里的 Aerial imagery (UC Berkeley) 样例。
  3. 选模式:

    模式 操作
    Text prompt 概念词(building、tree)+ 置信度阈值 + mask 尺寸限制
    Point prompts 点 + Foreground point / − Background point 后在地图上点;绿色与红色标记显示提示。至少需要一个前景点
    Bounding box (find similar) 点 Draw box on map 拖一个矩形;SAM 3 返回所有与框内对象相似的目标
    Automatic (everything) 分割所有可区分对象
  4. 点 Segment。多边形从栅格 CRS 重投影到 WGS84,图层名取自提示词或模式,地图缩放到它。

一个模式上的不对称值得注意:SAM 3 是提示驱动的模型,所以 Automatic 模式实际跑的是 SAM 2 的自动掩膜生成器(checkpoint 在 SAM2 model 里选,points_per_side、IoU 与 stability 阈值在这个模式下生效)。

用 Clear prompts 清除点或框。切换模式会清掉其他模式的提示,关闭面板会移除地图上的提示覆盖层。面板设置(API URL、模式、模型、阈值)随项目保存。

警告:图像会上传到你配置的那个 API URL。项目文件可以带一个保存下来的 API URL,所以用一个别人给的项目去分割本地影像之前,先看清那个字段指向哪里。

16.5.6 sidecar 暴露的端点

方法 端点 说明
GET /ml/status 后端可用性、默认模型、版本、模型列表
GET /ml/models 后端模型目录(可用与已加载)
POST /ml/segment/text 文本提示分割(prompt、confidence_threshold)
POST /ml/segment/automatic 自动掩膜生成
POST /ml/segment/predict 框/点提示分割(boxes、point_coords、point_labels、point_crs)

每个 segment/* 端点接受一个 multipart file(图像),外加 model_version(默认 sam3)与 output_format(默认 geojson),返回 GeoJSON FeatureCollection。

16.5.7 不需要 sidecar 的两个

工具 在哪 运行方式
Object Detection Processing → GeoLibre Toolbox → Object Detection 客户端,onnxruntime-web
Segment Everything Processing → GeoLibre Toolbox → Segment Everything 客户端,onnxruntime-web

这两个在浏览器里跑,所以 Web 构建与移动端都能用,不需要 sidecar、不需要 GPU 服务。

16.6 OBIA 全流程

16.6.1 它是什么、在哪

OBIA(Object-Based Image Analysis,面向对象影像分析)的核心主张是:高分辨率影像里一个地物跨很多像素,按像元分类没有意义,应该先把像素聚成对象再分类。

Processing → Object-Based Analysis 打开一个右侧栏工作台(与 Style 面板并排)。它完全在浏览器里跑,用 GeoLibre 自己的 WASM 工具引擎(geolibre-wasm),所以 Web 构建可用。

三个界面细节:

  • 首次打开前它不在侧边栏导轨上;打开后导轨图标会切到它;从它的标题栏关闭会把它从导轨上移除。
  • 关闭再打开保留当前会话。
  • 步骤是顺序的:Segment → Measure → Levels → Label → Classify → Assess → Export。

16.6.2 第一步:Segment

  1. 先加一个 GeoTIFF 或 COG 栅格图层,再打开工作台。它会自动选第一个栅格图层;换图层用 Image。
  2. 勾选要参与分割的 Bands。每个波段会先做 z-score 标准化,所以值域大的波段不会压倒其他波段。要用原始多光谱波段(比如红、绿、蓝、近红外),而不是显示用的渲染结果。
  3. 选 Area:Whole image 或 Current map view。下面那行会说明这次运行读多少像素、在什么分辨率上读。
  4. 设参数,点 Segment。
参数 含义
Similarity threshold 一个像素与其区域种子在标准化波段单位上允许差多少还能并入。值越大,对象越少越大
Minimum object size (pixels) 小于此值的对象并入最相似的邻居
Seed steps 以多少组种子分批生长,最同质的先长,所以区域从均匀处往外长而不是从边缘长

结果是名为 <image> objects 的矢量图层,每个对象一个多边形,叠在影像上。每个要素的 segment_id 属性(同时是要素 id)就是对象的标号——属性表、选择、后续所有步骤都用同一套对象。

勾选 Also add the label raster to the map 会把标号栅格本身也加进来。

算法是什么:Whitebox 的种子区域生长(image_segmentation)。它不是 eCognition 的多分辨率分割,所以eCognition 的 scale 参数不能照搬——必须在你自己的影像上调阈值与最小尺寸。Whitebox 目录里其他的分割工具(SLIC 超像素、Felzenszwalb 图、标记分水岭)都是同一个区域生长换了阈值映射的包装,这就是工作台用它的真名的原因。

第二个浏览器方法:Felzenszwalb graph method (browser),按 scikit-image 的实现方式做 Felzenszwalb-Huttenlocher 图分割,参数是 Scale、Smoothing (sigma)、Minimum object size,最多读 4 194 304 像素(4096 × 1024)。

原生分割(桌面版):桌面版可以在处理服务里用 scikit-image 做,方法选 SLIC superpixels(Object size (pixels)、Compactness)或 Felzenszwalb graph(Scale、Smoothing (sigma)、Minimum object size (pixels))。原生运行从文件读影像,所以影像必须来自本地 GeoTIFF(URL 加的影像会用浏览器引擎)。上限是 SLIC 1.2 亿像素、Felzenszwalb 2500 万像素。第一次原生运行会往处理服务里装 scikit-image,约一分钟。两种原生方法都是确定性的,重开项目会重建同样的对象。

性能对照(4 波段、9222 × 5089 = 4700 万像素的 Sentinel-2 样例,默认参数):

引擎 对象数 Segment Measure 峰值内存
浏览器,区域生长 63 942 369 s 208 s 7.6 GB
原生,SLIC 94 887 46 s 33 s 3.9 GB
原生,Felzenszwalb 234 343 131 s 36 s 16.9 GB

注意:浏览器的数字是引擎在浏览器之外跑整景的结果,工作台自己会从 overview 读。

大影像怎么办:单次运行最多读约 1678 万像素(4096 × 4096)。更大的影像仍然能处理:

  • Current map view 只读视口内的部分。放大到 Area 那行显示 at full resolution 为止。
  • 区域在全分辨率下超限时,工作台改从影像的 overview 读,选能装下的最细 overview(例如 10 m 的 Sentinel-2 会读 20 m)。对象因此更粗,Area 那行会说明。没有 overview 的影像必须先放大,或者先裁(例如 Processing → GeoLibre Toolbox → Raster → Clip by extent)。
  • URL 加的 COG 用 HTTP Range 读:列波段只读文件头,运行时只取该区域与该 overview 的瓦片,所以大范围远程场景不会被整份下载。

分割区域与 overview 会随运行一起记录,所以测量、导出、重开项目读的都是同一批像素,Provenance 会列出它们。导出按分割区域的栅格对齐。

从别的软件导入(Import from other software,在 Segment 步骤下面):

导入项 接受什么 行为
Objects 多边形图层 把多边形烧到 Segment 里选的影像上作为对象。像素中心落在多边形内即归它。对象 id 来自一个字段(互不相同的正整数,上限 16 777 216),或按顺序编号。没盖住任何像素中心的多边形被丢掉,步骤会说明丢了多少。原始属性保留
Samples 点图层(或多边形,取内部点) 给对象打标签,类别来自一个选定字段,角色(training / validation)来自字段或手选。样本冲突时第一个胜出,步骤会说明影响了多少对象
Class list JSON 的 {"name": ..., "color": "#rrggbb"} 列表,或含 name/color 列的 CSV 已有类别取文件里的颜色,新类别被添加
Feature table 含 segment_id 列的 CSV 并进已测特征(同名替换),所以规则与分类器都能用。匹配不到对象的行被丢弃并计数
Level mapping child_id,parent_id 的 CSV 按映射建上层,而不是靠合并。映射漏掉的对象各自成为自己的父对象;一个子对象被指派两个父对象会被拒绝
eCognition rule set .dcp 规则集或 .dpr 项目 转成 Classify 步骤可用的规则集(见 16.7)

16.6.3 第二步:Measure

对象有了之后,Measure 在原始波段(不是显示渲染)上算逐对象特征,写回对象图层。

组 字段
Spectral statistics(光谱统计) 每个参与分割的波段 n:mean_b<n>、std_b<n>、min_b<n>、max_b<n>(n 与源影像波段号一致)
Spectral indices(光谱指数) brightness(各波段均值的平均)、ndvi(红与近红外)、ndwi(McFeeters,绿与近红外)。哪个波段扮演哪个角色可以选;4 波段影像默认红 = 1、绿 = 2、近红外 = 4
Shape(形状) area_px、perimeter_px、compactness、bbox_width_px、bbox_height_px、elongation
GLCM texture(纹理) 选定波段上的 glcm_contrast_b<n>、glcm_homogeneity_b<n>、glcm_energy_b<n>、glcm_entropy_b<n>。太小的对象凑不出像素对,就没有值
Neighborhood(邻域) neighbor_count、shared_boundary_total、mean_shared_boundary

重新测量会替换之前的特征值,但保留其他属性(比如训练标签)。重新分割会开始一批新对象,所以训练分类器之前必须重新测量。

16.6.4 第三步:Levels(层级)

对象可以合并成更粗的层级,像 eCognition 的对象层次:建筑 → 街区 → 片区。

Build coarser level 把当前层的对象并成新层:邻居合并,最相似的先合,直到再合一次会让某对象在标准化波段单位上的值离散度(按面积加权的标准差,跨波段求和)增加超过 scale²。scale 越大,对象越大越杂。

这正是多分辨率分割的颜色准则,只不过作用在整对象上。

两条结构保证:

  • 每个粗层对象由下层整对象组成,层级严格嵌套。
  • 下层每个对象在 obia_parent 里记自己的父对象;粗层每个对象有 child_count。

粗层需要在下层测过光谱统计,它的特征从下层推上来:波段统计精确合并自子对象,形状与邻域特征在合并后的对象上重新测(GLCM 纹理不往上带)。粗层在浏览器里建,基于最多 1678 万像素的分割。

上下文特征(Add context features)把每个对象周边的信息加成特征:

字段 含义
nb_contrast_b<n> 对象波段均值减邻居均值,按共享边界长度加权
parent_<feature> 父对象的波段均值、指数、尺寸、child_count
parent_is_<class> 父对象被分到该类时为 1,否则 0(类继承作为特征)
child_frac_<class> 对象面积落在下层各类别里的比例(下层分类后才有)

类名进入字段名时转小写,其他字符换成下划线(”Trees, shrubs” 变成 parent_is_trees_shrubs)。再次添加上下文特征会替换旧的,所以分类完另一层之后要重跑一次。

Work on 决定后续步骤用哪一层:样本、分类器、精度评估、导出都归属那一层,每层各自保存自己的。Apply to other images 用的是第 1 层。 新的分割会开始新的层级;重新测量某一层会丢掉基于它建的层——那些层的对象图层还在图上,不需要就手动删。

16.6.5 第四步:Label samples(标注样本)

  1. 用 Add class 为每个地表覆盖类型加一个类,命名并选颜色。
  2. 用 GeoLibre 的任一选择工具选对象:地图选择工具、属性表行、或 Edit → Select by Expression…(例如 [">", ["get", "ndvi"], 0.2] 在测量之后选植被对象)。
  3. 选新标签是 Training 还是 Validation。
  4. 点某个类行上的标签按钮。对象填上类色,类行统计训练/验证样本数。
操作 行为
Clear labels of selected 从选中对象上移除标签
删除类 它的标签一起删
重命名类 它的对象重新打标
Split 把每类训练样本的一部分(四舍五入,每类至少一个)移到验证集。按类分层、可复现:同一个种子选同一批样本。每次 Split 作用于剩下的训练样本,所以连续 split 会继续搬走一部分

标签存在对象自身的 obia_class 与 obia_sample(值为 training 或 validation)属性里,所以随项目保存,属性表里也能看到。

精度评估需要分类器从没训练过的验证样本。要么单独标,要么用 Split 搬一部分。

16.6.6 第五步:Classify

预测结果写进 obia_predicted 属性,图层按预测类着色。四种方式:

方式 需要样本 行为
Random forest 是 用勾选的特征(默认全选)在训练样本上训练,验证样本不参与。引擎(classify_objects_random_forest)固定随机种子,所以同输入同输出。缺某个特征值的对象(例如极小对象的 GLCM 纹理)用该特征的均值填充,步骤会说明影响了哪些特征
Threshold rules 否 每条规则把一个特征和一个值比较,对象取从上到下第一条匹配的规则的类。一条都不匹配的落到默认类(灰色显示)。规则要从最具体排到最宽泛
Ruleset 否 跑 eCognition 风格的规则集:模糊类描述 + 过程树,JSON 形式(见下)
Inherit from level above 上层已分类 每个对象取父对象的类。先分粗层,再往下继承并细化——例如用上下文特征(parent_is_<class>)做阈值规则:”父对象是水体的建成对象是船”

Whitebox 目录里的 “SVM” 与 “ensemble” 对象分类器其实是同一个随机森林换了树数,所以工作台只提供随机森林。

提示:随机森林的”缺值用均值填”是一把双刃剑。它在小对象上稳住了训练,但也让这些对象的分类几乎必然落在多数类上。如果 GLCM 特征对你的分类很关键,考虑先按面积过滤掉过小的对象。

16.6.7 规则集(Rulesets)

规则集是一串按顺序跑的过程。每个过程作用于一个 domain:当前类属于 classes 的对象("" 表示未分类;不给 classes 表示任意类),且满足 conditions 里的每一个条件(一个特征、一个操作符 > / >= / < / <= / == / !=、一个值)。

四种过程:

kind 作用
assign 把 domain 的对象设为 className
unassign 把 domain 的对象变回未分类
fuzzy 按模糊类描述分类。每类用 and(最小值,默认)、or(最大值)或 mean 组合隶属函数;对象取隶属度最高且达到 minMembership(默认 0.1)的类,否则保持不变
loop 重复它自己的过程直到一轮没有变化(或到 maxIterations,默认 100)

隶属函数的四种形态:

type 形状 参数
larger 在 from 处 0,升到 to 处 1 from、to
smaller 在 from 处 1,降到 to 处 0 from、to
about 在 center 处峰值 1,到 width 距离外降为 0 center、width
curve 过 values(2 到 64 个数,0–1)在 from 到 to 之间等距取点,端点外保持端值 values、from、to
threshold 条件成立为 1,否则 0 op、value

curve 是最重要的一种——eCognition 就是这么存它的隶属函数的,包括 sigmoid。没有隶属函数的类隶属度为 1,与 eCognition 里空类描述的行为一致。

除了已测特征与上下文特征,条件与隶属函数还能读 nb_border_<class>:对象边界里与当前属于该类的邻居共享的比例,每个过程前重算。类必须是图例里的、规则集自己赋的、或(从当前分类开始时)现有分类里有的;拼错的会被拒绝。

后缀规则:类名转小写、去变音符号、连续的空格或标点换成 _(所以 Bare soil 变成 nb_border_bare_soil),两个类名撞到同一个后缀时加 _2、_3。Insert example 会给你的第一个类写一个示例。

有了它,一个 loop 就能一圈一圈把某类往外长。下面这个例子从全体未分类对象开始:

{
  "processes": [
    {
      "kind": "fuzzy",
      "name": "spectral classes",
      "minMembership": 0.5,
      "classes": [
        { "className": "vegetation", "memberships": [{ "field": "ndvi", "type": "larger", "from": 0.1, "to": 0.4 }] },
        { "className": "built", "memberships": [{ "field": "ndvi", "type": "smaller", "from": -0.1, "to": 0.1 }] }
      ]
    },
    {
      "kind": "loop",
      "name": "grow vegetation",
      "processes": [
        {
          "kind": "assign",
          "domain": {
            "classes": [""],
            "conditions": [
              { "field": "nb_border_vegetation", "op": ">=", "value": 0.5 },
              { "field": "ndvi", "op": ">", "value": 0 }
            ]
          },
          "className": "vegetation"
        }
      ]
    }
  ]
}

Insert example 会为你的类与特征写出这样的规则集;Open 与 Save 读写文件。规则集边输入边校验,消息会指出第一个问题。勾选 Start from the current classification 可以在现有分类上细化,而不是从未分类开始。跑完每个过程输出一行”改了多少对象”(loop 会说明跑了几轮)。

规则集只读特征与对象图,所以 Apply to other images 可以原样在别的影像上跑。

16.6.8 第六步:Assess accuracy

分类之后,工作台拿预测结果与验证样本(随机森林从没训练过的那批)比对,评分随重新标注实时更新。

指标 含义
Overall accuracy(总体精度) 验证对象中预测类与标签一致的比例
Kappa Cohen’s kappa,扣除随机一致后的一致性
Area-weighted(面积加权) 每个验证对象按像素面积加权的总体精度。大对象分错比小对象分错更严重。需要形状特征
Confusion matrix(混淆矩阵) 行是参考类、列是预测类,带每类的 producer’s accuracy(该类被找出了多少)与 user’s accuracy(该类的预测有多可靠)

Download report (CSV) 保存矩阵与各项数字。

注意:想拿到诚实的分数,验证样本要散布在整个场景,而不是贴着训练样本;而且不要在验证集上反复调参——否则它们就不再独立了。

16.6.9 第七步:Export

对象图层本身就是矢量结果:每个对象的 obia_predicted 属性存着它的类,所以它的图层菜单可以把分类导出成 GeoJSON、GeoPackage、Shapefile 以及其他矢量格式(Processing → GeoLibre Toolbox → Vector → Dissolve 还能按类合并对象)。

Export 步骤还会把类烧到像素栅格上:

选项 产出
Add classified raster 按类色渲染的彩色图层
Save class codes (GeoTIFF) 单波段 Cloud-Optimized GeoTIFF,存类代码,按分割区域的栅格、在源影像的 CRS 里,NoData 为 0
Save legend (CSV) 类代码、类名、颜色的对照表

类代码跟随类别列表顺序(第一个类是 1),所以一个类在多次运行之间保持同一个代码;规则集的默认类如果不在列表里,排在最后。

16.6.10 第八步:Apply to other images

一张影像分类完成后,Apply to other images 把同一套流程跑到项目里的其他栅格图层上:勾选影像,点 Apply。每张影像都按整幅范围分割(超像素上限时用 overview)、用同样的波段与参数、测同样的特征、用当前分类器分类。

分类方式 行为
Rules 原样应用
Random forest 用本影像的训练样本训练,去预测另一张影像的对象——所以其他影像必须有相同顺序的相同波段(同传感器、同处理级别),否则森林看到的值与它学到的不是一回事

它用的是点 Apply 那一刻的训练样本,以及最后一次 Classify 用过的特征——所以改过样本之后要重新分类,批处理才会和屏幕上的分类一致。

每张影像得到自己的对象图层,按类着色,步骤下面会列出它的对象数与类别数。标签、精度与导出仍属于第一张影像。

16.6.11 进度、取消与溯源

主题 行为
运行时 按钮下面那行显示工具名与已用时间
Cancel 立即停掉正在跑的工具,该步骤保留之前的结果
批量取消 已完成的影像保留
随项目保存 影像与波段选择、参数、类别与分类器设置、每步结果(对象图层保留特征、标签、预测类)、对象层级(每层带自己的样本与分类)、正在工作的层
粗层标号栅格 从下层与 obia_parent 链接重建,不存进项目
Provenance 展开可见:影像(图层名、文件路径或 URL)、尺寸与所用波段;每一步何时跑、确切的工具调用与参数、geolibre-wasm 与 GeoLibre 版本;每次 hold-out 的比例、种子、搬了多少样本;随机森林的树数、特征数、训练样本数;批量应用时每张影像的工具调用与版本
Copy as JSON 复制这份记录,就是项目 obia 字段里存的那一份

项目不存标号栅格(可能很大)。重开项目后某一步需要它时(重新测量、导出),工作台会在源影像上重跑记录下来的分割——分割是确定性的,而且它会先用保存的分割指纹(对象数 + 每个像素标签的哈希)核对重建结果再使用。如果影像已从项目移除或不再产出同样对象,步骤会明说:重新分割。

注意:Web 应用里从本地文件加的影像不会随项目保存。想重开项目还能重跑各步骤,就按 URL 加影像(或者用桌面版)。

16.7 从 eCognition 迁移

16.7.1 概念映射

工作台读 eCognition 规则集(.dcp)与项目(.dpr)的分类部分,其余靠下表手工翻译,结果用 Import from other software 搬过来。

图例:Yes 行为一致;Partly 可用但有差异;No 不可用。

eCognition 工作台  
影像对象层(Image object level) Levels Yes
多分辨率分割(第一层) 种子区域生长、SLIC、Felzenszwalb Partly:算法不同,scale 参数不能照搬
多分辨率分割(上层) Build coarser level Partly:同样的颜色异质性准则作用在整对象上,best-first;没有形状准则(compactness/smoothness 权重)
Chessboard / quadtree 分割 无 No
分割中的主题图层(沿矢量边界切对象) 无 No
光谱差异分割 小 scale 的 Build coarser level Partly:按异质性增量合并,不是按均值差
父对象包含子对象 每个粗层都是整子对象的并集(obia_parent、child_count) Yes
Merge region(按类合并) 无(合并只按异质性) No
Split / grow / shrink / 对象形态学 无 No
从导出的对象建层 先 Import objects,再 Level mapping Yes

特征层面的映射:

eCognition 工作台字段  
Layer mean / std / min / max mean_b<n>、std_b<n>、min_b<n>、max_b<n> Yes
Brightness brightness(波段均值的平均) Partly:未加权
自定义算术特征(指数) ndvi、ndwi;或导入 feature table Partly
Area / border length area_px、perimeter_px(像素与像素边) Yes,单位是像素
Compactness / roundness / shape index compactness(4π·面积/周长²) Partly:该族里只有一个形状量
Length/width / asymmetry elongation(基于外包框)、bbox_width_px、bbox_height_px Partly
GLCM homogeneity / contrast / entropy glcm_contrast_b<n> 等 Partly:每对象一个对称 GLCM,单波段,距离 1,水平与垂直像素对合并(仅浏览器引擎)
Number of neighbors / border to neighbors neighbor_count、shared_boundary_total、mean_shared_boundary Yes
与邻居的均值差 nb_contrast_b<n>(按边界加权) Yes
到某类的相对边界 nb_border_<class>(规则集里;类名做字段安全后缀) Yes
父对象特征 parent_<feature> Yes(波段均值、指数、尺寸)
某类父对象的存在性 parent_is_<class> Yes
子对象特征(数量、某类相对面积) child_count、child_frac_<class> Yes
主题图层特征 导入带 segment_id 列的 feature table Partly:在工作台之外算,比如在 GIS 里
到某类的距离 无 No

16.7.2 导入规则集

在 Import from other software 下,eCognition rule set 读 .dcp 或 .dpr(eCognition / Definiens Developer 7 及以后;加密规则集读不了;文件上限 128 MB):

eCognition 转成
Execute child processes 它的子过程按顺序;重复时变成一个 loop(给定次数,或”while something changes”,最多 1000 轮)
Assign class assign(未分类时 unassign)
Remove classification unassign
Classification(类描述) fuzzy:每个活动类的描述,隶属函数变 curve,阈值变 threshold,按 and(min) 或 or(max) 组合;用类层次的最小隶属度
Image object domain:类过滤 + 用 and 连接的条件 过程的 domain

特征会映射到工作台算同样东西的字段:Mean <layer>、Standard deviation <layer>、Max. pixel value <layer>、Min. pixel value <layer>(按图层的波段)、Brightness、Area(像素)、Number of pixels、Border length(像素)、Rel. border to <class>(→ nb_border_<class>)、Existence of <class> (0)(→ nb_border_<class> 大于 0 的邻居)、Existence of super objects <class> (1)(→ parent_is_<class>,需先加上下文特征)、以及两个图层均值构成的自定义归一化差值(当它读的图层正是 Measure 用的波段时,映射为 ndvi 或 ndwi)。其他特征保留 eCognition 原名——从 eCognition 导一份带该列的 feature table,规则集就能用。

不转换并会列出原因的:分割(在 Segment 里重做)、导出与显示过程、用 or 连接的条件、与变量比较的条件、其他 domain(像素层、链接对象、地图)、最近邻与类描述里的其他算子、变量与数组、合并/生长/收缩对象、层级管理、样本与监督分类,以及任何表里没有的算法。转换出来的过程全都跑在你正在工作的那一层,不管规则集原本写的是哪一层。

16.7.3 转换率有多高

取决于规则集里有多少是分类逻辑。在公开规则集上的统计(容器过程只要有一个子过程转换成功就算转换):

规则集 过程数 转换成功 主要没转的
Buildings and water, Hamden NY 54 39 merge region
Laughing gull nests 64 45 merge region、层级管理
Water, seed growing 27 14 分割、merge region
Historical imagery, NAIP 20 7 分割、merge region
Seafloor geomorphology 22 9 分割、对象缩放
NSW estuarine habitats 240 35 手工分类、变量、层级
Yalova land cover 2 813 191 监督分类、样本、地图
Field boundaries 292 11 变量、图层算术、层级

这张表最有价值的信息是分布:分类逻辑重的规则集能转 70%,而上千过程的大规则集往往只转 5%–10%——因为大部分过程是分割、层级管理与变量运算,这些工作台没有对应物。

16.7.4 迁移的四步

  1. 在 eCognition 里跑一遍并导出产物:每层的影像对象(多边形 + id)、样本、类层次(名称与颜色)、你依赖的对象特征(带对象 id 的 CSV)、每层的父对象 id。对象 id 必须是互不相同、不超过 16 777 216 的正整数,更大的要先重编号。
  2. 在 GeoLibre 里加影像与导出的图层,然后用 Import from other software 依次导入:objects(带 id 字段)、feature table、level mapping、class list、samples。
  3. 导入规则集(见上),然后重建没转过来的部分:用上表找对应关系,写成规则集或阈值规则。标 No 的算法,保留 eCognition 那部分的结果(把它的对象或类导进来),而不是硬造一个。
  4. 用 Assess accuracy 对照 eCognition 的结果,验证样本取自 eCognition 的分类。

16.7.5 验证结果:能带过来什么

工作台在三个 eCognition 培训课程的工作流上做过验证,都以 eCognition 自己的分类为参照打分。

数据:Landsat 7 ETM+ 场景(1999 年 9 月),威尔士北部高地,1001 × 1001 像素、15 m:全色波段加六个多光谱波段重采样到它,与 eCognition 项目用的图层和分辨率一致。参照是该项目的 18 类土地覆盖分类(栅格上 24 807 个对象)。

第一个工作流:手工翻译的阈值规则集。 六类阈值规则(按 NDVI 分水与非植被,按波段均值与 NDVI 分森林、改良草地、沼泽/石南,最后兜底一类)。18 个参照类按面积归并成六类后比较:

运行 面积一致率 Kappa
翻译后的规则集,整景 67.2% 0.55
翻译后的规则集,留出块 66.9% 0.55
随机森林,6 类,留出块 55%–62% 0.38–0.49
随机森林,18 类,留出块 30%–37% 0.24–0.31

逐类的 producer/user 精度:水 0.91/0.95、森林 0.82/0.87、沼泽石南 0.69/0.90、非植被 0.78/0.70、改良草地 0.86/0.50。兜底类最弱(0.20/0.24)——课程本身就预期如此。

作为对照,scikit-learn 的随机森林在同特征同标签(全样本)上 out-of-bag 精度是 0.74(6 类)与 0.44(18 类),与工作台的森林在 85% 的对象上一致。结论:限制精度的不是分类器,而是 18 个细类本身、以及标签被搬到画法不同的对象上。

第二个工作流:监督分类。 同一场景另一块 701 × 701 像素区域,参照是 eCognition 的四类分类(森林、水、非植被、其他植被)。工作台用默认阈值的区域生长(10473 个对象)加随机森林(100 棵树):

每类样本数 随机森林 Kappa 1-最近邻 Kappa
10 82%–87% 0.67–0.75 81%–91% 0.65–0.82
25 82%–84% 0.67–0.69 81%–87% 0.65–0.74
100 88%–90% 0.76–0.80 86%–88% 0.74–0.77
全部(3836 个对象) 93.5% 0.86 92.6% 0.84

随机森林在同一批样本上与最近邻打平——所以换分类器不是迁移监督工作流的瓶颈,样本数量才是。

第三个工作流:导入的过程树。 一个带主题图层的云标记过程,按 NDVI 分非植被,再在其中按 SWIR 波段均值与 NDVI 分水,然后用 “Rel. border to Water” 把水往外生长十次。导入器只转出了云分类(项目文件里唯一的分类),主题图层的 Num. of overlap: Cloud 特征保留了原名,靠 feature table 供值。两个分割(一个沿云与高地/低地多边形切)没被转换。手势翻译了非植被与水的 assign 过程,以及生长 loop。工作台的 loop 在 3 轮后停下(没有变化了)。merge region 没有对应物,被略过。

类 Producer’s User’s
水 0.97 0.97
水(不含生长 loop) 0.88 0.99
非植被 0.74 0.82

生长 loop 把水体的查全率从 0.88 提到 0.97——原过程树用它来处理小片云下被遮挡的湖面部分。

16.7.6 三条结论

  1. 推荐流程:区域生长(浏览器里)或 Felzenszwalb / SLIC 分割 → Measure → 导入或翻译 eCognition 的分类规则、检查没转过来的部分、对照 eCognition 输出做精度评估。
  2. 能带过来的是分类逻辑:阈值、隶属函数、类顺序、domain、基于邻居关系的 loop,都直接翻译且结果相近;随机森林在同一批样本上替代最近邻不掉精度。
  3. 带不过来的是 eCognition 的分割——没有任何方法能复现它的对象——以及跨层工作的过程。这是迁移时要预期的两处主要差异。

16.8 目标检测与 Segment Everything

工具 入口 引擎 输出
Object Detection Processing → GeoLibre Toolbox → Object Detection 客户端 onnxruntime-web 带类别与置信度的检测框
Segment Everything Processing → GeoLibre Toolbox → Segment Everything 客户端 onnxruntime-web 全图掩膜转多边形
AI Segmentation Processing → GeoLibre Toolbox → AI Segmentation sidecar + samgeo-api 提示驱动的多边形
SamGeo 面板 Plugins → SamGeo 直连 samgeo-api 四种提示模式的多边形

三者的分工很清楚:要”图里有什么”用 Object Detection,要”全部分开”用 Segment Everything,要”我要这一类”用 AI Segmentation 或 SamGeo。

前两个跑在浏览器里,所以 Web 与移动端可用,代价是模型小、类别固定;后两个需要 GPU 服务,换来 SAM 3 的开放词汇能力。

16.9 精度与适用边界

16.9.1 精度报告怎么读

指标 回答 什么时候会误导
Overall accuracy 总体对了多少 类别极度不平衡时(99% 是森林,全判森林也有 99%)
Kappa 扣除随机一致后的表现 类分布极偏时数值偏低,不代表方法差
Producer’s accuracy 这个类被找出了多少(查全) 单看它会把”宁可错杀”的方法显得很好
User’s accuracy 判成这个类的有多可靠(查准) 单看它会把”宁可漏掉”的方法显得很好
Area-weighted 按面积加权的总体精度 需要形状特征;大对象主导结果

必须成对看 producer’s 与 user’s。一个类的 0.95/0.30 意味着”几乎全找到了,但标成它的大多是错的”——这比 0.60/0.60 更糟。

16.9.2 适用边界

方法 分辨率要求 类别数上限(实践) 样本要求 典型失效场景
光谱指数 + 阈值 10–30 m 2–4 无 混合像元、薄云、阴影
随机森林(像元) 10–30 m 5–12 每类 100+ 空间自相关导致验证集不独立
OBIA 规则集 ≤ 10 m 5–10 无(需调参) 规则之间互相遮蔽
OBIA 随机森林 ≤ 10 m 5–12 每类 50+ 对象 对象边界与地物边界不一致
SAM 3 文本提示 ≤ 1 m 概念级 无 小目标、遮挡严重、非可见光
Object Detection ≤ 0.5 m 固定类别集 无 尺度差异大、旋转目标

16.9.3 十个常见错误

现象 原因 修法
算出的 NDVI 全在 −2 到 2 之外 波段号错位(Landsat 的 blue 是 2 不是 1) 核对传感器预设
EVI 值域是几百 没设 scale 填 0.0001(S2)或 0.0000275(Landsat C2)
影像显示几乎全黑 没设 rescale 从 [0, 3000] 起调
NDWI 判水体到处是水 阈值套用了别的场景 在当前景上用样本点重新定阈值
变化图全是边缘描边 两期影像未配准 先配准,或用更粗的聚合单元
OBIA 对象把道路和田地粘在一起 相似度阈值太大 调小阈值,或减小最小对象尺寸
分类结果整片是同一类 训练样本严重不平衡 每类样本数拉平
精度 95% 但图上明显错 验证样本与训练样本空间重叠 用 Split 做空间留出
批量应用后另一张影像结果全错 波段顺序或数量不同 确认同传感器同处理级别
粗层对象与子对象对不上 重新测量了子层 重新建粗层

16.9.4 一条元规则

所有基于阈值的结论都需要在当前数据上重新验证。 无论是 NDVI > 0.2 判植被、NDWI > 0 判水体,还是 dNBR 的burn severity 分级,那些数字都来自特定传感器、特定大气条件、特定地区的经验。把它们当”物理常数”用,是遥感分析里最常见的系统性错误。

16.10 本章小结

  • 影像入口有七条(本地、远程 COG、STAC Layer、STAC Catalogs、Sentinel-2 Explorer、NASA 三兄弟、Planetary Computer / Earth Engine)。只有能给原始像素值的入口才能做分析——GIBS 的预渲染瓦片不能。
  • Sentinel-2 Explorer 背后没有服务端:它读 Source Cooperative 上的静态 STAC-GeoParquet,每次检索是 HTTP Range 读;合成与指数在浏览器里拼,反射率已按 04.00 基线的 1000 偏移校正;composites 需要 MapLibre 渲染器。
  • 三个 “NASA” 分工:Earthdata (GIBS) 是预渲染瓦片(只能看)、Earthdata GIS 是 ArcGIS 服务、Earthaccess 才是原始 granule 下载(要 Earthdata Login token)。
  • 波段引用一律 1-based。Landsat 8/9 的 blue 是 2(B1 是海岸气溶胶),这是最高频的错位来源。
  • Style 面板三种 mode(single / rgb / index),公共字段包括 rescale、nodata、opacity、gamma、stretch(linear / log / sqrt)、colormap。rescale 决定值域,stretch 决定曲线,两者要一起调。
  • 八个内置光谱指数都是栅格计算器的预设前端:六个归一化差值、EVI、SAVI。归一化差值不需要设 scale,EVI 与 SAVI 必须设。
  • 四个传感器预设:Sentinel-2(blue 1 / red 3 / nir 4)、Landsat 8/9(blue 2 / red 4 / nir 5)、NAIP(无 SWIR,算不了 NDMI/NDBI/NBR)、Custom。
  • Raster calculator 的表达式把第一张栅格写作 A(波段 1)与 An,可用 where / log / exp / sqrt / abs / clip / minimum / maximum / sin / cos / tan。除零要自己用 where 兜底,尺寸一致性它不替你检查。
  • 变化检测的四步是:取同期同源影像 → 各算指数 → 相减 → Reclassify 定阈值(规则串形如 min:-0.1:1, -0.1:0.1:0, 0.1:max:2,区间左闭右开)。三个前提:几何配准、辐射一致、物候一致。
  • AI Segmentation 是三进程架构:对话框 → sidecar 的 /ml 反向代理 → 独立的 samgeo-api。桌面版的 sidecar 环境不带模型栈,所以必须手工起服务并导出 GEOLIBRE_ML_SAMGEO_URL。
  • SamGeo 面板直连 samgeo-api、在浏览器里也能用;它的 Automatic 模式实际跑的是 SAM 2 的自动掩膜生成器,其余三种模式走 SAM 3。
  • OBIA 工作台完全在浏览器里跑(geolibre-wasm),八步流程:Segment → Measure → Levels → Label → Classify → Assess → Export → Apply。单次分割上限约 1678 万像素(4096 × 4096),超了改读 overview 或只跑当前视口。
  • OBIA 的分割不是 eCognition 的多分辨率分割,是 Whitebox 的种子区域生长,scale 参数不能照搬。重新分割要重新测量;重新测量某一层会丢弃基于它建的粗层。
  • 关键字段名:segment_id、obia_class、obia_sample、obia_predicted、obia_parent、child_count,以及特征族的 mean_b<n> / nb_contrast_b<n> / parent_is_<class> / nb_border_<class>。
  • 规则集四种过程:assign / unassign / fuzzy / loop;隶属函数五种形态里 curve 是 eCognition 的原生存储形式。nb_border_<class> 让 loop 能一圈一圈把类往外长。
  • 精度必须成对读 producer’s 与 user’s,并尽量用面积加权与空间留出的验证样本。
  • eCognition 迁移的结论:分类逻辑能带过来,分割带不过来。分类逻辑重的规则集能转 70%,上千过程的大规则集往往只转 5%–10%。

16.11 实践练习

  1. 波段号实验:取一份 Landsat 8/9 的 7 波段反射率数据,先用 Sentinel-2 的预设(blue 1 / red 3 / nir 4)算一次 NDVI,再用 Landsat 预设算一次。把两个结果叠在同一份真彩色图上对比,说明错误结果在哪些地类上错得最明显。

  2. scale 的作用:对同一份 Sentinel-2 L2A 数据,把 Spectral index 设为 EVI,scale 分别填 1 与 0.0001。记录两次输出的最小值/最大值(用图层 Metadata 或 Zonal statistics),并解释为什么其中一个在物理上不成立。

  3. 变化检测全流程:取同一区域两个年份的 Sentinel-2 NDVI,用 Raster calculator 做差(记得用 where 处理除零),再用 Reclassify 分成”减少 / 不变 / 增加”三类。然后做一次伪差分(同一年两景),量出噪声量级,据此论证你的阈值选得是否合理。

  4. OBIA 全流程:用 Processing → Object-Based Analysis 对一份高分辨率影像走完 Segment → Measure → Label → Classify → Assess。要求:至少 4 个类、每类至少 20 个训练样本、用 Split 留出验证集、记录 Overall accuracy 与 Kappa,并只报告验证集上的分数。最后导出 Save class codes (GeoTIFF) 与 Save legend (CSV)。

  5. 规则集练习:用 Insert example 生成一个规则集,改写成”先按 NDVI 分植被与非植被,再用 nb_border_<class> 的 loop 把植被往外长 3 轮”,勾选 Start from the current classification 对比开与不开的差别。记录每个过程改了多少对象。

  6. 精度陷阱识别:构造一个类别极不平衡的场景(例如 95% 是一个类),故意做一个”全判多数类”的分类,算出它的 Overall accuracy、Kappa 与各类的 producer’s/user’s accuracy。用这组数字说明为什么不能只看 Overall accuracy。

16.12 延伸阅读