Raspberry Pi、Kinesis Video Streams、Amazon Rekognition によるリアルタイム顔照合

Raspberry Pi、Kinesis Video Streams、Amazon Rekognition によるリアルタイム顔照合

Raspberry Pi から Kinesis Video Streams へ動画を送信し、検出した顔を Amazon Rekognition のコレクションと照合します。

Takahiro Iwasa
10 min read

Raspberry Pi の USB カメラ映像を Kinesis Video Streams へ送信します。Amazon Rekognition Video のストリームプロセッサが顔を検出し、事前に登録した顔コレクションから一致する顔を検索します。

必要要件

ハードウェア要件

  • Raspberry Pi 4B(RAM 4 GB)
  • USB カメラ

ソフトウェア要件

AWS リソースの構築

AWS SAM テンプレート

template.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: face-detector-using-kinesis-video-streams
Resources:
Function:
Type: AWS::Serverless::Function
Properties:
FunctionName: face-detector-function
CodeUri: src/
Handler: app.lambda_handler
Runtime: python3.11
Architectures:
- arm64
Timeout: 3
MemorySize: 128
Role: !GetAtt FunctionIAMRole.Arn
Events:
KinesisEvent:
Type: Kinesis
Properties:
Stream: !GetAtt KinesisStream.Arn
MaximumBatchingWindowInSeconds: 10
MaximumRetryAttempts: 3
StartingPosition: LATEST
FunctionIAMRole:
Type: AWS::IAM::Role
Properties:
RoleName: face-detector-function-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole
Policies:
- PolicyName: policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- kinesisvideo:GetHLSStreamingSessionURL
- kinesisvideo:GetDataEndpoint
Resource: !GetAtt KinesisVideoStream.Arn
KinesisVideoStream:
Type: AWS::KinesisVideo::Stream
Properties:
Name: face-detector-kinesis-video-stream
DataRetentionInHours: 24
RekognitionCollection:
Type: AWS::Rekognition::Collection
Properties:
CollectionId: FaceCollection
RekognitionStreamProcessor:
Type: AWS::Rekognition::StreamProcessor
Properties:
Name: face-detector-rekognition-stream-processor
KinesisVideoStream:
Arn: !GetAtt KinesisVideoStream.Arn
KinesisDataStream:
Arn: !GetAtt KinesisStream.Arn
RoleArn: !GetAtt RekognitionStreamProcessorIAMRole.Arn
FaceSearchSettings:
CollectionId: !Ref RekognitionCollection
FaceMatchThreshold: 80
DataSharingPreference:
OptIn: false
KinesisStream:
Type: AWS::Kinesis::Stream
Properties:
Name: face-detector-kinesis-stream
StreamModeDetails:
StreamMode: ON_DEMAND
RekognitionStreamProcessorIAMRole:
Type: AWS::IAM::Role
Properties:
RoleName: face-detector-rekognition-stream-processor-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: rekognition.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonRekognitionServiceRole
Policies:
- PolicyName: policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- kinesis:PutRecord
- kinesis:PutRecords
Resource:
- !GetAtt KinesisStream.Arn

Lambda 関数

src/app.py
import base64
import json
import logging
from datetime import datetime, timedelta, timezone
from functools import cache
import boto3
JST = timezone(timedelta(hours=9))
kvs_client = boto3.client('kinesisvideo')
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
def lambda_handler(event: dict, context: dict) -> dict:
for record in event['Records']:
base64_data = record['kinesis']['data']
stream_processor_event = json.loads(base64.b64decode(base64_data).decode())
# Refer to https://docs.aws.amazon.com/rekognition/latest/dg/streaming-video-kinesis-output.html for details on the structure.
if not stream_processor_event['FaceSearchResponse']:
continue
logger.info(stream_processor_event)
url = get_hls_streaming_session_url(stream_processor_event)
logger.info(url)
return {
'statusCode': 200,
}
@cache
def get_kvs_am_client(api_name: str, stream_arn: str):
# Retrieves the data endpoint for the stream.
# See https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesisvideo/client/get_data_endpoint.html
endpoint = kvs_client.get_data_endpoint(
APIName=api_name.upper(),
StreamARN=stream_arn
)['DataEndpoint']
return boto3.client('kinesis-video-archived-media', endpoint_url=endpoint)
def get_hls_streaming_session_url(stream_processor_event: dict) -> str:
# Generates an HLS streaming URL for the video stream.
# See https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis-video-archived-media/client/get_hls_streaming_session_url.html
kinesis_video = stream_processor_event['InputInformation']['KinesisVideo']
stream_arn = kinesis_video['StreamArn']
kvs_am_client = get_kvs_am_client('get_hls_streaming_session_url', stream_arn)
start_timestamp = datetime.fromtimestamp(kinesis_video['ServerTimestamp'], JST)
end_timestamp = datetime.fromtimestamp(kinesis_video['ServerTimestamp'], JST) + timedelta(minutes=1)
return kvs_am_client.get_hls_streaming_session_url(
StreamARN=stream_arn,
PlaybackMode='ON_DEMAND',
HLSFragmentSelector={
'FragmentSelectorType': 'SERVER_TIMESTAMP',
'TimestampRange': {
'StartTimestamp': start_timestamp,
'EndTimestamp': end_timestamp,
},
},
ContainerFormat='FRAGMENTED_MP4',
Expires=300,
)['HLSStreamingSessionURL']

