2020

5

1

Javascriptでwebカメラ映像にぼかし機能付けてやんよ!!!

タグ: |

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

ZOOMが一般化してきて以前よりお世話になる回数もめちゃめちゃ増えてあっという間にバーチャル背景がリア充達のおもちゃになったので、
この辺りの技術もちょっとはかじってみようと思いましたが
いきなりバーチャル背景なんてのもハードルが高いので、WEBカメラの映像にモザイクくらいかけてストリーミングでもしてみようかと

WEBカメラの映像のプライバシーについて考える

簡単なWEB会議アプリを作ると想定してまず最初にプライバシー面を考えるならば、
WEBカメラを切るなり音声のみの機能が一番手っ取り早いですが、
会議や授業など相手から自分がカメラの前に存在しているのかがわからないのは困るという場合もあります。

webカメラの映像要素にモザイクをかけるならば相手側のページにCSSのfilterメソッドのblurで簡単にできちゃいますが、CSSで設定できる装飾はデベロッパーツールを使えば簡単に外せるので、相手には見えてないつもりでノーメイクで余裕ぶっこいて、ちょっとリテラシーある新人に気づかないままとんでもない素顔を晒してしまうなんて事があったらプライバシーの保護として大問題です

相手のページに自分のカメラ映像を加工してストリーミングに送るとなると、カメラのレンズ自体をぼかすか
カメラで取得した映像をぼかしたものを生成してその加工映像をストリーミングする必要があるのでcanvasにぼかしをかけられる方法を探します。

検証①:canvas要素にぼかしを付ける

canvasにモザイクをかけられる「StackBlur.js」

すぐ思いつく機能なんかは探せばすぐ先人の知恵が見つかるのでありがたいです。 今回はcanvasにぼかしを付けられるライブラリStackBlur.jsを利用させていただきます。

デモ解説

まず手始めに静的なvideoファイルの映像をリアルタイムでcanvasに反映させ
そのcanvas要素にぼかしを加えるデモページ

HTML
        <div id="videoHolder" >
            <h2>元videoファイル</h2>
            <video ref="video" class="videoplayer" id="blurVideo" poster="poster.jpg" controlslist="nodownload" controls="" muted>
                <source type="video/mp4" src="video.mp4">
            </video>
            <p><button id="play">動画を再生</button><button id="stop">一時停止</button></p>
        </div>


        <div id="canvasHolder">
            <h2>モザイク用canvas</h2>
            <canvas id="blurCanvas"></canvas>
            <p>モザイクレベル:<input type="range" min="0" max="50" value="0" id="slider" /><span id="blurVal">0</span></p>
        </div>
JavaScript
    const blurCan = document.getElementById("blurCanvas");
    const blurCtx = blurCan.getContext("2d");
    const blerVideo = document.getElementById("blurVideo");
    const playBtn = document.getElementById("play");
    const stopBtn = document.getElementById("stop");
    const slider = document.getElementById('slider');
    const blurVal = document.getElementById('blurVal');

    // videoに映る画像をcanvasに描画
    function drawLocalVideo(){
        blurCan.getContext("2d").drawImage(blurVideo, 0, 0, blurVideo.clientWidth, blurVideo.clientHeight);
        StackBlur.canvasRGB(blurCan, 0, 0, blurCan.clientWidth, blurCan.clientHeight, slider.value);
        // ループ描画
        requestAnimationFrame(drawLocalVideo)
    }

    // video再生開始とcanvas描画
    playBtn.addEventListener('click',function(){
        blerVideo.play();
        drawLocalVideo()
    });

    // videoを停止
    stopBtn.addEventListener('click',function(){
        blerVideo.pause();
    });

    // videoのポスター画像をcanvasにも反映
    const image = new Image();
    image.addEventListener("load",function (){
        blurCan.width = image.width;
        blurCan.height = image.height;
        blurCtx.drawImage(image, 0,0);
    });
    image.src = "poster.jpg";

    // モザイクレベル値を反映
    slider.onchange = function() {
        blurVal.innerHTML =this.value;
    };

うまくぼかしがかかってますね、ありがとう「StackBlur.js」

検証②:webカメラの映像をcanvasに出力させる

次はWEBカメラに映った映像をリアルタイムにcanvasに反映させます。
webカメラがあるデバイスで許可してください。デモページ

