Help us understand the problem. What is going on with this article?

WebカメラからJSで画像処理するひな形

概要

最近のブラウザだと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にフレーム間差分が見える。

image.png

Canvasにしてしまえば、各フレームに対する画像処理したり、適当な瞬間のフレームを保存したりとかができるはず。cv.jsとかにも使えそうなので、とにかく煮たり焼いたりなんでもできそう。

何か面白いことをやってみたいけど現状考え中。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away