mirror of https://github.com/opencv/opencv.git
Merge pull request #21662 from alalek:dnn_split
commit
685797f403
27 changed files with 6756 additions and 5855 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,67 @@ |
||||
// 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 "dnn_common.hpp" |
||||
#include <opencv2/core/utils/configuration.private.hpp> |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
size_t getParam_DNN_NETWORK_DUMP() |
||||
{ |
||||
static size_t DNN_NETWORK_DUMP = utils::getConfigurationParameterSizeT("OPENCV_DNN_NETWORK_DUMP", 0); |
||||
return DNN_NETWORK_DUMP; |
||||
} |
||||
|
||||
// this option is useful to run with valgrind memory errors detection
|
||||
bool getParam_DNN_DISABLE_MEMORY_OPTIMIZATIONS() |
||||
{ |
||||
static bool DNN_DISABLE_MEMORY_OPTIMIZATIONS = utils::getConfigurationParameterBool("OPENCV_DNN_DISABLE_MEMORY_OPTIMIZATIONS", false); |
||||
return DNN_DISABLE_MEMORY_OPTIMIZATIONS; |
||||
} |
||||
|
||||
#ifdef HAVE_OPENCL |
||||
bool getParam_DNN_OPENCL_ALLOW_ALL_DEVICES() |
||||
{ |
||||
static bool DNN_OPENCL_ALLOW_ALL_DEVICES = utils::getConfigurationParameterBool("OPENCV_DNN_OPENCL_ALLOW_ALL_DEVICES", false); |
||||
return DNN_OPENCL_ALLOW_ALL_DEVICES; |
||||
} |
||||
#endif |
||||
|
||||
int getParam_DNN_BACKEND_DEFAULT() |
||||
{ |
||||
static int PARAM_DNN_BACKEND_DEFAULT = (int)utils::getConfigurationParameterSizeT("OPENCV_DNN_BACKEND_DEFAULT", |
||||
#ifdef HAVE_INF_ENGINE |
||||
(size_t)DNN_BACKEND_INFERENCE_ENGINE |
||||
#else |
||||
(size_t)DNN_BACKEND_OPENCV |
||||
#endif |
||||
); |
||||
return PARAM_DNN_BACKEND_DEFAULT; |
||||
} |
||||
|
||||
// Additional checks (slowdowns execution!)
|
||||
bool getParam_DNN_CHECK_NAN_INF() |
||||
{ |
||||
static bool DNN_CHECK_NAN_INF = utils::getConfigurationParameterBool("OPENCV_DNN_CHECK_NAN_INF", false); |
||||
return DNN_CHECK_NAN_INF; |
||||
} |
||||
bool getParam_DNN_CHECK_NAN_INF_DUMP() |
||||
{ |
||||
static bool DNN_CHECK_NAN_INF_DUMP = utils::getConfigurationParameterBool("OPENCV_DNN_CHECK_NAN_INF_DUMP", false); |
||||
return DNN_CHECK_NAN_INF_DUMP; |
||||
} |
||||
bool getParam_DNN_CHECK_NAN_INF_RAISE_ERROR() |
||||
{ |
||||
static bool DNN_CHECK_NAN_INF_RAISE_ERROR = utils::getConfigurationParameterBool("OPENCV_DNN_CHECK_NAN_INF_RAISE_ERROR", false); |
||||
return DNN_CHECK_NAN_INF_RAISE_ERROR; |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,93 @@ |
||||
// 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" |
||||
|
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
Net readNet(const String& _model, const String& _config, const String& _framework) |
||||
{ |
||||
String framework = toLowerCase(_framework); |
||||
String model = _model; |
||||
String config = _config; |
||||
const std::string modelExt = model.substr(model.rfind('.') + 1); |
||||
const std::string configExt = config.substr(config.rfind('.') + 1); |
||||
if (framework == "caffe" || modelExt == "caffemodel" || configExt == "caffemodel" || modelExt == "prototxt" || configExt == "prototxt") |
||||
{ |
||||
if (modelExt == "prototxt" || configExt == "caffemodel") |
||||
std::swap(model, config); |
||||
return readNetFromCaffe(config, model); |
||||
} |
||||
if (framework == "tensorflow" || modelExt == "pb" || configExt == "pb" || modelExt == "pbtxt" || configExt == "pbtxt") |
||||
{ |
||||
if (modelExt == "pbtxt" || configExt == "pb") |
||||
std::swap(model, config); |
||||
return readNetFromTensorflow(model, config); |
||||
} |
||||
if (framework == "torch" || modelExt == "t7" || modelExt == "net" || configExt == "t7" || configExt == "net") |
||||
{ |
||||
return readNetFromTorch(model.empty() ? config : model); |
||||
} |
||||
if (framework == "darknet" || modelExt == "weights" || configExt == "weights" || modelExt == "cfg" || configExt == "cfg") |
||||
{ |
||||
if (modelExt == "cfg" || configExt == "weights") |
||||
std::swap(model, config); |
||||
return readNetFromDarknet(config, model); |
||||
} |
||||
if (framework == "dldt" || modelExt == "bin" || configExt == "bin" || modelExt == "xml" || configExt == "xml") |
||||
{ |
||||
if (modelExt == "xml" || configExt == "bin") |
||||
std::swap(model, config); |
||||
return readNetFromModelOptimizer(config, model); |
||||
} |
||||
if (framework == "onnx" || modelExt == "onnx") |
||||
{ |
||||
return readNetFromONNX(model); |
||||
} |
||||
CV_Error(Error::StsError, "Cannot determine an origin framework of files: " + model + (config.empty() ? "" : ", " + config)); |
||||
} |
||||
|
||||
Net readNet(const String& _framework, const std::vector<uchar>& bufferModel, |
||||
const std::vector<uchar>& bufferConfig) |
||||
{ |
||||
String framework = toLowerCase(_framework); |
||||
if (framework == "caffe") |
||||
return readNetFromCaffe(bufferConfig, bufferModel); |
||||
else if (framework == "tensorflow") |
||||
return readNetFromTensorflow(bufferModel, bufferConfig); |
||||
else if (framework == "darknet") |
||||
return readNetFromDarknet(bufferConfig, bufferModel); |
||||
else if (framework == "torch") |
||||
CV_Error(Error::StsNotImplemented, "Reading Torch models from buffers"); |
||||
else if (framework == "dldt") |
||||
return readNetFromModelOptimizer(bufferConfig, bufferModel); |
||||
CV_Error(Error::StsError, "Cannot determine an origin framework with a name " + framework); |
||||
} |
||||
|
||||
Net readNetFromModelOptimizer(const String& xml, const String& bin) |
||||
{ |
||||
return Net::readFromModelOptimizer(xml, bin); |
||||
} |
||||
|
||||
Net readNetFromModelOptimizer(const std::vector<uchar>& bufferCfg, const std::vector<uchar>& bufferModel) |
||||
{ |
||||
return Net::readFromModelOptimizer(bufferCfg, bufferModel); |
||||
} |
||||
|
||||
Net readNetFromModelOptimizer( |
||||
const uchar* bufferModelConfigPtr, size_t bufferModelConfigSize, |
||||
const uchar* bufferWeightsPtr, size_t bufferWeightsSize) |
||||
{ |
||||
return Net::readFromModelOptimizer( |
||||
bufferModelConfigPtr, bufferModelConfigSize, |
||||
bufferWeightsPtr, bufferWeightsSize); |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,158 @@ |
||||
// 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 <opencv2/imgproc.hpp> |
||||
|
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
Mat blobFromImage(InputArray image, double scalefactor, const Size& size, |
||||
const Scalar& mean, bool swapRB, bool crop, int ddepth) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
Mat blob; |
||||
blobFromImage(image, blob, scalefactor, size, mean, swapRB, crop, ddepth); |
||||
return blob; |
||||
} |
||||
|
||||
void blobFromImage(InputArray image, OutputArray blob, double scalefactor, |
||||
const Size& size, const Scalar& mean, bool swapRB, bool crop, int ddepth) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
std::vector<Mat> images(1, image.getMat()); |
||||
blobFromImages(images, blob, scalefactor, size, mean, swapRB, crop, ddepth); |
||||
} |
||||
|
||||
Mat blobFromImages(InputArrayOfArrays images, double scalefactor, Size size, |
||||
const Scalar& mean, bool swapRB, bool crop, int ddepth) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
Mat blob; |
||||
blobFromImages(images, blob, scalefactor, size, mean, swapRB, crop, ddepth); |
||||
return blob; |
||||
} |
||||
|
||||
void blobFromImages(InputArrayOfArrays images_, OutputArray blob_, double scalefactor, |
||||
Size size, const Scalar& mean_, bool swapRB, bool crop, int ddepth) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_CheckType(ddepth, ddepth == CV_32F || ddepth == CV_8U, "Blob depth should be CV_32F or CV_8U"); |
||||
if (ddepth == CV_8U) |
||||
{ |
||||
CV_CheckEQ(scalefactor, 1.0, "Scaling is not supported for CV_8U blob depth"); |
||||
CV_Assert(mean_ == Scalar() && "Mean subtraction is not supported for CV_8U blob depth"); |
||||
} |
||||
|
||||
std::vector<Mat> images; |
||||
images_.getMatVector(images); |
||||
CV_Assert(!images.empty()); |
||||
for (size_t i = 0; i < images.size(); i++) |
||||
{ |
||||
Size imgSize = images[i].size(); |
||||
if (size == Size()) |
||||
size = imgSize; |
||||
if (size != imgSize) |
||||
{ |
||||
if (crop) |
||||
{ |
||||
float resizeFactor = std::max(size.width / (float)imgSize.width, |
||||
size.height / (float)imgSize.height); |
||||
resize(images[i], images[i], Size(), resizeFactor, resizeFactor, INTER_LINEAR); |
||||
Rect crop(Point(0.5 * (images[i].cols - size.width), |
||||
0.5 * (images[i].rows - size.height)), |
||||
size); |
||||
images[i] = images[i](crop); |
||||
} |
||||
else |
||||
resize(images[i], images[i], size, 0, 0, INTER_LINEAR); |
||||
} |
||||
if (images[i].depth() == CV_8U && ddepth == CV_32F) |
||||
images[i].convertTo(images[i], CV_32F); |
||||
Scalar mean = mean_; |
||||
if (swapRB) |
||||
std::swap(mean[0], mean[2]); |
||||
|
||||
images[i] -= mean; |
||||
images[i] *= scalefactor; |
||||
} |
||||
|
||||
size_t nimages = images.size(); |
||||
Mat image0 = images[0]; |
||||
int nch = image0.channels(); |
||||
CV_Assert(image0.dims == 2); |
||||
if (nch == 3 || nch == 4) |
||||
{ |
||||
int sz[] = { (int)nimages, nch, image0.rows, image0.cols }; |
||||
blob_.create(4, sz, ddepth); |
||||
Mat blob = blob_.getMat(); |
||||
Mat ch[4]; |
||||
|
||||
for (size_t i = 0; i < nimages; i++) |
||||
{ |
||||
const Mat& image = images[i]; |
||||
CV_Assert(image.depth() == blob_.depth()); |
||||
nch = image.channels(); |
||||
CV_Assert(image.dims == 2 && (nch == 3 || nch == 4)); |
||||
CV_Assert(image.size() == image0.size()); |
||||
|
||||
for (int j = 0; j < nch; j++) |
||||
ch[j] = Mat(image.rows, image.cols, ddepth, blob.ptr((int)i, j)); |
||||
if (swapRB) |
||||
std::swap(ch[0], ch[2]); |
||||
split(image, ch); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
CV_Assert(nch == 1); |
||||
int sz[] = { (int)nimages, 1, image0.rows, image0.cols }; |
||||
blob_.create(4, sz, ddepth); |
||||
Mat blob = blob_.getMat(); |
||||
|
||||
for (size_t i = 0; i < nimages; i++) |
||||
{ |
||||
const Mat& image = images[i]; |
||||
CV_Assert(image.depth() == blob_.depth()); |
||||
nch = image.channels(); |
||||
CV_Assert(image.dims == 2 && (nch == 1)); |
||||
CV_Assert(image.size() == image0.size()); |
||||
|
||||
image.copyTo(Mat(image.rows, image.cols, ddepth, blob.ptr((int)i, 0))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void imagesFromBlob(const cv::Mat& blob_, OutputArrayOfArrays images_) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
// A blob is a 4 dimensional matrix in floating point precision
|
||||
// blob_[0] = batchSize = nbOfImages
|
||||
// blob_[1] = nbOfChannels
|
||||
// blob_[2] = height
|
||||
// blob_[3] = width
|
||||
CV_Assert(blob_.depth() == CV_32F); |
||||
CV_Assert(blob_.dims == 4); |
||||
|
||||
images_.create(cv::Size(1, blob_.size[0]), blob_.depth()); |
||||
|
||||
std::vector<Mat> vectorOfChannels(blob_.size[1]); |
||||
for (int n = 0; n < blob_.size[0]; ++n) |
||||
{ |
||||
for (int c = 0; c < blob_.size[1]; ++c) |
||||
{ |
||||
vectorOfChannels[c] = getPlane(blob_, n, c); |
||||
} |
||||
cv::merge(vectorOfChannels, images_.getMatRef(n)); |
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,247 @@ |
||||
// 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" |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
Layer::Layer() { preferableTarget = DNN_TARGET_CPU; } |
||||
|
||||
Layer::Layer(const LayerParams& params) |
||||
: blobs(params.blobs) |
||||
, name(params.name) |
||||
, type(params.type) |
||||
{ |
||||
preferableTarget = DNN_TARGET_CPU; |
||||
} |
||||
|
||||
void Layer::setParamsFrom(const LayerParams& params) |
||||
{ |
||||
blobs = params.blobs; |
||||
name = params.name; |
||||
type = params.type; |
||||
} |
||||
|
||||
int Layer::inputNameToIndex(String) |
||||
{ |
||||
return -1; |
||||
} |
||||
|
||||
int Layer::outputNameToIndex(const String&) |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
bool Layer::supportBackend(int backendId) |
||||
{ |
||||
return backendId == DNN_BACKEND_OPENCV; |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::initCUDA( |
||||
void*, |
||||
const std::vector<Ptr<BackendWrapper>>&, |
||||
const std::vector<Ptr<BackendWrapper>>&) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "CUDA pipeline of " + type + " layers is not defined."); |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::initVkCom(const std::vector<Ptr<BackendWrapper>>&) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "VkCom pipeline of " + type + " layers is not defined."); |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::initHalide(const std::vector<Ptr<BackendWrapper>>&) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "Halide pipeline of " + type + " layers is not defined."); |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::initNgraph(const std::vector<Ptr<BackendWrapper>>& inputs, const std::vector<Ptr<BackendNode>>& nodes) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "Inference Engine pipeline of " + type + " layers is not defined."); |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::initWebnn(const std::vector<Ptr<BackendWrapper>>& inputs, const std::vector<Ptr<BackendNode>>& nodes) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, "WebNN pipeline of " + type + " layers is not defined."); |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
Ptr<BackendNode> Layer::tryAttach(const Ptr<BackendNode>& node) |
||||
{ |
||||
return Ptr<BackendNode>(); |
||||
} |
||||
|
||||
bool Layer::setActivation(const Ptr<ActivationLayer>&) { return false; } |
||||
bool Layer::tryFuse(Ptr<Layer>&) { return false; } |
||||
void Layer::getScaleShift(Mat& scale, Mat& shift) const |
||||
{ |
||||
scale = Mat(); |
||||
shift = Mat(); |
||||
} |
||||
|
||||
void Layer::getScaleZeropoint(float& scale, int& zeropoint) const |
||||
{ |
||||
scale = 1.f; |
||||
zeropoint = 0; |
||||
} |
||||
|
||||
void Layer::unsetAttached() |
||||
{ |
||||
setActivation(Ptr<ActivationLayer>()); |
||||
} |
||||
|
||||
template <typename T> |
||||
static void vecToPVec(const std::vector<T>& v, std::vector<T*>& pv) |
||||
{ |
||||
pv.resize(v.size()); |
||||
for (size_t i = 0; i < v.size(); i++) |
||||
pv[i] = const_cast<T*>(&v[i]); |
||||
} |
||||
|
||||
void Layer::finalize(const std::vector<Mat>& inputs, std::vector<Mat>& outputs) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
this->finalize((InputArrayOfArrays)inputs, (OutputArrayOfArrays)outputs); |
||||
} |
||||
|
||||
void Layer::finalize(const std::vector<Mat*>& input, std::vector<Mat>& output) |
||||
{ |
||||
CV_UNUSED(input); |
||||
CV_UNUSED(output); |
||||
} |
||||
|
||||
void Layer::finalize(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
std::vector<Mat> inputs, outputs; |
||||
inputs_arr.getMatVector(inputs); |
||||
outputs_arr.getMatVector(outputs); |
||||
|
||||
std::vector<Mat*> inputsp; |
||||
vecToPVec(inputs, inputsp); |
||||
this->finalize(inputsp, outputs); |
||||
} |
||||
|
||||
std::vector<Mat> Layer::finalize(const std::vector<Mat>& inputs) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
std::vector<Mat> outputs; |
||||
this->finalize(inputs, outputs); |
||||
return outputs; |
||||
} |
||||
|
||||
void Layer::forward(std::vector<Mat*>& input, std::vector<Mat>& output, std::vector<Mat>& internals) |
||||
{ |
||||
// We kept this method for compatibility. DNN calls it now only to support users' implementations.
|
||||
} |
||||
|
||||
void Layer::forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(name, "name", name.c_str()); |
||||
|
||||
Layer::forward_fallback(inputs_arr, outputs_arr, internals_arr); |
||||
} |
||||
|
||||
void Layer::forward_fallback(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(name, "name", name.c_str()); |
||||
|
||||
if (preferableTarget == DNN_TARGET_OPENCL_FP16 && inputs_arr.depth() == CV_16S) |
||||
{ |
||||
std::vector<UMat> inputs; |
||||
std::vector<UMat> outputs; |
||||
std::vector<UMat> internals; |
||||
|
||||
std::vector<UMat> orig_inputs; |
||||
std::vector<UMat> orig_outputs; |
||||
std::vector<UMat> orig_internals; |
||||
|
||||
inputs_arr.getUMatVector(orig_inputs); |
||||
outputs_arr.getUMatVector(orig_outputs); |
||||
internals_arr.getUMatVector(orig_internals); |
||||
|
||||
inputs.resize(orig_inputs.size()); |
||||
for (size_t i = 0; i < orig_inputs.size(); i++) |
||||
convertFp16(orig_inputs[i], inputs[i]); |
||||
|
||||
outputs.resize(orig_outputs.size()); |
||||
for (size_t i = 0; i < orig_outputs.size(); i++) |
||||
outputs[i].create(shape(orig_outputs[i]), CV_32F); |
||||
|
||||
internals.resize(orig_internals.size()); |
||||
for (size_t i = 0; i < orig_internals.size(); i++) |
||||
internals[i].create(shape(orig_internals[i]), CV_32F); |
||||
|
||||
forward(inputs, outputs, internals); |
||||
|
||||
for (size_t i = 0; i < outputs.size(); i++) |
||||
convertFp16(outputs[i], orig_outputs[i]); |
||||
|
||||
// sync results back
|
||||
outputs_arr.assign(orig_outputs); |
||||
internals_arr.assign(orig_internals); |
||||
return; |
||||
} |
||||
std::vector<Mat> inpvec; |
||||
std::vector<Mat> outputs; |
||||
std::vector<Mat> internals; |
||||
|
||||
inputs_arr.getMatVector(inpvec); |
||||
outputs_arr.getMatVector(outputs); |
||||
internals_arr.getMatVector(internals); |
||||
|
||||
std::vector<Mat*> inputs(inpvec.size()); |
||||
for (int i = 0; i < inpvec.size(); i++) |
||||
inputs[i] = &inpvec[i]; |
||||
|
||||
this->forward(inputs, outputs, internals); |
||||
|
||||
// sync results back
|
||||
outputs_arr.assign(outputs); |
||||
internals_arr.assign(internals); |
||||
} |
||||
|
||||
void Layer::run(const std::vector<Mat>& inputs, std::vector<Mat>& outputs, std::vector<Mat>& internals) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
this->finalize(inputs, outputs); |
||||
this->forward(inputs, outputs, internals); |
||||
} |
||||
|
||||
bool Layer::tryQuantize(const std::vector<std::vector<float>>& scales, |
||||
const std::vector<std::vector<int>>& zeropoints, LayerParams& params) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
Layer::~Layer() {} |
||||
|
||||
bool Layer::getMemoryShapes(const std::vector<MatShape>& inputs, |
||||
const int requiredOutputs, |
||||
std::vector<MatShape>& outputs, |
||||
std::vector<MatShape>& internals) const |
||||
{ |
||||
CV_Assert(inputs.size()); |
||||
outputs.assign(std::max(requiredOutputs, (int)inputs.size()), inputs[0]); |
||||
return false; |
||||
} |
||||
|
||||
bool Layer::updateMemoryShapes(const std::vector<MatShape>& inputs) |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,111 @@ |
||||
// 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 <opencv2/imgproc.hpp> |
||||
|
||||
#include <opencv2/dnn/layer_reg.private.hpp> // getLayerFactoryImpl |
||||
|
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
Mutex& getLayerFactoryMutex() |
||||
{ |
||||
static Mutex* volatile instance = NULL; |
||||
if (instance == NULL) |
||||
{ |
||||
cv::AutoLock lock(getInitializationMutex()); |
||||
if (instance == NULL) |
||||
instance = new Mutex(); |
||||
} |
||||
return *instance; |
||||
} |
||||
|
||||
static LayerFactory_Impl& getLayerFactoryImpl_() |
||||
{ |
||||
static LayerFactory_Impl impl; |
||||
return impl; |
||||
} |
||||
|
||||
LayerFactory_Impl& getLayerFactoryImpl() |
||||
{ |
||||
static LayerFactory_Impl* volatile instance = NULL; |
||||
if (instance == NULL) |
||||
{ |
||||
cv::AutoLock lock(getLayerFactoryMutex()); |
||||
if (instance == NULL) |
||||
{ |
||||
instance = &getLayerFactoryImpl_(); |
||||
initializeLayerFactory(); |
||||
} |
||||
} |
||||
return *instance; |
||||
} |
||||
|
||||
void LayerFactory::registerLayer(const String& type, Constructor constructor) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(type, "type", type.c_str()); |
||||
|
||||
cv::AutoLock lock(getLayerFactoryMutex()); |
||||
LayerFactory_Impl::iterator it = getLayerFactoryImpl().find(type); |
||||
|
||||
if (it != getLayerFactoryImpl().end()) |
||||
{ |
||||
if (it->second.back() == constructor) |
||||
CV_Error(cv::Error::StsBadArg, "Layer \"" + type + "\" already was registered"); |
||||
it->second.push_back(constructor); |
||||
} |
||||
getLayerFactoryImpl().insert(std::make_pair(type, std::vector<Constructor>(1, constructor))); |
||||
} |
||||
|
||||
void LayerFactory::unregisterLayer(const String& type) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(type, "type", type.c_str()); |
||||
|
||||
cv::AutoLock lock(getLayerFactoryMutex()); |
||||
|
||||
LayerFactory_Impl::iterator it = getLayerFactoryImpl().find(type); |
||||
if (it != getLayerFactoryImpl().end()) |
||||
{ |
||||
if (it->second.size() > 1) |
||||
it->second.pop_back(); |
||||
else |
||||
getLayerFactoryImpl().erase(it); |
||||
} |
||||
} |
||||
|
||||
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) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(type, "type", type.c_str()); |
||||
|
||||
cv::AutoLock lock(getLayerFactoryMutex()); |
||||
LayerFactory_Impl::const_iterator it = getLayerFactoryImpl().find(type); |
||||
|
||||
if (it != getLayerFactoryImpl().end()) |
||||
{ |
||||
CV_Assert(!it->second.empty()); |
||||
return it->second.back()(params); |
||||
} |
||||
else |
||||
{ |
||||
return Ptr<Layer>(); // NULL
|
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,335 @@ |
||||
// 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.
|
||||
|
||||
#ifndef __OPENCV_DNN_SRC_LAYER_INTERNALS_HPP__ |
||||
#define __OPENCV_DNN_SRC_LAYER_INTERNALS_HPP__ |
||||
|
||||
namespace cv { namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
inline namespace detail { |
||||
|
||||
struct LayerPin |
||||
{ |
||||
int lid; |
||||
int oid; |
||||
|
||||
LayerPin(int layerId = -1, int outputId = -1) |
||||
: lid(layerId) |
||||
, oid(outputId) |
||||
{} |
||||
|
||||
bool valid() const |
||||
{ |
||||
return (lid >= 0 && oid >= 0); |
||||
} |
||||
|
||||
bool equal(const LayerPin& r) const |
||||
{ |
||||
return (lid == r.lid && oid == r.oid); |
||||
} |
||||
|
||||
bool operator<(const LayerPin& r) const |
||||
{ |
||||
return lid < r.lid || (lid == r.lid && oid < r.oid); |
||||
} |
||||
|
||||
bool operator==(const LayerPin& r) const |
||||
{ |
||||
return lid == r.lid && oid == r.oid; |
||||
} |
||||
}; |
||||
|
||||
struct LayerData |
||||
{ |
||||
LayerData() |
||||
: id(-1) |
||||
, dtype(CV_32F) |
||||
, skip(false) |
||||
, flag(0) |
||||
{} |
||||
LayerData(int _id, const String& _name, const String& _type, const int& _dtype, LayerParams& _params) |
||||
: id(_id) |
||||
, name(_name) |
||||
, type(_type) |
||||
, dtype(_dtype) |
||||
, params(_params) |
||||
, skip(false) |
||||
, flag(0) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
// add logging info
|
||||
params.name = name; |
||||
params.type = type; |
||||
} |
||||
|
||||
int id; |
||||
String name; |
||||
String type; |
||||
int dtype; // Datatype of output blobs.
|
||||
LayerParams params; |
||||
|
||||
std::vector<LayerPin> inputBlobsId; |
||||
std::set<int> inputLayersId; |
||||
std::set<int> requiredOutputs; |
||||
std::vector<LayerPin> consumers; |
||||
std::vector<Ptr<BackendWrapper>> outputBlobsWrappers; |
||||
std::vector<Ptr<BackendWrapper>> inputBlobsWrappers; |
||||
std::vector<Ptr<BackendWrapper>> internalBlobsWrappers; |
||||
|
||||
#ifdef HAVE_CUDA |
||||
/* output ids which must be transferred to the host in the background
|
||||
* after the completion of the forward pass of the layer |
||||
*/ |
||||
std::vector<int> cudaD2HBackgroundTransfers; |
||||
#endif |
||||
|
||||
Ptr<Layer> layerInstance; |
||||
std::vector<Mat> outputBlobs; |
||||
std::vector<Mat*> inputBlobs; |
||||
std::vector<Mat> internals; |
||||
// Computation nodes of implemented backends (except DEFAULT).
|
||||
std::map<int, Ptr<BackendNode>> backendNodes; |
||||
// Flag for skip layer computation for specific backend.
|
||||
bool skip; |
||||
|
||||
int flag; |
||||
|
||||
Ptr<Layer> getLayerInstance() |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(type, "type", type.c_str()); |
||||
|
||||
if (layerInstance) |
||||
return layerInstance; |
||||
|
||||
layerInstance = LayerFactory::createLayerInstance(type, params); |
||||
if (!layerInstance) |
||||
{ |
||||
CV_Error(Error::StsError, "Can't create layer \"" + name + "\" of type \"" + type + "\""); |
||||
} |
||||
|
||||
return layerInstance; |
||||
} |
||||
}; |
||||
|
||||
|
||||
// fake layer containing network input blobs
|
||||
struct DataLayer : public Layer |
||||
{ |
||||
DataLayer() |
||||
: Layer() |
||||
{ |
||||
skip = false; |
||||
} |
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE |
||||
{ |
||||
return backendId == DNN_BACKEND_OPENCV; |
||||
} |
||||
|
||||
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(name, "name", name.c_str()); |
||||
|
||||
// FIXIT: add wrapper without exception suppression
|
||||
CV_OCL_RUN(IS_DNN_OPENCL_TARGET(preferableTarget), |
||||
forward_ocl(inputs_arr, outputs_arr, internals_arr)) |
||||
|
||||
bool isFP16 = outputs_arr.depth() == CV_16S; |
||||
|
||||
std::vector<Mat> outputs, internals; |
||||
outputs_arr.getMatVector(outputs); |
||||
internals_arr.getMatVector(internals); |
||||
|
||||
for (int i = 0; i < inputsData.size(); ++i) |
||||
{ |
||||
double scale = scaleFactors[i]; |
||||
Scalar& mean = means[i]; |
||||
|
||||
CV_Assert(mean == Scalar() || inputsData[i].size[1] <= 4); |
||||
if (isFP16) |
||||
CV_CheckTypeEQ(outputs[i].type(), CV_16SC1, ""); |
||||
else |
||||
CV_CheckTypeEQ(outputs[i].type(), CV_32FC1, ""); |
||||
|
||||
bool singleMean = true; |
||||
for (int j = 1; j < std::min(4, inputsData[i].size[1]) && singleMean; ++j) |
||||
{ |
||||
singleMean = mean[j] == mean[j - 1]; |
||||
} |
||||
|
||||
if (singleMean) |
||||
{ |
||||
if (isFP16) |
||||
{ |
||||
Mat input_f32; |
||||
inputsData[i].convertTo(input_f32, CV_32F, scale, -mean[0] * scale); |
||||
convertFp16(input_f32, outputs[i]); |
||||
} |
||||
else |
||||
{ |
||||
inputsData[i].convertTo(outputs[i], CV_32F, scale, -mean[0] * scale); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
for (int n = 0; n < inputsData[i].size[0]; ++n) |
||||
{ |
||||
for (int c = 0; c < inputsData[i].size[1]; ++c) |
||||
{ |
||||
Mat inp = getPlane(inputsData[i], n, c); |
||||
Mat out = getPlane(outputs[i], n, c); |
||||
if (isFP16) |
||||
{ |
||||
Mat input_f32; |
||||
inp.convertTo(input_f32, CV_32F, scale, -mean[c] * scale); |
||||
convertFp16(input_f32, out); |
||||
} |
||||
else |
||||
{ |
||||
inp.convertTo(out, CV_32F, scale, -mean[c] * scale); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
#ifdef HAVE_OPENCL |
||||
bool forward_ocl(InputArrayOfArrays, OutputArrayOfArrays outputs_, OutputArrayOfArrays internals_) |
||||
{ |
||||
bool isFP16 = outputs_.depth() == CV_16S; |
||||
|
||||
std::vector<UMat> outputs; |
||||
outputs_.getUMatVector(outputs); |
||||
|
||||
for (int i = 0; i < inputsData.size(); ++i) |
||||
{ |
||||
Mat inputData = inputsData[i]; |
||||
|
||||
double scale = scaleFactors[i]; |
||||
Scalar& mean = means[i]; |
||||
|
||||
CV_Assert(mean == Scalar() || inputData.size[1] <= 4); |
||||
if (isFP16) |
||||
CV_CheckTypeEQ(outputs[i].type(), CV_16SC1, ""); |
||||
else |
||||
CV_CheckTypeEQ(outputs[i].type(), CV_32FC1, ""); |
||||
|
||||
bool singleMean = true; |
||||
for (int j = 1; j < std::min(4, inputData.size[1]) && singleMean; ++j) |
||||
{ |
||||
singleMean = mean[j] == mean[j - 1]; |
||||
} |
||||
|
||||
if (singleMean) |
||||
{ |
||||
if (isFP16) |
||||
{ |
||||
UMat input_i; |
||||
inputData.convertTo(input_i, CV_32F, scale, -mean[0] * scale); |
||||
convertFp16(input_i, outputs[i]); |
||||
} |
||||
else |
||||
{ |
||||
inputData.convertTo(outputs[i], CV_32F, scale, -mean[0] * scale); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
for (int n = 0; n < inputData.size[0]; ++n) |
||||
{ |
||||
for (int c = 0; c < inputData.size[1]; ++c) |
||||
{ |
||||
Mat inp = getPlane(inputData, n, c); |
||||
|
||||
std::vector<cv::Range> plane(4, Range::all()); |
||||
plane[0] = Range(n, n + 1); |
||||
plane[1] = Range(c, c + 1); |
||||
UMat out = outputs[i](plane).reshape(1, inp.dims, inp.size); |
||||
|
||||
if (isFP16) |
||||
{ |
||||
UMat input_i; |
||||
inp.convertTo(input_i, CV_32F, scale, -mean[c] * scale); |
||||
convertFp16(input_i, out); |
||||
} |
||||
else |
||||
{ |
||||
inp.convertTo(out, CV_32F, scale, -mean[c] * scale); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
#endif |
||||
|
||||
int outputNameToIndex(const String& tgtName) CV_OVERRIDE |
||||
{ |
||||
int idx = (int)(std::find(outNames.begin(), outNames.end(), tgtName) - outNames.begin()); |
||||
return (idx < (int)outNames.size()) ? idx : -1; |
||||
} |
||||
|
||||
void setNames(const std::vector<String>& names) |
||||
{ |
||||
outNames.assign(names.begin(), names.end()); |
||||
shapes.clear(); |
||||
shapes.resize(outNames.size()); |
||||
} |
||||
|
||||
void setInputShape(const String& tgtName, const MatShape& shape) |
||||
{ |
||||
std::vector<String>::const_iterator it = std::find(outNames.begin(), outNames.end(), tgtName); |
||||
CV_Check(tgtName, it != outNames.end(), "Unknown input"); |
||||
int idx = (int)(it - outNames.begin()); |
||||
|
||||
CV_Assert(idx < (int)shapes.size()); |
||||
CV_Check(tgtName, shapes[idx].empty(), "Input shape redefinition is not allowed"); |
||||
shapes[idx] = shape; |
||||
} |
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape>& inputs, |
||||
const int requiredOutputs, |
||||
std::vector<MatShape>& outputs, |
||||
std::vector<MatShape>& internals) const CV_OVERRIDE |
||||
{ |
||||
CV_Assert(inputs.size() == requiredOutputs); |
||||
outputs.assign(inputs.begin(), inputs.end()); |
||||
return false; |
||||
} |
||||
|
||||
virtual void finalize(InputArrayOfArrays, OutputArrayOfArrays outputs_arr) CV_OVERRIDE |
||||
{ |
||||
std::vector<Mat> outputs; |
||||
outputs_arr.getMatVector(outputs); |
||||
|
||||
CV_Assert_N(outputs.size() == scaleFactors.size(), outputs.size() == means.size(), |
||||
inputsData.size() == outputs.size()); |
||||
skip = true; |
||||
for (int i = 0; skip && i < inputsData.size(); ++i) |
||||
{ |
||||
if (inputsData[i].data != outputs[i].data || scaleFactors[i] != 1.0 || means[i] != Scalar()) |
||||
skip = false; |
||||
} |
||||
} |
||||
|
||||
|
||||
std::vector<String> outNames; |
||||
std::vector<MatShape> shapes; |
||||
// Preprocessing parameters for each network's input.
|
||||
std::vector<double> scaleFactors; |
||||
std::vector<Scalar> means; |
||||
std::vector<Mat> inputsData; |
||||
bool skip; |
||||
}; // DataLayer
|
||||
|
||||
|
||||
} // namespace detail
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
||||
#endif // __OPENCV_DNN_SRC_LAYER_INTERNALS_HPP__
|
@ -0,0 +1,122 @@ |
||||
// 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 "legacy_backend.hpp" |
||||
|
||||
#include "op_halide.hpp" |
||||
#include "op_inf_engine.hpp" |
||||
#include "ie_ngraph.hpp" |
||||
#include "op_vkcom.hpp" |
||||
#include "op_cuda.hpp" |
||||
#include "op_webnn.hpp" |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
BackendNode::BackendNode(int backendId) |
||||
: backendId(backendId) |
||||
{} |
||||
|
||||
BackendNode::~BackendNode() {}; |
||||
|
||||
BackendWrapper::BackendWrapper(int backendId, int targetId) |
||||
: backendId(backendId) |
||||
, targetId(targetId) |
||||
{} |
||||
|
||||
BackendWrapper::BackendWrapper(int targetId, const cv::Mat& m) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, |
||||
"Constructor of backend wrapper must be implemented"); |
||||
} |
||||
|
||||
BackendWrapper::BackendWrapper(const Ptr<BackendWrapper>& base, const MatShape& shape) |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, |
||||
"Constructor of backend wrapper must be implemented"); |
||||
} |
||||
|
||||
BackendWrapper::~BackendWrapper() {} |
||||
|
||||
|
||||
|
||||
inline namespace detail { |
||||
|
||||
|
||||
Ptr<BackendWrapper> wrapMat(int backendId, int targetId, cv::Mat& m) |
||||
{ |
||||
if (backendId == DNN_BACKEND_OPENCV) |
||||
{ |
||||
if (targetId == DNN_TARGET_CPU) |
||||
return Ptr<BackendWrapper>(); |
||||
#ifdef HAVE_OPENCL |
||||
else if (IS_DNN_OPENCL_TARGET(targetId)) |
||||
return OpenCLBackendWrapper::create(m); |
||||
#endif |
||||
else |
||||
CV_Error(Error::StsNotImplemented, "Unknown/unsupported target identifier"); |
||||
} |
||||
else if (backendId == DNN_BACKEND_HALIDE) |
||||
{ |
||||
CV_Assert(haveHalide()); |
||||
#ifdef HAVE_HALIDE |
||||
return Ptr<BackendWrapper>(new HalideBackendWrapper(targetId, m)); |
||||
#endif // HAVE_HALIDE
|
||||
} |
||||
else if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) |
||||
{ |
||||
CV_ERROR_DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019; |
||||
} |
||||
else if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
return Ptr<BackendWrapper>(new NgraphBackendWrapper(targetId, m)); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of OpenVINO / Inference Engine + nGraph"); |
||||
#endif |
||||
} |
||||
else if (backendId == DNN_BACKEND_WEBNN) |
||||
{ |
||||
#ifdef HAVE_WEBNN |
||||
return Ptr<BackendWrapper>(new WebnnBackendWrapper(targetId, m)); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of WebNN"); |
||||
#endif |
||||
} |
||||
else if (backendId == DNN_BACKEND_VKCOM) |
||||
{ |
||||
CV_Assert(haveVulkan()); |
||||
#ifdef HAVE_VULKAN |
||||
return Ptr<BackendWrapper>(new VkComBackendWrapper(m)); |
||||
#endif // HAVE_VULKAN
|
||||
} |
||||
else if (backendId == DNN_BACKEND_CUDA) |
||||
{ |
||||
CV_Assert(haveCUDA()); |
||||
|
||||
#ifdef HAVE_CUDA |
||||
switch (targetId) |
||||
{ |
||||
case DNN_TARGET_CUDA: |
||||
return CUDABackendWrapperFP32::create(m); |
||||
case DNN_TARGET_CUDA_FP16: |
||||
return CUDABackendWrapperFP16::create(m); |
||||
default: |
||||
CV_Assert(IS_DNN_CUDA_TARGET(targetId)); |
||||
} |
||||
#endif |
||||
} |
||||
else |
||||
CV_Error(Error::StsNotImplemented, "Unknown backend identifier"); |
||||
return Ptr<BackendWrapper>(); // TODO Error?
|
||||
} // wrapMat()
|
||||
|
||||
|
||||
} // namespace detail
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,339 @@ |
||||
// 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.
|
||||
|
||||
#ifndef __OPENCV_DNN_SRC_LEGACY_BACKEND_HPP__ |
||||
#define __OPENCV_DNN_SRC_LEGACY_BACKEND_HPP__ |
||||
|
||||
#include "layer_internals.hpp" // LayerPin LayerData DataLayer |
||||
|
||||
namespace cv { namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
inline namespace detail { |
||||
|
||||
|
||||
#ifdef HAVE_OPENCL |
||||
class OpenCLBackendWrapper : public BackendWrapper |
||||
{ |
||||
public: |
||||
OpenCLBackendWrapper(Mat& m) |
||||
: BackendWrapper(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL) |
||||
{ |
||||
m.copyTo(umat); |
||||
host = &m; |
||||
hostDirty = false; |
||||
} |
||||
|
||||
OpenCLBackendWrapper(const Ptr<BackendWrapper>& baseBuffer, Mat& m) |
||||
: BackendWrapper(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL) |
||||
{ |
||||
Ptr<OpenCLBackendWrapper> base = baseBuffer.dynamicCast<OpenCLBackendWrapper>(); |
||||
CV_Assert(!base.empty()); |
||||
|
||||
host = &m; |
||||
|
||||
int shape[] = { 1, (int)base->umat.total() }; |
||||
umat = base->umat.reshape(1, 2, &shape[0]) |
||||
.colRange(0, host->total()) |
||||
.reshape(1, host->dims, &host->size[0]); |
||||
hostDirty = false; |
||||
} |
||||
|
||||
static Ptr<BackendWrapper> create(Mat& m) |
||||
{ |
||||
return Ptr<BackendWrapper>(new OpenCLBackendWrapper(m)); |
||||
} |
||||
|
||||
static Ptr<BackendWrapper> create(const Ptr<BackendWrapper>& baseBuffer, Mat& m) |
||||
{ |
||||
return Ptr<BackendWrapper>(new OpenCLBackendWrapper(baseBuffer, m)); |
||||
} |
||||
|
||||
static std::vector<UMat> getUMatVector(const std::vector<Ptr<BackendWrapper>>& wrappers) |
||||
{ |
||||
const int numWrappers = wrappers.size(); |
||||
std::vector<UMat> mats(wrappers.size()); |
||||
for (int i = 0; i < numWrappers; ++i) |
||||
{ |
||||
Ptr<OpenCLBackendWrapper> umatWrapper = wrappers[i].dynamicCast<OpenCLBackendWrapper>(); |
||||
CV_Assert(!umatWrapper.empty()); |
||||
umatWrapper->copyToDevice(); |
||||
mats[i] = umatWrapper->umat; |
||||
} |
||||
return mats; |
||||
} |
||||
|
||||
// Replaces all umats in wrappers to specific ones.
|
||||
static void update(const std::vector<Ptr<BackendWrapper>>& wrappers, |
||||
const std::vector<UMat>& umats) |
||||
{ |
||||
CV_Assert(wrappers.size() == umats.size()); |
||||
for (int i = 0, n = umats.size(); i < n; ++i) |
||||
{ |
||||
Ptr<OpenCLBackendWrapper> umatWrapper = wrappers[i].dynamicCast<OpenCLBackendWrapper>(); |
||||
CV_Assert(!umatWrapper.empty()); |
||||
umatWrapper->umat = umats[i]; |
||||
} |
||||
} |
||||
|
||||
~OpenCLBackendWrapper() {} |
||||
|
||||
// Copies data from device to a host memory.
|
||||
virtual void copyToHost() CV_OVERRIDE |
||||
{ |
||||
umat.copyTo(*host); |
||||
} |
||||
|
||||
virtual void setHostDirty() CV_OVERRIDE |
||||
{ |
||||
hostDirty = true; |
||||
}; |
||||
|
||||
void copyToDevice() |
||||
{ |
||||
if (hostDirty) |
||||
{ |
||||
host->copyTo(umat); |
||||
hostDirty = false; |
||||
} |
||||
} |
||||
|
||||
private: |
||||
UMat umat; |
||||
Mat* host; |
||||
bool hostDirty; |
||||
}; // OpenCLBackendWrapper
|
||||
#endif // HAVE_OPENCL
|
||||
|
||||
|
||||
struct BlobManager |
||||
{ |
||||
public: |
||||
// Increase references counter to layer output.
|
||||
void addReference(const LayerPin& lp) |
||||
{ |
||||
std::map<LayerPin, int>::iterator it = refCounter.find(lp); |
||||
if (it == refCounter.end()) |
||||
refCounter[lp] = 1; |
||||
else |
||||
it->second += 1; |
||||
} |
||||
|
||||
void addReferences(const std::vector<LayerPin>& pins) |
||||
{ |
||||
for (int i = 0; i < pins.size(); i++) |
||||
{ |
||||
addReference(pins[i]); |
||||
} |
||||
} |
||||
|
||||
// Returns number of references to allocated memory that used in specific
|
||||
// layer blob.
|
||||
int numReferences(const LayerPin& lp) |
||||
{ |
||||
std::map<LayerPin, LayerPin>::const_iterator mapIt = reuseMap.find(lp); |
||||
CV_Assert(mapIt != reuseMap.end()); |
||||
LayerPin memHost = mapIt->second; |
||||
|
||||
std::map<LayerPin, int>::const_iterator refIt = refCounter.find(memHost); |
||||
CV_Assert(refIt != refCounter.end()); |
||||
return refIt->second; |
||||
} |
||||
|
||||
// Reuse data allocated in <host> inside the <user> blob.
|
||||
void reuse(const LayerPin& host, const LayerPin& user) |
||||
{ |
||||
CV_Assert(reuseMap.find(user) == reuseMap.end()); |
||||
CV_Assert(reuseMap.find(host) != reuseMap.end()); |
||||
LayerPin memHost = reuseMap[host]; |
||||
reuseMap[user] = memHost; |
||||
if (refCounter.find(memHost) != refCounter.end()) |
||||
{ |
||||
std::map<LayerPin, int>::iterator userRefIt = refCounter.find(user); |
||||
if (userRefIt != refCounter.end()) |
||||
{ |
||||
refCounter[memHost] += userRefIt->second; |
||||
refCounter.erase(userRefIt); |
||||
} |
||||
else |
||||
refCounter[memHost] += 1; |
||||
} |
||||
} |
||||
|
||||
// Decrease references counter to allocated memory inside specific blob.
|
||||
void releaseReference(const LayerPin& lp) |
||||
{ |
||||
std::map<LayerPin, LayerPin>::const_iterator mapIt = reuseMap.find(lp); |
||||
CV_Assert(mapIt != reuseMap.end()); |
||||
|
||||
std::map<LayerPin, int>::iterator refIt = refCounter.find(mapIt->second); |
||||
CV_Assert(refIt != refCounter.end()); |
||||
CV_Assert(refIt->second > 0); |
||||
refIt->second -= 1; |
||||
} |
||||
|
||||
void releaseReferences(const std::vector<LayerPin>& pins) |
||||
{ |
||||
for (int i = 0; i < pins.size(); i++) |
||||
{ |
||||
releaseReference(pins[i]); |
||||
} |
||||
} |
||||
|
||||
void reuseOrCreate(const MatShape& shape, const LayerPin& lp, Mat& dst, const int& dtype) |
||||
{ |
||||
if (!getParam_DNN_DISABLE_MEMORY_OPTIMIZATIONS()) |
||||
{ |
||||
Mat bestBlob; |
||||
LayerPin bestBlobPin; |
||||
|
||||
std::map<LayerPin, Mat>::const_iterator hostIt; |
||||
std::map<LayerPin, int>::const_iterator refIt; |
||||
|
||||
const int targetTotal = total(shape); |
||||
int bestBlobTotal = INT_MAX; |
||||
|
||||
for (hostIt = memHosts.begin(); hostIt != memHosts.end(); ++hostIt) |
||||
{ |
||||
refIt = refCounter.find(hostIt->first); |
||||
// Use only blobs that had references before because if not,
|
||||
// it might be used as output.
|
||||
if (refIt != refCounter.end() && refIt->second == 0) |
||||
{ |
||||
const Mat& unusedBlob = hostIt->second; |
||||
if (unusedBlob.total() >= targetTotal && unusedBlob.total() < bestBlobTotal && unusedBlob.type() == dtype) |
||||
{ |
||||
bestBlobPin = hostIt->first; |
||||
bestBlob = unusedBlob; |
||||
bestBlobTotal = unusedBlob.total(); |
||||
} |
||||
} |
||||
} |
||||
if (!bestBlob.empty()) |
||||
{ |
||||
reuse(bestBlobPin, lp); |
||||
dst = bestBlob.reshape(1, 1).colRange(0, targetTotal).reshape(1, shape); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
{ |
||||
// if dst already has been allocated with total(shape) elements,
|
||||
// it won't be recreated and pointer of dst.data remains the same.
|
||||
dst.create(shape, dtype); |
||||
addHost(lp, dst); |
||||
} |
||||
} |
||||
|
||||
void allocateBlobsForLayer(LayerData& ld, const LayerShapes& layerShapes, |
||||
std::vector<LayerPin>& pinsForInternalBlobs) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
pinsForInternalBlobs.clear(); |
||||
|
||||
std::vector<Mat>&outputBlobs = ld.outputBlobs, |
||||
&internalBlobs = ld.internals; |
||||
|
||||
const ShapesVec &outShapes = layerShapes.out, |
||||
internalShapes = layerShapes.internal; |
||||
|
||||
outputBlobs.resize(std::max((size_t)1, outShapes.size())); // layer produce at least one output blob
|
||||
internalBlobs.resize(internalShapes.size()); |
||||
|
||||
CV_Assert(ld.requiredOutputs.size() <= outShapes.size()); |
||||
|
||||
// Check that layer could work in-place.
|
||||
bool inPlace = false; |
||||
if (layerShapes.supportInPlace) |
||||
{ |
||||
if (ld.inputBlobs.size() == 1) |
||||
{ |
||||
// Get number of references to the input memory.
|
||||
int numRef = numReferences(ld.inputBlobsId[0]); |
||||
// If current layer is one and only customer of this blob.
|
||||
inPlace = numRef == 1; |
||||
} |
||||
} |
||||
|
||||
ShapesVec shapes(outShapes); |
||||
shapes.insert(shapes.end(), internalShapes.begin(), internalShapes.end()); |
||||
std::vector<Mat*> blobs; |
||||
for (int i = 0; i < outputBlobs.size(); i++) |
||||
{ |
||||
blobs.push_back(&outputBlobs[i]); |
||||
} |
||||
|
||||
for (int i = 0; i < internalBlobs.size(); i++) |
||||
{ |
||||
blobs.push_back(&internalBlobs[i]); |
||||
if (total(internalShapes[i])) |
||||
{ |
||||
pinsForInternalBlobs.push_back(LayerPin(ld.id, ld.outputBlobs.size() + i)); |
||||
} |
||||
} |
||||
|
||||
addReferences(pinsForInternalBlobs); |
||||
|
||||
std::map<int, std::vector<int>> idxSizes; |
||||
for (int i = 0; i < shapes.size(); i++) |
||||
{ |
||||
idxSizes[total(shapes[i])].push_back(i); |
||||
} |
||||
|
||||
std::map<int, std::vector<int>>::reverse_iterator it; |
||||
for (it = idxSizes.rbegin(); it != idxSizes.rend(); it++) |
||||
{ |
||||
for (int j = 0; j < it->second.size(); j++) |
||||
{ |
||||
int index = it->second[j]; |
||||
if (total(shapes[index])) |
||||
{ |
||||
LayerPin blobPin(ld.id, index); |
||||
if (index < outShapes.size() && inPlace) |
||||
{ |
||||
CV_Assert(ld.inputBlobs[0]->total() == total(shapes[index])); |
||||
ld.outputBlobs[index] = ld.inputBlobs[0]->reshape(1, shapes[index]); |
||||
reuse(ld.inputBlobsId[0], blobPin); |
||||
} |
||||
else |
||||
reuseOrCreate(shapes[index], blobPin, *blobs[index], ld.dtype); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Clear internal state. Calls before an every reallocation.
|
||||
void reset() |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
refCounter.clear(); |
||||
reuseMap.clear(); |
||||
memHosts.clear(); |
||||
} |
||||
|
||||
private: |
||||
// Register allocated memory.
|
||||
void addHost(const LayerPin& lp, const Mat& mat) |
||||
{ |
||||
CV_Assert(memHosts.find(lp) == memHosts.end()); |
||||
reuseMap[lp] = lp; |
||||
memHosts[lp] = mat; |
||||
} |
||||
|
||||
std::map<LayerPin, int> refCounter; |
||||
// Maps pin to origin blob (for whom memory was allocated firstly).
|
||||
// For origin blobs key == value.
|
||||
std::map<LayerPin, LayerPin> reuseMap; |
||||
std::map<LayerPin, Mat> memHosts; |
||||
}; // BlobManager
|
||||
|
||||
|
||||
Ptr<BackendWrapper> wrapMat(int backendId, int targetId, cv::Mat& m); |
||||
|
||||
|
||||
} // namespace detail
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
||||
#endif // __OPENCV_DNN_SRC_LEGACY_BACKEND_HPP__
|
@ -0,0 +1,414 @@ |
||||
// 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 |
||||
|
||||
Net::Net() |
||||
: impl(makePtr<Net::Impl>()) |
||||
{ |
||||
} |
||||
|
||||
Net::~Net() |
||||
{ |
||||
} |
||||
|
||||
int Net::addLayer(const String& name, const String& type, const int& dtype, LayerParams& params) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->addLayer(name, type, dtype, params); |
||||
} |
||||
|
||||
int Net::addLayer(const String& name, const String& type, LayerParams& params) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
return addLayer(name, type, CV_32F, params); |
||||
} |
||||
|
||||
int Net::addLayerToPrev(const String& name, const String& type, const int& dtype, LayerParams& params) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->addLayerToPrev(name, type, dtype, params); |
||||
} |
||||
|
||||
int Net::addLayerToPrev(const String& name, const String& type, LayerParams& params) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
return addLayerToPrev(name, type, CV_32F, params); |
||||
} |
||||
|
||||
void Net::connect(int outLayerId, int outNum, int inpLayerId, int inpNum) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
impl->connect(outLayerId, outNum, inpLayerId, inpNum); |
||||
} |
||||
|
||||
void Net::connect(String _outPin, String _inPin) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
CV_Assert(impl); |
||||
LayerPin outPin = impl->getPinByAlias(_outPin); |
||||
LayerPin inpPin = impl->getPinByAlias(_inPin); |
||||
|
||||
CV_Assert(outPin.valid() && inpPin.valid()); |
||||
|
||||
impl->connect(outPin.lid, outPin.oid, inpPin.lid, inpPin.oid); |
||||
} |
||||
|
||||
int Net::registerOutput(const std::string& outputName, int layerId, int outputPort) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->registerOutput(outputName, layerId, outputPort); |
||||
} |
||||
|
||||
Mat Net::forward(const String& outputName) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->forward(outputName); |
||||
} |
||||
|
||||
AsyncArray Net::forwardAsync(const String& outputName) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->forwardAsync(outputName); |
||||
} |
||||
|
||||
void Net::forward(OutputArrayOfArrays outputBlobs, const String& outputName) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->forward(outputBlobs, outputName); |
||||
} |
||||
|
||||
void Net::forward(OutputArrayOfArrays outputBlobs, |
||||
const std::vector<String>& outBlobNames) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->forward(outputBlobs, outBlobNames); |
||||
} |
||||
|
||||
void Net::forward(std::vector<std::vector<Mat>>& outputBlobs, |
||||
const std::vector<String>& outBlobNames) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->forward(outputBlobs, outBlobNames); |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
Net Net::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->quantize(calibData, inputsDtype, outputsDtype); |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
void Net::getInputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->getInputDetails(scales, zeropoints); |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
void Net::getOutputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->getOutputDetails(scales, zeropoints); |
||||
} |
||||
|
||||
void Net::setPreferableBackend(int backendId) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG(backendId); |
||||
CV_Assert(impl); |
||||
return impl->setPreferableBackend(backendId); |
||||
} |
||||
|
||||
void Net::setPreferableTarget(int targetId) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG(targetId); |
||||
CV_Assert(impl); |
||||
return impl->setPreferableTarget(targetId); |
||||
} |
||||
|
||||
void Net::setInputsNames(const std::vector<String>& inputBlobNames) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->setInputsNames(inputBlobNames); |
||||
} |
||||
|
||||
void Net::setInputShape(const String& inputName, const MatShape& shape) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->setInputShape(inputName, shape); |
||||
} |
||||
|
||||
void Net::setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(name, "name", name.c_str()); |
||||
CV_Assert(impl); |
||||
return impl->setInput(blob, name, scalefactor, mean); |
||||
} |
||||
|
||||
Mat Net::getParam(int layer, int numParam) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getParam(layer, numParam); |
||||
} |
||||
|
||||
void Net::setParam(int layer, int numParam, const Mat& blob) |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->setParam(layer, numParam, blob); |
||||
} |
||||
|
||||
int Net::getLayerId(const String& layer) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayerId(layer); |
||||
} |
||||
|
||||
String Net::dump() |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
return impl->dump(true); |
||||
} |
||||
|
||||
void Net::dumpToFile(const String& path) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
CV_Assert(!empty()); |
||||
std::ofstream file(path.c_str()); |
||||
file << dump(); |
||||
file.close(); |
||||
} |
||||
|
||||
Ptr<Layer> Net::getLayer(int layerId) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayer(layerId); |
||||
} |
||||
Ptr<Layer> Net::getLayer(const LayerId& layerId) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayer(layerId); |
||||
} |
||||
|
||||
std::vector<Ptr<Layer>> Net::getLayerInputs(int layerId) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayerInputs(layerId); |
||||
} |
||||
|
||||
std::vector<String> Net::getLayerNames() const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayerNames(); |
||||
} |
||||
|
||||
bool Net::empty() const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->empty(); |
||||
} |
||||
|
||||
// FIXIT drop "unconnected" API
|
||||
std::vector<int> Net::getUnconnectedOutLayers() const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getUnconnectedOutLayers(); |
||||
} |
||||
|
||||
// FIXIT drop "unconnected" API
|
||||
std::vector<String> Net::getUnconnectedOutLayersNames() const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getUnconnectedOutLayersNames(); |
||||
} |
||||
|
||||
void Net::getLayersShapes(const ShapesVec& netInputShapes, |
||||
std::vector<int>& layersIds, |
||||
std::vector<ShapesVec>& inLayersShapes, |
||||
std::vector<ShapesVec>& outLayersShapes) const |
||||
{ |
||||
CV_Assert(impl); |
||||
return impl->getLayersShapes(netInputShapes, layersIds, inLayersShapes, outLayersShapes); |
||||
} |
||||
|
||||
void Net::getLayersShapes(const MatShape& netInputShape, |
||||
std::vector<int>& layerIds, |
||||
std::vector<ShapesVec>& inLayersShapes, |
||||
std::vector<ShapesVec>& outLayersShapes) const |
||||
{ |
||||
getLayersShapes(ShapesVec(1, netInputShape), |
||||
layerIds, inLayersShapes, outLayersShapes); |
||||
} |
||||
|
||||
void Net::getLayerShapes(const MatShape& netInputShape, |
||||
const int layerId, |
||||
ShapesVec& inLayerShapes, |
||||
ShapesVec& outLayerShapes) const |
||||
{ |
||||
getLayerShapes(ShapesVec(1, netInputShape), |
||||
layerId, inLayerShapes, outLayerShapes); |
||||
} |
||||
|
||||
void Net::getLayerShapes(const ShapesVec& netInputShapes, |
||||
const int layerId, |
||||
ShapesVec& inLayerShapes, |
||||
ShapesVec& outLayerShapes) const |
||||
{ |
||||
CV_Assert(impl); |
||||
LayerShapes shapes; |
||||
impl->getLayerShapes(netInputShapes, layerId, shapes); |
||||
inLayerShapes = shapes.in; |
||||
outLayerShapes = shapes.out; |
||||
} |
||||
|
||||
int64 Net::getFLOPS(const std::vector<MatShape>& netInputShapes) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getFLOPS(netInputShapes); |
||||
} |
||||
|
||||
int64 Net::getFLOPS(const MatShape& netInputShape) const |
||||
{ |
||||
return getFLOPS(std::vector<MatShape>(1, netInputShape)); |
||||
} |
||||
|
||||
int64 Net::getFLOPS(const int layerId, |
||||
const std::vector<MatShape>& netInputShapes) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getFLOPS(layerId, netInputShapes); |
||||
} |
||||
|
||||
int64 Net::getFLOPS(const int layerId, |
||||
const MatShape& netInputShape) const |
||||
{ |
||||
return getFLOPS(layerId, std::vector<MatShape>(1, netInputShape)); |
||||
} |
||||
|
||||
void Net::getLayerTypes(std::vector<String>& layersTypes) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getLayerTypes(layersTypes); |
||||
} |
||||
|
||||
int Net::getLayersCount(const String& layerType) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getLayersCount(layerType); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const int layerId, |
||||
const std::vector<MatShape>& netInputShapes, |
||||
size_t& weights, size_t& blobs) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getMemoryConsumption(layerId, netInputShapes, weights, blobs); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const std::vector<MatShape>& netInputShapes, |
||||
size_t& weights, size_t& blobs) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getMemoryConsumption(netInputShapes, weights, blobs); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const int layerId, |
||||
const MatShape& netInputShape, |
||||
size_t& weights, size_t& blobs) const |
||||
{ |
||||
getMemoryConsumption(layerId, std::vector<MatShape>(1, netInputShape), |
||||
weights, blobs); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const MatShape& netInputShape, |
||||
size_t& weights, size_t& blobs) const |
||||
{ |
||||
getMemoryConsumption(std::vector<MatShape>(1, netInputShape), |
||||
weights, blobs); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const std::vector<MatShape>& netInputShapes, |
||||
std::vector<int>& layerIds, std::vector<size_t>& weights, |
||||
std::vector<size_t>& blobs) const |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getMemoryConsumption(netInputShapes, layerIds, weights, blobs); |
||||
} |
||||
|
||||
void Net::getMemoryConsumption(const MatShape& netInputShape, std::vector<int>& layerIds, |
||||
std::vector<size_t>& weights, std::vector<size_t>& blobs) const |
||||
{ |
||||
getMemoryConsumption(std::vector<MatShape>(1, netInputShape), layerIds, |
||||
weights, blobs); |
||||
} |
||||
|
||||
// FIXIT return old value or add get method
|
||||
void Net::enableFusion(bool fusion) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->enableFusion(fusion); |
||||
} |
||||
|
||||
void Net::setHalideScheduler(const String& scheduler) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_TRACE_ARG_VALUE(scheduler, "scheduler", scheduler.c_str()); |
||||
CV_Assert(impl); |
||||
return impl->setHalideScheduler(scheduler); |
||||
} |
||||
|
||||
int64 Net::getPerfProfile(std::vector<double>& timings) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(impl); |
||||
return impl->getPerfProfile(timings); |
||||
} |
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,261 @@ |
||||
// 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.
|
||||
|
||||
#ifndef __OPENCV_DNN_SRC_NET_IMPL_HPP__ |
||||
#define __OPENCV_DNN_SRC_NET_IMPL_HPP__ |
||||
|
||||
#include "op_halide.hpp" |
||||
#include "op_inf_engine.hpp" |
||||
#include "ie_ngraph.hpp" |
||||
#include "op_vkcom.hpp" |
||||
#include "op_cuda.hpp" |
||||
#include "op_webnn.hpp" |
||||
|
||||
#include <opencv2/dnn/shape_utils.hpp> |
||||
#include <opencv2/imgproc.hpp> |
||||
#include <opencv2/dnn/layer_reg.private.hpp> |
||||
|
||||
#include <opencv2/core/utils/fp_control_utils.hpp> |
||||
|
||||
#include <opencv2/core/utils/logger.hpp> |
||||
|
||||
#include "layer_internals.hpp" // LayerPin LayerData DataLayer |
||||
|
||||
#include "legacy_backend.hpp" // wrapMat BlobManager OpenCLBackendWrapper |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
using std::make_pair; |
||||
using std::string; |
||||
|
||||
// NB: Implementation is divided between of multiple .cpp files
|
||||
struct Net::Impl : public detail::NetImplBase |
||||
{ |
||||
typedef std::map<int, LayerShapes> LayersShapesMap; |
||||
typedef std::map<int, LayerData> MapIdToLayerData; |
||||
|
||||
Impl(); |
||||
|
||||
Ptr<DataLayer> netInputLayer; |
||||
std::vector<LayerPin> blobsToKeep; |
||||
MapIdToLayerData layers; |
||||
std::map<String, int> layerNameToId; |
||||
std::map<std::string, int> outputNameToId; // use registerOutput() to populate outputs
|
||||
BlobManager blobManager; |
||||
int preferableBackend; |
||||
int preferableTarget; |
||||
String halideConfigFile; |
||||
bool skipInfEngineInit; |
||||
bool hasDynamicShapes; |
||||
// Map host data to backend specific wrapper.
|
||||
std::map<void*, Ptr<BackendWrapper>> backendWrappers; |
||||
|
||||
int lastLayerId; |
||||
|
||||
bool netWasAllocated; |
||||
bool netWasQuantized; |
||||
bool fusion; |
||||
bool isAsync; |
||||
std::vector<int64> layersTimings; |
||||
|
||||
|
||||
bool empty() const; |
||||
void setPreferableBackend(int backendId); |
||||
void setPreferableTarget(int targetId); |
||||
|
||||
// FIXIT use inheritance
|
||||
Ptr<BackendWrapper> wrap(Mat& host); |
||||
|
||||
|
||||
void clear(); |
||||
|
||||
void setUpNet(const std::vector<LayerPin>& blobsToKeep_ = std::vector<LayerPin>()); |
||||
|
||||
|
||||
Ptr<Layer> getLayer(int layerId) const; |
||||
Ptr<Layer> getLayer(const LayerId& layerId) const; |
||||
|
||||
int getLayerId(const String& layerName) const; |
||||
|
||||
int getLayerId(int id) const; |
||||
|
||||
int getLayerId(DictValue& layerDesc) const; |
||||
|
||||
String getLayerName(int id) const; |
||||
|
||||
LayerData& getLayerData(int id) const; |
||||
|
||||
LayerData& getLayerData(const String& layerName) const; |
||||
|
||||
LayerData& getLayerData(const DictValue& layerDesc) const; |
||||
|
||||
static void addLayerInput(LayerData& ld, int inNum, LayerPin from); |
||||
|
||||
int resolvePinOutputName(LayerData& ld, const String& outName) const; |
||||
|
||||
LayerPin getPinByAlias(const String& layerName) const; |
||||
|
||||
std::vector<LayerPin> getLayerOutPins(const String& layerName) const; |
||||
|
||||
// FIXIT remove dtype
|
||||
int addLayer(const String& name, const String& type, const int& dtype, LayerParams& params); |
||||
|
||||
int addLayerToPrev(const String& name, const String& type, const int& dtype, LayerParams& params); |
||||
|
||||
|
||||
void connect(int outLayerId, int outNum, int inLayerId, int inNum); |
||||
|
||||
int registerOutput(const std::string& outputName, int layerId, int outputPort); |
||||
|
||||
// FIXIT drop "unconnected" API
|
||||
std::vector<int> getUnconnectedOutLayers() const; |
||||
std::vector<String> getUnconnectedOutLayersNames() /*const*/; |
||||
|
||||
|
||||
void setInputsNames(const std::vector<String>& inputBlobNames); |
||||
void setInputShape(const String& inputName, const MatShape& shape); |
||||
void setInput(InputArray blob, const String& name, double scalefactor, const Scalar& mean); |
||||
Mat getParam(int layer, int numParam) const; |
||||
void setParam(int layer, int numParam, const Mat& blob); |
||||
std::vector<Ptr<Layer>> getLayerInputs(int layerId) const; |
||||
std::vector<String> getLayerNames() const; |
||||
|
||||
|
||||
// TODO drop?
|
||||
void getLayerTypes(std::vector<String>& layersTypes) const; |
||||
int getLayersCount(const String& layerType) const; |
||||
|
||||
|
||||
// FIXIT use inheritance
|
||||
void initBackend(const std::vector<LayerPin>& blobsToKeep_); |
||||
|
||||
void setHalideScheduler(const String& scheduler); |
||||
#ifdef HAVE_HALIDE |
||||
void compileHalide(); |
||||
void initHalideBackend(); |
||||
#endif |
||||
|
||||
#ifdef HAVE_DNN_NGRAPH |
||||
void addNgraphOutputs(LayerData& ld); |
||||
void initNgraphBackend(const std::vector<LayerPin>& blobsToKeep_); |
||||
#endif |
||||
|
||||
#ifdef HAVE_WEBNN |
||||
void addWebnnOutputs(LayerData& ld); |
||||
void initWebnnBackend(const std::vector<LayerPin>& blobsToKeep_); |
||||
#endif |
||||
|
||||
#ifdef HAVE_VULKAN |
||||
void initVkComBackend(); |
||||
#endif |
||||
|
||||
#ifdef HAVE_CUDA |
||||
struct CudaInfo_t |
||||
{ |
||||
CudaInfo_t(cuda4dnn::csl::CSLContext ctxt, cuda4dnn::csl::Stream d2h_stream_) |
||||
: context(std::move(ctxt)) |
||||
, d2h_stream(std::move(d2h_stream_)) |
||||
{} |
||||
cuda4dnn::csl::CSLContext context; |
||||
cuda4dnn::csl::Stream d2h_stream; |
||||
cuda4dnn::csl::Workspace workspace; |
||||
}; |
||||
|
||||
std::unique_ptr<CudaInfo_t> cudaInfo; |
||||
|
||||
void initCUDABackend(const std::vector<LayerPin>& blobsToKeep_); |
||||
#endif |
||||
|
||||
void allocateLayer(int lid, const LayersShapesMap& layersShapes); |
||||
|
||||
// TODO add getter
|
||||
void enableFusion(bool fusion_); |
||||
|
||||
void fuseLayers(const std::vector<LayerPin>& blobsToKeep_); |
||||
|
||||
void allocateLayers(const std::vector<LayerPin>& blobsToKeep_); |
||||
|
||||
void forwardLayer(LayerData& ld); |
||||
|
||||
void forwardToLayer(LayerData& ld, bool clearFlags = true); |
||||
|
||||
Mat forward(const String& outputName); |
||||
AsyncArray forwardAsync(const String& outputName); |
||||
void forward(OutputArrayOfArrays outputBlobs, const String& outputName); |
||||
void forward(OutputArrayOfArrays outputBlobs, |
||||
const std::vector<String>& outBlobNames); |
||||
void forward(std::vector<std::vector<Mat>>& outputBlobs, |
||||
const std::vector<String>& outBlobNames); |
||||
|
||||
|
||||
void getLayerShapesRecursively(int id, LayersShapesMap& inOutShapes); |
||||
|
||||
void getLayersShapes( |
||||
const ShapesVec& netInputShapes, |
||||
std::vector<int>& layersIds, |
||||
std::vector<ShapesVec>& inLayersShapes, |
||||
std::vector<ShapesVec>& outLayersShapes) /*const*/; |
||||
|
||||
void getLayersShapes(const ShapesVec& netInputShapes, |
||||
LayersShapesMap& inOutShapes); |
||||
|
||||
void getLayerShapes(const ShapesVec& netInputShapes, |
||||
const int layerId, |
||||
LayerShapes& shapes); |
||||
|
||||
void updateLayersShapes(); |
||||
|
||||
int64 getFLOPS(const std::vector<MatShape>& netInputShapes) /*const*/; |
||||
int64 getFLOPS( |
||||
const int layerId, |
||||
const std::vector<MatShape>& netInputShapes) /*const*/; |
||||
|
||||
void getMemoryConsumption( |
||||
const int layerId, |
||||
const std::vector<MatShape>& netInputShapes, |
||||
size_t& weights, size_t& blobs) /*const*/; |
||||
void getMemoryConsumption( |
||||
const std::vector<MatShape>& netInputShapes, |
||||
size_t& weights, size_t& blobs) /*const*/; |
||||
void getMemoryConsumption( |
||||
const std::vector<MatShape>& netInputShapes, |
||||
std::vector<int>& layerIds, std::vector<size_t>& weights, |
||||
std::vector<size_t>& blobs) /*const*/; |
||||
int64 getPerfProfile(std::vector<double>& timings) const; |
||||
|
||||
// TODO drop
|
||||
LayerPin getLatestLayerPin(const std::vector<LayerPin>& pins) const; |
||||
|
||||
Mat getBlob(const LayerPin& pin) const; |
||||
|
||||
Mat getBlob(String outputName) const; |
||||
|
||||
#ifdef CV_CXX11 |
||||
AsyncArray getBlobAsync(const LayerPin& pin); |
||||
|
||||
AsyncArray getBlobAsync(String outputName); |
||||
#endif // CV_CXX11
|
||||
|
||||
#ifdef HAVE_INF_ENGINE |
||||
static |
||||
Net createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet); |
||||
#endif |
||||
|
||||
string dump(bool forceAllocation = false) const; |
||||
|
||||
void dumpNetworkToFile() const; |
||||
|
||||
// FIXIT drop from inference API
|
||||
Net quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype) /*const*/; |
||||
void getInputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/; |
||||
void getOutputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/; |
||||
|
||||
}; // Net::Impl
|
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
||||
#endif // __OPENCV_DNN_SRC_NET_IMPL_HPP__
|
@ -0,0 +1,200 @@ |
||||
// 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" |
||||
#include "legacy_backend.hpp" |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
Ptr<BackendWrapper> Net::Impl::wrap(Mat& host) |
||||
{ |
||||
if (preferableBackend == DNN_BACKEND_OPENCV && preferableTarget == DNN_TARGET_CPU) |
||||
return Ptr<BackendWrapper>(); |
||||
|
||||
MatShape shape(host.dims); |
||||
for (int i = 0; i < host.dims; ++i) |
||||
shape[i] = host.size[i]; |
||||
|
||||
void* data = host.data; |
||||
if (backendWrappers.find(data) != backendWrappers.end()) |
||||
{ |
||||
Ptr<BackendWrapper> baseBuffer = backendWrappers[data]; |
||||
if (preferableBackend == DNN_BACKEND_OPENCV) |
||||
{ |
||||
#ifdef HAVE_OPENCL |
||||
CV_Assert(IS_DNN_OPENCL_TARGET(preferableTarget)); |
||||
return OpenCLBackendWrapper::create(baseBuffer, host); |
||||
#else |
||||
CV_Error(Error::StsInternal, ""); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_HALIDE) |
||||
{ |
||||
CV_Assert(haveHalide()); |
||||
#ifdef HAVE_HALIDE |
||||
return Ptr<BackendWrapper>(new HalideBackendWrapper(baseBuffer, shape)); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019) |
||||
{ |
||||
CV_ERROR_DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019; |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
||||
{ |
||||
return wrapMat(preferableBackend, preferableTarget, host); |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_WEBNN) |
||||
{ |
||||
#ifdef HAVE_WEBNN |
||||
return wrapMat(preferableBackend, preferableTarget, host); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_VKCOM) |
||||
{ |
||||
#ifdef HAVE_VULKAN |
||||
return Ptr<BackendWrapper>(new VkComBackendWrapper(baseBuffer, host)); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_CUDA) |
||||
{ |
||||
CV_Assert(haveCUDA()); |
||||
#ifdef HAVE_CUDA |
||||
switch (preferableTarget) |
||||
{ |
||||
case DNN_TARGET_CUDA: |
||||
return CUDABackendWrapperFP32::create(baseBuffer, shape); |
||||
case DNN_TARGET_CUDA_FP16: |
||||
return CUDABackendWrapperFP16::create(baseBuffer, shape); |
||||
default: |
||||
CV_Assert(IS_DNN_CUDA_TARGET(preferableTarget)); |
||||
} |
||||
#endif |
||||
} |
||||
else |
||||
CV_Error(Error::StsNotImplemented, "Unknown backend identifier"); |
||||
} |
||||
|
||||
Ptr<BackendWrapper> wrapper = wrapMat(preferableBackend, preferableTarget, host); |
||||
backendWrappers[data] = wrapper; |
||||
return wrapper; |
||||
} |
||||
|
||||
|
||||
void Net::Impl::initBackend(const std::vector<LayerPin>& blobsToKeep_) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
if (preferableBackend == DNN_BACKEND_OPENCV) |
||||
{ |
||||
CV_Assert(preferableTarget == DNN_TARGET_CPU || IS_DNN_OPENCL_TARGET(preferableTarget)); |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_HALIDE) |
||||
{ |
||||
#ifdef HAVE_HALIDE |
||||
initHalideBackend(); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of Halide"); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
initNgraphBackend(blobsToKeep_); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of OpenVINO"); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_WEBNN) |
||||
{ |
||||
#ifdef HAVE_WEBNN |
||||
initWebnnBackend(blobsToKeep_); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of WebNN"); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_VKCOM) |
||||
{ |
||||
#ifdef HAVE_VULKAN |
||||
initVkComBackend(); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of Vulkan"); |
||||
#endif |
||||
} |
||||
else if (preferableBackend == DNN_BACKEND_CUDA) |
||||
{ |
||||
#ifdef HAVE_CUDA |
||||
initCUDABackend(blobsToKeep_); |
||||
#else |
||||
CV_Error(Error::StsNotImplemented, "This OpenCV version is built without support of CUDA/CUDNN"); |
||||
#endif |
||||
} |
||||
else |
||||
{ |
||||
CV_Error(Error::StsNotImplemented, cv::format("Unknown backend identifier: %d", preferableBackend)); |
||||
} |
||||
} |
||||
|
||||
|
||||
void Net::Impl::setPreferableBackend(int backendId) |
||||
{ |
||||
if (backendId == DNN_BACKEND_DEFAULT) |
||||
backendId = (Backend)getParam_DNN_BACKEND_DEFAULT(); |
||||
|
||||
if (netWasQuantized && backendId != DNN_BACKEND_OPENCV) |
||||
{ |
||||
CV_LOG_WARNING(NULL, "DNN: Only default backend supports quantized networks"); |
||||
backendId = DNN_BACKEND_OPENCV; |
||||
} |
||||
|
||||
#ifdef HAVE_INF_ENGINE |
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE) |
||||
backendId = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; |
||||
#endif |
||||
|
||||
if (preferableBackend != backendId) |
||||
{ |
||||
preferableBackend = backendId; |
||||
clear(); |
||||
} |
||||
} |
||||
|
||||
void Net::Impl::setPreferableTarget(int targetId) |
||||
{ |
||||
if (netWasQuantized && targetId != DNN_TARGET_CPU && |
||||
targetId != DNN_TARGET_OPENCL && targetId != DNN_TARGET_OPENCL_FP16) |
||||
{ |
||||
CV_LOG_WARNING(NULL, "DNN: Only CPU and OpenCL/OpenCL FP16 target is supported by quantized networks"); |
||||
targetId = DNN_TARGET_CPU; |
||||
} |
||||
|
||||
if (preferableTarget != targetId) |
||||
{ |
||||
preferableTarget = targetId; |
||||
if (IS_DNN_OPENCL_TARGET(targetId)) |
||||
{ |
||||
#ifndef HAVE_OPENCL |
||||
#ifdef HAVE_INF_ENGINE |
||||
if (preferableBackend == DNN_BACKEND_OPENCV) |
||||
#else |
||||
if (preferableBackend == DNN_BACKEND_DEFAULT || |
||||
preferableBackend == DNN_BACKEND_OPENCV) |
||||
#endif // HAVE_INF_ENGINE
|
||||
preferableTarget = DNN_TARGET_CPU; |
||||
#else |
||||
bool fp16 = ocl::Device::getDefault().isExtensionSupported("cl_khr_fp16"); |
||||
if (!fp16 && targetId == DNN_TARGET_OPENCL_FP16) |
||||
preferableTarget = DNN_TARGET_OPENCL; |
||||
#endif |
||||
} |
||||
clear(); |
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,607 @@ |
||||
// 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" |
||||
|
||||
#ifdef HAVE_CUDA |
||||
#include "cuda4dnn/primitives/eltwise.hpp" // required by fuseLayers |
||||
#endif |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
void Net::Impl::enableFusion(bool fusion_) |
||||
{ |
||||
if (fusion != fusion_) |
||||
{ |
||||
fusion = fusion_; |
||||
clear(); |
||||
} |
||||
} |
||||
|
||||
|
||||
#if 0 |
||||
#define printf_(args) printf args |
||||
#else |
||||
#define printf_(args) |
||||
#endif |
||||
|
||||
|
||||
void Net::Impl::fuseLayers(const std::vector<LayerPin>& blobsToKeep_) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
if(!fusion || (preferableBackend != DNN_BACKEND_OPENCV && |
||||
preferableBackend != DNN_BACKEND_CUDA && |
||||
preferableBackend != DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)) |
||||
return; |
||||
|
||||
#if 0 // FIXIT mode without fusion is broken due to unsupported layers and handling of "custom" nodes
|
||||
if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH) |
||||
return; |
||||
#endif |
||||
|
||||
// scan through all the layers. If there is convolution layer followed by the activation layer,
|
||||
// we try to embed this activation into the convolution and disable separate execution of the activation
|
||||
|
||||
// FIXIT replace by layersToKeep to avoid hacks like "LayerPin(lid, 0)"
|
||||
std::set<LayerPin> pinsToKeep(blobsToKeep_.begin(), |
||||
blobsToKeep_.end()); |
||||
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); it++) |
||||
{ |
||||
int lid = it->first; |
||||
LayerData& ld = layers[lid]; |
||||
if (ld.skip) |
||||
{ |
||||
printf_(("skipped %s: %s\n", ld.layerInstance->name.c_str(), ld.layerInstance->type.c_str())); |
||||
continue; |
||||
} |
||||
printf_(("analyzing %s: %s\n", ld.layerInstance->name.c_str(), ld.layerInstance->type.c_str())); |
||||
|
||||
// the optimization #1. try to fuse batch norm, scaling and/or activation layers
|
||||
// with the current layer if they follow it. Normally, the are fused with the convolution layer,
|
||||
// but some of them (like activation) may be fused with fully-connected, elemwise (+) and
|
||||
// some other layers.
|
||||
Ptr<Layer>& currLayer = ld.layerInstance; |
||||
if (ld.consumers.size() == 1 && pinsToKeep.count(LayerPin(lid, 0)) == 0) |
||||
{ |
||||
LayerData* nextData = &layers[ld.consumers[0].lid]; |
||||
LayerPin lpNext(ld.consumers[0].lid, 0); |
||||
while (nextData) |
||||
{ |
||||
#ifdef HAVE_INF_ENGINE |
||||
if (preferableBackend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH && pinsToKeep.count(lpNext) != 0) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: skip fusing with 'output' node: " << nextData->name << "@" << nextData->type); |
||||
break; |
||||
} |
||||
#endif |
||||
/* we use `tryFuse` member of convolution layer to fuse eltwise later
|
||||
* it's not intended to be fused here; hence, we stop when we encounter eltwise |
||||
*/ |
||||
if (preferableBackend == DNN_BACKEND_CUDA && ld.type == "Convolution" && nextData->type == "Eltwise") |
||||
break; |
||||
Ptr<Layer> nextLayer = nextData->layerInstance; |
||||
if (currLayer->tryFuse(nextLayer)) |
||||
{ |
||||
printf_(("\tfused with %s\n", nextLayer->name.c_str())); |
||||
nextData->skip = true; |
||||
ld.outputBlobs = layers[lpNext.lid].outputBlobs; |
||||
ld.outputBlobsWrappers = layers[lpNext.lid].outputBlobsWrappers; |
||||
if (nextData->consumers.size() == 1) |
||||
{ |
||||
int nextLayerId = nextData->consumers[0].lid; |
||||
nextData = &layers[nextLayerId]; |
||||
lpNext = LayerPin(nextLayerId, 0); |
||||
} |
||||
else |
||||
{ |
||||
nextData = 0; |
||||
break; |
||||
} |
||||
} |
||||
else |
||||
break; |
||||
} |
||||
|
||||
if (preferableBackend != DNN_BACKEND_OPENCV && preferableBackend != DNN_BACKEND_CUDA) |
||||
continue; // Go to the next layer.
|
||||
|
||||
// TODO: OpenCL target support more fusion styles.
|
||||
if ( preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget) && |
||||
(!cv::ocl::useOpenCL() || (ld.layerInstance->type != "Convolution" && |
||||
ld.layerInstance->type != "MVN" && ld.layerInstance->type != "Pooling" && |
||||
ld.layerInstance->type != "Concat")) ) |
||||
continue; |
||||
|
||||
if (preferableBackend == DNN_BACKEND_CUDA && IS_DNN_CUDA_TARGET(preferableTarget) |
||||
&& ld.layerInstance->type != "Convolution" |
||||
&& ld.layerInstance->type != "Concat") |
||||
continue; |
||||
|
||||
while (nextData) |
||||
{ |
||||
// For now, OpenCL target support fusion with activation of ReLU/ChannelsPReLU/Power/Tanh
|
||||
if (IS_DNN_OPENCL_TARGET(preferableTarget) && |
||||
nextData->type != "ReLU" && |
||||
nextData->type != "ChannelsPReLU" && |
||||
nextData->type != "ReLU6" && |
||||
nextData->type != "TanH" && |
||||
nextData->type != "Power") |
||||
break; |
||||
|
||||
Ptr<ActivationLayer> nextActivLayer = nextData->layerInstance.dynamicCast<ActivationLayer>(); |
||||
if (nextActivLayer.empty()) |
||||
break; |
||||
|
||||
if (currLayer->setActivation(nextActivLayer)) |
||||
{ |
||||
printf_(("\tfused with %s\n", nextActivLayer->name.c_str())); |
||||
nextData->skip = true; |
||||
ld.outputBlobs = layers[lpNext.lid].outputBlobs; |
||||
ld.outputBlobsWrappers = layers[lpNext.lid].outputBlobsWrappers; |
||||
if (nextData->consumers.size() == 1) |
||||
{ |
||||
int nextLayerId = nextData->consumers[0].lid; |
||||
nextData = &layers[nextLayerId]; |
||||
lpNext = LayerPin(nextLayerId, 0); |
||||
} |
||||
else |
||||
{ |
||||
nextData = 0; |
||||
break; |
||||
} |
||||
} |
||||
else |
||||
break; |
||||
} |
||||
|
||||
// OpenCL: fuse convolution layer followed by eltwise + relu
|
||||
// CUDA: fuse convolution layer followed by eltwise (and optional activation)
|
||||
while (nextData && |
||||
(IS_DNN_OPENCL_TARGET(preferableTarget) || IS_DNN_CUDA_TARGET(preferableTarget)) && |
||||
ld.layerInstance->type == "Convolution" |
||||
) // semantic of 'if'
|
||||
{ |
||||
Ptr<EltwiseLayer> nextEltwiseLayer = nextData->layerInstance.dynamicCast<EltwiseLayer>(); |
||||
if (nextEltwiseLayer.empty()) |
||||
break; |
||||
|
||||
#ifdef HAVE_CUDA |
||||
// CUDA backend supports fusion with eltwise sum (without variable channels)
|
||||
if (IS_DNN_CUDA_TARGET(preferableTarget) && !nextEltwiseLayer.empty()) |
||||
{ |
||||
// we create a temporary backend node for eltwise layer to obtain the eltwise configuration
|
||||
cuda4dnn::csl::CSLContext context; // assume that initCUDA and EltwiseOp do not use the context during init
|
||||
const auto node = nextData->layerInstance->initCUDA(&context, nextData->inputBlobsWrappers, nextData->outputBlobsWrappers); |
||||
auto eltwiseNode = node.dynamicCast<cuda4dnn::EltwiseOpBase>(); |
||||
|
||||
// broadcasting not supported in fused ops
|
||||
auto required_shape = shape(nextData->outputBlobs[0]); |
||||
for (int i = 0; i < nextData->inputBlobs.size(); i++) |
||||
{ |
||||
if (shape(*nextData->inputBlobs[i]) != required_shape) |
||||
{ |
||||
eltwiseNode.reset(); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// CUDA backend uses EltwiseOp when all operands have the same number of channels; otherwise, ShortcutOp is used.
|
||||
// Hence, a successful cast to EltwiseOp implies that the number of channels is same in all operand tensors.
|
||||
if (eltwiseNode.empty() || eltwiseNode->op != cuda4dnn::EltwiseOpType::SUM || !eltwiseNode->coeffs.empty()) |
||||
break; |
||||
} |
||||
#endif |
||||
|
||||
if (IS_DNN_OPENCL_TARGET(preferableTarget) && pinsToKeep.count(lpNext) != 0) |
||||
break; |
||||
if (nextData->inputBlobsId.size() != 2) |
||||
break; |
||||
|
||||
if (IS_DNN_OPENCL_TARGET(preferableTarget)) |
||||
{ |
||||
if (!nextData->params.has("operation") || toLowerCase(nextData->params.get<String>("operation")) == "sum") |
||||
{ |
||||
if (nextData->params.has("coeff")) |
||||
{ |
||||
DictValue paramCoeff = nextData->params.get("coeff"); |
||||
int n = paramCoeff.size(); |
||||
bool isCoeffOneOne = (n == 2); |
||||
for (int i = 0; isCoeffOneOne && i < n; i++) |
||||
{ |
||||
float c = paramCoeff.get<float>(i); |
||||
isCoeffOneOne &= (c == 1.0f); |
||||
} |
||||
if (!isCoeffOneOne) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/OpenCL: fusion of 'Sum' without coeffs (or {1.0, 1.0}) is supported only"); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/OpenCL: fusion with eltwise operation is not supported: " << nextData->params.get<String>("operation")); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
{ |
||||
LayerData *eltwiseData = nextData; |
||||
|
||||
// Eltwise layer has two inputs. We need to determine which
|
||||
// is a base convolution layer and which could be used as it's bias.
|
||||
LayerData* biasLayerData = 0; |
||||
for (int i = 0; i < 2; ++i) |
||||
{ |
||||
LayerData *downLayerData = &layers[eltwiseData->inputBlobsId[i].lid]; |
||||
CV_Assert(downLayerData); |
||||
while (downLayerData->skip) |
||||
{ |
||||
if (downLayerData->inputBlobsId.size() == 1) |
||||
downLayerData = &layers[downLayerData->inputBlobsId[0].lid]; |
||||
else |
||||
{ |
||||
downLayerData = 0; |
||||
break; |
||||
} |
||||
} |
||||
if (downLayerData && ld.id == downLayerData->id) |
||||
{ |
||||
biasLayerData = &layers[eltwiseData->inputBlobsId[1 - i].lid]; |
||||
break; |
||||
} |
||||
} |
||||
CV_Assert(biasLayerData); |
||||
{ |
||||
// fuse eltwise + activation layer
|
||||
// bias must already be computed to fuse => bias layer must appear before convolution
|
||||
if (biasLayerData->id < ld.id) |
||||
{ |
||||
/* we can fuse activation if:
|
||||
* => activation layer that follows is the only consumer of eltwise output |
||||
* => activation layer does not process multiple inputs |
||||
* => we do not require to keep the output of eltwise |
||||
*/ |
||||
Ptr<ActivationLayer> nextFusabeleActivLayer; |
||||
if (eltwiseData->consumers.size() == 1 && pinsToKeep.count(lpNext) == 0) |
||||
{ |
||||
nextData = &layers[eltwiseData->consumers[0].lid]; |
||||
lpNext = LayerPin(eltwiseData->consumers[0].lid, 0); |
||||
CV_Assert(nextData); |
||||
if (nextData->outputBlobs.size() == 1) |
||||
nextFusabeleActivLayer = nextData->layerInstance.dynamicCast<ActivationLayer>(); |
||||
} |
||||
else |
||||
{ |
||||
// OCL backend cannot fuse in this case but the CUDA backend can continue with just eltwise
|
||||
nextData = 0; |
||||
} |
||||
|
||||
// the requirements of OCV OpenCL backend and CUDA backend are different
|
||||
// we need to check them separately; hence, the fuse variables
|
||||
bool fuse_eltwise = false, fuse_activation = false; |
||||
|
||||
Ptr<PowerLayer> activ_power; |
||||
if (IS_DNN_OPENCL_TARGET(preferableTarget) && !nextFusabeleActivLayer.empty() && |
||||
nextData && |
||||
(!nextData->type.compare("ReLU") || |
||||
!nextData->type.compare("ChannelsPReLU") || |
||||
(!nextData->type.compare("Power") && (activ_power = nextFusabeleActivLayer.dynamicCast<PowerLayer>()) && activ_power->scale == 1.0f) |
||||
) && |
||||
currLayer->setActivation(nextFusabeleActivLayer)) |
||||
{ |
||||
fuse_eltwise = true; |
||||
fuse_activation = true; |
||||
} |
||||
|
||||
if (IS_DNN_CUDA_TARGET(preferableTarget)) |
||||
{ |
||||
/* supported fusion options:
|
||||
* => convolution + eltwise |
||||
* => activation(convolution) + eltwise |
||||
* > convolution + activation would have been fused already; we have to fuse eltwise |
||||
* => activation(convolution + eltwise) |
||||
* > fuse eltwise and then activation |
||||
*/ |
||||
auto layer = nextEltwiseLayer.staticCast<Layer>(); |
||||
if (currLayer->tryFuse(layer)) |
||||
{ |
||||
fuse_eltwise = true; /* eltwise was successfully fused */ |
||||
if (!nextFusabeleActivLayer.empty() && nextData) |
||||
{ |
||||
if ((!nextData->type.compare("ReLU") || |
||||
!nextData->type.compare("ReLU6") || |
||||
!nextData->type.compare("Power") || |
||||
!nextData->type.compare("TanH") || |
||||
!nextData->type.compare("Sigmoid") || |
||||
!nextData->type.compare("Swish") || |
||||
!nextData->type.compare("Mish")) && |
||||
currLayer->setActivation(nextFusabeleActivLayer)) |
||||
{ |
||||
// activation was fused
|
||||
fuse_activation = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
CV_Assert(!fuse_activation || fuse_eltwise); /* cannot fuse activation without eltwise */ |
||||
if(fuse_eltwise && fuse_activation) |
||||
{ |
||||
CV_Assert(nextData); |
||||
CV_Assert_N(biasLayerData->outputBlobsWrappers.size() == 1, ld.inputBlobsWrappers.size() == 1); |
||||
ld.inputBlobsWrappers.push_back(biasLayerData->outputBlobsWrappers[0]); |
||||
printf_(("\tfused with %s\n", nextEltwiseLayer->name.c_str())); |
||||
printf_(("\tfused with %s\n", nextFusabeleActivLayer->name.c_str())); |
||||
eltwiseData->skip = true; |
||||
nextData->skip = true; |
||||
// This optimization for cases like
|
||||
// some_layer conv
|
||||
// | |
|
||||
// +-- eltwise --+
|
||||
// |
|
||||
// activ
|
||||
// This way all the element-wise computations
|
||||
// (i.e. some_layer+conv or some_layer*conv)
|
||||
// would be done at [conv] layer. So we need to
|
||||
// replace [conv]'s output blob to [eltwise]'s one
|
||||
// considering that [activ] is an in-place layer.
|
||||
// Also we need to move all the consumers' references.
|
||||
// To prevent memory collisions (i.e. when input of
|
||||
// [conv] and output of [eltwise] is the same blob)
|
||||
// we allocate a new blob.
|
||||
CV_Assert_N(ld.outputBlobs.size() == 1, ld.outputBlobsWrappers.size() == 1); |
||||
ld.outputBlobs[0] = ld.outputBlobs[0].clone(); |
||||
ld.outputBlobsWrappers[0] = wrap(ld.outputBlobs[0]); |
||||
|
||||
eltwiseData->outputBlobs = ld.outputBlobs; |
||||
nextData->outputBlobs = ld.outputBlobs; |
||||
eltwiseData->outputBlobsWrappers = ld.outputBlobsWrappers; |
||||
nextData->outputBlobsWrappers = ld.outputBlobsWrappers; |
||||
|
||||
// Move references of [activ] layer consumers to the newly allocated blob.
|
||||
for (int i = 0; i < nextData->consumers.size(); ++i) |
||||
{ |
||||
LayerData& consumer = layers[nextData->consumers[i].lid]; |
||||
for (int j = 0; j < consumer.inputBlobsId.size(); ++j) |
||||
{ |
||||
if (consumer.inputBlobsId[j].lid == lpNext.lid) |
||||
{ |
||||
consumer.inputBlobs[j] = &ld.outputBlobs[0]; |
||||
consumer.inputBlobsWrappers[j] = ld.outputBlobsWrappers[0]; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
else if (fuse_eltwise) // conv + eltwise (note: conv could have fused activations before eltwise)
|
||||
{ |
||||
CV_Assert(IS_DNN_CUDA_TARGET(preferableTarget)); |
||||
CV_Assert_N(biasLayerData->outputBlobsWrappers.size() == 1, ld.inputBlobsWrappers.size() == 1); |
||||
ld.inputBlobsWrappers.push_back(biasLayerData->outputBlobsWrappers[0]); |
||||
printf_(("\tfused with %s\n", nextEltwiseLayer->name.c_str())); |
||||
eltwiseData->skip = true; |
||||
// This optimization is for cases like
|
||||
// some_layer conv (maybe fused with activ)
|
||||
// | |
|
||||
// +-- eltwise --+
|
||||
//
|
||||
// This way all the element-wise computations
|
||||
// (i.e. some_layer+conv or some_layer*conv)
|
||||
// would be done at [conv] layer. So we need to
|
||||
// replace [conv]'s output blob to [eltwise]'s one.
|
||||
// Also we need to move all the consumers' references.
|
||||
// To prevent memory collisions (i.e. when input of
|
||||
// [conv] and output of [eltwise] is the same blob)
|
||||
// we allocate a new blob.
|
||||
CV_Assert_N(ld.outputBlobs.size() == 1, ld.outputBlobsWrappers.size() == 1); |
||||
ld.outputBlobs[0] = ld.outputBlobs[0].clone(); |
||||
ld.outputBlobsWrappers[0] = wrap(ld.outputBlobs[0]); |
||||
|
||||
eltwiseData->outputBlobs = ld.outputBlobs; |
||||
eltwiseData->outputBlobsWrappers = ld.outputBlobsWrappers; |
||||
|
||||
// Move references of [eltwise] layer consumers to the newly allocated blob.
|
||||
for (int i = 0; i < eltwiseData->consumers.size(); ++i) |
||||
{ |
||||
LayerData& consumer = layers[eltwiseData->consumers[i].lid]; |
||||
for (int j = 0; j < consumer.inputBlobsId.size(); ++j) |
||||
{ |
||||
if (consumer.inputBlobsId[j].lid == eltwiseData->id) |
||||
{ |
||||
consumer.inputBlobs[j] = &ld.outputBlobs[0]; |
||||
consumer.inputBlobsWrappers[j] = ld.outputBlobsWrappers[0]; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (preferableBackend != DNN_BACKEND_OPENCV && preferableBackend != DNN_BACKEND_CUDA) |
||||
continue; // Go to the next layer.
|
||||
|
||||
// the optimization #2. if there is concat layer that concatenates channels
|
||||
// from the inputs together (i.e. axis == 1) then we make the inputs of
|
||||
// the concat layer to write to the concatenation output buffer
|
||||
// (and so we eliminate the concatenation layer, because the channels
|
||||
// are concatenated implicitly).
|
||||
Ptr<ConcatLayer> concatLayer = ld.layerInstance.dynamicCast<ConcatLayer>(); |
||||
if( !concatLayer.empty() && !concatLayer->padding && ld.outputBlobs.size() == 1 ) |
||||
{ |
||||
Mat& output = ld.outputBlobs[0]; |
||||
UMat umat_output; |
||||
#ifdef HAVE_OPENCL |
||||
if (!ld.outputBlobsWrappers.empty() && |
||||
(preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget))) |
||||
{ |
||||
size_t i, ninputs = ld.inputBlobsId.size(); |
||||
bool conv_layer = true; |
||||
for( i = 0; i < ninputs; i++ ) |
||||
{ |
||||
LayerPin pin = ld.inputBlobsId[i]; |
||||
LayerData* inp_i_data = &layers[pin.lid]; |
||||
while(inp_i_data->skip && |
||||
inp_i_data->inputBlobsId.size() == 1 && |
||||
inp_i_data->consumers.size() == 1) |
||||
{ |
||||
pin = inp_i_data->inputBlobsId[0]; |
||||
inp_i_data = &layers[pin.lid]; |
||||
} |
||||
conv_layer = conv_layer && (inp_i_data->getLayerInstance()->type == "Convolution"); |
||||
} |
||||
if (!conv_layer) |
||||
continue; |
||||
std::vector<UMat> umat_outputBlobs; |
||||
umat_outputBlobs = OpenCLBackendWrapper::getUMatVector(ld.outputBlobsWrappers); |
||||
umat_output = umat_outputBlobs[0]; |
||||
} |
||||
#endif |
||||
|
||||
// TODO: in general, this optimization can always be done, but
|
||||
// many layers currently check that the input/output blobs are
|
||||
// continuous arrays. Unfortunately, this is not true when
|
||||
// the concatenation optimization is applied with batch_size > 1.
|
||||
// so, for now, we only apply this optimization in the most popular
|
||||
// case batch_size == 1.
|
||||
int axis = normalize_axis(concatLayer->axis, output.dims); |
||||
if( output.total(0, axis) == 1 ) |
||||
{ |
||||
size_t i, ninputs = ld.inputBlobsId.size(); |
||||
std::vector<LayerPin> realinputs(ninputs); |
||||
for( i = 0; i < ninputs; i++ ) |
||||
{ |
||||
LayerPin pin = ld.inputBlobsId[i]; |
||||
LayerData* inp_i_data = &layers[pin.lid]; |
||||
while(inp_i_data->skip && |
||||
inp_i_data->inputBlobsId.size() == 1 && |
||||
inp_i_data->consumers.size() == 1) |
||||
{ |
||||
pin = inp_i_data->inputBlobsId[0]; |
||||
inp_i_data = &layers[pin.lid]; |
||||
} |
||||
printf_(("\treal input for %s is %s\n", |
||||
layers[ld.inputBlobsId[i].lid].getLayerInstance()->name.c_str(), |
||||
inp_i_data->getLayerInstance()->name.c_str())); |
||||
|
||||
if(inp_i_data->skip || inp_i_data->consumers.size() != 1) |
||||
break; |
||||
#ifdef HAVE_CUDA |
||||
if (preferableBackend == DNN_BACKEND_CUDA && |
||||
(inp_i_data->layerInstance->supportBackend(DNN_BACKEND_CUDA) == false || |
||||
(inp_i_data->layerInstance->type != "Convolution" && |
||||
inp_i_data->layerInstance->type != "Pooling" && |
||||
inp_i_data->layerInstance->type != "Resize" && |
||||
inp_i_data->layerInstance->type != "Flatten" && |
||||
inp_i_data->layerInstance->type != "Permute" && |
||||
inp_i_data->layerInstance->type != "Reorg" && |
||||
inp_i_data->layerInstance->type != "Eltwise" && |
||||
inp_i_data->layerInstance.dynamicCast<ActivationLayer>().empty()))) |
||||
{ |
||||
break; |
||||
} |
||||
#endif |
||||
realinputs[i] = pin; |
||||
} |
||||
|
||||
if( i >= ninputs ) |
||||
{ |
||||
// Allocate new memory to prevent collisions during memory
|
||||
// reusing (see https://github.com/opencv/opencv/pull/10456).
|
||||
output = output.clone(); |
||||
#ifdef HAVE_OPENCL |
||||
if (preferableBackend == DNN_BACKEND_OPENCV && |
||||
IS_DNN_OPENCL_TARGET(preferableTarget)) |
||||
{ |
||||
std::vector<UMat> umats(1); |
||||
umat_output = umat_output.clone(); |
||||
umats[0] = umat_output; |
||||
OpenCLBackendWrapper::update(ld.outputBlobsWrappers, umats); |
||||
} |
||||
#endif |
||||
|
||||
#ifdef HAVE_CUDA |
||||
if (preferableBackend == DNN_BACKEND_CUDA) |
||||
ld.outputBlobsWrappers[0] = wrap(output); |
||||
#endif |
||||
std::vector<Range> chrange(output.dims, Range::all()); |
||||
int ofs = 0; |
||||
for( i = 0; i < ninputs; i++ ) |
||||
{ |
||||
LayerPin pin = realinputs[i]; |
||||
LayerData* inp_i_data = &layers[pin.lid]; |
||||
int channels_i = ld.inputBlobs[i]->size[axis]; |
||||
chrange[axis] = Range(ofs, ofs + channels_i); |
||||
printf_(("\toutput %s(%d) to channels (%d, %d)\n", inp_i_data->layerInstance->name.c_str(), |
||||
pin.oid, ofs, ofs + channels_i)); |
||||
ofs += channels_i; |
||||
Mat output_slice = output(chrange); |
||||
Mat& curr_output = inp_i_data->outputBlobs[pin.oid]; |
||||
CV_Assert(output_slice.isContinuous() && output_slice.size == curr_output.size); |
||||
Mat* oldPtr = &curr_output; |
||||
curr_output = output_slice; |
||||
#ifdef HAVE_OPENCL |
||||
if (preferableBackend == DNN_BACKEND_OPENCV && IS_DNN_OPENCL_TARGET(preferableTarget)) |
||||
{ |
||||
std::vector<UMat> umats(inp_i_data->outputBlobsWrappers.size()); |
||||
umats[pin.oid] = umat_output(chrange); |
||||
OpenCLBackendWrapper::update(inp_i_data->outputBlobsWrappers, umats); |
||||
} |
||||
#endif |
||||
#ifdef HAVE_CUDA |
||||
if (preferableBackend == DNN_BACKEND_CUDA) |
||||
{ |
||||
auto cuda_wrapper = wrap(output).dynamicCast<CUDABackendWrapper>(); |
||||
auto offset = chrange[axis].start * output_slice.total(axis + 1, output.dims); |
||||
auto new_shape = shape(output_slice); |
||||
cuda_wrapper->update(new_shape, offset); |
||||
inp_i_data->outputBlobsWrappers[pin.oid] = cuda_wrapper.staticCast<BackendWrapper>(); |
||||
} |
||||
#endif |
||||
// Layers that refer old input Mat will refer to the
|
||||
// new data but the same Mat object.
|
||||
CV_Assert_N(curr_output.data == output_slice.data, oldPtr == &curr_output); |
||||
} |
||||
|
||||
#ifdef HAVE_CUDA |
||||
if (preferableBackend == DNN_BACKEND_CUDA) |
||||
{ |
||||
for (int i = 0; i < ld.consumers.size(); i++) |
||||
{ |
||||
LayerData& consumer = layers[ld.consumers[i].lid]; |
||||
for (int j = 0; j < consumer.inputBlobsId.size(); j++) |
||||
{ |
||||
if (consumer.inputBlobsId[j].lid == ld.id) |
||||
{ |
||||
CV_Assert(consumer.inputBlobs[j]->data == ld.outputBlobs[0].data); |
||||
consumer.inputBlobsWrappers[j] = ld.outputBlobsWrappers[0]; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endif |
||||
ld.skip = true; |
||||
printf_(("\toptimized out Concat layer %s\n", concatLayer->name.c_str())); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,568 @@ |
||||
// 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 <opencv2/core/utils/fp_control_utils.hpp> |
||||
|
||||
#include <opencv2/core/utils/configuration.private.hpp> |
||||
#include <opencv2/core/utils/logger.hpp> |
||||
|
||||
#include "net_impl.hpp" |
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
#ifdef HAVE_INF_ENGINE |
||||
|
||||
|
||||
/** mark input pins as outputs from other subnetworks
|
||||
* FIXIT must be done by DNN engine not ngraph. |
||||
*/ |
||||
void Net::Impl::addNgraphOutputs(LayerData& ld) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
CV_LOG_DEBUG(NULL, "DNN/IE: layer of new subnet: " << ld.name << "@" << ld.type); |
||||
|
||||
Ptr<InfEngineNgraphNet> layerNet; |
||||
auto it = ld.backendNodes.find(preferableBackend); |
||||
if (it != ld.backendNodes.end()) |
||||
{ |
||||
Ptr<BackendNode> node = it->second; |
||||
if (!node.empty()) |
||||
{ |
||||
Ptr<InfEngineNgraphNode> ieNode = node.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieNode.empty()); |
||||
CV_Assert(!ieNode->net.empty()); |
||||
layerNet = ieNode->net; |
||||
} |
||||
} |
||||
|
||||
for (int i = 0; i < ld.inputBlobsId.size(); ++i) |
||||
{ |
||||
LayerData& inpLd = layers[ld.inputBlobsId[i].lid]; |
||||
Ptr<BackendNode> inpNode = inpLd.backendNodes[preferableBackend]; |
||||
if (!inpNode.empty()) |
||||
{ |
||||
Ptr<InfEngineNgraphNode> ieInpNode = inpNode.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieInpNode.empty()); |
||||
CV_Assert(!ieInpNode->net.empty()); |
||||
if (layerNet != ieInpNode->net) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: pin output between subnets: " << ieInpNode->node->get_friendly_name()); |
||||
ieInpNode->net->addOutput(ieInpNode); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Net::Impl::initNgraphBackend(const std::vector<LayerPin>& blobsToKeep_) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_CheckEQ(preferableBackend, DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, ""); |
||||
|
||||
Ptr<InfEngineNgraphNet> net; |
||||
|
||||
for (MapIdToLayerData::const_iterator it = layers.begin(); it != layers.end(); ++it) |
||||
{ |
||||
const LayerData& ld = it->second; |
||||
if (ld.id == 0) |
||||
{ |
||||
CV_Assert((netInputLayer->outNames.empty() && ld.outputBlobsWrappers.size() == 1) || |
||||
(netInputLayer->outNames.size() == ld.outputBlobsWrappers.size())); |
||||
for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) |
||||
{ |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); |
||||
std::string outputName = netInputLayer->outNames.empty() ? ld.name : netInputLayer->outNames[i]; |
||||
outputName = ld.outputBlobsWrappers.size() > 1 ? (outputName + "." + std::to_string(i)) : outputName; |
||||
dataPtr->setName(outputName); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) |
||||
{ |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); |
||||
std::string outputName = ld.outputBlobsWrappers.size() > 1 ? (ld.name + "." + std::to_string(i)) : ld.name; |
||||
dataPtr->setName(outputName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (skipInfEngineInit) |
||||
{ |
||||
Ptr<BackendNode> node = layers[lastLayerId].backendNodes[preferableBackend]; |
||||
CV_Assert(!node.empty()); |
||||
|
||||
Ptr<InfEngineNgraphNode> ieNode = node.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieNode.empty()); |
||||
|
||||
CV_Assert(ieNode->net); |
||||
InfEngineNgraphNet& ienet = *ieNode->net; |
||||
ienet.reset(); |
||||
|
||||
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); ++it) |
||||
{ |
||||
LayerData& ld = it->second; |
||||
if (ld.id == 0) |
||||
{ |
||||
for (int i = 0; i < ld.inputBlobsWrappers.size(); ++i) |
||||
{ |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.inputBlobsWrappers[i]); |
||||
dataPtr->setName(netInputLayer->outNames[i]); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) |
||||
{ |
||||
auto it = ienet.outputsDesc.find(ld.name); |
||||
if (it != ienet.outputsDesc.end()) |
||||
{ |
||||
const InferenceEngine::TensorDesc& descriptor = it->second; |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataOutputNode(ld.outputBlobsWrappers[i], descriptor, ld.name); |
||||
dataPtr->setName(ld.name); |
||||
} |
||||
else |
||||
{ |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); |
||||
dataPtr->setName(ld.name); |
||||
} |
||||
} |
||||
} |
||||
ienet.addBlobs(ld.inputBlobsWrappers); |
||||
ienet.addBlobs(ld.outputBlobsWrappers); |
||||
ld.skip = true; |
||||
} |
||||
layers[lastLayerId].skip = false; |
||||
ienet.init((Target)preferableTarget); |
||||
return; |
||||
} |
||||
|
||||
bool supportsCPUFallback = !isArmComputePlugin() && (preferableTarget == DNN_TARGET_CPU || |
||||
openvino::checkTarget(DNN_TARGET_CPU)); |
||||
|
||||
// Build Inference Engine networks from sets of layers that support this
|
||||
// backend. Split a whole model on several Inference Engine networks if
|
||||
// some of layers are not implemented.
|
||||
for (MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); ++it) |
||||
{ |
||||
LayerData& ld = it->second; |
||||
|
||||
CV_LOG_DEBUG(NULL, "DNN/IE: processing layer " << ld.name << "@" << ld.type << " (" << ld.id << ") ..."); |
||||
|
||||
if (ld.id == 0 && ld.skip) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: SKIP!"); |
||||
continue; |
||||
} |
||||
|
||||
bool fused = ld.skip; |
||||
Ptr<Layer> layer = ld.layerInstance; |
||||
if (!fused && !layer->supportBackend(preferableBackend)) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: NOT supported!"); |
||||
bool customizable = ld.id != 0 && supportsCPUFallback; |
||||
|
||||
// TODO: there is a bug in Myriad plugin with custom layers shape infer.
|
||||
if (preferableTarget == DNN_TARGET_MYRIAD || preferableTarget == DNN_TARGET_HDDL) |
||||
{ |
||||
for (int i = 0; customizable && i < ld.inputBlobs.size(); ++i) |
||||
{ |
||||
customizable = ld.inputBlobs[i]->size[0] == 1; |
||||
} |
||||
} |
||||
|
||||
// TODO: fix these workarounds
|
||||
if (preferableTarget == DNN_TARGET_MYRIAD || |
||||
preferableTarget == DNN_TARGET_HDDL || |
||||
preferableTarget == DNN_TARGET_OPENCL || |
||||
preferableTarget == DNN_TARGET_OPENCL_FP16) |
||||
customizable &= ld.type != "Concat"; |
||||
|
||||
if (preferableTarget == DNN_TARGET_OPENCL || |
||||
preferableTarget == DNN_TARGET_OPENCL_FP16) |
||||
customizable &= ld.type != "Power"; |
||||
|
||||
if (preferableTarget == DNN_TARGET_OPENCL) |
||||
customizable &= ld.type != "Eltwise"; |
||||
|
||||
if (!customizable) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: NOT customizable!"); |
||||
addNgraphOutputs(ld); |
||||
net = Ptr<InfEngineNgraphNet>(); |
||||
layer->preferableTarget = DNN_TARGET_CPU; |
||||
|
||||
for (int i = 0; i < ld.inputBlobsId.size(); ++i) |
||||
{ |
||||
LayerData& inpLd = layers[ld.inputBlobsId[i].lid]; |
||||
Ptr<BackendNode> inpNode = inpLd.backendNodes[preferableBackend]; |
||||
if (!inpNode.empty()) |
||||
{ |
||||
Ptr<InfEngineNgraphNode> ieNode = inpNode.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieNode.empty()); |
||||
ieNode->net->addOutput(ieNode); |
||||
} |
||||
} |
||||
continue; |
||||
} |
||||
} |
||||
ld.skip = true; // Initially skip all Inference Engine supported layers.
|
||||
|
||||
// Create a new network if one of inputs from different Inference Engine graph.
|
||||
std::vector<Ptr<BackendNode>> inputNodes; |
||||
for (int i = 0; i < ld.inputBlobsId.size(); ++i) |
||||
{ |
||||
// Layer_Test_ROIPooling.Accuracy has 2 inputs inpLD = 0, 0 -> has 4 inputNodes (input, rois, input, rois)
|
||||
if (inputNodes.size() == ld.inputBlobsId.size()) |
||||
{ |
||||
break; |
||||
} |
||||
LayerData& inpLd = layers[ld.inputBlobsId[i].lid]; |
||||
Ptr<BackendNode> inpNode = inpLd.backendNodes[preferableBackend]; |
||||
if (!inpNode.empty()) |
||||
{ |
||||
Ptr<InfEngineNgraphNode> ieInpNode = inpNode.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieInpNode.empty()); |
||||
CV_Assert(!ieInpNode->net.empty()); |
||||
if (ieInpNode->net == net && !fused) |
||||
{ |
||||
inputNodes.push_back(inpNode); |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
if (net.empty()) |
||||
{ |
||||
net = Ptr<InfEngineNgraphNet>(new InfEngineNgraphNet(*this)); |
||||
} |
||||
|
||||
if (!fused) |
||||
{ |
||||
std::vector<std::string> inputNames; |
||||
std::vector<cv::Mat> inputs; |
||||
|
||||
auto curr_pos = inpLd.consumers.begin(); |
||||
auto compare = [&ld](const LayerPin& lp) { return lp.lid == ld.id; }; |
||||
auto cons = curr_pos; |
||||
while ((cons = std::find_if(curr_pos, inpLd.consumers.end(), compare)) != |
||||
inpLd.consumers.end()) { |
||||
int cons_inp = cons->oid; |
||||
Ptr<NgraphBackendWrapper> inpWrapper = inpLd.outputBlobsWrappers[cons_inp]. |
||||
dynamicCast<NgraphBackendWrapper>(); |
||||
CV_Assert(!inpWrapper.empty()); |
||||
auto iter = std::find(inputNames.begin(), inputNames.end(), |
||||
inpWrapper->dataPtr->getName()); |
||||
if (iter == inputNames.end()) |
||||
{ |
||||
inputNames.push_back(inpWrapper->dataPtr->getName()); |
||||
inputs.push_back(inpLd.outputBlobs[cons_inp]); |
||||
} |
||||
curr_pos = cons + 1; |
||||
} |
||||
|
||||
auto inps = net->setInputs(inputs, inputNames); |
||||
for (auto& inp : inps) |
||||
{ |
||||
inputNodes.emplace_back(Ptr<BackendNode>(new InfEngineNgraphNode(inp))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Ptr<BackendNode> node; |
||||
if (!net.empty()) |
||||
{ |
||||
if (fused) |
||||
{ |
||||
bool inPlace = ld.inputBlobsId.size() == 1 && ld.outputBlobs.size() == 1 && |
||||
ld.inputBlobs[0]->data == ld.outputBlobs[0].data; |
||||
CV_Assert(inPlace); |
||||
node = layers[ld.inputBlobsId[0].lid].backendNodes[preferableBackend]; |
||||
ld.inputBlobsWrappers = layers[ld.inputBlobsId[0].lid].inputBlobsWrappers; |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
net = Ptr<InfEngineNgraphNet>(new InfEngineNgraphNet(*this)); |
||||
} |
||||
|
||||
if (!fused) |
||||
{ |
||||
CV_Assert(ld.inputBlobsId.size() == inputNodes.size()); |
||||
for (int i = 0; i < ld.inputBlobsId.size(); ++i) |
||||
{ |
||||
int lid = ld.inputBlobsId[i].lid; |
||||
int oid = ld.inputBlobsId[i].oid; |
||||
if (oid == 0 || lid == 0) |
||||
continue; |
||||
|
||||
auto ieInpNode = inputNodes[i].dynamicCast<InfEngineNgraphNode>(); |
||||
const auto& ngraph_input_node = ieInpNode->node; |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: bind output port " << lid << ":" << oid << " (" << ngraph_input_node->get_friendly_name() << ":" << ngraph_input_node->get_type_info().name << ")"); |
||||
|
||||
// Handle parameters from other subnets. Output port is not used in this case
|
||||
if ((ngraph::op::is_parameter(ngraph_input_node) || ngraph::op::is_constant(ngraph_input_node)) && |
||||
ngraph_input_node->get_output_size() == 1) |
||||
{ |
||||
inputNodes[i] = Ptr<BackendNode>(new InfEngineNgraphNode(ngraph_input_node)); |
||||
continue; |
||||
} |
||||
CV_CheckLT((size_t)oid, ngraph_input_node->get_output_size(), ""); |
||||
#if INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_4) |
||||
// FIXIT refactor ".initNgraph()" API to use Output<Node>
|
||||
// WA: use Concat to emulate Identity operation with requested output port
|
||||
auto oid_node = std::make_shared<ngraph::op::Concat>(ngraph::OutputVector { ngraph_input_node->output(oid) }, 0); |
||||
inputNodes[i] = Ptr<BackendNode>(new InfEngineNgraphNode(oid_node)); |
||||
#elif INF_ENGINE_VER_MAJOR_GT(INF_ENGINE_RELEASE_2020_3) |
||||
inputNodes[i] = Ptr<BackendNode>(new InfEngineNgraphNode(ieInpNode->node->get_output_as_single_output_node(oid))); |
||||
#else |
||||
inputNodes[i] = Ptr<BackendNode>(new InfEngineNgraphNode(ieInpNode->node->get_output_as_single_output_node(oid, false))); |
||||
#endif |
||||
} |
||||
|
||||
if (layer->supportBackend(preferableBackend)) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: wrap layer " << ld.name << "@" << ld.type << " - outputs: " << ld.outputBlobsWrappers.size()); |
||||
node = layer->initNgraph(ld.inputBlobsWrappers, inputNodes); |
||||
#if 0 // FIXIT doesn't work with multiple outputs (set name is applied to the same node)
|
||||
for (int i = 0; i < ld.outputBlobsWrappers.size(); ++i) |
||||
{ |
||||
InferenceEngine::DataPtr dataPtr = ngraphDataNode(ld.outputBlobsWrappers[i]); |
||||
node.dynamicCast<InfEngineNgraphNode>()->setName(dataPtr->getName()); |
||||
} |
||||
#else |
||||
node.dynamicCast<InfEngineNgraphNode>()->setName(layer->name); |
||||
#endif |
||||
} |
||||
else |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: layer is not supported: " << ld.name << "@" << ld.type); |
||||
node = Ptr<BackendNode>(new InfEngineNgraphNode(inputNodes, |
||||
ld.layerInstance, ld.inputBlobs, ld.outputBlobs, ld.internals)); |
||||
} |
||||
} |
||||
else if (node.empty()) |
||||
{ |
||||
CV_LOG_DEBUG(NULL, "DNN/IE: node.empty() bypass..."); |
||||
continue; |
||||
} |
||||
|
||||
ld.backendNodes[preferableBackend] = node; |
||||
|
||||
Ptr<InfEngineNgraphNode> ieNode = node.dynamicCast<InfEngineNgraphNode>(); |
||||
CV_Assert(!ieNode.empty()); |
||||
ieNode->net = net; |
||||
|
||||
for (const auto& pin : blobsToKeep_) |
||||
{ |
||||
if (pin.lid == ld.id) |
||||
{ |
||||
ieNode->net->addOutput(ieNode); |
||||
break; |
||||
} |
||||
} |
||||
ieNode->net->setNodePtr(&ieNode->node); |
||||
|
||||
net->addBlobs(ld.inputBlobsWrappers); |
||||
net->addBlobs(ld.outputBlobsWrappers); |
||||
addNgraphOutputs(ld); |
||||
} |
||||
|
||||
// Initialize all networks.
|
||||
for (MapIdToLayerData::reverse_iterator it = layers.rbegin(); it != layers.rend(); ++it) |
||||
{ |
||||
LayerData& ld = it->second; |
||||
auto iter = ld.backendNodes.find(preferableBackend); |
||||
if (iter == ld.backendNodes.end()) |
||||
continue; |
||||
|
||||
Ptr<BackendNode>& node = iter->second; |
||||
if (node.empty()) |
||||
continue; |
||||
|
||||
Ptr<InfEngineNgraphNode> ieNode = node.dynamicCast<InfEngineNgraphNode>(); |
||||
if (ieNode.empty()) |
||||
continue; |
||||
|
||||
CV_Assert(!ieNode->net.empty()); |
||||
|
||||
if (!ieNode->net->isInitialized()) |
||||
{ |
||||
ieNode->net->addOutput(ieNode); |
||||
ieNode->net->createNet((Target)preferableTarget); |
||||
ld.skip = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
//} // Net::Impl
|
||||
|
||||
/*static*/ |
||||
Net Net::Impl::createNetworkFromModelOptimizer(InferenceEngine::CNNNetwork& ieNet) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
|
||||
CV_TRACE_REGION("register_inputs"); |
||||
|
||||
std::vector<String> inputsNames; |
||||
std::vector<MatShape> inp_shapes; |
||||
for (auto& it : ieNet.getInputsInfo()) |
||||
{ |
||||
inputsNames.push_back(it.first); |
||||
std::vector<size_t> dims = it.second->getTensorDesc().getDims(); |
||||
inp_shapes.push_back(std::vector<int>(dims.begin(), dims.end())); |
||||
} |
||||
|
||||
Net cvNet; |
||||
cvNet.setInputsNames(inputsNames); |
||||
|
||||
// set empty input to determine input shapes
|
||||
for (int inp_id = 0; inp_id < inputsNames.size(); ++inp_id) |
||||
{ |
||||
cvNet.setInputShape(inputsNames[inp_id], inp_shapes[inp_id]); |
||||
} |
||||
|
||||
CV_TRACE_REGION_NEXT("backendNode"); |
||||
|
||||
Ptr<BackendNode> backendNode; |
||||
{ |
||||
auto fake_node = std::make_shared<ngraph::op::Parameter>(ngraph::element::f32, ngraph::Shape {}); |
||||
Ptr<InfEngineNgraphNode> backendNodeNGraph(new InfEngineNgraphNode(fake_node)); |
||||
backendNodeNGraph->net = Ptr<InfEngineNgraphNet>(new InfEngineNgraphNet(*(cvNet.impl), ieNet)); |
||||
backendNode = backendNodeNGraph; |
||||
} |
||||
|
||||
CV_TRACE_REGION_NEXT("register_outputs"); |
||||
|
||||
auto ngraphFunction = ieNet.getFunction(); |
||||
CV_Assert(ngraphFunction); |
||||
std::vector<std::shared_ptr<ngraph::Node>> ngraphOperations = ngraphFunction->get_ops(); |
||||
|
||||
for (auto& it : ieNet.getOutputsInfo()) |
||||
{ |
||||
CV_TRACE_REGION("output"); |
||||
const auto& outputName = it.first; |
||||
|
||||
LayerParams lp; |
||||
int lid = cvNet.addLayer(it.first, "", lp); |
||||
|
||||
LayerData& ld = cvNet.impl->layers[lid]; |
||||
|
||||
{ |
||||
Ptr<Layer> cvLayer(new NgraphBackendLayer(ieNet)); |
||||
cvLayer->name = outputName; |
||||
cvLayer->type = "_unknown_"; |
||||
|
||||
auto process_layer = [&](const std::string& name) -> bool |
||||
{ |
||||
CV_TRACE_REGION("ngraph_function"); |
||||
for (const auto& op : ngraphOperations) |
||||
{ |
||||
CV_Assert(op); |
||||
if (op->get_friendly_name() == name) |
||||
{ |
||||
const std::string typeName = op->get_type_info().name; |
||||
cvLayer->type = typeName; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
}; |
||||
|
||||
bool found = process_layer(outputName); |
||||
if (!found) |
||||
{ |
||||
auto pos = outputName.rfind('.'); // cut port number: ".0"
|
||||
if (pos != std::string::npos) |
||||
{ |
||||
std::string layerName = outputName.substr(0, pos); |
||||
found = process_layer(layerName); |
||||
} |
||||
} |
||||
if (!found) |
||||
CV_LOG_WARNING(NULL, "DNN/IE: Can't determine output layer type: '" << outputName << "'"); |
||||
|
||||
ld.layerInstance = cvLayer; |
||||
ld.backendNodes[DNN_BACKEND_INFERENCE_ENGINE_NGRAPH] = backendNode; |
||||
} |
||||
|
||||
for (int i = 0; i < inputsNames.size(); ++i) |
||||
cvNet.connect(0, i, lid, i); |
||||
} |
||||
|
||||
CV_TRACE_REGION_NEXT("finalize"); |
||||
|
||||
cvNet.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH); |
||||
|
||||
cvNet.impl->skipInfEngineInit = true; |
||||
return cvNet; |
||||
} |
||||
#endif // HAVE_INF_ENGINE
|
||||
|
||||
Net Net::readFromModelOptimizer(const String& xml, const String& bin) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
#ifndef HAVE_INF_ENGINE |
||||
CV_UNUSED(xml); CV_UNUSED(bin); |
||||
CV_Error(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); |
||||
#else |
||||
|
||||
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
||||
|
||||
InferenceEngine::Core& ie = getCore(""); |
||||
InferenceEngine::CNNNetwork ieNet = ie.ReadNetwork(xml, bin); |
||||
|
||||
return Impl::createNetworkFromModelOptimizer(ieNet); |
||||
#endif // HAVE_INF_ENGINE
|
||||
} |
||||
|
||||
Net Net::readFromModelOptimizer(const std::vector<uchar>& bufferModelConfig, const std::vector<uchar>& bufferWeights) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
CV_Assert(!bufferModelConfig.empty()); |
||||
CV_Assert(!bufferWeights.empty()); |
||||
return readFromModelOptimizer(bufferModelConfig.data(), bufferModelConfig.size(), |
||||
bufferWeights.data(), bufferWeights.size()); |
||||
} |
||||
|
||||
Net Net::readFromModelOptimizer( |
||||
const uchar* bufferModelConfigPtr, size_t bufferModelConfigSize, |
||||
const uchar* bufferWeightsPtr, size_t bufferWeightsSize |
||||
) |
||||
{ |
||||
CV_TRACE_FUNCTION(); |
||||
#ifndef HAVE_INF_ENGINE |
||||
CV_UNUSED(bufferModelConfigPtr); CV_UNUSED(bufferWeightsPtr); |
||||
CV_UNUSED(bufferModelConfigSize); CV_UNUSED(bufferModelConfigSize); |
||||
CV_Error(Error::StsError, "Build OpenCV with Inference Engine to enable loading models from Model Optimizer."); |
||||
#else |
||||
|
||||
FPDenormalsIgnoreHintScope fp_denormals_ignore_scope; |
||||
|
||||
InferenceEngine::Core& ie = getCore(""); |
||||
|
||||
std::string model; model.assign((char*)bufferModelConfigPtr, bufferModelConfigSize); |
||||
|
||||
InferenceEngine::CNNNetwork ieNet; |
||||
try |
||||
{ |
||||
InferenceEngine::TensorDesc tensorDesc(InferenceEngine::Precision::U8, { bufferWeightsSize }, InferenceEngine::Layout::C); |
||||
InferenceEngine::Blob::CPtr weights_blob = InferenceEngine::make_shared_blob<uint8_t>(tensorDesc, (uint8_t*)bufferWeightsPtr, bufferWeightsSize); |
||||
|
||||
ieNet = ie.ReadNetwork(model, weights_blob); |
||||
} |
||||
catch (const std::exception& e) |
||||
{ |
||||
CV_Error(Error::StsError, std::string("DNN: IE failed to load model: ") + e.what()); |
||||
} |
||||
|
||||
return Impl::createNetworkFromModelOptimizer(ieNet); |
||||
#endif // HAVE_INF_ENGINE
|
||||
} |
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,296 @@ |
||||
// 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 |
||||
|
||||
|
||||
// FIXIT drop from inference API
|
||||
static |
||||
void getQuantizationParams(const Mat& src, std::vector<float>& scales, std::vector<int>& zeropoints) |
||||
{ |
||||
const int qmin = -128; // INT8_MIN
|
||||
const int qmax = 127; // INT8_MAX
|
||||
|
||||
double rmin, rmax, sc, zp; |
||||
cv::minMaxIdx(src, &rmin, &rmax); |
||||
|
||||
// 0 must be present in the range [rmin, rmax]
|
||||
rmin = std::min(rmin, 0.0); |
||||
rmax = std::max(rmax, 0.0); |
||||
|
||||
sc = (rmax == rmin) ? 1.0 : (rmax - rmin)/(qmax - qmin); |
||||
zp = qmin - (rmin/sc); |
||||
|
||||
scales.push_back((float)sc); |
||||
zeropoints.push_back((int)std::round(zp)); |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
Net Net::Impl::quantize(InputArrayOfArrays calibData, int inputsDtype, int outputsDtype) |
||||
{ |
||||
// Net can be quantized only once.
|
||||
if (netWasQuantized) |
||||
CV_Error(Error::StsBadArg, "Cannot quantize a quantized net"); |
||||
|
||||
CV_CheckType(inputsDtype, inputsDtype == CV_32F || inputsDtype == CV_8S, "Input depth should be CV_32F or CV_8S"); |
||||
CV_CheckType(outputsDtype, outputsDtype == CV_32F || outputsDtype == CV_8S, "Output depth should be CV_32F or CV_8S"); |
||||
|
||||
bool originalFusion = fusion; |
||||
int prefBackend = preferableBackend; |
||||
int prefTarget = preferableTarget; |
||||
|
||||
// Disable fusions and use CPU backend to quantize net
|
||||
setPreferableBackend(DNN_BACKEND_OPENCV); |
||||
setPreferableTarget(DNN_TARGET_CPU); |
||||
enableFusion(false); |
||||
|
||||
if (calibData.isMat()) |
||||
{ |
||||
setInput(calibData.getMat(), /*name=*/"", /*scalefactor=*/1.0, /*mean=*/Scalar()); |
||||
} |
||||
else if (calibData.isMatVector()) |
||||
{ |
||||
std::vector<Mat> calibDataVec; |
||||
calibData.getMatVector(calibDataVec); |
||||
|
||||
std::vector<String> inpNames = netInputLayer->outNames; |
||||
CV_CheckEQ(calibDataVec.size(), inpNames.size(), "Calibration data size should be equal to number of inputs"); |
||||
for (int i = 0; i < calibDataVec.size(); i++) |
||||
setInput(calibDataVec[i], inpNames[i], /*scalefactor=*/1.0, /*mean=*/Scalar()); |
||||
} |
||||
|
||||
std::vector<String> outNames = getUnconnectedOutLayersNames(); |
||||
std::vector<LayerPin> pins; |
||||
for (int i = 0; i < outNames.size(); i++) |
||||
pins.push_back(getPinByAlias(outNames[i])); |
||||
setUpNet(pins); |
||||
|
||||
// Compute scales and zeropoints for all the layers
|
||||
std::vector<std::vector<float> > scales; |
||||
std::vector<std::vector<int> > zeropoints; |
||||
for (Impl::MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) |
||||
{ |
||||
LayerData& ld = it->second; |
||||
if (!ld.skip) |
||||
{ |
||||
Ptr<Layer> layer = ld.layerInstance; |
||||
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); |
||||
} |
||||
|
||||
std::vector<float> sc; |
||||
std::vector<int> zp; |
||||
if (ld.type == "TanH") |
||||
{ |
||||
sc.push_back(1.f/128); |
||||
zp.push_back(0); |
||||
} |
||||
else if (ld.type == "Sigmoid" || ld.type == "Softmax" || ld.type == "SoftMax") |
||||
{ |
||||
if (ld.params.get<bool>("log_softmax", false)) |
||||
{ |
||||
sc.push_back(16.f/256); |
||||
zp.push_back(127); |
||||
} |
||||
else |
||||
{ |
||||
sc.push_back(1.f/256); |
||||
zp.push_back(-128); |
||||
} |
||||
} |
||||
else if (ld.type == "Split" || ld.type == "Slice" || ld.type == "Crop") |
||||
{ |
||||
std::vector<float> inp_sc; std::vector<int> inp_zp; |
||||
getQuantizationParams(*ld.inputBlobs[0], inp_sc, inp_zp); |
||||
sc.assign(ld.outputBlobs.size(), inp_sc[0]); |
||||
zp.assign(ld.outputBlobs.size(), inp_zp[0]); |
||||
} |
||||
else |
||||
{ |
||||
for (int i = 0; i < ld.outputBlobs.size(); i++) |
||||
getQuantizationParams(ld.outputBlobs[i], sc, zp); |
||||
} |
||||
scales.push_back(sc); |
||||
zeropoints.push_back(zp); |
||||
} |
||||
|
||||
// For some layers, the input and output scales/zeropoints must be equal so that rescaling of inputs
|
||||
// is not needed during quantized inference. We start from the last layer and modify the layer's input scales/zeropoints
|
||||
// TODO : Need a different approach. Current solution fails when 2 such layers have the same input layer
|
||||
for (Impl::MapIdToLayerData::reverse_iterator it = layers.rbegin(); it != layers.rend(); ++it) |
||||
{ |
||||
LayerData& ld = it->second; |
||||
// Layers with multiple outputs. Number of outputs is equal to number of inputs
|
||||
if (ld.type == "Blank" || ld.type == "Dropout" || ld.type == "Identity" || ld.type == "Silence" || |
||||
ld.type == "Flatten" || ld.type == "Padding" || ld.type == "Permute" || ld.type == "Reshape" || |
||||
ld.type == "ReLU6" || ld.type == "Reorg" || ld.type == "ShuffleChannel" || ld.type == "Resize" || |
||||
(ld.type == "ReLU" && !ld.params.get<float>("negative_slope", 0.f)) /* ReLU with negative slope 0 */) |
||||
{ |
||||
for (int i = 0; i < ld.outputBlobs.size(); i++) |
||||
{ |
||||
LayerPin &pin = ld.inputBlobsId[i]; |
||||
scales[pin.lid][pin.oid] = scales[ld.id][i]; |
||||
zeropoints[pin.lid][pin.oid] = zeropoints[ld.id][i]; |
||||
} |
||||
} |
||||
// Layers with multiple inputs and single output.
|
||||
else if ((ld.type == "Pooling" && toLowerCase(ld.params.get<String>("pool", "max")) == "max") /* Max Pooling */ || |
||||
(ld.type == "Eltwise" && toLowerCase(ld.params.get<String>("operation", "sum")) == "max") /* Elementwise max */ || |
||||
ld.type == "Concat") |
||||
{ |
||||
for (int i = 0; i < ld.inputBlobsId.size(); i++) |
||||
{ |
||||
LayerPin &pin = ld.inputBlobsId[i]; |
||||
scales[pin.lid][pin.oid] = scales[ld.id][0]; |
||||
zeropoints[pin.lid][pin.oid] = zeropoints[ld.id][0]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Create a new Net and add quantized layers to it.
|
||||
Net dstNet_; |
||||
Net::Impl& dstNet = *(dstNet_.impl); |
||||
dstNet.netWasQuantized = true; |
||||
dstNet.setInputsNames(netInputLayer->outNames); |
||||
dstNet.setPreferableBackend(prefBackend); |
||||
dstNet.setPreferableTarget(prefTarget); |
||||
dstNet.enableFusion(originalFusion); |
||||
|
||||
for (Impl::MapIdToLayerData::iterator it = layers.begin(); it != layers.end(); it++) |
||||
{ |
||||
LayerData ld = it->second; |
||||
if (ld.id == 0) |
||||
{ |
||||
LayerData &quantInpLd = dstNet.layers[0]; |
||||
quantInpLd.dtype = inputsDtype; |
||||
quantInpLd.params.set("scales", DictValue::arrayReal(scales[0].data(), scales[0].size())); |
||||
quantInpLd.params.set("zeropoints", DictValue::arrayInt(zeropoints[0].data(), zeropoints[0].size())); |
||||
continue; |
||||
} |
||||
|
||||
std::vector<LayerPin> inpPins = ld.inputBlobsId; |
||||
// Fill input and output scales/zeropoints for the layer
|
||||
std::vector<std::vector<float> > inp_out_sc(2); |
||||
std::vector<std::vector<int> > inp_out_zp(2); |
||||
for (int i = 0; i < inpPins.size(); i++) |
||||
{ |
||||
LayerPin &pin = inpPins[i]; |
||||
inp_out_sc[0].push_back(scales[pin.lid][pin.oid]); |
||||
inp_out_zp[0].push_back(zeropoints[pin.lid][pin.oid]); |
||||
} |
||||
inp_out_sc[1] = scales[ld.id]; |
||||
inp_out_zp[1] = zeropoints[ld.id]; |
||||
|
||||
// Quantize layer
|
||||
Ptr<Layer> layer = ld.layerInstance; |
||||
if (layer->tryQuantize(inp_out_sc, inp_out_zp, ld.params)) |
||||
{ |
||||
ld.type += "Int8"; |
||||
ld.dtype = CV_8S; |
||||
} |
||||
ld.params.set("scales", DictValue::arrayReal(inp_out_sc[1].data(), inp_out_sc[1].size())); |
||||
ld.params.set("zeropoints", DictValue::arrayInt(inp_out_zp[1].data(), inp_out_zp[1].size())); |
||||
|
||||
// Check and add quantize/dequantize node before layer
|
||||
for (int i = 0; i < inpPins.size(); i++) |
||||
{ |
||||
LayerPin &pin = inpPins[i]; |
||||
LayerData &inpLd = dstNet.getLayerData(getLayerName(pin.lid)); |
||||
pin.lid = inpLd.id; |
||||
if (inpLd.dtype != ld.dtype) |
||||
{ |
||||
String layerName = (inpLd.dtype == CV_32F && ld.dtype == CV_8S) ? cv::format("quantize/%s/%d", inpLd.name.c_str(), pin.oid) |
||||
: cv::format("dequantize/%s/%d", inpLd.name.c_str(), pin.oid); |
||||
// Check if quantize/dequantize node for the input layer already exists
|
||||
if (dstNet.getLayerId(layerName) >= 0) |
||||
{ |
||||
pin.lid = dstNet.getLayerId(layerName); |
||||
pin.oid = 0; |
||||
} |
||||
else |
||||
{ |
||||
LayerParams lp; |
||||
lp.set("scales", inp_out_sc[0][i]); |
||||
lp.set("zeropoints", inp_out_zp[0][i]); |
||||
lp.name = layerName; |
||||
lp.type = (inpLd.dtype == CV_32F && ld.dtype == CV_8S) ? "Quantize" : "Dequantize"; |
||||
int newLid = dstNet.addLayer(lp.name, lp.type, ld.dtype, lp); |
||||
dstNet.connect(pin.lid, pin.oid, newLid, 0); |
||||
pin.lid = newLid; pin.oid = 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Add quantized layer to Net and connect to its inputs.
|
||||
int newLid = dstNet.addLayer(ld.name, ld.type, ld.dtype, ld.params); |
||||
for( int i = 0; i < inpPins.size(); i++ ) |
||||
dstNet.connect(inpPins[i].lid, inpPins[i].oid, newLid, i); |
||||
|
||||
// If the layer is a output layer, add quantize/dequantize node after it based on output's data type.
|
||||
if (ld.requiredOutputs.size() == 0 && ld.dtype != outputsDtype) |
||||
{ |
||||
LayerParams lp; |
||||
lp.set("scales", inp_out_sc[1][0]); |
||||
lp.set("zeropoints", inp_out_zp[1][0]); |
||||
lp.name = ((ld.dtype == CV_32F && outputsDtype == CV_8S) ? "quantize/" : "dequantize/") + ld.name; |
||||
lp.type = (ld.dtype == CV_32F && outputsDtype == CV_8S) ? "Quantize" : "Dequantize"; |
||||
dstNet.addLayerToPrev(lp.name, lp.type, outputsDtype, lp); |
||||
} |
||||
} |
||||
// Restore FP32 Net's backend, target and fusion
|
||||
setPreferableBackend(prefBackend); |
||||
setPreferableTarget(prefTarget); |
||||
enableFusion(originalFusion); |
||||
return dstNet_; |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
void Net::Impl::getInputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/ |
||||
{ |
||||
if (!netWasQuantized) |
||||
CV_Error(Error::StsBadFunc, "Net isn't quantized"); |
||||
|
||||
LayerParams &lp = layers[0].params; |
||||
DictValue sc = lp.get("scales"); |
||||
DictValue zp = lp.get("zeropoints"); |
||||
|
||||
for (int i = 0; i < sc.size(); i++) |
||||
{ |
||||
scales.push_back(sc.get<float>(i)); |
||||
zeropoints.push_back(zp.get<int>(i)); |
||||
} |
||||
} |
||||
|
||||
// FIXIT drop from inference API
|
||||
void Net::Impl::getOutputDetails(std::vector<float>& scales, std::vector<int>& zeropoints) /*const*/ |
||||
{ |
||||
if (!netWasQuantized) |
||||
CV_Error(Error::StsBadFunc, "Net isn't quantized"); |
||||
|
||||
std::vector<int> outLayerIds = getUnconnectedOutLayers(); |
||||
for (auto &lid : outLayerIds) |
||||
{ |
||||
LayerParams &lp = layers[lid].params; |
||||
DictValue sc = lp.get("scales"); |
||||
DictValue zp = lp.get("zeropoints"); |
||||
|
||||
for (int i = 0; i < sc.size(); i++) |
||||
{ |
||||
scales.push_back(sc.get<float>(i)); |
||||
zeropoints.push_back(zp.get<int>(i)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
@ -0,0 +1,106 @@ |
||||
// 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" |
||||
|
||||
#ifdef HAVE_CUDA |
||||
#include "op_cuda.hpp" |
||||
#include "cuda4dnn/init.hpp" |
||||
#include "net_impl.hpp" |
||||
|
||||
namespace cv { namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
void Net::Impl::initCUDABackend(const std::vector<LayerPin>& blobsToKeep_) |
||||
{ |
||||
CV_Assert(preferableBackend == DNN_BACKEND_CUDA); |
||||
|
||||
if (!cudaInfo) /* we need to check only once */ |
||||
cuda4dnn::checkVersions(); |
||||
|
||||
if (cuda4dnn::getDeviceCount() <= 0) |
||||
CV_Error(Error::StsError, "No CUDA capable device found."); |
||||
|
||||
if (cuda4dnn::getDevice() < 0) |
||||
CV_Error(Error::StsError, "No CUDA capable device selected."); |
||||
|
||||
if (!cuda4dnn::isDeviceCompatible()) |
||||
CV_Error(Error::GpuNotSupported, "OpenCV was not built to work with the selected device. Please check CUDA_ARCH_PTX or CUDA_ARCH_BIN in your build configuration."); |
||||
|
||||
if (preferableTarget == DNN_TARGET_CUDA_FP16 && !cuda4dnn::doesDeviceSupportFP16()) |
||||
{ |
||||
CV_LOG_WARNING(NULL, "The selected CUDA device does not support FP16 target; switching to FP32 target."); |
||||
preferableTarget = DNN_TARGET_CUDA; |
||||
} |
||||
|
||||
if (!cudaInfo) |
||||
{ |
||||
cuda4dnn::csl::CSLContext context; |
||||
context.stream = cuda4dnn::csl::Stream(true); |
||||
context.cublas_handle = cuda4dnn::csl::cublas::Handle(context.stream); |
||||
context.cudnn_handle = cuda4dnn::csl::cudnn::Handle(context.stream); |
||||
|
||||
auto d2h_stream = cuda4dnn::csl::Stream(true); // stream for background D2H data transfers
|
||||
cudaInfo = std::unique_ptr<CudaInfo_t>(new CudaInfo_t(std::move(context), std::move(d2h_stream))); |
||||
} |
||||
|
||||
cudaInfo->workspace = cuda4dnn::csl::Workspace(); // release workspace memory if any
|
||||
|
||||
for (auto& layer : layers) |
||||
{ |
||||
auto& ld = layer.second; |
||||
if (ld.id == 0) |
||||
{ |
||||
for (auto& wrapper : ld.inputBlobsWrappers) |
||||
{ |
||||
auto cudaWrapper = wrapper.dynamicCast<CUDABackendWrapper>(); |
||||
cudaWrapper->setStream(cudaInfo->context.stream, cudaInfo->d2h_stream); |
||||
} |
||||
} |
||||
|
||||
for (auto& wrapper : ld.outputBlobsWrappers) |
||||
{ |
||||
auto cudaWrapper = wrapper.dynamicCast<CUDABackendWrapper>(); |
||||
cudaWrapper->setStream(cudaInfo->context.stream, cudaInfo->d2h_stream); |
||||
} |
||||
} |
||||
|
||||
for (auto& layer : layers) |
||||
{ |
||||
auto& ld = layer.second; |
||||
auto& layerInstance = ld.layerInstance; |
||||
|
||||
if (!layerInstance->supportBackend(DNN_BACKEND_CUDA)) |
||||
{ |
||||
std::ostringstream os; |
||||
os << "CUDA backend will fallback to the CPU implementation for the layer \"" << ld.name |
||||
<< "\" of type " << ld.type << '\n'; |
||||
CV_LOG_INFO(NULL, os.str().c_str()); |
||||
continue; |
||||
} |
||||
|
||||
/* we make a copy so that `initCUDA` doesn't modify `cudaInfo->context` */ |
||||
auto context = cudaInfo->context; |
||||
auto node = layerInstance->initCUDA(&context, ld.inputBlobsWrappers, ld.outputBlobsWrappers); |
||||
ld.backendNodes[DNN_BACKEND_CUDA] = node; |
||||
|
||||
auto cudaNode = node.dynamicCast<CUDABackendNode>(); |
||||
cudaInfo->workspace.require(cudaNode->get_workspace_memory_in_bytes()); |
||||
} |
||||
|
||||
if (blobsToKeep_.size() > 1) |
||||
{ |
||||
for (const auto& pin : blobsToKeep_) |
||||
{ |
||||
LayerData& ld = layers[pin.lid]; |
||||
ld.cudaD2HBackgroundTransfers.push_back(pin.oid); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
||||
#endif // HAVE_CUDA
|
@ -0,0 +1,144 @@ |
||||
// 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 "op_halide.hpp" |
||||
#include "op_inf_engine.hpp" |
||||
#include "ie_ngraph.hpp" |
||||
#include "op_vkcom.hpp" |
||||
#include "op_cuda.hpp" |
||||
#include "op_webnn.hpp" |
||||
|
||||
#include "halide_scheduler.hpp" |
||||
|
||||
|
||||
namespace cv { |
||||
namespace dnn { |
||||
CV__DNN_INLINE_NS_BEGIN |
||||
|
||||
|
||||
class BackendRegistry |
||||
{ |
||||
public: |
||||
typedef std::vector< std::pair<Backend, Target> > BackendsList; |
||||
const BackendsList & getBackends() const { return backends; } |
||||
static BackendRegistry & getRegistry() |
||||
{ |
||||
static BackendRegistry impl; |
||||
return impl; |
||||
} |
||||
|
||||
|
||||
private: |
||||
BackendRegistry() |
||||
{ |
||||
#ifdef HAVE_HALIDE |
||||
backends.push_back(std::make_pair(DNN_BACKEND_HALIDE, DNN_TARGET_CPU)); |
||||
#ifdef HAVE_OPENCL |
||||
if (cv::ocl::useOpenCL()) |
||||
backends.push_back(std::make_pair(DNN_BACKEND_HALIDE, DNN_TARGET_OPENCL)); |
||||
#endif |
||||
#endif // HAVE_HALIDE
|
||||
|
||||
#ifdef HAVE_INF_ENGINE |
||||
if (openvino::checkTarget(DNN_TARGET_CPU)) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_CPU)); |
||||
#endif |
||||
} |
||||
if (openvino::checkTarget(DNN_TARGET_MYRIAD)) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_MYRIAD)); |
||||
#endif |
||||
} |
||||
if (openvino::checkTarget(DNN_TARGET_HDDL)) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_HDDL)); |
||||
#endif |
||||
} |
||||
#ifdef HAVE_OPENCL |
||||
if (cv::ocl::useOpenCL() && ocl::Device::getDefault().isIntel()) |
||||
{ |
||||
if (openvino::checkTarget(DNN_TARGET_OPENCL)) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_OPENCL)); |
||||
#endif |
||||
} |
||||
if (openvino::checkTarget(DNN_TARGET_OPENCL_FP16)) |
||||
{ |
||||
#ifdef HAVE_DNN_NGRAPH |
||||
backends.push_back(std::make_pair(DNN_BACKEND_INFERENCE_ENGINE_NGRAPH, DNN_TARGET_OPENCL_FP16)); |
||||
#endif |
||||
} |
||||
} |
||||
#endif |
||||
#endif // HAVE_INF_ENGINE
|
||||
|
||||
#ifdef HAVE_WEBNN |
||||
if (haveWebnn()) |
||||
{ |
||||
backends.push_back(std::make_pair(DNN_BACKEND_WEBNN, DNN_TARGET_CPU)); |
||||
} |
||||
#endif // HAVE_WEBNN
|
||||
|
||||
#ifdef HAVE_OPENCL |
||||
if (cv::ocl::useOpenCL()) |
||||
{ |
||||
backends.push_back(std::make_pair(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL)); |
||||
backends.push_back(std::make_pair(DNN_BACKEND_OPENCV, DNN_TARGET_OPENCL_FP16)); |
||||
} |
||||
#endif |
||||
|
||||
backends.push_back(std::make_pair(DNN_BACKEND_OPENCV, DNN_TARGET_CPU)); |
||||
|
||||
#ifdef HAVE_VULKAN |
||||
if (haveVulkan()) |
||||
backends.push_back(std::make_pair(DNN_BACKEND_VKCOM, DNN_TARGET_VULKAN)); |
||||
#endif |
||||
|
||||
#ifdef HAVE_CUDA |
||||
if (haveCUDA()) |
||||
{ |
||||
backends.push_back(std::make_pair(DNN_BACKEND_CUDA, DNN_TARGET_CUDA)); |
||||
backends.push_back(std::make_pair(DNN_BACKEND_CUDA, DNN_TARGET_CUDA_FP16)); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
BackendsList backends; |
||||
}; |
||||
|
||||
|
||||
std::vector<std::pair<Backend, Target>> getAvailableBackends() |
||||
{ |
||||
return BackendRegistry::getRegistry().getBackends(); |
||||
} |
||||
|
||||
std::vector<Target> getAvailableTargets(Backend be) |
||||
{ |
||||
if (be == DNN_BACKEND_DEFAULT) |
||||
be = (Backend)getParam_DNN_BACKEND_DEFAULT(); |
||||
#ifdef HAVE_INF_ENGINE |
||||
if (be == DNN_BACKEND_INFERENCE_ENGINE) |
||||
be = DNN_BACKEND_INFERENCE_ENGINE_NGRAPH; |
||||
#endif |
||||
|
||||
std::vector<Target> result; |
||||
const BackendRegistry::BackendsList all_backends = getAvailableBackends(); |
||||
for (BackendRegistry::BackendsList::const_iterator i = all_backends.begin(); i != all_backends.end(); ++i) |
||||
{ |
||||
if (i->first == be) |
||||
result.push_back(i->second); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
CV__DNN_INLINE_NS_END |
||||
}} // namespace cv::dnn
|
Loading…
Reference in new issue