nareshmlx/opencv-pr huggingface dataset:
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
> if we have current indices_offset, we can get input_offset by replacing the 'axis' dimension in indices_offset IIUC, It is true if shape(input) === shape(indices). However, it is allowed to have mismatched shapes like the following: ```python data = np.array([[1.0, 2.0, 3.0, 4.0, 5.0]], dtype=np.float32) # (1, 5) indices = np.array([[1, 3]], dtype=np.int64) # (1, 2) updates = np.array([[1.1, 2.1]], dtype=np.float32) # (1, 2) y = scatter_elements(data, indices, updates, axis) # print(y) produces # [[1.0, 1.1, 3.0, 2.1, 5.0]] ``` In this case, I am not sure whether getting rid of loops can still work.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
The extra dimensions of indices are used to index indices (to index output) and updates. Do you mean flattening extra dimensions of indices. If yes, the extra dimensions of both indices and updates need to be flattened.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
If no-op rd is special-cased, I think this for loop needs to be moved into those lambda expressions (rd).
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
1. It might be a call to `total()` inside each iteration of for loop - let's try to cache it and see if it improves. 2. As for signature, it can be reduced to `void forward_impl(const Functor& rd, const Mat& input, const Mat& indices, const Mat& updates, Mat& out)` 3. What hardware are you testing on? My AMD Ryzen 5 5600H manages to run in 800ms(!), not 200ms. Update: I see your edit about M1. I still think it's a little bit too much, isn't it?
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Oh, yes, you're right, sorry for misleading.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -181,6 +181,7 @@ class ONNXImporter
     void parseElementWise          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseDepthToSpace         (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseRange                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseScatter              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseSimpleLayers         (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // Domain: com.microsoft
@@ -3106,6 +3107,58 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP
     constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1)));
```

Review comment:
If you look at other layer handlers, it typically implemented inside one handler with `if node_proto.op_type() == ... {layerParams.type = ...; } else {}`
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Yes, do you think that's possible?
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Cached `total()` and it did improve a lot, but still a little slower (+5ms w/o rd, +8ms w rd) than the initial version. Also tried to restore current_indices with: ```cpp current_indices[ndims - 1] += 1; for (int j = ndims - 1; j > 0; j--) { current_indices[j - 1] += int(current_indices[j] / ind_shape[j]); current_indices[j] %= ind_shape[j]; } ``` , and this went way slower (+100ms). Code is updated and you could take a look if you want. > I still think it's a little bit too much, isn't it? It depends on the indices shape. The shape of indices in the perf test I set is 8x256x128x100, which is kind of stressful. I checked the models which require ScatterND, and the shapes of indices are like 1x3x80x80x6x5 (1/45 of the stress test) from https://github.com/opencv/opencv/issues/22528, 1x168x2 from https://github.com/opencv/opencv/issues/22541#issuecomment-1252268440. I could not say we can never reach a scale like the stress test, but maybe the current implementation satisfies most use cases, not to mention that a model containing scatter or scatterND is quite rare. We could improve it latter if there is a great demand on the speed of scatter & scatterND. Will update the benchmark for scatterND latter.
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
I think 5ms here is negligible. Does caching step_ and indices_step_ improve the performance? Everything looks good, so please make the same changes to scatterND to be consistent and it will be merged.
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
> Does caching step_ and indices_step_ improve the performance? Minor improvement like -2ms. I think we dont cache step_ and indices_step_ to save mem and keep the code clean and simple. > so please make the same changes to scatterND Sure, will do.
**File:** modules/dnn/src/onnx/onnx_importer.cpp
**Change Type:** modified
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -181,6 +181,7 @@ class ONNXImporter
     void parseElementWise          (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseDepthToSpace         (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseRange                (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
+    void parseScatter              (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
     void parseSimpleLayers         (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
 
     // Domain: com.microsoft
@@ -3106,6 +3107,58 @@ void ONNXImporter::parseRange(LayerParams& layerParams, const opencv_onnx::NodeP
     constBlobsExtraInfo.insert(std::make_pair(node_proto.output(0), TensorInfo(1)));
```

