Fusic Tech Blog

Fusicエンジニアによる技術ブログ

SORACOM AI カメラ 「S+ Camera Basic」の初期設定から、画像をAWS S3に保存するまで解説!
2024/03/29

SORACOM AI カメラ 「S+ Camera Basic」の初期設定から、画像をAWS S3に保存するまで解説!

こんにちは、鷲崎です。最近、SORACOMのAI カメラ「S+ Camera Basic」というエッジAIが可能なカメラデバイスを楽しんでいます。このカメラは、電源をつなぐだけで、通信が可能になる、エッジAIカメラです。通信部分は、SORACOM Air のセルラー通信がになってくれており、機械学習エンジニアがアルゴリズム開発に集中できるという利点があります。

他にも、以下のような素晴らしい利点があります。

  1. デバイス側で処理が行えるので、画像など重いデータの送信量を減らせる
  2. プログラムのデプロイがリモートで可能 (SSHでカメラ内に入る必要がない)
  3. デバイスから、AWS Lambdaを実行することが可能

本記事では、このカメラの初期設定から、デバイスで撮影した画像をAWS S3に保存するまでの手順を紹介したいと思います。

【Ask SA!】S+ Camera Basic 入門と AWS サービスとの連携による表情解析とダッシュボード作成 という公式の記事が出ています。この記事は、AWSとの連携に関する情報として、かなり有益で、本記事の作成時にも参考にしました。

1. 初期設定

カメラの初期設定と、カメラの遠隔操作に使用するSORACOM Mosaicに関して説明します。 SORACOM Mosaicとはエッジデバイスの管理を遠隔からすることができるWebサービスです。

  1. SIMの設定
    SORACOM Mosaic の事前準備 を参考に、SIMを設定できます。このSIMには、SORACOMのサービスを有効化したグループを割り当てることができます。今回は、SORACOM Mosaic用にグループを作成し、 SORACOM Air、メタデータサービス 及び SORACOM Harvest Filesの3つのサービスを有効化します。

  2. SORACOM Mosaicにて、カメラ画像を取得
    SORACOM Mosaic コンソールを利用してデバイスを操作するを参考にしてください。SORACOM MosaicのCAMERAという項目にて画像を取得できます。

2. 開発の準備

エッジ側で動くプログラム(例えば、撮影して、画像をs3に保存するなど)をローカルで開発するため、以下の準備を行います。

  1. アクセス管理 (SORACOM Access Management; SAM)にユーザを追加する
    ローカルから、デバイスにプログラムをアップデートするために必要となります。 SAM ユーザーを作成する を参考に作成します。

3. 開発環境の作成

ここでは、Dockerfileでローカル開発環境を構築する方法を説明します。アルゴリズムの更新方法を主に参考にして、構築しました。

  1. requirements.txtの作成

    SORACOM Mosaic Python module 一覧のモジュールを記載します。 後の処理で、エラーが出たためtflite-runtime==***のみコメントアウトしました。もし、開発に必要なライブラリがあったら、追記、もしくは、別途インストールをしてください。

  2. Dockerfileによる環境構築

    下記のDockerfileを作成します。ここで、上記のようにコメントアウトしたtfliteを別途インストールしていることに注意してください。

    FROM ubuntu:18.04
    ENV DEBIAN_FRONTEND=noninteractive
    RUN apt-get update -y && apt-get install -y build-essential vim \
       wget curl git zip gcc cmake make openssl \
       libssl-dev libbz2-dev libreadline-dev \
       libsqlite3-dev python3-tk tk-dev python-tk \
       libfreetype6-dev libffi-dev liblzma-dev -y jq sudo
    
    RUN git clone https://github.com/yyuu/pyenv.git /root/.pyenv
    ENV HOME  /root
    ENV PYENV_ROOT $HOME/.pyenv
    ENV PATH $PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
    RUN pyenv --version
    RUN pyenv install 3.7.3
    RUN pyenv local 3.7.3
    RUN python --version
    RUN pyenv rehash
    
    # このディレクトリに作成することが重要
    RUN sudo mkdir -p /opt/soracom/python/
    RUN chown -R $(whoami) /opt/soracom/
    RUN python -m venv /opt/soracom/python/
    
    # pyenvの環境を構築
    RUN pip install --upgrade pip
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    
    # tfliteは別途ダウンロードした
    RUN pip3 install https://dl.google.com/coral/python/tflite_runtime-2.1.0.post1-cp37-cp37m-linux_x86_64.whl
    
    # SORACOM CLIのダウンロード
    RUN wget https://github.com/soracom/soracom-cli/releases/download/v0.6.2/soracom_0.6.2_linux_amd64.tar.gz
    RUN tar xvf ./soracom_0.6.2_linux_amd64.tar.gz
    RUN cp ./soracom_0.6.2_linux_amd64/soracom /usr/local/bin/
    
    WORKDIR /workspace
    

    docker-compose.ymlファイルも作成します。

    version: '2.3'
    
    services:
    ai-camera-dev:
       build: .
       container_name: ai-camera-dev
       image: ai-camera-dev
       command: /bin/sh -c "while :; do sleep 10; done"
       volumes: 
          - $PWD:/workspace
       ports:
          - 18071-18080:18071-18080
    

    docker-compose up -d コマンドでDockerを用いた開発環境構築は終了です。

