nareshmlx/opencv-pr huggingface dataset:
**File:** modules/dnn/src/layers/attention_layer.cpp
**Change Type:** added
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 145

**Code Changes:**
```diff
+            const auto &input = inputs[0];
+            const auto &bias = inputs[2];
+            const auto *input_data = input.ptr<const float>();
+            const auto *bias_data = bias.ptr<const float>();
+
+            opt.multi_thread = false;
+            auto fn = [&](const Range &r) {
+                for (int i = r.start; i < r.end; i++) {
+                    const int batch_index = static_cast<int>((i / 3) / num_heads);
+                    const int head_index = static_cast<int>((i / 3) % num_heads);
+                    const int qkv_index = static_cast<int>(i % 3);
```

Review comment:
No, it is intended to do so. We have input data of shape `[B, S, D]` and weight `[D, W]`, where `W = qk_hidden_size + qk_hidden_size + v_hidden_size` (also `qk_hidden_size = qk_head_size(H) * num_heads(N)`, `v_hidden_size = v_head_size * num_heads`, and `D=W` in most vits I observe), then weight is extraced repactively (with shape `[D, NH]`) and prepacked to have shape `[N, D, H]`. We loop over dimension `B` and `N` in parallel, so it turns into Gemm with two tensors of shape `[S, D]` and `[D, H]` for each iteration. This inner Gemm needs to be run in single-thread way. Of course I know it is not the most efficient because usually `B=1` and `N` is also small (`N=12` in ViTs), while `S=197` and `D=768`. It can be further improved if time is enough before release.
**File:** modules/dnn/test/test_onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 2954

**Code Changes:**
```diff
     testONNXModels("test_expand_shape_model4", pb, 0, 0, false, true, 1);
 }
 
+TEST_P(Test_ONNX_layers, Attention) {
+    testONNXModels("attention");
+}
+TEST_P(Test_ONNX_layers, AttentionSingleHead) {
+    testONNXModels("attention_single_head");
+}
+
+TEST_P(Test_ONNX_nets, ViT_B_32) {
```

Review comment:
This test fails with disabled `AttentionSubGraph`. Is that expected?
**File:** modules/dnn/test/test_onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 2954

**Code Changes:**
```diff
     testONNXModels("test_expand_shape_model4", pb, 0, 0, false, true, 1);
 }
 
+TEST_P(Test_ONNX_layers, Attention) {
+    testONNXModels("attention");
+}
+TEST_P(Test_ONNX_layers, AttentionSingleHead) {
+    testONNXModels("attention_single_head");
+}
+
+TEST_P(Test_ONNX_nets, ViT_B_32) {
```

Review comment:
Benchmark `w/o Attention` is done with disabled `AttentionSubGraph`. It should work. What is the error? Accuracy?
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 762

**Code Changes:**
```diff
+        randu(x, 0.f, 1.f);
+        randu(weight, 0.f, 1.f);
+        randu(bias, 0.f, 1.f);
+
+        LayerParams lp;
+        lp.type = "Attention";
+        lp.name = "testLayer";
+        lp.set("num_heads", num_heads);
+        lp.set("qkv_hidden_sizes", DictValue::arrayInt(qkv_hidden_sizes.data(), qkv_hidden_sizes.size()));
+
+        Net net;
```

Review comment:
How the numbers from https://github.com/opencv/opencv/pull/24476#issue-1971965496 can be reproduced? I mean does it make sense to run test on an ONNX model to catch the fusion too?
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 762

**Code Changes:**
```diff
+        randu(x, 0.f, 1.f);
+        randu(weight, 0.f, 1.f);
+        randu(bias, 0.f, 1.f);
+
+        LayerParams lp;
+        lp.type = "Attention";
+        lp.name = "testLayer";
+        lp.set("num_heads", num_heads);
+        lp.set("qkv_hidden_sizes", DictValue::arrayInt(qkv_hidden_sizes.data(), qkv_hidden_sizes.size()));
+
+        Net net;
```

