GEE使用随机森林和变化检测进行长时序土地覆盖制图

问题背景

本次实践主要是对论文《Time-series land cover mapping and urban expansion analysis using OpenStreetMap data and remote sensing big data: A case study of Guangdong-Hong Kong-Macao Greater Bay Area, China》的代码复现(DOI: DOI),现在共享出来供大家学习。
整体流程如下图所示:
在这里插入图片描述

数据预处理

在GEE平台上编写代码,引入相关数据,选择武汉市作为研究区,数据预处理代码如下所示:

var Landsat5 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
var Landsat7 = ee.ImageCollection('LANDSAT/LE07/C02/T1_L2')
var Landsat8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')

// var table = table1;
var wuhan = table;



/** function definition **/
function applyScaleFactorsL89(image) {
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
              .addBands(thermalBands, null, true);
}

function applyScaleFactorsL457(image) {
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
              .addBands(thermalBand, null, true);
}

function cloudmaskL89(image) {
  // Bits 3 and 5 are cloud shadow and cloud, respectively.
  var cloudShadowBitMask = (1 << 4);
  var cloudsBitMask = (1 << 3);
  // Get the pixel QA band.
  var qa = image.select('QA_PIXEL');
  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
                 .and(qa.bitwiseAnd(cloudsBitMask).eq(0));
  return image.updateMask(mask);
}

function cloudMaskL457(image) {
  var qa = image.select('QA_PIXEL');
  // If the cloud bit (5) is set and the cloud confidence (7) is high
  // or the cloud shadow bit is set (3), then it's a bad pixel.
  var cloud = qa.bitwiseAnd(1 << 3)
                  .and(qa.bitwiseAnd(1 << 9))
                  .or(qa.bitwiseAnd(1 << 4));
  // Remove edge pixels that don't occur in all bands
  var mask2 = image.mask().reduce(ee.Reducer.min());
  return image.updateMask(cloud.not()).updateMask(mask2);
}

/** bands rename function **/

function bandRenameL89(image) {
  var blue = image.select(['SR_B2']).rename('blue');
  var green = image.select(['SR_B3']).rename('green');
  var red = image.select(['SR_B4']).rename('red');
  var nir = image.select(['SR_B5']).rename('nir');
  var swir1 = image.select(['SR_B6']).rename('swir1');
  var swir2 = image.select(['SR_B7']).rename('swir2');
  var new_image = blue.addBands([green, red, nir, swir1, swir2]);
  return new_image;
}

function bandRenameL457(image) {
  var blue = image.select(['SR_B1']).rename('blue');
  var green = image.select(['SR_B2']).rename('green');
  var red = image.select(['SR_B3']).rename('red');
  var nir = image.select(['SR_B4']).rename('nir');
  var swir1 = image.select(['SR_B5']).rename('swir1');
  var swir2 = image.select(['SR_B7']).rename('swir2');
  var new_image = blue.addBands([green, red, nir, swir1, swir2]);
  return new_image;
}

/** caculate the NDVI, NDWI, NDBI **/
function NDVI(img) {
 var nir = img.select("nir");
 var red = img.select("red");
 var ndvi = img.expression(
   "(nir - red)/(nir + red)",
   {
     "nir": nir,
     "red": red
   }
 ).rename("NDVI");
 return ndvi;
}

function NDWI(img) {
 var green = img.select("green");
 var nir = img.select("nir");
 var ndwi = img.expression(
   "(green - nir)/(green + nir)",
   {
     "green": green,
     "nir": nir
   }
 ).rename("NDWI");
 return ndwi;
}

function NDBI(img){
 var nir = img.select("nir");
 var swir = img.select("swir1");
 var ndbi = img.expression(
   "(swir - nir)/(nir + swir)",
   {
     "nir": nir,
     "swir": swir
   }
 ).rename("NDBI");
 return ndbi;
}

function MNDWI(img) {
 var green = img.select("green");
 var swir1 = img.select("swir1");
 var mndwi = img.expression(
   "(green - swir1)/(green + swir1)",
   {
     "green": green,
     "swir1": swir1
   }
 );
 return mndwi;
}

