Coverage for colour/io/tests/test_image.py: 100%

164 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 23:01 +1300

1"""Define the unit tests for the :mod:`colour.io.image` module.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import platform 

7import shutil 

8import tempfile 

9 

10import numpy as np 

11import pytest 

12 

13from colour.constants import TOLERANCE_ABSOLUTE_TESTS 

14from colour.io import ( 

15 Image_Specification_Attribute, 

16 as_3_channels_image, 

17 convert_bit_depth, 

18 image_specification_OpenImageIO, 

19 read_image, 

20 read_image_Imageio, 

21 read_image_OpenImageIO, 

22 write_image, 

23 write_image_Imageio, 

24 write_image_OpenImageIO, 

25) 

26from colour.utilities import attest, full 

27 

28__author__ = "Colour Developers" 

29__copyright__ = "Copyright 2013 Colour Developers" 

30__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

31__maintainer__ = "Colour Developers" 

32__email__ = "colour-developers@colour-science.org" 

33__status__ = "Production" 

34 

35__all__ = [ 

36 "ROOT_RESOURCES", 

37 "TestImageSpecificationOpenImageIO", 

38 "TestConvertBitDepth", 

39 "TestReadImageOpenImageIO", 

40 "TestWriteImageOpenImageIO", 

41 "TestReadImageImageio", 

42 "TestWriteImageImageio", 

43 "TestReadImage", 

44 "TestWriteImage", 

45 "TestAs3ChannelsImage", 

46] 

47 

48ROOT_RESOURCES: str = os.path.join(os.path.dirname(__file__), "resources") 

49 

50 

51@pytest.mark.skipif( 

52 platform.system() == "Windows", 

53 reason="OpenImageIO crashes on Windows due to thread-safety issues", 

54) 

55class TestImageSpecificationOpenImageIO: 

56 """ 

57 Define :func:`colour.io.image.image_specification_OpenImageIO` definition 

58 unit tests methods. 

59 """ 

60 

61 def test_image_specification_OpenImageIO(self) -> None: # pragma: no cover 

62 """ 

63 Test :func:`colour.io.image.image_specification_OpenImageIO` 

64 definition. 

65 """ 

66 

67 from OpenImageIO import HALF # noqa: PLC0415 

68 

69 compression = Image_Specification_Attribute("Compression", "none") 

70 specification = image_specification_OpenImageIO( 

71 1920, 1080, 3, "float16", [compression] 

72 ) 

73 

74 assert specification.width == 1920 # pyright: ignore 

75 assert specification.height == 1080 # pyright: ignore 

76 assert specification.nchannels == 3 # pyright: ignore 

77 assert specification.format == HALF # pyright: ignore 

78 assert specification.extra_attribs[0].name == "Compression" # pyright: ignore 

79 

80 

81class TestConvertBitDepth: 

82 """ 

83 Define :func:`colour.io.image.convert_bit_depth` definition unit tests 

84 methods. 

