From 4132228c107c0f5a39641426ba7d8af4501c35e4 Mon Sep 17 00:00:00 2001 From: shenwei41 Date: Sat, 21 Nov 2020 16:08:24 +0800 Subject: [PATCH] Add c++ api of randomresizecropwithbbox --- .../ccsrc/minddata/dataset/api/vision.cc | 91 +++++++++++ .../ccsrc/minddata/dataset/include/vision.h | 39 +++++ tests/ut/cpp/dataset/c_api_vision_test.cc | 145 +++++++++++++++++- 3 files changed, 267 insertions(+), 8 deletions(-) diff --git a/mindspore/ccsrc/minddata/dataset/api/vision.cc b/mindspore/ccsrc/minddata/dataset/api/vision.cc index 3a9685cf87d..fc99b80c01b 100644 --- a/mindspore/ccsrc/minddata/dataset/api/vision.cc +++ b/mindspore/ccsrc/minddata/dataset/api/vision.cc @@ -47,6 +47,7 @@ #include "minddata/dataset/kernels/image/random_crop_op.h" #include "minddata/dataset/kernels/image/random_crop_decode_resize_op.h" #include "minddata/dataset/kernels/image/random_crop_with_bbox_op.h" +#include "minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h" #include "minddata/dataset/kernels/image/random_horizontal_flip_op.h" #include "minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h" #include "minddata/dataset/kernels/image/random_posterize_op.h" @@ -283,6 +284,17 @@ std::shared_ptr RandomResizedCrop(std::vectorValidateParams() ? op : nullptr; } +// Function to create RandomResizedCropOperation. +std::shared_ptr RandomResizedCropWithBBox(std::vector size, + std::vector scale, + std::vector ratio, + InterpolationMode interpolation, + int32_t max_attempts) { + auto op = std::make_shared(size, scale, ratio, interpolation, max_attempts); + // Input validation + return op->ValidateParams() ? op : nullptr; +} + // Function to create RandomRotationOperation. std::shared_ptr RandomRotation(std::vector degrees, InterpolationMode resample, bool expand, std::vector center, @@ -1441,6 +1453,85 @@ std::shared_ptr RandomResizedCropOperation::Build() { return tensor_op; } +// RandomResizedCropWithBBoxOperation +RandomResizedCropWithBBoxOperation::RandomResizedCropWithBBoxOperation(std::vector size, + std::vector scale, + std::vector ratio, + InterpolationMode interpolation, + int32_t max_attempts) + : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} + +Status RandomResizedCropWithBBoxOperation::ValidateParams() { + // size + if (size_.size() != 2 && size_.size() != 1) { + std::string err_msg = + "RandomResizedCropWithBBox: size must be a vector of one or two values, got: " + std::to_string(size_.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (size_[0] <= 0 || (size_.size() == 2 && size_[1] <= 0)) { + std::string err_msg = "RandomResizedCropWithBBox: size must only contain positive integers."; + MS_LOG(ERROR) << "RandomResizedCropWithBBox: size must only contain positive integers, got: " << size_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + // scale + if (scale_.size() != 2) { + std::string err_msg = + "RandomResizedCropWithBBox: scale must be a vector of two values, got: " + std::to_string(scale_.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (scale_[0] < 0 || scale_[1] < 0) { + std::string err_msg = "RandomResizedCropWithBBox: scale must be greater than or equal to 0."; + MS_LOG(ERROR) << "RandomResizedCropWithBBox: scale must be greater than or equal to 0, got: " << scale_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (scale_[1] < scale_[0]) { + std::string err_msg = "RandomResizedCropWithBBox: scale must have a size of two in the format of (min, max)."; + MS_LOG(ERROR) << "RandomResizedCropWithBBox: scale must have a size of two in the format of (min, max), but got: " + << scale_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + // ratio + if (ratio_.size() != 2) { + std::string err_msg = + "RandomResizedCropWithBBox: ratio must be a vector of two values, got: " + std::to_string(ratio_.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (ratio_[0] < 0 || ratio_[1] < 0) { + std::string err_msg = "RandomResizedCropWithBBox: ratio must be greater than or equal to 0."; + MS_LOG(ERROR) << "RandomResizedCropWithBBox: ratio must be greater than or equal to 0, got: " << ratio_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (ratio_[1] < ratio_[0]) { + std::string err_msg = "RandomResizedCropWithBBox: ratio must have a size of two in the format of (min, max)."; + MS_LOG(ERROR) << "RandomResizedCropWithBBox: ratio must have a size of two in the format of (min, max), but got: " + << ratio_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + // max_attempts + if (max_attempts_ < 1) { + std::string err_msg = "RandomResizedCropWithBBox: max_attempts must be greater than or equal to 1, got: " + + std::to_string(max_attempts_); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + return Status::OK(); +} + +std::shared_ptr RandomResizedCropWithBBoxOperation::Build() { + int32_t height = size_[0]; + int32_t width = size_[0]; + // User specified the width value. + if (size_.size() == 2) { + width = size_[1]; + } + std::shared_ptr tensor_op = std::make_shared( + height, width, scale_[0], scale_[1], ratio_[0], ratio_[1], interpolation_, max_attempts_); + return tensor_op; +} + // Function to create RandomRotationOperation. RandomRotationOperation::RandomRotationOperation(std::vector degrees, InterpolationMode interpolation_mode, bool expand, std::vector center, diff --git a/mindspore/ccsrc/minddata/dataset/include/vision.h b/mindspore/ccsrc/minddata/dataset/include/vision.h index d16655c0931..3c352d96284 100644 --- a/mindspore/ccsrc/minddata/dataset/include/vision.h +++ b/mindspore/ccsrc/minddata/dataset/include/vision.h @@ -64,6 +64,7 @@ class RandomPosterizeOperation; class RandomResizeOperation; class RandomResizeWithBBoxOperation; class RandomResizedCropOperation; +class RandomResizedCropWithBBoxOperation; class RandomRotationOperation; class RandomSelectSubpolicyOperation; class RandomSharpnessOperation; @@ -343,6 +344,23 @@ std::shared_ptr RandomResizedCrop( std::vector size, std::vector scale = {0.08, 1.0}, std::vector ratio = {3. / 4., 4. / 3.}, InterpolationMode interpolation = InterpolationMode::kLinear, int32_t max_attempts = 10); +/// \brief Function to create a RandomResizedCropWithBBox TensorOperation. +/// \notes Crop the input image to a random size and aspect ratio. +/// \param[in] size A vector representing the output size of the cropped image. +/// If size is a single value, a square crop of size (size, size) is returned. +/// If size has 2 values, it should be (height, width). +/// \param[in] scale Range [min, max) of respective size of the original +/// size to be cropped (default=(0.08, 1.0)) +/// \param[in] ratio Range [min, max) of aspect ratio to be cropped +/// (default=(3. / 4., 4. / 3.)). +/// \param[in] interpolation Image interpolation mode (default=InterpolationMode::kLinear) +/// \param[in] max_attempts The maximum number of attempts to propose a valid +/// crop_area (default=10). If exceeded, fall back to use center_crop instead. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr RandomResizedCropWithBBox( + std::vector size, std::vector scale = {0.08, 1.0}, std::vector ratio = {3. / 4., 4. / 3.}, + InterpolationMode interpolation = InterpolationMode::kLinear, int32_t max_attempts = 10); + /// \brief Function to create a RandomRotation TensorOp /// \notes Rotates the image according to parameters /// \param[in] degrees A float vector of size 2, representing the starting and ending degree @@ -863,6 +881,27 @@ class RandomResizedCropOperation : public TensorOperation { int32_t max_attempts_; }; +class RandomResizedCropWithBBoxOperation : public TensorOperation { + public: + explicit RandomResizedCropWithBBoxOperation(std::vector size, std::vector scale = {0.08, 1.0}, + std::vector ratio = {3. / 4., 4. / 3.}, + InterpolationMode interpolation = InterpolationMode::kNearestNeighbour, + int32_t max_attempts = 10); + + ~RandomResizedCropWithBBoxOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + private: + std::vector size_; + std::vector scale_; + std::vector ratio_; + InterpolationMode interpolation_; + int32_t max_attempts_; +}; + class RandomRotationOperation : public TensorOperation { public: RandomRotationOperation(std::vector degrees, InterpolationMode interpolation_mode, bool expand, diff --git a/tests/ut/cpp/dataset/c_api_vision_test.cc b/tests/ut/cpp/dataset/c_api_vision_test.cc index a5306e4aeae..e3dffab56ed 100644 --- a/tests/ut/cpp/dataset/c_api_vision_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_test.cc @@ -1969,6 +1969,133 @@ TEST_F(MindDataTestPipeline, TestRandomResizedCropFail4) { EXPECT_EQ(random_resized_crop, nullptr); } +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxSuccess1) { + // Testing RandomResizedCropWithBBox with default values + // Create an VOC Dataset + std::string folder_path = datasets_root_path_ + "/testVOC2012_2"; + std::shared_ptr ds = VOC(folder_path, "Detection", "train", {}, true, SequentialSampler(0, 4)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox({5}); + EXPECT_NE(random_resized_crop, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_resized_crop}, {"image", "bbox"}); + EXPECT_NE(ds, nullptr); + + // Create an iterator over the result of the above dataset + // This will trigger the creation of the Execution Tree and launch it. + std::shared_ptr iter = ds->CreateIterator(); + EXPECT_NE(iter, nullptr); + + // Iterate the dataset and get each row + std::unordered_map> row; + iter->GetNextRow(&row); + + uint64_t i = 0; + while (row.size() != 0) { + i++; + auto image = row["image"]; + MS_LOG(INFO) << "Tensor image shape: " << image->shape(); + EXPECT_EQ(image->shape()[0] == 5 && image->shape()[1] == 5, true); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 4); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxSuccess2) { + // Testing RandomResizedCropWithBBox with non-default values + // Create an VOC Dataset + std::string folder_path = datasets_root_path_ + "/testVOC2012_2"; + std::shared_ptr ds = VOC(folder_path, "Detection", "train", {}, true, SequentialSampler(0, 4)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox( + {5, 10}, {0.25, 0.75}, {0.5, 1.25}, mindspore::dataset::InterpolationMode::kArea, 20); + EXPECT_NE(random_resized_crop, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_resized_crop}, {"image", "bbox"}); + EXPECT_NE(ds, nullptr); + + // Create an iterator over the result of the above dataset + // This will trigger the creation of the Execution Tree and launch it. + std::shared_ptr iter = ds->CreateIterator(); + EXPECT_NE(iter, nullptr); + + // Iterate the dataset and get each row + std::unordered_map> row; + iter->GetNextRow(&row); + + uint64_t i = 0; + while (row.size() != 0) { + i++; + auto image = row["image"]; + MS_LOG(INFO) << "Tensor image shape: " << image->shape(); + EXPECT_EQ(image->shape()[0] == 5 && image->shape()[1] == 10, true); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 4); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxFail1) { + // This should fail because size has negative value + // Create a Cifar10 Dataset + std::string folder_path = datasets_root_path_ + "/testCifar10Data/"; + std::shared_ptr ds = Cifar10(folder_path, "all", RandomSampler(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox({5, -10}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxFail2) { + // This should fail because scale isn't in {min, max} format + // Create a Cifar10 Dataset + std::string folder_path = datasets_root_path_ + "/testCifar10Data/"; + std::shared_ptr ds = Cifar10(folder_path, "all", RandomSampler(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox({5, 10}, {4, 3}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxFail3) { + // This should fail because ratio isn't in {min, max} format + // Create a Cifar10 Dataset + std::string folder_path = datasets_root_path_ + "/testCifar10Data/"; + std::shared_ptr ds = Cifar10(folder_path, "all", RandomSampler(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox({5, 10}, {4, 5}, {7, 6}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropWithBBoxFail4) { + // This should fail because scale has a size of more than 2 + // Create a Cifar10 Dataset + std::string folder_path = datasets_root_path_ + "/testCifar10Data/"; + std::shared_ptr ds = Cifar10(folder_path, "all", RandomSampler(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_resized_crop = vision::RandomResizedCropWithBBox({5, 10, 20}, {4, 5}, {7, 6}); + EXPECT_EQ(random_resized_crop, nullptr); +} + TEST_F(MindDataTestPipeline, TestRandomRotation) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomRotation."; @@ -2116,8 +2243,8 @@ TEST_F(MindDataTestPipeline, TestRandomSelectSubpolicyFail) { EXPECT_EQ(random_select_subpolicy1, nullptr); // RandomSelectSubpolicy: policy must not be empty - std::shared_ptr random_select_subpolicy2 = vision::RandomSelectSubpolicy( - {{{vision::Invert(), 0.5}, {vision::Equalize(), 0.5}}, {{nullptr, 1}}}); + std::shared_ptr random_select_subpolicy2 = + vision::RandomSelectSubpolicy({{{vision::Invert(), 0.5}, {vision::Equalize(), 0.5}}, {{nullptr, 1}}}); EXPECT_EQ(random_select_subpolicy2, nullptr); // RandomSelectSubpolicy: policy must not be empty @@ -2125,13 +2252,13 @@ TEST_F(MindDataTestPipeline, TestRandomSelectSubpolicyFail) { EXPECT_EQ(random_select_subpolicy3, nullptr); // RandomSelectSubpolicy: policy must not be empty - std::shared_ptr random_select_subpolicy4 = vision::RandomSelectSubpolicy( - {{{vision::Invert(), 0.5}, {vision::Equalize(), 0.5}}, {}}); + std::shared_ptr random_select_subpolicy4 = + vision::RandomSelectSubpolicy({{{vision::Invert(), 0.5}, {vision::Equalize(), 0.5}}, {}}); EXPECT_EQ(random_select_subpolicy4, nullptr); // RandomSelectSubpolicy: policy must not be empty - std::shared_ptr random_select_subpolicy5 = vision::RandomSelectSubpolicy( - {{{}, {vision::Equalize(), 0.5}}, {{vision::Resize({15, 15}), 1}}}); + std::shared_ptr random_select_subpolicy5 = + vision::RandomSelectSubpolicy({{{}, {vision::Equalize(), 0.5}}, {{vision::Resize({15, 15}), 1}}}); EXPECT_EQ(random_select_subpolicy5, nullptr); } @@ -2569,7 +2696,8 @@ TEST_F(MindDataTestPipeline, TestRescaleFail) { } TEST_F(MindDataTestPipeline, TestSoftDvppDecodeRandomCropResizeJpegSuccess1) { - MS_LOG(INFO) << "Doing MindDataTestPipeline-TestSoftDvppDecodeRandomCropResizeJpegSuccess1 with single integer input."; + MS_LOG(INFO) + << "Doing MindDataTestPipeline-TestSoftDvppDecodeRandomCropResizeJpegSuccess1 with single integer input."; // Create an ImageFolder Dataset std::string folder_path = datasets_root_path_ + "/testPK/data/"; @@ -2610,7 +2738,8 @@ TEST_F(MindDataTestPipeline, TestSoftDvppDecodeRandomCropResizeJpegSuccess1) { } TEST_F(MindDataTestPipeline, TestSoftDvppDecodeRandomCropResizeJpegSuccess2) { - MS_LOG(INFO) << "Doing MindDataTestPipeline-TestSoftDvppDecodeRandomCropResizeJpegSuccess2 with (height, width) input."; + MS_LOG(INFO) + << "Doing MindDataTestPipeline-TestSoftDvppDecodeRandomCropResizeJpegSuccess2 with (height, width) input."; // Create an ImageFolder Dataset std::string folder_path = datasets_root_path_ + "/testPK/data/";