From 9b145926536a966840500b63c520f61b61b9a9a6 Mon Sep 17 00:00:00 2001 From: Steven Meyer <108885656+meyertst-aws@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:29:57 -0400 Subject: [PATCH] All done --- .doc_gen/metadata/rekognition_metadata.yaml | 27 ++++ cpp/example_code/rekognition/CMakeLists.txt | 5 +- cpp/example_code/rekognition/README.md | 117 ++++++++++++++++ .../rekognition/detect_labels.cpp | 11 +- .../rekognition/rekognition_samples.h | 25 ++-- .../rekognition/tests/CMakeLists.txt | 5 +- .../rekognition/tests/gtest_detect_labels.cpp | 30 ++++ .../rekognition/tests/rekognition_gtests.cpp | 131 ++++++++++++++++-- .../rekognition/tests/rekognition_gtests.h | 23 ++- 9 files changed, 334 insertions(+), 40 deletions(-) create mode 100644 cpp/example_code/rekognition/README.md create mode 100644 cpp/example_code/rekognition/tests/gtest_detect_labels.cpp diff --git a/.doc_gen/metadata/rekognition_metadata.yaml b/.doc_gen/metadata/rekognition_metadata.yaml index a4f47f7ea8c..e2ca7e25171 100644 --- a/.doc_gen/metadata/rekognition_metadata.yaml +++ b/.doc_gen/metadata/rekognition_metadata.yaml @@ -1,4 +1,23 @@ # zexi 0.4.0 +rekognition_Hello: + title: Hello &REK; + title_abbrev: Hello &REK; + synopsis: get started using &REK;. + category: Hello + languages: + C++: + versions: + - sdk_version: 1 + github: cpp/example_code/rekognition/hello_rekognition + excerpts: + - description: Code for the CMakeLists.txt CMake file. + snippet_tags: + - cpp.example_code.rekognition.hello_rekognition.cmake + - description: Code for the hello_rekognition.cpp source file. + snippet_tags: + - cpp.example_code.rekognition.hello_rekognition + services: + rekognition: {ListCollections} rekognition_DescribeCollection: guide_topic: title: Describing a collection @@ -555,6 +574,14 @@ rekognition_DetectLabels: snippet_tags: - python.example_code.rekognition.RekognitionImage - python.example_code.rekognition.DetectLabels + C++: + versions: + - sdk_version: 1 + github: cpp/example_code/rekognition + excerpts: + - description: + snippet_tags: + - cpp.example_code.rekognition.DetectLabels services: rekognition: {DetectLabels} rekognition_DetectModerationLabels: diff --git a/cpp/example_code/rekognition/CMakeLists.txt b/cpp/example_code/rekognition/CMakeLists.txt index 54e209408df..637ce92d9e7 100644 --- a/cpp/example_code/rekognition/CMakeLists.txt +++ b/cpp/example_code/rekognition/CMakeLists.txt @@ -17,7 +17,7 @@ set(CMAKE_CXX_STANDARD 11) set(WINDOWS_BUILD ${MSVC}) # Set the location of where Windows can 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 () @@ -50,8 +50,7 @@ foreach (file ${AWSDOC_SOURCE}) # Build the code example executables. set(EXAMPLE_EXE run_${EXAMPLE}) - add_executable(${EXAMPLE_EXE} ${file} - hello_rekognition/hello_rekognition.cpp) + add_executable(${EXAMPLE_EXE} ${file}) target_link_libraries(${EXAMPLE_EXE} ${AWSSDK_LINK_LIBRARIES} ${AWSSDK_PLATFORM_DEPS}) diff --git a/cpp/example_code/rekognition/README.md b/cpp/example_code/rekognition/README.md new file mode 100644 index 00000000000..e0d78cdbe96 --- /dev/null +++ b/cpp/example_code/rekognition/README.md @@ -0,0 +1,117 @@ +# Amazon Rekognition code examples for the SDK for C++ + +## Overview + +Shows how to use the AWS SDK for C++ to work with Amazon Rekognition. + + + + +_Amazon Rekognition makes it easy to add image and video analysis to your applications._ + +## ⚠ Important + +* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/). +* Running the tests might result in charges to your AWS account. +* 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). +* 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). + + + + +## Code examples + +### Prerequisites + + + +Before using the code examples, first complete the installation and setup steps +for [Getting started](https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/getting-started.html) in the AWS SDK for +C++ Developer Guide. +This section covers how to get and build the SDK, and how to build your own code by using the SDK with a +sample Hello World-style application. + +Next, for information on code example structures and how to build and run the examples, see [Getting started with the AWS SDK for C++ code examples](https://docs.aws.amazon.com/sdk-for-cpp/v1/developer-guide/getting-started-code-examples.html). + + + + + +### Get started + +- [Hello Amazon Rekognition](hello_rekognition/CMakeLists.txt#L4) (`ListCollections`) + + +### Single actions + +Code excerpts that show you how to call individual service functions. + +- [DetectLabels](detect_labels.cpp#L22) + +### Cross-service examples + +Sample applications that work across multiple AWS services. + +- [Create a serverless application to manage photos](../../example_code/cross-service/photo_asset_manager) + + + + + +## Run the examples + +### Instructions + +An executable is built for each source file in this folder. These executables are located in the build folder and have +"run_" prepended to the source file name, minus the suffix. See the "main" function in the source file for further instructions. + +For example, to run the action in the source file "my_action.cpp", execute the following command from within the build folder. The command +will display any required arguments. + +``` +./run_my_action +``` + +If the source file is in a different folder, instructions can be found in the README in that +folder. + + + + +#### Hello Amazon Rekognition + +This example shows you how to get started using Amazon Rekognition. + + + +### Tests + +⚠ Running tests might result in charges to your AWS account. + + + +```sh + cd + cmake -DBUILD_TESTS=ON + make + ctest +``` + + + + + +## Additional resources + +- [Amazon Rekognition Developer Guide](https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html) +- [Amazon Rekognition API Reference](https://docs.aws.amazon.com/rekognition/latest/APIReference/Welcome.html) +- [SDK for C++ Amazon Rekognition reference](https://sdk.amazonaws.com/cpp/api/LATEST/aws-cpp-sdk-rekognition/html/annotated.html) + + + + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 \ No newline at end of file diff --git a/cpp/example_code/rekognition/detect_labels.cpp b/cpp/example_code/rekognition/detect_labels.cpp index c7103f3de9c..0fb6b5851f2 100644 --- a/cpp/example_code/rekognition/detect_labels.cpp +++ b/cpp/example_code/rekognition/detect_labels.cpp @@ -29,8 +29,7 @@ */ bool AwsDoc::Rekognition::detectLabels(const Aws::String &imageBucket, const Aws::String &imageKey, - const Aws::Client::ClientConfiguration &clientConfiguration) -{ + const Aws::Client::ClientConfiguration &clientConfiguration) { Aws::Rekognition::RekognitionClient rekognitionClient(clientConfiguration); Aws::Rekognition::Model::DetectLabelsRequest request; @@ -49,14 +48,12 @@ bool AwsDoc::Rekognition::detectLabels(const Aws::String &imageBucket, const Aws::Vector &labels = outcome.GetResult().GetLabels(); if (labels.empty()) { std::cout << "No labels detected" << std::endl; - } - else { + } else { for (const Aws::Rekognition::Model::Label &label: labels) { std::cout << label.GetName() << ": " << label.GetConfidence() << std::endl; } } - } - else { + } else { std::cerr << "Error while detecting labels: '" << outcome.GetError().GetMessage() << "'" << std::endl; @@ -68,14 +65,12 @@ bool AwsDoc::Rekognition::detectLabels(const Aws::String &imageBucket, // snippet-end:[cpp.example_code.rekognition.DetectLabels] /* - * * main function * * Usage: 'run_detect_labels ' * * Prerequisites: An S3 bucket with an image. * - * */ #ifndef TESTING_BUILD diff --git a/cpp/example_code/rekognition/rekognition_samples.h b/cpp/example_code/rekognition/rekognition_samples.h index eafa8b2dc83..9c19d500259 100644 --- a/cpp/example_code/rekognition/rekognition_samples.h +++ b/cpp/example_code/rekognition/rekognition_samples.h @@ -1,7 +1,7 @@ -// -// Created by Meyer, Steve on 6/11/24. -// +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +#pragma once #ifndef EXAMPLES_REKOGNITION_SAMPLES_H #define EXAMPLES_REKOGNITION_SAMPLES_H @@ -10,17 +10,16 @@ namespace AwsDoc { namespace Rekognition { //! Detect instances of real-world entities within an image by using Amazon Rekognition -/*! - \param image: The - \param clientConfiguration: AWS client configuration. - \return bool: Function succeeded. - */ - -bool detectLabels(const Aws::String &imageBucket, - const Aws::String &imageKey, - const Aws::Client::ClientConfiguration &clientConfiguration); + /*! + \param imageBucket: The Amazon Simple Storage Service (Amazon S3) bucket containing an image. + \param imageKey: The Amazon S3 key of an image object. + \param clientConfiguration: AWS client configuration. + \return bool: Function succeeded. + */ + bool detectLabels(const Aws::String &imageBucket, + const Aws::String &imageKey, + const Aws::Client::ClientConfiguration &clientConfiguration); } // Rekognition - }// AwsDoc #endif //EXAMPLES_REKOGNITION_SAMPLES_H diff --git a/cpp/example_code/rekognition/tests/CMakeLists.txt b/cpp/example_code/rekognition/tests/CMakeLists.txt index 39d1c244efb..7f0bfec79c5 100644 --- a/cpp/example_code/rekognition/tests/CMakeLists.txt +++ b/cpp/example_code/rekognition/tests/CMakeLists.txt @@ -4,9 +4,9 @@ # Set the minimum required version of CMake for this project. cmake_minimum_required(VERSION 3.14) -set(EXAMPLE_SERVICE_NAME "ServiceTemplate") +set(EXAMPLE_SERVICE_NAME "rekognition") set(CURRENT_TARGET "${EXAMPLE_SERVICE_NAME}_gtest") -set(CURRENT_TARGET_AWS_DEPENDENCIES) +set(CURRENT_TARGET_AWS_DEPENDENCIES rekognition s3) # Set this project's name. project("${EXAMPLE_SERVICE_NAME}-examples-gtests") @@ -95,6 +95,7 @@ target_compile_definitions( ${CURRENT_TARGET} PUBLIC TESTING_BUILD + TESTS_DIR="${CMAKE_CURRENT_SOURCE_DIR}" ) target_link_libraries( diff --git a/cpp/example_code/rekognition/tests/gtest_detect_labels.cpp b/cpp/example_code/rekognition/tests/gtest_detect_labels.cpp new file mode 100644 index 00000000000..7cc1bc6a8a8 --- /dev/null +++ b/cpp/example_code/rekognition/tests/gtest_detect_labels.cpp @@ -0,0 +1,30 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* + * Test types are indicated by the test label ending. + * + * _1_ Requires credentials, permissions, and AWS resources. + * _2_ Requires credentials and permissions. + * _3_ Does not require credentials. + * + */ + +#include +#include "rekognition_samples.h" +#include "rekognition_gtests.h" + +namespace AwsDocTest { + // NOLINTNEXTLINE(readability-named-parameter) + TEST_F(Rekognition_GTests, detect_labels_2_) { + Aws::String bucketName = getImageBucket(); + ASSERT_FALSE(bucketName.empty()) << preconditionError() << std::endl; + Aws::String fileName = getImageFileName(); + Aws::String imageKey = "test_rekognition_cpp.jpg"; + + bool result = uploadImage(bucketName, fileName, imageKey); + ASSERT_TRUE(result) << preconditionError() << std::endl; + + result = AwsDoc::Rekognition::detectLabels(bucketName, imageKey, *s_clientConfig); + ASSERT_TRUE(result); + } +} // namespace AwsDocTest diff --git a/cpp/example_code/rekognition/tests/rekognition_gtests.cpp b/cpp/example_code/rekognition/tests/rekognition_gtests.cpp index e3c7a7a94ee..022d2ce82a7 100644 --- a/cpp/example_code/rekognition/tests/rekognition_gtests.cpp +++ b/cpp/example_code/rekognition/tests/rekognition_gtests.cpp @@ -4,11 +4,19 @@ #include "rekognition_gtests.h" #include #include +#include +#include +#include +#include +#include +#include -Aws::SDKOptions AwsDocTest::S3_GTests::s_options; -std::unique_ptr AwsDocTest::S3_GTests::s_clientConfig; -void AwsDocTest::S3_GTests::SetUpTestSuite() { +Aws::SDKOptions AwsDocTest::Rekognition_GTests::s_options; +std::unique_ptr AwsDocTest::Rekognition_GTests::s_clientConfig; +Aws::String AwsDocTest::Rekognition_GTests::s_bucketName; + +void AwsDocTest::Rekognition_GTests::SetUpTestSuite() { InitAPI(s_options); // s_clientConfig must be a pointer because the client config must be initialized @@ -16,12 +24,16 @@ void AwsDocTest::S3_GTests::SetUpTestSuite() { s_clientConfig = std::make_unique(); } -void AwsDocTest::S3_GTests::TearDownTestSuite() { +void AwsDocTest::Rekognition_GTests::TearDownTestSuite() { + if (!s_bucketName.empty()) { + deleteBucket(s_bucketName); + } + ShutdownAPI(s_options); } -void AwsDocTest::S3_GTests::SetUp() { +void AwsDocTest::Rekognition_GTests::SetUp() { if (suppressStdOut()) { m_savedBuffer = std::cout.rdbuf(); std::cout.rdbuf(&m_coutBuffer); @@ -35,7 +47,7 @@ void AwsDocTest::S3_GTests::SetUp() { std::cin.exceptions(std::ios_base::badbit); } -void AwsDocTest::S3_GTests::TearDown() { +void AwsDocTest::Rekognition_GTests::TearDown() { if (m_savedBuffer != nullptr) { std::cout.rdbuf(m_savedBuffer); m_savedBuffer = nullptr; @@ -48,11 +60,11 @@ void AwsDocTest::S3_GTests::TearDown() { } } -Aws::String AwsDocTest::S3_GTests::preconditionError() { +Aws::String AwsDocTest::Rekognition_GTests::preconditionError() { return "Failed to meet precondition."; } -void AwsDocTest::S3_GTests::AddCommandLineResponses( +void AwsDocTest::Rekognition_GTests::AddCommandLineResponses( const std::vector &responses) { std::stringstream stringStream; @@ -63,11 +75,112 @@ void AwsDocTest::S3_GTests::AddCommandLineResponses( } -bool AwsDocTest::S3_GTests::suppressStdOut() { +bool AwsDocTest::Rekognition_GTests::suppressStdOut() { return std::getenv("EXAMPLE_TESTS_LOG_ON") == nullptr; } +Aws::String AwsDocTest::Rekognition_GTests::getImageBucket() { + if (s_bucketName.empty()) { + Aws::String bucketName = uuidName("rekognition-test-cpp-"); + Aws::S3::S3Client client(*s_clientConfig); + Aws::S3::Model::CreateBucketRequest request; + request.SetBucket(bucketName); + if (s_clientConfig->region != Aws::Region::US_EAST_1) { + Aws::S3::Model::CreateBucketConfiguration createBucketConfiguration; + createBucketConfiguration.WithLocationConstraint( + Aws::S3::Model::BucketLocationConstraintMapper::GetBucketLocationConstraintForName( + s_clientConfig->region)); + request.WithCreateBucketConfiguration(createBucketConfiguration); + } + + Aws::S3::Model::CreateBucketOutcome outcome = client.CreateBucket(request); + if (!outcome.IsSuccess()) { + const Aws::S3::S3Error &err = outcome.GetError(); + std::cerr << "Rekognition_GTests::getImageBucket Error: CreateBucket: " << + err.GetExceptionName() << ": " << err.GetMessage() << std::endl; + } else { + s_bucketName = bucketName; + } + } + + return s_bucketName; +} + + +Aws::String AwsDocTest::Rekognition_GTests::uuidName(const Aws::String &prefix) { + Aws::String uuid = Aws::Utils::UUID::RandomUUID(); + return prefix + Aws::Utils::StringUtils::ToLower(uuid.c_str()); +} + +Aws::String AwsDocTest::Rekognition_GTests::getImageFileName() { + return TESTS_DIR"/../../../../resources/sample_files/.sample_media/market_2.jpg"; +} + +bool AwsDocTest::Rekognition_GTests::uploadImage(const Aws::String &bucketName, const Aws::String &imageFileName, + const Aws::String &keyName) { + Aws::S3::S3Client client(*s_clientConfig); + Aws::S3::Model::PutObjectRequest request; + request.SetBucket(bucketName); + request.SetKey(keyName); + std::shared_ptr inputData = + Aws::MakeShared("SampleAllocationTag", + imageFileName.c_str(), + std::ios_base::in | std::ios_base::binary); + + if (!*inputData) { + std::cerr << "Error unable to read file " << imageFileName << std::endl; + return false; + } + request.SetBody(inputData); + Aws::S3::Model::PutObjectOutcome outcome = client.PutObject(request); + if (!outcome.IsSuccess()) { + const Aws::S3::S3Error &err = outcome.GetError(); + std::cerr << "Rekognition_GTests::uploadImage Error: PutObject: " << + err.GetExceptionName() << ": " << err.GetMessage() << std::endl; + } + + return outcome.IsSuccess(); +} + +void AwsDocTest::Rekognition_GTests::deleteBucket(const Aws::String &bucketName) { + Aws::S3::S3Client s3_client(*s_clientConfig); + + Aws::S3::Model::ListObjectsRequest listObjectsRequest; + listObjectsRequest.SetBucket(bucketName); + + auto listObjectsOutcome = s3_client.ListObjects(listObjectsRequest); + if (listObjectsOutcome.IsSuccess()) { + Aws::Vector objects = listObjectsOutcome.GetResult().GetContents(); + for (const auto &object: objects) { + Aws::S3::Model::DeleteObjectRequest deleteObjectRequest; + deleteObjectRequest.SetBucket(bucketName); + deleteObjectRequest.SetKey(object.GetKey()); + + auto deleteObjectOutcome = s3_client.DeleteObject(deleteObjectRequest); + if (!deleteObjectOutcome.IsSuccess()) { + auto &err = deleteObjectOutcome.GetError(); + std::cerr << "Error: DeleteObject: " << err.GetExceptionName() << ": " << err.GetMessage() << std::endl; + break; + } + } + } else { + auto &err = listObjectsOutcome.GetError(); + std::cerr << "Error: ListObjects: " << err.GetExceptionName() << ": " << err.GetMessage() << std::endl; + } + + // Delete the bucket + Aws::S3::Model::DeleteBucketRequest deleteBucketRequest; + deleteBucketRequest.SetBucket(bucketName); + + auto deleteBucketOutcome = s3_client.DeleteBucket(deleteBucketRequest); + if (!deleteBucketOutcome.IsSuccess()) { + auto &err = deleteBucketOutcome.GetError(); + std::cerr << "Error: DeleteBucket: " << err.GetExceptionName() << ": " << err.GetMessage() << std::endl; + } +} + + int AwsDocTest::MyStringBuffer::underflow() { int result = basic_stringbuf::underflow(); if (result == EOF) { diff --git a/cpp/example_code/rekognition/tests/rekognition_gtests.h b/cpp/example_code/rekognition/tests/rekognition_gtests.h index 2d32f7369f6..7850ce63745 100644 --- a/cpp/example_code/rekognition/tests/rekognition_gtests.h +++ b/cpp/example_code/rekognition/tests/rekognition_gtests.h @@ -3,8 +3,8 @@ #pragma once -#ifndef S3_EXAMPLES_S3_GTESTS_H -#define S3_EXAMPLES_S3_GTESTS_H +#ifndef REKOGNITION_EXAMPLES_REKOGNITION_GTESTS_H +#define REKOGNITION_EXAMPLES_REKOGNITION_GTESTS_H #include #include @@ -16,7 +16,7 @@ namespace AwsDocTest { int underflow() override; }; - class S3_GTests : public testing::Test { + class Rekognition_GTests : public testing::Test { protected: void SetUp() override; @@ -31,6 +31,15 @@ namespace AwsDocTest { void AddCommandLineResponses(const std::vector &responses); + Aws::String uuidName(const Aws::String &prefix); + + Aws::String getImageBucket(); + + Aws::String getImageFileName(); + + bool uploadImage(const Aws::String &bucketName, const Aws::String &imageFileName, + const Aws::String &keyName); + // s_clientConfig must be a pointer because the client config must be initialized // after InitAPI. static std::unique_ptr s_clientConfig; @@ -39,6 +48,8 @@ namespace AwsDocTest { static bool suppressStdOut(); + static void deleteBucket(const Aws::String &bucketName); + static Aws::SDKOptions s_options; std::stringbuf m_coutBuffer; // Used to silence cout. @@ -46,7 +57,9 @@ namespace AwsDocTest { MyStringBuffer m_cinBuffer; std::streambuf *m_savedInBuffer = nullptr; - }; // S3_GTests + + static Aws::String s_bucketName; + }; // Rekognition_GTests } // AwsDocTest -#endif //S3_EXAMPLES_S3_GTESTS_H +#endif //REKOGNITION_EXAMPLES_REKOGNITION_GTESTS_H