mirror of
https://github.com/opencv/opencv.git
synced 2024-12-14 17:29:17 +08:00
7b582b71ba
dnn: TIM-VX NPU backend support * Add TimVX NPU backend for DNN module. * use official branch from tim-vx repo; fix detecting viv sdk Co-authored-by: fytao <yuantao.feng@outlook.com>
931 lines
26 KiB
C++
931 lines
26 KiB
C++
// 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.
|
|
//
|
|
// Copyright (C) 2019-2021, Shenzhen Institute of Artificial Intelligence and
|
|
// Robotics for Society, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
|
|
#include "precomp.hpp"
|
|
#include <opencv2/dnn/shape_utils.hpp>
|
|
#include "op_timvx.hpp"
|
|
#include "net_impl.hpp"
|
|
|
|
namespace cv
|
|
{
|
|
namespace dnn
|
|
{
|
|
#ifdef HAVE_TIMVX
|
|
|
|
CV__DNN_INLINE_NS_BEGIN
|
|
|
|
// update all comsumer
|
|
void Net::Impl::tvUpdateConfictMap(int graphIndex, LayerData& ld, std::vector<std::vector<int> >& graphConflictMap)
|
|
{
|
|
if (ld.consumers.empty())
|
|
return;
|
|
for (int i = 0; i < ld.consumers.size(); i++)
|
|
{
|
|
LayerData &consumerld = layers[ld.consumers[i].lid];
|
|
std::vector<int>::iterator it = std::find(graphConflictMap[ld.consumers[i].lid].begin(),
|
|
graphConflictMap[ld.consumers[i].lid].end(), graphIndex);
|
|
|
|
if (it == graphConflictMap[ld.consumers[i].lid].end())
|
|
{
|
|
graphConflictMap[ld.consumers[i].lid].push_back(graphIndex);
|
|
tvUpdateConfictMap(graphIndex, consumerld, graphConflictMap);
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Convert TRANSIENT to OUTPUT
|
|
void Net::Impl::tvConvertToOutputNode(const LayerData& ld, Ptr<TimVXBackendWrapper>& targetWrap)
|
|
{
|
|
// find right layer.
|
|
for (auto& inputLayerId : ld.inputLayersId)
|
|
{
|
|
LayerData &inputld = layers[inputLayerId];
|
|
auto itWrap = std::find(inputld.outputBlobsWrappers.begin(),
|
|
inputld.outputBlobsWrappers.end(), targetWrap);
|
|
if (itWrap != inputld.outputBlobsWrappers.end())
|
|
{
|
|
auto outputWrap = (*itWrap).dynamicCast<TimVXBackendWrapper>();
|
|
if (!outputWrap->isTensor())
|
|
continue;
|
|
|
|
auto inputNode = inputld.backendNodes[DNN_BACKEND_TIMVX].dynamicCast<TimVXBackendNode>();
|
|
if (!inputNode->isLast && inputNode->opIndex != -1)
|
|
{
|
|
CV_Assert(outputWrap->getTensorAttr() == tim::vx::TRANSIENT);
|
|
// set last
|
|
inputNode->isLast = true;
|
|
|
|
auto shapeType = getShapeTypeFromMat(outputWrap->getMat());
|
|
auto outQuant = outputWrap->getTensorQuantization();
|
|
|
|
outputWrap->setTensorShape(shapeType);
|
|
outputWrap->createTensor(inputNode->tvGraph->graph,
|
|
tim::vx::TensorAttribute::OUTPUT, outQuant);
|
|
int outIndex = inputNode->tvGraph->addWrapper(outputWrap);
|
|
inputNode->outputIndexList.clear();
|
|
inputNode->outputIndexList.push_back(outIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Net::Impl::initTimVXBackend()
|
|
{
|
|
CV_TRACE_FUNCTION();
|
|
CV_Assert(preferableBackend == DNN_BACKEND_TIMVX);
|
|
|
|
// Build TimVX Graph from sets of layers that support this TimVX backend.
|
|
// Split a whole model on several TimVX Graph if some of layers are not implemented by TimVX backend.
|
|
if (!haveTimVX())
|
|
return;
|
|
|
|
// Allocate graphConflictMap
|
|
if (timVxInfo.graphConflictMap.empty())
|
|
timVxInfo.graphConflictMap.resize(layers.size());
|
|
|
|
auto it = layers.begin();
|
|
bool isLast = false; // If the node is the last node in current tvGraph.
|
|
|
|
for (; it != layers.end(); it++)
|
|
{
|
|
isLast = false;
|
|
LayerData &ld = it->second;
|
|
if(ld.skip)
|
|
continue;
|
|
Ptr<Layer> layer = ld.layerInstance;
|
|
if (!layer->supportBackend(preferableBackend))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If layer consumers are more than one, set isLast true.
|
|
// For now, TimVX backend divides multiple branchs into multiple tvGraph.
|
|
if (ld.consumers.size() == 0)
|
|
{
|
|
isLast = true;
|
|
}
|
|
else if(ld.consumers.size() == 1)
|
|
{
|
|
LayerData* consumerld = &layers[ld.consumers[0].lid];
|
|
|
|
while (consumerld)
|
|
{
|
|
if (consumerld->skip)
|
|
{
|
|
if (consumerld->consumers.size() == 1)
|
|
{
|
|
int nextLayerId = consumerld->consumers[0].lid;
|
|
consumerld = &layers[nextLayerId];
|
|
}
|
|
else
|
|
{
|
|
isLast = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
Ptr<Layer>& consumerLayer = consumerld->layerInstance;
|
|
|
|
if (!isLast && !consumerLayer->supportBackend(preferableBackend))
|
|
{
|
|
isLast = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there are is multiple input, and only one of them is supported.
|
|
int tvSupportNum = 0;
|
|
for (int i = 0; i<ld.consumers.size(); i++)
|
|
{
|
|
LayerData* consumerld = &layers[ld.consumers[0].lid];
|
|
|
|
while (consumerld)
|
|
{
|
|
if (consumerld->skip)
|
|
{
|
|
if (consumerld->consumers.size() == 1)
|
|
{
|
|
int nextLayerId = consumerld->consumers[0].lid;
|
|
consumerld = &layers[nextLayerId];
|
|
}
|
|
else
|
|
{
|
|
isLast = true;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
Ptr<Layer>& consumerLayer = consumerld->layerInstance;
|
|
|
|
if (consumerLayer->supportBackend(preferableBackend))
|
|
{
|
|
tvSupportNum++;
|
|
}
|
|
}
|
|
|
|
if (tvSupportNum != 1)
|
|
isLast = true;
|
|
}
|
|
|
|
int graphIndex = -1;
|
|
bool needRecorrect = !timVxInfo.findGraphIndex(ld.inputBlobsWrappers, graphIndex);
|
|
|
|
if (graphIndex != -1 && !needRecorrect)
|
|
{
|
|
needRecorrect = timVxInfo.isConflict(ld.id, graphIndex);
|
|
}
|
|
|
|
// Recorrect the input layer.
|
|
if (needRecorrect)
|
|
{
|
|
// set all inputLayers' as last layer, and convert TRANSIENT to output.
|
|
for (int i = 0; i < ld.inputBlobsWrappers.size(); i++)
|
|
{
|
|
auto inputWrap = ld.inputBlobsWrappers[i];
|
|
auto tvInputWrap = inputWrap.dynamicCast<TimVXBackendWrapper>();
|
|
if (!tvInputWrap->isTensor())
|
|
continue;
|
|
|
|
auto attr = tvInputWrap->getTensorAttr();
|
|
if (attr == tim::vx::TensorAttribute::OUTPUT)
|
|
{
|
|
continue;
|
|
}
|
|
else if (attr == tim::vx::TensorAttribute::INPUT)
|
|
{
|
|
Mat matTmp = tvInputWrap->getMat();
|
|
tvInputWrap = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(matTmp));
|
|
|
|
}
|
|
else if (attr == tim::vx::TensorAttribute::TRANSIENT)
|
|
{
|
|
tvConvertToOutputNode(ld, tvInputWrap);
|
|
// updateConflictMap
|
|
tvUpdateConfictMap(graphIndex, ld, timVxInfo.graphConflictMap);
|
|
}
|
|
}
|
|
graphIndex = -1;
|
|
}
|
|
|
|
if (graphIndex == -1)
|
|
{
|
|
graphIndex = timVxInfo.createGraph();
|
|
}
|
|
timVxInfo.setTmpGraphIndex(graphIndex);
|
|
|
|
ld.backendNodes[DNN_BACKEND_TIMVX] =
|
|
layer->initTimVX(&timVxInfo, ld.inputBlobsWrappers, ld.outputBlobsWrappers, isLast);
|
|
|
|
// post process, create last node correctly.
|
|
if (isLast && ld.backendNodes[DNN_BACKEND_TIMVX])
|
|
{
|
|
auto tmpNode = ld.backendNodes[DNN_BACKEND_TIMVX].dynamicCast<TimVXBackendNode>();
|
|
tmpNode->isLast = true;
|
|
// update graphConflictMap
|
|
tvUpdateConfictMap(graphIndex, ld, timVxInfo.graphConflictMap);
|
|
}
|
|
|
|
// post process for failing to create timvx Node.
|
|
if (!ld.backendNodes[DNN_BACKEND_TIMVX])
|
|
{
|
|
for (int i = 0; i < ld.inputBlobsWrappers.size(); i++)
|
|
{
|
|
auto inputWrap = ld.inputBlobsWrappers[i];
|
|
auto tvInputWrap = inputWrap.dynamicCast<TimVXBackendWrapper>();
|
|
if (!tvInputWrap->isTensor())
|
|
continue;
|
|
|
|
auto attr = tvInputWrap->getTensorAttr();
|
|
if (attr == tim::vx::TensorAttribute::TRANSIENT)
|
|
{
|
|
tvConvertToOutputNode(ld, tvInputWrap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Op Binding
|
|
it = layers.begin();
|
|
Ptr<TimVXBackendNode> node;
|
|
std::vector<Ptr<TimVXGraph> > tmpGrapList;
|
|
for (; it != layers.end(); it++)
|
|
{
|
|
LayerData &ld = it->second;
|
|
|
|
if (ld.backendNodes[DNN_BACKEND_TIMVX])
|
|
node = ld.backendNodes[DNN_BACKEND_TIMVX].dynamicCast<TimVXBackendNode>();
|
|
else
|
|
continue;
|
|
|
|
// Binding tvTensor and tvOp
|
|
if (node->opIndex >= 0)
|
|
node->opBinding();
|
|
}
|
|
}
|
|
|
|
CV__DNN_INLINE_NS_END
|
|
|
|
// from CPU to NPU
|
|
bool copyToTensor(std::shared_ptr<tim::vx::Tensor> &dst, const Mat &src)
|
|
{
|
|
CV_Assert(src.isContinuous() && (src.type() == CV_8S || src.type() == CV_32F));
|
|
if (dst->CopyDataToTensor(src.data, src.total()))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// from NPU to CPU
|
|
bool copyToMat(const Mat &dst, std::shared_ptr<tim::vx::Tensor> &src)
|
|
{
|
|
CV_Assert(dst.isContinuous() && (dst.type() == CV_8S || dst.type() == CV_32F));
|
|
if (src->CopyDataFromTensor(dst.data))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
tvActivationType getTimVXActType(String & actString)
|
|
{
|
|
if (actString == "ReLUInt8") return tvActReLU;
|
|
if (actString == "ReLU6Int8") return tvActReLU6;
|
|
if (actString == "TanHInt8") return tvActTanH;
|
|
if (actString == "SwishInt8") return tvActSwish;
|
|
if (actString == "MishInt8") return tvActMish;
|
|
if (actString == "SigmoidInt8") return tvActSigmoid;
|
|
if (actString == "ELUInt8") return tvActELU;
|
|
|
|
return tvActNotSupported;
|
|
}
|
|
|
|
tim::vx::ShapeType getShapeTypeFromMat(const Mat& mat, bool ifConst)
|
|
{
|
|
/* Convert Mat shape to TimVX Tensor shape.
|
|
DataLayout in TimVX is WHCN, while NCHW in OpenCV.
|
|
So we do vector reverse.
|
|
*/
|
|
CV_Assert(!mat.empty());
|
|
tim::vx::ShapeType tvInputShape;
|
|
auto matShape = shape(mat);
|
|
tvInputShape.assign(matShape.begin(), matShape.end());
|
|
|
|
if ( matShape.size() > 1 ) // TODO: check when we need reverse the shape vector.
|
|
{
|
|
if (ifConst && tvInputShape.size() == 2 && tvInputShape[1] == 1)
|
|
{ // if bias vector, shape [n, 1] to [n].
|
|
tvInputShape.resize(1);
|
|
}
|
|
else
|
|
std::reverse(tvInputShape.begin(), tvInputShape.end());
|
|
}
|
|
return tvInputShape;
|
|
}
|
|
|
|
bool getQuantType(const std::vector<float>& scales, int numOutput)
|
|
{
|
|
CV_Assert(!scales.empty());
|
|
if (numOutput == -1)
|
|
{
|
|
numOutput = scales.size();
|
|
}
|
|
bool tvSymmetric = false;
|
|
|
|
for (int i =1; i < numOutput; i++)
|
|
{
|
|
if (std::abs(scales[0] - scales[i]) > std::numeric_limits<float>::epsilon())
|
|
{
|
|
tvSymmetric = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return tvSymmetric;
|
|
}
|
|
|
|
// convert mat Depth to tensorDataType
|
|
tim::vx::DataType dataTypeConvert(int matDepth)
|
|
{
|
|
tim::vx::DataType tensorDataType;
|
|
switch(matDepth)
|
|
{
|
|
case CV_8U:
|
|
{
|
|
tensorDataType = tim::vx::DataType::UINT8;
|
|
break;
|
|
}
|
|
case CV_8S:
|
|
{
|
|
tensorDataType = tim::vx::DataType::INT8;
|
|
break;
|
|
}
|
|
case CV_16U:
|
|
{
|
|
tensorDataType = tim::vx::DataType::UINT16;
|
|
break;
|
|
}
|
|
case CV_16S:
|
|
{
|
|
tensorDataType = tim::vx::DataType::INT16;
|
|
break;
|
|
}
|
|
case CV_32S:
|
|
{
|
|
tensorDataType = tim::vx::DataType::INT32;
|
|
break;
|
|
}
|
|
case CV_32F:
|
|
{
|
|
tensorDataType = tim::vx::DataType::FLOAT32;
|
|
break;
|
|
}
|
|
case CV_16F:
|
|
{
|
|
tensorDataType = tim::vx::DataType::FLOAT16;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
tensorDataType = tim::vx::DataType::UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
return tensorDataType;
|
|
}
|
|
|
|
std::vector<Ptr<TimVXBackendWrapper> > getWrappers(const std::vector<int> wrappersIndex,
|
|
Ptr<TimVXGraph> tvGraph)
|
|
{
|
|
std::vector<Ptr<TimVXBackendWrapper> > wrappers;
|
|
for (int i = 0; i<wrappersIndex.size(); i++)
|
|
{
|
|
auto wrapper = tvGraph->getWrapper(wrappersIndex[i]);
|
|
if (wrapper)
|
|
wrappers.push_back(wrapper);
|
|
}
|
|
|
|
return wrappers;
|
|
}
|
|
|
|
// *********************** TimVXGraph ********************
|
|
TimVXGraph::TimVXGraph()
|
|
{
|
|
// new TimVX Graph
|
|
context = tim::vx::Context::Create();
|
|
graph = context->CreateGraph();
|
|
isCompiled = false;
|
|
}
|
|
|
|
TimVXGraph::~TimVXGraph()
|
|
{
|
|
|
|
// release opList
|
|
for (auto& tensor: tensorList)
|
|
tensor.reset();
|
|
|
|
// release tensorList
|
|
for (auto& op: opList)
|
|
op.reset();
|
|
|
|
// release graph
|
|
graph.reset();
|
|
|
|
// release context
|
|
context.reset();
|
|
}
|
|
|
|
std::shared_ptr<tim::vx::Operation> TimVXGraph::getOp(const int opIndex)
|
|
{
|
|
CV_Assert(0 <= opIndex && !opList.empty() && opIndex < opList.size());
|
|
return opList[opIndex];
|
|
}
|
|
|
|
int TimVXGraph::addWrapper(Ptr<TimVXBackendWrapper>& tensorWrapper)
|
|
{
|
|
CV_Assert(tensorWrapper->isTensor());
|
|
tim::vx::TensorAttribute tensorAttr = tensorWrapper->getTensorAttr();
|
|
|
|
wrapperList.push_back(tensorWrapper);
|
|
tensorList.push_back(tensorWrapper->getTensor());
|
|
int wrapperIndex = wrapperList.size() -1;
|
|
|
|
if (tensorAttr == tim::vx::TensorAttribute::INPUT)
|
|
{
|
|
inputWrappersIndex.push_back(wrapperIndex);
|
|
}
|
|
|
|
if (tensorAttr == tim::vx::TensorAttribute::OUTPUT)
|
|
{
|
|
outputWrappersIndex.push_back(wrapperIndex);
|
|
}
|
|
|
|
return wrapperIndex;
|
|
}
|
|
|
|
Ptr<TimVXBackendWrapper> TimVXGraph::getWrapper(int wrapperIndex)
|
|
{
|
|
CV_Assert(wrapperIndex>=0 && wrapperIndex < wrapperList.size());
|
|
return wrapperList[wrapperIndex];
|
|
}
|
|
|
|
int TimVXGraph::addOp(const std::shared_ptr<tim::vx::Operation>& op)
|
|
{
|
|
CV_Assert(op);
|
|
opList.emplace_back(op);
|
|
return opList.size()-1;
|
|
}
|
|
|
|
int TimVXGraph::getTensorIndex(const std::shared_ptr<tim::vx::Tensor>& tensor)
|
|
{
|
|
auto it = find(tensorList.begin(), tensorList.end(), tensor);
|
|
if (it != tensorList.end())
|
|
return it - tensorList.begin();
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
void TimVXGraph::forward()
|
|
{
|
|
CV_Assert(!inputWrappersIndex.empty() && !outputWrappersIndex.empty());
|
|
|
|
// Every TimVXGraph Instance only compiles once.
|
|
if (!this->isCompiled)
|
|
{
|
|
if (!graph->Compile())
|
|
CV_Error(cv::Error::StsBadArg, " Fail to compile TimVX graph!");
|
|
this->isCompiled = true;
|
|
}
|
|
|
|
if (!graph->Run())
|
|
CV_Error(cv::Error::StsBadArg, " Fail to run TimVX graph!");
|
|
}
|
|
|
|
// *********************** TimVXBackendNode ********************
|
|
TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_): BackendNode(DNN_BACKEND_TIMVX)
|
|
{
|
|
opIndex = -1;
|
|
tvGraph = tvGraph_;
|
|
isLast = false;
|
|
}
|
|
|
|
TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_,
|
|
const std::shared_ptr<tim::vx::Operation>& op_): BackendNode(DNN_BACKEND_TIMVX)
|
|
{
|
|
tvGraph = tvGraph_;
|
|
opIndex = tvGraph->addOp(op_);
|
|
isLast = false;
|
|
}
|
|
|
|
TimVXBackendNode::TimVXBackendNode(const Ptr<TimVXGraph>& tvGraph_, std::shared_ptr<tim::vx::Operation>& op_,
|
|
std::vector<int>& inputsIndex, std::vector<int>& outpusIndex)
|
|
:BackendNode(DNN_BACKEND_TIMVX)
|
|
{
|
|
tvGraph = tvGraph_;
|
|
opIndex = tvGraph->addOp(op_);
|
|
isLast = false;
|
|
|
|
if (!inputsIndex.empty())
|
|
inputIndexList.assign(inputsIndex.begin(), inputsIndex.end());
|
|
|
|
if (!outpusIndex.empty())
|
|
outputIndexList.assign(outpusIndex.begin(), outpusIndex.end());
|
|
}
|
|
|
|
bool TimVXBackendNode::opBinding()
|
|
{
|
|
if (!tvGraph || tvGraph->isCompiled || opIndex == -1)
|
|
return false;
|
|
|
|
std::shared_ptr<tim::vx::Operation> op = tvGraph->getOp(opIndex);
|
|
|
|
if (!inputIndexList.empty())
|
|
{
|
|
std::vector<Ptr<TimVXBackendWrapper> > inputsWrapper = getWrappers(inputIndexList, tvGraph);
|
|
// Binding input Tensor.
|
|
for (auto& warpper: inputsWrapper)
|
|
{
|
|
op->BindInput(warpper->getTensor());
|
|
}
|
|
}
|
|
|
|
if (!outputIndexList.empty())
|
|
{
|
|
std::vector<Ptr<TimVXBackendWrapper> > outputsWrapper = getWrappers(outputIndexList, tvGraph);
|
|
for (auto& warpper: outputsWrapper)
|
|
{
|
|
op->BindOutput(warpper->getTensor());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TimVXBackendNode::setInputTensor()
|
|
{
|
|
if (!tvGraph || opIndex == -1)
|
|
return;
|
|
|
|
if (!inputIndexList.empty())
|
|
{
|
|
std::vector<Ptr<TimVXBackendWrapper> > inputsWrapper = getWrappers(inputIndexList, tvGraph);
|
|
|
|
// Binding input Tensor.
|
|
for (auto& warpper: inputsWrapper)
|
|
{
|
|
if (warpper->getTensorAttr() == tim::vx::TensorAttribute::INPUT)
|
|
{
|
|
warpper->setHostDirty();
|
|
warpper->copyToDevice();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// *********************** TimVXBackendWrapper ********************
|
|
// Default Constructor
|
|
TimVXBackendWrapper::TimVXBackendWrapper() : BackendWrapper(DNN_BACKEND_TIMVX, DNN_TARGET_NPU)
|
|
{
|
|
isTensor_ = false;
|
|
deviceDirty = false;
|
|
hostDirty = false;
|
|
tensorType = tim::vx::DataType::UNKNOWN;
|
|
tensorShape = {};
|
|
tensorIndex = -1;
|
|
tensorAttr = tim::vx::TensorAttribute::CONSTANT;
|
|
}
|
|
|
|
TimVXBackendWrapper::TimVXBackendWrapper(Mat& m) : BackendWrapper(DNN_BACKEND_TIMVX,
|
|
DNN_TARGET_NPU)
|
|
{
|
|
host = m;
|
|
isTensor_ = false;
|
|
deviceDirty = false;
|
|
hostDirty = true;
|
|
tensorType = dataTypeConvert(m.type());
|
|
tensorShape = {};
|
|
tensorIndex = -1;
|
|
tensorAttr = tim::vx::TensorAttribute::CONSTANT;
|
|
|
|
// TODO: unsupported data by TimVX should run convert function first.
|
|
CV_Assert(tensorType != tim::vx::DataType::UNKNOWN);
|
|
}
|
|
|
|
TimVXBackendWrapper::TimVXBackendWrapper(const Ptr<BackendWrapper>& baseBuffer, Mat& m)
|
|
:BackendWrapper(DNN_BACKEND_TIMVX, DNN_TARGET_NPU)
|
|
{
|
|
Ptr<TimVXBackendWrapper> base = baseBuffer.dynamicCast<TimVXBackendWrapper>();
|
|
CV_Assert(!base.empty());
|
|
tensor = base->tensor;
|
|
isTensor_ = base->isTensor_;
|
|
tensorIndex = base->tensorIndex;
|
|
tensorType = base->tensorType;
|
|
tensorAttr = base->tensorAttr;
|
|
tensorShape = base->tensorShape;
|
|
deviceDirty = base->deviceDirty;
|
|
hostDirty = base->hostDirty;
|
|
host = m;
|
|
}
|
|
|
|
TimVXBackendWrapper::TimVXBackendWrapper(std::shared_ptr<tim::vx::Tensor>& tensor_)
|
|
:BackendWrapper(DNN_BACKEND_TIMVX, DNN_TARGET_NPU)
|
|
{
|
|
tensor = tensor_;
|
|
isTensor_ = true;
|
|
deviceDirty = true;
|
|
hostDirty = false;
|
|
tensorType = tensor_->GetDataType(); // getTensor DataType.
|
|
tensorAttr = tensor_->GetSpec().attr_; // getTensor Attribution.
|
|
tensorShape = tensor_->GetShape();
|
|
tensorIndex = -1;
|
|
}
|
|
|
|
void TimVXBackendWrapper::setTensorShape(const tim::vx::ShapeType & matShape)
|
|
{
|
|
CV_Assert(!matShape.empty());
|
|
tensorShape.assign(matShape.begin(), matShape.end());
|
|
}
|
|
|
|
int TimVXBackendWrapper::getTensorIndex()
|
|
{
|
|
CV_Assert(isTensor_);
|
|
return tensorIndex;
|
|
}
|
|
|
|
tim::vx::TensorAttribute TimVXBackendWrapper::getTensorAttr()
|
|
{
|
|
CV_Assert(isTensor_);
|
|
return tensorAttr;
|
|
}
|
|
|
|
// Create tensor
|
|
void TimVXBackendWrapper::createTensor(std::shared_ptr<tim::vx::Graph>& graph,
|
|
tim::vx::TensorAttribute tensorAttribute)
|
|
{
|
|
Ptr<tim::vx::Quantization> epmtyQuant = nullptr;
|
|
return this->createTensor(graph, tensorAttribute, epmtyQuant);
|
|
}
|
|
|
|
// Create tensor
|
|
void TimVXBackendWrapper::createTensor(std::shared_ptr<tim::vx::Graph>& graph,
|
|
tim::vx::TensorAttribute tensorAttribute, Ptr<tim::vx::Quantization>& tvQuant)
|
|
{
|
|
CV_Assert(graph);
|
|
tim::vx::TensorSpec tensorSpec;
|
|
|
|
if (tensorAttribute == tim::vx::INPUT)
|
|
{
|
|
CV_Assert(!host.empty());
|
|
tensorShape = getShapeTypeFromMat(host);
|
|
}
|
|
else if (tensorAttribute == tim::vx::OUTPUT)
|
|
{
|
|
CV_Assert(!tensorShape.empty() && !host.empty());
|
|
tensorShape = getShapeTypeFromMat(host);
|
|
}
|
|
else if (tensorAttribute == tim::vx::CONSTANT)
|
|
{
|
|
if (!host.empty())
|
|
tensorShape = getShapeTypeFromMat(host, true);
|
|
}
|
|
else
|
|
{
|
|
if (!host.empty())
|
|
tensorShape = getShapeTypeFromMat(host);
|
|
}
|
|
|
|
// Tensor shape
|
|
if (tvQuant)
|
|
{
|
|
tensorSpec = tim::vx::TensorSpec(tensorType, tensorShape, tensorAttribute, *tvQuant);
|
|
}
|
|
else
|
|
{
|
|
tensorSpec = tim::vx::TensorSpec(tensorType, tensorShape, tensorAttribute);
|
|
}
|
|
|
|
if (!host.empty() && tensorAttribute != tim::vx::INPUT && tensorAttribute != tim::vx::OUTPUT && tensorAttribute != tim::vx::TRANSIENT)
|
|
{
|
|
tensor = graph->CreateTensor(tensorSpec, (void *)(host.data));
|
|
}
|
|
else
|
|
{
|
|
tensor = graph->CreateTensor(tensorSpec);
|
|
}
|
|
isTensor_ = true;
|
|
|
|
// set Attribution
|
|
tensorAttr = tensorAttribute;
|
|
}
|
|
|
|
Ptr<tim::vx::Quantization> TimVXBackendWrapper::getTensorQuantization()
|
|
{
|
|
CV_Assert(isTensor_ && tensor);
|
|
auto quantize = tensor->GetQuantization();
|
|
return makePtr<tim::vx::Quantization>(quantize);
|
|
}
|
|
|
|
std::shared_ptr<tim::vx::Tensor> TimVXBackendWrapper::getTensor()
|
|
{
|
|
CV_Assert(isTensor_);
|
|
return tensor;
|
|
}
|
|
|
|
Mat TimVXBackendWrapper::getMat()
|
|
{
|
|
if (host.empty())
|
|
return {};
|
|
return host;
|
|
}
|
|
|
|
|
|
bool TimVXBackendWrapper::isTensor()
|
|
{
|
|
return isTensor_;
|
|
}
|
|
|
|
void TimVXBackendWrapper::copyToHost()
|
|
{
|
|
if (deviceDirty && !host.empty())
|
|
{
|
|
copyToMat(host, tensor);
|
|
deviceDirty = false;
|
|
}
|
|
}
|
|
|
|
void TimVXBackendWrapper::setHostDirty()
|
|
{
|
|
hostDirty = true;
|
|
}
|
|
|
|
void TimVXBackendWrapper::setDeviceDirty()
|
|
{
|
|
deviceDirty = true;
|
|
}
|
|
|
|
void TimVXBackendWrapper::copyToDevice()
|
|
{
|
|
if (isTensor_ && hostDirty && !host.empty())
|
|
{
|
|
copyToTensor(tensor, host);
|
|
hostDirty = false;
|
|
}
|
|
}
|
|
|
|
// *********************** TimVXInfo ********************
|
|
TimVXInfo::TimVXInfo()
|
|
{
|
|
graphIndex = -1;
|
|
}
|
|
|
|
TimVXInfo::~TimVXInfo()
|
|
{}
|
|
|
|
int TimVXInfo::createGraph()
|
|
{
|
|
Ptr<TimVXGraph> tmpGraph = Ptr<TimVXGraph>(new TimVXGraph());
|
|
this->tvGraphList.push_back(tmpGraph);
|
|
return this->tvGraphList.size() - 1;
|
|
}
|
|
|
|
bool TimVXInfo::findGraphIndex(const std::vector<Ptr<BackendWrapper> > &inputsWrapper, int& graphIndex)
|
|
{
|
|
graphIndex = -1;
|
|
int wrapperSize = inputsWrapper.size();
|
|
int graphSize = tvGraphList.size();
|
|
|
|
if (wrapperSize != 0 && graphSize == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int tensorIndex = -1;
|
|
Ptr<TimVXBackendWrapper> wrapper;
|
|
Ptr<TimVXGraph> tvGraph;
|
|
|
|
for (int i = 0; i < graphSize; i++)
|
|
{
|
|
tvGraph = tvGraphList[i];
|
|
for (int j = 0; j < wrapperSize; j++ )
|
|
{
|
|
wrapper = inputsWrapper[j].dynamicCast<TimVXBackendWrapper>();
|
|
|
|
if (!wrapper->isTensor()) // Skip wrapper without Tensor.
|
|
continue;
|
|
|
|
tensorIndex = tvGraph->getTensorIndex(wrapper->getTensor());
|
|
if (tensorIndex != -1 && wrapper->getTensorAttr() == tim::vx::TensorAttribute::TRANSIENT)
|
|
{
|
|
if (graphIndex == -1)
|
|
graphIndex = i;
|
|
else if (graphIndex != i) // if inputs of the same inputWrapper are from differen tvGraph.
|
|
{
|
|
graphIndex = -1;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TimVXInfo::setTmpGraphIndex(int graphIndex)
|
|
{
|
|
this->graphIndex = graphIndex;
|
|
}
|
|
|
|
int TimVXInfo::getTmpGraphIndex()
|
|
{
|
|
int res = -1;
|
|
if (graphIndex != -1)
|
|
{
|
|
res = graphIndex;
|
|
graphIndex = -1;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool TimVXInfo::isConflict(int layerId, int graphIndex)
|
|
{
|
|
if (graphConflictMap[layerId].empty())
|
|
return false;
|
|
|
|
std::vector<int>::iterator it = std::find(graphConflictMap[layerId].begin(),
|
|
graphConflictMap[layerId].end(), graphIndex);
|
|
if (it != graphConflictMap[layerId].end())
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
Ptr<TimVXGraph> TimVXInfo::getGraph()
|
|
{
|
|
int index = getTmpGraphIndex();
|
|
if (0 <= index && index < tvGraphList.size())
|
|
return tvGraphList[index];
|
|
else
|
|
return {};
|
|
}
|
|
|
|
#endif
|
|
|
|
void forwardTimVX(std::vector<Ptr<BackendWrapper> >& outputs, const Ptr<BackendNode>& node_)
|
|
{
|
|
#ifdef HAVE_TIMVX
|
|
CV_Assert(!node_.empty());
|
|
Ptr<TimVXBackendNode> node = node_.dynamicCast<TimVXBackendNode>();
|
|
|
|
if (node)
|
|
{
|
|
// set input
|
|
node->setInputTensor();
|
|
|
|
// graph Forward
|
|
if (node->isLast)
|
|
{
|
|
node->tvGraph->forward();
|
|
}
|
|
}
|
|
else
|
|
return;
|
|
|
|
// set ouput
|
|
Ptr<TimVXBackendWrapper> outWarpper;
|
|
for (int i = 0; i < outputs.size(); i++)
|
|
{
|
|
outWarpper = outputs[i].dynamicCast<TimVXBackendWrapper>();
|
|
if (outWarpper->isTensor() && outWarpper->getTensorAttr() == tim::vx::TensorAttribute::OUTPUT)
|
|
{
|
|
outWarpper->setDeviceDirty();
|
|
outWarpper->copyToHost();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool haveTimVX()
|
|
{
|
|
#ifdef HAVE_TIMVX
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
} // namespace dnn
|
|
} // namespace cv
|