まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:curl

IBM Bluemix を通じて提供されている Watson API の1つ Visual Recognition(画像認識) API に「類似イメージ検索」というベータ版の新機能が追加されました:
https://new-console.ng.bluemix.net/catalog/services/visual-recognition/


2016101101


この API はその名の通り、「ある画像に似た画像を(あらかじめ登録しておいた画像群の中から)探す」というものです。個々の REST API としては画像の登録や削除、そして類似検索といった機能が用意されており、それらを組み合わせて実装することになります。また他の Watson API 同様、検索した結果に対してはスコアという検索結果に対する自信の数値根拠が合わせて返される、という特徴があります。


またこの類似画像検索 API のデモサイトも公開されています。選択した画像に似た画像が表示される、というものです。一度使ってみると、どういったことが可能になるのか、というイメージが付きやすくなると思います:
https://similarity-search-demo.mybluemix.net/

2016101201


このブログは公私混同を売りにしている(笑)こともあるので、実際に自分のサービスを使って試してみた様子を以下に紹介します。自分の場合は(お約束ですが)マンホールマップの画像を使わせていただきました。複数の種類のマンホール画像をあらかじめ登録しておいて、後から登録していないマンホール画像を指定して類似検索した時に同じ絵柄のマンホールを見つけることができるかどうか!? という挑戦です。

実はこの挑戦はかなりハードルの高い挑戦でもあります。なぜなら「単にマンホールとして認識」されてしまうと、登録画像は全てマンホールなので「似た絵柄のマンホール」を探すことができないと思われるからです。またマンホールの外側にある部分の類似性は無視してほしいわけですが、その辺りの所、ワトソンはどうなのよ!? ということを確認するための実験的要素の強い作業です。

なおこちらのページのマンホール画像はあらかじめ登録しておく画像の1つとしています:
2016101202


詳しくは API Reference を参照いただきたいのですが、あらかじめ画像を登録する際には画像データに加えて、JSON 形式のメタデータファイルを用意する必要があります。このメタデータに登録された内容は検索結果に含まれて返されることになるものです。そのため ID や作成日時、作成ユーザーといった情報をこのメタデータに加えておくと、画像検索した結果から作成ユーザー情報まで取り出す、ということも可能になります。

今回は登録するデータとしてこの画像と、



画像に加えて、こんな内容のメタデータを用意しておきました:
{
  "id":120002,
  "username":"morimo_t",
  "created":"2010-08-17 22:03:26",
  "text":"川崎市の色蓋。よく通る道路だったのにはじめて気がつきました。",
  "address":"神奈川県川崎市幸区堀川町",
  "lat":35.53229904174805,
  "lng":139.697998046875,
  "nice":5
}

↑細かい説明は省きますが、画像検索した結果にこの画像が含まれていた場合に、これらのメタデータと合わせて結果が取得できる、というためのものです(なのでメタデータが不要であれば空オブジェクトでも構いません)。このように画像とメタデータの組み合わせを登録する画像全てに対して用意しておきます。

実際に画像を登録する前にいくつか準備が必要です。まず当たりまえですが、IBM Bluemix にログインし、Visual Recognition サービスを追加しておきます:
2016101203


サービス追加後、「サービス資格情報」の「資格情報の表示」を選択して、認証情報の "api_key" の値を確認しておいてください(この後で使います):
2016101204


ここからは curl を使って REST API を実行するので、Linux か Mac のユーザーはターミナルを開いてください。Windows ユーザーの場合、curl は別途インストールする必要があります。以下のサイトから環境にあった Windows 用 curl をダウンロードし、インストールしてください:
https://curl.haxx.se/download.html

2016101205


curl コマンドを使う準備ができたら実際に REST API を実行してみましょう。画像を登録するにはまず入れ物(「コレクション」といいます)を用意します。ちなみに類似画像検索はこのコレクション単位で実行して、回答を取得することになります。上記デモサイトのようにショッピング用の商品画像から類似画像を探す、という使い方であれば商品の画像を登録するコレクションを作って、そこに商品画像をまとめて登録して検索する、という使い方をすることになります。

