はじめに
だいぶ夏本番になってきましたね
京都の夏は天然サウナじゃないかと感じてます・・・
さて先日、ふと「AWS Rekognition」を使ってみたくなったのと、ただ単純に使うだけでは面白くないと思ったので、Raspiカメラで撮影した顔写真をS3にアップして、Rekognitionに判定させてみよう!ってなったのがことの始まり
ざっくりとした流れ
- Raspiカメラで顔写真を撮影
- S3にアップロード
- S3へのPutを検地してLambdaを起動
- RekognitionをSDKで呼び出して結果をCloudWatchLogsに出力
Raspi側の処理
使うRaspiカメラは市販されているものを使用しました
これをRaspiに接続して、外部カメラの設定を変更(許可してあげないとカメラを認識しない)
$ sudo raspi-config
「6 Enable Camera」を選択して、Enableにしてあげる
カメラ撮影するためのShellファイルを作成
raspistill -vf -w 480 -h 320 -o /home/raspi-image.jpg
s3cmdでs3に画像を送信するので「s3cmd」をインストール
$ sudo yum -y --enablerepo epel install s3cmd
Key情報を登録
$ s3cmd --configure
Access Key: xxxx
Secret Key: xxxx
バケット確認
$ s3cmd ls
「shoot.sh」にS3へ画像を送信する処理を追記
raspistill -vf -w 480 -h 320 -o /home/raspi-image.jpg
s3cmd put -r /home/raspi-image.jpg s3://rekognition-image-bucket
Shellファイルを実行して、画像がS3に送信されていればOK!
AWS側の処理
Lambda
Rekognitionにリクエストを送信するLambdaを作成する
権限はRekognitionのFull権限とS3のReadOnlyは必要かと思われる
ソースコードはNodeで書いていて、Zipに固めてアップロードしてます
中身はこんな感じ
"use strict";
const AWS = require("aws-sdk");
const co = require("co");
const rekognition = new AWS.Rekognition();
const rekognitionApi = require("./lib/rekognition_api");
exports.handler = (event, context, callback) => {
console.log(JSON.stringify(event));
co(function *() {
const bucketName = event["Records"][0]["s3"]["bucket"]["name"];
const objectKey = event["Records"][0]["s3"]["object"]["key"];
const s3ImageItem = {
S3Object: {
Bucket: bucketName,
Name: objectKey
}
};
// 画像のLabel情報を判定
const labelParams = {
Image: s3ImageItem
};
console.log(JSON.stringify(labelParams));
const labels = yield rekognitionApi.detectLabels(rekognition, labelParams);
console.log(JSON.stringify(labels));
// 顔から年齢や性別を判定
const faceParams = {
Image: s3ImageItem,
Attributes: ["ALL", "DEFAULT"]
};
console.log(JSON.stringify(faceParams));
const faces = yield rekognitionApi.detectFaces(rekognition, faceParams);
console.log(JSON.stringify(faces));
}).then(() => {
console.log("success");
}).catch((err) => {
console.log(err);
});
};
"use strict";
class RekognitionApi {
/**
*
* @param rekognition
* @param params
* @returns {*}
*/
static detectLabels(rekognition, params) {
console.log("detectLabels");
return rekognition.detectLabels(params).promise();
}
/**
*
* @param rekognition
* @param params
* @returns {*}
*/
static detectFaces(rekognition, params) {
console.log("detectFaces");
return rekognition.detectFaces(params).promise();
}
}
module.exports = RekognitionApi;
S3
S3にPut処理があったのを検地してLambdaファンクションをキックするので、S3のコンソール画面から
1. 画像が送信されてくるバケットを選択
2. プロパティタグを選択
3. 詳細設定のEventsを選択して、Lambdaをキックするように設定(下の画像は一例)
結果
さぁ画像をS3に送るぞ!
駄菓子菓子・・・
実際にRaspiカメラで撮影した画像は社内が写っていていろいろまずいという大人の事情が発生したので、ためしにマツコ・デラックスの画像で判定させてみた
判定結果
{
"Labels": [
{
"Name": "Human",
"Confidence": 99.30243682861328
},
{
"Name": "People",
"Confidence": 99.30485534667969
},
{
"Name": "Person",
"Confidence": 99.30485534667969
},
{
"Name": "Bowl",
"Confidence": 83.46495056152344
},
{
"Name": "Food",
"Confidence": 51.59776306152344
},
{
"Name": "Furniture",
"Confidence": 50.9080810546875
},
{
"Name": "Portrait",
"Confidence": 50.520469665527344
},
{
"Name": "Selfie",
"Confidence": 50.520469665527344
}
],
"OrientationCorrection": "ROTATE_0"
}
{
"FaceDetails": [
{
"BoundingBox": {
"Width": 0.2286902219057083,
"Height": 0.30531322956085205,
"Left": 0.39798039197921753,
"Top": 0.17049960792064667
},
"AgeRange": {
"Low": 35,
"High": 52
},
"Smile": {
"Value": false,
"Confidence": 99.96104431152344
},
"Eyeglasses": {
"Value": false,
"Confidence": 99.99803924560547
},
"Sunglasses": {
"Value": false,
"Confidence": 99.96407318115234
},
"Gender": {
"Value": "Male",
"Confidence": 98.87186431884766
},
"Beard": {
"Value": false,
"Confidence": 99.96611785888672
},
"Mustache": {
"Value": false,
"Confidence": 99.72440338134766
},
"EyesOpen": {
"Value": true,
"Confidence": 98.1362075805664
},
"MouthOpen": {
"Value": false,
"Confidence": 99.86360931396484
},
"Emotions": [
{
"Type": "CALM",
"Confidence": 79.443603515625
},
{
"Type": "SAD",
"Confidence": 10.143173217773438
},
{
"Type": "ANGRY",
"Confidence": 8.333391189575195
}
],
"Landmarks": [
{
"Type": "eyeLeft",
"X": 0.4735484719276428,
"Y": 0.29314953088760376
},
{
"Type": "eyeRight",
"X": 0.552726149559021,
"Y": 0.2864242494106293
},
{
"Type": "nose",
"X": 0.5234030485153198,
"Y": 0.3416678309440613
},
{
"Type": "mouthLeft",
"X": 0.4967532455921173,
"Y": 0.40519285202026367
},
{
"Type": "mouthRight",
"X": 0.5460912585258484,
"Y": 0.4002365469932556
},
{
"Type": "leftPupil",
"X": 0.4636710584163666,
"Y": 0.291595458984375
},
{
"Type": "rightPupil",
"X": 0.5477691292762756,
"Y": 0.2862780690193176
},
{
"Type": "leftEyeBrowLeft",
"X": 0.44416821002960205,
"Y": 0.2564706802368164
},
{
"Type": "leftEyeBrowRight",
"X": 0.4688655436038971,
"Y": 0.2472878247499466
},
{
"Type": "leftEyeBrowUp",
"X": 0.49349144101142883,
"Y": 0.2572619318962097
},
{
"Type": "rightEyeBrowLeft",
"X": 0.5358542799949646,
"Y": 0.2516691982746124
},
{
"Type": "rightEyeBrowRight",
"X": 0.5540421605110168,
"Y": 0.24033693969249725
},
{
"Type": "rightEyeBrowUp",
"X": 0.5732214450836182,
"Y": 0.2406926304101944
},
{
"Type": "leftEyeLeft",
"X": 0.45701533555984497,
"Y": 0.29349184036254883
},
{
"Type": "leftEyeRight",
"X": 0.48969772458076477,
"Y": 0.293197363615036
},
{
"Type": "leftEyeUp",
"X": 0.4736858308315277,
"Y": 0.286814421415329
},
{
"Type": "leftEyeDown",
"X": 0.47360309958457947,
"Y": 0.2992895543575287
},
{
"Type": "rightEyeLeft",
"X": 0.5374438166618347,
"Y": 0.28980445861816406
},
{
"Type": "rightEyeRight",
"X": 0.5670959949493408,
"Y": 0.284206748008728
},
{
"Type": "rightEyeUp",
"X": 0.5524247288703918,
"Y": 0.28035080432891846
},
{
"Type": "rightEyeDown",
"X": 0.5534836649894714,
"Y": 0.2919163405895233
},
{
"Type": "noseLeft",
"X": 0.5067237019538879,
"Y": 0.3605457544326782
},
{
"Type": "noseRight",
"X": 0.5351842045783997,
"Y": 0.35733121633529663
},
{
"Type": "mouthUp",
"X": 0.5246664881706238,
"Y": 0.38926947116851807
},
{
"Type": "mouthDown",
"X": 0.5240681767463684,
"Y": 0.4240013659000397
}
],
"Pose": {
"Roll": -4.440439224243164,
"Yaw": 5.928189754486084,
"Pitch": 7.750663757324219
},
"Quality": {
"Brightness": 52.19685363769531,
"Sharpness": 98.55191040039062
},
"Confidence": 99.98099517822266
}
],
"OrientationCorrection": "ROTATE_0"
}
よかった。ちゃんと男として判定されたw
ほかにも35~52歳と判定されていたり、「球体」っていうタグがついていたりと、そこそこ面白い結果になりましたw
また顔の部位の位置やどんな表情なのかまで判定してくれています
この結果の中から必要なAttributeを取り出して、Slackなりに通知するBotでも作ろうかな・・・
ではまた!