2026-06-03 CLCD数据处理

使用数据组git中的hangzhou-clcd-pipeline分支来处理

杭州 CLCD(中国土地覆盖)数据接入 OlmoEarth 流水线 — 运行指南

⚠️ CLCD 不是 USDA CDL:虽然名字相近,这是中国独立的 30m 土地覆盖产品。OlmoEarth 派生新 Modality.CLCD(olmoearth_pretrain/data/constants.py:472-485),完全不动现有 Modality.CDL(美国本土)。详见 CLAUDE.md "CLCD 中国土地覆盖数据集" 段。

⚠️ 本流水线与 SRTM / Landsat 完全独立:代码 / 脚本 / 测试 / 数据目录全部新建,不放在 hangzhou/ 下。可独立 revert,不影响前两个模态。


适用前提

  • 本地 .tif 文件:data/clcd_zhejiang/CLCD_v01_<year>_albert_<province>.tif 至少有一份(下载步骤见下)
  • Python 3.12 + uv(uv sync --locked --all-extras --python 3.12 完成)
  • 磁盘:本地样例 < 100 MB;生产 ≥ 2 GB(uint8 单波段,体积比 Landsat 小很多)
  • 不需要:AWS / 网络(纯本地数据源)

数据下载(用户手动)

  1. 浏览器打开 https://zenodo.org/records/12779975
  2. CLCD_v01_<year>_albert_province.zip(每年一个,~800 MB)
  3. 解压,在解压目录里找 CLCD_v01_<year>_albert_zhejiang.tif(~10 MB)
  4. 把它移到 data/clcd_zhejiang/:
    mkdir -p data/clcd_zhejiang
    mv ~/Downloads/CLCD_v01_2025_albert_province/CLCD_v01_2025_albert_zhejiang.tif data/clcd_zhejiang/
    
  5. 重复对 2024、2023 等年份(可选)

与 SRTM/Landsat 流水线的关系

流水线 原始数据 rslearn dataset OlmoEarth 输出 windows time Modality
SRTM data/srtm_hangzhou/ data/rslearn_hangzhou_srtm/ data/olmoearth_hangzhou_srtm/ 2020-01-01 Modality.SRTM
Landsat (S3 远程) data/rslearn_hangzhou_landsat/ data/olmoearth_hangzhou_landsat/ 2025-07-01 Modality.LANDSAT
本流水线 CLCD data/clcd_zhejiang/ data/rslearn_hangzhou_clcd/ data/olmoearth_hangzhou_clcd/ 2025-07-01 Modality.CLCD(新增)

零目录交集,可独立 revert。

资源预算

模式 数据量 时间(8 workers)
本地样例(1 window × 1 年) < 100 MB ~30 秒
生产全杭州 1 年(~5800 windows) ~50 MB 5-10 分钟

CLCD 是 uint8 单波段静态分类,体积只有 Landsat 的几百分之一。

类别码表(uint8,nodata=0)

类别 杭州常见占比
0 nodata (浙江省矩形外接区域)
1 耕地(cropland) ~16%
2 林地(forest) ~70%(杭州周边丘陵)
3 灌木(shrub) <1%
4 草地(grassland) <1%
5 水体(water) ~6%
6 雪/冰(snow/ice) 0%(亚热带)
7 裸地(barren) <1%
8 不透水面(impervious) ~8%(城市)
9 湿地(wetland) <1%

第 1 步 单元测试(确认环境)

uv run --extra all-no-flash pytest tests/unit/clcd/ -v

~16 个测试。如果 sample 数据未生成,2 个 sample-conditional 测试会 SKIPPED。

第 2 步 拉本地样例(开发用,~30 秒)

bash scripts/clcd/04_create_clcd_sample.sh

样例脚本和生产脚本结构完全一致(下面"第 3 步"逐步表),只是体量小:

  • 极小 bbox(西湖区中心 ~2 km × 2 km)→ 1 个 window
  • 默认年份 2025
  • 输出在 data/rslearn_hangzhou_clcd_sample/data/olmoearth_hangzhou_clcd_sample/

样例就绪后,跑 unit test 全 PASS:

