0

I am trying to distinguish seagrass from seaweed (macroalgae) and ultimately map seagrass around Britain. I am aware it may not be possible because it is challenging to differentiate seagrass and seaweed. The following code produces a 'classified' layer which has failed to actually distinguish anything (purple layer). Is there anything obvious I am doing wrong here? I will try to downscale the project to a smaller region if not.

//--------------------------------
// PRE-CLASSIFICATION 
//--------------------------------

// Prepare images
var polygon = GB
var filteredImage = GB_coastraw.filterBounds(polygon);
//print(filteredImage);

// LAND MASKING
// Import the Hansen et al. forest change dataset
var hansenImage = ee.Image('UMD/hansen/global_forest_change_2015');

//land mask
var datamask = hansenImage.select('datamask');

//binary mask
var mask = datamask.eq(1);

//get the median first
var dataset = ee.ImageCollection('COPERNICUS/S2_SR')
var median_2021 = dataset.filterDate('2021-06-01', '2021-09-01').median();

// back to binary mask
var maskedComposite = median_2021.updateMask(mask);

// CLOUD MASKING Provided by Qiuyang

/**
 * Function to mask clouds using the Sentinel-2 QA band
 * @param {ee.Image} image Sentinel-2 image
 * @return {ee.Image} cloud masked Sentinel-2 image
 */
 
Map.centerObject(GB);
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);
}

// Map the function from June - September of data and take the median.
// Load Sentinel-2 TOA reflectance data.
var collection = ee.ImageCollection('COPERNICUS/S2_SR')
                  .filterBounds(GB)
                  .filterDate('2021-06-01', '2021-09-01')
                  //another way to do it
                  .filterMetadata('CLOUDY_PIXEL_PERCENTAGE','less_than',30);

var unmasked_image = collection.mosaic();
var masked_image = collection.map(maskS2clouds).median();
var final_masked_image = masked_image.clip(GB);
var final_unmasked_image = unmasked_image.clip(GB);

// Upload the macroalgae shapefile from EMODnet
var macroalgae = ee.FeatureCollection('users/rebekahtullis/eov_macroalgalcanopycover_polygons_2019');


//Map.addLayer(final_masked_image,{bands:["B1", "B2", "B3"], min: [0,0,0], max: [256,256,256]}, "summer2021");



//--------------------------------
// CLASSIFICATION 
//--------------------------------

//  PIXELS HAVE BEEN CLASSIFIED AROUND THE GB COAST

// Use the Naïve Bayes classifier equation to show you have correctly assigned pixels 
var NB = ee.Classifier.smileNaiveBayes();

// Getting training data in the correct format for classification
var bands = ['B1','B2','B3','B4', 'B5','B6']
var allPolygons = ee.FeatureCollection([
  ee.Feature(Seagrass, {'class':0}),
  ee.Feature(Macroalgae, {'class':1}),
  ee.Feature(Sand, {'class':2}),
  ee.Feature(Deep_water,{'class':3}),
]);


// Sample the pixel values of your image inside the polygons so that the pixels all comply with the classification 

var training2021 = final_masked_image.sampleRegions({
  collection: allPolygons,
  properties: ['class'],
  scale: 30
})


// Training the classifier
var trained2021 = NB.train(training2021, 'class', bands);

// Classify image
var classified2021 = final_masked_image.classify(trained2021);



//--------------------------------

// OUTPUTS

//--------------------------------

// PRE-CLASSIFICATION
//Map.addLayer(image,{bands:["B4", "B3", "B2"], min: [50,50,40], max: [665,560,490]}, "summer2021");
Map.addLayer(final_masked_image,{min:0, max:0.12, bands: ['B2', 'B3', 'B4']}, 'Cloudmask_GB_summer_2021');
Map.addLayer(maskedComposite,{bands: ["B4", "B3", "B2"]}, 'land_mask');
Map.addLayer(macroalgae, {min:0, max:0.1}, 'macroalgae');

// CLASSIFICATION

// Adding the classified layer
Map.addLayer(classified2021, {min:0, max:0.12,palette:['green','yellow','blue','purple']}, 'classified_2021')

https://code.earthengine.google.com/496728e1f974b63d3aa5725c3c6579ea

1 Answer 1

2

For starters, your max value for the classification visualization looks wrong. The max should be 3.

For picking up the differences, you could try out different classifier, like smileRandomForest(), and see which works best. You could also try to help the classifier a bit by adding some indexes or band ratios to the composite you're classifying.

But at the end of the day, I think maybe your biggest problem will be left-over clouds and haze from your masking step. You could perhaps improve on the cloud masking with the COPERNICUS/S2_CLOUD_PROBABILITY collection, but I'm not sure it's efficient enough over water.

One workaround could perhaps be to visualize the composite you're classifying, ensure your reference data isn't cloudy or hazy in any way, then collect additional reference data for a cloudy/hazy class.

For your reference, here's how you can use the cloud probability collection:

var collection = ee.ImageCollection(
  ee.Join.saveFirst('cloudProbability').apply({
    primary: ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED').filter(filter),
    secondary: ee.ImageCollection('COPERNICUS/S2_CLOUD_PROBABILITY').filter(filter),
    condition: ee.Filter.equals({
      leftField: 'system:index',
      rightField: 'system:index'
    })
  })
).map(function(image) {
  var cloudFree = ee.Image(image.get('cloudProbability')).lt(cloudThreshold)
  return image
    .select(
      ['B2', 'B3', 'B4', 'B8', 'B11', 'B12'],
      ['blue', 'green', 'red', 'nir', 'swir1', 'swir2']
    )    
    .updateMask(cloudFree)
})

https://code.earthengine.google.com/0cf0afea089ff9d4e73f9c223e6c087e

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.