0

I am carrying out a hybrid classification based on OBIA, following the magnificent code that Noel Gorelick and company developed:

https://code.earthengine.google.com/?scriptPath=users%2Fnclinton%2FEE101%3A14%20-%20OBIA

It is just a trial but I would like to see the differences between a hybrid classification considering OBIA and without considering OBIA.

When I run the code, I get the following error: Output of image computation is too large (8 bands for 3733050 pixels = 113.9 MiB > 80.0 MiB). If this is a reduction, try specifying a larger 'tileScale' parameter.

I am confused, I do not really understand where to specify the 'tileScale' parameter.

The code is the following:

https://code.earthengine.google.com/5bb30c868bb6442b60bc688acdf6ae96

var region = geometry


/////////////////////////////// SENTINEL 1///////////////////////////////////////////

// Get the VV collection.
var collectionVV = ee.ImageCollection('COPERNICUS/S1_GRD')
.filterBounds(geometry)
.select('VV','VH')

// Create a 3 band stack by selecting from different periods (months)
var im1 = ee.Image(collectionVV.filterDate('2020-05-15', '2020-05-19').mean().divide(100));


print(im1)


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);
}

//////////////////////SENTINEL 2/////////////////////////////////////////////////

// Map the function over one year of data and take the median.
// Load Sentinel-2 TOA reflectance data.
var col = ee.ImageCollection('COPERNICUS/S2')
                  .filterDate('2020-05-01', '2020-05-30')
                  // Pre-filter to get less cloudy granules.
                  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
                  .map(maskS2clouds)
                  .filterBounds(geometry)
print(col)

//// PALETTE /////

var rgbVis = {
  min: 0.0,
  max: 0.3,
  bands: ['B4', 'B3', 'B2'],
};

///////////////////////////////ADD BANDS TOGETHER/////////////////////////////////////

// Function to add an NDVI band, treat as above function
var addNDVI = function(image) {
  return image
    .addBands(image.normalizedDifference(['B5', 'B4'])
    .rename('NDVI'))
    .float();
};


// Function to add an NDVI band, treat as above function
var addSAVI = function(image) {
  return image
    .addBands(image.expression(
      '(1 + L) * float(nir - red)/ (nir + red + L)',
      {
        'nir': image.select('B8'),
        'red': image.select('B4'),
        'L': 0.2
      })
    .rename('SAVI'))
    .float();
};

var addSENTINEL1 = function(image) {
  return image
    .addBands(im1.select('VV','VH'))
    .float();
};

var dataset = col.map(addSAVI).map(addNDVI).map(addSENTINEL1);

var sentinel = dataset.median().select("B2",'B3','B4','B8', 'NDVI', 'SAVI', 'VV', 'VH').clip(geometry)
var addSENTINEL1 = function(image) {
  return image
    .addBands(im1.select('VV','VH'))
    .float();
};

print(sentinel)

////////////////// SEGMENTATION ///////////////////////////

////Cada seed inicia un objeto nuevo
//Malla de puntos. A partir de estos puntos los clusteres se desarrollaran.
//El numero de seeds ha de ser acorde a los objetos que se quieren encontrar.
//Para encontrar carreteras u objetos mas pequenyos que los crops, se necesitaria una malla con mas seeds

var seeds = ee.Algorithms.Image.Segmentation.seedGrid(15);

//Map.addLayer(seeds)

// Run SNIC on the regular square grid. (re-name the bands)
var segmentation = ee.Algorithms.Image.Segmentation.SNIC({
  image: sentinel, 
  size: 5,
  compactness: 5,
  connectivity: 8,
  neighborhoodSize:256,
  seeds: seeds
}).select(["B2_mean",'B3_mean','B4_mean','B8_mean', 'NDVI_mean', 'SAVI_mean', 'VV_mean', 'VH_mean', 'clusters'], ["B2",'B3','B4','B8', 'NDVI', 'SAVI', 'VV', 'VH', 'clusters'])

print(segmentation)

var clusters = segmentation.select('clusters')

//Map.addLayer(segmentation)
//Map.addLayer(sentinel, rgbVis, 'RGB');

////// REDUCE COMPONENTS in order to get OBJECT VARIABLES //////////////////

//.reduceConnectedComponents (reduce todos los componentes de un cluster a un valor)

// Compute per-cluster stdDev. Indicador de buenos clusters y malos clusters (setdDev alta representa peores clusters)
var stdDev = sentinel.addBands(clusters).reduceConnectedComponents(ee.Reducer.stdDev(), 'clusters', 256)
//Map.addLayer(stdDev, {min:0, max:0.1}, 'StdDev', false)

