TensorFlow +次.背景を削除し、Webカメラで仮想背景画像を追加


こんにちは、みんな.
私はアプリケーションを開発したBodyPix , これは元の背景を削除し、新しい仮想背景画像を追加します.

本稿では、このアプリケーションの開発方法を説明します.
デモ→ https://travel-app-three.vercel.app/
ギタブ→ https://github.com/yuikoito/tensorflow-bodypix-sample

セットアップは次.JSアプリケーションとインストール
$ yarn create next-app <app-name>
$ cd <app-name>
$ touch tsconfig.json
$ yarn add --dev typescript @types/react
次に、インデックス名を変更します.JSとRainアプリ.インデックスへのJS.TSX、Rainアプリ.TSX
ウェブカメラをインストールします.
$ yarn add react-webcam @types/react-webcam
さて、開発の準備ができています:)

TensorFlowをインストールします.js
を使用する場合.あなたは少し慎重にする必要があります.
私も同様に書いたが、あなたはyarn add @tensorflow/tfjs TypeScriptを使用すると、型エラーが発生します.
次に、次のようにインストールします.
$ yarn add @tensorflow-models/body-pix @tensorflow/tfjs-core @tensorflow/tfjs-converter @tensorflow/tfjs-backend-webgl
この場合、我々はまたbody-pix model , それで、同様にそれをインストールしてください.

bodypixの設定
としてthe official documentation , それは非常に使いやすいです.
あなたがしなければならないすべては、1、インポートbodypix、2)ロード、および3)ロードが完了すると、分析したい画像データをsegmentPerson 関数.
さて、次は次のように書いています.ので、コードはこのようです.
// first, import all you need
import { useRef, useState, useEffect } from "react";
import Head from "next/head";
import "@tensorflow/tfjs-core";
import "@tensorflow/tfjs-converter";
import "@tensorflow/tfjs-backend-webgl";
import styles from "../styles/Home.module.scss";
import * as bodyPix from "@tensorflow-models/body-pix";
import Webcam from "react-webcam";

function Home() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const webcamRef = useRef<Webcam>(null);
  // Manage the state of bodypixnet with useState
  const [bodypixnet, setBodypixnet] = useState<bodyPix.BodyPix>();

  // Run only when the page is first loaded
  useEffect(() => {
    bodyPix.load().then((net: bodyPix.BodyPix) => {
      setBodypixnet(net);
    });
  }, []);

  const drawimage = async (
    webcam: HTMLVideoElement
  ) => {
    const segmentation = await bodypixnet.segmentPerson(webcam);
    console.log(segmentation);
  };

  const clickHandler = async () => {
    const webcam = webcamRef.current.video as HTMLVideoElement;
    const canvas = canvasRef.current;
    // Make the canvas, webcam, and video size all the same size.
    webcam.width = canvas.width = webcam.videoWidth;
    webcam.height = canvas.height = webcam.videoHeight;
    const context = canvas.getContext("2d");
    context.clearRect(0, 0, canvas.width, canvas.height);
    // If it is clicked before bodypixnet is set, it will cause an error, so just in case.
    if (bodypixnet) {
      drawimage(webcam);
    }
  };
  return (
    <div className={styles.container}>
      <Head>
        <title>Travel App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/static/logo.jpg" />
      </Head>
      <header className={styles.header}>
        <h1 className={styles.title}>Title</h1>
      </header>
      <main className={styles.main}>
        <div className={styles.videoContainer}>
          <Webcam audio={false} ref={webcamRef} className={styles.video} />
          <canvas ref={canvasRef} className={styles.canvas} />
        </div>
        <div className={styles.right}>
          <h4 className={styles.title}>{t.select}</h4>
          <div className={styles.buttons}>
            <button onClick={clickHandler}>
              Button
            </button>
          </div>
        </div>
      </main>
    </div>
  );
}

export default Home;
bodypixが正しく動作しているか確認しましょう.
ボタンをクリックすると、Webカメラ、キャンバス、およびビデオデータのすべての要素が同じサイズに設定されます.
インdrawimage , 我々は、ビデオデータを解析することができますbodypixnet.segmentPerson . (以下の部分)
const segmentation = await bodypixnet.segmentPerson(webcam);
セグメンテーションがログに正しく出力されるならば、すべてはすばらしいです.

