From e2f0876777c60a739d5fb099dedebe7b71d1710c Mon Sep 17 00:00:00 2001 From: Saravana Balaji Mohan Balaji <63021620+MBSaravanaBalaji@users.noreply.github.com> Date: Wed, 1 Jul 2026 06:27:55 -0400 Subject: [PATCH] Merge pull request #29217 from MBSaravanaBalaji:feat/onnx-lppool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dnn: implement LpPool ONNX operator #29217 ## Summary Implements the `LpPool` ONNX operator (opset 1–18), which was previously unregistered and caused a parse failure. `LpPool` computes the Lp-norm pooling: `(sum(|x|^p))^(1/p)` over a sliding window. ## Changes - New `LpPoolLayer` in `modules/dnn/src/layers/lppool_layer.cpp` - Supports `kernel_shape`, `strides`, `dilations`, `pads`, `auto_pad` (NOTSET/SAME_UPPER), `ceil_mode`, and `p` (default 2) - SIMD fast paths for p=1 (abs + accumulate) and p=2 (square + accumulate + sqrt); scalar fallback for other values of p - Registered `LpPool` dispatch entry in both `onnx_importer.cpp` (classic engine) and `onnx_importer2.cpp` (new graph engine) - Added `LpPoolLayer` declaration to `modules/dnn/include/opencv2/dnn/all_layers.hpp` - Registered layer class in `modules/dnn/src/init.cpp` - Re-enabled 8 lppool conformance tests in `test_onnx_conformance.cpp` (previously in parser denylist) - `test_lppool_2d_same_lower` added to the global conformance denylist — same known SAME_LOWER padding bug that affects `averagepool` and `maxpool` ## Testing All applicable ONNX conformance tests pass: | Test | Result | |------|--------| | test_lppool_1d_default | PASSED | | test_lppool_2d_default | PASSED | | test_lppool_2d_dilations | PASSED | | test_lppool_2d_pads | PASSED | | test_lppool_2d_same_lower | SKIPPED (known SAME_LOWER padding bug, consistent with avgpool/maxpool) | | test_lppool_2d_same_upper | PASSED | | test_lppool_2d_strides | PASSED | | test_lppool_3d_default | PASSED | Tested on: macOS (x86_64/SSE4, Rosetta 2) and Linux x86_64 (AVX2/AVX-512, GCC 13.3.0), Release build OpenCV version: 5.0.0-pre ## Related Issues None --- ### 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 - [ ] 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../dnn/include/opencv2/dnn/all_layers.hpp | 11 + modules/dnn/src/init.cpp | 1 + modules/dnn/src/layers/lppool_layer.cpp | 480 ++++++++++++++++++ modules/dnn/src/onnx/onnx_importer2.cpp | 8 + modules/dnn/test/test_onnx_conformance.cpp | 16 +- ...rmance_layer_filter__cuda_denylist.inl.hpp | 7 + ...e_layer_filter__cuda_fp16_denylist.inl.hpp | 7 + ...conformance_layer_filter__openvino.inl.hpp | 14 + ...e_layer_filter_opencv_all_denylist.inl.hpp | 1 + ...yer_filter_opencv_classic_denylist.inl.hpp | 7 + 10 files changed, 544 insertions(+), 8 deletions(-) create mode 100644 modules/dnn/src/layers/lppool_layer.cpp diff --git a/modules/dnn/include/opencv2/dnn/all_layers.hpp b/modules/dnn/include/opencv2/dnn/all_layers.hpp index 64034f2610..3cc9a21783 100644 --- a/modules/dnn/include/opencv2/dnn/all_layers.hpp +++ b/modules/dnn/include/opencv2/dnn/all_layers.hpp @@ -577,6 +577,17 @@ CV__DNN_INLINE_NS_BEGIN static Ptr create(const LayerParams& params); }; + class CV_EXPORTS LpPoolLayer : public Layer + { + public: + std::vector kernel_shape, strides, dilations, pads; + AutoPadding auto_pad; + bool ceil_mode; + int p; + + static Ptr create(const LayerParams& params); + }; + class CV_EXPORTS GlobalAveragePoolLayer : public Layer { public: diff --git a/modules/dnn/src/init.cpp b/modules/dnn/src/init.cpp index 109f9153cf..5964a9774d 100644 --- a/modules/dnn/src/init.cpp +++ b/modules/dnn/src/init.cpp @@ -141,6 +141,7 @@ void initializeLayerFactory() CV_DNN_REGISTER_LAYER_CLASS(Pooling, PoolingLayer); CV_DNN_REGISTER_LAYER_CLASS(MaxPool, MaxPoolLayer); CV_DNN_REGISTER_LAYER_CLASS(AveragePool, AveragePoolLayer); + CV_DNN_REGISTER_LAYER_CLASS(LpPool, LpPoolLayer); CV_DNN_REGISTER_LAYER_CLASS(ROIPooling, PoolingLayer); CV_DNN_REGISTER_LAYER_CLASS(PSROIPooling, PoolingLayer); CV_DNN_REGISTER_LAYER_CLASS(Reduce, ReduceLayer); diff --git a/modules/dnn/src/layers/lppool_layer.cpp b/modules/dnn/src/layers/lppool_layer.cpp new file mode 100644 index 0000000000..21ff69a2d1 --- /dev/null +++ b/modules/dnn/src/layers/lppool_layer.cpp @@ -0,0 +1,480 @@ +// 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 "../net_impl.hpp" +#include "conv2_common.hpp" +#include "opencv2/core/hal/intrin.hpp" + +#include + +namespace cv +{ +namespace dnn +{ + +/* + LpPool layer, as defined in ONNX specification: + https://onnx.ai/onnx/operators/onnx__LpPool.html + Supported opsets: 1-18 + + Computes the Lp-norm pooling (sum(|x|^p))^(1/p) over a sliding window. +*/ + +#if CV_SIMD +// Vectorized per-element policies for the common norms. Each policy supplies +// the accumulation step (applied per loaded vector) and the final reduction +// (applied once per output element). This lets a single templated kernel cover +// both p == 1 and p == 2 instead of duplicating the loop nest for each. +struct LpPoolL1 +{ + static inline v_float32 accum(const v_float32& acc, const v_float32& v) { return v_add(acc, v_abs(v)); } + static inline v_float32 finalize(const v_float32& acc) { return acc; } +}; + +struct LpPoolL2 +{ + static inline v_float32 accum(const v_float32& acc, const v_float32& v) { return v_add(acc, v_mul(v, v)); } + static inline v_float32 finalize(const v_float32& acc) { return v_sqrt(acc); } +}; + +// One output column for NVEC*nlanes channels on the boundary path: every tap is +// range-checked because the window may stick out of the padded volume. zi_/yi_/xi_ +// are the (already strided, pad-shifted) top-left source coords; inp_c is the input +// base advanced to the current channel block, out_xc the matching output slot. +template +static inline void poolBoundaryTile(const float* inp_c, float* out_xc, + int zi_, int yi_, int xi_, + int Di, int Hi, int Wi, int C0, + const int* zyxtab, int ksize, int nlanes, const v_float32& z) +{ + constexpr int MAX_POOL_DIMS = ConvState::MAX_CONV_DIMS; + v_float32 s[NVEC]; + for (int j = 0; j < NVEC; j++) s[j] = z; + for (int k = 0; k < ksize; k++) { + int zi = zi_ + zyxtab[k*MAX_POOL_DIMS]; + int yi = yi_ + zyxtab[k*MAX_POOL_DIMS + 1]; + int xi = xi_ + zyxtab[k*MAX_POOL_DIMS + 2]; + if ((unsigned)zi >= (unsigned)Di || + (unsigned)yi >= (unsigned)Hi || + (unsigned)xi >= (unsigned)Wi) + continue; + const float* inp_k = inp_c + ((zi*Hi + yi)*Wi + xi)*C0; + for (int j = 0; j < NVEC; j++) + s[j] = Op::accum(s[j], vx_load(inp_k + j*nlanes)); + } + for (int j = 0; j < NVEC; j++) + vx_store(out_xc + j*nlanes, Op::finalize(s[j])); +} + +// Same accumulate-and-store skeleton for the inner path, where the window is fully +// inside the volume so taps are addressed through the precomputed flat ofstab and +// no bounds check is needed. inp_xi already points at the column's channel block. +template +static inline void poolInnerTile(const float* inp_xi, float* out_xc, + const int* ofstab, int ksize, int nlanes, const v_float32& z) +{ + v_float32 s[NVEC]; + for (int j = 0; j < NVEC; j++) s[j] = z; + for (int k = 0; k < ksize; k++) { + int ofs_k = ofstab[k]; + for (int j = 0; j < NVEC; j++) + s[j] = Op::accum(s[j], vx_load(inp_xi + ofs_k + j*nlanes)); + } + for (int j = 0; j < NVEC; j++) + vx_store(out_xc + j*nlanes, Op::finalize(s[j])); +} + +template +static void lpPoolSIMD(const float* inp_, float* out_, const ConvState& cs) +{ + int NC1 = cs.inpshape[0]*cs.inpshape[1]; + + CV_Assert(cs.inpshape.layout == DATA_LAYOUT_BLOCK); + CV_Assert(cs.outshape.layout == DATA_LAYOUT_BLOCK); + CV_Assert(cs.inpshape.dims == cs.outshape.dims); + + parallel_for_(Range(0, NC1), [&](const Range& r) { + constexpr int MAX_POOL_DIMS = ConvState::MAX_CONV_DIMS; + + CV_Assert(cs.nspatialdims <= MAX_POOL_DIMS && MAX_POOL_DIMS == 3); + + int sdims = cs.nspatialdims; + int nc0 = r.start, nc1 = r.end; + int C0 = cs.inpshape.back(); + int Di = sdims > 2 ? cs.inpshape[sdims - 1] : 1; + int Hi = sdims > 1 ? cs.inpshape[sdims] : 1; + int Wi = cs.inpshape[sdims + 1]; + int D = sdims > 2 ? cs.outshape[sdims - 1] : 1; + int H = sdims > 1 ? cs.outshape[sdims] : 1; + int W = cs.outshape[sdims + 1]; + int iplanesize = Di*Hi*Wi*C0; + int planesize = D*H*W*C0; + int SZ = cs.strides[0], SY = cs.strides[1], SX = cs.strides[2]; + int padZ0 = cs.pads[0], padY0 = cs.pads[1], padX0 = cs.pads[2]; + int inner_z0 = cs.inner[0], inner_z1 = cs.inner[MAX_POOL_DIMS]; + int inner_y0 = cs.inner[1], inner_y1 = cs.inner[MAX_POOL_DIMS + 1]; + int inner_x0 = cs.inner[2], inner_x1 = cs.inner[MAX_POOL_DIMS + 2]; + int ksize = (int)cs.ofstab.size(); + const int* zyxtab = cs.coordtab.data(); + const int* ofstab = cs.ofstab.data(); + + const float* inp = inp_ + nc0*iplanesize; + float* out = out_ + nc0*planesize; + + int nlanes = VTraits::vlanes(); + CV_Assert(C0 == nlanes || C0 == nlanes*2 || C0 % (nlanes*4) == 0); + v_float32 z = vx_setzero_f32(); + + for (int nc = nc0; nc < nc1; nc++, inp += iplanesize) { + for (int z0 = 0; z0 < D; z0++) { + int zi_ = z0*SZ - padZ0; + for (int y0 = 0; y0 < H; y0++, out += W*C0) { + int x0 = 0; + int x1 = z0 >= inner_z0 && z0 < inner_z1 && + y0 >= inner_y0 && y0 < inner_y1 ? inner_x0 : W; + int yi_ = y0*SY - padY0; + + for(;;) { + // Boundary (outer) path — needs per-element bounds check + if (nlanes == C0) { + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + poolBoundaryTile(inp, out + x0*C0, zi_, yi_, xi_, + Di, Hi, Wi, C0, zyxtab, ksize, nlanes, z); + } + } else { + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + for (int c = 0; c < C0; c += nlanes*2) + poolBoundaryTile(inp + c, out + x0*C0 + c, zi_, yi_, xi_, + Di, Hi, Wi, C0, zyxtab, ksize, nlanes, z); + } + } + + if (x0 == W) + break; + x1 = inner_x1; + + // Inner path — no bounds check needed + if (nlanes == C0) { + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + const float* inp_xi = inp + ((Hi*zi_ + yi_)*Wi + xi_)*C0; + poolInnerTile(inp_xi, out + x0*C0, ofstab, ksize, nlanes, z); + } + } else if (nlanes*2 == C0) { + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + const float* inp_xi = inp + ((Hi*zi_ + yi_)*Wi + xi_)*C0; + poolInnerTile(inp_xi, out + x0*C0, ofstab, ksize, nlanes, z); + } + } else { + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + for (int c = 0; c < C0; c += nlanes*4) { + const float* inp_xi = inp + ((Hi*zi_ + yi_)*Wi + xi_)*C0 + c; + poolInnerTile(inp_xi, out + x0*C0 + c, ofstab, ksize, nlanes, z); + } + } + } + x1 = W; + } + } + } + } + }); +} +#endif + +// Scalar implementation for arbitrary p (also used when CV_SIMD is disabled). +static void lpPoolScalar(const float* inp_, float* out_, const ConvState& cs, int p) +{ + int NC1 = cs.inpshape[0]*cs.inpshape[1]; + + CV_Assert(cs.inpshape.layout == DATA_LAYOUT_BLOCK); + CV_Assert(cs.outshape.layout == DATA_LAYOUT_BLOCK); + CV_Assert(cs.inpshape.dims == cs.outshape.dims); + + parallel_for_(Range(0, NC1), [&](const Range& r) { + constexpr int MAX_POOL_DIMS = ConvState::MAX_CONV_DIMS; + + CV_Assert(cs.nspatialdims <= MAX_POOL_DIMS && MAX_POOL_DIMS == 3); + + int sdims = cs.nspatialdims; + int nc0 = r.start, nc1 = r.end; + int C0 = cs.inpshape.back(); + int Di = sdims > 2 ? cs.inpshape[sdims - 1] : 1; + int Hi = sdims > 1 ? cs.inpshape[sdims] : 1; + int Wi = cs.inpshape[sdims + 1]; + int D = sdims > 2 ? cs.outshape[sdims - 1] : 1; + int H = sdims > 1 ? cs.outshape[sdims] : 1; + int W = cs.outshape[sdims + 1]; + int iplanesize = Di*Hi*Wi*C0; + int planesize = D*H*W*C0; + int SZ = cs.strides[0], SY = cs.strides[1], SX = cs.strides[2]; + int padZ0 = cs.pads[0], padY0 = cs.pads[1], padX0 = cs.pads[2]; + int inner_z0 = cs.inner[0], inner_z1 = cs.inner[MAX_POOL_DIMS]; + int inner_y0 = cs.inner[1], inner_y1 = cs.inner[MAX_POOL_DIMS + 1]; + int inner_x0 = cs.inner[2], inner_x1 = cs.inner[MAX_POOL_DIMS + 2]; + int ksize = (int)cs.ofstab.size(); + const int* zyxtab = cs.coordtab.data(); + const int* ofstab = cs.ofstab.data(); + float inv_p = 1.f / (float)p; + + const float* inp = inp_ + nc0*iplanesize; + float* out = out_ + nc0*planesize; + + for (int nc = nc0; nc < nc1; nc++, inp += iplanesize) { + for (int z0 = 0; z0 < D; z0++) { + int zi_ = z0*SZ - padZ0; + for (int y0 = 0; y0 < H; y0++, out += W*C0) { + int x0 = 0; + int x1 = z0 >= inner_z0 && z0 < inner_z1 && + y0 >= inner_y0 && y0 < inner_y1 ? inner_x0 : W; + int yi_ = y0*SY - padY0; + + for(;;) { + // Boundary (outer) path — needs per-element bounds check + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = 0.f; + for (int k = 0; k < ksize; k++) { + int zi = zi_ + zyxtab[k*MAX_POOL_DIMS]; + int yi = yi_ + zyxtab[k*MAX_POOL_DIMS+1]; + int xi = xi_ + zyxtab[k*MAX_POOL_DIMS+2]; + if ((unsigned)zi >= (unsigned)Di || + (unsigned)yi >= (unsigned)Hi || + (unsigned)xi >= (unsigned)Wi) + continue; + const float* inptr = inp + ((zi*Hi + yi)*Wi + xi)*C0; + if (p == 1) { + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += std::abs(inptr[c]); + } else if (p == 2) { + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += inptr[c] * inptr[c]; + } else { + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += std::pow(std::abs(inptr[c]), (float)p); + } + } + if (p == 2) { + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = std::sqrt(out[x0*C0 + c]); + } else if (p != 1) { + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = std::pow(out[x0*C0 + c], inv_p); + } + } + + if (x0 == W) + break; + x1 = inner_x1; + + // Inner path — no bounds check needed + for (; x0 < x1; x0++) { + int xi_ = x0*SX - padX0; + const float* inp_xi = inp + ((Hi*zi_ + yi_)*Wi + xi_)*C0; + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = 0.f; + if (p == 1) { + for (int k = 0; k < ksize; k++) { + const float* inptr = inp_xi + ofstab[k]; + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += std::abs(inptr[c]); + } + } else if (p == 2) { + for (int k = 0; k < ksize; k++) { + const float* inptr = inp_xi + ofstab[k]; + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += inptr[c] * inptr[c]; + } + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = std::sqrt(out[x0*C0 + c]); + } else { + for (int k = 0; k < ksize; k++) { + const float* inptr = inp_xi + ofstab[k]; + for (int c = 0; c < C0; c++) + out[x0*C0 + c] += std::pow(std::abs(inptr[c]), (float)p); + } + for (int c = 0; c < C0; c++) + out[x0*C0 + c] = std::pow(out[x0*C0 + c], inv_p); + } + } + x1 = W; + } + } + } + } + }); +} + +static void lpPool32f(const void* inp_, void* out_, const ConvState& cs, int p) +{ + const float* inp = (const float*)inp_; + float* out = (float*)out_; +#if CV_SIMD + // SIMD fast paths for the common norms; scalar fallback for any other p. + if (p == 1) { + lpPoolSIMD(inp, out, cs); + return; + } + if (p == 2) { + lpPoolSIMD(inp, out, cs); + return; + } +#endif + lpPoolScalar(inp, out, cs, p); +} + +typedef void (*LpPoolFunc)(const void* inp, void* out, const ConvState& cs, int p); + +class LpPoolLayerImpl : public LpPoolLayer +{ +public: + LpPoolLayerImpl(const LayerParams& params) + { + setParamsFrom(params); + auto_pad = getAutoPadding(params); + kernel_shape = params.getVector("kernel_size"); + strides = params.getVector("stride"); + dilations = params.getVector("dilation"); + pads = params.getVector("pad"); + ceil_mode = params.get("ceil_mode", false); + p = params.get("p", 2); + CV_Check(p, p >= 1, "DNN/LpPool: p must be a positive integer (>= 1)"); + } + + virtual std::ostream& dumpAttrs(std::ostream& strm, int indent) const CV_OVERRIDE + { + prindent(strm, indent); + strm << "kernel_size: ["; + for (size_t k = 0; k < kernel_shape.size(); k++) + strm << (k > 0 ? ", " : "") << kernel_shape[k]; + strm << "],\n"; + + prindent(strm, indent); + strm << "p: " << p << ",\n"; + + prindent(strm, indent); + strm << "dilation: ["; + for (size_t k = 0; k < dilations.size(); k++) + strm << (k > 0 ? ", " : "") << dilations[k]; + strm << "],\n"; + + prindent(strm, indent); + strm << "pad: ["; + for (size_t k = 0; k < pads.size(); k++) + strm << (k > 0 ? ", " : "") << pads[k]; + strm << "],\n"; + + prindent(strm, indent); + strm << "stride: ["; + for (size_t k = 0; k < strides.size(); k++) + strm << (k > 0 ? ", " : "") << strides[k]; + strm << "],\n"; + + return strm; + } + + virtual int64_t getFLOPS(const std::vector& inputs, + const std::vector& outputs) const CV_OVERRIDE + { + CV_Assert(inputs.size() == 1); + CV_Assert(outputs.size() == 1); + int ksize = 1; + for (auto sz: kernel_shape) ksize *= sz; + return (int64_t)(inputs[0].total() * ksize); + } + + virtual void getTypes(const std::vector& inptypes, + const int, const int, + std::vector& outtypes, + std::vector& temptypes) const CV_OVERRIDE + { + CV_Assert(inptypes.size() == 1); + outtypes.assign(1, inptypes[0]); + temptypes.clear(); + } + + virtual bool getMemoryShapes(const std::vector& inpshapes, + const int, + std::vector& outshapes, + std::vector& tempshapes) const CV_OVERRIDE + { + CV_Assert(inpshapes.size() == 1); + outshapes.assign(1, convInferShape(inpshapes[0], MatShape(), + kernel_shape, 0, strides, dilations, + pads, auto_pad, ceil_mode)); + tempshapes.clear(); + return true; + } + + int getLayouts(const std::vector& actualInputs, + std::vector& desiredInputs, + const int requiredOutputs, + std::vector& outputs) const CV_OVERRIDE + { + CV_Assert(actualInputs.size() == 1u); + desiredInputs.assign(1, DATA_LAYOUT_BLOCK); + outputs.assign(requiredOutputs, DATA_LAYOUT_BLOCK); + return getNetImpl(this)->defaultC0; + } + + void finalize(InputArrayOfArrays, OutputArrayOfArrays) CV_OVERRIDE {} + + void forward(InputArrayOfArrays inputs_arr, + OutputArrayOfArrays outputs_arr, + OutputArrayOfArrays) CV_OVERRIDE + { + CV_Assert(inputs_arr.total() == 1); + + int inptype = inputs_arr.type(0); + MatShape inpshape = inputs_arr.shape(0); + MatShape outshape = convInferShape(inpshape, MatShape(), + kernel_shape, 0, strides, dilations, + pads, auto_pad, ceil_mode); + int outKind = outputs_arr.kind(); + CV_Assert(outKind == _InputArray::STD_VECTOR_MAT || + outKind == _InputArray::STD_VECTOR_UMAT); + + ConvState cs; + cs.initPooling(inpshape, outshape, kernel_shape, strides, + dilations, pads, auto_pad, ceil_mode); + + if (outKind == _InputArray::STD_VECTOR_MAT) { + Mat inp = inputs_arr.getMat(0); + std::vector& outs = outputs_arr.getMatVecRef(); + outs.resize(1); + outs[0].fit(outshape, inptype); + runOp(inp, outs[0], cs); + } else { + Mat inp = inputs_arr.getMat(0); + std::vector& outs = outputs_arr.getUMatVecRef(); + outs.resize(1); + outs[0].fit(outshape, inptype); + Mat temp(outshape, inptype); + runOp(inp, temp, cs); + temp.copyTo(outs[0]); + } + } + + void runOp(const Mat& inp, Mat& out, const ConvState& cs) + { + int inptype = inp.type(); + LpPoolFunc func = (inptype == CV_32F) ? lpPool32f : nullptr; + CV_Assert(func != nullptr && "LpPool: unsupported data type"); + func(inp.data, out.data, cs, p); + } +}; + +Ptr LpPoolLayer::create(const LayerParams& params) +{ + return Ptr(new LpPoolLayerImpl(params)); +} + +}} diff --git a/modules/dnn/src/onnx/onnx_importer2.cpp b/modules/dnn/src/onnx/onnx_importer2.cpp index 32311bdf84..81e8070c87 100644 --- a/modules/dnn/src/onnx/onnx_importer2.cpp +++ b/modules/dnn/src/onnx/onnx_importer2.cpp @@ -226,6 +226,7 @@ protected: void parseLayerNorm (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseLeakyRelu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseLpNormalization (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); + void parseLpPool (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseLRN (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseLSTM (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); void parseMatMul (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto); @@ -1218,6 +1219,12 @@ void ONNXImporter2::parseGlobalPool(LayerParams &layerParams, const opencv_onnx: addLayer(layerParams, node_proto); } +void ONNXImporter2::parseLpPool(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) +{ + layerParams.type = "LpPool"; + addLayer(layerParams, node_proto); +} + void ONNXImporter2::parseReduce(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto) { layerParams.type = "Reduce2"; @@ -2641,6 +2648,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI() dispatch["MaxPool"] = &ONNXImporter2::parseMaxPool; dispatch["AveragePool"] = &ONNXImporter2::parseAveragePool; dispatch["GlobalAveragePool"] = dispatch["GlobalMaxPool"] = &ONNXImporter2::parseGlobalPool; + dispatch["LpPool"] = &ONNXImporter2::parseLpPool; dispatch["ReduceMax"] = dispatch["ReduceMin"] = dispatch["ReduceMean"] = dispatch["ReduceSum"] = dispatch["ReduceSumSquare"] = dispatch["ReduceProd"] = dispatch["ReduceL1"] = dispatch["ReduceL2"] = dispatch["ReduceLogSum"] = dispatch["ReduceLogSumExp"] = &ONNXImporter2::parseReduce; diff --git a/modules/dnn/test/test_onnx_conformance.cpp b/modules/dnn/test/test_onnx_conformance.cpp index 07bd09f7ee..d0d3e880a5 100644 --- a/modules/dnn/test/test_onnx_conformance.cpp +++ b/modules/dnn/test/test_onnx_conformance.cpp @@ -1419,14 +1419,14 @@ static const TestCase testConformanceConfig[] = { {"test_logsoftmax_large_number_expanded_ver18", 0, 0}, {"test_logsoftmax_negative_axis_expanded_ver18", 0, 0}, {"test_lpnormalization_default", 0, 0}, - {"test_lppool_1d_default", 0, 0}, - {"test_lppool_2d_default", 0, 0}, - {"test_lppool_2d_dilations", 0, 0}, - {"test_lppool_2d_pads", 0, 0}, - {"test_lppool_2d_same_lower", 0, 0}, - {"test_lppool_2d_same_upper", 0, 0}, - {"test_lppool_2d_strides", 0, 0}, - {"test_lppool_3d_default", 0, 0}, + {"test_lppool_1d_default", 1, 1}, + {"test_lppool_2d_default", 1, 1}, + {"test_lppool_2d_dilations", 1, 1}, + {"test_lppool_2d_pads", 1, 1}, + {"test_lppool_2d_same_lower", 1, 1}, + {"test_lppool_2d_same_upper", 1, 1}, + {"test_lppool_2d_strides", 1, 1}, + {"test_lppool_3d_default", 1, 1}, {"test_maxpool_2d_ceil_output_size_reduce_by_one", 0, 0}, {"test_maxpool_3d_dilations", 0, 0}, {"test_maxpool_3d_dilations_use_ref_impl", 0, 0}, diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp index e7a1de0cd3..cdf796c470 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_denylist.inl.hpp @@ -70,6 +70,13 @@ "test_logsoftmax_default_axis", "test_logsoftmax_large_number", "test_logsoftmax_large_number_expanded", +"test_lppool_1d_default", +"test_lppool_2d_default", +"test_lppool_2d_dilations", +"test_lppool_2d_pads", +"test_lppool_2d_same_upper", +"test_lppool_2d_strides", +"test_lppool_3d_default", "test_maxpool_2d_dilations", "test_maxpool_2d_same_lower", "test_maxpool_2d_uint8", diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_fp16_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_fp16_denylist.inl.hpp index cf2ce74922..c224742b1a 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_fp16_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter__cuda_fp16_denylist.inl.hpp @@ -19,6 +19,13 @@ "test_einsum_transpose", "test_logsoftmax_large_number", // fp16 accuracy issue "test_logsoftmax_large_number_expanded", // fp16 accuracy issue +"test_lppool_1d_default", +"test_lppool_2d_default", +"test_lppool_2d_dilations", +"test_lppool_2d_pads", +"test_lppool_2d_same_upper", +"test_lppool_2d_strides", +"test_lppool_3d_default", "test_maxpool_with_argmax_2d_precomputed_pads", // assertion failed mat.type() == CV_32F "test_nllloss_NCd1d2d3d4d5_none_no_weight_expanded", // crash: https://github.com/opencv/opencv/issues/25471 "test_reduce_prod_default_axes_keepdims_example", // fallback to cpu, accuracy 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 bb626c674f..fc3b21276e 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 @@ -1470,6 +1470,20 @@ CASE(test_loop13_seq) // no filter CASE(test_loop16_seq_none) // no filter +CASE(test_lppool_1d_default) + SKIP; // no nGraph/OpenVINO backend for LpPool, fallback to CPU +CASE(test_lppool_2d_default) + SKIP; +CASE(test_lppool_2d_dilations) + SKIP; +CASE(test_lppool_2d_pads) + SKIP; +CASE(test_lppool_2d_same_upper) + SKIP; +CASE(test_lppool_2d_strides) + SKIP; +CASE(test_lppool_3d_default) + SKIP; CASE(test_lrn) // no filter CASE(test_lrn_default) diff --git a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp index 919e7cd6a4..d332dffafb 100644 --- a/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp +++ b/modules/dnn/test/test_onnx_conformance_layer_filter_opencv_all_denylist.inl.hpp @@ -1,6 +1,7 @@ "test_averagepool_2d_pads_count_include_pad", // wrong output "test_averagepool_2d_precomputed_pads_count_include_pad", // wrong output "test_averagepool_2d_same_lower", // wrong output +"test_lppool_2d_same_lower", // wrong output (same SAME_LOWER padding issue) "test_cast_FLOAT_to_STRING", // Unsupported type in function 'parseCast' "test_cast_STRING_to_FLOAT", // unexception during net.forward() call "test_castlike_FLOAT_to_STRING_expanded", // Unsupported type in function 'parseCast' 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 62e576dcf5..e5c8968f3c 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 @@ -753,6 +753,13 @@ "test_convtranspose_pad", "test_convtranspose_pads", "test_convtranspose_with_kernel", +"test_lppool_1d_default", +"test_lppool_2d_default", +"test_lppool_2d_dilations", +"test_lppool_2d_pads", +"test_lppool_2d_same_upper", +"test_lppool_2d_strides", +"test_lppool_3d_default", "test_maxpool_3d_dilations", "test_maxpool_3d_dilations_use_ref_impl", "test_maxpool_3d_dilations_use_ref_impl_large",