Skip to content

Support Convolution3D layer on IE backend#14301

Merged
alalek merged 11 commits intoopencv:3.4from
l-bat:conv3d
Apr 30, 2019
Merged

Support Convolution3D layer on IE backend#14301
alalek merged 11 commits intoopencv:3.4from
l-bat:conv3d

Conversation

@l-bat
Copy link
Copy Markdown
Contributor

@l-bat l-bat commented Apr 11, 2019

Merge with extra: opencv/opencv_extra#594

force_builders=Custom,Linux AVX2
buildworker:Custom=linux-2
docker_image:Custom=ubuntu-openvino-2019r1:16.04
test_opencl:Custom=ON
test_bigdata:Custom=1
test_filter:Custom=*
test_modules:Custom=dnn

ci_branch:Linux AVX2=no-checks
docker_image:Linux AVX2=ubuntu-openvino-2018r5:16.04
test_opencl:Linux AVX2=OFF
buildworker:Linux AVX2=linux-1
build_parallel_tests:Linux AVX2=1
test_bigdata:Linux AVX2=1
test_module:Linux AVX2=dnn,python2,python3,java
tests_filter:Linux AVX2=*

buildworker:Mac OpenCL=macosx-1
build_image:Mac OpenCL=openvino-2019r1
test_module:Mac OpenCL=dnn,python2,python3,java

TEST_P(Test_ONNX_layers, Convolution3D)
{
if (backend == DNN_BACKEND_OPENCV || target != DNN_TARGET_CPU)
throw SkipTestException("");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's replace to

    if (backend != DNN_BACKEND_INFERENCE_ENGINE || target != DNN_TARGET_CPU)
        throw SkipTestException("Only DLIE backend on CPU is supported");

int kernel[] = {layerParams.blobs[0].size[2], layerParams.blobs[0].size[3], layerParams.blobs[0].size[4]};
layerParams.set("kernel", DictValue::arrayInt(kernel, 3));
setDilations(layerParams, layer);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we generalize it? In example,

Mat weights = layerParams.blobs[0];
layerParams.set("kernel",  DictValue::arrayInt(&weights.size[2], weights.dims - 2));

else if (layerParams.blobs[0].dims == 5) {
int kernel[] = {layerParams.blobs[0].size[2], layerParams.blobs[0].size[3], layerParams.blobs[0].size[4]};
layerParams.set("kernel", DictValue::arrayInt(kernel, 3));
setDilations(layerParams, layer);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need dilations in case of 5D kernel but not for 4D one?

#if INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2018R5)
InferenceEngine::Builder::ConvolutionLayer ieLayer(name);

ieLayer.setKernel({(size_t)kernel.get<int>(0), (size_t)kernel.get<int>(1), (size_t)kernel.get<int>(2)});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please experiment with merging Convolution3D into the Convolution layer. IE backend receives vectors as convolution's hyperparameters so it might looks like:

// std::vector<size_t> kernel: height+width for Convolution and depth+height+width for Convolution3D
ieLayer.setKernel(kernel);

ieLayer.setStrides(std::vector<size_t>(strides.begin(), strides.end()));
ieLayer.setDilation(std::vector<size_t>(dilations.begin(), dilations.end()));
ieLayer.setPaddingsBegin(std::vector<size_t>(pads.begin(), pads.begin() + pads.size() / 2));
ieLayer.setPaddingsEnd(std::vector<size_t>(pads.begin() + pads.size() / 2, pads.end()));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just use std::vector<size_t> data type for all the kernel_size, pads, strides and dilations?

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 (whcn or whdcn)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename whcn to WHIO (and whdcn to WHDIO correspondingly) due there is no batch (n) and channels (c) in the kernel but there are input channels (I) and output channels (O).

const Mat &input = inputs[0];
CV_Assert(input.dims == 4 && (input.type() == CV_32F || input.type() == CV_64F || input.type() == CV_16S));
for (size_t i = 0; i < inputs.size(); i++)
CV_Assert((input.dims == 4 || input.dims == 5) && (input.type() == CV_32F || input.type() == CV_64F || input.type() == CV_16S));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we can remove input.type() == CV_64F check due there is no double precision support.

CV_Assert(input.dims == 4 && (input.type() == CV_32F || input.type() == CV_64F || input.type() == CV_16S));
for (size_t i = 0; i < inputs.size(); i++)
CV_Assert((input.dims == 4 || input.dims == 5) && (input.type() == CV_32F || input.type() == CV_64F || input.type() == CV_16S));
for (int i = 0; i < inputs.size(); i++)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not size_t?

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[i] + pads[i + pads.size() / 2] - dilations[i] * (kernel_size[i] - 1) - 1) / strides[i] + 1);
Copy link
Copy Markdown
Member

@dkurt dkurt Apr 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it also make sense for us to split paddings vector to begins and ends as IE expects. Can you try?

