LoginSignup
8
7

More than 5 years have passed since last update.

[Watson] [Node-RED] 日本語に対応したVisual RecognitionのClassify an image(イメージ分類)を試してみた

Last updated at Posted at 2017-03-26

おことわり

本稿に掲載しているVisual Recognitionの解析結果は2017年3月現在のものです。
Watsonは日々進化していますので、本稿執筆時点と最新の結果は異なる可能性があります。

概要

Visual RecognitionのClassify an image API(イメージ分類=タグ付け機能)が日本語対応になりました。
Node-REDでアプリケーションを作成して日本語でのタグ付けを試してみました。

前置きはさておき、先に結果から

FireShot Capture 6 - Watson Visual Recognition_ - https___isol-watson-app.mybluemix.net_vr-classify_.png


FireShot Capture 7 - Watson Visual Recognition_ - https___isol-watson-app.mybluemix.net_vr-classify_.png


FireShot Capture 3 - Watson Visual Recognition_ - https___isol-watson-app.mybluemix.net_vr-classify_.png


FireShot Capture 5 - Watson Visual Recognition_ - https___isol-watson-app.mybluemix.net_vr-classify_.png


FireShot Capture 4 - Watson Visual Recognition_ - https___isol-watson-app.mybluemix.net_vr-classify_.png

Watson Visual Recognitionとは

Visual Recognitionも他の機能と同様にWeb APIとして提供されています。
APIは大きく分けて4つ。

  1. Classify an image …イメージ分類
  2. Detect faces …顔検出および性別・年齢の推定
  3. Custom classifiers …カスタム・イメージ分類
  4. Collections …類似画像抽出(2017年3月現在、ベータ版)

上記1、2はトレーニング済みで提供されており、事前学習の必要はありません。
一方3、4はユーザーがトレーニング画像を準備してWatsonに学習させ、独自の分類を行わせることができます。

本稿で使ったAPIは上記1です。
その他についてはWatson Developer Cloudか、あるいは以下の記事を参照してください。

なお、Alchemy APIという名前でもイメージ分類や顔検出機能を提供していましたが、Visual Recognitionに統合され、Alchemy APIは非推奨となっています。

Visual Recognitionの利用開始手順については以下の記事が参考になります。

BluemixでWatson API のVisual Recognition を使う by curl

開発したアプリケーションについて

Node-REDとは

あらかじめ用意された「ノード」と呼ばれる部品をつなげることにより、最小限のコードでIBM Bluemix上にWebアプリを構築できます。

Node-REDの利用開始手順はこちらの記事などをご参照ください。

コードをほとんど書かずにNode-REDでWebアプリを作ろう

ノード定義イメージ

ノード定義はインポートすることができます。
今回は、こちらのサイトからインポートさせていただき、さらにカスタマイズを加えました。

画像の顔認識!Node-REDとVisual Recognitionでリア充判定をしてみた!

ノード定義の全容はこんな感じです。
スクリーンショット 2017-03-26 21.47.32.png

アプリに対してHTTPリクエストがあった際、画像のURLが渡された場合はVisual Recognitionにリクエストを行い、そうでない場合はHTMLのみを返しています。

Visual Recognition用のノードはBluemixにあらかじめ用意されており、API KeyとAPIの種類、言語を入力するだけで使えます。
スクリーンショット 2017-03-26 21.30.08.png

ちなみに、Classify an image APIには、HTTP GETと、POSTのインターフェイスがあります。
前者はURLを、後者は画像ファイルを引数にとります。
詳細は公式ドキュメントを参照してください。

HTML

Node-REDではテンプレートエンジンMustacheを使うことができます。
scriptを埋め込まなくても条件分岐ができたり、配列を取り出してlistが作れたりと、便利ですね。

上図の"html"と命名したtemplateという種類のノードに、以下のHTMLソースを記述しています。


