4

I want to calculate the proportion of a study area that is covered by (non null) pixels within an image. I have seen examples that do similar counts (here, here, and here), and followed code from here, but it doesn't seem to give the correct answer.

// get aoi feature
var aoi_ext = ee.Geometry.Polygon(
  [[-3.0,53.0],
  [-2.5,53.0],
  [-2.5,53.5],
  [-3.0,53.5],
  [-3.0,53.0]]
  );


// Get image collection from Sentinel 2
var collection = ee.ImageCollection("COPERNICUS/S2_SR")
  .filterBounds(aoi_ext) // just images intersecting with this
  .filterDate('2017-05-01', '2018-06-01') // %Y-%m-%d
  .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 80)
  .select(['B2', 'B3', 'B4', 'B5', 'QA60']) 
  .map(function(image) { return image.clip(aoi_ext); }) // clip here to mask extent
  ;

print('no. images', collection.size());

// Look at first image
var img1 = ee.Image(collection.first());
print('image 1', img1);

// Display image
Map.addLayer(img1, {bands:['B2'], min: 0, max: 3000}, 'image 1');
Map.addLayer(aoi_ext, {}, 'aoi', true, 0.4);
Map.centerObject(aoi_ext);

// calculate number of all pixels in aoi

// get an image of aoi
var aoi_img = ee.Image(1).clip(aoi_ext);

// count pixels in aoi image
var totPixels = ee.Number(aoi_img.reduceRegion({
    reducer: ee.Reducer.count(),
    scale: 10,
    maxPixels: 999999999,
  }).values().get(0));

// Count the non zero/null pixels in the image within the aoi
  var actPixels = ee.Number(img1.select('B2').reduceRegion({
    reducer: ee.Reducer.count(),
    scale: 10,
    geometry: aoi_ext, // need it here
    maxPixels: 999999999,
  }).values().get(0));

  // calculate the perc of cover
var pcPix = actPixels.divide(totPixels).multiply(100);

print('pixels in image:', actPixels, 'pixels in aoi', totPixels);

print('% cover', pcPix);

Looking at the display, I would estimate that about 15% was covered. With other images, it also clearly does not do the calculation I expect. I also tried masking the image with the result from a .gte() call, but it gives the same result.

1 Answer 1

2

You should restructure the code a bit. Unmask does (in your case) not unmask pixels outside the image extent. Thus, your variable totPixels is not right. Make an unbounded constant image, and apply reduceRegion with the crs and scale argument to calculate the number of total pixels with the same projection as your image:

// calculate number of all pixels in aoi

// count pixels in aoi image
var totPixels = ee.Number(ee.Image(1).reduceRegion({
    reducer: ee.Reducer.count(),
    geometry: aoi_ext,
    scale: 10,
    maxPixels: 999999999,
  }).values().get(0));

// Count the non zero/null pixels in the image within the aoi
  var actPixels = ee.Number(img1.select('B2').reduceRegion({
    reducer: ee.Reducer.count(),
    scale: 10,
    geometry: aoi_ext, 
    maxPixels: 999999999,
  }).values().get(0));

  // calculate the perc of cover
var pcPix = actPixels.divide(totPixels).multiply(100);

Link example, with an addition to map it over the full collection. Two additional notes: clipping is generally not necesary and when working with Sentinel-2 imagery, always consider first making daily composites. Then, you'll have unbounded composite images, which will work with the previous example.

1
  • Thanks. There's a typo in your code here (carried through from the complete example) in totPix, crs should read "crs: img1.select('B2').projection()" so that it works with above example. Also, just to clarify, it's the clip that isn't somehow? extending beyond the image in my example? Unmask refers to the previous example.
    – CCID
    Commented Apr 5, 2020 at 7:36

Your Answer

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

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