Coverage for models/rgb/tests/test_ycbcr.py: 100%
178 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-16 22:49 +1300
1"""Define the unit tests for the :mod:`colour.models.rgb.ycbcr` module."""
3from __future__ import annotations
5from itertools import product
7import numpy as np
9from colour.constants import TOLERANCE_ABSOLUTE_TESTS
10from colour.models.rgb.ycbcr import (
11 WEIGHTS_YCBCR,
12 RGB_to_YCbCr,
13 RGB_to_YcCbcCrc,
14 YCbCr_to_RGB,
15 YcCbcCrc_to_RGB,
16 matrix_YCbCr,
17 offset_YCbCr,
18 ranges_YCbCr,
19 round_BT2100,
20)
21from colour.utilities import domain_range_scale, ignore_numpy_errors
23__author__ = "Colour Developers"
24__copyright__ = "Copyright 2013 Colour Developers"
25__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause"
26__maintainer__ = "Colour Developers"
27__email__ = "colour-developers@colour-science.org"
28__status__ = "Development"
30__all__ = [
31 "TestRoundBT2100",
32 "TestRangeYCbCr",
33 "TestMatrixYCbCr",
34 "TestOffsetYCbCr",
35 "TestRGB_to_YCbCr",
36 "TestYCbCr_to_RGB",
37 "TestRGB_to_YcCbcCrc",
38 "TestYcCbcCrc_to_RGB",
39]
42class TestRoundBT2100:
43 """
44 Define :func:`colour.models.rgb.ycbcr.round_BT2100` definition unit tests
45 methods.
46 """
48 def test_round_BT2100(self) -> None:
49 """Test :func:`colour.models.rgb.ycbcr.round_BT2100` definition."""
51 np.testing.assert_array_equal(
52 round_BT2100([-0.6, -0.5, -0.4, 0.4, 0.5, 0.6]),
53 np.array([-1.0, -1.0, -0.0, 0.0, 1.0, 1.0]),
54 )
57class TestRangeYCbCr:
58 """
59 Define :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition unit tests
60 methods.
61 """
63 def test_ranges_YCbCr(self) -> None:
64 """Test :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition."""
66 np.testing.assert_allclose(
67 ranges_YCbCr(8, True, True),
68 np.array([16.00000000, 235.00000000, 16.00000000, 240.00000000]),
69 atol=TOLERANCE_ABSOLUTE_TESTS,
70 )
72 np.testing.assert_allclose(
73 ranges_YCbCr(8, True, False),
74 np.array([0.06274510, 0.92156863, 0.06274510, 0.94117647]),
75 atol=TOLERANCE_ABSOLUTE_TESTS,
76 )
78 np.testing.assert_allclose(
79 ranges_YCbCr(8, False, True),
80 np.array([0.00000000, 255.00000000, 0.50000000, 255.50000000]),
81 atol=TOLERANCE_ABSOLUTE_TESTS,
82 )
84 np.testing.assert_allclose(
85 ranges_YCbCr(8, False, False),
86 np.array([0.00000000, 1.00000000, -0.50000000, 0.50000000]),
87 atol=TOLERANCE_ABSOLUTE_TESTS,
88 )
90 np.testing.assert_allclose(
91 ranges_YCbCr(10, True, True),
92 np.array([64.00000000, 940.00000000, 64.00000000, 960.00000000]),
93 atol=TOLERANCE_ABSOLUTE_TESTS,
94 )
96 np.testing.assert_allclose(
97 ranges_YCbCr(10, True, False),
98 np.array([0.06256109, 0.91886608, 0.06256109, 0.93841642]),
99 atol=TOLERANCE_ABSOLUTE_TESTS,
100 )
102 np.testing.assert_allclose(
103 ranges_YCbCr(10, False, True),
104 np.array([0.00000000, 1023.00000000, 0.50000000, 1023.50000000]),
105 atol=TOLERANCE_ABSOLUTE_TESTS,
106 )
108 np.testing.assert_allclose(
109 ranges_YCbCr(10, False, False),
110 np.array([0.00000000, 1.00000000, -0.50000000, 0.50000000]),
111 atol=TOLERANCE_ABSOLUTE_TESTS,
112 )
115class TestMatrixYCbCr:
116 """
117 Define :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition unit tests
118 methods.
119 """
121 def test_matrix_YCbCr(self) -> None:
122 """Test :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition."""
124 np.testing.assert_allclose(
125 matrix_YCbCr(),
126 np.array(
127 [
128 [1.00000000, 0.00000000, 1.57480000],
129 [1.00000000, -0.18732427, -0.46812427],
130 [1.00000000, 1.85560000, 0.00000000],
131 ]
132 ),
133 atol=TOLERANCE_ABSOLUTE_TESTS,
134 )
136 np.testing.assert_allclose(
137 matrix_YCbCr(K=WEIGHTS_YCBCR["ITU-R BT.601"]),
138 np.array(
139 [
140 [1.00000000, 0.00000000, 1.40200000],
141 [1.00000000, -0.34413629, -0.71413629],
142 [1.00000000, 1.77200000, -0.00000000],
143 ]
144 ),
145 atol=TOLERANCE_ABSOLUTE_TESTS,
146 )
148 np.testing.assert_allclose(
149 matrix_YCbCr(is_legal=True),
150 np.array(
151 [
152 [1.16438356, 0.00000000, 1.79274107],
153 [1.16438356, -0.21324861, -0.53290933],
154 [1.16438356, 2.11240179, -0.00000000],
155 ]
156 ),
157 atol=TOLERANCE_ABSOLUTE_TESTS,
158 )
160 np.testing.assert_allclose(
161 matrix_YCbCr(bits=10),
162 np.array(
163 [
164 [1.00000000, 0.00000000, 1.57480000],
165 [1.00000000, -0.18732427, -0.46812427],
166 [1.00000000, 1.85560000, 0.00000000],
167 ]
168 ),
169 atol=TOLERANCE_ABSOLUTE_TESTS,
170 )
172 np.testing.assert_allclose(
173 matrix_YCbCr(bits=10, is_int=True),
174 np.array(
175 [
176 [0.00097752, 0.00000000, 0.00153939],
177 [0.00097752, -0.00018311, -0.00045760],
178 [0.00097752, 0.00181388, 0.00000000],
179 ]
180 ),
181 atol=TOLERANCE_ABSOLUTE_TESTS,
182 )
185class TestOffsetYCbCr:
186 """
187 Define :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition unit tests
188 methods.
189 """
191 def test_offset_YCbCr(self) -> None:
192 """Test :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition."""
194 np.testing.assert_allclose(
195 offset_YCbCr(),
196 np.array([0.00000000, 0.00000000, 0.00000000]),
197 atol=TOLERANCE_ABSOLUTE_TESTS,
198 )
200 np.testing.assert_allclose(
201 offset_YCbCr(is_legal=True),
202 np.array([0.06274510, 0.50196078, 0.50196078]),
203 atol=TOLERANCE_ABSOLUTE_TESTS,
204 )
206 np.testing.assert_allclose(
207 offset_YCbCr(bits=10),
208 np.array([0.00000000, 0.00000000, 0.00000000]),
209 atol=TOLERANCE_ABSOLUTE_TESTS,
210 )
212 np.testing.assert_allclose(
213 offset_YCbCr(bits=10, is_int=True),
214 np.array([0.00000000, 512.00000000, 512.00000000]),
215 atol=TOLERANCE_ABSOLUTE_TESTS,
216 )
219class TestRGB_to_YCbCr:
220 """
221 Define :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition unit tests
222 methods.
223 """
225 def test_RGB_to_YCbCr(self) -> None:
226 """Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition."""
228 np.testing.assert_allclose(
229 RGB_to_YCbCr(np.array([0.75, 0.75, 0.0])),
230 np.array([0.66035745, 0.17254902, 0.53216593]),
231 atol=TOLERANCE_ABSOLUTE_TESTS,
232 )
234 np.testing.assert_allclose(
235 RGB_to_YCbCr(
236 np.array([0.25, 0.5, 0.75]),
237 K=WEIGHTS_YCBCR["ITU-R BT.601"],
238 out_int=True,
239 out_legal=True,
240 out_bits=10,
241 ),
242 np.array([461, 662, 382]),
243 atol=TOLERANCE_ABSOLUTE_TESTS,
244 )
246 np.testing.assert_allclose(
247 RGB_to_YCbCr(
248 np.array([0.0, 0.75, 0.75]),
249 K=WEIGHTS_YCBCR["ITU-R BT.2020"],
250 out_int=False,
251 out_legal=False,
252 ),
253 np.array([0.55297500, 0.10472255, -0.37500000]),
254 atol=TOLERANCE_ABSOLUTE_TESTS,
255 )
257 np.testing.assert_allclose(
258 RGB_to_YCbCr(
259 np.array([0.75, 0.0, 0.75]),
260 K=WEIGHTS_YCBCR["ITU-R BT.709"],
261 out_range=(16 / 255, 235 / 255, 15.5 / 255, 239.5 / 255),
262 ),
263 np.array([0.24618980, 0.75392897, 0.79920662]),
264 atol=TOLERANCE_ABSOLUTE_TESTS,
265 )
267 def test_n_dimensional_RGB_to_YCbCr(self) -> None:
268 """
269 Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition
270 n-dimensional arrays support.
271 """
273 RGB = np.array([0.75, 0.5, 0.25])
274 YCbCr = RGB_to_YCbCr(RGB)
276 RGB = np.tile(RGB, 4)
277 RGB = np.reshape(RGB, (4, 3))
278 YCbCr = np.tile(YCbCr, 4)
279 YCbCr = np.reshape(YCbCr, (4, 3))
280 np.testing.assert_allclose(RGB_to_YCbCr(RGB), YCbCr)
282 RGB = np.tile(RGB, 4)
283 RGB = np.reshape(RGB, (4, 4, 3))
284 YCbCr = np.tile(YCbCr, 4)
285 YCbCr = np.reshape(YCbCr, (4, 4, 3))
286 np.testing.assert_allclose(RGB_to_YCbCr(RGB), YCbCr)
288 RGB = np.tile(RGB, 4)
289 RGB = np.reshape(RGB, (4, 4, 4, 3))
290 YCbCr = np.tile(YCbCr, 4)
291 YCbCr = np.reshape(YCbCr, (4, 4, 4, 3))
292 np.testing.assert_allclose(RGB_to_YCbCr(RGB), YCbCr)
294 def test_domain_range_scale_RGB_to_YCbCr(self) -> None:
295 """
296 Test :func:`colour.models.rgb.prismatic.RGB_to_YCbCr` definition
297 domain and range scale support.
298 """
300 RGB = np.array([0.75, 0.5, 0.25])
301 YCbCr = RGB_to_YCbCr(RGB)
303 d_r = (("reference", 1), ("1", 1), ("100", 100))
304 for scale, factor in d_r:
305 with domain_range_scale(scale):
306 np.testing.assert_allclose(
307 RGB_to_YCbCr(RGB * factor),
308 YCbCr * factor,
309 atol=TOLERANCE_ABSOLUTE_TESTS,
310 )
312 @ignore_numpy_errors
313 def test_nan_RGB_to_YCbCr(self) -> None:
314 """
315 Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition nan
316 support.
317 """
319 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
320 cases = np.array(list(set(product(cases, repeat=3))))
321 RGB_to_YCbCr(cases)
324class TestYCbCr_to_RGB:
325 """
326 Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests
327 methods.
328 """
330 def test_YCbCr_to_RGB(self) -> None:
331 """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition."""
333 np.testing.assert_allclose(
334 YCbCr_to_RGB(np.array([0.66035745, 0.17254902, 0.53216593])),
335 np.array([0.75, 0.75, 0.0]),
336 atol=TOLERANCE_ABSOLUTE_TESTS,
337 )
339 np.testing.assert_allclose(
340 YCbCr_to_RGB(
341 np.array([471, 650, 390]),
342 in_bits=10,
343 in_legal=True,
344 in_int=True,
345 ),
346 np.array([0.25018598, 0.49950072, 0.75040741]),
347 atol=TOLERANCE_ABSOLUTE_TESTS,
348 )
350 np.testing.assert_allclose(
351 YCbCr_to_RGB(
352 np.array([150, 99, 175]),
353 in_bits=8,
354 in_legal=False,
355 in_int=True,
356 out_bits=8,
357 out_legal=True,
358 out_int=True,
359 ),
360 np.array([208, 131, 99]),
361 atol=TOLERANCE_ABSOLUTE_TESTS,
362 )
364 def test_n_dimensional_YCbCr_to_RGB(self) -> None:
365 """
366 Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition
367 n-dimensional arrays support.
368 """
370 YCbCr = np.array([0.52230157, 0.36699593, 0.62183309])
371 RGB = YCbCr_to_RGB(YCbCr)
373 RGB = np.tile(RGB, 4)
374 RGB = np.reshape(RGB, (4, 3))
375 YCbCr = np.tile(YCbCr, 4)
376 YCbCr = np.reshape(YCbCr, (4, 3))
377 np.testing.assert_allclose(YCbCr_to_RGB(YCbCr), RGB)
379 RGB = np.tile(RGB, 4)
380 RGB = np.reshape(RGB, (4, 4, 3))
381 YCbCr = np.tile(YCbCr, 4)
382 YCbCr = np.reshape(YCbCr, (4, 4, 3))
383 np.testing.assert_allclose(YCbCr_to_RGB(YCbCr), RGB)
385 RGB = np.tile(RGB, 4)
386 RGB = np.reshape(RGB, (4, 4, 4, 3))
387 YCbCr = np.tile(YCbCr, 4)
388 YCbCr = np.reshape(YCbCr, (4, 4, 4, 3))
389 np.testing.assert_allclose(YCbCr_to_RGB(YCbCr), RGB)
391 def test_domain_range_scale_YCbCr_to_RGB(self) -> None:
392 """
393 Test :func:`colour.models.rgb.prismatic.YCbCr_to_RGB` definition
394 domain and range scale support.
395 """
397 YCbCr = np.array([0.52230157, 0.36699593, 0.62183309])
398 RGB = YCbCr_to_RGB(YCbCr)
400 d_r = (("reference", 1), ("1", 1), ("100", 100))
401 for scale, factor in d_r:
402 with domain_range_scale(scale):
403 np.testing.assert_allclose(
404 YCbCr_to_RGB(YCbCr * factor),
405 RGB * factor,
406 atol=TOLERANCE_ABSOLUTE_TESTS,
407 )
409 @ignore_numpy_errors
410 def test_nan_YCbCr_to_RGB(self) -> None:
411 """
412 Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition nan
413 support.
414 """
416 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
417 cases = np.array(list(set(product(cases, repeat=3))))
418 YCbCr_to_RGB(cases)
421class TestRGB_to_YcCbcCrc:
422 """
423 Define :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition unit
424 tests methods.
425 """
427 def test_RGB_to_YcCbcCrc(self) -> None:
428 """Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition."""
430 np.testing.assert_allclose(
431 RGB_to_YcCbcCrc(np.array([0.45620519, 0.03081071, 0.04091952])),
432 np.array([0.37020379, 0.41137200, 0.77704674]),
433 atol=TOLERANCE_ABSOLUTE_TESTS,
434 )
436 np.testing.assert_allclose(
437 RGB_to_YcCbcCrc(
438 np.array([0.18, 0.18, 0.18]),
439 out_bits=10,
440 out_legal=True,
441 out_int=True,
442 is_12_bits_system=False,
443 ),
444 np.array([422, 512, 512]),
445 atol=TOLERANCE_ABSOLUTE_TESTS,
446 )
448 def test_n_dimensional_RGB_to_YcCbcCrc(self) -> None:
449 """
450 Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition
451 n-dimensional arrays support.
452 """
454 RGB = np.array([0.45620519, 0.03081071, 0.04091952])
455 YcCbcCrc = RGB_to_YcCbcCrc(RGB)
457 RGB = np.tile(RGB, 4)
458 RGB = np.reshape(RGB, (4, 3))
459 YcCbcCrc = np.tile(YcCbcCrc, 4)
460 YcCbcCrc = np.reshape(YcCbcCrc, (4, 3))
461 np.testing.assert_allclose(
462 RGB_to_YcCbcCrc(RGB), YcCbcCrc, atol=TOLERANCE_ABSOLUTE_TESTS
463 )
465 RGB = np.tile(RGB, 4)
466 RGB = np.reshape(RGB, (4, 4, 3))
467 YcCbcCrc = np.tile(YcCbcCrc, 4)
468 YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 3))
469 np.testing.assert_allclose(
470 RGB_to_YcCbcCrc(RGB), YcCbcCrc, atol=TOLERANCE_ABSOLUTE_TESTS
471 )
473 RGB = np.tile(RGB, 4)
474 RGB = np.reshape(RGB, (4, 4, 4, 3))
475 YcCbcCrc = np.tile(YcCbcCrc, 4)
476 YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 4, 3))
477 np.testing.assert_allclose(
478 RGB_to_YcCbcCrc(RGB), YcCbcCrc, atol=TOLERANCE_ABSOLUTE_TESTS
479 )
481 def test_domain_range_scale_RGB_to_YcCbcCrc(self) -> None:
482 """
483 Test :func:`colour.models.rgb.prismatic.RGB_to_YcCbcCrc` definition
484 domain and range scale support.
485 """
487 RGB = np.array([0.45620519, 0.03081071, 0.04091952])
488 YcCbcCrc = RGB_to_YcCbcCrc(RGB)
490 d_r = (("reference", 1), ("1", 1), ("100", 100))
491 for scale, factor in d_r:
492 with domain_range_scale(scale):
493 np.testing.assert_allclose(
494 RGB_to_YcCbcCrc(RGB * factor),
495 YcCbcCrc * factor,
496 atol=TOLERANCE_ABSOLUTE_TESTS,
497 )
499 @ignore_numpy_errors
500 def test_nan_RGB_to_YcCbcCrc(self) -> None:
501 """
502 Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition nan
503 support.
504 """
506 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
507 cases = np.array(list(set(product(cases, repeat=3))))
508 RGB_to_YcCbcCrc(cases)
511class TestYcCbcCrc_to_RGB:
512 """
513 Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests
514 methods.
515 """
517 def test_YcCbcCrc_to_RGB(self) -> None:
518 """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition."""
520 np.testing.assert_allclose(
521 YcCbcCrc_to_RGB(np.array([0.37020379, 0.41137200, 0.77704674])),
522 np.array([0.45620519, 0.03081071, 0.04091952]),
523 atol=TOLERANCE_ABSOLUTE_TESTS,
524 )
526 np.testing.assert_allclose(
527 YcCbcCrc_to_RGB(
528 np.array([1689, 2048, 2048]),
529 in_bits=12,
530 in_legal=True,
531 in_int=True,
532 is_12_bits_system=True,
533 ),
534 np.array([0.18009037, 0.18009037, 0.18009037]),
535 atol=TOLERANCE_ABSOLUTE_TESTS,
536 )
538 def test_n_dimensional_YcCbcCrc_to_RGB(self) -> None:
539 """
540 Test :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition
541 n-dimensional arrays support.
542 """
544 YcCbcCrc = np.array([0.37020379, 0.41137200, 0.77704674])
545 RGB = YcCbcCrc_to_RGB(YcCbcCrc)
547 RGB = np.tile(RGB, 4)
548 RGB = np.reshape(RGB, (4, 3))
549 YcCbcCrc = np.tile(YcCbcCrc, 4)
550 YcCbcCrc = np.reshape(YcCbcCrc, (4, 3))
551 np.testing.assert_allclose(
552 YcCbcCrc_to_RGB(YcCbcCrc), RGB, atol=TOLERANCE_ABSOLUTE_TESTS
553 )
555 RGB = np.tile(RGB, 4)
556 RGB = np.reshape(RGB, (4, 4, 3))
557 YcCbcCrc = np.tile(YcCbcCrc, 4)
558 YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 3))
559 np.testing.assert_allclose(
560 YcCbcCrc_to_RGB(YcCbcCrc), RGB, atol=TOLERANCE_ABSOLUTE_TESTS
561 )
563 RGB = np.tile(RGB, 4)
564 RGB = np.reshape(RGB, (4, 4, 4, 3))
565 YcCbcCrc = np.tile(YcCbcCrc, 4)
566 YcCbcCrc = np.reshape(YcCbcCrc, (4, 4, 4, 3))
567 np.testing.assert_allclose(
568 YcCbcCrc_to_RGB(YcCbcCrc), RGB, atol=TOLERANCE_ABSOLUTE_TESTS
569 )
571 def test_domain_range_scale_YcCbcCrc_to_RGB(self) -> None:
572 """
573 Test :func:`colour.models.rgb.prismatic.YcCbcCrc_to_RGB` definition
574 domain and range scale support.
575 """
577 YcCbcCrc = np.array([0.69943807, 0.38814348, 0.61264549])
578 RGB = YcCbcCrc_to_RGB(YcCbcCrc)
580 d_r = (("reference", 1), ("1", 1), ("100", 100))
581 for scale, factor in d_r:
582 with domain_range_scale(scale):
583 np.testing.assert_allclose(
584 YcCbcCrc_to_RGB(YcCbcCrc * factor),
585 RGB * factor,
586 atol=TOLERANCE_ABSOLUTE_TESTS,
587 )
589 @ignore_numpy_errors
590 def test_nan_YcCbcCrc_to_RGB(self) -> None:
591 """
592 Test :func:`colour.models.rgb.ycbcr.YcCbcCrc_to_RGB` definition nan
593 support.
594 """
596 cases = [-1.0, 0.0, 1.0, -np.inf, np.inf, np.nan]
597 cases = np.array(list(set(product(cases, repeat=3))))
598 YcCbcCrc_to_RGB(cases)