1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #28889 from abhishek-gola:simd_kernel_speedup

SIMD Kernel speedup for DNN layers #28889

This PR add following speedups for Grounding Dino tiny model.

For Device:  Intel(R) Core(TM) i9-14900KS, x86, 32 Cores, ubuntu 24.04,
| Model | `ENGINE_NEW (Before)` | `ENGINE_NEW (After)` | `ENGINE_ORT` |
| :--- | :--- | :--- | :--- | 
| **Grounding Dino Tiny** | 3130 ms | 1872.06 ms| 1800.18 ms|

### 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
This commit is contained in:
Abhishek Gola
2026-05-21 18:23:15 +05:30
committed by GitHub
parent 1d54398f07
commit 0a8603b3ac
21 changed files with 1850 additions and 236 deletions
+1
View File
@@ -16,6 +16,7 @@ ocv_add_dispatched_file(split SSE2 AVX2 LASX)
ocv_add_dispatched_file(sum SSE2 AVX2 LASX)
ocv_add_dispatched_file(reduce SSE2 SSSE3 AVX2 NEON_DOTPROD)
ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX)
ocv_add_dispatched_file(transpose AVX AVX2 NEON RVV LASX)
# dispatching for accuracy tests
ocv_add_dispatched_file_force_all(test_intrin128 TEST SSE2 SSE3 SSSE3 SSE4_1 SSE4_2 AVX FP16 AVX2 AVX512_SKX)
+7 -32
View File
@@ -7,6 +7,9 @@
#include "hal_replacement.hpp"
#include "opencv2/core/detail/dispatch_helper.impl.hpp"
#include "transpose.simd.hpp"
#include "transpose.simd_declarations.hpp"
#include <algorithm> // std::swap_ranges
#include <numeric> // std::accumulate
@@ -179,38 +182,10 @@ static void transpose_16bit_simd(const uchar* src, size_t sstep, uchar* dst, siz
static void transpose_32bit_simd(const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size sz)
{
const uint32_t* src32 = reinterpret_cast<const uint32_t*>(src);
uint32_t* dst32 = reinterpret_cast<uint32_t*>(dst);
const size_t sstep_e = sstep / sizeof(uint32_t);
const size_t dstep_e = dstep / sizeof(uint32_t);
const int m = sz.width, n = sz.height;
int i = 0;
for (; i <= m - 4; i += 4)
{
int j = 0;
for (; j <= n - 4; j += 4)
{
v_uint32x4 r0 = v_load(src32 + i + sstep_e*(j+0));
v_uint32x4 r1 = v_load(src32 + i + sstep_e*(j+1));
v_uint32x4 r2 = v_load(src32 + i + sstep_e*(j+2));
v_uint32x4 r3 = v_load(src32 + i + sstep_e*(j+3));
v_uint32x4 o0, o1, o2, o3;
v_transpose4x4(r0, r1, r2, r3, o0, o1, o2, o3);
v_store(dst32 + dstep_e*(i+0) + j, o0);
v_store(dst32 + dstep_e*(i+1) + j, o1);
v_store(dst32 + dstep_e*(i+2) + j, o2);
v_store(dst32 + dstep_e*(i+3) + j, o3);
}
for (; j < n; j++)
for (int k = 0; k < 4; k++)
dst32[dstep_e*(i+k) + j] = src32[i + sstep_e*j + k];
}
for (; i < m; i++)
for (int j = 0; j < n; j++)
dst32[dstep_e*i + j] = src32[i + sstep_e*j];
// Cache-blocked + AVX2-aware path lives in transpose.simd.hpp; pick the
// best variant available at runtime.
CV_CPU_DISPATCH(transpose_32bit_blocks_simd, (src, sstep, dst, dstep, sz),
CV_CPU_DISPATCH_MODES_ALL);
}
static void transpose_48bit_simd(const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size sz)
+131
View File
@@ -0,0 +1,131 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "opencv2/core/hal/intrin.hpp"
namespace cv {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
void transpose_32bit_blocks_simd(const uchar* src, size_t sstep,
uchar* dst, size_t dstep, Size sz);
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
static inline void transpose_32bit_blocks_tile(const uint32_t* src32, size_t sstep_e,
uint32_t* dst32, size_t dstep_e,
int i_lo, int i_hi, int j_lo, int j_hi)
{
int i = i_lo;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (VTraits<v_uint32>::vlanes() == 8)
{
for (; i + 8 <= i_hi; i += 8)
{
int j = j_lo;
for (; j + 8 <= j_hi; j += 8)
{
v_uint32 r0 = vx_load(src32 + i + sstep_e*(j+0));
v_uint32 r1 = vx_load(src32 + i + sstep_e*(j+1));
v_uint32 r2 = vx_load(src32 + i + sstep_e*(j+2));
v_uint32 r3 = vx_load(src32 + i + sstep_e*(j+3));
v_uint32 r4 = vx_load(src32 + i + sstep_e*(j+4));
v_uint32 r5 = vx_load(src32 + i + sstep_e*(j+5));
v_uint32 r6 = vx_load(src32 + i + sstep_e*(j+6));
v_uint32 r7 = vx_load(src32 + i + sstep_e*(j+7));
v_uint32 a0, a1, a2, a3, a4, a5, a6, a7;
v_transpose4x4(r0, r1, r2, r3, a0, a1, a2, a3);
v_transpose4x4(r4, r5, r6, r7, a4, a5, a6, a7);
v_uint32 b0 = v_combine_low (a0, a4);
v_uint32 b1 = v_combine_low (a1, a5);
v_uint32 b2 = v_combine_low (a2, a6);
v_uint32 b3 = v_combine_low (a3, a7);
v_uint32 b4 = v_combine_high(a0, a4);
v_uint32 b5 = v_combine_high(a1, a5);
v_uint32 b6 = v_combine_high(a2, a6);
v_uint32 b7 = v_combine_high(a3, a7);
vx_store(dst32 + dstep_e*(i+0) + j, b0);
vx_store(dst32 + dstep_e*(i+1) + j, b1);
vx_store(dst32 + dstep_e*(i+2) + j, b2);
vx_store(dst32 + dstep_e*(i+3) + j, b3);
vx_store(dst32 + dstep_e*(i+4) + j, b4);
vx_store(dst32 + dstep_e*(i+5) + j, b5);
vx_store(dst32 + dstep_e*(i+6) + j, b6);
vx_store(dst32 + dstep_e*(i+7) + j, b7);
}
for (; j < j_hi; j++)
for (int k = 0; k < 8; k++)
dst32[dstep_e*(i+k) + j] = src32[i + sstep_e*j + k];
}
}
else if (VTraits<v_uint32>::vlanes() == 4)
{
for (; i + 4 <= i_hi; i += 4)
{
int j = j_lo;
for (; j + 4 <= j_hi; j += 4)
{
v_uint32 r0 = vx_load(src32 + i + sstep_e*(j+0));
v_uint32 r1 = vx_load(src32 + i + sstep_e*(j+1));
v_uint32 r2 = vx_load(src32 + i + sstep_e*(j+2));
v_uint32 r3 = vx_load(src32 + i + sstep_e*(j+3));
v_uint32 o0, o1, o2, o3;
v_transpose4x4(r0, r1, r2, r3, o0, o1, o2, o3);
vx_store(dst32 + dstep_e*(i+0) + j, o0);
vx_store(dst32 + dstep_e*(i+1) + j, o1);
vx_store(dst32 + dstep_e*(i+2) + j, o2);
vx_store(dst32 + dstep_e*(i+3) + j, o3);
}
for (; j < j_hi; j++)
for (int k = 0; k < 4; k++)
dst32[dstep_e*(i+k) + j] = src32[i + sstep_e*j + k];
}
}
#endif
for (; i < i_hi; i++)
for (int j = j_lo; j < j_hi; j++)
dst32[dstep_e*i + j] = src32[i + sstep_e*j];
}
void transpose_32bit_blocks_simd(const uchar* src, size_t sstep,
uchar* dst, size_t dstep, Size sz)
{
const uint32_t* src32 = reinterpret_cast<const uint32_t*>(src);
uint32_t* dst32 = reinterpret_cast<uint32_t*>(dst);
const size_t sstep_e = sstep / sizeof(uint32_t);
const size_t dstep_e = dstep / sizeof(uint32_t);
const int m = sz.width, n = sz.height;
// Single-threaded for small inputs to avoid parallel_for_ overhead.
const int64_t kMinParallelElements = 64 * 1024;
if ((int64_t)m * n < kMinParallelElements)
{
transpose_32bit_blocks_tile(src32, sstep_e, dst32, dstep_e, 0, m, 0, n);
return;
}
const int TILE = 32;
const int mtiles = (m + TILE - 1) / TILE;
const int ntiles = (n + TILE - 1) / TILE;
const int64_t totalTiles = (int64_t)mtiles * ntiles;
parallel_for_(Range(0, (int)totalTiles), [&](const Range& r)
{
for (int idx = r.start; idx < r.end; idx++)
{
int ti = idx / ntiles;
int tj = idx - ti * ntiles;
int i_lo = ti * TILE, i_hi = std::min(i_lo + TILE, m);
int j_lo = tj * TILE, j_hi = std::min(j_lo + TILE, n);
transpose_32bit_blocks_tile(src32, sstep_e, dst32, dstep_e, i_lo, i_hi, j_lo, j_hi);
}
});
}
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
CV_CPU_OPTIMIZATION_NAMESPACE_END
} // namespace cv
+4
View File
@@ -14,6 +14,10 @@ ocv_add_dispatched_file("layers/cpu_kernels/conv2_depthwise" AVX AVX2 NEON NEON_
ocv_add_dispatched_file("layers/cpu_kernels/conv2_kernels" AVX AVX2 NEON NEON_FP16)
ocv_add_dispatched_file_force_all("int8layers/conv2_int8_kernels" AVX2)
ocv_add_dispatched_file("layers/cpu_kernels/activation_kernels" AVX AVX2 NEON NEON_FP16)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/reduce2_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/transpose_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/gridsample_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/nary_eltwise_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_module(dnn opencv_core opencv_imgproc WRAP python java objc js)
+42
View File
@@ -5,6 +5,23 @@
// Copyright (C) 2025, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "../precomp.hpp"
#define CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
#include "cpu_kernels/activation_kernels.simd.hpp"
#include "layers/cpu_kernels/activation_kernels.simd_declarations.hpp"
#undef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn { namespace {
static inline void clampFloatChunkDispatch(const float* src, float* dst,
size_t n, float lo, float hi) {
CV_CPU_DISPATCH(clampFloatChunk_, (src, dst, n, lo, hi),
CV_CPU_DISPATCH_MODES_ALL);
}
}}}
#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline {
#define CV_CPU_OPTIMIZATION_NAMESPACE_END }
#undef CV_CPU_DISPATCH_MODES_ALL
#include "layers_common.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include <opencv2/core/hal/interface.h>
@@ -67,6 +84,9 @@ public:
return backendId == DNN_BACKEND_OPENCV;
}
// Clip rewrites values in place of the input layout.
virtual bool alwaysSupportInplace() const CV_OVERRIDE { return true; }
bool getMemoryShapes(const std::vector<MatShape> &inputs,
const int requiredOutputs,
std::vector<MatShape> &outputs,
@@ -122,6 +142,28 @@ public:
double actualMax = dynMax ? getScalar(inputs[2]) : (hasMax ? maxValue : typeMax(data.depth()));
CV_Assert(actualMin <= actualMax);
// Fused single-pass clamp for contiguous CV_32F. Halves memory traffic
// vs the cv::max + cv::min pair, and lets the allocator run it in-place.
if (data.depth() == CV_32F && data.isContinuous() && dst.isContinuous() &&
data.total() == dst.total()) {
const float lo = (float)actualMin;
const float hi = (float)actualMax;
const size_t total = data.total();
const float* src = data.ptr<float>();
float* out = dst.ptr<float>();
const size_t CHUNK = 16384;
int nChunks = (int)((total + CHUNK - 1) / CHUNK);
parallel_for_(Range(0, nChunks), [&](const Range& r) {
for (int c = r.start; c < r.end; c++) {
size_t start = (size_t)c * CHUNK;
size_t end = std::min(start + CHUNK, total);
clampFloatChunkDispatch(src + start, out + start,
end - start, lo, hi);
}
});
return;
}
Scalar lowS = Scalar::all(actualMin);
Scalar highS = Scalar::all(actualMax);
cv::max(data, lowS, dst);
@@ -5,8 +5,12 @@
// Third party copyrights are property of their respective owners.
#include <opencv2/dnn/all_layers.hpp>
#include <opencv2/dnn/shape_utils.hpp>
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/core/fast_math.hpp"
#include <math.h>
#include <cfloat>
#include <algorithm>
namespace cv {
namespace dnn {
@@ -14,6 +18,12 @@ CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
cv::dnn::ActivationFunc getActivationFunc_(int type);
// Per-row softmax over a contiguous Mat axis.
void softmax_(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep, float scale);
// Fused clamp on a single contiguous chunk, 4x unrolled. Used by clip_layer.
void clampFloatChunk_(const float* src, float* dst, size_t n, float lo, float hi);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
@@ -300,6 +310,132 @@ static void activationClip(const void* input, void* output,
}
}
void clampFloatChunk_(const float* src, float* dst, size_t n, float lo, float hi) {
size_t i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int lanes = VTraits<v_float32>::vlanes();
v_float32 vlo = vx_setall_f32(lo);
v_float32 vhi = vx_setall_f32(hi);
for (; i + lanes * 4 <= n; i += lanes * 4) {
v_store(dst + i, v_min(v_max(vx_load(src + i), vlo), vhi));
v_store(dst + i + lanes, v_min(v_max(vx_load(src + i + lanes), vlo), vhi));
v_store(dst + i + lanes * 2, v_min(v_max(vx_load(src + i + lanes * 2), vlo), vhi));
v_store(dst + i + lanes * 3, v_min(v_max(vx_load(src + i + lanes * 3), vlo), vhi));
}
for (; i + lanes <= n; i += lanes)
v_store(dst + i, v_min(v_max(vx_load(src + i), vlo), vhi));
#endif
for (; i < n; i++)
dst[i] = std::min(std::max(src[i], lo), hi);
}
void softmax_(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep, float scale) {
CV_Assert(src.type() == CV_32F);
CV_Assert(src.isContinuous() && dst.isContinuous());
CV_Assert(src.size == dst.size);
axis = normalize_axis(axis, src.dims);
size_t outerSize = src.total(0, axis),
innerSize = src.total(axis + 1);
const float *srcPtr = src.ptr<float>();
float *dstPtr = dst.ptr<float>();
size_t outerStep = src.total(axis);
size_t cnStep = src.total(axis + 1);
// multi-threads: weight by axisStep so axis=-1 with small outerSize*innerSize
// (e.g. [4,256,13294] -> 1024 tasks of 13294 elems) still parallelizes.
size_t totalTasks = outerSize * innerSize;
double nstripes = (double) totalTasks * (double) axisStep / 8192.0;
if (nstripes < 1.0) nstripes = 1.0;
size_t channelAxis = (axisStep + 7) & -8;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int nlanes = VTraits<v_float32>::vlanes();
#endif
parallel_for_(Range(0, (int) totalTasks), [&](const Range &range) {
AutoBuffer<float> axisBuf_(channelAxis);
float *axisBuf = axisBuf_.data();
for (size_t i = range.start; i < range.end; i++) {
size_t outerDim = i / innerSize;
size_t innerDim = i % innerSize;
size_t srcOffset = outerDim * outerStep + innerDim;
size_t _cnDim = 0;
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
for (; _cnDim + 3 < axisStep; _cnDim += 4) {
axisBuf[_cnDim + 0] = srcPtr[srcOffset + (_cnDim + 0 + axisBias) * cnStep];
axisBuf[_cnDim + 1] = srcPtr[srcOffset + (_cnDim + 1 + axisBias) * cnStep];
axisBuf[_cnDim + 2] = srcPtr[srcOffset + (_cnDim + 2 + axisBias) * cnStep];
axisBuf[_cnDim + 3] = srcPtr[srcOffset + (_cnDim + 3 + axisBias) * cnStep];
}
#endif
for (; _cnDim < axisStep; _cnDim++)
axisBuf[_cnDim] = srcPtr[srcOffset + (_cnDim + axisBias) * cnStep];
float maxVal = -FLT_MAX;
int cnDim = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 vmax = vx_setall_f32(-FLT_MAX);
for (; cnDim < axisStep; cnDim += nlanes) {
if (cnDim > axisStep - nlanes) {
if (cnDim == 0) { break; }
cnDim = axisStep - nlanes;
}
v_float32 val = vx_load(axisBuf + cnDim);
vmax = v_max(vmax, val);
}
maxVal = v_reduce_max(vmax);
#endif
for (; cnDim < axisStep; cnDim++) {
maxVal = std::max(maxVal, axisBuf[cnDim]);
}
// Fuse the input scale into the centered exp: softmax(x*scale) where
// exp((x - maxVal) * scale) is numerically equivalent to pre-multiplying.
float s = 0.f;
cnDim = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 vs = vx_setzero_f32();
vmax = vx_setall_f32(maxVal);
v_float32 vscale = vx_setall_f32(scale);
for (; cnDim <= axisStep - nlanes; cnDim += nlanes) {
v_float32 val = vx_load(axisBuf + cnDim);
val = v_mul(v_sub(val, vmax), vscale);
val = v_exp(val);
vs = v_add(vs, val);
v_store(axisBuf + cnDim, val);
}
s = v_reduce_sum(vs);
#endif
for (; cnDim < axisStep; cnDim++) {
axisBuf[cnDim] = expf((axisBuf[cnDim] - maxVal) * scale);
s += axisBuf[cnDim];
}
_cnDim = 0;
if (s == 0.f || cvIsInf(1.f / s)) {
for (; _cnDim < axisStep; _cnDim++)
dstPtr[srcOffset + (_cnDim + axisBias) * cnStep] = 0.f;
} else {
s = 1.f / s;
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
for (; _cnDim + 3 < axisStep; _cnDim += 4) {
dstPtr[srcOffset + (_cnDim + 0 + axisBias) * cnStep] = axisBuf[_cnDim + 0] * s;
dstPtr[srcOffset + (_cnDim + 1 + axisBias) * cnStep] = axisBuf[_cnDim + 1] * s;
dstPtr[srcOffset + (_cnDim + 2 + axisBias) * cnStep] = axisBuf[_cnDim + 2] * s;
dstPtr[srcOffset + (_cnDim + 3 + axisBias) * cnStep] = axisBuf[_cnDim + 3] * s;
}
#endif
for (; _cnDim < axisStep; _cnDim++)
dstPtr[srcOffset + (_cnDim + axisBias) * cnStep] = axisBuf[_cnDim] * s;
}
}
}, nstripes);
}
ActivationFunc getActivationFunc_(int type)
{
switch (type) {
@@ -0,0 +1,695 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include <opencv2/core.hpp>
#include "opencv2/core/hal/intrin.hpp"
#include <cfloat>
#include <algorithm>
#include <cmath>
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// padding values: 0 = zeros, 1 = border, 2 = reflection.
void gridSampleBilinear2D_f32_(
const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding);
void gridSampleNearest2D_f32_(
const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding);
void gridSampleBicubic2D_f32_(
const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding, float cubic_alpha);
void gridSampleBilinear3D_f32_(
const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W, int Dout, int Hout, int Wout,
bool align_corners, int padding);
void gridSampleNearest3D_f32_(
const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W, int Dout, int Hout, int Wout,
bool align_corners, int padding);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
namespace gs_simd_internal {
enum { GS_ZEROS = 0, GS_BORDER = 1, GS_REFLECTION = 2 };
static inline float gs_reflect(float x, int limit, bool align_corners)
{
if (limit <= 1) return 0.f;
const float minv = align_corners ? 0.f : -0.5f;
const float maxv = align_corners ? float(limit - 1) : float(limit) - 0.5f;
const float m = maxv - minv;
const float two_m = 2.f * m;
float t = fmodf(x - minv, two_m);
if (t < 0) t += two_m;
if (t > m) t = two_m - t;
return t + minv;
}
static inline void gs_cubic_coeffs(float x, float A, float* c)
{
c[0] = ((A*(x + 1.0f) - 5.0f*A)*(x + 1.0f) + 8.0f*A)*(x + 1.0f) - 4.0f*A;
c[1] = ((A + 2.0f)*x - (A + 3.0f))*x*x + 1.0f;
c[2] = ((A + 2.0f)*(1.0f - x) - (A + 3.0f))*(1.0f - x)*(1.0f - x) + 1.0f;
c[3] = 1.0f - c[0] - c[1] - c[2];
}
static inline void gs_xy_params(int W, int H, bool align_corners,
float& xs, float& ys, float& xd, float& yd)
{
const float delta = align_corners ? 1.f : 0.f;
xs = 0.5f * (W - delta);
ys = 0.5f * (H - delta);
xd = 0.5f * (W - delta) + 0.5f * (delta - 1.f);
yd = 0.5f * (H - delta) + 0.5f * (delta - 1.f);
}
static inline void gs_xyz_params(int W, int H, int D, bool align_corners,
float& xs, float& ys, float& zs,
float& xd, float& yd, float& zd)
{
const float delta = align_corners ? 1.f : 0.f;
xs = 0.5f * (W - delta);
ys = 0.5f * (H - delta);
zs = 0.5f * (D - delta);
xd = 0.5f * (W - delta) + 0.5f * (delta - 1.f);
yd = 0.5f * (H - delta) + 0.5f * (delta - 1.f);
zd = 0.5f * (D - delta) + 0.5f * (delta - 1.f);
}
template<int PAD>
static inline float gs_fetch2D(const float* baseNC, int yy, int xx,
int H, int W, size_t xHStride, bool align_corners)
{
int xi = xx, yi = yy;
if (PAD == GS_BORDER) {
xi = std::max(0, std::min(W - 1, xi));
yi = std::max(0, std::min(H - 1, yi));
} else if (PAD == GS_REFLECTION) {
xi = saturate_cast<int>(std::floor(gs_reflect((float)xi, W, align_corners) + 0.5f));
yi = saturate_cast<int>(std::floor(gs_reflect((float)yi, H, align_corners) + 0.5f));
}
if (xi < 0 || yi < 0 || xi >= W || yi >= H) return 0.f;
return baseNC[(size_t)yi * xHStride + (size_t)xi];
}
template<int PAD>
static inline float gs_fetch3D(const float* baseNC, int zz, int yy, int xx,
int D, int H, int W,
size_t xDStride, size_t xHStride, bool align_corners)
{
int xi = xx, yi = yy, zi = zz;
if (PAD == GS_BORDER) {
xi = std::max(0, std::min(W - 1, xi));
yi = std::max(0, std::min(H - 1, yi));
zi = std::max(0, std::min(D - 1, zi));
} else if (PAD == GS_REFLECTION) {
xi = saturate_cast<int>(std::floor(gs_reflect((float)xi, W, align_corners) + 0.5f));
yi = saturate_cast<int>(std::floor(gs_reflect((float)yi, H, align_corners) + 0.5f));
zi = saturate_cast<int>(std::floor(gs_reflect((float)zi, D, align_corners) + 0.5f));
}
if (xi < 0 || yi < 0 || zi < 0 || xi >= W || yi >= H || zi >= D) return 0.f;
return baseNC[(size_t)zi * xDStride + (size_t)yi * xHStride + (size_t)xi];
}
template<int PAD>
static inline void bilinear2D(const float* X, const float* G, float* Y,
int N, int C, int H, int W,
int Hout, int Wout, bool align_corners)
{
const size_t xNStride = (size_t)C * H * W;
const size_t xCStride = (size_t)H * W;
const size_t xHStride = (size_t)W;
const size_t gNStride = (size_t)Hout * Wout * 2;
const size_t gHStride = (size_t)Wout * 2;
const size_t yNStride = (size_t)C * Hout * Wout;
const size_t yCStride = (size_t)Hout * Wout;
const size_t yHStride = (size_t)Wout;
float xscale, yscale, xdelta, ydelta;
gs_xy_params(W, H, align_corners, xscale, yscale, xdelta, ydelta);
parallel_for_(Range(0, N * Hout), [&](const Range& r) {
for (int idx = r.start; idx < r.end; idx++) {
int n = idx / Hout, h = idx - n * Hout;
const float* baseN = X + (size_t)n * xNStride;
const float* gRow = G + (size_t)n * gNStride + (size_t)h * gHStride;
float* outBase = Y + (size_t)n * yNStride + (size_t)h * yHStride;
for (int w = 0; w < Wout; w++) {
float xf = gRow[w * 2 + 0] * xscale + xdelta;
float yf = gRow[w * 2 + 1] * yscale + ydelta;
int x0 = saturate_cast<int>(floorf(xf));
int y0 = saturate_cast<int>(floorf(yf));
float fx = xf - x0, fy = yf - y0;
float w00 = (1.f - fx) * (1.f - fy);
float w01 = fx * (1.f - fy);
float w10 = (1.f - fx) * fy;
float w11 = fx * fy;
bool interior = (x0 >= 0 && y0 >= 0 && x0 + 1 < W && y0 + 1 < H);
int c = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (interior) {
const int L = VTraits<v_float32>::vlanes();
const size_t off00 = (size_t)y0 * xHStride + (size_t)x0;
const size_t off01 = off00 + 1;
const size_t off10 = off00 + xHStride;
const size_t off11 = off10 + 1;
const v_float32 vw00 = vx_setall_f32(w00);
const v_float32 vw01 = vx_setall_f32(w01);
const v_float32 vw10 = vx_setall_f32(w10);
const v_float32 vw11 = vx_setall_f32(w11);
for (; c + L <= C; c += L) {
float CV_DECL_ALIGNED(32) b00[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b01[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b10[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b11[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) bo[VTraits<v_float32>::max_nlanes];
for (int k = 0; k < L; k++) {
const float* pNC = baseN + (size_t)(c + k) * xCStride;
b00[k] = pNC[off00];
b01[k] = pNC[off01];
b10[k] = pNC[off10];
b11[k] = pNC[off11];
}
v_float32 V00 = vx_load_aligned(b00);
v_float32 V01 = vx_load_aligned(b01);
v_float32 V10 = vx_load_aligned(b10);
v_float32 V11 = vx_load_aligned(b11);
v_float32 R = v_add(v_add(v_mul(V00, vw00), v_mul(V01, vw01)),
v_add(v_mul(V10, vw10), v_mul(V11, vw11)));
v_store_aligned(bo, R);
for (int k = 0; k < L; k++) {
outBase[(size_t)(c + k) * yCStride + (size_t)w] = bo[k];
}
}
}
#endif
for (; c < C; c++) {
const float* baseNC = baseN + (size_t)c * xCStride;
float v00, v01, v10, v11;
if (interior) {
const float* py0 = baseNC + (size_t)y0 * xHStride;
const float* py1 = py0 + xHStride;
v00 = py0[x0]; v01 = py0[x0 + 1];
v10 = py1[x0]; v11 = py1[x0 + 1];
} else {
v00 = gs_fetch2D<PAD>(baseNC, y0, x0, H, W, xHStride, align_corners);
v01 = gs_fetch2D<PAD>(baseNC, y0, x0 + 1, H, W, xHStride, align_corners);
v10 = gs_fetch2D<PAD>(baseNC, y0 + 1, x0, H, W, xHStride, align_corners);
v11 = gs_fetch2D<PAD>(baseNC, y0 + 1, x0 + 1, H, W, xHStride, align_corners);
}
outBase[(size_t)c * yCStride + (size_t)w] = w00 * v00 + w01 * v01 + w10 * v10 + w11 * v11;
}
}
}
});
}
template<int PAD>
static inline void nearest2D(const float* X, const float* G, float* Y,
int N, int C, int H, int W,
int Hout, int Wout, bool align_corners)
{
const size_t xNStride = (size_t)C * H * W;
const size_t xCStride = (size_t)H * W;
const size_t xHStride = (size_t)W;
const size_t gNStride = (size_t)Hout * Wout * 2;
const size_t gHStride = (size_t)Wout * 2;
const size_t yNStride = (size_t)C * Hout * Wout;
const size_t yCStride = (size_t)Hout * Wout;
const size_t yHStride = (size_t)Wout;
float xscale, yscale, xdelta, ydelta;
gs_xy_params(W, H, align_corners, xscale, yscale, xdelta, ydelta);
parallel_for_(Range(0, N * Hout), [&](const Range& r) {
for (int idx = r.start; idx < r.end; idx++) {
int n = idx / Hout, h = idx - n * Hout;
const float* baseN = X + (size_t)n * xNStride;
const float* gRow = G + (size_t)n * gNStride + (size_t)h * gHStride;
float* outBase = Y + (size_t)n * yNStride + (size_t)h * yHStride;
for (int w = 0; w < Wout; w++) {
float xf = gRow[w * 2 + 0] * xscale + xdelta;
float yf = gRow[w * 2 + 1] * yscale + ydelta;
int xi = cvRound(xf);
int yi = cvRound(yf);
bool interior = (xi >= 0 && yi >= 0 && xi < W && yi < H);
int c = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (interior) {
const int L = VTraits<v_float32>::vlanes();
const size_t off = (size_t)yi * xHStride + (size_t)xi;
for (; c + L <= C; c += L) {
float CV_DECL_ALIGNED(32) b[VTraits<v_float32>::max_nlanes];
for (int k = 0; k < L; k++) {
b[k] = baseN[(size_t)(c + k) * xCStride + off];
}
for (int k = 0; k < L; k++) {
outBase[(size_t)(c + k) * yCStride + (size_t)w] = b[k];
}
}
}
#endif
for (; c < C; c++) {
const float* baseNC = baseN + (size_t)c * xCStride;
float v;
if (interior) v = baseNC[(size_t)yi * xHStride + (size_t)xi];
else v = gs_fetch2D<PAD>(baseNC, yi, xi, H, W, xHStride, align_corners);
outBase[(size_t)c * yCStride + (size_t)w] = v;
}
}
}
});
}
template<int PAD>
static inline void bicubic2D(const float* X, const float* G, float* Y,
int N, int C, int H, int W,
int Hout, int Wout, bool align_corners,
float cubic_alpha)
{
const size_t xNStride = (size_t)C * H * W;
const size_t xCStride = (size_t)H * W;
const size_t xHStride = (size_t)W;
const size_t gNStride = (size_t)Hout * Wout * 2;
const size_t gHStride = (size_t)Wout * 2;
const size_t yNStride = (size_t)C * Hout * Wout;
const size_t yCStride = (size_t)Hout * Wout;
const size_t yHStride = (size_t)Wout;
float xscale, yscale, xdelta, ydelta;
gs_xy_params(W, H, align_corners, xscale, yscale, xdelta, ydelta);
parallel_for_(Range(0, N * Hout), [&](const Range& r) {
for (int idx = r.start; idx < r.end; idx++) {
int n = idx / Hout, h = idx - n * Hout;
const float* baseN = X + (size_t)n * xNStride;
const float* gRow = G + (size_t)n * gNStride + (size_t)h * gHStride;
float* outBase = Y + (size_t)n * yNStride + (size_t)h * yHStride;
for (int w = 0; w < Wout; w++) {
float xf = gRow[w * 2 + 0] * xscale + xdelta;
float yf = gRow[w * 2 + 1] * yscale + ydelta;
int x1 = saturate_cast<int>(floorf(xf));
int y1 = saturate_cast<int>(floorf(yf));
float tx = xf - x1, ty = yf - y1;
float wx[4], wy[4];
gs_cubic_coeffs(tx, cubic_alpha, wx);
gs_cubic_coeffs(ty, cubic_alpha, wy);
bool interior = (x1 >= 1 && y1 >= 1 && x1 + 2 < W && y1 + 2 < H);
int c = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (interior) {
const int L = VTraits<v_float32>::vlanes();
const v_float32 vwx0 = vx_setall_f32(wx[0]);
const v_float32 vwx1 = vx_setall_f32(wx[1]);
const v_float32 vwx2 = vx_setall_f32(wx[2]);
const v_float32 vwx3 = vx_setall_f32(wx[3]);
const v_float32 vwy0 = vx_setall_f32(wy[0]);
const v_float32 vwy1 = vx_setall_f32(wy[1]);
const v_float32 vwy2 = vx_setall_f32(wy[2]);
const v_float32 vwy3 = vx_setall_f32(wy[3]);
for (; c + L <= C; c += L) {
float CV_DECL_ALIGNED(32) a[16][VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) bo[VTraits<v_float32>::max_nlanes];
for (int k = 0; k < L; k++) {
const float* pNC = baseN + (size_t)(c + k) * xCStride;
for (int j = 0; j < 4; j++) {
const float* p = pNC + (size_t)(y1 - 1 + j) * xHStride + (size_t)(x1 - 1);
a[j*4 + 0][k] = p[0];
a[j*4 + 1][k] = p[1];
a[j*4 + 2][k] = p[2];
a[j*4 + 3][k] = p[3];
}
}
v_float32 row0, row1, row2, row3;
v_float32 mn, mx;
{
v_float32 c0 = vx_load_aligned(a[0]);
v_float32 c1 = vx_load_aligned(a[1]);
v_float32 c2 = vx_load_aligned(a[2]);
v_float32 c3 = vx_load_aligned(a[3]);
row0 = v_add(v_add(v_mul(c0, vwx0), v_mul(c1, vwx1)),
v_add(v_mul(c2, vwx2), v_mul(c3, vwx3)));
mn = v_min(v_min(c0, c1), v_min(c2, c3));
mx = v_max(v_max(c0, c1), v_max(c2, c3));
}
{
v_float32 c0 = vx_load_aligned(a[4]);
v_float32 c1 = vx_load_aligned(a[5]);
v_float32 c2 = vx_load_aligned(a[6]);
v_float32 c3 = vx_load_aligned(a[7]);
row1 = v_add(v_add(v_mul(c0, vwx0), v_mul(c1, vwx1)),
v_add(v_mul(c2, vwx2), v_mul(c3, vwx3)));
mn = v_min(mn, v_min(v_min(c0, c1), v_min(c2, c3)));
mx = v_max(mx, v_max(v_max(c0, c1), v_max(c2, c3)));
}
{
v_float32 c0 = vx_load_aligned(a[8]);
v_float32 c1 = vx_load_aligned(a[9]);
v_float32 c2 = vx_load_aligned(a[10]);
v_float32 c3 = vx_load_aligned(a[11]);
row2 = v_add(v_add(v_mul(c0, vwx0), v_mul(c1, vwx1)),
v_add(v_mul(c2, vwx2), v_mul(c3, vwx3)));
mn = v_min(mn, v_min(v_min(c0, c1), v_min(c2, c3)));
mx = v_max(mx, v_max(v_max(c0, c1), v_max(c2, c3)));
}
{
v_float32 c0 = vx_load_aligned(a[12]);
v_float32 c1 = vx_load_aligned(a[13]);
v_float32 c2 = vx_load_aligned(a[14]);
v_float32 c3 = vx_load_aligned(a[15]);
row3 = v_add(v_add(v_mul(c0, vwx0), v_mul(c1, vwx1)),
v_add(v_mul(c2, vwx2), v_mul(c3, vwx3)));
mn = v_min(mn, v_min(v_min(c0, c1), v_min(c2, c3)));
mx = v_max(mx, v_max(v_max(c0, c1), v_max(c2, c3)));
}
v_float32 R = v_add(v_add(v_mul(row0, vwy0), v_mul(row1, vwy1)),
v_add(v_mul(row2, vwy2), v_mul(row3, vwy3)));
R = v_max(mn, v_min(R, mx));
v_store_aligned(bo, R);
for (int k = 0; k < L; k++) {
outBase[(size_t)(c + k) * yCStride + (size_t)w] = bo[k];
}
}
}
#endif
for (; c < C; c++) {
const float* baseNC = baseN + (size_t)c * xCStride;
float a[4][4];
float minv = FLT_MAX, maxv = -FLT_MAX;
if (interior) {
const float* p = baseNC + (size_t)(y1 - 1) * xHStride + (size_t)(x1 - 1);
for (int j = 0; j < 4; j++) {
a[j][0] = p[0]; a[j][1] = p[1]; a[j][2] = p[2]; a[j][3] = p[3];
p += xHStride;
}
} else {
for (int j = 0; j < 4; j++) {
a[j][0] = gs_fetch2D<PAD>(baseNC, y1 - 1 + j, x1 - 1, H, W, xHStride, align_corners);
a[j][1] = gs_fetch2D<PAD>(baseNC, y1 - 1 + j, x1, H, W, xHStride, align_corners);
a[j][2] = gs_fetch2D<PAD>(baseNC, y1 - 1 + j, x1 + 1, H, W, xHStride, align_corners);
a[j][3] = gs_fetch2D<PAD>(baseNC, y1 - 1 + j, x1 + 2, H, W, xHStride, align_corners);
}
}
float rowv[4];
for (int j = 0; j < 4; j++) {
rowv[j] = a[j][0] * wx[0] + a[j][1] * wx[1] + a[j][2] * wx[2] + a[j][3] * wx[3];
for (int i = 0; i < 4; i++) {
minv = std::min(minv, a[j][i]);
maxv = std::max(maxv, a[j][i]);
}
}
float outv = rowv[0] * wy[0] + rowv[1] * wy[1] + rowv[2] * wy[2] + rowv[3] * wy[3];
outv = std::max(minv, std::min(outv, maxv));
outBase[(size_t)c * yCStride + (size_t)w] = outv;
}
}
}
});
}
template<int PAD>
static inline void bilinear3D(const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W,
int Dout, int Hout, int Wout, bool align_corners)
{
const size_t xNStride = (size_t)C * D * H * W;
const size_t xCStride = (size_t)D * H * W;
const size_t xDStride = (size_t)H * W;
const size_t xHStride = (size_t)W;
const size_t gNStride = (size_t)Dout * Hout * Wout * 3;
const size_t gDStride = (size_t)Hout * Wout * 3;
const size_t gHStride = (size_t)Wout * 3;
const size_t yNStride = (size_t)C * Dout * Hout * Wout;
const size_t yCStride = (size_t)Dout * Hout * Wout;
const size_t yDStride = (size_t)Hout * Wout;
const size_t yHStride = (size_t)Wout;
float xscale, yscale, zscale, xdelta, ydelta, zdelta;
gs_xyz_params(W, H, D, align_corners, xscale, yscale, zscale, xdelta, ydelta, zdelta);
parallel_for_(Range(0, N * Dout * Hout), [&](const Range& r) {
for (int idx = r.start; idx < r.end; idx++) {
int n = idx / (Dout * Hout);
int rem = idx - n * Dout * Hout;
int d = rem / Hout;
int h = rem - d * Hout;
const float* baseN = X + (size_t)n * xNStride;
const float* gRow = G + (size_t)n * gNStride + (size_t)d * gDStride + (size_t)h * gHStride;
float* outBase = Y + (size_t)n * yNStride + (size_t)d * yDStride + (size_t)h * yHStride;
for (int w = 0; w < Wout; w++) {
float xf = gRow[w * 3 + 0] * xscale + xdelta;
float yf = gRow[w * 3 + 1] * yscale + ydelta;
float zf = gRow[w * 3 + 2] * zscale + zdelta;
int x0 = saturate_cast<int>(floorf(xf));
int y0 = saturate_cast<int>(floorf(yf));
int z0 = saturate_cast<int>(floorf(zf));
float dx = xf - x0, dy = yf - y0, dz = zf - z0;
float w000 = (1.f - dx) * (1.f - dy) * (1.f - dz);
float w001 = dx * (1.f - dy) * (1.f - dz);
float w010 = (1.f - dx) * dy * (1.f - dz);
float w011 = dx * dy * (1.f - dz);
float w100 = (1.f - dx) * (1.f - dy) * dz;
float w101 = dx * (1.f - dy) * dz;
float w110 = (1.f - dx) * dy * dz;
float w111 = dx * dy * dz;
bool interior = (x0 >= 0 && y0 >= 0 && z0 >= 0 &&
x0 + 1 < W && y0 + 1 < H && z0 + 1 < D);
int c = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (interior) {
const int L = VTraits<v_float32>::vlanes();
const size_t off000 = (size_t)z0 * xDStride + (size_t)y0 * xHStride + (size_t)x0;
const size_t off001 = off000 + 1;
const size_t off010 = off000 + xHStride;
const size_t off011 = off010 + 1;
const size_t off100 = off000 + xDStride;
const size_t off101 = off100 + 1;
const size_t off110 = off100 + xHStride;
const size_t off111 = off110 + 1;
const v_float32 vw000 = vx_setall_f32(w000);
const v_float32 vw001 = vx_setall_f32(w001);
const v_float32 vw010 = vx_setall_f32(w010);
const v_float32 vw011 = vx_setall_f32(w011);
const v_float32 vw100 = vx_setall_f32(w100);
const v_float32 vw101 = vx_setall_f32(w101);
const v_float32 vw110 = vx_setall_f32(w110);
const v_float32 vw111 = vx_setall_f32(w111);
for (; c + L <= C; c += L) {
float CV_DECL_ALIGNED(32) b000[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b001[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b010[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b011[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b100[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b101[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b110[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) b111[VTraits<v_float32>::max_nlanes];
float CV_DECL_ALIGNED(32) bo[VTraits<v_float32>::max_nlanes];
for (int k = 0; k < L; k++) {
const float* pNC = baseN + (size_t)(c + k) * xCStride;
b000[k] = pNC[off000]; b001[k] = pNC[off001];
b010[k] = pNC[off010]; b011[k] = pNC[off011];
b100[k] = pNC[off100]; b101[k] = pNC[off101];
b110[k] = pNC[off110]; b111[k] = pNC[off111];
}
v_float32 V000 = vx_load_aligned(b000);
v_float32 V001 = vx_load_aligned(b001);
v_float32 V010 = vx_load_aligned(b010);
v_float32 V011 = vx_load_aligned(b011);
v_float32 V100 = vx_load_aligned(b100);
v_float32 V101 = vx_load_aligned(b101);
v_float32 V110 = vx_load_aligned(b110);
v_float32 V111 = vx_load_aligned(b111);
v_float32 R = v_add(
v_add(v_add(v_mul(V000, vw000), v_mul(V001, vw001)),
v_add(v_mul(V010, vw010), v_mul(V011, vw011))),
v_add(v_add(v_mul(V100, vw100), v_mul(V101, vw101)),
v_add(v_mul(V110, vw110), v_mul(V111, vw111))));
v_store_aligned(bo, R);
for (int k = 0; k < L; k++) {
outBase[(size_t)(c + k) * yCStride + (size_t)w] = bo[k];
}
}
}
#endif
for (; c < C; c++) {
const float* baseNC = baseN + (size_t)c * xCStride;
float v000, v001, v010, v011, v100, v101, v110, v111;
if (interior) {
const float* pz0 = baseNC + (size_t)z0 * xDStride;
const float* pz1 = pz0 + xDStride;
const float* p00 = pz0 + (size_t)y0 * xHStride;
const float* p01 = pz0 + (size_t)(y0 + 1) * xHStride;
const float* p10 = pz1 + (size_t)y0 * xHStride;
const float* p11 = pz1 + (size_t)(y0 + 1) * xHStride;
v000 = p00[x0]; v001 = p00[x0 + 1];
v010 = p01[x0]; v011 = p01[x0 + 1];
v100 = p10[x0]; v101 = p10[x0 + 1];
v110 = p11[x0]; v111 = p11[x0 + 1];
} else {
v000 = gs_fetch3D<PAD>(baseNC, z0, y0, x0, D, H, W, xDStride, xHStride, align_corners);
v001 = gs_fetch3D<PAD>(baseNC, z0, y0, x0 + 1, D, H, W, xDStride, xHStride, align_corners);
v010 = gs_fetch3D<PAD>(baseNC, z0, y0 + 1, x0, D, H, W, xDStride, xHStride, align_corners);
v011 = gs_fetch3D<PAD>(baseNC, z0, y0 + 1, x0 + 1, D, H, W, xDStride, xHStride, align_corners);
v100 = gs_fetch3D<PAD>(baseNC, z0 + 1, y0, x0, D, H, W, xDStride, xHStride, align_corners);
v101 = gs_fetch3D<PAD>(baseNC, z0 + 1, y0, x0 + 1, D, H, W, xDStride, xHStride, align_corners);
v110 = gs_fetch3D<PAD>(baseNC, z0 + 1, y0 + 1, x0, D, H, W, xDStride, xHStride, align_corners);
v111 = gs_fetch3D<PAD>(baseNC, z0 + 1, y0 + 1, x0 + 1, D, H, W, xDStride, xHStride, align_corners);
}
outBase[(size_t)c * yCStride + (size_t)w] =
w000 * v000 + w001 * v001 + w010 * v010 + w011 * v011 +
w100 * v100 + w101 * v101 + w110 * v110 + w111 * v111;
}
}
}
});
}
template<int PAD>
static inline void nearest3D(const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W,
int Dout, int Hout, int Wout, bool align_corners)
{
const size_t xNStride = (size_t)C * D * H * W;
const size_t xCStride = (size_t)D * H * W;
const size_t xDStride = (size_t)H * W;
const size_t xHStride = (size_t)W;
const size_t gNStride = (size_t)Dout * Hout * Wout * 3;
const size_t gDStride = (size_t)Hout * Wout * 3;
const size_t gHStride = (size_t)Wout * 3;
const size_t yNStride = (size_t)C * Dout * Hout * Wout;
const size_t yCStride = (size_t)Dout * Hout * Wout;
const size_t yDStride = (size_t)Hout * Wout;
const size_t yHStride = (size_t)Wout;
float xscale, yscale, zscale, xdelta, ydelta, zdelta;
gs_xyz_params(W, H, D, align_corners, xscale, yscale, zscale, xdelta, ydelta, zdelta);
parallel_for_(Range(0, N * Dout * Hout), [&](const Range& r) {
for (int idx = r.start; idx < r.end; idx++) {
int n = idx / (Dout * Hout);
int rem = idx - n * Dout * Hout;
int d = rem / Hout;
int h = rem - d * Hout;
const float* baseN = X + (size_t)n * xNStride;
const float* gRow = G + (size_t)n * gNStride + (size_t)d * gDStride + (size_t)h * gHStride;
float* outBase = Y + (size_t)n * yNStride + (size_t)d * yDStride + (size_t)h * yHStride;
for (int w = 0; w < Wout; w++) {
float xf = gRow[w * 3 + 0] * xscale + xdelta;
float yf = gRow[w * 3 + 1] * yscale + ydelta;
float zf = gRow[w * 3 + 2] * zscale + zdelta;
int xi = cvRound(xf), yi = cvRound(yf), zi = cvRound(zf);
bool interior = (xi >= 0 && yi >= 0 && zi >= 0 && xi < W && yi < H && zi < D);
int c = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (interior) {
const int L = VTraits<v_float32>::vlanes();
const size_t off = (size_t)zi * xDStride + (size_t)yi * xHStride + (size_t)xi;
for (; c + L <= C; c += L) {
float CV_DECL_ALIGNED(32) b[VTraits<v_float32>::max_nlanes];
for (int k = 0; k < L; k++) {
b[k] = baseN[(size_t)(c + k) * xCStride + off];
}
for (int k = 0; k < L; k++) {
outBase[(size_t)(c + k) * yCStride + (size_t)w] = b[k];
}
}
}
#endif
for (; c < C; c++) {
const float* baseNC = baseN + (size_t)c * xCStride;
float v;
if (interior) v = baseNC[(size_t)zi * xDStride + (size_t)yi * xHStride + (size_t)xi];
else v = gs_fetch3D<PAD>(baseNC, zi, yi, xi, D, H, W, xDStride, xHStride, align_corners);
outBase[(size_t)c * yCStride + (size_t)w] = v;
}
}
}
});
}
} // namespace gs_simd_internal
void gridSampleBilinear2D_f32_(const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding)
{
using namespace gs_simd_internal;
if (padding == GS_ZEROS) bilinear2D<GS_ZEROS>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
else if (padding == GS_BORDER) bilinear2D<GS_BORDER>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
else bilinear2D<GS_REFLECTION>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
}
void gridSampleNearest2D_f32_(const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding)
{
using namespace gs_simd_internal;
if (padding == GS_ZEROS) nearest2D<GS_ZEROS>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
else if (padding == GS_BORDER) nearest2D<GS_BORDER>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
else nearest2D<GS_REFLECTION>(X, G, Y, N, C, H, W, Hout, Wout, align_corners);
}
void gridSampleBicubic2D_f32_(const float* X, const float* G, float* Y,
int N, int C, int H, int W, int Hout, int Wout,
bool align_corners, int padding, float cubic_alpha)
{
using namespace gs_simd_internal;
if (padding == GS_ZEROS) bicubic2D<GS_ZEROS>(X, G, Y, N, C, H, W, Hout, Wout, align_corners, cubic_alpha);
else if (padding == GS_BORDER) bicubic2D<GS_BORDER>(X, G, Y, N, C, H, W, Hout, Wout, align_corners, cubic_alpha);
else bicubic2D<GS_REFLECTION>(X, G, Y, N, C, H, W, Hout, Wout, align_corners, cubic_alpha);
}
void gridSampleBilinear3D_f32_(const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W, int Dout, int Hout, int Wout,
bool align_corners, int padding)
{
using namespace gs_simd_internal;
if (padding == GS_ZEROS) bilinear3D<GS_ZEROS>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
else if (padding == GS_BORDER) bilinear3D<GS_BORDER>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
else bilinear3D<GS_REFLECTION>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
}
void gridSampleNearest3D_f32_(const float* X, const float* G, float* Y,
int N, int C, int D, int H, int W, int Dout, int Hout, int Wout,
bool align_corners, int padding)
{
using namespace gs_simd_internal;
if (padding == GS_ZEROS) nearest3D<GS_ZEROS>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
else if (padding == GS_BORDER) nearest3D<GS_BORDER>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
else nearest3D<GS_REFLECTION>(X, G, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners);
}
CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // cv::dnn
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
@@ -0,0 +1,121 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include <opencv2/core.hpp>
#include "opencv2/core/hal/intrin.hpp"
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// Op codes that simd_binop_f32_ understands.
enum SimdBinOp { SIMD_BIN_ADD = 0, SIMD_BIN_SUB = 1, SIMD_BIN_MUL = 2, SIMD_BIN_DIV = 3 };
// Apply binary op on n contiguous floats.
int simd_binop_f32_(const float* a, const float* b, float* out, int n, int op);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
int simd_binop_f32_(const float* a, const float* b, float* out, int n, int op) {
int i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int lanes = VTraits<v_float32>::vlanes();
if (op == SIMD_BIN_ADD) {
for (; i <= n - lanes * 4; i += lanes * 4) {
v_float32 a0 = vx_load(a + i), b0 = vx_load(b + i);
v_float32 a1 = vx_load(a + i + lanes), b1 = vx_load(b + i + lanes);
v_float32 a2 = vx_load(a + i + lanes * 2), b2 = vx_load(b + i + lanes * 2);
v_float32 a3 = vx_load(a + i + lanes * 3), b3 = vx_load(b + i + lanes * 3);
v_float32 r0 = v_add(a0, b0), r1 = v_add(a1, b1);
v_float32 r2 = v_add(a2, b2), r3 = v_add(a3, b3);
vx_store(out + i, r0); vx_store(out + i + lanes, r1);
vx_store(out + i + lanes * 2, r2); vx_store(out + i + lanes * 3, r3);
}
for (; i <= n - lanes; i += lanes)
vx_store(out + i, v_add(vx_load(a + i), vx_load(b + i)));
if (i < n && n >= lanes) {
i = n - lanes;
vx_store(out + i, v_add(vx_load(a + i), vx_load(b + i)));
i = n;
}
} else if (op == SIMD_BIN_MUL) {
for (; i <= n - lanes * 4; i += lanes * 4) {
v_float32 a0 = vx_load(a + i), b0 = vx_load(b + i);
v_float32 a1 = vx_load(a + i + lanes), b1 = vx_load(b + i + lanes);
v_float32 a2 = vx_load(a + i + lanes * 2), b2 = vx_load(b + i + lanes * 2);
v_float32 a3 = vx_load(a + i + lanes * 3), b3 = vx_load(b + i + lanes * 3);
v_float32 r0 = v_mul(a0, b0), r1 = v_mul(a1, b1);
v_float32 r2 = v_mul(a2, b2), r3 = v_mul(a3, b3);
vx_store(out + i, r0); vx_store(out + i + lanes, r1);
vx_store(out + i + lanes * 2, r2); vx_store(out + i + lanes * 3, r3);
}
for (; i <= n - lanes; i += lanes)
vx_store(out + i, v_mul(vx_load(a + i), vx_load(b + i)));
if (i < n && n >= lanes) {
i = n - lanes;
vx_store(out + i, v_mul(vx_load(a + i), vx_load(b + i)));
i = n;
}
} else if (op == SIMD_BIN_SUB) {
for (; i <= n - lanes * 4; i += lanes * 4) {
v_float32 a0 = vx_load(a + i), b0 = vx_load(b + i);
v_float32 a1 = vx_load(a + i + lanes), b1 = vx_load(b + i + lanes);
v_float32 a2 = vx_load(a + i + lanes * 2), b2 = vx_load(b + i + lanes * 2);
v_float32 a3 = vx_load(a + i + lanes * 3), b3 = vx_load(b + i + lanes * 3);
v_float32 r0 = v_sub(a0, b0), r1 = v_sub(a1, b1);
v_float32 r2 = v_sub(a2, b2), r3 = v_sub(a3, b3);
vx_store(out + i, r0); vx_store(out + i + lanes, r1);
vx_store(out + i + lanes * 2, r2); vx_store(out + i + lanes * 3, r3);
}
for (; i <= n - lanes; i += lanes)
vx_store(out + i, v_sub(vx_load(a + i), vx_load(b + i)));
if (i < n && n >= lanes) {
i = n - lanes;
vx_store(out + i, v_sub(vx_load(a + i), vx_load(b + i)));
i = n;
}
} else if (op == SIMD_BIN_DIV) {
for (; i <= n - lanes * 4; i += lanes * 4) {
v_float32 a0 = vx_load(a + i), b0 = vx_load(b + i);
v_float32 a1 = vx_load(a + i + lanes), b1 = vx_load(b + i + lanes);
v_float32 a2 = vx_load(a + i + lanes * 2), b2 = vx_load(b + i + lanes * 2);
v_float32 a3 = vx_load(a + i + lanes * 3), b3 = vx_load(b + i + lanes * 3);
v_float32 r0 = v_div(a0, b0), r1 = v_div(a1, b1);
v_float32 r2 = v_div(a2, b2), r3 = v_div(a3, b3);
vx_store(out + i, r0); vx_store(out + i + lanes, r1);
vx_store(out + i + lanes * 2, r2); vx_store(out + i + lanes * 3, r3);
}
for (; i <= n - lanes; i += lanes)
vx_store(out + i, v_div(vx_load(a + i), vx_load(b + i)));
if (i < n && n >= lanes) {
i = n - lanes;
vx_store(out + i, v_div(vx_load(a + i), vx_load(b + i)));
i = n;
}
}
vx_cleanup();
#endif
if (op == SIMD_BIN_ADD) {
for (; i < n; i++) out[i] = a[i] + b[i];
} else if (op == SIMD_BIN_MUL) {
for (; i < n; i++) out[i] = a[i] * b[i];
} else if (op == SIMD_BIN_SUB) {
for (; i < n; i++) out[i] = a[i] - b[i];
} else {
for (; i < n; i++) out[i] = a[i] / b[i];
}
return n;
}
CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // cv::dnn
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
@@ -0,0 +1,299 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/dnn/all_layers.hpp>
#include "opencv2/core/hal/intrin.hpp"
#include <cfloat>
#include <cmath>
#include <vector>
#include <algorithm>
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// Reduce-all over a contiguous CV_32F src
void reduceAllFloatParallel_(const Mat& src, Mat& dst, int reduce_type);
// Reduce a contiguous trailing block of axes for CV_32F src.
void reduceLastAxesFloatParallel_(const Mat& src, Mat& dst, size_t innerLen, int reduce_type);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
void reduceAllFloatParallel_(const Mat& src, Mat& dst, int reduce_type_int) {
const Reduce2Layer::ReduceType rt = (Reduce2Layer::ReduceType)reduce_type_int;
const float* p = src.ptr<const float>();
const size_t total = src.total();
const int nThreads = std::max(1, cv::getNumThreads());
const int stripes = std::max(1, std::min<int>(
(int)((total + 16383) / 16384), nThreads));
float init_f = 0.0f;
if (rt == Reduce2Layer::ReduceType::MAX) init_f = -FLT_MAX;
else if (rt == Reduce2Layer::ReduceType::MIN) init_f = FLT_MAX;
else if (rt == Reduce2Layer::ReduceType::PROD) init_f = 1.0f;
std::vector<float> partial_f(stripes, init_f);
parallel_for_(Range(0, stripes), [&](const Range& r) {
for (int s = r.start; s < r.end; s++) {
size_t start = (size_t)s * total / stripes;
size_t end = (size_t)(s + 1) * total / stripes;
const float* q = p + start;
size_t n = end - start, i = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int L = VTraits<v_float32>::vlanes();
#endif
switch (rt) {
case Reduce2Layer::ReduceType::MAX: {
float acc = -FLT_MAX;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setall_f32(-FLT_MAX);
for (; i + L <= n; i += L) va = v_max(va, vx_load(q + i));
acc = v_reduce_max(va);
#endif
for (; i < n; i++) acc = acc > q[i] ? acc : q[i];
partial_f[s] = acc;
break;
}
case Reduce2Layer::ReduceType::MIN: {
float acc = FLT_MAX;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setall_f32(FLT_MAX);
for (; i + L <= n; i += L) va = v_min(va, vx_load(q + i));
acc = v_reduce_min(va);
#endif
for (; i < n; i++) acc = acc < q[i] ? acc : q[i];
partial_f[s] = acc;
break;
}
case Reduce2Layer::ReduceType::SUM:
case Reduce2Layer::ReduceType::MEAN:
case Reduce2Layer::ReduceType::LOG_SUM: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) va = v_add(va, vx_load(q + i));
acc += v_reduce_sum(va);
#endif
for (; i < n; i++) acc += q[i];
partial_f[s] = acc;
break;
}
case Reduce2Layer::ReduceType::L1: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) va = v_add(va, v_abs(vx_load(q + i)));
acc += v_reduce_sum(va);
#endif
for (; i < n; i++) acc += std::fabs(q[i]);
partial_f[s] = acc;
break;
}
case Reduce2Layer::ReduceType::L2:
case Reduce2Layer::ReduceType::SUM_SQUARE: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) {
v_float32 x = vx_load(q + i);
va = v_add(va, v_mul(x, x));
}
acc += v_reduce_sum(va);
#endif
for (; i < n; i++) { float x = q[i]; acc += x * x; }
partial_f[s] = acc;
break;
}
case Reduce2Layer::ReduceType::PROD: {
float acc = 1.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 va = vx_setall_f32(1.0f);
for (; i + L <= n; i += L) va = v_mul(va, vx_load(q + i));
float buf[VTraits<v_float32>::max_nlanes];
v_store(buf, va);
for (int k = 0; k < L; k++) acc *= buf[k];
#endif
for (; i < n; i++) acc *= q[i];
partial_f[s] = acc;
break;
}
default: CV_Error(Error::StsInternal, "reduceAllFloatParallel_: unhandled type");
}
}
});
float* out = dst.ptr<float>();
switch (rt) {
case Reduce2Layer::ReduceType::MAX: {
float m = -FLT_MAX;
for (int s = 0; s < stripes; s++) m = m > partial_f[s] ? m : partial_f[s];
*out = m;
break;
}
case Reduce2Layer::ReduceType::MIN: {
float m = FLT_MAX;
for (int s = 0; s < stripes; s++) m = m < partial_f[s] ? m : partial_f[s];
*out = m;
break;
}
case Reduce2Layer::ReduceType::SUM: {
float acc = 0.0f; for (int s = 0; s < stripes; s++) acc += partial_f[s];
*out = acc;
break;
}
case Reduce2Layer::ReduceType::MEAN: {
float acc = 0.0f; for (int s = 0; s < stripes; s++) acc += partial_f[s];
*out = total > 0 ? (float)((double)acc / (double)total) : 0.0f;
break;
}
case Reduce2Layer::ReduceType::L1:
case Reduce2Layer::ReduceType::SUM_SQUARE: {
float acc = 0.0f; for (int s = 0; s < stripes; s++) acc += partial_f[s];
*out = acc;
break;
}
case Reduce2Layer::ReduceType::L2: {
float acc = 0.0f; for (int s = 0; s < stripes; s++) acc += partial_f[s];
*out = std::sqrt(acc);
break;
}
case Reduce2Layer::ReduceType::LOG_SUM: {
float acc = 0.0f; for (int s = 0; s < stripes; s++) acc += partial_f[s];
*out = total > 0 ? std::log(acc) : -std::numeric_limits<float>::infinity();
break;
}
case Reduce2Layer::ReduceType::PROD: {
float acc = 1.0f; for (int s = 0; s < stripes; s++) acc *= partial_f[s];
*out = acc;
break;
}
default: CV_Error(Error::StsInternal, "reduceAllFloatParallel_: unhandled type");
}
}
void reduceLastAxesFloatParallel_(const Mat& src, Mat& dst, size_t innerLen, int reduce_type_int) {
const Reduce2Layer::ReduceType rt = (Reduce2Layer::ReduceType)reduce_type_int;
const float* p = src.ptr<const float>();
float* q = dst.ptr<float>();
const size_t nOut = src.total() / innerLen;
const float inv_inner = innerLen > 0 ? 1.0f / (float)innerLen : 0.0f;
parallel_for_(Range(0, (int)nOut), [&](const Range& r) {
for (int row = r.start; row < r.end; row++) {
const float* s0 = p + (size_t)row * innerLen;
size_t i = 0, n = innerLen;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int L = VTraits<v_float32>::vlanes();
#endif
switch (rt) {
case Reduce2Layer::ReduceType::MAX: {
float acc = -FLT_MAX;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setall_f32(-FLT_MAX);
for (; i + L <= n; i += L) va = v_max(va, vx_load(s0 + i));
acc = v_reduce_max(va);
}
#endif
for (; i < n; i++) acc = acc > s0[i] ? acc : s0[i];
q[row] = acc;
break;
}
case Reduce2Layer::ReduceType::MIN: {
float acc = FLT_MAX;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setall_f32(FLT_MAX);
for (; i + L <= n; i += L) va = v_min(va, vx_load(s0 + i));
acc = v_reduce_min(va);
}
#endif
for (; i < n; i++) acc = acc < s0[i] ? acc : s0[i];
q[row] = acc;
break;
}
case Reduce2Layer::ReduceType::SUM:
case Reduce2Layer::ReduceType::MEAN:
case Reduce2Layer::ReduceType::LOG_SUM: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) va = v_add(va, vx_load(s0 + i));
acc += v_reduce_sum(va);
}
#endif
for (; i < n; i++) acc += s0[i];
if (rt == Reduce2Layer::ReduceType::MEAN) q[row] = acc * inv_inner;
else if (rt == Reduce2Layer::ReduceType::LOG_SUM) q[row] = std::log(acc);
else q[row] = acc;
break;
}
case Reduce2Layer::ReduceType::L1: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) va = v_add(va, v_abs(vx_load(s0 + i)));
acc += v_reduce_sum(va);
}
#endif
for (; i < n; i++) acc += std::fabs(s0[i]);
q[row] = acc;
break;
}
case Reduce2Layer::ReduceType::L2:
case Reduce2Layer::ReduceType::SUM_SQUARE: {
float acc = 0.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setzero_f32();
for (; i + L <= n; i += L) {
v_float32 x = vx_load(s0 + i);
va = v_add(va, v_mul(x, x));
}
acc += v_reduce_sum(va);
}
#endif
for (; i < n; i++) { float x = s0[i]; acc += x * x; }
q[row] = (rt == Reduce2Layer::ReduceType::L2) ? std::sqrt(acc) : acc;
break;
}
case Reduce2Layer::ReduceType::PROD: {
float acc = 1.0f;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (n >= (size_t)L) {
v_float32 va = vx_setall_f32(1.0f);
for (; i + L <= n; i += L) va = v_mul(va, vx_load(s0 + i));
float buf[VTraits<v_float32>::max_nlanes];
v_store(buf, va);
for (int k = 0; k < L; k++) acc *= buf[k];
}
#endif
for (; i < n; i++) acc *= s0[i];
q[row] = acc;
break;
}
default: CV_Error(Error::StsInternal, "reduceLastAxesFloatParallel_: unhandled type");
}
}
}, (double)nOut * (double)innerLen / 16384.0);
}
CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // cv::dnn
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
+9 -126
View File
@@ -11,138 +11,21 @@
#include "../../precomp.hpp"
#include "softmax.hpp"
#include "opencv2/core/fast_math.hpp"
#define CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
#include "activation_kernels.simd.hpp"
#include "layers/cpu_kernels/activation_kernels.simd_declarations.hpp"
#undef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn {
void softmax(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep, float scale){
CV_Assert(src.type() == CV_32F);
CV_Assert(src.isContinuous() && dst.isContinuous());
CV_Assert(src.size == dst.size);
axis = normalize_axis(axis, src.dims);
const bool scaled = scale != 1.f;
size_t outerSize = src.total(0, axis),
innerSize = src.total(axis + 1);
const float *srcPtr = src.ptr<float>();
float *dstPtr = dst.ptr<float>();
size_t outerStep = src.total(axis);
size_t cnStep = src.total(axis + 1);
// multi-threads
size_t totalTasks = outerSize * innerSize;
double nstripes = (double) totalTasks / 1024.0;
// make the channel axis to be multiple of 8
size_t channelAxis = (axisStep + 7) & -8;
#if (CV_SIMD || CV_SIMD_SCALABLE)
const int nlanes = VTraits<v_float32>::vlanes();
#endif
parallel_for_(Range(0, (int) totalTasks), [&](const Range &range) {
AutoBuffer<float> axisBuf_(channelAxis);
float *axisBuf = axisBuf_.data();
for (size_t i = range.start; i < range.end; i++) {
size_t outerDim = i / innerSize;
size_t innerDim = i % innerSize;
size_t srcOffset = outerDim * outerStep + innerDim;
// copy data from src to buf along axis, since the data may not be continuous
size_t _cnDim = 0;
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
for (; _cnDim + 3 < axisStep; _cnDim += 4) {
axisBuf[_cnDim + 0] = srcPtr[srcOffset + (_cnDim + 0 + axisBias) * cnStep];
axisBuf[_cnDim + 1] = srcPtr[srcOffset + (_cnDim + 1 + axisBias) * cnStep];
axisBuf[_cnDim + 2] = srcPtr[srcOffset + (_cnDim + 2 + axisBias) * cnStep];
axisBuf[_cnDim + 3] = srcPtr[srcOffset + (_cnDim + 3 + axisBias) * cnStep];
}
#endif
for (; _cnDim < axisStep; _cnDim++)
axisBuf[_cnDim] = srcPtr[srcOffset + (_cnDim + axisBias) * cnStep];
// Bake the fused pre-Softmax scale into the buffer once. axisBuf
// already lives in L1 from the load above, so this pass is short.
if (scaled) {
int sk = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
v_float32 vscale = vx_setall_f32(scale);
for (; sk <= axisStep - nlanes; sk += nlanes) {
v_float32 val = vx_load(axisBuf + sk);
val = v_mul(val, vscale);
v_store(axisBuf + sk, val);
}
#endif
for (; sk < axisStep; sk++)
axisBuf[sk] *= scale;
}
float maxVal = -FLT_MAX;
int cnDim = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
// calculate the max value along the axis
v_float32 vmax = vx_setall_f32(-FLT_MAX);
for (; cnDim < axisStep; cnDim += nlanes) {
if (cnDim > axisStep - nlanes) {
if (cnDim == 0) { break; }
cnDim = axisStep - nlanes;
}
v_float32 val = vx_load(axisBuf + cnDim);
vmax = v_max(vmax, val);
}
maxVal = v_reduce_max(vmax);
#endif
for (; cnDim < axisStep; cnDim++) {
maxVal = std::max(maxVal, axisBuf[cnDim]);
}
float s = 0.f;
cnDim = 0;
#if (CV_SIMD || CV_SIMD_SCALABLE)
// calculate the exp value along the axis
v_float32 vs = vx_setzero_f32();
vmax = vx_setall_f32(maxVal);
// calculate and sum all data along axis
for (; cnDim <= axisStep - nlanes; cnDim += nlanes) {
// cannot apply halide trick here due to axisBuf is constantly updated
v_float32 val = vx_load(axisBuf + cnDim);
val = v_sub(val, vmax);
val = v_exp(val);
vs = v_add(vs, val);
v_store(axisBuf + cnDim, val);
}
s = v_reduce_sum(vs);
#endif
for (; cnDim < axisStep; cnDim++) {
axisBuf[cnDim] = expf(axisBuf[cnDim] - maxVal);
s += axisBuf[cnDim];
}
// copy back the result to src
_cnDim = 0;
if (s == 0.f || cvIsInf(1.f / s)) {
for (; _cnDim < axisStep; _cnDim++)
dstPtr[srcOffset + (_cnDim + axisBias) * cnStep] = 0.f;
} else {
s = 1.f / s;
#if CV_ENABLE_UNROLLED && defined(_M_ARM64)
for (; _cnDim + 3 < axisStep; _cnDim += 4) {
dstPtr[srcOffset + (_cnDim + 0 + axisBias) * cnStep] = axisBuf[_cnDim + 0] * s;
dstPtr[srcOffset + (_cnDim + 1 + axisBias) * cnStep] = axisBuf[_cnDim + 1] * s;
dstPtr[srcOffset + (_cnDim + 2 + axisBias) * cnStep] = axisBuf[_cnDim + 2] * s;
dstPtr[srcOffset + (_cnDim + 3 + axisBias) * cnStep] = axisBuf[_cnDim + 3] * s;
}
#endif
for (; _cnDim < axisStep; _cnDim++)
dstPtr[srcOffset + (_cnDim + axisBias) * cnStep] = axisBuf[_cnDim] * s;
}
}
}, nstripes);
void softmax(Mat &dst, const Mat &src, int axis, int axisBias, int axisStep, float scale) {
CV_CPU_DISPATCH(softmax_, (dst, src, axis, axisBias, axisStep, scale),
CV_CPU_DISPATCH_MODES_ALL);
}
void softmax(Mat &dst, const Mat &src, int axis) {
softmax(dst, src, axis, 0, src.size[axis]);
softmax(dst, src, axis, 0, src.size[axis], 1.f);
}
void softmax(Mat &dst, const Mat &src, int axis, float scale) {
@@ -0,0 +1,123 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include "opencv2/core/hal/intrin.hpp"
#include <cstdint>
#include <algorithm>
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
// Blocked 2D float transpose.
void transpose2D_f32_(const float* inp, float* out,
int64_t outer, int64_t rows, int64_t cols);
CV_CPU_OPTIMIZATION_NAMESPACE_END
}}
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
namespace cv { namespace dnn {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
#if (CV_SIMD || CV_SIMD_SCALABLE)
// AVX2 8x8 f32 in-register transpose. v_transpose4x4 transposes the two
// 128-bit halves independently; v_combine_low/high then exchange halves so
// element k of each target row comes from the right source row.
static inline void transpose_8x8_f32_avx2_(const float* src, int64_t src_stride,
float* dst, int64_t dst_stride)
{
v_float32 r0 = vx_load(src + 0 * src_stride);
v_float32 r1 = vx_load(src + 1 * src_stride);
v_float32 r2 = vx_load(src + 2 * src_stride);
v_float32 r3 = vx_load(src + 3 * src_stride);
v_float32 r4 = vx_load(src + 4 * src_stride);
v_float32 r5 = vx_load(src + 5 * src_stride);
v_float32 r6 = vx_load(src + 6 * src_stride);
v_float32 r7 = vx_load(src + 7 * src_stride);
v_float32 a0, a1, a2, a3, a4, a5, a6, a7;
v_transpose4x4(r0, r1, r2, r3, a0, a1, a2, a3);
v_transpose4x4(r4, r5, r6, r7, a4, a5, a6, a7);
v_float32 b0 = v_combine_low (a0, a4);
v_float32 b1 = v_combine_low (a1, a5);
v_float32 b2 = v_combine_low (a2, a6);
v_float32 b3 = v_combine_low (a3, a7);
v_float32 b4 = v_combine_high(a0, a4);
v_float32 b5 = v_combine_high(a1, a5);
v_float32 b6 = v_combine_high(a2, a6);
v_float32 b7 = v_combine_high(a3, a7);
v_store(dst + 0 * dst_stride, b0);
v_store(dst + 1 * dst_stride, b1);
v_store(dst + 2 * dst_stride, b2);
v_store(dst + 3 * dst_stride, b3);
v_store(dst + 4 * dst_stride, b4);
v_store(dst + 5 * dst_stride, b5);
v_store(dst + 6 * dst_stride, b6);
v_store(dst + 7 * dst_stride, b7);
}
#endif
void transpose2D_f32_(const float* inp, float* out,
int64_t outer, int64_t rows, int64_t cols)
{
const int TILE = 32;
int64_t batchStride = rows * cols;
int64_t rtiles = (rows + TILE - 1) / TILE;
int64_t ctiles = (cols + TILE - 1) / TILE;
int64_t total = outer * rtiles * ctiles;
parallel_for_(Range(0, (int)total), [&](const Range& range) {
for (int64_t idx = range.start; idx < range.end; idx++) {
int64_t rem = idx;
int64_t b = rem / (rtiles * ctiles);
rem -= b * rtiles * ctiles;
int64_t ti = rem / ctiles;
int64_t tj = rem - ti * ctiles;
int64_t r0 = ti * TILE, r1 = std::min(r0 + TILE, rows);
int64_t c0 = tj * TILE, c1 = std::min(c0 + TILE, cols);
const float* inB = inp + b * batchStride;
float* outB = out + b * batchStride;
#if (CV_SIMD || CV_SIMD_SCALABLE)
if (VTraits<v_float32>::vlanes() == 8) {
int64_t r = r0;
for (; r + 8 <= r1; r += 8) {
int64_t c = c0;
for (; c + 8 <= c1; c += 8) {
transpose_8x8_f32_avx2_(inB + c * rows + r, rows,
outB + r * cols + c, cols);
}
for (; c < c1; c++) {
for (int k = 0; k < 8; k++)
outB[(r + k) * cols + c] = inB[c * rows + (r + k)];
}
}
for (; r < r1; r++) {
for (int64_t c = c0; c < c1; c++)
outB[r * cols + c] = inB[c * rows + r];
}
continue;
}
#endif
for (int64_t r = r0; r < r1; r++) {
for (int64_t c = c0; c < c1; c++)
outB[r * cols + c] = inB[c * rows + r];
}
}
});
}
CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // cv::dnn
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
@@ -5,6 +5,14 @@
// Third party copyrights are property of their respective owners.
#include "../precomp.hpp"
// Dispatch infrastructure for the SIMD GridSample kernels.
#include "cpu_kernels/gridsample_kernels.simd.hpp"
#include "layers/cpu_kernels/gridsample_kernels.simd_declarations.hpp"
#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline {
#define CV_CPU_OPTIMIZATION_NAMESPACE_END }
#undef CV_CPU_DISPATCH_MODES_ALL
#include "layers_common.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include <opencv2/core/utility.hpp>
@@ -234,6 +242,7 @@ static inline void gridSampleComputeRows(
});
}
template<typename T>
static inline void gridSampleDispatch(
const T* Xptr,
@@ -245,6 +254,24 @@ static inline void gridSampleDispatch(
int mode, int padding,
float cubic_alpha)
{
if (std::is_same<T, float>::value) {
const float* X = reinterpret_cast<const float*>(Xptr);
float* Y = reinterpret_cast<float*>(Yptr);
if (mode == M_NEAREST) {
CV_CPU_DISPATCH(gridSampleNearest2D_f32_,
(X, Gptr, Y, N, C, H, W, Hout, Wout, align_corners, padding),
NEON, AVX2, AVX, BASELINE);
} else if (mode == M_BILINEAR) {
CV_CPU_DISPATCH(gridSampleBilinear2D_f32_,
(X, Gptr, Y, N, C, H, W, Hout, Wout, align_corners, padding),
NEON, AVX2, AVX, BASELINE);
} else {
CV_CPU_DISPATCH(gridSampleBicubic2D_f32_,
(X, Gptr, Y, N, C, H, W, Hout, Wout, align_corners, padding, cubic_alpha),
NEON, AVX2, AVX, BASELINE);
}
return;
}
if (mode == M_NEAREST) {
if (padding == P_ZEROS) gridSampleComputeRows<T, M_NEAREST, P_ZEROS>(Xptr, Gptr, Yptr, N, C, H, W, Hout, Wout, align_corners, cubic_alpha);
else if (padding == P_BORDER) gridSampleComputeRows<T, M_NEAREST, P_BORDER>(Xptr, Gptr, Yptr, N, C, H, W, Hout, Wout, align_corners, cubic_alpha);
@@ -387,6 +414,7 @@ static inline void gridSampleCompute3D(
});
}
template<typename T>
static inline void gridSampleDispatch3D(
const T* Xptr,
@@ -397,6 +425,22 @@ static inline void gridSampleDispatch3D(
bool align_corners,
int mode, int padding)
{
if (std::is_same<T, float>::value) {
const float* X = reinterpret_cast<const float*>(Xptr);
float* Y = reinterpret_cast<float*>(Yptr);
if (mode == M_NEAREST) {
CV_CPU_DISPATCH(gridSampleNearest3D_f32_,
(X, Gptr, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners, padding),
NEON, AVX2, AVX, BASELINE);
} else if (mode == M_BILINEAR) {
CV_CPU_DISPATCH(gridSampleBilinear3D_f32_,
(X, Gptr, Y, N, C, D, H, W, Dout, Hout, Wout, align_corners, padding),
NEON, AVX2, AVX, BASELINE);
} else {
CV_Error(Error::StsNotImplemented, "GridSample bicubic mode is not supported for 5D inputs");
}
return;
}
if (mode == M_NEAREST) {
if (padding == P_ZEROS) gridSampleCompute3D<T, M_NEAREST, P_ZEROS>(Xptr, Gptr, Yptr, N, C, D, H, W, Dout, Hout, Wout, align_corners);
else if (padding == P_BORDER) gridSampleCompute3D<T, M_NEAREST, P_BORDER>(Xptr, Gptr, Yptr, N, C, D, H, W, Dout, Hout, Wout, align_corners);
+94 -76
View File
@@ -3,6 +3,13 @@
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "cpu_kernels/nary_eltwise_kernels.simd.hpp"
#include "layers/cpu_kernels/nary_eltwise_kernels.simd_declarations.hpp"
#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline {
#define CV_CPU_OPTIMIZATION_NAMESPACE_END }
#undef CV_CPU_DISPATCH_MODES_ALL
#include "../net_impl.hpp"
#include "layers_common.hpp"
#include "../op_cuda.hpp"
@@ -36,6 +43,13 @@ static int _mod(int x, int y) {
return res;
}
// Wrapper that turns the CV_CPU_DISPATCH return-chain into a normal call.
static inline int simd_binop_f32_dispatch(const float* a, const float* b,
float* out, int n, int op) {
CV_CPU_DISPATCH(simd_binop_f32_, (a, b, out, n, op),
NEON, AVX2, AVX, BASELINE);
}
}
class NaryEltwiseHelper CV_FINAL
@@ -450,10 +464,32 @@ public:
}
template <typename T, typename RESULT_T, typename Functor>
void binary_forward_impl(const Functor& op, int ndims, const std::vector<int>& shape,
const char* data1, const std::vector<size_t>& step1,
const char* data2, const std::vector<size_t>& step2,
char* data, const std::vector<size_t>& step, size_t block_size) {
void binary_forward_impl(const Functor& op, int ndims, const std::vector<int>& shape_in,
const char* data1, const std::vector<size_t>& step1_in,
const char* data2, const std::vector<size_t>& step2_in,
char* data, const std::vector<size_t>& step_in, size_t block_size) {
// Collapse consecutive dims that are contiguous in all three tensors.
std::vector<int> shape(shape_in.begin(), shape_in.begin() + ndims);
std::vector<size_t> step1(step1_in.begin(), step1_in.begin() + ndims);
std::vector<size_t> step2(step2_in.begin(), step2_in.begin() + ndims);
std::vector<size_t> step (step_in .begin(), step_in .begin() + ndims);
for (int k = (int)shape.size() - 2; k >= 0; k--) {
size_t e1 = (size_t)shape[k + 1] * step1[k + 1];
size_t e2 = (size_t)shape[k + 1] * step2[k + 1];
size_t e = (size_t)shape[k + 1] * step [k + 1];
if (step1[k] == e1 && step2[k] == e2 && step[k] == e) {
shape[k] *= shape[k + 1];
step1[k] = step1[k + 1];
step2[k] = step2[k + 1];
step [k] = step [k + 1];
shape.erase(shape.begin() + k + 1);
step1.erase(step1.begin() + k + 1);
step2.erase(step2.begin() + k + 1);
step .erase(step .begin() + k + 1);
}
}
ndims = (int)shape.size();
size_t dp1 = 0, dp2 = 0, dp = 0;
int plane_size = 1;
size_t nplanes = 1;
@@ -469,46 +505,35 @@ public:
}
}
#if CV_SIMD
// Fast path: fully contiguous float Add → flatten + SIMD + parallel_for_
bool is_add = (this->op == OPERATION::SUM || this->op == OPERATION::ADD);
if (is_add && std::is_same<T, float>::value && std::is_same<RESULT_T, float>::value &&
dp1 == 1 && dp2 == 1 && dp == 1 && ndims >= 1) {
bool contiguous = true;
for (int k = ndims - 2; k >= 0; k--) {
if (shape[k] <= 1) continue; // size-1 dims have stride 0, skip
size_t expected = (size_t)shape[k + 1] * step1[k + 1];
if (step1[k] != expected || step2[k] != expected || step[k] != expected) {
contiguous = false;
break;
const bool is_add = (this->op == OPERATION::SUM || this->op == OPERATION::ADD);
const bool is_sub = (this->op == OPERATION::SUB);
const bool is_mul = (this->op == OPERATION::PROD);
const bool is_div = (this->op == OPERATION::DIV);
const bool simd_f32 = std::is_same<T, float>::value && std::is_same<RESULT_T, float>::value &&
(is_add || is_sub || is_mul || is_div);
const int simd_bin_op = is_add ? cpu_baseline::SIMD_BIN_ADD :
is_sub ? cpu_baseline::SIMD_BIN_SUB :
is_mul ? cpu_baseline::SIMD_BIN_MUL :
is_div ? cpu_baseline::SIMD_BIN_DIV : -1;
// Fast path: fully-flat contiguous float binop -> one big parallel SIMD loop.
if (simd_f32 && ndims == 1 && dp1 == 1 && dp2 == 1 && dp == 1) {
int64_t total = (int64_t)nplanes * plane_size;
const float* p1 = (const float*)data1;
const float* p2 = (const float*)data2;
float* po = (float*)data;
const int64_t chunk = 4096;
int64_t nchunks = (total + chunk - 1) / chunk;
parallel_for_(Range(0, (int)nchunks), [&](const Range& r) {
for (int c = r.start; c < r.end; c++) {
int64_t start = c * chunk;
int64_t end = std::min(start + chunk, total);
int n = (int)(end - start);
simd_binop_f32_dispatch(p1 + start, p2 + start, po + start, n, simd_bin_op);
}
}
if (contiguous) {
int64_t total = (int64_t)nplanes * plane_size;
const float* p1 = (const float*)data1;
const float* p2 = (const float*)data2;
float* po = (float*)data;
const int64_t chunk = 1024;
int64_t nchunks = (total + chunk - 1) / chunk;
parallel_for_(Range(0, (int)nchunks), [&](const Range& r) {
for (int c = r.start; c < r.end; c++) {
int64_t start = c * chunk;
int64_t end = std::min(start + chunk, total);
int64_t i = start;
for (; i <= end - (int64_t)VTraits<v_float32>::nlanes * 4; i += VTraits<v_float32>::nlanes * 4) {
v_store(po + i, v_add(vx_load(p1 + i), vx_load(p2 + i)));
v_store(po + i + VTraits<v_float32>::nlanes, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes), vx_load(p2 + i + VTraits<v_float32>::nlanes)));
v_store(po + i + VTraits<v_float32>::nlanes*2, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes*2), vx_load(p2 + i + VTraits<v_float32>::nlanes*2)));
v_store(po + i + VTraits<v_float32>::nlanes*3, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes*3), vx_load(p2 + i + VTraits<v_float32>::nlanes*3)));
}
for (; i < end; i++)
po[i] = p1[i] + p2[i];
}
});
return;
}
});
return;
}
#endif
if (nplanes == 1) { // parallelize within the plane
const T* ptr1 = (const T*)data1;
@@ -516,24 +541,14 @@ public:
RESULT_T* ptr = (RESULT_T*)data;
auto worker = [&](const Range &r) {
if (dp1 == 1 && dp2 == 1 && dp == 1) {
int i = r.start;
#if CV_SIMD
if (is_add && std::is_same<T, float>::value && std::is_same<RESULT_T, float>::value) {
if (simd_f32) {
const float* p1 = (const float*)(const void*)&ptr1[r.start];
const float* p2 = (const float*)(const void*)&ptr2[r.start];
float* po = (float*)(void*)&ptr[r.start];
int len = r.end - r.start, j = 0;
for (; j <= len - VTraits<v_float32>::nlanes * 4; j += VTraits<v_float32>::nlanes * 4) {
v_store(po + j, v_add(vx_load(p1 + j), vx_load(p2 + j)));
v_store(po + j + VTraits<v_float32>::nlanes, v_add(vx_load(p1 + j + VTraits<v_float32>::nlanes), vx_load(p2 + j + VTraits<v_float32>::nlanes)));
v_store(po + j + VTraits<v_float32>::nlanes*2, v_add(vx_load(p1 + j + VTraits<v_float32>::nlanes*2), vx_load(p2 + j + VTraits<v_float32>::nlanes*2)));
v_store(po + j + VTraits<v_float32>::nlanes*3, v_add(vx_load(p1 + j + VTraits<v_float32>::nlanes*3), vx_load(p2 + j + VTraits<v_float32>::nlanes*3)));
}
i = r.start + j;
}
#endif
for(; i < r.end; i++) {
ptr[i] = op(ptr1[i], ptr2[i]);
simd_binop_f32_dispatch(p1, p2, po, r.end - r.start, simd_bin_op);
} else {
for (int i = r.start; i < r.end; i++)
ptr[i] = op(ptr1[i], ptr2[i]);
}
} else if (dp1 == 1 && dp2 == 0 && dp == 1){
T x2 = *ptr2;
@@ -574,22 +589,14 @@ public:
const T* ptr2 = (const T*)ptr2_;
RESULT_T* ptr = (RESULT_T*)ptr_;
if (dp1 == 1 && dp2 == 1 && dp == 1) {
int i = 0;
#if CV_SIMD
if (is_add && std::is_same<T, float>::value && std::is_same<RESULT_T, float>::value) {
const float* p1 = (const float*)(const void*)ptr1;
const float* p2 = (const float*)(const void*)ptr2;
float* po = (float*)(void*)ptr;
for (; i <= plane_size - VTraits<v_float32>::nlanes * 4; i += VTraits<v_float32>::nlanes * 4) {
v_store(po + i, v_add(vx_load(p1 + i), vx_load(p2 + i)));
v_store(po + i + VTraits<v_float32>::nlanes, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes), vx_load(p2 + i + VTraits<v_float32>::nlanes)));
v_store(po + i + VTraits<v_float32>::nlanes*2, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes*2), vx_load(p2 + i + VTraits<v_float32>::nlanes*2)));
v_store(po + i + VTraits<v_float32>::nlanes*3, v_add(vx_load(p1 + i + VTraits<v_float32>::nlanes*3), vx_load(p2 + i + VTraits<v_float32>::nlanes*3)));
}
}
#endif
for(; i < plane_size; i++) {
ptr[i] = op(ptr1[i], ptr2[i]);
if (simd_f32) {
simd_binop_f32_dispatch((const float*)(const void*)ptr1,
(const float*)(const void*)ptr2,
(float*)(void*)ptr,
plane_size, simd_bin_op);
} else {
for (int i = 0; i < plane_size; i++)
ptr[i] = op(ptr1[i], ptr2[i]);
}
} else if (dp1 == 1 && dp2 == 0 && dp == 1){
T x2 = *ptr2;
@@ -608,7 +615,14 @@ public:
}
}
};
double nstripes = nplanes * (1.0 / double(block_size));
// Use total work (planes * plane_size) so large broadcasts parallelize
// even when nplanes alone is below the old nstripes threshold. Target
// ~16KB of output work per stripe so even tiny plane_size ops (e.g.
// 2-element broadcast rows) spread across threads instead of running
// serially.
double nstripes = (double)nplanes * plane_size * sizeof(T) / 16384.0;
if (nstripes < 1.0) nstripes = 1.0;
(void)block_size;
parallel_for_(Range(0, nplanes), worker, nstripes);
}
}
@@ -741,7 +755,9 @@ public:
}
}
};
double nstripes = nplanes * (1.0 / double(block_size));
double nstripes = (double)nplanes * plane_size * sizeof(T) / 16384.0;
if (nstripes < 1.0) nstripes = 1.0;
(void)block_size;
parallel_for_(Range(0, nplanes), worker, nstripes);
}
}
@@ -880,7 +896,9 @@ public:
}
}
};
double nstripes = nplanes * (1.0 / double(block_size));
double nstripes = (double)nplanes * plane_size * (sizeof(T_INP1) + sizeof(T_OUT)) / 16384.0;
if (nstripes < 1.0) nstripes = 1.0;
(void)block_size;
parallel_for_(Range(0, nplanes), worker, nstripes);
}
}
+1 -1
View File
@@ -147,7 +147,7 @@ public:
{
CV_Assert(in_arr.size().area() == 1);
const Mat X = in_arr.getMat(0);
CV_Assert(X.data != nullptr);
CV_Assert(X.total() == 0 || X.data != nullptr);
const int rank = X.dims;
std::vector<int> dims(rank), strides(rank);
+40
View File
@@ -5,6 +5,13 @@
// Third party copyrights are property of their respective owners.
#include "../precomp.hpp"
#include "cpu_kernels/reduce2_kernels.simd.hpp"
#include "layers/cpu_kernels/reduce2_kernels.simd_declarations.hpp"
#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline {
#define CV_CPU_OPTIMIZATION_NAMESPACE_END }
#undef CV_CPU_DISPATCH_MODES_ALL
#include <opencv2/dnn/shape_utils.hpp>
#include "../net_impl.hpp"
#include "../op_cann.hpp"
@@ -521,9 +528,42 @@ public:
outputs_arr.getMatVector(outputs);
Mat& dst = outputs[0];
if (src.depth() == CV_32F && src.isContinuous() && dst.isContinuous() &&
reduce_type != ReduceType::LOG_SUM_EXP) {
if (dst.total() == 1) {
CV_CPU_DISPATCH(reduceAllFloatParallel_, (src, dst, (int)reduce_type),
NEON, AVX2, AVX, BASELINE);
return;
}
size_t innerLen = 1;
if (reduceTrailingAxesLen(src, axes, innerLen) && innerLen > 1) {
CV_CPU_DISPATCH(reduceLastAxesFloatParallel_, (src, dst, innerLen, (int)reduce_type),
NEON, AVX2, AVX, BASELINE);
return;
}
}
typeDispatch(dst.type(), src, dst, axes, noop_with_empty_axes);
}
static bool reduceTrailingAxesLen(const Mat& src, const std::vector<int>& axes,
size_t& innerLen)
{
MatShape s = shape(src);
int nd = s.dims;
if (axes.empty() || (int)axes.size() > nd) return false;
std::vector<int> sorted_axes(axes.begin(), axes.end());
std::sort(sorted_axes.begin(), sorted_axes.end());
// Must be the trailing [nd - k .. nd - 1] block.
int k = (int)sorted_axes.size();
for (int i = 0; i < k; i++)
if (sorted_axes[i] != nd - k + i) return false;
innerLen = 1;
for (int i = nd - k; i < nd; i++) innerLen *= (size_t)s[i];
return true;
}
virtual std::ostream& dumpAttrs(std::ostream& strm, int indent) const CV_OVERRIDE
{
prindent(strm, indent);
@@ -132,6 +132,10 @@ public:
return backendId == DNN_BACKEND_OPENCV;
}
// Reshape just re-interprets the same contiguous buffer. Let the graph
// buffer allocator alias input and output so the memcpy in forward() is a no-op.
virtual bool alwaysSupportInplace() const CV_OVERRIDE { return true; }
bool haveShapeSpec() const
{
return newShapeDesc.dims >= 0;
+2
View File
@@ -48,6 +48,8 @@ public:
return backendId == DNN_BACKEND_OPENCV;
}
virtual bool alwaysSupportInplace() const CV_OVERRIDE { return true; }
MatShape getOutShape(const MatShape& inpShape, const std::vector<int>& axes_) const
{
bool squeezeMask[MatShape::MAX_DIMS];
@@ -3,6 +3,12 @@
// of this distribution and at http://opencv.org/license.html.
#include "../precomp.hpp"
#include "cpu_kernels/transpose_kernels.simd.hpp"
#include "layers/cpu_kernels/transpose_kernels.simd_declarations.hpp"
#define CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN namespace cpu_baseline {
#define CV_CPU_OPTIMIZATION_NAMESPACE_END }
#undef CV_CPU_DISPATCH_MODES_ALL
#include "layers_common.hpp"
#include "../net_impl.hpp"
//#include "../op_cuda.hpp"
@@ -26,6 +32,26 @@ namespace dnn
Opset's 1 to 23 are covered.
*/
// Detect a "swap innermost two dims" permutation, collapsing leading dims.
// Returns true and fills outer/rows/cols when applicable.
static bool isSwapLastTwo(const MatShape& inpShape,
const std::vector<int>& perm,
int esz,
int64_t& outer, int64_t& rows, int64_t& cols)
{
if (esz != 4) return false;
int nd = inpShape.dims;
if (nd < 2 || (int)perm.size() != nd) return false;
for (int i = 0; i < nd - 2; i++)
if (perm[i] != i) return false;
if (perm[nd - 2] != nd - 1 || perm[nd - 1] != nd - 2) return false;
outer = 1;
for (int i = 0; i < nd - 2; i++) outer *= inpShape[i];
rows = inpShape[nd - 1]; // output rows = input inner dim
cols = inpShape[nd - 2]; // output cols = input outer-of-pair dim
return true;
}
static void transpose(const Mat& inp, const std::vector<int>& perm, Mat& out)
{
enum {TRANSPOSE_MAX_DIMS=7};
@@ -35,6 +61,65 @@ static void transpose(const Mat& inp, const std::vector<int>& perm, Mat& out)
size_t esz = inp.elemSize();
CV_Assert(esz == 1 || esz == 2 || esz == 4 || esz == 8);
int64_t outer = 1, rows = 0, cols = 0;
if (isSwapLastTwo(inpShape, perm, (int)esz, outer, rows, cols)
&& inp.isContinuous() && out.isContinuous()) {
CV_CPU_DISPATCH(transpose2D_f32_,
(inp.ptr<float>(), out.ptr<float>(), outer, rows, cols),
NEON, AVX2, AVX, BASELINE);
return;
}
if (inp.isContinuous() && out.isContinuous() &&
ndims >= 2 && (int)perm.size() == ndims &&
perm[ndims - 1] == ndims - 1)
{
std::vector<int64_t> inStride(ndims, 0);
inStride[ndims - 1] = 1;
for (int i = ndims - 2; i >= 0; i--)
inStride[i] = inStride[i + 1] * (int64_t)inpShape[i + 1];
const int64_t inner = inpShape[ndims - 1];
std::vector<int> outOuterShape(ndims - 1);
for (int i = 0; i < ndims - 1; i++) outOuterShape[i] = outShape[i];
int64_t outerTotal = 1;
for (int i = 0; i < ndims - 1; i++) outerTotal *= outOuterShape[i];
if (outerTotal > 0 && inner > 0) {
const size_t innerBytes = (size_t)inner * esz;
const char* in_base = (const char*)inp.data;
char* out_base = (char*)out.data;
parallel_for_(Range(0, (int)outerTotal), [&](const Range& r) {
std::vector<int> outIdx(ndims - 1, 0);
// initialize outIdx to r.start in row-major
int64_t rem = r.start;
for (int k = ndims - 2; k >= 0; k--) {
outIdx[k] = (int)(rem % outOuterShape[k]);
rem /= outOuterShape[k];
}
for (int64_t idx = r.start; idx < r.end; idx++) {
// Compute input outer offset using perm.
int64_t inOff = 0;
for (int i = 0; i < ndims - 1; i++) {
// perm[i] is the input axis providing this output axis.
inOff += (int64_t)outIdx[i] * inStride[perm[i]];
}
// Output position: linear idx * inner.
std::memcpy(out_base + (size_t)idx * innerBytes,
in_base + (size_t)inOff * esz,
innerBytes);
// Advance outIdx (row-major over outOuterShape).
for (int k = ndims - 2; k >= 0; k--) {
if (++outIdx[k] < outOuterShape[k]) break;
outIdx[k] = 0;
}
}
}, std::max(1.0, (double)outerTotal * (double)innerBytes / 16384.0));
return;
}
}
int perm_[TRANSPOSE_MAX_DIMS];
int inpShape_[TRANSPOSE_MAX_DIMS];
int outShape_[TRANSPOSE_MAX_DIMS];
@@ -48,6 +48,8 @@ public:
return backendId == DNN_BACKEND_OPENCV;
}
virtual bool alwaysSupportInplace() const CV_OVERRIDE { return true; }
MatShape getOutShape(const MatShape& inpShape, const std::vector<int>& axes_) const
{
bool unsqueezeMask[MatShape::MAX_DIMS];
+2 -1
View File
@@ -1245,8 +1245,9 @@ void Net::Impl::forwardGraph(Ptr<Graph>& graph, InputArrayOfArrays inputs_,
mCond = outputs[0];
active = loopLayer->cond(mCond);
// Deep-copy: body buffers (and their UMats via Mat::fit reuse) are recycled across iterations.
for (int i = 0; i < n_state; i++)
state[i] = outputs[1 + i];
state[i] = outputs[1 + i].clone();
for (int i = 0; i < n_accum; i++)
history[i].push_back(outputs[1 + n_state + i].clone());
}
@@ -1889,6 +1889,14 @@ TEST_P(Test_ONNX_conformance, Layer_Test)
default_lInf = 0.0005; // Expected: (normInf) <= (lInf), actual: 0.000455521 vs 0.0001
}
}
if (name == "test_reduce_prod_default_axes_keepdims_random") {
default_l1 = 0.002; // Expected: (normL1) <= (l1), actual: 0.00195312 vs 1e-05
default_lInf = 0.002; // Expected: (normInf) <= (lInf), actual: 0.00195312 vs 0.0001
}
if (name == "test_reduce_sum_square_default_axes_keepdims_random" ||
name == "test_reduce_sum_square_default_axes_keepdims_random_expanded") {
default_l1 = 2e-5; // Expected: (normL1) <= (l1), actual: 1.52588e-05 vs 1e-05
}
}
#ifdef HAVE_HALIDE
else if (backend == DNN_BACKEND_HALIDE)