85 """ 

86 

87 def test_convert_bit_depth(self) -> None: 

88 """Test :func:`colour.io.image.convert_bit_depth` definition.""" 

89 

90 a = np.around(np.linspace(0, 1, 10) * 255).astype("uint8") 

91 assert convert_bit_depth(a, "uint8").dtype is np.dtype("uint8") 

92 np.testing.assert_equal(convert_bit_depth(a, "uint8"), a) 

93 

94 assert convert_bit_depth(a, "uint16").dtype is np.dtype("uint16") 

95 np.testing.assert_equal( 

96 convert_bit_depth(a, "uint16"), 

97 np.array( 

98 [ 

99 0, 

100 7196, 

101 14649, 

102 21845, 

103 29041, 

104 36494, 

105 43690, 

106 50886, 

107 58339, 

108 65535, 

109 ] 

110 ), 

111 ) 

112 

113 assert convert_bit_depth(a, "float16").dtype is np.dtype("float16") 

114 np.testing.assert_allclose( 

115 convert_bit_depth(a, "float16"), 

116 np.array( 

117 [ 

118 0.0000, 

119 0.1098, 

120 0.2235, 

121 0.3333, 

122 0.443, 

123 0.5566, 

124 0.6665, 

125 0.7764, 

126 0.8900, 

127 1.0000, 

128 ] 

129 ), 

130 atol=5e-4, 

131 ) 

132 

133 assert convert_bit_depth(a, "float32").dtype is np.dtype("float32") 

134 np.testing.assert_allclose( 

135 convert_bit_depth(a, "float32"), 

136 np.array( 

137 [ 

138 0.00000000, 

139 0.10980392, 

140 0.22352941, 

141 0.33333334, 

142 0.44313726, 

143 0.55686277, 

144 0.66666669, 

145 0.77647060, 

146 0.89019608, 

147 1.00000000, 

148 ] 

149 ), 

150 atol=TOLERANCE_ABSOLUTE_TESTS, 

151 ) 

152 

153 assert convert_bit_depth(a, "float64").dtype is np.dtype("float64") 

154 

155 if hasattr(np, "float128"): # pragma: no cover 

156 assert convert_bit_depth(a, "float128").dtype is np.dtype("float128") 

157 

158 a = np.around(np.linspace(0, 1, 10) * 65535).astype("uint16") 

159 assert convert_bit_depth(a, "uint8").dtype is np.dtype("uint8") 

160 np.testing.assert_equal( 

161 convert_bit_depth(a, "uint8"), 

162 np.array([0, 28, 56, 85, 113, 141, 170, 198, 226, 255]), 

163 ) 

164 

165 assert convert_bit_depth(a, "uint16").dtype is np.dtype("uint16") 

166 np.testing.assert_equal(convert_bit_depth(a, "uint16"), a) 

167 

168 assert convert_bit_depth(a, "float16").dtype is np.dtype("float16") 

169 np.testing.assert_allclose( 

170 convert_bit_depth(a, "float16"), 

171 np.array( 

172 [ 

173 0.0000, 

174 0.1098, 

175 0.2235, 

176 0.3333, 

177 0.443, 

178 0.5566, 

179 0.6665, 

180 0.7764, 

181 0.8900, 

182 1.0000, 

183 ] 

184 ), 

185 atol=5e-2, 

186 ) 

187 

188 assert convert_bit_depth(a, "float32").dtype is np.dtype("float32") 

189 np.testing.assert_allclose( 

190 convert_bit_depth(a, "float32"), 

191 np.array( 

192 [ 

193 0.00000000, 

194 0.11111620, 

195 0.22221714, 

196 0.33333334, 

197 0.44444954, 

198 0.55555046, 

199 0.66666669, 

200 0.77778286, 

201 0.88888383, 

202 1.00000000, 

203 ] 

204 ), 

205 atol=TOLERANCE_ABSOLUTE_TESTS, 

206 ) 

207 

208 assert convert_bit_depth(a, "float64").dtype is np.dtype("float64") 

209 

210 if hasattr(np, "float128"): # pragma: no cover 

211 assert convert_bit_depth(a, "float128").dtype is np.dtype("float128") 

212 

213 a = np.linspace(0, 1, 10, dtype=np.float64) 

214 assert convert_bit_depth(a, "uint8").dtype is np.dtype("uint8") 

215 np.testing.assert_equal( 

216 convert_bit_depth(a, "uint8"), 

217 np.array([0, 28, 57, 85, 113, 142, 170, 198, 227, 255]), 

218 ) 

219 

220 assert convert_bit_depth(a, "uint16").dtype is np.dtype("uint16") 

221 np.testing.assert_equal( 

222 convert_bit_depth(a, "uint16"), 

223 np.array( 

224 [ 

225 0, 

226 7282, 

227 14563, 

228 21845, 

229 29127, 

230 36408, 

231 43690, 

232 50972, 

233 58253, 

234 65535, 

235 ] 

236 ), 

237 ) 

238 

239 assert convert_bit_depth(a, "float16").dtype is np.dtype("float16") 

240 np.testing.assert_allclose( 

241 convert_bit_depth(a, "float16"), 

242 np.array( 

243 [ 

244 0.0000, 

245 0.1111, 

246 0.2222, 

247 0.3333, 

248 0.4443, 

249 0.5557, 

250 0.6665, 

251 0.7780, 

252 0.8887, 

253 1.0000, 

254 ] 

255 ), 

256 atol=5e-4, 

257 ) 

258 

259 assert convert_bit_depth(a, "float32").dtype is np.dtype("float32") 

260 np.testing.assert_allclose( 

261 convert_bit_depth(a, "float32"), a, atol=TOLERANCE_ABSOLUTE_TESTS 

262 ) 

263 

264 assert convert_bit_depth(a, "float64").dtype is np.dtype("float64") 

265 

266 if hasattr(np, "float128"): # pragma: no cover 

267 assert convert_bit_depth(a, "float128").dtype is np.dtype("float128") 

268 

269 

270@pytest.mark.skipif( 

271 platform.system() == "Windows", 

272 reason="OpenImageIO crashes on Windows due to thread-safety issues", 

273) 

274class TestReadImageOpenImageIO: 

275 """ 

