タグ:canvas | StackBlur.js
ZOOMが一般化してきて以前よりお世話になる回数もめちゃめちゃ増えてあっという間にバーチャル背景がリア充達のおもちゃになったので、
この辺りの技術もちょっとはかじってみようと思いましたが
いきなりバーチャル背景なんてのもハードルが高いので、WEBカメラの映像にモザイクくらいかけてストリーミングでもしてみようかと
簡単なWEB会議アプリを作ると想定してまず最初にプライバシー面を考えるならば、
WEBカメラを切るなり音声のみの機能が一番手っ取り早いですが、
会議や授業など相手から自分がカメラの前に存在しているのかがわからないのは困るという場合もあります。
webカメラの映像要素にモザイクをかけるならば相手側のページにCSSのfilterメソッドのblurで簡単にできちゃいますが、CSSで設定できる装飾はデベロッパーツールを使えば簡単に外せるので、相手には見えてないつもりでノーメイクで余裕ぶっこいて、ちょっとリテラシーある新人に気づかないままとんでもない素顔を晒してしまうなんて事があったらプライバシーの保護として大問題です
相手のページに自分のカメラ映像を加工してストリーミングに送るとなると、カメラのレンズ自体をぼかすか
カメラで取得した映像をぼかしたものを生成してその加工映像をストリーミングする必要があるのでcanvasにぼかしをかけられる方法を探します。
すぐ思いつく機能なんかは探せばすぐ先人の知恵が見つかるのでありがたいです。 今回はcanvasにぼかしを付けられるライブラリのStackBlur.jsを利用させていただきます。
まず手始めに静的なvideoファイルの映像をリアルタイムでcanvasに反映させ
そのcanvas要素にぼかしを加えるデモページ
<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>
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カメラがあるデバイスで許可してください。デモページ
<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>
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映像をリアルタイム描画しています
あとはこの検証canvasをストリームの映像対象にON/OFFとして切り替えられればほぼ課題クリアです。デモページ
モザイクモードのONボタンでモザイク加工したWEBカメラ映像をcanvasに描画
OFFボタンで通常のWEBカメラにそのまま映る映像をcanvas描画に戻します。
<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>
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カメラ映像を合成してバーチャル背景にも挑戦したいです!