Thumbnail generation much slower with Pica (with benchmark)
Pithikos opened this issue · comments
Thank you very much for the work on this library. I was hoping it will help speed up a file uploader I have in Vue.js, where I generate thumbnails of dragged files.
My usage of Pica, yields x3-x5 slower timings. I'm not sure I'm using the library wrong or something else is going on. The Demo gives 25-60ms range for cib. Canvas resize always gives <1ms. Without cib, I get around 360ms.
Still I was hoping to get similar timings but in my own usage of Pica, everything is slower than the Demo. Except cib, where in the Demo I get usually <50ms, but I don't know that is an artificial number. However I never managed to get such a timing in my own usage.
NOTE: For all tests I use a 4.4MB image file of 6000x4000. Converting to 360x240. I don't draw anything on the DOM except the final thumbnail generated.
Pica timings
Browser | Options | Image load time | Resize time | Draw thumbnail |
---|---|---|---|---|
Chrome 83 | ww, cib, js, wasm | 25ms | 220ms | 10s |
Chrome 83 | cib, js, wasm | 12ms | 258ms | 6ms |
Chrome 83 | ww, js, wasm | 33ms | 830ms | 15ms |
Firefox 77 | ww, cib, js, wasm | 23ms | 539ms | 2s |
Firefox 77 | cib, js, wasm | 26ms | 971ms | 2s |
Firefox 77 | ww, js, wasm | 22ms | 524ms | 2s |
Own implementation timing
Browser | Image load time | Resize time |
---|---|---|
Chrome 83 | 38ms | 82ms |
Firefox 77 | 38ms | 269ms |
My own implementation is 4x faster on Chrome and 2x faster on Firefox. And I don't use anything fancy like web workers. (I never managed to get the timings seen in the Demo with cib.)
I wonder why this is the case. I would expect using workers to give much faster results. Is there some explanation for this behaviour? Or am I using the library wrong? And why are the timings so different from the Demo example?
Below I have the code that i used for testing.
You just need to drag a file and you will get the timings printed in the console.
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pica/5.3.0/pica.min.js"></script>
<script src="./script.js"></script>
</head>
<body>
<img id="thumbnail" />
</body>
</html>
script.js
const resizer = window.pica({ features: [ 'ww', 'cib', 'js', 'wasm'] });
function onImageLoad(img){
var offScreenCanvas = document.createElement('canvas')
offScreenCanvas.width = 360;
offScreenCanvas.height = 240;
var start = Date.now();
var finish;
// Resize image
resizer.resize(img, offScreenCanvas)
.then(function(result){
finish = Date.now();
console.log("Resized in; " + (finish-start) + "ms")
// Draw thumbnail
let thumbnail = $('#thumbnail')[0];
start = Date.now();
resizer.toBlob(offScreenCanvas, "image/jpeg")
.then(function(blob){
const blobURL = URL.createObjectURL(blob);
thumbnail.src = blobURL
thumbnail.onload = function(){
finish = Date.now();
console.log("Draw thumbnail in; " + (finish-start) + "ms")
}
})
})
}
// Load image on drag
function loadImage(file){
const start = Date.now();
const fileURL = URL.createObjectURL(file);
let img = new Image();
img.onload = function(){
const finish = Date.now();
console.log("Loaded image in; " + (finish-start) + "ms")
URL.revokeObjectURL(fileURL);
onImageLoad(img);
}
img.src = fileURL;
}
// Catch file drop
$('*').on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
e.preventDefault();
e.stopPropagation();
if (e.type == "drop"){
loadImage(e.originalEvent.dataTransfer.files[0])
}
})
And finally my own implementation:
export function generateThumbnail(file){
let w = 360
let h = 240
return new Promise((resolve, reject) => {
const fileURL = URL.createObjectURL(file);
var img = new Image();
var start = Date.now();
var finish;
img.onload = function(){
finish = Date.now();
console.log("Loaded image in; " + (finish-start) + "ms")
URL.revokeObjectURL(fileURL);
const canvas = document.createElement("canvas")
canvas.width = w;
canvas.height = h;
// Resize image
start = Date.now();
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, w, h);
finish = Date.now();
console.log("Resize image in; " + (finish-start) + "ms")
return canvas.toBlob(resolve, file.type, 0.95)
}
img.src = fileURL;
})
}
"your implementation" has poor image quality. That's a reason why pica was created.
Also, i would no recommend use cib
due quality problms in chrome. Just use pica with defaults, those are good enough.
Thanks @puzrin for the prompt reply. Didn't think of that.
It might be worth to highlight it in the Demo (e.g. add a note that explicitly states that the canvas resize is faster but of poorer quality). Since I was only looking at the timing numbers and totally omitted that the canvas resize
at the bottom is of lower quality.
Thanks again for your work!