Performance issue with canvas.getImageData + canvas.putImageData
Asked Answered
T

1

7

I'm calling getImageData/putImageData a lot and it causes my Chrome process to fill up until it crashes when it reaches 2.5Gb memory.

I'm using simpleheat.js with a leaflet heat map plugin to display heatmap on a canvas.

Basically, I'm calling the draw fn a lot to establish a "heatmap over time" effect. The data I deal with each run (each getImageData/putImageData) is 4Mb, while called at 10-20 fps.

I'm wondering how I can optimize this code. I know very little about working with canvas.

This is the relevant code from simpleheat.js:

draw: function() {
    ...
    var colored = ctx.getImageData(0, 0, this._width, this._height);
    this._colorize(colored.data, this._grad);
    ctx.putImageData(colored, 0, 0);
},
_colorize: function (pixels, gradient) {
    for (var i = 0, len = pixels.length, j; i < len; i += 4) {
        j = pixels[i + 3] * 4; // get gradient color from opacity value

        if (j) {
            pixels[i] = gradient[j];
            pixels[i + 1] = gradient[j + 1];
            pixels[i + 2] = gradient[j + 2];
        }
    }
}

I've tried reducing DOM access (suggested in another question)

var colored = ctx.getImageData(0, 0, this._width, this._height);
var coloredData = colored.data;
this._colorize(coloredData, this._grad);
colored.data = coloredData;
ctx.putImageData(colored, 0, 0);

I think the issue is that the browser doesn't release the 4Mb of data properly since if I keep the page for a while the memory will eventually drop

I've tried some silly attempts to help the GC release the data array (colored.data = null or undefined)

Tref answered 6/9, 2016 at 18:57 Comment(6)
I had a look at your problem. The is a point where GC does not keep up, 90% of the time I could catch the error and then add wait cycles to give GC time to clean up though sometimes this could be up to 16 seconds.before I could getImageData. 20% of the time the page just crashed. I was testing 16Mb image every 10ms (plus execution time) and at 2G I got problems. Do you have a fiddle or codePen showing more of your code as there may be a way past this problem but will need to know what you do to the canvas between put and getImageData.Superheterodyne
First make sure you draw to canvas only with window.requestAnimationFrameHighline
Teemu Ikonen could be right. What do you use as update loop ? window.requestAnimationFrame or setInterval ?Delaney
If you haven't solved this yet, are you able to create an online demo (like a fiddle)? It would make it much easier to help you optimise. I think the simplest way would be to not call getImageData at all (other than maybe an initial call). Create a clamped uint8 typed array, and continuously work on that, pushing it to the canvas with putImageData whenever you want to paint.Superload
Can you show us how you establish the frame rate? I think there lies your problem.Donohue
Have you tried to run it in Chrome and use the Memory profiler to see which object is causing the leak? An object in JavaScript will stay in memory as long as something has a reference to it. The proper way to release a variable is using the delete keyword.Incumber
G
0

When creating the ctx, you can give it an attribute which makes these operations faster.

const ctx = canvas.getContext("2d", { willReadFrequently: true }); More about willReadFrequently: HTML willReadFrequently specifications (If you scroll around there are some other tidbits about canvas that might be useful to performance)

Also, if possible, avoid running getImageData every frame. You can have one image that you can change and place every frame, if there is a way to get the opacity value for the gradient without getting the values of every pixel.

You could also just forget this method entirely and do the same using gpu.js, which wouldn't be too difficult looking at the implementation.

Garner answered 15/12, 2022 at 17:23 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Affray

© 2022 - 2025 — McMap. All rights reserved.