Slide 1

Slide 1 text

Canvasで簡易背景ぼかしを やってみた WebRTC Meetup #25 2023.09.22 @massie_g / がねこまさし

Slide 2

Slide 2 text

背景ぼかし ● Web会議ツールで必須の機能 ○ AIを使って人の輪郭を抽出 ○ 背景をぼかしたり、仮想背景に差し替えたりする ○ 良いところ ■ 人が動いても、(ほぼ)追従する ○ 辛いところ ■ 結構マシンパワーが必要、ファンが唸ったりする ● 原始的な背景ぼかしをやってみた ○ 良いところ ■ 軽い ■ 作るのが手軽 ○ 辛いところ … 人が頑張る

Slide 3

Slide 3 text

カメラ映像取得から、通信まで ● navigator.mediaDevices.getUserMedia() で、映像/音声を取得 ○ MediaStreamを取得、MediaStreamTrack (Video, Audio) をもつ ● RTCPeerConnectionのインスタンスを生成 ● そのインスタンスに、 addTrack()でMediaStreamTrackを追加 ● その後、シグナリングを行い通信を開始 navigator.mediaDevices.getUserMedia() MediaStream MediaStreamTrack MediaStreamTrack video audio RTCPeerConnection addTrack() addTrack()

Slide 4

Slide 4 text

映像の加工はCanvasで ● 取得したカメラ映像(MediaStream)を要素で表示 ● drawImage()をつかって要素に描画 ○ ※requestAnimationFrame()を使って繰り返す ● 要素から、captureStream()を使って映像(MediaStream)を取得 MediaStream MediaStreamTrack MediaStreamTrack video audio RTCPeerConnection addTrack() VideoElement Canvas drawImage() captureStream() MediaStream MediaStreamTrack video addTrack() requestAnimationFrame()で繰り返す

Slide 5

Slide 5 text

背景の簡易ぼかし ● まじめなぼかし処理ではなく ● 縮小→拡大による簡易ぼかし video canvas workCanvas ※以前どこかの記事で見かけたやり方です。元ネタのページは忘れてしまいました

Slide 6

Slide 6 text

縮小、拡大で簡易ぼかし function drawMosaicBackground () { // モザイクのブロックのサイズ blockWidth, blockHeight をあらかじめ指定 // 縮小後の画像サイズ const smallWidth = video.videoWidth / blockWidth; const smallHeight = video.videoHeight / blockHeight; // 画像を縮小して描画 ctxWork.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, // src 0, 0, smallWidth, smallHeight //dest ); // 画像を再拡大 // zoomLeft, zoomTop, zoomWidth, zoomHeight は計算しておく ctx.drawImage(canvasWork, 0, 0, smallWidth, smallHeight, // src zoomLeft, zoomTop, zoomWidth, zoomHeight //dest ); }

Slide 7

Slide 7 text

Demo https://mganeko.github.io/webrtc_begins/background_mosaic.html

Slide 8

Slide 8 text

顔映像の切り抜き ● 固定位置を切り抜く ○ AIモデルは利用していないので、顔検出や追跡はできない ○ 自分で頑張ってその位置に顔を持って行く video canvas

Slide 9

Slide 9 text

円形にくりぬいて描画 function drawClipedVideo() { ctx.save(); // コンテキストの情報を保存 /* -- 円を指定 --*/ // r:半径、円の中心(centerX, centerY) を指定しておく ctx.beginPath(); ctx.arc(centerX, centerY, r, 0, Math.PI * 2, false); ctx.clip(); // 画像を描画 // srcR, srcLeft, srcTop, srcWidth, srcHeight を計算しておく ctx.drawImage(video, srcLeft, srcTop, srcWidth, srcHeight, // src (left, top, width, height) centerX - r, 0, r * 2, r * 2, // dest (left, top, width, height) ); ctx.restore(); // コンテキストの情報を復元 }

Slide 10

Slide 10 text

Demo https://mganeko.github.io/webrtc_begins/camera_crop.html

Slide 11

Slide 11 text

合体 ● 縮小→拡大による簡易ぼかし ● 顔の切り抜き video canvas workCanvas

Slide 12

Slide 12 text

requestAnimationFrame()で連続描画 let requestId = null; // 描画開始 function startDraw() { requestId = requestAnimationFrame(draw); } // 描画停止 function stopDraw() { if (requestId) { cancelAnimationFrame(requestId); requestId = null; } } // 描画 function draw() { drawMosaicBackground(); drawClipedVideo(); requestId = requestAnimationFrame(draw); }

Slide 13

Slide 13 text

Demo ● video は display:none ● workCanvasは OffScreenCanvasに ● canvasもdisplay:none https://mganeko.github.io/webrtc_begins/ https://mganeko.github.io/webrtc_begins/crop_mosaic.html https://mganeko.github.io/webrtc_begins/crop_mosaic_slim.html

Slide 14

Slide 14 text

Demo Skyway 経由の P2P通信 ※ファイルはローカル(未公開)

Slide 15

Slide 15 text

まとめ、注意点 ● Canvasを使うと、映像を加工して、さらに映像として取り出すことができる ○ Chrome/Edge, Firefox, Safariで利用可能 ● AIモデルを使えば、人の輪郭も抽出できる ○ 例) body-segmentation ■ https://github.com/tensorflow/tfjs-models/tree/master/body-segmentation ● requestAnimationFrame() 利用時の注意 ○ ブラウザのタブ/ウィンドウが完全に非表示になると ○ → アニメーションイベントが発生しない ○ → canvasへの描画が止まる ○ → 映像が止まる ● この欠点を回避するには、MediaStreamTrackProcessorが利用できる ○ ※Chrome/Edgeのみ

Slide 16

Slide 16 text

参考 ● GitHub でソースを見る ○ https://github.com/mganeko/webrtc_begins ○ camera_crop.html … カメラを丸く切り取る ○ background_mosaic.html … 背景を簡易ぼかし ○ crop_mosaic.html … カメラ切り抜きと背景ぼかしの合成 ○ crop_mosaic_slim.html … 余計な要素を隠してすっきりさせたもの ● GitHub Pages で試す ○ https://mganeko.github.io/webrtc_begins/