GeoAI 树冠高度估算指南:基于 DINOv2 + DPT 的高分辨率 RGB 遥感影像 canopy height 制图

原创2026-10-03 15:04:071,427 阅读
文章标签:人工智能深度学习计算机视觉数据可视化数据分析

GeoAI 树冠高度估算指南:基于 DINOv2 + DPT 的高分辨率 RGB 遥感影像 canopy height 制图

本指南围绕 GeoAI 项目中的 geoai.canopy 模块展开,讲解如何仅凭 RGB 航拍/卫星影像直接预测逐像元的树冠高度(单位:米),支撑林业分析、碳储量估算与城市树木覆盖评估。读完本文,你将掌握模型变体选型、CanopyHeightEstimation 类与 canopy_height_estimation() 便捷函数的完整用法、瓦片化推理的参数调优,以及基于源码实现的底层原理。

模块定位与应用场景

docs/canopy.md 对 canopy 模块的定位十分清晰:利用深度学习模型从卫星影像中进行树冠高度估算,并且支持大规模预测,典型应用包括:

  • 林业分析(forestry analysis):快速生成区域尺度的树冠高度图,替代或补充大范围 LiDAR 测量;
  • 碳储量估算(carbon stock estimation):树冠高度是生物量与碳储量反演的关键输入;
  • 城市树木覆盖评估(urban tree cover assessment):为城市规划与生态监测提供量化指标。

在 GeoAI 项目中,该模块的实现位于 geoai/canopy.py(约 1400 行),配套的实战 Notebook 为 docs/examples/canopy_height.ipynb,后者以 NEON 森林站点航拍影像 + LiDAR 真值 CHM(Canopy Height Model)演示了从数据下载到结果可视化的完整流程。

技术原理:自监督 ViT 骨干 + DPT 解码器

geoai.canopy 集成了 Meta(FAIR)的 HighResCanopyHeight 模型,核心架构为:

  • DINOv2 骨干网络:使用自监督 Vision Transformer 提取多尺度视觉特征;
  • DPT(Dense Prediction Transformer)解码器:将多尺度特征重组、融合并逐级上采样,最终输出与输入同分辨率的逐像素高度图。

模型在 NAIP 航拍影像 + 机载 LiDAR 真值 上训练,论文出处为 Tolan 等人 2024 年发表于 Remote Sensing of Environment 的工作("Very high resolution canopy height maps from RGB imagery using self-supervised vision transformer and convolutional decoder trained on Aerial Lidar"),这一信息在 geoai/canopy.py 的模块 docstring 中有明确记载。

从源码结构看,模型组件以 _ 前缀私有类的形式完整内嵌在模块内,无需额外依赖即可独立运行:

  • _SSLVisionTransformer:在 _DinoVisionTransformer 基础上按 out_indices(默认 (4, 11, 17, 23))输出四层中间特征,并支持 frozen_stages 冻结策略与任意尺寸输入的自适应 padding(_AdaptivePadding);
  • _DPTHead:包含 _ReassembleBlocks(多尺度特征重组)、_PreActResidualConvUnit(预激活残差卷积)与 _FeatureFusionBlock(特征融合 + 2 倍上采样),最终经 _HeadDepth 输出高度;
  • _SSLAE:将骨干与解码头组合成完整的自监督编码-解码模型,支持 huge=True(embed_dim=1280、32 层)与标准版(embed_dim=1024、24 层)两种规格。

推理前,输入图像会按模型训练时的统计量做归一化(geoai/canopy.py):

self._norm_mean = [0.420, 0.411, 0.296]
self._norm_std  = [0.213, 0.156, 0.143]

模型原始输出为归一化高度值,predict() 会乘以 scale_factor(默认 10.0)并经过 ReLU 截断(非负)后,换算成以米为单位的树冠高度(见 geoai/canopy.py)。

可用模型变体与选型建议

模块内置 4 个官方模型变体,定义于 geoai/canopy.py 的 MODEL_VARIANTS 常量中,可通过 list_canopy_models() 随时查询:

模型名称 文件大小 压缩 适用场景
compressed_SSLhuge(默认) 749M 是(INT8 动态量化) CPU 友好,通用性最佳
SSLhuge_satellite 2.9G 否 卫星影像效果最佳,需 GPU
compressed_SSLlarge 400M 是 CPU 友好的消融/轻量模型
compressed_SSLhuge_aerial 749M 是 在航拍影像上微调,适合 NEON/NAIP 等航拍数据

