0
0

AI動画解析サービスを試してみる

Posted at

AmazonのAmazon Rekognition VideoとGoogleのVideo AI(Video Intelligence API)の解析を使用してみて、それぞれどのような結果が得られるのか確認したいと思います。

今回は1回目ということでAmazon Rekognitionを使うまでの設定とPythonスクリプトを使用した解析を実行して結果をJSONで取得してみます。

Amazon Rekognitionとは?

詳しくは公式Webサイト見ていただくのが一番ですが、簡単に言えばAWSのS3に保存してある画像や動画に対して顔検出、テキスト検出、ラベル検出などの解析が行えるサービスです。

検出について細かいカスタマイズはできませんが、手元に解析するハード・ソフト含めた環境や知識がない場合でも手軽にAI動画解析を試すことができます。

アカウント設定の手順について

基本的には下記のURLの手順に沿って進めれば準備が完了しますが、Pythonのスクリプトで解析の実行と結果の取得をしたいのでPython用のSDKが動くところまでの設定をします。
SDKを使用する場合は下記ページのステップ3~8は必要ありません。
また、以下の説明は下記ページのステップの数字に合わせています。

アカウント設定の参考ページ:
https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/api-video-roles.html

1. アカウント作成

  • ユーザーページ から「ユーザーの作成」クリック
    6adae320-e068-4da8-b0da-1677f102b981.png

  • 次のページで 「ユーザー名」を入力し「次へ」
    今回は「ai-test」というユーザー名で進めます
    ffe8b780-0575-46ac-855f-904285dcc7aa.png

  • 許可のオプションについて
    許可についてはステップ3で設定します。
    ここではそのまま進めて「次へ」をクリックします。
    d0300f27-9a41-44cf-b95f-77c23e4132e9.png

  • 確認画面
    「ユーザーを作成」をクリックで作成完了です。
    cf314305-cae2-4c97-b8b5-a33c9cf7936c.png

2. SDKから使用するアクセスキーの設定

  • ユーザーの設定を表示
    ユーザーページでユーザー名の「ai-test」をクリックしユーザーのごと設定ページを開きます
    6386cf2f-4e1d-4f43-8f31-4ccbb421b286.png

  • アクセスキーの作成の開始
    「アクセスキー 1」(末尾の数字は作成したキーの数により変動) の下にある「アクセスキーを作成」をクリックして 「アクセスキーの設定」を表示します
    d7ad0613-b342-42fc-a812-41214f952af4.png

  • アクセスキーの作成
    「ユースケース」で「ローカルコード」を選択し、画面下部にチェックボックスにチェックを入れ「次へ」をクリック
    fe792866-d0e7-4fd2-9812-37f8da060ce9.png

    必要な場合に説明を入れ「アクセスキーを作成」で作成完了します
    31825476-31ac-4334-9fee-a3ed3485882a.png

  • シークレットキーの保存
    この画面でシークレットキーを保存する最後のタイミングなので「.csvファイルをダウンロード」でcsvファイルを忘れずにダウンロードしてください
    c25f7e61-ee26-4751-8d25-76d2f4a7cd28.png
    もし、シークレットキーを保存し忘れた場合は今回作成したアクセスキーを削除し、再度作り直してcsvファイルのダウンロードを忘れずに行ってください。

  • シークレットキーの設定
    各プラットフォームのユーザーのホームディレクトリ直下の「.aws」というディレクトリの「credentials」というファイル名に下記の所定のフォーマットでファイルを作成してください

    • Linux の場合
      ~/.aws/credentials
    • Windows の場合
      %HOMEPATH%\.aws\credentials
    • フォーマット
      [default]
      aws_access_key_id = アクセスキー
      aws_secret_access_key = シークレットキー
      
      [default] はprofile名でSDKやCLIなどでprofileを指定する際に使用する名前です。 default は名前の通りデフォルトで使用されるprofile名となります

