Masking overcorrected pixels in Surface Reflectance assets using an EvalScript

Surface Reflectance (SR) assets, such as those from PlanetScope, can sometimes exhibit overcorrection artifacts.

This phenomenon typically occurs when the atmospheric correction algorithm processes very dark, low-illuminance areas like deep shadows or water bodies. The correction can "overshoot," resulting in calculated reflectance values that are negative.

Because PlanetScope SR data is delivered as an unsigned 16-bit integer (scaled by 10,000), these negative values are clamped to 0. This means a pixel value of 0 (or a near-zero value like 1) in the reflectance bands is a strong indicator of an overcorrected pixel, not a real physical measurement.

These artifacts can skew analyses and vegetation indices (like NDVI). You can use an EvalScript to identify and mask these pixels, typically by setting them to be transparent.

EvalScript: Masked NDVI Visualization

This script calculates NDVI and applies a standard color map where barren areas are brown, sparse vegetation is yellow, and dense vegetation is green. Pixels identified as overcorrected or having no data are made fully transparent.
 

//VERSION=3


function setup() {

  return {

    // OPTIMIZATION: Only request the bands needed for NDVI and masking.

    // Dropping "Green" and "Blue" significantly reduces data processing.

    input: ["Red", "NIR", "dataMask"],

    // Output is 4 bands: R, G, B from the color map, and Alpha for transparency.

    output: { 

      bands: 4,

      sampleType: "AUTO" // AUTO sampleType is efficient for visualizations (0-1 float)

    }

  };

}

// A color ramp to visualize NDVI values.

// NDVI values are mapped to a color gradient.

const viz = new ColorRampVisualizer([

  [-1.0, 0xa52a2a], // Barren/water (Brown)

  [0.0,  0xffff00], // No vegetation (Yellow)

  [0.25, 0x00ff00], // Low vegetation (Light Green)

  [1.0,  0x006400]  // High vegetation (Dark Green)

]);

function evaluatePixel(sample) {

  // First, check for invalid pixels to avoid unnecessary calculations.

  // isOvercorrected will be true if Red or NIR are 0 or 1.

  let isOvercorrected = (sample.Red <= 1) || (sample.NIR <= 1);


  // If the pixel has no data OR is overcorrected, return a fully transparent pixel.

  if (!sample.dataMask || isOvercorrected) {

    return [0, 0, 0, 0]; // Transparent black

  }

  // Calculate NDVI for valid pixels.

  // Adding a very small number (epsilon) to the denominator prevents division by zero.

  let nir = sample.NIR;

  let red = sample.Red;

  let ndvi = (nir - red) / (nir + red + 1e-9);

  // Use the visualizer to get the RGB color for the calculated NDVI value.

  // The viz.process() function returns an array of [R, G, B] values between 0 and 1.

  let rgb = viz.process(ndvi);

  // Return the RGB color with full opacity (alpha = 1).

  return [...rgb, 1];

}
Was this article helpful?
0 out of 0 found this helpful

Comments

0 comments

Please sign in to leave a comment.