diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 03de0d8eac..8548842848 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -1263,6 +1263,12 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams ¶ms); }; + class CV_EXPORTS DetLayer : 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 de5dc661a7..f053f324c8 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -111,6 +111,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Unsqueeze, UnsqueezeLayer); CV_DNN_REGISTER_LAYER_CLASS(IsNaN, IsNaNLayer); CV_DNN_REGISTER_LAYER_CLASS(IsInf, IsInfLayer); + CV_DNN_REGISTER_LAYER_CLASS(Det, DetLayer); CV_DNN_REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer); CV_DNN_REGISTER_LAYER_CLASS(Deconvolution, DeconvolutionLayer); diff --git a/modules/dnn/src/layers/det_layer.cpp b/modules/dnn/src/layers/det_layer.cpp new file mode 100644 index 0000000000..7131318c64 --- /dev/null +++ b/modules/dnn/src/layers/det_layer.cpp @@ -0,0 +1,143 @@ +// 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 { + +// ONNX Det operator +// Spec: https://onnx.ai/onnx/operators/onnx__Det.html +// Supported opsets: 11-22 + +class DetLayerImpl CV_FINAL : public DetLayer +{ +public: + DetLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + } + + 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 + { + CV_Assert(inputs.size() == 1); + const MatShape& in = inputs[0]; + CV_Assert(in.size() >= 2); + + int n0 = in[in.size() - 2]; + int n1 = in[in.size() - 1]; + CV_Assert(n0 == -1 || n1 == -1 || n0 == n1); + + MatShape out; + if (in.size() > 2) + out.assign(in.begin(), in.end() - 2); + else + out = MatShape({1}); + + outputs.assign(1, out); + return false; + } + + void getTypes(const std::vector& inputs, + const int requiredOutputs, + const int requiredInternals, + std::vector& outputs, + std::vector& internals) const CV_OVERRIDE + { + CV_Assert(!inputs.empty()); + int t = inputs[0]; + + CV_Assert(t == CV_32F || t == CV_64F || t == CV_16F || t == CV_16BF); + outputs.assign(requiredOutputs, MatType(t)); + internals.assign(requiredInternals, MatType(t)); + } + + 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]; + + CV_Assert(X.dims >= 2); + int n = X.size[X.dims - 1]; + int m = X.size[X.dims - 2]; + CV_Assert(n == m); + + size_t batch = X.total() / (X.size[X.dims - 2] * X.size[X.dims - 1]); + + int outDims; + std::vector outSizes; + if (X.dims > 2) + { + outDims = X.dims - 2; + outSizes.assign(X.size.p, X.size.p + outDims); + } + else + { + outDims = 1; + outSizes = {1}; + } + outputs[0].create(outDims, outSizes.data(), X.type()); + + const int type = X.type(); + const size_t elemSz = X.elemSize(); + const size_t matStrideBytes = (size_t)n * (size_t)m * elemSz; + + const uchar* base = X.data; + uchar* outp = outputs[0].ptr(); + + parallel_for_(Range(0, static_cast(batch)), [&](const Range& r){ + Mat temp; + for (int bi = r.start; bi < r.end; ++bi) + { + size_t b = static_cast(bi); + const uchar* p = base + b * matStrideBytes; + Mat A(m, n, type, const_cast(p)); + + double det; + if (type == CV_32F || type == CV_64F) { + det = determinant(A); + } else { + A.convertTo(temp, CV_32F); + det = determinant(temp); + } + + if (type == CV_32F) + reinterpret_cast(outp)[b] = static_cast(det); + else if (type == CV_64F) + reinterpret_cast(outp)[b] = det; + else if (type == CV_16F) + reinterpret_cast(outp)[b] = saturate_cast(det); + else if (type == CV_16BF) + reinterpret_cast(outp)[b] = saturate_cast(det); + else + CV_Error(Error::BadDepth, "Unsupported input/output depth for DetLayer"); + } + }); + } +}; + +Ptr DetLayer::create(const LayerParams& params) +{ + return Ptr(new DetLayerImpl(params)); +} + +}} diff --git a/modules/dnn/src/onnx/onnx_importer2.cpp b/modules/dnn/src/onnx/onnx_importer2.cpp index 9e810e6166..a53cab38b6 100644 --- a/modules/dnn/src/onnx/onnx_importer2.cpp +++ b/modules/dnn/src/onnx/onnx_importer2.cpp @@ -212,6 +212,7 @@ protected: void parseTrilu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseIsNaN (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseIsInf (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseDet (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); @@ -1624,6 +1625,12 @@ void ONNXImporter2::parseIsInf(LayerParams& layerParams, const opencv_onnx::Node addLayer(layerParams, node_proto); } +void ONNXImporter2::parseDet(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + layerParams.type = "Det"; + addLayer(layerParams, node_proto); +} + void ONNXImporter2::parseUpsample(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { int n_inputs = node_proto.input_size(); @@ -2456,6 +2463,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI(int opset_version) dispatch["Trilu"] = &ONNXImporter2::parseTrilu; dispatch["IsNaN"] = &ONNXImporter2::parseIsNaN; dispatch["IsInf"] = &ONNXImporter2::parseIsInf; + dispatch["Det"] = &ONNXImporter2::parseDet; dispatch["Upsample"] = &ONNXImporter2::parseUpsample; dispatch["SoftMax"] = dispatch["Softmax"] = dispatch["LogSoftmax"] = &ONNXImporter2::parseSoftMax; dispatch["DetectionOutput"] = &ONNXImporter2::parseDetectionOutput; 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 4590e6da50..a5f22a3bdc 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 @@ -507,9 +507,9 @@ CASE(test_dequantizelinear_axis) CASE(test_dequantizelinear_blocked) SKIP; CASE(test_det_2d) - // no filter + SKIP; CASE(test_det_nd) - // no filter + SKIP; CASE(test_div) // no filter CASE(test_div_bcast) 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 8557647e98..4c61bffead 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 @@ -91,3 +91,5 @@ "test_triu_pos", "test_triu_square", "test_triu_square_neg", +"test_det_2d", +"test_det_nd", 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 a06b416099..5d2ba4e890 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 @@ -69,8 +69,6 @@ "test_convtranspose_pad", // Issue::Parser::Weights are required as inputs "test_convtranspose_pads", // Issue::Parser::Weights are required as inputs "test_convtranspose_with_kernel", // Issue::Parser::Weights are required as inputs -"test_det_2d", // Issue:: Unkonwn error -"test_det_nd", // Issue:: Unkonwn error "test_dropout_default_mask", // Issue::cvtest::norm::wrong data type "test_dropout_default_mask_ratio", // ---- same as above --- "test_dynamicquantizelinear", // Issue:: Unkonwn error