Review comment:
> I mean does it make sense to run test on an ONNX model to catch the fusion too? Yes, definitely. I will add a test case for attention fusion in `test_graph_simplifier`. > How the numbers from https://github.com/opencv/opencv/pull/24476#issue-1971965496 can be reproduced? For `w/o Attention`, I use the onnx model and test it in `perf_layer.cpp` **with `AttentionSubgraph` disabled**: ```c++ struct Layer_Attention_expanded : public TestBaseWithParam<tuple<Backend, Target>> { void test_layer(const std::vector<int> x_shape, const std::vector<int> qkv_hidden_sizes, const int num_heads) { int backendId = get<0>(GetParam()); int targetId = get<1>(GetParam()); Mat x(x_shape, CV_32F); randu(x, 0.f, 1.f); auto net = readNet("/path/to/attention.onnx"); { net.setInput(x); net.setPreferableBackend(backendId); net.setPreferableTarget(targetId); Mat out = net.forward(); } TEST_CYCLE() { Mat out = net.forward(); } SANITY_CHECK_NOTHING(); } }; PERF_TEST_P_(Layer_Attention_expanded, VisionTransformer) { test_layer({1, 197, 768}, {768, 768, 768}, 12); } INSTANTIATE_TEST_CASE_P(/**/, Layer_Attention_expanded, testing::Values(std::make_tuple(DNN_BACKEND_OPENCV, DNN_TARGET_CPU))); ``` This code is not pushed. Also python script to run benchmark with ORT: ```python import time import numpy as np import onnxruntime as ort np.random.seed(0) x = np.random.rand(1, 197, 768).astype(np.float32) net = ort.InferenceSession("./attention_subgraph.onnx", providers=["CPUExecutionProvider"]) # warmup net.run([], {"x": x}) # benchmark all_times = [] for _ in range(100): start = time.time() net.run([], {"x": x}) end = time.time() all_times.append(1000 * (end - start)) all_times = sorted(all_times) mean = sum(all_times) / len(all_times) median = (all_times[49] + all_times[50]) / 2 minimum = min(all_times) print("mean={:.2f}, median={:.2f}, min={:.2f}".format(mean, median, minimum)) ```
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 781

**Code Changes:**
```diff
+            net.setInput(x, input_names[0]);
+            net.setInput(weight, input_names[1]);
+            net.setInput(bias, input_names[2]);
+
+            net.setPreferableBackend(backendId);
+            net.setPreferableTarget(targetId);
+            Mat out = net.forward();
+        }
+
+        TEST_CYCLE()
+        {
```

Review comment:
Current numbers a bit confusing. Let's wait for https://github.com/opencv/opencv/pull/24476#discussion_r1404041757 because it fails. input shape: `[1, 320, 48]` model from https://github.com/opencv/opencv_extra/pull/1128 CPU: 12th Gen Intel(R) Core(TM) i9-12900 | backend | 4.x | PR | |---|---|---| | OpenCV, CPU | 18.46ms | 11.90ms (x0.64) | | OpenVINO, CPU | 0.25ms | 11.91ms |
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 781

**Code Changes:**
```diff
+            net.setInput(x, input_names[0]);
+            net.setInput(weight, input_names[1]);
+            net.setInput(bias, input_names[2]);
+
+            net.setPreferableBackend(backendId);
+            net.setPreferableTarget(targetId);
+            Mat out = net.forward();
+        }
+
+        TEST_CYCLE()
+        {
```

Review comment:
Well, the acc test have a reduced scale so that it does not take a lot time to run. For benchmark, I use [1, 197, 768] (I have two `attention.onnx` models, one with input [1, 197, 768], the other one [1, 320, 48]). --- For OpenVINO, since we do not have backend-specific graph fusion, we can recreate this subgraph in `initNgraph`.
**File:** modules/dnn/test/test_onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 2954

**Code Changes:**
```diff
     testONNXModels("test_expand_shape_model4", pb, 0, 0, false, true, 1);
 }
 
+TEST_P(Test_ONNX_layers, Attention) {
+    testONNXModels("attention");
+}
+TEST_P(Test_ONNX_layers, AttentionSingleHead) {
+    testONNXModels("attention_single_head");
+}
+
+TEST_P(Test_ONNX_nets, ViT_B_32) {
```

Review comment:
It's because onnx operator define `opset<=11` default axis is 1. Some old tests haven't opset value, so I use default 1 if no opset.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
Should it be removed?
**File:** modules/dnn/perf/perf_net.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -93,7 +93,6 @@ class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple<Backend, Target>
     }
 };
 
