首页 > 其他分享 >GEE使用随机森林和变化检测进行长时序土地覆盖制图

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

时间:2024-05-24 21:29:01浏览次数:12  
标签:变化检测 ee image print 时序 nir GEE var select

问题背景

本次实践主要是对论文《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);

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

标签:变化检测,ee,image,print,时序,nir,GEE,var,select
From: https://blog.csdn.net/MemoryCholer/article/details/139098472

相关文章

  • CodeGeeX 智能编程助手 6 项功能升级,在Visual Studio插件市场霸榜2周!
    CodeGeeX是支持适配VisualStudio2019的唯一一款国产智能辅助编程工具,已经在VisualStudio趋势上霸榜2周!CodeGeeXv1.0.4版本上线VisualStudio插件市场,带来了多项新功能和性能优化,提升编程体验。新功能亮点速览:一、侧边栏工具箱功能v1.0.4版本中,CodeGeeX新增了侧边栏工具箱......
  • 程序员的AI编程小助手,CodeGeeX在vscode,vs2022,IDEA2023使用体验总结
    程序员的AI编程小助手,CodeGeeX使用体验总结一、1.CodeGeeX是什么?能做什么?CodeGeeX是一个智能编程软件工具,目前CodeGeeX支持多种主流IDE,如VSCode、visualstudio2022,IntelliJIDEA、PyCharm、Vim等,同时,支持Python、Java、C++/C、JavaScript、Go等多种语言。CodeGeeX如何......
  • 程序员的AI编程小助手,CodeGeeX使用体验总结
    程序员的AI编程小助手,CodeGeeX使用体验总结:::warning一、1.CodeGeeX是什么?能做什么?CodeGeeX是一个智能编程软件工具,目前CodeGeeX支持多种主流IDE,如VSCode、visualstudio2022,IntelliJIDEA、PyCharm、Vim等,同时,支持Python、Java、C++/C、JavaScript、Go等多种语言。::......
  • VSCODE安装codegeexAI插件
    VSCODE安装codegeexAI插件一、vscode下载安装:https://zhuanlan.zhihu.com/p/2647854411.1、Vscode官网下载地址https://code.visualstudio.com/download这里以win10为例:1.2、安装1.3、配置中午软件包安装中文(简体)包,关闭VScode,重启即可二、CODEGEEX插件安装:1、Vscod......
  • 时序图
    时序图时序图1.参考资料2.基础3.符号3.1.斜线形式的上升沿、下降沿3.2.Eitheror信号3.3.波形省略3.2.1.虚线3.2.2.波浪号3.4.地址&数据表示4.实例-WT588F语音芯片时序图4.1.了解背景4.2.分析4.3.列逻辑4.4.根据逻辑写代码(伪代码)5.总......
  • GEE C25 高级矢量运算
    主要内容介绍GEE中可视化和处理矢量的高级技巧。 1、可视化要素集;一、可视化要素集1.可使用的函数Map.addLayer:draw:paint:style:2.创建等值线图ChoroplethMapvarblocks=ee.FeatureCollection('TIGER/2010/Blocks');varroads=ee.FeatureCollection('TIGER/2......
  • buuctf-pwn-[OGeek2019]babyrop
    查看一下保护情况丢进ida里分析主函数调用了一个含有alarm的函数,这个函数会设置一个定时器,到时间自动退出程序为了方便调试,我们直接patch掉这个函数接着分析,主函数读入了一个随机数,并将其传入sub_804871F函数sub_804871F函数读取输入,并检查输入的是否和随机数相同,不相同......
  • 免费的visual studio智能代码插件——CodeGeeX
    CodeGeeX是什么?什么是CodeGeeX?CodeGeeX是一款基于大模型的智能编程助手,它可以实现代码的生成与补全,自动为代码添加注释,不同编程语言的代码间实现互译,针对技术和代码问题的智能问答,当然还包括代码解释,生成单元测试,实现代码审查,修复代码bug等非常丰富的功能。CodeGeeX是一款基于......
  • 免费的visual studio智能代码插件——CodeGeeX
    CodeGeeX是什么?什么是CodeGeeX?CodeGeeX是一款基于大模型的智能编程助手,它可以实现代码的生成与补全,自动为代码添加注释,不同编程语言的代码间实现互译,针对技术和代码问题的智能问答,当然还包括代码解释,生成单元测试,实现代码审查,修复代码bug等非常丰富的功能。CodeGeeX是一款基于......
  • https://geek-docs.com/python/python-ask-answer/74_hk_1707485473.html
    Python中的b是什么介绍 在Python中,我们经常会看到一种奇特的表示方法,即以字符’b’开头的字符串,例如b'Hello'。这种表示方法在Python中被称为字节字符串(bytestring),简称为b字符串。在本文中,我们将详细介绍b字符串的特点、用途和常见应用场景。b字符串的特点字节字符串以字......