function glcm(img) {
 var nir = img.select("nir").toUint16();
 return nir.glcmTexture();
}


/** generate the bi-temporal image**/
function generateImage(start_date, end_date, cloudCover, wuhan){
  // landsat 8
    var l8_col = Landsat8.filterBounds(wuhan)
                          .filterDate(start_date, end_date)
                          .filter(ee.Filter.lt('CLOUD_COVER', cloudCover))
                          .map(applyScaleFactorsL89)
                          .map(cloudmaskL89)
                          .map(bandRenameL89);
    print('landsat8', l8_col.size())
    
    // landsat 7
    var l7_col = Landsat7.filterBounds(wuhan)
                          .filterDate(start_date, end_date)
                          .filter(ee.Filter.lt('CLOUD_COVER', cloudCover))
                          .map(applyScaleFactorsL457)
                          .map(cloudMaskL457)
                          .map(bandRenameL457)
                      ;
    print('landsat7', l7_col.size())
    
    // landsat 5
    var l5_col = Landsat5.filterBounds(wuhan)
                        .filterDate(start_date, end_date)
                        .filter(ee.Filter.lt('CLOUD_COVER', cloudCover))
                        .map(applyScaleFactorsL457)
                        .map(cloudMaskL457)
                        .map(bandRenameL457)
                      ;
    print('landsat5', l5_col.size())
    
    
    /** composite the image **/
    var l5 = l5_col
    var l7 = l7_col.mean()
    var l8 = l8_col
    
    print('l5', l5)
    print('l7', l7)
    print('l8', l8)
    
    /** merge image **/
    var image = l8.merge(l7).merge(l5);
    
    print('image', image)
                      
    // print("final image count", image.size(), image)
    
    
    /** start caculate **/
    var final_image = image.mean().clip(wuhan);
    var image_ndvi = NDVI(final_image)
    var image_ndwi= NDWI(final_image)
    var image_ndbi = NDBI(final_image)
    
    var optim_image = final_image.addBands(image_ndvi.select("NDVI")).addBands(image_ndwi.select("NDWI"))
                      .addBands(image_ndbi.select("NDBI")).clip(wuhan)
    
    // print("optim image", optim_image)
    return optim_image
}

上述代码主要针对Landsat卫星数据进行处理。

主成分提取

var year = 2021;

for(var i = year; i<=year; i++){
  var year_name = i;
  var start_date = (year_name) + '-01-01';
  var end_date   = (year_name + 1) + '-01-01';
  var cloudCover = 20
  
  /** IMAGE HANDLE **/
  var t1 = generateImage(start_date, end_date, cloudCover, table)
  
  print("---------------------")
  
  var start_date = (year_name + 1) + '-01-01';
  var end_date   = (year_name + 2) + '-01-01';
  var t2 = generateImage(start_date, end_date, cloudCover, table)
  
  
  /** PCA Analysis **/
  // print("PCA Image is:", t2)
  // var image = t1;
  var region = table;
  // var bandNames = image.bandNames();
  
  var getNewBandNames = function(image, prefix) 
  {
     var seq = ee.List.sequence(1, image.bandNames().length());
     return seq.map(function(b) {return ee.String(prefix).cat(ee.Number(b).int());});
  };
  
  var getPrincipalComponents = function(image, table) 
  {
     var arrays = image.toArray();
     var covar = arrays.reduceRegion({
       reducer: ee.Reducer.centeredCovariance(),
       geometry:table,
       scale: 30,
       maxPixels: 1e13});
     var covarArray = ee.Array(covar.get('array'));
     var eigens = covarArray.eigen();
     var eigenValues = eigens.slice(1, 0, 1);
     var eigenVectors = eigens.slice(1, 1);
     var arrayImage = arrays.toArray(1);
     var principalComponents = ee.Image(eigenVectors).matrixMultiply(arrayImage);
     var sdImage = ee.Image(eigenValues.sqrt()).arrayProject([0]).arrayFlatten([getNewBandNames(image, 'sd')]);
     return principalComponents.arrayProject([0]).arrayFlatten([getNewBandNames(image, 'pc')]).divide(sdImage);
  };
  //pcImage中各波段名叫pc1、pc2
  var pcImage1 = getPrincipalComponents(t1, table);
  var pcImage2 = getPrincipalComponents(t2, table);
  //取主成分分析的第一主成分pc1
  // var pc1 = pcImage.select([pcImage.bandNames().get(0).getInfo()]);
  // pc1 = pc1.float();
  // print("pcImage:", pcImage)
  
  var imgA = pcImage1.select(["pc1", "pc2", "pc3"])
  var imgB = pcImage2.select(["pc1", "pc2", "pc3"])
  // print("imgA:", imgA)
  
  // 区域就是设置的roi,分辨率30米
    // Export.image.toAsset({
    //   image: imgA,
    //   description: 'pca'+i,
    //   assetId: 'Article/pca'+i,
    //   scale: 30,
    //   maxPixels:1e13
    // });
}

