官方教程:TFRecord 和地球引擎
在GEE的JS Code Editor中,我们按照我们的需要去处理对应的遥感影像,得到处理后Image影像。为了导出后读取数据,在导出前一定清楚每个波段的名称(不然没法读取)。深度学习数据集所需数据以为patch为单位,所以需要对整个遥感影像进行裁剪,我探索出来GEE能实现的裁剪方法的有2种:滑窗裁剪、随机裁剪
随机裁剪:随机选择生成多个样点,然后裁剪样点周围的邻域,导出。用作构造数据集
滑窗裁剪:设定步幅和窗口大小,裁剪完整影像,导出。用作大范围推理验证
导出有2个重要参数:patchSize 和 kernelSize。patchSize 是指滑窗不重叠区域的大小,kernelSize是指滑窗重叠缓冲区的大小。例如:patchSize 为32,kernelSize为32,则导出的图像单个大小为64*64,中间的32*32为不重叠区域,边缘的16格像素为缓冲区大小。导出图像时,数据按通道、高度、宽度 (CHW) 排序。导出可以拆分为多个 TFRecord 文件,每个文件包含一个或多个 大小的 patches ,这是用户在导出中指定的。文件的大小(以字节为单位是用户在参数中指定的)。
代码如下:
Export.image.toCloudStorage({
image : image.toFloat(), //保证所有层数据类型统一,导出Image影像
description : desc, //任务名称
bucket : BUCKET, //存储桶名称
fileNamePrefix : FOLDER + '/' + desc, //保存的路径及文件名
region : ee.Feature(savePolys.get(g)).geometry(),//范围矢量
scale : 10, //分辨率
fileFormat : 'TFRecord', //格式
maxPixels : 1e13,
formatOptions : {
'patchDimensions': [32,32], //核心区大小
'kernelSize': [32,32], //缓冲区
'compressed': true,
'maxFileSize': 104857600*10 //400M
}
});
导出结果:
导出内容除了包含TFRecord格式的数据包之外,还有一个json格式文件,文件内部保存着影像的裁剪结果和地理位置信息,例如:
{
"projection": {
"crs": "EPSG:4326",
"affine": {
"doubleMatrix": [8.983152841195215E-5, 0.0, 116.04113446753695, 0.0, -8.983152841195215E-5, 33.789500591456914]
}
},
"patchDimensions": [32, 32],
"patchesPerRow": 19,
"totalPatches": 285
}
其中:crs为投影坐标系、doubleMatrix为地理仿射变换矩阵、patchDimensions为不重叠区域大小、patchesPerRow为裁剪区域内每行得到的样本数量、totalPatches为总的patch数量。这些信息都是为了将裁剪后的普通图像恢复为遥感影像。
随机生成样点,然后裁剪样点周围的邻域,导出。主要用作构造数据集,扩充数据集的样本量。
因为本文的使用的预处理过程比较复杂,数据量比较大,直接对整幅影像导出会导致GEE用户内存溢出,所以本文对研究区进行区块划分
划分训练、验证、测试区域的代码分为以下几个步骤:
//将研究区划分为多个区域,并按照6:2:2的比例划分训练、验证、测试区域,
//!!!仅需执行一次,手动将区域格式变化为featureColection
var roi = ee.FeatureCollection('TIGER/2018/States').filter(ee.Filter.eq('NAME', 'Illinois'));
// 将研究区域按照 10*10 的大小进行分割,循环处理子区域
var corner1 = [-91.70112615545109, 36.986063288694794]
var corner2 = [-87.30659490545109, 42.60259690092805]
var sliceN = 10;
var slicePolysList = ee.List([])
for (var x = corner1[0]; x < corner2[0]; x += (corner2[0]-corner1[0])/sliceN) {
for (var y = corner1[1]; y < corner2[1]; y += (corner2[1]-corner1[1])/sliceN) {
var x2 = x+(corner2[0]-corner1[0])/sliceN
var y2 = y+(corner2[1]-corner1[1])/sliceN
var area = ee.Geometry.Rectangle(x, y, x2, y2);
slicePolysList = slicePolysList.add(ee.Feature(area))
}
}
var slicePolys = ee.FeatureCollection(slicePolysList) // 去除不被完全包含的区域
.filter(ee.Filter.isContained({leftField: '.geo',rightValue: roi.geometry()}))
.randomColumn("random", 1)
var trainPolys = slicePolys.filter(ee.Filter.rangeContains("random",0,0.6))
var evalPolys = slicePolys.filter(ee.Filter.rangeContains("random",0.6,0.8))
var testPolys = slicePolys.filter(ee.Filter.rangeContains("random",0.8,1))
var geometryLayer = ui.Map.GeometryLayer({geometries: [trainPolys.geometry().getInfo()], name: 'trainPolys',color: 'green'});
// 将 GeometryLayer 添加到地图
Map.drawingTools().layers().add(geometryLayer);
var geometryLayer = ui.Map.GeometryLayer({geometries: [evalPolys.geometry().getInfo()], name: 'evalPolys',color: 'red'});
// 将 GeometryLayer 添加到地图
Map.drawingTools().layers().add(geometryLayer);
var geometryLayer = ui.Map.GeometryLayer({geometries: [testPolys.geometry().getInfo()], name: 'testPolys',color: 'blue'});
// 将 GeometryLayer 添加到地图
Map.drawingTools().layers().add(geometryLayer);
区块划分完成后,我们使用循环逐个区块随机采样并进行导出,单个样本大小为64*64。单次采样过多也会内存溢出,所以一个区块也要采样多次,每次采样少一点。
//此时需要让单次处理的区域尽可能小
//https://developers.google.com/earth-engine/guides/tf_examples
var BUCKET = 'yqs'
var FOLDER = 'yangfangVal'
var TRAINING_BASE = 'training_patches'
var EVAL_BASE = 'eval_patches'
var TEST_BASE = 'test_patches'
var BANDS = ["B2","B3","B4","B5","B6","B7","B8","B8A","B11","B12"]
var RESPONSE = 'soya'
var FEATURES = BANDS.concat([RESPONSE])
print(FEATURES)
var N = 160 //单个区域内的采样数量。
var n = 16 //单区域采样次数。增大n,可以减少单次采样的数量,防止出现Computed value is too large.错误 !但是导出速度会变慢
var KERNEL_SIZE = 64 //减小此值可以防止出现采样时出现 Computed value is too large. 128导出数据包太大了
var KERNEL_SHAPE = [KERNEL_SIZE, KERNEL_SIZE]
var list = ee.List.repeat(1, KERNEL_SIZE)
var lists = ee.List.repeat(list, KERNEL_SIZE)
var kernel = ee.Kernel.fixed(KERNEL_SIZE, KERNEL_SIZE, lists)
trainPolys = trainPolys.toList(trainPolys.size()); // 将 FeatureCollection 转换为 FeatureList
evalPolys = evalPolys.toList(evalPolys.size());
testPolys = testPolys.toList(testPolys.size());
var savePolys = testPolys
var BASE = TEST_BASE//TRAINING_BASE;//EVAL_BASE//TEST_BASE
//为了保证内存不溢出,在处理图片的时候尽可能处理较小块的区域
for (var g = 0; g < savePolys.size().getInfo(); g++) {
var geomSample = ee.FeatureCollection([]); // 用于存储采样点的 FeatureCollection
// 数据集标签的加载函数,请根据自己需求对应修改。
var soya = CDLlib.getCDL()
//影像预处理函数,请根据自己需求对应修改。
var S2 = S2lib.getS2Image(BANDS,ee.Feature(savePolys.get(g)).geometry())
var image = S2.addBands([soya]).rename(FEATURES)
//给多个数据包命名
var desc = BASE + '_g' + g.toString();
print(image)
var arrays = image.toFloat().neighborhoodToArray(kernel)
for (var i = 0; i < n; i++) {
var sample = arrays.sample({
region: ee.Feature(savePolys.get(g)).geometry(),
scale: 10,
numPixels: N / n,
seed: i,
tileScale: 8
});
// print(sample.size())
geomSample = geomSample.merge(sample); // 将采样点合并到 geomSample 中
}
// Map.addLayer(image,{bands: ['soya']},desc);//调试用
// //print(geomSample.size())//调试用
// Map.addLayer(ee.Feature(savePolys.get(g)).geometry().buffer(640),[],desc);//调试用
var task = Export.table.toCloudStorage({
collection: geomSample,
description: desc,
bucket: BUCKET,
fileNamePrefix: FOLDER + '/' + desc,
fileFormat: 'TFRecord',
});
}