-
 PERF_TEST_P_(DNNTestNetwork, AlexNet)
 {
     processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt",
@@ -391,17 +390,16 @@ PERF_TEST_P_(DNNTestNetwork, CRNN) {
     processNet("", "dnn/text_recognition_CRNN_EN_2021sep.onnx", "", inp);
```

Review comment:
Please remove the results from source code - it's enough to add to PR's description
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 781

**Code Changes:**
```diff
+            net.setInput(x, input_names[0]);
+            net.setInput(weight, input_names[1]);
+            net.setInput(bias, input_names[2]);
+
+            net.setPreferableBackend(backendId);
+            net.setPreferableTarget(targetId);
+            Mat out = net.forward();
+        }
+
+        TEST_CYCLE()
+        {
```

Review comment:
Updated results. Please ignore the table above: input: 1x197x768 CPU: 12th Gen Intel(R) Core(TM) i9-12900 | backend | 4.x | PR | |---|---|---| | OpenCV, CPU | 16.93ms | 2.37ms (x7.14) | | OpenVINO, CPU | 1.54ms | 2.33ms (x0.66) | So there is a degradation in OpenVINO performance in case of fallback to OpenCV layer.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
no longer valid.
**File:** modules/dnn/perf/perf_net.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -93,7 +93,6 @@ class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple<Backend, Target>
     }
 };
 
-
 PERF_TEST_P_(DNNTestNetwork, AlexNet)
 {
     processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt",
@@ -391,17 +390,16 @@ PERF_TEST_P_(DNNTestNetwork, CRNN) {
     processNet("", "dnn/text_recognition_CRNN_EN_2021sep.onnx", "", inp);
```

Review comment:
Running this models in `perf_net.cpp` is taking up too much time for now. How about we comment `PERF_TEST_P_(DNNTestNetwork, VIT_B_16)` and such for now until we get the close or even better inference speed as ORT? In this case, these perfamce results should be deleted and kept in the first comment of this PR.
**File:** modules/dnn/perf/perf_net.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -93,7 +93,6 @@ class DNNTestNetwork : public ::perf::TestBaseWithParam< tuple<Backend, Target>
     }
 };
 
