My goal is to create an image collection that has 100% free cloud over a small region of interest, for example a lake.

This script filters landsat 8 images based on location and cloud cover:

var nocloudimages =  landsat8.filterBounds(ROI)
                     .filter(ee.Filter.lt('CLOUD_COVER', value))
                     .sort('system:time_start', true)

However, as we know, the 'CLOUD_COVER' accounted for the whole landsat 8 image percentage of cloud, not a particular Region of Interest (ROI) cloud cover.

Is there a method to achieve that?

2 Answers 2


It's going to be something like this, but you'll need to play with the threshold (10 in this example) to meet your needs. Watch out for ROIs that overlap a scene's footprint, but do not contain any valid pixels. Also watch out for ROIs that are very large or span multiple WRS cells.

var ic = ee.ImageCollection("LANDSAT/LC08/C01/T1_RT_TOA");

// A polygon representing the roi.
var geometry = ee.Geometry.Polygon(
        [[[-121.85897778617999, 37.70881514186375],
          [-121.83975284337708, 37.76202899390253],
          [-121.94137041100134, 37.759857750255144]]]);

var c = ic.filterBounds(geometry);

var withCloudiness = c.map(function(image) {
  var cloud = ee.Algorithms.Landsat.simpleCloudScore(image).select('cloud');
  var cloudiness = cloud.reduceRegion({
    reducer: 'mean', 
    geometry: geometry, 
    scale: 30,
  return image.set(cloudiness);

var filteredCollection = withCloudiness.filter(ee.Filter.lt('cloud', 10));

There is standard example in the code editor that is pretty close to what you want. https://code.earthengine.google.com/1be28850c6c7880d8fcd5f1e0a808986

// SimpleCloudScore, an example of computing a cloud-free composite with L8
// by selecting the least-cloudy pixel from the collection.

// A mapping from a common name to the sensor-specific bands.
var LC8_BANDS = ['B2',   'B3',    'B4',  'B5',  'B6',    'B7',    'B10'];
var STD_NAMES = ['blue', 'green', 'red', 'nir', 'swir1', 'swir2', 'temp'];

// Compute a cloud score.  This expects the input image to have the common
// band names: ["red", "blue", etc], so it can work across sensors.
var cloudScore = function(img) {
  // A helper to apply an expression and linearly rescale the output.
  var rescale = function(img, exp, thresholds) {
    return img.expression(exp, {img: img})
        .subtract(thresholds[0]).divide(thresholds[1] - thresholds[0]);

  // Compute several indicators of cloudyness and take the minimum of them.
  var score = ee.Image(1.0);
  // Clouds are reasonably bright in the blue band.
  score = score.min(rescale(img, 'img.blue', [0.1, 0.3]));

  // Clouds are reasonably bright in all visible bands.
  score = score.min(rescale(img, 'img.red + img.green + img.blue', [0.2, 0.8]));

  // Clouds are reasonably bright in all infrared bands.
  score = score.min(
      rescale(img, 'img.nir + img.swir1 + img.swir2', [0.3, 0.8]));

  // Clouds are reasonably cool in temperature.
  score = score.min(rescale(img, 'img.temp', [300, 290]));

  // However, clouds are not snow.
  var ndsi = img.normalizedDifference(['green', 'swir1']);
  return score.min(rescale(ndsi, 'img', [0.8, 0.6]));

// Filter the TOA collection to a time-range and add the cloudscore band.
var collection = ee.ImageCollection('LC8_L1T_TOA')
    .filterDate('2013-05-01', '2013-07-01')
    .map(function(img) {
      // Invert the cloudscore so 1 is least cloudy, and rename the band.
      var score = cloudScore(img.select(LC8_BANDS, STD_NAMES));
      score = ee.Image(1).subtract(score).select([0], ['cloudscore']);
      return img.addBands(score);

// Define visualization parameters for a true color image.
var vizParams = {'bands': ['B4', 'B3', 'B2'], 'max': 0.4, 'gamma': 1.6};
Map.setCenter(-120.24487, 37.52280, 8);
Map.addLayer(collection.qualityMosaic('cloudscore'), vizParams);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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