Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

OpenCV.jsで動画再生を実施

先日、OpenCV.jsの導入手順や画像をグレーやカラーに変化して表示させてみました。
elsammit-beginnerblg.hatenablog.com

今回はOpenCV.jsで動画の再生を試してみたいと思います。



OpenCV.js公式サイトのサンプルを試してみる

OpenCV.jsの公式サイトには動画再生のサンプルコードが掲載されております。
OpenCV: Getting Started with Videos

しかしながら、、
こちらのコードを実行してみたところ、上手く動かない。。
動いてもなぜかすぐにエラーで止まってしまう。。
エラーコードは、

Uncaught 6640648

とよく分からない。。

■なぜエラーとなってしまったか?

サンプルコードがなぜ動かなかったのか調査してみました。
結果、どうやらサンプルコードだと
src、dstが開放されずたまり続けてしまい最終的にエラーで落ちてしまっているようです。

このため、

cv.imshow('canvasOutput', dst);

setTimeout(processVideo, delay);

の後に、

src.delete();
dst.delete();

で開放させてあげる必要があるようです。

OpenCV.jsでの動画再生コード紹介

では作成した動画再生のコードをご紹介します。
まずはhtmlです。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>OpenCV.js sample</title>
    </head>
    <body>
        <h2>OpenCV.js sample code</h2>
        <p id="status">OpenCV.js is loading...</p>
        <div>
            <select name="imgType" id="ImgType">
                <option value="カラー">カラー</option>
                <option value="グレー">グレー</option>
                <option value="2値化">2値化</option>
            </select>
            <div class="inputoutput">
                <div class="caption">imageSrc <input type="file" id="fileInput" name="file" /></div>
            </div>
            <div class="inputoutput">
                <div class="caption">canvasOutput</div>
                <canvas id="canvasOutput" ></canvas>
            </div>
        </div>
    </body>
    <script src="js/ImgScript.js" type="text/javascript"></script>
    <script async src="js/opencv.js" onload="onOpenCvReady();" type="text/javascript"></script>
</html>

サンプルではvideoタグで再生した動画データをOpenCVでグレーアウト処理を行いとなりに表示するような処理でした。
しかし、、、
同じ動画を表示させる必要はないので、
javascriptのDOMを用いて新規作成したvideoタグを表示せずにOpenCVによる画像処理でcanvasタグに表示するようにいたしました。

さらに、javascriptのコードはこちら。

let inputElement = document.getElementById('fileInput');
let selectElement = document.getElementById('ImgType');

let filename = "";

function onOpenCvReady() {
    document.getElementById('status').innerHTML = 'OpenCV.js is ready.';
}

inputElement.addEventListener('change', (e) => {
    filename = e.target.files[0];
    InitVideo();
    SettingVideo();
}, false);

let videoElement = document.createElement('video');

InitVideo = () =>{
    videoElement.src = URL.createObjectURL(filename);
    videoElement.height = 540;
    videoElement.width = 960;
    videoElement.autoplay = true;
    videoElement.muted = false;
    videoElement.controls = false;
    videoElement.loop = true;
}

SettingVideo = () =>{
    let src = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
    let dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC1);
    let cap = new cv.VideoCapture(videoElement);
    try{
        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }
        cv.imshow('canvasOutput',dst);
        let delay = 1000/30;
        setTimeout(SettingVideo, delay);
        src.delete();
        dst.delete();
    }catch(err){
        console.error(err);
        src.delete();
        dst.delete();
        InitVideo();
    }
}

先ほど記載した通り、videoタグをcreateElementで作成し初期化処理でvideoタグのサイズやattributeを設定しました。

let videoElement = document.createElement('video');

InitVideo = () =>{
    videoElement.src = URL.createObjectURL(filename);
    videoElement.height = 540;
    videoElement.width = 960;
    videoElement.autoplay = true;
    videoElement.muted = false;
    videoElement.controls = false;
    videoElement.loop = true;
}

OpenCV.jsによる画像処理とcanvasタグへの表示はこちら。

SettingVideo = () =>{
    let src = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
    let dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC1);
    let cap = new cv.VideoCapture(videoElement);
    try{
        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }
        cv.imshow('canvasOutput',dst);
        let delay = 1000/30;
        setTimeout(SettingVideo, delay);
        src.delete();
        dst.delete();
    }catch(err){
        console.error(err);
        src.delete();
        dst.delete();
        InitVideo();
    }
}

先ほど記載しました通り、

cv.imshow('canvasOutput',dst);
let delay = 1000/30;
setTimeout(SettingVideo, delay);
src.delete();
dst.delete();

により、srcやdstを都度開放しております。

ただグレーアウトした動画を流すだけだとつまらないところがあったので、
readで読み取ったデータに対して、
selectタグで切り替えた動画種類に応じて画像処理を施すことにより、
流す動画を切り替えるようにしました。

        cap.read(src);
        if(selectElement.value === "2値化"){
            cv.threshold(src, dst, 177, 200, cv.THRESH_BINARY);
        }else if(selectElement.value === "グレー"){
            cv.cvtColor(src,dst,cv.COLOR_RGB2GRAY);
        }else{
            dst = new cv.Mat(videoElement.height, videoElement.width, cv.CV_8UC4);
            dst = src.clone();
        }

■実際に動かしてみる

先ほどご紹介したコードを実行するとこちらのようになります。
選択した動画種類に応じて表示する動画を切り替えて再生させることが出来ました。
こちらは動画を再生してもエラーが出ることはありません。
※こちらの動画はcanvasのサイズを480x270に変更しております。

■最後に

今回はOpenCV.jsで動画再生を試してみました。
やはり画像処理を行っている関係で初回動画読み込みに少し時間を要しておりました。
一回読み込めてしまえばカクつくことなく表示していたので、再生するだけならそこまで重くはないのかな?
初回読み込みだけ気になるだけであとは普通に使えそうです。