1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

added basic support for CV_16F (the new datatype etc.) (#12463)

* added basic support for CV_16F (the new datatype etc.). CV_USRTYPE1 is now equal to CV_16F, which may break some [rarely used] functionality. We'll see

* fixed just introduced bug in norm; reverted errorneous changes in Torch importer (need to find a better solution)

* addressed some issues found during the PR review

* restored the patch to fix some perf test failures
This commit is contained in:
Vadim Pisarevsky
2018-09-10 16:56:29 +03:00
committed by GitHub
parent dca657a2fd
commit 6d7f5871db
23 changed files with 281 additions and 278 deletions
+50 -167
View File
@@ -48,18 +48,6 @@
#include "precomp.hpp"
#if defined _WIN32 || defined WINCE
#include <windows.h>
#undef small
#undef min
#undef max
#undef abs
#endif
#if defined __SSE2__ || (defined _M_IX86_FP && 2 == _M_IX86_FP)
#include "emmintrin.h"
#endif
namespace cv
{
@@ -74,12 +62,6 @@ namespace cv
#define RNG_NEXT(x) ((uint64)(unsigned)(x)*CV_RNG_COEFF + ((x) >> 32))
#ifdef __PPC64__
#define PPC_MUL_ADD(ret, tmp, p0, p1) \
asm volatile("fmuls %0,%1,%2\n\t fadds %0,%0,%3" : "=&f" (ret) \
: "f" (tmp), "f" (p0), "f" (p1))
#endif
/***************************************************************************************\
* Pseudo-Random Number Generators (PRNGs) *
\***************************************************************************************/
@@ -154,59 +136,26 @@ template<typename T> static void
randi_( T* arr, int len, uint64* state, const DivStruct* p )
{
uint64 temp = *state;
int i = 0;
unsigned t0, t1, v0, v1;
for( i = 0; i <= len - 4; i += 4 )
for( int i = 0; i < len; i++ )
{
temp = RNG_NEXT(temp);
t0 = (unsigned)temp;
temp = RNG_NEXT(temp);
t1 = (unsigned)temp;
v0 = (unsigned)(((uint64)t0 * p[i].M) >> 32);
v1 = (unsigned)(((uint64)t1 * p[i+1].M) >> 32);
v0 = (v0 + ((t0 - v0) >> p[i].sh1)) >> p[i].sh2;
v1 = (v1 + ((t1 - v1) >> p[i+1].sh1)) >> p[i+1].sh2;
v0 = t0 - v0*p[i].d + p[i].delta;
v1 = t1 - v1*p[i+1].d + p[i+1].delta;
arr[i] = saturate_cast<T>((int)v0);
arr[i+1] = saturate_cast<T>((int)v1);
temp = RNG_NEXT(temp);
t0 = (unsigned)temp;
temp = RNG_NEXT(temp);
t1 = (unsigned)temp;
v0 = (unsigned)(((uint64)t0 * p[i+2].M) >> 32);
v1 = (unsigned)(((uint64)t1 * p[i+3].M) >> 32);
v0 = (v0 + ((t0 - v0) >> p[i+2].sh1)) >> p[i+2].sh2;
v1 = (v1 + ((t1 - v1) >> p[i+3].sh1)) >> p[i+3].sh2;
v0 = t0 - v0*p[i+2].d + p[i+2].delta;
v1 = t1 - v1*p[i+3].d + p[i+3].delta;
arr[i+2] = saturate_cast<T>((int)v0);
arr[i+3] = saturate_cast<T>((int)v1);
unsigned t = (unsigned)temp;
unsigned v = (unsigned)(((uint64)t * p[i].M) >> 32);
v = (v + ((t - v) >> p[i].sh1)) >> p[i].sh2;
v = t - v*p[i].d + p[i].delta;
arr[i] = saturate_cast<T>((int)v);
}
for( ; i < len; i++ )
{
temp = RNG_NEXT(temp);
t0 = (unsigned)temp;
v0 = (unsigned)(((uint64)t0 * p[i].M) >> 32);
v0 = (v0 + ((t0 - v0) >> p[i].sh1)) >> p[i].sh2;
v0 = t0 - v0*p[i].d + p[i].delta;
arr[i] = saturate_cast<T>((int)v0);
}
*state = temp;
}
#define DEF_RANDI_FUNC(suffix, type) \
static void randBits_##suffix(type* arr, int len, uint64* state, \
const Vec2i* p, bool small_flag) \
const Vec2i* p, void*, bool small_flag) \
{ randBits_(arr, len, state, p, small_flag); } \
\
static void randi_##suffix(type* arr, int len, uint64* state, \
const DivStruct* p, bool ) \
const DivStruct* p, void*, bool ) \
{ randi_(arr, len, state, p); }
DEF_RANDI_FUNC(8u, uchar)
@@ -215,131 +164,62 @@ DEF_RANDI_FUNC(16u, ushort)
DEF_RANDI_FUNC(16s, short)
DEF_RANDI_FUNC(32s, int)
static void randf_32f( float* arr, int len, uint64* state, const Vec2f* p, bool )
static void randf_32f( float* arr, int len, uint64* state, const Vec2f* p, void*, bool )
{
uint64 temp = *state;
int i = 0;
for( ; i <= len - 4; i += 4 )
for( int i = 0; i < len; i++ )
{
float f[4];
f[0] = (float)(int)(temp = RNG_NEXT(temp));
f[1] = (float)(int)(temp = RNG_NEXT(temp));
f[2] = (float)(int)(temp = RNG_NEXT(temp));
f[3] = (float)(int)(temp = RNG_NEXT(temp));
// handwritten SSE is required not for performance but for numerical stability!
// both 32-bit gcc and MSVC compilers trend to generate double precision SSE
// while 64-bit compilers generate single precision SIMD instructions
// so manual vectorisation forces all compilers to the single precision
#if defined __SSE2__ || (defined _M_IX86_FP && 2 == _M_IX86_FP)
__m128 q0 = _mm_loadu_ps((const float*)(p + i));
__m128 q1 = _mm_loadu_ps((const float*)(p + i + 2));
__m128 q01l = _mm_unpacklo_ps(q0, q1);
__m128 q01h = _mm_unpackhi_ps(q0, q1);
__m128 p0 = _mm_unpacklo_ps(q01l, q01h);
__m128 p1 = _mm_unpackhi_ps(q01l, q01h);
_mm_storeu_ps(arr + i, _mm_add_ps(_mm_mul_ps(_mm_loadu_ps(f), p0), p1));
#elif defined __ARM_NEON && defined __aarch64__
// handwritten NEON is required not for performance but for numerical stability!
// 64bit gcc tends to use fmadd instead of separate multiply and add
// use volatile to ensure to separate the multiply and add
float32x4x2_t q = vld2q_f32((const float*)(p + i));
float32x4_t p0 = q.val[0];
float32x4_t p1 = q.val[1];
volatile float32x4_t v0 = vmulq_f32(vld1q_f32(f), p0);
vst1q_f32(arr+i, vaddq_f32(v0, p1));
#elif defined __PPC64__
// inline asm is required for numerical stability!
// compilers tends to use floating multiply-add single(fmadds)
// instead of separate multiply and add
PPC_MUL_ADD(arr[i+0], f[0], p[i+0][0], p[i+0][1]);
PPC_MUL_ADD(arr[i+1], f[1], p[i+1][0], p[i+1][1]);
PPC_MUL_ADD(arr[i+2], f[2], p[i+2][0], p[i+2][1]);
PPC_MUL_ADD(arr[i+3], f[3], p[i+3][0], p[i+3][1]);
#else
arr[i+0] = f[0]*p[i+0][0] + p[i+0][1];
arr[i+1] = f[1]*p[i+1][0] + p[i+1][1];
arr[i+2] = f[2]*p[i+2][0] + p[i+2][1];
arr[i+3] = f[3]*p[i+3][0] + p[i+3][1];
#endif
int t = (int)(temp = RNG_NEXT(temp));
arr[i] = (float)(t*p[i][0]);
}
for( ; i < len; i++ )
{
temp = RNG_NEXT(temp);
#if defined __SSE2__ || (defined _M_IX86_FP && 2 == _M_IX86_FP)
_mm_store_ss(arr + i, _mm_add_ss(
_mm_mul_ss(_mm_set_ss((float)(int)temp), _mm_set_ss(p[i][0])),
_mm_set_ss(p[i][1]))
);
#elif defined __ARM_NEON && defined __aarch64__
float32x2_t t = vadd_f32(vmul_f32(
vdup_n_f32((float)(int)temp), vdup_n_f32(p[i][0])),
vdup_n_f32(p[i][1]));
arr[i] = vget_lane_f32(t, 0);
#elif defined __PPC64__
PPC_MUL_ADD(arr[i], (float)(int)temp, p[i][0], p[i][1]);
#else
arr[i] = (int)temp*p[i][0] + p[i][1];
#endif
}
*state = temp;
}
// add bias separately to make the generated random numbers
// more deterministic, independent of
// architecture details (FMA instruction use etc.)
hal::addRNGBias32f(arr, &p[0][0], len);
}
static void
randf_64f( double* arr, int len, uint64* state, const Vec2d* p, bool )
randf_64f( double* arr, int len, uint64* state, const Vec2d* p, void*, bool )
{
uint64 temp = *state;
int64 v = 0;
int i;
for( i = 0; i <= len - 4; i += 4 )
{
double f0, f1;
temp = RNG_NEXT(temp);
v = (temp >> 32)|(temp << 32);
f0 = v*p[i][0] + p[i][1];
temp = RNG_NEXT(temp);
v = (temp >> 32)|(temp << 32);
f1 = v*p[i+1][0] + p[i+1][1];
arr[i] = f0; arr[i+1] = f1;
temp = RNG_NEXT(temp);
v = (temp >> 32)|(temp << 32);
f0 = v*p[i+2][0] + p[i+2][1];
temp = RNG_NEXT(temp);
v = (temp >> 32)|(temp << 32);
f1 = v*p[i+3][0] + p[i+3][1];
arr[i+2] = f0; arr[i+3] = f1;
}
for( ; i < len; i++ )
for( int i = 0; i < len; i++ )
{
temp = RNG_NEXT(temp);
v = (temp >> 32)|(temp << 32);
arr[i] = v*p[i][0] + p[i][1];
int64 v = (temp >> 32)|(temp << 32);
arr[i] = v*p[i][0];
}
*state = temp;
hal::addRNGBias64f(arr, &p[0][0], len);
}
typedef void (*RandFunc)(uchar* arr, int len, uint64* state, const void* p, bool small_flag);
static void randf_16f( float16_t* arr, int len, uint64* state, const Vec2f* p, float* fbuf, bool )
{
uint64 temp = *state;
for( int i = 0; i < len; i++ )
{
float f = (float)(int)(temp = RNG_NEXT(temp));
fbuf[i] = f*p[i][0];
}
*state = temp;
// add bias separately to make the generated random numbers
// more deterministic, independent of
// architecture details (FMA instruction use etc.)
hal::addRNGBias32f(fbuf, &p[0][0], len);
hal::cvt32f16f(fbuf, arr, len);
}
typedef void (*RandFunc)(uchar* arr, int len, uint64* state, const void* p, void* tempbuf, bool small_flag);
static RandFunc randTab[][8] =
{
{
(RandFunc)randi_8u, (RandFunc)randi_8s, (RandFunc)randi_16u, (RandFunc)randi_16s,
(RandFunc)randi_32s, (RandFunc)randf_32f, (RandFunc)randf_64f, 0
(RandFunc)randi_32s, (RandFunc)randf_32f, (RandFunc)randf_64f, (RandFunc)randf_16f
},
{
(RandFunc)randBits_8u, (RandFunc)randBits_8s, (RandFunc)randBits_16u, (RandFunc)randBits_16s,
@@ -350,7 +230,7 @@ static RandFunc randTab[][8] =
/*
The code below implements the algorithm described in
"The Ziggurat Method for Generating Random Variables"
by Marsaglia and Tsang, Journal of Statistical Software.
by George Marsaglia and Wai Wan Tsang, Journal of Statistical Software, 2007.
*/
static void
randn_0_1_32f( float* arr, int len, uint64* state )
@@ -631,8 +511,8 @@ void RNG::fill( InputOutputArray _mat, int disttype,
// for each channel i compute such dparam[0][i] & dparam[1][i],
// so that a signed 32/64-bit integer X is transformed to
// the range [param1.val[i], param2.val[i]) using
// dparam[1][i]*X + dparam[0][i]
if( depth == CV_32F )
// dparam[0][i]*X + dparam[1][i]
if( depth != CV_64F )
{
fp = (Vec2f*)(parambuf + cn*2);
for( j = 0; j < cn; j++ )
@@ -704,6 +584,7 @@ void RNG::fill( InputOutputArray _mat, int disttype,
AutoBuffer<double> buf;
uchar* param = 0;
float* nbuf = 0;
float* tmpbuf = 0;
if( disttype == UNIFORM )
{
@@ -727,12 +608,14 @@ void RNG::fill( InputOutputArray _mat, int disttype,
p[j + k] = ip[k];
}
}
else if( depth == CV_32F )
else if( depth != CV_64F )
{
Vec2f* p = (Vec2f*)param;
for( j = 0; j < blockSize*cn; j += cn )
for( k = 0; k < cn; k++ )
p[j + k] = fp[k];
if( depth == CV_16F )
tmpbuf = (float*)p + blockSize*cn*2;
}
else
{
@@ -755,7 +638,7 @@ void RNG::fill( InputOutputArray _mat, int disttype,
int len = std::min(total - j, blockSize);
if( disttype == CV_RAND_UNI )
func( ptr, len*cn, &state, param, smallFlag );
func( ptr, len*cn, &state, param, tmpbuf, smallFlag );
else
{
randn_0_1_32f(nbuf, len*cn, &state);