Browse Source

!8511 [MD] Decouple more Transform Ops

From: @luoyang42
Reviewed-by: 
Signed-off-by:
tags/v1.1.0
mindspore-ci-bot Gitee 5 years ago
parent
commit
f352f9a54a
5 changed files with 582 additions and 131 deletions
  1. +200
    -11
      mindspore/ccsrc/minddata/dataset/api/transforms.cc
  2. +9
    -94
      mindspore/ccsrc/minddata/dataset/api/vision.cc
  3. +127
    -0
      mindspore/ccsrc/minddata/dataset/include/transforms.h
  4. +222
    -0
      tests/ut/cpp/dataset/c_api_transforms_test.cc
  5. +24
    -26
      tests/ut/cpp/dataset/c_api_vision_test.cc

+ 200
- 11
mindspore/ccsrc/minddata/dataset/api/transforms.cc View File

@@ -17,39 +17,168 @@
#include "minddata/dataset/include/transforms.h" #include "minddata/dataset/include/transforms.h"


// Kernel data headers (in alphabetical order) // Kernel data headers (in alphabetical order)
#include "minddata/dataset/kernels/data/compose_op.h"
#include "minddata/dataset/kernels/data/duplicate_op.h"
#include "minddata/dataset/kernels/data/one_hot_op.h" #include "minddata/dataset/kernels/data/one_hot_op.h"
#include "minddata/dataset/kernels/data/random_apply_op.h"
#include "minddata/dataset/kernels/data/random_choice_op.h"
#include "minddata/dataset/kernels/data/type_cast_op.h" #include "minddata/dataset/kernels/data/type_cast_op.h"
#ifndef ENABLE_ANDROID
#include "minddata/dataset/kernels/data/unique_op.h"
#endif


