From 779f0f387b672b20bf20dcc82c65fa6dfe25179f Mon Sep 17 00:00:00 2001 From: shenwei41 Date: Mon, 26 Oct 2020 19:16:21 +0800 Subject: [PATCH] Add transforms API --- .../ccsrc/minddata/dataset/api/vision.cc | 414 ++++++++++--- .../ccsrc/minddata/dataset/include/vision.h | 121 ++++ tests/ut/cpp/dataset/c_api_vision_test.cc | 574 +++++++++++++++++- 3 files changed, 1008 insertions(+), 101 deletions(-) diff --git a/mindspore/ccsrc/minddata/dataset/api/vision.cc b/mindspore/ccsrc/minddata/dataset/api/vision.cc index abb2932088..5f76b8c235 100644 --- a/mindspore/ccsrc/minddata/dataset/api/vision.cc +++ b/mindspore/ccsrc/minddata/dataset/api/vision.cc @@ -21,6 +21,7 @@ #endif // Kernel image headers (in alphabetical order) #ifndef ENABLE_ANDROID +#include "minddata/dataset/kernels/image/auto_contrast_op.h" #include "minddata/dataset/kernels/image/center_crop_op.h" #endif #include "minddata/dataset/kernels/image/crop_op.h" @@ -31,6 +32,7 @@ #include "minddata/dataset/kernels/image/decode_op.h" #ifndef ENABLE_ANDROID #include "minddata/dataset/kernels/image/hwc_to_chw_op.h" +#include "minddata/dataset/kernels/image/invert_op.h" #include "minddata/dataset/kernels/image/mixup_batch_op.h" #endif #include "minddata/dataset/kernels/image/normalize_op.h" @@ -42,12 +44,15 @@ #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_crop_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" #include "minddata/dataset/kernels/image/random_rotation_op.h" #include "minddata/dataset/kernels/image/random_sharpness_op.h" #include "minddata/dataset/kernels/image/random_solarize_op.h" #include "minddata/dataset/kernels/image/random_vertical_flip_op.h" +#include "minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h" #include "minddata/dataset/kernels/image/rescale_op.h" #endif #include "minddata/dataset/kernels/image/resize_op.h" @@ -68,6 +73,16 @@ namespace vision { // FUNCTIONS TO CREATE VISION TRANSFORM OPERATIONS // (In alphabetical order) +// Function to create AutoContrastOperation. +std::shared_ptr AutoContrast(float cutoff, std::vector ignore) { + auto op = std::make_shared(cutoff, ignore); + // Input validation + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create CenterCropOperation. std::shared_ptr CenterCrop(std::vector size) { auto op = std::make_shared(size); @@ -129,6 +144,16 @@ std::shared_ptr HWC2CHW() { return op; } +// Function to create InvertOperation. +std::shared_ptr Invert() { + auto op = std::make_shared(); + // Input validation + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create MixUpBatchOperation. std::shared_ptr MixUpBatch(float alpha) { auto op = std::make_shared(alpha); @@ -229,6 +254,18 @@ std::shared_ptr RandomCropDecodeResize(std::vec return op; } +// Function to create RandomCropWithBBoxOperation. +std::shared_ptr RandomCropWithBBox(std::vector size, std::vector padding, + bool pad_if_needed, std::vector fill_value, + BorderType padding_mode) { + auto op = std::make_shared(size, padding, pad_if_needed, fill_value, padding_mode); + // Input validation + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create RandomHorizontalFlipOperation. std::shared_ptr RandomHorizontalFlip(float prob) { auto op = std::make_shared(prob); @@ -239,6 +276,16 @@ std::shared_ptr RandomHorizontalFlip(float prob) return op; } +// Function to create RandomHorizontalFlipOperation. +std::shared_ptr RandomHorizontalFlipWithBBox(float prob) { + auto op = std::make_shared(prob); + // Input validation + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create RandomPosterizeOperation. std::shared_ptr RandomPosterize(const std::vector &bit_range) { auto op = std::make_shared(bit_range); @@ -303,6 +350,16 @@ std::shared_ptr RandomVerticalFlip(float prob) { return op; } +// Function to create RandomVerticalFlipWithBBoxOperation. +std::shared_ptr RandomVerticalFlipWithBBox(float prob) { + auto op = std::make_shared(prob); + // Input validation + if (!op->ValidateParams()) { + return nullptr; + } + return op; +} + // Function to create RescaleOperation. std::shared_ptr Rescale(float rescale, float shift) { auto op = std::make_shared(rescale, shift); @@ -368,6 +425,60 @@ std::shared_ptr UniformAugment(std::vector &fill_value) { + if (fill_value.empty() || (fill_value.size() != 1 && fill_value.size() != 3)) { + std::string err_msg = dataset_name + ": fill_value vector has incorrect size: " + std::to_string(fill_value.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + for (uint8_t single_fill_value : fill_value) { + if (single_fill_value > 255) { + std::string err_msg = + dataset_name + ": fill_value has to be between 0 and 255, got:" + std::to_string(single_fill_value); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + } + + return Status::OK(); +} + +Status ValidateVectorProbability(const std::string &dataset_name, const float &probability) { + if (probability < 0.0 || probability > 1.0) { + std::string err_msg = + dataset_name + ": probability must be between 0.0 and 1.0, got: " + std::to_string(probability); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + + return Status::OK(); +} + +Status ValidateVectorPadding(const std::string &dataset_name, const std::vector &padding) { + if (padding.empty() || padding.size() == 3 || padding.size() > 4) { + std::string err_msg = dataset_name + ": padding vector has incorrect size: " + std::to_string(padding.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + for (int32_t i = 0; i < padding.size(); ++i) { + if (padding[i] < 0) { + std::string err_msg = + dataset_name + + ": invalid padding, padding value must be greater than or equal to 0, got: " + std::to_string(padding[i]); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + if (padding[i] == INT_MAX) { + std::string err_msg = + dataset_name + ": invalid padding, padding value too large, got: " + std::to_string(padding[i]); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + } + + return Status::OK(); +} + Status ValidateVectorPositive(const std::string &dataset_name, const std::vector &size) { for (int32_t i = 0; i < size.size(); ++i) { if (size[i] <= 0) { @@ -387,6 +498,34 @@ bool CmpFloat(const float &a, const float &b, float epsilon = 0.0000000001f) { r // (In alphabetical order) #ifndef ENABLE_ANDROID + +// AutoContrastOperation +AutoContrastOperation::AutoContrastOperation(float cutoff, std::vector ignore) + : cutoff_(cutoff), ignore_(ignore) {} + +Status AutoContrastOperation::ValidateParams() { + if (cutoff_ < 0 || cutoff_ > 100) { + std::string err_msg = "AutoContrast: cutoff has to be between 0 and 100, got: " + std::to_string(cutoff_); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + + for (uint32_t single_ignore : ignore_) { + if (single_ignore > 255) { + std::string err_msg = + "AutoContrast: invalid size, ignore has to be between 0 and 255, got: " + std::to_string(single_ignore); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + } + return Status::OK(); +} + +std::shared_ptr AutoContrastOperation::Build() { + std::shared_ptr tensor_op = std::make_shared(cutoff_, ignore_); + return tensor_op; +} + // CenterCropOperation CenterCropOperation::CenterCropOperation(std::vector size) : size_(size) {} @@ -542,6 +681,11 @@ Status HwcToChwOperation::ValidateParams() { return Status::OK(); } std::shared_ptr HwcToChwOperation::Build() { return std::make_shared(); } +// InvertOperation +Status InvertOperation::ValidateParams() { return Status::OK(); } + +std::shared_ptr InvertOperation::Build() { return std::make_shared(); } + // MixUpOperation MixUpBatchOperation::MixUpBatchOperation(float alpha) : alpha_(alpha) {} @@ -598,37 +742,9 @@ PadOperation::PadOperation(std::vector padding, std::vector fi Status PadOperation::ValidateParams() { // padding - if (padding_.empty() || padding_.size() == 3 || padding_.size() > 4) { - std::string err_msg = "Pad: padding vector has incorrect size: " + std::to_string(padding_.size()); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < padding_.size(); ++i) { - if (padding_[i] < 0) { - std::string err_msg = - "Pad: invalid padding, padding value must be greater than or equal to 0, got: " + std::to_string(padding_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (padding_[i] == INT_MAX) { - std::string err_msg = "Pad: invalid padding, padding value too large, got: " + std::to_string(padding_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } + RETURN_IF_NOT_OK(ValidateVectorPadding("Pad", padding_)); // fill_value - if (fill_value_.empty() || (fill_value_.size() != 1 && fill_value_.size() != 3)) { - std::string err_msg = "Pad: fill_value vector has incorrect size: " + std::to_string(fill_value_.size()); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < fill_value_.size(); ++i) { - if (fill_value_[i] < 0 || fill_value_[i] > 255) { - std::string err_msg = "Pad: fill_value has to be between 0 and 255, got:" + std::to_string(fill_value_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } + RETURN_IF_NOT_OK(ValidateVectorFillvalue("Pad", fill_value_)); return Status::OK(); } @@ -975,37 +1091,9 @@ Status RandomCropOperation::ValidateParams() { } RETURN_IF_NOT_OK(ValidateVectorPositive("RandomCrop", size_)); // padding - if (padding_.empty() || padding_.size() != 4) { - std::string err_msg = "RandomCrop: padding vector has incorrect size: " + std::to_string(padding_.size()); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < padding_.size(); ++i) { - if (padding_[i] < 0) { - std::string err_msg = "RandomCrop: invalid padding, padding value must be greater than or equal to 0, got: " + - std::to_string(padding_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - if (padding_[i] == INT_MAX) { - std::string err_msg = "RandomCrop: invalid padding, padding value too large, got: " + std::to_string(padding_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } + RETURN_IF_NOT_OK(ValidateVectorPadding("RandomCrop", padding_)); // fill_value - if (fill_value_.empty() || fill_value_.size() != 3) { - std::string err_msg = "RandomCrop: fill_value vector has incorrect size: " + std::to_string(fill_value_.size()); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < fill_value_.size(); ++i) { - if (fill_value_[i] < 0 || fill_value_[i] > 255) { - std::string err_msg = "RandomCrop: fill_value has to be between 0 and 255, got:" + std::to_string(fill_value_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } + RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomCrop", fill_value_)); return Status::OK(); } @@ -1018,14 +1106,37 @@ std::shared_ptr RandomCropOperation::Build() { crop_width = size_[1]; } - int32_t pad_top = padding_[0]; - int32_t pad_bottom = padding_[1]; - int32_t pad_left = padding_[2]; - int32_t pad_right = padding_[3]; + int32_t pad_top, pad_bottom, pad_left, pad_right; + switch (padding_.size()) { + case 1: + pad_left = padding_[0]; + pad_top = padding_[0]; + pad_right = padding_[0]; + pad_bottom = padding_[0]; + break; + case 2: + pad_left = padding_[0]; + pad_top = padding_[1]; + pad_right = padding_[0]; + pad_bottom = padding_[1]; + break; + default: + pad_left = padding_[0]; + pad_top = padding_[1]; + pad_right = padding_[2]; + pad_bottom = padding_[3]; + } + + uint8_t fill_r, fill_g, fill_b; + fill_r = fill_value_[0]; + fill_g = fill_value_[0]; + fill_b = fill_value_[0]; - uint8_t fill_r = fill_value_[0]; - uint8_t fill_g = fill_value_[1]; - uint8_t fill_b = fill_value_[2]; + if (fill_value_.size() == 3) { + fill_r = fill_value_[0]; + fill_g = fill_value_[1]; + fill_b = fill_value_[2]; + } auto tensor_op = std::make_shared(crop_height, crop_width, pad_top, pad_bottom, pad_left, pad_right, padding_mode_, pad_if_needed_, fill_r, fill_g, fill_b); @@ -1125,15 +1236,83 @@ std::shared_ptr RandomCropDecodeResizeOperation::Build() { return tensor_op; } -// RandomHorizontalFlipOperation -RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {} +// RandomCropWithBBoxOperation +RandomCropWithBBoxOperation::RandomCropWithBBoxOperation(std::vector size, std::vector padding, + bool pad_if_needed, std::vector fill_value, + BorderType padding_mode) + : size_(size), + padding_(padding), + pad_if_needed_(pad_if_needed), + fill_value_(fill_value), + padding_mode_(padding_mode) {} -Status RandomHorizontalFlipOperation::ValidateParams() { - if (probability_ < 0.0 || probability_ > 1.0) { - std::string err_msg = "RandomHorizontalFlip: probability must be between 0.0 and 1.0."; +Status RandomCropWithBBoxOperation::ValidateParams() { + // size + if (size_.empty() || size_.size() > 2) { + std::string err_msg = "RandomCropWithBBox: size must be a vector of one or two values"; MS_LOG(ERROR) << err_msg; RETURN_STATUS_SYNTAX_ERROR(err_msg); } + RETURN_IF_NOT_OK(ValidateVectorPositive("RandomCropWithBBox", size_)); + // padding + RETURN_IF_NOT_OK(ValidateVectorPadding("RandomCropWithBBox", padding_)); + // fill_value + RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomCropWithBBox", fill_value_)); + return Status::OK(); +} + +std::shared_ptr RandomCropWithBBoxOperation::Build() { + int32_t crop_height = size_[0]; + int32_t crop_width = size_[0]; + + // User has specified the crop_width value. + if (size_.size() == 2) { + crop_width = size_[1]; + } + + int32_t pad_top, pad_bottom, pad_left, pad_right; + switch (padding_.size()) { + case 1: + pad_left = padding_[0]; + pad_top = padding_[0]; + pad_right = padding_[0]; + pad_bottom = padding_[0]; + break; + case 2: + pad_left = padding_[0]; + pad_top = padding_[1]; + pad_right = padding_[0]; + pad_bottom = padding_[1]; + break; + default: + pad_left = padding_[0]; + pad_top = padding_[1]; + pad_right = padding_[2]; + pad_bottom = padding_[3]; + } + + uint8_t fill_r, fill_g, fill_b; + fill_r = fill_value_[0]; + fill_g = fill_value_[0]; + fill_b = fill_value_[0]; + + if (fill_value_.size() == 3) { + fill_r = fill_value_[0]; + fill_g = fill_value_[1]; + fill_b = fill_value_[2]; + } + + auto tensor_op = + std::make_shared(crop_height, crop_width, pad_top, pad_bottom, pad_left, pad_right, + padding_mode_, pad_if_needed_, fill_r, fill_g, fill_b); + return tensor_op; +} + +// RandomHorizontalFlipOperation +RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {} + +Status RandomHorizontalFlipOperation::ValidateParams() { + RETURN_IF_NOT_OK(ValidateVectorProbability("RandomHorizontalFlip", probability_)); return Status::OK(); } @@ -1143,6 +1322,22 @@ std::shared_ptr RandomHorizontalFlipOperation::Build() { return tensor_op; } +// RandomHorizontalFlipWithBBoxOperation +RandomHorizontalFlipWithBBoxOperation::RandomHorizontalFlipWithBBoxOperation(float probability) + : probability_(probability) {} + +Status RandomHorizontalFlipWithBBoxOperation::ValidateParams() { + RETURN_IF_NOT_OK(ValidateVectorProbability("RandomHorizontalFlipWithBBox", probability_)); + + return Status::OK(); +} + +std::shared_ptr RandomHorizontalFlipWithBBoxOperation::Build() { + std::shared_ptr tensor_op = + std::make_shared(probability_); + return tensor_op; +} + // RandomPosterizeOperation RandomPosterizeOperation::RandomPosterizeOperation(const std::vector &bit_range) : bit_range_(bit_range) {} @@ -1253,16 +1448,21 @@ RandomRotationOperation::RandomRotationOperation(std::vector degrees, Int Status RandomRotationOperation::ValidateParams() { // degrees - if (degrees_.size() != 2) { + if (degrees_.size() != 2 && degrees_.size() != 1) { std::string err_msg = - "RandomRotation: degrees must be a vector of two values, got: " + std::to_string(degrees_.size()); - MS_LOG(ERROR) << "RandomRotation: degrees must be a vector of two values, got: " << degrees_; + "RandomRotation: degrees must be a vector of one or two values, got: " + std::to_string(degrees_.size()); + MS_LOG(ERROR) << "RandomRotation: degrees must be a vector of one or two values, got: " << degrees_; RETURN_STATUS_SYNTAX_ERROR(err_msg); } - if (degrees_[1] < degrees_[0]) { + if ((degrees_[1] < degrees_[0]) && (degrees_.size() == 2)) { std::string err_msg = "RandomRotation: degrees must be in the format of (min, max)."; MS_LOG(ERROR) << "RandomRotation: degrees must be in the format of (min, max), got: " << degrees_; RETURN_STATUS_SYNTAX_ERROR(err_msg); + } else if ((degrees_[0] <= 0) && degrees_.size() == 1) { + std::string err_msg = "RandomRotation: if the degress has one element, the value must be greater than 0."; + MS_LOG(ERROR) << "RandomRotation: if the degress has one element, the value must be greater than 0, got: " + << degrees_; + RETURN_STATUS_SYNTAX_ERROR(err_msg); } // center if (center_.empty() || center_.size() != 2) { @@ -1272,27 +1472,33 @@ Status RandomRotationOperation::ValidateParams() { RETURN_STATUS_SYNTAX_ERROR(err_msg); } // fill_value - if (fill_value_.empty() || fill_value_.size() != 3) { - std::string err_msg = - "RandomRotation: fill_value must be a vector of two values, got: " + std::to_string(fill_value_.size()); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - for (int32_t i = 0; i < fill_value_.size(); ++i) { - if (fill_value_[i] < 0 || fill_value_[i] > 255) { - std::string err_msg = - "RandomRotation: fill_value has to be between 0 and 255, got: " + std::to_string(fill_value_[i]); - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } - } + RETURN_IF_NOT_OK(ValidateVectorFillvalue("RandomRotation", fill_value_)); return Status::OK(); } std::shared_ptr RandomRotationOperation::Build() { - std::shared_ptr tensor_op = - std::make_shared(degrees_[0], degrees_[1], center_[0], center_[1], interpolation_mode_, expand_, - fill_value_[0], fill_value_[1], fill_value_[2]); + float start_degree, end_degree; + if (degrees_.size() == 1) { + start_degree = -degrees_[0]; + end_degree = degrees_[0]; + } else if (degrees_.size() == 2) { + start_degree = degrees_[0]; + end_degree = degrees_[1]; + } + + uint8_t fill_r, fill_g, fill_b; + fill_r = fill_value_[0]; + fill_g = fill_value_[0]; + fill_b = fill_value_[0]; + + if (fill_value_.size() == 3) { + fill_r = fill_value_[0]; + fill_g = fill_value_[1]; + fill_b = fill_value_[2]; + } + + std::shared_ptr tensor_op = std::make_shared( + start_degree, end_degree, center_[0], center_[1], interpolation_mode_, expand_, fill_r, fill_g, fill_b); return tensor_op; } @@ -1355,11 +1561,7 @@ std::shared_ptr RandomSolarizeOperation::Build() { RandomVerticalFlipOperation::RandomVerticalFlipOperation(float probability) : probability_(probability) {} Status RandomVerticalFlipOperation::ValidateParams() { - if (probability_ < 0.0 || probability_ > 1.0) { - std::string err_msg = "RandomVerticalFlip: probability must be between 0.0 and 1.0."; - MS_LOG(ERROR) << err_msg; - RETURN_STATUS_SYNTAX_ERROR(err_msg); - } + RETURN_IF_NOT_OK(ValidateVectorProbability("RandomVerticalFlip", probability_)); return Status::OK(); } @@ -1369,6 +1571,22 @@ std::shared_ptr RandomVerticalFlipOperation::Build() { return tensor_op; } +// RandomVerticalFlipWithBBoxOperation +RandomVerticalFlipWithBBoxOperation::RandomVerticalFlipWithBBoxOperation(float probability) + : probability_(probability) {} + +Status RandomVerticalFlipWithBBoxOperation::ValidateParams() { + RETURN_IF_NOT_OK(ValidateVectorProbability("RandomVerticalFlipWithBBox", probability_)); + + return Status::OK(); +} + +std::shared_ptr RandomVerticalFlipWithBBoxOperation::Build() { + std::shared_ptr tensor_op = + std::make_shared(probability_); + return tensor_op; +} + // RescaleOperation RescaleOperation::RescaleOperation(float rescale, float shift) : rescale_(rescale), shift_(shift) {} diff --git a/mindspore/ccsrc/minddata/dataset/include/vision.h b/mindspore/ccsrc/minddata/dataset/include/vision.h index f757398734..831ea6e5a9 100644 --- a/mindspore/ccsrc/minddata/dataset/include/vision.h +++ b/mindspore/ccsrc/minddata/dataset/include/vision.h @@ -32,6 +32,7 @@ namespace vision { // Transform Op classes (in alphabetical order) #ifndef ENABLE_ANDROID +class AutoContrastOperation; class CenterCropOperation; #endif class CropOperation; @@ -42,6 +43,7 @@ class CutOutOperation; class DecodeOperation; #ifndef ENABLE_ANDROID class HwcToChwOperation; +class InvertOperation; class MixUpBatchOperation; #endif class NormalizeOperation; @@ -52,13 +54,16 @@ class RandomColorOperation; class RandomColorAdjustOperation; class RandomCropOperation; class RandomCropDecodeResizeOperation; +class RandomCropWithBBoxOperation; class RandomHorizontalFlipOperation; +class RandomHorizontalFlipWithBBoxOperation; class RandomPosterizeOperation; class RandomResizedCropOperation; class RandomRotationOperation; class RandomSharpnessOperation; class RandomSolarizeOperation; class RandomVerticalFlipOperation; +class RandomVerticalFlipWithBBoxOperation; class RescaleOperation; #endif class ResizeOperation; @@ -68,6 +73,13 @@ class RgbaToRgbOperation; class SwapRedBlueOperation; class UniformAugOperation; +/// \brief Function to create a AutoContrast TensorOperation. +/// \notes Apply automatic contrast on input image. +/// \param[in] cutoff Percent of pixels to cut off from the histogram, the valid range of cutoff value is 0 to 100. +/// \param[in] ignore Pixel values to ignore. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr AutoContrast(float cutoff = 0.0, std::vector ignore = {}); + /// \brief Function to create a CenterCrop TensorOperation. /// \notes Crops the input image at the center to the given size. /// \param[in] size A vector representing the output size of the cropped image. @@ -113,6 +125,11 @@ std::shared_ptr Decode(bool rgb = true); /// \return Shared pointer to the current TensorOperation. std::shared_ptr HWC2CHW(); +/// \brief Function to create a Invert TensorOperation. +/// \notes Apply invert on input image in RGB mode. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr Invert(); + /// \brief Function to create a MixUpBatch TensorOperation. /// \notes Apply MixUp transformation on an input batch of images and labels. The labels must be in /// one-hot format and Batch must be called before calling this function. @@ -232,12 +249,38 @@ std::shared_ptr RandomCropDecodeResize( 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 RandomCropWithBBox TensorOperation. +/// \Crop the input image at a random location and adjust bounding boxes accordingly. +/// \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] padding A vector with the value of pixels to pad the image. If 4 values are provided, +/// it pads the left, top, right and bottom respectively. +/// \param[in] pad_if_needed A boolean whether to pad the image if either side is smaller than +/// the given output size. +/// \param[in] fill_value A vector representing the pixel intensity of the borders, it is used to +/// fill R, G, B channels respectively. +/// \param[in] padding_mode The method of padding (default=BorderType::kConstant).It can be any of +/// [BorderType::kConstant, BorderType::kEdge, BorderType::kReflect, BorderType::kSymmetric]. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr RandomCropWithBBox(std::vector size, + std::vector padding = {0, 0, 0, 0}, + bool pad_if_needed = false, + std::vector fill_value = {0, 0, 0}, + BorderType padding_mode = BorderType::kConstant); + /// \brief Function to create a RandomHorizontalFlip TensorOperation. /// \notes Tensor operation to perform random horizontal flip. /// \param[in] prob A float representing the probability of flip. /// \return Shared pointer to the current TensorOperation. std::shared_ptr RandomHorizontalFlip(float prob = 0.5); +/// \brief Function to create a RandomHorizontalFlipWithBBox TensorOperation. +/// \notes Flip the input image horizontally, randomly with a given probability and adjust bounding boxes accordingly. +/// \param[in] prob A float representing the probability of flip. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr RandomHorizontalFlipWithBBox(float prob = 0.5); + /// \brief Function to create a RandomPosterize TensorOperation. /// \notes Tensor operation to perform random posterize. /// \param[in] bit_range - uint8_t vector representing the minimum and maximum bit in range. (Default={4, 8}) @@ -293,6 +336,12 @@ std::shared_ptr RandomSolarize(std::vector thr /// \return Shared pointer to the current TensorOperation. std::shared_ptr RandomVerticalFlip(float prob = 0.5); +/// \brief Function to create a RandomVerticalFlipWithBBox TensorOperation. +/// \notes Flip the input image vertically, randomly with a given probability and adjust bounding boxes accordingly. +/// \param[in] prob A float representing the probability of flip. +/// \return Shared pointer to the current TensorOperation. +std::shared_ptr RandomVerticalFlipWithBBox(float prob = 0.5); + /// \brief Function to create a RescaleOperation TensorOperation. /// \notes Tensor operation to rescale the input image. /// \param[in] rescale Rescale factor. @@ -337,6 +386,21 @@ std::shared_ptr UniformAugment(std::vector ignore = {}); + + ~AutoContrastOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + private: + float cutoff_; + std::vector ignore_; +}; + class CenterCropOperation : public TensorOperation { public: explicit CenterCropOperation(std::vector size); @@ -423,6 +487,15 @@ class HwcToChwOperation : public TensorOperation { Status ValidateParams() override; }; +class InvertOperation : public TensorOperation { + public: + ~InvertOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; +}; + class MixUpBatchOperation : public TensorOperation { public: explicit MixUpBatchOperation(float alpha = 1); @@ -566,6 +639,26 @@ class RandomCropDecodeResizeOperation : public TensorOperation { int32_t max_attempts_; }; +class RandomCropWithBBoxOperation : public TensorOperation { + public: + RandomCropWithBBoxOperation(std::vector size, std::vector padding = {0, 0, 0, 0}, + bool pad_if_needed = false, std::vector fill_value = {0, 0, 0}, + BorderType padding_mode = BorderType::kConstant); + + ~RandomCropWithBBoxOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + private: + std::vector size_; + std::vector padding_; + bool pad_if_needed_; + std::vector fill_value_; + BorderType padding_mode_; +}; + class RandomHorizontalFlipOperation : public TensorOperation { public: explicit RandomHorizontalFlipOperation(float probability = 0.5); @@ -580,6 +673,20 @@ class RandomHorizontalFlipOperation : public TensorOperation { float probability_; }; +class RandomHorizontalFlipWithBBoxOperation : public TensorOperation { + public: + explicit RandomHorizontalFlipWithBBoxOperation(float probability = 0.5); + + ~RandomHorizontalFlipWithBBoxOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + private: + float probability_; +}; + class RandomPosterizeOperation : public TensorOperation { public: explicit RandomPosterizeOperation(const std::vector &bit_range = {4, 8}); @@ -676,6 +783,20 @@ class RandomVerticalFlipOperation : public TensorOperation { float probability_; }; +class RandomVerticalFlipWithBBoxOperation : public TensorOperation { + public: + explicit RandomVerticalFlipWithBBoxOperation(float probability = 0.5); + + ~RandomVerticalFlipWithBBoxOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + private: + float probability_; +}; + class RescaleOperation : public TensorOperation { public: explicit RescaleOperation(float rescale, float shift); diff --git a/tests/ut/cpp/dataset/c_api_vision_test.cc b/tests/ut/cpp/dataset/c_api_vision_test.cc index be3e8ddb1f..9642ac8652 100644 --- a/tests/ut/cpp/dataset/c_api_vision_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_test.cc @@ -33,9 +33,11 @@ #include "minddata/dataset/engine/ir/datasetops/source/cifar10_node.h" #include "minddata/dataset/engine/ir/datasetops/source/image_folder_node.h" #include "minddata/dataset/engine/ir/datasetops/source/mnist_node.h" +#include "minddata/dataset/engine/ir/datasetops/source/voc_node.h" using namespace mindspore::dataset::api; using mindspore::dataset::BorderType; +using mindspore::dataset::InterpolationMode; using mindspore::dataset::Tensor; class MindDataTestPipeline : public UT::DatasetOpTesting { @@ -44,6 +46,114 @@ class MindDataTestPipeline : public UT::DatasetOpTesting { // Tests for vision ops (in alphabetical order) +TEST_F(MindDataTestPipeline, TestAutoContrastSuccess1) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestAutoContrastSuccess1."; + + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, RandomSampler(false, 5)); + EXPECT_NE(ds, nullptr); + + // Create a Repeat operation on ds + int32_t repeat_num = 3; + ds = ds->Repeat(repeat_num); + EXPECT_NE(ds, nullptr); + + // Create auto contrast object with default values + std::shared_ptr auto_contrast = vision::AutoContrast(); + EXPECT_NE(auto_contrast, nullptr); + + // Create a Map operation on ds + ds = ds->Map({auto_contrast}); + EXPECT_NE(ds, nullptr); + + // Create a Batch operation on ds + int32_t batch_size = 1; + ds = ds->Batch(batch_size); + 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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 15); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestAutoContrastSuccess2) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestAutoContrastSuccess2."; + + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, RandomSampler(false, 5)); + EXPECT_NE(ds, nullptr); + + // Create a Repeat operation on ds + int32_t repeat_num = 3; + ds = ds->Repeat(repeat_num); + EXPECT_NE(ds, nullptr); + + // Create auto contrast object + std::shared_ptr auto_contrast = vision::AutoContrast(10, {10, 20}); + EXPECT_NE(auto_contrast, nullptr); + + // Create a Map operation on ds + ds = ds->Map({auto_contrast}); + EXPECT_NE(ds, nullptr); + + // Create a Batch operation on ds + int32_t batch_size = 1; + ds = ds->Batch(batch_size); + 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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 15); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestAutoContrastFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestAutoContrastFail with invalid params."; + // Testing invalid cutoff < 0 + std::shared_ptr auto_contrast1 = vision::AutoContrast(-1.0); + EXPECT_EQ(auto_contrast1, nullptr); + // Testing invalid cutoff > 100 + std::shared_ptr auto_contrast2 = vision::AutoContrast(110.0, {10, 20}); + EXPECT_EQ(auto_contrast2, nullptr); +} + TEST_F(MindDataTestPipeline, TestCenterCrop) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestCenterCrop with single integer input."; @@ -535,6 +645,44 @@ TEST_F(MindDataTestPipeline, TestHwcToChw) { iter->Stop(); } +TEST_F(MindDataTestPipeline, TestInvert) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestInvert."; + + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, RandomSampler(false, 20)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr invert_op = vision::Invert(); + EXPECT_NE(invert_op, nullptr); + + // Create a Map operation on ds + ds = ds->Map({invert_op}); + 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(); + iter->GetNextRow(&row); + } + EXPECT_EQ(i, 20); + + // Manually terminate the pipeline + iter->Stop(); +} + TEST_F(MindDataTestPipeline, TestMixUpBatchFail1) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestMixUpBatchFail1 with negative alpha parameter."; @@ -947,7 +1095,7 @@ TEST_F(MindDataTestPipeline, TestRandomColor) { // Failure case: Set invalid negative lower bound std::shared_ptr random_color_op_4 = vision::RandomColor(-0.5, 0.5); - EXPECT_EQ(random_color_op_2, nullptr); + EXPECT_EQ(random_color_op_4, nullptr); // Create a Map operation on ds ds = ds->Map({random_color_op_1, random_color_op_3}); @@ -1050,6 +1198,182 @@ TEST_F(MindDataTestPipeline, TestRandomColorAdjust) { iter->Stop(); } +TEST_F(MindDataTestPipeline, TestRandomCropSuccess) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropSuccess."; + // 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, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // Testing siez of size vector is 1 + std::shared_ptr random_crop = vision::RandomCrop({20}); + EXPECT_NE(random_crop, nullptr); + + // Testing siez of size vector is 2 + std::shared_ptr random_crop1 = vision::RandomCrop({20, 20}); + EXPECT_NE(random_crop1, nullptr); + + // Testing siez of paddiing vector is 1 + std::shared_ptr random_crop2 = vision::RandomCrop({20, 20}, {10}); + EXPECT_NE(random_crop2, nullptr); + + // Testing siez of paddiing vector is 2 + std::shared_ptr random_crop3 = vision::RandomCrop({20, 20}, {10, 20}); + EXPECT_NE(random_crop3, nullptr); + + // Testing siez of paddiing vector is 2 + std::shared_ptr random_crop4 = vision::RandomCrop({20, 20}, {10, 10, 10, 10}); + EXPECT_NE(random_crop4, nullptr); + + // Testing siez of fill_value vector is 1 + std::shared_ptr random_crop5 = vision::RandomCrop({20, 20}, {10, 10, 10, 10}, false, {5}); + EXPECT_NE(random_crop5, nullptr); + + // Testing siez of fill_value vector is 3 + std::shared_ptr random_crop6 = vision::RandomCrop({20, 20}, {10, 10, 10, 10}, false, {4, 4, 4}); + EXPECT_NE(random_crop6, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_crop, random_crop1, random_crop2, random_crop3, random_crop4, random_crop5, random_crop6}); + 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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 10); + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomCropFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropFail with invalid parameters."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // Testing the size parameter is negative. + std::shared_ptr random_crop = vision::RandomCrop({-28, 28}); + EXPECT_EQ(random_crop, nullptr); + // Testing the size parameter is None. + std::shared_ptr random_crop1 = vision::RandomCrop({}); + EXPECT_EQ(random_crop1, nullptr); + // Testing the size of size vector is 3. + std::shared_ptr random_crop2 = vision::RandomCrop({28, 28, 28}); + EXPECT_EQ(random_crop2, nullptr); + // Testing the padding parameter is negative. + std::shared_ptr random_crop3 = vision::RandomCrop({28, 28}, {-5}); + EXPECT_EQ(random_crop3, nullptr); + // Testing the size of padding vector is empty. + std::shared_ptr random_crop4 = vision::RandomCrop({28, 28}, {}); + EXPECT_EQ(random_crop4, nullptr); + // Testing the size of padding vector is 3. + std::shared_ptr random_crop5 = vision::RandomCrop({28, 28}, {5, 5, 5}); + EXPECT_EQ(random_crop5, nullptr); + // Testing the size of padding vector is 5. + std::shared_ptr random_crop6 = vision::RandomCrop({28, 28}, {5, 5, 5, 5, 5}); + EXPECT_EQ(random_crop6, nullptr); + // Testing the size of fill_value vector is empty. + std::shared_ptr random_crop7 = vision::RandomCrop({28, 28}, {0, 0, 0, 0}, false, {}); + EXPECT_EQ(random_crop7, nullptr); + // Testing the size of fill_value vector is 2. + std::shared_ptr random_crop8 = vision::RandomCrop({28, 28}, {0, 0, 0, 0}, false, {0, 0}); + EXPECT_EQ(random_crop8, nullptr); + // Testing the size of fill_value vector is 4. + std::shared_ptr random_crop9 = vision::RandomCrop({28, 28}, {0, 0, 0, 0}, false, {0, 0, 0, 0}); + EXPECT_EQ(random_crop9, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRandomCropWithBboxSuccess) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropWithBboxSuccess."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_crop = mindspore::dataset::api::vision::RandomCropWithBBox({128, 128}); + EXPECT_NE(random_crop, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_crop}, {"image", "bbox"}, {"image", "bbox"}, {"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], 128); + EXPECT_EQ(image->shape()[1], 128); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 3); + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomCropWithBboxFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropWithBboxFail with invalid parameters."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // The size parameter is negative. + std::shared_ptr random_crop = vision::RandomCropWithBBox({-10}); + EXPECT_EQ(random_crop, nullptr); + // The parameter in the padding vector is negative. + std::shared_ptr random_crop1 = vision::RandomCropWithBBox({10, 10}, {-2, 2, 2, 2}); + EXPECT_EQ(random_crop1, nullptr); + // The size container is empty. + std::shared_ptr random_crop2 = vision::RandomCropWithBBox({}); + EXPECT_EQ(random_crop2, nullptr); + // The size of the size container is too large. + std::shared_ptr random_crop3 = vision::RandomCropWithBBox({10, 10, 10}); + EXPECT_EQ(random_crop3, nullptr); + // The padding container is empty. + std::shared_ptr random_crop4 = vision::RandomCropWithBBox({10, 10}, {}); + EXPECT_EQ(random_crop4, nullptr); + // The size of the padding container is too large. + std::shared_ptr random_crop5 = vision::RandomCropWithBBox({10, 10}, {5, 5, 5, 5, 5}); + EXPECT_EQ(random_crop5, nullptr); + // The fill_value container is empty. + std::shared_ptr random_crop6 = vision::RandomCropWithBBox({10, 10}, {5, 5, 5, 5}, false, {}); + EXPECT_EQ(random_crop6, nullptr); + // The size of the fill_value container is too large. + std::shared_ptr random_crop7 = + vision::RandomCropWithBBox({10, 10}, {5, 5, 5, 5}, false, {3, 3, 3, 3}); + EXPECT_EQ(random_crop7, nullptr); +} + TEST_F(MindDataTestPipeline, DISABLED_TestRandomHorizontalFlipFail) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomHorizontalFlipFail with invalid parameters."; @@ -1062,6 +1386,59 @@ TEST_F(MindDataTestPipeline, DISABLED_TestRandomHorizontalFlipFail) { EXPECT_EQ(random_horizontal_flip_op, nullptr); } +TEST_F(MindDataTestPipeline, TestRandomHorizontalFlipWithBBoxSuccess) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomHorizontalFlipWithBBoxSuccess."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_horizontal_flip_op = vision::RandomHorizontalFlipWithBBox(0.5); + EXPECT_NE(random_horizontal_flip_op, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_horizontal_flip_op}, {"image", "bbox"}, {"image", "bbox"}, {"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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 3); + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomHorizontalFlipWithBBoxFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomHorizontalFlipWithBBoxFail with invalid parameters."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // Incorrect prob parameter. + std::shared_ptr random_horizontal_flip_op = vision::RandomHorizontalFlipWithBBox(-1.0); + EXPECT_EQ(random_horizontal_flip_op, nullptr); + // Incorrect prob parameter. + std::shared_ptr random_horizontal_flip_op1 = vision::RandomHorizontalFlipWithBBox(2.0); + EXPECT_EQ(random_horizontal_flip_op1, nullptr); +} + TEST_F(MindDataTestPipeline, TestRandomHorizontalAndVerticalFlip) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomHorizontalAndVerticalFlip for horizontal and vertical flips."; @@ -1371,11 +1748,23 @@ TEST_F(MindDataTestPipeline, TestRandomRotation) { EXPECT_NE(ds, nullptr); // Create objects for the tensor ops - std::shared_ptr random_rotation_op = vision::RandomRotation({-180, 180}); + // Testing the size of degrees is 1 + std::shared_ptr random_rotation_op = vision::RandomRotation({180}); EXPECT_NE(random_rotation_op, nullptr); + // Testing the size of degrees is 2 + std::shared_ptr random_rotation_op1 = vision::RandomRotation({-180, 180}); + EXPECT_NE(random_rotation_op1, nullptr); + // Testing the size of fill_value is 1 + std::shared_ptr random_rotation_op2 = + vision::RandomRotation({180}, InterpolationMode::kNearestNeighbour, false, {-1, -1}, {2}); + EXPECT_NE(random_rotation_op2, nullptr); + // Testing the size of fill_value is 3 + std::shared_ptr random_rotation_op3 = + vision::RandomRotation({180}, InterpolationMode::kNearestNeighbour, false, {-1, -1}, {2, 2, 2}); + EXPECT_NE(random_rotation_op3, nullptr); // Create a Map operation on ds - ds = ds->Map({random_rotation_op}); + ds = ds->Map({random_rotation_op, random_rotation_op1, random_rotation_op2, random_rotation_op3}); EXPECT_NE(ds, nullptr); // Create a Batch operation on ds @@ -1406,6 +1795,42 @@ TEST_F(MindDataTestPipeline, TestRandomRotation) { iter->Stop(); } +TEST_F(MindDataTestPipeline, TestRandomRotationFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomRotationFail with invalid parameters."; + + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, RandomSampler(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // Testing the size of degrees vector is 0 + std::shared_ptr random_rotation_op = vision::RandomRotation({}); + EXPECT_EQ(random_rotation_op, nullptr); + // Testing the size of degrees vector is 3 + std::shared_ptr random_rotation_op1 = vision::RandomRotation({-50.0, 50.0, 100.0}); + EXPECT_EQ(random_rotation_op1, nullptr); + // Test the case where the first column value of degrees is greater than the second column value + std::shared_ptr random_rotation_op2 = vision::RandomRotation({50.0, -50.0}); + EXPECT_EQ(random_rotation_op2, nullptr); + // Testing the size of center vector is 1 + std::shared_ptr random_rotation_op3 = + vision::RandomRotation({-50.0, 50.0}, InterpolationMode::kNearestNeighbour, false, {-1.0}); + EXPECT_EQ(random_rotation_op3, nullptr); + // Testing the size of center vector is 3 + std::shared_ptr random_rotation_op4 = + vision::RandomRotation({-50.0, 50.0}, InterpolationMode::kNearestNeighbour, false, {-1.0, -1.0, -1.0}); + EXPECT_EQ(random_rotation_op4, nullptr); + // Testing the size of fill_value vector is 2 + std::shared_ptr random_rotation_op5 = + vision::RandomRotation({-50.0, 50.0}, InterpolationMode::kNearestNeighbour, false, {-1.0, -1.0}, {2, 2}); + EXPECT_EQ(random_rotation_op5, nullptr); + // Testing the size of fill_value vector is 4 + std::shared_ptr random_rotation_op6 = + vision::RandomRotation({-50.0, 50.0}, InterpolationMode::kNearestNeighbour, false, {-1.0, -1.0}, {2, 2, 2, 2}); + EXPECT_EQ(random_rotation_op6, nullptr); +} + TEST_F(MindDataTestPipeline, TestRandomSharpness) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomSharpness."; @@ -1592,6 +2017,59 @@ TEST_F(MindDataTestPipeline, TestResizeFail) { EXPECT_EQ(resize_op, nullptr); } +TEST_F(MindDataTestPipeline, TestRandomVerticalFlipWithBBoxSuccess) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomVerticalFlipWithBBoxSuccess."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr random_vertical_flip_op = vision::RandomVerticalFlipWithBBox(0.4); + EXPECT_NE(random_vertical_flip_op, nullptr); + + // Create a Map operation on ds + ds = ds->Map({random_vertical_flip_op}, {"image", "bbox"}, {"image", "bbox"}, {"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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 3); + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRandomVerticalFlipWithBBoxFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomVerticalFlipWithBBoxFail with invalid parameters."; + // 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, 3)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + // Incorrect prob parameter. + std::shared_ptr random_vertical_flip_op = vision::RandomVerticalFlipWithBBox(-0.5); + EXPECT_EQ(random_vertical_flip_op, nullptr); + // Incorrect prob parameter. + std::shared_ptr random_vertical_flip_op1 = vision::RandomVerticalFlipWithBBox(3.0); + EXPECT_EQ(random_vertical_flip_op1, nullptr); +} + TEST_F(MindDataTestPipeline, TestResize1) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestResize1 with single integer input."; // Create an ImageFolder Dataset @@ -1640,6 +2118,96 @@ TEST_F(MindDataTestPipeline, TestResize1) { iter->Stop(); } +TEST_F(MindDataTestPipeline, TestRescaleSucess1) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRescaleSucess1."; + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, SequentialSampler(0, 1)); + 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); + + auto image = row["image"]; + + // Create objects for the tensor ops + std::shared_ptr rescale = mindspore::dataset::api::vision::Rescale(1.0, 0.0); + EXPECT_NE(rescale, nullptr); + + // Convert to the same type + std::shared_ptr type_cast = transforms::TypeCast("uint8"); + EXPECT_NE(type_cast, nullptr); + + ds = ds->Map({rescale, type_cast}, {"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 iter1 = ds->CreateIterator(); + EXPECT_NE(iter1, nullptr); + + // Iterate the dataset and get each row1 + std::unordered_map> row1; + iter1->GetNextRow(&row1); + + auto image1 = row1["image"]; + + EXPECT_EQ(*image, *image1); + + // Manually terminate the pipeline + iter1->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRescaleSucess2) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRescaleSucess2 with different params."; + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, RandomSampler(false, 1)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr rescale = mindspore::dataset::api::vision::Rescale(1.0 / 255, 1.0); + EXPECT_NE(rescale, nullptr); + + ds = ds->Map({rescale}, {"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(); + iter->GetNextRow(&row); + } + + EXPECT_EQ(i, 1); + + // Manually terminate the pipeline + iter->Stop(); +} + +TEST_F(MindDataTestPipeline, TestRescaleFail) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRescaleFail with invalid params."; + // incorrect negative rescale parameter + std::shared_ptr rescale = mindspore::dataset::api::vision::Rescale(-1.0, 0.0); + EXPECT_EQ(rescale, nullptr); +} + TEST_F(MindDataTestPipeline, DISABLED_TestUniformAugmentFail1) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestUniformAugmentFail1 with invalid zero num_ops parameter.";