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

Merge pull request #29057 from Teddy-Yangjiale:rvv-core-norm

Rvv core norm #29057

Fixes opencv/opencv#29052

### Problem
`Core_Norm/ElemWiseTest.accuracy/0` failed on RISC-V with RVV enabled when computing norm for `CV_16S` data.
The reported failing case was:

```text
src[0] ~ 16sC4 3-dim (1 x 116 x 40)
```
The expected norm result was a large positive `double`, but the RVV path returned an incorrect value:

```text
expected: 3370900308417
actual:   -173296
```

This indicates that the problem was not in the public `cv::norm()` API, but in the RVV HAL implementation used for the `CV_16S` L2/L2SQR accumulation path.

### Root Cause

The RVV HAL has a specialized implementation for `CV_16S` L2 norm:

```cpp
NormL2_RVV<short, double>
```

The implementation widens `int16` values, squares them, converts the widened products to `float64`, accumulates them in an `f64m8` vector, and finally reduces the vector to a scalar `double`:

```cpp
auto s = __riscv_vfmv_v_f_f64m8(0, vlmax);
...
auto v_mul = __riscv_vwmul(v, v, vl);
s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl);
...
return __riscv_vfmv_f(__riscv_vfredosum(...));
```

The bug was in the scalar initializer passed to `__riscv_vfredosum`.

Before this patch, the code created an `f64m1` scalar vector but used the maximum vector length for `e32m1`:

```cpp
__riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e32m1())
```

