Coder Social home page Coder Social logo

ytiurin / downscale Goto Github PK

View Code? Open in Web Editor NEW
94.0 4.0 16.0 35.77 MB

Better image downscale with canvas.

Home Page: https://ytiurin.github.io/downscale/demo/multiselect.html

License: MIT License

JavaScript 76.47% HTML 15.92% Makefile 7.61%
crop downscale downsample fast jpeg image linear performance photo pica

downscale's Introduction

Better image downscale with canvas (demo)

This function downscales images in the browser, producing a better quality result, than the traditional CanvasRenderingContext2D.scale() method. It neutralises the "fuzzy" look caused by the native canvas downsampling, when processing relatively large images like photos taken with a smartphone. Check the demo page.

Better image downscale demo

Motivation

While other image resizing libraries are based on complex interpolation algorithms such as Lanczos resampling, image downscaling usually doesn't require that complexity, because there is no interpolation happening (in other words we don't create new pixels).

On the other hand, browsers implement very fast HTMLCanvasElement downsampling, when the pixel from source position is directly transfered to the destination position, loosing all the neighbouring pixels information. The resulting image may often look very noisy.

To resolve this problem, the proposed function does a simple area-average downsampling, producing preferable results with relatively small processing time.

Performance

This function uses the technique, proposed by Paul Rouget in his article about pixel manipulation with Typed Arrays. His method reduces the number of read/write operations to the ArrayBuffer of the ImageData returned by the CanvasRenderingContext2D.getImageData() method. This saves overall processing time when you want to iterate through every pixel of the source image.

Also, the usage of Math.round() method is avoided in favour of Bitwise operators, giving a significant boost in performance in some browsers.

Image cropping

Image cropping is very often used in pair with resizing, but both can be very naturally combined. As we don't need to iterate through pixels in cropped areas, the function does both downscaling and cropping in range of the same processing loop. This saves some memory and processing time.

By default, the source image is cropped in the way, that the center of the source image is transfered to the resulting image.

Rollback to canvas resizing

The function also uses basic canvas resizing method when the scale factor of the resulting image is greater than 0.5x. So the better downscaling happen only when the resulting image is at least 2 times smaller than the initial image. In other cases basic canvas resizing gives better image quality result.

Install

npm install downscale

Syntax

Promise<DOMString> downscale(source, width, height[, options]);

Parameters

source
Defines the source of the image data to downscale. This can either be:
width
A Number indicating width of the resulting image. If the value is 0, the width is adapted to keep the same aspect ratio as in the source image.
height
A Number indicating height of the resulting image. If the value is 0, the height is adapted to keep the same aspect ratio as in the source image.
options (optional)
An object with properties representing optional function parameters:
  • imageType
    A DOMString indicating image format. Possible values are jpeg, png, webp. The default format type is jpeg.
  • quality
    A Number between 0 and 1 indicating image quality if the requested imageType is jpeg or webp. The default value is 0.85.
  • returnBlob
    A Boolean indicating if the returned Promise should resolve with Blob object representing the resulting image. The default value is false.
  • returnCanvas
    A Boolean indicating if the returned Promise should resolve with HTMLCanvasElement containing the resulting image. The default value is false.
  • sourceX
    A Number indicating distance from the left side of the source image to draw into the destination context. This allows to crop the source image from the left side. The default value is calculated to centralize the destination rectangle relatively to the source canvas.
  • sourceY
    A Number indicating distance from the top side of the source image to draw into the destination context. This allows to crop the source image from the top side. The default value is calculated to centralize the destination rectangle relatively to the source canvas.

Return value

A Promise that resolves to a DOMString containing the resulting image in data URI format.

Examples

Send image data with <form>

This is just a simple code snippet which uses the form file input as a source of the image data.

HTML

<input type="file" accept="image/*" onchange="filesChanged(this.files)" multiple />
<form method="post"><input type="submit"/></form>

Javascript

function filesChanged(files)
{
  for (var i = 0; i < files.length; i++) {
    downscale(files[i], 400, 400).
    then(function(dataURL) {
      var destInput = document.createElement("input");
      destInput.type = "hidden";
      destInput.name = "image[]";
      destInput.value = dataURL;
      // Append image to form as hidden input
      document.forms[0].appendChild(destInput);
      // Preview image
      var destImg = document.createElement("img");
      destImg.src = dataURL;
      document.body.appendChild(destImg);
    })
  }
}

Send image data with FormData

You can use even cleaner FormData interface to send pure blob data to the server.

HTML

<input type="file" accept="image/*" onchange="filesChanged(this.files)" multiple />
<button onclick="submitForm()">Submit form data</button>

<div id="previews"></div>

Javascript

var formData = new FormData();
var URL = window.URL || window.webkitURL;

function filesChanged(files)
{
  for (let i = 0; i < files.length; i++) {
    downscale(files[i], 400, 400, {returnBlob: 1}).
    then(function(blob) {
      // Append image to form as a blob data
      formData.append("userpic[]", blob, files[i].name);
      // Preview image
      var destImg = document.createElement("img");
      destImg.src = URL.createObjectURL(blob);
      document.body.appendChild(destImg);
    })
  }
}

function submitForm()
{
  var request = new XMLHttpRequest();
  request.open("POST", "http://foo.com/submitform.php");
  request.send(formData);
}

Resize <img> element

Processing an <img> element is quite simple. The function will wait for image load, so you don't have to worry about it.

HTML

