Google Cloud Vison のラベル検出をGoogle Cloud SDKなしのpythonで実行する

[link-01]:https://cloud.google.com/
[link-02]:https://cloud.google.com/vision/

#はじめに
[Google Cloud Vison][link-02]はしばらく見ないうちに機能が増えていたが、まずはラベル検出でプログラムの定型化を試してみた。

pythonは[Cloud SDK](https://cloud.google.com/sdk/)は使わずに記述した。

#pythonプラグラム

##参考文献

素直に[Google Cloud Vison][link-02]の入門ガイドに従った。

- [入門ガイド](https://cloud.google.com/vision/docs/how-to)
 - [Vision API リクエストを作成する](https://cloud.google.com/vision/docs/request)
 - [Base64 エンコード](https://cloud.google.com/vision/docs/base64)

ずーっとハマっていた、ちょっと特殊（と思った）なjsonデータの読み方は、以下の記事で解決！！！　まだまだjsonはわからないことだらけです。

- [Google Cloud Vision APIのTEXT_DETECTION を Pythonで試してみる](http://jun-networks.hatenablog.com/entry/2018/02/01/044255)

##ライブラリ

ライブラリはrequestsを使用。特殊なものは使っていない。

##auth key情報

[GCP][link-01]の自分のAPI key情報を、gcpapikey.pyに記載する。

```gcpapikey.py
str_api_key = "1234567890ABCDEFGHIJKLMNOPQRSTUVXYZ"
```

メインプログラム側で、gcpapikey.pyの変数をimportする。

```python
from gcpapikey import(
    str_api_key
)
```

##[GCP][link-01]アクセス

お決まりの記述とお約束で記述。[Google Cloud Vison][link-02]のURLは以下のようになる。

```python
    str_url = "https://vision.googleapis.com/v1/images:annotate?key="
    str_headers = {'Content-Type': 'application/json'}
    url = str_url + str_api_key
```

##画像のエンコード

**base64エンコードの画像文字列**として送る必要があり、エンコードしてから文字列化する。

```python
    ENCODING = 'utf-8'
    str_encode_file = base64.b64encode(image)
    str_decode_file = str_encode_file.decode(ENCODING)
```

##jsonデータ

入門ガイドの[リクエストを作成する](https://cloud.google.com/vision/docs/request)の通りに記述。**type**で検出認識機能を指定する。これはラベル検出の場合で、最大**10**ラベルを画像から検出する設定。

```python
    str_json_data = {
        'requests': [
            {
                'image': {
                    'content': str_decode_file
                },
                'features': [{
                    'type': "LABEL_DETECTION",
                    'maxResults': 10
                }]
            }
        ]
    }
```

##リクエストを投げる

お決まりの記述。jsonデータを整形して、指定のURLにヘッダをつけて投げる。

```python
    jd = json.dumps(str_json_data)
    #print(jd)
    print("begin request")

    s = requests.Session()
    r = requests.post(url, data=jd, headers=str_headers)

    print("status code : ", r.status_code)
    print("end request")
```

##検出結果の抜出し

ステータスコードが200ならアクセス成功！　以下のようなjsonデータが返ってくるが、**"labelAnnotations"の[]**が曲者だった。

```json
{
  "responses": [
    {
      "labelAnnotations": [
        {
          "description": "room",
          "mid": "/m/06ht1",
          "score": 0.9037287,
          "topicality": 0.9037287
        },
        {
          "description": "interior design",
          "mid": "/m/02rfdq",
          "score": 0.86764306,
          "topicality": 0.86764306
        },
        ...
        ...
        ...
        {
          "description": "curtain",
          "mid": "/m/03rszm",
          "score": 0.72007716,
          "topicality": 0.72007716
        }
      ]
    }
  ]
}
```

jsonデータの**description**つまり、検出されたラベルのみを抜き出して、テキストファイルに保存する。
ちゃんと理解しないといけないが、[参考文献](http://jun-networks.hatenablog.com/entry/2018/02/01/044255)にあるように**[0]**を入れることで辞書型のネストに入っていくことができた。

```python
    if r.status_code == 200:
        parsed = json.loads(r.text)
        dtext = ""
        for labelann in parsed["responses"][0]["labelAnnotations"]:
            dtext += (labelann["description"] + "\n")
            #print(dtext)
            
        with open(fn_recog, 'w') as outfile:
            outfile.write(dtext)
        return r.text
    else:
        print("error statsus code : ", r.status_code)
        return "error"
```

##作成結果

プログラム全体は以下。あらかじめ準備した画像ファイルを読み込んで、ラベル検出する。リクエストと結果のjsonデータが検出したいものによって変わるので、関数名の末尾に**LA**を付けた。

```gglrecogLA.py
#coding:utf-8
import base64
import json
import requests
import sys

from gcpapikey import(
    str_api_key
)

def getRecogImageLA(image, fn_recog):

    ENCODING = 'utf-8'
    str_encode_file = base64.b64encode(image)
    str_decode_file = str_encode_file.decode(ENCODING)

    str_url = "https://vision.googleapis.com/v1/images:annotate?key="
    str_headers = {'Content-Type': 'application/json'}
    url = str_url + str_api_key

    str_json_data = {
        'requests': [
            {
                'image': {
                    'content': str_decode_file
                },
                'features': [{
                    'type': "LABEL_DETECTION",
                    'maxResults': 10
                }]
            }
        ]
    }

    # save to file:
    with open('postdata.json', 'w') as f:
        json.dump(str_json_data, f)

    jd = json.dumps(str_json_data)
    #print(jd)
    print("begin request")

    s = requests.Session()
    r = requests.post(url, data=jd, headers=str_headers)

    print("status code : ", r.status_code)
    print("end request")
    
    if r.status_code == 200:
        parsed = json.loads(r.text)
        """
        print (r.text)
        print (parsed)
        print(len(parsed["responses"][0]["labelAnnotations"]))
        print(parsed["responses"][0]["labelAnnotations"][0]["description"])
        with open('data.json', 'w') as outfile:
            json.dump(parsed, outfile, sort_keys=True, indent=2)
        """

        dtext = ""
        for labelann in parsed["responses"][0]["labelAnnotations"]:
            dtext += (labelann["description"] + "\n")
            #print(dtext)
            
        with open(fn_recog, 'w') as outfile:
            outfile.write(dtext)
        return r.text
    else:
        print("error statsus code : ", r.status_code)
        return "error"

if __name__ == '__main__':

    args =sys.argv

    if len(args) < 3:
        print("Usage: python gglrecog.py <img file> <text file>")
        exit()
    else:
        pass

    image = open(args[1], 'rb').read()
    fn_recog = args[2]
    getRecogImageLA(image, fn_recog)
```

##ラベル検出

以下を実行し、画像ファイル**img.jpg**に写っている物体が検出され、テキストファイル**result.txt**に書き込まれていることを確認した。**result.txt**を[こっちのプログラム](https://qiita.com/teraken_/items/5df9ec814759efbb492b)に喰わせると、検出されたラベルが読み上げられた。

```sh
$ python3 gglrecogLA.py img.jpg result.txt
```

##さいごに

[Google Cloud Vison][link-02]と[Text-to-Speech](https://cloud.google.com/text-to-speech/)をつなげることで、画像認識結果を音声で伝えることができるようになった。

ラズパイカメラと連動させると画像認識カメラが作れるが、さらに検出結果を元画像に重畳したり、カテゴリ分けの工夫を考えたりすると、できることが広がりそう。。