AWS Panorama アプリケーションの開発 - AWS Panorama

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

AWS Panorama アプリケーションの開発

サンプルアプリケーションを使用して、AWS Panorama アプリケーションの構造について学んだり、独自のアプリケーションの開始点にしたりできます。

次の図表に、AWS Panorama アプライアンスで実行されるアプリケーションの主要なコンポーネントを示します。アプリケーションコードは AWS Panorama アプリケーションSDK を使用して画像を取得し、直接アクセスできないモデルとやり取りします。アプリケーションは接続されたディスプレイに動画を出力しますが、ローカルネットワークの外部には画像データを送信しません。

AWS Panorama サンプルアプリケーションのアーキテクチャ。

この例では、アプリケーションは AWS Panorama アプリケーション SDK を使用してカメラからビデオフレームを取得し、ビデオデータを前処理して、オブジェクトを検出するコンピュータビジョンモデルにデータを送信します。アプリケーションは、アプライアンスに接続された HDMI ディスプレイに結果を表示します。

アプリケーションマニフェスト。

アプリケーションマニフェストは、graphs フォルダー内の graph.json という名前のファイルです。マニフェストは、パッケージ、ノード、エッジなどのアプリケーションのコンポーネントを定義します。

パッケージは、アプリケーションコード、モデル、カメラ、ディスプレイのコード、構成、バイナリファイルです。サンプルアプリケーションは 4 つのパッケージを使用します。

graphs/aws-panorama-sample/graph.json - パッケージ
"packages": [ { "name": "123456789012::SAMPLE_CODE", "version": "1.0" }, { "name": "123456789012::SQUEEZENET_PYTORCH_V1", "version": "1.0" }, { "name": "panorama::abstract_rtsp_media_source", "version": "1.0" }, { "name": "panorama::hdmi_data_sink", "version": "1.0" } ],

最初の 2 つのパッケージは、アプリケーション内の packages ディレクトリで定義されています。これらには、このアプリケーション固有のコードとモデルが含まれています。次の 2 つのパッケージは、AWS Panorama サービスが提供する汎用のカメラおよびディスプレイパッケージです。abstract_rtsp_media_source パッケージは、デプロイ時にオーバーライドするカメラのプレースホルダーです。hdmi_data_sink パッケージはデバイスの HDMI 出力コネクタを表します。

ノードはパッケージへのインターフェースであると同時に、デプロイ時にオーバーライドできるデフォルト値を持つ非パッケージパラメーターのインターフェースでもあります。コードパッケージとモデルパッケージは、ビデオストリームでも、フロート、ブーリアン、文字列などの基本データ型でも、入力と出力を指定する package.json ファイル内のインターフェイスを定義します。

たとえば、code_node ノードは SAMPLE_CODE パッケージのインターフェースを参照します。

