diff --git a/mindspore/ccsrc/minddata/dataset/api/python/bindings/dataset/kernels/ir/image/bindings.cc b/mindspore/ccsrc/minddata/dataset/api/python/bindings/dataset/kernels/ir/image/bindings.cc index 85e2f94e54b..8b322641d7b 100644 --- a/mindspore/ccsrc/minddata/dataset/api/python/bindings/dataset/kernels/ir/image/bindings.cc +++ b/mindspore/ccsrc/minddata/dataset/api/python/bindings/dataset/kernels/ir/image/bindings.cc @@ -55,6 +55,7 @@ #include "minddata/dataset/kernels/ir/vision/rescale_ir.h" #include "minddata/dataset/kernels/ir/vision/resize_ir.h" #include "minddata/dataset/kernels/ir/vision/resize_with_bbox_ir.h" +#include "minddata/dataset/kernels/ir/vision/rotate_ir.h" #include "minddata/dataset/kernels/ir/vision/softdvpp_decode_random_crop_resize_jpeg_ir.h" #include "minddata/dataset/kernels/ir/vision/softdvpp_decode_resize_jpeg_ir.h" #include "minddata/dataset/kernels/ir/vision/uniform_aug_ir.h" @@ -514,6 +515,18 @@ PYBIND_REGISTER(ResizeWithBBoxOperation, 1, ([](const py::module *m) { })); })); +PYBIND_REGISTER(RotateOperation, 1, ([](const py::module *m) { + (void)py::class_>( + *m, "RotateOperation") + .def(py::init([](float degrees, InterpolationMode resample, bool expand, std::vector center, + std::vector fill_value) { + auto rotate = + std::make_shared(degrees, resample, expand, center, fill_value); + THROW_IF_ERROR(rotate->ValidateParams()); + return rotate; + })); + })); + PYBIND_REGISTER(SoftDvppDecodeRandomCropResizeJpegOperation, 1, ([](const py::module *m) { (void)py::class_>( diff --git a/mindspore/ccsrc/minddata/dataset/api/vision.cc b/mindspore/ccsrc/minddata/dataset/api/vision.cc index 5a683aaf978..28d5116dde6 100644 --- a/mindspore/ccsrc/minddata/dataset/api/vision.cc +++ b/mindspore/ccsrc/minddata/dataset/api/vision.cc @@ -820,12 +820,32 @@ std::shared_ptr ResizePreserveAR::Parse() { return std::make_shared(data_->height_, data_->width_, data_->img_orientation_); } -#ifdef ENABLE_ANDROID // Rotate Transform Operation. +#ifdef ENABLE_ANDROID Rotate::Rotate() {} std::shared_ptr Rotate::Parse() { return std::make_shared(); } -#endif // ENABLE_ANDROID +#else +struct Rotate::Data { + Data(const float °rees, InterpolationMode resample, bool expand, const std::vector ¢er, + const std::vector &fill_value) + : degrees_(degrees), interpolation_mode_(resample), expand_(expand), center_(center), fill_value_(fill_value) {} + float degrees_; + InterpolationMode interpolation_mode_; + std::vector center_; + bool expand_; + std::vector fill_value_; +}; + +Rotate::Rotate(float degrees, InterpolationMode resample, bool expand, std::vector center, + std::vector fill_value) + : data_(std::make_shared(degrees, resample, expand, center, fill_value)) {} + +std::shared_ptr Rotate::Parse() { + return std::make_shared(data_->degrees_, data_->interpolation_mode_, data_->expand_, data_->center_, + data_->fill_value_); +} +#endif #ifndef ENABLE_ANDROID // ResizeWithBBox Transform Operation. diff --git a/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h b/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h index f4a976fb88f..49374c9184b 100644 --- a/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h +++ b/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h @@ -631,7 +631,7 @@ class RandomResizedCropWithBBox final : public TensorTransform { class RandomRotation final : public TensorTransform { public: /// \brief Constructor. - /// \param[in] degrees A float vector of size, representing the starting and ending degree. + /// \param[in] degrees A float vector of size 2, representing the starting and ending degrees. /// \param[in] resample An enum for the mode of interpolation. /// \param[in] expand A boolean representing whether the image is expanded after rotation. /// \param[in] center A float vector of size 2, representing the x and y center of rotation. diff --git a/mindspore/ccsrc/minddata/dataset/include/dataset/vision_lite.h b/mindspore/ccsrc/minddata/dataset/include/dataset/vision_lite.h index 8c0707f10c8..04fac4f915a 100644 --- a/mindspore/ccsrc/minddata/dataset/include/dataset/vision_lite.h +++ b/mindspore/ccsrc/minddata/dataset/include/dataset/vision_lite.h @@ -291,12 +291,23 @@ class ResizePreserveAR final : public TensorTransform { }; /// \brief Rotate TensorTransform. -/// \note Rotate the input image using a specified angle id. +/// \note Rotate the input image according to parameters. class Rotate final : public TensorTransform { public: /// \brief Constructor. Rotate(); + /// \brief Constructor. + /// \param[in] degrees A float value, representing the rotation degrees. + /// \param[in] resample An enum for the mode of interpolation. + /// \param[in] expand A boolean representing whether the image is expanded after rotation. + /// \param[in] center A float vector of size 2, representing the x and y center of rotation. + /// \param[in] fill_value A vector representing the value to fill the area outside the transform. + /// in the output image. If 1 value is provided, it is used for all RGB channels. + /// If 3 values are provided, it is used to fill R, G, B channels respectively. + Rotate(float degrees, InterpolationMode resample = InterpolationMode::kNearestNeighbour, bool expand = false, + std::vector center = {-1, -1}, std::vector fill_value = {0, 0, 0}); + /// \brief Destructor. ~Rotate() = default; @@ -307,6 +318,8 @@ class Rotate final : public TensorTransform { private: std::shared_ptr op_; + struct Data; + std::shared_ptr data_; }; } // namespace vision diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc index abb180bc594..9eb37b27daf 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc @@ -1,5 +1,5 @@ /** - * Copyright 2020 Huawei Technologies Co., Ltd + * Copyright 2020-2021 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,25 +15,71 @@ */ #include "minddata/dataset/kernels/image/rotate_op.h" -#ifdef ENABLE_ANDROID + +#ifndef ENABLE_ANDROID +#include "minddata/dataset/kernels/image/image_utils.h" +#else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif namespace mindspore { namespace dataset { +const float RotateOp::kDefCenterX = -1; +const float RotateOp::kDefCenterY = -1; +const InterpolationMode RotateOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; +const bool RotateOp::kDefExpand = false; +const uint8_t RotateOp::kDefFillR = 0; +const uint8_t RotateOp::kDefFillG = 0; +const uint8_t RotateOp::kDefFillB = 0; RotateOp::RotateOp(int angle_id) : angle_id_(angle_id) {} +RotateOp::RotateOp(float degrees, InterpolationMode resample, bool expand, float center_x, float center_y, + uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) + : degrees_(degrees), + center_x_(center_x), + center_y_(center_y), + interpolation_(resample), + expand_(expand), + fill_r_(fill_r), + fill_g_(fill_g), + fill_b_(fill_b) {} + Status RotateOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { IO_CHECK(input, output); CHECK_FAIL_RETURN_UNEXPECTED( input->shape().Size() >= 2, "Rotate: image shape " + std::to_string(input->shape().Size()) + " is not or ."); -#ifdef ENABLE_ANDROID - Rotate(input, output, angle_id_); +#ifndef ENABLE_ANDROID + return Rotate(input, output, center_x_, center_y_, degrees_, interpolation_, expand_, fill_r_, fill_g_, fill_b_); +#else + return Rotate(input, output, angle_id_); #endif - return Status::OK(); } +Status RotateOp::OutputShape(const std::vector &inputs, std::vector &outputs) { +#ifndef ENABLE_ANDROID + RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + outputs.clear(); + int32_t outputH = -1, outputW = -1; + // if expand_, then we cannot know the shape. We need the input image to find the output shape --> set it to + // <-1,-1[,3]> + if (!expand_) { + outputH = inputs[0][0]; + outputW = inputs[0][1]; + } + TensorShape out = TensorShape{outputH, outputW}; + if (inputs[0].Rank() == 2) outputs.emplace_back(out); + if (inputs[0].Rank() == 3) outputs.emplace_back(out.AppendDim(inputs[0][2])); + if (!outputs.empty()) return Status::OK(); + return Status(StatusCode::kMDUnexpectedError, "Rotate: invalid input shape."); +#else + if (inputs.size() != NumInput()) + return Status(StatusCode::kMDUnexpectedError, + "The size of the input argument vector does not match the number of inputs"); + outputs = inputs; + return Status::OK(); +#endif +} } // namespace dataset } // namespace mindspore diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.h b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.h index 2cca502fe85..0b0b6e9b32a 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Huawei Technologies Co., Ltd + * Copyright 2020-2021 Huawei Technologies Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,11 +29,26 @@ namespace mindspore { namespace dataset { class RotateOp : public TensorOp { public: + // Default values, also used by python_bindings.cc + static const float kDefCenterX; + static const float kDefCenterY; + static const InterpolationMode kDefInterpolation; + static const bool kDefExpand; + static const uint8_t kDefFillR; + static const uint8_t kDefFillG; + static const uint8_t kDefFillB; + /// Constructor explicit RotateOp(int angle_id); + explicit RotateOp(float degrees, InterpolationMode resample = kDefInterpolation, bool expand = kDefExpand, + float center_x = kDefCenterX, float center_y = kDefCenterY, uint8_t fill_r = kDefFillR, + uint8_t fill_g = kDefFillG, uint8_t fill_b = kDefFillB); + ~RotateOp() override = default; + Status OutputShape(const std::vector &inputs, std::vector &outputs) override; + std::string Name() const override { return kRotateOp; } Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; @@ -42,11 +57,18 @@ class RotateOp : public TensorOp { /// Member variables protected: - std::string kRotateOp = "RotateOp"; uint64_t angle_id_; -}; + private: + float degrees_; + float center_x_; + float center_y_; + InterpolationMode interpolation_; + bool expand_; + uint8_t fill_r_; + uint8_t fill_g_; + uint8_t fill_b_; +}; } // namespace dataset } // namespace mindspore - #endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ROTATE_OP_H_ diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.cc b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.cc index dd2a5bace83..e46e9668817 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.cc @@ -13,12 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include - #include "minddata/dataset/kernels/ir/vision/rotate_ir.h" #include "minddata/dataset/kernels/image/rotate_op.h" - #include "minddata/dataset/kernels/ir/validators.h" namespace mindspore { @@ -27,20 +24,69 @@ namespace dataset { namespace vision { // RotateOperation -RotateOperation::RotateOperation() { rotate_op = std::make_shared(0); } +RotateOperation::RotateOperation() { rotate_op_ = std::make_shared(0); } + +RotateOperation::RotateOperation(float degrees, InterpolationMode resample, bool expand, std::vector center, + std::vector fill_value) + : degrees_(degrees), interpolation_mode_(resample), expand_(expand), center_(center), fill_value_(fill_value) {} RotateOperation::~RotateOperation() = default; std::string RotateOperation::Name() const { return kRotateOperation; } -Status RotateOperation::ValidateParams() { return Status::OK(); } - -std::shared_ptr RotateOperation::Build() { return rotate_op; } - -void RotateOperation::setAngle(uint64_t angle_id) { - std::dynamic_pointer_cast(rotate_op)->setAngle(angle_id); +Status RotateOperation::ValidateParams() { +#ifndef ENABLE_ANDROID + // center + if (center_.empty() || center_.size() != 2) { + std::string err_msg = "Rotate: center must be a vector of two values, got: " + std::to_string(center_.size()); + MS_LOG(ERROR) << err_msg; + RETURN_STATUS_SYNTAX_ERROR(err_msg); + } + // fill_value + RETURN_IF_NOT_OK(ValidateVectorFillvalue("Rotate", fill_value_)); +#endif + return Status::OK(); } +std::shared_ptr RotateOperation::Build() { +#ifndef ENABLE_ANDROID + 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(degrees_, interpolation_mode_, expand_, center_[0], center_[1], fill_r, fill_g, fill_b); + return tensor_op; +#else + return rotate_op_; +#endif +} + +Status RotateOperation::to_json(nlohmann::json *out_json) { + nlohmann::json args; +#ifndef ENABLE_ANDROID + args["degree"] = degrees_; + args["resample"] = interpolation_mode_; + args["expand"] = expand_; + args["center"] = center_; + args["fill_value"] = fill_value_; +#else + args["angle_id"] = angle_id_; +#endif + *out_json = args; + return Status::OK(); +} + +void RotateOperation::setAngle(uint64_t angle_id) { + std::dynamic_pointer_cast(rotate_op_)->setAngle(angle_id); +} } // namespace vision } // namespace dataset } // namespace mindspore diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.h b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.h index 283cd436662..3a5c34f56d5 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/rotate_ir.h @@ -39,6 +39,9 @@ class RotateOperation : public TensorOperation { public: RotateOperation(); + RotateOperation(float degrees, InterpolationMode resample, bool expand, std::vector center, + std::vector fill_value); + ~RotateOperation(); std::shared_ptr Build() override; @@ -47,10 +50,18 @@ class RotateOperation : public TensorOperation { std::string Name() const override; + Status to_json(nlohmann::json *out_json) override; + void setAngle(uint64_t angle_id); private: - std::shared_ptr rotate_op; + std::shared_ptr rotate_op_; + uint64_t angle_id_; + float degrees_; + InterpolationMode interpolation_mode_; + std::vector center_; + bool expand_; + std::vector fill_value_; }; } // namespace vision diff --git a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h index aad0ff7f2e4..5fec3d01357 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h @@ -102,6 +102,7 @@ constexpr char kResizeWithBBoxOp[] = "ResizeWithBBoxOp"; constexpr char kRgbaToBgrOp[] = "RgbaToBgrOp"; constexpr char kRgbaToRgbOp[] = "RgbaToRgbOp"; constexpr char kRgbToGrayOp[] = "RgbToGrayOp"; +constexpr char kRotateOp[] = "RotateOp"; constexpr char kSharpnessOp[] = "SharpnessOp"; constexpr char kSoftDvppDecodeRandomCropResizeJpegOp[] = "SoftDvppDecodeRandomCropResizeJpegOp"; constexpr char kSoftDvppDecodeReiszeJpegOp[] = "SoftDvppDecodeReiszeJpegOp"; diff --git a/mindspore/dataset/core/validator_helpers.py b/mindspore/dataset/core/validator_helpers.py index 572f4994d72..61bf2b751f9 100644 --- a/mindspore/dataset/core/validator_helpers.py +++ b/mindspore/dataset/core/validator_helpers.py @@ -183,7 +183,7 @@ def check_2tuple(value, arg_name=""): :return: Exception: when the validation fails, nothing otherwise. """ if not (isinstance(value, tuple) and len(value) == 2): - raise ValueError("Value {0}needs to be a 2-tuple.".format(arg_name)) + raise ValueError("Value {0} needs to be a 2-tuple.".format(arg_name)) def check_uint8(value, arg_name=""): diff --git a/mindspore/dataset/vision/c_transforms.py b/mindspore/dataset/vision/c_transforms.py index 187e00b2249..b42146b4439 100644 --- a/mindspore/dataset/vision/c_transforms.py +++ b/mindspore/dataset/vision/c_transforms.py @@ -54,7 +54,7 @@ from .validators import check_prob, check_crop, check_resize_interpolation, chec check_uniform_augment_cpp, \ check_bounding_box_augment_cpp, check_random_select_subpolicy_op, check_auto_contrast, check_random_affine, \ check_random_solarize, check_soft_dvpp_decode_random_crop_resize_jpeg, check_positive_degrees, FLOAT_MAX_INTEGER, \ - check_cut_mix_batch_c, check_posterize, check_gaussian_blur + check_cut_mix_batch_c, check_posterize, check_gaussian_blur, check_rotate from ..transforms.c_transforms import TensorOperation @@ -1129,7 +1129,7 @@ class RandomRotation(ImageTensorOperation): Rotate the input image by a random angle. Args: - degrees (Union[int, float, sequence): Range of random rotation degrees. + degrees (Union[int, float, sequence]): Range of random rotation degrees. If degrees is a number, the range will be converted to (-degrees, degrees). If degrees is a sequence, it should be (min, max). resample (Inter mode, optional): An optional resampling filter (default=Inter.NEAREST). @@ -1426,6 +1426,66 @@ class ResizeWithBBox(ImageTensorOperation): return cde.ResizeWithBBoxOperation(size, DE_C_INTER_MODE[self.interpolation]) +class Rotate(ImageTensorOperation): + """ + Rotate the input image by specified degrees. + + Args: + degrees (Union[int, float]): Rotation degrees. + + resample (Inter mode, optional): An optional resampling filter (default=Inter.NEAREST). + If omitted, or if the image has mode "1" or "P", it is set to be Inter.NEAREST. + It can be any of [Inter.BILINEAR, Inter.NEAREST, Inter.BICUBIC]. + + - Inter.BILINEAR, means resample method is bilinear interpolation. + + - Inter.NEAREST, means resample method is nearest-neighbor interpolation. + + - Inter.BICUBIC, means resample method is bicubic interpolation. + + expand (bool, optional): Optional expansion flag (default=False). If set to True, expand the output + image to make it large enough to hold the entire rotated image. + If set to False or omitted, make the output image the same size as the input. + Note that the expand flag assumes rotation around the center and no translation. + center (tuple, optional): Optional center of rotation (a 2-tuple) (default=None). + Origin is the top left corner. None sets to the center of the image. + fill_value (Union[int, tuple], optional): Optional fill color for the area outside the rotated image. + If it is a 3-tuple, it is used to fill R, G, B channels respectively. + If it is an integer, it is used for all RGB channels. + The fill_value values must be in range [0, 255] (default=0). + + Examples: + >>> from mindspore.dataset.vision import Inter + >>> transforms_list = [c_vision.Decode(), + ... c_vision.Rotate(degrees=30.0, + ... resample=Inter.NEAREST, + ... expand=True)] + >>> image_folder_dataset = image_folder_dataset.map(operations=transforms_list, + ... input_columns=["image"]) + """ + + @check_rotate + def __init__(self, degrees, resample=Inter.NEAREST, expand=False, center=None, fill_value=0): + if isinstance(degrees, numbers.Number): + degrees = degrees % 360 + + self.degrees = degrees + self.resample = resample + self.expand = expand + self.center = center + self.fill_value = fill_value + + def parse(self): + # pylint false positive + # pylint: disable=E1130 + degrees = self.degrees + interpolation = DE_C_INTER_MODE[self.resample] + expand = self.expand + center = (-1, -1) if self.center is None else self.center + fill_value = tuple([self.fill_value] * 3) if isinstance(self.fill_value, int) else self.fill_value + return cde.RotateOperation(degrees, interpolation, expand, center, fill_value) + + class SoftDvppDecodeRandomCropResizeJpeg(ImageTensorOperation): """ Tensor operation to decode, random crop and resize JPEG image using the simulation algorithm of diff --git a/mindspore/dataset/vision/validators.py b/mindspore/dataset/vision/validators.py index 7333a8dd847..8db5c8047b2 100644 --- a/mindspore/dataset/vision/validators.py +++ b/mindspore/dataset/vision/validators.py @@ -404,6 +404,30 @@ def check_random_rotation(method): return new_method +def check_rotate(method): + """Wrapper method to check the parameters of rotate.""" + + @wraps(method) + def new_method(self, *args, **kwargs): + [degrees, resample, expand, center, fill_value], _ = parse_user_args(method, *args, **kwargs) + + type_check(degrees, (numbers.Number,), "degrees") + check_float32(degrees, "degrees") + + if resample is not None: + type_check(resample, (Inter,), "resample") + if expand is not None: + type_check(expand, (bool,), "expand") + if center is not None: + check_2tuple(center, "center") + if fill_value is not None: + check_fill_value(fill_value) + + return method(self, *args, **kwargs) + + return new_method + + def check_ten_crop(method): """Wrapper method to check the parameters of crop.""" diff --git a/mindspore/lite/minddata/CMakeLists.txt b/mindspore/lite/minddata/CMakeLists.txt index 9efbb103281..edc7d1c21ee 100644 --- a/mindspore/lite/minddata/CMakeLists.txt +++ b/mindspore/lite/minddata/CMakeLists.txt @@ -482,7 +482,6 @@ elseif(BUILD_MINDDATA STREQUAL "lite") "${MINDDATA_DIR}/kernels/image/cut_out_op.cc" "${MINDDATA_DIR}/kernels/image/cutmix_batch_op.cc" "${MINDDATA_DIR}/kernels/image/equalize_op.cc" - "${MINDDATA_DIR}/kernels/image/gaussian_blur.cc" "${MINDDATA_DIR}/kernels/image/hwc_to_chw_op.cc" "${MINDDATA_DIR}/kernels/image/image_utils.cc" "${MINDDATA_DIR}/kernels/image/invert_op.cc" diff --git a/tests/ut/cpp/dataset/c_api_vision_gaussian_blur_test.cc b/tests/ut/cpp/dataset/c_api_vision_gaussian_blur_test.cc index 9142d999a84..cd4e4ab2b62 100644 --- a/tests/ut/cpp/dataset/c_api_vision_gaussian_blur_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_gaussian_blur_test.cc @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "minddata/dataset/kernels/image/gaussian_blur_op.h" - #include "common/common.h" #include "minddata/dataset/include/dataset/datasets.h" #include "minddata/dataset/include/dataset/execute.h" diff --git a/tests/ut/cpp/dataset/c_api_vision_horizontal_flip_test.cc b/tests/ut/cpp/dataset/c_api_vision_horizontal_flip_test.cc index 6a48eb76d29..4f0ba47b131 100644 --- a/tests/ut/cpp/dataset/c_api_vision_horizontal_flip_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_horizontal_flip_test.cc @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "minddata/dataset/kernels/image/horizontal_flip_op.h" - #include "common/common.h" #include "minddata/dataset/include/dataset/datasets.h" #include "minddata/dataset/include/dataset/execute.h" diff --git a/tests/ut/cpp/dataset/c_api_vision_r_to_z_test.cc b/tests/ut/cpp/dataset/c_api_vision_r_to_z_test.cc index 6d5870c4150..ebffd3807ee 100644 --- a/tests/ut/cpp/dataset/c_api_vision_r_to_z_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_r_to_z_test.cc @@ -231,3 +231,84 @@ TEST_F(MindDataTestPipeline, TestRGB2GRAYSucess) { // Manually terminate the pipeline iter->Stop(); } + +TEST_F(MindDataTestPipeline, TestRotateParamCheck) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRotateParamCheck with invalid parameters."; + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, std::make_shared(false, 10)); + EXPECT_NE(ds, nullptr); + + // Case 1: Size of center is not 2 + // Create objects for the tensor ops + std::shared_ptr rotate1(new vision::Rotate(90.0, InterpolationMode::kNearestNeighbour, false, {0.})); + auto ds2 = ds->Map({rotate1}); + EXPECT_NE(ds2, nullptr); + // Create an iterator over the result of the above dataset + std::shared_ptr iter2 = ds2->CreateIterator(); + // Expect failure: invalid center for Rotate + EXPECT_EQ(iter2, nullptr); + + // Case 2: Size of fill_value is not 1 or 3 + // Create objects for the tensor ops + std::shared_ptr rotate2( + new vision::Rotate(-30, InterpolationMode::kNearestNeighbour, false, {1.0, 1.0}, {2, 2})); + auto ds3 = ds->Map({rotate2}); + EXPECT_NE(ds3, nullptr); + // Create an iterator over the result of the above dataset + std::shared_ptr iter3 = ds3->CreateIterator(); + // Expect failure: invalid fill_value for Rotate + EXPECT_EQ(iter3, nullptr); +} + +TEST_F(MindDataTestPipeline, TestRotatePass) { + MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRotatePass."; + + // Create an ImageFolder Dataset + std::string folder_path = datasets_root_path_ + "/testPK/data/"; + std::shared_ptr ds = ImageFolder(folder_path, true, std::make_shared(false, 10)); + EXPECT_NE(ds, nullptr); + + // Create objects for the tensor ops + std::shared_ptr resize(new vision::Resize({50, 25})); + + std::shared_ptr rotate( + new vision::Rotate(90, InterpolationMode::kLinear, true, {-1, -1}, {255, 255, 255})); + + // Resize the image to 50 * 25 + ds = ds->Map({resize}); + EXPECT_NE(ds, nullptr); + + // Rotate the image 90 degrees + ds = ds->Map({rotate}); + 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; + ASSERT_OK(iter->GetNextRow(&row)); + + uint64_t i = 0; + while (row.size() != 0) { + i++; + auto image = row["image"]; + // After rotation with expanding, the image size comes to 25 * 50 + EXPECT_EQ(image.Shape()[1], 25); + EXPECT_EQ(image.Shape()[2], 50); + ASSERT_OK(iter->GetNextRow(&row)); + } + + EXPECT_EQ(i, 10); + + // Manually terminate the pipeline + iter->Stop(); +} diff --git a/tests/ut/cpp/dataset/c_api_vision_vertical_flip_test.cc b/tests/ut/cpp/dataset/c_api_vision_vertical_flip_test.cc index ee76f68acdd..416a4e09f81 100644 --- a/tests/ut/cpp/dataset/c_api_vision_vertical_flip_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_vertical_flip_test.cc @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "minddata/dataset/kernels/image/vertical_flip_op.h" - #include "common/common.h" #include "minddata/dataset/include/dataset/datasets.h" #include "minddata/dataset/include/dataset/execute.h" diff --git a/tests/ut/cpp/dataset/execute_test.cc b/tests/ut/cpp/dataset/execute_test.cc index 2007dd9562b..f31b7834ea0 100644 --- a/tests/ut/cpp/dataset/execute_test.cc +++ b/tests/ut/cpp/dataset/execute_test.cc @@ -246,3 +246,19 @@ TEST_F(MindDataTestExecute, TestBasicTokenizer) { ASSERT_EQ(txt_result.size(), 3); ASSERT_TRUE(rc.IsOk()); } + +TEST_F(MindDataTestExecute, TestRotate) { + MS_LOG(INFO) << "Doing MindDataTestExecute-TestRotate."; + + // Read images + auto image = ReadFileToTensor("data/dataset/apple.jpg"); + + // Transform params + auto decode = vision::Decode(); + auto rotate = vision::Rotate(10.5); + + auto transform = Execute({decode, rotate}); + Status rc = transform(image, &image); + + EXPECT_EQ(rc, Status::OK()); +} \ No newline at end of file diff --git a/tests/ut/python/dataset/test_gaussian_blur.py b/tests/ut/python/dataset/test_gaussian_blur.py index 2599eaed8f3..1864f5b745f 100644 --- a/tests/ut/python/dataset/test_gaussian_blur.py +++ b/tests/ut/python/dataset/test_gaussian_blur.py @@ -13,7 +13,7 @@ # limitations under the License. # ============================================================================== """ -Testing GaussianBlur op in DE +Testing GaussianBlur Python API """ import cv2 @@ -27,8 +27,6 @@ DATA_DIR = ["../data/dataset/test_tf_file_3_images/train-0000-of-0001.data"] SCHEMA_DIR = "../data/dataset/test_tf_file_3_images/datasetSchema.json" IMAGE_FILE = "../data/dataset/apple.jpg" -GENERATE_GOLDEN = False - def test_gaussian_blur_pipeline(plot=False): """ @@ -78,7 +76,7 @@ def test_gaussian_blur_eager(): def test_gaussian_blur_exception(): """ - Test GsianBlur with invalid parameters + Test GaussianBlur with invalid parameters """ logger.info("test_gaussian_blur_exception") try: @@ -104,6 +102,6 @@ def test_gaussian_blur_exception(): if __name__ == "__main__": - test_gaussian_blur_pipeline(plot=True) + test_gaussian_blur_pipeline(plot=False) test_gaussian_blur_eager() test_gaussian_blur_exception() diff --git a/tests/ut/python/dataset/test_horizontal_flip.py b/tests/ut/python/dataset/test_horizontal_flip.py index 2b685e3b194..3d3a3a49781 100644 --- a/tests/ut/python/dataset/test_horizontal_flip.py +++ b/tests/ut/python/dataset/test_horizontal_flip.py @@ -54,7 +54,7 @@ def test_horizontal_flip_pipeline(plot=False): original = data2["image"] horizontal_flip_cv = cv2.flip(original, 1) mse = diff_mse(horizontal_flip_ms, horizontal_flip_cv) - logger.info("gaussian_blur_{}, mse: {}".format(num_iter + 1, mse)) + logger.info("horizontal_flip_{}, mse: {}".format(num_iter + 1, mse)) assert mse == 0 num_iter += 1 if plot: @@ -75,5 +75,5 @@ def test_horizontal_flip_eager(): if __name__ == "__main__": - test_horizontal_flip_pipeline(plot=True) + test_horizontal_flip_pipeline(plot=False) test_horizontal_flip_eager() diff --git a/tests/ut/python/dataset/test_rotate.py b/tests/ut/python/dataset/test_rotate.py new file mode 100644 index 00000000000..88cc7539a89 --- /dev/null +++ b/tests/ut/python/dataset/test_rotate.py @@ -0,0 +1,121 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +Testing Rotate Python API +""" +import cv2 + +import mindspore.dataset as ds +import mindspore.dataset.vision.c_transforms as c_vision +from mindspore import log as logger +from mindspore.dataset.vision.utils import Inter +from util import visualize_image, diff_mse + +DATA_DIR = ["../data/dataset/test_tf_file_3_images/train-0000-of-0001.data"] +SCHEMA_DIR = "../data/dataset/test_tf_file_3_images/datasetSchema.json" +IMAGE_FILE = "../data/dataset/apple.jpg" + + +def test_rotate_pipeline_with_expanding(plot=False): + """ + Test Rotate of c_transforms with expanding + """ + logger.info("test_rotate_pipeline_with_expanding") + + # First dataset + dataset1 = ds.TFRecordDataset(DATA_DIR, SCHEMA_DIR, shuffle=False) + decode_op = c_vision.Decode() + rotate_op = c_vision.Rotate(90, expand=True) + dataset1 = dataset1.map(operations=decode_op, input_columns=["image"]) + dataset1 = dataset1.map(operations=rotate_op, input_columns=["image"]) + + # Second dataset + dataset2 = ds.TFRecordDataset(DATA_DIR, SCHEMA_DIR, columns_list=["image"], shuffle=False) + dataset2 = dataset2.map(operations=decode_op, input_columns=["image"]) + + num_iter = 0 + for data1, data2 in zip(dataset1.create_dict_iterator(num_epochs=1, output_numpy=True), + dataset2.create_dict_iterator(num_epochs=1, output_numpy=True)): + if num_iter > 0: + break + rotate_ms = data1["image"] + original = data2["image"] + rotate_cv = cv2.rotate(original, cv2.ROTATE_90_COUNTERCLOCKWISE) + mse = diff_mse(rotate_ms, rotate_cv) + logger.info("rotate_{}, mse: {}".format(num_iter + 1, mse)) + assert mse == 0 + num_iter += 1 + if plot: + visualize_image(original, rotate_ms, mse, rotate_cv) + + +def test_rotate_pipeline_without_expanding(): + """ + Test Rotate of c_transforms without expanding + """ + logger.info("test_rotate_pipeline_without_expanding") + + # Create a Dataset then decode and rotate the image + dataset = ds.TFRecordDataset(DATA_DIR, SCHEMA_DIR, shuffle=False) + decode_op = c_vision.Decode() + resize_op = c_vision.Resize((64, 128)) + rotate_op = c_vision.Rotate(30) + dataset = dataset.map(operations=decode_op, input_columns=["image"]) + dataset = dataset.map(operations=resize_op, input_columns=["image"]) + dataset = dataset.map(operations=rotate_op, input_columns=["image"]) + + for data in dataset.create_dict_iterator(num_epochs=1, output_numpy=True): + rotate_img = data["image"] + assert rotate_img.shape == (64, 128, 3) + + +def test_rotate_eager(): + """ + Test Rotate with eager mode + """ + logger.info("test_rotate_eager") + img = cv2.imread(IMAGE_FILE) + resize_img = c_vision.Resize((32, 64))(img) + rotate_img = c_vision.Rotate(-90, expand=True)(resize_img) + assert rotate_img.shape == (64, 32, 3) + + +def test_rotate_exception(): + """ + Test Rotate with invalid parameters + """ + logger.info("test_rotate_exception") + try: + _ = c_vision.Rotate("60") + except TypeError as e: + logger.info("Got an exception in Rotate: {}".format(str(e))) + assert "not of type []" in str(e) + try: + _ = c_vision.Rotate(30, Inter.BICUBIC, False, (0, 0, 0)) + except ValueError as e: + logger.info("Got an exception in Rotate: {}".format(str(e))) + assert "Value center needs to be a 2-tuple." in str(e) + try: + _ = c_vision.Rotate(-120, Inter.NEAREST, False, (-1, -1), (255, 255)) + except TypeError as e: + logger.info("Got an exception in Rotate: {}".format(str(e))) + assert "fill_value should be a single integer or a 3-tuple." in str(e) + + +if __name__ == "__main__": + test_rotate_pipeline_with_expanding(False) + test_rotate_pipeline_without_expanding() + test_rotate_eager() + test_rotate_exception() diff --git a/tests/ut/python/dataset/test_vertical_flip.py b/tests/ut/python/dataset/test_vertical_flip.py index 5ad43fd4789..efb9bbe05b5 100644 --- a/tests/ut/python/dataset/test_vertical_flip.py +++ b/tests/ut/python/dataset/test_vertical_flip.py @@ -54,7 +54,7 @@ def test_vertical_flip_pipeline(plot=False): original = data2["image"] vertical_flip_cv = cv2.flip(original, 0) mse = diff_mse(vertical_flip_ms, vertical_flip_cv) - logger.info("gaussian_blur_{}, mse: {}".format(num_iter + 1, mse)) + logger.info("vertical_flip_{}, mse: {}".format(num_iter + 1, mse)) assert mse == 0 num_iter += 1 if plot: @@ -75,5 +75,5 @@ def test_vertical_flip_eager(): if __name__ == "__main__": - test_vertical_flip_pipeline(plot=True) + test_vertical_flip_pipeline(plot=False) test_vertical_flip_eager()