276 Define :func:`colour.io.image.read_image_OpenImageIO` definition unit 

277 tests methods. 

278 """ 

279 

280 def test_read_image_OpenImageIO(self) -> None: # pragma: no cover 

281 """Test :func:`colour.io.image.read_image_OpenImageIO` definition.""" 

282 

283 image = read_image_OpenImageIO( 

284 os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr"), 

285 additional_data=False, 

286 ) 

287 assert image.shape == (1267, 1274, 3) 

288 assert image.dtype is np.dtype("float32") 

289 

290 image = read_image_OpenImageIO( 

291 os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr"), 

292 "float16", 

293 additional_data=False, 

294 ) 

295 assert image.dtype is np.dtype("float16") 

296 

297 image, attributes = read_image_OpenImageIO( 

298 os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr"), 

299 additional_data=True, 

300 ) 

301 assert image.shape == (1267, 1274, 3) 

302 assert len(attributes) > 0 

303 compression_attribute = next( 

304 (attribute for attribute in attributes if attribute.name == "compression"), 

305 None, 

306 ) 

307 assert compression_attribute is not None 

308 

309 image = read_image_OpenImageIO( 

310 os.path.join(ROOT_RESOURCES, "Single_Channel.exr"), 

311 additional_data=False, 

312 ) 

313 assert image.shape == (256, 256) 

314 

315 image = read_image_OpenImageIO( 

316 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), 

317 "uint8", 

318 additional_data=False, 

319 ) 

320 assert image.shape == (128, 256, 4) 

321 assert image.dtype is np.dtype("uint8") 

322 assert np.min(image) == 0 

323 assert np.max(image) == 255 

324 

325 image = read_image_OpenImageIO( 

326 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), 

327 "uint16", 

328 additional_data=False, 

329 ) 

330 assert image.shape == (128, 256, 4) 

331 assert image.dtype is np.dtype("uint16") 

332 assert np.min(image) == 0 

333 assert np.max(image) == 65535 

334 

335 # TODO: Investigate "OIIO" behaviour here: 1.0 != 15360.0 

336 # image = read_image_OpenImageIO( 

337 # os.path.join(ROOT_RESOURCES, 'Colour_Logo.png'), 'float16') 

338 # self.assertIs(image.dtype, np.dtype('float16')) 

339 # self.assertEqual(np.min(image), 0.0) 

340 # self.assertEqual(np.max(image), 1.0) 

341 

342 image = read_image_OpenImageIO( 

343 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), 

344 "float32", 

345 additional_data=False, 

346 ) 

347 assert image.dtype is np.dtype("float32") 

348 assert np.min(image) == 0.0 

349 assert np.max(image) == 1.0 

350 

351 

352@pytest.mark.skipif( 

353 platform.system() == "Windows", 

354 reason="OpenImageIO crashes on Windows due to thread-safety issues", 

355) 

356class TestWriteImageOpenImageIO: 

357 """ 

358 Define :func:`colour.io.image.write_image_OpenImageIO` definition unit 

359 tests methods. 

