はじめに
おつかれさまです。
おおくまです。
今回はRailsで実装した個人開発のアプリにGoogle Cloud Vision APIを導入し、不適切な画像をバリデーションしてみました。
というのも、先日、そういったことが起こったので、対策として実装してみました。笑
注意
私は今年の4月からプログラミング学習を始めました。
内容に誤りがある場合がございます。
あらかじめご了承ください。
また、コメント等で教えていただけると幸甚です🙇
Cloud Vision APIとは
Cloud Vision APIとは、Google Cloud Platformで提供されている画像処理サービスです。
このAPIを利用することで、画像を分析し、テキスト検出、物体検出、顔認識、ラベル付けなどのさまざまなことができます。
こちらのリンクから試すことができます。
Google APIキーの取得
まずはAPI ライブラリ – API とサービスにアクセスして、Cloud Vision APIを探して、クリックします。
そして、「有効にする」をクリックします。
すると、Googleアカウントでログインしていない方は、ログインが求められますので、ログインします。
このような画面になるかと思いますので、「プロジェクトを作成」をクリックします。
プロジェクト名を入力し、「作成」をクリックします。
Cloud Vision APIの画面に戻ると思いますので、再度「有効にする」をクリックします。
このような画面になるかと思いますので、「認証情報を作成」をクリックします。
「ユーザーデータ」にチェック → 「次へ」をクリック → 「アプリ名」、「ユーザーサポートメール」、「デベロッパーの連絡先情報 メールアドレス」を入力 → 「保存して次へ」をクリック → スコープは省略可なので、「保存して次へ」をクリック → アプリケーションの種類「ウェブアプリケーション」を選択し、名前にアプリ名を入力 → 「作成」をクリック → 「完了」をクリック
次に、左のサイドバーの「認証情報」をクリック → 上の方にある「認証情報を作成」をクリック → 「APIキー」をクリック
するとAPIキーが作成されますので、これを控えておきます。
Railsに実装する
今回はCloud Vision AIの「SAFE_SEARCH_DETECTION」という機能を使用します。
この機能は、Cloud Vision AIの機能の1つで、与えられた画像に含まれる可能性のある不適切なコンテンツを検出するための機能です。
SAFE_SEARCH_DETECTIONは与えられた画像について5つの観点から6段階で評価してくれます。
詳しくはこれらのリンクを参考してください。
不適切なコンテンツを検出する(セーフサーチ)
SafeSearchAnnotation
まず、先ほど取得したAPIキーを.envファイルに記載します。
GOOGLE_API_KEY="先ほど取得したAPIキー"
次にlib
フォルダの直下にvision.rb
を作成します。
require "base64"
require "json"
require "net/https"
module Vision
class << self
def image_analysis(image_file)
api_url = "https://vision.googleapis.com/v1/images:annotate?key=#{ENV['GOOGLE_API_KEY']}"
base64_image = Base64.encode64(image_file.tempfile.read)
params = {
requests: [{
image: {
content: base64_image
},
features: [
{
type: "SAFE_SEARCH_DETECTION"
}
]
}]
}.to_json
uri = URI.parse(api_url)
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request["Content-Type"] = "application/json"
response = https.request(request, params)
result = JSON.parse(response.body)
if (error = result["responses"][0]["error"]).present?
raise error["message"]
else
result_arr = result["responses"].flatten.map do |parsed_image|
parsed_image["safeSearchAnnotation"].values
end.flatten
if result_arr.include?("LIKELY") || result_arr.include?("VERY_LIKELY")
false
else
true
end
end
end
end
end
コードの内容としては、先ほどの5つの観点のうち1つでも"LIKELY"か"VERY_LIKELY"の判定を受けるとfalseを返すコードになっています。
次にlib/vision.rb
を読み込むためにconfig/application.rb
に以下の記述を追記します。
module ZooMania
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s]
config.i18n.default_locale = :ja
+ config.paths.add "lib", eager_load: true #この1行を追記
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
end
end
私のアプリではPostテーブルにimageというカラムがあり、それが画像投稿機能と結びついています。
なので、今回はPostコントローラのcreateアクションとupdateアクションを編集していきます。
def new
@post = Post.new
end
def create
@post = current_user.posts.build(post_params)
if post_params[:image].present?
result = Vision.image_analysis(post_params[:image])
if result
if @post.save
redirect_to post_path(@post), notice: t('.success_create_post')
else
flash.now['danger'] = t('.fail_create_post')
render :new, status: :unprocessable_entity
end
else
flash.now['danger'] = t('defaults.inappropriate_image')
render :new, status: :unprocessable_entity
end
elsif @post.save
else
flash.now['danger'] = t('.fail_create_post')
render :new, status: :unprocessable_entity
end
end
def edit
@post = current_user.posts.find(params[:id])
end
def update
@post = current_user.posts.find(params[:id])
if post_params[:image].present?
result = Vision.image_analysis(post_params[:image])
if result
if @post.update(post_params)
redirect_to post_path(@post), notice: t('.success_update_post')
else
flash.now['danger'] = t('.fail_update_post')
render :edit, status: :unprocessable_entity
end
else
flash.now['danger'] = t('defaults.inappropriate_image')
render :new, status: :unprocessable_entity
end
elsif @post.update(post_params)
redirect_to post_path(@post), notice: t('.success_update_post')
else
flash.now['danger'] = t('.fail_update_post')
render :new, status: :unprocessable_entity
end
end
クソコードなので、これからリファクタリング頑張ります。
先ほどlib/vision.rb
で定義したメソッドをcreateアクションとupdateアクションで呼び出しており、falseの場合は不適切な画像として投稿できない仕組みになっています。
動作
まとめ
今回、初めて使ったAPIでしたが、無事に実装できてよかったです。
こういったハプニングや予期せぬことを繰り返さないために機能を追加していくことも、アプリケーションを運用する醍醐味かと思いますので、今後も機能追加を頑張りたいと思います。
最後まで読んでいただき、ありがとうございました。