Weitere AWS SDK Beispiele sind im Repo AWS Doc SDK Examples
Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
HealthImaging Beispiele für die Verwendung von SDK für C++
Die folgenden Codebeispiele zeigen Ihnen, wie Sie mithilfe von AWS SDK for C++ with Aktionen ausführen und allgemeine Szenarien implementieren HealthImaging.
Aktionen sind Codeauszüge aus größeren Programmen und müssen im Kontext ausgeführt werden. Aktionen zeigen Ihnen zwar, wie Sie einzelne Servicefunktionen aufrufen, aber Sie können Aktionen im Kontext der zugehörigen Szenarien sehen.
Szenarien sind Codebeispiele, die Ihnen zeigen, wie Sie bestimmte Aufgaben ausführen, indem Sie mehrere Funktionen innerhalb eines Dienstes oder in Kombination mit anderen aufrufen AWS-Services.
Jedes Beispiel enthält einen Link zum vollständigen Quellcode, in dem Sie Anweisungen zum Einrichten und Ausführen des Codes im Kontext finden.
Erste Schritte
Die folgenden Codebeispiele zeigen, wie Sie mit der Verwendung beginnen HealthImaging.
- SDKfür C++
-
Code für die CMakeLists CMake .txt-Datei.
# Set the minimum required version of CMake for this project. cmake_minimum_required(VERSION 3.13) # Set the AWS service components used by this project. set(SERVICE_COMPONENTS medical-imaging) # Set this project's name. project("hello_health-imaging") # Set the C++ standard to use to build this target. # At least C++ 11 is required for the AWS SDK for C++. set(CMAKE_CXX_STANDARD 11) # Use the MSVC variable to determine if this is a Windows build. set(WINDOWS_BUILD ${MSVC}) if (WINDOWS_BUILD) # Set the location where CMake can find the installed libraries for the AWS SDK. 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 () # Find the AWS SDK for C++ package. find_package(AWSSDK REQUIRED COMPONENTS ${SERVICE_COMPONENTS}) if (WINDOWS_BUILD AND AWSSDK_INSTALL_AS_SHARED_LIBS) # Copy relevant AWS SDK for C++ libraries into the current binary directory for running and debugging. # 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 executable location. AWSSDK_CPY_DYN_LIBS(SERVICE_COMPONENTS "" ${CMAKE_CURRENT_BINARY_DIR}${BIN_SUB_DIR}) endif () add_executable(${PROJECT_NAME} hello_health_imaging.cpp) target_link_libraries(${PROJECT_NAME} ${AWSSDK_LINK_LIBRARIES})
Code für die Quelldatei hello_health_imaging.cpp.
#include <aws/core/Aws.h> #include <aws/medical-imaging/MedicalImagingClient.h> #include <aws/medical-imaging/model/ListDatastoresRequest.h> #include <iostream> /* * A "Hello HealthImaging" starter application which initializes an AWS HealthImaging (HealthImaging) client * and lists the HealthImaging data stores in the current account. * * main function * * Usage: 'hello_health-imaging' * */ #include <aws/core/auth/AWSCredentialsProviderChain.h> #include <aws/core/platform/Environment.h> int main(int argc, char **argv) { (void) argc; (void) argv; Aws::SDKOptions options; // Optional: change the log level for debugging. // options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug; Aws::InitAPI(options); // Should only be called once. { Aws::Client::ClientConfiguration clientConfig; // Optional: Set to the AWS Region (overrides config file). // clientConfig.region = "us-east-1"; Aws::MedicalImaging::MedicalImagingClient medicalImagingClient(clientConfig); Aws::MedicalImaging::Model::ListDatastoresRequest listDatastoresRequest; Aws::Vector<Aws::MedicalImaging::Model::DatastoreSummary> allDataStoreSummaries; Aws::String nextToken; // Used for paginated results. do { if (!nextToken.empty()) { listDatastoresRequest.SetNextToken(nextToken); } Aws::MedicalImaging::Model::ListDatastoresOutcome listDatastoresOutcome = medicalImagingClient.ListDatastores(listDatastoresRequest); if (listDatastoresOutcome.IsSuccess()) { const Aws::Vector<Aws::MedicalImaging::Model::DatastoreSummary> &dataStoreSummaries = listDatastoresOutcome.GetResult().GetDatastoreSummaries(); allDataStoreSummaries.insert(allDataStoreSummaries.cend(), dataStoreSummaries.cbegin(), dataStoreSummaries.cend()); nextToken = listDatastoresOutcome.GetResult().GetNextToken(); } else { std::cerr << "ListDatastores error: " << listDatastoresOutcome.GetError().GetMessage() << std::endl; break; } } while (!nextToken.empty()); std::cout << allDataStoreSummaries.size() << " HealthImaging data " << ((allDataStoreSummaries.size() == 1) ? "store was retrieved." : "stores were retrieved.") << std::endl; for (auto const &dataStoreSummary: allDataStoreSummaries) { std::cout << " Datastore: " << dataStoreSummary.GetDatastoreName() << std::endl; std::cout << " Datastore ID: " << dataStoreSummary.GetDatastoreId() << std::endl; } } Aws::ShutdownAPI(options); // Should only be called once. return 0; }
-
APIEinzelheiten finden Sie ListDatastoresunter AWS SDK for C++ APIReferenz.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Aktionen
Das folgende Codebeispiel zeigt, wie man es benutztDeleteImageSet
.
- SDKfür C++
-
//! Routine which deletes an AWS HealthImaging image set. /*! \param dataStoreID: The HealthImaging data store ID. \param imageSetID: The image set ID. \param clientConfig: Aws client configuration. \return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::deleteImageSet( const Aws::String &dataStoreID, const Aws::String &imageSetID, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::DeleteImageSetRequest request; request.SetDatastoreId(dataStoreID); request.SetImageSetId(imageSetID); Aws::MedicalImaging::Model::DeleteImageSetOutcome outcome = client.DeleteImageSet( request); if (outcome.IsSuccess()) { std::cout << "Successfully deleted image set " << imageSetID << " from data store " << dataStoreID << std::endl; } else { std::cerr << "Error deleting image set " << imageSetID << " from data store " << dataStoreID << ": " << outcome.GetError().GetMessage() << std::endl; } return outcome.IsSuccess(); }
-
APIEinzelheiten finden Sie DeleteImageSetunter AWS SDK for C++ APIReferenz.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Das folgende Codebeispiel zeigt, wie man es benutztGetDICOMImportJob
.
- SDKfür C++
-
//! Routine which gets a HealthImaging DICOM import job's properties. /*! \param dataStoreID: The HealthImaging data store ID. \param importJobID: The DICOM import job ID \param clientConfig: Aws client configuration. \return GetDICOMImportJobOutcome: The import job outcome. */ Aws::MedicalImaging::Model::GetDICOMImportJobOutcome AwsDoc::Medical_Imaging::getDICOMImportJob(const Aws::String &dataStoreID, const Aws::String &importJobID, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::GetDICOMImportJobRequest request; request.SetDatastoreId(dataStoreID); request.SetJobId(importJobID); Aws::MedicalImaging::Model::GetDICOMImportJobOutcome outcome = client.GetDICOMImportJob( request); if (!outcome.IsSuccess()) { std::cerr << "GetDICOMImportJob error: " << outcome.GetError().GetMessage() << std::endl; } return outcome; }
-
APIEinzelheiten finden Sie unter G etDICOMImport Job in AWS SDK for C++ APIReference.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Das folgende Codebeispiel zeigt, wie man es benutztGetImageFrame
.
- SDKfür C++
-
//! Routine which downloads an AWS HealthImaging image frame. /*! \param dataStoreID: The HealthImaging data store ID. \param imageSetID: The image set ID. \param frameID: The image frame ID. \param jphFile: File to store the downloaded frame. \param clientConfig: Aws client configuration. \return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::getImageFrame(const Aws::String &dataStoreID, const Aws::String &imageSetID, const Aws::String &frameID, const Aws::String &jphFile, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::GetImageFrameRequest request; request.SetDatastoreId(dataStoreID); request.SetImageSetId(imageSetID); Aws::MedicalImaging::Model::ImageFrameInformation imageFrameInformation; imageFrameInformation.SetImageFrameId(frameID); request.SetImageFrameInformation(imageFrameInformation); Aws::MedicalImaging::Model::GetImageFrameOutcome outcome = client.GetImageFrame( request); if (outcome.IsSuccess()) { std::cout << "Successfully retrieved image frame." << std::endl; auto &buffer = outcome.GetResult().GetImageFrameBlob(); std::ofstream outfile(jphFile, std::ios::binary); outfile << buffer.rdbuf(); } else { std::cout << "Error retrieving image frame." << outcome.GetError().GetMessage() << std::endl; } return outcome.IsSuccess(); }
-
APIEinzelheiten finden Sie GetImageFrameunter AWS SDK for C++ APIReferenz.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Das folgende Codebeispiel zeigt, wie man es benutztGetImageSetMetadata
.
- SDKfür C++
-
Utility-Funktion zum Abrufen von Bilddatensatz-Metadaten.
//! Routine which gets a HealthImaging image set's metadata. /*! \param dataStoreID: The HealthImaging data store ID. \param imageSetID: The HealthImaging image set ID. \param versionID: The HealthImaging image set version ID, ignored if empty. \param outputFilePath: The path where the metadata will be stored as gzipped json. \param clientConfig: Aws client configuration. \\return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::getImageSetMetadata(const Aws::String &dataStoreID, const Aws::String &imageSetID, const Aws::String &versionID, const Aws::String &outputFilePath, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::Model::GetImageSetMetadataRequest request; request.SetDatastoreId(dataStoreID); request.SetImageSetId(imageSetID); if (!versionID.empty()) { request.SetVersionId(versionID); } Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::GetImageSetMetadataOutcome outcome = client.GetImageSetMetadata( request); if (outcome.IsSuccess()) { std::ofstream file(outputFilePath, std::ios::binary); auto &metadata = outcome.GetResult().GetImageSetMetadataBlob(); file << metadata.rdbuf(); } else { std::cerr << "Failed to get image set metadata: " << outcome.GetError().GetMessage() << std::endl; } return outcome.IsSuccess(); }
Ruft Bildsatz-Metadaten ohne Version ab.
if (AwsDoc::Medical_Imaging::getImageSetMetadata(dataStoreID, imageSetID, "", outputFilePath, clientConfig)) { std::cout << "Successfully retrieved image set metadata." << std::endl; std::cout << "Metadata stored in: " << outputFilePath << std::endl; }
Holen Sie sich Bildsatz-Metadaten mit Version.
if (AwsDoc::Medical_Imaging::getImageSetMetadata(dataStoreID, imageSetID, versionID, outputFilePath, clientConfig)) { std::cout << "Successfully retrieved image set metadata." << std::endl; std::cout << "Metadata stored in: " << outputFilePath << std::endl; }
-
APIEinzelheiten finden Sie GetImageSetMetadataunter AWS SDK for C++ APIReferenz.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Das folgende Codebeispiel zeigt, wie man es benutztSearchImageSets
.
- SDKfür C++
-
Die Hilfsfunktion für die Suche nach Bilddatensätzen.
//! Routine which searches for image sets based on defined input attributes. /*! \param dataStoreID: The HealthImaging data store ID. \param searchCriteria: A search criteria instance. \param imageSetResults: Vector to receive the image set IDs. \param clientConfig: Aws client configuration. \return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::searchImageSets(const Aws::String &dataStoreID, const Aws::MedicalImaging::Model::SearchCriteria &searchCriteria, Aws::Vector<Aws::String> &imageSetResults, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::SearchImageSetsRequest request; request.SetDatastoreId(dataStoreID); request.SetSearchCriteria(searchCriteria); Aws::String nextToken; // Used for paginated results. bool result = true; do { if (!nextToken.empty()) { request.SetNextToken(nextToken); } Aws::MedicalImaging::Model::SearchImageSetsOutcome outcome = client.SearchImageSets( request); if (outcome.IsSuccess()) { for (auto &imageSetMetadataSummary: outcome.GetResult().GetImageSetsMetadataSummaries()) { imageSetResults.push_back(imageSetMetadataSummary.GetImageSetId()); } nextToken = outcome.GetResult().GetNextToken(); } else { std::cout << "Error: " << outcome.GetError().GetMessage() << std::endl; result = false; } } while (!nextToken.empty()); return result; }
Anwendungsfall #1: EQUAL Operator.
Aws::Vector<Aws::String> imageIDsForPatientID; Aws::MedicalImaging::Model::SearchCriteria searchCriteriaEqualsPatientID; Aws::Vector<Aws::MedicalImaging::Model::SearchFilter> patientIDSearchFilters = { Aws::MedicalImaging::Model::SearchFilter().WithOperator(Aws::MedicalImaging::Model::Operator::EQUAL) .WithValues({Aws::MedicalImaging::Model::SearchByAttributeValue().WithDICOMPatientId(patientID)}) }; searchCriteriaEqualsPatientID.SetFilters(patientIDSearchFilters); bool result = AwsDoc::Medical_Imaging::searchImageSets(dataStoreID, searchCriteriaEqualsPatientID, imageIDsForPatientID, clientConfig); if (result) { std::cout << imageIDsForPatientID.size() << " image sets found for the patient with ID '" << patientID << "'." << std::endl; for (auto &imageSetResult : imageIDsForPatientID) { std::cout << " Image set with ID '" << imageSetResult << std::endl; } }
Anwendungsfall #2: BETWEEN Operator, der DICOMStudyDate und verwendetDICOMStudyTime.
Aws::MedicalImaging::Model::SearchByAttributeValue useCase2StartDate; useCase2StartDate.SetDICOMStudyDateAndTime(Aws::MedicalImaging::Model::DICOMStudyDateAndTime() .WithDICOMStudyDate("19990101") .WithDICOMStudyTime("000000.000")); Aws::MedicalImaging::Model::SearchByAttributeValue useCase2EndDate; useCase2EndDate.SetDICOMStudyDateAndTime(Aws::MedicalImaging::Model::DICOMStudyDateAndTime() .WithDICOMStudyDate(Aws::Utils::DateTime(std::chrono::system_clock::now()).ToLocalTimeString("%Y%m%d")) .WithDICOMStudyTime("000000.000")); Aws::MedicalImaging::Model::SearchFilter useCase2SearchFilter; useCase2SearchFilter.SetValues({useCase2StartDate, useCase2EndDate}); useCase2SearchFilter.SetOperator(Aws::MedicalImaging::Model::Operator::BETWEEN); Aws::MedicalImaging::Model::SearchCriteria useCase2SearchCriteria; useCase2SearchCriteria.SetFilters({useCase2SearchFilter}); Aws::Vector<Aws::String> usesCase2Results; result = AwsDoc::Medical_Imaging::searchImageSets(dataStoreID, useCase2SearchCriteria, usesCase2Results, clientConfig); if (result) { std::cout << usesCase2Results.size() << " image sets found for between 1999/01/01 and present." << std::endl; for (auto &imageSetResult : usesCase2Results) { std::cout << " Image set with ID '" << imageSetResult << std::endl; } }
Anwendungsfall #3: BETWEEN Operator verwendetcreatedAt. Zeitstudien wurden zuvor fortgeführt.
Aws::MedicalImaging::Model::SearchByAttributeValue useCase3StartDate; useCase3StartDate.SetCreatedAt(Aws::Utils::DateTime("20231130T000000000Z",Aws::Utils::DateFormat::ISO_8601_BASIC)); Aws::MedicalImaging::Model::SearchByAttributeValue useCase3EndDate; useCase3EndDate.SetCreatedAt(Aws::Utils::DateTime(std::chrono::system_clock::now())); Aws::MedicalImaging::Model::SearchFilter useCase3SearchFilter; useCase3SearchFilter.SetValues({useCase3StartDate, useCase3EndDate}); useCase3SearchFilter.SetOperator(Aws::MedicalImaging::Model::Operator::BETWEEN); Aws::MedicalImaging::Model::SearchCriteria useCase3SearchCriteria; useCase3SearchCriteria.SetFilters({useCase3SearchFilter}); Aws::Vector<Aws::String> usesCase3Results; result = AwsDoc::Medical_Imaging::searchImageSets(dataStoreID, useCase3SearchCriteria, usesCase3Results, clientConfig); if (result) { std::cout << usesCase3Results.size() << " image sets found for created between 2023/11/30 and present." << std::endl; for (auto &imageSetResult : usesCase3Results) { std::cout << " Image set with ID '" << imageSetResult << std::endl; } }
Anwendungsfall #4: EQUAL Operator ein DICOMSeriesInstanceUID und BETWEEN weiter updatedAt und Antwort in der ASC Reihenfolge nach updatedAt Feld sortieren.
Aws::MedicalImaging::Model::SearchByAttributeValue useCase4StartDate; useCase4StartDate.SetUpdatedAt(Aws::Utils::DateTime("20231130T000000000Z",Aws::Utils::DateFormat::ISO_8601_BASIC)); Aws::MedicalImaging::Model::SearchByAttributeValue useCase4EndDate; useCase4EndDate.SetUpdatedAt(Aws::Utils::DateTime(std::chrono::system_clock::now())); Aws::MedicalImaging::Model::SearchFilter useCase4SearchFilterBetween; useCase4SearchFilterBetween.SetValues({useCase4StartDate, useCase4EndDate}); useCase4SearchFilterBetween.SetOperator(Aws::MedicalImaging::Model::Operator::BETWEEN); Aws::MedicalImaging::Model::SearchByAttributeValue seriesInstanceUID; seriesInstanceUID.SetDICOMSeriesInstanceUID(dicomSeriesInstanceUID); Aws::MedicalImaging::Model::SearchFilter useCase4SearchFilterEqual; useCase4SearchFilterEqual.SetValues({seriesInstanceUID}); useCase4SearchFilterEqual.SetOperator(Aws::MedicalImaging::Model::Operator::EQUAL); Aws::MedicalImaging::Model::SearchCriteria useCase4SearchCriteria; useCase4SearchCriteria.SetFilters({useCase4SearchFilterBetween, useCase4SearchFilterEqual}); Aws::MedicalImaging::Model::Sort useCase4Sort; useCase4Sort.SetSortField(Aws::MedicalImaging::Model::SortField::updatedAt); useCase4Sort.SetSortOrder(Aws::MedicalImaging::Model::SortOrder::ASC); useCase4SearchCriteria.SetSort(useCase4Sort); Aws::Vector<Aws::String> usesCase4Results; result = AwsDoc::Medical_Imaging::searchImageSets(dataStoreID, useCase4SearchCriteria, usesCase4Results, clientConfig); if (result) { std::cout << usesCase4Results.size() << " image sets found for EQUAL operator " << "on DICOMSeriesInstanceUID and BETWEEN on updatedAt and sort response\n" << "in ASC order on updatedAt field." << std::endl; for (auto &imageSetResult : usesCase4Results) { std::cout << " Image set with ID '" << imageSetResult << std::endl; } }
-
APIEinzelheiten finden Sie SearchImageSetsunter AWS SDK for C++ APIReferenz.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Das folgende Codebeispiel zeigt, wie man es benutztStartDICOMImportJob
.
- SDKfür C++
-
//! Routine which starts a HealthImaging import job. /*! \param dataStoreID: The HealthImaging data store ID. \param inputBucketName: The name of the Amazon S3 bucket containing the DICOM files. \param inputDirectory: The directory in the S3 bucket containing the DICOM files. \param outputBucketName: The name of the S3 bucket for the output. \param outputDirectory: The directory in the S3 bucket to store the output. \param roleArn: The ARN of the IAM role with permissions for the import. \param importJobId: A string to receive the import job ID. \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, const Aws::String &outputDirectory, const Aws::String &roleArn, Aws::String &importJobId, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient medicalImagingClient(clientConfig); Aws::String inputURI = "s3://" + inputBucketName + "/" + inputDirectory + "/"; Aws::String outputURI = "s3://" + outputBucketName + "/" + outputDirectory + "/"; Aws::MedicalImaging::Model::StartDICOMImportJobRequest startDICOMImportJobRequest; startDICOMImportJobRequest.SetDatastoreId(dataStoreID); startDICOMImportJobRequest.SetDataAccessRoleArn(roleArn); startDICOMImportJobRequest.SetInputS3Uri(inputURI); startDICOMImportJobRequest.SetOutputS3Uri(outputURI); Aws::MedicalImaging::Model::StartDICOMImportJobOutcome startDICOMImportJobOutcome = medicalImagingClient.StartDICOMImportJob( startDICOMImportJobRequest); if (startDICOMImportJobOutcome.IsSuccess()) { importJobId = startDICOMImportJobOutcome.GetResult().GetJobId(); } else { std::cerr << "Failed to start DICOM import job because " << startDICOMImportJobOutcome.GetError().GetMessage() << std::endl; } return startDICOMImportJobOutcome.IsSuccess(); }
-
APIEinzelheiten finden Sie unter S tartDICOMImport Job in AWS SDK for C++ APIReference.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -
Szenarien
Das folgende Codebeispiel zeigt, wie Sie DICOM Dateien importieren und Bildrahmen herunterladen HealthImaging.
Die Implementierung ist als Workflow-Befehlszeilenanwendung strukturiert.
Richten Sie Ressourcen für einen Import einDICOM.
DICOMImportiert Dateien in einen Datenspeicher.
Rufen Sie den Bilddatensatz IDs für den Importjob ab.
Rufen Sie den Bildrahmen IDs für die Bildsätze ab.
Laden Sie die Bildrahmen herunter, dekodieren Sie sie und überprüfen Sie sie.
Ressourcen bereinigen.
- SDKfür C++
-
Erstellen Sie einen AWS CloudFormation Stack mit den erforderlichen Ressourcen.
Aws::String inputBucketName; Aws::String outputBucketName; Aws::String dataStoreId; Aws::String roleArn; Aws::String stackName; if (askYesNoQuestion( "Would you like to let this workflow create the resources for you? (y/n) ")) { stackName = askQuestion( "Enter a name for the AWS CloudFormation stack to create. "); Aws::String dataStoreName = askQuestion( "Enter a name for the HealthImaging datastore to create. "); Aws::Map<Aws::String, Aws::String> outputs = createCloudFormationStack( stackName, dataStoreName, clientConfiguration); if (!retrieveOutputs(outputs, dataStoreId, inputBucketName, outputBucketName, roleArn)) { return false; } std::cout << "The following resources have been created." << std::endl; std::cout << "A HealthImaging datastore with ID: " << dataStoreId << "." << std::endl; std::cout << "An Amazon S3 input bucket named: " << inputBucketName << "." << std::endl; std::cout << "An Amazon S3 output bucket named: " << outputBucketName << "." << std::endl; std::cout << "An IAM role with the ARN: " << roleArn << "." << std::endl; askQuestion("Enter return to continue.", alwaysTrueTest); } else { std::cout << "You have chosen to use preexisting resources:" << std::endl; dataStoreId = askQuestion( "Enter the data store ID of the HealthImaging datastore you wish to use: "); inputBucketName = askQuestion( "Enter the name of the S3 input bucket you wish to use: "); outputBucketName = askQuestion( "Enter the name of the S3 output bucket you wish to use: "); roleArn = askQuestion( "Enter the ARN for the IAM role with the proper permissions to import a DICOM series: "); }
Kopieren Sie DICOM Dateien in den Amazon S3 S3-Import-Bucket.
std::cout << "This workflow uses DICOM files from the National Cancer Institute Imaging Data\n" << "Commons (IDC) Collections." << std::endl; std::cout << "Here is the link to their website." << std::endl; std::cout << "https://registry.opendata.aws/nci-imaging-data-commons/" << std::endl; std::cout << "We will use DICOM files stored in an S3 bucket managed by the IDC." << std::endl; std::cout << "First one of the DICOM folders in the IDC collection must be copied to your\n" "input S3 bucket." << std::endl; std::cout << "You have the choice of one of the following " << IDC_ImageChoices.size() << " folders to copy." << std::endl; int index = 1; for (auto &idcChoice: IDC_ImageChoices) { std::cout << index << " - " << idcChoice.mDescription << std::endl; index++; } int choice = askQuestionForIntRange("Choose DICOM files to import: ", 1, 4); Aws::String fromDirectory = IDC_ImageChoices[choice - 1].mDirectory; Aws::String inputDirectory = "input"; std::cout << "The files in the directory '" << fromDirectory << "' in the bucket '" << IDC_S3_BucketName << "' will be copied " << std::endl; std::cout << "to the folder '" << inputDirectory << "/" << fromDirectory << "' in the bucket '" << inputBucketName << "'." << std::endl; askQuestion("Enter return to start the copy.", alwaysTrueTest); if (!AwsDoc::Medical_Imaging::copySeriesBetweenBuckets( IDC_S3_BucketName, fromDirectory, inputBucketName, inputDirectory, clientConfiguration)) { std::cerr << "This workflow will exit because of an error." << std::endl; cleanup(stackName, dataStoreId, clientConfiguration); return false; }
Importieren Sie die DICOM Dateien in den Amazon S3 S3-Datenspeicher.
bool AwsDoc::Medical_Imaging::startDicomImport(const Aws::String &dataStoreID, const Aws::String &inputBucketName, const Aws::String &inputDirectory, const Aws::String &outputBucketName, const Aws::String &outputDirectory, const Aws::String &roleArn, Aws::String &importJobId, const Aws::Client::ClientConfiguration &clientConfiguration) { bool result = false; if (startDICOMImportJob(dataStoreID, inputBucketName, inputDirectory, outputBucketName, outputDirectory, roleArn, importJobId, clientConfiguration)) { std::cout << "DICOM import job started with job ID " << importJobId << "." << std::endl; result = waitImportJobCompleted(dataStoreID, importJobId, clientConfiguration); if (result) { std::cout << "DICOM import job completed." << std::endl; } } return result; } //! Routine which starts a HealthImaging import job. /*! \param dataStoreID: The HealthImaging data store ID. \param inputBucketName: The name of the Amazon S3 bucket containing the DICOM files. \param inputDirectory: The directory in the S3 bucket containing the DICOM files. \param outputBucketName: The name of the S3 bucket for the output. \param outputDirectory: The directory in the S3 bucket to store the output. \param roleArn: The ARN of the IAM role with permissions for the import. \param importJobId: A string to receive the import job ID. \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, const Aws::String &outputDirectory, const Aws::String &roleArn, Aws::String &importJobId, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient medicalImagingClient(clientConfig); Aws::String inputURI = "s3://" + inputBucketName + "/" + inputDirectory + "/"; Aws::String outputURI = "s3://" + outputBucketName + "/" + outputDirectory + "/"; Aws::MedicalImaging::Model::StartDICOMImportJobRequest startDICOMImportJobRequest; startDICOMImportJobRequest.SetDatastoreId(dataStoreID); startDICOMImportJobRequest.SetDataAccessRoleArn(roleArn); startDICOMImportJobRequest.SetInputS3Uri(inputURI); startDICOMImportJobRequest.SetOutputS3Uri(outputURI); Aws::MedicalImaging::Model::StartDICOMImportJobOutcome startDICOMImportJobOutcome = medicalImagingClient.StartDICOMImportJob( startDICOMImportJobRequest); if (startDICOMImportJobOutcome.IsSuccess()) { importJobId = startDICOMImportJobOutcome.GetResult().GetJobId(); } else { std::cerr << "Failed to start DICOM import job because " << startDICOMImportJobOutcome.GetError().GetMessage() << std::endl; } return startDICOMImportJobOutcome.IsSuccess(); } //! Routine which waits for a DICOM import job to complete. /*! * @param dataStoreID: The HealthImaging data store ID. * @param importJobId: The import job ID. * @param clientConfiguration : Aws client configuration. * @return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::waitImportJobCompleted(const Aws::String &datastoreID, const Aws::String &importJobId, const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::MedicalImaging::Model::JobStatus jobStatus = Aws::MedicalImaging::Model::JobStatus::IN_PROGRESS; while (jobStatus == Aws::MedicalImaging::Model::JobStatus::IN_PROGRESS) { std::this_thread::sleep_for(std::chrono::seconds(1)); Aws::MedicalImaging::Model::GetDICOMImportJobOutcome getDicomImportJobOutcome = getDICOMImportJob( datastoreID, importJobId, clientConfiguration); if (getDicomImportJobOutcome.IsSuccess()) { jobStatus = getDicomImportJobOutcome.GetResult().GetJobProperties().GetJobStatus(); std::cout << "DICOM import job status: " << Aws::MedicalImaging::Model::JobStatusMapper::GetNameForJobStatus( jobStatus) << std::endl; } else { std::cerr << "Failed to get import job status because " << getDicomImportJobOutcome.GetError().GetMessage() << std::endl; return false; } } return jobStatus == Aws::MedicalImaging::Model::JobStatus::COMPLETED; } //! Routine which gets a HealthImaging DICOM import job's properties. /*! \param dataStoreID: The HealthImaging data store ID. \param importJobID: The DICOM import job ID \param clientConfig: Aws client configuration. \return GetDICOMImportJobOutcome: The import job outcome. */ Aws::MedicalImaging::Model::GetDICOMImportJobOutcome AwsDoc::Medical_Imaging::getDICOMImportJob(const Aws::String &dataStoreID, const Aws::String &importJobID, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::GetDICOMImportJobRequest request; request.SetDatastoreId(dataStoreID); request.SetJobId(importJobID); Aws::MedicalImaging::Model::GetDICOMImportJobOutcome outcome = client.GetDICOMImportJob( request); if (!outcome.IsSuccess()) { std::cerr << "GetDICOMImportJob error: " << outcome.GetError().GetMessage() << std::endl; } return outcome; }
Ruft Bildsätze ab, die durch den DICOM Importjob erstellt wurden.
bool AwsDoc::Medical_Imaging::getImageSetsForDicomImportJob(const Aws::String &datastoreID, const Aws::String &importJobId, Aws::Vector<Aws::String> &imageSets, const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::MedicalImaging::Model::GetDICOMImportJobOutcome getDicomImportJobOutcome = getDICOMImportJob( datastoreID, importJobId, clientConfiguration); bool result = false; if (getDicomImportJobOutcome.IsSuccess()) { auto outputURI = getDicomImportJobOutcome.GetResult().GetJobProperties().GetOutputS3Uri(); Aws::Http::URI uri(outputURI); const Aws::String &bucket = uri.GetAuthority(); Aws::String key = uri.GetPath(); Aws::S3::S3Client s3Client(clientConfiguration); Aws::S3::Model::GetObjectRequest objectRequest; objectRequest.SetBucket(bucket); objectRequest.SetKey(key + "/" + IMPORT_JOB_MANIFEST_FILE_NAME); auto getObjectOutcome = s3Client.GetObject(objectRequest); if (getObjectOutcome.IsSuccess()) { auto &data = getObjectOutcome.GetResult().GetBody(); std::stringstream stringStream; stringStream << data.rdbuf(); try { // Use JMESPath to extract the image set IDs. // https://jmespath.org/specification.html std::string jmesPathExpression = "jobSummary.imageSetsSummary[].imageSetId"; jsoncons::json doc = jsoncons::json::parse(stringStream.str()); jsoncons::json imageSetsJson = jsoncons::jmespath::search(doc, jmesPathExpression);\ for (auto &imageSet: imageSetsJson.array_range()) { imageSets.push_back(imageSet.as_string()); } result = true; } catch (const std::exception &e) { std::cerr << e.what() << '\n'; } } else { std::cerr << "Failed to get object because " << getObjectOutcome.GetError().GetMessage() << std::endl; } } else { std::cerr << "Failed to get import job status because " << getDicomImportJobOutcome.GetError().GetMessage() << std::endl; } return result; }
Ruft Bildrahmeninformationen für Bildsätze ab.
bool AwsDoc::Medical_Imaging::getImageFramesForImageSet(const Aws::String &dataStoreID, const Aws::String &imageSetID, const Aws::String &outDirectory, Aws::Vector<ImageFrameInfo> &imageFrames, const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::String fileName = outDirectory + "/" + imageSetID + "_metadata.json.gzip"; bool result = false; if (getImageSetMetadata(dataStoreID, imageSetID, "", // Empty string for version ID. fileName, clientConfiguration)) { try { std::string metadataGZip; { std::ifstream inFileStream(fileName.c_str(), std::ios::binary); if (!inFileStream) { throw std::runtime_error("Failed to open file " + fileName); } std::stringstream stringStream; stringStream << inFileStream.rdbuf(); metadataGZip = stringStream.str(); } std::string metadataJson = gzip::decompress(metadataGZip.data(), metadataGZip.size()); // Use JMESPath to extract the image set IDs. // https://jmespath.org/specification.html jsoncons::json doc = jsoncons::json::parse(metadataJson); std::string jmesPathExpression = "Study.Series.*.Instances[].*[]"; jsoncons::json instances = jsoncons::jmespath::search(doc, jmesPathExpression); for (auto &instance: instances.array_range()) { jmesPathExpression = "DICOM.RescaleSlope"; std::string rescaleSlope = jsoncons::jmespath::search(instance, jmesPathExpression).to_string(); jmesPathExpression = "DICOM.RescaleIntercept"; std::string rescaleIntercept = jsoncons::jmespath::search(instance, jmesPathExpression).to_string(); jmesPathExpression = "ImageFrames[][]"; jsoncons::json imageFramesJson = jsoncons::jmespath::search(instance, jmesPathExpression); for (auto &imageFrame: imageFramesJson.array_range()) { ImageFrameInfo imageFrameIDs; imageFrameIDs.mImageSetId = imageSetID; imageFrameIDs.mImageFrameId = imageFrame.find( "ID")->value().as_string(); imageFrameIDs.mRescaleIntercept = rescaleIntercept; imageFrameIDs.mRescaleSlope = rescaleSlope; imageFrameIDs.MinPixelValue = imageFrame.find( "MinPixelValue")->value().as_string(); imageFrameIDs.MaxPixelValue = imageFrame.find( "MaxPixelValue")->value().as_string(); jmesPathExpression = "max_by(PixelDataChecksumFromBaseToFullResolution, &Width).Checksum"; jsoncons::json checksumJson = jsoncons::jmespath::search(imageFrame, jmesPathExpression); imageFrameIDs.mFullResolutionChecksum = checksumJson.as_integer<uint32_t>(); imageFrames.emplace_back(imageFrameIDs); } } result = true; } catch (const std::exception &e) { std::cerr << "getImageFramesForImageSet failed because " << e.what() << std::endl; } } return result; } //! Routine which gets a HealthImaging image set's metadata. /*! \param dataStoreID: The HealthImaging data store ID. \param imageSetID: The HealthImaging image set ID. \param versionID: The HealthImaging image set version ID, ignored if empty. \param outputFilePath: The path where the metadata will be stored as gzipped json. \param clientConfig: Aws client configuration. \\return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::getImageSetMetadata(const Aws::String &dataStoreID, const Aws::String &imageSetID, const Aws::String &versionID, const Aws::String &outputFilePath, const Aws::Client::ClientConfiguration &clientConfig) { Aws::MedicalImaging::Model::GetImageSetMetadataRequest request; request.SetDatastoreId(dataStoreID); request.SetImageSetId(imageSetID); if (!versionID.empty()) { request.SetVersionId(versionID); } Aws::MedicalImaging::MedicalImagingClient client(clientConfig); Aws::MedicalImaging::Model::GetImageSetMetadataOutcome outcome = client.GetImageSetMetadata( request); if (outcome.IsSuccess()) { std::ofstream file(outputFilePath, std::ios::binary); auto &metadata = outcome.GetResult().GetImageSetMetadataBlob(); file << metadata.rdbuf(); } else { std::cerr << "Failed to get image set metadata: " << outcome.GetError().GetMessage() << std::endl; } return outcome.IsSuccess(); }
Laden Sie Bildrahmen herunter, dekodieren und verifizieren Sie sie.
bool AwsDoc::Medical_Imaging::downloadDecodeAndCheckImageFrames( const Aws::String &dataStoreID, const Aws::Vector<ImageFrameInfo> &imageFrames, const Aws::String &outDirectory, const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::Client::ClientConfiguration clientConfiguration1(clientConfiguration); clientConfiguration1.executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>( "executor", 25); Aws::MedicalImaging::MedicalImagingClient medicalImagingClient( clientConfiguration1); Aws::Utils::Threading::Semaphore semaphore(0, 1); std::atomic<size_t> count(imageFrames.size()); bool result = true; for (auto &imageFrame: imageFrames) { Aws::MedicalImaging::Model::GetImageFrameRequest getImageFrameRequest; getImageFrameRequest.SetDatastoreId(dataStoreID); getImageFrameRequest.SetImageSetId(imageFrame.mImageSetId); Aws::MedicalImaging::Model::ImageFrameInformation imageFrameInformation; imageFrameInformation.SetImageFrameId(imageFrame.mImageFrameId); getImageFrameRequest.SetImageFrameInformation(imageFrameInformation); auto getImageFrameAsyncLambda = [&semaphore, &result, &count, imageFrame, outDirectory]( const Aws::MedicalImaging::MedicalImagingClient *client, const Aws::MedicalImaging::Model::GetImageFrameRequest &request, Aws::MedicalImaging::Model::GetImageFrameOutcome outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext> &context) { 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) { semaphore.ReleaseAll(); } }; // End of 'getImageFrameAsyncLambda' lambda. medicalImagingClient.GetImageFrameAsync(getImageFrameRequest, getImageFrameAsyncLambda); } if (count > 0) { semaphore.WaitOne(); } if (result) { std::cout << imageFrames.size() << " image files were downloaded." << std::endl; } return result; } bool AwsDoc::Medical_Imaging::decodeJPHFileAndValidateWithChecksum( const Aws::String &jphFile, uint32_t crc32Checksum) { opj_image_t *outputImage = jphImageToOpjBitmap(jphFile); if (!outputImage) { return false; } bool result = true; if (!verifyChecksumForImage(outputImage, crc32Checksum)) { std::cerr << "The checksum for the image does not match the expected value." << std::endl; std::cerr << "File :" << jphFile << std::endl; result = false; } opj_image_destroy(outputImage); return result; } opj_image * AwsDoc::Medical_Imaging::jphImageToOpjBitmap(const Aws::String &jphFile) { opj_stream_t *inFileStream = nullptr; opj_codec_t *decompressorCodec = nullptr; opj_image_t *outputImage = nullptr; try { std::shared_ptr<opj_dparameters> decodeParameters = std::make_shared<opj_dparameters>(); memset(decodeParameters.get(), 0, sizeof(opj_dparameters)); opj_set_default_decoder_parameters(decodeParameters.get()); decodeParameters->decod_format = 1; // JP2 image format. decodeParameters->cod_format = 2; // BMP image format. std::strncpy(decodeParameters->infile, jphFile.c_str(), OPJ_PATH_LEN); inFileStream = opj_stream_create_default_file_stream( decodeParameters->infile, true); if (!inFileStream) { throw std::runtime_error( "Unable to create input file stream for file '" + jphFile + "'."); } decompressorCodec = opj_create_decompress(OPJ_CODEC_JP2); if (!decompressorCodec) { throw std::runtime_error("Failed to create decompression codec."); } int decodeMessageLevel = 1; if (!setupCodecLogging(decompressorCodec, &decodeMessageLevel)) { std::cerr << "Failed to setup codec logging." << std::endl; } if (!opj_setup_decoder(decompressorCodec, decodeParameters.get())) { throw std::runtime_error("Failed to setup decompression codec."); } if (!opj_codec_set_threads(decompressorCodec, 4)) { throw std::runtime_error("Failed to set decompression codec threads."); } if (!opj_read_header(inFileStream, decompressorCodec, &outputImage)) { throw std::runtime_error("Failed to read header."); } if (!opj_decode(decompressorCodec, inFileStream, outputImage)) { throw std::runtime_error("Failed to decode."); } if (DEBUGGING) { std::cout << "image width : " << outputImage->x1 - outputImage->x0 << std::endl; std::cout << "image height : " << outputImage->y1 - outputImage->y0 << std::endl; std::cout << "number of channels: " << outputImage->numcomps << std::endl; std::cout << "colorspace : " << outputImage->color_space << std::endl; } } catch (const std::exception &e) { std::cerr << e.what() << std::endl; if (outputImage) { opj_image_destroy(outputImage); outputImage = nullptr; } } if (inFileStream) { opj_stream_destroy(inFileStream); } if (decompressorCodec) { opj_destroy_codec(decompressorCodec); } return outputImage; } //! Template function which converts a planar image bitmap to an interleaved image bitmap and //! then verifies the checksum of the bitmap. /*! * @param image: The OpenJPEG image struct. * @param crc32Checksum: The CRC32 checksum. * @return bool: Function succeeded. */ template<class myType> bool verifyChecksumForImageForType(opj_image_t *image, uint32_t crc32Checksum) { uint32_t width = image->x1 - image->x0; uint32_t height = image->y1 - image->y0; uint32_t numOfChannels = image->numcomps; // Buffer for interleaved bitmap. std::vector<myType> buffer(width * height * numOfChannels); // Convert planar bitmap to interleaved bitmap. for (uint32_t channel = 0; channel < numOfChannels; channel++) { for (uint32_t row = 0; row < height; row++) { uint32_t fromRowStart = row / image->comps[channel].dy * width / image->comps[channel].dx; uint32_t toIndex = (row * width) * numOfChannels + channel; for (uint32_t col = 0; col < width; col++) { uint32_t fromIndex = fromRowStart + col / image->comps[channel].dx; buffer[toIndex] = static_cast<myType>(image->comps[channel].data[fromIndex]); toIndex += numOfChannels; } } } // Verify checksum. boost::crc_32_type crc32; crc32.process_bytes(reinterpret_cast<char *>(buffer.data()), buffer.size() * sizeof(myType)); bool result = crc32.checksum() == crc32Checksum; if (!result) { std::cerr << "verifyChecksumForImage, checksum mismatch, expected - " << crc32Checksum << ", actual - " << crc32.checksum() << std::endl; } return result; } //! Routine which verifies the checksum of an OpenJPEG image struct. /*! * @param image: The OpenJPEG image struct. * @param crc32Checksum: The CRC32 checksum. * @return bool: Function succeeded. */ bool AwsDoc::Medical_Imaging::verifyChecksumForImage(opj_image_t *image, uint32_t crc32Checksum) { uint32_t channels = image->numcomps; bool result = false; if (0 < channels) { // Assume the precision is the same for all channels. uint32_t precision = image->comps[0].prec; bool signedData = image->comps[0].sgnd; uint32_t bytes = (precision + 7) / 8; if (signedData) { switch (bytes) { case 1 : result = verifyChecksumForImageForType<int8_t>(image, crc32Checksum); break; case 2 : result = verifyChecksumForImageForType<int16_t>(image, crc32Checksum); break; case 4 : result = verifyChecksumForImageForType<int32_t>(image, crc32Checksum); break; default: std::cerr << "verifyChecksumForImage, unsupported data type, signed bytes - " << bytes << std::endl; break; } } else { switch (bytes) { case 1 : result = verifyChecksumForImageForType<uint8_t>(image, crc32Checksum); break; case 2 : result = verifyChecksumForImageForType<uint16_t>(image, crc32Checksum); break; case 4 : result = verifyChecksumForImageForType<uint32_t>(image, crc32Checksum); break; default: std::cerr << "verifyChecksumForImage, unsupported data type, unsigned bytes - " << bytes << std::endl; break; } } if (!result) { std::cerr << "verifyChecksumForImage, error bytes " << bytes << " signed " << signedData << std::endl; } } else { std::cerr << "'verifyChecksumForImage', no channels in the image." << std::endl; } return result; }
Ressourcen bereinigen.
bool AwsDoc::Medical_Imaging::cleanup(const Aws::String &stackName, const Aws::String &dataStoreId, const Aws::Client::ClientConfiguration &clientConfiguration) { bool result = true; if (!stackName.empty() && askYesNoQuestion( "Would you like to delete the stack " + stackName + "? (y/n)")) { std::cout << "Deleting the image sets in the stack." << std::endl; result &= emptyDatastore(dataStoreId, clientConfiguration); printAsterisksLine(); std::cout << "Deleting the stack." << std::endl; result &= deleteStack(stackName, clientConfiguration); } return result; } bool AwsDoc::Medical_Imaging::emptyDatastore(const Aws::String &datastoreID, const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::MedicalImaging::Model::SearchCriteria emptyCriteria; Aws::Vector<Aws::String> imageSetIDs; bool result = false; if (searchImageSets(datastoreID, emptyCriteria, imageSetIDs, clientConfiguration)) { result = true; for (auto &imageSetID: imageSetIDs) { result &= deleteImageSet(datastoreID, imageSetID, clientConfiguration); } } return result; }
-
APIEinzelheiten finden Sie unter den folgenden Themen im AWS SDK for C++ APIReferenzhandbuch.
Anmerkung
Es gibt noch mehr dazu GitHub. Sie sehen das vollständige Beispiel und erfahren, wie Sie das AWS -Code-Beispiel-Repository
einrichten und ausführen. -