How to control output bit depth in graphicsmagick (for Node)? - node.js

So I have two PNG images, both non-transparent 24bpp.
One image contains a rainbow, other one contains a single line of text:
I do the same thing with both of them:
var gm = require('gm').subClass({imageMagick: true})
gm("./sources/source.png").bitdepth(24).write("test.png", function(){
console.log("test.png")
});
gm("./sources/source2.png").bitdepth(24).write("test2.png", function(){
console.log("test2.png")
});
where gm is this
And I set both to 24bpp explicitly
In result I have two images with different bit depth:
In some cases I also had 32bpp image.
How can I make it create only 24bpp image (discard alpha channel if needed).
Also, I don't want to create jpgs.
Thanks to #mark-setchell, I could force bit depth. I did it this way in Node:
gm("./sources/source.png")
.out("-define")
.out("png:color-type=2")
.write("test.png", function(){
console.log("test.png")
});
out() is an undocumented method but it basically helps you add custom parameters to commandline. Notice that
.out("-define png:color-type=2")
won't work, it only works if you pass each parameter in individual .out() call
.bitdepth(24) doesn't seem to affect output at all, probably because I did .subClass({imageMagick: true}) above.

My suggestion is to try using -define to set the variable png:color-type=2. As you worked out, and kindly shared with the community, it is done as follows:
gm("./sources/source.png")
.out("-define")
.out("png:color-type=2")
.write("test.png", function(){
console.log("test.png")
});

Related

Leaflet geojson layer with customized divIcon for each feature

I'm trying to set a different divIcon for each point on a leaflet geoJson layer. I have tried everything under the sun but it just doesn't work for me. This is what I'm doing
geoJsonLayer = L.geoJson(null, {
pointToLayer: function(feature, latlng) {
var smallIcon = L.DivIcon.extend({
options: {
iconSize: [27, 27],
html: "<div>" + feature.properties.FEATURE_STYLE.SVG_ELEMENT + "</div>"
}
});
return L.marker(latlng, {icon: new smallIcon()});
},
style: getLayerStyle,
onEachFeature: setFeatureProperties,
});
geoJsonLayer.addTo(baseMap);
feature.properties.FEATURE_STYLE.SVG_ELEMENT is an html <svg> containing the icon.
The icons are displayed ok, but every feature display the same icon.
I've also tried doing the following:
using L.Icon with different .png in iconUrl for each feature
using L.circleMarker with different colors for each feature
They both works as expected (different color / icon per feature). But I can't seem to get the divIcon to display differently for each feature.
Anyone have idea why this is the case?
Thanks in advance.
UPDATE:
This is what feature.properties.FEATURE_STYLE.SVG_ELEMENT looks like
Your code to instantiate a new L.divIcon is more complicated than really necessary, but it works, not considering the SVG part:
https://jsfiddle.net/3v7hd2vx/236/
That being said, please note that:
style option is used for vector layers. Therefore in the case of Point features that are rendered as L.divIcon's, it is not used.
onEachFeature option is applied after the pointToLayer one, because the latter is needed to create the layer that is fed to onEachFeature. Therefore if you build the feature.properties.FEATURE_STYLE.SVG_ELEMENT in there (as the name of your function setFeatureProperties suggests), it is too late.
If you need further help, you would very probably need to share more code, e.g. the style and onEachFeature options, and some sample data, in particular with feature.properties.FEATURE_STYLE.SVG_ELEMENT.

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).

Can I chain commands together with gm for node.js?

Can I chain two or more commands together when using gm, the GraphicsMagick library for node?
Specifically, I've got an image that I'd like to add text to, then put a watermark on it, but nothing I try seems to work.
I've tried using gm(image).drawText(0,0,"Text").composite(logo) ... but that tells me Unrecognized option (-draw). Similar thing when I composite first, then draw text.
I also tried writing the file, then adding the .drawText call to the end, but that didn't work.
So can I chain two or more commands together?
Yes, you can, but not in this situation. You can make some workaround calling gm twice and piping result of first call to another:
const stream = require('stream');
const passThrough = new stream.PassThrough();
gm(image).drawText(0, 0, 'Text').stream().pipe(passThrough);
gm(passThrough).composite(logo).write('./output.png', e => console.log(e || 'OK'));
To be honest gm library sucks. If you don't know anything about GraphicsMagick, gm fails to provide good enough abstraction to hide it. You are constantly forced to use constructions like .resize(240, 240, '!') which make no sense unless you know syntax of GraphicsMagick's -resize option. That's because when you call gm's method it just appends option to some GraphicsMagick command which will be called when you execute .write() method, and this is one of these situations when this approach fails. GraphicsMagick provides few commands which supports different options. Most commonly used command is convert, it supports for example -draw option which is used by .drawText() method. Another command is composite which is used for merging to images together - it doesn't support -draw option. When you use .composite() method gm uses composite command so .drawText() methods starts failing. So, you can chain methods like .drawText() and .resize(), but not .drawText() and .composite().
You can use toBuffer and passe it to the next gm call (this particular code is not tested but I have used a similar technic of AWS Lambda).
gm(image)
.drawText(0, 0, 'Text')
.toBuffer('PNG',function (err, buffer) {
if (err) return handle(err);
gm(buffer)
.composite(logo)
.write('./output.png', e => console.log(e || 'OK'));
console.log('done!');})

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?

nodejs image manipulation with gm / imagemagick

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

Resources