Merge pull request !17836 from xiaotianci/rotatetags/v1.3.0
| @@ -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_<vision::RotateOperation, TensorOperation, std::shared_ptr<vision::RotateOperation>>( | |||
| *m, "RotateOperation") | |||
| .def(py::init([](float degrees, InterpolationMode resample, bool expand, std::vector<float> center, | |||
| std::vector<uint8_t> fill_value) { | |||
| auto rotate = | |||
| std::make_shared<vision::RotateOperation>(degrees, resample, expand, center, fill_value); | |||
| THROW_IF_ERROR(rotate->ValidateParams()); | |||
| return rotate; | |||
| })); | |||
| })); | |||
| PYBIND_REGISTER(SoftDvppDecodeRandomCropResizeJpegOperation, 1, ([](const py::module *m) { | |||
| (void)py::class_<vision::SoftDvppDecodeRandomCropResizeJpegOperation, TensorOperation, | |||
| std::shared_ptr<vision::SoftDvppDecodeRandomCropResizeJpegOperation>>( | |||
| @@ -820,12 +820,32 @@ std::shared_ptr<TensorOperation> ResizePreserveAR::Parse() { | |||
| return std::make_shared<ResizePreserveAROperation>(data_->height_, data_->width_, data_->img_orientation_); | |||
| } | |||
| #ifdef ENABLE_ANDROID | |||
| // Rotate Transform Operation. | |||
| #ifdef ENABLE_ANDROID | |||
| Rotate::Rotate() {} | |||
| std::shared_ptr<TensorOperation> Rotate::Parse() { return std::make_shared<RotateOperation>(); } | |||
| #endif // ENABLE_ANDROID | |||
| #else | |||
| struct Rotate::Data { | |||
| Data(const float °rees, InterpolationMode resample, bool expand, const std::vector<float> ¢er, | |||
| const std::vector<uint8_t> &fill_value) | |||
| : degrees_(degrees), interpolation_mode_(resample), expand_(expand), center_(center), fill_value_(fill_value) {} | |||
| float degrees_; | |||
| InterpolationMode interpolation_mode_; | |||
| std::vector<float> center_; | |||
| bool expand_; | |||
| std::vector<uint8_t> fill_value_; | |||
| }; | |||
| Rotate::Rotate(float degrees, InterpolationMode resample, bool expand, std::vector<float> center, | |||
| std::vector<uint8_t> fill_value) | |||
| : data_(std::make_shared<Data>(degrees, resample, expand, center, fill_value)) {} | |||
| std::shared_ptr<TensorOperation> Rotate::Parse() { | |||
| return std::make_shared<RotateOperation>(data_->degrees_, data_->interpolation_mode_, data_->expand_, data_->center_, | |||
| data_->fill_value_); | |||
| } | |||
| #endif | |||
| #ifndef ENABLE_ANDROID | |||
| // ResizeWithBBox Transform Operation. | |||
| @@ -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. | |||
| @@ -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<float> center = {-1, -1}, std::vector<uint8_t> fill_value = {0, 0, 0}); | |||
| /// \brief Destructor. | |||
| ~Rotate() = default; | |||
| @@ -307,6 +318,8 @@ class Rotate final : public TensorTransform { | |||
| private: | |||
| std::shared_ptr<RotateOperation> op_; | |||
| struct Data; | |||
| std::shared_ptr<Data> data_; | |||
| }; | |||
| } // namespace vision | |||
| @@ -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<Tensor> &input, std::shared_ptr<Tensor> *output) { | |||
| IO_CHECK(input, output); | |||
| CHECK_FAIL_RETURN_UNEXPECTED( | |||
| input->shape().Size() >= 2, | |||
| "Rotate: image shape " + std::to_string(input->shape().Size()) + " is not <H,W,C> or <H,W>."); | |||
| #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<TensorShape> &inputs, std::vector<TensorShape> &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 | |||
| @@ -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<TensorShape> &inputs, std::vector<TensorShape> &outputs) override; | |||
| std::string Name() const override { return kRotateOp; } | |||
| Status Compute(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *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_ | |||
| @@ -13,12 +13,9 @@ | |||
| * See the License for the specific language governing permissions and | |||
| * limitations under the License. | |||
| */ | |||
| #include <algorithm> | |||
| #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<RotateOp>(0); } | |||
| RotateOperation::RotateOperation() { rotate_op_ = std::make_shared<RotateOp>(0); } | |||
| RotateOperation::RotateOperation(float degrees, InterpolationMode resample, bool expand, std::vector<float> center, | |||
| std::vector<uint8_t> 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(); } | |||
| 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<TensorOp> RotateOperation::Build() { return rotate_op; } | |||
| std::shared_ptr<TensorOp> 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]; | |||
| void RotateOperation::setAngle(uint64_t angle_id) { | |||
| std::dynamic_pointer_cast<RotateOp>(rotate_op)->setAngle(angle_id); | |||
| if (fill_value_.size() == 3) { | |||
| fill_r = fill_value_[0]; | |||
| fill_g = fill_value_[1]; | |||
| fill_b = fill_value_[2]; | |||
| } | |||
| std::shared_ptr<RotateOp> tensor_op = | |||
| std::make_shared<RotateOp>(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<RotateOp>(rotate_op_)->setAngle(angle_id); | |||
| } | |||
| } // namespace vision | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| @@ -39,6 +39,9 @@ class RotateOperation : public TensorOperation { | |||
| public: | |||
| RotateOperation(); | |||
| RotateOperation(float degrees, InterpolationMode resample, bool expand, std::vector<float> center, | |||
| std::vector<uint8_t> fill_value); | |||
| ~RotateOperation(); | |||
| std::shared_ptr<TensorOp> 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<TensorOp> rotate_op; | |||
| std::shared_ptr<TensorOp> rotate_op_; | |||
| uint64_t angle_id_; | |||
| float degrees_; | |||
| InterpolationMode interpolation_mode_; | |||
| std::vector<float> center_; | |||
| bool expand_; | |||
| std::vector<uint8_t> fill_value_; | |||
| }; | |||
| } // namespace vision | |||
| @@ -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"; | |||
| @@ -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=""): | |||
| @@ -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 | |||
| @@ -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.""" | |||
| @@ -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" | |||
| @@ -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" | |||
| @@ -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" | |||
| @@ -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<Dataset> ds = ImageFolder(folder_path, true, std::make_shared<RandomSampler>(false, 10)); | |||
| EXPECT_NE(ds, nullptr); | |||
| // Case 1: Size of center is not 2 | |||
| // Create objects for the tensor ops | |||
| std::shared_ptr<TensorTransform> 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<Iterator> 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<TensorTransform> 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<Iterator> 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<Dataset> ds = ImageFolder(folder_path, true, std::make_shared<RandomSampler>(false, 10)); | |||
| EXPECT_NE(ds, nullptr); | |||
| // Create objects for the tensor ops | |||
| std::shared_ptr<TensorTransform> resize(new vision::Resize({50, 25})); | |||
| std::shared_ptr<TensorTransform> 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<Iterator> iter = ds->CreateIterator(); | |||
| EXPECT_NE(iter, nullptr); | |||
| // Iterate the dataset and get each row | |||
| std::unordered_map<std::string, mindspore::MSTensor> 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(); | |||
| } | |||
| @@ -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" | |||
| @@ -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()); | |||
| } | |||
| @@ -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() | |||
| @@ -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() | |||
| @@ -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 [<class 'numbers.Number'>]" 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() | |||
| @@ -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() | |||