diff --git a/mindspore/ccsrc/dataset/api/python_bindings.cc b/mindspore/ccsrc/dataset/api/python_bindings.cc index 214ce4c1537ac658b15947ea7f1e72e3c3115454..655fad7d55feba6409a92af1b7c3d8257192fea9 100644 --- a/mindspore/ccsrc/dataset/api/python_bindings.cc +++ b/mindspore/ccsrc/dataset/api/python_bindings.cc @@ -40,6 +40,7 @@ #include "dataset/kernels/image/rescale_op.h" #include "dataset/kernels/image/resize_bilinear_op.h" #include "dataset/kernels/image/resize_op.h" +#include "dataset/kernels/image/uniform_aug_op.h" #include "dataset/kernels/data/type_cast_op.h" #include "dataset/engine/datasetops/source/cifar_op.h" #include "dataset/engine/datasetops/source/image_folder_op.h" @@ -264,6 +265,10 @@ void bindTensorOps1(py::module *m) { .def(py::init(), py::arg("targetHeight"), py::arg("targetWidth") = ResizeOp::kDefWidth, py::arg("interpolation") = ResizeOp::kDefInterpolation); + (void)py::class_>( + *m, "UniformAugOp", "Tensor operation to apply random augmentation(s).") + .def(py::init(), py::arg("operations"), py::arg("NumOps") = UniformAugOp::kDefNumOps); + (void)py::class_>( *m, "ResizeBilinearOp", "Tensor operation to resize an image using " diff --git a/mindspore/ccsrc/dataset/kernels/image/CMakeLists.txt b/mindspore/ccsrc/dataset/kernels/image/CMakeLists.txt index 23a26d5214aca3de388b304c976931047c42275a..33e681337c9b131cd9dba5c625d18a734644d70f 100644 --- a/mindspore/ccsrc/dataset/kernels/image/CMakeLists.txt +++ b/mindspore/ccsrc/dataset/kernels/image/CMakeLists.txt @@ -19,6 +19,7 @@ if (WIN32) rescale_op.cc resize_bilinear_op.cc resize_op.cc + uniform_aug_op.cc ) else() add_library(kernels-image OBJECT @@ -42,5 +43,6 @@ else() rescale_op.cc resize_bilinear_op.cc resize_op.cc + uniform_aug_op.cc ) endif() diff --git a/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.cc b/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.cc new file mode 100644 index 0000000000000000000000000000000000000000..5725c10908031e3f550caaf49c822a890d806107 --- /dev/null +++ b/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.cc @@ -0,0 +1,87 @@ +/** + * Copyright 2020 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. +*/ +#include "dataset/kernels/image/uniform_aug_op.h" +#include "dataset/kernels/py_func_op.h" +#include "dataset/util/random.h" + +namespace mindspore { +namespace dataset { +const int UniformAugOp::kDefNumOps = 2; + +UniformAugOp::UniformAugOp(py::list op_list, int32_t num_ops) : num_ops_(num_ops) { + std::shared_ptr tensor_op; + // iterate over the op list, cast them to TensorOp and add them to tensor_op_list_ + for (auto op : op_list) { + if (py::isinstance(op)) { + // python op + tensor_op = std::make_shared(op.cast()); + } else if (py::isinstance(op)) { + // C++ op + tensor_op = op.cast>(); + } + tensor_op_list_.insert(tensor_op_list_.begin(), tensor_op); + } + + rnd_.seed(GetSeed()); +} +// compute method to apply uniformly random selected augmentations from a list +Status UniformAugOp::Compute(const std::vector> &input, + std::vector> *output) { + IO_CHECK_VECTOR(input, output); + + // variables to generate random number to select ops from the list + std::vector random_indexes; + + // variables to copy the result to output if it is not already + std::vector> even_out; + std::vector> *even_out_ptr = &even_out; + int count = 1; + + // select random indexes for candidates to be applied + for (int i = 0; i < num_ops_; ++i) { + random_indexes.insert(random_indexes.end(), + std::uniform_int_distribution(0, tensor_op_list_.size() - 1)(rnd_)); + } + + for (auto it = random_indexes.begin(); it != random_indexes.end(); ++it) { + // Do NOT apply the op, if second random generator returned zero + if (std::uniform_int_distribution(0, 1)(rnd_)) { + continue; + } + std::shared_ptr tensor_op = tensor_op_list_[*it]; + + // apply python/C++ op + if (count == 1) { + (*tensor_op).Compute(input, output); + } else if (count % 2 == 0) { + (*tensor_op).Compute(*output, even_out_ptr); + } else { + (*tensor_op).Compute(even_out, output); + } + count++; + } + + // copy the result to output if it is not in output + if (count == 1) { + *output = input; + } else if ((count % 2 == 1)) { + (*output).swap(even_out); + } + + return Status::OK(); +} +} // namespace dataset +} // namespace mindspore diff --git a/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.h b/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.h new file mode 100644 index 0000000000000000000000000000000000000000..336bc8c859820f9963726adff8fa8fbfa97ae40d --- /dev/null +++ b/mindspore/ccsrc/dataset/kernels/image/uniform_aug_op.h @@ -0,0 +1,60 @@ +/** + * Copyright 2020 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. +*/ +#ifndef DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ +#define DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ + +#include +#include +#include +#include + +#include "dataset/core/tensor.h" +#include "dataset/kernels/tensor_op.h" +#include "dataset/util/status.h" +#include "dataset/kernels/py_func_op.h" + +#include "pybind11/stl.h" + +namespace mindspore { +namespace dataset { +class UniformAugOp : public TensorOp { + public: + // Default number of Operations to be applied + static const int kDefNumOps; + + // Constructor for UniformAugOp + // @param list op_list: list of candidate python operations + // @param list num_ops: number of augemtation operations to applied + UniformAugOp(py::list op_list, int32_t num_ops); + + ~UniformAugOp() override = default; + + void Print(std::ostream &out) const override { out << "UniformAugOp:: number of ops " << num_ops_; } + + // Overrides the base class compute function + // @return Status - The error code return + Status Compute(const std::vector> &input, + std::vector> *output) override; + + private: + int32_t num_ops_; + std::vector> tensor_op_list_; + std::mt19937 rnd_; +}; +} // namespace dataset +} // namespace mindspore + +#endif // DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ diff --git a/mindspore/dataset/transforms/vision/c_transforms.py b/mindspore/dataset/transforms/vision/c_transforms.py index 171eb846a80fa4d2eb58be4172e96a827ddc064e..07011b1d53a1da80664612078633a9614ce02cde 100644 --- a/mindspore/dataset/transforms/vision/c_transforms.py +++ b/mindspore/dataset/transforms/vision/c_transforms.py @@ -45,7 +45,7 @@ import mindspore._c_dataengine as cde from .utils import Inter, Border from .validators import check_prob, check_crop, check_resize_interpolation, check_random_resize_crop, \ check_normalize_c, check_random_crop, check_random_color_adjust, check_random_rotation, \ - check_resize, check_rescale, check_pad, check_cutout + check_resize, check_rescale, check_pad, check_cutout, check_uniform_augmentation DE_C_INTER_MODE = {Inter.NEAREST: cde.InterpolationMode.DE_INTER_NEAREST_NEIGHBOUR, Inter.LINEAR: cde.InterpolationMode.DE_INTER_LINEAR, @@ -447,3 +447,19 @@ class Pad(cde.PadOp): fill_value = tuple([fill_value] * 3) padding_mode = DE_C_BORDER_TYPE[padding_mode] super().__init__(*padding, padding_mode, *fill_value) + + +class UniformAugment(cde.UniformAugOp): + """ + Tensor operation to perform randomly selected augmentation + + Args: + operations: list of python operations. + NumOps (int): number of OPs to be selected and applied. + """ + + @check_uniform_augmentation + def __init__(self, operations, num_ops=2): + self.operations = operations + self.num_ops = num_ops + super().__init__(operations, num_ops) diff --git a/mindspore/dataset/transforms/vision/validators.py b/mindspore/dataset/transforms/vision/validators.py index ef4b879f8cde3cf782b5722b817dd54954eb3b56..713d9c5714111e1dbe7dad27ed409bfc398e03d0 100644 --- a/mindspore/dataset/transforms/vision/validators.py +++ b/mindspore/dataset/transforms/vision/validators.py @@ -812,3 +812,36 @@ def check_rescale(method): return method(self, **kwargs) return new_method + + +def check_uniform_augmentation(method): + """Wrapper method to check the parameters of UniformAugmentation.""" + + @wraps(method) + def new_method(self, *args, **kwargs): + operations, num_ops = (list(args) + 2 * [None])[:2] + if "operations" in kwargs: + operations = kwargs.get("operations") + else: + raise ValueError("operations list required") + if "num_ops" in kwargs: + num_ops = kwargs.get("num_ops") + else: + num_ops = 2 + + if num_ops <= 0: + raise ValueError("num_ops should be greater than zero") + if num_ops > len(operations): + raise ValueError("num_ops is greater than operations list size") + if not isinstance(operations, list): + raise ValueError("operations is not a python list") + for op in operations: + if not callable(op): + raise ValueError("non-callable op in operations list") + + kwargs["num_ops"] = num_ops + kwargs["operations"] = operations + + return method(self, **kwargs) + + return new_method