Here is an example of how you can do this using Moving Window Smoothing, taken from https://www.youtube.com/watch?v=ncb5_zbvnaU&t=85s
by Ujaval Gandhi of Spatial Thoughts:
var start = '2021-01-01'
var end = '2022-01-01'
var point = ee.Geometry.Point(-156.85136, 59.41592)
var patch = point.buffer(50).bounds()
Map.addLayer(patch)
Map.centerObject(patch)
// Get S2 data
var S2_data = ee.ImageCollection('COPERNICUS/S2_SR')
.filterBounds(patch).filterDate(start, end);
print('no. of S2 images', S2_data.size());
// Function to add a NDVI band to an image
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
// Function to mask clouds
function maskS2clouds(image) {
var qa = image.select('QA60')
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;
var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
qa.bitwiseAnd(cirrusBitMask).eq(0))
return image.updateMask(mask).divide(10000)
.select("B.*")
.copyProperties(image, ["system:time_start"])
}
// Make S2 Image Collection
var originalCollection = S2_data
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.map(maskS2clouds)
.map(addNDVI);
print ('no. of cloud-free S2 images', originalCollection.size())
// Moving-Window Smoothing
// Specify the time-window
var days = 15;
// Convert to milliseconds
var millis = ee.Number(days).multiply(1000*60*60*24);
// We use a 'save-all join' to find all images
// that are within the time-window
// The join will add all matching images into a
// new property called 'images'
var join = ee.Join.saveAll({
matchesKey: 'images'
});
// This filter will match all images that are captured
// within the specified day of the source image
var diffFilter = ee.Filter.maxDifference({
difference: millis,
leftField: 'system:time_start',
rightField: 'system:time_start'
});
var joinedCollection = join.apply({
primary: originalCollection,
secondary: originalCollection,
condition: diffFilter
});
print('Joined Collection', joinedCollection);
// Each image in the joined collection will contain
// matching images in the 'images' property
// Extract and return the mean of matched images
var extractAndComputeMean = function(image) {
var matchingImages = ee.ImageCollection.fromImages(image.get('images'));
var meanImage = matchingImages.reduce(
ee.Reducer.mean().setOutputs(['moving_average']))
return ee.Image(image).addBands(meanImage)
}
var smoothedCollection = ee.ImageCollection(
joinedCollection.map(extractAndComputeMean));
print('Smoothed Collection', smoothedCollection)
// Define the chart and print it to the console.
// Display a time-series chart
var chart_NDVI = ui.Chart.image.series({
imageCollection: smoothedCollection.select(['ndvi', 'ndvi_moving_average']),
region: patch,
reducer: ee.Reducer.mean(),
scale: 20
}).setOptions({
title: 'NDVI Time Series',
interpolateNulls: false,
vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
hAxis: {title: '', format: 'YYYY-MM'},
lineWidth: 1,
pointSize: 4,
series: {
0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI
1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI
},
})
print(chart_NDVI);