はじめに
Splunkでデータ集約、分析する中で、画像や動画データも対象にしたいという場合のお話です。
※例えばコミュニティサイトを運用している場合に変な画像をアップロードされていないかとか、ビジネス分析用途で、インスタグラムとかで自社の製品とかサービスに関連する画像でどんなものがアップされてるかとか
残念ながらSplunk自身では画像、動画データを取り込むことはできません。
じゃあ他の偉大なサービスの力を借りて実現しようということで、AmazonのRekognitionで解析して解析データを取り込んでみましたのでメモです。
Amazon Rekognitionとは
公式からコピペ。
Amazon Rekognition を使用すると、画像と動画の物体、人物、テキスト、シーン、活動を特定し、不適切なコンテンツを検出できます。
Amazon Rekognition は、非常に正確な顔分析および顔検索機能も備えています。これを使用して、さまざまなユーザー検証、人数のカウント、および公共安全のユースケースで顔を検出、分析、比較できます。
面白そうな関数はこの辺りです。恐ろしい時代です。
- DetectLables:エンティティを検出してラベル付け
- GetFaceDetection:人物の年齢、性別、眼鏡をかけているか、感情などを推定
- CompareFaces:元となる顔画像と見比べ、その人物かを判別
- GetSegmentDetection:表情から感情分析
- DetectText:画像に写っているテキストを抽出
- GetContentModeration:コンテンツが健全かどうか判別
環境
- Splunk Enterprise (HEC使うのでAWS上にあるもの) or Splunk Cloud
- AWS Lambda
- Amazon S3
- Amazon Rekognition
こんなフローです。
S3へのアップロードをトリガーにLambdaを起動し、S3の情報をLambdaからRekognitionに渡して画像解析、結果をSplunkのHECに送信します。
それじゃやっていきましょう。
事前準備
IAMの設定
LambdaからS3、RekognitionにアクセスするためのIAMロールを作成します。
ユースケースはLambdaを選択します。
ポリシーで必要なのは以下です。
- AmazonS3ReadOnlyAccess
- AmazonRekognitionFullAccess
- AWSLambdaBasicExecutionRole ※CloudWatchへのログ出力
S3 Bucketの準備
画像アップロードするS3 Bucketも作っておきましょう。
既存のものがあればそれでも結構です。
テスト用の画像をアップロードしておきます。
「情報量の多い画像」で有名なミシン紳士をお借りしますね。
Rekognitionのデータを取り込んでみよう
Lambda関数の作成
Lambdaの関数を作成します。
「一から作成」でランタイムはPython、実行ロールは先ほど作ったロールを選択します。
画像データが大きい場合に時間を要する場合があります。
関数作成後、設定 > 一般設定からタイムアウト値を増やしておきます。
※デフォルト3秒から10秒に変更しておきました
Rekognitionで解析
さて、Pythonのスクリプト作っていきましょう。
とは言ってもめちゃ簡単です。
画像に何が含まれているかを抽出してくれるDetectLabels
で試します。
import json
import boto3
def lambda_handler(event, context):
bucket = "symmr.rekognition" # S3 Bucket名
key = "test_image.jpg" # 画像ファイル名
client = boto3.client("rekognition")
response = client.detect_labels(Image = {"S3Object": {"Bucket": bucket, "Name": key}}, MaxLabels=20, MinConfidence=70)
print(response)
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
boto3を使い、S3からデータ取得する場合はdetect_labelsにS3の情報とMaxLabels
で取得ラベル数(デフォルト無制限)、MinConfidence
で最小confidence(デフォルト55%)を指定するだけです。
※APIリファレンスはこちら
※S3ではなく画像データを直接解析する場合はImage.Bytes
にbase64にエンコードしたデータをセットすればOKです
ではテストしてみましょう。
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}
Function Logs
START RequestId: cf212be4-c922-41b8-ab91-7c90c3a285ec Version: $LATEST
{'Labels': [{'Name': 'Person', 'Confidence': 99.77276611328125, 'Instances': [{'BoundingBox': {'Width': 0.07846866548061371, 'Height': 0.3057587146759033, 'Left': 0.13179878890514374, 'Top': 0.15125896036624908}, 'Confidence': 99.77276611328125}, {'BoundingBox': {'Width': 0.0827648788690567, 'Height': 0.20582953095436096, 'Left': 0.25745800137519836, 'Top': 0.1811434030532837}, 'Confidence': 99.7685546875}, {'BoundingBox': {'Width': 0.06163841113448143, 'Height': 0.2630268633365631, 'Left': 0.07908330857753754, 'Top': 0.16579791903495789}, 'Confidence': 99.69869995117188}, {'BoundingBox': {'Width': 0.042920779436826706, 'Height': 0.26353126764297485, 'Left': 0.18419542908668518, 'Top': 0.15169677138328552}, 'Confidence': 99.55378723144531}], 'Parents': []}, {'Name': 'Human', 'Confidence': 99.77276611328125, 'Instances': [], 'Parents': []}, {'Name': 'Suit', 'Confidence': 99.72770690917969, 'Instances': [], 'Parents': [{'Name': 'Overcoat'}, {'Name': 'Coat'}, {'Name': 'Clothing'}]}, {'Name': 'Clothing', 'Confidence': 99.72770690917969, 'Instances': [], 'Parents': []}, {'Name': 'Overcoat', 'Confidence': 99.72770690917969, 'Instances': [], 'Parents': [{'Name': 'Coat'}, {'Name': 'Clothing'}]}, {'Name': 'Coat', 'Confidence': 99.72770690917969, 'Instances': [], 'Parents': [{'Name': 'Clothing'}]}, {'Name': 'Apparel', 'Confidence': 99.72770690917969, 'Instances': [], 'Parents': []}, {'Name': 'Tuxedo', 'Confidence': 98.28694152832031, 'Instances': [], 'Parents': [{'Name': 'Suit'}, {'Name': 'Overcoat'}, {'Name': 'Coat'}, {'Name': 'Clothing'}]}, {'Name': 'Bush', 'Confidence': 88.96171569824219, 'Instances': [], 'Parents': [{'Name': 'Vegetation'}, {'Name': 'Plant'}]}, {'Name': 'Plant', 'Confidence': 88.96171569824219, 'Instances': [], 'Parents': []}, {'Name': 'Vegetation', 'Confidence': 88.96171569824219, 'Instances': [], 'Parents': [{'Name': 'Plant'}]}, {'Name': 'Blazer', 'Confidence': 71.90851593017578, 'Instances': [], 'Parents': [{'Name': 'Jacket'}, {'Name': 'Coat'}, {'Name': 'Clothing'}]}, {'Name': 'Jacket', 'Confidence': 71.90851593017578, 'Instances': [], 'Parents': [{'Name': 'Coat'}, {'Name': 'Clothing'}]}, {'Name': 'Housing', 'Confidence': 70.93521881103516, 'Instances': [], 'Parents': [{'Name': 'Building'}]}, {'Name': 'Building', 'Confidence': 70.93521881103516, 'Instances': [], 'Parents': []}], 'LabelModelVersion': '2.0', 'ResponseMetadata': {'RequestId': '786baec7-a946-4391-b728-4288d53a96e0', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'application/x-amz-json-1.1', 'date': 'Sat, 03 Apr 2021 14:43:01 GMT', 'x-amzn-requestid': '786baec7-a946-4391-b728-4288d53a96e0', 'content-length': '2166', 'connection': 'keep-alive'}, 'RetryAttempts': 0}}
END RequestId: cf212be4-c922-41b8-ab91-7c90c3a285ec
REPORT RequestId: cf212be4-c922-41b8-ab91-7c90c3a285ec Duration: 1706.71 ms Billed Duration: 1707 ms Memory Size: 128 MB Max Memory Used: 73 MB Init Duration: 255.29 ms
Request ID
cf212be4-c922-41b8-ab91-7c90c3a285ec
Labelsに取得したラベルが入ってきます。ちょっと見にくいので抽出すると
Name, Confidence
Person, 99.77276611328125
Human, 99.77276611328125
Suit, 99.72770690917969
Clothing, 99.72770690917969
Overcoat, 99.72770690917969
Coat, 99.72770690917969
Apparel, 99.72770690917969
Tuxedo, 98.28694152832031
Bush, 88.96171569824219
Plant, 88.96171569824219
Vegetation, 88.96171569824219
Blazer, 71.90851593017578
Jacket, 71.90851593017578
Housing, 70.93521881103516
Building, 70.93521881103516
すげぇや。
ちょっと違うかな?と思ったラベルについてはConfidenceもしっかり低めに出ています。
他にもそれぞれのラベルの親子関係や、対する画像内の位置情報も含まれています。
SplunkのHECに送信してみる
この結果をSplunkに送信しましょう。
準備など詳細は以下をご参照ください。
AWS LambdaでRESTでデータ取得してSplunkに送信してみる (Python編)
それで、できたスクリプトがこちらです。
※HECのパラメータはSPLUNK_HEC_TOKEN
、SPLUNK_HEC_URL
で環境変数化しています
※エラー処理は省いています
from splunk_http_event_collector import http_event_collector
import json
import boto3
import os
import logging
import sys
def lambda_handler(event, context):
targetBucket = "symmr.rekognition"
targetKey = "test_image.jpg"
client = boto3.client("rekognition")
response = client.detect_labels(Image = {"S3Object": {"Bucket": targetBucket, "Name": targetKey}}, MaxLabels=20, MinConfidence=70)
print(response)
# init logging config, this would be job of your main code using this class.
logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S %z')
# Create event collector object, default SSL and HTTP Event Collector Port
http_event_collector_key = os.environ["SPLUNK_HEC_TOKEN"]
http_event_collector_host = os.environ["SPLUNK_HEC_URL"]
event_to_send = http_event_collector(http_event_collector_key, http_event_collector_host)
# perform a HEC reachable check
hec_reachable = event_to_send.check_connectivity()
if not hec_reachable:
sys.exit(1)
# Set to pop null fields. Always a good idea
event_to_send.popNullFields = True
# set logging to DEBUG for example
event_to_send.log.setLevel(logging.DEBUG)
# Start event payload and add the metadata information
payload = {}
payload.update({"index":"aws_lambda"})
payload.update({"sourcetype":"rekognition"})
payload.update({"source":context.function_name})
payload.update({"host":"lambda"})
payload.update({"event":response})
event_to_send.sendEvent(payload)
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
テストして、Splunkで見てみましょう。
やったね!
S3へのファイルアップロードをトリガーに設定
Rekognition→Splunk送信がうまくいったので、次はS3にファイルアップロードされたらLambdaが自動実行されるようにしましょう。
[トリガーを追加] で...
S3 Bucketを選択してトリガー作成します。
コードも若干修正します。
S3からのトリガー時には、対象のファイル情報はevent
に入ってきます。BucketとKey情報を渡すようにします。
※S3通知イベントの詳細はこちら
from splunk_http_event_collector import http_event_collector
import json
import boto3
import os
import logging
import sys
def lambda_handler(event, context):
# アップロードされたファイル情報
bucket = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
client = boto3.client("rekognition")
response = client.detect_labels(Image = {"S3Object": {"Bucket": bucket, "Name": key}}, MaxLabels=20, MinConfidence=70)
# 解析結果にファイル情報を結合
response.update(event)
print(response)
# init logging config, this would be job of your main code using this class.
logging.basicConfig(format='%(asctime)s %(name)s %(levelname)s %(message)s', datefmt='%Y-%m-%d %H:%M:%S %z')
# Create event collector object, default SSL and HTTP Event Collector Port
http_event_collector_key = os.environ["SPLUNK_HEC_TOKEN"]
http_event_collector_host = os.environ["SPLUNK_HEC_URL"]
event_to_send = http_event_collector(http_event_collector_key, http_event_collector_host)
# perform a HEC reachable check
hec_reachable = event_to_send.check_connectivity()
if not hec_reachable:
sys.exit(1)
# Set to pop null fields. Always a good idea
event_to_send.popNullFields = True
# set logging to DEBUG for example
event_to_send.log.setLevel(logging.DEBUG)
# Start event payload and add the metadata information
payload = {}
payload.update({"index":"aws_lambda"})
payload.update({"sourcetype":"rekognition"})
payload.update({"source":context.function_name})
payload.update({"host":"lambda"})
payload.update({"event":response})
event_to_send.sendEvent(payload)
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
すると自動でLambdaが動作して...
おk
どんどんアップロードしてみる
解析してみたい画像をどんどんアップロードしましょう。
※課金に注意
Splunkで分析
とりあえず扱いやすいように整形。
index=aws_lambda sourcetype=rekognition
| rename Labels{}.Name as label
| table _time, label, Records{}.s3.object.key
| mvexpand label
後はいかようにでも。
まとめ
Amazon Rekognitionを使い解析した画像(動画も)の結果をSplunkでお手軽に分析できるようになりました。
色々なサービスと組み合わせると面白いことができますね。