3. ユーザーに許可ポリシーの追加

  • ユーザーページでユーザー名の「ai-test」をクリックしユーザーのごと設定ページを開きます。
    27e6c521-c4ee-4967-911e-adf738975aab.png

  • 「許可の追加」をクリックし、更に「許可の追加」をクリック
    6df632eb-d46f-43b5-a94d-d3e53af7d3ab.png

  • 「許可のオプション」から「ポリシーを直接アタッチする」を選択。
    検索欄が出てくるので下記を許可ポリシー検索し、それぞれチェックをして最後に画面下部の「次へ」をクリック

    • AmazonRekognitionFullAccess
    • AmazonSNSFullAccess
    • AmazonSQSFullAccess
    • AmazonS3FullAccess
      8fcb1350-67cc-4ec4-a9b8-ab51dcdca684.png
  • 次に確認画面が出るので「許可を追加」をクリックして完了

    a8c103d1-9b74-405d-bab5-bb2e0c68bd54.png

4. Amazon SNS の設定

SDKを使用する場合は必要ないのでスキップ

5. Amazon SQS の設定

SDKを使用する場合は必要ないのでスキップ

6. SNSトピックのサブスクライブ

SDKを使用する場合は必要ないのでスキップ

7. SNSトピックからSQSへメッセージを送る許可設定

SDKを使用する場合は必要ないのでスキップ

8. Amazon Rekognitionにアクセスするロールを作成

  • ロールページから「ロールを作成」をクリック
    6a072325-5bef-4471-a55a-292f36cc4f33.png

  • 「信頼されたエンティティタイプ」で「AWS のサービス」を選択
    3765fce0-e81a-482d-8f2f-afe11be8afcd.png

  • 「ユースケース」から「Rekognition」を検索し選択し「次へ」
    85da3ff4-e72d-4447-9eab-1721b38026bd.png

  • 「許可を追加」のページで、そのまま「次へ」をクリック
    a9473e94-0c98-4ce1-850f-95ffed31e599.png

  • 「ロールの詳細」で「ロール名」を指定します
    今回は「ai-demo-role」としました。
    このロールのARNをステップ9,10と、後ほどのSDKを使用したスクリプトで使用します
    6977798c-15dc-4a47-8308-f296c2412f96.png

  • 画面下部の「ロールを作成」をクリックして完了
    c3187137-bf76-427e-9545-05291a492f04.png

9. 上記ロールの信頼関係を編集

  • ロールページからステップ8で作成した「ai-test-role」をクリック
    8aefae21-73ba-4c0b-b163-ccc986bf7d76.png

  • 「信頼関係」タブで下記の様にPincipalとConditionに値を追加
    115d53eb-fe71-4195-8924-74dae3edeebf.png

<user-arn> は作成されたユーザーのARNです。ARNは ユーザーページからユーザー名の「ai-test」をクリックしユーザーの詳細ページで確認できます。
<user-number> は、ユーザーのARNにある数値を使用します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "rekognition.amazonaws.com",
                "AWS": "<user-arn>"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "<user-number>"
                }
            }
        }
    ]
}

10. ユーザーにインラインポリシーの追加

  • ユーザーページでユーザー名の「ai-test」をクリックしユーザーのごと設定ページを開きます
    01ff615c-ced4-48a5-aa08-82fa24965e6c.png

  • 「許可」タブの「許可を追加」クリックして、「インラインポリシーを作成」をクリック
    85ebb420-bc8a-4a28-b0ba-e4a202373ffe.png

  • 「ポリシーエディタ」で「JSON」を選択し、下記のように変更します。
    ad21ca5c-f179-4f62-9fe1-a9b946502802.png

<user-arn>をステップ8で追加したロールのARNを使用します。
ARNの確認方法はステップ9を参照してください

{
"Version": "2012-10-17",
"Statement": [
        {
            "Sid": "MySid",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "<user-arn>"
        }
    ]
}

アカウント設定の参考ページのJSONの例にはSTEP 7のARNと書いてありますがSTEP 8の間違いだと思われます

11. 暗号化と復号

今回は暗号化/復号の設定は行いません。

12. SDKの準備

  • boto3 のインストール
    Python3の実行環境に pip で boto3 をインストールします。
    その他、足りないライブラリがあれば pip install をしてください

    $ pip install boto3
    

参考: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html

ラベル解析の実行

S3にアップロードした動画ファイルに対して、ラベル解析を行い、その結果をJSONファイルとして保存するPythonスクリプトについて解説します。

S3に動画ファイルのアップロード

事前にAWSのS3に解析を行う動画をアップロードしておきます。