Review comment:
Done. Scatter, ScatterElements and ScatterND now share the same parser (parseScatter).
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
tried with `std::memcpy` instead of `std::copy`, since latter one only works with iterators and what I got are pointers not vectors. It went slower with `std::memcpy` like +100ms. Related code is updated as comments.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
I need to think of it carefully. Will reach back asap.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
I would further suggest to merge this as is, but open a ticket for a potential improvement, so that this PR doesn't block anyone.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Well that was unexpected.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
~I prefer to keep current impl. I mean we can always index `indices` without `idx`, but I dont figure how to index `updates` without `idx`.~ ```python # ScatterND impl in python output = np.copy(data) update_indices = indices.shape[:-1] for idx in np.ndindex(update_indices): output[indices[idx]] = f(output[indices[idx]], updates[idx]) // how to index updates without idx? ``` Well, actually I figured it out. Lets say we have `indices` of shape [m, n], and `updates` of shape [x, y, z]. Indexing the next element in `updates` is always adding offset y_i * z + z_i to the previous offset. We just need to know the number of dimensions that involve in the offset calculation. Let me have a try.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Also tested with the latest optimized impl. It is just slower (+80ms) with `std::memcpy`.
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
I think we can get rid of `inp_current_indices` and `finalize()`: ```cpp const T* tmp_p_indices = p_indices + ind_start_offset; inp_start_offset = 0; for (int j = 0; j < k; j++) { CV_Assert(tmp_p_indices[j] < shape[j] && tmp_p_indices[j] > -shape[j]); inp_start_offset += (((int)tmp_p_indices[j] + shape[j]) % shape[j]) * step[j]; } inp_start_offset /= sizeof(T); ``` It shows another 2x speedup on my machine.
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Seems like here we can also get rid of `current_indices` and `finalize()` and also get 2x speedup: ```cpp t = i; int at_axis_val = 0; ind_offset = 0; inp_offset = 0; for (j = ndims - 1; j >= 0; j--) { idx = t / ind_shape[j]; offset_at_idx = (int)(t - idx * ind_shape[j]); ind_offset += offset_at_idx * ind_step[j]; inp_offset += offset_at_idx * step[j]; t = idx; if (j == axis) { at_axis_val = offset_at_idx * step[j]; } } ind_offset /= sizeof(T); // get index and overwrite current indices const T* tmp_p_index = p_index + ind_offset; index = (int)(*tmp_p_index); CV_Assert(index < shape[axis] && index > -shape[axis]); inp_offset = inp_offset - at_axis_val + ((index + shape[axis]) % shape[axis]) * step[axis]; inp_offset /= sizeof(T); ```
**File:** modules/dnn/src/layers/scatterND_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,202 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
It is indeed ~2X speedup getting rid of `inp_current_indices`
**File:** modules/dnn/src/layers/scatter_layer.cpp
**Change Type:** added
**Context:** PR #22529: DNN: supports Scatter and ScatterND from ONNX

**Code Changes:**
```diff
@@ -0,0 +1,208 @@
+// 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 <algorithm> // for std::max & std::min
+
```

Review comment:
Yes, another speedup of ~1.5X.
**File:** modules/dnn/test/test_onnx_conformance.cpp
**Change Type:** modified
**Context:** PR #23343: DNN Test ONNX: Fix the logic of the test case

**Code Changes:**
```diff
@@ -937,6 +937,7 @@ class Test_ONNX_conformance : public TestWithParam<ONNXConfParams>
 
     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;
@@ -1001,6 +1002,10 @@ class Test_ONNX_conformance : public TestWithParam<ONNXConfParams>
             #include "test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp"
```

Review comment:
There is no dedicated skip list for OpenCV backend after the patch. I propose to add it and let it be empty for now.
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support

