如何在Google Earth Engine中下载前识别并剔除MODIS LAI 8天产品的掩膜图像?
问题分析与解决方案
核心问题诊断
你的代码存在三个关键问题,导致所有影像被误判为有云且掩膜效果不符合预期:
- QC波段位运算逻辑错误:通过加法合并云状态位与LAI质量位,无法准确区分不同不良像素类型,还遗漏了云状态未设置、LAI未生成等不良情况。
- 未过滤低有效像素影像:仅对影像内像素进行掩膜,但未剔除那些大部分像素都被掩膜的影像,导致所有影像都被判定为"有云"。
- 不必要的
unmask()调用:下载前填充掩膜像素为0,破坏了原始NoData标识,可能不符合后续分析需求。
修正后的完整代码
1. 修正掩膜函数
# 修正后的MODIS LAI云/质量掩膜函数 def build_cloud_mask(image): qc_lai = image.select(['FparLai_QC']) # 提取LAI质量位(0-1位):仅保留质量为0(良好)的像素 quality_bits = qc_lai.bitwiseAnd(0b00000011) good_quality = quality_bits.eq(0) # 提取云状态位(3-4位):仅保留云状态为0(无云)的像素 cloud_state_bits = qc_lai.bitwiseAnd(0b00011000).rightShift(3) no_cloud = cloud_state_bits.eq(0) # 组合掩膜:同时满足良好质量和无云的像素才保留 final_mask = good_quality.And(no_cloud) return image.updateMask(final_mask)
2. 有效像素比例计算函数
# 计算影像中有效像素(未被掩膜)的比例 def calculate_unmasked_percent(image, aoi): # 统计AOI内未被掩膜的像素数量 unmasked_pixels = image.select('Lai_500m').mask().reduceRegion( reducer=ee.Reducer.sum(), geometry=aoi, scale=500, maxPixels=1e13 ).get('Lai_500m') # 统计AOI内总像素数量 total_pixels = ee.Image.pixelArea().divide(500*500).reduceRegion( reducer=ee.Reducer.sum(), geometry=aoi, scale=500, maxPixels=1e13 ).get('area') # 为影像添加有效像素比例属性 return image.set('unmasked_percent', ee.Number(unmasked_pixels).divide(total_pixels).multiply(100))
3. 主循环逻辑优化
# 时间窗口 start_dt = '2000-05-01' end_dt = '2023-12-31' for index, row in ads_sample.iterrows(): # 提取AOI坐标 corners = list(row['geometry'].exterior.coords) aoi = ee.Geometry.Polygon([list(coord) for coord in corners]) # 构建影像集并筛选夏季影像 modis_collection = ee.ImageCollection("MODIS/061/MOD15A2H").filterBounds(aoi).filterDate(start_dt, end_dt) summer_collection = modis_collection.filter(ee.Filter.calendarRange(6, 7, 'month')) # 应用掩膜并计算有效像素比例 modis_lai_masked = summer_collection.map(build_cloud_mask) modis_lai_with_percent = modis_lai_masked.map(lambda img: calculate_unmasked_percent(img, aoi)) # 筛选有效像素比例≥50%的影像(可根据需求调整阈值) filtered_collection = modis_lai_with_percent.filter(ee.Filter.gte('unmasked_percent', 50)) # 创建输出文件夹 ads_id = row['id'] folder_path = f'./output/images/{ads_id}' print(folder_path) try: os.makedirs(folder_path) except: print('文件夹已存在') # 获取筛选后的影像数量 num_images = filtered_collection.size().getInfo() image_list = filtered_collection.toList(num_images) # 下载每个影像 for item in range(num_images): filter_image = ee.Image(image_list.get(item)) # 选择需要的波段(移除unmask()以保留NoData) filter_band = filter_image.select(['Lai_500m', 'FparLai_QC']).clip(aoi) # 获取影像名称 im_name = str(filter_band.get('system:index').getInfo()) # 生成下载链接 try: url = filter_band.getDownloadURL({ 'scale': 500, 'filePerBand': False, 'region': aoi, 'crs': 'EPSG:26911', 'maxPixels': 1e13 }) print(url) # 可添加下载代码,例如: # import requests # response = requests.get(url) # with open(f'{folder_path}/{im_name}.tif', 'wb') as f: # f.write(response.content) except Exception as e: print(f'生成下载链接失败: {e}')
关键改进说明
- 精准位运算:分别判断LAI质量和云状态,确保所有不良像素(包括未生成、云状态未设置)都被正确掩膜。
- 有效像素筛选:通过计算每个影像在AOI内的有效像素比例,剔除无效像素过多的影像,解决"所有影像都有云"的误判问题。
- 保留NoData:移除
unmask()调用,保留原始掩膜的空值标识;若需填充特定值,可改为unmask(填充值)。 - 异常处理:为下载链接生成过程添加异常捕获,避免单个影像失败导致整个循环中断。
内容的提问来源于stack exchange,提问作者Anuruddha Marambe
相关产品推荐
相关产品推荐

