イノベーション エンジニアブログ


株式会社イノベーションのエンジニアたちの技術系ブログです。ITトレンド・List Finderの開発をベースに、業務外での技術研究などもブログとして発信していってます!


このエントリーをはてなブックマークに追加

EC2 + SpringBoot でLINE@のBotアプリを作る(Google Cloud Vision API) その3

こんにちは。 エンジニアのNew塚本です。

近頃はGolangでコーディングをしているので、久々にJavaで何かしたくなりました!
私のまわりでは画像解析ブームが来ているようなので、その流れにのります。

今回は、Google Cloud Vision APIを使ってみようと思います。
画像の中の顔を認識するFACE_DETECTIONを試してみます。

Google Cloud Vision API って?

APIに画像を送信すると、機械学習によって画像解析した結果を返してくれます。
詳細はこちら)
https://cloud.google.com/vision/docs/?hl=ja

プログラムの流れ
  1. LINEへ画像を送信し、Webサーバでメッセージを受信(Webhook)

  2. Webサーバで受信したメッセージIDを指定して、LINEから画像データを取得

  3. 取得した画像データをBASE64にエンコードして、Google Cloud Vision APIへ送信

  4. Google Cloud Vision APIからの解析結果を編集し、LINEへメッセージ送信

Webサーバの設定、Google Cloud Platform (GCP) の設定は、ここでは割愛します。

FACE_DETECTIONで使用する値について

FACE_DETECTIONを指定すると、認識した顔のパーツの位置情報や感情情報など、
非常に多くの情報が返却されます。とっても多いので、今回は以下の値を使用しました。

・顔検出信頼度
  detectionConfidence

・感情情報
  joyLikelihood(よろこび)
  surpriseLikelihood(おどろき)
  angerLikelihood(いかり)
  sorrowLikelihood(かなしみ)

解析結果の感情情報には、
VERY_UNLIKELY, UNLIKELY, POSSIBLE, LIKELY, VERY_LIKELYが指定されています。

今回のプログラム
// 呼び出し元
msgId = event.getMessage().getId();
List visionMsg = ExternalSrv.googleVision(msgId);
if (visionMsg.isEmpty()) {
  sampleText = "顔認識できませんでした";
  sendSrv.send(createSendObject(sampleText, event.getReplyToken(), true));
} else {
  sendSrv.send(createSendObjectArray(visionMsg, event.getReplyToken()));
}
// LINEから画像データを取得し、 Google Cloud Vision API で解析。
// 解析結果をLINEへ送信するメッセージを作成
public List googleVision(String msgId) {
  // 返却用のメッセージリスト
  ArrayList speechList = new ArrayList();

  // Lineから画像データを取得する
  CloseableHttpClient httpClient = HttpClients.createDefault();
  String targetUrl = appConfig.getContentsUrl().replace("{messageId}", msgId);
  HttpGet request = new HttpGet(targetUrl);
  request.addHeader("Authorization", "Bearer {%s}".replace("%s", appConfig.getChannelAccessToken()));
  CloseableHttpResponse response = null;
  byte[] encodeImg = null;
  try {
    response = httpClient.execute(request);
    HttpEntity entity = response.getEntity();
    encodeImg = Base64.encodeBase64(EntityUtils.toByteArray(entity));
    httpClient.close();
    EntityUtils.consume(entity);
  } catch (Exception ex) {
    ex.printStackTrace();
  }

  // GoogleVisionApiインタフェース
  SendData sData = new SendData();
  List requestList = new ArrayList();
  Requests req = new Requests();
  Image image = new Image();
  try {
    image.setContent(new String(encodeImg, "UTF-8"));
    req.setImage(image);
  } catch (UnsupportedEncodingException e1) {
    e1.printStackTrace();
  }
  List featuresList = new ArrayList();
  Features features = new Features();
  features.setMaxResults("5");
  features.setType("FACE_DETECTION");
  featuresList.add(features);
  req.setFeatures(featuresList);
  requestList.add(req);
  sData.setRequests(requestList);

  // GoogleVisionApi 画像解析
  try {
    httpClient = HttpClients.createDefault();
    StringBuilder urlBuff = new StringBuilder();
    urlBuff.append(appConfig.getGoogleVisionApi());
    urlBuff.append(appConfig.getGoogleVisionApiKey());

    HttpPost postReq = new HttpPost(urlBuff.toString());
    postReq.addHeader("Content-type", "application/json; charset=UTF-8");
    ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(Inclusion.NON_NULL);
    final String json = mapper.writeValueAsString(sData);
    postReq.setEntity(new StringEntity(json, "UTF-8"));
    String result = httpClient.execute(postReq, new ResponseHandler(){
      public String handleResponse(HttpResponse res) throws IOException{
        String result = EntityUtils.toString(res.getEntity(), "UTF-8");
        return result;
      }
    });

    // 画像解析結果からLineへ送信するメッセージの構築
    Result visionMessage = new ObjectMapper().readValue(result, Result.class);
    for (Responses res : visionMessage.getResponses()){
      if (null != res.getFaceAnnotations()) {
        for (FaceAnnotations data : res.getFaceAnnotations()) {
              StringBuilder sb = new StringBuilder();
          sb.append("[顔検出信頼度] : " + data.getDetectionConfidence() + "\n");
          sb.append("楽しそう? : " + data.getJoyLikelihood() + "\n");
          sb.append("驚いてる? : " + data.getSurpriseLikelihood() + "\n");
          sb.append("怒ってる? : " + data.getAngerLikelihood() + "\n");
          sb.append("悲しそう? : " + data.getSorrowLikelihood());
          speechList.add(sb.toString());
        }
      }
    }
    return speechList;
  } catch (Exception e) {
    e.printStackTrace();
    throw new RuntimeException(e);
  }
}
実験

では、早速動かしてみましょう。

今回は、弊社フリー素材のKTNさんにご協力頂きました。
ありがとうございます!

まずは、「よろこび」から。

1 yorokobi.png

楽しそう?のところがVERY_LIKELYになってますねー
その他は、VERY_UNLIKELYなので、VisionAPIは画像からKTNさんの感情を”よろこんでそう”と判定したようです。 いい笑顔?ですねー

次は、「おどろき」を表現してもらいます。

1 odoroki.png

楽しそう?のところがLIKELYになってますねー
驚きの判定はされていません。

次! 「いかり」はどうでしょう。

1 ikari 1.png

・・・

次!

「かなしみ」です。

1 kanashimi 1.png

・・・。

表現変えてチャレンジ!

1 kanashimi 2.png

顔写ってないから、そりゃそうなりますよね。
ごめんなさい。KTNさん。

最後に弊社のPepper君はどうでしょうか?

1 pepper.png

正解!!

感想

凄いですね。今回はその画像を作るのが悪かったようです。

おわり