I had watched the following tutorial https://youtu.be/BTWPDboW38o?si=_21TOjCfrXC3VWIL and tried the same. What I expect is, I want to change the background color of document based on the color of video stream. But it is always returning rgb(0,0,0).
'use strict';
try {
const w = window,
d = document,
ng = navigator,
id = e => {
return d.getElementById(e)
},
cn = id('lh'),
i = id('i'),
o = id('o'),
cx = cn.getContext('2d', {
willReadFrequently: true
}),
face = r => ng.mediaDevices.getUserMedia({
video: {
facingMode: {
exact: 'user'
}
}
}).then(s => {
i.srcObject = s;
i.onloadedmetadata = e => {
setInterval(() => {
let c = 0,
k = -4,
h = cn.height = i.videoHeight,
w = cn.width = i.videoWidth,
dt = cx.getImageData(0, 0, w, h),
io = dt.data,
dl = io.length,
R, G, B;
R = G = B = 0;
cx.drawImage(i, 0, 0, w, h);
o.src = cn.toDataURL('image/webp');
while ((k += r * 4) < dl) {
++c;
R += io[k];
G += io[k + 1];
B += io[k + 2]
};
['R', 'G', 'B'].forEach(e1 => {
eval(e1 + '=' + `~~(${e1}/c)`)
});
let rgb = `rgb(${R},${G},${B})`;
d.body.style.background = rgb
}, -1)
}
});
face(4)
} catch (e) {
alert(e)
}
I expect it should change the background color of document based on video stream.
ok, heres whats wrong with your code
after assigning the srcObject
of a video
element you have to call play
in the setInterval
function when you assign the values of h
and w
you also set the width
and height
of the canvas
, doing this clears the canvas
notes:
you could do with some clarification that after invoking face(4)
the then
function is actually part of the getUserMedia
, it confused me for a while
its probably the most elaborate way perform a Math.floor
I have ever seen
'use strict';
var timer;
var stream;
document.querySelector('input[value=stop]').onclick = e=>{
clearInterval(timer);
if(stream)stream.getTracks().forEach(track=>track.stop());
}
try {
const w = window,
d = document,
ng = navigator,
id = e => {
return d.getElementById(e)
},
cn = id('lh'),
i = id('i'),
o = id('o'),
cx = cn.getContext('2d', {
willReadFrequently: true
}),
face = r => ng.mediaDevices.getUserMedia({
video: {width:100,height:100}
}).then(s => {
stream=s;
i.srcObject = s;
i.play();
i.onloadedmetadata = e => {
timer=setInterval(() => {
let c = 0,
k = -4,
h = cn.height = i.videoHeight,
w = cn.width = i.videoWidth;
cx.drawImage(i, 0, 0, w, h);
let dt = cx.getImageData(0, 0, w, h),
io = dt.data,
dl = io.length,
R, G, B;
R = G = B = 0;
o.src = cn.toDataURL('image/webp');
while ((k += r*4) < dl) {
++c;
R += io[k];
G += io[k + 1];
B += io[k + 2]
};
['R', 'G', 'B'].forEach(e1 => {
eval(e1 + '=' + `~~(${e1}/c)`)
});
let rgb = `rgb(${R},${G},${B})`;
d.body.style.background = rgb
}, -1)
}
});
face(4)
} catch (e) {
alert(e)
}
canvas {
border:1px solid lightgray;
}
video {
border:1px solid lightgray;
}
img {
border:1px solid lightgray;
}
input {
font-size:16px;
padding:5px;
}
<canvas id=lh></canvas>
<video id=i></video>
<img id=o>
<input type=button value=stop>
i added a stop feature so it can be turned off without reloading the page
im afraid that it wont actually run in the stackoverflow website, i think they must have webcam access turned off and wont allow video to play
ive created a much neater version for anyone who visits this page at a later date, it needs a canvas element and its derived 2d context
var stream = await navigator.mediaDevices.getUserMedia({video:true});
var video = document.createElement('video');
video.srcObject = stream;
video.play();
(function update(){
ctx.drawImage(video,0,0,canvas.width,canvas.height);
var img = ctx.getImageData(0,0,canvas.width,canvas.height);
var n = img.data.length;
var r = 0;
var g = 0;
var b = 0;
for(var i=0;i<n;i+=4){
r += img.data[i]/n*4;
g += img.data[i+1]/n*4;
b += img.data[i+2]/n*4;
}//for
document.body.style.background = `rgb(${r},${g},${b})`;
if(abort)return;
requestAnimationFrame(update);
})();
i thought this was a nice little project so ive added a working example, i dont think a lot of these sites that allow code generation allow video, anyway heres what all the fuss is about
var ctx = canvas.getContext('2d');
var balls = ballsmod(10);
var abort = false;
var framerate = 20;
quit.onclick = e=>abort = true;
(function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
balls();
var data = ctx.getImageData(0,0,canvas.width,canvas.height);
var n = data.data.length;
var r = 0;
var g = 0;
var b = 0;
var s = 4;
for(var i=0;i<n;i+=4*s){
r += data.data[i]/n*4*s;
g += data.data[i+1]/n*4*s;
b += data.data[i+2]/n*4*s;
}//for
document.body.style.background = `rgb(${r},${g},${b})`;
if(abort)return;
setTimeout(update,1000/framerate);
})();
function ballsmod(num){
var cols = ['blue','green','red','yellow','lightblue','lightgray'];
var balls = [];
for(var i=0;i<num;i++)balls.push(ball());
function update(){
balls.forEach(update_ball);
}//update
function rnd(size,offset){return Math.random()*size+(offset||0)}
function ball(){
var col = cols[parseInt(rnd(cols.length))]
var r = rnd(20,20);
var x = rnd(canvas.width-2*r,r);
var y = rnd(canvas.height-2*r,+r);
var dx = rnd(20,-10);
var dy = rnd(20,-10);
var ball = {x,y,r,col,dx,dy,update};
return ball;
}//ball
function update_ball(ball){
ball.x += ball.dx;
ball.y += ball.dy;
if(ball.x-ball.r<0 || ball.x+ball.r>canvas.width){
ball.dx *= -1;
ball.x += ball.dx;
}
if(ball.y-ball.r<0 || ball.y+ball.r>canvas.height){
ball.dy *= -1
ball.y += ball.dy;
}
ctx.beginPath();
ctx.arc(ball.x,ball.y,ball.r,0,2*Math.PI,false);
ctx.fillStyle = ball.col;
ctx.fill();
}//update
return update;
}//ballsmod
body {
text-align:center;
}
canvas {
border:1px solid lightgray;
}
input {
font-size:16px;
padding:7px;
margin-left:10px;
vertical-align:top;
}
<input id=quit type=button value=stop>
<canvas id=canvas></canvas>