スタックのデプロイ

SAM アプリケーションをビルドしてデプロイします。

Terminal window
sam build
sam deploy

顔のインデックス登録

カメラ映像から既知の顔を検索するため、IndexFaces API で参照画像の顔を Rekognition コレクションへ登録します。

以下を実際の値に置き換えてください。

  • <YOUR_BUCKET>
  • <YOUR_OBJECT>
  • <PERSON_ID>
Terminal window
aws rekognition index-faces \
--image '{"S3Object": {"Bucket": "<YOUR_BUCKET>", "Name": "<YOUR_OBJECT>"}}' \
--collection-id FaceCollection \
--external-image-id <PERSON_ID>

Rekognition は顔コレクションに元の画像データを保存しません。抽出した顔特徴ベクトルと関連メタデータを保存します。

https://docs.aws.amazon.com/rekognition/latest/dg/add-faces-to-collection-procedure.html

For each face detected, Amazon Rekognition extracts facial features and stores the feature information in a database. In addition, the command stores metadata for each face that’s detected in the specified face collection. Amazon Rekognition doesn’t store the actual image bytes.

ビデオプロデューサーのセットアップ

この例では、Ubuntu 23.10 を実行する Raspberry Pi 4B(RAM 4 GB)をビデオプロデューサーとして使用します。

Raspberry Pi and USB Camera Setup

GStreamer プラグインのビルド

AWS は Amazon Kinesis Video Streams C++ Producer SDK、GStreamer プラグイン、JNI を提供しています。kvssink プラグインを使って、Raspberry Pi のカメラ映像を Kinesis Video Streams へ送信します。

ℹ️ Note

AWS は GStreamer プラグイン用の Docker イメージ を提供していますが、アーキテクチャの制約により Raspberry Pi では動作しない場合があります。

以下のコマンドを実行します。システムの性能によっては、ビルドに 20 分以上かかります。

Terminal window
sudo apt update
sudo apt upgrade
sudo apt install \
make \
cmake \
build-essential \
m4 \
autoconf \
default-jdk
sudo apt install \
libssl-dev \
libcurl4-openssl-dev \
liblog4cplus-dev \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base-apps \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-ugly \
gstreamer1.0-tools
git clone https://github.com/awslabs/amazon-kinesis-video-streams-producer-sdk-cpp.git
mkdir -p amazon-kinesis-video-streams-producer-sdk-cpp/build
cd amazon-kinesis-video-streams-producer-sdk-cpp/build
sudo cmake .. -DBUILD_GSTREAMER_PLUGIN=ON -DBUILD_JNI=TRUE
sudo make

ビルドが完了したら、GStreamer が kvssink を読み込めることを確認します。

Terminal window
cd ~/amazon-kinesis-video-streams-producer-sdk-cpp
export GST_PLUGIN_PATH=`pwd`/build
export LD_LIBRARY_PATH=`pwd`/open-source/local/lib
gst-inspect-1.0 kvssink

以下のような出力が表示されるはずです。

Factory Details:
Rank primary + 10 (266)
Long-name KVS Sink
Klass Sink/Video/Network
Description GStreamer AWS KVS plugin
Author AWS KVS <[email protected]>
...

今後のログインセッションでも環境変数を設定するため、~/.profile に以下の export 文を追加します。

Terminal window
echo "" >> ~/.profile
echo "# GStreamer" >> ~/.profile
echo "export GST_PLUGIN_PATH=$GST_PLUGIN_PATH" >> ~/.profile
echo "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> ~/.profile

GStreamer の実行

USB カメラを Raspberry Pi に接続し、以下のパイプラインで H.264 動画を Kinesis Video Streams へ送信します。

以下を実際の値に置き換えてください。

  • <KINESIS_VIDEO_STREAM_NAME>
  • <YOUR_ACCESS_KEY>
  • <YOUR_SECRET_KEY>
  • <YOUR_AWS_REGION>
🔥 Caution

解像度、フレームレート、ビットレートを上げると、Kinesis Video Streams が取り込み、保存するデータ量が増えます。Rekognition のストリーミング動画料金は、主に処理時間に基づきます。

