Node.js: image resizing without ImageMagick

Imagenode.jsImage Resizing

Image Problem Overview


I'm developing a web app on Node.js (+ express 4) where users can set their profile image by uploading it to the server. We already limit the file mimetype and max filesize, so the user can't upload more than 200KB png or jpeg images.

The problem is we'd like to resize (serverside) the uploaded image resolution to 200x200 to improve page loading and saving space on disk. After some research, all answers pointed to using any module based on ImageMagick or GraphicsMagick.

However, having to install ImageMagick/GraphicsMagick to do a simple image resizing seems too overkill for me, so, is there any other solution other than this for Node.js?

Edit: I've changed the accepted solution to sharp as the previous solution (lwip) is no longer maintained. Thanks for all your feedback!

Image Solutions


Solution 1 - Image

I would vote for sharp:

sharp('input.jpg')
  .resize(200, 200)
  .toFile('ouput.jpg', function(err) {
    // output.jpg is a 200 pixels wide and 200 pixels high image
    // containing a scaled and cropped version of input.jpg
  });

It's fast, typically 6x faster than the fastest imagemagick-based node bindings, and runs in very little memory, perhaps 10x less. sharp links to the libvips image library directly, there is no shelling out to an external program, and the library itself is faster and more efficient than *magick at this task. It supports useful things like stream, buffer and filesystem input and output, colour management, transparency, promises, overlays, WebP, SVG, and more.

As of sharp 0.20, npm will automatically download complete pre-compiled binaries on most platforms, so there's no need for node-gyp. Just enter:

npm install sharp

or:

yarn add sharp

And off you go.

Solution 2 - Image

I have recently started developing an image processing module for NodeJS without any runtime dependencies (read why). It's still at early stages, but already usable.

What you are asking for would be done as follows:

image.resize(200, 200, function(err, image){
    // encode resized image to jpeg and get a Buffer object
    image.toBuffer('jpg', function(err, buffer){
        // save buffer to disk / send over network / etc.
    });
});

More info at the module's Github repo.

Solution 3 - Image

Take a look at lwip : https://github.com/EyalAr/lwip

Very simple and easy to use

npm install lwip

and then in your node code,

// obtain an image object:
require('lwip').open('image.jpg', function(err, image){

  // check err...
  // define a batch of manipulations and save to disk as JPEG:
  image.batch()
    .scale(0.75)          // scale to 75%
    .rotate(45, 'white')  // rotate 45degs clockwise (white fill)
    .crop(200)            // crop a 200X200 square from center
    .blur(5)              // Gaussian blur with SD=5
    .writeFile('output.jpg', function(err){
      // check err...
      // done.
    });

});

I have successfully implemented this in my file uploader and it works like a charm.

Solution 4 - Image

There is a good image manipulation library written entirely in JavaScript, without dependencies to any other libraries, Jimp. https://github.com/oliver-moran/jimp

Example usage:

var Jimp = require("jimp");

// open a file called "lenna.png"
Jimp.read("lenna.png", function (err, lenna) {
    if (err) throw err;
    lenna.resize(256, 256)            // resize
         .quality(60)                 // set JPEG quality
         .write("lena-small.jpg"); // save
});

Solution 5 - Image

sharp has enjoyed some popularity recently, but it’s the same idea as *Magick bindings.

> However, having to install ImageMagick/GraphicsMagick to do a simple image resizing seems too overkill for me

Image resizing is anything but simple. The JPEG format is particularly complex, and there are several ways to scale graphics with results of varying quality, few of them easily implemented. Image processing libraries exist to do this job, so if there’s no other reason why you can’t install them, go for it.

Solution 6 - Image

According to images-manipulation-performance, Canvas is 2.3 times faster than ImageMagick.

Sample results:

Library Imges per Second Minimum Free Memory
sharp.js 9.501 929Mb
canvas.js 8.246 578Mb
gm.js 4.433 791Mb
gm-imagemagic.js 3.654 804Mb
lwip.js 1.203 54Mb
jimp.js 0.445 82Mb

Solution 7 - Image

If you don't need a large image, you can resize it on the client side before uploading it:

Reading files in JavaScript using the File APIs

https://stackoverflow.com/questions/2434458/image-resizing-client-side-with-javascript-before-upload-to-the-server

Many users might have a good picture of themselves from a smartphone, and many of them are over 200kB. Note that client-provided data is not to be trusted, so server-side checks still apply.