参考: https://aws.amazon.com/jp/s3/getting-started/?nc=sn&loc=6&dn=1

Python スクリプト

ここでは複数ある解析の種類からラベルの検出をして、その結果をJSONとしてファイルに保存します。

Amazon Rekognitionで動画の解析を実行するとJobIdが与えられます。今回は実行結果の出力として表示するだけですが、このJobIdを用いて解析を実行すると、解析実行結果が保存されている間は解析の再実行を行わず、解析結果だけを再取得することもできます。

Amazon Rekognitionの解析結果は、解析結果の項目(今回はラベル)が1000件ごとに分割されます。
その場合、結果のJSONに NextToken という残りの結果を取得するための値が返ってきます。

今回のスクリプトでは下記の参考ページのスクリプトを元に NextToken がある限り繰り返し実行し、NextToken がある場合は1000件ごとに分割したJSONファイルをその都度保存し、最後に残りのデータと、全て結果をデータ構造が壊れないように整形しながらまとめたJSONファイルを保存します。

参考: https://docs.aws.amazon.com/ja_jp/rekognition/latest/dg/video-analyzing-with-sqs.html

設定項目

スクリプトファイルの下部にあるmain関数の中に、ユーザーと解析対象の動画ファイルに関する設定項目があります。

  • <profile-name>
    デフォルトのままの場合は default と指定してください
  • <user-role-arn>
    ロールページからステップ8で作成した「ai-test-role」をクリックして表示されるroleのARNを指定してください
  • <bucket-name>
    動画ファイルを保存しているS3のバケット名を指定してください
  • <file-name>
    上記バケットに保存している動画ファイル名を指定してください

出力結果 (例)

$ ./run-detection.py
Start JobId: 99dfef36...snip...757d530485
.........
Matching JobId found: 99dfef36...snip...757d530485
-> Status: SUCCEEDED
ai-test.mp4-label-1.json
ai-test.mp4-label-2.json
ai-test.mp4-label-3.json
ai-test.mp4-label-4.json
ai-test.mp4-label-5.json
ai-test.mp4-label-all.json

スクリプト全体

以下、スクリプト全体です。
各APIの細かい仕様については以下のBoto3のリファレンスを参照してください。

Boto3 APIリファレンス:
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rekognition.html

import boto3
import json
import sys
import time

