☰
GEE高分辨率LST反演:源码解析与工程实践避坑指南
2026/10/17 18:48:23 网站建设 项目流程

简介:这份源码资源面向遥感、地理信息与生态环境方向的开发者与研究者,聚焦GEE平台下高分辨率地表温度反演这一典型难题。针对高分辨率热红外数据稀缺的痛点,方案融合Landsat 8与Sentinel-2多源数据,借助随机森林回归建模与残差融合技术,将30米LST降尺度至10米,覆盖数据加载、预处理、指数计算、模型训练、残差处理到结果验证的完整链路,可服务于城市热岛分析与生态监测等场景。资源包共4个文件,以JavaScript脚本为核心,辅以HTML页面、inscode配置与gitignore文件,整体约9KB,结构轻量、便于直接运行与二次修改。目前已有170人学习下载,适合希望快速上手GEE降尺度反演、理解多源数据融合思路并复用建模流程的读者参考。

1. GEE高分辨率LST反演:从源码到落地的完整拆解

做热红外遥感的人大多经历过这个场景:手头只有Landsat 8/9 的 TIRS 波段,想反演出一张 30 米地表温度(LST)图,结果卡在辐射定标、大气校正、比辐射率估算这几步上,本地 ENVI 跑一遍要半天,换景数据还得重来。GEE 高分辨率 LST 反演源码解决的正是这件事——把整条链路搬到云端,用 JavaScript 或 Python API 调 Landsat、MODIS 数据,在线完成亮温计算、NDVI 阈值比辐射率、单窗/劈窗算法,最后导出 GeoTIFF。它适合做城市热岛、农业旱情、地表能量平衡的从业者,也适合想用 GEE 教程快速上手遥感云计算的开发者。源码本身不复杂,难的是参数怎么设、云怎么去、结果怎么验。

2. 反演链路拆解:从亮温到地表温度的每一步

2.1 为什么不能直接拿 TIRS 波段当 LST 用

Landsat 8/9 的 TIRS 波段记录的是星上辐射亮度,不是温度。传感器接收到的能量经过大气衰减、地表比辐射率调制,直接换算出来的只是亮温(Brightness Temperature),和真实地表温度差 3~8 K 是常事。反演的核心就是把亮温还原成地表温度,中间要过三关:辐射定标、大气校正、比辐射率校正。

辐射定标把 DN 值转成辐亮度,这一步 GEE 已经用TOA_REFLECTANCE和TOA_RADIANCE预处理好了,直接调LANDSAT/LC08/C02/T1_L2集合里的ST_B10波段就是亮温,单位开尔文,缩放因子 0.00341802,偏移 149.0。很多人不知道这个偏移量,直接乘缩放因子,结果整张图偏了 149 K,这就是典型的翻车现场。

大气校正决定反演精度上限。单窗算法需要大气透过率、大气平均作用温度、大气水汽含量三个参数,GEE 里没有现成的大气廓线数据,常见做法是用 MODIS 大气产品(MOD07_L2)或者 NCEP/NCAR 再分析数据估算。源码里一般会封装一个atmosphericCorrection()函数,输入水汽含量和气温,输出透过率和平均作用温度。

比辐射率校正最容易被忽略。地表不是黑体,不同地物的比辐射率从 0.90 到 0.99 不等,裸土和植被差得尤其多。NDVI 阈值法是工程上最常用的方案:NDVI < 0.2 算裸土,比辐射率取 0.97;NDVI > 0.5 算全植被,取 0.99;中间用植被覆盖度做线性插值。这套逻辑在源码里通常就是一个emitNDVI()函数,二十行代码,但参数改一点结果就差一截。

2.2 单窗算法和劈窗算法怎么选

单窗算法(Qin et al., 2001)只需要一个热红外波段,Landsat 8 的 TIRS 波段 10 就能跑,适合只有单波段的情况。公式不复杂:

// 单窗算法核心计算,输入亮温、比辐射率、大气参数 function monoWindow(bt, emissivity, tau, Ta) { // bt: 亮温(K), emissivity: 比辐射率, tau: 大气透过率, Ta: 大气平均作用温度(K) var a = -67.355351; var b = 0.458606; var C = emissivity * tau; var D = (1 - tau) * (1 + (1 - emissivity) * tau); var LST = (a * (1 - C - D) + (b * (1 - C - D) + C + D) * bt - D * Ta) / C; return LST; // 返回地表温度(K) }

