blob: 5cecd866a4d40761d5d47e4a620f3dc8eb864c3b [file] [log] [blame]
/*!
* Copyright (c) 2015 by Contributors
* \file pad.cc
* \brief
* \author Sebastian Bodenstein
*/
#include "./pad-inl.h"
namespace mshadow {
////////////////////////////////////////////////////////////////////////////////
// Special Case: 2d image (so only pad width + height)
// Case 1: Edge Padding (or Replication Padding)
// single_image_2d_edge adapted from Torch
// https://github.com/torch/nn/blob/master/lib/THNN/generic/SpatialReplicationPadding.c
template <typename DType>
void single_image_edge(const Tensor<cpu, 3, DType> dst,
const Tensor<cpu, 3, DType> src, mxnet::TShape pad) {
const int nslices = src.size(0);
const int iheight = src.size(1);
const int iwidth = src.size(2);
const int oheight = dst.size(1);
const int owidth = dst.size(2);
const int pad_t = pad[4];
const int pad_l = pad[6];
int iStartX = std::max(0, -pad_l);
int iStartY = std::max(0, -pad_t);
int oStartX = std::max(0, pad_l);
int oStartY = std::max(0, pad_t);
int k, ip_x, ip_y;
#pragma omp parallel for private(k, ip_x, ip_y)
for (k = 0; k < nslices; k++) {
int i, j;
for (i = 0; i < oheight; i++) {
for (j = 0; j < owidth; j++) {
if (j < pad_l) {
ip_x = pad_l;
} else if (j >= pad_l && j < iwidth + pad_l) {
ip_x = j;
} else {
ip_x = iwidth + pad_l - 1;
}
ip_x = ip_x - oStartX + iStartX;
if (i < pad_t) {
ip_y = pad_t;
} else if (i >= pad_t && i < iheight + pad_t) {
ip_y = i;
} else {
ip_y = iheight + pad_t - 1;
}
ip_y = ip_y - oStartY + iStartY;
DType *dest_p = dst.dptr_ + k * owidth * oheight + i * owidth + j;
DType *src_p = src.dptr_ + k * iwidth * iheight + ip_y * iwidth + ip_x;
*dest_p = *src_p;
}
}
}
}
template <typename DType>
void single_image_edge_grad(const Tensor<cpu, 3, DType> &grad_in,
const Tensor<cpu, 3, DType> grad_out,
mxnet::TShape pad) {
const int nslices = grad_in.size(0);
const int iheight = grad_in.size(1);
const int iwidth = grad_in.size(2);
const int oheight = grad_out.size(1);
const int owidth = grad_out.size(2);
const int pad_t = pad[4];
const int pad_l = pad[6];
int iStartX = std::max(0, -pad_l);
int iStartY = std::max(0, -pad_t);
int oStartX = std::max(0, pad_l);
int oStartY = std::max(0, pad_t);
int k, ip_x, ip_y;
#pragma omp parallel for private(k, ip_x, ip_y)
for (k = 0; k < nslices; k++) {
int i, j;
for (i = 0; i < oheight; i++) {
for (j = 0; j < owidth; j++) {
if (j < pad_l) {
ip_x = pad_l;
} else if (j >= pad_l && j < iwidth + pad_l) {
ip_x = j;
} else {
ip_x = iwidth + pad_l - 1;
}
ip_x = ip_x - oStartX + iStartX;
if (i < pad_t) {
ip_y = pad_t;
} else if (i >= pad_t && i < iheight + pad_t) {
ip_y = i;
} else {
ip_y = iheight + pad_t - 1;
}
ip_y = ip_y - oStartY + iStartY;
DType *src_p = grad_out.dptr_ + k * owidth * oheight + i * owidth + j;
DType *dest_p =
grad_in.dptr_ + k * iwidth * iheight + ip_y * iwidth + ip_x;
*dest_p += *src_p;
}
}
}
}
// Case 2: Zero Padding
template <typename DType>
void single_image_constant(const Tensor<cpu, 3, DType> &dst,
const Tensor<cpu, 3, DType> src, mxnet::TShape pad,
DType constant_value) {
const int pad_t = pad[4];
const int pad_l = pad[6];
int c, w, h;
#pragma omp parallel for private(c, w, h)
for (c = 0; c < dst.size(0); ++c) {
for (h = 0; h < dst.size(1); ++h) {
for (w = 0; w < dst.size(2); ++w) {
if ((w < pad_l) || (h < pad_t) || (h >= (src.size(1) + pad_t)) ||
(w >= (src.size(2) + pad_l))) {
dst[c][h][w] = constant_value;
} else {
dst[c][h][w] = src[c][h - pad_t][w - pad_l];
}
}
}
}
}
template <typename DType>
void single_image_constant_grad(const Tensor<cpu, 3, DType> &in_grad,
const Tensor<cpu, 3, DType> out_grad,
mxnet::TShape pad) {
const int pad_t = pad[4];
const int pad_l = pad[6];
int c, h, w;
#pragma omp parallel for private(c, w, h)
for (c = 0; c < in_grad.size(0); ++c) {
for (h = 0; h < in_grad.size(1); ++h) {
for (w = 0; w < in_grad.size(2); ++w) {
in_grad[c][h][w] += out_grad[c][h + pad_t][w + pad_l];
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Special Case: 3d image (so only pad width + height + depth)
// Case 1: Edge Padding (or Replication Padding)
// single_image_3d_edge adapted from Torch
// https://github.com/torch/nn/blob/master/lib/THNN/generic/VolumetricReplicationPadding.c
template <typename DType>
void single_image_edge(const Tensor<cpu, 4, DType> dst,
const Tensor<cpu, 4, DType> src, mxnet::TShape pad) {
const int nslices = src.size(0);
const int idepth = src.size(1);
const int iheight = src.size(2);
const int iwidth = src.size(3);
const int odepth = dst.size(1);
const int oheight = dst.size(2);
const int owidth = dst.size(3);
const int pad_f = pad[4];
const int pad_t = pad[6];
const int pad_l = pad[8];
int iStartX = std::max(0, -pad_l);
int iStartY = std::max(0, -pad_t);
int iStartZ = std::max(0, -pad_f);
int oStartX = std::max(0, pad_l);
int oStartY = std::max(0, pad_t);
int oStartZ = std::max(0, pad_f);
int k, ip_x, ip_y, ip_z;
#pragma omp parallel for private(k, ip_x, ip_y, ip_z)
for (k = 0; k < nslices; k++) {
int i, j, z;
for (z = 0; z < odepth; z++) {
for (i = 0; i < oheight; i++) {
for (j = 0; j < owidth; j++) {
if (j < pad_l) {
ip_x = pad_l;
} else if (j >= pad_l && j < iwidth + pad_l) {
ip_x = j;
} else {
ip_x = iwidth + pad_l - 1;
}
ip_x = ip_x - oStartX + iStartX;
if (i < pad_t) {
ip_y = pad_t;
} else if (i >= pad_t && i < iheight + pad_t) {
ip_y = i;
} else {
ip_y = iheight + pad_t - 1;
}
ip_y = ip_y - oStartY + iStartY;
if (z < pad_f) {
ip_z = pad_f;
} else if (z >= pad_f && z < idepth + pad_f) {
ip_z = z;
} else {
ip_z = idepth + pad_f - 1;
}
ip_z = ip_z - oStartZ + iStartZ;
DType *dest_p = dst.dptr_ + k * owidth * oheight * odepth +
z * owidth * oheight + i * owidth + j;
DType *src_p = src.dptr_ + k * iwidth * iheight * idepth +
ip_z * iwidth * iheight + ip_y * iwidth + ip_x;
*dest_p = *src_p;
}
}
}
}
}
template <typename DType>
void single_image_edge_grad(const Tensor<cpu, 4, DType> &grad_in,
const Tensor<cpu, 4, DType> grad_out,
mxnet::TShape pad) {
const int nslices = grad_in.size(0);
const int idepth = grad_in.size(1);
const int iheight = grad_in.size(2);
const int iwidth = grad_in.size(3);
const int odepth = grad_out.size(1);
const int oheight = grad_out.size(2);
const int owidth = grad_out.size(3);
const int pad_f = pad[4];
const int pad_t = pad[6];
const int pad_l = pad[8];
int iStartX = std::max(0, -pad_l);
int iStartY = std::max(0, -pad_t);
int iStartZ = std::max(0, -pad_f);
int oStartX = std::max(0, pad_l);
int oStartY = std::max(0, pad_t);
int oStartZ = std::max(0, pad_f);
int k, ip_x, ip_y, ip_z;
#pragma omp parallel for private(k, ip_x, ip_y, ip_z)
for (k = 0; k < nslices; k++) {
int i, j, z;
for (z = 0; z < odepth; z++) {
for (i = 0; i < oheight; i++) {
for (j = 0; j < owidth; j++) {
if (j < pad_l) {
ip_x = pad_l;
} else if (j >= pad_l && j < iwidth + pad_l) {
ip_x = j;
} else {
ip_x = iwidth + pad_l - 1;
}
ip_x = ip_x - oStartX + iStartX;
if (i < pad_t) {
ip_y = pad_t;
} else if (i >= pad_t && i < iheight + pad_t) {
ip_y = i;
} else {
ip_y = iheight + pad_t - 1;
}
ip_y = ip_y - oStartY + iStartY;
if (z < pad_f) {
ip_z = pad_f;
} else if (z >= pad_f && z < idepth + pad_f) {
ip_z = z;
} else {
ip_z = idepth + pad_f - 1;
}
ip_z = ip_z - oStartZ + iStartZ;
DType *src_p = grad_out.dptr_ + k * owidth * oheight * odepth +
z * owidth * oheight + i * owidth + j;
DType *dest_p = grad_in.dptr_ + k * iwidth * iheight * idepth +
ip_z * iwidth * iheight + ip_y * iwidth + ip_x;
*dest_p += *src_p;
}
}
}
}
}
// Case 2: Zero Padding
template <typename DType>
void single_image_constant(const Tensor<cpu, 4, DType> &dst,
const Tensor<cpu, 4, DType> src, mxnet::TShape pad,
DType constant_value) {
const int pad_f = pad[4];
const int pad_t = pad[6];
const int pad_l = pad[8];
int c, d, w, h;
#pragma omp parallel for private(c, d, w, h)
for (c = 0; c < dst.size(0); ++c) {
for (d = 0; d < dst.size(1); ++d) {
for (h = 0; h < dst.size(2); ++h) {
for (w = 0; w < dst.size(3); ++w) {
if ((w < pad_l) || (h < pad_t) || (d < pad_f) ||
(d >= (src.size(1) + pad_f)) || (h >= (src.size(2) + pad_t)) ||
(w >= (src.size(3) + pad_l))) {
dst[c][d][h][w] = constant_value;
} else {
dst[c][d][h][w] = src[c][d - pad_f][h - pad_t][w - pad_l];
}
}
}
}
}
}
template <typename DType>
void single_image_constant_grad(const Tensor<cpu, 4, DType> &in_grad,
const Tensor<cpu, 4, DType> out_grad,
mxnet::TShape pad) {
const int pad_f = pad[4];
const int pad_t = pad[6];
const int pad_l = pad[8];
int c, d, w, h;
#pragma omp parallel for private(c, d, w, h)
for (c = 0; c < in_grad.size(0); ++c) {
for (d = 0; d < in_grad.size(1); ++d) {
for (h = 0; h < in_grad.size(2); ++h) {
for (w = 0; w < in_grad.size(3); ++w) {
in_grad[c][d][h][w] += out_grad[c][d + pad_f][h + pad_t][w + pad_l];
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Interface to 2d and 3d image pad methods
template <int dim, typename DType>
void pad_image(const Tensor<cpu, dim, DType> &dst,
const Tensor<cpu, dim, DType> src, mxnet::TShape pad, int mode,
DType constant_value) {
for (index_t n = 0; n < dst.size(0); ++n) {
switch (mode) {
case mxnet::op::pad_enum::kEdge:
single_image_edge(dst[n], src[n], pad);
break;
case mxnet::op::pad_enum::kConstant:
single_image_constant(dst[n], src[n], pad, constant_value);
break;
}
}
}
template <int dim, typename DType>
void pad_image_grad(const Tensor<cpu, dim, DType> &in_grad,
const Tensor<cpu, dim, DType> out_grad, mxnet::TShape pad,
int mode) {
for (index_t n = 0; n < in_grad.size(0); ++n) {
switch (mode) {
case mxnet::op::pad_enum::kEdge:
single_image_edge_grad(in_grad[n], out_grad[n], pad);
break;
case mxnet::op::pad_enum::kConstant:
single_image_constant_grad(in_grad[n], out_grad[n], pad);
break;
}
}
}
} // namespace mshadow
namespace mxnet {
namespace op {
template <>
Operator *CreateOp<cpu>(PadParam param, int dtype) {
Operator *op = NULL;
MSHADOW_REAL_TYPE_SWITCH(dtype, DType, { op = new PadOp<cpu, DType>(param); })
return op;
}
// DO_BIND_DISPATCH comes from operator_common.h
Operator *PadProp::CreateOperatorEx(Context ctx, std::vector<TShape> *in_shape,
std::vector<int> *in_type) const {
std::vector<TShape> out_shape, aux_shape;
std::vector<int> out_type, aux_type;
CHECK(InferType(in_type, &out_type, &aux_type));
CHECK(InferShape(in_shape, &out_shape, &aux_shape));
DO_BIND_DISPATCH(CreateOp, param_, (*in_type)[0]);
}
DMLC_REGISTER_PARAMETER(PadParam);
MXNET_REGISTER_OP_PROPERTY(Pad, PadProp)
.describe(R"code(Pad an array.
Only supports 4-D and 5-D input array.
)code" ADD_FILELINE)
.add_argument("data", "ndarray-or-symbol", "An n-dimensional input tensor.")
.add_arguments(PadParam::__FIELDS__());
NNVM_REGISTER_OP(Pad).add_alias("pad");
} // namespace op
} // namespace mxnet