360 """ 

361 

362 def setup_method(self) -> None: 

363 """Initialise the common tests attributes.""" 

364 

365 self._temporary_directory = tempfile.mkdtemp() 

366 

367 def teardown_method(self) -> None: 

368 """After tests actions.""" 

369 

370 shutil.rmtree(self._temporary_directory) 

371 

372 def test_write_image_OpenImageIO(self) -> None: # pragma: no cover 

373 """Test :func:`colour.io.image.write_image_OpenImageIO` definition.""" 

374 

375 from OpenImageIO import TypeDesc # noqa: PLC0415 

376 

377 path = os.path.join(self._temporary_directory, "8-bit.png") 

378 RGB = full((1, 1, 3), 255, np.uint8) 

379 write_image_OpenImageIO(RGB, path, bit_depth="uint8") 

380 image = read_image_OpenImageIO(path, bit_depth="uint8") 

381 np.testing.assert_equal(np.squeeze(RGB), image) 

382 

383 path = os.path.join(self._temporary_directory, "16-bit.png") 

384 RGB = full((1, 1, 3), 65535, np.uint16) 

385 write_image_OpenImageIO(RGB, path, bit_depth="uint16") 

386 image = read_image_OpenImageIO(path, bit_depth="uint16") 

387 np.testing.assert_equal(np.squeeze(RGB), image) 

388 

389 source_path = os.path.join(ROOT_RESOURCES, "Overflowing_Gradient.png") 

390 source_image = read_image_OpenImageIO(source_path, bit_depth="uint8") 

391 target_path = os.path.join( 

392 self._temporary_directory, "Overflowing_Gradient.png" 

393 ) 

394 RGB = np.arange(0, 256, 1, dtype=np.uint8)[None] * 2 

395 write_image_OpenImageIO(RGB, target_path, bit_depth="uint8") 

396 target_image = read_image_OpenImageIO(source_path, bit_depth="uint8") 

397 np.testing.assert_equal(source_image, target_image) 

398 np.testing.assert_equal(np.squeeze(RGB), target_image) 

399 

400 source_path = os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr") 

401 source_image = read_image_OpenImageIO( 

402 source_path, 

403 additional_data=False, 

404 ) 

405 target_path = os.path.join(self._temporary_directory, "CMS_Test_Pattern.exr") 

406 write_image_OpenImageIO(source_image, target_path) 

407 target_image = read_image_OpenImageIO( 

408 target_path, 

409 additional_data=False, 

410 ) 

411 np.testing.assert_equal(source_image, target_image) 

412 assert target_image.shape == (1267, 1274, 3) 

413 assert target_image.dtype is np.dtype("float32") 

414 

415 chromaticities = ( 

416 0.73470, 

417 0.26530, 

418 0.00000, 

419 1.00000, 

420 0.00010, 

421 -0.07700, 

422 0.32168, 

423 0.33767, 

424 ) 

425 write_attributes = [ 

426 Image_Specification_Attribute("customBooleanFlag", True), 

427 Image_Specification_Attribute( 

428 "chromaticities", chromaticities, TypeDesc("float[8]") 

429 ), 

430 Image_Specification_Attribute("compression", "none"), 

431 ] 

432 write_image_OpenImageIO(target_image, target_path, attributes=write_attributes) 

433 target_image, read_attributes = read_image_OpenImageIO( 

434 target_path, additional_data=True 

435 ) 

436 for write_attribute in write_attributes: 

437 attribute_exists = False 

438 for read_attribute in read_attributes: 

439 if write_attribute.name == read_attribute.name: 

440 attribute_exists = True 

441 if isinstance(write_attribute.value, tuple): 

442 np.testing.assert_allclose( 

443 write_attribute.value, 

444 read_attribute.value, 

445 atol=TOLERANCE_ABSOLUTE_TESTS, 

446 ) 

447 else: 

448 assert write_attribute.value == read_attribute.value 

449 

450 attest( 

451 attribute_exists, 

452 f'"{write_attribute.name}" attribute was not found on image!', 

453 ) 

454 

455 

456class TestReadImageImageio: 

457 """ 

458 Define :func:`colour.io.image.read_image_Imageio` definition unit tests 

459 methods. 

