未来工作ブログ
2020.07.14

HTML+Javascriptでスマートフォンアプリ開発![4:AIによる画像識別を埋め込んでみる前編]

  • よーだのプログラミング講座

こんにちは。
未来工作ゼミのハカセ。よーだです。

思い出したように帰ってきたプログラミング講座。
今回は流行りのAIを利用してみたいと思います。

準備

[1:はじめてみよう]で準備した開発環境とMonacaデバッガーをインストールしたスマートフォンを用意します。
[3:スマートフォンの機能を使ってみよう]まで編集したプロジェクトを開いておきましょう。

Teachable Machineサイトで学習モデルを作成する

Teachable Machine(ティーチャブルマシン)はGoogleが提供するオープンソースの深層学習ライブラリであるTensorflow(テンサーフロー)の学習モデルをWEB上で作成して利用できるサービスです。
1
機能自体はシンプルですが、それゆえに扱いやすい利点があります。

モデルを作る

公式サイトにアクセスして「使ってみる」をクリックしましょう。
プロジェクトの作成画面で「画像プロジェクト」を選択します。
1
例としてシンプルに犬と猫の画像を判断するモデルを作ってみます。
Class 1を「dog」Class 2を「cat」に名前を変更しましょう。
2
Classは判別した画像が何になるのかの定義です。ここで指定されたもの以外は判別できません。本稿では解説しませんが、Classを追加してさらに複数の画像を判別することが出来ます。

Classが決まったら学習に使う画像を設定します。Webカメラでも取り込めますが、ここではあらかじめ用意した画像をアップロードしたいと思います。
3
犬の画像をドラッグアンドドロップでアップロードしました。この画像はフリー写真サイトから探しました。
それぞれいくつかの画像をアップロードします。基本的にはこの画像のバリエーションが多ければ多いほど認識の精度は上がります。
4
学習用画像のアップロードが終わったらモデルをトレーニングするをクリックしてしばらく待ちます。
6
トレーニングが終わるとプレビューが表示されます。WEBカメラかファイルのアップロードで判定をチェックできます。
7
これで画像認識のモデルデータが生成されました。
プレビューに猫の画像をアップロードした結果がこちらです。約80%で猫と判別されています。
8
学習のデータを保存するためには左上のメニューからプロジェクトをファイルとしてダウンロードを選びましょう。開く場合はファイルからプロジェクトを開くで保存したファイルを選択します。

アプリに埋め込む

TensorflowはJavascript版のライブラリがあるため、HTML+Javascriptでアプリ制作している場合、比較的苦労せずに埋め込むことができます。
まずはプレビューの左上にあるモデルをエクスポートするを選択しましょう。
9
ダイアログが開いたら、モデルをアップロードをクリックします。
10
こうすることでモデルデータがTeachable Machineのサーバにアップロードされ、インターネット経由で利用できるようになります。
11
ダイアログの下の方には、Javascriptのコードサンプルが記載されています。左上にあるコピーのボタンを押すとコード全てがコピーできます。

コピーしたらMonacaのIDEを開きましょう。
12
以下のコードを参考にTeachable Machineのサイトでコピーしたコードを貼り付けます。
以下のコードは私が作成したモデルのデータを使っています。あなたのコードとは異なりますので必ずあなたのつくったモデルデータを使うように注意しましょう。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: content: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
    <script src="components/loader.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <script type="text/javascript">
        function hello_world() {
            alert("画面に表示したい文字列");
        }

        function input_text(text_box) {
            alert(text_box.value);
        }

        let lat;
        let long;
        let altitude;
        let speed;

        let geolocationSuccess = function(position) {
            lat.innerHTML = position.coords.latitude;
            long.innerHTML = position.coords.longitude;
            altitude.innerHTML = position.coords.altitude;
            speed.innerHTML = position.coords.speed;
        }

        let options = {
            enableHighAccuracy: true,
            timeout: 5000,
            maximumAge: 0
        };

        function init() {
            lat = document.getElementById("lat");
            long = document.getElementById("long");
            altitude = document.getElementById("altitude");
            speed = document.getElementById("speed");

            navigator.geolocation.watchPosition(geolocationSuccess, null, options);   
        }

    </script>