4. Dockerコンテナ内で開発環境を設定

VScodeのリモートエクスプローラから、開発用コンテナを選択することで、簡単にコンテナ内で開発できます。 コンテナ内に入ると、SORACOM CLIの設定と、SORACOM Mosaicデプロイツールの取得を行います。

  1. 実行したいPython開発環境を設定
    source /opt/soracom/python/bin/activate で、ライブラリをインストールしたPython開発環境設定できます。

  2. SORACOM CLI の設定
    SORACOM CLI をインストールし SIM カードの一覧を取得する の記事を参考にしつつ、SORACOM CLIを設定します。 記事中のステップ1は、Dockerで環境構築時に行っているので、ステップ2より行います。

    まず、認証情報の設定します。

    LANG=ja soracom configure
    

    次に、確認のため、SIMカードの一覧を取得します。

    soracom subscribers list
    
  3. SORACOM Mosaicのデプロイツールを取得
    ローカルで開発したプログラムをデバイスにデプロイする必要があり、そのツールが準備されています。ここから、デプロイツールをダウンロードしてください。そして、以下のコマンドが動作するか確認します。

    jq
    mosaic_deploy.sh
    

    もし、動作しない場合は、権限を与えれば良いかもしれません。

5. サンプルアルゴリズムに関して

サンプルプログラムをここからダウンロードして、解凍してください。

まずは、簡単に、カメラから画像を取得する方法を説明します。

  1. 画像取得用のURLを発行
    SORACOM Mosaicにて、Remote Accessというフォームより、SORACOM Napterによるリモートアクセス用のURLを発行できます。設定として、USE TLSにチェックを入れ、portを8080、開発環境のIPアドレスを設定してください。IPアドレスに関しては、空のままだと、SORACOM Mosaicを表示しているIPが設定されます。

    取得したURLに対して、以下のURLを叩けば、画像が表示されます。

    https://<ip>.napter.soracom.io:<port>/v1/startCameraCapture
    https://<ip>.napter.soracom.io:<port>/v1/cameraJpegImage
    

    ここで取得する画像は、思ったより赤みがかっているかもしれません。しかし、その原因は、暗視も可能にするIRカメラを使用している影響です。精度を保証するわけではありませんが、画像が赤みがかっている場合でも、意外と機械学習モデルは認識してくれます。

  2. ローカルの開発環境から画像へのアクセス
    発行したURLを環境変数に設定します。

    export DEVICE_INTERFACE_URI=https://{Napter で発行した URL}
    

    ダウンロードしたサンプルプログラムのディレクトリに移動し、サンプルプログラムを実行します。

    cd ${sample_dir}
    ./CameraApp0
    

    サンプルプログラムの場合、/tmpディレクトリに定期的にカメラ画像が保存されます。

    サンプルプログラム内には、画像の取得ライブラリなども入っており、基本的そのライブラリを駆使しつつ./CameraApp0.pyのプログラムを変更していけば、開発できると思います。

6. デプロイ

実際のデバイスにプログラムをデプロイする方法を説明します。

4で取得したデプロイツールを実行します。

./mosaic_deploy.sh -d <Device id>  /workspace/CameraApp0

の部分は、SORACOM Mosaicで確認できるデプロイしたいデバイスのIDです。

7. 開発時のTips

ここでは、開発時に、対処に迷った部分を解説します。

  1. 環境変数の設定
    SORACOM MosaicのAPP(CAMERAAPP0)という項目にて、環境変数を設定できます。プログラムでは、

    os.getenv( 'SORACOM_ENV_WAIT', 5)
    

    のような形で読み込めます。ローカルで確認する時は、環境変数が設定されておらず、デフォルト値が読み込まれます。

    自由に環境変数名を設定することができないため、もし、別途パラメータを注入したい場合、SORACOM_ENV_FREE_PARAMが使用できます。これは、辞書型で、以下のように設定できます。

    {'PARAM1':1, 'HOGEHOGE': 'test param', 'PARAM2': 'test2'}
    

    この変数は、以下のようにして、読み込むことができます。

    FREE_PARAM = str(os.getenv('SORACOM_ENV_FREE_PARAM', '{}'))
    FREE_PARAM = json.loads(FREE_PARAM)
    
    PARAM1 = int(FREE_PARAM.get('PARAM1', 0))
    PARAM2 = str(FREE_PARAM.get('PARAM2', 'param2'))
    
  2. ライブラリの取得
    デバイス内でPythonプログラムから使用できるライブラリは、ここに記載されています。しかし、要件によっては、必要なライブラリのインストールが必要なこともあると思います。そこで、PreSetupというファイルがサンプルプログラムに含まれており、このPreSetupにインストール処理を書くことで、対処できます。PreSetupは、デバイス側でCameraApp0.pyが実行される前に、実行されるファイルです。例えば、以下のように記載できます。

    #!/bin/bash
    echo PreSetup
    pip install Keras==2.3.1
    pip freeze
    
    mkdir -p /opt/app
    wget -O /opt/app/ssd_mobilenet_v2_coco_quant_postprocess.tflite "https://github.com/google-coral/edgetpu/raw/master/test_data/ssd_mobilenet_v2_coco_quant_postprocess.tflite"
    

    ここでは、機械学習ライブラリの一つであるKerasのインストールと、モデル重みのダウンロードを行っています。これで、機械学習モデルによる予測に必要なライブラリやファイルをダウンロードできます。

