nareshmlx/opencv-pr huggingface dataset:
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
Again, we assume that input `C` is skipped, but that might not be the case. And I think there should be "7" either way - the sixth tensor is just empty, no?
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
Do we support non-default zero point for W?
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter
**Review Line:** 3300

**Code Changes:**
```diff
+    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);
```

Review comment:
Yes, you're right. It will be fixed in the next update.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter
**Review Line:** 3296

**Code Changes:**
```diff
+    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))
+    {
```

Review comment:
That's a good idea. In the near future, we do not have a plan to load this type ONNX op (like QGemm with non-constant `inp_sc/inp_zp`). I will try to do it.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter
**Review Line:** 3300

**Code Changes:**
```diff
+    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);
```

Review comment:
Hi, I did some research. And currently, we can do the same with `parseGemm`. Because the majority of `gemm` op was generated by the FC layer. And we always think the `transA` and `alpha` are default values.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter
**Review Line:** 3291

**Code Changes:**
```diff
+// 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");
```

Review comment:
Because we can't get the corresponding attributes of the input data. Currently, I can only support 8 or 9 inputs.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
Currently, I can only support 8 or 9 inputs. If it has 9 inputs, there are two different cases: the sixth tensor is empty or not. If it has 8 inputs, we think the `C` is empty, and we get the `output_scale` at sixth tensor. This is not a complete solution, but it will handle most cases. The [example](https://github.com/opencv/opencv_extra/pull/989) has 9 inputs, and with empty tensor at the sixth tensor, so we can only get the `output_scale` from the seventh tensor by `getBlob()`.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
Yes, we do need to support this. However, adding the support of W non-0 requires modifying the forward code of the FC layer, which takes a certain amount of time. My plan is following: In this PR, we just focus on the Op parsing, and in next PR, we need to support the non-0 zero-point for W both in `QGemm`, `MatMul` and `QConv`.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter
**Review Line:** 3300

**Code Changes:**
```diff
+    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);
```

Review comment:
Then it might be better if we had an assertion for that.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
It's good to something working right away! My point was that we should have an assertion for cases that we do not support, so that it's not surprise for anyone
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
I think we should test the "else" case, because as far as I understand [this](https://github.com/onnx/onnx/blob/main/docs/IR.md#optional-inputs-and-outputs) paragraph, out_sc should still be the seventh blob. Either way, if we expect something to be missing - we should add an assertion for that. In case of 8 inputs I think we don't have `y_zero_point`, and in case of 9 it seems that we ignore it even if we have it.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
Agree, now I get your points.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
In my understanding, the current `QGemm` layer does not need to care about the `output_zeropoint`. The `input_zeropoint` of his consumer is equal to the output_zeropoint of `QGemm`. We can process it when we get `input_zeropoint` in the next layer. In `QConv`, `QMatMul` and all other quantization layers, we only set the `input_scale` and `input_zeropoint` when we create a new quantized layer.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
I don't see why would output zero point be so different from output scale - we care about the latter, but not the former?
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
It seems unreasonable to calculate the `outputMultiplier` in the parse stage, maybe we can move it to the initialization stage of every quantized node.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22306: DNN: Add qgemm and squeeze op13 supported on ONNXImporter

**Code Changes:**
```diff
@@ -192,6 +192,7 @@ class ONNXImporter
     void parseQSigmoid             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQAvgPool             (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseQConcat              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseQGemm                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // '???' domain or '???' layer type
     void parseCustomLayer          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2183,17 +2184,39 @@ void ONNXImporter::parseTranspose(LayerParams& layerParams, const opencv_onnx::N
 
```

Review comment:
That function seems very fragile. It relies on the names the producer decided, not the official spec. I think your PR can be merged, but we need to fix this function in the future.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
1. why such a big buffer for the whole image is allocated? 2. it's not deallocated, it's a memory leak. Please, do not allocate memory manually unless it's necessary. Use std::vector.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
is it necessary to call this function? You decode the image row by row after it in any case.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
the code is correct, but still quite difficult to read. Could you please use explicit `if (ret == SPNG_OK) { ... }` to make the code easier to read?
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
where is ctx released?
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
the API is unstable. You call close() after data is read. User may want, for some reason, call readData several times.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
I do not see where proper encoding to memory is implemented. SPngEncoder::write() only writes image to disk
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
spng_decode_row() requires the decoder to be initialized by calling spng_decode_image() with the SPNG_DECODE_PROGRESSIVE flag set. This function call does not decode image. It just sets internal flags. It is necessary.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
If if understand it correctly, there are related tests in OpenCV for that. It encodes to the buffer and compares with some Vector. All the test cases is passed. The answer to your question is in line 407, it checks if the encoder should write it to buffer or file. I copied that part from the libpng codec. ```cpp if( m_buf ) { spng_set_png_stream(ctx,(spng_rw_fn*)writeDataToBuf, this); } else { f = fopen( m_filename.c_str(), "wb" ); if( f ) spng_set_png_file( ctx, f ); } ``` Here spng_set_png_stream function call does that.
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
The following lines should be ```cpp if(!ctx) { spng_ctx_free(ctx); return false; } ``` I missed that. Will fix it! Done!
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** added
**Context:** PR #22226: [GSoC 2022] spng encoder/decoder added as optional png codec

**Code Changes:**
```diff
@@ -0,0 +1,754 @@
+// 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_SPNG
+
+/****************************************************************************************\
```

Review comment:
I don't see a reason why a user wants to read the same data twice. The current png decoder is implemented in this way too. After reading the data, the user can just copy it. Reference to the current PNG codec. ``` https://github.com/opencv/opencv/blob/4.x/modules/imgcodecs/src/grfmt_png.cpp#L307 ``` ```cpp Mat mat; decoder.readData(mat) Mat matOther; matOther = mat; ``` Users should favor that in case of performance. But I think you are right. If the user calls twice in a row, its behavior is undefined. We also should remove this from the current png codec implementation too.