Edited at

Visual Recognitonを利用する最初のレシピ

文章が分かりづらかったので、再構築(2018/6/5)


このページについて

このページはハッカソンを迅速にお楽しみいただくために開発した開発レシピ集です。レシピのまとめページはIBM Cloudのビギナー開発者向け「最初のレシピ」集になります。

あくまで個人の活動として掲載しているので、IBM ソーシャル・コンピューティング・ガイドラインに則っています。

すなわち、このサイトの掲載内容は私自身の見解であり、必ずしもIBMの立場、戦略、意見を代表するものではありません。


はじめに

IBM CloudとはIBM社が提供するクラウドサービスです。

クラウドとは・・・と紙で理解するよりは、実際に使って理解したほうが早いです。

そのため、ここでは画像認識サービスであるVisual Recognitionについてレシピとしてまとめます。


Visual Recognitionを試す

「AIとは・・・」とか「この製品とは・・」とか、細かい話の前に使ってみましょう!


Visual Recognitonのデモサイトを触ってみる。

デモサイトでは、IBM社が収集した画像データに基づいて、学習データを準備しなくても画像認識できる機能があるので、何も準備しなくてもお試しができます。

Visual Recognitionのサイトトップはこちら。

https://www.ibm.com/watson/jp-ja/developercloud/visual-recognition.html

こんな画面が出るはずです。

さっそく「デモを試す」をクリックしましょう。

スクリーンショット 2018-06-05 7.53.37.jpg

さっそく「デモを試す」という場所に移動したはずです。

ここの「画像認識Visual Recognitionを試してみる(英語)」を選んでください。

下記の画像の赤線参照。

スクリーンショット 2018-06-05 7.47.11.jpg

デモサイトに来ました。実は下にスクロールできます。

下にスクロールしましょう。

スクリーンショット 2018-06-05 7.56.14.jpg

こんな画像が出るはずです。

右上のような「青色の吹き出し(マニュアル)」が出たら「☓ボタン」を押してください。

(我々にはこのサイトがあるので、読まなくてよいです)

スクリーンショット 2018-06-05 7.58.14.jpg

とりあえず、動かしてみましょう。

画面下の画像をクリックしてみてください。

スクリーンショット 2018-06-05 8.07.07.jpg

そうすると、右側の文字列が変わるはずです。

[electrical device]とか[memory device]とか書いていますね。

これが、画像認識した結果、Visual Recognitionが認識した結果です。

なお、0.69とか書いているのは、Socreといって、認識の確信度みたいなものです。

よく使うパターンとしては100掛けて、69%と表現したりします。

スクリーンショット 2018-06-05 8.08.19.jpg

次に、女性を選んでみましょう。

スクリーンショット 2018-06-05 8.11.13.jpg

先ほどと同じように、右側の文字が変わりましたね。[person]とか。

ここで、その下にある「Face Model +」の「+」をクリックしてみましょう。

スクリーンショット 2018-06-05 8.12.19.jpg

なんとAge、FEMALE・・・年齢と性別が出ています!

スクリーンショット 2018-06-05 8.14.30.jpg

これらはIBMさんが夜なべして画像認識の学習データを取り込んで、何の準備もなくても、これらの機能が使えるように準備してくれていたのです。すごい!

さて、ここで信じられないという人がいるでしょう。

「予め認識しやすい画像だからでしょ!?」

そんな人のために、画像をアップロードしてテストすることが可能です。

画面のカメラマークをクリックしてみてください。

スクリーンショット 2018-06-05 8.18.44.jpg

画像のアップロード方法が出てくるので、適宜選んでください。

さすがにこれ以上の説明は割愛します。

スクリーンショット 2018-06-05 8.19.41.jpg


Visual Recognition を作ってみる。

「「All, Right!」「わかったわかった!」、デモサイトは完成しているからね。でも、作るの大変なんでしょう?」という方のために、自作の方法を伝授します!


IBM Cloud環境を準備する

IBM Cloudを無料で使うライト・アカウントを作成未済であれば、以下のサイトで登録ください。


IBM Cloud基本的な使い方

何も購入していないと、こんな感じになりますが、何かを購入すると画像は変わってきます。

スクリーンショット 2018-06-05 8.35.43.jpg


Visual Recognitionを導入する

ではさっそく、Visual Recognition(無料)を購入しに行きましょう。

右上の「リソース設定」をクリックします。

スクリーンショット 2018-06-05 8.36.55.jpg

リソース設定の画面が出ましたね。

実は、IBM Cloudはネットショッピングの如く、「ショッピング・カート機能」のようなものです。

ではさっそく、検索バーに「Visual」と入力して、Visual Recognitionと追記しましょう。

そうすると、特にボタンを幼くても、「Watson]「Visual Recognition」と出るはずです。

この「Visual Recoginition」をクリックします。

スクリーンショット 2018-06-05 8.44.42.jpg

Visual Recognitionの説明画面が出ましたね。

