mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 15:23:05 +04:00
Merge pull request #29217 from MBSaravanaBalaji:feat/onnx-lppool
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
This commit is contained in:
committed by
GitHub
parent
d36133b325
commit
e2f0876777
@@ -577,6 +577,17 @@ CV__DNN_INLINE_NS_BEGIN
|
||||
static Ptr<MaxPoolLayer> create(const LayerParams& params);
|
||||
};
|
||||
|
||||
class CV_EXPORTS LpPoolLayer : public Layer
|
||||
{
|
||||
public:
|
||||
std::vector<int> kernel_shape, strides, dilations, pads;
|
||||
AutoPadding auto_pad;
|
||||
bool ceil_mode;
|
||||
int p;
|
||||
|
||||
static Ptr<LpPoolLayer> create(const LayerParams& params);
|
||||
};
|
||||
|
||||
class CV_EXPORTS GlobalAveragePoolLayer : public Layer
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <cmath>
|
||||
|
||||
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<class Op, int NVEC>
|
||||
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<class Op, int NVEC>
|
||||
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<class Op>
|
||||
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<v_float32>::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<Op, 1>(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<Op, 2>(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<Op, 1>(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<Op, 2>(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<Op, 4>(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<LpPoolL1>(inp, out, cs);
|
||||
return;
|
||||
}
|
||||
if (p == 2) {
|
||||
lpPoolSIMD<LpPoolL2>(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<int>("kernel_size");
|
||||
strides = params.getVector<int>("stride");
|
||||
dilations = params.getVector<int>("dilation");
|
||||
pads = params.getVector<int>("pad");
|
||||
ceil_mode = params.get<bool>("ceil_mode", false);
|
||||
p = params.get<int>("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<MatShape>& inputs,
|
||||
const std::vector<MatShape>& 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<MatType>& inptypes,
|
||||
const int, const int,
|
||||
std::vector<MatType>& outtypes,
|
||||
std::vector<MatType>& temptypes) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(inptypes.size() == 1);
|
||||
outtypes.assign(1, inptypes[0]);
|
||||
temptypes.clear();
|
||||
}
|
||||
|
||||
virtual bool getMemoryShapes(const std::vector<MatShape>& inpshapes,
|
||||
const int,
|
||||
std::vector<MatShape>& outshapes,
|
||||
std::vector<MatShape>& 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<DataLayout>& actualInputs,
|
||||
std::vector<DataLayout>& desiredInputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<DataLayout>& 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<Mat>& 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<UMat>& 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> LpPoolLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<LpPoolLayer>(new LpPoolLayerImpl(params));
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -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;
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user