class VideoDetect:
  def __init__(self, role, bucket, video, client, rek, sqs, sns):
    self.jobtype = 'label'
    self.roleArn = role
    self.bucket = bucket
    self.video = video
    self.client = client
    self.rek = rek
    self.sqs = sqs
    self.sns = sns

  def GetSQSMessageSuccess(self):
    jobFound = False
    succeeded = False
    dotLine = 0

    while jobFound == False:
      sqsResponse = self.sqs.receive_message(
              QueueUrl=self.sqsQueueUrl,
              MessageAttributeNames=['ALL'],
              MaxNumberOfMessages=10,
              )

      if sqsResponse:
        if 'Messages' not in sqsResponse:
          if dotLine < 40:
            print('.', end='')
            dotLine = dotLine + 1
          else:
            print()
            dotLine = 0
          sys.stdout.flush()
          time.sleep(3)
          continue

        print()
        for message in sqsResponse['Messages']:
          notification = json.loads(message['Body'])
          rekMessage = json.loads(notification['Message'])

          if rekMessage['JobId'] == self.startJobId:
            print('Matching JobId found: ' + rekMessage['JobId'])
            jobFound = True

            if (rekMessage['Status'] == 'SUCCEEDED'):
              print('-> Status: ' + rekMessage['Status'])
              succeeded = True

            self.sqs.delete_message(QueueUrl=self.sqsQueueUrl,
                        ReceiptHandle=message['ReceiptHandle'])
          else:
            print("JobId didn't match: " +
                str(rekMessage['JobId']) + ' : ' + self.startJobId)

          # Delete the unknown message. Consider sending to dead letter queue
          self.sqs.delete_message(QueueUrl=self.sqsQueueUrl,
                      ReceiptHandle=message['ReceiptHandle'])
    return succeeded

  def CreateTopicandQueue(self):
    millis = str(int(round(time.time() * 1000)))

    # Create SNS topic
    snsTopicName = "AmazonRekognitionExample" + millis
    topicResponse = self.sns.create_topic(Name=snsTopicName)
    self.snsTopicArn = topicResponse['TopicArn']

    # create SQS queue
    sqsQueueName = "AmazonRekognitionQueue" + millis
    self.sqs.create_queue(QueueName=sqsQueueName)
    self.sqsQueueUrl = self.sqs.get_queue_url(QueueName=sqsQueueName)['QueueUrl']
    attribs = self.sqs.get_queue_attributes(
          QueueUrl=self.sqsQueueUrl,
          AttributeNames=['QueueArn']
        )['Attributes']
    sqsQueueArn = attribs['QueueArn']

    # Subscribe SQS queue to SNS topic
    self.sns.subscribe(
      TopicArn=self.snsTopicArn,
      Protocol='sqs',
      Endpoint=sqsQueueArn)

    # Authorize SNS to write SQS queue
    policy = """
{{
"Version":"2012-10-17",
"Statement":[{{
"Sid":"MyPolicy",
"Effect":"Allow",
"Principal" : {{"AWS" : "*"}},
"Action":"SQS:SendMessage",
"Resource": "{}",
"Condition":{{
"ArnEquals":{{
"aws:SourceArn": "{}"
}}}}}}]
}}""".format(sqsQueueArn, self.snsTopicArn)

    response = self.sqs.set_queue_attributes(
      QueueUrl=self.sqsQueueUrl,
      Attributes={
        'Policy': policy
      })

  def DeleteTopicandQueue(self):
    self.sqs.delete_queue(QueueUrl=self.sqsQueueUrl)
    self.sns.delete_topic(TopicArn=self.snsTopicArn)

  def WriteJsonFile(self, name, data):
    print(name)
    with open(name, mode="wt", encoding="utf-8") as f:
      json.dump(data, f, ensure_ascii=False, indent=2)

  def StartDetection(self):
    self.StartLabelDetection()

  def GetDetectionResults(self):
    try:
      self.GetLabelDetectionResults()
    except Exception as e:
      print(e)
    return

  # LabelDetection
  def StartLabelDetection(self):
    response = self.rek.start_label_detection(
            Video={'S3Object': {'Bucket': self.bucket, 'Name': self.video}},
            NotificationChannel={'RoleArn': self.roleArn, 'SNSTopicArn': self.snsTopicArn},
            )
    self.startJobId = response['JobId']
    print('Start JobId: ' + self.startJobId)

  def GetLabelDetectionResults(self):
    maxResults = 1000
    paginationToken = ''
    finished = False
    loopCount = 1
    responseAll = None

    while finished == False:
      response = self.rek.get_label_detection(
              JobId=self.startJobId,
              MaxResults=maxResults,
              NextToken=paginationToken,
              SortBy='TIMESTAMP',
              AggregateBy="TIMESTAMPS"
              )

      json_name = '{}-{}-{}.json'.format(self.video, self.jobtype, loopCount)
      self.WriteJsonFile(json_name, response)

      if 'NextToken' in response:
        paginationToken = response['NextToken']
        loopCount += 1
        if responseAll == None:
          responseAll = response
        else:
          responseAll['Labels'] += response['Labels']
      else:
        if responseAll != None:
          responseAll['Labels'] += response['Labels']
        finished = True

    if responseAll != None:
      json_name = '{}-{}-all.json'.format(self.video, self.jobtype)
      responseAll.pop('NextToken')
      self.WriteJsonFile(json_name, responseAll)

def main():
  profile = '<profile-name>'
  role_arn = '<user-role-arn>'
  bucket  = '<bucket-name>'
  video   = '<file-name>'

  session = boto3.Session(profile_name=profile)
  client = session.client('rekognition')
  rek = boto3.client('rekognition')
  sqs = boto3.client('sqs')
  sns = boto3.client('sns')

  analyzer = VideoDetect(role_arn, bucket, video, client, rek, sqs, sns)
  analyzer.CreateTopicandQueue()
  analyzer.StartDetection()

  if analyzer.GetSQSMessageSuccess() == True:
    analyzer.GetDetectionResults()

  analyzer.DeleteTopicandQueue()

if __name__ == "__main__":
  main()
0
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
0
0