概要
最近のブラウザだとWebカメラからの映像取得が簡単にできるようになっている。
この映像を毎フレームごとに画像処理したい場合、以下のように処理用の配列に変換する流れが一般的っぽい。
MDNに以下2つの良いサンプルがあったので組み合わせると良さそう。
Webカメラ → <video>
以下はWebカメラからMediaStreamを取得してvideo要素に読ませているサンプル
<video>
→ <canvas>
→ImageData
以下はvideo要素からクロマキー合成とかやってるデモプログラム
ソースコード
以下のソースコードはMDNにあったコードを繋げてアレンジしたもの。
せっかく動画を使うので、数行追加する程度の簡単なデモとして、1フレーム前との差分を取ってみた。
動かす場合、ローカルかHTTPS環境でないとWebカメラにアクセスできないので注意。
HTML部分(クリックで展開)
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webカメラから画像処理</title>
</head>
<body>
<video width="160" height="120" autoplay></video>
<div>
<canvas id="c1" width="80" height="60"></canvas>
<canvas id="c2" width="80" height="60"></canvas>
<canvas id="c3" width="80" height="60"></canvas>
</div>
<script src="webcam.js"></script>
</body>
</html>
JavaScript部分(クリックで展開)
webcam.js
const constraints = { audio: false, video: { width: 160, height: 120 } };
navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
const video = document.querySelector('video');
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
video.play();
};
})
.catch(function(err) { console.log(err.name + ": " + err.message); }); // always check for errors at the end.
// https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia
let processor = {
timerCallback: function() {
if (this.video.paused || this.video.ended) {
return;
}
this.computeFrame();
let self = this;
setTimeout(function () {
self.timerCallback();
}, 0);
},
doLoad: function() {
this.video = document.querySelector("video");
this.c1 = document.getElementById("c1");
this.ctx1 = this.c1.getContext("2d");
this.c2 = document.getElementById("c2");
this.ctx2 = this.c2.getContext("2d");
this.c3 = document.getElementById("c3");
this.ctx3 = this.c3.getContext("2d");
let self = this;
this.video.addEventListener("play", function() {
self.width = self.video.videoWidth / 2;
self.height = self.video.videoHeight / 2;
self.timerCallback();
}, false);
},
computeFrame: function() {
let frame1 = this.ctx1.getImageData(0, 0, this.width, this.height);
this.ctx2.putImageData(frame1, 0, 0);
this.ctx1.drawImage(this.video, 0, 0, this.width, this.height);
frame1 = this.ctx1.getImageData(0, 0, this.width, this.height);
let frame2 = this.ctx2.getImageData(0, 0, this.width, this.height);
let l = frame1.data.length / 4;
diffFrame = this.ctx3.createImageData(this.width, this.height);
for (let i = 0; i < l; i++) {
let r = frame1.data[i * 4 + 0] - frame2.data[i * 4 + 0];
let g = frame1.data[i * 4 + 1] - frame2.data[i * 4 + 1];
let b = frame1.data[i * 4 + 2] - frame2.data[i * 4 + 2];
diffFrame.data[i * 4 + 0] = Math.abs(r);
diffFrame.data[i * 4 + 1] = Math.abs(g);
diffFrame.data[i * 4 + 2] = Math.abs(b);
diffFrame.data[i * 4 + 3] = 255;
}
this.ctx3.putImageData(diffFrame, 0, 0);
return;
}
};
document.addEventListener("DOMContentLoaded", () => {
processor.doLoad();
});
// https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas
実用
Webカメラを動かすと3つ目(一番右)のCanvasにフレーム間差分が見える。
Canvasにしてしまえば、各フレームに対する画像処理したり、適当な瞬間のフレームを保存したりとかができるはず。cv.js
とかにも使えそうなので、とにかく煮たり焼いたりなんでもできそう。
何か面白いことをやってみたいけど現状考え中。