Terminal window
gst-launch-1.0 -v v4l2src device=/dev/video0 \
! videoconvert \
! video/x-raw,format=I420,width=320,height=240,framerate=5/1 \
! x264enc bframes=0 key-int-max=45 bitrate=500 tune=zerolatency \
! video/x-h264,stream-format=avc,alignment=au \
! kvssink stream-name=<KINESIS_VIDEO_STREAM_NAME> storage-size=128 access-key="<YOUR_ACCESS_KEY>" secret-key="<YOUR_SECRET_KEY>" aws-region="<YOUR_AWS_REGION>"

Kinesis Video Streams コンソールでライブ映像を確認します。

Kinesis Video Streams Management Console

テスト

Rekognition Video ストリームプロセッサの起動

Rekognition Video ストリームプロセッサ を起動します。Kinesis の動画ストリームを読み込み、検出した顔をコレクションと照合して、結果を Kinesis Data Streams へ出力します。

ストリームプロセッサを起動します。

Terminal window
aws rekognition start-stream-processor \
--name face-detector-rekognition-stream-processor

ストリームプロセッサが実行中であることを確認するため、ステータスを確認します。

Terminal window
aws rekognition describe-stream-processor \
--name face-detector-rekognition-stream-processor | grep "Status"

"Status": "RUNNING" と表示されれば正常です。

顔の検出と照合

USB カメラから動画が送信されると、Rekognition Video ストリームプロセッサが顔を検出し、顔コレクションから一致する候補を返します。

結果を確認するには、以下のコマンドで Lambda 関数のログを表示します。

Terminal window
sam logs -n Function \
--stack-name face-detector-using-kinesis-video-streams \
--tail

ログレコードには、以下の例のようにストリームプロセッサイベントの詳細情報が含まれます。

{
"InputInformation": {
"KinesisVideo": {
"StreamArn": "arn:aws:kinesisvideo:<AWS_REGION>:<AWS_ACCOUNT_ID>:stream/face-detector-kinesis-video-stream/xxxxxxxxxxxxx",
"FragmentNumber": "91343852333181501717324262640137742175000164731",
"ServerTimestamp": 1702208586.022,
"ProducerTimestamp": 1702208585.699,
"FrameOffsetInSeconds": 0.0,
}
},
"StreamProcessorInformation": {"Status": "RUNNING"},
"FaceSearchResponse": [
{
"DetectedFace": {
"BoundingBox": {
"Height": 0.4744676,
"Width": 0.29107505,
"Left": 0.33036956,
"Top": 0.19599175,
},
"Confidence": 99.99677,
"Landmarks": [
{"X": 0.41322955, "Y": 0.33761832, "Type": "eyeLeft"},
{"X": 0.54405355, "Y": 0.34024307, "Type": "eyeRight"},
{"X": 0.424819, "Y": 0.5417343, "Type": "mouthLeft"},
{"X": 0.5342691, "Y": 0.54362005, "Type": "mouthRight"},
{"X": 0.48934412, "Y": 0.43806323, "Type": "nose"},
],
"Pose": {"Pitch": 5.547308, "Roll": 0.85795176, "Yaw": 4.76913},
"Quality": {"Brightness": 57.938313, "Sharpness": 46.0298},
},
"MatchedFaces": [
{
"Similarity": 99.986176,
"Face": {
"BoundingBox": {
"Height": 0.417963,
"Width": 0.406223,
"Left": 0.28826,
"Top": 0.242463,
},
"FaceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"Confidence": 99.996605,
"ImageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ExternalImageId": "iwasa",
},
}
],
}
],
}

動画再生用の HLS URL

顔の検索結果を含むイベントでは、該当する再生時間の HLS URL もログに出力されます。

https://x-xxxxxxxx.kinesisvideo.<AWS_REGION>.amazonaws.com/hls/v1/getHLSMasterPlaylist.m3u8?SessionToken=xxxxxxxxxx

Safari または HLS 対応プレーヤーで URL を開きます。

ℹ️ Note

Chrome は HLS 再生をネイティブにサポートしていません。Native HLS Playback などのサードパーティ拡張機能を利用できます。

HLS Playback Example

クリーンアップ

作業が終わったら、ストリームプロセッサを停止しスタックを削除します。

Terminal window
aws rekognition stop-stream-processor \
--name face-detector-rekognition-stream-processor
sam delete

まとめ

Kinesis Video Streams が Raspberry Pi のカメラ映像を配信し、Rekognition Video ストリームプロセッサが検出した顔を登録済みのコレクションと照合します。Lambda 関数は検索結果をログへ出力し、該当する再生時間の HLS URL を生成します。

顔の検出と照合は Rekognition Video が行うため、Lambda 関数は結果レコードのデコードと再生 URL の取得だけを担当し、動画フレームを処理しません。

kvssink に渡す解像度、フレームレート、ビットレートは、Kinesis Video Streams の取り込み・保存データ量に影響します。まずはこの記事の低い設定から始め、安定した照合に画質が足りない場合だけ引き上げてください。

About the author

Takahiro Iwasa

Takahiro Iwasa

Software Developer

This blog shares technical notes from hands-on projects—architecture, implementation, and AWS service integrations.