今回は「類似マンホール検索」が目的なので、マンホール画像だけを登録するコレクションをあらかじめ作っておくことにします(そしてそこにはマンホール画像以外は登録しないようにします)。新たにコレクションを作成するにはこの REST API を実行します:
# curl -X POST -F "name=XXXXXXXX" "https://gateway-a.watsonplatform.net/visual-recgnition/api/v3/collections?api_key=(apy_key)&version=2016-05-20"

メソッドは POST で、マルチパートフォームデータとして name パラメータでコレクションの名前(上の例では "XXXXXXXX")を指定します。また URL パラメータで Visual Recognition サービスを参照して確認した api_key と、version("2016-05-20")を指定して実行します。

この実行が成功すると、指定した名前のコレクションが作成され、以下のような実行結果 JSON が返ってきます:
{
  "collection_id": "XXXXXXXX_xxxxxx",
  "name": "XXXXXXXX",
  "status": "available",
  "created": "2016-10-11T05:58:47.129Z",
  "images": 0,
  "capacity": 1000000
}

"name" に指定したコレクション名が入っていて、その名称に "_xxxxxx" という形式が付いた "collection_id" が得られているはずです。この後からはこの collection_id を使って API を実行するので、この値をメモしておきましょう。またこのコレクションには現在 0 枚の画像が登録されており("images" の値)、最大で 1000000 枚の画像が登録できる、という状態になっているようです。

では入れ物ができたので、この入れ物に画像を登録します。画像ファイル名が image.png、対応するメタファイル名が image.json である場合、以下の API を実行します:
# curl -X POST -F "image_file=@image.png" -F "metadata=@image.json" "https://gateway-a.watsonplatform.net/visual-recgnition/api/v3/collections/XXXXXXXX_xxxxxx/images?api_key=(apy_key)&version=2016-05-20"

マルチパートフォームデータで画像ファイルとメタデータファイルを POST メソッドで送り、URL パラメータの中で collection_id を指定しています。この API の実行が成功すると、このような JSON が返ってきます:
{
  "images": [
    {
      "image_id": "mmmmmm",
      "image_file": "image.png",
      "created": "2016-10-11T16:12:16.435Z",
      "metadata": {
        "id":120002,
        "username":"morimo_t",
        "created":"2010-08-17 22:03:26",
        "text":"川崎市の色蓋。よく通る道路だったのにはじめて気がつきました。",
        "address":"神奈川県川崎市幸区堀川町",
        "lat":35.53229904174805,
        "lng":139.697998046875,
        "nice":5
      }
    }
  ],
  "images_processed": 1
}

実行結果として image_id などが生成されています。また metadata の中身は指定してメタデータファイルの中身になっているはずです。

この時点でコレクションの状態を確認してみることにします。コレクションの状態を確認するにはこの APIを実行します:
# curl "https://gateway-a.watsonplatform.net/visual-recgnition/api/v3/collections/XXXXXXXX_xxxxxx?api_key=(apy_key)&version=2016-05-20"

実行が成功すると、以下のような JSON が返ってきます。画像が1つ登録されたので "images" の値が 0 から 1 に変化しています:
{
  "collection_id": "XXXXXXXX_xxxxxx",
  "name": "XXXXXXXX",
  "status": "available",
  "created": "2016-10-11T05:58:47.129Z",
  "images": 1,
  "capacity": 1000000
}

こんな調子であらかじめ登録しておく画像(=検索対象となる画像)をすべて登録しておきます。なお、コレクションに追加できる画像は(API Reference によると) 2MB 以内にするべきとのこと。また1つのコレクションに 1000000 (100万)画像まで登録できることになっていますが、1つのファイルを登録するのに仮に1秒かかるとすると、100 万画像で 100 万秒、つまりノンストップで行っても約 11.5 日かかる計算になります。派手に使う場合は準備に2週間~程度かかる、という心づもりが必要になりますね。

全ての画像が登録されたら、最後に類似画像認識を実行してみます。今回はこの画像を検索してみました(この画像はコレクションには登録していません):



↑上記で紹介した登録画像と同じ絵柄のマンホール画像です。この画像を search.png として保存し、以下の REST API を実行します:
# curl -X POST -F "image_file=@search.png" "https://gateway-a.watsonplatform.net/visual-recgnition/api/v3/collections/XXXXXXXX_xxxxxx/find_similar?api_key=(apy_key)&version=2016-05-20&limit=5"

