FaceAPIで顔パーツ位置を特定して目をハートにしてみれば、オンライン飲み会が楽しくなるかも。

**オンライン飲み会などで、Webカメラでアクション出来ると面白いかもしれないと思い試しに作成。**今回は単純に目をハートにしているだけですが、p5jsを応用すれば色々なエフェクトでアクションが出来て面白いと思う。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725039/e09371bc-b009-486c-fca8-98375c158491.png" width="450">
Webカメラから**faceAPIで目の位置を特定**し、**目をハート**にしてみました。
画像なので少し分かり辛いかもしれませんが、動くと目の位置に合わせてハートが付いてきてくれます。

#実装あれこれ
**利用したライブラリ関係**
・[ml5.js](https://learn.ml5js.org/#/)
・[faceapi.js](https://github.com/justadudewhohacks/face-api.js/blob/master/README.md)
・[p5.js](https://p5js.jp/get-started/)　※今回は使えてない。

**VSCodeで作成**
heart.pngがハート画像。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725039/1ae864f3-c179-6873-e45c-94022270a5fd.png)

#コード
```html:ml5_p5_faceapi.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>MEGAハート</title>
  </head>
  <body>
    <h1>MEGAハート</h1>
    <!-- CDNの読み込み -->
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
    <!-- <script src="https://cdn.jsdelivr.net/npm/p5@0.10.2/lib/p5.js"></script> -->

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

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

        // メイン処理
        async function make(){
            img = new Image();
            img.src = './heart.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();
        });

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

        // faceAPI呼び出し関数
        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)
                }
            }
            // faceapiの再呼び出し
            faceapi.detect(gotResults)
        }

        // faceapiで取得した目の位置を特定する処理
        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>
```

#課題
・ボタンやアクションでハートだけじゃなく、色々なエフェクトを入れてみたかった。
・目の位置を取得しているが、少し無理やり画像の位置を決めてるので、少しズレたりする。
・首を傾けてもハート画像は傾かないので上手いことコントロールしたい。

今回使えてませんが、p5jsは極めれば、色々な描画が出来るようなので面白そう。中々奥が深いので今回はここまで。