namespace mindspore { namespace mindspore {
namespace dataset { namespace dataset {


TensorOperation::TensorOperation() {} TensorOperation::TensorOperation() {}


/* ####################################### Validator Functions ############################################ */
Status ValidateVectorFillvalue(const std::string &transform_name, const std::vector<uint8_t> &fill_value) {
if (fill_value.empty() || (fill_value.size() != 1 && fill_value.size() != 3)) {
std::string err_msg =
transform_name + ": fill_value vector has incorrect size: " + std::to_string(fill_value.size());
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
for (uint8_t single_fill_value : fill_value) {
if (single_fill_value > 255) {
std::string err_msg =
transform_name + ": fill_value has to be between 0 and 255, got:" + std::to_string(single_fill_value);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

Status ValidateProbability(const std::string &transform_name, const float &probability) {
if (probability < 0.0 || probability > 1.0) {
std::string err_msg =
transform_name + ": probability must be between 0.0 and 1.0, got: " + std::to_string(probability);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}

return Status::OK();
}

Status ValidateVectorPadding(const std::string &transform_name, const std::vector<int32_t> &padding) {
if (padding.empty() || padding.size() == 3 || padding.size() > 4) {
std::string err_msg = transform_name + ": padding vector has incorrect size: " + std::to_string(padding.size());
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
for (int32_t i = 0; i < padding.size(); ++i) {
if (padding[i] < 0) {
std::string err_msg =
transform_name +
": invalid padding, padding value must be greater than or equal to 0, got: " + std::to_string(padding[i]);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
if (padding[i] == INT_MAX) {
std::string err_msg =
transform_name + ": invalid padding, padding value too large, got: " + std::to_string(padding[i]);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

Status ValidateVectorPositive(const std::string &transform_name, const std::vector<int32_t> &size) {
for (int32_t i = 0; i < size.size(); ++i) {
if (size[i] <= 0) {
std::string err_msg =
transform_name + ": Non-positive size value: " + std::to_string(size[i]) + " at element: " + std::to_string(i);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

Status ValidateVectorTransforms(const std::string &transform_name,
const std::vector<std::shared_ptr<TensorOperation>> &transforms) {
if (transforms.empty()) {
std::string err_msg = transform_name + ": transform list must not be empty.";
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
for (int32_t i = 0; i < transforms.size(); ++i) {
if (transforms[i] == nullptr) {
std::string err_msg =
transform_name + ": transform ops must not be null, got transform[" + std::to_string(i) + "] == nullptr.";
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

bool CmpFloat(const float &a, const float &b, float epsilon) { return (std::fabs(a - b) < epsilon); }

// Transform operations for data. // Transform operations for data.
namespace transforms { namespace transforms {


// FUNCTIONS TO CREATE DATA TRANSFORM OPERATIONS // FUNCTIONS TO CREATE DATA TRANSFORM OPERATIONS
// (In alphabetical order) // (In alphabetical order)


// Function to create ComposeOperation.
std::shared_ptr<ComposeOperation> Compose(const std::vector<std::shared_ptr<TensorOperation>> &transforms) {
auto op = std::make_shared<ComposeOperation>(transforms);
// Input validation
return op->ValidateParams() ? op : nullptr;
}

// Function to create DuplicateOperation.
std::shared_ptr<DuplicateOperation> Duplicate() {
auto op = std::make_shared<DuplicateOperation>();
// Input validation
return op->ValidateParams() ? op : nullptr;
}

// Function to create OneHotOperation. // Function to create OneHotOperation.
std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes) { std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes) {
auto op = std::make_shared<OneHotOperation>(num_classes); auto op = std::make_shared<OneHotOperation>(num_classes);
// Input validation // Input validation
if (!op->ValidateParams()) {
return nullptr;
}
return op;
return op->ValidateParams() ? op : nullptr;
}

// Function to create RandomApplyOperation.
std::shared_ptr<RandomApplyOperation> RandomApply(const std::vector<std::shared_ptr<TensorOperation>> &transforms,
double prob) {
auto op = std::make_shared<RandomApplyOperation>(transforms, prob);
// Input validation
return op->ValidateParams() ? op : nullptr;
}

// Function to create RandomChoiceOperation.
std::shared_ptr<RandomChoiceOperation> RandomChoice(const std::vector<std::shared_ptr<TensorOperation>> &transforms) {
auto op = std::make_shared<RandomChoiceOperation>(transforms);
// Input validation
return op->ValidateParams() ? op : nullptr;
} }


// Function to create TypeCastOperation. // Function to create TypeCastOperation.
std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type) { std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type) {
auto op = std::make_shared<TypeCastOperation>(data_type); auto op = std::make_shared<TypeCastOperation>(data_type);
// Input validation // Input validation
if (!op->ValidateParams()) {
return nullptr;
}
return op;
return op->ValidateParams() ? op : nullptr;
}

#ifndef ENABLE_ANDROID
// Function to create UniqueOperation.
std::shared_ptr<UniqueOperation> Unique() {
auto op = std::make_shared<UniqueOperation>();
// Input validation
return op->ValidateParams() ? op : nullptr;
} }
#endif


/* ####################################### Validator Functions ############################################ */ /* ####################################### Validator Functions ############################################ */


@@ -57,13 +186,33 @@ std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type) {


// (In alphabetical order) // (In alphabetical order)


// ComposeOperation
ComposeOperation::ComposeOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms)
: transforms_(transforms) {}

Status ComposeOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorTransforms("Compose", transforms_));
return Status::OK();
}

std::shared_ptr<TensorOp> ComposeOperation::Build() {
std::vector<std::shared_ptr<TensorOp>> tensor_ops;
(void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops),
[](std::shared_ptr<TensorOperation> op) -> std::shared_ptr<TensorOp> { return op->Build(); });
return std::make_shared<ComposeOp>(tensor_ops);
}

// DuplicateOperation
Status DuplicateOperation::ValidateParams() { return Status::OK(); }

std::shared_ptr<TensorOp> DuplicateOperation::Build() { return std::make_shared<DuplicateOp>(); }

// OneHotOperation // OneHotOperation
OneHotOperation::OneHotOperation(int32_t num_classes) : num_classes_(num_classes) {} OneHotOperation::OneHotOperation(int32_t num_classes) : num_classes_(num_classes) {}


Status OneHotOperation::ValidateParams() { Status OneHotOperation::ValidateParams() {
if (num_classes_ <= 0) { if (num_classes_ <= 0) {
std::string err_msg =
"OneHot: Number of classes must be greater than 0. num_classes: " + std::to_string(num_classes_);
std::string err_msg = "OneHot: Number of classes must be greater than 0, but got: " + std::to_string(num_classes_);
MS_LOG(ERROR) << err_msg; MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg); RETURN_STATUS_SYNTAX_ERROR(err_msg);
} }
@@ -73,6 +222,39 @@ Status OneHotOperation::ValidateParams() {


std::shared_ptr<TensorOp> OneHotOperation::Build() { return std::make_shared<OneHotOp>(num_classes_); } std::shared_ptr<TensorOp> OneHotOperation::Build() { return std::make_shared<OneHotOp>(num_classes_); }


// RandomApplyOperation
RandomApplyOperation::RandomApplyOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms, double prob)
: transforms_(transforms), prob_(prob) {}

Status RandomApplyOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomApply", transforms_));
RETURN_IF_NOT_OK(ValidateProbability("RandomApply", prob_));
return Status::OK();
}

std::shared_ptr<TensorOp> RandomApplyOperation::Build() {
std::vector<std::shared_ptr<TensorOp>> tensor_ops;
(void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops),
[](std::shared_ptr<TensorOperation> op) -> std::shared_ptr<TensorOp> { return op->Build(); });
return std::make_shared<RandomApplyOp>(prob_, tensor_ops);
}

// RandomChoiceOperation
RandomChoiceOperation::RandomChoiceOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms)
: transforms_(transforms) {}

Status RandomChoiceOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorTransforms("RandomChoice", transforms_));
return Status::OK();
}

std::shared_ptr<TensorOp> RandomChoiceOperation::Build() {
std::vector<std::shared_ptr<TensorOp>> tensor_ops;
(void)std::transform(transforms_.begin(), transforms_.end(), std::back_inserter(tensor_ops),
[](std::shared_ptr<TensorOperation> op) -> std::shared_ptr<TensorOp> { return op->Build(); });
return std::make_shared<RandomChoiceOp>(tensor_ops);
}

// TypeCastOperation // TypeCastOperation
TypeCastOperation::TypeCastOperation(std::string data_type) : data_type_(data_type) {} TypeCastOperation::TypeCastOperation(std::string data_type) : data_type_(data_type) {}


@@ -83,7 +265,7 @@ Status TypeCastOperation::ValidateParams() {
if (itr == predefine_type.end()) { if (itr == predefine_type.end()) {
std::string err_msg = "TypeCast: Invalid data type: " + data_type_; std::string err_msg = "TypeCast: Invalid data type: " + data_type_;
MS_LOG(ERROR) << "TypeCast: Only supports data type bool, int8, uint8, int16, uint16, int32, uint32, " MS_LOG(ERROR) << "TypeCast: Only supports data type bool, int8, uint8, int16, uint16, int32, uint32, "
<< "int64, uint64, float16, float32, float64, string, but got " << data_type_;
<< "int64, uint64, float16, float32, float64, string, but got: " << data_type_;
RETURN_STATUS_SYNTAX_ERROR(err_msg); RETURN_STATUS_SYNTAX_ERROR(err_msg);
} }


@@ -92,6 +274,13 @@ Status TypeCastOperation::ValidateParams() {


std::shared_ptr<TensorOp> TypeCastOperation::Build() { return std::make_shared<TypeCastOp>(data_type_); } std::shared_ptr<TensorOp> TypeCastOperation::Build() { return std::make_shared<TypeCastOp>(data_type_); }


#ifndef ENABLE_ANDROID
// UniqueOperation
Status UniqueOperation::ValidateParams() { return Status::OK(); }

std::shared_ptr<TensorOp> UniqueOperation::Build() { return std::make_shared<UniqueOp>(); }
#endif

} // namespace transforms } // namespace transforms
} // namespace dataset } // namespace dataset
} // namespace mindspore } // namespace mindspore

+ 9
- 94
mindspore/ccsrc/minddata/dataset/api/vision.cc View File

@@ -394,77 +394,7 @@ std::shared_ptr<UniformAugOperation> UniformAugment(std::vector<std::shared_ptr<
// Input validation // Input validation
return op->ValidateParams() ? op : nullptr; return op->ValidateParams() ? op : nullptr;
} }

#endif #endif
/* ####################################### Validator Functions ############################################ */
Status ValidateVectorFillvalue(const std::string &dataset_name, const std::vector<uint8_t> &fill_value) {
if (fill_value.empty() || (fill_value.size() != 1 && fill_value.size() != 3)) {
std::string err_msg = dataset_name + ": fill_value vector has incorrect size: " + std::to_string(fill_value.size());
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
for (uint8_t single_fill_value : fill_value) {
if (single_fill_value > 255) {
std::string err_msg =
dataset_name + ": fill_value has to be between 0 and 255, got:" + std::to_string(single_fill_value);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

Status ValidateVectorProbability(const std::string &dataset_name, const float &probability) {
if (probability < 0.0 || probability > 1.0) {
std::string err_msg =
dataset_name + ": probability must be between 0.0 and 1.0, got: " + std::to_string(probability);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}

return Status::OK();
}

Status ValidateVectorPadding(const std::string &dataset_name, const std::vector<int32_t> &padding) {
if (padding.empty() || padding.size() == 3 || padding.size() > 4) {
std::string err_msg = dataset_name + ": padding vector has incorrect size: " + std::to_string(padding.size());
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
for (int32_t i = 0; i < padding.size(); ++i) {
if (padding[i] < 0) {
std::string err_msg =
dataset_name +
": invalid padding, padding value must be greater than or equal to 0, got: " + std::to_string(padding[i]);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
if (padding[i] == INT_MAX) {
std::string err_msg =
dataset_name + ": invalid padding, padding value too large, got: " + std::to_string(padding[i]);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

Status ValidateVectorPositive(const std::string &dataset_name, const std::vector<int32_t> &size) {
for (int32_t i = 0; i < size.size(); ++i) {
if (size[i] <= 0) {
std::string err_msg =
dataset_name + ": Non-positive size value: " + std::to_string(size[i]) + " at element: " + std::to_string(i);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}

return Status::OK();
}

bool CmpFloat(const float &a, const float &b, float epsilon = 0.0000000001f) { return (std::fabs(a - b) < epsilon); }


/* ####################################### Derived TensorOperation classes ################################# */ /* ####################################### Derived TensorOperation classes ################################# */


@@ -503,17 +433,8 @@ BoundingBoxAugmentOperation::BoundingBoxAugmentOperation(std::shared_ptr<TensorO
: transform_(transform), ratio_(ratio) {} : transform_(transform), ratio_(ratio) {}


Status BoundingBoxAugmentOperation::ValidateParams() { Status BoundingBoxAugmentOperation::ValidateParams() {
if (transform_ == nullptr) {
std::string err_msg = "BoundingBoxAugment: transform must not be null.";
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}

if (ratio_ < 0.0 || ratio_ > 1.0) {
std::string err_msg = "BoundingBoxAugment: ratio has to be between 0.0 and 1.0, got: " + std::to_string(ratio_);
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
RETURN_IF_NOT_OK(ValidateVectorTransforms("BoundingBoxAugment", {transform_}));
RETURN_IF_NOT_OK(ValidateProbability("BoundingBoxAugment", ratio_));
return Status::OK(); return Status::OK();
} }


@@ -1315,7 +1236,7 @@ std::shared_ptr<TensorOp> RandomCropWithBBoxOperation::Build() {
RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {} RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {}


Status RandomHorizontalFlipOperation::ValidateParams() { Status RandomHorizontalFlipOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorProbability("RandomHorizontalFlip", probability_));
RETURN_IF_NOT_OK(ValidateProbability("RandomHorizontalFlip", probability_));


return Status::OK(); return Status::OK();
} }
@@ -1330,7 +1251,7 @@ RandomHorizontalFlipWithBBoxOperation::RandomHorizontalFlipWithBBoxOperation(flo
: probability_(probability) {} : probability_(probability) {}


Status RandomHorizontalFlipWithBBoxOperation::ValidateParams() { Status RandomHorizontalFlipWithBBoxOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorProbability("RandomHorizontalFlipWithBBox", probability_));
RETURN_IF_NOT_OK(ValidateProbability("RandomHorizontalFlipWithBBox", probability_));


return Status::OK(); return Status::OK();
} }
@@ -1696,7 +1617,7 @@ std::shared_ptr<TensorOp> RandomSolarizeOperation::Build() {
RandomVerticalFlipOperation::RandomVerticalFlipOperation(float probability) : probability_(probability) {} RandomVerticalFlipOperation::RandomVerticalFlipOperation(float probability) : probability_(probability) {}


Status RandomVerticalFlipOperation::ValidateParams() { Status RandomVerticalFlipOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorProbability("RandomVerticalFlip", probability_));
RETURN_IF_NOT_OK(ValidateProbability("RandomVerticalFlip", probability_));


return Status::OK(); return Status::OK();
} }
@@ -1711,7 +1632,7 @@ RandomVerticalFlipWithBBoxOperation::RandomVerticalFlipWithBBoxOperation(float p
: probability_(probability) {} : probability_(probability) {}


Status RandomVerticalFlipWithBBoxOperation::ValidateParams() { Status RandomVerticalFlipWithBBoxOperation::ValidateParams() {
RETURN_IF_NOT_OK(ValidateVectorProbability("RandomVerticalFlipWithBBox", probability_));
RETURN_IF_NOT_OK(ValidateProbability("RandomVerticalFlipWithBBox", probability_));


return Status::OK(); return Status::OK();
} }
@@ -1945,21 +1866,15 @@ UniformAugOperation::UniformAugOperation(std::vector<std::shared_ptr<TensorOpera


Status UniformAugOperation::ValidateParams() { Status UniformAugOperation::ValidateParams() {
// transforms // transforms
RETURN_IF_NOT_OK(ValidateVectorTransforms("UniformAug", transforms_));
if (num_ops_ > transforms_.size()) { if (num_ops_ > transforms_.size()) {
std::string err_msg = "UniformAug: num_ops is greater than transforms size, num_ops: " + std::to_string(num_ops_);
std::string err_msg = "UniformAug: num_ops is greater than transforms size, but got: " + std::to_string(num_ops_);
MS_LOG(ERROR) << err_msg; MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg); RETURN_STATUS_SYNTAX_ERROR(err_msg);
} }
for (int32_t i = 0; i < transforms_.size(); ++i) {
if (transforms_[i] == nullptr) {
std::string err_msg = "UniformAug: transform ops must not be null.";
MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg);
}
}
// num_ops // num_ops
if (num_ops_ <= 0) { if (num_ops_ <= 0) {
std::string err_msg = "UniformAug: num_ops must be greater than 0, num_ops: " + std::to_string(num_ops_);
std::string err_msg = "UniformAug: num_ops must be greater than 0, but got: " + std::to_string(num_ops_);
MS_LOG(ERROR) << err_msg; MS_LOG(ERROR) << err_msg;
RETURN_STATUS_SYNTAX_ERROR(err_msg); RETURN_STATUS_SYNTAX_ERROR(err_msg);
} }


+ 127
- 0
mindspore/ccsrc/minddata/dataset/include/transforms.h View File

@@ -44,12 +44,50 @@ class TensorOperation : public std::enable_shared_from_this<TensorOperation> {
virtual Status ValidateParams() = 0; virtual Status ValidateParams() = 0;
}; };


// Helper function to validate fill value
Status ValidateVectorFillvalue(const std::string &transform_name, const std::vector<uint8_t> &fill_value);

// Helper function to validate probability
Status ValidateProbability(const std::string &transform_name, const float &probability);

// Helper function to validate padding
Status ValidateVectorPadding(const std::string &transform_name, const std::vector<int32_t> &padding);

// Helper function to validate size
Status ValidateVectorPositive(const std::string &transform_name, const std::vector<int32_t> &size);

// Helper function to validate transforms
Status ValidateVectorTransforms(const std::string &transform_name,
const std::vector<std::shared_ptr<TensorOperation>> &transforms);

// Helper function to compare float value
bool CmpFloat(const float &a, const float &b, float epsilon = 0.0000000001f);

// Transform operations for performing data transformation. // Transform operations for performing data transformation.
namespace transforms { namespace transforms {


// Transform Op classes (in alphabetical order) // Transform Op classes (in alphabetical order)
class ComposeOperation;
class DuplicateOperation;
class OneHotOperation; class OneHotOperation;
class RandomApplyOperation;
class RandomChoiceOperation;
class TypeCastOperation; class TypeCastOperation;
#ifndef ENABLE_ANDROID
class UniqueOperation;
#endif

/// \brief Function to create a Compose TensorOperation.
/// \notes Compose a list of transforms into a single transform.
/// \param[in] transforms A vector of transformations to be applied.
/// \return Shared pointer to the current TensorOperation.
std::shared_ptr<ComposeOperation> Compose(const std::vector<std::shared_ptr<TensorOperation>> &transforms);

/// \brief Function to create a Duplicate TensorOperation.
/// \notes Duplicate the input tensor to a new output tensor.
/// The input tensor is carried over to the output list.
/// \return Shared pointer to the current TensorOperation.
std::shared_ptr<DuplicateOperation> Duplicate();


/// \brief Function to create a OneHot TensorOperation. /// \brief Function to create a OneHot TensorOperation.
/// \notes Convert the labels into OneHot format. /// \notes Convert the labels into OneHot format.
@@ -57,14 +95,61 @@ class TypeCastOperation;
/// \return Shared pointer to the current TensorOperation. /// \return Shared pointer to the current TensorOperation.
std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes); std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes);


/// \brief Function to create a RandomApply TensorOperation.
/// \notes Randomly perform a series of transforms with a given probability.
/// \param[in] transforms A vector of transformations to be applied.
/// \param[in] prob The probability to apply the transformation list (default=0.5)
/// \return Shared pointer to the current TensorOperation.
std::shared_ptr<RandomApplyOperation> RandomApply(const std::vector<std::shared_ptr<TensorOperation>> &transforms,
double prob = 0.5);

/// \brief Function to create a RandomChoice TensorOperation.
/// \notes Randomly selects one transform from a list of transforms to perform operation.
/// \param[in] transforms A vector of transformations to be chosen from to apply.
/// \return Shared pointer to the current TensorOperation.
std::shared_ptr<RandomChoiceOperation> RandomChoice(const std::vector<std::shared_ptr<TensorOperation>> &transforms);

/// \brief Function to create a TypeCast TensorOperation. /// \brief Function to create a TypeCast TensorOperation.
/// \notes Tensor operation to cast to a given MindSpore data type. /// \notes Tensor operation to cast to a given MindSpore data type.
/// \param[in] data_type mindspore.dtype to be cast to. /// \param[in] data_type mindspore.dtype to be cast to.
/// \return Shared pointer to the current TensorOperation. /// \return Shared pointer to the current TensorOperation.
std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type); std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type);


#ifndef ENABLE_ANDROID
/// \brief Function to create a Unique TensorOperation.
/// \notes Return an output tensor containing all the unique elements of the input tensor in
/// the same order that they occur in the input tensor.
/// \return Shared pointer to the current TensorOperation.
std::shared_ptr<UniqueOperation> Unique();
#endif

/* ####################################### Derived TensorOperation classes ################################# */ /* ####################################### Derived TensorOperation classes ################################# */


class ComposeOperation : public TensorOperation {
public:
explicit ComposeOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms);

~ComposeOperation() = default;

std::shared_ptr<TensorOp> Build() override;

Status ValidateParams() override;

private:
std::vector<std::shared_ptr<TensorOperation>> transforms_;
};

class DuplicateOperation : public TensorOperation {
public:
DuplicateOperation() = default;

~DuplicateOperation() = default;

std::shared_ptr<TensorOp> Build() override;

Status ValidateParams() override;
};

class OneHotOperation : public TensorOperation { class OneHotOperation : public TensorOperation {
public: public:
explicit OneHotOperation(int32_t num_classes_); explicit OneHotOperation(int32_t num_classes_);
@@ -79,6 +164,35 @@ class OneHotOperation : public TensorOperation {
float num_classes_; float num_classes_;
}; };


class RandomApplyOperation : public TensorOperation {
public:
explicit RandomApplyOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms, double prob);

~RandomApplyOperation() = default;

std::shared_ptr<TensorOp> Build() override;

Status ValidateParams() override;

private:
std::vector<std::shared_ptr<TensorOperation>> transforms_;
double prob_;
};

class RandomChoiceOperation : public TensorOperation {
public:
explicit RandomChoiceOperation(const std::vector<std::shared_ptr<TensorOperation>> &transforms);

~RandomChoiceOperation() = default;

std::shared_ptr<TensorOp> Build() override;

Status ValidateParams() override;

private:
std::vector<std::shared_ptr<TensorOperation>> transforms_;
};

class TypeCastOperation : public TensorOperation { class TypeCastOperation : public TensorOperation {
public: public:
explicit TypeCastOperation(std::string data_type); explicit TypeCastOperation(std::string data_type);
@@ -92,6 +206,19 @@ class TypeCastOperation : public TensorOperation {
private: private:
std::string data_type_; std::string data_type_;
}; };

#ifndef ENABLE_ANDROID
class UniqueOperation : public TensorOperation {
public:
UniqueOperation() = default;

~UniqueOperation() = default;

std::shared_ptr<TensorOp> Build() override;

Status ValidateParams() override;
};
#endif
} // namespace transforms } // namespace transforms
} // namespace dataset } // namespace dataset
} // namespace mindspore } // namespace mindspore


+ 222
- 0
tests/ut/cpp/dataset/c_api_transforms_test.cc View File

@@ -28,6 +28,107 @@ class MindDataTestPipeline : public UT::DatasetOpTesting {


// Tests for data transforms ops (in alphabetical order) // Tests for data transforms ops (in alphabetical order)


TEST_F(MindDataTestPipeline, TestComposeSuccess) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestComposeSuccess.";

// Create an ImageFolder Dataset
std::string folder_path = datasets_root_path_ + "/testPK/data/";
std::shared_ptr<Dataset> ds = ImageFolder(folder_path, false, RandomSampler(false, 3));
EXPECT_NE(ds, nullptr);

// Create objects for the tensor ops
std::shared_ptr<TensorOperation> compose = transforms::Compose({vision::Decode(), vision::Resize({777, 777})});
EXPECT_NE(compose, nullptr);

// Create a Map operation on ds
ds = ds->Map({compose}, {"image"});
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, std::shared_ptr<Tensor>> row;
iter->GetNextRow(&row);

uint64_t i = 0;
while (row.size() != 0) {
i++;
auto image = row["image"];
auto label = row["label"];
MS_LOG(INFO) << "Tensor image shape: " << image->shape();
MS_LOG(INFO) << "Label shape: " << label->shape();
EXPECT_EQ(image->shape()[0], 777);
EXPECT_EQ(image->shape()[1], 777);
iter->GetNextRow(&row);
}

EXPECT_EQ(i, 3);

// Manually terminate the pipeline
iter->Stop();
}

TEST_F(MindDataTestPipeline, TestComposeFail) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestComposeFail with invalid transform.";

// Resize: Non-positive size value: -1 at element: 0
// Compose: transform ops must not be null
std::shared_ptr<TensorOperation> compose1 = transforms::Compose({vision::Decode(), vision::Resize({-1})});
EXPECT_EQ(compose1, nullptr);

// Compose: transform ops must not be null
std::shared_ptr<TensorOperation> compose2 = transforms::Compose({vision::Decode(), nullptr});
EXPECT_EQ(compose2, nullptr);

// Compose: transform list must not be empty
std::shared_ptr<TensorOperation> compose3 = transforms::Compose({});
EXPECT_EQ(compose3, nullptr);
}

TEST_F(MindDataTestPipeline, TestDuplicateSuccess) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestDuplicateSuccess.";

// Create a Cifar10 Dataset
std::string folder_path = datasets_root_path_ + "/testCifar10Data/";
std::shared_ptr<Dataset> ds = Cifar10(folder_path, "all", RandomSampler(false, 10));
EXPECT_NE(ds, nullptr);

// Create objects for the tensor ops
std::shared_ptr<TensorOperation> duplicate = transforms::Duplicate();
EXPECT_NE(duplicate, nullptr);

// Create a Map operation on ds
ds = ds->Map({duplicate}, {"image"}, {"image", "image_copy"});
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, std::shared_ptr<Tensor>> row;
iter->GetNextRow(&row);

uint64_t i = 0;
while (row.size() != 0) {
i++;
auto image = row["image"];
auto image_copy = row["image_copy"];
MS_LOG(INFO) << "Tensor image shape: " << image->shape();
EXPECT_EQ(*image, *image_copy);
iter->GetNextRow(&row);
}

EXPECT_EQ(i, 10);

// Manually terminate the pipeline
iter->Stop();
}

TEST_F(MindDataTestPipeline, TestOneHotSuccess1) { TEST_F(MindDataTestPipeline, TestOneHotSuccess1) {
// Testing CutMixBatch on a batch of CHW images // Testing CutMixBatch on a batch of CHW images
// Create a Cifar10 Dataset // Create a Cifar10 Dataset
@@ -157,6 +258,127 @@ TEST_F(MindDataTestPipeline, TestOneHotFail) {
EXPECT_EQ(one_hot_op2, nullptr); EXPECT_EQ(one_hot_op2, nullptr);
} }


TEST_F(MindDataTestPipeline, TestRandomApplySuccess) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomApplySuccess.";

// Create an ImageFolder Dataset
std::string folder_path = datasets_root_path_ + "/testPK/data/";
std::shared_ptr<Dataset> ds = ImageFolder(folder_path, true, RandomSampler(false, 5));
EXPECT_NE(ds, nullptr);

// Create objects for the tensor ops
std::shared_ptr<TensorOperation> random_apply = transforms::RandomApply({vision::Resize({777, 777})}, 0.8);
EXPECT_NE(random_apply, nullptr);

// Create a Map operation on ds
ds = ds->Map({random_apply}, {"image"});
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, std::shared_ptr<Tensor>> row;
iter->GetNextRow(&row);

uint64_t i = 0;
while (row.size() != 0) {
i++;
auto image = row["image"];
auto label = row["label"];
MS_LOG(INFO) << "Tensor image shape: " << image->shape();
MS_LOG(INFO) << "Label shape: " << label->shape();
iter->GetNextRow(&row);
}

EXPECT_EQ(i, 5);

// Manually terminate the pipeline
iter->Stop();
}

TEST_F(MindDataTestPipeline, TestRandomApplyFail) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomApplyFail with invalid transform.";

// Resize: Non-positive size value: -1 at element: 0
// RandomApply: transform ops must not be null
std::shared_ptr<TensorOperation> random_apply1 = transforms::RandomApply({vision::Decode(), vision::Resize({-1})});
EXPECT_EQ(random_apply1, nullptr);

// RandomApply: transform ops must not be null
std::shared_ptr<TensorOperation> random_apply2 = transforms::RandomApply({vision::Decode(), nullptr});
EXPECT_EQ(random_apply2, nullptr);

// RandomApply: transform list must not be empty
std::shared_ptr<TensorOperation> random_apply3 = transforms::RandomApply({});
EXPECT_EQ(random_apply3, nullptr);

// RandomApply: Probability has to be between 0 and 1
std::shared_ptr<TensorOperation> random_apply4 = transforms::RandomApply({vision::Resize({100})}, -1);
EXPECT_EQ(random_apply4, nullptr);
}

TEST_F(MindDataTestPipeline, TestRandomChoiceSuccess) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomChoiceSuccess.";

// Create an ImageFolder Dataset
std::string folder_path = datasets_root_path_ + "/testPK/data/";
std::shared_ptr<Dataset> ds = ImageFolder(folder_path, true, RandomSampler(false, 3));
EXPECT_NE(ds, nullptr);

// Create objects for the tensor ops
std::shared_ptr<TensorOperation> random_choice =
transforms::RandomChoice({vision::Resize({777, 777}), vision::Resize({888, 888})});
EXPECT_NE(random_choice, nullptr);

// Create a Map operation on ds
ds = ds->Map({random_choice}, {"image"});
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, std::shared_ptr<Tensor>> row;
iter->GetNextRow(&row);

uint64_t i = 0;
while (row.size() != 0) {
i++;
auto image = row["image"];
auto label = row["label"];
MS_LOG(INFO) << "Tensor image shape: " << image->shape();
MS_LOG(INFO) << "Label shape: " << label->shape();
iter->GetNextRow(&row);
}

EXPECT_EQ(i, 3);

// Manually terminate the pipeline
iter->Stop();
}

TEST_F(MindDataTestPipeline, TestRandomChoiceFail) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomChoiceFail with invalid transform.";

// Resize: Non-positive size value: -1 at element: 0
// RandomChoice: transform ops must not be null
std::shared_ptr<TensorOperation> random_choice1 = transforms::RandomChoice({vision::Decode(), vision::Resize({-1})});
EXPECT_EQ(random_choice1, nullptr);

// RandomChoice: transform ops must not be null
std::shared_ptr<TensorOperation> random_choice2 = transforms::RandomChoice({vision::Decode(), nullptr});
EXPECT_EQ(random_choice2, nullptr);

// RandomChoice: transform list must not be empty
std::shared_ptr<TensorOperation> random_choice3 = transforms::RandomChoice({});
EXPECT_EQ(random_choice3, nullptr);
}

TEST_F(MindDataTestPipeline, TestTypeCastSuccess) { TEST_F(MindDataTestPipeline, TestTypeCastSuccess) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestTypeCastSuccess."; MS_LOG(INFO) << "Doing MindDataTestPipeline-TestTypeCastSuccess.";




+ 24
- 26
tests/ut/cpp/dataset/c_api_vision_test.cc View File

@@ -2799,14 +2799,8 @@ TEST_F(MindDataTestPipeline, TestSoftDvppDecodeResizeJpegFail) {
EXPECT_EQ(soft_dvpp_decode_resize_jpeg_op4, nullptr); EXPECT_EQ(soft_dvpp_decode_resize_jpeg_op4, nullptr);
} }


TEST_F(MindDataTestPipeline, DISABLED_TestUniformAugmentFail1) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestUniformAugmentFail1 with invalid zero num_ops parameter.";

// Create a Mnist Dataset
std::string folder_path = datasets_root_path_ + "/testMnistData/";
std::shared_ptr<Dataset> ds = Mnist(folder_path, "all", RandomSampler(false, 20));
EXPECT_NE(ds, nullptr);

TEST_F(MindDataTestPipeline, TestUniformAugmentFail1) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestUniformAugmentFail1 with invalid num_ops parameter.";
// Create objects for the tensor ops // Create objects for the tensor ops
std::shared_ptr<TensorOperation> random_crop_op = vision::RandomCrop({28, 28}); std::shared_ptr<TensorOperation> random_crop_op = vision::RandomCrop({28, 28});
EXPECT_NE(random_crop_op, nullptr); EXPECT_NE(random_crop_op, nullptr);
@@ -2814,29 +2808,33 @@ TEST_F(MindDataTestPipeline, DISABLED_TestUniformAugmentFail1) {
std::shared_ptr<TensorOperation> center_crop_op = vision::CenterCrop({16, 16}); std::shared_ptr<TensorOperation> center_crop_op = vision::CenterCrop({16, 16});
EXPECT_NE(center_crop_op, nullptr); EXPECT_NE(center_crop_op, nullptr);


// Try UniformAugment with invalid zero num_ops value
std::shared_ptr<TensorOperation> uniform_aug_op = vision::UniformAugment({random_crop_op, center_crop_op}, 0);
EXPECT_EQ(uniform_aug_op, nullptr);
}
// UniformAug: num_ops must be greater than 0
std::shared_ptr<TensorOperation> uniform_aug_op1 = vision::UniformAugment({random_crop_op, center_crop_op}, 0);
EXPECT_EQ(uniform_aug_op1, nullptr);


TEST_F(MindDataTestPipeline, DISABLED_TestUniformAugmentFail2) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestUniformAugmentFail2 with invalid negative num_ops parameter.";
// UniformAug: num_ops must be greater than 0
std::shared_ptr<TensorOperation> uniform_aug_op2 = vision::UniformAugment({random_crop_op, center_crop_op}, -1);
EXPECT_EQ(uniform_aug_op2, nullptr);


// Create a Mnist Dataset
std::string folder_path = datasets_root_path_ + "/testMnistData/";
std::shared_ptr<Dataset> ds = Mnist(folder_path, "all", RandomSampler(false, 20));
EXPECT_NE(ds, nullptr);
// UniformAug: num_ops is greater than transforms size
std::shared_ptr<TensorOperation> uniform_aug_op3 = vision::UniformAugment({random_crop_op, center_crop_op}, 3);
EXPECT_EQ(uniform_aug_op3, nullptr);
}


// Create objects for the tensor ops
std::shared_ptr<TensorOperation> random_crop_op = vision::RandomCrop({28, 28});
EXPECT_NE(random_crop_op, nullptr);
TEST_F(MindDataTestPipeline, TestUniformAugmentFail2) {
MS_LOG(INFO) << "Doing MindDataTestPipeline-TestUniformAugmentFail2 with invalid transform.";


std::shared_ptr<TensorOperation> center_crop_op = vision::CenterCrop({16, 16});
EXPECT_NE(center_crop_op, nullptr);
// UniformAug: transform ops must not be null
std::shared_ptr<TensorOperation> uniform_aug_op1 = vision::UniformAugment({vision::RandomCrop({-28})}, 1);
EXPECT_EQ(uniform_aug_op1, nullptr);

// UniformAug: transform ops must not be null
std::shared_ptr<TensorOperation> uniform_aug_op2 = vision::UniformAugment({vision::RandomCrop({28}), nullptr}, 2);
EXPECT_EQ(uniform_aug_op2, nullptr);


// Try UniformAugment with invalid negative num_ops value
std::shared_ptr<TensorOperation> uniform_aug_op = vision::UniformAugment({random_crop_op, center_crop_op}, -1);
EXPECT_EQ(uniform_aug_op, nullptr);
// UniformAug: transform list must not be empty
std::shared_ptr<TensorOperation> uniform_aug_op3 = vision::UniformAugment({}, 1);
EXPECT_EQ(uniform_aug_op3, nullptr);
} }


TEST_F(MindDataTestPipeline, TestUniformAugWithOps) { TEST_F(MindDataTestPipeline, TestUniformAugWithOps) {


Loading…
Cancel
Save