ここをスクロールしてみましょう。

スクリーンショット 2018-06-05 8.47.17.jpg

ショッピングカートのごとく、購入する商品が見れます。

今回は「ライト」無料が選ばれていることを確認して、「作成」ボタンを押しましょう。

スクリーンショット 2018-06-05 8.48.38.jpg


Visual Recognitionのツール起動のための画面の開き方

Visual Recognition作成されるとこんな画面が出ます。

でも、次からはログインしてこの画面を出さないといけないので、ここで画面の出し方を試しておきましょう。

左上のハンバーガーをクリックします。

スクリーンショット 2018-06-05 8.56.00.jpg

ダッシュボードをクリックします。

スクリーンショット 2018-06-05 8.57.21.jpg

ダッシュボード画面が出ました。

さきほどと画面の内容が違うと思います。

画面に迷った方は、このダッシュボード画面を起点に操作してください。

このVisual Recognitionの箇所をクリックすると、次の画面に移動できます。

スクリーンショット 2018-06-05 8.58.13.jpg


Visual Recognitionツールを使う

まずは「ツールを起動」ボタンをクリックします。

スクリーンショット 2018-06-05 8.54.04.jpg

何も気にせず「Continue」ボタンを押します。

スクリーンショット 2018-06-05 9.01.10.jpg

「Done」と出ると成功です。

スクリーンショット 2018-06-05 9.02.05.jpg

例によってマニュアルポップアップが出るので「☓」をクリックして閉じます。

スクリーンショット 2018-06-05 9.03.40.jpg

メニューが立ち上がるので、少しテストをしてみましょう。

先程も説明したとおり、顔認識はIBM社が学習データを準備しているため、我々がデータを準備しなくても、すぐ実用化することができます。

今回は、その環境を利用してテストしてみましょう。

Facesの「Test」をクリックします。

スクリーンショット 2018-06-05 9.04.44.jpg

次に「テスト」タブをクリックします。

スクリーンショット 2018-06-05 9.05.47.jpg

右下に「Drag & Drop」という文字があると思います。

この辺のエリアに画像をDrag&Dropしてみましょう。

スクリーンショット 2018-06-05 9.06.38.jpg

こんな感じでできました。

スクリーンショット 2018-06-05 9.08.12.jpg

はい、これでVisual Recognitionの


スマホでも使える顔認識アプリを作ろう!


アプリの環境構築

スマホでも使える顔画像認識アプリを作ります。

今回は、開発を簡単にするために、Node-REDボイラーテンプレート利用します。

まずは、ダッシュボード画面に移動します。

まずは、ハンバーガーをクリックしましょう。

スクリーンショット 2018-06-05 9.11.06.jpg

メニューのダッシュボードをクリックします。

スクリーンショット 2018-06-05 9.12.19.jpg

ダッシュボード画面に戻りました。

「あれれ?」いつのまにか、Visual Recognitionとは別のもの(Knowledge CatalogとWatson Studio)が増えていますね。

でも、これは気にしなくていいです。

(説明すると長くなるので今回は割愛します)

気にせず、「リソースの作成」ボタンを押します。

スクリーンショット 2018-06-05 9.13.15.jpg

Visual Recognitionと同じようにソフトを購入します。

検索バーに「Node-RED」という文字を入力すると、ソフト一覧が変わると思います。

その中で、「Node-RED Starter」があると思うので、クリックしてください。

スクリーンショット 2018-06-05 9.15.03.jpg

ロードが遅いとこんな画面が出ますが、操作せずにお待ち下さい。

スクリーンショット 2018-06-05 9.17.07.jpg

こんな画面になると思います。

スクリーンショット 2018-06-05 9.17.37.jpg

アプリ名を入力してください。

ここでは「visual-recognition-test-00001」としていますが、ユニークな文字なら何でもいいです。アルファベットと'-'と数字の組み合わせであれば。

多くの方は「0001」を任意の数字にするといいかもしれません。

そうすると「作成ボタン」が押せるようになりますので、クリックしてください。

スクリーンショット 2018-06-05 9.21.02.jpg

アプリケーションを動かす環境がセットアップするまで、しばらくお待ち下さい。

1分ぐらいですが、気分的にティータイム推奨。

スクリーンショット 2018-06-05 9.23.11.jpg


アプリを実装する

さっそく、アプリを実装しますが、道に迷わないようにダッシュボードからの移動方法を説明します。

ハンバーガーをクリックします。

スクリーンショット 2018-06-05 9.30.35.jpg

「ダッシュボード」をクリックします。

スクリーンショット 2018-06-05 9.31.59.jpg

ダッシュボードには、先ほどつけた「visual-recognition-test-00001」という行が増えていると思います。

アプリを作るので、ここをクリックします。

スクリーンショット 2018-06-05 9.33.41.jpg

開かれたメニューをスクロールします。

スクリーンショット 2018-06-05 11.07.23.jpg