460 """ 

461 

462 def test_read_image_Imageio(self) -> None: 

463 """Test :func:`colour.io.image.read_image_Imageio` definition.""" 

464 

465 image = read_image_Imageio(os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr")) 

466 assert image.shape == (1267, 1274, 3) 

467 assert image.dtype is np.dtype("float32") 

468 

469 image = read_image_Imageio( 

470 os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr"), 

471 "float16", 

472 ) 

473 assert image.shape == (1267, 1274, 3) 

474 assert image.dtype is np.dtype("float16") 

475 

476 image = read_image_Imageio(os.path.join(ROOT_RESOURCES, "Single_Channel.exr")) 

477 assert image.shape == (256, 256) 

478 

479 image = read_image_Imageio( 

480 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), "uint8" 

481 ) 

482 assert image.shape == (128, 256, 4) 

483 assert image.dtype is np.dtype("uint8") 

484 assert np.min(image) == 0 

485 assert np.max(image) == 255 

486 

487 image = read_image_Imageio( 

488 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), "uint16" 

489 ) 

490 assert image.shape == (128, 256, 4) 

491 assert image.dtype is np.dtype("uint16") 

492 assert np.min(image) == 0 

493 assert np.max(image) == 65535 

494 

495 image = read_image_Imageio( 

496 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), "float16" 

497 ) 

498 assert image.dtype is np.dtype("float16") 

499 assert np.min(image) == 0.0 

500 assert np.max(image) == 1.0 

501 

502 image = read_image_Imageio( 

503 os.path.join(ROOT_RESOURCES, "Colour_Logo.png"), "float32" 

504 ) 

505 assert image.dtype is np.dtype("float32") 

506 assert np.min(image) == 0.0 

507 assert np.max(image) == 1.0 

508 

509 

510class TestWriteImageImageio: 

511 """ 

512 Define :func:`colour.io.image.write_image_Imageio` definition unit 

513 tests methods. 

514 """ 

515 

516 def setup_method(self) -> None: 

517 """Initialise the common tests attributes.""" 

518 

519 self._temporary_directory = tempfile.mkdtemp() 

520 

521 def teardown_method(self) -> None: 

522 """After tests actions.""" 

523 

524 shutil.rmtree(self._temporary_directory) 

525 

526 def test_write_image_Imageio(self) -> None: 

527 """Test :func:`colour.io.image.write_image_Imageio` definition.""" 

528 

529 source_path = os.path.join(ROOT_RESOURCES, "Overflowing_Gradient.png") 

530 source_image = read_image_Imageio(source_path, bit_depth="uint8") 

531 target_path = os.path.join( 

532 self._temporary_directory, "Overflowing_Gradient.png" 

533 ) 

534 RGB = np.arange(0, 256, 1, dtype=np.uint8)[None] * 2 

535 write_image_Imageio(RGB, target_path, bit_depth="uint8") 

536 target_image = read_image_Imageio(target_path, bit_depth="uint8") 

537 np.testing.assert_equal(np.squeeze(RGB), target_image) 

538 np.testing.assert_equal(source_image, target_image) 

539 

540 @pytest.mark.skipif( 

541 platform.system() == "Linux", 

542 reason="EXR tests are breaking on Linux", 

543 ) 

544 def test_write_image_Imageio_exr(self) -> None: 

545 """ 

546 Test :func:`colour.io.image.write_image_Imageio` definition with EXR 

547 files. 

548 """ 

549 

550 source_path = os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr") 

551 source_image = read_image_Imageio(source_path) 

552 target_path = os.path.join(self._temporary_directory, "CMS_Test_Pattern.exr") 

553 write_image_Imageio(source_image, target_path) 

554 target_image = read_image_Imageio(target_path) 

555 np.testing.assert_allclose( 

556 source_image, target_image, atol=TOLERANCE_ABSOLUTE_TESTS 

557 ) 

558 assert target_image.shape == (1267, 1274, 3) 

559 assert target_image.dtype is np.dtype("float32") 

560 

561 target_path = os.path.join(self._temporary_directory, "Full_White.exr") 

562 target_image = full((32, 16, 3), 1e6, dtype=np.float16) 

563 write_image_Imageio(target_image, target_path) 

564 target_image = read_image_Imageio(target_path) 

565 assert np.max(target_image) == np.inf 

566 

567 target_image = full((32, 16, 3), 1e6) 

568 write_image_Imageio(target_image, target_path) 

569 target_image = read_image_Imageio(target_path) 

570 assert np.max(target_image) == 1e6 

571 

572 

573class TestReadImage: 

574 """ 

