mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
Merge pull request #29369 from abhishek-gola:fp8_support
FP8 support in core module #29369 Core part of https://github.com/opencv/opencv/issues/29313 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -521,7 +521,10 @@ Cv64suf;
|
||||
CV_16U - 2 bytes
|
||||
...
|
||||
*/
|
||||
#define CV_ELEM_SIZE1(type) ((int)((0x4881228442211ULL >> (CV_MAT_DEPTH(type) * 4)) & 15))
|
||||
#define CV_ELEM_SIZE1(type) \
|
||||
((int)((CV_MAT_DEPTH(type) < 16 \
|
||||
? (0x1114881228442211ULL >> (CV_MAT_DEPTH(type) * 4)) \
|
||||
: (0x0000000000000001ULL >> ((CV_MAT_DEPTH(type) - 16) * 4))) & 15))
|
||||
|
||||
#define CV_ELEM_SIZE(type) (CV_MAT_CN(type)*CV_ELEM_SIZE1(type))
|
||||
|
||||
@@ -979,6 +982,83 @@ protected:
|
||||
ushort w;
|
||||
};
|
||||
|
||||
namespace fp8_detail {
|
||||
|
||||
// round-half-up (ties away from zero) — deliberately NOT round-to-nearest-even / OCP-ONNX spec
|
||||
inline unsigned roundHalfUp(unsigned full, int shift)
|
||||
{
|
||||
if (shift <= 0) return full << (-shift);
|
||||
unsigned q = full >> shift, rem = full & ((1u << shift) - 1), half = 1u << (shift - 1);
|
||||
if (rem >= half) q++;
|
||||
return q;
|
||||
}
|
||||
inline float pow2(int n) { Cv32suf s; s.u = (unsigned)((n + 127) << 23); return s.f; }
|
||||
|
||||
// E4M3 encode; no inf, bias/fnuz select E4M3FN vs E4M3FNUZ
|
||||
inline uchar encodeE4M3(float x, int bias, bool fnuz)
|
||||
{
|
||||
Cv32suf in; in.f = x;
|
||||
unsigned u = in.u, sign = (u >> 31) & 1, e = (u >> 23) & 0xFF, m = u & 0x7FFFFF;
|
||||
const unsigned sbit = sign << 7;
|
||||
const unsigned nanc = fnuz ? 0x80u : (sbit | 0x7Fu);
|
||||
if (e == 0xFF) return (uchar)nanc;
|
||||
if (e == 0 && m == 0) return (uchar)(fnuz ? 0u : sbit);
|
||||
int newexp = (int)e - 127 + bias;
|
||||
const unsigned full = (1u << 23) | m;
|
||||
if (newexp <= 0) // subnormal
|
||||
{
|
||||
const int shift = 20 + (1 - newexp);
|
||||
unsigned mant = (shift >= 32) ? 0u : roundHalfUp(full, shift);
|
||||
return (uchar)(mant == 0 ? (fnuz ? 0u : sbit) : (sbit | mant));
|
||||
}
|
||||
unsigned rounded = roundHalfUp(full, 20);
|
||||
if (rounded & 16u) { rounded >>= 1; newexp++; } // carry into exponent
|
||||
const unsigned mant = rounded & 7u;
|
||||
const bool ov = fnuz ? ((unsigned)newexp > 15)
|
||||
: ((unsigned)newexp > 15 || ((unsigned)newexp == 15 && mant == 7));
|
||||
if (ov) return (uchar)nanc;
|
||||
return (uchar)(sbit | ((unsigned)newexp << 3) | mant);
|
||||
}
|
||||
|
||||
inline float decodeE4M3(uchar b, int bias, bool fnuz)
|
||||
{
|
||||
const unsigned sign = ((unsigned)b >> 7) & 1, exp = ((unsigned)b >> 3) & 15, man = (unsigned)b & 7;
|
||||
const float s = sign ? -1.f : 1.f;
|
||||
Cv32suf qn; qn.u = sign ? 0xFFC00000u : 0x7FC00000u;
|
||||
if (fnuz) { if ((unsigned)b == 0x80u) return qn.f; }
|
||||
else if (exp == 15) { if (man == 7) return qn.f; }
|
||||
if (exp == 0) return s * (float)man * pow2(1 - bias - 3);
|
||||
return s * (1.0f + (float)man / 8.0f) * pow2((int)exp - bias);
|
||||
}
|
||||
|
||||
} // namespace fp8_detail
|
||||
|
||||
struct fp8_t // E4M3FN: bias 7, no inf, max 448
|
||||
{
|
||||
fp8_t() : b(0) {}
|
||||
explicit fp8_t(float x) : b(fp8_detail::encodeE4M3(x, 7, false)) {}
|
||||
operator float() const { return table()[b]; }
|
||||
static const float* decodeLUT() { return table(); }
|
||||
protected:
|
||||
uchar b;
|
||||
private:
|
||||
static const float* table()
|
||||
{ static const struct T { float v[256]; T() { for (int i = 0; i < 256; i++) v[i] = fp8_detail::decodeE4M3((uchar)i, 7, false); } } t; return t.v; }
|
||||
};
|
||||
|
||||
struct fp8a_t // E4M3FNUZ: bias 8, no inf, single NaN, no -0, max 240
|
||||
{
|
||||
fp8a_t() : b(0) {}
|
||||
explicit fp8a_t(float x) : b(fp8_detail::encodeE4M3(x, 8, true)) {}
|
||||
operator float() const { return table()[b]; }
|
||||
static const float* decodeLUT() { return table(); }
|
||||
protected:
|
||||
uchar b;
|
||||
private:
|
||||
static const float* table()
|
||||
{ static const struct T { float v[256]; T() { for (int i = 0; i < 256; i++) v[i] = fp8_detail::decodeE4M3((uchar)i, 8, true); } } t; return t.v; }
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -64,12 +64,16 @@ typedef int16_t cv_hal_bf16;
|
||||
#define CV_64U 10
|
||||
#define CV_64S 11
|
||||
#define CV_32U 12
|
||||
#define CV_DEPTH_CURR_MAX 13
|
||||
#define CV_8F_E4M3FN 13 // OCP/ONNX FLOAT8E4M3FN (1-4-3, bias 7, no inf, max 448)
|
||||
#define CV_8F_E4M3FNUZ 14 // FLOAT8E4M3FNUZ (1-4-3, bias 8, no inf, single NaN, no -0)
|
||||
#define CV_8F CV_8F_E4M3FN // alias for the main 8-bit floating-point type
|
||||
#define CV_DEPTH_CURR_MAX 15
|
||||
|
||||
#define CV_MAT_DEPTH_MASK (CV_DEPTH_MAX - 1)
|
||||
#define CV_MAT_DEPTH(flags) ((flags) & CV_MAT_DEPTH_MASK)
|
||||
#define CV_IS_INT_TYPE(flags) (((1 << CV_MAT_DEPTH(flags)) & 0x1e1f) != 0)
|
||||
#define CV_IS_FLOAT_TYPE(flags) (((1 << CV_MAT_DEPTH(flags)) & 0x1e0) != 0)
|
||||
// float family: 32F,64F,16F,16BF (bits 5-8) + the four FP8 depths (bits 13-16)
|
||||
#define CV_IS_FLOAT_TYPE(flags) (((1 << CV_MAT_DEPTH(flags)) & 0x1e1e0) != 0)
|
||||
|
||||
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
|
||||
#define CV_MAKE_TYPE CV_MAKETYPE
|
||||
@@ -152,6 +156,17 @@ typedef int16_t cv_hal_bf16;
|
||||
#define CV_16BFC4 CV_MAKETYPE(CV_16BF,4)
|
||||
#define CV_16BFC(n) CV_MAKETYPE(CV_16BF,(n))
|
||||
|
||||
#define CV_8F_E4M3FNC1 CV_MAKETYPE(CV_8F_E4M3FN,1)
|
||||
#define CV_8F_E4M3FNC(n) CV_MAKETYPE(CV_8F_E4M3FN,(n))
|
||||
#define CV_8F_E4M3FNUZC1 CV_MAKETYPE(CV_8F_E4M3FNUZ,1)
|
||||
#define CV_8F_E4M3FNUZC(n) CV_MAKETYPE(CV_8F_E4M3FNUZ,(n))
|
||||
|
||||
#define CV_8FC1 CV_MAKETYPE(CV_8F,1)
|
||||
#define CV_8FC2 CV_MAKETYPE(CV_8F,2)
|
||||
#define CV_8FC3 CV_MAKETYPE(CV_8F,3)
|
||||
#define CV_8FC4 CV_MAKETYPE(CV_8F,4)
|
||||
#define CV_8FC(n) CV_MAKETYPE(CV_8F,(n))
|
||||
|
||||
//! @name Comparison operation
|
||||
//! @sa cv::CmpTypes
|
||||
//! @{
|
||||
|
||||
@@ -672,6 +672,8 @@ CV_EXPORTS void write( FileStorage& fs, const String& name, double value );
|
||||
CV_EXPORTS void write( FileStorage& fs, const String& name, const String& value );
|
||||
CV_EXPORTS void write( FileStorage& fs, const String& name, const Mat& value );
|
||||
CV_EXPORTS void write( FileStorage& fs, const String& name, const SparseMat& value );
|
||||
static inline void write( FileStorage& fs, const String& name, const fp8_t& value ) { write( fs, name, (float)value ); }
|
||||
static inline void write( FileStorage& fs, const String& name, const fp8a_t& value ) { write( fs, name, (float)value ); }
|
||||
#ifdef CV__LEGACY_PERSISTENCE
|
||||
CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector<KeyPoint>& value);
|
||||
CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector<DMatch>& value);
|
||||
@@ -690,6 +692,10 @@ CV_EXPORTS void read(const FileNode& node, double& value, double default_value);
|
||||
CV_EXPORTS void read(const FileNode& node, std::string& value, const std::string& default_value);
|
||||
CV_EXPORTS void read(const FileNode& node, Mat& mat, const Mat& default_mat = Mat() );
|
||||
CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat = SparseMat() );
|
||||
static inline void read(const FileNode& node, fp8_t& value, const fp8_t& default_value = fp8_t())
|
||||
{ float f; read(node, f, (float)default_value); value = fp8_t(f); }
|
||||
static inline void read(const FileNode& node, fp8a_t& value, const fp8a_t& default_value = fp8a_t())
|
||||
{ float f; read(node, f, (float)default_value); value = fp8a_t(f); }
|
||||
#ifdef CV__LEGACY_PERSISTENCE
|
||||
CV_EXPORTS void read(const FileNode& node, std::vector<KeyPoint>& keypoints);
|
||||
CV_EXPORTS void read(const FileNode& node, std::vector<DMatch>& matches);
|
||||
|
||||
@@ -207,6 +207,28 @@ template<> inline bool saturate_cast<bool>(int64_t v){ return v != 0; }
|
||||
template<> inline bool saturate_cast<bool>(hfloat v){ return (float)v != 0; }
|
||||
template<> inline bool saturate_cast<bool>(bfloat v){ return (float)v != 0; }
|
||||
|
||||
// saturate_cast for the FP8 family — routes through float, mirroring hfloat/bfloat above.
|
||||
#define CV_FP8_SATURATE_CAST(T) \
|
||||
template<typename _Tp> static inline _Tp saturate_cast(T v) { return saturate_cast<_Tp>((float)v); } \
|
||||
template<> inline T saturate_cast<T>(uchar v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(schar v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(ushort v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(short v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(unsigned v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(int v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(uint64 v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(int64 v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(float v) { return T(v); } \
|
||||
template<> inline T saturate_cast<T>(double v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(hfloat v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(bfloat v) { return T((float)v); } \
|
||||
template<> inline T saturate_cast<T>(T v) { return v; } \
|
||||
template<> inline bool saturate_cast<bool>(T v) { return (float)v != 0; }
|
||||
|
||||
CV_FP8_SATURATE_CAST(fp8_t)
|
||||
CV_FP8_SATURATE_CAST(fp8a_t)
|
||||
#undef CV_FP8_SATURATE_CAST
|
||||
|
||||
//! @}
|
||||
|
||||
} // cv
|
||||
|
||||
@@ -336,6 +336,28 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
template<> class DataType<fp8_t>
|
||||
{
|
||||
public:
|
||||
typedef fp8_t value_type;
|
||||
typedef float work_type;
|
||||
typedef value_type channel_type;
|
||||
typedef value_type vec_type;
|
||||
enum { generic_type = 0, depth = CV_8F_E4M3FN, channels = 1,
|
||||
fmt = (int)'e', type = CV_MAKETYPE(depth, channels) };
|
||||
};
|
||||
|
||||
template<> class DataType<fp8a_t>
|
||||
{
|
||||
public:
|
||||
typedef fp8a_t value_type;
|
||||
typedef float work_type;
|
||||
typedef value_type channel_type;
|
||||
typedef value_type vec_type;
|
||||
enum { generic_type = 0, depth = CV_8F_E4M3FNUZ, channels = 1,
|
||||
fmt = (int)'E', type = CV_MAKETYPE(depth, channels) };
|
||||
};
|
||||
|
||||
/** @brief A helper class for cv::DataType
|
||||
|
||||
The class is specialized for each fundamental numerical data type supported by OpenCV. It provides
|
||||
@@ -434,6 +456,18 @@ template<> class TypeDepth<CV_16BF>
|
||||
typedef bfloat value_type;
|
||||
};
|
||||
|
||||
template<> class TypeDepth<CV_8F_E4M3FN>
|
||||
{
|
||||
enum { depth = CV_8F_E4M3FN };
|
||||
typedef fp8_t value_type;
|
||||
};
|
||||
|
||||
template<> class TypeDepth<CV_8F_E4M3FNUZ>
|
||||
{
|
||||
enum { depth = CV_8F_E4M3FNUZ };
|
||||
typedef fp8a_t value_type;
|
||||
};
|
||||
|
||||
template<> class TypeDepth<CV_Bool>
|
||||
{
|
||||
enum { depth = CV_Bool };
|
||||
|
||||
@@ -46,7 +46,8 @@ static const char* getTestOpMath(unsigned testOp)
|
||||
const char* depthToString_(int depth)
|
||||
{
|
||||
static const char* depthNames[] = { "CV_8U", "CV_8S", "CV_16U", "CV_16S", "CV_32S", "CV_32F", "CV_64F", "CV_16F",
|
||||
"CV_16BF", "CV_Bool", "CV_64U", "CV_64S", "CV_32U" };
|
||||
"CV_16BF", "CV_Bool", "CV_64U", "CV_64S", "CV_32U",
|
||||
"CV_8F", "CV_8FNUZ" };
|
||||
return (depth < CV_DEPTH_CURR_MAX && depth >= 0) ? depthNames[depth] : NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,11 @@ static bool ocl_convertTo(InputArray src_, OutputArray dst_, int ddepth, bool no
|
||||
int sdepth = CV_MAT_DEPTH(stype);
|
||||
int cn = CV_MAT_CN(stype);
|
||||
|
||||
// FP8 types are not supported by the OpenCL kernels yet; fall back to CPU.
|
||||
if ((sdepth >= CV_8F_E4M3FN && sdepth <= CV_8F_E4M3FNUZ) ||
|
||||
(ddepth >= CV_8F_E4M3FN && ddepth <= CV_8F_E4M3FNUZ))
|
||||
return false;
|
||||
|
||||
int dtype = CV_MAKETYPE(ddepth, cn);
|
||||
|
||||
int wdepth = (sdepth == CV_64F) ? CV_64F : CV_32F;
|
||||
|
||||
@@ -508,6 +508,55 @@ static void cvt32s(const uchar* src, size_t sstep, const uchar*, size_t, uchar*
|
||||
static void cvt64s(const uchar* src, size_t sstep, const uchar*, size_t, uchar* dst, size_t dstep, Size size, void*)
|
||||
{ CV_INSTRUMENT_REGION(); cvtCopy((const uchar*)src, sstep, (uchar*)dst, dstep, size, 8); }
|
||||
|
||||
//////////////////// FP8 (1-byte float) conversions — scalar via saturate_cast ////////////////////
|
||||
// Suffixes: 8fe4m3 (E4M3FN), 8fe4m3u (E4M3FNUZ).
|
||||
#define DEF_CVT_FP8(S, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##8u, T, uchar) DEF_CVT_SCALAR_FUNC(8u##S, uchar, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##8s, T, schar) DEF_CVT_SCALAR_FUNC(8s##S, schar, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##16u, T, ushort) DEF_CVT_SCALAR_FUNC(16u##S, ushort, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##16s, T, short) DEF_CVT_SCALAR_FUNC(16s##S, short, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##32u, T, unsigned) DEF_CVT_SCALAR_FUNC(32u##S, unsigned, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##32s, T, int) DEF_CVT_SCALAR_FUNC(32s##S, int, T) \
|
||||
DEF_CVT_SCALAR_FUNC(32f##S, float, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##64f, T, double) DEF_CVT_SCALAR_FUNC(64f##S, double, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##16f, T, hfloat) DEF_CVT_SCALAR_FUNC(16f##S, hfloat, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##16bf, T, bfloat) DEF_CVT_SCALAR_FUNC(16bf##S, bfloat, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##64u, T, uint64_t) DEF_CVT_SCALAR_FUNC(64u##S, uint64_t, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##64s, T, int64_t) DEF_CVT_SCALAR_FUNC(64s##S, int64_t, T) \
|
||||
DEF_CVT_SCALAR_FUNC(S##8b, T, bool)
|
||||
|
||||
DEF_CVT_FP8(8fe4m3, fp8_t)
|
||||
DEF_CVT_FP8(8fe4m3u, fp8a_t)
|
||||
|
||||
// FP8 -> FP8, cross-format only (identity uses the 1-byte copy cvt8u, like 16f->16f uses cvt16u)
|
||||
DEF_CVT_SCALAR_FUNC(8fe4m38fe4m3u, fp8_t, fp8a_t)
|
||||
DEF_CVT_SCALAR_FUNC(8fe4m3u8fe4m3, fp8a_t, fp8_t)
|
||||
|
||||
// FP8 -> float32: 256-entry decode table gathered via universal intrinsics (same table as scalar)
|
||||
template<typename FP8>
|
||||
static void cvtFp8ToF32(const uchar* src, size_t sstep, const uchar*, size_t,
|
||||
uchar* dst_, size_t dstep, Size size, void*)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
float* dst = (float*)dst_;
|
||||
const float* tab = FP8::decodeLUT();
|
||||
dstep /= sizeof(dst[0]);
|
||||
for (int i = 0; i < size.height; i++, src += sstep, dst += dstep)
|
||||
{
|
||||
int j = 0;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
const int VECSZ = VTraits<v_float32>::vlanes();
|
||||
for (; j <= size.width - VECSZ; j += VECSZ)
|
||||
v_store(dst + j, v_lut(tab, v_reinterpret_as_s32(vx_load_expand_q(src + j))));
|
||||
#endif
|
||||
for (; j < size.width; j++) dst[j] = tab[src[j]];
|
||||
}
|
||||
}
|
||||
static void cvt8fe4m332f(const uchar* s, size_t ss, const uchar* p, size_t ps, uchar* d, size_t ds, Size sz, void* x)
|
||||
{ cvtFp8ToF32<fp8_t>(s, ss, p, ps, d, ds, sz, x); }
|
||||
static void cvt8fe4m3u32f(const uchar* s, size_t ss, const uchar* p, size_t ps, uchar* d, size_t ds, Size sz, void* x)
|
||||
{ cvtFp8ToF32<fp8a_t>(s, ss, p, ps, d, ds, sz, x); }
|
||||
|
||||
BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
{
|
||||
int sdepth = CV_MAT_DEPTH(sdepth_);
|
||||
@@ -527,6 +576,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b8u :
|
||||
sdepth == CV_64U ? cvt64u8u :
|
||||
sdepth == CV_64S ? cvt64s8u :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m38u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u8u :
|
||||
0) :
|
||||
ddepth == CV_8S ? (
|
||||
sdepth == CV_8U ? cvt8u8s :
|
||||
@@ -542,6 +593,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b8u :
|
||||
sdepth == CV_64U ? cvt64u8s :
|
||||
sdepth == CV_64S ? cvt64s8s :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m38s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u8s :
|
||||
0) :
|
||||
ddepth == CV_16U ? (
|
||||
sdepth == CV_8U ? cvt8u16s : // same as cvt8u16u
|
||||
@@ -557,6 +610,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b16s :
|
||||
sdepth == CV_64U ? cvt64u16u :
|
||||
sdepth == CV_64S ? cvt64s16u :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m316u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u16u :
|
||||
0) :
|
||||
ddepth == CV_16S ? (
|
||||
sdepth == CV_8U ? cvt8u16s :
|
||||
@@ -572,6 +627,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b16s :
|
||||
sdepth == CV_64U ? cvt64u16s :
|
||||
sdepth == CV_64S ? cvt64s16s :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m316s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u16s :
|
||||
0) :
|
||||
ddepth == CV_32U ? (
|
||||
sdepth == CV_8U ? cvt8u32s : // same as cvt8u32u
|
||||
@@ -587,7 +644,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b32s :
|
||||
sdepth == CV_64U ? cvt64u32u :
|
||||
sdepth == CV_64S ? cvt64s32u :
|
||||
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m332u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u32u :
|
||||
0) :
|
||||
ddepth == CV_32S ? (
|
||||
sdepth == CV_8U ? cvt8u32s :
|
||||
@@ -603,6 +661,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b32s :
|
||||
sdepth == CV_64U ? cvt64u32s :
|
||||
sdepth == CV_64S ? cvt64s32s :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m332s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u32s :
|
||||
0) :
|
||||
ddepth == CV_32F ? (
|
||||
sdepth == CV_8U ? cvt8u32f :
|
||||
@@ -618,6 +678,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b32f :
|
||||
sdepth == CV_64U ? cvt64u32f :
|
||||
sdepth == CV_64S ? cvt64s32f :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m332f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u32f :
|
||||
0) :
|
||||
ddepth == CV_64F ? (
|
||||
sdepth == CV_8U ? cvt8u64f :
|
||||
@@ -633,6 +695,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b64f :
|
||||
sdepth == CV_64U ? cvt64u64f :
|
||||
sdepth == CV_64S ? cvt64s64f :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m364f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u64f :
|
||||
0) :
|
||||
ddepth == CV_16F ? (
|
||||
sdepth == CV_8U ? cvt8u16f :
|
||||
@@ -648,6 +712,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b16f :
|
||||
sdepth == CV_64U ? cvt64u16f :
|
||||
sdepth == CV_64S ? cvt64s16f :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m316f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u16f :
|
||||
0) :
|
||||
ddepth == CV_16BF ? (
|
||||
sdepth == CV_8U ? cvt8u16bf :
|
||||
@@ -663,6 +729,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b16bf :
|
||||
sdepth == CV_64U ? cvt64u16bf :
|
||||
sdepth == CV_64S ? cvt64s16bf :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m316bf :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u16bf :
|
||||
0) :
|
||||
ddepth == CV_Bool ? (
|
||||
sdepth == CV_8U ? cvt8u8b :
|
||||
@@ -678,6 +746,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8u :
|
||||
sdepth == CV_64U ? cvt64s8b :
|
||||
sdepth == CV_64S ? cvt64s8b :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m38b :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u8b :
|
||||
0) :
|
||||
ddepth == CV_64U ? (
|
||||
sdepth == CV_8U ? cvt8u64s : // same as cvt8u64u
|
||||
@@ -693,6 +763,8 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b64s :
|
||||
sdepth == CV_64U ? cvt64s :
|
||||
sdepth == CV_64S ? cvt64s64u :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m364u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u64u :
|
||||
0) :
|
||||
ddepth == CV_64S ? (
|
||||
sdepth == CV_8U ? cvt8u64s :
|
||||
@@ -708,6 +780,42 @@ BinaryFunc getConvertFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_Bool ? cvt8b64s :
|
||||
sdepth == CV_64U ? cvt64s :
|
||||
sdepth == CV_64S ? cvt64s :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m364s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u64s :
|
||||
0) :
|
||||
ddepth == CV_8F_E4M3FN ? (
|
||||
sdepth == CV_8U ? cvt8u8fe4m3 :
|
||||
sdepth == CV_8S ? cvt8s8fe4m3 :
|
||||
sdepth == CV_16U ? cvt16u8fe4m3 :
|
||||
sdepth == CV_16S ? cvt16s8fe4m3 :
|
||||
sdepth == CV_32U ? cvt32u8fe4m3 :
|
||||
sdepth == CV_32S ? cvt32s8fe4m3 :
|
||||
sdepth == CV_32F ? cvt32f8fe4m3 :
|
||||
sdepth == CV_64F ? cvt64f8fe4m3 :
|
||||
sdepth == CV_16F ? cvt16f8fe4m3 :
|
||||
sdepth == CV_16BF ? cvt16bf8fe4m3 :
|
||||
sdepth == CV_Bool ? cvt8u8fe4m3 : // bool stored as 0/1 byte -> reuse uchar path
|
||||
sdepth == CV_64U ? cvt64u8fe4m3 :
|
||||
sdepth == CV_64S ? cvt64s8fe4m3 :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8u : // identity: 1-byte copy
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8fe4m3u8fe4m3 :
|
||||
0) :
|
||||
ddepth == CV_8F_E4M3FNUZ ? (
|
||||
sdepth == CV_8U ? cvt8u8fe4m3u :
|
||||
sdepth == CV_8S ? cvt8s8fe4m3u :
|
||||
sdepth == CV_16U ? cvt16u8fe4m3u :
|
||||
sdepth == CV_16S ? cvt16s8fe4m3u :
|
||||
sdepth == CV_32U ? cvt32u8fe4m3u :
|
||||
sdepth == CV_32S ? cvt32s8fe4m3u :
|
||||
sdepth == CV_32F ? cvt32f8fe4m3u :
|
||||
sdepth == CV_64F ? cvt64f8fe4m3u :
|
||||
sdepth == CV_16F ? cvt16f8fe4m3u :
|
||||
sdepth == CV_16BF ? cvt16bf8fe4m3u :
|
||||
sdepth == CV_Bool ? cvt8u8fe4m3u : // bool stored as 0/1 byte -> reuse uchar path
|
||||
sdepth == CV_64U ? cvt64u8fe4m3u :
|
||||
sdepth == CV_64S ? cvt64s8fe4m3u :
|
||||
sdepth == CV_8F_E4M3FN ? cvt8fe4m38fe4m3u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvt8u : // identity: 1-byte copy
|
||||
0) :
|
||||
0;
|
||||
CV_Assert(func != 0);
|
||||
|
||||
@@ -22,9 +22,17 @@ template<typename _Ts, typename _Td> inline void
|
||||
cvtabs_32f( const _Ts* src, size_t sstep, _Td* dst, size_t dstep,
|
||||
Size size, float a, float b )
|
||||
{
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
// Excluding GNU in CV_SIMD_SCALABLE because of "opencv/issues/26936"
|
||||
#if (CV_SIMD || (CV_SIMD_SCALABLE && !(defined(__GNUC__) && !defined(__clang__))) )
|
||||
v_float32 va = vx_setall_f32(a), vb = vx_setall_f32(b);
|
||||
const int VECSZ = VTraits<v_float32>::vlanes()*2;
|
||||
// GCC miscompiles this scalable block only on VLEN=128 RVV (opencv/issues/26936).
|
||||
// v_float32 is LMUL=2, so vlanes()==8 means VLEN==128: use scalar there, keep SIMD elsewhere.
|
||||
#if (CV_SIMD_SCALABLE && defined(__GNUC__) && !defined(__clang__))
|
||||
const bool useSIMD = VTraits<v_float32>::vlanes() != 8;
|
||||
#else
|
||||
const bool useSIMD = true;
|
||||
#endif
|
||||
#endif
|
||||
sstep /= sizeof(src[0]);
|
||||
dstep /= sizeof(dst[0]);
|
||||
@@ -33,6 +41,7 @@ cvtabs_32f( const _Ts* src, size_t sstep, _Td* dst, size_t dstep,
|
||||
{
|
||||
int j = 0;
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||
if( useSIMD )
|
||||
for( ; j < size.width; j += VECSZ )
|
||||
{
|
||||
if( j > size.width - VECSZ )
|
||||
@@ -423,9 +432,27 @@ DEF_CVT_SCALEBOOL2_FUNC(8b64f, double, double)
|
||||
DEF_CVT_SCALEBOOL2_FUNC(8b16f, hfloat, float)
|
||||
DEF_CVT_SCALEBOOL2_FUNC(8b16bf, bfloat, float)
|
||||
|
||||
// FP8 src -> CV_8U abs|scale|shift (scalar)
|
||||
#define DEF_CVT_SCALE_ABS_SCALAR_FUNC(suffix, stype) \
|
||||
static void cvtScaleAbs##suffix( const uchar* src_, size_t sstep, const uchar*, size_t, \
|
||||
uchar* dst, size_t dstep, Size size, void* scale_) \
|
||||
{ \
|
||||
const stype* src = (const stype*)src_; \
|
||||
const double* scale = (const double*)scale_; \
|
||||
float a = (float)scale[0], b = (float)scale[1]; \
|
||||
sstep /= sizeof(src[0]); \
|
||||
for( int i = 0; i < size.height; i++, src += sstep, dst += dstep ) \
|
||||
for (int j = 0; j < size.width; j++) \
|
||||
dst[j] = saturate_cast<uchar>(std::abs((float)src[j]*a + b)); \
|
||||
}
|
||||
DEF_CVT_SCALE_ABS_SCALAR_FUNC(8fe4m3u8u, fp8a_t)
|
||||
DEF_CVT_SCALE_ABS_SCALAR_FUNC(8fe4m38u, fp8_t)
|
||||
|
||||
BinaryFunc getCvtScaleAbsFunc(int depth)
|
||||
{
|
||||
BinaryFunc func =
|
||||
depth == CV_8F_E4M3FN ? (BinaryFunc)cvtScaleAbs8fe4m38u :
|
||||
depth == CV_8F_E4M3FNUZ ? (BinaryFunc)cvtScaleAbs8fe4m3u8u :
|
||||
depth == CV_8U ? (BinaryFunc)cvtScaleAbs8u :
|
||||
depth == CV_8S ? (BinaryFunc)cvtScaleAbs8s8u :
|
||||
depth == CV_Bool ? (BinaryFunc)cvtScaleAbs8b8u :
|
||||
@@ -443,6 +470,40 @@ BinaryFunc getCvtScaleAbsFunc(int depth)
|
||||
return func;
|
||||
}
|
||||
|
||||
//////////////////// FP8 scaled conversions — scalar via saturate_cast(src*a+b) ////////////////////
|
||||
#define DEF_CVT_SCALE_SCALAR_FUNC(suffix, stype, dtype, wtype) \
|
||||
static void cvtScale##suffix( const uchar* src_, size_t sstep, const uchar*, size_t, \
|
||||
uchar* dst_, size_t dstep, Size size, void* scale_) \
|
||||
{ \
|
||||
const stype* src = (const stype*)src_; dtype* dst = (dtype*)dst_; \
|
||||
const double* scale = (const double*)scale_; \
|
||||
wtype a = (wtype)scale[0], b = (wtype)scale[1]; \
|
||||
sstep /= sizeof(src[0]); dstep /= sizeof(dst[0]); \
|
||||
for( int i = 0; i < size.height; i++, src += sstep, dst += dstep ) \
|
||||
for (int j = 0; j < size.width; j++) \
|
||||
dst[j] = saturate_cast<dtype>((wtype)src[j]*a + b); \
|
||||
}
|
||||
|
||||
// double intermediate so coarse FP8 grids round identically to the (double-precision) reference
|
||||
#define DEF_CVT_SCALE_FP8(S, T) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##8u, T, uchar, double) DEF_CVT_SCALE_SCALAR_FUNC(8u##S, uchar, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##8s, T, schar, double) DEF_CVT_SCALE_SCALAR_FUNC(8s##S, schar, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##16u, T, ushort, double) DEF_CVT_SCALE_SCALAR_FUNC(16u##S, ushort, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##16s, T, short, double) DEF_CVT_SCALE_SCALAR_FUNC(16s##S, short, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##32u, T, unsigned, double) DEF_CVT_SCALE_SCALAR_FUNC(32u##S, unsigned, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##32s, T, int, double) DEF_CVT_SCALE_SCALAR_FUNC(32s##S, int, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##32f, T, float, double) DEF_CVT_SCALE_SCALAR_FUNC(32f##S, float, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##64f, T, double, double) DEF_CVT_SCALE_SCALAR_FUNC(64f##S, double, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##16f, T, hfloat, double) DEF_CVT_SCALE_SCALAR_FUNC(16f##S, hfloat, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##16bf, T, bfloat, double) DEF_CVT_SCALE_SCALAR_FUNC(16bf##S, bfloat, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##64u, T, uint64_t, double) DEF_CVT_SCALE_SCALAR_FUNC(64u##S, uint64_t, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##64s, T, int64_t, double) DEF_CVT_SCALE_SCALAR_FUNC(64s##S, int64_t, T, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##8b, T, bool, double) \
|
||||
DEF_CVT_SCALE_SCALAR_FUNC(S##8fe4m3, T, fp8_t, double) DEF_CVT_SCALE_SCALAR_FUNC(S##8fe4m3u, T, fp8a_t, double)
|
||||
|
||||
DEF_CVT_SCALE_FP8(8fe4m3, fp8_t)
|
||||
DEF_CVT_SCALE_FP8(8fe4m3u, fp8a_t)
|
||||
|
||||
BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
{
|
||||
int sdepth = CV_MAT_DEPTH(sdepth_);
|
||||
@@ -462,6 +523,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf8u :
|
||||
sdepth == CV_64U ? cvtScale64u8u :
|
||||
sdepth == CV_64S ? cvtScale64s8u :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m38u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u8u :
|
||||
0) :
|
||||
ddepth == CV_8S ? (
|
||||
sdepth == CV_8U ? cvtScale8u8s :
|
||||
@@ -477,6 +540,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf8s :
|
||||
sdepth == CV_64U ? cvtScale64u8s :
|
||||
sdepth == CV_64S ? cvtScale64s8s :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m38s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u8s :
|
||||
0) :
|
||||
ddepth == CV_16U ? (
|
||||
sdepth == CV_8U ? cvtScale8u16u :
|
||||
@@ -492,6 +557,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf16u :
|
||||
sdepth == CV_64U ? cvtScale64u16u :
|
||||
sdepth == CV_64S ? cvtScale64s16u :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m316u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u16u :
|
||||
0) :
|
||||
ddepth == CV_16S ? (
|
||||
sdepth == CV_8U ? cvtScale8u16s :
|
||||
@@ -507,6 +574,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf16s :
|
||||
sdepth == CV_64U ? cvtScale64u16s :
|
||||
sdepth == CV_64S ? cvtScale64s16s :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m316s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u16s :
|
||||
0) :
|
||||
ddepth == CV_32U ? (
|
||||
sdepth == CV_8U ? cvtScale8u32u :
|
||||
@@ -522,7 +591,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf32u :
|
||||
sdepth == CV_64U ? cvtScale64u32u :
|
||||
sdepth == CV_64S ? cvtScale64s32u :
|
||||
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m332u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u32u :
|
||||
0) :
|
||||
ddepth == CV_32S ? (
|
||||
sdepth == CV_8U ? cvtScale8u32s :
|
||||
@@ -538,6 +608,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf32s :
|
||||
sdepth == CV_64U ? cvtScale64u32s :
|
||||
sdepth == CV_64S ? cvtScale64s32s :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m332s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u32s :
|
||||
0) :
|
||||
ddepth == CV_32F ? (
|
||||
sdepth == CV_8U ? cvtScale8u32f :
|
||||
@@ -553,6 +625,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf32f :
|
||||
sdepth == CV_64U ? cvtScale64u32f :
|
||||
sdepth == CV_64S ? cvtScale64s32f :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m332f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u32f :
|
||||
0) :
|
||||
ddepth == CV_64F ? (
|
||||
sdepth == CV_8U ? cvtScale8u64f :
|
||||
@@ -568,6 +642,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf64f :
|
||||
sdepth == CV_64U ? cvtScale64u64f :
|
||||
sdepth == CV_64S ? cvtScale64s64f :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m364f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u64f :
|
||||
0) :
|
||||
ddepth == CV_16F ? (
|
||||
sdepth == CV_8U ? cvtScale8u16f :
|
||||
@@ -583,6 +659,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf16f :
|
||||
sdepth == CV_64U ? cvtScale64u16f :
|
||||
sdepth == CV_64S ? cvtScale64s16f :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m316f :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u16f :
|
||||
0) :
|
||||
ddepth == CV_16BF ? (
|
||||
sdepth == CV_8U ? cvtScale8u16bf :
|
||||
@@ -598,6 +676,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf :
|
||||
sdepth == CV_64U ? cvtScale64u16bf :
|
||||
sdepth == CV_64S ? cvtScale64s16bf :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m316bf :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u16bf :
|
||||
0) :
|
||||
ddepth == CV_Bool ? (
|
||||
sdepth == CV_8U ? cvtScale8u8b :
|
||||
@@ -613,6 +693,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf8b :
|
||||
sdepth == CV_64U ? cvtScale64u8b :
|
||||
sdepth == CV_64S ? cvtScale64s8b :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m38b :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u8b :
|
||||
0) :
|
||||
ddepth == CV_64U ? (
|
||||
sdepth == CV_8U ? cvtScale8u64u :
|
||||
@@ -628,6 +710,8 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf64u :
|
||||
sdepth == CV_64U ? cvtScale64u :
|
||||
sdepth == CV_64S ? cvtScale64s64u :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m364u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u64u :
|
||||
0) :
|
||||
ddepth == CV_64S ? (
|
||||
sdepth == CV_8U ? cvtScale8u64s :
|
||||
@@ -643,6 +727,42 @@ BinaryFunc getConvertScaleFunc(int sdepth_, int ddepth_)
|
||||
sdepth == CV_16BF ? cvtScale16bf64s :
|
||||
sdepth == CV_64U ? cvtScale64u64s :
|
||||
sdepth == CV_64S ? cvtScale64s :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m364s :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u64s :
|
||||
0) :
|
||||
ddepth == CV_8F_E4M3FN ? (
|
||||
sdepth == CV_8U ? cvtScale8u8fe4m3 :
|
||||
sdepth == CV_8S ? cvtScale8s8fe4m3 :
|
||||
sdepth == CV_Bool ? cvtScale8u8fe4m3 : // bool stored as 0/1 byte -> reuse uchar path
|
||||
sdepth == CV_16U ? cvtScale16u8fe4m3 :
|
||||
sdepth == CV_16S ? cvtScale16s8fe4m3 :
|
||||
sdepth == CV_32U ? cvtScale32u8fe4m3 :
|
||||
sdepth == CV_32S ? cvtScale32s8fe4m3 :
|
||||
sdepth == CV_32F ? cvtScale32f8fe4m3 :
|
||||
sdepth == CV_64F ? cvtScale64f8fe4m3 :
|
||||
sdepth == CV_16F ? cvtScale16f8fe4m3 :
|
||||
sdepth == CV_16BF ? cvtScale16bf8fe4m3 :
|
||||
sdepth == CV_64U ? cvtScale64u8fe4m3 :
|
||||
sdepth == CV_64S ? cvtScale64s8fe4m3 :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m38fe4m3 :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u8fe4m3 :
|
||||
0) :
|
||||
ddepth == CV_8F_E4M3FNUZ ? (
|
||||
sdepth == CV_8U ? cvtScale8u8fe4m3u :
|
||||
sdepth == CV_8S ? cvtScale8s8fe4m3u :
|
||||
sdepth == CV_Bool ? cvtScale8u8fe4m3u : // bool stored as 0/1 byte -> reuse uchar path
|
||||
sdepth == CV_16U ? cvtScale16u8fe4m3u :
|
||||
sdepth == CV_16S ? cvtScale16s8fe4m3u :
|
||||
sdepth == CV_32U ? cvtScale32u8fe4m3u :
|
||||
sdepth == CV_32S ? cvtScale32s8fe4m3u :
|
||||
sdepth == CV_32F ? cvtScale32f8fe4m3u :
|
||||
sdepth == CV_64F ? cvtScale64f8fe4m3u :
|
||||
sdepth == CV_16F ? cvtScale16f8fe4m3u :
|
||||
sdepth == CV_16BF ? cvtScale16bf8fe4m3u :
|
||||
sdepth == CV_64U ? cvtScale64u8fe4m3u :
|
||||
sdepth == CV_64S ? cvtScale64s8fe4m3u :
|
||||
sdepth == CV_8F_E4M3FN ? cvtScale8fe4m38fe4m3u :
|
||||
sdepth == CV_8F_E4M3FNUZ ? cvtScale8fe4m3u8fe4m3u :
|
||||
0) :
|
||||
0;
|
||||
CV_Assert(func != 0);
|
||||
|
||||
@@ -92,6 +92,12 @@ void scalarToRawData(const Scalar& s, void* _buf, int type, int unroll_to)
|
||||
case CV_16BF:
|
||||
scalarToRawData_(s, (bfloat*)_buf, cn, unroll_to);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
scalarToRawData_(s, (fp8_t*)_buf, cn, unroll_to);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
scalarToRawData_(s, (fp8a_t*)_buf, cn, unroll_to);
|
||||
break;
|
||||
case CV_32U:
|
||||
scalarToRawData_(s, (unsigned*)_buf, cn, unroll_to);
|
||||
break;
|
||||
|
||||
@@ -575,7 +575,10 @@ void cv::cuda::GpuMat::convertTo(OutputArray _dst, int rtype, Stream& stream) co
|
||||
{convertToNoScale<uint32_t, uchar>, convertToNoScale<uint32_t, schar>, convertToNoScale<uint32_t, ushort>, convertToNoScale<uint32_t, short>, convertToNoScale<uint32_t, int>, convertToNoScale<uint32_t, float>, convertToNoScale<uint32_t, double>, 0, 0, 0, convertToNoScale<uint32_t, uint64_t>, convertToNoScale<uint32_t, int64_t>, 0},
|
||||
};
|
||||
|
||||
funcs[sdepth][ddepth](src.reshape(1), dst.reshape(1), stream);
|
||||
const func_t func = funcs[sdepth][ddepth];
|
||||
CV_Assert(func);
|
||||
|
||||
func(src.reshape(1), dst.reshape(1), stream);
|
||||
}
|
||||
|
||||
void cv::cuda::GpuMat::convertTo(OutputArray _dst, int rtype, double alpha, double beta, Stream& stream) const
|
||||
|
||||
@@ -178,12 +178,12 @@ char* floatToString( char* buf, size_t bufSize, float value, bool halfprecision,
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char symbols[] = "ucwsifdhHbUIn";
|
||||
static const char symbols[] = "ucwsifdhHbUIneE";
|
||||
|
||||
static char typeSymbol(int depth)
|
||||
{
|
||||
CV_StaticAssert(CV_64F == 6, "");
|
||||
CV_CheckDepth(depth, depth >= 0 && depth <= CV_32U, "");
|
||||
CV_CheckDepth(depth, depth >= 0 && depth <= CV_8F_E4M3FNUZ, "");
|
||||
return symbols[depth];
|
||||
}
|
||||
|
||||
@@ -310,6 +310,8 @@ int calcStructSize( const char* dt, int initial_size )
|
||||
case 'd': { elem_max_size = std::max( elem_max_size, sizeof(double) ); break; }
|
||||
case 'h': { elem_max_size = std::max( elem_max_size, sizeof(hfloat)); break; }
|
||||
case 'H': { elem_max_size = std::max( elem_max_size, sizeof(bfloat)); break; }
|
||||
case 'e': { elem_max_size = std::max( elem_max_size, sizeof(fp8_t) ); break; }
|
||||
case 'E': { elem_max_size = std::max( elem_max_size, sizeof(fp8a_t)); break; }
|
||||
case 'I': { elem_max_size = std::max( elem_max_size, sizeof(int64_t)); break; }
|
||||
case 'U': { elem_max_size = std::max( elem_max_size, sizeof(uint64_t)); break; }
|
||||
default:
|
||||
@@ -1229,6 +1231,14 @@ void FileStorage::Impl::writeRawData(const std::string &dt, const void *_data, s
|
||||
ptr = fs::floatToString(buf, sizeof(buf), (float) *(bfloat *) data, true, explicitZero);
|
||||
data += sizeof(bfloat);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
ptr = fs::floatToString(buf, sizeof(buf), (float) *(fp8_t *) data, true, explicitZero);
|
||||
data += sizeof(fp8_t);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
ptr = fs::floatToString(buf, sizeof(buf), (float) *(fp8a_t *) data, true, explicitZero);
|
||||
data += sizeof(fp8a_t);
|
||||
break;
|
||||
default:
|
||||
CV_Error(cv::Error::StsUnsupportedFormat, "Unsupported type");
|
||||
return;
|
||||
@@ -1930,6 +1940,14 @@ char *FileStorage::Impl::parseBase64(char *ptr, int indent, FileNode &collection
|
||||
fval = float(hfloatFromBits(base64decoder.getUInt16()));
|
||||
node_type = FileNode::REAL;
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
fval = fp8_t::decodeLUT()[base64decoder.getUInt8()];
|
||||
node_type = FileNode::REAL;
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
fval = fp8a_t::decodeLUT()[base64decoder.getUInt8()];
|
||||
node_type = FileNode::REAL;
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat, "Unsupported type");
|
||||
}
|
||||
@@ -2780,6 +2798,14 @@ FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, void* _data0, si
|
||||
*(bfloat*)data = bfloat((float)ival);
|
||||
data += sizeof(bfloat);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
*(fp8_t*)data = fp8_t((float)ival);
|
||||
data += sizeof(fp8_t);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
*(fp8a_t*)data = fp8a_t((float)ival);
|
||||
data += sizeof(fp8a_t);
|
||||
break;
|
||||
default:
|
||||
CV_Error( Error::StsUnsupportedFormat, "Unsupported type" );
|
||||
}
|
||||
@@ -2838,6 +2864,14 @@ FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, void* _data0, si
|
||||
*(bfloat*)data = bfloat((float)fval);
|
||||
data += sizeof(bfloat);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
*(fp8_t*)data = fp8_t((float)fval);
|
||||
data += sizeof(fp8_t);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
*(fp8a_t*)data = fp8a_t((float)fval);
|
||||
data += sizeof(fp8a_t);
|
||||
break;
|
||||
default:
|
||||
CV_Error( Error::StsUnsupportedFormat, "Unsupported type" );
|
||||
}
|
||||
|
||||
@@ -212,6 +212,8 @@ int base64::icvCalcStructSize(const char *dt, int initial_size) {
|
||||
case 'd': { elem_max_size = std::max( elem_max_size, sizeof(double) ); break; }
|
||||
case 'I': { elem_max_size = std::max( elem_max_size, sizeof(int64_t)); break; }
|
||||
case 'U': { elem_max_size = std::max( elem_max_size, sizeof(uint64_t)); break; }
|
||||
case 'e': { elem_max_size = std::max( elem_max_size, sizeof(uchar) ); break; }
|
||||
case 'E': { elem_max_size = std::max( elem_max_size, sizeof(uchar) ); break; }
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -361,6 +363,11 @@ size_t base64::RawDataToBinaryConvertor::make_to_binary_funcs(const std::string
|
||||
size = sizeof(uint64_t);
|
||||
pack.func = to_binary<uint64_t>;
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
size = sizeof(uchar);
|
||||
pack.func = to_binary<uchar>;
|
||||
break;
|
||||
case 'r':
|
||||
default:
|
||||
CV_Error(cv::Error::StsError, "type is not supported");
|
||||
|
||||
@@ -232,12 +232,20 @@ DEF_RANDI_FUNC(32s, int)
|
||||
DEF_RANDI_FUNC(64u, uint64_t)
|
||||
DEF_RANDI_FUNC(64s, int64_t)
|
||||
|
||||
// Narrow an f32 buffer into one of the 1-byte FP8 destinations.
|
||||
static inline void cvt32fToFP8(const float* src, void* dst, int len, int depth)
|
||||
{
|
||||
if (depth == CV_8F_E4M3FN) { fp8_t* d = (fp8_t*)dst; for (int i = 0; i < len; i++) d[i] = fp8_t(src[i]); }
|
||||
else { fp8a_t* d = (fp8a_t*)dst; for (int i = 0; i < len; i++) d[i] = fp8a_t(src[i]); }
|
||||
}
|
||||
static inline bool isFP8Depth(int d) { return d >= CV_8F_E4M3FN && d <= CV_8F_E4M3FNUZ; }
|
||||
|
||||
static void randf_16_or_32f( void* dst, int len_, int cn, uint64* state, const Vec2f* p, float* fbuf, int flags )
|
||||
{
|
||||
int depth = CV_MAT_DEPTH(flags);
|
||||
uint64 temp = *state;
|
||||
int k = 0, len = len_*cn;
|
||||
float* arr = depth == CV_16F || depth == CV_16BF ? fbuf : (float*)dst;
|
||||
float* arr = depth == CV_16F || depth == CV_16BF || isFP8Depth(depth) ? fbuf : (float*)dst;
|
||||
cn--;
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
@@ -251,6 +259,8 @@ static void randf_16_or_32f( void* dst, int len_, int cn, uint64* state, const V
|
||||
hal::cvt32f16f(fbuf, (hfloat*)dst, len);
|
||||
else if (depth == CV_16BF)
|
||||
hal::cvt32f16bf(fbuf, (bfloat*)dst, len);
|
||||
else if (isFP8Depth(depth))
|
||||
cvt32fToFP8(fbuf, dst, len, depth);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -280,7 +290,8 @@ static RandFunc randTab[CV_DEPTH_MAX][CV_DEPTH_MAX] =
|
||||
(RandFunc)randi_16s, (RandFunc)randi_32s, (RandFunc)randf_16_or_32f,
|
||||
(RandFunc)randf_64f, (RandFunc)randf_16_or_32f, (RandFunc)randf_16_or_32f,
|
||||
(RandFunc)randi_8b, (RandFunc)randi_64u, (RandFunc)randi_64s,
|
||||
(RandFunc)randi_32u, 0, 0, 0
|
||||
(RandFunc)randi_32u,
|
||||
(RandFunc)randf_16_or_32f, (RandFunc)randf_16_or_32f // CV_8F_E4M3FN, E4M3FNUZ
|
||||
},
|
||||
{
|
||||
(RandFunc)randBits_8u, (RandFunc)randBits_8s, (RandFunc)randBits_16u,
|
||||
@@ -425,7 +436,7 @@ randnScale_16_or_32f(float* fbuf, float* dst, int len, int cn,
|
||||
{
|
||||
bool stdmtx = (flags & RNG_FLAG_STDMTX) != 0;
|
||||
int depth = CV_MAT_DEPTH(flags);
|
||||
float* arr = depth == CV_16F || depth == CV_16BF ? fbuf : dst;
|
||||
float* arr = depth == CV_16F || depth == CV_16BF || isFP8Depth(depth) ? fbuf : dst;
|
||||
int i, j, k;
|
||||
|
||||
if( !stdmtx || cn == 1 )
|
||||
@@ -483,6 +494,8 @@ randnScale_16_or_32f(float* fbuf, float* dst, int len, int cn,
|
||||
hal::cvt32f16f(fbuf, (hfloat*)dst, len);
|
||||
else if (depth == CV_16BF)
|
||||
hal::cvt32f16bf(fbuf, (bfloat*)dst, len);
|
||||
else if (isFP8Depth(depth))
|
||||
cvt32fToFP8(fbuf, dst, len, depth);
|
||||
}
|
||||
|
||||
#define DEF_RANDNSCALE_FUNC(suffix, T, PT) \
|
||||
@@ -510,7 +523,8 @@ static RandnScaleFunc randnScaleTab[CV_DEPTH_MAX] =
|
||||
(RandnScaleFunc)randnScale_16s, (RandnScaleFunc)randnScale_32s, (RandnScaleFunc)randnScale_16_or_32f,
|
||||
(RandnScaleFunc)randnScale_64f, (RandnScaleFunc)randnScale_16_or_32f, (RandnScaleFunc)randnScale_16_or_32f,
|
||||
(RandnScaleFunc)randnScale_8b, (RandnScaleFunc)randnScale_64u, (RandnScaleFunc)randnScale_64s,
|
||||
(RandnScaleFunc)randnScale_32u, 0, 0, 0
|
||||
(RandnScaleFunc)randnScale_32u,
|
||||
(RandnScaleFunc)randnScale_16_or_32f, (RandnScaleFunc)randnScale_16_or_32f // CV_8F_E4M3FN, E4M3FNUZ
|
||||
};
|
||||
|
||||
void RNG::fill( InputOutputArray _mat, int disttype,
|
||||
|
||||
@@ -13,6 +13,15 @@ const int ARITHM_MAX_CHANNELS = 4;
|
||||
const int ARITHM_MAX_NDIMS = 4;
|
||||
const int ARITHM_MAX_SIZE_LOG = 10;
|
||||
|
||||
// fp8 (E4M3) is excluded from the tolerance-checked element-wise pool: its 3-bit
|
||||
// mantissa can't meet these tests' error bounds and out-of-range inputs overflow to
|
||||
// NaN. fp8 conversion/arithmetic is covered directly in test_fp8.cpp.
|
||||
static const _OutputArray::DepthMask DEPTH_MASK_ALL_NO_FP8 =
|
||||
_OutputArray::DepthMask(_OutputArray::DEPTH_MASK_ALL &
|
||||
~((1 << CV_8F_E4M3FN) | (1 << CV_8F_E4M3FNUZ)));
|
||||
static const _OutputArray::DepthMask DEPTH_MASK_ALL_BUT_8S_NO_FP8 =
|
||||
_OutputArray::DepthMask(DEPTH_MASK_ALL_NO_FP8 & ~_OutputArray::DEPTH_MASK_8S);
|
||||
|
||||
struct BaseElemWiseOp
|
||||
{
|
||||
enum
|
||||
@@ -41,7 +50,7 @@ struct BaseElemWiseOp
|
||||
|
||||
virtual int getRandomType(RNG& rng)
|
||||
{
|
||||
return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL_BUT_8S, 1,
|
||||
return cvtest::randomType(rng, DEPTH_MASK_ALL_BUT_8S_NO_FP8, 1,
|
||||
ninputs > 1 ? ARITHM_MAX_CHANNELS : 4);
|
||||
}
|
||||
|
||||
@@ -895,8 +904,8 @@ struct ConvertScaleOp : public BaseElemWiseOp
|
||||
}
|
||||
int getRandomType(RNG& rng)
|
||||
{
|
||||
int srctype = cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, ARITHM_MAX_CHANNELS);
|
||||
ddepth = cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1, 1);
|
||||
int srctype = cvtest::randomType(rng, DEPTH_MASK_ALL_NO_FP8, 1, ARITHM_MAX_CHANNELS);
|
||||
ddepth = cvtest::randomType(rng, DEPTH_MASK_ALL_NO_FP8, 1, 1);
|
||||
return srctype;
|
||||
}
|
||||
double getMaxErr(int)
|
||||
@@ -994,7 +1003,7 @@ struct ConvertScaleAbsOp : public BaseElemWiseOp
|
||||
}
|
||||
int getRandomType(RNG& rng)
|
||||
{
|
||||
return cvtest::randomType(rng, _OutputArray::DEPTH_MASK_ALL, 1,
|
||||
return cvtest::randomType(rng, DEPTH_MASK_ALL_NO_FP8, 1,
|
||||
ninputs > 1 ? ARITHM_MAX_CHANNELS : 4);
|
||||
}
|
||||
double getMaxErr(int)
|
||||
|
||||
@@ -21,9 +21,9 @@ TEST_P(GpuMat, convertTo)
|
||||
{
|
||||
int sdepth = get<0>(GetParam());
|
||||
int ddepth = get<1>(GetParam());
|
||||
if (sdepth == CV_16F || sdepth == CV_Bool || sdepth == CV_16BF)
|
||||
if (sdepth == CV_16F || sdepth == CV_Bool || sdepth == CV_16BF || (sdepth >= CV_8F_E4M3FN && sdepth <= CV_8F_E4M3FNUZ))
|
||||
throw SkipTestException("Unsupported src type");
|
||||
if (ddepth == CV_16F || ddepth == CV_Bool || ddepth == CV_16BF)
|
||||
if (ddepth == CV_16F || ddepth == CV_Bool || ddepth == CV_16BF || (ddepth >= CV_8F_E4M3FN && ddepth <= CV_8F_E4M3FNUZ))
|
||||
throw SkipTestException("Unsupported dst type");
|
||||
|
||||
Mat ref(16, 20, CV_8U), testMat;
|
||||
@@ -47,9 +47,9 @@ TEST_P(GpuMat, convertToScale)
|
||||
{
|
||||
int sdepth = get<0>(GetParam());
|
||||
int ddepth = get<1>(GetParam());
|
||||
if (sdepth == CV_16F || sdepth == CV_Bool || sdepth == CV_16BF)
|
||||
if (sdepth == CV_16F || sdepth == CV_Bool || sdepth == CV_16BF || (sdepth >= CV_8F_E4M3FN && sdepth <= CV_8F_E4M3FNUZ))
|
||||
throw SkipTestException("Unsupported src type");
|
||||
if (ddepth == CV_16F || ddepth == CV_Bool || ddepth == CV_16BF)
|
||||
if (ddepth == CV_16F || ddepth == CV_Bool || ddepth == CV_16BF || (ddepth >= CV_8F_E4M3FN && ddepth <= CV_8F_E4M3FNUZ))
|
||||
throw SkipTestException("Unsupported dst type");
|
||||
|
||||
Mat ref(16, 20, CV_8U), testMat;
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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 "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
// The two FP8 depths and their wrapper types share one set of expectations.
|
||||
// Values chosen to be exactly representable (so round-trips are bit-exact) plus
|
||||
// the special/overflow cases that distinguish the formats.
|
||||
|
||||
TEST(Core_FP8, type_basics)
|
||||
{
|
||||
const int depths[] = { CV_8F_E4M3FN, CV_8F_E4M3FNUZ };
|
||||
for (int d : depths)
|
||||
{
|
||||
EXPECT_EQ(CV_ELEM_SIZE1(d), 1) << "depth " << d;
|
||||
Mat m(3, 4, CV_MAKETYPE(d, 1));
|
||||
EXPECT_EQ(m.depth(), d);
|
||||
EXPECT_EQ(m.channels(), 1);
|
||||
EXPECT_EQ(m.elemSize(), (size_t)1);
|
||||
EXPECT_EQ(m.elemSize1(), (size_t)1);
|
||||
EXPECT_EQ(m.total(), (size_t)12);
|
||||
// depthToString should not return null for a registered depth
|
||||
EXPECT_NE(cv::depthToString(d), (const char*)NULL);
|
||||
}
|
||||
Mat c3(2, 2, CV_8FC(3));
|
||||
EXPECT_EQ(c3.channels(), 3);
|
||||
EXPECT_EQ(c3.elemSize(), (size_t)3);
|
||||
}
|
||||
|
||||
TEST(Core_FP8, scalar_roundtrip_exact)
|
||||
{
|
||||
// {0, .5, 1, 1.5, 2, 3, 4, 6} and negatives are exact in every FP8 format here.
|
||||
const float exact[] = { 0.f, 0.5f, 1.f, 1.5f, 2.f, 3.f, 4.f, 6.f, -2.5f, -0.75f };
|
||||
for (float v : exact)
|
||||
{
|
||||
EXPECT_EQ((float)cv::fp8_t(v), v) << v;
|
||||
EXPECT_EQ((float)cv::fp8a_t(v), v) << v;
|
||||
}
|
||||
// round-to-nearest-even onto the grid
|
||||
EXPECT_EQ((float)cv::fp8_t(1.234f), 1.25f); // 3 mantissa bits
|
||||
}
|
||||
|
||||
TEST(Core_FP8, format_specific_limits)
|
||||
{
|
||||
// max finite values
|
||||
EXPECT_EQ((float)cv::fp8_t(448.f), 448.f);
|
||||
EXPECT_EQ((float)cv::fp8a_t(240.f), 240.f);
|
||||
|
||||
// overflow: these formats have no inf -> overflow to NaN
|
||||
EXPECT_TRUE(cvIsNaN((float)cv::fp8_t(1e6f)));
|
||||
EXPECT_TRUE(cvIsNaN((float)cv::fp8a_t(1e6f)));
|
||||
// 448 exceeds the FNUZ E4M3 range (max 240) -> NaN
|
||||
EXPECT_TRUE(cvIsNaN((float)cv::fp8a_t(448.f)));
|
||||
|
||||
// NaN propagates
|
||||
EXPECT_TRUE(cvIsNaN((float)cv::fp8_t(std::numeric_limits<float>::quiet_NaN())));
|
||||
|
||||
// smallest E4M3FN subnormal is 2^-9
|
||||
EXPECT_EQ((float)cv::fp8_t(0.001953125f), 0.001953125f);
|
||||
}
|
||||
|
||||
TEST(Core_FP8, mat_convert_roundtrip)
|
||||
{
|
||||
float vals[] = { 0.f, 0.5f, 1.f, 1.5f, 2.f, 3.f, 4.f, 6.f, -1.f, -4.f };
|
||||
Mat f(1, 10, CV_32F, vals);
|
||||
const int depths[] = { CV_8F_E4M3FN, CV_8F_E4M3FNUZ };
|
||||
for (int d : depths)
|
||||
{
|
||||
Mat q, back;
|
||||
f.convertTo(q, d);
|
||||
EXPECT_EQ(q.depth(), d);
|
||||
EXPECT_EQ(q.elemSize(), (size_t)1);
|
||||
q.convertTo(back, CV_32F);
|
||||
ASSERT_EQ(back.type(), CV_32FC1);
|
||||
for (int i = 0; i < 10; i++)
|
||||
EXPECT_EQ(back.at<float>(i), vals[i]) << "depth " << d << " idx " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Core_FP8, convert_from_and_to_other_types)
|
||||
{
|
||||
// f16 -> fp8 -> f32 (f16 source is lossless into the conversion)
|
||||
Mat f32(1, 5, CV_32F);
|
||||
float v[] = { 0.5f, 1.f, 2.f, 4.f, -3.f };
|
||||
memcpy(f32.data, v, sizeof(v));
|
||||
Mat f16; f32.convertTo(f16, CV_16F);
|
||||
Mat q; f16.convertTo(q, CV_8F_E4M3FN);
|
||||
Mat back; q.convertTo(back, CV_32F);
|
||||
for (int i = 0; i < 5; i++)
|
||||
EXPECT_EQ(back.at<float>(i), v[i]);
|
||||
|
||||
// fp8 -> int (saturate_cast rounds to nearest)
|
||||
Mat qi; f32.convertTo(qi, CV_8F_E4M3FN);
|
||||
Mat i32; qi.convertTo(i32, CV_32S);
|
||||
EXPECT_EQ(i32.at<int>(0), 0); // 0.5 -> 0 (round to even)
|
||||
EXPECT_EQ(i32.at<int>(1), 1);
|
||||
EXPECT_EQ(i32.at<int>(2), 2);
|
||||
EXPECT_EQ(i32.at<int>(3), 4);
|
||||
EXPECT_EQ(i32.at<int>(4), -3);
|
||||
}
|
||||
|
||||
TEST(Core_FP8, cross_fp8_conversion)
|
||||
{
|
||||
float v[] = { 0.5f, 1.5f, 6.f, 100.f, -2.f };
|
||||
Mat f(1, 5, CV_32F, v);
|
||||
Mat e4m3, e4m3u, back;
|
||||
f.convertTo(e4m3, CV_8F_E4M3FN);
|
||||
e4m3.convertTo(e4m3u, CV_8F_E4M3FNUZ); // FP8 -> FP8
|
||||
e4m3u.convertTo(back, CV_32F);
|
||||
// values <=6 are representable in both grids -> preserved exactly
|
||||
EXPECT_EQ(back.at<float>(0), 0.5f);
|
||||
EXPECT_EQ(back.at<float>(1), 1.5f);
|
||||
EXPECT_EQ(back.at<float>(2), 6.f);
|
||||
EXPECT_EQ(back.at<float>(4), -2.f);
|
||||
}
|
||||
|
||||
TEST(Core_FP8, convert_scale)
|
||||
{
|
||||
Mat f = (Mat_<float>(1, 4) << 1.f, 2.f, 3.f, 4.f);
|
||||
Mat q, back;
|
||||
f.convertTo(q, CV_8F_E4M3FN, 2.0, 1.0); // 2x+1 -> {3,5,7,9}
|
||||
q.convertTo(back, CV_32F);
|
||||
EXPECT_EQ(back.at<float>(0), 3.f); // 1.5*2, exact
|
||||
EXPECT_EQ(back.at<float>(1), 5.f); // 1.25*4, exact
|
||||
EXPECT_EQ(back.at<float>(2), 7.f); // 1.75*4, exact
|
||||
EXPECT_EQ(back.at<float>(3), 9.f); // 9 = 1.125*8 is exact in E4M3 (3 mantissa bits)
|
||||
}
|
||||
|
||||
TEST(Core_FP8, set_scalar)
|
||||
{
|
||||
Mat m(3, 3, CV_8F_E4M3FN);
|
||||
m.setTo(Scalar(2.5));
|
||||
Mat back; m.convertTo(back, CV_32F);
|
||||
for (int i = 0; i < 9; i++)
|
||||
EXPECT_EQ(back.at<float>(i), 2.5f);
|
||||
|
||||
Mat z = Mat::zeros(2, 2, CV_8F_E4M3FNUZ);
|
||||
Mat zf; z.convertTo(zf, CV_32F);
|
||||
EXPECT_EQ(countNonZero(zf), 0);
|
||||
}
|
||||
|
||||
// both fp8 flavors <-> every other depth, both directions; values exact in all types
|
||||
TEST(Core_FP8, convert_all_depths)
|
||||
{
|
||||
const int fp8[] = { CV_8F, CV_8F_E4M3FNUZ };
|
||||
const int others[] = { CV_8U, CV_8S, CV_16U, CV_16S, CV_32S, CV_32F,
|
||||
CV_64F, CV_16F, CV_16BF, CV_64U, CV_64S, CV_32U };
|
||||
float vals[] = { 0.f, 1.f, 2.f, 3.f, 4.f, 6.f };
|
||||
Mat f(1, 6, CV_32F, vals);
|
||||
for (int d : fp8)
|
||||
for (int o : others)
|
||||
{
|
||||
Mat q, viaO, back;
|
||||
f.convertTo(q, d); q.convertTo(viaO, o); viaO.convertTo(back, CV_32F);
|
||||
Mat so, q2, back2;
|
||||
f.convertTo(so, o); so.convertTo(q2, d); q2.convertTo(back2, CV_32F);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
EXPECT_EQ(back.at<float>(i), vals[i]) << "fp8 " << d << " -> " << o << " idx " << i;
|
||||
EXPECT_EQ(back2.at<float>(i), vals[i]) << o << " -> fp8 " << d << " idx " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -2370,10 +2370,61 @@ TEST_P(FileStorage_exact_type, long_int_mat)
|
||||
EXPECT_EQ(cv::norm(src, dst, NORM_INF), 0.0);
|
||||
}
|
||||
|
||||
TEST_P(FileStorage_exact_type, fp8_mat)
|
||||
{
|
||||
const int fp8[] = { CV_8F_E4M3FN, CV_8F_E4M3FNUZ };
|
||||
float vals[] = { 0.f, 0.5f, 1.f, 1.5f, 2.f, 3.f, 4.f, 6.f, -1.5f, -2.f };
|
||||
Mat f(1, 10, CV_32F, vals);
|
||||
for (int d : fp8)
|
||||
{
|
||||
Mat src; f.convertTo(src, d);
|
||||
Mat dst = fsWriteRead(src, GetParam());
|
||||
ASSERT_EQ(src.type(), dst.type());
|
||||
ASSERT_EQ(src.size, dst.size);
|
||||
EXPECT_EQ(0, memcmp(src.data, dst.data, src.total() * src.elemSize())) << "fp8 depth " << d;
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Core_InputOutput,
|
||||
FileStorage_exact_type, Values(".yml", ".xml", ".json", ".xml.gz", ".xml.gz0", ".xml.gz9")
|
||||
);
|
||||
|
||||
TEST(Core_InputOutput, fp8_base64)
|
||||
{
|
||||
const int fp8[] = { CV_8F_E4M3FN, CV_8F_E4M3FNUZ };
|
||||
float vals[] = { 0.f, 0.5f, 1.f, 1.5f, 2.f, 3.f, 4.f, 6.f, -1.5f, -2.f };
|
||||
Mat f(1, 10, CV_32F, vals);
|
||||
for (const char* ext : { ".yml", ".xml", ".json" })
|
||||
for (int d : fp8)
|
||||
{
|
||||
Mat src; f.convertTo(src, d);
|
||||
std::string fn = cv::tempfile(ext);
|
||||
{ FileStorage fs(fn, FileStorage::WRITE_BASE64); fs << "m" << src; }
|
||||
Mat dst; { FileStorage fs(fn, FileStorage::READ); fs["m"] >> dst; }
|
||||
remove(fn.c_str());
|
||||
ASSERT_EQ(src.type(), dst.type());
|
||||
ASSERT_EQ(src.size, dst.size);
|
||||
EXPECT_EQ(0, memcmp(src.data, dst.data, src.total() * src.elemSize()))
|
||||
<< "fp8 depth " << d << " ext " << ext;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, fp8_scalar)
|
||||
{
|
||||
for (const char* ext : { ".yml", ".xml", ".json" })
|
||||
{
|
||||
cv::fp8_t a(2.5f);
|
||||
cv::fp8a_t b(-1.5f);
|
||||
std::string fn = cv::tempfile(ext);
|
||||
{ FileStorage fs(fn, FileStorage::WRITE); fs << "a" << a << "b" << b; }
|
||||
cv::fp8_t a2; cv::fp8a_t b2;
|
||||
{ FileStorage fs(fn, FileStorage::READ); fs["a"] >> a2; fs["b"] >> b2; }
|
||||
remove(fn.c_str());
|
||||
EXPECT_EQ((float)a, (float)a2) << ext;
|
||||
EXPECT_EQ((float)b, (float)b2) << ext;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Core_InputOutput, YAML_Compatibility)
|
||||
{
|
||||
string filename = cv::tempfile(".yaml");
|
||||
|
||||
@@ -332,6 +332,12 @@ convertTo(const _Tp* src, void* dst, int dtype,
|
||||
case CV_16BF:
|
||||
convert_(src, (cv::bfloat*)dst, total, alpha, beta);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
convert_(src, (cv::fp8_t*)dst, total, alpha, beta);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
convert_(src, (cv::fp8a_t*)dst, total, alpha, beta);
|
||||
break;
|
||||
case CV_Bool:
|
||||
convert_to_bool(src, (bool*)dst, total, alpha, beta);
|
||||
break;
|
||||
@@ -415,6 +421,12 @@ void convert(const Mat& src, cv::OutputArray _dst,
|
||||
case CV_16BF:
|
||||
convertTo((const cv::bfloat*)sptr, dptr, dtype, total, alpha, beta);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
convertTo((const cv::fp8_t*)sptr, dptr, dtype, total, alpha, beta);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
convertTo((const cv::fp8a_t*)sptr, dptr, dtype, total, alpha, beta);
|
||||
break;
|
||||
default:
|
||||
CV_Error(cv::Error::StsNotImplemented, "unknown/unsupported depth");
|
||||
}
|
||||
@@ -2251,6 +2263,15 @@ int check( const Mat& a, double fmin, double fmax, vector<int>* _idx )
|
||||
// success_err_level is maximum allowed difference, idx is the index of the first
|
||||
// element for which difference is >success_err_level
|
||||
// (or index of element with the maximum difference)
|
||||
static inline double decodeFP8(const uchar* p, int depth)
|
||||
{
|
||||
switch (depth)
|
||||
{
|
||||
case CV_8F_E4M3FN: return (double)(float)*reinterpret_cast<const cv::fp8_t*>(p);
|
||||
default: return (double)(float)*reinterpret_cast<const cv::fp8a_t*>(p);
|
||||
}
|
||||
}
|
||||
|
||||
int cmpEps( const Mat& arr_, const Mat& refarr_, double* _realmaxdiff,
|
||||
double success_err_level, vector<int>* _idx,
|
||||
bool element_wise_relative_error )
|
||||
@@ -2375,6 +2396,38 @@ int cmpEps( const Mat& arr_, const Mat& refarr_, double* _realmaxdiff,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
case CV_8F_E4M3FNUZ:
|
||||
for( j = 0; j < total; j++ )
|
||||
{
|
||||
if( ((uchar*)sptr1)[j] == ((uchar*)sptr2)[j] )
|
||||
continue;
|
||||
double a_val = decodeFP8((const uchar*)sptr1 + j, depth);
|
||||
double b_val = decodeFP8((const uchar*)sptr2 + j, depth);
|
||||
double threshold;
|
||||
if( cvIsNaN(a_val) || cvIsInf(a_val) )
|
||||
{
|
||||
result = CMP_EPS_INVALID_TEST_DATA;
|
||||
idx = startidx + j;
|
||||
break;
|
||||
}
|
||||
if( cvIsNaN(b_val) || cvIsInf(b_val) )
|
||||
{
|
||||
result = CMP_EPS_INVALID_REF_DATA;
|
||||
idx = startidx + j;
|
||||
break;
|
||||
}
|
||||
a_val = fabs(a_val - b_val);
|
||||
threshold = element_wise_relative_error ? fabs(b_val) + 1 : maxval;
|
||||
if( a_val > threshold*success_err_level )
|
||||
{
|
||||
realmaxdiff = a_val/threshold;
|
||||
if( idx == 0 )
|
||||
idx = startidx + j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CV_32F:
|
||||
for( j = 0; j < total; j++ )
|
||||
{
|
||||
@@ -3404,6 +3457,14 @@ static void writeElems(std::ostream& out, const void* data, int nelems, int dept
|
||||
writeElems<cv::bfloat, float>(out, data, nelems, starpos);
|
||||
out.precision(pp);
|
||||
}
|
||||
else if(depth == CV_8F_E4M3FN || depth == CV_8F_E4M3FNUZ)
|
||||
{
|
||||
std::streamsize pp = out.precision();
|
||||
out.precision(4);
|
||||
if(depth == CV_8F_E4M3FN) writeElems<cv::fp8_t, float>(out, data, nelems, starpos);
|
||||
else writeElems<cv::fp8a_t, float>(out, data, nelems, starpos);
|
||||
out.precision(pp);
|
||||
}
|
||||
else if(depth == CV_32F)
|
||||
{
|
||||
std::streamsize pp = out.precision();
|
||||
|
||||
Reference in New Issue
Block a user