// A UI to interactively filter a Sentinel-1 collection and
// create a multitemporal RGB mosaic
// Work in progress!!!
// by Juan Doblas (Instituto Socioambiental)
// Any thoughts, write to: juan@[Link]
// Based on Sentinel-1 explorer, by unknown. Lee filter programming by Guido
Lemoine
// CHANGE LOG
// 11/apr/18
// - Now the Landsat reference image is a mosaic covering the whole canvas, instead
of a single scene
// 06/apr/18
// - Updated collection name (S1_GRD_FLOAT)
// 23/jan/18
// - Added temporal adaptative filtering (Quegan&Yu,2001)
// - Major change: now the images are collected around three reference dates
// 07/jan/18
// - Added new visualization mode: change over the whole period
// 15/nov/17
// - Corrected bug that set the visualization status of the base layers to false
after redraw
// - Added grid option to help manual mapping
// 30/oct/17
// - Updated deforestation layers with last PRODES data
// - Updated initial dates
// - Updated Landsat 8 collection
// - Updated p2-p3 change detection mode (still beta)
//
var app = {};
var stack=[Link](1)
var l8melhorCena=[Link](1)
var gridLines=[Link](1)
// Estabelece as coordenadas e n�vel de zoom iniciais
var LatInicial=-3.22
var LongInicial=-52.17
var ZoomInicial=10
/** Creates the UI panels. */
[Link] = function() {
/* The introduction section. */
[Link] = {
panel: [Link]([
[Link]({
value: 'Script SIRAD',
style: {fontWeight: 'bold', fontSize: '24px', margin: '10px 5px'}
}),
[Link]('Sistema de Indica��o Radar de Desmatamento ' +
'utilizando imagens Sentinel-1.')
])
};
/* The collection filter controls. */
[Link] = {
selectExtent: [Link]({
items: [Link]([Link])
}),
t1: [Link]('YYYY-MM-DD', '2018-01-15'),
t2: [Link]('YYYY-MM-DD', '2018-02-15'),
t3: [Link]('YYYY-MM-DD', '2018-03-15'),
tww: [Link]('','27'),
rotuloNumImg: [Link]('nd'),
applyButton: [Link]('Criar Mosaico', [Link]),
};
/* The panel for the filter control widgets. */
[Link] = [Link]({
widgets: [
[Link]('Datas de refer�ncia e janela temporal ', {fontWeight: 'bold'}),
[Link]([[Link]('t1:', app.HELP_TEXT_STYLE), [Link].t1],
[Link]('horizontal')),
[Link]([[Link]('t2:', app.HELP_TEXT_STYLE), [Link].t2],
[Link]('horizontal')),
[Link]([[Link]('t3:', app.HELP_TEXT_STYLE), [Link].t3],
[Link]('horizontal')),
[Link]([[Link]('dias:', app.HELP_TEXT_STYLE), [Link]],
[Link]('horizontal')),
[Link]('N�mero de imagens promediadas nos tr�s periodos definidos:'),
[Link],
[Link]('Selecione a extens�o do mosaico', {fontWeight: 'bold'}),
[Link]([[Link],[Link]],[Link]('h
orizontal'))
],
style: app.SECTION_STYLE
});
[Link]([Link]().get(0));
/* The visualization section. */
[Link] = {
label: [Link](),
// Create a select with a function that reacts to the "change" event.
select: [Link]({
items: [Link](app.VIS_OPTIONS),
onChange: function() {
// Update the label's value with the select's description.
var option = app.VIS_OPTIONS[[Link]()];
[Link]([Link]);
// Refresh the map layer.
[Link]();
}
}),
LeeOption: [Link]({
label: 'Aplicar filtro Lee',
value: false,
onChange: function() {
// Refresh the map layer.
[Link]();
}
}),
QueganOption: [Link]({
label: 'Aplicar filtro temporal',
value: false,
onChange: function() {
// Refresh the map layer.
[Link]();
}
})
};
/* The panel for the visualization section with corresponding widgets. */
[Link] = [Link]({
widgets: [
[Link]('Selecione um tipo de visualiza��o', {fontWeight: 'bold'}),
[Link],
[Link],
[Link],
[Link]
],
style: app.SECTION_STYLE
});
// Default the select to the first value.
[Link]([Link]().get(4));
// Base de referencia
[Link] = {
chkProdes2016: [Link]({
label: 'Prodes at� 2016',
value: false,
onChange: function(checked) {
[Link]().get(1).setShown(checked)
app.VIS_BASE.l1=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkProdes2017: [Link]({
label: 'Prodes 2017 (definitivo)',
value: false,
onChange: function(checked) {
[Link]().get(2).setShown(checked)
app.VIS_BASE.l2=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkUCs: [Link]({
label: 'Unidades de Conserva��o Federais',
value: false,
onChange: function(checked) {
[Link]().get(3).setShown(checked)
app.VIS_BASE.l3=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkTIS: [Link]({
label: 'Terras Ind�genas',
value: false,
onChange: function(checked) {
[Link]().get(4).setShown(checked)
app.VIS_BASE.l4=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkMUN: [Link]({
label: 'Municipios PA/MT',
value: false,
onChange: function(checked) {
[Link]().get(5).setShown(checked)
app.VIS_BASE.l5=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkASS: [Link]({
label: 'Assentamentos INCRA',
value: false,
onChange: function(checked) {
[Link]().get(6).setShown(checked)
app.VIS_BASE.l6=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkL8: [Link]({
label: 'Mosaico Landsat 8 do periodo',
value: false,
onChange: function(checked) {
[Link]().get(7).setShown(checked)
app.VIS_BASE.l7=checked;
// Refresh the map layer.
//[Link]();
}
}),
chkGrid: [Link]({
label: 'Malha de referencia',
value: false,
onChange: function(checked) {
[Link]().get(8).setShown(checked)
app.VIS_BASE.l8=checked;
// Refresh the map layer.
//[Link]();
}
}),
labelCreditos: [Link]('Juan Doblas, Instituto Socioambiental, 2017',
{fontSize:'small'})
}
[Link] = [Link]({
widgets:[
[Link]('Base de referencia', {fontWeight: 'bold'}),
[Link].chkProdes2016,
[Link].chkProdes2017,
[Link],
[Link],
[Link],
[Link],
[Link].chkL8,
[Link],
[Link]
],
style: app.SECTION_STYLE
});
/* The export section. */
[Link] = {
ebutton: [Link]({
label: 'Exportar o mosaico como imagem',
// React to the button's click event.
onClick: function() {
// Get the visualization options.
var visOption = app.VIS_OPTIONS[[Link]()];
// Export the image to GCS.
[Link]({
image: [Link]([Link]),
description: 'sentinel1_mosaico_multitemporal_AREA_PERIODO',
scale: 10,
maxPixels:2512890090
})
}
}),
ebutton2: [Link]({
label: 'Exportar o mosaico como tiles',
// React to the button's click event.
onClick: function() {
// Get the visualization options.
var visOption = app.VIS_OPTIONS[[Link]()];
// Export map to GCS.
[Link]({
image: [Link]([Link]),
description: 'sentinel1_mosaico_multitemporal_AREA_PERIODO',
bucket: 'rmtx',
minZoom:4,
maxZoom:15,
fileFormat: 'png'
})
}
}),
// ebutton3: [Link]({
// label: 'Exportar poligonos como kml',
// // React to the button's click event.
// onClick: function() {
// // Export polygons
// [Link]({
// collection: geometry,
// description:'poligonos_mapeados_AREA_DATA',
// fileFormat: 'KML'
// });
// }
// }),
// ebutton4: [Link]({
// label: 'Duplicar mosaico atual (avan�ado)',
// // React to the button's click event.
// onClick: function() {
// var stackAux=stack
// var visOption = app.VIS_OPTIONS[[Link]()];
// [Link](stackAux, [Link], 'Camada auxiliar');
// }
// }),
};
/* The panel for the export section with corresponding widgets. */
[Link] = [Link]({
widgets: [
// [Link]('4) Start an export', {fontWeight: 'bold'}),
[Link],
[Link].ebutton2//,
// [Link].ebutton3,
//[Link].ebutton4
],
style: app.SECTION_STYLE
});
};
/** Creates the app helper functions. */
[Link] = function() {
/** Applies the selection filters currently selected in the UI. */
[Link] = function() {
[Link]('Calculando...')
var filtered =
[Link](app.COLLECTION_ID).map(toDB).map(maskEdge)//.select([[Link]
ATION]);
var extentOption = [Link][[Link]()];
if (extentOption===0) {
filtered = [Link]([Link](true));
}
if (extentOption===1) {
filtered = [Link]([Link]());
}
var filtered0=QueganYuFilter([Link](0),5)
var filtered1=QueganYuFilter([Link](1),5)
if ([Link]()){filtered=[Link](filtered1)}
// Set filter variables.
var t1v = [Link]();
if (t1v) t1v = [Link](t1v);
var t2v = [Link]();
if (t2v) t2v = [Link](t2v);
var t3v = [Link]();
if (t3v) t3v = [Link](t3v);
var twwv = [Link](); // tww=temporal window width
if (twwv) twwv = [Link](twwv);
// Calculo das colecoes por periodo
var midWindow=[Link](1).divide(2)
var t1v1=[Link]([Link](-1),'day')
var t1v2=[Link](midWindow,'day')
var t2v1=[Link]([Link](-1),'day')
var t2v2=[Link](midWindow,'day')
var t3v1=[Link]([Link](-1),'day')
var t3v2=[Link](midWindow,'day')
print ('Datas p1:',[Link]('YYYY-MM-dd'),[Link]('YYYY-MM-dd'))
print ('Datas p2:',[Link]('YYYY-MM-dd'),[Link]('YYYY-MM-dd'))
print ('Datas p3:',[Link]('YYYY-MM-dd'),[Link]('YYYY-MM-dd'))
var im1_col=[Link](t1v1,t1v2)
var im2_col=[Link](t2v1,t2v2)
var im3_col=[Link](t3v1,t3v2)
print('Datas das imagens do periodo p1:')
print(extractDates(im1_col))
print('Datas das imagens do periodo p2:')
print(extractDates(im2_col))
print('Datas das imagens do periodo p3:')
print(extractDates(im3_col))
// Calculo asincrono do total de imagens
var rotulo1=im1_col.size().format('p1: %s ')
var rotulo2=im2_col.size().format('p2: %s ')
var rotulo3=im3_col.size().format('p3: %s')
var rotulo=[Link](rotulo2).cat(rotulo3)
[Link](function(resultado){
[Link](resultado)
})
var im1vv = [Link](toDB(im1_col.select(0).map(toNatural).mean()));
var im2vv = [Link](toDB(im2_col.select(0).map(toNatural).mean()));
var im3vv = [Link](toDB(im3_col.select(0).map(toNatural).mean()));
var im1vh = [Link](toDB(im1_col.select(1).map(toNatural).mean()));
var im2vh = [Link](toDB(im2_col.select(1).map(toNatural).mean()));
var im3vh = [Link](toDB(im3_col.select(1).map(toNatural).mean()));
// Generate a dB version of the RL filtered image stack
var im1vv_lee= [Link](toDB(RefinedLee(toNatural(im1vv))))
var im2vv_lee= [Link](toDB(RefinedLee(toNatural(im2vv))))
var im3vv_lee= [Link](toDB(RefinedLee(toNatural(im3vv))))
var im1vh_lee= [Link](toDB(RefinedLee(toNatural(im1vh))))
var im2vh_lee= [Link](toDB(RefinedLee(toNatural(im2vh))))
var im3vh_lee= [Link](toDB(RefinedLee(toNatural(im3vh))))
// Generate Lee version
var
stack_lee=im1vv_lee.addBands(im2vv_lee).addBands(im3vv_lee).addBands(im1vh_lee).add
Bands(im2vh_lee).addBands(im3vh_lee)
stack=[Link](im2vv).addBands(im3vv).addBands(im1vh).addBands(im2vh).addBand
s(im3vh)
if ([Link]()){stack=stack_lee}
stack=[Link]([0,1,2,3,4,5],['p1vv','p2vv','p3vv','p1vh','p2vh','p3vh'])
// Get Landsat-8 for comparation purposes
var
l8col=[Link]('LANDSAT/LC08/C01/T1_RT_TOA').filterBounds([Link](t
rue)).filterDate(t1v1,t3v2).sort('CLOUD_COVER',false);
l8melhorCena=[Link]([Link]())
//var datal8 = [Link]([Link]('system:time_start'));
//print('Data melhor imagem Landsat: ', datal8); // [Link]
var vizParams = {
bands: ['B6', 'B5', 'B4'],
min: 0.01,
max: 0.5,
gamma: [0.95, 1.1, 1]}
var vizParamsB8 = {
bands: ['B8'],
min: 0.01,
max: 0.25,
gamma: [1.3]}
// Computes mapping aid grid
gridLines=createGrid([Link],[Link]([Link](true)).buffer([Link]
cale).geometry())
// Redisplay everything
[Link]();
};
/** Refreshes the current map layer based on the UI widget states. */
[Link] = function() {
[Link]();
var visOption = app.VIS_OPTIONS[[Link]()];
[Link](stack, [Link], 'Composite RGB');
[Link]([Link]("users/juandb/SIRAD_DATA/PRODES_desmatAcumulado_2016"),
{},'Prodes 2016',app.VIS_BASE.l1)
[Link]([Link]("users/juandb/SIRAD_DATA/PRODES_definitivo_2017")
,{},'Prodes 2017',app.VIS_BASE.l2)
[Link]([Link]().toByte().paint([Link](app.FT_UCS), 1, 3),
{"palette":["009b2f"]},'Unidades de Conserva��o',app.VIS_BASE.l3)
[Link]([Link]().toByte().paint([Link](app.FT_TIS), 1, 3),
{"palette":["e59f12"]},'Terras Ind�genas',app.VIS_BASE.l4)
[Link]([Link]().toByte().paint([Link](app.FT_MUN), 1, 1),
{"palette":["9212a5"]},'Munic�pios',app.VIS_BASE.l5)
[Link]([Link]().toByte().paint([Link](app.FT_ASS), 1, 2),
{"palette":["1153a3"]},'Assentamentos',app.VIS_BASE.l6)
[Link]([Link](l8melhorCena),app.VIS_OPTIONS_L8,'Mosaico L8',
app.VIS_BASE.l7)
[Link](gridLines ,{'palette': '000000', 'opacity': 0.5},"Malha de
referencia",app.VIS_BASE.l8);
//[Link]([Link](l8melhorCena),app.VIS_OPTIONS_L8B8,'Cena L8B8', false
};
}
/** Creates the app constants. */
[Link] = function() {
app.FT_UCS='ft:1DnN6jij1KKPF04OsEdBCqhUUWlrlad_6nN0Nma3I'
app.FT_TIS='users/juandb/SIRAD_DATA/TIs_mai2018'
app.FT_MUN='ft:1tHPugMfiZXFfwoNCdZqdOcz1fRpBNQtOT7gGkam5'
app.FT_ASS='ft:173TRgqGmvCjGxww242guBEFmLwKqKbk1TqXmbEGV'
app.COLLECTION_ID = 'COPERNICUS/S1_GRD_FLOAT';
[Link]= 18000
[Link]='VH'
app.SECTION_STYLE = {margin: '20px 0 0 0'};
[Link]={'Tela completa':0,'Centro':1}
[Link]= ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
'74A901', '66A000', '529400', '3E8601', '207401', '056201',
'004C00', '023B01', '012E01', '011D01', '011301'];
app.VIS_OPTIONS = {
'composi��o multitemporal VV': {
description: 'Cada banda representa a m�dia das observa��es entre duas datas.
' +
'Tons coloridos indicam altera��o da cobertura do solo.',
visParams: {gamma: 1, min: -14, max: -4, bands: ['p1vv','p2vv','p3vv']}
},
'Imagem periodo 1 vv': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t1-t2.',
visParams: {gamma: 1, min: -14, max: -4, bands: ['p1vv']}
},
'Imagem periodo 2 vv': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t2-t3.',
visParams: {gamma: 1, min: -14, max: -4, bands: ['p2vv']}
},
'Imagem periodo 3 vv': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t3-t4.',
visParams: {gamma: 1, min: -14, max: -4, bands: ['p3vv']}
},
'composi��o multitemporal VH': {
description: 'Cada banda representa a m�dia das observa��es entre duas datas.
' +
'Tons coloridos indicam altera��o da cobertura do solo.',
visParams: {gamma: 1, min: -20, max: -8, bands: ['p1vh','p2vh','p3vh']}
},
'Imagem periodo 1 vh': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t1-t2.',
visParams: {gamma: 1, min: -20, max: -8, bands: ['p1vh']}
},
'Imagem periodo 2 vh': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t2-t3.',
visParams: {gamma: 1, min: -20, max: -8, bands: ['p2vh']}
},
'Imagem periodo 3 vh': {
description: 'Imagem de retroespalhamento relativa a ' +
'm�dia das observa��es no periodo t3-t4.',
visParams: {gamma: 1, min: -20, max: -8, bands: ['p3vh']}
},
'Diferen�a total vh': {
description: 'Composite RGB (mudan�a, m�nimo, gradiente)' +
' entre a primera e a �ltima data',
visParams: {min:[0.01,-20,0],max:[0.3,-8,10], bands:
['variation','min','diff_max']}
}
};
app.VIS_OPTIONS_L8 = {
bands: ['B6', 'B5', 'B4'],
min: 0.01,
max: 0.5,
gamma: [0.95, 1.1, 1]
}
app.VIS_OPTIONS_L8B8 = {
bands: ['B8'],
min: 0.01,
max: 0.25,
gamma: [1.3]
}
app.HELPER_TEXT_STYLE = {
margin: '8px 0 -3px 8px',
fontSize: '12px',
color: 'gray'
}
app.VIS_BASE= {
l1: false,
l2: false,
l3: false,
l4: false,
l5: false,
l6: false,
l7: false,
l8: false,
}
};
/** Creates the application interface. */
[Link] = function() {
[Link]();
[Link]();
[Link]();
var main = [Link]({
widgets: [
[Link],
[Link],
[Link],
[Link],
[Link]
],
style: {width: '320px', padding: '8px'}
});
[Link](LongInicial, LatInicial, ZoomInicial);
[Link](0, main);
[Link]();
};
[Link]();
function toDB(img) {
return [Link](img).log10().multiply(10.0).copyProperties(img,
['system:time_start','sliceNumber']);
}
function toNatural(img) {
return [Link](10.0).pow([Link](0).divide(10.0)).copyProperties(img,
['system:time_start','sliceNumber']);
}
function toGamma0(img) {
return
[Link](0).subtract([Link](1).multiply([Link]/180.0).cos().log10().multiply
(10.0));
}
// Fun��o para calcular ENL em uma imagem S1, sobre uma area de referencia AOI
function computeENLS1(S1img,AOI){
// This function computes ENL index on Sentinel-1 bands.
// It will take in account only the first band
S1img=[Link](S1img)
S1img=[Link]([0],['band'])
var mean=[Link]([Link]([Link](),AOI,20).get('band'))
var std=[Link]([Link]([Link](),AOI,20).get('band'))
return ([Link](std)).pow(2)
}
// Fun��o para filtrar uma cole�a� seguindo a metodologia de Quagan&Yu, 2001
// Importante: a cole��o deve estar em numeros naturais. N=tamanho do filtro
espacial
function QueganYuFilter(imgCol,N){
// This function will return a filtered collection
// the filter is the proposed by Quegan&Yu (2011),
// Warning: will only work on single scene time series on natural. No mosaics
(for now), no DB
var boxcar = [Link]({radius: N, units: 'pixels', normalize: false});
var imgColMedian = [Link](function(img){return [Link](boxcar)})
var correctionFactorCol=[Link](function(img){return
[Link]([Link](boxcar))})
var numberofsamples=[Link](function(img){return [Link](img)}).sum()
var correctionFactor=[Link]().divide(numberofsamples)
return [Link](function(img){return
[Link](correctionFactor).copyProperties(img,
['system:time_start','sliceNumber'])})
}
function extractDates(col){
var datesList=[Link](col.aggregate_array('system:time_start'));
var datesListFmt=[Link](formatDate)
return datesListFmt
}
function formatDate(date){
return [Link](date).format('YYYY-MM-dd')
}
function maskEdge(img) {
var mask = [Link](0).unitScale(-25,
5).multiply(255).toByte().connectedComponents([Link](1,1), 100);
return [Link]([Link](0));
}
// Function to create grid layer
function createGrid(gridScale, gridBounds) {
var proj = [Link]([0]).projection(); //
Solicit native projection - can change if desired
var im = [Link]().clip(gridBounds);
// create an image defined by each pixel's location
var im2 = [Link]([0]).add([Link]([1])).multiply(-1000000).int(); //
creates an image where each pixel has a unique value --> diagonals aren't unique,
but works if eightConnected is set to false below
im2 = [Link](proj, null,
gridScale); // reproject image at the desired scale
(gridScale)
var grd = [Link]({ //
vectorize pixels - Polygons
geometry: gridBounds,
scale: gridScale,
geometryType: 'polygon',
eightConnected: false,
bestEffort: true,
geometryInNativeProjection: true
});
var blank = [Link](0).mask(0).toByte(); //
create a blank image,
var grd_otln = [Link](grd, 3, 1); //
outline using color 3, width 1.
return grd_otln
}
// helper to apply Lee filter to db images
function RefinedLeeDb(img){
return toDB(RefinedLee(toNatural(img)))
}
// The RL speckle filter
function RefinedLee(img) {
// img must be in natural units, i.e. not in dB!
img=[Link](img)
// Set up 3x3 kernels
var weights3 = [Link]([Link](1,3),3);
var kernel3 = [Link](3,3, weights3, 1, 1, false);
var mean3 = [Link]([Link](), kernel3);
var variance3 = [Link]([Link](), kernel3);
// Use a sample of the 3x3 windows inside a 7x7 windows to determine gradients
and directions
var sample_weights = [Link]([[0,0,0,0,0,0,0], [0,1,0,1,0,1,0],[0,0,0,0,0,0,0],
[0,1,0,1,0,1,0], [0,0,0,0,0,0,0], [0,1,0,1,0,1,0],[0,0,0,0,0,0,0]]);
var sample_kernel = [Link](7,7, sample_weights, 3,3, false);
// Calculate mean and variance for the sampled windows and store as 9 bands
var sample_mean = [Link](sample_kernel);
var sample_var = [Link](sample_kernel);
// Determine the 4 gradients for the sampled windows
var gradients = sample_mean.select(1).subtract(sample_mean.select(7)).abs();
gradients =
[Link](sample_mean.select(6).subtract(sample_mean.select(2)).abs());
gradients =
[Link](sample_mean.select(3).subtract(sample_mean.select(5)).abs());
gradients =
[Link](sample_mean.select(0).subtract(sample_mean.select(8)).abs());
// And find the maximum gradient amongst gradient bands
var max_gradient = [Link]([Link]());
// Create a mask for band pixels that are the maximum gradient
var gradmask = [Link](max_gradient);
// duplicate gradmask bands: each gradient represents 2 directions
gradmask = [Link](gradmask);
// Determine the 8 directions
var directions =
sample_mean.select(1).subtract(sample_mean.select(4)).gt(sample_mean.select(4).subt
ract(sample_mean.select(7))).multiply(1);
directions =
[Link](sample_mean.select(6).subtract(sample_mean.select(4)).gt(sample
_mean.select(4).subtract(sample_mean.select(2))).multiply(2));
directions =
[Link](sample_mean.select(3).subtract(sample_mean.select(4)).gt(sample
_mean.select(4).subtract(sample_mean.select(5))).multiply(3));
directions =
[Link](sample_mean.select(0).subtract(sample_mean.select(4)).gt(sample
_mean.select(4).subtract(sample_mean.select(8))).multiply(4));
// The next 4 are the not() of the previous 4
directions = [Link]([Link](0).not().multiply(5));
directions = [Link]([Link](1).not().multiply(6));
directions = [Link]([Link](2).not().multiply(7));
directions = [Link]([Link](3).not().multiply(8));
// Mask all values that are not 1-8
directions = [Link](gradmask);
// "collapse" the stack into a singe band image (due to masking, each pixel has
just one value (1-8) in it's directional band, and is otherwise masked)
directions = [Link]([Link]());
//var pal = ['ffffff','ff0000','ffff00', '00ff00', '00ffff', '0000ff', 'ff00ff',
'000000'];
//[Link]([Link]([Link]()), {min:1, max:8, palette: pal},
'Directions', false);
var sample_stats = sample_var.divide(sample_mean.multiply(sample_mean));
// Calculate localNoiseVariance
var sigmaV =
sample_stats.toArray().arraySort().arraySlice(0,0,5).arrayReduce([Link](),
[0]);
// Set up the 7*7 kernels for directional statistics
var rect_weights =
[Link]([Link](0,7),3).cat([Link]([Link](1,7),4));
var diag_weights = [Link]([[1,0,0,0,0,0,0], [1,1,0,0,0,0,0], [1,1,1,0,0,0,0],
[1,1,1,1,0,0,0], [1,1,1,1,1,0,0], [1,1,1,1,1,1,0], [1,1,1,1,1,1,1]]);
var rect_kernel = [Link](7,7, rect_weights, 3, 3, false);
var diag_kernel = [Link](7,7, diag_weights, 3, 3, false);
// Create stacks for mean and variance using the original kernels. Mask with
relevant direction.
var dir_mean = [Link]([Link](),
rect_kernel).updateMask([Link](1));
var dir_var = [Link]([Link](),
rect_kernel).updateMask([Link](1));
dir_mean = dir_mean.addBands([Link]([Link](),
diag_kernel).updateMask([Link](2)));
dir_var = dir_var.addBands([Link]([Link](),
diag_kernel).updateMask([Link](2)));
// and add the bands for rotated kernels
for (var i=1; i<4; i++) {
dir_mean = dir_mean.addBands([Link]([Link](),
rect_kernel.rotate(i)).updateMask([Link](2*i+1)));
dir_var = dir_var.addBands([Link]([Link](),
rect_kernel.rotate(i)).updateMask([Link](2*i+1)));
dir_mean = dir_mean.addBands([Link]([Link](),
diag_kernel.rotate(i)).updateMask([Link](2*i+2)));
dir_var = dir_var.addBands([Link]([Link](),
diag_kernel.rotate(i)).updateMask([Link](2*i+2)));
}
// "collapse" the stack into a single band image (due to masking, each pixel has
just one value in it's directional band, and is otherwise masked)
dir_mean = dir_mean.reduce([Link]());
dir_var = dir_var.reduce([Link]());
// A finally generate the filtered value
var varX =
dir_var.subtract(dir_mean.multiply(dir_mean).multiply(sigmaV)).divide([Link](1.
0));
var b = [Link](dir_var);
var result = dir_mean.add([Link]([Link](dir_mean)));
return([Link]([['sum']]));
}