Merge pull request !511 from AdelShafiei/uniform_augmentationtags/v0.2.0-alpha
| @@ -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" | |||
| @@ -270,6 +271,10 @@ void bindTensorOps1(py::module *m) { | |||
| .def(py::init<int32_t, int32_t, InterpolationMode>(), py::arg("targetHeight"), | |||
| py::arg("targetWidth") = ResizeOp::kDefWidth, py::arg("interpolation") = ResizeOp::kDefInterpolation); | |||
| (void)py::class_<UniformAugOp, TensorOp, std::shared_ptr<UniformAugOp>>( | |||
| *m, "UniformAugOp", "Tensor operation to apply random augmentation(s).") | |||
| .def(py::init<py::list, int32_t>(), py::arg("operations"), py::arg("NumOps") = UniformAugOp::kDefNumOps); | |||
| (void)py::class_<ResizeBilinearOp, TensorOp, std::shared_ptr<ResizeBilinearOp>>( | |||
| *m, "ResizeBilinearOp", | |||
| "Tensor operation to resize an image using " | |||
| @@ -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() | |||
| @@ -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<TensorOp> 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<py::function>(op)) { | |||
| // python op | |||
| tensor_op = std::make_shared<PyFuncOp>(op.cast<py::function>()); | |||
| } else if (py::isinstance<TensorOp>(op)) { | |||
| // C++ op | |||
| tensor_op = op.cast<std::shared_ptr<TensorOp>>(); | |||
| } | |||
| 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<std::shared_ptr<Tensor>> &input, | |||
| std::vector<std::shared_ptr<Tensor>> *output) { | |||
| IO_CHECK_VECTOR(input, output); | |||
| // variables to generate random number to select ops from the list | |||
| std::vector<int> random_indexes; | |||
| // variables to copy the result to output if it is not already | |||
| std::vector<std::shared_ptr<Tensor>> even_out; | |||
| std::vector<std::shared_ptr<Tensor>> *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<int>(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<int>(0, 1)(rnd_)) { | |||
| continue; | |||
| } | |||
| std::shared_ptr<TensorOp> 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 | |||
| @@ -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 <memory> | |||
| #include <random> | |||
| #include <string> | |||
| #include <vector> | |||
| #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<std::shared_ptr<Tensor>> &input, | |||
| std::vector<std::shared_ptr<Tensor>> *output) override; | |||
| private: | |||
| int32_t num_ops_; | |||
| std::vector<std::shared_ptr<TensorOp>> tensor_op_list_; | |||
| std::mt19937 rnd_; | |||
| }; | |||
| } // namespace dataset | |||
| } // namespace mindspore | |||
| #endif // DATASET_KERNELS_IMAGE_UNIFORM_AUG_OP_H_ | |||
| @@ -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) | |||
| @@ -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 | |||