「あれ、どこだっけ？」を解決！Teachable Machineで画像判別

## 画像判別で社内データ投稿先が瞬時に分かる！
皆様、お疲れ様です。総合小売業に勤めるデジタル初心者です。
今回は、**Teachable Machine**を使って画像判別ツールを作成しました。
売場で使用する「販促物」（商品やサービスの販売を促進するために使用されるアイテムやツール）の社内システム内の投稿先を値札の画像で判別してナンバーを返答するツールになります。普段の業務の中で、「あれ、どこに投稿してあったかな？」と探す時間が無駄だなと思い、作成するに至りました。実際の動作はこんな感じです。
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">Teachable MachineとCodePenで画像識別できました！<br>画像を認識して返答してくれる便利なツール！ <a href="https://t.co/LC8Vyv3rJz">pic.twitter.com/LC8Vyv3rJz</a></p>&mdash; minaharu (@mina_haru_32527) <a href="https://twitter.com/mina_haru_32527/status/1838558536563130479?ref_src=twsrc%5Etfw">September 24, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

これでキーワード検索欄を何度も行き来しなくても済みそうです！
![kennsaku.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/5bbad389-8b6b-6e7f-61fe-9f3bf71958e7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/6c31b3a0-e833-2def-bb6f-6e3382fe8b4b.png)

例えば、「新商品」と検索してもこんなに膨大な販促物データがヒットします。
ここから我々が使用する物を「目で見て」探します。急にアナログ…。
使用する物は基本的にいつも同じですが、何種類もあって覚えてられないので、
値札のデザインを識別して「投稿ナンバー」を返答するツール作成に至りました。
## 設定の詳細
:::note
使用したツール：Teachable Machine・CodePen・ChatGPT
:::
① [Teachable Machine](https://teachablemachine.withgoogle.com/)を設定。（無料で使えます）
実際の画面は、こんな感じです。今回は画像プロジェクトを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/dd562275-da3b-4df3-a155-6b22e3f5ec29.png)
<details><summary>★画像プロジェクトの設定詳細★</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/4ada4588-b422-378d-5748-4c01aff9766c.png)
Class：今回は販促物の名称・投稿ナンバーを入力します。
ウェブカメラ：画像をウェブカメラで連写しアップロードできます。
アップロード：デバイス内の画像アップロードをできます。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/3e7d99d5-7586-8b73-910b-77e7736d9171.png)
・値札のデザインを連写で撮影し（約100枚程度）トレーニングを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/ebd4bb20-1466-3b6a-fc01-2baad51bb303.png)
・トレーニングが完了したらプレビューで動作確認をします。
　成功であれば、モデルをエクスポートします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/432d5526-670d-1f9c-255b-98e503549108.png)
ここまで完了したら共有可能なリンクの **「URL」を使うのでブラウザを閉じずに**
次の工程に行きましょう。

</details>

② [CodePen](https://codepen.io/your-work)を設定。（無料で使えます）
実際の画面はこんな感じです。「Pen」をクリックすると新規作成できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/c3e393bd-3170-5b49-e734-b8d6048df73f.png)
<details><summary>★CodePenの設定詳細★</summary>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/176f8623-964e-ed76-6a86-9183e614bab8.png)
画像左上のUntitledには、このペンの名称を入力し保存します。
<font color="Red">■</font>HTML：文章や画像、リンクなどの要素をページ内へ配置するために使います。
<font color="Blue">■</font>CSS：色やフォント、レイアウトやサイズなど、見た目を整えるために使います。
<font color="Yellow">■</font>JS：操作に応じてページを変化させたり、動作を実行させるために使います。