8. s3に画像ファイルを送信する

s3への画像送信方法は、【Ask SA!】S+ Camera Basic 入門と AWS サービスとの連携による表情解析とダッシュボード作成 という公式の記事に記載されています。ここでは、実装中に調査したことについて説明したいと思います。

  1. s3のPresigned URLの取得
    s3に保存するためのPresigned URLをAWS Lambdaで発行します。 AWS Lambdaへのアクセスは、SORACOM Funkを用いています。AWS Lambdaの実行権限を与えたロールと実行したいARNを記載することで設定できます。

    デバイス上で実行されるS3のPresigned URL取得関数は、以下のようになります。この関数によりSORACOM Funkを介して、S3に画像ファイルを送信するためのPresigned URLが発行されます。

    def send_req_s3_presignedurl(file_name, bucket_name):
       """
       s3のpresigenedurlを取得する
       file_name: s3に置く際のパス
       bucket_name: バケットの名前
       """
       msg = json.dumps({
                "type": "get_upload_url",
                "file_name": file_name,
                "bucket_name": bucket_name
          }
       endpoint = 'http://funk.soracom.io'
       req = urllib.request.Request(
          endpoint,
          data = msg.encode(),
          headers = {"content-type":"application/json"}
       )
       with urllib.request.urlopen(req) as res:
          response_data = json.loads(res.read().decode('utf8'))
       return response_dat
    

    AWS Lambdaにおいて、Presigned URLを発行するコードは以下になります。最近、AWS LambdaがDockerに対応していたので、AWS Lambdaがコンテナイメージをサポートしたので、Detectron2 を使って画像認識(Object Detection)を行うAPI を作るという記事を参考にしつつ、Docker in AWS Lambdaで動作確認しました。

    import boto3
    
    _expire_time = 600 #10min
    
    def get_pre_signedurl(obj_name, bucket_name):
       s3 = boto3.client('s3')
       try:
          res = s3.generate_presigned_post(
                bucket_name, 
                obj_name, 
                ExpiresIn=_expire_time
          )
          return res
       except Exception as e:
          logger.error(f'failed to create presigned url, {e}')
          return None
    
    def handler(event, context):
       if "file_name" in event:
          file_name = event["file_name"]
          bucket_name = event["bucket_name"]
          res = get_pre_signedurl(file_name, bucket_name)
          if res is None:
                raise ValueError(f"Can not create sigend url: {event}")
       else:
          raise ValueError(f"Can not get event params key: {event}")
       return res
    
  2. s3に画像を送信

    1により発行されたPresigned URLと, デバイスから取得したRGB画像をもとに、s3に画像を保存するプログラムは、以下の通りです。

    def upload_s3(url_data, image):
       """
       画像をs3にアップロードする
       url_data: presiendurlの取得結果
       image: rgb img
       """
       # convert to jpg
       if image is None:
          raise ValueError("Upload s3 can not convert img")
    
       # 画像をエンコード
       encode_param = [cv2.IMWRITE_JPEG_QUALITY, UPLOAD_QUALITY]
       _, im_data = cv2.imencode('.jpg', image, encode_param)
       im_data_byte = im_data.tobytes()
       files = {'file': im_data_byte}
    
       # 送信する情報をまとめる
       if "url" in url_data and "fields" in url_data:
          url = url_data["url"]
          fields = url_data["fields"]
       else:
          raise ValueError("url and fields not included in Presigned url")
       
       # s3に送信
       try:
          res= requests.post(url, data=fields, files=files)
          logger.debug(res.text)
       except Exception as e:
          raise ValueError(f"Upload s3 post fail {e}")
       return True
    

基本的には、上記に示した記事とプログラムで、画像をs3に送信できると思います。

まとめ

以上、SORACOMのAI カメラ「S+ Camera Basic」で取得した画像をs3に保存する方法を紹介しました。最近は、モデルの軽量化手法も多く開発されてきており、エッジで処理することが当たり前になる時代がきているかもしれません。その中で、SORACOMのこのカメラは、面倒な通信部分などは、SORACOM側で処理してくれており、AIや画像処理の実装に集中できるので、機械学習エンジニアとしても重宝しています!

Kai Washizaki

Kai Washizaki

Conpany: Fusic Co., Ltd. Program Language: Python, Go, PHP Interest: Machine Learning, MLOps