AndroidからEmotion APIを使ってみる

以前ブログの方に書いた記事ですが、誰もこないようなブログなのでQiitaの方がいいかなぁと思い改めて掲載

7/28から7/30に開催された<a href="http://ishinomakihack.com/">石巻ハッカソン</a>に参加してきました。

そこでAndroidからMicrosoft Cognitive Services のEmotion APIを使うことがあったので、その時のメモとして書いておこうと思います。

実装にあたって以下のサイトを参考にしました。
http://qiita.com/kosfuji/items/575408ae17113d7b58e9
http://qiita.com/a_nishimura/items/19cf3f60ad1dd3f66a84


<p>公式の実装サンプルにjavaのコードはあったのですが、Android6.0からApache HTTP Clientのサポートが切られているため、HttpUrlConnectionを使う必要がありました。</p>
# 準備

APIの利用にはSubscription Keyが必要になるため、<a href="https://www.microsoft.com/cognitive-services/en-us/subscriptions?productId=/products/54d873dd5eefd00dc474a0f4">ここ</a>からMicrosoftアカウントでログインしてSubscription Keyを取得します。

<p>HTTP通信を行うので、INTERNETパーミッションを追加しておきます。</p>
<pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'Menlo'; font-size: 9.0pt;"><span style="color: #e8bf6a;">&lt;uses-permission </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:name=</span><span style="color: #6a8759;">"android.permission.INTERNET" </span><span style="color: #e8bf6a;">/&gt;</span></pre>
# 実装例
<p> まず、通信用のスレッドを立てるためにAsyncTaskを継承したクラスを作成します。</p>

```java
public class ConnectToEmotionAPI extends AsyncTask<Void, Void, JSONObject> {
    @Override
    protected void onPreExecute() {
    }
    @Override
    protected JSONObject doInBackground(Void... params) {
    }
    @Override
    protected void onPostExecute(JSONObject result) {
    }
}
```

Emotion APIから画像を分析した結果がJSONで返ってくるので、doInBackgroundの戻り値はJSONObjectにします。

次に、doInBackground内に通信処理を書いていきます。

```java
HttpURLConnection con = null;
URL url = null;
String urlStr = "https://westus.api.cognitive.microsoft.com/emotion/v1.0/recognize";
String key = "{Your Key}";                  //Subscription Key
DataOutputStream os = null;
BufferedReader reader;
JSONObject json = null;

try {
            url = new URL(urlStr);
            con = (HttpURLConnection)url.openConnection();
            con.setReadTimeout(10000);
            con.setConnectTimeout(20000);
            con.setRequestMethod("POST");
            con.setDoInput(true);
            con.setDoOutput(true);
            //リクエストヘッダーの設定
            con.setRequestProperty("Content-Type", "application/octet-stream");
            con.setRequestProperty("Ocp-Apim-Subscription-Key", key);

            // リクエストボディの作成
            Resources r = main_.getResources();
            Bitmap bmp = BitmapFactory.decodeResource(r, R.drawable.face_small);

            // 画像をバイナリデータに変換
            byte[] byteArray;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            byteArray = bos.toByteArray();

            os = new DataOutputStream(con.getOutputStream());
            for(int i =  0 ; i < byteArray.length;i++){
                   os.writeByte(byteArray[i]);
            }

            // APIに接続
            con.connect();
            os.close();
            
            int status = con.getResponseCode();

            switch (status) {
                case HttpURLConnection.HTTP_OK:
                    InputStream in = con.getInputStream();
                    reader = new BufferedReader(new InputStreamReader(in));
                    String line;
                    String readStr = new String();

                    while (null != (line = reader.readLine())){
                        readStr += line;
                    }
                    Log.d("EmotionAPI","read string: " + readStr);

                    in.close();

                    json = new JSONArray(readStr).getJSONObject(0);
                    break;

                case HttpURLConnection.HTTP_UNAUTHORIZED:
                    break;
                default:
                    break;
            }

} catch (MalformedURLException e){
            e.printStackTrace();
} catch (JSONException e){
            e.printStackTrace();
} catch (IOException e){
            e.printStackTrace();
}

return json;
```


画像を送信するにあたり、バイナリデータで送信するため、ByteArrayOutputStreamを使用します。

返ってくるJSONは配列形式なのでJSONArrayを使ってJSONをパースします。

最後にonPostExecuteで取得したJSONをオブジェクト名ごとにパースしています。

```java
    @Override
    protected void onPostExecute(JSONObject result) {
        super.onPostExecute(result);

        JSONObject jsonData;
        String[] str = new String[2];
        try {
            jsonData = result.getJSONObject("scores");
            str[0] = jsonData.getString("happiness");
            str[1] = jsonData.getString("anger");
        } catch (Exception e){
            e.printStackTrace();
        }

        if (isSmile(str[0])) {
            Log.d("EmotionAPI","素敵な笑顔です！");
        } else if (isAnger(str[1])) {
            Log.d("EmotionAPI","そんなに怒らないでくださいよ~");
        } else {
            Log.d("EmotionAPI","つまんないです。何かリアクションしてください");
        }
    }

    public boolean isSmile(String strValue){

        double value = Double.parseDouble(strValue);
        if (value > 0.5) return true;
        else return false;
    }

    public boolean isAnger(String strValue){

        double value = Double.parseDouble(strValue);
        if (value > 0.5) return true;
        else return false;
    }
```

ハッカソンで実装したコードの方はGitHubに公開しているので、全体の実装の方はそちらをご覧ください。
[Github EmotionAPI Sample](https://github.com/sansuke05/EmotionAPISample/tree/master/app/src/main/java/com/alicelab/motionapisample)