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->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->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.size() / X.dim32(0);
197  Ydata += Y->size() / 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.size() / X.dim32(0);
221  Ydata += Y->size() / 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.size() / X.dim32(0);
239  Ydata += Y->size() / 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  auto* dX = Output(0);
250  dX->Resize(
251  dY.dim32(0),
252  dY.dim32(1),
253  dY.dim32(2) - pad_t() - pad_b(),
254  dY.dim32(3) - pad_l() - pad_r());
255  int padded_height = dY.dim32(2);
256  int padded_width = dY.dim32(3);
257  int channels = dX->dim32(1);
258  int height = dX->dim32(2);
259  int width = dX->dim32(3);
260 
261  const float* dYdata = dY.data<float>();
262  float* dXdata = dX->mutable_data<float>();
263  math::Set<float, CPUContext>(dX->size(), 0, dXdata, &context_);
264  // The main loop
265  switch (mode_) {
266  case PadMode::CONSTANT:
267  for (int n = 0; n < dY.dim32(0); ++n) {
268  for (int c = 0; c < channels; ++c) {
269  for (int ph = 0; ph < padded_height; ++ph) {
270  for (int pw = 0; pw < padded_width; ++pw) {
271  int h = ph - pad_t();
272  int w = pw - pad_l();
273  if (!(h < 0 || w < 0 || h >= height || w >= width)) {
274  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
275  }
276  }
277  }
278  // Do offset.
279  dXdata += height * width;
280  dYdata += padded_height * padded_width;
281  }
282  }
283  break;
284  case PadMode::REFLECT:
285  for (int n = 0; n < dY.dim32(0); ++n) {
286  for (int c = 0; c < channels; ++c) {
287  for (int ph = 0; ph < padded_height; ++ph) {
288  for (int pw = 0; pw < padded_width; ++pw) {
289  int h = ph - pad_t();
290  int w = pw - pad_l();
291  // max(h, -h) does reflection over 0
292  h = max(h, -h);
293  // min(h, 2 * height - h - 2) does reflection over height.
294  h = min(h, 2 * height - h - 2);
295  w = max(w, -w);
296  w = min(w, 2 * width - w - 2);
297  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
298  }
299  }
300  // Do offset.
301  dXdata += height * width;
302  dYdata += padded_height * padded_width;
303  }
304  }
305  break;
306  case PadMode::EDGE:
307  for (int n = 0; n < dY.dim32(0); ++n) {
308  for (int c = 0; c < channels; ++c) {
309  for (int ph = 0; ph < padded_height; ++ph) {
310  for (int pw = 0; pw < padded_width; ++pw) {
311  int h = min(height - 1, max(ph - pad_t(), 0));
312  int w = min(width - 1, max(pw - pad_l(), 0));
313  dXdata[h * width + w] += dYdata[ph * padded_width + pw];
314  }
315  }
316  // Do offset.
317  dXdata += height * width;
318  dYdata += padded_height * padded_width;
319  }
320  }
321  break;
322  }
323  return true;
324 }
325 
326 template <>
327 bool PadImageGradientOp<float, CPUContext>::RunOnDeviceWithOrderNHWC() {
328  auto& dY = Input(0);
329  auto* dX = Output(0);
330  dX->Resize(
331  dY.dim32(0),
332  dY.dim32(1) - pad_t() - pad_b(),
333  dY.dim32(2) - pad_l() - pad_r(),
334  dY.dim32(3));
335  int padded_height = dY.dim32(1);
336  int padded_width = dY.dim32(2);
337  int channels = dY.dim32(3);
338  int height = dX->dim32(1);
339  int width = dX->dim32(2);
340 
341  const float* dYdata = dY.data<float>();
342  float* dXdata = dX->mutable_data<float>();
343  math::Set<float, CPUContext>(dX->size(), 0, dXdata, &context_);
344 
345  switch (mode_) {
346  case PadMode::CONSTANT:
347  for (int n = 0; n < dY.dim32(0); ++n) {
348  for (int ph = 0; ph < padded_height; ++ph) {
349  for (int pw = 0; pw < padded_width; ++pw) {
350  int h = ph - pad_t();
351  int w = pw - pad_l();
352  const int pad_index = (ph * padded_width + pw) * channels;
353  if (!(h < 0 || w < 0 || h >= height || w >= width)) {
354  const int input_index = (h * width + w) * channels;
355  for (int c = 0; c < channels; ++c) {
356  dXdata[input_index + c] += dYdata[pad_index + c];
357  }
358  }
359  }
360  }
361  // Do offset.
362  dXdata += dX->size() / dX->dim32(0);
363  dYdata += dY.size() / dY.dim32(0);
364  }
365  break;
366  case PadMode::REFLECT:
367  for (int n = 0; n < dY.dim32(0); ++n) {
368  for (int ph = 0; ph < padded_height; ++ph) {
369  for (int pw = 0; pw < padded_width; ++pw) {
370  const int pad_index = (ph * padded_width + pw) * channels;
371  int h = ph - pad_t();
372  int w = pw - pad_l();
373  // max(h, -h) does reflection over 0
374  h = max(h, -h);
375  // min(h, 2 * height - h - 2) does reflection over height.
376  h = min(h, 2 * height - h - 2);
377  w = max(w, -w);
378  w = min(w, 2 * width - w - 2);
379  const int input_index = (h * width + w) * channels;
380  for (int c = 0; c < channels; ++c) {
381  dXdata[input_index + c] += dYdata[pad_index + c];
382  }
383  }
384  }
385  // Do offset.
386  dXdata += dX->size() / dX->dim32(0);
387  dYdata += dY.size() / dY.dim32(0);
388  }
389  break;
390  case PadMode::EDGE:
391  for (int n = 0; n < dY.dim32(0); ++n) {
392  for (int ph = 0; ph < padded_height; ++ph) {
393  for (int pw = 0; pw < padded_width; ++pw) {
394  const int pad_index = (ph * padded_width + pw) * channels;
395  // Bounds to the right range.
396  int h = min(height - 1, max(ph - pad_t(), 0));
397  int w = min(width - 1, max(pw - pad_l(), 0));
398  const int input_index = (h * width + w) * channels;
399  for (int c = 0; c < channels; ++c) {
400  dXdata[input_index + c] += dYdata[pad_index + c];
401  }
402  }
403  }
404  // Do offset.
405  dXdata += dX->size() / dX->dim32(0);
406  dYdata += dY.size() / dY.dim32(0);
407  }
408  break;
409  }
410  return true;
411 }
412 
413 template <>
414 std::vector<TensorShape> PadImageOp<float, CPUContext>::PadTensorInference(
415  const OperatorDef& def,
416  const vector<TensorShape>& in) {
417  return ConvPoolOpBase::TensorInferenceForPool(def, in);
418 }
419 
420 REGISTER_CPU_OPERATOR(PadImage, PadImageOp<float, CPUContext>);
421 REGISTER_CPU_OPERATOR(PadImageGradient, PadImageGradientOp<float, CPUContext>);
422 
423 OPERATOR_SCHEMA(PadImage)
424  .NumInputs(1)
425  .NumOutputs(1)
426  .TensorInferenceFunction(PadImageOp<float, CPUContext>::PadTensorInference)
427  .SetDoc(R"DOC(
428 PadImage pads values around the boundary of an image according to the pad
429 values and stride sizes defined by the ConvPoolOpBase operator.
430  )DOC")
431  .Input(
432  0,
433  "X",
434  "Input data tensor from the previous operator; dimensions "
435  "depend on whether the NCHW or NHWC operators are being used. For example, "
436  "in the former, the input has size (N x C x H x W), where N is the batch "
437  "size, C is the number of channels, and H and W are the height and the width "
438  "of the data. The corresponding permutation of dimensions is used in the "
439  "latter case. ")
440  .Output(
441  0,
442  "Y",
443  "Output data tensor from padding the H and W dimensions on "
444  "the tensor. Dimensions will vary based on various pad and stride "
445  "sizes.");
447 OPERATOR_SCHEMA(PadImageGradient).NumInputs(1).NumOutputs(1);
448 
449 class GetPadImageGradient : public GradientMakerBase {
450  using GradientMakerBase::GradientMakerBase;
451  vector<OperatorDef> GetGradientDefs() override {
452  return SingleGradientDef(
453  "PadImageGradient", "", vector<string>{GO(0)}, vector<string>{GI(0)});
454  }
455 };
456 REGISTER_GRADIENT(PadImage, GetPadImageGradient);
457 
458 } // namespace caffe2
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
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 ...