I am rewriting a code to apply the edge Otsu method for a collection of image in GEE. My code is divided into 2 part, first I determined an image using the Canny edge detection, after that I used the Otsu to defined the optimized threshold on the image above, then I extracted the water_mask from the images. I got this error: " Layer error: Image.select, argument 'input': Invalid type. Expected type: Image. Actual type: ImageCollection." But when I set up the input, it was already a collection ? Here is my code: https://code.earthengine.google.com/2e95a8d056dbc5df21ddea06d762f923

function otsu(histogram) {
  // make sure histogram is an ee.Dictionary object
  histogram = ee.Dictionary(histogram);
  // extract relevant values into arrays
  var counts = ee.Array(histogram.get('histogram'));
  var means  = ee.Array(histogram.get('bucketMeans'));
  // calculate single statistics over arrays
  var size  = means.length().get([0]);
  var total = counts.reduce(ee.Reducer.sum(), [0]).get([0]);
  var sum   = means.multiply(counts).reduce(ee.Reducer.sum(), [0]).get([0]);
  var mean  = sum.divide(total);
  // compute between sum of squares, where each mean partitions the data
  var indices = ee.List.sequence(1, size);
  var bss     = indices.map(function(i) {
    var aCounts = counts.slice(0, 0, i);
    var aCount  = aCounts.reduce(ee.Reducer.sum(), [0]).get([0]);
    var aMeans  = means.slice(0, 0, i);
    var aMean   = aMeans.multiply(aCounts)
      .reduce(ee.Reducer.sum(), [0]).get([0])
    var bCount = total.subtract(aCount);
    var bMean  = sum.subtract(aCount.multiply(aMean)).divide(bCount);
    return aCount.multiply(aMean.subtract(mean).pow(2)).add(
  // return the mean value corresponding to the maximum BSS
  return means.sort(bss).get([-1]);

//Edge Otsu Algorithms
function edgeOtsu(image,kwargs) {
  var geom = image.geometry()
  // get list of band names used later
  var bandList = ee.Image(image).bandNames();
  var kwargKeys = [];
  for(var key in kwargDefaults) kwargKeys.push( key );
  var params;
  var i,k,v;
  // loop through the keywords and construct ee.Dictionary from them,
  // if the key is defined in the input then pass else use default
  params = ee.Dictionary(kwargs);
  for (i=0;i<kwargKeys.length;i++) {
    k = kwargKeys[i];
    v = kwargDefaults[k];
    params = ee.Dictionary(
     // parameters for all methods
  var initialThreshold = ee.Number( params.get('initialThreshold') ),
      reductionScale   = ee.Number( params.get('reductionScale') ),
      smoothing        = ee.Number( params.get('smoothing') ),
      bandName         = ee.String( params.get('bandName') ),
      connectedPixels  = ee.Number( params.get('connectedPixels') ),
      edgeLength       = ee.Number( params.get('edgeLength') ),
      smoothEdges      = ee.Number( params.get('smoothEdges') ),
      cannyThreshold   = ee.Number( params.get('cannyThreshold') ),
      cannySigma       = ee.Number( params.get('cannySigma') ),
      cannyLt          = ee.Number( params.get('cannyLt') ),
      maxBuckets       = ee.Number( params.get('maxBuckets') ),
      minBucketWidth   = ee.Number( params.get('minBucketWidth') ),
      maxRaw           = ee.Number( params.get('maxRaw') ),
      invert           = params.get('invert'),
      verbose          = params.get('verbose').getInfo();
  // get preliminary water
  var binary = ee.Image(image).lt(initialThreshold).rename('binary');
  // get canny edges
  var canny = ee.Algorithms.CannyEdgeDetector(binary, cannyThreshold, cannySigma);
  // process canny edges
  var connected  = canny.updateMask(canny).lt(cannyLt).connectedPixelCount(connectedPixels, true);
  var edges      = connected.gte(edgeLength);
  edges          = edges.updateMask(edges);
  var edgeBuffer = edges.focal_max(smoothEdges, 'square', 'meters');

    // get histogram for Otsu
  var histogram_image = ee.Image(image).updateMask(edgeBuffer);
    var histogram = ee.Dictionary(histogram_image.reduceRegion({
    reducer:ee.Reducer.histogram(maxBuckets, minBucketWidth,maxRaw)
      .combine('mean', null, true).combine('variance', null,true),
    geometry: roi,
    scale: reductionScale,
    maxPixels: 1e13,
   var threshold = otsu(histogram_image);
  // get watermask
  var waterMask = ee.Image(image).select('VV_smoothed').gt(threshold).rename('waterMask');
  waterMask = waterMask.updateMask(waterMask); //Remove all pixels equal to 0
  return ee.Image(image).addBands(waterMask);

var roi = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level0").filter(ee.Filter.eq('ADM0_NAME', 'Pakistan'));
// convert feature collection into geometry
var roi = roi.geometry().bounds();
Map.addLayer(roi, {color: 'black'}, 'Study Area',1);
// Zoom to regions of interest
// import sentinel 1 and filter data series
var s1 =  ee.ImageCollection('COPERNICUS/S1_GRD')
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
.filter(ee.Filter.eq('instrumentMode', 'IW'))
.filter(ee.Filter.eq('orbitProperties_pass', 'ASCENDING'))
//.filter(ee.Filter.contains({leftField: ".geo", rightValue: aoi})) // Filter partial S1-Images of AOI
.map(function(image){return image.clip(Map.getBounds(true))})
.map(function(image){return image.addBands(image.select('VV').focal_median(parseFloat('50'),'circle','meters').rename('VV_smoothed'))}); // Smooth S1-Images

var kwargDefaults = {
    'reductionScale': 180,
    'smoothing': 100,
    'bandName': "VV",
    'connectedPixels': 100,
    'edgeLength': 20,
    'smoothEdges': 20,
    'cannyThreshold': 1,
    'cannySigma': 1,
    'cannyLt': 0.05,
    'maxBuckets': 255,
    'minBucketWidth': 0.001,
    'maxRaw': 1e6,
    'verbose': false

var s1_edge = edgeOtsu(s1,kwargDefaults);

1 Answer 1


When I run your code, I got the following error message:

Layer 2: Layer error: Image.select, argument 'input': Invalid type. Expected type: Image. Actual type: ImageCollection.

It is clear that you are mapping the Image Collection in a wrong way. However, mapping it adequately, it produces other issues indicating some bugs in your edgeOtsu function.

To map adequately your Image Collection s1, I'm going to use a list for returning other objects instead images. So, following lines are placed at the bottom of your code.

var s1_list = s1.toList(s1.size());

var col = s1_list.map(function (ele) {
  var s1_edge = edgeOtsu(ele, kwargDefaults);
  return s1_edge;


When I run complete code in this link, I got the result of following picture.

enter image description here

To corroborate that is nothing wrong with your edgeOtsu function, I'm going to return params object. Result can be observed in following picture. There were printed 2298 elements (with the same 15 properties) without any problem. Complete code is here.

enter image description here

Now, I'm going to return the first candidate with .rename method: binary variable. Result of following image highlights that the problem is produced in that variable.

enter image description here

Avoiding to use .rename method in this code for binary variable, I got following result without any problem. However, there are 4 possible binary bands (all them have values 0 or 1): VV, VH, angle and VV_smoothed. So, you need to select whatever of these four bands before renaming it as "binary". However, I know that referred band is not 'VV_smoothed' because this is selected and renamed later as 'waterMask'. Then, what sense does it make to produce and rename the binary band if it is not going to be added to the final result?

enter image description here

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.