機械学習「Face-Api」を使って、顔パーツにピカチュウを表示する


機械学習(Face-Api)

数日前に「機械学習」を習ったので、ポケモンネタで何かを作ろうと試行錯誤した話です。
"モンスターボールから、ピカチュウが飛び出してくるイメージ"のものを作ろうとしたのですが、果たしてどのモデルで実現できるのかが分からず・・・。
そこで、試しにml5.jsの「Face-Api」を使ってみました。
結果、無事にピカチュウは表示できたものの、イメージどおりにはなりませんでした・・・。
ですが、いつか使える日が来るかも?と思い、ちょっとおもしろかったので記事にしておきます。

試作品

両目の位置が特定されると、その上にピカチュウが表示されます。(何だかイメージしてたのと違う・・・)
ピカチュウ画像は、PokeAPIから取ってきたものをイメージファイルとして呼び出しています。

ソースコード

作成にあたっては、以下の記事を大変参考にさせていただきました。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ピカチュウeyes</title>
  </head>
  <body>
    <h1>ピカチュウeyes</h1>
    <!-- CDNの読み込み -->
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
    <!-- <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.js"></script> -->

    <script>
        let faceapi;
        let video;
        let width = 640;
        let height = 480;
        let ctx;
        let img;

        // Face-Apiのオプション設定
        const detection_options = {
            withLandmarks: true,
            withDescriptors: false,
        }

        // メイン処理
        async function make(){
            img = new Image();
            img.src = 'https://cdn-ak.f.st-hatena.com/images/fotolife/K/Kokano23/20210508/20210508184830.png';
            video = await getVideo();
            let canvas = createCanvas(width, height);
            ctx = canvas.getContext('2d');
            // faceapiモデルのオブジェクト生成
            faceapi = await ml5.faceApi(video, detection_options, modelReady)
        }

        // DOMの読み込み
        window.addEventListener('DOMContentLoaded', function() {
            make();
        });

        // Face-Apiモデルの読み込み
        function modelReady() {
            console.log('ready!')
            faceapi.detect(gotResults)
        }

        // Face-Api呼び出し関数
        function gotResults(err, result) {
            if (err) {
                console.log(err)
                return
            }
        // 自分の映像の情報を取得
         let detections = result;

        // 描画キャンパスを初期化
         ctx.fillStyle = "#000000"
         ctx.fillRect(0,0, width, height);
         ctx.drawImage(video, 0,0, width, height);

        // 自分の映像に描画する処理
          if (detections) {
           if(detections.length > 0){
               drawLandmarks(detections)
                }
            }
        // Face-Apiの再呼び出し
           faceapi.detect(gotResults)
        }

        // Face-Apiで取得した目の位置を特定する処理
        function drawLandmarks(detections){
            for(let i = 0; i < detections.length; i++){
                const leftEye = detections[i].parts.leftEye;
                const rightEye = detections[i].parts.rightEye;
                // 目に画像を描画
                ctx.drawImage(img, leftEye[0].x -10, leftEye[0].y -25, 50, 50);
                ctx.drawImage(img, rightEye[0].x -10, rightEye[0].y -25, 50, 50);
            }
        }

        // Helper Functions
        async function getVideo(){
            // 要素の取得、設定の作成など
            const videoElement = document.createElement('video');
            videoElement.setAttribute("style", "display: none;");
            videoElement.width = width;
            videoElement.height = height;
            document.body.appendChild(videoElement);
            // Webカメラのキャプチャを作成
            const capture = await navigator.mediaDevices.getUserMedia({ video: true })
            videoElement.srcObject = capture;
            videoElement.play();
            return videoElement
        }
        // キャンパスの作成
        function createCanvas(w, h){
            const canvas = document.createElement("canvas");
            canvas.width  = w;
            canvas.height = h;
            document.body.appendChild(canvas);
            return canvas;
        }
    </script>
  </body>
</html>

最後に

  • 本当にやりたいのは、「Teachable Machine」で機械学習したモンスターボールを検知した場合、モンスターボール上にピカチュウを表示させることです。
  • 上記を実現するには、「Handpose」を使って、モンスターボールを持つ手の関節にピカチュウを表示させる、という方法がよさそうです。(実現できたら、別記事にする予定です)
  • 今回の試作品は、想像していたものとは大分違ったのですが、これはこれで子どもに「おもしろい!」と言ってもらえました。
  • 試しにマスクを着用してみたところ、うまく座標位置が認識されませんでした。(勉強になりました)
  • ポケモン好きのお子さんがいらっしゃる方など、ぜひ遊んでみてください!