mirror of https://github.com/opencv/opencv.git
Open Source Computer Vision Library
https://opencv.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2123 lines
70 KiB
2123 lines
70 KiB
// This file is part of OpenCV project. |
|
// It is subject to the license terms in the LICENSE file found in the top-level directory |
|
// of this distribution and at http://opencv.org/license.html. |
|
|
|
#include "precomp.hpp" |
|
|
|
#include "net_impl.hpp" |
|
|
|
namespace cv { |
|
namespace dnn { |
|
CV__DNN_INLINE_NS_BEGIN |
|
|
|
|
|
static int g_networkId = 0; |
|
|
|
|
|
detail::NetImplBase::NetImplBase() |
|
: networkId(CV_XADD(&g_networkId, 1)) |
|
, networkDumpCounter(0) |
|
, dumpLevel(getParam_DNN_NETWORK_DUMP()) |
|
{ |
|
// nothing |
|
} |
|
|
|
|
|
std::string detail::NetImplBase::getDumpFileNameBase() const |
|
{ |
|
std::string dumpFileNameBase = cv::format("ocv_dnn_net_%05d_%02d", networkId, networkDumpCounter++); |
|
return dumpFileNameBase; |
|
} |
|
|
|
|
|
Net::Impl::~Impl() |
|
{ |
|
#ifdef HAVE_VULKAN |
|
if (context) |
|
context->reset(); |
|
#endif |
|
} |
|
|
|
|
|
Net::Impl::Impl() |
|
{ |
|
// allocate fake net input layer |
|
netInputLayer = Ptr<DataLayer>(new DataLayer()); |
|
LayerData& inpl = layers.insert(make_pair(0, LayerData())).first->second; |
|
inpl.id = 0; |
|
netInputLayer->name = inpl.name = "_input"; |
|
inpl.type = "__NetInputLayer__"; |
|
inpl.layerInstance = netInputLayer; |
|
layerNameToId.insert(std::make_pair(inpl.name, inpl.id)); |
|
|
|
lastLayerId = 0; |
|
netWasAllocated = false; |
|
netWasQuantized = false; |
|
fusion = true; |
|
isAsync = false; |
|
preferableBackend = (Backend)getParam_DNN_BACKEND_DEFAULT(); |
|
preferableTarget = DNN_TARGET_CPU; |
|
hasDynamicShapes = false; |
|
useWinograd = true; |
|
} |
|
|
|
|
|
bool Net::Impl::empty() const |
|
{ |
|
return layers.size() <= 1; // first layer is default Data layer |
|
} |
|
|
|
|
|
void Net::Impl::clear() |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
MapIdToLayerData::iterator it; |
|
for (it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
if (it->second.id != 0) |
|
{ |
|
it->second.inputBlobs.clear(); |
|
it->second.outputBlobs.clear(); |
|
it->second.internals.clear(); |
|
} |
|
it->second.skip = false; |
|
// it->second.consumers.clear(); |
|
Ptr<Layer> currLayer = it->second.layerInstance; |
|
|
|
if (currLayer.empty()) |
|
continue; |
|
|
|
currLayer->unsetAttached(); |
|
} |
|
netWasAllocated = false; |
|
layersTimings.clear(); |
|
} |
|
|
|
|
|
void Net::Impl::validateBackendAndTarget() |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
CV_Assert(preferableBackend != DNN_BACKEND_OPENCV || |
|
preferableTarget == DNN_TARGET_CPU || |
|
preferableTarget == DNN_TARGET_CPU_FP16 || |
|
preferableTarget == DNN_TARGET_OPENCL || |
|
preferableTarget == DNN_TARGET_OPENCL_FP16); |
|
CV_Assert(preferableBackend != DNN_BACKEND_HALIDE || |
|
preferableTarget == DNN_TARGET_CPU || |
|
preferableTarget == DNN_TARGET_OPENCL); |
|
#ifdef HAVE_WEBNN |
|
if (preferableBackend == DNN_BACKEND_WEBNN) |
|
{ |
|
CV_Assert(preferableTarget == DNN_TARGET_CPU || |
|
preferableTarget == DNN_TARGET_OPENCL); |
|
} |
|
#endif |
|
CV_Assert(preferableBackend != DNN_BACKEND_VKCOM || |
|
preferableTarget == DNN_TARGET_VULKAN); |
|
CV_Assert(preferableBackend != DNN_BACKEND_CUDA || |
|
IS_DNN_CUDA_TARGET(preferableTarget)); |
|
CV_Assert(preferableBackend != DNN_BACKEND_TIMVX || |
|
preferableTarget == DNN_TARGET_NPU); |
|
|
|
CV_Assert(preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && "Inheritance internal error"); |
|
} |
|
|
|
void Net::Impl::setUpNet(const std::vector<LayerPin>& blobsToKeep_) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
if (dumpLevel && networkDumpCounter == 0) |
|
{ |
|
dumpNetworkToFile(); |
|
} |
|
|
|
validateBackendAndTarget(); |
|
|
|
if (!netWasAllocated || this->blobsToKeep != blobsToKeep_) |
|
{ |
|
if (preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget)) |
|
#ifndef HAVE_OPENCL |
|
{ |
|
CV_LOG_WARNING(NULL, "DNN: OpenCL target is not available in this OpenCV build, switching to CPU."); |
|
preferableTarget = DNN_TARGET_CPU; |
|
} |
|
#else |
|
{ |
|
if (!getParam_DNN_OPENCL_ALLOW_ALL_DEVICES()) |
|
{ |
|
// Current implementation is only valid for GPU (#11494) |
|
if (ocl::Device::getDefault().type() != ocl::Device::TYPE_GPU) |
|
{ |
|
CV_LOG_WARNING(NULL, "DNN: OpenCL target is not supported with current OpenCL device (tested with GPUs only), switching to CPU."); |
|
preferableTarget = DNN_TARGET_CPU; |
|
} |
|
else if (preferableTarget == DNN_TARGET_OPENCL_FP16 && !ocl::Device::getDefault().isIntel()) |
|
{ |
|
CV_LOG_WARNING(NULL, |
|
"DNN: OpenCL target with fp16 precision is not supported " |
|
"with current OpenCL device (tested with Intel GPUs only), " |
|
"switching to OpenCL with fp32 precision."); |
|
preferableTarget = DNN_TARGET_OPENCL; |
|
} |
|
} |
|
} |
|
#endif |
|
if (preferableBackend == DNN_BACKEND_VKCOM && !haveVulkan()) |
|
{ |
|
preferableBackend = DNN_BACKEND_OPENCV; |
|
preferableTarget = DNN_TARGET_CPU; |
|
} |
|
|
|
if (preferableBackend == DNN_BACKEND_CUDA && !haveCUDA()) |
|
{ |
|
#ifdef HAVE_CUDA |
|
CV_LOG_WARNING(NULL, "unable to use CUDA backend; switching to CPU"); |
|
#else |
|
CV_LOG_WARNING(NULL, "DNN module was not built with CUDA backend; switching to CPU"); |
|
#endif |
|
preferableBackend = DNN_BACKEND_OPENCV; |
|
preferableTarget = DNN_TARGET_CPU; |
|
} |
|
|
|
if (preferableBackend == DNN_BACKEND_TIMVX && !haveTimVX()) |
|
{ |
|
preferableBackend = DNN_BACKEND_OPENCV; |
|
preferableTarget = DNN_TARGET_CPU; |
|
} |
|
|
|
clear(); |
|
|
|
if (hasDynamicShapes) |
|
{ |
|
updateLayersShapes(); |
|
} |
|
|
|
this->blobsToKeep = blobsToKeep_; |
|
|
|
allocateLayers(blobsToKeep_); |
|
|
|
MapIdToLayerData::iterator it = layers.find(0); |
|
CV_Assert(it != layers.end()); |
|
it->second.skip = netInputLayer->skip; |
|
|
|
initBackend(blobsToKeep_); |
|
|
|
if (!netWasAllocated) |
|
{ |
|
#ifdef HAVE_HALIDE |
|
if (preferableBackend == DNN_BACKEND_HALIDE) |
|
compileHalide(); |
|
#else |
|
CV_Assert(preferableBackend != DNN_BACKEND_HALIDE); |
|
#endif |
|
} |
|
|
|
netWasAllocated = true; |
|
|
|
if (dumpLevel) |
|
{ |
|
dumpNetworkToFile(); |
|
} |
|
} |
|
} |
|
|
|
|
|
Ptr<Layer> Net::Impl::getLayer(int layerId) const |
|
{ |
|
LayerData& ld = getLayerData(layerId); |
|
return getLayerInstance(ld); |
|
} |
|
|
|
|
|
Ptr<Layer> Net::Impl::getLayer(const LayerId& layerId) const |
|
{ |
|
LayerData& ld = getLayerData(layerId); |
|
return getLayerInstance(ld); |
|
} |
|
|
|
|
|
int Net::Impl::getLayerId(const String& layerName) const |
|
{ |
|
std::map<String, int>::const_iterator it = layerNameToId.find(layerName); |
|
return (it != layerNameToId.end()) ? it->second : -1; |
|
} |
|
|
|
|
|
int Net::Impl::getLayerId(int id) const |
|
{ |
|
MapIdToLayerData::const_iterator it = layers.find(id); |
|
return (it != layers.end()) ? id : -1; |
|
} |
|
|
|
|
|
int Net::Impl::getLayerId(DictValue& layerDesc) const |
|
{ |
|
if (layerDesc.isInt()) |
|
return getLayerId(layerDesc.get<int>()); |
|
else if (layerDesc.isString()) |
|
return getLayerId(layerDesc.get<String>()); |
|
|
|
CV_Assert(layerDesc.isInt() || layerDesc.isString()); |
|
return -1; |
|
} |
|
|
|
|
|
String Net::Impl::getLayerName(int id) const |
|
{ |
|
MapIdToLayerData::const_iterator it = layers.find(id); |
|
return (it != layers.end()) ? it->second.name : "(unknown layer)"; |
|
} |
|
|
|
|
|
LayerData& Net::Impl::getLayerData(int id) const |
|
{ |
|
MapIdToLayerData::const_iterator it = layers.find(id); |
|
|
|
if (it == layers.end()) |
|
CV_Error(Error::StsObjectNotFound, format("Layer with requested id=%d not found", id)); |
|
|
|
return const_cast<LayerData&>(it->second); |
|
} |
|
|
|
|
|
LayerData& Net::Impl::getLayerData(const String& layerName) const |
|
{ |
|
int id = getLayerId(layerName); |
|
|
|
if (id < 0) |
|
CV_Error(Error::StsError, "Requested layer \"" + layerName + "\" not found"); |
|
|
|
return getLayerData(id); |
|
} |
|
|
|
|
|
LayerData& Net::Impl::getLayerData(const DictValue& layerDesc) const |
|
{ |
|
CV_Assert(layerDesc.isInt() || layerDesc.isString()); |
|
if (layerDesc.isInt()) |
|
return getLayerData(layerDesc.get<int>()); |
|
else /*if (layerDesc.isString())*/ |
|
return getLayerData(layerDesc.get<String>()); |
|
} |
|
|
|
|
|
/*static*/ |
|
void Net::Impl::addLayerInput(LayerData& ld, int inNum, LayerPin from) |
|
{ |
|
if ((int)ld.inputBlobsId.size() <= inNum) |
|
{ |
|
ld.inputBlobsId.resize(inNum + 1); |
|
} |
|
else |
|
{ |
|
LayerPin storedFrom = ld.inputBlobsId[inNum]; |
|
if (storedFrom.valid() && !storedFrom.equal(from)) |
|
CV_Error(Error::StsError, format("Input #%d of layer \"%s\" already was connected", |
|
inNum, ld.name.c_str())); |
|
} |
|
|
|
ld.inputBlobsId[inNum] = from; |
|
} |
|
|
|
|
|
int Net::Impl::resolvePinOutputName(LayerData& ld, const String& outName) const |
|
{ |
|
if (outName.empty()) |
|
return 0; |
|
return getLayerInstance(ld)->outputNameToIndex(outName); |
|
} |
|
|
|
|
|
LayerPin Net::Impl::getPinByAlias(const String& layerName) const |
|
{ |
|
LayerPin pin; |
|
pin.lid = (layerName.empty()) ? 0 : getLayerId(layerName); |
|
|
|
if (pin.lid >= 0) |
|
pin.oid = resolvePinOutputName(getLayerData(pin.lid), layerName); |
|
|
|
return pin; |
|
} |
|
|
|
|
|
std::vector<LayerPin> Net::Impl::getLayerOutPins(const String& layerName) const |
|
{ |
|
int lid = (layerName.empty()) ? 0 : getLayerId(layerName); |
|
|
|
MapIdToLayerData::const_iterator it = layers.find(lid); |
|
if (it == layers.end()) |
|
CV_Error_(Error::StsOutOfRange, ("Layer #%d is not valid", lid)); |
|
const size_t nOutputs = it->second.outputBlobs.size(); |
|
|
|
std::vector<LayerPin> pins; |
|
for (int i = 0; i < nOutputs; i++) |
|
{ |
|
pins.push_back(LayerPin(lid, i)); |
|
} |
|
|
|
return pins; |
|
} |
|
|
|
|
|
// FIXIT remove dtype |
|
int Net::Impl::addLayer(const String& name, const String& type, const int& dtype, LayerParams& params) |
|
{ |
|
int id = getLayerId(name); |
|
if (id >= 0) |
|
{ |
|
if (!DNN_DIAGNOSTICS_RUN || type != "NotImplemented") |
|
{ |
|
CV_Error(Error::StsBadArg, "Layer \"" + name + "\" already into net"); |
|
return -1; |
|
} |
|
else |
|
{ |
|
LayerData& ld = layers.find(id)->second; |
|
ld.type = type; |
|
ld.params = params; |
|
return -1; |
|
} |
|
} |
|
|
|
id = ++lastLayerId; |
|
layerNameToId.insert(std::make_pair(name, id)); |
|
layers.insert(std::make_pair(id, LayerData(id, name, type, dtype, params))); |
|
if (params.get<bool>("has_dynamic_shapes", false)) |
|
hasDynamicShapes = true; |
|
|
|
if (dtype == CV_8S) |
|
netWasQuantized = true; |
|
|
|
return id; |
|
} |
|
|
|
|
|
int Net::Impl::addLayerToPrev(const String& name, const String& type, const int& dtype, LayerParams& params) |
|
{ |
|
int prvLid = lastLayerId; |
|
int newLid = addLayer(name, type, dtype, params); |
|
connect(prvLid, 0, newLid, 0); |
|
return newLid; |
|
} |
|
|
|
|
|
void Net::Impl::connect(int outLayerId, int outNum, int inLayerId, int inNum) |
|
{ |
|
CV_Assert(outLayerId < inLayerId); |
|
LayerData& ldOut = getLayerData(outLayerId); |
|
LayerData& ldInp = getLayerData(inLayerId); |
|
|
|
addLayerInput(ldInp, inNum, LayerPin(outLayerId, outNum)); |
|
ldOut.requiredOutputs.insert(outNum); |
|
ldOut.consumers.push_back(LayerPin(inLayerId, outNum)); |
|
|
|
CV_LOG_VERBOSE(NULL, 0, "DNN: connect(" << outLayerId << ":" << outNum << " ==> " << inLayerId << ":" << inNum << ")"); |
|
} |
|
|
|
|
|
int Net::Impl::registerOutput(const std::string& outputName, int layerId, int outputPort) |
|
{ |
|
int checkLayerId = getLayerId(outputName); |
|
if (checkLayerId >= 0) |
|
{ |
|
if (checkLayerId == layerId) |
|
{ |
|
if (outputPort == 0) |
|
{ |
|
// layer name correlates with its output name |
|
CV_LOG_DEBUG(NULL, "DNN: register output='" << outputName << "': reuse layer with the same name and id=" << layerId << " to be linked"); |
|
outputNameToId.insert(std::make_pair(outputName, layerId)); |
|
return checkLayerId; |
|
} |
|
} |
|
CV_Error_(Error::StsBadArg, ("Layer with name='%s' already exists id=%d (to be linked with %d:%d)", outputName.c_str(), checkLayerId, layerId, outputPort)); |
|
} |
|
#if 0 // TODO |
|
if (outputPort == 0) |
|
// make alias only, need to adopt getUnconnectedOutLayers() call |
|
#endif |
|
LayerParams outputLayerParams; |
|
outputLayerParams.name = outputName; |
|
outputLayerParams.type = "Identity"; |
|
int dtype = CV_32F; // FIXIT remove |
|
int outputLayerId = addLayer(outputLayerParams.name, outputLayerParams.type, dtype, outputLayerParams); |
|
connect(layerId, outputPort, outputLayerId, 0); |
|
CV_LOG_DEBUG(NULL, "DNN: register output='" << outputName << "' id=" << outputLayerId << " defined as " << layerId << ":" << outputPort); |
|
outputNameToId.insert(std::make_pair(outputName, outputLayerId)); |
|
return outputLayerId; |
|
} |
|
|
|
|
|
void Net::Impl::allocateLayer(int lid, const LayersShapesMap& layersShapes) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
LayerData& ld = layers[lid]; |
|
|
|
// already allocated |
|
if (ld.flag) |
|
return; |
|
|
|
size_t ninputs = ld.inputBlobsId.size(); |
|
#if 0 |
|
printf("layer %s:", ld.name.c_str()); |
|
for (size_t i = 0; i < ninputs; i++) |
|
{ |
|
int inp_lid = ld.inputBlobsId[i].lid; |
|
LayerData &inp_ld = layers[inp_lid]; |
|
int inp_outputs = (int)inp_ld.outputBlobs.size(); |
|
std::cout << " " << inp_ld.name << "(" << inp_outputs; |
|
|
|
for( int j = 0; j < inp_outputs; j++ ) |
|
{ |
|
std::cout << (j == 0 ? ": " : ", ") << inp_ld.outputBlobs[j].size; |
|
} |
|
std::cout << ")"; |
|
} |
|
printf("\n"); |
|
#endif |
|
|
|
// determine parent layers |
|
for (size_t i = 0; i < ninputs; i++) |
|
ld.inputLayersId.insert(ld.inputBlobsId[i].lid); |
|
|
|
// allocate parents |
|
for (std::set<int>::const_iterator i = ld.inputLayersId.begin(); i != ld.inputLayersId.end(); i++) |
|
allocateLayer(*i, layersShapes); |
|
|
|
// bind inputs |
|
if (ld.id == 0) // DataLayer |
|
{ |
|
ninputs = netInputLayer->inputsData.size(); |
|
ld.inputBlobsWrappers.resize(ninputs); |
|
for (size_t i = 0; i < ninputs; i++) |
|
ld.inputBlobsWrappers[i] = wrap(netInputLayer->inputsData[i]); |
|
} |
|
else |
|
{ |
|
ld.inputBlobs.resize(ninputs); |
|
ld.inputBlobsWrappers.resize(ninputs); |
|
for (size_t i = 0; i < ninputs; i++) |
|
{ |
|
LayerPin from = ld.inputBlobsId[i]; |
|
CV_Assert(from.valid()); |
|
CV_DbgAssert(layers.count(from.lid) && (int)layers[from.lid].outputBlobs.size() > from.oid); |
|
ld.inputBlobs[i] = &layers[from.lid].outputBlobs[from.oid]; |
|
ld.inputBlobsWrappers[i] = layers[from.lid].outputBlobsWrappers[from.oid]; |
|
} |
|
} |
|
|
|
LayersShapesMap::const_iterator layerShapesIt = layersShapes.find(lid); |
|
|
|
CV_Assert(layerShapesIt != layersShapes.end()); |
|
|
|
if (preferableBackend == DNN_BACKEND_OPENCV && preferableTarget == DNN_TARGET_OPENCL_FP16 && ld.dtype == CV_32F) |
|
ld.dtype = CV_16S; |
|
|
|
std::vector<LayerPin> pinsForInternalBlobs; |
|
blobManager.allocateBlobsForLayer(ld, layerShapesIt->second, pinsForInternalBlobs); |
|
ld.outputBlobsWrappers.resize(ld.outputBlobs.size()); |
|
for (int i = 0; i < ld.outputBlobs.size(); ++i) |
|
ld.outputBlobsWrappers[i] = wrap(ld.outputBlobs[i]); |
|
|
|
/* CUDA & CANN backend has its own system for internal blobs; we don't need these */ |
|
ld.internalBlobsWrappers.resize((preferableBackend == DNN_BACKEND_CUDA || preferableBackend == DNN_BACKEND_TIMVX || preferableBackend == DNN_BACKEND_CANN) ? 0 : ld.internals.size()); |
|
for (int i = 0; i < ld.internalBlobsWrappers.size(); ++i) |
|
ld.internalBlobsWrappers[i] = wrap(ld.internals[i]); |
|
|
|
Ptr<Layer> layerPtr = getLayerInstance(ld); |
|
{ |
|
std::vector<Mat> inps(ld.inputBlobs.size()); |
|
for (int i = 0; i < ld.inputBlobs.size(); ++i) |
|
{ |
|
inps[i] = *ld.inputBlobs[i]; |
|
} |
|
layerPtr->finalize(inps, ld.outputBlobs); |
|
layerPtr->preferableTarget = preferableTarget; |
|
#if 0 |
|
std::cout << "\toutputs:"; |
|
size_t noutputs = ld.outputBlobs.size(); |
|
for (size_t j = 0; j < noutputs; j++) |
|
{ |
|
std::cout << (j == 0 ? " " : ", ") << ld.outputBlobs[j].size; |
|
} |
|
std::cout << "\n"; |
|
#endif |
|
} |
|
|
|
// After allocation of layer, we decrease counters to it's input blobs. |
|
blobManager.releaseReferences(ld.inputBlobsId); |
|
blobManager.releaseReferences(pinsForInternalBlobs); |
|
|
|
ld.flag = 1; |
|
} |
|
|
|
|
|
void Net::Impl::allocateLayers(const std::vector<LayerPin>& blobsToKeep_) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) |
|
it->second.flag = 0; |
|
|
|
CV_Assert(!layers[0].outputBlobs.empty()); |
|
ShapesVec inputShapes; |
|
for (int i = 0; i < layers[0].outputBlobs.size(); i++) |
|
{ |
|
Mat& inp = layers[0].outputBlobs[i]; |
|
CV_Assert(inp.total()); |
|
if (preferableBackend == DNN_BACKEND_OPENCV && |
|
preferableTarget == DNN_TARGET_OPENCL_FP16 && |
|
layers[0].dtype == CV_32F) |
|
{ |
|
layers[0].outputBlobs[i].create(inp.dims, inp.size, CV_16S); |
|
} |
|
inputShapes.push_back(shape(inp)); |
|
} |
|
LayersShapesMap layersShapes; |
|
getLayersShapes(inputShapes, layersShapes); |
|
|
|
blobManager.reset(); |
|
backendWrappers.clear(); |
|
|
|
for (auto& layer : layers) |
|
{ |
|
auto& ld = layer.second; |
|
ld.inputBlobsWrappers.clear(); |
|
ld.outputBlobsWrappers.clear(); |
|
ld.internalBlobsWrappers.clear(); |
|
} |
|
|
|
// Fake references to input blobs. |
|
for (int i = 0; i < layers[0].outputBlobs.size(); ++i) |
|
blobManager.addReference(LayerPin(0, i)); |
|
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); ++it) |
|
{ |
|
const LayerData& ld = it->second; |
|
blobManager.addReferences(ld.inputBlobsId); |
|
} |
|
|
|
for (int i = 0; i < blobsToKeep_.size(); i++) |
|
{ |
|
blobManager.addReference(blobsToKeep_[i]); |
|
} |
|
|
|
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
int lid = it->first; |
|
allocateLayer(lid, layersShapes); |
|
} |
|
|
|
layersTimings.resize(lastLayerId + 1, 0); |
|
fuseLayers(blobsToKeep_); |
|
} |
|
|
|
|
|
void Net::Impl::forwardLayer(LayerData& ld) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
Ptr<Layer> layer = ld.layerInstance; |
|
|
|
if (!ld.skip) |
|
{ |
|
TickMeter tm; |
|
tm.start(); |
|
|
|
#ifndef HAVE_VULKAN |
|
std::map<int, Ptr<BackendNode>>::const_iterator it = ld.backendNodes.find(preferableBackend); |
|
#else |
|
std::map<int, Ptr<BackendNode>>::iterator it = ld.backendNodes.find(preferableBackend); |
|
#endif |
|
if (preferableBackend == DNN_BACKEND_OPENCV || it == ld.backendNodes.end() || it->second.empty()) |
|
{ |
|
if (isAsync) |
|
CV_Error(Error::StsNotImplemented, "Default implementation fallbacks in asynchronous mode"); |
|
|
|
if (!layer->supportBackend(DNN_BACKEND_OPENCV)) |
|
CV_Error(Error::StsNotImplemented, format("Layer \"%s\" of type \"%s\" unsupported on OpenCV backend", |
|
ld.name.c_str(), ld.type.c_str())); |
|
|
|
#ifdef HAVE_OPENCL |
|
if (preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget)) |
|
{ |
|
std::vector<UMat> umat_inputBlobs = OpenCLBackendWrapper::getUMatVector(ld.inputBlobsWrappers); |
|
std::vector<UMat> umat_outputBlobs = OpenCLBackendWrapper::getUMatVector(ld.outputBlobsWrappers); |
|
std::vector<UMat> umat_internalBlobs = OpenCLBackendWrapper::getUMatVector(ld.internalBlobsWrappers); |
|
layer->forward(umat_inputBlobs, |
|
umat_outputBlobs, |
|
umat_internalBlobs); |
|
if (getParam_DNN_CHECK_NAN_INF()) |
|
{ |
|
bool fail = false; |
|
for (size_t i = 0; i < umat_outputBlobs.size(); ++i) |
|
{ |
|
UMat& u = umat_outputBlobs[i]; |
|
Mat m; |
|
if (u.depth() == CV_16S) // FP16 |
|
convertFp16(u, m); |
|
else |
|
m = u.getMat(ACCESS_READ); |
|
if (!checkRange(m)) |
|
{ |
|
CV_LOG_WARNING(NULL, "NaN detected in layer output: id=" << ld.id << " name=" << layer->name |
|
<< " output id=" << i << " output shape=" << shape(m)); |
|
fail = true; |
|
} |
|
else if (!checkRange(m, true, NULL, -1e6, 1e6)) |
|
{ |
|
CV_LOG_WARNING(NULL, "Inf detected in layer output: id=" << ld.id << " name=" << layer->name |
|
<< " output id=" << i << " output shape=" << shape(m)); |
|
fail = true; |
|
} |
|
} |
|
if (fail) |
|
{ |
|
for (size_t i = 0; i < umat_inputBlobs.size(); ++i) |
|
{ |
|
UMat& u = umat_inputBlobs[i]; |
|
Mat m; |
|
if (u.depth() == CV_16S) // FP16 |
|
convertFp16(u, m); |
|
else |
|
m = u.getMat(ACCESS_READ); |
|
std::cout << "INPUT " << i << " " << cv::typeToString(u.type()) << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << m.reshape(1, 1) << std::endl; |
|
} |
|
for (size_t i = 0; i < umat_outputBlobs.size(); ++i) |
|
{ |
|
UMat& u = umat_outputBlobs[i]; |
|
Mat m; |
|
if (u.depth() == CV_16S) // FP16 |
|
convertFp16(u, m); |
|
else |
|
m = u.getMat(ACCESS_READ); |
|
std::cout << "OUTPUT " << i << " " << cv::typeToString(u.type()) << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << m.reshape(1, 1) << std::endl; |
|
} |
|
for (size_t i = 0; i < umat_internalBlobs.size(); ++i) |
|
{ |
|
UMat& u = umat_internalBlobs[i]; |
|
Mat m; |
|
if (u.depth() == CV_16S) // FP16 |
|
convertFp16(u, m); |
|
else |
|
m = u.getMat(ACCESS_READ); |
|
std::cout << "INTERNAL " << i << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << cv::typeToString(u.type()) << " " << m.reshape(1, 1) << std::endl; |
|
} |
|
if (getParam_DNN_CHECK_NAN_INF_RAISE_ERROR()) |
|
CV_Assert(!fail); |
|
} |
|
} |
|
OpenCLBackendWrapper::update(ld.outputBlobsWrappers, umat_outputBlobs); |
|
} |
|
else |
|
#endif |
|
{ |
|
for (int i = 0, n = ld.inputBlobsWrappers.size(); i < n; ++i) |
|
{ |
|
if (!ld.inputBlobsWrappers[i].empty()) |
|
ld.inputBlobsWrappers[i]->copyToHost(); |
|
} |
|
|
|
std::vector<Mat> inps(ld.inputBlobs.size()); |
|
for (int i = 0; i < ld.inputBlobs.size(); ++i) |
|
{ |
|
inps[i] = *ld.inputBlobs[i]; |
|
} |
|
layer->forward(inps, ld.outputBlobs, ld.internals); |
|
|
|
if (getParam_DNN_CHECK_NAN_INF()) |
|
{ |
|
bool fail = false; |
|
for (size_t i = 0; i < ld.outputBlobs.size(); ++i) |
|
{ |
|
const Mat& m = ld.outputBlobs[i]; |
|
if (!checkRange(m)) |
|
{ |
|
CV_LOG_WARNING(NULL, "NaN detected in layer output: " |
|
<< cv::format("id=%d name=%s output id=%zu output shape=", ld.id, layer->name.c_str(), i) << shape(m)); |
|
fail = true; |
|
} |
|
else if (!checkRange(m, true, NULL, -1e6, 1e6)) |
|
{ |
|
CV_LOG_WARNING(NULL, "Inf detected in layer output: " |
|
<< cv::format("id=%d name=%s output id=%zu output shape=", ld.id, layer->name.c_str(), i) << shape(m)); |
|
fail = true; |
|
} |
|
} |
|
if (fail) |
|
{ |
|
for (size_t i = 0; i < ld.inputBlobs.size(); ++i) |
|
{ |
|
const Mat* pM = ld.inputBlobs[i]; |
|
if (!pM) |
|
{ |
|
std::cout << "INPUT " << i << " is NULL" << std::endl; |
|
continue; |
|
} |
|
const Mat& m = *pM; |
|
std::cout << "INPUT " << i << " " << cv::typeToString(m.type()) << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << m.reshape(1, 1) << std::endl; |
|
} |
|
for (size_t i = 0; i < ld.outputBlobs.size(); ++i) |
|
{ |
|
const Mat& m = ld.outputBlobs[i]; |
|
std::cout << "OUTPUT " << i << " " << cv::typeToString(m.type()) << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << m.reshape(1, 1) << std::endl; |
|
} |
|
for (size_t i = 0; i < ld.internals.size(); ++i) |
|
{ |
|
const Mat& m = ld.internals[i]; |
|
std::cout << "INTERNAL " << i << " " << cv::typeToString(m.type()) << " " << shape(m) << std::endl; |
|
if (getParam_DNN_CHECK_NAN_INF_DUMP()) std::cout << m.reshape(1, 1) << std::endl; |
|
} |
|
if (getParam_DNN_CHECK_NAN_INF_RAISE_ERROR()) |
|
CV_Assert(!fail); |
|
} |
|
} |
|
|
|
for (int i = 0, n = ld.outputBlobsWrappers.size(); i < n; ++i) |
|
{ |
|
if (!ld.outputBlobsWrappers[i].empty()) |
|
ld.outputBlobsWrappers[i]->setHostDirty(); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
Ptr<BackendNode> node = it->second; |
|
CV_Assert(!node.empty()); |
|
if (preferableBackend == DNN_BACKEND_CUDA) |
|
{ |
|
CV_Assert(haveCUDA()); |
|
|
|
#ifdef HAVE_CUDA |
|
Ptr<CUDABackendNode> cudaNode = node.dynamicCast<CUDABackendNode>(); |
|
CV_Assert(!cudaNode.empty()); |
|
|
|
cudaNode->forward(ld.inputBlobsWrappers, ld.outputBlobsWrappers, cudaInfo->workspace); |
|
|
|
for (auto id : ld.cudaD2HBackgroundTransfers) |
|
{ |
|
auto wrapper = ld.outputBlobsWrappers[id].dynamicCast<CUDABackendWrapper>(); |
|
wrapper->copyToHostInBackground(); |
|
} |
|
#endif |
|
} |
|
else if (preferableBackend == DNN_BACKEND_HALIDE) |
|
{ |
|
forwardHalide(ld.outputBlobsWrappers, node); |
|
} |
|
else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
|
{ |
|
CV_Assert(preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && "Inheritance internal error"); |
|
} |
|
else if (preferableBackend == DNN_BACKEND_WEBNN) |
|
{ |
|
forwardWebnn(ld.outputBlobsWrappers, node, isAsync); |
|
} |
|
else if (preferableBackend == DNN_BACKEND_TIMVX) |
|
{ |
|
forwardTimVX(ld.outputBlobsWrappers, node); |
|
} |
|
#ifdef HAVE_VULKAN |
|
else if (preferableBackend == DNN_BACKEND_VKCOM) |
|
{ |
|
try |
|
{ |
|
forwardVkCom(ld.outputBlobsWrappers, node); |
|
} |
|
catch (const cv::Exception& e) |
|
{ |
|
CV_LOG_ERROR(NULL, "forwardVkCom failed, fallback to CPU implementation. " << e.what()); |
|
it->second = Ptr<BackendNode>(); |
|
forwardLayer(ld); |
|
} |
|
} |
|
#endif |
|
else |
|
{ |
|
CV_Error(Error::StsNotImplemented, cv::format("Unknown backend identifier: %d", preferableBackend)); |
|
} |
|
} |
|
|
|
tm.stop(); |
|
int64 t = tm.getTimeTicks(); |
|
layersTimings[ld.id] = (t > 0) ? t : t + 1; // zero for skipped layers only |
|
} |
|
else |
|
{ |
|
layersTimings[ld.id] = 0; |
|
} |
|
|
|
ld.flag = 1; |
|
} |
|
|
|
|
|
void Net::Impl::forwardToLayer(LayerData& ld, bool clearFlags) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
if (clearFlags) |
|
{ |
|
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) |
|
it->second.flag = 0; |
|
} |
|
|
|
// already was forwarded |
|
if (ld.flag) |
|
return; |
|
|
|
// forward parents |
|
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end() && (it->second.id < ld.id); ++it) |
|
{ |
|
LayerData& ld = it->second; |
|
if (ld.flag) |
|
continue; |
|
forwardLayer(ld); |
|
} |
|
|
|
// forward itself |
|
forwardLayer(ld); |
|
|
|
#ifdef HAVE_CUDA |
|
if (preferableBackend == DNN_BACKEND_CUDA) |
|
cudaInfo->context.stream.synchronize(); |
|
#endif |
|
} |
|
|
|
|
|
Mat Net::Impl::forward(const String& outputName) |
|
{ |
|
CV_Assert(!empty()); |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
String layerName = outputName; |
|
|
|
if (layerName.empty()) |
|
{ |
|
std::vector<String> layerNames = getLayerNames(); |
|
CV_Assert(!layerNames.empty()); |
|
layerName = layerNames.back(); |
|
} |
|
|
|
std::vector<LayerPin> pins(1, getPinByAlias(layerName)); |
|
setUpNet(pins); |
|
forwardToLayer(getLayerData(layerName)); |
|
|
|
return getBlob(layerName); |
|
} |
|
|
|
|
|
AsyncArray Net::Impl::forwardAsync(const String& outputName) |
|
{ |
|
CV_Assert(!empty()); |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
#ifdef CV_CXX11 |
|
String layerName = outputName; |
|
|
|
if (layerName.empty()) |
|
{ |
|
std::vector<String> layerNames = getLayerNames(); |
|
CV_Assert(!layerNames.empty()); |
|
layerName = layerNames.back(); |
|
} |
|
|
|
std::vector<LayerPin> pins(1, getPinByAlias(layerName)); |
|
setUpNet(pins); |
|
|
|
if (preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
|
CV_Error(Error::StsNotImplemented, "DNN: Asynchronous forward is supported for Inference Engine backend only"); |
|
|
|
isAsync = true; |
|
forwardToLayer(getLayerData(layerName)); |
|
isAsync = false; |
|
|
|
return getBlobAsync(layerName); |
|
#else |
|
CV_Error(Error::StsNotImplemented, "DNN: Asynchronous forward requires build with enabled C++11"); |
|
#endif // CV_CXX11 |
|
} |
|
|
|
|
|
void Net::Impl::forward(OutputArrayOfArrays outputBlobs, const String& outputName) |
|
{ |
|
CV_Assert(!empty()); |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
String layerName = outputName; |
|
|
|
if (layerName.empty()) |
|
{ |
|
std::vector<String> layerNames = getLayerNames(); |
|
CV_Assert(!layerNames.empty()); |
|
layerName = layerNames.back(); |
|
} |
|
|
|
std::vector<LayerPin> pins(1, getPinByAlias(layerName)); |
|
setUpNet(pins); |
|
forwardToLayer(getLayerData(layerName)); |
|
|
|
LayerPin pin = getPinByAlias(layerName); |
|
LayerData& ld = layers[pin.lid]; |
|
|
|
if (outputBlobs.isUMat()) |
|
{ |
|
getBlob(layerName).copyTo(outputBlobs); |
|
} |
|
else if (outputBlobs.isMat()) |
|
{ |
|
outputBlobs.assign(getBlob(layerName)); |
|
} |
|
else if (outputBlobs.isMatVector()) |
|
{ |
|
// The DNN_TARGET_CPU and DNN_TARGET_CPU_FP16 both use the CPU memory, do not need the copyToHost. |
|
if (preferableTarget != DNN_TARGET_CPU && preferableTarget != DNN_TARGET_CPU_FP16) |
|
{ |
|
for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) |
|
{ |
|
CV_Assert(!ld.outputBlobsWrappers[i].empty()); |
|
ld.outputBlobsWrappers[i]->copyToHost(); |
|
} |
|
} |
|
if (ld.outputBlobs[0].depth() == CV_16S) |
|
{ |
|
std::vector<Mat>& outputvec = *(std::vector<Mat>*)outputBlobs.getObj(); |
|
outputvec.resize(ld.outputBlobs.size()); |
|
for (int i = 0; i < outputvec.size(); i++) |
|
convertFp16(ld.outputBlobs[i], outputvec[i]); |
|
} |
|
else |
|
{ |
|
// Output depth can be CV_32F or CV_8S |
|
std::vector<Mat>& outputvec = *(std::vector<Mat>*)outputBlobs.getObj(); |
|
outputvec = ld.outputBlobs; |
|
} |
|
} |
|
else if (outputBlobs.isUMatVector()) |
|
{ |
|
std::vector<UMat>& outputvec = *(std::vector<UMat>*)outputBlobs.getObj(); |
|
|
|
#ifdef HAVE_OPENCL |
|
if (preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget)) |
|
{ |
|
if (preferableTarget == DNN_TARGET_OPENCL) |
|
outputvec = OpenCLBackendWrapper::getUMatVector(ld.outputBlobsWrappers); |
|
else if (preferableTarget == DNN_TARGET_OPENCL_FP16) |
|
{ |
|
std::vector<UMat> out_vec = OpenCLBackendWrapper::getUMatVector(ld.outputBlobsWrappers); |
|
outputvec.resize(out_vec.size()); |
|
for (int i = 0; i < out_vec.size(); i++) |
|
convertFp16(out_vec[i], outputvec[i]); |
|
} |
|
} |
|
else |
|
#endif |
|
{ |
|
outputvec.resize(ld.outputBlobs.size()); |
|
for (int i = 0; i < outputvec.size(); ++i) |
|
ld.outputBlobs[i].copyTo(outputvec[i]); |
|
} |
|
} |
|
} |
|
|
|
|
|
void Net::Impl::forward(OutputArrayOfArrays outputBlobs, |
|
const std::vector<String>& outBlobNames) |
|
{ |
|
CV_Assert(!empty()); |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
std::vector<LayerPin> pins; |
|
for (int i = 0; i < outBlobNames.size(); i++) |
|
{ |
|
pins.push_back(getPinByAlias(outBlobNames[i])); |
|
} |
|
|
|
setUpNet(pins); |
|
|
|
LayerPin out = getLatestLayerPin(pins); |
|
|
|
forwardToLayer(getLayerData(out.lid)); |
|
|
|
std::vector<Mat> matvec; |
|
for (int i = 0; i < pins.size(); i++) |
|
{ |
|
matvec.push_back(getBlob(pins[i])); |
|
} |
|
|
|
outputBlobs.create((int)matvec.size(), 1, CV_32F/*FIXIT*/, -1); // allocate vector |
|
outputBlobs.assign(matvec); |
|
} |
|
|
|
|
|
void Net::Impl::forward(std::vector<std::vector<Mat>>& outputBlobs, |
|
const std::vector<String>& outBlobNames) |
|
{ |
|
CV_Assert(!empty()); |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
std::vector<LayerPin> pins; |
|
for (int i = 0; i < outBlobNames.size(); i++) |
|
{ |
|
pins.push_back(getPinByAlias(outBlobNames[i])); |
|
} |
|
|
|
setUpNet(pins); |
|
|
|
LayerPin out = getLatestLayerPin(pins); |
|
|
|
forwardToLayer(getLayerData(out.lid)); |
|
|
|
outputBlobs.resize(outBlobNames.size()); |
|
for (int i = 0; i < outBlobNames.size(); i++) |
|
{ |
|
std::vector<LayerPin> lp = getLayerOutPins(outBlobNames[i]); |
|
outputBlobs[i].resize(lp.size()); |
|
for (int j = 0; j < lp.size(); j++) |
|
{ |
|
outputBlobs[i][j] = getBlob(lp[j]); |
|
} |
|
} |
|
} |
|
|
|
|
|
void Net::Impl::getLayerShapesRecursively(int id, LayersShapesMap& inOutShapes) |
|
{ |
|
CV_CheckGE(id, 0, ""); |
|
CV_CheckLT(id, (int)layers.size(), ""); |
|
LayerData& layerData = layers[id]; |
|
std::vector<LayerPin>& inputLayerIds = layerData.inputBlobsId; |
|
LayerShapes& layerShapes = inOutShapes[id]; |
|
|
|
if (id == 0 && layerShapes.in[0].empty()) |
|
{ |
|
if (!layerData.outputBlobs.empty()) |
|
{ |
|
ShapesVec shapes; |
|
for (int i = 0; i < layerData.outputBlobs.size(); i++) |
|
{ |
|
Mat& inp = layerData.outputBlobs[i]; |
|
CV_Assert(!inp.empty()); |
|
shapes.push_back(shape(inp)); |
|
} |
|
layerShapes.in = shapes; |
|
} |
|
else |
|
{ |
|
const std::vector<MatShape>& inputShapes = netInputLayer->shapes; |
|
bool none = true; |
|
for (size_t i = 0; i < inputShapes.size(); i++) |
|
{ |
|
if (!inputShapes[i].empty()) |
|
{ |
|
none = false; |
|
break; |
|
} |
|
} |
|
if (none) |
|
{ |
|
layerShapes.out.clear(); |
|
return; |
|
} |
|
else |
|
{ |
|
layerShapes.in = inputShapes; |
|
} |
|
} |
|
} |
|
|
|
if (layerShapes.in.empty()) |
|
{ |
|
for (int i = 0; i < inputLayerIds.size(); i++) |
|
{ |
|
int layerId = inputLayerIds[i].lid; |
|
LayersShapesMap::const_iterator it = inOutShapes.find(layerId); |
|
if (it == inOutShapes.end() || it->second.out.empty()) |
|
{ |
|
getLayerShapesRecursively(layerId, inOutShapes); |
|
it = inOutShapes.find(layerId); |
|
CV_Assert(it != inOutShapes.end()); |
|
} |
|
const int out_port = inputLayerIds[i].oid; |
|
CV_CheckLT(out_port, (int)it->second.out.size(), ""); |
|
const MatShape& shape = it->second.out[out_port]; |
|
layerShapes.in.push_back(shape); |
|
} |
|
} |
|
const ShapesVec& is = layerShapes.in; |
|
ShapesVec& os = layerShapes.out; |
|
ShapesVec& ints = layerShapes.internal; |
|
int requiredOutputs = layerData.requiredOutputs.size(); |
|
const Ptr<Layer>& l = getLayerInstance(layerData); |
|
CV_Assert(l); |
|
bool layerSupportInPlace = false; |
|
try |
|
{ |
|
layerSupportInPlace = l->getMemoryShapes(is, requiredOutputs, os, ints); |
|
} |
|
catch (const cv::Exception& e) |
|
{ |
|
CV_LOG_ERROR(NULL, "OPENCV/DNN: [" << l->type << "]:(" << l->name << "): getMemoryShapes() throws exception." << |
|
" inputs=" << is.size() << |
|
" outputs=" << os.size() << "/" << requiredOutputs << |
|
" blobs=" << l->blobs.size()); |
|
for (size_t i = 0; i < is.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " input[" << i << "] = " << toString(is[i])); |
|
} |
|
for (size_t i = 0; i < os.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " output[" << i << "] = " << toString(os[i])); |
|
} |
|
for (size_t i = 0; i < l->blobs.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " blobs[" << i << "] = " << typeToString(l->blobs[i].type()) << " " << toString(shape(l->blobs[i]))); |
|
} |
|
CV_LOG_ERROR(NULL, "Exception message: " << e.what()); |
|
throw; |
|
} |
|
layerShapes.supportInPlace = layerSupportInPlace; |
|
|
|
try |
|
{ |
|
for (int i = 0; i < ints.size(); i++) |
|
CV_CheckGT(total(ints[i]), 0, ""); |
|
|
|
for (int i = 0; i < os.size(); i++) |
|
CV_CheckGT(total(os[i]), 0, ""); |
|
} |
|
catch (const cv::Exception& e) |
|
{ |
|
CV_LOG_ERROR(NULL, "OPENCV/DNN: [" << l->type << "]:(" << l->name << "): getMemoryShapes() post validation failed." << |
|
" inputs=" << is.size() << |
|
" outputs=" << os.size() << "/" << requiredOutputs << |
|
" blobs=" << l->blobs.size() << |
|
" inplace=" << layerSupportInPlace); |
|
for (size_t i = 0; i < is.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " input[" << i << "] = " << toString(is[i])); |
|
} |
|
for (size_t i = 0; i < os.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " output[" << i << "] = " << toString(os[i])); |
|
} |
|
for (size_t i = 0; i < l->blobs.size(); ++i) |
|
{ |
|
CV_LOG_ERROR(NULL, " blobs[" << i << "] = " << typeToString(l->blobs[i].type()) << " " << toString(shape(l->blobs[i]))); |
|
} |
|
CV_LOG_ERROR(NULL, "Exception message: " << e.what()); |
|
throw; |
|
} |
|
} |
|
|
|
void Net::Impl::getLayersShapes( |
|
const ShapesVec& netInputShapes, |
|
std::vector<int>& layersIds, |
|
std::vector<ShapesVec>& inLayersShapes, |
|
std::vector<ShapesVec>& outLayersShapes) /*const*/ |
|
{ |
|
layersIds.clear(); |
|
inLayersShapes.clear(); |
|
outLayersShapes.clear(); |
|
|
|
Impl::LayersShapesMap inOutShapes; |
|
getLayersShapes(netInputShapes, inOutShapes); |
|
|
|
for (Impl::LayersShapesMap::const_iterator it = inOutShapes.begin(); |
|
it != inOutShapes.end(); it++) |
|
{ |
|
layersIds.push_back(it->first); |
|
inLayersShapes.push_back(it->second.in); |
|
outLayersShapes.push_back(it->second.out); |
|
} |
|
} |
|
|
|
|
|
void Net::Impl::getLayersShapes(const ShapesVec& netInputShapes, |
|
LayersShapesMap& inOutShapes) |
|
{ |
|
inOutShapes.clear(); |
|
|
|
inOutShapes[0].in = netInputShapes; // insert shape for first input layer |
|
for (MapIdToLayerData::const_iterator it = layers.begin(); |
|
it != layers.end(); it++) |
|
{ |
|
getLayerShapesRecursively(it->first, inOutShapes); |
|
} |
|
} |
|
|
|
void Net::Impl::getLayerShapes(const ShapesVec& netInputShapes, |
|
const int layerId, |
|
LayerShapes& shapes) |
|
{ |
|
LayersShapesMap inOutShapes; |
|
inOutShapes[0].in = netInputShapes; // insert shape for first input layer |
|
getLayerShapesRecursively(layerId, inOutShapes); |
|
shapes = inOutShapes[layerId]; |
|
} |
|
|
|
void Net::Impl::updateLayersShapes() |
|
{ |
|
CV_LOG_DEBUG(NULL, "updateLayersShapes() with layers.size=" << layers.size()); |
|
CV_Assert(netInputLayer); |
|
DataLayer& inputLayer = *netInputLayer; |
|
LayerData& inputLayerData = layers[0]; |
|
CV_Assert(inputLayerData.layerInstance.get() == &inputLayer); |
|
CV_Assert(!inputLayerData.outputBlobs.empty()); |
|
ShapesVec inputShapes; |
|
for (int i = 0; i < inputLayerData.outputBlobs.size(); i++) |
|
{ |
|
Mat& inp = inputLayerData.outputBlobs[i]; |
|
CV_Assert(!inp.empty()); |
|
if (preferableBackend == DNN_BACKEND_OPENCV && // FIXIT: wrong place for output allocation |
|
preferableTarget == DNN_TARGET_OPENCL_FP16 && |
|
inputLayerData.dtype == CV_32F) |
|
{ |
|
inp.create(inp.dims, inp.size, CV_16S); |
|
} |
|
inputShapes.push_back(shape(inp)); |
|
} |
|
CV_LOG_DEBUG(NULL, toString(inputShapes, "Network input shapes")); |
|
LayersShapesMap layersShapes; |
|
layersShapes[0].in = inputShapes; |
|
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
int layerId = it->first; |
|
LayerData& layerData = it->second; |
|
const std::vector<LayerPin>& inputLayerIds = layerData.inputBlobsId; |
|
LayerShapes& layerShapes = layersShapes[layerId]; |
|
CV_LOG_DEBUG(NULL, "layer " << layerId << ": [" << layerData.type << "]:(" << layerData.name << ") with inputs.size=" << inputLayerIds.size()); |
|
if (layerShapes.in.empty()) |
|
{ |
|
for (int i = 0; i < inputLayerIds.size(); i++) |
|
{ |
|
const LayerPin& inputPin = inputLayerIds[i]; |
|
int inputLayerId = inputPin.lid; |
|
CV_LOG_DEBUG(NULL, " input[" << i << "] " << inputLayerId << ":" << inputPin.oid << " as [" << layers[inputLayerId].type << "]:(" << layers[inputLayerId].name << ")"); |
|
LayersShapesMap::const_iterator inputIt = layersShapes.find(inputLayerId); |
|
if (inputIt == layersShapes.end() || inputIt->second.out.empty()) |
|
{ |
|
getLayerShapesRecursively(inputLayerId, layersShapes); |
|
} |
|
const MatShape& shape = layersShapes[inputLayerId].out[inputPin.oid]; |
|
layerShapes.in.push_back(shape); |
|
} |
|
getLayerInstance(layerData)->updateMemoryShapes(layerShapes.in); |
|
} |
|
CV_LOG_DEBUG(NULL, "Layer " << layerId << ": " << toString(layerShapes.in, "input shapes")); |
|
CV_LOG_IF_DEBUG(NULL, !layerShapes.out.empty(), "Layer " << layerId << ": " << toString(layerShapes.out, "output shapes")); |
|
CV_LOG_IF_DEBUG(NULL, !layerShapes.internal.empty(), "Layer " << layerId << ": " << toString(layerShapes.internal, "internal shapes")); |
|
} |
|
CV_LOG_DEBUG(NULL, "updateLayersShapes() - DONE"); |
|
} |
|
|
|
|
|
LayerPin Net::Impl::getLatestLayerPin(const std::vector<LayerPin>& pins) const |
|
{ |
|
return *std::max_element(pins.begin(), pins.end()); |
|
} |
|
|
|
Mat Net::Impl::getBlob(const LayerPin& pin) const |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
|
|
if (!pin.valid()) |
|
CV_Error(Error::StsObjectNotFound, "Requested blob not found"); |
|
|
|
MapIdToLayerData::const_iterator it = layers.find(pin.lid); |
|
if (it == layers.end()) |
|
CV_Error_(Error::StsOutOfRange, ("Layer #%d is not valid (output #%d requested)", pin.lid, pin.oid)); |
|
|
|
const LayerData& ld = it->second; |
|
if ((size_t)pin.oid >= ld.outputBlobs.size()) |
|
{ |
|
CV_Error(Error::StsOutOfRange, format("Layer \"%s\" produce only %zu outputs, " |
|
"the #%d was requested", |
|
ld.name.c_str(), ld.outputBlobs.size(), pin.oid)); |
|
} |
|
if (preferableTarget != DNN_TARGET_CPU && preferableTarget != DNN_TARGET_CPU_FP16) |
|
{ |
|
CV_Assert(!ld.outputBlobsWrappers.empty() && !ld.outputBlobsWrappers[pin.oid].empty()); |
|
// Transfer data to CPU if it's require. |
|
ld.outputBlobsWrappers[pin.oid]->copyToHost(); |
|
} |
|
|
|
if (ld.outputBlobs[pin.oid].depth() == CV_16S) |
|
{ |
|
Mat output_blob; |
|
convertFp16(ld.outputBlobs[pin.oid], output_blob); |
|
return output_blob; |
|
} |
|
else |
|
return ld.outputBlobs[pin.oid]; |
|
} |
|
|
|
Mat Net::Impl::getBlob(String outputName) const |
|
{ |
|
return getBlob(getPinByAlias(outputName)); |
|
} |
|
|
|
|
|
AsyncArray Net::Impl::getBlobAsync(const LayerPin& pin) |
|
{ |
|
CV_TRACE_FUNCTION(); |
|
CV_Error(Error::StsNotImplemented, "DNN: OpenVINO/nGraph backend is required"); |
|
} |
|
|
|
|
|
AsyncArray Net::Impl::getBlobAsync(String outputName) |
|
{ |
|
return getBlobAsync(getPinByAlias(outputName)); |
|
} |
|
|
|
|
|
void Net::Impl::setInputsNames(const std::vector<String>& inputBlobNames) |
|
{ |
|
CV_Assert(netInputLayer); |
|
netInputLayer->setNames(inputBlobNames); |
|
} |
|
|
|
|
|
void Net::Impl::setInputShape(const String& inputName, const MatShape& shape) |
|
{ |
|
CV_Assert(netInputLayer); |
|
netInputLayer->setInputShape(inputName, shape); |
|
} |
|
|
|
|
|
void Net::Impl::setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean) |
|
{ |
|
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
|
|
|
LayerPin pin; |
|
pin.lid = 0; |
|
pin.oid = resolvePinOutputName(getLayerData(pin.lid), name); |
|
|
|
if (!pin.valid()) |
|
CV_Error(Error::StsObjectNotFound, "Requested blob \"" + name + "\" not found"); |
|
|
|
Mat blob_ = blob.getMat(); // can't use InputArray directly due MatExpr stuff |
|
MatShape blobShape = shape(blob_); |
|
|
|
#if 0 // TODO: DNNTestNetwork.MobileNet_SSD_Caffe_Different_Width_Height/0 |
|
if (pin.lid == 0) |
|
{ |
|
CV_Assert(!netInputLayer.empty()); |
|
const DataLayer& netInputLayer = *(this->netInputLayer); |
|
if (!netInputLayer.shapes.empty()) |
|
{ |
|
CV_CheckLT(pin.oid, (int)netInputLayer.shapes.size(), ""); |
|
const MatShape& inputShapeLimitation = netInputLayer.shapes[pin.oid]; |
|
if (!inputShapeLimitation.empty()) |
|
{ |
|
CV_CheckEQ(inputShapeLimitation.size(), blobShape.size(), ""); |
|
const size_t dims = inputShapeLimitation.size(); |
|
for (size_t dim = 0; dim < dims; dim++) |
|
{ |
|
if (dims >= 3 && dim == 0 && inputShapeLimitation[0] == 1) |
|
continue; // don't limit batch |
|
CV_CheckEQ(inputShapeLimitation[dim], blobShape[dim], ""); |
|
} |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
LayerData& ld = layers[pin.lid]; |
|
const int numInputs = std::max(pin.oid + 1, (int)ld.requiredOutputs.size()); |
|
ld.outputBlobs.resize(numInputs); |
|
ld.outputBlobsWrappers.resize(numInputs); |
|
netInputLayer->inputsData.resize(numInputs); |
|
netInputLayer->scaleFactors.resize(numInputs); |
|
netInputLayer->means.resize(numInputs); |
|
|
|
MatShape prevShape = shape(netInputLayer->inputsData[pin.oid]); |
|
bool oldShape = prevShape == blobShape; |
|
|
|
blob_.copyTo(netInputLayer->inputsData[pin.oid]); |
|
if (!oldShape) |
|
ld.outputBlobs[pin.oid] = netInputLayer->inputsData[pin.oid]; |
|
|
|
if (!ld.outputBlobsWrappers[pin.oid].empty()) |
|
{ |
|
ld.outputBlobsWrappers[pin.oid]->setHostDirty(); |
|
} |
|
netInputLayer->scaleFactors[pin.oid] = scalefactor; |
|
netInputLayer->means[pin.oid] = mean; |
|
netWasAllocated = netWasAllocated && oldShape; |
|
} |
|
|
|
|
|
Mat Net::Impl::getParam(int layer, int numParam) const |
|
{ |
|
LayerData& ld = getLayerData(layer); |
|
std::vector<Mat>& layerBlobs = getLayerInstance(ld)->blobs; |
|
CV_Assert(numParam < (int)layerBlobs.size()); |
|
return layerBlobs[numParam]; |
|
} |
|
|
|
void Net::Impl::setParam(int layer, int numParam, const Mat& blob) |
|
{ |
|
LayerData& ld = getLayerData(layer); |
|
|
|
// FIXIT we should not modify "execution" instance |
|
std::vector<Mat>& layerBlobs = getLayerInstance(ld)->blobs; |
|
CV_Assert(numParam < (int)layerBlobs.size()); |
|
// we don't make strong checks, use this function carefully |
|
layerBlobs[numParam] = blob; |
|
} |
|
|
|
|
|
static |
|
string dumpLayerParameterSize(const string& name, const LayerParams& lp) |
|
{ |
|
std::ostringstream out(name, std::ios::ate); |
|
DictValue param = lp.get(name); |
|
switch (param.size()) |
|
{ |
|
case 1: out << " : "; break; |
|
case 2: out << " (HxW): "; break; |
|
case 3: out << " (DxHxW): "; break; |
|
default: |
|
CV_LOG_INFO(NULL, format("DNN/dumpLayerParameterSize(): Unsupported '%s' size = %d", name.c_str(), param.size())); |
|
out << ": "; |
|
} |
|
for (size_t i = 0; i < param.size(); i++) |
|
{ |
|
if (i > 0) |
|
out << " x "; |
|
out << param.get<int>(i); |
|
} |
|
return out.str(); |
|
} |
|
|
|
string Net::Impl::dump(bool forceAllocation) const |
|
{ |
|
bool hasInput = !netInputLayer->inputsData.empty(); |
|
if (forceAllocation) |
|
{ |
|
if (!netWasAllocated) |
|
const_cast<Net::Impl*>(this)->setUpNet(); |
|
} |
|
|
|
std::ostringstream out; |
|
const std::map<int, LayerData>& map = layers; |
|
|
|
Backend prefBackend = (Backend)preferableBackend; |
|
std::vector<std::vector<int>> skippedLayers; |
|
std::vector<int> skipId; |
|
std::vector<int> allLayers(map.size(), -1); |
|
int idPrev = -1; |
|
Ptr<BackendNode> prevNode; |
|
for (std::map<int, LayerData>::const_reverse_iterator rit = map.rbegin(); rit != map.rend(); ++rit) |
|
{ |
|
std::map<int, Ptr<BackendNode>>::const_iterator itBackend = rit->second.backendNodes.find(prefBackend); |
|
if (prefBackend == DNN_BACKEND_OPENCV || itBackend == rit->second.backendNodes.end() || itBackend->second.empty()) |
|
{ |
|
if (rit->second.skip) |
|
skipId.push_back(rit->first); |
|
else if (!skipId.empty()) |
|
{ |
|
if (prefBackend == DNN_BACKEND_OPENCV || prevNode.empty()) |
|
skipId.push_back(rit->first); |
|
else if (idPrev != -1) |
|
skipId.push_back(idPrev); |
|
|
|
std::sort(skipId.begin(), skipId.end()); |
|
for (int i = 0; i < skipId.size(); i++) |
|
{ |
|
allLayers[skipId[i]] = skippedLayers.size(); |
|
} |
|
skippedLayers.push_back(skipId); |
|
skipId.clear(); |
|
} |
|
} |
|
else |
|
{ |
|
if (itBackend->second == prevNode) |
|
{ |
|
if (idPrev != -1) |
|
skipId.push_back(idPrev); |
|
} |
|
else if (!skipId.empty()) |
|
{ |
|
if (idPrev != -1) |
|
skipId.push_back(idPrev); |
|
std::sort(skipId.begin(), skipId.end()); |
|
for (int i = 0; i < skipId.size(); i++) |
|
{ |
|
allLayers[skipId[i]] = skippedLayers.size(); |
|
} |
|
skippedLayers.push_back(skipId); |
|
skipId.clear(); |
|
} |
|
idPrev = rit->first; |
|
prevNode = itBackend->second; |
|
} |
|
} |
|
std::vector<string> colors = { "#ffffb3", "#fccde5", "#8dd3c7", "#bebada", "#80b1d3", "#fdb462", "#ff4848", "#b35151", "#b266ff", "#b266ff", "#3cb371", "#ffcab3"}; |
|
string backend; |
|
switch (prefBackend) |
|
{ |
|
case DNN_BACKEND_DEFAULT: backend = "DEFAULT/"; break; |
|
case DNN_BACKEND_HALIDE: backend = "HALIDE/"; break; |
|
case DNN_BACKEND_INFERENCE_ENGINE: // fallthru |
|
case DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019: // fallthru |
|
case DNN_BACKEND_INFERENCE_ENGINE_NGRAPH: backend = "OpenVINO/"; break; |
|
case DNN_BACKEND_OPENCV: backend = "OCV/"; break; |
|
case DNN_BACKEND_VKCOM: backend = "VULKAN/"; break; |
|
case DNN_BACKEND_CUDA: backend = "CUDA/"; break; |
|
case DNN_BACKEND_WEBNN: backend = "WEBNN/"; break; |
|
case DNN_BACKEND_TIMVX: backend = "TIMVX/"; break; |
|
case DNN_BACKEND_CANN: backend = "CANN/"; break; |
|
// don't use default: |
|
} |
|
out << "digraph G {\n"; |
|
// Add nodes |
|
for (std::map<int, LayerData>::const_iterator it = map.begin(); it != map.end(); ++it) |
|
{ |
|
const LayerData& ld = it->second; |
|
string name = ld.params.name; |
|
std::vector<int> clusterIds(1, it->first); |
|
if (allLayers[it->first] == -1 && !name.empty()) |
|
{ |
|
out << "\t\"" << name << "\" [label=\""; |
|
} |
|
else if (name.empty() || it->first != skippedLayers[allLayers[it->first]][0]) |
|
{ |
|
continue; |
|
} |
|
else // first node in cluster : it->first == skippedLayers[allLayers[it->first]][0] |
|
{ |
|
int cluster = allLayers[it->first]; |
|
out << "\t\"" |
|
<< "cluster_" << cluster << "\" [label=\"{"; |
|
clusterIds = skippedLayers[allLayers[it->first]]; // vertices in current cluster |
|
} |
|
for (int i = 0; i < clusterIds.size(); i++) |
|
{ |
|
CV_DbgAssert(map.find(clusterIds[i]) != map.end()); |
|
const LayerParams& lp = map.find(clusterIds[i])->second.params; |
|
if (!lp.name.empty()) |
|
{ |
|
if (i > 0) |
|
{ |
|
out << " | "; |
|
} |
|
out << lp.name << "\\n" |
|
<< lp.type << "\\n"; // align center |
|
if (lp.has("kernel_size")) |
|
{ |
|
string kernel = dumpLayerParameterSize("kernel_size", lp); |
|
out << kernel; |
|
out << "\\l"; // align left |
|
} |
|
else if (lp.has("kernel_h") && lp.has("kernel_w")) |
|
{ |
|
DictValue h = lp.get("kernel_h"); |
|
DictValue w = lp.get("kernel_w"); |
|
out << "kernel (HxW): " << h << " x " << w; |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("stride")) |
|
{ |
|
string stride = dumpLayerParameterSize("stride", lp); |
|
out << stride; |
|
out << "\\l"; // align left |
|
} |
|
else if (lp.has("stride_h") && lp.has("stride_w")) |
|
{ |
|
DictValue h = lp.get("stride_h"); |
|
DictValue w = lp.get("stride_w"); |
|
out << "stride (HxW): " << h << " x " << w; |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("dilation")) |
|
{ |
|
string dilation = dumpLayerParameterSize("dilation", lp); |
|
out << dilation; |
|
out << "\\l"; // align left |
|
} |
|
else if (lp.has("dilation_h") && lp.has("dilation_w")) |
|
{ |
|
DictValue h = lp.get("dilation_h"); |
|
DictValue w = lp.get("dilation_w"); |
|
out << "dilation (HxW): " << h << " x " << w; |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("pad")) |
|
{ |
|
DictValue pad = lp.get("pad"); |
|
out << "pad "; |
|
switch (pad.size()) |
|
{ |
|
case 1: out << ": " << pad; break; |
|
case 2: |
|
out << "(HxW): (" << pad.get<int>(0) << " x " << pad.get<int>(1) << ")"; |
|
break; |
|
case 4: |
|
out << "(HxW): (" << pad.get<int>(0) << ", " << pad.get<int>(2) |
|
<< ") x (" << pad.get<int>(1) << ", " << pad.get<int>(3) << ")"; |
|
break; |
|
case 6: |
|
out << "(DxHxW): (" << pad.get<int>(0) << ", " << pad.get<int>(3) |
|
<< ") x (" << pad.get<int>(1) << ", " << pad.get<int>(4) |
|
<< ") x (" << pad.get<int>(2) << ", " << pad.get<int>(5) << ")"; |
|
break; |
|
default: CV_Error(Error::StsNotImplemented, format("Unsupported pad size = %d", pad.size())); |
|
} |
|
out << "\\l"; // align left |
|
} |
|
else if (lp.has("pad_l") && lp.has("pad_t") && lp.has("pad_r") && lp.has("pad_b")) |
|
{ |
|
DictValue l = lp.get("pad_l"); |
|
DictValue t = lp.get("pad_t"); |
|
DictValue r = lp.get("pad_r"); |
|
DictValue b = lp.get("pad_b"); |
|
out << "pad (HxW): (" << t << ", " << b << ") x (" << l << ", " << r << ")"; |
|
out << "\\l"; // align left |
|
} |
|
else if (lp.has("pooled_w") || lp.has("pooled_h")) |
|
{ |
|
DictValue h = lp.get("pooled_h"); |
|
DictValue w = lp.get("pooled_w"); |
|
out << "pad pooled (HxW): " << h << " x " << w; |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("pool")) |
|
{ |
|
out << "pool: " << lp.get("pool"); |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("global_pooling")) |
|
{ |
|
out << "global_pooling: " << lp.get("global_pooling"); |
|
out << "\\l"; // align left |
|
} |
|
if (lp.has("group")) |
|
{ |
|
out << "group: " << lp.get("group"); |
|
out << "\\l"; // align left |
|
} |
|
} |
|
} |
|
if (!ld.outputBlobs.empty()) |
|
{ |
|
out << "output: " << ld.outputBlobs[0].size; |
|
out << "\\l"; // align left |
|
} |
|
|
|
Ptr<BackendNode> layerBackend; |
|
std::map<int, Ptr<BackendNode>>::const_iterator ibn = ld.backendNodes.find(prefBackend); |
|
if (ibn != ld.backendNodes.end()) |
|
layerBackend = ibn->second; |
|
out << (!layerBackend.empty() ? backend : "OCV/"); |
|
int colorId = 0; |
|
const Target target = ld.layerInstance.empty() |
|
? DNN_TARGET_CPU |
|
: (Target)(ld.layerInstance->preferableTarget); // TODO fix preferableTarget type |
|
switch (target) |
|
{ |
|
case DNN_TARGET_CPU: |
|
out << "CPU"; |
|
colorId = layerBackend.empty() ? 0 : 5; |
|
break; |
|
case DNN_TARGET_OPENCL: |
|
out << "OCL"; |
|
colorId = 1; |
|
break; |
|
case DNN_TARGET_OPENCL_FP16: |
|
out << "OCL_FP16"; |
|
colorId = 2; |
|
break; |
|
case DNN_TARGET_MYRIAD: |
|
out << "MYRIAD"; |
|
colorId = 3; |
|
break; |
|
case DNN_TARGET_HDDL: |
|
out << "HDDL"; |
|
colorId = 8; |
|
break; |
|
case DNN_TARGET_VULKAN: |
|
out << "VULKAN"; |
|
colorId = 7; |
|
break; |
|
case DNN_TARGET_FPGA: |
|
out << "FPGA"; |
|
colorId = 4; |
|
break; |
|
case DNN_TARGET_CUDA: |
|
out << "CUDA"; |
|
colorId = 5; |
|
break; |
|
case DNN_TARGET_CUDA_FP16: |
|
out << "CUDA_FP16"; |
|
colorId = 6; |
|
break; |
|
case DNN_TARGET_NPU: |
|
out << "NPU"; |
|
colorId = 9; |
|
break; |
|
case DNN_TARGET_CPU_FP16: |
|
out << "CPU_FP16"; |
|
colorId = 10; |
|
break; |
|
// don't use default: |
|
} |
|
CV_Assert(colorId < colors.size()); |
|
out << "\\n"; // align center |
|
out << ((clusterIds.size() == 1) ? "\" " : " }\" "); |
|
out << "fillcolor=\"" << colors[colorId] << "\" "; |
|
out << "style=filled "; |
|
out << "shape=" << ((clusterIds.size() == 1) ? "box" : "record") << "]\n"; |
|
} |
|
out << '\n'; |
|
// Add edges |
|
int inputsSize = hasInput ? netInputLayer->outNames.size() : 0; |
|
for (std::map<int, LayerData>::const_iterator it = map.begin(); it != map.end(); ++it) |
|
{ |
|
const LayerData& ld = it->second; |
|
if (allLayers[it->first] == -1) // node |
|
{ |
|
for (int i = 0; i < ld.consumers.size(); i++) |
|
{ |
|
int outId = ld.consumers[i].lid; |
|
if (it == map.begin() && inputsSize > 1) |
|
out << "\t\"" << ld.name << "_" << i << "\"" |
|
<< " -> "; |
|
else |
|
out << "\t\"" << ld.name << "\"" |
|
<< " -> "; |
|
if (allLayers[outId] == -1) // node |
|
{ |
|
CV_DbgAssert(map.find(outId) != map.end()); |
|
out << "\"" << map.find(outId)->second.name << "\"\n"; |
|
} |
|
else // cluster |
|
{ |
|
out << "\"" |
|
<< "cluster_" << allLayers[outId] << "\"\n"; |
|
} |
|
} |
|
} |
|
else if (it->first == skippedLayers[allLayers[it->first]].back()) // edges from last layer in cluster |
|
{ |
|
for (int i = 0; i < ld.consumers.size(); i++) |
|
{ |
|
int outId = ld.consumers[i].lid; |
|
if (allLayers[outId] == -1) // node |
|
{ |
|
CV_DbgAssert(map.find(outId) != map.end()); |
|
out << "\t\"" |
|
<< "cluster_" << allLayers[it->first] << "\"" |
|
<< " -> "; |
|
out << "\"" << map.find(outId)->second.name << "\"\n"; |
|
} |
|
else if (allLayers[outId] != allLayers[it->first]) |
|
{ // another cluster |
|
out << "\t\"" |
|
<< "cluster_" << allLayers[it->first] << "\"" |
|
<< " -> "; |
|
out << "\"" |
|
<< "cluster_" << allLayers[outId] << "\"\n"; |
|
} |
|
} |
|
} |
|
} |
|
out << "}\n"; |
|
return out.str(); |
|
} |
|
|
|
|
|
void Net::Impl::dumpNetworkToFile() const |
|
{ |
|
#ifndef OPENCV_DNN_DISABLE_NETWORK_AUTO_DUMP |
|
string dumpFileNameBase = getDumpFileNameBase(); |
|
string dumpFileName = dumpFileNameBase + ".dot"; |
|
try |
|
{ |
|
string dumpStr = dump(); |
|
std::ofstream out(dumpFileName.c_str(), std::ios::out | std::ios::binary); |
|
out << dumpStr; |
|
} |
|
catch (const std::exception& e) |
|
{ |
|
std::ofstream out((dumpFileName + ".error").c_str(), std::ios::out); |
|
out << "Exception: " << e.what() << std::endl; |
|
} |
|
catch (...) |
|
{ |
|
std::ofstream out((dumpFileName + ".error").c_str(), std::ios::out); |
|
out << "Can't dump: unknown exception" << std::endl; |
|
} |
|
#endif |
|
} |
|
|
|
|
|
std::vector<Ptr<Layer>> Net::Impl::getLayerInputs(int layerId) const |
|
{ |
|
LayerData& ld = getLayerData(layerId); |
|
|
|
std::vector<Ptr<Layer>> inputLayers; |
|
inputLayers.reserve(ld.inputBlobsId.size()); |
|
for (int i = 0; i < ld.inputBlobsId.size(); ++i) |
|
{ |
|
inputLayers.push_back(getLayer(ld.inputBlobsId[i].lid)); |
|
} |
|
return inputLayers; |
|
} |
|
|
|
std::vector<String> Net::Impl::getLayerNames() const |
|
{ |
|
std::vector<String> res; |
|
res.reserve(layers.size()); |
|
|
|
Impl::MapIdToLayerData::const_iterator it; |
|
for (it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
if (it->second.id) // skip Data layer |
|
res.push_back(it->second.name); |
|
} |
|
|
|
return res; |
|
} |
|
|
|
|
|
// FIXIT drop "unconnected" API |
|
std::vector<int> Net::Impl::getUnconnectedOutLayers() const |
|
{ |
|
std::vector<int> layersIds; |
|
|
|
// registerOutput() flow |
|
if (!outputNameToId.empty()) |
|
{ |
|
for (std::map<std::string, int>::const_iterator it = outputNameToId.begin(); it != outputNameToId.end(); ++it) |
|
{ |
|
layersIds.push_back(it->second); |
|
} |
|
return layersIds; |
|
} |
|
|
|
Impl::MapIdToLayerData::const_iterator it; |
|
for (it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
int lid = it->first; |
|
const LayerData& ld = it->second; |
|
|
|
if (ld.requiredOutputs.size() == 0) |
|
layersIds.push_back(lid); |
|
} |
|
|
|
return layersIds; |
|
} |
|
|
|
|
|
// FIXIT drop "unconnected" API |
|
std::vector<String> Net::Impl::getUnconnectedOutLayersNames() /*const*/ |
|
{ |
|
std::vector<int> ids = getUnconnectedOutLayers(); |
|
const size_t n = ids.size(); |
|
std::vector<String> names(n); |
|
for (size_t i = 0; i < n; ++i) |
|
{ |
|
names[i] = layers[ids[i]].name; |
|
} |
|
return names; |
|
} |
|
|
|
|
|
int64 Net::Impl::getFLOPS(const std::vector<MatShape>& netInputShapes) /*const*/ |
|
{ |
|
int64 flops = 0; |
|
std::vector<int> ids; |
|
std::vector<std::vector<MatShape>> inShapes, outShapes; |
|
getLayersShapes(netInputShapes, ids, inShapes, outShapes); |
|
CV_Assert(inShapes.size() == outShapes.size()); |
|
CV_Assert(inShapes.size() == ids.size()); |
|
|
|
for (int i = 0; i < ids.size(); i++) |
|
{ |
|
flops += getLayerInstance(layers[ids[i]])->getFLOPS(inShapes[i], outShapes[i]); |
|
} |
|
|
|
return flops; |
|
} |
|
|
|
|
|
int64 Net::Impl::getFLOPS( |
|
const int layerId, |
|
const std::vector<MatShape>& netInputShapes) /*const*/ |
|
{ |
|
Impl::MapIdToLayerData::const_iterator layer = layers.find(layerId); |
|
CV_Assert(layer != layers.end()); |
|
|
|
LayerShapes shapes; |
|
getLayerShapes(netInputShapes, layerId, shapes); |
|
|
|
return getLayerInstance(const_cast<LayerData&>(layer->second))->getFLOPS(shapes.in, shapes.out); |
|
} |
|
|
|
|
|
void Net::Impl::getMemoryConsumption( |
|
const int layerId, |
|
const std::vector<MatShape>& netInputShapes, |
|
size_t& weights, size_t& blobs) /*const*/ |
|
{ |
|
Impl::MapIdToLayerData::const_iterator layer = layers.find(layerId); |
|
CV_Assert(layer != layers.end()); |
|
|
|
weights = blobs = 0; |
|
|
|
for (int i = 0; i < layer->second.params.blobs.size(); i++) |
|
{ |
|
const Mat& weightsBlob = layer->second.params.blobs[i]; |
|
weights += weightsBlob.total() * weightsBlob.elemSize(); |
|
} |
|
|
|
LayerShapes shapes; |
|
getLayerShapes(netInputShapes, layerId, shapes); |
|
const ShapesVec& outLayerShapes = shapes.out; |
|
|
|
// FIXIT netWasQuantized check is not enough - per layer check should be done |
|
size_t elemSize = netWasQuantized ? sizeof(char) : sizeof(float); |
|
for (int i = 0; i < outLayerShapes.size(); i++) |
|
{ |
|
blobs += total(outLayerShapes[i]) * elemSize; |
|
} |
|
} |
|
|
|
|
|
void Net::Impl::getMemoryConsumption( |
|
const std::vector<MatShape>& netInputShapes, |
|
size_t& weights, size_t& blobs) /*const*/ |
|
{ |
|
std::vector<int> layerIds; |
|
std::vector<size_t> w, b; |
|
getMemoryConsumption(netInputShapes, layerIds, w, b); |
|
|
|
weights = blobs = 0; |
|
for (int i = 0; i < layerIds.size(); i++) |
|
{ |
|
weights += w[i]; |
|
blobs += b[i]; |
|
} |
|
} |
|
|
|
|
|
int64 Net::Impl::getPerfProfile(std::vector<double>& timings) const |
|
{ |
|
timings = std::vector<double>(layersTimings.begin() + 1, layersTimings.end()); |
|
int64 total = (int64)std::accumulate(timings.begin(), timings.end(), 0.0); |
|
return total; |
|
} |
|
|
|
void Net::Impl::getMemoryConsumption( |
|
const std::vector<MatShape>& netInputShapes, |
|
std::vector<int>& layerIds, std::vector<size_t>& weights, |
|
std::vector<size_t>& blobs) /*const*/ |
|
{ |
|
layerIds.clear(); |
|
weights.clear(); |
|
blobs.clear(); |
|
|
|
std::vector<std::vector<MatShape>> inLayerShapes, outLayerShapes; |
|
|
|
getLayersShapes(netInputShapes, layerIds, inLayerShapes, outLayerShapes); |
|
// FIXIT netWasQuantized check is not enough - per layer check should be done |
|
size_t elemSize = netWasQuantized ? sizeof(char) : sizeof(float); |
|
for (int i = 0; i < layerIds.size(); i++) |
|
{ |
|
int w = 0, b = 0; |
|
Impl::MapIdToLayerData::const_iterator layer = layers.find(layerIds[i]); |
|
CV_Assert(layer != layers.end()); |
|
|
|
for (int j = 0; j < layer->second.params.blobs.size(); j++) |
|
{ |
|
const Mat& weightsBlob = layer->second.params.blobs[j]; |
|
w += weightsBlob.total() * weightsBlob.elemSize(); |
|
} |
|
|
|
for (int j = 0; j < outLayerShapes[i].size(); j++) |
|
{ |
|
b += total(outLayerShapes[i][j]) * elemSize; |
|
} |
|
|
|
weights.push_back(w); |
|
blobs.push_back(b); |
|
} |
|
} |
|
|
|
void Net::Impl::enableWinograd(bool useWinograd_) |
|
{ |
|
if (useWinograd != useWinograd_) |
|
{ |
|
useWinograd = useWinograd_; |
|
|
|
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
int lid = it->first; |
|
LayerData &ld = layers[lid]; |
|
Ptr<Layer>& currLayer = ld.layerInstance; |
|
|
|
if (ld.type == "Convolution") |
|
{ |
|
ld.params.set("use_winograd", useWinograd_); |
|
Ptr<ConvolutionLayer> convLayer = ld.layerInstance.dynamicCast<ConvolutionLayer>(); |
|
if (!convLayer.empty()) |
|
convLayer->useWinograd = useWinograd_; |
|
} |
|
|
|
if (ld.type == "ConvolutionInt8") |
|
{ |
|
Ptr<ConvolutionLayerInt8> convLayer = currLayer.dynamicCast<ConvolutionLayerInt8>(); |
|
ld.params.set("use_winograd", useWinograd_); |
|
if (!convLayer.empty()) |
|
convLayer->useWinograd = useWinograd_; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
// TODO drop? |
|
void Net::Impl::getLayerTypes(std::vector<String>& layersTypes) const |
|
{ |
|
layersTypes.clear(); |
|
|
|
std::map<String, int> layers_type_map; |
|
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) |
|
{ |
|
if (layers_type_map.find(it->second.type) == layers_type_map.end()) |
|
layers_type_map[it->second.type] = 0; |
|
layers_type_map[it->second.type]++; |
|
} |
|
|
|
for (std::map<String, int>::const_iterator it = layers_type_map.begin(); it != layers_type_map.end(); it++) |
|
{ |
|
layersTypes.push_back(it->first); |
|
} |
|
} |
|
|
|
|
|
// TODO drop? |
|
int Net::Impl::getLayersCount(const String& layerType) const |
|
{ |
|
int count = 0; |
|
for (Impl::MapIdToLayerData::const_iterator it = layers.begin(); |
|
it != layers.end(); it++) |
|
{ |
|
if (it->second.type == layerType) |
|
count++; |
|
} |
|
return count; |
|
} |
|
|
|
|
|
CV__DNN_INLINE_NS_END |
|
}} // namespace cv::dnn
|
|
|