A modular land cover system part 2: shadow masking
Step 1: create a new script and call it shadowMask_module
Step 2: copy the code below and store it in the script
// zScoreThresh: Threshold for cloud shadow masking- lower number masks out var zScoreThresh = -1.80; // shadowSumThresh: Sum of IR bands to include as shadows within TDOM and the // shadow shift method (lower number masks out less) var shadowSumThresh = 0.20; // contractPixels: The radius of the number of pixels to contract (negative // buffer) clouds and cloud shadows by. Intended to eliminate smaller cloud // patches that are likely errors // (1.5 results in a -1 pixel buffer)(0.5 results in a -0 pixel buffer) // (1.5 or 2.5 generally is sufficient) var contractPixels = 1.5; // dilatePixels: The radius of the number of pixels to dilate (buffer) clouds // and cloud shadows by. Intended to include edges of clouds/cloud shadows // that are often missed // (1.5 results in a 1 pixel buffer)(0.5 results in a 0 pixel buffer) // (2.5 or 3.5 generally is sufficient) var dilatePixels = 2.5; //////////////////////////////////////////////////////////////////////////////// // Function for finding dark outliers in time series. // Original concept written by Carson Stam and adapted by Ian Housman. // Adds a band that is a mask of pixels that are dark, and dark outliers. exports.shadowMask = function(collection,studyArea) { var inBands = ["B8",'B11']; var shadowSumBands = ['nir','swir1']; var allCollection = ee.ImageCollection('COPERNICUS/S2').filterBounds(studyArea) .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',70)) .map(maskClouds).select(inBands,shadowSumBands); // allCollection = allCollection.map(function(img){return img.divide(10000)}); // Get some pixel-wise stats for the time series var irStdDev = allCollection.select(shadowSumBands).reduce(ee.Reducer.stdDev()); var irMean = allCollection.select(shadowSumBands).mean(); var maskDarkOutliers = function(img){ var zScore = ee.Image(img.select(shadowSumBands)).subtract(irMean).divide(irStdDev); var irSum = img.select(shadowSumBands).reduce(ee.Reducer.sum()); var TDOMMask = zScore.lt(zScoreThresh).reduce(ee.Reducer.sum()).eq(2).and(irSum.lt(shadowSumThresh)); TDOMMask = TDOMMask.focal_min(contractPixels).focal_max(dilatePixels).rename('TDOMMask'); return img.updateMask(TDOMMask.not()).addBands(TDOMMask).addBands(irSum).addBands(zScore); }; // Mask out dark dark outliers collection = collection.map(maskDarkOutliers); return collection; }; function maskClouds(image){ var qa = image.select('QA60').int16(); // Bits 10 and 11 are clouds and cirrus, respectively. var cloudBitMask = Math.pow(2, 10); var cirrusBitMask = Math.pow(2, 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 the masked and scaled data. return image.updateMask(mask); }
Step 4: Open the main script and add the lines below
print("applying cloud shadow mask"); s2 = shadowS2.shadowMask(??,??); Map.addLayer(ee.Image(s2.first()),{min:0,max:0.6,bands:"??,??,??","ShadowMask"); print(ee.Image(s2.first()));
Step 5: Run the script