1
0
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:
Abhishek Gola
2026-07-15 18:15:01 +05:30
committed by GitHub
parent 18f9bffb0d
commit 34074075b2
19 changed files with 766 additions and 22 deletions
+81 -1
View File
@@ -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 };
+2 -1
View File
@@ -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;
}
+5
View File
@@ -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;
+109 -1
View File
@@ -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);
+122 -2
View File
@@ -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);
+6
View File
@@ -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;
+4 -1
View File
@@ -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
+36 -2
View File
@@ -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");
+18 -4
View File
@@ -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 -4
View File
@@ -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)
+4 -4
View File
@@ -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;
+168
View File
@@ -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
+51
View File
@@ -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");
+61
View File
@@ -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();