PepperでCloud Vision APIを試すためのボックスを作ってみた

Pepperが周囲にあるものを見て、そこから何か動作を変えることができると、Pepperがより「空気を読んだ」振る舞いをできるようになるんでないかと思っているわけですが、どうも組み込みの画像認識モジュールは使い勝手がよくなく、うーんと思っていました。

そう思っていた矢先に、[Cloud Vision APIの凄さを伝えるべくRasPi botとビデオを作った話](http://qiita.com/kazunori279/items/768c7fdf96cdf45a9d16)を読みました。うへえ何それすごいと思って [Cloud Vision API](https://cloud.google.com/vision/)のLimited Previewに申し込んでPepperに組み込んで遊んでみていました。(2016/2/19 Betaになりました・・・！)

今回は個人的にCloud Visionで遊ぶために作っているボックスを、簡単なものですがアップしましたのでそのネタを。


# Pepperと画像認識

Pepperは標準で顔認識と、(顔以外の)画像認識用のモジュールを持っています。

コミュニケーションのためのロボットという名目もあり、搭載されている顔認識エンジンは非常に強力で、性別認識、年齢認識など申し分ないと個人的に思っています。ドキュメント中には顔認識はオムロンのものを使っていると言及があるので、[OKAO Vision](http://plus-sensing.omron.co.jp/technology/)なんだと思いますが。

一方で画像認識については、ちょっと実用には向かないかな、という感じを持っています。
というのも、[Pepperチュートリアル (7)：画像認識#画像の学習](http://qiita.com/Atelier-Akihabara/items/8df3e81d286e2e15d9b6#%E7%94%BB%E5%83%8F%E3%81%AE%E5%AD%A6%E7%BF%92)のようにして対象の物体(カメラで撮影した画像の領域)を指示して学習させてあげるわけですが、当然物体をどの角度から撮影するかにより撮影画像は変化しますし、照明などの撮影時の環境にも依存しますから、実用にはそれなりの量の学習用データが必要になりそうです。

# Cloud Vision用ボックス

そんな問題意識があったので、すでにGoogleが大量のデータで学習済みの画像分析処理がCloud Vision API経由で簡単に使えるようになるとなれば、これは試すしかないなと。さっそく、Choregraphe上で、Cloud Vision APIをボックスとして簡単に利用できるようにしてみました。

## ボックスの場所

Cloud Vision API用ボックスは[これまで作ってきた](http://qiita.com/yacchin1205/items/4b2aab2f6d9ee3b03746#%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF)ボックスライブラリに追加しています。

- https://github.com/yacchin1205/pepper-web-boxes

[PepperのALMemoryとMQTTを相互接続するボックスを作った話#ボックスライブラリの読み込み](http://qiita.com/yacchin1205/items/4b2aab2f6d9ee3b03746#%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF) を参考にインポートしてください。

## ボックス仕様

Google > Cloud Visionに以下の3つのボックスがあります。今回はCloud Vision APIのすべてのFeatureを実装してはおらず、とりあえず自分が使ってみているものだけを公開しています。

![cloud-vision-boxlib.png](https://qiita-image-store.s3.amazonaws.com/0/28770/4ea571b0-64e6-2aff-182a-1a4e626d991a.png)


### Label Detectionボックス

Cloud Vision APIのLABEL_DETECTION機能を利用するためのボックスです。

![cloud-vision-label.png](https://qiita-image-store.s3.amazonaws.com/0/28770/3b05f531-e663-e33a-e836-bca320de7e8f.png)


#### 設定可能なパラメータ

LABEL_DETECTIONボックスには以下のパラメータが指定できます。LANDMARK_DETECTIONボックス, TEXT_DETECTIONボックスも同様の設定を持ちます。

![cloud-vision-config.png](https://qiita-image-store.s3.amazonaws.com/0/28770/9283ab9a-84d8-d414-7234-12e637993517.png)

- Endpoint: Cloud Vision APIのエンドポイントのURLを指定します。Cloud Vision APIのバージョンアップなどがない限りは変更する必要はありません。`{api_key}`はAPI Keyパラメータで自動的に置換されます
- API Key: Cloud Vision APIを利用可能なAPIキーを指定します
- Max Results: Cloud Vision APIが返す項目数の最大値を指定します

実行にはAPI Keyを指定する必要があります。詳しくは使い方の手順で詳しく説明します。

#### onStart入力

onStart入力には、Pepper内にある、解析させたい画像ファイルの絶対パスを与えます。

例えばTake Pictureボックスの場合、デフォルトでは `/home/nao/recordings/cameras/image.jpg` に画像が保存されますので、これを文字列として与えます。

#### labels出力

labels出力には、以下のようなリストが出力されます。

```
[[候補1のスコア, 候補1の名前], [候補2のスコア, 候補2の名前], ...]
```

スコアは0.0～1.0の間の数値として表現されます。名前は文字列で表現されます。

#### onError出力

ボックスの実行時にエラーが発生した際に、エラー内容を示す文字列が出力されます。
Cloud Vision APIへのリクエストに失敗した場合はonErrorが出力されます。

### Landmark Detection

Cloud Vision APIのLANDMARK_DETECTION機能を利用するためのボックスです。

![cloud-vision-landmark.png](https://qiita-image-store.s3.amazonaws.com/0/28770/64bfe9d3-6bbd-77aa-28aa-6cca2adb3ed9.png)


設定可能なパラメータおよびonStart入力, onError出力は、Label Detectionボックスと同様です。

#### landmarks出力

landmarks出力には、以下のようなリストが出力されます。

```
[[候補1のスコア, 候補1の名前, [候補1の緯度, 候補1の経度]],
 [候補2のスコア, 候補2の名前, [候補1の緯度, 候補1の経度]], ...]
```

緯度の値は-90.0～+90.0、経度の値は-180.0～+180.0で表現されます。

### Text Detection

Cloud Vision APIのTEXT_DETECTION機能を利用するためのボックスです。

![cloud-vision-text.png](https://qiita-image-store.s3.amazonaws.com/0/28770/bb908bff-52dc-b698-627e-e1f9ca048485.png)


#### 設定可能なパラメータ

TEXT_DETECTIONにはLABEL_DETECTIONなどで利用可能なパラメータに加え、言語指定に関するパラメータを指定することが可能です。

![cloud-vision-text-conf.png](https://qiita-image-store.s3.amazonaws.com/0/28770/6db5ffc4-2daf-0131-63cc-a13a556ba869.png)


Languagesには、認識結果として期待する言語を、 https://cloud.google.com/translate/v2/using_rest#language-params の言語コードとしてスペース区切りで指定します。
この指定が空である場合、言語は自動的に検出されます。

#### texts出力

texts出力には、以下のようなリストが出力されます。

```
[[結果1の言語, 結果1の内容], [結果2の言語, 結果2の内容], ...]
```

内容の文字列が複数行の場合、改行位置に`\n`が付加されます。

## 使い方

これらCloud Vision APIボックスは、以下のように利用します。

1. Cloud Vision APIの有効化とAPI Keyの準備
    Cloud Vision APIのドキュメントのうち、Getting Startedを見ながら、Setting Up an API Keyまで進めます。
    これで、自分のプロジェクトでCloud Vision APIが使えるようになります。

2. ビヘイビアの作成
    Cloud Vision API用ボックスを使って、Choregrapheでビヘイビアを作成します。

    ![cloud-vision-sample-flow.png](https://qiita-image-store.s3.amazonaws.com/0/28770/d8d897f2-6d87-ea45-2c19-f1637fd92263.png)    
    Pepperのカメラからの画像取得は、標準ボックスライブラリの Sensing > Vision > Camera Actions > Take Pictureボックスを改修して使用しています。詳しくは[Take Pictureボックスの改修](#take-picture%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E6%94%B9%E4%BF%AE)で説明します。

3. Cloud Vision APIボックスにAPI Keyを指定
    ボックスにAPI Keyを設定します。

    ![cloud-vision-sample-conf.png](https://qiita-image-store.s3.amazonaws.com/0/28770/709739cd-a675-a6c3-0fc6-818385c92a9a.png)
    なお、ボックスにAPI Keyを指定するのはちょっと悩ましいなあと考え中・・・うっかりビヘイビアを公開してしまうとAPI Keyを不正に使われるおそれもありますしね・・・Preferencesに持たせるほうがよいのかもしれない。

これだけでPepperのカメラで撮影された画像をCloud Vision APIで処理してログに出力することができます。どんな出力がされるかは [ボックスの出力例](#ボックスの出力例) で説明します。

### Take Pictureボックスの改修

Choregraphe標準のTake PictureボックスはALPhotoCapture APIにアクセスを簡単におこなえるボックスですが、書き込み先が `/home/nao/recording/cameras` としてハードコーディングされています。
Take Pictureボックスの設定がデフォルトのままであれば、Take PictureボックスとLabel Detectionボックスの接続は以下のようにしてもOKです。

![cloud-vision-take-picture-01.png](https://qiita-image-store.s3.amazonaws.com/0/28770/1e6528f1-5fc4-a249-4d45-3fcca58380c6.png)

ただ、最近のアプリのガイドラインに従おうとするとホームディレクトリ以下に勝手にディレクトリを作るのではなく、User Writable Data PathをAPIに問い合わせて使えということにもなりそうな気がしていて、あんまり定数としてこの手のパスを含めたくないと思うのが人情です。

そこで、今回の例ではこんな感じにしています。

1. Take Pictureボックスの出力をString(文字列)に変更する
    ![cloud-vision-take-picture-02.png](https://qiita-image-store.s3.amazonaws.com/0/28770/135b3204-b6b7-f63d-b079-d99aa954924c.png)

2. Take PictureボックスのPythonスクリプトを修正する

    ```py
        if self.photoCapture:
            self.photoCapture.setResolution(resolution)
            self.photoCapture.setCameraID(cameraID)
            self.photoCapture.setPictureFormat("jpg")
            # savedPath = を追加
            savedPath = self.photoCapture.takePicture( self.recordFolder, fileName )
        self.bIsRunning = False
        # onStopped呼び出し時に引数を追加
        self.onStopped(savedPath[0])
    ```

バーチャルロボットなどではsavedPath変数が未定義になるのでエラーとなるわけですが、そもそも先行してALPhotoCapture APIが見つからないと怒られているはずなのでいいでしょうということで。

なお、Take Pictureボックスで、ALPhotoCapture.takePictureを何度か呼び出していると、takePictureメソッドから制御が戻ってこないケースがあるようです。この現象が起きるのは、今のところオートノマスライフをオンにしているときのみで、オートノマスライフをオフにしておくと起きないように見えます。ALVideoDeviceのsubscribeでブロックされる現象は以前も遭遇したことがあったので、もしかしたらオートノマスライフ時にアプリ間で競合状態などが生じているのかも、と想像しています。

# ボックスの出力例

このボックスを使っていくつか画像を認識させてみました。

## Label Detectionボックス

いくつかPepperに物を見せて、Label Detectionボックスの出力を見てみました。

例えばコート。

![label-clothing.jpg](https://qiita-image-store.s3.amazonaws.com/0/28770/28f48e60-1641-3b9d-ab91-3fa23794aa1a.jpeg)

こんな感じの出力が返ってきました。

```
[[0.98875492811203, 'clothing'],
 [0.8403595685958862, 'room'],
 [0.797779381275177, 'dress']]
```

「衣類」がスコア0.98となりよい感じです。ドアや壁からの推測なのか「部屋」としているのも面白い感じです。

次にビデオカメラ。

![label-camera.jpg](https://qiita-image-store.s3.amazonaws.com/0/28770/db8dbf47-8c55-3c84-92a2-9241cc1cda06.jpeg)

```
[[0.5420256853103638, 'cameras & optics'],
 [0.5059739351272583, 'wall']]
```

スコアは低めですが、間違ってはいない感じです。いろいろなものが映り込んでいるので、ちょっと自信がなくなるのかもしれません。

次、椅子。

![label-chair.jpg](https://qiita-image-store.s3.amazonaws.com/0/28770/6f27f80d-5a36-0d61-09d1-239642c3797c.jpeg)

```
[[0.9610474705696106, 'chair'],
 [0.9104857444763184, 'furniture'],
 [0.5046526193618774, 'couch']]
```

何も考えず三脚も一緒に見せてしまいましたが、椅子のほうに反応してくれたよう。

いろいろ見せてみると、なるほどと思う結果を返してくれます。これらの認識結果に基づいて、話題を選択できたりすると面白いかもしれません。

## Landmark Detectionボックス

Landmark Detectionボックスの実験。Pepperを外に連れ出すのはなかなかむつかしいので、旅行ガイドブックを見せてみました。

![landmark-itsukushima.jpg](https://qiita-image-store.s3.amazonaws.com/0/28770/99b8f652-dd44-7c79-ab82-1137cfbe4e01.jpeg)

```
[[0.2688983678817749, 'Itsukushima', [34.31437683105469, 132.43777465820312]],
 [0.2660306990146637, 'Itsukushima Shrine', [34.296932220458984, 132.31935119628906]]]
```

文字など風景以外の要素がかなり入っている気がするので判定してくれないかと思いましたが、スコアが低めではありますが厳島神社と判定してくれました。おもしろい。


## Text Detectionボックス

Text DetectionボックスでOCR機能も試してみます。作業場にあったものを見せてみました。

![ocr-item.jpg](https://qiita-image-store.s3.amazonaws.com/0/28770/9f1826a6-ac04-3290-e51e-4b3fc710afa5.jpeg)

こんな出力結果。

```
[['ja',
"ELECOM
日々のお掃除に
サッ!とひと拭き
お得用
1,日
ウエットクリーニングティッシュ
パソコンやデスクまわりに
600090
ぜ
アルカリ
SO
電澥イオン水
AL 150"]]
```

いくつか試してみましたが、かなり検出能力高いなあという感じです。

円形のアイコンの並びを数字だと思ってしまうのはちょっと悩ましい感じですが、「お徳用」みたいなフォントもある程度認識してくれるようなので、面白そうです。手書きの認識とかも試してみたいですねー

# まとめ

こんな感じで、Pepperの認識機能の一部をGoogle Cloud Platformにお願いしてみるということを試してみています。

Pepperのロードアベレージなどを[Pepperのログや稼働情報をFluentd経由でBigQueryに突っ込んでみる](http://qiita.com/yacchin1205/items/11301d79380d08d2dbf6)以来測り続けているわけですが、PepperのCPUは思いのほか忙しくしているようですし、クラウドサービスに移譲できる部分は移譲して・・・ということができると幸せになれるかもしれません。

あと、いろいろ画像を認識させていて、焼きそばの画像に対してCloud Visionが yakisoba と yi mein という2つの認識結果を返したのが面白かった。自分自身はyi meinを知らなかったので焼きそばの画像からyi meinを想起することはないわけですが、Cloud Vision APIはyi meinを知ってしまっているわけです・・・
人間の個体よりもCloud Visionのほうがずっと多くのものを見ているようなので、ここで得られた認識結果をコミュニケーションに使うためには、相手の人間の文脈に合わせてフィルタなどしてあげる必要があるのかなと。いろいろ工夫しどころがありそうで面白そうですねー・・・！