2021-07-26に更新

気軽にバーチャル旅行をしよう

こんにちは、Yuiです。
今回はバケーションがテーマなんですが、このコロナ禍ではなかなか旅行も行けないですよね。
せめてバーチャルでということで、気軽にバーチャル旅行ができるサービスを作りました。

ドゴーン.png

https://travel-app-three.vercel.app/

ボタンを押すとその国の背景に変わります。

ちなみに背景画像に関しては全て私が過去に撮った画像を使ってます。
あと30ヶ国ぐらい追加できますが、あんまり増やしすぎるのも面倒なので、個人的に好きな16ヶ国にしました。

使用技術は、以下です。
- Next.js
- TypeScript
- TensorFlow.js(bodyPixを利用しました)

今回、作ってて思ったのですが、このアプリでNext.js + TypeScriptは明らかにオーバースペックすぎました。笑
ただ、TensorFlow.jsをNext.jsで使ってる例が調べる限りではなさそうだったので、個人的に良いテンプレができたんじゃないかなと思います。(今後使うかはさておき)

技術的なこと

tensorflow.jsのbodyPixでは背景をぼかしたり、色を塗ったりということは予め用意されている関数を使えばすぐにできます。
以下公式より。

const img = document.getElementById('image');

const net = await bodyPix.load();
const segmentation = await net.segmentPerson(img);

// The mask image is an binary mask image with a 1 where there is a person and
// a 0 where there is not.
const coloredPartImage = bodyPix.toMask(segmentation);
const opacity = 0.7;
const flipHorizontal = false;
const maskBlurAmount = 0;
const canvas = document.getElementById('canvas');
// Draw the mask image on top of the original image onto a canvas.
// The colored part image will be drawn semi-transparent, with an opacity of
// 0.7, allowing for the original image to be visible under.
bodyPix.drawMask(
    canvas, img, coloredPartImage, opacity, maskBlurAmount,
    flipHorizontal);

ただ、背景をくり抜く部分はなかなかに苦戦して、結構時間がかかりました。
最終的に、描画部分のcanvasの他にもう一つcanvas要素を用意してcanvasのdestination-outを利用したらなんとかなりました。

以下みたいな感じです。

  const drawimage = async (
    webcam: HTMLVideoElement,
    context: CanvasRenderingContext2D,
    canvas: HTMLCanvasElement
  ) => {
    webcam.width = canvas.width = webcam.videoWidth;
    webcam.height = canvas.height = webcam.videoHeight;
    const tempCanvas = document.createElement("canvas");
    tempCanvas.width = webcam.videoWidth;
    tempCanvas.height = webcam.videoHeight;
    const tempCtx = tempCanvas.getContext("2d");
    (async function drawMask() {
      requestAnimationFrame(drawMask);
      const segmentation = await bodypixnet.segmentPerson(webcam);
      const mask = bodyPix.toMask(segmentation);
      tempCtx.putImageData(mask, 0, 0);
      context.drawImage(webcam, 0, 0, canvas.width, canvas.height);
      context.save();
      context.globalCompositeOperation = "destination-out";
      context.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
      context.restore();
    })();
  };

こちら詳細はqiitaに書いてるので、よければご確認下さい。
https://qiita.com/yuikoito/items/bb9c40bbb673e4f71abe

あとがき

TensorFlow.jsを使ったアプリ開発は顔にマスクをかけるアプリに引き続き2つ目でした!
機械学習など全くわからん私でも公式を読みながら実装できたので、非常にありがたいライブラリだなと思います。

ただ、どうしても人物の輪郭ぴったりにくり抜いて、とまではできなかったので、google meetの実装方法が気になる今日このごろです。

それでは、最後までお読みいただきありがとうございました!良いバケーションを!


YuikoIto
コメント