Vue+Flask+CubismWebでアヤメの分類を行う

今回はVue.jsとFlaskでアヤメの分類を行って、分類結果の出力に合わせてCubismWeb(Live2D Cubism3SDK for Web)のモデルを動かしてみます。

アプリケーションの動作

1. Vueにアヤメのパラメータ(がく片の長さ、がく片の幅、花弁の長さ、花弁の幅)を入力する
2. Vueに入力されたパラメータをFlaskの分類器に渡す
3. 分類器の判定結果をVueに返す
4. 分類結果を表示してCubismWebと連動させる

使用するイラスト

CubismWebで表示するモデルです。

使用するイラスト
© Unity Technologies Japan/UCL

上記モデルには以前の記事で Live2D のテンプレート「FaceRig」を適用しています。
テンプレートを適用する手順についてはこちらで紹介しています。

Live2Dのテンプレート機能[FaceRig]
この記事では、FaceRig向けのLive2Dモデルの構造(デフォーマ)やパラメータを、制作するモデルに移す機能(テンプレート機能)について紹介しています。

動作確認サンプル

ChromeとEdgeで動作を確認しています。(下の画面はChrome)

アヤメの品種名を答えてくれるLive2Dモデルの作成

Vue.jsとFlaskでアヤメの分類を行って、分類結果をLive2Dモデル(CubismWeb)に渡すことで、アニメーションと連動させます。

1. scikit-learnでアヤメの学習モデルを作成する

機械学習のライブラリ「scikit-learn」にはサンプルとしてデータセットが用意されているので、今回はその中からアヤメの品種データを学習させます。
scikit-learnの使い方については、解説しているサイトが多数あるので省略します。

アヤメの学習モデルについてはpickleで保存します。

iris.py
# save the model as pickle
joblib.dump(forest, './trained-model/rfcParam.pkl', compress=True)

2. Vue.jsで入力フォームを作成する

Vue.jsでアヤメのパラメータの入力フォームと決定ボタンを作成します。

irisCubism.html
<div class="param">
  <div id="paramsL">
    <select v-model="selectedA">
      <option v-for="option in options" v-bind:value="option.value">
        [[ option.text ]]
      </option>
    </select>
    <span>がく片の長さ SepalLength: [[ selectedA ]] cm</span>
  </div>
  <div id="paramsL">
    <select v-model="selectedB">
      <option v-for="option in options" v-bind:value="option.value">
        [[ option.text ]]
      </option>
    </select>
    <span>がく片の幅 SepalWidth: [[ selectedB ]] cm</span>
  </div>
・ ・ ・
</div>

<div id="answer">
<h2>Answer</h2>
  <button @click="getAnswer" id="btn">try</button>
  <p>[[ irisName ]]</p>
</div>
VueとFlaskが共存できるように delimiters: [“[[“, “]]”] としました。

入力フォーム

3. 入力された値を分類器に渡す

入力フォーム関連の記述を追加します。

irisCubism.html
<script>
var ans = new Vue({
  el: "#exampleA",
  delimiters: ["[[", "]]"],
  data: {
    irisName: '// 菖蒲のパラメータが入力されたら品種名を出力します //',
    selectedA: '0',
    selectedB: '0',
    selectedC: '0',
    selectedD: '0',
   options: [
     {text:'0',value:'0'},  //0~10までの数値でアヤメのパラメータを選択
     {text:'1',value:'1'},
     {text:'2',value:'2'},
     {text:'3',value:'3'},
     {text:'4',value:'4'},
     {text:'5',value:'5'},
     {text:'6',value:'6'},
     {text:'7',value:'7'},
     {text:'8',value:'8'},
     {text:'9',value:'9'},
     {text:'10',value:'10'}
   ]
  },
  methods: {
    update_name: function(str) {
      this.irisName = str;
    },
    getAnswer:function() {
      var src = [];
      var SepalLength = parseFloat(this.selectedA);
      var SepalWidth = parseFloat(this.selectedB);
      var PetalLength = parseFloat(this.selectedC);
      var PetalWidth = parseFloat(this.selectedD);
      src = [SepalLength, SepalWidth, PetalLength, PetalWidth];
      callback = this.update_name;
      fetch('http://localhost:5000/irisCubism', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(src),  //入力された値を分類器に渡す
     }).then(function(res){
        return res.json();
     }).then(function(src) {
        callback('「品種名は ' + src.irisName + ' です」');  //分類器からirisNameが返ってくる
     }).catch(function(error) {
        console.log(error)
    })
  }}
})
</script>

値を分類器に送る

4. 分類器の処理

Vueから渡された値を基にして、分類器でアヤメの品種を判定します。

web.py
@app.route('/irisCubism', methods = ['GET', 'POST'])
def irisCubism():
    src = request.json                 //Vueから送られてきた値
    params = np.array(src)
    if request.method == 'POST':
        pred = predictIris(params)       //1.で作成した学習モデルで判定を行う、内容については「機械学習」で調べて
        irisName = getIrisName(pred)     //0,1,2で出力される判定結果を品種名に変換する処理を行う
        return make_response(jsonify({   //分類器の判定結果をVueに返す
            'irisName':irisName
        }))
    elif request.method == 'GET':
        return render_template('irisCubism.html')

品種を判定

5. CubismWebの画面を表示する

Vueの入力フォームの上にCubismWebの画面を配置します。

irisCubism.html
<div class="sizer" id="example">
  <canvas id="SAMPLE" width="1280" height="640">
    このブラウザは canvas 要素をサポートしていません。
  </canvas>
</div>

live2dcubismcoreなどはページの最後尾で読み込みます。

irisCubism.html
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<script src="static/Core/live2dcubismcore.min.js"></script>
<script src="static/dist/index.js"></script>

lappdefine.tsの相対パス ResourcesPath を作業環境に合わせて変更します。
今回は static というフォルダに必要なファイルを格納しているので、下記のように変更しました。

lappdefine.ts
// 相対パス
export const ResourcesPath: string = "static/Sample/JavaScript/Demo/Resources/";

Cubism表示

6. 分類結果の表示に合わせてモデルのモーションを変更する

getAnswerのボタン(id=”btn”)がクリックされた時にモデルのモーションを更新することにしました。
モデルの更新処理及び描画処理を行うonUpdateを使って強引に更新しています。
(できればモーション更新用の処理で切り替えたい)

lapplive2dmanager.ts
window.onload = () => {
  let isPlay = false;
  let bb = document.getElementById("btn");
  bb.onclick = () => {
    isPlay = true;
    this.onUpdate = () => {

    //onUpdateの中身と同じ//

    if (isPlay === false) {
      return;
    }
    for (let i: number = 0; i < this._models.getSize(); i++) {
      //更新用のモーション
      this._models.at(i).startMotion(LAppDefine.MotionGroupAdd, 0, LAppDefine.PriorityNormal);
    }
    isPlay = false;
    }
  }
}

モーション更新

タイトルとURLをコピーしました