LoginSignup
1
0

More than 1 year has passed since last update.

AmazonRekognitionを使って画像検索機能を作る

Last updated at Posted at 2021-07-20

はじめに

はじめて外部APIを使ってみたのでその内容を書きます。

順序

1:画像を送信する。

2:送信された画像をawsのs3を使って、bucketを指定して保存する。

3:rubyからAmazon RekognitonのAPIを叩いて、先程保存した画像を画像認識して、ラベルを取得する。

4:取得したラベルをAmazon Translateを使って日本語翻訳して、そのワードを元にフリーワード検索をしてデータを取得する。

awsのIAMユーザーの作成

まず、awsのIAMにアクセスしてユーザーを作成します。

その際にAccess key IDSecret access keyが作成されるので、保管して置いてください。

また、Amazon s3Amazon RekognitonAmazon Translateを使用するので、それらのアクセス許可のポリシーをアタッチしてください。

詳しい方法についてはドキュメントを参照してください。

[Amazon S3 とは - Amazon Simple Storage] (Service[https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Welcome.html:embed:cite)

[Amazon Rekognition とは - Amazon Rekognition]
(https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/what-is.html:embed:cite)

[Amazon Translate とは - Amazon Translate]
(https://docs.aws.amazon.com/ja_jp/translate/latest/dg/what-is.html:embed:cite)

aws s3のバケットを作成

続いて、s3にアクセスしてバケットを作成します。

やることとしては、任意のバケット名をつけることと、リージョンを設定するだけです。

詳しくはドキュメントを参照してください。

[Amazon S3 バケットの作成、設定、操作 - Amazon Simple Storage Service]
(https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/creating-buckets-s3.html:embed:cite)

環境変数を設定

先程保管しておいたしておいた、Access key IDSecret access keyを環境変数に設定します。

.zprofileまたは.bash_profile
export AWS_ACCESS_KEY_ID=<取得したAccess key ID>
export AWS_SECRET_ACCESS_KEY=<取得したSecret access key>

ここまでの設定が終わったら、本題の画像検索機能を実装していきます。

Gemのインストール

まずは下記のgemをインストールします。

Gemfile
gem 'aws-sdk-s3'
gem 'aws-sdk-rekognition'
gem 'aws-sdk-translate'

画像のアップロード画面を作成

続いて画像入力のビューを作ります。

※active storage使ってます

画像のアップロード画面のビュー
              <%= form_with url: image_search_bugs_path, method: :post, local: true do |f| %>
                <div class="form-group mb-3">
                  <p style="color: red;">※ファイル形式は`png``jpeg`でお願いします。</p>
                  <%= f.file_field :image, class: 'form-control', id: 'input_image', accept: 'image/png, image/jpeg' %>
                </div>
                <div class="mb-3">
                  <%= image_tag '', id: 'image_preview', style: 'display: none;' %>
                </div>
                <div class="actions mb-3">
                  <%= f.submit '検索する', class: 'btn btn-primary' %>
                </div>
              <% end %>

画像をs3に保存する

続いて、取得した画像をs3に保存します。

search_images_helper.rb
  def upload_image(data)
    credentials = Aws::Credentials.new(
      ENV['AWS_ACCESS_KEY_ID'],
      ENV['AWS_SECRET_ACCESS_KEY']
    )
    region = 'us-east-1'

    # アップロードされた画像をs3に保存
    s3_client = Aws::S3::Client.new(region: region, credentials: credentials)

    body = data
    bucket = '先程作成したバケット名'
    key = "任意の名前"

    s3_client.put_object({
      body: body,
      bucket: bucket,
      key: key
    })

    put_labels(key, s3_client)
  end

upload_imageというメソッドを作って、画像のデータを引数として受け取ります。

    credentials = Aws::Credentials.new(
      ENV['AWS_ACCESS_KEY_ID'],
      ENV['AWS_SECRET_ACCESS_KEY']
    )
    region = 'us-east-1'

credentialsregionを定義します。

regions3のバケットを作成したときに設定したものを指定します。

    s3_client = Aws::S3::Client.new(region: region, credentials: credentials)

    body = data
    bucket = 'article-image-search'
    key = "article_image"

    s3_client.put_object({
      body: body,
      bucket: bucket,
      key: key
    })

    put_labels(key, s3_client, credentials, bucket)

bodyに引数で受け取った画像データを代入します。

bucketには先程作成したバケット名を定義します。

keyには任意の値をしていします。

put_object メソッドを使用して、s3に画像を保存します。

最後にこの後作成するput_labels というメソッドを呼び出します。

画像のラベルを検出する

search_images_helper.rb
  def put_labels(key, s3_client, credentials, bucket)
    rekogniton_client   = Aws::Rekognition::Client.new(credentials: credentials)
    attrs = {
      image: {
        s3_object: {
          bucket: bucket,
          name: key
        },
      },
      max_labels: 10
    }

    response = rekogniton_client.detect_labels attrs
    label_list = []
    response.labels.each do |label|
      label_list << label.name.downcase
    end

    s3_client.delete_objects({
      bucket: bucket,
      delete: {
        objects: [
          {
            key: key
          },
        ],
        quiet: false
      }
    })

    translate_label(label_list)
    rekogniton_client   = Aws::Rekognition::Client.new(credentials: credentials)
    attrs = {
      image: {
        s3_object: {
          bucket: bucket,
          name: key
        },
      },
      max_labels: 10
    }

max_labelsで検出するラベルの上限を指定できます。

    response = rekogniton_client.detect_labels attrs
    label_list = []
    response.labels.each do |label|
      label_list << label.name.downcase

detect_labelsを使って画像を解析したデータを取得して、

ラベル名だけを配列に入れます。

このとき、ラベル名の頭文字が大文字になっていることがあるので、

downcaseメソッドを使ってすべて小文字に変換します。

これをしないと、次で説明する日本語翻訳がうまくいきません。

    s3_client.delete_objects({
      bucket: bucket,
      delete: {
        objects: [
          {
            key: key
          },
        ],
        quiet: false
      }
    })

    translate_label(label_list)

delete_objectsメソッドを使って、画像認識に使用した画像を削除します。

最後に次で作成するtranslate_labelという日本語翻訳するためのメソッドを呼び出します。

ラベル名を翻訳する

search_images_helper.rb
  def translate_label(label_list)
    region = 'us-east-1'

    # ラベルを翻訳
    translate_client = Aws::Translate::Client.new(
      region: region,
      credentials: credentials,
    )

    label_list_ja = []

    label_list.each do |label|
      res = translate_client.translate_text({
              text: label,
              source_language_code: 'auto',
              target_language_code: 'ja'
            })
      label_list_ja << res.translated_text
    end

    # ラベルのリストを返す
    label_list_ja
  end
    label_list.each do |label|
      res = translate_client.translate_text({
              text: label,
              source_language_code: 'auto',
              target_language_code: 'ja'
            })
      label_list_ja << res.translated_text
    end

translate_textメソッドを使用して先程のラベル名を翻訳します。

textには翻訳したいテキストを指定します。

source_language_codeには翻訳前の言語を指定します。

autoを指定するとapi側がよしなに指定してくれます。

target_language_codeには、翻訳後の言語を指定します。

翻訳したラベルのリストをlabel_list_jaに代入して値を返します。

あいまい検索のメソッドを作成

続いて、先程翻訳したラベルの配列を使って、あいまい検索をします。

  def label_search(label_list)
    relation = Bug.distinct

    result = []

    label_list.each do |label|
      relation.where('name LIKE ?', "%#{label}%")
              .or(relation.where('feature LIKE ?', "%#{label}%"))
              .or(relation.where('approach LIKE ?', "%#{label}%"))
              .or(relation.where('prevention LIKE ?', "%#{label}%"))
              .or(relation.where('harm LIKE ?', "%#{label}%"))
              .each { |bug| result << bug }
    end
    result.uniq!
  end

orメソッドを使ってor検索をしています。

取得したレコードを配列にいれ、uniq!メソッドを使って重複したレコードを削除します。

コントローラを作成

続いて、コントローラに下記のコードを追加します。

コントローラ
class BugsController < ApplicationController
  include SearchImagesHelper

  def image_search
    if params[:image]
      label_list = upload_image(data)
      bug_array = label_search(label_list)
      if bug_array.present?
        @bugs = Bug.where(name: bug_array.map(&:name)).page(params[:page])
      else
        @bugs = Bug.none.page(params[:page])
      end
      render :index
    end
    redirect_to bugs_path
  end
end

先程のヘルパーをインクルードします。

    if params[:image]
      label_list = upload_image(data)

paramsで画像データを取得して、それを引数にして先程作成したupload_imageメソッドを呼び出します。

帰ってきたラベルの配列データをlabel_listに代入します。

    bug_array = label_search(label_list)

先程作成したlabel_searchを使って、レコードの配列を取得します。

    if bug_array.present?
      @bugs = Bug.where(name: bug_array.map(&:name)).page(params[:page])
    else
      @bugs = Bug.none.page(params[:page])
    end
    render 'bugs/index'

kaminariというページネーションのgemで使用できるようになるpageメソッドを使う際に、

Arrayクラスでは使用できないため、whereメソッドとnoneメソッドを使って無理やりActiveRecord::Relationクラスに変更させます。

最後に

もっといい方法があればコメントで教えていただければ幸いです!

参考

[Railsで配列をActive Record Relationに変換したい - Qiita]
(https://qiita.com/fgem28/items/25e25d400f2ce21f4235:embed:cite)

[Amazon S3 とは - Amazon Simple Storage] (Service[https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Welcome.html:embed:cite)

[Amazon Rekognition とは - Amazon Rekognition]
(https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/what-is.html:embed:cite)

[Amazon Translate とは - Amazon Translate]
(https://docs.aws.amazon.com/ja_jp/translate/latest/dg/what-is.html:embed:cite)

1
0
0

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
1
0