Cloud masking
Wolkbedekking
Wolkbedekking is een grote barrière tijdens het analyseren en verwerken van (spectrale) satellietbeelden. Recente satellietdata komen vaak met automatische classificaties van de wolkbedekking, waardoor deze relatief eenvoudig uit het beeld verwijderd kan worden.
Earth Engine bevat naast deze standaard ‘cloud masks’ ook algoritmes om de wolken en wolkschaduw te verwijderen uit het beeld. Een keten van filter-, maskeer- en reduce-strategieën kan de aanwezigheid van wolken minimaliseren. Volgende drie stappen kunnen onderdeel zijn van deze keten:
- Filteren op maximaal wolkenpercentage
- Cloudmasking-algoritme toepassen om wolken te verwijderen
- Reduceren met een mediane reducer van de resterende collectie
1. Cloud Masking van Landsat data
1.1 Filteren van de ImageCollection op wolkbedekking
Een eerste optie is om een beeldcollectie te filteren (zie voorgaand hoofdstuk) op wolkbedekking, waardoor enkel de beelden binnen een bepaalde range van wolkenpercentages worden weerhouden:
// Filteren van de Landsat 9 collectie tot beelden met maximaal 40% wolkbedekking
L9 = L9.filter(ee.Filter.lt('CLOUD_COVER', 40))
ee.filter()
Bij de voorgaande filters gebruikten we de eenvoudige functies .filterBounds()
en .filterDate()
, twee standaardfilters om op respectievelijk locatie (van een geometrie) en datum te filteren.
De .filter()
methode wordt gebruikt om te filteren op eender welke metadata-eigenschap die een beeldcollectie bevat. Binnen de filter-functie wordt vervolgens een Earth Engine filter-functie toegepast, die aangeeft op welke manier er gefilterd moet worden. De ee.Filter.lt('CLOUD_COVER', 40)
('less than') weerhoudt dus alle beelden met minder dan 40% wolkbedekking.
Gebruik de Docs om het gebruik van deze functie verder te bekijken.
Beschikbare metadata kan steeds in de Earth Engine Catalog worden geraadpleegd. Voor Landsat 9 is dit: https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LC09_C02_T1_L2#image-properties.
1.2 Cloud Mask functies
Als tweede stap kunnen de overgebleven wolken/wolkschaduwen per beeld worden ‘geknipt’ (cloudmask) door deze pixels naar een waarde 0 om te zetten. Voor Landsat 8 en 9 wordt dit gedaan op basis van het CFMask-algoritme, dat automatische detecties van wolken, wolkschaduwen en sneeuw of ijs uitvoert. Dit resultaat zit vervat in de metadata van elk beeld als binair raster, wat gebruikt kan worden om de wolken weg te knippen.
Toepassen van de cloudmask voor Landsat 9:
// TO DO: LAAD DE L9 collectie in, filter op locatie en gewenste data (bv Gent, zomer 2024)
var L9 = ... // Vul hier de start van je script aan.
// 1. Voeg een extra filter in op basis van wolkbedekking ('Cloud_cover')
L9 = L9.filter(ee.Filter.lt('CLOUD_COVER', 40))
// Definiëren van CloudMask-functie (gegeven)
function maskL89sr(image) {
// Gebaseerd op de QA-waarde, wat de uitkomst is van het FMASK algoritme
// Develop masks for unwanted pixels (fill, cloud, cloud shadow).
// De waarden voor de eerste 4 QA_pixelbanden moeten gelijk zijn aan 0 (dus geen wolken/schaduw aanwezig)
var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
var saturationMask = image.select('QA_RADSAT').eq(0);
// Apply masks.
return image.updateMask(qaMask).updateMask(saturationMask);
}
// Pas de functie over elk beeld binnen de collectie toe met de .map-functie:
var L9_masked = L9.map(maskL89sr);
// Eerste beeld uit collectie zonder Cloudmask:
Map.addLayer(L9.first(), trueColor, 'L9 - 1e beeld - No Cloudmask')
// Eerste beeld uit collectie mét Cloudmask:
Map.addLayer(L9_masked.first(), trueColor, 'L9 - 1e beeld - met Cloudmask')
Resulterend is een ImageCollectie met dezelfde beelden, maar waaruit de wolken gemaskeerd zijn (mask toegepast). Echter kunnen sommige wolkenranden nog zichtbaar zijn, die de mask-functies hebben gemist.
Opdracht 3.3
Maak de L9_masked-collectie aan, en neem hiervan een .median() reducer. Visualiseer dit beeld. Merk je een verbetering in vergelijking met de voorgaande .median()-gereduceerde beelden, zonder de cloudmask?
De .map()-functie
In bovenstaand voorbeeld werd de cloudmask-functie toegepast door gebruik te maken van .map()
. De .map()
wordt steeds gebruikt om een functie (die op afzonderlijke beelden dient toegepast te worden, zoals maskL9sr
) toe te passen over elk beeld binnen een ImageCollection afzonderlijk. Het is als het ware een veel efficiëntere manier dan de aangemaakte functie te itereren via een for-loop.
2. Cloud Mask van Sentinel-2 data
Om wolken en wolkenschaduwen in Sentinel-2 beelden in Google Earth Engine te verwijderen, maken we gebruik van de Cloud+ strategie. Een interessante Mediumpost over dit algoritme kun je hier vinden: https://medium.com/google-earth/all-clear-with-cloud-score-bd6ee2e2235e.
Hiervoor wordt er gebruik gemaakt van een bijkomende collectie: de Cloud Score + Image Collection. Deze collectie bevat voor elk Sentinel-2 beeld een bepaalde score waarbij de kans op wolken, wolkenschaduwen of andere atmosferische verstoringen wordt gegeven.
Om dus tot een collectie te komen waarbij de 'cloudmask' is toegepast, gebruik je dus onderstaande code. Hierbij kun je de variable CLEAR_THRESHOLD
verhogen om tot een strengere cloudmask te komen. In de functie kun je ook de herschalingsparameters toevoegen
var S2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');
var csPlus = ee.ImageCollection('GOOGLE/CLOUD_SCORE_PLUS/V1/S2_HARMONIZED');
// Aanmaken van treshold + maskfunctie + Herschalingsfunctie
var CLEAR_THRESHOLD = 0.6
var applyCloudMask = function(img){
return img.updateMask(img.select('cs').gte(CLEAR_THRESHOLD))
.multiply(0.0001) //
.copyProperties(img, ["system:time_start"]);
}
// Toepassen van Sentinel-2 Cloudmask:
var S2_coll = S2
.linkCollection(csPlus, ['cs']) // Toevoegen CloudScore aan collectie
// .filter... => Voeg hier eerst je andere filters toe, voor je de cloudmask toepast. (Computationeel beter)
.map(applyCloudMask) // Uitvoeren Cloudmask
// Vervolgens reducer
.median()
Vervolgens kun je met de `S2_coll
verderwerken, waarbij de wolken verwijderd werden.
maskS2clouds
In het voorbeeldscriptje in de Sentinel-2 data catalogus van Earth Engine staat ook een voorbeeldscriptje gebruik makend van de functie maskS2clouds
:
/**
* Function to mask clouds using the Sentinel-2 QA band
* @param {ee.Image} image Sentinel-2 image
* @return {ee.Image} cloud masked Sentinel-2 image
*/
function maskS2clouds(image) {
var qa = image.select('QA60');
// Bits 10 and 11 are clouds and cirrus, respectively.
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 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 image.updateMask(mask).divide(10000).copyProperties(image, ["system:time_start"]);
}
Deze functie werkt niet voor alle Sentinel-2 beelden na Januari 2022. Gezien na deze datum de preprocessing algoritmen wat gewijzigd zijn, zijn ook bitmasks aangepast, waardoor bovenstaande code niet meer zorgt voor het gewenste resultaat.
Om dit op te lossen, maak je best gebruik van de Cloud+ score collectie zoals hierboven beschreven. Deze methode, die gebruik maakt van externe wolkbedekkingsdetectie-algoritmen (mooi woord voor scrabble), is nauwkeurigere resolutie.