How to reduce the spatial resolution of an image collection in Google Earth Engine? - resolution

Good Day
Please may you kindly assist me with the following.
I am attempting to generate a time series of NDVI estimates using Landsat 7 and 8 for a particular region of interest (ROI). I would then like to compare these estimates against NDVI values that are acquired from the MODIS 16-day NDVI composite. While, I am familiar with how to acquire the mean Landsat NDVI values for my ROI at the Landsat spatial resolution (30 m), I would like to aggregate the pixels from this resolution to the MODIS NDVI product resolution (500 m).
I have attempted to do this in the code provided below based on an example provided at https://developers.google.com/earth-engine/guides/resample but I have been unsuccessful in my attempts. I receive the error "ndvi.reduceResolution is not a function"
Do I need to create a function for this instead and map it over the image collection or should I just specify the scale that I would like the averaging to be performed at when charting or exporting the data as a csv?
Many thanks in advance.
///Add region of interest
var ROI = ROI
Map.addLayer(ROI, {}, 'ROI')
Map.centerObject(ROI, 10)
//Define time of interest
// Ensure that the first image that is collected possesses data to calculate NDVI otherwise the script will not work as required
var startdate = '2013-01-01'
var enddate = '2021-01-01'
var years = ee.List.sequence(ee.Date(startdate).get('year'), ee.Date(enddate).get('year'));
///Create functions to mask clouds
/// see: https://landsat.usgs.gov/sites/default/files/documents/landsat_QA_tools_userguide.pdf
///This function masks clouds in Landsat 7 imagery.
function maskL7(im) {
var qa = im.select('BQA');
var mask = qa.eq(672);
return im.updateMask(mask).copyProperties(im);
}
///This function masks clouds in Landsat 8 imagery.
function maskL8(im) {
var qa = im.select('BQA');
var mask = qa.eq(2720);
return im.updateMask(mask).copyProperties(im);
}
///Import image collections, filter by date and ROI, apply cloud mask and clip to ROI
///Landsat 7 Collection 1 Tier 1 calibrated top-of-atmosphere (TOA) reflectance
var ls7toa = ee.ImageCollection('LANDSAT/LE07/C01/T1_TOA')
.filterBounds(ROI)
.filterDate(startdate, enddate)
.map(function(im) {return maskL7(im)})
.map(function(image){return image.clip(ROI)})
///Landsat 8 Collection 1 Tier 1 calibrated top-of-atmosphere (TOA) reflectance
var ls8toa = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')
.filterBounds(ROI)
.filterDate(startdate, enddate)
.map(function(im) {return maskL8(im)})
.map(function(image){return image.clip(ROI)})
///Create function to calculate NDVI using Landsat data
///Calculate NDVI for Landsat 7
var ls7_ndvi = ls7toa.map(function(image) {
var ndvi = image.normalizedDifference(['B4', 'B3']).rename('ndvi');
return image.addBands(ndvi);
});
///Calculate NDVI for Landsat 8
var ls8_ndvi = ls8toa.map(function(image) {
var ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi');
return image.addBands(ndvi);
});
///Merge the image collections into one and only select the NDVI data
var landsat = ee.ImageCollection(ls7_ndvi.merge(ls8_ndvi));
var ndvi = landsat.select(['ndvi'])
print(ndvi, 'ndvi')
// Load a MODIS NDVI Collection
var modis = ee.Image(ee.ImageCollection("MODIS/006/MOD13A1")
.first()
.select('NDVI'))
// Get information about the MODIS projection.
var modisProjection = modis.projection();
print('MODIS projection:', modisProjection);
// Get the Landsat NDVI collection at MODIS scale and projection.
var ndviMean = ndvi
// Force the next reprojection to aggregate instead of resampling.
.reduceResolution({
reducer: ee.Reducer.mean(),
})
// Request the data at the scale and projection of the MODIS image.
.reproject({
crs: modisProjection
});
///Create a timer-series plot of NDVI
var chart = ui.Chart.image.series({
imageCollection: ndviMean,
region: ROI,
reducer: ee.Reducer.mean(),
scale: 500,
})
print(chart, "ndvi")