<img id="source" src="../public/1.jpg" />

Javascript

var sourceImg = document.getElementById('source');

downscale(sourceImg, 400, 400).
then(function(dataURL) {
  var destImg = document.createElement('img');
  destImg.src = dataURL;
  document.body.appendChild(destImg);
})

Load image from URL

The function can upload the source image from the given URL with no extra code needed. Keep in mind that the image should share origin with the code file.

var imageURL = "/public/1.jpg";

downscale(imageURL, 400, 400).
then(function(dataURL) {
  var destImg = document.createElement('img');
  destImg.src = dataURL;
  document.body.appendChild(destImg);
})

Other libraries

Check out other great in-browser image resizing libraries:

License

MIT

downscale's People

Contributors

adrianmg avatar dependabot[bot] avatar faleidel avatar ytiurin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

downscale's Issues

EXIF Orientation

How easy would it be to support the EXIF Orientation in JPGs?

Uglify fails to minify

I'm using create-react-app and it fails to generate the build with this module because Uglify does not support ES6 template strings.

downscale

unsafe-eval when using content-security-policy

the following lines will produce unsafe-eval when using content-security-policy

https://github.com/ytiurin/downscale/blob/master/src/downscale.js#L14
https://github.com/ytiurin/downscale/blob/master/src/downscale.js#L20

The use of new Function is considered unsafe. I am not sure why this approach was taken, but my guess is there was a no-op function that was needed. I think the safer approach would be

function _noop(){}

function createTiming(enabled, source, destWidth, destHeight)
{
  var start  = new Date
  var timing = {}
  var prev   = start
  var n      = "01"

  return {
    mark: enabled ? function(name) {
      name = n + ". " + (name || "...")
      timing[name] = { "time (ms)": (new Date) - prev }
      prev = new Date
      n = ("0" + ((n >> 0) + 1)).substr(-2)
    } : _noop,
    finish: enabled ? function() {
      timing[n + " TOTAL"] = { "time (ms)": (new Date) - start }
      console.log("IMAGE SOURCE:", source)
      console.log("DOWNSCALE TO:", destWidth + "x" + destHeight)
      console.table(timing)
    } : _noop
  }
}

Getting duplicate Images

While I was using this in my project, I am using multiple input file type fields and each input can take multiple images . When I am using this function to resize the images I am getting duplicate images and I'm guessing this is probably because the loop is not waiting for the downscale function to finish and the loop continues.

`(value + 0.5) << 0` might actually be slightly faster, and is more accurate

For the round function, I noticed that you use 0.49, which seems to give a speed boost in some circumstances, but with random numbers, apparently not.

function createRandom(count, scale) {
  const a = new Float64Array(count);
  for (let i = 0; i < count; i++) {
    a[i] = Math.random() * scale;
  }
  return a;
}

function roundA(a, b) {
  for (let i = 0; i < a.length; i++) {
    b[i] = (a[i] + 0.49) << 0;
  }
}

function roundB(a, b) {
  for (let i = 0; i < a.length; i++) {
    b[i] = (a[i] + 0.5) << 0;
  }
}

function trunc(a, b) {
  for (let i = 0; i < a.length; i++) {
    b[i] = a[i] << 0;
  }
}

function main() {
  const a = createRandom(100000000, 1000);
  const b = new Int32Array(a.length);
  console.time("roundA");
  roundA(a, b);
  console.timeEnd("roundA");
  console.time("roundB");
  roundB(a, b);
  console.timeEnd("roundB");
  console.time("trunc");
  trunc(a, b);
  console.timeEnd("trunc");
  console.time("roundA");
  roundA(a, b);
  console.timeEnd("roundA");
  console.time("roundB");
  roundB(a, b);
  console.timeEnd("roundB");
  console.time("trunc");
  trunc(a, b);
  console.timeEnd("trunc");
}

main();
roundA: 301.97607421875 ms
roundB: 184.259033203125 ms
trunc: 179.185791015625 ms
roundA: 177.18603515625 ms
roundB: 179.783935546875 ms
trunc: 178.2119140625 ms

Downscal doesn't work for image type TIF

Hi, there
I've used this library for moths and it worked very good so far.
But I just noticed that the downscale function doesn't work for TIF images.
Is there a way that I could figure this out?

Thanks!

Out of memory issue

I have to say this is indeed a great tool for downscaling image. The downscale.js has better balance between processing speed and image quality. However, I encountered two problem when using the downscale.js. The major problem is that it is very memory consuming. Even only one photo (about 10m size, 6000 X 4000px), it consumes 100m memory. It seems the memory (probably the ImageData variables) is not freed properly. I tested the provided demo (https://ytiurin.github.io/downscale/demo/multiselect.html) and I chose 10 photos (taken by camera, all about 10m size, 6000 X 4000px), then the browser will crash and report ‘out of memory’ error. The second problem is that the downscale.js doesn’t support IE (IE10 or IE11), it will report ImageData not support error. I am not proficient in this area, so could you kindly help to check these problems? Thank you!

Use performance.now() instead of new Date for measuring performance in demo?

Hi,

I noticed that demo use new Date for measuring performance metrics

var start = new Date
canvasResize(source, WIDTH, HEIGHT).
then(function(r) {
canvTime = (new Date) - start
next(r)
})

Should it use performance.now() instead?

  • it's more precise in orders of magnitude (SO answer)
  • Google lighthouse audit also recommends using performance.now() over Date (link)

Regards,
Trivikram

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.