Create animated gif from array of png data - javascript

I am working on a project where I'm using the getUserMedia to create a array of png image data from the webcam.
I am now trying to convert this array into one animated gif.
Thanks in advance,
PS: I would like to use only pure javascript but if I need a external library I will use one
demo Code:
<!Doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>getUserMedia API - jsFiddle demo by Vulpus</title>
<script type="text/javascript" src="LZWEncoder.js"></script>
<script type="text/javascript" src="NeuQuant.js"></script>
<script type="text/javascript" src="GIFEncoder.js"></script>
<script type="text/javascript" src="b64.js"></script>
</head>
<body>
<title>RecorderGif</title>
<header>
<h1>getUserMedia</h1>
</header>
<article>
<video id="video" width="320" height="200" style="display:none" autoplay=""></video>
<section>
<button id="btnStart">Start video</button>
<button id="btnStop">Stop video</button>
<button id="btnSave">Download</button>
</section>
<canvas id="canvas" width="320" height="240"></canvas>
</article>
<script type="text/javascript">//<![CDATA[
var encoder = new GIFEncoder();
encoder.setRepeat(0);
encoder.setDelay(250);
encoder.start();
window.onload = function() {
//Compatibility
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
var data = []
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
video = document.getElementById("video"),
btnStart = document.getElementById("btnStart"),
btnStop = document.getElementById("btnStop"),
btnSave = document.getElementById("btnSave")
videoObj = {
video: true,
};
btnStart.addEventListener("click", function() {
var localMediaStream;
if (navigator.getUserMedia) {
navigator.getUserMedia(videoObj, function(stream) {
video.src = (navigator.webkitGetUserMedia) ? window.webkitURL.createObjectURL(stream) : stream;
localMediaStream = stream;
var addFrame = setInterval(function() {
data.push(canvas.toDataURL('image/png'))
},100);
}, function(error) {
console.error("Video capture error: ", error.code);
});
btnStop.addEventListener("click", function() {
localMediaStream.stop();
clearInterval(addFrame)
});
btnSave.addEventListener("click", function() {
for (var i = 0; i < data.length; i++) {
var frame = new Image();
frame.src=data[i]
context.drawImage(frame,0,0)
encoder.addFrame(context);
setTimeout(function(){},100)
};
encoder.finish();
var binary_gif = encoder.stream().getData() //notice this is different from the as3gif package!
var data_url = 'data:image/gif;base64,'+encode64(binary_gif);
window.location.href=data_url;
});
setInterval(function() {context.drawImage(video, 0, 0, 320, 240)},100);
}
});
};
//]]>
</script>
</body>
</html>

You cannot create GIFs natively using canvas but you can use a libray called JSGif to do this:
https://github.com/antimatter15/jsgif
From the read me file:
Now we need to init the GIFEncoder.
var encoder = new GIFEncoder();
If you are making an animated gif, you need to add the following
encoder.setRepeat(0); //0 -> loop forever
//1+ -> loop n times then stop
encoder.setDelay(500); //go to next frame every n milliseconds
Now, you need to tell the magical thing that you're gonna start inserting frames (even if it's only one).
encoder.start();
And for the part that took the longest to port: adding a real frame.
encoder.addFrame(context);
For more details check out its documentation.

Related

How to fix 'HTML 5 Audio pool exhausted, returning potentially locked audio object' in JavaScript?

