Python入門者でもComputer Vision APIを叩くのは怖くない

##はじめに
本記事は[CogBot Advent Calendar 2017](https://qiita.com/advent-calendar/2017/cogbot) 12日目の記事です。

2017年6月に参加したハッカソンにて、ほとんど経験のないPythonを使って
Microsoft Cognitive ServicesのComputer Vision APIを呼び出しました。
思っていたよりも簡単にAPI呼び出しができたので、
その経験をもとにComputer Vision APIの呼び出し手順をまとめます。
Python入門者の方がComputer Vision APIを始めとするCognitice Servicesを使う一助になれば幸いです。

##前提
Computer Vision APIは画像を分析し、タグ付け、説明文付けやOCRなどができます。

Computer Vision APIのイメージ
![20170710PythonLT_slide11.png](https://qiita-image-store.s3.amazonaws.com/0/82132/89080a32-403e-34cf-3774-4054d890ecd5.png)

APIで説明文をつける画像として以下のものを使います。[^1]
https://emotionwebsto.blob.core.windows.net/handson/emotionweb_happiness.jpg

以下については本記事では扱いません。
エヴァンジェリストの方々が発信されている情報などを参照ください。

- Computer Vision APIを始めとするMicrosoft Cognitive Servicesの概要[^2]
- Computer Vision APIのAPIキーの発行の仕方[^3]

##動作環境
2017年6月のハッカソンではPython3.5.1で動かしました。
今回記事にするにあたりPython **3.6.3** で再度動作確認しました。

##動かし方：サンプルコードをコピーして数カ所変更
Computer Vision API公式ドキュメントからPython3.6のコードをコピーしましょう。これで7割終わりです。
https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/quickstarts/python#analyze-an-image-with-computer-vision-api-using-python-a-nameanalyzeimage-a
そして、以下で説明する箇所を変更すると動くはずです。

```python3:document_copy.py
########### Python 3.6 #############
import http.client, urllib.request, urllib.parse, urllib.error, base64, json

###############################################
#### Update or verify the following values. ###
###############################################

# Replace the subscription_key string value with your valid subscription key.
subscription_key = 'please enter your subscription key' ##### (1) #####

# Replace or verify the region.
#
# You must use the same region in your REST API call as you used to obtain your subscription keys.
# For example, if you obtained your subscription keys from the westus region, replace
# "westcentralus" in the URI below with "westus".
#
# NOTE: Free trial subscription keys are generated in the westcentralus region, so if you are using
# a free trial subscription key, you should not need to change this region.
uri_base = 'westcentralus.api.cognitive.microsoft.com' ##### (2) #####

headers = {
    # Request headers.
    'Content-Type': 'application/json',
    'Ocp-Apim-Subscription-Key': subscription_key,
}

params = urllib.parse.urlencode({
    # Request parameters. All of them are optional.
    'visualFeatures': 'Description', ##### (3) #####
    'language': 'en',
})

# Replace the three dots below with the URL of a JPEG image of a celebrity.
body = "{'url': 'https://emotionwebsto.blob.core.windows.net/handson/emotionweb_happiness.jpg'}" ##### (4) #####

try:
    # Execute the REST API call and get the response.
    conn = http.client.HTTPSConnection('westcentralus.api.cognitive.microsoft.com') ##### (2) #####
    conn.request("POST", "/vision/v1.0/analyze?%s" % params, body, headers) ##### (5) #####
    response = conn.getresponse()
    data = response.read() ##### (6) #####

    # 'data' contains the JSON data. The following formats the JSON data for display.
    parsed = json.loads(data)
    print ("Response:")
    print (json.dumps(parsed, sort_keys=True, indent=2))
    conn.close()

except Exception as e:
    print('Error:')
    print(e)

####################################
```

####必須の変更（2点）
- (1)：サブスクリプションキーはご自身のものに変更してください
- (2)：設定したComputer Vision APIのリクエストURLに合わせて変更してください。（ドキュメントからコピーした直後はドメインが"westcentralus"となっています）
**注意: (2)は2箇所にあります。**

####必須ではない変更（2点）
- 画像に説明文をつけてほしいので、(3)をDescriptionのみに変更しています。[^4]
- (4)の画像のURLを今回の画像に合わせて変更しています。

####結果

```
$ python document_copy.py 
Response:
{
  "description": {
    "captions": [
      {
        "confidence": 0.4793838274204443,
        "text": "a close up of a woman holding a cup"
      }
    ],
    "tags": [
      "person",
      "indoor",
      "sitting",
      "holding",
      "woman",
      "food",
      "cup",
      "young",
      "table",
      "little",
      "red",
      "piece",
      "white",
      "hair",
      "girl",
      "close",
      "eating",
      "sandwich",
      "wearing",
      "desk",
      "plate",
      "computer",
      "restaurant",
      "coffee",
      "remote",
      "room"
    ]
  },
  "metadata": {
    "format": "Jpeg",
    "height": 500,
    "width": 500
  },
  "requestId": "5cff8871-64b9-45bd-aeaf-e359f36d424f"
}
```

###### 余談-1 Python3.6.3ではSSLのエラーが出た
Python3.5.1では動いていたコードをそのままPython3.6.3で動かしたところ、
以下のエラーが(5)の箇所で出ました。

```
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed
```

こちらの記事を参考にしたところ解決できました。
[macOS用公式インストーラーのPython 3.6でCERTIFICATE_VERIFY_FAILEDとなる問題](https://qiita.com/orangain/items/0a641d980019fd7e0c52)

###### 余談-2 Python3.6.3では```json.loads```がbytes型にも対応していた
Python3.5.1では(6)の箇所を以下のようにする必要がありました。

```python3
data = response.read().decode() ##### (6) #####
```

ハッカソンで教えてもらったところ

- APIからのレスポンスはbytes型として扱われている。
- ```json.loads()```がstr型しか受け付けないため、APIからのレスポンスをstr型に変換する必要がある。

→そのために```decode()```を使うそうです。(Python3.5.1での話です)
https://docs.python.jp/3/library/stdtypes.html#bytes.decode

Python3.6.3では```json.loads()```がbytes型にも対応しており、```decode()```は不要でした。
https://docs.python.jp/3/library/json.html#json.loads
>バージョン 3.6 で変更: s には bytes 型と bytearray 型も使えるようになりました。

##まとめ
Python入門者でもComputer Vision APIを叩くのは怖くない。
なぜなら、Computer Vision API公式ドキュメントからサンプルコードをコピーして数カ所変更するだけだから。

##関連資料
- 本記事中で使ったスライドは筆者が以下で公開しているものを元にしています。
https://speakerdeck.com/ftnext/call-cvapi-with-python
- 本記事中のソースコードはGitHubでも公開しています。[^5]
https://github.com/ftnext/computer-vision-api-sample


##脚注
[^1]: この画像はこちらのCognitive Servicesハンズオン記事で見つけました。[人工知能パーツ Microsoft Cognitive Services を使った表情分析アプリを作ろう！ (Emotion API × JavaScript 編)](https://qiita.com/annie/items/96b37ef94885510c1b6b)
[^2]: 例えば次のような記事があります。[これから始める人のための最新Cognitive Services入門](http://www.buildinsider.net/small/cognitiveservices/01)
[^3]: 次の記事のSTEP1は参考にできそうです。[はじめてのAzure Cognitive Service – 画像認識（Computer Vision API編）](http://memo.tyoshida.me/azure/beginners-guide-to-azure-cognitive-service-computer-vision-api/)
[^4]: リクエストパラメータvisualFeaturesについてはAPI Referenceを参照。https://westcentralus.dev.cognitive.microsoft.com/docs/services/56f91f2d778daf23d8ec6739/operations/56f91f2e778daf14a499e1fa
[^5]: 本記事執筆にあたりrequestsを用いた実装も用意しました。本記事が長くなってしまったので別の記事とさせていただきます。