forked from mindspore-Ecosystem/mindspore
!17836 Add Rotate API for CV data processing
Merge pull request !17836 from xiaotianci/rotate
This commit is contained in:
commit
e60f5f12f5
|
@ -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(); }
|
||||
|
||||
std::shared_ptr<TensorOp> RotateOperation::Build() { return rotate_op; }
|
||||
|
||||
void RotateOperation::setAngle(uint64_t angle_id) {
|
||||
std::dynamic_pointer_cast<RotateOp>(rotate_op)->setAngle(angle_id);
|
||||
Status RotateOperation::ValidateParams() {
|
||||
#ifndef ENABLE_ANDROID
|
||||
// center
|
||||
if (center_.empty() || center_.size() != 2) {
|
||||
std::string err_msg = "Rotate: center must be a vector of two values, got: " + std::to_string(center_.size());
|
||||
MS_LOG(ERROR) << err_msg;
|
||||
RETURN_STATUS_SYNTAX_ERROR(err_msg);
|
||||
}
|
||||
// fill_value
|
||||
RETURN_IF_NOT_OK(ValidateVectorFillvalue("Rotate", fill_value_));
|
||||
#endif
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::shared_ptr<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];
|
||||
|
||||
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()
|
||||
|
|
Loading…
Reference in New Issue