mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 15:23:05 +04:00
Merge pull request #28966 from abhishek-gola:dequantized_issue_fix
Fixed dequantizelinear slicing bug #28966 closes: https://github.com/opencv/opencv/issues/25999 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -138,6 +138,7 @@ class QuantizeLayerImpl CV_FINAL : public QuantizeLayer
|
||||
public:
|
||||
int axis;
|
||||
int block_size;
|
||||
int outDepth; // CV_8S (legacy default) or CV_8U; driven by ONNX y_zero_point dtype
|
||||
bool is1D;
|
||||
Mat scalesMat, zeropointsMat; // Saving the broadcasted scales data.
|
||||
bool quantParamExternal = true; // Indicates if the quantization parameters (scale and zero point) are provided as inputs to the node.
|
||||
@@ -147,6 +148,7 @@ public:
|
||||
is1D = params.get<bool>("is1D", false);
|
||||
axis = params.get<int>("axis", 1);
|
||||
block_size = params.get<int>("block_size", 0);
|
||||
outDepth = params.get<int>("depth", CV_8S);
|
||||
|
||||
if (!is1D)
|
||||
{
|
||||
@@ -196,7 +198,7 @@ public:
|
||||
std::vector<MatType>& outputs,
|
||||
std::vector<MatType>& internals) const CV_OVERRIDE
|
||||
{
|
||||
outputs.assign(requiredOutputs, CV_8S);
|
||||
outputs.assign(requiredOutputs, outDepth);
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +231,7 @@ public:
|
||||
inputs[0] = inputFp32; // replace
|
||||
}
|
||||
|
||||
inputs[0].convertTo(outputs[0], CV_8S, 1.f/scales[0], zeropoints[0]);
|
||||
inputs[0].convertTo(outputs[0], outDepth, 1.f/scales[0], zeropoints[0]);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -263,8 +265,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (outputs[0].depth() != CV_8S)
|
||||
outputs[0].convertTo(outputs[0], CV_8S);
|
||||
if (outputs[0].depth() != outDepth)
|
||||
outputs[0].convertTo(outputs[0], outDepth);
|
||||
}
|
||||
|
||||
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr, OutputArrayOfArrays internals_arr) CV_OVERRIDE
|
||||
@@ -287,10 +289,10 @@ public:
|
||||
divide(inputs[0], scalesMat, inputTmp);
|
||||
add(inputTmp, zeropointsMat, inputTmp);
|
||||
|
||||
inputTmp.convertTo(outputs[0], CV_8S);
|
||||
inputTmp.convertTo(outputs[0], outDepth);
|
||||
}
|
||||
else
|
||||
inputs[0].convertTo(outputs[0], CV_8S, 1.f/scales[0], zeropoints[0]);
|
||||
inputs[0].convertTo(outputs[0], outDepth, 1.f/scales[0], zeropoints[0]);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNN_NGRAPH
|
||||
|
||||
@@ -100,7 +100,26 @@ static void dequantizeLinear(const _InpTp* inp_, const _ScaleTp* scale_,
|
||||
const _ScaleTp* sc = scale_ + scale_ofs;
|
||||
_OutTp* out = out_ + block_ofs;
|
||||
|
||||
if (slice_size > 1) {
|
||||
if (slice_size > 1 && block_size > 0) {
|
||||
// Blocked mode: each of `delta` blocks has `block_size` rows along the
|
||||
// axis (clipped at sz_a), and within each row the scale/zp vary across
|
||||
// the `slice_size` trailing positions. The same `slice_size` scales/zps
|
||||
// are reused for every row inside one block.
|
||||
for (int k = 0; k < delta; k++) {
|
||||
int rows = std::min(block_size, sz_a - (block_idx + k)*block_size);
|
||||
for (int b = 0; b < rows; b++) {
|
||||
for (int64_t j = 0; j < slice_size; j++) {
|
||||
float scval = (float)sc[j];
|
||||
_InpTp zpval = zp ? zp[j] : (_InpTp)0;
|
||||
out[j] = _OutTp((inp[j] - zpval)*scval);
|
||||
}
|
||||
inp += slice_size;
|
||||
out += slice_size;
|
||||
}
|
||||
sc += scale_step;
|
||||
if (zp) zp += zp_step;
|
||||
}
|
||||
} else if (slice_size > 1) {
|
||||
for (int k = 0; k < delta; k++, inp += slice_size, out += slice_size,
|
||||
sc += scale_step, zp += zp_step) {
|
||||
float scval = (float)*sc;
|
||||
|
||||
@@ -64,6 +64,21 @@ static T getScalarFromMat(Mat m)
|
||||
return m.at<T>(0);
|
||||
}
|
||||
|
||||
// Read scalar zero-point from a Mat of any supported integer depth.
|
||||
// `unshiftFromInt8` undoes the -128 offset that populateNet() applies when
|
||||
// rewriting UINT8 initializers as INT8.
|
||||
static int readZpScalar(const Mat& m, int i, bool unshiftFromInt8 = false)
|
||||
{
|
||||
switch (m.depth()) {
|
||||
case CV_8U: return (int)m.at<uint8_t>(i);
|
||||
case CV_8S: return (int)m.at<int8_t>(i) + (unshiftFromInt8 ? 128 : 0);
|
||||
case CV_16U: return (int)m.at<uint16_t>(i);
|
||||
case CV_16S: return (int)m.at<int16_t>(i);
|
||||
case CV_32S: return m.at<int>(i);
|
||||
default: CV_Error(Error::StsNotImplemented, "Unsupported zero_point depth");
|
||||
}
|
||||
}
|
||||
|
||||
static int onnxDataTypeToCvDepth(int onnxType)
|
||||
{
|
||||
switch (onnxType)
|
||||
@@ -98,7 +113,9 @@ class ONNXImporter
|
||||
|
||||
struct TensorInfo {
|
||||
int real_ndims;
|
||||
TensorInfo(int _real_ndims = 0) : real_ndims(_real_ndims) {}
|
||||
int onnx_dtype;
|
||||
TensorInfo(int _real_ndims = 0, int _onnx_dtype = 0)
|
||||
: real_ndims(_real_ndims), onnx_dtype(_onnx_dtype) {}
|
||||
};
|
||||
|
||||
std::map<std::string, Mat> getGraphTensors(
|
||||
@@ -446,7 +463,7 @@ std::map<std::string, Mat> ONNXImporter::getGraphTensors(
|
||||
continue;
|
||||
|
||||
layers_weights.insert(std::make_pair(tensor_proto.name(), mat));
|
||||
constBlobsExtraInfo.insert(std::make_pair(tensor_proto.name(), TensorInfo(tensor_proto.dims_size())));
|
||||
constBlobsExtraInfo.insert(std::make_pair(tensor_proto.name(), TensorInfo(tensor_proto.dims_size(), tensor_proto.data_type())));
|
||||
}
|
||||
return layers_weights;
|
||||
}
|
||||
@@ -867,6 +884,8 @@ void ONNXImporter::populateNet()
|
||||
const opencv_onnx::TypeProto::Tensor& tensor = typeProto.tensor_type();
|
||||
CV_Assert(tensor.has_shape());
|
||||
const opencv_onnx::TensorShapeProto& tensorShape = tensor.shape();
|
||||
if (constBlobsExtraInfo.find(name) == constBlobsExtraInfo.end())
|
||||
constBlobsExtraInfo.insert(std::make_pair(name, TensorInfo(tensor.shape().dim_size(), tensor.elem_type())));
|
||||
|
||||
int dim_size = tensorShape.dim_size();
|
||||
CV_CheckGE(dim_size, 0, ""); // some inputs are scalars (dims=0), e.g. in Test_ONNX_nets.Resnet34_kinetics test
|
||||
@@ -3411,13 +3430,22 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx
|
||||
// or 1-D tensor (per-channel quantized).
|
||||
bool is1D = false;
|
||||
|
||||
if (layerParams.type == "Quantize")
|
||||
layerParams.set("depth", CV_8S);
|
||||
else // Dequantize
|
||||
layerParams.set("depth", CV_32F);
|
||||
int outDepth = (layerParams.type == "Quantize") ? CV_8U : CV_32F;
|
||||
int zpOnnxDtype = 0;
|
||||
if (node_proto.input_size() == 3)
|
||||
{
|
||||
auto it = constBlobsExtraInfo.find(node_proto.input(2));
|
||||
if (it != constBlobsExtraInfo.end())
|
||||
zpOnnxDtype = it->second.onnx_dtype;
|
||||
}
|
||||
|
||||
// If scale is not defined as a constant blob, it is considered an external input.
|
||||
if(constBlobs.find(node_proto.input(1)) == constBlobs.end()){
|
||||
if (layerParams.type == "Quantize")
|
||||
{
|
||||
layerParams.set("depth", zpOnnxDtype == 0 ? CV_8U : onnxDataTypeToCvDepth(zpOnnxDtype));
|
||||
}
|
||||
else
|
||||
layerParams.set("depth", outDepth);
|
||||
addLayer(layerParams, node_proto);
|
||||
return;
|
||||
}
|
||||
@@ -3431,6 +3459,9 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx
|
||||
zpMat = getBlob(node_proto, 2);
|
||||
CV_Assert(zpMat.total() == scaleMat.total()); // zero point should has the same shape as scale.
|
||||
}
|
||||
if (layerParams.type == "Quantize")
|
||||
outDepth = CV_8S;
|
||||
layerParams.set("depth", outDepth);
|
||||
|
||||
if (is1D)
|
||||
{
|
||||
@@ -3443,8 +3474,7 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx
|
||||
{
|
||||
scales[i] = scaleMat.at<float>(i);
|
||||
if (!zpMat.empty())
|
||||
zeropoints[i] = zpMat.depth() == CV_32S ?
|
||||
zpMat.at<int>(i) : (int)zpMat.at<int8_t>(i);
|
||||
zeropoints[i] = readZpScalar(zpMat, i);
|
||||
}
|
||||
|
||||
layerParams.set("is1D", true);
|
||||
@@ -3454,8 +3484,7 @@ void ONNXImporter::parseQuantDequant(LayerParams& layerParams, const opencv_onnx
|
||||
}
|
||||
else
|
||||
{
|
||||
int zeropoint = zpMat.empty() ? 0 : zpMat.depth() == CV_32S ?
|
||||
getScalarFromMat<int>(zpMat) : (int)getScalarFromMat<int8_t>(zpMat);
|
||||
int zeropoint = zpMat.empty() ? 0 : readZpScalar(zpMat, 0);
|
||||
float scale = getScalarFromMat<float>(scaleMat);
|
||||
|
||||
layerParams.set("is1D", false);
|
||||
|
||||
@@ -1711,10 +1711,8 @@ public:
|
||||
|
||||
static std::set<std::string> parser_deny_list;
|
||||
static std::set<std::string> global_deny_list;
|
||||
static std::set<std::string> opencv_deny_list;
|
||||
static std::set<std::string> opencl_fp16_deny_list;
|
||||
static std::set<std::string> opencl_deny_list;
|
||||
static std::set<std::string> cpu_deny_list;
|
||||
static std::set<std::string> classic_deny_list;
|
||||
#ifdef HAVE_HALIDE
|
||||
static std::set<std::string> halide_deny_list;
|
||||
@@ -1778,10 +1776,6 @@ public:
|
||||
#include "test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp"
|
||||
};
|
||||
|
||||
opencv_deny_list = {
|
||||
#include "test_onnx_conformance_layer_filter_opencv_denylist.inl.hpp"
|
||||
};
|
||||
|
||||
opencl_fp16_deny_list = {
|
||||
#include "test_onnx_conformance_layer_filter_opencv_ocl_fp16_denylist.inl.hpp"
|
||||
};
|
||||
@@ -1790,10 +1784,6 @@ public:
|
||||
#include "test_onnx_conformance_layer_filter_opencv_ocl_fp32_denylist.inl.hpp"
|
||||
};
|
||||
|
||||
cpu_deny_list = {
|
||||
#include "test_onnx_conformance_layer_filter_opencv_cpu_denylist.inl.hpp"
|
||||
};
|
||||
|
||||
EngineType engine_forced =
|
||||
(EngineType)utils::getConfigurationParameterSizeT(
|
||||
"OPENCV_FORCE_DNN_ENGINE", ENGINE_AUTO);
|
||||
@@ -1832,10 +1822,8 @@ public:
|
||||
|
||||
std::set<std::string> Test_ONNX_conformance::parser_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::global_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::opencv_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::opencl_fp16_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::opencl_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::cpu_deny_list;
|
||||
std::set<std::string> Test_ONNX_conformance::classic_deny_list;
|
||||
#ifdef HAVE_HALIDE
|
||||
std::set<std::string> Test_ONNX_conformance::halide_deny_list;
|
||||
@@ -1876,10 +1864,6 @@ TEST_P(Test_ONNX_conformance, Layer_Test)
|
||||
|
||||
if (backend == DNN_BACKEND_OPENCV)
|
||||
{
|
||||
if (opencv_deny_list.find(name) != opencv_deny_list.end())
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCV_BACKEND, CV_TEST_TAG_DNN_SKIP_ONNX_CONFORMANCE);
|
||||
}
|
||||
if ((target == DNN_TARGET_OPENCL_FP16) && (opencl_fp16_deny_list.find(name) != opencl_fp16_deny_list.end()))
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL_FP16, CV_TEST_TAG_DNN_SKIP_OPENCV_BACKEND, CV_TEST_TAG_DNN_SKIP_ONNX_CONFORMANCE);
|
||||
@@ -1888,10 +1872,6 @@ TEST_P(Test_ONNX_conformance, Layer_Test)
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL, CV_TEST_TAG_DNN_SKIP_OPENCV_BACKEND, CV_TEST_TAG_DNN_SKIP_ONNX_CONFORMANCE);
|
||||
}
|
||||
if ((target == DNN_TARGET_CPU) && (cpu_deny_list.find(name) != cpu_deny_list.end()))
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_CPU, CV_TEST_TAG_DNN_SKIP_OPENCV_BACKEND, CV_TEST_TAG_DNN_SKIP_ONNX_CONFORMANCE);
|
||||
}
|
||||
if (name == "test_roialign_aligned_false" || name == "test_roialign_aligned_true")
|
||||
{
|
||||
default_l1 = std::max(default_l1, 3e-5);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
"test_dequantizelinear_blocked", // Issue https://github.com/opencv/opencv/issues/25999
|
||||
"test_quantizelinear", // Issue https://github.com/opencv/opencv/issues/25999
|
||||
"test_quantizelinear_axis", // Issue https://github.com/opencv/opencv/issues/25999
|
||||
"test_quantizelinear_blocked", // Issue https://github.com/opencv/opencv/issues/25999
|
||||
Reference in New Issue
Block a user