From 38a72d57c1af35a6de5f4fc067ca9fed7e9db439 Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Tue, 12 Aug 2025 17:08:21 +0530 Subject: [PATCH] Merge pull request #27656 from abhishek-gola:size_layer_add Added Size layer to new DNN engine #27656 Merge with https://github.com/opencv/opencv_extra/pull/1274 ### 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 --- .../dnn/include/opencv2/dnn/all_layers.hpp | 6 ++ modules/dnn/src/init.cpp | 1 + modules/dnn/src/layers/size_layer.cpp | 70 +++++++++++++++++++ modules/dnn/src/onnx/onnx_importer2.cpp | 8 +++ modules/dnn/test/test_layers.cpp | 53 ++++++++++++++ ...conformance_layer_filter__openvino.inl.hpp | 10 +-- ...yer_filter_opencv_classic_denylist.inl.hpp | 5 ++ ..._conformance_layer_parser_denylist.inl.hpp | 5 -- 8 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 modules/dnn/src/layers/size_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 330e8a332f..1cf387123c 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -1245,6 +1245,12 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS SizeLayer : public Layer + { + public: + static Ptr create(const LayerParams ¶ms); + }; + /** * @brief Bilinear resize layer from https://github.com/cdmh/deeplab-public-ver2 * diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index 18c0a08485..56e8a9b85b 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -99,6 +99,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Reshape, ReshapeLayer); CV_DNN_REGISTER_LAYER_CLASS(Reshape2, Reshape2Layer); CV_DNN_REGISTER_LAYER_CLASS(Resize, ResizeLayer); + CV_DNN_REGISTER_LAYER_CLASS(Size, SizeLayer); CV_DNN_REGISTER_LAYER_CLASS(Shape, ShapeLayer); CV_DNN_REGISTER_LAYER_CLASS(Slice, SliceLayer); CV_DNN_REGISTER_LAYER_CLASS(Slice2, Slice2Layer); diff --git a/modules/dnn/src/layers/size_layer.cpp b/modules/dnn/src/layers/size_layer.cpp new file mode 100644 index 0000000000..266e67002d --- /dev/null +++ b/modules/dnn/src/layers/size_layer.cpp @@ -0,0 +1,70 @@ +// 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. +// Copyright (C) 2025, BigVision LLC, all rights reserved. +// Third party copyrights are property of their respective owners. + +#include "../precomp.hpp" +#include "layers_common.hpp" +#include + +namespace cv { +namespace dnn { + +class SizeLayerImpl CV_FINAL : public SizeLayer +{ +public: + SizeLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + } + + virtual bool supportBackend(int backendId) CV_OVERRIDE + { + return backendId == DNN_BACKEND_OPENCV; + } + + bool getMemoryShapes(const std::vector& inputs, + const int requiredOutputs, + std::vector& outputs, + std::vector& internals) const CV_OVERRIDE + { + outputs.assign(1, MatShape({1})); + return false; + } + + void getTypes(const std::vector& /*inputs*/, + const int requiredOutputs, + const int requiredInternals, + std::vector& outputs, + std::vector& internals) const CV_OVERRIDE + { + outputs.assign(requiredOutputs, MatType(CV_64S)); + internals.assign(requiredInternals, MatType(CV_64S)); + } + + void forward(InputArrayOfArrays inputs_arr, + OutputArrayOfArrays outputs_arr, + OutputArrayOfArrays /*internals_arr*/) CV_OVERRIDE + { + std::vector inputs, outputs; + inputs_arr.getMatVector(inputs); + outputs_arr.getMatVector(outputs); + + CV_Assert(inputs.size() == 1); + const Mat& x = inputs[0]; + + const MatShape xShape = shape(x); + int64_t totalElems = static_cast(total(xShape)); + + outputs[0].create(1, 1, CV_64S); + outputs[0].at(0) = totalElems; + } +}; + +Ptr SizeLayer::create(const LayerParams& params) +{ + return Ptr(new SizeLayerImpl(params)); +} + +}} diff --git a/modules/dnn/src/onnx/onnx_importer2.cpp b/modules/dnn/src/onnx/onnx_importer2.cpp index 1ce93b4fb2..97345015f1 100644 --- a/modules/dnn/src/onnx/onnx_importer2.cpp +++ b/modules/dnn/src/onnx/onnx_importer2.cpp @@ -211,6 +211,7 @@ protected: void parseRelu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseTrilu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseResize (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseSize (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseReshape (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseShape (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); @@ -1595,6 +1596,12 @@ void ONNXImporter2::parseResize(LayerParams& layerParams, const opencv_onnx::Nod addLayer(layerParams, node_proto, ninputs); } +void ONNXImporter2::parseSize(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + layerParams.type = "Size"; + addLayer(layerParams, node_proto); +} + void ONNXImporter2::parseTrilu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { int ninputs = node_proto.input_size(); @@ -2431,6 +2438,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI(int opset_version) dispatch["Concat"] = &ONNXImporter2::parseConcat; dispatch["If"] = &ONNXImporter2::parseIf; dispatch["Resize"] = &ONNXImporter2::parseResize; + dispatch["Size"] = &ONNXImporter2::parseSize; dispatch["Trilu"] = &ONNXImporter2::parseTrilu; dispatch["Upsample"] = &ONNXImporter2::parseUpsample; dispatch["SoftMax"] = dispatch["Softmax"] = dispatch["LogSoftmax"] = &ONNXImporter2::parseSoftMax; diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index 852f0af27a..67c04a5a9f 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -2853,4 +2853,57 @@ TEST(Layer_If, resize) } } +TEST(Layer_Size, onnx_1d) +{ + auto engine_forced = static_cast( + cv::utils::getConfigurationParameterSizeT("OPENCV_FORCE_DNN_ENGINE", cv::dnn::ENGINE_AUTO)); + if (engine_forced == cv::dnn::ENGINE_CLASSIC) + { + applyTestTag(CV_TEST_TAG_DNN_SKIP_PARSER); + return; + } + + const std::string modelname = findDataFile("dnn/onnx/models/test_size_1d_model.onnx", true); + cv::dnn::Net net = cv::dnn::readNetFromONNX(modelname, ENGINE_NEW); + + int sz1d[1] = {7}; + cv::Mat x(1, sz1d, CV_32F); + cv::randu(x, 0, 1); + net.setInput(x); + + std::vector outs; + net.forward(outs); + + ASSERT_EQ(outs.size(), 1u); + EXPECT_EQ(outs[0].total(), (size_t)1); + EXPECT_EQ(outs[0].type(), CV_64S); + EXPECT_EQ(outs[0].at(0), static_cast(sz1d[0])); +} + +TEST(Layer_Size, onnx_0d_scalar) +{ + auto engine_forced = static_cast( + cv::utils::getConfigurationParameterSizeT("OPENCV_FORCE_DNN_ENGINE", cv::dnn::ENGINE_AUTO)); + if (engine_forced == cv::dnn::ENGINE_CLASSIC) + { + applyTestTag(CV_TEST_TAG_DNN_SKIP_PARSER); + return; + } + + const std::string modelname = findDataFile("dnn/onnx/models/test_size_0d_model.onnx", true); + cv::dnn::Net net = cv::dnn::readNetFromONNX(modelname, ENGINE_NEW); + + cv::Mat x(1, 1, CV_32F); + x.at(0, 0) = 3.14f; + net.setInput(x); + + std::vector outs; + net.forward(outs); + + ASSERT_EQ(outs.size(), 1u); + EXPECT_EQ(outs[0].total(), (size_t)1); + EXPECT_EQ(outs[0].type(), CV_64S); + EXPECT_EQ(outs[0].at(0), 1); +} + }} // namespace diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp index 0c096b8f41..504b475222 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__openvino.inl.hpp @@ -1021,11 +1021,11 @@ CASE(test_maxunpool_export_without_output_shape) SKIP; #endif CASE(test_mean_example) - // no filter + SKIP; CASE(test_mean_one_input) - // no filter + SKIP; CASE(test_mean_two_inputs) - // no filter + SKIP; CASE(test_min_example) // no filter CASE(test_min_float16) @@ -1868,9 +1868,9 @@ CASE(test_sinh) CASE(test_sinh_example) // no filter CASE(test_size) - // no filter + SKIP; CASE(test_size_example) - // no filter + SKIP; CASE(test_slice) SKIP; CASE(test_slice_default_axes) diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_classic_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_classic_denylist.inl.hpp index 0e68325946..8c93cb4b55 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_classic_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_classic_denylist.inl.hpp @@ -66,3 +66,8 @@ "test_clip_inbounds", "test_clip_outbounds", "test_clip_splitbounds", +"test_size", +"test_size_example", +"test_mean_example", +"test_mean_one_input", +"test_mean_two_inputs", diff --git a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp index 164aabfd37..0661999db8 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_parser_denylist.inl.hpp @@ -123,9 +123,6 @@ "test_max_uint16", // Issue:: Unsupported data type "test_max_uint32", // Issue:: Unsupported data type "test_max_uint64", // Issue:: Unsupported data type -"test_mean_example", // Issues::Layer does not exist. Can't create layer "onnx_node_output_0!result" of type "Mean" in function 'getLayerInstance' -"test_mean_one_input", // ---- same as above --- -"test_mean_two_inputs", // ---- same as above --- "test_min_int16", // Issue:: Unsupported data type "test_min_uint16", // Issue:: Unsupported data type "test_min_uint32", // Issue:: Unkonwn error @@ -320,8 +317,6 @@ "test_simple_rnn_batchwise", // Issue:: Parser: Can't create layer "onnx_node_output_1!Y_h" of type "RNN" in function 'getLayerInstance' "test_simple_rnn_defaults", // ---- same as above --- "test_simple_rnn_with_initial_bias", // ---- same as above --- -"test_size", // Issue:: Parser: Can't create layer "onnx_node_output_0!y" of type "Size" in function 'getLayerInstance' -"test_size_example", // ---- same as above --- "test_slice_start_out_of_bounds", "test_split_zero_size_splits", // ---- incompatible type of input tensor #0 'input': CV_8UC1 given, CV_32FC1 expected in function 'setGraphInput' --- "test_strnormalizer_export_monday_casesensintive_lower", // 'Strings' (1) are not supported in function 'getLayerParams'