Google Earth Engine – Creating Mean Image from Cloud-Free Images over ROI for Landsat SR Products

cloudmaskinggoogle-earth-engine

I want to filter all the available cloud-free images of the Landsat series of each year separately and find the mean image in Google Earth Engine. As my ROI is very small, I want images that don't have a cloud over my ROI, not on the entire image.

I am using Landsat Collection 2 products that have surface reflectance values. Also, as I'm doing temporal analysis, the Landsat 5, 7, and 8 images are included in this.

I'm doing as bellow in GEE for the Landsat 8,

Map.centerObject(mumbai, 10); 

//load images for composite
var sr = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filter(ee.Filter.eq('WRS_PATH', 148)).filter(ee.Filter.eq('WRS_ROW', 47))
  .filterDate('2013-01-01','2013-12-31')
  .sort('CLOUD_COVER',false);

// Applies scaling factors.
function applyScaleFactors(image) {
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
              .addBands(thermalBands, null, true);
}
sr =sr.map(applyScaleFactors);

// Temporally composite the images with a maximum value function.
var visParams = {bands: ['SR_B4', 'SR_B3', 'SR_B2'], min:0 ,max: 0.3};
Map.addLayer(sr, visParams, 'max value composite');

var getQABits = function(image, start, end, newName) {
    // Compute the bits we need to extract.
    var pattern = 0;
    for (var i = start; i <= end; i++) {
       pattern += Math.pow(2, i);
    }
    // Return a single band image of the extracted QA bits, giving the band
    // a new name.
    return image.select([0], [newName])
                  .bitwiseAnd(pattern)
                  .rightShift(start);
};

// A function to mask out cloudy pixels.
var cloud_shadows = function(image) {
  // Select the QA band.
  var QA = image.select(['QA_PIXEL']);
  // Get the internal_cloud_algorithm_flag bit.
  return getQABits(QA, 3,3, 'cloud_shadows').eq(0);
  // Return an image masking out cloudy areas.
};

// A function to mask out cloudy pixels.
var clouds = function(image) {
  // Select the QA band.
  var QA = image.select(['QA_PIXEL']);
  // Get the internal_cloud_algorithm_flag bit.
  return getQABits(QA, 5,5, 'Cloud').eq(0);
  // Return an image masking out cloudy areas.
};

var maskClouds = function(image) {
  var cs = cloud_shadows(image);
  var c = clouds(image);
  image = image.updateMask(cs);
  return image.updateMask(c);
};

var composite_free = sr.map(maskClouds).mean().clip(mumbai);
print(sr);
print(composite_free);

Map.addLayer(composite_free,visParams, 'composite collection without clouds');

I'm following the answers to these two questions, Cloud mask for Landsat8 on Google Earth Engine and Cloud mask in Surface Reflectance Landsat 8 test.

My intended output from the script is to get a mean cloud-free image for the entire year. But the composite_free image i.e. the mean image contains clouds (bellow image).

![enter image description here

Why this is the case? How to get the intended result? Also is there any inbuilt function of GEE which does the cloud masking?

My GEE code

Best Answer

Your bits are wrong; you're using 3 and 5, when it should be 3 and 4. From the Landsat 8 Collection 2 (C2) Level 2 Science Product (L2SP) Guide (https://www.usgs.gov/media/files/landsat-8-collection-2-level-2-science-product-guide):

Bit Description
0 Fill
1 Dilated Cloud
2 Cirrus
3 Cloud
4 Cloud Shadow
5 Snow
Related Question