Merge pull request #28728 from pratham-mcw:calchist-simd-opt

imgproc: add simd support for calchist & calchist1d function - #28728

- This PR adds OpenCV SIMD intrinsics-based optimizations to the Calchist and calcHist1d function for improved performance on Windows-ARM64 platforms.
- The optimized implementation uses vectorized operations to accelerate histogram computation.
- In x64 architecture, `calcHist1d` benefits from IPP-based optimized implementations. However, on ARM64 platforms, the execution falls back to scalar implementation, which results in lower performance.
- After introducing these changes, the calcHist and calchist1d function showed noticeable performance improvements on Windows-ARM64.

**Performance Benchmarks:**
<img width="1247" height="460" alt="image" src="https://github.com/user-attachments/assets/ed740d87-158e-49d4-889e-9c7dda482a63" />
<img width="552" height="210" alt="image" src="https://github.com/user-attachments/assets/76b9e46e-304e-480c-9a57-af0aba6e937a" />

- [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
This commit is contained in:
Pratham Kumar
2026-08-13 17:02:47 +05:30
committed by GitHub
parent c6e84d494c
commit fece2acfba

View File

@@ -260,16 +260,74 @@ calcHist_( std::vector<uchar*>& _ptrs, const std::vector<int>& _deltas,
for( ; imsize.height--; p0 += step0, mask += mstep )
{
if( !mask )
for( x = 0; x < imsize.width; x++, p0 += d0 )
{
#if (CV_SIMD || CV_SIMD_SCALABLE)
if( d0 == 1 && sizeof(T) == 2 )
{
double v0 = (double)*p0;
int idx = cvFloor(v0*a + b);
if (v0 < v0_lo || v0 >= v0_hi)
continue;
idx = CV_CLAMP_INT(idx, 0, sz - 1);
CV_DbgAssert((unsigned)idx < (unsigned)sz);
((int*)H)[idx]++;
const ushort* p = (const ushort*)p0;
const int nlanes = VTraits<v_uint16>::vlanes();
const v_float32 va = vx_setall_f32((float)a);
const v_float32 vb = vx_setall_f32((float)b);
const v_float32 vlo = vx_setall_f32((float)v0_lo);
const v_float32 vhi = vx_setall_f32((float)v0_hi);
const v_int32 vi0 = vx_setall_s32(0);
const v_int32 visz = vx_setall_s32(sz - 1);
x = 0;
for( ; x <= imsize.width - nlanes; x += nlanes )
{
v_uint16 pix = vx_load(&p[x]);
v_uint32 lo32, hi32;
v_expand(pix, lo32, hi32);
v_float32 flo = v_cvt_f32(v_reinterpret_as_s32(lo32));
v_float32 fhi = v_cvt_f32(v_reinterpret_as_s32(hi32));
v_uint32 rlo = v_reinterpret_as_u32(v_and(v_ge(flo, vlo), v_lt(flo, vhi)));
v_uint32 rhi = v_reinterpret_as_u32(v_and(v_ge(fhi, vlo), v_lt(fhi, vhi)));
v_int32 idx_lo = v_max(vi0, v_min(visz, v_floor(v_fma(flo, va, vb))));
v_int32 idx_hi = v_max(vi0, v_min(visz, v_floor(v_fma(fhi, va, vb))));
int idxbuf[2 * VTraits<v_int32>::max_nlanes];
v_store(idxbuf, idx_lo);
v_store(idxbuf + nlanes / 2, idx_hi);
if( v_check_all(v_reinterpret_as_s32(rlo)) && v_check_all(v_reinterpret_as_s32(rhi)) )
{
for( int k = 0; k < nlanes; k++ )
((int*)H)[idxbuf[k]]++;
}
else
{
int deltabuf[2 * VTraits<v_int32>::max_nlanes];
v_store(deltabuf, v_reinterpret_as_s32(rlo));
v_store(deltabuf + nlanes / 2, v_reinterpret_as_s32(rhi));
for( int k = 0; k < nlanes; k++ )
((int*)H)[idxbuf[k]] += (deltabuf[k] != 0);
}
}
for( ; x < imsize.width; x++ )
{
double v0 = (double)p[x];
int delta = int(v0_lo <= v0) & int(v0 < v0_hi);
int idx = CV_CLAMP_INT(cvFloor(v0*a + b), 0, sz-1);
((int*)H)[idx] += delta;
}
p0 = (const T*)(p + imsize.width);
}
else
#endif
for( x = 0; x < imsize.width; x++, p0 += d0 )
{
double v0 = (double)*p0;
int idx = cvFloor(v0*a + b);
if (v0 < v0_lo || v0 >= v0_hi)
continue;
idx = CV_CLAMP_INT(idx, 0, sz - 1);
CV_DbgAssert((unsigned)idx < (unsigned)sz);
((int*)H)[idx]++;
}
}
else
for( x = 0; x < imsize.width; x++, p0 += d0 )
if( mask[x] )