From 70a2c50b43a4516ff2cad1d366200c06b26eea2c Mon Sep 17 00:00:00 2001 From: Madan mohan Manokar Date: Thu, 18 Jun 2026 19:59:18 +0530 Subject: [PATCH] Merge pull request #29250 from amd:fast_lut8u_simd imgproc: optimized LUT and equalizeHist with SIMD #29250 - optimized lut with SIMD - support equalizeHist with v_lut ### 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 - [ ] 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 --- modules/core/CMakeLists.txt | 1 + .../include/opencv2/core/hal/intrin_avx.hpp | 14 ++ .../opencv2/core/hal/intrin_avx512.hpp | 116 ++++++++++++++ .../include/opencv2/core/hal/intrin_cpp.hpp | 11 ++ .../include/opencv2/core/hal/intrin_lasx.hpp | 15 ++ .../include/opencv2/core/hal/intrin_lsx.hpp | 10 ++ .../include/opencv2/core/hal/intrin_msa.hpp | 9 ++ .../include/opencv2/core/hal/intrin_neon.hpp | 43 ++++++ .../opencv2/core/hal/intrin_rvv071.hpp | 11 ++ .../opencv2/core/hal/intrin_rvv_scalable.hpp | 8 + .../include/opencv2/core/hal/intrin_sse.hpp | 11 ++ .../include/opencv2/core/hal/intrin_vsx.hpp | 10 ++ .../include/opencv2/core/hal/intrin_wasm.hpp | 10 ++ modules/core/src/lut.cpp | 10 +- modules/core/src/lut.dispatch.cpp | 56 +++++++ modules/core/src/lut.simd.hpp | 146 ++++++++++++++++++ modules/imgproc/CMakeLists.txt | 1 + .../imgproc/src/equalize_hist.dispatch.cpp | 37 +++++ modules/imgproc/src/equalize_hist.simd.hpp | 61 ++++++++ modules/imgproc/src/histogram.cpp | 40 ++--- 20 files changed, 593 insertions(+), 27 deletions(-) create mode 100644 modules/core/src/lut.dispatch.cpp create mode 100644 modules/core/src/lut.simd.hpp create mode 100644 modules/imgproc/src/equalize_hist.dispatch.cpp create mode 100644 modules/imgproc/src/equalize_hist.simd.hpp diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 0abd534d85..414dc9907b 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -14,6 +14,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(lut AVX512_ICL) # 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) diff --git a/modules/core/include/opencv2/core/hal/intrin_avx.hpp b/modules/core/include/opencv2/core/hal/intrin_avx.hpp index c7ce1e3d33..efc642cb00 100644 --- a/modules/core/include/opencv2/core/hal/intrin_avx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_avx.hpp @@ -1598,6 +1598,20 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); } inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); } +inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx) +{ + uchar CV_DECL_ALIGNED(32) indices[32], result[32]; + _mm256_store_si256((__m256i*)indices, idx.val); + for (int i = 0; i < 32; i++) result[i] = tab[indices[i]]; + return v_uint8x32(_mm256_load_si256((const __m256i*)result)); +} +inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx) +{ return v_reinterpret_as_s8(v256_lut((const uchar *)tab, idx)); } + + +inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); } +inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); } + inline v_int16x16 v256_lut(const short* tab, const int* idx) { return v_int16x16(_mm256_setr_epi16(tab[idx[0]], tab[idx[1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], tab[idx[ 5]], tab[idx[ 6]], tab[idx[ 7]], diff --git a/modules/core/include/opencv2/core/hal/intrin_avx512.hpp b/modules/core/include/opencv2/core/hal/intrin_avx512.hpp index 077b4d17a7..e06dc05d72 100644 --- a/modules/core/include/opencv2/core/hal/intrin_avx512.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_avx512.hpp @@ -1634,6 +1634,122 @@ inline v_uint8x64 v512_lut(const uchar* tab, const int* idx) { return v_reinterp inline v_uint8x64 v512_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_pairs((const schar *)tab, idx)); } inline v_uint8x64 v512_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v512_lut_quads((const schar *)tab, idx)); } +// Byte-indexed LUT: takes v_uint8x64 byte indices instead of int* indices +// Table must have at least 256 entries (byte indices cover 0-255) +// VBMI path uses vpermb (register-to-register), fallback uses gather +inline v_uint8x64 v512_lut(const uchar* tab, const v_uint8x64& idx) +{ +#if CV_AVX_512VBMI + const __m512i lut0 = _mm512_loadu_si512(tab); + const __m512i lut1 = _mm512_loadu_si512(tab + 64); + const __m512i lut2 = _mm512_loadu_si512(tab + 128); + const __m512i lut3 = _mm512_loadu_si512(tab + 192); + + __m512i idx6 = _mm512_and_si512(idx.val, _mm512_set1_epi8(0x3F)); + + __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0); + __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1); + __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2); + __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3); + + __mmask64 k6 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8(0x40)); + __mmask64 k7 = _mm512_test_epi8_mask(idx.val, _mm512_set1_epi8((char)0x80)); + + __m512i low = _mm512_mask_blend_epi8(k6, r0, r1); + __m512i high = _mm512_mask_blend_epi8(k6, r2, r3); + return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high)); +#else + uchar CV_DECL_ALIGNED(64) indices[64], result[64]; + _mm512_store_si512((__m512i*)indices, idx.val); + for (int i = 0; i < 64; i++) result[i] = tab[indices[i]]; + return v_uint8x64(_mm512_load_si512((const __m512i*)result)); +#endif +} +inline v_int8x64 v512_lut(const schar* tab, const v_uint8x64& idx) +{ return v_reinterpret_as_s8(v512_lut((const uchar *)tab, idx)); } + +// Universal v_lut overloads for vector byte indices (aliases to v512_lut) +inline v_uint8x64 v_lut(const uchar* tab, const v_uint8x64& idx) +{ return v512_lut(tab, idx); } + +inline v_int8x64 v_lut(const schar* tab, const v_uint8x64& idx) +{ return v512_lut(tab, idx); } + +// Byte-indexed pair LUT: 32 byte indices (lower half of idx) → 32 pairs = 64 bytes +inline v_uint8x64 v512_lut_pairs(const uchar* tab, const v_uint8x64& idx) +{ +#if CV_AVX_512VBMI + const __m512i lut0 = _mm512_loadu_si512(tab); + const __m512i lut1 = _mm512_loadu_si512(tab + 64); + const __m512i lut2 = _mm512_loadu_si512(tab + 128); + const __m512i lut3 = _mm512_loadu_si512(tab + 192); + + // Expand 32 byte indices to interleaved pairs: (i0, i0+1, i1, i1+1, ...) + __m512i idx16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(idx.val)); + __m512i idx_dup = _mm512_or_si512(idx16, _mm512_slli_epi16(idx16, 8)); + __m512i idx_pairs = _mm512_add_epi8(idx_dup, _mm512_set1_epi16(0x0100)); + + __m512i idx6 = _mm512_and_si512(idx_pairs, _mm512_set1_epi8(0x3F)); + + __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0); + __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1); + __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2); + __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3); + + __mmask64 k6 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8(0x40)); + __mmask64 k7 = _mm512_test_epi8_mask(idx_pairs, _mm512_set1_epi8((char)0x80)); + + __m512i low = _mm512_mask_blend_epi8(k6, r0, r1); + __m512i high = _mm512_mask_blend_epi8(k6, r2, r3); + return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high)); +#else + uchar CV_DECL_ALIGNED(64) indices[64], result[64]; + _mm512_store_si512((__m512i*)indices, idx.val); + for (int i = 0; i < 64; i++) result[i] = tab[indices[i]]; + return v_uint8x64(_mm512_load_si512((const __m512i*)result)); +#endif +} +inline v_int8x64 v512_lut_pairs(const schar* tab, const v_uint8x64& idx) +{ return v_reinterpret_as_s8(v512_lut_pairs((const uchar *)tab, idx)); } + +// Byte-indexed quad LUT: 16 byte indices (lower 16 of idx) → 16 quads = 64 bytes +inline v_uint8x64 v512_lut_quads(const uchar* tab, const v_uint8x64& idx) +{ +#if CV_AVX_512VBMI + const __m512i lut0 = _mm512_loadu_si512(tab); + const __m512i lut1 = _mm512_loadu_si512(tab + 64); + const __m512i lut2 = _mm512_loadu_si512(tab + 128); + const __m512i lut3 = _mm512_loadu_si512(tab + 192); + + // Expand 16 byte indices to quad offsets: (i0, i0+1, i0+2, i0+3, i1, ...) + __m512i idx32 = _mm512_cvtepu8_epi32(_mm512_castsi512_si128(idx.val)); + __m512i t1 = _mm512_or_si512(idx32, _mm512_slli_epi32(idx32, 8)); + __m512i idx_bcast = _mm512_or_si512(t1, _mm512_slli_epi32(t1, 16)); + __m512i idx_quads = _mm512_add_epi8(idx_bcast, _mm512_set1_epi32(0x03020100)); + + __m512i idx6 = _mm512_and_si512(idx_quads, _mm512_set1_epi8(0x3F)); + + __m512i r0 = _mm512_permutexvar_epi8(idx6, lut0); + __m512i r1 = _mm512_permutexvar_epi8(idx6, lut1); + __m512i r2 = _mm512_permutexvar_epi8(idx6, lut2); + __m512i r3 = _mm512_permutexvar_epi8(idx6, lut3); + + __mmask64 k6 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8(0x40)); + __mmask64 k7 = _mm512_test_epi8_mask(idx_quads, _mm512_set1_epi8((char)0x80)); + + __m512i low = _mm512_mask_blend_epi8(k6, r0, r1); + __m512i high = _mm512_mask_blend_epi8(k6, r2, r3); + return v_uint8x64(_mm512_mask_blend_epi8(k7, low, high)); +#else + uchar CV_DECL_ALIGNED(64) indices[64], result[64]; + _mm512_store_si512((__m512i*)indices, idx.val); + for (int i = 0; i < 64; i++) result[i] = tab[indices[i]]; + return v_uint8x64(_mm512_load_si512((const __m512i*)result)); +#endif +} +inline v_int8x64 v512_lut_quads(const schar* tab, const v_uint8x64& idx) +{ return v_reinterpret_as_s8(v512_lut_quads((const uchar *)tab, idx)); } + inline v_int16x32 v512_lut(const short* tab, const int* idx) { __m256i p0 = _mm512_cvtepi32_epi16(_mm512_i32gather_epi32(_mm512_loadu_si512((const __m512i*)idx ), (const int *)tab, 2)); diff --git a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp index 756602c710..5f4dbfc571 100644 --- a/modules/core/include/opencv2/core/hal/intrin_cpp.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_cpp.hpp @@ -14,6 +14,7 @@ // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Copyright (C) 2015, Itseez Inc., all rights reserved. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -2714,6 +2715,16 @@ template inline v_reg<_Tp, simd128_width / sizeof(_Tp)> v_lut_quad return c; } +template inline v_reg v_lut(const uchar* tab, const v_reg& idx) +{ + v_reg c; + for( int i = 0; i < n; i++ ) + c.s[i] = tab[idx.s[i]]; + return c; +} +template inline v_reg v_lut(const schar* tab, const v_reg& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + template inline v_reg v_lut(const int* tab, const v_reg& idx) { v_reg c; diff --git a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp index 3661b7ef32..2bb4340932 100644 --- a/modules/core/include/opencv2/core/hal/intrin_lasx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_lasx.hpp @@ -1673,6 +1673,21 @@ inline v_uint8x32 v256_lut(const uchar* tab, const int* idx) { return v_reinterp inline v_uint8x32 v256_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_pairs((const schar *)tab, idx)); } inline v_uint8x32 v256_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v256_lut_quads((const schar *)tab, idx)); } +// Byte-indexed LUT: 32 byte indices in a vector -> 32 looked-up bytes +inline v_uint8x32 v256_lut(const uchar* tab, const v_uint8x32& idx) +{ + uchar CV_DECL_ALIGNED(32) indices[32], result[32]; + __lasx_xvst(idx.val, indices, 0); + for (int i = 0; i < 32; i++) result[i] = tab[indices[i]]; + return v_uint8x32(__lasx_xvld(result, 0)); +} +inline v_int8x32 v256_lut(const schar* tab, const v_uint8x32& idx) +{ return v_reinterpret_as_s8(v256_lut((const uchar*)tab, idx)); } + +// Universal v_lut overloads for vector byte indices (aliases to v256_lut) +inline v_uint8x32 v_lut(const uchar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); } +inline v_int8x32 v_lut(const schar* tab, const v_uint8x32& idx) { return v256_lut(tab, idx); } + inline v_int16x16 v256_lut(const short* tab, const int* idx) { return v_int16x16(_v256_setr_h(tab[idx[ 0]], tab[idx[ 1]], tab[idx[ 2]], tab[idx[ 3]], tab[idx[ 4]], diff --git a/modules/core/include/opencv2/core/hal/intrin_lsx.hpp b/modules/core/include/opencv2/core/hal/intrin_lsx.hpp index a2f23d6abe..457787b8dc 100644 --- a/modules/core/include/opencv2/core/hal/intrin_lsx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_lsx.hpp @@ -1443,6 +1443,16 @@ inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + __lsx_vst(idx.val, indices, 0); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(__lsx_vld(result, 0)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_int16x8 v_lut(const short* tab, const int* idx) { return v_int16x8(_v128_setr_h(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], diff --git a/modules/core/include/opencv2/core/hal/intrin_msa.hpp b/modules/core/include/opencv2/core/hal/intrin_msa.hpp index 3917faa292..a1458e224d 100644 --- a/modules/core/include/opencv2/core/hal/intrin_msa.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_msa.hpp @@ -1566,6 +1566,15 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + msa_st1q_u8(indices, idx.val); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(msa_ld1q_u8(result)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } inline v_int16x8 v_lut(const short* tab, const int* idx) { diff --git a/modules/core/include/opencv2/core/hal/intrin_neon.hpp b/modules/core/include/opencv2/core/hal/intrin_neon.hpp index 074e96da8a..62607f627e 100644 --- a/modules/core/include/opencv2/core/hal/intrin_neon.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_neon.hpp @@ -14,6 +14,7 @@ // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Copyright (C) 2015, Itseez Inc., all rights reserved. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -2370,6 +2371,48 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ +#if CV_NEON_AARCH64 + uint8x16_t index = idx.val; + uint8x16_t idx6 = vandq_u8(index, vdupq_n_u8(0x3F)); + + uint8x16x4_t t0, t1, t2, t3; + t0.val[0] = vld1q_u8(tab); t0.val[1] = vld1q_u8(tab + 16); + t0.val[2] = vld1q_u8(tab + 32); t0.val[3] = vld1q_u8(tab + 48); + uint8x16_t r0 = vqtbl4q_u8(t0, idx6); + + t1.val[0] = vld1q_u8(tab + 64); t1.val[1] = vld1q_u8(tab + 80); + t1.val[2] = vld1q_u8(tab + 96); t1.val[3] = vld1q_u8(tab + 112); + uint8x16_t r1 = vqtbl4q_u8(t1, idx6); + + t2.val[0] = vld1q_u8(tab + 128); t2.val[1] = vld1q_u8(tab + 144); + t2.val[2] = vld1q_u8(tab + 160); t2.val[3] = vld1q_u8(tab + 176); + uint8x16_t r2 = vqtbl4q_u8(t2, idx6); + + t3.val[0] = vld1q_u8(tab + 192); t3.val[1] = vld1q_u8(tab + 208); + t3.val[2] = vld1q_u8(tab + 224); t3.val[3] = vld1q_u8(tab + 240); + uint8x16_t r3 = vqtbl4q_u8(t3, idx6); + + uint8x16_t bit6 = vtstq_u8(index, vdupq_n_u8(0x40)); + uint8x16_t low = vbslq_u8(bit6, r1, r0); + uint8x16_t high = vbslq_u8(bit6, r3, r2); + + uint8x16_t bit7 = vtstq_u8(index, vdupq_n_u8(0x80)); + return v_uint8x16(vbslq_u8(bit7, high, low)); +#else + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + vst1q_u8(indices, idx.val); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(vld1q_u8(result)); +#endif +} + +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ + return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); +} + inline v_int16x8 v_lut(const short* tab, const int* idx) { short CV_DECL_ALIGNED(32) elems[8] = diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp index 146335dc01..8e470b18e5 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv071.hpp @@ -3,6 +3,7 @@ // of this distribution and at http://opencv.org/license.html // Copyright (C) 2015, PingTouGe Semiconductor Co., Ltd., all rights reserved. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. #ifndef OPENCV_HAL_INTRIN_RISCVV_HPP #define OPENCV_HAL_INTRIN_RISCVV_HPP @@ -1625,6 +1626,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + vse8_v_u8m1(indices, idx.val, 16); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(vle8_v_u8m1(result, 16)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_int16x8 v_lut(const short* tab, const int* idx) { #if 0 diff --git a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp index ca82d140f8..05fdbc4bdc 100644 --- a/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_rvv_scalable.hpp @@ -4,6 +4,7 @@ // The original implementation is contributed by HAN Liutong. // Copyright (C) 2022, Institute of Software, Chinese Academy of Sciences. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. #ifndef OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP #define OPENCV_HAL_INTRIN_RVV_SCALABLE_HPP @@ -534,6 +535,13 @@ inline void v_lut_deinterleave(const double* tab, const v_int32& vidx, v_float64 inline v_uint8 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); } inline v_uint8 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); } inline v_uint8 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((schar*)tab, idx)); } + +// Byte-indexed LUT: vector byte indices -> looked-up bytes (uses RVV indexed load) +inline v_uint8 v_lut(const uchar* tab, const v_uint8& idx) +{ return __riscv_vluxei8_v_u8m2(tab, idx, VTraits::vlanes()); } +inline v_int8 v_lut(const schar* tab, const v_uint8& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_uint16 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); } inline v_uint16 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); } inline v_uint16 v_lut_quads(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_quads((short*)tab, idx)); } diff --git a/modules/core/include/opencv2/core/hal/intrin_sse.hpp b/modules/core/include/opencv2/core/hal/intrin_sse.hpp index d9e0585eef..ca1f12416f 100644 --- a/modules/core/include/opencv2/core/hal/intrin_sse.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_sse.hpp @@ -14,6 +14,7 @@ // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Copyright (C) 2013, OpenCV Foundation, all rights reserved. // Copyright (C) 2015, Itseez Inc., all rights reserved. +// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, @@ -3084,6 +3085,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + _mm_store_si128((__m128i*)indices, idx.val); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(_mm_load_si128((const __m128i*)result)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_int16x8 v_lut(const short* tab, const int* idx) { return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], diff --git a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp index 0a0915a22f..a0383e7352 100644 --- a/modules/core/include/opencv2/core/hal/intrin_vsx.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_vsx.hpp @@ -1175,6 +1175,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar*)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar*)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + vsx_st(idx.val, 0, indices); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(vsx_ld(0, result)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_int16x8 v_lut(const short* tab, const int* idx) { return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], tab[idx[4]], tab[idx[5]], tab[idx[6]], tab[idx[7]]); diff --git a/modules/core/include/opencv2/core/hal/intrin_wasm.hpp b/modules/core/include/opencv2/core/hal/intrin_wasm.hpp index 3d8588a68d..256637552e 100644 --- a/modules/core/include/opencv2/core/hal/intrin_wasm.hpp +++ b/modules/core/include/opencv2/core/hal/intrin_wasm.hpp @@ -2584,6 +2584,16 @@ inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((const schar *)tab, idx)); } inline v_uint8x16 v_lut_quads(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_quads((const schar *)tab, idx)); } +inline v_uint8x16 v_lut(const uchar* tab, const v_uint8x16& idx) +{ + uchar CV_DECL_ALIGNED(16) indices[16], result[16]; + wasm_v128_store(indices, idx.val); + for (int i = 0; i < 16; i++) result[i] = tab[indices[i]]; + return v_uint8x16(wasm_v128_load(result)); +} +inline v_int8x16 v_lut(const schar* tab, const v_uint8x16& idx) +{ return v_reinterpret_as_s8(v_lut((const uchar*)tab, idx)); } + inline v_int16x8 v_lut(const short* tab, const int* idx) { return v_int16x8(tab[idx[0]], tab[idx[1]], tab[idx[2]], tab[idx[3]], diff --git a/modules/core/src/lut.cpp b/modules/core/src/lut.cpp index feae254358..1736938ea3 100644 --- a/modules/core/src/lut.cpp +++ b/modules/core/src/lut.cpp @@ -1,6 +1,7 @@ // 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, Advanced Micro Devices, Inc., all rights reserved. #include "precomp.hpp" @@ -8,6 +9,11 @@ #include "convert.hpp" #include +namespace cv { +void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn ); +void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn ); +} // namespace cv + /****************************************************************************************\ * LUT Transform * \****************************************************************************************/ @@ -40,9 +46,9 @@ static LUTFunc getLUTFunc(const int srcDepth, const int dstDepth) { switch(dstDepth) { - case CV_8U: ret = (LUTFunc)LUT_; break; + case CV_8U: ret = (LUTFunc)LUT8u_dispatch; break; case CV_8S: ret = (LUTFunc)LUT_; break; - case CV_16U: ret = (LUTFunc)LUT_; break; + case CV_16U: ret = (LUTFunc)LUT16u_dispatch; break; case CV_16S: ret = (LUTFunc)LUT_; break; case CV_32S: ret = (LUTFunc)LUT_; break; case CV_32F: ret = (LUTFunc)LUT_; break; // float diff --git a/modules/core/src/lut.dispatch.cpp b/modules/core/src/lut.dispatch.cpp new file mode 100644 index 0000000000..a95a54daa0 --- /dev/null +++ b/modules/core/src/lut.dispatch.cpp @@ -0,0 +1,56 @@ +// 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, Advanced Micro Devices, Inc., all rights reserved. + +#include "precomp.hpp" + +#include "lut.simd.hpp" +#include "lut.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX512_ICL,...,BASELINE based on CMakeLists.txt content + +namespace cv { + +namespace { + +typedef void (*LUT8uFunc)( const uchar*, const uchar*, uchar*, int, int, int ); +typedef void (*LUT16uFunc)( const uchar*, const ushort*, ushort*, int, int, int ); + +static LUT8uFunc resolveLUT8uFunc() +{ +#if CV_TRY_AVX512_ICL + if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL)) + return opt_AVX512_ICL::LUT8u_; +#endif + return cpu_baseline::LUT8u_; +} + +static LUT16uFunc resolveLUT16uFunc() +{ +#if CV_TRY_AVX512_ICL + if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL)) + return opt_AVX512_ICL::LUT16u_; +#endif + return cpu_baseline::LUT16u_; +} + +} // namespace + +void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn ); + +void LUT8u_dispatch( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn ) +{ + CV_INSTRUMENT_REGION(); + static LUT8uFunc fn = resolveLUT8uFunc(); + fn(src, lut, dst, len, cn, lutcn); +} + +void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn ); + +void LUT16u_dispatch( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn ) +{ + CV_INSTRUMENT_REGION(); + static LUT16uFunc fn = resolveLUT16uFunc(); + fn(src, lut, dst, len, cn, lutcn); +} + +} // namespace cv diff --git a/modules/core/src/lut.simd.hpp b/modules/core/src/lut.simd.hpp new file mode 100644 index 0000000000..c8152a5ae5 --- /dev/null +++ b/modules/core/src/lut.simd.hpp @@ -0,0 +1,146 @@ +// 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, Advanced Micro Devices, Inc., all rights reserved. + +#include "precomp.hpp" + +// VBMI vpermb is the only x86 SIMD path faster than scalar for byte LUT. +// x86 gather (AVX2/SSE) is slower than scalar due to high gather latency. +// Non-x86 (NEON, etc.) benefits from v_lut implementation. +#if CV_AVX_512VBMI +#define CV_LUT8U_SIMD 1 +#elif (defined(CV_CPU_COMPILE_SSE) || defined(CV_CPU_COMPILE_SSE2) || defined(CV_CPU_COMPILE_SSE3) || \ + defined(CV_CPU_COMPILE_SSSE3) || defined(CV_CPU_COMPILE_SSE4_1) || defined(CV_CPU_COMPILE_SSE4_2) || \ + defined(CV_CPU_COMPILE_AVX) || defined(CV_CPU_COMPILE_AVX2) || defined(CV_CPU_COMPILE_AVX_512F) || \ + defined(CV_CPU_COMPILE_AVX512_COMMON) || defined(CV_CPU_COMPILE_AVX512_SKX) || \ + defined(CV_CPU_COMPILE_AVX512_CNL) || defined(CV_CPU_COMPILE_AVX512_CLX)) +#define CV_LUT8U_SIMD 0 +#elif CV_SIMD || CV_SIMD_SCALABLE +#define CV_LUT8U_SIMD 1 +#else +#define CV_LUT8U_SIMD 0 +#endif + +namespace cv { +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn ); +void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn ); + +#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +void LUT8u_( const uchar* src, const uchar* lut, uchar* dst, int len, int cn, int lutcn ) +{ + const int total = len * cn; + + if( lutcn == 1 ) + { + int i = 0; +#if CV_LUT8U_SIMD + const int nlanes = VTraits::vlanes(); + for( ; i <= total - nlanes; i += nlanes ) + { + v_uint8 idx = vx_load(src + i); + v_uint8 result = v_lut(lut, idx); + v_store(dst + i, result); + } +#endif + for( ; i < total; i++ ) + dst[i] = lut[src[i]]; + } +#if CV_LUT8U_SIMD + else if( cn == 3 ) + { + // Deinterleave the 3-channel LUT into per-channel tables + uchar CV_DECL_ALIGNED(64) lut0[256], lut1[256], lut2[256]; + const int nlanes = VTraits::vlanes(); + { + unsigned j = 0; + for( ; j + (unsigned)nlanes <= 256; j += (unsigned)nlanes ) + { + v_uint8 a, b, c; + v_load_deinterleave(lut + j * 3, a, b, c); + v_store(lut0 + j, a); + v_store(lut1 + j, b); + v_store(lut2 + j, c); + } + for( ; j < 256; j++ ) + { + const unsigned idx = j * 3; + lut0[j] = lut[idx]; + lut1[j] = lut[idx + 1]; + lut2[j] = lut[idx + 2]; + } + } + int i = 0; + for( ; i <= total - nlanes*3; i += nlanes*3 ) + { + v_uint8 r, g, b; + v_load_deinterleave(src + i, r, g, b); + r = v_lut(lut0, r); + g = v_lut(lut1, g); + b = v_lut(lut2, b); + v_store_interleave(dst + i, r, g, b); + } + for( ; i < total; i += 3 ) + { + dst[i] = lut0[src[i]]; + dst[i+1] = lut1[src[i+1]]; + dst[i+2] = lut2[src[i+2]]; + } + } +#endif + else + { + for( int i = 0; i < total; i += cn ) + for( int k = 0; k < cn; k++ ) + dst[i+k] = lut[src[i+k]*cn + k]; + } +} + +void LUT16u_( const uchar* src, const ushort* lut, ushort* dst, int len, int cn, int lutcn ) +{ + const int total = len * cn; + + if( lutcn == 1 ) + { + int i = 0; +#if CV_LUT8U_SIMD + // Split ushort LUT into low-byte and high-byte tables, + // then use two byte v_lut (vpermb on VBMI) + interleave. + uchar CV_DECL_ALIGNED(64) lut_lo[256], lut_hi[256]; + for( int j = 0; j < 256; j++ ) + { + lut_lo[j] = (uchar)(lut[j]); + lut_hi[j] = (uchar)(lut[j] >> 8); + } + const int nlanes8 = VTraits::vlanes(); + const int nlanes16 = VTraits::vlanes(); + for( ; i <= total - nlanes8; i += nlanes8 ) + { + v_uint8 idx = vx_load(src + i); + v_uint8 lo = v_lut(lut_lo, idx); + v_uint8 hi = v_lut(lut_hi, idx); + // Zip low and high bytes: [l0,h0,l1,h1,...] → ushort values on little-endian + v_uint8 res0, res1; + v_zip(lo, hi, res0, res1); + v_store(dst + i, v_reinterpret_as_u16(res0)); + v_store(dst + i + nlanes16, v_reinterpret_as_u16(res1)); + } +#endif + for( ; i < total; i++ ) + dst[i] = lut[src[i]]; + } + else + { + for( int i = 0; i < total; i += cn ) + for( int k = 0; k < cn; k++ ) + dst[i+k] = lut[src[i+k]*cn + k]; + } +} + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END +} // namespace cv diff --git a/modules/imgproc/CMakeLists.txt b/modules/imgproc/CMakeLists.txt index c0eb22023c..6ff2621c80 100644 --- a/modules/imgproc/CMakeLists.txt +++ b/modules/imgproc/CMakeLists.txt @@ -10,6 +10,7 @@ ocv_add_dispatched_file(median_blur SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL) ocv_add_dispatched_file(morph SSE2 SSE4_1 AVX2) ocv_add_dispatched_file(smooth SSE2 SSE4_1 AVX2 AVX512_ICL) ocv_add_dispatched_file(sumpixels SSE2 AVX2 AVX512_SKX) +ocv_add_dispatched_file(equalize_hist AVX512_ICL) ocv_define_module(imgproc opencv_core WRAP java objc python js) if(OPENCV_CORE_EXCLUDE_C_API) diff --git a/modules/imgproc/src/equalize_hist.dispatch.cpp b/modules/imgproc/src/equalize_hist.dispatch.cpp new file mode 100644 index 0000000000..ebe0c347a3 --- /dev/null +++ b/modules/imgproc/src/equalize_hist.dispatch.cpp @@ -0,0 +1,37 @@ +// 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, Advanced Micro Devices, Inc., all rights reserved. + +#include "precomp.hpp" + +#include "equalize_hist.simd.hpp" +#include "equalize_hist.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX512_ICL,...,BASELINE + +namespace cv { + +namespace { + +typedef void (*EqualizeHistLutFunc)( const uchar*, uchar*, int, const uchar* ); + +static EqualizeHistLutFunc resolveEqualizeHistLutFunc() +{ +#if CV_TRY_AVX512_ICL + if (cv::checkHardwareSupport(CV_CPU_AVX512_ICL)) + return opt_AVX512_ICL::equalizeHistLut_; +#endif + return cpu_baseline::equalizeHistLut_; +} + +} // namespace + +void equalizeHistLut_dispatch( const uchar* src, uchar* dst, int len, const uchar* lut ); + +void equalizeHistLut_dispatch( const uchar* src, uchar* dst, int len, const uchar* lut ) +{ + CV_INSTRUMENT_REGION(); + static EqualizeHistLutFunc fn = resolveEqualizeHistLutFunc(); + fn(src, dst, len, lut); +} + +} // namespace cv diff --git a/modules/imgproc/src/equalize_hist.simd.hpp b/modules/imgproc/src/equalize_hist.simd.hpp new file mode 100644 index 0000000000..3fc3595d5e --- /dev/null +++ b/modules/imgproc/src/equalize_hist.simd.hpp @@ -0,0 +1,61 @@ +// 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, Advanced Micro Devices, Inc., all rights reserved. + +#include "precomp.hpp" +#include "opencv2/core/hal/intrin.hpp" + +// VBMI vpermb is the only x86 SIMD path faster than scalar for byte LUT. +// Non-VBMI x86 SIMD (gather-based) is slower than scalar for byte LUT. +// Non-x86 (NEON, RVV, etc.) benefits from v_lut implementation. +#if CV_AVX_512VBMI +#define CV_EQUALIZE_HIST_SIMD 1 +#elif (defined(CV_CPU_COMPILE_SSE) || defined(CV_CPU_COMPILE_SSE2) || defined(CV_CPU_COMPILE_SSE3) || \ + defined(CV_CPU_COMPILE_SSSE3) || defined(CV_CPU_COMPILE_SSE4_1) || defined(CV_CPU_COMPILE_SSE4_2) || \ + defined(CV_CPU_COMPILE_AVX) || defined(CV_CPU_COMPILE_AVX2) || defined(CV_CPU_COMPILE_AVX_512F) || \ + defined(CV_CPU_COMPILE_AVX512_COMMON) || defined(CV_CPU_COMPILE_AVX512_SKX) || \ + defined(CV_CPU_COMPILE_AVX512_CNL) || defined(CV_CPU_COMPILE_AVX512_CLX)) +#define CV_EQUALIZE_HIST_SIMD 0 +#elif CV_SIMD || CV_SIMD_SCALABLE +#define CV_EQUALIZE_HIST_SIMD 1 +#else +#define CV_EQUALIZE_HIST_SIMD 0 +#endif + +namespace cv { +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +void equalizeHistLut_( const uchar* src, uchar* dst, int len, const uchar* lut ); + +#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +void equalizeHistLut_( const uchar* src, uchar* dst, int len, const uchar* lut ) +{ + int x = 0; +#if CV_EQUALIZE_HIST_SIMD + const int nlanes = VTraits::vlanes(); + for( ; x <= len - nlanes; x += nlanes ) + { + v_uint8 idx = vx_load(src + x); + v_uint8 result = v_lut(lut, idx); + v_store(dst + x, result); + } +#endif + for( ; x <= len - 4; x += 4 ) + { + uchar v0 = src[x], v1 = src[x+1]; + dst[x] = lut[v0]; + dst[x+1] = lut[v1]; + v0 = src[x+2]; v1 = src[x+3]; + dst[x+2] = lut[v0]; + dst[x+3] = lut[v1]; + } + for( ; x < len; x++ ) + dst[x] = lut[src[x]]; +} + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END +} // namespace cv diff --git a/modules/imgproc/src/histogram.cpp b/modules/imgproc/src/histogram.cpp index b240896575..9691f61fa3 100644 --- a/modules/imgproc/src/histogram.cpp +++ b/modules/imgproc/src/histogram.cpp @@ -3321,10 +3321,14 @@ private: cv::Mutex* histogramLock_; }; +namespace cv { + void equalizeHistLut_dispatch( const uchar* src, uchar* dst, int len, const uchar* lut ); +} + class EqualizeHistLut_Invoker : public cv::ParallelLoopBody { public: - EqualizeHistLut_Invoker( cv::Mat& src, cv::Mat& dst, int* lut ) + EqualizeHistLut_Invoker( cv::Mat& src, cv::Mat& dst, uchar* lut ) : src_(src), dst_(dst), lut_(lut) @@ -3337,7 +3341,7 @@ public: int width = src_.cols; int height = rowRange.end - rowRange.start; - int* lut = lut_; + const uchar* lut = lut_; if (src_.isContinuous() && dst_.isContinuous()) { @@ -3350,26 +3354,7 @@ public: for (; height--; sptr += sstep, dptr += dstep) { - int x = 0; - for (; x <= width - 4; x += 4) - { - int v0 = sptr[x]; - int v1 = sptr[x+1]; - int x0 = lut[v0]; - int x1 = lut[v1]; - dptr[x] = (uchar)x0; - dptr[x+1] = (uchar)x1; - - v0 = sptr[x+2]; - v1 = sptr[x+3]; - x0 = lut[v0]; - x1 = lut[v1]; - dptr[x+2] = (uchar)x0; - dptr[x+3] = (uchar)x1; - } - - for (; x < width; ++x) - dptr[x] = (uchar)lut[sptr[x]]; + cv::equalizeHistLut_dispatch(sptr, dptr, width, lut); } } @@ -3383,7 +3368,7 @@ private: cv::Mat& src_; cv::Mat& dst_; - int* lut_; + uchar* lut_; }; CV_IMPL void cvEqualizeHist( const CvArr* srcarr, CvArr* dstarr ) @@ -3464,10 +3449,9 @@ void cv::equalizeHist( InputArray _src, OutputArray _dst ) const int hist_sz = EqualizeHistCalcHist_Invoker::HIST_SZ; int hist[hist_sz] = {0,}; - int lut[hist_sz]; + int lut[hist_sz] = {0,}; EqualizeHistCalcHist_Invoker calcBody(src, hist, &histogramLockInstance); - EqualizeHistLut_Invoker lutBody(src, dst, lut); cv::Range heightRange(0, src.rows); if(EqualizeHistCalcHist_Invoker::isWorthParallel(src)) @@ -3494,6 +3478,12 @@ void cv::equalizeHist( InputArray _src, OutputArray _dst ) lut[i] = saturate_cast(sum * scale); } + uchar lut_u8[hist_sz]; + for (int j = 0; j < hist_sz; ++j) + lut_u8[j] = (uchar)lut[j]; + + EqualizeHistLut_Invoker lutBody(src, dst, lut_u8); + if(EqualizeHistLut_Invoker::isWorthParallel(src)) parallel_for_(heightRange, lutBody); else