Help us understand the problem. What is going on with this article?

MATLAB のニューラルネットをブラウザで動かす: JavaScript を App Designer でデバッグ

はじめに

前回、ブラウザ上で手書き文字の認識を行ってみましたが、精度がいまいちなので結果として「数字を正しく認識させる」ゲームとなってしまっていました(笑)
試してみたい方はこちらからどうぞ。Github Pages: Hand-written Digit Prediction on Browser

参照:MATLAB のニューラルネットをブラウザで動かす: MATLAB > C++ > WebAssembly の自動変換

精度が悪い理由?

もちろん学習画像では悪くない分類精度でした。なので学習に使った手書き文字画像と、ブラウザ上で書かれる手書き画像の特徴が違うんだろうなと推測。ただ JavaScript 側での画像データの確認は自分には大変難しい・・。

なので JavaScript (HTML) を MATLAB 側で実行させ、 signiture_pad で作られる手書き文字画像が MATLAB 側でどう認識されているかを確認するアプリを使ったお話。

やったこと

コードこちらから: GitHub: minoue-xx/handwritten-digit-prediction-on-browser

attach:cat

こんな具合です。

実際にプロットしてみると signature_pad から送られる画像は

  • 転置画像・・
  • 学習に使った画像より線が細い

という特徴がありました。転置を修正してやると精度は向上しそう(それでもダメな場合もありますが)

環境

  • MATLAB (R2019b Update 5)
  • Image Processing Toolbox
  • Deep Learning Toolbox (学習画像の表示のみに使用)

