diff --git a/mindspore/ccsrc/minddata/dataset/api/vision.cc b/mindspore/ccsrc/minddata/dataset/api/vision.cc index eba3bc3089..83a68c726a 100644 --- a/mindspore/ccsrc/minddata/dataset/api/vision.cc +++ b/mindspore/ccsrc/minddata/dataset/api/vision.cc @@ -31,6 +31,7 @@ #include "minddata/dataset/kernels/image/random_affine_op.h" #include "minddata/dataset/kernels/image/random_color_op.h" #include "minddata/dataset/kernels/image/random_color_adjust_op.h" +#include "minddata/dataset/kernels/image/random_crop_and_resize_op.h" #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_horizontal_flip_op.h" @@ -236,6 +237,18 @@ std::shared_ptr RandomPosterize(const std::vector RandomResizedCrop(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 + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create RandomRotationOperation. std::shared_ptr RandomRotation(std::vector degrees, InterpolationMode resample, bool expand, std::vector center, @@ -909,6 +922,43 @@ std::shared_ptr RandomPosterizeOperation::Build() { return tensor_op; } +// RandomResizedCropOperation +RandomResizedCropOperation::RandomResizedCropOperation(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) {} +bool RandomResizedCropOperation::ValidateParams() { + if (size_.size() != 2 && size_.size() != 1) { + MS_LOG(ERROR) << "RandomResizedCrop: size variable must have a length of 1 or 2 but it has a length of: " + << size_.size(); + return false; + } + if (size_[0] < 0 || (size_.size() == 2 && size_[1] < 0)) { + MS_LOG(ERROR) << "RandomResizedCrop: size variable must only contain positive integers. However, it is: " << size_; + return false; + } + if (scale_.size() != 2 || scale_[1] < scale_[0]) { + MS_LOG(ERROR) + << "RandomResizedCrop: scale variable must have a size of two in the format of (min, max). However, it is: " + << scale_; + return false; + } + if (ratio_.size() != 2 || ratio_[1] < ratio_[0]) { + MS_LOG(ERROR) << "RandomResizedCrop: ratio variable must be in the format of (min, max). However , it is: " + << ratio_; + return false; + } + return true; +} + +std::shared_ptr RandomResizedCropOperation::Build() { + int32_t height = size_[0], width = size_[0]; + 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 409fc4e218..db84ac92bb 100644 --- a/mindspore/ccsrc/minddata/dataset/include/vision.h +++ b/mindspore/ccsrc/minddata/dataset/include/vision.h @@ -46,6 +46,7 @@ class RandomCropOperation; class RandomCropDecodeResizeOperation; class RandomHorizontalFlipOperation; class RandomPosterizeOperation; +class RandomResizedCropOperation; class RandomRotationOperation; class RandomSharpnessOperation; class RandomSolarizeOperation; @@ -227,6 +228,23 @@ std::shared_ptr RandomHorizontalFlip(float prob = /// \return Shared pointer to the current TensorOperation. std::shared_ptr RandomPosterize(const std::vector &bit_range = {4, 8}); +/// \brief Function to create a RandomResizedCrop 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 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 RandomRotation TensorOp /// \notes Rotates the image according to parameters /// \param[in] degrees A float vector size 2, representing the starting and ending degree @@ -553,6 +571,27 @@ class RandomPosterizeOperation : public TensorOperation { std::vector bit_range_; }; +class RandomResizedCropOperation : public TensorOperation { + public: + explicit RandomResizedCropOperation(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); + + ~RandomResizedCropOperation() = default; + + std::shared_ptr Build() override; + + bool 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 db5d9e078c..e771463fd0 100644 --- a/tests/ut/cpp/dataset/c_api_vision_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_test.cc @@ -228,6 +228,133 @@ TEST_F(MindDataTestPipeline, TestCutMixBatchFail3) { EXPECT_EQ(cutmix_batch_op, nullptr); } +TEST_F(MindDataTestPipeline, TestRandomResizedCropSuccess1) { + // Testing RandomResizedCrop with default values + // 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::RandomResizedCrop({5}); + EXPECT_NE(random_resized_crop, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_resized_crop}, {"image"}); + 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, 10); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropSuccess2) { + // Testing RandomResizedCrop with non-default values + // 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::RandomResizedCrop({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"}); + 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, 10); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropFail1) { + // 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::RandomResizedCrop({5, -10}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropFail2) { + // 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::RandomResizedCrop({5, 10}, {4, 3}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropFail3) { + // 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::RandomResizedCrop({5, 10}, {4, 5}, {7, 6}); + EXPECT_EQ(random_resized_crop, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomResizedCropFail4) { + // 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::RandomResizedCrop({5, 10, 20}, {4, 5}, {7, 6}); + EXPECT_EQ(random_resized_crop, nullptr); +} + TEST_F(MindDataTestPipeline, TestCutOut) { // Create an ImageFolder Dataset std::string folder_path = datasets_root_path_ + "/testPK/data/"; @@ -1303,8 +1430,8 @@ TEST_F(MindDataTestPipeline, TestCenterCropFail) { TEST_F(MindDataTestPipeline, TestNormalizeFail) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestNormalize with invalid params."; // mean value 0.0 - std::shared_ptr normalize = mindspore::dataset::api::vision::Normalize({0.0, 115.0, 100.0}, - {70.0, 68.0, 71.0}); + std::shared_ptr normalize = + mindspore::dataset::api::vision::Normalize({0.0, 115.0, 100.0}, {70.0, 68.0, 71.0}); EXPECT_EQ(normalize, nullptr); // std value at 0.0 normalize = mindspore::dataset::api::vision::Normalize({121.0, 115.0, 100.0}, {0.0, 68.0, 71.0});