TensorFlow.jsでバーチャル背景を作る

先週の8/8 (土) に行われた TensorFlow User Group Niigata #3で、 TensorFlow.jsでバーチャル背景を作ってみたネタをお話ししたので、そのまとめです。

勉強会の発表資料は、こちらになります。

ソースコードはこちら。

https://github.com/kasacchiful/bodypix-sample

デモンストレーションサイトはこちら。 ブラウザでカメラ使用を許可してもらえれば、新潟市内にある萬代橋を背景にすることができます。1

https://tfug-niigata-bodypix-demo.netlify.app

私の方では、macOS Catalina上のGoogle ChromeとiPhone XS(iOS 13)上のSafariで動作確認しています。 発表中に参加者の方から「Androidでも動きました」とコメントいただいたので、Androidでも動くはずです。

発表のきっかけ

主催のパイソン.ほしな氏から、connpass公開前に依頼がありました。 開催日がいつなのか尋ねたら「8月8日」だったので、最初「無理」と返事しました。 開催週の8/5までの間、多忙で時間が割けないことが見えていました。

「そこを何とか…」とお願いされたので渋々了承し、 8/5まではネタだけ考えて、残りの2日間で資料作ることを決めました。

ネタについては、Zoom等のWeb会議ツールでバーチャル背景機能が実装されているので、 確か人物セグメンテーションモデルがTensorFlow.jsにあったと記憶していたので、 案外簡単にできるんじゃないかと安易に考えました。 Google検索すると、BodyPixというモデルがすぐ見つかりましたので、 これなら数日で実装して夏休みの自由研究ネタとして使えると思い、作ってみました。

実装

基本的にはBodyPixデモのソースを参考にして、実装しました。

BodyPixのセグメンテーション結果の配列 (segmentation.data[n]) に、 各ピクセルが人なら1、背景なら0が格納されています。

デモのソースでは単一色で塗りつぶしていましたが、 今回は背景ならば元々用意していた背景画像を割り当ててあげるように実装すればOKです。

ソース

// 描画
const ctx = canvas.getContext('2d');
let ctxImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let bytes = ctxImageData.data;

for (let i = 0; i < canvas.height; i++) {
  for (let j = 0; j < canvas.width; j++) {
    const n = i * canvas.width + j;
    if (segmentation.data[n] === 1) {
      // for foreground (人)
      bytes[4 * n + 0] = fgImg.data[4 * n + 0];
      bytes[4 * n + 1] = fgImg.data[4 * n + 1];
      bytes[4 * n + 2] = fgImg.data[4 * n + 2];
      bytes[4 * n + 3] = fgImg.data[4 * n + 3];  
    } else {
      // for background (背景)
      bytes[4 * n + 0] = bgImg.data[4 * n + 0];
      bytes[4 * n + 1] = bgImg.data[4 * n + 1];
      bytes[4 * n + 2] = bgImg.data[4 * n + 2];
      bytes[4 * n + 3] = bgImg.data[4 * n + 3];  
    }
  }
}

ctx.putImageData(ctxImageData, 0, 0);

その際、背景画像とカメラ画像が縦横同じピクセル数にしてあげる必要があるので、 canvasでサイズ調整しています。

OBS Virtual Cameraを使って、Google Meetに反映させる

今回のTensorFlow User Group Niigataでは、 Google Meetを使用して発表することになっていたので、 実際のMeetの発表者画面にも表示させてデモをしたくなります。

私の使用しているmacには、OBSがインストール済だったので、 プラグインのOBS Virtual Cameraを入れて行いました。

OBSはHomebrewでインストール済でした。

brew cask install obs

OBS Virtual Cameraも、Homebrewでインストールできました。

brew cask install obs-virtualcam

OBS Studio起動後、入力ソースをブラウザにして、バーチャル背景を写しているタブを選択することで、 表示された内容をMeet等の入力カメラとして投影することができます。

まとめ

TensorFlow.jsとBodyPixを使うと、結構短時間で作った割には何とかバーチャル背景ができました。 ただし、まだまだ作り込みは足りないです。 夏休みの自由研究のネタの1つとして、この内容をベースにうまく改良してもらえればと思います。

作り終わった後でいろいろ検索すると、 BodyPix使ったバーチャル背景の記事が出てきました。 他の人も同じこと考えているんだなと。


  1. 写真は2年くらい前に、私が八千代橋の歩道から萬代橋方面を撮影したものです。 ↩︎

comments powered by Disqus