I am attempting terrain flattening of the Sentinel-1 images here. However, I am encountering strange black pixels scattered throughout, as shown in the attached figure. I was calculating annual median values after the terrain correction but encountered this issue.
The standalone code for reproducing the issue is available here:
https://code.earthengine.google.com/bdc77c48882e6e9a6f302cec874add26
var ROI = ee.Geometry.Polygon(
[[[77.7977109177407, 31.763799704742873],
[77.7977109177407, 29.140781031782804],
[81.5550351364907, 29.140781031782804],
[81.5550351364907, 31.763799704742873]]], null, false);
Map.centerObject(ROI, 10);
var toDB = function (img) {
return ee.Image(img).log10().multiply(10.0);
};
function terrainCorrection(image) {
var imgGeom = image.geometry();
var srtm = ee.Image('USGS/SRTMGL1_003').clip(imgGeom); // 30m srtm
var sigma0Pow = image;
// Article ( numbers relate to chapters)
// 2.1.1 Radar geometry
var theta_i = image.select('angle');
var phi_i = ee.Terrain.aspect(theta_i)
.reduceRegion(ee.Reducer.mean(), theta_i.get('system:footprint'), 1000)
.get('aspect');
// 2.1.2 Terrain geometry
var alpha_s = ee.Terrain.slope(srtm).select('slope');
var phi_s = ee.Terrain.aspect(srtm).select('aspect');
// 2.1.3 Model geometry
// reduce to 3 angle
var phi_r = ee.Image.constant(phi_i).subtract(phi_s);
// convert all to radians
var phi_rRad = phi_r.multiply(Math.PI / 180);
var alpha_sRad = alpha_s.multiply(Math.PI / 180);
var theta_iRad = theta_i.multiply(Math.PI / 180);
var ninetyRad = ee.Image.constant(90).multiply(Math.PI / 180);
// slope steepness in range (eq. 2)
var alpha_r = (alpha_sRad.tan().multiply(phi_rRad.cos())).atan();
// slope steepness in azimuth (eq 3)
var alpha_az = (alpha_sRad.tan().multiply(phi_rRad.sin())).atan();
// local incidence angle (eq. 4)
var theta_lia = (alpha_az.cos().multiply((theta_iRad.subtract(alpha_r)).cos())).acos();
var theta_liaDeg = theta_lia.multiply(180 / Math.PI);
// 2.2
// Gamma_nought_flat
var gamma0 = sigma0Pow.divide(theta_iRad.cos());
var gamma0dB = ee.Image.constant(10).multiply(gamma0.log10());
var ratio_1 = gamma0dB.select('VV').subtract(gamma0dB.select('VH'));
// Volumetric Model
var nominator = (ninetyRad.subtract(theta_iRad).add(alpha_r)).tan();
var denominator = (ninetyRad.subtract(theta_iRad)).tan();
var volModel = (nominator.divide(denominator)).abs();
// apply model
var gamma0_Volume = gamma0.divide(volModel);
var gamma0_VolumeDB = ee.Image.constant(10).multiply(gamma0_Volume.log10());
// we add a layover/shadow maskto the original implmentation
// layover, where slope > radar viewing angle
var alpha_rDeg = alpha_r.multiply(180 / Math.PI);
var layover = alpha_rDeg.lt(theta_i);
// shadow where LIA > 90
var shadow = theta_liaDeg.lt(85);
// calculate the ratio for RGB vis
var ratio = gamma0_VolumeDB.select('VV').subtract(gamma0_VolumeDB.select('VH'));
return gamma0_VolumeDB;}
var S1 = ee.ImageCollection('COPERNICUS/S1_GRD_FLOAT')
.filterDate('2019-01-01', '2020-01-01')
.filterBounds(ROI)
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VH'))
.filter(ee.Filter.eq('instrumentMode', 'IW'));
var S1_original = toDB(S1.select('VH').median());
Map.addLayer(S1_original.select('VH'), {min:-30, max:1},'Original');
var S1_terrainCorrected = S1.map(terrainCorrection).select('VH').median();
Map.addLayer(S1_terrainCorrected, {min:-30, max:1},'corrected');