| @@ -19,6 +19,7 @@ | |||||
| // Kernel data headers (in alphabetical order) | // Kernel data headers (in alphabetical order) | ||||
| #include "minddata/dataset/kernels/data/one_hot_op.h" | #include "minddata/dataset/kernels/data/one_hot_op.h" | ||||
| #include "minddata/dataset/kernels/data/type_cast_op.h" | |||||
| namespace mindspore { | namespace mindspore { | ||||
| namespace dataset { | namespace dataset { | ||||
| @@ -42,6 +43,16 @@ std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes) { | |||||
| return op; | return op; | ||||
| } | } | ||||
| // Function to create TypeCastOperation. | |||||
| std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type) { | |||||
| auto op = std::make_shared<TypeCastOperation>(data_type); | |||||
| // Input validation | |||||
| if (!op->ValidateParams()) { | |||||
| return nullptr; | |||||
| } | |||||
| return op; | |||||
| } | |||||
| /* ####################################### Validator Functions ############################################ */ | /* ####################################### Validator Functions ############################################ */ | ||||
| /* ####################################### Derived TensorOperation classes ################################# */ | /* ####################################### Derived TensorOperation classes ################################# */ | ||||
| @@ -62,6 +73,24 @@ bool OneHotOperation::ValidateParams() { | |||||
| std::shared_ptr<TensorOp> OneHotOperation::Build() { return std::make_shared<OneHotOp>(num_classes_); } | std::shared_ptr<TensorOp> OneHotOperation::Build() { return std::make_shared<OneHotOp>(num_classes_); } | ||||
| // TypeCastOperation | |||||
| TypeCastOperation::TypeCastOperation(std::string data_type) : data_type_(data_type) {} | |||||
| bool TypeCastOperation::ValidateParams() { | |||||
| std::vector<std::string> predefine_type = {"bool", "int8", "uint8", "int16", "uint16", "int32", "uint32", | |||||
| "int64", "uint64", "float16", "float32", "float64", "string"}; | |||||
| auto itr = std::find(predefine_type.begin(), predefine_type.end(), data_type_); | |||||
| if (itr == predefine_type.end()) { | |||||
| MS_LOG(ERROR) << "TypeCast: Only support type bool, int8, uint8, int16, uint16, int32, uint32, " | |||||
| << "int64, uint64, float16, float32, float64, string, but got " << data_type_; | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| std::shared_ptr<TensorOp> TypeCastOperation::Build() { return std::make_shared<TypeCastOp>(data_type_); } | |||||
| } // namespace transforms | } // namespace transforms | ||||
| } // namespace api | } // namespace api | ||||
| } // namespace dataset | } // namespace dataset | ||||
| @@ -32,6 +32,7 @@ | |||||
| #include "minddata/dataset/kernels/image/random_color_op.h" | #include "minddata/dataset/kernels/image/random_color_op.h" | ||||
| #include "minddata/dataset/kernels/image/random_color_adjust_op.h" | #include "minddata/dataset/kernels/image/random_color_adjust_op.h" | ||||
| #include "minddata/dataset/kernels/image/random_crop_op.h" | #include "minddata/dataset/kernels/image/random_crop_op.h" | ||||
| #include "minddata/dataset/kernels/image/random_crop_decode_resize_op.h" | |||||
| #include "minddata/dataset/kernels/image/random_horizontal_flip_op.h" | #include "minddata/dataset/kernels/image/random_horizontal_flip_op.h" | ||||
| #include "minddata/dataset/kernels/image/random_posterize_op.h" | #include "minddata/dataset/kernels/image/random_posterize_op.h" | ||||
| #include "minddata/dataset/kernels/image/random_rotation_op.h" | #include "minddata/dataset/kernels/image/random_rotation_op.h" | ||||
| @@ -200,6 +201,20 @@ std::shared_ptr<RandomCropOperation> RandomCrop(std::vector<int32_t> size, std:: | |||||
| return op; | return op; | ||||
| } | } | ||||
| // Function to create RandomCropDecodeResizeOperation. | |||||
| std::shared_ptr<RandomCropDecodeResizeOperation> RandomCropDecodeResize(std::vector<int32_t> size, | |||||
| std::vector<float> scale, | |||||
| std::vector<float> ratio, | |||||
| InterpolationMode interpolation, | |||||
| int32_t max_attempts) { | |||||
| auto op = std::make_shared<RandomCropDecodeResizeOperation>(size, scale, ratio, interpolation, max_attempts); | |||||
| // Input validation | |||||
| if (!op->ValidateParams()) { | |||||
| return nullptr; | |||||
| } | |||||
| return op; | |||||
| } | |||||
| // Function to create RandomHorizontalFlipOperation. | // Function to create RandomHorizontalFlipOperation. | ||||
| std::shared_ptr<RandomHorizontalFlipOperation> RandomHorizontalFlip(float prob) { | std::shared_ptr<RandomHorizontalFlipOperation> RandomHorizontalFlip(float prob) { | ||||
| auto op = std::make_shared<RandomHorizontalFlipOperation>(prob); | auto op = std::make_shared<RandomHorizontalFlipOperation>(prob); | ||||
| @@ -784,6 +799,66 @@ std::shared_ptr<TensorOp> RandomCropOperation::Build() { | |||||
| return tensor_op; | return tensor_op; | ||||
| } | } | ||||
| // RandomCropDecodeResizeOperation | |||||
| RandomCropDecodeResizeOperation::RandomCropDecodeResizeOperation(std::vector<int32_t> size, std::vector<float> scale, | |||||
| std::vector<float> ratio, | |||||
| InterpolationMode interpolation, int32_t max_attempts) | |||||
| : size_(size), scale_(scale), ratio_(ratio), interpolation_(interpolation), max_attempts_(max_attempts) {} | |||||
| bool RandomCropDecodeResizeOperation::ValidateParams() { | |||||
| if (size_.empty() || size_.size() > 2) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: size vector has incorrect size: " << size_.size(); | |||||
| return false; | |||||
| } | |||||
| if (scale_.empty() || scale_.size() != 2) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: scale vector has incorrect size: " << scale_.size(); | |||||
| return false; | |||||
| } | |||||
| if (scale_[0] > scale_[1]) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: scale should be in (min,max) format. Got (max,min)."; | |||||
| return false; | |||||
| } | |||||
| if (ratio_.empty() || ratio_.size() != 2) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: ratio vector has incorrect size: " << ratio_.size(); | |||||
| return false; | |||||
| } | |||||
| if (ratio_[0] > ratio_[1]) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: ratio should be in (min,max) format. Got (max,min)."; | |||||
| return false; | |||||
| } | |||||
| if (max_attempts_ < 1) { | |||||
| MS_LOG(ERROR) << "RandomCropDecodeResize: max_attempts must be greater than or equal to 1."; | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| std::shared_ptr<TensorOp> RandomCropDecodeResizeOperation::Build() { | |||||
| int32_t crop_height = size_[0]; | |||||
| int32_t crop_width = size_[0]; | |||||
| // User has specified the crop_width value. | |||||
| if (size_.size() == 2) { | |||||
| crop_width = size_[1]; | |||||
| } | |||||
| float scale_lower_bound = scale_[0]; | |||||
| float scale_upper_bound = scale_[1]; | |||||
| float aspect_lower_bound = ratio_[0]; | |||||
| float aspect_upper_bound = ratio_[1]; | |||||
| auto tensor_op = | |||||
| std::make_shared<RandomCropDecodeResizeOp>(crop_height, crop_width, scale_lower_bound, scale_upper_bound, | |||||
| aspect_lower_bound, aspect_upper_bound, interpolation_, max_attempts_); | |||||
| return tensor_op; | |||||
| } | |||||
| // RandomHorizontalFlipOperation | // RandomHorizontalFlipOperation | ||||
| RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {} | RandomHorizontalFlipOperation::RandomHorizontalFlipOperation(float probability) : probability_(probability) {} | ||||
| @@ -19,6 +19,7 @@ | |||||
| #include <vector> | #include <vector> | ||||
| #include <memory> | #include <memory> | ||||
| #include <string> | |||||
| #include "minddata/dataset/core/constants.h" | #include "minddata/dataset/core/constants.h" | ||||
| namespace mindspore { | namespace mindspore { | ||||
| @@ -48,6 +49,7 @@ namespace transforms { | |||||
| // Transform Op classes (in alphabetical order) | // Transform Op classes (in alphabetical order) | ||||
| class OneHotOperation; | class OneHotOperation; | ||||
| class TypeCastOperation; | |||||
| /// \brief Function to create a OneHot TensorOperation. | /// \brief Function to create a OneHot TensorOperation. | ||||
| /// \notes Convert the labels into OneHot format. | /// \notes Convert the labels into OneHot format. | ||||
| @@ -55,6 +57,12 @@ class OneHotOperation; | |||||
| /// \return Shared pointer to the current TensorOperation. | /// \return Shared pointer to the current TensorOperation. | ||||
| std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes); | std::shared_ptr<OneHotOperation> OneHot(int32_t num_classes); | ||||
| /// \brief Function to create a TypeCast TensorOperation. | |||||
| /// \notes Tensor operation to cast to a given MindSpore data type. | |||||
| /// \param[in] data_type mindspore.dtype to be cast to. | |||||
| /// \return Shared pointer to the current TensorOperation. | |||||
| std::shared_ptr<TypeCastOperation> TypeCast(std::string data_type); | |||||
| /* ####################################### Derived TensorOperation classes ################################# */ | /* ####################################### Derived TensorOperation classes ################################# */ | ||||
| class OneHotOperation : public TensorOperation { | class OneHotOperation : public TensorOperation { | ||||
| @@ -70,6 +78,20 @@ class OneHotOperation : public TensorOperation { | |||||
| private: | private: | ||||
| float num_classes_; | float num_classes_; | ||||
| }; | }; | ||||
| class TypeCastOperation : public TensorOperation { | |||||
| public: | |||||
| explicit TypeCastOperation(std::string data_type); | |||||
| ~TypeCastOperation() = default; | |||||
| std::shared_ptr<TensorOp> Build() override; | |||||
| bool ValidateParams() override; | |||||
| private: | |||||
| std::string data_type_; | |||||
| }; | |||||
| } // namespace transforms | } // namespace transforms | ||||
| } // namespace api | } // namespace api | ||||
| } // namespace dataset | } // namespace dataset | ||||
| @@ -43,6 +43,7 @@ class RandomAffineOperation; | |||||
| class RandomColorOperation; | class RandomColorOperation; | ||||
| class RandomColorAdjustOperation; | class RandomColorAdjustOperation; | ||||
| class RandomCropOperation; | class RandomCropOperation; | ||||
| class RandomCropDecodeResizeOperation; | |||||
| class RandomHorizontalFlipOperation; | class RandomHorizontalFlipOperation; | ||||
| class RandomPosterizeOperation; | class RandomPosterizeOperation; | ||||
| class RandomRotationOperation; | class RandomRotationOperation; | ||||
| @@ -196,6 +197,23 @@ std::shared_ptr<RandomCropOperation> RandomCrop(std::vector<int32_t> size, std:: | |||||
| bool pad_if_needed = false, std::vector<uint8_t> fill_value = {0, 0, 0}, | bool pad_if_needed = false, std::vector<uint8_t> fill_value = {0, 0, 0}, | ||||
| BorderType padding_mode = BorderType::kConstant); | BorderType padding_mode = BorderType::kConstant); | ||||
| /// \brief Function to create a RandomCropDecodeResize TensorOperation. | |||||
| /// \notes Equivalent to RandomResizedCrop, but crops before decodes. | |||||
| /// \param[in] size - a vector representing the output size of the cropped image. | |||||
| /// If size is a single value, a square crop of size (size, size) is returned. | |||||
| /// If size has 2 values, it should be (height, width). | |||||
| /// \param[in] scale - range [min, max) of respective size of the | |||||
| /// original size to be cropped (default=(0.08, 1.0)) | |||||
| /// \param[in] ratio - range [min, max) of aspect ratio to be | |||||
| /// cropped (default=(3. / 4., 4. / 3.)) | |||||
| /// \param[in] interpolation - an enum for the mode of interpolation | |||||
| /// \param[in] The maximum number of attempts to propose a valid crop_area (default=10). | |||||
| /// If exceeded, fall back to use center_crop instead. | |||||
| /// \return Shared pointer to the current TensorOperation. | |||||
| std::shared_ptr<RandomCropDecodeResizeOperation> RandomCropDecodeResize( | |||||
| std::vector<int32_t> size, std::vector<float> scale = {0.08, 1.0}, std::vector<float> ratio = {3. / 4, 4. / 3}, | |||||
| InterpolationMode interpolation = InterpolationMode::kLinear, int32_t max_attempts = 10); | |||||
| /// \brief Function to create a RandomHorizontalFlip TensorOperation. | /// \brief Function to create a RandomHorizontalFlip TensorOperation. | ||||
| /// \notes Tensor operation to perform random horizontal flip. | /// \notes Tensor operation to perform random horizontal flip. | ||||
| /// \param[in] prob - float representing the probability of flip. | /// \param[in] prob - float representing the probability of flip. | ||||
| @@ -480,6 +498,25 @@ class RandomCropOperation : public TensorOperation { | |||||
| BorderType padding_mode_; | BorderType padding_mode_; | ||||
| }; | }; | ||||
| class RandomCropDecodeResizeOperation : public TensorOperation { | |||||
| public: | |||||
| RandomCropDecodeResizeOperation(std::vector<int32_t> size, std::vector<float> scale, std::vector<float> ratio, | |||||
| InterpolationMode interpolation, int32_t max_attempts); | |||||
| ~RandomCropDecodeResizeOperation() = default; | |||||
| std::shared_ptr<TensorOp> Build() override; | |||||
| bool ValidateParams() override; | |||||
| private: | |||||
| std::vector<int32_t> size_; | |||||
| std::vector<float> scale_; | |||||
| std::vector<float> ratio_; | |||||
| InterpolationMode interpolation_; | |||||
| int32_t max_attempts_; | |||||
| }; | |||||
| class RandomHorizontalFlipOperation : public TensorOperation { | class RandomHorizontalFlipOperation : public TensorOperation { | ||||
| public: | public: | ||||
| explicit RandomHorizontalFlipOperation(float probability = 0.5); | explicit RandomHorizontalFlipOperation(float probability = 0.5); | ||||
| @@ -142,3 +142,61 @@ TEST_F(MindDataTestPipeline, TestOneHotSuccess2) { | |||||
| // Manually terminate the pipeline | // Manually terminate the pipeline | ||||
| iter->Stop(); | iter->Stop(); | ||||
| } | } | ||||
| TEST_F(MindDataTestPipeline, TestTypeCastSuccess) { | |||||
| MS_LOG(INFO) << "Doing MindDataTestPipeline-TestTypeCastSuccess."; | |||||
| // Create a Cifar10 Dataset | |||||
| std::string folder_path = datasets_root_path_ + "/testCifar10Data/"; | |||||
| std::shared_ptr<Dataset> ds = Cifar10(folder_path, "all", RandomSampler(false, 1)); | |||||
| EXPECT_NE(ds, nullptr); | |||||
| // Create an iterator over the result of the above dataset | |||||
| // This will trigger the creation of the Execution Tree and launch it. | |||||
| std::shared_ptr<Iterator> iter = ds->CreateIterator(); | |||||
| EXPECT_NE(iter, nullptr); | |||||
| // Iterate the dataset and get each row | |||||
| std::unordered_map<std::string, std::shared_ptr<Tensor>> row; | |||||
| iter->GetNextRow(&row); | |||||
| // Check original data type of dataset | |||||
| auto image = row["image"]; | |||||
| std::string ori_type = image->type().ToString(); | |||||
| MS_LOG(INFO) << "Original data type: " << ori_type; | |||||
| EXPECT_NE(ori_type.c_str(), "uint8"); | |||||
| // Manually terminate the pipeline | |||||
| iter->Stop(); | |||||
| // Create objects for the tensor ops | |||||
| std::shared_ptr<TensorOperation> type_cast = transforms::TypeCast("uint16"); | |||||
| EXPECT_NE(type_cast, nullptr); | |||||
| // Create a Map operation on ds | |||||
| std::shared_ptr<Dataset> ds2 = ds->Map({type_cast}, {"image"}); | |||||
| EXPECT_NE(ds2, nullptr); | |||||
| // Create an iterator over the result of the above dataset | |||||
| // This will trigger the creation of the Execution Tree and launch it. | |||||
| std::shared_ptr<Iterator> iter2 = ds2->CreateIterator(); | |||||
| EXPECT_NE(iter2, nullptr); | |||||
| // Check current data type of dataset | |||||
| iter2->GetNextRow(&row); | |||||
| auto image2 = row["image"]; | |||||
| std::string cur_type = image2->type().ToString(); | |||||
| MS_LOG(INFO) << "Current data type: " << cur_type; | |||||
| EXPECT_NE(cur_type.c_str(), "uint16"); | |||||
| // Manually terminate the pipeline | |||||
| iter2->Stop(); | |||||
| } | |||||
| TEST_F(MindDataTestPipeline, TestTypeCastFail) { | |||||
| MS_LOG(INFO) << "Doing MindDataTestPipeline-TestTypeCastFail with invalid params."; | |||||
| // incorrect data type | |||||
| std::shared_ptr<TensorOperation> type_cast = transforms::TypeCast("char"); | |||||
| EXPECT_EQ(type_cast, nullptr); | |||||
| } | |||||
| @@ -1313,3 +1313,117 @@ TEST_F(MindDataTestPipeline, TestNormalizeFail) { | |||||
| normalize = mindspore::dataset::api::vision::Normalize({300.0, 115.0, 100.0}, {70.0, 68.0, 71.0}); | normalize = mindspore::dataset::api::vision::Normalize({300.0, 115.0, 100.0}, {70.0, 68.0, 71.0}); | ||||
| EXPECT_EQ(normalize, nullptr); | EXPECT_EQ(normalize, nullptr); | ||||
| } | } | ||||
| TEST_F(MindDataTestPipeline, TestRandomCropDecodeResizeSucess1) { | |||||
| MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropDecodeResize with default params."; | |||||
| // Create an ImageFolder Dataset | |||||
| std::string folder_path = datasets_root_path_ + "/testPK/data/"; | |||||
| std::shared_ptr<Dataset> ds = ImageFolder(folder_path, false, SequentialSampler(0, 2)); | |||||
| EXPECT_NE(ds, nullptr); | |||||
| // Create objects for the tensor ops | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 60}); | |||||
| EXPECT_NE(random_crop_decode_resize, nullptr); | |||||
| // Create a Map operation on ds | |||||
| ds = ds->Map({random_crop_decode_resize}); | |||||
| EXPECT_NE(ds, nullptr); | |||||
| // Create an iterator over the result of the above dataset | |||||
| // This will trigger the creation of the Execution Tree and launch it. | |||||
| std::shared_ptr<Iterator> iter = ds->CreateIterator(); | |||||
| EXPECT_NE(iter, nullptr); | |||||
| // Iterate the dataset and get each row | |||||
| std::unordered_map<std::string, std::shared_ptr<Tensor>> row; | |||||
| iter->GetNextRow(&row); | |||||
| uint64_t i = 0; | |||||
| while (row.size() != 0) { | |||||
| i++; | |||||
| auto image = row["image"]; | |||||
| MS_LOG(INFO) << "Tensor image shape: " << image->shape(); | |||||
| iter->GetNextRow(&row); | |||||
| EXPECT_EQ(image->shape()[0], 50); | |||||
| EXPECT_EQ(image->shape()[1], 60); | |||||
| } | |||||
| EXPECT_EQ(i, 2); | |||||
| // Manually terminate the pipeline | |||||
| iter->Stop(); | |||||
| } | |||||
| TEST_F(MindDataTestPipeline, TestRandomCropDecodeResizeSucess2) { | |||||
| MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropDecodeResize with single size."; | |||||
| // Create an ImageFolder Dataset | |||||
| std::string folder_path = datasets_root_path_ + "/testPK/data/"; | |||||
| std::shared_ptr<Dataset> ds = ImageFolder(folder_path, false, RandomSampler(false, 3)); | |||||
| EXPECT_NE(ds, nullptr); | |||||
| // Create objects for the tensor ops | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({100}); | |||||
| EXPECT_NE(random_crop_decode_resize, nullptr); | |||||
| // Create a Map operation on ds | |||||
| ds = ds->Map({random_crop_decode_resize}); | |||||
| EXPECT_NE(ds, nullptr); | |||||
| // Create an iterator over the result of the above dataset | |||||
| // This will trigger the creation of the Execution Tree and launch it. | |||||
| std::shared_ptr<Iterator> iter = ds->CreateIterator(); | |||||
| EXPECT_NE(iter, nullptr); | |||||
| // Iterate the dataset and get each row | |||||
| std::unordered_map<std::string, std::shared_ptr<Tensor>> row; | |||||
| iter->GetNextRow(&row); | |||||
| uint64_t i = 0; | |||||
| while (row.size() != 0) { | |||||
| i++; | |||||
| auto image = row["image"]; | |||||
| MS_LOG(INFO) << "Tensor image shape: " << image->shape(); | |||||
| iter->GetNextRow(&row); | |||||
| EXPECT_EQ(image->shape()[0], 100); | |||||
| EXPECT_EQ(image->shape()[1], 100); | |||||
| } | |||||
| EXPECT_EQ(i, 3); | |||||
| // Manually terminate the pipeline | |||||
| iter->Stop(); | |||||
| } | |||||
| TEST_F(MindDataTestPipeline, TestRandomCropDecodeResizeFail) { | |||||
| MS_LOG(INFO) << "Doing MindDataTestPipeline-TestRandomCropDecodeResize with invalid params."; | |||||
| // size of size vector is not 1 or 2 | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_1 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 100, 150}); | |||||
| EXPECT_EQ(random_crop_decode_resize_1, nullptr); | |||||
| // incorrect scale vector | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_2 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 50}, {0.5}); | |||||
| EXPECT_EQ(random_crop_decode_resize_2, nullptr); | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_3 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 50}, {0.5, 0.1}); | |||||
| EXPECT_EQ(random_crop_decode_resize_3, nullptr); | |||||
| // incorrect ratio vector | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_4 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 50}, {0.5, 0.6}, {0.9}); | |||||
| EXPECT_EQ(random_crop_decode_resize_4, nullptr); | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_5 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 50}, {0.5, 0.6}, {0.9, 0.1}); | |||||
| EXPECT_EQ(random_crop_decode_resize_5, nullptr); | |||||
| // incorrect max_attempts range | |||||
| std::shared_ptr<TensorOperation> random_crop_decode_resize_6 = | |||||
| mindspore::dataset::api::vision::RandomCropDecodeResize({50, 50}, {0.5, 0.6}, {0.9, 0.9}, | |||||
| mindspore::dataset::InterpolationMode::kLinear, 0); | |||||
| EXPECT_EQ(random_crop_decode_resize_6, nullptr); | |||||
| } | |||||