自分の声で元気かどうかを客観的に判断してみた

# 毎日の朝会で元気？って聞かれても・・・
2020年コロナの影響でテレワークが主になった人も多いはず。
僕の会社も多分に漏れず、テレワークが主になったのですが、その際、**Webでの朝会が行われることに。**
元気？と毎日聞かれて、元気！と答えているのですが、やっぱり朝なので、テンション低く、**本当に元気なのか？**と自分で思うこともしばしば・・・。なので、**自分の元気な声、元気のない声から機械学習を通して、声から自分が元気なのか判断できるものを作ってみました！**


アプリで判定開始をすると判断してくれる！！（このときは元気じゃないと判断されてしまいました・・・。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726018/0953c51d-87e1-daf1-8bb1-30d431b81057.png)

ついでにGoogleスプレッドシートにも記録として残すように！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726018/0a366987-f5d2-413d-c962-d011afbb60c7.png)

# 使用したもの
・Teachable Machine
・ml5.js（SoundClassifier）
・Integromat
・Googleスプレッドシート

# システムの流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726018/1c7c3964-1afb-11c8-ddda-e9047705f2c4.png)

### Teachable Machine
１．ノイズと元気な声と元気のない声の3パターンを登録する（ノイズは20個登録、それぞれの声は8個登録が必要）
２．モデルのトレーニングを実施する
３．プレビューを実施し、問題なさそうであれば、モデルのエクスポートを選択し、モデルのアップロードを実施する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726018/69bebca0-77e1-634e-afc8-ff7aa00164ba.png)

### Integromatの設定
１．WebhookとGoogleスプレッドシートをつなげる
２．WebhookはURLのパラメータとしてtextというパラメータを許容できるようにする
３．URLパラメータとして受け取ったtextをGoogleスプレッドシートに記載する
４．実行タイミングはImmediatelyを設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726018/88a4390d-a85d-e86c-c718-fdada34d86d6.png)

### プログラム
```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>声で元気か判定</title>
  </head>

  <body>
    <h1>声で元気か判定</h1>

    <!-- このapp内のみVue.jsの管理範囲です -->
    <div id="app">
      <p>{{ modelState }}</p>
      <p>結果……{{ result }}</p>
      <button v-on:click="onButtonClicked">判定開始！</button>
      <video id="myvideo" width="640" height="480" autoplay></video>
    </div>

    <!-- ml5.js、Vue.js、axios.jsをCDNから読み込みます -->
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <script>  
      // 作成したモデルのURL
      const imageModelURL = 'Teachable MachineでアップロードしたモデルのURL';
      let classifier;

      const webhook = 'Integromatで発行したWebhookのURL';

      const app = new Vue({
        el: '#app',
        data: {
          modelState: 'モデルロード中...',
          result: 'まだ始まってないよ',
        },

        async mounted() {
          // カメラからの映像取得
          const stream = await navigator.mediaDevices.getUserMedia({
            audio: false,
            video: true,
          });

          // IDが"myvideo"であるDOMを取得
          const video = document.getElementById('myvideo');

          // videoにカメラ映像をセット
          video.srcObject = stream;

          // 自作モデルのロード
          classifier = ml5.soundClassifier(imageModelURL + 'model.json', () => {
            // ロード完了
            app.modelState = 'モデルロード完了！';
            console.log('Model Loaded!');
          });
        },

        methods: {
          getResult(error, results) {
            if (error) {
              console.error(error);
              return;
            }
            label = results[0].label;
            console.log(label);

            if (label === '元気なおはよう') {
              app.result = '元気！';
            } else if (label === '元気のないおはよう') {
              app.result = '元気じゃない・・・';
            } else {
              app.result = '測定できない';
            }
          },
          onButtonClicked: async function () {
            const results = await classifier.classify(app.getResult);
            let res = 'NotFound';

            if (app.result === '元気！') {
              res = 'fine';
            } else if (app.result === '元気じゃない・・・') {
              res = 'notfine';
            } else {
              res = 'NotFound';
            }

            try {
              // GETリクエストでWebhookに送信
              const response = await axios.get(webhook + '?text=' + res);
              // データ送信が成功するとレスポンスが来る
              console.log('レスポンスを受信しました：' + response.data);
              console.log('POSTに成功しました！');
            } catch (error) {
              // ネットワークに接続できてない、サーバーが落ちてる、URLが違うなど様々なエラー
              console.log('POSTに失敗しました……');
              console.error(error);
            }
          },
        },
      });

    </script>
  </body>
</html>
```

# 最後に
まずは、Teachable Machineを利用するとこんなに簡単に機械学習のモデルが利用できるのかという感動がありました。自分の声で学習データを作成したうえで、判定をしてもらう機構が作れたのはとても勉強になりました。
体温計から体温も取得して、同じGoogleスプレッドシートに記録とかできれば、記録表になって便利そうだなと思ってます。