Google Earth Engine – Examining Consistency Issues with the SNIC Segmentation Algorithm in Google Earth Engine

google earthgoogle-earth-engineimage segmentation

I have been using GEE for about a month now and I ran into some issue

I am trying to use OBIA classification based on this presentation by Sai Cheemalapati, EEAI 2019.

The main problem is when I perform SNIC algorithm on sattlite image, everytime I change scale, it recalculate and give me a new result. I tested my hypothesis that SNIC is sensitive to scale by exporting region in 2 scales. And to my surprise, both image did show inconsistency (large area segmentation on scale of 10 vs small area segmentation on scale of 2)

Is it possible to specify scale to make SNIC algorithm segmentation on that scale only?

Is this a bug?

Or I am missing something in the documentation, and if so can you advise?

Example Image

scale of 2 export
SNIC with scale size 2

enter image description here
SNIC with scale size 10

The simplified demo code can be found here

var S2 = ee.ImageCollection("COPERNICUS/S2_SR"),
    ROI = ee.Geometry.Polygon(
        [[[10.093076096894833, 55.45897533022183],
          [10.093076096894833, 55.407161055045385],
          [10.231778489472958, 55.407161055045385],
          [10.231778489472958, 55.45897533022183]]], null, false);

// Prepare Image
function maskS2clouds(image) {
  var qa = image.select('QA60');

  // Bits 10 and 11 are clouds and cirrus, respectively.
  var cloudBitMask = 1 << 10;
  var cirrusBitMask = 1 << 11;

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

  return image.updateMask(mask).divide(10000);
}

var image = S2
  .filterDate("2019-03-01", "2019-04-01")
  .filterBounds(ROI)
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 80))
  .map(maskS2clouds)
  .median()
  .clip(ROI)

// Prepare SNIC
var kernel = ee.Kernel.gaussian(3);
image = image.convolve(kernel);

var seeds = ee.Algorithms.Image.Segmentation.seedGrid(36);
var snic = ee.Algorithms.Image.Segmentation.SNIC({
  image: image,
  compactness: 0,
  connectivity: 8,
  neighborhoodSize: 64,
  size: 3,
  seeds: seeds
});

// Visualize
Map.addLayer(image, {
  bands: ["B4", "B3", "B2"],
  min: 0,
  max: 0.255
}, "RGB")
// Map.addLayer(snic.randomVisualizer(), null, "snic")

// // Run this on 1st time
// Export
Export.image.toAsset({
  image: snic.select(["clusters"]),
  description: "cluster_scale-10",
  assetId: "cluster_scale-10",
  region: ROI,
  scale: 10,
  maxPixels: 1e13
})
Export.image.toAsset({
  image: snic.select(["clusters"]),
  description: "cluster_scale-2",
  assetId: "cluster_scale-2",
  region: ROI,
  scale: 2,
  maxPixels: 1e13
})

// // For other time
// var user = "your-user-name"
// var snic_s_2 = ee.Image("users/" + user + "/cluster_scale-2");
// var snic_s_10 = ee.Image("users/" + user + "/cluster_scale-10");

// Map.addLayer(snic_s_2.randomVisualizer(), null, "SNIC 2 m")
// Map.addLayer(snic_s_10.randomVisualizer(), null, "SNIC 10 m")

Best Answer

I just found out that there exists a reproject method in ee.Image. So when I change my code to the following, the result image is consistent

var snic = ee.Algorithms.Image.Segmentation.SNIC({
  image: image,
  compactness: 0,
  connectivity: 8,
  neighborhoodSize: 64,
  size: 3,
  seeds: seeds
}).reproject({
  crs: 'EPSG:4326',
  scale: 5
});
Related Question