adjustPad.height < stride.height);
adjust_pads.resize(2);
adjust_pads[0] = params.get<int>("adj_h", 0);
adjust_pads[1] = params.get<int>("adj_w", 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that we can have adjust paddings with 3D convolution as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now Deconvolution layer is supported only for 4D input

(dilation.height == 1 && dilation.width == 1);
return (kernel_size.back() == 1 && kernel_size[kernel_size.size() - 2] == 1) &&
(strides.back() == 1 && strides[strides.size() - 2] == 1) &&
(dilations.back() == 1 && dilations[dilations.size() - 2] == 1);
Copy link
Copy Markdown
Member

@dkurt dkurt Apr 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due is1x1 method is called only int the default implementation for 2D convs only we can keep cv::Size checks because they will be actual.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{
if (INF_ENGINE_RELEASE >= 2018050000 && (adjustPad.height || adjustPad.width))
if (kernel_size.size() == 3)
return (INF_ENGINE_RELEASE >= 2018050000 && preferableTarget == DNN_TARGET_CPU);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3D deconvolution?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (pads_begin.size() == kernel.size() - 1 && pads_end.size() == kernel.size() - 1) {
pads_begin.push_back(pads_begin.back());
pads_end.push_back(pads_end.back());
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we need to remove

            if (parameter.size() == 1) {
                parameter.push_back(parameter.back());
            }

above and before the final checks do something like

if (pads_begin.size() == 1) {
  pads_begin.resize(kernel.size(), pads_begin[0]);
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if (kernel_size.size() == 3)
return false;

if (INF_ENGINE_RELEASE >= 2018050000 && (adjust_pads[0] || adjust_pads[1]))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still check adjustPad.height and adjustPad.width and dilation.width with dilation.height here (2D convolution).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

dims.push_back(inputs[0][0]);
dims.push_back(outCn);
dims.insert(dims.end(), outShape.begin(), outShape.end());
outputs.resize(inputs.size(), shape(&dims[0], dims.size()));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's push inputs[0][0] and outCn right into outShape at the beginning.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

std::vector<int> inpShape;
for (int i = 2; i < inputs[0].size(); i++) {
inpShape.push_back(inputs[0][i]);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::vector<int> inpShape(inputs[0].begin() + 2, inputs[0].end());

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return backendId == DNN_BACKEND_OPENCV ||
(backendId == DNN_BACKEND_HALIDE && haveHalide() &&
(type == MAX || (type == AVE && !pad_t && !pad_l && !pad_b && !pad_r)));
return (kernel_size.size() != 3) && (backendId == DNN_BACKEND_OPENCV ||
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kernel_size.size() == 2

to be more certain

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Layer_Test_ROIPooling.Accuracy "kernel_size.size() = 0

}
util::checkSize(size, pads_begin);
util::checkSize(size, pads_end);
util::checkSize(size, strides);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can align it to kernel size here because getKernelSize already increases it up to 2.

CV_Assert_N(kernel.size() == pads_begin.size() || pads_begin.size() == 1, pads_begin.size() == pads_end.size());
pads_begin.resize(kernel.size(), pads_begin[0]);
pads_end.resize(kernel.size(), pads_end[0]);
CV_Assert(pads_begin.size() == pads_end.size() && pads_begin.size() == kernel.size());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that at this point kernel.size() must be equal to pads_begin.size() and pads_end.size()`. May you check this?

if (backendId == DNN_BACKEND_INFERENCE_ENGINE)
{
if (kernel_size.size() == 3)
return false;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it's better to have a NotImplemented assertion for kernel_size.size() != 2 at the beginning of supportBackend method.


pad.width = pad_l;
pad.height = pad_t;
std::vector<int> inpShape;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra offsets?

if (hasDefault)
{
parameterH = parameterW = defaultValue;
parameter.push_back(defaultValue);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above,

parameter.assign(2, defaultValue);

A single kernel_size, pad, stride etc. are observed only with Caffe framework which has no 3d convolutions.


CV_Assert(kernelH > 0 && kernelW > 0);
if (kernel.size() == 1)
kernel.resize(2, kernel[0]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering proposed changes in the getParameter we won't need any other alignments.

@l-bat l-bat mentioned this pull request Apr 29, 2019
{
type = ROI;
pads_begin.resize(1, 0);
pads_end.resize(1, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this?

{
type = PSROI;
pads_begin.resize(1, 0);
pads_end.resize(1, 0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above

else
{
return false;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep

            else
            {
                return false;
            }

if (params.has("pad_mode"))
{
padMode = params.get<String>("pad_mode");
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep

    if (params.has("pad_mode"))
    {
        padMode = params.get<String>("pad_mode");
    }

else
{
CV_Error(Error::StsError, "Unsupported padding mode");
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep

    else
    {
        CV_Error(Error::StsError, "Unsupported padding mode");
    }

(type == MAX || (type == AVE && !pad_t && !pad_l && !pad_b && !pad_r)));
return (kernel_size.empty() || kernel_size.size() == 2) && (backendId == DNN_BACKEND_OPENCV ||
(backendId == DNN_BACKEND_HALIDE && haveHalide() &&
(type == MAX || (type == AVE && !pad_t && !pad_l && !pad_b && !pad_r))));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep aligned.

@l-bat l-bat force-pushed the conv3d branch 3 times, most recently from e1b13d8 to 749a9fd Compare April 30, 2019 13:04
Copy link
Copy Markdown
Member

@dkurt dkurt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to merge, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants