mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge pull request #22199 from zihaomu:bug_fix_22195
DNN: Reduce Layer (add dynamic batch and ReduceSum support)
This commit is contained in:
commit
bb71cb200e
@ -334,7 +334,8 @@ CV__DNN_INLINE_NS_BEGIN
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int reduceType;
|
int reduceType;
|
||||||
std::vector<size_t> reduceDims;
|
// reduceDims contains the dimensions that need to be reduced, targetDims is the target output dimension.
|
||||||
|
std::vector<size_t> reduceDims, targetDims;
|
||||||
static Ptr<ReduceLayer> create(const LayerParams& params);
|
static Ptr<ReduceLayer> create(const LayerParams& params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,6 +38,15 @@ public:
|
|||||||
{
|
{
|
||||||
reduceDims[i] = tempDims.get<int>(i);
|
reduceDims[i] = tempDims.get<int>(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CV_Assert(params.has("target_dims"));
|
||||||
|
tempDims = params.get("target_dims");
|
||||||
|
n = tempDims.size();
|
||||||
|
targetDims.resize(n);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
targetDims[i] = tempDims.get<int>(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
@ -161,18 +170,30 @@ public:
|
|||||||
std::vector<MatShape> &internals) const CV_OVERRIDE
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
||||||
{
|
{
|
||||||
CV_Assert(inputs.size() > 0);
|
CV_Assert(inputs.size() > 0);
|
||||||
CV_Assert(reduceDims.size() != 0 && inputs[0].size() >= reduceDims.size());
|
CV_Assert( reduceDims.size() !=0 && targetDims.size() != 0 && inputs[0].size() >= reduceDims.size());
|
||||||
|
|
||||||
std::vector<int> outShape;
|
// outShapeTmp can save the right number of `total(outShapeTmp)`. And the outShape is used as the final output shape.
|
||||||
|
std::vector<int> outShapeTmp, outShape;
|
||||||
|
outShape.assign(targetDims.begin(), targetDims.end());
|
||||||
if (inputs[0].size() == reduceDims.size())
|
if (inputs[0].size() == reduceDims.size())
|
||||||
outShape.push_back(1);
|
outShapeTmp.push_back(1);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
|
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
|
||||||
{
|
{
|
||||||
outShape.push_back(inputs[0][i]);
|
outShapeTmp.push_back(inputs[0][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support dynamic shape of Batch size.
|
||||||
|
// Note that: when there are multiple dynamic inputs, we will give an error.
|
||||||
|
if (total(outShape) != total(outShapeTmp))
|
||||||
|
{
|
||||||
|
if (outShape[0] != outShapeTmp[0])
|
||||||
|
outShape[0] = outShapeTmp[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CV_Assert(total(outShape) == total(outShapeTmp));
|
||||||
outputs.assign(1, outShape);
|
outputs.assign(1, outShape);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -61,6 +61,15 @@ public:
|
|||||||
{
|
{
|
||||||
reduceDims[i] = tempDims.get<int>(i);
|
reduceDims[i] = tempDims.get<int>(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CV_Assert(params.has("target_dims"));
|
||||||
|
tempDims = params.get("target_dims");
|
||||||
|
n = tempDims.size();
|
||||||
|
targetDims.resize(n);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
targetDims[i] = tempDims.get<int>(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||||
@ -325,18 +334,29 @@ public:
|
|||||||
std::vector<MatShape> &internals) const CV_OVERRIDE
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
||||||
{
|
{
|
||||||
CV_Assert(inputs.size() > 0);
|
CV_Assert(inputs.size() > 0);
|
||||||
CV_Assert(reduceDims.size() != 0 && inputs[0].size() >= reduceDims.size());
|
CV_Assert( reduceDims.size() !=0 && targetDims.size() != 0 && inputs[0].size() >= reduceDims.size());
|
||||||
|
|
||||||
std::vector<int> outShape;
|
// outShapeTmp can save the right number of `total(outShapeTmp)`. And the outShape is used as the final output shape.
|
||||||
|
std::vector<int> outShapeTmp, outShape;
|
||||||
|
outShape.assign(targetDims.begin(), targetDims.end());
|
||||||
if (inputs[0].size() == reduceDims.size())
|
if (inputs[0].size() == reduceDims.size())
|
||||||
outShape.push_back(1);
|
outShapeTmp.push_back(1);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
|
for (int i = 0; i < inputs[0].size() - reduceDims.size(); i++)
|
||||||
{
|
{
|
||||||
outShape.push_back(inputs[0][i]);
|
outShapeTmp.push_back(inputs[0][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support dynamic shape of Batch size.
|
||||||
|
// Note that: when there are multiple dynamic inputs, we will give an error.
|
||||||
|
if (total(outShape) != total(outShapeTmp) && outShape[0] != outShapeTmp[0])
|
||||||
|
{
|
||||||
|
outShape[0] = outShapeTmp[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
CV_Assert(total(outShape) == total(outShapeTmp));
|
||||||
outputs.assign(1, outShape);
|
outputs.assign(1, outShape);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1203,36 +1203,47 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node
|
|||||||
layerParams.set("reduce", reduceType);
|
layerParams.set("reduce", reduceType);
|
||||||
bool keepdims = layerParams.get<int>("keepdims", 1) == 1;
|
bool keepdims = layerParams.get<int>("keepdims", 1) == 1;
|
||||||
|
|
||||||
if (layer_type == "ReduceSum" && node_proto.input_size() == 2)
|
|
||||||
{
|
|
||||||
// TODO support the opset 13 of ReduceSum.
|
|
||||||
// in opset 13, the ReduceSum has two input, it takes axes as input instead of attribute
|
|
||||||
// details:https://github.com/onnx/onnx/issues/3420#issuecomment-844295687
|
|
||||||
CV_Error(Error::StsNotImplemented, "Unsupported " + layer_type + " operation of opset 13, please try to "
|
|
||||||
"re-export the onnx model with opset 11.");
|
|
||||||
}
|
|
||||||
|
|
||||||
MatShape inpShape = outShapes[node_proto.input(0)];
|
MatShape inpShape = outShapes[node_proto.input(0)];
|
||||||
std::vector<bool> shouldDelete(inpShape.size(), false);
|
std::vector<bool> shouldDelete(inpShape.size(), false);
|
||||||
|
|
||||||
if (layerParams.has("axes"))
|
if (layer_type == "ReduceSum" && node_proto.input_size() == 2)
|
||||||
{
|
{
|
||||||
DictValue axes = layerParams.get("axes");
|
if (constBlobs.find(node_proto.input(1)) != constBlobs.end())
|
||||||
for (int i = 0; i < axes.size(); i++)
|
|
||||||
{
|
{
|
||||||
int axis = normalize_axis(axes.get<int>(i), inpShape.size());
|
Mat axesMat = getBlob(node_proto, 1);
|
||||||
shouldDelete[axis] = true;
|
int axesNum = axesMat.total();
|
||||||
|
for (int i = 0; i < axesNum; i++)
|
||||||
|
{
|
||||||
|
int axis = normalize_axis(axesMat.at<int>(i), inpShape.size());
|
||||||
|
shouldDelete[axis] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
// in opset 13, the ReduceSum has two input, it takes axes as input instead of attribute
|
||||||
|
// details:https://github.com/onnx/onnx/issues/3420#issuecomment-844295687
|
||||||
|
CV_Error(Error::StsNotImplemented, "Non-constant axis values in ReduceSum are not supported.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i = 0; i < inpShape.size(); i++)
|
if (layerParams.has("axes"))
|
||||||
{
|
{
|
||||||
shouldDelete[i] = true;
|
DictValue axes = layerParams.get("axes");
|
||||||
|
for (int i = 0; i < axes.size(); i++)
|
||||||
|
{
|
||||||
|
int axis = normalize_axis(axes.get<int>(i), inpShape.size());
|
||||||
|
shouldDelete[axis] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < inpShape.size(); i++)
|
||||||
|
{
|
||||||
|
shouldDelete[i] = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MatShape targetShape;
|
std::vector<int> targetShape;
|
||||||
for (int i = 0; i < inpShape.size(); ++i)
|
for (int i = 0; i < inpShape.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!shouldDelete[i])
|
if (!shouldDelete[i])
|
||||||
@ -1302,19 +1313,10 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerParams reduceLp = layerParams;
|
layerParams.set("deleted_dims", DictValue::arrayInt(&deletedDims[0], deletedDims.size()));
|
||||||
reduceLp.name = layerParams.name + "/reduce";
|
layerParams.set("target_dims", DictValue::arrayInt(&targetShape[0], targetShape.size()));
|
||||||
CV_Assert(layer_id.find(reduceLp.name) == layer_id.end());
|
|
||||||
reduceLp.set("deleted_dims", DictValue::arrayInt(&deletedDims[0], deletedDims.size()));
|
|
||||||
|
|
||||||
node_proto.set_input(0, inputString);
|
node_proto.set_input(0, inputString);
|
||||||
node_proto.set_output(0, reduceLp.name);
|
|
||||||
addLayer(reduceLp, node_proto);
|
|
||||||
|
|
||||||
layerParams.type = (depth == CV_8S) ? "ReshapeInt8" : "Reshape";
|
|
||||||
layerParams.set("dim", DictValue::arrayInt(&targetShape[0], targetShape.size()));
|
|
||||||
|
|
||||||
node_proto.set_input(0, node_proto.output(0));
|
|
||||||
node_proto.set_output(0, output_name);
|
node_proto.set_output(0, output_name);
|
||||||
|
|
||||||
addLayer(layerParams, node_proto);
|
addLayer(layerParams, node_proto);
|
||||||
|
@ -411,6 +411,7 @@ TEST_P(Test_ONNX_layers, ReduceMean)
|
|||||||
TEST_P(Test_ONNX_layers, ReduceSum)
|
TEST_P(Test_ONNX_layers, ReduceSum)
|
||||||
{
|
{
|
||||||
testONNXModels("reduce_sum");
|
testONNXModels("reduce_sum");
|
||||||
|
testONNXModels("reduce_sum_axis_dynamic_batch");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Test_ONNX_layers, ReduceMax)
|
TEST_P(Test_ONNX_layers, ReduceMax)
|
||||||
|
Loading…
Reference in New Issue
Block a user