Indeed, mapping a function over the ImageCollection will do the trick:
///Add region of interest
var ROI = ROI
Map.addLayer(ROI, {}, 'ROI')
Map.centerObject(ROI, 10)
//Define time of interest
// Ensure that the first image that is collected possesses data to calculate NDVI otherwise the script will not work as required
var startdate = '2013-01-01'
var enddate = '2021-01-01'
var years = ee.List.sequence(ee.Date(startdate).get('year'), ee.Date(enddate).get('year'));
///Create functions to mask clouds
/// see: https://landsat.usgs.gov/sites/default/files/documents/landsat_QA_tools_userguide.pdf
///This function masks clouds in Landsat 7 imagery.
function maskL7(im) {
var qa = im.select('BQA');
var mask = qa.eq(672);
return im.updateMask(mask).copyProperties(im);
}
///This function masks clouds in Landsat 8 imagery.
function maskL8(im) {
var qa = im.select('BQA');
var mask = qa.eq(2720);
return im.updateMask(mask).copyProperties(im);
}
///Import image collections, filter by date and ROI, apply cloud mask and clip to ROI
///Landsat 7 Collection 1 Tier 1 calibrated top-of-atmosphere (TOA) reflectance
var ls7toa = ee.ImageCollection('LANDSAT/LE07/C01/T1_TOA')
.filterBounds(ROI)
.filterDate(startdate, enddate)
.map(function(im) {return maskL7(im)})
.map(function(image){return image.clip(ROI)})
///Landsat 8 Collection 1 Tier 1 calibrated top-of-atmosphere (TOA) reflectance
var ls8toa = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA')
.filterBounds(ROI)
.filterDate(startdate, enddate)
.map(function(im) {return maskL8(im)})
.map(function(image){return image.clip(ROI)})
///Create function to calculate NDVI using Landsat data
///Calculate NDVI for Landsat 7
var ls7_ndvi = ls7toa.map(function(image) {
var ndvi = image.normalizedDifference(['B4', 'B3']).rename('ndvi');
return image.addBands(ndvi);
});
///Calculate NDVI for Landsat 8
var ls8_ndvi = ls8toa.map(function(image) {
var ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi');
return image.addBands(ndvi);
});
///Merge the image collections into one and only select the NDVI data
var landsat = ee.ImageCollection(ls7_ndvi.merge(ls8_ndvi));
var ndvi = landsat.select(['ndvi'])
print(ndvi, 'ndvi')
// Load a MODIS NDVI Collection
var modis = ee.Image(ee.ImageCollection("MODIS/006/MOD13A1")
.first()
.select('NDVI'))
// Get information about the MODIS projection.
var modisProjection = modis.projection();
print('MODIS projection:', modisProjection);
// Get the Landsat NDVI collection at MODIS scale and projection.
var landsat_pro = ndvi.first().projection();
var CopyScale = landsat_pro.nominalScale();
print(CopyScale, 'original scale Landsat (m)')
var landsat_resample = function(image){
return image.reproject(landsat_pro, null, 500) // insert here the desired scale in meters
// Force the next reprojection to aggregate instead of resampling.
.reduceResolution({
reducer: ee.Reducer.mean(),
maxPixels: 1024
})
.copyProperties(image)
}
var ndviResample = ndvi.map(landsat_resample)
///Create a timer-series plot of NDVI
var chart = ui.Chart.image.series({
imageCollection: ndviResample,
region: ROI,
reducer: ee.Reducer.mean(),
scale: 500,
})
print(chart, "ndvi")

Related

Pixi.js v5 - apply alpha to displacement map

