I am trying to mask clouds in a Sentinel 2 image collection following this procedure:


I have adapted it slightly to suit my parameters and roi etc. I am then trying to add a fire index band to the newly masked image collection and then finally fill the null data gaps (where the mask exists) with a constant value. Here is my code so far:


var sentinel2series = ee.ImageCollection(sentinel2
        .filterDate('2019-01-01', '2020-01-01')
        .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 90)
        .select('B12', 'B11', 'B8', 'QA60', 'SCL'));

var s2_cloudless_col = (ee.ImageCollection('COPERNICUS/S2_CLOUD_PROBABILITY')
        .filterDate('2019-01-01', '2020-01-01'))
var joined = ee.ImageCollection(ee.Join.saveFirst('s2cloudless').apply(  {
        'primary': sentinel2series,
        'secondary': s2_cloudless_col,
        'condition': ee.Filter.equals(  {
            'leftField': 'system:index',
            'rightField': 'system:index'


var NIR_DRK_THRESH = 0.15
var CLD_PRJ_DIST = 1
var BUFFER = 50

function add_cloud_bands(image) {
    // Get s2cloudless image, subset the probability band.
    var cld_prb = ee.Image(image.get('s2cloudless')).select('probability')

    // Condition s2cloudless by the probability threshold value.
    var is_cloud = cld_prb.gt('CLD_PRB_THRESH').rename('clouds')

    // Add the cloud probability layer and cloud mask as image bands.
    return image.addBands(ee.Image([cld_prb, is_cloud]))

function add_shadow_bands(image) {
    // Identify water pixels from the SCL band.
    var not_water = image.select('SCL').neq(6)

    // Identify dark NIR pixels that are not water (potential cloud shadow pixels).
    var SR_BAND_SCALE = 1e4
    var dark_pixels = image.select('B8').lt('NIR_DRK_THRESH'*'SR_BAND_SCALE').multiply(not_water).rename('dark_pixels')

    // Determine the direction to project cloud shadow from clouds (assumes UTM projection).
    var shadow_azimuth = ee.Number(90).subtract(ee.Number(image.get('MEAN_SOLAR_AZIMUTH_ANGLE')));

    // Project shadows from clouds for the distance specified by the CLD_PRJ_DIST input.
    var cld_proj = (image.select('clouds').directionalDistanceTransform(shadow_azimuth, 'CLD_PRJ_DIST'*10)
        .reproject(  {'crs': image.select(0).projection(), 'scale': 100})

    // Identify the intersection of dark pixels with cloud shadow projection.
    var shadows = cld_proj.multiply(dark_pixels).rename('shadows')

    // Add dark pixels, cloud projection, and identified shadows as image bands.
    return image.addBands(ee.Image([dark_pixels, cld_proj, shadows]))

function add_cld_shdw_mask(image) {
    // Add cloud component bands.
    var img_cloud = add_cloud_bands(image)

    // Add cloud shadow component bands.
    var img_cloud_shadow = add_shadow_bands(img_cloud)

    // Combine cloud and shadow mask, set cloud and shadow as value 1, else 0.
    var is_cld_shdw = img_cloud_shadow.select('clouds').add(img_cloud_shadow.select('shadows')).gt(0)

    // Remove small cloud-shadow patches and dilate remaining pixels by BUFFER input.
    // 20 m scale is for speed, and assumes clouds don't require 10 m precision.
    is_cld_shdw = (is_cld_shdw.focal_min(2).focal_max('BUFFER'*2/20)
        .reproject(  {'crs': image.select([0]).projection(), 'scale': 20})

    // Add the final cloud-shadow mask to the image.
    return img_cloud_shadow.addBands(is_cld_shdw)

function apply_cld_shdw_mask(image) {
    // Subset the cloudmask band and invert it so clouds/shadow are 0, else 1.
    var not_cld_shdw = image.select('cloudmask').not()

    // Subset reflectance bands and update their masks, return the result.
    return image.select('B.*').updateMask(not_cld_shdw)

var s2_sr = joined.map(add_cld_shdw_mask)

var addMIRBI = function(image) {
  var MIRBI = image.expression(
  '(10*SWIR2)-(9.8*SWIR1)+2', {
        'SWIR2' : image.select('B12'),
        'SWIR1' : image.select('B11')
   return image.addBands(MIRBI);

var cloudlessSerieswithMIRBI = s2_sr.map(addMIRBI) 

var JustMIRBIseries = cloudlessSerieswithMIRBI.select('MIRBI')

function interpolate(image) {
  var meanMIRBI = -0.68361706349206
  return image.unmask(meanMIRBI)

var completeSeries = JustMIRBIseries.map(interpolate)

However, my issue is that when I try to even visualise one of the images from the masked collection (using .first()), even before adding the burn index band, I get an error saying Parameter value is required. As I understand this occurs when there is no data to display in the variable that you are trying to visualise. However, this shouldn't be the case as there should still be an image that just has null data areas where the clouds have been masked.

Does anyone know what the issue is that I am encountering here?

  • Do you have a link to a script so that we can reproduce your error? Else we can't help you out here...
    – Jobbo90
    Sep 9, 2021 at 8:12

1 Answer 1


I think your issue is related to how you call your global variables (lines 28-32) inside your functions. You used parentheses to call these variables, but actually you shouldn't. Like I suggested it is hard to reproduce your error without a working example but I think this should do the trick:


  • That worked like a charm - thank you. Out of curiosity, how come in the buffering section You didn't remove BUFFER from inverted commas like you did with the other global variable? Regardless, it seemed to work fine. Cheers
    – samm
    Sep 9, 2021 at 22:03
  • Probably because I forgot and because the variable is already declared before that it doesn't throw an error when you use BUFFER like that. Probably change that ;) If you can except the answer that would be helpful
    – Jobbo90
    Sep 10, 2021 at 7:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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