I am trying to perform an image segmentation using Google Earth Engine. I have performed OBIA in the past using python and scikit-image (skimage.segmentation.felzenszwalb) , but not sure how to do this using GEE.

the following code comes from : https://developers.google.com/earth-engine/image_convolutions

// Load and display an image.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_044034_20140318');
Map.setCenter(-121.9785, 37.8694, 11);
Map.addLayer(image, {bands: ['B5', 'B4', 'B3'], max: 0.5}, 'input image');

// Define a boxcar or low-pass kernel.
var boxcar = ee.Kernel.square({
  radius: 7, units: 'pixels', normalize: true });

// Smooth the image by convolving with the boxcar kernel.
var smooth = image.convolve(boxcar);
Map.addLayer(smooth, {bands: ['B5', 'B4', 'B3'], max: 0.5}, 'smoothed');

// Define a Laplacian, or edge-detection kernel.
var laplacian = ee.Kernel.laplacian8({ normalize: false });

// Apply the edge-detection kernel.
var edgy = image.convolve(laplacian);
Map.addLayer(edgy,{bands: ['B5', 'B4', 'B3'], max: 0.5, format: 'png'},'edges');

How do I convert the 'edges' image to a 1-band raster with N objects, with the individual objects being grouped by similar spectral values?

1 Answer 1


Here is an EE OBIA example based on Noel Gorelick's recent image segmentation presentation and example code (available here). Note that you'll need to adapt this to your particular input dataset and use case. (I feel justified making this generalization because the OP is asking about 'OBIA', which is not what the originally posted code represents). If all you care about are segments, then you can stop with clusters. If you care about segment properties, and using those properties to classify the segments, see everything after clusters:

var imageCollection = ee.ImageCollection('USDA/NAIP/DOQQ');

var geometry = /* color: #0b4a8b */ee.Geometry.Polygon(
        [[[-121.89511299133301, 38.98496606984683],
          [-121.89511299133301, 38.909335196675435],
          [-121.69358253479004, 38.909335196675435],
          [-121.69358253479004, 38.98496606984683]]], null, false);

var cdl2016 = ee.Image('USDA/NASS/CDL/2016');

var bands = ['R', 'G', 'B', 'N']
var img = imageCollection
    .filterDate('2015-01-01', '2017-01-01')
img = ee.Image(img).clip(geometry).divide(255).select(bands)
Map.centerObject(geometry, 13)
Map.addLayer(img, {gamma: 0.8}, 'RGBN', false)

var seeds = ee.Algorithms.Image.Segmentation.seedGrid(36);

// Run SNIC on the regular square grid.
var snic = ee.Algorithms.Image.Segmentation.SNIC({
  image: img, 
  size: 32,
  compactness: 5,
  connectivity: 8,
  seeds: seeds
}).select(['R_mean', 'G_mean', 'B_mean', 'N_mean', 'clusters'], ['R', 'G', 'B', 'N', 'clusters'])

var clusters = snic.select('clusters')
Map.addLayer(clusters.randomVisualizer(), {}, 'clusters')
Map.addLayer(snic, {bands: ['R', 'G', 'B'], min:0, max:1, gamma: 0.8}, 'means', false)

// Compute per-cluster stdDev.
var stdDev = img.addBands(clusters).reduceConnectedComponents(ee.Reducer.stdDev(), 'clusters', 256)
Map.addLayer(stdDev, {min:0, max:0.1}, 'StdDev', false)

// Area, Perimeter, Width and Height
var area = ee.Image.pixelArea().addBands(clusters).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256)
Map.addLayer(area, {min:50000, max: 500000}, 'Cluster Area', false)

var minMax = clusters.reduceNeighborhood(ee.Reducer.minMax(), ee.Kernel.square(1));
var perimeterPixels = minMax.select(0).neq(minMax.select(1)).rename('perimeter');
Map.addLayer(perimeterPixels, {min: 0, max: 1}, 'perimeterPixels');

var perimeter = perimeterPixels.addBands(clusters)
    .reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256);
Map.addLayer(perimeter, {min: 100, max: 400}, 'Perimeter size', false);

var sizes = ee.Image.pixelLonLat().addBands(clusters).reduceConnectedComponents(ee.Reducer.minMax(), 'clusters', 256)
var width = sizes.select('longitude_max').subtract(sizes.select('longitude_min')).rename('width')
var height = sizes.select('latitude_max').subtract(sizes.select('latitude_min')).rename('height')
Map.addLayer(width, {min:0, max:0.02}, 'Cluster width', false)
Map.addLayer(height, {min:0, max:0.02}, 'Cluster height', false)

var objectPropertiesImage = ee.Image.cat([

var training = objectPropertiesImage.addBands(cdl2016.select('cropland'))
    .sample(geometry, 5);
var classifier = ee.Classifier.smileRandomForest(10).train(training, 'cropland')
Map.addLayer(objectPropertiesImage.classify(classifier), {min:0, max:254}, 'Classified objects')
  • 1
    What's the difference between seedsGrid and size. Is size the maximum a seed can grow into, the docs say, its the spacing between seeds. Don't understand what helps in what? Commented Aug 4, 2020 at 8:53

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