[wasm] Optimization of tf.browser.fromPixels.
See original GitHub issueI noticed that tf.browser.fromPixels can be very slow when it spends time in this loop:
const numPixels = width * height;
values = new Int32Array(numPixels * numChannels);
for (let i = 0; i < numPixels; i++) {
for (let channel = 0; channel < numChannels; ++channel) {
values[i * numChannels + channel] = vals[i * 4 + channel];
}
}
(Also reported by #4218)
I was able to get significantly better performance with my application by calling this function instead:
function convertArrayBufferRGBAToUInt8RGB(buffer) {
// create view of ArrayBuffer
const x = new Uint8Array(buffer);
const n = x.length;
let i = 0;
let j = 0;
// loop through all pixels and shift them over to remove alpha
while (i < n) {
x[j] = x[i]; i++; j++;
x[j] = x[i]; i++; j++;
x[j] = x[i]; i++; j++;
i++;
}
// return a subset of the original
return x.slice(0, j);
}
And then calling it like sort of like this:
const fromPixels2DContext = document.createElement('canvas').getContext('2d'); // only call this once
function fromPixelsFast(inputElt, width, height) {
return tf.tidy(() => {
fromPixels2DContext.canvas.width = width;
fromPixels2DContext.canvas.height = height;
fromPixels2DContext.drawImage(inputElt, 0, 0, width, height);
const buffer = fromPixels2DContext.getImageData(0, 0, width, height).data.buffer;
const UInt8RGB = convertArrayBufferRGBAToUInt8RGB(inputArray);
return tf.tensor3d(UInt8RGB, [height, width, 3]);
});
}
I think the thing that makes it faster is connected to:
- Using Uint8Array instead of UInt32Array (small effect)
- The unrolled loop is better optimized by the browser. (medium effect)
- It is more efficient to shuffle data in one array than between two arrays due to cache effects. (big effect)
(But I have not done rigorous testing)
It might even be possible to get a little more speed from .subarray
instead of .slice
to avoid making the copy.
A complete example using this function with blazeface and a custom model on a webcam inside a webworker is available in this repo.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:5 (3 by maintainers)
Top Results From Across the Web
Tensorflow.js tf.browser.fromPixels() Function
The tf.browser.fromPixels() function is used to creates a Tensor of pixels values of an specified image. Syntax: tf.browser.
Read more >TensorFlow.js API
This function returns the shape of the result of an operation between two tensors of size s0 and s1 performed with broadcast. Parameters:...
Read more >How can I get this basic example working of TensorFlow.js ...
forEach(canvas => { const xs = tf.browser.fromPixels(canvas); const ys = tf.tensor([1]); // output 1, since this canvas is from the `t` ...
Read more >Run TensorFlow Models in the Browser | by André Ribeiro
fromPixels ' converts the canvas data into a Tensor (line 8). 'tf.image.resizeBilinear' resizes the image from the canvas size to the model size ......
Read more >End-to-End ML Solutions using TensorFlow.js
In the above code example, an HTML image is converted into a tensor using the tf.browser.fromPixels JavaScript function. In Listing 6-4b, the same...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@kylemcdonald This is very much inline in what I’m suggesting in #4218
Two suggestions that can enhance your code:
Try OffscreenCanvas before DOM, it’s slightly faster and works inside web workers. But it’s not supported on Firefox yet.
Try GL
readPixels
method first as its quite a bit faster.And fallback to 2D context and
getImageData
if cannot get GL context.But…one issue here would be that
getImageData
returns data starting top/left andreadPixels
returns data starting bottom/left, so processing loop needs to be inverted.Of course, best method would be to implement
fromPixels
directly in WASM backend just like it’s implemented in WebGL backend.Thanks for reporting this @kylemcdonald ! I’m going to close this as a duplicate of https://github.com/tensorflow/tfjs/issues/4218 - which was created 1 day earlier 😃
Thanks for the greaat discussion on this issue - this will be a really helpful reference for us!