This is inconsistent: the vector type is `f64m1`, so the VL used to initialize it must correspond to `e64m1`, not `e32m1`.
This commit is contained in:
Teddy-Yangjiale
2026-05-19 14:28:52 +08:00
committed by GitHub
parent 0d6af9162c
commit 66263c5952
3 changed files with 32 additions and 48 deletions
+2 -2
View File
@@ -313,7 +313,7 @@ struct NormL2_RVV<short, double> {
auto v_mul = __riscv_vwmul(v, v, vl);
s = __riscv_vfadd_tu(s, s, __riscv_vfwcvt_f(v_mul, vl), vl);
}
return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e32m1()), vlmax));
return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax));
}
};
@@ -695,7 +695,7 @@ struct MaskedNormL2_RVV<short, double> {
s = __riscv_vfadd_tumu(b, s, s, __riscv_vfwcvt_f(b, v_mul, vl), vl);
}
}
return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e32m1()), vlmax));
return __riscv_vfmv_f(__riscv_vfredosum(s, __riscv_vfmv_s_f_f64m1(0, __riscv_vsetvlmax_e64m1()), vlmax));
}
};
@@ -1560,80 +1560,56 @@ OPENCV_HAL_IMPL_RVV_UNPACKS(v_float64, 64)
#define OPENCV_HAL_IMPL_RVV_INTERLEAVED(_Tpvec, _Tp, suffix, width, hwidth, vl) \
inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b) \
{ \
vuint##width##m2x2_t seg = __riscv_vlseg2e##width##_v_u##width##m2x2( \
(const uint##width##_t*)ptr, VTraits<v_##_Tpvec>::vlanes()); \
a = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x2_u##width##m2(seg, 0))); \
b = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x2_u##width##m2(seg, 1))); \
a = __riscv_vlse##width##_v_##suffix##m2(ptr , sizeof(_Tp)*2, VTraits<v_##_Tpvec>::vlanes()); \
b = __riscv_vlse##width##_v_##suffix##m2(ptr+1, sizeof(_Tp)*2, VTraits<v_##_Tpvec>::vlanes()); \
}\
inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, v_##_Tpvec& c) \
{ \
vuint##width##m2x3_t seg = __riscv_vlseg3e##width##_v_u##width##m2x3( \
(const uint##width##_t*)ptr, VTraits<v_##_Tpvec>::vlanes()); \
a = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x3_u##width##m2(seg, 0))); \
b = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x3_u##width##m2(seg, 1))); \
c = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x3_u##width##m2(seg, 2))); \
a = __riscv_vlse##width##_v_##suffix##m2(ptr , sizeof(_Tp)*3, VTraits<v_##_Tpvec>::vlanes()); \
b = __riscv_vlse##width##_v_##suffix##m2(ptr+1, sizeof(_Tp)*3, VTraits<v_##_Tpvec>::vlanes()); \
c = __riscv_vlse##width##_v_##suffix##m2(ptr+2, sizeof(_Tp)*3, VTraits<v_##_Tpvec>::vlanes()); \
} \
inline void v_load_deinterleave(const _Tp* ptr, v_##_Tpvec& a, v_##_Tpvec& b, \
v_##_Tpvec& c, v_##_Tpvec& d) \
{ \
\
vuint##width##m2x4_t seg = __riscv_vlseg4e##width##_v_u##width##m2x4( \
(const uint##width##_t*)ptr, VTraits<v_##_Tpvec>::vlanes()); \
a = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x4_u##width##m2(seg, 0))); \
b = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x4_u##width##m2(seg, 1))); \
c = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x4_u##width##m2(seg, 2))); \
d = v_reinterpret_as_##suffix( \
v_uint##width(__riscv_vget_v_u##width##m2x4_u##width##m2(seg, 3))); \
a = __riscv_vlse##width##_v_##suffix##m2(ptr , sizeof(_Tp)*4, VTraits<v_##_Tpvec>::vlanes()); \
b = __riscv_vlse##width##_v_##suffix##m2(ptr+1, sizeof(_Tp)*4, VTraits<v_##_Tpvec>::vlanes()); \
c = __riscv_vlse##width##_v_##suffix##m2(ptr+2, sizeof(_Tp)*4, VTraits<v_##_Tpvec>::vlanes()); \
d = __riscv_vlse##width##_v_##suffix##m2(ptr+3, sizeof(_Tp)*4, VTraits<v_##_Tpvec>::vlanes()); \
} \
inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \
hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \
{ \
vuint##width##m2x2_t seg; \
seg = __riscv_vset_v_u##width##m2_u##width##m2x2(seg, 0, v_reinterpret_as_u##width(a)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x2(seg, 1, v_reinterpret_as_u##width(b)); \
__riscv_vsseg2e##width##_v_u##width##m2x2( \
(uint##width##_t*)ptr, seg, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr, sizeof(_Tp)*2, a, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+1, sizeof(_Tp)*2, b, VTraits<v_##_Tpvec>::vlanes()); \
} \
inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \
const v_##_Tpvec& c, hal::StoreMode /*mode*/=hal::STORE_UNALIGNED) \
{ \
vuint##width##m2x3_t seg; \
seg = __riscv_vset_v_u##width##m2_u##width##m2x3(seg, 0, v_reinterpret_as_u##width(a)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x3(seg, 1, v_reinterpret_as_u##width(b)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x3(seg, 2, v_reinterpret_as_u##width(c)); \
__riscv_vsseg3e##width##_v_u##width##m2x3( \
(uint##width##_t*)ptr, seg, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr, sizeof(_Tp)*3, a, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+1, sizeof(_Tp)*3, b, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+2, sizeof(_Tp)*3, c, VTraits<v_##_Tpvec>::vlanes()); \
} \
inline void v_store_interleave( _Tp* ptr, const v_##_Tpvec& a, const v_##_Tpvec& b, \
const v_##_Tpvec& c, const v_##_Tpvec& d, \
hal::StoreMode /*mode*/=hal::STORE_UNALIGNED ) \
{ \
vuint##width##m2x4_t seg; \
seg = __riscv_vset_v_u##width##m2_u##width##m2x4(seg, 0, v_reinterpret_as_u##width(a)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x4(seg, 1, v_reinterpret_as_u##width(b)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x4(seg, 2, v_reinterpret_as_u##width(c)); \
seg = __riscv_vset_v_u##width##m2_u##width##m2x4(seg, 3, v_reinterpret_as_u##width(d)); \
__riscv_vsseg4e##width##_v_u##width##m2x4( \
(uint##width##_t*)ptr, seg, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr, sizeof(_Tp)*4, a, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+1, sizeof(_Tp)*4, b, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+2, sizeof(_Tp)*4, c, VTraits<v_##_Tpvec>::vlanes()); \
__riscv_vsse##width(ptr+3, sizeof(_Tp)*4, d, VTraits<v_##_Tpvec>::vlanes()); \
}
OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint8, uchar, u8, 8, 4, VTraits<v_uint8>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int8, schar, s8, 8, 4, VTraits<v_int8>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int8, schar, i8, 8, 4, VTraits<v_int8>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint16, ushort, u16, 16, 8, VTraits<v_uint16>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int16, short, s16, 16, 8, VTraits<v_int16>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int16, short, i16, 16, 8, VTraits<v_int16>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint32, unsigned, u32, 32, 16, VTraits<v_uint32>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int32, int, s32, 32, 16, VTraits<v_int32>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int32, int, i32, 32, 16, VTraits<v_int32>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(float32, float, f32, 32, 16, VTraits<v_float32>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(uint64, uint64, u64, 64, 32, VTraits<v_uint64>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int64, int64, s64, 64, 32, VTraits<v_int64>::vlanes())
OPENCV_HAL_IMPL_RVV_INTERLEAVED(int64, int64, i64, 64, 32, VTraits<v_int64>::vlanes())
#if CV_SIMD_SCALABLE_64F
OPENCV_HAL_IMPL_RVV_INTERLEAVED(float64, double, f64, 64, 32, VTraits<v_float64>::vlanes())
#endif
+8
View File
@@ -2770,6 +2770,14 @@ TEST(Core_Norm, NORM_L2_8UC4)
EXPECT_EQ(kNorm, cv::norm(a, b, NORM_L2));
}
TEST(Core_Norm, NORM_L2SQR_16SC4_large)
{
const int sizes[] = {1, 116, 40};
Mat src(3, sizes, CV_16SC4, Scalar::all(16384));
const double expected = static_cast<double>(src.total()) * src.channels() * 16384.0 * 16384.0;
EXPECT_EQ(expected, cv::norm(src, NORM_L2SQR));
}
TEST(Core_ConvertTo, regression_12121)
{
{