"nodes": [ { "name": "code_node", "interface": "123456789012::SAMPLE_CODE.interface", "overridable": false, "launch": "onAppStart" },

このインターフェースはパッケージ構成ファイル、package.json 内で定義されています。このインターフェースは、パッケージがビジネスロジックであり、video_in という名前のビデオストリームと thresholdという名前の浮動小数点数を入力として受け取ることを指定しています。また、このインターフェースでは、ビデオをディスプレイに出力するための video_out という名前のビデオストリームバッファがコードに必要であることも指定されています。

packages/123456789012-SAMPLE_CODE-1.0/package.json
{ "nodePackage": { "envelopeVersion": "2021-01-01", "name": "SAMPLE_CODE", "version": "1.0", "description": "Computer vision application code.", "assets": [], "interfaces": [ { "name": "interface", "category": "business_logic", "asset": "code_asset", "inputs": [ { "name": "video_in", "type": "media" }, { "name": "threshold", "type": "float32" } ], "outputs": [ { "description": "Video stream output", "name": "video_out", "type": "media" } ] } ] } }

アプリケーションマニフェストに戻ると、camera_node ノードはカメラからのビデオストリームを表しています。これには、アプリケーションをデプロイするとコンソールに表示され、カメラストリームの選択を促すデコレータが含まれています。

graphs/aws-panorama-sample/graph.json – Camera ノード
{ "name": "camera_node", "interface": "panorama::abstract_rtsp_media_source.rtsp_v1_interface", "overridable": true, "launch": "onAppStart", "decorator": { "title": "Camera", "description": "Choose a camera stream." } },

パラメータノード threshold_param は、アプリケーションコードで使用される信頼度しきい値パラメータを定義します。デフォルト値は 60 で、デプロイ時に上書きできます。

graphs/aws-panorama-sample/graph.json — パラメータノード
{ "name": "threshold_param", "interface": "float32", "value": 60.0, "overridable": true, "decorator": { "title": "Confidence threshold", "description": "The minimum confidence for a classification to be recorded." } }

アプリケーションマニフェストの最後のセクション edges では、ノード間の接続を行います。カメラのビデオストリームとしきい値パラメータはコードノードの入力に接続し、コードノードからのビデオ出力はディスプレイに接続します。

graphs/aws-panorama-sample/graph.json - エッジ
"edges": [ { "producer": "camera_node.video_out", "consumer": "code_node.video_in" }, { "producer": "code_node.video_out", "consumer": "output_node.video_in" }, { "producer": "threshold_param", "consumer": "code_node.threshold" } ]

サンプルアプリケーションでのビルド

サンプルコードを開始点として使用して、独自のアプリケーションを作成することができます。

各パッケージ名は一意にする必要があります。自分とアカウント内の別のユーザーの両方が codemodel などの汎用パッケージ名を使用している場合、デプロイ時に間違ったバージョンのパッケージが表示される可能性があります。コードパッケージの名前を、自分のアプリケーションを表す名前に変更してください。

コードパッケージの名前を変更するには
  1. パッケージフォルダーの名前を変更します:packages/123456789012-SAMPLE_CODE-1.0/

  2. 次の場所でパッケージ名を更新します。

    • アプリケーションマニフェストgraphs/aws-panorama-sample/graph.json

    • パッケージ構成packages/123456789012-SAMPLE_CODE-1.0/package.json

    • ビルドスクリプト3-build-container.sh

アプリケーションコードを更新するには
  1. packages/123456789012-SAMPLE_CODE-1.0/src/application.py でアプリケーションコードを変更します。

  2. コンテナを構築するには、3-build-container.sh を実行します。

    aws-panorama-sample$ ./3-build-container.sh TMPDIR=$(pwd) docker build -t code_asset packages/123456789012-SAMPLE_CODE-1.0 Sending build context to Docker daemon 61.44kB Step 1/2 : FROM public.ecr.aws/panorama/panorama-application ---> 9b197f256b48 Step 2/2 : COPY src /panorama ---> 55c35755e9d2 Successfully built 55c35755e9d2 Successfully tagged code_asset:latest docker export --output=code_asset.tar $(docker create code_asset:latest) gzip -9 code_asset.tar Updating an existing asset with the same name { "name": "code_asset", "implementations": [ { "type": "container", "assetUri": "98aaxmpl1c1ef64cde5ac13bd3be5394e5d17064beccee963b4095d83083c343.tar.gz", "descriptorUri": "1872xmpl129481ed053c52e66d6af8b030f9eb69b1168a29012f01c7034d7a8f.json" } ] } Container asset for the package has been succesfully built at ~/aws-panorama-sample-dev/assets/98aaxmpl1c1ef64cde5ac13bd3be5394e5d17064beccee963b4095d83083c343.tar.gz

    CLI は古いコンテナアセットを assets フォルダから自動的に削除し、パッケージ構成を更新します。

  3. パッケージをアップロードするには、4-package-application.py を実行します。

  4. AWS Panorama コンソールの [デプロイされたアプリケーション] ページを開きます。

  5. アプリケーションを選択します。

  6. Replace (置換)]を選択します。

  7. アプリケーションを実行するには、手順に従います。必要に応じて、アプリケーションマニフェスト、カメラストリーム、またはパラメータを変更できます。

コンピュータービジョンモデルの変更

サンプルアプリケーションにはコンピュータビジョンモデルが含まれています。独自のモデルを使用するには、モデルノードの構成を変更し、AWS Panorama アプリケーション CLI を使用してアセットとしてインポートします。

次の例では MXNet SSD ResNet50 モデルを使用しています。このモデルは、このガイドの GitHub リポジトリからダウンロードできます。ssd_512_resnet50_v1_voc.tar.gz

