Tuesday 18 June 2019

Performing object based image classification in Google Earth Engine?


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?



Answer



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')
.filterBounds(geometry)

.mosaic()
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,
neighborhoodSize:256,
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([
snic.select(bands),
stdDev,
area,
perimeter,
width,
height

]).float();

var training = objectPropertiesImage.addBands(cdl2016.select('cropland'))
.updateMask(seeds)
.sample(geometry, 5);
var classifier = ee.Classifier.randomForest(10).train(training, 'cropland')
Map.addLayer(objectPropertiesImage.classify(classifier), {min:0, max:254}, 'Classified objects')

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...