1 #include <cmath>
2
3 #include "colorspace/gamma.h"
4 #include "gtest/gtest.h"
5
6 namespace {
7
test_monotonic(float (* func)(float),float min,float max,unsigned long steps)8 void test_monotonic(float (*func)(float), float min, float max, unsigned long steps)
9 {
10 zimg::colorspace::EnsureSinglePrecision x87;
11
12 float cur = -INFINITY;
13
14 for (unsigned long i = 0; i <= steps; ++i) {
15 float x = min + i * ((max - min) / steps);
16 float y = func(x);
17 ASSERT_FALSE(std::isnan(y)) << " x=" << x << " i=" << i;
18 ASSERT_GE(y, cur) << " x=" << x << " i=" << i;
19 cur = y;
20 }
21 }
22
test_accuracy(float (* f)(float),float (* g)(float),float min,float max,float errthr,float biasthr)23 void test_accuracy(float (*f)(float), float (*g)(float), float min, float max, float errthr, float biasthr)
24 {
25 zimg::colorspace::EnsureSinglePrecision x87;
26
27 const unsigned long STEPS = 1UL << 16;
28 float err = 0.0f;
29 float bias = 0.0f;
30
31 for (unsigned long i = 0; i <= STEPS; ++i) {
32 float x = min + i * ((max - min) / STEPS);
33 float y = f(x);
34 float x_roundtrip = g(y);
35
36 err += std::fabs((x_roundtrip - x) / (x == 0.0f ? FLT_EPSILON : x));
37 bias += x_roundtrip - x;
38 }
39
40 err /= (STEPS + 1);
41 bias /= (STEPS + 1);
42
43 EXPECT_LT(err, errthr);
44 EXPECT_LT(std::fabs(bias), biasthr);
45 }
46
47 } // namespace
48
49
TEST(GammaTest,test_rec709)50 TEST(GammaTest, test_rec709)
51 {
52 EXPECT_EQ(0.0f, zimg::colorspace::rec_709_oetf(0.0f));
53 EXPECT_EQ(1.0f, zimg::colorspace::rec_709_oetf(1.0f));
54 EXPECT_EQ(0.0f, zimg::colorspace::rec_709_inverse_oetf(0.0f));
55 EXPECT_EQ(1.0f, zimg::colorspace::rec_709_inverse_oetf(1.0f));
56
57 SCOPED_TRACE("forward");
58 test_monotonic(zimg::colorspace::rec_709_oetf, 0.0f, 1.0f, 1UL << 16);
59 SCOPED_TRACE("reverse");
60 test_monotonic(zimg::colorspace::rec_709_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
61 SCOPED_TRACE("forward->reverse");
62 test_accuracy(zimg::colorspace::rec_709_oetf, zimg::colorspace::rec_709_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
63 SCOPED_TRACE("reverse->forward");
64 test_accuracy(zimg::colorspace::rec_709_inverse_oetf, zimg::colorspace::rec_709_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
65
66 SCOPED_TRACE("wtw");
67 test_monotonic(zimg::colorspace::rec_709_oetf, 1.0f, 2.0f, 1UL << 16);
68 test_monotonic(zimg::colorspace::rec_709_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
69
70 SCOPED_TRACE("btb");
71 test_monotonic(zimg::colorspace::rec_709_oetf, -1.0f, 0.0f, 1UL << 16);
72 test_monotonic(zimg::colorspace::rec_709_inverse_oetf, -1.0f, 0.0f, 1UL << 16);
73 }
74
TEST(GammaTest,test_log100)75 TEST(GammaTest, test_log100)
76 {
77 EXPECT_EQ(0.0f, zimg::colorspace::log100_oetf(0.01f));
78 EXPECT_GE(zimg::colorspace::log100_oetf(std::nextafter(0.01f, INFINITY)), 0.0f);
79 EXPECT_EQ(1.0f, zimg::colorspace::log100_oetf(1.0f));
80 EXPECT_EQ(0.01f, zimg::colorspace::log100_inverse_oetf(0.0f));
81 EXPECT_GE(zimg::colorspace::log100_inverse_oetf(std::nextafter(0.0f, INFINITY)), 0.01f);
82 EXPECT_EQ(1.0f, zimg::colorspace::log100_inverse_oetf(1.0f));
83
84 SCOPED_TRACE("forward");
85 test_monotonic(zimg::colorspace::log100_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
86 SCOPED_TRACE("reverse");
87 test_monotonic(zimg::colorspace::log100_oetf, 0.0f, 1.0f, 1UL << 16);
88 SCOPED_TRACE("forward->reverse");
89 test_accuracy(zimg::colorspace::log100_oetf, zimg::colorspace::log100_inverse_oetf, 0.01f, 1.0f, 1e-6f, 1e-6f);
90 SCOPED_TRACE("reverse->forward");
91 test_accuracy(zimg::colorspace::log100_inverse_oetf, zimg::colorspace::log100_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
92
93 SCOPED_TRACE("wtw");
94 test_monotonic(zimg::colorspace::log100_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
95 test_monotonic(zimg::colorspace::log100_oetf, 1.0f, 2.0f, 1UL << 16);
96 }
97
TEST(GammaTest,test_log316)98 TEST(GammaTest, test_log316)
99 {
100 EXPECT_EQ(0.0f, zimg::colorspace::log316_oetf(0.00316227766f));
101 EXPECT_GE(zimg::colorspace::log316_oetf(std::nextafter(0.00316227766f, INFINITY)), 0.0f);
102 EXPECT_EQ(1.0f, zimg::colorspace::log316_oetf(1.0f));
103 EXPECT_EQ(0.00316227766f, zimg::colorspace::log316_inverse_oetf(0.0f));
104 EXPECT_GE(zimg::colorspace::log316_inverse_oetf(std::nextafterf(0.0f, INFINITY)), 0.00316227766f);
105 EXPECT_EQ(1.0f, zimg::colorspace::log316_inverse_oetf(1.0f));
106
107 SCOPED_TRACE("forward");
108 test_monotonic(zimg::colorspace::log316_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
109 SCOPED_TRACE("reverse");
110 test_monotonic(zimg::colorspace::log316_oetf, 0.0f, 1.0f, 1UL << 16);
111 SCOPED_TRACE("forward->reverse");
112 test_accuracy(zimg::colorspace::log316_oetf, zimg::colorspace::log316_inverse_oetf, 0.0316227766f, 1.0f, 1e-6f, 1e-6f);
113 SCOPED_TRACE("reverse->forward");
114 test_accuracy(zimg::colorspace::log316_inverse_oetf, zimg::colorspace::log316_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
115
116 SCOPED_TRACE("wtw");
117 test_monotonic(zimg::colorspace::log316_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
118 test_monotonic(zimg::colorspace::log316_oetf, 1.0f, 2.0f, 1UL << 16);
119 }
120
TEST(GammaTest,test_rec_470m_470bg)121 TEST(GammaTest, test_rec_470m_470bg)
122 {
123 SCOPED_TRACE("470m");
124 test_accuracy(zimg::colorspace::rec_470m_oetf, zimg::colorspace::rec_470m_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
125 test_accuracy(zimg::colorspace::rec_470m_inverse_oetf, zimg::colorspace::rec_470m_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
126 SCOPED_TRACE("470bg");
127 test_accuracy(zimg::colorspace::rec_470bg_oetf, zimg::colorspace::rec_470bg_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
128 test_accuracy(zimg::colorspace::rec_470bg_inverse_oetf, zimg::colorspace::rec_470bg_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
129 }
130
TEST(GammaTest,test_smpte_240m)131 TEST(GammaTest, test_smpte_240m)
132 {
133 EXPECT_EQ(0.0f, zimg::colorspace::smpte_240m_oetf(0.0f));
134 EXPECT_EQ(1.0f, zimg::colorspace::smpte_240m_oetf(1.0f));
135 EXPECT_EQ(0.0f, zimg::colorspace::smpte_240m_inverse_oetf(0.0f));
136 EXPECT_EQ(1.0f, zimg::colorspace::smpte_240m_inverse_oetf(1.0f));
137
138 SCOPED_TRACE("forward");
139 test_monotonic(zimg::colorspace::smpte_240m_oetf, 0.0f, 1.0f, 1UL << 16);
140 SCOPED_TRACE("reverse");
141 test_monotonic(zimg::colorspace::smpte_240m_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
142 SCOPED_TRACE("forward->reverse");
143 test_accuracy(zimg::colorspace::smpte_240m_oetf, zimg::colorspace::smpte_240m_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
144 SCOPED_TRACE("reverse->forward");
145 test_accuracy(zimg::colorspace::smpte_240m_inverse_oetf, zimg::colorspace::smpte_240m_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
146
147 SCOPED_TRACE("wtw");
148 test_monotonic(zimg::colorspace::smpte_240m_oetf, 1.0f, 2.0f, 1UL << 16);
149 test_monotonic(zimg::colorspace::smpte_240m_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
150
151 SCOPED_TRACE("btb");
152 test_monotonic(zimg::colorspace::smpte_240m_oetf, -1.0f, 0.0f, 1UL << 16);
153 test_monotonic(zimg::colorspace::smpte_240m_inverse_oetf, -1.0f, 0.0f, 1UL << 16);
154 }
155
TEST(GammaTest,test_rec1886)156 TEST(GammaTest, test_rec1886)
157 {
158 EXPECT_EQ(0.0f, zimg::colorspace::rec_1886_inverse_eotf(0.0f));
159 EXPECT_EQ(1.0f, zimg::colorspace::rec_1886_inverse_eotf(1.0f));
160 EXPECT_EQ(0.0f, zimg::colorspace::rec_1886_eotf(0.0f));
161 EXPECT_EQ(1.0f, zimg::colorspace::rec_1886_eotf(1.0f));
162
163 SCOPED_TRACE("forward");
164 test_monotonic(zimg::colorspace::rec_1886_inverse_eotf, 0.0f, 1.0f, 1UL << 16);
165 SCOPED_TRACE("reverse");
166 test_monotonic(zimg::colorspace::rec_1886_eotf, 0.0f, 1.0f, 1UL << 16);
167 SCOPED_TRACE("forward->reverse");
168 test_accuracy(zimg::colorspace::rec_1886_inverse_eotf, zimg::colorspace::rec_1886_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
169 SCOPED_TRACE("reverse->forward");
170 test_accuracy(zimg::colorspace::rec_1886_eotf, zimg::colorspace::rec_1886_inverse_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
171
172 SCOPED_TRACE("wtw");
173 test_monotonic(zimg::colorspace::rec_1886_inverse_eotf, 1.0f, 2.0f, 1UL << 16);
174 test_monotonic(zimg::colorspace::rec_1886_eotf, 1.0f, 2.0f, 1UL << 16);
175
176 SCOPED_TRACE("btb");
177 test_monotonic(zimg::colorspace::rec_1886_inverse_eotf, -1.0f, 0.0f, 1UL << 16);
178 test_monotonic(zimg::colorspace::rec_1886_eotf, -1.0f, 0.0f, 1UL << 16);
179 }
180
TEST(GammaTest,test_srgb)181 TEST(GammaTest, test_srgb)
182 {
183 EXPECT_EQ(0.0f, zimg::colorspace::srgb_inverse_eotf(0.0f));
184 EXPECT_EQ(1.0f, zimg::colorspace::srgb_inverse_eotf(1.0f));
185 EXPECT_EQ(0.0f, zimg::colorspace::srgb_eotf(0.0f));
186 EXPECT_EQ(1.0f, zimg::colorspace::srgb_eotf(1.0f));
187
188 SCOPED_TRACE("forward");
189 test_monotonic(zimg::colorspace::srgb_inverse_eotf, 0.0f, 1.0f, 1UL << 16);
190 SCOPED_TRACE("reverse");
191 test_monotonic(zimg::colorspace::srgb_eotf, 0.0f, 1.0f, 1UL << 16);
192 SCOPED_TRACE("forward->reverse");
193 test_accuracy(zimg::colorspace::srgb_inverse_eotf, zimg::colorspace::srgb_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
194 SCOPED_TRACE("reverse->forward");
195 test_accuracy(zimg::colorspace::srgb_eotf, zimg::colorspace::srgb_inverse_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
196
197 SCOPED_TRACE("wtw");
198 test_monotonic(zimg::colorspace::srgb_inverse_eotf, 1.0f, 2.0f, 1UL << 16);
199 test_monotonic(zimg::colorspace::srgb_eotf, 1.0f, 2.0f, 1UL << 16);
200
201 SCOPED_TRACE("btb");
202 test_monotonic(zimg::colorspace::srgb_inverse_eotf, -1.0f, 0.0f, 1UL << 16);
203 test_monotonic(zimg::colorspace::srgb_eotf, -1.0f, 0.0f, 1UL << 16);
204 }
205
TEST(GammaTest,test_xvycc)206 TEST(GammaTest, test_xvycc)
207 {
208 SCOPED_TRACE("oetf forward");
209 test_accuracy(zimg::colorspace::xvycc_oetf, zimg::colorspace::rec_709_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
210 SCOPED_TRACE("oetf reverse");
211 test_accuracy(zimg::colorspace::xvycc_inverse_oetf, zimg::colorspace::rec_709_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
212 SCOPED_TRACE("eotf forward");
213 test_accuracy(zimg::colorspace::xvycc_oetf, zimg::colorspace::rec_709_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
214 SCOPED_TRACE("eotf reverse");
215 test_accuracy(zimg::colorspace::xvycc_inverse_oetf, zimg::colorspace::rec_709_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
216
217 SCOPED_TRACE("oetf wtw");
218 test_monotonic(zimg::colorspace::xvycc_oetf, 1.0f, 2.0f, 1UL << 16);
219 test_monotonic(zimg::colorspace::xvycc_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
220 SCOPED_TRACE("eotf wtw");
221 test_monotonic(zimg::colorspace::xvycc_inverse_eotf, 1.0f, 2.0f, 1UL << 16);
222 test_monotonic(zimg::colorspace::xvycc_eotf, 1.0f, 2.0f, 1UL << 16);
223 SCOPED_TRACE("oetf btb");
224 test_monotonic(zimg::colorspace::xvycc_oetf, -1.0f, 0.0f, 1UL << 16);
225 test_monotonic(zimg::colorspace::xvycc_inverse_oetf, -1.0f, 0.0f, 1UL << 16);
226 SCOPED_TRACE("eotf btb");
227 test_monotonic(zimg::colorspace::xvycc_inverse_eotf, -1.0f, 0.0f, 1UL << 16);
228 test_monotonic(zimg::colorspace::xvycc_eotf, -1.0f, 1.0f, 1UL << 16);
229 }
230
TEST(GammaTest,test_st_2084)231 TEST(GammaTest, test_st_2084)
232 {
233 EXPECT_EQ(0.0f, zimg::colorspace::st_2084_inverse_eotf(0.0f));
234 EXPECT_EQ(1.0f, zimg::colorspace::st_2084_inverse_eotf(1.0f));
235 EXPECT_EQ(0.0f, zimg::colorspace::st_2084_eotf(0.0f));
236 EXPECT_EQ(1.0f, zimg::colorspace::st_2084_eotf(1.0f));
237
238 SCOPED_TRACE("forward");
239 test_monotonic(zimg::colorspace::st_2084_inverse_eotf, 0.0f, 1.0f, 1UL << 16);
240 SCOPED_TRACE("reverse");
241 test_monotonic(zimg::colorspace::st_2084_eotf, 0.0f, 1.0f, 1UL << 16);
242 SCOPED_TRACE("forward->reverse");
243 test_accuracy(zimg::colorspace::st_2084_inverse_eotf, zimg::colorspace::st_2084_eotf, 0.0f, 1.0f, 1e-4f, 1e-6f);
244 SCOPED_TRACE("reverse->forward");
245 test_accuracy(zimg::colorspace::st_2084_eotf, zimg::colorspace::st_2084_inverse_eotf, 0.0f, 1.0f, 1e-4f, 1e-6f);
246
247 SCOPED_TRACE("wtw");
248 test_monotonic(zimg::colorspace::st_2084_inverse_eotf, 1.0f, 2.0f, 1UL << 16);
249 test_monotonic(zimg::colorspace::st_2084_eotf, 1.0f, 2.0f, 1UL << 16);
250 }
251
TEST(GammaTest,test_st_2084_oetf)252 TEST(GammaTest, test_st_2084_oetf)
253 {
254 EXPECT_EQ(0.0f, zimg::colorspace::st_2084_oetf(0.0f));
255 EXPECT_NEAR(1.0f, zimg::colorspace::st_2084_oetf(1.0f), 1e-6f);
256 EXPECT_EQ(0.0f, zimg::colorspace::st_2084_inverse_oetf(0.0f));
257 EXPECT_NEAR(1.0f, zimg::colorspace::st_2084_inverse_oetf(1.0f), 1e-6f);
258
259 SCOPED_TRACE("forward");
260 test_monotonic(zimg::colorspace::st_2084_oetf, 0.0f, 1.0f, 1UL << 16);
261 SCOPED_TRACE("reverse");
262 test_monotonic(zimg::colorspace::st_2084_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
263 SCOPED_TRACE("forward->reverse");
264 test_accuracy(zimg::colorspace::st_2084_oetf, zimg::colorspace::st_2084_inverse_oetf, 0.0f, 1.0f, 1e-4f, 1e-6f);
265 SCOPED_TRACE("reverse->forward");
266 test_accuracy(zimg::colorspace::st_2084_inverse_oetf, zimg::colorspace::st_2084_oetf, 0.0f, 1.0f, 1e-4f, 1e-6f);
267
268 SCOPED_TRACE("wtw");
269 test_monotonic(zimg::colorspace::st_2084_oetf, 1.0f, 2.0f, 1UL << 16);
270 test_monotonic(zimg::colorspace::st_2084_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
271 }
272
TEST(GammaTest,test_arib_b67)273 TEST(GammaTest, test_arib_b67)
274 {
275 EXPECT_EQ(0.0f, zimg::colorspace::arib_b67_oetf(0.0f));
276 EXPECT_EQ(0.5f, zimg::colorspace::arib_b67_oetf(1.0f / 12.0f));
277 EXPECT_EQ(1.0f, zimg::colorspace::arib_b67_oetf(1.0f));
278 EXPECT_EQ(0.0f, zimg::colorspace::arib_b67_inverse_oetf(0.0f));
279 EXPECT_EQ(1.0f / 12.0f, zimg::colorspace::arib_b67_inverse_oetf(0.5f));
280 EXPECT_NEAR(1.0f, zimg::colorspace::arib_b67_inverse_oetf(1.0f), 1e-6f);
281
282 SCOPED_TRACE("forward");
283 test_monotonic(zimg::colorspace::arib_b67_oetf, 0.0f, 1.0f, 1UL << 16);
284 SCOPED_TRACE("reverse");
285 test_monotonic(zimg::colorspace::arib_b67_inverse_oetf, 0.0f, 1.0f, 1UL << 16);
286 SCOPED_TRACE("forward->reverse");
287 test_accuracy(zimg::colorspace::arib_b67_oetf, zimg::colorspace::arib_b67_inverse_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
288 SCOPED_TRACE("reverse->forward");
289 test_accuracy(zimg::colorspace::arib_b67_inverse_oetf, zimg::colorspace::arib_b67_oetf, 0.0f, 1.0f, 1e-6f, 1e-6f);
290
291 SCOPED_TRACE("wtw");
292 test_monotonic(zimg::colorspace::arib_b67_oetf, 1.0f, 2.0f, 1UL << 16);
293 test_monotonic(zimg::colorspace::arib_b67_inverse_oetf, 1.0f, 2.0f, 1UL << 16);
294 }
295
TEST(GammaTest,test_arib_b67_eotf)296 TEST(GammaTest, test_arib_b67_eotf)
297 {
298 EXPECT_EQ(0.0f, zimg::colorspace::arib_b67_inverse_eotf(0.0f));
299 EXPECT_EQ(1.0f, zimg::colorspace::arib_b67_inverse_eotf(1.0f));
300 EXPECT_EQ(0.0f, zimg::colorspace::arib_b67_eotf(0.0f));
301 EXPECT_NEAR(1.0f, zimg::colorspace::arib_b67_eotf(1.0f), 1e-6f);
302
303 SCOPED_TRACE("forward");
304 test_monotonic(zimg::colorspace::arib_b67_inverse_eotf, 0.0f, 1.0f, 1UL << 16);
305 SCOPED_TRACE("reverse");
306 test_monotonic(zimg::colorspace::arib_b67_eotf, 0.0f, 1.0f, 1UL << 16);
307 SCOPED_TRACE("forward->reverse");
308 test_accuracy(zimg::colorspace::arib_b67_inverse_eotf, zimg::colorspace::arib_b67_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
309 SCOPED_TRACE("reverse->forward");
310 test_accuracy(zimg::colorspace::arib_b67_eotf, zimg::colorspace::arib_b67_inverse_eotf, 0.0f, 1.0f, 1e-6f, 1e-6f);
311
312 SCOPED_TRACE("wtw");
313 test_monotonic(zimg::colorspace::arib_b67_inverse_eotf, 1.0f, 2.0f, 1UL << 16);
314 test_monotonic(zimg::colorspace::arib_b67_eotf, 1.0f, 2.0f, 1UL << 16);
315 }
316