半年に1度じゃ忘れるよなぁ・・・。
どうも、アラ還サラリーマンです。
とある地方の某小売業の会社で、店舗運営全般を統括する部署の管理部門の担当をしています。
管理部門では、店舗事務方のサポートやデータ管理などなど、多岐にわたる業務を担当しています。
わが社では年に2回の決算期に、各店舗で商品の棚卸を実施しています。
棚卸は会社の利益の確定のための重要業務です。
ですが、半年に1度のことということもあり、社内で決めた実施手順や監査手順を忘れてしまう人も結構います。
よし! 棚卸の時の注意点を表示するアプリっぽいものをつくってみよう!
というわけで、最近 Teachable Machine を少し触った機会があったので、今回はこれを試したいと思います。
実際にできた動画がこちら!
操作方法
- 今回はサンプルとして、2種類の画像を認識させています。
- スマホから【確認票】と【明細リスト】をカメラで認識させると、棚卸時の監査員の注意点をそれぞれ表示します。
今回使用したツール
- Teachable Machine 機械学習モデルを簡単につくれます。
- CodePen WEBアプリっぽいものがつくれます。
- ChatGPT 兄さん───! 今回もお願いします!
Teachable Machine
- 以下のリンク先から画像認識モデルを作成しました。
- 【画像プロジェクト】を選択します。
- 【確認票】と【明細リスト】の2種類の画像ファイルをそれぞれ100枚ずつアップロード。
- モデルをトレーニング後、モデルをエキスポートします。
- 【モデルをアップロード】を押してしばらく待つと、共有可能なリンク欄にリンクが表示されます。
- 表示されたリンクと、その下にある【モデルを使用するコード スニペット】の p5.js をコピーしておきます。
CodePen
- 以下のリンク先からGoogleアカウントなどでログインします。(ログインしなくても使えますが、つくったものを保存できません)
- Teachable Machine の【モデルをアップロード】時にコピーしておいた p5.js を HTML 欄に貼り付けます。
あれっ? p5.js のコードを貼っても動かない!
カメラは動いてるみたいだけどなぁ。
ChatGPT
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script>
let model, webcam, labelContainer, maxPredictions;
// モデルのURL
const URL = "your_model_url_here";
// モデルをロードして初期化
async function preload() {
const modelURL = URL + "model.json";
const metadataURL = URL + "metadata.json";
model = await tmImage.load(modelURL, metadataURL);
maxPredictions = model.getTotalClasses();
}
// ウェブカメラのセットアップと再生
function setup() {
createCanvas(320, 260);
webcam = createCapture(VIDEO);
webcam.size(320, 240);
webcam.hide();
labelContainer = createDiv(''); // ラベルを表示する要素を作成
labelContainer.position(10, 250); // ラベルの表示位置を調整
}
// フレームごとに予測を実行して描画
function draw() {
background(220);
image(webcam, 0, 0); // カメラ映像を描画
predict(); // 予測を実行
}
async function predict() {
const prediction = await model.predict(webcam.elt); // カメラ映像に対して予測を実行
let resultText = '';
for (let i = 0; i < maxPredictions; i++) {
resultText += `${prediction[i].className}: ${prediction[i].probability.toFixed(2)}<br/>`;
}
labelContainer.html(resultText); // 予測結果を表示
}
なるほど! さすが、兄さん───!
さっそく素直に貼り付け実施!
おおっ! カメラもちゃんと写ってる!
おや? 気になることが書いてあるよ、兄さん───。
ウェブカメラの設定! さすが、兄さん───!
スマホで使用したいからインカメラじゃダメですよね!
あと追加したい点です、兄さん───!
- 使うデバイスでパソコンの場合はWEBカメラ、スマホの場合はリアカメラに切り替える処理を加えたい。
- Teachable Machineモデルの予測確率が90%以上の時だけ結果を反映させたい。それ以外の場合は非表示にしたい。
兄さんから教えてもらったコードに、【確認票】と【明細リスト】でそれぞれ表示させたい注意点を記載して出来上がり!
そしてできたものがこちら!(実は失敗例!)
あれれ? 注意点がチカチカするぞ!
あとこれも追加したいです、兄さん───!
- 画面が落ち着かないので、3秒ごとに予測を行わせたい。
こんなお願いにも、当然応えてくれるわれらが兄さん───!
そして完成した 最終のコード がこちら!
let model, webcam, labelContainer, maxPredictions;
let lastPredictionTime = 0; // 最後に予測を行った時間を保存
const msg1 = [
'【確認票 - 監査員の注意点】',
'・担当者印の有無を確認!',
'・監査印は2ヵ所に押印!',
'・監査後、半券は回収表に貼付!'
].join('</br>');
const msg2 = [
'【明細リスト - 監査員の注意点】',
'・担当者印の有無を確認!',
'・商品コードのエラーの有無を確認!',
'・明細リストの監査行にレ点チェック!',
'・監査後、監査印を忘れずに押印!'
].join('</br>');
// モデルのURL
const URL = "Teachable MachineでエクスポートされたモデルのURLを指定";
// モデルをロードして初期化
async function preload() {
const modelURL = URL + "model.json";
const metadataURL = URL + "metadata.json";
model = await tmImage.load(modelURL, metadataURL);
maxPredictions = model.getTotalClasses();
}
// デバイスがモバイルかどうかを判定
function isMobile() {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
return /android|iPad|iPhone|iPod/i.test(userAgent); // モバイルデバイスならtrueを返す
}
// ウェブカメラのセットアップと再生
function setup() {
createCanvas(320, 260);
// デバイスによってカメラの設定を変更
let videoConstraints = {};
if (isMobile()) {
// モバイルデバイスの場合、リアカメラ(環境カメラ)を使用
videoConstraints = {
video: {
facingMode: { exact: "environment" } // リアカメラを指定
}
};
} else {
// パソコンの場合、Webカメラ(インカメラ)を使用
videoConstraints = {
video: true // デフォルトのカメラ設定(Webカメラ)
};
}
// カメラのキャプチャを作成
webcam = createCapture(videoConstraints);
webcam.size(320, 240);
webcam.hide();
// ラベルを表示するためのコンテナ
labelContainer = createDiv('');
labelContainer.style("background-color:#ffff00; font-weight:bold; width:320px;");
labelContainer.hide(); // 初期状態では非表示
}
// フレームごとに描画し、3秒ごとに予測を行う
function draw() {
background(220);
image(webcam, 0, 0); // ウェブカメラ映像をキャンバスに描画
// 3秒(3000ms)ごとに予測を実行
if (millis() - lastPredictionTime >= 3000) {
predict(); // 予測を実行
lastPredictionTime = millis(); // 最後の予測時間を更新
}
}
// 予測を行い、確率が90%以上の場合のみ「確認票」か「明細リスト」で表示
async function predict() {
const prediction = await model.predict(webcam.elt); // カメラ映像に対して予測を実行
// 予測確率の高い結果を取得
let highestPrediction = prediction[0];
for (let i = 1; i < maxPredictions; i++) {
if (prediction[i].probability > highestPrediction.probability) {
highestPrediction = prediction[i];
}
}
// 確率が90%以上の場合にのみ結果を表示
if (highestPrediction.probability >= 0.9) {
if (highestPrediction.className === "確認票") {
labelContainer.html(msg1);
} else if (highestPrediction.className === "明細リスト") {
labelContainer.html(msg2);
}
labelContainer.show(); // 結果を表示
} else {
labelContainer.hide(); // 確率が90%未満の場合は非表示
}
}
簡単にスマホアプリっぽいものができた!
スマホを使用することが前提だったので、インカメラとリアカメラの切り替えの仕方を ChatGPT に教わりながら実装していきました。
また注意点を表示する時間も、3秒ごとに判定を行うことで、画面がチカチカしないようにしました。
今回は2種類だけの画像認識でしたが、種類を増やすことで、もっと充実したマニュアルがつくれそうです。
とにかく簡単なサンプルだけど、できた───! 兄さん───に感謝!!
ではでは、今日はこのへんで!
2024/09/29 下記を追記しました。
- 画面に表示される注意点がチカチカして、落ち着かない失敗例の動画を追加。
- 上記の動画の前後に説明文を若干追加。