aboutsummaryrefslogtreecommitdiffstats
path: root/libavutil/tests
diff options
context:
space:
mode:
authorNiklas Haas <git@haasn.dev>2024-11-25 14:10:46 +0100
committerNiklas Haas <git@haasn.dev>2024-12-05 12:27:33 +0100
commit7b73ea501dc738c52daf2a2fef0c41b4ce95928f (patch)
treec4454c4d489d014d1a874a1ce0756f002a268cb0 /libavutil/tests
parent06f084468e034ecb0bfbe522953c1efd77595454 (diff)
downloadffmpeg-7b73ea501dc738c52daf2a2fef0c41b4ce95928f.tar.gz
avutil/tests/color_utils: add tests for av_csp_itu_eotf
Diffstat (limited to 'libavutil/tests')
-rw-r--r--libavutil/tests/color_utils.c110
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];
+ }
+ }
+ }
+ }
+ }
}