Skip to content

Commit b587a7f

Browse files
authored
Merge pull request #11293 from JiayiFeng/update_crop_op
Update crop op
2 parents 259e63d + c7bbfb3 commit b587a7f

File tree

4 files changed

+89
-8
lines changed

4 files changed

+89
-8
lines changed

paddle/fluid/operators/crop_op.cc

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ class CropOp : public framework::OperatorWithKernel {
4848
ctx->SetOutputDim("Out", y_dim);
4949
}
5050
}
51+
52+
framework::OpKernelType GetExpectedKernelType(
53+
const framework::ExecutionContext& ctx) const override {
54+
return framework::OpKernelType(
55+
framework::ToDataType(ctx.Input<framework::LoDTensor>("X")->type()),
56+
ctx.device_context());
57+
}
5158
};
5259

5360
class CropOpMaker : public framework::OpProtoAndCheckerMaker {
@@ -60,13 +67,19 @@ class CropOpMaker : public framework::OpProtoAndCheckerMaker {
6067
"The input used as reference for cropping, "
6168
"which is of the same dimensions as X.")
6269
.AsDispensable();
70+
AddInput("Offsets",
71+
"The input used to describe offsets in runtime, which is a "
72+
"1-D vector whose size equals to the rank of input 'X'. The "
73+
"elements data type must be int.")
74+
.AsDispensable();
6375
AddOutput("Out",
6476
"The output of crop op, "
6577
"which is of the same dimensions as X.");
6678
AddAttr<std::vector<int>>("offsets",
6779
"A list<int> describing offsets to be cropped. "
6880
"The size of offsets list should be the same as "
69-
"the dimension size of input X.");
81+
"the dimension size of input X.")
82+
.SetDefault(std::vector<int>());
7083
AddAttr<std::vector<int>>("shape",
7184
"A list<int> describing the shape of output. "
7285
"The size of shape list should be the same as "
@@ -77,6 +90,17 @@ Crop Operator.
7790
7891
Crop input into output, as specified by offsets and shape.
7992
93+
There are two ways to set the offsets:
94+
1. In runtime: Using the input 'Offsets', which is a Vairbale and can be
95+
output of other operators. This way is suitable for
96+
dynamic offsets.
97+
2. In network configuration: Using the attribute 'offsets', which will be
98+
set in Python configure script. This way is
99+
suitable for fixed offsets.
100+
You CANNOT use these two ways at the same time. An exception will be raised
101+
if input 'Offset' is configured and meanwhile the attribute 'offsets' is
102+
not empty.
103+
80104
There are two ways to set shape:
81105
1. reference input: crop input X into the same shape as reference input.
82106
The dimension of reference input should
@@ -146,6 +170,15 @@ class CropOpGrad : public framework::OperatorWithKernel {
146170
ctx->SetOutputDim(x_grad_name, x_dims);
147171
}
148172
}
173+
174+
framework::OpKernelType GetExpectedKernelType(
175+
const framework::ExecutionContext& ctx) const override {
176+
return framework::OpKernelType(
177+
framework::ToDataType(
178+
ctx.Input<framework::LoDTensor>(framework::GradVarName("Out"))
179+
->type()),
180+
ctx.device_context());
181+
}
149182
};
150183

151184
} // namespace operators

paddle/fluid/operators/crop_op.h

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,37 @@ template <typename T, size_t D, int MajorType = Eigen::RowMajor,
2727
using EigenTensor = framework::EigenTensor<T, D, MajorType, IndexType>;
2828
using framework::Tensor;
2929

