fix model diagnostic tool

pull/21357/head
rogday 3 years ago
parent a55950865e
commit 0fe7420638
  1. 3
      modules/dnn/include/opencv2/dnn/layer.hpp
  2. 11
      modules/dnn/src/debug_utils.cpp
  3. 7
      modules/dnn/src/dnn.cpp
  4. 4
      modules/dnn/src/dnn_common.hpp
  5. 151
      modules/dnn/src/onnx/onnx_importer.cpp
  6. 6
      modules/dnn/src/tensorflow/tf_importer.cpp

@ -66,6 +66,9 @@ public:
//! Unregisters registered layer with specified type name. Thread-safe. //! Unregisters registered layer with specified type name. Thread-safe.
static void unregisterLayer(const String &type); static void unregisterLayer(const String &type);
//! Check if layer is registered.
static bool isLayerRegistered(const std::string& type);
/** @brief Creates instance of registered layer. /** @brief Creates instance of registered layer.
* @param type type name of creating layer. * @param type type name of creating layer.
* @param params parameters which will be used for layer initialization. * @param params parameters which will be used for layer initialization.

@ -37,11 +37,8 @@ void skipModelImport(bool skip)
void detail::LayerHandler::addMissing(const std::string& name, const std::string& type) void detail::LayerHandler::addMissing(const std::string& name, const std::string& type)
{ {
cv::AutoLock lock(getLayerFactoryMutex());
auto& registeredLayers = getLayerFactoryImpl();
// If we didn't add it, but can create it, it's custom and not missing. // If we didn't add it, but can create it, it's custom and not missing.
if (layers.find(type) == layers.end() && registeredLayers.find(type) != registeredLayers.end()) if (!contains(type) && LayerFactory::isLayerRegistered(type))
{ {
return; return;
} }
@ -51,17 +48,17 @@ void detail::LayerHandler::addMissing(const std::string& name, const std::string
bool detail::LayerHandler::contains(const std::string& type) const bool detail::LayerHandler::contains(const std::string& type) const
{ {
return layers.find(type) != layers.end(); return layers.count(type) != 0;
} }
void detail::LayerHandler::printMissing() void detail::LayerHandler::printMissing() const
{ {
if (layers.empty()) if (layers.empty())
{ {
return; return;
} }
std::stringstream ss; std::ostringstream ss;
ss << "DNN: Not supported types:\n"; ss << "DNN: Not supported types:\n";
for (const auto& type_names : layers) for (const auto& type_names : layers)
{ {

@ -6092,6 +6092,13 @@ void LayerFactory::unregisterLayer(const String &type)
} }
} }
bool LayerFactory::isLayerRegistered(const std::string& type)
{
cv::AutoLock lock(getLayerFactoryMutex());
auto& registeredLayers = getLayerFactoryImpl();
return registeredLayers.find(type) != registeredLayers.end();
}
Ptr<Layer> LayerFactory::createLayerInstance(const String &type, LayerParams& params) Ptr<Layer> LayerFactory::createLayerInstance(const String &type, LayerParams& params)
{ {
CV_TRACE_FUNCTION(); CV_TRACE_FUNCTION();

@ -5,8 +5,8 @@
#ifndef __OPENCV_DNN_COMMON_HPP__ #ifndef __OPENCV_DNN_COMMON_HPP__
#define __OPENCV_DNN_COMMON_HPP__ #define __OPENCV_DNN_COMMON_HPP__
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <opencv2/dnn.hpp> #include <opencv2/dnn.hpp>
@ -59,7 +59,7 @@ class LayerHandler
public: public:
void addMissing(const std::string& name, const std::string& type); void addMissing(const std::string& name, const std::string& type);
bool contains(const std::string& type) const; bool contains(const std::string& type) const;
void printMissing(); void printMissing() const;
protected: protected:
LayerParams getNotImplementedParams(const std::string& name, const std::string& op); LayerParams getNotImplementedParams(const std::string& name, const std::string& op);

@ -48,6 +48,8 @@ CV__DNN_INLINE_NS_BEGIN
extern bool DNN_DIAGNOSTICS_RUN; extern bool DNN_DIAGNOSTICS_RUN;
class ONNXLayerHandler;
class ONNXImporter class ONNXImporter
{ {
opencv_onnx::ModelProto model_proto; opencv_onnx::ModelProto model_proto;
@ -80,7 +82,7 @@ public:
void populateNet(); void populateNet();
protected: protected:
std::unique_ptr<detail::LayerHandler> missingLayerHandler; std::unique_ptr<ONNXLayerHandler> layerHandler;
Net& dstNet; Net& dstNet;
opencv_onnx::GraphProto graph_proto; opencv_onnx::GraphProto graph_proto;
@ -98,11 +100,14 @@ protected:
void handleNode(const opencv_onnx::NodeProto& node_proto); void handleNode(const opencv_onnx::NodeProto& node_proto);
private: private:
friend class ONNXLayerHandler;
typedef void (ONNXImporter::*ONNXImporterNodeParser)(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); typedef void (ONNXImporter::*ONNXImporterNodeParser)(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
typedef std::map<std::string, ONNXImporterNodeParser> DispatchMap; typedef std::map<std::string, ONNXImporterNodeParser> DispatchMap;
typedef std::map<std::string, DispatchMap> DomainDispatchMap; typedef std::map<std::string, DispatchMap> DomainDispatchMap;
DomainDispatchMap domain_dispatch_map; DomainDispatchMap domain_dispatch_map;
std::string getLayerTypeDomain(const opencv_onnx::NodeProto& node_proto);
const DispatchMap& getDispatchMap(const opencv_onnx::NodeProto& node_proto);
void buildDispatchMap_ONNX_AI(int opset_version); void buildDispatchMap_ONNX_AI(int opset_version);
void buildDispatchMap_COM_MICROSOFT(int opset_version); void buildDispatchMap_COM_MICROSOFT(int opset_version);
@ -156,6 +161,7 @@ private:
void parseSoftMax (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseSoftMax (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseDetectionOutput (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseDetectionOutput (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseCumSum (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseCumSum (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseSimpleLayers (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
// Domain: com.microsoft // Domain: com.microsoft
// URL: https://github.com/microsoft/onnxruntime/blob/master/docs/ContribOperators.md // URL: https://github.com/microsoft/onnxruntime/blob/master/docs/ContribOperators.md
@ -178,9 +184,38 @@ private:
const std::string str_domain_ai_onnx = "ai.onnx"; const std::string str_domain_ai_onnx = "ai.onnx";
}; };
class ONNXLayerHandler : public detail::LayerHandler
{
public:
explicit ONNXLayerHandler(ONNXImporter* importer_);
void fillRegistry(const opencv_onnx::GraphProto& net);
protected:
ONNXImporter* importer;
};
ONNXLayerHandler::ONNXLayerHandler(ONNXImporter* importer_) : importer(importer_){}
void ONNXLayerHandler::fillRegistry(const opencv_onnx::GraphProto &net)
{
int layersSize = net.node_size();
for (int li = 0; li < layersSize; li++) {
const opencv_onnx::NodeProto &node_proto = net.node(li);
const std::string& name = node_proto.output(0);
const std::string& type = node_proto.op_type();
const std::string& layer_type_domain = importer->getLayerTypeDomain(node_proto);
const auto& dispatch = importer->getDispatchMap(node_proto);
if (dispatch.find(type) == dispatch.end())
{
addMissing(name, cv::format("%s.%s", layer_type_domain.c_str(), type.c_str()));
}
}
printMissing();
}
ONNXImporter::ONNXImporter(Net& net, const char *onnxFile) ONNXImporter::ONNXImporter(Net& net, const char *onnxFile)
: missingLayerHandler(DNN_DIAGNOSTICS_RUN ? new detail::LayerHandler() : nullptr) : layerHandler(DNN_DIAGNOSTICS_RUN ? new ONNXLayerHandler(this) : nullptr)
, dstNet(net) , dstNet(net)
, onnx_opset(0) , onnx_opset(0)
{ {
@ -203,7 +238,7 @@ ONNXImporter::ONNXImporter(Net& net, const char *onnxFile)
} }
ONNXImporter::ONNXImporter(Net& net, const char* buffer, size_t sizeBuffer) ONNXImporter::ONNXImporter(Net& net, const char* buffer, size_t sizeBuffer)
: missingLayerHandler(DNN_DIAGNOSTICS_RUN ? new detail::LayerHandler() : nullptr) : layerHandler(DNN_DIAGNOSTICS_RUN ? new ONNXLayerHandler(this) : nullptr)
, dstNet(net) , dstNet(net)
, onnx_opset(0) , onnx_opset(0)
{ {
@ -795,6 +830,7 @@ void ONNXImporter::populateNet()
if (DNN_DIAGNOSTICS_RUN) { if (DNN_DIAGNOSTICS_RUN) {
CV_LOG_INFO(NULL, "DNN/ONNX: start diagnostic run!"); CV_LOG_INFO(NULL, "DNN/ONNX: start diagnostic run!");
layerHandler->fillRegistry(graph_proto);
} }
for(int li = 0; li < layersSize; li++) for(int li = 0; li < layersSize; li++)
@ -806,54 +842,48 @@ void ONNXImporter::populateNet()
CV_LOG_DEBUG(NULL, (DNN_DIAGNOSTICS_RUN ? "DNN/ONNX: diagnostic run completed!" : "DNN/ONNX: import completed!")); CV_LOG_DEBUG(NULL, (DNN_DIAGNOSTICS_RUN ? "DNN/ONNX: diagnostic run completed!" : "DNN/ONNX: import completed!"));
} }
std::string ONNXImporter::getLayerTypeDomain(const opencv_onnx::NodeProto& node_proto)
{
if (!node_proto.has_domain())
return str_domain_ai_onnx;
const std::string& domain = node_proto.domain();
if (domain.empty())
return str_domain_ai_onnx;
return domain;
}
const ONNXImporter::DispatchMap& ONNXImporter::getDispatchMap(const opencv_onnx::NodeProto& node_proto)
{
static DispatchMap empty_map;
const std::string& layer_type_domain = getLayerTypeDomain(node_proto);
auto it = domain_dispatch_map.find(layer_type_domain);
if (it == domain_dispatch_map.end())
{
return empty_map;
}
return it->second;
}
void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto) void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto)
{ {
CV_Assert(node_proto.output_size() >= 1); CV_Assert(node_proto.output_size() >= 1);
const std::string& name = node_proto.output(0); const std::string& name = node_proto.output(0);
const std::string& layer_type = node_proto.op_type(); const std::string& layer_type = node_proto.op_type();
const std::string& layer_type_domain = [&]() const std::string& layer_type_domain = getLayerTypeDomain(node_proto);
{ const auto& dispatch = getDispatchMap(node_proto);
if (!node_proto.has_domain())
return str_domain_ai_onnx; CV_LOG_DEBUG(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and "
const std::string& domain = node_proto.domain(); << node_proto.output_size() << " outputs: "
if (domain.empty()) << cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str())
return str_domain_ai_onnx; << cv::format(" from %sdomain='", onnx_opset_map.count(layer_type_domain) == 1 ? "" : "undeclared ")
return domain; << layer_type_domain << "'"
}(); );
const auto& dispatch = [&]()
if (dispatch.empty())
{ {
if (layer_type_domain != str_domain_ai_onnx) CV_LOG_WARNING(NULL, "DNN/ONNX: missing dispatch map for domain='" << layer_type_domain << "'");
{ }
if (onnx_opset_map.find(layer_type_domain) == onnx_opset_map.end())
{
CV_LOG_WARNING(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: "
<< cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str())
<< " from undeclared domain='" << layer_type_domain << "'"
);
}
else
{
CV_LOG_DEBUG(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: "
<< cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str())
<< " from domain='" << layer_type_domain << "'"
);
}
auto it = domain_dispatch_map.find(layer_type_domain);
if (it == domain_dispatch_map.end())
{
CV_LOG_WARNING(NULL, "DNN/ONNX: missing dispatch map for domain='" << layer_type_domain << "'");
return DispatchMap();
}
return it->second;
}
else
{
CV_LOG_DEBUG(NULL, "DNN/ONNX: processing node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: "
<< cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str())
);
return domain_dispatch_map[str_domain_ai_onnx];
}
}();
LayerParams layerParams; LayerParams layerParams;
try try
@ -2871,6 +2901,15 @@ void ONNXImporter::parseCumSum(LayerParams& layerParams, const opencv_onnx::Node
addLayer(layerParams, node_proto); addLayer(layerParams, node_proto);
} }
void ONNXImporter::parseSimpleLayers(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
for (int j = 0; j < node_proto.input_size(); j++) {
if (layer_id.find(node_proto.input(j)) == layer_id.end())
layerParams.blobs.push_back(getBlob(node_proto, j));
}
addLayer(layerParams, node_proto);
}
void ONNXImporter::parseCustomLayer(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) void ONNXImporter::parseCustomLayer(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{ {
const std::string& name = layerParams.name; const std::string& name = layerParams.name;
@ -2886,20 +2925,11 @@ void ONNXImporter::parseCustomLayer(LayerParams& layerParams, const opencv_onnx:
} }
} }
CV_LOG_INFO(NULL, "DNN/ONNX: unknown node type, try using custom handler for node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: " CV_LOG_IF_INFO(NULL, !LayerFactory::isLayerRegistered(layer_type), "DNN/ONNX: unknown node type, try using custom handler for node with " << node_proto.input_size() << " inputs and " << node_proto.output_size() << " outputs: "
<< cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str()) << cv::format("[%s]:(%s)", layer_type.c_str(), name.c_str())
); );
if (missingLayerHandler) parseSimpleLayers(layerParams, node_proto);
{
missingLayerHandler->addMissing(layerParams.name, layerParams.type);
}
for (int j = 0; j < node_proto.input_size(); j++) {
if (layer_id.find(node_proto.input(j)) == layer_id.end())
layerParams.blobs.push_back(getBlob(node_proto, j));
}
addLayer(layerParams, node_proto);
} }
void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
@ -3349,6 +3379,15 @@ void ONNXImporter::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["DetectionOutput"] = &ONNXImporter::parseDetectionOutput; dispatch["DetectionOutput"] = &ONNXImporter::parseDetectionOutput;
dispatch["CumSum"] = &ONNXImporter::parseCumSum; dispatch["CumSum"] = &ONNXImporter::parseCumSum;
std::vector<std::string> simpleLayers{"Acos", "Acosh", "Asin", "Asinh", "Atan", "Atanh", "Ceil", "Celu", "Cos",
"Cosh", "Dropout", "Erf", "Exp", "Floor", "HardSigmoid", "HardSwish",
"Identity", "Log", "Round", "Selu", "Sigmoid", "Sin", "Sinh", "Softmax",
"Softplus", "Softsign", "Sqrt", "Tan", "ThresholdedRelu"};
for (const auto& name : simpleLayers)
{
dispatch[name] = &ONNXImporter::parseSimpleLayers;
}
// ai.onnx: opset 10+ // ai.onnx: opset 10+
dispatch["QuantizeLinear"] = dispatch["DequantizeLinear"] = &ONNXImporter::parseQuantDequant; dispatch["QuantizeLinear"] = dispatch["DequantizeLinear"] = &ONNXImporter::parseQuantDequant;
dispatch["QLinearConv"] = &ONNXImporter::parseQConv; dispatch["QLinearConv"] = &ONNXImporter::parseQConv;

@ -3090,10 +3090,8 @@ void TFImporter::populateNet()
{ {
const tensorflow::NodeDef& layer = net.node(li); const tensorflow::NodeDef& layer = net.node(li);
const std::string name = layer.name(); CV_LOG_DEBUG(NULL, "DNN/TF: processing node (" << li << "/" << layersSize << ") with " << layer.input_size() << " inputs: "
const std::string type = layer.op(); << cv::format("[%s]:(%s)", layer.op().c_str(), layer.name().c_str()));
const int ninputs = layer.input_size();
CV_LOG_DEBUG(NULL, "DNN/TF: (" << li << "/" << layersSize << ") Parse layer " << name << " @ " << type << " with " << ninputs << " inputs");
parseNode(layer); parseNode(layer);
} }

Loading…
Cancel
Save