GEE实战:用Sentinel-2 2A级数据做地表覆盖分类,完整代码与避坑指南
当我们需要监测森林砍伐、城市扩张或农作物生长时,地表覆盖分类是最基础也最关键的遥感应用之一。相比传统的1C级数据,Sentinel-2的2A级产品经过大气校正,反射率更接近地表真实情况,特别适合需要精确量化分析的场景。本文将带你从零开始,完成一个端到端的地表覆盖分类项目,过程中会特别关注那些容易被忽略但影响结果的关键细节。
1. 数据准备与预处理
1.1 选择合适的数据集
在GEE中调用2A级数据非常简单:
var s2 = ee.ImageCollection('COPERNICUS/S2_SR');但直接使用原始数据会遇到几个典型问题:
- 云遮挡:QA60波段包含云掩膜信息
- 缩放因子:反射率值被存储为整数需要除以10000
- 无效值:处理后的像素可能包含异常值
这里推荐一个经过实战检验的预处理函数:
function preprocessS2(image) { // 云掩膜 var qa = image.select('QA60'); var cloudBitMask = 1 << 10; var cirrusBitMask = 1 << 11; var mask = qa.bitwiseAnd(cloudBitMask).eq(0) .and(qa.bitwiseAnd(cirrusBitMask).eq(0)); // 应用缩放因子并裁剪有效范围 var scaled = image.select('B.*').divide(10000) .updateMask(mask) .clamp(0, 1); // 添加NDVI等常用指数 var ndvi = scaled.normalizedDifference(['B8', 'B4']).rename('NDVI'); return scaled.addBands(ndvi); }1.2 时空筛选策略
合理的筛选条件能显著提高数据质量:
| 筛选条件 | 推荐值 | 说明 |
|---|---|---|
| 时间范围 | 生长季(6-9月) | 植被特征明显 |
| 云量阈值 | <20% | 平衡数据量与质量 |
| 太阳高度角 | >30度 | 避免阴影影响 |
| 空间覆盖 | 研究区≥80% | 确保完整覆盖 |
实现代码示例:
var filtered = s2.filterDate('2022-06-01', '2022-09-30') .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20)) .filterBounds(geometry) .map(preprocessS2);2. 特征工程与样本设计
2.1 构建特征集
除了原始波段,建议添加这些衍生特征:
光谱指数:
- NDVI:植被健康度
- NDWI:水体检测
- NDBI:建筑指数
纹理特征:
- GLCM对比度
- 熵值
时序特征:
- 季度中值
- 季节性变化幅度
计算纹理特征的示例:
function addTexture(image) { var gray = image.select('B8').multiply(10000).toInt(); var glcm = gray.glcmTexture({size: 3}); return image.addBands(glcm.select('B8_contrast')); }2.2 样本采集技巧
样本质量决定分类上限,注意:
- 类别平衡:每类至少100个样本点
- 空间分布:均匀覆盖研究区
- 时间一致性:与影像日期匹配
- 混合像素:避开地类边界
创建样本的推荐方式:
var samples = ee.FeatureCollection([ // 森林样本 ee.Feature(geometry, {'class': 0}), // 水体样本 ee.Feature(geometry, {'class': 1}), // ...其他类别 ]);3. 模型训练与优化
3.1 随机森林实现
GEE内置的随机森林分类器简单高效:
var classifier = ee.Classifier.smileRandomForest(50) .train({ features: samples, classProperty: 'class', inputProperties: ['B2','B3','B4','B8','NDVI'] });关键参数调优建议:
| 参数 | 默认值 | 优化范围 | 影响 |
|---|---|---|---|
| 树数量 | 50 | 20-100 | 精度与效率平衡 |
| 变量数 | sqrt(特征数) | 特征数/3 | 防止过拟合 |
| 节点大小 | 5 | 1-10 | 模型复杂度 |
3.2 精度验证方法
避免常见的数据泄露问题:
- 空间分块验证:将研究区划分为网格
- 时间交叉验证:使用不同年份数据
- 独立验证集:30%样本留作验证
精度评估代码:
var validation = classified.sampleRegions({ collection: testSamples, properties: ['class'], scale: 10 }); var matrix = validation.errorMatrix('class', 'classification'); print('总体精度:', matrix.accuracy()); print('Kappa系数:', matrix.kappa());4. 后处理与结果优化
4.1 分类结果平滑
原始分类常存在"椒盐噪声",推荐使用:
- 众数滤波:3x3或5x5窗口
- 形态学操作:先腐蚀后膨胀
实现示例:
var smoothed = classified.focal_mode(3).rename('smoothed');4.2 不确定性分析
识别低置信度区域:
var probs = classified.select('.*probability'); var uncertainty = probs.reduce(ee.Reducer.max()).subtract(probs.reduce(ee.Reducer.sum()));4.3 结果导出策略
批量导出时注意:
- 分块处理:大区域切分网格
- 压缩优化:LZW或DEFLATE
- 元数据:添加分类说明
导出配置示例:
Export.image.toDrive({ image: classified, description: 'LandCover_2022', scale: 10, region: geometry, maxPixels: 1e13, fileFormat: 'GeoTIFF', formatOptions: { quality: 90 } });5. 典型问题解决方案
在实际项目中,这些坑我至少各踩过一次:
- DN值异常:2022年后1C级数据需要改用
S2_HARMONIZED集合 - 内存溢出:使用
batch()分批处理大数据量 - 投影偏差:统一使用EPSG:4326坐标系统
- 季节影响:冬季影像可能误判落叶林为裸土
一个实用的调试技巧是添加临时可视化:
Map.addLayer(classified.randomVisualizer(), {}, '临时分类结果');最后分享一个完整的端到端脚本框架:
// 1. 数据准备 var s2 = ee.ImageCollection('COPERNICUS/S2_SR'); var filtered = s2.filterDate(...).map(preprocessS2); // 2. 样本准备 var samples = ee.FeatureCollection([...]); // 3. 模型训练 var classifier = ee.Classifier.smileRandomForest(50).train(...); // 4. 分类应用 var composite = filtered.median(); var classified = composite.classify(classifier); // 5. 后处理 var smoothed = classified.focal_mode(3); // 6. 结果导出 Export.image.toDrive(...);