Solution 8 - Image

I was using lwip (as previously suggested by arvind) but switched to png-crop. It seems to work a little faster for me (Win 8.1 x64, Node v0.12.7). The code in the repo looks incredibly lightweight, and operationally it's simple to use.

var pngcrop = require('png-crop');
var config = {left: 10, top: 100, height: 150, width: 150};
pngcrop.crop('cats.png','cats-cropped.png',config);

Of course, it'll only do png files...

Solution 9 - Image

Sharp work very well and is easy to use with streams, work like a charm, but you need to compile it with the node version, this is a downside to it. I was using Sharp for image processing, with an image from an AWS S3 bucket and worked perfectly, but I had to use another module. GM didn't work for me, but Jimp worked very good!

You have to pay attention to the path of the written picture, it might give you some errors if you start the path with a "/".

This is how I used Jimp in nodeJS:

const imageUrl = `SOME_URL`;
let imgExported = 'EXPORTED_PIC.png';

Jimp.read(imageUrl)
    .then(image => {
        image   
            .resize(X, Y) 
            .write(`tmp/`+ imgExported, err => { 
                if(err) 
                    console.error('Write error: ', err);
                else { ... // don't forget to put a callback() } }
                    
            });

Also watch out for the order of execution, put a callback so other things don't happen when you don't want to. Tried using "await" for the Jimp.read() but it didn't do the job well.

Solution 10 - Image

You can do this using jimp (node_module)

Local Write:

Jimp.read(path) // this can be url or local location
      .then(image=> {
          image
            .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
            .write('path-to-save');
      })
      .catch(err => {
        console.log(err);
      });

To upload to s3 or where ever you like.

Jimp.read(urls) // this can be url or local location
          .then(image=> {
              image
                .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
                .getBase64(Jimp.AUTO, (err, res) => {
                  const buf = new Buffer(
                    res.replace(/^data:image\/\w+;base64,/, ""),
                    "base64"
                  );
                  var data = {
                    Key: key,
                    Bucket: bucket,
                    Body: body,
                    ContentEncoding: "base64",
                    ContentType: "image/jpeg"
                  };
                  s3.putObject(data, function(err, data) {
                    if (err) {
                      throw err;
                    } else {
                      console.log("succesfully uploaded the image!");
                    }
                  });
                });
          })
          .catch(err => {
            console.log(err);
          });

Solution 11 - Image

I like resize-img library for its simplicity.

const fs = require('fs');
const resizeImg = require('resize-img');

(async () => {
	const image = fs.readFileSync('unicorn.png');

    const newImage = await resizeImg(image, { width: 128, height: 128 });
 
    fs.writeFileSync('unicorn-128x128.png', newImage);
})();

Solution 12 - Image

Implemented image resize using Google Drive API v3. This method is recommended for Google Apps Script to insert images into Google Sheets.

Algorithm:

  1. Upload image to the Google Drive folder.
  2. Get image thumbnail public URL.
  3. Replace 'resize' parameter in the URL with necessary width and/or height. (Default thumbnail size is 220px).
  4. Download resized thumbnail from the Google Drive.

See example here: https://github.com/dobromyslov/google-drive-utils/blob/511c44c2c48862b47c60038423b7f71bf1d28f49/src/index.ts#L150

And beware of GDrive quotas:

  • queries per day: 1000000000

  • queries per 100 sec per user: 1000

  • queries per 100 sec: 10000

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionzacr0View Question on Stackoverflow
Solution 1 - ImagejcupittView Answer on Stackoverflow
Solution 2 - ImageEyalArView Answer on Stackoverflow
Solution 3 - ImageArvindView Answer on Stackoverflow
Solution 4 - ImageedtechView Answer on Stackoverflow
Solution 5 - ImageRy-View Answer on Stackoverflow
Solution 6 - ImageDimitry IvanovView Answer on Stackoverflow
Solution 7 - ImageAndrei VolginView Answer on Stackoverflow
Solution 8 - ImageDan CaseleyView Answer on Stackoverflow
Solution 9 - ImageAlex SeceleanuView Answer on Stackoverflow
Solution 10 - ImageNouman DilshadView Answer on Stackoverflow
Solution 11 - Imagens16View Answer on Stackoverflow
Solution 12 - ImageViacheslav DobromyslovView Answer on Stackoverflow