<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>Watson Visual Recognition Classifier</title>
        <!-- Bootswatch(Free themes for Bootstrap) -->
        <link rel="stylesheet" href="//bootswatch.com/cerulean/bootstrap.min.css">
        <style>
            body {
                padding-bottom: 2rem;
            }
            #img-result {
                width: 75%;
                height: auto;
            }
        </style>
    </head>

    <body>
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <a class="navbar-brand" href="{{req._parsedUrl.pathname}}">Watson Visual Recognition Classifier</a>
                </div>
            </div>
        </nav>
        <div class="container">
            <div class="row">
                <div class="col-xs-1 bg-left"></div>
                <div class="col-xs-10 bg-main">
                    <form action="{{req._parsedUrl.pathname}}" method="GET">
                        <div class="form-group row">
                            <p>画像のURLを入力して「送信」を押してください。</p>
                            <label for="imageUrl" class="col-form-label">URL</label>
                            <br>
                            <input type="url" class="form-control" name="imageUrl" required>
                            <br>
                            <button class="btn btn-primary">送信</button>
                        </div>
                    </form>
                </div>
                <div class="col-xs-1 bg-right"></div>
            </div>
            {{#payload.imageSrc}}
            <!-- See: Mustache リクエストのimageSrcがNULLでない場合のみ表示される -->
            <div class="row">
                <div class="col-xs-1 bg-left"></div>
                <div class="col-xs-10 bg-main">
                    <ul class="list-group">
                        <li class="list-group-item active">解析結果</li>
                        {{#payload.classes}}
                            <li class="list-group-item"><div class="text-primary">クラス:{{class}}</div> 確信度:{{score}}{{#type_hierarchy}} 分類構造:{{type_hierarchy}}{{/type_hierarchy}}</li>
                        {{/payload.classes}}
                    </ul>
                    <img src="{{payload.imageSrc}}" id="img-result" class="img-responsive">
                </div>
                <div class="col-xs-1 bg-right"></div>
            </div>
            {{/payload.imageSrc}}
        </div>
    <body>

    <script src="//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7" crossorigin="anonymous"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js" integrity="sha384-BLiI7JTZm+JWlgKa0M0kGRpJbF2J8q+qreVrKBC47e3K6BW78kGLrCkeRX6I9RoK" crossorigin="anonymous"></script>
</html>

Classify an imageのレスポンスサンプル


{ "custom_classes": 0,
  "images": [
    { "classifiers": [
        { "classes": [
            { "class": "ベン油", "score": 0.751, "type_hierarchy": "/自然/ベン油" },
            { "class": "自然", "score": 0.917 },
            { "class": "丘", "score": 0.865, "type_hierarchy": "/自然/丘" },
            { "class": "稜線", "score": 0.554, "type_hierarchy": "/自然/稜線" } ],
        "classifier_id": "default",
        "name": "default" } ],
      "resolved_url": "https://example.com/aaaaa.jpg",
      "source_url": "https://example.com/aaaaa.jpg" } ],
  "images_processed": 1 }

JavaScript

上図の"class取り出し"と命名したfunctionという種類のノードに、以下のJavaScriptを記述しています。
上記レスポンスの"classes"配下の配列と、"resolved_url"の文字列を"html"ノードに渡してレンダリングします。


var classes = [];
msg.payload = {};
// VRのレスポンスからclassの配列を取り出す
classes = msg.result.images[0].classifiers[0].classes;
msg.payload.classes = classes;
msg.payload.imageSrc = msg.result.images[0].resolved_url;
return msg;

インポート用ノード定義

以下をコピーしてBluemix上のNode-REDにインポートすれば、同様のアプリを構築できます。
Visual RecognitionのAPIキーを設定することをお忘れなく。


[{"id":"b180e849.1c2848","type":"http in","z":"5d709634.480f18","name":"","url":"/vr-classify","method":"get","swaggerDoc":"","x":85,"y":30,"wires":[["80b1c068.51aa8","1592f1b.854650e"]]},{"id":"804eb7e9.388dd8","type":"visual-recognition-v3","z":"5d709634.480f18","name":"visual recognition","apikey":"","image-feature":"classifyImage","lang":"ja","x":311.8834228515625,"y":312.8118591308594,"wires":[["f54469b8.d8afe8","d0a7136.7578cf"]]},{"id":"f54469b8.d8afe8","type":"debug","z":"5d709634.480f18","name":"","active":true,"console":"false","complete":"result","x":496.687744140625,"y":281.41363525390625,"wires":[]},{"id":"b0b63e79.4ba05","type":"function","z":"5d709634.480f18","name":"class取り出し","func":"var classes = [];\nmsg.payload = {};\n// VRのレスポンスからclassの配列を取り出す\nclasses = msg.result.images[0].classifiers[0].classes;\nmsg.payload.classes = classes;\nmsg.payload.imageSrc = msg.result.images[0].resolved_url;\nreturn msg;","outputs":1,"noerr":0,"x":728,"y":474,"wires":[["c7c46c18.f1771","34891497.54e25c"]]},{"id":"dd318243.dc0ba","type":"http response","z":"5d709634.480f18","name":"","x":890.2205810546875,"y":193.25299072265625,"wires":[]},{"id":"80b1c068.51aa8","type":"debug","z":"5d709634.480f18","name":"","active":true,"console":"false","complete":"payload","x":322,"y":30,"wires":[]},{"id":"376b6ed7.9a8352","type":"change","z":"5d709634.480f18","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.imageUrl","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":232,"y":223,"wires":[["804eb7e9.388dd8","7f02a16d.b6f71"]]},{"id":"c7c46c18.f1771","type":"template","z":"5d709634.480f18","name":"html","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<!DOCTYPE html>\n<html lang=\"ja\">\n    <head>\n        <meta charset=\"utf-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n        <meta http-equiv=\"x-ua-compatible\" content=\"ie=edge\">\n        <title>Watson Visual Recognition Classifier</title>\n        <!-- Bootswatch(Free themes for Bootstrap) -->\n        <link rel=\"stylesheet\" href=\"//bootswatch.com/cerulean/bootstrap.min.css\">\n        <style>\n            body {\n                padding-bottom: 2rem;\n            }\n            #img-result {\n                width: 75%;\n                height: auto;\n            }\n        </style>\n    </head>\n    \n    <body>\n        <nav class=\"navbar navbar-default\">\n            <div class=\"container-fluid\">\n                <div class=\"navbar-header\">\n                    <a class=\"navbar-brand\" href=\"{{req._parsedUrl.pathname}}\">Watson Visual Recognition Classifier</a>\n                </div>\n            </div>\n        </nav>\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col-xs-1 bg-left\"></div>\n                <div class=\"col-xs-10 bg-main\">\n                    <form action=\"{{req._parsedUrl.pathname}}\" method=\"GET\">\n                        <div class=\"form-group row\">\n                            <p>画像のURLを入力して「送信」を押してください。</p>\n                            <label for=\"imageUrl\" class=\"col-form-label\">URL</label>\n                            <br>\n                            <input type=\"url\" class=\"form-control\" name=\"imageUrl\" required>\n                            <br>\n                            <button class=\"btn btn-primary\">送信</button>\n                        </div>\n                    </form>\n                </div>\n                <div class=\"col-xs-1 bg-right\"></div>\n            </div>\n            {{#payload.imageSrc}}\n            <!-- See: Mustache リクエストのimageSrcがNULLでない場合のみ表示される -->\n            <div class=\"row\">\n                <div class=\"col-xs-1 bg-left\"></div>\n                <div class=\"col-xs-10 bg-main\">\n                    <ul class=\"list-group\">\n                    \t<li class=\"list-group-item active\">解析結果</li>\n                        {{#payload.classes}}\n                        \t<li class=\"list-group-item\"><div class=\"text-primary\">クラス:{{class}}</div> 確信度:{{score}}{{#type_hierarchy}} 分類構造:{{type_hierarchy}}{{/type_hierarchy}}</li>\n                        {{/payload.classes}}\n                    </ul>\n                    <img src=\"{{payload.imageSrc}}\" id=\"img-result\" class=\"img-responsive\">\n                </div>\n                <div class=\"col-xs-1 bg-right\"></div>\n            </div>\n            {{/payload.imageSrc}}\n        </div>\n    <body>\n        \n    <script src=\"//ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js\" integrity=\"sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7\" crossorigin=\"anonymous\"></script>\n    <script src=\"//cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js\" integrity=\"sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8\" crossorigin=\"anonymous\"></script>\n    <script src=\"//maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/js/bootstrap.min.js\" integrity=\"sha384-BLiI7JTZm+JWlgKa0M0kGRpJbF2J8q+qreVrKBC47e3K6BW78kGLrCkeRX6I9RoK\" crossorigin=\"anonymous\"></script>\n</html>","x":762,"y":111,"wires":[["dd318243.dc0ba"]]},{"id":"1592f1b.854650e","type":"switch","z":"5d709634.480f18","name":"画像URLチェック","property":"payload.imageUrl","propertyType":"msg","rules":[{"t":"null"},{"t":"else"}],"checkall":"true","outputs":2,"x":175,"y":116,"wires":[["c7c46c18.f1771"],["376b6ed7.9a8352"]]},{"id":"7f02a16d.b6f71","type":"debug","z":"5d709634.480f18","name":"","active":true,"console":"false","complete":"payload","x":463,"y":223,"wires":[]},{"id":"34891497.54e25c","type":"debug","z":"5d709634.480f18","name":"","active":true,"console":"false","complete":"payload","x":799,"y":538,"wires":[]},{"id":"d0a7136.7578cf","type":"switch","z":"5d709634.480f18","name":"レスポンスチェック","property":"result.images[0].classifiers[0].classes.length","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","outputs":2,"x":432,"y":406,"wires":[["c7c46c18.f1771"],["b0b63e79.4ba05"]]},{"id":"ba454e74.81fd2","type":"comment","z":"5d709634.480f18","name":"解析NGの場合","info":"","x":629,"y":355,"wires":[]},{"id":"d1cad779.c8e9b8","type":"comment","z":"5d709634.480f18","name":"解析OKの場合","info":"","x":540,"y":457,"wires":[]},{"id":"54535598.a2cd2c","type":"comment","z":"5d709634.480f18","name":"初期表示の場合:画像URL=NULL","info":"","x":415,"y":89,"wires":[]},{"id":"fad119bd.6977f8","type":"comment","z":"5d709634.480f18","name":"解析実行の場合:画像URL≠NULL","info":"","x":313,"y":181,"wires":[]}]

8
7
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
8
7