选型建议(与 Notebook 一致):航拍影像(NEON、NAIP)选 compressed_SSLhuge_aerial;卫星影像(如 Maxar)选 SSLhuge_satellite;通用场景默认 compressed_SSLhuge 即可。

需要说明的源码细节:压缩模型(compressed=True)的 checkpoint 存的是量化后的 state dict,加载时先在 CPU 上通过 torch.quantization.quantize_dynamic 得到 qint8 量化模型以对齐键名;若目标设备是 GPU,则会反量化出 float32 权重并装入全新浮点模型(geoai/canopy.py)。此外,checkpoint 加载统一使用 weights_only=True 限制反序列化,避免任意 Python 对象执行(安全性设计,见 geoai/canopy.py)。

安装与依赖

geoai.canopy 的硬性依赖为 PyTorch(torch / torchvision)与 rasterio,缺失时导入模块会直接抛出带安装提示的 ImportError(geoai/canopy.py):

pip install torch torchvision rasterio

GeoAI 本身对 canopy 相关导出采用惰性导入机制,geoai.canopy 只有在实际使用时才被加载(见 geoai/init.py 中 CanopyHeightEstimation、canopy_height_estimation、list_canopy_models 三个符号的懒加载注册),因此无需担心导入整个 geoai 包带来的额外开销。

模型 checkpoint 默认缓存到 ~/.cache/geoai/canopy/(即 DEFAULT_CACHE_DIR,geoai/canopy.py),首次使用时自动下载,已存在则直接复用,不会重复下载。

快速上手:完整预测流程

1. 导入与查看可用模型

import geoai
from geoai.canopy import CanopyHeightEstimation, list_canopy_models

models = list_canopy_models()
for name, desc in models.items():
    print(f"{name:30s} -> {desc}")

list_canopy_models() 返回 {模型名: 描述} 字典,可方便地核对各变体适用场景。

2. 获取示例数据

Notebook 使用 NEON 的 WLOU 森林站点数据:一张 RGB 航拍影像与一张对应的 LiDAR 真值 CHM,通过 geoai.download_file() 下载:

image_path = geoai.download_file("<RGB 影像 URL>")
chm_path   = geoai.download_file("<CHM 真值 URL>")

download_file 位于 geoai/utils/download.py,支持进度条显示、断点重名跳过(overwrite=False 时文件已存在则直接返回)与 ZIP 自动解压,适合在 Notebook 中交互式取数。

3. 初始化模型

# 航拍数据选 aerial 微调版;默认 checkpoint 首次使用自动下载(约 749 MB)
estimator = CanopyHeightEstimation(model_name="compressed_SSLhuge_aerial")

CanopyHeightEstimation 的构造参数(geoai/canopy.py)包括:

  • model_name:模型变体,见上文选型表;
  • checkpoint_path:本地 checkpoint 路径,传入后跳过自动下载(测试 TestCanopyInit 验证了该分支,见 tests/test_canopy.py);
  • device:'cpu'、'cuda'、'cuda:0' 等;不传则自动选择——CUDA 可用时用 cuda,否则退回 cpu;
  • cache_dir:checkpoint 缓存目录,默认 ~/.cache/geoai/canopy/。

4. 执行预测

output_path = "canopy_height_output.tif"
height_map = estimator.predict(
    image_path,
    output_path=output_path,
    batch_size=4,
)

predict() 返回形状为 (height, width) 的 2D numpy 数组,每个像素值即树冠高度(米)。predict 的完整参数(geoai/canopy.py):

参数 默认值 说明
input_path 必填 输入 RGB GeoTIFF 路径
output_path None 输出 GeoTIFF 路径;为 None 时仅返回数组
tile_size 256 瓦片尺寸,模型期望 256×256 输入
overlap 128 相邻瓦片重叠像素数,越大拼接越平滑但计算量越大;合法范围为 0 <= overlap < tile_size
batch_size 4 单批处理的瓦片数,越大越吃显存/内存但速度更快
scale_factor 10.0 模型输出换算为米的倍率,由原模型设定

5. 检查与可视化结果

print(f"Height map shape: {height_map.shape}")
print(f"Height range: {height_map.min():.2f} - {height_map.max():.2f} meters")
print(f"Mean height: {height_map.mean():.2f} meters")

visualize() 方法可一键生成对比图(geoai/canopy.py):

# 传入原图 + 高度数组:并排显示“输入影像 | 预测高度图”
fig = estimator.visualize(image_path, height_map, cmap="viridis")

# 只传入高度 GeoTIFF:单独显示高度图
fig = estimator.visualize(output_path, cmap="viridis")

