|
|
|
@ -309,6 +309,7 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
if (vtype == TYPE_TORCH) |
|
|
|
|
{ |
|
|
|
|
int index = readInt(); |
|
|
|
|
int numModules = curModule->modules.size(); |
|
|
|
|
readTorchObject(index); |
|
|
|
|
|
|
|
|
|
if (tensors.count(index)) //tensor was readed
|
|
|
|
@ -324,6 +325,14 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
DictValue scalar = DictValue::arrayReal(matCasted.ptr<double>(), matCasted.total()); |
|
|
|
|
scalarParams.set(key, scalar); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
// Only tensors and scalars are supported for table fields.
|
|
|
|
|
// i.e. nn.Inception has field `transfer` which is an
|
|
|
|
|
// activation layer. So we remove added modules as readTorchObject(index).
|
|
|
|
|
while (curModule->modules.size() > numModules) |
|
|
|
|
curModule->modules.pop_back(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (vtype == TYPE_NUMBER) |
|
|
|
|
{ |
|
|
|
@ -469,7 +478,8 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
layerParams.set("torch_index", index); |
|
|
|
|
|
|
|
|
|
if (nnName == "Sequential" || nnName == "Parallel" || |
|
|
|
|
nnName == "Concat" || nnName == "ConcatTable" || nnName == "JoinTable") |
|
|
|
|
nnName == "Concat" || nnName == "ConcatTable" || nnName == "JoinTable" || |
|
|
|
|
nnName == "DepthConcat" || nnName == "Inception") |
|
|
|
|
{ |
|
|
|
|
Module *parentModule = curModule; |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
@ -490,8 +500,12 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
{ |
|
|
|
|
layerParams.set("dimension", scalarParams.get<int>("dimension")); |
|
|
|
|
} |
|
|
|
|
if (nnName == "DepthConcat") |
|
|
|
|
{ |
|
|
|
|
layerParams.set("dimension", scalarParams.get<int>("dimension")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if (nnName == "SpatialConvolution") |
|
|
|
|
else if (nnName == "SpatialConvolution" || nnName == "SpatialConvolutionMM") |
|
|
|
|
{ |
|
|
|
|
newModule->apiType = "Convolution"; |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
@ -507,8 +521,37 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
layerParams.set("num_output", scalarParams.get<int>("nOutputPlane")); |
|
|
|
|
convertTorchKernelsParams(scalarParams, layerParams); |
|
|
|
|
|
|
|
|
|
if (nnName == "SpatialConvolutionMM") |
|
|
|
|
{ |
|
|
|
|
// Split weights from a [ outCh x inCh*kH*kW ] 2D matrix
|
|
|
|
|
// onto a 4D [ outCh x inCh x kH x kW ] blob.
|
|
|
|
|
CV_Assert(layerParams.blobs[0].dims == 2); |
|
|
|
|
const int kernel = layerParams.blobs[0].size[1]; // inCh * kH * kW
|
|
|
|
|
MatShape kernelShape(4); |
|
|
|
|
kernelShape[0] = layerParams.blobs[0].size[0]; // outCh.
|
|
|
|
|
kernelShape[2] = layerParams.get<int>("kernel_h"); |
|
|
|
|
kernelShape[3] = layerParams.get<int>("kernel_w"); |
|
|
|
|
kernelShape[1] = kernel / (kernelShape[2] * kernelShape[3]); // inCh.
|
|
|
|
|
layerParams.blobs[0] = layerParams.blobs[0].reshape(1, kernelShape); |
|
|
|
|
} |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "SpatialLPPooling") |
|
|
|
|
{ |
|
|
|
|
// nn.Sequential {
|
|
|
|
|
// [input -> (1) -> (2) -> output]
|
|
|
|
|
// (1): nn.Sequential {
|
|
|
|
|
// [input -> (1) -> (2) -> (3) -> (4) -> output]
|
|
|
|
|
// (1): nn.Power
|
|
|
|
|
// (2): nn.SpatialAveragePooling(...)
|
|
|
|
|
// (3): nn.MulConstant
|
|
|
|
|
// (4): nn.Power
|
|
|
|
|
// }
|
|
|
|
|
// (2): nn.Sigmoid
|
|
|
|
|
// }
|
|
|
|
|
// nn.SpatialLPPooling is just a table so we skip it.
|
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "SpatialMaxPooling" || nnName == "SpatialAveragePooling") |
|
|
|
|
{ |
|
|
|
|
newModule->apiType = "Pooling"; |
|
|
|
@ -522,6 +565,9 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
layerParams.set("pool", "AVE"); |
|
|
|
|
convertTorchKernelsParams(scalarParams, layerParams); |
|
|
|
|
|
|
|
|
|
CV_Assert(scalarParams.has("ceil_mode")); |
|
|
|
|
layerParams.set("ceil_mode", scalarParams.get<bool>("ceil_mode")); |
|
|
|
|
|
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "Linear") |
|
|
|
@ -541,7 +587,7 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
layerParams.set("num_output", weightBlob.size[0]); |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "Reshape") |
|
|
|
|
else if (nnName == "Reshape" || nnName == "View") |
|
|
|
|
{ |
|
|
|
|
newModule->apiType = "Reshape"; |
|
|
|
|
|
|
|
|
@ -576,15 +622,24 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
newModule->apiType = "BatchNorm"; |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
|
|
|
|
|
CV_Assert(tensorParams.count("running_var") && |
|
|
|
|
tensorParams.count("running_mean")); |
|
|
|
|
layerParams.blobs.push_back(tensorParams["running_mean"].second); |
|
|
|
|
layerParams.blobs.push_back(tensorParams["running_var"].second); |
|
|
|
|
|
|
|
|
|
CV_Assert(scalarParams.has("eps")); |
|
|
|
|
float eps = float(scalarParams.get<double>("eps")); |
|
|
|
|
layerParams.set("eps", eps); |
|
|
|
|
|
|
|
|
|
CV_Assert((tensorParams.count("running_var") || tensorParams.count("running_std")) && |
|
|
|
|
tensorParams.count("running_mean")); |
|
|
|
|
layerParams.blobs.push_back(tensorParams["running_mean"].second); |
|
|
|
|
if (tensorParams.count("running_var")) |
|
|
|
|
{ |
|
|
|
|
layerParams.blobs.push_back(tensorParams["running_var"].second); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
layerParams.blobs.push_back(tensorParams["running_std"].second); |
|
|
|
|
pow(layerParams.blobs.back(), -2, layerParams.blobs.back()); |
|
|
|
|
subtract(layerParams.blobs.back(), eps, layerParams.blobs.back()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (tensorParams.count("weight")) |
|
|
|
|
{ |
|
|
|
|
layerParams.set("has_weight", true); |
|
|
|
@ -642,6 +697,18 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
newModule->apiType = "Identity"; |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "Normalize") |
|
|
|
|
{ |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
CV_Assert(scalarParams.has("p")); |
|
|
|
|
|
|
|
|
|
layerParams.set("p", scalarParams.get<float>("p")); |
|
|
|
|
if (scalarParams.has("eps")) |
|
|
|
|
layerParams.set("eps", scalarParams.get<float>("eps")); |
|
|
|
|
|
|
|
|
|
newModule->apiType = "LPNormalize"; |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "Padding") |
|
|
|
|
{ |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
@ -760,6 +827,46 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
layerParams.set("log_softmax", true); |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "SpatialCrossMapLRN") |
|
|
|
|
{ |
|
|
|
|
newModule->apiType = "LRN"; |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
|
|
|
|
|
CV_Assert(scalarParams.has("alpha")); |
|
|
|
|
CV_Assert(scalarParams.has("beta")); |
|
|
|
|
CV_Assert(scalarParams.has("k")); |
|
|
|
|
CV_Assert(scalarParams.has("size")); |
|
|
|
|
|
|
|
|
|
layerParams.set("norm_region", "ACROSS_CHANNELS"); |
|
|
|
|
layerParams.set("alpha", scalarParams.get<float>("alpha")); |
|
|
|
|
layerParams.set("beta", scalarParams.get<float>("beta")); |
|
|
|
|
layerParams.set("bias", scalarParams.get<float>("k")); |
|
|
|
|
layerParams.set("local_size", scalarParams.get<int>("size")); |
|
|
|
|
layerParams.set("norm_by_size", true); |
|
|
|
|
|
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "Square" || nnName == "Sqrt" || nnName == "Power") |
|
|
|
|
{ |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
|
|
|
|
|
float power; |
|
|
|
|
if (nnName == "Square") power = 2.0f; |
|
|
|
|
else if (nnName == "Sqrt") power = 0.5f; |
|
|
|
|
else if (nnName == "Power") power = scalarParams.get<float>("pow", 1.0f); |
|
|
|
|
|
|
|
|
|
newModule->apiType = "Power"; |
|
|
|
|
layerParams.set("power", power); |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else if (nnName == "MulConstant") |
|
|
|
|
{ |
|
|
|
|
readTorchTable(scalarParams, tensorParams); |
|
|
|
|
CV_Assert(scalarParams.has("constant_scalar")); |
|
|
|
|
newModule->apiType = "Power"; |
|
|
|
|
layerParams.set("scale", scalarParams.get<float>("constant_scalar")); |
|
|
|
|
curModule->modules.push_back(newModule); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
CV_Error(Error::StsNotImplemented, "Unknown nn class \"" + className + "\""); |
|
|
|
@ -816,7 +923,7 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if (module->thName == "Sequential") |
|
|
|
|
if (module->thName == "Sequential" || module->thName == "Inception") |
|
|
|
|
{ |
|
|
|
|
for (size_t i = 0; i < module->modules.size(); i++) |
|
|
|
|
{ |
|
|
|
@ -851,6 +958,30 @@ struct TorchImporter : public ::cv::dnn::Importer |
|
|
|
|
addedModules.push_back(std::make_pair(mergeId, module)); |
|
|
|
|
return mergeId; |
|
|
|
|
} |
|
|
|
|
else if (module->thName == "DepthConcat") |
|
|
|
|
{ |
|
|
|
|
int newId, mergeId; |
|
|
|
|
LayerParams mergeParams; |
|
|
|
|
mergeParams.set("axis", module->params.get<int>("dimension") - 1); |
|
|
|
|
mergeParams.set("padding", true); |
|
|
|
|
|
|
|
|
|
std::vector<int> branchIds; |
|
|
|
|
for (int i = 0; i < (int)module->modules.size(); i++) |
|
|
|
|
{ |
|
|
|
|
newId = fill(module->modules[i], addedModules, prevLayerId, prevOutNum); |
|
|
|
|
branchIds.push_back(newId); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mergeId = net.addLayer(generateLayerName("torchMerge"), "Concat", mergeParams); |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < branchIds.size(); i++) |
|
|
|
|
{ |
|
|
|
|
net.connect(branchIds[i], 0, mergeId, i); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
addedModules.push_back(std::make_pair(mergeId, module)); |
|
|
|
|
return mergeId; |
|
|
|
|
} |
|
|
|
|
else if (module->thName == "Parallel") |
|
|
|
|
{ |
|
|
|
|
int newId, splitId, mergeId, reshapeId; |
|
|
|
|