Has anyone found a good solution to mask out cloud/shadow cover not using quality assessesment pixels or combining it with something else?

I am trying to generate cloud/shadow free composite for a large area [code with 1 point to make it easier] using Landsat 8 SR. I wrote this code based on qa_pixels. It works, but there is still cloud pixels which are described as clear land in the quality assessment [easier to see them when plotting an image w/ the max NDVI value per pixel]. Here is the code: https://code.earthengine.google.com/8b42d2a7c3c31ee3606a3994373ea59a

var UXRB = /* color: #0b4a8b */ee.Geometry.Point([-52.207, -14.349])
var colL8_SR = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR")
    .filterDate('2015-01-01', '2015-12-31')
print("colL8_SR", colL8_SR);
Map.addLayer(colL8_SR, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'colL8_SR');

var colL8_SRnc = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR")
    .filterDate('2015-01-01', '2015-12-31')
    .filterMetadata('CLOUD_COVER', 'equals', 0)
print("colL8_SRnc", colL8_SRnc);
Map.addLayer(colL8_SRnc, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'colL8_SRnc');

// I can get cloud-shadow free images w/previous section - static scene;
// However, instead of static scene, I need a composed of the maximum number
// of cloud-shadow free images in a year as possible to calculate temporal indexes

// Add additional bands to Landsat 8 collection
print('Creates add NDVI function');
var addBANDS = function(image) {
    var ndvi = image.normalizedDifference(['B5', 'B4']).rename('NDVI');
    var evi = image.expression('2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
        'NIR': image.select('B5'),
        'RED': image.select('B4'),
        'BLUE': image.select('B2')}).rename('EVI');
    var time = image.metadata ('system:time_start').rename('Time');
    return image.addBands(ndvi).addBands(evi).addBands(time);

// Call L8 REFLEC collection T1 & filter by the bounds (UXRB), date (2015), pixel & add NDVI & Mosaic
print('L8 SR, Spatial/Temporal filter, greenest');
var colL8_SR15 = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR")
    .filterDate('2015-01-01', '2015-12-31')
print("colL8_SR15", colL8_SR15);

//A function to mask out cloudy pixels based on L8 SR qa_pixel
var cloud_shadows1 = function(image) {
    // Select the QA band.
    var QA = image.select(['pixel_qa']);
    // Get the values for clear & water & fill
    var mask = QA.eq(322) //Clear land: 322
              .or(QA.eq(324)) //Clear water: 324
              .or(QA.eq(336)) //Snow-Ice: 336
              .or(QA.eq(1)) //Fill: 1
    // Get updated image
    return image.updateMask(mask);

var colL8_SR15ncs1 = colL8_SR15.map(cloud_shadows1);
Map.addLayer(colL8_SR15ncs1, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'colL8_SR15ncs1');
print("colL8_SR15ncs1", colL8_SR15ncs1);

var L8_SR15ncs1m = colL8_SR15ncs1.mean();
Map.addLayer(L8_SR15ncs1m, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'L8_SR15ncs1mean');
print("L8_SR15ncs1m", L8_SR15ncs1m);

var L8_SR15gr11 = colL8_SR15ncs1.qualityMosaic('EVI');
Map.addLayer(L8_SR15gr11, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'L8_SR15gEVI1');
print("L8_SR15gr11", L8_SR15gr11);

var L8_SR15gr21 = colL8_SR15ncs1.qualityMosaic('NDVI');
Map.addLayer(L8_SR15gr21, {bands: ['B7', 'B5', 'B4'], min:0, max:10000}, 'L8_SR15gNDVI1');
print("L8_SR15gr21", L8_SR15gr21);
// A lot of bad pixels 'cause of cloud/shadow which are described in qa as clear pixels

I also run the code described here [Cloud mask for Landsat8 on Google Earth Engine ], but retrieves the same result - some bad pixels due to cloud-shadow which are identified as clear land in qa.

Has anyone found a better solution for this issue? Or provide any advise on the way to go?

  • 1
    Have a look a this code: code.earthengine.google.com/a1277b10ca9ed5c34038991a14b597c4 made by Gennadii Donchyts Apr 21, 2018 at 23:01
  • Hi Rodrigo. Thanks for replying!! If possible, could you clarify some points of the code and workflow for me?
    – 1Garcia
    Apr 23, 2018 at 16:01
  • Hi Rodrigo. Thanks for replying!! If possible, could you clarify some points of the code and workflow for me? (i) what exactly "cloudUtils.computeCloudShadowMask" is doing? I didn't really get the meaning of the output; (ii) What is the difference using such function from using only DarkPixels?; (iii) What would be the way of applying it to SR images? I thought about creating those masks in TOA and transferring it to SR products based on the start system-time and path&row OR converting the TOA images to SR after filtering the cloud.
    – 1Garcia
    Apr 23, 2018 at 16:18


Your Answer

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