From 8963a3868e88af3f023bdaf399996bb69a656301 Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Thu, 4 Jun 2026 12:04:19 +0530 Subject: [PATCH 1/6] ximgproc: add NEON intrinsics support for Guided Filter Function --- modules/imgproc/src/box_filter.simd.hpp | 90 +++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 1eec7a0a7d..2ecdbc7766 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -102,14 +102,90 @@ struct RowSum : } else if( cn == 1 ) { - ST s = 0; - for( i = 0; i < ksz_cn; i++ ) - s += (ST)S[i]; - D[0] = s; - for( i = 0; i < width; i++ ) + #if CV_NEON + if constexpr (std::is_same::value && std::is_same::value) { - s += (ST)S[i + ksz_cn] - (ST)S[i]; - D[i+1] = s; + ST s = 0; + i = 0; + { + float64x2_t vsum0 = vdupq_n_f64(0.0); + float64x2_t vsum1 = vdupq_n_f64(0.0); + for( ; i <= ksz_cn - 8; i += 8 ) + { + float32x4_t va = vld1q_f32(S + i); + float32x4_t vb = vld1q_f32(S + i + 4); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(va))); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(va))); + vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_low_f32(vb))); + vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_high_f32(vb))); + } + for( ; i <= ksz_cn - 4; i += 4 ) + { + float32x4_t v = vld1q_f32(S + i); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(v))); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(v))); + } + vsum0 = vaddq_f64(vsum0, vsum1); + s = vgetq_lane_f64(vsum0, 0) + vgetq_lane_f64(vsum0, 1); + for( ; i < ksz_cn; i++ ) + s += (ST)S[i]; + } + D[0] = s; + static const int CHUNK = 512; + double delta[CHUNK]; + const float64x2_t vzero = vdupq_n_f64(0.0); + + i = 0; + while( i < width ) + { + const int chunk_sz = std::min(width - i, CHUNK); + int j = 0; + for( ; j <= chunk_sz - 4; j += 4 ) + { + float32x4_t vnew = vld1q_f32(S + i + j + ksz_cn); + float32x4_t vold = vld1q_f32(S + i + j); + vst1q_f64(delta + j, vsubq_f64(vcvt_f64_f32(vget_low_f32(vnew)), vcvt_f64_f32(vget_low_f32(vold)))); + vst1q_f64(delta + j + 2, vsubq_f64(vcvt_f64_f32(vget_high_f32(vnew)), vcvt_f64_f32(vget_high_f32(vold)))); + } + for( ; j < chunk_sz; j++ ) + delta[j] = (double)S[i + j + ksz_cn] - (double)S[i + j]; + + j = 0; + for( ; j <= chunk_sz - 4; j += 4 ) + { + float64x2_t vd0 = vld1q_f64(delta + j); + float64x2_t vd1 = vld1q_f64(delta + j + 2); + float64x2_t vp0 = vaddq_f64(vd0, vextq_f64(vzero, vd0, 1)); + float64x2_t vp1 = vaddq_f64(vd1, vextq_f64(vzero, vd1, 1)); + double pair0_sum = vgetq_lane_f64(vp0, 1); + float64x2_t vp1off = vaddq_f64(vp1, vdupq_n_f64(pair0_sum)); + float64x2_t vc = vdupq_n_f64(s); + vst1q_f64(D + i + j + 1, vaddq_f64(vp0, vc)); + float64x2_t vr2 = vaddq_f64(vp1off, vc); + vst1q_f64(D + i + j + 3, vr2); + s = vgetq_lane_f64(vr2, 1); + } + for( ; j < chunk_sz; j++ ) + { + s += delta[j]; + D[i + j + 1] = s; + } + + i += chunk_sz; + } + } + else + #endif + { + ST s = 0; + for( i = 0; i < ksz_cn; i++ ) + s += (ST)S[i]; + D[0] = s; + for( i = 0; i < width; i++ ) + { + s += (ST)S[i + ksz_cn] - (ST)S[i]; + D[i + 1] = s; + } } } else if( cn == 3 ) From d87e0e611762a79d710fb5d21482afe15bb8142b Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Thu, 2 Jul 2026 20:16:32 +0530 Subject: [PATCH 2/6] imgproc: fix C++17 extension warning in box_filter NEON code --- modules/imgproc/src/box_filter.simd.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 2ecdbc7766..941667b3c9 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -103,7 +103,7 @@ struct RowSum : else if( cn == 1 ) { #if CV_NEON - if constexpr (std::is_same::value && std::is_same::value) + if (std::is_same::value && std::is_same::value) { ST s = 0; i = 0; From 2665cec7f282a90f6956341721a46be7af3c0be6 Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Thu, 16 Jul 2026 18:49:00 +0530 Subject: [PATCH 3/6] fix NEON RowSum build failure on non-float/double type instantiations --- modules/imgproc/src/box_filter.simd.hpp | 163 +++++++++++++----------- 1 file changed, 90 insertions(+), 73 deletions(-) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 941667b3c9..1d45c63d91 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -65,6 +65,94 @@ Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize, int a \****************************************************************************************/ namespace { +#if CV_NEON_AARCH64 +template +struct RowSumCn1Neon +{ + static CV_ALWAYS_INLINE bool apply(const T*, ST*, int, int) + { + return false; + } +}; + +template<> +struct RowSumCn1Neon +{ + static CV_ALWAYS_INLINE bool apply(const float* S, double* D, int width, int ksz_cn) + { + double s = 0; + int i = 0; + { + float64x2_t vsum0 = vdupq_n_f64(0.0); + float64x2_t vsum1 = vdupq_n_f64(0.0); + for( ; i <= ksz_cn - 8; i += 8 ) + { + float32x4_t va = vld1q_f32(S + i); + float32x4_t vb = vld1q_f32(S + i + 4); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(va))); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(va))); + vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_low_f32(vb))); + vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_high_f32(vb))); + } + for( ; i <= ksz_cn - 4; i += 4 ) + { + float32x4_t v = vld1q_f32(S + i); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(v))); + vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(v))); + } + vsum0 = vaddq_f64(vsum0, vsum1); + s = vgetq_lane_f64(vsum0, 0) + vgetq_lane_f64(vsum0, 1); + for( ; i < ksz_cn; i++ ) + s += (double)S[i]; + } + D[0] = s; + static const int CHUNK = 512; + double delta[CHUNK]; + const float64x2_t vzero = vdupq_n_f64(0.0); + + i = 0; + while( i < width ) + { + const int chunk_sz = std::min(width - i, CHUNK); + int j = 0; + for( ; j <= chunk_sz - 4; j += 4 ) + { + float32x4_t vnew = vld1q_f32(S + i + j + ksz_cn); + float32x4_t vold = vld1q_f32(S + i + j); + vst1q_f64(delta + j, vsubq_f64(vcvt_f64_f32(vget_low_f32(vnew)), vcvt_f64_f32(vget_low_f32(vold)))); + vst1q_f64(delta + j + 2, vsubq_f64(vcvt_f64_f32(vget_high_f32(vnew)), vcvt_f64_f32(vget_high_f32(vold)))); + } + for( ; j < chunk_sz; j++ ) + delta[j] = (double)S[i + j + ksz_cn] - (double)S[i + j]; + + j = 0; + for( ; j <= chunk_sz - 4; j += 4 ) + { + float64x2_t vd0 = vld1q_f64(delta + j); + float64x2_t vd1 = vld1q_f64(delta + j + 2); + float64x2_t vp0 = vaddq_f64(vd0, vextq_f64(vzero, vd0, 1)); + float64x2_t vp1 = vaddq_f64(vd1, vextq_f64(vzero, vd1, 1)); + double pair0_sum = vgetq_lane_f64(vp0, 1); + float64x2_t vp1off = vaddq_f64(vp1, vdupq_n_f64(pair0_sum)); + float64x2_t vc = vdupq_n_f64(s); + vst1q_f64(D + i + j + 1, vaddq_f64(vp0, vc)); + float64x2_t vr2 = vaddq_f64(vp1off, vc); + vst1q_f64(D + i + j + 3, vr2); + s = vgetq_lane_f64(vr2, 1); + } + for( ; j < chunk_sz; j++ ) + { + s += delta[j]; + D[i + j + 1] = s; + } + + i += chunk_sz; + } + return true; + } +}; +#endif // CV_NEON_AARCH64 + template struct RowSum : public BaseRowFilter @@ -102,79 +190,8 @@ struct RowSum : } else if( cn == 1 ) { - #if CV_NEON - if (std::is_same::value && std::is_same::value) - { - ST s = 0; - i = 0; - { - float64x2_t vsum0 = vdupq_n_f64(0.0); - float64x2_t vsum1 = vdupq_n_f64(0.0); - for( ; i <= ksz_cn - 8; i += 8 ) - { - float32x4_t va = vld1q_f32(S + i); - float32x4_t vb = vld1q_f32(S + i + 4); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(va))); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(va))); - vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_low_f32(vb))); - vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_high_f32(vb))); - } - for( ; i <= ksz_cn - 4; i += 4 ) - { - float32x4_t v = vld1q_f32(S + i); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(v))); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(v))); - } - vsum0 = vaddq_f64(vsum0, vsum1); - s = vgetq_lane_f64(vsum0, 0) + vgetq_lane_f64(vsum0, 1); - for( ; i < ksz_cn; i++ ) - s += (ST)S[i]; - } - D[0] = s; - static const int CHUNK = 512; - double delta[CHUNK]; - const float64x2_t vzero = vdupq_n_f64(0.0); - - i = 0; - while( i < width ) - { - const int chunk_sz = std::min(width - i, CHUNK); - int j = 0; - for( ; j <= chunk_sz - 4; j += 4 ) - { - float32x4_t vnew = vld1q_f32(S + i + j + ksz_cn); - float32x4_t vold = vld1q_f32(S + i + j); - vst1q_f64(delta + j, vsubq_f64(vcvt_f64_f32(vget_low_f32(vnew)), vcvt_f64_f32(vget_low_f32(vold)))); - vst1q_f64(delta + j + 2, vsubq_f64(vcvt_f64_f32(vget_high_f32(vnew)), vcvt_f64_f32(vget_high_f32(vold)))); - } - for( ; j < chunk_sz; j++ ) - delta[j] = (double)S[i + j + ksz_cn] - (double)S[i + j]; - - j = 0; - for( ; j <= chunk_sz - 4; j += 4 ) - { - float64x2_t vd0 = vld1q_f64(delta + j); - float64x2_t vd1 = vld1q_f64(delta + j + 2); - float64x2_t vp0 = vaddq_f64(vd0, vextq_f64(vzero, vd0, 1)); - float64x2_t vp1 = vaddq_f64(vd1, vextq_f64(vzero, vd1, 1)); - double pair0_sum = vgetq_lane_f64(vp0, 1); - float64x2_t vp1off = vaddq_f64(vp1, vdupq_n_f64(pair0_sum)); - float64x2_t vc = vdupq_n_f64(s); - vst1q_f64(D + i + j + 1, vaddq_f64(vp0, vc)); - float64x2_t vr2 = vaddq_f64(vp1off, vc); - vst1q_f64(D + i + j + 3, vr2); - s = vgetq_lane_f64(vr2, 1); - } - for( ; j < chunk_sz; j++ ) - { - s += delta[j]; - D[i + j + 1] = s; - } - - i += chunk_sz; - } - } - else + #if CV_NEON_AARCH64 + if (!RowSumCn1Neon::apply(S, D, width, ksz_cn)) #endif { ST s = 0; From c6b17073e9c29afd20b13b326b2da899d1f0b764 Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Fri, 17 Jul 2026 15:58:35 +0530 Subject: [PATCH 4/6] Replace CV_NEON_AARCH64 with CV_NEON in box_filter --- modules/imgproc/src/box_filter.simd.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 1d45c63d91..3c937e608d 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -65,7 +65,7 @@ Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize, int a \****************************************************************************************/ namespace { -#if CV_NEON_AARCH64 +#if CV_NEON template struct RowSumCn1Neon { @@ -151,7 +151,7 @@ struct RowSumCn1Neon return true; } }; -#endif // CV_NEON_AARCH64 +#endif // CV_NEON template struct RowSum : @@ -190,7 +190,7 @@ struct RowSum : } else if( cn == 1 ) { - #if CV_NEON_AARCH64 + #if CV_NEON if (!RowSumCn1Neon::apply(S, D, width, ksz_cn)) #endif { From ed1a25df19b254c2b0a4d556dffdf04ae182cd82 Mon Sep 17 00:00:00 2001 From: pratham-mcw Date: Sun, 19 Jul 2026 16:43:18 +0530 Subject: [PATCH 5/6] fix NEON box filter build failure on armv7 --- modules/imgproc/src/box_filter.simd.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 3c937e608d..9d2c74a9d9 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -75,6 +75,7 @@ struct RowSumCn1Neon } }; +#if CV_NEON && (defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) template<> struct RowSumCn1Neon { @@ -151,6 +152,7 @@ struct RowSumCn1Neon return true; } }; +#endif // CV_NEON && AArch64 #endif // CV_NEON template From 2cfad59a385b2d666916fb57e81d13bfc87805b6 Mon Sep 17 00:00:00 2001 From: Pratham Kumar Date: Sun, 23 Aug 2026 13:15:01 +0530 Subject: [PATCH 6/6] fuse delta/prefix-sum SIMD loops in row sum --- modules/imgproc/src/box_filter.simd.hpp | 107 ++++++++++-------------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/modules/imgproc/src/box_filter.simd.hpp b/modules/imgproc/src/box_filter.simd.hpp index 9d2c74a9d9..7f50dbc518 100644 --- a/modules/imgproc/src/box_filter.simd.hpp +++ b/modules/imgproc/src/box_filter.simd.hpp @@ -65,9 +65,9 @@ Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize, int a \****************************************************************************************/ namespace { -#if CV_NEON +#if CV_SIMD128_64F template -struct RowSumCn1Neon +struct RowSumCn1SIMD128 { static CV_ALWAYS_INLINE bool apply(const T*, ST*, int, int) { @@ -75,85 +75,64 @@ struct RowSumCn1Neon } }; -#if CV_NEON && (defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) template<> -struct RowSumCn1Neon +struct RowSumCn1SIMD128 { static CV_ALWAYS_INLINE bool apply(const float* S, double* D, int width, int ksz_cn) { double s = 0; int i = 0; { - float64x2_t vsum0 = vdupq_n_f64(0.0); - float64x2_t vsum1 = vdupq_n_f64(0.0); - for( ; i <= ksz_cn - 8; i += 8 ) + v_float64x2 vsum0 = v_setzero_f64(); + v_float64x2 vsum1 = v_setzero_f64(); + for( ; i <= ksz_cn - 2*VTraits::nlanes; i += 2*VTraits::nlanes ) { - float32x4_t va = vld1q_f32(S + i); - float32x4_t vb = vld1q_f32(S + i + 4); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(va))); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(va))); - vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_low_f32(vb))); - vsum1 = vaddq_f64(vsum1, vcvt_f64_f32(vget_high_f32(vb))); + v_float32x4 va = v_load(S + i); + v_float32x4 vb = v_load(S + i + VTraits::nlanes); + vsum0 = v_add(vsum0, v_cvt_f64(va)); + vsum0 = v_add(vsum0, v_cvt_f64_high(va)); + vsum1 = v_add(vsum1, v_cvt_f64(vb)); + vsum1 = v_add(vsum1, v_cvt_f64_high(vb)); } - for( ; i <= ksz_cn - 4; i += 4 ) + for( ; i <= ksz_cn - VTraits::nlanes; i += VTraits::nlanes ) { - float32x4_t v = vld1q_f32(S + i); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_low_f32(v))); - vsum0 = vaddq_f64(vsum0, vcvt_f64_f32(vget_high_f32(v))); + v_float32x4 v = v_load(S + i); + vsum0 = v_add(vsum0, v_cvt_f64(v)); + vsum0 = v_add(vsum0, v_cvt_f64_high(v)); } - vsum0 = vaddq_f64(vsum0, vsum1); - s = vgetq_lane_f64(vsum0, 0) + vgetq_lane_f64(vsum0, 1); + vsum0 = v_add(vsum0, vsum1); + s = v_reduce_sum(vsum0); for( ; i < ksz_cn; i++ ) s += (double)S[i]; } D[0] = s; - static const int CHUNK = 512; - double delta[CHUNK]; - const float64x2_t vzero = vdupq_n_f64(0.0); - i = 0; - while( i < width ) + int j = 0; + for( ; j <= width - VTraits::nlanes; j += VTraits::nlanes ) { - const int chunk_sz = std::min(width - i, CHUNK); - int j = 0; - for( ; j <= chunk_sz - 4; j += 4 ) - { - float32x4_t vnew = vld1q_f32(S + i + j + ksz_cn); - float32x4_t vold = vld1q_f32(S + i + j); - vst1q_f64(delta + j, vsubq_f64(vcvt_f64_f32(vget_low_f32(vnew)), vcvt_f64_f32(vget_low_f32(vold)))); - vst1q_f64(delta + j + 2, vsubq_f64(vcvt_f64_f32(vget_high_f32(vnew)), vcvt_f64_f32(vget_high_f32(vold)))); - } - for( ; j < chunk_sz; j++ ) - delta[j] = (double)S[i + j + ksz_cn] - (double)S[i + j]; - - j = 0; - for( ; j <= chunk_sz - 4; j += 4 ) - { - float64x2_t vd0 = vld1q_f64(delta + j); - float64x2_t vd1 = vld1q_f64(delta + j + 2); - float64x2_t vp0 = vaddq_f64(vd0, vextq_f64(vzero, vd0, 1)); - float64x2_t vp1 = vaddq_f64(vd1, vextq_f64(vzero, vd1, 1)); - double pair0_sum = vgetq_lane_f64(vp0, 1); - float64x2_t vp1off = vaddq_f64(vp1, vdupq_n_f64(pair0_sum)); - float64x2_t vc = vdupq_n_f64(s); - vst1q_f64(D + i + j + 1, vaddq_f64(vp0, vc)); - float64x2_t vr2 = vaddq_f64(vp1off, vc); - vst1q_f64(D + i + j + 3, vr2); - s = vgetq_lane_f64(vr2, 1); - } - for( ; j < chunk_sz; j++ ) - { - s += delta[j]; - D[i + j + 1] = s; - } - - i += chunk_sz; + v_float32x4 vnew = v_load(S + j + ksz_cn); + v_float32x4 vold = v_load(S + j); + v_float64x2 vd0 = v_sub(v_cvt_f64(vnew), v_cvt_f64(vold)); + v_float64x2 vd1 = v_sub(v_cvt_f64_high(vnew), v_cvt_f64_high(vold)); + v_float64x2 vp0 = v_add(vd0, v_rotate_left<1>(vd0)); + v_float64x2 vp1 = v_add(vd1, v_rotate_left<1>(vd1)); + double pair0_sum = v_extract_n<1>(vp0); + v_float64x2 vp1off = v_add(vp1, v_setall_f64(pair0_sum)); + v_float64x2 vc = v_setall_f64(s); + v_store(D + j + 1, v_add(vp0, vc)); + v_float64x2 vr2 = v_add(vp1off, vc); + v_store(D + j + 1 + VTraits::nlanes, vr2); + s = v_extract_n<1>(vr2); + } + for( ; j < width; j++ ) + { + s += (double)S[j + ksz_cn] - (double)S[j]; + D[j + 1] = s; } return true; } }; -#endif // CV_NEON && AArch64 -#endif // CV_NEON +#endif // CV_SIMD128_64F template struct RowSum : @@ -192,9 +171,9 @@ struct RowSum : } else if( cn == 1 ) { - #if CV_NEON - if (!RowSumCn1Neon::apply(S, D, width, ksz_cn)) - #endif +#if CV_SIMD128_64F + if (!RowSumCn1SIMD128::apply(S, D, width, ksz_cn)) +#endif { ST s = 0; for( i = 0; i < ksz_cn; i++ ) @@ -1855,4 +1834,4 @@ Ptr getSqrRowSumFilter(int srcType, int sumType, int ksize, int a #endif CV_CPU_OPTIMIZATION_NAMESPACE_END -} // namespace +} // namespace \ No newline at end of file