The purpose of this package is to provide a CLI tool to facilitate Panorama developer creating a local application as well as being able to upload the application to the cloud using the CLI.
Dependencies
You will need Docker and AWS CLI installed on your machine.
Docker is required for building a package and AWS CLI is needed for downloading a model from S3 and packaging the application to Panorama Cloud.
Since Panorama CLI builds ARM Docker images, it needs these extra steps on Linux to build cross platform images. Installing Docker Desktop on Mac should automatically handle cross platform builds.
This is an example of a sample app which has two node packages. people_counter package has core logic for counting the number of people, call_node has the model which people_counter package uses. We will also add an abstract camera to the application which can be linked to a real camera from the console while deploying the application.
$ panorama-cli init-project --name example_project
Successfully created the project skeleton at <path>
$ cd example_project
$ panorama-cli create-package --name people_counter
$ panorama-cli create-package --name call_node --type Model
Application Structure
At this point, the application structure looks as follows.
graph.json under graphs directory lists down all the packages, nodes and edges in this application. Nodes and Edges are the way to define an application graph in Panorama.
package.json in each package has details about the package and the assets it uses. Interface definitions for the package need to be defined in this as well.
Model package call-node has a descriptor.json which needs to have the metadata required for compiling the model. More about this in the models section.
In people_counter package which is the default i.e container type, all the implementation related files go into the src directory and descriptor.json has details about which command and file to use when the container is launched. More about container package management later.
assets directory is where all the assets reside. Developer is not expected to make any changes in this directory.
Panorama has a concept of Abstract Camera Package which the developers can use while developing their apps. This abstract camera package can be overriden and linked to an actual camera in the developer’s Panorama account while deploying.
Let’s add an abstract camera to this application by running the following command.
$ panorama-cli add-panorama-package --type camera --name front_door_camera
This command defines an abstract camera package in the packages section and adds the following snippet in the nodes section of the graph.json. You can modify the title and description to be more relevant to the use case.
rtsp_v1_interface is the defined interface for an abstract camera and it has an output port called video_out which can be used to forward the camera output to another node. More details about connecting this camera are discussed in the app graph section below
Preparing a model for Panorama
Raw models are compiled using Sagemaker Neo on Panorama Cloud before being deployed onto the device. All models for this reason are paired with a descriptor.json which has the required meta deta for compiling the raw model on the cloud.
All the model info that is used to compile models on Sagemaker Neo are part of the descriptor.json. Values used in this example are specific to the squeezenet model that is being used in this example.
Since call_node has the model in this example, edit packages/accountXYZ-call-node-1.0/descriptor.json and add the following snippet into it.
Now we can add the model by passing in the path to the descriptor file which we just updated.
If you want to download the model from S3 and then add it pass --model-s3-uri as shown below. Otherwise just use --model-local-path to pass the local model path instead.
--packages-path can be used to pass all the packges where this model is being used and after downloading the model, DX CLI automatically adds the downloaded model into assets section of all the specified packages.
If you make any updates to your model or desriptor.json file after running this command, just re-run the command with the same --model-asset-name and the old asset will be updated with the new assets.
Writing code and building a container
people_counter package has the core logic to count the number of people, so let’s create a file called people_counter_main.py at packages/accountXYZ-people_counter-1.0/src and add the relevant code to that.
Edit packages/accountXYZ-people_counter-1.0/descriptor.json to have the following content
descriptor.json basically provides the path for the command that needs to run and the path to the file that needs to be executed once the container starts.
We can now build the package using the following command to create a container asset.
If you make any updates to your code or desriptor.json or Dockerfile file after building a container, just re-run the command with the same --container-asset-name and the old assets will be updated with the new assets.
Defining interfaces and app graph
Let’s take a look at how the package.json looks for people_counter package after running build-container
A new asset named people_counter_container_binary has been added under assets and a new interface named people_counter_container_binary_interface has been defined. In Panorama, interfaces are a way to programtically interact with a package and each interface is linked to an asset. For example, people_counter_container_binary_interface has an asset field which points to people_counter_container_binary. That means that we are defining an interface to that asset. In this case, since our asset is a container with your code in it, all the inputs your code expects can be part of the inputs under interfaces. In this example, the code just expects one input which is a video stream. If output of your code needs to be consumed by another asset, that can be part of the ouputs. Similarly, a new interface was added to the call-node package when we can add-raw-model command. In that case, interface was linked to the model asset which we added using that command.
At this point, graph.json under the graphs directory looks like this
packages section here has all the packages that are part of this application and we can see that nodes section has some nodes defined already. To be able to use any package, we need to define a corresponding nodes in the graph.json for all the interfaces that are part of the package. people_counter_container_binary_node node is linked to people_counter_container_binary_interface interface from people_counter package which we just looked at and similarly callable_squeezenet node is linked to callable_squeezenet_interface interface from the call_node package. We already discussed the front_door_camera node in setting up cameras section
Next thing we will do is set up the edges for the application graph. people_counter_container_binary_interface had one input video_in as part of the interface definition and that was the video input to the code in that package. We can connect that input to the camera node’s output by adding the following edge under the edges section.
Registering and Uploading all local packages in the Cloud
When the application is ready, use the following command to upload all the packages to the cloud
$ panorama-cli package-application
Deploying the application
After packaging the application, you can now use the graph.json from the package to start a deployment from the cloud!
Panorama Application Concepts
Container Package Basics
Handling implementation related files
This is a directory tree of how an example container package. All the implementation related files for this package go into the src directory. In this package, people_counter_main.py has the logic for processing the frames from the camera and people_counter_main.py depends on blueprint.csv and requirements.json for some of its functionality and therefore those are under the src directory as well. If the application requires multiple .py files then all those will be under the src directory as well.
Let’s now take a look at the Dockerfile provided as part of the package
FROM public.ecr.aws/panorama/panorama-application
COPY src /panorama
In the second line, we are basically copying all the contents of the src directory into the /panorama directory of the Docker image. Therefore, its important to note that when people_counter_main.py accesses other files which were originally part of the src directory, they are actually under /panorama when the application is running on the Panorama Appliance.
Handling dynamic data
Since all the containers run in read-only mode on the Panorama Appliance, its not possible to create new files at all paths. To facilitate this, Panorama base image(i.e first line in Dockerfile) has two directories /opt/aws/panorama/logs and /opt/aws/panorama/storage which are empty. During runtime, these directories are mounted to the device file system and allow the developer to store new files and data dynamically.
/opt/aws/panorama/logs is the location to store all the logs and all files created in the directory are uploaded to CloudWatch for the account which was used to provision the device.
/opt/aws/panorama/storage is a good location to store all the dynamic info that the application might need.
When the device re-starts, all the memory locations are deleted but the data under these two directories is persistent and therefore should contain all the context for the application to function from where it left off on a reboot.
Installing dependencies
The image(public.ecr.aws/panorama/panorama-application) provided by Panorama is a ARM64v8 Ubuntu image with just Panorama base software installed so all the additional dependencies for the application must be installed separately. For example, add the following line to the Dockerfile to install OpenCV and boto3. This Dockerfile fetches the latest panorama-application image by default, to use a specific version of the image, refer to https://gallery.ecr.aws/panorama/panorama-application and tag the image in the Dockerfile with the version you’re planning to use.
FROM public.ecr.aws/panorama/panorama-application
COPY src /panorama
RUN pip3 install opencv-python boto3
Overriding Abstract Cameras
We added an Abstract Camera in setting up cameras section. If you’re deploying an app through Panorama console, you will be automatically promted to replace the abstract camera node with a data source in your account. This section mentions how to create an override.json which can be used to replace abstract camera with a real camera while deploying applications from command line.
Create a Camera node using the following command. --output-package-name is the name of the camera package and --node-name specifies the node name under this package. We are keeping the names for both of these as my_rtsp_camera to keep it simple. Update --template-parameters with the correct Username, Password and StreamUrl.
Since the status says succeeded, we are now ready to use this camera. Now, let’s create an override.json for this camera. Here we are replacing front_door_camera node which we created in setting up cameras section with the newly created my_rtsp_camera
If you want to process streams from two or more cameras together, you can also replace the front_door_camera node with multiple cameras. For processing two streams together for example, create a second camera using the steps mentioned above and use the following override file.
At the end of last section, we looked at how to modify the override file we want to process streams from multiple cameras together. This section speaks about using multiple camera streams in the same application and processing them separately. This is useful when multiple cameras are being used for different purposes.
Let’s take the package.json of people_counter package from the defining interfaces section and modify it to have two video inputs.
Let’s add another camera to the application by using the following command.
$ panorama-cli add-panorama-package --type camera --name back_door_camera
A node named back_door_camera will be added into the nodes section of graph.json and let us connect both the cameras to the video inputs defined above in the edges section as shown below.
In this people counter example application, if we also want to draw bounding boxes around people and view those processed frames on a screen, we can do that as well by adding a Data Sink node. Data Sink node forwards the input it receives to the HDMI port.
Like the abstract camera package, Panorama also provides a data sink package and we can create a data_sink using the following command.
We can now connect this data_sink_node to the output of people_counter_container_binary_node in the edges section. At this point, graph.json looks as follows.
PanoramaDevPythonCLI
The purpose of this package is to provide a CLI tool to facilitate Panorama developer creating a local application as well as being able to upload the application to the cloud using the CLI.
Dependencies
You will need Docker and AWS CLI installed on your machine. Docker is required for building a package and AWS CLI is needed for downloading a model from S3 and packaging the application to Panorama Cloud.
Docker Setup
https://docs.docker.com/get-docker/
Since Panorama CLI builds ARM Docker images, it needs these extra steps on Linux to build cross platform images. Installing Docker Desktop on Mac should automatically handle cross platform builds.
On Debian based distros
On CentOS/RHEL based distros
AWS CLI Setup
https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html
AWS CLI version should be >=2.3.0 for v2 and >=1.21.0 for v1
Install
Panorama CLI is only supported on Linux and macOS right now.
Upgrade
To upgrade to the latest release
Commands
Basic structure of the commands is as follows
To view help documentation, use one of the following
Deploying a sample application
Instructions for downloading and deploying a sample application can be found at https://docs.aws.amazon.com/panorama/latest/dev/gettingstarted-deploy.html
Related Github Repositories
Developer Guide - https://github.com/awsdocs/aws-panorama-developer-guide
Sample Applications - https://github.com/aws-samples/aws-panorama-samples
Panorama Docs
https://docs.aws.amazon.com/panorama/
Application creation flow example
This is an example of a sample app which has two node packages. people_counter package has core logic for counting the number of people, call_node has the model which people_counter package uses. We will also add an abstract camera to the application which can be linked to a real camera from the console while deploying the application.
Application Structure
At this point, the application structure looks as follows.
graph.jsonundergraphsdirectory lists down all the packages, nodes and edges in this application. Nodes and Edges are the way to define an application graph in Panorama.package.jsonin each package has details about the package and the assets it uses. Interface definitions for the package need to be defined in this as well. Model packagecall-nodehas adescriptor.jsonwhich needs to have the metadata required for compiling the model. More about this in the models section. Inpeople_counterpackage which is the default i.e container type, all the implementation related files go into thesrcdirectory anddescriptor.jsonhas details about which command and file to use when the container is launched. More about container package management later.assetsdirectory is where all the assets reside. Developer is not expected to make any changes in this directory.Setting up Cameras for Panorama
Panorama has a concept of Abstract Camera Package which the developers can use while developing their apps. This abstract camera package can be overriden and linked to an actual camera in the developer’s Panorama account while deploying.
Let’s add an abstract camera to this application by running the following command.
This command defines an abstract camera package in the
packagessection and adds the following snippet in thenodessection of thegraph.json. You can modify the title and description to be more relevant to the use case.rtsp_v1_interfaceis the defined interface for an abstract camera and it has an output port calledvideo_outwhich can be used to forward the camera output to another node. More details about connecting this camera are discussed in the app graph section belowPreparing a model for Panorama
Raw models are compiled using Sagemaker Neo on Panorama Cloud before being deployed onto the device. All models for this reason are paired with a
descriptor.jsonwhich has the required meta deta for compiling the raw model on the cloud.Details about using Sagemaker Neo to compile models can be found at https://docs.aws.amazon.com/sagemaker/latest/dg/neo-job-compilation.html
All the model info that is used to compile models on Sagemaker Neo are part of the
descriptor.json. Values used in this example are specific to the squeezenet model that is being used in this example.Since call_node has the model in this example, edit
packages/accountXYZ-call-node-1.0/descriptor.jsonand add the following snippet into it.Now we can add the model by passing in the path to the descriptor file which we just updated.
If you want to download the model from S3 and then add it pass
--model-s3-urias shown below. Otherwise just use--model-local-pathto pass the local model path instead.--packages-pathcan be used to pass all the packges where this model is being used and after downloading the model, DX CLI automatically adds the downloaded model into assets section of all the specified packages.If you make any updates to your model or
desriptor.jsonfile after running this command, just re-run the command with the same--model-asset-nameand the old asset will be updated with the new assets.Writing code and building a container
people_counter package has the core logic to count the number of people, so let’s create a file called
people_counter_main.pyatpackages/accountXYZ-people_counter-1.0/srcand add the relevant code to that. Editpackages/accountXYZ-people_counter-1.0/descriptor.jsonto have the following contentdescriptor.json basically provides the path for the command that needs to run and the path to the file that needs to be executed once the container starts.
We can now build the package using the following command to create a container asset.
If you make any updates to your code or
desriptor.jsonorDockerfilefile after building a container, just re-run the command with the same--container-asset-nameand the old assets will be updated with the new assets.Defining interfaces and app graph
Let’s take a look at how the
package.jsonlooks for people_counter package after running build-containerA new asset named
people_counter_container_binaryhas been added under assets and a new interface namedpeople_counter_container_binary_interfacehas been defined. In Panorama, interfaces are a way to programtically interact with a package and each interface is linked to an asset. For example,people_counter_container_binary_interfacehas an asset field which points topeople_counter_container_binary. That means that we are defining an interface to that asset. In this case, since our asset is a container with your code in it, all the inputs your code expects can be part of the inputs under interfaces. In this example, the code just expects one input which is a video stream. If output of your code needs to be consumed by another asset, that can be part of the ouputs. Similarly, a new interface was added to the call-node package when we canadd-raw-modelcommand. In that case, interface was linked to the model asset which we added using that command.At this point,
graph.jsonunder the graphs directory looks like thispackagessection here has all the packages that are part of this application and we can see thatnodessection has some nodes defined already. To be able to use any package, we need to define a corresponding nodes in thegraph.jsonfor all the interfaces that are part of the package.people_counter_container_binary_nodenode is linked topeople_counter_container_binary_interfaceinterface from people_counter package which we just looked at and similarlycallable_squeezenetnode is linked tocallable_squeezenet_interfaceinterface from the call_node package. We already discussed thefront_door_cameranode in setting up cameras sectionNext thing we will do is set up the edges for the application graph.
people_counter_container_binary_interfacehad one inputvideo_inas part of the interface definition and that was the video input to the code in that package. We can connect that input to the camera node’s output by adding the following edge under theedgessection.Registering and Uploading all local packages in the Cloud
When the application is ready, use the following command to upload all the packages to the cloud
Deploying the application
After packaging the application, you can now use the graph.json from the package to start a deployment from the cloud!
Panorama Application Concepts
Container Package Basics
Handling implementation related files
This is a directory tree of how an example container package. All the implementation related files for this package go into the
srcdirectory. In this package,people_counter_main.pyhas the logic for processing the frames from the camera andpeople_counter_main.pydepends onblueprint.csvandrequirements.jsonfor some of its functionality and therefore those are under thesrcdirectory as well. If the application requires multiple.pyfiles then all those will be under thesrcdirectory as well.Let’s now take a look at the Dockerfile provided as part of the package
In the second line, we are basically copying all the contents of the
srcdirectory into the/panoramadirectory of the Docker image. Therefore, its important to note that whenpeople_counter_main.pyaccesses other files which were originally part of thesrcdirectory, they are actually under/panoramawhen the application is running on the Panorama Appliance.Handling dynamic data
Since all the containers run in read-only mode on the Panorama Appliance, its not possible to create new files at all paths. To facilitate this, Panorama base image(i.e first line in Dockerfile) has two directories
/opt/aws/panorama/logsand/opt/aws/panorama/storagewhich are empty. During runtime, these directories are mounted to the device file system and allow the developer to store new files and data dynamically./opt/aws/panorama/logsis the location to store all the logs and all files created in the directory are uploaded to CloudWatch for the account which was used to provision the device./opt/aws/panorama/storageis a good location to store all the dynamic info that the application might need. When the device re-starts, all the memory locations are deleted but the data under these two directories is persistent and therefore should contain all the context for the application to function from where it left off on a reboot.Installing dependencies
The image(
public.ecr.aws/panorama/panorama-application) provided by Panorama is a ARM64v8 Ubuntu image with just Panorama base software installed so all the additional dependencies for the application must be installed separately. For example, add the following line to the Dockerfile to install OpenCV and boto3. This Dockerfile fetches the latestpanorama-applicationimage by default, to use a specific version of the image, refer to https://gallery.ecr.aws/panorama/panorama-application and tag the image in the Dockerfile with the version you’re planning to use.Overriding Abstract Cameras
We added an Abstract Camera in setting up cameras section. If you’re deploying an app through Panorama console, you will be automatically promted to replace the abstract camera node with a data source in your account. This section mentions how to create an
override.jsonwhich can be used to replace abstract camera with a real camera while deploying applications from command line.Create a Camera node using the following command.
--output-package-nameis the name of the camera package and--node-namespecifies the node name under this package. We are keeping the names for both of these asmy_rtsp_camerato keep it simple. Update--template-parameterswith the correct Username, Password and StreamUrl.This command will return an output like
Let’s make sure camera was created successfully by running the following command using the job id from above.
Since the status says succeeded, we are now ready to use this camera. Now, let’s create an
override.jsonfor this camera. Here we are replacingfront_door_cameranode which we created in setting up cameras section with the newly createdmy_rtsp_cameraProcessing streams from multiple cameras together
If you want to process streams from two or more cameras together, you can also replace the
front_door_cameranode with multiple cameras. For processing two streams together for example, create a second camera using the steps mentioned above and use the following override file.Using multiple cameras
At the end of last section, we looked at how to modify the override file we want to process streams from multiple cameras together. This section speaks about using multiple camera streams in the same application and processing them separately. This is useful when multiple cameras are being used for different purposes.
Let’s take the
package.jsonof people_counter package from the defining interfaces section and modify it to have two video inputs.Let’s add another camera to the application by using the following command.
A node named
back_door_camerawill be added into thenodessection ofgraph.jsonand let us connect both the cameras to the video inputs defined above in theedgessection as shown below.Viewing output on a HDMI
In this people counter example application, if we also want to draw bounding boxes around people and view those processed frames on a screen, we can do that as well by adding a Data Sink node. Data Sink node forwards the input it receives to the HDMI port.
Like the abstract camera package, Panorama also provides a data sink package and we can create a data_sink using the following command.
This command adds the following node in the
nodessection ofgraph.jsonWe can now connect this
data_sink_nodeto the output ofpeople_counter_container_binary_nodein theedgessection. At this point,graph.jsonlooks as follows.