「接続の作成」ボタンを押します。

スクリーンショット 2018-06-05 11.08.17.jpg

そうすると、先程購入したVisual Recognitionが出てきます。

ここにマウスカーソルをあてると・・・

スクリーンショット 2018-06-05 11.09.22.jpg

カーソルが当たると「Connect」というボタンが出るので、クリックしてください。

スクリーンショット 2018-06-05 11.10.44.jpg

次の画面の「接続」ボタンを押します。

こうすることで、これから作るアプリの環境と、購入したVisual Recognitionが関連付けられます。

関連付けることで、アプリを作るとき、普段はプログラムしなくてはならないVisual Recognitionの細かい設定プログラムを一切考えなくても開発できるようになります。

スクリーンショット 2018-06-05 11.13.03.jpg

関連付けを有効化するため、初期化処理を実行します。

色々記述がありますが、要は「再ステージ」は、「色々アップデートされたので、パソコンを再起動するイメージ」で考えておけばいいです。

スクリーンショット 2018-06-05 11.14.18.jpg

「パソコンの再起動のイメージ」なので、少し時間がかかります。

クリックした後の画面を見ると、赤線の当たりが「再ステージング中」となっており、アイコンがぐーるぐーるしていると思います。

スクリーンショット 2018-06-05 11.16.41.jpg

ぐーるぐーるが終わると、こんな感じになります。

ここで「経路」というボタンをクリックしてみてください。

スクリーンショット 2018-06-05 11.18.12.jpg

開かれたメニューのリンクをクリックします。

そうすると、別ウィンドウが開かれるはずです。

スクリーンショット 2018-06-05 11.20.01.jpg

別ウィンドウに出た画面を開くと、開発環境の初期設定画面が出ます。

(次からは出なくなるので、1回目だけの作業だと思って下さい)

さっそく「NEXT」を押してください。

スクリーンショット 2018-06-05 11.21.02.jpg

次にusernameとpasswordを入れる画面が出ます。

このusernameとpasswordは次回も使うので、メモしておいてください!!!!

これらの値は何でもいいですが、IBM CloudのログインID/パスワードだと覚えやすそうですね(責任は持ちませんが)。

usernameとpasswordを入力したら、「NEXT」ボタンを押してください。

スクリーンショット 2018-06-05 11.22.17.jpg

次に色々説明する画面がありますが、特に気にせず「NEXT」ボタンを押してください。

スクリーンショット 2018-06-05 11.25.11.jpg

最後に「Finish」ボタンを押してください。

スクリーンショット 2018-06-05 11.25.58.jpg

これで環境ができました。

おつかれさまでした。


アプリ開発画面のログインについて

先ほど流れで初期設定を終えたとき、あるいは、(補足)の画面で「経路」を選択したとき、このような画面になります。

これはアプリを開発する画面の初期画面になります。

さっそく、右側にある「Go to your Node-RED flow editor」をクリックしてみましょう。

スクリーンショット 2018-06-05 11.27.08.jpg

*補足

次からは、先程あった「経路」ボタンの「URL」をクリックしても同じ画面になります。

さっきのURLの画面は以下のやつ。困ったら、前の工程から同じ画面を探してみてください。

スクリーンショット 2018-06-05 11.20.01.jpg

ログイン画面が出ました。

先程、登録したusernameとpasswordを入力して、「ログイン」ボタンを押してみましょう。

スクリーンショット 2018-06-05 11.30.17.jpg


アプリを開発する

ログインすると、こんな画面が出てきます。

これがアプリを開発する画面です。

さっそく、右上のハンバーガーを選んでみましょう。

スクリーンショット 2018-06-05 11.33.21.jpg

そうするとメニューが出てくるので、下記のようにマウスカーソルを合わせて、「クリップボード」をクリックしてください。

スクリーンショット 2018-06-05 11.34.56.jpg

こんな画面が出てきました。

*初期画面

スクリーンショット 2018-06-05 11.36.22.jpg

赤線の枠線に以下の文字列をコピーしてください。


JSON.json

