I am working on a time series with Landsat 7 and I have issues with the scan line correction. I already saw the question LS7 filling the gaps image with google earth engine and tried to adopt the posted solution. I do not know why exactly the code does not work and if it is so, because the original post used raw scenes, and I am using surface reflectance.
I only changed few lines of this GEE code and used SR-Landsat scenes in my study area. I always get the error in line 40
regress.ReduceNeighborhood is not a function
I normally get such an error, if an ImageCollection instead of an image it used, so I wonder, why this error appears.
var MIN_SCALE = 1/3;
var MAX_SCALE = 3;
var MIN_NEIGHBORS = 144;
/* Apply the USGS L7 Phase-2 Gap filling protocol, using a single kernel size. */
var GapFill = function(src, fill, kernelSize, upscale) {
var kernel = ee.Kernel.square(kernelSize * 30, "meters", false)
// Find the pixels common to both scenes.
var common = src.mask().and(fill.mask())
var fc = fill.updateMask(common)
var sc = src.updateMask(common)
Map.addLayer(common.select(0).mask(common.select(0)), {palette:['000000']}, 'common mask (both exist)', false)
// Find the primary scaling factors with a regression.
// Interleave the bands for the regression. This assumes the bands have the same names.
var regress = fc.addBands(sc)
regress = regress.select(regress.bandNames().sort())
print(regress,'regress')
var ratio = 5
if(upscale) {
var fit = regress
.ReduceResolution(ee.Reducer.median(), false, 500)
.reproject(regress.select(0).projection().scale(ratio, ratio))
.ReduceNeighborhood(ee.Reducer.linearFit().forEach(src.bandNames()), kernel, null, false)
.unmask()
.reproject(regress.select(0).projection().scale(ratio, ratio))
} else {
var fit = regress
.ReduceNeighborhood(ee.Reducer.linearFit().forEach(src.bandNames()), kernel, null, false)
}
var offset = fit.select(".*_offset")
var scale = fit.select(".*_scale")
Map.addLayer(scale.select('B1_scale'), {min:-2, max:2}, 'scale B1', false)
// Find the secondary scaling factors using just means and stddev
var Reducer = ee.Reducer.mean().combine(ee.Reducer.stdDev(), null, true)
if(upscale) {
var src_stats = src
.ReduceResolution(ee.Reducer.median(), false, 500)
.reproject(regress.select(0).projection().scale(ratio, ratio))
.ReduceNeighborhood(Reducer, kernel, null, false)
.reproject(regress.select(0).projection().scale(ratio, ratio))
var fill_stats = fill
.ReduceResolution(ee.Reducer.median(), false, 500)
.reproject(regress.select(0).projection().scale(ratio, ratio))
.ReduceNeighborhood(Reducer, kernel, null, false)
.reproject(regress.select(0).projection().scale(ratio, ratio))
} else {
var src_stats = src
.ReduceNeighborhood(Reducer, kernel, null, false)
var fill_stats = fill
.ReduceNeighborhood(Reducer, kernel, null, false)
}
var scale2 = src_stats.select(".*stdDev").divide(fill_stats.select(".*stdDev"))
var offset2 = src_stats.select(".*mean").subtract(fill_stats.select(".*mean").multiply(scale2))
var invalid = scale.lt(MIN_SCALE).or(scale.gt(MAX_SCALE))
Map.addLayer(invalid.select(0).mask(invalid.select(0)), {palette:['550000']}, 'invalid1', false)
scale = scale.where(invalid, scale2)
offset = offset.where(invalid, offset2)
// When all else fails, just use the difference of means as an offset.
var invalid2 = scale.lt(MIN_SCALE).or(scale.gt(MAX_SCALE))
Map.addLayer(invalid2.select(0).mask(invalid2.select(0)), {palette:['552020']}, 'invalid2', false)
scale = scale.where(invalid2, 1)
offset = offset.where(invalid2, src_stats.select(".*mean").subtract(fill_stats.select(".*mean")))
// Apply the scaling and mask off pixels that didn't have enough neighbors.
var count = common.ReduceNeighborhood(ee.Reducer.count(), kernel, null, true, "boxcar")
var scaled = fill.multiply(scale).add(offset)
.updateMask(count.gte(MIN_NEIGHBORS))
return src.unmask(scaled, true)
}
//////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
var etmCol = ee.ImageCollection('LANDSAT/LE07/C01/T1_SR')
.filterDate('2005-05-30', '2005-10-30');
// Define a collection filter.
var colFilter = ee.Filter.and(
ee.Filter.bounds(area),
ee.Filter.lt('CLOUD_COVER', 30),
ee.Filter.lt('GEOMETRIC_RMSE_MODEL', 10),
ee.Filter.or(
ee.Filter.eq('IMAGE_QUALITY', 9),
ee.Filter.eq('IMAGE_QUALITY_OLI', 9)
));
// Filter collections and prepare them for merging.
var l7images = etmCol.filter(colFilter);
print(l7images,'Landsat 7')
var source = ee.Image(listOfImages.get(0))
var fill = ee.Image(listOfImages.get(1))
Map.addLayer(fill, {min:400, max:2500, gamma: 2.222 , bands:["B3", "B2", "B1"]}, "1", true)
Map.addLayer(source, {min:400, max:2500, gamma: 2.222 , bands:["B3", "B2", "B1"]}, "2", true)
print(source,'First')
print(fill,'Second')
//var source = ee.Image("LANDSAT/LE07/C01/T1_SR/LE07_167063_20150207")
//var fill = ee.Image(" LANDSAT/LE07/C01/T1_SR/LE07_167063_20150615")
var result = GapFill(source, fill, 10, false);
Map.addLayer(result, {min:0, max:200, bands:["B3", "B2", "B1"]}, "filled", true)
var result = GapFill(source, fill, 10, true);
Map.addLayer(result, {min:0, max:200, bands:["B3", "B2", "B1"]}, "filled (upscaled)", true)
Here is my code https://code.earthengine.google.com/690a5042671f6effed934d1f8e0ef01d