Teachable Machineを上手く使って判別結果の履歴(→動きや被写体の変化)を分析してみたい

# 簡単に自己紹介
初めて投稿します。理学療法士の資格を持っています。
数年前に仕事で使えそうなアイデアをネット上で探している途中に、Qiitaにたどり着きました。最初は全くわからないことだらけでしたが、下手の横好きで学ばせてもらい、GASが少し使えるようになってきました。最近ではLINEWORKSをGASで動かしたりと、少しずつ仕事で活かすことができるようになりました。

# どういうことをしたかったのか
さて本題に入ります。リハビリでは動作や歩き方(歩容といいます)を観察して、改善度合いを判断することが多いですが、第3者に変化を伝えるには、定量化と数値化が必要です。
しかし動作は連続しているため、人の目で「観察」しながら、ストップウォッチを操作して時間を「計測」する、ことは難しいと思っていました(いや難しいのです)。
陸上や水泳などスポーツ競技では、百分の一秒まで計測し、ほんの僅かな変化もつかめるようにできますが、それは専用の計測器械があってこそ。現場にはストップウオッチしかありませんし、時間計測を繰り返し何度やってもまったく同じ値にはなりません。
もし「観察」と「計測」を器械ができるようになれば、人はカメラを向けることと、結果を細かく分析することに時間を使えるのにな〜？　そう考え始めたときに、Teachable　Machineというサービスを知りました。
Teachable　Machineは判別モデルを複数準備し、カメラから取り込んだ画像(映像？)を瞬時に判定してくれるツールです（と、私は認識しています。細かな表現の違いはご容赦ください）。その瞬間に映った画像について都度判定していると思いましたので、画像が変化していく時間の経過を「計測」できるようになれば、自分が思い描くようなことまでは、器械はしてくれるのではなかろうか？
つまり順序が決まっている人の動作(ベッドに仰向けに寝る〜肘をついて起きる〜ベッドの端に腰掛ける、とか　一定の区間を普段通り歩く　など)であれば、動作の節目で判別モデルを作り、A~B、B~Cに変わるまでの時間を測ってあげると、スムーズに行えるようになっていく様はわからないか？また正しい動作を学習する際に、良いフォーム、悪いフォームをモデルとして準備し、一定時間動作を行わせて、「良いフォーム」が占める割合が増えていくことを目指して、動作学習をすることはできないだろうか？
いろんな期待は拡がりますが、実装すべきものは単純で、より良い/より細かい「判別モデル」を作ること、判別した結果を連続的に記録するツールを作ること、の2つだと考えました。

# 試作にむけて
良い判別モデルについては、アングル、明るさなどを考慮して、できるだけたくさんのサンプル数を蓄積することだと思いますので今回は割愛し、「判別した結果を連続的に記録するツール」の試作を行いました。

判別した結果を連続的に記録する方法は、@sawayamakouji(さわやま こうじ)さんの記事「すっかり人気者気分！Teachable Machineを使ったデジタルサイネージを作ったら、LINEの通知が鳴りやまなくなりました！」を参考にしました。
https://qiita.com/sawayamakouji/items/08feb8d8ab4bc6b839c7

こちらの記事では、Teachable　MachineとMake、CodePenからLINEに転送させると、止めどなくデータ転送するが紹介されていました。私にはこれがなぜ起こっているのか、未だに理解できてないのですが、この現象を今回使ってみることにしました。

# 変更点
Webhookを打ってSpreadsheetに転送する方法を今回使ってみることにしました。IFTTTには元々そのアプレットがあったからです。タイムスタンプと判別結果の２つを記録させ、ほしい結果の分析はSpreadsheet側で工夫することにしました。

:::note warn
@moririn0128　さんの記事より
2024年2月15日より、IFTTTでのWebhook実行が有料化されたみたいです。
この記事の方法は無料では使えなくなってしまいました…残念です(T_T)
:::

**＜入力側＞**
Codepenでのプログラムは、さわやまさんの記事をほぼ活用させていただきました（この場をお借りして、大変感謝申し上げます！）。しかしコード内のWebhookのアドレスだけを変更しても、IFTTTへの転送がうまく行きません。ネットで様々記事を探し、試行錯誤しながら以下のような変更にしました。

**変更箇所**
さわやまさんの記事内で、画面上に表示する判別クラスをMakeに転送するコード部分です。抽出位置がわかりやすいように手前の部分から抜きました。わかりにくいとは思いますが、素人ゆえにご容赦ください。
```
else {
  
 comment = "レッドブル\n翼を授ける!!\n空も飛べるはず!!";
    axios.post('https://hook.eu1.make.com/61ssv3o1fxqmp95l23f9ist0qaf4j6mp',"レッドブル");
}   
  ```
