diff --git a/3rdparty/hal_rvv/hal_rvv.hpp b/3rdparty/hal_rvv/hal_rvv.hpp index 6f62d8471a..f09e5aca7a 100644 --- a/3rdparty/hal_rvv/hal_rvv.hpp +++ b/3rdparty/hal_rvv/hal_rvv.hpp @@ -24,10 +24,12 @@ #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 #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/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; 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/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, diff --git a/CMakeLists.txt b/CMakeLists.txt index 155a8f1b2f..96069c2e0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -657,6 +657,7 @@ if(UNIX OR MINGW) endif() include(CheckFunctionExists) include(CheckIncludeFile) + include(CheckLibraryExists) include(CheckSymbolExists) if(NOT APPLE) @@ -668,7 +669,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/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 0000000000..dce7213236 Binary files /dev/null and b/doc/tutorials/introduction/building_fastcv/fastcv_hal_extns.png differ diff --git a/doc/tutorials/introduction/table_of_content_introduction.markdown b/doc/tutorials/introduction/table_of_content_introduction.markdown index 22d9688291..c9f096b0be 100644 --- a/doc/tutorials/introduction/table_of_content_introduction.markdown +++ b/doc/tutorials/introduction/table_of_content_introduction.markdown @@ -30,6 +30,7 @@ Introduction to OpenCV {#tutorial_table_of_content_introduction} - @subpage tutorial_arm_crosscompile_with_cmake - @subpage tutorial_crosscompile_with_multiarch - @subpage tutorial_building_tegra_cuda +- @subpage tutorial_building_fastcv - @ref tutorial_ios_install ##### Usage basics diff --git a/modules/3d/src/fisheye.cpp b/modules/3d/src/fisheye.cpp index 32a1645782..35cfbd9dab 100644 --- a/modules/3d/src/fisheye.cpp +++ b/modules/3d/src/fisheye.cpp @@ -426,10 +426,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/3d/src/undistort.dispatch.cpp b/modules/3d/src/undistort.dispatch.cpp index 6401feb139..493144c40c 100644 --- a/modules/3d/src/undistort.dispatch.cpp +++ b/modules/3d/src/undistort.dispatch.cpp @@ -480,11 +480,14 @@ static void undistortPointsInternal( const Mat& _src, Mat& _dst, const Mat& _cam } } - 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.empty() || !matP.empty() ) + { + 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 ) { diff --git a/modules/core/CMakeLists.txt b/modules/core/CMakeLists.txt index 82d820f3b3..990bb6a0e9 100644 --- a/modules/core/CMakeLists.txt +++ b/modules/core/CMakeLists.txt @@ -14,6 +14,7 @@ ocv_add_dispatched_file(minmax SSE2 SSE4_1 AVX2 VSX3 LASX) ocv_add_dispatched_file(nan_mask 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/perf/perf_arithm.cpp b/modules/core/perf/perf_arithm.cpp index 964f0fa316..f3baa6b2c2 100644 --- a/modules/core/perf/perf_arithm.cpp +++ b/modules/core/perf/perf_arithm.cpp @@ -706,6 +706,28 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest, ) ); +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, 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 //////////////////////// typedef perf::TestBaseWithParam> RotateTest; @@ -862,5 +884,29 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , FiniteMaskFixture, ) ); +//////////////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/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 diff --git a/modules/core/src/mathfuncs_core.simd.hpp b/modules/core/src/mathfuncs_core.simd.hpp index cb21064041..f92234f140 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) { @@ -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); diff --git a/modules/core/src/norm.cpp b/modules/core/src/norm.dispatch.cpp similarity index 85% rename from modules/core/src/norm.cpp rename to modules/core/src/norm.dispatch.cpp index f462149497..1a28fd2f0b 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,127 +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++ ) - { - ST v = (ST)src[k]; - result += v*v; - } - } - } - *_result = result; - return 0; -} - -static int -normInf_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) -{ - int result = *_result; - if( !mask ) - { - for ( int i = 0; i < len*cn; i++ ) { - result = std::max(result, (int)(src[i] != 0)); - if (result != 0) - break; - } - } - else - { - for( int i = 0; i < len; i++, src += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result = std::max(result, (int)(src[k] != 0)); - if (result != 0) - break; - } - } - *_result = result; - return 0; -} - -static int -normL1_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) -{ - int result = *_result; - if( !mask ) - { - for ( int i = 0; i < len*cn; i++ ) - result += (int)(src[i] != 0); - } - else - { - for( int i = 0; i < len; i++, src += cn ) - if( mask[i] ) - { - for( int k = 0; k < cn; k++ ) - result += (int)(src[k] != 0); - } - } - *_result = result; - return 0; -} - -static int -normL2_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) -{ - return normL1_Bool(src, mask, _result, len, cn); -} - template int normDiffInf_(const T* src1, const T* src2, const uchar* mask, ST* _result, int len, int cn) { @@ -457,141 +339,68 @@ normDiffL2_Bool(const uchar* src1, const uchar* src2, const uchar* mask, int* _r return normDiffL1_Bool(src1, src2, mask, _result, len, cn); } -#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); } +{ 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) +#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_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(32u, unsigned, unsigned, double, double) -CV_DEF_NORM_ALL(32s, int, unsigned, double, double) -CV_DEF_NORM_ALL(32f, float, float, double, double) -CV_DEF_NORM_ALL(64f, double, double, double, double) -CV_DEF_NORM_ALL(64u, uint64, uint64, double, double) -CV_DEF_NORM_ALL(64s, int64, uint64, double, double) -CV_DEF_NORM_ALL(16f, hfloat, float, float, float) -CV_DEF_NORM_ALL(16bf, bfloat, float, float, float) +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(32u, unsigned, unsigned, double, 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) +CV_DEF_NORM_DIFF_ALL(64u, uint64, uint64, double, double) +CV_DEF_NORM_DIFF_ALL(64s, int64, uint64, double, double) +CV_DEF_NORM_DIFF_ALL(16f, hfloat, float, float, float) +CV_DEF_NORM_DIFF_ALL(16bf, bfloat, float, float, float) -typedef int (*NormFunc)(const uchar*, const uchar*, void*, int, int); -typedef int (*NormDiffFunc)(const uchar*, const uchar*, const uchar*, void*, int, int); - -static NormFunc getNormFunc(int normType, int depth) -{ - static NormFunc normTab[3][CV_DEPTH_MAX] = - { - { - (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, - (NormFunc)GET_OPTIMIZED(normInf_16f), - (NormFunc)GET_OPTIMIZED(normInf_16bf), - (NormFunc)normInf_Bool, - (NormFunc)GET_OPTIMIZED(normInf_64u), - (NormFunc)GET_OPTIMIZED(normInf_64s), - (NormFunc)GET_OPTIMIZED(normInf_32u), - 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, - (NormFunc)GET_OPTIMIZED(normL1_16f), - (NormFunc)GET_OPTIMIZED(normL1_16bf), - (NormFunc)normL1_Bool, - (NormFunc)GET_OPTIMIZED(normL1_64u), - (NormFunc)GET_OPTIMIZED(normL1_64s), - (NormFunc)GET_OPTIMIZED(normL1_32u), - 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, - (NormFunc)GET_OPTIMIZED(normL2_16f), - (NormFunc)GET_OPTIMIZED(normL2_16bf), - (NormFunc)normL2_Bool, - (NormFunc)GET_OPTIMIZED(normL2_64u), - (NormFunc)GET_OPTIMIZED(normL2_64s), - (NormFunc)GET_OPTIMIZED(normL2_32u), - 0 - } - }; - - return normTab[normType][depth]; -} +typedef int (*NormFunc)(const uchar*, const uchar*, uchar*, int, int); +typedef int (*NormDiffFunc)(const uchar*, const uchar*, const uchar*, uchar*, int, int); static NormDiffFunc getNormDiffFunc(int normType, int depth) { static NormDiffFunc normDiffTab[3][CV_DEPTH_MAX] = { { - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8u), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8s), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_16u), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_16s), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_32s), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_32f), - (NormDiffFunc)normDiffInf_64f, - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_16f), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_16bf), + (NormDiffFunc)GET_OPTIMIZED(normDiffInf_8u), (NormDiffFunc)normDiffInf_8s, + (NormDiffFunc)normDiffInf_16u, (NormDiffFunc)normDiffInf_16s, + (NormDiffFunc)normDiffInf_32s, + (NormDiffFunc)normDiffInf_32f, (NormDiffFunc)normDiffInf_64f, + (NormDiffFunc)normDiffInf_16f, (NormDiffFunc)normDiffInf_16bf, (NormDiffFunc)normDiffInf_Bool, - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_64u), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_64s), - (NormDiffFunc)GET_OPTIMIZED(normDiffInf_32u), + (NormDiffFunc)normDiffInf_64u, (NormDiffFunc)normDiffInf_64s, + (NormDiffFunc)normDiffInf_32u, 0 }, { - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_8u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_8s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_16u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_16s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_32s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_32f), - (NormDiffFunc)normDiffL1_64f, - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_16f), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_16bf), + (NormDiffFunc)GET_OPTIMIZED(normDiffL1_8u), (NormDiffFunc)normDiffL1_8s, + (NormDiffFunc)normDiffL1_16u, (NormDiffFunc)GET_OPTIMIZED(normDiffL1_16s), + (NormDiffFunc)normDiffL1_32s, + (NormDiffFunc)normDiffL1_32f, (NormDiffFunc)normDiffL1_64f, + (NormDiffFunc)normDiffL1_16f, (NormDiffFunc)normDiffL1_16bf, (NormDiffFunc)normDiffL1_Bool, - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_64u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_64s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL1_32u), + (NormDiffFunc)normDiffL1_64u, (NormDiffFunc)normDiffL1_64s, + (NormDiffFunc)normDiffL1_32u, 0 }, { (NormDiffFunc)GET_OPTIMIZED(normDiffL2_8u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_8s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_16u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_16s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_32s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_32f), - (NormDiffFunc)normDiffL2_64f, - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_16f), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_16bf), + (NormDiffFunc)normDiffL2_8s, + (NormDiffFunc)normDiffL2_16u, (NormDiffFunc)normDiffL2_16s, + (NormDiffFunc)normDiffL2_32s, + (NormDiffFunc)normDiffL2_32f, (NormDiffFunc)normDiffL2_64f, + (NormDiffFunc)normDiffL2_16f, (NormDiffFunc)normDiffL2_16bf, (NormDiffFunc)normDiffL2_Bool, - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_64u), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_64s), - (NormDiffFunc)GET_OPTIMIZED(normDiffL2_32u), + (NormDiffFunc)normDiffL2_64u, (NormDiffFunc)normDiffL2_64s, + (NormDiffFunc)normDiffL2_32u, 0 } }; @@ -783,6 +592,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(); @@ -817,6 +631,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_Assert( func != 0 ); + if( src.isContinuous() && mask.empty() ) { size_t len = src.total()*cn; @@ -824,30 +641,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; } } @@ -857,7 +662,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 ) @@ -894,9 +699,6 @@ double norm( InputArray _src, int normType, InputArray _mask ) return result; } - NormFunc func = getNormFunc(normType >> 1, depth); - CV_Assert( func != 0 ); - const Mat* arrays[] = {&src, &mask, 0}; uchar* ptrs[2] = {}; union @@ -934,7 +736,7 @@ double norm( InputArray _src, int normType, InputArray _mask ) for (int j = 0; j < total; j += blockSize) { int bsz = std::min(total - j, blockSize); - func(ptrs[0], ptrs[1], &blocksum.i, bsz, cn); + func(ptrs[0], ptrs[1], (uchar*)&blocksum, bsz, cn); count += bsz; if (count + blockSize >= blockSize0 || (i+1 >= it.nplanes && j+bsz >= total)) { @@ -953,7 +755,7 @@ double norm( InputArray _src, int normType, InputArray _mask ) // generic implementation for (size_t i = 0; i < it.nplanes; i++, ++it) { - func(ptrs[0], ptrs[1], &result, (int)it.size, cn); + func(ptrs[0], ptrs[1], (uchar*)&result, (int)it.size, cn); } } @@ -1418,7 +1220,7 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask for (int j = 0; j < total; j += blockSize) { int bsz = std::min(total - j, blockSize); - func(ptrs[0], ptrs[1], ptrs[2], &blocksum.i, bsz, cn); + func(ptrs[0], ptrs[1], ptrs[2], (uchar*)&blocksum, bsz, cn); count += bsz; if (count + blockSize >= blockSize0 || (i+1 >= it.nplanes && j+bsz >= total)) { @@ -1438,7 +1240,7 @@ double norm( InputArray _src1, InputArray _src2, int normType, InputArray _mask // generic implementation for (size_t i = 0; i < it.nplanes; i++, ++it) { - func(ptrs[0], ptrs[1], ptrs[2], &result, (int)it.size, cn); + func(ptrs[0], ptrs[1], ptrs[2], (uchar*)&result, (int)it.size, cn); } } 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..541c27f12e --- /dev/null +++ b/modules/core/src/norm.simd.hpp @@ -0,0 +1,779 @@ +// 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, std::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 ) + { + 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++ ) + { + ST v = (ST)src[k]; + result += v*v; + } + } + } + *_result = result; + return 0; +} + +static int +normInf_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) +{ + int result = *_result; + if( !mask ) + { + for ( int i = 0; i < len*cn; i++ ) { + result = std::max(result, (int)(src[i] != 0)); + if (result != 0) + break; + } + } + else + { + for( int i = 0; i < len; i++, src += cn ) + if( mask[i] ) + { + for( int k = 0; k < cn; k++ ) + result = std::max(result, (int)(src[k] != 0)); + if (result != 0) + break; + } + } + *_result = result; + return 0; +} + +static int +normL1_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) +{ + int result = *_result; + if( !mask ) + { + for ( int i = 0; i < len*cn; i++ ) + result += (int)(src[i] != 0); + } + else + { + for( int i = 0; i < len; i++, src += cn ) + if( mask[i] ) + { + for( int k = 0; k < cn; k++ ) + result += (int)(src[k] != 0); + } + } + *_result = result; + return 0; +} + +static int +normL2_Bool(const uchar* src, const uchar* mask, int* _result, int len, int cn) +{ + return normL1_Bool(src, mask, _result, len, cn); +} + +#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(32u, unsigned, unsigned, double, double) +CV_DEF_NORM_ALL(32s, int, unsigned, double, double) +CV_DEF_NORM_ALL(32f, float, float, double, double) +CV_DEF_NORM_ALL(64f, double, double, double, double) +CV_DEF_NORM_ALL(64u, uint64, uint64, double, double) +CV_DEF_NORM_ALL(64s, int64, uint64, double, double) +CV_DEF_NORM_ALL(16f, hfloat, float, float, float) +CV_DEF_NORM_ALL(16bf, bfloat, float, float, float) + +NormFunc getNormFunc(int normType, int depth) +{ + static NormFunc normTab[3][CV_DEPTH_MAX] = + { + { + (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, + (NormFunc)GET_OPTIMIZED(normInf_16f), + (NormFunc)GET_OPTIMIZED(normInf_16bf), + (NormFunc)normInf_Bool, + (NormFunc)GET_OPTIMIZED(normInf_64u), + (NormFunc)GET_OPTIMIZED(normInf_64s), + (NormFunc)GET_OPTIMIZED(normInf_32u), + 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, + (NormFunc)GET_OPTIMIZED(normL1_16f), + (NormFunc)GET_OPTIMIZED(normL1_16bf), + (NormFunc)normL1_Bool, + (NormFunc)GET_OPTIMIZED(normL1_64u), + (NormFunc)GET_OPTIMIZED(normL1_64s), + (NormFunc)GET_OPTIMIZED(normL1_32u), + 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, + (NormFunc)GET_OPTIMIZED(normL2_16f), + (NormFunc)GET_OPTIMIZED(normL2_16bf), + (NormFunc)normL2_Bool, + (NormFunc)GET_OPTIMIZED(normL2_64u), + (NormFunc)GET_OPTIMIZED(normL2_64s), + (NormFunc)GET_OPTIMIZED(normL2_32u), + 0 + } + }; + + return normTab[normType][depth]; +} + +#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY + +CV_CPU_OPTIMIZATION_NAMESPACE_END + +} // cv:: 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 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 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 diff --git a/modules/imgproc/src/imgwarp.cpp b/modules/imgproc/src/imgwarp.cpp index 606efa97b4..cd3f171e63 100644 --- a/modules/imgproc/src/imgwarp.cpp +++ b/modules/imgproc/src/imgwarp.cpp @@ -3572,7 +3572,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| @@ -3584,12 +3584,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); @@ -3608,8 +3624,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 25ffc61a2a..01b033cbef 100644 --- a/modules/imgproc/test/test_imgwarp.cpp +++ b/modules/imgproc/test/test_imgwarp.cpp @@ -1131,5 +1131,40 @@ TEST(Imgproc_Resize, issue_26497) EXPECT_LE(maxv, 3.); } +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. */ diff --git a/modules/python/test/tests_common.py b/modules/python/test/tests_common.py index a204550b5f..6591733803 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): diff --git a/modules/stereo/src/stereosgbm.cpp b/modules/stereo/src/stereosgbm.cpp index 5423efb640..8bf047ed70 100644 --- a/modules/stereo/src/stereosgbm.cpp +++ b/modules/stereo/src/stereosgbm.cpp @@ -2344,21 +2344,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++ ) { 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() 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.