未来工作ブログ
2020.07.14

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

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

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

思い出したように帰ってきたプログラミング講座。
AIによる画像認識後編では、Teachable Machineサイトからコピーしたコードの意味を追いつつすこし改造してみます。

準備

[1:はじめてみよう]で準備した開発環境とMonacaデバッガーをインストールしたスマートフォンを用意します。
[4:AIによる画像識別を埋め込んでみる前編]まで編集したプロジェクトを開いておきましょう。

Teachable Machineサイトのコードを確認する

こちらのコードを確認していきます。
一部あなたのコードと違う部分があるかもしれませんが、作ったモデルデータが違うためです。
14

コードは大きく分けてHTML部分とJavascript部分に分けられます。

    <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>


HTML部分ではまず、1行目でTeachable Machine Image Modelという文字列の表示を定義しています。
2行目は処理を開始するStartのボタンを定義しています。
3行目はカメラの画像を表示する空間を定義しています。
4行目は認識した結果を表示する空間の定義です。
5と6行目はTensorflowの処理が書いてあるjavascriptのファイルを外部から読み込んでいます。
それぞれここでは「そういうものなんだな」となんとなく分かっておきましょう。

<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>


javascript部分は非常に長いですが、スラッシュ二つ(//)から始まる行が結構あります。
スラッシュ二つをつなげるとコメントアウトといってその行はプログラムの中に説明を書ける行になります。

    // More API functions here:
    // https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/image


例えば2-3行目は、このプログラムが使用しているライブラリのGithubというサービスで作られたWEBページへのリンクです。

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


5行目には、the link to your model provided by Teachable Machine export panel と書かれています。
これを訳すとTeachable Machineでエクスポートパネルで保存したあなたのモデルのリンクです。となります。この文字列でTeachable Machineで作ってサーバー上に保存したモデルデータへアクセスしていることが分かります。
このようにコメント(説明)が英語でも、翻訳サイトなどを使って変換すれば、何をしているかある程度知ることが出来ます。コメントやメッセージはあきらめずに確認するようにしましょう。

javascriptの関数の役目を知る

ここで書かれているコードには3つの関数があります。それぞれの役割を説明していきます。

async function tm_init() {


tm_init()は、カメラの映像が映る空間、結果を表示する空間の準備やWebカメラの準備を行う関数です。

async function loop() {


loop()はカメラの映像の更新やpredict()関数を定期的に呼び出している関数です。プログラムでカメラを制御するときは映像の更新も自分で行ってあげる必要があります。loop()関数は、関数の処理の中でloop()をもう一度呼び出すことで、繰り返し実行されるようになっています。

async function predict() {


predict()関数はカメラの画像をTeachable Machineで作成したモデルデータと照合して、結果を画面に表示している関数です。これを画面の更新のたびに呼び出すことで、カメラ画像に移ったものをリアルタイムで判別しています。

大きく関数単位でどんな処理をしているのかを解説しました。本稿では関数の処理の中まで詳細に解説はしませんが、なんとなく何をしているのか、が関数単位でわかると動きを変えたい時に詳しく調査する箇所が見えてきます。

フロントカメラを利用できるようにしてみる

それでは試しにプログラムを改造してみましょう。
ここまでのプログラムでは、カメラがスマートフォンの内部カメラで動いています。この状態だと町にあるものを画像を識別するのには不向きです。そこでスマートフォンのフロントカメラを使用して画像識別きるように変更してみます。

変更するのは、Webカメラの準備の処理をしているtm_init()関数の処理です。
この中をよく見ると、webcam.setup()といかにもWebカメラをセットアップしている処理があります。ここを変えていきます。

    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"));
        }
    }


webcam.setup()関数の()の中に引数としてカメラの設定を引き渡します。関数にはこのようにして引数として値を渡すことができます。なお、コードが読みやすいように改行しています。

    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(
                {
                    facingMode: "environment" 
                }
            ); // 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"));
        }
    }


この改造を保存して、Monacaデバッガーで動作させてみます。Startのボタンを押してしばらく待つとフロントカメラが起動しました。※カメラが立ち上がるまでにしばらく時間が必要かもしれません。
Screenshot_20200714-102023

むすび

Teachable Machineのサイトが提供しているサンプルコードを確認して、少し改造してみました。
改造にはJavascriptの知識が必要になってきますが、少し頑張れば画像認識の結果で様々な処理を行えるようになります。認識結果で処理したいときはpredict()関数の内容を改造します。
本稿ではごく基本的な説明だけを行いました。これを参考に少しずつ色々な個所のプログラムを改造しながら何が変化するかを確認してみてください。

関連するブログ
RECOMMEND WEBLOG

チャンネル
CHANNEL