1 /**
2 * Copyright 2021 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "minddata/dataset/kernels/image/random_crop_op.h"
17 #include <random>
18 #include <tuple>
19 #include "minddata/dataset/kernels/image/image_utils.h"
20 #include "minddata/dataset/util/random.h"
21 #include "minddata/dataset/util/status.h"
22
23 namespace mindspore {
24 namespace dataset {
25 const int32_t RandomCropOp::kDefPadTop = 0;
26 const int32_t RandomCropOp::kDefPadBottom = 0;
27 const int32_t RandomCropOp::kDefPadLeft = 0;
28 const int32_t RandomCropOp::kDefPadRight = 0;
29 const BorderType RandomCropOp::kDefBorderType = BorderType::kConstant;
30 const bool RandomCropOp::kDefPadIfNeeded = false;
31 const uint8_t RandomCropOp::kDefFillR = 0;
32 const uint8_t RandomCropOp::kDefFillG = 0;
33 const uint8_t RandomCropOp::kDefFillB = 0;
34
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)35 RandomCropOp::RandomCropOp(int32_t crop_height, int32_t crop_width, int32_t pad_top, int32_t pad_bottom,
36 int32_t pad_left, int32_t pad_right, bool pad_if_needed, BorderType padding_mode,
37 uint8_t fill_r, uint8_t fill_g, uint8_t fill_b)
38 : crop_height_(crop_height),
39 crop_width_(crop_width),
40 pad_top_(pad_top),
41 pad_bottom_(pad_bottom),
42 pad_left_(pad_left),
43 pad_right_(pad_right),
44 pad_if_needed_(pad_if_needed),
45 border_type_(padding_mode),
46 fill_r_(fill_r),
47 fill_g_(fill_g),
48 fill_b_(fill_b) {
49 rnd_.seed(GetSeed());
50 is_deterministic_ = false;
51 }
52
ImagePadding(const std::shared_ptr<Tensor> & input,std::shared_ptr<Tensor> * 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)53 Status RandomCropOp::ImagePadding(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *pad_image,
54 int32_t *t_pad_top, int32_t *t_pad_bottom, int32_t *t_pad_left, int32_t *t_pad_right,
55 int32_t *padded_image_w, int32_t *padded_image_h, bool *crop_further) {
56 *t_pad_top = pad_top_;
57 *t_pad_bottom = pad_bottom_;
58 *t_pad_left = pad_left_;
59 *t_pad_right = pad_right_;
60
61 constexpr int64_t max_ratio = 3;
62 CHECK_FAIL_RETURN_UNEXPECTED(
63 pad_top_ < input->shape()[0] * max_ratio && pad_bottom_ < input->shape()[0] * max_ratio &&
64 pad_left_ < input->shape()[1] * max_ratio && pad_right_ < input->shape()[1] * max_ratio,
65 "Pad: padding size is three times bigger than the image size, padding top: " + std::to_string(pad_top_) +
66 ", padding bottom: " + std::to_string(pad_bottom_) + ", padding pad_left_: " + std::to_string(pad_left_) +
67 ", padding padding right:" + std::to_string(pad_right_) + ", image shape: " + std::to_string(input->shape()[0]) +
68 ", " + std::to_string(input->shape()[1]));
69
70 RETURN_IF_NOT_OK(
71 Pad(input, pad_image, pad_top_, pad_bottom_, pad_left_, pad_right_, border_type_, fill_r_, fill_g_, fill_b_));
72 CHECK_FAIL_RETURN_UNEXPECTED(
73 (*pad_image)->shape().Size() >= 2,
74 "RandomCrop: invalid shape of image after pad, got rank: " + std::to_string((*pad_image)->shape().Size()));
75
76 *padded_image_h = (*pad_image)->shape()[0];
77 *padded_image_w = (*pad_image)->shape()[1];
78
79 if (*padded_image_h == crop_height_ && *padded_image_w == crop_width_) {
80 *crop_further = false; // no need for further crop
81 return Status::OK();
82 } else if (pad_if_needed_) {
83 // check the dimensions of the image for padding, if we do need padding, then we change the pad values
84 if (*padded_image_h < crop_height_) {
85 RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, crop_height_ - *padded_image_h, crop_height_ - *padded_image_h, 0, 0,
86 border_type_, fill_r_, fill_g_, fill_b_));
87
88 // update pad total above/below
89 t_pad_top += (crop_height_ - *padded_image_h);
90 t_pad_bottom += (crop_height_ - *padded_image_h);
91 }
92 if (*padded_image_w < crop_width_) {
93 RETURN_IF_NOT_OK(Pad(*pad_image, pad_image, 0, 0, crop_width_ - *padded_image_w, crop_width_ - *padded_image_w,
94 border_type_, fill_r_, fill_g_, fill_b_));
95 // update pad total left/right
96 t_pad_left += (crop_width_ - *padded_image_w);
97 t_pad_right += (crop_width_ - *padded_image_w);
98 }
99 *padded_image_h = (*pad_image)->shape()[0];
100 *padded_image_w = (*pad_image)->shape()[1];
101 }
102
103 if (crop_height_ == 0 || crop_width_ == 0) {
104 return Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__,
105 "RandomCrop: invalid crop size, crop width or crop height is not allowed to be zero.");
106 }
107 if (*padded_image_h < crop_height_ || *padded_image_w < crop_width_ || crop_height_ == 0 || crop_width_ == 0) {
108 return Status(StatusCode::kMDShapeMisMatch, __LINE__, __FILE__,
109 "RandomCrop: invalid crop size, crop size is bigger than the image dimensions, got crop height: " +
110 std::to_string(crop_height_) + ", crop width: " + std::to_string(crop_width_));
111 }
112 return Status::OK();
113 }
114
GenRandomXY(int * x,int * y,const int32_t & padded_image_w,const int32_t & padded_image_h)115 void RandomCropOp::GenRandomXY(int *x, int *y, const int32_t &padded_image_w, const int32_t &padded_image_h) {
116 // GenCropPoints for cropping
117 *x = std::uniform_int_distribution<int>(0, padded_image_w - crop_width_)(rnd_);
118 *y = std::uniform_int_distribution<int>(0, padded_image_h - crop_height_)(rnd_);
119 }
120
Compute(const TensorRow & input,TensorRow * output)121 Status RandomCropOp::Compute(const TensorRow &input, TensorRow *output) {
122 IO_CHECK_VECTOR(input, output);
123 if (input.size() != 1) {
124 for (size_t i = 0; i < input.size() - 1; i++) {
125 if (input[i]->Rank() != 2 && input[i]->Rank() != 3) {
126 std::string err_msg =
127 "RandomCropOp: image shape is not <H,W,C> or <H, W>, but got rank:" + std::to_string(input[i]->Rank());
128 RETURN_STATUS_UNEXPECTED(err_msg);
129 }
130 if (input[i]->shape()[0] != input[i + 1]->shape()[0] || input[i]->shape()[1] != input[i + 1]->shape()[1]) {
131 RETURN_STATUS_UNEXPECTED("RandomCropOp: Input images must have the same size.");
132 }
133 }
134 }
135 int x = 0;
136 int y = 0;
137 const int output_count = input.size();
138 output->resize(output_count);
139 for (size_t i = 0; i < input.size(); i++) {
140 RETURN_IF_NOT_OK(ValidateImageRank("RandomCrop", input[i]->shape().Size()));
141 std::shared_ptr<Tensor> pad_image = nullptr;
142 int32_t t_pad_top = 0;
143 int32_t t_pad_bottom = 0;
144 int32_t t_pad_left = 0;
145 int32_t t_pad_right = 0;
146 int32_t padded_image_w = 0;
147 int32_t padded_image_h = 0;
148 bool crop_further = true; // whether image needs further cropping based on new size & requirements
149
150 RETURN_IF_NOT_OK( // error code sent back directly
151 ImagePadding(input[i], &pad_image, &t_pad_top, &t_pad_bottom, &t_pad_left, &t_pad_right, &padded_image_w,
152 &padded_image_h, &crop_further));
153 if (!crop_further) {
154 (*output)[i] = pad_image;
155 continue;
156 }
157 if (i == 0) {
158 GenRandomXY(&x, &y, padded_image_w, padded_image_h);
159 }
160 RETURN_IF_NOT_OK(Crop(pad_image, &(*output)[i], x, y, crop_width_, crop_height_));
161 }
162 return Status::OK();
163 }
164
OutputShape(const std::vector<TensorShape> & inputs,std::vector<TensorShape> & outputs)165 Status RandomCropOp::OutputShape(const std::vector<TensorShape> &inputs, std::vector<TensorShape> &outputs) {
166 RETURN_IF_NOT_OK(TensorOp::OutputShape(inputs, outputs));
167 outputs.clear();
168 TensorShape out = TensorShape{crop_height_, crop_width_};
169 if (inputs[0].Rank() == 2) {
170 (void)outputs.emplace_back(out);
171 }
172 if (inputs[0].Rank() == 3) {
173 (void)outputs.emplace_back(out.AppendDim(inputs[0][2]));
174 }
175 if (!outputs.empty()) {
176 return Status::OK();
177 }
178 return Status(StatusCode::kMDUnexpectedError,
179 "RandomCrop: invalid input shape, expected 2D or 3D input, but got input dimension is:" +
180 std::to_string(inputs[0].Rank()));
181 }
182 } // namespace dataset
183 } // namespace mindspore
184