Webcam snapshot to canvas - javascript

I'm trying to create some little webcam tool that let's you take snapshots with an overlay. Now I've been working with the html5 canvas and got the webcam working. But whenever I try to save the webcam snapshot it just only loads either the webcam snapshot or the overlay and never both.
I've been trying out a couple things this is the closest I could get:
var teaser = new Image();
teaser.src = "http://www.fillmurray.com/g/1200/1200";
teaser.onload = function() {
context.drawImage(teaser, 0, 0);
};
context.drawImage(v, teaser, 0 , 0, w, h); // this only draws the image
// and
context.drawImage(v, 0 , 0, w, h); // this just draws the webcam snapshot
Does anyone know if this is even possible or if I'm just being a complete idiot? So what I want to achieve is to draw two images in 1 canvas and to let the overlay to be on top of the video.
Here is a jsfiddle of what I have so far: http://jsfiddle.net/Px8g3/

You should do this :
context.globalCompositionOperation = "source-atop"
before drawing (or after the 1st draw, try both)
To be more precise :
var teaser = new Image();
teaser.src = "http://www.fillmurray.com/g/1200/1200";
teaser.onload = function() {
context.drawImage(teaser, 0, 0);
};
//context.save(); Try with and without
context.globalCompositionOperation = "source-atop";
context.drawImage(v, 0 , 0, w, h); // Don't know what v is, but i guess it's an image
//context.restore(); Same
To be precise, this make the canvas to be in "source-atop" mode.
What does it means ?
It means that where the top layer (the 2nd image you draw) is transparent, it will keep the background (video) and draw it. Elsewhere, it will draw your 2nd layer

Related

javascript ImageData copy or move performance

I am writing a "waterfall" diagram for an SDR receiver which is displayed in a canvas on a web page.
The canvas has a size of w=1000 h=800 pixels.
The top line is delivered every 50ms from a server.
The browser (using javascript) must move all lines down one line and then insert the new line at the top. This gives the look of a waterfall where all pixels are moving from top to bottom.
It is working fine, but the CPU load for the pixel moving is very high, too high for i.e. a raspberry.
What I am doing is:
var imagedata = context.getImageData(0,0,pixwidth,height-1);
var dataCopy = new Uint8ClampedArray(imagedata.data);
for(i=(dataCopy.length - (2*pixwidth*4)); i>= 0; i--) {
dataCopy[i+ pixwidth*4] = dataCopy[i];
}
imagedata.data.set(dataCopy);
// insert new top line
// ....
context.putImageData(imagedata, 0, 0);
I also tried to directly copy the pixel data in imagedata[some index],
which gives almost the same bad performance.
In another C-Program I did the same thing with a simple memcpy operation which is very fast. But what to do in Javascript ?
There are 800.000 pixels, which are 3.200.000 bytes. How can I copy or move them with the best possible performance in Javascript ?
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');
function draw() {
ctx.fillStyle = `hsla(${360 * Math.random()}, 100%, 50%, 1)`;
ctx.fillRect(0, 0, cv.width, 10);
ctx.drawImage(cv, 0, 10);
}
setInterval(function() { draw() }, 200)
<canvas id="cv" width="800" height="400"></canvas>
After drawing a line, take a snapshot of the entire canvas and redraw it with an offset of 10 px on the y scale. Repeat the process and you will get an waterfall like effect.

Add click event to canvas or make area map