// Area, Perimeter, Width and Height (cada valor dle pixel representa el area, perimetro y medidas de todo el cluster. 
//todos los pixeles tienen el mismo valor dentro del cluster)
var area = ee.Image.pixelArea().addBands(clusters).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256)
//Map.addLayer(area, {min:50000, max: 500000}, 'Cluster Area', false)

var minMax = clusters.reduceNeighborhood(ee.Reducer.minMax(), ee.Kernel.square(1));
var perimeterPixels = minMax.select(0).neq(minMax.select(1)).rename('perimeter');
//Map.addLayer(perimeterPixels, {min: 0, max: 1}, 'perimeterPixels');

var perimeter = perimeterPixels.addBands(clusters)
    .reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256);
//Map.addLayer(perimeter, {min: 100, max: 400}, 'Perimeter size', false);

var sizes = ee.Image.pixelLonLat().addBands(clusters).reduceConnectedComponents(ee.Reducer.minMax(), 'clusters', 256)
var width = sizes.select('longitude_max').subtract(sizes.select('longitude_min')).rename('width')
var height = sizes.select('latitude_max').subtract(sizes.select('latitude_min')).rename('height')
//Map.addLayer(width, {min:0, max:0.02}, 'Cluster width', false)
//Map.addLayer(height, {min:0, max:0.02}, 'Cluster height', false)


//////// PUT ALL BANDS TOGETHER (Concatenate) ///////////////

var objectPropertiesImage = ee.Image.cat([
  segmentation,
  stdDev,
  area,
  perimeter,
  width,
  height
]).float();

print(objectPropertiesImage, 'ObjectProperties')


////////////////////// UNSUPERVISED CLASSIFICATION ////////////////////


  // training region is the full image
  var training = objectPropertiesImage.sample({
    region: geometry,
    scale: 10,
    numPixels: 1000
  })

print(  objectPropertiesImage.reduceRegion({
  reducer: ee.Reducer.count(),
    geometry: geometry,
    scale: 10,
    maxPixels: 1e13
  })
)
  // train cluster on image
  var clusterer = ee.Clusterer.wekaKMeans(60).train(training)

    // cluster the complete image
  var result_unsupervised = objectPropertiesImage.cluster(clusterer)

////////////////////// SUPERVISED CLASSIFICATION RANDOM FORESTS based on properties image /////////////////////////

////// creating training data and test data ////////////

var TrainingData = urban.merge(water).merge(crops1).merge(bare_crops).merge(roads).randomColumn("random");

print(TrainingData)

var trainingData = TrainingData.filter(ee.Filter.lt("random",0.8))
var testData = TrainingData.filter(ee.Filter.gt("random",0.8))

///// blending training data into the Image /////////////

var TrainingSample = result_unsupervised.sampleRegions(trainingData,["land_class"],10);
var TestSample = result_unsupervised.sampleRegions(testData,["land_class"],10);

var bandNames = result_unsupervised.bandNames()

print(TrainingSample)

var classifier = ee.Classifier.randomForest(20,0).train(TrainingSample,"land_class",bandNames);

var classification = result_unsupervised.classify(classifier);
print(classification);

Map.addLayer(classification.randomVisualizer())
2

1 Answer 1

0

First, it is good practice to label you print statements as those are where errors will be thrown. That way you know where to track them down. Something like

print('Some output', eeObject);

The errors you are seeing are indicative of a reducer consuming too much memory. The tileScale parameter allows you to split up that work into finer chunks on the backend, at the expense of slower execution time.

tileScale (Float, default: 1):
A scaling factor used to reduce aggregation tile size; using a larger tileScale
(e.g. 2 or 4) may enable computations that run out of memory with the default.

"2 or 4" still yields errors, until you get on up to tileScale: 16...

//training region is the full image
var training = objectPropertiesImage.sample({
   region: geometry,
   scale: 10,
   tileScale: 16,
   numPixels: 1000
})

print('count',  
   objectPropertiesImage.reduceRegion({
      reducer: ee.Reducer.count(),
      geometry: geometry,
      scale: 10,
      tileScale: 16, 
      maxPixels: 1e13
})
)

But then the issue goes from memory to timeouts. So I think to get this to work you'll need to export to an asset, which will allow for longer computation times.

Export.image.toAsset(classification);

Which will take awhile, but should eventually complete.

https://code.earthengine.google.com/af4413b32e1de7c2d227b7cb5f8731dd?noload=1

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.