mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 09:25:45 +08:00
Merge pull request #19546 from LupusSanctus:am/slice_steps
* Added Steps support in DNN Slice layer * Added code corrections * dnn(slice): fix OCL and OCL_FP16 processing
This commit is contained in:
parent
bf9f67e93f
commit
3e48a91d97
@ -364,6 +364,7 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
|
||||
* Inner vector has slice ranges for the first number of input dimensions.
|
||||
*/
|
||||
std::vector<std::vector<Range> > sliceRanges;
|
||||
std::vector<std::vector<int> > sliceSteps;
|
||||
int axis;
|
||||
int num_split;
|
||||
|
||||
|
@ -64,6 +64,7 @@ public:
|
||||
SliceLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
hasSteps = false;
|
||||
axis = params.get<int>("axis", 1);
|
||||
num_split = params.get<int>("num_split", 0);
|
||||
hasDynamicShapes = params.get<bool>("has_dynamic_shapes", false);
|
||||
@ -112,6 +113,22 @@ public:
|
||||
sliceRanges[0][i].end = end; // We'll finalize a negative value later.
|
||||
}
|
||||
}
|
||||
|
||||
if (params.has("steps"))
|
||||
{
|
||||
const DictValue &steps = params.get("steps");
|
||||
sliceSteps.resize(1);
|
||||
sliceSteps[0].resize(steps.size());
|
||||
|
||||
for (int i = 0; i < steps.size(); ++i)
|
||||
{
|
||||
int step = steps.get<int>(i);
|
||||
CV_Assert(step >= 1);
|
||||
if (step > 1)
|
||||
hasSteps = true;
|
||||
sliceSteps[0][i] = step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,11 +137,11 @@ public:
|
||||
#ifdef HAVE_DNN_IE_NN_BUILDER_2019
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||
return INF_ENGINE_VER_MAJOR_GE(INF_ENGINE_RELEASE_2019R1) &&
|
||||
sliceRanges.size() == 1 && sliceRanges[0].size() == 4;
|
||||
sliceRanges.size() == 1 && sliceRanges[0].size() == 4 && !hasSteps;
|
||||
#endif
|
||||
#ifdef HAVE_DNN_NGRAPH
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
return sliceRanges.size() == 1;
|
||||
return sliceRanges.size() == 1 && !hasSteps;
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
}
|
||||
@ -147,6 +164,9 @@ public:
|
||||
{
|
||||
if (shapesInitialized || inpShape[j] > 0)
|
||||
outputs[i][j] = normalize_axis_range(sliceRanges[i][j], inpShape[j]).size();
|
||||
|
||||
if (!sliceSteps.empty() && (i < sliceSteps.size()) && (j < sliceSteps[i].size()) && (sliceSteps[i][j] > 1))
|
||||
outputs[i][j] = (outputs[i][j] + sliceSteps[i][j] - 1) / sliceSteps[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,6 +201,7 @@ public:
|
||||
const MatSize& inpShape = inputs[0].size;
|
||||
|
||||
finalSliceRanges = sliceRanges;
|
||||
|
||||
if (sliceRanges.empty())
|
||||
{
|
||||
// Divide input blob on equal parts by axis.
|
||||
@ -213,6 +234,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!sliceSteps.empty() && sliceSteps[0].size() != inputs[0].dims)
|
||||
sliceSteps[0].resize(inputs[0].dims, 1);
|
||||
|
||||
#if 0
|
||||
std::cout << "DEBUG: DNN/Slice: " << outputs.size() << " inpShape=" << inpShape << std::endl;
|
||||
for (int i = 0; i < outputs.size(); ++i)
|
||||
@ -420,6 +444,9 @@ public:
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
if (hasSteps)
|
||||
return false; // TODO not implemented yet: https://github.com/opencv/opencv/pull/19546
|
||||
|
||||
std::vector<UMat> inputs;
|
||||
std::vector<UMat> outputs;
|
||||
|
||||
@ -478,9 +505,24 @@ public:
|
||||
|
||||
const Mat& inpMat = inputs[0];
|
||||
CV_Assert(outputs.size() == finalSliceRanges.size());
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
|
||||
if (!hasSteps)
|
||||
{
|
||||
inpMat(finalSliceRanges[i]).copyTo(outputs[i]);
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
inpMat(finalSliceRanges[i]).copyTo(outputs[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int dimsNum = inpMat.dims;
|
||||
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
std::vector<int> inpIdx(dimsNum, 0);
|
||||
std::vector<int> outIdx(dimsNum, 0);
|
||||
getSliceRecursive(inpMat, inpIdx, finalSliceRanges[i], sliceSteps[i], 0, dimsNum, outputs[i], outIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -570,11 +612,42 @@ public:
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
|
||||
private:
|
||||
void getSliceRecursive(const Mat &inpMat, std::vector<int> &inpIdx,
|
||||
const std::vector<Range> &sliceRanges,
|
||||
const std::vector<int> &sliceSteps, int dim, int dimsNum,
|
||||
Mat &outputs, std::vector<int> &outIdx)
|
||||
{
|
||||
int begin = sliceRanges[dim].start;
|
||||
int end = sliceRanges[dim].end;
|
||||
int step = !sliceSteps.empty() ? sliceSteps[dim] : 1;
|
||||
|
||||
const bool is32F = inpMat.depth() == CV_32F;
|
||||
|
||||
// TODO optimization is required (for 2D tail case at least)
|
||||
for (int k = begin, j = 0; k < end; k += step, j++)
|
||||
{
|
||||
inpIdx[dim] = k;
|
||||
outIdx[dim] = j;
|
||||
|
||||
if (dim + 1 < dimsNum)
|
||||
getSliceRecursive(inpMat, inpIdx, sliceRanges, sliceSteps, dim + 1, dimsNum, outputs, outIdx);
|
||||
else
|
||||
{
|
||||
if (is32F)
|
||||
outputs.at<float>(outIdx.data()) = inpMat.at<float>(inpIdx.data());
|
||||
else
|
||||
outputs.at<short>(outIdx.data()) = inpMat.at<short>(inpIdx.data()); // 16F emulation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// The actual non-negative values determined from @p sliceRanges depends on input size.
|
||||
std::vector<std::vector<Range> > finalSliceRanges;
|
||||
bool hasDynamicShapes;
|
||||
bool shapesInitialized;
|
||||
bool hasSteps;
|
||||
};
|
||||
|
||||
class CropLayerImpl CV_FINAL : public SliceLayerImpl
|
||||
|
@ -641,20 +641,11 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
int axis = 0;
|
||||
std::vector<int> begin;
|
||||
std::vector<int> end;
|
||||
std::vector<int> steps;
|
||||
int inp_size = node_proto.input_size();
|
||||
|
||||
if (inp_size == 1)
|
||||
{
|
||||
if (layerParams.has("steps"))
|
||||
{
|
||||
DictValue steps = layerParams.get("steps");
|
||||
for (int i = 0; i < steps.size(); ++i)
|
||||
{
|
||||
if (steps.get<int>(i) != 1)
|
||||
CV_Error(Error::StsNotImplemented,
|
||||
"Slice layer only supports steps = 1");
|
||||
}
|
||||
}
|
||||
if (layerParams.has("axes")) {
|
||||
DictValue axes = layerParams.get("axes");
|
||||
for (int i = 1; i < axes.size(); ++i) {
|
||||
@ -677,7 +668,7 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
int finish = ends.get<int>(i);
|
||||
end.push_back((finish < 0) ? --finish : finish); // numpy doesn't include last dim
|
||||
}
|
||||
} else {
|
||||
} else { // inp_size > 1
|
||||
CV_Assert(inp_size >= 3);
|
||||
for (int i = 1; i < inp_size; i++) {
|
||||
CV_Assert(constBlobs.find(node_proto.input(i)) != constBlobs.end());
|
||||
@ -711,6 +702,12 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
if (inp_size == 5) {
|
||||
CV_Assert(constBlobs.find(node_proto.input(4)) != constBlobs.end());
|
||||
Mat step_blob = getBlob(node_proto, 4);
|
||||
const int* steps_ptr = step_blob.ptr<int>();
|
||||
|
||||
if (axis > 0)
|
||||
steps.resize(axis, 1);
|
||||
|
||||
std::copy(steps_ptr, steps_ptr + step_blob.total(), std::back_inserter(steps));
|
||||
|
||||
// Very strange application for Slice op with tensor reversing.
|
||||
// We just workaround it for 2d constants.
|
||||
@ -728,13 +725,15 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
|
||||
return;
|
||||
}
|
||||
}
|
||||
CV_CheckEQ(countNonZero(step_blob != 1), 0, "Slice layer only supports steps = 1");
|
||||
}
|
||||
}
|
||||
layerParams.set("begin", DictValue::arrayInt(&begin[0], begin.size()));
|
||||
layerParams.set("end", DictValue::arrayInt(&end[0], end.size()));
|
||||
layerParams.set("axis", axis);
|
||||
|
||||
if (!steps.empty())
|
||||
layerParams.set("steps", DictValue::arrayInt(&steps[0], steps.size()));
|
||||
|
||||
if (constBlobs.find(node_proto.input(0)) != constBlobs.end())
|
||||
{
|
||||
Mat inp = getBlob(node_proto, 0);
|
||||
|
@ -627,6 +627,26 @@ TEST_P(Test_ONNX_layers, Slice)
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_2DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_2d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_3DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_3d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_4DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_4d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Slice_Steps_5DInput)
|
||||
{
|
||||
testONNXModels("slice_opset_11_steps_5d");
|
||||
}
|
||||
|
||||
TEST_P(Test_ONNX_layers, Softmax)
|
||||
{
|
||||
testONNXModels("softmax");
|
||||
|
Loading…
Reference in New Issue
Block a user