I'm scaling a displacement map on click but would like that map to fade away once it reaches almost full scale. The idea is that the filter should be non-existent after a couple of seconds.
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage("https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80");
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.05)
displacement.alpha = 1
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
app.ticker.add(function() {
displacement.scale.x += 0.05
displacement.scale.y += 0.05
if (displacement.scale.x > 10) app.ticker.stop()
});
logo.on('mousedown', function() {
displacement.scale.set(0.05)
app.ticker.start()
});
Here's what I have so far:
https://codepen.io/mariojankovic/pen/pojjNae?editors=0111
I've only just started looking at Pixi but I think you want to use the scale property of the displacement filter. This value says how far to shift. Reducing this value to 0 will lessen its effect to none.
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html#scale
The way it works is it uses the values of the displacement map to look
up the correct pixels to output. This means it's not technically
moving the original. Instead, it's starting at the output and asking
"which pixel from the original goes here". For example, if a
displacement map pixel has red = 1 and the filter scale is 20, this
filter will output the pixel approximately 20 pixels to the right of
the original.
https://codepen.io/PAEz/pen/BaoREwv
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage(
"https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80"
);
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.0);
displacement.alpha = 1;
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
const displacementScaleFrom = 0.05;
const displacementScaleTo = 10 ;
const displacementStep = 0.05;
const filterScaleFrom = 20;// the default value for the filter is 20
const filterStep = filterScaleFrom / ((displacementScaleTo-displacementScaleFrom) / displacementStep);
app.ticker.add(function () {
displacement.scale.x += displacementStep;
displacement.scale.y += displacementStep;
filter.scale.x -= filterStep;
filter.scale.y -= filterStep;
if (displacement.scale.x >= displacementScaleTo) {
app.ticker.stop();
filter.scale.x = 0;
filter.scale.y = 0;
}
});
logo.on("mousedown", function () {
displacement.scale.set(displacementScaleFrom);
filter.scale.x = filterScaleFrom;
filter.scale.y = filterScaleFrom;
app.ticker.start();
});

Masking datasets with different resolutions in GEE

I've calculated the NDMI index based on the MCD43A4 (spatial resolution 500m) collection for an area where various water bodies exist.
What I want to do, is to mask out these water bodies from my collection, based on the Landsat Global Inland Water dataset (spatial resolution 30m),
but I have no idea how to do this.
The first thing I must do, is to change the spatial resolution of Landsat in order to match it with the MODIS one but I do not understand
how to this, should I use a type of Reduce?
Thanks
var geometry = /* color: #d63000 */ee.Geometry.Polygon(
[[[69.75758392503599, 50.151303763817786],
[71.60328705003599, 40.18192251959151],
[93.70777923753599, 41.54446477874571],
[91.86207611253599, 51.09912927236651]]]);
var dataset = ee.ImageCollection('GLCF/GLS_WATER')
.filterBounds(geometry)
.map(function(image){return image.clip(geometry)}) ;
var water = dataset.select('water');
var imageCollection = ee.ImageCollection("MODIS/006/MCD43A4")
.filterBounds(geometry)
.map(function(image){return image.clip(geometry)})
.filter(ee.Filter.calendarRange(6,8,'month'));
var modNDMI = imageCollection.select("Nadir_Reflectance_Band2","Nadir_Reflectance_Band6","BRDF_Albedo_Band_Mandatory_Quality_Band2","BRDF_Albedo_Band_Mandatory_Quality_Band6");
/////////////////////////////////////////////////
var quality = function(image){
var mask1 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band2").eq(0);
var mask2 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band6").eq(0);
return image.updateMask(mask1).updateMask(mask2);
};
var clean_collection = modNDMI.map(quality);
var addNDMI = function(image) {
var ndmi = image.normalizedDifference(['Nadir_Reflectance_Band2', 'Nadir_Reflectance_Band6']).rename('NDMI');
return image.addBands(ndmi);
};
var ndmi = clean_collection.map(addNDMI);
var NDMI=ndmi.select('NDMI')
print(water)
//And from this point, I have no idea how to mask the water bodies based on the
//Landsat collection
It's not entirely obvious what you mean by "mask the waterbodies," but if this is not what you intend, then just use water_mask.not().
var gsw = ee.Image('JRC/GSW1_0/GlobalSurfaceWater');
var occurrence = gsw.select('occurrence');
// Create a water mask layer, and set the image mask so that non-water areas
// are opaque.
var water_mask = occurrence.gt(90).unmask(0);
Map.addLayer(water_mask)
var dataset = ee.ImageCollection('GLCF/GLS_WATER')
var water = dataset.select('water');
var imageCollection = ee.ImageCollection("MODIS/006/MCD43A4")
.filterDate('2017-01-01', '2018-12-31')
.filter(ee.Filter.calendarRange(6,8,'month'));
var modNDMI = imageCollection.select("Nadir_Reflectance_Band2","Nadir_Reflectance_Band6","BRDF_Albedo_Band_Mandatory_Quality_Band2","BRDF_Albedo_Band_Mandatory_Quality_Band6");
var quality = function(image){
var mask1 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band2").eq(0);
var mask2 = image.select("BRDF_Albedo_Band_Mandatory_Quality_Band6").eq(0);
return image.updateMask(mask1).updateMask(mask2);
};
var clean_collection = modNDMI.map(quality);
var addNDMI = function(image) {
var ndmi = image.normalizedDifference(['Nadir_Reflectance_Band2', 'Nadir_Reflectance_Band6']).rename('NDMI');
return image.addBands(ndmi).updateMask(water_mask);
};
var ndmi = clean_collection.map(addNDMI);
var NDMI=ndmi.select('NDMI')
Map.addLayer(NDMI)
See also this tutorial.

Bing Maps SpatialMath Module Intersection is not accurate with Multiple pins with same coordinates

I figured an issue, while i have thousands of pins over the map, i am using drawing tool to draw shapes free hand and then executing the Intersection on "drawingEnded" event, While i could see the intersection should return more than it actually returns,
Am i missing something ? For Example, If there are around 500 pins under the new area drawn, Intersection method only returns 100 or few more,
My Spider Cluster Configuration:
` Microsoft.Maps.loadModule(['SpiderClusterManager'], function () {
spiderManager = new SpiderClusterManager(map, pinssame, {
//clusteredPinCallback: function (cluster) {
// //Customize clustered pushpin.
// cluster.setOptions({
// color: 'red',
// icon:'https://www.bingmapsportal.com/Content/images/poi_custom.png'
// });
//},
pinSelected: function (pin, cluster) {
if (cluster) {
showInfobox(cluster.getLocation(), pin);
} else {
showInfobox(pin.getLocation(), pin);
}
},
pinUnselected: function () {
hideInfobox();
},
gridSize: 80
});
});
`
Intersection Function Code which gets triggered after "drawingEnded" event:
` function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
//Get all the pushpins from the pinLayer.
//var pins = spiderManager._data;
//Using spatial math find all pushpins that intersect with the drawn search area.
//The returned data is a copy of the intersecting data and not a reference to the original shapes,
//so making edits to them will not cause any updates on the map.
var intersectingPins = Microsoft.Maps.SpatialMath.Geometry.intersection(pins, searchArea);
//The data returned by the intersection function can be null, a single shape, or an array of shapes.
if (intersectingPins) {
//For ease of usem wrap individudal shapes in an array.
if (intersectingPins && !(intersectingPins instanceof Array)) {
intersectingPins = [intersectingPins];
}
var selectedPins = [];
//Loop through and map the intersecting pushpins back to their original pushpins by comparing their coordinates.
for (var j = 0; j < intersectingPins.length; j++) {
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.Location.areEqual(pins[i].getLocation(), intersectingPins[j].getLocation())) {
selectedPins.push(pins[i]);
break;
}
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
return selectedPins;
}
}
return null;
}
`
The function is not returning accurate pin data,
Am i missing something here ?
Any Help Appreciated,
Thanks & Regards,
Shohil Sethia
UPDATE :
Just figured, It is an assumption ,I have multiple pins with same coordinates over the layer, Is this the reason that it returns only pins which intersects with different coordinates over the map ?,
Thanks & Regards,
Shohil Sethia
The method returns objects that represent the intersection, not the exact copies of input shapes. So yes, if multiple pushpins with the same coordinates are within the area, only one pushpin of that coordinates will be in the result, since that alone is good enough as a representation.
You can try the sample below, only one pushpin is returned:
// Creates a polygon of current map bounds
var polygon = new Microsoft.Maps.SpatialMath.locationRectToPolygon(map.getBounds());
// Creates a bunch of the pushpins of the same coordinates(map center)
var pushpin1 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin2 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin3 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin4 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin5 = new Microsoft.Maps.Pushpin(map.getCenter());
// Adds the shapes to map for some visualization
map.entities.push([polygon, pushpin1, pushpin2, pushpin3, pushpin4, pushpin5]);
// Only one pushpin is returned as result
var intersectingPin = Microsoft.Maps.SpatialMath.Geometry.intersection([pushpin1, pushpin2, pushpin3, pushpin4, pushpin5], polygon);
Have you checked if the number of results adds up when taking duplicate pins into account?
I got a solution, Since Intersection API ignore multiple pushPins with same coordinates, Therefore there is another API named as contains which takes two parameters which are the shape and the pushpin, and it returns whether it is contained in that shape or not in a boolean form. So true if pushpin is in that shape, and false in the other way.
function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
var selectedPins = [];
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.SpatialMath.Geometry.contains(searchArea, pins[i])) {
selectedPins.push(pins[i]);
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
//return updatePrescriberTerr(selectedPins);
return selectedPins;
}
return null;
}
Therefore in the above function the we can loop it from the pushPins array and form the intersection set accordingly based on the boolean values.
Hope it helps to those with similar scenario !
Regards,
Shohil Sethia

Making realtime datatable updates

I built an app which consumes data from a redis channel(sellers) with socketio and push the data in realtime to the frontend. The dataset could contain up to a thousand rows so I'm thinking about using a datatable to represent the data in a clean way. The table elements will be updated regularly, but there will be no rows to add/remove, only updates.
The problem I'm facing is that I don't know which would be the proper way to implement it due to my inexperience in the visualization ecosystem. I've been toying with d3js but I think It'll be too difficult to have something ready quickly and also tried using the datatables js library but I failed to see how to make the datatable realtime.
This is the code excerpt from the front end:
socket.on('sellers', function(msg){
var seller = $.parseJSON(msg);
var sales = [];
var visits = [];
var conversion = [];
var items = seller['items'];
var data = [];
for(item in items) {
var item_data = items[item];
//data.push(item_data)
data.push([item_data['title'], item_data['today_visits'], item_data['sold_today'], item_data['conversion-rate']]);
}
//oTable.dataTable(data);
$(".chart").html("");
drawBar(data);
});
Using d3 to solve your problem is simple and elegant. I took a little time this morning to create a fiddle that you can adapt to your own needs:
http://jsfiddle.net/CelloG/47nxxhfu/
To use d3, you need to understand how it works with joining the data to the html elements. Check out http://bost.ocks.org/mike/join/ for a brief description by the author.
The code in the fiddle is:
var table = d3.select('#data')
// set up the table header
table.append('thead')
.append('tr')
.selectAll('th')
.data(['Title', 'Visits', 'Sold', 'Conversion Rate'])
.enter()
.append('th')
.text(function (d) { return d })
table.append('tbody')
// set up the data
// note that both the creation of the table AND the update is
// handled by the same code. The code must be run on each time
// the data is changed.
function setupData(data) {
// first, select the table and join the data to its rows
// just in case we have unsorted data, use the item's title
// as a key for mapping data on update
var rows = d3.select('tbody')
.selectAll('tr')
.data(data, function(d) { return d.title })
// if you do end up having variable-length data,
// uncomment this line to remove the old ones.
// rows.exit().remove()
// For new data, we create rows of <tr> containing
// a <td> for each item.
// d3.map().values() converts an object into an array of
// its values
var entertd = rows.enter()
.append('tr')
.selectAll('td')
.data(function(d) { return d3.map(d).values() })
.enter()
.append('td')
entertd.append('div')
entertd.append('span')
// now that all the placeholder tr/td have been created
// and mapped to their data, we populate the <td> with the data.
// First, we split off the individual data for each td.
// d3.map().entries() returns each key: value as an object
// { key: "key", value: value}
// to get a different color for each column, we set a
// class using the attr() function.
// then, we add a div with a fixed height and width
// proportional to the relative size of the value compared
// to all values in the input set.
// This is accomplished with a linear scale (d3.scale.linear)
// that maps the extremes of values to the width of the td,
// which is 100px
// finally, we display the value. For the title entry, the div
// is 0px wide
var td = rows.selectAll('td')
.data(function(d) { return d3.map(d).entries() })
.attr('class', function (d) { return d.key })
// the simple addition of the transition() makes the
// bars update smoothly when the data changes
td.select('div')
.transition()
.duration(800)
.style('width', function(d) {
switch (d.key) {
case 'conversion_rate' :
// percentage scale is static
scale = d3.scale.linear()
.domain([0, 1])
.range([0, 100])
break;
case 'today_visits':
case 'sold_today' :
scale = d3.scale.linear()
.domain(d3.extent(data, function(d1) { return d1[d.key] }))
.range([0, 100])
break;
default:
return '0px'
}
return scale(d.value) + 'px'
})
td.select('span')
.text(function(d) {
if (d.key == 'conversion_rate') {
return Math.round(100*d.value) + '%'
}
return d.value
})
}
setupData(randomizeData())
d3.select('#update')
.on('click', function() {
setupData(randomizeData())
})
// dummy randomized data: use this function for the socketio data
// instead
//
// socket.on('sellers', function(msg){
// setupData(JSON.parse(msg).items)
// })
function randomizeData() {
var ret = []
for (var i = 0; i < 1000; i++) {
ret.push({
title: "Item " + i,
today_visits: Math.round(Math.random() * 300),
sold_today: Math.round(Math.random() * 200),
conversion_rate: Math.random()
})
}
return ret
}