30+
static std::vector<int> GetOffsets(const framework::ExecutionContext& ctx) {
31+
std::vector<int> res;
32+
int rank = ctx.Input<Tensor>("X")->dims().size();
33+
if (ctx.HasInput("Offsets")) {
34+
PADDLE_ENFORCE(ctx.Attr<std::vector<int>>("offsets").empty(),
35+
"Input 'Offsets' and attribute 'offsets' should not be used "
36+
"at the same time.");
37+
const auto* offsets_tensor = ctx.Input<Tensor>("Offsets");
38+
PADDLE_ENFORCE_EQ(offsets_tensor->dims().size(), 1);
39+
PADDLE_ENFORCE_EQ(
40+
rank, offsets_tensor->dims()[0],
41+
"Offsets size should be equal to dimension size of input tensor.");
42+
const int* offsets_data;
43+
framework::Tensor cpu_tmp_tensor;
44+
if (platform::is_cpu_place(offsets_tensor->place())) {
45+
offsets_data = offsets_tensor->data<int>();
46+
} else {
47+
framework::TensorCopySync(*offsets_tensor, platform::CPUPlace(),
48+
&cpu_tmp_tensor);
49+
offsets_data = cpu_tmp_tensor.data<int>();
50+
}
51+
res = std::vector<int>(offsets_data, offsets_data + rank);
52+
} else {
53+
res = ctx.Attr<std::vector<int>>("offsets");
54+
PADDLE_ENFORCE_EQ(
55+
rank, res.size(),
56+
"Offsets size should be equal to dimension size of input tensor.");
57+
}
58+
return res;
59+
}
60+
3061
template <typename T>
3162
class CropKernel : public framework::OpKernel<T> {
3263
public:
@@ -37,10 +68,7 @@ class CropKernel : public framework::OpKernel<T> {
3768
T* out_data = out->mutable_data<T>(context.GetPlace());
3869
auto x_stride = framework::stride(x->dims());
3970
auto out_stride = framework::stride(out->dims());
40-
auto offsets = context.Attr<std::vector<int>>("offsets");
41-
PADDLE_ENFORCE_EQ(
42-
x->dims().size(), static_cast<int64_t>(offsets.size()),
43-
"Offsets size should be equal to dimension size of input tensor.");
71+
auto offsets = GetOffsets(context);
4472
int64_t offset = 0;
4573
for (size_t i = 0; i < offsets.size(); ++i) {
4674
offset += (x_stride[i] * offsets[i]);
@@ -56,7 +84,7 @@ void CropGradFunction(const framework::ExecutionContext& context) {
5684
if (d_x != nullptr) {
5785
auto* d_out = context.Input<Tensor>(framework::GradVarName("Out"));
5886
d_x->mutable_data<T>(context.GetPlace());
59-
auto offsets = context.Attr<std::vector<int>>("offsets");
87+
auto offsets = GetOffsets(context);
6088
Eigen::array<std::pair<int, int>, D> paddings;
6189
for (size_t i = 0; i < D; ++i) {
6290
paddings[i].first = offsets[i];

paddle/fluid/operators/random_crop_op.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class RandomCropOp : public framework::OperatorWithKernel {
2020
public:
2121
using framework::OperatorWithKernel::OperatorWithKernel;
2222

23-
protected:
2423
framework::OpKernelType GetExpectedKernelType(
2524
const framework::ExecutionContext& ctx) const override {
2625
return framework::OpKernelType(

python/paddle/fluid/tests/unittests/test_crop_op.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ class TestCropOp(OpTest):
4242
def setUp(self):
4343
self.op_type = "crop"
4444
self.crop_by_input = False
45+
self.offset_by_input = False
4546
self.attrs = {}
4647
self.initTestCase()
47-
self.attrs['offsets'] = self.offsets
4848
if self.crop_by_input:
4949
self.inputs = {
5050
'X': np.random.random(self.x_shape).astype("float32"),
@@ -55,6 +55,10 @@ def setUp(self):
5555
self.inputs = {
5656
'X': np.random.random(self.x_shape).astype("float32"),
5757
}
58+
if self.offset_by_input:
59+
self.inputs['Offsets'] = np.array(self.offsets).astype('int32')
60+
else:
61+
self.attrs['offsets'] = self.offsets
5862
self.outputs = {
5963
'Out': crop(self.inputs['X'], self.offsets, self.crop_shape)
6064
}
@@ -101,5 +105,22 @@ def initTestCase(self):
101105
self.crop_by_input = True
102106

103107

108+
class TestCase5(TestCropOp):
109+
def initTestCase(self):
110+
self.x_shape = (3, 4, 5)
111+
self.crop_shape = [2, 2, 3]
112+
self.offsets = [1, 0, 2]
113+
self.offset_by_input = True
114+
115+
116+
class TestCase6(TestCropOp):
117+
def initTestCase(self):
118+
self.x_shape = (10, 9, 14)
119+
self.crop_shape = [3, 3, 5]
120+
self.offsets = [3, 5, 4]
121+
self.crop_by_input = True
122+
self.offset_by_input = True
123+
124+
104125
if __name__ == '__main__':
105126
unittest.main()

0 commit comments

Comments
 (0)