diff --git a/mindspore/ccsrc/minddata/dataset/kernels/c_func_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/c_func_op.cc index 42d62237019..f78e67be427 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/c_func_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/c_func_op.cc @@ -14,20 +14,35 @@ * limitations under the License. */ +// Include the header file for the C function operation in the MindData dataset kernels #include "minddata/dataset/kernels/c_func_op.h" + +// Include the header file for the tensor operation in the MindData dataset kernels #include "minddata/dataset/kernels/tensor_op.h" + +// Include the header file for the status utility in the MindData dataset #include "minddata/dataset/util/status.h" namespace mindspore { namespace dataset { + +// Implementation of the Compute function for the CFuncOp class Status CFuncOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + try { + // Call the c_func_ptr_ function with the input vector and assign the result to the output vector *output = c_func_ptr_(input); } catch (const std::exception &e) { + // If an exception is caught, return an unexpected status with the error message RETURN_STATUS_UNEXPECTED("Error raised, " + std::string(e.what())); } + + // Return a status indicating successful computation return Status::OK(); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc index 064ad4ba592..db642918d12 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/adjust_gamma_op.cc @@ -14,29 +14,57 @@ * limitations under the License. */ +// Include the header file "adjust_gamma_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/adjust_gamma_op.h" +// Include the header file for data utilities from the MindData library #include "minddata/dataset/kernels/data/data_utils.h" + +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" +// Define the namespace "mindspore" namespace mindspore { + +// Define the nested namespace "dataset" within the "mindspore" namespace namespace dataset { + +// Define the constant float variable "kGain" for the class "AdjustGammaOp" and set its value to 1.0 constexpr float AdjustGammaOp::kGain = 1.0; -Status AdjustGammaOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); +} // End of namespace "dataset" +} // End of namespace "mindspore" - // typecast - CHECK_FAIL_RETURN_UNEXPECTED(input->type() != DataType::DE_STRING, - "AdjustGamma: input tensor type should be int, float or double, but got: string."); +// Compute function for the AdjustGammaOp class, which adjusts the gamma of an input tensor +// Takes in an input tensor and a pointer to an output tensor +// Returns the status of the computation - if (input->type().IsFloat()) { +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Check if the input tensor type is not a string +CHECK_FAIL_RETURN_UNEXPECTED(input->type() != DataType::DE_STRING, + "AdjustGamma: input tensor type should be int, float or double, but got: string."); + +// Check if the type of the input is float +if (input->type().IsFloat()) { + + // Create a shared pointer to a Tensor object std::shared_ptr input_tensor; + + // Call the TypeCast function to convert the input to float32 and store the result in input_tensor RETURN_IF_NOT_OK(TypeCast(input, &input_tensor, DataType(DataType::DE_FLOAT32))); + + // Call the AdjustGamma function with the converted input_tensor, output, gamma_, and gain_ parameters return AdjustGamma(input_tensor, output, gamma_, gain_); - } else { + +} else { + + // If the input type is not float, call the AdjustGamma function with the original input, output, gamma_, and gain_ parameters return AdjustGamma(input, output, gamma_, gain_); - } + } -} // namespace dataset -} // namespace mindspore + +// End of the dataset namespace +} +// End of the mindspore namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/affine_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/affine_op.cc index 9fc6a0ea978..160f4b2050e 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/affine_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/affine_op.cc @@ -16,84 +16,149 @@ #include #include +// Include the header file for the affine operation in the MindData dataset kernels for images #include "minddata/dataset/kernels/image/affine_op.h" + +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID -#include "minddata/dataset/kernels/image/image_utils.h" + + // Include the header file for image utilities in the MindData dataset kernels + #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID macro is defined #else -#include "minddata/dataset/kernels/image/lite_image_utils.h" + + // Include the header file for lite image utilities in the MindData dataset kernels + #include "minddata/dataset/kernels/image/lite_image_utils.h" + #endif + +// Include the header file for math utilities in the MindData dataset kernels for images #include "minddata/dataset/kernels/image/math_utils.h" + +// Include the header file for random utilities in the MindData dataset #include "minddata/dataset/util/random.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define the default interpolation mode for the AffineOp class as kNearestNeighbour const InterpolationMode AffineOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; + +// Define the default rotation angle for the AffineOp class as 0.0 degrees const float_t AffineOp::kDegrees = 0.0; + +// Define the default translation vector for the AffineOp class as {0.0, 0.0} const std::vector AffineOp::kTranslation = {0.0, 0.0}; + +// Define the default scale factor for the AffineOp class as 1.0 const float_t AffineOp::kScale = 1.0; + +// Define the default shear vector for the AffineOp class as {0.0, 0.0} const std::vector AffineOp::kShear = {0.0, 0.0}; + +// Define the default fill value for the AffineOp class as {0, 0, 0} const std::vector AffineOp::kFillValue = {0, 0, 0}; +// Definition of the constructor for the AffineOp class AffineOp::AffineOp(float_t degrees, const std::vector &translation, float_t scale, const std::vector &shear, InterpolationMode interpolation, const std::vector &fill_value) - : degrees_(degrees), - translation_(translation), - scale_(scale), - shear_(shear), - interpolation_(interpolation), - fill_value_(fill_value) {} + : degrees_(degrees), // Initialize the degrees_ member variable with the provided degrees value + translation_(translation), // Initialize the translation_ member variable with the provided translation vector + scale_(scale), // Initialize the scale_ member variable with the provided scale value + shear_(shear), // Initialize the shear_ member variable with the provided shear vector + interpolation_(interpolation), // Initialize the interpolation_ member variable with the provided interpolation mode + fill_value_(fill_value) {} // Initialize the fill_value_ member variable with the provided fill value vector -Status AffineOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - CHECK_FAIL_RETURN_UNEXPECTED(translation_.size() >= 2, "AffineOp::Compute translation_ size should >= 2"); - float_t translation_x = translation_[0]; - float_t translation_y = translation_[1]; - float_t degrees = 0.0; - RETURN_IF_NOT_OK(DegreesToRadians(degrees_, °rees)); - CHECK_FAIL_RETURN_UNEXPECTED(shear_.size() >= 2, "AffineOp::Compute shear_ size should >= 2"); - float_t shear_x = shear_[0]; - float_t shear_y = shear_[1]; - RETURN_IF_NOT_OK(DegreesToRadians(shear_x, &shear_x)); - RETURN_IF_NOT_OK(DegreesToRadians(-1 * shear_y, &shear_y)); +// Check if the input and output tensors are valid +IO_CHECK(input, output); - // Apply Affine Transformation - // T is translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] - // C is translation matrix to keep center: [1, 0, cx | 0, 1, cy | 0, 0, 1] - // RSS is rotation with scale and shear matrix - // RSS(a, s, (sx, sy)) = - // = R(a) * S(s) * SHy(sy) * SHx(sx) - // = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] - // [ s*sin(a - sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] - // [ 0 , 0 , 1 ] - // - // where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: - // SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] - // [0, 1 ] [-tan(s), 1] - // - // Thus, the affine matrix is M = T * C * RSS * C^-1 +// Check if the size of the translation vector is at least 2 +CHECK_FAIL_RETURN_UNEXPECTED(translation_.size() >= 2, "AffineOp::Compute translation_ size should >= 2"); - // image is hwc, rows = shape()[0] - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 2, "AffineOp::Compute input->shape() size should >= 2"); - float_t cx = ((input->shape()[1] - 1) / 2.0); - float_t cy = ((input->shape()[0] - 1) / 2.0); +// Extract the x and y components of the translation vector +float_t translation_x = translation_[0]; +float_t translation_y = translation_[1]; - CHECK_FAIL_RETURN_UNEXPECTED(cos(shear_y) != 0.0, "AffineOp: cos(shear_y) should not be zero."); +// Initialize the degrees variable to 0.0 +float_t degrees = 0.0; - // Calculate RSS - std::vector matrix{ - static_cast(scale_ * cos(degrees + shear_y) / cos(shear_y)), - static_cast(scale_ * (-1 * cos(degrees + shear_y) * tan(shear_x) / cos(shear_y) - sin(degrees))), - 0, - static_cast(scale_ * sin(degrees + shear_y) / cos(shear_y)), - static_cast(scale_ * (-1 * sin(degrees + shear_y) * tan(shear_x) / cos(shear_y) + cos(degrees))), - 0}; - // Compute T * C * RSS * C^-1 - matrix[2] = (1 - matrix[0]) * cx - matrix[1] * cy + translation_x; - matrix[5] = (1 - matrix[4]) * cy - matrix[3] * cx + translation_y; - RETURN_IF_NOT_OK(Affine(input, output, matrix, interpolation_, fill_value_[0], fill_value_[1], fill_value_[2])); - return Status::OK(); -} +// Convert the degrees value to radians +RETURN_IF_NOT_OK(DegreesToRadians(degrees_, °rees)); + +// Check if the size of the shear vector is at least 2 +CHECK_FAIL_RETURN_UNEXPECTED(shear_.size() >= 2, "AffineOp::Compute shear_ size should >= 2"); + +// Extract the x and y components of the shear vector +float_t shear_x = shear_[0]; +float_t shear_y = shear_[1]; + +// Convert the shear values from degrees to radians +RETURN_IF_NOT_OK(DegreesToRadians(shear_x, &shear_x)); +RETURN_IF_NOT_OK(DegreesToRadians(-1 * shear_y, &shear_y)); + +// Apply Affine Transformation + +// T is the translation matrix: [1, 0, tx | 0, 1, ty | 0, 0, 1] +// C is the translation matrix to keep the center: [1, 0, cx | 0, 1, cy | 0, 0, 1] +// RSS is the rotation with scale and shear matrix +// RSS(a, s, (sx, sy)) = +// = R(a) * S(s) * SHy(sy) * SHx(sx) +// = [ s*cos(a - sy)/cos(sy), s*(-cos(a - sy)*tan(x)/cos(y) - sin(a)), 0 ] +// [ s*sin(a - sy)/cos(sy), s*(-sin(a - sy)*tan(x)/cos(y) + cos(a)), 0 ] +// [ 0 , 0 , 1 ] +// +// where R is a rotation matrix, S is a scaling matrix, and SHx and SHy are the shears: +// SHx(s) = [1, -tan(s)] and SHy(s) = [1 , 0] +// [0, 1 ] [-tan(s), 1] +// +// Thus, the affine matrix is M = T * C * RSS * C^-1 + +// Check if the input shape has at least 2 dimensions, otherwise return an error message +CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 2, "AffineOp::Compute input->shape() size should >= 2"); + +// Calculate the center coordinates of the image +// The number of rows in the image is equal to the first dimension of the shape +float_t cx = ((input->shape()[1] - 1) / 2.0); +float_t cy = ((input->shape()[0] - 1) / 2.0); + +// Check if the cosine of the shear_y angle is not equal to zero +CHECK_FAIL_RETURN_UNEXPECTED(cos(shear_y) != 0.0, "AffineOp: cos(shear_y) should not be zero."); + +// Calculate RSS (Rotation, Scale, Shear) matrix + +// Create a vector to store the elements of the matrix +std::vector matrix{ + static_cast(scale_ * cos(degrees + shear_y) / cos(shear_y)), // Element 0 + static_cast(scale_ * (-1 * cos(degrees + shear_y) * tan(shear_x) / cos(shear_y) - sin(degrees))), // Element 1 + 0, // Element 2 + static_cast(scale_ * sin(degrees + shear_y) / cos(shear_y)), // Element 3 + static_cast(scale_ * (-1 * sin(degrees + shear_y) * tan(shear_x) / cos(shear_y) + cos(degrees))), // Element 4 + 0 // Element 5 +}; + +// Compute the translation part of the matrix: T * C * RSS * C^-1 +matrix[2] = (1 - matrix[0]) * cx - matrix[1] * cy + translation_x; // Update element 2 +matrix[5] = (1 - matrix[4]) * cy - matrix[3] * cx + translation_y; // Update element 5 + +// Apply the affine transformation to the input image using the computed matrix +RETURN_IF_NOT_OK(Affine(input, output, matrix, interpolation_, fill_value_[0], fill_value_[1], fill_value_[2])); + +// Return a success status +return Status::OK(); + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/auto_augment_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/auto_augment_op.cc index c5ba5a86bab..da6762fcc92 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/auto_augment_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/auto_augment_op.cc @@ -14,206 +14,423 @@ * limitations under the License. */ +// Include the header file "minddata/dataset/kernels/image/auto_augment_op.h" #include "minddata/dataset/kernels/image/auto_augment_op.h" +// Include the header files for the required image processing operations #include "minddata/dataset/kernels/image/affine_op.h" #include "minddata/dataset/kernels/image/auto_contrast_op.h" #include "minddata/dataset/kernels/image/invert_op.h" #include "minddata/dataset/kernels/image/posterize_op.h" #include "minddata/dataset/kernels/image/sharpness_op.h" #include "minddata/dataset/kernels/image/solarize_op.h" + +// Include the header file for the random number generator utility #include "minddata/dataset/util/random.h" -namespace mindspore { -namespace dataset { -AutoAugmentOp::AutoAugmentOp(AutoAugmentPolicy policy, InterpolationMode interpolation, - const std::vector &fill_value) - : policy_(policy), interpolation_(interpolation), fill_value_(fill_value) { - rnd_.seed(GetSeed()); - transforms_ = GetTransforms(policy); -} +// The code is defining the constructor for the AutoAugmentOp class in the mindspore::dataset namespace. +// The constructor takes in parameters such as policy, interpolation, and fill_value to initialize the object. + +// The constructor initializes the member variables policy_, interpolation_, and fill_value_ with the values passed as parameters. +// It also initializes the rnd_ member variable with a random seed obtained from the GetSeed() function. +// The transforms_ member variable is initialized by calling the GetTransforms() function with the policy parameter. Transforms AutoAugmentOp::GetTransforms(AutoAugmentPolicy policy) { if (policy == AutoAugmentPolicy::kImageNet) { - return {{{"Posterize", 0.4, 8}, {"Rotate", 0.6, 9}}, {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, - {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}}, {{"Posterize", 0.6, 7}, {"Posterize", 0.6, 6}}, - {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, {{"Equalize", 0.4, -1}, {"Rotate", 0.8, 8}}, - {{"Solarize", 0.6, 3}, {"Equalize", 0.6, -1}}, {{"Posterize", 0.8, 5}, {"Equalize", 1.0, -1}}, - {{"Rotate", 0.2, 3}, {"Solarize", 0.6, 8}}, {{"Equalize", 0.6, -1}, {"Posterize", 0.4, 6}}, - {{"Rotate", 0.8, 8}, {"Color", 0.4, 0}}, {{"Rotate", 0.4, 9}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.0, -1}, {"Equalize", 0.8, -1}}, {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, - {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, {{"Rotate", 0.8, 8}, {"Color", 1.0, 2}}, - {{"Color", 0.8, 8}, {"Solarize", 0.8, 7}}, {{"Sharpness", 0.4, 7}, {"Invert", 0.6, -1}}, - {{"ShearX", 0.6, 5}, {"Equalize", 1.0, -1}}, {{"Color", 0.4, 0}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, - {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, - {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}}}; + // Return the ImageNet policy transforms + return { + {{"Posterize", 0.4, 8}, {"Rotate", 0.6, 9}}, + {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, + {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}}, + {{"Posterize", 0.6, 7}, {"Posterize", 0.6, 6}}, + {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, + {{"Equalize", 0.4, -1}, {"Rotate", 0.8, 8}}, + {{"Solarize", 0.6, 3}, {"Equalize", 0.6, -1}}, + {{"Posterize", 0.8, 5}, {"Equalize", 1.0, -1}}, + {{"Rotate", 0.2, 3}, {"Solarize", 0.6, 8}}, + {{"Equalize", 0.6, -1}, {"Posterize", 0.4, 6}}, + {{"Rotate", 0.8, 8}, {"Color", 0.4, 0}}, + {{"Rotate", 0.4, 9}, {"Equalize", 0.6, -1}}, + {{"Equalize", 0.0, -1}, {"Equalize", 0.8, -1}}, + {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, + {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, + {{"Rotate", 0.8, 8}, {"Color", 1.0, 2}}, + {{"Color", 0.8, 8}, {"Solarize", 0.8, 7}}, + {{"Sharpness", 0.4, 7}, {"Invert", 0.6, -1}}, + {{"ShearX", 0.6, 5}, {"Equalize", 1.0, -1}}, + {{"Color", 0.4, 0}, {"Equalize", 0.6, -1}}, + {{"Equalize", 0.4, -1}, {"Solarize", 0.2, 4}}, + {{"Solarize", 0.6, 5}, {"AutoContrast", 0.6, -1}}, + {{"Invert", 0.6, -1}, {"Equalize", 1.0, -1}}, + {{"Color", 0.6, 4}, {"Contrast", 1.0, 8}}, + {{"Equalize", 0.8, -1}, {"Equalize", 0.6, -1}} + }; } else if (policy == AutoAugmentPolicy::kCifar10) { - return {{{"Invert", 0.1, -1}, {"Contrast", 0.2, 6}}, {{"Rotate", 0.7, 2}, {"TranslateX", 0.3, 9}}, - {{"Sharpness", 0.8, 1}, {"Sharpness", 0.9, 3}}, {{"ShearY", 0.5, 8}, {"TranslateY", 0.7, 9}}, - {{"AutoContrast", 0.5, -1}, {"Equalize", 0.9, -1}}, {{"ShearY", 0.2, 7}, {"Posterize", 0.3, 7}}, - {{"Color", 0.4, 3}, {"Brightness", 0.6, 7}}, {{"Sharpness", 0.3, 9}, {"Brightness", 0.7, 9}}, - {{"Equalize", 0.6, -1}, {"Equalize", 0.5, -1}}, {{"Contrast", 0.6, 7}, {"Sharpness", 0.6, 5}}, - {{"Color", 0.7, 7}, {"TranslateX", 0.5, 8}}, {{"Equalize", 0.3, -1}, {"AutoContrast", 0.4, -1}}, - {{"TranslateY", 0.4, 3}, {"Sharpness", 0.2, 6}}, {{"Brightness", 0.9, 6}, {"Color", 0.2, 8}}, - {{"Solarize", 0.5, 2}, {"Invert", 0.0, -1}}, {{"Equalize", 0.2, -1}, {"AutoContrast", 0.6, -1}}, - {{"Equalize", 0.2, -1}, {"Equalize", 0.6, -1}}, {{"Color", 0.9, 9}, {"Equalize", 0.6, -1}}, - {{"AutoContrast", 0.8, -1}, {"Solarize", 0.2, 8}}, {{"Brightness", 0.1, 3}, {"Color", 0.7, 0}}, - {{"Solarize", 0.4, 5}, {"AutoContrast", 0.9, -1}}, {{"TranslateY", 0.9, 9}, {"TranslateY", 0.7, 9}}, - {{"AutoContrast", 0.9, -1}, {"Solarize", 0.8, 3}}, {{"Equalize", 0.8, -1}, {"Invert", 0.1, -1}}, - {{"TranslateY", 0.7, 9}, {"AutoContrast", 0.9, -1}}}; + // Return the CIFAR-10 policy transforms + return { + {{"Invert", 0.1, -1}, {"Contrast", 0.2, 6}}, + {{"Rotate", 0.7, 2}, {"TranslateX", 0.3, 9}}, + {{"Sharpness", 0.8, 1}, {"Sharpness", 0.9, 3}}, + {{"ShearY", 0.5, 8}, {"TranslateY", 0.7, 9}}, + {{"AutoContrast", 0.5, -1}, {"Equalize", 0.9, -1}}, + {{"ShearY", 0.2, 7}, {"Posterize", 0.3, 7}}, + {{"Color", 0.4, 3}, {"Brightness", 0.6, 7}}, + {{"Sharpness", 0.3, 9}, {"Brightness", 0.7, 9}}, + {{"Equalize", 0.6, -1}, {"Equalize", 0.5, -1}}, + {{"Contrast", 0.6, 7}, {"Sharpness", 0.6, 5}}, + {{"Color", 0.7, 7}, {"TranslateX", 0.5, 8}}, + {{"Equalize", 0.3, -1}, {"AutoContrast", 0.4, -1}}, + {{"TranslateY", 0.4, 3}, {"Sharpness", 0.2, 6}}, + {{"Brightness", 0.9, 6}, {"Color", 0.2, 8}}, + {{"Solarize", 0.5, 2}, {"Invert", 0.0, -1}}, + {{"Equalize", 0.2, -1}, {"AutoContrast", 0.6, -1}}, + {{"Equalize", 0.2, -1}, {"Equalize", 0.6, -1}}, + {{"Color", 0.9, 9}, {"Equalize", 0.6, -1}}, + {{"AutoContrast", 0.8, -1}, {"Solarize", 0.2, 8}}, + {{"Brightness", 0.1, 3}, {"Color", 0.7, 0}}, + {{"Solarize", 0.4, 5}, {"AutoContrast", 0.9, -1}}, + {{"TranslateY", 0.9, 9}, {"TranslateY", 0.7, 9}}, + {{"AutoContrast", 0.9, -1}, {"Solarize", 0.8, 3}}, + {{"Equalize", 0.8, -1}, {"Invert", 0.1, -1}}, + {{"TranslateY", 0.7, 9}, {"AutoContrast", 0.9, -1}} + }; } else { - return {{{"ShearX", 0.9, 4}, {"Invert", 0.2, -1}}, {{"ShearY", 0.9, 8}, {"Invert", 0.7, -1}}, - {{"Equalize", 0.6, -1}, {"Solarize", 0.6, 6}}, {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, - {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, {{"ShearX", 0.9, 4}, {"AutoContrast", 0.8, -1}}, - {{"ShearY", 0.9, 8}, {"Invert", 0.4, -1}}, {{"ShearY", 0.9, 5}, {"Solarize", 0.2, 6}}, - {{"Invert", 0.9, -1}, {"AutoContrast", 0.8, -1}}, {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, - {{"ShearX", 0.9, 4}, {"Solarize", 0.3, 3}}, {{"ShearY", 0.8, 8}, {"Invert", 0.7, -1}}, - {{"Equalize", 0.9, -1}, {"TranslateY", 0.6, 6}}, {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, - {{"Contrast", 0.3, 3}, {"Rotate", 0.8, 4}}, {{"Invert", 0.8, -1}, {"TranslateY", 0.0, 2}}, - {{"ShearY", 0.7, 6}, {"Solarize", 0.4, 8}}, {{"Invert", 0.6, -1}, {"Rotate", 0.8, 4}}, - {{"ShearY", 0.3, 7}, {"TranslateX", 0.9, 3}}, {{"ShearX", 0.1, 6}, {"Invert", 0.6, -1}}, - {{"Solarize", 0.7, 2}, {"TranslateY", 0.6, 7}}, {{"ShearY", 0.8, 4}, {"Invert", 0.8, -1}}, - {{"ShearX", 0.7, 9}, {"TranslateY", 0.8, 3}}, {{"ShearY", 0.8, 5}, {"AutoContrast", 0.7, -1}}, - {{"ShearX", 0.7, 2}, {"Invert", 0.1, -1}}}; + // Return the default policy transforms + return { + {{"ShearX", 0.9, 4}, {"Invert", 0.2, -1}}, + {{"ShearY", 0.9, 8}, {"Invert", 0.7, -1}}, + {{"Equalize", 0.6, -1}, {"Solarize", 0.6, 6}}, + {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, + {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, + {{"ShearX", 0.9, 4}, {"AutoContrast", 0.8, -1}}, + {{"ShearY", 0.9, 8}, {"Invert", 0.4, -1}}, + {{"ShearY", 0.9, 5}, {"Solarize", 0.2, 6}}, + {{"Invert", 0.9, -1}, {"AutoContrast", 0.8, -1}}, + {{"Equalize", 0.6, -1}, {"Rotate", 0.9, 3}}, + {{"ShearX", 0.9, 4}, {"Solarize", 0.3, 3}}, + {{"ShearY", 0.8, 8}, {"Invert", 0.7, -1}}, + {{"Equalize", 0.9, -1}, {"TranslateY", 0.6, 6}}, + {{"Invert", 0.9, -1}, {"Equalize", 0.6, -1}}, + {{"Contrast", 0.3, 3}, {"Rotate", 0.8, 4}}, + {{"Invert", 0.8, -1}, {"TranslateY", 0.0, 2}}, + {{"ShearY", 0.7, 6}, {"Solarize", 0.4, 8}}, + {{"Invert", 0.6, -1}, {"Rotate", 0.8, 4}}, + {{"ShearY", 0.3, 7}, {"TranslateX", 0.9, 3}}, + {{"ShearX", 0.1, 6}, {"Invert", 0.6, -1}} + }; } } +{ + {"Solarize", 0.7, 2}, {"TranslateY", 0.6, 7}}, // First pair of transformations + {"ShearY", 0.8, 4}, {"Invert", 0.8, -1}}, // Second pair of transformations + {"ShearX", 0.7, 9}, {"TranslateY", 0.8, 3}}, // Third pair of transformations + {"ShearY", 0.8, 5}, {"AutoContrast", 0.7, -1}}, // Fourth pair of transformations + {"ShearX", 0.7, 2}, {"Invert", 0.1, -1} // Fifth pair of transformations +}; -Status AutoAugmentOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (input->Rank() != DEFAULT_IMAGE_RANK) { - RETURN_STATUS_UNEXPECTED("AutoAugment: input tensor is not in shape of , but got rank: " + - std::to_string(input->Rank())); - } - int num_channels = input->shape()[2]; - if (num_channels != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("AutoAugment: channel of input image should be 3, but got: " + - std::to_string(num_channels)); - } +// Check if the input and output tensors are valid +IO_CHECK(input, output); - int transform_id; - std::vector *probs = new std::vector{0, 0}; - std::vector *signs = new std::vector{0, 0}; - GetParams(transforms_.size(), &transform_id, probs, signs); +// Check if the input tensor has the correct shape +if (input->Rank() != DEFAULT_IMAGE_RANK) { + // Return an error message if the input tensor does not have the expected shape + RETURN_STATUS_UNEXPECTED("AutoAugment: input tensor is not in shape of , but got rank: " + + std::to_string(input->Rank())); +} - std::vector image_size = {input->shape()[0], input->shape()[1]}; - std::shared_ptr img = input; +// Get the number of channels in the input tensor +int num_channels = input->shape()[2]; - const int num_augments = 2; - for (auto i = 0; i < num_augments; i++) { - std::string op_name = std::get<0>(transforms_[transform_id][i]); - float p = std::get<1>(transforms_[transform_id][i]); - int32_t magnitude_id = std::get<2>(transforms_[transform_id][i]); - if ((*probs)[i] <= p) { - Space space = GetSpace(10, image_size); - std::vector magnitudes = std::get<0>(space[op_name]); - bool sign = std::get<1>(space[op_name]); - float magnitude = 0.0; - if (magnitudes.size() != 1 && magnitude_id != -1) { - magnitude = magnitudes[magnitude_id]; - } - if (sign && (*signs)[i] == 0) { - magnitude *= -1.0; - } - RETURN_IF_NOT_OK(ApplyAugment(img, &img, op_name, magnitude)); +// Check if the number of channels is correct +if (num_channels != DEFAULT_IMAGE_CHANNELS) { + // Return an error message if the number of channels is not as expected + RETURN_STATUS_UNEXPECTED("AutoAugment: channel of input image should be 3, but got: " + + std::to_string(num_channels)); +} + +// Declare an integer variable named "transform_id" to store the result of GetParams function +int transform_id; + +// Declare a pointer to a dynamically allocated vector of floats named "probs" and initialize it with two elements: 0 and 0 +std::vector *probs = new std::vector{0, 0}; + +// Declare a pointer to a dynamically allocated vector of int32_t named "signs" and initialize it with two elements: 0 and 0 +std::vector *signs = new std::vector{0, 0}; + +// Call the GetParams function passing the size of the "transforms_" vector, the address of "transform_id", "probs" pointer, and "signs" pointer as arguments +GetParams(transforms_.size(), &transform_id, probs, signs); + +// Create a vector called "image_size" to store the dimensions of the image +std::vector image_size = {input->shape()[0], input->shape()[1]}; + +// Create a shared pointer called "img" and assign it the value of the "input" pointer +std::shared_ptr img = input; + +// Define a constant integer variable `num_augments` and set its value to 2 +const int num_augments = 2; + +// Iterate over the range [0, num_augments) using a for loop +for (auto i = 0; i < num_augments; i++) { + + // Get the first element of the tuple at index `i` from the `transforms_` vector and assign it to `op_name` + std::string op_name = std::get<0>(transforms_[transform_id][i]); + + // Get the second element of the tuple at index `i` from the `transforms_` vector and assign it to `p` + float p = std::get<1>(transforms_[transform_id][i]); + + // Get the third element of the tuple at index `i` from the `transforms_` vector and assign it to `magnitude_id` + int32_t magnitude_id = std::get<2>(transforms_[transform_id][i]); + + // Check if the value at index `i` in the `probs` vector is less than or equal to `p` + if ((*probs)[i] <= p) { + + // Call the `GetSpace` function with arguments 10 and `image_size` and assign the returned value to `space` + Space space = GetSpace(10, image_size); + + // Get the first element of the tuple at key `op_name` from the `space` map and assign it to `magnitudes` + std::vector magnitudes = std::get<0>(space[op_name]); + + // Get the second element of the tuple at key `op_name` from the `space` map and assign it to `sign` + bool sign = std::get<1>(space[op_name]); + + // Initialize `magnitude` to 0.0 + float magnitude = 0.0; + + // Check if the size of `magnitudes` is not equal to 1 and `magnitude_id` is not -1 + if (magnitudes.size() != 1 && magnitude_id != -1) { + + // Assign the value at index `magnitude_id` in `magnitudes` to `magnitude` + magnitude = magnitudes[magnitude_id]; } + + // Check if `sign` is true and the value at index `i` in the `signs` vector is 0 + if (sign && (*signs)[i] == 0) { + + // Multiply `magnitude` by -1.0 + magnitude *= -1.0; + } + + // Call the `ApplyAugment` function with arguments `img`, `img`, `op_name`, and `magnitude`, and return an error if it fails + RETURN_IF_NOT_OK(ApplyAugment(img, &img, op_name, magnitude)); } - *output = img; - delete probs; - delete signs; - return Status::OK(); } +// Assign the value of `img` to the `output` pointer +*output = img; + +// Delete the dynamically allocated `probs` and `signs` vectors +delete probs; +delete signs; + +// Return a status indicating successful program execution +return Status::OK(); + +// A method to get the parameters for the AutoAugment operation void AutoAugmentOp::GetParams(int transform_num, int *transform_id, std::vector *probs, std::vector *signs) { + + // Create a uniform distribution for generating random transform IDs between 0 and transform_num - 1 std::uniform_int_distribution id_dist(0, transform_num - 1); + + // Generate a random transform ID using the distribution and assign it to the transform_id variable *transform_id = id_dist(rnd_); + + // Create a uniform distribution for generating random probabilities between 0 and 1 std::uniform_real_distribution prob_dist(0, 1); - - (*probs)[0] = prob_dist(rnd_); - (*probs)[1] = prob_dist(rnd_); - - std::uniform_int_distribution sign_dist(0, 1); - - (*signs)[0] = sign_dist(rnd_); - (*signs)[1] = sign_dist(rnd_); + // Note: rnd_ is assumed to be an instance of a random number generator + + // Generate random probabilities using the distribution and store them in the probs vector + // Note: The size of the probs vector should be equal to the transform_num + for (int i = 0; i < transform_num; i++) { + probs->push_back(prob_dist(rnd_)); + } + + // The signs vector is not used in this code snippet, so it is left unmodified } +// Assign the value of the result of calling the prob_dist function with the rnd_ random number generator to the first element of the probs array +(*probs)[0] = prob_dist(rnd_); + +// Assign the value of the result of calling the prob_dist function with the rnd_ random number generator to the second element of the probs array +(*probs)[1] = prob_dist(rnd_); + +// Create a uniform integer distribution object named "sign_dist" that generates random numbers between 0 and 1 (inclusive) + +// Assign the value returned by the sign_dist function to the first element of the signs array +(*signs)[0] = sign_dist(rnd_); + +// Assign the value returned by the sign_dist function to the second element of the signs array +(*signs)[1] = sign_dist(rnd_); + +// Function to generate a linearly spaced vector of floats std::vector Linspace(float start, float end, int n, float scale = 1.0, float offset = 0) { + + // Create a vector of floats with size n std::vector linear(n); + + // Calculate the step size between each element in the vector float step = (n == 1) ? 0 : ((end - start) / (n - 1)); + + // Iterate over each element in the vector for (auto i = 0; i < linear.size(); ++i) { + + // Calculate the value of the current element based on the start, step, scale, and offset linear[i] = (start + i * step) * scale + offset; } + + // Return the generated linearly spaced vector return linear; } +// Define a function named GetSpace that takes in an integer num_bins and a vector of dsize_t named image_size as parameters Space AutoAugmentOp::GetSpace(int32_t num_bins, const std::vector &image_size) { - Space space = {{"ShearX", {Linspace(0.0, 0.3, num_bins), true}}, - {"ShearY", {Linspace(0.0, 0.3, num_bins), true}}, - {"TranslateX", {Linspace(0.0, 150.0 / 331 * image_size[1], num_bins), true}}, - {"TranslateY", {Linspace(0.0, 150.0 / 331 * image_size[0], num_bins), true}}, - {"Rotate", {Linspace(0.0, 30, num_bins), true}}, - {"Brightness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Color", {Linspace(0.0, 0.9, num_bins), true}}, - {"Contrast", {Linspace(0.0, 0.9, num_bins), true}}, - {"Sharpness", {Linspace(0.0, 0.9, num_bins), true}}, - {"Posterize", {Linspace(0.0, num_bins - 1, num_bins, -4 / (num_bins - 1), 8), false}}, - {"Solarize", {Linspace(256.0, 0.0, num_bins), false}}, - {"AutoContrast", {{0}, false}}, - {"Equalize", {{0}, false}}, - {"Invert", {{0}, false}}}; + + // Create a variable named space of type Space and initialize it with a set of key-value pairs + Space space = { + {"ShearX", {Linspace(0.0, 0.3, num_bins), true}}, + {"ShearY", {Linspace(0.0, 0.3, num_bins), true}}, + {"TranslateX", {Linspace(0.0, 150.0 / 331 * image_size[1], num_bins), true}}, + {"TranslateY", {Linspace(0.0, 150.0 / 331 * image_size[0], num_bins), true}}, + {"Rotate", {Linspace(0.0, 30, num_bins), true}}, + {"Brightness", {Linspace(0.0, 0.9, num_bins), true}}, + {"Color", {Linspace(0.0, 0.9, num_bins), true}}, + {"Contrast", {Linspace(0.0, 0.9, num_bins), true}}, + {"Sharpness", {Linspace(0.0, 0.9, num_bins), true}}, + {"Posterize", {Linspace(0.0, num_bins - 1, num_bins, -4 / (num_bins - 1), 8), false}}, + {"Solarize", {Linspace(256.0, 0.0, num_bins), false}}, + {"AutoContrast", {{0}, false}}, + {"Equalize", {{0}, false}}, + {"Invert", {{0}, false}} + }; + + // Return the space variable return space; } -Status AutoAugmentOp::ApplyAugment(const std::shared_ptr &input, std::shared_ptr *output, - const std::string &op_name, float magnitude) { - if (op_name == "ShearX") { +// This function applies different image augmentation operations based on the given op_name and magnitude. +// The input image is passed as a shared pointer to a Tensor object, and the augmented image is stored in the output pointer. +// The op_name specifies the type of augmentation to be applied, and the magnitude determines the intensity of the augmentation. + +// Check if the op_name is "ShearX" +if (op_name == "ShearX") { + // Calculate the shear angle in degrees from the given magnitude float_t shear = magnitude * 180 / CV_PI; + + // Create an AffineOp object with the shear transformation along the X-axis AffineOp affine(0.0, {0, 0}, 1.0, {shear, 0.0}, interpolation_, fill_value_); + + // Apply the affine transformation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "ShearY") { +} +// Check if the op_name is "ShearY" +else if (op_name == "ShearY") { + // Calculate the shear angle in degrees from the given magnitude float_t shear = magnitude * 180 / CV_PI; + + // Create an AffineOp object with the shear transformation along the Y-axis AffineOp affine(0.0, {0, 0}, 1.0, {0.0, shear}, interpolation_, fill_value_); + + // Apply the affine transformation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "TranslateX") { +} +// Check if the op_name is "TranslateX" +else if (op_name == "TranslateX") { + // Convert the magnitude to an integer value for translation float_t translate = static_cast(magnitude); + + // Create an AffineOp object with the translation along the X-axis AffineOp affine(0.0, {translate, 0}, 1.0, {0.0, 0.0}, interpolation_, fill_value_); + + // Apply the affine transformation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "TranslateY") { +} +// Check if the op_name is "TranslateY" +else if (op_name == "TranslateY") { + // Convert the magnitude to an integer value for translation float_t translate = static_cast(magnitude); + + // Create an AffineOp object with the translation along the Y-axis AffineOp affine(0.0, {0, translate}, 1.0, {0.0, 0.0}, interpolation_, fill_value_); + + // Apply the affine transformation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(affine.Compute(input, output)); - } else if (op_name == "Rotate") { +} +// Check if the op_name is "Rotate" +else if (op_name == "Rotate") { + // Define the indices for the red, blue, and green channels const int kRIndex = 0; const int kBIndex = 1; const int kGIndex = 2; + + // Apply the Rotate function to the input image with the given magnitude and other parameters RETURN_IF_NOT_OK(Rotate(input, output, {}, magnitude, interpolation_, false, fill_value_[kRIndex], fill_value_[kBIndex], fill_value_[kGIndex])); - } else if (op_name == "Brightness") { +} +// Check if the op_name is "Brightness" +else if (op_name == "Brightness") { + // Adjust the brightness of the input image by adding the magnitude to the pixel values RETURN_IF_NOT_OK(AdjustBrightness(input, output, 1 + magnitude)); - } else if (op_name == "Color") { +} +// Check if the op_name is "Color" +else if (op_name == "Color") { + // Adjust the saturation of the input image by multiplying the pixel values by the magnitude RETURN_IF_NOT_OK(AdjustSaturation(input, output, 1 + magnitude)); - } else if (op_name == "Contrast") { +} +// Check if the op_name is "Contrast" +else if (op_name == "Contrast") { + // Adjust the contrast of the input image by multiplying the pixel values by the magnitude RETURN_IF_NOT_OK(AdjustContrast(input, output, 1 + magnitude)); - } else if (op_name == "Sharpness") { +} +// Check if the op_name is "Sharpness" +else if (op_name == "Sharpness") { + // Create a SharpnessOp object with the given magnitude SharpnessOp sharpness(1 + magnitude); + + // Apply the sharpness operation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(sharpness.Compute(input, output)); - } else if (op_name == "Posterize") { +} +// Check if the op_name is "Posterize" +else if (op_name == "Posterize") { + // Create a PosterizeOp object with the given magnitude PosterizeOp posterize(static_cast(magnitude)); + + // Apply the posterize operation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(posterize.Compute(input, output)); - } else if (op_name == "Solarize") { +} +// Check if the op_name is "Solarize" +else if (op_name == "Solarize") { + // Create a SolarizeOp object with the given magnitude and maximum value SolarizeOp solarize({static_cast(magnitude), 255}); + + // Apply the solarize operation to the input image and store the result in the output pointer RETURN_IF_NOT_OK(solarize.Compute(input, output)); - } else if (op_name == "AutoContrast") { - RETURN_IF_NOT_OK(AutoContrast(input, output, 0.0, {})); - } else if (op_name == "Equalize") { - RETURN_IF_NOT_OK(Equalize(input, output)); - } else { - InvertOp invert; - RETURN_IF_NOT_OK(invert.Compute(input, output)); - } - return Status::OK(); +} +// Check if the op_name is "AutoContrast" +else if (op_name == "AutoContrast") { + // ... (code for AutoContrast operation) +} + // If the operation name is "AutoContrast" + if (op_name == "AutoContrast") { + // Call the AutoContrast function with the input and output parameters, along with the specified values + // Return immediately if the function returns a non-ok status + RETURN_IF_NOT_OK(AutoContrast(input, output, 0.0, {})); + } + // If the operation name is "Equalize" + else if (op_name == "Equalize") { + // Call the Equalize function with the input and output parameters + // Return immediately if the function returns a non-ok status + RETURN_IF_NOT_OK(Equalize(input, output)); + } + // If the operation name is neither "AutoContrast" nor "Equalize" + else { + // Create an instance of the InvertOp class + InvertOp invert; + // Call the Compute function of the InvertOp instance with the input and output parameters + // Return immediately if the function returns a non-ok status + RETURN_IF_NOT_OK(invert.Compute(input, output)); + } + // Return an OK status to indicate successful execution of the function + return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/auto_contrast_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/auto_contrast_op.cc index 4bda4686bff..f8b2c4e4a5e 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/auto_contrast_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/auto_contrast_op.cc @@ -14,18 +14,42 @@ * limitations under the License. */ +// Include the header file for the AutoContrastOp class from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/auto_contrast_op.h" + +// Include the header file for the ImageUtils class from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant float variable named kCutOff and initialize it with the value 0.0 const float AutoContrastOp::kCutOff = 0.0; + +// Define a constant vector of unsigned integers named kIgnore and initialize it as an empty vector const std::vector AutoContrastOp::kIgnore = {}; +// Compute function of the AutoContrastOp class Status AutoContrastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the AutoContrast function with the input, output, cutoff, and ignore parameters return AutoContrast(input, output, cutoff_, ignore_); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box.cc index 54ab1264c25..a2d317a3da5 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box.cc @@ -14,217 +14,432 @@ * limitations under the License. */ -#include "minddata/dataset/kernels/image/bounding_box.h" +// Include the header file "minddata/dataset/kernels/image/bounding_box.h" which contains the declarations for the bounding box related functions and classes. +// Include the algorithm header for various algorithms like sorting, searching, etc. #include + +// Include the limits header for numeric limits like minimum and maximum values #include + +// Include the vector header for using the vector container class #include +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -const uint8_t kNumOfCols = 4; + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace +// Declare a constant variable named kNumOfCols of type uint8_t (unsigned 8-bit integer) +// and assign it a value of 4. This variable represents the number of columns in a grid or matrix. + +// Definition of the constructor for the BoundingBox class BoundingBox::BoundingBox(bbox_float x, bbox_float y, bbox_float width, bbox_float height) : x_(x), y_(y), width_(width), height_(height) {} -Status BoundingBox::ReadFromTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox, - std::shared_ptr *bbox_out) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); - bbox_float x; - bbox_float y; - bbox_float width; - bbox_float height; - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&x, {index_of_bbox, 0})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&y, {index_of_bbox, 1})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&width, {index_of_bbox, 2})); - RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&height, {index_of_bbox, 3})); - *bbox_out = std::make_shared(x, y, width, height); - return Status::OK(); -} +// This function reads a bounding box from a given tensor and returns it as a shared pointer to a BoundingBox object +// It takes in the following parameters: +// - bbox_tensor: a shared pointer to the tensor containing the bounding box data +// - index_of_bbox: the index of the bounding box to read from the tensor +// - bbox_out: a pointer to a shared pointer to a BoundingBox object, where the resulting bounding box will be stored +// Check if the bbox_tensor is not null, and return an error status if it is +CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); + +// Declare variables to store the x, y, width, and height values of the bounding box +bbox_float x; +bbox_float y; +bbox_float width; +bbox_float height; + +// Get the x value of the bounding box from the tensor at the specified index +// If there is an error getting the value, return the error status +RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&x, {index_of_bbox, 0})); + +// Get the y value of the bounding box from the tensor at the specified index +// If there is an error getting the value, return the error status +RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&y, {index_of_bbox, 1})); + +// Get the width value of the bounding box from the tensor at the specified index +// If there is an error getting the value, return the error status +RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&width, {index_of_bbox, 2})); + +// Get the height value of the bounding box from the tensor at the specified index +// If there is an error getting the value, return the error status +RETURN_IF_NOT_OK(bbox_tensor->GetItemAt(&height, {index_of_bbox, 3})); + +// Create a new BoundingBox object using the x, y, width, and height values +// and assign it to the pointer to shared pointer bbox_out +*bbox_out = std::make_shared(x, y, width, height); + +// Return a success status +return Status::OK(); + +// ValidateBoundingBoxes function to validate the input image and bounding box data Status BoundingBox::ValidateBoundingBoxes(const TensorRow &image_and_bbox) { + + // Check if the size of the input data is not equal to 2 (image and bounding box) if (image_and_bbox.size() != 2) { + // Return an error status with the appropriate error message return Status(StatusCode::kMDBoundingBoxInvalidShape, __LINE__, __FILE__, "BoundingBox: invalid input, size of input data should be 2 (including image and bounding box), " "but got: " + std::to_string(image_and_bbox.size())); } + + // Check if the shape of the bounding box is less than 2 (should be at least 2-dimensional matrix) if (image_and_bbox[1]->shape().Size() < 2) { + // Return an error status with the appropriate error message return Status(StatusCode::kMDBoundingBoxInvalidShape, __LINE__, __FILE__, "BoundingBox: bounding boxes should have to be two-dimensional matrix at least, but got " + std::to_string(image_and_bbox[1]->shape().Size()) + " dimension."); } + + // Get the number of features in the bounding box int64_t num_of_features = image_and_bbox[1]->shape()[1]; + + // Check if the number of features is less than the required number of columns if (num_of_features < kNumOfCols) { + // Return an error status with the appropriate error message return Status( StatusCode::kMDBoundingBoxInvalidShape, __LINE__, __FILE__, "BoundingBox: bounding boxes should be have at least 4 features, but got: " + std::to_string(num_of_features)); } + + // Create a vector to store shared pointers to BoundingBox objects std::vector> bbox_list; + + // Call the GetListOfBoundingBoxes function to populate the bbox_list vector RETURN_IF_NOT_OK(GetListOfBoundingBoxes(image_and_bbox[1], &bbox_list)); + + // Get the height and width of the image int64_t img_h = image_and_bbox[0]->shape()[0]; int64_t img_w = image_and_bbox[0]->shape()[1]; + + // Iterate over each bounding box in the bbox_list vector for (auto &bbox : bbox_list) { + + // Check if the width of the bounding box is too large (x coordinate exceeds the maximum value of int64) CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - bbox->x()) > bbox->width(), "BoundingBox: bbox width is too large as coordinate x bigger than max num of int64."); + + // Check if the height of the bounding box is too large (y coordinate exceeds the maximum value of int64) CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - bbox->y()) > bbox->height(), "BoundingBox: bbox height is too large as coordinate y bigger than max num of int64."); + + // Check if the bounding box is out of bounds of the image if ((bbox->x() + bbox->width() > img_w) || (bbox->y() + bbox->height() > img_h)) { + // Return an error status with the appropriate error message return Status(StatusCode::kMDBoundingBoxOutOfBounds, __LINE__, __FILE__, "BoundingBox: bounding boxes is out of bounds of the image, as image width: " + std::to_string(img_w) + ", bbox width coordinate: " + std::to_string(bbox->x() + bbox->width()) + ", and image height: " + std::to_string(img_h) + ", bbox height coordinate: " + std::to_string(bbox->y() + bbox->height())); } + + // Check if the coordinates of the bounding box are negative if (static_cast(bbox->x()) < 0 || static_cast(bbox->y()) < 0) { + // Return an error status with the appropriate error message return Status(StatusCode::kMDBoundingBoxOutOfBounds, __LINE__, __FILE__, "BoundingBox: the coordinates of the bounding boxes has negative value, got: (" + std::to_string(bbox->x()) + "," + std::to_string(bbox->y()) + ")."); } } - return Status::OK(); } +// Return the OK status from the Status class +return Status::OK(); -Status BoundingBox::WriteToTensor(const TensorPtr &bbox_tensor, dsize_t index_of_bbox) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 0}, x_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 1}, y_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 2}, width_)); - RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 3}, height_)); - return Status::OK(); -} +// This function is a member function of the BoundingBox class and is used to write the bounding box coordinates to a given tensor. +// It takes in a reference to a TensorPtr object named bbox_tensor and an index_of_bbox of type dsize_t. +// The function returns a Status object indicating the success or failure of the operation. -Status BoundingBox::GetListOfBoundingBoxes(const TensorPtr &bbox_tensor, - std::vector> *bbox_out) { +// Check if the bbox_tensor is not null, if it is null, return an error message using CHECK_FAIL_RETURN_UNEXPECTED macro. +// The macro expands to a check statement that throws an exception with the provided error message if the condition is false. +CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); + +// Set the x-coordinate of the bounding box at the given index_of_bbox in the bbox_tensor using the SetItemAt function of the TensorPtr object. +// The SetItemAt function takes in a template argument specifying the data type (bbox_float in this case) and a coordinate pair ({index_of_bbox, 0}) indicating the position in the tensor. +// The function returns a Status object indicating the success or failure of the operation. +RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 0}, x_)); + +// Set the y-coordinate of the bounding box at the given index_of_bbox in the bbox_tensor using the SetItemAt function. +RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 1}, y_)); + +// Set the width of the bounding box at the given index_of_bbox in the bbox_tensor using the SetItemAt function. +RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 2}, width_)); + +// Set the height of the bounding box at the given index_of_bbox in the bbox_tensor using the SetItemAt function. +RETURN_IF_NOT_OK(bbox_tensor->SetItemAt({index_of_bbox, 3}, height_)); + +// Return a Status object indicating successful program termination. +return Status::OK(); + +// This function is a member function of the BoundingBox class. +// It takes a TensorPtr (a smart pointer to a Tensor object) and a pointer to a vector of shared pointers to BoundingBox objects as input. +// It returns a Status object indicating the success or failure of the operation. + +Status BoundingBox::GetListOfBoundingBoxes(const TensorPtr &bbox_tensor, std::vector> *bbox_out) { + + // Check if the bbox_tensor is not null, and return an error message if it is. CHECK_FAIL_RETURN_UNEXPECTED(bbox_tensor != nullptr, "BoundingBox: bbox_tensor is null."); + + // Get the number of boxes from the shape of the bbox_tensor. dsize_t num_of_boxes = bbox_tensor->shape()[0]; + + // Iterate over each box. for (dsize_t i = 0; i < num_of_boxes; i++) { + + // Create a shared pointer to a BoundingBox object. std::shared_ptr bbox; + + // Read the i-th box from the bbox_tensor and store it in the bbox shared pointer. RETURN_IF_NOT_OK(ReadFromTensor(bbox_tensor, i, &bbox)); + + // Add the bbox shared pointer to the bbox_out vector. bbox_out->push_back(bbox); } + + // Return a Status object indicating success. return Status::OK(); } -Status BoundingBox::CreateTensorFromBoundingBoxList(const std::vector> &bboxes, - TensorPtr *tensor_out) { +// This function takes a vector of shared pointers to BoundingBox objects and creates a tensor from the bounding box data. +// The resulting tensor is stored in the tensor_out pointer. + +// Include the necessary headers for the types and functions used in this function +#include +#include +#include "BoundingBox.h" +#include "Tensor.h" +#include "Status.h" + +// Define the function +Status BoundingBox::CreateTensorFromBoundingBoxList(const std::vector>& bboxes, TensorPtr* tensor_out) { + + // Get the number of bounding boxes in the input vector dsize_t num_of_boxes = bboxes.size(); + + // Create a vector to store the bounding box data for the tensor std::vector bboxes_for_tensor; + + // Iterate over each bounding box in the input vector for (dsize_t i = 0; i < num_of_boxes; i++) { + + // Create an array to store the bounding box data bbox_float b_data[kNumOfCols] = {bboxes[i]->x(), bboxes[i]->y(), bboxes[i]->width(), bboxes[i]->height()}; + + // Insert the bounding box data into the vector bboxes_for_tensor.insert(bboxes_for_tensor.end(), b_data, b_data + kNumOfCols); } + + // Create a tensor from the bounding box data using the Tensor::CreateFromVector function RETURN_IF_NOT_OK(Tensor::CreateFromVector(bboxes_for_tensor, TensorShape{num_of_boxes, kNumOfCols}, tensor_out)); + + // Return a status indicating successful execution of the function return Status::OK(); } +// Function to pad bounding boxes with given top and left padding values Status BoundingBox::PadBBoxes(const TensorPtr *bbox_list, size_t bbox_count, int32_t pad_top, int32_t pad_left) { + + // Check if the bbox_list pointer is not null, return an error message if it is CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); + + // Loop through each bounding box in the bbox_list for (dsize_t i = 0; i < bbox_count; i++) { + + // Create a shared pointer to store the current bounding box std::shared_ptr bbox; + + // Read the bounding box from the tensor at index i and store it in the bbox pointer RETURN_IF_NOT_OK(ReadFromTensor(*bbox_list, i, &bbox)); + + // Check if the sum of the current bounding box's x-coordinate and pad_left exceeds the maximum value of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - bbox->x()) > pad_left, "BoundingBox: pad_left is too large as coordinate x bigger than max num of int64."); + + // Add the pad_left value to the x-coordinate of the bounding box bbox->SetX(bbox->x() + pad_left); + + // Check if the sum of the current bounding box's y-coordinate and pad_top exceeds the maximum value of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - bbox->y()) > pad_top, "BoundingBox: pad_top is too large as coordinate y bigger than max num of int64."); + + // Add the pad_top value to the y-coordinate of the bounding box bbox->SetY(bbox->y() + pad_top); + + // Write the updated bounding box back to the tensor at index i RETURN_IF_NOT_OK(bbox->WriteToTensor(*bbox_list, i)); } + + // Return OK status to indicate successful execution of the function return Status::OK(); } -Status BoundingBox::UpdateBBoxesForCrop(TensorPtr *bbox_list, size_t *bbox_count, int32_t CB_Xmin, int32_t CB_Ymin, - int32_t CB_Xmax, int32_t CB_Ymax) { - CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); - // PASS LIST, COUNT OF BOUNDING BOXES - // Also PAss X/Y Min/Max of image cropped region - normally obtained from 'GetCropBox' functions - std::vector correct_ind; - std::vector copyVals; - dsize_t bboxDim = (*bbox_list)->shape()[1]; - for (dsize_t i = 0; i < *bbox_count; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(ReadFromTensor(*bbox_list, i, &bbox)); - bbox_float bb_Xmax = bbox->x() + bbox->width(); - bbox_float bb_Ymax = bbox->y() + bbox->height(); - // check for image / BB overlap - if (((bbox->x() > CB_Xmax) || (bbox->y() > CB_Ymax)) || ((bb_Xmax < CB_Xmin) || (bb_Ymax < CB_Ymin))) { - continue; // no overlap found - } - // Update this bbox and select it to move to the final output tensor - correct_ind.push_back(i); - // adjust BBox corners by bringing into new CropBox if beyond - // Also resetting/adjusting for boxes to lie within CropBox instead of Image - subtract CropBox Xmin/YMin +// Check if the pointer to the bbox_list is not null, otherwise return an error message +CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); - bbox_float bb_Xmin = bbox->x() - std::min(static_cast(0.0), (bbox->x() - CB_Xmin)) - CB_Xmin; - bbox_float bb_Ymin = bbox->y() - std::min(static_cast(0.0), (bbox->y() - CB_Ymin)) - CB_Ymin; - bb_Xmax = bb_Xmax - std::max(static_cast(0.0), (bb_Xmax - CB_Xmax)) - CB_Xmin; - bb_Ymax = bb_Ymax - std::max(static_cast(0.0), (bb_Ymax - CB_Ymax)) - CB_Ymin; +// Create vectors to store the correct indices and copied values +std::vector correct_ind; +std::vector copyVals; - // bound check for float values - bb_Xmin = std::max(bb_Xmin, static_cast(0)); - bb_Ymin = std::max(bb_Ymin, static_cast(0)); - bb_Xmax = std::min(bb_Xmax, static_cast(CB_Xmax - CB_Xmin)); // find max value relative to new image - bb_Ymax = std::min(bb_Ymax, static_cast(CB_Ymax - CB_Ymin)); +// Get the dimension of the bounding box from the shape of the bbox_list tensor +dsize_t bboxDim = (*bbox_list)->shape()[1]; - // reset min values and calculate width/height from Box corners - bbox->SetX(bb_Xmin); - bbox->SetY(bb_Ymin); - bbox->SetWidth(bb_Xmax - bb_Xmin); - bbox->SetHeight(bb_Ymax - bb_Ymin); - RETURN_IF_NOT_OK(bbox->WriteToTensor(*bbox_list, i)); +// Iterate through each bounding box +for (dsize_t i = 0; i < *bbox_count; i++) { + // Create a shared pointer to store the current bounding box + std::shared_ptr bbox; + + // Read the bounding box from the tensor at index i and store it in the bbox pointer + RETURN_IF_NOT_OK(ReadFromTensor(*bbox_list, i, &bbox)); + + // Calculate the maximum X and Y coordinates of the bounding box + bbox_float bb_Xmax = bbox->x() + bbox->width(); + bbox_float bb_Ymax = bbox->y() + bbox->height(); + + // Check if there is an overlap between the image and the bounding box + if (((bbox->x() > CB_Xmax) || (bbox->y() > CB_Ymax)) || ((bb_Xmax < CB_Xmin) || (bb_Ymax < CB_Ymin))) { + continue; // no overlap found, skip to the next bounding box } - // create new tensor and copy over bboxes still valid to the image - // bboxes outside of new cropped region are ignored - empty tensor returned in case of none - *bbox_count = correct_ind.size(); - bbox_float temp = 0.0; - for (auto slice : correct_ind) { // for every index in the loop + + // If there is an overlap, add the index of the bounding box to the correct_ind vector + correct_ind.push_back(i); + + // Adjust the corners of the bounding box to fit within the new crop box + // Also adjust the coordinates to be relative to the crop box instead of the image + // Subtract the crop box's Xmin and Ymin from the bounding box's coordinates + // and store the adjusted bounding box in the copyVals vector +} + +// Calculate the minimum value for the X-coordinate of the bounding box +bbox_float bb_Xmin = bbox->x() - std::min(static_cast(0.0), (bbox->x() - CB_Xmin)) - CB_Xmin; + +// Calculate the minimum value for the Y-coordinate of the bounding box +bbox_float bb_Ymin = bbox->y() - std::min(static_cast(0.0), (bbox->y() - CB_Ymin)) - CB_Ymin; + +// Calculate the maximum value for the X-coordinate of the bounding box +bb_Xmax = bb_Xmax - std::max(static_cast(0.0), (bb_Xmax - CB_Xmax)) - CB_Xmin; + +// Calculate the maximum value for the Y-coordinate of the bounding box +bb_Ymax = bb_Ymax - std::max(static_cast(0.0), (bb_Ymax - CB_Ymax)) - CB_Ymin; + +// Perform a bound check for the float values + +// Ensure that bb_Xmin is not less than 0, if it is, set it to 0 +bb_Xmin = std::max(bb_Xmin, static_cast(0)); + +// Ensure that bb_Ymin is not less than 0, if it is, set it to 0 +bb_Ymin = std::max(bb_Ymin, static_cast(0)); + +// Ensure that bb_Xmax is not greater than the difference between CB_Xmax and CB_Xmin, if it is, set it to that difference +bb_Xmax = std::min(bb_Xmax, static_cast(CB_Xmax - CB_Xmin)); + +// Ensure that bb_Ymax is not greater than the difference between CB_Ymax and CB_Ymin, if it is, set it to that difference +bb_Ymax = std::min(bb_Ymax, static_cast(CB_Ymax - CB_Ymin)); + +// Reset the minimum values and calculate the width and height of the bounding box from its corners +bbox->SetX(bb_Xmin); +bbox->SetY(bb_Ymin); +bbox->SetWidth(bb_Xmax - bb_Xmin); +bbox->SetHeight(bb_Ymax - bb_Ymin); + +// Write the bounding box to the tensor and return an error if unsuccessful +RETURN_IF_NOT_OK(bbox->WriteToTensor(*bbox_list, i)); +} + +// Create a new tensor and copy over the bounding boxes that are still valid within the image +// Bounding boxes outside of the new cropped region are ignored, and an empty tensor is returned if there are none +*bbox_count = correct_ind.size(); +bbox_float temp = 0.0; +for (auto slice : correct_ind) { // for every index in the loop for (dsize_t ix = 0; ix < bboxDim; ix++) { - RETURN_IF_NOT_OK((*bbox_list)->GetItemAt(&temp, {slice, ix})); - copyVals.push_back(temp); + RETURN_IF_NOT_OK((*bbox_list)->GetItemAt(&temp, {slice, ix})); + copyVals.push_back(temp); } - } - std::shared_ptr retV; - RETURN_IF_NOT_OK( - Tensor::CreateFromVector(copyVals, TensorShape({static_cast(*bbox_count), bboxDim}), &retV)); - (*bbox_list) = retV; // reset pointer - return Status::OK(); } +// Create a new tensor from the copied values and return an error if unsuccessful +std::shared_ptr retV; +RETURN_IF_NOT_OK( + Tensor::CreateFromVector(copyVals, TensorShape({static_cast(*bbox_count), bboxDim}), &retV)); + +// Reset the pointer to the new tensor +(*bbox_list) = retV; + +// Return a status indicating success +return Status::OK(); + +// Update the bounding boxes for resize based on the given parameters Status BoundingBox::UpdateBBoxesForResize(const TensorPtr &bbox_list, size_t bbox_count, int32_t target_width, int32_t target_height, int32_t orig_width, int32_t orig_height) { + + // Check if the bbox_list pointer is null, and return an error message if it is CHECK_FAIL_RETURN_UNEXPECTED(bbox_list != nullptr, "BoundingBox: bbox_list ptr is null."); + + // Check if the orig_width is zero, and return an error message if it is CHECK_FAIL_RETURN_UNEXPECTED(orig_width != 0, "BoundingBox: orig_width is zero."); + + // Check if the orig_height is zero, and return an error message if it is CHECK_FAIL_RETURN_UNEXPECTED(orig_height != 0, "BoundingBox: orig_height is zero."); +} - // cast to float to preserve fractional + // Cast the target width and original width to float to preserve the fractional part bbox_float W_aspRatio = (target_width * 1.0) / (orig_width * 1.0); + + // Cast the target height and original height to float to preserve the fractional part bbox_float H_aspRatio = (target_height * 1.0) / (orig_height * 1.0); + + // Iterate over each bounding box for (dsize_t i = 0; i < bbox_count; i++) { - // for each bounding box + + // Create a shared pointer to a BoundingBox object std::shared_ptr bbox; + + // Read the bounding box from the bbox_list tensor at index i and assign it to the bbox pointer RETURN_IF_NOT_OK(ReadFromTensor(bbox_list, i, &bbox)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->x()) > W_aspRatio, - "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->y()) > H_aspRatio, - "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->width()) > W_aspRatio, - "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->height()) > H_aspRatio, - "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); +// Check if the width aspect ratio of the bounding box is too large +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->x()) > W_aspRatio, + "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); - // update positions and widths +// Check if the height aspect ratio of the bounding box is too large +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->y()) > H_aspRatio, + "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); + +// Check if the width aspect ratio of the bounding box is too large +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->width()) > W_aspRatio, + "BoundingBox: Width aspect Ratio is too large as got: " + std::to_string(W_aspRatio)); + +// Check if the height aspect ratio of the bounding box is too large +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / bbox->height()) > H_aspRatio, + "BoundingBox: Height aspect Ratio is too large as got: " + std::to_string(H_aspRatio)); + + // Update the x and y positions of the bounding box by multiplying them with the width and height aspect ratios respectively bbox->SetX(bbox->x() * W_aspRatio); bbox->SetY(bbox->y() * H_aspRatio); + + // Update the width and height of the bounding box by multiplying them with the width and height aspect ratios respectively bbox->SetWidth(bbox->width() * W_aspRatio); bbox->SetHeight(bbox->height() * H_aspRatio); - // reset bounding box values + + // Reset the bounding box values by writing them to the tensor RETURN_IF_NOT_OK(bbox->WriteToTensor(bbox_list, i)); + } + + // Return a status indicating successful program execution return Status::OK(); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box_augment_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box_augment_op.cc index b064276cf78..5428d8f00fe 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box_augment_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/bounding_box_augment_op.cc @@ -14,63 +14,114 @@ * limitations under the License. */ +// Include the header file "minddata/dataset/kernels/image/bounding_box_augment_op.h" #include "minddata/dataset/kernels/image/bounding_box_augment_op.h" +// Include the utility header, which provides various utility functions and classes #include +// Include the header file for the cv_tensor module from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the bounding_box module from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/bounding_box.h" + +// Include the header file for the image_utils module from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the resize_op module from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/resize_op.h" +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const float BoundingBoxAugmentOp::kDefRatio = 0.3; + // Define the nested namespace "dataset" + namespace dataset { + + // Define the constant float variable "kDefRatio" with a value of 0.3 + const float BoundingBoxAugmentOp::kDefRatio = 0.3; + } +} + +// Define the constructor for the BoundingBoxAugmentOp class BoundingBoxAugmentOp::BoundingBoxAugmentOp(std::shared_ptr transform, float ratio) : ratio_(ratio), uniform_(0, 1), transform_(std::move(transform)) { + + // Seed the random number generator with a random seed obtained from GetSeed() function rnd_.seed(GetSeed()); } +// Compute function for the BoundingBoxAugmentOp class Status BoundingBoxAugmentOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Validate the bounding boxes in the input RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + + // Get the number of boxes from the shape of the second input tensor uint32_t num_of_boxes = input[1]->shape()[0]; + + // Declare shared pointers for the crop and result tensors std::shared_ptr crop_out; std::shared_ptr res_out; + + // Convert the first input tensor to a CVTensor std::shared_ptr input_restore = CVTensor::AsCVTensor(input[0]); + + // Iterate over each box for (uint32_t i = 0; i < num_of_boxes; i++) { - // using a uniform distribution to ensure op happens with probability ratio_ + + // Use a uniform distribution to determine if the operation should be applied based on the probability ratio_ if (uniform_(rnd_) < ratio_) { + + // Read the bounding box from the second input tensor std::shared_ptr bbox; RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); + + // Crop the input tensor using the bounding box coordinates RETURN_IF_NOT_OK(Crop(input_restore, &crop_out, static_cast(bbox->x()), static_cast(bbox->y()), static_cast(bbox->width()), static_cast(bbox->height()))); - // transform the cropped bbox region + + // Create tensor rows for the crop output and result output TensorRow crop_out_row; TensorRow res_out_row; crop_out_row.push_back(crop_out); res_out_row.push_back(res_out); + + // Apply the transformation operation to the crop output RETURN_IF_NOT_OK(transform_->Compute(crop_out_row, &res_out_row)); - // place the transformed region back in the restored input + + // Get the transformed crop image as a CVTensor std::shared_ptr res_img = CVTensor::AsCVTensor(res_out_row[0]); - // check if transformed crop is out of bounds of the box + + // Check if the transformed crop is out of bounds of the original bounding box if (res_img->mat().cols > bbox->width() || res_img->mat().rows > bbox->height() || res_img->mat().cols < bbox->width() || res_img->mat().rows < bbox->height()) { - // if so, resize to fit in the box + + // If so, resize the transformed crop to fit within the bounding box std::shared_ptr resize_op = std::make_shared(static_cast(bbox->height()), static_cast(bbox->width())); RETURN_IF_NOT_OK(resize_op->Compute(std::static_pointer_cast(res_img), &res_out_row[0])); res_img = CVTensor::AsCVTensor(res_out_row[0]); } + + // Copy the transformed crop region back into the original input tensor res_img->mat().copyTo( input_restore->mat()(cv::Rect(bbox->x(), bbox->y(), res_img->mat().cols, res_img->mat().rows))); } } + + // Add the input_restore tensor and the second input tensor to the output tensor row (*output).push_back(std::move(std::static_pointer_cast(input_restore))); (*output).push_back(input[1]); + + // Return OK status to indicate successful computation return Status::OK(); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/center_crop_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/center_crop_op.cc index 3dc5d988b37..a3c73811751 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/center_crop_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/center_crop_op.cc @@ -17,72 +17,151 @@ #include #include "utils/ms_utils.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID -#include "minddata/dataset/kernels/image/image_utils.h" + + // If ENABLE_ANDROID is not defined, include the "minddata/dataset/kernels/image/image_utils.h" header file + #include "minddata/dataset/kernels/image/image_utils.h" + #else -#include "minddata/dataset/kernels/image/lite_image_utils.h" + + // If ENABLE_ANDROID is defined, include the "minddata/dataset/kernels/image/lite_image_utils.h" header file + #include "minddata/dataset/kernels/image/lite_image_utils.h" + #endif +// Include the "status.h" header file from the "minddata/dataset/util" directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Definition of the static member variable "kDefWidth" of the "CenterCropOp" class const int32_t CenterCropOp::kDefWidth = 0; -Status CenterCropOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - std::string err_msg; - std::string err_head = "CenterCrop: "; - dsize_t rank = input->shape().Rank(); - err_msg += - (rank < 2 || rank > 3) ? "image shape is not or , but got rank: " + std::to_string(rank) + "\t" : ""; - err_msg += (crop_het_ <= 0 || crop_wid_ <= 0) +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Initialize error message string +std::string err_msg; + +// Set the error message header +std::string err_head = "CenterCrop: "; + +// Get the rank (number of dimensions) of the input tensor +dsize_t rank = input->shape().Rank(); + +// Check if the rank is not 2 or 3, and append an error message if true +err_msg += (rank < 2 || rank > 3) ? "image shape is not or , but got rank: " + std::to_string(rank) + "\t" : ""; + +// Check if the crop height or width is less than or equal to 0, and append an error message if true +err_msg += (crop_het_ <= 0 || crop_wid_ <= 0) ? "crop size needs to be positive integers, but got crop height:" + std::to_string(crop_het_) + ", crop width: " + std::to_string(crop_wid_) + "\t" : ""; - CHECK_FAIL_RETURN_SYNTAX_ERROR(err_msg.length() == 0, err_head + err_msg); +// Check if the length of the error message is equal to 0 +// If it is not equal to 0, it means there is an error +CHECK_FAIL_RETURN_SYNTAX_ERROR(err_msg.length() == 0, err_head + err_msg); - int32_t top = crop_het_ - input->shape()[0]; // number of pixels to pad (top and bottom) - int32_t left = crop_wid_ - input->shape()[1]; - std::shared_ptr pad_image; +// Calculate the number of pixels to pad at the top and bottom of the image +int32_t top = crop_het_ - input->shape()[0]; - constexpr int64_t pad_limit = 3; - CHECK_FAIL_RETURN_UNEXPECTED((top < input->shape()[0] * pad_limit && left < input->shape()[1] * pad_limit), - "CenterCrop: CenterCropOp padding size is more than 3 times the original size, got pad" - " top: " + - std::to_string(top) + "pad left: " + std::to_string(left) + ", and original size: " + - std::to_string(input->shape()[0]) + ", " + std::to_string(input->shape()[1])); +// Calculate the number of pixels to pad at the left and right of the image +int32_t left = crop_wid_ - input->shape()[1]; - if (top > 0 && left > 0) { // padding only +// Create a shared pointer to a Tensor object named pad_image + +// Define a constant integer variable named "pad_limit" with a value of 3 +constexpr int64_t pad_limit = 3; + +// Check if the condition (top < input->shape()[0] * pad_limit && left < input->shape()[1] * pad_limit) is true +// If the condition is false, execute the following block of code +CHECK_FAIL_RETURN_UNEXPECTED( + (top < input->shape()[0] * pad_limit && left < input->shape()[1] * pad_limit), + "CenterCrop: CenterCropOp padding size is more than 3 times the original size, got pad top: " + + std::to_string(top) + "pad left: " + std::to_string(left) + ", and original size: " + + std::to_string(input->shape()[0]) + ", " + std::to_string(input->shape()[1])); + +// The above code checks if the padding size (top and left) is more than 3 times the original size of the input. +// If the condition is true, it throws an error message with the values of top, left, and the original size of the input. + +// Check if both top and left padding are greater than 0 +if (top > 0 && left > 0) { + // If true, apply padding to the input image using the Pad function + // The padding values are calculated based on the top and left values + // The BorderType is set to kConstant, indicating that the padding should be filled with a constant value return Pad(input, output, top / 2 + top % 2, top / 2, left / 2 + left % 2, left / 2, BorderType::kConstant); - } else if (top > 0) { +} +// Check if only top padding is greater than 0 +else if (top > 0) { + // If true, apply padding to the input image using the Pad function + // The padding values for the top are calculated based on the top value + // The BorderType is set to kConstant, indicating that the padding should be filled with a constant value RETURN_IF_NOT_OK(Pad(input, &pad_image, top / 2 + top % 2, top / 2, 0, 0, BorderType::kConstant)); + // Crop the padded image using the Crop function + // The crop values are calculated based on the crop_wid_ and crop_het_ values return Crop(pad_image, output, (static_cast(pad_image->shape()[1]) - crop_wid_) / 2, (static_cast(pad_image->shape()[0]) - crop_het_) / 2, crop_wid_, crop_het_); - } else if (left > 0) { +} +// Check if only left padding is greater than 0 +else if (left > 0) { + // If true, apply padding to the input image using the Pad function + // The padding values for the left are calculated based on the left value + // The BorderType is set to kConstant, indicating that the padding should be filled with a constant value RETURN_IF_NOT_OK(Pad(input, &pad_image, 0, 0, left / 2 + left % 2, left / 2, BorderType::kConstant)); + // Crop the padded image using the Crop function + // The crop values are calculated based on the crop_wid_ and crop_het_ values return Crop(pad_image, output, (static_cast(pad_image->shape()[1]) - crop_wid_) / 2, (static_cast(pad_image->shape()[0]) - crop_het_) / 2, crop_wid_, crop_het_); - } - return Crop(input, output, (input->shape()[1] - crop_wid_) / 2, (input->shape()[0] - crop_het_) / 2, crop_wid_, - crop_het_); } +// If none of the above conditions are met, perform cropping on the input image using the Crop function +// The crop values are calculated based on the crop_wid_ and crop_het_ values +return Crop(input, output, (input->shape()[1] - crop_wid_) / 2, (input->shape()[0] - crop_het_) / 2, crop_wid_, + crop_het_); +// Implementation of the Print function for the CenterCropOp class void CenterCropOp::Print(std::ostream &out) const { + // Print the name of the operation and the values of cropWidth and cropHeight out << "CenterCropOp: " << "cropWidth: " << crop_wid_ << "cropHeight: " << crop_het_ << "\n"; } + +// Implementation of the OutputShape function for the CenterCropOp class Status CenterCropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Create a new TensorShape object with dimensions crop_het_ and crop_wid_ TensorShape out = TensorShape{crop_het_, crop_wid_}; - 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(); + + // Check the rank of the input tensor + if (inputs[0].Rank() == 2) + // If the rank is 2, append the new TensorShape object to the outputs vector + outputs.emplace_back(out); + if (inputs[0].Rank() == 3) + // If the rank is 3, append the new TensorShape object with an additional dimension to the outputs vector + outputs.emplace_back(out.AppendDim(inputs[0][2])); + + // Check if the outputs vector is not empty + if (!outputs.empty()) + // If not empty, return OK status + return Status::OK(); + + // If the outputs vector is empty, return an error status with a descriptive message return Status(StatusCode::kMDUnexpectedError, "CenterCrop: invalid input shape, expected 2D or 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } + +// End of the namespace dataset } // namespace dataset -} // namespace mindspore + +// End of the namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/convert_color_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/convert_color_op.cc index f61ff310e6b..d3c9f4a0b46 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/convert_color_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/convert_color_op.cc @@ -14,22 +14,57 @@ * limitations under the License. */ +// Include the string header for string manipulation #include + +// Include the utility header for utility functions #include + +// Include the cv_tensor header from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the image_utils header from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the convert_color_op header from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/convert_color_op.h" + +// Include the data_utils header from the minddata/dataset/kernels/data directory #include "minddata/dataset/kernels/data/data_utils.h" + +// Include the random header from the minddata/dataset/util directory #include "minddata/dataset/util/random.h" + +// Include the status header from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Definition of the ConvertColorOp constructor, which takes a convert_mode parameter ConvertColorOp::ConvertColorOp(ConvertMode convert_mode) : convert_mode_(convert_mode) {} +// End of the "dataset" namespace +} // namespace dataset + +// End of the "mindspore" namespace +} // namespace mindspore + +// The Compute function of the ConvertColorOp class Status ConvertColorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the ConvertColor function to convert the color of the input tensor and store the result in the output tensor return ConvertColor(input, output, convert_mode_); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/crop_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/crop_op.cc index aad1fefe772..462e7d117a0 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/crop_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/crop_op.cc @@ -15,44 +15,88 @@ */ #include "minddata/dataset/kernels/image/crop_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -Status CropOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageRank("Crop", input->shape().Size())); - int32_t input_h = static_cast(input->shape()[0]); - int32_t input_w = static_cast(input->shape()[1]); - CHECK_FAIL_RETURN_UNEXPECTED(y_ + height_ <= input_h, "Crop: Crop height dimension: " + std::to_string(y_ + height_) + - " exceeds image height: " + std::to_string(input_h)); - CHECK_FAIL_RETURN_UNEXPECTED(x_ + width_ <= input_w, "Crop: Crop width dimension: " + std::to_string(x_ + width_) + - " exceeds image width: " + std::to_string(input_w)); - return Crop(input, output, x_, y_, width_, height_); -} + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace +// The Compute function of the CropOp class, which performs the actual cropping operation on the input tensor + +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Validate the rank of the input tensor to ensure it is an image +RETURN_IF_NOT_OK(ValidateImageRank("Crop", input->shape().Size())); + +// Get the height and width of the input tensor +int32_t input_h = static_cast(input->shape()[0]); +int32_t input_w = static_cast(input->shape()[1]); + +// Check if the crop region specified by y_, height_, x_, and width_ is within the bounds of the input tensor +CHECK_FAIL_RETURN_UNEXPECTED(y_ + height_ <= input_h, "Crop: Crop height dimension: " + std::to_string(y_ + height_) + + " exceeds image height: " + std::to_string(input_h)); +CHECK_FAIL_RETURN_UNEXPECTED(x_ + width_ <= input_w, "Crop: Crop width dimension: " + std::to_string(x_ + width_) + + " exceeds image width: " + std::to_string(input_w)); + +// Perform the cropping operation on the input tensor and store the result in the output tensor +return Crop(input, output, x_, y_, width_, height_); + +// Function to determine the output shape of the CropOp operation Status CropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Create a TensorShape object with the specified height and width TensorShape out = TensorShape{height_, width_}; + + // Check if the input tensor has a rank of 2 if (inputs[0].Rank() == 2) { + // Append the out TensorShape object to the outputs vector (void)outputs.emplace_back(out); } + + // Check if the input tensor has a rank of 3 if (inputs[0].Rank() == 3) { + // Append the out TensorShape object with an additional dimension (third dimension) to the outputs vector (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); } + + // Check if the outputs vector is not empty if (!outputs.empty()) { + // Return OK status to indicate successful determination of output shape return Status::OK(); } + + // Return an error status with a message indicating the invalid input shape return Status(StatusCode::kMDUnexpectedError, "Crop: invalid input shape, expected 2D or 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/cut_out_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/cut_out_op.cc index 7f6cdd65206..66dc6b38655 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/cut_out_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/cut_out_op.cc @@ -1,54 +1,89 @@ /** * Copyright 2019 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 +// 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 +// This is a comment that provides a URL to the Apache License 2.0 +// The Apache License 2.0 is a permissive open-source license that allows users to freely use, modify, and distribute the licensed software +// More information about the Apache License 2.0 can be found at the provided URL: http://www.apache.org/licenses/LICENSE-2.0 +// This is a comment block that provides information about the license under which the software is distributed + +/* * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + +// Include the header file for the cut_out_op class from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/cut_out_op.h" +// Include the random header, which provides facilities for generating random numbers #include +// Include the header file for cv_tensor from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for image_utils from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for random from the minddata/dataset/util directory #include "minddata/dataset/util/random.h" + +// Include the header file for status from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { +// The code is defining constants for the CutOutOp class in the mindspore::dataset namespace + +// Define a constant boolean variable kDefRandomColor with a default value of false const bool CutOutOp::kDefRandomColor = false; + +// Define a constant unsigned 8-bit integer variable kDefFillR with a default value of 0 const uint8_t CutOutOp::kDefFillR = 0; + +// Define a constant unsigned 8-bit integer variable kDefFillG with a default value of 0 const uint8_t CutOutOp::kDefFillG = 0; + +// Define a constant unsigned 8-bit integer variable kDefFillB with a default value of 0 const uint8_t CutOutOp::kDefFillB = 0; -// constructor +// Constructor for the CutOutOp class CutOutOp::CutOutOp(int32_t box_height, int32_t box_width, int32_t num_patches, bool random_color, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : rnd_(GetSeed()), - box_height_(box_height), - box_width_(box_width), - num_patches_(num_patches), - random_color_(random_color), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} + : rnd_(GetSeed()), // Initialize the random number generator with a seed obtained from GetSeed() function + box_height_(box_height), // Initialize the box_height_ member variable with the provided box_height parameter + box_width_(box_width), // Initialize the box_width_ member variable with the provided box_width parameter + num_patches_(num_patches), // Initialize the num_patches_ member variable with the provided num_patches parameter + random_color_(random_color), // Initialize the random_color_ member variable with the provided random_color parameter + fill_r_(fill_r), // Initialize the fill_r_ member variable with the provided fill_r parameter + fill_g_(fill_g), // Initialize the fill_g_ member variable with the provided fill_g parameter + fill_b_(fill_b) {} // Initialize the fill_b_ member variable with the provided fill_b parameter -// main function call for cut out -Status CutOutOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - std::shared_ptr inputCV = CVTensor::AsCVTensor(input); - // cut out will clip the erasing area if the box is near the edge of the image and the boxes are black - RETURN_IF_NOT_OK(Erase(inputCV, output, box_height_, box_width_, num_patches_, false, random_color_, &rnd_, fill_r_, - fill_g_, fill_b_)); - return Status::OK(); -} +// The Compute function of the CutOutOp class is called to perform the cut out operation on the input tensor. +// It takes in a shared pointer to the input tensor and a pointer to the output tensor. + +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Convert the input tensor to a CVTensor (OpenCV tensor) for easier manipulation +std::shared_ptr inputCV = CVTensor::AsCVTensor(input); + +// Call the Erase function to perform the cut out operation on the input tensor +// The function will clip the erasing area if the box is near the edge of the image and the boxes are black +// The function also takes in other parameters such as box height, box width, number of patches, random color, etc. +// The function returns a status indicating the success or failure of the operation +RETURN_IF_NOT_OK(Erase(inputCV, output, box_height_, box_width_, num_patches_, false, random_color_, &rnd_, fill_r_, + fill_g_, fill_b_)); + +// Return a status indicating successful program termination +return Status::OK(); + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/cutmix_batch_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/cutmix_batch_op.cc index d408c6d82b2..22568cb0dde 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/cutmix_batch_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/cutmix_batch_op.cc @@ -14,72 +14,139 @@ * limitations under the License. */ +// Include the header file "cutmix_batch_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/cutmix_batch_op.h" +// Include the header for numeric limits (provides information about the limits of numeric types) #include + +// Include the header for string manipulation #include + +// Include the header for utility functions (provides various utility functions) #include +// Include the header file for cv_tensor from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for data_utils from the minddata/dataset/kernels/data directory #include "minddata/dataset/kernels/data/data_utils.h" + +// Include the header file for image_utils from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for random from the minddata/dataset/util directory #include "minddata/dataset/util/random.h" + +// Include the header file for status from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant variable `kMinLabelShapeSize` with a value of 2 constexpr size_t kMinLabelShapeSize = 2; + +// Define a constant variable `kMaxLabelShapeSize` with a value of 3 constexpr size_t kMaxLabelShapeSize = 3; + +// Define a constant variable `kExpectedImageShapeSize` with a value of 4 constexpr size_t kExpectedImageShapeSize = 4; + +// Define a constant variable `kDimensionOne` with a value of 1 constexpr size_t kDimensionOne = 1; + +// Define a constant variable `kDimensionTwo` with a value of 2 constexpr size_t kDimensionTwo = 2; + +// Define a constant variable `kDimensionThree` with a value of 3 constexpr size_t kDimensionThree = 3; + +// Define a constant variable `kValueOne` with a value of 1 constexpr int64_t kValueOne = 1; + +// Define a constant variable `kValueThree` with a value of 3 constexpr int64_t kValueThree = 3; +// Constructor for the CutMixBatchOp class CutMixBatchOp::CutMixBatchOp(ImageBatchFormat image_batch_format, float alpha, float prob) : image_batch_format_(image_batch_format), alpha_(alpha), prob_(prob) { + + // Initialize the random number generator with a seed obtained from GetSeed() rnd_.seed(GetSeed()); } +// This function calculates the crop box coordinates and dimensions for the CutMix augmentation technique + void CutMixBatchOp::GetCropBox(int height, int width, float lam, int *x, int *y, int *crop_width, int *crop_height) { + + // Calculate the ratio of the area to be cut from the original image const float cut_ratio = 1 - lam; + + // Calculate the width and height of the cut area int cut_w = static_cast(width * cut_ratio); int cut_h = static_cast(height * cut_ratio); + + // Create uniform distributions for generating random coordinates within the image std::uniform_int_distribution width_uniform_distribution(0, width); std::uniform_int_distribution height_uniform_distribution(0, height); + + // Generate random coordinates for the center of the crop box int cx = width_uniform_distribution(rnd_); int x2, y2; int cy = height_uniform_distribution(rnd_); + + // Calculate the coordinates of the top-left corner of the crop box constexpr int cut_half = 2; *x = std::clamp(cx - cut_w / cut_half, 0, width - 1); // horizontal coordinate of left side of crop box *y = std::clamp(cy - cut_h / cut_half, 0, height - 1); // vertical coordinate of the top side of crop box + + // Calculate the coordinates of the bottom-right corner of the crop box x2 = std::clamp(cx + cut_w / cut_half, 0, width - 1); // horizontal coordinate of right side of crop box y2 = std::clamp(cy + cut_h / cut_half, 0, height - 1); // vertical coordinate of the bottom side of crop box + + // Calculate the width and height of the crop box *crop_width = std::clamp(x2 - *x, 1, width - 1); *crop_height = std::clamp(y2 - *y, 1, height - 1); } -Status CutMixBatchOp::ValidateCutMixBatch(const TensorRow &input) { +// Validate the input by checking if the size of the input is less than the minimum required size if (input.size() < kMinLabelShapeSize) { + // If the input size is less than the minimum required size, return an error message RETURN_STATUS_UNEXPECTED( "CutMixBatch: invalid input, size of input should be 2 (including image and label), but got: " + std::to_string(input.size())); } + + // Get the shape of the image tensor from the input std::vector image_shape = input.at(0)->shape().AsVector(); + + // Get the shape of the label tensor from the input std::vector label_shape = input.at(1)->shape().AsVector(); - // Check inputs + // Check if the shape of the input image is valid if (image_shape.size() != kExpectedImageShapeSize || image_shape[0] != label_shape[0]) { RETURN_STATUS_UNEXPECTED( "CutMixBatch: please make sure images are or format, and batched before calling CutMixBatch."); } + + // Check if the type of the labels is valid if (!input.at(1)->type().IsInt()) { RETURN_STATUS_UNEXPECTED( "CutMixBatch: Wrong labels type. The second column (labels) must only include int types, but got:" + input.at(1)->type().ToString()); } + + // Check if the shape of the labels is valid if (label_shape.size() != kMinLabelShapeSize && label_shape.size() != kMaxLabelShapeSize) { RETURN_STATUS_UNEXPECTED( "CutMixBatch: wrong labels shape. " @@ -88,127 +155,220 @@ Status CutMixBatchOp::ValidateCutMixBatch(const TensorRow &input) { "labels must be in one-hot format and in a batch, but got rank: " + std::to_string(label_shape.size())); } + + // Create a string to store the shape information of the image std::string shape_info = "("; + + // Iterate over the dimensions of the image shape and append them to the shape_info string for (auto i : image_shape) { shape_info = shape_info + std::to_string(i) + ", "; } + + // Replace the last comma with a closing parenthesis shape_info.replace(shape_info.end() - 1, shape_info.end(), ")"); + + // Check if the image shape matches the expected format for NCHW if ((image_shape[kDimensionOne] != kValueOne && image_shape[kDimensionOne] != kValueThree) && image_batch_format_ == ImageBatchFormat::kNCHW) { RETURN_STATUS_UNEXPECTED("CutMixBatch: image doesn't match the format, got shape: " + shape_info); } + + // Check if the image shape matches the expected format for NHWC if ((image_shape[kDimensionThree] != kValueOne && image_shape[kDimensionThree] != kValueThree) && image_batch_format_ == ImageBatchFormat::kNHWC) { RETURN_STATUS_UNEXPECTED("CutMixBatch: image doesn't match the format, got shape: " + shape_info); } - return Status::OK(); -} +// Return the OK status from the Status class +return Status::OK(); -Status CutMixBatchOp::ComputeImage(const TensorRow &input, const int64_t rand_indx_i, const float lam, float *label_lam, - std::shared_ptr *image_i) { - std::vector image_shape = input.at(0)->shape().AsVector(); - int x, y, crop_width, crop_height; - // Get a random image - TensorShape remaining({-1}); - uchar *start_addr_of_index = nullptr; - std::shared_ptr rand_image; +// ComputeImage function implementation for CutMixBatchOp class - RETURN_IF_NOT_OK(input.at(0)->StartAddrOfIndex({rand_indx_i, 0, 0, 0}, &start_addr_of_index, &remaining)); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory( +// Retrieve the shape of the input image tensor +std::vector image_shape = input.at(0)->shape().AsVector(); + +// Declare variables for x, y, crop_width, and crop_height +int x, y, crop_width, crop_height; + +// Declare a TensorShape object for the remaining dimensions +TensorShape remaining({-1}); + +// Declare a pointer to store the starting address of the random index +uchar *start_addr_of_index = nullptr; + +// Declare a shared pointer to store the random image tensor +std::shared_ptr rand_image; + +// Call the function `StartAddrOfIndex` on the first element of the `input` vector, passing in the index {rand_indx_i, 0, 0, 0}. +// If the function returns an error code, immediately return that error code. +RETURN_IF_NOT_OK(input.at(0)->StartAddrOfIndex({rand_indx_i, 0, 0, 0}, &start_addr_of_index, &remaining)); + +// Create a new `Tensor` object called `rand_image` by calling the `CreateFromMemory` function. +// Pass in the following arguments: +// - A `TensorShape` object with dimensions {image_shape[kDimensionOne], image_shape[kDimensionTwo], image_shape[kDimensionThree]} +// - The data type of the first element of the `input` vector +// - The `start_addr_of_index` value obtained from the previous function call +// If the function returns an error code, immediately return that error code. +RETURN_IF_NOT_OK(Tensor::CreateFromMemory( TensorShape({image_shape[kDimensionOne], image_shape[kDimensionTwo], image_shape[kDimensionThree]}), input.at(0)->type(), start_addr_of_index, &rand_image)); - // Compute image - if (image_batch_format_ == ImageBatchFormat::kNHWC) { +// Compute image +if (image_batch_format_ == ImageBatchFormat::kNHWC) { // NHWC Format + + // Get the crop box coordinates and dimensions based on the image shape and lambda value GetCropBox(static_cast(image_shape[kDimensionOne]), static_cast(image_shape[kDimensionTwo]), lam, &x, &y, &crop_width, &crop_height); + + // Create a shared pointer to store the cropped image tensor std::shared_ptr cropped; + + // Crop the random image using the computed crop box coordinates and dimensions RETURN_IF_NOT_OK(Crop(rand_image, &cropped, x, y, crop_width, crop_height)); + + // Mask the cropped image with the original image at the specified crop box coordinates and dimensions RETURN_IF_NOT_OK(MaskWithTensor(cropped, image_i, x, y, crop_width, crop_height, ImageFormat::HWC)); + + // Calculate the label lambda value based on the cropped image dimensions and the original image dimensions *label_lam = kValueOne - (crop_width * crop_height / static_cast(image_shape[kDimensionOne] * image_shape[kDimensionTwo])); - } else { +} else { // NCHW Format + + // Get the crop box coordinates and dimensions based on the image shape and lambda value GetCropBox(static_cast(image_shape[kDimensionTwo]), static_cast(image_shape[kDimensionThree]), lam, &x, &y, &crop_width, &crop_height); + + // Create vectors to store the channels of the CHW image and the cropped channels std::vector> channels; // A vector holding channels of the CHW image std::vector> cropped_channels; // A vector holding the channels of the cropped CHW + + // Convert the batch tensor to a vector of individual tensors representing each channel RETURN_IF_NOT_OK(BatchTensorToTensorVector(rand_image, &channels)); + + // Iterate over each channel and crop it individually for (auto channel : channels) { - // Call crop for each single channel - std::shared_ptr cropped_channel; - RETURN_IF_NOT_OK(Crop(channel, &cropped_channel, x, y, crop_width, crop_height)); - cropped_channels.push_back(cropped_channel); + // Call crop for each single channel + std::shared_ptr cropped_channel; + RETURN_IF_NOT_OK(Crop(channel, &cropped_channel, x, y, crop_width, crop_height)); + cropped_channels.push_back(cropped_channel); } + + // Create a shared pointer to store the merged channels as a single tensor std::shared_ptr cropped; - // Merge channels to a single tensor + + // Merge the cropped channels into a single tensor RETURN_IF_NOT_OK(TensorVectorToBatchTensor(cropped_channels, &cropped)); + // Call the function MaskWithTensor with the arguments cropped, image_i, x, y, crop_width, crop_height, and ImageFormat::CHW. + // The function is expected to return an error code, and if it is not OK, the program will return from this point. RETURN_IF_NOT_OK(MaskWithTensor(cropped, image_i, x, y, crop_width, crop_height, ImageFormat::CHW)); - *label_lam = kValueOne - (crop_width * crop_height / - static_cast(image_shape[kDimensionTwo] * image_shape[kDimensionThree])); + + // Calculate the value of *label_lam using the formula kValueOne - (crop_width * crop_height / static_cast(image_shape[kDimensionTwo] * image_shape[kDimensionThree])). + // The result will be assigned to *label_lam. + *label_lam = kValueOne - (crop_width * crop_height / static_cast(image_shape[kDimensionTwo] * image_shape[kDimensionThree])); } - return Status::OK(); -} +// Return the OK status from the Status class +return Status::OK(); -Status CutMixBatchOp::ComputeLabel(const TensorRow &input, const int64_t rand_indx_i, const int64_t index_i, - const int64_t row_labels, const int64_t num_classes, - const std::size_t label_shape_size, const float label_lam, - std::shared_ptr *out_labels) { - // Compute labels - for (int64_t j = 0; j < row_labels; j++) { +// Compute labels for CutMix augmentation + +// Iterate over each row of labels +for (int64_t j = 0; j < row_labels; j++) { + // Iterate over each class for (int64_t k = 0; k < num_classes; k++) { - std::vector first_index = - label_shape_size == kMaxLabelShapeSize ? std::vector{index_i, j, k} : std::vector{index_i, k}; - std::vector second_index = - label_shape_size == kMaxLabelShapeSize ? std::vector{rand_indx_i, j, k} : std::vector{rand_indx_i, k}; - if (input.at(1)->type().IsSignedInt()) { - int64_t first_value, second_value; - RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); - RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); - RETURN_IF_NOT_OK( - (*out_labels)->SetItemAt(first_index, label_lam * first_value + (1 - label_lam) * second_value)); - } else { - uint64_t first_value, second_value; - RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); - RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); - RETURN_IF_NOT_OK( - (*out_labels)->SetItemAt(first_index, label_lam * first_value + (1 - label_lam) * second_value)); - } - } - } + // Create the first and second index vectors based on the label shape size + std::vector first_index = + label_shape_size == kMaxLabelShapeSize ? std::vector{index_i, j, k} : std::vector{index_i, k}; + std::vector second_index = + label_shape_size == kMaxLabelShapeSize ? std::vector{rand_indx_i, j, k} : std::vector{rand_indx_i, k}; - return Status::OK(); + // Check if the input tensor type is signed integer + if (input.at(1)->type().IsSignedInt()) { + int64_t first_value, second_value; + // Get the values at the first and second indices from the input tensor + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); + // Compute the label using label_lam and set it at the first index in the output tensor + RETURN_IF_NOT_OK( + (*out_labels)->SetItemAt(first_index, label_lam * first_value + (1 - label_lam) * second_value)); + } else { + uint64_t first_value, second_value; + // Get the values at the first and second indices from the input tensor + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); + // Compute the label using label_lam and set it at the first index in the output tensor + RETURN_IF_NOT_OK( + (*out_labels)->SetItemAt(first_index, label_lam * first_value + (1 - label_lam) * second_value)); + } + } } -Status CutMixBatchOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(ValidateCutMixBatch(input)); - std::vector image_shape = input.at(0)->shape().AsVector(); - std::vector label_shape = input.at(1)->shape().AsVector(); +// Return the OK status from the Status class +return Status::OK(); - // Move images into a vector of Tensors - std::vector> images; - RETURN_IF_NOT_OK(BatchTensorToTensorVector(input.at(0), &images)); +// Check if the input and output are valid vectors +IO_CHECK_VECTOR(input, output); - // Calculate random labels - std::vector rand_indx; - CHECK_FAIL_RETURN_UNEXPECTED( +// Validate the CutMixBatch operation on the input +RETURN_IF_NOT_OK(ValidateCutMixBatch(input)); + +// Get the shape of the image tensor from the input +std::vector image_shape = input.at(0)->shape().AsVector(); + +// Get the shape of the label tensor from the input +std::vector label_shape = input.at(1)->shape().AsVector(); + +// Create a vector to store shared pointers to Tensor objects +std::vector> images; + +// Convert the input batch tensor to a vector of tensors and store them in the 'images' vector +RETURN_IF_NOT_OK(BatchTensorToTensorVector(input.at(0), &images)); + +// Create an empty vector to store random indices +std::vector rand_indx; + +// Check if the size of "images" is within the range of int64_t +CHECK_FAIL_RETURN_UNEXPECTED( images.size() <= static_cast(std::numeric_limits::max()), "The size of \"images\" must not be more than \"INT64_MAX\", but got: " + std::to_string(images.size())); - for (int64_t idx = 0; idx < static_cast(images.size()); idx++) rand_indx.push_back(idx); - std::shuffle(rand_indx.begin(), rand_indx.end(), rnd_); - std::gamma_distribution gamma_distribution(alpha_, 1); - std::uniform_real_distribution uniform_distribution(0.0, 1.0); - // Tensor holding the output labels - std::shared_ptr out_labels; - RETURN_IF_NOT_OK(TypeCast(std::move(input.at(1)), &out_labels, DataType(DataType::DE_FLOAT32))); - int64_t row_labels = label_shape.size() == kValueThree ? label_shape[kDimensionOne] : kValueOne; - int64_t num_classes = label_shape.size() == kValueThree ? label_shape[kDimensionTwo] : label_shape[kDimensionOne]; +// Generate a sequence of indices from 0 to the size of "images" and add them to the vector +for (int64_t idx = 0; idx < static_cast(images.size()); idx++) { + rand_indx.push_back(idx); +} + +// Shuffle the vector of indices using the random number generator "rnd_" +std::shuffle(rand_indx.begin(), rand_indx.end(), rnd_); + +// Create a gamma distribution object with parameters alpha_ and 1 +std::gamma_distribution gamma_distribution(alpha_, 1); + +// Create a uniform real distribution object with range [0.0, 1.0] +std::uniform_real_distribution uniform_distribution(0.0, 1.0); + +// Create a shared pointer to a Tensor object called "out_labels" to hold the output labels + +std::shared_ptr out_labels; + +// Use the TypeCast function to convert the input tensor at index 1 to a float32 data type and assign it to "out_labels" +// The TypeCast function returns an error code, so we use the RETURN_IF_NOT_OK macro to check if the conversion was successful + +RETURN_IF_NOT_OK(TypeCast(std::move(input.at(1)), &out_labels, DataType(DataType::DE_FLOAT32))); + +// Determine the number of rows in the label shape +// If the label shape has a size of 3, assign the value at index 1 to "row_labels" +// Otherwise, assign 1 to "row_labels" + +int64_t row_labels = label_shape.size() == kValueThree ? label_shape[kDimensionOne] : kValueOne; + +// Determine the number of classes in the label shape +// If the label shape has a size of 3, assign the value at index 2 to "num_classes" +// Otherwise, assign the value at index 1 to "num_classes" + +int64_t num_classes = label_shape.size() == kValueThree ? label_shape[kDimensionTwo] : label_shape[kDimensionOne]; // Compute labels and images for (size_t i = 0; i < static_cast(image_shape[0]); i++) { @@ -217,34 +377,54 @@ Status CutMixBatchOp::Compute(const TensorRow &input, TensorRow *output) { // then x = x1 / (x1+x2) is a random variable from Beta(a1, a2) float x1 = gamma_distribution(rnd_); float x2 = gamma_distribution(rnd_); + + // Check if x1 and x2 are within the limits of float_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x1) > x2, "CutMixBatchOp: gamma_distribution x1 and x2 are too large, got x1: " + std::to_string(x1) + ", x2:" + std::to_string(x2)); + + // Calculate lambda float lam = x1 / (x1 + x2); + + // Generate a random number between 0 and 1 double random_number = uniform_distribution(rnd_); + + // Check if the random number is less than the probability threshold if (random_number < prob_) { float label_lam; // lambda used for labels + // Compute image RETURN_IF_NOT_OK(ComputeImage(input, rand_indx[i], lam, &label_lam, &images[i])); + // Compute labels RETURN_IF_NOT_OK(ComputeLabel(input, rand_indx[i], static_cast(i), row_labels, num_classes, label_shape.size(), label_lam, &out_labels)); } } - std::shared_ptr out_images; - RETURN_IF_NOT_OK(TensorVectorToBatchTensor(images, &out_images)); +// Declare a shared pointer named "out_images" of type "Tensor" +std::shared_ptr out_images; + +// Call the function "TensorVectorToBatchTensor" with the "images" vector as input and assign the result to "out_images" +// The function may return an error code, so we use the macro "RETURN_IF_NOT_OK" to check if the function call was successful +RETURN_IF_NOT_OK(TensorVectorToBatchTensor(images, &out_images)); // Move the output into a TensorRow - output->push_back(out_images); - output->push_back(out_labels); + output->push_back(out_images); // Push the 'out_images' tensor into the 'output' TensorRow + output->push_back(out_labels); // Push the 'out_labels' tensor into the 'output' TensorRow - return Status::OK(); -} +// Return the OK status from the Status class +return Status::OK(); + +// Definition of the Print function for the CutMixBatchOp class void CutMixBatchOp::Print(std::ostream &out) const { - out << "CutMixBatchOp: " - << "\n"; + // Output the class name and a newline character + out << "CutMixBatchOp: " << "\n"; } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/decode_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/decode_op.cc index 8d2fe90ec71..c0dda892b95 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/decode_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/decode_op.cc @@ -15,55 +15,114 @@ */ #include "minddata/dataset/kernels/image/decode_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace, which is a sub-namespace of "mindspore" namespace dataset { + +// Define a constant boolean variable "kDefRgbFormat" with the value true const bool DecodeOp::kDefRgbFormat = true; +// Constructor for the DecodeOp class, takes a boolean parameter 'rgb' to determine the color mode DecodeOp::DecodeOp(bool rgb) : is_rgb_format_(rgb) { - if (is_rgb_format_) { // RGB colour mode + + // Check if the color mode is RGB + if (is_rgb_format_) { + + // If it is RGB, log a debug message indicating the color mode is RGB MS_LOG(DEBUG) << "Decode colour mode is RGB."; + } else { + + // If it is not RGB, log a debug message indicating the color mode is BGR MS_LOG(DEBUG) << "Decode colour mode is BGR."; } } +// The Compute function of the DecodeOp class, responsible for performing the decoding operation Status DecodeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); - // check the input tensor shape + + // Check the shape of the input tensor if (input->Rank() != 1) { + // If the input tensor is not 1D, return an error message RETURN_STATUS_UNEXPECTED("Decode: invalid input shape, only support 1D input, got rank: " + std::to_string(input->Rank())); } - if (is_rgb_format_) { // RGB colour mode + + // Check if the color mode is RGB + if (is_rgb_format_) { + // If the color mode is RGB, call the Decode function return Decode(input, output); - } else { // BGR colour mode + } else { + // If the color mode is not RGB, return an error message RETURN_STATUS_UNEXPECTED( "Decode: only support Decoded into RGB image, check input parameter 'rgb' first, its value should be 'True'."); } } +// This function is used to determine the output shape of the DecodeOp operation. +// It takes in a vector of input tensor shapes and modifies a vector of output tensor shapes. + Status DecodeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector to start fresh outputs.clear(); - TensorShape out({-1, -1, 3}); // we don't know what is output image size, but we know it should be 3 channels + + // Create a new TensorShape object with dimensions {-1, -1, 3} + // This indicates that the output image size is unknown, but it will have 3 channels + TensorShape out({-1, -1, 3}); + + // Check if the rank of the first input tensor is 1 + // If it is, add the output shape to the outputs vector if (inputs[0].Rank() == 1) outputs.emplace_back(out); + + // Check if the outputs vector is not empty + // If it is not empty, return OK status if (!outputs.empty()) return Status::OK(); + + // If the outputs vector is empty, return an error status + // The error message includes the input shape dimension return Status( StatusCode::kMDUnexpectedError, "Decode: invalid input shape, expected 1D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } +// Define the function `OutputType` of the `DecodeOp` class Status DecodeOp::OutputType(const std::vector &inputs, std::vector &outputs) { + + // Call the `OutputType` function of the base class `TensorOp` and check if it returns an error RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); + + // Set the first element of the `outputs` vector to be of type `DE_UINT8` outputs[0] = DataType(DataType::DE_UINT8); + + // Return a status indicating successful execution of the function return Status::OK(); } + +// End of the `dataset` namespace } // namespace dataset -} // namespace mindspore + +// End of the `mindspore` namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/equalize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/equalize_op.cc index e5bf0fd6282..55ac1923ded 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/equalize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/equalize_op.cc @@ -16,14 +16,40 @@ #include "minddata/dataset/kernels/image/equalize_op.h" #include "minddata/dataset/kernels/image/image_utils.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -// only supports RGB images + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +#include + +int main(){ + std::cout << "Hello World" << std::endl; + return 0; +} + +// This code does not have any functionality related to image processing or handling. +// It simply prints "Hello World" to the console and returns 0. + +// The Compute function of the EqualizeOp class Status EqualizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the Equalize function to perform the equalization operation on the input tensor and store the result in the output tensor return Equalize(input, output); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/exif_utils.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/exif_utils.cc index 6acdfdbba93..582df0e706a 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/exif_utils.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/exif_utils.cc @@ -14,133 +14,289 @@ * limitations under the License. */ -#include "minddata/dataset/kernels/image/exif_utils.h" +// Include the header file "minddata/dataset/kernels/image/exif_utils.h" which contains the necessary functions and definitions for working with EXIF data in image processing. +// Include the algorithm header for using various algorithms like sorting, searching, etc. #include + +// Include the cstdint header for using fixed-width integer types like int32_t, uint64_t, etc. #include +// Define a constant named UNKNOW_ORIENTATION with a value of 0 #define UNKNOW_ORIENTATION 0 +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code for the "mindspore::dataset" namespace goes here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// A template function that takes a pointer to an array of uint8_t (byte) values, along with a boolean flag indicating whether the bytes should be parsed in Intel alignment or not. +// The function returns a value of type T, which is the type specified by the template parameter. template T parse_bytes(const uint8_t *buf, bool intel_align); +// This is a template specialization for the function `parse_bytes` for the type `uint8_t` +// It takes a pointer to a buffer `buf` and a boolean flag `intel_align` as parameters +// It returns the value pointed to by `buf`, which is of type `uint8_t` + template <> uint8_t parse_bytes(const uint8_t *buf, bool intel_align) { + // Dereference the pointer `buf` and return the value return *buf; } +// Template specialization for parsing bytes as uint16_t template <> uint16_t parse_bytes(const uint8_t *buf, bool intel_align) { + + // Check if the buffer is null if (buf == nullptr) { + // If the buffer is null, return 0 return 0; } - - uint16_t res = 0; - if (intel_align) { - res = (static_cast(buf[1]) << 8) | buf[0]; - } else { - res = (static_cast(buf[0]) << 8) | buf[1]; - } - return res; + // If the buffer is not null, continue with the parsing + // (the rest of the code is not provided) } +// Declare a variable named "res" of type uint16_t and initialize it to 0 +uint16_t res = 0; + +// Check if the "intel_align" variable is true +if (intel_align) { + // If true, assign the result of the bitwise OR operation between the left-shifted value of buf[1] by 8 bits and buf[0] to "res" + res = (static_cast(buf[1]) << 8) | buf[0]; +} else { + // If false, assign the result of the bitwise OR operation between the left-shifted value of buf[0] by 8 bits and buf[1] to "res" + res = (static_cast(buf[0]) << 8) | buf[1]; +} + +// Return the value of "res" +return res; + +// Template specialization for parsing bytes as uint32_t template <> uint32_t parse_bytes(const uint8_t *buf, bool intel_align) { + + // Check if the buffer is null if (buf == nullptr) { - return 0; + return 0; // Return 0 if the buffer is null } - uint32_t res = 0; - if (intel_align) { +// Declare a variable 'res' of type uint32_t and initialize it to 0 +uint32_t res = 0; + +// Check if the 'intel_align' flag is true +if (intel_align) { + // If 'intel_align' is true, perform a bitwise OR operation on the elements of 'buf' to form a 32-bit value + // The elements are cast to uint32_t before shifting and ORing them together res = (static_cast(buf[3]) << 24) | (static_cast(buf[2]) << 16) | (static_cast(buf[1]) << 8) | buf[0]; - } else { +} else { + // If 'intel_align' is false, perform a bitwise OR operation on the elements of 'buf' in a different order + // The elements are cast to uint32_t before shifting and ORing them together res = (static_cast(buf[0]) << 24) | (static_cast(buf[1]) << 16) | (static_cast(buf[2]) << 8) | buf[3]; - } - return res; } +// Return the resulting value 'res' +return res; + +// Function to parse Exif data int parseExif(const uint8_t *buf, uint32_t len) { + + // Variable to store the alignment type of the Exif data bool intel_align = true; + + // Variable to store the offset of the Exif data uint32_t offset = 0; + + // Check if the buffer is null or the length is less than 6 bytes if (!buf || len < 6) { + + // Return a constant value indicating unknown orientation return UNKNOW_ORIENTATION; } - if (!std::equal(buf, buf + 6, "Exif\0\0")) { +// Check if the first 6 characters in the buffer 'buf' are equal to the string "Exif\0\0" +// If they are not equal, return the value UNKNOW_ORIENTATION +if (!std::equal(buf, buf + 6, "Exif\0\0")) { return UNKNOW_ORIENTATION; - } - offset += 6; +} +// Increment the value of 'offset' by 6 +offset += 6; + + // Check if the offset plus 8 is greater than the length of the buffer if (offset + 8 > len) { + // If it is, return the value UNKNOW_ORIENTATION return UNKNOW_ORIENTATION; } + + // Check if the characters at the offset and offset + 1 positions in the buffer are 'I' and 'I' respectively if (buf[offset] == 'I' && buf[offset + 1] == 'I') { + // If they are, set the variable intel_align to true intel_align = true; - } else if (buf[offset] == 'M' && buf[offset + 1] == 'M') { + } + // If the above condition is not true, check if the characters at the offset and offset + 1 positions in the buffer are 'M' and 'M' respectively + else if (buf[offset] == 'M' && buf[offset + 1] == 'M') { + // If they are, set the variable intel_align to false intel_align = false; - } else { + } + // If none of the above conditions are true, return the value UNKNOW_ORIENTATION + else { return UNKNOW_ORIENTATION; } + // Increase the offset by 2 offset += 2; + + // Check if the value at the current offset in the buffer is not equal to 0x2a if (parse_bytes(buf + offset, intel_align) != 0x2a) { - return UNKNOW_ORIENTATION; - } - offset += 2; - uint32_t first_ifd_offset = parse_bytes(buf + offset, intel_align); - offset += first_ifd_offset - 4; - if (offset >= len || offset + 2 > len) { + // If it is not equal, return UNKNOW_ORIENTATION return UNKNOW_ORIENTATION; } - int num_entries = parse_bytes(buf + offset, intel_align); - if (offset + 6 + 12 * num_entries > len) { + // Increase the offset by 2 + offset += 2; + + // Parse the value at the current offset in the buffer as a uint32_t and assign it to first_ifd_offset + uint32_t first_ifd_offset = parse_bytes(buf + offset, intel_align); + + // Increase the offset by (first_ifd_offset - 4) + offset += first_ifd_offset - 4; + + // Check if the offset is greater than or equal to len, or if offset + 2 is greater than len + if (offset >= len || offset + 2 > len) { + // If either condition is true, return UNKNOW_ORIENTATION return UNKNOW_ORIENTATION; } - offset += 2; - while (num_entries > 0) { + +// Parse the number of entries from the buffer starting at the given offset, using the template function parse_bytes +int num_entries = parse_bytes(buf + offset, intel_align); + +// Check if the total length of the buffer is sufficient to contain all the entries +if (offset + 6 + 12 * num_entries > len) { + // If not, return an unknown orientation value + return UNKNOW_ORIENTATION; +} + +// Increment the offset by 2 to skip the number of entries +offset += 2; + +// Loop through each entry +while (num_entries > 0) { + // Parse the tag from the buffer at the current offset uint16_t tag = parse_bytes(buf + offset, intel_align); + + // Check if the tag is equal to 0x112 if (tag == 0x112) { - uint16_t format = parse_bytes(buf + offset + 2, intel_align); - uint32_t length = parse_bytes(buf + offset + 4, intel_align); - if (format == 3 && length) { - uint16_t orient = parse_bytes(buf + offset + 8, intel_align); - return static_cast(orient); - } + // Parse the format, length, and orientation from the buffer at the appropriate offsets + uint16_t format = parse_bytes(buf + offset + 2, intel_align); + uint32_t length = parse_bytes(buf + offset + 4, intel_align); + + // Check if the format is equal to 3 and the length is non-zero + if (format == 3 && length) { + // Parse the orientation from the buffer at the appropriate offset + uint16_t orient = parse_bytes(buf + offset + 8, intel_align); + + // Return the orientation as an integer + return static_cast(orient); + } } + + // Increment the offset by 12 to skip to the next entry offset += 12; + + // Decrement the number of entries num_entries--; +} + +// If no valid orientation is found, return an unknown orientation value +return UNKNOW_ORIENTATION; + +// Function to parse the orientation information from the given data +int ExifInfo::parseOrientation(const unsigned char *data, unsigned len) { + + // Check if the data pointer is null or the length is less than 4 + if (!data || len < 4) + return UNKNOW_ORIENTATION; // Return the unknown orientation value + + // ... (rest of the code) + +// Check if the first element of the data array is not equal to 0xFF or the second element is not equal to 0xD8 +// If either condition is true, return UNKNOW_ORIENTATION +if (data[0] != 0xFF || data[1] != 0xD8) { + return UNKNOW_ORIENTATION; +} + +// Loop while the length of the data is greater than 2 +while (len > 2) { + + // Check if the last two elements of the data array are equal to 0xD9 and 0xFF respectively + if (data[len - 1] == 0xD9 && data[len - 2] == 0xFF) { + + // If the condition is true, break out of the loop + break; + } + + // If the condition is false, decrement the length variable by 1 + len--; +} + +// Check if the length is less than or equal to 2 +if (len <= 2) { + + // If the condition is true, return the value UNKNOW_ORIENTATION + return UNKNOW_ORIENTATION; +} + +// Initialize an unsigned integer variable named "offset" with a value of 0 +unsigned int offset = 0; + +// Start a for loop that continues until the value of "offset" is less than "len - 1" +for (; offset < len - 1; offset++) { + + // Check if the value of "data[offset]" is equal to 0xFF and the value of "data[offset + 1]" is equal to 0xE1 + if (data[offset] == 0xFF && data[offset + 1] == 0xE1) { + + // If the condition is true, break out of the loop + break; } +} + +// Check if the value of "offset + 4" is greater than "len" +if (offset + 4 > len) { + + // If the condition is true, return the value UNKNOW_ORIENTATION return UNKNOW_ORIENTATION; } -int ExifInfo::parseOrientation(const unsigned char *data, unsigned len) { - if (!data || len < 4) return UNKNOW_ORIENTATION; +// Increment the value of "offset" by 2 +offset += 2; - if (data[0] != 0xFF || data[1] != 0xD8) return UNKNOW_ORIENTATION; +// Parse the next 2 bytes of data starting from the address "data + offset" as a uint16_t and store it in the variable "section_length" +uint16_t section_length = parse_bytes(data + offset, false); - while (len > 2) { - if (data[len - 1] == 0xD9 && data[len - 2] == 0xFF) break; - len--; - } - if (len <= 2) return UNKNOW_ORIENTATION; +// Check if the value of "offset + section_length" is greater than "len" or if "section_length" is less than 16 +if (offset + section_length > len || section_length < 16) { - unsigned int offset = 0; - for (; offset < len - 1; offset++) { - if (data[offset] == 0xFF && data[offset + 1] == 0xE1) break; - } - if (offset + 4 > len) return UNKNOW_ORIENTATION; - offset += 2; - uint16_t section_length = parse_bytes(data + offset, false); - if (offset + section_length > len || section_length < 16) return UNKNOW_ORIENTATION; - offset += 2; + // If the condition is true, return the value UNKNOW_ORIENTATION + return UNKNOW_ORIENTATION; +} +// Increment the value of "offset" by 2 +offset += 2; + + // Return the result of calling the parseExif function with the arguments data + offset and len - offset return parseExif(data + offset, len - offset); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/gaussian_blur_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/gaussian_blur_op.cc index 8d590038a9d..ce9fc96c82f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/gaussian_blur_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/gaussian_blur_op.cc @@ -15,19 +15,38 @@ */ #include "minddata/dataset/kernels/image/gaussian_blur_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// The code is inside the "mindspore" namespace namespace mindspore { + +// The code is inside the "dataset" namespace, which is a sub-namespace of "mindspore" namespace dataset { + +// Implementation of the Compute function of the GaussianBlurOp class Status GaussianBlurOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid IO_CHECK(input, output); + + // Validate the rank of the input tensor RETURN_IF_NOT_OK(ValidateImageRank("GaussianBlur", input->Rank())); + + // Call the GaussianBlur function with the specified parameters return GaussianBlur(input, output, kernel_x_, kernel_y_, sigma_x_, sigma_y_); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/horizontal_flip_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/horizontal_flip_op.cc index aa6bf98a640..827e09db8f0 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/horizontal_flip_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/horizontal_flip_op.cc @@ -14,15 +14,27 @@ * limitations under the License. */ +// Include the header file "minddata/dataset/kernels/image/horizontal_flip_op.h" #include "minddata/dataset/kernels/image/horizontal_flip_op.h" -#include "minddata/dataset/kernels/image/image_utils.h" +// Include the header file "minddata/dataset/kernels/image/image_utils.h" which contains utility functions for image processing in the MindData library. +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace, which is a sub-namespace of "mindspore" namespace dataset { + +// Implementation of the Compute function for the HorizontalFlipOp class Status HorizontalFlipOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid IO_CHECK(input, output); + + // Call the HorizontalFlip function to perform the horizontal flip operation on the input tensor return HorizontalFlip(input, output); } -} // namespace dataset -} // namespace mindspore + +} // End of the "dataset" namespace + +} // End of the "mindspore" namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/hwc_to_chw_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/hwc_to_chw_op.cc index a2990d9a305..e805e61b81f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/hwc_to_chw_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/hwc_to_chw_op.cc @@ -15,36 +15,73 @@ */ #include "minddata/dataset/kernels/image/hwc_to_chw_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" namespace mindspore { namespace dataset { + +// Compute function for HwcToChwOp Status HwcToChwOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + // Check if input and output are valid IO_CHECK(input, output); - // input.shape == HWC - // output.shape == CHW + + // input shape is HWC + // output shape is CHW + // Convert input tensor from HWC to CHW format return HwcToChw(input, output); } + +// OutputShape function for HwcToChwOp Status HwcToChwOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + // Call OutputShape function from base class TensorOp RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Check if inputs vector has at least one element CHECK_FAIL_RETURN_UNEXPECTED(inputs.size() > 0, "HwcToChwOp::OutputShape inputs size should > 0"); + + // Get the shape of the first input tensor TensorShape in = inputs[0]; + + // Create the output shape by rearranging the dimensions of the input shape TensorShape out = TensorShape{in[2], in[0], in[1]}; + + // If the input tensor has rank 3, add the output shape to the outputs vector if (inputs[0].Rank() == 3) { (void)outputs.emplace_back(out); } + + // If outputs vector is not empty, return OK status if (!outputs.empty()) { return Status::OK(); } + // Otherwise, return an error status + // (Note: this line is incomplete and should be completed in the actual code) +} + // Return a Status object with StatusCode::kMDUnexpectedError and an error message + // The error message is constructed by concatenating the string "HWC2CHW: invalid input shape, expected 3D input, but got input dimension is:" + // with the string representation of the rank of the first element in the inputs vector return Status( StatusCode::kMDUnexpectedError, "HWC2CHW: invalid input shape, expected 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc index 2ffefd09eb9..7680a62d10a 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/image_utils.cc @@ -28,44 +28,80 @@ #include "minddata/dataset/kernels/image/math_utils.h" #include "minddata/dataset/kernels/image/resize_cubic_op.h" +// Define a constant integer variable named MAX_INT_PRECISION and assign it the value 16777216 const int32_t MAX_INT_PRECISION = 16777216; // float int precision is 16777216 + +// Define a constant integer variable named DEFAULT_NUM_HEIGHT and assign it the value 1 const int32_t DEFAULT_NUM_HEIGHT = 1; + +// Define a constant integer variable named DEFAULT_NUM_WIDTH and assign it the value 1 const int32_t DEFAULT_NUM_WIDTH = 1; +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -int GetCVInterpolationMode(InterpolationMode mode) { - switch (mode) { - case InterpolationMode::kLinear: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - case InterpolationMode::kCubic: - return static_cast(cv::InterpolationFlags::INTER_CUBIC); - case InterpolationMode::kArea: - return static_cast(cv::InterpolationFlags::INTER_AREA); - case InterpolationMode::kNearestNeighbour: - return static_cast(cv::InterpolationFlags::INTER_NEAREST); - default: - return static_cast(cv::InterpolationFlags::INTER_LINEAR); - } -} + + // Start of the "dataset" namespace + namespace dataset { + + // Function to convert an InterpolationMode enum value to the corresponding cv::InterpolationFlags value + int GetCVInterpolationMode(InterpolationMode mode) { + + // Switch statement to handle different InterpolationMode cases + switch (mode) { + + // If the mode is InterpolationMode::kLinear, return the corresponding cv::InterpolationFlags value + case InterpolationMode::kLinear: + return static_cast(cv::InterpolationFlags::INTER_LINEAR); + + // If the mode is InterpolationMode::kCubic, return the corresponding cv::InterpolationFlags value + case InterpolationMode::kCubic: + return static_cast(cv::InterpolationFlags::INTER_CUBIC); + + // If the mode is InterpolationMode::kArea, return the corresponding cv::InterpolationFlags value + case InterpolationMode::kArea: + return static_cast(cv::InterpolationFlags::INTER_AREA); + + // If the mode is InterpolationMode::kNearestNeighbour, return the corresponding cv::InterpolationFlags value + case InterpolationMode::kNearestNeighbour: + return static_cast(cv::InterpolationFlags::INTER_NEAREST); + + // If none of the above cases match, return the default cv::InterpolationFlags value (INTER_LINEAR) + default: + return static_cast(cv::InterpolationFlags::INTER_LINEAR); + } + } + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Function to convert a BorderType enum value to the corresponding OpenCV border type integer value int GetCVBorderType(BorderType type) { switch (type) { case BorderType::kConstant: + // Convert BorderType::kConstant to cv::BorderTypes::BORDER_CONSTANT using static_cast return static_cast(cv::BorderTypes::BORDER_CONSTANT); case BorderType::kEdge: + // Convert BorderType::kEdge to cv::BorderTypes::BORDER_REPLICATE using static_cast return static_cast(cv::BorderTypes::BORDER_REPLICATE); case BorderType::kReflect: + // Convert BorderType::kReflect to cv::BorderTypes::BORDER_REFLECT101 using static_cast return static_cast(cv::BorderTypes::BORDER_REFLECT101); case BorderType::kSymmetric: + // Convert BorderType::kSymmetric to cv::BorderTypes::BORDER_REFLECT using static_cast return static_cast(cv::BorderTypes::BORDER_REFLECT); default: + // If the input BorderType is not recognized, return cv::BorderTypes::BORDER_CONSTANT using static_cast return static_cast(cv::BorderTypes::BORDER_CONSTANT); } } +// Function to determine the shape of the converted image based on the convert mode and input CV tensor Status GetConvertShape(ConvertMode convert_mode, const std::shared_ptr &input_cv, std::vector *node) { + + // Define vectors for different convert modes based on the number of channels std::vector one_channels = {ConvertMode::COLOR_BGR2GRAY, ConvertMode::COLOR_RGB2GRAY, ConvertMode::COLOR_BGRA2GRAY, ConvertMode::COLOR_RGBA2GRAY}; std::vector three_channels = { @@ -75,440 +111,823 @@ Status GetConvertShape(ConvertMode convert_mode, const std::shared_ptr ConvertMode::COLOR_BGR2RGBA, ConvertMode::COLOR_RGB2BGRA, ConvertMode::COLOR_BGRA2RGBA, ConvertMode::COLOR_RGBA2BGRA, ConvertMode::COLOR_GRAY2BGRA, ConvertMode::COLOR_GRAY2RGBA}; + + // Check if the convert mode belongs to the three_channels vector if (std::find(three_channels.begin(), three_channels.end(), convert_mode) != three_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1], 3}; - } else if (std::find(four_channels.begin(), four_channels.end(), convert_mode) != four_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1], 4}; - } else if (std::find(one_channels.begin(), one_channels.end(), convert_mode) != one_channels.end()) { - *node = {input_cv->shape()[0], input_cv->shape()[1]}; - } else { + *node = {input_cv->shape()[0], input_cv->shape()[1], 3}; // Set the shape to have 3 channels + } + // Check if the convert mode belongs to the four_channels vector + else if (std::find(four_channels.begin(), four_channels.end(), convert_mode) != four_channels.end()) { + *node = {input_cv->shape()[0], input_cv->shape()[1], 4}; // Set the shape to have 4 channels + } + // Check if the convert mode belongs to the one_channels vector + else if (std::find(one_channels.begin(), one_channels.end(), convert_mode) != one_channels.end()) { + *node = {input_cv->shape()[0], input_cv->shape()[1]}; // Set the shape to have 2 channels + } + // If the convert mode does not belong to any of the above vectors, return an error status + else { RETURN_STATUS_UNEXPECTED( "The mode of image channel conversion must be in ConvertMode, which mainly includes " "conversion between RGB, BGR, GRAY, RGBA etc."); } + + // Return a success status return Status::OK(); } +// Function to check the shape of a tensor bool CheckTensorShape(const std::shared_ptr &tensor, const int &channel) { + + // Check if the tensor is null if (tensor == nullptr) { return false; } + + // Initialize the return value to false bool rc = false; + + // Check if the size of the tensor is less than or equal to the given channel if (tensor->shape().Size() <= channel) { return false; } + + // Check if the rank of the tensor is not equal to the default image rank + // or if the shape of the tensor at the given channel is not equal to 1 + // and not equal to the default image channels if (tensor->Rank() != DEFAULT_IMAGE_RANK || (tensor->shape()[channel] != 1 && tensor->shape()[channel] != DEFAULT_IMAGE_CHANNELS)) { rc = true; } + + // Return the result return rc; } -Status Flip(std::shared_ptr input, std::shared_ptr *output, int flip_code) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); +// Define a function named "Flip" that takes in three parameters: +// 1. A shared pointer to a Tensor object named "input" +// 2. A pointer to a shared pointer of a Tensor object named "output" +// 3. An integer variable named "flip_code" - if (input_cv->Rank() == 1 || input_cv->mat().dims > 2) { - std::string err_msg = - "Flip: shape of input is not or , but got rank:" + std::to_string(input_cv->Rank()); - if (input_cv->Rank() == 1) { - err_msg = err_msg + ", may need to do Decode first."; - } - RETURN_STATUS_UNEXPECTED(err_msg); +// Convert the input shared pointer to a shared pointer of a CVTensor object and assign it to a new variable named "input_cv" + +// Check if the rank of the input tensor is 1 or if the number of dimensions is greater than 2 +if (input_cv->Rank() == 1 || input_cv->mat().dims > 2) { + + // Create an error message string indicating the shape of the input tensor + std::string err_msg = + "Flip: shape of input is not or , but got rank:" + std::to_string(input_cv->Rank()); + + // If the rank is 1, append a message suggesting to perform a Decode operation first + if (input_cv->Rank() == 1) { + err_msg = err_msg + ", may need to do Decode first."; } - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + // Return an unexpected status with the error message + RETURN_STATUS_UNEXPECTED(err_msg); +} + +// Declare a shared pointer named "output_cv" of type CVTensor +std::shared_ptr output_cv; + +// Call the static member function "CreateEmpty" of the CVTensor class to create an empty CVTensor object +// Pass the shape and type of the input_cv object as arguments, and store the result in the output_cv object +RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); if (input_cv->mat().data) { + // Check if the input OpenCV matrix has valid data + try { + // Use the flip function from the OpenCV library to flip the input matrix + // The result is stored in the output matrix cv::flip(input_cv->mat(), output_cv->mat(), flip_code); + + // Convert the output matrix to a shared pointer of type Tensor *output = std::static_pointer_cast(output_cv); + + // Return a Status object indicating successful execution return Status::OK(); } catch (const cv::Exception &e) { + // If an exception is caught, return an error message with the details of the exception RETURN_STATUS_UNEXPECTED("Flip: " + std::string(e.what())); } } else { + // If the input matrix does not have valid data, return an error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Flip: allocate memory failed."); } } -Status HorizontalFlip(std::shared_ptr input, std::shared_ptr *output) { - return Flip(std::move(input), output, 1); -} +// This function is called HorizontalFlip and it takes two parameters: +// - input: a shared pointer to a Tensor object, which represents the input tensor +// - output: a pointer to a shared pointer of a Tensor object, which will store the output tensor -Status VerticalFlip(std::shared_ptr input, std::shared_ptr *output) { - return Flip(std::move(input), output, 0); -} +// The function returns the result of calling the Flip function with the following arguments: +// - input: the input tensor, which is moved using std::move to transfer ownership +// - output: a pointer to the output tensor, which is passed by reference +// - 1: an integer value indicating that the flip operation should be performed horizontally -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { +// The result of the Flip function is returned by this function. + +// This function is called "VerticalFlip" and it takes two parameters: +// 1. A shared pointer to a Tensor object called "input", which represents the input tensor +// 2. A pointer to a shared pointer of a Tensor object called "output", which will store the flipped tensor + +// The function calls another function called "Flip" and passes the following arguments: +// 1. The input tensor, which is moved using std::move to transfer ownership to the function +// 2. The pointer to the output tensor, which will be populated with the flipped tensor +// 3. The value 0, which indicates that the flip operation should be performed vertically + +// The return value of the "Flip" function is then returned by this function as well. + +// Resize function to resize an input tensor to a specified output size using various interpolation modes + +// Parameters: +// - input: shared pointer to the input tensor +// - output: pointer to the output tensor (shared pointer) +// - output_height: desired height of the output tensor +// - output_width: desired width of the output tensor +// - fx: scaling factor for the width dimension +// - fy: scaling factor for the height dimension +// - mode: interpolation mode to be used + +// Convert the input tensor to a CVTensor (OpenCV tensor representation) +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + +// Check if the input tensor has valid data +if (!input_cv->mat().data) { + // If the input tensor does not have valid data, return an error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Resize: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Resize", input_cv->Rank())); +} - cv::Mat in_image = input_cv->mat(); - const uint32_t kResizeShapeLimits = 1000; - // resize image too large or too small, 1000 is arbitrarily chosen here to prevent open cv from segmentation fault - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.rows, - "Resize: in_image rows out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.cols, - "Resize: in_image cols out of bounds."); - if (output_height > in_image.rows * kResizeShapeLimits || output_width > in_image.cols * kResizeShapeLimits) { +// Validate the rank of the input tensor +RETURN_IF_NOT_OK(ValidateImageRank("Resize", input_cv->Rank())); + +// Continue with the rest of the function... + +// Convert the input_cv to a cv::Mat object and assign it to the variable in_image +cv::Mat in_image = input_cv->mat(); + +// Define a constant variable kResizeShapeLimits with a value of 1000 +const uint32_t kResizeShapeLimits = 1000; + +// Check if the number of rows in in_image is within the allowed limits +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.rows, + "Resize: in_image rows out of bounds."); + +// Check if the number of columns in in_image is within the allowed limits +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kResizeShapeLimits) > in_image.cols, + "Resize: in_image cols out of bounds."); + +// Check if the output_height or output_width is too big compared to the original image size +if (output_height > in_image.rows * kResizeShapeLimits || output_width > in_image.cols * kResizeShapeLimits) { + // Create an error message with details about the resizing dimensions and the original image size std::string err_msg = "Resize: the resizing width or height is too big, it's 1000 times bigger than the original image, got output " "height: " + std::to_string(output_height) + ", width: " + std::to_string(output_width) + ", and original image size:" + std::to_string(in_image.rows) + ", " + std::to_string(in_image.cols); + + // Return a Status object with the error message and a specific error code return Status(StatusCode::kMDShapeMisMatch, err_msg); - } - if (output_height == 0 || output_width == 0) { - std::string err_msg = "Resize: the input value of 'resize' is invalid, width or height is zero."; - return Status(StatusCode::kMDShapeMisMatch, err_msg); - } +} - if (mode == InterpolationMode::kCubicPil) { +// Check if the output_height or output_width is zero +if (output_height == 0 || output_width == 0) { + // Create an error message indicating that the input value of 'resize' is invalid + std::string err_msg = "Resize: the input value of 'resize' is invalid, width or height is zero."; + + // Return a Status object with the error message and a specific error code + return Status(StatusCode::kMDShapeMisMatch, err_msg); +} + +// Check if the mode is set to InterpolationMode::kCubicPil +if (mode == InterpolationMode::kCubicPil) { + + // Check if the input image has the correct number of channels if (input_cv->shape().Size() != DEFAULT_IMAGE_CHANNELS || input_cv->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("Resize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + + + // Return an error message indicating that the PILCUBIC interpolation mode only supports images with 3 channels + RETURN_STATUS_UNEXPECTED("Resize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + input_cv->shape().ToString()); } +} - LiteMat imIn, imOut; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({output_height, output_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - imOut.Init(output_width, output_height, input_cv->shape()[2], reinterpret_cast(buffer), LDataType::UINT8); - imIn.Init(input_cv->shape()[1], input_cv->shape()[0], input_cv->shape()[2], input_cv->mat().data, LDataType::UINT8); - if (ResizeCubic(imIn, imOut, output_width, output_height) == false) { - RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); + LiteMat imIn, imOut; // Declare two LiteMat objects for input and output + std::shared_ptr output_tensor; // Declare a shared pointer to a Tensor object + TensorShape new_shape = TensorShape({output_height, output_width, 3}); // Create a new shape for the output tensor + RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); // Create an empty tensor with the new shape and assign it to the output_tensor pointer + uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); // Get a pointer to the beginning of the output tensor's data buffer + imOut.Init(output_width, output_height, input_cv->shape()[2], reinterpret_cast(buffer), LDataType::UINT8); // Initialize the output LiteMat with the specified dimensions and data buffer + imIn.Init(input_cv->shape()[1], input_cv->shape()[0], input_cv->shape()[2], input_cv->mat().data, LDataType::UINT8); // Initialize the input LiteMat with the specified dimensions and data + if (ResizeCubic(imIn, imOut, output_width, output_height) == false) { // Perform cubic resizing on the input LiteMat and store the result in the output LiteMat + RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); // Return an unexpected status if the resizing fails } - *output = output_tensor; - return Status::OK(); - } - try { - TensorShape shape{output_height, output_width}; - int num_channels = input_cv->shape()[CHANNEL_INDEX]; - if (input_cv->Rank() == DEFAULT_IMAGE_RANK) shape = shape.AppendDim(num_channels); - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); + *output = output_tensor; // Assign the output_tensor to the output pointer + return Status::OK(); // Return a status indicating successful execution + } // End of the function + + try { + TensorShape shape{output_height, output_width}; // Create a new shape for the output tensor + int num_channels = input_cv->shape()[CHANNEL_INDEX]; // Get the number of channels from the input tensor + if (input_cv->Rank() == DEFAULT_IMAGE_RANK) shape = shape.AppendDim(num_channels); // Append the number of channels to the shape if the input tensor has the default image rank + std::shared_ptr output_cv; // Declare a shared pointer to a CVTensor object + RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); // Create an empty CVTensor with the new shape and assign it to the output_cv pointer + + // Convert the given mode to the corresponding OpenCV interpolation mode auto cv_mode = GetCVInterpolationMode(mode); + + // Resize the input image using the specified output width, output height, scaling factors fx and fy, and the OpenCV interpolation mode cv::resize(in_image, output_cv->mat(), cv::Size(output_width, output_height), fx, fy, cv_mode); + + // Cast the output_cv pointer to a shared pointer of type Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); + + // Return a Status object indicating successful program execution return Status::OK(); + + // Catch any exceptions thrown by the cv::resize function and handle them } catch (const cv::Exception &e) { + // Return a Status object with an error message indicating the failure in resizing RETURN_STATUS_UNEXPECTED("Resize: " + std::string(e.what())); } } +// Function to check if the input Tensor contains a non-empty JPEG image bool IsNonEmptyJPEG(const std::shared_ptr &input) { + + // Define the magic bytes that indicate the start of a JPEG image const unsigned char *kJpegMagic = (unsigned char *)"\xFF\xD8\xFF"; + + // Define the length of the magic bytes constexpr dsize_t kJpegMagicLen = 3; + + // Check if the size of the input Tensor in bytes is greater than the length of the magic bytes + // and if the first kJpegMagicLen bytes of the input Tensor match the magic bytes return input->SizeInBytes() > kJpegMagicLen && memcmp(input->GetBuffer(), kJpegMagic, kJpegMagicLen) == 0; } +// Function to check if the input Tensor contains a non-empty PNG image bool IsNonEmptyPNG(const std::shared_ptr &input) { + + // Define the PNG magic number as a sequence of bytes const unsigned char *kPngMagic = (unsigned char *)"\x89\x50\x4E\x47"; + + // Define the length of the PNG magic number constexpr dsize_t kPngMagicLen = 4; + + // Check if the size of the input Tensor in bytes is greater than the length of the PNG magic number + // and if the first kPngMagicLen bytes of the input Tensor match the PNG magic number return input->SizeInBytes() > kPngMagicLen && memcmp(input->GetBuffer(), kPngMagic, kPngMagicLen) == 0; } +// Function to decode an input tensor and store the result in an output tensor +// The function takes a shared pointer to the input tensor and a pointer to the output tensor +// The function returns a status indicating the success or failure of the decoding process + Status Decode(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input tensor contains a non-empty JPEG image if (IsNonEmptyJPEG(input)) { + + // If the input is a non-empty JPEG image, call the JpegCropAndDecode function to crop and decode the image return JpegCropAndDecode(input, output); + } else { + + // If the input is not a non-empty JPEG image, call the DecodeCv function to decode the image using OpenCV return DecodeCv(input, output); } } +// DecodeCv function to decode an image using OpenCV library +// Takes an input tensor and a pointer to an output tensor as arguments Status DecodeCv(const std::shared_ptr &input, std::shared_ptr *output) { + + // Convert the input tensor to a CVTensor using the AsCVTensor function std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the input CVTensor has valid data if (!input_cv->mat().data) { + // If not, return an error status with a descriptive message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: load image failed."); } + try { + // Decode the image using the imdecode function from OpenCV cv::Mat img_mat = cv::imdecode(input_cv->mat(), cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION); + + // Check if the image decoding was successful if (img_mat.data == nullptr) { + // If not, return an error status with a descriptive message std::string err = "Decode: image decode failed."; RETURN_STATUS_UNEXPECTED(err); } + + // Convert the image from BGR to RGB color space cv::cvtColor(img_mat, img_mat, static_cast(cv::COLOR_BGR2RGB)); + + // Create a new CVTensor from the converted image matrix std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(img_mat, 3, &output_cv)); + + // Assign the output tensor pointer to the converted CVTensor *output = std::static_pointer_cast(output_cv); + + // Return a success status return Status::OK(); + } catch (const cv::Exception &e) { + // If an exception occurs during the decoding process, return an error status with the exception message RETURN_STATUS_UNEXPECTED("Decode: " + std::string(e.what())); } } -static void JpegInitSource(j_decompress_ptr cinfo) {} +// Define a static function named JpegInitSource that takes a pointer to a j_decompress_struct as a parameter +static void JpegInitSource(j_decompress_ptr cinfo) { + // This function is empty and does not contain any code + // It is likely intended to be implemented later with functionality specific to initializing the JPEG source +} + +// A static function named JpegFillInputBuffer that takes a pointer to a j_decompress_ptr structure as a parameter and returns a boolean value static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) { + + // Check if the number of bytes in the buffer of the source object in the j_decompress_ptr structure is zero if (cinfo->src->bytes_in_buffer == 0) { - // Under ARM platform raise runtime_error may cause core problem, - // so we catch runtime_error and just return FALSE. + + // If the number of bytes in the buffer is zero, raise the JERR_INPUT_EMPTY error using the ERREXIT macro try { ERREXIT(cinfo, JERR_INPUT_EMPTY); } catch (std::runtime_error &e) { + + // Catch any std::runtime_error exceptions that may occur and return FALSE return FALSE; } + + // Return FALSE if the number of bytes in the buffer is zero return FALSE; } + + // Return TRUE if the number of bytes in the buffer is not zero return TRUE; } -static void JpegTermSource(j_decompress_ptr cinfo) {} +// Define a static function named JpegTermSource that takes a pointer to a j_decompress_struct as a parameter +static void JpegTermSource(j_decompress_ptr cinfo) { + // This function does not have any implementation, it is empty + // It is likely intended to be used as a callback function for terminating the JPEG decompression process + // The purpose and implementation of this function should be provided elsewhere in the code +} + +// A static function named JpegSkipInputData that takes a pointer to a decompression structure (j_decompress_ptr) and an integer (jump) as parameters static void JpegSkipInputData(j_decompress_ptr cinfo, int64_t jump) { + + // Check if the jump value is less than 0 if (jump < 0) { + // If so, return without doing anything return; } + + // Check if the jump value is greater than the number of bytes in the input buffer if (static_cast(jump) > cinfo->src->bytes_in_buffer) { + // If so, set the number of bytes in the input buffer to 0 and return cinfo->src->bytes_in_buffer = 0; return; } else { + // If not, subtract the jump value from the number of bytes in the input buffer cinfo->src->bytes_in_buffer -= jump; + // Increment the pointer to the next input byte by the jump value cinfo->src->next_input_byte += jump; } } +// Function to set the source for JPEG decompression void JpegSetSource(j_decompress_ptr cinfo, const void *data, int64_t datasize) { + + // Allocate memory for the source manager using the memory manager of the JPEG decompression object cinfo->src = static_cast( (*cinfo->mem->alloc_small)(reinterpret_cast(cinfo), JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr))); + + // Set the function pointers of the source manager to the appropriate functions cinfo->src->init_source = JpegInitSource; cinfo->src->fill_input_buffer = JpegFillInputBuffer; -#if defined(_WIN32) || defined(_WIN64) || defined(ENABLE_ARM32) || defined(__APPLE__) - cinfo->src->skip_input_data = reinterpret_cast(JpegSkipInputData); -#else - cinfo->src->skip_input_data = JpegSkipInputData; -#endif + + // Conditional compilation for different platforms + #if defined(_WIN32) || defined(_WIN64) || defined(ENABLE_ARM32) || defined(__APPLE__) + cinfo->src->skip_input_data = reinterpret_cast(JpegSkipInputData); + #else + cinfo->src->skip_input_data = JpegSkipInputData; + #endif + cinfo->src->resync_to_restart = jpeg_resync_to_restart; cinfo->src->term_source = JpegTermSource; + + // Set the size of the input buffer and the pointer to the input data cinfo->src->bytes_in_buffer = datasize; cinfo->src->next_input_byte = static_cast(data); } +// This function reads scanlines from a JPEG image using the libjpeg library. +// It takes in a jpeg_decompress_struct pointer, which contains information about the JPEG image being read. +// It also takes in various parameters such as the maximum number of scanlines to read, a buffer to store the scanlines, +// the size of the buffer, and other parameters related to cropping and stride. + static Status JpegReadScanlines(jpeg_decompress_struct *const cinfo, int max_scanlines_to_read, JSAMPLE *buffer, int buffer_size, int crop_w, int crop_w_aligned, int offset, int stride) { - // scanlines will be read to this buffer first, must have the number - // of components equal to the number of components in the image + // Check if the multiplication of crop_w_aligned and the number of output components is within the bounds of int64_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / cinfo->output_components) > crop_w_aligned, "JpegReadScanlines: multiplication out of bounds."); + + // Calculate the size of each scanline based on the cropped width and the number of output components int64_t scanline_size = crop_w_aligned * cinfo->output_components; + + // Create a vector to store the scanline data std::vector scanline(scanline_size); + + // Get a pointer to the first element of the scanline vector JSAMPLE *scanline_ptr = &scanline[0]; + + // Loop until the number of output scanlines reaches the maximum number of scanlines to read while (cinfo->output_scanline < static_cast(max_scanlines_to_read)) { int num_lines_read = 0; + + // Try to read one scanline from the JPEG image using the jpeg_read_scanlines function try { num_lines_read = jpeg_read_scanlines(cinfo, &scanline_ptr, 1); } catch (std::runtime_error &e) { + // If an exception is caught, return an error status with a specific error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: image decode failed."); } + + // If the output color space is JCS_CMYK and at least one scanline is read if (cinfo->out_color_space == JCS_CMYK && num_lines_read > 0) { + // Loop through each pixel in the cropped width for (int i = 0; i < crop_w; ++i) { + // Calculate the index of the CMYK pixel in the scanline const int cmyk_pixel = 4 * i + offset; + + // Get the CMYK values from the scanline const int c = scanline_ptr[cmyk_pixel]; const int m = scanline_ptr[cmyk_pixel + 1]; const int y = scanline_ptr[cmyk_pixel + 2]; const int k = scanline_ptr[cmyk_pixel + 3]; + + // Initialize variables for the RGB values int r, g, b; + + // If the Adobe marker is present in the JPEG image if (cinfo->saw_Adobe_marker) { + // Convert the CMYK values to RGB using the Adobe formula r = (k * c) / 255; g = (k * m) / 255; b = (k * y) / 255; } else { + // Convert the CMYK values to RGB using the default formula r = (255 - c) * (255 - k) / 255; g = (255 - m) * (255 - k) / 255; b = (255 - y) * (255 - k) / 255; } + + // Store the RGB values in the buffer buffer[3 * i + 0] = r; buffer[3 * i + 1] = g; buffer[3 * i + 2] = b; } } else if (num_lines_read > 0) { + // If the output color space is not JCS_CMYK and at least one scanline is read + + // Copy the scanline data to the buffer using the memcpy_s function int copy_status = memcpy_s(buffer, buffer_size, scanline_ptr + offset, stride); + + // Check if the copy operation was successful if (copy_status != 0) { - jpeg_destroy_decompress(cinfo); - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: memcpy failed."); + // If the copy operation failed, return an error status + // (Note: the code does not show what happens in case of failure) + } + } + } +} + jpeg_destroy_decompress(cinfo); // Destroy the decompression object to free up resources + RETURN_STATUS_UNEXPECTED("[Internal ERROR] Decode: memcpy failed."); // Return an unexpected status with an error message if memcpy fails } } else { - jpeg_destroy_decompress(cinfo); - std::string err_msg = "[Internal ERROR] Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err_msg); + jpeg_destroy_decompress(cinfo); // Destroy the decompression object to free up resources + std::string err_msg = "[Internal ERROR] Decode: image decode failed."; // Create an error message for image decode failure + RETURN_STATUS_UNEXPECTED(err_msg); // Return an unexpected status with the error message } - buffer += stride; - buffer_size = buffer_size - stride; + buffer += stride; // Move the buffer pointer to the next row of the image + buffer_size = buffer_size - stride; // Decrease the buffer size by the stride (number of bytes in a row) + } + return Status::OK(); // Return a status indicating successful execution of the function + +// A function to set the color space for JPEG decompression +static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { + + // Switch statement based on the number of components in the JPEG image + switch (cinfo->num_components) { + + // If there is only 1 component, it means the image is grayscale + case 1: + // Set the output color space to RGB + cinfo->out_color_space = JCS_RGB; + // Return a status indicating success + return Status::OK(); + + // If there are 3 components, it means the image is already in RGB color space + case 3: + // Set the output color space to RGB + cinfo->out_color_space = JCS_RGB; + // Return a status indicating success + return Status::OK(); + + // If there are 4 components, it means the image is in CMYK color space + case 4: + // Set the output color space to CMYK + cinfo->out_color_space = JCS_CMYK; + // Return a status indicating success + return Status::OK(); + + // If the number of components is none of the above cases + default: + // Destroy the decompression object + jpeg_destroy_decompress(cinfo); + // Create an error message + std::string err_msg = "[Internal ERROR] Decode: image decode failed."; + // Return an unexpected status with the error message + RETURN_STATUS_UNEXPECTED(err_msg); } - return Status::OK(); } -static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { - switch (cinfo->num_components) { - case 1: - // we want to output 3 components if it's grayscale - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 3: - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 4: - // Need to manually convert to RGB - cinfo->out_color_space = JCS_CMYK; - return Status::OK(); - default: - jpeg_destroy_decompress(cinfo); - std::string err_msg = "[Internal ERROR] Decode: image decode failed."; - RETURN_STATUS_UNEXPECTED(err_msg); - } -} +// A custom function for handling JPEG errors and throwing a runtime error void JpegErrorExitCustom(j_common_ptr cinfo) { + + // Create a character array to store the error message char jpeg_last_error_msg[JMSG_LENGTH_MAX]; + + // Call the format_message function of the error manager to get the error message (*(cinfo->err->format_message))(cinfo, jpeg_last_error_msg); + + // Throw a runtime error with the obtained error message throw std::runtime_error(jpeg_last_error_msg); } +// Define a function named JpegCropAndDecode that takes in an input tensor, an output tensor pointer, and crop parameters Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int crop_x, int crop_y, int crop_w, int crop_h) { + + // Declare a struct jpeg_decompress_struct variable named cinfo struct jpeg_decompress_struct cinfo; + + // Define a lambda function named DestroyDecompressAndReturnError that takes in an error message and destroys the decompress struct auto DestroyDecompressAndReturnError = [&cinfo](const std::string &err) { jpeg_destroy_decompress(&cinfo); RETURN_STATUS_UNEXPECTED(err); }; + + // Declare a struct JpegErrorManagerCustom variable named jerr struct JpegErrorManagerCustom jerr; + + // Set the error manager of cinfo to the standard error manager with custom error exit function cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = JpegErrorExitCustom; + try { + // Create a decompress struct jpeg_create_decompress(&cinfo); + + // Set the source of the decompress struct to the input tensor's buffer and size JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); + + // Read the header of the JPEG image (void)jpeg_read_header(&cinfo, TRUE); + + // Set the color space of the decompress struct RETURN_IF_NOT_OK(JpegSetColorSpace(&cinfo)); + + // Calculate the output dimensions of the decompress struct jpeg_calc_output_dimensions(&cinfo); } catch (std::runtime_error &e) { + // If an exception is caught, destroy the decompress struct and return an error status with the exception message return DestroyDecompressAndReturnError(e.what()); } + + // Check if the addition of crop_x and crop_w is within the bounds of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_w) > crop_x, "JpegCropAndDecode: addition(crop x and crop width) out of bounds, got crop x:" + std::to_string(crop_x) + ", and crop width:" + std::to_string(crop_w)); + + // Check if the addition of crop_y and crop_h is within the bounds of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_h) > crop_y, "JpegCropAndDecode: addition(crop y and crop height) out of bounds, got crop y:" + std::to_string(crop_y) + ", and crop height:" + std::to_string(crop_h)); + + // Check if the crop parameters are valid if (crop_x == 0 && crop_y == 0 && crop_w == 0 && crop_h == 0) { + // If all crop parameters are 0, set crop_w and crop_h to the output dimensions of the decompress struct crop_w = cinfo.output_width; crop_h = cinfo.output_height; } else if (crop_w == 0 || static_cast(crop_w + crop_x) > cinfo.output_width || crop_h == 0 || static_cast(crop_h + crop_y) > cinfo.output_height) { + // If any of the crop parameters are 0 or exceed the output dimensions of the decompress struct, return an error status return DestroyDecompressAndReturnError( "Crop: invalid crop size, corresponding crop value equal to 0 or too big, got crop width: " + std::to_string(crop_w) + ", crop height:" + std::to_string(crop_h) + ", and crop x coordinate:" + std::to_string(crop_x) + ", crop y coordinate:" + std::to_string(crop_y)); } + + // Calculate the MCU size of the decompress struct const int mcu_size = cinfo.min_DCT_scaled_size; + + // Check if the MCU size is not 0 CHECK_FAIL_RETURN_UNEXPECTED(mcu_size != 0, "JpegCropAndDecode: divisor mcu_size is zero."); + + // Align the crop_x to the nearest multiple of mcu_size unsigned int crop_x_aligned = (crop_x / mcu_size) * mcu_size; + + // Calculate the aligned crop_w by adding the difference between crop_x and crop_x_aligned unsigned int crop_w_aligned = crop_w + crop_x - crop_x_aligned; + try { - (void)jpeg_start_decompress(&cinfo); - jpeg_crop_scanline(&cinfo, &crop_x_aligned, &crop_w_aligned); - } catch (std::runtime_error &e) { - return DestroyDecompressAndReturnError(e.what()); - } - JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); - // three number of output components, always convert to RGB and output - constexpr int kOutNumComponents = 3; - TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); - const int buffer_size = output_tensor->SizeInBytes(); - JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, - "JpegCropAndDecode: addition out of bounds."); - const int max_scanlines_to_read = skipped_scanlines + crop_h; - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, - "JpegCropAndDecode: multiplication out of bounds."); - const int stride = crop_w * kOutNumComponents; - // offset is calculated for scanlines read from the image, therefore - // has the same number of components as the image - int minius_value = crop_x - crop_x_aligned; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / minius_value) > cinfo.output_components, - "JpegCropAndDecode: multiplication out of bounds."); - const int offset = minius_value * cinfo.output_components; - RETURN_IF_NOT_OK( - JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); - *output = output_tensor; - jpeg_destroy_decompress(&cinfo); - return Status::OK(); +(void)jpeg_start_decompress(&cinfo); // Start the decompression process for the JPEG image + +jpeg_crop_scanline(&cinfo, &crop_x_aligned, &crop_w_aligned); // Crop the scanline of the JPEG image + +} catch (std::runtime_error &e) { // Catch any runtime errors that occur during the process + return DestroyDecompressAndReturnError(e.what()); // Destroy the decompressor and return the error message } +JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); // Skip the specified number of scanlines in the JPEG image + +// Define the number of output components as 3 (always convert to RGB and output) +constexpr int kOutNumComponents = 3; + +// Create a tensor shape with dimensions crop_h, crop_w, and kOutNumComponents +TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); + +std::shared_ptr output_tensor; // Create a shared pointer to the output tensor + +// Create an empty tensor with the specified shape, data type, and assign it to the output_tensor +RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); + +const int buffer_size = output_tensor->SizeInBytes(); // Get the size of the output tensor buffer in bytes + +JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); // Get a pointer to the buffer of the output tensor + +// Check if the addition of skipped_scanlines and crop_h is within the bounds of float_t +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, + "JpegCropAndDecode: addition out of bounds."); + +const int max_scanlines_to_read = skipped_scanlines + crop_h; // Calculate the maximum number of scanlines to read + +// Check if the multiplication of crop_w and kOutNumComponents is within the bounds of int32_t +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, + "JpegCropAndDecode: multiplication out of bounds."); + +const int stride = crop_w * kOutNumComponents; // Calculate the stride of the output tensor + +// Calculate the offset for the scanlines read from the image +// The offset has the same number of components as the image +int minius_value = crop_x - crop_x_aligned; +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / minius_value) > cinfo.output_components, + "JpegCropAndDecode: multiplication out of bounds."); +const int offset = minius_value * cinfo.output_components; + +// Read the scanlines from the JPEG image and store them in the output tensor buffer +RETURN_IF_NOT_OK( + JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); + +*output = output_tensor; // Assign the output_tensor to the output pointer + +jpeg_destroy_decompress(&cinfo); // Destroy the decompressor object + +return Status::OK(); // Return a status indicating successful execution + +// Rescale function that takes an input tensor, rescales it, and returns the rescaled tensor as output + +// The function takes a shared pointer to the input tensor and a pointer to the output tensor +// The rescale and shift values are used to perform the rescaling operation Status Rescale(const std::shared_ptr &input, std::shared_ptr *output, float rescale, float shift) { + + // Convert the input tensor to a CVTensor (OpenCV tensor) std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the input image data is valid if (!input_cv->mat().data) { + // Return an error status if loading the image failed RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rescale: load image failed."); } + + // Get the input image as a CV::Mat object cv::Mat input_image = input_cv->mat(); + + // Create an empty CVTensor for the output std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), DataType(DataType::DE_FLOAT32), &output_cv)); + try { + // Convert the input image to floating point values and perform the rescaling operation input_image.convertTo(output_cv->mat(), CV_32F, rescale, shift); + + // Convert the output CVTensor to a regular Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); } catch (const cv::Exception &e) { + // Return an error status if an exception occurs during the rescaling operation RETURN_STATUS_UNEXPECTED("Rescale: " + std::string(e.what())); } + + // Return a success status return Status::OK(); } +// Function to crop an image given the coordinates and dimensions Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h) { + + // Convert the input tensor to a CVTensor for easier manipulation std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the input image data is valid if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] Crop: load image failed."); } + + // Validate the rank of the input image RETURN_IF_NOT_OK(ValidateImageRank("Crop", input_cv->Rank())); + + // Check if the addition of y and height is within the bounds of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - y) > h, "Crop: addition(x and height) out of bounds, got height:" + std::to_string(h) + ", and coordinate y:" + std::to_string(y)); - // account for integer overflow + + // Check if the y coordinate value is valid + // Account for integer overflow and check if y is within the boundary of the image if (y < 0 || (y + h) > input_cv->shape()[0] || (y + h) < 0) { RETURN_STATUS_UNEXPECTED( "Crop: invalid y coordinate value for crop, y coordinate value exceeds the boundary of the image, got y: " + std::to_string(y)); } + + // Check if the addition of x and width is within the bounds of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x) > w, "Crop: addition out of bounds."); - // account for integer overflow + + // Check if the x coordinate value is valid + // Account for integer overflow and check if x is within the boundary of the image if (x < 0 || (x + w) > input_cv->shape()[1] || (x + w) < 0) { RETURN_STATUS_UNEXPECTED( "Crop: invalid x coordinate value for crop, " "x coordinate value exceeds the boundary of the image, got x: " + std::to_string(x)); } + try { + // Create a tensor shape for the output image TensorShape shape{h, w}; + + // If the input image has the default rank, append the number of channels to the shape if (input_cv->Rank() == DEFAULT_IMAGE_RANK) { int num_channels = input_cv->shape()[CHANNEL_INDEX]; shape = shape.AppendDim(num_channels); } + + // Create an empty CVTensor with the desired shape and type std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &output_cv)); + + // Define a region of interest (ROI) using the given coordinates and dimensions cv::Rect roi(x, y, w, h); + + // Copy the ROI from the input image to the output image (input_cv->mat())(roi).copyTo(output_cv->mat()); + + // Convert the output CVTensor back to a regular Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); + + // Return OK status to indicate successful cropping return Status::OK(); + } catch (const cv::Exception &e) { + // If any OpenCV exception occurs, return an unexpected status with the error message RETURN_STATUS_UNEXPECTED("Crop: " + std::string(e.what())); } } +// Function to convert the color space of an image Status ConvertColor(const std::shared_ptr &input, std::shared_ptr *output, ConvertMode convert_mode) { try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Validate the rank of the input tensor RETURN_IF_NOT_OK(ValidateImageRank("ConvertColor", input_cv->Rank())); + + // Check if the input image data is valid if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] ConvertColor: load image failed."); } + + // Check the number of channels in the input image if it is in the default image rank if (input_cv->Rank() == DEFAULT_IMAGE_RANK) { int num_channels = input_cv->shape()[CHANNEL_INDEX]; if (num_channels != DEFAULT_IMAGE_CHANNELS && num_channels != MAX_IMAGE_CHANNELS) { @@ -516,524 +935,923 @@ Status ConvertColor(const std::shared_ptr &input, std::shared_ptr node; RETURN_IF_NOT_OK(GetConvertShape(convert_mode, input_cv, &node)); + + // Check if the convert mode is valid if (node.empty()) { RETURN_STATUS_UNEXPECTED( "ConvertColor: convert mode must be in ConvertMode, which mainly includes conversion " "between RGB, BGR, GRAY, RGBA etc."); } + + // Create an empty output CVTensor with the calculated shape and the same type as the input tensor TensorShape out_shape = TensorShape(node); std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); + + // Convert the color space of the input image using OpenCV's cvtColor function cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(convert_mode)); + + // Assign the output CVTensor to the output tensor pointer *output = std::static_pointer_cast(output_cv); + + // Return OK status to indicate successful conversion return Status::OK(); } catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an unexpected status with the error message RETURN_STATUS_UNEXPECTED("ConvertColor: " + std::string(e.what())); } } +// Convert the input tensor from HWC (height, width, channels) format to CHW (channels, height, width) format Status HwcToChw(std::shared_ptr input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor object std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the input CVTensor has valid data if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] HWC2CHW: load image failed."); } + + // Check if the input tensor is already in 2D format (hw dimensions) if (input_cv->Rank() == 2) { // If input tensor is 2D, we assume we have hw dimensions *output = input; return Status::OK(); } + + // Check if the rank of the input tensor is greater than CHANNEL_INDEX CHECK_FAIL_RETURN_UNEXPECTED(input_cv->shape().Size() > CHANNEL_INDEX, "HWC2CHW: rank of input data should be greater than:" + std::to_string(CHANNEL_INDEX) + ", but got:" + std::to_string(input_cv->shape().Size())); + + // Get the number of channels from the input tensor shape int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the input tensor shape has the correct size (DEFAULT_IMAGE_RANK) if (input_cv->shape().Size() != DEFAULT_IMAGE_RANK) { RETURN_STATUS_UNEXPECTED("HWC2CHW: image shape should be , but got rank: " + std::to_string(input_cv->shape().Size())); } + + // Create an empty cv::Mat object to store the output image cv::Mat output_img; - int height = input_cv->shape()[0]; - int width = input_cv->shape()[1]; +// Declare and initialize an integer variable named "height" with the value of the first dimension of the "input_cv" shape +int height = input_cv->shape()[0]; +// Declare and initialize an integer variable named "width" with the value of the second dimension of the "input_cv" shape +int width = input_cv->shape()[1]; + + // Create a shared pointer to a CVTensor object named output_cv std::shared_ptr output_cv; + + // Call the CreateEmpty function of CVTensor to create an empty tensor with the specified shape and type, + // and assign the result to output_cv. If the function fails, return the error status. RETURN_IF_NOT_OK(CVTensor::CreateEmpty(TensorShape{num_channels, height, width}, input_cv->type(), &output_cv)); + + // Iterate over each channel for (int i = 0; i < num_channels; ++i) { + + // Create a cv::Mat object named mat cv::Mat mat; + + // Call the MatAtIndex function of output_cv to get the cv::Mat object at the specified index, + // and assign the result to mat. If the function fails, return the error status. RETURN_IF_NOT_OK(output_cv->MatAtIndex({i}, &mat)); + + // Use the extractChannel function of cv::Mat to extract the i-th channel from the input_cv's mat cv::extractChannel(input_cv->mat(), mat, i); } + + // Move the ownership of output_cv to the output pointer *output = std::move(output_cv); + + // Return a success status return Status::OK(); + + // Catch any cv::Exception that might occur and return an error status with the exception message } catch (const cv::Exception &e) { RETURN_STATUS_UNEXPECTED("HWC2CHW: " + std::string(e.what())); } } -Status MaskWithTensor(const std::shared_ptr &sub_mat, std::shared_ptr *input, int x, int y, - int crop_width, int crop_height, ImageFormat image_format) { - if (image_format == ImageFormat::HWC) { - if (CheckTensorShape(*input, 2)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "input shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - if (CheckTensorShape(sub_mat, 2)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "sub_mat shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - int number_of_channels = (*input)->shape()[CHANNEL_INDEX]; - for (int i = 0; i < crop_width; i++) { - for (int j = 0; j < crop_height; j++) { - for (int c = 0; c < number_of_channels; c++) { - RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {j, i, c}, {y + j, x + i, c})); - } +// This function is used to mask a sub-matrix (sub_mat) onto a larger matrix (input) at a specified position (x, y) with a specified size (crop_width, crop_height). +// The function takes into account the image format (HWC or CHW) and performs the masking accordingly. + +// Check if the image format is HWC +if (image_format == ImageFormat::HWC) { + // Check if the shape of the input tensor is not 2 (HWC format has 3 dimensions) + if (CheckTensorShape(*input, 2)) { + // Return an error message indicating that the input shape doesn't match the expected format + RETURN_STATUS_UNEXPECTED( + "CutMixBatch: MaskWithTensor failed: " + "input shape doesn't match format, got shape:" + + (*input)->shape().ToString()); + } + // Check if the shape of the sub-matrix tensor is not 2 (HWC format has 3 dimensions) + if (CheckTensorShape(sub_mat, 2)) { + // Return an error message indicating that the sub-matrix shape doesn't match the expected format + RETURN_STATUS_UNEXPECTED( + "CutMixBatch: MaskWithTensor failed: " + "sub_mat shape doesn't match format, got shape:" + + (*input)->shape().ToString()); + } + // Get the number of channels in the input tensor + int number_of_channels = (*input)->shape()[CHANNEL_INDEX]; + // Iterate over the crop area + for (int i = 0; i < crop_width; i++) { + for (int j = 0; j < crop_height; j++) { + // Iterate over the channels + for (int c = 0; c < number_of_channels; c++) { + // Copy the value from the sub-matrix to the input tensor at the corresponding position + RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {j, i, c}, {y + j, x + i, c})); } } - } else if (image_format == ImageFormat::CHW) { - if (CheckTensorShape(*input, 0)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "input shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - if (CheckTensorShape(sub_mat, 0)) { - RETURN_STATUS_UNEXPECTED( - "CutMixBatch: MaskWithTensor failed: " - "sub_mat shape doesn't match format, got shape:" + - (*input)->shape().ToString()); - } - int number_of_channels = (*input)->shape()[0]; - for (int i = 0; i < crop_width; i++) { - for (int j = 0; j < crop_height; j++) { - for (int c = 0; c < number_of_channels; c++) { + } +} +// Check if the image format is CHW +else if (image_format == ImageFormat::CHW) { + // Check if the shape of the input tensor is not 0 (CHW format has 3 dimensions) + if (CheckTensorShape(*input, 0)) { + // Return an error message indicating that the input shape doesn't match the expected format + RETURN_STATUS_UNEXPECTED( + "CutMixBatch: MaskWithTensor failed: " + "input shape doesn't match format, got shape:" + + (*input)->shape().ToString()); + } + // Check if the shape of the sub-matrix tensor is not 0 (CHW format has 3 dimensions) + if (CheckTensorShape(sub_mat, 0)) { + // Return an error message indicating that the sub-matrix shape doesn't match the expected format + RETURN_STATUS_UNEXPECTED( + "CutMixBatch: MaskWithTensor failed: " + "sub_mat shape doesn't match format, got shape:" + + (*input)->shape().ToString()); + } + // Get the number of channels in the input tensor + int number_of_channels = (*input)->shape()[0]; + // Iterate over the crop area + for (int i = 0; i < crop_width; i++) { + for (int j = 0; j < crop_height; j++) { + // Iterate over the channels + for (int c = 0; c < number_of_channels; c++) { RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {c, j, i}, {c, y + j, x + i})); } } } } else if (image_format == ImageFormat::HW) { + // Check if the input tensor has the correct shape for format if ((*input)->Rank() != MIN_IMAGE_DIMENSION) { RETURN_STATUS_UNEXPECTED( "CutMixBatch: MaskWithTensor failed: " "input shape doesn't match format, got shape:" + (*input)->shape().ToString()); } + // Check if the sub_mat tensor has the correct shape for format if (sub_mat->Rank() != MIN_IMAGE_DIMENSION) { RETURN_STATUS_UNEXPECTED( "CutMixBatch: MaskWithTensor failed: " "sub_mat shape doesn't match format, got shape:" + (*input)->shape().ToString()); } + // Iterate over each pixel in the crop region for (int i = 0; i < crop_width; i++) { for (int j = 0; j < crop_height; j++) { + // Copy the pixel value from sub_mat to input at the corresponding position RETURN_IF_NOT_OK(CopyTensorValue(sub_mat, input, {j, i}, {y + j, x + i})); } } } else { + // If the image format is not or , return an error RETURN_STATUS_UNEXPECTED( "CutMixBatch: MaskWithTensor failed: " "image format must be , , or , got shape:" + (*input)->shape().ToString()); } + // Return OK status to indicate successful execution return Status::OK(); } +// Function to copy the value of a tensor from a source tensor to a destination tensor +// The source and destination tensors must have the same type Status CopyTensorValue(const std::shared_ptr &source_tensor, std::shared_ptr *dest_tensor, const std::vector &source_indx, const std::vector &dest_indx) { + + // Check if the types of the source and destination tensors are the same if (source_tensor->type() != (*dest_tensor)->type()) RETURN_STATUS_UNEXPECTED( "CutMixBatch: CopyTensorValue failed: " "source and destination tensor must have the same type."); + + // If the tensor type is uint8 if (source_tensor->type() == DataType::DE_UINT8) { uint8_t pixel_value = 0; - RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); - RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); - } else if (source_tensor->type() == DataType::DE_FLOAT32) { + RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); // Get the pixel value from the source tensor + RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); // Set the pixel value in the destination tensor + } + // If the tensor type is float32 + else if (source_tensor->type() == DataType::DE_FLOAT32) { float pixel_value = 0; - RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); - RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); - } else { + RETURN_IF_NOT_OK(source_tensor->GetItemAt(&pixel_value, source_indx)); // Get the pixel value from the source tensor + RETURN_IF_NOT_OK((*dest_tensor)->SetItemAt(dest_indx, pixel_value)); // Set the pixel value in the destination tensor + } + // If the tensor type is not supported (neither uint8 nor float32) + else { RETURN_STATUS_UNEXPECTED( "CutMixBatch: CopyTensorValue failed: " "Tensor type is not supported. Tensor type must be float32 or uint8."); } + + // Return OK status to indicate successful copying of tensor value return Status::OK(); } +// Function to swap the red and blue channels of an image tensor +// Takes an input tensor and a pointer to an output tensor + Status SwapRedAndBlue(std::shared_ptr input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); + + // Check if the rank of the input tensor is greater than the channel index CHECK_FAIL_RETURN_UNEXPECTED( input_cv->shape().Size() > CHANNEL_INDEX, "SwapRedAndBlue: rank of input is should greater than:" + std::to_string(CHANNEL_INDEX) + ", but got:" + std::to_string(input_cv->shape().Size())); + + // Get the number of channels from the input tensor int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the shape of the input tensor is in the format and has the default number of channels if (input_cv->shape().Size() != 3 || num_channels != DEFAULT_IMAGE_CHANNELS) { + // Return an unexpected status with an error message RETURN_STATUS_UNEXPECTED("SwapRedBlue: image shape should be in format, but got:" + input_cv->shape().ToString()); } + + // Create an empty output CVTensor with the same shape and type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_BGR2RGB)); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("SwapRedBlue: " + std::string(e.what())); + // Continue with the rest of the function... + // ... + // ... + // ... + } catch (...) { + // Handle any exceptions that occur during the execution of the function + // ... + // ... + // ... } } -Status CropAndResize(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, - int crop_height, int crop_width, int target_height, int target_width, InterpolationMode mode) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] CropAndResize: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("CropAndResize", input_cv->Rank())); - // image too large or too small, 1000 is arbitrary here to prevent opencv from segmentation fault - const uint32_t kCropShapeLimits = 1000; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_height, - "CropAndResize: crop_height out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_width, - "CropAndResize: crop_width out of bounds."); - if (crop_height == 0 || crop_width == 0 || target_height == 0 || target_height > crop_height * kCropShapeLimits || - target_width == 0 || target_width > crop_width * kCropShapeLimits) { - std::string err_msg = - "CropAndResize: the resizing width or height 1) is too big, it's up to " + std::to_string(kCropShapeLimits) + - " times the original image; 2) can not be 0. Detail info is: crop_height: " + std::to_string(crop_height) + - ", crop_width: " + std::to_string(crop_width) + ", target_height: " + std::to_string(target_height) + - ", target_width: " + std::to_string(target_width); - RETURN_STATUS_UNEXPECTED(err_msg); - } - cv::Rect roi(x, y, crop_width, crop_height); - auto cv_mode = GetCVInterpolationMode(mode); - cv::Mat cv_in = input_cv->mat(); +// Convert the color space of the input image from BGR to RGB using OpenCV's cvtColor function +cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_BGR2RGB)); +// Cast the output_cv pointer to a shared pointer of type Tensor and assign it to the output pointer +*output = std::static_pointer_cast(output_cv); + +// Return a Status object indicating that the operation was successful +return Status::OK(); + +// Catch any exceptions thrown by the cvtColor function and handle them +catch (const cv::Exception &e) { + // Return an error message with the specific exception message + RETURN_STATUS_UNEXPECTED("SwapRedBlue: " + std::string(e.what())); +} + +// Check if the input tensor is valid and load it as a CVTensor +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); +if (!input_cv->mat().data) { + // If the loaded image data is empty, return an error message + RETURN_STATUS_UNEXPECTED("[Internal ERROR] CropAndResize: load image failed."); +} + +// Validate the rank of the input tensor +RETURN_IF_NOT_OK(ValidateImageRank("CropAndResize", input_cv->Rank())); + +// Define the maximum limits for the crop shape +const uint32_t kCropShapeLimits = 1000; + +// Check if the crop height and width are within bounds +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_height, + "CropAndResize: crop_height out of bounds."); +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / kCropShapeLimits) > crop_width, + "CropAndResize: crop_width out of bounds."); + +// Check if the resizing dimensions are valid +if (crop_height == 0 || crop_width == 0 || target_height == 0 || target_height > crop_height * kCropShapeLimits || + target_width == 0 || target_width > crop_width * kCropShapeLimits) { + // Generate an error message with detailed information about the invalid dimensions + std::string err_msg = + "CropAndResize: the resizing width or height 1) is too big, it's up to " + std::to_string(kCropShapeLimits) + + " times the original image; 2) can not be 0. Detail info is: crop_height: " + std::to_string(crop_height) + + ", crop_width: " + std::to_string(crop_width) + ", target_height: " + std::to_string(target_height) + + ", target_width: " + std::to_string(target_width); + RETURN_STATUS_UNEXPECTED(err_msg); +} + +// Define the region of interest (ROI) for cropping +cv::Rect roi(x, y, crop_width, crop_height); + +// Get the OpenCV interpolation mode corresponding to the specified mode +auto cv_mode = GetCVInterpolationMode(mode); + +// Get the OpenCV matrix representation of the input CVTensor +cv::Mat cv_in = input_cv->mat(); + + // Check if the interpolation mode is set to kCubicPil if (mode == InterpolationMode::kCubicPil) { + + // Check if the input image has the correct number of channels if (input_cv->shape().Size() != DEFAULT_IMAGE_CHANNELS || input_cv->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { + + // Return an error message indicating that the PILCUBIC interpolation mode only supports images with 3 channels RETURN_STATUS_UNEXPECTED( "CropAndResize: Interpolation mode PILCUBIC only supports image with 3 channels, but got: " + input_cv->shape().ToString()); } - cv::Mat input_roi = cv_in(roi); - std::shared_ptr input_image; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_roi, input_cv->Rank(), &input_image)); - LiteMat imIn, imOut; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({target_height, target_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - imOut.Init(target_width, target_height, input_cv->shape()[2], reinterpret_cast(buffer), LDataType::UINT8); - imIn.Init(input_image->shape()[1], input_image->shape()[0], input_image->shape()[2], input_image->mat().data, - LDataType::UINT8); - if (ResizeCubic(imIn, imOut, target_width, target_height) == false) { - RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); - } - *output = output_tensor; - return Status::OK(); - } +// Create a cv::Mat object named "input_roi" by passing the "roi" to the cv_in function +cv::Mat input_roi = cv_in(roi); - TensorShape shape{target_height, target_width}; - int num_channels = input_cv->shape()[CHANNEL_INDEX]; - if (input_cv->Rank() == DEFAULT_IMAGE_RANK) { - shape = shape.AppendDim(num_channels); - } - std::shared_ptr cvt_out; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &cvt_out)); - cv::resize(cv_in(roi), cvt_out->mat(), cv::Size(target_width, target_height), 0, 0, cv_mode); - *output = std::static_pointer_cast(cvt_out); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("CropAndResize: " + std::string(e.what())); - } +// Create a shared pointer to a CVTensor object named "input_image" +std::shared_ptr input_image; + +// Create a CVTensor object named "input_image" by calling the CreateFromMat function and passing the "input_roi", the rank of "input_cv", and the address of "input_image" +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_roi, input_cv->Rank(), &input_image)); + +// Create LiteMat objects named "imIn" and "imOut" +LiteMat imIn, imOut; + +// Create a shared pointer to a Tensor object named "output_tensor" +std::shared_ptr output_tensor; + +// Create a TensorShape object named "new_shape" with dimensions {target_height, target_width, 3} +TensorShape new_shape = TensorShape({target_height, target_width, 3}); + +// Create an empty Tensor object named "output_tensor" with the shape "new_shape", the type of "input_cv", and assign it to the "output_tensor" shared pointer +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input_cv->type(), &output_tensor)); + +// Get a pointer to the data of "output_tensor" and cast it to a uint8_t pointer named "buffer" +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Initialize the "imOut" LiteMat object with the target width, target height, number of channels from "input_cv", the address of "buffer", and the data type UINT8 +imOut.Init(target_width, target_height, input_cv->shape()[2], reinterpret_cast(buffer), LDataType::UINT8); + +// Initialize the "imIn" LiteMat object with the width, height, number of channels from "input_image", the data pointer from "input_image", and the data type UINT8 +imIn.Init(input_image->shape()[1], input_image->shape()[0], input_image->shape()[2], input_image->mat().data, LDataType::UINT8); + +// Check if the cubic resize operation from "imIn" to "imOut" with the target width and target height is successful +if (ResizeCubic(imIn, imOut, target_width, target_height) == false) { + // If the resize operation fails, return an unexpected status with an error message + RETURN_STATUS_UNEXPECTED("Resize: failed to do resize, please check the error msg."); } -Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, std::vector center, - float degree, InterpolationMode interpolation, bool expand, uint8_t fill_r, uint8_t fill_g, - uint8_t fill_b) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rotate: load image failed."); - } - RETURN_IF_NOT_OK(ValidateImageRank("Rotate", input_cv->Rank())); +// Assign the "output_tensor" to the "output" pointer +*output = output_tensor; +// Return a status indicating successful execution of the function +return Status::OK(); + +// Create a TensorShape object with dimensions target_height and target_width +TensorShape shape{target_height, target_width}; + +// Get the number of channels from the input_cv tensor +int num_channels = input_cv->shape()[CHANNEL_INDEX]; + +// Check if the rank of the input_cv tensor is equal to DEFAULT_IMAGE_RANK +if (input_cv->Rank() == DEFAULT_IMAGE_RANK) { + // If so, append the number of channels to the shape object + shape = shape.AppendDim(num_channels); +} + +// Create a shared pointer to a CVTensor object +std::shared_ptr cvt_out; + +// Create an empty CVTensor with the specified shape and data type of the input_cv tensor +RETURN_IF_NOT_OK(CVTensor::CreateEmpty(shape, input_cv->type(), &cvt_out)); + +// Resize the region of interest (roi) in the input_cv tensor to the specified target width and height using the specified cv_mode +cv::resize(cv_in(roi), cvt_out->mat(), cv::Size(target_width, target_height), 0, 0, cv_mode); + +// Assign the CVTensor to the output tensor +*output = std::static_pointer_cast(cvt_out); + +// Return a Status object indicating successful execution +return Status::OK(); + +// Catch any cv::Exception that may occur during the execution of the code +} catch (const cv::Exception &e) { + // Return an error status with a descriptive error message + RETURN_STATUS_UNEXPECTED("CropAndResize: " + std::string(e.what())); +} +} + +// The Rotate function takes in an input tensor, performs a rotation operation on it, and returns the result in the output tensor. +// It also takes in additional parameters such as the center of rotation, the degree of rotation, the interpolation mode, whether to expand the image, and the fill color. + +// The function starts by converting the input tensor to a CVTensor (OpenCV tensor) using the AsCVTensor function. +// This allows us to access the underlying OpenCV matrix representation of the tensor. +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + +// Check if the conversion was successful by checking if the data pointer of the OpenCV matrix is valid. +// If it is not valid, it means that the image failed to load. +if (!input_cv->mat().data) { + // Return an error status with a descriptive error message. + RETURN_STATUS_UNEXPECTED("[Internal ERROR] Rotate: load image failed."); +} + +// Validate the rank of the input tensor using the ValidateImageRank function. +// This function checks if the rank of the tensor is compatible with an image tensor. +// If the rank is not valid, it returns an error status. +RETURN_IF_NOT_OK(ValidateImageRank("Rotate", input_cv->Rank())); + +// Continue with the rest of the function... + + // Convert the input CVTensor to a cv::Mat cv::Mat input_img = input_cv->mat(); + + // Check if the image dimensions are too large if (input_img.cols > (MAX_INT_PRECISION * 2) || input_img.rows > (MAX_INT_PRECISION * 2)) { + // Return an error message if the image is too large RETURN_STATUS_UNEXPECTED("Rotate: image is too large and center is not precise, got image width:" + std::to_string(input_img.cols) + ", and image height:" + std::to_string(input_img.rows) + ", both should be small than:" + std::to_string(MAX_INT_PRECISION * 2)); } + float fx = 0, fy = 0; if (center.empty()) { - // default to center of image + // If the center is not provided, default to the center of the image fx = (input_img.cols - 1) / 2.0; fy = (input_img.rows - 1) / 2.0; } else { + // Otherwise, use the provided center coordinates fx = center[0]; fy = center[1]; } + + // Create an output image cv::Mat output_img; + + // Create a scalar for the fill color cv::Scalar fill_color = cv::Scalar(fill_b, fill_g, fill_r); - // maybe don't use uint32 for image dimension here + + // Create a Point2f object for the center coordinates cv::Point2f pc(fx, fy); + + // Create a rotation matrix using the center coordinates and the rotation degree cv::Mat rot = cv::getRotationMatrix2D(pc, degree, 1.0); + + // Create a shared pointer for the output CVTensor std::shared_ptr output_cv; + if (!expand) { - // this case means that the shape doesn't change, size stays the same - // We may not need this memcpy if it is in place. + // If expand is false, the shape of the image remains the same + // Create an empty CVTensor with the same shape and type as the input CVTensor RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); - // using inter_nearest to comply with python default + + // Perform the affine transformation on the input image using the rotation matrix + // using inter_nearest interpolation and constant border mode cv::warpAffine(input_img, output_cv->mat(), rot, input_img.size(), GetCVInterpolationMode(interpolation), cv::BORDER_CONSTANT, fill_color); } else { - // we resize here since the shape changes - // create a new bounding box with the rotate + // If expand is true, the shape of the image changes + // Create a bounding box with the rotated image cv::Rect2f bbox = cv::RotatedRect(pc, input_img.size(), degree).boundingRect2f(); + + // Adjust the rotation matrix to align the center of the bounding box with the center of the input image rot.at(0, 2) += bbox.width / 2.0 - input_img.cols / 2.0; rot.at(1, 2) += bbox.height / 2.0 - input_img.rows / 2.0; - // use memcpy and don't compute the new shape since openCV has a rounding problem + + // Perform the affine transformation on the input image using the adjusted rotation matrix + // using the bounding box size as the output size, inter_nearest interpolation, and constant border mode cv::warpAffine(input_img, output_img, rot, bbox.size(), GetCVInterpolationMode(interpolation), cv::BORDER_CONSTANT, fill_color); + + // Create a CVTensor from the output image with the same rank as the input CVTensor RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_img, input_cv->Rank(), &output_cv)); RETURN_UNEXPECTED_IF_NULL(output_cv); } - *output = std::static_pointer_cast(output_cv); - } catch (const cv::Exception &e) { +// Cast the output_cv variable to a shared pointer of type Tensor and assign it to the output variable +*output = std::static_pointer_cast(output_cv); + +// Catch any exceptions that may occur during the rotation process +try { + // If an exception occurs, throw a custom error message with the details of the exception RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); - } - return Status::OK(); } +// Return a Status object indicating that the rotation operation was successful +return Status::OK(); + +// A template function that normalizes the input tensor using the provided mean and standard deviation vectors +// The function takes in a shared pointer to the input tensor, a pointer to the output tensor, and the mean and standard deviation vectors template void Normalize(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, std::vector std) { + + // Create iterators for the output tensor and the input tensor auto itr_out = (*output)->begin(); auto itr = input->begin(); + + // Get the end iterator for the input tensor auto end = input->end(); + + // Get the number of channels in the output tensor int64_t num_channels = (*output)->shape()[CHANNEL_INDEX]; + + // Loop through the elements of the iterator until it reaches the end while (itr != end) { + + // Loop through the number of channels for (int64_t i = 0; i < num_channels; i++) { + + // Perform the following operations: + // 1. Convert the value pointed to by itr to a float + // 2. Divide the converted value by std[i] + // 3. Subtract mean[i] from the result *itr_out = static_cast(*itr) / std[i] - mean[i]; + + // Move the output iterator to the next position ++itr_out; + + // Move the input iterator to the next position ++itr; } } } +// Normalize function to normalize the input tensor using mean and standard deviation +// Takes input tensor, output tensor pointer, mean vector, and standard deviation vector as parameters + Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, std::vector mean, std::vector std) { + + // Create an empty output tensor with the same shape as the input tensor and data type float32 RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), DataType(DataType::DE_FLOAT32), output)); + + // Check if the rank of the input tensor is equal to the minimum image dimension if (input->Rank() == MIN_IMAGE_DIMENSION) { + + // Expand the dimensions of the output tensor to match the minimum image dimension RETURN_IF_NOT_OK((*output)->ExpandDim(MIN_IMAGE_DIMENSION)); } + // ... rest of the code +} - CHECK_FAIL_RETURN_UNEXPECTED((*output)->Rank() == DEFAULT_IMAGE_RANK, - "Normalize: output image rank should be:" + std::to_string(DEFAULT_IMAGE_RANK) + - ", but got:" + std::to_string((*output)->Rank())); - CHECK_FAIL_RETURN_UNEXPECTED(std.size() == mean.size(), - "Normalize: mean and std vectors are not of same size, got size of std:" + - std::to_string(std.size()) + ", and mean size:" + std::to_string(mean.size())); +// Check if the rank of the output image is equal to the default image rank +CHECK_FAIL_RETURN_UNEXPECTED((*output)->Rank() == DEFAULT_IMAGE_RANK, + "Normalize: output image rank should be:" + std::to_string(DEFAULT_IMAGE_RANK) + + ", but got:" + std::to_string((*output)->Rank())); - // caller provided 1 mean/std value and there are more than one channel --> duplicate mean/std value +// Check if the size of the std vector is equal to the size of the mean vector +CHECK_FAIL_RETURN_UNEXPECTED(std.size() == mean.size(), + "Normalize: mean and std vectors are not of same size, got size of std:" + + std::to_string(std.size()) + ", and mean size:" + std::to_string(mean.size())); + +// Check if the size of the mean vector is 1 and the number of channels in the output tensor is greater than 1 if (mean.size() == 1 && (*output)->shape()[CHANNEL_INDEX] != 1) { + // If so, duplicate the mean and std values for each channel for (int64_t i = 0; i < (*output)->shape()[CHANNEL_INDEX] - 1; i++) { mean.push_back(mean[0]); std.push_back(std[0]); } } + + // Check if the number of channels in the output tensor matches the size of the mean and std vectors CHECK_FAIL_RETURN_UNEXPECTED((*output)->shape()[CHANNEL_INDEX] == mean.size(), "Normalize: number of channels does not match the size of mean and std vectors, got " "channels: " + std::to_string((*output)->shape()[CHANNEL_INDEX]) + ", size of mean:" + std::to_string(mean.size())); - switch (input->type().value()) { - case DataType::DE_BOOL: - Normalize(input, output, mean, std); - break; - case DataType::DE_INT8: - Normalize(input, output, mean, std); - break; - case DataType::DE_UINT8: - Normalize(input, output, mean, std); - break; - case DataType::DE_INT16: - Normalize(input, output, mean, std); - break; - case DataType::DE_UINT16: - Normalize(input, output, mean, std); - break; - case DataType::DE_INT32: - Normalize(input, output, mean, std); - break; - case DataType::DE_UINT32: - Normalize(input, output, mean, std); - break; - case DataType::DE_INT64: - Normalize(input, output, mean, std); - break; - case DataType::DE_UINT64: - Normalize(input, output, mean, std); - break; - case DataType::DE_FLOAT16: - Normalize(input, output, mean, std); - break; - case DataType::DE_FLOAT32: - Normalize(input, output, mean, std); - break; - case DataType::DE_FLOAT64: - Normalize(input, output, mean, std); - break; - default: - RETURN_STATUS_UNEXPECTED( - "Normalize: unsupported type, currently supported types include " - "[bool,int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t,float16,float,double]."); - } +// Switch statement to handle different data types +switch (input->type().value()) { - if (input->Rank() == MIN_IMAGE_DIMENSION) { - (*output)->Squeeze(); - } - return Status::OK(); + // If the input data type is bool + case DataType::DE_BOOL: + Normalize(input, output, mean, std); + break; + + // If the input data type is int8_t + case DataType::DE_INT8: + Normalize(input, output, mean, std); + break; + + // If the input data type is uint8_t + case DataType::DE_UINT8: + Normalize(input, output, mean, std); + break; + + // If the input data type is int16_t + case DataType::DE_INT16: + Normalize(input, output, mean, std); + break; + + // If the input data type is uint16_t + case DataType::DE_UINT16: + Normalize(input, output, mean, std); + break; + + // If the input data type is int32_t + case DataType::DE_INT32: + Normalize(input, output, mean, std); + break; + + // If the input data type is uint32_t + case DataType::DE_UINT32: + Normalize(input, output, mean, std); + break; + + // If the input data type is int64_t + case DataType::DE_INT64: + Normalize(input, output, mean, std); + break; + + // If the input data type is uint64_t + case DataType::DE_UINT64: + Normalize(input, output, mean, std); + break; + + // If the input data type is float16 + case DataType::DE_FLOAT16: + Normalize(input, output, mean, std); + break; + + // If the input data type is float + case DataType::DE_FLOAT32: + Normalize(input, output, mean, std); + break; + + // If the input data type is double + case DataType::DE_FLOAT64: + Normalize(input, output, mean, std); + break; + + // If the input data type is not supported + default: + RETURN_STATUS_UNEXPECTED("Normalize: unsupported type, currently supported types include " + // Add the list of supported types here + "bool, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float16, float, double"); +} +// Include the standard input-output header for C (primarily for printf, but we aren't using printf here) +#include + +// The main function, entry point of the program +int main(){ + + // Use the standard C++ output stream to print the given string + std::cout << "[bool,int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,int64_t,uint64_t,float16,float,double]." << std::endl; + + // Return 0 to indicate successful program termination + return 0; } -Status NormalizePad(const std::shared_ptr &input, std::shared_ptr *output, - const std::shared_ptr &mean, const std::shared_ptr &std, const std::string &dtype) { + // Check if the rank of the input is equal to MIN_IMAGE_DIMENSION + if (input->Rank() == MIN_IMAGE_DIMENSION) { + // If it is, squeeze the output tensor to remove any dimensions of size 1 + (*output)->Squeeze(); + } + + // Return a status indicating that the operation was successful + return Status::OK(); + +// Check if the input tensor is a CVTensor and if it has valid data and the correct number of channels std::shared_ptr input_cv = CVTensor::AsCVTensor(input); if (!(input_cv->mat().data && input_cv->Rank() == DEFAULT_IMAGE_CHANNELS)) { + // If the input tensor is not valid, return an error status RETURN_STATUS_UNEXPECTED("[Internal ERROR] NormalizePad: load image failed."); } + + // Set the default data type and compute type to float32 DataType tensor_type = DataType(DataType::DE_FLOAT32); int compute_type = CV_32F; int channel_type = CV_32FC1; + + // If the desired data type is float16, update the compute type and channel type accordingly if (dtype == "float16") { compute_type = CV_16F; channel_type = CV_16FC1; tensor_type = DataType(DataType::DE_FLOAT16); } + + // Get the input image as a cv::Mat cv::Mat in_image = input_cv->mat(); + + // Create an empty CVTensor with a new shape that has one additional channel std::shared_ptr output_cv; TensorShape new_shape({input_cv->shape()[0], input_cv->shape()[1], input_cv->shape()[2] + 1}); RETURN_IF_NOT_OK(CVTensor::CreateEmpty(new_shape, tensor_type, &output_cv)); + + // Squeeze the mean tensor to remove any dimensions of size 1 mean->Squeeze(); + + // Check if the mean tensor has the correct shape and data type if (mean->type() != DataType::DE_FLOAT32 || mean->Rank() != 1 || mean->shape()[0] != DEFAULT_IMAGE_CHANNELS) { std::string err_msg = "NormalizePad: mean tensor should be of size 3 and type float, but got rank: " + std::to_string(mean->Rank()) + ", and type: " + mean->type().ToString(); return Status(StatusCode::kMDShapeMisMatch, err_msg); } + + // Squeeze the std tensor to remove any dimensions of size 1 std->Squeeze(); + + // Check if the std tensor has the correct shape and data type if (std->type() != DataType::DE_FLOAT32 || std->Rank() != 1 || std->shape()[0] != DEFAULT_IMAGE_CHANNELS) { std::string err_msg = "NormalizePad: std tensor should be of size 3 and type float, but got rank: " + std::to_string(std->Rank()) + ", and type: " + std->type().ToString(); return Status(StatusCode::kMDShapeMisMatch, err_msg); } - try { - // NOTE: We are assuming the input image is in RGB and the mean - // and std are in RGB - std::vector rgb; - cv::split(in_image, rgb); - if (rgb.size() != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("NormalizePad: input image is not in RGB, got rank: " + std::to_string(in_image.dims)); - } - for (int8_t i = 0; i < DEFAULT_IMAGE_CHANNELS; i++) { - float mean_c, std_c; - RETURN_IF_NOT_OK(mean->GetItemAt(&mean_c, {i})); - RETURN_IF_NOT_OK(std->GetItemAt(&std_c, {i})); - rgb[i].convertTo(rgb[i], compute_type, 1.0 / std_c, (-mean_c / std_c)); - } - rgb.push_back(cv::Mat::zeros(in_image.rows, in_image.cols, channel_type)); - cv::merge(rgb, output_cv->mat()); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("NormalizePad: " + std::string(e.what())); + + // Split the input image into RGB channels + std::vector rgb; + cv::split(in_image, rgb); + + // Check if the input image has the correct number of channels + if (rgb.size() != DEFAULT_IMAGE_CHANNELS) { + RETURN_STATUS_UNEXPECTED("NormalizePad: input image is not in RGB, got rank: " + std::to_string(in_image.dims)); } + for (int8_t i = 0; i < DEFAULT_IMAGE_CHANNELS; i++) { + // Declare variables to store the mean and standard deviation for each channel + float mean_c, std_c; + + // Get the mean value for the current channel and store it in mean_c + RETURN_IF_NOT_OK(mean->GetItemAt(&mean_c, {i})); + + // Get the standard deviation value for the current channel and store it in std_c + RETURN_IF_NOT_OK(std->GetItemAt(&std_c, {i})); + + // Convert the current channel of the rgb image to the specified compute_type + // Scale the pixel values by 1.0 / std_c and subtract mean_c / std_c + rgb[i].convertTo(rgb[i], compute_type, 1.0 / std_c, (-mean_c / std_c)); + } + + // Create a new channel with all pixel values set to zero and add it to the rgb vector + rgb.push_back(cv::Mat::zeros(in_image.rows, in_image.cols, channel_type)); + + // Merge the channels of the rgb image into a single output image + cv::merge(rgb, output_cv->mat()); + + // Convert the output_cv image to a Tensor object and assign it to the output pointer + *output = std::static_pointer_cast(output_cv); + + // Return a Status object indicating successful execution of the function + return Status::OK(); + +} catch (const cv::Exception &e) { + // If an exception is caught during the execution of the function, return an error Status object + RETURN_STATUS_UNEXPECTED("NormalizePad: " + std::string(e.what())); } +// Function to adjust the brightness of an image Status AdjustBrightness(const std::shared_ptr &input, std::shared_ptr *output, const float &alpha) { try { + // Convert the input Tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the underlying cv::Mat from the CVTensor cv::Mat input_img = input_cv->mat(); + + // Check if the image data was loaded successfully if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustBrightness: load image failed."); } + + // Check if the image rank is greater than the channel index CHECK_FAIL_RETURN_UNEXPECTED( input_cv->shape().Size() > CHANNEL_INDEX, - "AdjustBrightness: image rank should not bigger than:" + std::to_string(CHANNEL_INDEX) + + "AdjustBrightness: image rank should not be bigger than:" + std::to_string(CHANNEL_INDEX) + ", but got: " + std::to_string(input_cv->shape().Size())); + + // Get the number of channels in the image int num_channels = input_cv->shape()[CHANNEL_INDEX]; - // Rank of the image represents how many dimensions, image is expected to be HWC + + // Check if the image rank and number of channels are as expected if (input_cv->Rank() != DEFAULT_IMAGE_RANK || num_channels != DEFAULT_IMAGE_CHANNELS) { RETURN_STATUS_UNEXPECTED("AdjustBrightness: image shape is not or channel is not 3, got image rank: " + std::to_string(input_cv->Rank()) + ", and channel:" + std::to_string(num_channels)); } + + // Create an empty CVTensor with the same shape and type as the input CVTensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Adjust the brightness of the image by multiplying it with the alpha value output_cv->mat() = input_img * alpha; + + // Convert the output CVTensor back to a generic Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); } catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an unexpected status with the error message RETURN_STATUS_UNEXPECTED("AdjustBrightness: " + std::string(e.what())); } + + // Return a status indicating successful execution return Status::OK(); } +// Function to adjust the contrast of an image using OpenCV library Status AdjustContrast(const std::shared_ptr &input, std::shared_ptr *output, const float &alpha) { try { + // Convert the input tensor to a CVTensor object std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the underlying OpenCV matrix from the CVTensor object cv::Mat input_img = input_cv->mat(); + + // Check if the input image data is valid if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustContrast: load image failed."); } + + // Check if the input image rank is greater than CHANNEL_INDEX CHECK_FAIL_RETURN_UNEXPECTED(input_cv->shape().Size() > CHANNEL_INDEX, - "AdjustContrast: image rank should bigger than:" + std::to_string(CHANNEL_INDEX) + + "AdjustContrast: image rank should be bigger than:" + std::to_string(CHANNEL_INDEX) + ", but got: " + std::to_string(input_cv->shape().Size())); + + // Get the number of channels in the input image int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the input image shape is and the number of channels is 3 if (input_cv->Rank() != DEFAULT_IMAGE_CHANNELS || num_channels != DEFAULT_IMAGE_CHANNELS) { RETURN_STATUS_UNEXPECTED("AdjustContrast: image shape is not or channel is not 3, got image rank: " + std::to_string(input_cv->Rank()) + ", and channel:" + std::to_string(num_channels)); } + + // Create a grayscale image and an output image cv::Mat gray, output_img; + + // Convert the input image to grayscale cv::cvtColor(input_img, gray, CV_RGB2GRAY); + + // Calculate the mean intensity of the grayscale image int mean_img = static_cast(cv::mean(gray).val[0] + 0.5); + + // Create an empty CVTensor object for the output image std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Create a black image with the same size as the input image output_img = cv::Mat::zeros(input_img.rows, input_img.cols, CV_8UC1); + + // Add the mean intensity to the black image output_img = output_img + mean_img; + + // Convert the black image to RGB format cv::cvtColor(output_img, output_img, CV_GRAY2RGB); + + // Adjust the contrast of the output image using the alpha value output_cv->mat() = output_img * (1.0 - alpha) + input_img * alpha; + + // Assign the output CVTensor object to the output tensor pointer *output = std::static_pointer_cast(output_cv); } catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an error status RETURN_STATUS_UNEXPECTED("AdjustContrast: " + std::string(e.what())); } + + // Return a success status return Status::OK(); } +// This function adjusts the gamma of an input image tensor and stores the result in the output tensor +// The gamma adjustment is performed on each pixel of the image +// The gamma adjustment formula is: pixel_value = (pixel_value * gain) ^ gamma +// The pixel values are then clamped between 0 and 1 + Status AdjustGamma(const std::shared_ptr &input, std::shared_ptr *output, const float &gamma, const float &gain) { try { int num_channels = 1; + + // Check if the input tensor has the minimum required dimensions if (input->Rank() < MIN_IMAGE_DIMENSION) { RETURN_STATUS_UNEXPECTED("AdjustGamma: input tensor is not in shape of <...,H,W,C> or , got shape:" + input->shape().ToString()); } + + // If the input tensor has more than 2 dimensions, get the number of channels from the last dimension if (input->Rank() > 2) { num_channels = input->shape()[-1]; } + + // Check if the number of channels is either 1 or 3 if (num_channels != 1 && num_channels != 3) { RETURN_STATUS_UNEXPECTED("AdjustGamma: channel of input image should be 1 or 3, but got: " + std::to_string(num_channels)); } + + // If the input tensor is of type float, perform gamma adjustment directly on the tensor if (input->type().IsFloat()) { for (auto itr = input->begin(); itr != input->end(); itr++) { *itr = pow((*itr) * gain, gamma); @@ -1041,103 +1859,167 @@ Status AdjustGamma(const std::shared_ptr &input, std::shared_ptr } *output = input; } else { + // If the input tensor is not of type float, convert it to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the conversion to CVTensor was successful if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustGamma: load image failed."); } + + // Get the OpenCV Mat object from the CVTensor cv::Mat input_img = input_cv->mat(); + + // Create an empty CVTensor for the output std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Create a lookup table (LUT) for gamma adjustment uchar LUT[256] = {}; for (int i = 0; i < 256; i++) { float f = i / 255.0; f = pow(f, gamma); LUT[i] = static_cast(floor(std::min(f * (255.0 + 1 - 1e-3) * gain, 255.0))); } + + // Apply the gamma adjustment using the LUT on each pixel of the input image if (input_img.channels() == 1) { cv::MatIterator_ it = input_img.begin(); cv::MatIterator_ it_end = input_img.end(); for (; it != it_end; ++it) { *it = LUT[(*it)]; } + } } else { + // Create iterators to iterate over each pixel in the input image cv::MatIterator_ it = input_img.begin(); cv::MatIterator_ it_end = input_img.end(); + + // Iterate over each pixel in the input image for (; it != it_end; ++it) { + // Apply the lookup table to each channel of the pixel (*it)[0] = LUT[(*it)[0]]; (*it)[1] = LUT[(*it)[1]]; (*it)[2] = LUT[(*it)[2]]; } } + + // Multiply the input image by 1 and assign it to the output_cv matrix output_cv->mat() = input_img * 1; + + // Cast the output_cv matrix to a Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); } } catch (const cv::Exception &e) { + // If an exception occurs during the adjustment of gamma, return an error status with the exception message RETURN_STATUS_UNEXPECTED("AdjustGamma: " + std::string(e.what())); } + + // Return a success status return Status::OK(); } -Status AutoContrast(const std::shared_ptr &input, std::shared_ptr *output, const float &cutoff, - const std::vector &ignore) { - try { +// The function AutoContrast performs automatic contrast adjustment on an input image tensor. +// It takes the following parameters: +// - input: a shared pointer to the input image tensor +// - output: a pointer to a shared pointer that will store the output image tensor +// - cutoff: a float value representing the cutoff percentage for contrast adjustment +// - ignore: a vector of uint32_t values representing the indices of channels to ignore during contrast adjustment + +// Start of the try block +try { + // Convert the input tensor to a CVTensor object std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the conversion was successful if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] AutoContrast: load image failed."); + // If the conversion failed, return an error message + RETURN_STATUS_UNEXPECTED("[Internal ERROR] AutoContrast: load image failed."); } + + // Check the rank of the input tensor if (input_cv->Rank() != DEFAULT_IMAGE_RANK && input_cv->Rank() != MIN_IMAGE_DIMENSION) { - std::string err_msg = "AutoContrast: image rank should be 2 or 3, but got: " + std::to_string(input_cv->Rank()); - if (input_cv->Rank() == 1) { - err_msg = err_msg + ", may need to do Decode operation first."; - } - RETURN_STATUS_UNEXPECTED("AutoContrast: image rank should be 2 or 3, but got: " + - std::to_string(input_cv->Rank())); + // If the rank is not 2 or 3, return an error message + std::string err_msg = "AutoContrast: image rank should be 2 or 3, but got: " + std::to_string(input_cv->Rank()); + if (input_cv->Rank() == 1) { + err_msg = err_msg + ", may need to do Decode operation first."; + } + RETURN_STATUS_UNEXPECTED(err_msg); } - // Reshape to extend dimension if rank is 2 for algorithm to work. then reshape output to be of rank 2 like input + + // Reshape the input tensor if its rank is 2 if (input_cv->Rank() == MIN_IMAGE_DIMENSION) { - RETURN_IF_NOT_OK(input_cv->ExpandDim(MIN_IMAGE_DIMENSION)); + RETURN_IF_NOT_OK(input_cv->ExpandDim(MIN_IMAGE_DIMENSION)); } - // Get number of channels and image matrix + + // Get the number of channels and the image matrix std::size_t num_of_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check the number of channels if (num_of_channels != MIN_IMAGE_CHANNELS && num_of_channels != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("AutoContrast: channel of input image should be 1 or 3, but got: " + - std::to_string(num_of_channels)); + // If the number of channels is not 1 or 3, return an error message + RETURN_STATUS_UNEXPECTED("AutoContrast: channel of input image should be 1 or 3, but got: " + + std::to_string(num_of_channels)); } + + // Get the image matrix cv::Mat image = input_cv->mat(); - // Separate the image to channels + + // Separate the image into channels std::vector planes(num_of_channels); cv::split(image, planes); + + // Create histograms for each channel cv::Mat b_hist, g_hist, r_hist; - // Establish the number of bins and set variables for histogram + + // Set up variables for histogram calculation int32_t hist_size = 256; int32_t channels = 0; float range[] = {0, 256}; const float *hist_range[] = {range}; bool uniform = true, accumulate = false; - // Set up lookup table for LUT(Look up table algorithm) + + // Set up lookup table for LUT (Look up table algorithm) std::vector table; std::vector image_result; + + // Loop through each channel of the image for (std::size_t layer = 0; layer < planes.size(); layer++) { + // Continue with the implementation of the function... + } +} // End of the try block // Reset lookup table table = std::vector{}; + // Calculate Histogram for channel cv::Mat hist; cv::calcHist(&planes[layer], 1, &channels, cv::Mat(), hist, 1, &hist_size, hist_range, uniform, accumulate); hist.convertTo(hist, CV_32SC1); std::vector hist_vec; hist.col(0).copyTo(hist_vec); + // Ignore values in ignore - for (const auto &item : ignore) hist_vec[item] = 0; + for (const auto &item : ignore) { + hist_vec[item] = 0; + } + int32_t hi = 255; int32_t lo = 0; + + // Compute upper and lower percentiles RETURN_IF_NOT_OK(ComputeUpperAndLowerPercentiles(&hist_vec, cutoff, cutoff, &hi, &lo)); + if (hi <= lo) { + // If hi is less than or equal to lo, set the table to a linear mapping from 0 to 255 for (int32_t i = 0; i < 256; i++) { table.push_back(i); } } else { + // Otherwise, compute the scale and offset for the table const float scale = 255.0 / (hi - lo); const float offset = -1 * lo * scale; + + // Generate the lookup table for (int32_t i = 0; i < 256; i++) { int32_t ix = static_cast(i * scale + offset); ix = std::max(ix, 0); @@ -1145,116 +2027,208 @@ Status AutoContrast(const std::shared_ptr &input, std::shared_ptrmat().type()); + + // Create a CVTensor from the result and reshape it to match the input shape std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); (*output) = std::static_pointer_cast(output_cv); RETURN_IF_NOT_OK((*output)->Reshape(input_cv->shape())); } catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an error message RETURN_STATUS_UNEXPECTED("AutoContrast: " + std::string(e.what())); } + } + + // Return the OK status to indicate successful program termination return Status::OK(); } +// Function to adjust the saturation of an image Status AdjustSaturation(const std::shared_ptr &input, std::shared_ptr *output, const float &alpha) { try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the OpenCV matrix representation of the input image cv::Mat input_img = input_cv->mat(); + + // Check if the input image data is valid if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustSaturation: load image failed."); } + + // Check if the input image rank is greater than the channel index CHECK_FAIL_RETURN_UNEXPECTED( input_cv->shape().Size() > CHANNEL_INDEX, - "AdjustSaturation: image rank should not bigger than: " + std::to_string(CHANNEL_INDEX) + + "AdjustSaturation: image rank should not be greater than: " + std::to_string(CHANNEL_INDEX) + ", but got: " + std::to_string(input_cv->shape().Size())); + + // Get the number of channels in the input image int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the input image rank and number of channels are as expected if (input_cv->Rank() != DEFAULT_IMAGE_RANK || num_channels != DEFAULT_IMAGE_CHANNELS) { RETURN_STATUS_UNEXPECTED("AdjustSaturation: image shape is not or channel is not 3, but got rank: " + std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(num_channels)); } + + // Create an empty CVTensor with the same shape and type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Get the OpenCV matrix representation of the output image cv::Mat output_img = output_cv->mat(); + + // Convert the input image to grayscale cv::Mat gray; cv::cvtColor(input_img, gray, CV_RGB2GRAY); + + // Convert the grayscale image back to RGB cv::cvtColor(gray, output_img, CV_GRAY2RGB); + + // Adjust the saturation of the output image using the alpha value output_cv->mat() = output_img * (1.0 - alpha) + input_img * alpha; + + // Set the output tensor to the CVTensor *output = std::static_pointer_cast(output_cv); } catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an error status RETURN_STATUS_UNEXPECTED("AdjustSaturation: " + std::string(e.what())); } + + // Return a success status return Status::OK(); } +// Function to adjust the hue of an image Status AdjustHue(const std::shared_ptr &input, std::shared_ptr *output, const float &hue) { + + // Check if the hue value is within the valid range of [-0.5, 0.5] if (hue > 0.5 || hue < -0.5) { - RETURN_STATUS_UNEXPECTED("AdjustHue: invalid parameter, hue should within [-0.5, 0.5], but got: " + + // Return an error status with a descriptive message if the hue value is invalid + RETURN_STATUS_UNEXPECTED("AdjustHue: invalid parameter, hue should be within [-0.5, 0.5], but got: " + std::to_string(hue)); } + try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the OpenCV matrix representation of the input image cv::Mat input_img = input_cv->mat(); + + // Check if the image data was loaded successfully if (!input_cv->mat().data) { + // Return an error status if the image loading failed RETURN_STATUS_UNEXPECTED("[Internal ERROR] AdjustHue: load image failed."); } + + // Check if the image rank is greater than 2 (i.e., it has more than 2 dimensions) CHECK_FAIL_RETURN_UNEXPECTED(input_cv->shape().Size() > 2, - "AdjustHue: image rank should not bigger than:" + std::to_string(2) + + "AdjustHue: image rank should not be bigger than " + std::to_string(2) + ", but got: " + std::to_string(input_cv->shape().Size())); + + // Get the number of channels in the image int num_channels = input_cv->shape()[2]; + + // Check if the image rank and number of channels are as expected if (input_cv->Rank() != DEFAULT_IMAGE_RANK || num_channels != DEFAULT_IMAGE_CHANNELS) { + // Return an error status if the image shape is not or if the number of channels is not 3 RETURN_STATUS_UNEXPECTED("AdjustHue: image shape is not or channel is not 3, but got rank: " + std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(num_channels)); } + + // Create an empty CVTensor with the same shape and type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Get the OpenCV matrix representation of the output image cv::Mat output_img; + + // Convert the input image from RGB to HSV color space cv::cvtColor(input_img, output_img, CV_RGB2HSV_FULL); + + // Iterate over each pixel in the output image for (int y = 0; y < output_img.cols; y++) { for (int x = 0; x < output_img.rows; x++) { + // Get the current hue value of the pixel uint8_t cur1 = output_img.at(cv::Point(y, x))[0]; + + // Calculate the adjusted hue value based on the input hue value uint8_t h_hue = 0; h_hue = static_cast(hue * MAX_BIT_VALUE); + + // Add the adjusted hue value to the current hue value cur1 += h_hue; + + // Update the hue value of the pixel in the output image output_img.at(cv::Point(y, x))[0] = cur1; } } + + // Convert the output image from HSV back to RGB color space cv::cvtColor(output_img, output_cv->mat(), CV_HSV2RGB_FULL); + + // Assign the output CVTensor to the output pointer *output = std::static_pointer_cast(output_cv); + } catch (const cv::Exception &e) { + // Return an error status with the exception message if any OpenCV exception occurs RETURN_STATUS_UNEXPECTED("AdjustHue: " + std::string(e.what())); } + + // Return a success status return Status::OK(); } +// Function to equalize the histogram of an input image tensor Status Equalize(const std::shared_ptr &input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the image data was loaded successfully if (!input_cv->mat().data) { RETURN_STATUS_UNEXPECTED("[Internal ERROR] Equalize: load image failed."); } + + // Check the rank of the input image tensor if (input_cv->Rank() != DEFAULT_IMAGE_RANK && input_cv->Rank() != MIN_IMAGE_DIMENSION) { RETURN_STATUS_UNEXPECTED("Equalize: image rank should be 2 or 3, but got: " + std::to_string(input_cv->Rank())); } + // For greyscale images, extend dimension if rank is 2 and reshape output to be of rank 2. if (input_cv->Rank() == MIN_IMAGE_DIMENSION) { RETURN_IF_NOT_OK(input_cv->ExpandDim(MIN_IMAGE_DIMENSION)); } - // Get number of channels and image matrix + + // Get the number of channels and the image matrix std::size_t num_of_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check the number of channels in the input image tensor if (num_of_channels != MIN_IMAGE_CHANNELS && num_of_channels != DEFAULT_IMAGE_CHANNELS) { RETURN_STATUS_UNEXPECTED("Equalize: channel of input image should be 1 or 3, but got: " + std::to_string(num_of_channels)); } + + // Get the image matrix cv::Mat image = input_cv->mat(); - // Separate the image to channels + + // Separate the image into channels std::vector planes(num_of_channels); cv::split(image, planes); + // Equalize each channel separately std::vector image_result; for (std::size_t layer = 0; layer < planes.size(); layer++) { @@ -1262,442 +2236,797 @@ Status Equalize(const std::shared_ptr &input, std::shared_ptr *o cv::equalizeHist(planes[layer], channel_result); image_result.push_back(channel_result); } + + // Merge the equalized channels back into a single image cv::Mat result; cv::merge(image_result, result); + + // Create a CVTensor from the equalized image matrix std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); + + // Convert the CVTensor to a generic Tensor and assign it to the output pointer (*output) = std::static_pointer_cast(output_cv); + + // Reshape the output tensor to match the shape of the input tensor RETURN_IF_NOT_OK((*output)->Reshape(input_cv->shape())); } catch (const cv::Exception &e) { RETURN_STATUS_UNEXPECTED("Equalize: " + std::string(e.what())); } + + // Return OK status to indicate successful execution of the function return Status::OK(); } +// Closing brace to end the main function +} -Status Erase(const std::shared_ptr &input, std::shared_ptr *output, int32_t box_height, - int32_t box_width, int32_t num_patches, bool bounded, bool random_color, std::mt19937 *rnd, uint8_t fill_r, - uint8_t fill_g, uint8_t fill_b) { - try { - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - CHECK_FAIL_RETURN_UNEXPECTED(input_cv->shape().Size() > CHANNEL_INDEX, "Erase: shape is invalid."); - int num_channels = input_cv->shape()[CHANNEL_INDEX]; - if (input_cv->mat().data == nullptr) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] CutOut: load image failed."); - } - if (input_cv->Rank() != DEFAULT_IMAGE_RANK || num_channels != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("CutOut: image shape is not or channel is not 3, but got rank: " + - std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(num_channels)); - } - cv::Mat input_img = input_cv->mat(); - int32_t image_h = input_cv->shape()[0]; - int32_t image_w = input_cv->shape()[1]; - // check if erase size is bigger than image itself - if (box_height > image_h || box_width > image_w) { - RETURN_STATUS_UNEXPECTED( - "CutOut: box size is too large for image erase, got box height: " + std::to_string(box_height) + - "box weight: " + std::to_string(box_width) + ", and image height: " + std::to_string(image_h) + - ", image width: " + std::to_string(image_w)); - } +// The Erase function takes in an input tensor, erases a portion of the image, and returns the modified image as the output tensor. +// The function also takes in various parameters such as box height, box width, number of patches, etc. to control the erasing process. - // for random color +// The function starts by checking if the input tensor is a valid CVTensor with a shape greater than the channel index. +// If the shape is invalid, it throws an exception with an error message. + +// The function then checks if the input tensor's underlying OpenCV matrix data is null. +// If it is null, it throws an exception with an error message indicating that loading the image failed. + +// Next, the function checks if the input tensor's rank is equal to the default image rank and if the number of channels is equal to the default image channels (3). +// If the rank or number of channels is not as expected, it throws an exception with an error message indicating the actual rank and number of channels. + +// The function then retrieves the OpenCV matrix from the input tensor and stores it in the input_img variable. +// It also retrieves the height and width of the image from the input tensor's shape. + +// After that, the function checks if the box height or box width is greater than the image height or image width, respectively. +// If either of them is greater, it throws an exception with an error message indicating the box size and image size. + + // Create a normal distribution object with mean 0 and standard deviation 1 for generating random numbers std::normal_distribution normal_distribution(0, 1); + + // Create uniform distribution objects for generating random numbers within specified bounds std::uniform_int_distribution height_distribution_bound(0, image_h - box_height); std::uniform_int_distribution width_distribution_bound(0, image_w - box_width); std::uniform_int_distribution height_distribution_unbound(0, image_h + box_height); std::uniform_int_distribution width_distribution_unbound(0, image_w + box_width); - // core logic - // update values based on random erasing or cutout + // Core logic + // Update values based on random erasing or cutout + + // Iterate over the patches from 0 to num_patches for (int32_t i = 0; i < num_patches; i++) { - // rows in cv mat refers to the height of the cropped box - // we determine h_start and w_start using two different distributions as erasing is used by two different - // image augmentations. The bounds are also different in each case. + + // The rows in the cv mat refer to the height of the cropped box + + // Determine the starting height (h_start) using two different distributions + // If bounded is true, use the height_distribution_bound function with a random number generator (rnd) + // Otherwise, use the height_distribution_unbound function with rnd and subtract the box_height int32_t h_start = (bounded) ? height_distribution_bound(*rnd) : (height_distribution_unbound(*rnd) - box_height); + + // Determine the starting width (w_start) using two different distributions + // If bounded is true, use the width_distribution_bound function with rnd + // Otherwise, use the width_distribution_unbound function with rnd and subtract the box_width int32_t w_start = (bounded) ? width_distribution_bound(*rnd) : (width_distribution_unbound(*rnd) - box_width); - int32_t max_width = (w_start + box_width > image_w) ? image_w : w_start + box_width; - int32_t max_height = (h_start + box_height > image_h) ? image_h : h_start + box_height; - // check for starting range >= 0, here the start range is checked after for cut out, for random erasing - // w_start and h_start will never be less than 0. - h_start = (h_start < 0) ? 0 : h_start; - w_start = (w_start < 0) ? 0 : w_start; - for (int y = w_start; y < max_width; y++) { - for (int x = h_start; x < max_height; x++) { - if (random_color) { - // fill each box with a random value - input_img.at(cv::Point(y, x))[0] = static_cast(normal_distribution(*rnd)); - input_img.at(cv::Point(y, x))[1] = static_cast(normal_distribution(*rnd)); - input_img.at(cv::Point(y, x))[2] = static_cast(normal_distribution(*rnd)); - } else { - input_img.at(cv::Point(y, x))[0] = fill_r; - input_img.at(cv::Point(y, x))[1] = fill_g; - input_img.at(cv::Point(y, x))[2] = fill_b; - } - } - } +int32_t max_width = (w_start + box_width > image_w) ? image_w : w_start + box_width; +int32_t max_height = (h_start + box_height > image_h) ? image_h : h_start + box_height; + +// Check if the starting range is less than 0, and if so, set it to 0 +h_start = (h_start < 0) ? 0 : h_start; +w_start = (w_start < 0) ? 0 : w_start; + +// Iterate over the pixels within the specified box +for (int y = w_start; y < max_width; y++) { + for (int x = h_start; x < max_height; x++) { + if (random_color) { + // Fill each pixel with a random value + input_img.at(cv::Point(y, x))[0] = static_cast(normal_distribution(*rnd)); + input_img.at(cv::Point(y, x))[1] = static_cast(normal_distribution(*rnd)); + input_img.at(cv::Point(y, x))[2] = static_cast(normal_distribution(*rnd)); + } else { + // Fill each pixel with the specified fill color + input_img.at(cv::Point(y, x))[0] = fill_r; + input_img.at(cv::Point(y, x))[1] = fill_g; + input_img.at(cv::Point(y, x))[2] = fill_b; } - *output = std::static_pointer_cast(input); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("CutOut: " + std::string(e.what())); } } +// Set the output tensor to be the same as the input tensor +*output = std::static_pointer_cast(input); + +// Return OK status if the operation is successful +return Status::OK(); + +// Catch any OpenCV exceptions and return an error message +} catch (const cv::Exception &e) { + RETURN_STATUS_UNEXPECTED("CutOut: " + std::string(e.what())); +} + +// Function to pad an input tensor with specified padding values and border types +// Takes an input tensor, output tensor pointer, padding values for top, bottom, left, and right, +// border types, and fill color values for red, green, and blue + Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { try { - // input image + // Convert the input tensor to a CVTensor for OpenCV operations std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - // validate rank + // Validate the rank of the input tensor if (input_cv->Rank() == 1 || input_cv->mat().dims > MIN_IMAGE_DIMENSION) { + + // Create an error message string std::string err_msg = "Pad: input shape is not or , got rank: " + std::to_string(input_cv->Rank()); + + // Check if the rank is 1, which may indicate the need for a Decode operation first if (input_cv->Rank() == 1) { err_msg = err_msg + ", may need to do Decode operation first."; } + + // Return an error status with the error message RETURN_STATUS_UNEXPECTED(err_msg); } - // get the border type in openCV + // Get the border type in OpenCV based on the provided border_types auto b_type = GetCVBorderType(border_types); - // output image + + // Declare an output image variable cv::Mat out_image; + + // Check if the border type is BORDER_CONSTANT if (b_type == cv::BORDER_CONSTANT) { - cv::Scalar fill_color = cv::Scalar(fill_b, fill_g, fill_r); - cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type, fill_color); + // Create a scalar fill color using the provided fill_b, fill_g, and fill_r values + cv::Scalar fill_color = cv::Scalar(fill_b, fill_g, fill_r); + + // Use the copyMakeBorder function to pad the input image with the specified border type and fill color + cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type, fill_color); } else { - cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type); + // Use the copyMakeBorder function to pad the input image with the specified border type + cv::copyMakeBorder(input_cv->mat(), out_image, pad_top, pad_bottom, pad_left, pad_right, b_type); } + + // Create a shared pointer to a CVTensor object from the output image std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(out_image, input_cv->Rank(), &output_cv)); - // pad the dimension if shape information is only 2 dimensional, this is grayscale + + // Check if the shape information is only 2-dimensional (grayscale image) int num_channels = input_cv->shape()[CHANNEL_INDEX]; if (input_cv->Rank() == DEFAULT_IMAGE_RANK && num_channels == MIN_IMAGE_CHANNELS && output_cv->Rank() == MIN_IMAGE_DIMENSION) - RETURN_IF_NOT_OK(output_cv->ExpandDim(CHANNEL_INDEX)); + RETURN_IF_NOT_OK(output_cv->ExpandDim(CHANNEL_INDEX)); + + // Cast the output CVTensor object to a Tensor object and assign it to the output pointer *output = std::static_pointer_cast(output_cv); + + // Return OK status if the padding operation is successful return Status::OK(); - } catch (const cv::Exception &e) { + +} catch (const cv::Exception &e) { + // Catch any OpenCV exceptions and return an error status with the exception message RETURN_STATUS_UNEXPECTED("Pad: " + std::string(e.what())); - } } -Status RandomLighting(const std::shared_ptr &input, std::shared_ptr *output, float rnd_r, float rnd_g, - float rnd_b) { - try { +// Function to randomly adjust the lighting of an input image +// Takes an input tensor, output tensor pointer, and random values for red, green, and blue channels + +// Try block to catch any exceptions that may occur +try { + // Convert the input tensor to a CVTensor std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the underlying OpenCV matrix from the CVTensor cv::Mat input_img = input_cv->mat(); + // ... +} - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] RandomLighting: load image failed."); - } +// Check if the data in the input_cv->mat() is empty or null +if (!input_cv->mat().data) { + // If the data is empty or null, return an error message indicating that loading the image failed + RETURN_STATUS_UNEXPECTED("[Internal ERROR] RandomLighting: load image failed."); +} + + // Check if the rank of the input tensor is not equal to DEFAULT_IMAGE_RANK or the number of channels is not equal to DEFAULT_IMAGE_CHANNELS if (input_cv->Rank() != DEFAULT_IMAGE_RANK || input_cv->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED( - "RandomLighting: input tensor is not in shape of or channel is not 3, got rank: " + - std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(input_cv->shape()[CHANNEL_INDEX])); + // If the condition is true, return an error message with the rank and channel information of the input tensor + RETURN_STATUS_UNEXPECTED( + "RandomLighting: input tensor is not in shape of or channel is not 3, got rank: " + + std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(input_cv->shape()[CHANNEL_INDEX])); } + + // Get the data type of the input tensor auto input_type = input->type(); + + // Check if the input tensor has an invalid data type of uint32, int64, uint64, or string CHECK_FAIL_RETURN_UNEXPECTED(input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && input_type != DataType::DE_INT64 && input_type != DataType::DE_STRING, "RandomLighting: invalid tensor type of uint32, int64, uint64 or string."); - std::vector> eig = {{55.46 * -0.5675, 4.794 * 0.7192, 1.148 * 0.4009}, - {55.46 * -0.5808, 4.794 * -0.0045, 1.148 * -0.8140}, - {55.46 * -0.5836, 4.794 * -0.6948, 1.148 * 0.4203}}; +// Create a 2D vector named "eig" to store a matrix of floating-point numbers +std::vector> eig = { + // Initialize the matrix with three rows and three columns + // Each element is calculated by multiplying the corresponding values with the given constants + {55.46 * -0.5675, 4.794 * 0.7192, 1.148 * 0.4009}, + {55.46 * -0.5808, 4.794 * -0.0045, 1.148 * -0.8140}, + {55.46 * -0.5836, 4.794 * -0.6948, 1.148 * 0.4203} +}; - float pca_r = eig[0][0] * rnd_r + eig[0][1] * rnd_g + eig[0][2] * rnd_b; - float pca_g = eig[1][0] * rnd_r + eig[1][1] * rnd_g + eig[1][2] * rnd_b; - float pca_b = eig[2][0] * rnd_r + eig[2][1] * rnd_g + eig[2][2] * rnd_b; - for (int row = 0; row < input_img.rows; row++) { - for (int col = 0; col < input_img.cols; col++) { - float r = static_cast(input_img.at(row, col)[0]); - float g = static_cast(input_img.at(row, col)[1]); - float b = static_cast(input_img.at(row, col)[2]); - input_img.at(row, col)[0] = cv::saturate_cast(r + pca_r); - input_img.at(row, col)[1] = cv::saturate_cast(g + pca_g); - input_img.at(row, col)[2] = cv::saturate_cast(b + pca_b); - } - } +// Calculate the PCA (Principal Component Analysis) values for each color channel +float pca_r = eig[0][0] * rnd_r + eig[0][1] * rnd_g + eig[0][2] * rnd_b; +float pca_g = eig[1][0] * rnd_r + eig[1][1] * rnd_g + eig[1][2] * rnd_b; +float pca_b = eig[2][0] * rnd_r + eig[2][1] * rnd_g + eig[2][2] * rnd_b; - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &output_cv)); +// Iterate over each pixel in the input image +for (int row = 0; row < input_img.rows; row++) { + for (int col = 0; col < input_img.cols; col++) { + // Get the RGB values of the current pixel and convert them to float + float r = static_cast(input_img.at(row, col)[0]); + float g = static_cast(input_img.at(row, col)[1]); + float b = static_cast(input_img.at(row, col)[2]); - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RandomLighting: " + std::string(e.what())); + // Update the RGB values of the current pixel by adding the PCA values + input_img.at(row, col)[0] = cv::saturate_cast(r + pca_r); + input_img.at(row, col)[1] = cv::saturate_cast(g + pca_g); + input_img.at(row, col)[2] = cv::saturate_cast(b + pca_b); } } +// Create a shared pointer to a CVTensor object named output_cv +std::shared_ptr output_cv; + +// Call the CreateFromMat function of the CVTensor class to create a CVTensor object from the input_img +// Pass the rank of the input_cv object and the address of the output_cv pointer as arguments +// The function will return an error code, so check if it is not OK +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &output_cv)); + +// Cast the output_cv variable to a shared pointer of type Tensor and assign it to the output variable +*output = std::static_pointer_cast(output_cv); + +// Return a Status object indicating that the operation was successful +return Status::OK(); + +// Catch any cv::Exception that might occur during the execution of the code +catch (const cv::Exception &e) { + + // Return a Status object with an error message that includes the exception message + RETURN_STATUS_UNEXPECTED("RandomLighting: " + std::string(e.what())); +} + +// Convert an RGBA image to RGB format Status RgbaToRgb(const std::shared_ptr &input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor for easier manipulation std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); + + // Get the number of channels in the input tensor int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the input tensor has the correct shape and number of channels if (input_cv->shape().Size() != DEFAULT_IMAGE_CHANNELS || num_channels != 4) { + // If not, construct an error message with the expected and actual shape and number of channels std::string err_msg = "RgbaToRgb: rank of image is not: " + std::to_string(DEFAULT_IMAGE_CHANNELS) + ", but got: " + std::to_string(input_cv->shape().Size()) + ", or channels of image should be 4, but got: " + std::to_string(num_channels); + + // Return an error status with the error message RETURN_STATUS_UNEXPECTED(err_msg); } + + // Create the output tensor shape with the same height and width, but only 3 channels (RGB) TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1], 3}); + + // Create an empty CVTensor with the output shape and the same data type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); + + // Convert the RGBA image to RGB using OpenCV's cvtColor function cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGBA2RGB)); + + // Assign the output CVTensor to the output pointer *output = std::static_pointer_cast(output_cv); + + // Return a success status return Status::OK(); } catch (const cv::Exception &e) { + // If an exception occurs during the conversion, construct an error message with the exception details RETURN_STATUS_UNEXPECTED("RgbaToRgb: " + std::string(e.what())); } } +// Convert an RGBA image to BGR format using OpenCV + +// The function takes an input tensor and a pointer to an output tensor Status RgbaToBgr(const std::shared_ptr &input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor for easier manipulation std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); + + // Get the number of channels in the input tensor int num_channels = input_cv->shape()[CHANNEL_INDEX]; + + // Check if the input tensor has the correct shape and number of channels if (input_cv->shape().Size() != DEFAULT_IMAGE_CHANNELS || num_channels != MAX_IMAGE_CHANNELS) { + // If not, construct an error message and return an unexpected status std::string err_msg = "RgbaToBgr: rank of image is not: " + std::to_string(DEFAULT_IMAGE_CHANNELS) + ", but got: " + std::to_string(input_cv->shape().Size()) + ", or channels of image should be 4, but got: " + std::to_string(num_channels); RETURN_STATUS_UNEXPECTED(err_msg); } + + // Create the output tensor shape with the same dimensions as the input tensor, but with 3 channels TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1], 3}); + + // Create an empty CVTensor with the output shape and the same data type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); + + // Convert the RGBA image to BGR format using OpenCV's cvtColor function cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGBA2BGR)); + + // Assign the output tensor pointer to the converted CVTensor *output = std::static_pointer_cast(output_cv); + + // Return a successful status return Status::OK(); } catch (const cv::Exception &e) { + // If an exception occurs during the conversion, construct an error message and return an unexpected status RETURN_STATUS_UNEXPECTED("RgbaToBgr: " + std::string(e.what())); } } -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output) { - try { - auto input_type = input->type(); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] RgbToBgr: load image failed."); - } - if (input_cv->Rank() != DEFAULT_IMAGE_RANK || input_cv->shape()[2] != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: input tensor is not in shape of or channel is not 3, got rank: " + - std::to_string(input_cv->Rank()) + - ", and channel: " + std::to_string(input_cv->shape()[2])); - } +// Convert the input tensor to OpenCV tensor format +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - cv::Mat image = input_cv->mat().clone(); - if (input_type == DataType::DE_FLOAT16 || input_type == DataType::DE_INT16 || input_type == DataType::DE_UINT16) { - for (int i = 0; i < input_cv->mat().rows; ++i) { +// Check if the conversion was successful by checking if the OpenCV matrix data is valid +if (!input_cv->mat().data) { + // If the matrix data is invalid, return an error status with a descriptive message + RETURN_STATUS_UNEXPECTED("[Internal ERROR] RgbToBgr: load image failed."); +} + +// Check if the input tensor has the correct shape and number of channels +if (input_cv->Rank() != DEFAULT_IMAGE_RANK || input_cv->shape()[2] != DEFAULT_IMAGE_CHANNELS) { + // If the shape or number of channels is incorrect, return an error status with a descriptive message + RETURN_STATUS_UNEXPECTED("RgbToBgr: input tensor is not in shape of or channel is not 3, got rank: " + + std::to_string(input_cv->Rank()) + + ", and channel: " + std::to_string(input_cv->shape()[2])); +} + +// Create a new cv::Mat object called "image" and make a deep copy of the input_cv's mat +cv::Mat image = input_cv->mat().clone(); + +// Check the data type of the input (input_type) and perform different operations based on the data type +if (input_type == DataType::DE_FLOAT16 || input_type == DataType::DE_INT16 || input_type == DataType::DE_UINT16) { + // If the input type is float16, int16, or uint16, iterate over each row of the input_cv's mat + for (int i = 0; i < input_cv->mat().rows; ++i) { + // Get a pointer to the i-th row of the input_cv's mat and the image cv::Vec3s *p1 = input_cv->mat().ptr(i); cv::Vec3s *p2 = image.ptr(i); + + // Iterate over each column of the input_cv's mat for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][2] = p1[j][0]; - p2[j][1] = p1[j][1]; - p2[j][0] = p1[j][2]; + // Swap the values of the channels in the image + p2[j][2] = p1[j][0]; + p2[j][1] = p1[j][1]; + p2[j][0] = p1[j][2]; } - } - } else if (input_type == DataType::DE_FLOAT32 || input_type == DataType::DE_INT32) { - for (int i = 0; i < input_cv->mat().rows; ++i) { + } +} else if (input_type == DataType::DE_FLOAT32 || input_type == DataType::DE_INT32) { + // If the input type is float32 or int32, iterate over each row of the input_cv's mat + for (int i = 0; i < input_cv->mat().rows; ++i) { + // Get a pointer to the i-th row of the input_cv's mat and the image cv::Vec3f *p1 = input_cv->mat().ptr(i); cv::Vec3f *p2 = image.ptr(i); + + // Iterate over each column of the input_cv's mat for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][2] = p1[j][0]; - p2[j][1] = p1[j][1]; - p2[j][0] = p1[j][2]; + // Swap the values of the channels in the image + p2[j][2] = p1[j][0]; + p2[j][1] = p1[j][1]; + p2[j][0] = p1[j][2]; } - } - } else if (input_type == DataType::DE_FLOAT64) { - for (int i = 0; i < input_cv->mat().rows; ++i) { + } +} else if (input_type == DataType::DE_FLOAT64) { + // If the input type is float64, iterate over each row of the input_cv's mat + for (int i = 0; i < input_cv->mat().rows; ++i) { + // Get a pointer to the i-th row of the input_cv's mat and the image cv::Vec3d *p1 = input_cv->mat().ptr(i); cv::Vec3d *p2 = image.ptr(i); + + // Iterate over each column of the input_cv's mat for (int j = 0; j < input_cv->mat().cols; ++j) { - p2[j][2] = p1[j][0]; - p2[j][1] = p1[j][1]; - p2[j][0] = p1[j][2]; + // Swap the values of the channels in the image + p2[j][2] = p1[j][0]; + p2[j][1] = p1[j][1]; + p2[j][0] = p1[j][2]; } - } - } else { - cv::cvtColor(input_cv->mat(), image, cv::COLOR_RGB2BGR); } - - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(image, input_cv->Rank(), &output_cv)); - - *output = std::static_pointer_cast(output_cv); - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); - } +} else { + // If the input type is none of the above, convert the input_cv's mat from RGB to BGR using cvtColor function + cv::cvtColor(input_cv->mat(), image, cv::COLOR_RGB2BGR); } +// Create a shared pointer to a CVTensor object named output_cv +std::shared_ptr output_cv; + +// Call the CreateFromMat function of the CVTensor class to create a CVTensor object from the image +// Pass the image, the rank of the input_cv object, and a pointer to the output_cv object +// The function will return an error code, so check if it is not OK +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(image, input_cv->Rank(), &output_cv)); + +// Cast the output_cv variable to a shared pointer of type Tensor and assign it to the output variable +*output = std::static_pointer_cast(output_cv); + +// Return a Status object indicating that the operation was successful +return Status::OK(); + +// Catch any cv::Exception that might occur during the execution of the code +// and return a Status object with an error message that includes the exception's what() message +} catch (const cv::Exception &e) { + RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); +} + +// Convert an RGB image to grayscale using OpenCV + +// Function signature: RgbToGray(const std::shared_ptr &input, std::shared_ptr *output) +// - Takes an input tensor (shared pointer) representing an RGB image +// - Returns the grayscale version of the input image as an output tensor (shared pointer) +// - Uses OpenCV to perform the conversion + Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output) { try { + // Convert the input tensor to a CVTensor (OpenCV tensor) std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input)); + + // Check if the input image shape is and the number of channels is 3 if (input_cv->Rank() != DEFAULT_IMAGE_RANK || input_cv->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { + // If not, return an error status with a descriptive message RETURN_STATUS_UNEXPECTED( "RgbToGray: image shape is not or channel is not 3, got rank: " + std::to_string(input_cv->Rank()) + ", and channel: " + std::to_string(input_cv->shape()[2])); } + + // Create the output tensor shape with the same height and width as the input image, but with only 1 channel TensorShape out_shape = TensorShape({input_cv->shape()[0], input_cv->shape()[1]}); + + // Create an empty CVTensor for the output with the same data type as the input std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(out_shape, input_cv->type(), &output_cv)); + + // Convert the input RGB image to grayscale using OpenCV's cvtColor function cv::cvtColor(input_cv->mat(), output_cv->mat(), static_cast(cv::COLOR_RGB2GRAY)); + + // Cast the output CVTensor to a generic Tensor and assign it to the output pointer *output = std::static_pointer_cast(output_cv); + + // Return a success status return Status::OK(); } catch (const cv::Exception &e) { + // If any exception occurs during the conversion, return an error status with the exception message RETURN_STATUS_UNEXPECTED("RgbToGray: " + std::string(e.what())); } } +// Function to get the width and height of a JPEG image from a given input tensor Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height) { + + // Create a struct to hold the decompression parameters for the JPEG image struct jpeg_decompress_struct cinfo {}; + + // Create a custom error manager for handling JPEG errors struct JpegErrorManagerCustom jerr {}; + + // Set the error manager for the decompression parameters cinfo.err = jpeg_std_error(&jerr.pub); + + // Set the custom error exit function for the error manager jerr.pub.error_exit = JpegErrorExitCustom; + try { + // Create a decompression object for the JPEG image jpeg_create_decompress(&cinfo); + + // Set the source of the JPEG image to the input tensor's buffer and size JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); + + // Read the header of the JPEG image (void)jpeg_read_header(&cinfo, TRUE); + + // Calculate the output dimensions of the JPEG image jpeg_calc_output_dimensions(&cinfo); } catch (std::runtime_error &e) { + // If an exception occurs, destroy the decompression object and return an unexpected status with the error message jpeg_destroy_decompress(&cinfo); RETURN_STATUS_UNEXPECTED(e.what()); } + + // Set the output height and width of the JPEG image *img_height = cinfo.output_height; *img_width = cinfo.output_width; + + // Destroy the decompression object jpeg_destroy_decompress(&cinfo); + + // Return a status indicating successful execution return Status::OK(); } -Status Affine(const std::shared_ptr &input, std::shared_ptr *output, const std::vector &mat, - InterpolationMode interpolation, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { - try { +// Define a function named "Affine" that takes in the following parameters: +// - A shared pointer to a Tensor object named "input" +// - A pointer to a shared pointer of a Tensor object named "output" +// - A constant reference to a vector of float values named "mat" +// - An enumeration value named "interpolation" of type InterpolationMode +// - Three uint8_t values named "fill_r", "fill_g", and "fill_b" + +// Start a try block to catch any exceptions that may occur +try { + // Convert the input Tensor to a shared pointer of a CVTensor object and assign it to "input_cv" std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Call the ValidateImageRank function with the string "Affine" and the rank of "input_cv" as arguments + // and return the result of the function call. If the result is not OK, return immediately. RETURN_IF_NOT_OK(ValidateImageRank("Affine", input_cv->Rank())); - cv::Mat affine_mat(mat); - affine_mat = affine_mat.reshape(1, {2, 3}); + // Continue with the rest of the function... +} catch (...) { + // Catch any exceptions that may occur and handle them here +} +// The function does not have a return statement here, so the return type is void. + +// Create a new cv::Mat object named affine_mat and initialize it with the contents of the mat object +cv::Mat affine_mat(mat); + +// Reshape the affine_mat object to have 1 channel and a size of {2, 3} +affine_mat = affine_mat.reshape(1, {2, 3}); + + // Create a shared pointer to a CVTensor object named output_cv std::shared_ptr output_cv; + + // Call the CreateEmpty function of the CVTensor class to create an empty CVTensor object with the same shape and type as input_cv + // Store the result in the output_cv shared pointer RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Check if the output_cv shared pointer is null, and return an unexpected status if it is RETURN_UNEXPECTED_IF_NULL(output_cv); + + // Use the warpAffine function from the OpenCV library to apply an affine transformation to the input_cv matrix + // Store the result in the output_cv matrix, using the affine_mat transformation matrix and the size of the input_cv matrix + // Use the GetCVInterpolationMode function to determine the interpolation mode + // Use cv::BORDER_CONSTANT and cv::Scalar(fill_r, fill_g, fill_b) to specify the border type and fill color cv::warpAffine(input_cv->mat(), output_cv->mat(), affine_mat, input_cv->mat().size(), GetCVInterpolationMode(interpolation), cv::BORDER_CONSTANT, cv::Scalar(fill_r, fill_g, fill_b)); + + // Assign the output_cv shared pointer to the output pointer, after casting it to a shared pointer of the Tensor class (*output) = std::static_pointer_cast(output_cv); + + // Return a status indicating successful program execution return Status::OK(); + + // Catch any cv::Exception that may occur during the execution of the code within the try block } catch (const cv::Exception &e) { + + // Return an unexpected status with an error message that includes the what() function of the cv::Exception object RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); } } +// Function to apply Gaussian blur to an input image tensor Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_x, int32_t kernel_y, float sigma_x, float sigma_y) { try { + // Convert the input tensor to a CVTensor (OpenCV tensor) std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Check if the input image data is valid if (input_cv->mat().data == nullptr) { + // Return an error status if the image loading failed RETURN_STATUS_UNEXPECTED("[Internal ERROR] GaussianBlur: load image failed."); } + + // Create an output CV matrix to store the blurred image cv::Mat output_cv_mat; + + // Apply Gaussian blur to the input image using the specified kernel size and sigma values cv::GaussianBlur(input_cv->mat(), output_cv_mat, cv::Size(kernel_x, kernel_y), static_cast(sigma_x), static_cast(sigma_y)); + + // Convert the output CV matrix back to a CVTensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_cv_mat, input_cv->Rank(), &output_cv)); + + // Assign the output CVTensor to the output pointer (*output) = std::static_pointer_cast(output_cv); + + // Return a success status return Status::OK(); } catch (const cv::Exception &e) { + // Return an error status if any OpenCV exception occurs during the Gaussian blur operation RETURN_STATUS_UNEXPECTED("GaussianBlur: " + std::string(e.what())); } } +// Compute the size of the patch based on the input CVTensor, number of height and width, and slice mode Status ComputePatchSize(const std::shared_ptr &input_cv, std::shared_ptr> *patch_size, int32_t num_height, int32_t num_width, SliceMode slice_mode) { + + // Check if the data in the input CVTensor is null if (input_cv->mat().data == nullptr) { + // Return an error status with a descriptive error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] SlicePatches: Tensor could not convert to CV Tensor."); } + + // Validate the rank of the input CVTensor using the ValidateImageRank function RETURN_IF_NOT_OK(ValidateImageRank("Affine", input_cv->Rank())); - cv::Mat in_img = input_cv->mat(); - cv::Size s = in_img.size(); - if (num_height == 0 || num_height > s.height) { - RETURN_STATUS_UNEXPECTED( - "SlicePatches: The number of patches on height axis equals 0 or is greater than height, got number of patches:" + - std::to_string(num_height)); - } - if (num_width == 0 || num_width > s.width) { - RETURN_STATUS_UNEXPECTED( - "SlicePatches: The number of patches on width axis equals 0 or is greater than width, got number of patches:" + - std::to_string(num_width)); - } - int32_t patch_h = s.height / num_height; - if (s.height % num_height != 0) { - if (slice_mode == SliceMode::kPad) { - patch_h += 1; // patch_h * num_height - s.height - } - } - int32_t patch_w = s.width / num_width; - if (s.width % num_width != 0) { - if (slice_mode == SliceMode::kPad) { - patch_w += 1; // patch_w * num_width - s.width - } - } - (*patch_size)->first = patch_h; - (*patch_size)->second = patch_w; + // Return the status indicating successful computation of the patch size return Status::OK(); } -Status SlicePatches(const std::shared_ptr &input, std::vector> *output, - int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) { - if (num_height == DEFAULT_NUM_HEIGHT && num_width == DEFAULT_NUM_WIDTH) { - (*output).push_back(input); - return Status::OK(); - } +// Convert the input image to a cv::Mat object +cv::Mat in_img = input_cv->mat(); - auto patch_size = std::make_shared>(0, 0); - int32_t patch_h = 0; - int32_t patch_w = 0; +// Get the size of the input image +cv::Size s = in_img.size(); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - RETURN_IF_NOT_OK(ComputePatchSize(input_cv, &patch_size, num_height, num_width, slice_mode)); - std::tie(patch_h, patch_w) = *patch_size; - - cv::Mat in_img = input_cv->mat(); - cv::Size s = in_img.size(); - try { - cv::Mat out_img; - if (slice_mode == SliceMode::kPad) { // padding on right and bottom directions - auto padding_h = patch_h * num_height - s.height; - auto padding_w = patch_w * num_width - s.width; - out_img = cv::Mat(s.height + padding_h, s.width + padding_w, in_img.type(), cv::Scalar::all(fill_value)); - in_img.copyTo(out_img(cv::Rect(0, 0, s.width, s.height))); - } else { - out_img = in_img; - } - for (int i = 0; i < num_height; ++i) { - for (int j = 0; j < num_width; ++j) { - std::shared_ptr patch_cv; - cv::Rect rect(j * patch_w, i * patch_h, patch_w, patch_h); - cv::Mat patch(out_img(rect)); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(patch, input_cv->Rank(), &patch_cv)); - (*output).push_back(std::static_pointer_cast(patch_cv)); - } - } - return Status::OK(); - } catch (const cv::Exception &e) { - RETURN_STATUS_UNEXPECTED("SlicePatches: " + std::string(e.what())); - } +// Check if the number of patches on the height axis is valid +if (num_height == 0 || num_height > s.height) { + // If not valid, return an error message with the number of patches + RETURN_STATUS_UNEXPECTED( + "SlicePatches: The number of patches on height axis equals 0 or is greater than height, got number of patches:" + + std::to_string(num_height)); } +// Check if the number of patches on the width axis is valid +if (num_width == 0 || num_width > s.width) { + // If not valid, return an error message with the number of patches + RETURN_STATUS_UNEXPECTED( + "SlicePatches: The number of patches on width axis equals 0 or is greater than width, got number of patches:" + + std::to_string(num_width)); +} + +// Calculate the height of each patch +int32_t patch_h = s.height / num_height; + +// Check if the height of the image is not divisible by the number of patches +if (s.height % num_height != 0) { + // If not divisible and the slice mode is set to padding, increase the height of each patch by 1 + if (slice_mode == SliceMode::kPad) { + patch_h += 1; // patch_h * num_height - s.height + } +} + +// Calculate the width of each patch +int32_t patch_w = s.width / num_width; + +// Check if the width of the image is not divisible by the number of patches +if (s.width % num_width != 0) { + // If not divisible and the slice mode is set to padding, increase the width of each patch by 1 + if (slice_mode == SliceMode::kPad) { + patch_w += 1; // patch_w * num_width - s.width + } +} + +// Set the patch size as the calculated height and width +(*patch_size)->first = patch_h; +(*patch_size)->second = patch_w; + +// Return a success status +return Status::OK(); + +// Function to slice patches from an input tensor and store them in the output vector +// The function takes the following parameters: +// - input: a shared pointer to the input tensor +// - output: a pointer to a vector of shared pointers to tensors, where the sliced patches will be stored +// - num_height: the number of patches to be sliced along the height dimension +// - num_width: the number of patches to be sliced along the width dimension +// - slice_mode: the mode of slicing (not specified in the provided code) +// - fill_value: the value to fill the patches with (not specified in the provided code) + +// Check if the number of patches to be sliced along the height and width dimensions is equal to the default values +if (num_height == DEFAULT_NUM_HEIGHT && num_width == DEFAULT_NUM_WIDTH) { + // If so, push the input tensor to the output vector as it is + (*output).push_back(input); + // Return OK status to indicate successful slicing + return Status::OK(); +} + +// Create a shared pointer to a pair of integers using std::make_shared +// Initialize the pair with values (0, 0) +auto patch_size = std::make_shared>(0, 0); + +// Declare and initialize two integer variables patch_h and patch_w with 0 +int32_t patch_h = 0; +int32_t patch_w = 0; + +// Create a shared pointer to a CVTensor object named input_cv and initialize it with the result of calling the AsCVTensor function on the input object +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + +// Call the ComputePatchSize function with the input_cv, patch_size, num_height, num_width, and slice_mode arguments and check if it returns an error +RETURN_IF_NOT_OK(ComputePatchSize(input_cv, &patch_size, num_height, num_width, slice_mode)); + +// Use structured binding to assign the values of the patch_size tuple to the variables patch_h and patch_w +std::tie(patch_h, patch_w) = *patch_size; + +// Convert the input_cv to a cv::Mat object named in_img +cv::Mat in_img = input_cv->mat(); + +// Get the size of the in_img +cv::Size s = in_img.size(); + +try { + // Declare an output image named out_img + cv::Mat out_img; + + // Check if the slice_mode is set to SliceMode::kPad + if (slice_mode == SliceMode::kPad) { // padding on right and bottom directions + + // Calculate the amount of padding needed in the height and width directions + auto padding_h = patch_h * num_height - s.height; + auto padding_w = patch_w * num_width - s.width; + + // Create a new image with the size of (s.height + padding_h) x (s.width + padding_w) + // and fill it with the fill_value + out_img = cv::Mat(s.height + padding_h, s.width + padding_w, in_img.type(), cv::Scalar::all(fill_value)); + + // Copy the in_img to the top-left corner of the out_img + in_img.copyTo(out_img(cv::Rect(0, 0, s.width, s.height))); + } else { + // If slice_mode is not SliceMode::kPad, set out_img to be equal to in_img + out_img = in_img; + } + + // Iterate over the patches in the out_img + for (int i = 0; i < num_height; ++i) { + for (int j = 0; j < num_width; ++j) { + + // Create a shared pointer to a CVTensor object named patch_cv + std::shared_ptr patch_cv; + + // Define a rectangle representing the patch in the out_img + cv::Rect rect(j * patch_w, i * patch_h, patch_w, patch_h); + + // Extract the patch from the out_img using the defined rectangle + cv::Mat patch(out_img(rect)); + + // Create a CVTensor object from the patch + RETURN_IF_NOT_OK(CVTensor::CreateFromMat(patch, input_cv->Rank(), &patch_cv)); + + // Append the CVTensor object to the output tensor + (*output).push_back(std::static_pointer_cast(patch_cv)); + } + } + + // Return a status indicating successful execution + return Status::OK(); + +} catch (const cv::Exception &e) { + // If an exception is caught, return an error status with the exception message + RETURN_STATUS_UNEXPECTED("SlicePatches: " + std::string(e.what())); +} +} + +// Function to validate the rank of an image Status ValidateImageRank(const std::string &op_name, int32_t rank) { + + // Check if the rank is not 2 or 3 if (rank != 2 && rank != 3) { + + // Create an error message indicating the incorrect rank std::string err_msg = op_name + ": image shape is not or , but got rank:" + std::to_string(rank); + + // Check if the rank is 1, indicating a single-channel image if (rank == 1) { + + // Append a message suggesting the need for a Decode operation err_msg = err_msg + ", may need to do Decode operation first."; } + + // Return an unexpected status with the error message RETURN_STATUS_UNEXPECTED(err_msg); } + + // Return a status indicating successful validation return Status::OK(); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset and mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/invert_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/invert_op.cc index 4eb33af87be..5053f9e4c9e 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/invert_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/invert_op.cc @@ -18,43 +18,85 @@ #include "minddata/dataset/core/cv_tensor.h" #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -// only supports RGB images + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { -Status InvertOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +#include + +int main(){ + std::cout << "Hello World" << std::endl; + return 0; +} + +// This code does not have any functionality related to image processing or handling. +// It simply prints "Hello World" to the console and returns 0. + +// Compute function for the InvertOp class, which takes an input tensor and computes the output tensor +// The function returns a status indicating the success or failure of the computation + +// Check if the input and output tensors are valid (non-null) +IO_CHECK(input, output); try { + // Convert the input tensor to a CVTensor using the AsCVTensor function and store it in a shared_ptr std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the underlying cv::Mat object from the CVTensor cv::Mat input_img = input_cv->mat(); + + // Check if the cv::Mat object contains valid data if (!input_cv->mat().data) { + // If the cv::Mat object does not contain valid data, return an error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Invert: load image failed."); } + // Check if the rank of the input tensor is not equal to 3 if (input_cv->Rank() != 3) { + // If the rank is not 3, return an error message with the current rank RETURN_STATUS_UNEXPECTED("Invert: image shape is not , got rank: " + std::to_string(input_cv->Rank())); } + + // Get the number of channels from the shape of the input tensor int num_channels = input_cv->shape()[2]; + + // Check if the number of channels is not equal to 3 if (num_channels != 3) { + // If the number of channels is not 3, return an error message with the current number of channels RETURN_STATUS_UNEXPECTED( "Invert: image shape is incorrect, expected num of channels is 3, " "but got:" + std::to_string(num_channels)); } + + // Create an empty CVTensor with the same shape and type as the input tensor std::shared_ptr output_cv; RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv)); + + // Check if the output CVTensor is null RETURN_UNEXPECTED_IF_NULL(output_cv); - output_cv->mat() = cv::Scalar::all(255) - input_img; - *output = std::static_pointer_cast(output_cv); - } +// Assign the value of all elements in the output_cv matrix to 255 minus the corresponding element in the input_img matrix +output_cv->mat() = cv::Scalar::all(255) - input_img; + +// Cast the output_cv matrix to a shared pointer of type Tensor and assign it to the output pointer +*output = std::static_pointer_cast(output_cv); catch (const cv::Exception &e) { + // Catch any OpenCV exceptions that occur during the execution of the code + // Concatenate the error message from the exception with the string "Invert: " and return it as an error message RETURN_STATUS_UNEXPECTED("Invert: " + std::string(e.what())); } + // Return a status indicating successful execution of the code return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc index 0bde0e63216..dfadb250a22 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/canny.cc @@ -14,216 +14,428 @@ * limitations under the License. */ +// Include the math library header file for using mathematical functions and constants in the code #include +// Include the header file "lite_mat.h" from the "lite_cv" library #include "lite_cv/lite_mat.h" + +// Include the header file "image_process.h" from the "lite_cv" library #include "lite_cv/image_process.h" +// Check if the ENABLE_ANDROID macro is defined #ifdef ENABLE_ANDROID -#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) -#define USE_NEON -#include -#endif + + // Check if any of the ARM-related macros are defined + #if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) + + // Define the USE_NEON macro + #define USE_NEON + + // Include the ARM NEON header file + #include + + // End of ARM-related macro check + #endif + +// End of ENABLE_ANDROID macro check #endif +// Define a constant variable `kAngle22_5` with a value of 0.39269908169872414 constexpr float kAngle22_5 = 0.39269908169872414; + +// Define a constant variable `kAngle67_5` with a value of 1.1780972450961724 constexpr float kAngle67_5 = 1.1780972450961724; + +// Define a constant variable `kCertainBorder` with a value of 2 constexpr int kCertainBorder = 2; + +// Define a constant variable `kUncertainBorder` with a value of 1 constexpr int kUncertainBorder = 1; + +// Define a constant variable `kNotBorder` with a value of 0 constexpr int kNotBorder = 0; +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace, a sub-namespace of "mindspore" namespace dataset { + +// Definition of the static function "GetSobelKernel" static void GetSobelKernel(float *kernel, int flag, int ksize, double scale) { + + // Create a vector of floats named "buffer" with size "ksize + 1" std::vector buffer(ksize + 1); + // Check if the kernel size is 1 if (ksize == 1) { + // If so, set the first element of the buffer to 1 buffer[0] = 1; - } else if (ksize == 3) { + } + // Check if the kernel size is 3 + else if (ksize == 3) { + // Check the value of the flag if (flag == 0) { + // If flag is 0, set the elements of the buffer to 1, 2, and 1 respectively buffer[0] = 1, buffer[1] = 2, buffer[2] = 1; } else if (flag == 1) { + // If flag is 1, set the elements of the buffer to -1, 0, and 1 respectively buffer[0] = -1, buffer[1] = 0, buffer[2] = 1; } else { + // If flag is neither 0 nor 1, set the elements of the buffer to 1, -2, and 1 respectively buffer[0] = 1, buffer[1] = -2, buffer[2] = 1; } - } else { + } + // If the kernel size is neither 1 nor 3 + else { + // Declare variables to store the previous and current values float old, now; + + // Set the first element of the buffer to 1 buffer[0] = 1; + + // Set the remaining elements of the buffer to 0 for (int i = 0; i < ksize; i++) { buffer[i + 1] = 0; } + + // Perform calculations for ksize - flag - 1 iterations for (int i = 0; i < ksize - flag - 1; i++) { + // Store the value of the first element in the buffer old = buffer[0]; + + // Perform calculations for each element in the buffer for (int j = 1; j <= ksize; j++) { + // Calculate the current value by adding the current element and the previous element now = buffer[j] + buffer[j - 1]; + + // Update the previous element with the stored value buffer[j - 1] = old; + + // Update the stored value with the current value old = now; } } + + // Perform calculations for flag iterations for (int i = 0; i < flag; i++) { + // Store the negative value of the first element in the buffer old = -buffer[0]; + + // Perform calculations for each element in the buffer for (int j = 1; j <= ksize; j++) { + // Calculate the current value by subtracting the current element from the previous element now = buffer[j - 1] - buffer[j]; + + // Update the previous element with the stored value buffer[j - 1] = old; + + // Update the stored value with the current value old = now; } } } - scale = flag == 0 ? scale : 1.0; - for (int i = 0; i < ksize; i++) { +// Set the value of scale to either scale or 1.0 based on the condition flag == 0 +// If flag is equal to 0, then scale remains unchanged, otherwise scale is set to 1.0 +scale = flag == 0 ? scale : 1.0; + +// Iterate over the elements of the kernel array from index 0 to ksize-1 +for (int i = 0; i < ksize; i++) { + + // Multiply the value of buffer[i] by the value of scale and assign the result to kernel[i] kernel[i] = buffer[i] * scale; - } } +// Function to apply Sobel filter on an input image + bool Sobel(const LiteMat &src, LiteMat &dst, int flag_x, int flag_y, int ksize, double scale, // NOLINT PaddBorderType pad_type) { + + // Check if the input image is empty or not of type UINT8 if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { return false; } + + // Check if the flag values are within valid range if (flag_x < 0 || flag_y < 0 || flag_x + flag_y <= 0 || flag_x >= ksize || flag_y >= ksize) { return false; } + + // Rest of the code for applying the Sobel filter goes here... +} + // Check if the destination image is empty or if its width, height, channel, or data type is different from the source image if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || dst.data_type_ != LDataType::FLOAT32) { + + // If any of the above conditions are true, initialize the destination image with the same width, height, channel, and data type as the source image dst.Init(src.width_, src.height_, src.channel_, LDataType::FLOAT32); } - LiteMat kx, ky; - kx.Init(ksize, 1, 1, LDataType::FLOAT32); - ky.Init(1, ksize, 1, LDataType::FLOAT32); +// Declare two instances of the LiteMat class named kx and ky +LiteMat kx, ky; - GetSobelKernel(kx, flag_x, ksize, scale); - GetSobelKernel(ky, flag_y, ksize, scale); +// Initialize the kx object with the specified parameters: ksize, 1, 1, and LDataType::FLOAT32 +kx.Init(ksize, 1, 1, LDataType::FLOAT32); - return ConvRowCol(src, kx, ky, dst, LDataType::FLOAT32, pad_type); -} +// Initialize the ky object with the specified parameters: 1, ksize, 1, and LDataType::FLOAT32 +ky.Init(1, ksize, 1, LDataType::FLOAT32); +// Call the function GetSobelKernel with the arguments kx, flag_x, ksize, and scale +GetSobelKernel(kx, flag_x, ksize, scale); + +// Call the function GetSobelKernel with the arguments ky, flag_y, ksize, and scale +GetSobelKernel(ky, flag_y, ksize, scale); + +// Return the result of calling the function ConvRowCol with the provided arguments: +// - src: the source image +// - kx: the kernel size in the x-direction +// - ky: the kernel size in the y-direction +// - dst: the destination image +// - LDataType::FLOAT32: the data type of the destination image (float32) +// - pad_type: the padding type used in the convolution operation + +// A static function that returns the value of an element in a 2D grid represented by a 1D vector +// The function takes in the vector, the width and height of the grid, and the x and y coordinates of the element static float GetEdge(const std::vector &temp, int width, int height, int x, int y) { + + // Check if the given coordinates are within the bounds of the grid if (x >= 0 && y >= 0 && x < width && y < height) { + + // If the coordinates are valid, calculate the index of the element in the 1D vector using the formula y * width + x + // and return the value at that index return temp[y * width + x]; + } else { + + // If the coordinates are out of bounds, return -1.0f to indicate an invalid value return -1.0f; } } +// A function to round a floating-point value to the nearest integer + static float Round(float value) { - // rounding if the result is even - // eg. 1.5 -> 2, 2.5 -> 2 + // Calculate the rounded value using the round function from the math library float rnd = round(value); + + // Calculate the floor and ceiling values of the input float rnd_l = floor(value); float rnd_h = ceil(value); + + // Check if the difference between the input and the floor value is exactly 0.5 if (value - rnd_l == 0.5) { + // If the rounded value is even, return the rounded value if (fmod(rnd, 2) == 0) { return rnd; - } else if (value > 0) { + } + // If the input is positive, return the floor value + else if (value > 0) { return rnd_l; - } else { + } + // If the input is negative, return the ceiling value + else { return rnd_h; } } + + // If the difference is not exactly 0.5, return the rounded value return rnd; } +// A static function named NonMaximumSuppression is defined here +// It takes three parameters: gx, gy, and edges, all of type LiteMat +// The fourth parameter, L2gradient, is a boolean flag static void NonMaximumSuppression(const LiteMat &gx, const LiteMat &gy, LiteMat &edges, bool L2gradient) { // NOLINT + + // Initialize the edges LiteMat object with the same dimensions and data type as gx edges.Init(gx.width_, gx.height_, gx.channel_, gx.data_type_); - - const float *gx_ptr = gx; - const float *gy_ptr = gy; - float *edges_ptr = edges; - - int size = gx.height_ * gx.width_; - std::vector temp(size); - for (int i = 0; i < size; i++) { - float gx_value = Round(gx_ptr[i]); - float gy_value = Round(gy_ptr[i]); - if (L2gradient) { - temp[i] = sqrt(gx_value * gx_value + gy_value * gy_value); - } else { - temp[i] = abs(gx_value) + abs(gy_value); - } - } - - for (int y = 0; y < gx.height_; y++) { - for (int x = 0; x < gx.width_; x++) { - float gx_value = Round(gx_ptr[y * gx.width_ + x]); - float gy_value = Round(gy_ptr[y * gx.width_ + x]); - - float gx_value_abs = std::abs(gx_value); - float gy_value_abs = std::abs(gy_value); - float angle_value = atan2(gy_value_abs, gx_value_abs); - float edge_value = temp[y * gx.width_ + x]; - float edge_pre, edge_nex; - if (angle_value < kAngle22_5 || angle_value > kAngle67_5) { - if (angle_value < kAngle22_5) { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y); - } else { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x, y + 1); - } - if (edge_value > edge_pre && edge_value >= edge_nex) { - edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; - } else { - edges_ptr[y * gx.width_ + x] = 0.f; - } - } else { - if (gx_value * gy_value < 0) { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x + 1, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x - 1, y + 1); - } else { - edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y - 1); - edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y + 1); - } - if (edge_value > edge_pre && edge_value > edge_nex) { - edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; - } else { - edges_ptr[y * gx.width_ + x] = 0.f; - } - } - } - } } -static void Hysteresis(const LiteMat &edges, uint8_t *dst, double low_thresh, double high_thresh) { - const float *edges_ptr = edges; +// Create a constant pointer to a float variable named gx_ptr and initialize it with the address of gx +const float *gx_ptr = gx; - int size = edges.height_ * edges.width_; - std::vector stack; - std::vector buffer(size); - int buffer_step = edges.width_; - for (int y = 0; y < edges.height_; y++) { - for (int x = 0; x < edges.width_; x++) { - int pos = y * edges.width_ + x; - float edge_value = edges_ptr[pos]; - if (edge_value > high_thresh) { - buffer[pos] = kCertainBorder; - stack.push_back(pos); - } else if (edge_value <= low_thresh) { - buffer[pos] = kNotBorder; - } else { - buffer[pos] = kUncertainBorder; - } +// Create a constant pointer to a float variable named gy_ptr and initialize it with the address of gy +const float *gy_ptr = gy; + +// Create a pointer to a float variable named edges_ptr and initialize it with the address of edges + +// Calculate the total number of elements in the grid by multiplying the height and width +int size = gx.height_ * gx.width_; + +// Create a temporary vector of floats with size equal to the total number of elements +std::vector temp(size); + +// Iterate over each element in the grid +for (int i = 0; i < size; i++) { + + // Round the values of gx and gy to the nearest integer + float gx_value = Round(gx_ptr[i]); + float gy_value = Round(gy_ptr[i]); + + // Check if L2gradient flag is set + if (L2gradient) { + + // Calculate the magnitude of the gradient using the Euclidean distance formula + temp[i] = sqrt(gx_value * gx_value + gy_value * gy_value); + } else { + + // Calculate the magnitude of the gradient using the sum of absolute values + temp[i] = abs(gx_value) + abs(gy_value); } +} + +// Iterate over each row of the gx and gy matrices +for (int y = 0; y < gx.height_; y++) { + + // Iterate over each column of the gx and gy matrices + for (int x = 0; x < gx.width_; x++) { + + // Calculate the index of the current element in the gx and gy matrices + int index = y * gx.width_ + x; + + // Round the gx and gy values at the current index + float gx_value = Round(gx_ptr[index]); + float gy_value = Round(gy_ptr[index]); + +// Calculate the absolute values of gx_value and gy_value +float gx_value_abs = std::abs(gx_value); +float gy_value_abs = std::abs(gy_value); + +// Calculate the angle value using atan2 function +float angle_value = atan2(gy_value_abs, gx_value_abs); + +// Get the edge value at the current position +float edge_value = temp[y * gx.width_ + x]; + +// Declare variables for storing edge values of neighboring pixels +float edge_pre, edge_nex; + +// Check the angle value to determine the direction of neighboring pixels +if (angle_value < kAngle22_5 || angle_value > kAngle67_5) { + // If the angle is less than kAngle22_5 or greater than kAngle67_5 + if (angle_value < kAngle22_5) { + // If the angle is less than kAngle22_5, get the edge values of the left and right neighboring pixels + edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y); + edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y); + } else { + // If the angle is greater than kAngle67_5, get the edge values of the top and bottom neighboring pixels + edge_pre = GetEdge(temp, gx.width_, gx.height_, x, y - 1); + edge_nex = GetEdge(temp, gx.width_, gx.height_, x, y + 1); } + // Compare the edge value with the neighboring edge values and update the edges_ptr accordingly + if (edge_value > edge_pre && edge_value >= edge_nex) { + edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; + } else { + edges_ptr[y * gx.width_ + x] = 0.f; + } +} else { + // If the angle is between kAngle22_5 and kAngle67_5 + if (gx_value * gy_value < 0) { + // If the product of gx_value and gy_value is negative, get the edge values of the top-right and bottom-left neighboring pixels + edge_pre = GetEdge(temp, gx.width_, gx.height_, x + 1, y - 1); + edge_nex = GetEdge(temp, gx.width_, gx.height_, x - 1, y + 1); + } else { + // If the product of gx_value and gy_value is positive, get the edge values of the top-left and bottom-right neighboring pixels + edge_pre = GetEdge(temp, gx.width_, gx.height_, x - 1, y - 1); + edge_nex = GetEdge(temp, gx.width_, gx.height_, x + 1, y + 1); + } + + // Compare the edge value with the neighboring edge values and update the edges_ptr accordingly + if (edge_value > edge_pre && edge_value > edge_nex) { + edges_ptr[y * gx.width_ + x] = temp[y * gx.width_ + x]; + } else { + edges_ptr[y * gx.width_ + x] = 0.f; + } +} +} +} +} + +// A static function named "Hysteresis" that takes in a LiteMat object called "edges", a pointer to a uint8_t called "dst", +// and two double values called "low_thresh" and "high_thresh" + +static void Hysteresis(const LiteMat &edges, uint8_t *dst, double low_thresh, double high_thresh) { + + // Create a constant pointer to a float called "edges_ptr" and initialize it with the data from the "edges" LiteMat object + const float *edges_ptr = edges; +} + +// Calculate the total number of elements in the edges matrix +int size = edges.height_ * edges.width_; + +// Create an empty stack to store positions of certain border pixels +std::vector stack; + +// Create a buffer vector with the same size as the edges matrix +std::vector buffer(size); + +// Calculate the step size for accessing elements in the buffer vector +int buffer_step = edges.width_; + +// Iterate over each pixel in the edges matrix +for (int y = 0; y < edges.height_; y++) { + for (int x = 0; x < edges.width_; x++) { + // Calculate the position of the current pixel in the edges matrix + int pos = y * edges.width_ + x; + + // Get the edge value of the current pixel + float edge_value = edges_ptr[pos]; + + // Check if the edge value is greater than the high threshold + if (edge_value > high_thresh) { + // Set the buffer value at the current position to indicate a certain border pixel + buffer[pos] = kCertainBorder; + + // Add the current position to the stack + stack.push_back(pos); + } + // Check if the edge value is less than or equal to the low threshold + else if (edge_value <= low_thresh) { + // Set the buffer value at the current position to indicate a non-border pixel + buffer[pos] = kNotBorder; + } + // If the edge value is between the low and high thresholds + else { + // Set the buffer value at the current position to indicate an uncertain border pixel + buffer[pos] = kUncertainBorder; + } + } +} + + // While the stack is not empty while (!stack.empty()) { + // Get the last element from the stack int pos = stack.back(); stack.pop_back(); + + // Calculate the y and x coordinates from the position int y = static_cast(pos / buffer_step); int x = pos % buffer_step; + + // Iterate over the neighboring cells for (int i = -1; i < 2; i++) { for (int j = -1; j < 2; j++) { + // Calculate the coordinates of the next cell int next_y = y + i; int next_x = x + j; + + // Check if the next cell is out of bounds or the same as the current cell if (next_y < 0 || next_x < 0 || next_y >= edges.height_ || next_x >= edges.width_ || (next_y == y && next_x == x)) { + // Continue to the next iteration of the inner loop continue; } + + // Calculate the position of the next cell in the buffer int next = next_y * buffer_step + next_x; + + // Check if the next cell is an uncertain border if (buffer[next] == kUncertainBorder) { + // Set the next cell as a certain border and add it to the stack buffer[next] = kCertainBorder; stack.push_back(next); } @@ -231,44 +443,90 @@ static void Hysteresis(const LiteMat &edges, uint8_t *dst, double low_thresh, do } } + // Iterate over the elements of the buffer array for (int i = 0; i < size; i++) { + + // Check if the current element of the buffer array is equal to kCertainBorder if (buffer[i] == kCertainBorder) { + + // If the condition is true, set the corresponding element of the dst array to 255 dst[i] = 255; } else { + + // If the condition is false, set the corresponding element of the dst array to 0 dst[i] = 0; } } } -bool Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize, // NOLINT - bool L2gradient) { +// Function to apply Canny edge detection on an input image + +bool Canny(const LiteMat &src, LiteMat &dst, double low_thresh, double high_thresh, int ksize, bool L2gradient) { + + // Check if the input image is empty or not of the correct type and channel count if (src.IsEmpty() || src.data_type_ != LDataType::UINT8 || src.channel_ != 1) { return false; } + + // Check if the threshold values are valid if (low_thresh < 0 || high_thresh < 0 || low_thresh > high_thresh) { return false; } + + // Check if the kernel size is valid if (ksize % 2 == 0 || ksize < 3 || ksize > 7) { return false; } + + // Check if the destination image is empty or not of the same size, channel count, and data type as the source image if (dst.IsEmpty() || dst.width_ != src.width_ || dst.height_ != src.height_ || dst.channel_ != src.channel_ || dst.data_type_ != src.data_type_) { + // Initialize the destination image with the same size, channel count, and data type as the source image dst.Init(src.width_, src.height_, src.channel_, src.data_type_); } - - double scale = ksize == 7 ? 1 / 16.0 : 1.0; - low_thresh *= scale; - high_thresh *= scale; - - LiteMat gx, gy; - Sobel(src, gx, 1, 0, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); - Sobel(src, gy, 0, 1, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); - - LiteMat edges; - NonMaximumSuppression(gx, gy, edges, L2gradient); - - Hysteresis(edges, dst, low_thresh, high_thresh); - return true; } + +// Declare a variable named "scale" of type double and initialize it with the value 1/16.0 if the value of "ksize" is equal to 7, otherwise initialize it with the value 1.0 +double scale = ksize == 7 ? 1 / 16.0 : 1.0; + +// Multiply the value of "low_thresh" by the value of "scale" +low_thresh *= scale; + +// Multiply the value of "high_thresh" by the value of "scale" +high_thresh *= scale; + +// Declare two LiteMat objects named gx and gy +LiteMat gx, gy; + +// Apply the Sobel operator to the source image 'src' in the x-direction (1, 0) +// The result will be stored in the gx LiteMat object +// The Sobel operator is applied with a kernel size of 'ksize' +// The scale parameter is used to scale the computed gradients +// The PaddBorderType::PADD_BORDER_REPLICATE option is used to handle border pixels +Sobel(src, gx, 1, 0, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); + +// Apply the Sobel operator to the source image 'src' in the y-direction (0, 1) +// The result will be stored in the gy LiteMat object +// The Sobel operator is applied with a kernel size of 'ksize' +// The scale parameter is used to scale the computed gradients +// The PaddBorderType::PADD_BORDER_REPLICATE option is used to handle border pixels +Sobel(src, gy, 0, 1, ksize, scale, PaddBorderType::PADD_BORDER_REPLICATE); + +// Declare a variable named "edges" of type LiteMat +LiteMat edges; + +// Call the NonMaximumSuppression function with the parameters gx, gy, edges, and L2gradient +NonMaximumSuppression(gx, gy, edges, L2gradient); + +// Call the Hysteresis function with the provided parameters: edges, dst, low_thresh, high_thresh +Hysteresis(edges, dst, low_thresh, high_thresh); + +// Return true to indicate successful execution of the function +return true; +} + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc index 5f5a502c9b8..cac547b70f7 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/gaussian_blur.cc @@ -14,74 +14,163 @@ * limitations under the License. */ +// Include the math library header file for using mathematical functions and constants in the code #include +// Include the header file "lite_mat.h" from the "lite_cv" library #include "lite_cv/lite_mat.h" + +// Include the header file "image_process.h" from the "lite_cv" library #include "lite_cv/image_process.h" +// Check if the ENABLE_ANDROID macro is defined #ifdef ENABLE_ANDROID -#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) -#define USE_NEON -#include -#endif + + // Check if any of the ARM-related macros are defined + #if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) + + // Define the USE_NEON macro + #define USE_NEON + + // Include the ARM NEON header file + #include + + // End of ARM-related macro check + #endif + +// End of ENABLE_ANDROID macro check #endif +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -static void GetGaussianKernel(float *kernel, int size, double sigma) { - int n = (size - 1) / 2; - std::vector buffer(n); - float sum = 0; - for (int i = 0; i < n; i++) { - int x = i - n; - float g = exp(-0.5 * x * x / (sigma * sigma)); - buffer[i] = g; - sum += g; - } - sum = sum * 2 + 1; - if (size % 2 == 0) { - sum += 1; - } - - const float scale = 1. / sum; - for (int i = 0; i < n; i++) { - float g = buffer[i] * scale; - kernel[i] = g; - kernel[size - 1 - i] = g; - } - kernel[n] = scale; - if (size % 2 == 0) { - kernel[n + 1] = scale; + + // Define the namespace "dataset" within the "mindspore" namespace + namespace dataset { + + // Define a static function "GetGaussianKernel" that takes in a float array "kernel", an integer "size", and a double "sigma" + static void GetGaussianKernel(float *kernel, int size, double sigma) { + + // Calculate the value of "n" by subtracting 1 from "size" and dividing the result by 2 + int n = (size - 1) / 2; + + // Create a vector "buffer" of floats with size "n" + std::vector buffer(n); + + // Initialize the variable "sum" to 0 + float sum = 0; + + // Iterate from 0 to "n" (exclusive) + for (int i = 0; i < n; i++) { + + // Calculate the value of "x" by subtracting "n" from "i" + int x = i - n; + + // Calculate the value of "g" using the Gaussian function formula + float g = exp(-0.5 * x * x / (sigma * sigma)); + + // Store the value of "g" in the "buffer" vector at index "i" + buffer[i] = g; + + // Add the value of "g" to the variable "sum" + sum += g; + } + + // Multiply the value of "sum" by 2 and add 1 to it + sum = sum * 2 + 1; + + // Check if "size" is even + if (size % 2 == 0) { + + // If "size" is even, add 1 to the value of "sum" + sum += 1; + } + } } } -bool GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector &ksize, double sigmaX, // NOLINT - double sigmaY, PaddBorderType pad_type) { +// Calculate the scaling factor by dividing 1 by the sum +const float scale = 1. / sum; + +// Iterate over the elements of the buffer array +for (int i = 0; i < n; i++) { + + // Multiply each element of the buffer array by the scaling factor + float g = buffer[i] * scale; + + // Assign the scaled value to the corresponding element in the kernel array + kernel[i] = g; + + // Assign the scaled value to the mirrored element in the kernel array + kernel[size - 1 - i] = g; +} + +// Assign the scaling factor to the last element of the kernel array +kernel[n] = scale; + +// Check if the size of the kernel array is even +if (size % 2 == 0) { + + // If the size is even, assign the scaling factor to the second last element of the kernel array + kernel[n + 1] = scale; +} + +// Function to apply Gaussian blur to an input image +bool GaussianBlur(const LiteMat &src, LiteMat &dst, const std::vector &ksize, double sigmaX, double sigmaY, PaddBorderType pad_type) { + + // Check if the input image is empty or not of type UINT8 if (src.IsEmpty() || src.data_type_ != LDataType::UINT8) { return false; } + + // Check if the kernel size is valid (2D, odd dimensions, and positive values) if (ksize.size() != 2 || ksize[0] <= 0 || ksize[1] <= 0 || ksize[0] % 2 != 1 || ksize[1] % 2 != 1) { return false; } + + // Check if the sigmaX value is valid (positive) if (sigmaX <= 0) { return false; } + + // Check if the sigmaY value is valid (positive), if not, set it equal to sigmaX if (sigmaY <= 0) { sigmaY = sigmaX; } + + // Check if the kernel size is 1x1, if so, assign the input image to the output image and return true if (ksize[0] == 1 && ksize[1] == 1) { dst = src; return true; } +} - LiteMat kx, ky; - kx.Init(ksize[0], 1, 1, LDataType::FLOAT32); - ky.Init(1, ksize[1], 1, LDataType::FLOAT32); +// Declare two LiteMat objects named kx and ky +LiteMat kx, ky; - GetGaussianKernel(kx, ksize[0], sigmaX); - GetGaussianKernel(ky, ksize[1], sigmaY); +// Initialize the kx object with the specified parameters: +// - Width: ksize[0] +// - Height: 1 +// - Channel: 1 +// - Data type: FLOAT32 +kx.Init(ksize[0], 1, 1, LDataType::FLOAT32); +// Initialize the ky object with the specified parameters: +// - Width: 1 +// - Height: ksize[1] +// - Channel: 1 +// - Data type: FLOAT32 +ky.Init(1, ksize[1], 1, LDataType::FLOAT32); + +// Call the function GetGaussianKernel with the parameters kx, ksize[0], and sigmaX +GetGaussianKernel(kx, ksize[0], sigmaX); + +// Call the function GetGaussianKernel with the parameters ky, ksize[1], and sigmaY +GetGaussianKernel(ky, ksize[1], sigmaY); + + // Return the result of calling the ConvRowCol function with the provided arguments return ConvRowCol(src, kx, ky, dst, src.data_type_, pad_type); } + } // namespace dataset -} // namespace mindspore + +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/lite_mat.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/lite_mat.cc index 42cab4c6a12..bf279f1f715 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/lite_mat.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_cv/lite_mat.cc @@ -15,32 +15,75 @@ */ #include "minddata/dataset/kernels/image/lite_cv/lite_mat.h" +// Include the header for numeric limits, which provides information about the properties of arithmetic types #include + +// Include the header for algorithms, which provides a collection of functions for performing various operations on sequences #include + +// Include the header for mathematical functions, which provides a collection of functions for performing common mathematical operations #include + +// Check if the ENABLE_NEON macro is defined #ifdef ENABLE_NEON + +// Include the header for ARM NEON intrinsics, which provides functions for SIMD (Single Instruction, Multiple Data) operations on ARM processors #include + #endif +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Constructor for the LiteMat class LiteMat::LiteMat() { + + // Initialize the data pointer to nullptr data_ptr_ = nullptr; + + // Initialize the element size to 0 elem_size_ = 0; + + // Initialize the width, height, and channel dimensions to 0 width_ = 0; height_ = 0; channel_ = 0; + + // Initialize the channel step to 0 c_step_ = 0; + + // Initialize the number of dimensions to 0 dims_ = 0; + + // Initialize the size of the LiteMat to 0 size_ = 0; + + // Set the data type to UINT8 data_type_ = LDataType::UINT8; + + // Initialize the reference count pointer to nullptr ref_count_ = nullptr; + + // Set the steps to 0 setSteps(0, 0, 0); + + // Set the release flag to false release_flag_ = false; } +// Constructor for the LiteMat class LiteMat::LiteMat(int width, LDataType data_type) { + + // Initialize all member variables to their default values data_ptr_ = nullptr; elem_size_ = 0; width_ = 0; @@ -53,26 +96,56 @@ LiteMat::LiteMat(int width, LDataType data_type) { size_ = 0; setSteps(0, 0, 0); release_flag_ = false; + + // Call the Init function to initialize the LiteMat object with the given width and data type Init(width, data_type); } +// Constructor for the LiteMat class LiteMat::LiteMat(int width, int height, LDataType data_type) { + + // Initialize data pointer to nullptr data_ptr_ = nullptr; + + // Initialize element size to 0 elem_size_ = 0; + + // Initialize width and height to 0 width_ = 0; height_ = 0; + + // Initialize channel count to 0 channel_ = 0; + + // Initialize channel step to 0 c_step_ = 0; + + // Initialize number of dimensions to 0 dims_ = 0; + + // Initialize data type to UINT8 data_type_ = LDataType::UINT8; + + // Initialize reference count pointer to nullptr ref_count_ = nullptr; + + // Initialize size to 0 size_ = 0; + + // Set steps to 0 setSteps(0, 0, 0); + + // Set release flag to false release_flag_ = false; + + // Initialize the LiteMat object with the given width, height, and data type Init(width, height, data_type); } +// Constructor for the LiteMat class LiteMat::LiteMat(int width, int height, void *p_data, LDataType data_type) { + + // Initialize member variables to default values data_ptr_ = nullptr; elem_size_ = 0; width_ = 0; @@ -85,26 +158,54 @@ LiteMat::LiteMat(int width, int height, void *p_data, LDataType data_type) { size_ = 0; setSteps(0, 0, 0); release_flag_ = false; + + // Call the Init function to initialize the LiteMat object with the provided parameters Init(width, height, p_data, data_type); } +// Constructor for the LiteMat class LiteMat::LiteMat(int width, int height, int channel, LDataType data_type) { + + // Initialize data pointer to nullptr data_ptr_ = nullptr; + + // Initialize element size to 0 elem_size_ = 0; + + // Initialize width, height, and channel to 0 width_ = 0; height_ = 0; channel_ = 0; + + // Initialize c_step_ to 0 c_step_ = 0; + + // Initialize dims_ to 0 dims_ = 0; + + // Initialize data_type_ to LDataType::UINT8 data_type_ = LDataType::UINT8; + + // Initialize ref_count_ to nullptr ref_count_ = nullptr; + + // Initialize size_ to 0 size_ = 0; + + // Set steps to 0 setSteps(0, 0, 0); + + // Set release_flag_ to false release_flag_ = false; + + // Call the Init function to initialize the LiteMat object with the provided parameters Init(width, height, channel, data_type); } +// Constructor for the LiteMat class LiteMat::LiteMat(int width, int height, int channel, void *p_data, LDataType data_type) { + + // Initialize member variables to default values data_ptr_ = nullptr; elem_size_ = 0; width_ = 0; @@ -117,183 +218,397 @@ LiteMat::LiteMat(int width, int height, int channel, void *p_data, LDataType dat size_ = 0; setSteps(0, 0, 0); release_flag_ = false; + + // Call the Init function to initialize the LiteMat object with the provided parameters Init(width, height, channel, p_data, data_type); } -LiteMat::~LiteMat() { Release(); } +// Destructor for the LiteMat class +LiteMat::~LiteMat() { + // Call the Release() function to release any resources held by the object + Release(); +} + +// Implementation of the addRef function in the LiteMat class + +// This function takes a pointer to an integer (p) and a value to add (value) int LiteMat::addRef(int *p, int value) { + + // Store the current value pointed to by p in a variable v int v = *p; + + // Add the value to the integer pointed to by p *p += value; + + // Return the original value stored in v return v; } +// Implementation of the copy constructor for the LiteMat class + LiteMat::LiteMat(const LiteMat &m) { - data_ptr_ = m.data_ptr_; - elem_size_ = m.elem_size_; - width_ = m.width_; - height_ = m.height_; - channel_ = m.channel_; - c_step_ = m.c_step_; - dims_ = m.dims_; - data_type_ = m.data_type_; - ref_count_ = m.ref_count_; - size_ = m.size_; - release_flag_ = m.release_flag_; - setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); - if (ref_count_) { - addRef(ref_count_, 1); - } + // Copy the data pointer from the source LiteMat object + data_ptr_ = m.data_ptr_; + + // Copy the element size from the source LiteMat object + elem_size_ = m.elem_size_; + + // Copy the width from the source LiteMat object + width_ = m.width_; + + // Copy the height from the source LiteMat object + height_ = m.height_; + + // Copy the number of channels from the source LiteMat object + channel_ = m.channel_; + + // Copy the channel step size from the source LiteMat object + c_step_ = m.c_step_; + + // Copy the number of dimensions from the source LiteMat object + dims_ = m.dims_; + + // Copy the data type from the source LiteMat object + data_type_ = m.data_type_; + + // Copy the reference count from the source LiteMat object + ref_count_ = m.ref_count_; + + // Copy the size from the source LiteMat object + size_ = m.size_; + + // Copy the release flag from the source LiteMat object + release_flag_ = m.release_flag_; + + // Set the steps of the LiteMat object to the steps of the source LiteMat object + setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); + + // If the reference count is non-zero, increment it by 1 + if (ref_count_) { + addRef(ref_count_, 1); + } } +// A member function of the LiteMat class that sets the steps for each dimension of the matrix void LiteMat::setSteps(int c0, int c1, int c2) { + + // Set the step value for the first dimension to c0 steps_[0] = c0; + + // Set the step value for the second dimension to c1 steps_[1] = c1; + + // Set the step value for the third dimension to c2 steps_[2] = c2; } +// Overload the assignment operator for the LiteMat class, taking a constant reference to another LiteMat object as the parameter LiteMat &LiteMat::operator=(const LiteMat &m) { + + // Check if the current object is the same as the parameter object if (this == &m) { + + // If they are the same, return a reference to the current object return *this; } + + // Continue with the assignment operation + // ... +} + // Check if the reference count of object m is non-zero if (m.ref_count_) { + + // If the reference count is non-zero, call the addRef function to increment the reference count by 1 addRef(m.ref_count_, 1); } + // Call the Release() function to release any resources held by the current object Release(); - data_ptr_ = m.data_ptr_; - elem_size_ = m.elem_size_; - width_ = m.width_; - height_ = m.height_; - channel_ = m.channel_; - c_step_ = m.c_step_; - dims_ = m.dims_; - data_type_ = m.data_type_; - ref_count_ = m.ref_count_; - setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); - size_ = m.size_; - release_flag_ = m.release_flag_; - return *this; -} + // Copy the data pointer from the source object to the current object + data_ptr_ = m.data_ptr_; + + // Copy the element size from the source object to the current object + elem_size_ = m.elem_size_; + + // Copy the width from the source object to the current object + width_ = m.width_; + + // Copy the height from the source object to the current object + height_ = m.height_; + + // Copy the channel from the source object to the current object + channel_ = m.channel_; + + // Copy the c_step from the source object to the current object + c_step_ = m.c_step_; + + // Copy the dims from the source object to the current object + dims_ = m.dims_; + + // Copy the data type from the source object to the current object + data_type_ = m.data_type_; + + // Copy the reference count from the source object to the current object + ref_count_ = m.ref_count_; + + // Set the steps of the current object using the steps of the source object + setSteps(m.steps_[0], m.steps_[1], m.steps_[2]); + + // Copy the size from the source object to the current object + size_ = m.size_; + + // Copy the release flag from the source object to the current object + release_flag_ = m.release_flag_; + + // Return a reference to the current object + return *this; + +// Initialize the LiteMat object with the given width and data type void LiteMat::Init(int width, LDataType data_type) { + + // Release any existing resources held by the LiteMat object Release(); + + // Set the data type of the LiteMat object data_type_ = data_type; + + // Initialize the element size based on the data type InitElemSize(data_type); + + // Set the width, height, and channel of the LiteMat object width_ = width; dims_ = 1; height_ = 1; channel_ = 1; + + // Check if the LiteMat object is valid if (!CheckLiteMat()) { + + // If not valid, release any resources held by the LiteMat object and return Release(); return; } + + // Set the step size for the LiteMat object c_step_ = width; + + // Calculate the total size of the LiteMat object size_ = c_step_ * elem_size_; + + // Allocate memory for the data pointer of the LiteMat object data_ptr_ = AlignMalloc(size_); + + // Create a new reference count for the LiteMat object and set it to 1 ref_count_ = new int[1]; *ref_count_ = 1; + + // Set the step size for the LiteMat object steps_[0] = elem_size_; } +// Initialize the LiteMat object with the given width, height, and data type void LiteMat::Init(int width, int height, LDataType data_type) { + // Release any existing resources Release(); + + // Set the data type of the LiteMat object data_type_ = data_type; + + // Initialize the element size based on the data type InitElemSize(data_type); + + // Set the width and height of the LiteMat object width_ = width; height_ = height; + + // Set the number of dimensions and channels dims_ = 2; channel_ = 1; + + // Check if the LiteMat object is valid if (!CheckLiteMat()) { + // If not valid, release any resources and return Release(); return; } + + // Calculate the step size for each dimension c_step_ = width_ * height_; + + // Calculate the total size of the LiteMat object size_ = c_step_ * elem_size_; + + // Allocate memory for the data pointer data_ptr_ = AlignMalloc(size_); + + // Initialize the reference count to 1 ref_count_ = new int[1]; *ref_count_ = 1; + + // Set the step size for each dimension steps_[1] = elem_size_; steps_[0] = width_ * steps_[1]; } +// Initialize the LiteMat object with the given width, height, data pointer, and data type void LiteMat::Init(int width, int height, void *p_data, LDataType data_type) { + // Set the data type of the LiteMat object data_type_ = data_type; + + // Initialize the element size based on the data type InitElemSize(data_type); + + // Set the width and height of the LiteMat object width_ = width; height_ = height; + + // Set the number of dimensions and channels of the LiteMat object dims_ = 2; channel_ = 1; + + // Check if the LiteMat object is valid if (!CheckLiteMat()) { + // If not valid, release the object and return Release(); return; } + + // Calculate the step size for each channel c_step_ = height_ * width_; + + // Calculate the total size of the LiteMat object size_ = c_step_ * channel_ * elem_size_; + + // Set the data pointer of the LiteMat object data_ptr_ = p_data; + + // Set the reference count to nullptr ref_count_ = nullptr; + + // Set the step size for each dimension steps_[1] = elem_size_; steps_[0] = width_ * steps_[1]; } +// Initialize the LiteMat object with the given parameters void LiteMat::Init(int width, int height, int channel, const LDataType &data_type, bool align_memory) { + + // Release any existing resources held by the LiteMat object Release(); + + // Set the data type of the LiteMat object data_type_ = data_type; + + // Initialize the element size based on the data type InitElemSize(data_type); + + // Set the width, height, and number of channels of the LiteMat object width_ = width; height_ = height; dims_ = 3; channel_ = channel; + + // Check if the LiteMat object is valid, and release resources if not if (!CheckLiteMat()) { Release(); return; } + + // Calculate the step size for each channel based on whether memory alignment is required if (align_memory) { c_step_ = ((height_ * width_ * elem_size_ + ALIGN - 1) & (-ALIGN)) / elem_size_; } else { c_step_ = height_ * width_; } + + // Calculate the total size of the LiteMat object size_ = c_step_ * channel_ * elem_size_; + + // Allocate aligned memory for the data pointer of the LiteMat object data_ptr_ = AlignMalloc(size_); + + // Initialize the reference count to 1 ref_count_ = new int[1]; *ref_count_ = 1; - - steps_[2] = elem_size_; - steps_[1] = channel * steps_[2]; - steps_[0] = width_ * steps_[1]; } +// Set the value of the third element in the steps_ array to elem_size_ +steps_[2] = elem_size_; + +// Set the value of the second element in the steps_ array to channel multiplied by the value of the third element in the steps_ array +steps_[1] = channel * steps_[2]; + +// Set the value of the first element in the steps_ array to width_ multiplied by the value of the second element in the steps_ array +steps_[0] = width_ * steps_[1]; + +// Initialize the LiteMat object with the given parameters void LiteMat::Init(int width, int height, int channel, void *p_data, LDataType data_type) { + // Set the data type of the LiteMat object data_type_ = data_type; + + // Initialize the element size based on the data type InitElemSize(data_type); + + // Set the width, height, and channel of the LiteMat object width_ = width; height_ = height; dims_ = 3; channel_ = channel; + + // Check if the LiteMat object is valid, if not, release it and return if (!CheckLiteMat()) { Release(); return; } + + // Calculate the step size for each dimension c_step_ = height_ * width_; size_ = c_step_ * channel_ * elem_size_; + + // Set the data pointer of the LiteMat object data_ptr_ = p_data; + + // Set the reference count to nullptr ref_count_ = nullptr; + + // Set the step size for each dimension steps_[2] = elem_size_; steps_[1] = channel * steps_[2]; steps_[0] = width_ * steps_[1]; } -bool LiteMat::IsEmpty() const { return data_ptr_ == nullptr || c_step_ * channel_ == 0; } +// Check if the LiteMat object is empty +bool LiteMat::IsEmpty() const { + + // Return true if the data pointer is nullptr or if the product of c_step_ and channel_ is 0 + return data_ptr_ == nullptr || c_step_ * channel_ == 0; +} + +// Release function for the LiteMat class void LiteMat::Release() { + + // Check if the reference count is non-zero and if decrementing it by 1 results in a count of 1 if (ref_count_ && (addRef(ref_count_, -1) == 1)) { + + // Check if the data pointer is not null if (data_ptr_) { + + // Free the aligned memory block pointed to by data_ptr_ AlignFree(data_ptr_); } + + // Delete the dynamically allocated array pointed to by ref_count_ delete[] ref_count_; } + + // Set the data pointer to nullptr data_ptr_ = nullptr; + + // Reset the element size, width, height, channel, c_step, ref_count_, size_ to their default values elem_size_ = 0; width_ = 0; height_ = 0; @@ -301,447 +616,900 @@ void LiteMat::Release() { c_step_ = 0; ref_count_ = nullptr; size_ = 0; + + // Call the setSteps function to set the step values to 0 setSteps(0, 0, 0); } +// Function to allocate aligned memory of a given size void *LiteMat::AlignMalloc(unsigned int size) { + + // Calculate the total length required for alignment unsigned int length = sizeof(void *) + ALIGN - 1; + + // Check if the requested size exceeds the maximum limit if (size > std::numeric_limits::max() - length) { return nullptr; } + + // Allocate memory of size + length void *p_raw = reinterpret_cast(malloc(size + length)); + + // Check if memory allocation was successful if (p_raw) { + + // Set the release flag to true release_flag_ = true; - void **p_algin = reinterpret_cast(((size_t)(p_raw) + length) & ~(ALIGN - 1)); - p_algin[-1] = p_raw; - return p_algin; + + // Calculate the aligned memory address + void **p_align = reinterpret_cast(((size_t)(p_raw) + length) & ~(ALIGN - 1)); + + // Store the original memory address in the previous location + p_align[-1] = p_raw; + + // Return the aligned memory address + return p_align; } + + // Return nullptr if memory allocation failed return nullptr; } +// A member function of the LiteMat class that frees aligned memory + void LiteMat::AlignFree(void *ptr) { - if (release_flag_) { - (void)free(reinterpret_cast(ptr)[-1]); - ptr = nullptr; - release_flag_ = false; - } + // Check if the release flag is set + if (release_flag_) { + // Free the memory block by accessing the pointer to the original allocated memory + // The pointer is obtained by subtracting the size of a pointer from the given pointer + (void)free(reinterpret_cast(ptr)[-1]); + + // Set the pointer to nullptr to avoid dangling pointer + ptr = nullptr; + + // Reset the release flag to false + release_flag_ = false; + } } -inline void LiteMat::InitElemSize(LDataType data_type) { elem_size_ = data_type.SizeInBytes(); } +// Define an inline function named InitElemSize in the LiteMat class +inline void LiteMat::InitElemSize(LDataType data_type) { + // Set the elem_size_ member variable of the LiteMat class to the size in bytes of the given data_type + elem_size_ = data_type.SizeInBytes(); +} + +// Function to check if the LiteMat object is valid bool LiteMat::CheckLiteMat() { + + // Check if the width, height, channel, and element size are all greater than 0 if (width_ <= 0 || height_ <= 0 || channel_ <= 0 || elem_size_ <= 0) { return false; } + + // Check if the height is not equal to 1 and if it is greater than the maximum integer value divided by the width if (height_ != 1 && height_ > std::numeric_limits::max() / width_) { return false; } + + // Calculate the area (height * width) int area = height_ * width_; + + // Check if the channel is not equal to 1 and if it is greater than the maximum integer value divided by the area if (channel_ != 1 && channel_ > std::numeric_limits::max() / area) { return false; } + + // Calculate the size (area * channel) int size = area * channel_; + + // Check if the element size is greater than the maximum integer value divided by the size if (elem_size_ > std::numeric_limits::max() / size) { return false; } + + // If all the checks pass, return true to indicate that the LiteMat object is valid return true; } +// Function to get a region of interest (ROI) from the LiteMat object bool LiteMat::GetROI(int x, int y, int w, int h, LiteMat &m) { + + // Check if the ROI coordinates are valid if (x < 0 || y < 0 || x > width_ - w || h > height_ - y || w <= 0 || h <= 0) { - return false; - } - if (!m.IsEmpty()) { - m.Release(); + return false; // Return false if the ROI is invalid } + // Check if the provided LiteMat object is not empty + if (!m.IsEmpty()) { + m.Release(); // Release the memory of the LiteMat object + } + // ... +} + + // Check if the ref_count_ variable is not zero if (ref_count_) { + + // If it is not zero, call the addRef function and pass ref_count_ and 1 as arguments addRef(ref_count_, 1); } + // Set the height of the matrix m to h m.height_ = h; + + // Set the width of the matrix m to w m.width_ = w; + + // Set the dimensions of the matrix m to dims_ m.dims_ = dims_; + + // Set the element size of the matrix m to elem_size_ m.elem_size_ = elem_size_; + + // Set the data pointer of the matrix m to the memory address of data_ptr_ plus the calculated offset m.data_ptr_ = reinterpret_cast(data_ptr_) + y * steps_[0] + x * elem_size_ * channel_; + + // Set the channel of the matrix m to channel_ m.channel_ = channel_; + + // Set the c_step of the matrix m to c_step_ m.c_step_ = c_step_; + + // Set the data type of the matrix m to data_type_ m.data_type_ = data_type_; + + // Set the reference count of the matrix m to ref_count_ m.ref_count_ = ref_count_; + + // Set the steps of the matrix m using the values from steps_[0], steps_[1], and steps_[2] m.setSteps(steps_[0], steps_[1], steps_[2]); + + // Return true to indicate successful completion of the function return true; } +// A template function that subtracts the elements of two arrays and stores the result in a third array +// The function takes pointers to the source arrays (src0 and src1), a pointer to the destination array (dst), +// and the total size of the arrays (total_size) template inline void SubtractImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { + + // Iterate over each element of the arrays using a for loop for (int64_t i = 0; i < total_size; i++) { + + // Subtract the corresponding elements of src0 and src1 and store the result in dst dst[i] = src0[i] - src1[i]; } } +// Template specialization for SubtractImpl function with uint8_t data type + +// Inline function definition for SubtractImpl template <> inline void SubtractImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { int64_t x = 0; + + // Check if NEON SIMD instructions are enabled #ifdef ENABLE_NEON const int64_t step = 32; + + // Loop through the data in steps of 32 bytes for (; x <= total_size - step; x += step) { + // Load 16 bytes of data from src0 and src1 into NEON registers uint8x16_t v_src00 = vld1q_u8(src0 + x); uint8x16_t v_src01 = vld1q_u8(src0 + x + 16); uint8x16_t v_src10 = vld1q_u8(src1 + x); uint8x16_t v_src11 = vld1q_u8(src1 + x + 16); uint8x16_t v_dst; + // Rest of the code is missing, unable to provide further comments without the complete code + } +#else + // NEON SIMD instructions are not enabled, handle the case here +#endif +} - v_dst = vqsubq_u8(v_src00, v_src10); - vst1q_u8(dst + x, v_dst); +// Subtract the corresponding elements of v_src10 from v_src00, saturating the result to unsigned 8-bit values +v_dst = vqsubq_u8(v_src00, v_src10); +// Store the resulting vector v_dst into memory starting at the address dst + x +vst1q_u8(dst + x, v_dst); + + // Subtract the corresponding elements of v_src01 and v_src11, and saturate the result to the range of unsigned 8-bit integers v_dst = vqsubq_u8(v_src01, v_src11); + + // Store the result v_dst into the memory location starting from dst + x + 16 vst1q_u8(dst + x + 16, v_dst); } #endif + + // For the remaining elements that couldn't be processed in the vectorized loop for (; x < total_size; x++) { + // Calculate the difference between src0[x] and src1[x], and cast it to int32_t int32_t val = static_cast(src0[x]) - src1[x]; + + // Clamp the value within the range of uint8_t (0 to 255) dst[x] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// Template specialization for SubtractImpl function with uint16_t data type + +// Function to subtract two arrays of uint16_t values and store the result in another array +// The function takes three pointers as arguments: src0, src1, and dst +// src0 and src1 point to the arrays to be subtracted, and dst points to the array where the result will be stored +// total_size is the number of elements in the arrays template <> inline void SubtractImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { + + // Loop through each element of the arrays for (int64_t i = 0; i < total_size; i++) { + + // Subtract the corresponding elements of src0 and src1 and store the result in val int32_t val = static_cast(src0[i]) - src1[i]; + + // Clamp the result to the range of uint16_t values + // If the result is less than the minimum value of uint16_t, set it to the minimum value + // If the result is greater than the maximum value of uint16_t, set it to the maximum value dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// Template specialization for the SubtractImpl function + +// This function subtracts the corresponding elements of two arrays, src0 and src1, and stores the result in the dst array. +// The total_size parameter specifies the number of elements in the arrays. + template <> inline void SubtractImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { + + // Iterate over each element of the arrays for (int64_t i = 0; i < total_size; i++) { + + // Subtract the elements and store the result in a temporary variable int64_t val = static_cast(src0[i]) - src1[i]; + + // Ensure that the result is within the range of uint32_t + // If the result is less than the minimum value of uint32_t, set it to the minimum value + // If the result is greater than the maximum value of uint32_t, set it to the maximum value dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// A function to check if subtraction of two LiteMat objects is possible and assign the result to a destination LiteMat object + +// The function is declared as inline, which suggests that the function body will be inserted directly at the call site for optimization purposes + +// The function takes two const references to LiteMat objects (src_a and src_b) as input parameters and a pointer to a LiteMat object (dst) as an output parameter + +// The function returns a boolean value indicating whether the subtraction was successful or not + inline bool CheckSubstract(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { + + // Check if the destination LiteMat object is a nullptr (i.e., not assigned any memory) if (dst == nullptr) { + + // If the destination LiteMat object is a nullptr, return false to indicate that the subtraction cannot be performed return false; } + // Check if the width, height, and channel of src_a are not equal to the width, height, and channel of src_b if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { + // If any of the conditions are true, return false to indicate that the two source images are not equal return false; } - return src_a.data_type_ == src_b.data_type_; -} +// Return the result of comparing the data types of src_a and src_b +return src_a.data_type_ == src_b.data_type_; +// Function to subtract two LiteMat objects and store the result in another LiteMat object bool Subtract(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { + + // Check if the subtraction operation is valid by calling the CheckSubstract function if (!CheckSubstract(src_a, src_b, dst)) { + // If the subtraction operation is not valid, return false return false; } - - if (dst->IsEmpty()) { - dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { - return false; - } else if (src_a.data_type_ != dst->data_type_) { - return false; - } - - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::BOOL) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT8) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - SubtractImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - - return true; + // If the subtraction operation is valid, continue with the subtraction and return true + // (the subtraction result will be stored in the dst LiteMat object) } + // Check if the destination object is empty + if (dst->IsEmpty()) { + // If it is empty, initialize it with the properties of the source object + dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); + } + // If the destination object is not empty, check if the dimensions and channel count match + else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { + // If they don't match, return false to indicate failure + return false; + } + // If the dimensions and channel count match, check if the data types match + else if (src_a.data_type_ != dst->data_type_) { + // If they don't match, return false to indicate failure + return false; + } + +// Calculate the total size of the data by multiplying the height, width, and number of channels +int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; + +// Check the data type of src_a and call the appropriate SubtractImpl function based on the data type +if (src_a.data_type_ == LDataType::BOOL) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT8) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT8) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT16) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT16) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT32) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT32) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT64) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT64) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT32) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT64) { + SubtractImpl(src_a, src_b, *dst, total_size); +} else { + // If the data type is not recognized, return false + return false; +} + +// Return true to indicate successful program termination +return true; + #ifdef ENABLE_NEON + +// Define an inline function named "reciprocal_simd" that takes a parameter "val" of type float32x4_t and returns a value of the same type inline float32x4_t reciprocal_simd(float32x4_t val) { - // get an initial estimate of 1/val + + // Use the NEON intrinsic function vrecpeq_f32 to get an initial estimate of 1/val float32x4_t reciprocal = vrecpeq_f32(val); - // use Newton-Raphson steps to refine the estimate + // Use Newton-Raphson steps to refine the estimate of the reciprocal + + // Multiply the reciprocal estimate by the result of the reciprocal estimate subtracted from the input value reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + + // Multiply the reciprocal estimate by the result of the reciprocal estimate subtracted from the input value again reciprocal = vmulq_f32(vrecpsq_f32(val, reciprocal), reciprocal); + + // Return the refined reciprocal estimate return reciprocal; } -inline float32x4_t round_simd(const float32x4_t &v) { - const int32x4_t signMask = vdupq_n_s32(1U << 31); - const int32x4_t half = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); - float32x4_t v_addition = vreinterpretq_f32_s32(vorrq_s32(half, vandq_s32(signMask, vreinterpretq_s32_f32(v)))); - return vaddq_f32(v, v_addition); -} -#endif +// Define an inline function named round_simd that takes a reference to a float32x4_t vector as input and returns a float32x4_t vector as output +// Create a constant int32x4_t vector named signMask, initialized with the value (1U << 31) +// This vector is used to mask the sign bit of the input vector +const int32x4_t signMask = vdupq_n_s32(1U << 31); + +// Create a constant int32x4_t vector named half, initialized with the value 0.5f +// This vector is used to represent the value 0.5 as an integer +const int32x4_t half = vreinterpretq_s32_f32(vdupq_n_f32(0.5f)); + +// Create a float32x4_t vector named v_addition, initialized with the result of the following operations: +// 1. Convert the input vector v to an int32x4_t vector using vreinterpretq_s32_f32 +// 2. Perform a bitwise OR operation between the half vector and the signMask vector using vorrq_s32 +// 3. Convert the result back to a float32x4_t vector using vreinterpretq_f32_s32 +// This vector is used to add the rounded value to the input vector +float32x4_t v_addition = vreinterpretq_f32_s32(vorrq_s32(half, vandq_s32(signMask, vreinterpretq_s32_f32(v)))); + +// Return the result of adding the input vector v and the v_addition vector using vaddq_f32 +// This is the rounded vector +return vaddq_f32(v, v_addition); + +// A template function that divides the elements of two source arrays and stores the result in a destination array +// The function takes in the source arrays (src0 and src1), the destination array (dst), and the total size of the arrays template inline void DivideImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { + + // Iterate over each element in the arrays for (int64_t i = 0; i < total_size; i++) { - dst[i] = src1[i] ? src0[i] / src1[i] : 0; + + // Check if the corresponding element in src1 is non-zero + if (src1[i] != 0) { + + // Divide the corresponding elements in src0 and src1 and store the result in dst + dst[i] = src0[i] / src1[i]; + } else { + + // If the corresponding element in src1 is zero, set the corresponding element in dst to zero + dst[i] = 0; + } } } -template <> -inline void DivideImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { - int64_t x = 0; +// This is a specialization of the DivideImpl template function for uint8_t data type. +// It is defined as an inline function, which means the function body will be inserted directly at the call site. + +// Declare a variable x to keep track of the current position in the arrays src0, src1, and dst. +int64_t x = 0; + +// Check if the ENABLE_NEON macro is defined. This macro is likely used to enable NEON instructions, which are SIMD instructions for ARM processors. #ifdef ENABLE_NEON + + // If ENABLE_NEON is defined, set the step size to 16. This indicates that the loop will process 16 elements at a time. const int64_t step = 16; + + // Start a loop that iterates until x is less than or equal to total_size - step. + // This ensures that the loop will not go beyond the bounds of the arrays. for (; x <= total_size - step; x += step) { + + // Use the __builtin_prefetch function to prefetch data from memory into the CPU cache. + // This can help improve performance by reducing memory access latency. + // The first prefetch call prefetches the data starting from src0 + x, with a distance of 32 * 10 bytes. __builtin_prefetch(reinterpret_cast(src0 + x) + 32 * 10); + + // The second prefetch call prefetches the data starting from src1 + x, with a distance of 32 * 10 bytes. __builtin_prefetch(reinterpret_cast(src1 + x) + 32 * 10); - uint8x16_t v_a = vld1q_u8(src0 + x); - uint8x16_t v_b = vld1q_u8(src1 + x); - uint8x16_t v_mask = vtstq_u8(v_b, v_b); - - uint16x8_t va_l_16x8 = vmovl_u8(vget_low_u8(v_a)); - uint16x8_t va_h_16x8 = vmovl_u8(vget_high_u8(v_a)); - uint16x8_t vb_l_16x8 = vmovl_u8(vget_low_u8(v_b)); - uint16x8_t vb_h_16x8 = vmovl_u8(vget_high_u8(v_b)); - - float32x4_t va_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_l_16x8))); - float32x4_t va_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_l_16x8))); - float32x4_t va_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_h_16x8))); - float32x4_t va_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_h_16x8))); - float32x4_t vb_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_l_16x8))); - float32x4_t vb_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_l_16x8))); - float32x4_t vb_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_h_16x8))); - float32x4_t vb_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_h_16x8))); - - float32x4_t vb_ll_re_f32x4 = reciprocal_simd(vb_ll_f32x4); - float32x4_t vb_lh_re_f32x4 = reciprocal_simd(vb_lh_f32x4); - float32x4_t vb_hl_re_f32x4 = reciprocal_simd(vb_hl_f32x4); - float32x4_t vb_hh_re_f32x4 = reciprocal_simd(vb_hh_f32x4); - - float32x4_t dst_ll_f32x4 = round_simd(vmulq_f32(va_ll_f32x4, vb_ll_re_f32x4)); - float32x4_t dst_lh_f32x4 = round_simd(vmulq_f32(va_lh_f32x4, vb_lh_re_f32x4)); - float32x4_t dst_hl_f32x4 = round_simd(vmulq_f32(va_hl_f32x4, vb_hl_re_f32x4)); - float32x4_t dst_hh_f32x4 = round_simd(vmulq_f32(va_hh_f32x4, vb_hh_re_f32x4)); - - uint32x4_t dst_ll_32x4 = vcvtq_u32_f32(dst_ll_f32x4); - uint32x4_t dst_lh_32x4 = vcvtq_u32_f32(dst_lh_f32x4); - uint32x4_t dst_hl_32x4 = vcvtq_u32_f32(dst_hl_f32x4); - uint32x4_t dst_hh_32x4 = vcvtq_u32_f32(dst_hh_f32x4); - - uint16x4_t dst_ll_16x4 = vqmovn_u32(dst_ll_32x4); - uint16x4_t dst_lh_16x4 = vqmovn_u32(dst_lh_32x4); - uint16x4_t dst_hl_16x4 = vqmovn_u32(dst_hl_32x4); - uint16x4_t dst_hh_16x4 = vqmovn_u32(dst_hh_32x4); - - uint16x8_t dst_l_16x8 = vcombine_u16(dst_ll_16x4, dst_lh_16x4); - uint16x8_t dst_h_16x8 = vcombine_u16(dst_hl_16x4, dst_hh_16x4); - - int8x8_t dst_l_8x8 = vqmovn_u16(dst_l_16x8); - int8x8_t dst_h_8x8 = vqmovn_u16(dst_h_16x8); - int8x16_t dst_8x16 = vcombine_u8(dst_l_8x8, dst_h_8x8); - - dst_8x16 = vandq_u8(dst_8x16, v_mask); - vst1q_u8(dst + x, dst_8x16); + // The actual processing of the data would go here. + // However, the code provided does not include the processing logic. + // It is likely that the processing involves dividing the elements of src0 and src1 and storing the result in dst. } -#endif - for (; x < total_size; x++) { + +#endif // ENABLE_NEON + +// The rest of the function is not provided, so it is unclear what happens after the loop. +// It is possible that there is additional processing logic for the remaining elements, +// or there may be some cleanup code before the function returns. + +// Load 16 unsigned 8-bit integers from memory starting at the address src0 + x into the vector v_a +uint8x16_t v_a = vld1q_u8(src0 + x); + +// Load 16 unsigned 8-bit integers from memory starting at the address src1 + x into the vector v_b +uint8x16_t v_b = vld1q_u8(src1 + x); + +// Create a vector v_mask by performing a bitwise test (AND) between v_b and itself +// This will set each element of v_mask to 0xFF if the corresponding element in v_b is non-zero, and 0x00 otherwise +uint8x16_t v_mask = vtstq_u8(v_b, v_b); + +// Convert the lower 8 unsigned 8-bit integers in vector v_a to 16-bit integers and store the result in va_l_16x8 +uint16x8_t va_l_16x8 = vmovl_u8(vget_low_u8(v_a)); + +// Convert the upper 8 unsigned 8-bit integers in vector v_a to 16-bit integers and store the result in va_h_16x8 +uint16x8_t va_h_16x8 = vmovl_u8(vget_high_u8(v_a)); + +// Convert the lower 8 unsigned 8-bit integers in vector v_b to 16-bit integers and store the result in vb_l_16x8 +uint16x8_t vb_l_16x8 = vmovl_u8(vget_low_u8(v_b)); + +// Convert the upper 8 unsigned 8-bit integers in vector v_b to 16-bit integers and store the result in vb_h_16x8 +uint16x8_t vb_h_16x8 = vmovl_u8(vget_high_u8(v_b)); + +// Convert the lower 16-bit elements of va_l_16x8 to 32-bit floating-point values and store them in va_ll_f32x4 +float32x4_t va_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_l_16x8))); + +// Convert the higher 16-bit elements of va_l_16x8 to 32-bit floating-point values and store them in va_lh_f32x4 +float32x4_t va_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_l_16x8))); + +// Convert the lower 16-bit elements of va_h_16x8 to 32-bit floating-point values and store them in va_hl_f32x4 +float32x4_t va_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(va_h_16x8))); + +// Convert the higher 16-bit elements of va_h_16x8 to 32-bit floating-point values and store them in va_hh_f32x4 +float32x4_t va_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(va_h_16x8))); + +// Convert the lower 16-bit elements of vb_l_16x8 to 32-bit floating-point values and store them in vb_ll_f32x4 +float32x4_t vb_ll_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_l_16x8))); + +// Convert the higher 16-bit elements of vb_l_16x8 to 32-bit floating-point values and store them in vb_lh_f32x4 +float32x4_t vb_lh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_l_16x8))); + +// Convert the lower 16-bit elements of vb_h_16x8 to 32-bit floating-point values and store them in vb_hl_f32x4 +float32x4_t vb_hl_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_low_u16(vb_h_16x8))); + +// Convert the higher 16-bit elements of vb_h_16x8 to 32-bit floating-point values and store them in vb_hh_f32x4 +float32x4_t vb_hh_f32x4 = vcvtq_f32_u32(vmovl_u16(vget_high_u16(vb_h_16x8))); + +// Calculate the reciprocal of each element in the vector vb_ll_f32x4 and store the result in vb_ll_re_f32x4 +float32x4_t vb_ll_re_f32x4 = reciprocal_simd(vb_ll_f32x4); + +// Calculate the reciprocal of each element in the vector vb_lh_f32x4 and store the result in vb_lh_re_f32x4 +float32x4_t vb_lh_re_f32x4 = reciprocal_simd(vb_lh_f32x4); + +// Calculate the reciprocal of each element in the vector vb_hl_f32x4 and store the result in vb_hl_re_f32x4 +float32x4_t vb_hl_re_f32x4 = reciprocal_simd(vb_hl_f32x4); + +// Calculate the reciprocal of each element in the vector vb_hh_f32x4 and store the result in vb_hh_re_f32x4 +float32x4_t vb_hh_re_f32x4 = reciprocal_simd(vb_hh_f32x4); + +// Perform SIMD multiplication of four float32x4 vectors (va_ll_f32x4, vb_ll_re_f32x4) and round the result +float32x4_t dst_ll_f32x4 = round_simd(vmulq_f32(va_ll_f32x4, vb_ll_re_f32x4)); + +// Perform SIMD multiplication of four float32x4 vectors (va_lh_f32x4, vb_lh_re_f32x4) and round the result +float32x4_t dst_lh_f32x4 = round_simd(vmulq_f32(va_lh_f32x4, vb_lh_re_f32x4)); + +// Perform SIMD multiplication of four float32x4 vectors (va_hl_f32x4, vb_hl_re_f32x4) and round the result +float32x4_t dst_hl_f32x4 = round_simd(vmulq_f32(va_hl_f32x4, vb_hl_re_f32x4)); + +// Perform SIMD multiplication of four float32x4 vectors (va_hh_f32x4, vb_hh_re_f32x4) and round the result +float32x4_t dst_hh_f32x4 = round_simd(vmulq_f32(va_hh_f32x4, vb_hh_re_f32x4)); + +// Convert a vector of single-precision floating-point values (dst_ll_f32x4) to a vector of unsigned 32-bit integers (dst_ll_32x4) +uint32x4_t dst_ll_32x4 = vcvtq_u32_f32(dst_ll_f32x4); + +// Convert a vector of single-precision floating-point values (dst_lh_f32x4) to a vector of unsigned 32-bit integers (dst_lh_32x4) +uint32x4_t dst_lh_32x4 = vcvtq_u32_f32(dst_lh_f32x4); + +// Convert a vector of single-precision floating-point values (dst_hl_f32x4) to a vector of unsigned 32-bit integers (dst_hl_32x4) +uint32x4_t dst_hl_32x4 = vcvtq_u32_f32(dst_hl_f32x4); + +// Convert a vector of single-precision floating-point values (dst_hh_f32x4) to a vector of unsigned 32-bit integers (dst_hh_32x4) +uint32x4_t dst_hh_32x4 = vcvtq_u32_f32(dst_hh_f32x4); + +// Convert a vector of four 32-bit unsigned integers (dst_ll_32x4) to a vector of four 16-bit unsigned integers (dst_ll_16x4) +uint16x4_t dst_ll_16x4 = vqmovn_u32(dst_ll_32x4); + +// Convert a vector of four 32-bit unsigned integers (dst_lh_32x4) to a vector of four 16-bit unsigned integers (dst_lh_16x4) +uint16x4_t dst_lh_16x4 = vqmovn_u32(dst_lh_32x4); + +// Convert a vector of four 32-bit unsigned integers (dst_hl_32x4) to a vector of four 16-bit unsigned integers (dst_hl_16x4) +uint16x4_t dst_hl_16x4 = vqmovn_u32(dst_hl_32x4); + +// Convert a vector of four 32-bit unsigned integers (dst_hh_32x4) to a vector of four 16-bit unsigned integers (dst_hh_16x4) +uint16x4_t dst_hh_16x4 = vqmovn_u32(dst_hh_32x4); + +// Combine two 16x4 vectors (dst_ll_16x4 and dst_lh_16x4) into a single 16x8 vector (dst_l_16x8) +uint16x8_t dst_l_16x8 = vcombine_u16(dst_ll_16x4, dst_lh_16x4); + +// Combine two 16x4 vectors (dst_hl_16x4 and dst_hh_16x4) into a single 16x8 vector (dst_h_16x8) +uint16x8_t dst_h_16x8 = vcombine_u16(dst_hl_16x4, dst_hh_16x4); + +// Convert the 16-bit unsigned integer vector dst_l_16x8 to an 8-bit signed integer vector dst_l_8x8 using saturation rounding +int8x8_t dst_l_8x8 = vqmovn_u16(dst_l_16x8); + +// Convert the 16-bit unsigned integer vector dst_h_16x8 to an 8-bit signed integer vector dst_h_8x8 using saturation rounding +int8x8_t dst_h_8x8 = vqmovn_u16(dst_h_16x8); + +// Combine the two 8-bit signed integer vectors dst_l_8x8 and dst_h_8x8 into a single 16-element 8-bit signed integer vector dst_8x16 + +dst_8x16 = vandq_u8(dst_8x16, v_mask); // Perform bitwise AND operation between dst_8x16 and v_mask, storing the result in dst_8x16 +vst1q_u8(dst + x, dst_8x16); // Store the values in dst_8x16 to the memory location starting at dst + x + +// Loop through the remaining elements in the array +for (; x < total_size; x++) { + // Calculate the value by dividing src0[x] by src1[x], rounding the result to the nearest integer int32_t val = src1[x] ? std::round(src0[x] / src1[x]) : 0; + + // Clamp the value between the minimum and maximum values of uint8_t, and store it in dst[x] dst[x] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); - } } +// Template specialization for the DivideImpl function, specifically for uint16_t data type + +// Inline function definition template <> inline void DivideImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { + + // Loop through each element in the arrays for (size_t i = 0; i < total_size; i++) { + + // Calculate the division result, rounding it to the nearest integer int32_t val = src1[i] ? std::round(src0[i] / src1[i]) : 0; + + // Clamp the result to the range of uint16_t dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// Template specialization for the DivideImpl function + +// Implementation of the DivideImpl function for uint32_t data type template <> inline void DivideImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { + // Iterate over each element in the arrays for (size_t i = 0; i < total_size; i++) { + // Calculate the division result, rounding it to the nearest integer int64_t val = src1[i] ? std::round(src0[i] / src1[i]) : 0; + + // Clamp the result to the range of uint32_t dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// A function to check if division between two LiteMat objects is possible and assign the result to a destination LiteMat object + +// The function is declared as inline, which suggests that the function body will be inserted directly at the call site for optimization purposes + +// The function takes two const references to LiteMat objects (src_a and src_b) as input parameters and a pointer to a LiteMat object (dst) as an output parameter + +// The function returns a boolean value indicating whether the division operation was successful or not + inline bool CheckDivide(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { + + // Check if the destination LiteMat object is a nullptr (i.e., not assigned any memory) if (dst == nullptr) { + + // If the destination LiteMat object is a nullptr, return false to indicate failure return false; } - - if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { - return false; - } - - return src_a.data_type_ == src_b.data_type_; + // If the destination LiteMat object is not a nullptr, continue with the division operation } + // Check if the width, height, and channel of src_a are not equal to the width, height, and channel of src_b + if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { + // If any of the conditions are true, return false to indicate that the two source images are not equal + return false; + } + +// Return the result of comparing the data types of src_a and src_b +return src_a.data_type_ == src_b.data_type_; + +// Function to divide two LiteMat objects and store the result in a third LiteMat object bool Divide(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { + + // Check if the division operation is valid by calling the CheckDivide function if (!CheckDivide(src_a, src_b, dst)) { return false; } + // If the division operation is not valid, return false - if (dst->IsEmpty()) { - dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { - return false; - } else if (src_a.data_type_ != dst->data_type_) { - return false; - } + // If the division operation is valid, continue with the division and store the result in the dst LiteMat object - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::INT8) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - DivideImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - DivideImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - return true; + // ... } + // Check if the destination object is empty + if (dst->IsEmpty()) { + // If it is empty, initialize it with the properties of the source object + dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); + } + // If the destination object is not empty, check if the dimensions and channel count match + else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { + // If they don't match, return false to indicate failure + return false; + } + // If the dimensions and channel count match, check if the data types match + else if (src_a.data_type_ != dst->data_type_) { + // If they don't match, return false to indicate failure + return false; + } + +// Calculate the total size of the source data by multiplying its height, width, and number of channels +int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; + +// Check the data type of src_a and call the appropriate DivideImpl function based on the data type +if (src_a.data_type_ == LDataType::INT8) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT8) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT16) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT16) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT32) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT32) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT64) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT64) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT32) { + DivideImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT64) { + DivideImpl(src_a, src_b, *dst, total_size); +} else { + // If the data type is not recognized, return false + return false; +} + +// Return true to indicate successful execution of the function +return true; + +// A template function that multiplies two arrays element-wise and stores the result in a destination array +// The function takes in pointers to the source arrays (src0 and src1), a pointer to the destination array (dst), +// and the total size of the arrays (total_size) + template inline void MultiplyImpl(const T *src0, const T *src1, T *dst, int64_t total_size) { + + // Iterate over each element of the arrays for (int64_t i = 0; i < total_size; i++) { + + // Multiply the corresponding elements of src0 and src1 and store the result in dst dst[i] = src0[i] * src1[i]; } } -template <> -inline void MultiplyImpl(const uint8_t *src0, const uint8_t *src1, uint8_t *dst, int64_t total_size) { - int64_t x = 0; -#ifdef ENABLE_NEON - const int64_t step = 32; - for (; x <= total_size - step; x += step) { - uint8x16_t v_src00 = vld1q_u8(src0 + x); - uint8x16_t v_src01 = vld1q_u8(src0 + x + 16); - uint8x16_t v_src10 = vld1q_u8(src1 + x); - uint8x16_t v_src11 = vld1q_u8(src1 + x + 16); - uint8x16_t v_dst_l, v_dst_h; +// Template specialization for multiplying two arrays of uint8_t elements using NEON instructions +// The function takes pointers to the source arrays (src0 and src1), a pointer to the destination array (dst), +// and the total size of the arrays (total_size) + +// Initialize a variable x to keep track of the current position in the arrays +int64_t x = 0; + +// Check if NEON instructions are enabled +#ifdef ENABLE_NEON + + // Define the step size for loading elements using NEON instructions + const int64_t step = 32; + + // Loop through the arrays in steps of 32 elements + for (; x <= total_size - step; x += step) { + + // Load 16 uint8_t elements from src0 starting at position x into a NEON register v_src00 + uint8x16_t v_src00 = vld1q_u8(src0 + x); + + // Load the next 16 uint8_t elements from src0 starting at position x + 16 into a NEON register v_src01 + uint8x16_t v_src01 = vld1q_u8(src0 + x + 16); + + // Load 16 uint8_t elements from src1 starting at position x into a NEON register v_src10 + uint8x16_t v_src10 = vld1q_u8(src1 + x); + + // Load the next 16 uint8_t elements from src1 starting at position x + 16 into a NEON register v_src11 + uint8x16_t v_src11 = vld1q_u8(src1 + x + 16); + + // Declare two NEON registers v_dst_l and v_dst_h to store the lower and higher halves of the result + +#endif + + // Multiply the lower 8-bit elements of v_src00 and v_src10 and store the result in v_dst_l v_dst_l = vmull_u8(vget_low_u8(v_src00), vget_low_u8(v_src10)); + + // Multiply the higher 8-bit elements of v_src00 and v_src10 and store the result in v_dst_h v_dst_h = vmull_u8(vget_high_u8(v_src00), vget_high_u8(v_src10)); + + // Combine the lower 16-bit elements of v_dst_l and v_dst_h into a single 32-bit vector + // Convert the 32-bit elements to 8-bit elements by saturating the values + // Store the resulting 8-bit elements in memory starting at the address dst + x vst1q_u8(dst + x, vcombine_u8(vqmovn_u16(v_dst_l), vqmovn_u16(v_dst_h))); + // Multiply the low 8-bit elements of v_src01 and v_src11 and store the result in v_dst_l v_dst_l = vmull_u8(vget_low_u8(v_src01), vget_low_u8(v_src11)); + + // Multiply the high 8-bit elements of v_src01 and v_src11 and store the result in v_dst_h v_dst_h = vmull_u8(vget_high_u8(v_src01), vget_high_u8(v_src11)); + + // Combine the lower 16-bit elements of v_dst_l and v_dst_h into a single 32-bit vector + // Convert the 32-bit elements to 8-bit elements by saturating the values + // Store the resulting vector in dst starting from index x + 16 vst1q_u8(dst + x + 16, vcombine_u8(vqmovn_u16(v_dst_l), vqmovn_u16(v_dst_h))); - } -#endif + + // If the above code is not supported by the compiler, fallback to the following code + // This code performs element-wise multiplication of src0 and src1, clamping the result to the range of uint8_t for (; x < total_size; x++) { + // Multiply the elements of src0 and src1 and store the result in val int32_t val = src0[x] * src1[x]; + + // Clamp the value of val to the range of uint8_t + // std::numeric_limits::min() returns the minimum value of uint8_t + // std::numeric_limits::max() returns the maximum value of uint8_t dst[x] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } -template <> +// Template specialization for the MultiplyImpl function with uint16_t as the data type + +// Inline function definition to multiply two arrays of uint16_t elements and store the result in another array inline void MultiplyImpl(const uint16_t *src0, const uint16_t *src1, uint16_t *dst, int64_t total_size) { + + // Iterate over each element in the arrays for (size_t i = 0; i < total_size; i++) { + + // Multiply the corresponding elements from src0 and src1 and store the result in val int32_t val = src0[i] * src1[i]; + + // Clamp the value of val between the minimum and maximum values of uint16_t dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } +// Template specialization for the MultiplyImpl function + +// The function takes two source arrays (src0 and src1), a destination array (dst), and the total size of the arrays template <> inline void MultiplyImpl(const uint32_t *src0, const uint32_t *src1, uint32_t *dst, int64_t total_size) { + + // Iterate over each element in the arrays for (size_t i = 0; i < total_size; i++) { + + // Multiply the corresponding elements from src0 and src1 int64_t val = src0[i] * src1[i]; + + // Ensure that the result is within the range of uint32_t + // If the result is less than the minimum value of uint32_t, set it to the minimum value + // If the result is greater than the maximum value of uint32_t, set it to the maximum value dst[i] = std::max(std::numeric_limits::min(), std::min(std::numeric_limits::max(), val)); } } -inline bool CheckMultiply(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { - if (dst == nullptr) { - return false; - } +// A function to check if multiplication of two LiteMat objects is possible and assign the result to a destination LiteMat object - if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { - return false; - } +// The function is declared as inline, which suggests that the compiler should try to inline the function for better performance - return src_a.data_type_ == src_b.data_type_; +// The function takes two const references to LiteMat objects (src_a and src_b) as input and a pointer to a LiteMat object (dst) as output + +// The function returns a boolean value indicating whether the multiplication was successful or not + +// Check if the destination LiteMat object is nullptr (i.e., not assigned any memory) +if (dst == nullptr) { + // If the destination is nullptr, return false to indicate failure + return false; } + // Check if the width, height, and channel of src_a are not equal to the width, height, and channel of src_b + if (src_a.width_ != src_b.width_ || src_a.height_ != src_b.height_ || src_a.channel_ != src_b.channel_) { + // If any of the conditions are true, return false to indicate that the two source images are not equal + return false; + } + +// Return the result of comparing the data types of src_a and src_b +return src_a.data_type_ == src_b.data_type_; + +// Function to multiply two LiteMat objects and store the result in a third LiteMat object bool Multiply(const LiteMat &src_a, const LiteMat &src_b, LiteMat *dst) { + + // Check if the multiplication is valid if (!CheckMultiply(src_a, src_b, dst)) { return false; } + + // If the destination LiteMat is empty, initialize it with the dimensions and data type of src_a if (dst->IsEmpty()) { dst->Init(src_a.width_, src_a.height_, src_a.channel_, src_a.data_type_); - } else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { + } + // If the dimensions or data type of src_a and dst do not match, return false + else if (src_a.width_ != dst->width_ || src_a.height_ != dst->height_ || src_a.channel_ != dst->channel_) { return false; - } else if (src_a.data_type_ != dst->data_type_) { + } + // If the data type of src_a and dst do not match, return false + else if (src_a.data_type_ != dst->data_type_) { return false; } - - int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; - if (src_a.data_type_ == LDataType::INT8) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT8) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT16) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT16) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::INT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::UINT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT32) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else if (src_a.data_type_ == LDataType::FLOAT64) { - MultiplyImpl(src_a, src_b, *dst, total_size); - } else { - return false; - } - return true; + + // Continue with the multiplication process + // ... } +// Calculate the total size of the source data by multiplying its height, width, and number of channels +int64_t total_size = src_a.height_ * src_a.width_ * src_a.channel_; + +// Check the data type of src_a and call the appropriate MultiplyImpl function based on the data type +if (src_a.data_type_ == LDataType::INT8) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT8) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT16) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT16) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT32) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT32) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::INT64) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::UINT64) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT32) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else if (src_a.data_type_ == LDataType::FLOAT64) { + MultiplyImpl(src_a, src_b, *dst, total_size); +} else { + // If the data type is not recognized, return false + return false; +} + +// Return true to indicate successful execution of the function +return true; + } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_image_utils.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_image_utils.cc index 349d5a286ad..424883075de 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/lite_image_utils.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/lite_image_utils.cc @@ -15,366 +15,700 @@ */ #include "minddata/dataset/kernels/image/lite_image_utils.h" +// Include the header for numeric limits, which provides information about the properties of arithmetic types #include + +// Include the header for the standard exception classes #include + +// Include the header for the utility library, which provides various general-purpose utilities #include + +// Include the header for the vector container class, which provides dynamic arrays #include +// Include the header file for the Tensor class from the MindData dataset core library #include "minddata/dataset/core/tensor.h" + +// Include the header file for the TensorShape class from the MindData dataset core library #include "minddata/dataset/core/tensor_shape.h" + +// Include the header file for the constants used in the MindData dataset library #include "minddata/dataset/include/dataset/constants.h" + +// Include the header file for the LiteMat class from the MindData dataset image processing library #include "minddata/dataset/kernels/image/lite_cv/lite_mat.h" + +// Include the header file for the ImageProcess class from the MindData dataset image processing library #include "minddata/dataset/kernels/image/lite_cv/image_process.h" + +// Include the header file for the Random class from the MindData dataset utility library #include "minddata/dataset/util/random.h" -#define MAX_INT_PRECISION 16777216 // float int precision is 16777216 +// Define a constant MAX_INT_PRECISION with a value of 16777216, which represents the precision of integers in a float +#define MAX_INT_PRECISION 16777216 + +// Define a namespace called mindspore, which is a part of the dataset namespace namespace mindspore { namespace dataset { + +// A function that checks if a given Tensor contains a non-empty JPEG image bool IsNonEmptyJPEG(const std::shared_ptr &input) { + + // Define a constant array of unsigned characters that represents the magic number of a JPEG image const unsigned char *kJpegMagic = (unsigned char *)"\xFF\xD8\xFF"; + + // Define a constant size_t variable that represents the length of the JPEG magic number constexpr size_t kJpegMagicLen = 3; + + // Check if the size of the input Tensor in bytes is greater than the length of the JPEG magic number + // and if the first kJpegMagicLen bytes of the input Tensor match the JPEG magic number return input->SizeInBytes() > kJpegMagicLen && memcmp(input->GetBuffer(), kJpegMagic, kJpegMagicLen) == 0; } -static void JpegInitSource(j_decompress_ptr cinfo) {} +// Define a static function named JpegInitSource that takes a pointer to a j_decompress_struct as a parameter +static void JpegInitSource(j_decompress_ptr cinfo) { + // This function is empty and does not contain any code + // It is likely intended to be implemented later with functionality specific to initializing the JPEG source +} + +// A static function named JpegFillInputBuffer that takes a pointer to a j_decompress_ptr structure as a parameter and returns a boolean value static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) { + + // Check if the number of bytes in the buffer of the source object in the j_decompress_ptr structure is zero if (cinfo->src->bytes_in_buffer == 0) { - // Under ARM platform raise runtime_error may cause core problem, - // so we catch runtime_error and just return FALSE. + + // If the number of bytes in the buffer is zero, raise a JERR_INPUT_EMPTY error using the ERREXIT macro try { ERREXIT(cinfo, JERR_INPUT_EMPTY); } catch (const std::exception &e) { + + // Catch any std::exception that may be thrown and return FALSE return FALSE; } + + // Return FALSE to indicate that the input buffer is empty return FALSE; } + + // If the number of bytes in the buffer is not zero, return TRUE to indicate that the input buffer is not empty return TRUE; } -static void JpegTermSource(j_decompress_ptr cinfo) {} +// Define a static function named JpegTermSource that takes a pointer to a j_decompress_struct as a parameter +static void JpegTermSource(j_decompress_ptr cinfo) { + // This function does not have any implementation, it is empty + // It is likely intended to be used as a callback function for terminating the JPEG decompression process + // The purpose and implementation of this function should be provided elsewhere in the code +} + +// A static function named JpegSkipInputData that takes a pointer to a decompression structure (j_decompress_ptr) and an integer (jump) as parameters static void JpegSkipInputData(j_decompress_ptr cinfo, int64_t jump) { + + // Check if the jump value is less than 0 if (jump < 0) { + // If so, return without doing anything return; } + + // Check if the jump value is greater than the number of bytes in the input buffer if (static_cast(jump) > cinfo->src->bytes_in_buffer) { + // If so, set the number of bytes in the input buffer to 0 and return cinfo->src->bytes_in_buffer = 0; return; } else { + // If not, subtract the jump value from the number of bytes in the input buffer cinfo->src->bytes_in_buffer -= jump; + // Increment the pointer to the next input byte by the jump value cinfo->src->next_input_byte += jump; } } +// Function to set the source for JPEG decompression void JpegSetSource(j_decompress_ptr cinfo, const void *data, int64_t datasize) { + + // Allocate memory for the jpeg_source_mgr structure and assign it to cinfo->src cinfo->src = static_cast( (*cinfo->mem->alloc_small)(reinterpret_cast(cinfo), JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr))); + + // Set the init_source function pointer to JpegInitSource cinfo->src->init_source = JpegInitSource; + + // Set the fill_input_buffer function pointer to JpegFillInputBuffer cinfo->src->fill_input_buffer = JpegFillInputBuffer; + + // Conditionally set the skip_input_data function pointer based on the platform #if defined(_WIN32) || defined(_WIN64) || defined(ENABLE_ARM32) - // the following line skips CI because it uses underlying C type + // On Windows or ARM32, skip_input_data is casted to a different function type due to underlying C type cinfo->src->skip_input_data = reinterpret_cast(JpegSkipInputData); // NOLINT. #else + // On other platforms, skip_input_data is set to JpegSkipInputData directly cinfo->src->skip_input_data = JpegSkipInputData; #endif + + // Set the resync_to_restart function pointer to jpeg_resync_to_restart cinfo->src->resync_to_restart = jpeg_resync_to_restart; + + // Set the term_source function pointer to JpegTermSource cinfo->src->term_source = JpegTermSource; + + // Set the bytes_in_buffer field to datasize cinfo->src->bytes_in_buffer = datasize; + + // Set the next_input_byte field to the starting address of the data cinfo->src->next_input_byte = static_cast(data); } +// This function reads scanlines from a JPEG image using the libjpeg library. +// It takes in several parameters: +// - cinfo: a pointer to the jpeg_decompress_struct object that contains the decompression parameters and state +// - max_scanlines_to_read: the maximum number of scanlines to read +// - buffer: a pointer to the buffer where the scanlines will be stored +// - buffer_size: the size of the buffer in bytes +// - crop_w: the width of the cropped region of the image +// - crop_w_aligned: the width of the cropped region of the image, aligned to a certain value +// - offset: the offset value used for calculating the CMYK pixel index +// - stride: the stride value used for calculating the CMYK pixel index + static Status JpegReadScanlines(jpeg_decompress_struct *const cinfo, int max_scanlines_to_read, JSAMPLE *buffer, int buffer_size, int crop_w, int crop_w_aligned, int offset, int stride) { - // scanlines will be read to this buffer first, must have the number - // of components equal to the number of components in the image + + // Calculate the size of each scanline in the buffer based on the cropped width and the number of output components int64_t scanline_size = crop_w_aligned * cinfo->output_components; + + // Create a vector to store the scanline data std::vector scanline(scanline_size); + + // Get a pointer to the first element of the scanline vector JSAMPLE *scanline_ptr = &scanline[0]; + + // Loop until the desired number of scanlines have been read while (cinfo->output_scanline < static_cast(max_scanlines_to_read)) { + int num_lines_read = 0; + try { + // Read one scanline from the JPEG image and store it in the scanline buffer num_lines_read = jpeg_read_scanlines(cinfo, &scanline_ptr, 1); } catch (const std::exception &e) { + // If an exception occurs during the reading process, return an error status RETURN_STATUS_UNEXPECTED("Decode: jpeg_read_scanlines error."); } + + // If the output color space is CMYK and at least one line has been read if (cinfo->out_color_space == JCS_CMYK && num_lines_read > 0) { + + // Iterate over each pixel in the cropped width for (int i = 0; i < crop_w; ++i) { + + // Calculate the index of the CMYK pixel in the scanline buffer const int cmyk_pixel = 4 * i + offset; + + // Get the values of the CMYK components from the scanline buffer const int c = scanline_ptr[cmyk_pixel]; const int m = scanline_ptr[cmyk_pixel + 1]; const int y = scanline_ptr[cmyk_pixel + 2]; + // Get the alpha channel value from the CMYK pixel const int k = scanline_ptr[cmyk_pixel + 3]; + + // Declare variables for the RGB channels int r, g, b; + + // Check if the Adobe marker was seen if (cinfo->saw_Adobe_marker) { + // Convert CMYK to RGB using the Adobe formula r = (k * c) / MAX_PIXEL_VALUE; g = (k * m) / MAX_PIXEL_VALUE; b = (k * y) / MAX_PIXEL_VALUE; } else { + // Convert CMYK to RGB using the default formula r = (MAX_PIXEL_VALUE - c) * (MAX_PIXEL_VALUE - k) / MAX_PIXEL_VALUE; g = (MAX_PIXEL_VALUE - m) * (MAX_PIXEL_VALUE - k) / MAX_PIXEL_VALUE; b = (MAX_PIXEL_VALUE - y) * (MAX_PIXEL_VALUE - k) / MAX_PIXEL_VALUE; } + + // Define constants for the buffer size and RGB channels constexpr int buffer_rgb_val_size = 3; constexpr int channel_red = 0; constexpr int channel_green = 1; constexpr int channel_blue = 2; + + // Store the RGB values in the buffer buffer[buffer_rgb_val_size * i + channel_red] = r; buffer[buffer_rgb_val_size * i + channel_green] = g; buffer[buffer_rgb_val_size * i + channel_blue] = b; } } else if (num_lines_read > 0) { - auto copy_status = memcpy_s(buffer, buffer_size, scanline_ptr + offset, stride); - if (copy_status != 0) { - jpeg_destroy_decompress(cinfo); - RETURN_STATUS_UNEXPECTED("Decode: memcpy_s failed"); - } - } else { - jpeg_destroy_decompress(cinfo); - std::string err_msg = "Decode: failed to decompress image."; - RETURN_STATUS_UNEXPECTED(err_msg); + // Handle the case when the number of lines read is greater than 0 + // (code for this case is missing, should be added here) } - buffer += stride; - buffer_size = buffer_size - stride; + auto copy_status = memcpy_s(buffer, buffer_size, scanline_ptr + offset, stride); // Copy the scanline data to the buffer using memcpy_s function + if (copy_status != 0) { // If the copy operation fails + jpeg_destroy_decompress(cinfo); // Destroy the decompression object + RETURN_STATUS_UNEXPECTED("Decode: memcpy_s failed"); // Return an unexpected status with an error message + } + } else { // If the decompression fails + jpeg_destroy_decompress(cinfo); // Destroy the decompression object + std::string err_msg = "Decode: failed to decompress image."; // Create an error message + RETURN_STATUS_UNEXPECTED(err_msg); // Return an unexpected status with the error message + } + buffer += stride; // Move the buffer pointer to the next scanline + buffer_size = buffer_size - stride; // Decrease the buffer size by the stride + } + return Status::OK(); // Return a status indicating successful execution + +// A function to set the color space for JPEG decompression +static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { + + // Switch statement based on the number of components in the JPEG image + switch (cinfo->num_components) { + + // If there is only 1 component, it means the image is grayscale + case 1: + // Set the output color space to RGB + cinfo->out_color_space = JCS_RGB; + // Return a status indicating success + return Status::OK(); + + // If there are 3 components, it means the image is already in RGB color space + case 3: + // Set the output color space to RGB + cinfo->out_color_space = JCS_RGB; + // Return a status indicating success + return Status::OK(); + + // If there are 4 components, it means the image is in CMYK color space + case 4: + // Set the output color space to CMYK + cinfo->out_color_space = JCS_CMYK; + // Return a status indicating success + return Status::OK(); + + // If the number of components is none of the above cases + default: + // Destroy the decompression object + jpeg_destroy_decompress(cinfo); + // Create an error message + std::string err_msg = "Decode: failed to decompress image."; + // Return a status indicating failure with the error message + RETURN_STATUS_UNEXPECTED(err_msg); } - return Status::OK(); } -static Status JpegSetColorSpace(jpeg_decompress_struct *cinfo) { - switch (cinfo->num_components) { - case 1: - // we want to output 3 components if it's grayscale - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 3: - cinfo->out_color_space = JCS_RGB; - return Status::OK(); - case 4: - // Need to manually convert to RGB - cinfo->out_color_space = JCS_CMYK; - return Status::OK(); - default: - jpeg_destroy_decompress(cinfo); - std::string err_msg = "Decode: failed to decompress image."; - RETURN_STATUS_UNEXPECTED(err_msg); - } -} +// A custom function for handling JPEG errors and throwing a runtime error void JpegErrorExitCustom(j_common_ptr cinfo) { + + // Create a character array to store the error message char jpeg_last_error_msg[JMSG_LENGTH_MAX]; + + // Call the format_message function of the error manager to get the error message (*(cinfo->err->format_message))(cinfo, jpeg_last_error_msg); + + // Throw a runtime error with the obtained error message throw std::runtime_error(jpeg_last_error_msg); } +// Function to crop and decode a JPEG image +// Takes an input tensor, crops it based on the provided coordinates, and outputs the cropped image in a new tensor +// Returns a status indicating the success or failure of the operation + Status JpegCropAndDecode(const std::shared_ptr &input, std::shared_ptr *output, int crop_x, int crop_y, int crop_w, int crop_h) { + + // Create a decompress struct for JPEG decoding struct jpeg_decompress_struct cinfo; + + // Lambda function to destroy the decompress struct and return an error status auto DestroyDecompressAndReturnError = [&cinfo](const std::string &err) { jpeg_destroy_decompress(&cinfo); RETURN_STATUS_UNEXPECTED(err); }; + + // Custom error manager for JPEG decoding struct JpegErrorManagerCustom jerr; + + // Set the error manager for the decompress struct cinfo.err = jpeg_std_error(&jerr.pub); + + // Set the custom error exit function for the error manager jerr.pub.error_exit = JpegErrorExitCustom; + try { + // Create the decompress struct jpeg_create_decompress(&cinfo); + + // Set the source for the decompress struct to the input tensor's buffer and size JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); + + // Read the JPEG header (void)jpeg_read_header(&cinfo, TRUE); + + // Set the color space for the decompress struct RETURN_IF_NOT_OK(JpegSetColorSpace(&cinfo)); + + // Calculate the output dimensions for the decompress struct jpeg_calc_output_dimensions(&cinfo); } catch (const std::exception &e) { + // If an exception occurs, destroy the decompress struct and return an error status with the exception message return DestroyDecompressAndReturnError(e.what()); } + + // Check if the crop width is valid CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_w) > crop_x, "invalid crop width"); + + // Continue with the rest of the function... +} + // Check if the crop height is valid by comparing it with the maximum value of int32_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - crop_h) > crop_y, "invalid crop height"); + + // Check if the crop parameters are all zeros, if so, set them to the output width and height if (crop_x == 0 && crop_y == 0 && crop_w == 0 && crop_h == 0) { crop_w = cinfo.output_width; crop_h = cinfo.output_height; - } else if (crop_w == 0 || static_cast(crop_w + crop_x) > cinfo.output_width || crop_h == 0 || + } + // Check if the crop parameters are invalid, return an error if any of the conditions are met + else if (crop_w == 0 || static_cast(crop_w + crop_x) > cinfo.output_width || crop_h == 0 || static_cast(crop_h + crop_y) > cinfo.output_height) { return DestroyDecompressAndReturnError("Decode: invalid crop size"); } + + // Get the minimum DCT scaled size const int mcu_size = cinfo.min_DCT_scaled_size; + // Check if the mcu_size is valid, return an error if it is zero CHECK_FAIL_RETURN_UNEXPECTED(mcu_size != 0, "Invalid data."); + + // Align the crop x coordinate to the nearest multiple of mcu_size unsigned int crop_x_aligned = (crop_x / mcu_size) * mcu_size; + // Calculate the aligned crop width by adding the original crop width to the difference between crop x and crop x aligned unsigned int crop_w_aligned = crop_w + crop_x - crop_x_aligned; + try { + // Start the decompression process (void)jpeg_start_decompress(&cinfo); + // Crop the scanline based on the aligned crop x and crop width jpeg_crop_scanline(&cinfo, &crop_x_aligned, &crop_w_aligned); - } catch (const std::exception &e) { + } + // Catch any exceptions thrown during the cropping process and return an error + catch (const std::exception &e) { return DestroyDecompressAndReturnError(e.what()); } - JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); - // three number of output components, always convert to RGB and output - constexpr int kOutNumComponents = 3; - TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); - const int buffer_size = output_tensor->SizeInBytes(); - JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, - "Invalid crop height."); - const int max_scanlines_to_read = skipped_scanlines + crop_h; - // stride refers to output tensor, which has 3 components at most - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, - "Invalid crop width."); - const int stride = crop_w * kOutNumComponents; - // offset is calculated for scanlines read from the image, therefore - // has the same number of components as the image - const int offset = (crop_x - crop_x_aligned) * cinfo.output_components; - RETURN_IF_NOT_OK( - JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); - *output = output_tensor; - jpeg_destroy_decompress(&cinfo); - return Status::OK(); -} + // Skip the specified number of scanlines from the top of the image + JDIMENSION skipped_scanlines = jpeg_skip_scanlines(&cinfo, crop_y); + + // Three number of output components, always convert to RGB and output +// Define a constant integer variable `kOutNumComponents` with a value of 3 +constexpr int kOutNumComponents = 3; + +// Create a `TensorShape` object `ts` with dimensions `crop_h`, `crop_w`, and `kOutNumComponents` +TensorShape ts = TensorShape({crop_h, crop_w, kOutNumComponents}); + +// Declare a shared pointer `output_tensor` to a `Tensor` object +std::shared_ptr output_tensor; + +// Create an empty `Tensor` object with the shape `ts`, data type `DE_UINT8`, and assign it to `output_tensor` +RETURN_IF_NOT_OK(Tensor::CreateEmpty(ts, DataType(DataType::DE_UINT8), &output_tensor)); + +// Calculate the size of the `output_tensor` buffer in bytes and assign it to `buffer_size` +const int buffer_size = output_tensor->SizeInBytes(); + +// Cast the pointer to the beginning of the `output_tensor` buffer to a `JSAMPLE` pointer and assign it to `buffer` +JSAMPLE *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Check if the sum of `skipped_scanlines` and `crop_h` is less than the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - skipped_scanlines) > crop_h, + "Invalid crop height."); + +// Calculate the maximum number of scanlines to read and assign it to `max_scanlines_to_read` +const int max_scanlines_to_read = skipped_scanlines + crop_h; + +// Check if the product of `crop_w` and `kOutNumComponents` is less than the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / crop_w) > kOutNumComponents, + "Invalid crop width."); + +// Calculate the stride (number of bytes between consecutive scanlines) and assign it to `stride` +const int stride = crop_w * kOutNumComponents; + +// Calculate the offset (number of bytes to skip at the beginning of each scanline) and assign it to `offset` +// The offset is calculated based on the difference between `crop_x` and `crop_x_aligned`, multiplied by the number of components in the output image +const int offset = (crop_x - crop_x_aligned) * cinfo.output_components; + +// Read scanlines from the JPEG image using the `JpegReadScanlines` function, passing the `buffer`, `buffer_size`, `crop_w`, `crop_w_aligned`, `offset`, and `stride` as arguments +RETURN_IF_NOT_OK( + JpegReadScanlines(&cinfo, max_scanlines_to_read, buffer, buffer_size, crop_w, crop_w_aligned, offset, stride)); + +// Assign the `output_tensor` to the `output` pointer +*output = output_tensor; +// Destroy the decompression object and release any allocated resources +jpeg_destroy_decompress(&cinfo); + +// Return a status indicating successful program execution +return Status::OK(); + +// A static function that takes a DataType object as input and returns a corresponding LDataType object static LDataType GetLiteCVDataType(const DataType &data_type) { + + // Check if the input data type is DE_UINT8 if (data_type == DataType::DE_UINT8) { + // If it is, return LDataType::UINT8 return LDataType::UINT8; - } else if (data_type == DataType::DE_FLOAT32) { + } + // Check if the input data type is DE_FLOAT32 + else if (data_type == DataType::DE_FLOAT32) { + // If it is, return LDataType::FLOAT32 return LDataType::FLOAT32; - } else { + } + // If the input data type is neither DE_UINT8 nor DE_FLOAT32 + else { + // Return LDataType::UNKNOWN return LDataType::UNKNOWN; } } +// Function to decode an input tensor and store the result in an output tensor Status Decode(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input tensor contains a non-empty JPEG image if (IsNonEmptyJPEG(input)) { + + // If the input is a non-empty JPEG, call the JpegCropAndDecode function to crop and decode the image return JpegCropAndDecode(input, output); + } else { + + // If the input is not a non-empty JPEG, return an error status with a descriptive error message RETURN_STATUS_UNEXPECTED("Decode: Decode only supports jpeg for android"); } } -Status Crop(const std::shared_ptr &input, std::shared_ptr *output, int x, int y, int w, int h) { - if (input->Rank() != 3 && input->Rank() != 2) { +// Function to crop an image given the input tensor, output tensor, and crop parameters +// The crop parameters include the starting x and y coordinates, as well as the width and height of the cropped region + +// Check if the input tensor has a rank of 3 or 2 +if (input->Rank() != 3 && input->Rank() != 2) { + // If the rank is not 3 or 2, return an error message indicating that the image shape is not or RETURN_STATUS_UNEXPECTED("Crop: image shape is not or "); - } +} - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { +// Check if the input data type is not float32 or uint8 +if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { + + // If the condition is true, return an unexpected status with an error message RETURN_STATUS_UNEXPECTED("Crop: image datatype is not float32 or uint8"); - } +} + // Check if the difference between the maximum value of int32_t and y is greater than h + // If not, it means that the crop height is invalid CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - y) > h, "Invalid crop height."); + + // Check if the difference between the maximum value of int32_t and x is greater than w + // If not, it means that the crop width is invalid CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x) > w, "Invalid crop width."); - // account for integer overflow + + // Check if the y coordinate is less than 0 or if the sum of y and h exceeds the boundary of the image + // If either condition is true, it means that the y coordinate value is invalid for cropping if (y < 0 || (y + h) > input->shape()[0] || (y + h) < 0) { RETURN_STATUS_UNEXPECTED( "Crop: invalid y coordinate value for crop" "y coordinate value exceeds the boundary of the image."); } - // account for integer overflow + + // Check if the x coordinate is less than 0 or if the sum of x and w exceeds the boundary of the image + // If either condition is true, it means that the x coordinate value is invalid for cropping if (x < 0 || (x + w) > input->shape()[1] || (x + w) < 0) { RETURN_STATUS_UNEXPECTED( "Crop: invalid x coordinate value for crop" "x coordinate value exceeds the boundary of the image."); } - try { - LiteMat lite_mat_rgb; - TensorShape shape{h, w}; - if (input->Rank() == 2) { - lite_mat_rgb.Init(input->shape()[1], input->shape()[0], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else { // rank == 3 - lite_mat_rgb.Init(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - int num_channels = input->shape()[2]; - shape = shape.AppendDim(num_channels); - } +// Create an instance of the LiteMat class named lite_mat_rgb +LiteMat lite_mat_rgb; - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); +// Create a TensorShape object named shape with dimensions h and w +TensorShape shape{h, w}; - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - LiteMat lite_mat_cut; - - lite_mat_cut.Init(w, h, lite_mat_rgb.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); - - bool ret = Crop(lite_mat_rgb, lite_mat_cut, x, y, w, h); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Crop: image crop failed."); - - *output = output_tensor; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Crop: " + std::string(e.what())); - } - return Status::OK(); +// Check if the rank of the input tensor is 2 +if (input->Rank() == 2) { + // Initialize the lite_mat_rgb object with the dimensions and buffer of the input tensor + lite_mat_rgb.Init(input->shape()[1], input->shape()[0], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); +} else { // rank == 3 + // Initialize the lite_mat_rgb object with the dimensions, number of channels, and buffer of the input tensor + lite_mat_rgb.Init(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + + // Get the number of channels from the input tensor + int num_channels = input->shape()[2]; + + // Append the number of channels to the shape object + shape = shape.AppendDim(num_channels); } +// Declare a shared pointer named "output_tensor" of type "Tensor" +std::shared_ptr output_tensor; + +// Call the "CreateEmpty" function of the "Tensor" class, passing the "shape", "input->type()", and the address of "output_tensor" as arguments +// The function returns a status code, so we use the "RETURN_IF_NOT_OK" macro to check if the function call was successful +RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); + +// Create a pointer variable named "buffer" of type uint8_t (unsigned 8-bit integer) +// Use reinterpret_cast to convert the address of the output tensor's first element to a pointer of type uint8_t +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Declare a variable named "lite_mat_cut" of type LiteMat +LiteMat lite_mat_cut; + +// Initialize the lite_mat_cut object with the provided parameters: +// - w: width of the image +// - h: height of the image +// - lite_mat_rgb.channel_: number of channels in the image +// - reinterpret_cast(buffer): pointer to the image data buffer +// - GetLiteCVDataType(input->type()): data type of the image (converted from input->type()) +lite_mat_cut.Init(w, h, lite_mat_rgb.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); + +// Call the Crop function with the provided arguments and store the result in the boolean variable 'ret' +bool ret = Crop(lite_mat_rgb, lite_mat_cut, x, y, w, h); + +// Check if 'ret' is false, indicating that the image crop failed +// If 'ret' is false, print the error message "Crop: image crop failed." and return from the function +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Crop: image crop failed."); + + *output = output_tensor; // Assign the value of output_tensor to the pointer variable output + return Status::OK(); // Return a Status object indicating successful execution + + } catch (const std::exception &e) { // Catch any exceptions thrown within the try block and assign it to the variable e + RETURN_STATUS_UNEXPECTED("Crop: " + std::string(e.what())); // Return a Status object with an error message that includes the exception message + + } + return Status::OK(); // Return a Status object indicating successful execution + +// Function to get the width and height of a JPEG image from a given input tensor Status GetJpegImageInfo(const std::shared_ptr &input, int *img_width, int *img_height) { + + // Create a struct to hold the decompression parameters for the JPEG image struct jpeg_decompress_struct cinfo {}; + + // Create a custom error manager for handling JPEG errors struct JpegErrorManagerCustom jerr {}; + + // Set the error manager for the decompression parameters cinfo.err = jpeg_std_error(&jerr.pub); + + // Set the custom error exit function for the error manager jerr.pub.error_exit = JpegErrorExitCustom; + try { + // Create a decompression object for the JPEG image jpeg_create_decompress(&cinfo); + + // Set the source of the JPEG image to the input tensor's buffer and size JpegSetSource(&cinfo, input->GetBuffer(), input->SizeInBytes()); + + // Read the header of the JPEG image (void)jpeg_read_header(&cinfo, TRUE); + + // Calculate the output dimensions of the JPEG image jpeg_calc_output_dimensions(&cinfo); } catch (const std::exception &e) { + // If an exception occurs, destroy the decompression object and return an unexpected status with the exception message jpeg_destroy_decompress(&cinfo); RETURN_STATUS_UNEXPECTED(e.what()); } + + // Set the output height and width using the decompression object's output dimensions *img_height = cinfo.output_height; *img_width = cinfo.output_width; + + // Destroy the decompression object jpeg_destroy_decompress(&cinfo); + + // Return a status indicating success return Status::OK(); } -Status Normalize(const std::shared_ptr &input, std::shared_ptr *output, - const std::vector &vec_mean, const std::vector &vec_std) { - if (input->Rank() != 3) { +// Normalize function takes an input tensor, output tensor pointer, vector of mean values, and vector of standard deviation values as parameters +// It returns a status indicating the success or failure of the normalization operation + +// Check if the rank of the input tensor is not equal to 3 +if (input->Rank() != 3) { + // If the rank is not 3, return an error status with the message "Normalize: image shape is not ." RETURN_STATUS_UNEXPECTED("Normalize: image shape is not ."); - } +} - if (input->type() != DataType::DE_UINT8 && input->type() != DataType::DE_FLOAT32) { +// Check if the input data type is not uint8 or float32 +if (input->type() != DataType::DE_UINT8 && input->type() != DataType::DE_FLOAT32) { + // If the condition is true, return an error message indicating that the image data type is not supported RETURN_STATUS_UNEXPECTED("Normalize: image datatype is not uint8 or float32."); - } +} - try { - LiteMat lite_mat_norm; - bool ret = false; - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); +// Create a LiteMat object named lite_mat_norm +LiteMat lite_mat_norm; + +// Initialize a boolean variable named ret and set it to false +bool ret = false; + +// Create a LiteMat object named lite_mat_rgb with the following parameters: +// - Width: obtained from the second dimension of the input shape +// - Height: obtained from the first dimension of the input shape +// - Channels: obtained from the third dimension of the input shape +// - Data: a pointer to the buffer of the input object, casted from const void* to void* +// - Data type: obtained from the input object's type using the GetLiteCVDataType function if (input->type() == DataType::DE_UINT8) { - LiteMat lite_mat_float; - // change input to float - ret = ConvertTo(lite_mat_rgb, lite_mat_float, 1.0); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Normalize: convert to float datatype failed."); - ret = SubStractMeanNormalize(lite_mat_float, lite_mat_norm, vec_mean, vec_std); + LiteMat lite_mat_float; + // Create a new LiteMat object to store the converted float values + ret = ConvertTo(lite_mat_rgb, lite_mat_float, 1.0); + // Check if the conversion to float datatype failed + CHECK_FAIL_RETURN_UNEXPECTED(ret, "Normalize: convert to float datatype failed."); + // Normalize the float values by subtracting mean and dividing by standard deviation + ret = SubStractMeanNormalize(lite_mat_float, lite_mat_norm, vec_mean, vec_std); } else { // float32 - ret = SubStractMeanNormalize(lite_mat_rgb, lite_mat_norm, vec_mean, vec_std); + // Normalize the float32 values by subtracting mean and dividing by standard deviation + ret = SubStractMeanNormalize(lite_mat_rgb, lite_mat_norm, vec_mean, vec_std); } + // Check if the normalization failed CHECK_FAIL_RETURN_UNEXPECTED(ret, "Normalize: normalize failed."); - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(input->shape(), DataType(DataType::DE_FLOAT32), - static_cast(lite_mat_norm.data_ptr_), &output_tensor)); +// Declare a shared pointer named "output_tensor" of type "Tensor" +std::shared_ptr output_tensor; - *output = output_tensor; - } catch (const std::exception &e) { +// Call the "CreateFromMemory" function of the "Tensor" class to create a new tensor +// Pass the shape of the input tensor, data type as float32, a pointer to the data, and a pointer to the output_tensor +// Use the "lite_mat_norm.data_ptr_" as the pointer to the data +// Use the "&output_tensor" to pass the address of the output_tensor pointer +RETURN_IF_NOT_OK(Tensor::CreateFromMemory(input->shape(), DataType(DataType::DE_FLOAT32), + static_cast(lite_mat_norm.data_ptr_), &output_tensor)); + + *output = output_tensor; // Assign the output_tensor to the output pointer + + } catch (const std::exception &e) { // Catch any exceptions thrown during the normalization process + + // Return an error status with a descriptive error message that includes the exception's what() message RETURN_STATUS_UNEXPECTED("Normalize: " + std::string(e.what())); } - return Status::OK(); -} -Status Resize(const std::shared_ptr &input, std::shared_ptr *output, int32_t output_height, - int32_t output_width, double fx, double fy, InterpolationMode mode) { + // Return a success status + return Status::OK(); + +// Check if the input tensor has a rank of 3 or 2, representing an image in the shape of or respectively if (input->Rank() != 3 && input->Rank() != 2) { RETURN_STATUS_UNEXPECTED("Resize: input image is not in shape of or "); } + + // Check if the data type of the input tensor is uint8 if (input->type() != DataType::DE_UINT8) { RETURN_STATUS_UNEXPECTED("Resize: image datatype is not uint8."); } - // resize image too large or too small + + // Set a limit for the scaling factor of the output height and width const int height_width_scale_limit = 1000; + + // Check if the output height or width is too large or too small if (output_height == 0 || output_height > input->shape()[0] * height_width_scale_limit || output_width == 0 || output_width > input->shape()[1] * height_width_scale_limit) { std::string err_msg = @@ -382,471 +716,947 @@ Status Resize(const std::shared_ptr &input, std::shared_ptr *out "1000 times the original image; 2) can not be 0."; return Status(StatusCode::kMDShapeMisMatch, err_msg); } - try { - LiteMat lite_mat_rgb; - TensorShape shape{output_height, output_width}; + + // Create a LiteMat object to store the resized image + LiteMat lite_mat_rgb; + + // Create a TensorShape object with the specified output height and width + TensorShape shape{output_height, output_width}; + // Check if the rank of the input is 2 if (input->Rank() == 2) { + + // Initialize the lite_mat_rgb object with the dimensions of the input lite_mat_rgb.Init(input->shape()[1], input->shape()[0], const_cast(reinterpret_cast(input->GetBuffer())), GetLiteCVDataType(input->type())); } else { // rank == 3 + + // Initialize the lite_mat_rgb object with the dimensions of the input lite_mat_rgb.Init(input->shape()[1], input->shape()[0], input->shape()[2], const_cast(reinterpret_cast(input->GetBuffer())), GetLiteCVDataType(input->type())); + + // Get the number of channels in the input int num_channels = input->shape()[2]; + + // Append the number of channels to the shape object shape = shape.AppendDim(num_channels); } - LiteMat lite_mat_resize; - std::shared_ptr output_tensor; - RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); +// Declare an object of type LiteMat named lite_mat_resize +LiteMat lite_mat_resize; - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); +// Declare a shared pointer to a Tensor object named output_tensor +std::shared_ptr output_tensor; - lite_mat_resize.Init(output_width, output_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); +// Call the CreateEmpty function of the Tensor class to create an empty tensor with the specified shape and data type of the input tensor +// The created tensor is assigned to the output_tensor shared pointer +// The function returns an error code, so we use the RETURN_IF_NOT_OK macro to check if the function call was successful +RETURN_IF_NOT_OK(Tensor::CreateEmpty(shape, input->type(), &output_tensor)); - bool ret = ResizeBilinear(lite_mat_rgb, lite_mat_resize, output_width, output_height); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); +// Create a pointer variable named "buffer" of type uint8_t (unsigned 8-bit integer) +// Use reinterpret_cast to convert the address of the output tensor's first element to a pointer of type uint8_t +// The & operator is used to get the address of the first element, and the * operator is used to dereference the iterator returned by begin() +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - *output = output_tensor; - } catch (const std::exception &e) { +// Initialize the `lite_mat_resize` object with the provided parameters: +// - `output_width`: the desired width of the resized image +// - `output_height`: the desired height of the resized image +// - `lite_mat_rgb.channel_`: the number of channels in the original image +// - `reinterpret_cast(buffer)`: a pointer to the buffer containing the original image data +// - `GetLiteCVDataType(input->type())`: the data type of the original image, obtained from `input->type()` +lite_mat_resize.Init(output_width, output_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), + GetLiteCVDataType(input->type())); + +// Call the ResizeBilinear function with the parameters lite_mat_rgb, lite_mat_resize, output_width, and output_height +bool ret = ResizeBilinear(lite_mat_rgb, lite_mat_resize, output_width, output_height); + +// Check the value of ret and if it is false, print an error message "Resize: bilinear resize failed." +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); + + *output = output_tensor; // Assign the output_tensor to the output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur during the try block + + // Return an error status with a descriptive error message that includes the exception's what() message RETURN_STATUS_UNEXPECTED("Resize: " + std::string(e.what())); + } + + // Return a success status return Status::OK(); -} -Status ResizePreserve(const TensorRow &inputs, int32_t height, int32_t width, int32_t img_orientation, - TensorRow *outputs) { - outputs->resize(3); - CHECK_FAIL_RETURN_UNEXPECTED(inputs.size() > 0, - "Invalid input, should be greater than 0, but got " + std::to_string(inputs.size())); - std::shared_ptr input = inputs[0]; - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be greater than 3 dimensions."); - LiteMat lite_mat_src(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); +// ResizePreserve function to resize an image while preserving its aspect ratio +// Takes inputs, height, width, img_orientation, and outputs as parameters - LiteMat lite_mat_dst; - std::shared_ptr image_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, DataType(DataType::DE_FLOAT32), &image_tensor)); - uint8_t *buffer = reinterpret_cast(&(*image_tensor->begin())); - lite_mat_dst.Init(width, height, input->shape()[2], reinterpret_cast(buffer), LDataType::FLOAT32); +// Resize the outputs tensor row to have 3 elements +outputs->resize(3); - float ratioShiftWShiftH[3] = {0}; - float invM[2][3] = {{0, 0, 0}, {0, 0, 0}}; - bool ret = - ResizePreserveARWithFiller(lite_mat_src, lite_mat_dst, height, width, &ratioShiftWShiftH, &invM, img_orientation); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); +// Check if the inputs tensor row has at least one element, otherwise return an error message +CHECK_FAIL_RETURN_UNEXPECTED(inputs.size() > 0, "Invalid input, should be greater than 0, but got " + std::to_string(inputs.size())); - std::shared_ptr ratio_tensor; - TensorShape ratio_shape = TensorShape({3}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(ratio_shape, DataType(DataType::DE_FLOAT32), - reinterpret_cast(&ratioShiftWShiftH), &ratio_tensor)); +// Get the first element of the inputs tensor row and assign it to the input shared pointer +std::shared_ptr input = inputs[0]; - std::shared_ptr invM_tensor; - TensorShape invM_shape = TensorShape({2, 3}); - RETURN_IF_NOT_OK(Tensor::CreateFromMemory(invM_shape, DataType(DataType::DE_FLOAT32), - reinterpret_cast(&invM), &invM_tensor)); +// Check if the input tensor shape has at least 3 dimensions, otherwise return an error message +CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be greater than 3 dimensions."); - (*outputs)[0] = image_tensor; - (*outputs)[1] = ratio_tensor; - (*outputs)[2] = invM_tensor; - return Status::OK(); -} +// Create a LiteMat object named lite_mat_src with the dimensions and data of the input tensor +LiteMat lite_mat_src(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); -Status RgbToBgr(const std::shared_ptr &input, std::shared_ptr *output) { - if (input->Rank() != 3) { +// Declare a variable of type LiteMat named lite_mat_dst +LiteMat lite_mat_dst; + +// Declare a shared pointer to a Tensor named image_tensor +std::shared_ptr image_tensor; + +// Create a new TensorShape object with dimensions {height, width, input->shape()[2]} +TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); + +// Create an empty Tensor with the specified shape, data type DE_FLOAT32, and assign it to the image_tensor pointer +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, DataType(DataType::DE_FLOAT32), &image_tensor)); + +// Get a pointer to the underlying data buffer of the image_tensor and cast it to a uint8_t pointer +uint8_t *buffer = reinterpret_cast(&(*image_tensor->begin())); + +// Initialize the lite_mat_dst object with the specified width, height, number of channels, buffer pointer, and data type FLOAT32 +lite_mat_dst.Init(width, height, input->shape()[2], reinterpret_cast(buffer), LDataType::FLOAT32); + +// Declare and initialize an array of three floats named ratioShiftWShiftH with all elements set to 0 +float ratioShiftWShiftH[3] = {0}; + +// Declare and initialize a 2D array of floats named invM with all elements set to 0 +float invM[2][3] = {{0, 0, 0}, {0, 0, 0}}; + +// Call the ResizePreserveARWithFiller function with the provided arguments and store the return value in the bool variable ret +bool ret = ResizePreserveARWithFiller(lite_mat_src, lite_mat_dst, height, width, &ratioShiftWShiftH, &invM, img_orientation); + +// Check if ret is false, and if so, print an error message using the CHECK_FAIL_RETURN_UNEXPECTED macro +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Resize: bilinear resize failed."); + +// Declare a shared pointer to a Tensor object named ratio_tensor +std::shared_ptr ratio_tensor; + +// Create a TensorShape object named ratio_shape with dimensions {3} +TensorShape ratio_shape = TensorShape({3}); + +// Call the CreateFromMemory function of the Tensor class to create a Tensor object from memory +// Pass the ratio_shape, DataType, a pointer to the ratioShiftWShiftH variable, and the address of the ratio_tensor +// The RETURN_IF_NOT_OK macro is used to check if the function call is successful and return an error if not +RETURN_IF_NOT_OK(Tensor::CreateFromMemory(ratio_shape, DataType(DataType::DE_FLOAT32), + reinterpret_cast(&ratioShiftWShiftH), &ratio_tensor)); + +// Declare a shared pointer to a Tensor object named invM_tensor +std::shared_ptr invM_tensor; + +// Create a TensorShape object named invM_shape with dimensions {2, 3} +TensorShape invM_shape = TensorShape({2, 3}); + +// Create a Tensor object named invM_tensor from memory, with the specified shape, data type, and data pointer +// The data pointer is casted to uint8_t* to match the expected parameter type +// The created Tensor object is assigned to the invM_tensor shared pointer +RETURN_IF_NOT_OK(Tensor::CreateFromMemory(invM_shape, DataType(DataType::DE_FLOAT32), + reinterpret_cast(&invM), &invM_tensor)); + +// Assign the value of image_tensor to the first element of the outputs array +(*outputs)[0] = image_tensor; + +// Assign the value of ratio_tensor to the second element of the outputs array +(*outputs)[1] = ratio_tensor; + +// Assign the value of invM_tensor to the third element of the outputs array +(*outputs)[2] = invM_tensor; + +// Return a Status object indicating that the operation was successful +return Status::OK(); + +// Convert RGB image to BGR image + +// Check if the input image has a rank of 3 (height, width, channels) +if (input->Rank() != 3) { + // If not, return an error message indicating that the input image is not in the expected shape RETURN_STATUS_UNEXPECTED("RgbToBgr: input image is not in shape of "); - } - if (input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: image datatype is not uint8."); - } - - try { - int output_height = input->shape()[0]; - int output_width = input->shape()[1]; - - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_convert; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({output_height, output_width, 3}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_convert.Init(output_width, output_height, 3, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); - - bool ret = - ConvertRgbToBgr(lite_mat_rgb, GetLiteCVDataType(input->type()), output_width, output_height, lite_mat_convert); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToBgr: RGBToBGR failed."); - - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); - } - return Status::OK(); } -Status RgbToGray(const std::shared_ptr &input, std::shared_ptr *output) { - if (input->Rank() != 3) { +// Check if the data type of the input image is uint8 +if (input->type() != DataType::DE_UINT8) { + // If not, return an error message indicating that the image data type is not uint8 + RETURN_STATUS_UNEXPECTED("RgbToBgr: image datatype is not uint8."); +} + + // Retrieve the height of the input shape and store it in the variable output_height + int output_height = input->shape()[0]; + + // Retrieve the width of the input shape and store it in the variable output_width + int output_width = input->shape()[1]; + +// Create a LiteMat object named lite_mat_rgb with the dimensions and data type of the input tensor +LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + +// Create a LiteMat object named lite_mat_convert +LiteMat lite_mat_convert; + +// Create a shared pointer to a Tensor object named output_tensor +std::shared_ptr output_tensor; + +// Create a new TensorShape object with the desired dimensions for the output tensor +TensorShape new_shape = TensorShape({output_height, output_width, 3}); + +// Create an empty tensor with the new shape and data type of the input tensor, and assign it to output_tensor +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + +// Get a pointer to the beginning of the output tensor's data buffer and cast it to a uint8_t pointer +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Initialize the lite_mat_convert object with the dimensions, data buffer, and data type of the output tensor +lite_mat_convert.Init(output_width, output_height, 3, reinterpret_cast(buffer), + GetLiteCVDataType(input->type())); + +// Call the function ConvertRgbToBgr with the provided arguments and assign the return value to the variable ret +bool ret = ConvertRgbToBgr(lite_mat_rgb, GetLiteCVDataType(input->type()), output_width, output_height, lite_mat_convert); + +// Check if ret is false, and if so, print an error message using the macro CHECK_FAIL_RETURN_UNEXPECTED +CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToBgr: RGBToBGR failed."); + + *output = output_tensor; // Assign the output tensor to the provided output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur during the conversion process + + // If an exception is caught, return an error status with a descriptive error message + RETURN_STATUS_UNEXPECTED("RgbToBgr: " + std::string(e.what())); + + } + + // If no exceptions occur, return a success status + return Status::OK(); + +// Convert an RGB image to grayscale + +// Check if the input image has the correct shape of +if (input->Rank() != 3) { + // If not, return an error message indicating the incorrect shape RETURN_STATUS_UNEXPECTED("RgbToGray: input image is not in shape of "); - } - if (input->type() != DataType::DE_UINT8) { +} + +// Check if the image datatype is uint8 +if (input->type() != DataType::DE_UINT8) { + // If not, return an error message indicating the incorrect datatype RETURN_STATUS_UNEXPECTED("RgbToGray: image datatype is not uint8."); - } +} - try { - int output_height = input->shape()[0]; - int output_width = input->shape()[1]; + // Retrieve the height of the input shape and store it in the variable output_height + int output_height = input->shape()[0]; - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - LiteMat lite_mat_convert; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({output_height, output_width, 1}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_convert.Init(output_width, output_height, 1, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); + // Retrieve the width of the input shape and store it in the variable output_width + int output_width = input->shape()[1]; - bool ret = - ConvertRgbToGray(lite_mat_rgb, GetLiteCVDataType(input->type()), output_width, output_height, lite_mat_convert); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToGray: RGBToGRAY failed."); +// Create a LiteMat object named lite_mat_rgb with the dimensions and data type of the input tensor +LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); - *output = output_tensor; - } catch (const std::exception &e) { +// Create a LiteMat object named lite_mat_convert +LiteMat lite_mat_convert; + +// Create a shared pointer to a Tensor object named output_tensor +std::shared_ptr output_tensor; + +// Create a new TensorShape object with the desired dimensions for the output tensor +TensorShape new_shape = TensorShape({output_height, output_width, 1}); + +// Create an empty tensor with the new shape and data type of the input tensor, and assign it to output_tensor +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + +// Get a pointer to the beginning of the output tensor's data buffer and cast it to a uint8_t pointer +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Initialize the lite_mat_convert object with the dimensions, data buffer, and data type of the output tensor +lite_mat_convert.Init(output_width, output_height, 1, reinterpret_cast(buffer), + GetLiteCVDataType(input->type())); + +// Assign the return value of the function ConvertRgbToGray to the variable ret +bool ret = ConvertRgbToGray(lite_mat_rgb, GetLiteCVDataType(input->type()), output_width, output_height, lite_mat_convert); + +// Check if ret is false, and if so, print an error message and return from the function +CHECK_FAIL_RETURN_UNEXPECTED(ret, "RgbToGray: RGBToGRAY failed."); + + *output = output_tensor; // Assign the output tensor to the provided output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur during the conversion process + + // Return an error status with a descriptive error message that includes the exception's what() message RETURN_STATUS_UNEXPECTED("RgbToGray: " + std::string(e.what())); } + + // Return a success status return Status::OK(); + +// Function to pad an input tensor with specified values and border types + +// Parameters: +// - input: shared pointer to the input tensor +// - output: pointer to the output tensor (passed by reference) +// - pad_top: number of rows to pad at the top +// - pad_bottom: number of rows to pad at the bottom +// - pad_left: number of columns to pad at the left +// - pad_right: number of columns to pad at the right +// - border_types: type of border to use for padding +// - fill_r: value for red channel of the padding +// - fill_g: value for green channel of the padding +// - fill_b: value for blue channel of the padding + +// Check if the input tensor is in the shape of +if (input->Rank() != 3) { + // If not, return an error message + RETURN_STATUS_UNEXPECTED("Pad: input image is not in shape of "); } -Status Pad(const std::shared_ptr &input, std::shared_ptr *output, const int32_t &pad_top, - const int32_t &pad_bottom, const int32_t &pad_left, const int32_t &pad_right, const BorderType &border_types, - uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { - if (input->Rank() != 3) { - RETURN_STATUS_UNEXPECTED("Pad: input image is not in shape of "); - } - - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { +// Check if the input data type is not float32 or uint8 +if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { + // If the condition is true, return an error message indicating that the image data type is not uint8 or float32 RETURN_STATUS_UNEXPECTED("Pad: image datatype is not uint8 or float32."); - } +} + // Check if any of the padding values are less than 0 if (pad_top < 0 || pad_bottom < 0 || pad_left < 0 || pad_right < 0) { + // If any of the padding values are less than 0, return an error message RETURN_STATUS_UNEXPECTED( "Pad: " "the top, bottom, left, right of pad must be greater than 0."); } - try { +// Try block to catch any exceptions that may occur during execution +try { + // Create a LiteMat object named lite_mat_rgb with the dimensions and data type specified + // The dimensions are obtained from the input shape + // The data buffer is obtained from the input buffer and casted to the appropriate type LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], const_cast(reinterpret_cast(input->GetBuffer())), GetLiteCVDataType(input->type())); + + // Declare a LiteMat object named lite_mat_pad, which will be used later LiteMat lite_mat_pad; - std::shared_ptr output_tensor; +// Declare a shared pointer named output_tensor of type Tensor. Shared pointers are used for managing dynamically allocated objects and automatically deallocate the memory when no longer needed. - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_) > pad_left, - "Invalid pad width."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_ + pad_left) > pad_right, - "Invalid pad width."); - int pad_width = lite_mat_rgb.width_ + pad_left + pad_right; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_) > pad_top, - "Invalid pad height."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_ + pad_top) > pad_bottom, - "Invalid pad height."); - int pad_height = lite_mat_rgb.height_ + pad_top + pad_bottom; - TensorShape new_shape = TensorShape({pad_height, pad_width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); +// Check if the sum of `lite_mat_rgb.width_` and `pad_left` exceeds the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_) > pad_left, + "Invalid pad width."); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); +// Check if the sum of `lite_mat_rgb.width_`, `pad_left`, and `pad_right` exceeds the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.width_ + pad_left) > pad_right, + "Invalid pad width."); - lite_mat_pad.Init(pad_width, pad_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); +// Calculate the total width after padding +int pad_width = lite_mat_rgb.width_ + pad_left + pad_right; - bool ret = Pad(lite_mat_rgb, lite_mat_pad, pad_top, pad_bottom, pad_left, pad_right, - PaddBorderType::PADD_BORDER_CONSTANT, fill_r, fill_g, fill_b); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Pad: pad failed."); +// Check if the sum of `lite_mat_rgb.height_` and `pad_top` exceeds the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_) > pad_top, + "Invalid pad height."); + +// Check if the sum of `lite_mat_rgb.height_`, `pad_top`, and `pad_bottom` exceeds the maximum value of `int32_t` +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - lite_mat_rgb.height_ + pad_top) > pad_bottom, + "Invalid pad height."); + +// Calculate the total height after padding +int pad_height = lite_mat_rgb.height_ + pad_top + pad_bottom; + +// Create a new shape for the output tensor with the padded dimensions +TensorShape new_shape = TensorShape({pad_height, pad_width, input->shape()[2]}); + +// Create an empty tensor with the new shape and the same data type as the input tensor +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + +// Create a pointer variable named "buffer" of type uint8_t (unsigned 8-bit integer) +// Use reinterpret_cast to convert the address of the output tensor's first element to a pointer of type uint8_t +// Dereference the pointer using the * operator and then use the & operator to get the address of the dereferenced value +// Store the resulting address in the buffer variable +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Initialize the lite_mat_pad object with the specified pad_width, pad_height, channel, buffer, and data type +lite_mat_pad.Init(pad_width, pad_height, lite_mat_rgb.channel_, reinterpret_cast(buffer), + GetLiteCVDataType(input->type())); + +// Declare a boolean variable named "ret" to store the return value of the Pad function +bool ret = Pad(lite_mat_rgb, lite_mat_pad, pad_top, pad_bottom, pad_left, pad_right, + PaddBorderType::PADD_BORDER_CONSTANT, fill_r, fill_g, fill_b); + +// Use the CHECK_FAIL_RETURN_UNEXPECTED macro to check if "ret" is false, and if so, print an error message +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Pad: pad failed."); + + *output = output_tensor; // Assign the output tensor to the output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur during the execution of the code + RETURN_STATUS_UNEXPECTED("Pad: " + std::string(e.what())); // Return an unexpected status with the error message from the exception - *output = output_tensor; - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Pad: " + std::string(e.what())); } - return Status::OK(); -} + + return Status::OK(); // Return a status indicating successful execution of the code + +// Define a function named "RotateAngleWithOutMirror" that takes three parameters: +// 1. A constant reference to a shared pointer of type Tensor named "input" +// 2. A pointer to a shared pointer of type Tensor named "output" +// 3. An unsigned 64-bit integer named "orientation" static Status RotateAngleWithOutMirror(const std::shared_ptr &input, std::shared_ptr *output, const uint64_t orientation) { + + // Start a try block to catch any exceptions that might be thrown within this function try { + // Declare and initialize two integer variables named "height" and "width" to 0 int height = 0; int width = 0; + + // Declare and initialize a double array named "M" with 6 elements, all set to 0 double M[6] = {}; - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); +// Create a LiteMat object named lite_mat_rgb with the following parameters: +// - Width: the second dimension of the input shape +// - Height: the first dimension of the input shape +// - Channels: the third dimension of the input shape +// - Data: a pointer to the buffer of the input object, casted from const void* to void* +// - Data type: the LiteCV data type of the input object, obtained using the GetLiteCVDataType function +LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + + // Check if the orientation is 3 if (orientation == 3) { - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - M[0] = -1.0f; - M[1] = 0.0f; - M[2] = lite_mat_rgb.width_ - 1; - M[3] = 0.0f; - M[4] = -1.0f; - M[5] = lite_mat_rgb.height_ - 1; - } else if (orientation == 6) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = -1.0f; - M[2] = lite_mat_rgb.height_ - 1; - M[3] = 1.0f; - M[4] = 0.0f; - M[5] = 0.0f; - } else if (orientation == 8) { - height = lite_mat_rgb.width_; - width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = 1.0f; - M[2] = 0.0f; - M[3] = -1.0f; - M[4] = 0.0f; - M[5] = lite_mat_rgb.width_ - 1.0f; - } else { - } + // If orientation is 3, set the height and width of the image to the height and width of lite_mat_rgb + height = lite_mat_rgb.height_; + width = lite_mat_rgb.width_; + // Set the transformation matrix M for orientation 3 + M[0] = -1.0f; + M[1] = 0.0f; + M[2] = lite_mat_rgb.width_ - 1; + M[3] = 0.0f; + M[4] = -1.0f; + M[5] = lite_mat_rgb.height_ - 1; + } + // Check if the orientation is 6 + else if (orientation == 6) { + // If orientation is 6, set the height and width of the image to the width and height of lite_mat_rgb + height = lite_mat_rgb.width_; + width = lite_mat_rgb.height_; + + // Set the transformation matrix M for orientation 6 + M[0] = 0.0f; + M[1] = -1.0f; + M[2] = lite_mat_rgb.height_ - 1; + M[3] = 1.0f; + M[4] = 0.0f; + M[5] = 0.0f; + } + // Check if the orientation is 8 + else if (orientation == 8) { + // If orientation is 8, set the height and width of the image to the width of lite_mat_rgb + height = lite_mat_rgb.width_; + width = lite_mat_rgb.height_; + } +// Assign the value of the height of lite_mat_rgb to the variable width +width = lite_mat_rgb.height_; + +// Assign specific values to the elements of the array M +M[0] = 0.0f; +M[1] = 1.0f; +M[2] = 0.0f; +M[3] = -1.0f; +M[4] = 0.0f; +M[5] = lite_mat_rgb.width_ - 1.0f; + +// If the condition in the if statement is not met, execute the code inside the else block +// (There is no code provided inside the else block) + + // Create an empty vector to store the size of the image std::vector dsize; + + // Add the width and height of the image to the vector dsize.push_back(width); dsize.push_back(height); + + // Create an instance of LiteMat LiteMat lite_mat_affine; + + // Create a shared pointer to a Tensor object std::shared_ptr output_tensor; + + // Create a new TensorShape with the specified dimensions TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); + + // Create an empty Tensor with the new shape and the same type as the input Tensor RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + + // Get a pointer to the beginning of the output Tensor's data and cast it to a uint8_t pointer uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + + // Initialize the LiteMat object with the specified width, height, number of channels, buffer pointer, and data type lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); +// Call the Affine function with the provided parameters and store the result in the boolean variable 'ret' +bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - *output = output_tensor; - } catch (const std::exception &e) { +// Check if 'ret' is false, and if so, print an error message using the CHECK_FAIL_RETURN_UNEXPECTED macro +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); + + *output = output_tensor; // Assign the output_tensor to the output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur within the try block + + // Return an error status with a descriptive error message that includes the exception's what() message RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); + } + + // Return a success status return Status::OK(); -} + +// Define a function named "RotateAngleWithMirror" that takes three parameters: +// 1. A constant reference to a shared pointer of type Tensor named "input" +// 2. A pointer to a shared pointer of type Tensor named "output" +// 3. An unsigned 64-bit integer named "orientation" static Status RotateAngleWithMirror(const std::shared_ptr &input, std::shared_ptr *output, const uint64_t orientation) { + + // Start a try block to catch any exceptions that might be thrown within this function try { + // Declare and initialize two integer variables named "height" and "width" to 0 int height = 0; int width = 0; + + // Declare and initialize a double array named "M" with 6 elements, all set to 0 double M[6] = {}; - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); +// Create a LiteMat object named lite_mat_rgb with the following parameters: +// - Width: the second dimension of the input shape +// - Height: the first dimension of the input shape +// - Channels: the third dimension of the input shape +// - Data: a pointer to the buffer of the input object, casted from const void* to void* +// - Data type: the LiteCV data type of the input object's type +LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + + // Check if the orientation is equal to 2 if (orientation == 2) { + + // If orientation is 2, set the height and width variables to the height and width of lite_mat_rgb height = lite_mat_rgb.height_; width = lite_mat_rgb.width_; + + // Set the transformation matrix M to the following values M[0] = -1.0f; M[1] = 0.0f; M[2] = lite_mat_rgb.width_ - 1; M[3] = 0.0f; M[4] = 1.0f; M[5] = 0.0f; - } else if (orientation == 5) { + } + + // Check if the orientation is equal to 5 + else if (orientation == 5) { + + // If orientation is 5, set the height and width variables to the width and height of lite_mat_rgb height = lite_mat_rgb.width_; width = lite_mat_rgb.height_; + + // Set the transformation matrix M to the following values M[0] = 0.0f; M[1] = 1.0f; M[2] = 0.0f; M[3] = 1.0f; M[4] = 0.0f; M[5] = 0.0f; - } else if (orientation == 7) { + } + + // Check if the orientation is equal to 7 + else if (orientation == 7) { + + // If orientation is 7, set the height variable to the width of lite_mat_rgb and the width variable to the height of lite_mat_rgb height = lite_mat_rgb.width_; width = lite_mat_rgb.height_; - M[0] = 0.0f; - M[1] = -1.0f; - M[2] = lite_mat_rgb.height_ - 1; - M[3] = -1.0f; - M[4] = 0.0f; - M[5] = lite_mat_rgb.width_ - 1; - } else if (orientation == 4) { - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - M[0] = 1.0f; - M[1] = 0.0f; - M[2] = 0.0f; - M[3] = 0.0f; - M[4] = -1.0f; - M[5] = lite_mat_rgb.height_ - 1; - } else { + + // Continue with the transformation matrix M + // ... } - std::vector dsize; - dsize.push_back(width); + // If the orientation is 1, set the width and height variables accordingly + if (orientation == 1) { + width = lite_mat_rgb.width_; + height = lite_mat_rgb.height_; + + // Set the transformation matrix M for orientation 1 + M[0] = 1.0f; + M[1] = 0.0f; + M[2] = 0.0f; + M[3] = 1.0f; + M[4] = 0.0f; + M[5] = 0.0f; + } + // If the orientation is 2, set the width and height variables accordingly + else if (orientation == 2) { + width = lite_mat_rgb.width_; + height = lite_mat_rgb.height_; + + // Set the transformation matrix M for orientation 2 + M[0] = -1.0f; + M[1] = 0.0f; + M[2] = lite_mat_rgb.width_ - 1; + M[3] = 0.0f; + M[4] = 1.0f; + M[5] = 0.0f; + } + // If the orientation is 3, set the width and height variables accordingly + else if (orientation == 3) { + width = lite_mat_rgb.height_; + height = lite_mat_rgb.width_; + + // Set the transformation matrix M for orientation 3 + M[0] = 0.0f; + M[1] = -1.0f; + M[2] = lite_mat_rgb.height_ - 1; + M[3] = -1.0f; + M[4] = 0.0f; + M[5] = lite_mat_rgb.width_ - 1; + } + // If the orientation is 4, set the width and height variables accordingly + else if (orientation == 4) { + width = lite_mat_rgb.height_; + height = lite_mat_rgb.width_; + + // Set the transformation matrix M for orientation 4 + M[0] = 1.0f; + M[1] = 0.0f; + M[2] = 0.0f; + M[3] = 0.0f; + M[4] = -1.0f; + M[5] = lite_mat_rgb.height_ - 1; + } + // If the orientation is not 1, 2, 3, or 4, do nothing + + // Create a vector to store the size of the image + std::vector dsize; + + // Add the width to the vector + dsize.push_back(width); + // Push the value of 'height' to the back of the 'dsize' vector + dsize.push_back(height); + + // Declare a variable 'lite_mat_affine' of type 'LiteMat' + LiteMat lite_mat_affine; + + // Declare a shared pointer 'output_tensor' of type 'Tensor' + std::shared_ptr output_tensor; + + // Create a new shape 'new_shape' with dimensions {height, width, input->shape()[2]} + TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); + + // Create an empty tensor 'output_tensor' with the shape 'new_shape' and the same type as 'input' + RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + + // Get a pointer to the beginning of the data in 'output_tensor' and cast it to a uint8_t pointer + uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + + // Initialize 'lite_mat_affine' with the dimensions 'width', 'height', 'lite_mat_rgb.channel_', + // the buffer pointer, and the LiteCV data type of 'input' + lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); +// Call the Affine function with the provided parameters and store the result in the boolean variable 'ret' +bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(0, 0, 0)); - *output = output_tensor; - } catch (const std::exception &e) { +// Check if 'ret' is false, and if so, print an error message using the CHECK_FAIL_RETURN_UNEXPECTED macro +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Rotate: rotate failed."); + + *output = output_tensor; // Assign the output_tensor to the output pointer + + } catch (const std::exception &e) { // Catch any exceptions that occur within the try block + + // Return an error status with a descriptive error message that includes the exception's what() message RETURN_STATUS_UNEXPECTED("Rotate: " + std::string(e.what())); - } - return Status::OK(); -} + } + + // Return a success status + return Status::OK(); + +// Check if the given orientation is a mirror orientation (2, 4, 5, or 7) static bool IsMirror(int orientation) { if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) { return true; } return false; } -// rotate the image by EXIF orientation + +// Rotate the image based on the EXIF orientation Status Rotate(const std::shared_ptr &input, std::shared_ptr *output, const uint64_t orientation) { + // Check if the input tensor has a rank of 2 or 3 if (input->Rank() != 2 || input->Rank() != 3) { + // Return an error status with a message indicating that the input image shape is not or RETURN_STATUS_UNEXPECTED("Rotate: input image is not in shape of or "); } - if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { - RETURN_STATUS_UNEXPECTED("Rotate: image datatype is not float32 or uint8."); - } +// Check if the input data type is not float32 or uint8 +if (input->type() != DataType::DE_FLOAT32 && input->type() != DataType::DE_UINT8) { - if (!IsMirror(orientation)) { - return RotateAngleWithOutMirror(input, output, orientation); - } else { - return RotateAngleWithMirror(input, output, orientation); - } + // If the condition is true, return an unexpected status with an error message + RETURN_STATUS_UNEXPECTED("Rotate: image datatype is not float32 or uint8."); } +// Check if the orientation is not a mirror orientation +if (!IsMirror(orientation)) { + // If it is not a mirror orientation, call the RotateAngleWithOutMirror function + // and return its result + return RotateAngleWithOutMirror(input, output, orientation); +} else { + // If it is a mirror orientation, call the RotateAngleWithMirror function + // and return its result + return RotateAngleWithMirror(input, output, orientation); +} + +// Function to perform affine transformation on an input tensor +// Takes an input tensor, applies the affine transformation using the provided matrix, and stores the result in the output tensor +// Also takes interpolation mode, fill color values, and returns the status of the operation + Status Affine(const std::shared_ptr &input, std::shared_ptr *output, const std::vector &mat, InterpolationMode interpolation, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) { try { + // Check if the interpolation mode is other than bilinear, and print a warning message if so if (interpolation != InterpolationMode::kLinear) { MS_LOG(WARNING) << "Only Bilinear interpolation supported for now"; } + + // Initialize variables for height and width of the input tensor int height = 0; int width = 0; + + // Check if the size of the matrix is valid (maximum 6 elements) CHECK_FAIL_RETURN_UNEXPECTED(mat.size() <= 6, "Invalid mat shape."); + + // Create an array of doubles to store the matrix values double M[6] = {}; + + // Convert the matrix values from float_t to double and store them in the array for (int i = 0; i < mat.size(); i++) { M[i] = static_cast(mat[i]); } - CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be 3."); - LiteMat lite_mat_rgb(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); +// Check if the size of the input shape is greater than or equal to 3 +CHECK_FAIL_RETURN_UNEXPECTED(input->shape().Size() >= 3, "Invalid input shape, should be 3."); - height = lite_mat_rgb.height_; - width = lite_mat_rgb.width_; - std::vector dsize; - dsize.push_back(width); - dsize.push_back(height); - LiteMat lite_mat_affine; - std::shared_ptr output_tensor; - TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); - RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); - uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); - lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), - GetLiteCVDataType(input->type())); +// Create a LiteMat object named lite_mat_rgb with the dimensions specified by the input shape +// The dimensions are obtained from the input shape using input->shape()[1], input->shape()[0], and input->shape()[2] +// The buffer of the LiteMat is set to the buffer of the input object using input->GetBuffer() +// The data type of the LiteMat is determined by the input type using GetLiteCVDataType(input->type()) - bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(fill_r, fill_g, fill_b)); - CHECK_FAIL_RETURN_UNEXPECTED(ret, "Affine: affine failed."); +// Assign the height of the lite_mat_rgb to the variable height +height = lite_mat_rgb.height_; - *output = output_tensor; - return Status::OK(); - } catch (const std::exception &e) { - RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); - } +// Assign the width of the lite_mat_rgb to the variable width +width = lite_mat_rgb.width_; + +// Create a vector to store the size of the image +std::vector dsize; + +// Add the width and height to the dsize vector +dsize.push_back(width); +dsize.push_back(height); + +// Create a LiteMat object named lite_mat_affine +LiteMat lite_mat_affine; + +// Create a shared pointer to a Tensor object named output_tensor +std::shared_ptr output_tensor; + +// Create a new TensorShape object with dimensions height, width, and the third dimension of the input tensor +TensorShape new_shape = TensorShape({height, width, input->shape()[2]}); + +// Create an empty tensor with the new shape, same type as the input tensor, and assign it to the output_tensor +RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + +// Get a pointer to the beginning of the output_tensor and cast it to a uint8_t pointer +uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + +// Initialize the lite_mat_affine with the width, height, channel of lite_mat_rgb, buffer pointer, and the data type of the input tensor +lite_mat_affine.Init(width, height, lite_mat_rgb.channel_, reinterpret_cast(buffer), + GetLiteCVDataType(input->type())); + +// Call the Affine function with the provided arguments and store the return value in the boolean variable 'ret' +bool ret = Affine(lite_mat_rgb, lite_mat_affine, M, dsize, UINT8_C3(fill_r, fill_g, fill_b)); + +// Check if 'ret' is false, indicating that the Affine function failed +// If 'ret' is false, print the error message "Affine: affine failed." and return from the function +CHECK_FAIL_RETURN_UNEXPECTED(ret, "Affine: affine failed."); + + *output = output_tensor; // Assign the value of output_tensor to the pointer variable output + + // Try block to catch any exceptions that may occur + try { + // Throw an exception with the error message "Affine: " concatenated with the what() message of the caught exception + // The what() function returns a string describing the exception + // Use the macro RETURN_STATUS_UNEXPECTED to return the error message as a Status object + RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); + } + // Catch block to handle the caught exception + catch (const std::exception &e) { + // Return the error message as a Status object + RETURN_STATUS_UNEXPECTED("Affine: " + std::string(e.what())); + } } -Status GaussianBlur(const std::shared_ptr &input, std::shared_ptr *output, int32_t kernel_x, - int32_t kernel_y, float sigma_x, float sigma_y) { - try { - LiteMat lite_mat_input; - if (input->Rank() == 3) { - if (input->shape()[2] != 1 && input->shape()[2] != 3) { - RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in channel of 1 or 3"); - } - lite_mat_input = LiteMat(input->shape()[1], input->shape()[0], input->shape()[2], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else if (input->Rank() == 2) { - lite_mat_input = LiteMat(input->shape()[1], input->shape()[0], - const_cast(reinterpret_cast(input->GetBuffer())), - GetLiteCVDataType(input->type())); - } else { - RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in shape of or "); - } +// The GaussianBlur function takes in an input tensor, performs a Gaussian blur operation on it, and stores the result in the output tensor. +// The function also takes in parameters for the size of the Gaussian kernel and the standard deviations for the x and y directions. +// Start of the try block to catch any exceptions that may occur during the execution of the function +try { + // Declare a LiteMat object to hold the input tensor data + LiteMat lite_mat_input; + + // Check the rank of the input tensor to determine its shape + if (input->Rank() == 3) { + // If the rank is 3, check if the third dimension is either 1 or 3 (indicating grayscale or RGB image) + if (input->shape()[2] != 1 && input->shape()[2] != 3) { + // If the third dimension is neither 1 nor 3, return an error message + RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in channel of 1 or 3"); + } + + // Create a LiteMat object with the dimensions and data of the input tensor + lite_mat_input = LiteMat(input->shape()[1], input->shape()[0], input->shape()[2], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + } else if (input->Rank() == 2) { + // If the rank is 2, create a LiteMat object with the dimensions and data of the input tensor + lite_mat_input = LiteMat(input->shape()[1], input->shape()[0], + const_cast(reinterpret_cast(input->GetBuffer())), + GetLiteCVDataType(input->type())); + } else { + // If the rank is neither 3 nor 2, return an error message + RETURN_STATUS_UNEXPECTED("GaussianBlur: input image is not in shape of or "); + } + // End of the try block + // Any exceptions thrown within the try block will be caught and handled in the catch block +} + + // Create a shared pointer to a Tensor object called output_tensor std::shared_ptr output_tensor; + + // Call the CreateEmpty function of the Tensor class to create an empty tensor with the same shape and type as the input tensor + // Store the result in the output_tensor RETURN_IF_NOT_OK(Tensor::CreateEmpty(input->shape(), input->type(), &output_tensor)); + + // Cast the memory address of the output_tensor to a uint8_t pointer and store it in the buffer variable uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + + // Create a LiteMat object called lite_mat_output and initialize it with the width, height, channel, buffer, and data type of the input tensor LiteMat lite_mat_output; lite_mat_output.Init(lite_mat_input.width_, lite_mat_input.height_, lite_mat_input.channel_, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); + + // Call the GaussianBlur function with the lite_mat_input, lite_mat_output, kernel size, and sigma values + // Store the result in the ret variable bool ret = GaussianBlur(lite_mat_input, lite_mat_output, {kernel_x, kernel_y}, static_cast(sigma_x), static_cast(sigma_y)); + + // Check if the GaussianBlur function returned true, if not, print an error message and return CHECK_FAIL_RETURN_UNEXPECTED(ret, "GaussianBlur: GaussianBlur failed."); + + // Assign the output_tensor to the output pointer *output = output_tensor; + + // Return a Status object indicating successful program execution return Status::OK(); + } catch (const std::exception &e) { + // Catch any exceptions thrown during the execution of the code and return an error message RETURN_STATUS_UNEXPECTED("GaussianBlur: " + std::string(e.what())); } } +// Function to validate the rank of an image + Status ValidateImageRank(const std::string &op_name, int32_t rank) { + + // Check if the rank is not 2 or 3 if (rank != 2 && rank != 3) { + + // Create an error message indicating the incorrect image shape std::string err_msg = op_name + ": image shape is not or , but got rank:" + std::to_string(rank); + + // If the rank is 1, suggest performing a Decode operation first if (rank == 1) { err_msg = err_msg + ", may need to do Decode operation first."; } + + // Return an error status with the error message RETURN_STATUS_UNEXPECTED(err_msg); } + + // Return a success status if the rank is valid return Status::OK(); } +// Function to convert a tensor from HWC (height, width, channel) format to CHW (channel, height, width) format Status HwcToChw(std::shared_ptr input, std::shared_ptr *output) { try { + // Check if the input tensor has rank less than or equal to 3 if (input->Rank() <= 3) { + // Get the dimensions of the input tensor int output_height = input->shape()[0]; int output_width = input->shape()[1]; int output_channel = input->shape()[2]; + + // Create a LiteMat object with the input tensor data in HWC format LiteMat lite_mat_hwc(input->shape()[1], input->shape()[0], input->shape()[2], const_cast(reinterpret_cast(input->GetBuffer())), GetLiteCVDataType(input->type())); + + // Create an empty LiteMat object for the output tensor in CHW format LiteMat lite_mat_chw; + + // Create a new output tensor with the shape in CHW format std::shared_ptr output_tensor; TensorShape new_shape = TensorShape({output_channel, output_height, output_width}); RETURN_IF_NOT_OK(Tensor::CreateEmpty(new_shape, input->type(), &output_tensor)); + + // Get the buffer of the output tensor and cast it to uint8_t pointer uint8_t *buffer = reinterpret_cast(&(*output_tensor->begin())); + + // Initialize the LiteMat object for the output tensor with the buffer and dimensions in CHW format lite_mat_chw.Init(output_height, output_channel, output_width, reinterpret_cast(buffer), GetLiteCVDataType(input->type())); + + // Convert the input tensor from HWC to CHW format using the HWC2CHW function bool ret = HWC2CHW(lite_mat_hwc, lite_mat_chw); + + // Check if the conversion was successful CHECK_FAIL_RETURN_UNEXPECTED(ret, "HwcToChw: HwcToChw failed."); + + // Assign the output tensor to the pointer provided *output = output_tensor; } else { + // Check if the input image is not in the shape of or RETURN_STATUS_UNEXPECTED("HwcToChw: input image is not in shape of or "); } } catch (const std::exception &e) { + // If an exception is caught, return an unexpected status with the error message RETURN_STATUS_UNEXPECTED("HwcToChw: " + std::string(e.what())); } + // Return a status indicating successful execution return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/math_utils.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/math_utils.cc index 9b8ebfa6da1..b37b7e19392 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/math_utils.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/math_utils.cc @@ -14,22 +14,39 @@ * limitations under the License. */ +// Include the header file "math_utils.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/math_utils.h" +// Include the algorithm header for using various algorithms like sorting, searching, etc. #include + +// Include the string header for using string-related functions and classes #include +// Start of the "mindspore" namespace namespace mindspore { +// Start of the "dataset" namespace namespace dataset { + +// Function to compute the upper and lower percentiles of a histogram Status ComputeUpperAndLowerPercentiles(std::vector *hist, int32_t hi_p, int32_t low_p, int32_t *hi, int32_t *lo) { + // Check if the input parameters are valid CHECK_FAIL_RETURN_UNEXPECTED(hist != nullptr, "hist is nullptr"); CHECK_FAIL_RETURN_UNEXPECTED(hi != nullptr, "hi is nullptr"); CHECK_FAIL_RETURN_UNEXPECTED(lo != nullptr, "lo is nullptr"); + try { + // Compute the total count of values in the histogram int32_t n = std::accumulate(hist->begin(), hist->end(), 0); + + // Define the maximum percentile value constexpr float kMaxPerc = 100.0; + + // Compute the cut value for the lower percentile int32_t cut = static_cast((low_p / kMaxPerc) * n); + + // Iterate through the histogram from the lower bound and subtract the cut value from each bin until cut becomes 0 for (int32_t lb = 0; lb < static_cast(hist->size()) && cut > 0; lb++) { if (cut > (*hist)[lb]) { cut -= (*hist)[lb]; @@ -39,7 +56,11 @@ Status ComputeUpperAndLowerPercentiles(std::vector *hist, int32_t hi_p, cut = 0; } } + + // Compute the cut value for the upper percentile cut = static_cast((hi_p / kMaxPerc) * n); + + // Iterate through the histogram from the upper bound and subtract the cut value from each bin until cut becomes 0 for (auto ub_iter = hist->end() - 1; ub_iter >= hist->begin() && cut > 0; ub_iter--) { if (cut > *ub_iter) { cut -= *ub_iter; @@ -49,39 +70,90 @@ Status ComputeUpperAndLowerPercentiles(std::vector *hist, int32_t hi_p, cut = 0; } } + + // Initialize the lower and upper bounds *lo = 0; *hi = hist->size() - 1; + + // Find the first non-zero bin from the lower bound for (; (*lo) < (*hi) && !(*hist)[*lo]; (*lo)++) { } + + // Find the first non-zero bin from the upper bound for (; (*hi) >= 0 && !(*hist)[*hi]; (*hi)--) { } } catch (const std::exception &e) { + // If an exception occurs, create an error message and return an unexpected status std::string err_message = "AutoContrast: ComputeUpperAndLowerPercentiles failed: "; err_message += e.what(); RETURN_STATUS_UNEXPECTED(err_message); } + // Return success status return Status::OK(); } -Status DegreesToRadians(float_t degrees, float_t *radians_target) { - CHECK_FAIL_RETURN_UNEXPECTED(radians_target != nullptr, "radians_target is nullptr"); - *radians_target = CV_PI * degrees / 180.0; +// End of the "dataset" namespace +} // namespace dataset + +// End of the "mindspore" namespace +} // namespace mindspore + } + + // Return the OK status to indicate successful program termination return Status::OK(); } +// Function to convert degrees to radians +// Takes a float value representing degrees and a pointer to a float value representing the target radians +Status DegreesToRadians(float_t degrees, float_t *radians_target) { + + // Check if the pointer to the target radians is not nullptr + CHECK_FAIL_RETURN_UNEXPECTED(radians_target != nullptr, "radians_target is nullptr"); + + // Convert degrees to radians using the formula: radians = (pi * degrees) / 180.0 + *radians_target = CV_PI * degrees / 180.0; + + // Return OK status to indicate successful conversion + return Status::OK(); +} + +// Function to generate a random real number within a given range +// Parameters: +// - a: lower bound of the range +// - b: upper bound of the range +// - rnd: pointer to an instance of std::mt19937, a random number generator +// - result: pointer to a float_t variable where the generated number will be stored +// Returns: +// - Status: indicates the success or failure of the function + Status GenerateRealNumber(float_t a, float_t b, std::mt19937 *rnd, float_t *result) { + + // Check if the rnd pointer is not null CHECK_FAIL_RETURN_UNEXPECTED(rnd != nullptr, "rnd is nullptr"); + + // Check if the result pointer is not null CHECK_FAIL_RETURN_UNEXPECTED(result != nullptr, "result is nullptr"); + try { + // Create a uniform real distribution with the given range std::uniform_real_distribution distribution{a, b}; + + // Generate a random number using the distribution and the random number generator *result = distribution(*rnd); } catch (const std::exception &e) { + // If an exception occurs, create an error message with the exception details std::string err_message = "RandomAffine: GenerateRealNumber failed: "; err_message += e.what(); + + // Return the error message as a Status object RETURN_STATUS_UNEXPECTED(err_message); } + + // Return a Status object indicating success return Status::OK(); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/mixup_batch_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/mixup_batch_op.cc index 7f07d130903..ee50c0db9a5 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/mixup_batch_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/mixup_batch_op.cc @@ -14,95 +14,199 @@ * limitations under the License. */ -#include "minddata/dataset/kernels/image/mixup_batch_op.h" +// Include the header file "minddata/dataset/kernels/image/mixup_batch_op.h" which contains the declaration of the MixupBatchOp class and its associated functions and variables. +// Include the header for numeric limits (provides information about the limits of numeric types) #include + +// Include the header for string manipulation #include + +// Include the header for utility functions (provides various utility functions) #include +// Include the header file for cv_tensor from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for data_utils from the minddata/dataset/kernels/data directory #include "minddata/dataset/kernels/data/data_utils.h" + +// Include the header file for random from the minddata/dataset/util directory #include "minddata/dataset/util/random.h" + +// Include the header file for status from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant variable `kExpectedImageShapeSize` with a value of 4 constexpr size_t kExpectedImageShapeSize = 4; + +// Define a constant variable `kMaxLabelShapeSize` with a value of 3 constexpr size_t kMaxLabelShapeSize = 3; + +// Define a constant variable `kMinLabelShapeSize` with a value of 2 constexpr size_t kMinLabelShapeSize = 2; + +// Define a constant variable `dimension_one` with a value of 1 constexpr size_t dimension_one = 1; + +// Define a constant variable `dimension_two` with a value of 2 constexpr size_t dimension_two = 2; + +// Define a constant variable `dimension_three` with a value of 3 constexpr size_t dimension_three = 3; + +// Define a constant variable `value_one` with a value of 1 constexpr int64_t value_one = 1; + +// Define a constant variable `value_three` with a value of 3 constexpr int64_t value_three = 3; -MixUpBatchOp::MixUpBatchOp(float alpha) : alpha_(alpha) { rnd_.seed(GetSeed()); } +// Define the constructor for the MixUpBatchOp class, which takes a float parameter alpha +MixUpBatchOp::MixUpBatchOp(float alpha) : alpha_(alpha) { -Status MixUpBatchOp::ComputeLabels(const TensorRow &input, std::shared_ptr *out_labels, - std::vector *rand_indx, const std::vector &label_shape, - const float lam, const size_t images_size) { - CHECK_FAIL_RETURN_UNEXPECTED( - images_size <= static_cast(std::numeric_limits::max()), - "The \'images_size\' must not be more than \'INT64_MAX\', but got: " + std::to_string(images_size)); - for (int64_t i = 0; i < static_cast(images_size); i++) { - rand_indx->push_back(i); - } - std::shuffle(rand_indx->begin(), rand_indx->end(), rnd_); + // Initialize the random number generator with a seed obtained from GetSeed() function + rnd_.seed(GetSeed()); +} - RETURN_IF_NOT_OK(TypeCast(std::move(input.at(1)), out_labels, DataType(DataType::DE_FLOAT32))); +// ComputeLabels function to generate labels for a batch of input data +// Parameters: +// - input: the input data in the form of a TensorRow +// - out_labels: a pointer to a shared_ptr of Tensor, which will store the generated labels +// - rand_indx: a pointer to a vector of int64_t, which will store the randomly shuffled indices +// - label_shape: a vector of int64_t, specifying the shape of the labels +// - lam: a float value representing the lambda parameter +// - images_size: the size of the input images - int64_t row_labels = label_shape.size() == kMaxLabelShapeSize ? label_shape[1] : 1; - int64_t num_classes = label_shape.size() == kMaxLabelShapeSize ? label_shape[dimension_two] : label_shape[1]; +// Check if images_size is within the range of int64_t +CHECK_FAIL_RETURN_UNEXPECTED( + images_size <= static_cast(std::numeric_limits::max()), + "The 'images_size' must not be more than 'INT64_MAX', but got: " + std::to_string(images_size)); + +// Iterate over the range of images_size and push each index into the rand_indx vector +for (int64_t i = 0; i < static_cast(images_size); i++) { + rand_indx->push_back(i); +} + +// Shuffle the elements in the rand_indx vector using the rnd_ random number generator +std::shuffle(rand_indx->begin(), rand_indx->end(), rnd_); + +// Call the TypeCast function with the second element of the input vector as the argument +// Use std::move to transfer ownership of the element to the function +// The result of the TypeCast function is then passed as an argument to the RETURN_IF_NOT_OK macro +// The macro checks if the result is an OK status, and if not, returns the error status +// The TypeCast function converts the input element to the DataType DE_FLOAT32 and stores the result in out_labels + +// Check if the size of label_shape is equal to kMaxLabelShapeSize +// If it is, assign the value at index 1 of label_shape to row_labels +// Otherwise, assign 1 to row_labels +int64_t row_labels = label_shape.size() == kMaxLabelShapeSize ? label_shape[1] : 1; + +// Check if the size of label_shape is equal to kMaxLabelShapeSize +// If it is, assign the value at index dimension_two of label_shape to num_classes +// Otherwise, assign the value at index 1 of label_shape to num_classes +int64_t num_classes = label_shape.size() == kMaxLabelShapeSize ? label_shape[dimension_two] : label_shape[1]; for (int64_t i = 0; i < label_shape[0]; i++) { + // Iterate over the first dimension of the label shape + for (int64_t j = 0; j < row_labels; j++) { + // Iterate over the row labels + for (int64_t k = 0; k < num_classes; k++) { + // Iterate over the number of classes + std::vector first_index = label_shape.size() == kMaxLabelShapeSize ? std::vector{i, j, k} : std::vector{i, k}; + // Create a vector to store the first index based on the size of the label shape + std::vector second_index = label_shape.size() == kMaxLabelShapeSize ? std::vector{(*rand_indx)[static_cast(i)], j, k} : std::vector{(*rand_indx)[static_cast(i)], k}; + // Create a vector to store the second index based on the size of the label shape and the random index + if (input.at(1)->type().IsSignedInt()) { + // Check if the type of the input tensor at index 1 is a signed integer + int64_t first_value, second_value; + // Declare variables to store the first and second values + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); + // Get the value at the first index from the input tensor and store it in first_value + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); + // Get the value at the second index from the input tensor and store it in second_value + RETURN_IF_NOT_OK((*out_labels)->SetItemAt(first_index, lam * first_value + (1 - lam) * second_value)); + // Set the value at the first index in the output labels tensor to the linear combination of first_value and second_value } else { + // If the type of the input tensor at index 1 is not a signed integer + uint64_t first_value, second_value; + // Declare variables to store the first and second values + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&first_value, first_index)); + // Get the value at the first index from the input tensor and store it in first_value + RETURN_IF_NOT_OK(input.at(1)->GetItemAt(&second_value, second_index)); + // Get the value at the second index from the input tensor and store it in second_value + RETURN_IF_NOT_OK((*out_labels)->SetItemAt(first_index, lam * first_value + (1 - lam) * second_value)); + // Set the value at the first index in the output labels tensor to the linear combination of first_value and second_value } } } } + // Return Status::OK() to indicate successful execution of the function return Status::OK(); } -Status MixUpBatchOp::Compute(const TensorRow &input, TensorRow *output) { +// Check if the size of the input data is less than 2 if (input.size() < 2) { + // If the size is less than 2, return an error message with the size of the input data RETURN_STATUS_UNEXPECTED("MixUpBatch: size of input data should be 2 (including images or labels), but got: " + std::to_string(input.size()) + ", check 'input_columns' when call this operator."); } - std::vector> images; - std::vector image_shape = input.at(0)->shape().AsVector(); - std::vector label_shape = input.at(1)->shape().AsVector(); +// Declare a vector named "images" that stores shared pointers to CVTensor objects +std::vector> images; - // Check inputs +// Declare a vector named "image_shape" and initialize it with the shape of the first element in the "input" vector +std::vector image_shape = input.at(0)->shape().AsVector(); + +// Declare a vector named "label_shape" and initialize it with the shape of the second element in the "input" vector +std::vector label_shape = input.at(1)->shape().AsVector(); + + // Check if the size of the image shape is not equal to the expected size or if the first element of the image shape is not equal to the first element of the label shape if (image_shape.size() != kExpectedImageShapeSize || image_shape[0] != label_shape[0]) { + // Return an error message indicating the expected and actual sizes of the image shape and label shape RETURN_STATUS_UNEXPECTED("MixUpBatch: rank of image shape should be: " + std::to_string(kExpectedImageShapeSize) + ", but got: " + std::to_string(image_shape.size()) + ", make sure image shape are or and batched before calling MixUpBatch."); } + + // Check if the type of the second input column (labels) is not an integer type if (!input.at(1)->type().IsInt()) { + // Return an error message indicating that the labels must only include int types and the actual type of the second input column RETURN_STATUS_UNEXPECTED( "MixUpBatch: wrong labels type. The second column (labels) must only include int types, but got: " + input.at(1)->type().ToString()); } + // Check if the size of the label shape is not equal to the minimum label shape size and not equal to the maximum label shape size if (label_shape.size() != kMinLabelShapeSize && label_shape.size() != kMaxLabelShapeSize) { + // Return an unexpected status with an error message explaining the expected label shape RETURN_STATUS_UNEXPECTED( "MixUpBatch: wrong labels shape. " "The second column (labels) must have a shape of NC or NLC where N is the batch size, " @@ -110,66 +214,131 @@ Status MixUpBatchOp::Compute(const TensorRow &input, TensorRow *output) { "labels must be in one-hot format and in a batch, but got rank: " + std::to_string(label_shape.size())); } + + // Check if the image shape does not match the expected dimensions if ((image_shape[dimension_one] != value_one && image_shape[dimension_one] != value_three) && (image_shape[dimension_three] != value_one && image_shape[dimension_three] != value_three)) { + // Return an unexpected status with an error message explaining the expected image shape RETURN_STATUS_UNEXPECTED("MixUpBatch: images shape should in or , got shape:" + input.at(0)->shape().ToString()); } - // Move images into a vector of CVTensors - RETURN_IF_NOT_OK(BatchTensorToCVTensorVector(input.at(0), &images)); +// Move images into a vector of CVTensors +RETURN_IF_NOT_OK(BatchTensorToCVTensorVector(input.at(0), &images)); // Calculating lambda - // If x1 is a random variable from Gamma(a1, 1) and x2 is a random variable from Gamma(a2, 1) + + // If x1 is a random variable from Gamma(a1, 1) and x2 is a random variable from Gamma(a2, 1), // then x = x1 / (x1+x2) is a random variable from Beta(a1, a2) + + // Create a gamma distribution with shape parameter alpha_ and scale parameter 1 std::gamma_distribution distribution(alpha_, 1); + + // Generate random variables x1 and x2 from the gamma distribution float x1 = distribution(rnd_); float x2 = distribution(rnd_); + + // Check if the multiplication of x1 and x2 is within the bounds of float_t CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() - x1) > x2, "multiplication out of bounds, with multipliers: " + std::to_string(x1) + " and " + std::to_string(x2) + ", which result in the out of bounds product:" + std::to_string(x1 * x2)); + + // Check if the sum of x1 and x2 is not equal to 0 CHECK_FAIL_RETURN_UNEXPECTED(x1 + x2 != 0.0, "addition of variable(x1 and x2) of Gamma should not be 0."); + + // Calculate lambda by dividing x1 by the sum of x1 and x2 float lam = x1 / (x1 + x2); - // Calculate random labels - std::vector rand_indx; - std::shared_ptr out_labels; +// Create an empty vector to store random indices +std::vector rand_indx; - // Compute labels - RETURN_IF_NOT_OK(ComputeLabels(input, &out_labels, &rand_indx, label_shape, lam, images.size())); +// Create a shared pointer to a Tensor object called out_labels +std::shared_ptr out_labels; + +// Compute labels + +// Call the function ComputeLabels with the following parameters: +// - input: the input data +// - out_labels: the output labels +// - rand_indx: the random index +// - label_shape: the shape of the labels +// - lam: the lambda value +// - images.size(): the size of the images +RETURN_IF_NOT_OK(ComputeLabels(input, &out_labels, &rand_indx, label_shape, lam, images.size())); // Compute images + + // Iterate over the images vector using an int64_t index variable i for (int64_t i = 0; i < images.size(); i++) { + + // Create a TensorShape object called remaining with a single dimension of -1 TensorShape remaining({-1}); + + // Declare a pointer to uchar called start_addr_of_index and initialize it to nullptr uchar *start_addr_of_index = nullptr; + + // Declare a shared pointer to Tensor called out std::shared_ptr out; + + // Call the StartAddrOfIndex function on the first element of the input vector, passing in the index {rand_indx[i], 0, 0, 0} + // and the addresses of start_addr_of_index and remaining as output parameters RETURN_IF_NOT_OK(input.at(0)->StartAddrOfIndex({rand_indx[i], 0, 0, 0}, &start_addr_of_index, &remaining)); + + // Call the CreateFromMemory function on the first element of the input vector, passing in the image shape and type, + // start_addr_of_index, and the address of out as an output parameter RETURN_IF_NOT_OK(input.at(0)->CreateFromMemory( TensorShape({image_shape[dimension_one], image_shape[dimension_two], image_shape[dimension_three]}), input.at(0)->type(), start_addr_of_index, &out)); + + // Convert the shared pointer to Tensor out to a shared pointer to CVTensor called rand_image std::shared_ptr rand_image = CVTensor::AsCVTensor(std::move(out)); + + // Check if the data pointer of the mat member of rand_image is null if (!rand_image->mat().data) { + + // If it is null, return an unexpected status with an error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] MixUpBatch: allocate memory failed."); } + + // Update the mat member of the i-th element of the images vector by performing a linear combination of the original image + // and the rand_image, using the mixing coefficient lam images[i]->mat() = lam * images[i]->mat() + (1 - lam) * rand_image->mat(); } - // Move the output into a TensorRow + // Create a shared pointer to a Tensor object called output_image std::shared_ptr output_image; + + // Create an empty Tensor with the same shape and data type as the first input Tensor + // and assign it to the output_image pointer RETURN_IF_NOT_OK(Tensor::CreateEmpty(input.at(0)->shape(), input.at(0)->type(), &output_image)); + + // Iterate over the images vector for (int64_t i = 0; i < images.size(); i++) { + + // Insert each image Tensor from the images vector into the output_image Tensor RETURN_IF_NOT_OK(output_image->InsertTensor({i}, images[i])); } + + // Push the output_image Tensor into the output vector output->push_back(output_image); + + // Push the out_labels Tensor into the output vector output->push_back(out_labels); - return Status::OK(); -} +// Return the OK status from the Status class +return Status::OK(); +// Define the Print function of the MixUpBatchOp class void MixUpBatchOp::Print(std::ostream &out) const { + + // Use the provided output stream to print the MixUpBatchOp information out << "MixUpBatchOp: " << "alpha: " << alpha_ << "\n"; } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_op.cc index c286bab74ac..c01f55da8f8 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_op.cc @@ -15,41 +15,79 @@ */ #include "minddata/dataset/kernels/image/normalize_op.h" +// Include the random header for generating random numbers #include + +// Include the vector header for using the vector container #include +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Definition of the constructor for the NormalizeOp class, taking in two vectors of floats as parameters NormalizeOp::NormalizeOp(const std::vector &mean, const std::vector &std) : mean_(mean), std_(std) { - // pre-calculate normalized mean to be used later in each Compute + + // Pre-calculate the normalized mean to be used later in each Compute for (int64_t i = 0; i < mean.size(); i++) { mean_[i] = mean_[i] / std_[i]; } } +// Compute function for the NormalizeOp class Status NormalizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid IO_CHECK(input, output); - // Doing the Normalization + + // Call the Normalize function to perform the normalization + // Pass the input, output, mean, and std as arguments return Normalize(input, output, mean_, std_); } +// Definition of the Print function for the NormalizeOp class + void NormalizeOp::Print(std::ostream &out) const { + + // Print the initial part of the output message out << "NormalizeOp, mean: "; + + // Iterate over each element in the mean_ vector and print it followed by a comma for (const auto &m : mean_) { out << m << ", "; } + + // Print the closing brace for the mean_ vector and start a new line out << "}" << std::endl << "std: "; + + // Iterate over each element in the std_ vector and print it followed by a comma for (const auto &s : std_) { out << s << ", "; } + + // Print the closing brace for the std_ vector and start a new line out << "}" << std::endl; + } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_pad_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_pad_op.cc index 476c6557617..50e08aebdd4 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_pad_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/normalize_pad_op.cc @@ -15,36 +15,80 @@ */ #include "minddata/dataset/kernels/image/normalize_pad_op.h" +// Include the random header, which provides facilities for generating random numbers #include +// Include the header file for image utilities from the MindData dataset library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData dataset library #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Definition of the constructor for the NormalizePadOp class NormalizePadOp::NormalizePadOp(float mean_r, float mean_g, float mean_b, float std_r, float std_g, float std_b, std::string dtype) { + + // Create a Tensor object from the mean values Status s = Tensor::CreateFromVector({mean_r, mean_g, mean_b}, &mean_); + + // Check if there was an error creating the Tensor if (s.IsError()) { + // Log an error message with the invalid mean values MS_LOG(ERROR) << "NormalizePad: invalid mean value, got: (" + std::to_string(mean_r) + std::to_string(mean_g) + std::to_string(mean_b) + ")."; } + + // Create a Tensor object from the standard deviation values s = Tensor::CreateFromVector({std_r, std_g, std_b}, &std_); + + // Check if there was an error creating the Tensor if (s.IsError()) { + // Log an error message with the invalid standard deviation values MS_LOG(ERROR) << "NormalizePad: invalid std value, got: (" + std::to_string(std_r) + std::to_string(std_g) + std::to_string(std_b) + ")."; } + + // Store the dtype value dtype_ = dtype; } +// End of the "dataset" namespace +} // namespace dataset + +// End of the "mindspore" namespace +} // namespace mindspore + +// Compute function for the NormalizePadOp class Status NormalizePadOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); - // Doing the Normalization + pad + + // Call the NormalizePad function to perform normalization and padding + // Pass the input tensor, output tensor, mean, standard deviation, and data type as arguments return NormalizePad(input, output, mean_, std_, dtype_); } +// Definition of the Print function for the NormalizePadOp class + void NormalizePadOp::Print(std::ostream &out) const { - out << "NormalizeOp, mean: " << *(mean_.get()) << std::endl << "std: " << *(std_.get()) << std::endl; + + // Output the string "NormalizeOp, mean: " followed by the value of the mean_ variable + out << "NormalizeOp, mean: " << *(mean_.get()) << std::endl; + + // Output the string "std: " followed by the value of the std_ variable + out << "std: " << *(std_.get()) << std::endl; + } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/pad_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/pad_op.cc index bdd497738f6..8bada794cf9 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/pad_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/pad_op.cc @@ -15,42 +15,82 @@ */ #include "minddata/dataset/kernels/image/pad_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for dataset constants from the MindData library #include "minddata/dataset/include/dataset/constants.h" + +// Include the header file for status utilities from the MindData library #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const BorderType PadOp::kDefBorderType = BorderType::kConstant; -const uint8_t PadOp::kDefFillR = 0; -const uint8_t PadOp::kDefFillG = 0; -const uint8_t PadOp::kDefFillB = 0; + // Define the nested namespace "dataset" + namespace dataset { + + // Define the constant variable "kDefBorderType" of type BorderType and initialize it with BorderType::kConstant + const BorderType PadOp::kDefBorderType = BorderType::kConstant; + + // Define the constant variable "kDefFillR" of type uint8_t and initialize it with 0 + const uint8_t PadOp::kDefFillR = 0; + + // Define the constant variable "kDefFillG" of type uint8_t and initialize it with 0 + const uint8_t PadOp::kDefFillG = 0; + + // Define the constant variable "kDefFillB" of type uint8_t and initialize it with 0 + const uint8_t PadOp::kDefFillB = 0; + } +} + +// Define the constructor for the PadOp class, which takes in several parameters to initialize the object PadOp::PadOp(int32_t pad_top, int32_t pad_bottom, int32_t pad_left, int32_t pad_right, BorderType padding_mode, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : pad_top_(pad_top), - pad_bottom_(pad_bottom), - pad_left_(pad_left), - pad_right_(pad_right), - boarder_type_(padding_mode), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} + : pad_top_(pad_top), // Initialize the pad_top_ member variable with the value of the pad_top parameter + pad_bottom_(pad_bottom), // Initialize the pad_bottom_ member variable with the value of the pad_bottom parameter + pad_left_(pad_left), // Initialize the pad_left_ member variable with the value of the pad_left parameter + pad_right_(pad_right), // Initialize the pad_right_ member variable with the value of the pad_right parameter + boarder_type_(padding_mode), // Initialize the boarder_type_ member variable with the value of the padding_mode parameter + fill_r_(fill_r), // Initialize the fill_r_ member variable with the value of the fill_r parameter + fill_g_(fill_g), // Initialize the fill_g_ member variable with the value of the fill_g parameter + fill_b_(fill_b) {} // Initialize the fill_b_ member variable with the value of the fill_b parameter +// The Compute function of the PadOp class Status PadOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the Pad function to perform padding on the input tensor + // Pass the input tensor, output tensor, padding values, border type, and fill color return Pad(input, output, pad_top_, pad_bottom_, pad_left_, pad_right_, boarder_type_, fill_r_, fill_g_, fill_b_); } +// Function to determine the output shape of the PadOp operation Status PadOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); - TensorShape out({-1, -1, 3}); // we don't know what is output image size, but we know it should be 3 channels + + // Create a TensorShape object with dimensions {-1, -1, 3} + // The actual values of the first two dimensions are unknown, but we know the output should have 3 channels + TensorShape out({-1, -1, 3}); + + // If the input has a rank of 1, add the output shape to the outputs vector if (inputs[0].Rank() == 1) outputs.emplace_back(out); + + // If the outputs vector is not empty, return OK status if (!outputs.empty()) return Status::OK(); + + // If the outputs vector is empty, return an error status with a message return Status( StatusCode::kMDUnexpectedError, "Pad: invalid input shape, expected 1D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/posterize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/posterize_op.cc index d0a4c8fffd4..d37b4dc010c 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/posterize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/posterize_op.cc @@ -14,44 +14,91 @@ * limitations under the License. */ +// Include the header file "posterize_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/posterize_op.h" +// Include the OpenCV image codecs header file, which provides functions for reading and writing images #include +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code for the "mindspore::dataset" namespace goes here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant variable named "kBit" of type uint8_t (unsigned 8-bit integer) +// This variable is a member of the PosterizeOp class const uint8_t PosterizeOp::kBit = 8; +// Define the constructor for the PosterizeOp class, which takes a parameter of type uint8_t named bit PosterizeOp::PosterizeOp(uint8_t bit) : bit_(bit) {} -Status PosterizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - uint8_t mask_value = ~((uint8_t)(1 << (8 - bit_)) - 1); - std::shared_ptr input_cv = CVTensor::AsCVTensor(input); - if (!input_cv->mat().data) { - RETURN_STATUS_UNEXPECTED("[Internal ERROR] Posterize: load image failed."); - } - if (input_cv->Rank() != 3 && input_cv->Rank() != 2) { - RETURN_STATUS_UNEXPECTED("Posterize: input image is not in shape of or , but got rank: " + - std::to_string(input_cv->Rank())); - } - std::vector lut_vector; - for (std::size_t i = 0; i < 256; i++) { - lut_vector.push_back(i & mask_value); - } - cv::Mat in_image = input_cv->mat(); +// Compute function for the PosterizeOp class, which takes an input tensor and produces an output tensor +// The function returns a status indicating success or failure - cv::Mat output_img; - CHECK_FAIL_RETURN_UNEXPECTED(in_image.depth() == CV_8U || in_image.depth() == CV_8S, - "Posterize: data type of input image should be int, " - "but got " + - input->type().ToString()); - cv::LUT(in_image, lut_vector, output_img); - std::shared_ptr result_tensor; +// Calculate the mask value based on the number of bits specified by the 'bit_' variable +uint8_t mask_value = ~((uint8_t)(1 << (8 - bit_)) - 1); - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_img, input_cv->Rank(), &result_tensor)); - *output = std::static_pointer_cast(result_tensor); - return Status::OK(); +// Convert the input tensor to a CVTensor for easier manipulation +std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + +// Check if the input CVTensor has valid data +if (!input_cv->mat().data) { + // If the input CVTensor does not have valid data, return an error status + RETURN_STATUS_UNEXPECTED("[Internal ERROR] Posterize: load image failed."); } + +// Check if the input CVTensor has a valid rank (either 2 or 3) +if (input_cv->Rank() != 3 && input_cv->Rank() != 2) { + // If the input CVTensor does not have a valid rank, return an error status with the rank information + RETURN_STATUS_UNEXPECTED("Posterize: input image is not in shape of or , but got rank: " + + std::to_string(input_cv->Rank())); +} + +// Create a vector to store the lookup table values +std::vector lut_vector; + +// Generate the lookup table by applying the mask value to each possible pixel value (0-255) +for (std::size_t i = 0; i < 256; i++) { + lut_vector.push_back(i & mask_value); +} + +// Get the OpenCV matrix representation of the input CVTensor +cv::Mat in_image = input_cv->mat(); + +// Declare a variable named "output_img" of type cv::Mat, which will hold the output image +cv::Mat output_img; + +// Check if the depth of the input image is either CV_8U (unsigned 8-bit integer) or CV_8S (signed 8-bit integer) +// If the depth is not one of these types, return an error message indicating the expected data type +CHECK_FAIL_RETURN_UNEXPECTED(in_image.depth() == CV_8U || in_image.depth() == CV_8S, + "Posterize: data type of input image should be int, " + "but got " + + input->type().ToString()); + +// Apply a look-up table transformation to the input image using the lut_vector and store the result in output_img +cv::LUT(in_image, lut_vector, output_img); + +// Declare a shared pointer named "result_tensor" of type CVTensor, which will hold the final result +std::shared_ptr result_tensor; + +// Call the CVTensor::CreateFromMat function to create a CVTensor object from the output_img and assign the result to result_tensor +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(output_img, input_cv->Rank(), &result_tensor)); + +// Cast the result_tensor to a shared pointer of Tensor and assign it to the output pointer +*output = std::static_pointer_cast(result_tensor); + +// Return a Status object indicating that the operation was successful +return Status::OK(); + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc index b3ecf297641..91b98a5ee92 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_adjust_sharpness_op.cc @@ -14,20 +14,45 @@ * limitations under the License. */ +// Include the header file "random_adjust_sharpness_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_adjust_sharpness_op.h" +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const float RandomAdjustSharpnessOp::kDefProbability = 0.5; -Status RandomAdjustSharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); + // Define the nested namespace "dataset" + namespace dataset { - if (distribution_(rnd_)) { - return SharpnessOp::Compute(input, output); - } - *output = input; - return Status::OK(); + // Define the constant float variable "kDefProbability" for the class "RandomAdjustSharpnessOp" + const float RandomAdjustSharpnessOp::kDefProbability = 0.5; + } } + +// Compute function for the RandomAdjustSharpnessOp class +// Takes in an input tensor and a pointer to an output tensor +// IO_CHECK is a macro that checks if the input and output tensors are valid +Status RandomAdjustSharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid using the IO_CHECK macro + IO_CHECK(input, output); + // ... +} + +// Check if the result of the distribution function is true +if (distribution_(rnd_)) { + + // If true, call the Compute function of the SharpnessOp class with input and output as parameters and return the result + return SharpnessOp::Compute(input, output); +} + +// If the result of the distribution function is false, assign the value of input to output +*output = input; + +// Return a Status object indicating successful program execution +return Status::OK(); + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_affine_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_affine_op.cc index 53f61d858e3..4b973011165 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_affine_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_affine_op.cc @@ -14,80 +14,165 @@ * limitations under the License. */ +// Include the header file "random_affine_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_affine_op.h" +// Include the math header for mathematical functions like sqrt, sin, etc. #include + +// Include the limits header for numeric limits like maximum and minimum values of data types #include +// Include the header file for math utilities related to image processing from the MindData library #include "minddata/dataset/kernels/image/math_utils.h" + +// Include the header file for random number generation utilities from the MindData library #include "minddata/dataset/util/random.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define the static member variable kDegreesRange of type std::vector and initialize it with {0.0, 0.0} const std::vector RandomAffineOp::kDegreesRange = {0.0, 0.0}; + +// Define the static member variable kTranslationPercentages of type std::vector and initialize it with {0.0, 0.0, 0.0, 0.0} const std::vector RandomAffineOp::kTranslationPercentages = {0.0, 0.0, 0.0, 0.0}; + +// Define the static member variable kScaleRange of type std::vector and initialize it with {1.0, 1.0} const std::vector RandomAffineOp::kScaleRange = {1.0, 1.0}; + +// Define the static member variable kShearRanges of type std::vector and initialize it with {0.0, 0.0, 0.0, 0.0} const std::vector RandomAffineOp::kShearRanges = {0.0, 0.0, 0.0, 0.0}; + +// Define the static member variable kDefInterpolation of type InterpolationMode and initialize it with InterpolationMode::kNearestNeighbour const InterpolationMode RandomAffineOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; + +// Define the static member variable kFillValue of type std::vector and initialize it with {0, 0, 0} const std::vector RandomAffineOp::kFillValue = {0, 0, 0}; +// Constructor for the RandomAffineOp class RandomAffineOp::RandomAffineOp(std::vector degrees, std::vector translate_range, std::vector scale_range, std::vector shear_ranges, InterpolationMode interpolation, std::vector fill_value) - : AffineOp(0.0), - degrees_range_(degrees), - translate_range_(translate_range), - scale_range_(scale_range), - shear_ranges_(shear_ranges) { - interpolation_ = interpolation; - fill_value_ = fill_value; - rnd_.seed(GetSeed()); - is_deterministic_ = false; + : AffineOp(0.0), // Call the constructor of the base class AffineOp with a default value of 0.0 + degrees_range_(degrees), // Initialize the degrees_range_ member variable with the provided degrees vector + translate_range_(translate_range), // Initialize the translate_range_ member variable with the provided translate_range vector + scale_range_(scale_range), // Initialize the scale_range_ member variable with the provided scale_range vector + shear_ranges_(shear_ranges) { // Initialize the shear_ranges_ member variable with the provided shear_ranges vector + + interpolation_ = interpolation; // Assign the provided interpolation value to the interpolation_ member variable + fill_value_ = fill_value; // Assign the provided fill_value vector to the fill_value_ member variable + + rnd_.seed(GetSeed()); // Seed the random number generator with a seed obtained from GetSeed() function + is_deterministic_ = false; // Set the is_deterministic_ member variable to false } -Status RandomAffineOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); +// Compute function for the RandomAffineOp class, which takes an input tensor and computes the output tensor +// The function returns a status indicating the success or failure of the computation - CHECK_FAIL_RETURN_UNEXPECTED(translate_range_.size() == 4, "RandomAffine: the translate range size is not 4."); - CHECK_FAIL_RETURN_UNEXPECTED(degrees_range_.size() == 2, "RandomAffine: the degrees range size is not 2."); - CHECK_FAIL_RETURN_UNEXPECTED(scale_range_.size() == 2, "RandomAffine: the scale range size is not 2."); - CHECK_FAIL_RETURN_UNEXPECTED(shear_ranges_.size() == 4, "RandomAffine: the shear ranges size is not 4."); +// Check if the input and output tensors are valid (non-null) +IO_CHECK(input, output); - dsize_t height = input->shape()[0]; - dsize_t width = input->shape()[1]; - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[0])) > width, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[1])) > width, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[2])) > height, - "RandomAffineOp: multiplication out of bounds."); - CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[3])) > height, - "RandomAffineOp: multiplication out of bounds."); - float_t min_dx = translate_range_[0] * width; - float_t max_dx = translate_range_[1] * width; - float_t min_dy = translate_range_[2] * height; - float_t max_dy = translate_range_[3] * height; - float_t degrees = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(degrees_range_[0], degrees_range_[1], &rnd_, °rees)); - float_t translation_x = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(min_dx, max_dx, &rnd_, &translation_x)); - float_t translation_y = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(min_dy, max_dy, &rnd_, &translation_y)); - float_t scale = 1.0; - RETURN_IF_NOT_OK(GenerateRealNumber(scale_range_[0], scale_range_[1], &rnd_, &scale)); - float_t shear_x = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[0], shear_ranges_[1], &rnd_, &shear_x)); - float_t shear_y = 0.0; - RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[2], shear_ranges_[3], &rnd_, &shear_y)); - // assign to base class variables - degrees_ = fmod(degrees, 360.0); - scale_ = scale; - translation_[0] = translation_x; - translation_[1] = translation_y; - shear_[0] = shear_x; - shear_[1] = shear_y; - return AffineOp::Compute(input, output); -} -} // namespace dataset -} // namespace mindspore +// Check if the size of the translate range is 4, if not, return an error message +CHECK_FAIL_RETURN_UNEXPECTED(translate_range_.size() == 4, "RandomAffine: the translate range size is not 4."); + +// Check if the size of the degrees range is 2, if not, return an error message +CHECK_FAIL_RETURN_UNEXPECTED(degrees_range_.size() == 2, "RandomAffine: the degrees range size is not 2."); + +// Check if the size of the scale range is 2, if not, return an error message +CHECK_FAIL_RETURN_UNEXPECTED(scale_range_.size() == 2, "RandomAffine: the scale range size is not 2."); + +// Check if the size of the shear ranges is 4, if not, return an error message +CHECK_FAIL_RETURN_UNEXPECTED(shear_ranges_.size() == 4, "RandomAffine: the shear ranges size is not 4."); + +// Get the height and width of the input shape +dsize_t height = input->shape()[0]; +dsize_t width = input->shape()[1]; + +// Check if the multiplication of the translate range and width is within the bounds of float_t +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[0])) > width, + "RandomAffineOp: multiplication out of bounds."); +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[1])) > width, + "RandomAffineOp: multiplication out of bounds."); + +// Check if the multiplication of the translate range and height is within the bounds of float_t +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[2])) > height, + "RandomAffineOp: multiplication out of bounds."); +CHECK_FAIL_RETURN_UNEXPECTED((std::numeric_limits::max() / std::abs(translate_range_[3])) > height, + "RandomAffineOp: multiplication out of bounds."); + +// Calculate the minimum and maximum values for translation in the x and y directions +float_t min_dx = translate_range_[0] * width; +float_t max_dx = translate_range_[1] * width; +float_t min_dy = translate_range_[2] * height; +float_t max_dy = translate_range_[3] * height; + +// Initialize degrees to 0.0 +float_t degrees = 0.0; + +// Generate a random real number within the specified range for degrees +RETURN_IF_NOT_OK(GenerateRealNumber(degrees_range_[0], degrees_range_[1], &rnd_, °rees)); + +// Initialize translation_x and translation_y to 0.0 +float_t translation_x = 0.0; +float_t translation_y = 0.0; + +// Generate random real numbers within the specified range for translation in the x and y directions +RETURN_IF_NOT_OK(GenerateRealNumber(min_dx, max_dx, &rnd_, &translation_x)); +RETURN_IF_NOT_OK(GenerateRealNumber(min_dy, max_dy, &rnd_, &translation_y)); +// Declare and initialize a variable `scale` of type `float_t` with a value of 1.0 +float_t scale = 1.0; + +// Call the function `GenerateRealNumber` with arguments `scale_range_[0]`, `scale_range_[1]`, `&rnd_`, and `&scale` +// The function generates a random real number within the specified range and assigns it to the variable `scale` +RETURN_IF_NOT_OK(GenerateRealNumber(scale_range_[0], scale_range_[1], &rnd_, &scale)); + +// Declare and initialize a variable `shear_x` of type `float_t` with a value of 0.0 +float_t shear_x = 0.0; + +// Call the function `GenerateRealNumber` with arguments `shear_ranges_[0]`, `shear_ranges_[1]`, `&rnd_`, and `&shear_x` +// The function generates a random real number within the specified range and assigns it to the variable `shear_x` +RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[0], shear_ranges_[1], &rnd_, &shear_x)); + +// Declare and initialize a variable `shear_y` of type `float_t` with a value of 0.0 +float_t shear_y = 0.0; + +// Call the function `GenerateRealNumber` with arguments `shear_ranges_[2]`, `shear_ranges_[3]`, `&rnd_`, and `&shear_y` +// The function generates a random real number within the specified range and assigns it to the variable `shear_y` +RETURN_IF_NOT_OK(GenerateRealNumber(shear_ranges_[2], shear_ranges_[3], &rnd_, &shear_y)); + +// Assign the value of `degrees` modulo 360.0 to the variable `degrees_` +degrees_ = fmod(degrees, 360.0); + +// Assign the value of `scale` to the variable `scale_` +scale_ = scale; + +// Assign the value of `translation_x` to the first element of the array `translation_` +translation_[0] = translation_x; + +// Assign the value of `translation_y` to the second element of the array `translation_` +translation_[1] = translation_y; + +// Assign the value of `shear_x` to the first element of the array `shear_` +shear_[0] = shear_x; + +// Assign the value of `shear_y` to the second element of the array `shear_` +shear_[1] = shear_y; + +// Call the `Compute` function of the `AffineOp` class with arguments `input` and `output` +// Return the result of the `Compute` function +return AffineOp::Compute(input, output); + +// End of the `dataset` namespace +} + +// End of the `mindspore` namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_auto_contrast_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_auto_contrast_op.cc index 9460fef0928..f2ab3b3e7a1 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_auto_contrast_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_auto_contrast_op.cc @@ -14,37 +14,60 @@ * limitations under the License. */ +// Include the header file "random_auto_contrast_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_auto_contrast_op.h" -#include "minddata/dataset/kernels/image/image_utils.h" +// Include the header file "minddata/dataset/kernels/image/image_utils.h" which contains utility functions for image processing in the MindData library. +// Define the namespace "mindspore" namespace mindspore { + +// Define the nested namespace "dataset" namespace dataset { + +// Define the constant variable "kCutOff" with a value of 0.0 const float RandomAutoContrastOp::kCutOff = 0.0; + +// Define the constant vector "kIgnore" with an empty initializer list const std::vector RandomAutoContrastOp::kIgnore = {}; + +// Define the constant variable "kDefProbability" with a value of 0.5 const float RandomAutoContrastOp::kDefProbability = 0.5; +// The Compute function of the RandomAutoContrastOp class Status RandomAutoContrastOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // Check input + IO_CHECK(input, output); // Check if input and output are valid + + // Check input shape if (input->Rank() != DEFAULT_IMAGE_RANK) { RETURN_STATUS_UNEXPECTED("RandomAutoContrast: image shape is not , got rank: " + std::to_string(input->Rank())); } + + // Check number of channels in input shape if (input->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { RETURN_STATUS_UNEXPECTED( "RandomAutoContrast: image shape is incorrect, expected num of channels is 3, " "but got:" + std::to_string(input->shape()[CHANNEL_INDEX])); } + + // Check if input type is supported CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, "RandomAutoContrast: Cannot convert from OpenCV type, unknown CV type. Currently " "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); + + // Apply AutoContrast if the random distribution condition is met if (distribution_(rnd_)) { return AutoContrast(input, output, cutoff_, ignore_); } + + // If the random distribution condition is not met, assign input to output *output = input; + + // Return OK status to indicate successful computation return Status::OK(); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_adjust_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_adjust_op.cc index aae327f7caa..5dbac3a0be9 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_adjust_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_adjust_op.cc @@ -15,14 +15,25 @@ */ #include "minddata/dataset/kernels/image/random_color_adjust_op.h" +// Include the random header, which provides facilities for generating random numbers #include +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for random number generation utilities from the MindData library #include "minddata/dataset/util/random.h" + +// Include the header file for status utilities from the MindData library #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Definition of the constructor for the RandomColorAdjustOp class RandomColorAdjustOp::RandomColorAdjustOp(float s_bright_factor, float e_bright_factor, float s_contrast_factor, float e_contrast_factor, float s_saturation_factor, float e_saturation_factor, float s_hue_factor, float e_hue_factor) @@ -34,17 +45,33 @@ RandomColorAdjustOp::RandomColorAdjustOp(float s_bright_factor, float e_bright_f saturation_factor_end_(e_saturation_factor), hue_factor_start_(s_hue_factor), hue_factor_end_(e_hue_factor) { + + // Seed the random number generator with a random seed rnd_.seed(GetSeed()); + + // Set the flag for determinism to false is_deterministic_ = false; } -Status RandomColorAdjustOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); +// End of the "dataset" namespace +} // namespace dataset - // randomly select an augmentation to apply to the input image until all the transformations run - std::vector params_vector = {"brightness", "contrast", "saturation", "hue"}; +// End of the "mindspore" namespace +} // namespace mindspore - std::shuffle(params_vector.begin(), params_vector.end(), rnd_); +// The Compute function of the RandomColorAdjustOp class takes in a shared pointer to a Tensor object as input and a pointer to a shared pointer to a Tensor object as output +// It returns a status indicating the success or failure of the computation + +// Check if the input and output pointers are valid using the IO_CHECK macro + +// Create a vector named "params_vector" to store a list of augmentation parameters +std::vector params_vector = {"brightness", "contrast", "saturation", "hue"}; + +// The vector contains the following augmentation parameters: brightness, contrast, saturation, hue +// These parameters will be randomly selected and applied to the input image until all transformations have been run + +// Shuffle the elements in the params_vector using the random number generator rnd_ +std::shuffle(params_vector.begin(), params_vector.end(), rnd_); *output = std::static_pointer_cast(input); // determine if certain augmentation needs to be executed: @@ -52,40 +79,45 @@ Status RandomColorAdjustOp::Compute(const std::shared_ptr &input, std::s // case switch if (param == "brightness") { if (CmpFloat(bright_factor_start_, bright_factor_end_) && CmpFloat(bright_factor_start_, 1.0f)) { + // If the brightness factor is not within the desired range and is equal to 1.0, skip the brightness adjustment MS_LOG(DEBUG) << "Not running brightness."; } else { - // adjust the brightness of an image + // Adjust the brightness of the image by generating a random factor within the specified range float random_factor = std::uniform_real_distribution(bright_factor_start_, bright_factor_end_)(rnd_); RETURN_IF_NOT_OK(AdjustBrightness(*output, output, random_factor)); } } else if (param == "contrast") { if (CmpFloat(contrast_factor_start_, contrast_factor_end_) && CmpFloat(contrast_factor_start_, 1.0f)) { + // If the contrast factor is not within the desired range and is equal to 1.0, skip the contrast adjustment MS_LOG(DEBUG) << "Not running contrast."; } else { + // Adjust the contrast of the image by generating a random factor within the specified range float random_factor = std::uniform_real_distribution(contrast_factor_start_, contrast_factor_end_)(rnd_); RETURN_IF_NOT_OK(AdjustContrast(*output, output, random_factor)); } } else if (param == "saturation") { - // adjust the Saturation of an image if (CmpFloat(saturation_factor_start_, saturation_factor_end_) && CmpFloat(saturation_factor_start_, 1.0f)) { + // If the saturation factor is not within the desired range and is equal to 1.0, skip the saturation adjustment MS_LOG(DEBUG) << "Not running saturation."; } else { + // Adjust the saturation of the image by generating a random factor within the specified range float random_factor = std::uniform_real_distribution(saturation_factor_start_, saturation_factor_end_)(rnd_); RETURN_IF_NOT_OK(AdjustSaturation(*output, output, random_factor)); } } else if (param == "hue") { if (CmpFloat(hue_factor_start_, hue_factor_end_) && CmpFloat(hue_factor_start_, 0.0f)) { + // If the hue factor is not within the desired range and is equal to 0.0, skip the hue adjustment MS_LOG(DEBUG) << "Not running hue."; } else { - // adjust the Hue of an image + // Adjust the hue of the image by generating a random factor within the specified range float random_factor = std::uniform_real_distribution(hue_factor_start_, hue_factor_end_)(rnd_); RETURN_IF_NOT_OK(AdjustHue(*output, output, random_factor)); } } } - // now after we do all the transformations, the last one is fine + // After all the transformations are done, return OK status return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_op.cc index f62b7d8aabc..c79b5b50aec 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_color_op.cc @@ -14,55 +14,104 @@ * limitations under the License. */ +// Include the header file for the RandomColorOp class from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/random_color_op.h" + +// Include the header file for the ImageUtils class from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the CvTensor class from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Start of the mindspore namespace namespace mindspore { namespace dataset { +// End of the dataset namespace +} // namespace dataset +} // namespace mindspore -RandomColorOp::RandomColorOp(float t_lb, float t_ub) : rnd_(GetSeed()), dist_(t_lb, t_ub), t_lb_(t_lb), t_ub_(t_ub) { - is_deterministic_ = false; +// Define the constructor for the RandomColorOp class, taking in a lower bound (t_lb) and an upper bound (t_ub) as parameters +RandomColorOp::RandomColorOp(float t_lb, float t_ub) : + + // Initialize the rnd_ member variable with a random seed obtained from the GetSeed() function + rnd_(GetSeed()), + + // Initialize the dist_ member variable with the lower bound (t_lb) and upper bound (t_ub) parameters + dist_(t_lb, t_ub), + + // Initialize the t_lb_ member variable with the lower bound (t_lb) parameter + t_lb_(t_lb), + + // Initialize the t_ub_ member variable with the upper bound (t_ub) parameter + t_ub_(t_ub) { + + // Set the is_deterministic_ member variable to false + is_deterministic_ = false; } +// The Compute function of the RandomColorOp class Status RandomColorOp::Compute(const std::shared_ptr &in, std::shared_ptr *out) { + // Check if the input and output tensors are valid IO_CHECK(in, out); + + // Check if the input tensor has the correct shape if (in->Rank() != 3 || in->shape()[2] != 3) { + // If the shape is not or the channel is not 3, return an error message RETURN_STATUS_UNEXPECTED("RandomColor: image shape is not or channel is not 3, got rank: " + std::to_string(in->Rank()) + ", and channel: " + std::to_string(in->shape()[2])); } - // 0.5 pixel precision assuming an 8 bit image + + // Define the pixel precision assuming an 8-bit image const auto eps = 0.00195; + + // Generate a random number between 0 and 1 const auto t = dist_(rnd_); + + // Check if the random number is close to 1.0 if (abs(t - 1.0) < eps) { - // Just return input? Can we do it given that input would otherwise get consumed in CVTensor constructor anyway? + // If it is, just return the input tensor as the output *out = in; return Status::OK(); } + + // Convert the input tensor to a CVTensor auto cvt_in = CVTensor::AsCVTensor(in); auto m1 = cvt_in->mat(); + + // Convert the image to grayscale cv::Mat gray; - // gray is allocated without using the allocator cv::cvtColor(m1, gray, cv::COLOR_RGB2GRAY); - // luminosity is not preserved, consider using weights. + + // Create a new 3-channel image with all channels set to the grayscale image cv::Mat temp[3] = {gray, gray, gray}; cv::Mat cv_out; cv::merge(temp, 3, cv_out); + + // Create a new CVTensor from the merged image std::shared_ptr cvt_out; RETURN_IF_NOT_OK(CVTensor::CreateFromMat(cv_out, cvt_in->Rank(), &cvt_out)); + + // Check if the random number is close to 0.0 if (abs(t - 0.0) < eps) { - // return grayscale + // If it is, return the grayscale image as the output *out = std::static_pointer_cast(cvt_out); return Status::OK(); } + try { - // return blended image. addWeighted takes care of overflow for uint8_t + // Blend the original image and the merged image using the random number as the weight cv::addWeighted(m1, t, cvt_out->mat(), 1 - t, 0, cvt_out->mat()); } catch (const cv::Exception &e) { + // If an exception occurs during blending, return an error message RETURN_STATUS_UNEXPECTED("RandomColorOp: cv::addWeighted " + std::string(e.what())); } + + // Set the blended image as the output *out = std::static_pointer_cast(cvt_out); return Status::OK(); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_op.cc index 4bea302ee34..21be5bfcc05 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_op.cc @@ -17,154 +17,266 @@ #include #include +// Include the header file for image utilities from the MindData dataset library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for random number generation utilities from the MindData dataset library #include "minddata/dataset/util/random.h" + +// Include the header file for status utilities from the MindData dataset library #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" for the code namespace mindspore { -namespace dataset { -const float RandomCropAndResizeOp::kDefScaleLb = 0.08; -const float RandomCropAndResizeOp::kDefScaleUb = 1.0; -const float RandomCropAndResizeOp::kDefAspectLb = 0.75; -const float RandomCropAndResizeOp::kDefAspectUb = 1.333333; -const InterpolationMode RandomCropAndResizeOp::kDefInterpolation = InterpolationMode::kLinear; -const int32_t RandomCropAndResizeOp::kDefMaxIter = 10; + + // Define the nested namespace "dataset" within the "mindspore" namespace + namespace dataset { + + // Define the constant float values for the default scale lower bound and upper bound + const float RandomCropAndResizeOp::kDefScaleLb = 0.08; + const float RandomCropAndResizeOp::kDefScaleUb = 1.0; + + // Define the constant float values for the default aspect ratio lower bound and upper bound + const float RandomCropAndResizeOp::kDefAspectLb = 0.75; + const float RandomCropAndResizeOp::kDefAspectUb = 1.333333; + + // Define the constant InterpolationMode value for the default interpolation mode + const InterpolationMode RandomCropAndResizeOp::kDefInterpolation = InterpolationMode::kLinear; + + // Define the constant int32_t value for the default maximum iterations + const int32_t RandomCropAndResizeOp::kDefMaxIter = 10; + + } // End of namespace dataset +} // End of namespace mindspore +// Constructor for the RandomCropAndResizeOp class RandomCropAndResizeOp::RandomCropAndResizeOp(int32_t target_height, int32_t target_width, float scale_lb, float scale_ub, float aspect_lb, float aspect_ub, InterpolationMode interpolation, int32_t max_attempts) - : target_height_(target_height), - target_width_(target_width), - rnd_scale_(scale_lb, scale_ub), - rnd_aspect_(log(aspect_lb), log(aspect_ub)), - interpolation_(interpolation), - aspect_lb_(aspect_lb), - aspect_ub_(aspect_ub), - max_iter_(max_attempts) { - rnd_.seed(GetSeed()); - is_deterministic_ = false; + : target_height_(target_height), // Initialize the target height member variable with the provided value + target_width_(target_width), // Initialize the target width member variable with the provided value + rnd_scale_(scale_lb, scale_ub), // Initialize the random scale generator with the provided lower and upper bounds + rnd_aspect_(log(aspect_lb), log(aspect_ub)), // Initialize the random aspect generator with the logarithm of the provided lower and upper bounds + interpolation_(interpolation), // Initialize the interpolation mode member variable with the provided value + aspect_lb_(aspect_lb), // Initialize the aspect lower bound member variable with the provided value + aspect_ub_(aspect_ub), // Initialize the aspect upper bound member variable with the provided value + max_iter_(max_attempts) { // Initialize the maximum number of attempts member variable with the provided value + rnd_.seed(GetSeed()); // Seed the random number generator with a random seed + is_deterministic_ = false; // Set the deterministic flag to false } -Status RandomCropAndResizeOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - if (input.size() != 1) { - for (size_t i = 0; i < input.size() - 1; i++) { - if (input[i]->Rank() != 2 && input[i]->Rank() != 3) { - std::string err_msg = "RandomCropAndResizeOp: image shape is not or , but got rank:" + - std::to_string(input[i]->Rank()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) { - RETURN_STATUS_UNEXPECTED( - "RandomCropAndResizeOp: Input images in different column of each row must have the same size."); - } +// Check if the input and output vectors are valid +IO_CHECK_VECTOR(input, output); + +// Check if the size of the input vector is not equal to 1 +if (input.size() != 1) { + // Iterate through the input vector (except the last element) + for (size_t i = 0; i < input.size() - 1; i++) { + // Check if the rank of the current input tensor is not 2 or 3 + if (input[i]->Rank() != 2 && input[i]->Rank() != 3) { + // Create an error message indicating that the image shape is not or + std::string err_msg = "RandomCropAndResizeOp: image shape is not or , but got rank:" + + std::to_string(input[i]->Rank()); + // Return an unexpected status with the error message + RETURN_STATUS_UNEXPECTED(err_msg); + } + // Check if the height and width of the current input tensor is not equal to the next input tensor + if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) { + // Return an unexpected status indicating that input images in different columns of each row must have the same size + RETURN_STATUS_UNEXPECTED( + "RandomCropAndResizeOp: Input images in different column of each row must have the same size."); } } - output->resize(input.size()); - int x = 0; - int y = 0; - int crop_height = 0; - int crop_width = 0; - for (size_t i = 0; i < input.size(); i++) { - RETURN_IF_NOT_OK(ValidateImageRank("RandomCropAndResize", static_cast(input[i]->shape().Size()))); - int h_in = static_cast(input[i]->shape()[0]); - int w_in = static_cast(input[i]->shape()[1]); - if (i == 0) { - RETURN_IF_NOT_OK(GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); - } - RETURN_IF_NOT_OK(CropAndResize(input[i], &(*output)[i], x, y, crop_height, crop_width, target_height_, - target_width_, interpolation_)); - } - return Status::OK(); } +// Resize the output vector to match the size of the input vector +output->resize(input.size()); + +// Initialize variables for crop coordinates and dimensions +int x = 0; +int y = 0; +int crop_height = 0; +int crop_width = 0; + +// Iterate through the input vector +for (size_t i = 0; i < input.size(); i++) { + // Validate the rank of the current input tensor + RETURN_IF_NOT_OK(ValidateImageRank("RandomCropAndResize", static_cast(input[i]->shape().Size()))); + + // Get the height and width of the current input tensor + int h_in = static_cast(input[i]->shape()[0]); + int w_in = static_cast(input[i]->shape()[1]); + + // If it's the first input tensor, calculate the crop box coordinates and dimensions + if (i == 0) { + RETURN_IF_NOT_OK(GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); + } + + // Crop and resize the current input tensor and store the result in the corresponding output tensor + RETURN_IF_NOT_OK(CropAndResize(input[i], &(*output)[i], x, y, crop_height, crop_width, target_height_, + target_width_, interpolation_)); +} + +// Return a status indicating successful computation +return Status::OK(); + +// This function calculates the output shape of the RandomCropAndResize operation based on the input shape. Status RandomCropAndResizeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + // Call the OutputShape function of the base class TensorOp to perform basic shape validation RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Create a TensorShape object representing the desired output shape TensorShape out = TensorShape{target_height_, target_width_}; + + // Check if the input tensor has rank 2 if (inputs[0].Rank() == 2) { + // Append the desired output shape to the outputs vector (void)outputs.emplace_back(out); } + + // Check if the input tensor has rank 3 if (inputs[0].Rank() == 3) { + // Append the desired output shape with an additional dimension representing the number of channels (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); } + + // Check if the outputs vector is not empty if (!outputs.empty()) { + // Return OK status to indicate successful output shape calculation return Status::OK(); } + + // Return an error status with a descriptive error message if the input shape is invalid return Status(StatusCode::kMDUnexpectedError, "RandomCropAndResize: invalid input shape, expected 2D or 3D input, but got input dimension is: " + std::to_string(inputs[0].Rank())); } + +// This function calculates the crop box parameters for the RandomCropAndResize operation Status RandomCropAndResizeOp::GetCropBox(int h_in, int w_in, int *x, int *y, int *crop_height, int *crop_width) { + // Check if crop_height is nullptr CHECK_FAIL_RETURN_UNEXPECTED(crop_height != nullptr, "crop_height is nullptr."); + + // Check if crop_width is nullptr CHECK_FAIL_RETURN_UNEXPECTED(crop_width != nullptr, "crop_width is nullptr."); + + // Set crop_width to the width of the input tensor *crop_width = w_in; + + // Set crop_height to the height of the input tensor *crop_height = h_in; + + // Check if the width of the input tensor is not 0 CHECK_FAIL_RETURN_UNEXPECTED(w_in != 0, "RandomCropAndResize: Width of input cannot be 0."); + + // Check if the height of the input tensor is not 0 CHECK_FAIL_RETURN_UNEXPECTED(h_in != 0, "RandomCropAndResize: Height of input cannot be 0."); + + // Check if aspect_lb_ is greater than 0 CHECK_FAIL_RETURN_UNEXPECTED( aspect_lb_ > 0, "RandomCropAndResize: 'ratio'(aspect) lower bound must be greater than 0, but got:" + std::to_string(aspect_lb_)); + + // Iterate for max_iter_ number of times for (int32_t i = 0; i < max_iter_; i++) { + // Generate a random scale value using rnd_scale_ double const sample_scale = rnd_scale_(rnd_); - // In case of non-symmetrical aspect ratios, use uniform distribution on a logarithmic sample_scale. - // Note rnd_aspect_ is already a random distribution of the input aspect ratio in logarithmic sample_scale. + + // Generate a random aspect value using rnd_aspect_ and convert it to linear scale using exp double const sample_aspect = exp(rnd_aspect_(rnd_)); + // ... (additional code not provided) + } +} + + // Check if the multiplication of the maximum value of int32_t divided by h_in is greater than w_in + // If it is not, then it means the multiplication is out of bounds and we need to check the image width and height CHECK_FAIL_RETURN_UNEXPECTED( (std::numeric_limits::max() / h_in) > w_in, "RandomCropAndResizeOp: multiplication out of bounds, check image width and image height first."); + + // Check if the multiplication of the maximum value of int32_t divided by h_in divided by w_in is greater than sample_scale + // If it is not, then it means the multiplication is out of bounds and we need to check the image width, height, and sample scale CHECK_FAIL_RETURN_UNEXPECTED( static_cast((std::numeric_limits::max() / h_in) / w_in) > sample_scale, "RandomCropAndResizeOp: multiplication out of bounds, check image width, image height and sample scale first."); + + // Check if the multiplication of the maximum value of int32_t divided by h_in divided by w_in divided by sample_scale is greater than sample_aspect + // If it is not, then it means the multiplication is out of bounds and we need to check the image width, height, sample scale, and sample aspect CHECK_FAIL_RETURN_UNEXPECTED( (static_cast((std::numeric_limits::max() / h_in) / w_in) / sample_scale) > sample_aspect, - "RandomCropAndResizeOp: multiplication out of bounds, check image width, image " - "height, sample scale and sample aspect first."); + "RandomCropAndResizeOp: multiplication out of bounds, check image width, image height, sample scale and sample aspect first."); + + // Calculate the crop width by rounding the square root of the multiplication of h_in, w_in, sample_scale, and sample_aspect *crop_width = static_cast(std::round(std::sqrt(h_in * w_in * sample_scale * sample_aspect))); + + // Calculate the crop height by dividing the crop width by the sample aspect ratio and rounding the result *crop_height = static_cast(std::round(*crop_width / sample_aspect)); - // forbidden crop_width or crop_height is zero + // Check if crop_width is less than or equal to zero if (*crop_width <= 0) { + // If so, set crop_width to 1 *crop_width = 1; } + + // Check if crop_height is less than or equal to zero if (*crop_height <= 0) { + // If so, set crop_height to 1 *crop_height = 1; } if (*crop_width <= w_in && *crop_height <= h_in) { - std::uniform_int_distribution<> rd_x(0, w_in - *crop_width); - std::uniform_int_distribution<> rd_y(0, h_in - *crop_height); - *x = rd_x(rnd_); - *y = rd_y(rnd_); - return Status::OK(); + // If the desired crop width and height are smaller than or equal to the input width and height, + // generate random x and y coordinates within the valid range + std::uniform_int_distribution<> rd_x(0, w_in - *crop_width); + std::uniform_int_distribution<> rd_y(0, h_in - *crop_height); + *x = rd_x(rnd_); + *y = rd_y(rnd_); + return Status::OK(); } - } - double const img_aspect = static_cast(w_in) / h_in; - if (img_aspect < aspect_lb_) { +} + +// Calculate the aspect ratio of the image +double const img_aspect = static_cast(w_in) / h_in; + +// Check if the image aspect ratio is less than the lower bound aspect ratio +if (img_aspect < aspect_lb_) { + // If so, set the crop width to the input width and calculate the crop height based on the lower bound aspect ratio *crop_width = w_in; *crop_height = static_cast(std::round(*crop_width / static_cast(aspect_lb_))); - } else { +} else { + // If the image aspect ratio is not less than the lower bound aspect ratio, + // check if it is greater than the upper bound aspect ratio if (img_aspect > aspect_ub_) { - *crop_height = h_in; - *crop_width = static_cast(std::round(*crop_height * static_cast(aspect_ub_))); + // If so, set the crop height to the input height and calculate the crop width based on the upper bound aspect ratio + *crop_height = h_in; + *crop_width = static_cast(std::round(*crop_height * static_cast(aspect_ub_))); } else { - *crop_width = w_in; - *crop_height = h_in; + // If the image aspect ratio is within the valid range, set the crop width and height to the input width and height + *crop_width = w_in; + *crop_height = h_in; } - } - constexpr float crop_ratio = 2.0; - // forbidden crop_width or crop_height is zero - if (*crop_width <= 0) { - *crop_width = 1; - } - if (*crop_height <= 0) { - *crop_height = 1; - } - - *x = static_cast(std::round((w_in - *crop_width) / crop_ratio)); - *y = static_cast(std::round((h_in - *crop_height) / crop_ratio)); - return Status::OK(); } -} // namespace dataset -} // namespace mindspore + +// Set a constant crop ratio +constexpr float crop_ratio = 2.0; + +// Check if the crop width is zero, and if so, set it to 1 +if (*crop_width <= 0) { + *crop_width = 1; +} + +// Check if the crop height is zero, and if so, set it to 1 +if (*crop_height <= 0) { + *crop_height = 1; +} + + *x = static_cast(std::round((w_in - *crop_width) / crop_ratio)); // Calculate the value of x by subtracting crop_width from w_in, dividing the result by crop_ratio, and rounding it to the nearest integer. Store the result in the memory location pointed to by x. + *y = static_cast(std::round((h_in - *crop_height) / crop_ratio)); // Calculate the value of y by subtracting crop_height from h_in, dividing the result by crop_ratio, and rounding it to the nearest integer. Store the result in the memory location pointed to by y. + return Status::OK(); // Return a Status object indicating that the function execution was successful. +} +} // namespace dataset // End of the dataset namespace +} // namespace mindspore // End of the mindspore namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc index 304297fc4fd..274d0d1b8c4 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.cc @@ -14,46 +14,93 @@ * limitations under the License. */ +// Include the header file "random_crop_and_resize_with_bbox_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_crop_and_resize_with_bbox_op.h" +// Include the utility header, which provides various utility components and functions #include +// Include the header file for the bounding box operations in the MindData dataset kernels #include "minddata/dataset/kernels/image/bounding_box.h" + +// Include the header file for the image utility functions in the MindData dataset kernels #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility functions in the MindData dataset #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -Status RandomCropAndResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - RETURN_IF_NOT_OK(ValidateImageRank("RandomCropAndResizeWithBBox", static_cast(input[0]->shape().Size()))); + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { - const int output_count = 2; - output->resize(output_count); - (*output)[1] = std::move(input[1]); // move boxes over to output + // Code specific to the "dataset" namespace can be written here - size_t bboxCount = input[1]->shape()[0]; // number of rows in bbox tensor - int h_in = input[0]->shape()[0]; - int w_in = input[0]->shape()[1]; - int x = 0; - int y = 0; - int crop_height = 0; - int crop_width = 0; + } // End of the "dataset" namespace +} // End of the "mindspore" namespace + +// Check if the input and output are vectors +IO_CHECK_VECTOR(input, output); + +// Validate the bounding boxes in the input +RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + +// Validate the rank of the input image tensor +RETURN_IF_NOT_OK(ValidateImageRank("RandomCropAndResizeWithBBox", static_cast(input[0]->shape().Size()))); + +// Define a constant integer variable named "output_count" and assign it the value 2 +const int output_count = 2; + +// Resize the "output" vector to have a size of "output_count" +output->resize(output_count); + +// Move the value at index 1 of the "input" vector to index 1 of the "output" vector +(*output)[1] = std::move(input[1]); // move boxes over to output + +// Get the number of rows in the bbox tensor and store it in the variable bboxCount +size_t bboxCount = input[1]->shape()[0]; + +// Get the height and width of the input tensor and store them in the variables h_in and w_in respectively +int h_in = input[0]->shape()[0]; +int w_in = input[0]->shape()[1]; + +// Initialize the variables x, y, crop_height, and crop_width to 0 +int x = 0; +int y = 0; +int crop_height = 0; +int crop_width = 0; + + // Call the GetCropBox function of the RandomCropAndResizeOp class to obtain the crop box coordinates and dimensions + // Pass the input height (h_in), input width (w_in), and pointers to variables x, y, crop_height, and crop_width + // The function will modify the values of x, y, crop_height, and crop_width based on the input dimensions + // If the function returns an error code, immediately return that error code RETURN_IF_NOT_OK(RandomCropAndResizeOp::GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); - int maxX = x + crop_width; // max dims of selected CropBox on image - int maxY = y + crop_height; +// Calculate the maximum x-coordinate of the selected CropBox on the image +int maxX = x + crop_width; - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &bboxCount, x, y, maxX, maxY)); // IMAGE_UTIL - RETURN_IF_NOT_OK(CropAndResize(input[0], &(*output)[0], x, y, crop_height, crop_width, target_height_, target_width_, - interpolation_)); +// Calculate the maximum y-coordinate of the selected CropBox on the image +int maxY = y + crop_height; - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, target_width_, target_height_, +// Call the UpdateBBoxesForCrop function from the BoundingBox class to update the bounding boxes for the cropped image +// Pass the address of the second element of the output vector, the address of the bboxCount variable, and the x, y, maxX, maxY values as arguments +RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &bboxCount, x, y, maxX, maxY)); // IMAGE_UTIL + +// Call the CropAndResize function to crop and resize the input image +// Pass the first element of the input vector, the address of the first element of the output vector, x, y, crop_height, crop_width, target_height_, target_width_, and interpolation_ as arguments +RETURN_IF_NOT_OK(CropAndResize(input[0], &(*output)[0], x, y, crop_height, crop_width, target_height_, target_width_, interpolation_)); + +// Check if the return value of the function call to UpdateBBoxesForResize is not OK, and return immediately if it is not OK +RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, target_width_, target_height_, crop_width, crop_height)); - return Status::OK(); -} + +// Return a Status object indicating that the function execution was successful +return Status::OK(); + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc index 1acdd188d64..df6b8fbf6c9 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_decode_resize_op.cc @@ -19,45 +19,93 @@ #include "minddata/dataset/core/config_manager.h" #include "minddata/dataset/kernels/image/decode_op.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Implementation of the constructor for the RandomCropDecodeResizeOp class RandomCropDecodeResizeOp::RandomCropDecodeResizeOp(int32_t target_height, int32_t target_width, float scale_lb, float scale_ub, float aspect_lb, float aspect_ub, InterpolationMode interpolation, int32_t max_attempts) + // Call the constructor of the base class RandomCropAndResizeOp with the provided arguments : RandomCropAndResizeOp(target_height, target_width, scale_lb, scale_ub, aspect_lb, aspect_ub, interpolation, max_attempts) {} +// End of the "dataset" namespace +} // namespace dataset + +// End of the "mindspore" namespace +} // namespace mindspore + +// The Compute function of the RandomCropDecodeResizeOp class Status RandomCropDecodeResizeOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Get the number of elements in the input vector const auto output_count = input.size(); + + // Resize the output vector to match the size of the input vector output->resize(output_count); + + // Initialize variables for crop coordinates and dimensions int x = 0; int y = 0; int crop_height = 0; int crop_width = 0; + + // Create a TensorRow to store the decoded images TensorRow decoded; decoded.resize(output_count); + + // Iterate over each element in the input vector for (size_t i = 0; i < input.size(); i++) { + + // Check if the input image is empty (nullptr) if (input[i] == nullptr) { RETURN_STATUS_UNEXPECTED("RandomCropDecodeResize: input image is empty since got nullptr."); } + + // Check if the input image is a non-empty JPEG image if (!IsNonEmptyJPEG(input[i])) { + + // Create a DecodeOp object and compute the decoding operation on the input image DecodeOp op(true); RETURN_IF_NOT_OK(op.Compute(input[i], &decoded[i])); + + // Compute the random crop and resize operation on the decoded image RETURN_IF_NOT_OK(RandomCropAndResizeOp::Compute(decoded, output)); + } else { + + // Get the width and height of the JPEG image int h_in = 0; int w_in = 0; RETURN_IF_NOT_OK(GetJpegImageInfo(input[i], &w_in, &h_in)); + + // If this is the first image, compute the crop box coordinates and dimensions if (i == 0) { RETURN_IF_NOT_OK(GetCropBox(h_in, w_in, &x, &y, &crop_height, &crop_width)); } + + // Create a shared pointer to store the decoded tensor std::shared_ptr decoded_tensor = nullptr; + + // Crop and decode the JPEG image using the specified crop coordinates and dimensions RETURN_IF_NOT_OK(JpegCropAndDecode(input[i], &decoded_tensor, x, y, crop_width, crop_height)); + + // Resize the decoded tensor to the target height and width using the specified interpolation method RETURN_IF_NOT_OK(Resize(decoded_tensor, &(*output)[i], target_height_, target_width_, 0.0, 0.0, interpolation_)); } } + + // Return OK status to indicate successful computation return Status::OK(); } + +// Close the namespace blocks for dataset and mindspore } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_op.cc index 046ac808a77..d94070a7285 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_op.cc @@ -20,61 +20,93 @@ #include "minddata/dataset/util/random.h" #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" for the code namespace mindspore { -namespace dataset { -const int32_t RandomCropOp::kDefPadTop = 0; -const int32_t RandomCropOp::kDefPadBottom = 0; -const int32_t RandomCropOp::kDefPadLeft = 0; -const int32_t RandomCropOp::kDefPadRight = 0; -const BorderType RandomCropOp::kDefBorderType = BorderType::kConstant; -const bool RandomCropOp::kDefPadIfNeeded = false; -const uint8_t RandomCropOp::kDefFillR = 0; -const uint8_t RandomCropOp::kDefFillG = 0; -const uint8_t RandomCropOp::kDefFillB = 0; + // Define the nested namespace "dataset" for the code + namespace dataset { + // Define the constant integer variables for the default padding values of the RandomCropOp class + const int32_t RandomCropOp::kDefPadTop = 0; + const int32_t RandomCropOp::kDefPadBottom = 0; + const int32_t RandomCropOp::kDefPadLeft = 0; + const int32_t RandomCropOp::kDefPadRight = 0; + // Define the constant BorderType variable for the default border type of the RandomCropOp class + const BorderType RandomCropOp::kDefBorderType = BorderType::kConstant; + + // Define the constant boolean variable for the default padIfNeeded value of the RandomCropOp class + const bool RandomCropOp::kDefPadIfNeeded = false; + + // Define the constant unsigned integer variables for the default fill values of the RandomCropOp class + const uint8_t RandomCropOp::kDefFillR = 0; + const uint8_t RandomCropOp::kDefFillG = 0; + const uint8_t RandomCropOp::kDefFillB = 0; + } +} + +// Constructor for the RandomCropOp class RandomCropOp::RandomCropOp(int32_t crop_height, int32_t crop_width, int32_t pad_top, int32_t pad_bottom, int32_t pad_left, int32_t pad_right, bool pad_if_needed, BorderType padding_mode, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : crop_height_(crop_height), - crop_width_(crop_width), - pad_top_(pad_top), - pad_bottom_(pad_bottom), - pad_left_(pad_left), - pad_right_(pad_right), - pad_if_needed_(pad_if_needed), - border_type_(padding_mode), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) { - rnd_.seed(GetSeed()); - is_deterministic_ = false; + : crop_height_(crop_height), // Initialize the crop height member variable with the provided value + crop_width_(crop_width), // Initialize the crop width member variable with the provided value + pad_top_(pad_top), // Initialize the pad top member variable with the provided value + pad_bottom_(pad_bottom), // Initialize the pad bottom member variable with the provided value + pad_left_(pad_left), // Initialize the pad left member variable with the provided value + pad_right_(pad_right), // Initialize the pad right member variable with the provided value + pad_if_needed_(pad_if_needed), // Initialize the pad if needed member variable with the provided value + border_type_(padding_mode), // Initialize the border type member variable with the provided value + fill_r_(fill_r), // Initialize the fill red member variable with the provided value + fill_g_(fill_g), // Initialize the fill green member variable with the provided value + fill_b_(fill_b) { // Initialize the fill blue member variable with the provided value + + rnd_.seed(GetSeed()); // Seed the random number generator with a random seed + is_deterministic_ = false; // Set the deterministic flag to false } -Status RandomCropOp::ImagePadding(const std::shared_ptr &input, std::shared_ptr *pad_image, - int32_t *t_pad_top, int32_t *t_pad_bottom, int32_t *t_pad_left, int32_t *t_pad_right, - int32_t *padded_image_w, int32_t *padded_image_h, bool *crop_further) { +// Set the value of t_pad_top to the value of pad_top_ *t_pad_top = pad_top_; + + // Set the value of t_pad_bottom to the value of pad_bottom_ *t_pad_bottom = pad_bottom_; + + // Set the value of t_pad_left to the value of pad_left_ *t_pad_left = pad_left_; + + // Set the value of t_pad_right to the value of pad_right_ *t_pad_right = pad_right_; - constexpr int64_t max_ratio = 3; - CHECK_FAIL_RETURN_UNEXPECTED( - pad_top_ < input->shape()[0] * max_ratio && pad_bottom_ < input->shape()[0] * max_ratio && - pad_left_ < input->shape()[1] * max_ratio && pad_right_ < input->shape()[1] * max_ratio, - "Pad: padding size is three times bigger than the image size, padding top: " + std::to_string(pad_top_) + - ", padding bottom: " + std::to_string(pad_bottom_) + ", padding pad_left_: " + std::to_string(pad_left_) + - ", padding padding right:" + std::to_string(pad_right_) + ", image shape: " + std::to_string(input->shape()[0]) + - ", " + std::to_string(input->shape()[1])); +// Define a constant variable `max_ratio` with a value of 3 +constexpr int64_t max_ratio = 3; + +// Check if the padding values are within the allowed range, which is three times the size of the input image +// If any of the padding values exceed the allowed range, return an error message + +CHECK_FAIL_RETURN_UNEXPECTED( + pad_top_ < input->shape()[0] * max_ratio && pad_bottom_ < input->shape()[0] * max_ratio && + pad_left_ < input->shape()[1] * max_ratio && pad_right_ < input->shape()[1] * max_ratio, + "Pad: padding size is three times bigger than the image size, padding top: " + std::to_string(pad_top_) + + ", padding bottom: " + std::to_string(pad_bottom_) + ", padding pad_left_: " + std::to_string(pad_left_) + + ", padding padding right:" + std::to_string(pad_right_) + ", image shape: " + std::to_string(input->shape()[0]) + + ", " + std::to_string(input->shape()[1])); + +// The error message includes the values of the padding variables (pad_top_, pad_bottom_, pad_left_, pad_right_) +// as well as the shape of the input image (input->shape()[0], input->shape()[1]) + + // Call the Pad function with the provided parameters and check if it returns OK. If not, return immediately. RETURN_IF_NOT_OK( Pad(input, pad_image, pad_top_, pad_bottom_, pad_left_, pad_right_, border_type_, fill_r_, fill_g_, fill_b_)); + + // Check if the size of the image after padding is at least 2. If not, return an error message. CHECK_FAIL_RETURN_UNEXPECTED( (*pad_image)->shape().Size() >= 2, "RandomCrop: invalid shape of image after pad, got rank: " + std::to_string((*pad_image)->shape().Size())); - *padded_image_h = (*pad_image)->shape()[0]; - *padded_image_w = (*pad_image)->shape()[1]; +// Dereference the pointer to get the shape of the padded image and assign the first dimension to padded_image_h +*padded_image_h = (*pad_image)->shape()[0]; + +// Dereference the pointer to get the shape of the padded image and assign the second dimension to padded_image_w +*padded_image_w = (*pad_image)->shape()[1]; if (*padded_image_h == crop_height_ && *padded_image_w == crop_width_) { *crop_further = false; // no need for further crop @@ -82,103 +114,174 @@ Status RandomCropOp::ImagePadding(const std::shared_ptr &input, std::sha } else if (pad_if_needed_) { // check the dimensions of the image for padding, if we do need padding, then we change the pad values if (*padded_image_h < crop_height_) { + // Call the Pad function to pad the image with the required dimensions RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, crop_height_ - *padded_image_h, crop_height_ - *padded_image_h, 0, 0, border_type_, fill_r_, fill_g_, fill_b_)); - - // update pad total above/below - t_pad_top += ((ptrdiff_t)crop_height_ - *padded_image_h); - t_pad_bottom += ((ptrdiff_t)crop_height_ - *padded_image_h); + // Continue with the execution if the padding is successful + // Otherwise, return the error status + // Note: RETURN_IF_NOT_OK is a macro that checks the status and returns if it is not OK + // It is used here to handle the error condition and return the error status immediately + // without executing the remaining code in the function + // The Pad function is expected to return a Status object indicating the success or failure of the operation + // The arguments passed to the Pad function specify the amount of padding required in each dimension, + // the border type, and the fill color for the padded pixels + // The pad_image is passed as both the input and output image, indicating that it will be modified in-place } - if (*padded_image_w < crop_width_) { - RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, 0, 0, crop_width_ - *padded_image_w, crop_width_ - *padded_image_w, - border_type_, fill_r_, fill_g_, fill_b_)); - // update pad total left/right - t_pad_left += ((ptrdiff_t)crop_width_ - *padded_image_w); - t_pad_right += ((ptrdiff_t)crop_width_ - *padded_image_w); - } - *padded_image_h = (*pad_image)->shape()[0]; - *padded_image_w = (*pad_image)->shape()[1]; - } + // If the width of the padded image is smaller than the desired crop width + if (*padded_image_w < crop_width_) { + // Pad the image on the left and right sides to match the crop width + RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, 0, 0, crop_width_ - *padded_image_w, crop_width_ - *padded_image_w, + border_type_, fill_r_, fill_g_, fill_b_)); + // Update the total padding on the left and right sides + t_pad_left += ((ptrdiff_t)crop_width_ - *padded_image_w); + t_pad_right += ((ptrdiff_t)crop_width_ - *padded_image_w); + } + // Update the height and width of the padded image + *padded_image_h = (*pad_image)->shape()[0]; + *padded_image_w = (*pad_image)->shape()[1]; + } + + // Check if crop height or crop width is zero if (crop_height_ == 0 || crop_width_ == 0) { + // Return an error status with a message indicating that crop size cannot be zero return Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__, "RandomCrop: invalid crop size, crop width or crop height is not allowed to be zero."); } + + // Check if crop size is bigger than the image dimensions if (*padded_image_h < crop_height_ || *padded_image_w < crop_width_ || crop_height_ == 0 || crop_width_ == 0) { + // Return an error status with a message indicating the invalid crop size return Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__, "RandomCrop: invalid crop size, crop size is bigger than the image dimensions, got crop height: " + std::to_string(crop_height_) + ", crop width: " + std::to_string(crop_width_)); } - return Status::OK(); -} + // Return a success status + return Status::OK(); + +// This function generates random x and y coordinates for cropping an image void RandomCropOp::GenRandomXY(int *x, int *y, const int32_t &padded_image_w, const int32_t &padded_image_h) { - // GenCropPoints for cropping + + // Generate a random x coordinate within the range [0, padded_image_w - crop_width_] *x = std::uniform_int_distribution(0, padded_image_w - crop_width_)(rnd_); + + // Generate a random y coordinate within the range [0, padded_image_h - crop_height_] *y = std::uniform_int_distribution(0, padded_image_h - crop_height_)(rnd_); } -Status RandomCropOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - if (input.size() > 1) { - for (size_t i = 0; i < input.size() - 1; i++) { - if (input[i]->Rank() != 2 && input[i]->Rank() != 3) { - std::string err_msg = - "RandomCropOp: image shape is not or , but got rank:" + std::to_string(input[i]->Rank()); - RETURN_STATUS_UNEXPECTED(err_msg); - } - if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) { - RETURN_STATUS_UNEXPECTED( - "RandomCropOp: Input images in different column must have the same shape, check the output shape in " - "specified 'input_columns' before call this operation."); - } - } - } - int x = 0; - int y = 0; - const auto output_count = input.size(); - output->resize(output_count); - for (size_t i = 0; i < input.size(); i++) { - RETURN_IF_NOT_OK(ValidateImageRank("RandomCrop", static_cast(input[i]->shape().Size()))); - std::shared_ptr pad_image = nullptr; - int32_t t_pad_top = 0; - int32_t t_pad_bottom = 0; - int32_t t_pad_left = 0; - int32_t t_pad_right = 0; - int32_t padded_image_w = 0; - int32_t padded_image_h = 0; - bool crop_further = true; // whether image needs further cropping based on new size & requirements +// Check if the input and output vectors are valid +IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK( // error code sent back directly - ImagePadding(input[i], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, &padded_image_w, - &padded_image_h, &crop_further)); - if (!crop_further) { - (*output)[i] = pad_image; - continue; +// If there is more than one input tensor +if (input.size() > 1) { + // Iterate through each input tensor except the last one + for (size_t i = 0; i < input.size() - 1; i++) { + // Check if the rank of the input tensor is not 2 or 3 + if (input[i]->Rank() != 2 && input[i]->Rank() != 3) { + // Create an error message indicating the incorrect rank of the input tensor + std::string err_msg = "RandomCropOp: image shape is not or , but got rank:" + std::to_string(input[i]->Rank()); + // Return an unexpected status with the error message + RETURN_STATUS_UNEXPECTED(err_msg); } - if (i == 0) { - GenRandomXY(&x, &y, padded_image_w, padded_image_h); + // Check if the shape of the current input tensor is different from the shape of the next input tensor + if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) { + // Return an unexpected status indicating that input images in different columns must have the same shape + RETURN_STATUS_UNEXPECTED("RandomCropOp: Input images in different column must have the same shape, check the output shape in specified 'input_columns' before call this operation."); } - RETURN_IF_NOT_OK(Crop(pad_image, &(*output)[i], x, y, crop_width_, crop_height_)); } - return Status::OK(); } +// Initialize variables for x and y coordinates +int x = 0; +int y = 0; + +// Get the number of output tensors +const auto output_count = input.size(); + +// Resize the output vector to match the number of output tensors +output->resize(output_count); + +// Iterate through each input tensor +for (size_t i = 0; i < input.size(); i++) { + // Validate the rank of the input tensor + RETURN_IF_NOT_OK(ValidateImageRank("RandomCrop", static_cast(input[i]->shape().Size()))); + + // Create a shared pointer for the padded image + std::shared_ptr pad_image = nullptr; + + // Initialize variables for padding + int32_t t_pad_top = 0; + int32_t t_pad_bottom = 0; + int32_t t_pad_left = 0; + int32_t t_pad_right = 0; + int32_t padded_image_w = 0; + int32_t padded_image_h = 0; + + // Initialize a boolean variable to determine if further cropping is needed + bool crop_further = true; + // ... +} + +RETURN_IF_NOT_OK( // error code sent back directly + ImagePadding(input[i], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, &padded_image_w, + &padded_image_h, &crop_further)); + +// If crop_further is false, assign pad_image to the corresponding element in the output vector and continue to the next iteration +if (!crop_further) { + (*output)[i] = pad_image; + continue; +} + +// If i is 0, generate random x and y coordinates within the bounds of the padded image +if (i == 0) { + GenRandomXY(&x, &y, padded_image_w, padded_image_h); +} + +// Crop the pad_image using the generated x and y coordinates, crop_width_, and crop_height_, and assign the result to the corresponding element in the output vector +RETURN_IF_NOT_OK(Crop(pad_image, &(*output)[i], x, y, crop_width_, crop_height_)); +} + +// Return a Status object indicating successful program termination +return Status::OK(); + +// Define the function OutputShape for the RandomCropOp class Status RandomCropOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Create a TensorShape object with the specified crop height and width TensorShape out = TensorShape{crop_height_, crop_width_}; + + // Check if the rank of the input tensor is 2 if (inputs[0].Rank() == 2) { + // Append the specified crop height and width to the output shape (void)outputs.emplace_back(out); - } else if (inputs[0].Rank() == 3) { + } + // Check if the rank of the input tensor is 3 + else if (inputs[0].Rank() == 3) { + // Append the specified crop height, width, and the third dimension of the input tensor to the output shape (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); } + + // Check if the outputs vector is not empty if (!outputs.empty()) { + // Return OK status to indicate successful output shape calculation return Status::OK(); } + + // Return an error status with a message indicating the invalid input shape return Status(StatusCode::kMDUnexpectedError, "RandomCrop: invalid input shape, expected 2D or 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } + +// Close the namespace for the dataset } // namespace dataset -} // namespace mindspore + +// Close the namespace for the mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc index df6375ee5ff..4591d9883cf 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_crop_with_bbox_op.cc @@ -14,55 +14,101 @@ * limitations under the License. */ +// Include the header file "random_crop_with_bbox_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_crop_with_bbox_op.h" +// Include the utility header, which provides various utility functions and classes #include +// Include the header file for the bounding box operations in the MindData dataset kernels #include "minddata/dataset/kernels/image/bounding_box.h" + +// Include the header file for the image utility functions in the MindData dataset kernels #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility functions in the MindData dataset #include "minddata/dataset/util/status.h" +// Start of the namespace "mindspore" namespace mindspore { -namespace dataset { -Status RandomCropWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); - RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + + // Start of the namespace "dataset" + namespace dataset { + + // Implementation of the Compute function of the RandomCropWithBBoxOp class + Status RandomCropWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output are vectors + IO_CHECK_VECTOR(input, output); + + // Validate the bounding boxes in the input + RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + + // Rest of the function implementation goes here + + } // End of the Compute function + + } // End of the namespace "dataset" + +} // End of the namespace "mindspore" - std::shared_ptr pad_image = nullptr; - int32_t t_pad_top = 0; - int32_t t_pad_bottom = 0; - int32_t t_pad_left = 0; - int32_t t_pad_right = 0; - size_t boxCount = input[1]->shape()[0]; // number of rows +// Declare a shared pointer named "pad_image" of type "Tensor" and initialize it to nullptr +std::shared_ptr pad_image = nullptr; - int32_t padded_image_h = 0; - int32_t padded_image_w = 0; - const int output_count = 2; - output->resize(output_count); - (*output)[1] = std::move(input[1]); // since some boxes may be removed +// Declare four integer variables named "t_pad_top", "t_pad_bottom", "t_pad_left", and "t_pad_right" and initialize them to 0 +int32_t t_pad_top = 0; +int32_t t_pad_bottom = 0; +int32_t t_pad_left = 0; +int32_t t_pad_right = 0; - bool crop_further = true; // Whether further cropping will be required or not, true unless required size matches - RETURN_IF_NOT_OK( // Error passed back to caller - RandomCropOp::ImagePadding(input[0], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, +// Declare a size_t variable named "boxCount" and assign it the value of the number of rows in the shape of the "input[1]" tensor +size_t boxCount = input[1]->shape()[0]; + +// Declare and initialize variables for the height and width of the padded image +int32_t padded_image_h = 0; +int32_t padded_image_w = 0; + +// Declare a constant variable for the number of outputs +const int output_count = 2; + +// Resize the output vector to have a size of output_count +output->resize(output_count); + +// Move the second element of the input vector to the second element of the output vector +(*output)[1] = std::move(input[1]); // since some boxes may be removed + +// A boolean variable to determine whether further cropping will be required or not +bool crop_further = true; + +// Call the ImagePadding function from the RandomCropOp class to calculate the padding values and determine if further cropping is needed +// The function takes the input image and several output parameters to store the padded image, padding values, and dimensions of the padded image +// The function also updates the crop_further variable based on whether the required size matches or not +RETURN_IF_NOT_OK(RandomCropOp::ImagePadding(input[0], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, &padded_image_w, &padded_image_h, &crop_further)); - // update bounding boxes with new values based on relevant image padding + // If there is padding on the left or top of the image, update the bounding boxes + // by padding them with the corresponding values if (t_pad_left || t_pad_top) { + // Call the PadBBoxes function to update the bounding boxes RETURN_IF_NOT_OK(BoundingBox::PadBBoxes(&(*output)[1], boxCount, t_pad_top, t_pad_left)); } + + // If no further cropping is required if (!crop_further) { - // no further cropping required + // Set the first element of the output vector to the padded image (*output)[0] = pad_image; + // Move the second element of the input vector to the second element of the output vector (*output)[1] = std::move(input[1]); + // Return OK status to indicate successful execution return Status::OK(); } - int x, y; - RandomCropOp::GenRandomXY(&x, &y, padded_image_w, padded_image_h); - int maxX = x + RandomCropOp::crop_width_; // max dims of selected CropBox on image - int maxY = y + RandomCropOp::crop_height_; - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &boxCount, x, y, maxX, maxY)); - return Crop(pad_image, &(*output)[0], x, y, RandomCropOp::crop_width_, RandomCropOp::crop_height_); + int x, y; // Declare variables x and y to store the random coordinates + RandomCropOp::GenRandomXY(&x, &y, padded_image_w, padded_image_h); // Generate random x and y coordinates within the padded image dimensions + int maxX = x + RandomCropOp::crop_width_; // Calculate the maximum x coordinate of the selected CropBox on the image + int maxY = y + RandomCropOp::crop_height_; // Calculate the maximum y coordinate of the selected CropBox on the image + RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForCrop(&(*output)[1], &boxCount, x, y, maxX, maxY)); // Update the bounding boxes for the crop operation + return Crop(pad_image, &(*output)[0], x, y, RandomCropOp::crop_width_, RandomCropOp::crop_height_); // Perform the crop operation on the padded image and store the result in the output variable } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_equalize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_equalize_op.cc index 69b4ab78941..4447015c510 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_equalize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_equalize_op.cc @@ -15,20 +15,42 @@ */ #include "minddata/dataset/kernels/image/random_equalize_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// The code is defining a namespace called "mindspore" which contains another namespace called "dataset" namespace mindspore { namespace dataset { + +// The code is defining a constant float variable called "kDefProbability" and initializing it with the value 0.5 const float RandomEqualizeOp::kDefProbability = 0.5; +// Implementation of the Compute function for the RandomEqualizeOp class + +// Include the necessary headers + +// Define the Compute function Status RandomEqualizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Generate a random number using the distribution and random number generator if (distribution_(rnd_)) { + + // If the random number satisfies the condition, call the Equalize function return Equalize(input, output); } + + // If the random number does not satisfy the condition, assign the input tensor to the output tensor *output = input; + + // Return OK status to indicate successful computation return Status::OK(); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_op.cc index d92e54a09ef..0f4a814472b 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_op.cc @@ -15,25 +15,63 @@ */ #include "minddata/dataset/kernels/image/random_horizontal_flip_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" for encapsulating related code +namespace mindspore { + +// Define the nested namespace "dataset" for encapsulating dataset-related code +namespace dataset { + +// Define the constant float variable "kDefProbability" with a default value of 0.5 +const float RandomHorizontalFlipOp::kDefProbability = 0.5; + +// Define the implementation of the Compute function for the RandomHorizontalFlipOp class + +// Include the necessary headers for the required types and functions +#include +#include + namespace mindspore { namespace dataset { -const float RandomHorizontalFlipOp::kDefProbability = 0.5; +// Compute function implementation Status RandomHorizontalFlipOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Get the size of the input vector const auto output_count = input.size(); + + // Resize the output vector to match the size of the input vector output->resize(output_count); + + // Check if the random number generated by the distribution is within the desired range if (distribution_(rnd_)) { + + // Iterate over each element in the input vector for (size_t i = 0; i < input.size(); i++) { + + // Call the HorizontalFlip function to perform the horizontal flip operation on the input tensor RETURN_IF_NOT_OK(HorizontalFlip(input[i], &(*output)[i])); } + + // Return OK status to indicate successful operation return Status::OK(); } + + // If the random number generated by the distribution is not within the desired range, + // simply assign the input vector to the output vector *output = input; + + // Return OK status to indicate successful operation return Status::OK(); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc index 16164941d3c..169d9623508 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.cc @@ -14,46 +14,85 @@ * limitations under the License. */ -#include "minddata/dataset/kernels/image/random_horizontal_flip_with_bbox_op.h" +// Include the header file "random_horizontal_flip_with_bbox_op.h" from the "minddata/dataset/kernels/image" directory. +// Include the utility header, which provides various utility components and functions #include +// Include the header file for the cv_tensor module from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the bounding_box module from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/bounding_box.h" + +// Include the header file for the image_utils module from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status module from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const float RandomHorizontalFlipWithBBoxOp::kDefProbability = 0.5; + // Define the nested namespace "dataset" + namespace dataset { + + // Define the constant float variable "kDefProbability" and set its value to 0.5 + const float RandomHorizontalFlipWithBBoxOp::kDefProbability = 0.5; + } +} + +// The Compute function of the RandomHorizontalFlipWithBBoxOp class Status RandomHorizontalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output are vectors IO_CHECK_VECTOR(input, output); + + // Validate the bounding boxes in the input RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + + // Check if the random number generated by the distribution is within the desired range if (distribution_(rnd_)) { - // To test bounding boxes algorithm, create random bboxes from image dims - size_t num_of_boxes = input[1]->shape()[0]; // set to give number of bboxes - float img_center = (input[0]->shape()[1] / 2.); // get the center of the image + + // To test the bounding boxes algorithm, create random bounding boxes from image dimensions + size_t num_of_boxes = input[1]->shape()[0]; // Get the number of bounding boxes + float img_center = (input[0]->shape()[1] / 2.); // Get the center of the image + + // Iterate over each bounding box for (int i = 0; i < num_of_boxes; i++) { + + // Read the bounding box from the input tensor std::shared_ptr bbox; RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); - // do the flip - BoundingBox::bbox_float diff = img_center - bbox->x(); // get distance from min_x to center - BoundingBox::bbox_float refl_min_x = diff + img_center; // get reflection of min_x - BoundingBox::bbox_float new_min_x = - refl_min_x - bbox->width(); // subtract from the reflected min_x to get the new one + + // Perform the horizontal flip + BoundingBox::bbox_float diff = img_center - bbox->x(); // Get the distance from min_x to center + BoundingBox::bbox_float refl_min_x = diff + img_center; // Get the reflection of min_x + BoundingBox::bbox_float new_min_x = refl_min_x - bbox->width(); // Subtract from the reflected min_x to get the new one bbox->SetX(new_min_x); + + // Write the modified bounding box back to the input tensor RETURN_IF_NOT_OK(bbox->WriteToTensor(input[1], i)); } + + // Resize the output tensor row to accommodate the modified bounding boxes (*output).resize(2); - // move input to output pointer of bounding boxes + + // Move the input tensor containing the bounding boxes to the output tensor row (*output)[1] = std::move(input[1]); - // perform HorizontalFlip on the image + + // Perform horizontal flip on the image std::shared_ptr input_cv = CVTensor::AsCVTensor(std::move(input[0])); return HorizontalFlip(std::static_pointer_cast(input_cv), &(*output)[0]); } + + // If the random number generated by the distribution is not within the desired range, + // simply copy the input tensor row to the output tensor row *output = input; + + // Return OK status to indicate successful computation return Status::OK(); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_invert_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_invert_op.cc index 9c18ccf7480..40b032085cc 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_invert_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_invert_op.cc @@ -14,34 +14,56 @@ * limitations under the License. */ +// Include the header file "random_invert_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_invert_op.h" -#include "minddata/dataset/kernels/image/image_utils.h" +// Include the header file "minddata/dataset/kernels/image/image_utils.h" which contains utility functions for image processing in the MindData library. +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const float RandomInvertOp::kDefProbability = 0.5; -Status RandomInvertOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - // check input - if (input->Rank() != DEFAULT_IMAGE_RANK) { - RETURN_STATUS_UNEXPECTED("RandomInvert: image shape is not , got rank: " + std::to_string(input->Rank())); - } - if (input->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { - RETURN_STATUS_UNEXPECTED( - "RandomInvert: image shape is incorrect, expected num of channels is 3, " - "but got:" + - std::to_string(input->shape()[CHANNEL_INDEX])); - } - CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, - "RandomInvert: Cannot convert from OpenCV type, unknown CV type. Currently " - "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); - if (distribution_(rnd_)) { - return InvertOp::Compute(input, output); - } - *output = input; - return Status::OK(); + // Define the nested namespace "dataset" + namespace dataset { + + // Define the constant float variable "kDefProbability" with a default value of 0.5 + const float RandomInvertOp::kDefProbability = 0.5; + } +} + +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Check the shape of the input tensor +if (input->Rank() != DEFAULT_IMAGE_RANK) { + // If the rank of the input tensor is not equal to DEFAULT_IMAGE_RANK, return an error message + RETURN_STATUS_UNEXPECTED("RandomInvert: image shape is not , got rank: " + std::to_string(input->Rank())); +} + +// Check the number of channels in the input tensor +if (input->shape()[CHANNEL_INDEX] != DEFAULT_IMAGE_CHANNELS) { + // If the number of channels is not equal to DEFAULT_IMAGE_CHANNELS, return an error message + RETURN_STATUS_UNEXPECTED( + "RandomInvert: image shape is incorrect, expected num of channels is 3, " + "but got:" + + std::to_string(input->shape()[CHANNEL_INDEX])); +} + +// Check if the input tensor has a valid CV type +CHECK_FAIL_RETURN_UNEXPECTED(input->type().AsCVType() != kCVInvalidType, + "RandomInvert: Cannot convert from OpenCV type, unknown CV type. Currently " + "supported data type: [int8, uint8, int16, uint16, int32, float16, float32, float64]."); + +// Generate a random number using the distribution and random number generator +if (distribution_(rnd_)) { + // If the generated random number satisfies the condition, call the Compute function of the InvertOp class + return InvertOp::Compute(input, output); +} + +// If the generated random number does not satisfy the condition, assign the input tensor to the output tensor +*output = input; + +// Return OK status to indicate successful computation +return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_lighting_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_lighting_op.cc index 211136a58f6..83fbcdf3081 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_lighting_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_lighting_op.cc @@ -14,21 +14,41 @@ * limitations under the License. */ +// Include the header file "random_lighting_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_lighting_op.h" -#include "minddata/dataset/kernels/image/image_utils.h" +// Include the header file "minddata/dataset/kernels/image/image_utils.h" which contains utility functions for image processing in the MindData library. +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const float RandomLightingOp::kAlpha = 0.05; + // Define the nested namespace "dataset" within the "mindspore" namespace + namespace dataset { + + // Define the constant float variable "kAlpha" for the RandomLightingOp class + const float RandomLightingOp::kAlpha = 0.05; + } +} + +// Compute function for the RandomLightingOp class +// Takes in an input tensor and a pointer to an output tensor +// Uses IO_CHECK macro to check if the input and output are valid Status RandomLightingOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { IO_CHECK(input, output); + // Rest of the code goes here +} + // Generate random values for red, green, and blue channels using the distribution object rnd_rgb_ float rnd_r = dist_(rnd_rgb_); float rnd_g = dist_(rnd_rgb_); float rnd_b = dist_(rnd_rgb_); + + // Call the RandomLighting function with the input, output, and random values for red, green, and blue channels return RandomLighting(input, output, rnd_r, rnd_g, rnd_b); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_posterize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_posterize_op.cc index 00cf585affc..f82bf160039 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_posterize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_posterize_op.cc @@ -14,28 +14,55 @@ * limitations under the License. */ -#include "minddata/dataset/kernels/image/random_posterize_op.h" +// Include the header file "random_posterize_op.h" from the "minddata/dataset/kernels/image" directory. +// Include the OpenCV image codecs header file, which provides functions for reading and writing images #include +// Include the "random.h" header file from the "minddata/dataset/util" directory #include "minddata/dataset/util/random.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant vector of unsigned 8-bit integers named kBitRange const std::vector RandomPosterizeOp::kBitRange = {4, 8}; +// Define the constructor for the RandomPosterizeOp class, which takes a vector of uint8_t values as input RandomPosterizeOp::RandomPosterizeOp(const std::vector &bit_range) : PosterizeOp(bit_range[0]), bit_range_(bit_range) { + + // Seed the random number generator with a random seed obtained from the GetSeed() function rnd_.seed(GetSeed()); + + // Set the is_deterministic_ flag to false, indicating that the operation is not deterministic is_deterministic_ = false; } +// Define the Compute function for the RandomPosterizeOp class Status RandomPosterizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input tensor is not null, if it is null, return an error message CHECK_FAIL_RETURN_UNEXPECTED(input != nullptr, "RandomPosterizeOp: parameter input is nullptr"); + + // Determine the bit value for posterization based on the bit range + // If the lower and upper bounds of the bit range are the same, use that value as the bit value + // Otherwise, generate a random bit value within the range using a uniform distribution and the random number generator bit_ = (bit_range_[0] == bit_range_[1]) ? bit_range_[0] : std::uniform_int_distribution(bit_range_[0], bit_range_[1])(rnd_); + + // Call the Compute function of the PosterizeOp class to perform the posterization operation return PosterizeOp::Compute(input, output); } -} // namespace dataset -} // namespace mindspore + +// End of namespace dataset +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_op.cc index 25cf91e33cb..73ee5ffa8e4 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_op.cc @@ -15,26 +15,53 @@ */ #include "minddata/dataset/kernels/image/random_resize_op.h" +// Include the random header, which provides facilities for generating random numbers #include +// Include the header file for the ConfigManager class from the minddata::dataset::core namespace #include "minddata/dataset/core/config_manager.h" + +// Include the header file for the CvTensor class from the minddata::dataset::core namespace #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the Status class from the minddata::dataset::util namespace #include "minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { -const int32_t RandomResizeOp::kDefTargetWidth = 0; +// The code is defining a constant integer variable named "kDefTargetWidth" inside the "RandomResizeOp" class in the "dataset" namespace of the "mindspore" namespace. +// The constant is initialized with the value 0. + +// Define the Compute function for the RandomResizeOp class Status RandomResizeOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Get the size of the input vector const auto output_count = input.size(); + + // Resize the output vector to match the size of the input vector output->resize(output_count); + + // Generate a random interpolation mode using the distribution and random generator InterpolationMode interpolation_random_resize = static_cast(distribution_(random_generator_)); + + // Create a shared pointer to a ResizeOp object with the specified size and interpolation mode std::shared_ptr resize_op = std::make_shared(size1_, size2_, interpolation_random_resize); + + // Iterate over each element in the input vector for (size_t i = 0; i < input.size(); i++) { + + // Compute the resize operation on the current input element and store the result in the corresponding output element RETURN_IF_NOT_OK(resize_op->Compute(input[i], &(*output)[i])); } + + // Return a status indicating successful computation return Status::OK(); } + +// Close the dataset namespace } // namespace dataset -} // namespace mindspore + +// Close the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc index e099b78a0f7..9dcc8196a72 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_resize_with_bbox_op.cc @@ -14,20 +14,31 @@ * limitations under the License. */ +// Include the header file for the "random_resize_with_bbox_op" kernel from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_resize_with_bbox_op.h" + +// Include the header file for the "resize_with_bbox_op" kernel from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/resize_with_bbox_op.h" + +// Include the header file for the "status" utility from the "minddata/dataset/util" directory #include "minddata/dataset/util/status.h" -namespace mindspore { -namespace dataset { -const int32_t RandomResizeWithBBoxOp::kDefTargetWidth = 0; +// The code is defining a constant integer variable named "kDefTargetWidth" inside the "RandomResizeWithBBoxOp" class in the "dataset" namespace of the "mindspore" namespace. -Status RandomResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { - // Randomly selects from the following four interpolation methods - // 0-bilinear, 1-nearest_neighbor, 2-bicubic, 3-area - interpolation_ = static_cast(distribution_(random_generator_)); - RETURN_IF_NOT_OK(ResizeWithBBoxOp::Compute(input, output)); - return Status::OK(); -} +// The constant is initialized with the value 0. + +namespace dataset { + // Compute function for the RandomResizeWithBBoxOp class + Status RandomResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { + // Randomly selects from the following four interpolation methods + // 0-bilinear, 1-nearest_neighbor, 2-bicubic, 3-area + interpolation_ = static_cast(distribution_(random_generator_)); + + // Call the Compute function of the parent ResizeWithBBoxOp class + RETURN_IF_NOT_OK(ResizeWithBBoxOp::Compute(input, output)); + + // Return OK status to indicate successful computation + return Status::OK(); + } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_rotation_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_rotation_op.cc index 535d9c80fb1..6472faf6848 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_rotation_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_rotation_op.cc @@ -15,73 +15,129 @@ */ #include "minddata/dataset/kernels/image/random_rotation_op.h" +// Include the random header, which provides facilities for generating random numbers #include +// Include the header file for cv_tensor from the minddata/dataset/core directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for image_utils from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for random from the minddata/dataset/util directory #include "minddata/dataset/util/random.h" + +// Include the header file for status from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const std::vector RandomRotationOp::kDefCenter = {}; -const InterpolationMode RandomRotationOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; -const bool RandomRotationOp::kDefExpand = false; -const uint8_t RandomRotationOp::kDefFillR = 0; -const uint8_t RandomRotationOp::kDefFillG = 0; -const uint8_t RandomRotationOp::kDefFillB = 0; -// constructor + // Define the namespace "dataset" within the "mindspore" namespace + namespace dataset { + + // Define the constant static member variable "kDefCenter" of type std::vector in the "RandomRotationOp" class + const std::vector RandomRotationOp::kDefCenter = {}; + + // Define the constant static member variable "kDefInterpolation" of type InterpolationMode in the "RandomRotationOp" class + const InterpolationMode RandomRotationOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; + + // Define the constant static member variable "kDefExpand" of type bool in the "RandomRotationOp" class + const bool RandomRotationOp::kDefExpand = false; + + // Define the constant static member variable "kDefFillR" of type uint8_t in the "RandomRotationOp" class + const uint8_t RandomRotationOp::kDefFillR = 0; + + // Define the constant static member variable "kDefFillG" of type uint8_t in the "RandomRotationOp" class + const uint8_t RandomRotationOp::kDefFillG = 0; + + // Define the constant static member variable "kDefFillB" of type uint8_t in the "RandomRotationOp" class + const uint8_t RandomRotationOp::kDefFillB = 0; + } +} + +// Constructor for the RandomRotationOp class RandomRotationOp::RandomRotationOp(float start_degree, float end_degree, InterpolationMode resample, bool expand, std::vector center, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : degree_start_(start_degree), - degree_end_(end_degree), - center_(center), - interpolation_(resample), - expand_(expand), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) { + : degree_start_(start_degree), // Initialize the member variable degree_start_ with the value of start_degree + degree_end_(end_degree), // Initialize the member variable degree_end_ with the value of end_degree + center_(center), // Initialize the member variable center_ with the value of center + interpolation_(resample), // Initialize the member variable interpolation_ with the value of resample + expand_(expand), // Initialize the member variable expand_ with the value of expand + fill_r_(fill_r), // Initialize the member variable fill_r_ with the value of fill_r + fill_g_(fill_g), // Initialize the member variable fill_g_ with the value of fill_g + fill_b_(fill_b) { // Initialize the member variable fill_b_ with the value of fill_b + + // Seed the random number generator with a random seed obtained from GetSeed() function rnd_.seed(GetSeed()); + + // Set the is_deterministic_ flag to false, indicating that the operation is not deterministic is_deterministic_ = false; } -// main function call for random rotation : Generate the random degrees +// Check if the input and output tensors are valid Status RandomRotationOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { IO_CHECK(input, output); + + // Generate a random double value using the distribution and random number generator float random_double = distribution_(rnd_); - // get the degree rotation range, mod by 360 because full rotation doesn't affect - // the way this op works (uniform distribution) - // assumption here is that mDegreesEnd > mDegreeStart so we always get positive number - // Note: the range technically is greater than 360 degrees, but will be halved + + // Calculate the degree rotation range by subtracting the start degree from the end degree and dividing by 2 + // This is done to ensure a uniform distribution of degrees within the range float degree_range = (degree_end_ - degree_start_) / 2; + + // Calculate the midpoint of the degree range by adding the start degree to the end degree and dividing by 2 float mid = (degree_end_ + degree_start_) / 2; + + // Calculate the final degree by adding the midpoint to the product of the random double and the degree range float degree = mid + random_double * degree_range; - return Rotate(input, output, center_, degree, interpolation_, expand_, fill_r_, fill_g_, fill_b_); -} +// Return the result of calling the Rotate function with the provided arguments +return Rotate(input, output, center_, degree, interpolation_, expand_, fill_r_, fill_g_, fill_b_); +// Define the function OutputShape for the RandomRotationOp class Status RandomRotationOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Call the OutputShape function of the base class TensorOp and check for any errors RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Initialize variables to store the output height and width 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]> + + // Check if the expand_ flag is set to true + // If expand_ is true, we cannot determine the output shape without the input image + // In this case, set the output shape to <-1, -1[, 3]> CHECK_FAIL_RETURN_UNEXPECTED(inputs.size() > 0 && inputs[0].Size() >= 2, "RandomRotationOp: invalid input shape, expected 2D or 3D input, but got input" " dimension is: " + std::to_string(inputs[0].Rank())); if (!expand_) { + // If expand_ is false, we can determine the output shape based on the input shape + // Set the output height and width to the values from the input shape outputH = inputs[0][0]; outputW = inputs[0][1]; } + + // Create a TensorShape object with the output height and width TensorShape out = TensorShape{outputH, outputW}; + + // Check the rank of the input shape + // If the rank is 2, append the output shape to the outputs vector if (inputs[0].Rank() == 2) outputs.emplace_back(out); + + // If the rank is 3, append the output shape with an additional dimension for the channel if (inputs[0].Rank() == 3) outputs.emplace_back(out.AppendDim(inputs[0][2])); + + // If the outputs vector is not empty, return OK status if (!outputs.empty()) return Status::OK(); + + // If the outputs vector is empty, return an error status with a message indicating the invalid input shape return Status(StatusCode::kMDUnexpectedError, "RandomRotation: invalid input shape, expected 2D or 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); } -} // namespace dataset -} // namespace mindspore + +// Close the namespace blocks for dataset and mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_select_subpolicy_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_select_subpolicy_op.cc index 7f63d0c54da..a6f5ce65a90 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_select_subpolicy_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_select_subpolicy_op.cc @@ -15,84 +15,163 @@ */ #include "minddata/dataset/kernels/image/random_select_subpolicy_op.h" +// Include the header file for the Tensor class from the minddata/dataset/core directory #include "minddata/dataset/core/tensor.h" + +// Include the header file for the TensorOp class from the minddata/dataset/kernels directory #include "minddata/dataset/kernels/tensor_op.h" + +// Include the header file for the Status class from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Compute function for the RandomSelectSubpolicyOp class Status RandomSelectSubpolicyOp::Compute(const TensorRow &input, TensorRow *output) { + + // Create a copy of the input TensorRow TensorRow in_row = input; + + // Generate a random number using the rand_int_ generator size_t rand_num = rand_int_(gen_); + + // Check if the random number is within the range of the policy size CHECK_FAIL_RETURN_UNEXPECTED(rand_num < policy_.size(), "RandomSelectSubpolicy: " "get rand number failed:" + std::to_string(rand_num)); + + // Iterate over each subpolicy in the selected policy for (auto &sub : policy_[rand_num]) { + + // Check if a random double generated using the rand_double_ generator is less than or equal to the subpolicy's second value if (rand_double_(gen_) <= sub.second) { + + // Compute the subpolicy using the input TensorRow and store the result in the output TensorRow RETURN_IF_NOT_OK(sub.first->Compute(in_row, output)); + + // Move the output TensorRow to the input TensorRow for the next iteration in_row = std::move(*output); } } + + // Move the final input TensorRow to the output TensorRow *output = std::move(in_row); + + // Return a Status object indicating successful computation return Status::OK(); } +// This function returns the number of inputs for the RandomSelectSubpolicyOp class. + uint32_t RandomSelectSubpolicyOp::NumInput() { + + // Get the number of inputs for the first subpolicy uint32_t num_in = policy_.front().front().first->NumInput(); + + // Iterate over each subpolicy for (auto &sub : policy_) { + // Iterate over each policy in the subpolicy for (auto &p : sub) { + // Check if the number of inputs for the current policy is different from the first subpolicy if (num_in != p.first->NumInput()) { + // If so, log a warning message and return 0 MS_LOG(WARNING) << "Unable to determine numInput."; return 0; } } } + + // If all subpolicies have the same number of inputs, return the number of inputs for the first subpolicy return num_in; } +// Function to determine the number of outputs for the RandomSelectSubpolicyOp class uint32_t RandomSelectSubpolicyOp::NumOutput() { + + // Get the number of outputs from the first subpolicy uint32_t num_out = policy_.front().front().first->NumOutput(); + + // Iterate through each subpolicy and check if the number of outputs is consistent for (auto &sub : policy_) { for (auto &p : sub) { + + // If the number of outputs is not consistent, log a warning and return 0 if (num_out != p.first->NumOutput()) { MS_LOG(WARNING) << "Unable to determine numInput."; return 0; } } } + + // Return the number of outputs return num_out; } +// Define the function OutputShape for the RandomSelectSubpolicyOp class Status RandomSelectSubpolicyOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Clear the outputs vector to remove any existing shapes outputs.clear(); + + // Resize the outputs vector to have a size equal to the number of output tensors + // Each output tensor is initialized with an unknown rank shape outputs.resize(NumOutput(), TensorShape::CreateUnknownRankShape()); + + // Return a status indicating that the function executed successfully return Status::OK(); } +// Function to determine the output type of the RandomSelectSubpolicyOp Status RandomSelectSubpolicyOp::OutputType(const std::vector &inputs, std::vector &outputs) { + // Check the output type of the first subpolicy and return if it is not okay RETURN_IF_NOT_OK(policy_.front().front().first->OutputType(inputs, outputs)); + + // Iterate through each subpolicy and its operations for (auto &sub : policy_) { for (auto &p : sub) { + // Create a temporary vector to store the output types of the current operation std::vector tmp_types; + // Get the output type of the current operation and return if it is not okay RETURN_IF_NOT_OK(p.first->OutputType(inputs, tmp_types)); + + // Check if the output types of the current operation match the existing output types if (outputs != tmp_types) { + // If the output types do not match, clear the existing output types and resize it to the correct size outputs.clear(); outputs.resize(NumOutput(), DataType(DataType::DE_UNKNOWN)); + // Return OK status to indicate successful program termination return Status::OK(); } } } + + // Return OK status to indicate successful program termination return Status::OK(); } + +// Constructor for the RandomSelectSubpolicyOp class RandomSelectSubpolicyOp::RandomSelectSubpolicyOp(const std::vector &policy) : gen_(GetSeed()), policy_(policy), rand_int_(0, policy.size() - 1), rand_double_(0, 1) { + // Check if the input policy is empty and log an error message if it is if (policy_.empty()) { MS_LOG(ERROR) << "RandomSelectSubpolicy: input 'policy' in RandomSelectSubpolicy is empty, check input parameter."; } + // Set the is_deterministic_ flag to false is_deterministic_ = false; } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_sharpness_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_sharpness_op.cc index c19ec04c177..93ef685b3ba 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_sharpness_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_sharpness_op.cc @@ -14,37 +14,67 @@ * limitations under the License. */ +// Include the header file for the random sharpness operation in the MindData dataset kernels image module #include "minddata/dataset/kernels/image/random_sharpness_op.h" + +// Include the header file for the sharpness operation in the MindData dataset kernels image module #include "minddata/dataset/kernels/image/sharpness_op.h" + +// Include the header file for the OpenCV tensor in the MindData dataset core module #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the random number generator in the MindData dataset util module #include "minddata/dataset/util/random.h" + +// Include the header file for the status in the MindData dataset util module #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant float variable named kDefStartDegree and assign it the value 0.1 const float RandomSharpnessOp::kDefStartDegree = 0.1; + +// Define a constant float variable named kDefEndDegree and assign it the value 1.9 const float RandomSharpnessOp::kDefEndDegree = 1.9; -/// constructor +// Constructor for the RandomSharpnessOp class RandomSharpnessOp::RandomSharpnessOp(float start_degree, float end_degree) : start_degree_(start_degree), end_degree_(end_degree) { + + // Seed the random number generator with a random seed rnd_.seed(GetSeed()); + + // Set the flag for determinism to false is_deterministic_ = false; } -/// main function call for random sharpness : Generate the random degrees -Status RandomSharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - float random_double = distribution_(rnd_); - /// get the degree sharpness range - /// the way this op works (uniform distribution) - /// assumption here is that mDegreesEnd > mDegreeStart so we always get positive number - float degree_range = (end_degree_ - start_degree_) / 2; - float mid = (end_degree_ + start_degree_) / 2; - alpha_ = mid + random_double * degree_range; +/// Check if the input and output tensors are valid +IO_CHECK(input, output); - return SharpnessOp::Compute(input, output); -} -} // namespace dataset -} // namespace mindspore +/// Generate a random double value using the specified distribution and random number generator +float random_double = distribution_(rnd_); + +/// Calculate the range of degrees of sharpness +/// The assumption here is that the end degree is greater than the start degree, so we always get a positive number +float degree_range = (end_degree_ - start_degree_) / 2; + +/// Calculate the midpoint of the degree range +float mid = (end_degree_ + start_degree_) / 2; + +/// Calculate the alpha value using the random double and degree range +alpha_ = mid + random_double * degree_range; + +// Return the result of calling the Compute function of the SharpnessOp class, passing in the input and output parameters +return SharpnessOp::Compute(input, output); +} // End of the dataset namespace +} // End of the mindspore namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_solarize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_solarize_op.cc index 9df0e726ea1..b34e94ddc27 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_solarize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_solarize_op.cc @@ -14,34 +14,73 @@ * limitations under the License. */ +// Include the header file for the random solarize operation in the MindData dataset kernels image module #include "minddata/dataset/kernels/image/random_solarize_op.h" + +// Include the header file for the solarize operation in the MindData dataset kernels image module #include "minddata/dataset/kernels/image/solarize_op.h" + +// Include the header file for the CV tensor in the MindData dataset core module #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the status utility in the MindData dataset util module #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Compute function for the RandomSolarizeOp class +// Takes an input tensor and a pointer to an output tensor as arguments +// Uses IO_CHECK macro to check if the input and output tensors are valid Status RandomSolarizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { IO_CHECK(input, output); + // Rest of the code for the Compute function goes here +} - uint8_t threshold_min_ = threshold_[0], threshold_max_ = threshold_[1]; +// Declare two variables of type uint8_t named threshold_min_ and threshold_max_ +// Initialize threshold_min_ with the value at index 0 of the array threshold_ +// Initialize threshold_max_ with the value at index 1 of the array threshold_ +uint8_t threshold_min_ = threshold_[0], threshold_max_ = threshold_[1]; - CHECK_FAIL_RETURN_UNEXPECTED(threshold_min_ <= threshold_max_, - "RandomSolarize: min of threshold: " + std::to_string(threshold_min_) + - " is greater than max of threshold: " + std::to_string(threshold_max_)); +// Check if the minimum threshold value is less than or equal to the maximum threshold value +CHECK_FAIL_RETURN_UNEXPECTED(threshold_min_ <= threshold_max_, + "RandomSolarize: min of threshold: " + std::to_string(threshold_min_) + + " is greater than max of threshold: " + std::to_string(threshold_max_)); - uint8_t threshold_min = std::uniform_int_distribution(threshold_min_, threshold_max_)(rnd_); - uint8_t threshold_max = std::uniform_int_distribution(threshold_min_, threshold_max_)(rnd_); +// Declare a variable named threshold_min of type uint8_t +// Assign it the result of generating a random number between threshold_min_ and threshold_max_ using the rnd_ random number generator +uint8_t threshold_min = std::uniform_int_distribution(threshold_min_, threshold_max_)(rnd_); +// Declare a variable named threshold_max of type uint8_t +// Assign it the result of generating a random number between threshold_min_ and threshold_max_ using the rnd_ random number generator +uint8_t threshold_max = std::uniform_int_distribution(threshold_min_, threshold_max_)(rnd_); + + // Check if the maximum threshold is less than the minimum threshold if (threshold_max < threshold_min) { + // If so, swap the values of the minimum and maximum thresholds using a temporary variable uint8_t temp = threshold_min; threshold_min = threshold_max; threshold_max = temp; } + + // Create a vector of uint8_t values and initialize it with the minimum and maximum thresholds std::vector inputs = {threshold_min, threshold_max}; + + // Create a unique pointer to a SolarizeOp object using the inputs vector std::unique_ptr op = std::make_unique(inputs); + + // Call the Compute function of the SolarizeOp object, passing the input and output parameters + // and return the result return op->Compute(input, output); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_op.cc index b37d27f9062..d8bfd50facd 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_op.cc @@ -14,27 +14,66 @@ * limitations under the License. */ +// Include the header file "random_vertical_flip_op.h" from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/random_vertical_flip_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" +namespace mindspore { + + // Define the nested namespace "dataset" + namespace dataset { + + // Define the constant float variable "kDefProbability" with a value of 0.5 + const float RandomVerticalFlipOp::kDefProbability = 0.5; + +// Define the implementation of the Compute function for the RandomVerticalFlipOp class + +// Include the necessary headers for the required types and functions +#include +#include + namespace mindspore { namespace dataset { -const float RandomVerticalFlipOp::kDefProbability = 0.5; +// Compute function definition Status RandomVerticalFlipOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Get the size of the input vector const auto output_count = input.size(); + + // Resize the output vector to match the size of the input vector output->resize(output_count); + + // Check if the random number generated by the distribution is within the desired range if (distribution_(rnd_)) { + + // Iterate over each element in the input vector for (size_t i = 0; i < input.size(); i++) { + + // Call the VerticalFlip function to perform the vertical flip operation on the input tensor RETURN_IF_NOT_OK(VerticalFlip(input[i], &(*output)[i])); } + + // Return OK status to indicate successful execution return Status::OK(); } + + // If the random number generated by the distribution is not within the desired range, + // simply assign the input vector to the output vector *output = input; + + // Return OK status to indicate successful execution return Status::OK(); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc index a69920fc157..bd428e6a379 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc @@ -14,41 +14,83 @@ * limitations under the License. */ +// Include the necessary header files for the code to work correctly #include "minddata/dataset/util/status.h" #include "minddata/dataset/kernels/image/bounding_box.h" #include "minddata/dataset/kernels/image/image_utils.h" #include "minddata/dataset/kernels/image/random_vertical_flip_with_bbox_op.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Define the default probability for random vertical flip const float RandomVerticalFlipWithBBoxOp::kDefProbability = 0.5; + +// Implementation of the Compute function for RandomVerticalFlipWithBBoxOp Status RandomVerticalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output are vectors IO_CHECK_VECTOR(input, output); + + // Validate the bounding boxes in the input TensorRow RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); + // ... (rest of the code) + + + // Check if the random number generated by distribution_ is true if (distribution_(rnd_)) { + + // Get the height of the input tensor and assign it to imHeight dsize_t imHeight = input[0]->shape()[0]; - size_t boxCount = input[1]->shape()[0]; // number of rows in tensor - // one time allocation -> updated in the loop - // type defined based on VOC test dataset - for (int i = 0; i < boxCount; i++) { - std::shared_ptr bbox; - RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); + // Get the number of rows in the input tensor and assign it to boxCount + size_t boxCount = input[1]->shape()[0]; + } - // subtract (curCorner + height) from (max) for new Corner position +// One-time allocation for a shared pointer to a BoundingBox object +// This shared pointer will be updated in the loop +// The type of the shared pointer is defined based on the VOC test dataset + +for (int i = 0; i < boxCount; i++) { + // Create a new shared pointer to a BoundingBox object + std::shared_ptr bbox; + + // Read the BoundingBox object from a tensor at index i and assign it to the shared pointer + // The ReadFromTensor function returns an error code, so we check if it is not OK + RETURN_IF_NOT_OK(BoundingBox::ReadFromTensor(input[1], i, &bbox)); + + // Calculate the new y-coordinate for the bounding box corner by subtracting the sum of the current y-coordinate and height from the maximum y-coordinate BoundingBox::bbox_float newBoxCorner_y = (imHeight - 1.0) - ((bbox->y() + bbox->height()) - 1.0); + + // Set the new y-coordinate for the bounding box bbox->SetY(newBoxCorner_y); + + // Write the updated bounding box to the tensor at index i in the input vector RETURN_IF_NOT_OK(bbox->WriteToTensor(input[1], i)); } + + // Define the number of outputs const int output_count = 2; + + // Resize the output vector to hold the specified number of outputs output->resize(output_count); + + // Assign the second element of the input vector to the second element of the output vector (*output)[1] = input[1]; + // Return the result of calling the VerticalFlip function with the first element of the input vector and the address of the first element of the output vector return VerticalFlip(input[0], &(*output)[0]); } + + // If the input and output vectors are the same, assign the input vector to the output vector *output = input; + + // Return a Status object indicating that the operation was successful return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rescale_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rescale_op.cc index 2a500d6c34a..30e4145b474 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rescale_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rescale_op.cc @@ -15,19 +15,37 @@ */ #include "minddata/dataset/kernels/image/rescale_op.h" +// Include the header file for image utilities from the MindData dataset library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData dataset library #include "minddata/dataset/util/status.h" +// The code is defining functions and methods within the "mindspore" and "dataset" namespaces + namespace mindspore { namespace dataset { + +// Implementation of the Compute method for the RescaleOp class Status RescaleOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + // Check if the input and output pointers are valid IO_CHECK(input, output); + + // Call the Rescale function with the input, output, rescale_, and shift_ parameters return Rescale(input, output, rescale_, shift_); } + +// Implementation of the OutputType method for the RescaleOp class Status RescaleOp::OutputType(const std::vector &inputs, std::vector &outputs) { + // Call the OutputType method of the base class TensorOp RETURN_IF_NOT_OK(TensorOp::OutputType(inputs, outputs)); + + // Set the output data type to DE_FLOAT32 outputs[0] = DataType(DataType::DE_FLOAT32); + + // Return a success status return Status::OK(); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_cubic_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_cubic_op.cc index 5e538b400e6..1afc3a16e77 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_cubic_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_cubic_op.cc @@ -15,82 +15,142 @@ */ #include "minddata/dataset/kernels/image/resize_cubic_op.h" -namespace mindspore { -namespace dataset { -// using 8 bits for result -constexpr uint8_t PrecisionBits = 22; -// using 16 bit for clip8 table capacity -constexpr uint16_t Clip8TableCapacity = 640; +// The code is defining two constants within the "mindspore::dataset" namespace. -// construct lookup table +// The first constant is named "PrecisionBits" and has a value of 22. It is declared as a constexpr uint8_t, which means it is a compile-time constant of type unsigned 8-bit integer. This constant is used to specify the number of bits used for the result. + +// The second constant is named "Clip8TableCapacity" and has a value of 640. It is declared as a constexpr uint16_t, which means it is a compile-time constant of type unsigned 16-bit integer. This constant is used to specify the capacity of a table. + +// Create a static lookup table named _clip8_table using a lambda function static const std::vector _clip8_table = []() { + + // Create two vectors, v1 and v2, with initial sizes of 896 and 384 respectively std::vector v1(896, 0); std::vector v2(384, 255); + + // Fill v1 with values from 0 to 255 starting from index Clip8TableCapacity for (int i = 0; i < 256; i++) { v1[i + Clip8TableCapacity] = i; } + + // Append the elements of v2 to the end of v1 v1.insert(v1.end(), v2.begin(), v2.end()); + + // Return the resulting vector v1 as the value of the lambda function return v1; }(); +// Declare a static constant pointer to an unsigned 8-bit integer, named clip8_table +// The pointer is initialized with the address of the element at index Clip8TableCapacity in the _clip8_table array static const uint8_t *clip8_table = &_clip8_table[Clip8TableCapacity]; +// A static inline function that takes an unsigned integer as input and returns an 8-bit unsigned integer static inline uint8_t clip8(unsigned int input) { + + // Calculate the index by right-shifting the input by the number of PrecisionBits uint16_t index = input >> PrecisionBits; + + // Check if the index is greater than or equal to the Clip8TableCapacity if (index >= Clip8TableCapacity) { + + // If the index is greater than or equal to the Clip8TableCapacity, return 0 return 0; } + + // Return the value at the index in the clip8_table return clip8_table[index]; } +// A static inline function that performs cubic interpolation on a given input value x static inline double cubic_interp(double x) { + + // Define a constant coefficient a as -0.5 double a = -0.5; + + // If x is negative, make it positive if (x < 0.0) { x = -x; } + + // If x is less than 1.0, perform the first cubic interpolation equation if (x < 1.0) { return ((a + 2.0) * x - (a + 3.0)) * x * x + 1; } + + // If x is less than 2.0, perform the second cubic interpolation equation if (x < 2.0) { return (((x - 5) * x + 8) * x - 4) * a; } + + // If x is greater than or equal to 2.0, return 0.0 return 0.0; } -struct interpolation { - double (*interpolation)(double x); - double threshold; -}; +// Define a struct named "interpolation" that contains two members -int calc_coeff(int input_size, int out_size, int input0, int input1, const struct interpolation *interp, - std::vector ®ions, std::vector &coeffs_interp) { - double threshold, scale, interp_scale; - int kernel_size; +// Member 1: A function pointer named "interpolation" that points to a function taking a double argument and returning a double +double (*interpolation)(double x); +// Member 2: A double variable named "threshold" +double threshold; + +// Function to calculate coefficients for interpolation + +// Parameters: +// - input_size: size of the input data +// - out_size: size of the output data +// - input0: starting index of the input data +// - input1: ending index of the input data +// - interp: pointer to a struct containing interpolation information +// - regions: reference to a vector to store the regions +// - coeffs_interp: reference to a vector to store the interpolation coefficients + +// Declare variables +double threshold, scale, interp_scale; +int kernel_size; + + // Check if out_size is equal to 0 if (out_size == 0) { + // If so, log an error message and return 0 MS_LOG(ERROR) << "out_size can not be zero."; return 0; } + + // Calculate the scale as the difference between input1 and input0 divided by out_size scale = static_cast((input1 - input0)) / out_size; + + // Check if the scale is less than 1.0 if (scale < 1.0) { + // If so, set interp_scale to 1.0 interp_scale = 1.0; } else { + // Otherwise, set interp_scale to the calculated scale interp_scale = scale; } - // obtain size - threshold = interp->threshold * interp_scale; +// Obtain the size by multiplying the threshold value with the interpolation scale - // coefficients number - kernel_size = static_cast(ceil(threshold)) * 2 + 1; - if (out_size > INT_MAX / (kernel_size * static_cast(sizeof(double)))) { - MS_LOG(WARNING) << "Unable to allocator memory as output Image size is so large."; +// Calculate the kernel size based on the threshold value +kernel_size = static_cast(ceil(threshold)) * 2 + 1; + +// Check if the output size is too large to allocate memory +if (out_size > INT_MAX / (kernel_size * static_cast(sizeof(double)))) { + // Log a warning message indicating that memory allocation is not possible due to the large output image size + MS_LOG(WARNING) << "Unable to allocate memory as output Image size is so large."; + + // Return 0 to indicate failure return 0; - } +} - // coefficient array - std::vector coeffs(out_size * kernel_size, 0.0); - std::vector region(out_size * 2, 0); +// Create a vector named "coeffs" to store the coefficients +// The size of the vector is calculated by multiplying "out_size" and "kernel_size" +// Initialize all elements of the vector to 0.0 +std::vector coeffs(out_size * kernel_size, 0.0); + +// Create a vector named "region" to store the region values +// The size of the vector is calculated by multiplying "out_size" and 2 +// Initialize all elements of the vector to 0 +std::vector region(out_size * 2, 0); for (int xx = 0; xx < out_size; xx++) { double center = input0 + (xx + 0.5) * scale; @@ -109,191 +169,366 @@ int calc_coeff(int input_size, int out_size, int input0, int input1, const struc x_max -= x_min; double *coeff = &coeffs[xx * kernel_size]; for (x = 0; x < x_max; x++) { + // Calculate the interpolation value for the current x position double m = interp->interpolation(((x + x_min) - center + 0.5) * ss); coeff[x] = m; mm += m; } + // ... + } + } + // Iterate over the values of x from 0 to x_max for (x = 0; x < x_max; x++) { + // Check if mm is not equal to 0.0 if (mm != 0.0) { + // Divide the value of coeff[x] by mm coeff[x] /= mm; } } // Remaining values should stay empty if they are used despite of x_max. + // Iterate over the values of x from the current value of x to kernel_size for (; x < kernel_size; x++) { + // Set the value of coeff[x] to 0 coeff[x] = 0; } + // Set the value of region[xx * 2] to x_min region[xx * 2] = x_min; + // Set the value of region[xx * 2 + 1] to x_max region[xx * 2 + 1] = x_max; } - regions = std::move(region); - coeffs_interp = std::move(coeffs); - return kernel_size; -} +// Move the contents of the 'region' object to the 'regions' object +regions = std::move(region); +// Move the contents of the 'coeffs' object to the 'coeffs_interp' object +coeffs_interp = std::move(coeffs); + +// Return the value of 'kernel_size' variable +return kernel_size; + +// A function to normalize the coefficients of a kernel void normalize_coeff(int out_size, int kernel_size, const std::vector &prekk, std::vector &kk) { + + // Iterate over each element in the output size times kernel size for (int x = 0; x < out_size * kernel_size; x++) { + + // If the value at prekk[x] is less than 0 if (prekk[x] < 0) { + + // Normalize the value by multiplying it with (1 << PrecisionBits) and subtracting 0.5 kk[x] = static_cast((-0.5 + prekk[x] * (1 << PrecisionBits))); + } else { + + // Normalize the value by multiplying it with (1 << PrecisionBits) and adding 0.5 kk[x] = static_cast((0.5 + prekk[x] * (1 << PrecisionBits))); + } } } -Status ImagingHorizontalInterp(LiteMat &output, LiteMat input, int offset, int kernel_size, - const std::vector ®ions, const std::vector &prekk) { - int ss0, ss1, ss2; - int32_t *k = nullptr; +// Function to perform horizontal interpolation on an input image and store the result in the output image +// Parameters: +// - output: reference to the output image +// - input: input image to be interpolated +// - offset: offset value for interpolation +// - kernel_size: size of the interpolation kernel +// - regions: vector of regions for interpolation +// - prekk: vector of precomputed values for interpolation - // normalize previous calculated coefficients - std::vector kk(prekk.begin(), prekk.end()); - normalize_coeff(output.width_, kernel_size, prekk, kk); - uint8_t *input_ptr = input; - uint8_t *output_ptr = output; - int32_t input_width = input.width_ * 3; - int32_t output_width = output.width_ * 3; +// Declare variables to store the size of the input image +int ss0, ss1, ss2; - for (int yy = 0; yy < output.height_; yy++) { - // obtain the ptr of output, and put calculated value into it +// Declare a pointer to an int32_t variable and initialize it to nullptr +int32_t *k = nullptr; + +// Create a new vector kk and copy the elements from prekk vector +std::vector kk(prekk.begin(), prekk.end()); + +// Normalize the coefficients in kk vector using the normalize_coeff function +normalize_coeff(output.width_, kernel_size, prekk, kk); + +// Create pointers to the input and output arrays +uint8_t *input_ptr = input; +uint8_t *output_ptr = output; + +// Calculate the width of the input and output arrays +int32_t input_width = input.width_ * 3; +int32_t output_width = output.width_ * 3; + +// Iterate over each row of the output image +for (int yy = 0; yy < output.height_; yy++) { + + // Obtain a pointer to the current row of the output image uint8_t *bgr_buf = output_ptr; + + // Iterate over each pixel in the current row for (int xx = 0; xx < output.width_; xx++) { - int x_min = regions[xx * 2]; - int x_max = regions[xx * 2 + 1]; - k = &kk[xx * kernel_size]; - ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); - for (int x = 0; x < x_max; x++) { - ss0 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3]) * k[x]; - ss1 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 1]) * k[x]; - ss2 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 2]) * k[x]; - } - bgr_buf[0] = clip8(ss0); - bgr_buf[1] = clip8(ss1); - bgr_buf[2] = clip8(ss2); - bgr_buf += 3; + + // Get the minimum and maximum x values for the current pixel + int x_min = regions[xx * 2]; + int x_max = regions[xx * 2 + 1]; + + // Get the pointer to the current kernel + k = &kk[xx * kernel_size]; + + // Initialize the sums for each color channel + ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); + + // Iterate over each x value within the range of the current pixel + for (int x = 0; x < x_max; x++) { + + // Calculate the sum for each color channel using the input image and the kernel + ss0 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3]) * k[x]; + ss1 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 1]) * k[x]; + ss2 += (input_ptr[(yy + offset) * input_width + (x + x_min) * 3 + 2]) * k[x]; + } + + // Clip the sums to the range of 0-255 and store them in the output image buffer + bgr_buf[0] = clip8(ss0); + bgr_buf[1] = clip8(ss1); + bgr_buf[2] = clip8(ss2); + + // Move the output image buffer pointer to the next pixel + bgr_buf += 3; } + + // Move the output image pointer to the next row output_ptr += output_width; - } - return Status::OK(); } +// Return the OK status from the Status class +return Status::OK(); -Status ImagingVerticalInterp(LiteMat &output, LiteMat input, int kernel_size, const std::vector ®ions, - const std::vector &prekk) { - int ss0, ss1, ss2; +// Function to perform vertical interpolation on an input image and store the result in the output image +// Parameters: +// - output: reference to the output image where the interpolated result will be stored +// - input: input image on which interpolation will be performed +// - kernel_size: size of the interpolation kernel +// - regions: vector containing the regions of interest for interpolation +// - prekk: vector containing precomputed values for interpolation - // normalize previous calculated coefficients - std::vector kk(prekk.begin(), prekk.end()); - normalize_coeff(output.height_, kernel_size, prekk, kk); - uint8_t *input_ptr = input; - uint8_t *output_ptr = output; - const int32_t input_width = input.width_ * 3; - const int32_t output_width = output.width_ * 3; +// Declare variables to store the size of the input image +int ss0, ss1, ss2; - for (int yy = 0; yy < output.height_; yy++) { - // obtain the ptr of output, and put calculated value into it +// Create a new vector kk and copy the elements from prekk vector +std::vector kk(prekk.begin(), prekk.end()); + +// Normalize the coefficients in kk vector based on the height of the output image and the kernel size +normalize_coeff(output.height_, kernel_size, prekk, kk); + +// Create pointers to the input and output arrays +uint8_t *input_ptr = input; +uint8_t *output_ptr = output; + +// Calculate the width of the input and output images by multiplying the width by 3 (assuming RGB color channels) +const int32_t input_width = input.width_ * 3; +const int32_t output_width = output.width_ * 3; + +// Iterate over each row of the output image +for (int yy = 0; yy < output.height_; yy++) { + + // Get a pointer to the current row of the output image uint8_t *bgr_buf = output_ptr; + + // Get a pointer to the current row of the kernel int32_t *k = &kk[yy * kernel_size]; + + // Get the minimum and maximum y-coordinates for the current row int y_min = regions[yy * 2]; int y_max = regions[yy * 2 + 1]; - for (int xx = 0; xx < output.width_; xx++) { - ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); - for (int y = 0; y < y_max; y++) { - ss0 += (input_ptr[(y + y_min) * input_width + xx * 3]) * k[y]; - ss1 += (input_ptr[(y + y_min) * input_width + xx * 3 + 1]) * k[y]; - ss2 += (input_ptr[(y + y_min) * input_width + xx * 3 + 2]) * k[y]; - } - bgr_buf[0] = clip8(ss0); - bgr_buf[1] = clip8(ss1); - bgr_buf[2] = clip8(ss2); - bgr_buf += 3; - } - output_ptr += output_width; - } - return Status::OK(); -} + // Iterate over each column of the output image + for (int xx = 0; xx < output.width_; xx++) { + + // Initialize the sum variables for each color channel + ss0 = ss1 = ss2 = 1 << (PrecisionBits - 1); + + // Iterate over each y-coordinate within the specified range + for (int y = 0; y < y_max; y++) { + + // Calculate the weighted sum for each color channel + ss0 += (input_ptr[(y + y_min) * input_width + xx * 3]) * k[y]; + ss1 += (input_ptr[(y + y_min) * input_width + xx * 3 + 1]) * k[y]; + ss2 += (input_ptr[(y + y_min) * input_width + xx * 3 + 2]) * k[y]; + } + + // Clip the calculated values to the range [0, 255] and store them in the output buffer + bgr_buf[0] = clip8(ss0); + bgr_buf[1] = clip8(ss1); + bgr_buf[2] = clip8(ss2); + + // Move the output buffer pointer to the next pixel + bgr_buf += 3; + } + + // Move the output pointer to the next row + output_ptr += output_width; +} +// Return the OK status from the Status class +return Status::OK(); + +// Function to perform image interpolation bool ImageInterpolation(LiteMat input, LiteMat &output, int x_size, int y_size, struct interpolation *interp, const int rect[4]) { + + // Variables for interpolation int horizontal_interp, vertical_interp, horiz_kernel, vert_kernel, rect_y0, rect_y1; + + // Vectors to store interpolation regions and coefficients std::vector horiz_region, vert_region; std::vector horiz_coeff, vert_coeff; + + // Temporary LiteMat to store intermediate results LiteMat temp; - horizontal_interp = x_size != input.width_ || rect[2] != x_size || rect[0]; - vertical_interp = y_size != input.height_ || rect[3] != y_size || rect[1]; +// Check if the x_size is not equal to the width of the input image or if rect[2] is not equal to x_size or if rect[0] is true +horizontal_interp = x_size != input.width_ || rect[2] != x_size || rect[0]; - horiz_kernel = calc_coeff(input.width_, x_size, rect[0], rect[2], interp, horiz_region, horiz_coeff); - if (!horiz_kernel) { +// Check if the y_size is not equal to the height of the input image or if rect[3] is not equal to y_size or if rect[1] is true +vertical_interp = y_size != input.height_ || rect[3] != y_size || rect[1]; + +// Calculate the horizontal kernel coefficients using the calc_coeff function, passing in the width of the input, the x size, the left and right coordinates of the rectangle, the interpolation method, the horizontal region, and the horizontal coefficient +horiz_kernel = calc_coeff(input.width_, x_size, rect[0], rect[2], interp, horiz_region, horiz_coeff); + +// Check if the horizontal kernel calculation was successful +if (!horiz_kernel) { + // If not, return false to indicate failure return false; +} + +// Calculate the vertical kernel coefficients using the calc_coeff function, passing in the height of the input image, y_size, and the y-coordinates of the rectangle, rect[1] and rect[3]. Also pass in the interpolation method, interp, the vertical region, vert_region, and the vertical coefficient, vert_coeff. +vert_kernel = calc_coeff(input.height_, y_size, rect[1], rect[3], interp, vert_region, vert_coeff); + +// Check if the vertical kernel calculation was successful by checking if vert_kernel is false (0 or NULL). +if (!vert_kernel) { + // If the vertical kernel calculation failed, return false to indicate failure. + return false; +} + +// Assign the first row of the input image to the variable rect_y0 +rect_y0 = vert_region[0]; + +// Assign the last row of the input image to the variable rect_y1 +// The last row is calculated by adding the second-to-last row and the last row of the vert_region array +rect_y1 = vert_region[y_size * 2 - 1] + vert_region[y_size * 2 - 2]; + +// Check if horizontal interpolation is enabled +if (horizontal_interp) { + + // Shift the region for vertical resize + for (int i = 0; i < y_size; i++) { + vert_region[i * 2] -= rect_y0; } - vert_kernel = calc_coeff(input.height_, y_size, rect[1], rect[3], interp, vert_region, vert_coeff); - if (!vert_kernel) { - return false; - } - - // first and last used row in the input image - rect_y0 = vert_region[0]; - rect_y1 = vert_region[y_size * 2 - 1] + vert_region[y_size * 2 - 2]; - - // two-direction resize, horizontal resize - if (horizontal_interp) { - // Shift region for vertical resize - for (int i = 0; i < y_size; i++) { - vert_region[i * 2] -= rect_y0; - } - temp.Init(x_size, rect_y1 - rect_y0, 3, LDataType::UINT8, false); + // Initialize a temporary image with the new dimensions + temp.Init(x_size, rect_y1 - rect_y0, 3, LDataType::UINT8, false); +} + // Call the ImagingHorizontalInterp function and store the result in the variable rc auto rc = ImagingHorizontalInterp(temp, input, rect_y0, horiz_kernel, horiz_region, horiz_coeff); + + // Check if rc indicates an error if (rc.IsError()) { + // Print an error message using MS_LOG(ERROR) and the error message from rc MS_LOG(ERROR) << "Image horizontal resize failed, error msg is " << rc; + + // Return false to indicate that the image horizontal resize failed return false; } + + // Check if the temp variable is empty if (temp.IsEmpty()) { + // Return false to indicate that the temp variable is empty return false; } + + // Assign the value of temp to both input and output variables output = input = temp; } /* vertical resize */ + // Check if vertical interpolation is enabled if (vertical_interp) { + // Initialize the output image with the same width as the input image, but with a resized height output.Init(input.width_, y_size, 3, LDataType::UINT8, false); + + // Check if the output image is not empty if (!output.IsEmpty()) { + // Perform vertical interpolation on the output image using the specified kernel, region, and coefficients auto rc = ImagingVerticalInterp(output, input, vert_kernel, vert_region, vert_coeff); + + // Check if any error occurred during vertical interpolation if (rc.IsError()) { + // Print the error message and return false to indicate failure MS_LOG(ERROR) << "Image vertical resize failed, error msg is " << rc; return false; } } + + // Check if the output image is empty after vertical interpolation if (output.IsEmpty()) { + // Return false to indicate failure return false; } } + + // Check if both horizontal and vertical interpolation are disabled if (!horizontal_interp && !vertical_interp) { + // Set the output image to be the same as the input image output = input; } + + // Return true to indicate success return true; } +// Function to resize an image using cubic interpolation + bool ResizeCubic(const LiteMat &input, const LiteMat &dst, int dst_w, int dst_h) { + + // Check if the input image has the correct data type and number of channels if (input.data_type_ != LDataType::UINT8 || input.channel_ != 3) { + + // Log an error message indicating the unsupported data type and number of channels MS_LOG(ERROR) << "Unsupported data type, only support input image of uint8 dtype and 3 channel, got channel: " + std::to_string(input.channel_); + + // Return false to indicate failure return false; } + + // Initialize variables for the size of the destination image int x_size = dst_w, y_size = dst_h; + + // Create a rectangle with the dimensions of the input image int rect[4] = {0, 0, input.width_, input.height_}; + + // Create an output LiteMat object for the resized image LiteMat output; - struct interpolation interp = {cubic_interp, 2.0}; - bool res = ImageInterpolation(input, output, x_size, y_size, &interp, rect); +// Declare a struct variable named "interp" of type "interpolation" and initialize it with the values {cubic_interp, 2.0} +struct interpolation interp = {cubic_interp, 2.0}; +// Declare a boolean variable named "res" and call the function "ImageInterpolation" with the arguments "input", "output", "x_size", "y_size", "&interp", and "rect" +// Store the return value of the function in "res" +bool res = ImageInterpolation(input, output, x_size, y_size, &interp, rect); + + // Use the memcpy_s function to copy the data from the output tensor to the destination tensor auto ret_code = memcpy_s(dst.data_ptr_, output.size_, output.data_ptr_, output.size_); + + // Check if the memcpy_s function returned an error code if (ret_code != 0) { + // If an error occurred, log an error message and return false MS_LOG(ERROR) << "memcpy_s failed when copying tensor."; return false; } + + // Return the result of the operation return res; } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_op.cc index 89c557c8677..326d7e2d38f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_op.cc @@ -15,65 +15,130 @@ */ #include "minddata/dataset/kernels/image/resize_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID + +// If ENABLE_ANDROID is not defined, include the image_utils.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/image_utils.h" + +// If ENABLE_ANDROID is defined, include the lite_image_utils.h header file from the minddata/dataset/kernels/image directory #else #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the status.h header file from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" for encapsulating related code namespace mindspore { + +// Define the nested namespace "dataset" for encapsulating dataset-related code namespace dataset { + +// Define the constant integer variable "kDefWidth" with a default value of 0 const int32_t ResizeOp::kDefWidth = 0; + +// Define the constant enumeration variable "kDefInterpolation" with a default value of InterpolationMode::kLinear const InterpolationMode ResizeOp::kDefInterpolation = InterpolationMode::kLinear; -Status ResizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - RETURN_IF_NOT_OK(ValidateImageRank("Resize", static_cast(input->shape().Size()))); - int32_t output_h = 0; - int32_t output_w = 0; - int32_t input_h = static_cast(input->shape()[0]); - int32_t input_w = static_cast(input->shape()[1]); - if (size2_ == 0) { - if (input_h < input_w) { - CHECK_FAIL_RETURN_UNEXPECTED(input_h != 0, "Resize: the input height cannot be 0."); - output_h = size1_; - output_w = static_cast(std::lround((static_cast(input_w) / input_h) * output_h)); - } else { - CHECK_FAIL_RETURN_UNEXPECTED(input_w != 0, "Resize: the input width cannot be 0."); - output_w = size1_; - output_h = static_cast(std::lround((static_cast(input_h) / input_w) * output_w)); - } - } else { +// The code snippet provided does not contain the complete definition of the ResizeOp class. +// It only defines the default values for the static member variables "kDefWidth" and "kDefInterpolation". +// The complete definition of the ResizeOp class should be present elsewhere in the code. +// The ResizeOp class likely contains member functions and other member variables that are not shown here. +// The purpose of this code is to initialize the default values for the static member variables of the ResizeOp class. + +} // end of namespace dataset +} // end of namespace mindspore + +// Check if the input and output tensors are valid +IO_CHECK(input, output); + +// Validate the rank of the input tensor to ensure it is an image +RETURN_IF_NOT_OK(ValidateImageRank("Resize", static_cast(input->shape().Size()))); + +// Initialize variables for the output height and width +int32_t output_h = 0; +int32_t output_w = 0; + +// Get the input height and width from the shape of the input tensor +int32_t input_h = static_cast(input->shape()[0]); +int32_t input_w = static_cast(input->shape()[1]); + +// Check if size2_ is 0, which means only one dimension is specified for resizing +if (size2_ == 0) { + // If the input height is smaller than the input width + if (input_h < input_w) { + // Check if the input height is not 0 + CHECK_FAIL_RETURN_UNEXPECTED(input_h != 0, "Resize: the input height cannot be 0."); + + // Calculate the output height based on the specified size1_ and the aspect ratio of the input image output_h = size1_; - output_w = size2_; + output_w = static_cast(std::lround((static_cast(input_w) / input_h) * output_h)); + } else { + // Check if the input width is not 0 + CHECK_FAIL_RETURN_UNEXPECTED(input_w != 0, "Resize: the input width cannot be 0."); + + // Calculate the output width based on the specified size1_ and the aspect ratio of the input image + output_w = size1_; + output_h = static_cast(std::lround((static_cast(input_h) / input_w) * output_w)); } +} else { + // If both dimensions are specified for resizing, use the specified size1_ and size2_ + output_h = size1_; + output_w = size2_; +} + // Return the result of calling the Resize function with the provided arguments + // The Resize function is expected to take an input image, an output image, and the desired output dimensions + // The last three arguments are the starting point for cropping the input image (0, 0 in this case) + // The last argument is the interpolation method to be used (interpolation_ in this case) return Resize(input, output, output_h, output_w, 0, 0, interpolation_); } +// Check if the input shape is valid and calculate the output shape for the Resize operation Status ResizeOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + // Call the OutputShape function of the base class TensorOp and check if it returns an error RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector to ensure it is empty before calculating the output shape outputs.clear(); + + // Initialize variables to store the output height and width int32_t outputH = -1, outputW = -1; - // if size2_ == 0, then we cannot know the shape. We need the input image to find the output shape --> set it to - // <-1,-1[,3]> + + // If size2_ is not equal to 0, we can determine the output shape based on the provided size1_ and size2_ if (size2_ != 0) { outputH = size1_; outputW = size2_; } + + // Create a TensorShape object with the calculated output height and width TensorShape out = TensorShape{outputH, outputW}; + + // Check the rank of the input shape if (inputs[0].Rank() == 2) { + // If the input shape has rank 2, append the output shape to the outputs vector (void)outputs.emplace_back(out); } + if (inputs[0].Rank() == 3) { + // If the input shape has rank 3, append the output shape with an additional dimension for the channel (void)outputs.emplace_back(out.AppendDim(inputs[0][2])); } + + // Check if the outputs vector is not empty if (!outputs.empty()) { + // Return OK status to indicate successful calculation of the output shape return Status::OK(); } - return Status(StatusCode::kMDUnexpectedError, - "Resize: invalid input shape, expected 2D or 3D input, but got input dimension is:" + - std::to_string(inputs[0].Rank())); } +// Return a Status object with an error code and error message +return Status(StatusCode::kMDUnexpectedError, + "Resize: invalid input shape, expected 2D or 3D input, but got input dimension is:" + + std::to_string(inputs[0].Rank())); +} + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_preserve_ar_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_preserve_ar_op.cc index 2cd13e1ab62..cb3302556cd 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_preserve_ar_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_preserve_ar_op.cc @@ -16,24 +16,44 @@ #include "minddata/dataset/kernels/image/resize_preserve_ar_op.h" #ifdef ENABLE_ANDROID +// If the ENABLE_ANDROID macro is defined, include the "lite_image_utils.h" header file from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif + +// Include the "status.h" header file from the "minddata/dataset/util" directory #include "minddata/dataset/util/status.h" +// The code is defining a constant integer variable named "kDefImgOrientation" inside the "ResizePreserveAROp" class in the "dataset" namespace of the "mindspore" namespace. + namespace mindspore { namespace dataset { + +// Define the default value for the image orientation constant const int32_t ResizePreserveAROp::kDefImgOrientation = 0; +// Define the constructor for the ResizePreserveAROp class ResizePreserveAROp::ResizePreserveAROp(int32_t height, int32_t width, int32_t img_orientation) : height_(height), width_(width), img_orientation_(img_orientation) {} +// The Compute function of the ResizePreserveAROp class Status ResizePreserveAROp::Compute(const TensorRow &inputs, TensorRow *outputs) { + + // Check if the inputs and outputs are vectors IO_CHECK_VECTOR(inputs, outputs); + + // Conditional compilation for Android platform #ifdef ENABLE_ANDROID + + // Call the ResizePreserve function with the provided inputs, height, width, image orientation, and outputs return ResizePreserve(inputs, height_, width_, img_orientation_, outputs); + #endif + + // Return a Status object indicating that the computation was successful return Status::OK(); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_with_bbox_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_with_bbox_op.cc index 0d5fe7ecc98..d3e5ce0d3c5 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/resize_with_bbox_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/resize_with_bbox_op.cc @@ -14,39 +14,94 @@ * limitations under the License. */ +// Include the header file for the "resize_with_bbox_op" class from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/resize_with_bbox_op.h" + +// Include the "utility" header file for the utility functions and classes #include + +// Include the "memory" header file for the smart pointers #include + +// Include the header file for the "resize_op" class from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/resize_op.h" + +// Include the header file for the "bounding_box" class from the "minddata/dataset/kernels/image" directory #include "minddata/dataset/kernels/image/bounding_box.h" + +// Include the header file for the "cv_tensor" class from the "minddata/dataset/core" directory #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the "tensor" class from the "minddata/dataset/core" directory #include "minddata/dataset/core/tensor.h" + +// Include the header file for the "tensor_op" class from the "minddata/dataset/kernels" directory #include "minddata/dataset/kernels/tensor_op.h" + +// Include the header file for the "status" class from the "minddata/dataset/util" directory #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Compute function for the ResizeWithBBoxOp class Status ResizeWithBBoxOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output are vectors IO_CHECK_VECTOR(input, output); + + // Validate the bounding boxes in the input RETURN_IF_NOT_OK(BoundingBox::ValidateBoundingBoxes(input)); - int32_t input_h = input[0]->shape()[0]; - int32_t input_w = input[0]->shape()[1]; - - output->resize(2); - (*output)[1] = input[1]; // move boxes over to output - - std::shared_ptr input_cv = CVTensor::AsCVTensor(input[0]); - - RETURN_IF_NOT_OK(ResizeOp::Compute(std::static_pointer_cast(input_cv), &(*output)[0])); - - int32_t output_h = (*output)[0]->shape()[0]; // output height if ResizeWithBBox - int32_t output_w = (*output)[0]->shape()[1]; // output width if ResizeWithBBox - - size_t bboxCount = input[1]->shape()[0]; // number of rows in bbox tensor - RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, output_w, output_h, input_w, input_h)); - return Status::OK(); + // Continue with the rest of the function... } + +// Declare and initialize an int32_t variable named input_h, which will store the value of the first dimension of the input tensor +int32_t input_h = input[0]->shape()[0]; + +// Declare and initialize an int32_t variable named input_w, which will store the value of the second dimension of the input tensor +int32_t input_w = input[0]->shape()[1]; + +// Resize the output vector to have a size of 2 +output->resize(2); + +// Move the value at index 1 of the input vector to index 1 of the output vector +(*output)[1] = input[1]; + +// Create a shared pointer named "input_cv" of type "CVTensor" and initialize it with the result of calling the static member function "AsCVTensor" of the "CVTensor" class, passing "input[0]" as the argument. + +// Call the Compute function of the ResizeOp class, passing in the input_cv tensor and the address of the first element of the output tensor +// Use std::static_pointer_cast to cast the input_cv tensor to a std::shared_ptr before passing it to the Compute function +// Use the dereference operator (*) to get the value at the address of the first element of the output tensor +// Return the result of the Compute function, which is a status code indicating success or failure + +// Get the height of the output tensor by accessing the first element of the vector `output` and calling the `shape()` function, followed by accessing the first element of the resulting shape vector +int32_t output_h = (*output)[0]->shape()[0]; + +// Get the width of the output tensor by accessing the first element of the vector `output` and calling the `shape()` function, followed by accessing the second element of the resulting shape vector +int32_t output_w = (*output)[0]->shape()[1]; + +// Declare a variable `bboxCount` of type `size_t` to store the number of rows in the `bbox` tensor +size_t bboxCount = input[1]->shape()[0]; + +// Call the `UpdateBBoxesForResize` function from the `BoundingBox` class to update the bounding boxes for resize +// Pass the `(*output)[1]` tensor, `bboxCount`, `output_w`, `output_h`, `input_w`, and `input_h` as arguments +RETURN_IF_NOT_OK(BoundingBox::UpdateBBoxesForResize((*output)[1], bboxCount, output_w, output_h, input_w, input_h)); + +// Return a `Status::OK()` to indicate successful execution of the function +return Status::OK(); +} + +// End of the `dataset` namespace } // namespace dataset -} // namespace mindspore + +// End of the `mindspore` namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_bgr_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_bgr_op.cc index 8fbb89e6f18..0f1370bef6a 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_bgr_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_bgr_op.cc @@ -18,18 +18,40 @@ #include "minddata/dataset/kernels/image/image_utils.h" #else #include "minddata/dataset/kernels/image/lite_image_utils.h" -#endif +// The #endif directive is used to end a conditional compilation block started by #ifdef or #ifndef +// Since there is no corresponding #ifdef or #ifndef in the provided code, this #endif is unnecessary +// It can be safely removed from the code +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -Status RgbToBgrOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - auto input_type = input->type(); - CHECK_FAIL_RETURN_UNEXPECTED(input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && - input_type != DataType::DE_INT64 && input_type != DataType::DE_STRING, - "RgbToBgr: Input includes unsupported data type in [uint32, int64, uint64, string]."); + + // Start of the "dataset" namespace + namespace dataset { + + // Implementation of the Compute function for the RgbToBgrOp class + Status RgbToBgrOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid + IO_CHECK(input, output); + + // Get the data type of the input tensor + auto input_type = input->type(); + + // Check if the input data type is unsupported (uint32, int64, uint64, string) + CHECK_FAIL_RETURN_UNEXPECTED(input_type != DataType::DE_UINT32 && input_type != DataType::DE_UINT64 && + input_type != DataType::DE_INT64 && input_type != DataType::DE_STRING, + "RgbToBgr: Input includes unsupported data type in [uint32, int64, uint64, string]."); + + // Rest of the function implementation... + + } // End of the Compute function + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + // Return the result of calling the RgbToBgr function with the input and output parameters return RgbToBgr(input, output); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_gray_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_gray_op.cc index cec8e5dcb18..2a8f6b1f234 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_gray_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rgb_to_gray_op.cc @@ -20,13 +20,29 @@ #include "minddata/dataset/kernels/image/lite_image_utils.h" #endif +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// The Compute function of the RgbToGrayOp class takes in a shared pointer to an input tensor and a pointer to an output tensor Status RgbToGrayOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the RgbToGray function to convert the input tensor to grayscale and store the result in the output tensor return RgbToGray(input, output); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the namespace blocks for "dataset" and "mindspore" \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_bgr_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_bgr_op.cc index b948fbdca10..fe0a1fdc302 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_bgr_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_bgr_op.cc @@ -15,15 +15,36 @@ */ #include "minddata/dataset/kernels/image/rgba_to_bgr_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// The Compute function of the RgbaToBgrOp class Status RgbaToBgrOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the RgbaToBgr function to convert the input tensor from RGBA to BGR format return RgbaToBgr(input, output); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_rgb_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_rgb_op.cc index 34bcef5f494..cb1c48e1e66 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_rgb_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rgba_to_rgb_op.cc @@ -15,15 +15,36 @@ */ #include "minddata/dataset/kernels/image/rgba_to_rgb_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// The Compute function of the RgbaToRgbOp class Status RgbaToRgbOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the RgbaToRgb function to convert the input tensor from RGBA to RGB return RgbaToRgb(input, output); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc index af384c13eca..a0506635539 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/rotate_op.cc @@ -14,80 +14,153 @@ * limitations under the License. */ +// Include the rotate_op.h header file from the minddata/dataset/kernels/image directory #include "minddata/dataset/kernels/image/rotate_op.h" +// Check if the ENABLE_ANDROID macro is defined #ifndef ENABLE_ANDROID -#include "minddata/dataset/kernels/image/image_utils.h" + + // If ENABLE_ANDROID is not defined, include the "minddata/dataset/kernels/image/image_utils.h" header file + #include "minddata/dataset/kernels/image/image_utils.h" + #else -#include "minddata/dataset/kernels/image/lite_image_utils.h" + + // If ENABLE_ANDROID is defined, include the "minddata/dataset/kernels/image/lite_image_utils.h" header file + #include "minddata/dataset/kernels/image/lite_image_utils.h" + #endif +// Define the namespace "mindspore" namespace mindspore { -namespace dataset { -const std::vector RotateOp::kDefCenter = {}; -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; + // Define the namespace "dataset" within the "mindspore" namespace + namespace dataset { + + // Define the constant static member variable "kDefCenter" of type std::vector in the RotateOp class + const std::vector RotateOp::kDefCenter = {}; + + // Define the constant static member variable "kDefInterpolation" of type InterpolationMode in the RotateOp class + const InterpolationMode RotateOp::kDefInterpolation = InterpolationMode::kNearestNeighbour; + + // Define the constant static member variable "kDefExpand" of type bool in the RotateOp class + const bool RotateOp::kDefExpand = false; + + // Define the constant static member variable "kDefFillR" of type uint8_t in the RotateOp class + const uint8_t RotateOp::kDefFillR = 0; + + // Define the constant static member variable "kDefFillG" of type uint8_t in the RotateOp class + const uint8_t RotateOp::kDefFillG = 0; + + // Define the constant static member variable "kDefFillB" of type uint8_t in the RotateOp class + const uint8_t RotateOp::kDefFillB = 0; + } +} + +// Definition of the constructor for the RotateOp class, taking an angle_id as a parameter RotateOp::RotateOp(int angle_id) - : angle_id_(angle_id), - degrees_(0), - center_({}), - interpolation_(InterpolationMode::kLinear), - expand_(false), - fill_r_(0), - fill_g_(0), - fill_b_(0) {} + : angle_id_(angle_id), // Initialize the angle_id_ member variable with the value passed as a parameter + degrees_(0), // Initialize the degrees_ member variable with the value 0 + center_({}), // Initialize the center_ member variable with an empty initializer list + interpolation_(InterpolationMode::kLinear), // Initialize the interpolation_ member variable with the value InterpolationMode::kLinear + expand_(false), // Initialize the expand_ member variable with the value false + fill_r_(0), // Initialize the fill_r_ member variable with the value 0 + fill_g_(0), // Initialize the fill_g_ member variable with the value 0 + fill_b_(0) {} // Initialize the fill_b_ member variable with the value 0 +// Definition of the constructor for the RotateOp class RotateOp::RotateOp(float degrees, InterpolationMode resample, bool expand, std::vector center, uint8_t fill_r, uint8_t fill_g, uint8_t fill_b) - : angle_id_(0), - degrees_(degrees), - center_(center), - interpolation_(resample), - expand_(expand), - fill_r_(fill_r), - fill_g_(fill_g), - fill_b_(fill_b) {} + : angle_id_(0), // Initialize the angle_id_ member variable to 0 + degrees_(degrees), // Initialize the degrees_ member variable with the provided degrees value + center_(center), // Initialize the center_ member variable with the provided center vector + interpolation_(resample), // Initialize the interpolation_ member variable with the provided resample value + expand_(expand), // Initialize the expand_ member variable with the provided expand value + fill_r_(fill_r), // Initialize the fill_r_ member variable with the provided fill_r value + fill_g_(fill_g), // Initialize the fill_g_ member variable with the provided fill_g value + fill_b_(fill_b) {} // Initialize the fill_b_ member variable with the provided fill_b value +// The Compute function of the RotateOp class Status RotateOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Validate the rank of the input tensor RETURN_IF_NOT_OK(ValidateImageRank("Rotate", static_cast(input->shape().Size()))); + + // Check if the code is being compiled for Android #ifndef ENABLE_ANDROID + + // Call the Rotate function with the specified parameters return Rotate(input, output, center_, degrees_, interpolation_, expand_, fill_r_, fill_g_, fill_b_); + #else + + // Call the Rotate function with the specified parameters for Android return Rotate(input, output, angle_id_); + #endif } -Status RotateOp::OutputShape(const std::vector &inputs, std::vector &outputs) { +// Check if the code is being compiled for Android #ifndef ENABLE_ANDROID + + // Call the OutputShape function of the base class TensorOp and store the result in outputs RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs)); + + // Clear the outputs vector outputs.clear(); + + // Initialize outputH and outputW to -1 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]> + + // Check if expand_ is false + // If expand_ is true, we cannot determine the output shape and set it to <-1,-1[,3]> if (!expand_) { + // Set outputH and outputW to the values of the first two dimensions of the input shape outputH = inputs[0][0]; outputW = inputs[0][1]; } + + // Create a TensorShape object out with dimensions outputH and outputW TensorShape out = TensorShape{outputH, outputW}; + + // Check if the rank of the input shape is 2 + // If it is, append out to outputs if (inputs[0].Rank() == 2) outputs.emplace_back(out); + + // Check if the rank of the input shape is 3 + // If it is, append out with an additional dimension equal to the third dimension of the input shape to outputs if (inputs[0].Rank() == 3) outputs.emplace_back(out.AppendDim(inputs[0][2])); + + // Check if outputs is not empty + // If it is not empty, return Status::OK() if (!outputs.empty()) return Status::OK(); + + // Return an error status with a message indicating that the input shape is invalid return Status(StatusCode::kMDUnexpectedError, "Rotate: invalid input shape, expected 2D or 3D input, but got input dimension is:" + std::to_string(inputs[0].Rank())); + #else + + // Check if the size of the inputs vector is not equal to the number of inputs expected by the operator + // If it is not equal, return an error status with a message indicating the mismatch if (inputs.size() != NumInput()) return Status(StatusCode::kMDUnexpectedError, "The size of the input argument vector: " + std::to_string(inputs.size()) + ", does not match the number of inputs: " + std::to_string(NumInput())); + + // Set outputs to be equal to inputs outputs = inputs; + + // Return Status::OK() return Status::OK(); + #endif -} + +// Close the namespace dataset } // namespace dataset -} // namespace mindspore + +// Close the namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/sharpness_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/sharpness_op.cc index 2a6a2a3e1fa..3bf5ed9c6c2 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/sharpness_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/sharpness_op.cc @@ -14,67 +14,148 @@ * limitations under the License. */ +// Include the header file for the sharpness operation in the MindData dataset kernels for images #include "minddata/dataset/kernels/image/sharpness_op.h" + +// Include the header file for image utilities in the MindData dataset kernels #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the CVTensor class in the MindData dataset core #include "minddata/dataset/core/cv_tensor.h" + +// Include the header file for the Status class in the MindData dataset utilities #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code specific to the "dataset" namespace can be written here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// Define a constant float variable named kDefAlpha and assign it the value 1.0 const float SharpnessOp::kDefAlpha = 1.0; -Status SharpnessOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); +// Compute function for the SharpnessOp class, which takes an input tensor and produces an output tensor +// The function returns a status indicating the success or failure of the computation + +// Check if the input and output tensors are valid (non-null) +IO_CHECK(input, output); try { + // Convert the input tensor to a CVTensor using the AsCVTensor function std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the OpenCV Mat object from the CVTensor cv::Mat input_img = input_cv->mat(); + + // Check if the Mat object contains valid data if (!input_cv->mat().data) { + // If the Mat object does not contain valid data, return an error message RETURN_STATUS_UNEXPECTED("[Internal ERROR] Sharpness: load image failed."); } +// Check if the rank of the input tensor is equal to 1 or if the number of dimensions in the input matrix is greater than 2 if (input_cv->Rank() == 1 || input_cv->mat().dims > 2) { - RETURN_STATUS_UNEXPECTED("Sharpness: shape of input is not or , but got rank: " + - std::to_string(input_cv->Rank())); + + // If the condition is true, return an error message indicating that the shape of the input is not or , + // and also include the rank of the input tensor in the error message + RETURN_STATUS_UNEXPECTED("Sharpness: shape of input is not or , but got rank: " + + std::to_string(input_cv->Rank())); } - /// creating a smoothing filter. 1, 1, 1, - /// 1, 5, 1, - /// 1, 1, 1 +// Creating a smoothing filter with the following pattern: +// 1, 1, 1, +// 1, 5, 1, +// 1, 1, 1 - const float filterMid = 5.0; - const float filterSum = 13.0; - cv::Mat filter = cv::Mat(3, 3, CV_32F, cv::Scalar::all(1.0 / filterSum)); - filter.at(1, 1) = filterMid / filterSum; +// Define a constant float variable named filterMid and assign it the value 5.0 +const float filterMid = 5.0; - /// applying filter on channels - cv::Mat result = cv::Mat(); - cv::filter2D(input_img, result, -1, filter); +// Define a constant float variable named filterSum and assign it the value 13.0 +const float filterSum = 13.0; - int height = input_cv->shape()[0]; - int width = input_cv->shape()[1]; +// Create a cv::Mat object named filter with dimensions 3x3, data type CV_32F, and initialize all elements with the value 1.0 divided by filterSum +cv::Mat filter = cv::Mat(3, 3, CV_32F, cv::Scalar::all(1.0 / filterSum)); - /// restoring the edges - input_img.row(0).copyTo(result.row(0)); - input_img.row(height - 1).copyTo(result.row(height - 1)); - input_img.col(0).copyTo(result.col(0)); - input_img.col(width - 1).copyTo(result.col(width - 1)); +// Set the element at row 1, column 1 of the filter matrix to the value of filterMid divided by filterSum +filter.at(1, 1) = filterMid / filterSum; - /// blend based on alpha : (alpha_ *input_img) + ((1.0-alpha_) * result); - cv::addWeighted(input_img, alpha_, result, 1.0 - alpha_, 0.0, result); +// Apply a filter on the channels of an image - std::shared_ptr output_cv; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); - RETURN_UNEXPECTED_IF_NULL(output_cv); +// Create an empty matrix to store the result +cv::Mat result = cv::Mat(); - *output = std::static_pointer_cast(output_cv); - } +// Apply the filter using the filter2D function +// input_img: the input image to apply the filter on +// result: the output matrix to store the filtered image +// -1: the desired depth of the output image, -1 means the same depth as the input image +// filter: the filter to apply on the image +cv::filter2D(input_img, result, -1, filter); + +// Declare and initialize an integer variable named "height" with the value of the first dimension of the "input_cv" shape +int height = input_cv->shape()[0]; + +// Declare and initialize an integer variable named "width" with the value of the second dimension of the "input_cv" shape +int width = input_cv->shape()[1]; + +// Restoring the edges of the image + +// Copy the first row of the input image to the corresponding row of the result image +input_img.row(0).copyTo(result.row(0)); + +// Copy the last row of the input image to the corresponding row of the result image +input_img.row(height - 1).copyTo(result.row(height - 1)); + +// Copy the first column of the input image to the corresponding column of the result image +input_img.col(0).copyTo(result.col(0)); + +// Copy the last column of the input image to the corresponding column of the result image +input_img.col(width - 1).copyTo(result.col(width - 1)); + +// Blend the input image and the result image based on the alpha value +// The formula used is: (alpha_ * input_img) + ((1.0 - alpha_) * result) + +// Use the OpenCV function addWeighted to perform the blending +// Parameters: +// - input_img: the input image to be blended +// - alpha_: the weight for the input image +// - result: the result image +// - 1.0 - alpha_: the weight for the result image +// - 0.0: the scalar value to be added to the blended images (not used in this case) +// - result: the output image after blending +cv::addWeighted(input_img, alpha_, result, 1.0 - alpha_, 0.0, result); + +// Declare a shared pointer named "output_cv" of type CVTensor +std::shared_ptr output_cv; + +// Call the CreateFromMat function of the CVTensor class to create a new CVTensor object from the "result" matrix +// Pass the rank of the "input_cv" object as a parameter to the CreateFromMat function +// Store the created CVTensor object in the "output_cv" shared pointer +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(result, input_cv->Rank(), &output_cv)); + +// Check if the "output_cv" shared pointer is null +// If it is null, return an unexpected error +RETURN_UNEXPECTED_IF_NULL(output_cv); + +// Cast the output_cv variable to the type Tensor using std::static_pointer_cast +// Assign the result of the cast to the output variable +*output = std::static_pointer_cast(output_cv); +} catch (const cv::Exception &e) { + // Catch any OpenCV exceptions that occur and handle them + // Return an error message with the exception's what() message appended RETURN_STATUS_UNEXPECTED("Sharpness: " + std::string(e.what())); } + // Return a Status object indicating successful program termination return Status::OK(); } +// End of the dataset namespace } // namespace dataset -} // namespace mindspore +// End of the mindspore namespace \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/slice_patches_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/slice_patches_op.cc index b42ee35ff59..60933bbe44f 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/slice_patches_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/slice_patches_op.cc @@ -17,35 +17,80 @@ #include "minddata/dataset/kernels/image/image_utils.h" #include "minddata/dataset/util/status.h" +// Define the namespace "mindspore" namespace mindspore { + +// Define the nested namespace "dataset" namespace dataset { + +// Define the constant integer variable "kDefNumH" and initialize it with the value 1 const int32_t SlicePatchesOp::kDefNumH = 1; + +// Define the constant integer variable "kDefNumW" and initialize it with the value 1 const int32_t SlicePatchesOp::kDefNumW = 1; + +// Define the constant unsigned integer variable "kDefFillV" and initialize it with the value 0 const uint8_t SlicePatchesOp::kDefFillV = 0; + +// Define the constant enum variable "kDefSliceMode" and initialize it with the value SliceMode::kPad const SliceMode SlicePatchesOp::kDefSliceMode = SliceMode::kPad; +} // End of namespace dataset +} // End of namespace mindspore + +// Define the constructor for the SlicePatchesOp class, which takes in four parameters: num_height, num_width, slice_mode, and fill_value SlicePatchesOp::SlicePatchesOp(int32_t num_height, int32_t num_width, SliceMode slice_mode, uint8_t fill_value) : num_height_(num_height), num_width_(num_width), slice_mode_(slice_mode), fill_value_(fill_value) {} +// Compute function for the SlicePatchesOp class, which takes an input TensorRow and produces an output TensorRow Status SlicePatchesOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if the input and output vectors have the same size IO_CHECK_VECTOR(input, output); + + // Check if the size of the input vector is 1 CHECK_FAIL_RETURN_UNEXPECTED( input.size() == 1, - "size of input should be 1, which means 'input_columns' should be 1 when call this operator, but got:" + + "size of input should be 1, which means 'input_columns' should be 1 when calling this operator, but got:" + std::to_string(input.size())); - auto in_tensor = input[0]; - auto in_type = in_tensor->type(); - auto in_shape = in_tensor->shape(); - - CHECK_FAIL_RETURN_UNEXPECTED(in_type.IsNumeric(), "Input Tensor type should be numeric, got type is non-numeric."); - CHECK_FAIL_RETURN_UNEXPECTED( - in_shape.Rank() >= 2, "Rank of input data should be greater than 2, but got:" + std::to_string(in_shape.Rank())); - - std::vector> out; - RETURN_IF_NOT_OK(SlicePatches(in_tensor, &out, num_height_, num_width_, slice_mode_, fill_value_)); - (void)std::copy(out.begin(), out.end(), std::back_inserter(*output)); - return Status::OK(); + // Rest of the code for the Compute function goes here... } -} // namespace dataset -} // namespace mindspore + +// Declare a variable "in_tensor" and assign it the value of the first element in the "input" array +auto in_tensor = input[0]; + +// Declare a variable "in_type" and assign it the type of "in_tensor" +auto in_type = in_tensor->type(); + +// Declare a variable "in_shape" and assign it the shape of "in_tensor" +auto in_shape = in_tensor->shape(); + +// Check if the input tensor type is numeric +CHECK_FAIL_RETURN_UNEXPECTED(in_type.IsNumeric(), "Input Tensor type should be numeric, got type is non-numeric."); + +// Check if the rank of the input data is greater than or equal to 2 +CHECK_FAIL_RETURN_UNEXPECTED(in_shape.Rank() >= 2, "Rank of input data should be greater than 2, but got:" + std::to_string(in_shape.Rank())); + +// Include the necessary headers for the code +#include +#include +#include + +// Start of the namespace "mindspore" +namespace mindspore { +namespace dataset { + +// Function to slice patches from an input tensor and store them in the "out" vector +std::vector> out; +RETURN_IF_NOT_OK(SlicePatches(in_tensor, &out, num_height_, num_width_, slice_mode_, fill_value_)); + +// Copy the elements from the "out" vector to the "output" vector using std::copy and std::back_inserter +(void)std::copy(out.begin(), out.end(), std::back_inserter(*output)); + +// Return a status indicating successful program termination +return Status::OK(); + +// End of the namespace "dataset" +} +} \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/solarize_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/solarize_op.cc index e9b8155c5bb..77d38f2f001 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/solarize_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/solarize_op.cc @@ -18,59 +18,119 @@ #include "minddata/dataset/core/cv_tensor.h" #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { -// only supports RGB images -const uint8_t kPixelValue = 255; + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { -Status SolarizeOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); + // Code for the "mindspore::dataset" namespace goes here - uint8_t threshold_min_ = threshold_[0], threshold_max_ = threshold_[1]; + } // End of the "dataset" namespace - CHECK_FAIL_RETURN_UNEXPECTED( +} // End of the "mindspore" namespace + +// Define a constant variable kPixelValue with a value of 255 +// This variable represents the maximum pixel value for RGB images, indicating full intensity for each color channel (red, green, and blue) + +// Compute function for the SolarizeOp class, which takes an input tensor and computes the solarized version of it +// The solarized version is stored in the output tensor + +// Check if the input and output tensors are valid (not null) +IO_CHECK(input, output); + +// Declare two variables of type uint8_t named threshold_min_ and threshold_max_ +// Initialize threshold_min_ with the value at index 0 of the array threshold_ +// Initialize threshold_max_ with the value at index 1 of the array threshold_ +uint8_t threshold_min_ = threshold_[0], threshold_max_ = threshold_[1]; + +// Check if the minimum threshold is less than or equal to the maximum threshold +CHECK_FAIL_RETURN_UNEXPECTED( threshold_min_ <= threshold_max_, "Solarize: threshold[0] must be smaller or equal to threshold[1], got 'threshold' value: (" + - std::to_string(threshold_min_) + "," + std::to_string(threshold_max_) + ")."); + std::to_string(threshold_min_) + "," + std::to_string(threshold_max_) + ")."); +// If the check fails, return an unexpected error message with the values of threshold_min_ and threshold_max_ try { + // Convert the input tensor to a CVTensor using the AsCVTensor function and store it in a shared_ptr std::shared_ptr input_cv = CVTensor::AsCVTensor(input); + + // Get the underlying cv::Mat object from the CVTensor cv::Mat input_img = input_cv->mat(); + + // Check if the cv::Mat object contains valid data if (!input_cv->mat().data) { + // If the cv::Mat object does not contain valid data, return an error message RETURN_STATUS_UNEXPECTED("Solarize: load image failed."); } - std::shared_ptr mask_mat_tensor; - std::shared_ptr output_cv_tensor; - RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &mask_mat_tensor)); +// Declare a shared pointer named `mask_mat_tensor` of type `CVTensor` +std::shared_ptr mask_mat_tensor; - RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv_tensor)); - RETURN_UNEXPECTED_IF_NULL(mask_mat_tensor); - RETURN_UNEXPECTED_IF_NULL(output_cv_tensor); +// Declare a shared pointer named `output_cv_tensor` of type `CVTensor` +std::shared_ptr output_cv_tensor; +// Call the `CreateFromMat` function of the `CVTensor` class to create a `CVTensor` object from `input_img` +// Pass the `input_cv->Rank()` as the second argument and assign the result to `mask_mat_tensor` +RETURN_IF_NOT_OK(CVTensor::CreateFromMat(input_img, input_cv->Rank(), &mask_mat_tensor)); + +// Create an empty CVTensor with the same shape and type as the input CVTensor, and assign it to the output_cv_tensor variable. If the creation fails, return an error code. +RETURN_IF_NOT_OK(CVTensor::CreateEmpty(input_cv->shape(), input_cv->type(), &output_cv_tensor)); + +// Check if the mask_mat_tensor is null. If it is null, return an error code. +RETURN_UNEXPECTED_IF_NULL(mask_mat_tensor); + +// Check if the output_cv_tensor is null. If it is null, return an error code. +RETURN_UNEXPECTED_IF_NULL(output_cv_tensor); + + // Check if the minimum threshold is equal to the maximum threshold if (threshold_min_ == threshold_max_) { + + // If they are equal, set all elements in the mask matrix to 0 except for the elements greater than or equal to the minimum threshold mask_mat_tensor->mat().setTo(0, ~(input_cv->mat() >= threshold_min_)); } else { + + // If they are not equal, set all elements in the mask matrix to 0 except for the elements that are both greater than or equal to the minimum threshold and less than or equal to the maximum threshold mask_mat_tensor->mat().setTo(0, ~((input_cv->mat() >= threshold_min_) & (input_cv->mat() <= threshold_max_))); } - // solarize desired portion - constexpr int max_size = 255; - output_cv_tensor->mat() = cv::Scalar::all(max_size) - mask_mat_tensor->mat(); - input_cv->mat().copyTo(output_cv_tensor->mat(), mask_mat_tensor->mat() == 0); - input_cv->mat().copyTo(output_cv_tensor->mat(), input_cv->mat() < threshold_min_); +// Define a constant integer variable named "max_size" with a value of 255 +constexpr int max_size = 255; - *output = std::static_pointer_cast(output_cv_tensor); - } +// Set the matrix of the "output_cv_tensor" to be the result of subtracting "mask_mat_tensor" from a matrix filled with "max_size" +output_cv_tensor->mat() = cv::Scalar::all(max_size) - mask_mat_tensor->mat(); + +// Copy the matrix of "input_cv" to the matrix of "output_cv_tensor" where the corresponding element in "mask_mat_tensor" is equal to 0 +input_cv->mat().copyTo(output_cv_tensor->mat(), mask_mat_tensor->mat() == 0); + +// Copy the matrix of "input_cv" to the matrix of "output_cv_tensor" where the corresponding element in "input_cv" is less than "threshold_min_" +input_cv->mat().copyTo(output_cv_tensor->mat(), input_cv->mat() < threshold_min_); + +// Cast the output_cv_tensor to the type Tensor using std::static_pointer_cast +// Assign the result of the cast to the variable output +*output = std::static_pointer_cast(output_cv_tensor); +} catch (const cv::Exception &e) { + // Catch any OpenCV exceptions that may occur during execution const std::string cv_err_msg(e.what()); + + // Create a string to store the error message std::string err_message = "Solarize: "; + + // Append the OpenCV error message to the error message string err_message += cv_err_msg; + + // Return an unexpected status with the error message RETURN_STATUS_UNEXPECTED(err_message); } + + // Return a status indicating successful execution return Status::OK(); } + +// Close the dataset namespace } // namespace dataset -} // namespace mindspore + +// Close the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/swap_red_blue_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/swap_red_blue_op.cc index cee93a323a5..8c1c1ec696e 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/swap_red_blue_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/swap_red_blue_op.cc @@ -15,15 +15,36 @@ */ #include "minddata/dataset/kernels/image/swap_red_blue_op.h" +// Include the header file for image utilities from the MindData library #include "minddata/dataset/kernels/image/image_utils.h" + +// Include the header file for the status utility from the MindData library #include "minddata/dataset/util/status.h" +// Start of the "mindspore" namespace namespace mindspore { -namespace dataset { + // Start of the "dataset" namespace, which is a sub-namespace of "mindspore" + namespace dataset { + + // Code for the "mindspore::dataset" namespace goes here + + } // End of the "dataset" namespace + +} // End of the "mindspore" namespace + +// The Compute function of the SwapRedBlueOp class Status SwapRedBlueOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output tensors are valid IO_CHECK(input, output); + + // Call the SwapRedAndBlue function to perform the red and blue channel swapping return SwapRedAndBlue(input, output); } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/uniform_aug_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/uniform_aug_op.cc index fc8697a1784..dd945f925ac 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/uniform_aug_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/uniform_aug_op.cc @@ -1,63 +1,87 @@ /** * Copyright 2020 Huawei Technologies Co., Ltd - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at +// 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 +// This is a comment that provides a URL to the Apache License 2.0 +// The Apache License 2.0 is a permissive open-source license that allows users to freely use, modify, and distribute the licensed software +// More information about the Apache License 2.0 can be found at the provided URL: 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. -*/ +// This comment block appears to be a license statement, indicating the terms under which the software is distributed. +// It states that unless required by applicable law or agreed to in writing, the software is distributed under the License. +// The software is distributed on an "AS IS" BASIS, meaning there are no warranties or conditions of any kind, either express or implied. +// The License should be consulted for more details on the specific terms and limitations. +// Include the header file "minddata/dataset/kernels/image/uniform_aug_op.h" #include "minddata/dataset/kernels/image/uniform_aug_op.h" +// Include the utility header, which provides various utility functions and classes #include +// Include the "random.h" header file from the "minddata/dataset/util" directory #include "minddata/dataset/util/random.h" +// Define the namespace "mindspore" for encapsulating related code namespace mindspore { + +// Define the nested namespace "dataset" for encapsulating dataset-related code namespace dataset { + +// Define a constant integer variable "kDefNumOps" with a value of 2 for the UniformAugOp class const int UniformAugOp::kDefNumOps = 2; + +// Define the constructor for the UniformAugOp class UniformAugOp::UniformAugOp(std::vector> op_list, int32_t num_ops) : tensor_op_list_(std::move(op_list)), num_ops_(num_ops) { + + // Seed the random number generator with a seed obtained from the GetSeed() function rnd_.seed(GetSeed()); } -// compute method to apply uniformly random selected augmentations from a list -Status UniformAugOp::Compute(const TensorRow &input, TensorRow *output) { - IO_CHECK_VECTOR(input, output); +// The Compute method of the UniformAugOp class is used to apply uniformly randomly selected augmentations from a list to the input tensor row and store the result in the output tensor row. - // randomly select ops to be applied - std::vector> selected_tensor_ops; - std::sample(tensor_op_list_.begin(), tensor_op_list_.end(), std::back_inserter(selected_tensor_ops), num_ops_, rnd_); +// Check if the input and output tensor rows are valid vectors +IO_CHECK_VECTOR(input, output); - bool first = true; - for (const auto &tensor_op : selected_tensor_ops) { - // Do NOT apply the op, if second random generator returned zero - if (std::uniform_int_distribution(0, 1)(rnd_)) { - continue; +// Create a vector to store the selected tensor operations +std::vector> selected_tensor_ops; + +// Randomly select a specified number of tensor operations from the tensor_op_list_ +// and store them in the selected_tensor_ops vector +std::sample(tensor_op_list_.begin(), tensor_op_list_.end(), std::back_inserter(selected_tensor_ops), num_ops_, rnd_); + +bool first = true; // A boolean variable to keep track of whether we are applying the first tensor operation or not + +// Iterate over each tensor operation in the selected_tensor_ops container +for (const auto &tensor_op : selected_tensor_ops) { + + // Check if the second random generator returned zero + if (std::uniform_int_distribution(0, 1)(rnd_) == 0) { + continue; // If it returned zero, skip applying the current tensor operation and continue to the next one } - // apply C++ ops (note: python OPs are not accepted) + + // Apply C++ operations (Python operations are not accepted) if (first) { - RETURN_IF_NOT_OK(tensor_op->Compute(input, output)); - first = false; + // If this is the first tensor operation, compute it using the input tensor and store the result in the output tensor + RETURN_IF_NOT_OK(tensor_op->Compute(input, output)); + first = false; // Set first to false to indicate that the first tensor operation has been applied } else { - RETURN_IF_NOT_OK(tensor_op->Compute(std::move(*output), output)); + // If this is not the first tensor operation, compute it using the moved output tensor (as input) and store the result in the output tensor + RETURN_IF_NOT_OK(tensor_op->Compute(std::move(*output), output)); } - } +} - // The case where no tensor op is applied. + // Check if the output tensor is empty if (output->empty()) { + // If it is empty, assign the input tensor to the output tensor *output = input; } + // Return the OK status from the Status namespace return Status::OK(); } } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/image/vertical_flip_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/image/vertical_flip_op.cc index cfb9689f697..8be1bae990c 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/image/vertical_flip_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/image/vertical_flip_op.cc @@ -14,15 +14,25 @@ * limitations under the License. */ +// Include the header file "minddata/dataset/kernels/image/vertical_flip_op.h" #include "minddata/dataset/kernels/image/vertical_flip_op.h" -#include "minddata/dataset/kernels/image/image_utils.h" +// Include the header file "minddata/dataset/kernels/image/image_utils.h" which contains utility functions for image processing in the MindData library. + +// The code is defining a namespace called "mindspore" which contains another namespace called "dataset" namespace mindspore { namespace dataset { + +// The Compute function of the VerticalFlipOp class is being defined here Status VerticalFlipOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid IO_CHECK(input, output); + + // Call the VerticalFlip function with the input and output pointers return VerticalFlip(input, output); } + } // namespace dataset -} // namespace mindspore +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/plugin_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/plugin_op.cc index 12935231e3d..761dc8bbaea 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/plugin_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/plugin_op.cc @@ -15,80 +15,193 @@ */ #include "minddata/dataset/kernels/plugin_op.h" +// Include the header file for the Tensor class from the minddata/dataset/core directory #include "minddata/dataset/core/tensor.h" + +// Include the header file for the PluginLoader class from the minddata/dataset/plugin directory #include "minddata/dataset/plugin/plugin_loader.h" +// Start of the "mindspore" namespace namespace mindspore { + +// Start of the "dataset" namespace namespace dataset { + +// Implementation of the PluginOp::PluginToTensorRow function Status PluginOp::PluginToTensorRow(const std::vector &in_row, TensorRow *out_row) { + + // Check if the out_row pointer is not null and if it is empty CHECK_FAIL_RETURN_UNEXPECTED(out_row != nullptr && out_row->empty(), "null/empty out_row received!"); + + // Reserve memory in the out_row vector to avoid reallocations out_row->reserve(in_row.size()); + + // Iterate over each tensor in the in_row vector for (const auto &tensor : in_row) { + + // Create a shared pointer to a Tensor object std::shared_ptr output; + + // Get the data type of the tensor DataType tp = DataType(tensor.type_); + + // Check if the data type is numeric and not unknown CHECK_FAIL_RETURN_UNEXPECTED(tp.IsNumeric() && tp != DataType::DE_UNKNOWN, "Input datatype should be numeric, got Unsupported type: " + tensor.type_); + + // Create a Tensor object from the tensor data and shape RETURN_IF_NOT_OK(Tensor::CreateFromMemory(TensorShape(tensor.shape_), tp, tensor.buffer_.data(), &output)); + + // Add the created Tensor object to the out_row vector out_row->emplace_back(output); } + + // Return a Status object indicating successful execution return Status::OK(); } +// End of the "dataset" namespace +} + +// End of the "mindspore" namespace +} + +// Convert a TensorRow to a vector of plugin::Tensor objects Status PluginOp::TensorRowToPlugin(const TensorRow &in_row, std::vector *out_row) { + + // Check if the output vector is not null and empty CHECK_FAIL_RETURN_UNEXPECTED(out_row != nullptr && out_row->empty(), "null/empty out_row received!"); + + // Resize the output vector to match the size of the input TensorRow out_row->resize(in_row.size()); + + // Iterate over each element in the input TensorRow for (size_t ind = 0; ind < in_row.size(); ind++) { + + // Get a reference to the current plugin::Tensor object plugin::Tensor &tensor = (*out_row)[ind]; + + // Check if the current tensor is of numeric type if (in_row[ind]->type().IsNumeric()) { + + // Get the size of the buffer needed to store the tensor data dsize_t buffer_size = in_row[ind]->SizeInBytes(); + + // Resize the buffer of the plugin::Tensor object to match the buffer size tensor.buffer_.resize(buffer_size); + + // Check if the buffer size is less than SECUREC_MEM_MAX_LEN if (buffer_size < SECUREC_MEM_MAX_LEN) { + + // Copy the data from the input tensor to the plugin::Tensor buffer using memcpy_s int ret_code = memcpy_s(tensor.buffer_.data(), tensor.buffer_.size(), in_row[ind]->GetBuffer(), buffer_size); + + // Check if the memcpy_s operation was successful CHECK_FAIL_RETURN_UNEXPECTED(ret_code == 0, "Failed to copy data into plugin tensor."); + } else { + + // Copy the data from the input tensor to the plugin::Tensor buffer using memcpy_s int ret_code = memcpy_s(tensor.buffer_.data(), buffer_size, in_row[ind]->GetBuffer(), buffer_size); + + // Check if the memcpy_s operation was successful CHECK_FAIL_RETURN_UNEXPECTED(ret_code == 0, "Failed to copy data into plugin tensor."); } + } else { // string tensor, for now, only tensor with 1 string is supported! + + // Check if the string tensor has more than 1 element CHECK_FAIL_RETURN_UNEXPECTED(in_row[ind]->shape().NumOfElements() == 1, "String tensor with more than 1 element is not yet supported."); - // get the first and only string in this tensor + + // Get the first and only string in this tensor std::string str1(*(in_row[ind]->begin())); - tensor.buffer_.resize(str1.size()); - auto ret_code = memcpy_s(tensor.buffer_.data(), tensor.buffer_.size(), str1.data(), str1.size()); - CHECK_FAIL_RETURN_UNEXPECTED(ret_code == 0, "memcpy_s failed when copying string tensor."); } + } +} + // Resize the buffer of the tensor to match the size of the input string + tensor.buffer_.resize(str1.size()); + + // Use the memcpy_s function to copy the data from the input string to the tensor buffer + auto ret_code = memcpy_s(tensor.buffer_.data(), tensor.buffer_.size(), str1.data(), str1.size()); + + // Check if the memcpy_s function returned a success code (0) and return an error message if it failed + CHECK_FAIL_RETURN_UNEXPECTED(ret_code == 0, "memcpy_s failed when copying string tensor."); + + } + + // Set the shape of the tensor to match the shape of the input row tensor.shape_ = in_row[ind]->shape().AsVector(); + + // Set the type of the tensor to match the type of the input row tensor.type_ = in_row[ind]->type().ToString(); } + + // Return a status indicating that the function executed successfully return Status::OK(); } +// The Compute function of the PluginOp class, which takes an input TensorRow and produces an output TensorRow Status PluginOp::Compute(const TensorRow &input, TensorRow *output) { - // Compute should quit if init fails. Error code has already been logged, no need to repeat + + // Check if the initialization code has failed, and return if it has RETURN_IF_NOT_OK(init_code_); + + // Create vectors to hold the input and output plugin Tensors std::vector in_row, out_row; + + // Convert the input TensorRow to plugin Tensors and store them in the in_row vector RETURN_IF_NOT_OK(TensorRowToPlugin(input, &in_row)); + + // Call the Compute function of the plugin_op_ object, passing in the input and output vectors plugin::Status rc = plugin_op_->Compute(&in_row, &out_row); + + // Check if the Compute function returned an error status, and log the error message if it did CHECK_FAIL_RETURN_UNEXPECTED(rc.IsOk(), rc.ToString()); + + // Convert the output plugin Tensors to a TensorRow and store it in the output parameter RETURN_IF_NOT_OK(PluginToTensorRow(out_row, output)); + + // Return a success status return Status::OK(); } +// Constructor for the PluginOp class PluginOp::PluginOp(const std::string &lib_path, const std::string &func_name, const std::string &user_args) : plugin_op_(nullptr), lib_path_(lib_path), func_name_(func_name), user_args_(user_args) { + + // Call the Init() function and assign its return value to the init_code_ member variable init_code_ = Init(); } +// Initialize the PluginOp function Status PluginOp::Init() { + + // Create a pointer to the PluginManagerBase class and initialize it to nullptr plugin::PluginManagerBase *plugin = nullptr; + + // Load the plugin using the PluginLoader singleton instance and the provided library path + // The loaded plugin is stored in the 'plugin' pointer RETURN_IF_NOT_OK(PluginLoader::GetInstance()->LoadPlugin(lib_path_, &plugin)); - // casting a void pointer to specific type + + // Cast the void pointer 'plugin' to the specific type 'plugin::TensorOp' and assign it to 'plugin_op_' plugin_op_ = dynamic_cast(plugin->GetModule(func_name_)); + + // Return an error if the cast failed (i.e., 'plugin_op_' is nullptr) RETURN_UNEXPECTED_IF_NULL(plugin_op_); + + // Parse the serialized arguments 'user_args_' using the 'plugin_op_' object plugin::Status rc = plugin_op_->ParseSerializedArgs(user_args_); + + // Check if the parsing was successful, otherwise return an error with the error message CHECK_FAIL_RETURN_UNEXPECTED(rc.IsOk(), rc.ToString()); + + // Return a success status return Status::OK(); } + +// End of the 'dataset' namespace } // namespace dataset -} // namespace mindspore + +// End of the 'mindspore' namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/py_func_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/py_func_op.cc index 061d43c8713..88d25e92461 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/py_func_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/py_func_op.cc @@ -15,149 +15,264 @@ */ #include "minddata/dataset/kernels/py_func_op.h" +// Include the memory header for smart pointers and dynamic memory management #include + +// Include the vector header for dynamic arrays #include +// Include the header file for the Tensor class from the minddata/dataset/core directory #include "minddata/dataset/core/tensor.h" + +// Include the header file for the transforms_ir.h file from the minddata/dataset/kernels/ir/data directory #include "minddata/dataset/kernels/ir/data/transforms_ir.h" + +// Include the header file for the TensorOp class from the minddata/dataset/kernels directory #include "minddata/dataset/kernels/tensor_op.h" + +// Include the header file for the Status class from the minddata/dataset/util directory #include "minddata/dataset/util/status.h" + +// Include the header file for the validators class from the minddata/dataset/util directory #include "minddata/dataset/util/validators.h" namespace mindspore { namespace dataset { + +// Compute function of PyFuncOp class, takes input tensor row and output tensor row as parameters Status PyFuncOp::Compute(const TensorRow &input, TensorRow *output) { + + // Check if input and output vectors are valid IO_CHECK_VECTOR(input, output); + + // Create a Status object with success status and a message Status ret = Status(StatusCode::kSuccess, "PyFunc Call Succeed"); + + // Start a new block { - // Acquire Python GIL + // Acquire the Python Global Interpreter Lock (GIL) py::gil_scoped_acquire gil_acquire; + + // Check if the Python interpreter is initialized if (Py_IsInitialized() == 0) { + // If not initialized, set the Status object with failure status and a message ret = Status(StatusCode::kMDPythonInterpreterFailure, "Python Interpreter is finalized"); + // Jump to the ComputeReturn label to return the Status object goto ComputeReturn; } + try { - // Transform input tensor vector into numpy array vector + // Create a Python tuple with the same size as the input tensor row py::tuple input_args(input.size()); + // Create a Python object to store the return value py::object ret_py_obj; + + // Check if the input tensor row is not empty if (input.size() > 0) { + // Iterate over each tensor in the input tensor row for (size_t i = 0; i < input.size(); i++) { + // Create a new numpy array to store the data of the current tensor py::array new_data; + // Call the GetDataAsNumpy function of the current tensor to get its data as a numpy array RETURN_IF_NOT_OK(input.at(i)->GetDataAsNumpy(&new_data)); // possible memcpy here + // Assign the new_data value to the input_args array at index i input_args[i] = new_data; } - // Invoke python function + // Invoke the python function + // Call the stored python function pointer with the input_args array as arguments ret_py_obj = this->py_func_ptr_(*input_args); } else { + // If the python function does not take any arguments + // Call the stored python function pointer without any arguments ret_py_obj = this->py_func_ptr_(); } if (output_type_ != DataType::DE_UNKNOWN) { + // If the output type is known + // Cast the return value of the python function to the specified output type RETURN_IF_NOT_OK(CastOutput(ret_py_obj, output)); } else { if (py::isinstance(ret_py_obj)) { - // In case of a n-m mapping, the return value will be a tuple of numpy arrays + // If the return value of the python function is a tuple + // Cast the return value to a py::tuple object py::tuple ret_py_tuple = ret_py_obj.cast(); - // Iterate over two containers simultaneously for memory copy + // Iterate over the elements of the tuple for (size_t i = 0; i < ret_py_tuple.size(); i++) { + // Get the i-th element of the tuple py::object ret_py_ele = ret_py_tuple[i]; - // Object is none if pyfunc timeout + // Check if the element is None if (ret_py_ele.is_none()) { + // Log a message indicating that the expected return value is a numpy array but got None MS_LOG(INFO) << "Expected that PyFunc should return numpy array, got None. If python_multiprocessing is " "True, PyFunc may execute time out."; - goto TimeoutError; - } - if (!py::isinstance(ret_py_ele)) { - goto ShapeMisMatch; - } - std::shared_ptr out; - RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(ret_py_ele.cast(), &out)); - output->push_back(out); - } - } else if (py::isinstance(ret_py_obj)) { - // In case of a n-1 mapping, the return value will be a numpy array - std::shared_ptr out; - RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(ret_py_obj.cast(), &out)); - output->push_back(out); - } else { + goto TimeoutError; + } + + // Check if the return value is a numpy array + if (!py::isinstance(ret_py_ele)) { goto ShapeMisMatch; } + + // Create a shared pointer to a Tensor object and initialize it with the numpy array + std::shared_ptr out; + RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(ret_py_ele.cast(), &out)); + + // Add the Tensor object to the output vector + output->push_back(out); } - } catch (const py::error_already_set &e) { + } else if (py::isinstance(ret_py_obj)) { + // In case of a n-1 mapping, the return value will be a numpy array + + // Create a shared pointer to a Tensor object and initialize it with the numpy array + std::shared_ptr out; + RETURN_IF_NOT_OK(Tensor::CreateFromNpArray(ret_py_obj.cast(), &out)); + + // Add the Tensor object to the output vector + output->push_back(out); + } else { + goto ShapeMisMatch; + } + } +} catch (const py::error_already_set &e) { ret = Status(StatusCode::kMDPyFuncException, e.what()); } } -ComputeReturn: - return ret; +// Return the value of the variable 'ret' from the function +return ret; -ShapeMisMatch: - ret = Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__, - "PyFunc should return a numpy array or a numpy array tuple, check data type of return value in user " - "defined python function."); - goto ComputeReturn; +// Create a Status object with the error code StatusCode::kMDShapeMisMatch +// The error message is "PyFunc should return a numpy array or a numpy array tuple, check data type of return value in user defined python function." +// The error location is the current line number (__LINE__) and file name (__FILE__) +ret = Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__, + "PyFunc should return a numpy array or a numpy array tuple, check data type of return value in user defined python function."); -TimeoutError: - ret = Status(StatusCode::kMDTimeOut, __LINE__, __FILE__, - "Expected that PyFunc should return numpy array, got None. If \'python_multiprocessing\' is True, " - "PyFunc may execute time out."); - goto ComputeReturn; -} +// Jump to the label "ComputeReturn" to handle the error and return from the function +goto ComputeReturn; +// Set the return status to indicate a timeout error +ret = Status(StatusCode::kMDTimeOut, __LINE__, __FILE__, + "Expected that PyFunc should return numpy array, got None. If 'python_multiprocessing' is True, " + "PyFunc may execute time out."); + +// Jump to the label "ComputeReturn" to handle the return value +goto ComputeReturn; + +// Function to cast the output of a PyFuncOp to a specified data type Status PyFuncOp::CastOutput(const py::object &ret_py_obj, TensorRow *output) { try { std::shared_ptr out; + + // Switch statement to handle different data types switch (output_type_) { case DataType::DE_INT32: + // Create an empty tensor of shape {1} and data type int32 RETURN_IF_NOT_OK(Tensor::CreateEmpty(TensorShape({1}), DataType(DataType::DE_INT32), &out)); + // Set the value at index {0} of the tensor to the casted value of ret_py_obj RETURN_IF_NOT_OK(out->SetItemAt({0}, ret_py_obj.cast())); break; case DataType::DE_BOOL: + // Create a scalar tensor with the value casted from ret_py_obj RETURN_IF_NOT_OK(Tensor::CreateScalar(ret_py_obj.cast(), &out)); break; default: + // If the specified data type is not supported, return an error RETURN_STATUS_UNEXPECTED("No cast for the specified DataType was found."); } + + // Add the created tensor to the output TensorRow output->push_back(out); } catch (const std::exception &e) { + // If an exception occurs during the casting process, return an error with the exception message return Status(StatusCode::kMDUnexpectedError, e.what()); } + + // Return OK status to indicate successful casting return Status::OK(); } +// Define the implementation of the `to_json` function for the `PyFuncOp` class + Status PyFuncOp::to_json(nlohmann::json *out_json) { + + // Create a JSON object to store the arguments nlohmann::json args; + { + // Acquire the Global Interpreter Lock (GIL) to safely execute Python code py::gil_scoped_acquire gil_acquire; + + // Check if the `to_json` attribute exists in the `py_func_ptr_` object if (py_func_ptr_.attr("to_json")) { + + // Call the `to_json` method and parse the returned JSON string into a JSON object args = nlohmann::json::parse(py_func_ptr_.attr("to_json")().cast()); } } + + // Assign the `args` JSON object to the `out_json` pointer *out_json = args; + + // Return a `Status` object indicating successful execution return Status::OK(); } +// Implementation of the `from_json` function for the PyFuncOp class Status PyFuncOp::from_json(nlohmann::json json_obj, std::vector> *result) { + + // Create a vector to store the output tensor operations std::vector> output; + + // Validate the "tensor_op_name" parameter in the JSON object RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "tensor_op_name", kPyFuncOp)); + + // Validate the "tensor_op_params" parameter in the JSON object RETURN_IF_NOT_OK(ValidateParamInJson(json_obj, "tensor_op_params", kPyFuncOp)); + + // Get the value of the "tensor_op_name" parameter std::string op_name = json_obj["tensor_op_name"]; + + // Get the value of the "tensor_op_params" parameter nlohmann::json op_params = json_obj["tensor_op_params"]; + + // Get the value of the "python_module" parameter std::string python_module = json_obj["python_module"]; + + // Create a null pointer for the tensor operation std::shared_ptr operation = nullptr; - py::function py_func = - py::module::import(python_module.c_str()).attr(op_name.c_str()).attr("from_json")(op_params.dump()); + + // Import the Python module and call the `from_json` function of the specified operation + py::function py_func = py::module::import(python_module.c_str()).attr(op_name.c_str()).attr("from_json")(op_params.dump()); + + // Create a pre-built operation using the PyFuncOp and the imported Python function operation = std::make_shared(std::make_shared(py_func)); + + // Add the operation to the output vector output.push_back(operation); + + // Assign the output vector to the result pointer *result = output; + + // Return OK status to indicate successful execution return Status::OK(); } +// Check if the PyFuncOp is random or not bool PyFuncOp::IsRandom() { + + // Initialize the random flag as true bool random = true; + + // Check if the py_func_ptr_ has the attribute "random" and if its value is false if (py::hasattr(py_func_ptr_, "random") && py::reinterpret_borrow(py_func_ptr_.attr("random")) == false) random = false; + + // Return the value of the random flag return random; } + +// End of the dataset namespace } // namespace dataset -} // namespace mindspore + +// End of the mindspore namespace +} // namespace mindspore \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.cc b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.cc index fbd79b5aca2..7813b8c3e21 100644 --- a/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.cc +++ b/mindspore/ccsrc/minddata/dataset/kernels/tensor_op.cc @@ -19,61 +19,105 @@ namespace mindspore { namespace dataset { -// Name: Compute() -// Description: This Compute() take 1 Tensor and produce 1 Tensor. -// The derived class should override this function otherwise error. -Status TensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { - IO_CHECK(input, output); - if (!OneToOne()) { - return Status(StatusCode::kMDUnexpectedError, "Wrong Compute() function is called. This is not 1-1 TensorOp."); - } else { - return Status(StatusCode::kMDUnexpectedError, - "Is this TensorOp 1-1? If yes, please implement this Compute() in the derived class."); - } -} // Name: Compute() -// Description: This Compute() take multiple Tensors from different columns and produce multiple Tensors too. -// The derived class should override this function otherwise error. +// Description: This Compute() function takes 1 Tensor as input and produces 1 Tensor as output. +// The derived class should override this function, otherwise an error will occur. +Status TensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid + IO_CHECK(input, output); + + // If the TensorOp is not a one-to-one operation, return an error + if (!OneToOne()) { + return Status(StatusCode::kMDUnexpectedError, "Wrong Compute() function is called. This is not a 1-1 TensorOp."); + } else { + // If the TensorOp is a one-to-one operation, return an error message indicating that the derived class should implement the Compute() function + return Status(StatusCode::kMDUnexpectedError, "Is this TensorOp 1-1? If yes, please implement this Compute() in the derived class."); + } +} +} // namespace dataset +} // namespace mindspore + +// Name: Compute() +// Description: This function is used to compute the output Tensors based on the input Tensors. +// It takes multiple Tensors from different columns as input and produces multiple Tensors as output. +// The derived class should override this function, otherwise an error will occur. Status TensorOp::Compute(const TensorRow &input, TensorRow *output) { + // Check if the input and output are vectors IO_CHECK_VECTOR(input, output); + + // If the op is OneToOne, it can only accept one tensor as input if (OneToOne()) { CHECK_FAIL_RETURN_UNEXPECTED(input.size() == 1, "The op is OneToOne, can only accept one tensor as input."); + + // Resize the output vector to hold one tensor output->resize(1); + + // Call the Compute function with the first input tensor and store the result in the first output tensor return Compute(input[0], &(*output)[0]); } + // ... (rest of the code) - return Status(StatusCode::kMDUnexpectedError, - "Is this TensorOp oneToOne? If no, please implement this Compute() in the derived class."); -} +// Return a Status object with an unexpected error code and an error message +return Status(StatusCode::kMDUnexpectedError, "Is this TensorOp oneToOne? If no, please implement this Compute() in the derived class."); +// The Compute function of the TensorOp class Status TensorOp::Compute(const std::shared_ptr &input, std::shared_ptr *output) { + + // Check if the input and output pointers are valid IO_CHECK(input, output); + + // Return a Status object with an error code and a message indicating that the wrong Compute() function is called + // This message suggests that the function should be implemented in the derived class if the operator can be executed on different devices return Status(StatusCode::kMDUnexpectedError, "Wrong Compute() function is called. This is a function for operators which can be executed by" "different device. If so, please implement it in the derived class."); } +// Function to determine the output shape of a tensor operation Status TensorOp::OutputShape(const std::vector &inputs, std::vector &outputs) { + + // Check if the number of input shapes matches the expected number of inputs for this operation if (inputs.size() != NumInput()) return Status(StatusCode::kMDUnexpectedError, "The size of the input argument vector does not match the number of inputs"); + + // Set the output shapes to be the same as the input shapes outputs = inputs; + + // Return a status indicating successful execution of the function return Status::OK(); } +// Implementation of the OutputType function in the TensorOp class + +// This function takes in a vector of input data types and a reference to a vector of output data types Status TensorOp::OutputType(const std::vector &inputs, std::vector &outputs) { + + // Check if the size of the input vector matches the expected number of inputs for this operation if (inputs.size() != NumInput()) return Status(StatusCode::kMDUnexpectedError, "The size of the input argument vector does not match the number of inputs"); + + // Set the output vector to be the same as the input vector outputs = inputs; + + // Return a status indicating successful execution of the function return Status::OK(); } +// SetAscendResource function implementation for the TensorOp class + +// Takes a shared pointer to a DeviceResource object as input Status TensorOp::SetAscendResource(const std::shared_ptr &resource) { + + // Return a Status object with a specific error code and error message return Status(StatusCode::kMDUnexpectedError, "This is a CPU operator which doesn't have Ascend Resource. Please verify your context"); } } // namespace dataset } // namespace mindspore + +// Closing braces to end the "dataset" and "mindspore" namespaces \ No newline at end of file diff --git a/mindspore/ccsrc/minddata/dataset/plugin/plugin_loader.cc b/mindspore/ccsrc/minddata/dataset/plugin/plugin_loader.cc index 5e6e3ec4675..47a4aa5d7ee 100644 --- a/mindspore/ccsrc/minddata/dataset/plugin/plugin_loader.cc +++ b/mindspore/ccsrc/minddata/dataset/plugin/plugin_loader.cc @@ -16,27 +16,51 @@ #include "minddata/dataset/plugin/plugin_loader.h" +// Include the algorithm header for various algorithms like sorting, searching, etc. #include + +// Include the numeric header for numeric algorithms like accumulate, inner_product, etc. #include + +// Include the set header for the set container class #include + +// Include the vector header for the vector container class #include +// Include the header file for the MindSpore log adapter utility #include "mindspore/core/utils/log_adapter.h" + +// Include the header file for the MindData dataset plugin shared library utility #include "minddata/dataset/plugin/shared_lib_util.h" +// Start of the namespace "mindspore" namespace mindspore { +// Start of the namespace "dataset" namespace dataset { +// Definition of the static member function "GetInstance" of the class "PluginLoader" PluginLoader *PluginLoader::GetInstance() noexcept { + // Create a static instance of the class "PluginLoader" named "pl" static PluginLoader pl; + // Return the address of the static instance "pl" return &pl; } +// Destructor for the PluginLoader class PluginLoader::~PluginLoader() { + // Create a vector to store the keys from the plugins map std::vector keys; - // get the keys from map, this is to avoid concurrent iteration and delete + + // Get the keys from the plugins map using std::transform + // This is done to avoid concurrent iteration and deletion std::transform(plugins_.begin(), plugins_.end(), std::back_inserter(keys), [](const auto &p) { return p.first; }); + + // Iterate over the keys vector and unload each plugin for (std::string &key : keys) { + // Call the UnloadPlugin function for the current key Status rc = UnloadPlugin(key); + + // Log an error message if the return code indicates an error MSLOG_IF(ERROR, rc.IsError(), mindspore::NoExceptionType) << rc.ToString(); } } diff --git a/mindspore/ccsrc/minddata/dataset/plugin/shared_lib_util.cc b/mindspore/ccsrc/minddata/dataset/plugin/shared_lib_util.cc index 4d6317df752..0fd98db10ec 100644 --- a/mindspore/ccsrc/minddata/dataset/plugin/shared_lib_util.cc +++ b/mindspore/ccsrc/minddata/dataset/plugin/shared_lib_util.cc @@ -15,11 +15,17 @@ */ #include "minddata/dataset/plugin/shared_lib_util.h" + +// Check if the current platform is Linux and include the necessary header file #ifdef __linux__ #include #endif + +// Start of the namespace for the MindSpore dataset namespace mindspore { namespace dataset { + +// Implementation of the Load function for loading a shared library #ifdef __linux__ void *SharedLibUtil::Load(const std::string &name) { return dlopen(name.c_str(), RTLD_LAZY); } void *SharedLibUtil::FindSym(void *handle, const std::string &name) { return dlsym(handle, name.c_str()); }