From d32d4da9a39ce92bdc5603749ea6f2d90215f4ea Mon Sep 17 00:00:00 2001 From: kyler1cartesis <87242609+kyler1cartesis@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:13:48 +0300 Subject: [PATCH 01/19] Merge pull request #26887 from kyler1cartesis:4.x invSqrt SIMD_SCALABLE implementation & HAL tests refactoring #26887 Enable CV_SIMD_SCALABLE for invSqrt. * Banana Pi BF3 (SpacemiT K1) RISC-V * Compiler: Syntacore Clang 18.1.4 (build 2024.12) ``` Geometric mean (ms) Name of Test baseline simd simd scalable scalable vs baseline (x-factor) InvSqrtf::InvSqrtfFixture::(127x61, 32FC1) 0.163 0.051 3.23 InvSqrtf::InvSqrtfFixture::(127x61, 64FC1) 0.241 0.103 2.35 InvSqrtf::InvSqrtfFixture::(640x480, 32FC1) 6.460 1.893 3.41 InvSqrtf::InvSqrtfFixture::(640x480, 64FC1) 9.687 3.999 2.42 InvSqrtf::InvSqrtfFixture::(1280x720, 32FC1) 19.292 5.701 3.38 InvSqrtf::InvSqrtfFixture::(1280x720, 64FC1) 29.452 11.963 2.46 InvSqrtf::InvSqrtfFixture::(1920x1080, 32FC1) 43.326 12.805 3.38 InvSqrtf::InvSqrtfFixture::(1920x1080, 64FC1) 65.566 26.881 2.44 ``` --- modules/core/perf/perf_arithm.cpp | 17 ++ modules/core/src/mathfuncs_core.simd.hpp | 4 +- modules/core/test/test_hal_core.cpp | 289 +++++++++++------------ 3 files changed, 161 insertions(+), 149 deletions(-) diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index d98eb9abb3..6cc25a3476 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -706,6 +706,23 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest, ) ); +typedef Size_MatType InvSqrtFixture; +PERF_TEST_P(InvSqrtFixture, InvSqrt, testing::Combine( + testing::Values(TYPICAL_MAT_SIZES), + testing::Values(CV_32FC1, CV_64FC1))) +{ + Size sz = get<0>(GetParam()); + int type = get<1>(GetParam()); + + Mat src(sz, type), dst(sz, type); + randu(src, FLT_EPSILON, 1000); + declare.in(src).out(dst); + + TEST_CYCLE() cv::pow(src, -0.5, dst); + + SANITY_CHECK_NOTHING(); +} + ///////////// Rotate //////////////////////// typedef perf::TestBaseWithParam> RotateTest; diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index cb21064041..3fa3cba1b8 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -340,7 +340,7 @@ void invSqrt32f(const float* src, float* dst, int len) int i = 0; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); for( ; i < len; i += VECSZ*2 ) { @@ -368,7 +368,7 @@ void invSqrt64f(const double* src, double* dst, int len) CV_INSTRUMENT_REGION(); int i = 0; -#if CV_SIMD_64F +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) const int VECSZ = VTraits::vlanes(); for ( ; i < len; i += VECSZ*2) { diff --git a/modules/core/test/test_hal_core.cpp b/modules/core/test/test_hal_core.cpp index f9078e55f9..a86016d44f 100644 --- a/modules/core/test/test_hal_core.cpp +++ b/modules/core/test/test_hal_core.cpp @@ -42,168 +42,163 @@ namespace opencv_test { namespace { -enum +enum HALFunc { HAL_EXP = 0, HAL_LOG = 1, - HAL_SQRT = 2 + HAL_SQRT = 2, + HAL_INV_SQRT = 3, + HAL_LU = 4, + HAL_CHOL = 5, }; -TEST(Core_HAL, mathfuncs) +void PrintTo(const HALFunc& v, std::ostream* os) { - for( int hcase = 0; hcase < 6; hcase++ ) - { - int depth = hcase % 2 == 0 ? CV_32F : CV_64F; - double eps = depth == CV_32F ? 1e-5 : 1e-10; - int nfunc = hcase / 2; - int n = 100; - - Mat src(1, n, depth), dst(1, n, depth), dst0(1, n, depth); - randu(src, 1, 10); - - double min_hal_t = DBL_MAX, min_ocv_t = DBL_MAX; - - for( int iter = 0; iter < 10; iter++ ) - { - double t = (double)getTickCount(); - switch (nfunc) - { - case HAL_EXP: - if( depth == CV_32F ) - hal::exp32f(src.ptr(), dst.ptr(), n); - else - hal::exp64f(src.ptr(), dst.ptr(), n); - break; - case HAL_LOG: - if( depth == CV_32F ) - hal::log32f(src.ptr(), dst.ptr(), n); - else - hal::log64f(src.ptr(), dst.ptr(), n); - break; - case HAL_SQRT: - if( depth == CV_32F ) - hal::sqrt32f(src.ptr(), dst.ptr(), n); - else - hal::sqrt64f(src.ptr(), dst.ptr(), n); - break; - default: - CV_Error(Error::StsBadArg, "unknown function"); - } - t = (double)getTickCount() - t; - min_hal_t = std::min(min_hal_t, t); - - t = (double)getTickCount(); - switch (nfunc) - { - case HAL_EXP: - exp(src, dst0); - break; - case HAL_LOG: - log(src, dst0); - break; - case HAL_SQRT: - pow(src, 0.5, dst0); - break; - default: - CV_Error(Error::StsBadArg, "unknown function"); - } - t = (double)getTickCount() - t; - min_ocv_t = std::min(min_ocv_t, t); - } - EXPECT_LE(cvtest::norm(dst, dst0, NORM_INF | NORM_RELATIVE), eps); - - double freq = getTickFrequency(); - printf("%s (N=%d, %s): hal time=%.2fusec, ocv time=%.2fusec\n", - (nfunc == HAL_EXP ? "exp" : nfunc == HAL_LOG ? "log" : nfunc == HAL_SQRT ? "sqrt" : "???"), - n, (depth == CV_32F ? "f32" : "f64"), min_hal_t*1e6/freq, min_ocv_t*1e6/freq); - } + switch (v) { + case HAL_EXP: *os << "HAL_EXP"; return; + case HAL_LOG: *os << "HAL_LOG"; return; + case HAL_SQRT: *os << "HAL_SQRT"; return; + case HAL_INV_SQRT: *os << "HAL_INV_SQRT"; return; + case HAL_LU: *os << "LU"; return; + case HAL_CHOL: *os << "Cholesky"; return; + } // don't use "default:" to emit compiler warnings } -enum +typedef testing::TestWithParam > mathfuncs; +TEST_P(mathfuncs, accuracy) { - HAL_LU = 0, - HAL_CHOL = 1 -}; + const int depth = std::get<0>(GetParam()); + const int nfunc = std::get<1>(GetParam()); -typedef testing::TestWithParam HAL; + double eps = depth == CV_32F ? 1e-5 : 1e-10; + int n = 100; -TEST_P(HAL, mat_decomp) -{ - int hcase = GetParam(); - SCOPED_TRACE(cv::format("hcase=%d", hcase)); + Mat src(1, n, depth), dst(1, n, depth), dst0(1, n, depth); + randu(src, 1, 10); + + switch (nfunc) { - int depth = hcase % 2 == 0 ? CV_32F : CV_64F; - int size = (hcase / 2) % 4; - size = size == 0 ? 3 : size == 1 ? 4 : size == 2 ? 6 : 15; - int nfunc = (hcase / 8); - #if CV_LASX - double eps = depth == CV_32F ? 1e-5 : 2e-10; - #else - double eps = depth == CV_32F ? 1e-5 : 1e-10; - #endif + case HAL_EXP: + if( depth == CV_32F ) + hal::exp32f(src.ptr(), dst.ptr(), n); + else + hal::exp64f(src.ptr(), dst.ptr(), n); + break; + case HAL_LOG: + if( depth == CV_32F ) + hal::log32f(src.ptr(), dst.ptr(), n); + else + hal::log64f(src.ptr(), dst.ptr(), n); + break; + case HAL_SQRT: + if( depth == CV_32F ) + hal::sqrt32f(src.ptr(), dst.ptr(), n); + else + hal::sqrt64f(src.ptr(), dst.ptr(), n); + break; + case HAL_INV_SQRT: + if( depth == CV_32F ) + hal::invSqrt32f(src.ptr(), dst.ptr(), n); + else + hal::invSqrt64f(src.ptr(), dst.ptr(), n); + break; - if( size == 3 ) - return; // TODO ??? - - Mat a0(size, size, depth), a(size, size, depth), b(size, 1, depth), x(size, 1, depth), x0(size, 1, depth); - randu(a0, -1, 1); - a0 = a0*a0.t(); - randu(b, -1, 1); - - double min_hal_t = DBL_MAX, min_ocv_t = DBL_MAX; - size_t asize = size*size*a.elemSize(); - size_t bsize = size*b.elemSize(); - - for( int iter = 0; iter < 10; iter++ ) - { - memcpy(x.ptr(), b.ptr(), bsize); - memcpy(a.ptr(), a0.ptr(), asize); - - double t = (double)getTickCount(); - switch (nfunc) - { - case HAL_LU: - if( depth == CV_32F ) - hal::LU32f(a.ptr(), a.step, size, x.ptr(), x.step, 1); - else - hal::LU64f(a.ptr(), a.step, size, x.ptr(), x.step, 1); - break; - case HAL_CHOL: - if( depth == CV_32F ) - hal::Cholesky32f(a.ptr(), a.step, size, x.ptr(), x.step, 1); - else - hal::Cholesky64f(a.ptr(), a.step, size, x.ptr(), x.step, 1); - break; - default: - CV_Error(Error::StsBadArg, "unknown function"); - } - t = (double)getTickCount() - t; - min_hal_t = std::min(min_hal_t, t); - - t = (double)getTickCount(); - bool solveStatus = solve(a0, b, x0, (nfunc == HAL_LU ? DECOMP_LU : DECOMP_CHOLESKY)); - t = (double)getTickCount() - t; - EXPECT_TRUE(solveStatus); - min_ocv_t = std::min(min_ocv_t, t); - } - //std::cout << "x: " << Mat(x.t()) << std::endl; - //std::cout << "x0: " << Mat(x0.t()) << std::endl; - - EXPECT_LE(cvtest::norm(x, x0, NORM_INF | NORM_RELATIVE), eps) - << "x: " << Mat(x.t()) - << "\nx0: " << Mat(x0.t()) - << "\na0: " << a0 - << "\nb: " << b; - - double freq = getTickFrequency(); - printf("%s (%d x %d, %s): hal time=%.2fusec, ocv time=%.2fusec\n", - (nfunc == HAL_LU ? "LU" : nfunc == HAL_CHOL ? "Cholesky" : "???"), - size, size, - (depth == CV_32F ? "f32" : "f64"), - min_hal_t*1e6/freq, min_ocv_t*1e6/freq); + default: + CV_Error(Error::StsBadArg, "unknown function"); } + + src.copyTo(dst0); + switch (nfunc) + { + case HAL_EXP: + if( depth == CV_32F ) + dst0.forEach([](float& v, const int*) -> void { v = std::exp(v); }); + else + dst0.forEach([](double& v, const int*) -> void { v = std::exp(v); }); + break; + case HAL_LOG: + if( depth == CV_32F ) + dst0.forEach([](float& v, const int*) -> void { v = std::log(v); }); + else + dst0.forEach([](double& v, const int*) -> void { v = std::log(v); }); + break; + case HAL_SQRT: + if( depth == CV_32F ) + dst0.forEach([](float& v, const int*) -> void { v = std::sqrt(v); }); + else + dst0.forEach([](double& v, const int*) -> void { v = std::sqrt(v); }); + break; + case HAL_INV_SQRT: + if( depth == CV_32F ) + dst0.forEach([](float& v, const int*) -> void { v = std::pow(v, -0.5f); }); + else + dst0.forEach([](double& v, const int*) -> void { v = std::pow(v, -0.5); }); + break; + default: + CV_Error(Error::StsBadArg, "unknown function"); + } + EXPECT_LE(cvtest::norm(dst, dst0, NORM_INF | NORM_RELATIVE), eps); +} +INSTANTIATE_TEST_CASE_P(Core_HAL, mathfuncs, + testing::Combine( + testing::Values(CV_32F, CV_64F), + testing::Values(HAL_EXP, HAL_LOG, HAL_SQRT, HAL_INV_SQRT) + ) +); + +typedef testing::TestWithParam > mat_decomp; +TEST_P(mat_decomp, accuracy) +{ + const int depth = std::get<0>(GetParam()); + const int nfunc = std::get<1>(GetParam()); + const int size = std::get<2>(GetParam()); + +#if CV_LASX + double eps = depth == CV_32F ? 1e-5 : 2e-10; +#else + double eps = depth == CV_32F ? 1e-5 : 1e-10; +#endif + + Mat a0(size, size, depth), x0(size, 1, depth); + randu(a0, -1, 1); + a0 = a0*a0.t(); + randu(x0, -1, 1); + Mat b = a0 * x0; + Mat x = b.clone(); + Mat a = a0.clone(); + + int solveStatus; + switch (nfunc) + { + case HAL_LU: + if( depth == CV_32F ) + solveStatus = hal::LU32f(a.ptr(), a.step, size, x.ptr(), x.step, 1); + else + solveStatus = hal::LU64f(a.ptr(), a.step, size, x.ptr(), x.step, 1); + break; + case HAL_CHOL: + if( depth == CV_32F ) + solveStatus = hal::Cholesky32f(a.ptr(), a.step, size, x.ptr(), x.step, 1); + else + solveStatus = hal::Cholesky64f(a.ptr(), a.step, size, x.ptr(), x.step, 1); + break; + default: + CV_Error(Error::StsBadArg, "unknown function"); + } + EXPECT_NE(0, solveStatus); + EXPECT_LE(cvtest::norm(a0 * x, b, NORM_INF | NORM_RELATIVE), eps) + << "x: " << Mat(x.t()) + << "\nx0: " << Mat(x0.t()) + << "\na0: " << a0 + << "\nb: " << b; } -INSTANTIATE_TEST_CASE_P(Core, HAL, testing::Range(0, 16)); +INSTANTIATE_TEST_CASE_P(Core_HAL, mat_decomp, + testing::Combine( + testing::Values(CV_32F, CV_64F), + testing::Values(HAL_LU, HAL_CHOL), + testing::Values(3, 4, 6, 15) + ) +); }} // namespace From a6bfd87943890945b5daf5fac19cf4fe2198f8ad Mon Sep 17 00:00:00 2001 From: Vincent Rabaud Date: Wed, 19 Feb 2025 16:51:56 +0100 Subject: [PATCH 02/19] Bump openjp2 to v2.5.3 This should quiet some fuzzer bugs --- 3rdparty/openjpeg/CHANGELOG.md | 134 ++++++- 3rdparty/openjpeg/CMakeLists.txt | 2 +- 3rdparty/openjpeg/openjp2/bench_dwt.c | 1 + 3rdparty/openjpeg/openjp2/dwt.c | 224 ++++++++++- 3rdparty/openjpeg/openjp2/ht_dec.c | 6 +- 3rdparty/openjpeg/openjp2/j2k.c | 520 +++++++++++++++++++++---- 3rdparty/openjpeg/openjp2/j2k.h | 30 ++ 3rdparty/openjpeg/openjp2/jp2.c | 12 +- 3rdparty/openjpeg/openjp2/openjpeg.h | 14 +- 3rdparty/openjpeg/openjp2/opj_common.h | 6 +- 3rdparty/openjpeg/openjp2/t1.c | 153 +++++++- 3rdparty/openjpeg/openjp2/t2.c | 60 +-- 3rdparty/openjpeg/openjp2/tcd.c | 6 +- 3rdparty/openjpeg/openjp2/tcd.h | 7 +- 14 files changed, 1047 insertions(+), 128 deletions(-) diff --git a/3rdparty/openjpeg/CHANGELOG.md b/3rdparty/openjpeg/CHANGELOG.md index c7f5fb6d3d..926e6f1f4f 100644 --- a/3rdparty/openjpeg/CHANGELOG.md +++ b/3rdparty/openjpeg/CHANGELOG.md @@ -1,5 +1,137 @@ # Changelog +## [v2.5.3](https://github.com/uclouvain/openjpeg/releases/v2.5.3) (2024-12-09) + +[Full Changelog](https://github.com/uclouvain/openjpeg/compare/v2.5.2...v2.5.3) + +**Closed issues:** + +- Memory Leak When Using Invalid Output Path in opj\_compress [\#1567](https://github.com/uclouvain/openjpeg/issues/1567) +- heap-buffer-overflow at lib/openjp2/j2k.c:8460:84 in opj\_j2k\_add\_tlmarker in openjpeg/opj\_decompress [\#1564](https://github.com/uclouvain/openjpeg/issues/1564) +- heap-buffer-overflow at bin/common/color.c:215:42 in sycc422\_to\_rgb in openjpeg/opj\_decompress [\#1563](https://github.com/uclouvain/openjpeg/issues/1563) +- Can not open libjpeg [\#1550](https://github.com/uclouvain/openjpeg/issues/1550) +- \[ERROR\] Wrong values for: w\(525\) h\(700\) numcomps\(0\) \(ihdr\) [\#1545](https://github.com/uclouvain/openjpeg/issues/1545) +- Failed to Open a Specific JP2 file [\#1544](https://github.com/uclouvain/openjpeg/issues/1544) +- Outdated File in OpenJPEG Project Leading to Vulnerability \(CVE-2016-9534\) [\#1539](https://github.com/uclouvain/openjpeg/issues/1539) +- Heap-buffer-overflow in in opj\_mqc\_init\_dec\_common when disabling strict mode [\#1535](https://github.com/uclouvain/openjpeg/issues/1535) +- Heap-buffer-overflow in in opj\_t1\_decode\_cblk when disabling strict mode [\#1533](https://github.com/uclouvain/openjpeg/issues/1533) +- opj\_decode\_tile\_data takes a long time to decode a very small file [\#1524](https://github.com/uclouvain/openjpeg/issues/1524) +- Release v2.5.2 tag is outside of the repository [\#1521](https://github.com/uclouvain/openjpeg/issues/1521) +- Website broken [\#1513](https://github.com/uclouvain/openjpeg/issues/1513) +- Guard Bits In CINEMA2K Profile [\#1340](https://github.com/uclouvain/openjpeg/issues/1340) +- Support for OSX on ARM \(M1\) [\#1289](https://github.com/uclouvain/openjpeg/issues/1289) +- Building on Windows creates \_\_stdcall-convention static lib/dll regardless of compiler settings [\#722](https://github.com/uclouvain/openjpeg/issues/722) + +**Merged pull requests:** + +- sycc422\_to\_rgb\(\): fix out-of-bounds read accesses when 2 \* width\_component\_1\_or\_2 + 1 == with\_component\_0 [\#1566](https://github.com/uclouvain/openjpeg/pull/1566) ([rouault](https://github.com/rouault)) +- opj\_j2k\_add\_tlmarker\(\): validate that current tile-part number if smaller that total number of tile-parts [\#1565](https://github.com/uclouvain/openjpeg/pull/1565) ([rouault](https://github.com/rouault)) +- Amend fix of PR 1530 regarding m\_sot\_length check [\#1561](https://github.com/uclouvain/openjpeg/pull/1561) ([rouault](https://github.com/rouault)) +- Do not turn on 'TPsot==TNsot detection fix' when TNsot==1, and [\#1560](https://github.com/uclouvain/openjpeg/pull/1560) ([rouault](https://github.com/rouault)) +- opj\_j2k\_setup\_encoder\(\): set numgbits = 1 for Cinema2K [\#1559](https://github.com/uclouvain/openjpeg/pull/1559) ([rouault](https://github.com/rouault)) +- bench\_dwt: Add assert for memory allocation failure [\#1555](https://github.com/uclouvain/openjpeg/pull/1555) ([hleft](https://github.com/hleft)) +- Add AVX2 and AVX512 optimization [\#1552](https://github.com/uclouvain/openjpeg/pull/1552) ([tszumski](https://github.com/tszumski)) +- Updated softprops/action-gh-release to v2 [\#1551](https://github.com/uclouvain/openjpeg/pull/1551) ([radarhere](https://github.com/radarhere)) +- Updated softprops/action-gh-release to v2 [\#1549](https://github.com/uclouvain/openjpeg/pull/1549) ([radarhere](https://github.com/radarhere)) +- fix: abi check [\#1548](https://github.com/uclouvain/openjpeg/pull/1548) ([mayeut](https://github.com/mayeut)) +- fix: when EPH markers are specified, they are required. [\#1547](https://github.com/uclouvain/openjpeg/pull/1547) ([mayeut](https://github.com/mayeut)) +- CI: add macOS arm64 [\#1546](https://github.com/uclouvain/openjpeg/pull/1546) ([mayeut](https://github.com/mayeut)) +- thirdparty/libz: update to zlib-1.3.1 [\#1542](https://github.com/uclouvain/openjpeg/pull/1542) ([rouault](https://github.com/rouault)) +- thirdparty/libpng: update to libpng-1.6.43 [\#1541](https://github.com/uclouvain/openjpeg/pull/1541) ([rouault](https://github.com/rouault)) +- thirdparty/libtiff: update to libtiff 4.6.0 [\#1540](https://github.com/uclouvain/openjpeg/pull/1540) ([rouault](https://github.com/rouault)) +- Use TLM \(Tile Length Marker\) segments to optimize decoding [\#1538](https://github.com/uclouvain/openjpeg/pull/1538) ([rouault](https://github.com/rouault)) +- Add new test for file with non-consecutive tilepart and TLM marker [\#1537](https://github.com/uclouvain/openjpeg/pull/1537) ([rouault](https://github.com/rouault)) +- Avoid heap-buffer-overflow read on corrupted image in non-strict mode [\#1536](https://github.com/uclouvain/openjpeg/pull/1536) ([rouault](https://github.com/rouault)) +- opj\_j2k\_read\_sod\(\): validate opj\_stream\_read\_data\(\) return to avoid … [\#1534](https://github.com/uclouvain/openjpeg/pull/1534) ([rouault](https://github.com/rouault)) +- Fixed typos [\#1532](https://github.com/uclouvain/openjpeg/pull/1532) ([radarhere](https://github.com/radarhere)) +- CI: pin macos job to macos-13 to get x86\_64 [\#1531](https://github.com/uclouvain/openjpeg/pull/1531) ([rouault](https://github.com/rouault)) +- Integer Overflow at j2k.c:9614 [\#1530](https://github.com/uclouvain/openjpeg/pull/1530) ([headshog](https://github.com/headshog)) +- Support setting enumcs for CMYK and EYCC color space [\#1529](https://github.com/uclouvain/openjpeg/pull/1529) ([radarhere](https://github.com/radarhere)) +- opj\_j2k\_decode\_tiles\(\): avoid use of uninitialized l\_current\_tile\_no variable [\#1528](https://github.com/uclouvain/openjpeg/pull/1528) ([rouault](https://github.com/rouault)) +- Updated actions/upload-artifact to v4 [\#1527](https://github.com/uclouvain/openjpeg/pull/1527) ([radarhere](https://github.com/radarhere)) +- Do not allow header length to be zero in non-zero length packet [\#1526](https://github.com/uclouvain/openjpeg/pull/1526) ([radarhere](https://github.com/radarhere)) +- Fix building on OpenBSD big endian hosts [\#1520](https://github.com/uclouvain/openjpeg/pull/1520) ([brad0](https://github.com/brad0)) + +## [v2.5.2](https://github.com/uclouvain/openjpeg/releases/v2.5.2) (2024-02-28) + +[Full Changelog](https://github.com/uclouvain/openjpeg/compare/v2.5.1...v2.5.2) + +**Closed issues:** + +- API breakage in 2.5.1 / openjpeg version no longer detected \(openjpeg.h no longer includes opj\_config.h\) [\#1514](https://github.com/uclouvain/openjpeg/issues/1514) + +**Merged pull requests:** + +- openjpeg.h: make sure to include opj\_config.h \(fixes \#1514\) [\#1515](https://github.com/uclouvain/openjpeg/pull/1515) ([rouault](https://github.com/rouault)) + +## [v2.5.1](https://github.com/uclouvain/openjpeg/releases/v2.5.1) (2024-02-26) + +[Full Changelog](https://github.com/uclouvain/openjpeg/compare/v2.5.0...v2.5.1) + +**Closed issues:** + +- Exist a undefined-behavior issue in file src/lib/openjp2/dwt.c:2124 [\#1505](https://github.com/uclouvain/openjpeg/issues/1505) +- Potential double-free vulnerability in j2k.c [\#1498](https://github.com/uclouvain/openjpeg/issues/1498) +- opj\_compress -I / -mct 0 should conflict each others [\#1485](https://github.com/uclouvain/openjpeg/issues/1485) +- Exist a undefined-behavior issue in file src/lib/openjp2/tcd.c:2327 [\#1480](https://github.com/uclouvain/openjpeg/issues/1480) +- OOM in opj\_decompress [\#1476](https://github.com/uclouvain/openjpeg/issues/1476) +- v2.5.0 cannot be built successfully on aarch64 CentOS machine [\#1475](https://github.com/uclouvain/openjpeg/issues/1475) +- \[ Heap Overflow \] opj\_decompress [\#1473](https://github.com/uclouvain/openjpeg/issues/1473) +- Possible bug reading JP2 as grayscale when should be in color [\#1464](https://github.com/uclouvain/openjpeg/issues/1464) +- Crashes due to internal bad memory references when using reduce on a truncated file. [\#1459](https://github.com/uclouvain/openjpeg/issues/1459) +- No error.h \(non standard compliant\) [\#1453](https://github.com/uclouvain/openjpeg/issues/1453) +- JP2 File incorrectly decompressed to noise [\#1447](https://github.com/uclouvain/openjpeg/issues/1447) +- UB in tcd.c opj\_tcd\_dc\_level\_shift\_decode - pointer arithmetic on NULL pointer [\#1445](https://github.com/uclouvain/openjpeg/issues/1445) +- UB in ht\_dec.c opj\_t1\_ht\_decode\_cblk - memcpy invoked on NULL pointer [\#1444](https://github.com/uclouvain/openjpeg/issues/1444) +- Integer Overflow in `src/lib/openjp2/image.c` [\#1438](https://github.com/uclouvain/openjpeg/issues/1438) +- Integer-overflow · opj\_t1\_encode\_cblk [\#1432](https://github.com/uclouvain/openjpeg/issues/1432) +- OSX m1 v2.5.0 build fail [\#1430](https://github.com/uclouvain/openjpeg/issues/1430) +- Pixel value could be changed by 0-4 after compression and decompression [\#1429](https://github.com/uclouvain/openjpeg/issues/1429) +- Cannot determine library version at compile time [\#1428](https://github.com/uclouvain/openjpeg/issues/1428) +- ARM builds on Windows unsupported with Version 2.5.0 [\#1422](https://github.com/uclouvain/openjpeg/issues/1422) +- opj\_decompress heap overflow Denial of Service issue [\#1413](https://github.com/uclouvain/openjpeg/issues/1413) +- Color channel swapping for some JPEG2000 pictures [\#1382](https://github.com/uclouvain/openjpeg/issues/1382) +- Heap-buffer-overflow in color.c:379:42 in sycc420\_to\_rgb [\#1347](https://github.com/uclouvain/openjpeg/issues/1347) +- No colorspace information after opj\_read\_header [\#570](https://github.com/uclouvain/openjpeg/issues/570) + +**Merged pull requests:** + +- opj\_t2\_read\_packet\_header\(\): avoid unsigned integer overflow [\#1511](https://github.com/uclouvain/openjpeg/pull/1511) ([rouault](https://github.com/rouault)) +- opj\_dwt\_decode\_tile\(\): avoid potential UndefinedBehaviorSanitizer 'applying zero offset to null pointer' \(fixes \#1505\) [\#1510](https://github.com/uclouvain/openjpeg/pull/1510) ([rouault](https://github.com/rouault)) +- opj\_decompress: fix off-by-one read heap-buffer-overflow in sycc420\_to\_rgb\(\) when x0 and y0 are odd \(CVE-2021-3575, fixes \#1347\) [\#1509](https://github.com/uclouvain/openjpeg/pull/1509) ([rouault](https://github.com/rouault)) +- Always install pkgconfig files [\#1507](https://github.com/uclouvain/openjpeg/pull/1507) ([kmilos](https://github.com/kmilos)) +- CMake: drop support for cmake \< 3.5 [\#1503](https://github.com/uclouvain/openjpeg/pull/1503) ([domin144](https://github.com/domin144)) +- Fix compiler error on Windows [\#1502](https://github.com/uclouvain/openjpeg/pull/1502) ([scaramallion](https://github.com/scaramallion)) +- opj\_tcd\_dc\_level\_shift\_decode\(\): avoid increment nullptr \(fixes \#1480\) [\#1496](https://github.com/uclouvain/openjpeg/pull/1496) ([rouault](https://github.com/rouault)) +- Fix CI [\#1495](https://github.com/uclouvain/openjpeg/pull/1495) ([rouault](https://github.com/rouault)) +- suppress warning during build using clang [\#1494](https://github.com/uclouvain/openjpeg/pull/1494) ([tomoaki0705](https://github.com/tomoaki0705)) +- Add cmake version file [\#1493](https://github.com/uclouvain/openjpeg/pull/1493) ([domin144](https://github.com/domin144)) +- fix ht\_dec.c:1215 [\#1492](https://github.com/uclouvain/openjpeg/pull/1492) ([headshog](https://github.com/headshog)) +- Integer Overflow at j2k.c:11114 [\#1491](https://github.com/uclouvain/openjpeg/pull/1491) ([headshog](https://github.com/headshog)) +- Integer Overflow at j2k.c:3962 [\#1490](https://github.com/uclouvain/openjpeg/pull/1490) ([headshog](https://github.com/headshog)) +- Fixed typos [\#1486](https://github.com/uclouvain/openjpeg/pull/1486) ([radarhere](https://github.com/radarhere)) +- Require `stdint.h` & `inttypes.h` [\#1484](https://github.com/uclouvain/openjpeg/pull/1484) ([mayeut](https://github.com/mayeut)) +- fix: use `opj_uint_ceildiv` instead of `opj_int_ceildiv` when necessary [\#1482](https://github.com/uclouvain/openjpeg/pull/1482) ([mayeut](https://github.com/mayeut)) +- ht\_dec.c: Improve MSVC arm64 popcount performance [\#1479](https://github.com/uclouvain/openjpeg/pull/1479) ([PeterJohnson](https://github.com/PeterJohnson)) +- opj\_jp2\_read\_header\(\): move setting color\_space here instead in opj\_jp2\_decode\(\)/get\_tile\(\) \(fixes \#570\) [\#1463](https://github.com/uclouvain/openjpeg/pull/1463) ([rouault](https://github.com/rouault)) +- CMake: error out on warnings for strict/missing prototypes. [\#1462](https://github.com/uclouvain/openjpeg/pull/1462) ([sebras](https://github.com/sebras)) +- Fix CI [\#1461](https://github.com/uclouvain/openjpeg/pull/1461) ([rouault](https://github.com/rouault)) +- opj\_t2\_skip\_packet\_data\(\): avoid out-of-bounds reads on truncated images in non-strict mode \(fixes \#1459\) [\#1460](https://github.com/uclouvain/openjpeg/pull/1460) ([rouault](https://github.com/rouault)) +- Fix \#1424 [\#1456](https://github.com/uclouvain/openjpeg/pull/1456) ([autoantwort](https://github.com/autoantwort)) +- openjp2/j2k: replace sprintf calls with snprintf [\#1450](https://github.com/uclouvain/openjpeg/pull/1450) ([markmentovai](https://github.com/markmentovai)) +- Fix incorrect decoding of image with large number of progression levels [\#1448](https://github.com/uclouvain/openjpeg/pull/1448) ([rouault](https://github.com/rouault)) +- Fix Heap-buffer-overflow READ in opj\_jp2\_apply\_pclr [\#1441](https://github.com/uclouvain/openjpeg/pull/1441) ([sashashura](https://github.com/sashashura)) +- Significant speed-up rate allocation by rate/distoratio ratio [\#1440](https://github.com/uclouvain/openjpeg/pull/1440) ([rouault](https://github.com/rouault)) +- Make OpenJPEGConfig.cmake relocatable with CMake \> 3.0 [\#1439](https://github.com/uclouvain/openjpeg/pull/1439) ([arichardson](https://github.com/arichardson)) +- Replace the assert in mel\_init to an if statement to address an issue with fuzzing. [\#1436](https://github.com/uclouvain/openjpeg/pull/1436) ([aous72](https://github.com/aous72)) +- opj\_t1\_encode\_cblk\(\): avoid undefined behaviour on fuzzed input \(fixes \#1432\) [\#1433](https://github.com/uclouvain/openjpeg/pull/1433) ([rouault](https://github.com/rouault)) +- Build: fix linking of executables on some systems where TIFF/LCMS2 static libraries are not in system directories \(fixes \#1430\) [\#1431](https://github.com/uclouvain/openjpeg/pull/1431) ([rouault](https://github.com/rouault)) +- Fix opj\_t1\_allocate\_buffers malloc size error [\#1426](https://github.com/uclouvain/openjpeg/pull/1426) ([zodf0055980](https://github.com/zodf0055980)) +- Switch to GNUInstallDirs \[v2\] [\#1424](https://github.com/uclouvain/openjpeg/pull/1424) ([laumann](https://github.com/laumann)) +- Fix windows arm builds [\#1423](https://github.com/uclouvain/openjpeg/pull/1423) ([Neumann-A](https://github.com/Neumann-A)) +- pkgconfig: Define OPJ\_STATIC for static linking with pkgconf [\#1421](https://github.com/uclouvain/openjpeg/pull/1421) ([Biswa96](https://github.com/Biswa96)) + + ## [v2.5.0](https://github.com/uclouvain/openjpeg/releases/v2.5.0) (2022-05-13) [Full Changelog](https://github.com/uclouvain/openjpeg/compare/v2.4.0...v2.5.0) @@ -217,7 +349,7 @@ - LINUX install doesn't work when building shared libraries is disabled [\#1155](https://github.com/uclouvain/openjpeg/issues/1155) - OPENJPEG null ptr dereference in openjpeg-2.3.0/src/bin/jp2/convert.c:2243 [\#1152](https://github.com/uclouvain/openjpeg/issues/1152) - How to drop certain subbands/layers in DWT [\#1147](https://github.com/uclouvain/openjpeg/issues/1147) -- where is the MQ-Coder ouput stream in t2.c? [\#1146](https://github.com/uclouvain/openjpeg/issues/1146) +- where is the MQ-Coder output stream in t2.c? [\#1146](https://github.com/uclouvain/openjpeg/issues/1146) - OpenJPEG 2.3 \(and 2.2?\) multi component image fails to decode with KDU v7.10 [\#1132](https://github.com/uclouvain/openjpeg/issues/1132) - Missing checks for header\_info.height and header\_info.width in function pnmtoimage in src/bin/jpwl/convert.c, which can lead to heap buffer overflow [\#1126](https://github.com/uclouvain/openjpeg/issues/1126) - Assertion Failure in jp2.c [\#1125](https://github.com/uclouvain/openjpeg/issues/1125) diff --git a/3rdparty/openjpeg/CMakeLists.txt b/3rdparty/openjpeg/CMakeLists.txt index 188381f1e2..4e9e260e96 100644 --- a/3rdparty/openjpeg/CMakeLists.txt +++ b/3rdparty/openjpeg/CMakeLists.txt @@ -23,7 +23,7 @@ ocv_warnings_disable(CMAKE_C_FLAGS # OPENJPEG version number, useful for packaging and doxygen doc: set(OPENJPEG_VERSION_MAJOR 2) set(OPENJPEG_VERSION_MINOR 5) -set(OPENJPEG_VERSION_BUILD 0) +set(OPENJPEG_VERSION_BUILD 3) set(OPENJPEG_VERSION "${OPENJPEG_VERSION_MAJOR}.${OPENJPEG_VERSION_MINOR}.${OPENJPEG_VERSION_BUILD}") set(PACKAGE_VERSION diff --git a/3rdparty/openjpeg/openjp2/bench_dwt.c b/3rdparty/openjpeg/openjp2/bench_dwt.c index 4f2ea9fbd1..ee70c40548 100644 --- a/3rdparty/openjpeg/openjp2/bench_dwt.c +++ b/3rdparty/openjpeg/openjp2/bench_dwt.c @@ -64,6 +64,7 @@ void init_tilec(opj_tcd_tilecomp_t * l_tilec, nValues = (size_t)(l_tilec->x1 - l_tilec->x0) * (size_t)(l_tilec->y1 - l_tilec->y0); l_tilec->data = (OPJ_INT32*) opj_malloc(sizeof(OPJ_INT32) * nValues); + assert(l_tilec->data != NULL); for (i = 0; i < nValues; i++) { OPJ_INT32 val = getValue((OPJ_UINT32)i); if (irreversible) { diff --git a/3rdparty/openjpeg/openjp2/dwt.c b/3rdparty/openjpeg/openjp2/dwt.c index 6b18c5dd6e..11aae472de 100644 --- a/3rdparty/openjpeg/openjp2/dwt.c +++ b/3rdparty/openjpeg/openjp2/dwt.c @@ -52,7 +52,7 @@ #ifdef __SSSE3__ #include #endif -#ifdef __AVX2__ +#if (defined(__AVX2__) || defined(__AVX512F__)) #include #endif @@ -66,7 +66,10 @@ #define OPJ_WS(i) v->mem[(i)*2] #define OPJ_WD(i) v->mem[(1+(i)*2)] -#ifdef __AVX2__ +#if defined(__AVX512F__) +/** Number of int32 values in a AVX512 register */ +#define VREG_INT_COUNT 16 +#elif defined(__AVX2__) /** Number of int32 values in a AVX2 register */ #define VREG_INT_COUNT 8 #else @@ -331,6 +334,51 @@ static void opj_dwt_decode_1(const opj_dwt_t *v) #endif /* STANDARD_SLOW_VERSION */ +#if defined(__AVX512F__) +static int32_t loop_short_sse(int32_t len, const int32_t** lf_ptr, + const int32_t** hf_ptr, int32_t** out_ptr, + int32_t* prev_even) +{ + int32_t next_even; + __m128i odd, even_m1, unpack1, unpack2; + const int32_t batch = (len - 2) / 8; + const __m128i two = _mm_set1_epi32(2); + + for (int32_t i = 0; i < batch; i++) { + const __m128i lf_ = _mm_loadu_si128((__m128i*)(*lf_ptr + 1)); + const __m128i hf1_ = _mm_loadu_si128((__m128i*)(*hf_ptr)); + const __m128i hf2_ = _mm_loadu_si128((__m128i*)(*hf_ptr + 1)); + + __m128i even = _mm_add_epi32(hf1_, hf2_); + even = _mm_add_epi32(even, two); + even = _mm_srai_epi32(even, 2); + even = _mm_sub_epi32(lf_, even); + + next_even = _mm_extract_epi32(even, 3); + even_m1 = _mm_bslli_si128(even, 4); + even_m1 = _mm_insert_epi32(even_m1, *prev_even, 0); + + //out[0] + out[2] + odd = _mm_add_epi32(even_m1, even); + odd = _mm_srai_epi32(odd, 1); + odd = _mm_add_epi32(odd, hf1_); + + unpack1 = _mm_unpacklo_epi32(even_m1, odd); + unpack2 = _mm_unpackhi_epi32(even_m1, odd); + + _mm_storeu_si128((__m128i*)(*out_ptr + 0), unpack1); + _mm_storeu_si128((__m128i*)(*out_ptr + 4), unpack2); + + *prev_even = next_even; + + *out_ptr += 8; + *lf_ptr += 4; + *hf_ptr += 4; + } + return batch; +} +#endif + #if !defined(STANDARD_SLOW_VERSION) static void opj_idwt53_h_cas0(OPJ_INT32* tmp, const OPJ_INT32 sn, @@ -363,6 +411,145 @@ static void opj_idwt53_h_cas0(OPJ_INT32* tmp, if (!(len & 1)) { /* if len is even */ tmp[len - 1] = in_odd[(len - 1) / 2] + tmp[len - 2]; } +#else +#if defined(__AVX512F__) + OPJ_INT32* out_ptr = tmp; + int32_t prev_even = in_even[0] - ((in_odd[0] + 1) >> 1); + + const __m512i permutevar_mask = _mm512_setr_epi32( + 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e); + const __m512i store1_perm = _mm512_setr_epi64(0x00, 0x01, 0x08, 0x09, 0x02, + 0x03, 0x0a, 0x0b); + const __m512i store2_perm = _mm512_setr_epi64(0x04, 0x05, 0x0c, 0x0d, 0x06, + 0x07, 0x0e, 0x0f); + + const __m512i two = _mm512_set1_epi32(2); + + int32_t simd_batch_512 = (len - 2) / 32; + int32_t leftover; + + for (i = 0; i < simd_batch_512; i++) { + const __m512i lf_avx2 = _mm512_loadu_si512((__m512i*)(in_even + 1)); + const __m512i hf1_avx2 = _mm512_loadu_si512((__m512i*)(in_odd)); + const __m512i hf2_avx2 = _mm512_loadu_si512((__m512i*)(in_odd + 1)); + int32_t next_even; + __m512i duplicate, even_m1, odd, unpack1, unpack2, store1, store2; + + __m512i even = _mm512_add_epi32(hf1_avx2, hf2_avx2); + even = _mm512_add_epi32(even, two); + even = _mm512_srai_epi32(even, 2); + even = _mm512_sub_epi32(lf_avx2, even); + + next_even = _mm_extract_epi32(_mm512_extracti32x4_epi32(even, 3), 3); + + duplicate = _mm512_set1_epi32(prev_even); + even_m1 = _mm512_permutex2var_epi32(even, permutevar_mask, duplicate); + + //out[0] + out[2] + odd = _mm512_add_epi32(even_m1, even); + odd = _mm512_srai_epi32(odd, 1); + odd = _mm512_add_epi32(odd, hf1_avx2); + + unpack1 = _mm512_unpacklo_epi32(even_m1, odd); + unpack2 = _mm512_unpackhi_epi32(even_m1, odd); + + store1 = _mm512_permutex2var_epi64(unpack1, store1_perm, unpack2); + store2 = _mm512_permutex2var_epi64(unpack1, store2_perm, unpack2); + + _mm512_storeu_si512(out_ptr, store1); + _mm512_storeu_si512(out_ptr + 16, store2); + + prev_even = next_even; + + out_ptr += 32; + in_even += 16; + in_odd += 16; + } + + leftover = len - simd_batch_512 * 32; + if (leftover > 8) { + leftover -= 8 * loop_short_sse(leftover, &in_even, &in_odd, &out_ptr, + &prev_even); + } + out_ptr[0] = prev_even; + + for (j = 1; j < (leftover - 2); j += 2) { + out_ptr[2] = in_even[1] - ((in_odd[0] + (in_odd[1]) + 2) >> 2); + out_ptr[1] = in_odd[0] + ((out_ptr[0] + out_ptr[2]) >> 1); + in_even++; + in_odd++; + out_ptr += 2; + } + + if (len & 1) { + out_ptr[2] = in_even[1] - ((in_odd[0] + 1) >> 1); + out_ptr[1] = in_odd[0] + ((out_ptr[0] + out_ptr[2]) >> 1); + } else { //!(len & 1) + out_ptr[1] = in_odd[0] + out_ptr[0]; + } +#elif defined(__AVX2__) + OPJ_INT32* out_ptr = tmp; + int32_t prev_even = in_even[0] - ((in_odd[0] + 1) >> 1); + + const __m256i reg_permutevar_mask_move_right = _mm256_setr_epi32(0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06); + const __m256i two = _mm256_set1_epi32(2); + + int32_t simd_batch = (len - 2) / 16; + int32_t next_even; + __m256i even_m1, odd, unpack1_avx2, unpack2_avx2; + + for (i = 0; i < simd_batch; i++) { + const __m256i lf_avx2 = _mm256_loadu_si256((__m256i*)(in_even + 1)); + const __m256i hf1_avx2 = _mm256_loadu_si256((__m256i*)(in_odd)); + const __m256i hf2_avx2 = _mm256_loadu_si256((__m256i*)(in_odd + 1)); + + __m256i even = _mm256_add_epi32(hf1_avx2, hf2_avx2); + even = _mm256_add_epi32(even, two); + even = _mm256_srai_epi32(even, 2); + even = _mm256_sub_epi32(lf_avx2, even); + + next_even = _mm_extract_epi32(_mm256_extracti128_si256(even, 1), 3); + even_m1 = _mm256_permutevar8x32_epi32(even, reg_permutevar_mask_move_right); + even_m1 = _mm256_blend_epi32(even_m1, _mm256_set1_epi32(prev_even), (1 << 0)); + + //out[0] + out[2] + odd = _mm256_add_epi32(even_m1, even); + odd = _mm256_srai_epi32(odd, 1); + odd = _mm256_add_epi32(odd, hf1_avx2); + + unpack1_avx2 = _mm256_unpacklo_epi32(even_m1, odd); + unpack2_avx2 = _mm256_unpackhi_epi32(even_m1, odd); + + _mm_storeu_si128((__m128i*)(out_ptr + 0), _mm256_castsi256_si128(unpack1_avx2)); + _mm_storeu_si128((__m128i*)(out_ptr + 4), _mm256_castsi256_si128(unpack2_avx2)); + _mm_storeu_si128((__m128i*)(out_ptr + 8), _mm256_extracti128_si256(unpack1_avx2, + 0x1)); + _mm_storeu_si128((__m128i*)(out_ptr + 12), + _mm256_extracti128_si256(unpack2_avx2, 0x1)); + + prev_even = next_even; + + out_ptr += 16; + in_even += 8; + in_odd += 8; + } + out_ptr[0] = prev_even; + for (j = simd_batch * 16 + 1; j < (len - 2); j += 2) { + out_ptr[2] = in_even[1] - ((in_odd[0] + in_odd[1] + 2) >> 2); + out_ptr[1] = in_odd[0] + ((out_ptr[0] + out_ptr[2]) >> 1); + in_even++; + in_odd++; + out_ptr += 2; + } + + if (len & 1) { + out_ptr[2] = in_even[1] - ((in_odd[0] + 1) >> 1); + out_ptr[1] = in_odd[0] + ((out_ptr[0] + out_ptr[2]) >> 1); + } else { //!(len & 1) + out_ptr[1] = in_odd[0] + out_ptr[0]; + } #else OPJ_INT32 d1c, d1n, s1n, s0c, s0n; @@ -397,7 +584,8 @@ static void opj_idwt53_h_cas0(OPJ_INT32* tmp, } else { tmp[len - 1] = d1n + s0n; } -#endif +#endif /*(__AVX512F__ || __AVX2__)*/ +#endif /*TWO_PASS_VERSION*/ memcpy(tiledp, tmp, (OPJ_UINT32)len * sizeof(OPJ_INT32)); } @@ -511,10 +699,20 @@ static void opj_idwt53_h(const opj_dwt_t *dwt, #endif } -#if (defined(__SSE2__) || defined(__AVX2__)) && !defined(STANDARD_SLOW_VERSION) +#if (defined(__SSE2__) || defined(__AVX2__) || defined(__AVX512F__)) && !defined(STANDARD_SLOW_VERSION) /* Conveniency macros to improve the readability of the formulas */ -#if __AVX2__ +#if defined(__AVX512F__) +#define VREG __m512i +#define LOAD_CST(x) _mm512_set1_epi32(x) +#define LOAD(x) _mm512_loadu_si512((const VREG*)(x)) +#define LOADU(x) _mm512_loadu_si512((const VREG*)(x)) +#define STORE(x,y) _mm512_storeu_si512((VREG*)(x),(y)) +#define STOREU(x,y) _mm512_storeu_si512((VREG*)(x),(y)) +#define ADD(x,y) _mm512_add_epi32((x),(y)) +#define SUB(x,y) _mm512_sub_epi32((x),(y)) +#define SAR(x,y) _mm512_srai_epi32((x),(y)) +#elif defined(__AVX2__) #define VREG __m256i #define LOAD_CST(x) _mm256_set1_epi32(x) #define LOAD(x) _mm256_load_si256((const VREG*)(x)) @@ -576,7 +774,10 @@ static void opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2( const VREG two = LOAD_CST(2); assert(len > 1); -#if __AVX2__ +#if defined(__AVX512F__) + assert(PARALLEL_COLS_53 == 32); + assert(VREG_INT_COUNT == 16); +#elif defined(__AVX2__) assert(PARALLEL_COLS_53 == 16); assert(VREG_INT_COUNT == 8); #else @@ -584,10 +785,13 @@ static void opj_idwt53_v_cas0_mcols_SSE2_OR_AVX2( assert(VREG_INT_COUNT == 4); #endif +//For AVX512 code aligned load/store is set to it's unaligned equivalents +#if !defined(__AVX512F__) /* Note: loads of input even/odd values must be done in a unaligned */ /* fashion. But stores in tmp can be done with aligned store, since */ /* the temporary buffer is properly aligned */ assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0); +#endif s1n_0 = LOADU(in_even + 0); s1n_1 = LOADU(in_even + VREG_INT_COUNT); @@ -678,7 +882,10 @@ static void opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2( const OPJ_INT32* in_odd = &tiledp_col[0]; assert(len > 2); -#if __AVX2__ +#if defined(__AVX512F__) + assert(PARALLEL_COLS_53 == 32); + assert(VREG_INT_COUNT == 16); +#elif defined(__AVX2__) assert(PARALLEL_COLS_53 == 16); assert(VREG_INT_COUNT == 8); #else @@ -686,10 +893,13 @@ static void opj_idwt53_v_cas1_mcols_SSE2_OR_AVX2( assert(VREG_INT_COUNT == 4); #endif +//For AVX512 code aligned load/store is set to it's unaligned equivalents +#if !defined(__AVX512F__) /* Note: loads of input even/odd values must be done in a unaligned */ /* fashion. But stores in tmp can be done with aligned store, since */ /* the temporary buffer is properly aligned */ assert((OPJ_SIZE_T)tmp % (sizeof(OPJ_INT32) * VREG_INT_COUNT) == 0); +#endif s1_0 = LOADU(in_even + stride); /* in_odd[0] - ((in_even[0] + s1 + 2) >> 2); */ diff --git a/3rdparty/openjpeg/openjp2/ht_dec.c b/3rdparty/openjpeg/openjp2/ht_dec.c index a554b24a6a..2984f56098 100644 --- a/3rdparty/openjpeg/openjp2/ht_dec.c +++ b/3rdparty/openjpeg/openjp2/ht_dec.c @@ -901,7 +901,7 @@ typedef struct frwd_struct { * X controls this value. * * Unstuffing prevent sequences that are more than 0xFF7F from appearing - * in the conpressed sequence. So whenever a value of 0xFF is coded, the + * in the compressed sequence. So whenever a value of 0xFF is coded, the * MSB of the next byte is set 0 and must be ignored during decoding. * * Reading can go beyond the end of buffer by up to 3 bytes. @@ -1032,7 +1032,7 @@ OPJ_UINT32 frwd_fetch(frwd_struct_t *msp) //************************************************************************/ /** @brief Allocates T1 buffers * - * @param [in, out] t1 is codeblock cofficients storage + * @param [in, out] t1 is codeblock coefficients storage * @param [in] w is codeblock width * @param [in] h is codeblock height */ @@ -1120,7 +1120,7 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1, /** @brief Decodes one codeblock, processing the cleanup, siginificance * propagation, and magnitude refinement pass * - * @param [in, out] t1 is codeblock cofficients storage + * @param [in, out] t1 is codeblock coefficients storage * @param [in] cblk is codeblock properties * @param [in] orient is the subband to which the codeblock belongs (not needed) * @param [in] roishift is region of interest shift diff --git a/3rdparty/openjpeg/openjp2/j2k.c b/3rdparty/openjpeg/openjp2/j2k.c index c0551870b2..a2014c89b0 100644 --- a/3rdparty/openjpeg/openjp2/j2k.c +++ b/3rdparty/openjpeg/openjp2/j2k.c @@ -2484,6 +2484,11 @@ static OPJ_BOOL opj_j2k_read_siz(opj_j2k_t *p_j2k, ++l_current_tile_param; } + /*Allocate and initialize some elements of codestrem index*/ + if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) { + return OPJ_FALSE; + } + p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_MH; opj_image_comp_header_update(l_image, l_cp); @@ -3657,21 +3662,29 @@ static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k, opj_event_mgr_t * p_manager ) { - OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, l_tot_num_tp_remaining, l_quotient, - l_Ptlm_size; + OPJ_UINT32 l_Ztlm, l_Stlm, l_ST, l_SP, + l_Ptlm_size, l_entry_size, l_num_tileparts; + OPJ_UINT32 i; + opj_j2k_tlm_tile_part_info_t* l_tile_part_infos; + opj_j2k_tlm_info_t* l_tlm; + /* preconditions */ assert(p_header_data != 00); assert(p_j2k != 00); assert(p_manager != 00); - OPJ_UNUSED(p_j2k); + l_tlm = &(p_j2k->m_specific_param.m_decoder.m_tlm); if (p_header_size < 2) { - opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n"); + opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker.\n"); return OPJ_FALSE; } p_header_size -= 2; + if (l_tlm->m_is_invalid) { + return OPJ_TRUE; + } + opj_read_bytes(p_header_data, &l_Ztlm, 1); /* Ztlm */ ++p_header_data; @@ -3680,27 +3693,83 @@ static OPJ_BOOL opj_j2k_read_tlm(opj_j2k_t *p_j2k, ++p_header_data; l_ST = ((l_Stlm >> 4) & 0x3); + if (l_ST == 3) { + l_tlm->m_is_invalid = OPJ_TRUE; + opj_event_msg(p_manager, EVT_WARNING, + "opj_j2k_read_tlm(): ST = 3 is invalid.\n"); + return OPJ_TRUE; + } l_SP = (l_Stlm >> 6) & 0x1; l_Ptlm_size = (l_SP + 1) * 2; - l_quotient = l_Ptlm_size + l_ST; + l_entry_size = l_Ptlm_size + l_ST; - l_tot_num_tp_remaining = p_header_size % l_quotient; - - if (l_tot_num_tp_remaining != 0) { - opj_event_msg(p_manager, EVT_ERROR, "Error reading TLM marker\n"); - return OPJ_FALSE; + if ((p_header_size % l_entry_size) != 0) { + l_tlm->m_is_invalid = OPJ_TRUE; + opj_event_msg(p_manager, EVT_WARNING, + "opj_j2k_read_tlm(): TLM marker not of expected size.\n"); + return OPJ_TRUE; } - /* FIXME Do not care of this at the moment since only local variables are set here */ - /* - for - (i = 0; i < l_tot_num_tp; ++i) - { - opj_read_bytes(p_header_data,&l_Ttlm_i,l_ST); // Ttlm_i + + l_num_tileparts = p_header_size / l_entry_size; + if (l_num_tileparts == 0) { + /* not totally sure if this is valid... */ + return OPJ_TRUE; + } + + /* Highly unlikely, unless there are gazillions of TLM markers */ + if (l_tlm->m_entries_count > UINT32_MAX - l_num_tileparts || + l_tlm->m_entries_count + l_num_tileparts > UINT32_MAX / sizeof( + opj_j2k_tlm_tile_part_info_t)) { + l_tlm->m_is_invalid = OPJ_TRUE; + opj_event_msg(p_manager, EVT_WARNING, + "opj_j2k_read_tlm(): too many TLM markers.\n"); + return OPJ_TRUE; + } + + l_tile_part_infos = (opj_j2k_tlm_tile_part_info_t*)opj_realloc( + l_tlm->m_tile_part_infos, + (l_tlm->m_entries_count + l_num_tileparts) * sizeof( + opj_j2k_tlm_tile_part_info_t)); + if (!l_tile_part_infos) { + l_tlm->m_is_invalid = OPJ_TRUE; + opj_event_msg(p_manager, EVT_WARNING, + "opj_j2k_read_tlm(): cannot allocate m_tile_part_infos.\n"); + return OPJ_TRUE; + } + + l_tlm->m_tile_part_infos = l_tile_part_infos; + + for (i = 0; i < l_num_tileparts; ++ i) { + OPJ_UINT32 l_tile_index; + OPJ_UINT32 l_length; + + /* Read Ttlm_i */ + if (l_ST == 0) { + l_tile_index = l_tlm->m_entries_count; + } else { + opj_read_bytes(p_header_data, &l_tile_index, l_ST); p_header_data += l_ST; - opj_read_bytes(p_header_data,&l_Ptlm_i,l_Ptlm_size); // Ptlm_i - p_header_data += l_Ptlm_size; - }*/ + } + + if (l_tile_index >= p_j2k->m_cp.tw * p_j2k->m_cp.th) { + l_tlm->m_is_invalid = OPJ_TRUE; + opj_event_msg(p_manager, EVT_WARNING, + "opj_j2k_read_tlm(): invalid tile number %d\n", + l_tile_index); + return OPJ_TRUE; + } + + /* Read Ptlm_i */ + opj_read_bytes(p_header_data, &l_length, l_Ptlm_size); + p_header_data += l_Ptlm_size; + + l_tile_part_infos[l_tlm->m_entries_count].m_tile_index = + (OPJ_UINT16)l_tile_index; + l_tile_part_infos[l_tlm->m_entries_count].m_length = l_length; + ++l_tlm->m_entries_count; + } + return OPJ_TRUE; } @@ -4583,14 +4652,26 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k, } /* Index */ - if (p_j2k->cstr_index) { + { assert(p_j2k->cstr_index->tile_index != 00); p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].tileno = p_j2k->m_current_tile_number; p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_tpsno = l_current_part; - if (l_num_parts != 0) { + if (!p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid && + l_num_parts > + p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps) { + opj_event_msg(p_manager, EVT_WARNING, + "SOT marker for tile %u declares more tile-parts than found in TLM marker.", + p_j2k->m_current_tile_number); + p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid = OPJ_TRUE; + } + + if (!p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid) { + /* do nothing */ + } else if (l_num_parts != 0) { + p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps = l_num_parts; p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].current_nb_tps = @@ -4661,33 +4742,6 @@ static OPJ_BOOL opj_j2k_read_sot(opj_j2k_t *p_j2k, } - /* FIXME move this onto a separate method to call before reading any SOT, remove part about main_end header, use a index struct inside p_j2k */ - /* if (p_j2k->cstr_info) { - if (l_tcp->first) { - if (tileno == 0) { - p_j2k->cstr_info->main_head_end = p_stream_tell(p_stream) - 13; - } - - p_j2k->cstr_info->tile[tileno].tileno = tileno; - p_j2k->cstr_info->tile[tileno].start_pos = p_stream_tell(p_stream) - 12; - p_j2k->cstr_info->tile[tileno].end_pos = p_j2k->cstr_info->tile[tileno].start_pos + totlen - 1; - p_j2k->cstr_info->tile[tileno].num_tps = numparts; - - if (numparts) { - p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(numparts * sizeof(opj_tp_info_t)); - } - else { - p_j2k->cstr_info->tile[tileno].tp = (opj_tp_info_t *) opj_malloc(10 * sizeof(opj_tp_info_t)); // Fixme (10) - } - } - else { - p_j2k->cstr_info->tile[tileno].end_pos += totlen; - } - - p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos = p_stream_tell(p_stream) - 12; - p_j2k->cstr_info->tile[tileno].tp[partno].tp_end_pos = - p_j2k->cstr_info->tile[tileno].tp[partno].tp_start_pos + totlen - 1; - }*/ return OPJ_TRUE; } @@ -5023,7 +5077,7 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k, /* Index */ l_cstr_index = p_j2k->cstr_index; - if (l_cstr_index) { + { OPJ_OFF_T l_current_pos = opj_stream_tell(p_stream) - 2; OPJ_UINT32 l_current_tile_part = @@ -5059,6 +5113,11 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k, } if (l_current_read_size != p_j2k->m_specific_param.m_decoder.m_sot_length) { + if (l_current_read_size == (OPJ_SIZE_T)(-1)) { + /* Avoid issue of https://github.com/uclouvain/openjpeg/issues/1533 */ + opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n"); + return OPJ_FALSE; + } p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_NEOC; } else { p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT; @@ -6705,6 +6764,9 @@ void opj_j2k_decoder_set_strict_mode(opj_j2k_t *j2k, OPJ_BOOL strict) { if (j2k) { j2k->m_cp.strict = strict; + if (strict) { + j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1; + } } } @@ -8251,7 +8313,14 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, tccp->qmfbid = parameters->irreversible ? 0 : 1; tccp->qntsty = parameters->irreversible ? J2K_CCP_QNTSTY_SEQNT : J2K_CCP_QNTSTY_NOQNT; - tccp->numgbits = 2; + + if (OPJ_IS_CINEMA(parameters->rsiz) && + parameters->rsiz == OPJ_PROFILE_CINEMA_2K) { + /* From https://github.com/uclouvain/openjpeg/issues/1340 */ + tccp->numgbits = 1; + } else { + tccp->numgbits = 2; + } if ((OPJ_INT32)i == parameters->roi_compno) { tccp->roishift = parameters->roi_shift; @@ -8390,7 +8459,8 @@ static OPJ_BOOL opj_j2k_add_tlmarker(OPJ_UINT32 tileno, if (type == J2K_MS_SOT) { OPJ_UINT32 l_current_tile_part = cstr_index->tile_index[tileno].current_tpsno; - if (cstr_index->tile_index[tileno].tp_index) { + if (cstr_index->tile_index[tileno].tp_index && + l_current_tile_part < cstr_index->tile_index[tileno].nb_tps) { cstr_index->tile_index[tileno].tp_index[l_current_tile_part].start_pos = pos; } @@ -8467,13 +8537,6 @@ OPJ_BOOL opj_j2k_read_header(opj_stream_private_t *p_stream, /* Copy codestream image information to the output image */ opj_copy_image_header(p_j2k->m_private_image, *p_image); - /*Allocate and initialize some elements of codestrem index*/ - if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) { - opj_image_destroy(*p_image); - *p_image = NULL; - return OPJ_FALSE; - } - return OPJ_TRUE; } @@ -8825,6 +8888,87 @@ static OPJ_BOOL opj_j2k_decoding_validation(opj_j2k_t *p_j2k, return l_is_valid; } +/** Fill p_j2k->cstr_index->tp_index[].start_pos/end_pos fields from TLM marker segments */ +static void opj_j2k_build_tp_index_from_tlm(opj_j2k_t* p_j2k, + opj_event_mgr_t * p_manager) +{ + opj_j2k_tlm_info_t* l_tlm; + OPJ_UINT32 i; + OPJ_OFF_T l_cur_offset; + + assert(p_j2k->cstr_index->main_head_end > 0); + assert(p_j2k->cstr_index->nb_of_tiles > 0); + assert(p_j2k->cstr_index->tile_index != NULL); + + l_tlm = &(p_j2k->m_specific_param.m_decoder.m_tlm); + + if (l_tlm->m_entries_count == 0) { + l_tlm->m_is_invalid = OPJ_TRUE; + return; + } + + if (l_tlm->m_is_invalid) { + return; + } + + /* Initial pass to count the number of tile-parts per tile */ + for (i = 0; i < l_tlm->m_entries_count; ++i) { + OPJ_UINT32 l_tile_index_no = l_tlm->m_tile_part_infos[i].m_tile_index; + assert(l_tile_index_no < p_j2k->cstr_index->nb_of_tiles); + p_j2k->cstr_index->tile_index[l_tile_index_no].tileno = l_tile_index_no; + ++p_j2k->cstr_index->tile_index[l_tile_index_no].current_nb_tps; + } + + /* Now check that all tiles have at least one tile-part */ + for (i = 0; i < p_j2k->cstr_index->nb_of_tiles; ++i) { + if (p_j2k->cstr_index->tile_index[i].current_nb_tps == 0) { + opj_event_msg(p_manager, EVT_ERROR, + "opj_j2k_build_tp_index_from_tlm(): tile %d has no " + "registered tile-part in TLM marker segments.\n", i); + goto error; + } + } + + /* Final pass to fill p_j2k->cstr_index */ + l_cur_offset = p_j2k->cstr_index->main_head_end; + for (i = 0; i < l_tlm->m_entries_count; ++i) { + OPJ_UINT32 l_tile_index_no = l_tlm->m_tile_part_infos[i].m_tile_index; + opj_tile_index_t* l_tile_index = & + (p_j2k->cstr_index->tile_index[l_tile_index_no]); + if (!l_tile_index->tp_index) { + l_tile_index->tp_index = (opj_tp_index_t *) opj_calloc( + l_tile_index->current_nb_tps, sizeof(opj_tp_index_t)); + if (! l_tile_index->tp_index) { + opj_event_msg(p_manager, EVT_ERROR, + "opj_j2k_build_tp_index_from_tlm(): tile index allocation failed\n"); + goto error; + } + } + + assert(l_tile_index->nb_tps < l_tile_index->current_nb_tps); + l_tile_index->tp_index[l_tile_index->nb_tps].start_pos = l_cur_offset; + /* We don't know how to set the tp_index[].end_header field, but this is not really needed */ + /* If there would be no markers between SOT and SOD, that would be : */ + /* l_tile_index->tp_index[l_tile_index->nb_tps].end_header = l_cur_offset + 12; */ + l_tile_index->tp_index[l_tile_index->nb_tps].end_pos = l_cur_offset + + l_tlm->m_tile_part_infos[i].m_length; + ++l_tile_index->nb_tps; + + l_cur_offset += l_tlm->m_tile_part_infos[i].m_length; + } + + return; + +error: + l_tlm->m_is_invalid = OPJ_TRUE; + for (i = 0; i < l_tlm->m_entries_count; ++i) { + OPJ_UINT32 l_tile_index = l_tlm->m_tile_part_infos[i].m_tile_index; + p_j2k->cstr_index->tile_index[l_tile_index].current_nb_tps = 0; + opj_free(p_j2k->cstr_index->tile_index[l_tile_index].tp_index); + p_j2k->cstr_index->tile_index[l_tile_index].tp_index = NULL; + } +} + static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k, opj_stream_private_t *p_stream, opj_event_mgr_t * p_manager) @@ -9004,6 +9148,9 @@ static OPJ_BOOL opj_j2k_read_header_procedure(opj_j2k_t *p_j2k, /* Position of the last element if the main header */ p_j2k->cstr_index->main_head_end = (OPJ_UINT32) opj_stream_tell(p_stream) - 2; + /* Build tile-part index from TLM information */ + opj_j2k_build_tp_index_from_tlm(p_j2k, p_manager); + /* Next step: read a tile-part header */ p_j2k->m_specific_param.m_decoder.m_state = J2K_STATE_TPHSOT; @@ -9227,6 +9374,12 @@ void opj_j2k_destroy(opj_j2k_t *p_j2k) p_j2k->m_specific_param.m_decoder.m_comps_indices_to_decode = 00; p_j2k->m_specific_param.m_decoder.m_numcomps_to_decode = 0; + opj_free(p_j2k->m_specific_param.m_decoder.m_tlm.m_tile_part_infos); + p_j2k->m_specific_param.m_decoder.m_tlm.m_tile_part_infos = NULL; + + opj_free(p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset); + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset = NULL; + } else { if (p_j2k->m_specific_param.m_encoder.m_encoded_tile_data) { @@ -9577,6 +9730,39 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k, while ((!p_j2k->m_specific_param.m_decoder.m_can_decode) && (l_current_marker != J2K_MS_EOC)) { + if (p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts > 0 && + p_j2k->m_specific_param.m_decoder.m_idx_intersecting_tile_parts < + p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts) { + OPJ_OFF_T next_tp_sot_pos; + + next_tp_sot_pos = + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset[p_j2k->m_specific_param.m_decoder.m_idx_intersecting_tile_parts]; + ++p_j2k->m_specific_param.m_decoder.m_idx_intersecting_tile_parts; + if (!(opj_stream_read_seek(p_stream, + next_tp_sot_pos, + p_manager))) { + opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n"); + return OPJ_FALSE; + } + + /* Try to read 2 bytes (the marker ID) from stream and copy them into the buffer */ + if (opj_stream_read_data(p_stream, + p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) { + opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n"); + return OPJ_FALSE; + } + + /* Read 2 bytes from the buffer as the marker ID */ + opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, + &l_current_marker, + 2); + + if (l_current_marker != J2K_MS_SOT) { + opj_event_msg(p_manager, EVT_ERROR, "Did not get expected SOT marker\n"); + return OPJ_FALSE; + } + } + /* Try to read until the Start Of Data is detected */ while (l_current_marker != J2K_MS_SOD) { @@ -9610,7 +9796,13 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k, } /* Why this condition? FIXME */ - if (p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_TPH) { + if ((p_j2k->m_specific_param.m_decoder.m_state & J2K_STATE_TPH) && + p_j2k->m_specific_param.m_decoder.m_sot_length != 0) { + if (p_j2k->m_specific_param.m_decoder.m_sot_length < l_marker_size + 2) { + opj_event_msg(p_manager, EVT_ERROR, + "Sot length is less than marker size + marker ID\n"); + return OPJ_FALSE; + } p_j2k->m_specific_param.m_decoder.m_sot_length -= (l_marker_size + 2); } l_marker_size -= 2; /* Subtract the size of the marker ID already read */ @@ -9720,14 +9912,78 @@ OPJ_BOOL opj_j2k_read_tile_header(opj_j2k_t * p_j2k, if (! opj_j2k_read_sod(p_j2k, p_stream, p_manager)) { return OPJ_FALSE; } + + /* Check if we can use the TLM index to access the next tile-part */ + if (!p_j2k->m_specific_param.m_decoder.m_can_decode && + p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec >= 0 && + p_j2k->m_current_tile_number == (OPJ_UINT32) + p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec && + !p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid && + opj_stream_has_seek(p_stream)) { + l_tcp = p_j2k->m_cp.tcps + p_j2k->m_current_tile_number; + if (l_tcp->m_nb_tile_parts == + p_j2k->cstr_index->tile_index[p_j2k->m_current_tile_number].nb_tps && + (OPJ_UINT32)l_tcp->m_current_tile_part_number + 1 < l_tcp->m_nb_tile_parts) { + const OPJ_OFF_T next_tp_sot_pos = p_j2k->cstr_index->tile_index[ + p_j2k->m_current_tile_number].tp_index[l_tcp->m_current_tile_part_number + + 1].start_pos; + + if (next_tp_sot_pos != opj_stream_tell(p_stream)) { +#if 0 + opj_event_msg(p_manager, EVT_INFO, + "opj_j2k_read_tile_header(tile=%u): seek to tile part %u at %" PRId64 "\n", + p_j2k->m_current_tile_number, + l_tcp->m_current_tile_part_number + 1, + next_tp_sot_pos); +#endif + + if (!(opj_stream_read_seek(p_stream, + next_tp_sot_pos, + p_manager))) { + opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n"); + return OPJ_FALSE; + } + } + + /* Try to read 2 bytes (the marker ID) from stream and copy them into the buffer */ + if (opj_stream_read_data(p_stream, + p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) { + opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n"); + return OPJ_FALSE; + } + + /* Read 2 bytes from the buffer as the marker ID */ + opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, + &l_current_marker, + 2); + + if (l_current_marker != J2K_MS_SOT) { + opj_event_msg(p_manager, EVT_ERROR, "Did not get expected SOT marker\n"); + return OPJ_FALSE; + } + + continue; + } + } + if (p_j2k->m_specific_param.m_decoder.m_can_decode && !p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked) { /* Issue 254 */ - OPJ_BOOL l_correction_needed; + OPJ_BOOL l_correction_needed = OPJ_FALSE; p_j2k->m_specific_param.m_decoder.m_nb_tile_parts_correction_checked = 1; - if (!opj_j2k_need_nb_tile_parts_correction(p_stream, - p_j2k->m_current_tile_number, &l_correction_needed, p_manager)) { + if (p_j2k->m_cp.tcps[p_j2k->m_current_tile_number].m_nb_tile_parts == 1) { + /* Skip opj_j2k_need_nb_tile_parts_correction() if there is + * only a single tile part declared. The + * opj_j2k_need_nb_tile_parts_correction() hack was needed + * for files with 5 declared tileparts (where they were + * actually 6). + * Doing it systematically hurts performance when reading + * Sentinel2 L1C JPEG2000 files as explained in + * https://lists.osgeo.org/pipermail/gdal-dev/2024-November/059805.html + */ + } else if (!opj_j2k_need_nb_tile_parts_correction(p_stream, + p_j2k->m_current_tile_number, &l_correction_needed, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, "opj_j2k_apply_nb_tile_parts_correction error\n"); return OPJ_FALSE; @@ -11303,6 +11559,17 @@ static void opj_j2k_dump_MH_index(opj_j2k_t* p_j2k, FILE* out_stream) OPJ_UINT32 l_acc_nb_of_tile_part = 0; for (it_tile = 0; it_tile < cstr_index->nb_of_tiles ; it_tile++) { l_acc_nb_of_tile_part += cstr_index->tile_index[it_tile].nb_tps; + + /* To avoid regenerating expected opj_dump results from the test */ + /* suite when there is a TLM marker present */ + if (cstr_index->tile_index[it_tile].nb_tps && + cstr_index->tile_index[it_tile].tp_index && + cstr_index->tile_index[it_tile].tp_index[0].start_pos > 0 && + cstr_index->tile_index[it_tile].tp_index[0].end_header == 0 && + getenv("OJP_DO_NOT_DISPLAY_TILE_INDEX_IF_TLM") != NULL) { + l_acc_nb_of_tile_part = 0; + break; + } } if (l_acc_nb_of_tile_part) { @@ -11666,6 +11933,18 @@ static OPJ_BOOL opj_j2k_are_all_used_components_decoded(opj_j2k_t *p_j2k, return OPJ_TRUE; } +static int CompareOffT(const void* a, const void* b) +{ + const OPJ_OFF_T offA = *(const OPJ_OFF_T*)a; + const OPJ_OFF_T offB = *(const OPJ_OFF_T*)b; + if (offA < offB) { + return -1; + } + if (offA == offB) { + return 0; + } + return 1; +} static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k, opj_stream_private_t *p_stream, @@ -11676,6 +11955,7 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k, OPJ_INT32 l_tile_x0, l_tile_y0, l_tile_x1, l_tile_y1; OPJ_UINT32 l_nb_comps; OPJ_UINT32 nr_tiles = 0; + OPJ_OFF_T end_pos = 0; /* Particular case for whole single tile decoding */ /* We can avoid allocating intermediate tile buffers */ @@ -11698,8 +11978,9 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k, return OPJ_FALSE; } - if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0, - p_stream, p_manager)) { + if (!l_go_on || + ! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0, + p_stream, p_manager)) { opj_event_msg(p_manager, EVT_ERROR, "Failed to decode tile 1/1\n"); return OPJ_FALSE; } @@ -11717,6 +11998,77 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k, return OPJ_TRUE; } + p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts = 0; + p_j2k->m_specific_param.m_decoder.m_idx_intersecting_tile_parts = 0; + opj_free(p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset); + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset = NULL; + + /* If the area to decode only intersects a subset of tiles, and we have + * valid TLM information, then use it to plan the tilepart offsets to + * seek to. + */ + if (!(p_j2k->m_specific_param.m_decoder.m_start_tile_x == 0 && + p_j2k->m_specific_param.m_decoder.m_start_tile_y == 0 && + p_j2k->m_specific_param.m_decoder.m_end_tile_x == p_j2k->m_cp.tw && + p_j2k->m_specific_param.m_decoder.m_end_tile_y == p_j2k->m_cp.th) && + !p_j2k->m_specific_param.m_decoder.m_tlm.m_is_invalid && + opj_stream_has_seek(p_stream)) { + OPJ_UINT32 m_num_intersecting_tile_parts = 0; + + OPJ_UINT32 j; + for (j = 0; j < p_j2k->m_cp.tw * p_j2k->m_cp.th; ++j) { + if (p_j2k->cstr_index->tile_index[j].nb_tps > 0 && + p_j2k->cstr_index->tile_index[j].tp_index[ + p_j2k->cstr_index->tile_index[j].nb_tps - 1].end_pos > end_pos) { + end_pos = p_j2k->cstr_index->tile_index[j].tp_index[ + p_j2k->cstr_index->tile_index[j].nb_tps - 1].end_pos; + } + } + + for (j = p_j2k->m_specific_param.m_decoder.m_start_tile_y; + j < p_j2k->m_specific_param.m_decoder.m_end_tile_y; ++j) { + OPJ_UINT32 i; + for (i = p_j2k->m_specific_param.m_decoder.m_start_tile_x; + i < p_j2k->m_specific_param.m_decoder.m_end_tile_x; ++i) { + const OPJ_UINT32 tile_number = j * p_j2k->m_cp.tw + i; + m_num_intersecting_tile_parts += + p_j2k->cstr_index->tile_index[tile_number].nb_tps; + } + } + + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset = + (OPJ_OFF_T*) + opj_malloc(m_num_intersecting_tile_parts * sizeof(OPJ_OFF_T)); + if (m_num_intersecting_tile_parts > 0 && + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset) { + OPJ_UINT32 idx = 0; + for (j = p_j2k->m_specific_param.m_decoder.m_start_tile_y; + j < p_j2k->m_specific_param.m_decoder.m_end_tile_y; ++j) { + OPJ_UINT32 i; + for (i = p_j2k->m_specific_param.m_decoder.m_start_tile_x; + i < p_j2k->m_specific_param.m_decoder.m_end_tile_x; ++i) { + const OPJ_UINT32 tile_number = j * p_j2k->m_cp.tw + i; + OPJ_UINT32 k; + for (k = 0; k < p_j2k->cstr_index->tile_index[tile_number].nb_tps; ++k) { + const OPJ_OFF_T next_tp_sot_pos = + p_j2k->cstr_index->tile_index[tile_number].tp_index[k].start_pos; + p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset[idx] = + next_tp_sot_pos; + ++idx; + } + } + } + + p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts = idx; + + /* Sort by increasing offset */ + qsort(p_j2k->m_specific_param.m_decoder.m_intersecting_tile_parts_offset, + p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts, + sizeof(OPJ_OFF_T), + CompareOffT); + } + } + for (;;) { if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 && p_j2k->m_cp.tcps[0].m_data != NULL) { @@ -11776,6 +12128,12 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k, if (++nr_tiles == p_j2k->m_cp.th * p_j2k->m_cp.tw) { break; } + if (p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts > 0 && + p_j2k->m_specific_param.m_decoder.m_idx_intersecting_tile_parts == + p_j2k->m_specific_param.m_decoder.m_num_intersecting_tile_parts) { + opj_stream_seek(p_stream, end_pos + 2, p_manager); + break; + } } if (! opj_j2k_are_all_used_components_decoded(p_j2k, p_manager)) { @@ -11819,12 +12177,6 @@ static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k, OPJ_UINT32 l_nb_tiles; OPJ_UINT32 i; - /*Allocate and initialize some elements of codestrem index if not already done*/ - if (!p_j2k->cstr_index->tile_index) { - if (!opj_j2k_allocate_tile_element_cstr_index(p_j2k)) { - return OPJ_FALSE; - } - } /* Move into the codestream to the first SOT used to decode the desired tile */ l_tile_no_to_dec = (OPJ_UINT32) p_j2k->m_specific_param.m_decoder.m_tile_ind_to_dec; @@ -11839,12 +12191,38 @@ static OPJ_BOOL opj_j2k_decode_one_tile(opj_j2k_t *p_j2k, return OPJ_FALSE; } } else { + OPJ_OFF_T sot_pos = + p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos; + OPJ_UINT32 l_marker; + +#if 0 + opj_event_msg(p_manager, EVT_INFO, + "opj_j2k_decode_one_tile(%u): seek to %" PRId64 "\n", + l_tile_no_to_dec, + sot_pos); +#endif if (!(opj_stream_read_seek(p_stream, - p_j2k->cstr_index->tile_index[l_tile_no_to_dec].tp_index[0].start_pos + 2, + sot_pos, p_manager))) { opj_event_msg(p_manager, EVT_ERROR, "Problem with seek function\n"); return OPJ_FALSE; } + + /* Try to read 2 bytes (the marker ID) from stream and copy them into the buffer */ + if (opj_stream_read_data(p_stream, + p_j2k->m_specific_param.m_decoder.m_header_data, 2, p_manager) != 2) { + opj_event_msg(p_manager, EVT_ERROR, "Stream too short\n"); + return OPJ_FALSE; + } + + /* Read 2 bytes from the buffer as the marker ID */ + opj_read_bytes(p_j2k->m_specific_param.m_decoder.m_header_data, &l_marker, + 2); + + if (l_marker != J2K_MS_SOT) { + opj_event_msg(p_manager, EVT_ERROR, "Did not get expected SOT marker\n"); + return OPJ_FALSE; + } } /* Special case if we have previously read the EOC marker (if the previous tile getted is the last ) */ if (p_j2k->m_specific_param.m_decoder.m_state == J2K_STATE_EOC) { diff --git a/3rdparty/openjpeg/openjp2/j2k.h b/3rdparty/openjpeg/openjp2/j2k.h index e0b9688a35..bcf70a419c 100644 --- a/3rdparty/openjpeg/openjp2/j2k.h +++ b/3rdparty/openjpeg/openjp2/j2k.h @@ -466,6 +466,24 @@ typedef struct opj_cp { /* <enumcs = 0; } else { jp2->meth = 1; - if (image->color_space == 1) { + if (image->color_space == OPJ_CLRSPC_SRGB) { jp2->enumcs = 16; /* sRGB as defined by IEC 61966-2-1 */ - } else if (image->color_space == 2) { - jp2->enumcs = 17; /* greyscale */ - } else if (image->color_space == 3) { + } else if (image->color_space == OPJ_CLRSPC_GRAY) { + jp2->enumcs = 17; + } else if (image->color_space == OPJ_CLRSPC_SYCC) { jp2->enumcs = 18; /* YUV */ + } else if (image->color_space == OPJ_CLRSPC_EYCC) { + jp2->enumcs = 24; + } else if (image->color_space == OPJ_CLRSPC_CMYK) { + jp2->enumcs = 12; } } diff --git a/3rdparty/openjpeg/openjp2/openjpeg.h b/3rdparty/openjpeg/openjp2/openjpeg.h index 67d168bb57..59abd323ae 100644 --- a/3rdparty/openjpeg/openjp2/openjpeg.h +++ b/3rdparty/openjpeg/openjp2/openjpeg.h @@ -546,7 +546,7 @@ typedef struct opj_cparameters { } opj_cparameters_t; #define OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG 0x0001 -#define OPJ_DPARAMETERS_DUMP_FLAG 0x0002 +#define OPJ_DPARAMETERS_DUMP_FLAG 0x0002 /** * Decompression parameters @@ -772,7 +772,7 @@ typedef struct opj_packet_info { OPJ_OFF_T end_ph_pos; /** packet end position */ OPJ_OFF_T end_pos; - /** packet distorsion */ + /** packet distortion */ double disto; } opj_packet_info_t; @@ -1348,9 +1348,13 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_decoder(opj_codec_t *p_codec, opj_dparameters_t *parameters); /** - * Set strict decoding parameter for this decoder. If strict decoding is enabled, partial bit - * streams will fail to decode. If strict decoding is disabled, the decoder will decode partial - * bitstreams as much as possible without erroring + * Set strict decoding parameter for this decoder. + * If strict decoding is enabled, partial bit streams will fail to decode, and + * the check for invalid TPSOT values added in https://github.com/uclouvain/openjpeg/pull/514 + * will be disabled. + * If strict decoding is disabled, the decoder will decode partial + * bitstreams as much as possible without erroring, and the TPSOT fixing logic + * will be enabled. * * @param p_codec decompressor handler * @param strict OPJ_TRUE to enable strict decoding, OPJ_FALSE to disable diff --git a/3rdparty/openjpeg/openjp2/opj_common.h b/3rdparty/openjpeg/openjp2/opj_common.h index ee8adf4725..2923a35b7f 100644 --- a/3rdparty/openjpeg/openjp2/opj_common.h +++ b/3rdparty/openjpeg/openjp2/opj_common.h @@ -28,8 +28,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OPJ_COMMMON_H -#define OPJ_COMMMON_H +#ifndef OPJ_COMMON_H +#define OPJ_COMMON_H /* ========================================================== @@ -44,4 +44,4 @@ #define OPJ_COMP_PARAM_DEFAULT_PROG_ORDER OPJ_LRCP #define OPJ_COMP_PARAM_DEFAULT_NUMRESOLUTION 6 -#endif /* OPJ_COMMMON_H */ +#endif /* OPJ_COMMON_H */ diff --git a/3rdparty/openjpeg/openjp2/t1.c b/3rdparty/openjpeg/openjp2/t1.c index 52e466eb97..98dce47f55 100644 --- a/3rdparty/openjpeg/openjp2/t1.c +++ b/3rdparty/openjpeg/openjp2/t1.c @@ -47,6 +47,9 @@ #ifdef __SSE2__ #include #endif +#if (defined(__AVX2__) || defined(__AVX512F__)) +#include +#endif #if defined(__GNUC__) #pragma GCC poison malloc calloc realloc free @@ -1796,6 +1799,39 @@ static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls) OPJ_INT32* OPJ_RESTRICT tiledp = &tilec->data[(OPJ_SIZE_T)y * tile_w + (OPJ_SIZE_T)x]; for (j = 0; j < cblk_h; ++j) { + //positive -> round down aka. (83)/2 = 41.5 -> 41 + //negative -> round up aka. (-83)/2 = -41.5 -> -41 +#if defined(__AVX512F__) + OPJ_INT32* ptr_in = datap + (j * cblk_w); + OPJ_INT32* ptr_out = tiledp + (j * (OPJ_SIZE_T)tile_w); + for (i = 0; i < cblk_w / 16; ++i) { + __m512i in_avx = _mm512_loadu_si512((__m512i*)(ptr_in)); + const __m512i add_avx = _mm512_srli_epi32(in_avx, 31); + in_avx = _mm512_add_epi32(in_avx, add_avx); + _mm512_storeu_si512((__m512i*)(ptr_out), _mm512_srai_epi32(in_avx, 1)); + ptr_in += 16; + ptr_out += 16; + } + + for (i = 0; i < cblk_w % 16; ++i) { + ptr_out[i] = ptr_in[i] / 2; + } +#elif defined(__AVX2__) + OPJ_INT32* ptr_in = datap + (j * cblk_w); + OPJ_INT32* ptr_out = tiledp + (j * (OPJ_SIZE_T)tile_w); + for (i = 0; i < cblk_w / 8; ++i) { + __m256i in_avx = _mm256_loadu_si256((__m256i*)(ptr_in)); + const __m256i add_avx = _mm256_srli_epi32(in_avx, 31); + in_avx = _mm256_add_epi32(in_avx, add_avx); + _mm256_storeu_si256((__m256i*)(ptr_out), _mm256_srai_epi32(in_avx, 1)); + ptr_in += 8; + ptr_out += 8; + } + + for (i = 0; i < cblk_w % 8; ++i) { + ptr_out[i] = ptr_in[i] / 2; + } +#else i = 0; for (; i < (cblk_w & ~(OPJ_UINT32)3U); i += 4U) { OPJ_INT32 tmp0 = datap[(j * cblk_w) + i + 0U]; @@ -1811,6 +1847,7 @@ static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls) OPJ_INT32 tmp = datap[(j * cblk_w) + i]; ((OPJ_INT32*)tiledp)[(j * (OPJ_SIZE_T)tile_w) + i] = tmp / 2; } +#endif } } else { /* if (tccp->qmfbid == 0) */ const float stepsize = 0.5f * band->stepsize; @@ -2006,10 +2043,16 @@ static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1, opj_mqc_setstate(mqc, T1_CTXNO_AGG, 0, 3); opj_mqc_setstate(mqc, T1_CTXNO_ZC, 0, 4); + if (cblk->corrupted) { + assert(cblk->numchunks == 0); + return OPJ_TRUE; + } + /* Even if we have a single chunk, in multi-threaded decoding */ /* the insertion of our synthetic marker might potentially override */ /* valid codestream of other codeblocks decoded in parallel. */ - if (cblk->numchunks > 1 || t1->mustuse_cblkdatabuffer) { + if (cblk->numchunks > 1 || (t1->mustuse_cblkdatabuffer && + cblk->numchunks > 0)) { OPJ_UINT32 i; OPJ_UINT32 cblk_len; @@ -2124,7 +2167,7 @@ static OPJ_BOOL opj_t1_decode_cblk(opj_t1_t *t1, opj_mutex_lock(p_manager_mutex); } opj_event_msg(p_manager, EVT_WARNING, - "PTERM check failure: %d synthetized 0xFF markers read\n", + "PTERM check failure: %d synthesized 0xFF markers read\n", mqc->end_of_byte_stream_counter); if (p_manager_mutex) { opj_mutex_unlock(p_manager_mutex); @@ -2227,6 +2270,111 @@ static void opj_t1_cblk_encode_processor(void* user_data, opj_tls_t* tls) OPJ_UINT32* OPJ_RESTRICT t1data = (OPJ_UINT32*) t1->data; /* Change from "natural" order to "zigzag" order of T1 passes */ for (j = 0; j < (cblk_h & ~3U); j += 4) { +#if defined(__AVX512F__) + const __m512i perm1 = _mm512_setr_epi64(2, 3, 10, 11, 4, 5, 12, 13); + const __m512i perm2 = _mm512_setr_epi64(6, 7, 14, 15, 0, 0, 0, 0); + OPJ_UINT32* ptr = tiledp_u; + for (i = 0; i < cblk_w / 16; ++i) { + // INPUT OUTPUT + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 10 20 30 01 11 21 31 02 12 22 32 03 13 23 33 + // 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 04 14 24 34 05 15 25 35 06 16 26 36 07 17 27 37 + // 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 08 18 28 38 09 19 29 39 0A 1A 2A 3A 0B 1B 2B 3B + // 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 0C 1C 2C 3C 0D 1D 2D 3D 0E 1E 2E 3E 0F 1F 2F 3F + __m512i in1 = _mm512_slli_epi32(_mm512_loadu_si512((__m512i*)(ptr + + (j + 0) * tile_w)), T1_NMSEDEC_FRACBITS); + __m512i in2 = _mm512_slli_epi32(_mm512_loadu_si512((__m512i*)(ptr + + (j + 1) * tile_w)), T1_NMSEDEC_FRACBITS); + __m512i in3 = _mm512_slli_epi32(_mm512_loadu_si512((__m512i*)(ptr + + (j + 2) * tile_w)), T1_NMSEDEC_FRACBITS); + __m512i in4 = _mm512_slli_epi32(_mm512_loadu_si512((__m512i*)(ptr + + (j + 3) * tile_w)), T1_NMSEDEC_FRACBITS); + + __m512i tmp1 = _mm512_unpacklo_epi32(in1, in2); + __m512i tmp2 = _mm512_unpacklo_epi32(in3, in4); + __m512i tmp3 = _mm512_unpackhi_epi32(in1, in2); + __m512i tmp4 = _mm512_unpackhi_epi32(in3, in4); + + in1 = _mm512_unpacklo_epi64(tmp1, tmp2); + in2 = _mm512_unpacklo_epi64(tmp3, tmp4); + in3 = _mm512_unpackhi_epi64(tmp1, tmp2); + in4 = _mm512_unpackhi_epi64(tmp3, tmp4); + + _mm_storeu_si128((__m128i*)(t1data + 0), _mm512_castsi512_si128(in1)); + _mm_storeu_si128((__m128i*)(t1data + 4), _mm512_castsi512_si128(in3)); + _mm_storeu_si128((__m128i*)(t1data + 8), _mm512_castsi512_si128(in2)); + _mm_storeu_si128((__m128i*)(t1data + 12), _mm512_castsi512_si128(in4)); + + tmp1 = _mm512_permutex2var_epi64(in1, perm1, in3); + tmp2 = _mm512_permutex2var_epi64(in2, perm1, in4); + + _mm256_storeu_si256((__m256i*)(t1data + 16), _mm512_castsi512_si256(tmp1)); + _mm256_storeu_si256((__m256i*)(t1data + 24), _mm512_castsi512_si256(tmp2)); + _mm256_storeu_si256((__m256i*)(t1data + 32), _mm512_extracti64x4_epi64(tmp1, + 0x1)); + _mm256_storeu_si256((__m256i*)(t1data + 40), _mm512_extracti64x4_epi64(tmp2, + 0x1)); + _mm256_storeu_si256((__m256i*)(t1data + 48), + _mm512_castsi512_si256(_mm512_permutex2var_epi64(in1, perm2, in3))); + _mm256_storeu_si256((__m256i*)(t1data + 56), + _mm512_castsi512_si256(_mm512_permutex2var_epi64(in2, perm2, in4))); + t1data += 64; + ptr += 16; + } + for (i = 0; i < cblk_w % 16; ++i) { + t1data[0] = ptr[(j + 0) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[1] = ptr[(j + 1) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[2] = ptr[(j + 2) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[3] = ptr[(j + 3) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data += 4; + ptr += 1; + } +#elif defined(__AVX2__) + OPJ_UINT32* ptr = tiledp_u; + for (i = 0; i < cblk_w / 8; ++i) { + // INPUT OUTPUT + // 00 01 02 03 04 05 06 07 00 10 20 30 01 11 21 31 + // 10 11 12 13 14 15 16 17 02 12 22 32 03 13 23 33 + // 20 21 22 23 24 25 26 27 04 14 24 34 05 15 25 35 + // 30 31 32 33 34 35 36 37 06 16 26 36 07 17 27 37 + __m256i in1 = _mm256_slli_epi32(_mm256_loadu_si256((__m256i*)(ptr + + (j + 0) * tile_w)), T1_NMSEDEC_FRACBITS); + __m256i in2 = _mm256_slli_epi32(_mm256_loadu_si256((__m256i*)(ptr + + (j + 1) * tile_w)), T1_NMSEDEC_FRACBITS); + __m256i in3 = _mm256_slli_epi32(_mm256_loadu_si256((__m256i*)(ptr + + (j + 2) * tile_w)), T1_NMSEDEC_FRACBITS); + __m256i in4 = _mm256_slli_epi32(_mm256_loadu_si256((__m256i*)(ptr + + (j + 3) * tile_w)), T1_NMSEDEC_FRACBITS); + + __m256i tmp1 = _mm256_unpacklo_epi32(in1, in2); + __m256i tmp2 = _mm256_unpacklo_epi32(in3, in4); + __m256i tmp3 = _mm256_unpackhi_epi32(in1, in2); + __m256i tmp4 = _mm256_unpackhi_epi32(in3, in4); + + in1 = _mm256_unpacklo_epi64(tmp1, tmp2); + in2 = _mm256_unpacklo_epi64(tmp3, tmp4); + in3 = _mm256_unpackhi_epi64(tmp1, tmp2); + in4 = _mm256_unpackhi_epi64(tmp3, tmp4); + + _mm_storeu_si128((__m128i*)(t1data + 0), _mm256_castsi256_si128(in1)); + _mm_storeu_si128((__m128i*)(t1data + 4), _mm256_castsi256_si128(in3)); + _mm_storeu_si128((__m128i*)(t1data + 8), _mm256_castsi256_si128(in2)); + _mm_storeu_si128((__m128i*)(t1data + 12), _mm256_castsi256_si128(in4)); + _mm256_storeu_si256((__m256i*)(t1data + 16), _mm256_permute2x128_si256(in1, in3, + 0x31)); + _mm256_storeu_si256((__m256i*)(t1data + 24), _mm256_permute2x128_si256(in2, in4, + 0x31)); + t1data += 32; + ptr += 8; + } + for (i = 0; i < cblk_w % 8; ++i) { + t1data[0] = ptr[(j + 0) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[1] = ptr[(j + 1) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[2] = ptr[(j + 2) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data[3] = ptr[(j + 3) * tile_w] << T1_NMSEDEC_FRACBITS; + t1data += 4; + ptr += 1; + } +#else for (i = 0; i < cblk_w; ++i) { t1data[0] = tiledp_u[(j + 0) * tile_w + i] << T1_NMSEDEC_FRACBITS; t1data[1] = tiledp_u[(j + 1) * tile_w + i] << T1_NMSEDEC_FRACBITS; @@ -2234,6 +2382,7 @@ static void opj_t1_cblk_encode_processor(void* user_data, opj_tls_t* tls) t1data[3] = tiledp_u[(j + 3) * tile_w + i] << T1_NMSEDEC_FRACBITS; t1data += 4; } +#endif } if (j < cblk_h) { for (i = 0; i < cblk_w; ++i) { diff --git a/3rdparty/openjpeg/openjp2/t2.c b/3rdparty/openjpeg/openjp2/t2.c index 781a6a59a1..4e8cf60182 100644 --- a/3rdparty/openjpeg/openjp2/t2.c +++ b/3rdparty/openjpeg/openjp2/t2.c @@ -1111,6 +1111,7 @@ static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2, /* SOP markers */ if (p_tcp->csty & J2K_CP_CSTY_SOP) { + /* SOP markers are allowed (i.e. optional), just warn */ if (p_max_length < 6) { opj_event_msg(p_manager, EVT_WARNING, "Not enough space for expected SOP marker\n"); @@ -1163,12 +1164,15 @@ static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2, /* EPH markers */ if (p_tcp->csty & J2K_CP_CSTY_EPH) { + /* EPH markers are required */ if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data - *l_header_data_start)) < 2U) { - opj_event_msg(p_manager, EVT_WARNING, - "Not enough space for expected EPH marker\n"); + opj_event_msg(p_manager, EVT_ERROR, + "Not enough space for required EPH marker\n"); + return OPJ_FALSE; } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) { - opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n"); + opj_event_msg(p_manager, EVT_ERROR, "Expected EPH marker\n"); + return OPJ_FALSE; } else { l_header_data += 2; } @@ -1340,12 +1344,15 @@ static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2, /* EPH markers */ if (p_tcp->csty & J2K_CP_CSTY_EPH) { + /* EPH markers are required */ if ((*l_modified_length_ptr - (OPJ_UINT32)(l_header_data - *l_header_data_start)) < 2U) { - opj_event_msg(p_manager, EVT_WARNING, - "Not enough space for expected EPH marker\n"); + opj_event_msg(p_manager, EVT_ERROR, + "Not enough space for required EPH marker\n"); + return OPJ_FALSE; } else if ((*l_header_data) != 0xff || (*(l_header_data + 1) != 0x92)) { - opj_event_msg(p_manager, EVT_WARNING, "Expected EPH marker\n"); + opj_event_msg(p_manager, EVT_ERROR, "Expected EPH marker\n"); + return OPJ_FALSE; } else { l_header_data += 2; } @@ -1353,6 +1360,9 @@ static OPJ_BOOL opj_t2_read_packet_header(opj_t2_t* p_t2, l_header_length = (OPJ_UINT32)(l_header_data - *l_header_data_start); JAS_FPRINTF(stderr, "hdrlen=%d \n", l_header_length); + if (!l_header_length) { + return OPJ_FALSE; + } JAS_FPRINTF(stderr, "packet body\n"); *l_modified_length_ptr -= l_header_length; *l_header_data_start += l_header_length; @@ -1404,18 +1414,21 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2, l_nb_code_blocks = l_prc->cw * l_prc->ch; l_cblk = l_prc->cblks.dec; - for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno) { + for (cblkno = 0; cblkno < l_nb_code_blocks; ++cblkno, ++l_cblk) { opj_tcd_seg_t *l_seg = 00; - // if we have a partial data stream, set numchunks to zero - // since we have no data to actually decode. - if (partial_buffer) { - l_cblk->numchunks = 0; - } - if (!l_cblk->numnewpasses) { /* nothing to do */ - ++l_cblk; + continue; + } + + if (partial_buffer || l_cblk->corrupted) { + /* if a previous segment in this packet couldn't be decoded, + * or if this code block was corrupted in a previous layer, + * then mark it as corrupted. + */ + l_cblk->numchunks = 0; + l_cblk->corrupted = OPJ_TRUE; continue; } @@ -1448,18 +1461,13 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2, "read: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n", l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno); - // skip this codeblock since it is a partial read + /* skip this codeblock (and following ones in this + * packet) since it is a partial read + */ partial_buffer = OPJ_TRUE; + l_cblk->corrupted = OPJ_TRUE; l_cblk->numchunks = 0; - - l_seg->numpasses += l_seg->numnewpasses; - l_cblk->numnewpasses -= l_seg->numnewpasses; - if (l_cblk->numnewpasses > 0) { - ++l_seg; - ++l_cblk->numsegs; - break; - } - continue; + break; } } @@ -1516,7 +1524,7 @@ static OPJ_BOOL opj_t2_read_packet_data(opj_t2_t* p_t2, } while (l_cblk->numnewpasses > 0); l_cblk->real_num_segs = l_cblk->numsegs; - ++l_cblk; + } /* next code_block */ ++l_band; @@ -1600,6 +1608,8 @@ static OPJ_BOOL opj_t2_skip_packet_data(opj_t2_t* p_t2, "skip: segment too long (%d) with max (%d) for codeblock %d (p=%d, b=%d, r=%d, c=%d)\n", l_seg->newlen, p_max_length, cblkno, p_pi->precno, bandno, p_pi->resno, p_pi->compno); + + *p_data_read = p_max_length; return OPJ_TRUE; } } diff --git a/3rdparty/openjpeg/openjp2/tcd.c b/3rdparty/openjpeg/openjp2/tcd.c index 687aa61bb0..8ca259b71d 100644 --- a/3rdparty/openjpeg/openjp2/tcd.c +++ b/3rdparty/openjpeg/openjp2/tcd.c @@ -243,7 +243,7 @@ void opj_tcd_rateallocate_fixed(opj_tcd_t *tcd) /* ----------------------------------------------------------------------- */ /** Returns OPJ_TRUE if the layer allocation is unchanged w.r.t to the previous - * invokation with a different threshold */ + * invocation with a different threshold */ static OPJ_BOOL opj_tcd_makelayer(opj_tcd_t *tcd, OPJ_UINT32 layno, @@ -2861,12 +2861,12 @@ OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd, return intersects; } -/** Returns whether a tile componenent is fully decoded, taking into account +/** Returns whether a tile component is fully decoded, taking into account * p_tcd->win_* members. * * @param p_tcd TCD handle. * @param compno Component number - * @return OPJ_TRUE whether the tile componenent is fully decoded + * @return OPJ_TRUE whether the tile component is fully decoded */ static OPJ_BOOL opj_tcd_is_whole_tilecomp_decoding(opj_tcd_t *p_tcd, OPJ_UINT32 compno) diff --git a/3rdparty/openjpeg/openjp2/tcd.h b/3rdparty/openjpeg/openjp2/tcd.h index f659869a13..3371b08cb2 100644 --- a/3rdparty/openjpeg/openjp2/tcd.h +++ b/3rdparty/openjpeg/openjp2/tcd.h @@ -141,6 +141,7 @@ typedef struct opj_tcd_cblk_dec { OPJ_UINT32 numchunksalloc; /* Number of chunks item allocated */ /* Decoded code-block. Only used for subtile decoding. Otherwise tilec->data is directly updated */ OPJ_INT32* decoded_data; + OPJ_BOOL corrupted; /* whether the code block data is corrupted */ } opj_tcd_cblk_dec_t; /** Precinct structure */ @@ -312,7 +313,7 @@ typedef struct opj_tcd_marker_info { /** Dump the content of a tcd structure */ -/*void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t *img);*/ /* TODO MSD shoul use the new v2 structures */ +/*void tcd_dump(FILE *fd, opj_tcd_t *tcd, opj_tcd_image_t *img);*/ /* TODO MSD should use the new v2 structures */ /** Create a new TCD handle @@ -443,7 +444,7 @@ OPJ_BOOL opj_tcd_update_tile_data(opj_tcd_t *p_tcd, OPJ_SIZE_T opj_tcd_get_encoder_input_buffer_size(opj_tcd_t *p_tcd); /** - * Initialize the tile coder and may reuse some meory. + * Initialize the tile coder and may reuse some memory. * * @param p_tcd TCD handle. * @param p_tile_no current tile index to encode. @@ -491,7 +492,7 @@ void opj_tcd_reinit_segment(opj_tcd_seg_t* seg); * @param y0 Upper left y in subband coordinates * @param x1 Lower right x in subband coordinates * @param y1 Lower right y in subband coordinates - * @return OPJ_TRUE whether the sub-band region contributs to the area of + * @return OPJ_TRUE whether the sub-band region contributes to the area of * interest. */ OPJ_BOOL opj_tcd_is_subband_area_of_interest(opj_tcd_t *tcd, From 7a2b048c927eb080ba9b7ae7bd5904990f17e859 Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Thu, 20 Feb 2025 17:28:28 +0300 Subject: [PATCH 03/19] Merge pull request #26923 from dkurt:merge_rvv_opt Further optimization of cv::merge RVV HAL for 8U and 16S #26923 ### Pull Request Readiness Checklist * Banana Pi BF3 (SpacemiT K1) RISC-V * Compiler: Syntacore Clang 18.1.4 (build 2024.12) ``` Geometric mean (ms) Name of Test baseline pr pr merge vs baseline merge (x-factor) merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 2) 0.013 0.003 3.76 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 3) 0.020 0.006 3.46 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 4) 0.026 0.010 2.61 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 5) 0.043 0.028 1.56 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 6) 0.054 0.035 1.53 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 7) 0.065 0.050 1.30 merge::Size_SrcDepth_DstChannels::(127x61, 8UC1, 8) 0.070 0.036 1.95 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 2) 0.015 0.008 1.82 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 3) 0.022 0.015 1.48 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 4) 0.029 0.018 1.63 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 5) 0.067 0.044 1.54 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 6) 0.088 0.056 1.58 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 7) 0.104 0.076 1.38 merge::Size_SrcDepth_DstChannels::(127x61, 16SC1, 8) 0.116 0.065 1.79 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 2) 0.421 0.176 2.39 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 3) 0.792 0.284 2.79 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 4) 1.090 0.370 2.95 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 5) 1.835 1.399 1.31 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 6) 2.389 1.776 1.35 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 7) 3.000 2.471 1.21 merge::Size_SrcDepth_DstChannels::(640x480, 8UC1, 8) 3.178 2.104 1.51 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 2) 0.490 0.377 1.30 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 3) 1.348 0.602 2.24 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 4) 1.827 0.813 2.25 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 5) 3.283 2.692 1.22 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 6) 4.922 3.334 1.48 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 7) 5.725 4.399 1.30 merge::Size_SrcDepth_DstChannels::(640x480, 16SC1, 8) 6.278 4.748 1.32 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 2) 1.267 0.603 2.10 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 3) 2.394 0.934 2.56 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 4) 3.236 1.434 2.26 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 5) 5.398 4.345 1.24 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 6) 7.127 5.459 1.31 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 7) 8.590 7.298 1.18 merge::Size_SrcDepth_DstChannels::(1280x720, 8UC1, 8) 9.360 6.152 1.52 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 2) 1.482 1.242 1.19 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 3) 4.008 1.817 2.21 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 4) 6.079 2.468 2.46 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 5) 11.300 8.644 1.31 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 6) 15.125 12.126 1.25 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 7) 17.555 14.804 1.19 merge::Size_SrcDepth_DstChannels::(1280x720, 16SC1, 8) 18.890 14.163 1.33 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 2) 2.910 1.326 2.19 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 3) 5.351 1.997 2.68 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 4) 7.290 2.629 2.77 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 5) 12.426 9.611 1.29 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 6) 16.453 12.162 1.35 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 7) 19.420 16.190 1.20 merge::Size_SrcDepth_DstChannels::(1920x1080, 8UC1, 8) 20.588 13.699 1.50 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 2) 3.400 2.640 1.29 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 3) 8.986 3.952 2.27 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 4) 11.972 5.273 2.27 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 5) 20.544 17.996 1.14 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 6) 28.677 22.086 1.30 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 7) 32.958 27.713 1.19 merge::Size_SrcDepth_DstChannels::(1920x1080, 16SC1, 8) 36.499 27.439 1.33 ``` 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 - [x] 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 --- 3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp | 317 +++++++++++-------------- 1 file changed, 138 insertions(+), 179 deletions(-) diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp index 353885b709..760024f429 100644 --- a/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp +++ b/3rdparty/hal_rvv/hal_rvv_1p0/merge.hpp @@ -17,236 +17,195 @@ namespace cv { namespace cv_hal_rvv { #undef cv_hal_merge64s #define cv_hal_merge64s cv::cv_hal_rvv::merge64s -#if defined __GNUC__ -__attribute__((optimize("no-tree-vectorize"))) -#endif +#if defined __clang__ && __clang_major__ < 18 +#define OPENCV_HAL_IMPL_RVV_VCREATE_x2(suffix, width, v0, v1) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x2(seg, 0, v0); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x2(seg, 1, v1); + +#define OPENCV_HAL_IMPL_RVV_VCREATE_x3(suffix, width, v0, v1, v2) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(seg, 0, v0); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(seg, 1, v1); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x3(seg, 2, v2); + +#define OPENCV_HAL_IMPL_RVV_VCREATE_x4(suffix, width, v0, v1, v2, v3) \ + __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(seg, 0, v0); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(seg, 1, v1); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(seg, 2, v2); \ + seg = __riscv_vset_v_##suffix##m##width##_##suffix##m##width##x4(seg, 3, v3); + +#define __riscv_vcreate_v_u8m4x2(v0, v1) OPENCV_HAL_IMPL_RVV_VCREATE_x2(u8, 4, v0, v1) +#define __riscv_vcreate_v_u8m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u8, 2, v0, v1, v2) +#define __riscv_vcreate_v_u8m2x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(u8, 2, v0, v1, v2, v3) +#define __riscv_vcreate_v_u16m4x2(v0, v1) OPENCV_HAL_IMPL_RVV_VCREATE_x2(u16, 4, v0, v1) +#define __riscv_vcreate_v_u16m2x3(v0, v1, v2) OPENCV_HAL_IMPL_RVV_VCREATE_x3(u16, 2, v0, v1, v2) +#define __riscv_vcreate_v_u16m2x4(v0, v1, v2, v3) OPENCV_HAL_IMPL_RVV_VCREATE_x4(u16, 2, v0, v1, v2, v3) +#endif // clang < 18 + inline int merge8u(const uchar** src, uchar* dst, int len, int cn ) { - int k = cn % 4 ? cn % 4 : 4; - int i = 0; - int vl = __riscv_vsetvlmax_e8m1(); - if( k == 1 ) + int vl = 0; + if (cn == 1) { const uchar* src0 = src[0]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle8_v_u8m1(src0 + i, vl); - __riscv_vsse8_v_u8m1(dst + i*cn, sizeof(uchar)*cn, a, vl); + vl = __riscv_vsetvl_e8m8(len - i); + __riscv_vse8_v_u8m8(dst + i, __riscv_vle8_v_u8m8(src0 + i, vl), vl); } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++) - dst[i*cn] = src0[i]; } - else if( k == 2 ) + else if (cn == 2) { const uchar *src0 = src[0], *src1 = src[1]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle8_v_u8m1(src0 + i, vl); - auto b = __riscv_vle8_v_u8m1(src1 + i, vl); - __riscv_vsse8_v_u8m1(dst + i*cn, sizeof(uchar)*cn, a, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 1, sizeof(uchar)*cn, b, vl); - } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) - { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; + vl = __riscv_vsetvl_e8m4(len - i); + vuint8m4x2_t seg = __riscv_vcreate_v_u8m4x2( + __riscv_vle8_v_u8m4(src0 + i, vl), + __riscv_vle8_v_u8m4(src1 + i, vl) + ); + __riscv_vsseg2e8_v_u8m4x2(dst + i * cn, seg, vl); } } - else if( k == 3 ) + else if (cn == 3) { const uchar *src0 = src[0], *src1 = src[1], *src2 = src[2]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle8_v_u8m1(src0 + i, vl); - auto b = __riscv_vle8_v_u8m1(src1 + i, vl); - auto c = __riscv_vle8_v_u8m1(src2 + i, vl); - __riscv_vsse8_v_u8m1(dst + i*cn, sizeof(uchar)*cn, a, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 1, sizeof(uchar)*cn, b, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 2, sizeof(uchar)*cn, c, vl); + vl = __riscv_vsetvl_e8m2(len - i); + vuint8m2x3_t seg = __riscv_vcreate_v_u8m2x3( + __riscv_vle8_v_u8m2(src0 + i, vl), + __riscv_vle8_v_u8m2(src1 + i, vl), + __riscv_vle8_v_u8m2(src2 + i, vl) + ); + __riscv_vsseg3e8_v_u8m2x3(dst + i * cn, seg, vl); } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) + } + else if (cn == 4) + { + const uchar *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3]; + for (int i = 0; i < len; i += vl) { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; - dst[i*cn+2] = src2[i]; + vl = __riscv_vsetvl_e8m2(len - i); + vuint8m2x4_t seg = __riscv_vcreate_v_u8m2x4( + __riscv_vle8_v_u8m2(src0 + i, vl), + __riscv_vle8_v_u8m2(src1 + i, vl), + __riscv_vle8_v_u8m2(src2 + i, vl), + __riscv_vle8_v_u8m2(src3 + i, vl) + ); + __riscv_vsseg4e8_v_u8m2x4(dst + i * cn, seg, vl); } } else { - const uchar *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3]; - for( ; i <= len - vl; i += vl) + int k = 0; + for (; k <= cn - 4; k += 4) { - auto a = __riscv_vle8_v_u8m1(src0 + i, vl); - auto b = __riscv_vle8_v_u8m1(src1 + i, vl); - auto c = __riscv_vle8_v_u8m1(src2 + i, vl); - auto d = __riscv_vle8_v_u8m1(src3 + i, vl); - __riscv_vsse8_v_u8m1(dst + i*cn, sizeof(uchar)*cn, a, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 1, sizeof(uchar)*cn, b, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 2, sizeof(uchar)*cn, c, vl); - __riscv_vsse8_v_u8m1(dst + i*cn + 3, sizeof(uchar)*cn, d, vl); + const uchar *src0 = src[k], *src1 = src[k + 1], *src2 = src[k + 2], *src3 = src[k + 3]; + for (int i = 0; i < len; i += vl) + { + vl = __riscv_vsetvl_e8m2(len - i); + vuint8m2x4_t seg = __riscv_vcreate_v_u8m2x4( + __riscv_vle8_v_u8m2(src0 + i, vl), + __riscv_vle8_v_u8m2(src1 + i, vl), + __riscv_vle8_v_u8m2(src2 + i, vl), + __riscv_vle8_v_u8m2(src3 + i, vl) + ); + __riscv_vssseg4e8_v_u8m2x4(dst + k + i * cn, cn, seg, vl); + } } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) + for (; k < cn; ++k) { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; - dst[i*cn+2] = src2[i]; - dst[i*cn+3] = src3[i]; - } - } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; k < cn; k += 4 ) - { - const uchar *src0 = src[k], *src1 = src[k+1], *src2 = src[k+2], *src3 = src[k+3]; - i = 0; - for( ; i <= len - vl; i += vl) - { - auto a = __riscv_vle8_v_u8m1(src0 + i, vl); - auto b = __riscv_vle8_v_u8m1(src1 + i, vl); - auto c = __riscv_vle8_v_u8m1(src2 + i, vl); - auto d = __riscv_vle8_v_u8m1(src3 + i, vl); - __riscv_vsse8_v_u8m1(dst + k+i*cn, sizeof(uchar)*cn, a, vl); - __riscv_vsse8_v_u8m1(dst + k+i*cn + 1, sizeof(uchar)*cn, b, vl); - __riscv_vsse8_v_u8m1(dst + k+i*cn + 2, sizeof(uchar)*cn, c, vl); - __riscv_vsse8_v_u8m1(dst + k+i*cn + 3, sizeof(uchar)*cn, d, vl); - } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) - { - dst[k+i*cn] = src0[i]; - dst[k+i*cn+1] = src1[i]; - dst[k+i*cn+2] = src2[i]; - dst[k+i*cn+3] = src3[i]; + const uchar* srcK = src[k]; + for (int i = 0; i < len; i += vl) + { + vl = __riscv_vsetvl_e8m2(len - i); + vuint8m2_t seg = __riscv_vle8_v_u8m2(srcK + i, vl); + __riscv_vsse8_v_u8m2(dst + k + i * cn, cn, seg, vl); + } } } return CV_HAL_ERROR_OK; } -#if defined __GNUC__ -__attribute__((optimize("no-tree-vectorize"))) -#endif inline int merge16u(const ushort** src, ushort* dst, int len, int cn ) { - int k = cn % 4 ? cn % 4 : 4; - int i = 0; - int vl = __riscv_vsetvlmax_e16m1(); - if( k == 1 ) + int vl = 0; + if (cn == 1) { const ushort* src0 = src[0]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle16_v_u16m1(src0 + i, vl); - __riscv_vsse16_v_u16m1(dst + i*cn, sizeof(ushort)*cn, a, vl); + vl = __riscv_vsetvl_e16m8(len - i); + __riscv_vse16_v_u16m8(dst + i, __riscv_vle16_v_u16m8(src0 + i, vl), vl); } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++) - dst[i*cn] = src0[i]; } - else if( k == 2 ) + else if (cn == 2) { const ushort *src0 = src[0], *src1 = src[1]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle16_v_u16m1(src0 + i, vl); - auto b = __riscv_vle16_v_u16m1(src1 + i, vl); - __riscv_vsse16_v_u16m1(dst + i*cn, sizeof(ushort)*cn, a, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 1, sizeof(ushort)*cn, b, vl); - } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) - { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; + vl = __riscv_vsetvl_e16m4(len - i); + vuint16m4x2_t seg = __riscv_vcreate_v_u16m4x2( + __riscv_vle16_v_u16m4(src0 + i, vl), + __riscv_vle16_v_u16m4(src1 + i, vl) + ); + __riscv_vsseg2e16_v_u16m4x2(dst + i * cn, seg, vl); } } - else if( k == 3 ) + else if (cn == 3) { const ushort *src0 = src[0], *src1 = src[1], *src2 = src[2]; - for( ; i <= len - vl; i += vl) + for (int i = 0; i < len; i += vl) { - auto a = __riscv_vle16_v_u16m1(src0 + i, vl); - auto b = __riscv_vle16_v_u16m1(src1 + i, vl); - auto c = __riscv_vle16_v_u16m1(src2 + i, vl); - __riscv_vsse16_v_u16m1(dst + i*cn, sizeof(ushort)*cn, a, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 1, sizeof(ushort)*cn, b, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 2, sizeof(ushort)*cn, c, vl); + vl = __riscv_vsetvl_e16m2(len - i); + vuint16m2x3_t seg = __riscv_vcreate_v_u16m2x3( + __riscv_vle16_v_u16m2(src0 + i, vl), + __riscv_vle16_v_u16m2(src1 + i, vl), + __riscv_vle16_v_u16m2(src2 + i, vl) + ); + __riscv_vsseg3e16_v_u16m2x3(dst + i * cn, seg, vl); } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) + } + else if (cn == 4) + { + const ushort *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3]; + for (int i = 0; i < len; i += vl) { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; - dst[i*cn+2] = src2[i]; + vl = __riscv_vsetvl_e16m2(len - i); + vuint16m2x4_t seg = __riscv_vcreate_v_u16m2x4( + __riscv_vle16_v_u16m2(src0 + i, vl), + __riscv_vle16_v_u16m2(src1 + i, vl), + __riscv_vle16_v_u16m2(src2 + i, vl), + __riscv_vle16_v_u16m2(src3 + i, vl) + ); + __riscv_vsseg4e16_v_u16m2x4(dst + i * cn, seg, vl); } } else { - const ushort *src0 = src[0], *src1 = src[1], *src2 = src[2], *src3 = src[3]; - for( ; i <= len - vl; i += vl) + int k = 0; + for (; k <= cn - 4; k += 4) { - auto a = __riscv_vle16_v_u16m1(src0 + i, vl); - auto b = __riscv_vle16_v_u16m1(src1 + i, vl); - auto c = __riscv_vle16_v_u16m1(src2 + i, vl); - auto d = __riscv_vle16_v_u16m1(src3 + i, vl); - __riscv_vsse16_v_u16m1(dst + i*cn, sizeof(ushort)*cn, a, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 1, sizeof(ushort)*cn, b, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 2, sizeof(ushort)*cn, c, vl); - __riscv_vsse16_v_u16m1(dst + i*cn + 3, sizeof(ushort)*cn, d, vl); + const ushort *src0 = src[k], *src1 = src[k + 1], *src2 = src[k + 2], *src3 = src[k + 3]; + for (int i = 0; i < len; i += vl) + { + vl = __riscv_vsetvl_e16m2(len - i); + vuint16m2x4_t seg = __riscv_vcreate_v_u16m2x4( + __riscv_vle16_v_u16m2(src0 + i, vl), + __riscv_vle16_v_u16m2(src1 + i, vl), + __riscv_vle16_v_u16m2(src2 + i, vl), + __riscv_vle16_v_u16m2(src3 + i, vl) + ); + __riscv_vssseg4e16_v_u16m2x4(dst + k + i * cn, cn * sizeof(ushort), seg, vl); + } } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; i < len; i++ ) + for (; k < cn; ++k) { - dst[i*cn] = src0[i]; - dst[i*cn+1] = src1[i]; - dst[i*cn+2] = src2[i]; - dst[i*cn+3] = src3[i]; - } - } - #if defined(__clang__) - #pragma clang loop vectorize(disable) - #endif - for( ; k < cn; k += 4 ) - { - const uint16_t *src0 = src[k], *src1 = src[k+1], *src2 = src[k+2], *src3 = src[k+3]; - i = 0; - for( ; i <= len - vl; i += vl) - { - auto a = __riscv_vle16_v_u16m1(src0 + i, vl); - auto b = __riscv_vle16_v_u16m1(src1 + i, vl); - auto c = __riscv_vle16_v_u16m1(src2 + i, vl); - auto d = __riscv_vle16_v_u16m1(src3 + i, vl); - __riscv_vsse16_v_u16m1(dst + k+i*cn, sizeof(ushort)*cn, a, vl); - __riscv_vsse16_v_u16m1(dst + k+i*cn + 1, sizeof(ushort)*cn, b, vl); - __riscv_vsse16_v_u16m1(dst + k+i*cn + 2, sizeof(ushort)*cn, c, vl); - __riscv_vsse16_v_u16m1(dst + k+i*cn + 3, sizeof(ushort)*cn, d, vl); - } - for( ; i < len; i++ ) - { - dst[k+i*cn] = src0[i]; - dst[k+i*cn+1] = src1[i]; - dst[k+i*cn+2] = src2[i]; - dst[k+i*cn+3] = src3[i]; + const ushort* srcK = src[k]; + for (int i = 0; i < len; i += vl) + { + vl = __riscv_vsetvl_e16m2(len - i); + vuint16m2_t seg = __riscv_vle16_v_u16m2(srcK + i, vl); + __riscv_vsse16_v_u16m2(dst + k + i * cn, cn * sizeof(ushort), seg, vl); + } } } return CV_HAL_ERROR_OK; From a47f0f00cb68c6afd3a24a880cc4dc4517ae5c7d Mon Sep 17 00:00:00 2001 From: shyama7004 Date: Fri, 21 Feb 2025 10:37:11 +0530 Subject: [PATCH 04/19] replace deprecated np.fromstring() by np.frombuffer() --- modules/python/test/tests_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/python/test/tests_common.py b/modules/python/test/tests_common.py index d6931829d3..cda448891b 100644 --- a/modules/python/test/tests_common.py +++ b/modules/python/test/tests_common.py @@ -49,7 +49,7 @@ class NewOpenCVTests(unittest.TestCase): filepath = self.find_file(filename) with open(filepath, 'rb') as f: filedata = f.read() - self.image_cache[filename] = cv.imdecode(np.fromstring(filedata, dtype=np.uint8), iscolor) + self.image_cache[filename] = cv.imdecode(np.frombuffer(filedata, dtype=np.uint8), iscolor) return self.image_cache[filename] def setUp(self): From e2803bee5cf6f3bd55f4e839a654e96d22b1a769 Mon Sep 17 00:00:00 2001 From: Yuantao Feng Date: Fri, 21 Feb 2025 18:49:11 +0800 Subject: [PATCH 05/19] Merge pull request #26885 from fengyuentau:4x/core/normalize_simd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit core: vectorize cv::normalize / cv::norm #26885 Checklist: | | normInf | normL1 | normL2 | | ---- | ------- | ------ | ------ | | bool | - | - | - | | 8u | √ | √ | √ | | 8s | √ | √ | √ | | 16u | √ | √ | √ | | 16s | √ | √ | √ | | 16f | - | - | - | | 16bf | - | - | - | | 32u | - | - | - | | 32s | √ | √ | √ | | 32f | √ | √ | √ | | 64u | - | - | - | | 64s | - | - | - | | 64f | √ | √ | √ | *: Vectorization of data type bool, 16f, 16bf, 32u, 64u and 64s needs to be done on 5.x. ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/core/CMakeLists.txt | 1 + .../core/src/{norm.cpp => norm.dispatch.cpp} | 150 +--- modules/core/src/norm.rvv1p0.hpp | 200 ++++++ modules/core/src/norm.simd.hpp | 676 ++++++++++++++++++ 4 files changed, 905 insertions(+), 122 deletions(-) rename modules/core/src/{norm.cpp => norm.dispatch.cpp} (92%) create mode 100644 modules/core/src/norm.rvv1p0.hpp create mode 100644 modules/core/src/norm.simd.hpp diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 2412168c9d..62bddbb413 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -12,6 +12,7 @@ ocv_add_dispatched_file(mean SSE2 AVX2 LASX) ocv_add_dispatched_file(merge SSE2 AVX2 LASX) ocv_add_dispatched_file(split SSE2 AVX2 LASX) ocv_add_dispatched_file(sum SSE2 AVX2 LASX) +ocv_add_dispatched_file(norm SSE2 SSE4_1 AVX AVX2 NEON_DOTPROD LASX) # 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/src/norm.cpp b/modules/core/src/norm.dispatch.cpp similarity index 92% rename from modules/core/src/norm.cpp rename to modules/core/src/norm.dispatch.cpp index 0452e40a55..e43e33c92e 100644 --- a/modules/core/src/norm.cpp +++ b/modules/core/src/norm.dispatch.cpp @@ -7,6 +7,9 @@ #include "opencl_kernels_core.hpp" #include "stat.hpp" +#include "norm.simd.hpp" +#include "norm.simd_declarations.hpp" + /****************************************************************************************\ * norm * \****************************************************************************************/ @@ -215,72 +218,6 @@ int normL1_(const uchar* a, const uchar* b, int n) //================================================================================================== -template int -normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result = std::max(result, normInf(src, len*cn)); - } - else - { - for( int i = 0; i < len; i++, src += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result = std::max(result, ST(cv_abs(src[k]))); - } - } - *_result = result; - return 0; -} - -template int -normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result += normL1(src, len*cn); - } - else - { - for( int i = 0; i < len; i++, src += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result += cv_abs(src[k]); - } - } - *_result = result; - return 0; -} - -template int -normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) -{ - ST result = *_result; - if( !mask ) - { - result += normL2Sqr(src, len*cn); - } - else - { - for( int i = 0; i < len; i++, src += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - { - T v = src[k]; - result += (ST)v*v; - } - } - } - *_result = result; - return 0; -} - template int normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { @@ -347,51 +284,27 @@ normDiffL2_(const T* src1, const T* src2, const uchar* mask, ST* _result, int le return 0; } -#define CV_DEF_NORM_FUNC(L, suffix, type, ntype) \ - static int norm##L##_##suffix(const type* src, const uchar* mask, ntype* r, int len, int cn) \ -{ return norm##L##_(src, mask, r, len, cn); } \ +#define CV_DEF_NORM_DIFF_FUNC(L, suffix, type, ntype) \ static int normDiff##L##_##suffix(const type* src1, const type* src2, \ const uchar* mask, ntype* r, int len, int cn) \ { return normDiff##L##_(src1, src2, mask, r, (int)len, cn); } -#define CV_DEF_NORM_ALL(suffix, type, inftype, l1type, l2type) \ - CV_DEF_NORM_FUNC(Inf, suffix, type, inftype) \ - CV_DEF_NORM_FUNC(L1, suffix, type, l1type) \ - CV_DEF_NORM_FUNC(L2, suffix, type, l2type) - -CV_DEF_NORM_ALL(8u, uchar, int, int, int) -CV_DEF_NORM_ALL(8s, schar, int, int, int) -CV_DEF_NORM_ALL(16u, ushort, int, int, double) -CV_DEF_NORM_ALL(16s, short, int, int, double) -CV_DEF_NORM_ALL(32s, int, int, double, double) -CV_DEF_NORM_ALL(32f, float, float, double, double) -CV_DEF_NORM_ALL(64f, double, double, double, double) +#define CV_DEF_NORM_DIFF_ALL(suffix, type, inftype, l1type, l2type) \ + CV_DEF_NORM_DIFF_FUNC(Inf, suffix, type, inftype) \ + CV_DEF_NORM_DIFF_FUNC(L1, suffix, type, l1type) \ + CV_DEF_NORM_DIFF_FUNC(L2, suffix, type, l2type) +CV_DEF_NORM_DIFF_ALL(8u, uchar, int, int, int) +CV_DEF_NORM_DIFF_ALL(8s, schar, int, int, int) +CV_DEF_NORM_DIFF_ALL(16u, ushort, int, int, double) +CV_DEF_NORM_DIFF_ALL(16s, short, int, int, double) +CV_DEF_NORM_DIFF_ALL(32s, int, int, double, double) +CV_DEF_NORM_DIFF_ALL(32f, float, float, double, double) +CV_DEF_NORM_DIFF_ALL(64f, double, double, double, double) typedef int (*NormFunc)(const uchar*, const uchar*, uchar*, int, int); typedef int (*NormDiffFunc)(const uchar*, const uchar*, const uchar*, uchar*, int, int); -static NormFunc getNormFunc(int normType, int depth) -{ - static NormFunc normTab[3][8] = - { - { - (NormFunc)GET_OPTIMIZED(normInf_8u), (NormFunc)GET_OPTIMIZED(normInf_8s), (NormFunc)GET_OPTIMIZED(normInf_16u), (NormFunc)GET_OPTIMIZED(normInf_16s), - (NormFunc)GET_OPTIMIZED(normInf_32s), (NormFunc)GET_OPTIMIZED(normInf_32f), (NormFunc)normInf_64f, 0 - }, - { - (NormFunc)GET_OPTIMIZED(normL1_8u), (NormFunc)GET_OPTIMIZED(normL1_8s), (NormFunc)GET_OPTIMIZED(normL1_16u), (NormFunc)GET_OPTIMIZED(normL1_16s), - (NormFunc)GET_OPTIMIZED(normL1_32s), (NormFunc)GET_OPTIMIZED(normL1_32f), (NormFunc)normL1_64f, 0 - }, - { - (NormFunc)GET_OPTIMIZED(normL2_8u), (NormFunc)GET_OPTIMIZED(normL2_8s), (NormFunc)GET_OPTIMIZED(normL2_16u), (NormFunc)GET_OPTIMIZED(normL2_16s), - (NormFunc)GET_OPTIMIZED(normL2_32s), (NormFunc)GET_OPTIMIZED(normL2_32f), (NormFunc)normL2_64f, 0 - } - }; - - return normTab[normType][depth]; -} - static NormDiffFunc getNormDiffFunc(int normType, int depth) { static NormDiffFunc normDiffTab[3][8] = @@ -603,6 +516,11 @@ static bool ipp_norm(Mat &src, int normType, Mat &mask, double &result) } // ipp_norm() #endif // HAVE_IPP +static NormFunc getNormFunc(int normType, int depth) { + CV_INSTRUMENT_REGION(); + CV_CPU_DISPATCH(getNormFunc, (normType, depth), CV_CPU_DISPATCH_MODES_ALL); +} + double norm( InputArray _src, int normType, InputArray _mask ) { CV_INSTRUMENT_REGION(); @@ -637,6 +555,9 @@ double norm( InputArray _src, int normType, InputArray _mask ) CV_IPP_RUN(IPP_VERSION_X100 >= 700, ipp_norm(src, normType, mask, _result), _result); + NormFunc func = getNormFunc(normType >> 1, depth == CV_16F ? CV_32F : depth); + CV_Assert( func != 0 ); + if( src.isContinuous() && mask.empty() ) { size_t len = src.total()*cn; @@ -644,30 +565,18 @@ double norm( InputArray _src, int normType, InputArray _mask ) { if( depth == CV_32F ) { - const float* data = src.ptr(); + const uchar* data = src.ptr(); - if( normType == NORM_L2 ) + if( normType == NORM_L2 || normType == NORM_L2SQR || normType == NORM_L1 ) { double result = 0; - GET_OPTIMIZED(normL2_32f)(data, 0, &result, (int)len, 1); - return std::sqrt(result); - } - if( normType == NORM_L2SQR ) - { - double result = 0; - GET_OPTIMIZED(normL2_32f)(data, 0, &result, (int)len, 1); - return result; - } - if( normType == NORM_L1 ) - { - double result = 0; - GET_OPTIMIZED(normL1_32f)(data, 0, &result, (int)len, 1); - return result; + func(data, 0, (uchar*)&result, (int)len, 1); + return normType == NORM_L2 ? std::sqrt(result) : result; } if( normType == NORM_INF ) { float result = 0; - GET_OPTIMIZED(normInf_32f)(data, 0, &result, (int)len, 1); + func(data, 0, (uchar*)&result, (int)len, 1); return result; } } @@ -714,9 +623,6 @@ double norm( InputArray _src, int normType, InputArray _mask ) return result; } - NormFunc func = getNormFunc(normType >> 1, depth == CV_16F ? CV_32F : depth); - CV_Assert( func != 0 ); - const Mat* arrays[] = {&src, &mask, 0}; uchar* ptrs[2] = {}; union diff --git a/modules/core/src/norm.rvv1p0.hpp b/modules/core/src/norm.rvv1p0.hpp new file mode 100644 index 0000000000..3db05c50a4 --- /dev/null +++ b/modules/core/src/norm.rvv1p0.hpp @@ -0,0 +1,200 @@ +// 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. +// +// Copytright (C) 2025, SpaceMIT Inc., all rights reserved. + +#include "opencv2/core/hal/intrin.hpp" + +namespace cv { + +namespace { + +// [TODO] Drop this until rvv has dedicated intrinsics for abs on integers. +template inline ST __riscv_vabs(const T&); + +template<> inline +vuint8m1_t __riscv_vabs(const vint8m1_t& v) { + const int vle8m1 = __riscv_vsetvlmax_e8m1(); + vint8m1_t mask = __riscv_vsra_vx_i8m1(v, 7, vle8m1); + vint8m1_t v_xor = __riscv_vxor_vv_i8m1(v, mask, vle8m1); + return __riscv_vreinterpret_v_i8m1_u8m1( + __riscv_vsub_vv_i8m1(v_xor, mask, vle8m1) + ); +} + +template<> inline +vuint16m1_t __riscv_vabs(const vint16m1_t& v) { + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + vint16m1_t mask = __riscv_vsra_vx_i16m1(v, 15, vle16m1); + vint16m1_t v_xor = __riscv_vxor_vv_i16m1(v, mask, vle16m1); + return __riscv_vreinterpret_v_i16m1_u16m1( + __riscv_vsub_vv_i16m1(v_xor, mask, vle16m1) + ); +} + +template<> inline +vuint32m1_t __riscv_vabs(const vint32m1_t& v) { + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vint32m1_t mask = __riscv_vsra_vx_i32m1(v, 31, vle32m1); + vint32m1_t v_xor = __riscv_vxor_vv_i32m1(v, mask, vle32m1); + return __riscv_vreinterpret_v_i32m1_u32m1( + __riscv_vsub_vv_i32m1(v_xor, mask, vle32m1) + ); +} +} + +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +template inline +ST normInf_rvv(const T* src, int n, int& j); + +template<> inline +int normInf_rvv(const int* src, int n, int& j) { + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); + for (; j <= n - 2 * vle32m1; j += 2 * vle32m1) { + vuint32m1_t v0 = __riscv_vabs(__riscv_vle32_v_i32m1(src + j, vle32m1)); + r0 = __riscv_vmaxu(r0, v0, vle32m1); + + vuint32m1_t v1 = __riscv_vabs(__riscv_vle32_v_i32m1(src + j + vle32m1, vle32m1)); + r1 = __riscv_vmaxu(r1, v1, vle32m1); + } + r0 = __riscv_vmaxu(r0, r1, vle32m1); + return (int)__riscv_vmv_x(__riscv_vredmaxu(r0, __riscv_vmv_v_x_u32m1(0, vle32m1), vle32m1)); +} + +template inline +ST normL1_rvv(const T* src, int n, int& j); + +template<> inline +int normL1_rvv(const schar* src, int n, int& j) { + const int vle8m1 = __riscv_vsetvlmax_e8m1(); + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint16m1_t zero = __riscv_vmv_v_x_u16m1(0, vle16m1); + for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { + vuint8m1_t v0 = __riscv_vabs(__riscv_vle8_v_i8m1(src + j, vle8m1)); + vuint16m1_t u0 = __riscv_vwredsumu_tu(zero, v0, zero, vle8m1); + r0 = __riscv_vwredsumu(u0, r0, vle16m1); + + vuint8m1_t v1 = __riscv_vabs(__riscv_vle8_v_i8m1(src + j + vle8m1, vle8m1)); + vuint16m1_t u1 = __riscv_vwredsumu_tu(zero, v1, zero, vle8m1); + r1 = __riscv_vwredsumu(u1, r1, vle16m1); + } + return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); +} + +template<> inline +int normL1_rvv(const ushort* src, int n, int& j) { + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); + for (; j <= n - 2 * vle16m1; j += 2 * vle16m1) { + vuint16m1_t v0 = __riscv_vle16_v_u16m1(src + j, vle16m1); + r0 = __riscv_vwredsumu(v0, r0, vle16m1); + + vuint16m1_t v1 = __riscv_vle16_v_u16m1(src + j + vle16m1, vle16m1); + r1 = __riscv_vwredsumu(v1, r1, vle16m1); + } + return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); +} + +template<> inline +int normL1_rvv(const short* src, int n, int& j) { + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); + for (; j<= n - 2 * vle16m1; j += 2 * vle16m1) { + vuint16m1_t v0 = __riscv_vabs(__riscv_vle16_v_i16m1(src + j, vle16m1)); + r0 = __riscv_vwredsumu(v0, r0, vle16m1); + + vuint16m1_t v1 = __riscv_vabs(__riscv_vle16_v_i16m1(src + j + vle16m1, vle16m1)); + r1 = __riscv_vwredsumu(v1, r1, vle16m1); + } + return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); +} + +template<> inline +double normL1_rvv(const double* src, int n, int& j) { + const int vle64m1 = __riscv_vsetvlmax_e64m1(); + vfloat64m1_t r0 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); + vfloat64m1_t r1 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); + for (; j <= n - 2 * vle64m1; j += 2 * vle64m1) { + vfloat64m1_t v0 = __riscv_vle64_v_f64m1(src + j, vle64m1); + v0 = __riscv_vfabs(v0, vle64m1); + r0 = __riscv_vfadd(r0, v0, vle64m1); + + vfloat64m1_t v1 = __riscv_vle64_v_f64m1(src + j + vle64m1, vle64m1); + v1 = __riscv_vfabs(v1, vle64m1); + r1 = __riscv_vfadd(r1, v1, vle64m1); + } + r0 = __riscv_vfadd(r0, r1, vle64m1); + return __riscv_vfmv_f(__riscv_vfredusum(r0, __riscv_vfmv_v_f_f64m1(0.f, vle64m1), vle64m1)); +} + +template inline +ST normL2_rvv(const T* src, int n, int& j); + +template<> inline +int normL2_rvv(const uchar* src, int n, int& j) { + const int vle8m1 = __riscv_vsetvlmax_e8m1(); + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vuint32m1_t r0 = __riscv_vmv_v_x_u32m1(0, vle32m1); + vuint32m1_t r1 = __riscv_vmv_v_x_u32m1(0, vle32m1); + for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { + vuint8m1_t v0 = __riscv_vle8_v_u8m1(src + j, vle8m1); + vuint16m2_t u0 = __riscv_vwmulu(v0, v0, vle8m1); + r0 = __riscv_vwredsumu(u0, r0, vle16m1 * 2); + + vuint8m1_t v1 = __riscv_vle8_v_u8m1(src + j + vle8m1, vle8m1); + vuint16m2_t u1 = __riscv_vwmulu(v1, v1, vle8m1); + r1 = __riscv_vwredsumu(u1, r1, vle16m1 * 2); + } + return (int)__riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); +} + +template<> inline +int normL2_rvv(const schar* src, int n, int& j) { + const int vle8m1 = __riscv_vsetvlmax_e8m1(); + const int vle16m1 = __riscv_vsetvlmax_e16m1(); + const int vle32m1 = __riscv_vsetvlmax_e32m1(); + vint32m1_t r0 = __riscv_vmv_v_x_i32m1(0, vle32m1); + vint32m1_t r1 = __riscv_vmv_v_x_i32m1(0, vle32m1); + for (; j <= n - 2 * vle8m1; j += 2 * vle8m1) { + vint8m1_t v0 = __riscv_vle8_v_i8m1(src + j, vle8m1); + vint16m2_t u0 = __riscv_vwmul(v0, v0, vle8m1); + r0 = __riscv_vwredsum(u0, r0, vle16m1 * 2); + + vint8m1_t v1 = __riscv_vle8_v_i8m1(src + j + vle8m1, vle8m1); + vint16m2_t u1 = __riscv_vwmul(v1, v1, vle8m1); + r1 = __riscv_vwredsum(u1, r1, vle16m1 * 2); + } + return __riscv_vmv_x(__riscv_vadd(r0, r1, vle32m1)); +} + +template<> inline +double normL2_rvv(const double* src, int n, int& j) { + const int vle64m1 = __riscv_vsetvlmax_e64m1(); + vfloat64m1_t r0 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); + vfloat64m1_t r1 = __riscv_vfmv_v_f_f64m1(0.f, vle64m1); + for (; j <= n - 2 * vle64m1; j += 2 * vle64m1) { + vfloat64m1_t v0 = __riscv_vle64_v_f64m1(src + j, vle64m1); + r0 = __riscv_vfmacc(r0, v0, v0, vle64m1); + + vfloat64m1_t v1 = __riscv_vle64_v_f64m1(src + j + vle64m1, vle64m1); + r1 = __riscv_vfmacc(r1, v1, v1, vle64m1); + } + r0 = __riscv_vfadd(r0, r1, vle64m1); + return __riscv_vfmv_f(__riscv_vfredusum(r0, __riscv_vfmv_v_f_f64m1(0.f, vle64m1), vle64m1)); +} + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // cv:: diff --git a/modules/core/src/norm.simd.hpp b/modules/core/src/norm.simd.hpp new file mode 100644 index 0000000000..fd7b658ba1 --- /dev/null +++ b/modules/core/src/norm.simd.hpp @@ -0,0 +1,676 @@ +// 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 + +#include "precomp.hpp" + +#if CV_RVV +#include "norm.rvv1p0.hpp" +#endif + +namespace cv { + +using NormFunc = int (*)(const uchar*, const uchar*, uchar*, int, int); + +CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN + +NormFunc getNormFunc(int normType, int depth); + +#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +template +struct NormInf_SIMD { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s = std::max(s, (ST)cv_abs(src[i])); + } + return s; + } +}; + +template +struct NormL1_SIMD { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + s += cv_abs(src[i]); + } + return s; + } +}; + +template +struct NormL2_SIMD { + inline ST operator() (const T* src, int n) const { + ST s = 0; + for (int i = 0; i < n; i++) { + ST v = src[i]; + s += v * v; + } + return s; + } +}; + +#if (CV_SIMD || CV_SIMD_SCALABLE) + +template<> +struct NormInf_SIMD { + int operator() (const uchar* src, int n) const { + int j = 0; + int s = 0; + v_uint8 r0 = vx_setzero_u8(), r1 = vx_setzero_u8(); + v_uint8 r2 = vx_setzero_u8(), r3 = vx_setzero_u8(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r0 = v_max(r0, vx_load(src + j )); + r1 = v_max(r1, vx_load(src + j + VTraits::vlanes())); + r2 = v_max(r2, vx_load(src + j + 2 * VTraits::vlanes())); + r3 = v_max(r3, vx_load(src + j + 3 * VTraits::vlanes())); + } + r0 = v_max(r0, v_max(r1, v_max(r2, r3))); + for (; j < n; j++) { + s = std::max(s, (int)src[j]); + } + return std::max(s, (int)v_reduce_max(r0)); + } +}; + +template<> +struct NormInf_SIMD { + int operator() (const schar* src, int n) const { + int j = 0; + int s = 0; + v_uint8 r0 = vx_setzero_u8(), r1 = vx_setzero_u8(); + v_uint8 r2 = vx_setzero_u8(), r3 = vx_setzero_u8(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r0 = v_max(r0, v_abs(vx_load(src + j ))); + r1 = v_max(r1, v_abs(vx_load(src + j + VTraits::vlanes()))); + r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + r0 = v_max(r0, v_max(r1, v_max(r2, r3))); + for (; j < n; j++) { + s = std::max(s, cv_abs(src[j])); + } + return std::max(s, saturate_cast(v_reduce_max(r0))); + } +}; + +template<> +struct NormInf_SIMD { + int operator() (const ushort* src, int n) const { + int j = 0; + int s = 0; + v_uint16 d0 = vx_setzero_u16(), d1 = vx_setzero_u16(); + v_uint16 d2 = vx_setzero_u16(), d3 = vx_setzero_u16(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + d0 = v_max(d0, vx_load(src + j )); + d1 = v_max(d1, vx_load(src + j + VTraits::vlanes())); + d2 = v_max(d2, vx_load(src + j + 2 * VTraits::vlanes())); + d3 = v_max(d3, vx_load(src + j + 3 * VTraits::vlanes())); + } + d0 = v_max(d0, v_max(d1, v_max(d2, d3))); + for (; j < n; j++) { + s = std::max(s, (int)src[j]); + } + return std::max(s, (int)v_reduce_max(d0)); + } +}; + +template<> +struct NormInf_SIMD { + int operator() (const short* src, int n) const { + int j = 0; + int s = 0; + v_uint16 d0 = vx_setzero_u16(), d1 = vx_setzero_u16(); + v_uint16 d2 = vx_setzero_u16(), d3 = vx_setzero_u16(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + d0 = v_max(d0, v_abs(vx_load(src + j ))); + d1 = v_max(d1, v_abs(vx_load(src + j + VTraits::vlanes()))); + d2 = v_max(d2, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + d3 = v_max(d3, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + d0 = v_max(d0, v_max(d1, v_max(d2, d3))); + for (; j < n; j++) { + s = std::max(s, saturate_cast(cv_abs(src[j]))); + } + return std::max(s, saturate_cast(v_reduce_max(d0))); + } +}; + +template<> +struct NormInf_SIMD { + int operator() (const int* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normInf_rvv(src, n, j); +#else + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint32 r2 = vx_setzero_u32(), r3 = vx_setzero_u32(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r0 = v_max(r0, v_abs(vx_load(src + j ))); + r1 = v_max(r1, v_abs(vx_load(src + j + VTraits::vlanes()))); + r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + r0 = v_max(r0, v_max(r1, v_max(r2, r3))); + s = std::max(s, saturate_cast(v_reduce_max(r0))); +#endif + for (; j < n; j++) { + s = std::max(s, cv_abs(src[j])); + } + return s; + } +}; + +template<> +struct NormInf_SIMD { + float operator() (const float* src, int n) const { + int j = 0; + float s = 0.f; + v_float32 r0 = vx_setzero_f32(), r1 = vx_setzero_f32(); + v_float32 r2 = vx_setzero_f32(), r3 = vx_setzero_f32(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r0 = v_max(r0, v_abs(vx_load(src + j ))); + r1 = v_max(r1, v_abs(vx_load(src + j + VTraits::vlanes()))); + r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + r0 = v_max(r0, v_max(r1, v_max(r2, r3))); + for (; j < n; j++) { + s = std::max(s, cv_abs(src[j])); + } + return std::max(s, v_reduce_max(r0)); + } +}; + +template<> +struct NormL1_SIMD { + int operator() (const uchar* src, int n) const { + int j = 0; + int s = 0; + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint8 one = vx_setall_u8(1); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint8 v0 = vx_load(src + j); + r0 = v_dotprod_expand_fast(v0, one, r0); + + v_uint8 v1 = vx_load(src + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v1, one, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + s += src[j]; + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + int operator() (const schar* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normL1_rvv(src, n, j); +#else + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + v_uint8 one = vx_setall_u8(1); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint8 v0 = v_abs(vx_load(src + j)); + r0 = v_dotprod_expand_fast(v0, one, r0); + + v_uint8 v1 = v_abs(vx_load(src + j + VTraits::vlanes())); + r1 = v_dotprod_expand_fast(v1, one, r1); + } + s += v_reduce_sum(v_add(r0, r1)); +#endif + for (; j < n; j++) { + s += saturate_cast(cv_abs(src[j])); + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + int operator() (const ushort* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normL1_rvv(src, n, j); +#else + v_uint32 r00 = vx_setzero_u32(), r01 = vx_setzero_u32(); + v_uint32 r10 = vx_setzero_u32(), r11 = vx_setzero_u32(); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint16 v0 = vx_load(src + j); + v_uint32 v00, v01; + v_expand(v0, v00, v01); + r00 = v_add(r00, v00); + r01 = v_add(r01, v01); + + v_uint16 v1 = vx_load(src + j + VTraits::vlanes()); + v_uint32 v10, v11; + v_expand(v1, v10, v11); + r10 = v_add(r10, v10); + r11 = v_add(r11, v11); + } + s += (int)v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); +#endif + for (; j < n; j++) { + s += src[j]; + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + int operator() (const short* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normL1_rvv(src, n, j); +#else + v_uint32 r00 = vx_setzero_u32(), r01 = vx_setzero_u32(); + v_uint32 r10 = vx_setzero_u32(), r11 = vx_setzero_u32(); + for (; j<= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint16 v0 = v_abs(vx_load(src + j)); + v_uint32 v00, v01; + v_expand(v0, v00, v01); + r00 = v_add(r00, v00); + r01 = v_add(r01, v01); + + v_uint16 v1 = v_abs(vx_load(src + j + VTraits::vlanes())); + v_uint32 v10, v11; + v_expand(v1, v10, v11); + r10 = v_add(r10, v10); + r11 = v_add(r11, v11); + } + s += (int)v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); +#endif + for (; j < n; j++) { + s += saturate_cast(cv_abs(src[j])); + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + int operator() (const uchar* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normL2_rvv(src, n, j); +#else + v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint8 v0 = vx_load(src + j); + r0 = v_dotprod_expand_fast(v0, v0, r0); + + v_uint8 v1 = vx_load(src + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v1, v1, r1); + } + s += v_reduce_sum(v_add(r0, r1)); +#endif + for (; j < n; j++) { + int v = saturate_cast(src[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + int operator() (const schar* src, int n) const { + int j = 0; + int s = 0; +#if CV_RVV + s = normL2_rvv(src, n, j); +#else + v_int32 r0 = vx_setzero_s32(), r1 = vx_setzero_s32(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int8 v0 = vx_load(src + j); + r0 = v_dotprod_expand_fast(v0, v0, r0); + v_int8 v1 = vx_load(src + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v1, v1, r1); + } + s += v_reduce_sum(v_add(r0, r1)); +#endif + for (; j < n; j++) { + int v = saturate_cast(src[j]); + s += v * v; + } + return s; + } +}; + +#endif + +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) + +template<> +struct NormInf_SIMD { + double operator() (const double* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + v_float64 r2 = vx_setzero_f64(), r3 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r0 = v_max(r0, v_abs(vx_load(src + j ))); + r1 = v_max(r1, v_abs(vx_load(src + j + VTraits::vlanes()))); + r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + r0 = v_max(r0, v_max(r1, v_max(r2, r3))); + for (; j < n; j++) { + s = std::max(s, cv_abs(src[j])); + } + // [TODO]: use v_reduce_max when it supports float64 + double t[VTraits::max_nlanes]; + vx_store(t, r0); + for (int i = 0; i < VTraits::vlanes(); i++) { + s = std::max(s, cv_abs(t[i])); + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + double operator() (const int* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float32 v0 = v_abs(v_cvt_f32(vx_load(src + j))), v1 = v_abs(v_cvt_f32(vx_load(src + j + VTraits::vlanes()))); + r00 = v_add(r00, v_cvt_f64(v0)); r01 = v_add(r01, v_cvt_f64_high(v0)); + r10 = v_add(r10, v_cvt_f64(v1)); r11 = v_add(r11, v_cvt_f64_high(v1)); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); + for (; j < n; j++) { + s += cv_abs(src[j]); + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + double operator() (const float* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + v_float64 r20 = vx_setzero_f64(), r21 = vx_setzero_f64(); + v_float64 r30 = vx_setzero_f64(), r31 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_float32 v0 = v_abs(vx_load(src + j)), v1 = v_abs(vx_load(src + j + VTraits::vlanes())); + r00 = v_add(r00, v_cvt_f64(v0)); r01 = v_add(r01, v_cvt_f64_high(v0)); + r10 = v_add(r10, v_cvt_f64(v1)); r11 = v_add(r11, v_cvt_f64_high(v1)); + + v_float32 v2 = v_abs(vx_load(src + j + 2 * VTraits::vlanes())), v3 = v_abs(vx_load(src + j + 3 * VTraits::vlanes())); + r20 = v_add(r20, v_cvt_f64(v2)); r21 = v_add(r21, v_cvt_f64_high(v2)); + r30 = v_add(r30, v_cvt_f64(v3)); r31 = v_add(r31, v_cvt_f64_high(v3)); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); + s += v_reduce_sum(v_add(v_add(v_add(r20, r21), r30), r31)); + for (; j < n; j++) { + s += cv_abs(src[j]); + } + return s; + } +}; + +template<> +struct NormL1_SIMD { + double operator() (const double* src, int n) const { + int j = 0; + double s = 0.f; +#if CV_RVV // This is introduced to workaround the accuracy issue on ci + s = normL1_rvv(src, n, j); +#else + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + r00 = v_add(r00, v_abs(vx_load(src + j ))); + r01 = v_add(r01, v_abs(vx_load(src + j + VTraits::vlanes()))); + r10 = v_add(r10, v_abs(vx_load(src + j + 2 * VTraits::vlanes()))); + r11 = v_add(r11, v_abs(vx_load(src + j + 3 * VTraits::vlanes()))); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); +#endif + for (; j < n; j++) { + s += cv_abs(src[j]); + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + double operator() (const ushort* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_uint16 v0 = vx_load(src + j); + v_uint64 u0 = v_dotprod_expand_fast(v0, v0); + r0 = v_add(r0, v_cvt_f64(v_reinterpret_as_s64(u0))); + + v_uint16 v1 = vx_load(src + j + VTraits::vlanes()); + v_uint64 u1 = v_dotprod_expand_fast(v1, v1); + r1 = v_add(r1, v_cvt_f64(v_reinterpret_as_s64(u1))); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = saturate_cast(src[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + double operator() (const short* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int16 v0 = vx_load(src + j); + r0 = v_add(r0, v_cvt_f64(v_dotprod_expand_fast(v0, v0))); + + v_int16 v1 = vx_load(src + j + VTraits::vlanes()); + r1 = v_add(r1, v_cvt_f64(v_dotprod_expand_fast(v1, v1))); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = saturate_cast(src[j]); + s += v * v; + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + double operator() (const int* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r0 = vx_setzero_f64(), r1 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_int32 v0 = vx_load(src + j); + r0 = v_dotprod_expand_fast(v0, v0, r0); + + v_int32 v1 = vx_load(src + j + VTraits::vlanes()); + r1 = v_dotprod_expand_fast(v1, v1, r1); + } + s += v_reduce_sum(v_add(r0, r1)); + for (; j < n; j++) { + double v = src[j]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + double operator() (const float* src, int n) const { + int j = 0; + double s = 0.f; + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + for (; j <= n - 2 * VTraits::vlanes(); j += 2 * VTraits::vlanes()) { + v_float32 v0 = vx_load(src + j), v1 = vx_load(src + j + VTraits::vlanes()); + v_float64 v00 = v_cvt_f64(v0), v01 = v_cvt_f64_high(v0); + v_float64 v10 = v_cvt_f64(v1), v11 = v_cvt_f64_high(v1); + r00 = v_fma(v00, v00, r00); r01 = v_fma(v01, v01, r01); + r10 = v_fma(v10, v10, r10); r11 = v_fma(v11, v11, r11); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); + for (; j < n; j++) { + double v = src[j]; + s += v * v; + } + return s; + } +}; + +template<> +struct NormL2_SIMD { + double operator() (const double* src, int n) const { + int j = 0; + double s = 0.f; +#if CV_RVV // This is introduced to workaround the accuracy issue on ci + s = normL2_rvv(src, n, j); +#else + v_float64 r00 = vx_setzero_f64(), r01 = vx_setzero_f64(); + v_float64 r10 = vx_setzero_f64(), r11 = vx_setzero_f64(); + for (; j <= n - 4 * VTraits::vlanes(); j += 4 * VTraits::vlanes()) { + v_float64 v00 = vx_load(src + j ); + v_float64 v01 = vx_load(src + j + VTraits::vlanes()); + v_float64 v10 = vx_load(src + j + 2 * VTraits::vlanes()); + v_float64 v11 = vx_load(src + j + 3 * VTraits::vlanes()); + r00 = v_fma(v00, v00, r00); r01 = v_fma(v01, v01, r01); + r10 = v_fma(v10, v10, r10); r11 = v_fma(v11, v11, r11); + } + s += v_reduce_sum(v_add(v_add(v_add(r00, r01), r10), r11)); +#endif + for (; j < n; j++) { + double v = src[j]; + s += v * v; + } + return s; + } +}; + +#endif + +template int +normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormInf_SIMD op; + result = std::max(result, op(src, len*cn)); + } else { + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + result = std::max(result, ST(cv_abs(src[k]))); + } + } + } + } + *_result = result; + return 0; +} + +template int +normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormL1_SIMD op; + result += op(src, len*cn); + } else { + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + result += cv_abs(src[k]); + } + } + } + } + *_result = result; + return 0; +} + +template int +normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn) { + ST result = *_result; + if( !mask ) { + NormL2_SIMD op; + result += op(src, len*cn); + } else { + for( int i = 0; i < len; i++, src += cn ) { + if( mask[i] ) { + for( int k = 0; k < cn; k++ ) { + T v = src[k]; + result += (ST)v*v; + } + } + } + } + *_result = result; + return 0; +} + +#define CV_DEF_NORM_FUNC(L, suffix, type, ntype) \ + static int norm##L##_##suffix(const type* src, const uchar* mask, ntype* r, int len, int cn) \ +{ CV_INSTRUMENT_REGION(); return norm##L##_(src, mask, r, len, cn); } \ + +#define CV_DEF_NORM_ALL(suffix, type, inftype, l1type, l2type) \ + CV_DEF_NORM_FUNC(Inf, suffix, type, inftype) \ + CV_DEF_NORM_FUNC(L1, suffix, type, l1type) \ + CV_DEF_NORM_FUNC(L2, suffix, type, l2type) + +CV_DEF_NORM_ALL(8u, uchar, int, int, int) +CV_DEF_NORM_ALL(8s, schar, int, int, int) +CV_DEF_NORM_ALL(16u, ushort, int, int, double) +CV_DEF_NORM_ALL(16s, short, int, int, double) +CV_DEF_NORM_ALL(32s, int, int, double, double) +CV_DEF_NORM_ALL(32f, float, float, double, double) +CV_DEF_NORM_ALL(64f, double, double, double, double) + +NormFunc getNormFunc(int normType, int depth) +{ + CV_INSTRUMENT_REGION(); + static NormFunc normTab[3][8] = + { + { + (NormFunc)GET_OPTIMIZED(normInf_8u), (NormFunc)GET_OPTIMIZED(normInf_8s), (NormFunc)GET_OPTIMIZED(normInf_16u), (NormFunc)GET_OPTIMIZED(normInf_16s), + (NormFunc)GET_OPTIMIZED(normInf_32s), (NormFunc)GET_OPTIMIZED(normInf_32f), (NormFunc)normInf_64f, 0 + }, + { + (NormFunc)GET_OPTIMIZED(normL1_8u), (NormFunc)GET_OPTIMIZED(normL1_8s), (NormFunc)GET_OPTIMIZED(normL1_16u), (NormFunc)GET_OPTIMIZED(normL1_16s), + (NormFunc)GET_OPTIMIZED(normL1_32s), (NormFunc)GET_OPTIMIZED(normL1_32f), (NormFunc)normL1_64f, 0 + }, + { + (NormFunc)GET_OPTIMIZED(normL2_8u), (NormFunc)GET_OPTIMIZED(normL2_8s), (NormFunc)GET_OPTIMIZED(normL2_16u), (NormFunc)GET_OPTIMIZED(normL2_16s), + (NormFunc)GET_OPTIMIZED(normL2_32s), (NormFunc)GET_OPTIMIZED(normL2_32f), (NormFunc)normL2_64f, 0 + } + }; + + return normTab[normType][depth]; +} + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // cv:: From b5f5540e8ab5f94afc43e30b77f42afb90635fe4 Mon Sep 17 00:00:00 2001 From: Daniil Anufriev Date: Fri, 21 Feb 2025 17:36:54 +0300 Subject: [PATCH 06/19] Merge pull request #26886 from sk1er52:feature/exp64f MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable SIMD_SCALABLE for exp and sqrt #26886 ### 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 - [x] 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 ``` CPU - Banana Pi k1, compiler - clang 18.1.4 ``` ``` Geometric mean (ms) Name of Test baseline hal ui hal ui vs vs baseline baseline (x-factor) (x-factor) Exp::ExpFixture::(127x61, 32FC1) 0.358 -- 0.033 -- 10.70 Exp::ExpFixture::(640x480, 32FC1) 14.304 -- 1.167 -- 12.26 Exp::ExpFixture::(1280x720, 32FC1) 42.785 -- 3.538 -- 12.09 Exp::ExpFixture::(1920x1080, 32FC1) 96.206 -- 7.927 -- 12.14 Exp::ExpFixture::(127x61, 64FC1) 0.433 0.050 0.098 8.59 4.40 Exp::ExpFixture::(640x480, 64FC1) 17.315 1.935 3.813 8.95 4.54 Exp::ExpFixture::(1280x720, 64FC1) 52.181 5.877 11.519 8.88 4.53 Exp::ExpFixture::(1920x1080, 64FC1) 117.082 13.157 25.854 8.90 4.53 ``` Additionally, this PR brings Sqrt optimization with UI: ``` Geometric mean (ms) Name of Test baseline ui ui vs baseline (x-factor) Sqrt::SqrtFixture::(127x61, 5, false) 0.111 0.027 4.11 Sqrt::SqrtFixture::(127x61, 6, false) 0.149 0.053 2.82 Sqrt::SqrtFixture::(640x480, 5, false) 4.374 0.967 4.52 Sqrt::SqrtFixture::(640x480, 6, false) 5.885 2.046 2.88 Sqrt::SqrtFixture::(1280x720, 5, false) 12.960 2.915 4.45 Sqrt::SqrtFixture::(1280x720, 6, false) 17.648 6.107 2.89 Sqrt::SqrtFixture::(1920x1080, 5, false) 29.178 6.524 4.47 Sqrt::SqrtFixture::(1920x1080, 6, false) 39.709 13.670 2.90 ``` Reference Muller, J.-M. Elementary Functions: Algorithms and Implementation. 2nd ed. Boston: Birkhäuser, 2006. https://www.springer.com/gp/book/9780817643720 --- modules/core/perf/perf_arithm.cpp | 42 ++++++++++++++++++++---- modules/core/src/mathfuncs_core.simd.hpp | 8 ++--- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/modules/core/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index 6cc25a3476..b9decbfd4e 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -706,22 +706,27 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest, ) ); -typedef Size_MatType InvSqrtFixture; -PERF_TEST_P(InvSqrtFixture, InvSqrt, testing::Combine( - testing::Values(TYPICAL_MAT_SIZES), - testing::Values(CV_32FC1, CV_64FC1))) -{ +typedef perf::TestBaseWithParam> SqrtFixture; +PERF_TEST_P_(SqrtFixture, Sqrt) { Size sz = get<0>(GetParam()); int type = get<1>(GetParam()); + bool inverse = get<2>(GetParam()); Mat src(sz, type), dst(sz, type); randu(src, FLT_EPSILON, 1000); declare.in(src).out(dst); - TEST_CYCLE() cv::pow(src, -0.5, dst); + TEST_CYCLE() cv::pow(src, inverse ? -0.5 : 0.5, dst); SANITY_CHECK_NOTHING(); } +INSTANTIATE_TEST_CASE_P(/*nothing*/ , SqrtFixture, + testing::Combine( + testing::Values(TYPICAL_MAT_SIZES), + testing::Values(CV_32FC1, CV_64FC1), + testing::Bool() + ) +); ///////////// Rotate //////////////////////// @@ -815,4 +820,29 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , PatchNaNsFixture, ) ); +//////////////EXP//////////// + +typedef Size_MatType ExpFixture; + +PERF_TEST_P(ExpFixture, Exp, + testing::Combine(testing::Values(TYPICAL_MAT_SIZES), testing::Values(CV_32F, CV_64F))) +{ + cv::Size size = std::get<0>(GetParam()); + int type = std::get<1>(GetParam()); + + cv::Mat src(size, type); + cv::Mat dst(size, type); + + declare.in(src).out(dst); + + cv::randu(src, -5.0, 5.0); + + TEST_CYCLE() + { + cv::exp(src, dst); + } + + SANITY_CHECK_NOTHING(); +} + } // namespace diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index 3fa3cba1b8..f92234f140 100644 --- a/modules/core/src/mathfuncs_core.simd.hpp +++ b/modules/core/src/mathfuncs_core.simd.hpp @@ -396,7 +396,7 @@ void sqrt32f(const float* src, float* dst, int len) int i = 0; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); for( ; i < len; i += VECSZ*2 ) { @@ -425,7 +425,7 @@ void sqrt64f(const double* src, double* dst, int len) int i = 0; -#if CV_SIMD_64F +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) const int VECSZ = VTraits::vlanes(); for( ; i < len; i += VECSZ*2 ) { @@ -527,7 +527,7 @@ void exp32f( const float *_x, float *y, int n ) float maxval = (float)(exp_max_val/exp_prescale); float postscale = (float)exp_postscale; -#if CV_SIMD +#if (CV_SIMD || CV_SIMD_SCALABLE) const int VECSZ = VTraits::vlanes(); const v_float32 vprescale = vx_setall_f32((float)exp_prescale); const v_float32 vpostscale = vx_setall_f32((float)exp_postscale); @@ -641,7 +641,7 @@ void exp64f( const double *_x, double *y, int n ) double minval = (-exp_max_val/exp_prescale); double maxval = (exp_max_val/exp_prescale); -#if CV_SIMD_64F +#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F) const int VECSZ = VTraits::vlanes(); const v_float64 vprescale = vx_setall_f64(exp_prescale); const v_float64 vpostscale = vx_setall_f64(exp_postscale); From 6a6a5a765d68c40174f42cf9f68291b33d770226 Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts <147074368+GenshinImpactStarts@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:56:23 +0800 Subject: [PATCH 07/19] Merge pull request #26943 from GenshinImpactStarts:flip_hal_rvv Impl RISC-V HAL for cv::flip | Add perf test for flip #26943 Implement through the existing `cv_hal_flip` interfaces. Add perf test for `cv::flip`. The reason why select these args for testing: - **size**: copied from perf_lut - **type**: - U8C1: basic situation - U8C3: unaligned element size - U8C4: large element size Tested on - MUSE-PI (vlen=256) - Compiler: gcc 14.2 (riscv-collab/riscv-gnu-toolchain Nightly: December 16, 2024) ```sh $ opencv_test_core --gtest_filter="Core_Flip/ElemWiseTest.*" $ opencv_perf_core --gtest_filter="Size_MatType_FlipCode*" --perf_min_samples=300 --perf_force_samples=300 ``` ``` Geometric mean (ms) Name of Test scalar ui rvv ui rvv vs vs scalar scalar (x-factor) (x-factor) flip::Size_MatType_FlipCode::(320x240, 8UC1, FLIP_X) 0.026 0.033 0.031 0.81 0.84 flip::Size_MatType_FlipCode::(320x240, 8UC1, FLIP_XY) 0.206 0.212 0.091 0.97 2.26 flip::Size_MatType_FlipCode::(320x240, 8UC1, FLIP_Y) 0.185 0.189 0.082 0.98 2.25 flip::Size_MatType_FlipCode::(320x240, 8UC3, FLIP_X) 0.070 0.084 0.084 0.83 0.83 flip::Size_MatType_FlipCode::(320x240, 8UC3, FLIP_XY) 0.616 0.612 0.235 1.01 2.62 flip::Size_MatType_FlipCode::(320x240, 8UC3, FLIP_Y) 0.587 0.603 0.204 0.97 2.88 flip::Size_MatType_FlipCode::(320x240, 8UC4, FLIP_X) 0.263 0.110 0.109 2.40 2.41 flip::Size_MatType_FlipCode::(320x240, 8UC4, FLIP_XY) 0.930 0.831 0.316 1.12 2.95 flip::Size_MatType_FlipCode::(320x240, 8UC4, FLIP_Y) 1.175 1.129 0.313 1.04 3.75 flip::Size_MatType_FlipCode::(640x480, 8UC1, FLIP_X) 0.303 0.118 0.111 2.57 2.73 flip::Size_MatType_FlipCode::(640x480, 8UC1, FLIP_XY) 0.949 0.836 0.405 1.14 2.34 flip::Size_MatType_FlipCode::(640x480, 8UC1, FLIP_Y) 0.784 0.783 0.409 1.00 1.92 flip::Size_MatType_FlipCode::(640x480, 8UC3, FLIP_X) 1.084 0.360 0.355 3.01 3.06 flip::Size_MatType_FlipCode::(640x480, 8UC3, FLIP_XY) 3.768 3.348 1.364 1.13 2.76 flip::Size_MatType_FlipCode::(640x480, 8UC3, FLIP_Y) 4.361 4.473 1.296 0.97 3.37 flip::Size_MatType_FlipCode::(640x480, 8UC4, FLIP_X) 1.252 0.469 0.451 2.67 2.78 flip::Size_MatType_FlipCode::(640x480, 8UC4, FLIP_XY) 5.732 5.220 1.303 1.10 4.40 flip::Size_MatType_FlipCode::(640x480, 8UC4, FLIP_Y) 5.041 5.105 1.203 0.99 4.19 flip::Size_MatType_FlipCode::(1920x1080, 8UC1, FLIP_X) 2.382 0.903 0.903 2.64 2.64 flip::Size_MatType_FlipCode::(1920x1080, 8UC1, FLIP_XY) 8.606 7.508 2.581 1.15 3.33 flip::Size_MatType_FlipCode::(1920x1080, 8UC1, FLIP_Y) 8.421 8.535 2.219 0.99 3.80 flip::Size_MatType_FlipCode::(1920x1080, 8UC3, FLIP_X) 6.312 2.416 2.429 2.61 2.60 flip::Size_MatType_FlipCode::(1920x1080, 8UC3, FLIP_XY) 29.174 26.055 12.761 1.12 2.29 flip::Size_MatType_FlipCode::(1920x1080, 8UC3, FLIP_Y) 25.373 25.500 13.382 1.00 1.90 flip::Size_MatType_FlipCode::(1920x1080, 8UC4, FLIP_X) 7.620 3.204 3.115 2.38 2.45 flip::Size_MatType_FlipCode::(1920x1080, 8UC4, FLIP_XY) 32.876 29.310 12.976 1.12 2.53 flip::Size_MatType_FlipCode::(1920x1080, 8UC4, FLIP_Y) 28.831 29.094 14.919 0.99 1.93 ``` The optimization for vlen <= 256 and > 256 are different, but I have no real hardware with vlen > 256. So accuracy tests for that like 512 and 1024 are conducted on QEMU built from the `riscv-collab/riscv-gnu-toolchain`. ### 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 - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp | 248 ++++++++++++++++++++++++++ modules/core/perf/perf_flip.cpp | 45 +++++ 3 files changed, 294 insertions(+) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp create mode 100644 modules/core/perf/perf_flip.cpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 6f62d8471a..a32e0971b7 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -28,6 +28,7 @@ #include "hal_rvv_1p0/minmax.hpp" // core #include "hal_rvv_1p0/atan.hpp" // core #include "hal_rvv_1p0/split.hpp" // core +#include "hal_rvv_1p0/flip.hpp" // core #endif #endif diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp new file mode 100644 index 0000000000..fe5c5a8951 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/flip.hpp @@ -0,0 +1,248 @@ +// 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. +#pragma once + +#include +#include +#include + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_flip +#define cv_hal_flip cv::cv_hal_rvv::flip + +struct FlipVlen256 +{ + using TableElemType = uchar; + using TableType = vuint8m8_t; + + static inline size_t setvlmax() + { + return __riscv_vsetvlmax_e8m8(); + } + + static inline TableType vid(size_t vl) + { + return __riscv_vid_v_u8m8(vl); + } + + static inline TableType loadTable(const TableElemType* ptr, size_t vl) + { + return __riscv_vle8_v_u8m8(ptr, vl); + } + + static inline void gather(const uchar* src, TableType tab, uchar* dst, size_t vl) + { + auto v = __riscv_vle8_v_u8m8(src, vl); + __riscv_vse8(dst, __riscv_vrgather(v, tab, vl), vl); + } +}; + +struct FlipVlen512 +{ + using TableElemType = uint16_t; + using TableType = vuint16m8_t; + + static inline size_t setvlmax() + { + return __riscv_vsetvlmax_e8m4(); + } + + static inline TableType vid(size_t vl) + { + return __riscv_vid_v_u16m8(vl); + } + + static inline TableType loadTable(const TableElemType* ptr, size_t vl) + { + return __riscv_vle16_v_u16m8(ptr, vl); + } + + static inline void gather(const uchar* src, TableType tab, uchar* dst, size_t vl) + { + auto v = __riscv_vle8_v_u8m4(src, vl); + __riscv_vse8(dst, __riscv_vrgatherei16(v, tab, vl), vl); + } +}; + +template +inline void flipFillBuffer(cv::AutoBuffer& _buf, size_t len, int esz) +{ + T* buf = _buf.data(); + for (int i = (int)len - esz; i >= 0; i -= esz, buf += esz) + for (int j = 0; j < esz; j++) + buf[j] = (T)(i + j); +} + +inline void flipX(int esz, + const uchar* src_data, + size_t src_step, + int src_width, + int src_height, + uchar* dst_data, + size_t dst_step) +{ + size_t w = (size_t)src_width * esz; + auto src0 = src_data, src1 = src_data + src_step * (src_height - 1); + auto dst0 = dst_data, dst1 = dst_data + dst_step * (src_height - 1); + size_t vl; + for (src_height -= 2; src_height >= 0; + src_height -= 2, src0 += src_step, dst0 += dst_step, src1 -= src_step, dst1 -= dst_step) + { + for (size_t i = 0; i < w; i += vl) + { + vl = __riscv_vsetvl_e8m8(w - i); + __riscv_vse8(dst1 + i, __riscv_vle8_v_u8m8(src0 + i, vl), vl); + __riscv_vse8(dst0 + i, __riscv_vle8_v_u8m8(src1 + i, vl), vl); + } + } + if (src_height == -1) + { + for (size_t i = 0; i < w; i += (int)vl) + { + vl = __riscv_vsetvl_e8m8(w - i); + __riscv_vse8(dst0 + i, __riscv_vle8_v_u8m8(src1 + i, vl), vl); + } + } +} + +template +inline void flipY(int esz, + const uchar* src_data, + size_t src_step, + int src_width, + int src_height, + uchar* dst_data, + size_t dst_step) +{ + size_t w = (size_t)src_width * esz; + size_t vl = std::min(FlipVlen::setvlmax() / esz * esz, w); + typename FlipVlen::TableType tab_v; + if (esz == 1) + tab_v = __riscv_vrsub(FlipVlen::vid(vl), vl - 1, vl); + else + { + cv::AutoBuffer buf(vl); + flipFillBuffer(buf, vl, esz); + tab_v = FlipVlen::loadTable(buf.data(), vl); + } + if (vl == w) + for (; src_height; src_height--, src_data += src_step, dst_data += dst_step) + FlipVlen::gather(src_data, tab_v, dst_data, vl); + else + for (; src_height; src_height--, src_data += src_step, dst_data += dst_step) + { + auto src0 = src_data, src1 = src_data + w - vl; + auto dst0 = dst_data, dst1 = dst_data + w - vl; + for (; src0 < src1 + vl; src0 += vl, src1 -= vl, dst0 += vl, dst1 -= vl) + { + FlipVlen::gather(src0, tab_v, dst1, vl); + FlipVlen::gather(src1, tab_v, dst0, vl); + } + } +} + +template +inline void flipXY(int esz, + const uchar* src_data, + size_t src_step, + int src_width, + int src_height, + uchar* dst_data, + size_t dst_step) +{ + size_t w = (size_t)src_width * esz; + size_t vl = std::min(FlipVlen::setvlmax() / esz * esz, w); + typename FlipVlen::TableType tab_v; + if (esz == 1) + tab_v = __riscv_vrsub(FlipVlen::vid(vl), vl - 1, vl); + else + { + cv::AutoBuffer buf(vl); + flipFillBuffer(buf, vl, esz); + tab_v = FlipVlen::loadTable(buf.data(), vl); + } + auto src0 = src_data, src1 = src_data + src_step * (src_height - 1); + auto dst0 = dst_data, dst1 = dst_data + dst_step * (src_height - 1); + if (vl == w) + { + for (src_height -= 2; src_height >= 0; + src_height -= 2, + src0 += src_step, + dst0 += dst_step, + src1 -= src_step, + dst1 -= dst_step) + { + FlipVlen::gather(src0, tab_v, dst1, vl); + FlipVlen::gather(src1, tab_v, dst0, vl); + } + if (src_height == -1) + { + FlipVlen::gather(src1, tab_v, dst0, vl); + } + } + else + { + for (src_height -= 2; src_height >= 0; + src_height -= 2, + src0 += src_step, + dst0 += dst_step, + src1 -= src_step, + dst1 -= dst_step) + { + for (size_t i = 0; 2 * i < w; i += vl) + { + FlipVlen::gather(src0 + i, tab_v, dst1 + w - i - vl, vl); + FlipVlen::gather(src0 + w - i - vl, tab_v, dst1 + i, vl); + FlipVlen::gather(src1 + i, tab_v, dst0 + w - i - vl, vl); + FlipVlen::gather(src1 + w - i - vl, tab_v, dst0 + i, vl); + } + } + if (src_height == -1) + { + for (size_t i = 0; 2 * i < w; i += vl) + { + FlipVlen::gather(src1 + i, tab_v, dst0 + w - i - vl, vl); + FlipVlen::gather(src1 + w - i - vl, tab_v, dst0 + i, vl); + } + } + } +} + +inline int flip(int src_type, + const uchar* src_data, + size_t src_step, + int src_width, + int src_height, + uchar* dst_data, + size_t dst_step, + int flip_mode) +{ + if (src_width < 0 || src_height < 0 || src_data == dst_data) + return CV_HAL_ERROR_NOT_IMPLEMENTED; + + int esz = CV_ELEM_SIZE(src_type); + if (flip_mode == 0) + { + flipX(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + } + else if (flip_mode > 0) + { + if (__riscv_vlenb() * 8 <= 256) + flipY(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + else + flipY(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + } + else + { + if (__riscv_vlenb() * 8 <= 256) + flipXY(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + else + flipXY(esz, src_data, src_step, src_width, src_height, dst_data, dst_step); + } + + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv diff --git a/modules/core/perf/perf_flip.cpp b/modules/core/perf/perf_flip.cpp new file mode 100644 index 0000000000..2a24d394c0 --- /dev/null +++ b/modules/core/perf/perf_flip.cpp @@ -0,0 +1,45 @@ +// 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. + +#include "perf_precomp.hpp" + +namespace opencv_test { namespace { +using namespace perf; + +enum +{ + FLIP_XY = 0, + FLIP_X = 1, + FLIP_Y = 2, +}; + +#define FLIP_SIZES szQVGA, szVGA, sz1080p +#define FLIP_TYPES CV_8UC1, CV_8UC3, CV_8UC4 +#define FLIP_CODES FLIP_X, FLIP_Y, FLIP_XY + +CV_FLAGS(FlipCode, FLIP_X, FLIP_Y, FLIP_XY); +typedef tuple Size_MatType_FlipCode_t; +typedef perf::TestBaseWithParam Size_MatType_FlipCode; + +PERF_TEST_P(Size_MatType_FlipCode, + flip, + testing::Combine(testing::Values(FLIP_SIZES), + testing::Values(FLIP_TYPES), + testing::Values(FLIP_CODES))) +{ + Size sz = get<0>(GetParam()); + int matType = get<1>(GetParam()); + int flipCode = get<2>(GetParam()) - 1; + + Mat src(sz, matType); + Mat dst(sz, matType); + + declare.in(src, WARMUP_RNG).out(dst); + + TEST_CYCLE() cv::flip(src, dst, flipCode); + + SANITY_CHECK_NOTHING(); +} + +}} // namespace opencv_test From 33d632f85e4c1cc4d70bc7210c2f126fa8a4b0bb Mon Sep 17 00:00:00 2001 From: GenshinImpactStarts Date: Thu, 13 Feb 2025 14:16:44 +0000 Subject: [PATCH 08/19] impl hal_rvv norm_hamming Co-authored-by: Liutong HAN --- 3rdparty/hal_rvv/hal_rvv.hpp | 1 + 3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp | 182 ++++++++++++++++++ modules/core/src/norm.dispatch.cpp | 2 +- 3 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index a32e0971b7..f09e5aca7a 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -24,6 +24,7 @@ #include "hal_rvv_1p0/mean.hpp" // core #include "hal_rvv_1p0/norm.hpp" // core #include "hal_rvv_1p0/norm_diff.hpp" // core +#include "hal_rvv_1p0/norm_hamming.hpp" // core #include "hal_rvv_1p0/convert_scale.hpp" // core #include "hal_rvv_1p0/minmax.hpp" // core #include "hal_rvv_1p0/atan.hpp" // core diff --git a/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp b/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp new file mode 100644 index 0000000000..4fa2fe5da3 --- /dev/null +++ b/3rdparty/hal_rvv/hal_rvv_1p0/norm_hamming.hpp @@ -0,0 +1,182 @@ +// 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. +#pragma once + +#include +#include + +namespace cv { namespace cv_hal_rvv { + +#undef cv_hal_normHamming8u +#define cv_hal_normHamming8u cv::cv_hal_rvv::normHamming8u +#undef cv_hal_normHammingDiff8u +#define cv_hal_normHammingDiff8u cv::cv_hal_rvv::normHammingDiff8u + +template +inline void normHammingCnt_m8(vuint8m8_t v, vbool1_t mask, size_t len_bool, size_t& result) +{ + auto v_bool0 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 0)); + auto v_bool1 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 1)); + auto v_bool2 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 2)); + auto v_bool3 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 3)); + auto v_bool4 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 4)); + auto v_bool5 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 5)); + auto v_bool6 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 6)); + auto v_bool7 = __riscv_vreinterpret_b1(__riscv_vget_u8m1(v, 7)); + result += CellType::popcount(v_bool0, mask, len_bool); + result += CellType::popcount(v_bool1, mask, len_bool); + result += CellType::popcount(v_bool2, mask, len_bool); + result += CellType::popcount(v_bool3, mask, len_bool); + result += CellType::popcount(v_bool4, mask, len_bool); + result += CellType::popcount(v_bool5, mask, len_bool); + result += CellType::popcount(v_bool6, mask, len_bool); + result += CellType::popcount(v_bool7, mask, len_bool); +} + +template +inline void normHammingCnt_m1(vuint8m1_t v, vbool1_t mask, size_t len_bool, size_t& result) +{ + auto v_bool = __riscv_vreinterpret_b1(v); + result += CellType::popcount(v_bool, mask, len_bool); +} + +struct NormHammingCell1 +{ + static inline vbool1_t generateMask([[maybe_unused]] size_t len) + { + return vbool1_t(); + } + + template + static inline void preprocess([[maybe_unused]] T& v, [[maybe_unused]] size_t len) + { + } + + template + static inline size_t popcount(T v, [[maybe_unused]] vbool1_t mask, size_t len_bool) + { + return __riscv_vcpop(v, len_bool); + } +}; + +struct NormHammingCell2 +{ + static inline vbool1_t generateMask(size_t len) + { + return __riscv_vreinterpret_b1(__riscv_vmv_v_x_u8m1(0x55, len)); + } + + template + static inline void preprocess(T& v, size_t len) + { + v = __riscv_vor(v, __riscv_vsrl(v, 1, len), len); + } + + template + static inline size_t popcount(T v, vbool1_t mask, size_t len_bool) + { + return __riscv_vcpop(mask, v, len_bool); + } +}; + +struct NormHammingCell4 +{ + static inline vbool1_t generateMask(size_t len) + { + return __riscv_vreinterpret_b1(__riscv_vmv_v_x_u8m1(0x11, len)); + } + + template + static inline void preprocess(T& v, size_t len) + { + v = __riscv_vor(v, __riscv_vsrl(v, 2, len), len); + v = __riscv_vor(v, __riscv_vsrl(v, 1, len), len); + } + + template + static inline size_t popcount(T v, vbool1_t mask, size_t len_bool) + { + return __riscv_vcpop(mask, v, len_bool); + } +}; + +template +inline void normHamming8uLoop(const uchar* a, size_t n, size_t& result) +{ + size_t len = __riscv_vsetvlmax_e8m8(); + size_t len_bool = len * 8; + vbool1_t mask = CellType::generateMask(len); + + for (; n >= len; n -= len, a += len) + { + auto v = __riscv_vle8_v_u8m8(a, len); + CellType::preprocess(v, len); + normHammingCnt_m8(v, mask, len_bool, result); + } + for (; n > 0; n -= len, a += len) + { + len = __riscv_vsetvl_e8m1(n); + auto v = __riscv_vle8_v_u8m1(a, len); + CellType::preprocess(v, len); + normHammingCnt_m1(v, mask, len * 8, result); + } +} + +template +inline void normHammingDiff8uLoop(const uchar* a, const uchar* b, size_t n, size_t& result) +{ + size_t len = __riscv_vsetvlmax_e8m8(); + size_t len_bool = len * 8; + vbool1_t mask = CellType::generateMask(len); + + for (; n >= len; n -= len, a += len, b += len) + { + auto v_a = __riscv_vle8_v_u8m8(a, len); + auto v_b = __riscv_vle8_v_u8m8(b, len); + auto v = __riscv_vxor(v_a, v_b, len); + CellType::preprocess(v, len); + normHammingCnt_m8(v, mask, len_bool, result); + } + for (; n > 0; n -= len, a += len, b += len) + { + len = __riscv_vsetvl_e8m1(n); + auto v_a = __riscv_vle8_v_u8m1(a, len); + auto v_b = __riscv_vle8_v_u8m1(b, len); + auto v = __riscv_vxor(v_a, v_b, len); + CellType::preprocess(v, len); + normHammingCnt_m1(v, mask, len * 8, result); + } +} + +inline int normHamming8u(const uchar* a, int n, int cellSize, int* result) +{ + size_t _result = 0; + + switch (cellSize) + { + case 1: normHamming8uLoop(a, n, _result); break; + case 2: normHamming8uLoop(a, n, _result); break; + case 4: normHamming8uLoop(a, n, _result); break; + default: return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + *result = static_cast(_result); + return CV_HAL_ERROR_OK; +} + +inline int normHammingDiff8u(const uchar* a, const uchar* b, int n, int cellSize, int* result) +{ + size_t _result = 0; + + switch (cellSize) + { + case 1: normHammingDiff8uLoop(a, b, n, _result); break; + case 2: normHammingDiff8uLoop(a, b, n, _result); break; + case 4: normHammingDiff8uLoop(a, b, n, _result); break; + default: return CV_HAL_ERROR_NOT_IMPLEMENTED; + } + *result = static_cast(_result); + return CV_HAL_ERROR_OK; +} + +}} // namespace cv::cv_hal_rvv diff --git a/modules/core/src/norm.dispatch.cpp b/modules/core/src/norm.dispatch.cpp index e43e33c92e..a67df07eba 100644 --- a/modules/core/src/norm.dispatch.cpp +++ b/modules/core/src/norm.dispatch.cpp @@ -586,7 +586,7 @@ double norm( InputArray _src, int normType, InputArray _mask ) if( normType == NORM_HAMMING ) { - return hal::normHamming(data, (int)len); + return hal::normHamming(data, (int)len, 1); } if( normType == NORM_HAMMING2 ) From 39bc5df72ab6ccbb3a86c28e6da62f77525742b1 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Tue, 25 Feb 2025 15:24:25 +0300 Subject: [PATCH 09/19] Merge pull request #26973 from sturkmen72:png_test Add a test related IMWRITE_PNG_COMPRESSION parameter #26973 ### 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. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/imgcodecs/test/test_png.cpp | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index 4cf0e0cd99..2eb8a82a96 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -327,6 +327,38 @@ const string pngsuite_files_corrupted[] = { INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted, testing::ValuesIn(pngsuite_files_corrupted)); +typedef testing::TestWithParam> Imgcodecs_Png_ImwriteFlags; + +TEST_P(Imgcodecs_Png_ImwriteFlags, compression_level) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + get<0>(GetParam()); + + const int compression_level = get<1>(GetParam()); + const size_t compression_level_output_size = get<2>(GetParam()); + + Mat src = imread(filename, IMREAD_UNCHANGED); + EXPECT_FALSE(src.empty()) << "Cannot read test image " << filename; + + vector buf; + imencode(".png", src, buf, { IMWRITE_PNG_COMPRESSION, compression_level }); + EXPECT_EQ(buf.size(), compression_level_output_size); +} + +INSTANTIATE_TEST_CASE_P(/**/, + Imgcodecs_Png_ImwriteFlags, + testing::Values( + make_tuple("../perf/512x512.png", 0, 788279), + make_tuple("../perf/512x512.png", 1, 179503), + make_tuple("../perf/512x512.png", 2, 176007), + make_tuple("../perf/512x512.png", 3, 170497), + make_tuple("../perf/512x512.png", 4, 163357), + make_tuple("../perf/512x512.png", 5, 159190), + make_tuple("../perf/512x512.png", 6, 156621), + make_tuple("../perf/512x512.png", 7, 155696), + make_tuple("../perf/512x512.png", 8, 153708), + make_tuple("../perf/512x512.png", 9, 152181))); + #endif // HAVE_PNG }} // namespace From 43551b72d709d6487317f2b936e54fe67be8fcce Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 26 Feb 2025 14:04:37 +0300 Subject: [PATCH 10/19] Merge pull request #26948 from mshabunin:fix-videoio-test-params videoio: print test params instead of indexes #26948 _videoio_ test names changed - use string instead of index. E.g. `videoio_read.threads/0` is now `videoio_read.threads/h264_0_RAW`. It allows to filter tests independently of the platform. **Notes:** - not all tests has been updated - only simpler ones and those which have varying parameters depending on platform --- modules/videoio/test/test_audio.cpp | 22 ++++- modules/videoio/test/test_ffmpeg.cpp | 45 +++++++++- modules/videoio/test/test_gstreamer.cpp | 9 +- modules/videoio/test/test_mfx.cpp | 5 +- modules/videoio/test/test_orientation.cpp | 9 +- modules/videoio/test/test_precomp.hpp | 34 +++++++- modules/videoio/test/test_video_io.cpp | 101 ++++++++++++++++++++-- 7 files changed, 205 insertions(+), 20 deletions(-) diff --git a/modules/videoio/test/test_audio.cpp b/modules/videoio/test/test_audio.cpp index bf2b4bad68..e3041e6eaf 100644 --- a/modules/videoio/test/test_audio.cpp +++ b/modules/videoio/test/test_audio.cpp @@ -135,7 +135,16 @@ TEST_P(Audio, audio) doTest(); } -INSTANTIATE_TEST_CASE_P(/**/, Audio, testing::ValuesIn(audioParams)); +inline static std::string Audio_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream out; + out << getExtensionSafe(get<0>(info.param)) << "_" + << get<1>(info.param) << "CN" << "_" + << getBackendNameSafe(get<4>(info.param)); + return out.str(); +} + +INSTANTIATE_TEST_CASE_P(/**/, Audio, testing::ValuesIn(audioParams), Audio_name_printer); class MediaTestFixture : public AudioBaseTest, public testing::TestWithParam { @@ -283,7 +292,16 @@ const paramCombination mediaParams[] = #endif // _WIN32 }; -INSTANTIATE_TEST_CASE_P(/**/, Media, testing::ValuesIn(mediaParams)); +inline static std::string Media_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream out; + out << getExtensionSafe(get<0>(info.param)) << "_" + << get<1>(info.param) << "CN" << "_" + << getBackendNameSafe(get<10>(info.param)); + return out.str(); +} + +INSTANTIATE_TEST_CASE_P(/**/, Media, testing::ValuesIn(mediaParams), Media_name_printer); TEST(AudioOpenCheck, bad_arg_invalid_audio_stream) { diff --git a/modules/videoio/test/test_ffmpeg.cpp b/modules/videoio/test/test_ffmpeg.cpp index 588b286ec1..03e41bd8f0 100644 --- a/modules/videoio/test/test_ffmpeg.cpp +++ b/modules/videoio/test/test_ffmpeg.cpp @@ -78,7 +78,16 @@ const FourCC_Ext_Size entries[] = make_tuple("FFV1", "mkv", bigSize) }; -INSTANTIATE_TEST_CASE_P(videoio, videoio_ffmpeg, testing::ValuesIn(entries)); +inline static std::string videoio_ffmpeg_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + const string & fourcc = get<0>(info.param); + const Size sz = get<2>(info.param); + os << (fourcc.size() == 0 ? "NONE" : fourcc) << "_" << get<1>(info.param) << "_" << sz.height << "p"; + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, videoio_ffmpeg, testing::ValuesIn(entries), videoio_ffmpeg_name_printer); //========================================================================== @@ -143,9 +152,19 @@ const videoio_read_params_t videoio_read_params[] = //videoio_read_params_t("video/big_buck_bunny.wmv", 125, true), }; +inline static std::string videoio_read_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream out; + out << getExtensionSafe(get<0>(get<0>(info.param))) << "_" + << get<1>(info.param) << "_" + << (get<2>(info.param) ? "RAW" : "ENC"); + return out.str(); +} + INSTANTIATE_TEST_CASE_P(/**/, videoio_read, testing::Combine(testing::ValuesIn(videoio_read_params), testing::Values(0, 1, 2, 50), - testing::Values(true, false))); + testing::Values(true, false)), + videoio_read_name_printer); //========================================================================== @@ -720,7 +739,15 @@ const ffmpeg_get_fourcc_param_t ffmpeg_get_fourcc_param[] = ffmpeg_get_fourcc_param_t("video/sample_322x242_15frames.yuv420p.libx264.mp4", "h264") }; -INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_get_fourcc, testing::ValuesIn(ffmpeg_get_fourcc_param)); +inline static std::string ffmpeg_get_fourcc_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + const string & fourcc = get<1>(info.param); + os << info.index << "_" << (fourcc.size() == 0 ? "NONE" : fourcc); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, ffmpeg_get_fourcc, testing::ValuesIn(ffmpeg_get_fourcc_param), ffmpeg_get_fourcc_name_printer); static void ffmpeg_check_read_raw(VideoCapture& cap) { @@ -914,6 +941,16 @@ const FourCC_Ext_Color_Support sixteen_bit_modes[] = }; -INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_16bit, testing::ValuesIn(sixteen_bit_modes)); +inline static std::string videoio_ffmpeg_16bit_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << get<0>(info.param) << "_" + << get<1>(info.param) << "_" + << (get<2>(info.param) ? "COLOR" : "GRAY") << "_" + << (get<3>(info.param) ? "SUP" : "NOT"); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_16bit, testing::ValuesIn(sixteen_bit_modes), videoio_ffmpeg_16bit_name_printer); }} // namespace diff --git a/modules/videoio/test/test_gstreamer.cpp b/modules/videoio/test/test_gstreamer.cpp index ef9a6765a8..2ac06b6a20 100644 --- a/modules/videoio/test/test_gstreamer.cpp +++ b/modules/videoio/test/test_gstreamer.cpp @@ -218,7 +218,14 @@ static const string bunny_params[] = { string("mjpg.avi") }; -INSTANTIATE_TEST_CASE_P(videoio, gstreamer_bunny, testing::ValuesIn(bunny_params)); +inline static std::string gstreamer_bunny_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream out; + out << extToStringSafe(info.param); + return out.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, gstreamer_bunny, testing::ValuesIn(bunny_params), gstreamer_bunny_name_printer); }} // namespace diff --git a/modules/videoio/test/test_mfx.cpp b/modules/videoio/test/test_mfx.cpp index 032d5a96e0..c244d1c853 100644 --- a/modules/videoio/test/test_mfx.cpp +++ b/modules/videoio/test/test_mfx.cpp @@ -160,8 +160,9 @@ inline static std::string videoio_mfx_name_printer(const testing::TestParamInfo< { std::ostringstream out; const Size sz = get<0>(info.param); - const std::string ext = get<2>(info.param); - out << sz.width << "x" << sz.height << "x" << get<1>(info.param) << "x" << ext.substr(1, ext.size() - 1); + out << sz.height << "p" << "_" + << get<1>(info.param) << "FPS" << "_" + << extToStringSafe(get<2>(info.param)); return out.str(); } diff --git a/modules/videoio/test/test_orientation.cpp b/modules/videoio/test/test_orientation.cpp index ccff7391b0..7ebabc02b2 100644 --- a/modules/videoio/test/test_orientation.cpp +++ b/modules/videoio/test/test_orientation.cpp @@ -80,7 +80,14 @@ static cv::VideoCaptureAPIs supported_backends[] = { CAP_FFMPEG }; -INSTANTIATE_TEST_CASE_P(videoio, VideoCaptureAPITests, testing::ValuesIn(supported_backends)); +inline static std::string VideoCaptureAPITests_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream out; + out << getBackendNameSafe(info.param); + return out.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, VideoCaptureAPITests, testing::ValuesIn(supported_backends), VideoCaptureAPITests_name_printer); #endif // WIN32 diff --git a/modules/videoio/test/test_precomp.hpp b/modules/videoio/test/test_precomp.hpp index b17a2f0ab7..205d8f92bd 100644 --- a/modules/videoio/test/test_precomp.hpp +++ b/modules/videoio/test/test_precomp.hpp @@ -63,7 +63,8 @@ inline std::string fourccToStringSafe(int fourcc) { std::string res = fourccToString(fourcc); // TODO: return hex values for invalid characters - std::transform(res.begin(), res.end(), res.begin(), [](char c) -> char { return (c >= '0' && c <= 'z') ? c : (c == ' ' ? '_' : 'x'); }); + std::transform(res.begin(), res.end(), res.begin(), + [](char c) -> char { return (c >= '0' && c <= 'z') ? c : (c == ' ' ? '_' : 'x'); }); return res; } @@ -73,6 +74,37 @@ inline int fourccFromString(const std::string &fourcc) return cv::VideoWriter::fourcc(fourcc[0], fourcc[1], fourcc[2], fourcc[3]); } +inline std::string extToStringSafe(const std::string & ext) +{ + std::string res; + const bool start_with_dot = (ext.size() > 0) && (ext[0] == '.'); + std::transform(start_with_dot ? ext.begin() + 1 : ext.begin(), ext.end(), std::back_inserter(res), + [](char c) -> char { return (c >= '0' && c <= 'z') ? c : ((c == ' ' || c == '.') ? '_' : 'x'); }); + return res; +} + +inline std::string getExtensionSafe(const std::string & fname) +{ + std::string fext(std::find(fname.begin(), fname.end(), '.'), fname.end()); + if (fext.size() == 0) + return std::string("NOEXT"); + else + return extToStringSafe(fext); +} + +inline std::string getBackendNameSafe(const cv::VideoCaptureAPIs & api) +{ + const std::string res = cv::videoio_registry::getBackendName(api); + if (res.substr(0, 7) == "Unknown") + { + std::ostringstream os; os << "BACKEND_" << (size_t)api; return os.str(); + } + else + { + return res; + } +} + inline void generateFrame(int i, int FRAME_COUNT, cv::Mat & frame) { using namespace cv; diff --git a/modules/videoio/test/test_video_io.cpp b/modules/videoio/test/test_video_io.cpp index f3c0862999..03d69dfe5b 100644 --- a/modules/videoio/test/test_video_io.cpp +++ b/modules/videoio/test/test_video_io.cpp @@ -383,10 +383,20 @@ TEST_P(videoio_bunny, frame_count) { doFrameCountTest(); } TEST_P(videoio_bunny, frame_timestamp) { doTimestampTest(); } +inline static std::string videoio_bunny_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << extToStringSafe(get<0>(info.param)) << "_" + << getBackendNameSafe(get<1>(info.param)); + return os.str(); +} + INSTANTIATE_TEST_CASE_P(videoio, videoio_bunny, testing::Combine( testing::ValuesIn(bunny_params), - testing::ValuesIn(backend_params))); + testing::ValuesIn(backend_params)), + videoio_bunny_name_printer); + inline static std::ostream &operator<<(std::ostream &out, const Ext_Fourcc_PSNR &p) @@ -446,10 +456,23 @@ Size all_sizes[] = { TEST_P(videoio_synthetic, write_read_position) { doTest(); } +inline static std::string videoio_synthetic_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + const Size sz = get<0>(info.param); + const Ext_Fourcc_PSNR & param = get<1>(info.param); + os << sz.height << "p" << "_" + << param.ext << "_" + << param.fourcc << "_" + << getBackendNameSafe(param.api); + return os.str(); +} + INSTANTIATE_TEST_CASE_P(videoio, videoio_synthetic, testing::Combine( testing::ValuesIn(all_sizes), - testing::ValuesIn(synthetic_params))); + testing::ValuesIn(synthetic_params)), + videoio_synthetic_name_printer); struct Ext_Fourcc_API { @@ -531,7 +554,19 @@ static vector generate_Ext_Fourcc_API() return result; } -INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer, testing::ValuesIn(generate_Ext_Fourcc_API())); +inline static std::string Videoio_Writer_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + std::string ext(info.param.ext); + if (info.param.api == CAP_GSTREAMER && info.param.fourcc[0] == '\0') // gstreamer pipeline instead of extension + os << getExtensionSafe(info.param.ext) << "_" << "NONE"; + else + os << info.param.ext << "_" << info.param.fourcc; + os << "_" << getBackendNameSafe(info.param.api); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer, testing::ValuesIn(generate_Ext_Fourcc_API()), Videoio_Writer_name_printer); TEST(Videoio, exceptions) @@ -610,7 +645,7 @@ static vector generate_Ext_Fourcc_API_nocrash() return result; } -INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer_bad_fourcc, testing::ValuesIn(generate_Ext_Fourcc_API_nocrash())); +INSTANTIATE_TEST_CASE_P(videoio, Videoio_Writer_bad_fourcc, testing::ValuesIn(generate_Ext_Fourcc_API_nocrash()), Videoio_Writer_name_printer); typedef testing::TestWithParam safe_capture; @@ -643,7 +678,15 @@ TEST_P(safe_capture, frames_independency) } static VideoCaptureAPIs safe_apis[] = {CAP_FFMPEG, CAP_GSTREAMER, CAP_MSMF,CAP_AVFOUNDATION}; -INSTANTIATE_TEST_CASE_P(videoio, safe_capture, testing::ValuesIn(safe_apis)); + +inline static std::string safe_capture_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << getBackendNameSafe(info.param); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, safe_capture, testing::ValuesIn(safe_apis), safe_capture_name_printer); //================================================================================================== // TEST_P(videocapture_acceleration, ...) @@ -836,12 +879,22 @@ static bool hw_use_umat[] = { true }; +inline static std::string videocapture_acceleration_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << getExtensionSafe(get<0>(info.param).filename) << "_" + << getBackendNameSafe(get<1>(info.param)) << "_" + << get<2>(info.param) << "_" + << (get<3>(info.param) ? "UMAT" : "MAT"); + return os.str(); +} + INSTANTIATE_TEST_CASE_P(videoio, videocapture_acceleration, testing::Combine( testing::ValuesIn(hw_filename), testing::ValuesIn(hw_backends), testing::ValuesIn(hw_types), testing::ValuesIn(hw_use_umat) -)); +), videocapture_acceleration_name_printer); ////////////////////////////////////////// TEST_P(video_acceleration, write_read) @@ -1019,11 +1072,23 @@ static Ext_Fourcc_PSNR hw_codecs[] = { #endif }; +inline static std::string videowriter_acceleration_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + const Ext_Fourcc_PSNR & param = get<0>(info.param); + os << extToStringSafe(param.ext) << "_" + << param.fourcc << "_" + << getBackendNameSafe(param.api) << "_" + << get<1>(info.param) << "_" + << (get<2>(info.param) ? "UMAT" : "MAT"); + return os.str(); +} + INSTANTIATE_TEST_CASE_P(videoio, videowriter_acceleration, testing::Combine( testing::ValuesIn(hw_codecs), testing::ValuesIn(hw_types), testing::ValuesIn(hw_use_umat) -)); +), videowriter_acceleration_name_printer); class BufferStream : public cv::IStreamReader { @@ -1094,10 +1159,20 @@ TEST_P(stream_capture, read) for(int i = 0; i < numFrames; i++) EXPECT_EQ(0, cv::norm(frames[i], hardCopies[i], NORM_INF)) << i; } + +inline static std::string stream_capture_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << extToStringSafe(get<0>(info.param)) << "_" + << getBackendNameSafe(get<1>(info.param)); + return os.str(); +} + INSTANTIATE_TEST_CASE_P(videoio, stream_capture, testing::Combine( testing::ValuesIn(bunny_params), - testing::ValuesIn(backend_params))); + testing::ValuesIn(backend_params)), + stream_capture_name_printer); // This test for stream input for container format (See test_ffmpeg/videoio_container.read test) typedef testing::TestWithParam stream_capture_ffmpeg; @@ -1152,6 +1227,14 @@ TEST_P(stream_capture_ffmpeg, raw) EXPECT_EQ(0, cv::norm(frame, frameRef, NORM_INF)) << i; } } -INSTANTIATE_TEST_CASE_P(videoio, stream_capture_ffmpeg, testing::Values("h264", "h265", "mjpg.avi")); + +inline static std::string stream_capture_ffmpeg_name_printer(const testing::TestParamInfo& info) +{ + std::ostringstream os; + os << extToStringSafe(info.param); + return os.str(); +} + +INSTANTIATE_TEST_CASE_P(videoio, stream_capture_ffmpeg, testing::Values("h264", "h265", "mjpg.avi"), stream_capture_ffmpeg_name_printer); } // namespace From d3792dad8664bd134f60786c1c7c111cd9bf1623 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Wed, 26 Feb 2025 17:11:31 +0300 Subject: [PATCH 11/19] Backported some C API cleanup from 5.x to 4.x to reduce conflicts in 4.x->5.x merge. --- modules/highgui/src/window_cocoa.mm | 44 +++++++++++++-------------- modules/highgui/src/window_gtk.cpp | 42 +++++++++++++------------- modules/highgui/src/window_w32.cpp | 46 ++++++++++++++--------------- modules/videoio/src/cap_dshow.cpp | 6 ++-- 4 files changed, 69 insertions(+), 69 deletions(-) diff --git a/modules/highgui/src/window_cocoa.mm b/modules/highgui/src/window_cocoa.mm index 71b5c6fb9a..f14b45dab1 100644 --- a/modules/highgui/src/window_cocoa.mm +++ b/modules/highgui/src/window_cocoa.mm @@ -858,8 +858,8 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { mp.x = int(event.scrollingDeltaX / 0.100006); mp.y = int(event.scrollingDeltaY / 0.100006); } - if( mp.x && !mp.y && CV_EVENT_MOUSEWHEEL == type ) { - type = CV_EVENT_MOUSEHWHEEL; + if( mp.x && !mp.y && cv::EVENT_MOUSEWHEEL == type ) { + type = cv::EVENT_MOUSEHWHEEL; } mouseCallback(type, mp.x, mp.y, flags, mouseParam); } else if( mp.x >= 0 && mp.y >= 0 && mp.x < imageSize.width && mp.y < imageSize.height ) { @@ -872,32 +872,32 @@ static NSSize constrainAspectRatio(NSSize base, NSSize constraint) { return; int flags = 0; - if([event modifierFlags] & NSShiftKeyMask) flags |= CV_EVENT_FLAG_SHIFTKEY; - if([event modifierFlags] & NSControlKeyMask) flags |= CV_EVENT_FLAG_CTRLKEY; - if([event modifierFlags] & NSAlternateKeyMask) flags |= CV_EVENT_FLAG_ALTKEY; + if([event modifierFlags] & NSShiftKeyMask) flags |= cv::EVENT_FLAG_SHIFTKEY; + if([event modifierFlags] & NSControlKeyMask) flags |= cv::EVENT_FLAG_CTRLKEY; + if([event modifierFlags] & NSAlternateKeyMask) flags |= cv::EVENT_FLAG_ALTKEY; //modified code using ternary operator: if ([event type] == NSLeftMouseDown) { [self cvSendMouseEvent:event - type:([event modifierFlags] & NSControlKeyMask) ? CV_EVENT_RBUTTONDOWN : CV_EVENT_LBUTTONDOWN - flags:flags | (([event modifierFlags] & NSControlKeyMask) ? CV_EVENT_FLAG_RBUTTON : CV_EVENT_FLAG_LBUTTON)]; -} + type:([event modifierFlags] & NSControlKeyMask) ? cv::EVENT_RBUTTONDOWN : cv::EVENT_LBUTTONDOWN + flags:flags | (([event modifierFlags] & NSControlKeyMask) ? cv::EVENT_FLAG_RBUTTON : cv::EVENT_FLAG_LBUTTON)]; + } -if ([event type] == NSLeftMouseUp) { - [self cvSendMouseEvent:event - type:([event modifierFlags] & NSControlKeyMask) ? CV_EVENT_RBUTTONUP : CV_EVENT_LBUTTONUP - flags:flags | (([event modifierFlags] & NSControlKeyMask) ? CV_EVENT_FLAG_RBUTTON : CV_EVENT_FLAG_LBUTTON)]; -} + if ([event type] == NSLeftMouseUp) { + [self cvSendMouseEvent:event + type:([event modifierFlags] & NSControlKeyMask) ? cv::EVENT_RBUTTONUP : cv::EVENT_LBUTTONUP + flags:flags | (([event modifierFlags] & NSControlKeyMask) ? cv::EVENT_FLAG_RBUTTON : cv::EVENT_FLAG_LBUTTON)]; + } - if([event type] == NSRightMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONDOWN flags:flags | CV_EVENT_FLAG_RBUTTON];} - if([event type] == NSRightMouseUp) {[self cvSendMouseEvent:event type:CV_EVENT_RBUTTONUP flags:flags | CV_EVENT_FLAG_RBUTTON];} - if([event type] == NSOtherMouseDown){[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONDOWN flags:flags];} - if([event type] == NSOtherMouseUp) {[self cvSendMouseEvent:event type:CV_EVENT_MBUTTONUP flags:flags];} - if([event type] == NSMouseMoved) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE flags:flags];} - if([event type] == NSLeftMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE flags:flags | CV_EVENT_FLAG_LBUTTON];} - if([event type] == NSRightMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE flags:flags | CV_EVENT_FLAG_RBUTTON];} - if([event type] == NSOtherMouseDragged) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEMOVE flags:flags | CV_EVENT_FLAG_MBUTTON];} - if([event type] == NSEventTypeScrollWheel) {[self cvSendMouseEvent:event type:CV_EVENT_MOUSEWHEEL flags:flags ];} + if([event type] == NSRightMouseDown){[self cvSendMouseEvent:event type:cv::EVENT_RBUTTONDOWN flags:flags | cv::EVENT_FLAG_RBUTTON];} + if([event type] == NSRightMouseUp) {[self cvSendMouseEvent:event type:cv::EVENT_RBUTTONUP flags:flags | cv::EVENT_FLAG_RBUTTON];} + if([event type] == NSOtherMouseDown){[self cvSendMouseEvent:event type:cv::EVENT_MBUTTONDOWN flags:flags];} + if([event type] == NSOtherMouseUp) {[self cvSendMouseEvent:event type:cv::EVENT_MBUTTONUP flags:flags];} + if([event type] == NSMouseMoved) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags];} + if([event type] == NSLeftMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_LBUTTON];} + if([event type] == NSRightMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_RBUTTON];} + if([event type] == NSOtherMouseDragged) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEMOVE flags:flags | cv::EVENT_FLAG_MBUTTON];} + if([event type] == NSEventTypeScrollWheel) {[self cvSendMouseEvent:event type:cv::EVENT_MOUSEWHEEL flags:flags ];} } -(void)scrollWheel:(NSEvent *)theEvent { diff --git a/modules/highgui/src/window_gtk.cpp b/modules/highgui/src/window_gtk.cpp index 3a34983b81..c43629befc 100644 --- a/modules/highgui/src/window_gtk.cpp +++ b/modules/highgui/src/window_gtk.cpp @@ -1917,7 +1917,7 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da { GdkEventMotion* event_motion = (GdkEventMotion*)event; - cv_event = CV_EVENT_MOUSEMOVE; + cv_event = cv::EVENT_MOUSEMOVE; pt32f.x = cvFloor(event_motion->x); pt32f.y = cvFloor(event_motion->y); state = event_motion->state; @@ -1933,21 +1933,21 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da if( event_button->type == GDK_BUTTON_PRESS ) { - cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDOWN : - event_button->button == 2 ? CV_EVENT_MBUTTONDOWN : - event_button->button == 3 ? CV_EVENT_RBUTTONDOWN : 0; + cv_event = event_button->button == 1 ? cv::EVENT_LBUTTONDOWN : + event_button->button == 2 ? cv::EVENT_MBUTTONDOWN : + event_button->button == 3 ? cv::EVENT_RBUTTONDOWN : 0; } else if( event_button->type == GDK_BUTTON_RELEASE ) { - cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONUP : - event_button->button == 2 ? CV_EVENT_MBUTTONUP : - event_button->button == 3 ? CV_EVENT_RBUTTONUP : 0; + cv_event = event_button->button == 1 ? cv::EVENT_LBUTTONUP : + event_button->button == 2 ? cv::EVENT_MBUTTONUP : + event_button->button == 3 ? cv::EVENT_RBUTTONUP : 0; } else if( event_button->type == GDK_2BUTTON_PRESS ) { - cv_event = event_button->button == 1 ? CV_EVENT_LBUTTONDBLCLK : - event_button->button == 2 ? CV_EVENT_MBUTTONDBLCLK : - event_button->button == 3 ? CV_EVENT_RBUTTONDBLCLK : 0; + cv_event = event_button->button == 1 ? cv::EVENT_LBUTTONDBLCLK : + event_button->button == 2 ? cv::EVENT_MBUTTONDBLCLK : + event_button->button == 3 ? cv::EVENT_RBUTTONDBLCLK : 0; } state = event_button->state; } @@ -1960,9 +1960,9 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da #if defined(GTK_VERSION3_4) // NOTE: in current implementation doesn't possible to put into callback function delta_x and delta_y separately double delta = (event->scroll.delta_x + event->scroll.delta_y); - cv_event = (event->scroll.delta_x==0) ? CV_EVENT_MOUSEWHEEL : CV_EVENT_MOUSEHWHEEL; + cv_event = (event->scroll.delta_x==0) ? cv::EVENT_MOUSEWHEEL : cv::EVENT_MOUSEHWHEEL; #else - cv_event = CV_EVENT_MOUSEWHEEL; + cv_event = cv::EVENT_MOUSEWHEEL; #endif //GTK_VERSION3_4 state = event->scroll.state; @@ -1972,11 +1972,11 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da case GDK_SCROLL_SMOOTH: flags |= (((int)delta << 16)); break; #endif //GTK_VERSION3_4 - case GDK_SCROLL_LEFT: cv_event = CV_EVENT_MOUSEHWHEEL; + case GDK_SCROLL_LEFT: cv_event = cv::EVENT_MOUSEHWHEEL; /* FALLTHRU */ case GDK_SCROLL_UP: flags |= ~0xffff; break; - case GDK_SCROLL_RIGHT: cv_event = CV_EVENT_MOUSEHWHEEL; + case GDK_SCROLL_RIGHT: cv_event = cv::EVENT_MOUSEHWHEEL; /* FALLTHRU */ case GDK_SCROLL_DOWN: flags |= (((int)1 << 16)); break; @@ -2011,16 +2011,16 @@ static gboolean icvOnMouse( GtkWidget *widget, GdkEvent *event, gpointer user_da { // handle non-keyboard (mouse) modifiers first flags |= - BIT_MAP(state, GDK_BUTTON1_MASK, CV_EVENT_FLAG_LBUTTON) | - BIT_MAP(state, GDK_BUTTON2_MASK, CV_EVENT_FLAG_MBUTTON) | - BIT_MAP(state, GDK_BUTTON3_MASK, CV_EVENT_FLAG_RBUTTON); + BIT_MAP(state, GDK_BUTTON1_MASK, cv::EVENT_FLAG_LBUTTON) | + BIT_MAP(state, GDK_BUTTON2_MASK, cv::EVENT_FLAG_MBUTTON) | + BIT_MAP(state, GDK_BUTTON3_MASK, cv::EVENT_FLAG_RBUTTON); // keyboard modifiers state &= gtk_accelerator_get_default_mod_mask(); flags |= - BIT_MAP(state, GDK_SHIFT_MASK, CV_EVENT_FLAG_SHIFTKEY) | - BIT_MAP(state, GDK_CONTROL_MASK, CV_EVENT_FLAG_CTRLKEY) | - BIT_MAP(state, GDK_MOD1_MASK, CV_EVENT_FLAG_ALTKEY) | - BIT_MAP(state, GDK_MOD2_MASK, CV_EVENT_FLAG_ALTKEY); + BIT_MAP(state, GDK_SHIFT_MASK, cv::EVENT_FLAG_SHIFTKEY) | + BIT_MAP(state, GDK_CONTROL_MASK, cv::EVENT_FLAG_CTRLKEY) | + BIT_MAP(state, GDK_MOD1_MASK, cv::EVENT_FLAG_ALTKEY) | + BIT_MAP(state, GDK_MOD2_MASK, cv::EVENT_FLAG_ALTKEY); window->on_mouse( cv_event, pt.x, pt.y, flags, window->on_mouse_param ); } } diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 8e041c9609..4423ea5311 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -1624,13 +1624,13 @@ MainWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_MOUSEHWHEEL: if (window.on_mouse) { - int flags = (wParam & MK_LBUTTON ? CV_EVENT_FLAG_LBUTTON : 0)| - (wParam & MK_RBUTTON ? CV_EVENT_FLAG_RBUTTON : 0)| - (wParam & MK_MBUTTON ? CV_EVENT_FLAG_MBUTTON : 0)| - (wParam & MK_CONTROL ? CV_EVENT_FLAG_CTRLKEY : 0)| - (wParam & MK_SHIFT ? CV_EVENT_FLAG_SHIFTKEY : 0)| - (GetKeyState(VK_MENU) < 0 ? CV_EVENT_FLAG_ALTKEY : 0); - int event = (uMsg == WM_MOUSEWHEEL ? CV_EVENT_MOUSEWHEEL : CV_EVENT_MOUSEHWHEEL); + int flags = (wParam & MK_LBUTTON ? cv::EVENT_FLAG_LBUTTON : 0)| + (wParam & MK_RBUTTON ? cv::EVENT_FLAG_RBUTTON : 0)| + (wParam & MK_MBUTTON ? cv::EVENT_FLAG_MBUTTON : 0)| + (wParam & MK_CONTROL ? cv::EVENT_FLAG_CTRLKEY : 0)| + (wParam & MK_SHIFT ? cv::EVENT_FLAG_SHIFTKEY : 0)| + (GetKeyState(VK_MENU) < 0 ? cv::EVENT_FLAG_ALTKEY : 0); + int event = (uMsg == WM_MOUSEWHEEL ? cv::EVENT_MOUSEWHEEL : cv::EVENT_MOUSEHWHEEL); // Set the wheel delta of mouse wheel to be in the upper word of 'event' int delta = GET_WHEEL_DELTA_WPARAM(wParam); @@ -1822,22 +1822,22 @@ static LRESULT CALLBACK HighGUIProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM { POINT pt; - int flags = (wParam & MK_LBUTTON ? CV_EVENT_FLAG_LBUTTON : 0)| - (wParam & MK_RBUTTON ? CV_EVENT_FLAG_RBUTTON : 0)| - (wParam & MK_MBUTTON ? CV_EVENT_FLAG_MBUTTON : 0)| - (wParam & MK_CONTROL ? CV_EVENT_FLAG_CTRLKEY : 0)| - (wParam & MK_SHIFT ? CV_EVENT_FLAG_SHIFTKEY : 0)| - (GetKeyState(VK_MENU) < 0 ? CV_EVENT_FLAG_ALTKEY : 0); - int event = uMsg == WM_LBUTTONDOWN ? CV_EVENT_LBUTTONDOWN : - uMsg == WM_RBUTTONDOWN ? CV_EVENT_RBUTTONDOWN : - uMsg == WM_MBUTTONDOWN ? CV_EVENT_MBUTTONDOWN : - uMsg == WM_LBUTTONUP ? CV_EVENT_LBUTTONUP : - uMsg == WM_RBUTTONUP ? CV_EVENT_RBUTTONUP : - uMsg == WM_MBUTTONUP ? CV_EVENT_MBUTTONUP : - uMsg == WM_LBUTTONDBLCLK ? CV_EVENT_LBUTTONDBLCLK : - uMsg == WM_RBUTTONDBLCLK ? CV_EVENT_RBUTTONDBLCLK : - uMsg == WM_MBUTTONDBLCLK ? CV_EVENT_MBUTTONDBLCLK : - CV_EVENT_MOUSEMOVE; + int flags = (wParam & MK_LBUTTON ? cv::EVENT_FLAG_LBUTTON : 0)| + (wParam & MK_RBUTTON ? cv::EVENT_FLAG_RBUTTON : 0)| + (wParam & MK_MBUTTON ? cv::EVENT_FLAG_MBUTTON : 0)| + (wParam & MK_CONTROL ? cv::EVENT_FLAG_CTRLKEY : 0)| + (wParam & MK_SHIFT ? cv::EVENT_FLAG_SHIFTKEY : 0)| + (GetKeyState(VK_MENU) < 0 ? cv::EVENT_FLAG_ALTKEY : 0); + int event = uMsg == WM_LBUTTONDOWN ? cv::EVENT_LBUTTONDOWN : + uMsg == WM_RBUTTONDOWN ? cv::EVENT_RBUTTONDOWN : + uMsg == WM_MBUTTONDOWN ? cv::EVENT_MBUTTONDOWN : + uMsg == WM_LBUTTONUP ? cv::EVENT_LBUTTONUP : + uMsg == WM_RBUTTONUP ? cv::EVENT_RBUTTONUP : + uMsg == WM_MBUTTONUP ? cv::EVENT_MBUTTONUP : + uMsg == WM_LBUTTONDBLCLK ? cv::EVENT_LBUTTONDBLCLK : + uMsg == WM_RBUTTONDBLCLK ? cv::EVENT_RBUTTONDBLCLK : + uMsg == WM_MBUTTONDBLCLK ? cv::EVENT_MBUTTONDBLCLK : + cv::EVENT_MOUSEMOVE; if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN) SetCapture(hwnd); if (uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONUP || uMsg == WM_MBUTTONUP) diff --git a/modules/videoio/src/cap_dshow.cpp b/modules/videoio/src/cap_dshow.cpp index c383e478de..5cb8232fa8 100644 --- a/modules/videoio/src/cap_dshow.cpp +++ b/modules/videoio/src/cap_dshow.cpp @@ -3372,9 +3372,9 @@ VideoCapture_DShow::VideoCapture_DShow(int index, const VideoCaptureParameters& CoInitialize(0); if (!params.empty()) { - int tmpW = params.get(CV_CAP_PROP_FRAME_WIDTH, -1); - int tmpH = params.get(CV_CAP_PROP_FRAME_HEIGHT, -1); - int tmpFOURCC = params.get(CV_CAP_PROP_FOURCC, -1); + int tmpW = params.get(CAP_PROP_FRAME_WIDTH, -1); + int tmpH = params.get(CAP_PROP_FRAME_HEIGHT, -1); + int tmpFOURCC = params.get(CAP_PROP_FOURCC, -1); if (tmpW != -1 && tmpH != -1) { g_VI.setupDeviceFourcc(index, tmpW, tmpH, tmpFOURCC); } From a63ede6b1df6041f2cd1246e536f82e256f51220 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Wed, 26 Feb 2025 23:15:41 +0900 Subject: [PATCH 12/19] Merge pull request #26930 from Kumataro:fix26924 Imgcodecs: gif: support Disposal Method #26930 Close https://github.com/opencv/opencv/issues/26924 ### 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 - [x] 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/imgcodecs/src/grfmt_gif.cpp | 136 +++++++++++++++------------- modules/imgcodecs/src/grfmt_gif.hpp | 36 ++++++-- modules/imgcodecs/test/test_gif.cpp | 48 ++++++++++ 3 files changed, 147 insertions(+), 73 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 5e5695b07b..c229a45201 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -24,7 +24,6 @@ GifDecoder::GifDecoder() { hasRead = false; hasTransparentColor = false; transparentColor = 0; - opMode = GRFMT_GIF_Nothing; top = 0, left = 0, width = 0, height = 0; depth = 8; idx = 0; @@ -66,6 +65,8 @@ bool GifDecoder::readHeader() { for (int i = 0; i < 3 * globalColorTableSize; i++) { globalColorTable[i] = (uchar)m_strm.getByte(); } + CV_CheckGE(bgColor, 0, "bgColor should be >= 0"); + CV_CheckLT(bgColor, globalColorTableSize, "bgColor should be < globalColorTableSize"); } // get the frame count @@ -81,7 +82,8 @@ bool GifDecoder::readData(Mat &img) { return true; } - readExtensions(); + const GifDisposeMethod disposalMethod = readExtensions(); + // Image separator CV_Assert(!(m_strm.getByte()^0x2C)); left = m_strm.getWord(); @@ -93,38 +95,50 @@ bool GifDecoder::readData(Mat &img) { imgCodeStream.resize(width * height); Mat img_; - switch (opMode) { - case GifOpMode::GRFMT_GIF_PreviousImage: - if (lastImage.empty()){ - img_ = Mat::zeros(m_height, m_width, CV_8UC4); - } else { - img_ = lastImage; + if (lastImage.empty()) + { + Scalar background(0.0, 0.0, 0.0, 0.0); + if (bgColor < globalColorTableSize) + { + background = Scalar( globalColorTable[bgColor * 3 + 2], // B + globalColorTable[bgColor * 3 + 1], // G + globalColorTable[bgColor * 3 + 0], // R + 0); // A + } + img_ = Mat(m_height, m_width, CV_8UC4, background); + } else { + img_ = lastImage; + } + lastImage.release(); + + Mat restore; + switch(disposalMethod) + { + case GIF_DISPOSE_NA: + case GIF_DISPOSE_NONE: + // Do nothing + break; + case GIF_DISPOSE_RESTORE_BACKGROUND: + if (bgColor < globalColorTableSize) + { + const Scalar background = Scalar( globalColorTable[bgColor * 3 + 2], // B + globalColorTable[bgColor * 3 + 1], // G + globalColorTable[bgColor * 3 + 0], // R + 0); // A + restore = Mat(width, height, CV_8UC4, background); + } + else + { + CV_LOG_WARNING(NULL, cv::format("bgColor(%d) is out of globalColorTableSize(%d)", bgColor, globalColorTableSize)); } break; - case GifOpMode::GRFMT_GIF_Background: - // background color is valid iff global color table exists - CV_Assert(globalColorTableSize > 0); - if (hasTransparentColor && transparentColor == bgColor) { - img_ = Mat(m_height, m_width, CV_8UC4, - Scalar(globalColorTable[bgColor * 3 + 2], - globalColorTable[bgColor * 3 + 1], - globalColorTable[bgColor * 3], 0)); - } else { - img_ = Mat(m_height, m_width, CV_8UC4, - Scalar(globalColorTable[bgColor * 3 + 2], - globalColorTable[bgColor * 3 + 1], - globalColorTable[bgColor * 3], 255)); - } - break; - case GifOpMode::GRFMT_GIF_Nothing: - case GifOpMode::GRFMT_GIF_Cover: - // default value - img_ = Mat::zeros(m_height, m_width, CV_8UC4); + case GIF_DISPOSE_RESTORE_PREVIOUS: + restore = Mat(img_, cv::Rect(left,top,width,height)).clone(); break; default: CV_Assert(false); + break; } - lastImage.release(); auto flags = (uchar)m_strm.getByte(); if (flags & 0x80) { @@ -189,6 +203,13 @@ bool GifDecoder::readData(Mat &img) { // release the memory img_.release(); + // update lastImage to dispose current frame. + if(!restore.empty()) + { + Mat roi = Mat(lastImage, cv::Rect(left,top,width,height)); + restore.copyTo(roi); + } + return hasRead; } @@ -212,8 +233,9 @@ bool GifDecoder::nextPage() { } } -void GifDecoder::readExtensions() { +GifDisposeMethod GifDecoder::readExtensions() { uchar len; + GifDisposeMethod disposalMethod = GifDisposeMethod::GIF_DISPOSE_NA; while (!(m_strm.getByte() ^ 0x21)) { auto extensionType = (uchar)m_strm.getByte(); @@ -221,13 +243,19 @@ void GifDecoder::readExtensions() { // the scope of this extension is the next image or plain text extension if (!(extensionType ^ 0xF9)) { hasTransparentColor = false; - opMode = GifOpMode::GRFMT_GIF_Nothing;// default value len = (uchar)m_strm.getByte(); CV_Assert(len == 4); - auto flags = (uchar)m_strm.getByte(); + const uint8_t packedFields = (uchar)m_strm.getByte(); + + const uint8_t dm = (packedFields >> GIF_DISPOSE_METHOD_SHIFT) & GIF_DISPOSE_METHOD_MASK; + CV_CheckLE(dm, GIF_DISPOSE_MAX, "Unsupported Dispose Method"); + disposalMethod = static_cast(dm); + + const uint8_t transColorFlag = packedFields & GIF_TRANS_COLOR_FLAG_MASK; + CV_CheckLE(transColorFlag, GIF_TRANSPARENT_INDEX_MAX, "Unsupported Transparent Color Flag"); + hasTransparentColor = (transColorFlag == GIF_TRANSPARENT_INDEX_GIVEN); + m_animation.durations.push_back(m_strm.getWord() * 10); // delay time - opMode = (GifOpMode)((flags & 0x1C) >> 2); - hasTransparentColor = flags & 0x01; transparentColor = (uchar)m_strm.getByte(); } @@ -240,6 +268,8 @@ void GifDecoder::readExtensions() { } // roll back to the block identifier m_strm.setPos(m_strm.getPos() - 1); + + return disposalMethod; } void GifDecoder::code2pixel(Mat& img, int start, int k){ @@ -247,23 +277,6 @@ void GifDecoder::code2pixel(Mat& img, int start, int k){ for (int j = 0; j < width; j++) { uchar colorIdx = imgCodeStream[idx++]; if (hasTransparentColor && colorIdx == transparentColor) { - if (opMode != GifOpMode::GRFMT_GIF_PreviousImage) { - if (colorIdx < localColorTableSize) { - img.at(top + i, left + j) = - Vec4b(localColorTable[colorIdx * 3 + 2], // B - localColorTable[colorIdx * 3 + 1], // G - localColorTable[colorIdx * 3], // R - 0); // A - } else if (colorIdx < globalColorTableSize) { - img.at(top + i, left + j) = - Vec4b(globalColorTable[colorIdx * 3 + 2], // B - globalColorTable[colorIdx * 3 + 1], // G - globalColorTable[colorIdx * 3], // R - 0); // A - } else { - img.at(top + i, left + j) = Vec4b(0, 0, 0, 0); - } - } continue; } if (colorIdx < localColorTableSize) { @@ -437,14 +450,10 @@ bool GifDecoder::getFrameCount_() { int len = m_strm.getByte(); while (len) { if (len == 4) { - int packedFields = m_strm.getByte(); - // 3 bit : Reserved - // 3 bit : Disposal Method - // 1 bit : User Input Flag - // 1 bit : Transparent Color Flag - if ( (packedFields & 0x01)== 0x01) { - m_type = CV_8UC4; // Transparent Index is given. - } + const uint8_t packedFields = static_cast(m_strm.getByte()); // Packed Fields + const uint8_t transColorFlag = packedFields & GIF_TRANS_COLOR_FLAG_MASK; + CV_CheckLE(transColorFlag, GIF_TRANSPARENT_INDEX_MAX, "Unsupported Transparent Color Flag"); + m_type = (transColorFlag == GIF_TRANSPARENT_INDEX_GIVEN) ? CV_8UC4 : CV_8UC3; m_strm.skip(2); // Delay Time m_strm.skip(1); // Transparent Color Index } else { @@ -518,7 +527,6 @@ GifEncoder::GifEncoder() { m_height = 0, m_width = 0; width = 0, height = 0, top = 0, left = 0; m_buf_supported = true; - opMode = GRFMT_GIF_Cover; transparentColor = 0; // index of the transparent color, default 0. currently it is a constant number transparentRGB = Vec3b(0, 0, 0); // the transparent color, default black lzwMaxCodeSize = 12; // the maximum code size, default 12. currently it is a constant number @@ -665,11 +673,9 @@ bool GifEncoder::writeFrame(const Mat &img) { strm.putByte(0x21); // extension introducer strm.putByte(0xF9); // graphic control label strm.putByte(0x04); // block size, fixed number - // flag is a packed field, and the first 3 bits are reserved - uchar flag = opMode << 2; - if (criticalTransparency) - flag |= 1; - strm.putByte(flag); + const int gcePackedFields = static_cast(GIF_DISPOSE_RESTORE_PREVIOUS << GIF_DISPOSE_METHOD_SHIFT) | + static_cast(criticalTransparency ? GIF_TRANSPARENT_INDEX_GIVEN : GIF_TRANSPARENT_INDEX_NOT_GIVEN); + strm.putByte(gcePackedFields); strm.putWord(frameDelay); strm.putByte(transparentColor); strm.putByte(0x00); // end of the extension @@ -680,7 +686,7 @@ bool GifEncoder::writeFrame(const Mat &img) { strm.putWord(top); strm.putWord(width); strm.putWord(height); - flag = localColorTableSize > 0 ? 0x80 : 0x00; + uint8_t flag = localColorTableSize > 0 ? 0x80 : 0x00; if (localColorTableSize > 0) { std::vector img_vec(1, img); getColorTable(img_vec, false); diff --git a/modules/imgcodecs/src/grfmt_gif.hpp b/modules/imgcodecs/src/grfmt_gif.hpp index 8552718d00..106cc52186 100644 --- a/modules/imgcodecs/src/grfmt_gif.hpp +++ b/modules/imgcodecs/src/grfmt_gif.hpp @@ -11,14 +11,35 @@ namespace cv { -enum GifOpMode -{ - GRFMT_GIF_Nothing = 0, - GRFMT_GIF_PreviousImage = 1, - GRFMT_GIF_Background = 2, - GRFMT_GIF_Cover = 3 +// See https://www.w3.org/Graphics/GIF/spec-gif89a.txt +// 23. Graphic Control Extension. +// +// Reserved : 3 bits +// Disposal Method : 3 bits +// User Input Flag : 1 bit +// Transparent Color Flag : 1 bit +constexpr int GIF_DISPOSE_METHOD_SHIFT = 2; +constexpr int GIF_DISPOSE_METHOD_MASK = 7; // 0b111 +constexpr int GIF_TRANS_COLOR_FLAG_MASK = 1; // 0b1 + +enum GifDisposeMethod { + GIF_DISPOSE_NA = 0, + GIF_DISPOSE_NONE = 1, + GIF_DISPOSE_RESTORE_BACKGROUND = 2, + GIF_DISPOSE_RESTORE_PREVIOUS = 3, + // 4-7 are reserved/undefined. + + GIF_DISPOSE_MAX = GIF_DISPOSE_RESTORE_PREVIOUS, }; +enum GifTransparentColorFlag { + GIF_TRANSPARENT_INDEX_NOT_GIVEN = 0, + GIF_TRANSPARENT_INDEX_GIVEN = 1, + + GIF_TRANSPARENT_INDEX_MAX = GIF_TRANSPARENT_INDEX_GIVEN, +}; + + ////////////////////////////////////////////////////////////////////// //// GIF Decoder //// ////////////////////////////////////////////////////////////////////// @@ -43,7 +64,6 @@ protected: int depth; int idx; - GifOpMode opMode; bool hasTransparentColor; uchar transparentColor; int top, left, width, height; @@ -66,7 +86,7 @@ protected: std::vector prefix; }; - void readExtensions(); + GifDisposeMethod readExtensions(); void code2pixel(Mat& img, int start, int k); bool lzwDecode(); bool getFrameCount_(); diff --git a/modules/imgcodecs/test/test_gif.cpp b/modules/imgcodecs/test/test_gif.cpp index 50d0be6c3a..a7c5ce8264 100644 --- a/modules/imgcodecs/test/test_gif.cpp +++ b/modules/imgcodecs/test/test_gif.cpp @@ -366,6 +366,54 @@ TEST(Imgcodecs_Gif, encode_IMREAD_GRAYSCALE) { EXPECT_EQ(decoded.channels(), 1); } +// See https://github.com/opencv/opencv/issues/26924 +TEST(Imgcodecs_Gif, decode_disposal_method) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "gifsuite/disposalMethod.gif"; + cv::Animation anim; + bool ret = false; + EXPECT_NO_THROW(ret = imreadanimation(filename, anim, cv::IMREAD_UNCHANGED)); + EXPECT_TRUE(ret); + + /* [Detail of this test] + * disposalMethod.gif has 5 frames to draw 8x8 rectangles with each color index, offsets and disposal method. + * frame 1 draws {1, ... ,1} rectangle at (1,1) with DisposalMethod = 0. + * frame 2 draws {2, ... ,2} rectangle at (2,2) with DisposalMethod = 3. + * frame 3 draws {3, ... ,3} rectangle at (3,3) with DisposalMethod = 1. + * frame 4 draws {4, ... ,4} rectangle at (4,4) with DisposalMethod = 2. + * frame 5 draws {5, ... ,5} rectangle at (5,5) with DisposalMethod = 1. + * + * To convenience to test, color[N] in the color table has RGB(32*N, some, some). + * color[0] = RGB(0,0,0) (background color). + * color[1] = RGB(32,0,0) + * color[2] = RGB(64,0,255) + * color[3] = RGB(96,255,0) + * color[4] = RGB(128,128,128) + * color[5] = RGB(160,255,255) + */ + const int refIds[5][6] = + {// { 0, 0, 0, 0, 0, 0} 0 is background color. + { 0, 1, 1, 1, 1, 1}, // 1 is to be not disposed. + { 0, 1, 2, 2, 2, 2}, // 2 is to be restored to previous. + { 0, 1, 1, 3, 3, 3}, // 3 is to be left in place. + { 0, 1, 1, 3, 4, 4}, // 4 is to be restored to the background color. + { 0, 1, 1, 3, 0, 5}, // 5 is to be left in place. + }; + + for(int i = 0 ; i < 5; i++) + { + cv::Mat frame = anim.frames[i]; + EXPECT_FALSE(frame.empty()); + EXPECT_EQ(frame.type(), CV_8UC4); + for(int j = 0; j < 6; j ++ ) + { + const cv::Scalar p = frame.at(j,j); + EXPECT_EQ( p[2], refIds[i][j] * 32 ) << " i = " << i << " j = " << j << " pixels = " << p; + } + } +} + }//opencv_test }//namespace From 76d3bf0a3b6ce282c52ff8b4743a57910675f39b Mon Sep 17 00:00:00 2001 From: Anastasiya Pronina Date: Wed, 26 Feb 2025 16:48:09 +0000 Subject: [PATCH 13/19] Workaround for successfull append of OpenVINO Execution Provider: Moved creation of 'Ort::Env' before it --- modules/gapi/src/backends/onnx/gonnxbackend.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/gapi/src/backends/onnx/gonnxbackend.cpp b/modules/gapi/src/backends/onnx/gonnxbackend.cpp index fc9b12b081..546e5e9088 100644 --- a/modules/gapi/src/backends/onnx/gonnxbackend.cpp +++ b/modules/gapi/src/backends/onnx/gonnxbackend.cpp @@ -732,6 +732,11 @@ ONNXCompiled::ONNXCompiled(const gapi::onnx::detail::ParamDesc &pp) cv::util::throw_error(std::logic_error("Please specify output layer names for " + params.model_path)); } + // WA: Some ONNX Runtime + OVEP libraries don't allow + // creation of environment after addition of OpenVINO + // Execution Provider. + // Create and initialize the ONNX environment + this_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, ""); // Create and initialize the ONNX session Ort::SessionOptions session_options; GAPI_LOG_INFO(NULL, "Adding Execution Providers for \"" << pp.model_path << "\""); @@ -750,7 +755,6 @@ ONNXCompiled::ONNXCompiled(const gapi::onnx::detail::ParamDesc &pp) if (pp.opt_level.has_value()) { session_options.SetGraphOptimizationLevel(convertToGraphOptimizationLevel(pp.opt_level.value())); } - this_env = Ort::Env(ORT_LOGGING_LEVEL_WARNING, ""); #ifndef _WIN32 this_session = Ort::Session(this_env, params.model_path.data(), session_options); #else From 0b8cab368ba8a8603681a379fbbd9c7b6df4ab50 Mon Sep 17 00:00:00 2001 From: xi-guo Date: Thu, 27 Feb 2025 14:24:18 +0800 Subject: [PATCH 14/19] fix: qnx7.0 build --- CMakeLists.txt | 7 ++++++- modules/ts/CMakeLists.txt | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1739e066bd..6b607b943f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -720,6 +720,7 @@ if(UNIX OR MINGW) endif() include(CheckFunctionExists) include(CheckIncludeFile) + include(CheckLibraryExists) include(CheckSymbolExists) if(NOT APPLE) @@ -731,7 +732,11 @@ if(UNIX OR MINGW) elseif(EMSCRIPTEN) # no need to link to system libs with emscripten elseif(QNXNTO) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} m regex) + set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} m) + CHECK_LIBRARY_EXISTS(regex regexec "" HAVE_REGEX_LIBRARY) + if(HAVE_REGEX_LIBRARY) + set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} regex) + endif() elseif(MINGW) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} pthread) else() diff --git a/modules/ts/CMakeLists.txt b/modules/ts/CMakeLists.txt index 63edae1e67..5f984bd908 100644 --- a/modules/ts/CMakeLists.txt +++ b/modules/ts/CMakeLists.txt @@ -49,5 +49,9 @@ if(OPENCV_DISABLE_THREAD_SUPPORT) endif() if(CMAKE_SYSTEM_NAME STREQUAL "QNX") - ocv_target_link_libraries(${the_module} PUBLIC regex) -endif() \ No newline at end of file + include(CheckLibraryExists) + CHECK_LIBRARY_EXISTS(regex regexec "" HAVE_REGEX_LIBRARY) + if(HAVE_REGEX_LIBRARY) + ocv_target_link_libraries(${the_module} PUBLIC regex) + endif() +endif() From 87cc1643f415100b2dff98d516d8ee51c69b942b Mon Sep 17 00:00:00 2001 From: Anshuprem Date: Sat, 1 Mar 2025 17:24:26 +0530 Subject: [PATCH 15/19] Merge pull request #26992 from Anshuprem:4.x Some minor fixes #26992 ### 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 - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/videoio/doc/videoio_overview.markdown | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/videoio/doc/videoio_overview.markdown b/modules/videoio/doc/videoio_overview.markdown index 041f2949a5..8dcc923efb 100644 --- a/modules/videoio/doc/videoio_overview.markdown +++ b/modules/videoio/doc/videoio_overview.markdown @@ -10,7 +10,7 @@ Video I/O with OpenCV Overview {#videoio_overview} General Information ------------------- -The OpenCV @ref videoio module is a set of classes and functions to read and write video or images sequence. +The OpenCV @ref videoio module is a set of classes and functions to read and write video or images sequences. Basically, the module provides the cv::VideoCapture and cv::VideoWriter classes as 2-layer interface to many video I/O APIs used as backend. @@ -20,7 +20,7 @@ I/O APIs used as backend. Some backends such as Direct Show (DSHOW), Microsoft Media Foundation (MSMF), Video 4 Linux (V4L), etc... are interfaces to the video I/O library provided by the operating system. -Some others backends like OpenNI2 for Kinect, Intel Perceptual Computing SDK, GStreamer, +Some other backends like OpenNI2 for Kinect, Intel Perceptual Computing SDK, GStreamer, XIMEA Camera API, etc... are interfaces to proprietary drivers or to external library. See the list of supported backends here: cv::VideoCaptureAPIs @@ -84,8 +84,7 @@ for the operating system. Thus you can't use VideoCapture or VideoWriter with t To get access to their devices, manufactures provide their own C++ API and library that you have to include and link with your OpenCV application. -It is a common case that these libraries read/write images from/to a memory buffer. If it so, it is -possible to make a `Mat` header for memory buffer (user-allocated data) and process it +It is a common case that these libraries read/write images from/to a memory buffer. If so, it is possible to make a `Mat` header for memory buffer (user-allocated data) and process it in-place using OpenCV functions. See cv::Mat::Mat() for more details. @@ -93,7 +92,7 @@ The FFmpeg library ------------------ OpenCV can use the FFmpeg library (http://ffmpeg.org/) as backend to record, convert and stream audio and video. -FFmpeg is a complete, cross-reference solution. If you enable FFmpeg while configuring OpenCV than +FFmpeg is a complete, cross-reference solution. If you enable FFmpeg while configuring OpenCV then CMake will download and install the binaries in `OPENCV_SOURCE_CODE/3rdparty/ffmpeg/`. To use FFmpeg at runtime, you must deploy the FFmpeg binaries with your application. From dbd3ef9a6f838d3ac42f8cf4d9d1dd5c709cb807 Mon Sep 17 00:00:00 2001 From: Maxim Smolskiy Date: Sun, 2 Mar 2025 12:44:39 +0300 Subject: [PATCH 16/19] Merge pull request #26926 from MaximSmolskiy:fix-getPerspectiveTransform-for-singular-case Fix getPerspectiveTransform for singular case #26926 ### Pull Request Readiness Checklist Fix #26916 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 - [x] 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/imgproc/src/imgwarp.cpp | 40 ++++++++++++++++++++++++--- modules/imgproc/test/test_imgwarp.cpp | 35 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 609b3f4557..d512bbf887 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3392,7 +3392,7 @@ cv::Matx23d cv::getRotationMatrix2D_(Point2f center, double angle, double scale) * vi = --------------------- * c20*xi + c21*yi + c22 * - * Coefficients are calculated by solving linear system: + * Coefficients are calculated by solving one of 2 linear systems: * / x0 y0 1 0 0 0 -x0*u0 -y0*u0 \ /c00\ /u0\ * | x1 y1 1 0 0 0 -x1*u1 -y1*u1 | |c01| |u1| * | x2 y2 1 0 0 0 -x2*u2 -y2*u2 | |c02| |u2| @@ -3404,12 +3404,28 @@ cv::Matx23d cv::getRotationMatrix2D_(Point2f center, double angle, double scale) * * where: * cij - matrix coefficients, c22 = 1 + * + * or + * + * / x0 y0 1 0 0 0 -x0*u0 -y0*u0 -u0 \ /c00\ /0\ + * | x1 y1 1 0 0 0 -x1*u1 -y1*u1 -u1 | |c01| |0| + * | x2 y2 1 0 0 0 -x2*u2 -y2*u2 -u2 | |c02| |0| + * | x3 y3 1 0 0 0 -x3*u3 -y3*u3 -u3 |.|c10|=|0|, + * | 0 0 0 x0 y0 1 -x0*v0 -y0*v0 -v0 | |c11| |0| + * | 0 0 0 x1 y1 1 -x1*v1 -y1*v1 -v1 | |c12| |0| + * | 0 0 0 x2 y2 1 -x2*v2 -y2*v2 -v2 | |c20| |0| + * \ 0 0 0 x3 y3 1 -x3*v3 -y3*v3 -v3 / |c21| \0/ + * \c22/ + * + * where: + * cij - matrix coefficients, c00^2 + c01^2 + c02^2 + c10^2 + c11^2 + c12^2 + c20^2 + c21^2 + c22^2 = 1 */ cv::Mat cv::getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod) { CV_INSTRUMENT_REGION(); - Mat M(3, 3, CV_64F), X(8, 1, CV_64F, M.ptr()); + // try c22 = 1 + Mat M(3, 3, CV_64F), X8(8, 1, CV_64F, M.ptr()); double a[8][8], b[8]; Mat A(8, 8, CV_64F, a), B(8, 1, CV_64F, b); @@ -3428,8 +3444,24 @@ cv::Mat cv::getPerspectiveTransform(const Point2f src[], const Point2f dst[], in b[i+4] = dst[i].y; } - solve(A, B, X, solveMethod); - M.ptr()[8] = 1.; + if (solve(A, B, X8, solveMethod) && norm(A * X8, B) < 1e-8) + { + M.ptr()[8] = 1.; + + return M; + } + + // c00^2 + c01^2 + c02^2 + c10^2 + c11^2 + c12^2 + c20^2 + c21^2 + c22^2 = 1 + hconcat(A, -B, A); + + Mat AtA; + mulTransposed(A, AtA, true); + + Mat D, U; + SVDecomp(AtA, D, U, noArray()); + + Mat X9(9, 1, CV_64F, M.ptr()); + U.col(8).copyTo(X9); return M; } diff --git a/modules/imgproc/test/test_imgwarp.cpp b/modules/imgproc/test/test_imgwarp.cpp index 4cf99b4704..2ff1ca88ab 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1765,5 +1765,40 @@ TEST(Imgproc_Remap, issue_23562) } } +TEST(Imgproc_getPerspectiveTransform, issue_26916) +{ + double src_data[] = {320, 512, 960, 512, 0, 1024, 1280, 1024}; + const Mat src_points(4, 2, CV_64FC1, src_data); + + double dst_data[] = {0, 0, 1280, 0, 0, 1024, 1280, 1024}; + const Mat dst_points(4, 2, CV_64FC1, dst_data); + + Mat src_points_f; + src_points.convertTo(src_points_f, CV_32FC1); + + Mat dst_points_f; + dst_points.convertTo(dst_points_f, CV_32FC1); + + Mat perspective_transform = getPerspectiveTransform(src_points_f, dst_points_f); + EXPECT_NEAR(perspective_transform.at(2, 2), 0, 1e-16); + EXPECT_NEAR(cv::norm(perspective_transform), 1, 1e-14); + + const Mat ones = Mat::ones(4, 1, CV_64FC1); + + Mat homogeneous_src_points; + hconcat(src_points, ones, homogeneous_src_points); + + Mat obtained_homogeneous_dst_points = (perspective_transform * homogeneous_src_points.t()).t(); + for (int row = 0; row < 4; ++row) + { + obtained_homogeneous_dst_points.row(row) /= obtained_homogeneous_dst_points.at(row, 2); + } + + Mat expected_homogeneous_dst_points; + hconcat(dst_points, ones, expected_homogeneous_dst_points); + + EXPECT_MAT_NEAR(obtained_homogeneous_dst_points, expected_homogeneous_dst_points, 1e-10); +} + }} // namespace /* End of file. */ From 3f1e7fcb8f9d29e19eb10a48dc2b17c6f4080c4a Mon Sep 17 00:00:00 2001 From: Skreg <85214856+shyama7004@users.noreply.github.com> Date: Mon, 3 Mar 2025 17:42:18 +0530 Subject: [PATCH 17/19] Merge pull request #26996 from shyama7004:outofBound Fix Logical defect in FilterSpecklesImpl #26996 Fixes : #24963 ### 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 - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- modules/calib3d/src/stereosgbm.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/calib3d/src/stereosgbm.cpp b/modules/calib3d/src/stereosgbm.cpp index 75f6f32564..d2fa18e669 100644 --- a/modules/calib3d/src/stereosgbm.cpp +++ b/modules/calib3d/src/stereosgbm.cpp @@ -2345,21 +2345,33 @@ void filterSpecklesImpl(cv::Mat& img, int newVal, int maxSpeckleSize, int maxDif using namespace cv; int width = img.cols, height = img.rows, npixels = width*height; - size_t bufSize = npixels*(int)(sizeof(Point2s) + sizeof(int) + sizeof(uchar)); + // space allocation for: + // labels : npixels * sizeof(int) + // wbuf : npixels * sizeof(Point2s) + // rtype : (npixels + 1) * sizeof(uchar) + size_t bufSize = npixels * sizeof(int) // for labels + + npixels * sizeof(Point2s) // for wavefront buffer (wbuf) + + (npixels + 1) * sizeof(uchar); // for region type (rtype) if( !_buf.isContinuous() || _buf.empty() || _buf.cols*_buf.rows*_buf.elemSize() < bufSize ) _buf.reserveBuffer(bufSize); uchar* buf = _buf.ptr(); int i, j, dstep = (int)(img.step/sizeof(T)); + + // store labels and their corresponding region types int* labels = (int*)buf; buf += npixels*sizeof(labels[0]); + // store wavefront Point2s* wbuf = (Point2s*)buf; buf += npixels*sizeof(wbuf[0]); + // store region type uchar* rtype = (uchar*)buf; + buf += (npixels + 1) * sizeof(rtype[0]); int curlabel = 0; - // clear out label assignments + // clear out label and rtype buffers memset(labels, 0, npixels*sizeof(labels[0])); + memset(rtype, 0, (npixels + 1)*sizeof(rtype[0])); for( i = 0; i < height; i++ ) { From a62b78d6e3ff5eb6bc6be8abfd5a1abd04912e3c Mon Sep 17 00:00:00 2001 From: sssanjee-quic Date: Mon, 3 Mar 2025 17:44:08 +0530 Subject: [PATCH 18/19] Merge pull request #26910 from CodeLinaro:FastcvHAL_Documentation Documentation to enable FastCV based OpenCV HAL and Extensions #26910 ### 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 - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../building_fastcv/building_fastcv.markdown | 246 ++++++++++++++++++ .../building_fastcv/fastcv_hal_extns.png | Bin 0 -> 25874 bytes .../table_of_content_introduction.markdown | 1 + 3 files changed, 247 insertions(+) create mode 100644 doc/tutorials/introduction/building_fastcv/building_fastcv.markdown create mode 100644 doc/tutorials/introduction/building_fastcv/fastcv_hal_extns.png diff --git a/doc/tutorials/introduction/building_fastcv/building_fastcv.markdown b/doc/tutorials/introduction/building_fastcv/building_fastcv.markdown new file mode 100644 index 0000000000..7b48714f25 --- /dev/null +++ b/doc/tutorials/introduction/building_fastcv/building_fastcv.markdown @@ -0,0 +1,246 @@ +Building OpenCV with FastCV {#tutorial_building_fastcv} +=========================== + +| | | +| -: | :- | +| Compatibility | OpenCV >= 4.11.0 | + +Enable OpenCV with FastCV for Qualcomm Chipsets +----------------------------------------------- + +This document scope is to guide the Developers to enable OpenCV Acceleration with FastCV for the +Qualcomm chipsets with ARM64 architecture. Entablement of OpenCV with FastCV back-end on non-Qualcomm +chipsets or Linux platforms other than [Qualcomm Linux](https://www.qualcomm.com/developer/software/qualcomm-linux) +is currently out of scope. + +About FastCV +------------ + +FastCV provides two main features to computer vision application developers: + +- A library of frequently used computer vision (CV) functions, optimized to run efficiently on a wide variety of Qualcomm’s Snapdragon devices. +- A clean processor-agnostic hardware acceleration API, under which chipset vendors can hardware accelerate FastCV functions on Qualcomm’s Snapdragon hardware. + +FastCV is released as a unified binary, a single binary containing two implementations of the algorithms: + +- Generic implementation runs on Arm® architecture and is referred to as FastCV for Arm architecture. +- Implementation runs only on Qualcomm® Snapdragon™ chipsets and is called FastCV for Snapdragon. + +FastCV library is Qualcomm proprietary and provides faster implementation of CV algorithms on various hardware as compared to other CV libraries. + +OpenCV Acceleration with FastCV HAL and Extensions +-------------------------------------------------- + +OpenCV and FastCV integration is implemented in two ways: + +1. FastCV-based HAL for basic computer vision and arithmetic algorithms acceleration. +2. FastCV module in opencv_contrib with custom algorithms and FastCV function wrappers that do not fit generic OpenCV interface or behaviour. + + +![](fastcv_hal_extns.png) + +Supported Platforms +------------------- + +1. Android : Qualcomm Chipsets with the Android from Snapdragon 8 Gen 1 onwards(https://www.qualcomm.com/products/mobile/snapdragon/smartphones#product-list) +2. Linux : Qualcomm Linux Program related boards mentioned in [Hardware](https://www.qualcomm.com/developer/software/qualcomm-linux/hardware) + +Compiling OpenCV with FastCV for Android +---------------------------------------- + +1. **Follow Wiki page for OpenCV Compilation** : https://github.com/opencv/opencv/wiki/Custom-OpenCV-Android-SDK-and-AAR-package-build + + Once the OpenCV repository code is cloned into the workspace, please add `-DWITH_FASTCV=ON` flag to cmake vars as below to arm64 entry + in `opencv/platforms/android/default.config.py` or create new one with the option to enable FastCV HAL and/or extenstions compilation: + + ``` + ABI("3", "arm64-v8a", None, 24, cmake_vars=dict(WITH_FASTCV='ON')), + ``` + +2. Remaining steps can be followed as mentioned in [the wiki page](https://github.com/opencv/opencv/wiki/Custom-OpenCV-Android-SDK-and-AAR-package-build) + +Compiling OpenCV with FastCV for Qualcomm Linux +----------------------------------------------- + +@note: Only Ubuntu 22.04 is supported as host platform for eSDK deployment. + +1. Install eSDK by following [Qualcomm® Linux Documentation](https://docs.qualcomm.com/bundle/publicresource/topics/80-70017-51/install-sdk.html) + +2. After installing the eSDK, set the ESDK_ROOT: + + ``` + export ESDK_ROOT= + ``` + +3. Add SDK tools and libraries to your environment: + + ``` + source environment-setup-armv8-2a-qcom-linux + ``` + + If you encounter the following message: + ``` + Your environment is misconfigured, you probably need to 'unset LD_LIBRARY_PATH' + but please check why this was set in the first place and that it's safe to unset. + The SDK will not operate correctly in most cases when LD_LIBRARY_PATH is set. + ``` + just unset your host `LD_LIBRARY_PATH` environment variable: `unset LD_LIBRARY_PATH`. + +4. Clone OpenCV Repositories: + + Clone the OpenCV main and optionally opencv_contrib repositories into any directory + (it does not need to be inside the SDK directory). + + ``` + git clone https://github.com/opencv/opencv.git + git clone https://github.com/opencv/opencv_contrib.git + ``` + +5. Build OpenCV + + Create a build directory, navigate into it and build the project with CMake there: + + ``` + mkdir build + cd build + cmake -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=aarch64 -DWITH_FASTCV=ON -DBUILD_SHARED_LIBS=ON -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/fastcv/ ../opencv + make -j$(nproc) + ``` + + If the FastCV library is updated, please replace the old FastCV libraries located at: + ``` + \qcom-wayland_sdk\tmp\sysroots\qcs6490-rb3gen2-vision-kit\usr\lib + ``` + with the latest FastCV libraries downloaded in: + ``` + build\3rdparty\fastcv\libs + ``` + +6. Validate + + Push the OpenCV libraries, test binaries and test data on to the target. Execute the OpenCV conformance or performance tests. + During runtime, If libwebp.so.7 lib is missing, find the lib in the below Path and push it on the target + ``` + \qcom-wayland_sdk\tmp\sysroots\qcs6490-rb3gen2-vision-kit\usr\lib\libwebp.so.7 + ``` + +HAL and Extension list of APIs +------------------------------ + +**FastCV based OpenCV HAL APIs list :** + +|OpenCV module |OpenCV API | Underlying FastCV API for OpenCV acceleration | +|---------------|------------------|-----------------------------------------------| +|IMGPROC |medianBlur |fcvFilterMedian3x3u8_v3 | +| |sobel |fcvFilterSobel3x3u8s16 | +| | |fcvFilterSobel5x5u8s16 | +| | |fcvFilterSobel7x7u8s16 | +| |boxFilter |fcvBoxFilter3x3u8_v3 | +| | |fcvBoxFilter5x5u8_v2 | +| |adaptiveThreshold |fcvAdaptiveThresholdGaussian3x3u8_v2 | +| | |fcvAdaptiveThresholdGaussian5x5u8_v2 | +| | |fcvAdaptiveThresholdMean3x3u8_v2 | +| | |fcvAdaptiveThresholdMean5x5u8_v2 | +| |pyrUp & pyrDown |fcvPyramidCreateu8_v4 | +| |cvtColor |fcvColorRGB888toYCrCbu8_v3 | +| | |fcvColorRGB888ToHSV888u8 | +| |GaussianBlur |fcvFilterGaussian5x5u8_v3 | +| | |fcvFilterGaussian3x3u8_v4 | +| |cvWarpPerspective |fcvWarpPerspectiveu8_v5 | +| |Canny |fcvFilterCannyu8 | +| | | | +|CORE |lut | fcvTableLookupu8 | +| |norm |fcvHammingDistanceu8 | +| |multiply |fcvElementMultiplyu8u16_v2 | +| |transpose |fcvTransposeu8_v2 | +| | |fcvTransposeu16_v2 | +| | |fcvTransposef32_v2 | +| |meanStdDev |fcvImageIntensityStats_v2 | +| |flip |fcvFlipu8 | +| | |fcvFlipu16 | +| | |fcvFlipRGB888u8 | +| |rotate |fcvRotateImageu8 | +| | |fcvRotateImageInterleavedu8 | +| |multiply |fcvElementMultiplyu8 | +| | |fcvElementMultiplys16 | +| | |fcvElementMultiplyf32 | +| |addWeighted |fcvAddWeightedu8_v2 | +| |subtract |fcvImageDiffu8f32_v2 | + + +**FastCV based OpenCV Extensions APIs list :** + +|OpenCV Extension APIs |Underlying FastCV API for OpenCV acceleration | +|----------------------|----------------------------------------------| +|matmuls8s32 |fcvMatrixMultiplys8s32 | +|clusterEuclidean |fcvClusterEuclideanu8 | +|FAST10 |fcvCornerFast10InMaskScoreu8 | +| |fcvCornerFast10InMasku8 | +| |fcvCornerFast10Scoreu8 | +| |fcvCornerFast10u8 | +|FFT |fcvFFTu8 | +|IFFT |fcvIFFTf32 | +|fillConvexPoly |fcvFillConvexPolyu8 | +|houghLines |fcvHoughLineu8 | +|moments |fcvImageMomentsu8 | +| |fcvImageMomentss32 | +| |fcvImageMomentsf32 | +|runMSER |fcvMserInit | +| |fcvMserNN8Init | +| |fcvMserExtu8_v3 | +| |fcvMserExtNN8u8 | +| |fcvMserNN8u8 | +| |fcvMserRelease | +|remap |fcvRemapu8_v2 | +|remapRGBA |fcvRemapRGBA8888BLu8 | +| |fcvRemapRGBA8888NNu8 | +|resizeDownBy2 |fcvScaleDownBy2u8_v2 | +|resizeDownBy4 |fcvScaleDownBy4u8_v2 | +|meanShift |fcvMeanShiftu8 | +| |fcvMeanShifts32 | +| |fcvMeanShiftf32 | +|bilateralRecursive |fcvBilateralFilterRecursiveu8 | +|thresholdRange |fcvFilterThresholdRangeu8_v2 | +|bilateralFilter |fcvBilateralFilter5x5u8_v3 | +| |fcvBilateralFilter7x7u8_v3 | +| |fcvBilateralFilter9x9u8_v3 | +|calcHist |fcvImageIntensityHistogram | +|gaussianBlur |fcvFilterGaussian3x3u8_v4 | +| |fcvFilterGaussian5x5u8_v3 | +| |fcvFilterGaussian5x5s16_v3 | +| |fcvFilterGaussian5x5s32_v3 | +| |fcvFilterGaussian11x11u8_v2 | +|filter2D |fcvFilterCorrNxNu8 | +| |fcvFilterCorrNxNu8s16 | +| |fcvFilterCorrNxNu8f32 | +|sepFilter2D |fcvFilterCorrSepMxNu8 | +| |fcvFilterCorrSep9x9s16_v2 | +| |fcvFilterCorrSep11x11s16_v2 | +| |fcvFilterCorrSep13x13s16_v2 | +| |fcvFilterCorrSep15x15s16_v2 | +| |fcvFilterCorrSep17x17s16_v2 | +| |fcvFilterCorrSepNxNs16 | +|sobel3x3u8 |fcvImageGradientSobelPlanars8_v2 | +|sobel3x3u9 |fcvImageGradientSobelPlanars16_v2 | +|sobel3x3u10 |fcvImageGradientSobelPlanars16_v3 | +|sobel3x3u11 |fcvImageGradientSobelPlanarf32_v2 | +|sobel3x3u12 |fcvImageGradientSobelPlanarf32_v3 | +|sobel |fcvFilterSobel3x3u8_v2 | +| |fcvFilterSobel3x3u8s16 | +| |fcvFilterSobel5x5u8s16 | +| |fcvFilterSobel7x7u8s16 | +|DCT |fcvDCTu8 | +|iDCT |fcvIDCTs16 | +|sobelPyramid |fcvPyramidAllocate | +| |fcvPyramidAllocate_v2 | +| |fcvPyramidAllocate_v3 | +| |fcvPyramidSobelGradientCreatei8 | +| |fcvPyramidSobelGradientCreatei16 | +| |fcvPyramidSobelGradientCreatef32 | +| |fcvPyramidDelete | +| |fcvPyramidDelete_v2 | +| |fcvPyramidCreatef32_v2 | +| |fcvPyramidCreateu8_v4 | +|trackOpticalFlowLK |fcvTrackLKOpticalFlowu8_v3 | +| |fcvTrackLKOpticalFlowu8 | +|warpPerspective2Plane |fcv2PlaneWarpPerspectiveu8 | diff --git a/doc/tutorials/introduction/building_fastcv/fastcv_hal_extns.png b/doc/tutorials/introduction/building_fastcv/fastcv_hal_extns.png new file mode 100644 index 0000000000000000000000000000000000000000..dce7213236ade6e5f086381f7ba5a426ccdfa2d8 GIT binary patch literal 25874 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 vO)KVq;xUAqhJpXT Date: Mon, 3 Mar 2025 15:16:09 +0100 Subject: [PATCH 19/19] Merge pull request #26988 from DanBmh:opt_undistort Optimize undistort points #26988 Skips unnecessary rotation with identity matrix if no R or P mats are given. --------- Co-authored-by: Daniel --- modules/calib3d/src/fisheye.cpp | 14 +++++++++++--- modules/calib3d/src/undistort.dispatch.cpp | 13 ++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/modules/calib3d/src/fisheye.cpp b/modules/calib3d/src/fisheye.cpp index 5a30087b3f..a74ac5cf7a 100644 --- a/modules/calib3d/src/fisheye.cpp +++ b/modules/calib3d/src/fisheye.cpp @@ -478,10 +478,18 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted if ((converged || !isEps) && !theta_flipped) { Vec2d pu = pw * scale; //undistorted point + Vec2d fi; - // reproject - Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix - Vec2d fi(pr[0]/pr[2], pr[1]/pr[2]); // final + if (!R.empty() || !P.empty()) + { + // reproject + Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix + fi = Vec2d(pr[0]/pr[2], pr[1]/pr[2]); // final + } + else + { + fi = pu; + } if( sdepth == CV_32F ) dstf[i] = fi; diff --git a/modules/calib3d/src/undistort.dispatch.cpp b/modules/calib3d/src/undistort.dispatch.cpp index 7e09bd26a4..f40259f990 100644 --- a/modules/calib3d/src/undistort.dispatch.cpp +++ b/modules/calib3d/src/undistort.dispatch.cpp @@ -488,11 +488,14 @@ static void cvUndistortPointsInternal( const CvMat* _src, CvMat* _dst, const CvM } } - double xx = RR[0][0]*x + RR[0][1]*y + RR[0][2]; - double yy = RR[1][0]*x + RR[1][1]*y + RR[1][2]; - double ww = 1./(RR[2][0]*x + RR[2][1]*y + RR[2][2]); - x = xx*ww; - y = yy*ww; + if( matR || matP ) + { + double xx = RR[0][0]*x + RR[0][1]*y + RR[0][2]; + double yy = RR[1][0]*x + RR[1][1]*y + RR[1][2]; + double ww = 1./(RR[2][0]*x + RR[2][1]*y + RR[2][2]); + x = xx*ww; + y = yy*ww; + } if( dtype == CV_32FC2 ) {