I am trying to clone this particular website called patatap.com as I am learning JavaScript. But i keep getting this error that I don't know how to solve it. It directs me to a google website where it says that there was a policy change for 'Auto play Policy Changes'. But I can't quite figure out how to apply the given example in my code as I am using objects and howler.js.
error:
https://i.imgur.com/oybV1Vw.png
I have tried not using objects and directly defining all key press events inside keyDown function using if..else and it works but it gets confusing and messy.
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Circles</title>
<link rel="stylesheet" href="assets/css/circles.css">
<script type="text/javascript" src="assets/lib/paper-full.js"></script>
<script type="text/javascript" src="assets/lib/howler.js"></script>
<script type="text/paperscript" canvas="canvas">
var circles = [];
var keyData = {
a:{
color: "purple",
sound: new Howl({
src: ['assets/sounds/bubbles.mp3'],
html5:true
})
},
s:{
color: "green",
sound:new Howl({
src:['assets/sounds/clay.mp3'],
html5:true
})
},
d:{
color:"yellow",
sound:new Howl({
src:['assets/sounds/confetti.mp3'],
html5:true
})
}
}
function onKeyDown(event){
if(keyData[event.key]){
var maxPoint = new Point(view.size.width, view.size.height);
var randomPoint = Point.random();
var point = maxPoint * randomPoint;
var newCircle = new Path.Circle(point, 500);
newCircle.fillColor=keyData[event.key].color;
circles.push(newCircle);
}
}
function onFrame(event){
for(var i = 0; i < circles.length; i++) {
circles[i].fillColor.hue+=2;
circles[i].scale(.9);
}
}
</script>
</head>
<body>
<canvas id="canvas" resize></canvas>
</body>
</html>
I expect that the sounds start playing while I can still make use of objects and howler.js
In function onkeyDown add this line:
keyData[event.key].sound.play();
Hope this will work.

Javascript: image does not load on canvas

I am trying to use an image as the background of my canvas. However, I have a hard time to make the image load -- it always shows as blank:
<html>
<script>
var background = new Image();
background.src = "img/map.png";
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
window.onload = function() {
ctx.drawImage(background, 0, 0);
}
</script>
<body>
<canvas id="myCanvas" width="400" height="400"> </canvas>
</body>
</html>
However, if I put the following two lines into the window.onload function, then the image loads without any issue. what causes this issue? As I will use canvas and ctx in other functions, I really want to define them globally rather than individually define them in each function.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
you can still use them globally. As canvas is not available immediately you are not able to draw picture before on load.
<script>
var background = new Image();
background.src = "img/map.png";
var canvas;
var ctx ;
window.onload = function() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(background, 0, 0);
} </script>
1) The first option is to declare the canvas and context and assign them values after the document has loaded
<!DOCTYPE html>
<html lang="en">
<head>
<title>Onload</title>
<script>
const background = new Image();
background.src = "img/map.png";
let canvas;
let ctx;
window.onload = function() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.drawImage(background, 0, 0);
}
</script>
</head>
<body>
<h1>Method One</h1>
<p>Defined variables globally and assign then after the load event is fired</p>
<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>
2) The second method would be to put the script block after the canvas element so that it is already loaded in the DOM when your javascript tries to access it.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Onload</title>
</head>
<body>
<h1>Method Two</h1>
<p>Put the script after the DOM element you wish to access</p>
<canvas id="myCanvas" width="400" height="400"></canvas>
<script>
const background = new Image();
background.src = "img/map.png";
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
window.onload = function() {
ctx.drawImage(background, 0, 0);
}
</script>
</body>
</html>
I've used the new const and let keywords just to highlight the difference between these two approaches, although using var instead will work fine too. (see let and const)
If you want to avoid global variables then you might structure your code like this
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Onload</title>
<script>
const background = new Image();
background.src = "img/map.png";
draw = context => {
context.drawImage(background, 0, 0);
}
window.addEventListener('load', () => {
const canvas = document.querySelector("#myCanvas");
const ctx = canvas.getContext("2d");
draw(ctx);
});
</script>
</head>
<body>
<h1>Refactor</h1>
<p>Add an event listener and pass in context to draw function</p>
<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>
It doesn't matter that I'm using arrow functions, or querySelector, I just prefer these newer methods. The point is to pass in the canvas context to any function that needs it draw(ctx).
The two lines of code creating the Image() and then setting the background had to be done before the document had finished loading, this is a bit of a fudge. What we really need to do is make sure that the image we are trying to set as the background has loaded before calling drawImage() Using images
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Onload</title>
<script>
draw = context => {
const background = new Image();
background.src = "img/map.png";
background.addEventListener('load', () => {
context.drawImage(background, 0, 0);
});
}
window.addEventListener('load', () => {
const canvas = document.querySelector("#myCanvas");
const ctx = canvas.getContext("2d");
draw(ctx);
});
</script>
</head>
<body>
<h1>Final</h1>
<p>Add an event listener to the image</p>
<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>

