!17836 Add Rotate API for CV data processing

Merge pull request !17836 from xiaotianci/rotate
This commit is contained in:
i-robot 2021-06-10 09:29:18 +08:00 committed by Gitee
commit e60f5f12f5
22 changed files with 508 additions and 43 deletions

View File

@ -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>>(

View File

@ -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 &degrees, InterpolationMode resample, bool expand, const std::vector<float> &center,
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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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=""):

View File

@ -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

View File

@ -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."""

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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();
}

View File

@ -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"

View File

@ -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());
}

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()