diff --git a/cpp/Dockerfile b/cpp/Dockerfile index b8145cdc19c..210a091067f 100644 --- a/cpp/Dockerfile +++ b/cpp/Dockerfile @@ -1,7 +1,7 @@ -# To build the docker image, run the following command from the shell. This command must be run in -# the "aws-doc-sdk-examples" directory, the parent directory of "cpp", in order to access the resources folder. +# To build the docker image, run the following command from the shell. This Dockerfile +# is configured to be run from with the cpp directory of aws-doc-sdk-examples. # -# 'docker build -f cpp/Dockerfile -t .' +# 'docker build -t .' # # The following command will run the docker image, copying your AWS credentials. # 'docker run -it --volume ~/.aws/credentials:/home/tests/.aws/credentials ' @@ -15,7 +15,7 @@ RUN \ # Build only the services needed for example code. ENV SERVICES="acm;autoscaling;cloudtrail;codebuild;codecommit;cognito-idp;dynamodb;ec2;elasticache;elasticbeanstalk" -ENV SERVICES=${SERVICES}";elasticfilesystem;email;events;glacier;glue;guardduty;iam;kinesis;lambda;logs;mediaconvert;monitoring" +ENV SERVICES=${SERVICES}";elasticfilesystem;email;events;glacier;glue;guardduty;iam;kinesis;lambda;logs;mediaconvert;medical-imaging;monitoring" ENV SERVICES=${SERVICES}";monitoring;neptune;rds;rds-data;redshift;rekognition;s3;s3-crt;s3-encryption;secretsmanager;sesv2;sns;sqs" ENV SERVICES=${SERVICES}";storagegateway;sts;transfer;transcribe;transcribestreaming" @@ -58,4 +58,4 @@ RUN useradd -ms /bin/bash tests && \ USER tests -CMD ["python3", "/src/cpp/run_automated_tests.py "] +CMD ["python3", "/src/cpp/run_automated_tests.py", "-23"] diff --git a/cpp/example_code/medical-imaging/imaging_set_and_frames_workflow/medical_image_sets_and_frames_workflow.cpp b/cpp/example_code/medical-imaging/imaging_set_and_frames_workflow/medical_image_sets_and_frames_workflow.cpp index 7164dbd09e7..7b2faae7ab2 100644 --- a/cpp/example_code/medical-imaging/imaging_set_and_frames_workflow/medical_image_sets_and_frames_workflow.cpp +++ b/cpp/example_code/medical-imaging/imaging_set_and_frames_workflow/medical_image_sets_and_frames_workflow.cpp @@ -921,7 +921,13 @@ bool AwsDoc::Medical_Imaging::downloadDecodeAndCheckImageFrames( Aws::MedicalImaging::Model::GetImageFrameOutcome outcome, const std::shared_ptr &context) { - result = handleGetImageFrameResult(outcome, outDirectory, imageFrame); + if (!handleGetImageFrameResult(outcome, outDirectory, imageFrame)) + { + std::cerr << "Failed to download and convert image frame: " + << imageFrame.mImageFrameId << " from image set: " + << imageFrame.mImageSetId << std::endl; + result = false; + } count--; if (count <= 0) { diff --git a/cpp/example_code/medical-imaging/start_dicom_import_job.cpp b/cpp/example_code/medical-imaging/start_dicom_import_job.cpp index 631fb8374a6..38b7569a70a 100644 --- a/cpp/example_code/medical-imaging/start_dicom_import_job.cpp +++ b/cpp/example_code/medical-imaging/start_dicom_import_job.cpp @@ -34,7 +34,6 @@ \param clientConfig: Aws client configuration. \return bool: Function succeeded. */ - bool AwsDoc::Medical_Imaging::startDICOMImportJob( const Aws::String &dataStoreID, const Aws::String &inputBucketName, const Aws::String &inputDirectory, const Aws::String &outputBucketName, diff --git a/cpp/example_code/medical-imaging/tests/CMakeLists.txt b/cpp/example_code/medical-imaging/tests/CMakeLists.txt index 7d944e0426b..c74d8f75815 100644 --- a/cpp/example_code/medical-imaging/tests/CMakeLists.txt +++ b/cpp/example_code/medical-imaging/tests/CMakeLists.txt @@ -6,10 +6,10 @@ cmake_minimum_required(VERSION 3.14) set(EXAMPLE_SERVICE_NAME "medical-imaging") set(CURRENT_TARGET "${EXAMPLE_SERVICE_NAME}_gtest") -set(CURRENT_TARGET_AWS_DEPENDENCIES medical-imaging) +set(CURRENT_TARGET_AWS_DEPENDENCIES medical-imaging) # Set this project's name. -project("${EXAMPLE_SERVICE_NAME}-examples-gtests" ) +project("${EXAMPLE_SERVICE_NAME}-examples-gtests") # Set the C++ standard to use to build this target. set(CMAKE_CXX_STANDARD 14) @@ -21,7 +21,7 @@ enable_testing() find_package(GTest) -if(NOT GTest_FOUND) +if (NOT GTest_FOUND) include(FetchContent) FetchContent_Declare( googletest @@ -32,16 +32,16 @@ if(NOT GTest_FOUND) # For Windows: Prevent overriding the parent project's compiler/linker settings. set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) -endif() +endif () # Use the MSVC variable to determine if this is a Windows build. set(WINDOWS_BUILD ${MSVC}) # Set the location for Windows to find the installed libraries of the SDK. -if(WINDOWS_BUILD) +if (WINDOWS_BUILD) string(REPLACE ";" "/aws-cpp-sdk-all;" SYSTEM_MODULE_PATH "${CMAKE_SYSTEM_PREFIX_PATH}/aws-cpp-sdk-all") list(APPEND CMAKE_PREFIX_PATH ${SYSTEM_MODULE_PATH}) -endif() +endif () # Find the AWS SDK for C++ package. find_package(AWSSDK REQUIRED COMPONENTS ${CURRENT_TARGET_AWS_DEPENDENCIES}) @@ -50,26 +50,26 @@ add_executable( ${CURRENT_TARGET} ) -if(WINDOWS_BUILD) - # set(BIN_SUB_DIR "/Debug") # If you are building from the command line, you may need to uncomment this - # and set the proper subdirectory to the executables' location. +if (WINDOWS_BUILD) + # set(BIN_SUB_DIR "/Debug") # If you are building from the command line, you may need to uncomment this + # and set the proper subdirectory to the executables' location. # Copy relevant AWS SDK for C++ libraries into the current binary directory for running and debugging. AWSSDK_CPY_DYN_LIBS( CURRENT_TARGET_AWS_DEPENDENCIES - "" - ${CMAKE_CURRENT_BINARY_DIR}${BIN_SUB_DIR} + "" + ${CMAKE_CURRENT_BINARY_DIR} ${BIN_SUB_DIR} ) add_custom_command( - TARGET - ${CURRENT_TARGET} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}${BIN_SUB_DIR}/gtest.dll - ${CMAKE_CURRENT_BINARY_DIR}${BIN_SUB_DIR} + TARGET + ${CURRENT_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR} ${BIN_SUB_DIR}/gtest.dll + ${CMAKE_CURRENT_BINARY_DIR} ${BIN_SUB_DIR} ) -endif() +endif () # GTEST_SOURCE_FILES can be defined in the command line to limit the files in a build, for example to one action. if (NOT DEFINED GTEST_SOURCE_FILES) @@ -78,16 +78,16 @@ if (NOT DEFINED GTEST_SOURCE_FILES) GTEST_SOURCE_FILES "gtest_*.cpp" ) -endif() +endif () -foreach(TEST_FILE ${GTEST_SOURCE_FILES}) +foreach (TEST_FILE ${GTEST_SOURCE_FILES}) string(REPLACE "gtest_" "../" SOURCE_FILE ${TEST_FILE}) - if (EXISTS ${SOURCE_FILE}) + if (EXISTS ${SOURCE_FILE}) list(APPEND GTEST_SOURCE ${SOURCE_FILE} ${TEST_FILE}) - else() + else () message("Error: no associated source file found for ${TEST_FILE}") - endif() -endforeach() + endif () +endforeach () target_sources( ${CURRENT_TARGET} diff --git a/cpp/example_code/medical-imaging/tests/gtest_delete_image_set.cpp b/cpp/example_code/medical-imaging/tests/gtest_delete_image_set.cpp index 7ec9af27bd0..21ce10522e1 100644 --- a/cpp/example_code/medical-imaging/tests/gtest_delete_image_set.cpp +++ b/cpp/example_code/medical-imaging/tests/gtest_delete_image_set.cpp @@ -22,9 +22,10 @@ namespace AwsDocTest { bool result = mockHttp.addResponseWithBody("mock_input/deleteImageSet.json"); ASSERT_TRUE(result) << preconditionError() << std::endl; - result = AwsDoc::Medical_Imaging::deleteImageSet("12345678901234567890123456789012", - "12345678901234567890123456789012", - *s_clientConfig); + result = AwsDoc::Medical_Imaging::deleteImageSet( + "12345678901234567890123456789012", + "12345678901234567890123456789012", + *s_clientConfig); ASSERT_TRUE(result); } } // namespace AwsDocTest diff --git a/cpp/example_code/medical-imaging/tests/gtest_get_image_frame.cpp b/cpp/example_code/medical-imaging/tests/gtest_get_image_frame.cpp index 1326ccf5281..7ab08bed988 100644 --- a/cpp/example_code/medical-imaging/tests/gtest_get_image_frame.cpp +++ b/cpp/example_code/medical-imaging/tests/gtest_get_image_frame.cpp @@ -18,22 +18,23 @@ #include namespace AwsDocTest { - // NOLINTNEXTLINE(readability-named-parameter) - TEST_F(MedicalImaging_GTests, get_image_frame_3_ ) { + // NOLINTNEXTLINE(readability-named-parameter) + TEST_F(MedicalImaging_GTests, get_image_frame_3_) { MockHTTP mockHttp; bool result = mockHttp.addResponseWithBody( "mock_input/test.jph"); ASSERT_TRUE(result) << preconditionError() << std::endl; - Aws::String outputFileName = "test_output.jph"; - result = AwsDoc::Medical_Imaging::getImageFrame("12345678901234567890123456789012", - "12345678901234567890123456789012", - "12345678901234567890123456789012", - outputFileName, - *s_clientConfig); - ASSERT_TRUE(result); + Aws::String outputFileName = "test_output.jph"; + result = AwsDoc::Medical_Imaging::getImageFrame( + "12345678901234567890123456789012", + "12345678901234567890123456789012", + "12345678901234567890123456789012", + outputFileName, + *s_clientConfig); + ASSERT_TRUE(result); std::ifstream ifs(outputFileName); ASSERT_TRUE(ifs); std::remove(outputFileName.c_str()); - } + } } // namespace AwsDocTest diff --git a/cpp/example_code/medical-imaging/tests/gtest_get_image_set_metadata.cpp b/cpp/example_code/medical-imaging/tests/gtest_get_image_set_metadata.cpp index d4d193c86aa..8f94b6a453f 100644 --- a/cpp/example_code/medical-imaging/tests/gtest_get_image_set_metadata.cpp +++ b/cpp/example_code/medical-imaging/tests/gtest_get_image_set_metadata.cpp @@ -18,22 +18,44 @@ #include namespace AwsDocTest { - // NOLINTNEXTLINE(readability-named-parameter) - TEST_F(MedicalImaging_GTests, get_image_set_metadata_3_ ) { + // NOLINTNEXTLINE(readability-named-parameter) + TEST_F(MedicalImaging_GTests, get_image_set_metadata_without_version_3_) { MockHTTP mockHttp; bool result = mockHttp.addResponseWithBody( "mock_input/deleteImageSet.json.gz"); ASSERT_TRUE(result) << preconditionError() << std::endl; Aws::String outFileName = "test.json.gzip"; - result = AwsDoc::Medical_Imaging::getImageSetMetadata("12345678901234567890123456789012", - "12345678901234567890123456789012", - outFileName, *s_clientConfig); - ASSERT_TRUE(result); - - std::ifstream ifs(outFileName); - ASSERT_TRUE(ifs); - std::remove(outFileName.c_str()); - } + result = AwsDoc::Medical_Imaging::getImageSetMetadata( + "12345678901234567890123456789012", + "12345678901234567890123456789012", + "", + outFileName, *s_clientConfig); + ASSERT_TRUE(result); + + std::ifstream ifs(outFileName); + ASSERT_TRUE(ifs); + std::remove(outFileName.c_str()); + } + + // NOLINTNEXTLINE(readability-named-parameter) + TEST_F(MedicalImaging_GTests, get_image_set_metadata_with_version_3_) { + MockHTTP mockHttp; + bool result = mockHttp.addResponseWithBody( + "mock_input/deleteImageSet.json.gz"); + ASSERT_TRUE(result) << preconditionError() << std::endl; + + Aws::String outFileName = "test.json.gzip"; + result = AwsDoc::Medical_Imaging::getImageSetMetadata( + "12345678901234567890123456789012", + "12345678901234567890123456789012", + "1", + outFileName, *s_clientConfig); + ASSERT_TRUE(result); + + std::ifstream ifs(outFileName); + ASSERT_TRUE(ifs); + std::remove(outFileName.c_str()); + } } // namespace AwsDocTest diff --git a/cpp/example_code/medical-imaging/tests/gtest_search_image_sets.cpp b/cpp/example_code/medical-imaging/tests/gtest_search_image_sets.cpp index 1433b2e7c92..a1d756b54f1 100644 --- a/cpp/example_code/medical-imaging/tests/gtest_search_image_sets.cpp +++ b/cpp/example_code/medical-imaging/tests/gtest_search_image_sets.cpp @@ -26,10 +26,11 @@ namespace AwsDocTest { const Aws::MedicalImaging::Model::SearchCriteria searchCriteria; Aws::Vector imageSetResults; - result = AwsDoc::Medical_Imaging::searchImageSets("12345678901234567890123456789012", - searchCriteria, - imageSetResults, - *s_clientConfig); + result = AwsDoc::Medical_Imaging::searchImageSets( + "12345678901234567890123456789012", + searchCriteria, + imageSetResults, + *s_clientConfig); ASSERT_TRUE(result); ASSERT_EQ(2, imageSetResults.size()); } diff --git a/cpp/example_code/medical-imaging/tests/gtest_start_dicom_import_job.cpp b/cpp/example_code/medical-imaging/tests/gtest_start_dicom_import_job.cpp index b8f1a4f89ae..f1ffd748ef5 100644 --- a/cpp/example_code/medical-imaging/tests/gtest_start_dicom_import_job.cpp +++ b/cpp/example_code/medical-imaging/tests/gtest_start_dicom_import_job.cpp @@ -31,7 +31,7 @@ namespace AwsDocTest { "destination_bucket", "destination_folder", "arn:aws:iam::123456789012:role/dicom_import", - jobID, + jobID, *s_clientConfig); ASSERT_TRUE(result); diff --git a/cpp/example_code/medical-imaging/tests/medical-imaging_gtests.cpp b/cpp/example_code/medical-imaging/tests/medical-imaging_gtests.cpp index e793e021b0b..fa72d8b9a37 100644 --- a/cpp/example_code/medical-imaging/tests/medical-imaging_gtests.cpp +++ b/cpp/example_code/medical-imaging/tests/medical-imaging_gtests.cpp @@ -20,7 +20,7 @@ void AwsDocTest::MedicalImaging_GTests::SetUpTestSuite() { } void AwsDocTest::MedicalImaging_GTests::TearDownTestSuite() { - ShutdownAPI(s_options); + ShutdownAPI(s_options); } @@ -80,6 +80,7 @@ int AwsDocTest::MyStringBuffer::underflow() { return result; } + AwsDocTest::MockHTTP::MockHTTP() { mockHttpClient = Aws::MakeShared(ALLOCATION_TAG); mockHttpClientFactory = Aws::MakeShared(ALLOCATION_TAG); diff --git a/workflows/healthimaging_image_sets/.images/get_image_frame_ids.png b/workflows/healthimaging_image_sets/.images/get_image_frame_ids.png index 906ff4df91d..93dedf05cb0 100644 Binary files a/workflows/healthimaging_image_sets/.images/get_image_frame_ids.png and b/workflows/healthimaging_image_sets/.images/get_image_frame_ids.png differ diff --git a/workflows/healthimaging_image_sets/SPECIFICATION.md b/workflows/healthimaging_image_sets/SPECIFICATION.md index e2d3547246c..86fbf35876d 100644 --- a/workflows/healthimaging_image_sets/SPECIFICATION.md +++ b/workflows/healthimaging_image_sets/SPECIFICATION.md @@ -10,7 +10,7 @@ This document explains the following: - Flow of the demo, importing DICOM files, and downloading and decoding image frames. - Destroying the AWS resources at the end of the example. -For an introduction to *HealthImaging image sets and frames*, see the [TODO README.md](README.md). +For an introduction to *HealthImaging image sets and frames*, see the [README.md](README.md). Note: HealthImaging is often referenced as MedicalImaging or medical-imaging in the APIs. @@ -30,7 +30,7 @@ Note: HealthImaging is often referenced as MedicalImaging or medical-imaging in A CloudFormation template is used to create and destroy the following resources. 1. A HealthImaging data store. -2. An S3 bucket. +2. Two S3 buckets. 3. An IAM role with permissions for a DICOM import job. The template is stored at [resources/cfn_template.yaml](resources/cfn_template.yaml). @@ -77,7 +77,7 @@ the particular DICOM files to import: 3. Select the DICOM files to import. 4. Choose whether to delete the stack. -For more detail on how this is implemented, see [TODO Demo](#demo). +For more detail on how this is implemented, see [Demo](#demo). --- @@ -195,33 +195,22 @@ The application uses GetImageSetMetadata to retrieve the metadata as gzipped JSO retrieve the image frame IDs. Other values which are or could be used for image display are also retrieved, as an aid to customers. -TODO - document jmesPATH for these values. - -- DICOM.RescaleSlope -- DICOM.RescaleIntercept -- MinPixelValue -- MaxPixelValue - -In the code, the following comments were added about transforming the image data. - -/* -* Sometimes in DICOM, the max pixel value is small enough that all pixels will appear black -* without applying a transform. -* For example, if the max pixel value is 4000 and the precision is 16, -* then the max pixel will be (4000 / 65535 = 0.06) of the max for the precision. -* -* The following code scales the image data to make it visible when saved as a jpeg image. -* This is done simply for validation purposes, so that you can see if the downloaded -* image was correctly decoded. -* -* This code is not meant to demonstrate a rigorous transform of DICOM image data for -* viewing or other purposes. -* For example, it does not use the rescale slope or the rescale slope intercept values obtained from -* the DICOM metadata. -* -*/ +- An image set can have one or more image instances. +- An image instance can have one or more image frames. +- JMESPath query for array of image instances. "Study.Series.*.Instances[].*[]" +- JMESPath query on image instance for the rescale slope. "DICOM.RescaleSlope" +- JMESPath query on image instance for the rescale intercept. "DICOM.RescaleIntercept" +- JMESPath query on image instance for array of image frames. "ImageFrames[][]" +- JMESPath query on image frame for max pixel value. "MaxPixelValue" +- JMESPath query on image frame for min pixel value. "MinPixelValue" +- JMESPath query on image frame for checksum of max resolution image. "max_by(PixelDataChecksumFromBaseToFullResolution, &Width).Checksum" + + +The image frames are decoded to a bitmap format and then the checksum is calculated and compared with the checksum +retrieved from the metadata. + ``` *************************************************************************************** @@ -310,8 +299,6 @@ TODO * medical-imaging_DeleteImageSet -TODO - Should CloudFormation metadata be added. - --- # Other material