这段代码里a和b是经验系数,来自 Qin 的论文,针对 TIRS 波段 10 标定。tau和Ta需要外部输入,tau一般取 0.6~0.9,Ta用近地表气温加 2~3 K 估算。参数说明:bt从ST_B10波段直接取,emissivity由 NDVI 阈值法算,tau和Ta是精度瓶颈,有条件的用 MOD07 反演,没条件的用经验值也能跑,但误差会到 2~3 K。

劈窗算法(Wan & Dozier, 1996)用两个热红外波段,Landsat 8 的波段 10 和 11 理论上可以,但 TIRS 波段 11 的定标漂移问题严重,USGS 建议 2022 年后不要用波段 11 做定量反演。所以实际项目中,Landsat 8/9 基本走单窗,MODIS 走劈窗。源码里如果同时封装了两套算法,选单窗更稳。

2.3 源码结构:一个可复用的 GEE LST 反演模块

一份能跑的 GEE LST 反演源码通常包含四个部分:数据加载、云掩膜、反演计算、导出。下面是一个最小可复现的骨架:

// GEE LST反演主流程,适用于Landsat 8/9 Collection 2 Level 2 var roi = ee.Geometry.Rectangle([116.0, 39.5, 116.8, 40.2]); // 北京五环区域 // 1. 加载Landsat 8 L2数据,筛选时间和云量 var landsat = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .filterDate('2023-06-01', '2023-08-31') .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER', 10)); // 2. 云掩膜,用QA_PIXEL波段 function maskClouds(image) { var qa = image.select('QA_PIXEL'); var cloud = qa.bitwiseAnd(1 << 3).eq(0); // 云阴影 var cloudBit = qa.bitwiseAnd(1 << 4).eq(0); // 云 return image.updateMask(cloud.and(cloudBit)); } // 3. 计算NDVI和比辐射率 function addEmissivity(image) { var ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI'); var em = ndvi.expression( 'ndvi < 0.2 ? 0.97 : (ndvi > 0.5 ? 0.99 : 0.97 + (0.99 - 0.97) * (ndvi - 0.2) / 0.3)' ).rename('EM'); return image.addBands(ndvi).addBands(em); } // 4. 单窗算法反演LST function retrieveLST(image) { var bt = image.select('ST_B10').multiply(0.00341802).add(149.0); // 亮温 var em = image.select('EM'); var tau = 0.75; // 大气透过率,经验值 var Ta = 300; // 大气平均作用温度,经验值 var lst = monoWindow(bt, em, tau, Ta).rename('LST'); return image.addBands(lst); } // 5. 批量处理并导出 var result = landsat.map(maskClouds).map(addEmissivity).map(retrieveLST); var lstImage = result.select('LST').mean().clip(roi); Export.image.toDrive({ image: lstImage, description: 'LST_Beijing_2023', scale: 30, region: roi, maxPixels: 1e13 });

逻辑说明:第一步筛选时间和区域,云量阈值 10% 是经验值,城市区域可以放宽到 20%。第二步云掩膜用 QA_PIXEL 的 bit 3 和 bit 4,分别对应云阴影和云,bitwiseAnd后取反就是无云像元。第三步 NDVI 用 SR_B5 和 SR_B4,比辐射率按 NDVI 分段插值。第四步亮温转换用官方缩放因子和偏移量,单窗算法调用前面的函数。第五步取时间序列均值,导出到 Drive。

参数说明:scale设 30 米对应 Landsat 分辨率,maxPixels设 1e13 防止大区域导出失败。tau和Ta是经验值,实际项目中建议用 MOD07 或 ERA5 替换。如果研究区水汽变化大,固定tau会引入 1~2 K 误差。

3. 避坑与排查:LST反演中最容易翻车的五个点

3.1 亮温值偏了 149 K

现象:导出的 LST 图整张偏暖,数值在 400 K 以上。原因:Landsat Collection 2 Level 2 的ST_B10波段已经做了缩放,但偏移量 149.0 容易漏加。解决:确认代码里multiply(0.00341802).add(149.0)两步都写了,缺一不可。Collection 1 的偏移量不同,混用会出错。

