一、前言


GEE官方提供了一个云概率数据集 【点击链接可跳转至详情网页】,该数据集中仅有一个波段

在这里插入图片描述
其中,针对该波段的官方描述是这样的:

S2 cloud probability 是使用 Sentinel2-cloud- detector 库(使用 LightGBM)创建的。在应用梯度增强基础算法之前,所有频段均使用双线性插值上采样至 10m 分辨率。生成的 0…1 浮点概率缩放为 0…100 并存储为 UINT8。缺少任何或所有条带的区域被遮盖掉。较高的值更有可能是云或高反射表面(例如屋顶或雪)。


二、GEE利用 云概率数据集 对哨兵影像进行去云

传统的 QA60 波段位运算去云函数

 function maskS2clouds(image) {
  var qa = image.select('QA60');

  // Bits 10 and 11 are clouds and cirrus, respectively.
  var cloudBitMask = 1 << 10;
  var cirrusBitMask = 1 << 11;

  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
      .and(qa.bitwiseAnd(cirrusBitMask).eq(0));

  return image.updateMask(mask).divide(10000);
}

为了进一步去除细碎云、细小云可进一步利用 S2 cloud probability 数据集进行去云操作

//提取云概率波段的函数,并制作掩膜数据
function rmcloudByProbability(image,thread){
  var prob = image.select("probability");
  return image.updateMask(prob.lte(thread));
}

//数据集合并函数
function getMergeImages(primary, secondary){
  var join = ee.Join.inner();
  var filter = ee.Filter.equals({
    leftField: "system:index",
    rightField: "system:index",
  });
  var joinCol = join.apply(primary, secondary, filter);
  joinCol = joinCol.map(function(image){
    var img1 = ee.Image(image.get("primary"));
    var img2 = ee.Image(image.get("secondary"));
    return img1.addBands(img2);
  });
  return ee.ImageCollection(joinCol);
}

//设定时间间隔 
var dates=['2023-01-11','2023-01-30'];
var startDate = ee.Date(dates[0]);
var endDate = ee.Date(dates[1]);

var S2_images1 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
                  .filterDate(startDate,endDate)
                  .filterBounds(roi);
var S2_images2 = ee.ImageCollection("COPERNICUS/S2_CLOUD_PROBABILITY")
                  .filterDate(startDate,endDate)
                  .filterBounds(roi);
var S2_datasets =  getMergeImages(S2_images1,S2_images2);

S2_datasets = S2_datasets.map(function(image){
   return rmcloudByProbability(image,20);//数字越小越严格
 })

根据我的具体需求,在撰写过程中,我在数据集的操作中添加了指数计算。主函数的操作逻辑如下:

  1. 先利用 (‘CLOUDY_PIXEL_PERCENTAGE’,20) 操作对不超过20%云量的数据进行初筛,并进行批量裁剪

  2. 然后在进行完一系列指数计算后,利用 云概率数据集 S2 cloud probability 进行第二次掩膜去云

//针对影像集中的每一张影像裁剪ROI区域、计算NDVI、导出相应波段
var image_collection = ee.ImageCollection(S2_datasets)
                      .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',20))//数字越小越严格
                      .map(clip)
                      .map(get_NDWI)
                      .map(get_NDBI)
                      .map(get_NDVI)
                      .map(function(image){
                          return rmcloudByProbability(image,20);//数字越小越严格
                      });         

三、GEE批量导出函数 实现对Sentinel-2数据 相同日期影像 合并成一张的操作

在研究区域过大时,同一天内包含不同块大小的影像,可直接进行合并操作

