1
0
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:
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 };