[{"id":"a9c22725.da4bc8","type":"tab","label":"画像認識サンプル","disabled":false,"info":""},{"id":"e3c35cdd.d33ce","type":"template","z":"a9c22725.da4bc8","name":"写真アップロード","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"ja\" class=\"no-js\">\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no\" />\n  <title>Watson Visual Recognition on Node-RED</title>\n  <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css\">\n</head>\n<body>\n<div class=\"container-fluid\">\n  <div class=\"col-sm-12\">\n    <p>Watson Visual Recognition on Node-RED</p>\n  </div>\n\n  <div class=\"panel panel-success col-sm-12\">\n    <div class=\"panel-heading\">\n      Select an image file\n    </div>\n\n    <div class=\"panel-body\" style=\"height:300px;\">\n      <div class=\"row\">\n        <form id=\"src_image\" action=\"\" method=\"post\" enctype=\"multipart/form-data\">\n          <input class=\"btn btn-info btn-ls\" type=\"file\" name=\"imagedata\" accept=\"image/*\"  />\n        </form>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"btn btn-danger btn-ls\" onclick=\"callVisualRecognition()\"/>\n          Analyze\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"preview\">\n        </div>\n      </div>\n    </div>\n\n    <div class=\"panel-footer\" style=\"height:100px;\">\n      <div id=\"result\"></div>\n    </div>\n\n  </div>\n</div>\n\n<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\n<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js\"></script>\n\n<script type=\"text/javascript\">\n    // Node-REDのWeb API連携\n    var callVisualRecognition = function() {\n\n      // フォームデータを取得\n      var formdata = new FormData($('#src_image').get(0));\n\n      console.log(formdata);\n\n      // POSTでアップロード\n      $.ajax({\n          url  : \"./face1-post\",\n          type : \"POST\",\n          data : formdata,\n          cache       : false,\n          contentType : false,\n          processData : false,\n          dataType    : \"html\"\n      })\n      .done(function(data, textStatus, jqXHR) {\n        var result_element = document.getElementById('result');\n\n        var objResult = JSON.parse(data);\n\n        // 子ノードを全削除\n        if (result_element.hasChildNodes()){\n          for (var i=result_element.childNodes.length-1; i>=0; i--) {\n            result_element.removeChild(result_element.childNodes[i]);\n          }\n        }\n\n        // 年齢\n        var age;\n        age = document.createTextNode(\"age=\"+objResult.images[0].faces[0].age.min +\"~\"+objResult.images[0].faces[0].age.max+\"(\"+objResult.images[0].faces[0].age.score+\")\");\n        var ageBox = document.createElement('p');\n        ageBox.appendChild(age);\n        result_element.appendChild(ageBox);\n\n        // gender\n        gender = document.createTextNode(\"gender=\"+objResult.images[0].faces[0].gender.gender+\"(\"+objResult.images[0].faces[0].gender.score+\")\");\n        var genderBox = document.createElement('p');\n        genderBox.appendChild(gender);\n        result_element.appendChild(genderBox);\n\n        console.log(objResult);\n      })\n      .fail(function(jqXHR, textStatus, errorThrown) {\n          // 通信エラー\n          // do nothing\n          return;\n      })\n      .always(function(data) {\n        // do nothing\n      });\n    }\n</script>\n\n<script>\n$(function(){\n  //画像ファイルプレビュー表示のイベント追加 fileを選択時に発火するイベントを登録\n  $('form').on('change', 'input[type=\"file\"]', function(e) {\n    var file = e.target.files[0],\n        reader = new FileReader(),\n        $preview = $(\".preview\");\n        t = this;\n\n    // 画像ファイル以外の場合は何もしない\n    if(file.type.indexOf(\"image\") < 0){\n      return false;\n    }\n\n    // ファイル読み込みが完了した際のイベント登録\n    reader.onload = (function(file) {\n      return function(e) {\n        //既存のプレビューを削除\n        $preview.empty();\n        // .prevewの領域の中にロードした画像を表示するimageタグを追加\n        $preview.append($('<img>').attr({\n                  src: e.target.result,\n                  height: \"150px\",\n                  class: \"preview\",\n                  title: file.name\n              }));\n      };\n    })(file);\n\n    reader.readAsDataURL(file);\n  });\n});\n\n</script>\n</body>\n</html>\n","x":330,"y":80,"wires":[["9cb0b56a.e22928"]]},{"id":"cacbb4de.1cf6b8","type":"http in","z":"a9c22725.da4bc8","name":"[GET] /face1","url":"/face1","method":"get","upload":false,"swaggerDoc":"","x":130,"y":80,"wires":[["e3c35cdd.d33ce"]]},{"id":"9cb0b56a.e22928","type":"http response","z":"a9c22725.da4bc8","name":"http response","x":539.5,"y":80,"wires":[]},{"id":"3bf9c4b0.873fac","type":"function","z":"a9c22725.da4bc8","name":"jpeg抽出","func":"var buf = msg.req.body;\nvar SOI = new Buffer(\"FFD8\",\"hex\");\nvar EOI = new Buffer(\"FFD9\",\"hex\");\nvar iSOI = 0;\nvar file = \"\";\n\nfor (var i=0 ; i<=buf.length ; i++) {\n    if(SOI.equals(buf.slice(i,i+2))) {\n        iSOI = i;\n        }\n    if(EOI.equals(buf.slice(i,i+2))) {\n        file = buf.slice(iSOI,i+2);\n        break;\n        }\n    }\n\nmsg.file = file;\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":140,"wires":[["7f7844c9.9f5b0c"]]},{"id":"d88ef886.f00af8","type":"http in","z":"a9c22725.da4bc8","name":"[POST] /face1-post","url":"/face1-post","method":"post","upload":false,"swaggerDoc":"","x":125,"y":128,"wires":[["3bf9c4b0.873fac"]]},{"id":"7f7844c9.9f5b0c","type":"function","z":"a9c22725.da4bc8","name":"parse","func":"msg.payload = msg.file;\nreturn msg;","outputs":1,"noerr":0,"x":290,"y":200,"wires":[["a04c732d.6fe69"]]},{"id":"39150e99.319ec2","type":"json","z":"a9c22725.da4bc8","name":"","property":"payload","action":"str","pretty":true,"x":490,"y":260,"wires":[["e6746519.664be8","7d7e65a4.d5d25c"]]},{"id":"39a7ec11.2b0d24","type":"debug","z":"a9c22725.da4bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","x":570,"y":160,"wires":[]},{"id":"e6746519.664be8","type":"http response","z":"a9c22725.da4bc8","name":"http response","x":620,"y":280,"wires":[]},{"id":"7d7e65a4.d5d25c","type":"debug","z":"a9c22725.da4bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":610,"y":220,"wires":[]},{"id":"480df1d.1309f1","type":"function","z":"a9c22725.da4bc8","name":"parse","func":"msg.payload = msg.result;\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":320,"wires":[["39150e99.319ec2"]]},{"id":"49251648.c24de8","type":"template","z":"a9c22725.da4bc8","name":"写真アップロード","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"ja\" class=\"no-js\">\n<head>\n  <meta charset=\"UTF-8\" />\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n  <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no\" />\n  <title>Watson Visual Recognition on Node-RED</title>\n  <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css\">\n</head>\n<body>\n<div class=\"container-fluid\">\n  <div class=\"col-sm-12\">\n    <p>Watson Visual Recognition on Node-RED</p>\n  </div>\n\n  <div class=\"panel panel-success col-sm-12\">\n    <div class=\"panel-heading\">\n      Select an image file\n    </div>\n\n    <div class=\"panel-body\" style=\"height:300px;\">\n      <div class=\"row\">\n        <form id=\"src_image\" action=\"\" method=\"post\" enctype=\"multipart/form-data\">\n          <input class=\"btn btn-info btn-ls\" type=\"file\" name=\"imagedata\" accept=\"image/*\"  />\n        </form>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"btn btn-danger btn-ls\" onclick=\"callVisualRecognition()\"/>\n          Analyze\n        </div>\n      </div>\n\n      <div class=\"row\">\n        <div class=\"preview\">\n        </div>\n      </div>\n    </div>\n\n    <div class=\"panel-footer\" style=\"height:100px;\">\n      <div id=\"result\"></div>\n    </div>\n\n  </div>\n</div>\n\n<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>\n<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js\"></script>\n\n<script type=\"text/javascript\">\n    // Node-REDのWeb API連携\n    var callVisualRecognition = function() {\n\n      // フォームデータを取得\n      var formdata = new FormData($('#src_image').get(0));\n\n      console.log(formdata);\n\n      // POSTでアップロード\n      $.ajax({\n          url  : \"./cust1-post\",\n          type : \"POST\",\n          data : formdata,\n          cache       : false,\n          contentType : false,\n          processData : false,\n          dataType    : \"html\"\n      })\n      .done(function(data, textStatus, jqXHR) {\n        var result_element = document.getElementById('result');\n\n        var objResult = JSON.parse(data);\n        console.log(objResult);\n\n        // 子ノードを全削除\n        if (result_element.hasChildNodes()){\n          for (var i=result_element.childNodes.length-1; i>=0; i--) {\n            result_element.removeChild(result_element.childNodes[i]);\n          }\n        }\n\n        // classifierId+name\n        var classifier;\n        classifier = document.createTextNode(\"classifierId=\"+objResult.images[0].classifiers[0].classifier_id+\", classifierName=\"+objResult.images[0].classifiers[0].name);\n        var classifierBox = document.createElement('p');\n        classifierBox.appendChild(classifier);\n        result_element.appendChild(classifierBox);\n\n        // classes\n        var classes;\n        classes = document.createTextNode(\"className=\"+objResult.images[0].classifiers[0].classes[0].class +\", score=\"+objResult.images[0].classifiers[0].classes[0].score );\n        var classesBox = document.createElement('p');\n        classesBox.appendChild(classes);\n        result_element.appendChild(classesBox);\n      })\n      .fail(function(jqXHR, textStatus, errorThrown) {\n          // エラー\n          var error;\n          error = document.createTextNode(\"error=\"+data);\n          var errorBox = document.createElement('p');\n          errorBox.appendChild(error);\n          result_element.appendChild(errorBox);\n          return;\n      })\n      .always(function(data) {\n        // do nothing\n      });\n    }\n</script>\n\n<script>\n$(function(){\n  //画像ファイルプレビュー表示のイベント追加 fileを選択時に発火するイベントを登録\n  $('form').on('change', 'input[type=\"file\"]', function(e) {\n    var file = e.target.files[0],\n        reader = new FileReader(),\n        $preview = $(\".preview\");\n        t = this;\n\n    // 画像ファイル以外の場合は何もしない\n    if(file.type.indexOf(\"image\") < 0){\n      return false;\n    }\n\n    // ファイル読み込みが完了した際のイベント登録\n    reader.onload = (function(file) {\n      return function(e) {\n        //既存のプレビューを削除\n        $preview.empty();\n        // .prevewの領域の中にロードした画像を表示するimageタグを追加\n        $preview.append($('<img>').attr({\n                  src: e.target.result,\n                  height: \"150px\",\n                  class: \"preview\",\n                  title: file.name\n              }));\n      };\n    })(file);\n\n    reader.readAsDataURL(file);\n  });\n});\n\n</script>\n</body>\n</html>\n","x":330,"y":420,"wires":[["42ac763b.d26cc8"]]},{"id":"6c8788b1.6a9388","type":"http in","z":"a9c22725.da4bc8","name":"[GET] /cust1","url":"/cust1","method":"get","upload":false,"swaggerDoc":"","x":130,"y":420,"wires":[["49251648.c24de8"]]},{"id":"42ac763b.d26cc8","type":"http response","z":"a9c22725.da4bc8","name":"http response","x":539.5,"y":420,"wires":[]},{"id":"eb0058cc.eccc88","type":"function","z":"a9c22725.da4bc8","name":"jpeg抽出","func":"var buf = msg.req.body;\nvar SOI = new Buffer(\"FFD8\",\"hex\");\nvar EOI = new Buffer(\"FFD9\",\"hex\");\nvar iSOI = 0;\nvar file = \"\";\n\nfor (var i=0 ; i<=buf.length ; i++) {\n    if(SOI.equals(buf.slice(i,i+2))) {\n        iSOI = i;\n        }\n    if(EOI.equals(buf.slice(i,i+2))) {\n        file = buf.slice(iSOI,i+2);\n        break;\n        }\n    }\n\nmsg.file = file;\nreturn msg;","outputs":1,"noerr":0,"x":300,"y":480,"wires":[["ec132d76.00162"]]},{"id":"2cb99d1b.d0c382","type":"http in","z":"a9c22725.da4bc8","name":"[POST] /cust1-post","url":"/cust1-post","method":"post","upload":false,"swaggerDoc":"","x":125,"y":468,"wires":[["eb0058cc.eccc88"]]},{"id":"ec132d76.00162","type":"function","z":"a9c22725.da4bc8","name":"parse","func":"msg.payload = msg.file;\nreturn msg;","outputs":1,"noerr":0,"x":250,"y":540,"wires":[["d77b5a8d.d821a8"]]},{"id":"8b72a21d.04572","type":"json","z":"a9c22725.da4bc8","name":"","property":"payload","action":"str","pretty":true,"x":490,"y":600,"wires":[["cb27a8df.97c158","4f41385b.c698c8"]]},{"id":"44ab232b.6cc57c","type":"debug","z":"a9c22725.da4bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"result","x":570,"y":500,"wires":[]},{"id":"cb27a8df.97c158","type":"http response","z":"a9c22725.da4bc8","name":"http response","x":620,"y":620,"wires":[]},{"id":"4f41385b.c698c8","type":"debug","z":"a9c22725.da4bc8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":610,"y":560,"wires":[]},{"id":"643f16c0.7ae348","type":"function","z":"a9c22725.da4bc8","name":"parse","func":"msg.payload = msg.result;\nreturn msg;","outputs":1,"noerr":0,"x":450,"y":660,"wires":[["8b72a21d.04572"]]},{"id":"d77b5a8d.d821a8","type":"function","z":"a9c22725.da4bc8","name":"Visual Recognitionカスタム認識の設定","func":"msg.params = {};\nmsg.params.classifier_ids = \"DefaultCustomModel_1898051895\";\nmsg.params.threshold = \"0.2\";\nreturn msg;","outputs":1,"noerr":0,"x":230,"y":600,"wires":[["b4bbc2d6.cc09"]]},{"id":"b4bbc2d6.cc09","type":"visual-recognition-v3","z":"a9c22725.da4bc8","name":"Detect Custom","apikey":"","image-feature":"classifyImage","lang":"ja","x":280,"y":660,"wires":[["643f16c0.7ae348","44ab232b.6cc57c"]]},{"id":"a04c732d.6fe69","type":"visual-recognition-v3","z":"a9c22725.da4bc8","name":"","apikey":"","vr-service-endpoint":"https://gateway.watsonplatform.net/visual-recognition/api","image-feature":"detectFaces","lang":"","x":190,"y":280,"wires":[["480df1d.1309f1","39a7ec11.2b0d24"]]},{"id":"9327753a.fb1ab","type":"http in","z":"a9c22725.da4bc8","name":"","url":"/test","method":"get","upload":false,"swaggerDoc":"","x":140,"y":20,"wires":[["fd43d664.f36e48"]]},{"id":"fd43d664.f36e48","type":"template","z":"a9c22725.da4bc8","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"ホームページ出来た!","output":"str","x":300,"y":20,"wires":[["c6e31e84.746b28"]]},{"id":"c6e31e84.746b28","type":"http response","z":"a9c22725.da4bc8","name":"","statusCode":"","headers":{},"x":480,"y":20,"wires":[]}]


*貼り付け後

スクリーンショット 2018-06-05 11.40.21.jpg

貼り付けたら、「読み込み」ボタンを押してください。

スクリーンショット 2018-06-05 11.41.33.jpg

タブが増えました。

さっそく、追加された「画像認識サンプル」タブを押してみましょう。

スクリーンショット 2018-06-05 11.43.10.jpg

なんか、フロー図みたいなのができていますね。

赤枠線で囲っている範囲が、今回利用するアプリのプログラムです。

時代はもう、文字列を並べて作るだけがプログラムではないのです。

このプログラム環境を「Node-RED」というツールです。


(余談)Node−REDはIBMさんだけのものではなく、みんなのもの(オープンソース)です。なので、中身は個人でも見たり、修正したりできます。

興味が出た方は「Node-RED User Group Japan」へ!

https://nodered.jp/


スクリーンショット 2018-06-05 11.44.11.jpg

では、ベースができたところで、アプリを開発します。

「Visual Recognition」という部品をダブルクリックしてください。

スクリーンショット 2018-06-05 12.18.29.jpg

すると右側になにか出てきましたね。

「Service Endpoint」というものをクリックしてください。

スクリーンショット 2018-06-05 12.19.40.jpg

この中で、チェックの入っていない文字列を選択してください。

スクリーンショット 2018-06-05 12.20.30.jpg

先程、空白になっていた「Service Endpoint」に文字が入っていることを確認してください。

(Service Endpontは、最近、設定不要になったみたいです。設定が出てこなければ何もしなくて良いです)

スクリーンショット 2018-06-05 12.22.16.jpg

確認したら、右上の「完了」ボタンを押してください。

スクリーンショット 2018-06-05 12.23.16.jpg

さて、プログラムが出来たところで、プログラムをシステムに反映させましょう。

反映のことを「デプロイ(配置)」といいます。

「デプロイ」ボタンをクリックしてみてください。

スクリーンショット 2018-06-05 11.48.48.jpg

ボタンの色がグレーになったら反映完了です。

スクリーンショット 2018-06-05 11.50.15.jpg

これで開発は完了です。

おつかれさまでした。


アプリを動かす

さっそくアプリを動かしてみましょう。

Webアプリなので、アドレス(URL)を指定すると、アプリを動かすことが出来ます。

URLですが、参考になるのは先程の開発環境です。

この赤線を引いたアドレス(URL)の部分を修正します。

アドレス(赤線)をコピーして、新規タブを作成するボタン(赤線で囲ったボタン)を押します。

スクリーンショット 2018-06-05 12.00.53.png

新しいタブに「(元々のアドレス)」を貼り付けます。

アドレスは、


https://visual-recognition-test-00001.mybluemix.net/red/#flow/ほにゃほにゃ


です。

次に、


https://visual-recognition-test-00001.mybluemix.net/


より右の文字を削除して、「test」と追記します。

こんな感じです。


https://visual-recognition-test-00001.mybluemix.net/test


こんな感じになります。

入力が終わったら、キーボードの「エンター」キーを押して、画面を表示してみてください。

スクリーンショット 2018-06-05 17.07.02.png

ホームページができました!

次に


https://visual-recognition-test-00001.mybluemix.net/

より右の文字を削除して、「face1」と追記します。


こんな感じになります。

入力が終わったら、キーボードの「エンター」キーを押して、画面を表示してみてください。

スクリーンショット 2018-06-05 12.04.07.png

アプリ画面が出ました!

ここで、ファイルを選択するボタンを押してみましょう。

スクリーンショット 2018-06-05 12.07.35.png

ファイル選択画面。Windowsなら違うはずです。

この選択で、JPGのファイルを選択してください。

他の画像ではアプリは動きませんので注意してください!

スクリーンショット 2018-06-05 12.09.09.png

画像がちゃんとロードできると、サムネイル画像が出ます。

スクリーンショット 2018-06-05 12.13.13.jpg

ロード出来ていることを確認して、「Analyze」ボタンを押します。

スクリーンショット 2018-06-05 12.14.22.jpg

処理がうまくいくと、画面のグレー部分に、画像認識の結果が出ると思います。

スクリーンショット 2018-06-05 12.59.42.jpg


(応用編)Custom画像認識を作る

メニューのServiceを選び、Watson Serciceを選びます。

出てきたVisual RecognitionサービスのLaunch ボタンを押してください。

スクリーンショット 2018-05-18 15.13.30.jpg

プロジェクト作成がでてくるので、必要事項を入れてCreateします。

スクリーンショット 2018-05-18 15.15.38.jpg

カスタムデータモデルを作るのですが、右側のZipファイルをアップロードする必要があります。

スクリーンショット 2018-05-18 15.16.47.jpg

テスト用ならGoogle先輩にライセンスフリーの画像を探してもらいましょう!

スクリーンショット 2018-05-18 12.53.35.jpg

Zipがアップできると、add-modelというのが出るので、それを追加します。

スクリーンショット 2018-05-18 15.18.52.jpg

登録できた画像を学習させるため、Trainボタンを押します。

画面は誤ってボタンをおした後の画面です。

スクリーンショット 2018-05-18 16.38.56.jpg

トレーニングが終わったら学習データのIDを控える

スクリーンショット 2018-05-18 17.35.15.png


アプリを試す。

顔画像の認識に使った、Node-REDのフローに、今回用のフローがあります。

スクリーンショット 2018-06-05 12.50.27.jpg

このなかで、「Visual Recognitionカスタム認識の設定」をダブルクリックします。

スクリーンショット 2018-06-05 12.52.13.jpg

カスタムで作成したクラスIDに修正し、「完了」ボタンを押します。

スクリーンショット 2018-06-05 12.53.27.jpg

「デプロイボタン」を押します。

スクリーンショット 2018-06-05 12.55.40.jpg

処理が終わったら、完了です。


稼働確認する

顔画像認識アプリと同じですが、アドレス(URL)が違います。

URLの最後を[/cust1]に変更してアクセスしてみてください。

アプリは顔画像認識アプリと同じなので、操作説明は割愛します。

スクリーンショット 2018-06-05 12.56.49.jpg


(おまけ)画像へのURL指定で、画像認識を行う。

あまり使いませんが、画像へのURLを指定して、画像認識させるサンプルプログラムも掲載しておきます。

スクリーンショット 2018-09-12 14.23.08.png

[{"id":"3e4c0e0e.1d0542","type":"tab","label":"画像認識サンプル(URL)","disabled":false,"info":""},{"id":"28864629.0d428a","type":"http in","z":"3e4c0e0e.1d0542","name":"","url":"/face_url","method":"get","upload":false,"swaggerDoc":"","x":90,"y":60,"wires":[["3db8b82a.44e688"]]},{"id":"3db8b82a.44e688","type":"change","z":"3e4c0e0e.1d0542","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":260,"y":60,"wires":[["5bc91c85.e031e4"]]},{"id":"5bc91c85.e031e4","type":"visual-recognition-v3","z":"3e4c0e0e.1d0542","name":"","apikey":"","vr-service-endpoint":"https://gateway-a.watsonplatform.net/visual-recognition/api","image-feature":"detectFaces","lang":"en","x":390,"y":140,"wires":[["db4b187f.b63378","6264edb9.2baef4"]]},{"id":"db4b187f.b63378","type":"http response","z":"3e4c0e0e.1d0542","name":"","statusCode":"","headers":{},"x":550,"y":60,"wires":[]},{"id":"6264edb9.2baef4","type":"debug","z":"3e4c0e0e.1d0542","name":"","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"result","x":630,"y":160,"wires":[]},{"id":"93c6d1e9.77ed6","type":"http in","z":"3e4c0e0e.1d0542","name":"","url":"/general_url","method":"get","upload":false,"swaggerDoc":"","x":100,"y":220,"wires":[["fba9c35.38ff34"]]},{"id":"fba9c35.38ff34","type":"change","z":"3e4c0e0e.1d0542","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":200,"wires":[["8ee3aa49.914d48"]]},{"id":"dfbfab37.ea34a8","type":"visual-recognition-v3","z":"3e4c0e0e.1d0542","name":"","apikey":"","vr-service-endpoint":"https://gateway-a.watsonplatform.net/visual-recognition/api","image-feature":"classifyImage","lang":"en","x":310,"y":360,"wires":[["ab5d1a51.d6a5f8","5aac1ae8.536514"]]},{"id":"ab5d1a51.d6a5f8","type":"http response","z":"3e4c0e0e.1d0542","name":"","statusCode":"","headers":{},"x":550,"y":260,"wires":[]},{"id":"5aac1ae8.536514","type":"debug","z":"3e4c0e0e.1d0542","name":"","active":true,"tosidebar":true,"console":true,"tostatus":false,"complete":"result","x":550,"y":360,"wires":[]},{"id":"8ee3aa49.914d48","type":"function","z":"3e4c0e0e.1d0542","name":"Visual Recognitionカスタム認識の設定","func":"msg.params = {};\nmsg.params.classifier_ids = \"default\";\nmsg.params.threshold = \"0.2\";\nreturn msg;","outputs":1,"noerr":0,"x":210,"y":280,"wires":[["dfbfab37.ea34a8"]]}]

それでは!