• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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