Google Earth Engine(GEE)深度学习入门教程-GEE预处理篇-哨兵2时间序列影像

GEE预处理篇-哨兵2时间序列影像

数据集的在GEE平台导出已经在上篇文章讲过了,本篇文章主要介绍哨兵2时间序列的影像的预处理方法,每个人的预处理方法不同,仅供参考。

image-20240523140913366

本文利用Sentinel-2影像构建研究区的时间序列。研究区阴雨天气较多,云污染导致部分时相的影像覆盖率过低,无法构建完整的时间序列影像。综合考虑研究区的影像缺失情况,本文对去云后的时间序列影像进行线性插值,以补全云污染的区域,插值的时间单边窗口为30天。即缺失像素在分别向前后30天时间窗口中寻找最近高质量的观测值,采用线性插值填补当前的缺失值。

插值噪声、薄云等其他因素影响最终的分类结果,为了减轻这些影响,我们应用Savitzky-Golay滤波算法平滑和滤波插值后的时间序列。

本文将滤波的时间窗口设置25天,多项式阶数为3阶。采用时间窗口卷积的方式会导致时间序列两端出现不可避免的误差,为了消除这种影响,本文相应延长了插值时间序列和滤波时间序列的长度,并在滤波后截断多余的序列

以下是完整代码(包含测试代码):

