Reading a PNG image in Node.js - node.js

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.

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'));
});

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.

I think
var myimage = new PNG('myimage.png');
should be
var myimage = new PNG.load('myimage.png');

Related

How to convert a hex string to base64 for image saving in NodeJS

I'm making a NodeJS sockets.io multiplayer drawing canvas. The data gets saved as color id's in a 2D matrix, and I have an array of color hex codes to match with the id's.
I need to be able to save the image as a file, and so I got to the point of storing the image data as a continuous string, joining every pixel's hex code:
// 'boardData' contains the index of the color for each pixel
// 'colors' is an array on hex values
var hexString = ""
for (var x = 0; x < boardWidth; x++) {
for (var y = 0; y < boardHeight; y++) {
hexString += colors[boardData[x][y]].replace('#','');
}
}
The result is something like ffffff3fbae52dffba...
I managed to convert this string into base64 by doing the following:
var base64Data = Buffer.from(hexString, 'hex').toString('base64')
Then, I tried saving it by doing this:
fs.writeFile("image.png", base64Data, {encoding: 'base64'}, function(err) {
if (err) return err;
console.log('Saved!');
});
But the resulting images seem to have an incompatible format.
When I try saving a 16x16 completely white image, the base64 code comes out as ///////////////////////////..., but when I try online image converters, a 16x16 white image becomes iVBORw0KGgoAAAANSUhEUgAAAAg..., and hardcoding this into the fs.writeFile data works just fine. Actually, seems like even a 1x1 white image yields a lot of characters, so I'm not entirely sure how this even works.
I believe I'm missing a step here. Can someone point me in the right direction? Is there maybe another way of converting this raw color data into an image file?
I've found a solution. This library called Jimp does exactly what i was looking for.
For anyone that wants to know. just had to install the module by openning up a command terminal, navigating to my nodejs server folder and running this:
npm install --save jimp
Then, on my server code, i have to import this library (Note that it has to be uppercase J):
var Jimp = require('jimp')
And so i just do this to save the image:
let jimg = new Jimp(boardWidth, boardHeight);
for (var x=0; x<boardWidth; x++) {
for (var y=0; y<boardHeight; y++) {
var hex = colors[boardData[x][y]].replace('#','');
var num = parseInt(hex+"ff", 16);
jimg.setPixelColor(num, x, y);
}
}
jimg.write("output.png")
Hope this helps someone!

Convert canvas ImageData (Uint8ClampedArray) to openCV matrix (BRG or grey) using opencv4nodejs

I would like to tranform canvas ImageData (of type Uint8ClampedArray) into a n image matrix using opencv4nodejs npm package.
https://www.npmjs.com/package/opencv4nodejs
https://github.com/justadudewhohacks/opencv4nodejs#readme
The github README explains clearly how to transform an image matrix into canvas ImageData, but it does not show what methods to use for the opposite.
const img = ...
// convert your image to rgba color space
const matRGBA = img.channels === 1
? img.cvtColor(cv.COLOR_GRAY2RGBA)
: img.cvtColor(cv.COLOR_BGR2RGBA);
// create new ImageData from raw mat data
const imgData = new ImageData(
new Uint8ClampedArray(matRGBA.getData()),
img.cols,
img.rows
);
I would like to know how to convert a gray-scale canvas ImageData into the a matGRAY. (or a rgba ImageData into BGR matrix). Either strategy would be very helpful to me! I haven't found other documentation for this yet.
Goal:
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0,0,canvas.width, canvas.height);
//Then something like this....
// const matGRAY = opencv4nodejs.imagedata2matrix(imageData);
const _ = require('lodash'); // allows fast array transformations in javascript
const Canvas = require('canvas'); //node-canvas on server side
const cv = require('opencv4nodejs');
You can receive the Uint8ClampedArray image data from client directly
..but here we will create it from scratch.
const canvas = Canvas.createCanvas('2d');
const ctx = getContext('2d');
const image = new Canvas.Image();
image.onload = () => {
ctx.drawImage(image,0,0);
}
image.src = 'path/to/image.jpg'
//get imageData from the image on our canvas
const imageDataObject = ctx.getImageData(0,0,canvas.width,canvas.height);
const imageData = imageDataObject.data;
const width = imageDataObject.width;
const height = imageDataObject.height;
//imageData is Uint8ClampedArray
Now that we have the Uint8ClampedArray, we can convert to matrix
// convert to normal array
const normalArray = Array.from(imageData);
//nest the pixel channels
const channels = 4 //canvas pixels contain 4 elements: RGBA
const nestedChannelArray = _.chunk(normalArray, channels);
const nestedImageArray = _.chunk(nestedChannelArray, height);
//nestedImageArray is the correct shape to be converted to matrix.
const RGBAmat = new cv.Mat(nestedImageArray, cv.CV_8UC4);
//openCV often defaults to BGR-type image matrix, so lets color convert the pixel order
const BGRAmat = RGBAmat.cvtColor(cv.COLOR_RGBA2BGRA);
BGRAmat is an openCV matrix, ready for processing!
In case you are new to Node.js, here is the terminal code to get started.
First, install Node.js then run at your terminal
$ mkdir canvasToMatrix
$ cd canvasToMatrix
$ npm init -y
$ npm install lodash canvas opencv4nodejs
Create new file.js, and paste in the code above. After saving, run
$ node file.js