**Code Changes:**
```diff
@@ -0,0 +1,664 @@
+// 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 <opencv2/dnn/shape_utils.hpp>
+
+#include <algorithm>
```

Review comment:
cv::AutoBuffer<> is the proper alternative
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 545

**Code Changes:**
```diff
+            {
+                auto bitshift = [] (const uint8_t &a, const uint8_t &b) { return a << b; };
+                binary_forward<T>(bitshift, std::forward<Args>(args)...);
+                break;
+            }
+            case OPERATION::MAX:
+            {
+                auto max = [](const T &a, const T &b) { return std::max(a, b); };
+                nary_forward<T>(max, T{1}, std::forward<Args>(args)...);
+                break;
+            }
```

Review comment:
by ONNX specification, Max, Min, Mean and Sum may have more than 2 inputs. Is it planned to be added in this PR or later?
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 545

**Code Changes:**
```diff
+            {
+                auto bitshift = [] (const uint8_t &a, const uint8_t &b) { return a << b; };
+                binary_forward<T>(bitshift, std::forward<Args>(args)...);
+                break;
+            }
+            case OPERATION::MAX:
+            {
+                auto max = [](const T &a, const T &b) { return std::max(a, b); };
+                nary_forward<T>(max, T{1}, std::forward<Args>(args)...);
+                break;
+            }
```

Review comment:
Yes, it is planned, this is the reason this PR is still in draft.
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 260

**Code Changes:**
```diff
+        }
+    }
+
+    template <typename T, typename Functor>
+    void binary_forward(const Functor& f, const std::vector<Mat>& inputs, std::vector<Mat>& outputs)
+    {
+        const Mat& a = inputs[0];
+        const Mat& b = inputs[1];
+        Mat& out = outputs[0];
+
+        // collect info of inputs and output
```

Review comment:
the first part of forward_impl is populated `number_of_elemwise_ops x number_of_types` times, however, it's enough to call it once before typeDispatch and then call `do_broadcast_op()` directly from `opDispatch()`. The only parameterization to call prepare_for_broadcast_op() is sizeof(T), however, it can be retrieved as `{a.elemSize(), b.elemSize(), out.elemSize()}`.
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 260

**Code Changes:**
```diff
+        }
+    }
+
+    template <typename T, typename Functor>
+    void binary_forward(const Functor& f, const std::vector<Mat>& inputs, std::vector<Mat>& outputs)
+    {
+        const Mat& a = inputs[0];
+        const Mat& b = inputs[1];
+        Mat& out = outputs[0];
+
+        // collect info of inputs and output
```

Review comment:
I'll move this function to finalize(), since we don't need to reallocate and refold shapes every time.
**File:** modules/dnn/perf/perf_layer.cpp
**Change Type:** modified
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 58

**Code Changes:**
```diff
@@ -55,7 +55,156 @@ struct Layer_Slice : public TestBaseWithParam<tuple<Backend, Target> >
     }
 };
 
+struct Layer_NaryEltwise : public TestBaseWithParam<tuple<Backend, Target> >
+{
+    void test_layer(const std::vector<int>& a_shape, const std::vector<int>& b_shape, const String op, bool isRef = false)
+    {
+        int backendId = get<0>(GetParam());
+        int targetId = get<1>(GetParam());
```

Review comment:
If we decide to leave this performance test, we need to factor out common code and generate data randomly.
**File:** modules/dnn/src/layers/nary_eltwise_layers.cpp
**Change Type:** added
**Context:** PR #21865: Reimplementation of Element-wise layers with broadcasting support
**Review Line:** 503

**Code Changes:**
```diff
+    template<typename T, typename... Args>
+    inline void opDispatch(size_t ninputs, Args&&... args)
+    {
+        switch (op)
+        {
+            case OPERATION::EQUAL:
+            {
+                auto equal = [](const T &a, const T &b) { return a == b; };
+                binary_forward<T>(equal, std::forward<Args>(args)...);
+                break;
+            }
```

Review comment:
We may want to comment out operations that wont work for now - everything that returns bool.