nodejs - How to add image data from file into canvas - image

The following code is supposed to read an image file and then add the file data into a canvas with the help of the Canvas module.
When I run this code I receive the error message Image is not defined. Is the image object that I'm trying to initialise from a module that I simply import?
var http = require('http'), fs = require('fs'),
Canvas = require('canvas');
http.createServer(function (req, res) {
fs.readFile(__dirname + '/image.jpg', function(err, data) {
if (err) throw err;
img = new Image();
img.src = data;
ctx.drawImage(img, 0, 0, img.width / 4, img.height / 4);
res.write('<html><body>');
res.write('<img src="' + canvas.toDataURL() + '" />');
res.write('</body></html>');
res.end();
});
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

I apologize if I'm wrong here, but it looks like you've found this code somewhere and tried to use it without actually understanding what's happening under the covers. Even if you were to fix the Image is not defined error, there are many others.
I have the fixed code at the end of this post, but I'd recommend thinking more deeply about these issues in the code from your question:
What is Image? Where does it come from? You've imported http, fs, and Canvas, so those things are obviously defined. However, Image hase not been defined anywhere and it is not a built-in.
As it turns out, Image is from the node-canvas module, which you've imported with Canvas = require('canvas'). This means that Image is available as Canvas.Image.
It's important to understand that this is because of the imports you've setup. You could just have easily have done abc = require('canvas'), and then Image would be available as abc.Image.
What is ctx? Where is that coming from?
Again, this is another variable that just hasn't been defined anywhere. Unlike Image, it isn't available as Canvas.ctx. It's just a random variable name that doesn't correspond to anything at this point, so trying to call drawImage on it is going to throw an exception.
What about canvas (lowercase)? What is that?
You are using canvas.toDataURL, but there is no variable called canvas anywhere. What are you expecting this piece of code to do? Right now it's just going to throw an exception saying that canvas is undefined.
I'd recommend reading documentation more closely and looking more closely at any example code you copy into your own applications in the future.
Here is the fixed code, with some comments to explain my changes. I figured this out by taking a quick look at the documentation at https://github.com/learnboost/node-canvas.
var http = require('http'), fs = require('fs'),
Canvas = require('canvas');
http.createServer(function (req, res) {
fs.readFile(__dirname + '/image.jpg', function(err, data) {
if (err) throw err;
var img = new Canvas.Image; // Create a new Image
img.src = data;
// Initialiaze a new Canvas with the same dimensions
// as the image, and get a 2D drawing context for it.
var canvas = new Canvas(img.width, img.height);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width / 4, img.height / 4);
res.write('<html><body>');
res.write('<img src="' + canvas.toDataURL() + '" />');
res.write('</body></html>');
res.end();
});
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

node-canvas now has a helper function, loadImage, that returns a Promise resolving to a loaded Image object. This prevents having to mess around with onload handlers like in the accepted answer.
const http = require('http');
const fs = require('fs');
const Canvas = require('canvas');
http.createServer(function (req, res) {
fs.readFile(__dirname + '/image.jpg', async function(err, data) {
if (err) throw err;
const img = await Canvas.loadImage(data);
const canvas = new Canvas(img.width, img.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width / 4, img.height / 4);
res.write('<html><body>');
res.write('<img src="' + canvas.toDataURL() + '" />');
res.write('</body></html>');
res.end();
});
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');

Related

Embed logo in antmedia live stream via canvas

Am following the blog at https://antmedia.io/how-to-merge-live-stream-and-canvas-in-webrtc-easily/ that explains how to embed a logo in antmedia live stream. However, I couldn't quite figure out to initialise a localStream with javascript SDK as illustrated in the blog. Specifically, where is the implementation of initWebRTCAdaptor():
//initialize the webRTCAdaptor with the localStream created.
//initWebRTCAdaptor method is implemented below
initWebRTCAdaptor(localStream);
A complete working sample would be very helpful.
It seems that blog post is not fully up to date.
Let me share what to do to have this feature.
Just add a localStream parameter to the WebRTCAdaptor constructor.
Secondly, use the below code in place of initWebRTCAdaptor
For the full code, please take a look at this gist.
https://gist.github.com/mekya/d7d21f78e7ecb2c34d89bd6ec5bf5799
Make sure that you use your own image in image.src.(Use local images)
var canvas = document.getElementById('canvas');
var vid = document.getElementById('localVideo');
var image=new Image();
image.src="images/play.png";
function draw() {
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.drawImage(vid, 0, 0, 200, 150);
ctx.drawImage(image,50, 10, 100, 30);
}
}
setInterval(function() { draw(); }, 50);
//capture stream from canvas
var localStream = canvas.captureStream(25);
navigator.mediaDevices.getUserMedia({video: true, audio:true}).then(function (stream) {
var video = document.querySelector('video#localVideo');
video.srcObject = stream;
video.onloadedmetadata = function(e) {
video.play();
};
//initialize the webRTCAdaptor with the localStream created.
//initWebRTCAdaptor method is implemented below
localStream.addTrack(stream.getAudioTracks()[0]);
initWebRTCAdaptor(false, autoRepublishEnabled);
});

HTML5 toDataURL to Google Drive SDK - Multipart Upload with JPEG Data

The multipart upload example here mentions JPEG data . What exactly is it ? How do I fetch it out of an HTML IMG element ? I tried sending the dataUri ( after creating a canvas with drawImage() and extracting its dataUri ; it did not work .
this.sendToDrive = function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var img = document.createElement("img");
var that = this;
img.addEventListener("load", function () {
ctx.drawImage(img, 0, 0);
var dataUrl = canvas.toDataURL("image/jpeg", 1);
//window.open(dataUrl);
var binaryData = dataUrl.replace(/^data:image\/(png|jpeg);base64,/, "");
that.transmitFile(dataUrl, that.title(), token);
});
img.setAttribute("src", this.src);
canvas.width = img.width;
canvas.height = img.height;
}
I do get the complete image in a new tab (window.open() ), but if I transmit it using the 'transmitFile()' method - which simply sends a multipart post to google drive and leads to a 200 ,and download the file created in the drive, it does not work. I leads to errors like Error interpreting JPEG image file (Error interpreting JPEG image file (Not a JPEG file: starts with 0x2f 0x39)) . The data url is correct for sure, but is it what needs to be sent as the 'jpeg data' ? I get similar errors if I use window.btoa(dataUrl) or window.atob(dataUrl).

Canvas ctx.drawImage is not working with a transparent PNG

ctx.drawImage() is not working when I use a transparent PNG, but does work when I use a regular PNG.
var context = document.getElementById("canvas").getContext('2d');
....
function draw(img, x, y){
context.drawImage(img, x, y);
}
//actulaly there is loop here, but for simplicity I put only this.
var img = new Image();
img.src = "images/a.png";
img.onload = draw(img, 10, 10);
If I use a regular PNG image it works, but with a PNG with transparency that has the background deleted, it is not working.
Do you guys have any idea why? Thank you.
img.onload takes a function reference rather than a function call.
So do this:
img.onload=function(){draw(img,10,10);}
If you need to load many images, here is an image preloader that fully loads all images before calling the start() function:
// image loader
// put the paths to your images in imageURLs[]
var imageURLs=[];
// push all your image urls!
imageURLs.push("");
imageURLs.push("");
// the loaded images will be placed in images[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
}

Merge 2 images with node.js?

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

detecting "we have no imagery" of google maps street view static images

I'm generating street view static images like so:
https://maps.googleapis.com/maps/api/streetview?size=1080x400&location=%s&fov=90&heading=235&pitch=0&key=%s
If you visit that link you see an image that says, "Sorry, we have no imagery for this..."
Is there any way to detect this "sorry" state so that I can fall back to another image?
One quick solution would be to load the image file using xmlrpc and check that its md5sum is 30234b543d5438e0a0614bf07f1ebd25, or that its size is 1717 bytes (it's unlikely that another image can have exactly the same size), but that's not very robust since I have seen Google change the position of the text in the image. Though it's a very good start for a prototype.
You could go for image processing instead. Note that it's still not perfectly robust since Google could decide to change the looks of the image anytime. You'll have to decide whether it's worth it.
Anyway, here is how I would do it using jQuery:
load the image and open a 2D context for direct pxiel access (see this question for how to do it)
analyse the image:
sample groups of 2×2 pixels at random locations; I recommend at least 30 groups
a group of 2×2 pixels is good if all the pixels have the same value and their R/G/B values do not differ by more than 10% (ie. they're grey)
count the ratio of good pixel groups in the image
if there are more than 70% good pixel groups, then we are pretty sure this is the “no imagery” version: replace it with another image of your choice.
The reason I do not recommend testing directly for an RGB value is because JPEG decompression may have slightly different behaviours on different browsers.
this situation is already build in in the 3.0 version due
the boolean test status === streetviewStatus.Ok, here is a snippet from my situation solving
if (status === google.maps.StreetViewStatus.OK) {
var img = document.createElement("IMG");
img.src = 'http://maps.googleapis.com/maps/api/streetview?size=160x205&location='+ lat +','+ lng +'&sensor=false&key=AIzaSyC_OXsfB8-03ZXcslwOiN9EXSLZgwRy94s';
var oldImg = document.getElementById('streetViewImage');
document.getElementById('streetViewContainerShow').replaceChild(img, streetViewImage);
} else {
var img = document.createElement("IMG");
img.src = '../../images/ProfilnoProfilPicture.jpg';
img.height = 205;
img.width = 160;
var oldImg = document.getElementById('streetViewImage');
document.getElementById('streetViewContainerShow').replaceChild(img, streetViewImage);
}
As of 2016, you can use the new Street View Image Metadata API.
Now you just need the status field to know if a panorama is found.
Example requests:
https://maps.googleapis.com/maps/api/streetview/metadata?size=600x300&location=78.648401,14.194336&fov=90&heading=235&pitch=10&key=YOUR_API_KEY
{
"status" : "ZERO_RESULTS"
}
https://maps.googleapis.com/maps/api/streetview/metadata?size=600x300&location=eiffel%20tower,%20paris,%20france&heading=-45&pitch=42&fov=110&key=YOUR_API_KEY
{
...
"status" : "OK"
}
You can use the getPanoramaByLocation function (see http://code.google.com/apis/maps/documentation/javascript/services.html#StreetViewService).
try something like this:
function handleMapClick()
{
var ll= new google.maps.LatLng(latitude,longitude);
sv.getPanoramaByLocation(ll, 50, processSVData);
}
function processSVData(data, status) {
if (status==google.maps.StreetViewStatus.ZERO_RESULTS)
{
<DO SOMETHING>
}
}
Request a google street view image and if it has a specific file size it is a 'Not street view avaible'. I did the follwing:
var url = 'google street view url';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function (e) {
if (this.status == 200) {
try {
var image = new Blob([this.response], {type: 'image/jpeg'});
if (image.size) {
if (url.indexOf('640x640') > -1 && image.size === 8410) {
// Not street view
}
if (url.indexOf('400x300') > -1 && image.size === 3946) {
// Not street view
}
}
} catch (err) {
// IE 9 doesn't support blob
}
}
};
xhr.send();
Another way is to load the image and then compare some pixels colors. The "no streetview" image from google is always the same. Here is how you would compare 2 pixels:
var url = STREETVIEWURL
var img = new Image();
// Add some info to prevent cross origin tainting
img.src = url + '?' + new Date().getTime();
img.setAttribute('crossOrigin', '');
img.crossOrigin = "Anonymous";
img.onload = function() {
var context = document.createElement('CANVAS').getContext('2d');
context.drawImage(img, 0, 0);
//load 2 pixels. I chose the first one and the 5th row
var data1 = context.getImageData(0, 0, 1, 1).data;
var data2 = context.getImageData(0, 5, 1, 1).data;
console.log(data1);
// google unknown image is this pixel color [228,227,223,255]
if(data1[0]==228 && data1[1]==227 && data1[2]==223 && data1[3]==255 &&
data2[0]==228 && data2[1]==227 && data2[2]==223 && data2[3]==255){
console.log("NO StreetView Available");
}else{
console.log("StreetView is Available");
}
};
Some potential issues:
I've seen some errors with CrossOrigin tainting. Also, if google changes the image returned this code will break.

Resources