予測モデルは前回(MATLAB のニューラルネットをブラウザで動かす: MATLAB > C++ > WebAssembly の自動変換 )作成したものを使います。(digitPredictFcn.m

JavaScript を MATLAB のアプリ上で使う

uihtml (HTML UI コンポーネント)を使います。こんな感じで HTML のソースファイルを指定するとブラウザ上と同じ機能をアプリ上で再現できます。

signature_pad に使うライブラリ(https://cdnjs.cloudflare.com/ajax/libs/signature_pad/1.5.3/signature_pad.min.js)はローカルに落としておきます。

Code
fig = uifigure('Position', [100,100,400,500]);
h = uihtml(fig);
h.HTMLSource = fullfile(pwd,'index.html');
h.Position = [25,25,350,450];

attach:cat

ここで使った HTML は前回使ったものから要素だけ取り出したもの。

index.html はこちら(折りたたみ)
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>MNIST recognition with MATLAB</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
</head>

<body>
  <div class="container">
    <div class="columns is-centered">
      <div class="column is-narrow">
        <canvas id="draw-area" width="280" height="280" style="border: 2px solid;"></canvas>
        <div class="field is-grouped">
          <p class="control">
            <button id="getImageData">Get Image -> MATLAB</button><br />
            <button id="reset">Reset</button><br />
        </div>
      </div>
    </div>
  </div>


  <script src="signature_pad.min.js"></script>
  <script type="text/javascript">
    // init SignaturePad
    //https://cdnjs.cloudflare.com/ajax/libs/signature_pad/1.5.3/signature_pad.min.js
    const drawElement = document.getElementById('draw-area');
    const signaturePad = new SignaturePad(drawElement, {
      minWidth: 6,
      maxWidth: 6,
      penColor: 'white',
      backgroundColor: 'black',
    });

    function setup(htmlComponent) {
      document.getElementById("getImageData").addEventListener("click", function (event) {
        htmlComponent.Data = getImageData();
      });
      document.getElementById("reset").addEventListener("click", function (event) {
        reset()
      });
    }

    function getImageData() {
      const inputWidth = inputHeight = 28;

      // resize
      const tmpCanvas = document.createElement('canvas').getContext('2d');
      tmpCanvas.drawImage(drawElement, 0, 0, inputWidth, inputHeight);

      // convert grayscale
      let imageData = tmpCanvas.getImageData(0, 0, inputWidth, inputHeight);
      for (let i = 0; i < imageData.data.length; i += 4) {
        const avg = (imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]) / 3;
        imageData.data[i] = imageData.data[i + 1] = imageData.data[i + 2] = avg;
      }

      return imageData;
    }


    function reset() {
      signaturePad.clear();
      let elements = document.querySelectorAll(".accuracy");
      elements.forEach(el => {
        el.parentNode.classList.remove('is-selected');
        el.innerText = '-';
      })
    }
  </script>
</body>

</html>

JavaScript 側のデータを MATLAB に

JavaScript でのデータ変化に対する MATLAB の応答のコーディング(公式ページ)を参考にします。

JavaScript 側の設定

function setup(htmlComponent) {
      document.getElementById("getImageData").addEventListener("click", function (event) {
        htmlComponent.Data = getImageData();
      });
      document.getElementById("reset").addEventListener("click", function (event) {
        reset()
      });
    }

こんな感じ。htmlComponent.Data にデータを入れてやります。そうすると配列であれば構造体配列としてデータが確保され、MATLAB 側では uihtml オブジェクト(app.HTML)の Data プロパティに値が入ります。

MATLAB (App Designer) 側の設定

あとは app.HTML.Data.data を参照してやれば画像データをいじれる(以下は表示など)という話。App Designer だとこのように記載します。

Code(Display)
function imshowFcn(app)
    % src: uihtml
    img = struct2array(app.HTML.Data.data);
    img = img(1:4:end);
    img = reshape(img,[28,28]);

    if app.toDilate.Value % 画像を膨張させるなら
        se = strel('square',2);
        img = imdilate(img,se);
    end

    if app.toTranspose.Value % 転置するなら
        img = img';
    end
    app.imagedata = img;

    contour(app.UIAxes,app.imagedata);
end

そして、この imshowFcn 関数を app.HTML.Data.data が更新された(新しい画像が確保された)時に実行させるために、uihtml オブジェクトの DataChangedFcn として imshowFcn 関数を指定しておけばOK。

startupFcn にこのように記載します。

Code(Display)
function startupFcn(app)
    app.HTML.HTMLSource = fullfile(pwd,'index.html');
    app.HTML.DataChangedFcn = @(src,event) imshowFcn(app);

    app.UIAxes.YDir = 'reverse';
    app.UIAxes.XTick = [];
    app.UIAxes.YTick = [];
    app.UIAxes.LineWidth = 2;

    app.toTranspose.ValueChangedFcn = @(src,event) imshowFcn(app);
    app.toDilate.ValueChangedFcn = @(src,event) imshowFcn(app);
end

あと転置するかどうか、画像を膨張させる(imdilate)かどうかのチェックボックスを付ければ完成。

その他

予測結果で最も score の高い位置を黄色で表示していますが、これには AddStyle を使用します。

attach:cat

テーブル表示自体は uitable オブジェクト (app.UITable) ですがここに uistyle で作った設定を入れてやれば OK。

Code(Display)
[~,idx] = max(scores);
s = uistyle('BackgroundColor','yellow');
addStyle(app.UITable,s,'row',idx);

設定を取り消すのは removeStyle(app.UITable) です。

まとめ

JavaScript 側で作られる手書き数字の画像を確認するために App Designer を使いました。
転置させれば精度向上が見込める可能性があることが分かりました。

改めて学習画像に使用した画像を確認しておきます。

Code
%% サンプルデータ読み込み(Deep Learning Toolbox に入っているデータです)
[XTrain,YTrain,anglesTrain] = digitTrain4DArrayData;
montage(XTrain(:,:,:,1:16))

attach:cat

細めの画像もちゃんと入っていますね。なので膨張処理は必要ないかもしれません。
あとはそもそも 5,000 枚しかなかったので画像数をちゃんと増やせば解決する気もする。

eigs
MATLAB の中の人 MathWorks Japan アプリケーションエンジニア部 データ解析チームリーダー。 公式ブログも書いています。MATLAB の使い方に困ったら MATLAB Answers もどうぞ。 All comments are mine alone and do not necessarily reflect those of my employers.
https://blogs.mathworks.com/japan-community/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away