RailsにGoogle Cloud Vision APIを導入し、簡単に過激な画像を検知する

## はじめに

### Google Cloud Vision APIとは？

GCP の画像分析 API で、画像に写っている物を判定して、何が写っている画像かを判定したりできるサービスとなります

https://cloud.google.com/vision/?hl=ja

今回はこの API のセーフサーチ機能を Rails から利用して、アップロードされた画像が過激な画像だった場合に検知する仕組みを実装していきます。

## 環境

Ruby: `2.5.3`
Rails: `5.2`
gem google-cloud-vision: `0.32.2`

## Cloud Vision の導入

- まずは Cloud Vision API を利用するため GCP のサービスアカウントの json ファイルをダウンロードします

<img width="1001" alt="スクリーンショット 2019-02-07 17.34.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/191598/4fac1f8a-3588-6c8b-3582-b147e533f26e.png">

- ダウンロードしたらその json ファイルをプロジェクトルートに置きます

  - ※本番環境で扱う場合は Base64 でエンコードして Rails の credential 等で管理したほうがいいです、今回は例の為この方法で行きます。

- 次に[google-cloud-vision](https://github.com/googleapis/google-cloud-ruby/tree/master/google-cloud-vision)という gem を導入します

```Gemfile
gem 'google-cloud-vision', require: false
```

- `bundle install` を実行します

これで導入は完了です!!

## Cloud Vision API の不適切画像の評価について

Cloud Vision API では不適切画像かどうかを以下のカテゴリで判定します

| カテゴリ |                                       内容 |
| :------- | -----------------------------------------: |
| adult    |                 アダルト画像かどうか |
| spoof    |               インターネットミームかどうか |
| medical  | 病気の画像や手術などの医療的な画像かどうか |
| violence |                 暴力的な画像(グロ)かどうか |
| racy     |                         際どい画像かどうか |

またどのくらいそのカテゴリに該当する可能性があるかを以下のように評価します

| 評価値        | 不適切な可能性の度合い |
| :------------ | ---------------------: |
| UNKNOWN       |                   解析不可 |
| VERY_UNLIKELY |             非常に低い |
| UNLIKELY      |                   低い |
| POSSIBLE      |           可能性あり |
| LIKELY        |                   高い |
| VERY_LIKELY   |             非常に高い |

今回は画像をアップして LIKELY 以上の判定が出た場合、つまり結果が`LIKELY`か`VERY_LIKELY`の場合は不適切な画像とします。

## 判定してみる

いよいよ画像判定の準備ができました！
今回は例として

- Hoge モデルの image カラムに画像を保存するが、不適切な画像は保存しない

という処理を実装してみましょう！

まずはリクエストから

```ruby
image_annotator = Google::Cloud::Vision::ImageAnnotator.new(
    version: :v1,
    credentials: JSON.parse(File.open("key.jsonのpath"))
)

# リクエストパラメータ作成
image = "検知させたい画像"
requests_content = { image: { content: image }, features: [{ type: :SAFE_SEARCH_DETECTION }] }
requests =   [requests_content]

# Cloud Vision APIに画像を送信
image_annotator.batch_annotate_images(requests)
```

- `image_annotator.batch_annotate_images(requests)`の結果は以下のようになります

```ruby

<Google::Cloud::Vision::V1::BatchAnnotateImagesResponse: responses:
  [
    <Google::Cloud::Vision::V1::AnnotateImageResponse:
      face_annotations: [],
      landmark_annotations: [],
      logo_annotations: [],
      label_annotations: [],
      text_annotations: [],
      safe_search_annotation: <Google::Cloud::Vision::V1::SafeSearchAnnotation: adult: :VERY_UNLIKELY, spoof: :UNLIKELY, medical: :VERY_UNLIKELY, violence: :VERY_UNLIKELY, racy: :VERY_UNLIKELY>,
      image_properties_annotation: nil,
      error: nil,
      crop_hints_annotation: nil,
      full_text_annotation: nil,
      web_detection: nil,
      product_search_results: nil,
      context: nil,
      localized_object_annotations: []
    >
  ]
>
```

- 色々値がありますが、不適切な画像を検知する時に必要なのは`safe_search_annotation`の値だけですのでこちらを取得するようにしましょう。

```ruby
# Cloud Vision APIに画像を送信
response = image_annotator.batch_annotate_images(requests)
result = response.responses[0].safe_search_annotation.to_h 
# => {:adult=>:VERY_UNLIKELY, :spoof=>:UNLIKELY, :medical=>:VERY_UNLIKELY, :violence=>:VERY_UNLIKELY, :racy=>:VERY_UNLIKELY}
```

- 取得した結果を元に Ruby で判定していきます。

```ruby

# 不適切な画像の疑いがある場合は保存しない
return if result.value?(:LIKELY) || result.value?(:VERY_LIKELY)
Hoge.create(image: image)
```

これで`LIKELY`、`VERY_LIKELY`の評価が出た時は処理が終了するようになりました
つまり Hoge モデルにはある程度健全な画像のみが保存されることになります！

最終的なコードの流れは以下となります。

```ruby
@image_annotator = Google::Cloud::Vision::ImageAnnotator.new(
    version: :v1,
    credentials: JSON.parse(File.open("key.jsonのpath"))
)

# リクエストパラメータ作成
image = "検知させたい画像"
requests_content = { image: { content: image }, features: [{ type: :SAFE_SEARCH_DETECTION }] }
requests =   [requests_content]

# Cloud Vision APIに画像を送信
response = image_annotator.batch_annotate_images(requests)
result = response.responses[0].safe_search_annotation.to_h 
# => {:adult=>:VERY_UNLIKELY, :spoof=>:UNLIKELY, :medical=>:VERY_UNLIKELY, :violence=>:VERY_UNLIKELY, :racy=>:VERY_UNLIKELY}

# 不適切な画像の疑いがある場合は保存しない
return if result.value?(:LIKELY) || result.value?(:VERY_LIKELY)
Hoge.create(image: image)

```

## まとめ

こんな感じで機械学習などの知識を使わないでも、Railsで不適切な画像を簡単に検知できるようになりました！

今回は不適切画像検知を使ってみましたが、Google Cloud Vision APIには他にも、色々な検知の機能があるので、それを使っていくのも面白そうです。

## 参考

 - 公式
   - https://cloud.google.com/vision/?hl=ja
 - google-cloud-vision
   - https://github.com/googleapis/google-cloud-ruby/tree/master/google-cloud-vision
   - http://googleapis.github.io/google-cloud-ruby/docs/google-cloud-vision/v0.32.2/