サンプルアプリケーションのモデルを変更するには
  1. モデルに合うようにパッケージフォルダーの名前を変更します。たとえば、packages/123456789012-SSD_512_RESNET50_V1_VOC-1.0/ などにします。

  2. 次の場所でパッケージ名を更新します。

    • アプリケーションマニフェストgraphs/aws-panorama-sample/graph.json

    • パッケージ構成packages/123456789012-SSD_512_RESNET50_V1_VOC-1.0/package.json

  3. パッケージ構成ファイル (package.json) 内。assets 値を空白の配列に変更します。

    { "nodePackage": { "envelopeVersion": "2021-01-01", "name": "SSD_512_RESNET50_V1_VOC", "version": "1.0", "description": "Compact classification model", "assets": [],
  4. パッケージ記述ファイル (descriptor.json) を開きます。モデルと一致するようにframework およびshape の値を更新します。

    { "mlModelDescriptor": { "envelopeVersion": "2021-01-01", "framework": "MXNET", "inputs": [ { "name": "data", "shape": [ 1, 3, 512, 512 ] } ] } }

    シェイプ の値は 1,3,512,512、モデルが入力として受け取る画像の数 (1)、各画像のチャネル数 (3–- 赤、緑、青 )、および画像のサイズ (512 x 512) を示します。配列の値と順序はモデルによって異なります。

  5. AWS Panorama アプリケーション CLI を使用してモデルをインポートします。AWS Panorama アプリケーション CLI は、モデルファイルと記述子ファイルを一意の名前で assets フォルダにコピーし、パッケージ構成を更新します。

    aws-panorama-sample$ panorama-cli add-raw-model --model-asset-name model-asset \ --model-local-path ssd_512_resnet50_v1_voc.tar.gz \ --descriptor-path packages/123456789012-SSD_512_RESNET50_V1_VOC-1.0/descriptor.json \ --packages-path packages/123456789012-SSD_512_RESNET50_V1_VOC-1.0 { "name": "model-asset", "implementations": [ { "type": "model", "assetUri": "b1a1589afe449b346ff47375c284a1998c3e1522b418a7be8910414911784ce1.tar.gz", "descriptorUri": "a6a9508953f393f182f05f8beaa86b83325f4a535a5928580273e7fe26f79e78.json" } ] }
  6. モデルをアップロードするには、panorama-cli package-application を実行します。

    $ panorama-cli package-application Uploading package SAMPLE_CODE Patch Version 1844d5a59150d33f6054b04bac527a1771fd2365e05f990ccd8444a5ab775809 already registered, ignoring upload Uploading package SSD_512_RESNET50_V1_VOC Patch version for the package 244a63c74d01e082ad012ebf21e67eef5d81ce0de4d6ad1ae2b69d0bc498c8fd upload: assets/b1a1589afe449b346ff47375c284a1998c3e1522b418a7be8910414911784ce1.tar.gz to s3://arn:aws:s3:us-west-2:454554846382:accesspoint/panorama-123456789012-wc66m5eishf4si4sz5jefhx 63a/123456789012/nodePackages/SSD_512_RESNET50_V1_VOC/binaries/b1a1589afe449b346ff47375c284a1998c3e1522b418a7be8910414911784ce1.tar.gz upload: assets/a6a9508953f393f182f05f8beaa86b83325f4a535a5928580273e7fe26f79e78.json to s3://arn:aws:s3:us-west-2:454554846382:accesspoint/panorama-123456789012-wc66m5eishf4si4sz5jefhx63 a/123456789012/nodePackages/SSD_512_RESNET50_V1_VOC/binaries/a6a9508953f393f182f05f8beaa86b83325f4a535a5928580273e7fe26f79e78.json { "ETag": "\"2381dabba34f4bc0100c478e67e9ab5e\"", "ServerSideEncryption": "AES256", "VersionId": "KbY5fpESdpYamjWZ0YyGqHo3.LQQWUC2" } Registered SSD_512_RESNET50_V1_VOC with patch version 244a63c74d01e082ad012ebf21e67eef5d81ce0de4d6ad1ae2b69d0bc498c8fd Uploading package SQUEEZENET_PYTORCH_V1 Patch Version 568138c430e0345061bb36f05a04a1458ac834cd6f93bf18fdacdffb62685530 already registered, ignoring upload
  7. アプリケーションコードを更新します。ほとんどのコードは再利用できます。モデルのレスポンスに固有のコードは。process_results メソッド内にあります。

    def process_results(self, inference_results, stream): """Processes output tensors from a computer vision model and annotates a video frame.""" for class_tuple in inference_results: indexes = self.topk(class_tuple[0]) for j in range(2): label = 'Class [%s], with probability %.3f.'% (self.classes[indexes[j]], class_tuple[0][indexes[j]]) stream.add_label(label, 0.1, 0.25 + 0.1*j)

    モデルに応じて、preprocess メソッドを更新する必要があることもあります。

画像の前処理

アプリケーションがイメージをモデルに送信する前に、イメージのサイズを変更してカラーデータを正規化することで、イメージを推論できるように準備します。アプリケーションが使用するモデルには、最初のレイヤーの入力数に合わせて、3 つのカラーチャンネルを含む 224 x 224 ピクセルの画像が必要です。アプリケーションは、各色の値を 0 から 1 の間の数値に変換し、その色の平均値を引いて標準偏差で割ることによって調整します。最後に、カラーチャンネルを結合し、モデルが処理できるNumPy 配列に変換します。

アプリケーション.py — 前処理
def preprocess(self, img, width): resized = cv2.resize(img, (width, width)) mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] img = resized.astype(np.float32) / 255. img_a = img[:, :, 0] img_b = img[:, :, 1] img_c = img[:, :, 2] # Normalize data in each channel img_a = (img_a - mean[0]) / std[0] img_b = (img_b - mean[1]) / std[1] img_c = (img_c - mean[2]) / std[2] # Put the channels back together x1 = [[[], [], []]] x1[0][0] = img_a x1[0][1] = img_b x1[0][2] = img_c return np.asarray(x1)

このプロセスにより、0 を中心とした予測可能な範囲内のモデル値が得られます。これはトレーニングデータセット内の画像に適用される前処理と一致します。これは標準的な方法ですが、モデルごとに異なる場合があります。

Python 用 SDK によるメトリクスのアップロード

サンプルアプリケーションは Python 用 SDK を使用して Amazon CloudWatch にメトリクスをアップロードします。

アプリケーション.py — Python 用 SDK
def process_streams(self): """Processes one frame of video from one or more video streams.""" ... logger.info('epoch length: {:.3f} s ({:.3f} FPS)'.format(epoch_time, epoch_fps)) logger.info('avg inference time: {:.3f} ms'.format(avg_inference_time)) logger.info('max inference time: {:.3f} ms'.format(max_inference_time)) logger.info('avg frame processing time: {:.3f} ms'.format(avg_frame_processing_time)) logger.info('max frame processing time: {:.3f} ms'.format(max_frame_processing_time)) self.inference_time_ms = 0 self.inference_time_max = 0 self.frame_time_ms = 0 self.frame_time_max = 0 self.epoch_start = time.time() self.put_metric_data('AverageInferenceTime', avg_inference_time) self.put_metric_data('AverageFrameProcessingTime', avg_frame_processing_time) def put_metric_data(self, metric_name, metric_value): """Sends a performance metric to CloudWatch.""" namespace = 'AWSPanoramaApplication' dimension_name = 'Application Name' dimension_value = 'aws-panorama-sample' try: metric = self.cloudwatch.Metric(namespace, metric_name) metric.put_data( Namespace=namespace, MetricData=[{ 'MetricName': metric_name, 'Value': metric_value, 'Unit': 'Milliseconds', 'Dimensions': [ { 'Name': dimension_name, 'Value': dimension_value }, { 'Name': 'Device ID', 'Value': self.device_id } ] }] ) logger.info("Put data for metric %s.%s", namespace, metric_name) except ClientError: logger.warning("Couldn't put data for metric %s.%s", namespace, metric_name) except AttributeError: logger.warning("CloudWatch client is not available.")

デプロイ時に割り当てたランタイムロールから権限を取得します。ロールはaws-panorama-sample.yml AWS CloudFormation テンプレートで定義されます。

aws-panorama-サンプル.yml
Resources: runtimeRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - panorama.amazonaws.com Action: - sts:AssumeRole Policies: - PolicyName: cloudwatch-putmetrics PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: 'cloudwatch:PutMetricData' Resource: '*' Path: /service-role/

サンプルアプリケーションは、Python 用の SDK とその他の依存関係をピップでインストールします。アプリケーションコンテナをビルドすると、Dockerfile はコマンドを実行して、ベースイメージに付属しているものの上にライブラリをインストールします。

Dockerfile
FROM public.ecr.aws/panorama/panorama-application WORKDIR /panorama COPY . . RUN pip install --no-cache-dir --upgrade pip && \ pip install --no-cache-dir -r requirements.txt

AWS SDK をアプリケーションコードで使用するには、まずテンプレートを変更して、アプリケーションが使用するすべての API アクションにアクセス権限を追加します。変更を加えるたびに 1-create-role.sh を実行してAWS CloudFormation スタックを更新します。次に、変更内容をアプリケーションコードにデプロイします。

既存のリソースを変更または使用するアクションでは、ターゲット Resource の名前またはパターンを別のステートメントで指定して、このポリシーの範囲を最小限に抑えるのがベストプラクティスです。各サービスでサポートされているアクションとリソースの詳細については、サービス承認リファレンスの「アクション、リソース、条件キー」を参照してください。

次のステップ

AWS Panorama アプリケーション CLI を使用してアプリケーションをビルドし、パッケージを最初から作成する手順については、CLI の README を参照してください。

デプロイ前にアプリケーションコードを検証するために使用できるその他のサンプルコードとテストユーティリティについては、AWS Panorama サンプルリポジトリをご覧ください。