AWS HealthImaging compresses image frames losslessly using High Throughput J2K (HTJ2K). HTJ2K enables you to decode and display a lossy, smaller resolutioned image by reading a portion of the original image frame. This gives you the ability to store only one copy of your imaging data. For example, thumnbnails can be generated dynamically by reading the first several kilobytes of the full image frame. Another example is to display lower resolutioned frame data in a stack for faster time to first image and time to first cine, then progressively increasing display resolution by reading more of the image frame.
Discrete segements in an HTJ2K-compressed image frame are bounded by tile level markers (TLM). The number of discrete levels, or decomposition levels, vary between frame to frame. After reading level 0, it is not necessary to reread from the beginning of the stream the next level. Rather, you can start reading from where level 0 ended, up to the desired level (or the end of the frame). Then you can append the newly-read data on top of the level 0 data.
This proxy allows you to retrieve image frames from Amazon Health Lake Imaging while specifying a start and end tile level marker. Processing and caching is done server-side and the client receives the requested TLM-bounded image data.
The deployment includes a containerized proxy running on AWS Fargate, a serverless container engine. By default, two containers, each running 2 vCPU and 4GB of memory are deployed behind an Application Load Balancer (ALB) with an HTTPS endpoint. Authentication is provided by Amazon Cognito via a JSON web token (JWT).
AWS Cloud9 has the requirement packages already installed and can be used to deploy this project from the cloud.
The following requirements for this solution can be found in config.ts.
ACM_ARN
: this is the Amazon Resource Name (ARN) of an AWS Certificate Manager SSL certificate in your account. This is required to terminate SSL for Application Load Balancer. Make sure this is in the same region as where you're deploying this solution.COGNITO_USER_POOL_ID
: this is the Cognito user pool ID that is used to verify the JWT at the container level. See the AuthN/AuthZ section for more information. This can also be overriden by an environment variable.
- Check out the project.
- Change current directory to project directory:
cd tile-level-marker-proxy
. - If deploying for the first time, run
cdk bootstrap
to bootstrap the environment. - Run
npm install
to install dependencies. - Review the deployment requirements and update parameters in config.ts.
- Run
cdk deploy
from the project root to deploy directly to your AWS account. This may take a few minutes. - The deployment process will output the public URL of the Application Load Balancer (ALB). See the use section for directions on consuming this URL.
- If using SSL/HTTPS for the ALB, create a DNS CNAME record that points from the domain name of the
ACM_ARN
to the ALB URL.
The TLM proxy can be called at the URL generated by the CDK deployment. The proxy behaves the same way as the GetImageFrame call:
POST /datastore/<datastoreId>/imageSet/<imageSetId>/getImageFrame
Content-type: application/json
{
"imageFrameInformation": {
"imageFrameId": "<imageFrameId>"
}
}
To retrieve the TLM level, specify the startLevel
and endLevel
query parameters.
- If neither are specified, the full image frame is returned.
- If
startLevel
is not specified, it is assumed to be 0. - If
endLevel
is not specified, it is assumed to be the end of the file.
If requesting TLM level 0 (first level) or the entire frame, the entire frame, along with the TLM levels, will be cached.
For example, to retrieve the first level of an image frame,
POST /datastore/<datastoreId>/imageSet/<imageSetId>/getImageFrame?startLevel=0&endLevel=0
Content-type: application/json
{
"imageFrameInformation": {
"imageFrameId": "<imageFrameId>"
}
}
Note that you do not have to reread this level to continue loading the image to its full resolution. You can continue to request startLevel=1&endLevel=1
, then append that to the level 0 data, to get the level 1 frame data. Or request startLevel=1
, then append that to the level 0 data for the full image frame.
By default, the container in this application expects an Amazon Cognito JSON Web Token (JWT) to be passed in along with the request. The token should be passed in the header or as a query parameter using the key token
.
This auth flow uses the aws-jwt-verify library. The library can optionally be used with any OIDC-compatible IDP that signs JWTs with RS256, RS384, or RS512.
To disable security (not recommended), modify config.ts and set AUTH_MODE
to 'null'
. Note this will allow anyone access to your image frames.
The container uses AWS SDK v2. The service model JSON for AWS HealthImaging is imported and defined in medical-imaging.js. After the service is generally available, it is recommended to use the official SDK.
During the beta, the service region is hard-coded to us-east-1.
The following environment variables can be set on the container:
Variable | Use | Default |
---|---|---|
DEPLOY_REGION | CDK deploy region | us-east-1 |
AUTH_MODE | Auth mode for client verification | cognito_jwt |
COGNITO_USER_POOL_ID | Amazon Cognito user pool ID for JWT verification | Defined in config.ts |
COGNITO_CLIENT_ID | Amazon Cognito client ID for JWT verification | null |
ENABLE_LOCAL_NODECACHE | Enable local NodeJS cache | true |
ENABLE_AMAZON_ELASTICACHE | Enable Amazon Elasticache | true |
MEMCACHED_ADDRESS | Address of remote memcached instance | Set by CDK |
Yes. The container code is located in the tlm-proxy-container subdirectory. For AuthZ and AuthN against AWS and the medical-imaging service, refer to the AWS Identity and Access Management documentation.
Can I run the container code on AWS Lambda?
AWS Lambda has a 6MB syncrhonous request and response payload limit. In cases where the TLM level returns larger than this limit, you will get an HTTP 413: Body Size is Too Large error. Additional work is needed to work around this (pagination, for example).
Can I use Amazon CloudFront with this solution?
Yes, a CloudFront distribution can be created in front of the Application Load Balancer. Use the AWS-managed prefix list to configure the ALB security group to allow incoming connections from CloudFront.
This workload would be better served using Amazon CloudFront directly with Lambda@Edge. This will be provided in an upcoming project in this repository.
Yes. The ECS task role function uses an execution policy that allows it access to get all image frames. You can customize the IAM policy in the CDK stack by applying access restrictions to the medical-imaging:GetImageFrame
action for the ecsTaskRole
role.
Yes. ARM64 can furthur reduce ECS Fargate costs. By default, CDK uses the current machine architecture to build the container. This sample project has been coded to use x86-64/AMD64 for compatibility reasons. To update the CDK project to use ARM64, update tile-level-marker-proxy-stack.ts and change the DockerImageAsset
construct to use platform: Platform.LINUX_ARM64
and the FargateTaskDefinition
construct to use cpuArchitecture: ecs.CpuArchitecture.ARM64
.
To remove this app, run the following in the project directory:
cdk destroy --all