**変更後**
IFTTT側ではWebhookで3つの項目を受け取り、Spreadsheetに送ることにしています。
```
let d = new Date();
let year = d.getFullYear();
let month = ("0"+d.getMonth()).slice(-2);
let day = ("0"+d.getDate()).slice(-2);
let hour = ("0"+d.getHours()).slice(-2);
let minute = ("0"+d.getMinutes()).slice(-2);
let second = ("0"+d.getSeconds()).slice(-2);
let millisec = ("00"+d.getMilliseconds()).slice(-3);
```
で時間の取り出しを定義しておき、

Value1:日付が変わった0時0分(ミリ秒まで)からの経過時間
```
let keyno = Number(hour)*60*60*1000+Number(minute)*60*1000+Number(second)*1000+Number(millisec);
```
Value2:計測した現在時刻（ミリ秒まで）
```
let nowtime = year +'/'+ month +'/'+ day +' '+hour +':'+ minute +':'+ second +'.'+ millisec;
```
Value3:判定結果（label）
の3項目を送ります。
```
} 
  else {
 comment = "レッドブル\n翼を授ける!!\n空も飛べるはず!!";
   MessageSend(keyno,nowtime,label);
}   
  function MessageSend(keyno,nowtime,label){
   const endpoint = "https://maker.ifttt.com/trigger/TeachableMachine/with/key/＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊（IFTTT設定時のID）";

   const sendData ={"value1":keyno,"value2":nowtime,"value3":label};
 $.ajax({
    method: "post",
    url: endpoint,
    data: sendData,
    datatype:"json"
  })
  }
 }
 ```

この変更で偶然Webhookを叩くようになりましたが、なぜできるようになったのかが私には全く分からず・・・。ひとまず仕組みが動いてよかった。データの転送内容は、タイムスタンプ(ミリ秒まで)、判別モデルに基づく結果の２つを出すことにしました。今回判別モデルはA、B、C、Dを簡易的に作成しています。

**＜出力側＞**
本当は等間隔(0.1秒とか0.2秒とか)での記録出力にしたかったのですが、入力側にどう修正を加えても、思ったようにできる力が私にはありませんでした。とにかく数をたくさん打ってもらって、その後で整理をすることに。Spreadsheet上では、Value１を”millisec(A列)”、value2を”nowtime（B列）”、value3を”class（C列）”にデータが転送されています。案の定ものすごい数のデータが蓄積されます…。
![スクリーンショット 2024-01-15 13.53.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627859/1f9d4be0-fb17-0562-77ed-89be5f38d2be.png)
そもそも0.1秒とか0.2秒って、人の目では殆ど違いはわかりません。動作映像はコマの写真の蓄積なので、判別クラスはどこかで変化が生まれるはず。ただ間隔をどれくらいにしようか…？
Spreadsheetの行を見ていると、まばらにWebhookを打ってて、これを全部活かそうとすると無理がありそうです。

そこで今回は0.25秒間ずつを区切り、その中で最も多い判別クラスを代表値として、その変化を観察できるように作ってみることにしました。
![スクリーンショット 2024-01-15 13.53.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627859/cbf7c814-6323-7cfb-42fe-4f4b12998316.png)
改めて思いましたがモデルの完成度が一番大事ですね。きちんんとできれば、おそらくこれでグラデーションのように、変化を表現してくれるようになるはずではないでしょうか？ただこの程度の機能なら、もっとスマートにできるんじゃないか…？改良の余地は十分にありそうですが、現在できているのはここまでです。
※関数を所々に散りばめていますが、細かな点は今回は省かせていただきました。わかりにくく、ここまで読んでいただいた方へは、大変申し訳ないです。今後の反省とし、もっと上手く書けるよう努力します。
# 今後やりたいこと
◯と△、×の姿勢モデルを作っておいて、カメラを自分に向けながら、◯がたくさんになるよう動作の練習を行うことで、学習したい動作の自己修正や矯正をすることにも使えそうだと期待しています。機材のセッティングをすれば、教師が横についていなくてもいつでも自主練ができますし、成果がわかりやすいので、繰り返しの練習もとても励みになると思います。
今後もこの開発をチャレンジし、また応用ができたら投稿してみようと思います。プログラムの技術的な点は素人の域を脱しませんが、偶然の産物でも自分に最大限活かそうとする意気込みはありますので、よろしければコメントいただければ幸いです。ありがとうございました。