I write this html code :
<div id="container">
<canvas id="imageView" width="1181" height="1181">
<p>Unfortunately, your browser is currently unsupported by our web
application.</p>
</canvas>
<script type="text/javascript">
window.onload = function() {
var c = document.getElementById('imageView');
var cxt = c.getContext('2d');
var img = new Image();
img.src = "map.jpg";
cxt.drawImage(img, 0, 0);
};
</script>
</div>
And write this javascript :
this.mousemove = function(ev) {
if (!tool.started) {
return;
}
var x = Math.min(ev._x, tool.x0),
y = Math.min(ev._y, tool.y0),
w = Math.abs(ev._x - tool.x0),
h = Math.abs(ev._y - tool.y0);
context.clearRect(0, 0, canvas.width, canvas.height);
if (!w || !h) {
return;
}
context.clearRect(x, y, w, h);
context.strokeRect(x, y, w, h);
this code is make a rectangle . I want change this rectangle to a area map , that when I click on the area do something , (for example open google.com) .
If I understand you correctly you want to invoke a function when you hit a pixel on the actual map - not just in the map area.
Method 1
You can check a map click in more than one way. You can simply check for the pixel value at the click point to check if it is inside the area you want it to be by comparing the map color value.
I provided an example below for this method.
Method 2
You can pre-define a polygon which traces the outline of the map area you want to check.
Then build a path (ctx.beginPath(); and ctx.lineTo(..); etc.) to allow the use of the method:
if (ctx.isPointInPath(x, y)) { ... };
This is a good method if you have small regions to check.
Method 3
Store a separate image of the map containing only a matte (sort of an alpha map), That is usually black (or transparent) for non-clickable areas, white for clickable areas.
This is useful if your map is complex color-wise and a simple pixel value check is not trivial.
And speaking of which: you can even provide different solid color values for different areas so that you can define red color = USA, blue = Argentina, etc. As these are not visible to the user the only thing that matters is that the color value can be recognized (for this reason don't save images for this use with an ICC color profile).
Then project the mouse position from the click onto the matte image (which is basically an off-screen canvas where the matte image is drawn into) and check for the color (white or other color).
Example for method 1
This is a simple example, but in any case there are a couple of things you need to know in advance:
That the image is loaded from same server as the page or from a domain that allow cross-origin use. Or else you cannot grab a pixel from the map due to security reasons.
You need to know what color or alpha value to check for. If the map is solid and everything is transparent you just need to check for alpha value above zero (as in this example), and if not just check the RGB value of the region you want to trigger an action with.
ONLINE DEMO HERE
HTML:
<canvas width=725 height=420 id="demo"></canvas>
JavaScript:
var ctx = demo.getContext('2d'),
img = new Image();
/// we need to wait for the image to actually load:
img.onload = function() {
/// image is loaded and we can raw it onto canvas
ctx.drawImage(this, 0, 0);
/// enable mouse click
demo.onclick = function(e) {
/// adjust mouse position to be relative to canvas
var rect = demo.getBoundingClientRect(),
x = e.clientX - rect.left,
y = e.clientY - rect.top;
/// grab a pixel
var data = ctx.getImageData(x, y, 1, 1).data;
/// check it's alpha value to see if we're in a map point
/// this of course assumes the map has transparent areas.
/// if not just check for the color values instead.
if (data[3] > 0) alert('We hit map');
}
}
/// we need crossOrigin allowed image or we can't grab pixel later
img.crossOrigin = 'anonymous';
img.src = 'http://i.imgur.com/x8Ap3ij.png';
Just replace the alert with:
window.open('http://google.com/');
if you want it to open a new window/tab.
You can turn canvas into an anchor link by using addEventListener to listen for clicks on the canvas.
Then you can use window.open to open google in a new browser tab.
Also, you need to use image.onload to give your image time to load before using drawing it.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=function(){
ctx.drawImage(img,0,0);
canvas.addEventListener("click",function(){
window.open("http://google.com");
});
}
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/google.jpg";

Can't drawImage larger then 250px square

I'm having some issues using the drawImage method to place a pre-loaded image larger then 250PX width and height onto a canvas.
//Canvas
var canvas = document.createElement("canvas");
var contex = canvas_image.getContext('2d');
canvas.width = 350;
canvas.height = 350;
canvas.id = 'canvas'
$('.canvas').append(canvas);
//Draw Image to canvas
var imageObj = new Image();
imageObj.onload = new function() {
contex.drawImage(imageObj, 0, 0);
};
imageObj.src = $('img').attr('src');
I can't seem to get it to work with an image larger then 250PX Width or Height. But under 250 the image shows... It's really odd and frustrating.
You must get the context from the canvas element. The code you are showing in the post (not sure if it's a typo that happen when posting the question or not? though you shouldn't be able to draw anything if it's not a typo :-) ) has the following error:
This line:
var contex = canvas_image.getContext('2d');
should be:
var contex = canvas.getContext('2d');
as canvas_image does not seem to exist.
If you already have an image loaded you can draw that directly onto canvas instead - there is no need to do a second load of the image:
contex.drawImage($('img')[0], 0, 0);
just make sure you tap into its load event first as you do with the off-screen image.
var img = $('img');
img.on('load', function(e) {
contex.drawImage(img[0], 0, 0);
}
or call it on window's load event.
Other things to look out for is if the image actually has data in the 350x350 pixel area in top left corner (in case the image is very large). You can test by drawing it scaled to see if there is information there:
contex.drawImage(imageObj, 0, 0, canvas.width, canvas.height);

Use drawImage to draw pixel data on canvas

I have a pixel-array and I would like to draw it onto the canvas, without using the putImageData, because that function ignores clip data.
So, just to make this work i've converted the pixel-array to a data-url, and then appended the url to an image which I then draw onto the canvas, like this:
var ctx = {
display: document.querySelector('canvas#display').getContext('2d'),
convert: document.querySelector('canvas#convert').getContext('2d')
}
var image_data = THE_IMAGE_DATA_THAT_I_ALREADY_HAVE;
ctx.convert.putImageData(image_data, 0, 0);
var image_data_URL = ctx.convert.canvas.toDataURL('image/png');
var converter_image = document.querySelector('img#converter-image');
converter_image.onload = function () {
ctx.display.save();
ctx.display.beginPath();
ctx.display.arc(320, 240, 240, 0, Math.PI*2, true);
ctx.display.clip();
ctx.display.drawImage(converter_image, 0, 0, 640, 480);
ctx.display.restore();
}
converter_image.src = image_data_URL;
However, this is gives a REALLY bad performance, especially since I want to do this 60/sec.
There must be another way, right?
A work around will be to draw your pixel using getImageData / putImageData in an offscreen canvas then drawing the offscreen canvas back in your canvas.
More infos :
Canvas offscreen
Pixel drawing with putImagedata (click on the blank in top of the code to launch it)

Removing an image from a canvas in HTML5

there's an example, which loads 2 images:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var img1 = new Image();
img.src = "/path/to/image/img1.png";
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
var img2 = new Image();
img2.src = "/path/to/image/img2.png";
img2.onload = function() {
ctx.drawImage(img2, 100, 100);
};
I need to remove(replace) img2 from canvas. What is the best way to do it?
I think maybe you misunderstand what a Canvas is.
A canvas is essentially a 2 dimensional grid of pixels along an 'X' axis and a 'Y' axis. You use the API to draw pixels onto that canvas, so when you draw an image you're basically drawing the pixels that make up that image onto your canvas. The reason there is NO method that lets you just remove an image, is because the Canvas doesn't know there's an image there in the first place, it just see pixels.
This is unlike the HTML DOM (Document Object Model) where everything is a HTML element, or an actual 'thing' you can interact with, hook-up script events to etc. this isn't the case with stuff you draw onto a Canvas. When draw a 'thing' onto a Canvas, that thing doesn't become something you can target or hook into, it's just pixels. To get a 'thing' you need to represent your 'thing' in some way such as a JavaScript object, and maintain a collection of these JS objects somewhere. This how how Canvas games work. This lack of a DOM-like structure for Canvas makes rendering very fast, but can be a pain for implementing UI elements that you can easily hook into and interact with, remove etc. For that you might want to try SVG.
To answer your question, simply paint a rectangle onto your Canvas that covers up your image by using the same X/Y coords and dimensions you used for your original image, or try Pointy's solution. 'Cover-up' is probably the wrong terminology, since you're actually replacing the pixels (there are no layers in Canvas).
It's not clear what you want the canvas to show when the image is gone. If you want it to be transparent, you could get the image data and fill it with transparent pixels:
var img = ctx.createImageData(w, h);
for (var i = img.data.length; --i >= 0; )
img.data[i] = 0;
ctx.putImageData(img, 100, 100);
where "w" and "h" would be the width and height of your original image.
edit — if you just want another image there, why not just put one there? It will overwrite whatever pixels are there on the canvas.
You can use clearRect() function to clear the image area.Rather then clearing whole context you can clear only the image area using this:
ctx.clearRect(xcoordinate_of_img1,ycoordinate_of_img1,xcoordinate_of_img1 + img1.width ,ycoord_of_img1 +img1.height );
If what "Sunday Ironfoot" said is right, then the best way to remove an image is by drawing the images once again from scratch. For this, you need to have an array of images and draw only the ones you use. For example,
function EmptyClass{};
var img=new Array();
img[0]=new EmptyClass;
img[0].i=new Image();
img[0].src="yourfile1.jpg";
img[0].enabled=true;
img[1]=new EmptyClass;
img[1].i=new Image();
img[1].src="yourfile2.jpg";
img[1].enabled=false;// <-------- not enabled, should not be drawn equivalent to removing
img[2]=new EmptyClass;
img[2].i=new Image();
img[2].src="yourfile3.jpg";
img[2].enabled=true;
for(var i=0;i<3;i++){
if(img[i].enabled)ctx.drawImage(img[i], 100, 100);
}
P.S. I am creating an engine for javascript canvas. Will post it within a week
Peace
You can erase an image by drawing the same image again, using a different globalCompositeOperation
ctx.globalCompositeOperation ="xor"
ctx.drawImage(img2, 100, 100);
See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
Unlike drawing things yourself, if you 'replace' THE image on a canvas, the old one is still there.
Canvas c2;
...
if (null != Image2) {
var ctx = c2.getContext("2d");
ctx.clearRect(0, 0, c2.width, c2.height);
}
Can you overlay canvas objects (I guess I should try before asking, you can -1 one me for being lazy). I guess I'd be interested in have one canvas element as a background, and then another for a layer objects that pop in and out of view. Might be a little more efficient then having to redraw every image if one gets deleted or moved. I'll play around and see what I can find.
There is ``ctx.clearRect(x, y, w, h)'' but this is not a good way to remove the shape, because it will remove any full or partial shapes in the same area of the removed shape. This shouldn't happen, and may remove one or more shapes, I've found it's best to save all your shapes in a list that usually comes from the database using backend language or ajax request, and add for it's shape object an identifier, when you need to remove a shape just remove that shape from the list using the id or the index, then Redraw the canvas with this new array of shapes without a deleted shape, the next time the page loads, this shape will not be added to this list, because it should be deleted from database.
const projectStamps = [{image_id: 'scream', x: 100, y: 100, id: 1}, {image_id: 'scream', x: 100, y: 100, id: 2}, {image_id: 'scream', x: 50, y: 0, id: 3}, {image_id: 'scream', x: 150, y: 0, id: 4}];
let currentShapes = [];
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
function validStampObj(stamp){
if (typeof(stamp.x) !== 'number' || typeof(stamp.y) !== 'number' || typeof(stamp.image_id) === 'undefined' || !document.getElementById(stamp.image_id)){
return false;
} else {
return true;
}
}
function addStamp(stamp){
if (!validStampObj(stamp)){
console.log("can not add stamp, invalid object");
return false;
}
const image = document.getElementById(stamp.image_id);
stamp['w'] = image.getBoundingClientRect().width;
stamp['h'] = image.getBoundingClientRect().height;
ctx.drawImage(image, stamp.x, stamp.y, stamp.w, stamp.h);
currentShapes.push(stamp);
return stamp;
}
let id = 1;
window.onload = function() {
drawProject();
};
function clearCanvas(){
currentShapes = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
return true;
}
const projectImage = document.getElementById("project_image");
function drawProject(){
if (!projectImage){console.log('missing project image element');return false;}
clearCanvas();
ctx.drawImage(projectImage,0,0);
projectStamps.forEach( (stamp)=>{
addStamp(stamp);
});
}
function removeStamp(targetId){
let targetI = false;
for (let i=0; i<projectStamps.length; i++){
if (projectStamps[i].id == targetId){
targetI = i;
break;
}
}
if (targetI !== false){
/* remove the stamp from drawing stamps list and redraw the data */
projectStamps.splice(targetI,1);
drawProject();
}
}
setTimeout( ()=>{
removeStamp(3);
console.log("removed icon with id 3");
}, 2500 );
<p>Image to use:</p>
<img id="scream" width="35" height="35"
src="https://i.ibb.co/wYyc259/iconimage.png" alt="The Scream">
<img id="project_image" width="450" height="300"
src="https://i.ibb.co/sK5HtQy/bulding-image.png" style="position:absolute;left:-15455px;">
<p>Canvas:</p>
<button onclick="drawProject()">Redraw things</button>
<canvas id="myCanvas" width="450" height="300"
style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
notes if you used clearRect in this example it will remove the part of main image of the canvas not just the icon with id 3 like this code does hope it helps.

Categories