uv run --extra all-no-flash pytest tests/unit/clcd/ -v

第 3 步 全杭州流程

一键脚本(推荐):把 3.1 → 3.5 串起来跑,断点续跑,失败可直接重跑:

bash scripts/clcd/03_run_clcd_production.sh

自定义 workers / 年份 / 磁盘门槛 / 数据目录:

WORKERS=4 \
YEAR=2024 \
MIN_FREE_GB=1 \
CLCD_DIR=/data/raw/clcd \
RSLEARN_DS=/scratch/rslearn_clcd \
bash scripts/clcd/03_run_clcd_production.sh

可 override 的环境变量:YEAR / WORKERS / MIN_FREE_GB / CLCD_DIR(原始 .tif 目录) / RSLEARN_DS(rslearn 中间产物目录) / WINDOWS_JSON(默认 hangzhou_olmoearth_windows.json,几乎不需要改)。OLMOEARTH_DS 自动按 YEAR 拼后缀,不直接 override。

我执行的实际脚本:

WORKERS=4 \
YEAR=2024 \
MIN_FREE_GB=1 \
CLCD_DIR=/mnt/si000082vai5/default/Data/clcd_zhejiang \
RSLEARN_DS=/mnt/si000082vai5/default/Person/kongfanqiang/olmoearthData/olmoearth_pretrain_hz/data/rslearn_hangzhou_clcd \
bash scripts/clcd/03_run_clcd_production.sh

结果保存在data/olmoearth_hangzhou_clcd/目录下,此时csv文件还没有合并

如果想了解每一步具体在做什么,或想分步排查,继续看下面 3.1-3.5 手动版本。

3.1 准备 rslearn dataset 配置

# 先刷 file_list.json(扫 data/clcd_zhejiang/ 列出所有年份 .tif)
bash scripts/clcd/01_setup_clcd_mirror.sh

# 再渲染 config(把 LOCAL_ROOT_PLACEHOLDER 替换为 clcd_zhejiang 绝对路径)
sed "s|LOCAL_ROOT_PLACEHOLDER|$(pwd)/data/clcd_zhejiang|" \
    data/rslearn_hangzhou_clcd/config.template.json \
    > data/rslearn_hangzhou_clcd/config.json

预期:data/clcd_zhejiang/file_list.json 列出可用年份,data/rslearn_hangzhou_clcd/config.json 出现且 class_path 指向 LocalCLCD

3.2 生成杭州 res_10 windows

python scripts/clcd/02_create_clcd_windows.py \
  --ds_path data/rslearn_hangzhou_clcd \
  --year    2025

Windows 来自 repo 根目录的 hangzhou_olmoearth_windows.json(无需手动指定)。

预期:输出末尾 Created 6272 windows under data/rslearn_hangzhou_clcd/windows/res_10/。每 window 目录(如 EPSG:32651_10_88_-1309/)内有 metadata.json,window time = 2025-07-01T00:00:00Z

3.3 rslearn 三连(prepare + ingest + materialize)

与 Landsat 不同,CLCD 走完整三连(包括 ingest)——本地数据源,无需 wrapper,无 AWS。

export DS=./data/rslearn_hangzhou_clcd
export WORKERS=8

rslearn dataset prepare    --root $DS --group res_10 --workers $WORKERS
rslearn dataset ingest     --root $DS --group res_10 --workers $WORKERS --no-use-initial-job
rslearn dataset materialize --root $DS --group res_10 --workers $WORKERS --no-use-initial-job

预期:

  • prepare 后,每 window 目录下 items.json 出现(LocalCLCD 命中的年份 item)
  • ingest 后,本地 .tif 被注册到 rslearn tile_store
  • materialize 后,每 window 目录下 layer_clcd/0/clcd/geotiff.tif 出现(已重投影到 UTM,uint8)
  • 全 6,272 windows 大约 5-10 分钟跑完

与 JSON 清单对账

确认 materialize 后的 rslearn dataset 与 hangzhou_olmoearth_windows.json 完全一致(名字 + projection + bounds + 每个 window 都有非空 .tif):

