Coverage for models/rgb/tests/test_ycbcr.py: 100%

1"""Define the unit tests for the :mod:`colour.models.rgb.ycbcr` module."""

2

3 from __future__ import annotations

4

5 from itertools import product

6

7 import numpy as np

8

9 from colour.constants import TOLERANCE_ABSOLUTE_TESTS

10 from 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)

21 from colour.utilities import domain_range_scale, ignore_numpy_errors

22

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"

29

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]

40

41

42 class TestRoundBT2100:

43 """

44 Define :func:`colour.models.rgb.ycbcr.round_BT2100` definition unit tests

45 methods.

46 """

47

48 def test_round_BT2100(self) -> None:

49 """Test :func:`colour.models.rgb.ycbcr.round_BT2100` definition."""

50

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 )

55

56

57 class TestRangeYCbCr:

58 """

59 Define :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition unit tests

60 methods.

61 """

62

63 def test_ranges_YCbCr(self) -> None:

64 """Test :func:`colour.models.rgb.ycbcr.ranges_YCbCr` definition."""

65

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 )

71

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 )

77

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 )

83

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 )

89

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 )

95

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 )

101

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 )

107

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 )

113

114

115 class TestMatrixYCbCr:

116 """

117 Define :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition unit tests

118 methods.

119 """

120

121 def test_matrix_YCbCr(self) -> None:

122 """Test :func:`colour.models.rgb.ycbcr.matrix_YCbCr` definition."""

123

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 )

135

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 )

147

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 )

159

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 )

171

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 )

183

184

185 class TestOffsetYCbCr:

186 """

187 Define :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition unit tests

188 methods.

189 """

190

191 def test_offset_YCbCr(self) -> None:

192 """Test :func:`colour.models.rgb.ycbcr.offset_YCbCr` definition."""

193

194 np.testing.assert_allclose(

195 offset_YCbCr(),

196 np.array([0.00000000, 0.00000000, 0.00000000]),

197 atol=TOLERANCE_ABSOLUTE_TESTS,

198 )

199

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 )

205

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 )

211

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 )

217

218

219 class TestRGB_to_YCbCr:

220 """

221 Define :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition unit tests

222 methods.

223 """

224

225 def test_RGB_to_YCbCr(self) -> None:

226 """Test :func:`colour.models.rgb.ycbcr.RGB_to_YCbCr` definition."""

227

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 )

233

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 )

245

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 )

256

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 )

266

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 """

272

273 RGB = np.array([0.75, 0.5, 0.25])

274 YCbCr = RGB_to_YCbCr(RGB)

275

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)

281

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)

287

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)

293

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 """

299

300 RGB = np.array([0.75, 0.5, 0.25])

301 YCbCr = RGB_to_YCbCr(RGB)

302

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 )

311

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 """

318

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)

322

323

324 class TestYCbCr_to_RGB:

325 """

326 Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests

327 methods.

328 """

329

330 def test_YCbCr_to_RGB(self) -> None:

331 """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition."""

332

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 )

338

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 )

349

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 )

363

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 """

369

370 YCbCr = np.array([0.52230157, 0.36699593, 0.62183309])

371 RGB = YCbCr_to_RGB(YCbCr)

372

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)

378

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)

384

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)

390

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 """

396

397 YCbCr = np.array([0.52230157, 0.36699593, 0.62183309])

398 RGB = YCbCr_to_RGB(YCbCr)

399

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 )

408

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 """

415

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)

419

420

421 class TestRGB_to_YcCbcCrc:

422 """

423 Define :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition unit

424 tests methods.

425 """

426

427 def test_RGB_to_YcCbcCrc(self) -> None:

428 """Test :func:`colour.models.rgb.ycbcr.RGB_to_YcCbcCrc` definition."""

429

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 )

435

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 )

447

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 """

453

454 RGB = np.array([0.45620519, 0.03081071, 0.04091952])

455 YcCbcCrc = RGB_to_YcCbcCrc(RGB)

456

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 )

464

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 )

472

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 )

480

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 """

486

487 RGB = np.array([0.45620519, 0.03081071, 0.04091952])

488 YcCbcCrc = RGB_to_YcCbcCrc(RGB)

489

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 )

498

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 """

505

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)

509

510

511 class TestYcCbcCrc_to_RGB:

512 """

513 Define :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition unit tests

514 methods.

515 """

516

517 def test_YcCbcCrc_to_RGB(self) -> None:

518 """Test :func:`colour.models.rgb.ycbcr.YCbCr_to_RGB` definition."""

519

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 )

525

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 )

537

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 """

543

544 YcCbcCrc = np.array([0.37020379, 0.41137200, 0.77704674])

545 RGB = YcCbcCrc_to_RGB(YcCbcCrc)

546

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 )

554

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 )

562

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 )

570

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 """

576

577 YcCbcCrc = np.array([0.69943807, 0.38814348, 0.61264549])

578 RGB = YcCbcCrc_to_RGB(YcCbcCrc)

579

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 )

588

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 """

595

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)