0

I am attempting to use google's cloudscore methodology to remove clouds from LANDSAT8 imagery. I am using python to do this. I can get this to work in java from the web code editor, but am unable to get the same results from the python API.

Java

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]));
};

var image = ee.ImageCollection('LANDSAT/LC08/C01/T1')
.filter(ee.Filter.calendarRange(2018,2018,'year'))
.filter(ee.Filter.calendarRange(03,05,'month'))
.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);
});


var image = image.qualityMosaic('cloudscore')


var fc = ee.FeatureCollection('users/hoogstra_5/TN_CensusTractBoundaries')
var image = image.clip(fc.filterMetadata("Name","equals","kml_61239"))

var vizParams = {'bands': ['B4', 'B3', 'B2'], 'max': 30000, 'gamma': 1.6};

Map.addLayer(image, vizParams);

PYTHON

LC8_BANDS = ['B2',   'B3',    'B4',  'B5',  'B6',    'B7',    'B10'];
STD_NAMES = ['blue', 'green', 'red', 'nir', 'swir1', 'swir2', 'temp'];

def cloudscore(img):

    def rescale(img1, exp, thresholds):
        return img1.expression(exp, {img1: img1}).subtract(thresholds[0]).divide(thresholds[1] - thresholds[0])

    score = ee.Image(1.0);

    score = score.min(rescale(img, 'img.blue', [0.1, 0.3]));

    score = score.min(rescale(img, 'img.red + img.green + img.blue', [0.2, 0.8]));

    score = score.min(rescale(img, 'img.nir + img.swir1 + img.swir2', [0.3, 0.8]));

    score = score.min(rescale(img, 'img.temp', [300, 290]));

    ndsi = img.normalizedDifference(['green', 'swir1']);

    return score.min(rescale(ndsi, 'img', [0.8, 0.6]));

def badSelect(img):
    # Invert the cloudscore so 1 is least cloudy, and rename the band.
    score = cloudscore(img.select(LC8_BANDS, STD_NAMES))
    score = ee.Image(1).subtract(score).select([0], ['cloudscore'])
    return img.addBands(score)

image = ee.ImageCollection('LANDSAT/LC08/C01/T1')\
        .filter(ee.Filter.calendarRange(2018,2018,'year'))\
        .filter(ee.Filter.calendarRange(3,3,'month'))\
        .map(badSelect)

image = image.qualityMosaic('cloudscore')

image_out = image.clip(fc.filterMetadata("Name","equals",kml))

vizParams = {'bands': ['B4', 'B3', 'B2'], 'max': 30000, 'gamma': 1.6}

Coordinate_List = fc.filterMetadata("Name","equals",kml).geometry().bounds().getInfo()['coordinates']

geo = ee.Geometry.Polygon(Coordinate_List)

task = ee.batch.Export.image.toDrive(  
                    image = image_out.visualize(vizParams),
                    description = FileName,
                    folder = folderName,
                    scale=30,
                    region=Coordinate_List,
                    fileFormat='GeoTIFF'
                    )

task.start()

I am recieving the error: "keys must be str, int, float, bool or None, not Image" from my error handling module but am having a hard time troubleshooting where. I believe it to be the function passing image objects and expecting keys.

2

In your rescale function

def rescale(img1, exp, thresholds):
        return img1.expression(exp, {img1: img1}).subtract(thresholds[0]).divide(thresholds[1] - thresholds[0])

You have i'd say two mistakes, as the second argument for expression you have {img1:img1} which should be {'img':img1}. 1 because python expects a string as a key while js automatically reads keys as strings or numbers. This is where your error message "keys must be str, int, float, bool or None, not Image" comes from. 2 because in your expressions you have img as img.red, img.blue etc and not img1.red, img1.blue etc. So it should be

def rescale(img1, exp, thresholds):
        return img1.expression(exp, {"img": img1}).subtract(thresholds[0]).divide(thresholds[1] - thresholds[0])

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.