Reputation: 783
I want to merge 2 images using node.js. Or rather, i want to place one smaller image on cordinates x,y on a larger image. Even more precise: I have an image of glasses, and an image of a face and i want to put the glasses on the face. I did some googling, and found some image manipulating libraries, but none seem to be able to merge images.
Upvotes: 20
Views: 40056
Reputation: 381
I do no have enough reputation to add a comment, or else I would to Schmidko's answer.
Sharp works well, however, overlayWith is deprecated and you instead need to use composite. See below:
sharp(path + 'example.jpg')
.composite([{input: path + 'logo.png', gravity: 'southeast' }])
.toFile(path + 'output.png');
If you would like to center the image being overlayed:
gravity: 'centre'
Upvotes: 16
Reputation: 810
Tested some librarys for a similar task and implemented finally this one.
https://github.com/lovell/sharp.
Clean API and all you need to merge two images together.
Example:
sharp(path + 'example.jpg')
.overlayWith(path + 'label.png', { gravity: sharp.gravity.southeast } )
.toFile(path + 'output.png')
Upvotes: 5
Reputation: 281
You might need this: https://github.com/zhangyuanwei/node-images
Cross-platform image decoder(png/jpeg/gif) and encoder(png/jpeg) for Nodejs
images("big.jpg").draw(images("small.jpg"), 10, 10).save("output.jpg");
Upvotes: 28
Reputation: 2442
I've used:
https://github.com/learnboost/node-canvas
to do something similar (build a composite image from components on the fly).
It works great.
Here's some example code:
var Canvas = require('canvas'),
fs = require('fs'),
Image = Canvas.Image;
var _components = [{prefix:'f', count:12},
{prefix:'h', count:12},
{prefix:'i', count:12},
{prefix:'m', count:12}];
var _total = 1;
for (var i=_components.length - 1; i>=0; i--){
_components[i].mult = _total;
_total *= _components[i].count;
}
module.exports.ensureImageExists = function(img, cb){
fs.stat(__dirname + '/../public/images/rb/' + img, function(err, stats){
if (err){
if (err.code == 'ENOENT')
generateImage(img, cb);
else
cb(err);
}
else{
cb();
}
});
}
function generateImage(name, cb){
var re = /rb([0-9]*)\.png/
var num = parseInt(re.exec(name)[1]) % _total;
var parts = [];
for (var i=0; i<_components.length; i++){
var n = Math.floor(num / _components[i].mult);
parts.push(_components[i].prefix + (n + 1));
num -= n * _components[i].mult;
}
var canvas = new Canvas(45, 45),
ctx = canvas.getContext('2d');
drawParts();
function drawParts(){
var part = parts.shift();
if (!part)
saveCanvas();
else {
var img = new Image;
img.onload = function(){
ctx.drawImage(img, 0, 0, 45, 45);
drawParts();
};
img.src = __dirname + '/components/' + part + '.png';
}
}
function saveCanvas(){
canvas.toBuffer(function(err, buf){
if (err)
cb(err);
else
fs.writeFile(__dirname + '/../public/images/rb/' + name, buf, function(){
cb();
});
});
}
}
In this case, the components are selected based upon the name of the image, but you clearly could do otherwise. Also, I imagine you could just stream the image out if you wanted -- I write it to a file so it's available the next time it's requested.
I put a route like this in to handle the generation:
app.get('/images/rb/:img', requireLogin, function(req, res, next){
//first make sure image exists, then pass along so it is handled
//by the static router
rbgen.ensureImageExists(req.params.img, function(err){
next();
})
});
Upvotes: 7