collection_id を指定して、マルチパートフォームデータが画像ファイルを POST 送信しています。またこの例では URL パラメータに limit=5 を指定して、類似度の上位5つまでを取得するように指示しています。

この API が正しく実行されると、以下のような結果が取得できます:
{
  "image_file": "5137462154683827112.png",
  "similar_images": [
    {
      "image_id": "2f98c6",
      "image_file": "120002.png",
      "score": 0.7548828,
      "created": "2016-10-11T15:22:25.824Z",
      "metadata": {
        "address": "神奈川県川崎市幸区堀川町",
        "created": "2010-08-17 22:03:26",
        "id": 120002,
        "lat": 35.53229904174805,
        "lng": 139.697998046875,
        "nice": 5,
        "text": "川崎市の色蓋。よく通る道路だったのにはじめて気がつきました。€‚",
        "username": "morimo_t"
      }
    },
    {
      "image_id": "d20e86",
      "image_file": "804002.png",
      "score": 0.7416992,
      "created": "2016-10-11T15:59:02.442Z",
      "metadata": {
        "id": 804002,
           :
           :
      }
    },
    {
      "image_id": "b6f1e7",
      "image_file": "1594034.png",
      "score": 0.74121094,
      "created": "2016-10-11T15:17:22.733Z",
      "metadata": {
        "id": 1.594034e+06,
           :
           :
      }
    },
    {
      "image_id": "f0bb1f",
      "image_file": "1888484723171738767.png",
      "score": 0.73828125,
      "created": "2016-10-11T15:31:14.768Z",
      "metadata": {
        "id": 1.888484723171739e+18,
           :
           :
      }
    },
    {
      "image_id": "9f72ed",
      "image_file": "90003.png",
      "score": 0.72998047,
      "created": "2016-10-11T15:31:21.935Z",
      "metadata": {
        "id": 90003,
           :
           :
      }
    }
  ],
  "images_processed": 1
}

この形式だとわかりにくいので、実行結果を表&画像にしてみました:
#画像スコア
10.7548828
20.7416992
30.74121094
40.73828125
50.72998047


なんとも評価の難しい結果になりました。まず1位は期待通りに、事前に登録しておいた同じ絵柄のマンホールを取得することができました!これは素晴らしい!! そして問題は2位以下です。これらはマンホールの絵柄としては明らかに異なり、「類似画像」としてはふさわしくないのですが、そのスコアが1位の画像と大差ない、という結果になりました。

まず今回登録した画像には正解(というか、同じ絵柄のマンホール)が1つしかないので、それが1位になってることは評価に値すると思っています。ただ間違いである2位との差がほとんどないというのも実は厄介で、「似た画像はこの1つだけです」という判断をさせたり、正解が1個もないような検索時に「似た画像はありません」という判断をするのが難しくなってしまうのでした。うーむ・・・

一方で、今回は 500 枚のマンホール画像を事前に登録したのですが、この枚数が少なすぎた可能性はありますね。

とはいえ、最初に触れているように、今回の検証は「マンホールの絵柄の類似性を確認できるか?」というかなりハードルの高い検証をしたつもりでしたが、ある程度は判断できているようにも見えます。今後は登録画像枚数も増やした上で再度色んなパターンで検証することになると思ってます。



なお、この類似画像検索 API の詳細については API Reference を参照ください:
http://www.ibm.com/watson/developercloud/visual-recognition/api/v3/#collections


Cloudant のような NoSQL データベースの場合、バックアップは一般的には複製機能によって実現するケースが多いのかもしれません。要は生きたデータベースを2系統以上用意して、生きたデータベースにバックアップする、という考え方です。これだとメインDBに障害があってもすぐに切り替えて稼働ができます。

ただ、例えばデータベースの引越しが目的の場合や、あるタイミングでのスナップショットを取るような場合などでは、一旦データベースのダンプを取得して、引越先でリストアしたい、というケースもあります。このようなケースでは生きたデータベースに複製することが必ずしも正しい回答にはなりません。


