IBM Watson Developer Cloud Visual Recognition で物体認識させる

画像などの中から物体を検出することを物体認識と言います。
今回は IBM Watson Developer Cloud の提供するサービスの一つ Visual Recognition を用いて、物体認識を行います。

※ IBM Bluemixの登録済みである必要があります。

# API用のユーザ名とパスワードを取得

Visual Recognition の Web APIを使うためのユーザ名とパスワードを取得する必要があります。

IBM Bluemixの管理画面からアプリケーションを作成し、そのアプリケーションにサービスを追加する形でVisual Recognitionを追加します。

追加したのち、そのサービスの "資格情報の表示" をクリックするとユーザ名とパスワードが記載されています。

![スクリーンショット 2015-04-12 22.46.03.png](https://qiita-image-store.s3.amazonaws.com/0/36261/a1367c55-8e60-202c-00f1-aecf3295e174.png)

# ラベルを取得する

物体認識の結果はラベルとラベルのスコアが組みで返されます。
その際に使用されるラベルを取得します。

```py3

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""IBM Watson Developer Cloud の Visual Recognitionでラベル情報を取得
"""
import sys
import json
import requests
from pit import Pit

setting = Pit.get('iwdcat',
                  {'require': {'username': '',
                               'password': '',
                               }})

auth_token = setting['username'], setting['password']
url = 'https://gateway.watsonplatform.net/visual-recognition-beta/api/v1/tag/labels'
res = requests.get(url, auth=auth_token, headers={'content-type': 'application/json'})
if res.status_code == requests.codes.ok:
    labels = json.loads(res.text)
    print('label groups({}): {}'.format(len(labels['label_groups']), labels['label_groups']))
    print()
    print('labels({}): {}'.format(len(labels['labels']), labels['labels']))
else:  # error
    print('stauts_code: {} (reason: {})'.format(res.status_code, res.reason))
    sys.exit(1)
```

結果はJSONが返されます。label_groupsはラベルグループのリスト、labelsはラベルのリストです。

# 画像を解析する

物体認識のAPIには画像をマルチパートで送信する必要があります。
画像はpng, jpgか、zip圧縮されていても良いようです。
以下は1枚のpng画像を送信する例です。

```py3

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""IBM Watson Developer Cloud の Visual Recognitionで物体認識を行う
"""
import os
import sys
import json
import requests
from pit import Pit

setting = Pit.get('iwdcat',
                  {'require': {'username': '',
                               'password': '',
                               }})

auth_token = setting['username'], setting['password']
url = 'https://gateway.watsonplatform.net/visual-recognition-beta/api/v1/tag/recognize'

filepath = 'var/images/first/2015-04-12-11.47.01.png'  # path to image file
filename = os.path.basename(filepath)

res = requests.post(
    url, auth=auth_token,
    files={
        'imgFile': (filename, open(filepath, 'rb')),
        }
    )
if res.status_code == requests.codes.ok:
    data = json.loads(res.text)
    for img in data['images']:
        print('{} - {}'.format(img['image_id'], img['image_name']))
        for label in img['labels']:
            print('    {:30}: {}'.format(label['label_name'], label['label_score']))

else:  # error
    print('stauts_code: {} (reason: {})'.format(res.status_code, res.reason))
    sys.exit(1)

```

1枚の画像を解析します。
出力結果は以下のようになりました。

```shell-session

$ python analyze_image.py
0 - 2015-04-12-11.47.01.png
    Outdoors                      : 0.714211
    Nature Scene                  : 0.671271
    Winter Scene                  : 0.669832
    Vertebrate                    : 0.635903
    Boat                          : 0.61398
    Animal                        : 0.610709
    Water Vehicle                 : 0.607173
    Placental Mammal              : 0.580503
    Snow Scene                    : 0.571422
    Fabric                        : 0.563129
    Gray                          : 0.56078
    Water Sport                   : 0.555034
    Person                        : 0.533461
    Mammal                        : 0.515725
    Surface Water Sport           : 0.511447
```

返却されるデータは次のようなデータです。

```py3

{'images': [{'image_id': '0', 'labels': [{'label_score': '0.714211', 'label_name': 'Outdoors'}, {'label_score': '0.671271', 'label_name': 'Nature Scene'}, {'label_score': '0.669832', 'label_name': 'Winter Scene'}, {'label_score': '0.635903', 'label_name': 'Vertebrate'}, {'label_score': '0.61398', 'label_name': 'Boat'}, {'label_score': '0.610709', 'label_name': 'Animal'}, {'label_score': '0.607173', 'label_name': 'Water Vehicle'}, {'label_score': '0.580503', 'label_name': 'Placental Mammal'}, {'label_score': '0.571422', 'label_name': 'Snow Scene'}, {'label_score': '0.563129', 'label_name': 'Fabric'}, {'label_score': '0.56078', 'label_name': 'Gray'}, {'label_score': '0.555034', 'label_name': 'Water Sport'}, {'label_score': '0.533461', 'label_name': 'Person'}, {'label_score': '0.515725', 'label_name': 'Mammal'}, {'label_score': '0.511447', 'label_name': 'Surface Water Sport'}], 'image_name': '2015-04-12-11.47.01.png'}]}
```

# 一度のリクエストで複数の画像を送信して一括で解析する

マルチパートで送信するファイルを増やせば、一度のリクエストで複数のファイルを解析にかけることができます。

```
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""IBM Watson Developer Cloud の Visual Recognitionで物体認識を行う

1リクエストに3ファイルを含める
"""
import os
import sys
import json
import requests
from pit import Pit

setting = Pit.get('iwdcat',
                  {'require': {'username': '',
                               'password': '',
                               }})

auth_token = setting['username'], setting['password']
url = 'https://gateway.watsonplatform.net/visual-recognition-beta/api/v1/tag/recognize'

filepaths = [
    'var/images/first/2015-04-12-11.47.01.png',
    'var/images/first/2015-04-12-11.44.42.png',
    'var/images/first/2015-04-12-11.46.11.png',
    ]
files = dict((os.path.basename(filepath), (os.path.basename(filepath), open(filepath, 'rb'))) for filepath in filepaths)

res = requests.post(
    url, auth=auth_token,
    files=files,
    )

for key, (filename, fp) in files.items():
    fp.close()

if res.status_code == requests.codes.ok:
    data = json.loads(res.text)
    for img in data['images']:
        print('{} - {}'.format(img['image_id'], img['image_name']))
        for label in img['labels']:
            print('    {:30}: {}'.format(label['label_name'], label['label_score']))

else:  # error
    print('stauts_code: {} (reason: {})'.format(res.status_code, res.reason))
    sys.exit(1)
```

返されるJSONは'images' キーの要素がリストになっており、そこに入力した画像数の要素が入っています。
実行結果は以下のとおりです。

```sh

$ python analyze_image_multi.py
0 - 2015-04-12-11.44.42.png
    Gray                          : 0.735805
    Winter Scene                  : 0.7123
    Nature Scene                  : 0.674336
    Water Scene                   : 0.668881
    Outdoors                      : 0.658805
    Natural Activity              : 0.643865
    Vertebrate                    : 0.603751
    Climbing                      : 0.566247
    Animal                        : 0.537788
    Mammal                        : 0.518001
1 - 2015-04-12-11.46.11.png
    Gray                          : 0.719819
    Vertebrate                    : 0.692607
    Animal                        : 0.690942
    Winter Scene                  : 0.683918
    Mammal                        : 0.669149
    Snow Scene                    : 0.664266
    Placental Mammal              : 0.663866
    Outdoors                      : 0.66335
    Nature Scene                  : 0.656991
    Climbing                      : 0.645557
    Person                        : 0.557965
    Person View                   : 0.528335
2 - 2015-04-12-11.47.01.png
    Outdoors                      : 0.714211
    Nature Scene                  : 0.671271
    Winter Scene                  : 0.669832
    Vertebrate                    : 0.635903
    Boat                          : 0.61398
    Animal                        : 0.610709
    Water Vehicle                 : 0.607173
    Placental Mammal              : 0.580503
    Snow Scene                    : 0.571422
    Fabric                        : 0.563129
    Gray                          : 0.56078
    Water Sport                   : 0.555034
    Person                        : 0.533461
    Mammal                        : 0.515725
    Surface Water Sport           : 0.511447
```

一度のリクエストに30ファイルを含めても普通に処理してくれました。
もしかしたらもっといけるかも。

# 書いたスクリプト

## 画像をcut
https://gist.github.com/TakesxiSximada/ca1b5aac871ec7167ff9

## 物体認識させて結果をjsonファイルに保存
https://gist.github.com/TakesxiSximada/996dbbfae5fa3bbab61d

## 結果のjsonファイルをcsvに出力
https://gist.github.com/TakesxiSximada/d451221dc2a280b7e35d

# 注意

今回ユーザ名やパスワードを設定ファイルから取得するためにpitというPythonのサードパーティパッケージを使用しています。ただし2015/04/12現在、pitはPython3には対応していないため、普通にPython3でpip install pitしてもエラーしてしまいます。
pitのリポジトリをフォークしてPython3対応したブランチがありますのでそこからpitをインストールして使用してください。 

https://github.com/TakesxiSximada/pit/archive/fix/sximada/py3k.zip
https://github.com/TakesxiSximada/pit/tree/fix/sximada/py3k

...というかサボらずプルリク出せよ > 俺