【Part6】遠隔でのカメラ撮影&画像をS3に転送する
AWSでエッジコンピューティング環境を作る Part6です。
Part5までで、エッジ端末からのメッセージをS3に保存することができました。エッジからクラウドストレージまでつながったわけです。
しかし、S3に保存していたのは単調な文字列です。せっかくエッジの端末があるんだったら、エッジから取得したデータを格納したいですね。今回は、ラズパイカメラで写真を撮影して、S3に保存していきます。
すべき事が結構たくさんあるので、順を追って説明しますね
・Lambda関数の変更
・サブスクリプションの設定(AWS IoT⇨ラズパイ方向)
・リソースの設定
・デプロイ・確認
・S3に保存
Lambda関数の変更
※今回は、Part5まで使っていたGreengrass_Helloworld関数を使い回します。
カメラで写真を撮り、そのデータをMQTTで送信するように、関数の中身を書き換えます。
import greengrasssdk
import StringIO
from io import BytesIO
import picamera
import time
import logging
import json
import base64
# Initialize logger
customer_logger = logging.getLogger(__name__)
# Create MQTT client
iot_client = greengrasssdk.client('iot-data')
topic_mes = "/part6/message"
topic_pic = "/part6/picture"
class Camera(object):
# Capture Image
def capture_image(self):
camera = picamera.PiCamera()
imageData = StringIO.StringIO()
try:
camera.resolution = (224, 224)
camera.start_preview()
time.sleep(2)
camera.capture(imageData, format = "jpeg", resize = (224, 224))
camera.stop_preview()
imageData.seek(0)
return imageData
finally:
camera.close()
raise RuntimeError("There is problem to use your camera.")
def send_mqtt_message(mes):
iot_client.publish(topic=topic_mes,payload=mes)
def send_mqtt_picture(imgdata):
iot_client.publish(topic=topic_pic, payload=json.dumps({"data": imgdata}))
def take_pic():
"""
take picture with picamera
"""
send_mqtt_message("Taking a Photo")
my_camera = Camera()
imagebinary = my_camera.capture_image()
# DataEncode
image64 = base64.b64encode(imagebinary.getvalue())
image_str = image64.decode("utf-8")
return image_str
# The lambda to be invoked in Greengrass
def function_handler(event, context):
try:
img = take_pic()
send_mqtt_picture(img)
send_mqtt_message("Finish sending bin of picture")
except Exception as e:
customer_logger.exception(e)
send_mqtt_message('Exception occurred during prediction. Please check logs for troubleshooting: /greengrass/ggc/var/log.')
これにより、メッセージが"/part6/message"に、撮った写真(のbase64文字列)が"/part6/picture"に送られます。
picameraは写真をラズパイで撮るだけなので、まあ良いとして、ややこしいのは撮った画像の取り扱いです。推論用にも使うことを考え、↓のように変換してやりました。MQTTで送るのは文字列なので、上手くエンコードする必要があります。(この辺、試行錯誤した結果なので、もっと効率良い方法があれば教えてもらえると泣いて喜びます。)
【リソース】Lambdaでラズパイのカメラにアクセスするための設定
さて、ローカルのラズパイであれば、あまり意識せずpythonのpicameraライブラリを使えばよいのですが、クラウド上からラズパイのカメラを利用するためには、Lambdaがラズパイのローカルリソース(今回はカメラ)にアクセスする必要があります。Part4の図を再掲しますが、ラズパイのローカルリソースにアクセスするためには、Greengrassの"リソース"を設定します。
下記になるように、リソースを2つ作成します。それぞれ作成時の入力欄を埋めていってください。
Lambda関数を指定する箇所がありますが、カメラ画像をリソースに書き込むので、"読み取りと書き込みを許可"にチェックを入れておきましょう。
サブスクリプションの設定
デプロイ前に、サブスクリプションを設定し直します(詳しくはPart4参照)
今回は、下記のトピックを設定します
AWS IoT -> Lambda のトリガー用 : "/part6/trigger"
Lambda -> AWS IoT メッセージ送信用 : "/part6/message"
Lambda -> AWS IoT 画像送信用 : "/part6/picture"
カメラ発火のtopicだけ画像を貼っておきます。
最終的に、下図のようなサブスクリプションが作られていれば、上手く動作するはずです。
デプロイ・確認
ここまで設定が完了したら、デプロイして、AWS IoTのテストで動作確認しましょう。上手くいけばこのように、それぞれのトピックにそれぞれのデータが送られているはずです!
S3に保存
さて、最後に、S3にこの画像も保存してやりましょう。S3保存時に、文字列型から、画像ファイルに変換したいので、Part5で説明した通り、AWS IoTにLambdaの処理を噛ましてからS3に保存します。
AWS IoTのルールは下図の様にしてください。利用するLambdaは前回作った"test-fromIoTtoS3"を利用します
次はLambdaの書き換えです。ルールを新しく作ったので、Lambdaを発火するタイミングを下記の様に変更します。
トリガーを設定したら、S3に保存する関数を書きます。前回と違って画像ファイルを保存するので、それ用のコードを貼っておきます。
import os
import boto3
from datetime import datetime
from io import BytesIO
from base64 import b64decode
def upload_img(img_binary):
s3 = boto3.resource('s3')
bucket = os.environ["BUCKET"]
dir = "images"
filename = 'img_{}.jpg'.format(datetime.now().strftime('%Y%m%d-%H%M%S'))
obj = s3.Object(bucket, os.path.join(dir,filename))
print("put",filename,"to",bucket)
obj.put(Body=BytesIO(img_binary),ContentType="image/jpg")
return
def lambda_handler(event, context):
print("test",event)
img = event["data"]
img_binary = b64decode(img)
upload_img(img_binary)
return 0
成功していれば、s3のバケットにimages/<オブジェクト名>.jpgとして、保存されているはずです!
まとめ
これで、ラズパイの写真をクラウド上のS3に保存することができました。もちろん画像ではなく、センサーデータなどでも応用可能です(というか、画像みたいにエンコード/デコードがないのでそっちのほうが簡単です...笑)
次回はいよいよ推論部を作っていきたいと思います!いよいよエッジコンピューティングのさわり部分です。ではではっ
Part5. S3転送編
Part7. モデル圧縮専用ライブラリDistiller
サポートいただけると励みになります! よろしくおねがいします!!