var getS2Image = function(BANDS,geometry,DEBUG){
     
     
    // 第一步:选择研究区,对影像数据进行去云、计算NDVI。
    var s2 = ee.ImageCollection("COPERNICUS/S2_SR"); 
    if(DEBUG === undefined){
        DEBUG = false
    }
    if(geometry===undefined){
        var geometry = ee.Geometry.Polygon(
          [[[115.81330192014848, 33.16593487478022],
            [115.81330192014848, 33.1643901294541],
            [115.81527602598344, 33.1643901294541],
            [115.81527602598344, 33.16593487478022]]], null, false)
    }
    //因为S-G滤波的开始会出现问题,所以增长时间序列 共NUM+ADD_SERIES*2个时间序列
    var ADD_SERIES = 3
    //因为需要保证插值数据的完整,所以需要对滤波的时间序列进行增长
    var interp_SERIES = 7
    var NUM = 24
    var startDate = ee.Date('2019-06-14').advance(-(ADD_SERIES)*5, 'day')
    var endDate = startDate.advance(5*(NUM+ADD_SERIES*2+interp_SERIES), 'day')
    var filtered = s2
      .filter(ee.Filter.date(startDate, endDate))
      .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))//30->70
      .filter(ee.Filter.bounds(geometry))
    // S2数据的去云函数
    function maskS2clouds(image) {
      var cloudProb = image.select('MSK_CLDPRB');
      var snowProb = image.select('MSK_SNWPRB');
      var cloud = cloudProb.lt(50);   //5-30
      var snow = snowProb.lt(5);
      var scl = image.select('SCL'); 
      var shadow = scl.eq(3); // 3 = cloud shadow
      var cirrus = scl.eq(10); // 10 = cirrus
     
      // 云的概率小于30%或云影分类
      var mask = (cloud.and(snow)).and(cirrus.neq(1)).and(shadow.neq(1));
      return image.updateMask(mask).divide(10000)
          .toFloat()       //.toFloat()  !!如果没有这一步可能会报类型错误, 计算指数也需要此强制类型转换
          .select("B.*")
          .copyProperties(image, ["system:time_start"]);
    }
    function resample(img) {
     return ee.Image(img
     .resample('bicubic')
//     .reproject({'crs':'EPSG:4326','scale': 10.0}) 
     );
    }
    // function addNDVI(image) {
    //   //!!! .toFloat()  添加其他指数时也要进行强制类型(float)转化,防止报错
    //   var ndvi = image.normalizedDifference(['B8', 'B4']).toFloat().rename('ndvi');
    //   return image.addBands(ndvi);
    // }
    var filtered = filtered.map(maskS2clouds)
                    .map(resample)
                  //.map(addNDVI)
                  .select(BANDS)
    // 第二步:设置好需要被插值的空影像,需要包含时间信息。这样方程计算出来以后我们就可以根据时间计算当时的像元值。 这里我们每隔5天就设置一个空影像。我们把空影像标记为“interpolated”,之后把真实影像和需要被插值的影像结合到一起。
    
    var n = 5;
    var totalDays = endDate.difference(startDate, 'day');
    var daysToInterpolate = ee.List.sequence(1, totalDays, n)
    //声明多波段的影像
    var emptyImage = BANDS.map(function(){return ee.Image()})
    var initImages = daysToInterpolate.map(function(day) {
      var image = ee.Image(emptyImage).rename(BANDS).toFloat().set({
        'system:index': ee.Number(day).format('%d'),
        'system:time_start': startDate.advance(day, 'day').millis(),
        // Set a property so we can identify interpolated images
        'type': 'interpolated'
      })
      return image
    })
    
    var initCol = ee.ImageCollection.fromImages(initImages)

    var mergedCol = filtered.merge(initCol)
    var mergedCol = mergedCol.map(function(image) {
      var timeImage = image.metadata('system:time_start').rename('timestamp')
      var timeImageMasked = timeImage.updateMask(image.mask().select(0))
      return image.addBands(timeImageMasked)
    })
    
    // 第三步:利用Join函数把每个影像对应的之前和之后的影像链接起来。
    // 线性插值的间隔
    var days = 45
    var millis = ee.Number(days).multiply(1000*60*60*24)
    
    var maxDiffFilter = ee.Filter.maxDifference({
      difference: millis,
      leftField: 'system:time_start',
      rightField: 'system:time_start'
    })
    
    var lessEqFilter = ee.Filter.lessThanOrEquals({
      leftField: 'system:time_start',
      rightField: 'system:time_start'
    })
    
    
    var greaterEqFilter = ee.Filter.greaterThanOrEquals({
      leftField: 'system:time_start',
      rightField: 'system:time_start'
    })
    
    
    var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
    var join1 = ee.Join.saveAll({
      matchesKey: 'after',
      ordering: 'system:time_start',
      ascending: false})
      
    var join1Result = join1.apply({
      primary: mergedCol,
      secondary: mergedCol,
      condition: filter1
    })
    
    var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
    
    var join2 = ee.Join.saveAll({
      matchesKey: 'before',
      ordering: 'system:time_start',
      ascending: true})
      
    var join2Result = join2.apply({
      primary: join1Result,
      secondary: join1Result,
      condition: filter2
    })
    // 第四步:利用线性插值先把空数据插出来。
    
    var filtered = join2Result.filter(ee.Filter.eq('type', 'interpolated'))

    function interpolateImages(image) {
      var image = ee.Image(image)
    
      var beforeImages = ee.List(image.get('before'))
      var beforeMosaic = ee.ImageCollection.fromImages(beforeImages).mosaic()
      var afterImages = ee.List(image.get('after'))
      var afterMosaic = ee.ImageCollection.fromImages(afterImages).mosaic()
    
      var t1 = beforeMosaic.select('timestamp').rename('t1')
      var t2 = afterMosaic.select('timestamp').rename('t2')
    
      var t = image.metadata('system:time_start').rename('t')
    
      var timeImage = ee.Image.cat([t1, t2, t])
    
      var timeRatio = timeImage.expression('(t - t1) / (t2 - t1)', {
        't': timeImage.select('t'),
        't1': timeImage.select('t1'),
        't2': timeImage.select('t2'),
      })
    
      var interpolated = beforeMosaic
        .add((afterMosaic.subtract(beforeMosaic).multiply(timeRatio)))
      var result = image.unmask(interpolated)
      return result.copyProperties(image, ['system:time_start'])
    }
    var interpolatedCol = ee.ImageCollection(
      filtered.map(interpolateImages)).select(BANDS)
    var visualization = {
        min:[2955/10000,1223/10000,1461/10000],//%2线性拉伸
        max:[6008/10000,2424/10000,2416/10000],//%2线性拉伸
        bands: ['B8', 'B4', 'B3'],
      };
    // //调试代码
    // var smoothed_ = interpolatedCol.toList(100)
    // for (var index = 0;index< NUM+ADD_SERIES*2; index++){
    //     if(DEBUG)
    //         Map.addLayer(ee.Image(smoothed_.get(index)), visualization, index.toString());
    // }
   //...
    // print(filtered)
    // var debug =  ee.ImageCollection(filtered).filter(ee.Filter.eq("system:index",'2_106')).first()
    // print(debug)
    // print(ee.Image(interpolateImages(debug)).select(BANDS))
    // Map.addLayer(ee.Image(interpolateImages(debug)).select(BANDS), {}, 'interpolateImages')
    // print('Interpolated Collection', interpolatedCol)
    //...
    // 第五步:把插出来的数据进行SG滤波。
    
    var oeel=require('users/OEEL/lib:loadAll');
    // https://www.open-geocomputing.org/OpenEarthEngineLibrary/#.ImageCollection.SavatskyGolayFilter
    // S-G滤波的时间窗口 
    
    var window = ADD_SERIES*5    // 5*5= 25
    var millis = ee.Number(window).multiply(1000*60*60*24)
    
    // Use the same maxDiffFilter we used earlier
    var maxDiffFilter = ee.Filter.maxDifference({
      difference: millis,
      leftField: 'system:time_start',
      rightField: 'system:time_start'
    })
    
    // Use the default distanceFunction
    var distanceFunction = function(infromedImage, estimationImage) {
      return ee.Image.constant(
          ee.Number(infromedImage.get('system:time_start'))
          .subtract(
            ee.Number(estimationImage.get('system:time_start')))
            );
      }
    
    // 把SG滤波函数设置为三次函数
    var order = 3;
    var smoothed = oeel.ImageCollection.SavatskyGolayFilter(
      interpolatedCol, 
      maxDiffFilter,
      distanceFunction,
      order)
    //选择d_0_波段并重命名
    var d_0_BANDS = BANDS.map(function(band){return "d_0_"+band})
    var smoothed = smoothed.select(d_0_BANDS,BANDS)
    var allSmoothed = smoothed
    //截取掉延长后的序列
    
    smoothed = ee.ImageCollection(smoothed.toList(NUM+ADD_SERIES*2).slice(ADD_SERIES,ADD_SERIES+NUM))
    
    //调整编号
    smoothed = smoothed.map(function(img){return img.set("index",smoothed.toList(NUM).indexOf(img))})
    //合成缩减数据
    var visualization = {
        min:[2955/10000,1223/10000,1461/10000],//%2线性拉伸
        max:[6008/10000,2424/10000,2416/10000],//%2线性拉伸
        bands: ['B8', 'B4', 'B3'],
      };

    smoothed = smoothed.toList(NUM)
    var smoothedmean = ee.List([]);
    for (var index = 0;index< NUM/2; index++){
        var temp = ee.ImageCollection([
                  smoothed.get(2*index),
                  smoothed.get(2*index+1)])
                  .mean();
        smoothedmean=smoothedmean.add(temp);
        if(DEBUG)
            Map.addLayer(temp, visualization, index.toString());
    }
    smoothedmean = ee.ImageCollection(smoothedmean)
    //如果想返回滤波前的线性插值时间序列,DEBUG设置为true
    if(DEBUG)
        return [smoothedmean,smoothed,interpolatedCol,allSmoothed]
    else
        return smoothedmean
}
exports.getS2Image = getS2Image






  // 显示出来并绘图
