You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
7.5 KiB
223 lines
7.5 KiB
/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License. */
|
|
|
|
#include "paddle/fluid/operators/crop_op.h"
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace paddle {
|
|
namespace operators {
|
|
|
|
using framework::Tensor;
|
|
|
|
class CropOp : public framework::OperatorWithKernel {
|
|
public:
|
|
using framework::OperatorWithKernel::OperatorWithKernel;
|
|
|
|
void InferShape(framework::InferShapeContext* ctx) const override {
|
|
OP_INOUT_CHECK(ctx->HasInput("X"), "Input", "X", "Crop");
|
|
OP_INOUT_CHECK(ctx->HasOutput("Out"), "Output", "Out", "Crop");
|
|
auto x_dim = ctx->GetInputDim("X");
|
|
if (!ctx->HasInput("Y")) {
|
|
auto shape = ctx->Attrs().Get<std::vector<int>>("shape");
|
|
PADDLE_ENFORCE_EQ(
|
|
int64_t(shape.size()), x_dim.size(),
|
|
platform::errors::InvalidArgument(
|
|
"The number of elements (%d) of CropOp's "
|
|
"'shape' attribute should be equal to the number of dimensions "
|
|
"(%d) of the Input(X).",
|
|
shape.size(), x_dim.size()));
|
|
std::vector<int64_t> tensor_shape(shape.size());
|
|
for (size_t i = 0; i < shape.size(); ++i) {
|
|
tensor_shape[i] = static_cast<int64_t>(shape[i]);
|
|
}
|
|
ctx->SetOutputDim("Out", framework::make_ddim(tensor_shape));
|
|
} else {
|
|
auto y_dim = ctx->GetInputDim("Y");
|
|
PADDLE_ENFORCE_EQ(framework::arity(x_dim), framework::arity(y_dim),
|
|
platform::errors::InvalidArgument(
|
|
"The number of dimensions (%d) of CropOp's input(X)"
|
|
" must be equal to that (%d) of input(Y).",
|
|
framework::arity(x_dim), framework::arity(y_dim)));
|
|
ctx->SetOutputDim("Out", y_dim);
|
|
}
|
|
}
|
|
|
|
framework::OpKernelType GetExpectedKernelType(
|
|
const framework::ExecutionContext& ctx) const override {
|
|
return framework::OpKernelType(
|
|
OperatorWithKernel::IndicateVarDataType(ctx, "X"),
|
|
ctx.device_context());
|
|
}
|
|
};
|
|
|
|
class CropOpMaker : public framework::OpProtoAndCheckerMaker {
|
|
public:
|
|
void Make() override {
|
|
AddInput("X",
|
|
"The input of pad op. "
|
|
"The input should be a k-D tensor(k > 0 and k < 7).");
|
|
AddInput("Y",
|
|
"The input used as reference for cropping, "
|
|
"which is of the same dimensions as X.")
|
|
.AsDispensable();
|
|
AddInput("Offsets",
|
|
"The input used to describe offsets in runtime, which is a "
|
|
"1-D vector whose size equals to the rank of input 'X'. The "
|
|
"elements data type must be int.")
|
|
.AsDispensable();
|
|
AddOutput("Out",
|
|
"The output of crop op, "
|
|
"which is of the same dimensions as X.");
|
|
AddAttr<std::vector<int>>("offsets",
|
|
"A list<int> describing offsets to be cropped. "
|
|
"The size of offsets list should be the same as "
|
|
"the dimension size of input X.")
|
|
.SetDefault(std::vector<int>());
|
|
AddAttr<std::vector<int>>("shape",
|
|
"A list<int> describing the shape of output. "
|
|
"The size of shape list should be the same as "
|
|
"the dimension size of input X.")
|
|
.SetDefault(std::vector<int>());
|
|
AddComment(R"DOC(
|
|
Crop Operator.
|
|
|
|
Crop input into output, as specified by offsets and shape.
|
|
|
|
There are two ways to set the offsets:
|
|
1. In runtime: Using the input 'Offsets', which is a Vairbale and can be
|
|
output of other operators. This way is suitable for
|
|
dynamic offsets.
|
|
2. In network configuration: Using the attribute 'offsets', which will be
|
|
set in Python configure script. This way is
|
|
suitable for fixed offsets.
|
|
You CANNOT use these two ways at the same time. An exception will be raised
|
|
if input 'Offset' is configured and meanwhile the attribute 'offsets' is
|
|
not empty.
|
|
|
|
There are two ways to set shape:
|
|
1. reference input: crop input X into the same shape as reference input.
|
|
The dimension of reference input should
|
|
be the same as the dimension of input X.
|
|
2. shape list: crop input X into the shape described by a list<int>.
|
|
The size of shape list should be the same as
|
|
the dimension size of input X.
|
|
|
|
The input should be a k-D tensor(k > 0 and k < 7). As an example:
|
|
|
|
Case 1:
|
|
Given
|
|
|
|
X = [[0, 1, 2, 0, 0]
|
|
[0, 3, 4, 0, 0]
|
|
[0, 0, 0, 0, 0]],
|
|
|
|
and
|
|
|
|
offsets = [0, 1],
|
|
|
|
and
|
|
|
|
shape = [2, 2],
|
|
|
|
we get:
|
|
|
|
Out = [[1, 2],
|
|
[3, 4]].
|
|
|
|
|
|
Case 2:
|
|
Given
|
|
|
|
X = [[0, 1, 2, 5, 0]
|
|
[0, 3, 4, 6, 0]
|
|
[0, 0, 0, 0, 0]],
|
|
|
|
and
|
|
|
|
offsets = [0, 1],
|
|
|
|
and
|
|
|
|
Y = [[0, 0, 0]
|
|
[0, 0, 0]],
|
|
|
|
we get:
|
|
|
|
Out = [[1, 2, 5],
|
|
[3, 4, 6]].
|
|
)DOC");
|
|
}
|
|
};
|
|
|
|
class CropOpGrad : public framework::OperatorWithKernel {
|
|
public:
|
|
using framework::OperatorWithKernel::OperatorWithKernel;
|
|
|
|
void InferShape(framework::InferShapeContext* ctx) const override {
|
|
OP_INOUT_CHECK(ctx->HasInput("X"), "Input", "X", "CropGrad");
|
|
OP_INOUT_CHECK(ctx->HasInput(framework::GradVarName("Out")), "Input",
|
|
framework::GradVarName("Out"), "CropGrad");
|
|
auto x_dims = ctx->GetInputDim("X");
|
|
auto x_grad_name = framework::GradVarName("X");
|
|
if (ctx->HasOutput(x_grad_name)) {
|
|
ctx->SetOutputDim(x_grad_name, x_dims);
|
|
}
|
|
}
|
|
|
|
framework::OpKernelType GetExpectedKernelType(
|
|
const framework::ExecutionContext& ctx) const override {
|
|
return framework::OpKernelType(OperatorWithKernel::IndicateVarDataType(
|
|
ctx, framework::GradVarName("Out")),
|
|
ctx.device_context());
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
class CropGradOpMaker : public framework::SingleGradOpMaker<T> {
|
|
public:
|
|
using framework::SingleGradOpMaker<T>::SingleGradOpMaker;
|
|
|
|
protected:
|
|
void Apply(GradOpPtr<T> op) const override {
|
|
op->SetType("crop_grad");
|
|
op->SetInput(framework::GradVarName("Out"), this->OutputGrad("Out"));
|
|
op->SetInput("X", this->Input("X"));
|
|
if (this->HasInput("Offsets")) {
|
|
op->SetInput("Offsets", this->Input("Offsets"));
|
|
}
|
|
op->SetOutput(framework::GradVarName("X"), this->InputGrad("X"));
|
|
op->SetAttrMap(this->Attrs());
|
|
}
|
|
};
|
|
|
|
DECLARE_NO_NEED_BUFFER_VARS_INFERER(GropNoNeedBufferVarInferer, "Y");
|
|
|
|
} // namespace operators
|
|
} // namespace paddle
|
|
|
|
namespace ops = paddle::operators;
|
|
REGISTER_OPERATOR(crop, ops::CropOp, ops::CropOpMaker,
|
|
ops::CropGradOpMaker<paddle::framework::OpDesc>,
|
|
ops::CropGradOpMaker<paddle::imperative::OpBase>,
|
|
ops::GropNoNeedBufferVarInferer);
|
|
REGISTER_OPERATOR(crop_grad, ops::CropOpGrad);
|
|
REGISTER_OP_CPU_KERNEL(
|
|
crop, ops::CropKernel<paddle::platform::CPUDeviceContext, float>,
|
|
ops::CropKernel<paddle::platform::CPUDeviceContext, double>);
|
|
REGISTER_OP_CPU_KERNEL(
|
|
crop_grad, ops::CropGradKernel<paddle::platform::CPUDeviceContext, float>,
|
|
ops::CropGradKernel<paddle::platform::CPUDeviceContext, double>);
|