Caffe2 - C++ API
A deep learning, cross platform ML framework
pad_op.cc
1 #include "caffe2/operators/pad_op.h"
2 
3 #include <algorithm>
4 
5 namespace caffe2 {
6 
7 PadMode StringToPadMode(const string& mode) {
8  if (mode == "constant") {
9  return PadMode::CONSTANT;
10  } else if (mode == "reflect") {
11  return PadMode::REFLECT;
12  } else if (mode == "edge") {
13  return PadMode::EDGE;
14  } else {
15  CAFFE_THROW("Unknown padding mode: " + mode);
16  }
17 }
18 
19 using std::min;
20 using std::max;
21 
22 template <>
23 bool PadImageOp<float, CPUContext>::RunOnDeviceWithOrderNCHW() {
24  auto& X = Input(0);
25  auto* Y = Output(0);
26  int channels = X.dim32(1);
27  int height = X.dim32(2);
28  int width = X.dim32(3);
29  ConvPoolOpBase::SetOutputSize(X, Y, channels);
30 
31  const float* Xdata = X.data<float>();
32  float* Ydata = Y->template mutable_data<float>();
33  // The main loop
34  int padded_height = Y->dim32(2);
35  int padded_width = Y->dim32(3);
36 
37  switch (mode_) {
38  case PadMode::CONSTANT:
39  for (int n = 0; n < X.dim32(0); ++n) {
40  for (int c = 0; c < channels; ++c) {
41  for (int ph = 0; ph < padded_height; ++ph) {
42  for (int pw = 0; pw < padded_width; ++pw) {
43  int h = ph - pad_t();
44  int w = pw - pad_l();
45  Ydata[ph * padded_width + pw] =
46  (h < 0 || w < 0 || h >= height || w >= width)
47  ? value_
48  : Xdata[h * width + w];
49  }
50  }
51  // Do offset.
52  Xdata += height * width;
53  Ydata += padded_height * padded_width;
54  }
55  }
56  break;
57  case PadMode::REFLECT:
58  if (pad_r() >= 0 && pad_t() >= 0 && pad_l() >= 0 && pad_b() >= 0) {
59  for (int n = 0; n < X.dim32(0); ++n) {
60  for (int c = 0; c < channels; ++c) {
61  // Handle the valid region:
62  // i.e. Y[n][c][pad_t:pad_t+h][pad_l:pad_l+w]
63  auto* Ystart = Ydata + pad_t() * padded_width + pad_l();
64  math::CopyMatrix<CPUContext>(
65  sizeof(float),
66  height,
67  width,
68  Xdata,
69  width,
70  Ystart,
71  padded_width,
72  &context_);
73 
74 // Fixup areas where we need to reflect
75 #define X(ph, pw) \
76  int h = ph - pad_t(); \
77  int w = pw - pad_l(); \
78  h = max(h, -h); \
79  h = min(h, 2 * height - h - 2); \
80  w = max(w, -w); \
81  w = min(w, 2 * width - w - 2); \
82  Ydata[ph * padded_width + pw] = Xdata[h * width + w]
83 
84  // Top part
85  for (int ph = 0; ph < pad_t(); ++ph) {
86  for (int pw = 0; pw < padded_width; ++pw) {
87  X(ph, pw);
88  }
89  }
90 
91  // Bottom part
92  for (int ph = padded_height - pad_b(); ph < padded_height; ++ph) {
93  for (int pw = 0; pw < padded_width; ++pw) {
94  X(ph, pw);
95  }
96  }
97 
98  // Interior
99  for (int ph = pad_t(); ph < padded_height - pad_b(); ++ph) {
100  // Left
101  for (int pw = 0; pw < pad_l(); ++pw) {
102  X(ph, pw);
103  }
104  // Right
105  for (int pw = padded_width - pad_r(); pw < padded_width; ++pw) {
106  X(ph, pw);
107  }
108  }
109 #undef X
110 
111  // Do offset.
112  Xdata += height * width;
113  Ydata += padded_height * padded_width;
114  }
115  }
116  } else {
117  for (int n = 0; n < X.dim32(0); ++n) {
118  for (int c = 0; c < channels; ++c) {
119  for (int ph = 0; ph < padded_height; ++ph) {
120  for (int pw = 0; pw < padded_width; ++pw) {
121  int h = ph - pad_t();
122  int w = pw - pad_l();
123  // max(h, -h) does reflection over 0
124  h = max(h, -h);
125  // min(h, 2 * height - h - 2) does reflection over height.
126  h = min(h, 2 * height - h - 2);
127  w = max(w, -w);
128  w = min(w, 2 * width - w - 2);
129  Ydata[ph * padded_width + pw] = Xdata[h * width + w];
130  }
131  }
132  // Do offset.
133  Xdata += height * width;
134  Ydata += padded_height * padded_width;
135  }
136  }
137  }
138  break;
139  case PadMode::EDGE:
140  for (int n = 0; n < X.dim32(0); ++n) {
141  for (int c = 0; c < channels; ++c) {
142  for (int ph = 0; ph < padded_height; ++ph) {
143  for (int pw = 0; pw < padded_width; ++pw) {
144  // Bounds to the right range.
145  int h = min(height - 1, max(ph - pad_t(), 0));
146  int w = min(width - 1, max(pw - pad_l(), 0));
147  Ydata[ph * padded_width + pw] = Xdata[h * width + w];
148  }
149  }
150  // Do offset.
151  Xdata += height * width;
152  Ydata += padded_height * padded_width;
153  }
154  }
155  break;
156  }
157  return true;
158 }
159 
160 template <>
161 bool PadImageOp<float, CPUContext>::RunOnDeviceWithOrderNHWC() {
162  auto& X = Input(0);
163  auto* Y = Output(0);
164  int height = X.dim32(1);
165  int width = X.dim32(2);
166  int channels = X.dim32(3);
167  ConvPoolOpBase::SetOutputSize(X, Y, channels);
168  const float* Xdata = X.data<float>();
169  float* Ydata = Y->template mutable_data<float>();
170 
171  // The main loop
172  int padded_height = Y->dim32(1);
173  int padded_width = Y->dim32(2);
174 
175  switch (mode_) {
176  case PadMode::CONSTANT:
177  for (int n = 0; n < X.dim32(0); ++n) {
178  for (int ph = 0; ph < padded_height; ++ph) {
179  for (int pw = 0; pw < padded_width; ++pw) {
180  int h = ph - pad_t();
181  int w = pw - pad_l();
182  const int pad_index = (ph * padded_width + pw) * channels;
183  if (h < 0 || w < 0 || h >= height || w >= width) {
184  for (int c = 0; c < channels; ++c) {
185  Ydata[pad_index + c] = value_;
186  }
187  } else {
188  const int input_index = (h * width + w) * channels;
189  for (int c = 0; c < channels; ++c) {
190  Ydata[pad_index + c] = Xdata[input_index + c];
191  }
192  }
193  }
194  }
195  // Do offset.
196  Xdata += X.numel() / X.dim32(0);
197  Ydata += Y->numel() / Y->dim32(0);
198  }
199  break;
200  case PadMode::REFLECT:
201  for (int n = 0; n < X.dim32(0); ++n) {
202  for (int ph = 0; ph < padded_height; ++ph) {
203  for (int pw = 0; pw < padded_width; ++pw) {
204  const int pad_index = (ph * padded_width + pw) * channels;
205  int h = ph - pad_t();
206  int w = pw - pad_l();
207  // max(h, -h) does reflection over 0
208  h = max(h, -h);
209  // min(h, 2 * height - h - 2) does reflection over height.
210  h = min(h, 2 * height - h - 2);
211  w = max(w, -w);
212  w = min(w, 2 * width - w - 2);
213  const int input_index = (h * width + w) * channels;
214  for (int c = 0; c < channels; ++c) {
215  Ydata[pad_index + c] = Xdata[input_index + c];
216  }
217  }
218  }
219  // Do offset.
220  Xdata += X.numel() / X.dim32(0);
221  Ydata += Y->numel() / Y->dim32(0);
222  }
223  break;
224  case PadMode::EDGE:
225  for (int n = 0; n < X.dim32(0); ++n) {
226  for (int ph = 0; ph < padded_height; ++ph) {
227  for (int pw = 0; pw < padded_width; ++pw) {
228  const int pad_index = (ph * padded_width + pw) * channels;
229  int h = min(height - 1, max(ph - pad_t(), 0));
230  int w = min(width - 1, max(pw - pad_l(), 0));
231  const int input_index = (h * width + w) * channels;
232  for (int c = 0; c < channels; ++c) {
233  Ydata[pad_index + c] = Xdata[input_index + c];
234  }
235  }
236  }
237  // Do offset.
238  Xdata += X.numel() / X.dim32(0);
239  Ydata += Y->numel() / Y->dim32(0);
240  }
241  break;
242  }
243  return true;
244 }
245 
246 template <>
247 bool PadImageGradientOp<float, CPUContext>::RunOnDeviceWithOrderNCHW() {
248  auto& dY = Input(0);
249 
250  auto* dX = Output(
251  0,
252  {dY.dim32(0),
253  dY.dim32(1),
254  dY.dim32(2) - pad_t() - pad_b(),
255  dY.dim32(3) - pad_l() - pad_r()},
256  at::dtype<float>());
257  int padded_height = dY.dim32(2);
258  int padded_width = dY.dim32(3);
259  int channels = dX->dim32(1);
260  int height = dX->dim32(2);
261  int width = dX->dim32(3);
262 
263  const float* dYdata = dY.data<float>();
264  float* dXdata = dX->template mutable_data<float>();
265  math::Set<float, CPUContext>(dX->numel(), 0, dXdata, &context_);
266  // The main loop
267  switch (mode_) {
268  case PadMode::CONSTANT:
269  for (int n = 0; n < dY.dim32(0); ++n) {
270  for (int c = 0; c < channels; ++c) {
271  for (int ph = 0; ph < padded_height; ++ph) {
272  for (int pw = 0; pw < padded_width; ++pw) {
273  int h = ph - pad_t();
274  int w = pw - pad_l();
275  if (!(h < 0 || w < 0 || h >= height || w >= width)) {
276  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
277  }
278  }
279  }
280  // Do offset.
281  dXdata += height * width;
282  dYdata += padded_height * padded_width;
283  }
284  }
285  break;
286  case PadMode::REFLECT:
287  for (int n = 0; n < dY.dim32(0); ++n) {
288  for (int c = 0; c < channels; ++c) {
289  for (int ph = 0; ph < padded_height; ++ph) {
290  for (int pw = 0; pw < padded_width; ++pw) {
291  int h = ph - pad_t();
292  int w = pw - pad_l();
293  // max(h, -h) does reflection over 0
294  h = max(h, -h);
295  // min(h, 2 * height - h - 2) does reflection over height.
296  h = min(h, 2 * height - h - 2);
297  w = max(w, -w);
298  w = min(w, 2 * width - w - 2);
299  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
300  }
301  }
302  // Do offset.
303  dXdata += height * width;
304  dYdata += padded_height * padded_width;
305  }
306  }
307  break;
308  case PadMode::EDGE:
309  for (int n = 0; n < dY.dim32(0); ++n) {
310  for (int c = 0; c < channels; ++c) {
311  for (int ph = 0; ph < padded_height; ++ph) {
312  for (int pw = 0; pw < padded_width; ++pw) {
313  int h = min(height - 1, max(ph - pad_t(), 0));
314  int w = min(width - 1, max(pw - pad_l(), 0));
315  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
316  }
317  }
318  // Do offset.
319  dXdata += height * width;
320  dYdata += padded_height * padded_width;
321  }
322  }
323  break;
324  }
325  return true;
326 }
327 
328 template <>
329 bool PadImageGradientOp<float, CPUContext>::RunOnDeviceWithOrderNHWC() {
330  auto& dY = Input(0);
331 
332  auto* dX = Output(
333  0,
334  {dY.dim32(0),
335  dY.dim32(1) - pad_t() - pad_b(),
336  dY.dim32(2) - pad_l() - pad_r(),
337  dY.dim32(3)},
338  at::dtype<float>());
339  int padded_height = dY.dim32(1);
340  int padded_width = dY.dim32(2);
341  int channels = dY.dim32(3);
342  int height = dX->dim32(1);
343  int width = dX->dim32(2);
344 
345  const float* dYdata = dY.data<float>();
346  float* dXdata = dX->template mutable_data<float>();
347  math::Set<float, CPUContext>(dX->numel(), 0, dXdata, &context_);
348 
349  switch (mode_) {
350  case PadMode::CONSTANT:
351  for (int n = 0; n < dY.dim32(0); ++n) {
352  for (int ph = 0; ph < padded_height; ++ph) {
353  for (int pw = 0; pw < padded_width; ++pw) {
354  int h = ph - pad_t();
355  int w = pw - pad_l();
356  const int pad_index = (ph * padded_width + pw) * channels;
357  if (!(h < 0 || w < 0 || h >= height || w >= width)) {
358  const int input_index = (h * width + w) * channels;
359  for (int c = 0; c < channels; ++c) {
360  dXdata[input_index + c] += dYdata[pad_index + c];
361  }
362  }
363  }
364  }
365  // Do offset.
366  dXdata += dX->numel() / dX->dim32(0);
367  dYdata += dY.numel() / dY.dim32(0);
368  }
369  break;
370  case PadMode::REFLECT:
371  for (int n = 0; n < dY.dim32(0); ++n) {
372  for (int ph = 0; ph < padded_height; ++ph) {
373  for (int pw = 0; pw < padded_width; ++pw) {
374  const int pad_index = (ph * padded_width + pw) * channels;
375  int h = ph - pad_t();
376  int w = pw - pad_l();
377  // max(h, -h) does reflection over 0
378  h = max(h, -h);
379  // min(h, 2 * height - h - 2) does reflection over height.
380  h = min(h, 2 * height - h - 2);
381  w = max(w, -w);
382  w = min(w, 2 * width - w - 2);
383  const int input_index = (h * width + w) * channels;
384  for (int c = 0; c < channels; ++c) {
385  dXdata[input_index + c] += dYdata[pad_index + c];
386  }
387  }
388  }
389  // Do offset.
390  dXdata += dX->numel() / dX->dim32(0);
391  dYdata += dY.numel() / dY.dim32(0);
392  }
393  break;
394  case PadMode::EDGE:
395  for (int n = 0; n < dY.dim32(0); ++n) {
396  for (int ph = 0; ph < padded_height; ++ph) {
397  for (int pw = 0; pw < padded_width; ++pw) {
398  const int pad_index = (ph * padded_width + pw) * channels;
399  // Bounds to the right range.
400  int h = min(height - 1, max(ph - pad_t(), 0));
401  int w = min(width - 1, max(pw - pad_l(), 0));
402  const int input_index = (h * width + w) * channels;
403  for (int c = 0; c < channels; ++c) {
404  dXdata[input_index + c] += dYdata[pad_index + c];
405  }
406  }
407  }
408  // Do offset.
409  dXdata += dX->numel() / dX->dim32(0);
410  dYdata += dY.numel() / dY.dim32(0);
411  }
412  break;
413  }
414  return true;
415 }
416 
417 template <>
418 std::vector<TensorShape> PadImageOp<float, CPUContext>::PadTensorInference(
419  const OperatorDef& def,
420  const vector<TensorShape>& in) {
421  return ConvPoolOpBase::TensorInferenceForPool(def, in);
422 }
423 
424 REGISTER_CPU_OPERATOR(PadImage, PadImageOp<float, CPUContext>);
425 REGISTER_CPU_GRADIENT_OPERATOR(
426  PadImageGradient,
427  PadImageGradientOp<float, CPUContext>);
428 
429 OPERATOR_SCHEMA(PadImage)
430  .NumInputs(1)
431  .NumOutputs(1)
432  .TensorInferenceFunction(PadImageOp<float, CPUContext>::PadTensorInference)
433  .SetDoc(R"DOC(
434 PadImage pads values around the boundary of an image according to the pad
435 values and stride sizes defined by the ConvPoolOpBase operator.
436  )DOC")
437  .Input(
438  0,
439  "X",
440  "Input data tensor from the previous operator; dimensions "
441  "depend on whether the NCHW or NHWC operators are being used. For example, "
442  "in the former, the input has size (N x C x H x W), where N is the batch "
443  "size, C is the number of channels, and H and W are the height and the width "
444  "of the data. The corresponding permutation of dimensions is used in the "
445  "latter case. ")
446  .Output(
447  0,
448  "Y",
449  "Output data tensor from padding the H and W dimensions on "
450  "the tensor. Dimensions will vary based on various pad and stride "
451  "sizes.");
453 GRADIENT_OPERATOR_SCHEMA(PadImageGradient).NumInputs(1).NumOutputs(1);
454 
455 class GetPadImageGradient : public GradientMakerBase {
456  using GradientMakerBase::GradientMakerBase;
457  vector<OperatorDef> GetGradientDefs() override {
458  return SingleGradientDef(
459  "PadImageGradient", "", vector<string>{GO(0)}, vector<string>{GI(0)});
460  }
461 };
462 REGISTER_GRADIENT(PadImage, GetPadImageGradient);
463 
464 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
static vector< OperatorDef > SingleGradientDef(const Args &...args)
a helper function to allow one to create one single operator def, which is usually the case for many ...