|
|
|
@ -84,6 +84,7 @@ class ONNXImporter |
|
|
|
|
const opencv_onnx::GraphProto& graph_proto); |
|
|
|
|
Mat getBlob(const opencv_onnx::NodeProto& node_proto, int index); |
|
|
|
|
Mat getBlob(const std::string& input_name); |
|
|
|
|
Mat getIntBlob(const opencv_onnx::NodeProto& node_proto, int index); |
|
|
|
|
TensorInfo getBlobExtraInfo(const opencv_onnx::NodeProto& node_proto, int index); |
|
|
|
|
TensorInfo getBlobExtraInfo(const std::string& input_name); |
|
|
|
|
|
|
|
|
@ -596,6 +597,20 @@ Mat ONNXImporter::getBlob(const std::string& input_name) |
|
|
|
|
return constBlob->second; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Mat ONNXImporter::getIntBlob(const opencv_onnx::NodeProto& node_proto, int index) |
|
|
|
|
{ |
|
|
|
|
Mat blob = getBlob(node_proto, index); |
|
|
|
|
if (blob.depth() == CV_32S) |
|
|
|
|
return blob; |
|
|
|
|
if (blob.depth() == CV_64S) { |
|
|
|
|
Mat blobInt32; |
|
|
|
|
blob.convertTo(blobInt32, CV_32S); |
|
|
|
|
return blobInt32; |
|
|
|
|
} |
|
|
|
|
CV_Error(Error::BadDepth, "blob should have integer type"); |
|
|
|
|
return Mat(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ONNXImporter::TensorInfo ONNXImporter::getBlobExtraInfo(const opencv_onnx::NodeProto &node_proto, int index) |
|
|
|
|
{ |
|
|
|
|
CV_Assert(index < node_proto.input_size()); |
|
|
|
@ -1182,7 +1197,7 @@ void ONNXImporter::parseReduce(LayerParams& layerParams, const opencv_onnx::Node |
|
|
|
|
// "axes" is turned to one of the inputs since opset 18,
|
|
|
|
|
// except for ReduceSum, which has "axes" input since opset 13.
|
|
|
|
|
if (!layerParams.has("axes") && num_inputs == 2 && constBlobs.find(node_proto.input(1)) != constBlobs.end()) { |
|
|
|
|
Mat mat_axes = getBlob(node_proto, 1); |
|
|
|
|
Mat mat_axes = getIntBlob(node_proto, 1); |
|
|
|
|
int num_axes = (int)mat_axes.total(); |
|
|
|
|
std::vector<int> axes(num_axes); |
|
|
|
|
for (int i = 0; i < num_axes; ++i) |
|
|
|
@ -1228,15 +1243,15 @@ void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
{ |
|
|
|
|
CV_Assert(constBlobs.find(node_proto.input(i)) != constBlobs.end()); |
|
|
|
|
} |
|
|
|
|
Mat start_blob = getBlob(node_proto, 1); |
|
|
|
|
Mat end_blob = getBlob(node_proto, 2); |
|
|
|
|
Mat start_blob = getIntBlob(node_proto, 1); |
|
|
|
|
Mat end_blob = getIntBlob(node_proto, 2); |
|
|
|
|
CV_Assert(start_blob.total() == end_blob.total()); |
|
|
|
|
starts_ = DictValue::arrayInt(start_blob.begin<int>(), start_blob.total()); |
|
|
|
|
ends_ = DictValue::arrayInt(end_blob.begin<int>(), end_blob.total()); |
|
|
|
|
|
|
|
|
|
if (inp_size > 3 && !getBlob(node_proto, 3).empty()) |
|
|
|
|
{ |
|
|
|
|
Mat axes_blob = getBlob(node_proto, 3); |
|
|
|
|
Mat axes_blob = getIntBlob(node_proto, 3); |
|
|
|
|
CV_Assert(axes_blob.total() == start_blob.total()); |
|
|
|
|
axes_ = DictValue::arrayInt(axes_blob.begin<int>(), axes_blob.total()); |
|
|
|
|
axis = axes_.getIntValue(0) < 0 ? axes_.getIntValue(0) + dims : axes_.getIntValue(0); |
|
|
|
@ -1245,7 +1260,7 @@ void ONNXImporter::parseSlice(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
|
|
|
|
|
if (inp_size == 5 && !getBlob(node_proto, 4).empty()) |
|
|
|
|
{ |
|
|
|
|
Mat step_blob = getBlob(node_proto, 4); |
|
|
|
|
Mat step_blob = getIntBlob(node_proto, 4); |
|
|
|
|
CV_Assert(step_blob.total() == start_blob.total()); |
|
|
|
|
steps_ = DictValue::arrayInt(step_blob.begin<int>(), step_blob.total()); |
|
|
|
|
steps.resize(dims, 1); |
|
|
|
@ -1351,7 +1366,7 @@ void ONNXImporter::parseSplit(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
else if (node_proto.input_size() == 2) // opset >= 13, the split will be stored at the second input, instead of the attribute.
|
|
|
|
|
{ |
|
|
|
|
CV_Assert(constBlobs.find(node_proto.input(1)) != constBlobs.end()); |
|
|
|
|
Mat splitsBlob = getBlob(node_proto, 1); |
|
|
|
|
Mat splitsBlob = getIntBlob(node_proto, 1); |
|
|
|
|
int splitSize = splitsBlob.total(); |
|
|
|
|
if (splitSize == 1) |
|
|
|
|
{ |
|
|
|
@ -1650,7 +1665,7 @@ void ONNXImporter::parseLSTM(LayerParams& layerParams, const opencv_onnx::NodePr |
|
|
|
|
|
|
|
|
|
if (4 < lstm_proto.input_size() && !lstm_proto.input(4).empty()) |
|
|
|
|
{ |
|
|
|
|
Mat blob = getBlob(lstm_proto, 4); |
|
|
|
|
Mat blob = getIntBlob(lstm_proto, 4); |
|
|
|
|
CV_Assert(blob.total() == batch_size); |
|
|
|
|
for (MatIterator_<int32_t> it = blob.begin<int32_t>(); it != blob.end<int32_t>(); ++it) |
|
|
|
|
{ |
|
|
|
@ -2101,9 +2116,7 @@ void ONNXImporter::parseSqueeze(LayerParams& layerParams, const opencv_onnx::Nod |
|
|
|
|
{ |
|
|
|
|
if (constBlobs.find(node_proto.input(1)) != constBlobs.end()) |
|
|
|
|
{ |
|
|
|
|
Mat axesMat = getBlob(node_proto, 1); |
|
|
|
|
if (axesMat.depth() == CV_32F) |
|
|
|
|
axesMat.convertTo(axesMat, CV_32S); |
|
|
|
|
Mat axesMat = getIntBlob(node_proto, 1); |
|
|
|
|
size_t axesLen = axesMat.total(); |
|
|
|
|
for (int i = 0; i < axesLen; i++) |
|
|
|
|
{ |
|
|
|
@ -2232,7 +2245,7 @@ void ONNXImporter::parseUnsqueeze(LayerParams& layerParams, const opencv_onnx::N |
|
|
|
|
DictValue axes; |
|
|
|
|
if (node_proto.input_size() == 2) |
|
|
|
|
{ |
|
|
|
|
Mat blob = getBlob(node_proto, 1); |
|
|
|
|
Mat blob = getIntBlob(node_proto, 1); |
|
|
|
|
axes = DictValue::arrayInt(blob.ptr<int>(), blob.total()); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
@ -2301,7 +2314,7 @@ void ONNXImporter::parseExpand(LayerParams& layerParams, const opencv_onnx::Node |
|
|
|
|
CV_CheckTrue(constBlobs.find(node_proto.input(1)) != constBlobs.end(), |
|
|
|
|
"DNN/ONNXImporter-Expand: input shape must be constant"); |
|
|
|
|
|
|
|
|
|
Mat mat_input_shape = getBlob(node_proto, 1); |
|
|
|
|
Mat mat_input_shape = getIntBlob(node_proto, 1); |
|
|
|
|
CV_CheckTypeEQ(mat_input_shape.depth(), CV_32S, "DNN/ONNXImporter-Expand: data type of input shape must be CV_32S"); |
|
|
|
|
for (int i = 0; i < mat_input_shape.total(); ++i) { |
|
|
|
|
CV_Check(i, *(mat_input_shape.ptr<int>() + i) >= 0, "DNN/ONNXImporter-Expand: invalid shape dimension"); |
|
|
|
@ -2336,7 +2349,7 @@ void ONNXImporter::parseReshape(LayerParams& layerParams, const opencv_onnx::Nod |
|
|
|
|
layerParams.type += (depth == CV_8S) ? "Int8" : ""; |
|
|
|
|
|
|
|
|
|
if (node_proto.input_size() == 2) { |
|
|
|
|
Mat blob = getBlob(node_proto, 1); |
|
|
|
|
Mat blob = getIntBlob(node_proto, 1); |
|
|
|
|
CV_Assert(blob.type() == CV_32SC1); |
|
|
|
|
|
|
|
|
|
layerParams.set("dim", DictValue::arrayInt<int*>(blob.ptr<int>(), blob.total())); |
|
|
|
@ -2381,7 +2394,7 @@ void ONNXImporter::parsePad(LayerParams& layerParams, const opencv_onnx::NodePro |
|
|
|
|
{ |
|
|
|
|
// Paddings are in order begin0, begin1, .. beginN, end0, end1, ..., endN.
|
|
|
|
|
// We need to shuffle it to begin0, end0, begin1, end1, ...
|
|
|
|
|
Mat paddings = getBlob(node_proto, 1).reshape(1, 2); |
|
|
|
|
Mat paddings = getIntBlob(node_proto, 1).reshape(1, 2); |
|
|
|
|
paddings = paddings.t(); |
|
|
|
|
layerParams.set("paddings", DictValue::arrayInt(paddings.ptr<int>(), paddings.total())); |
|
|
|
|
|
|
|
|
@ -2411,13 +2424,13 @@ void ONNXImporter::parseShape(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
int dims = static_cast<int>(inpShape.size()); |
|
|
|
|
if (isInput1D) |
|
|
|
|
dims = 1; |
|
|
|
|
Mat shapeMat(1, dims, CV_32S); |
|
|
|
|
Mat shapeMat(1, dims, CV_64S); |
|
|
|
|
bool isDynamicShape = false; |
|
|
|
|
for (int j = 0; j < dims; ++j) |
|
|
|
|
{ |
|
|
|
|
int sz = inpShape[j]; |
|
|
|
|
isDynamicShape |= (sz == 0); |
|
|
|
|
shapeMat.at<int>(j) = sz; |
|
|
|
|
shapeMat.at<int64_t>(j) = sz; |
|
|
|
|
} |
|
|
|
|
shapeMat.dims = 1; // FIXIT Mat 1D
|
|
|
|
|
|
|
|
|
@ -2431,6 +2444,20 @@ void ONNXImporter::parseShape(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
|
|
|
|
|
void ONNXImporter::parseCast(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) |
|
|
|
|
{ |
|
|
|
|
int type; |
|
|
|
|
switch (layerParams.get<int>("to")) |
|
|
|
|
{ |
|
|
|
|
case opencv_onnx::TensorProto_DataType_FLOAT: type = CV_32F; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_UINT8: type = CV_8U; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_UINT16: type = CV_16U; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_FLOAT16: type = CV_16F; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT8: type = CV_8S; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT16: type = CV_16S; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT32: type = CV_32S; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT64: type = CV_64S; break; |
|
|
|
|
default: CV_Error(Error::BadDepth, "Unsupported type"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (constBlobs.find(node_proto.input(0)) != constBlobs.end()) |
|
|
|
|
{ |
|
|
|
|
Mat blob = getBlob(node_proto, 0); |
|
|
|
@ -2438,27 +2465,15 @@ void ONNXImporter::parseCast(LayerParams& layerParams, const opencv_onnx::NodePr |
|
|
|
|
{ |
|
|
|
|
constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), getBlobExtraInfo(node_proto, 0))); |
|
|
|
|
} |
|
|
|
|
int type; |
|
|
|
|
switch (layerParams.get<int>("to")) |
|
|
|
|
{ |
|
|
|
|
case opencv_onnx::TensorProto_DataType_FLOAT: type = CV_32F; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_UINT8: type = CV_8U; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_UINT16: type = CV_16U; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_FLOAT16: type = CV_16F; break; |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT8: |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT16: |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT32: |
|
|
|
|
case opencv_onnx::TensorProto_DataType_INT64: type = CV_32S; break; |
|
|
|
|
default: type = blob.type(); |
|
|
|
|
} |
|
|
|
|
Mat dst; |
|
|
|
|
blob.convertTo(dst, type); |
|
|
|
|
dst.dims = blob.dims; |
|
|
|
|
addConstant(node_proto.output(0), dst); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
layerParams.type = "Identity"; |
|
|
|
|
|
|
|
|
|
layerParams.type = "Cast"; |
|
|
|
|
layerParams.set("outputType", type); |
|
|
|
|
addLayer(layerParams, node_proto); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -2477,7 +2492,7 @@ void ONNXImporter::parseConstantFill(LayerParams& layerParams, const opencv_onnx |
|
|
|
|
else |
|
|
|
|
fill_value = layerParams.get("value", 0); |
|
|
|
|
|
|
|
|
|
MatShape inpShape = getBlob(node_proto, 0); |
|
|
|
|
MatShape inpShape = getIntBlob(node_proto, 0); |
|
|
|
|
for (int i = 0; i < inpShape.size(); i++) |
|
|
|
|
CV_CheckGT(inpShape[i], 0, ""); |
|
|
|
|
Mat tensor(inpShape.size(), &inpShape[0], depth, Scalar(fill_value)); |
|
|
|
@ -2498,15 +2513,12 @@ void ONNXImporter::parseGather(LayerParams& layerParams, const opencv_onnx::Node |
|
|
|
|
std::vector<Mat> inputs, output; |
|
|
|
|
|
|
|
|
|
Mat input = getBlob(node_proto, 0); |
|
|
|
|
int type = input.type(); |
|
|
|
|
input.convertTo(input, CV_32FC1); |
|
|
|
|
inputs.push_back(input); |
|
|
|
|
|
|
|
|
|
Mat indices = getBlob(node_proto, 1); |
|
|
|
|
inputs.push_back(indices); |
|
|
|
|
|
|
|
|
|
runLayer(layerParams, inputs, output); |
|
|
|
|
output.back().convertTo(output.back(), type); |
|
|
|
|
//output.back().dims = std::max(input.dims - real_ndims, 1);
|
|
|
|
|
addConstant(node_proto.output(0), output.back()); |
|
|
|
|
return; |
|
|
|
@ -2547,9 +2559,6 @@ void ONNXImporter::parseGatherElements(LayerParams& layerParams, const opencv_on |
|
|
|
|
std::vector<Mat> inputs, output; |
|
|
|
|
for (size_t i = 0; i < node_proto.input_size(); i++) { |
|
|
|
|
Mat blob = getBlob(node_proto, i); |
|
|
|
|
if (i == 1) { // indices, from int32/int64 to float32 for compatibility
|
|
|
|
|
blob.convertTo(blob, CV_32F); |
|
|
|
|
} |
|
|
|
|
inputs.push_back(blob); |
|
|
|
|
} |
|
|
|
|
runLayer(layerParams, inputs, output); |
|
|
|
@ -2560,9 +2569,6 @@ void ONNXImporter::parseGatherElements(LayerParams& layerParams, const opencv_on |
|
|
|
|
for (size_t i = 0; i < node_proto.input_size(); i++) { |
|
|
|
|
if (constBlobs.find(node_proto.input(i)) != constBlobs.end()) { |
|
|
|
|
Mat blob = getBlob(node_proto, i); |
|
|
|
|
if (i == 1) { // indices, from int32/int64 to float32 for compatibility
|
|
|
|
|
blob.convertTo(blob, CV_32F); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
LayerParams constParams; |
|
|
|
|
constParams.name = node_proto.input(i); |
|
|
|
@ -2691,11 +2697,8 @@ void ONNXImporter::parseResize(LayerParams& layerParams, const opencv_onnx::Node |
|
|
|
|
const std::string& inputSizes = node_proto.input(3); |
|
|
|
|
if (constBlobs.find(inputSizes) != constBlobs.end()) |
|
|
|
|
{ |
|
|
|
|
Mat shapes = getBlob(inputSizes); |
|
|
|
|
Mat shapes = getIntBlob(node_proto, 3); |
|
|
|
|
CV_CheckEQ(shapes.total(), (size_t)4, "HCHW layout is expected"); |
|
|
|
|
CV_CheckDepth(shapes.depth(), shapes.depth() == CV_32S || shapes.depth() == CV_32F, ""); |
|
|
|
|
if (shapes.depth() == CV_32F) |
|
|
|
|
shapes.convertTo(shapes, CV_32S); |
|
|
|
|
layerParams.set("width", shapes.at<int>(3)); |
|
|
|
|
layerParams.set("height", shapes.at<int>(2)); |
|
|
|
|
} |
|
|
|
@ -2809,7 +2812,7 @@ void ONNXImporter::parseCumSum(LayerParams& layerParams, const opencv_onnx::Node |
|
|
|
|
|
|
|
|
|
if (constBlobs.find(input1) != constBlobs.end()) |
|
|
|
|
{ |
|
|
|
|
Mat axis_blob = getBlob(input1); |
|
|
|
|
Mat axis_blob = getIntBlob(node_proto, 1); |
|
|
|
|
CV_Assert(axis_blob.total() == 1u); |
|
|
|
|
layerParams.set("axis", axis_blob.at<int>(0)); |
|
|
|
|
} |
|
|
|
@ -2989,15 +2992,15 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP |
|
|
|
|
// only supports the case which all inputs are constant
|
|
|
|
|
CV_Assert(const_id.size() == 3); |
|
|
|
|
|
|
|
|
|
Mat startMat = getBlob(node_proto, 0); |
|
|
|
|
Mat startMat = getIntBlob(node_proto, 0); |
|
|
|
|
CV_Assert(startMat.type() == CV_32SC1); |
|
|
|
|
int start = startMat.at<int>(0); |
|
|
|
|
|
|
|
|
|
Mat limitMat = getBlob(node_proto, 1); |
|
|
|
|
Mat limitMat = getIntBlob(node_proto, 1); |
|
|
|
|
CV_Assert(limitMat.type() == CV_32SC1); |
|
|
|
|
int limit = limitMat.at<int>(0); |
|
|
|
|
|
|
|
|
|
Mat deltaMat = getBlob(node_proto, 2); |
|
|
|
|
Mat deltaMat = getIntBlob(node_proto, 2); |
|
|
|
|
CV_Assert(deltaMat.type() == CV_32SC1); |
|
|
|
|
int delta = deltaMat.at<int>(0); |
|
|
|
|
|
|
|
|
@ -3043,8 +3046,6 @@ void ONNXImporter::parseScatter(LayerParams& layerParams, const opencv_onnx::Nod |
|
|
|
|
if (layer_id.find(node_proto.input(i)) == layer_id.end()) |
|
|
|
|
{ |
|
|
|
|
Mat blob = getBlob(node_proto, i); |
|
|
|
|
if (i == 1) // indices, from int32/int64 to float32
|
|
|
|
|
blob.convertTo(blob, CV_32F); |
|
|
|
|
|
|
|
|
|
LayerParams constParams; |
|
|
|
|
constParams.name = node_proto.input(i); |
|
|
|
@ -3100,14 +3101,14 @@ void ONNXImporter::parseTile(LayerParams& layerParams, const opencv_onnx::NodePr |
|
|
|
|
|
|
|
|
|
// repeats, treated as paramenter
|
|
|
|
|
std::vector<int> repeats_vec(input0_dims, 1); |
|
|
|
|
Mat input1_blob = getBlob(node_proto, 1); |
|
|
|
|
Mat input1_blob = getIntBlob(node_proto, 1); |
|
|
|
|
if (is_opset_1) |
|
|
|
|
{ |
|
|
|
|
// input1 in tile-1: tiles, 1d tensor of shape [1]
|
|
|
|
|
CV_CheckEQ(input1_blob.total(), 1ull, "ONNX/Tile: tiles must be a 0D tensor or 1D tensor of shape [1]."); |
|
|
|
|
int tiles = input1_blob.at<int>(0); |
|
|
|
|
// input2 in tile-1: axis, 1d tensor of shape [1]
|
|
|
|
|
Mat input2_blob = getBlob(node_proto, 2); |
|
|
|
|
Mat input2_blob = getIntBlob(node_proto, 2); |
|
|
|
|
CV_CheckEQ(input2_blob.total(), 1ull, "ONNX/Tile: axis must be a 0D tensor or 1D tensor of shape [1]."); |
|
|
|
|
int axis = input2_blob.at<int>(0); |
|
|
|
|
repeats_vec[axis] = tiles; |
|
|
|
|