そういった場合のダンプは以下のコマンドで取得することができます:
$ curl -X GET https://(username):(password)@(hostname)/(dbname)/_all_docs?include_docs=true > file.dump

上記例では file.dump というファイル名を指定してダンプ結果を取得するように指示しています。ダンプというか、データベースの _all_docs(全ドキュメント) に include_docs_true(中身ごと取得)オプションを付けて GET する、というよく考えたらごく普通のコマンドをそのまま実行しています。

取得したダンプファイル(file.dump)を使ってリストアする場合はちょっと準備が必要です。まず上記コマンドで取得したダンプファイルをテキストエディタで開き、1行目の "rows" を "docs" に書き換えて保存します:
{"total_rows":100,"offset":0,"rows":[ 
  :
  :

  ↓ "rows" を "docs" に書き換えて保存
{"total_rows":100,"offset":0,"docs":[ : :

編集後に次のコマンドを実行します:
$ curl -d @file.dump -H "Content-Type: application/json" -X POST https://(username):(password)@(hostname)/(dbname)/_bulk_docs

こちらはデータベースに対して、中身を JSON ファイルで指定して POST しているだけです。

これで手動でバックアップができるので、後はこれを cron などで自動化したり、取得したダンプファイルを外部のオブジェクトストレージとかに転送する仕組みまで作ってしまえば自動バックアップが実現できそうです。

(2016/Jul/30 追記)
上記方法でもリストアできますが、元のデータベース内と比べて JSON ドキュメントのフォーマットが変わってしまいます。フォーマットを変えずにリストアする場合の方法は別のエントリで紹介しています:
Cloudant のバックアップデータをリストアする





 

IBM Bluemix はオープンソース PaaS である Cloud Foundry をベースに作られています。ということは Cloud  Foundry の API を Bluemix に対して使う、ということもできるわけです。

実際に試してみた様子を以下に記載します。なお、実行にあたっては Bluemix のアカウントと、実行環境として curl を利用します。以下の例では CentOS 6 上の curl を使っていますが、他のプラットフォームでもほぼ同様に使えるはずです。また以下の例では北米データセンターを対象に API を実行しますが、他のデータセンターを使う場合は、"ng" の部分を "eu-gb"(英国)か "au-syd"(オーストラリア)といった具合に書き換えて実行してください:


(1) アクセストークンの取得

具体的な API の利用の前に OAuth のアクセストークンを取得しておく必要があります(このアクセストークンを使って個別の API を実行します)。

以下の curl コマンドを実行します(青字部分がレスポンスです):
# curl -XPOST -H"Application/json" -u "cf:" --data "username=username@xxxx.com&password=P@ssw0rd&grant_type=password&scope=" https://login.ng.bluemix.net/UAALoginServerWAR/oauth/token

{
 "access_token":"(accessToken)", 
"token_type":"bearer",
"refresh_token":"*******", "expires_in":43199, "scope":"cloud_controller.read password.write cloud_controller.write openid", "jti":"********" }

"--data" に続く部分にログイン情報を含めています。username は Bluemix のログインIDです(上の例では username@xxxx.com を指定しています)。また password にパスワードを指定しています(上の例では P@ssw0rd を指定します)。それぞれ自分の情報に書き換えて実行してください。すると青字のような JSON テキストが返ってきます。この中の access_token の値(青太字の部分)がアクセストークンです。実際にはかなり長い文字列になっているはずですが、これを使って (2) 以降で API を実行します。


(2) API の実行

アクセストークンが取得できたら、その情報を使って Cloud Foundry API を実行します。Cloud Foundry API そのもののリファレンスはこちらを参照ください:
https://apidocs.cloudfoundry.org/231/

例えば「現在利用中のアプリケーションランタイムの一覧を取得」するには、この API を使います:
https://apidocs.cloudfoundry.org/231/apps/list_all_apps.html

具体的には以下の様な curl コマンドを実行します。ヘッダ情報(Authorization ヘッダ)に上記で取得したアクセストークンを指定しています:
# curl -H"Authorization: bearer (accessToken)" https://api.ng.bluemix.net/v2/apps

{
 "total_results": 15,
 "total_pages": 1,
   :
   :
 "resources":[
  {
   "metadata": {
    "guid": "(appGuid1)",
    "url": "/v2/apps/(appGuid1)",
    "created_at": "2015-03-09T02:49:49Z",
    "updated_at": "2016-02-20T06:23:39Z"
   },
    :
    :
  },
  {
   "meatadata": {
    "guid": "(appGuid2)",
     :
     :
  },
     :
     :
  }
] }

"resources" 内に配列形式でランタイムアプリケーションの情報が含まれているはずです。各ランタイムアプリケーションにはアプリケーション GUID と呼ばれる ID が割り振られており、その値は各ランタイムアプリケーション内の metadata.guid として確認できます。

では、今度はこのアプリケーション GUID を使って、このランタイムアプリケーションの環境変数情報を API で確認してみましょう。具体的にはこちらの API を利用します:
https://apidocs.cloudfoundry.org/231/apps/get_the_env_for_an_app.html
# curl -H"Authorization: bearer (accessToken)" https://api.ng.bluemix.net/v2/apps/(appGuid1)/env

{
 "staging_env_json": {
  "BLUEMIX_REGION": "ibm:yp:us-south"
 },
 "running_env_json": {
  "BLUEMIX_REGION": "ibm:yp:us-south"
 },
 "environment_json": {
 },
 "system_env_json": {
  "VCAP_SERVICES": {
   "relationship_extraction": [
    {
     "name": "Relationship Extraction-dc",
     "label": "relationship_extraction",
     "tags": [
      "ibm_beta",
      "watson",
      "ibm_created",
      "ibm_dedicated_public"
     ],
     "plan": "relationship_extraction_free_plan",
     "credentials": {
      "url": "https://gateway.watsonplatform.net/relationship-extraction-beta/api",
      "sids": [
       {
        "sid": "ie-es-news",
        "description": "information extraction from Spanish news"
       },
       {
        "sid": "ie-en-news",
        "description": "information extraction from English news"
       }
      ],
      "username": "(username)",
      "password": "(password)"
     }
    }
   ],
    :
    :
    :
   ]
  }
 },
 "application_env_json": {
   :
   :
 }
}

(appGuid1) に上記で確認したアプリケーション GUID を指定して実行します。すると上記のようなフォーマットで、このランタイムアプリケーションの環境変数情報が(VCAP_SERVICES もそれ以外も)まとめて取得できます。特に VCAP_SERVICES の credentials(上記の青太字部分)にはバインドしているサービスの接続情報が含まれており、その値を外部から API で確認できたことになります。


curl(HTTP) だけで Cloud Foundry API を Bluemix に対して使うことができる、ということが確認できました。これで複数のランタイムアプリケーションのステータスを確認する、といった管理用のアプリケーションも作れそうです。もちろん管理用途だけでなく、Bluemix のダッシュボードで実現しているようなアプリケーションを独自に作る、といったことも含めてできそうです。

 

git の submodule コマンドは、バージョンが 1.7.10 以前のものだと http ではリクエストができない、という問題があるようです:
HTTPS cloning erros

一方、現在の CentOS 6 で普通に(デフォルトで) git を導入すると、そのバージョンは 1.7.1 でした。つまり CentOS 6 で普通に git をインストールすると、上記の問題に該当するバージョンが導入される、ということになります:
# git --version
git version 1.7.1

これが問題になるケースもあると思われます。というわけで、git のバージョンを手動で上げる手順を紹介します。以下の例は CentOS 6(64bit) 向けのバージョン 1.7.11.3 を導入する場合ですが、こんな感じ:
# curl -LO http://pkgs.repoforge.org/git/git-1.7.11.3-1.el6.rfx.x86_64.rpm
# curl -LO http://pkgs.repoforge.org/git/perl-Git-1.7.11.3-1.el6.rfx.x86_64.rpm
# yum erase perl-Git
# yum localinstall git-1.7.11.3-1.el6.rfx.x86_64.rpm perl-Git-1.7.11.3-1.el6.rfx.x86_64.rpm

該当の2モジュールを curl コマンドで取得し、現在のものを消してから、yum localinstall で取得したモジュールを使ってインストールしています。

最後に git のバージョンを確認します:
# git --version
git version 1.7.11.3

成功!
 

このページのトップヘ