AndroidからEmotion APIを使ってみる

7/28から7/30に開催された石巻ハッカソンに参加してきました。

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

実装にあたって以下のサイトを参考にしました。

qiita.com

qiita.com

 

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

 

準備 

APIの利用にはSubscription Keyが必要になるため、ここからMicrosoftアカウントでログインしてSubscription Keyを取得します。

HTTP通信を行うので、INTERNETパーミッションを追加しておきます。

<uses-permission android:name="android.permission.INTERNET" />

 

実装例

 まず、通信用のスレッドを立てるためにAsyncTaskを継承したクラスを作成します。

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内に通信処理を書いていきます。

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をオブジェクト名ごとにパースしています。

    @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.com