Count number of faces using javascript

Is there a way that I can count the number of faces in a live camera.
For example I've three persons in front of a webcam and I'm having 2 pages say, Success.html and error .html and as part of the underlying way to judge the redirection to Success or error pages the condition would be, if only one face is detected, it should be sent to success, else, it should be sent to error .
I'm using tracking.js to detect the face in my web page and currently my code is as below.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>tracking.js - face with camera</title>
<link rel="stylesheet" href="assets/demo.css">
<script src="js/tracking-min.js"></script>
<script src="js/data/face-min.js"></script>
<script src="js/dat.gui.min.js"></script>
<script src="assets/stats.min.js"></script>
<style>
video, canvas {
margin-left: 230px;
margin-top: 120px;
position: absolute;
}
</style>
</head>
<body>
<div class="demo-title">
<p>
tracking.js -
get user's webcam and detect faces
</p>
</div>
<div class="demo-frame">
<div class="demo-container">
<video id="video" width="320" height="240" preload autoplay loop
muted></video>
<canvas id="canvas" width="320" height="240"></canvas>
</div>
</div>
<script>
window.onload = function() {
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var tracker = new tracking.ObjectTracker('face');
tracker.setInitialScale(4);
tracker.setStepSize(2);
tracker.setEdgesDensity(0.1);
tracking.track('#video', tracker, {
camera : true
});
tracker.on('track', function(event) {
context.clearRect(0, 0, canvas.width, canvas.height);
event.data
.forEach(function(rect) {
console.log('face detected');
});
});
</script>
</body>
</html>
Here in my console I'm able to print, face detected.
please let me know how can I detect multiples faces in this window.
Thanks
From the documentation:
myTracker.on('track', function(event) {
if (event.data.length === 0) {
// No targets were detected in this frame.
} else {
event.data.forEach(function(data) {
// Plots the detected targets here.
});
}
});
It seems to be event.data.length that gives you the number of tracked elements.
Fom the docs at https://trackingjs.com/docs.html#trackers
myTracker.on('track', function(event) {
if (event.data.length === 0) {
// No targets were detected in this frame.
} else {
event.data.forEach(function(data) {
// Plots the detected targets here.
});
}
});
Use event.data.length to count the amount of faces.
On your piece of code :
tracker.on('track', function(event) {
context.clearRect(0, 0, canvas.width, canvas.height);
// console log amount of elements found
console.log(event.data.length);
event.data.forEach(function(rect) {
console.log('face detected');
});
});

Improve performance in converting video blob to gif

I have a program(Here) that converts a video blob into a gif using getusermedia. To take advantage of library (GIFEncoder) that let's me convert canvas frames into frames of a gif, I am first drawing the vide blob
to the canvas using ctx.drawImage then I am drawing the frames to the gif.
Is there any more efficient way to do this?
Code:
<!Doctype html>
<html>
<head>
<style type="text/css">
body {
}
</style>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="LZWEncoder.js"></script>
<script type="text/javascript" src="NeuQuant.js"></script>
<script type="text/javascript" src="GIFEncoder.js"></script>
<script type="text/javascript" src="b64.js"></script>
</head>
<body>
<title>RecorderGif</title>
<header>
<h1>RecordGif</h1>
</header>
<article>
<video id="video" width="320" height="200" style="display:none" autoplay=""></video>
<section>
<button id="btnStart">Start video</button>
<button id="btnStop">Stop video</button>
<button id="btnSave">Download</button>
</section>
<canvas id="canvas" width="320" height="240"></canvas>
</article>
<script type="text/javascript">//<![CDATA[
var encoder = new GIFEncoder();
encoder.setRepeat(0);
encoder.setDelay(100);
encoder.start();
window.onload = function() {
//Compatibility
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
var data = []
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
video = document.getElementById("video"),
btnStart = document.getElementById("btnStart"),
btnStop = document.getElementById("btnStop"),
btnSave = document.getElementById("btnSave")
videoObj = {
video: true,
};
btnStart.addEventListener("click", function() {
var localMediaStream;
if (navigator.getUserMedia) {
navigator.getUserMedia(videoObj, function(stream) {
video.src = (navigator.webkitGetUserMedia) ? window.webkitURL.createObjectURL(stream) : stream;
localMediaStream = stream;
var addFrame = setInterval(function() {
data.push(canvas.toDataURL('image/png'))
},100);
}, function(error) {
console.error("Video capture error: ", error.code);
});
btnStop.addEventListener("click", function() {
clearInterval(addFrame)
localMediaStream.stop();
});
btnSave.addEventListener("click", function() {
for (var i = 0; i < data.length; i++) {
var frame = new Image();
frame.src=data[i]
context.drawImage(frame,0,0)
encoder.addFrame(context);
};
encoder.finish();
var binary_gif = encoder.stream().getData() //notice this is different from the as3gif package!
var data_url = 'data:image/gif;base64,'+encode64(binary_gif);
var gif = window.open('about:blank','','width=320,height=240')
gif.document.location.href=data_url
});
setInterval(function() {context.drawImage(video, 0, 0, 320, 240)},100);
}
});
};
//]]>
</script>
</body>
</html>
As there is no native support for GIF encoding in the browsers you very much depend on JavaScript solutions such as the one you're using.
The only way to improve the encoding is to go through the source code and see if you can optimize parts of it such as utilizing typed arrays (if not), conform to engine specifics knowing what works well with the engine compilers (ie. things like using simulated types, full constructor, non-morphing objects etc.).
Another tip is to encode the GIF when the video is not playing (stepping it forward "manually" for each frame using currentPosition and the event for it) and with an off-screen video element. This may leave some more resources for the browser and the engine to run the script faster (depending partly on the system and its support for hardware decoding video - there will still be an overhead updating the video on-screen which may or may not affect the encoding process).
My 2.5 cents..

Creating a FallBack for Flash for IE8

I created an simple animation with Flash and used the CREATEJS PLuggin to convert my SWF to a HTML5 + JS solution. THis works fine in all browsers EXCEPT IE* and older of course.
My solution is to create a Flash FallBack to play the SWF file if the Browser is IE.
Unfortunately I am able to write this and was hopig someone can help me to achieve this.
Please see the HTML file below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CreateJS export from cwtXmasCard2013</title>
<script src="http://code.createjs.com/easeljs-0.6.0.min.js"></script>
<script src="http://code.createjs.com/tweenjs-0.4.0.min.js"></script>
<script src="http://code.createjs.com/movieclip-0.6.0.min.js"></script>
<script src="http://code.createjs.com/preloadjs-0.3.0.min.js"></script>
<script src="cwtXmasCard2013.js"></script>
<script>
var canvas, stage, exportRoot;
function init() {
canvas = document.getElementById("canvas");
images = images||{};
var manifest = [
{src:"images/Image.png", id:"Image"},
{src:"images/Image_1.png", id:"Image_1"},
{src:"images/Image_0.png", id:"Image_0"},
{src:"images/CWT_HolidayCard.jpg", id:"CWT_HolidayCard"}
];
var loader = new createjs.LoadQueue(false);
loader.addEventListener("fileload", handleFileLoad);
loader.addEventListener("complete", handleComplete);
loader.loadManifest(manifest);
}
function handleFileLoad(evt) {
if (evt.item.type == "image") { images[evt.item.id] = evt.result; }
}
function handleComplete() {
exportRoot = new lib.cwtXmasCard2013();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
createjs.Ticker.setFPS(24);
createjs.Ticker.addEventListener("tick", stage);
}
</script>
</head>
<body onload="init();" style="background-color:#D4D4D4">
<canvas id="canvas" width="711" height="661" style="background- color:#ffffff"></canvas>
</body>
</html>

Categories