mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29811 from pratham-mcw:buildPyramid_opt
imgproc: optimize PyrDownVecH<uchar,int,3> using v_load_deinterleave - #29811 **Summary:** - buildPyramid on Windows-ARM64 (1920×1080, 8UC3) was ~7× slower than x64. - Root cause: PyrDownVecH<uchar,int,3> used vx_lut_quads to gather pixels, which on NEON compiles to 80 scalar byte reads per SIMD iteration filling a stack buffer one byte at a time before loading it as a vector. The SIMD unit was effectively idle. **Root Cause:** - The 3-channel horizontal pass needs 5 non-contiguous source pixels per output pixel. The existing code precomputes byte-offset index arrays and calls vx_lut_quads(src, idx) five times per loop body. On NEON, vx_lut_quads is implemented as 16 individual scalar reads → vld1q_s8, so 5 calls = 80 scalar loads producing only 12 int32 outputs. **Fix:** - Replace the gather loop with v_load_deinterleave, which maps to vld3q_u8 on AArch64 — single instruction, loads 48 bytes and hardware-deinterleaves R/G/B in one shot **Performance Benchmarks:** <img width="1021" height="536" alt="image" src="https://github.com/user-attachments/assets/6c4b5d71-d096-4dfd-9bc4-4315d4e679af" />
This commit is contained in:
@@ -189,35 +189,40 @@ template<> int PyrDownVecH<uchar, int, 2>(const uchar* src, int* row, int width)
|
|||||||
}
|
}
|
||||||
template<> int PyrDownVecH<uchar, int, 3>(const uchar* src, int* row, int width)
|
template<> int PyrDownVecH<uchar, int, 3>(const uchar* src, int* row, int width)
|
||||||
{
|
{
|
||||||
int idx[VTraits<v_int8>::max_nlanes/2 + 4];
|
|
||||||
for (int i = 0; i < VTraits<v_int8>::vlanes()/4 + 2; i++)
|
|
||||||
{
|
|
||||||
idx[i] = 6*i;
|
|
||||||
idx[i + VTraits<v_int8>::vlanes()/4 + 2] = 6*i + 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = 0;
|
int x = 0;
|
||||||
v_int16 v_6_4 = v_reinterpret_as_s16(vx_setall_u32(0x00040006));
|
const int VU8 = VTraits<v_uint8>::vlanes();
|
||||||
for (; x <= width - VTraits<v_int8>::vlanes(); x += 3*VTraits<v_int8>::vlanes()/4, src += 6*VTraits<v_int8>::vlanes()/4, row += 3*VTraits<v_int8>::vlanes()/4)
|
const int VU32 = VTraits<v_uint32>::vlanes();
|
||||||
|
const int step = (VU8 / 2) * 3;
|
||||||
|
const v_uint16 lo_mask = vx_setall_u16(0x00FFu);
|
||||||
|
const v_uint16 w6 = vx_setall_u16(6u);
|
||||||
|
|
||||||
|
for (; x <= width - step - 3; x += step, src += VU8 * 3, row += step)
|
||||||
{
|
{
|
||||||
v_uint16 r0l, r0h, r1l, r1h, r2l, r2h, r3l, r3h, r4l, r4h;
|
v_uint8 s0R, s0G, s0B, scR, scG, scB, s4R, s4G, s4B;
|
||||||
v_expand(vx_lut_quads(src, idx ), r0l, r0h);
|
v_load_deinterleave(src, s0R, s0G, s0B);
|
||||||
v_expand(vx_lut_quads(src, idx + VTraits<v_int8>::vlanes()/4 + 2), r1l, r1h);
|
v_load_deinterleave(src + 6, scR, scG, scB);
|
||||||
v_expand(vx_lut_quads(src, idx + 1 ), r2l, r2h);
|
v_load_deinterleave(src + 12, s4R, s4G, s4B);
|
||||||
v_expand(vx_lut_quads(src, idx + VTraits<v_int8>::vlanes()/4 + 3), r3l, r3h);
|
|
||||||
v_expand(vx_lut_quads(src, idx + 2 ), r4l, r4h);
|
|
||||||
|
|
||||||
v_zip(r2l, v_add(r1l, r3l), r1l, r3l);
|
v_uint16 u0R = v_reinterpret_as_u16(s0R), ucR = v_reinterpret_as_u16(scR), up2R = v_reinterpret_as_u16(s4R);
|
||||||
v_zip(r2h, v_add(r1h, r3h), r1h, r3h);
|
v_uint16 u0G = v_reinterpret_as_u16(s0G), ucG = v_reinterpret_as_u16(scG), up2G = v_reinterpret_as_u16(s4G);
|
||||||
r0l = v_add(r0l, r4l); r0h = v_add(r0h, r4h);
|
v_uint16 u0B = v_reinterpret_as_u16(s0B), ucB = v_reinterpret_as_u16(scB), up2B = v_reinterpret_as_u16(s4B);
|
||||||
|
|
||||||
v_store(row , v_pack_triplets(v_add(v_dotprod(v_reinterpret_as_s16(r1l), v_6_4), v_reinterpret_as_s32(v_expand_low(r0l)))));
|
// Five-tap [1,4,6,4,1] filter; max value 255×16 = 4080, fits uint16.
|
||||||
v_store(row + 3*VTraits<v_int32>::vlanes()/4, v_pack_triplets(v_add(v_dotprod(v_reinterpret_as_s16(r3l), v_6_4), v_reinterpret_as_s32(v_expand_high(r0l)))));
|
v_uint16 accR = v_add(v_add(v_and(u0R, lo_mask), v_and(up2R, lo_mask)),
|
||||||
v_store(row + 6*VTraits<v_int32>::vlanes()/4, v_pack_triplets(v_add(v_dotprod(v_reinterpret_as_s16(r1h), v_6_4), v_reinterpret_as_s32(v_expand_low(r0h)))));
|
v_add(v_shl<2>(v_add(v_shr<8>(u0R), v_shr<8>(ucR))),
|
||||||
v_store(row + 9*VTraits<v_int32>::vlanes()/4, v_pack_triplets(v_add(v_dotprod(v_reinterpret_as_s16(r3h), v_6_4), v_reinterpret_as_s32(v_expand_high(r0h)))));
|
v_mul(v_and(ucR, lo_mask), w6)));
|
||||||
|
v_uint16 accG = v_add(v_add(v_and(u0G, lo_mask), v_and(up2G, lo_mask)),
|
||||||
|
v_add(v_shl<2>(v_add(v_shr<8>(u0G), v_shr<8>(ucG))),
|
||||||
|
v_mul(v_and(ucG, lo_mask), w6)));
|
||||||
|
v_uint16 accB = v_add(v_add(v_and(u0B, lo_mask), v_and(up2B, lo_mask)),
|
||||||
|
v_add(v_shl<2>(v_add(v_shr<8>(u0B), v_shr<8>(ucB))),
|
||||||
|
v_mul(v_and(ucB, lo_mask), w6)));
|
||||||
|
|
||||||
|
unsigned* dst = reinterpret_cast<unsigned*>(row);
|
||||||
|
v_store_interleave(dst, v_expand_low(accR), v_expand_low(accG), v_expand_low(accB));
|
||||||
|
v_store_interleave(dst + 3*VU32, v_expand_high(accR), v_expand_high(accG), v_expand_high(accB));
|
||||||
}
|
}
|
||||||
vx_cleanup();
|
vx_cleanup();
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
template<> int PyrDownVecH<uchar, int, 4>(const uchar* src, int* row, int width)
|
template<> int PyrDownVecH<uchar, int, 4>(const uchar* src, int* row, int width)
|
||||||
|
|||||||
Reference in New Issue
Block a user