I'm trying to play with a webcam delay per channel in JavaScript and my current code runs a bit slow. You can see a demo here
My approach to the delay is creating an array of 12 elements to store pixels(Canvas elements) then shift content from one frame to another by copying pixels (using getImageData and putImageData calls). Since I'm splitting colour channels, my update function looks like this:
function update() {
if (localMediaStream) {
// Update history
for (var i = hs - 1; i > 0; i--) {
var ctxCurr = history[i].getContext('2d');
var ctxPrev = history[i - 1].getContext('2d');
try {
ctxCurr.putImageData(ctxPrev.getImageData(0, 0, w, h), 0, 0);
} catch (err) {
console.log(err);
}
}
// Write the most recent webcam frame
var ctx0 = history[0].getContext('2d');
ctx0.drawImage(video, 0, 0, w, h);
// Get pixel data for most recent,halfway and last frames in history
var r = history[0].getContext('2d').getImageData(0, 0, w, h).data;
var g = history[hs / 2].getContext('2d').getImageData(0, 0, w, h).data;
var b = history[hs - 1].getContext('2d').getImageData(0, 0, w, h).data;
// Get a frame and it's pixels
var imageData = ctx0.getImageData(0, 0, w, h);
var data = imageData.data;
// Write the r,g,b channels using the offset frames for green and blue channels
for (var i = 0; i < data.length; i += 4) {
data[i] = r[i];
data[i + 1] = g[i + 1];
data[i + 2] = b[i + 2];
}
rgb.putImageData(imageData, 0, 0);
}
}
I guess I could skip using the red channel since the most recent frame already has that information. Is there a faster way to manipulate/write pixel data (other than getImageData()
and putImageData()
) or faster approaches to what I'm trying to achieve?
It looks like there are a couple of ways to speed up what you're trying to do. getImageData is pretty slow, and putImageData is frustratingly slow so eliminating those calls will always serve you well. You're right to forget about the red channel. I'd also get rid of this line:
var r = history[0].getContext('2d').getImageData(0, 0, w, h).data;
Also, you should be able to draw the canvas to the appropriate context, so long as you clear the context. I believe your try statement should look like:
try {
ctxCurr.clearRect(0, 0, w, h);
ctxCurr.drawImage(history[i-1], 0, 0);
}
Removing all of those redundant calls to putImageData should help a lot.