-
 PERF_TEST_P_(DNNTestNetwork, AlexNet)
 {
     processNet("dnn/bvlc_alexnet.caffemodel", "dnn/bvlc_alexnet.prototxt",
@@ -391,17 +390,16 @@ PERF_TEST_P_(DNNTestNetwork, CRNN) {
     processNet("", "dnn/text_recognition_CRNN_EN_2021sep.onnx", "", inp);
```

Review comment:
You may add a corresponding skip exception: ```cpp applyTestTag(CV_TEST_TAG_LONG, CV_TEST_TAG_DEBUG_LONG); ```
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
If https://github.com/opencv/opencv/pull/24672 is good to merge, the following change would be required: ```patch --- a/modules/dnn/src/onnx/onnx_graph_simplifier.cpp +++ b/modules/dnn/src/onnx/onnx_graph_simplifier.cpp @@ -313,18 +313,30 @@ class AttentionSubGraph : public Subgraph { att_add = addNodeToMatch("Add", addNodeToMatch(""), att_matmul); // v_path - slice_v = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_v = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int reshape_v = addNodeToMatch("Reshape", slice_v, addNodeToMatch("")); int transpose_v = addNodeToMatch("Transpose", reshape_v); // q_path - slice_q = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_q = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); reshape_q = addNodeToMatch("Reshape", slice_q, addNodeToMatch("")); int transpose_q = addNodeToMatch("Transpose", reshape_q); div_q = addNodeToMatch("Div", transpose_q, addNodeToMatch("")); // k_path - slice_k = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_k = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int reshape_k = addNodeToMatch("Reshape", slice_k, addNodeToMatch("")); int transpose_k = addNodeToMatch("Transpose", reshape_k); @@ -422,16 +434,28 @@ class AttentionSingleHeadSubGraph : public Subgraph { att_add = addNodeToMatch("Add", addNodeToMatch(""), att_matmul); // v_path - slice_v = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_v = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int transpose_v = addNodeToMatch("Transpose", slice_v); // q_path - slice_q = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_q = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int transpose_q = addNodeToMatch("Transpose", slice_q); div_q = addNodeToMatch("Div", transpose_q, addNodeToMatch("")); // k_path - slice_k = addNodeToMatch("Slice", att_add, addNodeToMatch(""), addNodeToMatch("")); + slice_k = addNodeToMatch("Slice", {att_add, + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch(""), + addNodeToMatch("")}); int transpose_k = addNodeToMatch("Transpose", slice_k); // qk ```
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
Why not compare `OPENCV_DNN_BACKEND_DEFAULT` with `DNN_BACKEND_INFERENCE_ENGINE` in runtime?
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
I actually want to shut it down when it is not DNN_BACKEND_OPENCV.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
This should be the same as value if default backend is defined during compilation. Adding one more compile definition seems extra.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
`OPENCV_DNN_BACKEND_DEFAULT` could be undef during compilation, so cannot always compare `OPENCV_DNN_BACKEND_DEFAULT` with `DNN_BACKEND_INFERENCE_ENGINE` in runtime.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
I guess it should be `if (getParam_DNN_BACKEND_DEFAULT()== DNN_BACKEND_OPENCV)` instead right?
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
Tests about Attention fusion in graph simplifier cannot be in sync with this.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
This is because https://github.com/opencv/opencv/pull/24672 has been merged but https://github.com/opencv/opencv/pull/24476#discussion_r1421301615 not applied. Can you do so?
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
Yes, but also I can say that `PR:4.x / Ubuntu2004-x64-OpenVINO / BuildAndTest` will still fail to pass because the attention subgraph fusion is turned off for openvino but the tests of graph simplifier do not.
**File:** modules/dnn/src/onnx/onnx_graph_simplifier.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer

**Code Changes:**
```diff
@@ -13,6 +13,7 @@
 
 #include <opencv2/core/utils/logger.hpp>
 #include <queue>
+#include <limits>
 
 namespace cv { namespace dnn {
 CV__DNN_INLINE_NS_BEGIN
@@ -181,6 +182,17 @@ static Mat extractConstant(const Ptr<ImportGraphWrapper>& net, int node_id, int
     }
```

Review comment:
If only OpenVINO is configured as a default backend in CMake flags. On our CI there is no such option: https://github.com/opencv/ci-gha-workflow/blob/49062b85612df5203c48735fdb107ffeff4cd800/.github/workflows/OCV-PR-4.x-U20-OpenVINO.yaml#L16. So test should pass.
**File:** modules/dnn/test/test_onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24476: dnn: add attention layer
**Review Line:** 2954

**Code Changes:**
```diff
     testONNXModels("test_expand_shape_model4", pb, 0, 0, false, true, 1);
 }
 
+TEST_P(Test_ONNX_layers, Attention) {
+    testONNXModels("attention");
+}
+TEST_P(Test_ONNX_layers, AttentionSingleHead) {
+    testONNXModels("attention_single_head");
+}
+
+TEST_P(Test_ONNX_nets, ViT_B_32) {
```

Review comment:
Solved in PR: https://github.com/opencv/opencv/pull/24613
**File:** modules/imgcodecs/src/grfmt_spng.cpp
**Change Type:** modified
**Context:** PR #24716: SPNG integration warning fixes on Windows

**Code Changes:**
```diff
@@ -521,7 +521,6 @@ int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t len
 
 bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
 {
-    int fmt;
     spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER);
     FILE *volatile f = 0;
     int width = img.cols, height = img.rows;
@@ -558,11 +557,10 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
                 isBilevel = params[i + 1] != 0;
```

Review comment:
`cv::saturate_cast`?
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24694: dnn: refactor ONNX MatMul with fastGemm
**Review Line:** 1964

**Code Changes:**
```diff
-        firstInpDims = blob.dims;
-        LayerParams constParams;
-        constParams.name = layerParams.name + "/const_0";
-        constParams.type = "Const";
-        constParams.blobs.push_back(blob);
+    for (int i = 0; i < node_proto.input_size(); i++) {
+        if (constBlobs.find(node_proto.input(i)) == constBlobs.end()) {
+            continue;
+        }
+
+        Mat blob = getBlob(node_proto, i);
```

Review comment:
Why we switch to a new MatMul instead of [GEMM](https://github.com/opencv/opencv/blob/4.x/modules/dnn/src/layers/gemm_layer.cpp)?
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #24694: dnn: refactor ONNX MatMul with fastGemm
**Review Line:** 1964

**Code Changes:**
```diff
-        firstInpDims = blob.dims;
-        LayerParams constParams;
-        constParams.name = layerParams.name + "/const_0";
-        constParams.type = "Const";
-        constParams.blobs.push_back(blob);
+    for (int i = 0; i < node_proto.input_size(); i++) {
+        if (constBlobs.find(node_proto.input(i)) == constBlobs.end()) {
+            continue;
+        }
+
+        Mat blob = getBlob(node_proto, i);
```

Review comment:
1. Gemm in dnn handles: 2d x 2d = 2d or 3d (batch) x 2d = 2d (force). MatMul in this PR handles: nd x nd = nd. 2. Broadcasting is handled carefully here in MatMul via `MatMulHelper` so that we don't do physical broadcast.
**File:** modules/dnn/src/layers/cpu_kernels/fast_gemm_kernels.default.hpp
**Change Type:** modified
**Context:** PR #24694: dnn: refactor ONNX MatMul with fastGemm
**Review Line:** 329

**Code Changes:**
```diff
+    int m_tiles = (M + MC - 1) / MC;
+    int n_tiles = (N + NC - 1) / NC;
+    int total_tiles = m_tiles * n_tiles;
+
+    auto fn = [&](const Range &r) {
+        char* packed_a = (char*)(use_stackbuff ? alloca(buff_size) : malloc(buff_size));
+        char* packed_b = packed_a + KC * MC * esz;
+        int start = r.start;
+        int end = r.end;
+
+        for (int tile_idx = start; tile_idx < end; tile_idx++) {
```

Review comment:
OpenCV AutoBuffer makes sense here: https://docs.opencv.org/4.x/d8/dd0/classcv_1_1AutoBuffer.html. No problems with memory leaks and it has built-in logic for alloca.