はじめに
RailsでFace APIを扱う記事が少なく実装に一手間かかってしまったので、
初歩的ですが今後同じように開発する方がスムーズに実装できるようにこの記事が手助けになれば幸いです!m(_ _)m
この記事に期待できること
- RailsでFace APIを扱うためのコードを理解できる
- リクエストするデータについての理解が深まる(おまけ)
前提としてREST APIについての知識があればより理解してもらえると思います。
Face APIについて
本題に入る前にFace APIとは?の説明をしておきます。
Microsoftが提供するAzure APIと呼ばれるサービスの一つです。
実装にあたりAPIキーを必要とするのでアカウント登録をしておく必要があります。
登録方法は沢山記事があるのでそちらを参考にしてください。
私はこの当たりの記事を参考にしたと思います。
今回は感情データのみ扱いますが、
もちろん感情データ以外の取得も可能なので詳しくは公式を確認して下さい。
やりたいこと
本記事では任意の画像データをDBに保存しないで直接APIリクエストし、
感情データのレスポンスを取得する方法を説明したいと思います。
見易くすると以下のようなデータを取得します↓
[
{
"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キーは各々違うので書き換えて下さいね。
gem 'slim-rails'
gem 'dotenv-rails'
Rails.application.routes.draw do
root 'home#top'
post 'check', to: 'check#result'
end
= form_with url: check_path do |f|
= f.file_field :image
= f.submit 'check'
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}}}]"
ちゃんと返ってきてますね◎
まとめ
お手紙のやり取りに例えると
- ①宛先書いて
- ②ハガキではなく封筒で
- ③手紙本文入れて
- ④見出し&添え書きして
- ⑤郵送受け渡し
- ⑥お返事を開封
といった感じに近いでしょうか。
個人的にハマったこと(おまけ)
- バイナリデータをテキスト化して読み込ませてしまっていた(´Д` )
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でリクエストしていました(笑)
因みに
body = params[:image].read
の中身は
=> "\xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x00\x00H\x00...
のようになり、
body = Base64.strict_encode64(params[:image].read)
の中身は
=> "/9j/4AAQSkZJRgABAQAASABIAAD/4UvnRXhpZgAATU0AKgAAAAgAB...
のようになります。
全然違いましたね^^;
おわりに
今回はFaceAPIの本当に導入だけの内容でしたが、
何となく動いた〜で終わらず噛み砕くことも大事だと思ってまとめてみました。
もし誤った記載があればご指摘頂けると幸いです!