avutil/tests/color_utils: add tests for av_csp_itu_eotf

This commit is contained in:
Niklas Haas 2024-11-25 14:10:46 +01:00
parent 06f084468e
commit 7b73ea501d
2 changed files with 2760 additions and 0 deletions

View File

@ -20,12 +20,35 @@
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "libavutil/csp.h"
#include "libavutil/macros.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixfmt.h"
static inline int fuzzy_equal(double a, double b)
{
const double epsilon = fmax(fmax(fabs(a), fabs(b)), 1.0) * 1e-7;
return fabs(a - b) <= epsilon;
}
#define TEST_EOTF(func, input, ref) do \
{ \
const double _b[3] = { (ref)[0], (ref)[1], (ref)[2] }; \
double _a[3] = { (input)[0], (input)[1], (input)[2] }; \
func(Lw, Lb, _a); \
for (int _i = 0; _i < 3; _i++) { \
if (!fuzzy_equal(_a[_i], _b[_i])) { \
printf("FAIL: trc=%s %s(%g, %g, %s) != %s\n" \
" expected {%g, %g, %g}, got {%g, %g, %g}\n", \
trc_name, #func, Lw, Lb, #input, #ref, \
_b[0], _b[1], _b[2], _a[0], _a[1], _a[2]); \
return 1; \
} \
} \
} while (0)
int main(int argc, char *argv[])
{
static const double test_data[] = {
@ -53,4 +76,91 @@ int main(int argc, char *argv[])
}
}
}
for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) {
av_csp_eotf_function eotf = av_csp_itu_eotf(trc);
av_csp_eotf_function eotf_inv = av_csp_itu_eotf_inv(trc);
const char *trc_name = av_color_transfer_name(trc);
if (!eotf)
continue;
if (trc == AVCOL_TRC_SMPTE2084) {
/* This one is equivalent to the TRC already tested above */
continue;
} else if (trc == AVCOL_TRC_SMPTE428) {
/* Test vectors from SMPTE RP-431-2 */
const struct { double E_xyz[3]; double luma; } tests[] = {
#define XYZ(X, Y, Z) { X / 4095.0, Y / 4095.0, Z / 4095.0 }
{ XYZ( 379, 396, 389), 0.14 },
{ XYZ( 759, 792, 778), 0.75 },
{ XYZ(1138, 1188, 1167), 2.12 },
{ XYZ(1518, 1584, 1556), 4.45 },
{ XYZ(1897, 1980, 1945), 7.94 },
{ XYZ(2276, 2376, 2334), 12.74 },
{ XYZ(2656, 2772, 2723), 19.01 },
{ XYZ(3035, 3168, 3112), 26.89 },
{ XYZ(3415, 3564, 3501), 36.52 },
{ XYZ(3794, 3960, 3890), 48.02 },
};
/* DCI reference display */
const double luminance = 48.00;
const double contrast = 2000;
/* Solve for Lw - Lb = luminance, Lw / Lb = contrast */
const double Lb = luminance / (contrast - 1);
const double Lw = Lb + luminance;
for (int i = 0; i < FF_ARRAY_ELEMS(tests); i++) {
double L_xyz[3];
memcpy(L_xyz, tests[i].E_xyz, sizeof(L_xyz));
eotf(Lw, Lb, L_xyz);
printf("trc=%s EOTF(%g, %g, {%g, %g, %g}) = {%g, %g %g}, expected Y=%f\n",
trc_name, Lw, Lb,
tests[i].E_xyz[0], tests[i].E_xyz[1], tests[i].E_xyz[2],
L_xyz[0], L_xyz[1], L_xyz[2], tests[i].luma);
if (fabs(L_xyz[1] - tests[i].luma) > 0.01) {
printf(" FAIL\n");
return 1;
}
}
} else {
/* Normal, display-relative RGB curve */
static const double black_points[] = { 0.0, 1e-6, 0.1, 1.5 };
static const double white_points[] = { 50.0, 100.0, 203.0, 1000.0, 10000.0 };
for (int i = 0; i < FF_ARRAY_ELEMS(black_points); i++) {
for (int j = 0; j < FF_ARRAY_ELEMS(white_points); j++) {
const double Lb = black_points[i];
const double Lw = white_points[j];
const double all0[3] = { 0.0, 0.0, 0.0 };
const double all1[3] = { 1.0, 1.0, 1.0 };
const double black[3] = { Lb, Lb, Lb };
const double white[3] = { Lw, Lw, Lw };
double L_prev;
TEST_EOTF(eotf, all0, black);
TEST_EOTF(eotf, all1, white);
TEST_EOTF(eotf_inv, black, all0);
TEST_EOTF(eotf_inv, white, all1);
/* Test round-trip on grayscale ramp */
for (double x = 0.0; x < 1.0; x += 0.1) {
const double E[3] = { x, x, x };
double L[3] = { x, x, x };
eotf(Lw, Lb, L);
printf("trc=%s EOTF(%g, %g, {%g}) = {%g}\n",
trc_name, Lw, Lb, E[1], L[1]);
TEST_EOTF(eotf_inv, L, E);
if (x > 0.0 && L[1] <= L_prev) {
printf(" FAIL: non-monotonic!\n");
return 1;
}
L_prev = L[1];
}
}
}
}
}
}

File diff suppressed because it is too large Load Diff