以前ブログの方に書いた記事ですが、誰もこないようなブログなのでQiitaの方がいいかなぁと思い改めて掲載
7/28から7/30に開催された石巻ハッカソンに参加してきました。
そこでAndroidからMicrosoft Cognitive Services のEmotion APIを使うことがあったので、その時のメモとして書いておこうと思います。
実装にあたって以下のサイトを参考にしました。
http://qiita.com/kosfuji/items/575408ae17113d7b58e9
http://qiita.com/a_nishimura/items/19cf3f60ad1dd3f66a84
公式の実装サンプルに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 EmotionAPI Sample