23
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RUNTEQAdvent Calendar 2021

Day 19

RailsでFace APIを扱うので噛み砕いてみた◎

Last updated at Posted at 2021-12-18

はじめに

RailsでFace APIを扱う記事が少なく実装に一手間かかってしまったので、
初歩的ですが今後同じように開発する方がスムーズに実装できるようにこの記事が手助けになれば幸いです!m(_ _)m

この記事に期待できること

  • RailsでFace APIを扱うためのコードを理解できる
  • リクエストするデータについての理解が深まる(おまけ)

前提としてREST APIについての知識があればより理解してもらえると思います。

Face APIについて

本題に入る前にFace APIとは?の説明をしておきます。

Microsoftが提供するAzure APIと呼ばれるサービスの一つです。

公式サイトはこちら
Face API詳細についてはこちら

実装にあたりAPIキーを必要とするのでアカウント登録をしておく必要があります。
登録方法は沢山記事があるのでそちらを参考にしてください。
私はこの当たりの記事を参考にしたと思います。

今回は感情データのみ扱いますが、
もちろん感情データ以外の取得も可能なので詳しくは公式を確認して下さい。

やりたいこと

本記事では任意の画像データをDBに保存しないで直接APIリクエストし、
感情データのレスポンスを取得する方法を説明したいと思います。

こんな感じ◎
※添付データはもちろん顔写真
Image from Gyazo

見易くすると以下のようなデータを取得します↓

JSON形式
[
    {
        "faceId": "c5c24a82-6845-4031-9d5d-978df9175426",
        "faceRectangle": {
            "width": 78,
            "height": 78,
            "left": 394,
            "top": 54
        },
        "faceAttributes": {
            "emotion": {
                "anger": 0.575,
                "contempt": 0,
                "disgust": 0.006,
                "fear": 0.008,
                "happiness": 0.394,
                "neutral": 0.013,
                "sadness": 0,
                "surprise": 0.004
            },
        }
    }
]

完成コード

まずは結論!(これで動くはず〜)
※エンドポイントとかAPIキーは各々違うので書き換えて下さいね。

Gemfile(追加)
gem 'slim-rails'
gem 'dotenv-rails'
routes.rb
Rails.application.routes.draw do
  root 'home#top'
  post 'check', to: 'check#result'
end
top.html.slim
= form_with url: check_path do |f|
  = f.file_field :image
  = f.submit 'check'
check_controller.rb
class CheckController < ApplicationController
  def result
    # ①URI指定
    uri = URI.parse("https://japanwest.api.cognitive.microsoft.com/face/v1.0/detect")
    uri.query = URI.encode_www_form({
      "returnFaceAttributes" => "emotion"
    })

    # ②https通信の準備
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme === "https"
    
    # ③画像データをバイナリで渡す
    body = params[:image].read
    
    # ④バイナリ&APIキーを指定
    headers = { 
      "Content-Type" => "application/octet-stream",
      "Ocp-Apim-Subscription-Key" => ENV["API_KEY"]
    }

    # ⑤POSTした引数データのレスポンスを代入
    response = http.post(uri, body, headers)
    
    # ⑥JSON形式でレスポンスを確認
    render json: response.body
  end
end

コードを噛み砕く

FaceAPIに関わるコントローラー箇所だけ補足説明していきます。
因みに公式のサンプルコードだと何故か動かなかったのでこちらの記事を参考に違う書き方をしました。
(何故だったのか未だに分かっていない、、)

①URI指定

URI.parse

引数のURLをURIオブジェクトとして生成しています。
※FaceAPI登録時のエンドポイントの設定によってURLは変わります。

URI.encode_www_form

引数からapplication/x-www-form-urlencoded(Content-Type ※後述)形式の文字列を生成しています。

"returnFaceAttributes" => "emotion"

FaceAPIから感情取得するためのハッシュです。
他にも色々あるので気になる方は公式で確認して下さい。

