Google Cloud Vision API でサンタ画像判定アプリをつくる @Rails4

**一体サンタクロースはどこからがサンタクロースなのだろうか...？？？**

赤い帽子をかぶって白い髭を生やしていればサンタなのか、、、それともトナカイにソリを引かせていればサンタなのか。。。
クリスマス前日に子供のためにおもちゃを選ぶお父さんはもう立派なサンタさんなんじゃないのか。

そんな議論は今日もつきません。

ならば世界最高峰全知全能の**Googleに一対どこまでがサンタでどこまでがサンタでないのか判定してもらおうではありませんか。**

ということで今回はGoogleが提供する**画像解析APIのGoogle Cloud Vision API**を使って、
**「投稿した画像がサンタなのかそうでないのか世界のGoogleが判定がする」アプリ**を作りました。

TECH::CAMPアドベントカレンダーの9日目を担当させていただきます！

# まずはアプリ紹介から

[アプリはこちらで遊べます](https://this-is-santa.herokuapp.com/)
【2017/1/25追記】APIの無料トライアル期間が切れてもう画像判定できなくなりました(泣)

小学生の図工みたいなUIの投稿画面で、ファイルを選択ボタンから、
**サンタっぽい画像を選んで "This is Santa" ボタンを押すと、、、**

![real_santa.gif](https://qiita-image-store.s3.amazonaws.com/0/92468/24852eb2-225d-1416-f05f-65498c4ed32e.gif)


**This is Santa!!!**こうなります！

**Google Cloud Vision APIが投稿した画像をサンタかどうか解析して、判別してくれます。**
ちなみにこの結果画面の画像は "This is Santa" って適当にぐぐってヒットした画像を適当に使ってます。

すごいですね。こんなことができてしまう世の中がくるなんて、、、。

また、**サンタっぽくない画像を選んでしまうと、**

![danbulu.gif](https://qiita-image-store.s3.amazonaws.com/0/92468/7badef74-0c78-dfc6-67b9-30286f2b2c1e.gif)


**This is not Santa!!!**こうなります！

これは**Google Cloud Vision APIがサンタの画像として認識しなかったようですね。**（この画像は逆に "This is not Santa" でぐぐってヒットした画像を使っております。）

あわよくばサンタと混乱しないかなとばかりに、**わりとサンタっぽいどっかの学校の校長先生をもってきた**んですが、さすがGoogle、騙されませんでした。ファンタスティック。

ちなみにアウトした場合は何と勘違いしているのか気になるので、下に判定結果の一部を表示しています。

ちなみに、サンタコスとかはどうなんでしょうね。ということで**橋本環奈のサンタコス**をいれてみると、、

![kanna.gif](https://qiita-image-store.s3.amazonaws.com/0/92468/d2a1f86f-0c72-dfce-1587-fff1da4a0966.gif)

。。アウト！アウトでした。。服が白いからですかね？

ではこれはどうでしょう？？**ちっさい子のサンタコス**、今度は服が赤くてよりサンタっぽいですが、サンタさんのチャームポイントの白い髭がないですね。いけますかね。。

![santa_baby.gif](https://qiita-image-store.s3.amazonaws.com/0/92468/a991887d-f2ec-6661-208e-d8629dd22421.gif)


おおお！よっしゃ！いけた！判定基準は不明ですが、素晴らしいGoogle Cloud Vision API。。。

ということで、お遊びはここらへんにして、これから本題に移っていきます。

以下、目次となっております。

[遊びたくなった方、アプリはこちらです！](https://this-is-santa.herokuapp.com/)

# 目次
・ Google Cloud Vision APIとは？

・ 環境

・ Railsの基本実装

・ Google Cloud Vision API周りの実装

・ まとめ

・ 反省

# Google Cloud Vision APIとは？
**Google Cloud Vision APIとは、Google Cloud Platform(GCP)が提供する機械学習サービスの一種です。パワフルな機械学習モデルにより画像の内容を認識します。**
例えば猫の画像を解析すると"cat"という文字列を返してくれたり、人の表情も感知できて、怒っている人の顔を怒っていると感知することもできます。
詳しくは[公式ドキュメント](https://cloud.google.com/vision/?hl=ja&utm_source=google&utm_medium=cpc&utm_campaign=2015-q1-cloud-japac-jp-vision-bkws-freetrial-jap&utm_term=%2Bgoogle%20%2Bcloud%20%2Bvision%20%2Bapi&gclid=Cj0KEQiApqTCBRC-977Hi9Ov8pkBEiQA5B_ipc3pmAqAmxA-Fyo0AE16h1kDZA0q88_pqqDcs3Htdn0aAjSU8P8HAQ)で。

精度も上で試してみたとおり素晴らしいです。ちなみに橋本環奈だと「グラビアアイドル」や「日本人アイドル」とか出てきます。おそろしい。

# 環境
Ruby 2.1.8
Rails 4.2.6

# Railsの基本実装
ここらへんはわりとRailsの知識があること前提に。。。

今回のアプリの流れは、
投稿画面（new action）→保存処理（create action）
→createアクションの中でモデルのインスタンスメソッドを呼び出す
→インスタンスメソッド内でGoogle Cloud Visionクラスのインスタンスを生成
→インスタンスメソッド内でGoogle Cloud Vision APIにリクエスト
→取得したデータ結果をビューに表示、でいきます。

## Routing
```routes.rb
Rails.application.routes.draw do
  root 'images#new'
  resources :images, only: [:new, :create]
  namespace :api do
    namespace :v1 do
      post 'check_santa' => 'images#check_santa'
    end
  end
end
```

## Controller
```images_controller.rb
class ImagesController < ApplicationController
  protect_from_forgery except: :create

  def new
    @image = Image.new
  end

  def create
    @image = Image.create(image_params)
    result = @image.check_santa
    # check_santaメソッドでサンタかどうか判別する。resultにはサンタかどうかと判別結果をハッシュ形式で取得する。
    @santa_status = result[:santa_status]
    # アップした画像がサンタ判定されたらtrue、されないならfalseをいれます。
    @descriptions = result[:descriptions][0..4]
    # サンタ判定されなかったものはなんて判定されたか気になるので、判定結果も一応変数に保持しておきます。
  end

  private

  def image_params
    params.require(:image).permit(:image)
  end
end
```

## Model
今回はimageカラムをもつimagesテーブルとimageモデルを作成しました。imageカラムにはcarrierwaveのuploderクラスを紐付けています。

```image.rb
class Image < ActiveRecord::Base
  mount_uploader :image, ImageUploader
  # ここは好みでcarrierwaveとか使ってください。画像のパスを生成しやすくするために使っています。

  def check_santa
    descriptions = GoogleCloudVision.new(self.image.path).request
  # APIの処理はそれようにクラスを作ったのでそこのインスタンスを作成します。画像を分析した結果の文字列を返り値として返すのがゴール。
    parse_result(descriptions)
  end

  def parse_result(descriptions)
    {
      santa_status: fetch_santa_status(descriptions),
      descriptions: descriptions
    }
  end

  # ここでサンタの画像から解析した文字列の配列をSettings.santa_wordsに格納されたサンタワードと照合してサンタかどうか確認します。
  def fetch_santa_status(descriptions)
    Settings.santa_words.each do |santa_word|
      descriptions.each do |description|
        return true if description.downcase.include?(santa_word)
      end
    end
    return false
  end
end
```

## View

**ビューは適当です（土下座）**

```new.slim
= form_for @image, remote: true do |f|
  = f.file_field :image, id: "js-file"
  = f.submit "This is Santa", style: "font-size: 16px;"

br
img#js-image style='height: 500px;'
```

```create.slim
- if @santa
  = image_tag("this_is_santa", id: "js-image", style: 'height: 500px;')

  h1 style="font-size: 30px;"
    | This is Santa!!! Good job!!!
- else
  = image_tag("not_santa", style: 'height: 500px;')
  div
    | This might be 
    h2
      - @descriptions.each do |description|
        = description
        | , or 
      | ...

= button_to '戻る', new_image_path, method: 'get'
```

## Carrierwave
デフォルトだと外部API用にうまく画像のパスが生成されない（http://とかlocalhost〜とかつかない）のでちょっといじる必要があります。

```initializers/carrierwave.rb
CarrierWave.configure do |config|
  config.asset_host = Settings.url
end
```

```settings.yml
url: 'http://localhost:3000'
```

# Google Cloud Vision API周りの実装
はい！やっと本題ですね。ここからAPIへのリクエストについて話していきます！
Google Cloud Vision APIで画像解析をするときに必要なことは以下の２つです。

**①API Keyの取得**

**②リクエストの作成**

## API KEYの取得
APIの取得は、こちらの記事がとても参考になります。
[Cloud Vision APIの使い方まとめ (サンプルコード付き)](https://syncer.jp/cloud-vision-api)

こちらの記事が死ぬほど参考になるので、ここでは割愛させていただきます。 **とにかく上の記事でAPIのキーをゲットしてきてください。検討を祈ります。それ以外いりません。**

クレジットカードの登録が必要になりますが、無料期間を超えて勝手に請求される仕様ではないようですね。。60日間は無料利用できるらしいです。

取得したキーはSettings.ymlとか.envファイルとかで定数管理してください。publicなリモートリポジトリにプッシュしないように。

今回はSettings.ymlに定数として値を保持しておきます。

```settings.yml
api_key: 'hogehogehogehoge'
santa_words: ['サンタ', 'santa', 'father christmas', '聖誕老人', 'Дед мороз', 'joulupukki', 'christkind', 'julenissen', 'père noël', 'papa noel']
ちなみに先程モデルファイルで使った定数のsanta_wordsには世界中のサンタクロースの呼び名を入れておきます。ここらへんとか自然言語処理のAPIとかいれたらこんなキモいコード書かずにいけそうですね。
```

## リクエストの作成
Google Cloud Vision APIへのリクエストは以下の3点を気をつけてください。
**① 画像パスをBase64にエンコードする**
**② postメソッドでリクエストする**
**③ jsonデータで送る**

今回は 'httpclient' というhttpのリクエストのgemを使って実装します。

```google_cloud_vision.rb
class GoogleCloudVision
  attr_accessor :endpoint_uri, :file_path

  def initialize(file_path)
    @endpoint_uri = "https://vision.googleapis.com/v1/images:annotate?key=#{Settings.api_key}"
    @file_path = file_path
  end

  def request
    http_client = HTTPClient.new
    content = Base64.strict_encode64(File.new(file_path, 'rb').read)
    # 画像パスをBase64にエンコード
    response = http_client.post_content(endpoint_uri, request_json(content), 'Content-Type' => 'application/json')
    # リクエストの作成。request_jsonメソッドでjsonデータを生成
    descriptions = fetch_descriptions(response)
    # レスポンスから解析結果の文字列を配列として抽出。（["Santa Clause", "Christmas"]とかが理想）
  end

  def request_json(content)
    {
      requests: [{
        image: {
          content: content
          # ここにエンコードした画像パスを
        },
        features: [{
          type: "LABEL_DETECTION",
          maxResults: 10
          # 結果の取得数。なんでも大丈夫です。
        }]
      }]
    }.to_json
  end

  def fetch_descriptions(response)
    result = JSON.parse(response)['responses'].first
    result['labelAnnotations'].map{ |label| label['description'] }
  end
end
```

ココらへんの実装は [icb54615さんのRailsからGoogle Cloud Vision APIを使ってみる](http://qiita.com/icb54615/items/f83d0294528b660e9383) をめちゃくちゃ参考にしました。ありがとうございます。

はい、これで完成したはずです！ぼくのは投稿画面で画像のプレビューができるようになっていますが、そこは割愛させていだきます。

# まとめ
どうでしたでしょうか？さすが世界のGoogle。　誰しもがこの最高レベルの技術を簡単にアクセスできる時代になってくるのですね。素晴らしいです。みなさんもこれを機に機械学習系のAPIと遊んでみてはいかがでしょうか？？

# 反省
以下、今回の反省を少し、、

・ 新たな技術的発信というよりは、ネタ記事っぽくなってしまって貢献度の低い記事になってしまった（せめてajax関数とかつかってリクエスト飛ばせばよかった。）
・ 顔の表情とかもスコアリングできるようなので、もっとそこらへんにも触れたかった。


では皆さん、今回はここまでということで。。
**良いお年をーー！**