diff --git a/modules/core/src/matrix_transform.cpp b/modules/core/src/matrix_transform.cpp index b1d72a075b..3de1024ff1 100644 --- a/modules/core/src/matrix_transform.cpp +++ b/modules/core/src/matrix_transform.cpp @@ -867,6 +867,8 @@ void broadcast(InputArray _src, InputArray _shape, OutputArray _dst) { // impl _dst.create(dims_shape, shape.ptr(), src.type()); Mat dst = _dst.getMat(); + if (dst.total() == 0) + return; std::vector is_same_shape(dims_shape, 0); for (int i = 0; i < static_cast(shape_src.size()); ++i) { if (shape_src[i] == ptr_shape[i]) { diff --git a/modules/dnn/src/layers/layers_common.cpp b/modules/dnn/src/layers/layers_common.cpp index 9a8fa9d0b5..f3c017011d 100644 --- a/modules/dnn/src/layers/layers_common.cpp +++ b/modules/dnn/src/layers/layers_common.cpp @@ -311,20 +311,30 @@ void reshapeAndCopyFirst(InputArrayOfArrays inputs, int inpType = inputs.type(0); if (inpKind == _InputArray::STD_VECTOR_MAT) { Mat inp = inputs.getMat(0); + MatShape inpShape = inp.shape(); + const size_t inpTotal = inpShape.total(); + const size_t outTotal = shape.total(); std::vector& outref = outputs.getMatVecRef(); outref.resize(1); outref[0].fit(shape, inpType); CV_Assert(outref[0].isContinuous()); + if (inpTotal == 0 && outTotal == 0) + return; Mat inp_ = inp.reshape(0, shape); if (inp_.data != outref[0].data) inp_.copyTo(outref[0]); } else { UMat inp = inputs.getUMat(0); + MatShape inpShape = inputs.shape(0); + const size_t inpTotal = inpShape.total(); + const size_t outTotal = shape.total(); std::vector& outref = outputs.getUMatVecRef(); outref.resize(1); outref[0].fit(shape, inpType); CV_Assert(outref[0].isContinuous()); + if (inpTotal == 0 && outTotal == 0) + return; UMat inp_ = inp.reshape(0, shape); inp_.copyTo(outref[0]); } diff --git a/modules/dnn/src/layers/nonmaxsuppression_layer.cpp b/modules/dnn/src/layers/nonmaxsuppression_layer.cpp index 47e743913d..df2a5d7f8a 100644 --- a/modules/dnn/src/layers/nonmaxsuppression_layer.cpp +++ b/modules/dnn/src/layers/nonmaxsuppression_layer.cpp @@ -129,6 +129,11 @@ public: outMat = outputs_arr.getMatRef(0); } + if (K == 0) + { + return; + } + auto* out = outMat.ptr(); std::vector offsets(tasks + 1, 0); for (int t = 0; t < tasks; ++t) diff --git a/modules/dnn/src/net_impl2.cpp b/modules/dnn/src/net_impl2.cpp index 4e4b5d83c3..0021611c96 100644 --- a/modules/dnn/src/net_impl2.cpp +++ b/modules/dnn/src/net_impl2.cpp @@ -1147,7 +1147,7 @@ void Net::Impl::forwardGraph(Ptr& graph, InputArrayOfArrays inputs_, buf.type() == m.type(), (!m.u || m.u->data == outOrigData[i].first), (!m.u || m.u->size == outOrigData[i].second)); - } else if (!buf.u || m.u->size > buf.u->size) { + } else if (!buf.u || (m.u && m.u->size > buf.u->size)) { buf = m; } else { // this branch means that the layer still calls diff --git a/modules/dnn/src/onnx/onnx_importer2.cpp b/modules/dnn/src/onnx/onnx_importer2.cpp index da2dad5808..340d9f4d72 100644 --- a/modules/dnn/src/onnx/onnx_importer2.cpp +++ b/modules/dnn/src/onnx/onnx_importer2.cpp @@ -2641,6 +2641,10 @@ void ONNXImporter2::parseAttentionOnnxAi(LayerParams& params, const opencv_onnx: void ONNXImporter2::parseRoiAlign(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { layerParams.type = "RoiAlign"; + if (!layerParams.has("coordinate_transformation_mode")) { + int onnx_opset = onnx_opset_map.count(str_domain_ai_onnx) ? onnx_opset_map.at(str_domain_ai_onnx) : 16; + layerParams.set("coordinate_transformation_mode", onnx_opset < 16 ? "output_half_pixel" : "half_pixel"); + } addLayer(layerParams, node_proto, 3); } diff --git a/modules/dnn/test/test_nms.cpp b/modules/dnn/test/test_nms.cpp index 6149125119..423bedf95e 100644 --- a/modules/dnn/test/test_nms.cpp +++ b/modules/dnn/test/test_nms.cpp @@ -6,6 +6,7 @@ // Third party copyrights are property of their respective owners. #include "test_precomp.hpp" +#include "npy_blob.hpp" namespace opencv_test { namespace { @@ -104,4 +105,32 @@ TEST(SoftNMS, Accuracy) } } +// Test NMS -> Reshape with zero detections using ONNX model. +// NMS with dynamic output shapes is only supported by the new engine. +TEST(NMS, ZeroDetections_Reshape) +{ + 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; + } + + std::string onnxmodel = findDataFile("dnn/onnx/models/nms_reshape_empty.onnx"); + cv::dnn::Net net = cv::dnn::readNetFromONNX(onnxmodel); + ASSERT_FALSE(net.empty()); + + Mat boxes = blobFromNPY(findDataFile("dnn/onnx/data/input_nms_reshape_empty_0.npy")); + Mat scores = blobFromNPY(findDataFile("dnn/onnx/data/input_nms_reshape_empty_1.npy")); + net.setInput(boxes, "boxes"); + net.setInput(scores, "scores"); + + std::vector outs; + net.forward(outs, std::vector{"output"}); + ASSERT_EQ(outs.size(), (size_t)1); + Mat ref = blobFromNPY(findDataFile("dnn/onnx/data/output_nms_reshape_empty.npy")); + normAssert(ref, outs[0], "NMS_ZeroDetections_Reshape"); +} + }} // namespace