aws-samples / aws-healthimaging-samples

Sample projects on working with AWS HealthImaging, an AWS service that allows you to store, analyze, and share medical images in the cloud at petabyte scale.
https://aws.amazon.com/healthimaging
MIT No Attribution
50 stars 94 forks source link

How to specify query parameters for getImageFrame #62

Closed terry-wilson closed 5 months ago

terry-wilson commented 6 months ago

I am interested in trying out the tile-level-marker-proxy sample, however, it is not clear to me how to add the necessary query parameters (startLevel, endLevel) to the getImageFrame method. I am using the official AWS SDK V3 (it takes care of the authentication for me) but don't really see any obvious way to add these parameters as the standard method does not define any in the SDK (GetImageFrameCommand) ... any suggestions on how to do this?

yyao84 commented 6 months ago

Hi @terry-wilson,

The idea behind retrieving discrete lower-resolutioned images from HealthImaging using HTJ2K is:

You can read from levels 0 - n to return a sub or full frame. If you've read a sub-frame and want to continue rendering the full resolutioned image, you can read levels n - m, then append it to 0 - n. This results in an object of 0 - m size, and you can decode that for a higher resolutioned image. This way you don't have to keep reading from the start of the frame to render the image at higher resolutions. As a side note, if you don't need discrete image resolutions, you can simply decode and display the frame as you're reading it from a stream.

The TLM sample project sets up a fastify environment and accepts a startLevel, endLevel parameter,

Please let us know if you have any further questions. Happy to set up a call too, if you'd like.

terry-wilson commented 6 months ago

Hi @yyao84, thanks for the detailed explanation, that all makes sense ... however, I still have my original question of HOW to pass the startLevel & endLevel query parameters on the getImageFrame API using the official AWS SDK V3 for javascript?

I also have an additional question on how the authentication works between the proxy and the corresponding HealthImaging data store? When I looked at the code in the proxy, I can see the call in tlm-proxy.js where the GetImageFrameCommand is sent, but I don't see any sort of authentication set up in the imagingClient ... do you somehow forward any authentication token from the incoming request?

At this point, I am just doing some testing with anonymized data, so I would be fine using no authentication at all, but I thought the back-end AWS data store required it.

yyao84 commented 6 months ago

I'll answer this in reverse since I think the authentication question helps to answer the query parameter question -

Authentication to AWS APIs uses AWS Signature v4, which the SDK takes care of for us. There's a number of ways to configure credentials for the SDK.

In the case of the TLM proxy, since it's running on an ECS cluster, the SDK picks up credentials from the ECS task role, which is an IAM role for specifically for this task. This uses the SDK container provider behind the scenes, and transparent to us. Processes running in this container can medical-imaging:GetImageFrame but nothing else.

The TLM proxy itself is exposed publically using an Application Load Balancer (ALB), which can be accessed using an HTTPS endpoint. This requires an Amazon Certificate Manager certificate, which is free if you already have a domain to use. The ALB has a public DNS name that you'll use to create a CNAME record, e.g. tlm.proxy.your.domain <> tlm-alb-123-abc.us-east-1.elb.amazonaws.com. This is where you'd authenticate with the JWT from Cognito or leave it open for testing, but keep in mind this request is signed and passed in as an authenticated to request to HealthImaging in the container.

Since the TLM proxy is not an AWS API, we're not able to use the official AWS SDK with it. You would call the endpoint using the same URL and body as the HealthImaging GetImageFrame request, but with the authentication that the TLM Proxy expects and the query parameters appended. This is implemented in the Sample Viewer's tlmLoader, where it loads the frames one level at a time:

This is just an example implementation where we request one TLM level at a time and put them together client-side. But you can do other things like startLevel=0, endLevel=3, etc.

Hope this helps!

terry-wilson commented 6 months ago

Thx, that helps to explain things and gives me the info I needed.

I do have a domain and created a certificate with AWS Certificate Mgr and configured the corresponding ARN in config.ts ... I also set AUTH_MODE to 'null'. I ran "cdk deploy" without issue, however, "cdk deploy" seems to mostly finish before stalling and eventually timing out after 3 hours and then rolling back (see screenshot). I have tried running the deploy command twice and both times had the exact same problem ... any suggestions for how to determine what the issue with the deployment is?

DeployError

yyao84 commented 6 months ago

I've seen this occur when the ECS service doesn't enter into a consistent state for whatever reason (container doesn't start, health check fails, etc.). You can typically see the reason from ECS logging:

To check the service logs:

To check the individual task logs:

In either case, there should be logs on why the container is failing to start.

I'll attempt to reproduce this on my side as well.

terry-wilson commented 6 months ago

I ran deploy again and I see this in the logs for the service ... AHI_REGION is set to 'us-east-1' in config.ts so not sure what exactly is the issue ... the only changes I made to config.ts was to add the ARN for the cert and set AUTH_MODE to 'null':

ServiceError

terry-wilson commented 6 months ago

@yyao84 I was looking at the code that was failing in tlm-proxy.js and it didn't seem correct to me so I changed it as follows (commented out code is the original):

//if (process.env.AHI_ENDPOINT) imagingClientConfig.endpoint = AHI_ENDPOINT;
if (process.env.AHI_ENDPOINT) imagingClientConfig.endpoint = process.env.AHI_ENDPOINT;
if (process.env.AHI_REGION) {
    // imagingClientConfig.endpoint = AHI_REGION;
    imagingClientConfig.region = process.env.AHI_REGION;
} else {
    imagingClientConfig.region = 'us-east-1';
}

Re-running the deployment with these changes works this time. I haven't tried actually using the system yet, but please let me know if these code changes are not appropriate for whatever reason.

yyao84 commented 6 months ago

Thanks @terry-wilson for finding that bug in the code. Feel free to submit a PR, otherwise I'll update it later today