Is there a way to make svg.js work with node.js

Did someone of you try to make svg.js work with node.js? I tried to use the jsdom module to render svg but jsdom but SVG.supported returns false. Is there a way to make this library work with node.js?
Thanks in advance!
EDIT: Here is my code, I want to make that on Node.js and then probably render the SVG in a pdf or as a png:
var draw = SVG('drawing').size(600, 600)
var image = draw.image('inclusions.png')
image.size(400, 150)
var rect = draw.rect(400, 150).attr({ fill: 'orange' })
for (i = 0; i < 10; i++) {
var point = draw.circle(5)
var xpos = Math.floor((Math.random() * 400) + 1);
var ypos = Math.floor((Math.random() * 150) + 1);
point.x(xpos)
point.y(ypos)
point.fill('black')
}
image.front()
Here is the working example usage of svg.js inside nodejs project,
svgdom is the suggested library from svg.js official website
const window = require('svgdom');
const SVG = require('svg.js')(window);
const document = window.document;
function generateSVGTextLines(width, height, lineList, tAsset) {
var draw = SVG(document.documentElement).size(width, height);
draw.clear();
draw.text(function (add) {
if (lineList) {
for (var i = 0; i < lineList.length; i++) {
add.tspan(lineList[i].text).attr("x", "50%").newLine();
}
}
}).font({
family: tAsset.fontFamily,
size: tAsset.fontHeight,
leading: '1.2em',
anchor: "middle"
}).move(width / 2, 0);
return draw.svg();
}
This link might be helpful: http://techslides.com/save-svg-as-an-image
This documents a client side solution that causes the requisite SVG to be drawn on the end user's browser, rather than on the server, but it provides the end result you want by putting the SVG into an image tag and allowing the user to download it. If keeping the SVG drawing logic secret is a problem, you could use a similar principle by sicing PhantomJS on the generator page and sending the user the image it downloads.

Raphael.js path to SVG

I have seen SVG images on wikipedia which you can open in notepad and find the code written inside it. My question is if I make a circle in raphael, can I display it as an svg image in the browser?
var p = paper.circle(10,10,10).attr({fill:'blue'});
then display it as a svg image in my browser. How can I achieve it?
This would only work on browsers that support SVG. I think it fails on IE9 too because it doesn't provide support for .serializeToString() (though there are shims for this).
window.onload = function () {
var paper = Raphael("container", 100, 100);
var p = paper.circle(10,10,10).attr({fill:'blue'});
var textarea = document.getElementById("code")
var serializer = new XMLSerializer();
textarea.value = serializer.serializeToString(paper.canvas);
};​
See demo here: http://jsfiddle.net/BvWkU/
window.onload = function () {
var paper = Raphael("container", 100, 100);
var p = paper.circle(10,10,10).attr({fill:'blue'});
var textarea = document.getElementById("code")
var serializer = new XMLSerializer();
textarea.value = serializer.serializeToString(paper.canvas);
};​

