AndroidからEmotion APIを使ってみる
7/28から7/30に開催された石巻ハッカソンに参加してきました。
そこでAndroidからMicrosoft Cognitive Services のEmotion APIを使うことがあったので、その時のメモとして書いておこうと思います。
実装にあたって以下のサイトを参考にしました。
公式の実装サンプルに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; }