24

Is there an easy way in Node.js to read a PNG file and get the pixels of the image? Something like node-image, but the other way :)

I went through the libraries listed at https://github.com/joyent/node/wiki/modules#wiki-graphics, but they are either simple wrappers around command line tools providing cropping and resizing or complex drawing tools like node-canvas.

Jan Špaček
  • 1,111
  • 1
  • 9
  • 24
  • 2
    Why wouldn't ``node-canvas`` work for you? Push the png into the canvas and then poke at the pixels in the canvas: http://falcon80.com/HTMLCanvas/PixelManipulation/getImageData.html –  Jun 28 '12 at 15:25
  • 3
    @david-ellis Thank you, I will probably use this, but I wonder if there is a library which does just reading images. Whole canvas with Cairo rendering seems to be a bit overkill. – Jan Špaček Jun 28 '12 at 15:40
  • It probably wouldn't be too hard of a library to write, but considering how new Node.js is, I doubt such a library exists, yet. –  Jun 28 '12 at 16:36
  • @DavidEllis [you're wrong](https://github.com/devongovett/png.js) – Alba Mendez Aug 09 '12 at 11:51
  • 1
    Thanks for the info, then, @jmendeth. I did state that I didn't *think* such a library existed. I didn't say I searched for one. –  Aug 09 '12 at 15:48
  • @honzasp By the way, if you find an answer which solves your problem, please **accept** it by clicking at its **green tick**. Thanks! – Alba Mendez Aug 10 '12 at 14:12
  • @DavidEllis oh, sorry if I sounded rude. Not my intention at all. – Alba Mendez Sep 06 '12 at 17:03

3 Answers3

28

This one does both PNG decoding and encoding without native dependancies:

pngjs - PNG encoder/decoder for Node.js with no native dependencies.

An example for inverting the colors of a PNG:

var fs = require('fs'),
PNG = require('pngjs').PNG;

fs.createReadStream('in.png')
  .pipe(new PNG())
  .on('parsed', function() {

    for (var y = 0; y < this.height; y++) {
        for (var x = 0; x < this.width; x++) {
            var idx = (this.width * y + x) << 2;

            // invert color
            this.data[idx] = 255 - this.data[idx]; // R
            this.data[idx+1] = 255 - this.data[idx+1]; // G
            this.data[idx+2] = 255 - this.data[idx+2]; // B

            // and reduce opacity
            this.data[idx+3] = this.data[idx+3] >> 1;
        }
    }

    this.pack().pipe(fs.createWriteStream('out.png'));
});
Gerd
  • 2,265
  • 1
  • 27
  • 46
P.D.P.
  • 627
  • 6
  • 14
  • Excuse me, could you have a look at my question here https://stackoverflow.com/questions/46186815/noise-in-png-produced-by-pngjs-for-nodejs ? I'm having trouble with PNG produced by pngjs - it's noisy... – kolyaseg Sep 13 '17 at 08:40
16

I was about to became mad searching, but I found one:

png.js ― A PNG decoder in JS for the canvas element or Node.js.

var PNG = require('png-js');

var myimage = new PNG('myimage.png');

var width  = myimage.width;
var height = myimage.height;

myimage.decode(function (pixels) {
    //Pixels is a 1D array containing pixel data
});

Please note it's pure JavaScript. Works both in the browser <canvas> and in Node.JS.

There are more properties apart from width and height, see this source.

ColinE
  • 68,894
  • 15
  • 164
  • 232
Alba Mendez
  • 4,432
  • 1
  • 39
  • 55
  • Oh, this looks great, but when I call PNG.decode, I get a one-dimensional array of pixels (according to the [README](https://github.com/devongovett/png.js/blob/master/README.md), [the code](https://github.com/devongovett/png.js/blob/master/png-node.coffee) and my simple test). Do you know how can I get the width and height? :) – Jan Špaček Aug 09 '12 at 17:01
  • Yeah, the module is not very well documented. I'll update my answer. – Alba Mendez Aug 10 '12 at 09:16
  • 1
    Aha, thank you, this is close; but I think the PNG constructor expects the actual bytes of the image, so you have to read the file yourself. It may stuck in an infinite loop if you give it bad data, it doesn't even check the PNG header, so be careful with this library! – Jan Špaček Aug 10 '12 at 13:53
  • Well, the README says you can give it a path or a `Buffer`. In fact, I've been using `new PNG('image.png')` with no problem. But good to know! – Alba Mendez Aug 10 '12 at 14:07
  • I agree, it should be more robust to avoid infinite loops. You can always `convert` the image to RAW data and manually read that. – Alba Mendez Aug 10 '12 at 14:09
  • 1
    I just tested the source code in the answer above (using the path to an existing image), and I got the following error: `/home/anderson/node_modules/png-js/png-node.js:140 throw new Error("Incomplete or corrupt PNG file");`. I specified the path to an existing image, so why would this happen? – Anderson Green Jan 13 '13 at 07:53
  • I consider this deprecated (at least on Node), use `node-pngjs` instead. It has more features, like encoding and incremental processing. See next answer. – Alba Mendez Jan 13 '13 at 09:24
  • This library still works fine. It's great for very small simple scripts. – Qix - MONICA WAS MISTREATED Jul 22 '15 at 22:46
4

I think

var myimage = new PNG('myimage.png');

should be

var myimage = new PNG.load('myimage.png');
Peter Grundmann
  • 115
  • 1
  • 10
  • 2
    Yes, this works, but the IO action is synchronous. I think the best way is `fs.readFile (err, data) -> image = new PNG(data)`. – Jan Špaček Aug 14 '12 at 10:45