uv run --extra all-no-flash python scripts/hangzhou/verify_windows_against_json.py \
  --ds_path data/rslearn_hangzhou_clcd \
  --require-materialized

预期:OK: 6272 windows match JSON exactly (names + projection + bounds), all materialized。该脚本三模态共用(SRTM / Landsat / CLCD 都跑同一份 JSON)。

3.4 跑 clcd_to_olmoearth.py 转 OlmoEarth 单文件格式

python -m olmoearth_pretrain.dataset_creation.clcd.clcd_to_olmoearth \
  --ds_path        $DS \
  --olmoearth_path ./data/olmoearth_hangzhou_clcd \
  --workers        $WORKERS

预期:data/olmoearth_hangzhou_clcd/10_clcd/ 目录出现,内含 <crs>_<col>_<row>_10.tif 文件(uint8,shape=(1, 256, 256))。每 window 一份(全 nodata 的 window 被自动过滤,所以总数 ≤ 5,800)。

3.5 抽样验证产出

python scripts/clcd/verify_clcd.py \
  --olmoearth_path data/olmoearth_hangzhou_clcd \
  --sample_n 10

预期:抽 10 个 .tif 全部 (dtype=uint8,CRS=EPSG:32650/32651,类别值在 [0, 9],像素非全 0),最后一行 OK: sampled CLCD outputs look healthy,并打印类别分布(应见耕地/林地/水体/不透水占主导)。


多年扩展(用户自助)

后续下到 2023、2024:

mv ~/Downloads/CLCD_v01_2023_albert_zhejiang.tif data/clcd_zhejiang/
mv ~/Downloads/CLCD_v01_2024_albert_zhejiang.tif data/clcd_zhejiang/

YEAR=2023 bash scripts/clcd/03_run_clcd_production.sh                    # 跑 2023
YEAR=2024 bash scripts/clcd/03_run_clcd_production.sh                    # 跑 2024

得到 data/olmoearth_hangzhou_clcd_2023/data/olmoearth_hangzhou_clcd_2024/(脚本按 --year 拼后缀;YEAR=2025 默认不加后缀)。

每跑一个年份得到一套 OlmoEarth 输出。下游训练把它们当成不同 (location, year) 样本即可,符合 Modality.CLCD 的 single-year 模式。


中间目录详解(rslearn 阶段)

data/rslearn_hangzhou_clcd/
├── config.template.json                 [入仓]   1 layer 模板,含 LOCAL_ROOT_PLACEHOLDER
├── config.json                          [生成]   3.1 sed 替换占位符后的实际 config
└── windows/
    ├── res_10/                          [生成]   主 group
    │   └── EPSG:32651_<col>_<row>/      ← 每个 window 一个目录
    │       ├── metadata.json            [3.2]   bounds + projection + time_range
    │       ├── items.json               [3.3a]  prepare 写:LocalCLCD 命中的 year 列表
    │       └── layer_clcd/              [3.3c]  materialize 写
    │           └── 0/                   ← 唯一 group_idx(static modality)
    │               └── clcd/            ← bandset = ["clcd"]
    │                   └── geotiff.tif  ← uint8,256×256,UTM 投影
    └── res_160/                         [生成]   3.2 自动派生粗分辨率 windows

data/clcd_zhejiang/                     [入仓白名单 file_list.json,其余原始 .tif 不入仓]
├── CLCD_v01_2025_albert_zhejiang.tif    [用户下]  ~10 MB
├── CLCD_v01_2024_albert_zhejiang.tif    [用户下,可选]
├── CLCD_v01_2023_albert_zhejiang.tif    [用户下,可选]
└── file_list.json                       [3.1]    JSON 数组列举可用 .tif basenames

输出目录详解(OlmoEarth 训练格式,最终交付物)

data/olmoearth_hangzhou_clcd/            (YEAR=2025 时无后缀;其它年份带 _<year> 后缀)
├── 10_clcd/                             [3.4]   静态层(TimeSpan.STATIC)
│   └── EPSG:32651_<col>_<row>_10.tif    ← shape=(1, 256, 256), uint8, 类别码 0-9
└── 10_clcd_meta/                        [3.4]   每 window 一个临时 CSV
    └── EPSG:32651_10_<col>_<row>.csv    ← image_idx + tile_time + start/end_time

