Teachable Machineを使ったWebアプリの作り方

こんにちは！小売業で働いているHaMaです。

私の勤めている店舗には、共有PCが事務所に4台あります。
皆さんが使うため、エクセルファイルやPDFファイルなど、データがデスクトップに散らかり放題です。
よく使うアプリケーションが埋もれてしまい、探すのに一苦労します。

そこで今回は、Teachable Machineを用いてデスクトップの散らかり具合を測定するWebアプリを制作しました。
「整頓済み」「散らかっている」という判断を、人の感覚ではなく、アプリにさせることで、あるべき姿を店舗従業員の皆さんで統一しようという試みです。

# 作品の紹介

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">Teachable Machineをつかってデスクトップの散らかり具合を測定するwebアプリを作りました。<br>これからqiitaの記事にまとめます。<a href="https://twitter.com/hashtag/qiita?src=hash&amp;ref_src=twsrc%5Etfw">#qiita</a><a href="https://twitter.com/hashtag/web%E3%82%A2%E3%83%97%E3%83%AA?src=hash&amp;ref_src=twsrc%5Etfw">#webアプリ</a><a href="https://twitter.com/hashtag/JavaScript?src=hash&amp;ref_src=twsrc%5Etfw">#JavaScript</a> <a href="https://t.co/NReJV3COuu">pic.twitter.com/NReJV3COuu</a></p>&mdash; HaMa (@HaMa53687932) <a href="https://twitter.com/HaMa53687932/status/1792897440686961113?ref_src=twsrc%5Etfw">May 21, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

デスクトップをカメラで写しながらチェックボタンを押すと、「整頓済み」か「散らかっている」かを識別します。

# 使用したツール
+ [Teachable Machine](https://teachablemachine.withgoogle.com/)
+ [Visual Studio Code (VSCode)](https://azure.microsoft.com/ja-jp/products/visual-studio-code)
+ HTML, JavaScript

# 制作の流れ
私はwebアプリの作成が初めてなので、まず、ChatGPTに作り方を質問しました。
そして回答に沿って、Teachable Machineで機械学習モデルを作成し、VSCodeでHTML, JavaScriptをコーディングしました。

## ChatGPTからの指導でWebアプリの基本を学ぶ
まず、ChatGPTにTeachable Machineを使ったWebアプリの作り方について質問しました。

質問内容：
「Teachable Machineを使って、デスクトップの散らかり具合を測定するWebアプリを作成したいです。どのように進めればいいですか？」

回答内容：
1. Teachable Machineでデスクトップの散らかり具合を判別するためのモデルを作成
2. モデルをエクスポートし、Webアプリで使用
3. HTMLとJavaScriptを用いてカメラ映像をキャプチャし、モデルで予測
4. 予測結果を画面に表示

## Teachable Machineで学習させる
Teachable Machineを使用して、デスクトップの状態（「整頓済み」「散らかっている」）を識別するモデルを作成しました。

ステップ：
1. Teachable Machine にアクセス
2. 「Image Project」を選択し、2つのクラスを設定
+ Class 1: 整頓済み
+ Class 2: 散らかっている
3. それぞれの状態の画像を複数アップロードして学習
4. モデルのトレーニングを開始
5. トレーニング完了後、モデルをエクスポート（TensorFlow.js形式）

## VSCodeでプログラミングする
エクスポートしたモデルを用いて、VSCodeでWebアプリを作成しました。
ChatGPTに、教えてもらいながらコーディングしました。
エラーが多発して苦労しましたが、なんとか形になりました。

HTML:
```
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>デスクトップ整頓チェックシステム</title>
</head>
<body>
    <h1>デスクトップ整頓チェックシステム</h1>
    <div>
        <video id="webcam" autoplay playsinline></video>
    </div>
    <button id="check">チェック</button>
    <p id="result">結果がここに表示されます</p>

    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.9.0/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8.5/dist/teachablemachine-image.min.js"></script>
    <script src="script.js"></script>
</body>
</html>
```

JavaScript (script.js):
```
const URL = 'https://teachablemachine.withgoogle.com/models/uxWdmo58S/'; // Teachable MachineからコピーしたモデルURLを貼り付け

let model, labelContainer, maxPredictions;

async function init() {
    const modelURL = URL + 'model.json';
    const metadataURL = URL + 'metadata.json';

    // モデルとメタデータをロード
    model = await tmImage.load(modelURL, metadataURL);
    maxPredictions = model.getTotalClasses();

    // ウェブカメラのセットアップ
    const webcamElement = document.getElementById('webcam');
    const stream = await navigator.mediaDevices.getUserMedia({ video: true });
    webcamElement.srcObject = stream;

    // デバッグ用ログ
    console.log('Webcam initialized');

    // ループ開始
    window.requestAnimationFrame(loop);

    document.getElementById('check').addEventListener('click', () => predict(webcamElement));
    labelContainer = document.getElementById('result');
}

async function loop() {
    window.requestAnimationFrame(loop);
}

async function predict(webcamElement) {
    // Webカメラの映像を取得
    const webcamCanvas = document.createElement('canvas');
    webcamCanvas.width = webcamElement.videoWidth;
    webcamCanvas.height = webcamElement.videoHeight;
    const ctx = webcamCanvas.getContext('2d');
    ctx.drawImage(webcamElement, 0, 0, webcamCanvas.width, webcamCanvas.height);

    const prediction = await model.predict(webcamCanvas);

    let highestProb = 0;
    let label = '';
    for (let i = 0; i < maxPredictions; i++) {
        if (prediction[i].probability > highestProb) {
            highestProb = prediction[i].probability;
            label = prediction[i].className;
        }
    }

    labelContainer.innerHTML = `結果: ${label} (${(highestProb * 100).toFixed(2)}%)`;
}

// ウェブカメラの起動を確実にするために、ページが読み込まれた後にinit関数を呼び出す
window.addEventListener('load', init);
```

# 今後の課題
###### Teachable Machineの学習不足
トレーニング用の画像が少なく、Teachable Machineの学習不足でした。
作品の紹介で写したデスクトップは、アイコンがひとつもないのに、散らかっている判定でした。恐らく、デスクトップ画像を散らかっている状態と判定したと推定できます。
もっと「整頓済み」「散らかっている」デスクトップ画像を用意し、学習させる必要性を感じました。
###### スマートフォンの正面カメラが起動してしまう
デスクトップの状態を判定するためにカメラが起動するのですが、スマートフォンですと、正面カメラが起動してしまいます。
背面カメラのほうが使いやすいので、コードの修正を検討いたします。

# 今後の発展
Teachable Machineの学習内容を変化させ、コードのモデルURLを更新するだけで、さまざまな判定に使用できるwebアプリとなりました。
例えば、笑顔の度合いを学習させることで、接遇の指導に使えます。（5分咲、7分咲の笑顔の判別など）
アイデアがありましたら、ぜひご活用ください。