1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2025-03-04 16:42:12 +03:00
42 changed files with 3445 additions and 816 deletions
+2
View File
@@ -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
+248
View File
@@ -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 <riscv_vector.h>
#include <opencv2/core/base.hpp>
#include <opencv2/core/utility.hpp>
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 <typename T>
inline void flipFillBuffer(cv::AutoBuffer<T>& _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 <typename FlipVlen>
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<typename FlipVlen::TableElemType> 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 <typename FlipVlen>
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<typename FlipVlen::TableElemType> 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<FlipVlen256>(esz, src_data, src_step, src_width, src_height, dst_data, dst_step);
else
flipY<FlipVlen512>(esz, src_data, src_step, src_width, src_height, dst_data, dst_step);
}
else
{
if (__riscv_vlenb() * 8 <= 256)
flipXY<FlipVlen256>(esz, src_data, src_step, src_width, src_height, dst_data, dst_step);
else
flipXY<FlipVlen512>(esz, src_data, src_step, src_width, src_height, dst_data, dst_step);
}
return CV_HAL_ERROR_OK;
}
}} // namespace cv::cv_hal_rvv
+138 -179
View File
@@ -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;
+182
View File
@@ -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 <riscv_vector.h>
#include <opencv2/core/base.hpp>
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 <typename CellType>
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 <typename CellType>
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 <typename T>
static inline void preprocess([[maybe_unused]] T& v, [[maybe_unused]] size_t len)
{
}
template <typename T>
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 <typename T>
static inline void preprocess(T& v, size_t len)
{
v = __riscv_vor(v, __riscv_vsrl(v, 1, len), len);
}
template <typename T>
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 <typename T>
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 <typename T>
static inline size_t popcount(T v, vbool1_t mask, size_t len_bool)
{
return __riscv_vcpop(mask, v, len_bool);
}
};
template <typename CellType>
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<CellType>(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<CellType>(v, mask, len * 8, result);
}
}
template <typename CellType>
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<CellType>(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<CellType>(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<NormHammingCell1>(a, n, _result); break;
case 2: normHamming8uLoop<NormHammingCell2>(a, n, _result); break;
case 4: normHamming8uLoop<NormHammingCell4>(a, n, _result); break;
default: return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
*result = static_cast<int>(_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<NormHammingCell1>(a, b, n, _result); break;
case 2: normHammingDiff8uLoop<NormHammingCell2>(a, b, n, _result); break;
case 4: normHammingDiff8uLoop<NormHammingCell4>(a, b, n, _result); break;
default: return CV_HAL_ERROR_NOT_IMPLEMENTED;
}
*result = static_cast<int>(_result);
return CV_HAL_ERROR_OK;
}
}} // namespace cv::cv_hal_rvv
+133 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+1
View File
@@ -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) {
+217 -7
View File
@@ -52,7 +52,7 @@
#ifdef __SSSE3__
#include <tmmintrin.h>
#endif
#ifdef __AVX2__
#if (defined(__AVX2__) || defined(__AVX512F__))
#include <immintrin.h>
#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); */
+3 -3
View File
@@ -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
+449 -71
View File
@@ -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) {
+30
View File
@@ -466,6 +466,24 @@ typedef struct opj_cp {
/* <<UniPG */
} opj_cp_t;
/** Entry of a TLM marker segment */
typedef struct opj_j2k_tlm_tile_part_info {
/** Tile index of the tile part. Ttlmi field */
OPJ_UINT16 m_tile_index;
/** Length in bytes, from the beginning of the SOT marker to the end of
* the bit stream data for that tile-part. Ptlmi field */
OPJ_UINT32 m_length;
} opj_j2k_tlm_tile_part_info_t;
/** Information got from the concatenation of TLM marker semgnets. */
typedef struct opj_j2k_tlm_info {
/** Number of entries in m_tile_part_infos. */
OPJ_UINT32 m_entries_count;
/** Array of m_entries_count values. */
opj_j2k_tlm_tile_part_info_t* m_tile_part_infos;
OPJ_BOOL m_is_invalid;
} opj_j2k_tlm_info_t;
typedef struct opj_j2k_dec {
/** locate in which part of the codestream the decoder is (main header, tile header, end) */
@@ -499,6 +517,18 @@ typedef struct opj_j2k_dec {
OPJ_UINT32 m_numcomps_to_decode;
OPJ_UINT32 *m_comps_indices_to_decode;
opj_j2k_tlm_info_t m_tlm;
/** Below if used when there's TLM information available and we use
* opj_set_decoded_area() to a subset of all tiles.
*/
/* Current index in m_intersecting_tile_parts_offset[] to seek to */
OPJ_UINT32 m_idx_intersecting_tile_parts;
/* Number of elements of m_intersecting_tile_parts_offset[] */
OPJ_UINT32 m_num_intersecting_tile_parts;
/* Start offset of contributing tile parts */
OPJ_OFF_T* m_intersecting_tile_parts_offset;
/** to tell that a tile can be decoded. */
OPJ_BITFIELD m_can_decode : 1;
OPJ_BITFIELD m_discard_tiles : 1;
+8 -4
View File
@@ -1989,12 +1989,16 @@ OPJ_BOOL opj_jp2_setup_encoder(opj_jp2_t *jp2,
jp2->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;
}
}
+9 -5
View File
@@ -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
+3 -3
View File
@@ -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 */
+151 -2
View File
@@ -47,6 +47,9 @@
#ifdef __SSE2__
#include <emmintrin.h>
#endif
#if (defined(__AVX2__) || defined(__AVX512F__))
#include <immintrin.h>
#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) {
+35 -25
View File
@@ -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;
}
}
+3 -3
View File
@@ -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)
+4 -3
View File
@@ -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,
+6 -1
View File
@@ -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()
@@ -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 Qualcomms Snapdragon devices.
- A clean processor-agnostic hardware acceleration API, under which chipset vendors can hardware accelerate FastCV functions on Qualcomms 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=<eSDK install location>
```
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:
```
<ESDK_PATH>\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
```
<ESDK_PATH>\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 |
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -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
+11 -3
View File
@@ -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;
+8 -5
View File
@@ -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 )
{
+1
View File
@@ -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)
+46
View File
@@ -706,6 +706,28 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/ , ArithmMixedTest,
)
);
typedef perf::TestBaseWithParam<std::tuple<cv::Size, int, bool>> 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<std::tuple<cv::Size, int, perf::MatType>> 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
+45
View File
@@ -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> Size_MatType_FlipCode_t;
typedef perf::TestBaseWithParam<Size_MatType_FlipCode_t> 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
+6 -6
View File
@@ -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<v_float32>::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<v_float64>::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<v_float32>::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<v_float64>::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<v_float32>::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<v_float64>::vlanes();
const v_float64 vprescale = vx_setall_f64(exp_prescale);
const v_float64 vpostscale = vx_setall_f64(exp_postscale);
@@ -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<typename T, typename ST> int
normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result = std::max(result, normInf<T, ST>(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<typename T, typename ST> int
normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result += normL1<T, ST>(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<typename T, typename ST> int
normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result += normL2Sqr<T, ST>(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<typename T, typename ST> 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##_<type, ntype>(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##_<type, ntype>(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<float>();
const uchar* data = src.ptr<const uchar>();
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);
}
}
+200
View File
@@ -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<typename T, typename ST> 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 <typename T, typename ST> 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<vint32m1_t, vuint32m1_t>(__riscv_vle32_v_i32m1(src + j, vle32m1));
r0 = __riscv_vmaxu(r0, v0, vle32m1);
vuint32m1_t v1 = __riscv_vabs<vint32m1_t, vuint32m1_t>(__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 <typename T, typename ST> 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<vint8m1_t, vuint8m1_t>(__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<vint8m1_t, vuint8m1_t>(__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<vint16m1_t, vuint16m1_t>(__riscv_vle16_v_i16m1(src + j, vle16m1));
r0 = __riscv_vwredsumu(v0, r0, vle16m1);
vuint16m1_t v1 = __riscv_vabs<vint16m1_t, vuint16m1_t>(__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 <typename T, typename ST> 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::
+779
View File
@@ -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 <typename T, typename ST>
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 <typename T, typename ST>
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 <typename T, typename ST>
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<uchar, int> {
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<v_uint8>::vlanes(); j += 4 * VTraits<v_uint8>::vlanes()) {
r0 = v_max(r0, vx_load(src + j ));
r1 = v_max(r1, vx_load(src + j + VTraits<v_uint8>::vlanes()));
r2 = v_max(r2, vx_load(src + j + 2 * VTraits<v_uint8>::vlanes()));
r3 = v_max(r3, vx_load(src + j + 3 * VTraits<v_uint8>::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<schar, int> {
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<v_int8>::vlanes(); j += 4 * VTraits<v_int8>::vlanes()) {
r0 = v_max(r0, v_abs(vx_load(src + j )));
r1 = v_max(r1, v_abs(vx_load(src + j + VTraits<v_int8>::vlanes())));
r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits<v_int8>::vlanes())));
r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits<v_int8>::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<int>(v_reduce_max(r0)));
}
};
template<>
struct NormInf_SIMD<ushort, int> {
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<v_uint16>::vlanes(); j += 4 * VTraits<v_uint16>::vlanes()) {
d0 = v_max(d0, vx_load(src + j ));
d1 = v_max(d1, vx_load(src + j + VTraits<v_uint16>::vlanes()));
d2 = v_max(d2, vx_load(src + j + 2 * VTraits<v_uint16>::vlanes()));
d3 = v_max(d3, vx_load(src + j + 3 * VTraits<v_uint16>::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<short, int> {
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<v_int16>::vlanes(); j += 4 * VTraits<v_int16>::vlanes()) {
d0 = v_max(d0, v_abs(vx_load(src + j )));
d1 = v_max(d1, v_abs(vx_load(src + j + VTraits<v_int16>::vlanes())));
d2 = v_max(d2, v_abs(vx_load(src + j + 2 * VTraits<v_int16>::vlanes())));
d3 = v_max(d3, v_abs(vx_load(src + j + 3 * VTraits<v_int16>::vlanes())));
}
d0 = v_max(d0, v_max(d1, v_max(d2, d3)));
for (; j < n; j++) {
s = std::max(s, saturate_cast<int>(cv_abs(src[j])));
}
return std::max(s, saturate_cast<int>(v_reduce_max(d0)));
}
};
template<>
struct NormInf_SIMD<int, int> {
int operator() (const int* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normInf_rvv<int, int>(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<v_int32>::vlanes(); j += 4 * VTraits<v_int32>::vlanes()) {
r0 = v_max(r0, v_abs(vx_load(src + j )));
r1 = v_max(r1, v_abs(vx_load(src + j + VTraits<v_int32>::vlanes())));
r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits<v_int32>::vlanes())));
r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits<v_int32>::vlanes())));
}
r0 = v_max(r0, v_max(r1, v_max(r2, r3)));
s = std::max(s, saturate_cast<int>(v_reduce_max(r0)));
#endif
for (; j < n; j++) {
s = std::max(s, std::abs(src[j]));
}
return s;
}
};
template<>
struct NormInf_SIMD<float, float> {
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<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes()) {
r0 = v_max(r0, v_abs(vx_load(src + j )));
r1 = v_max(r1, v_abs(vx_load(src + j + VTraits<v_float32>::vlanes())));
r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits<v_float32>::vlanes())));
r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits<v_float32>::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<uchar, int> {
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<v_uint8>::vlanes(); j += 2 * VTraits<v_uint8>::vlanes()) {
v_uint8 v0 = vx_load(src + j);
r0 = v_dotprod_expand_fast(v0, one, r0);
v_uint8 v1 = vx_load(src + j + VTraits<v_uint8>::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<schar, int> {
int operator() (const schar* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normL1_rvv<schar, int>(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<v_int8>::vlanes(); j += 2 * VTraits<v_int8>::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<v_int8>::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<int>(cv_abs(src[j]));
}
return s;
}
};
template<>
struct NormL1_SIMD<ushort, int> {
int operator() (const ushort* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normL1_rvv<ushort, int>(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<v_uint16>::vlanes(); j += 2 * VTraits<v_uint16>::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<v_uint16>::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<short, int> {
int operator() (const short* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normL1_rvv<short, int>(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<v_int16>::vlanes(); j += 2 * VTraits<v_int16>::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<v_int16>::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<int>(cv_abs(src[j]));
}
return s;
}
};
template<>
struct NormL2_SIMD<uchar, int> {
int operator() (const uchar* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normL2_rvv<uchar, int>(src, n, j);
#else
v_uint32 r0 = vx_setzero_u32(), r1 = vx_setzero_u32();
for (; j <= n - 2 * VTraits<v_uint8>::vlanes(); j += 2 * VTraits<v_uint8>::vlanes()) {
v_uint8 v0 = vx_load(src + j);
r0 = v_dotprod_expand_fast(v0, v0, r0);
v_uint8 v1 = vx_load(src + j + VTraits<v_uint8>::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<int>(src[j]);
s += v * v;
}
return s;
}
};
template<>
struct NormL2_SIMD<schar, int> {
int operator() (const schar* src, int n) const {
int j = 0;
int s = 0;
#if CV_RVV
s = normL2_rvv<schar, int>(src, n, j);
#else
v_int32 r0 = vx_setzero_s32(), r1 = vx_setzero_s32();
for (; j <= n - 2 * VTraits<v_int8>::vlanes(); j += 2 * VTraits<v_int8>::vlanes()) {
v_int8 v0 = vx_load(src + j);
r0 = v_dotprod_expand_fast(v0, v0, r0);
v_int8 v1 = vx_load(src + j + VTraits<v_int8>::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<int>(src[j]);
s += v * v;
}
return s;
}
};
#endif
#if (CV_SIMD_64F || CV_SIMD_SCALABLE_64F)
template<>
struct NormInf_SIMD<double, double> {
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<v_float64>::vlanes(); j += 4 * VTraits<v_float64>::vlanes()) {
r0 = v_max(r0, v_abs(vx_load(src + j )));
r1 = v_max(r1, v_abs(vx_load(src + j + VTraits<v_float64>::vlanes())));
r2 = v_max(r2, v_abs(vx_load(src + j + 2 * VTraits<v_float64>::vlanes())));
r3 = v_max(r3, v_abs(vx_load(src + j + 3 * VTraits<v_float64>::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<v_float64>::max_nlanes];
vx_store(t, r0);
for (int i = 0; i < VTraits<v_float64>::vlanes(); i++) {
s = std::max(s, cv_abs(t[i]));
}
return s;
}
};
template<>
struct NormL1_SIMD<int, double> {
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<v_int32>::vlanes(); j += 2 * VTraits<v_int32>::vlanes()) {
v_float32 v0 = v_abs(v_cvt_f32(vx_load(src + j))), v1 = v_abs(v_cvt_f32(vx_load(src + j + VTraits<v_int32>::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<float, double> {
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<v_float32>::vlanes(); j += 4 * VTraits<v_float32>::vlanes()) {
v_float32 v0 = v_abs(vx_load(src + j)), v1 = v_abs(vx_load(src + j + VTraits<v_float32>::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<v_float32>::vlanes())), v3 = v_abs(vx_load(src + j + 3 * VTraits<v_float32>::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, double> {
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<double, double>(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<v_float64>::vlanes(); j += 4 * VTraits<v_float64>::vlanes()) {
r00 = v_add(r00, v_abs(vx_load(src + j )));
r01 = v_add(r01, v_abs(vx_load(src + j + VTraits<v_float64>::vlanes())));
r10 = v_add(r10, v_abs(vx_load(src + j + 2 * VTraits<v_float64>::vlanes())));
r11 = v_add(r11, v_abs(vx_load(src + j + 3 * VTraits<v_float64>::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<ushort, double> {
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<v_uint16>::vlanes(); j += 2 * VTraits<v_uint16>::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<v_uint16>::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<double>(src[j]);
s += v * v;
}
return s;
}
};
template<>
struct NormL2_SIMD<short, double> {
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<v_int16>::vlanes(); j += 2 * VTraits<v_int16>::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<v_int16>::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<double>(src[j]);
s += v * v;
}
return s;
}
};
template<>
struct NormL2_SIMD<int, double> {
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<v_int32>::vlanes(); j += 2 * VTraits<v_int32>::vlanes()) {
v_int32 v0 = vx_load(src + j);
r0 = v_dotprod_expand_fast(v0, v0, r0);
v_int32 v1 = vx_load(src + j + VTraits<v_int32>::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<float, double> {
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<v_float32>::vlanes(); j += 2 * VTraits<v_float32>::vlanes()) {
v_float32 v0 = vx_load(src + j), v1 = vx_load(src + j + VTraits<v_float32>::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, double> {
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<double, double>(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<v_float64>::vlanes(); j += 4 * VTraits<v_float64>::vlanes()) {
v_float64 v00 = vx_load(src + j );
v_float64 v01 = vx_load(src + j + VTraits<v_float64>::vlanes());
v_float64 v10 = vx_load(src + j + 2 * VTraits<v_float64>::vlanes());
v_float64 v11 = vx_load(src + j + 3 * VTraits<v_float64>::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<typename T, typename ST> int
normInf_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result = std::max(result, normInf<T, ST>(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<typename T, typename ST> int
normL1_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result += normL1<T, ST>(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<typename T, typename ST> int
normL2_(const T* src, const uchar* mask, ST* _result, int len, int cn)
{
ST result = *_result;
if( !mask )
{
result += normL2Sqr<T, ST>(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::
+142 -147
View File
@@ -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<float>(), dst.ptr<float>(), n);
else
hal::exp64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_LOG:
if( depth == CV_32F )
hal::log32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::log64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_SQRT:
if( depth == CV_32F )
hal::sqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::sqrt64f(src.ptr<double>(), dst.ptr<double>(), 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<std::tuple<int, HALFunc> > 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<int> 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<float>(), dst.ptr<float>(), n);
else
hal::exp64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_LOG:
if( depth == CV_32F )
hal::log32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::log64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_SQRT:
if( depth == CV_32F )
hal::sqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::sqrt64f(src.ptr<double>(), dst.ptr<double>(), n);
break;
case HAL_INV_SQRT:
if( depth == CV_32F )
hal::invSqrt32f(src.ptr<float>(), dst.ptr<float>(), n);
else
hal::invSqrt64f(src.ptr<double>(), dst.ptr<double>(), 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<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
hal::LU64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
case HAL_CHOL:
if( depth == CV_32F )
hal::Cholesky32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
hal::Cholesky64f(a.ptr<double>(), a.step, size, x.ptr<double>(), 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>([](float& v, const int*) -> void { v = std::exp(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::exp(v); });
break;
case HAL_LOG:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::log(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::log(v); });
break;
case HAL_SQRT:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::sqrt(v); });
else
dst0.forEach<double>([](double& v, const int*) -> void { v = std::sqrt(v); });
break;
case HAL_INV_SQRT:
if( depth == CV_32F )
dst0.forEach<float>([](float& v, const int*) -> void { v = std::pow(v, -0.5f); });
else
dst0.forEach<double>([](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<std::tuple<int, HALFunc, int> > 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<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
solveStatus = hal::LU64f(a.ptr<double>(), a.step, size, x.ptr<double>(), x.step, 1);
break;
case HAL_CHOL:
if( depth == CV_32F )
solveStatus = hal::Cholesky32f(a.ptr<float>(), a.step, size, x.ptr<float>(), x.step, 1);
else
solveStatus = hal::Cholesky64f(a.ptr<double>(), a.step, size, x.ptr<double>(), 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
+71 -65
View File
@@ -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<GifDisposeMethod>(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<Vec4b>(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<Vec4b>(top + i, left + j) =
Vec4b(globalColorTable[colorIdx * 3 + 2], // B
globalColorTable[colorIdx * 3 + 1], // G
globalColorTable[colorIdx * 3], // R
0); // A
} else {
img.at<Vec4b>(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<uint8_t>(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<int>(GIF_DISPOSE_RESTORE_PREVIOUS << GIF_DISPOSE_METHOD_SHIFT) |
static_cast<int>(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<Mat> img_vec(1, img);
getColorTable(img_vec, false);
+28 -8
View File
@@ -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.
// <Packed Fields>
// 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<uchar> prefix;
};
void readExtensions();
GifDisposeMethod readExtensions();
void code2pixel(Mat& img, int start, int k);
bool lzwDecode();
bool getFrameCount_();
+48
View File
@@ -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<Vec4b>(j,j);
EXPECT_EQ( p[2], refIds[i][j] * 32 ) << " i = " << i << " j = " << j << " pixels = " << p;
}
}
}
}//opencv_test
}//namespace
+32
View File
@@ -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<testing::tuple<string, int, size_t>> 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<uchar> 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
+36 -4
View File
@@ -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<double>()[8] = 1.;
if (solve(A, B, X8, solveMethod) && norm(A * X8, B) < 1e-8)
{
M.ptr<double>()[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;
}
+35
View File
@@ -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<double>(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<double>(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. */
+1 -1
View File
@@ -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):
+14 -2
View File
@@ -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++ )
{
+6 -2
View File
@@ -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()
include(CheckLibraryExists)
CHECK_LIBRARY_EXISTS(regex regexec "" HAVE_REGEX_LIBRARY)
if(HAVE_REGEX_LIBRARY)
ocv_target_link_libraries(${the_module} PUBLIC regex)
endif()
endif()
@@ -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.