0

I have a simple question related to how GEE determines continuous vs discrete data, specifically during scaling operations as described here.

I am unable to find a proper description of this, but my assumption is that float data is always treated as continuous and integer always as discrete. So that an integer image would need to be recast to float to be scaled as continuous and vice versa.

Can anyone confirm this?

In any case, there are a couple of things that confuse me in the example I give below.

First, the printAtScale function (taken and adapted from here) gives the same results (integers) for the integer and the float data. I would expect decimal numbers representing the mean of the area for the latter.

Second, a histogram of integer data shows discrete x-axis values when plotting a single image band, but value ranges when plotting two images together, which suggests continuous values (maybe just a glitch in the plotting?).

Third, the n-counts are decimal numbers (data not shown), which makes no sense in a histogram.

I realize the histogram issues are not necessarily related to the scale question, but the latter came up while creating the histograms.

// Test behavior of scaling

var geometry = /* color: #0B4A8B */ee.Geometry.Point([-99.6759033203125, 47.77185170705089]);

var COL_FILTER = ee.Filter.and(
    ee.Filter.bounds(geometry),
    ee.Filter.date('2021-05-01', '2021-05-15'));
    
var dwCol = ee.ImageCollection('GOOGLE/DYNAMICWORLD/V1').filter(COL_FILTER);

print(dwCol.getInfo());

var dwImList = dwCol.toList(dwCol.size());
var dwLab1 = ee.Image(dwImList.get(0)).select('label');
var dwLab2 = ee.Image(dwImList.get(1)).select('label');
var dwLab = dwLab1.addBands(dwLab2);
var dwLab1cont = dwLab1.toFloat();

print(dwLab1.getInfo());
print(dwLab1cont.getInfo());
// print(dwLab2.getInfo());
// print(dwLab.getInfo());

var printAtScale = function(image, scale) {
  print('Pixel value at '+scale+' meters scale',
    image.reduceRegion({
      reducer: ee.Reducer.first(),
      geometry: image.geometry().centroid(),
      // The scale determines the pyramid level from which to pull the input
      scale: scale
  }).get('label'));
};

printAtScale(dwLab1, 10); 
printAtScale(dwLab1, 50); 
printAtScale(dwLab1, 100); 
printAtScale(dwLab1, 500); 

printAtScale(dwLab1cont, 10); 
printAtScale(dwLab1cont, 50); 
printAtScale(dwLab1cont, 100); 
printAtScale(dwLab1cont, 500); 



var chart = 
    ui.Chart.image.histogram({image: dwLab, scale: 500})
        .setSeriesNames(['Land Cover 1', 'Land Cover 2'])
        .setOptions({
          title: 'Two Dates',
          hAxis: {
            title: 'Cover Class',
            titleTextStyle: {italic: false, bold: true},
          },
          vAxis:
              {title: 'Count', titleTextStyle: {italic: false, bold: true}},
          colors: ['cf513e', '1d6b99']
        });
print(chart);

var chart2 = 
    ui.Chart.image.histogram({image: dwLab1, scale: 500})
        .setSeriesNames(['Most Likely Land Cover'])
        .setOptions({
          title: 'One date. Integer',
          hAxis: {
            title: 'Cover Class',
            titleTextStyle: {italic: false, bold: true},
          },
          vAxis:
              {title: 'Count', titleTextStyle: {italic: false, bold: true}},
          colors: ['cf513e']
        });
print(chart2);

histogram of 1 band integer image

histogram of 2 band integer image

2
  • my assumption is that float data is always treated as continuous and integer always as discrete. If this were true, wouldn't it mean SRTM elevation data (integer meters) would be treated as discrete? I don't think it is though Aug 2, 2022 at 12:57
  • @BarryCarter as I wrote above, I find no reference to clarify this. I think it should be better described in the guide (link in post), with examples for both discrete and continuous. Aug 3, 2022 at 12:43

1 Answer 1

0

The pyramiding policy is specified when the images are ingested, on a per-band basis. It has nothing to do with the pixel type (although the discrete bands like label are ingested with a 'mode' policy, so pixels aren't averaged at lower levels of the pyramid).

Your printAtScale function is just looking for the first pixel at the given scale; that will be nearest neighbor sampled from the closest pyramid level, and that pyramid level was made by taking the mode of the 4 higher-resolution pixels, not the mean (the mean of the labels would be a meaningless value). However, mean is used when pyramiding the other bands.

The histogram function doesn't know what you're histogramming, it's just counting values that fall into each bucket. In the first case, the charting code displays the bucket limits because it doesn't want to rely on the bands all being the same type; in the second, it doesn't (ie: it's just a display difference because there's more than 1 band; no difference in the computation).

You're getting decimal counts because you're specifying a scale larger than the native resolution, and at that resolution, some pixels aren't fully populated, so there's a partial mask reflecting that fact. The histogram reducer uses the mask as a weighting. It's hard to "undo" this with the chart helpers, but you can run your own reduceRegion with a histogram and set it to be unweighted.

4
  • OK, so I think I was misled by this sentence in the guide: "For continuous valued images, the pixel values of upper levels of the pyramid are the mean of pixels at the next lower level. For discrete valued images, pixel values of upper levels of the pyramid are a sample (usually the top left pixel) of pixels at the next lower level." Aug 4, 2022 at 7:37
  • So the pyramiding policy is determined by the procedure being applied. In the above case the ee.Reducer.first() function, which changed to ee.Reducer.mean() should apply the averaging policy. But I seem to still get the same integer results, so I'm still confused. Is this related to what you mentioned about this being a 'label' band, so that there are cases when bands are always treated as discrete? When, more precisely, does this occur? And how could they be converted to continuous if not by casting as float? Aug 4, 2022 at 8:02
  • Just to clarify, I'm well aware that the mean of a label is meaningless. My concern however was to understand when to expect which behavior. Aug 4, 2022 at 8:37
  • No, the pyramiding policy is determined at the time the original image is ingested. The operations applied to it later don't affect that (mostly: you can fake a new pyramiding with reduceResolution, and a forced reproject, but it doesn't scale). In essence we always ingest 5-10 copies of each image at different resolutions; how the downsampling occurs to get the lower resolution images, during ingestion, is the pyramiding policy. Once they're ingested, it's done. Aug 5, 2022 at 16:44

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.