Is it possible to animate filter in Fabric.js?

Is it possible to animate the images filter in Fabric.js? Such as a "pixelate" filter.
I solved it in the same way like the demo.
Unfortunately filters aren't able to be animated - they need too much processing time.
Here's my Code:
image = ... //Image, where the filter should be applied
var filter = new fabric.Image.filters.RemoveWhite({
threshold: 0,
distance: 140
});
image.filters.push(filter);
image.applyFilters(canvas.renderAll.bind(canvas));
animate(image,1, 400); //Start the Animation
function animate(image,value, stop){
value += ((stop-value)*0.02); //Change the threshold-value
if (image.filters[0]) {
image.filters[0]['threshold'] = value;
console.log(value);
image.applyFilters(canvas.renderAll.bind(canvas)); //Start creating the new image
if(value<stop-100){
setTimeout(function(){act(image,value,stop);},1);
}
}
}
I know the code isn't the most efficient one, but it works. And you can see that Animating filters consumes too much time.
(I tested it with a 1920x1080px image, maybe you can use it with smaller images)
Here is a updated version for the brightness filter
var brightnessValue = 0.9;
var brightnessFilter = new fabric.Image.filters.Brightness({
brightness: brightnessValue
});
fabricImage.filters.push(brightnessFilter);
fabric.util.requestAnimFrame(function brightnessFilterAnimation() {
brightnessValue = brightnessValue - 0.04;
brightnessFilter.brightness = brightnessValue;
fabricImage.applyFilters();
if (brightnessValue > 0) {
fabric.util.requestAnimFrame(brightnessFilterAnimation);
}
});

Resources