变化检测挑选变化点和不变点

/** CVA **/
print("start CVA analysis")
var roi = table;
// CVA没有亲和变换的一致性,因此需要对影像进行均值标准化,以消除辐射不一致性
// 1.计算均值、标准差
var meanA = imgA.reduceRegion({
  reducer:ee.Reducer.mean(), 
  geometry:roi, 
  scale:30, 
  maxPixels:1e13});
var meanB = imgB.reduceRegion({
  reducer:ee.Reducer.mean(), 
  geometry:roi, 
  scale:30, 
  maxPixels:1e13});
var stdA = imgA.reduceRegion({
  reducer:ee.Reducer.stdDev(), 
  geometry:roi, 
  scale:30, 
  maxPixels:1e13});
var stdB = imgB.reduceRegion({
  reducer:ee.Reducer.stdDev(), 
  geometry:roi, 
  scale:30, 
  maxPixels:1e13});
  
// 2.归一化
function get_mutibands_image(band_1, band_2, band_3) {
    var image1 = ee.Image.constant(ee.Number(band_1)).rename(band1_name);
    var image2 = ee.Image.constant(ee.Number(band_2)).rename(band2_name);
    var image3 = ee.Image.constant(ee.Number(band_3)).rename(band3_name);
    return image1.addBands(image2).addBands(image3);
}

// 设定波段组合,Sentinel-2数据中真彩色:432,假彩色:843
var band_list = ee.List(["pc1", "pc2", "pc3"]);
var band1_name = ee.String(band_list.get(0));
var band2_name = ee.String(band_list.get(1));
var band3_name = ee.String(band_list.get(2));

// print(meanA.get(band1_name))

var imgA_mean = get_mutibands_image(meanA.get(band1_name), meanA.get(band2_name), meanA.get(band3_name));
var imgB_mean = get_mutibands_image(meanB.get(band1_name), meanB.get(band2_name), meanB.get(band3_name));
var imgA_std = get_mutibands_image(stdA.get(band1_name), stdA.get(band2_name), stdA.get(band3_name));
var imgB_std = get_mutibands_image(stdB.get(band1_name), stdB.get(band2_name), stdB.get(band3_name));
var normal_imgA = imgA.subtract(imgA_mean).divide(imgA_std);
var normal_imgB = imgB.subtract(imgB_mean).divide(imgB_std);
// Map.addLayer(normal_imgA, {min: 0, max: 1}, "normal_imgA");
// Map.addLayer(normal_imgB, {min: 0, max: 1}, "normal_imgB");
// 3.CVA
function cva(normal_imgA, normal_imgB) {
    var img_diff = normal_imgA.subtract(normal_imgB);
    var square_img_diff = img_diff.pow(2);
    var sum_image = square_img_diff.expression("B1+B2+B3", {
    B1: square_img_diff.select(band1_name),
    B2: square_img_diff.select(band2_name),
    B3: square_img_diff.select(band3_name),
}); 
    var l2_norm = sum_image.rename("sum_image").sqrt();
    return l2_norm;
}
var l2_norm = cva(normal_imgA, normal_imgB);

