Skip to content

Commit 289af27

Browse files
authored
JavaScript (v3): HealthImaging - Image sets and data frames workflow. (#6345)
1 parent 2df4305 commit 289af27

30 files changed

+2388
-11
lines changed

.doc_gen/metadata/medical-imaging_metadata.yaml

+16
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,22 @@ medical-imaging_Scenario_ImageSetsAndFrames:
11691169
snippet_tags:
11701170
- cpp.example_code.medical-imaging.image-sets-workflow.clean_up
11711171
- cpp.example_code.medical-imaging.image-sets-workflow.empty_data_store
1172+
JavaScript:
1173+
versions:
1174+
- sdk_version: 3
1175+
github: javascriptv3/example_code/medical-imaging
1176+
github_note_at_bottom: true
1177+
excerpts:
1178+
- description:
1179+
snippet_files:
1180+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/index.js
1181+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-1.js
1182+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-2.js
1183+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-3.js
1184+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-4.js
1185+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-5.js
1186+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-6.js
1187+
- javascriptv3/example_code/medical-imaging/scenarios/health-image-sets/step-7.js
11721188
services:
11731189
medical-imaging:
11741190
{

javascriptv3/.eslintignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
/example_code/reactnative/
1+
/example_code/reactnative/
2+
/example_code/medical-imaging/scenarios/health-image-sets/pixel-data-verification

javascriptv3/example_code/libs/scenario/scenario.js

+17-8
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ export class Step {
2222
console.log(
2323
`[DEBUG ${new Date().toISOString()}] Handling step: ${
2424
this.constructor.name
25-
}<${this.name}>`,
25+
}<${this.name}>`
2626
);
2727
console.log(
28-
`[DEBUG ${new Date().toISOString()}] State: ${JSON.stringify(state)}`,
28+
`[DEBUG ${new Date().toISOString()}] State: ${JSON.stringify(state)}`
2929
);
3030
}
3131
}
@@ -126,7 +126,7 @@ export class ScenarioInput extends Step {
126126
});
127127
} else {
128128
throw new Error(
129-
`Error handling ScenarioInput, ${this.options?.type} is not supported.`,
129+
`Error handling ScenarioInput, ${this.options?.type} is not supported.`
130130
);
131131
}
132132

@@ -137,7 +137,7 @@ export class ScenarioInput extends Step {
137137
export class ScenarioAction extends Step {
138138
/**
139139
* @param {string} name
140-
* @param {(state: Record<string, any>) => Promise<void>} action
140+
* @param {(state: Record<string, any>, options) => Promise<void>} action
141141
* @param {{ whileConfig: { inputEquals: any, input: ScenarioInput, output: ScenarioOutput }}} [options]
142142
*/
143143
constructor(name, action, options) {
@@ -153,7 +153,7 @@ export class ScenarioAction extends Step {
153153
async handle(state, options) {
154154
const _handle = async () => {
155155
super.handle(state, options);
156-
await this.action(state);
156+
await this.action(state, options);
157157
};
158158

159159
if (!options?.confirmAll && this.options?.whileConfig) {
@@ -180,14 +180,19 @@ export class Scenario {
180180
*/
181181
state = {};
182182

183+
/**
184+
* @type {(ScenarioOutput | ScenarioInput | ScenarioAction | Scenario)[]}
185+
*/
186+
steps = [];
187+
183188
/**
184189
* @param {string} name
185-
* @param {(ScenarioOutput | ScenarioInput | ScenarioAction)[]} steps
190+
* @param {(ScenarioOutput | ScenarioInput | ScenarioAction | null)[]} steps
186191
* @param {Record<string, any>} initialState
187192
*/
188193
constructor(name, steps = [], initialState = {}) {
189194
this.name = name;
190-
this.steps = steps;
195+
this.steps = steps.filter((s) => !!s);
191196
this.state = { ...initialState, name };
192197
}
193198

@@ -196,7 +201,11 @@ export class Scenario {
196201
*/
197202
async run(runConfig) {
198203
for (const step of this.steps) {
199-
await step.handle(this.state, runConfig);
204+
if (step instanceof Scenario) {
205+
await step.run(runConfig);
206+
} else {
207+
await step.handle(this.state, runConfig);
208+
}
200209
}
201210
}
202211
}

javascriptv3/example_code/medical-imaging/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Code excerpts that show you how to call individual service functions.
6262
Code examples that show you how to accomplish a specific task by calling multiple
6363
functions within the same service.
6464

65+
- [Get started with image sets and image frames](scenarios/health-image-sets/index.js)
6566
- [Tagging a data store](scenarios/tagging-datastores.js)
6667
- [Tagging an image set](scenarios/tagging-imagesets.js)
6768

@@ -100,6 +101,27 @@ node ./hello.js
100101
```
101102

102103

104+
#### Get started with image sets and image frames
105+
106+
This example shows you how to import DICOM files and download image frames in HealthImaging.</para>
107+
<para>The implementation is structured as a workflow command-line
108+
application.
109+
110+
111+
- Set up resources for a DICOM import.
112+
- Import DICOM files into a data store.
113+
- Retrieve the image set IDs for the import job.
114+
- Retrieve the image frame IDs for the image sets.
115+
- Download, decode and verify the image frames.
116+
- Clean up resources.
117+
118+
<!--custom.scenario_prereqs.medical-imaging_Scenario_ImageSetsAndFrames.start-->
119+
<!--custom.scenario_prereqs.medical-imaging_Scenario_ImageSetsAndFrames.end-->
120+
121+
122+
<!--custom.scenarios.medical-imaging_Scenario_ImageSetsAndFrames.start-->
123+
<!--custom.scenarios.medical-imaging_Scenario_ImageSetsAndFrames.end-->
124+
103125
#### Tagging a data store
104126

105127
This example shows you how to tag a HealthImaging data store.

javascriptv3/example_code/medical-imaging/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
"license": "Apache-2.0",
66
"dependencies": {
77
"@aws-doc-sdk-examples/lib": "^1.0.0",
8-
"@aws-sdk/client-medical-imaging": "^3.391.0"
8+
"@aws-sdk/client-cloudformation": "3.540.0",
9+
"@aws-sdk/client-medical-imaging": "^3.391.0",
10+
"@aws-sdk/client-sts": "^3.540.0"
911
},
1012
"scripts": {
13+
"test": "vitest run **/*.unit.test.js",
1114
"integration-test": "vitest run **/*.integration.test.js"
1215
},
1316
"type": "module",
1417
"devDependencies": {
15-
"vitest": "^0.25.2"
18+
"vitest": "^1.5.0"
1619
}
1720
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*state.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Import HealthImaging Image Sets and Download Image Frames using the AWS SDK for JavaScript
2+
3+
## Overview
4+
5+
This workflow shows how to use the AWS SDK for JavaScript to import DICOM files into an AWS HealthImaging data store. It then shows how to download, decode, and verify the image frames created by the DICOM import.
6+
7+
Digital Imaging and Communications in Medicine (DICOM) is a technical standard for the digital storage and transmission of medical images and related information.
8+
9+
This workflow runs as a command-line application prompting for user input.
10+
11+
1. All the necessary resources are created from an AWS CloudFormation template.
12+
1. A HealthImaging data store.
13+
2. An Amazon Simple Storage Service (Amazon S3) input bucket for a DICOM import job.
14+
3. An Amazon S3 output bucket for a DICOM import job.
15+
4. An AWS Identity and Access Management (IAM) role with the appropriate permissions for a DICOM import job.
16+
17+
![CloudFormation stack diagram](../../../../../workflows/healthimaging_image_sets/.images/cfn_stack.png)
18+
19+
2. The user chooses a DICOM study to copy from the [National Cancer Institute Imaging Data Commons (IDC) Collections](https://registry.opendata.aws/nci-imaging-data-commons/)' public S3 bucket.
20+
21+
3. The chosen study is copied to the user's input S3 bucket.
22+
23+
![DICOM copy diagram](../../../../../workflows/healthimaging_image_sets/.images/copy_dicom.png)
24+
25+
4. A HealthImaging DICOM import job is run.
26+
27+
![DICOM import diagram](../../../../../workflows/healthimaging_image_sets/.images/dicom_import.png)
28+
29+
5. The workflow retrieves the IDs for the HealthImaging image frames created by the DICOM import job.
30+
31+
![Image frame ID retrieval diagram](../../../../../workflows/healthimaging_image_sets/.images/get_image_frame_ids.png)
32+
33+
6. The HealthImaging image frames are downloaded, decoded to a bitmap format, and verified using a CRC32 checksum.
34+
35+
7. The created resources can then be deleted, if the user chooses.
36+
37+
## ⚠ Important
38+
39+
- Running this code might result in charges to your AWS account.
40+
- Running the tests might result in charges to your AWS account.
41+
- We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
42+
- This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
43+
44+
## Scenario
45+
46+
### Prerequisites
47+
48+
For information on prerequisites for running this scenario, see the main [JavaScript V3 README.md](../../../../README.md).
49+
50+
### Run the Scenario
51+
52+
To run the scenario, follow these steps:
53+
54+
1. Clone the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples) to your local environment.
55+
2. Navigate to the `javascriptv3/example_code/medical-imaging/scenarios/health-image-sets` directory.
56+
3. Run the scenario by running `node index.js --scenario <deploy | demo | destroy> [-h|--help] [-y|--yes] [-v|--verbose]`
57+
58+
### Deploy
59+
This step will prompt you for a name for the CloudFormation stack and the datastore. It will then deploy the stack.
60+
61+
### Demo
62+
This step copies images from the public bucket, imports them into your datastore, and runs a checksum validation to ensure data integrity.
63+
64+
### Destroy
65+
This step deletes the image sets from the datastore and deletes the CloudFormation stack. It does not delete saved state that is output from the intermediary steps. Those are left behind for reference. You can delete them by running `rm *state.json` in this directory.
66+
67+
## Additional Resources
68+
69+
- [HealthImaging User Guide](https://docs.aws.amazon.com/healthimaging/latest/devguide/what-is.html)
70+
- [HealthImaging API Reference](https://docs.aws.amazon.com/healthimaging/latest/APIReference/Welcome.html)
71+
- [AWS SDK for JavaScript Documentation](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/index.html)
72+
73+
---
74+
75+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
76+
77+
SPDX-License-Identifier: Apache-2.0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import {
5+
parseScenarioArgs,
6+
Scenario,
7+
} from "@aws-doc-sdk-examples/lib/scenario/index.js";
8+
9+
import { step1 } from "./step-1.js";
10+
import { step2 } from "./step-2.js";
11+
import { step3 } from "./step-3.js";
12+
import { step4 } from "./step-4.js";
13+
import { step5 } from "./step-5.js";
14+
import { step6 } from "./step-6.js";
15+
import { step7 } from "./step-7.js";
16+
17+
const context = {};
18+
19+
const scenarios = {
20+
deploy: new Scenario("Deploy Resources", [step1], context),
21+
demo: new Scenario("Run Demo", [step2, step3, step4, step5, step6], context),
22+
destroy: new Scenario("Clean Up Resources", [step7], context),
23+
};
24+
25+
// Call function if run directly
26+
import { fileURLToPath } from "url";
27+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
28+
parseScenarioArgs(scenarios);
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# AWS HealthImaging Pixel Data Verification
2+
3+
This example demonstrates how to use the AWS HealthImaging (AHI) Pixel Data Verification feature to ensure the image you
4+
decoded matches the original DICOM P10 Image.
5+
6+
## Dependencies
7+
8+
This example is written in JavaScript and uses NodeJS. It depends on the following libraries:
9+
10+
- [AWS SDK for JavaScript v3](https://github.com/aws/aws-sdk-js-v3)
11+
- [CRC32 Algorithm](https://www.npmjs.com/package/crc-32)
12+
- [HTJ2K Decoding](https://github.com/chafey/openjphjs)
13+
14+
## Deployment
15+
16+
1. Check out the project.
17+
2. Change the current directory to this project: `cd pixel-data-verification`
18+
3. Install dependencies: `npm install`
19+
20+
## How to use
21+
22+
1. Create a datastore as per the AHLI developer guide.
23+
2. Import the DICOM file `test/fixtures/CT1_UNC` as per the AHLI developer guide.
24+
3. Retrieve the ImageSetId from the job output manifest file in S3.
25+
4. Run this tool passing in your datastore (from step 1), ImageSetId (from step 3), and series and SOP instance UIDs listed below.
26+
27+
```
28+
$ node index.js $DATASTOREID $IMAGESETID 1.3.6.1.4.1.5962.1.3.1.1.20040826185059.5457 1.3.6.1.4.1.5962.1.1.1.1.1.20040826185059.5457
29+
CRC32 match!
30+
```

0 commit comments

Comments
 (0)