var geometry = ee.Geometry.Polygon(
          [[[115.81330192014848, 33.16593487478022],
            [115.81330192014848, 33.1643901294541],
            [115.81527602598344, 33.1643901294541],
            [115.81527602598344, 33.16593487478022]]], null, false)
// var geometry =geometry2
var S2 = getS2Image(["B4","B3","B8",],geometry,true)
var S2_SG = ee.ImageCollection(S2[1]).select(["B8"], ["SG"])
var S2_interp = ee.ImageCollection(S2[2]).select(["B8"], ["interp"])
print(S2_SG)
Map.addLayer(geometry, {color: 'red'}, 'Farm')
Map.centerObject(geometry)
// 把原始的和经过SG滤波的数据NDVI曲线画出来
var chart = ui.Chart.image.series({
  imageCollection: S2_interp.merge(S2_SG),
  region: geometry,
  reducer: ee.Reducer.mean(),
  scale: 10,
}).setOptions({
      lineWidth: 1,
      title: "滤波后的时序曲线",
      interpolateNulls: true,
      vAxis: {title: 'B8'},
      hAxis: {title: '', format: 'YYYY-MMM'},
      series: {
        0: {color: 'red', lineWidth: 2 }, // Smoothed NDVI
        1: {color: 'blue', lineWidth: 1, 
          lineDashStyle: [1, 1], pointSize: 1,
          }, // interpolatedCol NDVI
      },

    })
print(chart)

image-20240523141249174

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值