mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 15:53: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 };
|
||||
|
||||
Reference in New Issue
Block a user