diff options
author | Niklas Haas <git@haasn.dev> | 2024-11-25 14:10:46 +0100 |
---|---|---|
committer | Niklas Haas <git@haasn.dev> | 2024-12-05 12:27:33 +0100 |
commit | 7b73ea501dc738c52daf2a2fef0c41b4ce95928f (patch) | |
tree | c4454c4d489d014d1a874a1ce0756f002a268cb0 /libavutil/tests/color_utils.c | |
parent | 06f084468e034ecb0bfbe522953c1efd77595454 (diff) | |
download | ffmpeg-7b73ea501dc738c52daf2a2fef0c41b4ce95928f.tar.gz |
avutil/tests/color_utils: add tests for av_csp_itu_eotf
Diffstat (limited to 'libavutil/tests/color_utils.c')
-rw-r--r-- | libavutil/tests/color_utils.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/libavutil/tests/color_utils.c b/libavutil/tests/color_utils.c index 9881cd2489..27ba8b529e 100644 --- a/libavutil/tests/color_utils.c +++ b/libavutil/tests/color_utils.c @@ -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]; + } + } + } + } + } } |