575 Define :func:`colour.io.image.read_image` definition unit tests 

576 methods. 

577 """ 

578 

579 def test_read_image(self) -> None: 

580 """Test :func:`colour.io.image.read_image` definition.""" 

581 

582 image = read_image(os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr")) 

583 assert image.shape == (1267, 1274, 3) 

584 assert image.dtype is np.dtype("float32") 

585 

586 image = read_image(os.path.join(ROOT_RESOURCES, "Single_Channel.exr")) 

587 assert image.shape == (256, 256) 

588 

589 

590@pytest.mark.skipif( 

591 platform.system() == "Windows", 

592 reason="OpenImageIO crashes on Windows due to thread-safety issues", 

593) 

594class TestWriteImage: 

595 """Define :func:`colour.io.image.write_image` definition unit tests methods.""" 

596 

597 def setup_method(self) -> None: 

598 """Initialise the common tests attributes.""" 

599 

600 self._temporary_directory = tempfile.mkdtemp() 

601 

602 def teardown_method(self) -> None: 

603 """After tests actions.""" 

604 

605 shutil.rmtree(self._temporary_directory) 

606 

607 def test_write_image(self) -> None: 

608 """Test :func:`colour.io.image.write_image` definition.""" 

609 

610 source_path = os.path.join(ROOT_RESOURCES, "CMS_Test_Pattern.exr") 

611 source_image = read_image(source_path) 

612 target_path = os.path.join(self._temporary_directory, "CMS_Test_Pattern.exr") 

613 write_image(source_image, target_path) 

614 target_image = read_image(target_path) 

615 np.testing.assert_allclose( 

616 source_image, target_image, atol=TOLERANCE_ABSOLUTE_TESTS 

617 ) 

618 assert target_image.shape == (1267, 1274, 3) 

619 assert target_image.dtype is np.dtype("float32") 

620 

621 

622class TestAs3ChannelsImage: 

623 """ 

624 Define :func:`colour.io.image.as_3_channels_image` definition unit tests 

625 methods. 

626 """ 

627 

628 def test_as_3_channels_image(self) -> None: 

629 """Test :func:`colour.io.image.as_3_channels_image` definition.""" 

630 

631 a = 0.18 

632 b = np.array([[[0.18, 0.18, 0.18]]]) 

633 np.testing.assert_equal(as_3_channels_image(a), b) 

634 a = np.array([0.18]) 

635 np.testing.assert_equal(as_3_channels_image(a), b) 

636 a = np.array([0.18, 0.18, 0.18]) 

637 np.testing.assert_equal(as_3_channels_image(a), b) 

638 a = np.array([[0.18, 0.18, 0.18]]) 

639 np.testing.assert_equal(as_3_channels_image(a), b) 

640 a = np.array([[[0.18, 0.18, 0.18]]]) 

641 np.testing.assert_equal(as_3_channels_image(a), b) 

642 a = np.array([[[[0.18, 0.18, 0.18]]]]) 

643 np.testing.assert_equal(as_3_channels_image(a), b) 

644 a = np.array([[0.18, 0.18, 0.18], [0.20, 0.20, 0.20]]) 

645 result = as_3_channels_image(a) 

646 assert result.shape == (1, 2, 3) 

647 

648 def test_raise_exception_as_3_channels_image(self) -> None: 

649 """ 

650 Test :func:`colour.io.image.as_3_channels_image` definition raised 

651 exception. 

652 """ 

653 

654 pytest.raises( 

655 ValueError, 

656 as_3_channels_image, 

657 [ 

658 [ 

659 [[0.18, 0.18, 0.18], [0.18, 0.18, 0.18]], 

660 [[0.18, 0.18, 0.18], [0.18, 0.18, 0.18]], 

661 ], 

662 [ 

663 [[0.18, 0.18, 0.18], [0.18, 0.18, 0.18]], 

664 [[0.18, 0.18, 0.18], [0.18, 0.18, 0.18]], 

665 ], 

666 ], 

667 ) 

668 

669 pytest.raises( 

670 ValueError, 

671 as_3_channels_image, 

672 [0.18, 0.18, 0.18, 0.18], 

673 )