</head>
<body onload="init();">
	<input type="button" value="hello world" onclick="hello_world();">
    <input type="text" value="hello world" onchange="input_text(this);">
    <dl>
        <dt>緯度:</dt><dd id="lat"></dd>
        <dt>経度:</dt><dd id="long"></dd>
        <dt>高さ:</dt><dd id="altitude"></dd>
        <dt>速度:</dt><dd id="speed"></dd>
    </dl>
    
    <div>Teachable Machine Image Model</div>
    <button type="button" onclick="tm_init()">Start</button>
    <div id="webcam-container"></div>
    <div id="label-container"></div>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/image@0.8/dist/teachablemachine-image.min.js"></script>
    <script type="text/javascript">
        // More API functions here:
        // https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/image

        // the link to your model provided by Teachable Machine export panel
        const URL = "https://teachablemachine.withgoogle.com/models/LeGqBCrJJ/";

        let model, webcam, labelContainer, maxPredictions;

        // Load the image model and setup the webcam
        async function tm_init() {
            const modelURL = URL + "model.json";
            const metadataURL = URL + "metadata.json";

            // load the model and metadata
            // Refer to tmImage.loadFromFiles() in the API to support files from a file picker
            // or files from your local hard drive
            // Note: the pose library adds "tmImage" object to your window (window.tmImage)
            model = await tmImage.load(modelURL, metadataURL);
            maxPredictions = model.getTotalClasses();

            // Convenience function to setup a webcam
            const flip = true; // whether to flip the webcam
            webcam = new tmImage.Webcam(200, 200, flip); // width, height, flip
            await webcam.setup(); // request access to the webcam
            await webcam.play();
            window.requestAnimationFrame(loop);

            // append elements to the DOM
            document.getElementById("webcam-container").appendChild(webcam.canvas);
            labelContainer = document.getElementById("label-container");
            for (let i = 0; i < maxPredictions; i++) { // and class labels
                labelContainer.appendChild(document.createElement("div"));
            }
        }

        async function loop() {
            webcam.update(); // update the webcam frame
            await predict();
            window.requestAnimationFrame(loop);
        }

        // run the webcam image through the image model
        async function predict() {
            // predict can take in an image, video or canvas html element
            const prediction = await model.predict(webcam.canvas);
            for (let i = 0; i < maxPredictions; i++) {
                const classPrediction =
                    prediction[i].className + ": " + prediction[i].probability.toFixed(2);
                labelContainer.childNodes[i].innerHTML = classPrediction;
            }
        }
    </script>

</body>
</html>

実はinit()という関数名が重複してしまっています。貼り付けた後、下記を参考に張り付けたほうの関数の名前と、呼び出している個所を修正します。

// 59行目あたり
// 変更前
<button type="button" onclick="init()">Start</button>
// 変更後
<button type="button" onclick="tm_init()">Start</button>


// 74行目あたり
// 変更前
async function init() {
// 変更後
async function tm_init() {

動作を確認する

デバッカーでアプリを起動して、Startのボタンを押してしばらく待つと、カメラの映像が表示されます。試しに犬か猫の画像をカメラに移してみると、どちらに近しいかが数値で表示されます。数値は1.00が最大で0.00が最小です。犬または猫に近いほうが1.00に近い数値になります。

むすび

機械学習のデータモデルをWEBサイト上で製作できるTeachable Machineでデータモデルを作り、Monacaで作ったアプリに埋め込んでみました。
Javascriptで動作するため、比較的簡単にスマートフォン上でも動作させられるのが利点です。後編はTeachable Machineサイトでコピーしたコードの解説と、スマートフォンで利用するといわゆるインカメラで動作しているものを、外向きのカメラで利用できるように変更してみたいと思います。

関連するブログ
RECOMMEND WEBLOG

チャンネル
CHANNEL