mirror of
https://github.com/opencv/opencv.git
synced 2024-12-17 02:48:01 +08:00
6da2ddcf0e
Remove support OpenVINO lower than 2022.1 release Remove legacy InferenceEngine wrappers
322 lines
12 KiB
C++
322 lines
12 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.
|
|
|
|
#include "../precomp.hpp"
|
|
#include "layers_common.hpp"
|
|
#include "../op_timvx.hpp"
|
|
#include "../ie_ngraph.hpp"
|
|
|
|
namespace cv
|
|
{
|
|
namespace dnn
|
|
{
|
|
|
|
class BatchNormLayerInt8Impl CV_FINAL : public BatchNormLayerInt8
|
|
{
|
|
public:
|
|
Mat origin_weights, origin_bias;
|
|
Mat weights_, bias_;
|
|
mutable int dims;
|
|
|
|
BatchNormLayerInt8Impl(const LayerParams& params)
|
|
: dims(-1)
|
|
{
|
|
setParamsFrom(params);
|
|
useGlobalStats = params.get<bool>("use_global_stats", true);
|
|
input_sc = params.get<float>("input_scale");
|
|
input_zp = params.get<int>("input_zeropoint");
|
|
output_sc = params.get<float>("scales");
|
|
output_zp = params.get<int>("zeropoints");
|
|
|
|
CV_Assert(blobs.size() == 2);
|
|
size_t n = blobs[0].total();
|
|
CV_Assert(blobs[1].total() == n &&
|
|
blobs[0].isContinuous() && blobs[1].isContinuous() &&
|
|
blobs[0].type() == CV_32F && blobs[1].type() == CV_32F);
|
|
|
|
origin_weights = blobs[0];
|
|
origin_bias = blobs[1];
|
|
}
|
|
|
|
virtual void finalize(InputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE
|
|
{
|
|
origin_weights.convertTo(weights_, CV_32F, input_sc/output_sc);
|
|
addWeighted(origin_bias, 1.0/output_sc, weights_, -input_zp, output_zp, bias_, CV_32F);
|
|
}
|
|
|
|
void getScaleShift(Mat& scale, Mat& shift) const CV_OVERRIDE
|
|
{
|
|
scale = origin_weights;
|
|
shift = origin_bias;
|
|
}
|
|
|
|
void getScaleZeropoint(float& scale, int& zeropoint) const CV_OVERRIDE
|
|
{
|
|
scale = output_sc;
|
|
zeropoint = output_zp;
|
|
}
|
|
|
|
virtual bool tryFuse(Ptr<Layer>& top) CV_OVERRIDE
|
|
{
|
|
Mat w_, b_;
|
|
top->getScaleShift(w_, b_);
|
|
if (w_.empty() && b_.empty())
|
|
return false;
|
|
|
|
const int numChannels = weights_.total();
|
|
const int numFusedWeights = w_.total();
|
|
const int numFusedBias = b_.total();
|
|
|
|
if ((numFusedWeights != numChannels && numFusedWeights != 1 && !w_.empty()) ||
|
|
(numFusedBias != numChannels && numFusedBias != 1 && !b_.empty()))
|
|
return false;
|
|
|
|
float new_sc;
|
|
int new_zp;
|
|
top->getScaleZeropoint(new_sc, new_zp);
|
|
|
|
Mat w = numFusedWeights == 1 ? Mat(1, numChannels, CV_32F, Scalar(w_.at<float>(0))) :
|
|
(w_.empty() ? Mat::ones(1, numChannels, CV_32F) : w_.reshape(1, 1));
|
|
|
|
Mat b = numFusedBias == 1 ? Mat(1, numChannels, CV_32F, Scalar(b_.at<float>(0))) :
|
|
(b_.empty() ? Mat::zeros(1, numChannels, CV_32F) : b_.reshape(1, 1));
|
|
|
|
weights_ = Mat(); bias_ = Mat();
|
|
multiply(origin_weights, w, weights_, input_sc/new_sc, CV_32F);
|
|
multiply(origin_bias, w, bias_);
|
|
add(bias_, b, bias_);
|
|
addWeighted(bias_, 1.0/new_sc, weights_, -input_zp, new_zp, bias_, CV_32F);
|
|
return true;
|
|
}
|
|
|
|
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
|
const int requiredOutputs,
|
|
std::vector<MatShape> &outputs,
|
|
std::vector<MatShape> &internals) const CV_OVERRIDE
|
|
{
|
|
dims = inputs[0].size();
|
|
if (!useGlobalStats && inputs[0][0] != 1)
|
|
CV_Error(Error::StsNotImplemented, "Batch normalization in training mode with batch size > 1");
|
|
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
|
return true;
|
|
}
|
|
|
|
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
|
{
|
|
if (backendId == DNN_BACKEND_TIMVX && haveTimVX())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return backendId == DNN_BACKEND_OPENCV ||
|
|
backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH;
|
|
}
|
|
|
|
bool setActivation(const Ptr<ActivationLayer>& layer) CV_OVERRIDE
|
|
{
|
|
Ptr<ActivationLayerInt8> activ_int8 = layer.dynamicCast<ActivationLayerInt8>();
|
|
if (!activ_int8.empty())
|
|
{
|
|
return activ_int8->blobs.empty();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual Ptr<BackendNode> initTimVX(void* timVXInfo_,
|
|
const std::vector<Ptr<BackendWrapper> > &inputsWrapper,
|
|
const std::vector<Ptr<BackendWrapper> > &outputsWrapper,
|
|
bool isLast) CV_OVERRIDE
|
|
{
|
|
#ifdef HAVE_TIMVX
|
|
// tvGraph Initialization.
|
|
auto timVxInfo = reinterpret_cast<TimVXInfo *>(timVXInfo_);
|
|
CV_Assert(timVxInfo);
|
|
Ptr<TimVXGraph> tvGraph = timVxInfo->getGraph();
|
|
CV_Assert(tvGraph);
|
|
Ptr<tim::vx::Graph> graph = tvGraph->graph;
|
|
|
|
const int numChannels = (int)origin_bias.total();
|
|
Mat tvGamma = origin_weights.reshape(1, numChannels);
|
|
Mat tvBeta = origin_bias.reshape(1, numChannels);
|
|
|
|
std::vector<int> inputsIndex;
|
|
std::vector<int> outputsIndex;
|
|
|
|
Mat tvMean = Mat::zeros(1, numChannels, CV_32F);
|
|
tvMean = tvMean.reshape(1, numChannels);
|
|
Mat tvVar = Mat::ones(1, numChannels, CV_32F);
|
|
tvVar = tvVar.reshape(1, numChannels);
|
|
|
|
CV_Assert(inputsWrapper.size() == 1);
|
|
if (outputsWrapper.size() > 1)
|
|
return Ptr<BackendNode>();
|
|
|
|
Ptr<tim::vx::Quantization> tvInputQuant = Ptr<tim::vx::Quantization>(
|
|
new tim::vx::Quantization(tim::vx::QuantType::ASYMMETRIC, input_sc, input_zp));
|
|
|
|
// input Tensor
|
|
auto inputWrapper = inputsWrapper[0].dynamicCast<TimVXBackendWrapper>();
|
|
Mat tmpInput = inputWrapper->getMat();
|
|
|
|
if (tmpInput.dims != 4) // Only support 4 dim input.
|
|
return Ptr<BackendNode>();
|
|
|
|
int input_index = -1, mean_index = -1, var_index = -1, gamma_index = -1, beta_index = -1, output_index = -1;
|
|
|
|
if (inputWrapper->isTensor())
|
|
{
|
|
input_index = tvGraph->getTensorIndex(inputWrapper->getTensor());
|
|
if (input_index == -1)
|
|
{
|
|
// Copy To New inputWrapper
|
|
Mat tmp = inputWrapper->getMat();
|
|
inputWrapper = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(tmp));
|
|
}
|
|
}
|
|
|
|
if (!inputWrapper->isTensor())
|
|
{
|
|
inputWrapper->createTensor(graph,tim::vx::TensorAttribute::INPUT, tvInputQuant);
|
|
input_index = tvGraph->addWrapper(inputWrapper);
|
|
}
|
|
inputsIndex.push_back(input_index);
|
|
|
|
// Mean tensor
|
|
Ptr<TimVXBackendWrapper> meanWrapper = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(tvMean));
|
|
Ptr<tim::vx::Quantization> meanQuant;
|
|
meanWrapper->createTensor(graph, tim::vx::TensorAttribute::CONSTANT);
|
|
mean_index = tvGraph->addWrapper(meanWrapper);
|
|
inputsIndex.push_back(mean_index);
|
|
|
|
// Var tensor
|
|
Ptr<TimVXBackendWrapper> varWrapper = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(tvVar));
|
|
varWrapper->createTensor(graph,tim::vx::TensorAttribute::CONSTANT);
|
|
var_index = tvGraph->addWrapper(varWrapper);
|
|
inputsIndex.push_back(var_index);
|
|
|
|
// Gamma tensor
|
|
Ptr<TimVXBackendWrapper> gammaWrapper = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(tvGamma));
|
|
gammaWrapper->createTensor(graph,tim::vx::TensorAttribute::CONSTANT);
|
|
gamma_index = tvGraph->addWrapper(gammaWrapper);
|
|
inputsIndex.push_back(gamma_index);
|
|
|
|
// Beta tensor
|
|
Ptr<TimVXBackendWrapper> betaWrapper = Ptr<TimVXBackendWrapper>(new TimVXBackendWrapper(tvBeta));
|
|
betaWrapper->createTensor(graph,tim::vx::TensorAttribute::CONSTANT);
|
|
beta_index = tvGraph->addWrapper(betaWrapper);
|
|
inputsIndex.push_back(beta_index);
|
|
|
|
// Output tensor
|
|
CV_Assert(outputsWrapper.size() == 1);
|
|
Ptr<TimVXBackendWrapper> outputWrapper = outputsWrapper[0].dynamicCast<TimVXBackendWrapper>();
|
|
Ptr<tim::vx::Quantization> outputQuant = Ptr<tim::vx::Quantization>(
|
|
new tim::vx::Quantization(tim::vx::QuantType::ASYMMETRIC, output_sc, output_zp));
|
|
|
|
if (isLast)
|
|
{
|
|
auto shapeType = getShapeTypeFromMat(outputWrapper->getMat());
|
|
|
|
// For Graph Output tensor, we need to set tensor shape before createTensor().
|
|
outputWrapper->setTensorShape(shapeType);
|
|
outputWrapper->createTensor(graph, tim::vx::TensorAttribute::OUTPUT, outputQuant);
|
|
}
|
|
else
|
|
{
|
|
outputWrapper->createTensor(graph, tim::vx::TensorAttribute::TRANSIENT, outputQuant);
|
|
}
|
|
|
|
output_index = tvGraph->addWrapper(outputWrapper);
|
|
outputsIndex.push_back(output_index);
|
|
|
|
std::shared_ptr<tim::vx::Operation> tvBatchNorm = graph->CreateOperation<tim::vx::ops::BatchNorm>(0.f);
|
|
|
|
Ptr<TimVXBackendNode> tvBackendNode = new TimVXBackendNode(tvGraph, tvBatchNorm, inputsIndex, outputsIndex);
|
|
|
|
return tvBackendNode;
|
|
#endif // HAVE_TIMVX
|
|
return Ptr<BackendNode>();
|
|
}
|
|
|
|
#ifdef HAVE_DNN_NGRAPH
|
|
virtual Ptr<BackendNode> initNgraph(const std::vector<Ptr<BackendWrapper> >& inputs, const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
|
{
|
|
auto input = nodes[0].dynamicCast<InfEngineNgraphNode>()->node;
|
|
|
|
input = ngraphDequantize(input, input_sc, input_zp);
|
|
|
|
std::vector<size_t> shape(input.get_shape().size(), 1);
|
|
shape[1] = origin_weights.total();
|
|
|
|
ov::Output<ov::Node> res;
|
|
auto ieWeights = std::make_shared<ov::op::v0::Constant>(ov::element::f32, shape, origin_weights.data);
|
|
auto ieBias = std::make_shared<ov::op::v0::Constant>(ov::element::f32, shape, origin_bias.data);
|
|
res = std::make_shared<ov::op::v1::Multiply>(input, ieWeights);
|
|
res = std::make_shared<ov::op::v1::Add>(res, ieBias);
|
|
|
|
res = ngraphQuantize(res, output_sc, output_zp);
|
|
return new InfEngineNgraphNode(res);
|
|
}
|
|
#endif // HAVE_DNN_NGRAPH
|
|
|
|
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());
|
|
|
|
std::vector<Mat> inputs, outputs;
|
|
inputs_arr.getMatVector(inputs);
|
|
outputs_arr.getMatVector(outputs);
|
|
|
|
CV_Assert(blobs.size() == 2);
|
|
CV_Assert(inputs.size() == 1);
|
|
|
|
Mat &inpBlob = inputs[0];
|
|
int planeSize = 1;
|
|
for (size_t i = 2; i < inpBlob.dims; i++) {
|
|
planeSize *= inpBlob.size[i];
|
|
}
|
|
|
|
for (size_t ii = 0; ii < outputs.size(); ii++)
|
|
{
|
|
Mat &outBlob = outputs[ii];
|
|
|
|
for(int num = 0; num < outBlob.size[0]; num++)
|
|
{
|
|
for (int n = 0; n < outBlob.size[1]; n++)
|
|
{
|
|
float w = weights_.at<float>(n);
|
|
float b = bias_.at<float>(n);
|
|
Mat inpBlobPlane(1, planeSize, CV_8S, inpBlob.ptr<int8_t>(num, n));
|
|
Mat outBlobPlane(1, planeSize, CV_8S, outBlob.ptr<int8_t>(num, n));
|
|
inpBlobPlane.convertTo(outBlobPlane, CV_8S, w, b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
|
const std::vector<MatShape> &outputs) const CV_OVERRIDE
|
|
{
|
|
CV_UNUSED(outputs); // suppress unused variable warning
|
|
|
|
int64 flops = 0;
|
|
for(int i = 0; i < inputs.size(); i++)
|
|
{
|
|
flops += 3*total(inputs[i]);
|
|
}
|
|
return flops;
|
|
}
|
|
|
|
private:
|
|
bool useGlobalStats;
|
|
};
|
|
|
|
Ptr<BatchNormLayerInt8> BatchNormLayerInt8::create(const LayerParams& params)
|
|
{
|
|
return Ptr<BatchNormLayerInt8>(new BatchNormLayerInt8Impl(params));
|
|
}
|
|
|
|
} // namespace dnn
|
|
} // namespace cv
|