// 4.OTSU
function otsu(histogram) {
  var counts = ee.Array(ee.Dictionary(histogram).get('histogram'));
  var means = ee.Array(ee.Dictionary(histogram).get('bucketMeans'));
  var size = means.length().get([0]);
  var total = counts.reduce(ee.Reducer.sum(), [0]).get([0]);
  var sum = means.multiply(counts).reduce(ee.Reducer.sum(), [0]).get([0]);
  var mean = sum.divide(total);
  var indices = ee.List.sequence(1, size);
  var bss = indices.map(function(i) {
    var aCounts = counts.slice(0, 0, i);
    var aCount = aCounts.reduce(ee.Reducer.sum(), [0]).get([0]);
    var aMeans = means.slice(0, 0, i);
    var aMean = aMeans.multiply(aCounts)
        .reduce(ee.Reducer.sum(), [0]).get([0])
        .divide(aCount);
    var bCount = total.subtract(aCount);
    var bMean = sum.subtract(aCount.multiply(aMean)).divide(bCount);
    return aCount.multiply(aMean.subtract(mean).pow(2)).add(
          bCount.multiply(bMean.subtract(mean).pow(2)));
  });
  return means.sort(bss).get([-1]);
}

var histogram = l2_norm.reduceRegion({
    reducer: ee.Reducer.histogram(), 
    geometry: roi, 
    scale: 30,
    maxPixels: 1e13,
  });
var threshold = otsu(histogram.get("sum_image"));
print(threshold,'分割阈值');

// 结果展示
var mask = l2_norm.gte(threshold).clip(roi);
var change_map = mask.rename("change_map");
// print("mask", mask)
Map.addLayer(change_map.clip(roi),{min:0,max:1,palette:['#000000','#ffffff']},'change_result');

// Export the change_map
Export.image.toAsset({
  image: change_map.clip(roi),
  description: 'change_map',
  assetId: 'Article/change_map',
  scale: 30,
  maxPixels:1e13
});


var change_map = change_map.clip(roi)
print("extract value to point")

根据变化检测结果选点

//1.Copland
var cropland = change_map.sampleRegions({
  collection: Cropland, 
  properties: ["fclass"], 
  scale: 30,
  geometries:true,
  tileScale:16
});
print("Cropland: ", Cropland)
var unchanged_cropland = cropland.filter(ee.Filter.eq('change_map', 0));

//2.Forest
var forest = change_map.sampleRegions({
  collection: Forest, 
  properties: ["fclass"], 
  scale: 30,
  geometries:true,
  tileScale:16
});
var unchanged_forest = forest.filter(ee.Filter.eq('change_map', 0));

//3.Isurface
var isurface = change_map.sampleRegions({
  collection: Isurface, 
  properties: ["fclass"], 
  scale: 30,
  geometries:true,
  tileScale:16
});
var unchanged_isurface = isurface.filter(ee.Filter.eq('change_map', 0));

//4.Water
var water = change_map.sampleRegions({
  collection: Water, 
  properties: ["fclass"], 
  scale: 30,
  geometries:true,
  tileScale:16
});
var unchanged_water = water.filter(ee.Filter.eq('change_map', 0));
print(unchanged_water.size())

训练随机森林并测试

//==================================================================================
// now, I get the unchanged points, then I can extract the landsat and other images
// 1.SRTM
var dataset = ee.Image('USGS/SRTMGL1_003').clip(roi);
var elevation = dataset.select('elevation');
var slope = ee.Terrain.slope(elevation);
var e = elevation.select("elevation").clip(roi)
var s = slope.select("slope").clip(roi)

//2.1 SAR_1, pre image
var sentinel_1 = ee.ImageCollection('COPERNICUS/S1_GRD').filterBounds(wuhan).filterDate(year+'-01-01', year+'-12-20').median().clip(roi);

