colab Open in SageMaker Studio Lab Open in Planetary Computer

Clouds/Shadows Masking on Sentinel-2 Surface Reflectance Product

Tutorial created by **David Montero Loaiza**: GitHub | Twitter

Let’s start!

If required, please uncomment:

[1]:
#!pip install eemont
#!pip install geemap

Import the required packages.

[2]:
import ee, eemont, geemap

Authenticate and Initialize Earth Engine and geemap.

[3]:
Map = geemap.Map()

Example point of interest to filter the image collection.

[4]:
point = ee.Geometry.Point([-75.92, 2.86])

Get and filter the Sentinel-2 Surface Reflectance image collection and filter it by region and time.

[5]:
S2 = (ee.ImageCollection('COPERNICUS/S2_SR')
      .filterBounds(point)
      .filterDate('2020-01-01','2021-01-01'))

Clouds/Shadows Masking

Multiple options are included in this feature, let’s take a look at them:

1. Quality Assessment masking

Here, the Quality Assessment band is used for masking.

[6]:
S2a = S2.maskClouds(method = 'qa')

2. Quality Assessment masking (without masking cirrus clouds)

By default, cirrus clouds are masked, but if required, the maskCirrus parameter can be set to False.

[7]:
S2b = S2.maskClouds(method = 'qa', maskCirrus = False)

3. Cloud Probability masking

Cloud Probability masking is the default method to mask clouds and shadows in Sentinel-2 (method = ‘cloud_prob’). By default, the Cloud Probability threshold is 60% (prob = 60).

[8]:
S2c = S2.maskClouds()

4. Cloud Probability masking with a different threshold value

The threshold value for Cloud Probability can be changed. Values in the range [0.0, 100.0] are accepted.

[9]:
S2d = S2.maskClouds(prob = 75)

5. Cloud Probability masking with a different buffer value

By default, clouds and shadows are dilated by a 250 m buffer to avoid border effects. This value can be changed by modifying the buffer parameter.

[10]:
S2e = S2.maskClouds(buffer = 500)

6. Cloud Probability masking with a different NIR threshold

By default, NIR values under 0.15 are considered potential cloud shadows. This value can be changed by modifying the dark parameter.

[11]:
S2f = S2.maskClouds(dark = 0.1)

7. Cloud Probability masking with a different shadow search range

By default, cloud shadows are searched within a 1000 m distance from cloud edges in the shadow direction. This value can be changed by modifying the cloudDist parameter.

[12]:
S2g = S2.maskClouds(cloudDist = 2000)

8. Cloud Probability masking using the Cloud Displacement Index (CDI)

CDI is an index used to avoid masking bright surfaces as clouds. By default, CDI is not used, but if required, the CDI value can be changed by modifying the cdi parameter. CDI values go from -1 to 1. The most used value for cloud masking is -0.5.

[13]:
S2h = S2.maskClouds(cdi = -0.5)

9. Cloud Probability masking (without masking shadows)

By default, shadows are masked as well as clouds, but if required, the maskShadows parameter can be set to False. This option is available for ‘qa’ and ‘cloud_prob’ methods.

[14]:
S2i = S2.maskClouds(maskShadows = False)

10. Cloud Probability masking of a scaled image (pixel values in [0, 1])

By default, clouds and shadows are masked on unscaled image collections, but if the collection is scaled, the scaledImage MUST be set to True.

[15]:
S2j = S2.scale().maskClouds(scaledImage = True)

Visualization

Set the visualization parameters.

[16]:
rgbUnscaled = {'min':0, 'max':3000, 'bands':['B4','B3','B2']}
rgbScaled = {'min':0, 'max':0.3, 'bands':['B4','B3','B2']}

Use geemap to display results.

[17]:
Map.centerObject(point,10)
Map.addLayer(S2.first(),rgbUnscaled,'No Clouds/Shadows masking')
Map.addLayer(S2a.first(),rgbUnscaled,'QA masking')
Map.addLayer(S2b.first(),rgbUnscaled,'QA masking with no cirrus masking')
Map.addLayer(S2c.first(),rgbUnscaled,'60% Cloud Probability masking')
Map.addLayer(S2d.first(),rgbUnscaled,'75% Cloud Probability masking')
Map.addLayer(S2e.first(),rgbUnscaled,'60% Cloud Probability masking with a 500 m dilation')
Map.addLayer(S2f.first(),rgbUnscaled,'60% Cloud Probability masking with a 0.1 NIR threshold')
Map.addLayer(S2g.first(),rgbUnscaled,'60% Cloud Probability masking with a 2000 m cloud shadow search range')
Map.addLayer(S2h.first(),rgbUnscaled,'60% Cloud Probability masking with a CDI of -0.5')
Map.addLayer(S2i.first(),rgbUnscaled,'60% Cloud Probability masking with no shadow masking')
Map.addLayer(S2j.first(),rgbScaled,'60% Cloud Probability masking of a scaled image')
Map