From 54b743dca81dd5865c9929f717c900c653fa437d Mon Sep 17 00:00:00 2001 From: Abhishek Gola Date: Thu, 26 Feb 2026 11:46:44 +0530 Subject: [PATCH] Merge pull request #28575 from abhishek-gola:shape_inference_bug_fix [BUG FIX] Incorrect shape assert bug fix in 5.x #28575 Closes: https://github.com/opencv/opencv/issues/28563 ### 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 --- modules/dnn/src/layers/reduce2_layer.cpp | 8 ++++---- modules/dnn/src/layers/reduce_layer.cpp | 4 ++-- modules/dnn/test/test_common.impl.hpp | 24 ++++++++++++++++++++---- modules/dnn/test/test_onnx_importer.cpp | 8 ++++++-- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/modules/dnn/src/layers/reduce2_layer.cpp b/modules/dnn/src/layers/reduce2_layer.cpp index 2049e6aa7b..56c59e96ce 100644 --- a/modules/dnn/src/layers/reduce2_layer.cpp +++ b/modules/dnn/src/layers/reduce2_layer.cpp @@ -122,7 +122,7 @@ public: std::fill(shape_out.begin(), shape_out.end(), 1); outs[0] = shape_out; } else { - outs[0] = MatShape(1, 1); + outs[0] = MatShape::scalar(); } } return false; @@ -142,7 +142,7 @@ public: shape_output.push_back(shape_output_[i]); } } - if (shape_output.empty()) shape_output.push_back(1); + if (shape_output.empty()) shape_output = MatShape::scalar(); outs[0] = shape_output; return false; } @@ -471,7 +471,7 @@ public: outShape = inpShape; for (int i = 0; i < (int)outShape.size(); ++i) outShape[i] = 1; } else { - outShape.assign(1, 1); + outShape = MatShape::scalar(); } } } else { @@ -487,7 +487,7 @@ public: outShape.push_back(tmp[i]); } } - if (outShape.empty()) outShape.push_back(1); + if (outShape.empty()) outShape = MatShape::scalar(); axes = norm_axes; } diff --git a/modules/dnn/src/layers/reduce_layer.cpp b/modules/dnn/src/layers/reduce_layer.cpp index 6ce05e7b2b..6cd662edb3 100644 --- a/modules/dnn/src/layers/reduce_layer.cpp +++ b/modules/dnn/src/layers/reduce_layer.cpp @@ -118,7 +118,7 @@ public: for (auto i = 0; i < shape_output.size(); ++i) shape_output[i] = 1; } else { - shape_output.push_back(1); + shape_output = MatShape::scalar(); } outputs.assign(1, shape_output); } @@ -139,7 +139,7 @@ public: shape_output.push_back(shape_output_[i]); } if (shape_output.empty()) - shape_output.push_back(1); + shape_output = MatShape::scalar(); outputs.assign(1, shape_output); } diff --git a/modules/dnn/test/test_common.impl.hpp b/modules/dnn/test/test_common.impl.hpp index b2a626d47a..499b610b4a 100644 --- a/modules/dnn/test/test_common.impl.hpp +++ b/modules/dnn/test/test_common.impl.hpp @@ -79,11 +79,27 @@ void normAssert( cv::InputArray ref, cv::InputArray test, const char *comment /*= ""*/, double l1 /*= 0.00001*/, double lInf /*= 0.0001*/) { - double normL1 = cvtest::norm(ref, test, cv::NORM_L1) / ref.getMat().total(); - EXPECT_LE(normL1, l1) << comment << " |ref| = " << cvtest::norm(ref, cv::NORM_INF); + cv::Mat refMat = ref.getMat(); + cv::Mat testMat = test.getMat(); + const cv::MatShape refShape = refMat.shape(); + const cv::MatShape testShape = testMat.shape(); + const bool scalar1dCompatible = + (refShape.isScalar() && testShape.size() == 1 && testShape[0] == 1) || + (testShape.isScalar() && refShape.size() == 1 && refShape[0] == 1); + if (scalar1dCompatible) + { + const cv::MatShape oneShape{1}; + if (refShape.isScalar()) + refMat = refMat.reshape(1, oneShape); + if (testShape.isScalar()) + testMat = testMat.reshape(1, oneShape); + } - double normInf = cvtest::norm(ref, test, cv::NORM_INF); - EXPECT_LE(normInf, lInf) << comment << " |ref| = " << cvtest::norm(ref, cv::NORM_INF); + double normL1 = cvtest::norm(refMat, testMat, cv::NORM_L1) / refMat.total(); + EXPECT_LE(normL1, l1) << comment << " |ref| = " << cvtest::norm(refMat, cv::NORM_INF); + + double normInf = cvtest::norm(refMat, testMat, cv::NORM_INF); + EXPECT_LE(normInf, lInf) << comment << " |ref| = " << cvtest::norm(refMat, cv::NORM_INF); } std::vector matToBoxes(const cv::Mat& m) diff --git a/modules/dnn/test/test_onnx_importer.cpp b/modules/dnn/test/test_onnx_importer.cpp index 49585dc7d1..625f5fb526 100644 --- a/modules/dnn/test/test_onnx_importer.cpp +++ b/modules/dnn/test/test_onnx_importer.cpp @@ -118,8 +118,12 @@ public: net.setInput(inps[i], inputNames[i]); Mat out = net.forward(""); - // BUG: https://github.com/opencv/opencv/issues/28563 - // EXPECT_EQ(shape(out), shape(ref)); + MatShape outShape = shape(out); + MatShape refShape = shape(ref); + bool scalar1dCompatible = + (outShape.isScalar() && refShape.size() == 1 && refShape[0] == 1) || + (refShape.isScalar() && outShape.size() == 1 && outShape[0] == 1); + EXPECT_TRUE(outShape == refShape || scalar1dCompatible); if (useSoftmax) {