summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc
blob: 76165d26e721fafadf32e14d003ad525060018df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/cms/tone_mapping_test.cc"
#include "lib/jxl/cms/tone_mapping.h"

#include <cstdio>
#include <hwy/foreach_target.h>

#include "lib/jxl/base/random.h"
#include "lib/jxl/cms/tone_mapping-inl.h"
#include "lib/jxl/testing.h"

// Test utils
#include <hwy/highway.h>
#include <hwy/tests/hwy_gtest.h>
HWY_BEFORE_NAMESPACE();
namespace jxl {
namespace HWY_NAMESPACE {
namespace {

HWY_NOINLINE void TestRec2408ToneMap() {
  constexpr size_t kNumTrials = 1 << 23;
  Rng rng(1);
  float max_abs_err = 0;
  HWY_FULL(float) d;
  for (size_t i = 0; i < kNumTrials; i++) {
    float src = 11000.0 + rng.UniformF(-150.0f, 150.0f);
    float tgt = 250 + rng.UniformF(-5.0f, 5.0f);
    Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
                       rng.UniformF(0.2f, 0.4f)};
    Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
              rng.UniformF(0.0f, 1.0f)};
    Rec2408ToneMapper<decltype(d)> tone_mapper({0, src}, {0, tgt}, luminances);
    auto r = Set(d, rgb[0]);
    auto g = Set(d, rgb[1]);
    auto b = Set(d, rgb[2]);
    tone_mapper.ToneMap(&r, &g, &b);
    Rec2408ToneMapperBase tone_mapper_base({0, src}, {0, tgt}, luminances);
    tone_mapper_base.ToneMap(rgb);
    const float actual_r = GetLane(r);
    const float expected_r = rgb[0];
    const float abs_err_r = std::abs(expected_r - actual_r);
    EXPECT_LT(abs_err_r, 2.75e-5);
    const float actual_g = GetLane(g);
    const float expected_g = rgb[1];
    const float abs_err_g = std::abs(expected_g - actual_g);
    EXPECT_LT(abs_err_g, 2.75e-5);
    const float actual_b = GetLane(b);
    const float expected_b = rgb[2];
    const float abs_err_b = std::abs(expected_b - actual_b);
    EXPECT_LT(abs_err_b, 2.75e-5);
    max_abs_err = std::max({max_abs_err, abs_err_r, abs_err_g, abs_err_b});
  }
  printf("max abs err %e\n", static_cast<double>(max_abs_err));
}

HWY_NOINLINE void TestHlgOotfApply() {
  constexpr size_t kNumTrials = 1 << 23;
  Rng rng(1);
  float max_abs_err = 0;
  HWY_FULL(float) d;
  for (size_t i = 0; i < kNumTrials; i++) {
    float src = 300.0 + rng.UniformF(-50.0f, 50.0f);
    float tgt = 80 + rng.UniformF(-5.0f, 5.0f);
    Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
                       rng.UniformF(0.2f, 0.4f)};
    Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
              rng.UniformF(0.0f, 1.0f)};
    HlgOOTF ootf(src, tgt, luminances);
    auto r = Set(d, rgb[0]);
    auto g = Set(d, rgb[1]);
    auto b = Set(d, rgb[2]);
    ootf.Apply(&r, &g, &b);
    HlgOOTF_Base ootf_base(src, tgt, luminances);
    ootf_base.Apply(rgb);
    const float actual_r = GetLane(r);
    const float expected_r = rgb[0];
    const float abs_err_r = std::abs(expected_r - actual_r);
    EXPECT_LT(abs_err_r, 7.2e-7);
    const float actual_g = GetLane(g);
    const float expected_g = rgb[1];
    const float abs_err_g = std::abs(expected_g - actual_g);
    EXPECT_LT(abs_err_g, 7.2e-7);
    const float actual_b = GetLane(b);
    const float expected_b = rgb[2];
    const float abs_err_b = std::abs(expected_b - actual_b);
    EXPECT_LT(abs_err_b, 7.2e-7);
    max_abs_err = std::max({max_abs_err, abs_err_r, abs_err_g, abs_err_b});
  }
  printf("max abs err %e\n", static_cast<double>(max_abs_err));
}

HWY_NOINLINE void TestGamutMap() {
  constexpr size_t kNumTrials = 1 << 23;
  Rng rng(1);
  float max_abs_err = 0;
  HWY_FULL(float) d;
  for (size_t i = 0; i < kNumTrials; i++) {
    float preserve_saturation = rng.UniformF(0.2f, 0.4f);
    Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f),
                       rng.UniformF(0.2f, 0.4f)};
    Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f),
              rng.UniformF(0.0f, 1.0f)};
    auto r = Set(d, rgb[0]);
    auto g = Set(d, rgb[1]);
    auto b = Set(d, rgb[2]);
    GamutMap(&r, &g, &b, luminances, preserve_saturation);
    GamutMapScalar(rgb, luminances, preserve_saturation);
    const float actual_r = GetLane(r);
    const float expected_r = rgb[0];
    const float abs_err_r = std::abs(expected_r - actual_r);
    EXPECT_LT(abs_err_r, 1e-10);
    const float actual_g = GetLane(g);
    const float expected_g = rgb[1];
    const float abs_err_g = std::abs(expected_g - actual_g);
    EXPECT_LT(abs_err_g, 1e-10);
    const float actual_b = GetLane(b);
    const float expected_b = rgb[2];
    const float abs_err_b = std::abs(expected_b - actual_b);
    EXPECT_LT(abs_err_b, 1e-10);
    max_abs_err = std::max({max_abs_err, abs_err_r, abs_err_g, abs_err_b});
  }
  printf("max abs err %e\n", static_cast<double>(max_abs_err));
}

}  // namespace
// NOLINTNEXTLINE(google-readability-namespace-comments)
}  // namespace HWY_NAMESPACE
}  // namespace jxl
HWY_AFTER_NAMESPACE();

#if HWY_ONCE
namespace jxl {

class ToneMappingTargetTest : public hwy::TestWithParamTarget {};
HWY_TARGET_INSTANTIATE_TEST_SUITE_P(ToneMappingTargetTest);

HWY_EXPORT_AND_TEST_P(ToneMappingTargetTest, TestRec2408ToneMap);
HWY_EXPORT_AND_TEST_P(ToneMappingTargetTest, TestHlgOotfApply);
HWY_EXPORT_AND_TEST_P(ToneMappingTargetTest, TestGamutMap);

}  // namespace jxl
#endif  // HWY_ONCE