2

I have a feature collection that is used as input for a model. In order to validate the model, I would like to mask a random percentage of pixels from each image, i.e. splitting the feature collection into a training and validation collection.

I tried to solve this by adding a random band (ee.Image.random()) to each image and then creating a mask with the .gte() and .lt() routines. I intended to map that function over the collection later on. However, the random band has a different resolution and projection than the image.

I also tried to re-project the random image to the projection of the target image. That did not work either.

Is there any easy implementable solution to mask random pixels in Google Earth Engine?

This is an example of what I am trying to do. Only the last three blocks are relevant. The pictures illustrate the respective outputs.

    [![// Function to cloud mask from the pixel_qa band of Landsat 8 SR data.
function maskL8sr(image) {
  // Bits 3 and 5 are cloud shadow and cloud, respectively.
  var cloudShadowBitMask = 1 << 3;
  var cloudsBitMask = 1 << 5;

  // Get the pixel QA band.
  var qa = image.select('pixel_qa');

  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
      .and(qa.bitwiseAnd(cloudsBitMask).eq(0));

  // Return the masked image, scaled to TOA reflectance, without the QA bands.
  return image.updateMask(mask).divide(10000)
      .select("B\[0-9\]*")
      .copyProperties(image, \["system:time_start"\]);
}

// Map the function over one year of data.
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
    .filterDate('2016-01-01', '2016-12-31')
    .map(maskL8sr);

var composite = collection.median();

// without reprojection
var sol1 = composite.addBands(ee.Image.random());
sol1 = sol1.updateMask(sol1.select('random').gt(0.2));

// with reprojection
var rand = ee.Image.random();
rand = rand.select('random').gt(0.2);
rand = rand.reproject({crs: composite.projection(), scale: 30});
var sol2 = composite.updateMask(rand);][1]][1]

// Display the results.
Map.addLayer(sol1, {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3});
Map.addLayer(sol2, {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3});

Without re-projection With re-projection

1

I am not sure what will be the purpose of making a random mask, but it is actually possible inside the GEE. However, you are now combining some impossible things.

Landsat images are projected in a rectangular grid, so around the globe they have different projections. Therefore, the masking of pixels is only possible correctly on an relatively small area on the globe where the projection is similar. Besides, you are making a composite median image. Making a composite will get rid of masked pixels, because only unmasked pixels are taken into the composite image. Furthermore, a composite image will blend pixels in the collection together, and therefore a composite image does not have a region specific projection, which explains why in your second image the masked pixels are not well aligned.

I will give you two options to perform this operation;

Option 1: Randomly mask pixels on a per image basis, and do not make the composite

Note that this solution work perfectly for every image you will show on the screen, and is the desired solution if you have a location of interest which falls completely inside a Landsat image tier.

// Function to randomly mask pixels in an image on a per-image basis
function randomMask(image) {
  // add random mask on a per image basis with reprojection
  var random = ee.Image.random().select('random').gt(0.2);
  var randReprojected = random.reproject({crs: image.projection(), scale: 30});

  // Return the randomly masked image
  return image.updateMask(randReprojected);
}

// Map the function over the image collection and add a random mask per-image
var colRandMasked = collection.map(randomMask);

// Show the result of one image and center the zoom on that image
Map.addLayer(colRandMasked.first(), {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3}, 'Masked per-image');
Map.centerObject(colRandMasked.first().geometry())

Option 2: Make a composite and randomly mask pixels correctly for a specified region of interest on earth

Note that this solutions makes an image composite for the whole world, as in you example script. However, the mask aligns correctly in the region of interest, but will mismatch pixels more the further you will go away from this ROI.

// set a location of interest
var ROI = ee.Geometry.Point([-90.43283, 20.76684]);
// Get the projection of the location of interest from a random Landsat image at that location
var proj = collection.filterBounds(ROI).first().projection();

// make a median composite image from the cloudmask images
var composite = collection.median();

// randomly mask the composite on the projection of the location of interest
var random = ee.Image.random().select('random').gt(0.2);
var randReprojected = random.reproject({crs: proj, scale: 30});

// Return the randomly masked image
var randMaskedComp = composite.updateMask(randReprojected);

// Show the result of the random masked composite and center on ROI
Map.addLayer(randMaskedComp, {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3}, 'Composite masked');
Map.centerObject(ROI, 13);

I hope I have made clear the drawbacks and impossibilities of your desired operation, but still provide a workable solution for you.
Here is the link to the working full code: Script

  • Thanks a lot. Option 1 is exactly what I was looking for. – Sebastian Hafner Dec 19 '18 at 10:01
0

To ensure that all metadata properties are copied over you could employ copyProperties (script):

// with property copying
var randProp = ee.Image.random()
var randProp = randProp.copyProperties({source: composite})
var sol2 = composite.addBands(randProp)
var sol2 = sol2.updateMask(sol2.select("random").gt(0.2))

Keep in mind however, that displaying the values of the random band as well as the Landsat data is dependant on your zoom level. If you want to ensure the alignment of the pixels I would advise to export a small subset. In my case your sol1 was working fine and aligned the same way as my proposed solution with copyProperties.

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.