上記３種のコードは[ChatGPT](https://chatgpt.com/)に以下のように質問して回答をコピペすればOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/ef673b64-7df8-0ee3-6c02-16540d0f4df9.png)
デザイン性にこだわるなら「CSS」コードについて追加で質問してみましょう。
基本的な機能は大きく変わらないので、そのままでも十分です。

※ご参考までに私が実際に作成したコードを掲載します。
<font color="Red">■</font>HTML
```ruby
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>販促物投稿No.を調べる</title>
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
</head>
<body>
    <h1>販促物投稿No.を調べる</h1>
    <video id="webcam" autoplay playsinline width="430" height="320"></video>
    <p id="result">Loading model...</p>

    <script>
        // アウトカメラを選択するための関数
        async function setupCamera() {
            const constraints = {
                video: {
                    facingMode: { exact: "environment" } // 背面カメラを指定
                }
            };

            try {
                const stream = await navigator.mediaDevices.getUserMedia(constraints);
                const videoElement = document.getElementById('webcam');
                videoElement.srcObject = stream;
            } catch (error) {
                console.error("カメラにアクセスできません: ", error);
                document.getElementById('result').innerText = "カメラにアクセスできません";
            }
        }

        // ページが読み込まれたらカメラをセットアップ
        window.addEventListener('load', () => {
            setupCamera();
        });
    </script>
</body>
</html>
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/76eb2ea0-45b7-8a91-f034-4388c86ba5f5.png)
この〈title〉に挟まれた部分にペンの名称を記入します。

<font color="Blue">■</font>CSS
```ruby
/* ページ全体のスタイル */
body {
  font-family: Arial, sans-serif;
  background-color: #f0f0f0;
  margin: 0;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

/* 見出しのスタイル */
h1 {
  color: #333;
}

/* Webカメラ映像のスタイル */
video, canvas {
  border: 5px solid #333;
  border-radius: 10px;
  margin-bottom: 10px;
  width: 320px;
  height: 240px;
}

/* ボタンのスタイル */
button {
  background-color: #4CAF50;
  color: white;
  border: none;
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  border-radius: 5px;
}

button:hover {
  background-color: #45a049;
}

/* 結果表示のスタイル */
#result {
  font-size: 18px;
  color: #333;
  margin-top: 10px;
  font-weight: bold;
}

```

<font color="Yellow">■</font>JS
```ruby

let classifier;
let video;
const modelURL = 'https://teachablemachine.withgoogle.com/models/u8g05bvhxP/';

// Load the Teachable Machine model
async function loadModel() {
    classifier = await ml5.imageClassifier(modelURL + 'model.json');
    document.getElementById('result').innerText = 'Model loaded';
}

// Setup the webcam with the rear camera
async function setupWebcam() {
    video = document.getElementById('webcam');
    try {
        const stream = await navigator.mediaDevices.getUserMedia({ 
            video: { facingMode: { exact: 'environment' } } // Use the rear camera
        });
        video.srcObject = stream;
    } catch (err) {
        console.error('Error accessing the webcam:', err);
    }
}

// Make a classification
async function classifyVideo() {
    const results = await classifier.classify(video);
    document.getElementById('result').innerText = `Label: ${results[0].label} Confidence: ${results[0].confidence.toFixed(4)}`;
    
    // Send the classification result using Axios
    axios.post('https://your-server-endpoint.com/classification', {
        label: results[0].label,
        confidence: results[0].confidence
    }).then(response => {
        console.log('Result sent successfully:', response.data);
    }).catch(error => {
        console.error('Error sending result:', error);
    });

    // Repeat classification
    classifyVideo();
}

// Initialize everything
async function init() {
    await setupWebcam();
    await loadModel();
    classifyVideo();
}

// Start the process
window.onload = init;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/80697b8d-4109-1741-a1a0-7bb58b0f6a05.png)
ここに先程①の最後でコピーしたURLをペーストします。
</details>

③スマホでCodePenを検索してブラウザで開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844566/05a14db2-1c8d-b645-73d6-68c6918e5480.png)
作成したペンがあり、使用できるようになっています。

:::note
Teachable Machineで画像認識させる際にはパソコンのインカメラを使用しておりますが、スマホではアウトカメラを使用して画像を映します。ChatGPTにコードを書いてもらう場合には、こういった詳細の記述が必要になりますので、ご注意下さい。
:::

## チームメンバーの感想
今回作成したツールを実際にメンバーに使用して頂きましたが、非常に便利な時短ツールだと好評でした。紹介した値札のデザインはごく一部ですので、今後は登録範囲を拡大し実用化に向けて取り組んでいきます。

## 参考記事の紹介

今回の実装は、こちらの記事を参考にしております。
非常に分かりやすく記載されていますので、ぜひご参照下さいませ。

https://qiita.com/M8nEl0s0v8/items/738d175390d4630bfb06