+// A * B + C = Y, we require that the dimension of A is [m, k], and the dimension of B is [n, k].
+// And the dim of output Y is [m, n]
+void ONNXImporter::parseQGemm(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
+{
+ int ninputs = node_proto.input_size();
+ CV_Assert(ninputs == 8 || ninputs == 9);
+
+ layerParams.type = "InnerProductInt8";
+
+ if (constBlobs.find(node_proto.input(3)) == constBlobs.end())
+ CV_Error(Error::StsNotImplemented, "Variable weights is not supported");
+
+ Mat weights = getBlob(node_proto, 3);
+
+ if (!layerParams.get<int>("transB", 0))
+ {
+ transpose(weights, weights);
+ }
+
+ CV_Assert(layerParams.get<float>("alpha", 1) == 1.0f);
+ CV_Assert(layerParams.get<int>("transA", 0) == 0);
+
+ int firstInpDims = outShapes[node_proto.input(0)].size();
+
+ Mat inp_sc = getBlob(node_proto, 1);
+ Mat inp_zp = getBlob(node_proto, 2);
+
+ int outCn = weights.size[0];
+ int secondInpDims = weights.dims;
+
+ Mat w_scale = getBlob(node_proto, 4);
+ CV_Assert(w_scale.total() == 1 || w_scale.total() == outCn);
+ bool per_channel = w_scale.total() == outCn;
+ Mat wt_sc = (w_scale.total() == outCn) ? w_scale : Mat(1, outCn, CV_32F, Scalar(w_scale.at<float>(0)));
+
+ Mat w_zp = getBlob(node_proto, 5);
+ int8_t* ptrZp = w_zp.ptr<int8_t>(0);
+
+ for (int i = 0; i < w_zp.total(); i++)
+ {
+ if (ptrZp[i] != (int8_t)0)
+ CV_Error(Error::StsUnsupportedFormat, "The zero-point non-zero case of W is not supported!");
+ }
+
+ Mat out_sc, bias;
+ out_sc = getBlob(node_proto, 7);
+ if (constBlobs.find(node_proto.input(6)) != constBlobs.end())
+ bias = getBlob(node_proto, 6);
+ else
+ bias = Mat::zeros(1, outCn, CV_32S);
+
+ Mat biasFused(1, outCn, CV_32S);
+ Mat outputMultiplier(1, outCn, CV_32F);
+ for (int i = 0; i < outCn; i++)
+ {
+ biasFused.at<int>(i) = bias.at<int>(i) - inp_zp.at<int8_t>(0)*(cv::sum(weights.row(i))[0]);
+ outputMultiplier.at<float>(i) = (inp_sc.at<float>(0) * wt_sc.at<float>(i)) / out_sc.at<float>(0);
+ }
+
+ layerParams.type = "InnerProductInt8";
+ layerParams.set("num_output", outCn);
+ layerParams.set("axis", firstInpDims - secondInpDims + 1);
+ layerParams.set("input_scale", inp_sc.at<float>(0));
+ layerParams.set("input_zeropoint", inp_zp.at<int8_t>(0));
+ layerParams.set("per_channel", per_channel);
+
+ layerParams.blobs.push_back(weights);
+ layerParams.blobs.push_back(biasFused);
+ layerParams.blobs.push_back(outputMultiplier);
+ addLayer(layerParams, node_proto);
+}
+