smartYUV improvements

* switch to Rec709 transfer function in SmartYUV
* use Rec709 for Gray evaluation too.
* stop iterations if error is going up

See paragraph 1.2 and 3.2:
https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-6-201506-I!!PDF-E.pdf

(digest: https://en.wikipedia.org/wiki/Rec._709#Transfer_characteristics)

suggested by pdknsk@gmail.com on the mailing list

Change-Id: I12b5f4d3e318dd5134984e1c0a4b244a620a57d7
This commit is contained in:
Pascal Massimino 2016-09-19 13:40:46 -07:00
parent 21e7537abe
commit 573cce270e

View File

@ -171,9 +171,9 @@ typedef uint16_t fixed_y_t; // unsigned type with extra SFIX precision for W
#if defined(USE_GAMMA_COMPRESSION)
// float variant of gamma-correction
// We use tables of different size and precision, along with a 'real-world'
// Gamma value close to ~2.
#define kGammaF 2.2
// We use tables of different size and precision for the Rec709
// transfer function.
#define kGammaF (1./0.45)
static float kGammaToLinearTabF[MAX_Y_T + 1]; // size scales with Y_FIX
static float kLinearToGammaTabF[kGammaTabSize + 2];
static volatile int kGammaTablesFOk = 0;
@ -183,11 +183,26 @@ static WEBP_TSAN_IGNORE_FUNCTION void InitGammaTablesF(void) {
int v;
const double norm = 1. / MAX_Y_T;
const double scale = 1. / kGammaTabSize;
const double a = 0.099;
const double thresh = 0.018;
for (v = 0; v <= MAX_Y_T; ++v) {
kGammaToLinearTabF[v] = (float)pow(norm * v, kGammaF);
const double g = norm * v;
if (g <= thresh * 4.5) {
kGammaToLinearTabF[v] = (float)(g / 4.5);
} else {
const double a_rec = 1. / (1. + a);
kGammaToLinearTabF[v] = (float)pow(a_rec * (g + a), kGammaF);
}
}
for (v = 0; v <= kGammaTabSize; ++v) {
kLinearToGammaTabF[v] = (float)(MAX_Y_T * pow(scale * v, 1. / kGammaF));
const double g = scale * v;
double value;
if (g <= thresh) {
value = 4.5 * g;
} else {
value = (1. + a) * pow(g, 1. / kGammaF) - a;
}
kLinearToGammaTabF[v] = (float)(MAX_Y_T * value);
}
// to prevent small rounding errors to cause read-overflow:
kLinearToGammaTabF[kGammaTabSize + 1] = kLinearToGammaTabF[kGammaTabSize];
@ -235,12 +250,12 @@ static fixed_y_t clip_y(int y) {
//------------------------------------------------------------------------------
static int RGBToGray(int r, int g, int b) {
const int luma = 19595 * r + 38470 * g + 7471 * b + YUV_HALF;
const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF;
return (luma >> YUV_FIX);
}
static float RGBToGrayF(float r, float g, float b) {
return 0.299f * r + 0.587f * g + 0.114f * b;
return (float)(0.2126 * r + 0.7152 * g + 0.0722 * b);
}
static int ScaleDown(int a, int b, int c, int d) {
@ -427,6 +442,7 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
const int h = (picture->height + 1) & ~1;
const int uv_w = w >> 1;
const int uv_h = h >> 1;
uint64_t prev_diff_y_sum = ~0;
int i, j, iter;
// TODO(skal): allocate one big memory chunk. But for now, it's easier
@ -514,7 +530,11 @@ static int PreprocessARGB(const uint8_t* const r_ptr,
}
}
// test exit condition
if (iter > 0 && diff_y_sum < diff_y_threshold) break;
if (iter > 0) {
if (diff_y_sum < diff_y_threshold) break;
if (diff_y_sum > prev_diff_y_sum) break;
}
prev_diff_y_sum = diff_y_sum;
}
// final reconstruction
ok = ConvertWRGBToYUV(best_y, best_uv, picture);