//2.2 SAR_2, post image
var sentinel_2 = ee.ImageCollection('COPERNICUS/S1_GRD').filterBounds(wuhan).filterDate((year+1)+'-01-01', (year+1)+'-12-20').median().clip(roi);

//3.GLCM
function glcm(img) {
   var nir = img.select("nir").toUint16();
   var gt = nir.glcmTexture();
   var savg = gt.select('nir_savg').rename('savg')
   var gvar = gt.select('nir_var').rename('var')
   var idm = gt.select('nir_idm').rename('idm')
   var con = gt.select('nir_contrast').rename('con')
   var diss = gt.select('nir_diss').rename('diss')
   var ent = gt.select('nir_ent').rename('ent')
   var asm = gt.select('nir_asm').rename('asm')
   var corr = gt.select('nir_corr').rename('corr')
   return savg.addBands(gvar).addBands(idm)
          .addBands(con).addBands(diss).addBands(ent).addBands(asm).addBands(corr)
}
var glcm_1 = glcm(t1)
var glcm_2 = glcm(t2)

print("glcm: ",glcm_1)

//4.concat the image
var t1 = t1.addBands(e).addBands(s).addBands(sentinel_1).addBands(glcm_1)
var t2 = t2.addBands(e).addBands(s).addBands(sentinel_2).addBands(glcm_2)
var t1 = t1.clip(roi)
var t2 = t2.clip(roi)

print("t1", t1)
print("t2", t2)

//----------------------------------------------------------
//----------------------------------------------------------
// start classifier, first do 2022, then pre years

var past_training = unchanged_cropland.merge(unchanged_forest).merge(unchanged_isurface).merge(unchanged_water);

print("2021 training:", past_training.limit(10))

var training = Cropland.merge(Forest).merge(Isurface).merge(Water);
print("2022 training: ",training.limit(10));

var training = past_training;
print("training: ",training.size());

var training = training.remap(
  ["cropland", "forest", "Impervious Surface", "water"], // 原始类别标签(0表示无效像素)
  [0, 1, 2, 3], // 映射后的数字(0留给无效像素)
  "fclass"          // 字段
);



var trainingData = training.randomColumn('random')
var sample_training = trainingData.filter(ee.Filter.lte("random", 0.8));
var sample_validate  = trainingData.filter(ee.Filter.gt("random", 0.8));
print("sample_training size is: ", sample_training.size())
print("sample_validate size is: ", sample_validate.size())

// 利用样本点拾取特征值用于模型训练和验证
var training = t1.clip(roi).sampleRegions({
  collection: sample_training, 
  properties: ["fclass"], 
  scale: 30,
  tileScale:16
});


var validation = t1.clip(roi).sampleRegions({
  collection: sample_validate, 
  properties: ["fclass"], 
  scale: 30,
  tileScale:16
});

print("validation: ", validation)



var classifier = ee.Classifier.smileRandomForest(50)
    .train({
      features: training, 
      classProperty: 'fclass', 
      inputProperties: t1.bandNames()
    });
    

var dict = classifier.explain();

var variable_importance = ee.Feature(null, ee.Dictionary(dict).get('importance'));

// 混淆矩阵法
var validated = validation.classify(classifier);
// 混淆矩阵
var testAccuracy = validated.errorMatrix('fclass', 'classification');
// 总体分类精度
var accuracy = testAccuracy.accuracy();
// 用户分类精度
var userAccuracy = testAccuracy.consumersAccuracy();
// 生产者精度
var producersAccuracy = testAccuracy.producersAccuracy();
// Kappa系数
var kappa = testAccuracy.kappa();

print('混淆矩阵:', testAccuracy);//
print('用户分类精度:', userAccuracy);//用户分类精度
print('生产者精度:', producersAccuracy);//生产者精度
print('总体分类精度:', accuracy);//总体分类精度
print('Kappa:', kappa);

现在已经可以进行土地覆盖制图,一些数据预处理的流程尚未提及,例如拓扑检查等操作。应该会在空闲时间写一下教程。

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值