关键约定:

  • .tif 文件名格式 <crs>_<col>_<row>_<resolution>.tif,由 olmoearth_pretrain/dataset/utils.py:get_modality_fname 决定
  • 单波段:形状第 0 维恒为 1
  • TimeSpan.STATIC → modality 目录名无 _freq / _monthly 后缀
  • 任何含 nodata=0 像素的 window 被 clcd_to_olmoearth 自动跳过(沿用 cdl.pyif image.min() == 0: return 约定),不出现在输出里
  • 多年版本通过 OlmoEarth 输出根目录区分(olmoearth_hangzhou_clcd_2023/ vs _2024/ vs 默认 2025)

第四步 合并csv

执行命令:

python -m olmoearth_pretrain.dataset_creation.make_meta_summary \
  --olmoearth_path /mnt/si000082vai5/default/Person/kongfanqiang/olmoearthData/olmoearth_pretrain_hz/data/olmoearth_hangzhou_clcd \
  --modality clcd

输出体量复核

du -sh data/olmoearth_hangzhou_clcd/                     # 期望 ~50 MB(uint8 + 5800 windows)
find data/olmoearth_hangzhou_clcd -name '*.tif' | wc -l  # 期望接近 5,800(去掉全-nodata window 后)

故障排查

没有 .tif 文件

ERROR: no CLCD_v01_<year>_albert_*.tif found under data/clcd_zhejiang

→ 还没下数据。从 Zenodo 下载,见上面"数据下载"。

LocalCLCD: no CLCD_v01_<year>_albert_*.tif found

→ 文件名不对。本流水线只识别精确格式 CLCD_v01_<year>_albert_<province>.tif(全小写省份)。如果 zip 解压出了别的命名,改回标准。

materialize 全部 window 都 failed

→ 检查 data/clcd_zhejiang/file_list.json 是否过期(把当前年份的 .tif 列进去)。重跑 bash scripts/clcd/01_setup_clcd_mirror.sh

输出 .tif 数远小于 windows 数

→ 正常。CLCD 在浙江省矩形之外大量 nodata(Albers 矩形 - 浙江多边形)。这些 window 被 clcd_to_olmoearthif image.min() == 0 过滤掉。如果想保留,可以改 converter 逻辑,但语义上 nodata 不应进训练。

想换省份

默认本流水线扫描 data/clcd_zhejiang/,即"杭州在浙江,只用浙江省级文件"。换其它省份(如 jiangsu):

mkdir -p data/clcd_jiangsu
mv ~/Downloads/CLCD_v01_2025_albert_jiangsu.tif data/clcd_jiangsu/
CLCD_DIR=$(pwd)/data/clcd_jiangsu bash scripts/clcd/03_run_clcd_production.sh   # 但脚本里 CLCD_DIR 写死,要改 03 脚本

清理 / 回滚

# 仅清 OlmoEarth 输出(rslearn 中间产物保留,可继续用)
rm -rf data/olmoearth_hangzhou_clcd data/olmoearth_hangzhou_clcd_*

# 全清(包括 rslearn dataset 和 windows)
rm -rf data/rslearn_hangzhou_clcd data/olmoearth_hangzhou_clcd*

# 再清样例
rm -rf data/rslearn_hangzhou_clcd_sample data/olmoearth_hangzhou_clcd_sample

.gitignore 已挡住,这些目录都不入仓,可放心删。原始 .tif (data/clcd_zhejiang/*.tif) 也不入仓,但删了得重新下,慎删。

本任务的边界

本指南只到 OlmoEarth 标准格式 CLCD 切片为止。后面还需要:

  • norm stats 计算(分类不需要 z-score normalize,直接 one-hot 或 embedding,代码侧适配)
  • 训练侧把 CLCD 当 modality / 当 finetune target,需改 evals/ 或 dataloader
  • USDA CDL 不能跟 CLCD 在同一 batch 融合(类别不互通,需独立 head)

这些不在本流水线范围

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容