4
10

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.

Visual RecognitionのカスタムClassifierをNode-REDから使う

Posted at

#はじめに
前々々回前回は、フライドチキンと犬の写真から、それぞれを分類するカスタムClassifierを紹介した。
前々回は、もともと入っている機能を使って、顔から年齢性別を認識させるアプリを紹介した。
今回はこれらを合体して、PCカメラでフライドチキンや犬の写真を撮ると、それをNode-REDアプリに送って、カスタムClassifierを呼び出して判別する、というアプリを作りたいと思う。

#Node-REDのフロー
Node-REDのフローは次のようになる。

見た目で言うと、違う点はカスタムClassifier設定のノードが追加されている点だ。

##カスタムClassifier対応
カスタムClassifierのClassifier IDと閾値を、msg.paramsのパラメーターとして指定する。例えば以下のような感じになる。

msg.params = {};
msg.params.classifier_ids = "Chicken_or_Dog_1056118805";
msg.params.threshold = "0";
return msg;

ノードの中身のキャプチャーはこんな感じ。

##VR呼び出し
VRの呼び出しは「Classify an image」を使う。

API Keyは自分のVRサービスのAPI Keyを入れるのは前と同じ。Languageはクラス名なんだけど、学習時に英語で学習させていたのでEnglishを選択。

##出力整形
VRの実行結果は次のようなJSONで返される。

{ "custom_classes": 2, 
  "images": [
    { "classifiers": [ 
      { "classes": [ 
        { "class": "FriedChiken", 
          "score": 0.144389 }, 
        { "class": "Labradoodle", 
          "score": 0.273302 } ], 
        "classifier_id": "Chicken_or_Dog_1056118805", 
        "name": "Chicken_or_Dog" } ], 
      "image": "1161028-58-swpyg9.jpg" } ], 
    "images_processed": 1 }

これをどう料理するかはアプリ次第。今回はスコアが高いほうのクラスと、スコアの割合を返すことにした。

##HTMLの表示
HTML側では返されたスコアをもとに結果を表示する。例えばこんな感じ。

スコアとコメントの関係は以下のようにしてみた。このあたりはコントロール可能なところなので、自分で好きなように変えられるぞ。

スコア コメント
80以下 ・・・何だ?
80~90 かな?
90~95 だよね
95以上 に違いない!

サンプルソースは以下。かなりベタ~な書き方をしているけどサンプルなので・・・。


