Two cloud masks for sentinel 2
1. Create a new script called cloud_module with the code below and save in your repository
var cloudScoreThresh = 20; var cloudScorePctl = 0; var contractPixels = 1.5; var dilatePixels = 2.5; //////////////////////////////////////////////////////////////////////////////// // Compute a cloud score and adds a band that represents the cloud mask. // Cloud masking algorithm for Sentinel2. Built on ideas from Landsat cloudScore // algorithm. Currently in beta and may need tweaking for individual study areas exports.sentinelCloudScore = function(s2s) { function getCloudScore(img){ // Compute several indicators of cloudyness and take the minimum of them. var score = ee.Image(1); var blueCirrusScore = ee.Image(0); // Clouds are reasonably bright in the blue or cirrus bands. //Use .max as a pseudo OR conditional blueCirrusScore = blueCirrusScore.max(rescale(img, 'img.blue', [0.1, 0.5])); blueCirrusScore = blueCirrusScore.max(rescale(img, 'img.cb', [0.1, 0.5])); blueCirrusScore = blueCirrusScore.max(rescale(img, 'img.cirrus', [0.1, 0.3])); score = score.min(blueCirrusScore); // Clouds are reasonably bright in all visible bands. score = score.min(rescale(img, 'img.red + img.green + img.blue', [0.2, 0.8])); // Clouds are reasonably bright in all infrared bands. score = score.min(rescale(img, 'img.nir + img.swir1 + img.swir2', [0.3, 0.8])); // However, clouds are not snow. var ndsi = img.normalizedDifference(['green', 'swir1']); score = score.min(rescale(ndsi, 'img', [0.8, 0.6])); score = score.multiply(100).byte(); score = score.clamp(0,100); return img.addBands(score.rename(['cloudScore'])); } function maskScore(img){ //var cloudMask = img.select(['cloudScore']).subtract(minCloudScore).lt(cloudScoreThresh) // .focal_max(contractPixels).focal_min(dilatePixels).rename('cloudMask'); var cloudMask = img.select(['cloudScore']).lt(cloudScoreThresh).focal_max(contractPixels).focal_min(dilatePixels).rename('cloudMask'); return img.updateMask(cloudMask).addBands(cloudMask); } s2s = s2s.map(getCloudScore); // Find low cloud score pctl for each pixel to avoid comission errors var minCloudScore = s2s.select(['cloudScore']).reduce(ee.Reducer.percentile([cloudScorePctl])); s2s = s2s.map(maskScore); return s2s; }; //////////////////////////////////////////////////////////////////////////////// // Function to mask clouds using the Sentinel-2 QA band. exports.QAMaskCloud= function(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); } collection = collection.map(maskClouds); return collection; }; //////////////////////////////////////////////////////////////////////////////// // Helper function to apply an expression and linearly rescale the output. // Used in the sentinelCloudScore function below. function rescale(img, exp, thresholds) { return img.expression(exp, {img: img}) .subtract(thresholds[0]).divide(thresholds[1] - thresholds[0]); }
link
2. add the code below to your main script:
var clouds = require("users/user/landcoverS2:cloud_module"); print("applying QA cloud mask"); s2 = clouds.QAMaskCloud(s2); Map.addLayer(ee.Image(s2.first()),{min:0,max:0.3,bands:"swir2,nir,red"},"after QA cloudmask"); print(ee.Image(s2.first())); print("applying sentinel cloud mask"); s2 = clouds.sentinelCloudScore(s2); Map.addLayer(ee.Image(s2.first()),{min:0,max:0.3,bands:"swir2,nir,red"},"after s2 cloudmask"); print(ee.Image(s2.first()));