在具体的实现逻辑中,我们首先要解决 如何识别是同一天的影像。GEE中哨兵数据的文件结构中 属性 system:index 的前31位完全相同时,则判定他们是同一天的

  1. 先将数据集合中所有的文件属性存储起来,并按照属性 system:index 前缀31个字符来进行进行 if 判定操作

  2. 判定后的影像序列 存放在一个影像集合中,利用最大值合成进行合并(因为哨兵是对同一天的影像进行切割分块存储的,所以这里合成方式并不影响实际存储的数值

  3. 最后利用导出函数进行导出

详细代码如下:

function exportImageCollection(imageCol) {
  var indexList = imageCol.reduceColumns(ee.Reducer.toList(), ["system:index"]).get("list");
  indexList.evaluate(function(indexs) {
    // 使用一个对象存储同一天的影像
    var groupedImages = {};
    
    // 遍历索引列表,将影像按日期分组
    for (var i = 0; i < indexs.length; i++) {
      var prefix = indexs[i].slice(0, 31);
      if (!groupedImages[prefix]) {
        groupedImages[prefix] = [];
      }
      groupedImages[prefix].push(indexs[i]);
    }
    
    // 遍历分组影像,导出最大影像
    for (var key in groupedImages) {
      var imagesToExport = imageCol.filter(ee.Filter.stringStartsWith('system:index', key));
      var image = imagesToExport.max();
      image = image.divide(10000).toFloat();
      
      Export.image.toDrive({
        image: image.clip(roi), //clip()可要可不要,没有影响
        description: key,
        fileNamePrefix: "S2_" + key,
        folder: 'S2_daily_image ',
        region: roi,
        scale: 10,
        crs: "EPSG:4326",
        maxPixels: 1e13
      });
    }
  });
}

细节解释
在这里插入图片描述


四、GEE完整代码 (可直接复制运行)

提示:在GEE中运行时,需设置自己所需的 roi 研究区范围


Map.centerObject(roi, 10);

//这里设置了clip的函数操作,通过map操作实现对所有影像的裁剪
function clip(image){
  var clipimage = image.clip(roi);
  return clipimage;
}                  

//NDVI计算函数 sentinel used B8 and B4
function get_NDWI(image) {
  var NDWI = image.normalizedDifference(['B3', 'B8']).rename(['NDWI']);
  return image.addBands(NDWI);
}

//NDVI计算函数 sentinel used B8 and B4
function get_NDBI(image) {
  var NDBI = image.normalizedDifference(['B11', 'B8']).rename(['NDBI']);
  return image.addBands(NDBI);
}

//NDVI计算函数 sentinel used B8 and B4
function get_NDVI(image) {
  var NDVI = image.normalizedDifference(['B8','B4']).rename(['NDVI']);
  return image.addBands(NDVI);
}

//提取云概率波段的函数,并制作掩膜数据
function rmcloudByProbability(image,thread){
  var prob = image.select("probability");
  return image.updateMask(prob.lte(thread));
}
//数据集合并函数
function getMergeImages(primary, secondary){
  var join = ee.Join.inner();
  var filter = ee.Filter.equals({
    leftField: "system:index",
    rightField: "system:index",
  });
  var joinCol = join.apply(primary, secondary, filter);
  joinCol = joinCol.map(function(image){
    var img1 = ee.Image(image.get("primary"));
    var img2 = ee.Image(image.get("secondary"));
    return img1.addBands(img2);
  });
  return ee.ImageCollection(joinCol);
}

//设定时间间隔 
var dates=['2023-01-11','2023-01-30'];
var startDate = ee.Date(dates[0]);
var endDate = ee.Date(dates[1]);

var S2_images1 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
                  .filterDate(startDate,endDate)
                  .filterBounds(roi);
var S2_images2 = ee.ImageCollection("COPERNICUS/S2_CLOUD_PROBABILITY")
                  .filterDate(startDate,endDate)
                  .filterBounds(roi);
//数据集的合并操作
var S2_datasets =  getMergeImages(S2_images1,S2_images2);
print("数据集合",S2_datasets)

//针对影像集中的每一张影像裁剪ROI区域、计算NDVI等指数、并进行去云操作
var image_collection = ee.ImageCollection(S2_datasets)
                      .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',20))//数字越小越严格
                      .map(clip)
                      .map(get_NDWI)
                      .map(get_NDBI)
                      .map(get_NDVI)
                      .map(function(image){
                          return rmcloudByProbability(image,20);//数字越小越严格
                      });

print('Sentinel2_Image_Collection',image_collection)

//真彩色合成显示
var visualization1 = {
  min: 0,
  max: 3000,
  bands: ['B4', 'B3', 'B2'],
};

Map.addLayer(image_collection.median(), visualization1, 'RGB')

// 使用下列波段作为特征
var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8','B11','B12','NDWI','NDBI','NDVI'];

var data_bands = ee.ImageCollection(image_collection).select(bands);
print("dataset_bands",data_bands)

var visParam = {
  min: -0.2,
  max: 1.0,
  palette: [
    'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301'
  ],
};

Map.addLayer(data_bands.select("NDVI"), visParam, "ndvi_data");

//单张数据的下载和导出
Export.image.toDrive({
  image:data_bands,
  description: '2024_03',
  folder: '2024_images',
  scale: 10,
  region:roi
});

function exportImageCollection(imageCol) {
  var indexList = imageCol.reduceColumns(ee.Reducer.toList(), ["system:index"]).get("list");
  indexList.evaluate(function(indexs) {
    // 使用一个对象存储同一天的影像
    var groupedImages = {};
    
    // 遍历索引列表,将影像按日期分组
    for (var i = 0; i < indexs.length; i++) {
      var prefix = indexs[i].slice(0, 31);
      if (!groupedImages[prefix]) {
        groupedImages[prefix] = [];
      }
      groupedImages[prefix].push(indexs[i]);
    }
    
    // 遍历分组影像,导出最大影像
    for (var key in groupedImages) {
      var imagesToExport = imageCol.filter(ee.Filter.stringStartsWith('system:index', key));
      var image = imagesToExport.max();
      image = image.divide(10000).toFloat();
      
      Export.image.toDrive({
        image: image.clip(roi),
        description: key,
        fileNamePrefix: "S2_" + key,
        folder: 'S2_daily_image ',
        region: roi,
        scale: 10,
        crs: "EPSG:4326",
        maxPixels: 1e13
      });
    }
  });
}

exportImageCollection(data_bands);


五、其他推荐文章


【GEE】针对单张影像的单波段批量导出(解决大范围影像所有波段集体导出数据量太大的方法之一)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