0

I am a GEE and JavaScript novice.

I am trying to calculate a specific fire index for a collection of sentinel 2 images so as to map burn scars throughout a one year period. I then want to apply a mask on all the images when this particular index exceeds a certain value. Finally I want to combine this collection of masked images into a single image with a mask on any pixel that satisfied the threshold range for the burn index within the specified date range. This is where I am at in terms of initialising the process.

var sentinel2series = ee.ImageCollection(sentinel2.filterDate('2019-01-01', '2020-01-01').filterBounds(geometry).select('B12', 'B11', 'B8'));
print(sentinel2series)


  // Function to calculate and add a MIRBI band
var addMIRBI = function(image) {
  var MIRBI = image.expression(
  '10*(SWIR2)-9.8*(SWIR1)+2', {
        'SWIR2' : image.select('B12'),
        'SWIR1' : image.select('B11')
         })
          .rename('MIRBI')
   return image.addBands(MIRBI);
  };
  
var serieswithBIndex = sentinel2series.map(addMIRBI)
print(serieswithBIndex)

I have tried various iterating functions on my images but haven't had much luck. The addition of the burn index band seems to work fine as when I view the collection each image has a new band titled 'MIRBI'. I just cannot get across the line with masking the images once they exceed a threshold for that value and then combining those masks into a singular image that is an aggregate of all the masks.

I have applied the process detailed below by Rodrigo Lustosa, and I have since tried to run the script there and it was displaying something like what I am looking for. However I know it's not quite right because there is a fire in particular that I am trying to capture and I know it's outline shape (ie. it's been groundtruthed and it is my way of QA) but it seemed to get obscured in the mask - probably by erroneous clouds I guessed.

I since applied a cloud mask to the ImageCollection after it had the MIRBI band appended to each image, and then did the same process as outlined below with using the Reducer to get the maximum MIRBI value for each pixel. However now the area where there should be a mask indicating fire (ie. where the groundtruthed fire is and I know there is a fire scar) there isn't a mask. Is it possible that something with my cloud masking has interfered with my subsequent masking of the MIRBI threshold?

Here's my working script

Map.centerObject(geometry);

var sentinel2series = ee.ImageCollection(sentinel2.filterDate('2019-01-01', '2020-01-01').filterBounds(geometry).select('B12', 'B11', 'B8', 'B4', 'B3', 'B2', 'QA60'));
print(sentinel2series)

  // Function to calculate and add a MIRBI band
var addMIRBI = function(image) {
  var MIRBI = image.expression(
  '(10*SWIR2)-(9.8*SWIR1)+2', {
        'SWIR2' : image.select('B12'),
        'SWIR1' : image.select('B11')
         })
          .rename('MIRBI')
   return image.addBands(MIRBI);
  };
  
var serieswithMIRBI = sentinel2series.map(addMIRBI)
print(serieswithMIRBI)


function maskS2sr(image) {
 var cloudShadowBitMask = (2<<10);
 var cloudsBitMask = (2<<11);
 // Get the pixel QA band.
 var qa = image.select('QA60');
 // 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);
}

// mask clouds and cirrus
var seriesNoClouds = serieswithMIRBI.map(maskS2sr);

var certainValue = -5000;
var othervalue = 0;
// get max value for each pixel
var final_image = seriesNoClouds.select('MIRBI')
  .reduce(ee.Reducer.max());
// mask every pixel which was greater than certainValue in any image
var final_image_masked = final_image
  .updateMask(final_image.gt(certainValue).and(final_image.lt(othervalue)));
// view example on the map
Map.addLayer(final_image_masked,{},'max MIRBI masked');
print(final_image_masked)

I am reasonably confident in my threshold values, at least for the ROI that I am looking where I have validated them with a fire scar that I know is there in that image series.

2

1 Answer 1

2

I'm answering based on what I understood of your question, but maybe you also want to make a cloud mask and/or to reescale the values (cose the data don't come in the right scale). I didn't make these changes, so the index might appear weird, with strange values. Here is the code you provided (I just made some changes so it's reproducible, I picked a case over Brazil and I assumed you used the Image Collection below).

var sentinel2 = ee.ImageCollection("COPERNICUS/S2_SR");
var geometry = ee.Geometry.Point([-57.23006235092618, -13.960284001225315]);
Map.centerObject(geometry,9); //change map position to be over geometry

var sentinel2series = sentinel2
  .filterDate('2019-01-01', '2020-01-01')
  .filterBounds(geometry)
  .select('B12', 'B11', 'B8');
print(sentinel2series);

  // Function to calculate and add a MIRBI band
var addMIRBI = function(image) {
  var MIRBI = image.expression(
  '10*(SWIR2)-9.8*(SWIR1)+2', {
        'SWIR2' : image.select('B12'),
        'SWIR1' : image.select('B11')
         })
          .rename('MIRBI');
   return image.addBands(MIRBI);
  };
  
var serieswithBIndex = sentinel2series.map(addMIRBI);
print(serieswithBIndex);

I understood that the first thing you wanted was to mask (in other words, to remove) pixels that are greater than a certain value. I chose a certain value so the mask could be seen in the map. I made a function that pick up the index of an image and make another image with true or false values, as described in the code. Then it is used as a mask. Here it is.

// mask, i.e. remove, pixels with index greater than certainValue
var certainValue = -7853;
var makeMask = function(image){
  // make an image with true or false values
  // true for MIRBI less than or equal (lte) a certainValue
  // false otherwise
  var mask = image.select('MIRBI').lte(certainValue);
  // remove false values, i.e, remove MIRBI greater than certainValue
  return image.updateMask(mask);
};
var serieswithBIndex_masked = serieswithBIndex.map(makeMask);

// view example on the map
Map.addLayer(serieswithBIndex.first().select('MIRBI'),{min:-12000,max:1000},'MIRBI unmasked');
Map.addLayer(serieswithBIndex_masked.first().select('MIRBI'),{min:-12000,max:1000},'MIRBI masked');

The second thing I understood that you want is to make an image with all the masked pixels from the images above also masked in this new image. For this, you don't need to use the serieswithBIndex_masked variable made above. You can first pick up the max value over each pixel then see if any pixels got greater than your certain value. Again I chose another certain value so the mask could be seen on the map.

certainValue = 5000;
// get max value for each pixel
var final_image = serieswithBIndex.select('MIRBI')
  .reduce(ee.Reducer.max());
// mask every pixel wich was greater than certainValue in any image
var final_image_masked = final_image
  .updateMask(final_image.lte(certainValue));
// view example on the map
Map.addLayer(final_image,{min:0,max:10000},'max MIRBI unmasked');
Map.addLayer(final_image_masked,{min:0,max:10000},'max MIRBI masked');

Hope this can answer you or at least some of your questions.

2
  • Thanks for your help there Rodrigo Lustosa! Yes I think you got the gist of what I was aiming for perfectly. I since tried to get to run your script there and it was displaying something like what I am looking for. However I know it's not quite right because there is a fire in particular that I am trying to capture and I know it's outline shape (ie. it's been groundtruthed and it is my way of QA) but it seemed to get obscured in the mask - probably by clouds I would guess.
    – samm
    Aug 30, 2021 at 2:16
  • @samm My pleasure! This is possible, it could be clouds. Maybe you could use a shorter period of time, around your case, and plot some bands of the images on the map (using .first() and .select('bandName') as I did) to see what is going on. Aug 30, 2021 at 15:04

Your Answer

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

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