nodejs image manipulation with gm / imagemagick - node.js

I'm writing simple app that downloads JPEGs images from Flickr API, and then process them.
All I want to do, is to pick 4 random pixels from each image and save the HEX values.
Is it possible at all? I read a lot of graphicmagick documentation, but can't find a way to do this.
Whats the best way to decode JPEG and get this values? I tried a few plugins but neither can do this by default...
Take care!

https://npmjs.org/package/get-pixels seems nice for that:
var getPixels = require("get-pixels")
getPixels("lena.png", function(err, pixels) {
if(err) {
console.log("Bad image path")
return
}
console.log("got pixels", pixels.shape)
})

Related

How to render/generate image according to text/string in nodejs?

How can i generate an image of a string that has:
a size in px
embossed effect of the letters in the image
a font
a color
and other less important stuff that i think i can figure out once i achieve whats above like:
rotation of text
drop shadow
basically the user will send a request on how he wants his image to be.
but when i receive the request how should i make use of nodejs to render a png or a base64 url to send it back to the user. is there any libraries or way to achieve this.
i did some previous research and it doesn't seem like there is a frameworks that helps render text with a font and text style like emboss
You can try node canvas implementation: https://github.com/Automattic/node-canvas
Basically you can "draw" anything you want like if you'd be using browser js canvas, but some things may be different
Update - This will cover updating attributes of an image, not pulling text from image and updating that - you may need an image analysis library for that
Use the sharp library to manipulate the image as desired. https://github.com/lovell/sharp
http://sharp.dimens.io/en/stable/
A simple example that resizes (docs will show how to make the changes you want outside of this):
const request = require('request').defaults({ encoding: null });
request.get(imgUrl, params, function (err, res, body) {
sharp(body)
.resize(params.width, params.height)
.toFormat('jpeg')
.toBuffer()
.then((outputBuffer) => {
// outputBuffer contains JPEG image data no wider than params.width and no higher
// than params.height while maintaining quality of image.
let output = "data:" + res.headers["content-type"] + ";base64," + new Buffer(outputBuffer).toString('base64');
return output;
});
The output here will be the base64 image

how to generate an image for each user in node.js

I am making a guess the number game over socket.io in node.js Lets say I ask the person to guess the a number 124 which is obviously shown to the user as 12_ ,I want to tell the person complete 12_, if they guessed 4 as the last digit, that would be right answer, how do I send an image containing the text 12_ for each person over socket.io. I need a way to generate images. I believe storing images for a large number of people would be a bad idea. or could I simply have 7 or 8 plain backgrounds and dynamically overlay text that I want on these backgrounds and send it to the user.
What have I tried
I cannot use node-canvas as it needs a native dependency on Heroku
I cannot use GraphicsMagick as Heroku s free plan doesnt support it
Any suggestions
you have to read the image and convert into base64 on the server side
socket.emit('image', { image: true, buffer: buf.toString('base64') });
where as on the client side you can use websocket to receive image
var ctx = document.getElementById('canvas').getContext('2d');
socket.on("image", function(info) {
if (info.image) {
var img = new Image();
img.src = 'data:image/jpeg;base64,' + info.buffer;
ctx.drawImage(img, 0, 0);
}
});
I know it's a really old question, but maybe it helps someone (as there's very little info about this).
I just wanted to say that you can use GraphicsMagick and ImageMagick in Heroku. You just need to add a buildpack for it to work (it works even on free instances).
Here's a blogpost I made saying how I used GraphicsMagick in Node.js. You can check the code on Github (fair warning, the code sucks, but hey, it works).

Pipe image output from Graphics Magic to response without base64 encoding

I have a Node server where, instead of storing cropped images, I want to crop them in response to an AJAX call, and send them to the client that way. I'm storing the information of what to crop and how to crop it in cookies and the body. On the server I crop it, encode it in base64, and send it back the user. Here is what my code looks like
res.set('Content-Type', 'image/jpeg');
gm(request(body.URL))
.crop(req.cookies[name+"Width"],req.cookies[name+"Height"],req.cookies[name+"X"],req.cookies[name+"Y"])
.stream(function streamOut (err, stdout, stderr) {
if (err) return next(err);
stdout.pipe(base64encode()).pipe(res);
stdout.on('error', next);
});
This works, but I don't like it. I was only able to get this to work by encoding it in base64, but on the client side this is seems slow to decode this to an image. I would rather just send an image directly, but I was unable to get this to work. Pipping the image without decoding it resulted in a gibberish response from the server. Is there a better way to do this? Or does the unsaved image have to be encoded in order to send?
I have no idea how you write node.js - it all looks like a bunch of dots and parentheses to me, but using what I know about the GraphicsMagick command line, I tried this and it does what I think you want - which is to write a JPEG encoded result on stdout:
// Send header "Content-type: image/jpeg"...
var gm = require('gm');
var input = 'input.jpg';
gm(input).resize(350).toBuffer('JPG',function (err, buffer) {
if (err) return handle(err);
process.stdout.write(buffer);
})
Update
Have you considered ruling out the AJAX aspects and just using a static src for your image that refers to the node script? As I said, I do not know node and Javascript but if I generate a thumbnail via a PHP script, I would add this into the HTML
<img src="/php/thumb.php"/>
So that just invokes a PHP script to generate an image. If you remove the /php/thumb.php and replace that with however you have named the node script I suggested above, it should tell you whether the problem is the AJAX or the GraphicsMagick aspects...

Get video resolution in nodejs

I have been trying to get an answer to this without really finding any. Excuse me if this sounds stupid or obvious.
I have a nodejs application and basically I would like to simply get the resolution of a video. Imagine I have film stored on disk and I would like to be able to know if it is in 720p or 1080p or anything else.
I understood that I might need to use ffmpeg to do so, but then I also understood that ffmpeg was mostly used to "record, convert and stream audio and video files".
That does not mean retrieve video resolution.
Thank you for your help
Edit 1:
The node.js app is a desktop app and needs to be portable to Linux, windows and OS X. If possible a portable answer would be more appreciated but of course any answer is welcome.
To be honest I think the best method I found was to use fluent-ffmpeg with ffprobe as you are able to set the the path to the executable. The only problem is that ffmpeg has to be shipped with the app. So different executables have to be shipped, one for each distribution/os/derivation. If anyone has anything better I am open to answers.
Getting the width, height and aspect ratio using fluent-ffmpeg is done like so:
var ffmpeg = require('fluent-ffmpeg');
ffmpeg.setFfprobePath(pathToFfprobeExecutable);
ffmpeg.ffprobe(pathToYourVideo, function(err, metadata) {
if (err) {
console.error(err);
} elseĀ {
// metadata should contain 'width', 'height' and 'display_aspect_ratio'
console.log(metadata);
}
});
There's a npm package called get-video-dimensions that also use ffprobe and it's much easier to use. It also support promises and async/await.
import getDimensions from 'get-video-dimensions';
Using promise:
getDimensions('video.mp4').then(dimensions => {
console.log(dimensions.width);
console.log(dimensions.height);
})
or async/await:
const dimensions = await getDimensions('video.mp4');
console.log(dimensions.width);
console.log(dimensions.height);
I use node-ffprobe to accomplish this for images:
var probe = require('/usr/lib/node_modules/node-ffprobe');
probe(filePath, function (err, data) {
//the 'data' variable contains the information about the media file
});
fileMetaData will have width, height, codec info, aspect ratio etc ...
const ffprobe = require('ffprobe')
const ffprobeStatic = require('ffprobe-static')
const fileMetaData = await ffprobe(fileName, { path: ffprobeStatic.path })
fileName could be video('webm', 'mov', 'wmv', 'mpg', 'mpeg', 'mp4','flv' etc..) or image(jpg, gif, png etc..) path.
fileName example: /path/to/video.mp4 or http://example.com/video.mp4
One way to do this would be to to run another application as a child process, and get the resolution from std out. I'm not aware of any pure node.js solution for this.
See child_process.exec https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback
and ffprobe
How can I get the resolution (width and height) for a video file from a linux command line?

Best way to record a HTML Canvas/WebGL animation server-side into a video?

I have a set of animations which I can make in Canvas (fabric.js) or WebGL (three.js). I need to record them automatically, server-side, through a script and output a video file.
The animations include:
Pictures
Videos (with audio)
Other animations/effects
I have researched a lot during last few months on this.
Results
1. Use PhantomJS + FFMPEG
Run HTML Canvas animations on headless browser(PhantomJS) and record with FFMPEG. Here the issue is PhantomJS supports neither WebGL nor Video element. http://phantomjs.org/supported-web-standards.html
2. Use Websockets to send data back to server using DataURL
Here again, we will need to run the animations on browser (which we can't because we have to do everything on server).
3. Use node-canvas
This is a library by TJ Holowaychuk which allows rendering HTML Canvas on Node.js. But it has its own limitations plus I haven't really explored this field much.
(If someone could shed more light on this library)
If anyone has done it before or can guide me somewhere useful.
All we need to do is use some data to create animations and record it into a video, everything on server side.
You can use electron to render WebGL pages with BrowserWindow option "show" set to false and/or use xvfb-run to run headless.
I don't think node-canvas supports the webgl context, so you'll
have to use a library built around 2d drawing, and it certainly
won't have support for any video codecs.
If you can get your animation to work using node-canvas, you can
grab the animated frames at a rate appropriate for your content,
something like this:
Disclosure: I've successfully used FFmpeg to encode a sequence
of externally generated images, but haven't tried the setInterval()
method below. In addition to the animation overhead itself, I don't
know how exporting a canvas to PNG files at 30 FPS would perform.
// assuming "canvas" is asynchronously drawn on some interval
function saveCanvas(canvas, destFile) {
return new Promise((resolve, reject) => {
const ext = path.extname(destFile),
encoder = '.png' === ext ? 'pngStream'
: 'jpegStream';
let writable = fs.createWriteStream(destFile),
readable = canvas[encoder]();
writable
.on('finish', resolve)
.on('error', err => {
let msg = `cannot write "${destFile}": ${err.message}`;
reject(new Error(msg));
});
readable
.on('end', () => writable.end())
.on('error', err => {
let msg = `cannot encode "${destFile}": ${err.message}`;
reject(new Error(msg));
});
readable.pipe(writable);
});
}
const FPS = 30;
let frame = 0,
tasks = [],
interval = setInterval(() => tasks.push(
saveCanvas(canvas, `frame_${frame++}.png`)), 1000 / FPS);
// when animation is done, stop timer
// and wait for images to be written
clearInterval(interval);
Promise.all(tasks).then(encodeVideo);
function encodeVideo() {
// too much code to show here, but basically run FFmpeg
// externally with "-i" option containing "frame_%d.png"
// and "-r" = FPS. If you want to encode to VP9 + WEBM,
// definitely see: http://wiki.webmproject.org/ffmpeg/vp9-encoding-guide
}
And then use FFmpeg to encode a sequence of images into a video.
For the code behind encodeVideo(), you can look at this example.
Edit: There may be an issue with canvas.pngStream() writing
incorrect frames while the animation loop continuously draws on
that one canvas--maybe a copy of the canvas needs to be created
per frame? That would surely create significant memory pressure.
I think that the chromium headless mode might support WebGL already and is another possibility. The video rendering part is yet to come though:
https://bugs.chromium.org/p/chromium/issues/detail?id=781117
CCapture.js makes this pretty easy.

Resources