6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

OpenCV.jsとWebカメラ画像表示

Last updated at Posted at 2021-02-08

OpenCV.jsでのWebカメラの取り扱いはOpenCVのサイトにも記載されているが、メモとしてこちらにも記載。OpenCV.jsの場合はWebカメラを直接扱ってくれるわけではなく、自分でgetUserMediaを扱う必要がある。
getUserMediaを使ったWebカメラ画像の表示については、GoogleのサイトWeb Fundamentalsがわかりやすい。
カメラのアクセスにはlocalhostもしくは、https接続が必要となる。

2021/09/10: iOS対応のためにはvideoタグに playsinline muted が必要だったため追記。

サンプルコード1(単純にWebカメラ画像を表示)

GoogleのサイトWeb Fundamentalsのコードそのまま。

getUserMedia.html
<video id="player" controls playsinline muted autoplay></video>
<script>
  const player = document.getElementById('player');

  const constraints = {
    video: true,
  };

  navigator.mediaDevices.getUserMedia(constraints)
    .then((stream) => {
      player.srcObject = stream;
    });
</script>

上記(とほぼ同じコード)はここにホストしてある。

サンプルコード2(canvasから画像を取得・canvasに表示)

src = cv.imread('canvas')でcanvasから画像を取得して、cv.imshow("canvas2",dst)でcanvasに画像を表示している部分がJavaScriptらしいところ。

ここではOpenCV.jsを持ってきて利用している。ロードは同期的に行った。

grabSnapshot.html
<!DOCTYPE html>
<html>
    <body>
        <video id="player" controls playsinline muted autoplay></video>
        <button id="capture">Capture</button>
        <canvas id="canvas" width=320 height=240></canvas>
        <canvas id="canvas2" width=320 height=240></canvas>
        <script src=https://docs.opencv.org/4.5.1/opencv.js></script>
    </body>
</html>
<script>
  const player = document.getElementById('player');
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const captureButton = document.getElementById('capture');

  const constraints = {
    video: true,
  };

  captureButton.addEventListener('click', () => {
    // Draw the video frame to the canvas.
    context.drawImage(player, 0, 0, canvas.width, canvas.height);

    // OpenCVでグレーに変換、表示
    let src = cv.imread('canvas');
    let dst = new cv.Mat();
    cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY, 0);
    cv.imshow("canvas2",dst)
    src.delete()
    dst.delete()
  });

  navigator.mediaDevices.getUserMedia(constraints)
    .then((stream) => {
      player.srcObject = stream;
    });
</script>

上記(とほぼ同じコード)はここにホストしてある。

サンプルコード3(cv.VideoCaptureの例)

cv.VideoCaptureを使うとvideo要素から画像を取得できるため、サンプルコード2で
context.drawImage(player, 0, 0, canvas.width, canvas.height);のようにいったんcanvasに書き込んで、src = cv.imread('canvas')で読み込んでいた部分が不要になる。
setTimeoutで定期的にprocessVideoを呼び出してOpenCVでフレームの処理を行っている。
ここではOpenCV.jsのロードは非同期的に行った。
OpenCV.jsの読み込みが完了したこと(isCvLoaded==true)と、getUserMediaのvideoの取り込みの準備が終わったこと(onVideoCanPlay呼び出し)を確認してからcap = new cv.VideoCapture(player)を行わないと、cap.read(src)で正しく画像が得られず、画像処理が行えない(時々canvasが黒画面になる問題に遭遇した)。

videoCapture.html
<!DOCTYPE html>
<html>
    <body>
        <video id="videoInput" controls playsinline muted autoplay></video>
        <canvas id="canvasOutput"></canvas>
        <script async src=https://docs.opencv.org/4.5.1/opencv.js onload="onOpenCvReady()"></script>
    </body>
</html>
<script>
    const player = document.getElementById('videoInput');
    let src = null;
    let dst = null;
    let cap = null;
    let isCvLoaded = false

    function onOpenCvReady() {
        // OpenCV.jsのロードが終わったら呼ばれる
        if (cv.getBuildInformation)
        {
            console.log(cv.getBuildInformation());
            onloadCallback();
        }
        else
        {
            // WASM
            cv['onRuntimeInitialized']=()=>{
                console.log(cv.getBuildInformation());
                onloadCallback();
            }
        }
    };

    function onloadCallback() {
        // OpenCV.jsのロード完了
        isCvLoaded = true;
    };

    const constraints = {
        video: true,
    };

    // Attach the video stream to the video element and autoplay.
    navigator.mediaDevices.getUserMedia(constraints)
        .then((stream) => {
            player.srcObject = stream;
            player.addEventListener('canplay', onVideoCanPlay, false);
        });

    function onVideoCanPlay() {
        // ビデオ再生が可能になった
        player.width = player.videoWidth // width, heightを設定しないとcap.read(src)で失敗する。
        player.height = player.videoHeight
        setTimeout(processVideo, 100);
    };

    const FPS = 30;
    function processVideo() {
        try {
            if(!isCvLoaded){
                setTimeout(processVideo, 100);
                return;
            }else if(cap==null){
                // OpenCVのロードが終わり、Videoのフレームがくるのを待つ
                cap = new cv.VideoCapture(player);
            }
            let begin = Date.now();

            // start processing.
            src = new cv.Mat(player.height, player.width, cv.CV_8UC4);
            dst = new cv.Mat();
            cap.read(src);
            cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
            cv.imshow('canvasOutput', dst);
            src.delete();
            dst.delete();

            // schedule the next one.
            let delay = 1000/FPS - (Date.now() - begin);
            setTimeout(processVideo, delay);
        } catch (err) {
            console.error(err.message);
        }
        return;
    };
</script>

上記(とほぼ同じコード)はここにホストしてある。

nodeで動作させるにはopencv4nodejsもあるが、手元の環境(macOS)ではうまくセットアップできず、OpenCV.jsを使って試した。

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?