Joshua Moerman
11 years ago
3 changed files with 102 additions and 76 deletions
@ -1,49 +1,91 @@ |
|||
#pragma once |
|||
|
|||
namespace jcmp { |
|||
// input in [0, 1], output [0, 255]
|
|||
inline uint8_t clamp_round(double x){ |
|||
if(x <= 0.0) return 0; |
|||
if(x >= 1.0) return 255; |
|||
return uint8_t(std::round(255.0*x)); |
|||
} |
|||
#include <functional> |
|||
|
|||
// parameters which need to be stored in the file
|
|||
// to be able to dequantize
|
|||
struct quantize_params { |
|||
double f_max_abs; |
|||
double f_min_abs; |
|||
}; |
|||
|
|||
// Quantization may be non linear (given by f and f_inv)
|
|||
struct quantization { |
|||
std::function<double(double)> f; |
|||
std::function<double(double)> f_inv; |
|||
quantize_params p; |
|||
|
|||
quantization() = default; |
|||
|
|||
template <typename F> |
|||
quantization(F const & f_, F const & f_inv_, double max_abs, double min_abs) |
|||
: f(f_) |
|||
, f_inv(f_inv_) |
|||
, p{f(max_abs), f(min_abs)} |
|||
{} |
|||
|
|||
uint8_t forwards(double x){ |
|||
double y = std::abs(x); |
|||
y = (f(y) - p.f_min_abs) / (p.f_max_abs - p.f_min_abs); |
|||
if(x < 0) y = -y; |
|||
return clamp_round(0.5 * (y + 1.0)); |
|||
namespace jcmp { |
|||
// Will quantize a double such that it fits in a uint8_t
|
|||
// Also support non-linear quantization (which is usefull
|
|||
// when the data is non-uniformly distributed)
|
|||
namespace quantization { |
|||
// input in [0, 1], output [0, 255]
|
|||
inline uint8_t clamp_round(double x){ |
|||
if(x <= 0.0) return 0; |
|||
if(x >= 1.0) return 255; |
|||
return uint8_t(std::round(255.0*x)); |
|||
} |
|||
|
|||
double backwards(uint8_t x){ |
|||
double y = 2.0 * x / 255.0 - 1.0; |
|||
y = (p.f_max_abs - p.f_min_abs) * y; |
|||
if(y < 0) return -f_inv(-y + p.f_min_abs); |
|||
return f_inv(y + p.f_min_abs); |
|||
// parameters which need to be stored in the file
|
|||
// to be able to dequantize
|
|||
// TODO: add type of quantization
|
|||
struct parameters { |
|||
double f_max_abs; |
|||
double f_min_abs; |
|||
}; |
|||
|
|||
// the basic struct doing the quantization
|
|||
struct base { |
|||
// f needs to be of type R+ -> R
|
|||
// and F-inv should be its inverse
|
|||
std::function<double(double)> f; |
|||
std::function<double(double)> f_inv; |
|||
parameters p; |
|||
|
|||
uint8_t forwards(double x){ |
|||
double y = std::abs(x); |
|||
y = (f(y) - p.f_min_abs) / (p.f_max_abs - p.f_min_abs); |
|||
if(x < 0) y = -y; |
|||
return clamp_round(0.5 * (y + 1.0)); |
|||
} |
|||
|
|||
double backwards(uint8_t x){ |
|||
double y = 2.0 * x / 255.0 - 1.0; |
|||
y = (p.f_max_abs - p.f_min_abs) * y; |
|||
if(y < 0) return -f_inv(-y + p.f_min_abs); |
|||
return f_inv(y + p.f_min_abs); |
|||
} |
|||
}; |
|||
|
|||
// kinds of quantization which come out of the
|
|||
// box with get(...)
|
|||
enum class type { |
|||
linear, |
|||
logarithmic, |
|||
square_root |
|||
}; |
|||
|
|||
// non-overloaded versions
|
|||
double id(double x) { return x; } |
|||
double log(double x) { return std::log(x); } |
|||
double exp(double x) { return std::exp(x); } |
|||
double sqrt(double x) { return std::sqrt(x); } |
|||
double sqr(double x) { return x*x; } |
|||
|
|||
base get(type t, double max_abs, double min_abs, bool apply = true){ |
|||
base b; |
|||
switch (t) { |
|||
case type::linear: |
|||
b.f = &id; |
|||
b.f_inv = &id; |
|||
break; |
|||
case type::logarithmic: |
|||
b.f = &log; |
|||
b.f_inv = &exp; |
|||
break; |
|||
case type::square_root: |
|||
b.f = &sqrt; |
|||
b.f_inv = &sqr; |
|||
break; |
|||
} |
|||
if(apply){ |
|||
b.p.f_max_abs = b.f(max_abs); |
|||
b.p.f_min_abs = b.f(min_abs); |
|||
} else { |
|||
b.p.f_max_abs = max_abs; |
|||
b.p.f_min_abs = min_abs; |
|||
} |
|||
return b; |
|||
} |
|||
}; |
|||
|
|||
static_assert(sizeof(quantize_params) == 16, "struct not propery packed"); |
|||
static_assert(sizeof(parameters) == 16, "struct not propery packed"); |
|||
} |
|||
} |
|||
|
Reference in new issue