1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #14301 from l-bat:conv3d

Support Convolution3D layer on IE backend (#14301)

* Add Convolution3D layer

* Disable CXX11

* Fixed tests

* Add Pooling3D layer

* Merge Conv2d with Conv3d and Pool2d with Pool3d layers

* Split pads

* Add Deconvolution layer

* Refactoring

* Deduplication

* Refactoring

* Add utils for Convolution and Pooling layers
This commit is contained in:
Lubov Batanina
2019-04-30 17:08:17 +03:00
committed by Alexander Alekhin
parent 3bcbd2a078
commit 77fa59c3da
10 changed files with 412 additions and 289 deletions
+112 -88
View File
@@ -66,31 +66,34 @@ public:
BaseConvolutionLayerImpl(const LayerParams &params)
{
setParamsFrom(params);
int pad_t = 0, pad_l = 0, pad_r = 0, pad_b = 0;
getConvolutionKernelParams(params, kernel.height, kernel.width, pad_t,
pad_l, pad_b, pad_r, stride.height, stride.width, dilation.height,
dilation.width, padMode);
if (pad_t != pad_b || pad_l != pad_r)
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer");
pad.width = pad_l;
pad.height = pad_t;
getConvolutionKernelParams(params, kernel_size, pads_begin, pads_end, strides, dilations, padMode);
numOutput = params.get<int>("num_output");
int ngroups = params.get<int>("group", 1);
adjustPad.height = params.get<int>("adj_h", 0);
adjustPad.width = params.get<int>("adj_w", 0);
CV_Assert(numOutput % ngroups == 0);
CV_Assert(adjustPad.width < stride.width &&
adjustPad.height < stride.height);
if (kernel_size.size() == 2) {
kernel = Size(kernel_size[1], kernel_size[0]);
stride = Size(strides[1], strides[0]);
for (int i = 0; i < pads_begin.size(); i++) {
if (pads_begin[i] != pads_end[i])
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer");
}
pad = Size(pads_begin[1], pads_begin[0]);
dilation = Size(dilations[1], dilations[0]);
adjust_pads.push_back(params.get<int>("adj_h", 0));
adjust_pads.push_back(params.get<int>("adj_w", 0));
adjustPad.height = adjust_pads[0];
adjustPad.width = adjust_pads[1];
CV_Assert(adjustPad.width < stride.width &&
adjustPad.height < stride.height);
}
newWeightAndBias = false;
}
void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE
virtual void finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) CV_OVERRIDE
{
std::vector<Mat> inputs, outputs;
inputs_arr.getMatVector(inputs);
@@ -98,31 +101,38 @@ public:
CV_Assert(inputs.size() > 0);
CV_Assert(blobs.size() >= 1 && blobs.size() <= 2);
CV_Assert(blobs[0].dims == 4 && blobs[0].size[3] == kernel.width && blobs[0].size[2] == kernel.height);
CV_Assert(blobs.size() == 1 || blobs.size() == 2);
CV_Assert(inputs[0].dims == outputs[0].dims);
CV_Assert(blobs[0].dims == kernel_size.size() + 2);
for (int i = 0; i < kernel_size.size(); i++) {
CV_Assert(blobs[0].size[i + 2] == kernel_size[i]);
}
const Mat &input = inputs[0];
CV_Assert(input.dims == 4 && (input.type() == CV_32F || input.type() == CV_64F || input.type() == CV_16S));
CV_Assert((input.dims == 4 || input.dims == 5) && (input.type() == CV_32F || input.type() == CV_16S));
for (size_t i = 0; i < inputs.size(); i++)
{
CV_Assert(inputs[i].type() == input.type());
CV_Assert(inputs[i].dims == 4 && inputs[i].size[1] == input.size[1]);
CV_Assert(inputs[i].size[2] == input.size[2] && inputs[i].size[3] == input.size[3]);
CV_Assert((inputs[i].dims == 4 || inputs[i].dims == 5) && inputs[i].size[1] == input.size[1]);
for (int j = 0; j < inputs[i].dims; j++) {
CV_Assert(inputs[i].size[j] == input.size[j]);
}
}
Size outSize = Size(outputs[0].size[3], outputs[0].size[2]);
int pad_t = pad.height, pad_l = pad.width, pad_b = pad.height, pad_r = pad.width;
getConvPoolPaddings(Size(input.size[3], input.size[2]), outSize,
kernel, stride, padMode, dilation, pad_t, pad_l, pad_b, pad_r);
if (pad_t != pad_b || pad_l != pad_r)
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer");
pad.width = pad_l;
pad.height = pad_t;
std::vector<int> inpShape;
std::vector<int> outShape;
for (int i = 2; i < inputs[0].dims; i++) {
inpShape.push_back(inputs[0].size[i]);
outShape.push_back(outputs[0].size[i]);
}
getConvPoolPaddings(inpShape, outShape, kernel_size, strides, padMode, dilations, pads_begin, pads_end);
if (pads_begin.size() == 2) {
for (int i = 0; i < pads_begin.size(); i++) {
if (pads_begin[i] != pads_end[i])
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer");
}
pad = Size(pads_begin[1], pads_begin[0]);
}
}
bool hasBias() const
@@ -134,8 +144,8 @@ public:
bool is1x1() const
{
return (kernel.height == 1 && kernel.width == 1) &&
(stride.height == 1 && stride.width == 1) &&
(dilation.height == 1 && dilation.width == 1);
(stride.height == 1 && stride.width == 1) &&
(dilation.height == 1 && dilation.width == 1);
}
virtual bool tryFuse(Ptr<Layer>& top) CV_OVERRIDE
@@ -237,12 +247,14 @@ public:
#ifdef HAVE_INF_ENGINE
if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
{
if (kernel_size.size() == 3)
return preferableTarget == DNN_TARGET_CPU;
return INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R4) ||
(preferableTarget != DNN_TARGET_MYRIAD || dilation.width == dilation.height);
}
else
#endif
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
return (kernel_size.size() == 2) && (backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE);
}
bool getMemoryShapes(const std::vector<MatShape> &inputs,
@@ -256,21 +268,23 @@ public:
internals.clear();
int inpCn = inputs[0][1];
int inpH = inputs[0][2];
int inpW = inputs[0][3];
CV_Assert(inputs.size() != 0);
std::vector<int> inpShape(inputs[0].begin() + 2, inputs[0].end());
int outCn = blobs[0].size[0];
Size out;
std::vector<int> outShape;
outShape.push_back(inputs[0][0]);
outShape.push_back(outCn);
int inpCn = inputs[0][1];
if (padMode.empty())
{
out.height = (inpH + 2 * pad.height - (dilation.height * (kernel.height - 1) + 1)) / stride.height + 1;
out.width = (inpW + 2 * pad.width - (dilation.width * (kernel.width - 1) + 1)) / stride.width + 1;
for (int i = 0; i < inpShape.size(); i++)
outShape.push_back((inpShape[i] + pads_begin[i] + pads_end[i] - dilations[i] * (kernel_size[i] - 1) - 1) / strides[i] + 1);
}
else
{
getConvPoolOutParams(Size(inpW, inpH), kernel, stride, padMode, dilation, out);
getConvPoolOutParams(inpShape, kernel_size, strides, padMode, dilations, outShape);
}
int ngroups = inpCn / blobs[0].size[1];
@@ -279,8 +293,7 @@ public:
"be multiple of %d but got %d", blobs[0].size[1], inpCn));
CV_Assert(ngroups > 0 && inpCn % ngroups == 0 && outCn % ngroups == 0);
int dims[] = {inputs[0][0], outCn, out.height, out.width};
outputs.resize(inputs.size(), shape(dims, 4));
outputs.resize(1, outShape);
return false;
}
@@ -451,25 +464,28 @@ public:
{
#ifdef HAVE_INF_ENGINE
InferenceEngine::DataPtr input = infEngineDataNode(inputs[0]);
CV_Assert(input->dims.size() == 4);
CV_Assert(input->dims.size() == 4 || input->dims.size() == 5);
const int inpCn = input->dims[2]; // NOTE: input->dims are reversed (whcn)
const int inpCn = input->dims[input->dims.size() - 2]; // NOTE: input->dims are reversed (WHIO or WHDIO)
const int outCn = blobs[0].size[0];
const int inpGroupCn = blobs[0].size[1];
const int group = inpCn / inpGroupCn;
auto ieWeights = wrapToInfEngineBlob(blobs[0], InferenceEngine::Layout::OIHW);
InferenceEngine::Layout layout = (input->dims.size() == 4) ? InferenceEngine::Layout::OIHW :
InferenceEngine::Layout::NCDHW;
auto ieWeights = wrapToInfEngineBlob(blobs[0], layout);
if (newWeightAndBias)
{
if (weightsMat.isContinuous())
{
Mat fusedWeights = weightsMat.reshape(1, blobs[0].dims, blobs[0].size);
ieWeights = wrapToInfEngineBlob(fusedWeights, InferenceEngine::Layout::OIHW);
ieWeights = wrapToInfEngineBlob(fusedWeights, layout);
}
else
{
ieWeights = InferenceEngine::make_shared_blob<float>(
InferenceEngine::Precision::FP32, InferenceEngine::Layout::OIHW,
InferenceEngine::Precision::FP32, layout,
ieWeights->dims());
ieWeights->allocate();
@@ -488,11 +504,11 @@ public:
#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5)
InferenceEngine::Builder::ConvolutionLayer ieLayer(name);
ieLayer.setKernel({(size_t)kernel.height, (size_t)kernel.width});
ieLayer.setStrides({(size_t)stride.height, (size_t)stride.width});
ieLayer.setDilation({(size_t)dilation.height, (size_t)dilation.width});
ieLayer.setPaddingsBegin({(size_t)pad.height, (size_t)pad.width});
ieLayer.setPaddingsEnd({(size_t)pad.height, (size_t)pad.width});
ieLayer.setKernel(kernel_size);
ieLayer.setStrides(strides);
ieLayer.setDilation(dilations);
ieLayer.setPaddingsBegin(pads_begin);
ieLayer.setPaddingsEnd(pads_end);
ieLayer.setGroup((size_t)group);
ieLayer.setOutDepth((size_t)outCn);
@@ -1085,6 +1101,10 @@ public:
CV_Assert_N(inputs.size() == (size_t)1, inputs[0].size[1] % blobs[0].size[1] == 0,
outputs.size() == 1, inputs[0].data != outputs[0].data);
if (inputs[0].dims == 5) {
CV_Error(Error::StsNotImplemented, "Convolution3D layer is not supported on OCV backend");
}
int ngroups = inputs[0].size[1]/blobs[0].size[1];
CV_Assert(outputs[0].size[1] % ngroups == 0);
int outCn = blobs[0].size[0];
@@ -1157,6 +1177,9 @@ public:
#ifdef HAVE_INF_ENGINE
if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
{
if (kernel_size.size() == 3)
CV_Error(Error::StsNotImplemented, "Unsupported deconvolution3D layer");
if (INF_ENGINE_RELEASE >= 2018050000 && (adjustPad.height || adjustPad.width))
return false;
@@ -1172,7 +1195,7 @@ public:
}
else
#endif // HAVE_INF_ENGINE
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE;
return kernel_size.size() == 2 && (backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_HALIDE);
}
bool getMemoryShapes(const std::vector<MatShape> &inputs,
@@ -1183,39 +1206,36 @@ public:
CV_Assert(!hasBias() || blobs[1].total() == (size_t)numOutput);
CV_Assert(inputs.size() != 0);
int inpCn = inputs[0][1];
int inpH = inputs[0][2];
int inpW = inputs[0][3];
int outH = -1, outW = -1;
int outCn = numOutput;
std::vector<int> outShape;
outShape.push_back(inputs[0][0]); // batch
outShape.push_back(outCn);
if (padMode.empty())
{
outH = stride.height * (inpH - 1) + kernel.height - 2 * pad.height + adjustPad.height;
outW = stride.width * (inpW - 1) + kernel.width - 2 * pad.width + adjustPad.width;
for (int i = 0; i < kernel_size.size(); i++)
outShape.push_back(strides[i] * (inputs[0][2 + i] - 1) + kernel_size[i] - pads_begin[i] - pads_end[i] + adjust_pads[i]);
}
else if (padMode == "VALID")
{
outH = stride.height * (inpH - 1) + kernel.height + adjustPad.height;
outW = stride.width * (inpW - 1) + kernel.width + adjustPad.width;
for (int i = 0; i < kernel_size.size(); i++)
outShape.push_back(strides[i] * (inputs[0][2 + i] - 1) + kernel_size[i] + adjust_pads[i]);
}
else if (padMode == "SAME")
{
outH = stride.height * (inpH - 1) + 1 + adjustPad.height;
outW = stride.width * (inpW - 1) + 1 + adjustPad.width;
for (int i = 0; i < kernel_size.size(); i++)
outShape.push_back(strides[i] * (inputs[0][2 + i] - 1) + 1 + adjust_pads[i]);
}
else
CV_Error(Error::StsError, "Unsupported padding mode " + padMode);
int outCn = numOutput;
CV_Assert(outCn % blobs[0].size[1] == 0);
int ngroups = outCn / blobs[0].size[1];
int inpCn = inputs[0][1];
CV_Assert(inpCn % ngroups == 0 && outCn % ngroups == 0);
CV_Assert(blobs[0].size[0] == inpCn);
int dims[] = {inputs[0][0], outCn, outH, outW};
outputs.resize(inputs.size(), shape(dims, 4));
outputs.resize(1, outShape);
if (!is1x1())
internals.push_back(computeColRowShape(inputs[0], outputs[0]));
@@ -1231,16 +1251,20 @@ public:
inputs_arr.getMatVector(inputs);
outputs_arr.getMatVector(outputs);
int pad_t = pad.height, pad_l = pad.width, pad_b = pad.height, pad_r = pad.width;
getConvPoolPaddings(Size(outputs[0].size[3], outputs[0].size[2]),
Size(inputs[0].size[3], inputs[0].size[2]),
kernel, stride, padMode, dilation, pad_t, pad_l, pad_b, pad_r);
if (pad_t != pad_b || pad_l != pad_r)
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in convolution layer");
pad.width = pad_l;
pad.height = pad_t;
std::vector<int> inpShape;
std::vector<int> outShape;
for (int i = 2; i < inputs[0].dims; i++) {
inpShape.push_back(inputs[0].size[i]);
outShape.push_back(outputs[0].size[i]);
}
getConvPoolPaddings(outShape, inpShape, kernel_size, strides, padMode, dilations, pads_begin, pads_end);
if (pads_begin.size() == 2) {
for (int i = 0; i < pads_begin.size(); i++) {
if (pads_begin[i] != pads_end[i])
CV_Error(Error::StsNotImplemented, "Unsupported asymmetric padding in deconvolution layer");
}
pad = Size(pads_begin[1], pads_begin[0]);
}
weightsMultipliers.assign(numOutput, 1.0);
if (weightsMat.empty())
@@ -1760,11 +1784,11 @@ public:
InferenceEngine::Builder::DeconvolutionLayer ieLayer(name);
ieLayer.setKernel({(size_t)kernel.height, (size_t)kernel.width});
ieLayer.setStrides({(size_t)stride.height, (size_t)stride.width});
ieLayer.setDilation({(size_t)dilation.height, (size_t)dilation.width});
ieLayer.setPaddingsBegin({(size_t)pad.height, (size_t)pad.width});
ieLayer.setPaddingsEnd({(size_t)pad.height, (size_t)pad.width});
ieLayer.setKernel(kernel_size);
ieLayer.setStrides(strides);
ieLayer.setDilation(dilations);
ieLayer.setPaddingsBegin(pads_begin);
ieLayer.setPaddingsEnd(pads_end);
ieLayer.setGroup((size_t)group);
ieLayer.setOutDepth((size_t)numOutput);