3.2 云掩膜后影像全黑

现象:maskClouds之后影像没有有效像元。原因:QA_PIXEL 的 bit 位搞反了,bitwiseAnd(1 << 3)返回的是云阴影像元,eq(0)取反后把无云像元也掩掉了。解决:先打印 QA_PIXEL 的直方图,确认 bit 3 和 bit 4 的分布,再决定取反逻辑。或者直接用ee.Algorithms.Landsat.simpleCloudScore()做快速云掩膜。

3.3 比辐射率全图一个值

现象:NDVI 计算出来全是负值或全大于 0.5。原因:SR_B5 和 SR_B4 的波段名写错,或者用了 TOA 反射率没做大气校正。解决:确认波段名是SR_B5和SR_B4,不是B5和B4。如果 NDVI 异常,先检查normalizedDifference的输入顺序,近红外在前,红波段在后。

3.4 导出任务卡在 0%

现象:Export 任务提交后一直排队。原因:maxPixels设太小,或者region范围太大。解决:maxPixels设 1e13,region用roi.bounds()而不是roi。如果还是卡,分块导出,按月份或按条带号拆分。

3.5 单窗算法结果比实测高 5 K

现象:LST 和地面站实测差 5 K 以上。原因:tau和Ta用了默认经验值,没有按研究区调整。解决:用 MOD07_L2 反演大气水汽含量,再算透过率;Ta用 ERA5 的近地表气温加 2 K。如果没条件,至少按季节调整,夏季tau取 0.7,冬季取 0.85。

4. 进阶技巧:用 Python API 批量跑多年 LST 并做趋势分析

GEE 的 JavaScript 代码编辑器适合调试,但批量处理多年数据、做趋势分析,Python API 更顺手。下面是一个用geemap和xarray跑 2013~2023 年 LST 并算趋势的流程:

import ee import geemap import xarray as xr import numpy as np ee.Initialize() # 定义研究区和时间范围 roi = ee.Geometry.Rectangle([116.0, 39.5, 116.8, 40.2]) years = list(range(2013, 2024)) def get_lst(year): start = f'{year}-06-01' end = f'{year}-08-31' landsat = (ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .filterDate(start, end) .filterBounds(roi) .filter(ee.Filter.lt('CLOUD_COVER', 10))) # 云掩膜和LST反演函数同上,此处省略 lst = landsat.map(maskClouds).map(addEmissivity).map(retrieveLST) return lst.select('LST').mean().clip(roi) # 逐年的LST均值 lst_collection = ee.ImageCollection([get_lst(y) for y in years]) # 导出为xarray,做线性趋势 lst_np = geemap.ee_to_numpy(lst_collection, region=roi, scale=1000) lst_da = xr.DataArray(lst_np, dims=['year', 'y', 'x'], coords={'year': years}) trend = lst_da.polyfit(dim='year', deg=1) print(trend['polyfit_coefficients'].sel(degree=1)) # 升温速率 K/yr

逻辑说明:get_lst函数按年筛选、掩膜、反演,返回单年夏季均值。ee.ImageCollection把多年结果拼成时间序列,geemap.ee_to_numpy转成 numpy 数组,再用xarray的polyfit算线性趋势。scale=1000是为了降低分辨率加快导出,做趋势分析够用。

参数说明:deg=1是一阶线性拟合,输出斜率就是升温速率,单位 K/yr。如果要做显著性检验,用scipy.stats.linregress逐像元算 p 值。注意ee_to_numpy对大区域会超内存,建议分块或降分辨率。

一个我踩过的坑:GEE 的ee_to_numpy默认返回 masked array,直接喂给xarray会报错,得先.filled(np.nan)。另外 Landsat 8 是 2013 年发射的,2013 年之前的数据得用 Landsat 7,但 Landsat 7 的条带问题严重,做趋势分析时要么补 Landsat 5,要么从 2013 年开始。

做 LST 反演这些年,最大的教训是别迷信源码里的默认参数。tau和Ta这两个值,换个研究区、换个季节,结果能差 3 K。我现在的习惯是每换一个区域,先用 MOD07 跑一遍大气参数,再和地面站比对,确认误差在 1.5 K 以内才批量跑。源码是起点,不是终点。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询