3

I want to use the Landsat BQA (TOA images) to create a cloud mask in GEE. In particular I want to mask out all areas where there is high confidence that cloud cover is present. I have tried to adapt the example script, which utilises the single bit cloud band:

var cloudmaskL8 = function(image) {
   var qa = image.select('BQA');  
   var mask = qa.bitwiseAnd(1 << 4).eq(0);
   return image.updateMask(mask);
}

But I do not understand how the 'bitwiseAnd' function works in this context and I'm therefore unable to adapt it for the double bit confidence bands.

How does the 'bitwiseAnd' function work in this context?

1
  • I have made a BitReader for the Python API that can decode any bit codification, just passing as an argument a dict holding the same info that shows the 'bit table', you can find it in github.com/gee-community/gee_tools. I am not posting it as an answer because you use the JS API Commented Aug 15, 2018 at 14:00

2 Answers 2

9

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

enter image description here

Depending on your particular use case, you may want to adjust the sensitivity to clouds and shadows by modifying testCloudConfidence and testShadowConfidence.

4

You need to know the bit pattern, in this case, pattern for bit 4 is 16 (2 at the power of 4). So:

var cloudmaskL8 = function(image) {
   var qa = image.select('BQA');
   var pattern = ee.Number(2).pow(4).toInt();
   var mask = qa.bitwise_and(pattern).rightShift(4);
   return image.updateMask(mask.not());
}

Then you can make a function to decode any other bit.

3
  • How can I adapt this to a double bit condition? For example: Say I have the number 2784 (000 01 01 01 11 0 00 0 0) - the 6th and 7th character from the right (11) represent high confidence cloud cover. How can I adapt the above code to recognise this double bit condition and not just recognise it as a single bit condition?
    – Wilson
    Commented Aug 16, 2018 at 13:58
  • This is why I created the BitReader in geetools. Don't have the time to make it in JS. But you can look at the source code and 'translate' it to JS (at least the main function). Commented Aug 16, 2018 at 14:32
  • @RodrigoE.Principe Can you please explain where in the function can I select the bit (cloud score or shadow score) as well as, where do I set the values that I would like to mask out?
    – OscarBau
    Commented Aug 12, 2019 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.