RubyでFace APIを使って人間の顔から感情検出する手順をまとめる

ハッカソンで、顔から感情を解析できるMicrosoft AzureのEmotion APIを使ったのですが、公式のドキュメントが古くなっていて（？）うまくいかなかったので方法をまとめます。

## 問題点

色々試したのですが、おそらくEmotion APIは使えません。
[公式ページ](https://azure.microsoft.com/ja-jp/services/cognitive-services/emotion/)には、

![スクリーンショット 2018-03-11 21.50.34.png](https://qiita-image-store.s3.amazonaws.com/0/202653/61bf1f15-a10f-8c74-3c08-89a36833929b.png)

「使えるけど、Face APIに統合するから、なるべくそっち使ってね」ってことが書いてありました。
これを見ると、Emotion APIもまだ使えるのかと思いましたが、使えませんでした。

[クイックスタートガイド](https://docs.microsoft.com/ja-jp/azure/cognitive-services/emotion/quickstarts/ruby)の

![スクリーンショット 2018-03-11 21.53.47.png](https://qiita-image-store.s3.amazonaws.com/0/202653/a434b9c4-c5ce-b232-3350-cea14d0fc021.png)

必須事項から、subscription keyを取得するのですが、このキーを使ってEmotion APIを使ってもAccess denyされて弾かれてしまいました。

## 解決策

Emotion APIではなくFace APIを使うとうまく行きました。
基本的な使い方や、APIキーの取得方法は[こちらのスタートガイド](https://docs.microsoft.com/ja-jp/azure/cognitive-services/face/quickstarts/ruby)で知ることができます。

しかしスタートガイドには感情分析したデータをどう取得するかが入っていません。
これは[公式のトップページ](https://azure.microsoft.com/ja-jp/services/cognitive-services/face/)を参考にするとうまく行きました。

```json
検出結果:
JSON:
 [
  {
    "faceId": "e3c54320-8be3-4f4a-aec8-4c7b2c132b1e",
    "faceRectangle": {
      "top": 128,
      "left": 459,
      "width": 224,
      "height": 224
    },
    "faceAttributes": {
      "hair": {
        "bald": 0.0,
        "invisible": false,
        "hairColor": [
          {
            "color": "brown",
            "confidence": 1.0
          },
          {
            "color": "blond",
            "confidence": 0.69
          },
          {
            "color": "black",
            "confidence": 0.54
          },
          {
            "color": "other",
            "confidence": 0.31
          },
          {
            "color": "gray",
            "confidence": 0.05
          },
          {
            "color": "red",
            "confidence": 0.04
          }
        ]
      },
      "smile": 0.939,
      "headPose": {
        "pitch": 0.0,
        "roll": -16.9,
        "yaw": 16.7
      },
      "gender": "female",
      "age": 23.4,
      "facialHair": {
        "moustache": 0.0,
        "beard": 0.0,
        "sideburns": 0.0
      },
      "glasses": "ReadingGlasses",
      "makeup": {
        "eyeMakeup": true,
        "lipMakeup": true
      },
      "emotion": {
        "anger": 0.037,
        "contempt": 0.001,
        "disgust": 0.015,
        "fear": 0.001,
        "happiness": 0.939,
        "neutral": 0.001,
        "sadness": 0.0,
        "surprise": 0.007
      },
      "occlusion": {
        "foreheadOccluded": false,
        "eyeOccluded": false,
        "mouthOccluded": false
      },
      "accessories": [
        {
          "type": "glasses",
          "confidence": 0.99
        }
      ],
      "blur": {
        "blurLevel": "low",
        "value": 0.0
      },
      "exposure": {
        "exposureLevel": "goodExposure",
        "value": 0.48
      },
      "noise": {
        "noiseLevel": "low",
        "value": 0.0
      }
    },
    "faceLandmarks": {
      "pupilLeft": {
        "x": 504.8,
        "y": 206.8
      },
      "pupilRight": {
        "x": 602.5,
        "y": 178.4
      },
      "noseTip": {
        "x": 593.5,
        "y": 247.3
      },
      "mouthLeft": {
        "x": 529.8,
        "y": 300.5
      },
      "mouthRight": {
        "x": 626.0,
        "y": 277.3
      },
      "eyebrowLeftOuter": {
        "x": 461.0,
        "y": 186.8
      },
      "eyebrowLeftInner": {
        "x": 541.9,
        "y": 178.9
      },
      "eyeLeftOuter": {
        "x": 490.9,
        "y": 209.0
      },
      "eyeLeftTop": {
        "x": 509.1,
        "y": 199.5
      },
      "eyeLeftBottom": {
        "x": 509.3,
        "y": 213.9
      },
      "eyeLeftInner": {
        "x": 529.0,
        "y": 205.0
      },
      "eyebrowRightInner": {
        "x": 579.2,
        "y": 169.2
      },
      "eyebrowRightOuter": {
        "x": 633.0,
        "y": 136.4
      },
      "eyeRightInner": {
        "x": 590.5,
        "y": 184.5
      },
      "eyeRightTop": {
        "x": 604.2,
        "y": 171.5
      },
      "eyeRightBottom": {
        "x": 608.4,
        "y": 184.0
      },
      "eyeRightOuter": {
        "x": 623.8,
        "y": 173.7
      },
      "noseRootLeft": {
        "x": 549.8,
        "y": 200.3
      },
      "noseRootRight": {
        "x": 580.7,
        "y": 192.3
      },
      "noseLeftAlarTop": {
        "x": 557.2,
        "y": 234.6
      },
      "noseRightAlarTop": {
        "x": 603.2,
        "y": 225.1
      },
      "noseLeftAlarOutTip": {
        "x": 545.4,
        "y": 255.5
      },
      "noseRightAlarOutTip": {
        "x": 615.9,
        "y": 239.5
      },
      "upperLipTop": {
        "x": 591.1,
        "y": 278.4
      },
      "upperLipBottom": {
        "x": 593.2,
        "y": 288.7
      },
      "underLipTop": {
        "x": 597.1,
        "y": 308.0
      },
      "underLipBottom": {
        "x": 600.3,
        "y": 324.8
      }
    }
  }
]
```

返ってくるJSONを眺めると

```json
      "emotion": {
        "anger": 0.037,
        "contempt": 0.001,
        "disgust": 0.015,
        "fear": 0.001,
        "happiness": 0.939,
        "neutral": 0.001,
        "sadness": 0.0,
        "surprise": 0.007
      },
```

と書かれている部分があるので、これを参考に

```ruby:emo_api.rb
require 'net/http'

# NOTE: You must use the same region in your REST call as you used to obtain your subscription keys.
#   For example, if you obtained your subscription keys from westus, replace "westcentralus" in the
#   URL below with "westus".
uri = URI('https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect')
uri.query = URI.encode_www_form({
    # Request parameters
    'returnFaceId' => 'true',
    'returnFaceLandmarks' => 'false',
    'returnFaceAttributes' => 'age,gender'
})

request = Net::HTTP::Post.new(uri.request_uri)
# Request headers
request['Content-Type'] = 'application/json'
# NOTE: Replace the "Ocp-Apim-Subscription-Key" value with a valid subscription key.
request['Ocp-Apim-Subscription-Key'] = '<Subscription Key>'
# Request body
request.body = "{\"url\":\"http://example.com/1.jpg\"}"

response = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
    http.request(request)
end

puts response.body
```

公式に乗っているサンプルコードの以下の部分をこのように書き換えると、感情の数値を取得することができました。

```ruby:emo_api.rb
'returnFaceAttributes' => 'emotion'
```