Image not saving on a Palm device

Updated: webOS version 2.1...
This base64 encoded image data generates the correct data image when I append it to the source of an image, like this:
var img = new Image();
img.src= data
var data = "data:image/png;base64,iVBORw0KGgoAAAANSUh
EUgAAAJYAAACmCAIAAAC3GeEYAAEkgklEQVR4AQD9/wIAADt8Fj5/GUSA IESAIEN/GUd/H0V+IEaEIE
WFHUOEHEqJIE2KIk2KI1GQKFSQLFaQKlaOKVaQKlORLlaYLVaVKFWV KVWUKliWKFiXJlKVIliTJW2oP
m6jOW+kPGqkPmehPWWePWagOmmjP2ykQ2ulQmylRWykQmqmP2qr QGqnP26qSGyvRmquQWyyQGquP2yu
RHSzSHWwSXGxSGytRG+vSG6vSW2vRWquRGqtRGmsQnO1R3Gw SG+rSXavUXWwUnKqR3CuRWquP26zQ3C
yRXK0SHG0SWupR2qoR3CuS2qrQ3CsSG6vS22pR26qSGyq RWetO22uQ2yqP22wRGetP2yyP4TEWgElAB
UrBRYmAx05AidHBB9MCydRDylSGChWGCZUFyFLEyNK Ex5IDBtJBhc/Bx9FDBxDDh5HDyRGExs8DRs4D
B04DRw8Exo6DxMuBw8kAhEeABIYAQ4VABAUAA0S AAwVAg8bAw0bAgwaAxAYAAULAQgQAQcQBQsPAAwQ
AggMAwMLAQAIAAgOBAYOAAsWBg4bChMgDxUk DxcmERopEh8vFBwuExspEhcnDxUpDhcqERUnDhUnDRQ
rDxgsERgvEx8xGQA+fxk7gxU9hBc9ghg/ gR1CgBxBhBtChRxIhyFMiyNMiyNNjiZNiypRkCpSjydRkC
VVkSpTkihYmi9YlC9XlCxVlClYlixW lSpZlS1eli16skJnqDxppj1qpDxmpD9mpD1loj1opz9qqENvq
Udpp0FmqD9npkFtpUVvp0ZvrUVs q0NsrEFtrURsrkBrsT9vskFvrj5srz5ssUJsrkJsrkNtr0NusEVm
qjxrrz5ttkNquEFqtEFu"
I am trying to save the image using my custom service, but it does not save the correct image. Here is the Node.js code for saving the image:
var fs = IMPORTS.require('fs');
var path = IMPORTS.require('path');
var buff = new Buffer(img.substr('data:image/png;base64,'.length), 'base64');
path.exists('/media/internal/wallpapers/', function(exists){
if (exists) {
var id = fs.openSync('/media/internal/wallpapers/i.png', 'w', 666);
var written = fs.writeSync(id, buff,0, buff.length, 0);
fs.closeSync(id);
}
});
The saved version of the image has totally different pixel values.
EDIT
I came to find out that the image data above isn't correct. I am using a PNG library to generate base64 image data of a canvas pixel array. The link is http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/.
That is an alternative to canvas.toDataURL();. webOS does not support toDataURL, so I had to use a library.
Here is the code I have used with this library to manipulate my canvas image data as a pixel array:
EditorAssistant.prototype.getDataURL = function(width,height,pixelArray) {
var p = new PNGlib(height, width, 256); // Construcor takes height, weight and color-depth.
var background = p.color(0, 0, 0, 0);
var k = 0;
for (var j = 0; j<height; j++) {
for (var i =0; i<width; i++) {
var x =i;
var y =j;
p.buffer[p.index(x,y)] = p.color(pixelArray[k], pixelArray[k+1], pixelArray[k+2]);
k+=4;
}
}
return 'data:image/png;base64,'+p.getBase64() ;
}
Does this library work and where am I doing it wrong?
libpng.js only supports 256 color images, so that may be the problem. There are other base64 encoding utilities which may work as expected.

Resources