6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ml5jsで特徴抽出を用いた回帰を試す

Last updated at Posted at 2019-02-27

Daniel Shiffmanさんのcodingtrainのチュートリアルを参考に
特徴抽出を用いた回帰(Regression with Feature Extractor)を試してみました。
featureExtractor()メソッドを用いて実装できます。

ファイルは以下にアップしています。
https://www.dropbox.com/s/3cgr99lwuocugwo/03_2_featureExtractorRegression-demo.zip?dl=0

コードは以下です。
試す場合は、プロジェクトフォルダに「images」フォルダを設置し、配下に「happy.png」「sad.png」を置いてください。
ライブラリはCDNを読み込んでますのでローカルサーバー上で実行してください。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Feature Extractor Regression</title>
  <style>
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #inputContainer {
      position: absolute;
      top: 10px;
      left: 10px;
    }
    input, button {
      display: block;
      margin: 0 10px 10px 0;
    }
  </style>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js"></script>
  <script src="https://unpkg.com/ml5@0.1.1/dist/ml5.min.js"></script>
  <script src="sketch.js"></script>
</head>
<body>
  <div id="inputContainer"></div>
</body>
</html>

let mobilenet;
let predictor;
let video;
let happyIco;
let sadIco;
let value = 0;
let slider;
let addButton;
let trainButton;
// MobileNetモデルの準備が完了したときコールバック関数として呼ばれる
function modelReady() {
  console.log('モデルが準備できた!');
}
// ビデオ入力したクラス分類機の準備が完了したときコールバック関数として呼ばれる
function videoReady() {
  console.log('ビデオが準備できた!!!');
}
// トレーニング完了時に1度だけ実行される関数
function whileTraining(loss) {
  if (loss == null) {
    console.log('トレーニングが完了した!');
    // 画像の予測を取得する
    predictor.predict(gotResults);
  } else {
    console.log(loss);
  }
}
// トレーニング完了時に再帰的に実行される関数
function gotResults(error, result) {
  if (error) {
    console.error(error);
  } else {
    // 画像の予測の値を代入する
    value = result;
    // 画像の予測を取得する
    // 再帰的に実行
    predictor.predict(gotResults);
  }
}
function setup() {
  // キャンバスを生成する
  createCanvas(windowWidth, windowHeight);
  // ウェブカメラから映像をキャプチャし、Video要素を生成
  video = createCapture(VIDEO);
  // Video要素を非表示にする
  video.hide();
  happyIco = loadImage('images/happy.png');
  sadIco = loadImage('images/sad.png');
  // 背景を黒で塗りつぶす
  background(0);
  // MobileNetで事前学習された特徴を展開
  mobilenet = ml5.featureExtractor('MobileNet', modelReady);
  // ビデオを入力にして、回帰のカスタム予測子を生成する
  predictor = mobilenet.regression(video, videoReady);
  // スライダーを生成(0 ~ 1)
  slider = createSlider(0, 1, 0.5, 0.01);
  slider.parent('inputContainer');
  // 「サンプル画像追加」ボタンンを生成する
  addButton = createButton('サンプル画像を追加する');
  addButton.parent('inputContainer');
  addButton.mousePressed(function() {
    // 0 ~ 1の値に画像を紐づける
    predictor.addImage(slider.value());
  });
  // 「トレーニング開始」ボタンを生成する
  trainButton = createButton('トレーニング開始');
  trainButton.parent('inputContainer');
  // 「トレーニング開始」ボタンが押された時の処理
  trainButton.mousePressed(function() {
    // クラス分類機を再トレーニングする(Transfer Learning 転移学習)
    predictor.train(whileTraining);
  });
}
function draw() {
  // 背景を黒で塗りつぶし
  background(0);
  // ウェブカメラからの映像をキャンバスに描画
  // ミラー表示
  imageMode(CORNER)
  push(); 
    translate(width,0); 
    scale(-1.0,1.0); 
    image(video, 0, 0, width, height);
  pop();
  // 画像の直径は横幅の1/10
  let diameter = width / 10;
  // 円を描画
  imageMode(CENTER);
  // 上下左右に画像を配置
  for ( let x = 0; x < width; x += diameter) {
    for ( let y = 0; y < height; y += diameter) {
      if (value == 0) {
        break; // value = 0(初期値)のときは表示しない
      } else if (value < 0.5) {
        // 楽しい顔のアイコンを表示
        image(happyIco, x + diameter/2, y + diameter/2, diameter*Math.cos(value * Math.PI), diameter); 
      } else {
        // 悲しい顔のアイコンを表示
        image(sadIco, x + diameter/2, y + diameter/2, diameter*Math.cos(value * Math.PI), diameter);
      }
    }
  }
  // 線を描画しない
  noStroke();
  // 塗りを黒へ
  fill(0);
  // 矩形の描画モードを左上へ
  rectMode(CORNER);
  rect(0, height - 50, width, 50);
  
  // 矩形の描画モードを中央へ
  rectMode(CENTER);
  // 塗りつぶしを赤へ
  fill(255, 0, 0);
  rect(value * width, height - 25, 50, 50);
  // 塗りつぶしを白に設定
  fill(255);
  // テキストのサイズを設定
  textSize(25);
  // テキストを描画 
  text(value, 10, height - 10);
}
function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

ローカルサーバーを立ち上げ、ブラウザでindex.htmlを開いたら、
以下の手順でテストしてみてください。

  1. スライダーを左端までドラッグし、ウェブカメラの前で前でポーズをとって、[楽しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  2. スライダーを右端までドラッグし、ウェブカメラの前でポーズを変えて、[悲しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  3. [トレーニング開始]をクリックして、トレーニングプロセスが完了するまで待ちます。 (コンソールログにプロセスが表示されます。)
  4. トレーニングが完了したら、モデルをトレーニングした2つのポーズを切り替えます。

※カメラのいろんな位置で、ポーズ画像を追加した方が精度が上がるかと思います。

  • 楽しい
    happy.png

  • 悲しい
    sad.png

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?