<!doctype html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Visual Recognition</title>
    <link rel="stylesheet" href="https://bootswatch.com/cyborg/bootstrap.min.css">
    <script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
  </head>
  
  <body>
    <!-- NavBar -->
    <nav class="navbar navbar-default">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="index.html">Watson API Demo</a>
        </div>
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">APIs <span class="caret"></span></a>
              <ul class="dropdown-menu" role="menu">
                <li><a href="index.html">Visual Recognition</a></li>
              </ul>
            </li>
          </ul>
        </div>
      </div>
    </nav>
    <!-- Container -->
    <div class="container">
      <div class="starter-template">
        <div class="row">
          <div class="col-md-6" id="video_area">
            <center>
              <p><video id="myVideo" width="400" height="300" autoplay="1"></video></p>
              <p><canvas id="myCanvas" width="400" height="300" style="display:none"></canvas></p>
            </center>
          </div>
          <div class="col-md-6" id="display_area">
            <center>
              <div id="recording" style="display:none">
                <!-- <p><img src="images/logo.png"></img></p> -->
                <br>
                <h3>送信ボタンを押してください</h3>
                <br>
                <p><a class="btn btn-primary" id="btn_submit" onClick="sendImage()">送信する</a></p>
              </div>
              <div id="processing" style="display:none">
                <!-- <p><img src="images/logo_animation.gif"></img></p> -->
                <br>
                <h3>処理中...</h3>
              </div>
              <div id="result" style="display:none">
                <h4>これは・・・</h4>
                <h3><div id="answer"></div></h3>
                <h3><div id="comment"></div></h3>
                <br>
                <p><a class="btn btn-primary" id="btn_retry" onClick="startRecording()">もう1回</a></p>
              </div>
            </center>
          </div>
        </div>
      </div>
    </div>

    <script type="text/javascript">
      //初期化
      navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
      window.URL = window.URL || window.webkitURL;    
      
      //カメラ入力
      var video = document.getElementById('myVideo');
      
      //中間出力
      var canvas = document.getElementById('myCanvas');
      var ctx = canvas.getContext('2d');
      
      //ストリーム作成とカメラ画像のストリーミング開始
      var localStream = null;
      navigator.getUserMedia({video: true, audio: false}, 
        function(stream) { // for success case
          console.log(stream);
          video.src = window.URL.createObjectURL(stream);
        },
        function(err) { // for error case
          console.log(err);
        }
      );
      
      // カメラ表示、Watson静止ロゴ&説明表示
      var startRecording = function(){
        console.log("startRecording");
        
        // 表示切替
        document.getElementById('recording').style="display:";
        document.getElementById('processing').style="display:none";
        document.getElementById('result').style="display:none";
        
        // ビデオ開始
        video.play();
      }
      
      // カメラ停止&カメラ画像をNode-REDに送る
      var sendImage = function() {
        console.log("sendImageToNodeRED");
        video.pause();                                                // ビデオ停止
        
        document.getElementById('recording').style="display:none";    // 表示切替
        document.getElementById('processing').style="display:";
        document.getElementById('result').style="display:none";
        
        var canvas_image = ctx.drawImage(video,0,0,400,300);          // カメラ→imgに変換
        console.log("canvas = "+ canvas);
        var dataURL = canvas.toDataURL("image/jpeg");                 // DataURLに変換
        var base64 = dataURL.replace(/^.*,/, '');                     // プレフィックスを削除してBase64部分だけ取り出し
        var xhr= new XMLHttpRequest();                                // Node-REDの呼び出し準備
        xhr.open("POST", "https://あなたのNode-REDアプリケーション.mybluemix.net/vr/chicken");
        xhr.onreadystatechange = function(e){                         // コールバック関数
          if(xhr.readyState == 4){
            if(xhr.status == 200){
              console.log(xhr.responseText);
              displayResult(JSON.parse(xhr.responseText));
            }else{
              console.log(e)
              console.log(xhr.responseText);
            }
          }
        };
        xhr.send(base64);                                             // Node-REDの呼び出し
      }
      
      // Node-REDの結果を分析&表示
      var displayResult = function(result) {
        console.log("displayResult" + result);
        var answer = "";
        var score = "";
        switch (result.top_class) {
          case "FriedChiken":
            answer = "フライドチキン";
            break;
          case "Labradoodle":
            answer = "ラブラドゥードル";
            break;
          default:
            answer = "判定失敗";
            break;
        }
        
        if (Math.round(result.score * 100) > 95) {
          comment = "に違いない!";
        } else if (Math.round(result.score * 100) > 90) {
          comment = "だよね";
        } else if (Math.round(result.score * 100) > 80) {
          comment = "かな?";
        } else {
          comment = "・・・何だ?";
          answer = "";
        }                
        document.getElementById('answer').innerHTML = answer;
        document.getElementById('comment').innerHTML = comment;
        
        // 表示切替
        document.getElementById('recording').style="display:none";
        document.getElementById('processing').style="display:none";
        document.getElementById('result').style="display:";  
      }
      startRecording();      // メインロジック開始
   </script>
 </font>
 </body>
</html>

#おわりに
ということでサクっとカスタムClassifierを呼ぶことができました。キモはmsg.paramsでclassifier_idsを指定するところかな。ここができれば簡単に呼ぶことが可能。

ということで今日はここまで。

フライドチキンはこのあとVegaSatoがおいしくいただきました。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?