ところで、あなただけの背景や人々や他の色の別の色をぼかしたい場合は、次のように簡単に行うことができます.
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);
必要なら読むhttps://github.com/tensorflow/tfjs-models/tree/master/body-pix#output-visualization-utility-functions .
With bodyPix.drawMask , あなたは簡単に透明性を指定することができます境界線をぼかし、左と右側を反転するなど.
こちらでは使用しませんbodyPix.drawMask , でも使うdrawImage() 直接キャンバスに.

背景を削除して、仮想背景を作成します
私は削除背景を書いたが、私は実際に透明なイメージを作成するための背景を空洞化していません.
私はいくつかの研究をしてきたし、完全にバックグラウンドを削除したいなら、WebGLを使う必要があるかもしれない.
リファレンス→ Using BodyPix segmentation in a WebGL shader
したがって、私は使用することを決めたdestination-out キャンバスです.
( xor も可能です.
下の図でわかるように.destination-out 重複する領域を削除する.

そのため、一時的なキャンバス要素を作成し、bodyPix.toMask() それには、マスク領域のみ削除され、我々は仮想背景を達成することができます.
また、ログの中にはbodyPix.toMask() はimagedataとして格納される.

を、今はコードを書く時間です!
  const drawimage = async (
    webcam: HTMLVideoElement,
    context: CanvasRenderingContext2D,
    canvas: HTMLCanvasElement
  ) => {
    // create tempCanvas
    const tempCanvas = document.createElement("canvas");
    tempCanvas.width = webcam.videoWidth;
    tempCanvas.height = webcam.videoHeight;
    const tempCtx = tempCanvas.getContext("2d");
    const segmentation = await bodypixnet.segmentPerson(webcam);
    const mask = bodyPix.toMask(segmentation);
    (async function drawMask() {
      requestAnimationFrame(drawMask);
      // draw mask on tempCanvas
      const segmentation = await bodypixnet.segmentPerson(webcam);
      const mask = bodyPix.toMask(segmentation);
      tempCtx.putImageData(mask, 0, 0);
      // draw original image
      context.drawImage(webcam, 0, 0, canvas.width, canvas.height);
      // use destination-out, then only masked area will be removed
      context.save();
      context.globalCompositeOperation = "destination-out";
      context.drawImage(tempCanvas, 0, 0, canvas.width, canvas.height);
      context.restore();
    })();
  };
ああ!背景が透明なので、キャンバス要素に背景画像を追加できます.
を使用して背景画像を直接追加することができますcanvas.style.backgroundImage , しかし、ここでは、可能性のイメージが即座にロードされない可能性があるため、クラス名を追加することをお勧めします.
それで、私はこれのように書きました.
  const clickHandler = async (className: string) => {
    const webcam = webcamRef.current.video as HTMLVideoElement;
    const canvas = canvasRef.current;
    webcam.width = canvas.width = webcam.videoWidth;
    webcam.height = canvas.height = webcam.videoHeight;
    const context = canvas.getContext("2d");
    context.clearRect(0, 0, canvas.width, canvas.height);
    canvas.classList.add(className);
    if (bodypixnet) {
      drawimage(webcam, context, canvas);
    }
  };
CSSは以下の通りです.
.turky {
  background-image: url(../assets/turky.jpg);
  background-size: cover;
}
それから、turky クラスが追加されました./assets/turky.jpg 背景画像として配置されます.
あなたがいくつかのクラス名を準備する必要があるならば、あなたは新しいものを加える前にクラス名を削除する必要があります.
    if (prevClassName) {
      canvas.classList.remove(prevClassName);
      setPrevClassName(className);
    } else {
      setPrevClassName(className);
    }
    canvas.classList.add(className);
終了!
ところで、背景画像は前に撮ったものです.
私はすぐに旅行できるように願っています!
これは一種の広告ですが、私は2009年にコピーし貼られましたui-components , 私はボタンのレイアウトのために開発した.
それは便利です!

それだ!
この記事は毎週少なくとも1つの記事を書きたいという10週目です.
あなたが望むならば、私の前の毎週のポストを見てください!
すぐにお会いしましょう!










  • 連絡先
    あなたが仕事を提供するか、私に何かを尋ねるならば、私にメッセージを送ってください.
    [email protected]