HTML
<div id="videoHolder" >
    <h2>webカメラをvideoで表示</h2>
    <video id="videoPreview" autoplay></video>
</div>

<div id="canvasHolder">
    <h2>webカメラをcanvasで表示</h2>
    <canvas id="canvasPreview"></canvas>
</div>
JavaScript
const video = document.getElementById("videoPreview");
const canvas = document.getElementById("canvasPreview");
var cw = canvas.clientWidth;
var ch = canvas.clientHeight;
canvas.width = cw;
canvas.height = ch;

// webカメラのストリームをvideoタブに出力
media = navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {
        width: video.width,
        height: video.height
    }
}).then(function(stream) {
    video.srcObject = stream;
});

// video映像をcanvasに描画
function videoToCanvas(){
    canvas.getContext("2d").drawImage(video, 0, 0, cw, ch);
    requestAnimationFrame(videoToCanvas)
}
videoToCanvas()

基本的な挙動は同じで、検証①の静的videoファイルをnavigator.mediaDevices.getUserMediaメソッドで取得したストリームをvideo要素に出力させてます。(音声は無しに設定)

そのvideo要素をもとにストリーミング映像用のcanvasにそのvideo映像をリアルタイム描画しています

検証③:WEBカメラcanvasにモザイクを付ける

あとはこの検証canvasをストリームの映像対象にON/OFFとして切り替えられればほぼ課題クリアです。デモページ

モザイクモードのONボタンでモザイク加工したWEBカメラ映像をcanvasに描画
OFFボタンで通常のWEBカメラにそのまま映る映像をcanvas描画に戻します。

HTML
<div class="container">
        <div id="videoHolder" >
            <h2>webカメラ映像</h2>
            <video id="userVideo" muted autoplay></video>
        </div>

        <div id="canvasHolder">
            <h2>ストリーム映像</h2>
            <canvas id="blurCanvas"></canvas>
        </div>
        <p style="text-align: center;"><button id="blurOn">モザイクON</button><button id="blurOff">モザイクOFF</button></p>
</div>
JavaScript
    const canvas = document.getElementById("blurCanvas");
    const context = canvas.getContext("2d");
    const video = document.getElementById("userVideo");
    const blurOn = document.getElementById("blurOn");
    const blurOff = document.getElementById("blurOff");
    const slider = document.getElementById('slider');
    const blurVal = document.getElementById('blurVal');
    const blurLevel = 20;   // モザイクレベル

    var cw = canvas.clientWidth;
    var ch = canvas.clientHeight;
    canvas.width = cw;
    canvas.height = ch;

    // webカメラのストリームをvideoタブに出力
    media = navigator.mediaDevices.getUserMedia({
        audio: false,
        video: {
            width: video.width,
            height: video.height
        }
    }).then(function(stream) {
        video.srcObject = stream;
        videoToCanvas()
    });

    // webカメラ映像をcanvasに描画
    function videoToCanvas(){
        canvas.getContext("2d").drawImage(video, 0, 0, cw, ch);
        requestAnimationFrame(videoToCanvas)
    }

    // canvasをモザイク加工描画
    function blurVideo(blur){
        canvas.getContext("2d").drawImage(video, 0, 0, cw, ch);
        StackBlur.canvasRGB(canvas, 0, 0, cw, ch, blurLevel);
        requestAnimationFrame(blurVideo)
    }

    // モザイクON
    blurOn.addEventListener('click',function(){
        blurVideo();
    });

    // モザイクOFF
    blurOff.addEventListener('click',function(){
        videoToCanvas();
    });

これでストリーム用の映像にぼかし機能のON/OFFを反映させられました!
あとはこのストリーム用のcanvas要素を常にストリーミング通信ロジックに組み込めば安全なオンライン会議ライフが送れそうです。

canvasのリアルタイム描画には以前はfps設定したメソッドをsetIntervalで繰り返す様な手法を使っていましたが、requestAnimationFrameというアニメーションループ用のメソッドある事を今回知れたので良かったです

ストリーミング加工の基本的な事は把握できたのでいつか背景としてのクロマキーcanvasとWEBカメラ映像を合成してバーチャル背景にも挑戦したいです!

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

トップへ