#はじめに
前々々回と前回は、フライドチキンと犬の写真から、それぞれを分類するカスタム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がおいしくいただきました。