nodeca / pica

Resize image in browser with high quality and high speed

Home Page:http://nodeca.github.io/pica/demo/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Thumbnail generation much slower with Pica (with benchmark)

Pithikos opened this issue · comments

commented

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.

commented

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!