Here is a more flexible approach that can handle dual (or larger) bit patterns. The bit shifts are performed server-side, using the ee.Image.rightShift() and ee.Image.mod() methods.
var RADIX = 2; // Radix for binary (base 2) data.
var extractQABits = function (qaBand, bitStart, bitEnd) {
var numBits = bitEnd - bitStart + 1;
var qaBits = qaBand.rightShift(bitStart).mod(Math.pow(RADIX, numBits));
return qaBits;
};
Once the bits are extracted, you can test them against a desired pattern using comparison operators (such as ee.Image.lte())
Below is a (verbose) example of masking out clouds and shadows in a Landsat 8 TOA image using its quality band (BQA). Clouds can be identified using the "Cloud Confidence" bits:
Bits 5-6: Cloud Confidence
0: Not Determined / Condition does not exist. \
1: Low, (0-33 percent confidence)
2: Medium, (34-66 percent confidence)
3: High, (67-100 percent confidence)
and shadows can be identified using the "Cloud Shadow Confidence" bits:
Bits 7-8: Cloud Shadow Confidence
0: Not Determined / Condition does not exist.
1: Low, (0-33 percent confidence)
2: Medium, (34-66 percent confidence)
3: High, (67-100 percent confidence)
Here is the full script which displays the final masked image an various intermediate step as map layers:
// Landsat 8 Cloud Masking Example
var RADIX = 2; // Radix for binary (base 2) data.
// Reference a sample Landsat 8 TOA image.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_043034_20180202');
// Extract the QA band.
var image_qa = image.select('BQA');
var extractQABits = function (qaBand, bitStart, bitEnd) {
var numBits = bitEnd - bitStart + 1;
var qaBits = qaBand.rightShift(bitStart).mod(Math.pow(RADIX, numBits));
//Map.addLayer(qaBits, {min:0, max:(Math.pow(RADIX, numBits)-1)}, 'qaBits');
return qaBits;
};
// Create a mask for the dual QA bit "Cloud Confidence".
var bitStartCloudConfidence = 5;
var bitEndCloudConfidence = 6;
var qaBitsCloudConfidence = extractQABits(image_qa, bitStartCloudConfidence, bitEndCloudConfidence);
// Test for clouds, based on the Cloud Confidence value.
var testCloudConfidence = qaBitsCloudConfidence.gte(2);
// Create a mask for the dual QA bit "Cloud Shadow Confidence".
var bitStartShadowConfidence = 7;
var bitEndShadowConfidence = 8;
var qaBitsShadowConfidence = extractQABits(image_qa, bitStartShadowConfidence, bitEndShadowConfidence);
// Test for shadows, based on the Cloud Shadow Confidence value.
var testShadowConfidence = qaBitsShadowConfidence.gte(2);
// Calculate a composite mask and apply it to the image.
var maskComposite = (testCloudConfidence.or(testShadowConfidence)).not();
var imageMasked = image.updateMask(maskComposite);
Map.addLayer(image, {bands:"B4,B3,B2", min:0, max:0.3}, 'original image', false);
Map.addLayer(image.select('BQA'), {min:0, max:10000}, 'BQA', false);
Map.addLayer(testCloudConfidence.mask(testCloudConfidence), {min:0, max:1, palette:'grey,white'}, 'testCloudConfidence');
Map.addLayer(testShadowConfidence.mask(testShadowConfidence), {min:0, max:1, palette:'grey,black'}, 'testShadowConfidence');
Map.addLayer(maskComposite.mask(maskComposite), {min:0, max:1, palette:'green'}, 'maskComposite', false);
Map.addLayer(imageMasked, {bands:"B4,B3,B2", min:0, max:0.3}, 'imageMasked');
Script link: https://code.earthengine.google.com/d1de93debe720642c02b1d5b3a57fb82
Depending on your particular use case, you may want to adjust the sensitivity to clouds and shadows by modifying testCloudConfidence
and testShadowConfidence
.
I think a good approach would be similar to this code sample below Sentinel Cloud-free Collection Google Earth Engine Code Editor
var Jan2016 = ee.ImageCollection('COPERNICUS/S2')
.filterBounds(NSW)
.filterDate('2016-01-01', '2016-01-30');
var trueColor = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000};
var falseColor= {bands: ['B8', 'B3', 'B4'], min: 0, max: 3000};
//Define the CLoud layer (here: QA60 is MSI2's quality mask)
var Clouds = {bands: ['QA60']};
//Add the cloud mask as layer; PROBLEM: SEEMS QA60 is taken from different images every time..
//Clouds is just a band, do i need to specify the threshold?
Map.addLayer(Jan2016, Clouds, "CloudsJan16");
// Mask clouds
var noclouds = Jan2016.map(function(img) {
var mask =
img.select(['QA60']).neq(11);
//.neq(xx) doesn't seem to make a difference
return img.updateMask(mask);
});
//Add the layer/imagery to the map with variable, display type, and name
Map.addLayer(noclouds, trueColor, "NoClouds");
Map.addLayer(Jan2016, falseColor, "Jan2016false");
Best Answer
In optical remote sensing in the visible spectrum you cannot see through clouds. So there is nothing you can do, except to wait for images without clouds.
Cloud masks are (as far as i know) used to exclude clouded areas from (for example) landcover classification, because results there would be incorrect anyways.
edit
As Aaron mentioned, you can sometimes use a cloud mask to insert cloud-free imagery into a cloudy image, but that only works if you a) do have cloud free imagery and b) you don't care that the resulting image is now a mosaic of images from different acquisition dates, and thus you cannot compute change detection etc. on it.