From 4191dde45f460fd90dd19a72c1f965e5dfbc49e0 Mon Sep 17 00:00:00 2001 From: chenx2ovo <417994009@qq.com> Date: Mon, 2 Aug 2021 18:09:40 +0800 Subject: [PATCH] [feat][assistant][I3CEGM] add op adjust gamma --- .../dataset/kernels/ir/image/bindings.cc | 12 + .../ccsrc/minddata/dataset/api/vision.cc | 14 + .../minddata/dataset/include/dataset/vision.h | 26 +- .../dataset/kernels/image/CMakeLists.txt | 1 + .../dataset/kernels/image/adjust_gamma_op.cc | 43 +++ .../dataset/kernels/image/adjust_gamma_op.h | 55 +++ .../dataset/kernels/image/image_utils.cc | 58 +++ .../dataset/kernels/image/image_utils.h | 10 + .../minddata/dataset/kernels/ir/validators.cc | 5 + .../minddata/dataset/kernels/ir/validators.h | 3 + .../dataset/kernels/ir/vision/CMakeLists.txt | 1 + .../kernels/ir/vision/adjust_gamma_ir.cc | 58 +++ .../kernels/ir/vision/adjust_gamma_ir.h | 60 ++++ .../minddata/dataset/kernels/tensor_op.h | 1 + mindspore/dataset/vision/c_transforms.py | 33 +- mindspore/dataset/vision/py_transforms.py | 42 ++- .../dataset/vision/py_transforms_util.py | 28 +- mindspore/dataset/vision/validators.py | 24 +- .../cpp/dataset/c_api_vision_a_to_q_test.cc | 96 +++++ tests/ut/cpp/dataset/common/cvop_common.cc | 4 + tests/ut/cpp/dataset/common/cvop_common.h | 1 + tests/ut/cpp/dataset/execute_test.cc | 29 ++ tests/ut/python/dataset/test_adjustgamma.py | 329 ++++++++++++++++++ 23 files changed, 924 insertions(+), 9 deletions(-) create mode 100644 mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc create mode 100644 mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.h create mode 100644 mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc create mode 100644 mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h create mode 100644 tests/ut/python/dataset/test_adjustgamma.py 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 50c427633f..524b1cd432 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 @@ -18,6 +18,7 @@ #include "minddata/dataset/api/python/pybind_register.h" #include "minddata/dataset/include/dataset/transforms.h" +#include "minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" #include "minddata/dataset/kernels/ir/vision/auto_contrast_ir.h" #include "minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h" #include "minddata/dataset/kernels/ir/vision/center_crop_ir.h" @@ -67,6 +68,17 @@ namespace mindspore { namespace dataset { +PYBIND_REGISTER( + AdjustGammaOperation, 1, ([](const py::module *m) { + (void)py::class_>( + *m, "AdjustGammaOperation") + .def(py::init([](float gamma, float gain) { + auto ajust_gamma = std::make_shared(gamma, gain); + THROW_IF_ERROR(ajust_gamma->ValidateParams()); + return ajust_gamma; + })); + })); + PYBIND_REGISTER( AutoContrastOperation, 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 c451cd19cc..e933e5fb44 100644 --- a/mindspore/ccsrc/minddata/dataset/api/vision.cc +++ b/mindspore/ccsrc/minddata/dataset/api/vision.cc @@ -21,6 +21,7 @@ #endif #include "minddata/dataset/include/dataset/transforms.h" +#include "minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" #include "minddata/dataset/kernels/ir/vision/affine_ir.h" #include "minddata/dataset/kernels/ir/vision/auto_contrast_ir.h" #include "minddata/dataset/kernels/ir/vision/bounding_box_augment_ir.h" @@ -118,6 +119,19 @@ std::shared_ptr Affine::Parse() { } #ifndef ENABLE_ANDROID +// AdjustGamma Transform Operation. +struct AdjustGamma::Data { + Data(float gamma, float gain) : gamma_(gamma), gain_(gain) {} + float gamma_; + float gain_; +}; + +AdjustGamma::AdjustGamma(float gamma, float gain) : data_(std::make_shared(gamma, gain)) {} + +std::shared_ptr AdjustGamma::Parse() { + return std::make_shared(data_->gamma_, data_->gain_); +} + // AutoContrast Transform Operation. struct AutoContrast::Data { Data(float cutoff, const std::vector &ignore) : cutoff_(cutoff), ignore_(ignore) {} diff --git a/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h b/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h index 0af4b98efb..c16b6e9e22 100644 --- a/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h +++ b/mindspore/ccsrc/minddata/dataset/include/dataset/vision.h @@ -57,7 +57,31 @@ class AutoContrast final : public TensorTransform { std::shared_ptr data_; }; -/// \brief Apply a given image transform on a random selection of bounding box regions of a given image. +/// \brief AdjustGamma TensorTransform. +/// \notes Apply gamma correction on input image. +class AdjustGamma final : public TensorTransform { + public: + /// \brief Constructor. + /// \param[in] gamma Non negative real number, which makes the output image pixel value + /// exponential in relation to the input image pixel value. + /// \param[in] gain The constant multiplier. + explicit AdjustGamma(float gamma, float gain = 1); + + /// \brief Destructor. + ~AdjustGamma() = default; + + protected: + /// \brief Function to convert TensorTransform object into a TensorOperation object. + /// \return Shared pointer to TensorOperation object. + std::shared_ptr Parse() override; + + private: + struct Data; + std::shared_ptr data_; +}; + +/// \brief BoundingBoxAugment TensorTransform. +/// \note Apply a given image transform on a random selection of bounding box regions of a given image. class BoundingBoxAugment final : public TensorTransform { public: /// \brief Constructor. diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/CMakeLists.txt b/mindspore/ccsrc/minddata/dataset/kernels/image/CMakeLists.txt index 8ef4bf82d8..47c4c2c8f7 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/CMakeLists.txt +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/CMakeLists.txt @@ -6,6 +6,7 @@ if(ENABLE_ACL) add_subdirectory(dvpp) endif() add_library(kernels-image OBJECT + adjust_gamma_op.cc affine_op.cc auto_contrast_op.cc bounding_box.cc diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc new file mode 100644 index 0000000000..3698482fe5 --- /dev/null +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc @@ -0,0 +1,43 @@ +/** + * 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. + */ + +#include "minddata/dataset/kernels/image/adjust_gamma_op.h" +#include +#include "minddata/dataset/kernels/data/data_utils.h" +#include "minddata/dataset/kernels/image/image_utils.h" + +namespace mindspore { +namespace dataset { + +const float AdjustGammaOp::kGain = 1.0; + +Status AdjustGammaOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + IO_CHECK(input, output); + + // typecast + CHECK_FAIL_RETURN_UNEXPECTED(input->type() != DataType::DE_STRING, + "AdjustGamma: input tensor type should be [int, float, double], but got string."); + + if (input->type().IsFloat()) { + std::shared_ptr input_tensor; + RETURN_IF_NOT_OK(TypeCast(input, &input_tensor, DataType(DataType::DE_FLOAT32))); + return AdjustGamma(input_tensor, output, gamma_, gain_); + } else { + return AdjustGamma(input, output, gamma_, gain_); + } +} +} // namespace dataset +} // namespace mindspore diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.h b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.h new file mode 100644 index 0000000000..c2b4c175a8 --- /dev/null +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.h @@ -0,0 +1,55 @@ +/** + * 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. + */ + +#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ +#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ + +#include +#include + +#include "minddata/dataset/core/tensor.h" +#include "minddata/dataset/core/cv_tensor.h" +#include "minddata/dataset/kernels/tensor_op.h" +#include "minddata/dataset/util/status.h" + +namespace mindspore { +namespace dataset { +class AdjustGammaOp : public TensorOp { + public: + /// Default gain to be used + static const float kGain; + AdjustGammaOp(const float &gamma, const float &gain) : gamma_(gamma), gain_(gain) {} + + ~AdjustGammaOp() override = default; + + /// Provide stream operator for displaying it + friend std::ostream &operator<<(std::ostream &out, const AdjustGammaOp &so) { + so.Print(out); + return out; + } + + Status Compute(const std::shared_ptr &input, std::shared_ptr *output) override; + + std::string Name() const override { return kAdjustGammaOp; } + + private: + float gamma_; + float gain_; +}; +} // namespace dataset +} // namespace mindspore + +#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IMAGE_ADJUST_GAMMA_OP_H_ diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc index 0478382e27..1a0c2ebb4f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc @@ -872,6 +872,64 @@ Status AdjustContrast(const std::shared_ptr &input, std::shared_ptr &input, std::shared_ptr *output, const float &gamma, + const float &gain) { + try { + int num_channels = 1; + if (input->Rank() < 2) { + RETURN_STATUS_UNEXPECTED("AdjustGamma: image shape is not <...,H,W,C> or ."); + } + if (input->Rank() > 2) { + num_channels = input->shape()[-1]; + } + if (num_channels != 1 && num_channels != 3) { + RETURN_STATUS_UNEXPECTED("AdjustGamma: channel of input image should be 1 or 3."); + } + if (input->type().IsFloat()) { + for (auto itr = input->begin(); itr != input->end(); itr++) { + *itr = pow((*itr) * gain, gamma); + *itr = std::min(std::max((*itr), 0.0f), 1.0f); + } + *output = input; + + } else { + std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + if (!input_cv->mat().data) { + RETURN_STATUS_UNEXPECTED("AdjustGamma: load image failed."); + } + cv::Mat input_img = input_cv->mat(); + std::shared_ptr output_cv; + RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + uchar LUT[256] = {}; + for (int i = 0; i < 256; i++) { + float f = i / 255.0; + f = pow(f, gamma); + LUT[i] = static_cast(floor(std::min(f * (255.0 + 1 - 1e-3) * gain, 255.0))); + } + if (input_img.channels() == 1) { + cv::MatIterator_ it = input_img.begin(); + cv::MatIterator_ it_end = input_img.end(); + for (; it != it_end; ++it) { + *it = LUT[(*it)]; + } + } else { + cv::MatIterator_ it = input_img.begin(); + cv::MatIterator_ it_end = input_img.end(); + for (; it != it_end; ++it) { + (*it)[0] = LUT[(*it)[0]]; + (*it)[1] = LUT[(*it)[1]]; + (*it)[2] = LUT[(*it)[2]]; + } + } + output_cv->mat() = input_img * 1; + *output = std::static_pointer_cast(output_cv); + } + } catch (const cv::Exception &e) { + RETURN_STATUS_UNEXPECTED("AdjustGamma: " + std::string(e.what())); + } + return Status::OK(); +} + Status AutoContrast(const std::shared_ptr &input, std::shared_ptr *output, const float &cutoff, const std::vector &ignore) { try { diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.h b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.h index a26671db49..6886f274bb 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.h @@ -234,6 +234,16 @@ Status AdjustContrast(const std::shared_ptr &input, std::shared_ptr &input, std::shared_ptr *output, const float &cutoff, const std::vector &ignore); +/// \brief Returns image with gamma correction. +/// \param[in] input: Tensor of shape // in RGB/Grayscale and any OpenCV compatible type, +/// see CVTensor. +/// \param[in] gamma: Non negative real number, same as gamma in the equation. gamma larger than 1 make the shadows +/// darker, while gamma smaller than 1 make dark regions lighter. +/// \param[in] gain: The constant multiplier. +/// \param[out] output: Adjusted image of same shape and type. +Status AdjustGamma(const std::shared_ptr &input, std::shared_ptr *output, const float &gamma, + const float &gain); + /// \brief Returns image with adjusted saturation. /// \param input: Tensor of shape in RGB order and any OpenCv compatible type, see CVTensor. /// \param alpha: Alpha value to adjust saturation by. Should be a positive number. diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.cc b/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.cc index 668337777c..0bc911024f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.cc @@ -38,6 +38,11 @@ Status ValidateFloatScalarPositive(const std::string &op_name, const std::string return Status::OK(); } +Status ValidateFloatScalarNonNegative(const std::string &op_name, const std::string &scalar_name, float scalar) { + RETURN_IF_NOT_OK(ValidateScalar(op_name, scalar_name, scalar, {0}, false)); + return Status::OK(); +} + Status ValidateVectorFillvalue(const std::string &op_name, const std::vector &fill_value) { if (fill_value.empty() || (fill_value.size() != 1 && fill_value.size() != 3)) { std::string err_msg = diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.h b/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.h index d420377bb0..72bbaf570e 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/validators.h @@ -36,6 +36,9 @@ Status ValidateIntScalarPositive(const std::string &op_name, const std::string & // Helper function to positive float scalar Status ValidateFloatScalarPositive(const std::string &op_name, const std::string &scalar_name, float scalar); +// Helper function to non-negative float scalar +Status ValidateFloatScalarNonNegative(const std::string &op_name, const std::string &scalar_name, float scalar); + // Helper function to validate scalar template Status ValidateScalar(const std::string &op_name, const std::string &scalar_name, const T scalar, diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/CMakeLists.txt b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/CMakeLists.txt index d46a9bfe52..7a241b89ed 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/CMakeLists.txt +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/CMakeLists.txt @@ -2,6 +2,7 @@ file(GLOB_RECURSE _CURRENT_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc" set_property(SOURCE ${_CURRENT_SRC_FILES} PROPERTY COMPILE_DEFINITIONS SUBMODULE_ID=mindspore::SubModuleId::SM_MD) set(DATASET_KERNELS_IR_VISION_SRC_FILES + adjust_gamma_ir.cc affine_ir.cc auto_contrast_ir.cc bounding_box_augment_ir.cc diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc new file mode 100644 index 0000000000..8b81888f96 --- /dev/null +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.cc @@ -0,0 +1,58 @@ +/** + * 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. + */ +#include + +#include "minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h" + +#ifndef ENABLE_ANDROID +#include "minddata/dataset/kernels/image/adjust_gamma_op.h" +#endif + +#include "minddata/dataset/kernels/ir/validators.h" + +namespace mindspore { +namespace dataset { + +namespace vision { + +#ifndef ENABLE_ANDROID + +// AdjustGammaOperation +AdjustGammaOperation::AdjustGammaOperation(float gamma, float gain) : gamma_(gamma), gain_(gain) {} + +Status AdjustGammaOperation::ValidateParams() { + // gamma + RETURN_IF_NOT_OK(ValidateFloatScalarNonNegative("AdjustGamma", "gamma", gamma_)); + return Status::OK(); +} + +std::shared_ptr AdjustGammaOperation::Build() { + std::shared_ptr tensor_op = std::make_shared(gamma_, gain_); + return tensor_op; +} + +Status AdjustGammaOperation::to_json(nlohmann::json *out_json) { + nlohmann::json args; + args["gamma"] = gamma_; + args["gain"] = gain_; + *out_json = args; + return Status::OK(); +} +#endif + +} // namespace vision +} // namespace dataset +} // namespace mindspore diff --git a/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h new file mode 100644 index 0000000000..7a103f2bd1 --- /dev/null +++ b/mindspore/ccsrc/minddata/dataset/kernels/ir/vision/adjust_gamma_ir.h @@ -0,0 +1,60 @@ +/** + * 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. + */ + +#ifndef MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ +#define MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ + +#include +#include +#include +#include +#include + +#include "include/api/status.h" +#include "minddata/dataset/include/dataset/constants.h" +#include "minddata/dataset/include/dataset/transforms.h" +#include "minddata/dataset/kernels/ir/tensor_operation.h" + +namespace mindspore { +namespace dataset { + +namespace vision { + +constexpr char kAdjustGammaOperation[] = "AdjustGamma"; + +class AdjustGammaOperation : public TensorOperation { + public: + explicit AdjustGammaOperation(float gamma, float gain); + + ~AdjustGammaOperation() = default; + + std::shared_ptr Build() override; + + Status ValidateParams() override; + + std::string Name() const override { return kAdjustGammaOperation; } + + Status to_json(nlohmann::json *out_json) override; + + private: + float gamma_; + float gain_; +}; + +} // namespace vision +} // namespace dataset +} // namespace mindspore +#endif // MINDSPORE_CCSRC_MINDDATA_DATASET_KERNELS_IR_VISION_ADJUST_GAMMA_IR_H_ diff --git a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h index 0f923a00a8..04bb0e16cc 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h +++ b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.h @@ -53,6 +53,7 @@ namespace dataset { constexpr char kTensorOp[] = "TensorOp"; // image +constexpr char kAdjustGammaOp[] = "AdjustGammaOp"; constexpr char kAffineOp[] = "AffineOp"; constexpr char kAutoContrastOp[] = "AutoContrastOp"; constexpr char kBoundingBoxAugmentOp[] = "BoundingBoxAugmentOp"; diff --git a/mindspore/dataset/vision/c_transforms.py b/mindspore/dataset/vision/c_transforms.py index fd6e1a0c2a..1f7fb720e8 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_center_crop, check_resize_ 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_rotate, check_slice_patches + check_cut_mix_batch_c, check_posterize, check_gaussian_blur, check_rotate, check_slice_patches, check_adjust_gamma from ..transforms.c_transforms import TensorOperation @@ -107,6 +107,37 @@ def parse_padding(padding): return padding +class AdjustGamma(ImageTensorOperation): + r""" + Apply gamma correction on input image. Input image is expected to be in [..., H, W, C] or [H, W, C] format. + .. math:: + I_{\text{out}} = 255 \times \text{gain} \times \left(\frac{I_{\text{in}}}{255}\right)^{\gamma} + + See `Gamma Correction`_ for more details. + + .. _Gamma Correction: https://en.wikipedia.org/wiki/Gamma_correction + + Args: + gamma (float): Non negative real number. + The output image pixel value is exponentially related to the input image pixel value. + gamma larger than 1 make the shadows darker, + while gamma smaller than 1 make dark regions lighter. + gain (float, optional): The constant multiplier (default=1). + + Examples: + >>> transforms_list = [c_vision.Decode(), c_vision.AdjustGamma(gamma=10.0, gain=1.0)] + >>> image_folder_dataset = image_folder_dataset.map(operations=transforms_list, + ... input_columns=["image"]) + """ + @check_adjust_gamma + def __init__(self, gamma, gain=1): + self.gamma = gamma + self.gain = gain + + def parse(self): + return cde.AdjustGammaOperation(self.gamma, self.gain) + + class AutoContrast(ImageTensorOperation): """ Apply automatic contrast on input image. This operator calculates histogram of image, reassign cutoff percent diff --git a/mindspore/dataset/vision/py_transforms.py b/mindspore/dataset/vision/py_transforms.py index af0ae88bc8..989d53c7a3 100644 --- a/mindspore/dataset/vision/py_transforms.py +++ b/mindspore/dataset/vision/py_transforms.py @@ -31,7 +31,8 @@ from .validators import check_prob, check_center_crop, check_five_crop, check_re check_normalize_py, check_normalizepad_py, check_random_crop, check_random_color_adjust, check_random_rotation, \ check_ten_crop, check_num_channels, check_pad, check_rgb_to_hsv, check_hsv_to_rgb, \ check_random_perspective, check_random_erasing, check_cutout, check_linear_transform, check_random_affine, \ - check_mix_up, check_positive_degrees, check_uniform_augment_py, check_auto_contrast, check_rgb_to_bgr + check_mix_up, check_positive_degrees, check_uniform_augment_py, check_auto_contrast, check_rgb_to_bgr, \ + check_adjust_gamma from .utils import Inter, Border from .py_transforms_util import is_pil @@ -1375,7 +1376,6 @@ class RgbToBgr: return util.rgb_to_bgrs(rgb_imgs, self.is_hwc) - class RgbToHsv: """ Convert a NumPy RGB image or a batch of NumPy RGB images to HSV images. @@ -1525,6 +1525,44 @@ class RandomSharpness: return util.random_sharpness(img, self.degrees) +class AdjustGamma: + """ + Adjust gamma of the input PIL image. + + Args: + gamma (float): Non negative real number, same as gamma in the equation. + gain (float, optional): The constant multiplier. + + Examples: + >>> from mindspore.dataset.transforms.py_transforms import Compose + >>> transforms_list = Compose([py_vision.Decode(), + ... py_vision.AdjustGamma(), + ... py_vision.ToTensor()]) + >>> # apply the transform to dataset through map function + >>> image_folder_dataset = image_folder_dataset.map(operations=transforms_list, + ... input_columns="image") + """ + + @check_adjust_gamma + def __init__(self, gamma, gain=1.0): + self.gamma = gamma + self.gain = gain + self.random = False + + def __call__(self, img): + """ + Call method. + + Args: + img (PIL image): Image to be augmented with AutoContrast. + + Returns: + img (PIL image), Augmented image. + """ + + return util.adjust_gamma(img, self.gamma, self.gain) + + class AutoContrast: """ Automatically maximize the contrast of the input PIL image. diff --git a/mindspore/dataset/vision/py_transforms_util.py b/mindspore/dataset/vision/py_transforms_util.py index 475a4bab9b..48ed345783 100644 --- a/mindspore/dataset/vision/py_transforms_util.py +++ b/mindspore/dataset/vision/py_transforms_util.py @@ -19,7 +19,6 @@ import math import numbers import random import colorsys - import numpy as np from PIL import Image, ImageOps, ImageEnhance, __version__ @@ -1243,6 +1242,7 @@ def rgb_to_bgr(np_rgb_img, is_hwc): np_bgr_img = np_rgb_img[::-1, :, :] return np_bgr_img + def rgb_to_bgrs(np_rgb_imgs, is_hwc): """ Convert RGB imgs to BGR imgs. @@ -1473,6 +1473,32 @@ def random_sharpness(img, degrees): return ImageEnhance.Sharpness(img).enhance(v) +def adjust_gamma(img, gamma, gain): + """ + Adjust gamma of the input PIL image. + + Args: + img (PIL image): Image to be augmented with AdjustGamma. + gamma (float): Non negative real number, same as gamma in the equation. + gain (float, optional): The constant multiplier. + + Returns: + img (PIL image), Augmented image. + + """ + + if not is_pil(img): + raise TypeError("img should be PIL image. Got {}.".format(type(img))) + + gamma_table = [(255 + 1 - 1e-3) * gain * pow(x / 255., gamma) for x in range(256)] + if len(img.split()) == 3: + gamma_table = gamma_table * 3 + img = img.point(gamma_table) + elif len(img.split()) == 1: + img = img.point(gamma_table) + return img + + def auto_contrast(img, cutoff, ignore): """ Automatically maximize the contrast of the input PIL image. diff --git a/mindspore/dataset/vision/validators.py b/mindspore/dataset/vision/validators.py index baecbabce7..d8d7b84385 100644 --- a/mindspore/dataset/vision/validators.py +++ b/mindspore/dataset/vision/validators.py @@ -19,10 +19,10 @@ from functools import wraps import numpy as np from mindspore._c_dataengine import TensorOp, TensorOperation -from mindspore.dataset.core.validator_helpers import check_value, check_uint8, FLOAT_MAX_INTEGER, check_pos_float32, \ - check_float32, check_2tuple, check_range, check_positive, INT32_MAX, INT32_MIN, parse_user_args, type_check, \ - type_check_list, check_c_tensor_op, UINT8_MAX, check_value_normalize_std, check_value_cutoff, check_value_ratio, \ - check_odd +from mindspore.dataset.core.validator_helpers import check_value, check_uint8, FLOAT_MIN_INTEGER, FLOAT_MAX_INTEGER, \ + check_pos_float32, check_float32, check_2tuple, check_range, check_positive, INT32_MAX, INT32_MIN, \ + parse_user_args, type_check, type_check_list, check_c_tensor_op, UINT8_MAX, check_value_normalize_std, \ + check_value_cutoff, check_value_ratio, check_odd from .utils import Inter, Border, ImageBatchFormat, SliceMode @@ -788,6 +788,22 @@ def check_bounding_box_augment_cpp(method): return new_method +def check_adjust_gamma(method): + """Wrapper method to check the parameters of AdjustGamma ops (Python and C++).""" + + @wraps(method) + def new_method(self, *args, **kwargs): + [gamma, gain], _ = parse_user_args(method, *args, **kwargs) + type_check(gamma, (float, int), "gamma") + check_value(gamma, (0, FLOAT_MAX_INTEGER)) + if gain is not None: + type_check(gain, (float, int), "gain") + check_value(gain, (FLOAT_MIN_INTEGER, FLOAT_MAX_INTEGER)) + return method(self, *args, **kwargs) + + return new_method + + def check_auto_contrast(method): """Wrapper method to check the parameters of AutoContrast ops (Python and C++).""" diff --git a/tests/ut/cpp/dataset/c_api_vision_a_to_q_test.cc b/tests/ut/cpp/dataset/c_api_vision_a_to_q_test.cc index 33149fd852..0647ae7bdd 100644 --- a/tests/ut/cpp/dataset/c_api_vision_a_to_q_test.cc +++ b/tests/ut/cpp/dataset/c_api_vision_a_to_q_test.cc @@ -27,6 +27,102 @@ class MindDataTestPipeline : public UT::DatasetOpTesting { // Tests for vision C++ API A to Q TensorTransform Operations (in alphabetical order) +TEST_F(MindDataTestPipeline, TestAdjustGammaSuccess1) { + // pipeline 3-channel + MS_LOG(INFO) << "Pipeline Test."; + std::string MindDataPath = "data/dataset"; + std::string folder_path = MindDataPath + "/testImageNetData/train/"; + std::shared_ptr ds1 = ImageFolder(folder_path, true, std::make_shared(false, 2)); + EXPECT_NE(ds1, nullptr); + std::shared_ptr ds2 = ImageFolder(folder_path, true, std::make_shared(false, 2)); + EXPECT_NE(ds2, nullptr); + + auto adjustgamma_op = vision::AdjustGamma(10.0); + + ds1 = ds1->Map({adjustgamma_op}); + EXPECT_NE(ds1, nullptr); + + std::shared_ptr iter1 = ds1->CreateIterator(); + EXPECT_NE(iter1, nullptr); + std::unordered_map row1; + iter1->GetNextRow(&row1); + + std::shared_ptr iter2 = ds2->CreateIterator(); + EXPECT_NE(iter2, nullptr); + std::unordered_map row2; + iter2->GetNextRow(&row2); + + uint64_t i = 0; + while (row1.size() != 0) { + i++; + auto image = row1["image"]; + iter1->GetNextRow(&row1); + iter2->GetNextRow(&row2); + } + EXPECT_EQ(i, 2); + + iter1->Stop(); + iter2->Stop(); +} + +TEST_F(MindDataTestPipeline, TestAdjustGammaSuccess2) { + // pipeline 1-channel + MS_LOG(INFO) << "Pipeline Test."; + std::string MindDataPath = "data/dataset"; + std::string folder_path = MindDataPath + "/testImageNetData/train/"; + std::shared_ptr ds1 = ImageFolder(folder_path, true, std::make_shared(false, 2)); + EXPECT_NE(ds1, nullptr); + std::shared_ptr ds2 = ImageFolder(folder_path, true, std::make_shared(false, 2)); + EXPECT_NE(ds2, nullptr); + + auto adjustgamma_op = vision::AdjustGamma(10.0); + auto rgb2gray_op = vision::RGB2GRAY(); + + ds1 = ds1->Map({rgb2gray_op, adjustgamma_op}); + EXPECT_NE(ds1, nullptr); + + std::shared_ptr iter1 = ds1->CreateIterator(); + EXPECT_NE(iter1, nullptr); + std::unordered_map row1; + iter1->GetNextRow(&row1); + + std::shared_ptr iter2 = ds2->CreateIterator(); + EXPECT_NE(iter2, nullptr); + std::unordered_map row2; + iter2->GetNextRow(&row2); + + uint64_t i = 0; + while (row1.size() != 0) { + i++; + auto image = row1["image"]; + iter1->GetNextRow(&row1); + iter2->GetNextRow(&row2); + } + EXPECT_EQ(i, 2); + + iter1->Stop(); + iter2->Stop(); +} + +TEST_F(MindDataTestPipeline, TestAdjustGammaParamCheck) { + // pipeline 3-channel + MS_LOG(INFO) << "Pipeline Test."; + std::string MindDataPath = "data/dataset"; + std::string folder_path = MindDataPath + "/testImageNetData/train/"; + std::shared_ptr ds = ImageFolder(folder_path, true, std::make_shared(false, 2)); + EXPECT_NE(ds, nullptr); + + // Case 1: Negative gamma + // Create objects for the tensor ops + std::shared_ptr adjust_gamma(new vision::AdjustGamma(-1, 1.0)); + auto ds1 = ds->Map({adjust_gamma}); + EXPECT_NE(ds1, nullptr); + // Create an iterator over the result of the above dataset + std::shared_ptr iter1 = ds1->CreateIterator(); + // Expect failure: invalid value of AdjustGamma + EXPECT_EQ(iter1, nullptr); +} + TEST_F(MindDataTestPipeline, TestAutoContrastSuccess1) { MS_LOG(INFO) << "Doing MindDataTestPipeline-TestAutoContrastSuccess1."; diff --git a/tests/ut/cpp/dataset/common/cvop_common.cc b/tests/ut/cpp/dataset/common/cvop_common.cc index adddb1ad41..4c43eebf39 100644 --- a/tests/ut/cpp/dataset/common/cvop_common.cc +++ b/tests/ut/cpp/dataset/common/cvop_common.cc @@ -134,6 +134,10 @@ void CVOpCommon::CheckImageShapeAndData(const std::shared_ptr &output_te expect_image_path = dir_path + "imagefolder/apple_expect_randomaffine.jpg"; actual_image_path = dir_path + "imagefolder/apple_actual_randomaffine.jpg"; break; + case kAdjustGamma: + expect_image_path = dir_path + "imagefolder/apple_expect_adjustgamma.png"; + actual_image_path = dir_path + "imagefolder/apple_actual_adjustgamma.png"; + break; case kAutoContrast: expect_image_path = dir_path + "imagefolder/apple_expect_autocontrast.jpg"; actual_image_path = dir_path + "imagefolder/apple_actual_autocontrast.jpg"; diff --git a/tests/ut/cpp/dataset/common/cvop_common.h b/tests/ut/cpp/dataset/common/cvop_common.h index 5dbb5ea98c..1effc6360a 100644 --- a/tests/ut/cpp/dataset/common/cvop_common.h +++ b/tests/ut/cpp/dataset/common/cvop_common.h @@ -44,6 +44,7 @@ class CVOpCommon : public Common { kRandomAffine, kRandomPosterize, kAutoContrast, + kAdjustGamma, kEqualize }; diff --git a/tests/ut/cpp/dataset/execute_test.cc b/tests/ut/cpp/dataset/execute_test.cc index c961db7a69..0337d2459b 100644 --- a/tests/ut/cpp/dataset/execute_test.cc +++ b/tests/ut/cpp/dataset/execute_test.cc @@ -70,6 +70,35 @@ TEST_F(MindDataTestExecute, TestAllpassBiquadWithWrongArg) { EXPECT_FALSE(s01.IsOk()); } +TEST_F(MindDataTestExecute, TestAdjustGammaEager1) { + // 3-channel eager + MS_LOG(INFO) << "3-channel image test"; + // Read images + auto image = ReadFileToTensor("data/dataset/apple.jpg"); + + // Transform params + auto decode = vision::Decode(); + auto adjust_gamma_op = vision::AdjustGamma(0.1, 1.0); + + auto transform = Execute({decode, adjust_gamma_op}); + Status rc = transform(image, &image); + EXPECT_EQ(rc, Status::OK()); +} + +TEST_F(MindDataTestExecute, TestAdjustGammaEager2) { + // 1-channel eager + MS_LOG(INFO) << "1-channel image test"; + auto m1 = ReadFileToTensor("data/dataset/apple.jpg"); + // Transform params + auto decode = vision::Decode(); + auto rgb2gray = vision::RGB2GRAY(); + auto adjust_gamma_op = vision::AdjustGamma(0.1, 1.0); + + auto transform = Execute({decode, rgb2gray, adjust_gamma_op}); + Status rc = transform(m1, &m1); + EXPECT_EQ(rc, Status::OK()); +} + TEST_F(MindDataTestExecute, TestComposeTransforms) { MS_LOG(INFO) << "Doing TestComposeTransforms."; diff --git a/tests/ut/python/dataset/test_adjustgamma.py b/tests/ut/python/dataset/test_adjustgamma.py new file mode 100644 index 0000000000..61e91fdc5f --- /dev/null +++ b/tests/ut/python/dataset/test_adjustgamma.py @@ -0,0 +1,329 @@ +# 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 AdjustGamma op in DE +""" +import numpy as np +from numpy.testing import assert_allclose +import PIL + +import mindspore.dataset as ds +import mindspore.dataset.transforms.py_transforms +import mindspore.dataset.vision.py_transforms as F +import mindspore.dataset.vision.c_transforms as C +from mindspore import log as logger + +DATA_DIR = "../data/dataset/testImageNetData/train/" +MNIST_DATA_DIR = "../data/dataset/testMnistData" + +DATA_DIR_2 = ["../data/dataset/test_tf_file_3_images/train-0000-of-0001.data"] +SCHEMA_DIR = "../data/dataset/test_tf_file_3_images/datasetSchema.json" + +GENERATE_GOLDEN = False + + +def generate_numpy_random_rgb(shape): + """ + Only generate floating points that are fractions like n / 256, since they + are RGB pixels. Some low-precision floating point types in this test can't + handle arbitrary precision floating points well. + """ + return np.random.randint(0, 256, shape) / 255. + + +def test_adjust_gamma_c_eager(): + # Eager 3-channel + rgb_flat = generate_numpy_random_rgb((64, 3)).astype(np.float32) + img_in = rgb_flat.reshape((8, 8, 3)) + + adjustgamma_op = C.AdjustGamma(10, 1) + img_out = adjustgamma_op(img_in) + assert img_out is not None + + +def test_adjust_gamma_py_eager(): + # Eager 3-channel + rgb_flat = generate_numpy_random_rgb((64, 3)).astype(np.uint8) + img_in = PIL.Image.fromarray(rgb_flat.reshape((8, 8, 3))) + + adjustgamma_op = F.AdjustGamma(10, 1) + img_out = adjustgamma_op(img_in) + assert img_out is not None + + +def test_adjust_gamma_c_eager_gray(): + # Eager 3-channel + rgb_flat = generate_numpy_random_rgb((64, 1)).astype(np.float32) + img_in = rgb_flat.reshape((8, 8)) + + adjustgamma_op = C.AdjustGamma(10, 1) + img_out = adjustgamma_op(img_in) + assert img_out is not None + + +def test_adjust_gamma_py_eager_gray(): + # Eager 3-channel + rgb_flat = generate_numpy_random_rgb((64, 1)).astype(np.uint8) + img_in = PIL.Image.fromarray(rgb_flat.reshape((8, 8))) + + adjustgamma_op = F.AdjustGamma(10, 1) + img_out = adjustgamma_op(img_in) + assert img_out is not None + + +def test_adjust_gamma_invalid_gamma_param_c(): + """ + Test AdjustGamma C Op with invalid ignore parameter + """ + logger.info("Test AdjustGamma C Op with invalid ignore parameter") + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + data_set = data_set.map(operations=[C.Decode(), + C.Resize((224, 224)), + lambda img: np.array(img[:, :, 0])], + input_columns=["image"]) + # invalid gamma + data_set = data_set.map(operations=C.AdjustGamma(gamma=-10.0, + gain=1.0), + input_columns="image") + except ValueError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "Input is not within the required interval of " in str(error) + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + data_set = data_set.map(operations=[C.Decode(), + C.Resize((224, 224)), + lambda img: np.array(img[:, :, 0])], + input_columns=["image"]) + # invalid gamma + data_set = data_set.map(operations=C.AdjustGamma(gamma=[1, 2], + gain=1.0), + input_columns="image") + except TypeError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "is not of type [, ], but got" in str(error) + + +def test_adjust_gamma_invalid_gamma_param_py(): + """ + Test AdjustGamma python Op with invalid ignore parameter + """ + logger.info("Test AdjustGamma python Op with invalid ignore parameter") + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + trans = mindspore.dataset.transforms.py_transforms.Compose([ + F.Decode(), + F.Resize((224, 224)), + F.AdjustGamma(gamma=-10.0), + F.ToTensor() + ]) + data_set = data_set.map(operations=[trans], + input_columns=["image"]) + except ValueError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "Input is not within the required interval of " in str(error) + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + trans = mindspore.dataset.transforms.py_transforms.Compose([ + F.Decode(), + F.Resize((224, 224)), + F.AdjustGamma(gamma=[1, 2]), + F.ToTensor() + ]) + data_set = data_set.map(operations=[trans], + input_columns=["image"]) + except TypeError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "is not of type [, ], but got" in str(error) + + +def test_adjust_gamma_invalid_gain_param_c(): + """ + Test AdjustGamma C Op with invalid gain parameter + """ + logger.info("Test AdjustGamma C Op with invalid gain parameter") + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + data_set = data_set.map(operations=[C.Decode(), + C.Resize((224, 224)), + lambda img: np.array(img[:, :, 0])], + input_columns=["image"]) + # invalid gain + data_set = data_set.map(operations=C.AdjustGamma(gamma=10.0, + gain=[1, 10]), + input_columns="image") + except TypeError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "is not of type [, ], but got " in str(error) + + +def test_adjust_gamma_invalid_gain_param_py(): + """ + Test AdjustGamma python Op with invalid gain parameter + """ + logger.info("Test AdjustGamma python Op with invalid gain parameter") + try: + data_set = ds.ImageFolderDataset(dataset_dir=DATA_DIR, shuffle=False) + trans = mindspore.dataset.transforms.py_transforms.Compose([ + F.Decode(), + F.Resize((224, 224)), + F.AdjustGamma(gamma=10.0, gain=[1, 10]), + F.ToTensor() + ]) + data_set = data_set.map(operations=[trans], + input_columns=["image"]) + except TypeError as error: + logger.info("Got an exception in AdjustGamma: {}".format(str(error))) + assert "is not of type [, ], but got " in str(error) + + +def test_adjust_gamma_pipeline_c(): + """ + Test AdjustGamma C Op Pipeline + """ + # First dataset + transforms1 = [C.Decode(), C.Resize([64, 64])] + transforms1 = mindspore.dataset.transforms.py_transforms.Compose( + transforms1) + ds1 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds1 = ds1.map(operations=transforms1, input_columns=["image"]) + + # Second dataset + transforms2 = [ + C.Decode(), + C.Resize([64, 64]), + C.AdjustGamma(1.0, 1.0) + ] + transform2 = mindspore.dataset.transforms.py_transforms.Compose( + transforms2) + ds2 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds2 = ds2.map(operations=transform2, input_columns=["image"]) + + num_iter = 0 + for data1, data2 in zip(ds1.create_dict_iterator(num_epochs=1), + ds2.create_dict_iterator(num_epochs=1)): + num_iter += 1 + ori_img = data1["image"].asnumpy() + cvt_img = data2["image"].asnumpy() + assert_allclose(ori_img.flatten(), + cvt_img.flatten(), + rtol=1e-5, + atol=0) + assert ori_img.shape == cvt_img.shape + + +def test_adjust_gamma_pipeline_py(): + """ + Test AdjustGamma python Op Pipeline + """ + # First dataset + transforms1 = [F.Decode(), F.Resize([64, 64]), F.ToTensor()] + transforms1 = mindspore.dataset.transforms.py_transforms.Compose( + transforms1) + ds1 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds1 = ds1.map(operations=transforms1, input_columns=["image"]) + + # Second dataset + transforms2 = [ + F.Decode(), + F.Resize([64, 64]), + F.AdjustGamma(1.0, 1.0), + F.ToTensor() + ] + transform2 = mindspore.dataset.transforms.py_transforms.Compose( + transforms2) + ds2 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds2 = ds2.map(operations=transform2, input_columns=["image"]) + + num_iter = 0 + for data1, data2 in zip(ds1.create_dict_iterator(num_epochs=1), + ds2.create_dict_iterator(num_epochs=1)): + num_iter += 1 + ori_img = data1["image"].asnumpy() + cvt_img = data2["image"].asnumpy() + assert_allclose(ori_img.flatten(), + cvt_img.flatten(), + rtol=1e-5, + atol=0) + assert ori_img.shape == cvt_img.shape + + +def test_adjust_gamma_pipeline_py_gray(): + """ + Test AdjustGamma python Op Pipeline 1-channel + """ + # First dataset + transforms1 = [F.Decode(), F.Resize([64, 64]), F.Grayscale(), F.ToTensor()] + transforms1 = mindspore.dataset.transforms.py_transforms.Compose( + transforms1) + ds1 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds1 = ds1.map(operations=transforms1, input_columns=["image"]) + + # Second dataset + transforms2 = [ + F.Decode(), + F.Resize([64, 64]), + F.Grayscale(), + F.AdjustGamma(1.0, 1.0), + F.ToTensor() + ] + transform2 = mindspore.dataset.transforms.py_transforms.Compose( + transforms2) + ds2 = ds.TFRecordDataset(DATA_DIR_2, + SCHEMA_DIR, + columns_list=["image"], + shuffle=False) + ds2 = ds2.map(operations=transform2, input_columns=["image"]) + + num_iter = 0 + for data1, data2 in zip(ds1.create_dict_iterator(num_epochs=1), + ds2.create_dict_iterator(num_epochs=1)): + num_iter += 1 + ori_img = data1["image"].asnumpy() + cvt_img = data2["image"].asnumpy() + assert_allclose(ori_img.flatten(), + cvt_img.flatten(), + rtol=1e-5, + atol=0) + + +if __name__ == "__main__": + test_adjust_gamma_c_eager() + test_adjust_gamma_py_eager() + test_adjust_gamma_c_eager_gray() + test_adjust_gamma_py_eager_gray() + + test_adjust_gamma_invalid_gamma_param_c() + test_adjust_gamma_invalid_gamma_param_py() + test_adjust_gamma_invalid_gain_param_c() + test_adjust_gamma_invalid_gain_param_py() + test_adjust_gamma_pipeline_c() + test_adjust_gamma_pipeline_py() + test_adjust_gamma_pipeline_py_gray()