常用参数:figsize=(16, 6)、cmap='viridis'(默认)、vmin=0、vmax(默认取高度正值的 98 分位数,抗离群值)、title 与 output_path(保存图件,dpi=150)。visualize 内部对 uint8/uint16/float 影像分别做归一化后再用 matplotlib 渲染,色条统一标注 "Height (meters)"。

6. 便捷函数:一行完成预测

对于一次性快速预测,可直接调用 canopy_height_estimation()(geoai/canopy.py),其内部等价于“创建实例 + predict”两步:

from geoai.canopy import canopy_height_estimation

height_map2 = canopy_height_estimation(
    image_path,
    output_path="canopy_height_output2.tif",
    model_name="compressed_SSLhuge_aerial",
)

大规模影像的瓦片化推理原理

面对大范围影像,predict() 并非整幅喂给模型,而是按 tile_size 切块、batch_size 分批推理后再无缝拼合。其实现逻辑(geoai/canopy.py)值得注意:

  1. 网格划分:step = tile_size - overlap,行/列数按 ceil((尺寸 - overlap) / step) 计算,并对影像边缘做零填充以对齐网格;
  2. 重叠加权混合:每个瓦片乘上一个 raised-cosine(升余弦)权重图 再累加——中心区域权重为 1,向边缘按余弦斜坡衰减到 0(_make_weight_map,geoai/canopy.py),重叠区加权平均后消除瓦片接缝伪影;
  3. 归一化平均:累加后除以权重计数,裁回原始尺寸输出。

_make_weight_map 的正确性在 tests/test_canopy.py 中有专门测试:中心为 1、四角接近 0、上下/左右对称、数值落在 [0,1] 区间、overlap<=0 时退化为全 1 矩阵。这些测试同时印证了输入归一化(_normalize_image)、checkpoint 安全加载(拒绝含 pickle 回调的对象)等关键路径的行为。

另外两个输入约束来自源码:

  • 波段要求:输入 GeoTIFF 必须至少 3 个波段,predict() 取前 3 个作为 RGB,否则抛出 ValueError;
  • 数值范围归一化:uint8 除以 255、uint16 除以 65535、float 数据若最大值 >1 则按最大值归一化,统一转换到 [0,1] 区间再送入模型。

输出 GeoTIFF 的格式细节

当指定 output_path 时,结果会写成保留原影像地理参考(profile 继承自输入)的单波段 GeoTIFF,属性更新为(geoai/canopy.py):

  • dtype="float32":高度值以 32 位浮点存储;
  • count=1:单波段输出;
  • compress="lzw":无损压缩,显著减小大区域成果文件体积;
  • nodata=0:无数据值标记为 0(与高度非负的输出特性一致);
  • 同时移除可能继承自 RGB 输入的 photometric(如 RGB)设置,避免写单波段图时出错。

这意味着预测结果可以直接在 QGIS、ArcGIS 等 GIS 软件中叠加原始影像查看,无需额外配准。

依赖声明与懒加载验证

geoai.canopy 在包级导出中注册了 3 个公开符号(geoai/init.py),通过 __getattr__ 惰性加载,避免导入 geoai 时即拉取 PyTorch/rasterio 重依赖。模块自身的 __all__ 导出(geoai/canopy.py)包含 CanopyHeightEstimation、canopy_height_estimation、list_canopy_models、MODEL_VARIANTS、DEFAULT_CACHE_DIR 五项,tests/test_canopy.py 对导入行为、函数签名、构造参数、未知模型报错等均有覆盖,可作为 API 契约的权威参考。

使用注意事项与限制

  • 默认 checkpoint 体积较大:首次使用 compressed_SSLhuge 需下载约 749 MB;请预留足够磁盘空间与网络带宽,下载失败时模块会清理部分文件并给出可手动下载的提示;
  • SSLhuge_satellite 需要 GPU:2.9G 全量模型在 CPU 上推理不现实,若机器无 CUDA 设备请改用压缩变体;
  • overlap 参数约束:overlap 必须满足 0 <= overlap < tile_size,否则 predict() 直接抛 ValueError;追求更平滑的拼接效果可在默认 128 基础上增大(上限 tile_size // 2 附近),代价是更多计算量;
  • 结果解读:输出单位为米,且经 ReLU 截断保证非负;与机载 LiDAR 真值 CHM 对比时,建议参照 Notebook 的做法按 98 分位数设置显示上限,避免极端值拉伸色带。

继续深入

登录后查看全文
geoai