uri.query

クエリ(上記で生成されたreturnFaceAttributes=emotion)を追加して
https://japanwest.api.cognitive.microsoft.com/face/v1.0/detect?returnFaceAttributes=emotion
を生成しています。

②https通信の準備

Net::HTTP.new

Net::HTTPオブジェクトを生成しています。

uri.host

ホスト名(ここではjapanwest.api.cognitive.microsoft.com)が返ります。

uri.port

ポート番号(ここではhttpsの443)が返ります。

uri.scheme === "https"

uri.scheme"https"であるか(真偽値)を返しています。

http.use_ssl

trueの場合)https通信ができるようになります。

③画像データをバイナリで渡す

params[:image].read

f.file_field :imageで添付された画像データを読み込んでいます。

④バイナリ&APIキーを指定

"Content-Type" => "application/octet-stream"

"Content-Type" はデータの種類を示す情報を指定する項目のこと。
※Face APIでは"application/octet-stream"(バイナリ指定)、
もしくはapplication/json(url指定)のみ使用できることが公式に明記されています。

"Ocp-Apim-Subscription-Key" => ENV["API_KEY"]

Face API登録時に発行したAPIキーを指定しています。
実際のキーはgem 'dotenv-rails'で.envファイルに記載してセキュリティ対策しています。

⑤POSTした引数データのレスポンスを代入

http.post(uri, body, headers)

ここまで準備した変数達をFace APIにリクエストしています。
WEB APIにはPOSTリクエストしてもGET相当の結果が返ってくるものもあるんだとか。

⑥JSON形式でレスポンスを確認

render json: response.body

renderメソッドは呼び出すテンプレート(ビュー)ファイルを指定しますが、
オプションにjsonをつける事で指定したオブジェクト(response.body)をJSON形式のデータにして返しています。

response.body
=> "[{\"faceId\":\"73ec49e2-e53c-49ef-b8e1-4e9640907434\",\"faceRectangle\":{\"top\":118,\"left\":116,\"width\":168,\"height\":168},\"faceAttributes\":{\"emotion\":{\"anger\":0.0,\"contempt\":0.0,\"disgust\":0.0,\"fear\":0.0,\"happiness\":0.0,\"neutral\":0.998,\"sadness\":0.001,\"surprise\":0.0}}}]"

ちゃんと返ってきてますね◎

まとめ

お手紙のやり取りに例えると

  • ①宛先書いて
  • ②ハガキではなく封筒で
  • ③手紙本文入れて
  • ④見出し&添え書きして
  • ⑤郵送受け渡し
  • ⑥お返事を開封

といった感じに近いでしょうか。

個人的にハマったこと(おまけ)

  • バイナリデータをテキスト化して読み込ませてしまっていた(´Д` )
check_controller.rb
body = Base64.strict_encode64(params[:image].read)

これは間違い×

(以下ウィキペディア参照)

Base64 is a group of binary-to-text encoding schemes that represent binary data (more specifically, a sequence of 8-bit bytes) in an ASCII string format by translating the data into a radix-64 representation.

Base64はバイナリをテキストエンコーディングしてるものですよ〜とばっちり書いてありました!
この違いを理解していなくて一生Base64でリクエストしていました(笑)

因みに

check_controller.rb
body = params[:image].read

の中身は

=> "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00H\x00...

のようになり、

check_controller.rb
body = Base64.strict_encode64(params[:image].read)

の中身は

=> "/9j/4AAQSkZJRgABAQAASABIAAD/4UvnRXhpZgAATU0AKgAAAAgAB...

のようになります。

全然違いましたね^^;

おわりに

今回はFaceAPIの本当に導入だけの内容でしたが、
何となく動いた〜で終わらず噛み砕くことも大事だと思ってまとめてみました。
もし誤った記載があればご指摘頂けると幸いです!

参考記事

23
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?