mirror of
https://github.com/opencv/opencv.git
synced 2026-09-11 04:43:22 -05:00
Merge pull request #29360 from abhishek-gola:exotic_cast_operations
Support ONNX Cast/CastLike for FP8/FP4/INT4/UINT4/E8M0 dtypes - #29360 ### 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:
@@ -8,6 +8,9 @@
|
||||
#include "layers_common.hpp"
|
||||
#include "../net_impl.hpp"
|
||||
|
||||
#include "opencv-onnx.pb.h"
|
||||
#include "../onnx/onnx_dtype_convert.hpp"
|
||||
|
||||
namespace cv { namespace dnn {
|
||||
|
||||
// ONNX Cast operator
|
||||
@@ -19,13 +22,18 @@ namespace cv { namespace dnn {
|
||||
|
||||
namespace
|
||||
{
|
||||
// ONNX Cast float->int truncates toward zero; Mat::convertTo rounds. Truncate to match the spec.
|
||||
template<typename DT>
|
||||
inline void truncateToIntImpl(const Mat& src, Mat& dst)
|
||||
{
|
||||
const int n = (int)src.total() * src.channels();
|
||||
DT* d = dst.ptr<DT>();
|
||||
if (src.depth() == CV_32F)
|
||||
if (src.depth() == CV_16F)
|
||||
{
|
||||
const hfloat* s = src.ptr<hfloat>();
|
||||
for (int i = 0; i < n; ++i)
|
||||
d[i] = saturate_cast<DT>(std::trunc((float)s[i]));
|
||||
}
|
||||
else if (src.depth() == CV_32F)
|
||||
{
|
||||
const float* s = src.ptr<float>();
|
||||
for (int i = 0; i < n; ++i)
|
||||
@@ -53,87 +61,6 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
inline void castQuantized(const Mat& src, Mat& dst, int targetDepth)
|
||||
{
|
||||
if (targetDepth == CV_16F)
|
||||
{
|
||||
CV_Assert(dst.depth() == CV_32F);
|
||||
if (src.depth() == CV_32F)
|
||||
{
|
||||
MatConstIterator_<float> sIt = src.begin<float>(), sEnd = src.end<float>();
|
||||
MatIterator_<float> dIt = dst.begin<float>();
|
||||
for (; sIt != sEnd; ++sIt, ++dIt)
|
||||
{
|
||||
*dIt = (float)hfloat(*sIt);
|
||||
}
|
||||
}
|
||||
else if (src.depth() == CV_64F)
|
||||
{
|
||||
MatConstIterator_<double> sIt = src.begin<double>(), sEnd = src.end<double>();
|
||||
MatIterator_<float> dIt = dst.begin<float>();
|
||||
for (; sIt != sEnd; ++sIt, ++dIt)
|
||||
{
|
||||
float v = (float)*sIt;
|
||||
*dIt = (float)hfloat(v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat src32; src.convertTo(src32, CV_32F);
|
||||
MatConstIterator_<float> sIt = src32.begin<float>(), sEnd = src32.end<float>();
|
||||
MatIterator_<float> dIt = dst.begin<float>();
|
||||
for (; sIt != sEnd; ++sIt, ++dIt)
|
||||
{
|
||||
*dIt = (float)hfloat(*sIt);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetDepth == CV_16BF)
|
||||
{
|
||||
const int ddepth = dst.depth();
|
||||
if (!(ddepth == CV_16BF || ddepth == CV_16U))
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "Unsupported destination depth for BF16 cast");
|
||||
}
|
||||
|
||||
Mat dst_bits(dst.size(), CV_MAKETYPE(CV_16U, dst.channels()), dst.data, dst.step);
|
||||
|
||||
const Mat* src32p;
|
||||
Mat src32;
|
||||
if (src.depth() == CV_32F)
|
||||
src32p = &src;
|
||||
else
|
||||
{
|
||||
src.convertTo(src32, CV_32F);
|
||||
src32p = &src32;
|
||||
}
|
||||
|
||||
const int rows = src32p->rows;
|
||||
const int cols_x_cn = src32p->cols * src32p->channels();
|
||||
for (int r = 0; r < rows; ++r)
|
||||
{
|
||||
const float* in = src32p->ptr<float>(r);
|
||||
ushort* out = dst_bits.ptr<ushort>(r);
|
||||
for (int i = 0; i < cols_x_cn; ++i)
|
||||
{
|
||||
// float32 -> bfloat16 with round-to-nearest-even (matches ONNX).
|
||||
Cv32suf u; u.f = in[i];
|
||||
const uint32_t x = u.u;
|
||||
if ((x & 0x7fffffffu) > 0x7f800000u) // NaN: keep it NaN
|
||||
out[i] = (ushort)((x >> 16) | 0x0040u);
|
||||
else
|
||||
{
|
||||
const uint32_t bias = 0x7fffu + ((x >> 16) & 1u);
|
||||
out[i] = (ushort)((x + bias) >> 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
src.convertTo(dst, dst.depth());
|
||||
}
|
||||
}
|
||||
|
||||
class Cast2LayerImpl CV_FINAL : public Cast2Layer
|
||||
@@ -144,10 +71,13 @@ public:
|
||||
setParamsFrom(params);
|
||||
hasToParam = false;
|
||||
toCvDepth_ = -1;
|
||||
toOnnxType_ = -1;
|
||||
saturate_ = params.get<int>("saturate", 1) != 0;
|
||||
if (params.has("to"))
|
||||
{
|
||||
hasToParam = true;
|
||||
toCvDepth_ = mapToCvDepth(params.get<int>("to"));
|
||||
toOnnxType_ = params.get<int>("to");
|
||||
toCvDepth_ = mapToCvDepth(toOnnxType_);
|
||||
}
|
||||
else if (params.has("outputType"))
|
||||
{
|
||||
@@ -167,6 +97,9 @@ public:
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE
|
||||
{
|
||||
// Exotic dtypes (FP8/FP4/INT4/UINT4) are handled on the CPU path only.
|
||||
if (onnx_dtype::isExotic(toOnnxType_))
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH;
|
||||
}
|
||||
@@ -181,6 +114,18 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Half targets are stored as FP32 unless native FP16 is enabled; forward() still rounds.
|
||||
int resolveStorageDepth(int targetDepth, bool exotic) const
|
||||
{
|
||||
if (!exotic && (targetDepth == CV_16F || targetDepth == CV_16BF))
|
||||
{
|
||||
Net::Impl* ni = getNetImpl(const_cast<Cast2LayerImpl*>(this));
|
||||
if (ni && !ni->enableFP16)
|
||||
return CV_32F;
|
||||
}
|
||||
return targetDepth;
|
||||
}
|
||||
|
||||
virtual void getTypes(const std::vector<MatType>& inputs,
|
||||
const int requiredOutputs,
|
||||
const int requiredInternals,
|
||||
@@ -216,9 +161,8 @@ public:
|
||||
|
||||
const int in0Type = inputs[0];
|
||||
const int in0CN = in0Type >= 0 ? CV_MAT_CN(in0Type) : 1;
|
||||
int planDepth = targetDepth;
|
||||
if (planDepth == CV_16F) planDepth = CV_32F;
|
||||
const int outType = CV_MAKETYPE(planDepth, in0CN);
|
||||
const bool exotic = hasToParam && onnx_dtype::isExotic(toOnnxType_);
|
||||
const int outType = CV_MAKETYPE(resolveStorageDepth(targetDepth, exotic), in0CN);
|
||||
outputs.assign(1, outType);
|
||||
}
|
||||
|
||||
@@ -227,6 +171,9 @@ public:
|
||||
{
|
||||
std::vector<UMat> inputs, outputs;
|
||||
|
||||
if (hasToParam && onnx_dtype::isExotic(toOnnxType_))
|
||||
return false; // exotic conversions run on the CPU path
|
||||
|
||||
inputs_.getUMatVector(inputs);
|
||||
outputs_.getUMatVector(outputs);
|
||||
CV_CheckEQ(inputs.size(), (size_t)1, "");
|
||||
@@ -305,46 +252,42 @@ public:
|
||||
}
|
||||
CV_CheckGE(runtimeTargetDepth, 0, "Cast: failed to resolve target data type at runtime");
|
||||
|
||||
int plannedDDepth = (runtimeTargetDepth == CV_16F) ? CV_32F : runtimeTargetDepth;
|
||||
if (dst0.depth() != plannedDDepth)
|
||||
dst0.create(dst0.size(), CV_MAKETYPE(plannedDDepth, src0.channels()));
|
||||
const bool exotic = hasToParam && onnx_dtype::isExotic(toOnnxType_);
|
||||
const int storeDepth = resolveStorageDepth(runtimeTargetDepth, exotic);
|
||||
if (dst0.depth() != storeDepth)
|
||||
dst0.create(dst0.size(), CV_MAKETYPE(storeDepth, src0.channels()));
|
||||
|
||||
Mat src = src0;
|
||||
Mat dst = dst0;
|
||||
|
||||
if (exotic)
|
||||
{
|
||||
castExotic(src, dst, toOnnxType_, saturate_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cast to half yields half-representable values even when FP32 carries them.
|
||||
if (storeDepth != runtimeTargetDepth &&
|
||||
(runtimeTargetDepth == CV_16F || runtimeTargetDepth == CV_16BF))
|
||||
{
|
||||
Mat half;
|
||||
src.convertTo(half, runtimeTargetDepth);
|
||||
half.convertTo(dst, storeDepth);
|
||||
return;
|
||||
}
|
||||
|
||||
const int sdepth = src.depth();
|
||||
const int ddepth = dst.depth();
|
||||
|
||||
if (sdepth == runtimeTargetDepth && !(runtimeTargetDepth == CV_16F && ddepth == CV_32F))
|
||||
if (sdepth == ddepth && sdepth == runtimeTargetDepth)
|
||||
{
|
||||
src0.copyTo(dst0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtimeTargetDepth == CV_16BF && (ddepth == CV_16BF || ddepth == CV_16U))
|
||||
if ((sdepth == CV_16F || sdepth == CV_32F || sdepth == CV_64F) && CV_IS_INT_TYPE(ddepth))
|
||||
{
|
||||
castQuantized(src, dst, CV_16BF);
|
||||
}
|
||||
else if (sdepth == CV_16BF)
|
||||
{
|
||||
src.convertTo(dst, ddepth);
|
||||
}
|
||||
else if (runtimeTargetDepth == CV_16F && ddepth == CV_32F)
|
||||
{
|
||||
castQuantized(src, dst, CV_16F);
|
||||
}
|
||||
else if (runtimeTargetDepth == CV_64F && ddepth != CV_64F)
|
||||
{
|
||||
if (ddepth == CV_16U || ddepth == CV_16BF)
|
||||
{
|
||||
castQuantized(src, dst, CV_16BF);
|
||||
}
|
||||
else
|
||||
src.convertTo(dst, ddepth);
|
||||
}
|
||||
else if ((sdepth == CV_32F || sdepth == CV_64F) && CV_IS_INT_TYPE(ddepth))
|
||||
{
|
||||
truncateFloatToInt(src, dst);
|
||||
truncateFloatToInt(src, dst); // ONNX float->int truncates toward zero
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -374,9 +317,67 @@ public:
|
||||
}
|
||||
#endif // HAVE_DNN_NGRAPH
|
||||
|
||||
void castExotic(const Mat& src, Mat& dst, int onnxType, bool saturate)
|
||||
{
|
||||
const int sdepth = src.depth();
|
||||
const float* sf = (sdepth == CV_32F) ? src.ptr<float>() : nullptr;
|
||||
const hfloat* sh = (sdepth == CV_16F) ? src.ptr<hfloat>() : nullptr;
|
||||
Mat src32;
|
||||
if (!sf && !sh) { src.convertTo(src32, CV_32F); sf = src32.ptr<float>(); }
|
||||
const size_t total = src.total() * src.channels();
|
||||
#define CV_DNN_SRC_F(i) (sf ? sf[i] : (float)sh[i])
|
||||
|
||||
if (onnx_dtype::isFp8(onnxType))
|
||||
{
|
||||
const onnx_dtype::Fp8Fmt fmt = onnx_dtype::fp8FmtFor(onnxType);
|
||||
const int ddepth = dst.depth();
|
||||
if (ddepth == CV_8F_E4M3FN || ddepth == CV_8F_E4M3FNUZ)
|
||||
{
|
||||
// Store the ONNX-encoded byte: core's E4M3 encode rounds differently.
|
||||
uchar* d = dst.ptr<uchar>();
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = onnx_dtype::f32ToFp8(CV_DNN_SRC_F(i), fmt, saturate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// E5M2/E5M2FNUZ have no native depth: round onto the FP8 grid, keep CV_16F.
|
||||
hfloat* d = dst.ptr<hfloat>();
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = hfloat(onnx_dtype::fp8ToF32(onnx_dtype::f32ToFp8(CV_DNN_SRC_F(i), fmt, saturate), fmt));
|
||||
}
|
||||
}
|
||||
else if (onnxType == onnx_dtype::ONNX_FLOAT8E8M0)
|
||||
{
|
||||
float* d = dst.ptr<float>(); // E8M0 range exceeds FP16, stays CV_32F
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = onnx_dtype::e8m0ToF32(onnx_dtype::f32ToE8M0(CV_DNN_SRC_F(i)));
|
||||
}
|
||||
else if (onnxType == opencv_onnx::TensorProto_DataType_FLOAT4E2M1)
|
||||
{
|
||||
hfloat* d = dst.ptr<hfloat>();
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = hfloat(onnx_dtype::fp4ToF32(onnx_dtype::f32ToFp4(CV_DNN_SRC_F(i))));
|
||||
}
|
||||
else if (onnx_dtype::isInt4(onnxType))
|
||||
{
|
||||
schar* d = dst.ptr<schar>();
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = onnx_dtype::f32ToInt4(CV_DNN_SRC_F(i));
|
||||
}
|
||||
else // UINT4
|
||||
{
|
||||
uchar* d = dst.ptr<uchar>();
|
||||
for (size_t i = 0; i < total; i++)
|
||||
d[i] = onnx_dtype::f32ToUint4(CV_DNN_SRC_F(i));
|
||||
}
|
||||
#undef CV_DNN_SRC_F
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasToParam = false;
|
||||
int toCvDepth_ = -1;
|
||||
int toOnnxType_ = -1;
|
||||
bool saturate_ = true;
|
||||
|
||||
// ONNX TensorProto::DataType values (see opencv-onnx.proto); the 'to'
|
||||
// attribute stores the raw ONNX value. Fixed by the ONNX specification,
|
||||
@@ -398,6 +399,11 @@ private:
|
||||
|
||||
static int mapToCvDepth(int v)
|
||||
{
|
||||
if (v == onnx_dtype::ONNX_FLOAT8E8M0) return CV_32F; // range exceeds FP16
|
||||
if (onnx_dtype::isFp8Native(v)) return onnx_dtype::fp8NativeDepth(v); // E4M3FN/E4M3FNUZ
|
||||
if (onnx_dtype::isExoticFloat(v)) return CV_16F; // E5M2/FP4
|
||||
if (onnx_dtype::isInt4(v)) return CV_8S;
|
||||
if (onnx_dtype::isUint4(v)) return CV_8U;
|
||||
switch (v)
|
||||
{
|
||||
case ONNX_DT_FLOAT: return CV_32F;
|
||||
|
||||
@@ -69,6 +69,14 @@ public:
|
||||
have_bias = params.get<bool>("have_bias", false);
|
||||
|
||||
real_ndims_C = params.get<int>("real_ndims_C", -1);
|
||||
|
||||
for (Mat& blob : blobs) {
|
||||
if (blob.type() == CV_16F || blob.type() == CV_16BF) {
|
||||
Mat widened;
|
||||
blob.convertTo(widened, CV_32F);
|
||||
blob = widened;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE {
|
||||
|
||||
@@ -42,6 +42,14 @@ class MatMulLayerImpl CV_FINAL : public MatMulLayer {
|
||||
beta = params.get<float>("beta", 1.f);
|
||||
|
||||
real_ndims_C = params.get<int>("real_ndims_C", -1);
|
||||
|
||||
for (Mat& blob : blobs) {
|
||||
if (blob.type() == CV_16F || blob.type() == CV_16BF) {
|
||||
Mat widened;
|
||||
blob.convertTo(widened, CV_32F);
|
||||
blob = widened;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE {
|
||||
|
||||
@@ -139,6 +139,7 @@ struct Net::Impl : public detail::NetImplBase
|
||||
KVCacheManager kvCacheManager;
|
||||
|
||||
Ptr<Graph> mainGraph;
|
||||
std::vector<int> mainGraphOutTypes;
|
||||
int globGraphIdx;
|
||||
|
||||
int accuracy;
|
||||
@@ -551,6 +552,8 @@ struct Net::Impl : public detail::NetImplBase
|
||||
void fuseScaleSoftmax();
|
||||
// replace constant sub-expressions with their results
|
||||
|
||||
// widen FP16/BF16 constants to execution precision while the engine lacks half kernels
|
||||
void widenHalfConstants();
|
||||
void fuseQDQ();
|
||||
void constFold();
|
||||
// make some operations (activation, batch norm, convolution) unary if
|
||||
|
||||
@@ -552,6 +552,29 @@ Ptr<Graph> Net::Impl::newGraph(const std::string& name_, const std::vector<Arg>&
|
||||
return graph;
|
||||
}
|
||||
|
||||
// No half kernels yet, so half constants are widened just as setGraphInput() widens inputs.
|
||||
void Net::Impl::widenHalfConstants()
|
||||
{
|
||||
if (enableFP16)
|
||||
return;
|
||||
size_t nargs = args.size();
|
||||
__tensors__.resize(nargs);
|
||||
for (size_t i = 1; i < nargs; i++) {
|
||||
ArgData& adata = args[i];
|
||||
if (adata.kind != DNN_ARG_CONST ||
|
||||
(adata.type != CV_16F && adata.type != CV_16BF))
|
||||
continue;
|
||||
Mat& t = __tensors__[i];
|
||||
if (!t.empty()) {
|
||||
Mat widened;
|
||||
widened.fit(t.shape(), accuracy);
|
||||
t.convertTo(widened, accuracy);
|
||||
t = widened;
|
||||
}
|
||||
adata.type = accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
void Net::Impl::prepareForInference()
|
||||
{
|
||||
#ifdef HAVE_ONNXRUNTIME
|
||||
@@ -563,6 +586,7 @@ void Net::Impl::prepareForInference()
|
||||
#endif
|
||||
|
||||
if (!prepared) {
|
||||
widenHalfConstants();
|
||||
fuseQDQ();
|
||||
constFold();
|
||||
fuseBN();
|
||||
@@ -1266,11 +1290,10 @@ void Net::Impl::setGraphInput(Ptr<Graph>& graph, size_t idx, const Mat& m)
|
||||
if ((adata_type == CV_16F || adata_type == CV_16BF) && !enableFP16)
|
||||
adata_type = CV_32F;
|
||||
|
||||
if (adata_type != mtype &&
|
||||
!((adata_type == CV_64F || adata_type == CV_32F || adata_type == CV_16F || adata_type == CV_16BF) &&
|
||||
(mtype == CV_64F || mtype == CV_32F || mtype == CV_16F || mtype == CV_16BF)) &&
|
||||
!((adata_type == CV_8U || adata_type == CV_8S || adata_type == CV_16U || adata_type == CV_16S || adata_type == CV_32S || adata_type == CV_32U || adata_type == CV_64S || adata_type == CV_64U) &&
|
||||
(mtype == CV_8U || mtype == CV_8S || mtype == CV_16U || mtype == CV_16S || mtype == CV_32S || mtype == CV_32U || mtype == CV_64S || mtype == CV_64U)) &&
|
||||
// setInput converts to the declared type, so any numeric source type is acceptable.
|
||||
const bool aNumeric = CV_IS_INT_TYPE(adata_type) || CV_IS_FLOAT_TYPE(adata_type);
|
||||
const bool mNumeric = CV_IS_INT_TYPE(mtype) || CV_IS_FLOAT_TYPE(mtype);
|
||||
if (adata_type != mtype && !(aNumeric && mNumeric) &&
|
||||
!(adata.type == CV_16BF && mtype == CV_16U) && !(adata.type == CV_16F && mtype == CV_16U) &&
|
||||
!m.empty())
|
||||
{
|
||||
@@ -1868,6 +1891,18 @@ void Net::Impl::forwardGraph(Ptr<Graph>& graph, InputArrayOfArrays inputs_,
|
||||
outputsVec[i].fit(outm.shape(), outm.type());
|
||||
outm.copyTo(outputsVec[i]);
|
||||
}
|
||||
// Narrow to the declared output dtype when an op computed in a wider type.
|
||||
// Half is excepted: the graph was widened, so narrowing would only lose precision.
|
||||
int declaredOutType = i < mainGraphOutTypes.size() ? mainGraphOutTypes[i] : -1;
|
||||
if (!enableFP16 && (declaredOutType == CV_16F || declaredOutType == CV_16BF))
|
||||
declaredOutType = -1;
|
||||
if (declaredOutType >= 0 && !outputsVec[i].empty() &&
|
||||
outputsVec[i].depth() != CV_MAT_DEPTH(declaredOutType))
|
||||
{
|
||||
Mat tmp;
|
||||
outputsVec[i].convertTo(tmp, CV_MAT_DEPTH(declaredOutType));
|
||||
outputsVec[i] = tmp;
|
||||
}
|
||||
} else {
|
||||
outputsVec[i] = outm;
|
||||
}
|
||||
|
||||
188
modules/dnn/src/onnx/onnx_dtype_convert.hpp
Normal file
188
modules/dnn/src/onnx/onnx_dtype_convert.hpp
Normal file
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
// Copyright (C) 2026, BigVision LLC, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
// TODO: drop when these dtypes get native cv::Mat depths; math then moves to core.
|
||||
|
||||
#ifndef OPENCV_DNN_ONNX_DTYPE_CONVERT_HPP
|
||||
#define OPENCV_DNN_ONNX_DTYPE_CONVERT_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
namespace cv { namespace dnn { namespace onnx_dtype {
|
||||
|
||||
// ONNX TensorProto.DataType value for FLOAT8E8M0 (absent from the bundled opencv-onnx.proto).
|
||||
enum { ONNX_FLOAT8E8M0 = 24 };
|
||||
|
||||
// Description of a sign+exponent+mantissa low-precision float (the FP8 family).
|
||||
struct Fp8Fmt { int ebits, mbits, bias; bool has_inf, fnuz; };
|
||||
|
||||
// Returns the format for an FP8 ONNX dtype, or {0,...} for non-FP8 types.
|
||||
inline Fp8Fmt fp8FmtFor(int onnxType)
|
||||
{
|
||||
switch (onnxType)
|
||||
{
|
||||
case 17: return {4, 3, 7, false, false}; // FLOAT8E4M3FN
|
||||
case 18: return {4, 3, 8, false, true}; // FLOAT8E4M3FNUZ
|
||||
case 19: return {5, 2, 15, true, false}; // FLOAT8E5M2
|
||||
case 20: return {5, 2, 16, false, true}; // FLOAT8E5M2FNUZ
|
||||
}
|
||||
return {0, 0, 0, false, false};
|
||||
}
|
||||
|
||||
inline bool isFp8(int onnxType) { return onnxType >= 17 && onnxType <= 20; }
|
||||
// E4M3FN (17) and E4M3FNUZ (18) have a native cv::Mat depth; E5M2/E5M2FNUZ do not.
|
||||
inline bool isFp8Native(int t) { return t == 17 || t == 18; }
|
||||
inline int fp8NativeDepth(int t) { return t == 17 ? CV_8F_E4M3FN : CV_8F_E4M3FNUZ; }
|
||||
// Storage: E4M3 native FP8; E5M2/FP4 CV_16F; E8M0 CV_32F; INT4 CV_8S; UINT4 CV_8U.
|
||||
inline bool isExoticFloat(int t) { return isFp8(t) || t == 23 /*FP4*/ || t == ONNX_FLOAT8E8M0; }
|
||||
inline bool isInt4(int t) { return t == 22; }
|
||||
inline bool isUint4(int t) { return t == 21; }
|
||||
inline bool isExotic(int t) { return isExoticFloat(t) || isInt4(t) || isUint4(t); }
|
||||
|
||||
inline uint32_t f2u(float f) { Cv32suf s; s.f = f; return s.u; }
|
||||
|
||||
// Round 'full' dropping 'shift' low bits, round to nearest, ties to even.
|
||||
inline uint32_t roundRNE(uint32_t full, int shift)
|
||||
{
|
||||
if (shift <= 0) return full << (-shift);
|
||||
uint32_t q = full >> shift;
|
||||
uint32_t rem = full & ((1u << shift) - 1);
|
||||
uint32_t half = 1u << (shift - 1);
|
||||
if (rem > half || (rem == half && (q & 1))) q++;
|
||||
return q;
|
||||
}
|
||||
|
||||
inline uint8_t f32ToFp8(float x, const Fp8Fmt& f, bool saturate)
|
||||
{
|
||||
uint32_t u = f2u(x), sign = (u >> 31) & 1, e = (u >> 23) & 0xFF, m = u & 0x7FFFFF;
|
||||
const int W = f.ebits + f.mbits;
|
||||
const uint32_t sbit = sign << W, maxe = (1u << f.ebits) - 1;
|
||||
const uint8_t NaNc = f.fnuz ? 0x80 : (uint8_t)(sbit | (maxe << f.mbits) | ((1u << f.mbits) - 1));
|
||||
uint8_t maxfin;
|
||||
if (f.has_inf) maxfin = (uint8_t)(sbit | ((maxe - 1) << f.mbits) | ((1u << f.mbits) - 1));
|
||||
else if (f.fnuz) maxfin = (uint8_t)(sbit | (maxe << f.mbits) | ((1u << f.mbits) - 1));
|
||||
else maxfin = (uint8_t)(sbit | (maxe << f.mbits) | ((1u << f.mbits) - 2));
|
||||
const uint8_t Infc = (uint8_t)(sbit | (maxe << f.mbits));
|
||||
|
||||
if (e == 0xFF && m != 0) return NaNc;
|
||||
if (e == 0xFF && m == 0)
|
||||
{
|
||||
if (f.has_inf && !f.fnuz) return saturate ? maxfin : Infc;
|
||||
return saturate ? maxfin : NaNc;
|
||||
}
|
||||
if (e == 0 && m == 0) return f.fnuz ? 0 : (uint8_t)sbit;
|
||||
|
||||
int newexp = (int)e - 127 + f.bias;
|
||||
const uint32_t full = (1u << 23) | m;
|
||||
if (newexp <= 0)
|
||||
{
|
||||
const int shift = (23 - f.mbits) + (1 - newexp);
|
||||
uint32_t mant = (shift >= 32) ? 0u : roundRNE(full, shift);
|
||||
if (mant == 0) return f.fnuz ? 0 : (uint8_t)sbit;
|
||||
return (uint8_t)(sbit | mant);
|
||||
}
|
||||
uint32_t rounded = roundRNE(full, 23 - f.mbits);
|
||||
if (rounded & (1u << (f.mbits + 1))) { rounded >>= 1; newexp++; }
|
||||
const uint32_t mant = rounded & ((1u << f.mbits) - 1);
|
||||
bool ov;
|
||||
if (f.has_inf) ov = (uint32_t)newexp >= maxe;
|
||||
else if (f.fnuz) ov = (uint32_t)newexp > maxe;
|
||||
else ov = (uint32_t)newexp > maxe || ((uint32_t)newexp == maxe && mant == (1u << f.mbits) - 1);
|
||||
if (ov)
|
||||
{
|
||||
if (f.has_inf && !f.fnuz) return saturate ? maxfin : Infc;
|
||||
return saturate ? maxfin : NaNc;
|
||||
}
|
||||
return (uint8_t)(sbit | ((uint32_t)newexp << f.mbits) | mant);
|
||||
}
|
||||
|
||||
// ONNX preserves the NaN sign bit (FNUZ 0x80 decodes to -NaN); bit-exact tests need it.
|
||||
inline float signedQNaN(uint32_t sign) { Cv32suf s; s.u = sign ? 0xFFC00000u : 0x7FC00000u; return s.f; }
|
||||
|
||||
inline float fp8ToF32(uint8_t code, const Fp8Fmt& f)
|
||||
{
|
||||
const int W = f.ebits + f.mbits;
|
||||
const uint32_t sign = (code >> W) & 1;
|
||||
const uint32_t exp = ((uint32_t)code >> f.mbits) & ((1u << f.ebits) - 1);
|
||||
const uint32_t man = code & ((1u << f.mbits) - 1);
|
||||
const uint32_t maxe = (1u << f.ebits) - 1;
|
||||
const float s = sign ? -1.f : 1.f;
|
||||
if (f.fnuz)
|
||||
{
|
||||
if ((uint32_t)code == (1u << W)) return signedQNaN(sign);
|
||||
}
|
||||
else if (exp == maxe)
|
||||
{
|
||||
if (f.has_inf) return man == 0 ? s * std::numeric_limits<float>::infinity() : signedQNaN(sign);
|
||||
if (man == (1u << f.mbits) - 1) return signedQNaN(sign);
|
||||
}
|
||||
if (exp == 0) return s * (float)man * std::ldexp(1.0f, 1 - f.bias - f.mbits);
|
||||
return s * (1.0f + (float)man / (float)(1u << f.mbits)) * std::ldexp(1.0f, (int)exp - f.bias);
|
||||
}
|
||||
|
||||
// E8M0: unsigned, 8-bit exponent only (no sign, no mantissa), bias 127; 0xFF == NaN.
|
||||
// Conversion rounds UP (ceil to next power of two), per the ONNX reference data.
|
||||
inline uint8_t f32ToE8M0(float x)
|
||||
{
|
||||
if (std::isnan(x)) return 0xFF;
|
||||
const uint32_t u = f2u(x), sign = (u >> 31) & 1, e = (u >> 23) & 0xFF, m = u & 0x7FFFFF;
|
||||
if (x == 0.f) return 0;
|
||||
if (sign) return 0xFF; // negative -> NaN (unsigned format)
|
||||
if (e == 0xFF) return 0xFF; // inf -> NaN
|
||||
int code = (int)e + (m > 0 ? 1 : 0);
|
||||
return (uint8_t)(code > 0xFE ? 0xFE : code);
|
||||
}
|
||||
|
||||
inline float e8m0ToF32(uint8_t c)
|
||||
{
|
||||
if (c == 0xFF) return std::numeric_limits<float>::quiet_NaN();
|
||||
return std::ldexp(1.0f, (int)c - 127);
|
||||
}
|
||||
|
||||
// FLOAT4E2M1: 1-2-1, bias 1. Magnitudes {0,.5,1,1.5,2,3,4,6}; saturating; NaN -> 0x8.
|
||||
inline const float* fp4Values()
|
||||
{
|
||||
static const float v[8] = {0.f, 0.5f, 1.f, 1.5f, 2.f, 3.f, 4.f, 6.f};
|
||||
return v;
|
||||
}
|
||||
|
||||
inline uint8_t f32ToFp4(float x)
|
||||
{
|
||||
if (std::isnan(x)) return 0x8;
|
||||
const uint32_t sign = (f2u(x) >> 31) & 1;
|
||||
const float ax = std::fabs(x);
|
||||
const float* V = fp4Values();
|
||||
int best;
|
||||
if (std::isinf(ax) || ax >= 6.0f) best = 7; // saturate to max magnitude
|
||||
else
|
||||
{
|
||||
best = 0; float bestd = std::numeric_limits<float>::max();
|
||||
for (int c = 0; c < 8; c++)
|
||||
{
|
||||
const float d = std::fabs(ax - V[c]);
|
||||
if (d < bestd) { bestd = d; best = c; }
|
||||
else if (d == bestd && (best & 1)) best = c; // tie -> even code
|
||||
}
|
||||
}
|
||||
return (uint8_t)((sign << 3) | best);
|
||||
}
|
||||
|
||||
inline float fp4ToF32(uint8_t code) { return (code & 0x8 ? -1.f : 1.f) * fp4Values()[code & 7]; }
|
||||
|
||||
// INT4/UINT4: truncate toward zero, keep low 4 bits (wraps; ONNX does not clamp).
|
||||
inline int8_t f32ToInt4(float x) { int n = (int)std::trunc(x) & 0xF; return (int8_t)(n >= 8 ? n - 16 : n); }
|
||||
inline uint8_t f32ToUint4(float x) { return (uint8_t)((int)std::trunc(x) & 0xF); }
|
||||
inline int8_t int4SignExtend(uint8_t nib) { return (int8_t)((nib & 0xF) >= 8 ? (int)(nib & 0xF) - 16 : (nib & 0xF)); }
|
||||
|
||||
// Extract the i-th 4-bit element from a tensor packed two-per-byte (low nibble first).
|
||||
inline uint8_t unpackNibble(const uchar* raw, size_t i) { return (i & 1) ? (raw[i >> 1] >> 4) & 0xF : raw[i >> 1] & 0xF; }
|
||||
|
||||
}}} // namespace cv::dnn::onnx_dtype
|
||||
|
||||
#endif // OPENCV_DNN_ONNX_DTYPE_CONVERT_HPP
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifdef HAVE_PROTOBUF
|
||||
#include "../graph_simplifier.hpp"
|
||||
#include "onnx_graph_simplifier.hpp"
|
||||
#include "onnx_dtype_convert.hpp"
|
||||
#include <opencv2/core/utils/filesystem.hpp>
|
||||
#include "opencv2/core/utils/filesystem.private.hpp"
|
||||
|
||||
@@ -1958,7 +1959,12 @@ int dataType2cv(int dt)
|
||||
dt == opencv_onnx::TensorProto_DataType_BFLOAT16 ? CV_16BF :
|
||||
dt == opencv_onnx::TensorProto_DataType_COMPLEX64 ? CV_32FC2 :
|
||||
dt == opencv_onnx::TensorProto_DataType_COMPLEX128 ? CV_64FC2 :
|
||||
dt == opencv_onnx::TensorProto_DataType_BOOL ? CV_Bool : -1;
|
||||
dt == opencv_onnx::TensorProto_DataType_BOOL ? CV_Bool :
|
||||
dt == opencv_onnx::TensorProto_DataType_UINT4 ? CV_8U :
|
||||
dt == opencv_onnx::TensorProto_DataType_INT4 ? CV_8S :
|
||||
dt == onnx_dtype::ONNX_FLOAT8E8M0 ? CV_32F :
|
||||
onnx_dtype::isFp8Native(dt) ? onnx_dtype::fp8NativeDepth(dt) :
|
||||
onnx_dtype::isExoticFloat(dt) ? CV_16F : -1;
|
||||
}
|
||||
|
||||
Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto, bool uint8ToInt8, const std::string base_path)
|
||||
@@ -2029,33 +2035,23 @@ Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto, bool uint8ToI
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_FLOAT16)
|
||||
{
|
||||
// FIXME, for now, we only load FP16 Tensor as FP32 Mat, full support for FP16 is required in the future.
|
||||
CV_LOG_ONCE_INFO(NULL, "DNN: load FP16 model as FP32 model, and it takes twice the FP16 RAM requirement.");
|
||||
|
||||
// ONNX saves float 16 data in two format: int32 and raw_data.
|
||||
// Load FP16 natively as CV_16F; ONNX stores it in int32_data or raw_data.
|
||||
// Link: https://github.com/onnx/onnx/issues/4460#issuecomment-1224373746
|
||||
if (!tensor_proto.int32_data().empty())
|
||||
{
|
||||
size_t sz = tensor_proto.int32_data().size();
|
||||
checkPayloadSize(sz);
|
||||
std::vector<int16_t> halfvec(sz);
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_16FC1);
|
||||
const int32_t* intdata = (const int32_t*)tensor_proto.int32_data().data();
|
||||
uint16_t* dst = (uint16_t*)blob.data;
|
||||
for (size_t i = 0; i < sz; i++)
|
||||
{
|
||||
union
|
||||
{
|
||||
int16_t h;
|
||||
int32_t i;
|
||||
} u;
|
||||
u.i = intdata[i];
|
||||
halfvec[i] = u.h;
|
||||
}
|
||||
Mat(sizes, CV_16FC1, halfvec.data()).convertTo(blob, CV_32FC1);
|
||||
dst[i] = (uint16_t)(intdata[i] & 0xFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkPayloadSize(raw_data_size / sizeof(int16_t));
|
||||
Mat(sizes, CV_16FC1, rawdata).convertTo(blob, CV_32FC1);
|
||||
Mat(sizes, CV_16FC1, rawdata).copyTo(blob);
|
||||
}
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_BFLOAT16)
|
||||
@@ -2236,6 +2232,58 @@ Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto, bool uint8ToI
|
||||
else
|
||||
Mat(sizes, CV_64UC1, rawdata).copyTo(blob);
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_FLOAT8E4M3FN ||
|
||||
datatype == opencv_onnx::TensorProto_DataType_FLOAT8E4M3FNUZ)
|
||||
{
|
||||
// E4M3FN/E4M3FNUZ have a native depth: keep the raw FP8 bytes.
|
||||
checkPayloadSize(raw_data_size);
|
||||
blob.create((int)sizes.size(), sizes.data(),
|
||||
CV_MAKETYPE(onnx_dtype::fp8NativeDepth(datatype), 1));
|
||||
memcpy(blob.data, rawdata, (size_t)blob.total() * blob.elemSize());
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_FLOAT8E5M2 ||
|
||||
datatype == opencv_onnx::TensorProto_DataType_FLOAT8E5M2FNUZ)
|
||||
{
|
||||
// E5M2/E5M2FNUZ have no native depth: decode losslessly into CV_16F.
|
||||
const onnx_dtype::Fp8Fmt fmt = onnx_dtype::fp8FmtFor(datatype);
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_16FC1);
|
||||
const uchar* src = (const uchar*)rawdata;
|
||||
hfloat* dst = blob.ptr<hfloat>();
|
||||
for (size_t i = 0, total = blob.total(); i < total; i++)
|
||||
dst[i] = hfloat(onnx_dtype::fp8ToF32(src[i], fmt));
|
||||
}
|
||||
else if (datatype == onnx_dtype::ONNX_FLOAT8E8M0)
|
||||
{
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_32FC1);
|
||||
const uchar* src = (const uchar*)rawdata;
|
||||
float* dst = blob.ptr<float>();
|
||||
for (size_t i = 0, total = blob.total(); i < total; i++)
|
||||
dst[i] = onnx_dtype::e8m0ToF32(src[i]);
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_FLOAT4E2M1)
|
||||
{
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_16FC1);
|
||||
const uchar* src = (const uchar*)rawdata;
|
||||
hfloat* dst = blob.ptr<hfloat>();
|
||||
for (size_t i = 0, total = blob.total(); i < total; i++)
|
||||
dst[i] = hfloat(onnx_dtype::fp4ToF32(onnx_dtype::unpackNibble(src, i)));
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_INT4)
|
||||
{
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_8SC1);
|
||||
const uchar* src = (const uchar*)rawdata;
|
||||
schar* dst = blob.ptr<schar>();
|
||||
for (size_t i = 0, total = blob.total(); i < total; i++)
|
||||
dst[i] = onnx_dtype::int4SignExtend(onnx_dtype::unpackNibble(src, i));
|
||||
}
|
||||
else if (datatype == opencv_onnx::TensorProto_DataType_UINT4)
|
||||
{
|
||||
blob.create((int)sizes.size(), sizes.data(), CV_8UC1);
|
||||
const uchar* src = (const uchar*)rawdata;
|
||||
uchar* dst = blob.ptr<uchar>();
|
||||
for (size_t i = 0, total = blob.total(); i < total; i++)
|
||||
dst[i] = onnx_dtype::unpackNibble(src, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// @TODO: refactor the error handling
|
||||
|
||||
@@ -61,9 +61,6 @@ static T getScalarFromMat(Mat m)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static std::string dataType2str(int dt)
|
||||
{
|
||||
const char* str =
|
||||
@@ -111,6 +108,7 @@ protected:
|
||||
Ptr<Graph> parseGraph(opencv_onnx::GraphProto* graph_proto, bool mainGraph);
|
||||
void parseNode(const opencv_onnx::NodeProto& node_proto);
|
||||
bool parseValueInfo(const opencv_onnx::ValueInfoProto& valueInfoProto, ArgData& data);
|
||||
int findGraphTensorOnnxType(const std::string& name) const;
|
||||
Mat parseTensor(const opencv_onnx::TensorProto& tensorProto);
|
||||
void rememberMissingOp(const std::string& opname);
|
||||
|
||||
@@ -718,6 +716,14 @@ Net ONNXImporter2::parseModel()
|
||||
parseOperatorSet();
|
||||
Ptr<Graph> mainGraph = parseGraph(graph_proto, true);
|
||||
netimpl->mainGraph = mainGraph;
|
||||
// Capture declared output dtypes before prepareForInference() replaces them with computed ones.
|
||||
if (mainGraph)
|
||||
{
|
||||
const std::vector<Arg>& outs = mainGraph->outputs();
|
||||
netimpl->mainGraphOutTypes.resize(outs.size());
|
||||
for (size_t i = 0; i < outs.size(); i++)
|
||||
netimpl->mainGraphOutTypes[i] = netimpl->args.at(outs[i].idx).type;
|
||||
}
|
||||
netimpl->modelFormat = DNN_MODEL_ONNX;
|
||||
netimpl->originalLayout = DATA_LAYOUT_NCHW;
|
||||
// netimpl->onnx_opset = onnx_opset;
|
||||
@@ -1689,10 +1695,37 @@ void ONNXImporter2::parseCast2(LayerParams& layerParams, const opencv_onnx::Node
|
||||
addLayer(layerParams, node_proto);
|
||||
}
|
||||
|
||||
// Returns a graph tensor's ONNX data_type by name, or -1 if unknown.
|
||||
int ONNXImporter2::findGraphTensorOnnxType(const std::string& name) const
|
||||
{
|
||||
if (!curr_graph_proto)
|
||||
return -1;
|
||||
const opencv_onnx::GraphProto& g = *curr_graph_proto;
|
||||
for (int i = 0; i < g.input_size(); i++)
|
||||
if (g.input(i).name() == name && g.input(i).has_type() && g.input(i).type().has_tensor_type())
|
||||
return g.input(i).type().tensor_type().elem_type();
|
||||
for (int i = 0; i < g.value_info_size(); i++)
|
||||
if (g.value_info(i).name() == name && g.value_info(i).has_type() && g.value_info(i).type().has_tensor_type())
|
||||
return g.value_info(i).type().tensor_type().elem_type();
|
||||
for (int i = 0; i < g.output_size(); i++)
|
||||
if (g.output(i).name() == name && g.output(i).has_type() && g.output(i).type().has_tensor_type())
|
||||
return g.output(i).type().tensor_type().elem_type();
|
||||
for (int i = 0; i < g.initializer_size(); i++)
|
||||
if (g.initializer(i).name() == name)
|
||||
return g.initializer(i).data_type();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ONNXImporter2::parseCastLike(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
|
||||
{
|
||||
CV_CheckEQ(node_proto.input_size(), 2, "CastLike requires two inputs");
|
||||
layerParams.type = "Cast2";
|
||||
if (!layerParams.has("to"))
|
||||
{
|
||||
int elemType = findGraphTensorOnnxType(node_proto.input(1));
|
||||
if (elemType > 0)
|
||||
layerParams.set("to", elemType);
|
||||
}
|
||||
addLayer(layerParams, node_proto);
|
||||
}
|
||||
|
||||
|
||||
@@ -416,6 +416,174 @@ CASE(test_castlike_STRING_to_FLOAT_expanded)
|
||||
#if SKIP_SET_1
|
||||
SKIP;
|
||||
#endif
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FN_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_UINT4)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_UINT4_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT4E2M1)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT4E2M1_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E4M3FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E4M3FN_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E5M2FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_FLOAT8E5M2_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_INT4)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT_to_INT4_expanded)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_FLOAT4E2M1)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_INT4)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT16_to_UINT4)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT4E2M1_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT4E2M1_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E4M3FNUZ_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E4M3FNUZ_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E4M3FN_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E4M3FN_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E5M2FNUZ_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E5M2FNUZ_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E5M2_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT8E5M2_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_FLOAT4E2M1)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_INT4)
|
||||
SKIP;
|
||||
CASE(test_cast_FLOAT_to_UINT4)
|
||||
SKIP;
|
||||
CASE(test_cast_INT4_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_INT4_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_INT4_to_INT8)
|
||||
SKIP;
|
||||
CASE(test_cast_UINT4_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_UINT4_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_UINT4_to_UINT8)
|
||||
SKIP;
|
||||
CASE(test_cast_e8m0_FLOAT16_to_FLOAT8E8M0)
|
||||
SKIP;
|
||||
CASE(test_cast_e8m0_FLOAT8E8M0_to_FLOAT)
|
||||
SKIP;
|
||||
CASE(test_cast_e8m0_FLOAT8E8M0_to_FLOAT16)
|
||||
SKIP;
|
||||
CASE(test_cast_e8m0_FLOAT_to_FLOAT8E8M0)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT16_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT16_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_cast_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT4E2M1)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT4E2M1_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E4M3FN)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E4M3FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E4M3FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E4M3FN_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E5M2)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E5M2FNUZ)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E5M2FNUZ_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_FLOAT8E5M2_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_INT4)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_INT4_expanded)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_UINT4)
|
||||
SKIP;
|
||||
CASE(test_castlike_FLOAT16_to_UINT4_expanded)
|
||||
SKIP;
|
||||
CASE(test_ceil)
|
||||
// no filter
|
||||
CASE(test_ceil_example)
|
||||
|
||||
@@ -39,63 +39,7 @@
|
||||
"test_bernoulli_expanded", // ---- same as above ---
|
||||
"test_bernoulli_seed", // ---- same as above ---
|
||||
"test_bernoulli_seed_expanded", // ---- same as above ---
|
||||
"test_cast_FLOAT16_to_FLOAT4E2M1",
|
||||
"test_cast_FLOAT16_to_FLOAT8E4M3FN",
|
||||
"test_cast_FLOAT16_to_FLOAT8E4M3FNUZ",
|
||||
"test_cast_FLOAT16_to_FLOAT8E5M2",
|
||||
"test_cast_FLOAT16_to_FLOAT8E5M2FNUZ",
|
||||
"test_cast_FLOAT16_to_INT4",
|
||||
"test_cast_FLOAT16_to_UINT4",
|
||||
"test_cast_FLOAT4E2M1_to_FLOAT",
|
||||
"test_cast_FLOAT4E2M1_to_FLOAT16",
|
||||
"test_cast_FLOAT8E4M3FNUZ_to_FLOAT",
|
||||
"test_cast_FLOAT8E4M3FNUZ_to_FLOAT16",
|
||||
"test_cast_FLOAT8E4M3FN_to_FLOAT",
|
||||
"test_cast_FLOAT8E4M3FN_to_FLOAT16",
|
||||
"test_cast_FLOAT8E5M2FNUZ_to_FLOAT",
|
||||
"test_cast_FLOAT8E5M2FNUZ_to_FLOAT16",
|
||||
"test_cast_FLOAT8E5M2_to_FLOAT",
|
||||
"test_cast_FLOAT8E5M2_to_FLOAT16",
|
||||
"test_cast_FLOAT_to_FLOAT4E2M1",
|
||||
"test_cast_FLOAT_to_FLOAT8E4M3FN",
|
||||
"test_cast_FLOAT_to_FLOAT8E4M3FNUZ",
|
||||
"test_cast_FLOAT_to_FLOAT8E5M2",
|
||||
"test_cast_FLOAT_to_FLOAT8E5M2FNUZ",
|
||||
"test_cast_FLOAT_to_INT4",
|
||||
"test_cast_FLOAT_to_UINT4",
|
||||
"test_cast_INT4_to_FLOAT",
|
||||
"test_cast_INT4_to_FLOAT16",
|
||||
"test_cast_INT4_to_INT8",
|
||||
"test_cast_UINT4_to_FLOAT",
|
||||
"test_cast_UINT4_to_FLOAT16",
|
||||
"test_cast_UINT4_to_UINT8",
|
||||
"test_cast_e8m0_FLOAT16_to_FLOAT8E8M0",
|
||||
"test_cast_e8m0_FLOAT8E8M0_to_FLOAT",
|
||||
"test_cast_e8m0_FLOAT8E8M0_to_FLOAT16",
|
||||
"test_cast_e8m0_FLOAT_to_FLOAT8E8M0",
|
||||
"test_cast_no_saturate_FLOAT16_to_FLOAT8E4M3FN",
|
||||
"test_cast_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ",
|
||||
"test_cast_no_saturate_FLOAT16_to_FLOAT8E5M2",
|
||||
"test_cast_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ",
|
||||
"test_cast_no_saturate_FLOAT_to_FLOAT8E4M3FN",
|
||||
"test_cast_no_saturate_FLOAT_to_FLOAT8E4M3FNUZ",
|
||||
"test_cast_no_saturate_FLOAT_to_FLOAT8E5M2",
|
||||
"test_cast_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_FLOAT16_to_FLOAT4E2M1",
|
||||
"test_castlike_FLOAT16_to_FLOAT4E2M1_expanded",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E4M3FN",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E4M3FNUZ",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E4M3FNUZ_expanded",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E4M3FN_expanded",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E5M2",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E5M2FNUZ_expanded",
|
||||
"test_castlike_FLOAT16_to_FLOAT8E5M2_expanded",
|
||||
"test_castlike_FLOAT16_to_INT4",
|
||||
"test_castlike_FLOAT16_to_INT4_expanded",
|
||||
"test_castlike_FLOAT16_to_UINT4",
|
||||
"test_castlike_FLOAT16_to_UINT4_expanded",
|
||||
"test_castlike_FLOAT4E2M1_to_FLOAT",
|
||||
"test_castlike_FLOAT4E2M1_to_FLOAT", // opencv_extra stores empty input data for these from-exotic CastLike cases
|
||||
"test_castlike_FLOAT4E2M1_to_FLOAT16",
|
||||
"test_castlike_FLOAT4E2M1_to_FLOAT16_expanded",
|
||||
"test_castlike_FLOAT4E2M1_to_FLOAT_expanded",
|
||||
@@ -115,21 +59,7 @@
|
||||
"test_castlike_FLOAT8E5M2_to_FLOAT16",
|
||||
"test_castlike_FLOAT8E5M2_to_FLOAT16_expanded",
|
||||
"test_castlike_FLOAT8E5M2_to_FLOAT_expanded",
|
||||
"test_castlike_FLOAT_to_FLOAT4E2M1",
|
||||
"test_castlike_FLOAT_to_FLOAT4E2M1_expanded",
|
||||
"test_castlike_FLOAT_to_FLOAT8E4M3FN",
|
||||
"test_castlike_FLOAT_to_FLOAT8E4M3FNUZ",
|
||||
"test_castlike_FLOAT_to_FLOAT8E4M3FNUZ_expanded",
|
||||
"test_castlike_FLOAT_to_FLOAT8E4M3FN_expanded",
|
||||
"test_castlike_FLOAT_to_FLOAT8E5M2",
|
||||
"test_castlike_FLOAT_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_FLOAT_to_FLOAT8E5M2FNUZ_expanded",
|
||||
"test_castlike_FLOAT_to_FLOAT8E5M2_expanded",
|
||||
"test_castlike_FLOAT_to_INT4",
|
||||
"test_castlike_FLOAT_to_INT4_expanded",
|
||||
"test_castlike_FLOAT_to_STRING",
|
||||
"test_castlike_FLOAT_to_UINT4",
|
||||
"test_castlike_FLOAT_to_UINT4_expanded",
|
||||
"test_castlike_INT4_to_FLOAT",
|
||||
"test_castlike_INT4_to_FLOAT16",
|
||||
"test_castlike_INT4_to_FLOAT16_expanded",
|
||||
@@ -143,20 +73,6 @@
|
||||
"test_castlike_UINT4_to_FLOAT_expanded",
|
||||
"test_castlike_UINT4_to_UINT8",
|
||||
"test_castlike_UINT4_to_UINT8_expanded",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FN",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FNUZ_expanded",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E4M3FN_expanded",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2FNUZ_expanded",
|
||||
"test_castlike_no_saturate_FLOAT16_to_FLOAT8E5M2_expanded",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E4M3FN",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E4M3FNUZ",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ_expanded",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2_expanded",
|
||||
"test_clip_min_greater_than_max",
|
||||
"test_col2im",
|
||||
"test_col2im_5d",
|
||||
|
||||
@@ -315,8 +315,8 @@ bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
template<>
|
||||
PyObject* pyopencv_from(const cv::Mat& m)
|
||||
{
|
||||
// NumPy has no bfloat16 dtype: widen CV_16BF to float32 (lossless).
|
||||
if( m.depth() == CV_16BF )
|
||||
// NumPy has no bfloat16 or float8 dtype: widen these to float32 (lossless).
|
||||
if( m.depth() == CV_16BF || m.depth() == CV_8F_E4M3FN || m.depth() == CV_8F_E4M3FNUZ )
|
||||
{
|
||||
cv::Mat m32f;
|
||||
ERRWRAP2(m.convertTo(m32f, CV_32F));
|
||||
|
||||
@@ -1460,7 +1460,7 @@ norm_flt_(const _Tp* src1, const _Tp* src2, size_t total, int cn, int normType,
|
||||
double norm(InputArray _src, int normType, InputArray _mask)
|
||||
{
|
||||
Mat src = _src.getMat(), mask = _mask.getMat();
|
||||
if( src.depth() == CV_16F )
|
||||
if( src.depth() == CV_16F || src.depth() == CV_8F_E4M3FN || src.depth() == CV_8F_E4M3FNUZ )
|
||||
{
|
||||
Mat src32f;
|
||||
src.convertTo(src32f, CV_32F);
|
||||
@@ -1660,6 +1660,12 @@ double norm(InputArray _src1, InputArray _src2, int normType, InputArray _mask)
|
||||
case CV_16BF:
|
||||
result = norm_flt_<cv::bfloat, short>((const cv::bfloat*)sptr1, (const cv::bfloat*)sptr2, total, cn, normType, result, mptr);
|
||||
break;
|
||||
case CV_8F_E4M3FN:
|
||||
result = norm_flt_<cv::fp8_t, schar>((const cv::fp8_t*)sptr1, (const cv::fp8_t*)sptr2, total, cn, normType, result, mptr);
|
||||
break;
|
||||
case CV_8F_E4M3FNUZ:
|
||||
result = norm_flt_<cv::fp8a_t, schar>((const cv::fp8a_t*)sptr1, (const cv::fp8a_t*)sptr2, total, cn, normType, result, mptr);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat, "");
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user