Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/Image.py: 17%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1714 statements  

1# 

2# The Python Imaging Library. 

3# $Id$ 

4# 

5# the Image class wrapper 

6# 

7# partial release history: 

8# 1995-09-09 fl Created 

9# 1996-03-11 fl PIL release 0.0 (proof of concept) 

10# 1996-04-30 fl PIL release 0.1b1 

11# 1999-07-28 fl PIL release 1.0 final 

12# 2000-06-07 fl PIL release 1.1 

13# 2000-10-20 fl PIL release 1.1.1 

14# 2001-05-07 fl PIL release 1.1.2 

15# 2002-03-15 fl PIL release 1.1.3 

16# 2003-05-10 fl PIL release 1.1.4 

17# 2005-03-28 fl PIL release 1.1.5 

18# 2006-12-02 fl PIL release 1.1.6 

19# 2009-11-15 fl PIL release 1.1.7 

20# 

21# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. 

22# Copyright (c) 1995-2009 by Fredrik Lundh. 

23# 

24# See the README file for information on usage and redistribution. 

25# 

26 

27from __future__ import annotations 

28 

29import abc 

30import atexit 

31import builtins 

32import io 

33import logging 

34import math 

35import os 

36import re 

37import struct 

38import sys 

39import tempfile 

40import warnings 

41from collections.abc import Callable, Iterator, MutableMapping, Sequence 

42from enum import IntEnum 

43from types import ModuleType 

44from typing import IO, Any, Literal, Protocol, cast 

45 

46# VERSION was removed in Pillow 6.0.0. 

47# PILLOW_VERSION was removed in Pillow 9.0.0. 

48# Use __version__ instead. 

49from . import ( 

50 ExifTags, 

51 ImageMode, 

52 TiffTags, 

53 UnidentifiedImageError, 

54 __version__, 

55 _plugins, 

56) 

57from ._binary import i32le, o32be, o32le 

58from ._deprecate import deprecate 

59from ._util import DeferredError, is_path 

60 

61ElementTree: ModuleType | None 

62try: 

63 from defusedxml import ElementTree 

64except ImportError: 

65 ElementTree = None 

66 

67logger = logging.getLogger(__name__) 

68 

69 

70class DecompressionBombWarning(RuntimeWarning): 

71 pass 

72 

73 

74class DecompressionBombError(Exception): 

75 pass 

76 

77 

78WARN_POSSIBLE_FORMATS: bool = False 

79 

80# Limit to around a quarter gigabyte for a 24-bit (3 bpp) image 

81MAX_IMAGE_PIXELS: int | None = int(1024 * 1024 * 1024 // 4 // 3) 

82 

83 

84try: 

85 # If the _imaging C module is not present, Pillow will not load. 

86 # Note that other modules should not refer to _imaging directly; 

87 # import Image and use the Image.core variable instead. 

88 # Also note that Image.core is not a publicly documented interface, 

89 # and should be considered private and subject to change. 

90 from . import _imaging as core 

91 

92 if __version__ != getattr(core, "PILLOW_VERSION", None): 

93 msg = ( 

94 "The _imaging extension was built for another version of Pillow or PIL:\n" 

95 f"Core version: {getattr(core, 'PILLOW_VERSION', None)}\n" 

96 f"Pillow version: {__version__}" 

97 ) 

98 raise ImportError(msg) 

99 

100except ImportError as v: 

101 core = DeferredError.new(ImportError("The _imaging C module is not installed.")) 

102 # Explanations for ways that we know we might have an import error 

103 if str(v).startswith("Module use of python"): 

104 # The _imaging C module is present, but not compiled for 

105 # the right version (windows only). Print a warning, if 

106 # possible. 

107 warnings.warn( 

108 "The _imaging extension was built for another version of Python.", 

109 RuntimeWarning, 

110 ) 

111 elif str(v).startswith("The _imaging extension"): 

112 warnings.warn(str(v), RuntimeWarning) 

113 # Fail here anyway. Don't let people run with a mostly broken Pillow. 

114 # see docs/porting.rst 

115 raise 

116 

117 

118def isImageType(t: Any) -> TypeGuard[Image]: 

119 """ 

120 Checks if an object is an image object. 

121 

122 .. warning:: 

123 

124 This function is for internal use only. 

125 

126 :param t: object to check if it's an image 

127 :returns: True if the object is an image 

128 """ 

129 deprecate("Image.isImageType(im)", 12, "isinstance(im, Image.Image)") 

130 return hasattr(t, "im") 

131 

132 

133# 

134# Constants 

135 

136 

137# transpose 

138class Transpose(IntEnum): 

139 FLIP_LEFT_RIGHT = 0 

140 FLIP_TOP_BOTTOM = 1 

141 ROTATE_90 = 2 

142 ROTATE_180 = 3 

143 ROTATE_270 = 4 

144 TRANSPOSE = 5 

145 TRANSVERSE = 6 

146 

147 

148# transforms (also defined in Imaging.h) 

149class Transform(IntEnum): 

150 AFFINE = 0 

151 EXTENT = 1 

152 PERSPECTIVE = 2 

153 QUAD = 3 

154 MESH = 4 

155 

156 

157# resampling filters (also defined in Imaging.h) 

158class Resampling(IntEnum): 

159 NEAREST = 0 

160 BOX = 4 

161 BILINEAR = 2 

162 HAMMING = 5 

163 BICUBIC = 3 

164 LANCZOS = 1 

165 

166 

167_filters_support = { 

168 Resampling.BOX: 0.5, 

169 Resampling.BILINEAR: 1.0, 

170 Resampling.HAMMING: 1.0, 

171 Resampling.BICUBIC: 2.0, 

172 Resampling.LANCZOS: 3.0, 

173} 

174 

175 

176# dithers 

177class Dither(IntEnum): 

178 NONE = 0 

179 ORDERED = 1 # Not yet implemented 

180 RASTERIZE = 2 # Not yet implemented 

181 FLOYDSTEINBERG = 3 # default 

182 

183 

184# palettes/quantizers 

185class Palette(IntEnum): 

186 WEB = 0 

187 ADAPTIVE = 1 

188 

189 

190class Quantize(IntEnum): 

191 MEDIANCUT = 0 

192 MAXCOVERAGE = 1 

193 FASTOCTREE = 2 

194 LIBIMAGEQUANT = 3 

195 

196 

197module = sys.modules[__name__] 

198for enum in (Transpose, Transform, Resampling, Dither, Palette, Quantize): 

199 for item in enum: 

200 setattr(module, item.name, item.value) 

201 

202 

203if hasattr(core, "DEFAULT_STRATEGY"): 

204 DEFAULT_STRATEGY = core.DEFAULT_STRATEGY 

205 FILTERED = core.FILTERED 

206 HUFFMAN_ONLY = core.HUFFMAN_ONLY 

207 RLE = core.RLE 

208 FIXED = core.FIXED 

209 

210 

211# -------------------------------------------------------------------- 

212# Registries 

213 

214TYPE_CHECKING = False 

215if TYPE_CHECKING: 

216 import mmap 

217 from xml.etree.ElementTree import Element 

218 

219 from IPython.lib.pretty import PrettyPrinter 

220 

221 from . import ImageFile, ImageFilter, ImagePalette, ImageQt, TiffImagePlugin 

222 from ._typing import CapsuleType, NumpyArray, StrOrBytesPath, TypeGuard 

223ID: list[str] = [] 

224OPEN: dict[ 

225 str, 

226 tuple[ 

227 Callable[[IO[bytes], str | bytes], ImageFile.ImageFile], 

228 Callable[[bytes], bool | str] | None, 

229 ], 

230] = {} 

231MIME: dict[str, str] = {} 

232SAVE: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} 

233SAVE_ALL: dict[str, Callable[[Image, IO[bytes], str | bytes], None]] = {} 

234EXTENSION: dict[str, str] = {} 

235DECODERS: dict[str, type[ImageFile.PyDecoder]] = {} 

236ENCODERS: dict[str, type[ImageFile.PyEncoder]] = {} 

237 

238# -------------------------------------------------------------------- 

239# Modes 

240 

241_ENDIAN = "<" if sys.byteorder == "little" else ">" 

242 

243 

244def _conv_type_shape(im: Image) -> tuple[tuple[int, ...], str]: 

245 m = ImageMode.getmode(im.mode) 

246 shape: tuple[int, ...] = (im.height, im.width) 

247 extra = len(m.bands) 

248 if extra != 1: 

249 shape += (extra,) 

250 return shape, m.typestr 

251 

252 

253MODES = [ 

254 "1", 

255 "CMYK", 

256 "F", 

257 "HSV", 

258 "I", 

259 "I;16", 

260 "I;16B", 

261 "I;16L", 

262 "I;16N", 

263 "L", 

264 "LA", 

265 "La", 

266 "LAB", 

267 "P", 

268 "PA", 

269 "RGB", 

270 "RGBA", 

271 "RGBa", 

272 "RGBX", 

273 "YCbCr", 

274] 

275 

276# raw modes that may be memory mapped. NOTE: if you change this, you 

277# may have to modify the stride calculation in map.c too! 

278_MAPMODES = ("L", "P", "RGBX", "RGBA", "CMYK", "I;16", "I;16L", "I;16B") 

279 

280 

281def getmodebase(mode: str) -> str: 

282 """ 

283 Gets the "base" mode for given mode. This function returns "L" for 

284 images that contain grayscale data, and "RGB" for images that 

285 contain color data. 

286 

287 :param mode: Input mode. 

288 :returns: "L" or "RGB". 

289 :exception KeyError: If the input mode was not a standard mode. 

290 """ 

291 return ImageMode.getmode(mode).basemode 

292 

293 

294def getmodetype(mode: str) -> str: 

295 """ 

296 Gets the storage type mode. Given a mode, this function returns a 

297 single-layer mode suitable for storing individual bands. 

298 

299 :param mode: Input mode. 

300 :returns: "L", "I", or "F". 

301 :exception KeyError: If the input mode was not a standard mode. 

302 """ 

303 return ImageMode.getmode(mode).basetype 

304 

305 

306def getmodebandnames(mode: str) -> tuple[str, ...]: 

307 """ 

308 Gets a list of individual band names. Given a mode, this function returns 

309 a tuple containing the names of individual bands (use 

310 :py:method:`~PIL.Image.getmodetype` to get the mode used to store each 

311 individual band. 

312 

313 :param mode: Input mode. 

314 :returns: A tuple containing band names. The length of the tuple 

315 gives the number of bands in an image of the given mode. 

316 :exception KeyError: If the input mode was not a standard mode. 

317 """ 

318 return ImageMode.getmode(mode).bands 

319 

320 

321def getmodebands(mode: str) -> int: 

322 """ 

323 Gets the number of individual bands for this mode. 

324 

325 :param mode: Input mode. 

326 :returns: The number of bands in this mode. 

327 :exception KeyError: If the input mode was not a standard mode. 

328 """ 

329 return len(ImageMode.getmode(mode).bands) 

330 

331 

332# -------------------------------------------------------------------- 

333# Helpers 

334 

335_initialized = 0 

336 

337 

338def preinit() -> None: 

339 """ 

340 Explicitly loads BMP, GIF, JPEG, PPM and PPM file format drivers. 

341 

342 It is called when opening or saving images. 

343 """ 

344 

345 global _initialized 

346 if _initialized >= 1: 

347 return 

348 

349 try: 

350 from . import BmpImagePlugin 

351 

352 assert BmpImagePlugin 

353 except ImportError: 

354 pass 

355 try: 

356 from . import GifImagePlugin 

357 

358 assert GifImagePlugin 

359 except ImportError: 

360 pass 

361 try: 

362 from . import JpegImagePlugin 

363 

364 assert JpegImagePlugin 

365 except ImportError: 

366 pass 

367 try: 

368 from . import PpmImagePlugin 

369 

370 assert PpmImagePlugin 

371 except ImportError: 

372 pass 

373 try: 

374 from . import PngImagePlugin 

375 

376 assert PngImagePlugin 

377 except ImportError: 

378 pass 

379 

380 _initialized = 1 

381 

382 

383def init() -> bool: 

384 """ 

385 Explicitly initializes the Python Imaging Library. This function 

386 loads all available file format drivers. 

387 

388 It is called when opening or saving images if :py:meth:`~preinit()` is 

389 insufficient, and by :py:meth:`~PIL.features.pilinfo`. 

390 """ 

391 

392 global _initialized 

393 if _initialized >= 2: 

394 return False 

395 

396 parent_name = __name__.rpartition(".")[0] 

397 for plugin in _plugins: 

398 try: 

399 logger.debug("Importing %s", plugin) 

400 __import__(f"{parent_name}.{plugin}", globals(), locals(), []) 

401 except ImportError as e: 

402 logger.debug("Image: failed to import %s: %s", plugin, e) 

403 

404 if OPEN or SAVE: 

405 _initialized = 2 

406 return True 

407 return False 

408 

409 

410# -------------------------------------------------------------------- 

411# Codec factories (used by tobytes/frombytes and ImageFile.load) 

412 

413 

414def _getdecoder( 

415 mode: str, decoder_name: str, args: Any, extra: tuple[Any, ...] = () 

416) -> core.ImagingDecoder | ImageFile.PyDecoder: 

417 # tweak arguments 

418 if args is None: 

419 args = () 

420 elif not isinstance(args, tuple): 

421 args = (args,) 

422 

423 try: 

424 decoder = DECODERS[decoder_name] 

425 except KeyError: 

426 pass 

427 else: 

428 return decoder(mode, *args + extra) 

429 

430 try: 

431 # get decoder 

432 decoder = getattr(core, f"{decoder_name}_decoder") 

433 except AttributeError as e: 

434 msg = f"decoder {decoder_name} not available" 

435 raise OSError(msg) from e 

436 return decoder(mode, *args + extra) 

437 

438 

439def _getencoder( 

440 mode: str, encoder_name: str, args: Any, extra: tuple[Any, ...] = () 

441) -> core.ImagingEncoder | ImageFile.PyEncoder: 

442 # tweak arguments 

443 if args is None: 

444 args = () 

445 elif not isinstance(args, tuple): 

446 args = (args,) 

447 

448 try: 

449 encoder = ENCODERS[encoder_name] 

450 except KeyError: 

451 pass 

452 else: 

453 return encoder(mode, *args + extra) 

454 

455 try: 

456 # get encoder 

457 encoder = getattr(core, f"{encoder_name}_encoder") 

458 except AttributeError as e: 

459 msg = f"encoder {encoder_name} not available" 

460 raise OSError(msg) from e 

461 return encoder(mode, *args + extra) 

462 

463 

464# -------------------------------------------------------------------- 

465# Simple expression analyzer 

466 

467 

468class ImagePointTransform: 

469 """ 

470 Used with :py:meth:`~PIL.Image.Image.point` for single band images with more than 

471 8 bits, this represents an affine transformation, where the value is multiplied by 

472 ``scale`` and ``offset`` is added. 

473 """ 

474 

475 def __init__(self, scale: float, offset: float) -> None: 

476 self.scale = scale 

477 self.offset = offset 

478 

479 def __neg__(self) -> ImagePointTransform: 

480 return ImagePointTransform(-self.scale, -self.offset) 

481 

482 def __add__(self, other: ImagePointTransform | float) -> ImagePointTransform: 

483 if isinstance(other, ImagePointTransform): 

484 return ImagePointTransform( 

485 self.scale + other.scale, self.offset + other.offset 

486 ) 

487 return ImagePointTransform(self.scale, self.offset + other) 

488 

489 __radd__ = __add__ 

490 

491 def __sub__(self, other: ImagePointTransform | float) -> ImagePointTransform: 

492 return self + -other 

493 

494 def __rsub__(self, other: ImagePointTransform | float) -> ImagePointTransform: 

495 return other + -self 

496 

497 def __mul__(self, other: ImagePointTransform | float) -> ImagePointTransform: 

498 if isinstance(other, ImagePointTransform): 

499 return NotImplemented 

500 return ImagePointTransform(self.scale * other, self.offset * other) 

501 

502 __rmul__ = __mul__ 

503 

504 def __truediv__(self, other: ImagePointTransform | float) -> ImagePointTransform: 

505 if isinstance(other, ImagePointTransform): 

506 return NotImplemented 

507 return ImagePointTransform(self.scale / other, self.offset / other) 

508 

509 

510def _getscaleoffset( 

511 expr: Callable[[ImagePointTransform], ImagePointTransform | float], 

512) -> tuple[float, float]: 

513 a = expr(ImagePointTransform(1, 0)) 

514 return (a.scale, a.offset) if isinstance(a, ImagePointTransform) else (0, a) 

515 

516 

517# -------------------------------------------------------------------- 

518# Implementation wrapper 

519 

520 

521class SupportsGetData(Protocol): 

522 def getdata( 

523 self, 

524 ) -> tuple[Transform, Sequence[int]]: ... 

525 

526 

527class Image: 

528 """ 

529 This class represents an image object. To create 

530 :py:class:`~PIL.Image.Image` objects, use the appropriate factory 

531 functions. There's hardly ever any reason to call the Image constructor 

532 directly. 

533 

534 * :py:func:`~PIL.Image.open` 

535 * :py:func:`~PIL.Image.new` 

536 * :py:func:`~PIL.Image.frombytes` 

537 """ 

538 

539 format: str | None = None 

540 format_description: str | None = None 

541 _close_exclusive_fp_after_loading = True 

542 

543 def __init__(self) -> None: 

544 # FIXME: take "new" parameters / other image? 

545 self._im: core.ImagingCore | DeferredError | None = None 

546 self._mode = "" 

547 self._size = (0, 0) 

548 self.palette: ImagePalette.ImagePalette | None = None 

549 self.info: dict[str | tuple[int, int], Any] = {} 

550 self.readonly = 0 

551 self._exif: Exif | None = None 

552 

553 @property 

554 def im(self) -> core.ImagingCore: 

555 if isinstance(self._im, DeferredError): 

556 raise self._im.ex 

557 assert self._im is not None 

558 return self._im 

559 

560 @im.setter 

561 def im(self, im: core.ImagingCore) -> None: 

562 self._im = im 

563 

564 @property 

565 def width(self) -> int: 

566 return self.size[0] 

567 

568 @property 

569 def height(self) -> int: 

570 return self.size[1] 

571 

572 @property 

573 def size(self) -> tuple[int, int]: 

574 return self._size 

575 

576 @property 

577 def mode(self) -> str: 

578 return self._mode 

579 

580 @property 

581 def readonly(self) -> int: 

582 return (self._im and self._im.readonly) or self._readonly 

583 

584 @readonly.setter 

585 def readonly(self, readonly: int) -> None: 

586 self._readonly = readonly 

587 

588 def _new(self, im: core.ImagingCore) -> Image: 

589 new = Image() 

590 new.im = im 

591 new._mode = im.mode 

592 new._size = im.size 

593 if im.mode in ("P", "PA"): 

594 if self.palette: 

595 new.palette = self.palette.copy() 

596 else: 

597 from . import ImagePalette 

598 

599 new.palette = ImagePalette.ImagePalette() 

600 new.info = self.info.copy() 

601 return new 

602 

603 # Context manager support 

604 def __enter__(self): 

605 return self 

606 

607 def __exit__(self, *args): 

608 from . import ImageFile 

609 

610 if isinstance(self, ImageFile.ImageFile): 

611 if getattr(self, "_exclusive_fp", False): 

612 self._close_fp() 

613 self.fp = None 

614 

615 def close(self) -> None: 

616 """ 

617 This operation will destroy the image core and release its memory. 

618 The image data will be unusable afterward. 

619 

620 This function is required to close images that have multiple frames or 

621 have not had their file read and closed by the 

622 :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for 

623 more information. 

624 """ 

625 if getattr(self, "map", None): 

626 if sys.platform == "win32" and hasattr(sys, "pypy_version_info"): 

627 self.map.close() 

628 self.map: mmap.mmap | None = None 

629 

630 # Instead of simply setting to None, we're setting up a 

631 # deferred error that will better explain that the core image 

632 # object is gone. 

633 self._im = DeferredError(ValueError("Operation on closed image")) 

634 

635 def _copy(self) -> None: 

636 self.load() 

637 self.im = self.im.copy() 

638 self.readonly = 0 

639 

640 def _ensure_mutable(self) -> None: 

641 if self.readonly: 

642 self._copy() 

643 else: 

644 self.load() 

645 

646 def _dump( 

647 self, file: str | None = None, format: str | None = None, **options: Any 

648 ) -> str: 

649 suffix = "" 

650 if format: 

651 suffix = f".{format}" 

652 

653 if not file: 

654 f, filename = tempfile.mkstemp(suffix) 

655 os.close(f) 

656 else: 

657 filename = file 

658 if not filename.endswith(suffix): 

659 filename = filename + suffix 

660 

661 self.load() 

662 

663 if not format or format == "PPM": 

664 self.im.save_ppm(filename) 

665 else: 

666 self.save(filename, format, **options) 

667 

668 return filename 

669 

670 def __eq__(self, other: object) -> bool: 

671 if self.__class__ is not other.__class__: 

672 return False 

673 assert isinstance(other, Image) 

674 return ( 

675 self.mode == other.mode 

676 and self.size == other.size 

677 and self.info == other.info 

678 and self.getpalette() == other.getpalette() 

679 and self.tobytes() == other.tobytes() 

680 ) 

681 

682 def __repr__(self) -> str: 

683 return ( 

684 f"<{self.__class__.__module__}.{self.__class__.__name__} " 

685 f"image mode={self.mode} size={self.size[0]}x{self.size[1]} " 

686 f"at 0x{id(self):X}>" 

687 ) 

688 

689 def _repr_pretty_(self, p: PrettyPrinter, cycle: bool) -> None: 

690 """IPython plain text display support""" 

691 

692 # Same as __repr__ but without unpredictable id(self), 

693 # to keep Jupyter notebook `text/plain` output stable. 

694 p.text( 

695 f"<{self.__class__.__module__}.{self.__class__.__name__} " 

696 f"image mode={self.mode} size={self.size[0]}x{self.size[1]}>" 

697 ) 

698 

699 def _repr_image(self, image_format: str, **kwargs: Any) -> bytes | None: 

700 """Helper function for iPython display hook. 

701 

702 :param image_format: Image format. 

703 :returns: image as bytes, saved into the given format. 

704 """ 

705 b = io.BytesIO() 

706 try: 

707 self.save(b, image_format, **kwargs) 

708 except Exception: 

709 return None 

710 return b.getvalue() 

711 

712 def _repr_png_(self) -> bytes | None: 

713 """iPython display hook support for PNG format. 

714 

715 :returns: PNG version of the image as bytes 

716 """ 

717 return self._repr_image("PNG", compress_level=1) 

718 

719 def _repr_jpeg_(self) -> bytes | None: 

720 """iPython display hook support for JPEG format. 

721 

722 :returns: JPEG version of the image as bytes 

723 """ 

724 return self._repr_image("JPEG") 

725 

726 @property 

727 def __array_interface__(self) -> dict[str, str | bytes | int | tuple[int, ...]]: 

728 # numpy array interface support 

729 new: dict[str, str | bytes | int | tuple[int, ...]] = {"version": 3} 

730 if self.mode == "1": 

731 # Binary images need to be extended from bits to bytes 

732 # See: https://github.com/python-pillow/Pillow/issues/350 

733 new["data"] = self.tobytes("raw", "L") 

734 else: 

735 new["data"] = self.tobytes() 

736 new["shape"], new["typestr"] = _conv_type_shape(self) 

737 return new 

738 

739 def __arrow_c_schema__(self) -> object: 

740 self.load() 

741 return self.im.__arrow_c_schema__() 

742 

743 def __arrow_c_array__( 

744 self, requested_schema: object | None = None 

745 ) -> tuple[object, object]: 

746 self.load() 

747 return (self.im.__arrow_c_schema__(), self.im.__arrow_c_array__()) 

748 

749 def __getstate__(self) -> list[Any]: 

750 im_data = self.tobytes() # load image first 

751 return [self.info, self.mode, self.size, self.getpalette(), im_data] 

752 

753 def __setstate__(self, state: list[Any]) -> None: 

754 Image.__init__(self) 

755 info, mode, size, palette, data = state[:5] 

756 self.info = info 

757 self._mode = mode 

758 self._size = size 

759 self.im = core.new(mode, size) 

760 if mode in ("L", "LA", "P", "PA") and palette: 

761 self.putpalette(palette) 

762 self.frombytes(data) 

763 

764 def tobytes(self, encoder_name: str = "raw", *args: Any) -> bytes: 

765 """ 

766 Return image as a bytes object. 

767 

768 .. warning:: 

769 

770 This method returns the raw image data from the internal 

771 storage. For compressed image data (e.g. PNG, JPEG) use 

772 :meth:`~.save`, with a BytesIO parameter for in-memory 

773 data. 

774 

775 :param encoder_name: What encoder to use. The default is to 

776 use the standard "raw" encoder. 

777 

778 A list of C encoders can be seen under 

779 codecs section of the function array in 

780 :file:`_imaging.c`. Python encoders are 

781 registered within the relevant plugins. 

782 :param args: Extra arguments to the encoder. 

783 :returns: A :py:class:`bytes` object. 

784 """ 

785 

786 encoder_args: Any = args 

787 if len(encoder_args) == 1 and isinstance(encoder_args[0], tuple): 

788 # may pass tuple instead of argument list 

789 encoder_args = encoder_args[0] 

790 

791 if encoder_name == "raw" and encoder_args == (): 

792 encoder_args = self.mode 

793 

794 self.load() 

795 

796 if self.width == 0 or self.height == 0: 

797 return b"" 

798 

799 # unpack data 

800 e = _getencoder(self.mode, encoder_name, encoder_args) 

801 e.setimage(self.im) 

802 

803 bufsize = max(65536, self.size[0] * 4) # see RawEncode.c 

804 

805 output = [] 

806 while True: 

807 bytes_consumed, errcode, data = e.encode(bufsize) 

808 output.append(data) 

809 if errcode: 

810 break 

811 if errcode < 0: 

812 msg = f"encoder error {errcode} in tobytes" 

813 raise RuntimeError(msg) 

814 

815 return b"".join(output) 

816 

817 def tobitmap(self, name: str = "image") -> bytes: 

818 """ 

819 Returns the image converted to an X11 bitmap. 

820 

821 .. note:: This method only works for mode "1" images. 

822 

823 :param name: The name prefix to use for the bitmap variables. 

824 :returns: A string containing an X11 bitmap. 

825 :raises ValueError: If the mode is not "1" 

826 """ 

827 

828 self.load() 

829 if self.mode != "1": 

830 msg = "not a bitmap" 

831 raise ValueError(msg) 

832 data = self.tobytes("xbm") 

833 return b"".join( 

834 [ 

835 f"#define {name}_width {self.size[0]}\n".encode("ascii"), 

836 f"#define {name}_height {self.size[1]}\n".encode("ascii"), 

837 f"static char {name}_bits[] = {{\n".encode("ascii"), 

838 data, 

839 b"};", 

840 ] 

841 ) 

842 

843 def frombytes( 

844 self, 

845 data: bytes | bytearray | SupportsArrayInterface, 

846 decoder_name: str = "raw", 

847 *args: Any, 

848 ) -> None: 

849 """ 

850 Loads this image with pixel data from a bytes object. 

851 

852 This method is similar to the :py:func:`~PIL.Image.frombytes` function, 

853 but loads data into this image instead of creating a new image object. 

854 """ 

855 

856 if self.width == 0 or self.height == 0: 

857 return 

858 

859 decoder_args: Any = args 

860 if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): 

861 # may pass tuple instead of argument list 

862 decoder_args = decoder_args[0] 

863 

864 # default format 

865 if decoder_name == "raw" and decoder_args == (): 

866 decoder_args = self.mode 

867 

868 # unpack data 

869 d = _getdecoder(self.mode, decoder_name, decoder_args) 

870 d.setimage(self.im) 

871 s = d.decode(data) 

872 

873 if s[0] >= 0: 

874 msg = "not enough image data" 

875 raise ValueError(msg) 

876 if s[1] != 0: 

877 msg = "cannot decode image data" 

878 raise ValueError(msg) 

879 

880 def load(self) -> core.PixelAccess | None: 

881 """ 

882 Allocates storage for the image and loads the pixel data. In 

883 normal cases, you don't need to call this method, since the 

884 Image class automatically loads an opened image when it is 

885 accessed for the first time. 

886 

887 If the file associated with the image was opened by Pillow, then this 

888 method will close it. The exception to this is if the image has 

889 multiple frames, in which case the file will be left open for seek 

890 operations. See :ref:`file-handling` for more information. 

891 

892 :returns: An image access object. 

893 :rtype: :py:class:`.PixelAccess` 

894 """ 

895 if self._im is not None and self.palette and self.palette.dirty: 

896 # realize palette 

897 mode, arr = self.palette.getdata() 

898 self.im.putpalette(self.palette.mode, mode, arr) 

899 self.palette.dirty = 0 

900 self.palette.rawmode = None 

901 if "transparency" in self.info and mode in ("LA", "PA"): 

902 if isinstance(self.info["transparency"], int): 

903 self.im.putpalettealpha(self.info["transparency"], 0) 

904 else: 

905 self.im.putpalettealphas(self.info["transparency"]) 

906 self.palette.mode = "RGBA" 

907 else: 

908 self.palette.palette = self.im.getpalette( 

909 self.palette.mode, self.palette.mode 

910 ) 

911 

912 if self._im is not None: 

913 return self.im.pixel_access(self.readonly) 

914 return None 

915 

916 def verify(self) -> None: 

917 """ 

918 Verifies the contents of a file. For data read from a file, this 

919 method attempts to determine if the file is broken, without 

920 actually decoding the image data. If this method finds any 

921 problems, it raises suitable exceptions. If you need to load 

922 the image after using this method, you must reopen the image 

923 file. 

924 """ 

925 pass 

926 

927 def convert( 

928 self, 

929 mode: str | None = None, 

930 matrix: tuple[float, ...] | None = None, 

931 dither: Dither | None = None, 

932 palette: Palette = Palette.WEB, 

933 colors: int = 256, 

934 ) -> Image: 

935 """ 

936 Returns a converted copy of this image. For the "P" mode, this 

937 method translates pixels through the palette. If mode is 

938 omitted, a mode is chosen so that all information in the image 

939 and the palette can be represented without a palette. 

940 

941 This supports all possible conversions between "L", "RGB" and "CMYK". The 

942 ``matrix`` argument only supports "L" and "RGB". 

943 

944 When translating a color image to grayscale (mode "L"), 

945 the library uses the ITU-R 601-2 luma transform:: 

946 

947 L = R * 299/1000 + G * 587/1000 + B * 114/1000 

948 

949 The default method of converting a grayscale ("L") or "RGB" 

950 image into a bilevel (mode "1") image uses Floyd-Steinberg 

951 dither to approximate the original image luminosity levels. If 

952 dither is ``None``, all values larger than 127 are set to 255 (white), 

953 all other values to 0 (black). To use other thresholds, use the 

954 :py:meth:`~PIL.Image.Image.point` method. 

955 

956 When converting from "RGBA" to "P" without a ``matrix`` argument, 

957 this passes the operation to :py:meth:`~PIL.Image.Image.quantize`, 

958 and ``dither`` and ``palette`` are ignored. 

959 

960 When converting from "PA", if an "RGBA" palette is present, the alpha 

961 channel from the image will be used instead of the values from the palette. 

962 

963 :param mode: The requested mode. See: :ref:`concept-modes`. 

964 :param matrix: An optional conversion matrix. If given, this 

965 should be 4- or 12-tuple containing floating point values. 

966 :param dither: Dithering method, used when converting from 

967 mode "RGB" to "P" or from "RGB" or "L" to "1". 

968 Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` 

969 (default). Note that this is not used when ``matrix`` is supplied. 

970 :param palette: Palette to use when converting from mode "RGB" 

971 to "P". Available palettes are :data:`Palette.WEB` or 

972 :data:`Palette.ADAPTIVE`. 

973 :param colors: Number of colors to use for the :data:`Palette.ADAPTIVE` 

974 palette. Defaults to 256. 

975 :rtype: :py:class:`~PIL.Image.Image` 

976 :returns: An :py:class:`~PIL.Image.Image` object. 

977 """ 

978 

979 if mode in ("BGR;15", "BGR;16", "BGR;24"): 

980 deprecate(mode, 12) 

981 

982 self.load() 

983 

984 has_transparency = "transparency" in self.info 

985 if not mode and self.mode == "P": 

986 # determine default mode 

987 if self.palette: 

988 mode = self.palette.mode 

989 else: 

990 mode = "RGB" 

991 if mode == "RGB" and has_transparency: 

992 mode = "RGBA" 

993 if not mode or (mode == self.mode and not matrix): 

994 return self.copy() 

995 

996 if matrix: 

997 # matrix conversion 

998 if mode not in ("L", "RGB"): 

999 msg = "illegal conversion" 

1000 raise ValueError(msg) 

1001 im = self.im.convert_matrix(mode, matrix) 

1002 new_im = self._new(im) 

1003 if has_transparency and self.im.bands == 3: 

1004 transparency = new_im.info["transparency"] 

1005 

1006 def convert_transparency( 

1007 m: tuple[float, ...], v: tuple[int, int, int] 

1008 ) -> int: 

1009 value = m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * 0.5 

1010 return max(0, min(255, int(value))) 

1011 

1012 if mode == "L": 

1013 transparency = convert_transparency(matrix, transparency) 

1014 elif len(mode) == 3: 

1015 transparency = tuple( 

1016 convert_transparency(matrix[i * 4 : i * 4 + 4], transparency) 

1017 for i in range(len(transparency)) 

1018 ) 

1019 new_im.info["transparency"] = transparency 

1020 return new_im 

1021 

1022 if mode == "P" and self.mode == "RGBA": 

1023 return self.quantize(colors) 

1024 

1025 trns = None 

1026 delete_trns = False 

1027 # transparency handling 

1028 if has_transparency: 

1029 if (self.mode in ("1", "L", "I", "I;16") and mode in ("LA", "RGBA")) or ( 

1030 self.mode == "RGB" and mode in ("La", "LA", "RGBa", "RGBA") 

1031 ): 

1032 # Use transparent conversion to promote from transparent 

1033 # color to an alpha channel. 

1034 new_im = self._new( 

1035 self.im.convert_transparent(mode, self.info["transparency"]) 

1036 ) 

1037 del new_im.info["transparency"] 

1038 return new_im 

1039 elif self.mode in ("L", "RGB", "P") and mode in ("L", "RGB", "P"): 

1040 t = self.info["transparency"] 

1041 if isinstance(t, bytes): 

1042 # Dragons. This can't be represented by a single color 

1043 warnings.warn( 

1044 "Palette images with Transparency expressed in bytes should be " 

1045 "converted to RGBA images" 

1046 ) 

1047 delete_trns = True 

1048 else: 

1049 # get the new transparency color. 

1050 # use existing conversions 

1051 trns_im = new(self.mode, (1, 1)) 

1052 if self.mode == "P": 

1053 assert self.palette is not None 

1054 trns_im.putpalette(self.palette, self.palette.mode) 

1055 if isinstance(t, tuple): 

1056 err = "Couldn't allocate a palette color for transparency" 

1057 assert trns_im.palette is not None 

1058 try: 

1059 t = trns_im.palette.getcolor(t, self) 

1060 except ValueError as e: 

1061 if str(e) == "cannot allocate more than 256 colors": 

1062 # If all 256 colors are in use, 

1063 # then there is no need for transparency 

1064 t = None 

1065 else: 

1066 raise ValueError(err) from e 

1067 if t is None: 

1068 trns = None 

1069 else: 

1070 trns_im.putpixel((0, 0), t) 

1071 

1072 if mode in ("L", "RGB"): 

1073 trns_im = trns_im.convert(mode) 

1074 else: 

1075 # can't just retrieve the palette number, got to do it 

1076 # after quantization. 

1077 trns_im = trns_im.convert("RGB") 

1078 trns = trns_im.getpixel((0, 0)) 

1079 

1080 elif self.mode == "P" and mode in ("LA", "PA", "RGBA"): 

1081 t = self.info["transparency"] 

1082 delete_trns = True 

1083 

1084 if isinstance(t, bytes): 

1085 self.im.putpalettealphas(t) 

1086 elif isinstance(t, int): 

1087 self.im.putpalettealpha(t, 0) 

1088 else: 

1089 msg = "Transparency for P mode should be bytes or int" 

1090 raise ValueError(msg) 

1091 

1092 if mode == "P" and palette == Palette.ADAPTIVE: 

1093 im = self.im.quantize(colors) 

1094 new_im = self._new(im) 

1095 from . import ImagePalette 

1096 

1097 new_im.palette = ImagePalette.ImagePalette( 

1098 "RGB", new_im.im.getpalette("RGB") 

1099 ) 

1100 if delete_trns: 

1101 # This could possibly happen if we requantize to fewer colors. 

1102 # The transparency would be totally off in that case. 

1103 del new_im.info["transparency"] 

1104 if trns is not None: 

1105 try: 

1106 new_im.info["transparency"] = new_im.palette.getcolor( 

1107 cast(tuple[int, ...], trns), # trns was converted to RGB 

1108 new_im, 

1109 ) 

1110 except Exception: 

1111 # if we can't make a transparent color, don't leave the old 

1112 # transparency hanging around to mess us up. 

1113 del new_im.info["transparency"] 

1114 warnings.warn("Couldn't allocate palette entry for transparency") 

1115 return new_im 

1116 

1117 if "LAB" in (self.mode, mode): 

1118 im = self 

1119 if mode == "LAB": 

1120 if im.mode not in ("RGB", "RGBA", "RGBX"): 

1121 im = im.convert("RGBA") 

1122 other_mode = im.mode 

1123 else: 

1124 other_mode = mode 

1125 if other_mode in ("RGB", "RGBA", "RGBX"): 

1126 from . import ImageCms 

1127 

1128 srgb = ImageCms.createProfile("sRGB") 

1129 lab = ImageCms.createProfile("LAB") 

1130 profiles = [lab, srgb] if im.mode == "LAB" else [srgb, lab] 

1131 transform = ImageCms.buildTransform( 

1132 profiles[0], profiles[1], im.mode, mode 

1133 ) 

1134 return transform.apply(im) 

1135 

1136 # colorspace conversion 

1137 if dither is None: 

1138 dither = Dither.FLOYDSTEINBERG 

1139 

1140 try: 

1141 im = self.im.convert(mode, dither) 

1142 except ValueError: 

1143 try: 

1144 # normalize source image and try again 

1145 modebase = getmodebase(self.mode) 

1146 if modebase == self.mode: 

1147 raise 

1148 im = self.im.convert(modebase) 

1149 im = im.convert(mode, dither) 

1150 except KeyError as e: 

1151 msg = "illegal conversion" 

1152 raise ValueError(msg) from e 

1153 

1154 new_im = self._new(im) 

1155 if mode == "P" and palette != Palette.ADAPTIVE: 

1156 from . import ImagePalette 

1157 

1158 new_im.palette = ImagePalette.ImagePalette("RGB", im.getpalette("RGB")) 

1159 if delete_trns: 

1160 # crash fail if we leave a bytes transparency in an rgb/l mode. 

1161 del new_im.info["transparency"] 

1162 if trns is not None: 

1163 if new_im.mode == "P" and new_im.palette: 

1164 try: 

1165 new_im.info["transparency"] = new_im.palette.getcolor( 

1166 cast(tuple[int, ...], trns), new_im # trns was converted to RGB 

1167 ) 

1168 except ValueError as e: 

1169 del new_im.info["transparency"] 

1170 if str(e) != "cannot allocate more than 256 colors": 

1171 # If all 256 colors are in use, 

1172 # then there is no need for transparency 

1173 warnings.warn( 

1174 "Couldn't allocate palette entry for transparency" 

1175 ) 

1176 else: 

1177 new_im.info["transparency"] = trns 

1178 return new_im 

1179 

1180 def quantize( 

1181 self, 

1182 colors: int = 256, 

1183 method: int | None = None, 

1184 kmeans: int = 0, 

1185 palette: Image | None = None, 

1186 dither: Dither = Dither.FLOYDSTEINBERG, 

1187 ) -> Image: 

1188 """ 

1189 Convert the image to 'P' mode with the specified number 

1190 of colors. 

1191 

1192 :param colors: The desired number of colors, <= 256 

1193 :param method: :data:`Quantize.MEDIANCUT` (median cut), 

1194 :data:`Quantize.MAXCOVERAGE` (maximum coverage), 

1195 :data:`Quantize.FASTOCTREE` (fast octree), 

1196 :data:`Quantize.LIBIMAGEQUANT` (libimagequant; check support 

1197 using :py:func:`PIL.features.check_feature` with 

1198 ``feature="libimagequant"``). 

1199 

1200 By default, :data:`Quantize.MEDIANCUT` will be used. 

1201 

1202 The exception to this is RGBA images. :data:`Quantize.MEDIANCUT` 

1203 and :data:`Quantize.MAXCOVERAGE` do not support RGBA images, so 

1204 :data:`Quantize.FASTOCTREE` is used by default instead. 

1205 :param kmeans: Integer greater than or equal to zero. 

1206 :param palette: Quantize to the palette of given 

1207 :py:class:`PIL.Image.Image`. 

1208 :param dither: Dithering method, used when converting from 

1209 mode "RGB" to "P" or from "RGB" or "L" to "1". 

1210 Available methods are :data:`Dither.NONE` or :data:`Dither.FLOYDSTEINBERG` 

1211 (default). 

1212 :returns: A new image 

1213 """ 

1214 

1215 self.load() 

1216 

1217 if method is None: 

1218 # defaults: 

1219 method = Quantize.MEDIANCUT 

1220 if self.mode == "RGBA": 

1221 method = Quantize.FASTOCTREE 

1222 

1223 if self.mode == "RGBA" and method not in ( 

1224 Quantize.FASTOCTREE, 

1225 Quantize.LIBIMAGEQUANT, 

1226 ): 

1227 # Caller specified an invalid mode. 

1228 msg = ( 

1229 "Fast Octree (method == 2) and libimagequant (method == 3) " 

1230 "are the only valid methods for quantizing RGBA images" 

1231 ) 

1232 raise ValueError(msg) 

1233 

1234 if palette: 

1235 # use palette from reference image 

1236 palette.load() 

1237 if palette.mode != "P": 

1238 msg = "bad mode for palette image" 

1239 raise ValueError(msg) 

1240 if self.mode not in {"RGB", "L"}: 

1241 msg = "only RGB or L mode images can be quantized to a palette" 

1242 raise ValueError(msg) 

1243 im = self.im.convert("P", dither, palette.im) 

1244 new_im = self._new(im) 

1245 assert palette.palette is not None 

1246 new_im.palette = palette.palette.copy() 

1247 return new_im 

1248 

1249 if kmeans < 0: 

1250 msg = "kmeans must not be negative" 

1251 raise ValueError(msg) 

1252 

1253 im = self._new(self.im.quantize(colors, method, kmeans)) 

1254 

1255 from . import ImagePalette 

1256 

1257 mode = im.im.getpalettemode() 

1258 palette_data = im.im.getpalette(mode, mode)[: colors * len(mode)] 

1259 im.palette = ImagePalette.ImagePalette(mode, palette_data) 

1260 

1261 return im 

1262 

1263 def copy(self) -> Image: 

1264 """ 

1265 Copies this image. Use this method if you wish to paste things 

1266 into an image, but still retain the original. 

1267 

1268 :rtype: :py:class:`~PIL.Image.Image` 

1269 :returns: An :py:class:`~PIL.Image.Image` object. 

1270 """ 

1271 self.load() 

1272 return self._new(self.im.copy()) 

1273 

1274 __copy__ = copy 

1275 

1276 def crop(self, box: tuple[float, float, float, float] | None = None) -> Image: 

1277 """ 

1278 Returns a rectangular region from this image. The box is a 

1279 4-tuple defining the left, upper, right, and lower pixel 

1280 coordinate. See :ref:`coordinate-system`. 

1281 

1282 Note: Prior to Pillow 3.4.0, this was a lazy operation. 

1283 

1284 :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. 

1285 :rtype: :py:class:`~PIL.Image.Image` 

1286 :returns: An :py:class:`~PIL.Image.Image` object. 

1287 """ 

1288 

1289 if box is None: 

1290 return self.copy() 

1291 

1292 if box[2] < box[0]: 

1293 msg = "Coordinate 'right' is less than 'left'" 

1294 raise ValueError(msg) 

1295 elif box[3] < box[1]: 

1296 msg = "Coordinate 'lower' is less than 'upper'" 

1297 raise ValueError(msg) 

1298 

1299 self.load() 

1300 return self._new(self._crop(self.im, box)) 

1301 

1302 def _crop( 

1303 self, im: core.ImagingCore, box: tuple[float, float, float, float] 

1304 ) -> core.ImagingCore: 

1305 """ 

1306 Returns a rectangular region from the core image object im. 

1307 

1308 This is equivalent to calling im.crop((x0, y0, x1, y1)), but 

1309 includes additional sanity checks. 

1310 

1311 :param im: a core image object 

1312 :param box: The crop rectangle, as a (left, upper, right, lower)-tuple. 

1313 :returns: A core image object. 

1314 """ 

1315 

1316 x0, y0, x1, y1 = map(int, map(round, box)) 

1317 

1318 absolute_values = (abs(x1 - x0), abs(y1 - y0)) 

1319 

1320 _decompression_bomb_check(absolute_values) 

1321 

1322 return im.crop((x0, y0, x1, y1)) 

1323 

1324 def draft( 

1325 self, mode: str | None, size: tuple[int, int] | None 

1326 ) -> tuple[str, tuple[int, int, float, float]] | None: 

1327 """ 

1328 Configures the image file loader so it returns a version of the 

1329 image that as closely as possible matches the given mode and 

1330 size. For example, you can use this method to convert a color 

1331 JPEG to grayscale while loading it. 

1332 

1333 If any changes are made, returns a tuple with the chosen ``mode`` and 

1334 ``box`` with coordinates of the original image within the altered one. 

1335 

1336 Note that this method modifies the :py:class:`~PIL.Image.Image` object 

1337 in place. If the image has already been loaded, this method has no 

1338 effect. 

1339 

1340 Note: This method is not implemented for most images. It is 

1341 currently implemented only for JPEG and MPO images. 

1342 

1343 :param mode: The requested mode. 

1344 :param size: The requested size in pixels, as a 2-tuple: 

1345 (width, height). 

1346 """ 

1347 pass 

1348 

1349 def _expand(self, xmargin: int, ymargin: int | None = None) -> Image: 

1350 if ymargin is None: 

1351 ymargin = xmargin 

1352 self.load() 

1353 return self._new(self.im.expand(xmargin, ymargin)) 

1354 

1355 def filter(self, filter: ImageFilter.Filter | type[ImageFilter.Filter]) -> Image: 

1356 """ 

1357 Filters this image using the given filter. For a list of 

1358 available filters, see the :py:mod:`~PIL.ImageFilter` module. 

1359 

1360 :param filter: Filter kernel. 

1361 :returns: An :py:class:`~PIL.Image.Image` object.""" 

1362 

1363 from . import ImageFilter 

1364 

1365 self.load() 

1366 

1367 if callable(filter): 

1368 filter = filter() 

1369 if not hasattr(filter, "filter"): 

1370 msg = "filter argument should be ImageFilter.Filter instance or class" 

1371 raise TypeError(msg) 

1372 

1373 multiband = isinstance(filter, ImageFilter.MultibandFilter) 

1374 if self.im.bands == 1 or multiband: 

1375 return self._new(filter.filter(self.im)) 

1376 

1377 ims = [ 

1378 self._new(filter.filter(self.im.getband(c))) for c in range(self.im.bands) 

1379 ] 

1380 return merge(self.mode, ims) 

1381 

1382 def getbands(self) -> tuple[str, ...]: 

1383 """ 

1384 Returns a tuple containing the name of each band in this image. 

1385 For example, ``getbands`` on an RGB image returns ("R", "G", "B"). 

1386 

1387 :returns: A tuple containing band names. 

1388 :rtype: tuple 

1389 """ 

1390 return ImageMode.getmode(self.mode).bands 

1391 

1392 def getbbox(self, *, alpha_only: bool = True) -> tuple[int, int, int, int] | None: 

1393 """ 

1394 Calculates the bounding box of the non-zero regions in the 

1395 image. 

1396 

1397 :param alpha_only: Optional flag, defaulting to ``True``. 

1398 If ``True`` and the image has an alpha channel, trim transparent pixels. 

1399 Otherwise, trim pixels when all channels are zero. 

1400 Keyword-only argument. 

1401 :returns: The bounding box is returned as a 4-tuple defining the 

1402 left, upper, right, and lower pixel coordinate. See 

1403 :ref:`coordinate-system`. If the image is completely empty, this 

1404 method returns None. 

1405 

1406 """ 

1407 

1408 self.load() 

1409 return self.im.getbbox(alpha_only) 

1410 

1411 def getcolors( 

1412 self, maxcolors: int = 256 

1413 ) -> list[tuple[int, tuple[int, ...]]] | list[tuple[int, float]] | None: 

1414 """ 

1415 Returns a list of colors used in this image. 

1416 

1417 The colors will be in the image's mode. For example, an RGB image will 

1418 return a tuple of (red, green, blue) color values, and a P image will 

1419 return the index of the color in the palette. 

1420 

1421 :param maxcolors: Maximum number of colors. If this number is 

1422 exceeded, this method returns None. The default limit is 

1423 256 colors. 

1424 :returns: An unsorted list of (count, pixel) values. 

1425 """ 

1426 

1427 self.load() 

1428 if self.mode in ("1", "L", "P"): 

1429 h = self.im.histogram() 

1430 out: list[tuple[int, float]] = [(h[i], i) for i in range(256) if h[i]] 

1431 if len(out) > maxcolors: 

1432 return None 

1433 return out 

1434 return self.im.getcolors(maxcolors) 

1435 

1436 def getdata(self, band: int | None = None) -> core.ImagingCore: 

1437 """ 

1438 Returns the contents of this image as a sequence object 

1439 containing pixel values. The sequence object is flattened, so 

1440 that values for line one follow directly after the values of 

1441 line zero, and so on. 

1442 

1443 Note that the sequence object returned by this method is an 

1444 internal PIL data type, which only supports certain sequence 

1445 operations. To convert it to an ordinary sequence (e.g. for 

1446 printing), use ``list(im.getdata())``. 

1447 

1448 :param band: What band to return. The default is to return 

1449 all bands. To return a single band, pass in the index 

1450 value (e.g. 0 to get the "R" band from an "RGB" image). 

1451 :returns: A sequence-like object. 

1452 """ 

1453 

1454 self.load() 

1455 if band is not None: 

1456 return self.im.getband(band) 

1457 return self.im # could be abused 

1458 

1459 def getextrema(self) -> tuple[float, float] | tuple[tuple[int, int], ...]: 

1460 """ 

1461 Gets the minimum and maximum pixel values for each band in 

1462 the image. 

1463 

1464 :returns: For a single-band image, a 2-tuple containing the 

1465 minimum and maximum pixel value. For a multi-band image, 

1466 a tuple containing one 2-tuple for each band. 

1467 """ 

1468 

1469 self.load() 

1470 if self.im.bands > 1: 

1471 return tuple(self.im.getband(i).getextrema() for i in range(self.im.bands)) 

1472 return self.im.getextrema() 

1473 

1474 def getxmp(self) -> dict[str, Any]: 

1475 """ 

1476 Returns a dictionary containing the XMP tags. 

1477 Requires defusedxml to be installed. 

1478 

1479 :returns: XMP tags in a dictionary. 

1480 """ 

1481 

1482 def get_name(tag: str) -> str: 

1483 return re.sub("^{[^}]+}", "", tag) 

1484 

1485 def get_value(element: Element) -> str | dict[str, Any] | None: 

1486 value: dict[str, Any] = {get_name(k): v for k, v in element.attrib.items()} 

1487 children = list(element) 

1488 if children: 

1489 for child in children: 

1490 name = get_name(child.tag) 

1491 child_value = get_value(child) 

1492 if name in value: 

1493 if not isinstance(value[name], list): 

1494 value[name] = [value[name]] 

1495 value[name].append(child_value) 

1496 else: 

1497 value[name] = child_value 

1498 elif value: 

1499 if element.text: 

1500 value["text"] = element.text 

1501 else: 

1502 return element.text 

1503 return value 

1504 

1505 if ElementTree is None: 

1506 warnings.warn("XMP data cannot be read without defusedxml dependency") 

1507 return {} 

1508 if "xmp" not in self.info: 

1509 return {} 

1510 root = ElementTree.fromstring(self.info["xmp"].rstrip(b"\x00")) 

1511 return {get_name(root.tag): get_value(root)} 

1512 

1513 def getexif(self) -> Exif: 

1514 """ 

1515 Gets EXIF data from the image. 

1516 

1517 :returns: an :py:class:`~PIL.Image.Exif` object. 

1518 """ 

1519 if self._exif is None: 

1520 self._exif = Exif() 

1521 elif self._exif._loaded: 

1522 return self._exif 

1523 self._exif._loaded = True 

1524 

1525 exif_info = self.info.get("exif") 

1526 if exif_info is None: 

1527 if "Raw profile type exif" in self.info: 

1528 exif_info = bytes.fromhex( 

1529 "".join(self.info["Raw profile type exif"].split("\n")[3:]) 

1530 ) 

1531 elif hasattr(self, "tag_v2"): 

1532 self._exif.bigtiff = self.tag_v2._bigtiff 

1533 self._exif.endian = self.tag_v2._endian 

1534 self._exif.load_from_fp(self.fp, self.tag_v2._offset) 

1535 if exif_info is not None: 

1536 self._exif.load(exif_info) 

1537 

1538 # XMP tags 

1539 if ExifTags.Base.Orientation not in self._exif: 

1540 xmp_tags = self.info.get("XML:com.adobe.xmp") 

1541 if not xmp_tags and (xmp_tags := self.info.get("xmp")): 

1542 xmp_tags = xmp_tags.decode("utf-8") 

1543 if xmp_tags: 

1544 match = re.search(r'tiff:Orientation(="|>)([0-9])', xmp_tags) 

1545 if match: 

1546 self._exif[ExifTags.Base.Orientation] = int(match[2]) 

1547 

1548 return self._exif 

1549 

1550 def _reload_exif(self) -> None: 

1551 if self._exif is None or not self._exif._loaded: 

1552 return 

1553 self._exif._loaded = False 

1554 self.getexif() 

1555 

1556 def get_child_images(self) -> list[ImageFile.ImageFile]: 

1557 from . import ImageFile 

1558 

1559 deprecate("Image.Image.get_child_images", 13) 

1560 return ImageFile.ImageFile.get_child_images(self) # type: ignore[arg-type] 

1561 

1562 def getim(self) -> CapsuleType: 

1563 """ 

1564 Returns a capsule that points to the internal image memory. 

1565 

1566 :returns: A capsule object. 

1567 """ 

1568 

1569 self.load() 

1570 return self.im.ptr 

1571 

1572 def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: 

1573 """ 

1574 Returns the image palette as a list. 

1575 

1576 :param rawmode: The mode in which to return the palette. ``None`` will 

1577 return the palette in its current mode. 

1578 

1579 .. versionadded:: 9.1.0 

1580 

1581 :returns: A list of color values [r, g, b, ...], or None if the 

1582 image has no palette. 

1583 """ 

1584 

1585 self.load() 

1586 try: 

1587 mode = self.im.getpalettemode() 

1588 except ValueError: 

1589 return None # no palette 

1590 if rawmode is None: 

1591 rawmode = mode 

1592 return list(self.im.getpalette(mode, rawmode)) 

1593 

1594 @property 

1595 def has_transparency_data(self) -> bool: 

1596 """ 

1597 Determine if an image has transparency data, whether in the form of an 

1598 alpha channel, a palette with an alpha channel, or a "transparency" key 

1599 in the info dictionary. 

1600 

1601 Note the image might still appear solid, if all of the values shown 

1602 within are opaque. 

1603 

1604 :returns: A boolean. 

1605 """ 

1606 if ( 

1607 self.mode in ("LA", "La", "PA", "RGBA", "RGBa") 

1608 or "transparency" in self.info 

1609 ): 

1610 return True 

1611 if self.mode == "P": 

1612 assert self.palette is not None 

1613 return self.palette.mode.endswith("A") 

1614 return False 

1615 

1616 def apply_transparency(self) -> None: 

1617 """ 

1618 If a P mode image has a "transparency" key in the info dictionary, 

1619 remove the key and instead apply the transparency to the palette. 

1620 Otherwise, the image is unchanged. 

1621 """ 

1622 if self.mode != "P" or "transparency" not in self.info: 

1623 return 

1624 

1625 from . import ImagePalette 

1626 

1627 palette = self.getpalette("RGBA") 

1628 assert palette is not None 

1629 transparency = self.info["transparency"] 

1630 if isinstance(transparency, bytes): 

1631 for i, alpha in enumerate(transparency): 

1632 palette[i * 4 + 3] = alpha 

1633 else: 

1634 palette[transparency * 4 + 3] = 0 

1635 self.palette = ImagePalette.ImagePalette("RGBA", bytes(palette)) 

1636 self.palette.dirty = 1 

1637 

1638 del self.info["transparency"] 

1639 

1640 def getpixel( 

1641 self, xy: tuple[int, int] | list[int] 

1642 ) -> float | tuple[int, ...] | None: 

1643 """ 

1644 Returns the pixel value at a given position. 

1645 

1646 :param xy: The coordinate, given as (x, y). See 

1647 :ref:`coordinate-system`. 

1648 :returns: The pixel value. If the image is a multi-layer image, 

1649 this method returns a tuple. 

1650 """ 

1651 

1652 self.load() 

1653 return self.im.getpixel(tuple(xy)) 

1654 

1655 def getprojection(self) -> tuple[list[int], list[int]]: 

1656 """ 

1657 Get projection to x and y axes 

1658 

1659 :returns: Two sequences, indicating where there are non-zero 

1660 pixels along the X-axis and the Y-axis, respectively. 

1661 """ 

1662 

1663 self.load() 

1664 x, y = self.im.getprojection() 

1665 return list(x), list(y) 

1666 

1667 def histogram( 

1668 self, mask: Image | None = None, extrema: tuple[float, float] | None = None 

1669 ) -> list[int]: 

1670 """ 

1671 Returns a histogram for the image. The histogram is returned as a 

1672 list of pixel counts, one for each pixel value in the source 

1673 image. Counts are grouped into 256 bins for each band, even if 

1674 the image has more than 8 bits per band. If the image has more 

1675 than one band, the histograms for all bands are concatenated (for 

1676 example, the histogram for an "RGB" image contains 768 values). 

1677 

1678 A bilevel image (mode "1") is treated as a grayscale ("L") image 

1679 by this method. 

1680 

1681 If a mask is provided, the method returns a histogram for those 

1682 parts of the image where the mask image is non-zero. The mask 

1683 image must have the same size as the image, and be either a 

1684 bi-level image (mode "1") or a grayscale image ("L"). 

1685 

1686 :param mask: An optional mask. 

1687 :param extrema: An optional tuple of manually-specified extrema. 

1688 :returns: A list containing pixel counts. 

1689 """ 

1690 self.load() 

1691 if mask: 

1692 mask.load() 

1693 return self.im.histogram((0, 0), mask.im) 

1694 if self.mode in ("I", "F"): 

1695 return self.im.histogram( 

1696 extrema if extrema is not None else self.getextrema() 

1697 ) 

1698 return self.im.histogram() 

1699 

1700 def entropy( 

1701 self, mask: Image | None = None, extrema: tuple[float, float] | None = None 

1702 ) -> float: 

1703 """ 

1704 Calculates and returns the entropy for the image. 

1705 

1706 A bilevel image (mode "1") is treated as a grayscale ("L") 

1707 image by this method. 

1708 

1709 If a mask is provided, the method employs the histogram for 

1710 those parts of the image where the mask image is non-zero. 

1711 The mask image must have the same size as the image, and be 

1712 either a bi-level image (mode "1") or a grayscale image ("L"). 

1713 

1714 :param mask: An optional mask. 

1715 :param extrema: An optional tuple of manually-specified extrema. 

1716 :returns: A float value representing the image entropy 

1717 """ 

1718 self.load() 

1719 if mask: 

1720 mask.load() 

1721 return self.im.entropy((0, 0), mask.im) 

1722 if self.mode in ("I", "F"): 

1723 return self.im.entropy( 

1724 extrema if extrema is not None else self.getextrema() 

1725 ) 

1726 return self.im.entropy() 

1727 

1728 def paste( 

1729 self, 

1730 im: Image | str | float | tuple[float, ...], 

1731 box: Image | tuple[int, int, int, int] | tuple[int, int] | None = None, 

1732 mask: Image | None = None, 

1733 ) -> None: 

1734 """ 

1735 Pastes another image into this image. The box argument is either 

1736 a 2-tuple giving the upper left corner, a 4-tuple defining the 

1737 left, upper, right, and lower pixel coordinate, or None (same as 

1738 (0, 0)). See :ref:`coordinate-system`. If a 4-tuple is given, the size 

1739 of the pasted image must match the size of the region. 

1740 

1741 If the modes don't match, the pasted image is converted to the mode of 

1742 this image (see the :py:meth:`~PIL.Image.Image.convert` method for 

1743 details). 

1744 

1745 Instead of an image, the source can be a integer or tuple 

1746 containing pixel values. The method then fills the region 

1747 with the given color. When creating RGB images, you can 

1748 also use color strings as supported by the ImageColor module. 

1749 

1750 If a mask is given, this method updates only the regions 

1751 indicated by the mask. You can use either "1", "L", "LA", "RGBA" 

1752 or "RGBa" images (if present, the alpha band is used as mask). 

1753 Where the mask is 255, the given image is copied as is. Where 

1754 the mask is 0, the current value is preserved. Intermediate 

1755 values will mix the two images together, including their alpha 

1756 channels if they have them. 

1757 

1758 See :py:meth:`~PIL.Image.Image.alpha_composite` if you want to 

1759 combine images with respect to their alpha channels. 

1760 

1761 :param im: Source image or pixel value (integer, float or tuple). 

1762 :param box: An optional 4-tuple giving the region to paste into. 

1763 If a 2-tuple is used instead, it's treated as the upper left 

1764 corner. If omitted or None, the source is pasted into the 

1765 upper left corner. 

1766 

1767 If an image is given as the second argument and there is no 

1768 third, the box defaults to (0, 0), and the second argument 

1769 is interpreted as a mask image. 

1770 :param mask: An optional mask image. 

1771 """ 

1772 

1773 if isinstance(box, Image): 

1774 if mask is not None: 

1775 msg = "If using second argument as mask, third argument must be None" 

1776 raise ValueError(msg) 

1777 # abbreviated paste(im, mask) syntax 

1778 mask = box 

1779 box = None 

1780 

1781 if box is None: 

1782 box = (0, 0) 

1783 

1784 if len(box) == 2: 

1785 # upper left corner given; get size from image or mask 

1786 if isinstance(im, Image): 

1787 size = im.size 

1788 elif isinstance(mask, Image): 

1789 size = mask.size 

1790 else: 

1791 # FIXME: use self.size here? 

1792 msg = "cannot determine region size; use 4-item box" 

1793 raise ValueError(msg) 

1794 box += (box[0] + size[0], box[1] + size[1]) 

1795 

1796 source: core.ImagingCore | str | float | tuple[float, ...] 

1797 if isinstance(im, str): 

1798 from . import ImageColor 

1799 

1800 source = ImageColor.getcolor(im, self.mode) 

1801 elif isinstance(im, Image): 

1802 im.load() 

1803 if self.mode != im.mode: 

1804 if self.mode != "RGB" or im.mode not in ("LA", "RGBA", "RGBa"): 

1805 # should use an adapter for this! 

1806 im = im.convert(self.mode) 

1807 source = im.im 

1808 else: 

1809 source = im 

1810 

1811 self._ensure_mutable() 

1812 

1813 if mask: 

1814 mask.load() 

1815 self.im.paste(source, box, mask.im) 

1816 else: 

1817 self.im.paste(source, box) 

1818 

1819 def alpha_composite( 

1820 self, im: Image, dest: Sequence[int] = (0, 0), source: Sequence[int] = (0, 0) 

1821 ) -> None: 

1822 """'In-place' analog of Image.alpha_composite. Composites an image 

1823 onto this image. 

1824 

1825 :param im: image to composite over this one 

1826 :param dest: Optional 2 tuple (left, top) specifying the upper 

1827 left corner in this (destination) image. 

1828 :param source: Optional 2 (left, top) tuple for the upper left 

1829 corner in the overlay source image, or 4 tuple (left, top, right, 

1830 bottom) for the bounds of the source rectangle 

1831 

1832 Performance Note: Not currently implemented in-place in the core layer. 

1833 """ 

1834 

1835 if not isinstance(source, (list, tuple)): 

1836 msg = "Source must be a list or tuple" 

1837 raise ValueError(msg) 

1838 if not isinstance(dest, (list, tuple)): 

1839 msg = "Destination must be a list or tuple" 

1840 raise ValueError(msg) 

1841 

1842 if len(source) == 4: 

1843 overlay_crop_box = tuple(source) 

1844 elif len(source) == 2: 

1845 overlay_crop_box = tuple(source) + im.size 

1846 else: 

1847 msg = "Source must be a sequence of length 2 or 4" 

1848 raise ValueError(msg) 

1849 

1850 if not len(dest) == 2: 

1851 msg = "Destination must be a sequence of length 2" 

1852 raise ValueError(msg) 

1853 if min(source) < 0: 

1854 msg = "Source must be non-negative" 

1855 raise ValueError(msg) 

1856 

1857 # over image, crop if it's not the whole image. 

1858 if overlay_crop_box == (0, 0) + im.size: 

1859 overlay = im 

1860 else: 

1861 overlay = im.crop(overlay_crop_box) 

1862 

1863 # target for the paste 

1864 box = tuple(dest) + (dest[0] + overlay.width, dest[1] + overlay.height) 

1865 

1866 # destination image. don't copy if we're using the whole image. 

1867 if box == (0, 0) + self.size: 

1868 background = self 

1869 else: 

1870 background = self.crop(box) 

1871 

1872 result = alpha_composite(background, overlay) 

1873 self.paste(result, box) 

1874 

1875 def point( 

1876 self, 

1877 lut: ( 

1878 Sequence[float] 

1879 | NumpyArray 

1880 | Callable[[int], float] 

1881 | Callable[[ImagePointTransform], ImagePointTransform | float] 

1882 | ImagePointHandler 

1883 ), 

1884 mode: str | None = None, 

1885 ) -> Image: 

1886 """ 

1887 Maps this image through a lookup table or function. 

1888 

1889 :param lut: A lookup table, containing 256 (or 65536 if 

1890 self.mode=="I" and mode == "L") values per band in the 

1891 image. A function can be used instead, it should take a 

1892 single argument. The function is called once for each 

1893 possible pixel value, and the resulting table is applied to 

1894 all bands of the image. 

1895 

1896 It may also be an :py:class:`~PIL.Image.ImagePointHandler` 

1897 object:: 

1898 

1899 class Example(Image.ImagePointHandler): 

1900 def point(self, im: Image) -> Image: 

1901 # Return result 

1902 :param mode: Output mode (default is same as input). This can only be used if 

1903 the source image has mode "L" or "P", and the output has mode "1" or the 

1904 source image mode is "I" and the output mode is "L". 

1905 :returns: An :py:class:`~PIL.Image.Image` object. 

1906 """ 

1907 

1908 self.load() 

1909 

1910 if isinstance(lut, ImagePointHandler): 

1911 return lut.point(self) 

1912 

1913 if callable(lut): 

1914 # if it isn't a list, it should be a function 

1915 if self.mode in ("I", "I;16", "F"): 

1916 # check if the function can be used with point_transform 

1917 # UNDONE wiredfool -- I think this prevents us from ever doing 

1918 # a gamma function point transform on > 8bit images. 

1919 scale, offset = _getscaleoffset(lut) # type: ignore[arg-type] 

1920 return self._new(self.im.point_transform(scale, offset)) 

1921 # for other modes, convert the function to a table 

1922 flatLut = [lut(i) for i in range(256)] * self.im.bands # type: ignore[arg-type] 

1923 else: 

1924 flatLut = lut 

1925 

1926 if self.mode == "F": 

1927 # FIXME: _imaging returns a confusing error message for this case 

1928 msg = "point operation not supported for this mode" 

1929 raise ValueError(msg) 

1930 

1931 if mode != "F": 

1932 flatLut = [round(i) for i in flatLut] 

1933 return self._new(self.im.point(flatLut, mode)) 

1934 

1935 def putalpha(self, alpha: Image | int) -> None: 

1936 """ 

1937 Adds or replaces the alpha layer in this image. If the image 

1938 does not have an alpha layer, it's converted to "LA" or "RGBA". 

1939 The new layer must be either "L" or "1". 

1940 

1941 :param alpha: The new alpha layer. This can either be an "L" or "1" 

1942 image having the same size as this image, or an integer. 

1943 """ 

1944 

1945 self._ensure_mutable() 

1946 

1947 if self.mode not in ("LA", "PA", "RGBA"): 

1948 # attempt to promote self to a matching alpha mode 

1949 try: 

1950 mode = getmodebase(self.mode) + "A" 

1951 try: 

1952 self.im.setmode(mode) 

1953 except (AttributeError, ValueError) as e: 

1954 # do things the hard way 

1955 im = self.im.convert(mode) 

1956 if im.mode not in ("LA", "PA", "RGBA"): 

1957 msg = "alpha channel could not be added" 

1958 raise ValueError(msg) from e # sanity check 

1959 self.im = im 

1960 self._mode = self.im.mode 

1961 except KeyError as e: 

1962 msg = "illegal image mode" 

1963 raise ValueError(msg) from e 

1964 

1965 if self.mode in ("LA", "PA"): 

1966 band = 1 

1967 else: 

1968 band = 3 

1969 

1970 if isinstance(alpha, Image): 

1971 # alpha layer 

1972 if alpha.mode not in ("1", "L"): 

1973 msg = "illegal image mode" 

1974 raise ValueError(msg) 

1975 alpha.load() 

1976 if alpha.mode == "1": 

1977 alpha = alpha.convert("L") 

1978 else: 

1979 # constant alpha 

1980 try: 

1981 self.im.fillband(band, alpha) 

1982 except (AttributeError, ValueError): 

1983 # do things the hard way 

1984 alpha = new("L", self.size, alpha) 

1985 else: 

1986 return 

1987 

1988 self.im.putband(alpha.im, band) 

1989 

1990 def putdata( 

1991 self, 

1992 data: Sequence[float] | Sequence[Sequence[int]] | core.ImagingCore | NumpyArray, 

1993 scale: float = 1.0, 

1994 offset: float = 0.0, 

1995 ) -> None: 

1996 """ 

1997 Copies pixel data from a flattened sequence object into the image. The 

1998 values should start at the upper left corner (0, 0), continue to the 

1999 end of the line, followed directly by the first value of the second 

2000 line, and so on. Data will be read until either the image or the 

2001 sequence ends. The scale and offset values are used to adjust the 

2002 sequence values: **pixel = value*scale + offset**. 

2003 

2004 :param data: A flattened sequence object. 

2005 :param scale: An optional scale value. The default is 1.0. 

2006 :param offset: An optional offset value. The default is 0.0. 

2007 """ 

2008 

2009 self._ensure_mutable() 

2010 

2011 self.im.putdata(data, scale, offset) 

2012 

2013 def putpalette( 

2014 self, 

2015 data: ImagePalette.ImagePalette | bytes | Sequence[int], 

2016 rawmode: str = "RGB", 

2017 ) -> None: 

2018 """ 

2019 Attaches a palette to this image. The image must be a "P", "PA", "L" 

2020 or "LA" image. 

2021 

2022 The palette sequence must contain at most 256 colors, made up of one 

2023 integer value for each channel in the raw mode. 

2024 For example, if the raw mode is "RGB", then it can contain at most 768 

2025 values, made up of red, green and blue values for the corresponding pixel 

2026 index in the 256 colors. 

2027 If the raw mode is "RGBA", then it can contain at most 1024 values, 

2028 containing red, green, blue and alpha values. 

2029 

2030 Alternatively, an 8-bit string may be used instead of an integer sequence. 

2031 

2032 :param data: A palette sequence (either a list or a string). 

2033 :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode 

2034 that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). 

2035 """ 

2036 from . import ImagePalette 

2037 

2038 if self.mode not in ("L", "LA", "P", "PA"): 

2039 msg = "illegal image mode" 

2040 raise ValueError(msg) 

2041 if isinstance(data, ImagePalette.ImagePalette): 

2042 if data.rawmode is not None: 

2043 palette = ImagePalette.raw(data.rawmode, data.palette) 

2044 else: 

2045 palette = ImagePalette.ImagePalette(palette=data.palette) 

2046 palette.dirty = 1 

2047 else: 

2048 if not isinstance(data, bytes): 

2049 data = bytes(data) 

2050 palette = ImagePalette.raw(rawmode, data) 

2051 self._mode = "PA" if "A" in self.mode else "P" 

2052 self.palette = palette 

2053 self.palette.mode = "RGBA" if "A" in rawmode else "RGB" 

2054 self.load() # install new palette 

2055 

2056 def putpixel( 

2057 self, xy: tuple[int, int], value: float | tuple[int, ...] | list[int] 

2058 ) -> None: 

2059 """ 

2060 Modifies the pixel at the given position. The color is given as 

2061 a single numerical value for single-band images, and a tuple for 

2062 multi-band images. In addition to this, RGB and RGBA tuples are 

2063 accepted for P and PA images. 

2064 

2065 Note that this method is relatively slow. For more extensive changes, 

2066 use :py:meth:`~PIL.Image.Image.paste` or the :py:mod:`~PIL.ImageDraw` 

2067 module instead. 

2068 

2069 See: 

2070 

2071 * :py:meth:`~PIL.Image.Image.paste` 

2072 * :py:meth:`~PIL.Image.Image.putdata` 

2073 * :py:mod:`~PIL.ImageDraw` 

2074 

2075 :param xy: The pixel coordinate, given as (x, y). See 

2076 :ref:`coordinate-system`. 

2077 :param value: The pixel value. 

2078 """ 

2079 

2080 if self.readonly: 

2081 self._copy() 

2082 self.load() 

2083 

2084 if ( 

2085 self.mode in ("P", "PA") 

2086 and isinstance(value, (list, tuple)) 

2087 and len(value) in [3, 4] 

2088 ): 

2089 # RGB or RGBA value for a P or PA image 

2090 if self.mode == "PA": 

2091 alpha = value[3] if len(value) == 4 else 255 

2092 value = value[:3] 

2093 assert self.palette is not None 

2094 palette_index = self.palette.getcolor(tuple(value), self) 

2095 value = (palette_index, alpha) if self.mode == "PA" else palette_index 

2096 return self.im.putpixel(xy, value) 

2097 

2098 def remap_palette( 

2099 self, dest_map: list[int], source_palette: bytes | bytearray | None = None 

2100 ) -> Image: 

2101 """ 

2102 Rewrites the image to reorder the palette. 

2103 

2104 :param dest_map: A list of indexes into the original palette. 

2105 e.g. ``[1,0]`` would swap a two item palette, and ``list(range(256))`` 

2106 is the identity transform. 

2107 :param source_palette: Bytes or None. 

2108 :returns: An :py:class:`~PIL.Image.Image` object. 

2109 

2110 """ 

2111 from . import ImagePalette 

2112 

2113 if self.mode not in ("L", "P"): 

2114 msg = "illegal image mode" 

2115 raise ValueError(msg) 

2116 

2117 bands = 3 

2118 palette_mode = "RGB" 

2119 if source_palette is None: 

2120 if self.mode == "P": 

2121 self.load() 

2122 palette_mode = self.im.getpalettemode() 

2123 if palette_mode == "RGBA": 

2124 bands = 4 

2125 source_palette = self.im.getpalette(palette_mode, palette_mode) 

2126 else: # L-mode 

2127 source_palette = bytearray(i // 3 for i in range(768)) 

2128 elif len(source_palette) > 768: 

2129 bands = 4 

2130 palette_mode = "RGBA" 

2131 

2132 palette_bytes = b"" 

2133 new_positions = [0] * 256 

2134 

2135 # pick only the used colors from the palette 

2136 for i, oldPosition in enumerate(dest_map): 

2137 palette_bytes += source_palette[ 

2138 oldPosition * bands : oldPosition * bands + bands 

2139 ] 

2140 new_positions[oldPosition] = i 

2141 

2142 # replace the palette color id of all pixel with the new id 

2143 

2144 # Palette images are [0..255], mapped through a 1 or 3 

2145 # byte/color map. We need to remap the whole image 

2146 # from palette 1 to palette 2. New_positions is 

2147 # an array of indexes into palette 1. Palette 2 is 

2148 # palette 1 with any holes removed. 

2149 

2150 # We're going to leverage the convert mechanism to use the 

2151 # C code to remap the image from palette 1 to palette 2, 

2152 # by forcing the source image into 'L' mode and adding a 

2153 # mapping 'L' mode palette, then converting back to 'L' 

2154 # sans palette thus converting the image bytes, then 

2155 # assigning the optimized RGB palette. 

2156 

2157 # perf reference, 9500x4000 gif, w/~135 colors 

2158 # 14 sec prepatch, 1 sec postpatch with optimization forced. 

2159 

2160 mapping_palette = bytearray(new_positions) 

2161 

2162 m_im = self.copy() 

2163 m_im._mode = "P" 

2164 

2165 m_im.palette = ImagePalette.ImagePalette( 

2166 palette_mode, palette=mapping_palette * bands 

2167 ) 

2168 # possibly set palette dirty, then 

2169 # m_im.putpalette(mapping_palette, 'L') # converts to 'P' 

2170 # or just force it. 

2171 # UNDONE -- this is part of the general issue with palettes 

2172 m_im.im.putpalette(palette_mode, palette_mode + ";L", m_im.palette.tobytes()) 

2173 

2174 m_im = m_im.convert("L") 

2175 

2176 m_im.putpalette(palette_bytes, palette_mode) 

2177 m_im.palette = ImagePalette.ImagePalette(palette_mode, palette=palette_bytes) 

2178 

2179 if "transparency" in self.info: 

2180 try: 

2181 m_im.info["transparency"] = dest_map.index(self.info["transparency"]) 

2182 except ValueError: 

2183 if "transparency" in m_im.info: 

2184 del m_im.info["transparency"] 

2185 

2186 return m_im 

2187 

2188 def _get_safe_box( 

2189 self, 

2190 size: tuple[int, int], 

2191 resample: Resampling, 

2192 box: tuple[float, float, float, float], 

2193 ) -> tuple[int, int, int, int]: 

2194 """Expands the box so it includes adjacent pixels 

2195 that may be used by resampling with the given resampling filter. 

2196 """ 

2197 filter_support = _filters_support[resample] - 0.5 

2198 scale_x = (box[2] - box[0]) / size[0] 

2199 scale_y = (box[3] - box[1]) / size[1] 

2200 support_x = filter_support * scale_x 

2201 support_y = filter_support * scale_y 

2202 

2203 return ( 

2204 max(0, int(box[0] - support_x)), 

2205 max(0, int(box[1] - support_y)), 

2206 min(self.size[0], math.ceil(box[2] + support_x)), 

2207 min(self.size[1], math.ceil(box[3] + support_y)), 

2208 ) 

2209 

2210 def resize( 

2211 self, 

2212 size: tuple[int, int] | list[int] | NumpyArray, 

2213 resample: int | None = None, 

2214 box: tuple[float, float, float, float] | None = None, 

2215 reducing_gap: float | None = None, 

2216 ) -> Image: 

2217 """ 

2218 Returns a resized copy of this image. 

2219 

2220 :param size: The requested size in pixels, as a tuple or array: 

2221 (width, height). 

2222 :param resample: An optional resampling filter. This can be 

2223 one of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, 

2224 :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, 

2225 :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. 

2226 If the image has mode "1" or "P", it is always set to 

2227 :py:data:`Resampling.NEAREST`. If the image mode is "BGR;15", 

2228 "BGR;16" or "BGR;24", then the default filter is 

2229 :py:data:`Resampling.NEAREST`. Otherwise, the default filter is 

2230 :py:data:`Resampling.BICUBIC`. See: :ref:`concept-filters`. 

2231 :param box: An optional 4-tuple of floats providing 

2232 the source image region to be scaled. 

2233 The values must be within (0, 0, width, height) rectangle. 

2234 If omitted or None, the entire source is used. 

2235 :param reducing_gap: Apply optimization by resizing the image 

2236 in two steps. First, reducing the image by integer times 

2237 using :py:meth:`~PIL.Image.Image.reduce`. 

2238 Second, resizing using regular resampling. The last step 

2239 changes size no less than by ``reducing_gap`` times. 

2240 ``reducing_gap`` may be None (no first step is performed) 

2241 or should be greater than 1.0. The bigger ``reducing_gap``, 

2242 the closer the result to the fair resampling. 

2243 The smaller ``reducing_gap``, the faster resizing. 

2244 With ``reducing_gap`` greater or equal to 3.0, the result is 

2245 indistinguishable from fair resampling in most cases. 

2246 The default value is None (no optimization). 

2247 :returns: An :py:class:`~PIL.Image.Image` object. 

2248 """ 

2249 

2250 if resample is None: 

2251 bgr = self.mode.startswith("BGR;") 

2252 resample = Resampling.NEAREST if bgr else Resampling.BICUBIC 

2253 elif resample not in ( 

2254 Resampling.NEAREST, 

2255 Resampling.BILINEAR, 

2256 Resampling.BICUBIC, 

2257 Resampling.LANCZOS, 

2258 Resampling.BOX, 

2259 Resampling.HAMMING, 

2260 ): 

2261 msg = f"Unknown resampling filter ({resample})." 

2262 

2263 filters = [ 

2264 f"{filter[1]} ({filter[0]})" 

2265 for filter in ( 

2266 (Resampling.NEAREST, "Image.Resampling.NEAREST"), 

2267 (Resampling.LANCZOS, "Image.Resampling.LANCZOS"), 

2268 (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), 

2269 (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), 

2270 (Resampling.BOX, "Image.Resampling.BOX"), 

2271 (Resampling.HAMMING, "Image.Resampling.HAMMING"), 

2272 ) 

2273 ] 

2274 msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" 

2275 raise ValueError(msg) 

2276 

2277 if reducing_gap is not None and reducing_gap < 1.0: 

2278 msg = "reducing_gap must be 1.0 or greater" 

2279 raise ValueError(msg) 

2280 

2281 if box is None: 

2282 box = (0, 0) + self.size 

2283 

2284 size = tuple(size) 

2285 if self.size == size and box == (0, 0) + self.size: 

2286 return self.copy() 

2287 

2288 if self.mode in ("1", "P"): 

2289 resample = Resampling.NEAREST 

2290 

2291 if self.mode in ["LA", "RGBA"] and resample != Resampling.NEAREST: 

2292 im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) 

2293 im = im.resize(size, resample, box) 

2294 return im.convert(self.mode) 

2295 

2296 self.load() 

2297 

2298 if reducing_gap is not None and resample != Resampling.NEAREST: 

2299 factor_x = int((box[2] - box[0]) / size[0] / reducing_gap) or 1 

2300 factor_y = int((box[3] - box[1]) / size[1] / reducing_gap) or 1 

2301 if factor_x > 1 or factor_y > 1: 

2302 reduce_box = self._get_safe_box(size, cast(Resampling, resample), box) 

2303 factor = (factor_x, factor_y) 

2304 self = ( 

2305 self.reduce(factor, box=reduce_box) 

2306 if callable(self.reduce) 

2307 else Image.reduce(self, factor, box=reduce_box) 

2308 ) 

2309 box = ( 

2310 (box[0] - reduce_box[0]) / factor_x, 

2311 (box[1] - reduce_box[1]) / factor_y, 

2312 (box[2] - reduce_box[0]) / factor_x, 

2313 (box[3] - reduce_box[1]) / factor_y, 

2314 ) 

2315 

2316 return self._new(self.im.resize(size, resample, box)) 

2317 

2318 def reduce( 

2319 self, 

2320 factor: int | tuple[int, int], 

2321 box: tuple[int, int, int, int] | None = None, 

2322 ) -> Image: 

2323 """ 

2324 Returns a copy of the image reduced ``factor`` times. 

2325 If the size of the image is not dividable by ``factor``, 

2326 the resulting size will be rounded up. 

2327 

2328 :param factor: A greater than 0 integer or tuple of two integers 

2329 for width and height separately. 

2330 :param box: An optional 4-tuple of ints providing 

2331 the source image region to be reduced. 

2332 The values must be within ``(0, 0, width, height)`` rectangle. 

2333 If omitted or ``None``, the entire source is used. 

2334 """ 

2335 if not isinstance(factor, (list, tuple)): 

2336 factor = (factor, factor) 

2337 

2338 if box is None: 

2339 box = (0, 0) + self.size 

2340 

2341 if factor == (1, 1) and box == (0, 0) + self.size: 

2342 return self.copy() 

2343 

2344 if self.mode in ["LA", "RGBA"]: 

2345 im = self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) 

2346 im = im.reduce(factor, box) 

2347 return im.convert(self.mode) 

2348 

2349 self.load() 

2350 

2351 return self._new(self.im.reduce(factor, box)) 

2352 

2353 def rotate( 

2354 self, 

2355 angle: float, 

2356 resample: Resampling = Resampling.NEAREST, 

2357 expand: int | bool = False, 

2358 center: tuple[float, float] | None = None, 

2359 translate: tuple[int, int] | None = None, 

2360 fillcolor: float | tuple[float, ...] | str | None = None, 

2361 ) -> Image: 

2362 """ 

2363 Returns a rotated copy of this image. This method returns a 

2364 copy of this image, rotated the given number of degrees counter 

2365 clockwise around its centre. 

2366 

2367 :param angle: In degrees counter clockwise. 

2368 :param resample: An optional resampling filter. This can be 

2369 one of :py:data:`Resampling.NEAREST` (use nearest neighbour), 

2370 :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 

2371 environment), or :py:data:`Resampling.BICUBIC` (cubic spline 

2372 interpolation in a 4x4 environment). If omitted, or if the image has 

2373 mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. 

2374 See :ref:`concept-filters`. 

2375 :param expand: Optional expansion flag. If true, expands the output 

2376 image to make it large enough to hold the entire rotated image. 

2377 If false or omitted, make the output image the same size as the 

2378 input image. Note that the expand flag assumes rotation around 

2379 the center and no translation. 

2380 :param center: Optional center of rotation (a 2-tuple). Origin is 

2381 the upper left corner. Default is the center of the image. 

2382 :param translate: An optional post-rotate translation (a 2-tuple). 

2383 :param fillcolor: An optional color for area outside the rotated image. 

2384 :returns: An :py:class:`~PIL.Image.Image` object. 

2385 """ 

2386 

2387 angle = angle % 360.0 

2388 

2389 # Fast paths regardless of filter, as long as we're not 

2390 # translating or changing the center. 

2391 if not (center or translate): 

2392 if angle == 0: 

2393 return self.copy() 

2394 if angle == 180: 

2395 return self.transpose(Transpose.ROTATE_180) 

2396 if angle in (90, 270) and (expand or self.width == self.height): 

2397 return self.transpose( 

2398 Transpose.ROTATE_90 if angle == 90 else Transpose.ROTATE_270 

2399 ) 

2400 

2401 # Calculate the affine matrix. Note that this is the reverse 

2402 # transformation (from destination image to source) because we 

2403 # want to interpolate the (discrete) destination pixel from 

2404 # the local area around the (floating) source pixel. 

2405 

2406 # The matrix we actually want (note that it operates from the right): 

2407 # (1, 0, tx) (1, 0, cx) ( cos a, sin a, 0) (1, 0, -cx) 

2408 # (0, 1, ty) * (0, 1, cy) * (-sin a, cos a, 0) * (0, 1, -cy) 

2409 # (0, 0, 1) (0, 0, 1) ( 0, 0, 1) (0, 0, 1) 

2410 

2411 # The reverse matrix is thus: 

2412 # (1, 0, cx) ( cos -a, sin -a, 0) (1, 0, -cx) (1, 0, -tx) 

2413 # (0, 1, cy) * (-sin -a, cos -a, 0) * (0, 1, -cy) * (0, 1, -ty) 

2414 # (0, 0, 1) ( 0, 0, 1) (0, 0, 1) (0, 0, 1) 

2415 

2416 # In any case, the final translation may be updated at the end to 

2417 # compensate for the expand flag. 

2418 

2419 w, h = self.size 

2420 

2421 if translate is None: 

2422 post_trans = (0, 0) 

2423 else: 

2424 post_trans = translate 

2425 if center is None: 

2426 center = (w / 2, h / 2) 

2427 

2428 angle = -math.radians(angle) 

2429 matrix = [ 

2430 round(math.cos(angle), 15), 

2431 round(math.sin(angle), 15), 

2432 0.0, 

2433 round(-math.sin(angle), 15), 

2434 round(math.cos(angle), 15), 

2435 0.0, 

2436 ] 

2437 

2438 def transform(x: float, y: float, matrix: list[float]) -> tuple[float, float]: 

2439 (a, b, c, d, e, f) = matrix 

2440 return a * x + b * y + c, d * x + e * y + f 

2441 

2442 matrix[2], matrix[5] = transform( 

2443 -center[0] - post_trans[0], -center[1] - post_trans[1], matrix 

2444 ) 

2445 matrix[2] += center[0] 

2446 matrix[5] += center[1] 

2447 

2448 if expand: 

2449 # calculate output size 

2450 xx = [] 

2451 yy = [] 

2452 for x, y in ((0, 0), (w, 0), (w, h), (0, h)): 

2453 transformed_x, transformed_y = transform(x, y, matrix) 

2454 xx.append(transformed_x) 

2455 yy.append(transformed_y) 

2456 nw = math.ceil(max(xx)) - math.floor(min(xx)) 

2457 nh = math.ceil(max(yy)) - math.floor(min(yy)) 

2458 

2459 # We multiply a translation matrix from the right. Because of its 

2460 # special form, this is the same as taking the image of the 

2461 # translation vector as new translation vector. 

2462 matrix[2], matrix[5] = transform(-(nw - w) / 2.0, -(nh - h) / 2.0, matrix) 

2463 w, h = nw, nh 

2464 

2465 return self.transform( 

2466 (w, h), Transform.AFFINE, matrix, resample, fillcolor=fillcolor 

2467 ) 

2468 

2469 def save( 

2470 self, fp: StrOrBytesPath | IO[bytes], format: str | None = None, **params: Any 

2471 ) -> None: 

2472 """ 

2473 Saves this image under the given filename. If no format is 

2474 specified, the format to use is determined from the filename 

2475 extension, if possible. 

2476 

2477 Keyword options can be used to provide additional instructions 

2478 to the writer. If a writer doesn't recognise an option, it is 

2479 silently ignored. The available options are described in the 

2480 :doc:`image format documentation 

2481 <../handbook/image-file-formats>` for each writer. 

2482 

2483 You can use a file object instead of a filename. In this case, 

2484 you must always specify the format. The file object must 

2485 implement the ``seek``, ``tell``, and ``write`` 

2486 methods, and be opened in binary mode. 

2487 

2488 :param fp: A filename (string), os.PathLike object or file object. 

2489 :param format: Optional format override. If omitted, the 

2490 format to use is determined from the filename extension. 

2491 If a file object was used instead of a filename, this 

2492 parameter should always be used. 

2493 :param params: Extra parameters to the image writer. These can also be 

2494 set on the image itself through ``encoderinfo``. This is useful when 

2495 saving multiple images:: 

2496 

2497 # Saving XMP data to a single image 

2498 from PIL import Image 

2499 red = Image.new("RGB", (1, 1), "#f00") 

2500 red.save("out.mpo", xmp=b"test") 

2501 

2502 # Saving XMP data to the second frame of an image 

2503 from PIL import Image 

2504 black = Image.new("RGB", (1, 1)) 

2505 red = Image.new("RGB", (1, 1), "#f00") 

2506 red.encoderinfo = {"xmp": b"test"} 

2507 black.save("out.mpo", save_all=True, append_images=[red]) 

2508 :returns: None 

2509 :exception ValueError: If the output format could not be determined 

2510 from the file name. Use the format option to solve this. 

2511 :exception OSError: If the file could not be written. The file 

2512 may have been created, and may contain partial data. 

2513 """ 

2514 

2515 filename: str | bytes = "" 

2516 open_fp = False 

2517 if is_path(fp): 

2518 filename = os.fspath(fp) 

2519 open_fp = True 

2520 elif fp == sys.stdout: 

2521 try: 

2522 fp = sys.stdout.buffer 

2523 except AttributeError: 

2524 pass 

2525 if not filename and hasattr(fp, "name") and is_path(fp.name): 

2526 # only set the name for metadata purposes 

2527 filename = os.fspath(fp.name) 

2528 

2529 preinit() 

2530 

2531 filename_ext = os.path.splitext(filename)[1].lower() 

2532 ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext 

2533 

2534 if not format: 

2535 if ext not in EXTENSION: 

2536 init() 

2537 try: 

2538 format = EXTENSION[ext] 

2539 except KeyError as e: 

2540 msg = f"unknown file extension: {ext}" 

2541 raise ValueError(msg) from e 

2542 

2543 from . import ImageFile 

2544 

2545 # may mutate self! 

2546 if isinstance(self, ImageFile.ImageFile) and os.path.abspath( 

2547 filename 

2548 ) == os.path.abspath(self.filename): 

2549 self._ensure_mutable() 

2550 else: 

2551 self.load() 

2552 

2553 save_all = params.pop("save_all", None) 

2554 self.encoderinfo = {**getattr(self, "encoderinfo", {}), **params} 

2555 self.encoderconfig: tuple[Any, ...] = () 

2556 

2557 if format.upper() not in SAVE: 

2558 init() 

2559 if save_all or ( 

2560 save_all is None 

2561 and params.get("append_images") 

2562 and format.upper() in SAVE_ALL 

2563 ): 

2564 save_handler = SAVE_ALL[format.upper()] 

2565 else: 

2566 save_handler = SAVE[format.upper()] 

2567 

2568 created = False 

2569 if open_fp: 

2570 created = not os.path.exists(filename) 

2571 if params.get("append", False): 

2572 # Open also for reading ("+"), because TIFF save_all 

2573 # writer needs to go back and edit the written data. 

2574 fp = builtins.open(filename, "r+b") 

2575 else: 

2576 fp = builtins.open(filename, "w+b") 

2577 else: 

2578 fp = cast(IO[bytes], fp) 

2579 

2580 try: 

2581 save_handler(self, fp, filename) 

2582 except Exception: 

2583 if open_fp: 

2584 fp.close() 

2585 if created: 

2586 try: 

2587 os.remove(filename) 

2588 except PermissionError: 

2589 pass 

2590 raise 

2591 finally: 

2592 try: 

2593 del self.encoderinfo 

2594 except AttributeError: 

2595 pass 

2596 if open_fp: 

2597 fp.close() 

2598 

2599 def seek(self, frame: int) -> None: 

2600 """ 

2601 Seeks to the given frame in this sequence file. If you seek 

2602 beyond the end of the sequence, the method raises an 

2603 ``EOFError`` exception. When a sequence file is opened, the 

2604 library automatically seeks to frame 0. 

2605 

2606 See :py:meth:`~PIL.Image.Image.tell`. 

2607 

2608 If defined, :attr:`~PIL.Image.Image.n_frames` refers to the 

2609 number of available frames. 

2610 

2611 :param frame: Frame number, starting at 0. 

2612 :exception EOFError: If the call attempts to seek beyond the end 

2613 of the sequence. 

2614 """ 

2615 

2616 # overridden by file handlers 

2617 if frame != 0: 

2618 msg = "no more images in file" 

2619 raise EOFError(msg) 

2620 

2621 def show(self, title: str | None = None) -> None: 

2622 """ 

2623 Displays this image. This method is mainly intended for debugging purposes. 

2624 

2625 This method calls :py:func:`PIL.ImageShow.show` internally. You can use 

2626 :py:func:`PIL.ImageShow.register` to override its default behaviour. 

2627 

2628 The image is first saved to a temporary file. By default, it will be in 

2629 PNG format. 

2630 

2631 On Unix, the image is then opened using the **xdg-open**, **display**, 

2632 **gm**, **eog** or **xv** utility, depending on which one can be found. 

2633 

2634 On macOS, the image is opened with the native Preview application. 

2635 

2636 On Windows, the image is opened with the standard PNG display utility. 

2637 

2638 :param title: Optional title to use for the image window, where possible. 

2639 """ 

2640 

2641 _show(self, title=title) 

2642 

2643 def split(self) -> tuple[Image, ...]: 

2644 """ 

2645 Split this image into individual bands. This method returns a 

2646 tuple of individual image bands from an image. For example, 

2647 splitting an "RGB" image creates three new images each 

2648 containing a copy of one of the original bands (red, green, 

2649 blue). 

2650 

2651 If you need only one band, :py:meth:`~PIL.Image.Image.getchannel` 

2652 method can be more convenient and faster. 

2653 

2654 :returns: A tuple containing bands. 

2655 """ 

2656 

2657 self.load() 

2658 if self.im.bands == 1: 

2659 return (self.copy(),) 

2660 return tuple(map(self._new, self.im.split())) 

2661 

2662 def getchannel(self, channel: int | str) -> Image: 

2663 """ 

2664 Returns an image containing a single channel of the source image. 

2665 

2666 :param channel: What channel to return. Could be index 

2667 (0 for "R" channel of "RGB") or channel name 

2668 ("A" for alpha channel of "RGBA"). 

2669 :returns: An image in "L" mode. 

2670 

2671 .. versionadded:: 4.3.0 

2672 """ 

2673 self.load() 

2674 

2675 if isinstance(channel, str): 

2676 try: 

2677 channel = self.getbands().index(channel) 

2678 except ValueError as e: 

2679 msg = f'The image has no channel "{channel}"' 

2680 raise ValueError(msg) from e 

2681 

2682 return self._new(self.im.getband(channel)) 

2683 

2684 def tell(self) -> int: 

2685 """ 

2686 Returns the current frame number. See :py:meth:`~PIL.Image.Image.seek`. 

2687 

2688 If defined, :attr:`~PIL.Image.Image.n_frames` refers to the 

2689 number of available frames. 

2690 

2691 :returns: Frame number, starting with 0. 

2692 """ 

2693 return 0 

2694 

2695 def thumbnail( 

2696 self, 

2697 size: tuple[float, float], 

2698 resample: Resampling = Resampling.BICUBIC, 

2699 reducing_gap: float | None = 2.0, 

2700 ) -> None: 

2701 """ 

2702 Make this image into a thumbnail. This method modifies the 

2703 image to contain a thumbnail version of itself, no larger than 

2704 the given size. This method calculates an appropriate thumbnail 

2705 size to preserve the aspect of the image, calls the 

2706 :py:meth:`~PIL.Image.Image.draft` method to configure the file reader 

2707 (where applicable), and finally resizes the image. 

2708 

2709 Note that this function modifies the :py:class:`~PIL.Image.Image` 

2710 object in place. If you need to use the full resolution image as well, 

2711 apply this method to a :py:meth:`~PIL.Image.Image.copy` of the original 

2712 image. 

2713 

2714 :param size: The requested size in pixels, as a 2-tuple: 

2715 (width, height). 

2716 :param resample: Optional resampling filter. This can be one 

2717 of :py:data:`Resampling.NEAREST`, :py:data:`Resampling.BOX`, 

2718 :py:data:`Resampling.BILINEAR`, :py:data:`Resampling.HAMMING`, 

2719 :py:data:`Resampling.BICUBIC` or :py:data:`Resampling.LANCZOS`. 

2720 If omitted, it defaults to :py:data:`Resampling.BICUBIC`. 

2721 (was :py:data:`Resampling.NEAREST` prior to version 2.5.0). 

2722 See: :ref:`concept-filters`. 

2723 :param reducing_gap: Apply optimization by resizing the image 

2724 in two steps. First, reducing the image by integer times 

2725 using :py:meth:`~PIL.Image.Image.reduce` or 

2726 :py:meth:`~PIL.Image.Image.draft` for JPEG images. 

2727 Second, resizing using regular resampling. The last step 

2728 changes size no less than by ``reducing_gap`` times. 

2729 ``reducing_gap`` may be None (no first step is performed) 

2730 or should be greater than 1.0. The bigger ``reducing_gap``, 

2731 the closer the result to the fair resampling. 

2732 The smaller ``reducing_gap``, the faster resizing. 

2733 With ``reducing_gap`` greater or equal to 3.0, the result is 

2734 indistinguishable from fair resampling in most cases. 

2735 The default value is 2.0 (very close to fair resampling 

2736 while still being faster in many cases). 

2737 :returns: None 

2738 """ 

2739 

2740 provided_size = tuple(map(math.floor, size)) 

2741 

2742 def preserve_aspect_ratio() -> tuple[int, int] | None: 

2743 def round_aspect(number: float, key: Callable[[int], float]) -> int: 

2744 return max(min(math.floor(number), math.ceil(number), key=key), 1) 

2745 

2746 x, y = provided_size 

2747 if x >= self.width and y >= self.height: 

2748 return None 

2749 

2750 aspect = self.width / self.height 

2751 if x / y >= aspect: 

2752 x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y)) 

2753 else: 

2754 y = round_aspect( 

2755 x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n) 

2756 ) 

2757 return x, y 

2758 

2759 preserved_size = preserve_aspect_ratio() 

2760 if preserved_size is None: 

2761 return 

2762 final_size = preserved_size 

2763 

2764 box = None 

2765 if reducing_gap is not None: 

2766 res = self.draft( 

2767 None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) 

2768 ) 

2769 if res is not None: 

2770 box = res[1] 

2771 

2772 if self.size != final_size: 

2773 im = self.resize(final_size, resample, box=box, reducing_gap=reducing_gap) 

2774 

2775 self.im = im.im 

2776 self._size = final_size 

2777 self._mode = self.im.mode 

2778 

2779 self.readonly = 0 

2780 

2781 # FIXME: the different transform methods need further explanation 

2782 # instead of bloating the method docs, add a separate chapter. 

2783 def transform( 

2784 self, 

2785 size: tuple[int, int], 

2786 method: Transform | ImageTransformHandler | SupportsGetData, 

2787 data: Sequence[Any] | None = None, 

2788 resample: int = Resampling.NEAREST, 

2789 fill: int = 1, 

2790 fillcolor: float | tuple[float, ...] | str | None = None, 

2791 ) -> Image: 

2792 """ 

2793 Transforms this image. This method creates a new image with the 

2794 given size, and the same mode as the original, and copies data 

2795 to the new image using the given transform. 

2796 

2797 :param size: The output size in pixels, as a 2-tuple: 

2798 (width, height). 

2799 :param method: The transformation method. This is one of 

2800 :py:data:`Transform.EXTENT` (cut out a rectangular subregion), 

2801 :py:data:`Transform.AFFINE` (affine transform), 

2802 :py:data:`Transform.PERSPECTIVE` (perspective transform), 

2803 :py:data:`Transform.QUAD` (map a quadrilateral to a rectangle), or 

2804 :py:data:`Transform.MESH` (map a number of source quadrilaterals 

2805 in one operation). 

2806 

2807 It may also be an :py:class:`~PIL.Image.ImageTransformHandler` 

2808 object:: 

2809 

2810 class Example(Image.ImageTransformHandler): 

2811 def transform(self, size, data, resample, fill=1): 

2812 # Return result 

2813 

2814 Implementations of :py:class:`~PIL.Image.ImageTransformHandler` 

2815 for some of the :py:class:`Transform` methods are provided 

2816 in :py:mod:`~PIL.ImageTransform`. 

2817 

2818 It may also be an object with a ``method.getdata`` method 

2819 that returns a tuple supplying new ``method`` and ``data`` values:: 

2820 

2821 class Example: 

2822 def getdata(self): 

2823 method = Image.Transform.EXTENT 

2824 data = (0, 0, 100, 100) 

2825 return method, data 

2826 :param data: Extra data to the transformation method. 

2827 :param resample: Optional resampling filter. It can be one of 

2828 :py:data:`Resampling.NEAREST` (use nearest neighbour), 

2829 :py:data:`Resampling.BILINEAR` (linear interpolation in a 2x2 

2830 environment), or :py:data:`Resampling.BICUBIC` (cubic spline 

2831 interpolation in a 4x4 environment). If omitted, or if the image 

2832 has mode "1" or "P", it is set to :py:data:`Resampling.NEAREST`. 

2833 See: :ref:`concept-filters`. 

2834 :param fill: If ``method`` is an 

2835 :py:class:`~PIL.Image.ImageTransformHandler` object, this is one of 

2836 the arguments passed to it. Otherwise, it is unused. 

2837 :param fillcolor: Optional fill color for the area outside the 

2838 transform in the output image. 

2839 :returns: An :py:class:`~PIL.Image.Image` object. 

2840 """ 

2841 

2842 if self.mode in ("LA", "RGBA") and resample != Resampling.NEAREST: 

2843 return ( 

2844 self.convert({"LA": "La", "RGBA": "RGBa"}[self.mode]) 

2845 .transform(size, method, data, resample, fill, fillcolor) 

2846 .convert(self.mode) 

2847 ) 

2848 

2849 if isinstance(method, ImageTransformHandler): 

2850 return method.transform(size, self, resample=resample, fill=fill) 

2851 

2852 if hasattr(method, "getdata"): 

2853 # compatibility w. old-style transform objects 

2854 method, data = method.getdata() 

2855 

2856 if data is None: 

2857 msg = "missing method data" 

2858 raise ValueError(msg) 

2859 

2860 im = new(self.mode, size, fillcolor) 

2861 if self.mode == "P" and self.palette: 

2862 im.palette = self.palette.copy() 

2863 im.info = self.info.copy() 

2864 if method == Transform.MESH: 

2865 # list of quads 

2866 for box, quad in data: 

2867 im.__transformer( 

2868 box, self, Transform.QUAD, quad, resample, fillcolor is None 

2869 ) 

2870 else: 

2871 im.__transformer( 

2872 (0, 0) + size, self, method, data, resample, fillcolor is None 

2873 ) 

2874 

2875 return im 

2876 

2877 def __transformer( 

2878 self, 

2879 box: tuple[int, int, int, int], 

2880 image: Image, 

2881 method: Transform, 

2882 data: Sequence[float], 

2883 resample: int = Resampling.NEAREST, 

2884 fill: bool = True, 

2885 ) -> None: 

2886 w = box[2] - box[0] 

2887 h = box[3] - box[1] 

2888 

2889 if method == Transform.AFFINE: 

2890 data = data[:6] 

2891 

2892 elif method == Transform.EXTENT: 

2893 # convert extent to an affine transform 

2894 x0, y0, x1, y1 = data 

2895 xs = (x1 - x0) / w 

2896 ys = (y1 - y0) / h 

2897 method = Transform.AFFINE 

2898 data = (xs, 0, x0, 0, ys, y0) 

2899 

2900 elif method == Transform.PERSPECTIVE: 

2901 data = data[:8] 

2902 

2903 elif method == Transform.QUAD: 

2904 # quadrilateral warp. data specifies the four corners 

2905 # given as NW, SW, SE, and NE. 

2906 nw = data[:2] 

2907 sw = data[2:4] 

2908 se = data[4:6] 

2909 ne = data[6:8] 

2910 x0, y0 = nw 

2911 As = 1.0 / w 

2912 At = 1.0 / h 

2913 data = ( 

2914 x0, 

2915 (ne[0] - x0) * As, 

2916 (sw[0] - x0) * At, 

2917 (se[0] - sw[0] - ne[0] + x0) * As * At, 

2918 y0, 

2919 (ne[1] - y0) * As, 

2920 (sw[1] - y0) * At, 

2921 (se[1] - sw[1] - ne[1] + y0) * As * At, 

2922 ) 

2923 

2924 else: 

2925 msg = "unknown transformation method" 

2926 raise ValueError(msg) 

2927 

2928 if resample not in ( 

2929 Resampling.NEAREST, 

2930 Resampling.BILINEAR, 

2931 Resampling.BICUBIC, 

2932 ): 

2933 if resample in (Resampling.BOX, Resampling.HAMMING, Resampling.LANCZOS): 

2934 unusable: dict[int, str] = { 

2935 Resampling.BOX: "Image.Resampling.BOX", 

2936 Resampling.HAMMING: "Image.Resampling.HAMMING", 

2937 Resampling.LANCZOS: "Image.Resampling.LANCZOS", 

2938 } 

2939 msg = unusable[resample] + f" ({resample}) cannot be used." 

2940 else: 

2941 msg = f"Unknown resampling filter ({resample})." 

2942 

2943 filters = [ 

2944 f"{filter[1]} ({filter[0]})" 

2945 for filter in ( 

2946 (Resampling.NEAREST, "Image.Resampling.NEAREST"), 

2947 (Resampling.BILINEAR, "Image.Resampling.BILINEAR"), 

2948 (Resampling.BICUBIC, "Image.Resampling.BICUBIC"), 

2949 ) 

2950 ] 

2951 msg += f" Use {', '.join(filters[:-1])} or {filters[-1]}" 

2952 raise ValueError(msg) 

2953 

2954 image.load() 

2955 

2956 self.load() 

2957 

2958 if image.mode in ("1", "P"): 

2959 resample = Resampling.NEAREST 

2960 

2961 self.im.transform(box, image.im, method, data, resample, fill) 

2962 

2963 def transpose(self, method: Transpose) -> Image: 

2964 """ 

2965 Transpose image (flip or rotate in 90 degree steps) 

2966 

2967 :param method: One of :py:data:`Transpose.FLIP_LEFT_RIGHT`, 

2968 :py:data:`Transpose.FLIP_TOP_BOTTOM`, :py:data:`Transpose.ROTATE_90`, 

2969 :py:data:`Transpose.ROTATE_180`, :py:data:`Transpose.ROTATE_270`, 

2970 :py:data:`Transpose.TRANSPOSE` or :py:data:`Transpose.TRANSVERSE`. 

2971 :returns: Returns a flipped or rotated copy of this image. 

2972 """ 

2973 

2974 self.load() 

2975 return self._new(self.im.transpose(method)) 

2976 

2977 def effect_spread(self, distance: int) -> Image: 

2978 """ 

2979 Randomly spread pixels in an image. 

2980 

2981 :param distance: Distance to spread pixels. 

2982 """ 

2983 self.load() 

2984 return self._new(self.im.effect_spread(distance)) 

2985 

2986 def toqimage(self) -> ImageQt.ImageQt: 

2987 """Returns a QImage copy of this image""" 

2988 from . import ImageQt 

2989 

2990 if not ImageQt.qt_is_installed: 

2991 msg = "Qt bindings are not installed" 

2992 raise ImportError(msg) 

2993 return ImageQt.toqimage(self) 

2994 

2995 def toqpixmap(self) -> ImageQt.QPixmap: 

2996 """Returns a QPixmap copy of this image""" 

2997 from . import ImageQt 

2998 

2999 if not ImageQt.qt_is_installed: 

3000 msg = "Qt bindings are not installed" 

3001 raise ImportError(msg) 

3002 return ImageQt.toqpixmap(self) 

3003 

3004 

3005# -------------------------------------------------------------------- 

3006# Abstract handlers. 

3007 

3008 

3009class ImagePointHandler(abc.ABC): 

3010 """ 

3011 Used as a mixin by point transforms 

3012 (for use with :py:meth:`~PIL.Image.Image.point`) 

3013 """ 

3014 

3015 @abc.abstractmethod 

3016 def point(self, im: Image) -> Image: 

3017 pass 

3018 

3019 

3020class ImageTransformHandler(abc.ABC): 

3021 """ 

3022 Used as a mixin by geometry transforms 

3023 (for use with :py:meth:`~PIL.Image.Image.transform`) 

3024 """ 

3025 

3026 @abc.abstractmethod 

3027 def transform( 

3028 self, 

3029 size: tuple[int, int], 

3030 image: Image, 

3031 **options: Any, 

3032 ) -> Image: 

3033 pass 

3034 

3035 

3036# -------------------------------------------------------------------- 

3037# Factories 

3038 

3039 

3040def _check_size(size: Any) -> None: 

3041 """ 

3042 Common check to enforce type and sanity check on size tuples 

3043 

3044 :param size: Should be a 2 tuple of (width, height) 

3045 :returns: None, or raises a ValueError 

3046 """ 

3047 

3048 if not isinstance(size, (list, tuple)): 

3049 msg = "Size must be a list or tuple" 

3050 raise ValueError(msg) 

3051 if len(size) != 2: 

3052 msg = "Size must be a sequence of length 2" 

3053 raise ValueError(msg) 

3054 if size[0] < 0 or size[1] < 0: 

3055 msg = "Width and height must be >= 0" 

3056 raise ValueError(msg) 

3057 

3058 

3059def new( 

3060 mode: str, 

3061 size: tuple[int, int] | list[int], 

3062 color: float | tuple[float, ...] | str | None = 0, 

3063) -> Image: 

3064 """ 

3065 Creates a new image with the given mode and size. 

3066 

3067 :param mode: The mode to use for the new image. See: 

3068 :ref:`concept-modes`. 

3069 :param size: A 2-tuple, containing (width, height) in pixels. 

3070 :param color: What color to use for the image. Default is black. 

3071 If given, this should be a single integer or floating point value 

3072 for single-band modes, and a tuple for multi-band modes (one value 

3073 per band). When creating RGB or HSV images, you can also use color 

3074 strings as supported by the ImageColor module. If the color is 

3075 None, the image is not initialised. 

3076 :returns: An :py:class:`~PIL.Image.Image` object. 

3077 """ 

3078 

3079 if mode in ("BGR;15", "BGR;16", "BGR;24"): 

3080 deprecate(mode, 12) 

3081 

3082 _check_size(size) 

3083 

3084 if color is None: 

3085 # don't initialize 

3086 return Image()._new(core.new(mode, size)) 

3087 

3088 if isinstance(color, str): 

3089 # css3-style specifier 

3090 

3091 from . import ImageColor 

3092 

3093 color = ImageColor.getcolor(color, mode) 

3094 

3095 im = Image() 

3096 if ( 

3097 mode == "P" 

3098 and isinstance(color, (list, tuple)) 

3099 and all(isinstance(i, int) for i in color) 

3100 ): 

3101 color_ints: tuple[int, ...] = cast(tuple[int, ...], tuple(color)) 

3102 if len(color_ints) == 3 or len(color_ints) == 4: 

3103 # RGB or RGBA value for a P image 

3104 from . import ImagePalette 

3105 

3106 im.palette = ImagePalette.ImagePalette() 

3107 color = im.palette.getcolor(color_ints) 

3108 return im._new(core.fill(mode, size, color)) 

3109 

3110 

3111def frombytes( 

3112 mode: str, 

3113 size: tuple[int, int], 

3114 data: bytes | bytearray | SupportsArrayInterface, 

3115 decoder_name: str = "raw", 

3116 *args: Any, 

3117) -> Image: 

3118 """ 

3119 Creates a copy of an image memory from pixel data in a buffer. 

3120 

3121 In its simplest form, this function takes three arguments 

3122 (mode, size, and unpacked pixel data). 

3123 

3124 You can also use any pixel decoder supported by PIL. For more 

3125 information on available decoders, see the section 

3126 :ref:`Writing Your Own File Codec <file-codecs>`. 

3127 

3128 Note that this function decodes pixel data only, not entire images. 

3129 If you have an entire image in a string, wrap it in a 

3130 :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load 

3131 it. 

3132 

3133 :param mode: The image mode. See: :ref:`concept-modes`. 

3134 :param size: The image size. 

3135 :param data: A byte buffer containing raw data for the given mode. 

3136 :param decoder_name: What decoder to use. 

3137 :param args: Additional parameters for the given decoder. 

3138 :returns: An :py:class:`~PIL.Image.Image` object. 

3139 """ 

3140 

3141 _check_size(size) 

3142 

3143 im = new(mode, size) 

3144 if im.width != 0 and im.height != 0: 

3145 decoder_args: Any = args 

3146 if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple): 

3147 # may pass tuple instead of argument list 

3148 decoder_args = decoder_args[0] 

3149 

3150 if decoder_name == "raw" and decoder_args == (): 

3151 decoder_args = mode 

3152 

3153 im.frombytes(data, decoder_name, decoder_args) 

3154 return im 

3155 

3156 

3157def frombuffer( 

3158 mode: str, 

3159 size: tuple[int, int], 

3160 data: bytes | SupportsArrayInterface, 

3161 decoder_name: str = "raw", 

3162 *args: Any, 

3163) -> Image: 

3164 """ 

3165 Creates an image memory referencing pixel data in a byte buffer. 

3166 

3167 This function is similar to :py:func:`~PIL.Image.frombytes`, but uses data 

3168 in the byte buffer, where possible. This means that changes to the 

3169 original buffer object are reflected in this image). Not all modes can 

3170 share memory; supported modes include "L", "RGBX", "RGBA", and "CMYK". 

3171 

3172 Note that this function decodes pixel data only, not entire images. 

3173 If you have an entire image file in a string, wrap it in a 

3174 :py:class:`~io.BytesIO` object, and use :py:func:`~PIL.Image.open` to load it. 

3175 

3176 The default parameters used for the "raw" decoder differs from that used for 

3177 :py:func:`~PIL.Image.frombytes`. This is a bug, and will probably be fixed in a 

3178 future release. The current release issues a warning if you do this; to disable 

3179 the warning, you should provide the full set of parameters. See below for details. 

3180 

3181 :param mode: The image mode. See: :ref:`concept-modes`. 

3182 :param size: The image size. 

3183 :param data: A bytes or other buffer object containing raw 

3184 data for the given mode. 

3185 :param decoder_name: What decoder to use. 

3186 :param args: Additional parameters for the given decoder. For the 

3187 default encoder ("raw"), it's recommended that you provide the 

3188 full set of parameters:: 

3189 

3190 frombuffer(mode, size, data, "raw", mode, 0, 1) 

3191 

3192 :returns: An :py:class:`~PIL.Image.Image` object. 

3193 

3194 .. versionadded:: 1.1.4 

3195 """ 

3196 

3197 _check_size(size) 

3198 

3199 # may pass tuple instead of argument list 

3200 if len(args) == 1 and isinstance(args[0], tuple): 

3201 args = args[0] 

3202 

3203 if decoder_name == "raw": 

3204 if args == (): 

3205 args = mode, 0, 1 

3206 if args[0] in _MAPMODES: 

3207 im = new(mode, (0, 0)) 

3208 im = im._new(core.map_buffer(data, size, decoder_name, 0, args)) 

3209 if mode == "P": 

3210 from . import ImagePalette 

3211 

3212 im.palette = ImagePalette.ImagePalette("RGB", im.im.getpalette("RGB")) 

3213 im.readonly = 1 

3214 return im 

3215 

3216 return frombytes(mode, size, data, decoder_name, args) 

3217 

3218 

3219class SupportsArrayInterface(Protocol): 

3220 """ 

3221 An object that has an ``__array_interface__`` dictionary. 

3222 """ 

3223 

3224 @property 

3225 def __array_interface__(self) -> dict[str, Any]: 

3226 raise NotImplementedError() 

3227 

3228 

3229class SupportsArrowArrayInterface(Protocol): 

3230 """ 

3231 An object that has an ``__arrow_c_array__`` method corresponding to the arrow c 

3232 data interface. 

3233 """ 

3234 

3235 def __arrow_c_array__( 

3236 self, requested_schema: "PyCapsule" = None # type: ignore[name-defined] # noqa: F821, UP037 

3237 ) -> tuple["PyCapsule", "PyCapsule"]: # type: ignore[name-defined] # noqa: F821, UP037 

3238 raise NotImplementedError() 

3239 

3240 

3241def fromarray(obj: SupportsArrayInterface, mode: str | None = None) -> Image: 

3242 """ 

3243 Creates an image memory from an object exporting the array interface 

3244 (using the buffer protocol):: 

3245 

3246 from PIL import Image 

3247 import numpy as np 

3248 a = np.zeros((5, 5)) 

3249 im = Image.fromarray(a) 

3250 

3251 If ``obj`` is not contiguous, then the ``tobytes`` method is called 

3252 and :py:func:`~PIL.Image.frombuffer` is used. 

3253 

3254 In the case of NumPy, be aware that Pillow modes do not always correspond 

3255 to NumPy dtypes. Pillow modes only offer 1-bit pixels, 8-bit pixels, 

3256 32-bit signed integer pixels, and 32-bit floating point pixels. 

3257 

3258 Pillow images can also be converted to arrays:: 

3259 

3260 from PIL import Image 

3261 import numpy as np 

3262 im = Image.open("hopper.jpg") 

3263 a = np.asarray(im) 

3264 

3265 When converting Pillow images to arrays however, only pixel values are 

3266 transferred. This means that P and PA mode images will lose their palette. 

3267 

3268 :param obj: Object with array interface 

3269 :param mode: Optional mode to use when reading ``obj``. Will be determined from 

3270 type if ``None``. 

3271 

3272 This will not be used to convert the data after reading, but will be used to 

3273 change how the data is read:: 

3274 

3275 from PIL import Image 

3276 import numpy as np 

3277 a = np.full((1, 1), 300) 

3278 im = Image.fromarray(a, mode="L") 

3279 im.getpixel((0, 0)) # 44 

3280 im = Image.fromarray(a, mode="RGB") 

3281 im.getpixel((0, 0)) # (44, 1, 0) 

3282 

3283 See: :ref:`concept-modes` for general information about modes. 

3284 :returns: An image object. 

3285 

3286 .. versionadded:: 1.1.6 

3287 """ 

3288 arr = obj.__array_interface__ 

3289 shape = arr["shape"] 

3290 ndim = len(shape) 

3291 strides = arr.get("strides", None) 

3292 if mode is None: 

3293 try: 

3294 typekey = (1, 1) + shape[2:], arr["typestr"] 

3295 except KeyError as e: 

3296 msg = "Cannot handle this data type" 

3297 raise TypeError(msg) from e 

3298 try: 

3299 mode, rawmode = _fromarray_typemap[typekey] 

3300 except KeyError as e: 

3301 typekey_shape, typestr = typekey 

3302 msg = f"Cannot handle this data type: {typekey_shape}, {typestr}" 

3303 raise TypeError(msg) from e 

3304 else: 

3305 rawmode = mode 

3306 if mode in ["1", "L", "I", "P", "F"]: 

3307 ndmax = 2 

3308 elif mode == "RGB": 

3309 ndmax = 3 

3310 else: 

3311 ndmax = 4 

3312 if ndim > ndmax: 

3313 msg = f"Too many dimensions: {ndim} > {ndmax}." 

3314 raise ValueError(msg) 

3315 

3316 size = 1 if ndim == 1 else shape[1], shape[0] 

3317 if strides is not None: 

3318 if hasattr(obj, "tobytes"): 

3319 obj = obj.tobytes() 

3320 elif hasattr(obj, "tostring"): 

3321 obj = obj.tostring() 

3322 else: 

3323 msg = "'strides' requires either tobytes() or tostring()" 

3324 raise ValueError(msg) 

3325 

3326 return frombuffer(mode, size, obj, "raw", rawmode, 0, 1) 

3327 

3328 

3329def fromarrow( 

3330 obj: SupportsArrowArrayInterface, mode: str, size: tuple[int, int] 

3331) -> Image: 

3332 """Creates an image with zero-copy shared memory from an object exporting 

3333 the arrow_c_array interface protocol:: 

3334 

3335 from PIL import Image 

3336 import pyarrow as pa 

3337 arr = pa.array([0]*(5*5*4), type=pa.uint8()) 

3338 im = Image.fromarrow(arr, 'RGBA', (5, 5)) 

3339 

3340 If the data representation of the ``obj`` is not compatible with 

3341 Pillow internal storage, a ValueError is raised. 

3342 

3343 Pillow images can also be converted to Arrow objects:: 

3344 

3345 from PIL import Image 

3346 import pyarrow as pa 

3347 im = Image.open('hopper.jpg') 

3348 arr = pa.array(im) 

3349 

3350 As with array support, when converting Pillow images to arrays, 

3351 only pixel values are transferred. This means that P and PA mode 

3352 images will lose their palette. 

3353 

3354 :param obj: Object with an arrow_c_array interface 

3355 :param mode: Image mode. 

3356 :param size: Image size. This must match the storage of the arrow object. 

3357 :returns: An Image object 

3358 

3359 Note that according to the Arrow spec, both the producer and the 

3360 consumer should consider the exported array to be immutable, as 

3361 unsynchronized updates will potentially cause inconsistent data. 

3362 

3363 See: :ref:`arrow-support` for more detailed information 

3364 

3365 .. versionadded:: 11.2.1 

3366 

3367 """ 

3368 if not hasattr(obj, "__arrow_c_array__"): 

3369 msg = "arrow_c_array interface not found" 

3370 raise ValueError(msg) 

3371 

3372 (schema_capsule, array_capsule) = obj.__arrow_c_array__() 

3373 _im = core.new_arrow(mode, size, schema_capsule, array_capsule) 

3374 if _im: 

3375 return Image()._new(_im) 

3376 

3377 msg = "new_arrow returned None without an exception" 

3378 raise ValueError(msg) 

3379 

3380 

3381def fromqimage(im: ImageQt.QImage) -> ImageFile.ImageFile: 

3382 """Creates an image instance from a QImage image""" 

3383 from . import ImageQt 

3384 

3385 if not ImageQt.qt_is_installed: 

3386 msg = "Qt bindings are not installed" 

3387 raise ImportError(msg) 

3388 return ImageQt.fromqimage(im) 

3389 

3390 

3391def fromqpixmap(im: ImageQt.QPixmap) -> ImageFile.ImageFile: 

3392 """Creates an image instance from a QPixmap image""" 

3393 from . import ImageQt 

3394 

3395 if not ImageQt.qt_is_installed: 

3396 msg = "Qt bindings are not installed" 

3397 raise ImportError(msg) 

3398 return ImageQt.fromqpixmap(im) 

3399 

3400 

3401_fromarray_typemap = { 

3402 # (shape, typestr) => mode, rawmode 

3403 # first two members of shape are set to one 

3404 ((1, 1), "|b1"): ("1", "1;8"), 

3405 ((1, 1), "|u1"): ("L", "L"), 

3406 ((1, 1), "|i1"): ("I", "I;8"), 

3407 ((1, 1), "<u2"): ("I", "I;16"), 

3408 ((1, 1), ">u2"): ("I", "I;16B"), 

3409 ((1, 1), "<i2"): ("I", "I;16S"), 

3410 ((1, 1), ">i2"): ("I", "I;16BS"), 

3411 ((1, 1), "<u4"): ("I", "I;32"), 

3412 ((1, 1), ">u4"): ("I", "I;32B"), 

3413 ((1, 1), "<i4"): ("I", "I;32S"), 

3414 ((1, 1), ">i4"): ("I", "I;32BS"), 

3415 ((1, 1), "<f4"): ("F", "F;32F"), 

3416 ((1, 1), ">f4"): ("F", "F;32BF"), 

3417 ((1, 1), "<f8"): ("F", "F;64F"), 

3418 ((1, 1), ">f8"): ("F", "F;64BF"), 

3419 ((1, 1, 2), "|u1"): ("LA", "LA"), 

3420 ((1, 1, 3), "|u1"): ("RGB", "RGB"), 

3421 ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), 

3422 # shortcuts: 

3423 ((1, 1), f"{_ENDIAN}i4"): ("I", "I"), 

3424 ((1, 1), f"{_ENDIAN}f4"): ("F", "F"), 

3425} 

3426 

3427 

3428def _decompression_bomb_check(size: tuple[int, int]) -> None: 

3429 if MAX_IMAGE_PIXELS is None: 

3430 return 

3431 

3432 pixels = max(1, size[0]) * max(1, size[1]) 

3433 

3434 if pixels > 2 * MAX_IMAGE_PIXELS: 

3435 msg = ( 

3436 f"Image size ({pixels} pixels) exceeds limit of {2 * MAX_IMAGE_PIXELS} " 

3437 "pixels, could be decompression bomb DOS attack." 

3438 ) 

3439 raise DecompressionBombError(msg) 

3440 

3441 if pixels > MAX_IMAGE_PIXELS: 

3442 warnings.warn( 

3443 f"Image size ({pixels} pixels) exceeds limit of {MAX_IMAGE_PIXELS} pixels, " 

3444 "could be decompression bomb DOS attack.", 

3445 DecompressionBombWarning, 

3446 ) 

3447 

3448 

3449def open( 

3450 fp: StrOrBytesPath | IO[bytes], 

3451 mode: Literal["r"] = "r", 

3452 formats: list[str] | tuple[str, ...] | None = None, 

3453) -> ImageFile.ImageFile: 

3454 """ 

3455 Opens and identifies the given image file. 

3456 

3457 This is a lazy operation; this function identifies the file, but 

3458 the file remains open and the actual image data is not read from 

3459 the file until you try to process the data (or call the 

3460 :py:meth:`~PIL.Image.Image.load` method). See 

3461 :py:func:`~PIL.Image.new`. See :ref:`file-handling`. 

3462 

3463 :param fp: A filename (string), os.PathLike object or a file object. 

3464 The file object must implement ``file.read``, 

3465 ``file.seek``, and ``file.tell`` methods, 

3466 and be opened in binary mode. The file object will also seek to zero 

3467 before reading. 

3468 :param mode: The mode. If given, this argument must be "r". 

3469 :param formats: A list or tuple of formats to attempt to load the file in. 

3470 This can be used to restrict the set of formats checked. 

3471 Pass ``None`` to try all supported formats. You can print the set of 

3472 available formats by running ``python3 -m PIL`` or using 

3473 the :py:func:`PIL.features.pilinfo` function. 

3474 :returns: An :py:class:`~PIL.Image.Image` object. 

3475 :exception FileNotFoundError: If the file cannot be found. 

3476 :exception PIL.UnidentifiedImageError: If the image cannot be opened and 

3477 identified. 

3478 :exception ValueError: If the ``mode`` is not "r", or if a ``StringIO`` 

3479 instance is used for ``fp``. 

3480 :exception TypeError: If ``formats`` is not ``None``, a list or a tuple. 

3481 """ 

3482 

3483 if mode != "r": 

3484 msg = f"bad mode {repr(mode)}" # type: ignore[unreachable] 

3485 raise ValueError(msg) 

3486 elif isinstance(fp, io.StringIO): 

3487 msg = ( # type: ignore[unreachable] 

3488 "StringIO cannot be used to open an image. " 

3489 "Binary data must be used instead." 

3490 ) 

3491 raise ValueError(msg) 

3492 

3493 if formats is None: 

3494 formats = ID 

3495 elif not isinstance(formats, (list, tuple)): 

3496 msg = "formats must be a list or tuple" # type: ignore[unreachable] 

3497 raise TypeError(msg) 

3498 

3499 exclusive_fp = False 

3500 filename: str | bytes = "" 

3501 if is_path(fp): 

3502 filename = os.fspath(fp) 

3503 

3504 if filename: 

3505 fp = builtins.open(filename, "rb") 

3506 exclusive_fp = True 

3507 else: 

3508 fp = cast(IO[bytes], fp) 

3509 

3510 try: 

3511 fp.seek(0) 

3512 except (AttributeError, io.UnsupportedOperation): 

3513 fp = io.BytesIO(fp.read()) 

3514 exclusive_fp = True 

3515 

3516 prefix = fp.read(16) 

3517 

3518 preinit() 

3519 

3520 warning_messages: list[str] = [] 

3521 

3522 def _open_core( 

3523 fp: IO[bytes], 

3524 filename: str | bytes, 

3525 prefix: bytes, 

3526 formats: list[str] | tuple[str, ...], 

3527 ) -> ImageFile.ImageFile | None: 

3528 for i in formats: 

3529 i = i.upper() 

3530 if i not in OPEN: 

3531 init() 

3532 try: 

3533 factory, accept = OPEN[i] 

3534 result = not accept or accept(prefix) 

3535 if isinstance(result, str): 

3536 warning_messages.append(result) 

3537 elif result: 

3538 fp.seek(0) 

3539 im = factory(fp, filename) 

3540 _decompression_bomb_check(im.size) 

3541 return im 

3542 except (SyntaxError, IndexError, TypeError, struct.error) as e: 

3543 if WARN_POSSIBLE_FORMATS: 

3544 warning_messages.append(i + " opening failed. " + str(e)) 

3545 except BaseException: 

3546 if exclusive_fp: 

3547 fp.close() 

3548 raise 

3549 return None 

3550 

3551 im = _open_core(fp, filename, prefix, formats) 

3552 

3553 if im is None and formats is ID: 

3554 checked_formats = ID.copy() 

3555 if init(): 

3556 im = _open_core( 

3557 fp, 

3558 filename, 

3559 prefix, 

3560 tuple(format for format in formats if format not in checked_formats), 

3561 ) 

3562 

3563 if im: 

3564 im._exclusive_fp = exclusive_fp 

3565 return im 

3566 

3567 if exclusive_fp: 

3568 fp.close() 

3569 for message in warning_messages: 

3570 warnings.warn(message) 

3571 msg = "cannot identify image file %r" % (filename if filename else fp) 

3572 raise UnidentifiedImageError(msg) 

3573 

3574 

3575# 

3576# Image processing. 

3577 

3578 

3579def alpha_composite(im1: Image, im2: Image) -> Image: 

3580 """ 

3581 Alpha composite im2 over im1. 

3582 

3583 :param im1: The first image. Must have mode RGBA. 

3584 :param im2: The second image. Must have mode RGBA, and the same size as 

3585 the first image. 

3586 :returns: An :py:class:`~PIL.Image.Image` object. 

3587 """ 

3588 

3589 im1.load() 

3590 im2.load() 

3591 return im1._new(core.alpha_composite(im1.im, im2.im)) 

3592 

3593 

3594def blend(im1: Image, im2: Image, alpha: float) -> Image: 

3595 """ 

3596 Creates a new image by interpolating between two input images, using 

3597 a constant alpha:: 

3598 

3599 out = image1 * (1.0 - alpha) + image2 * alpha 

3600 

3601 :param im1: The first image. 

3602 :param im2: The second image. Must have the same mode and size as 

3603 the first image. 

3604 :param alpha: The interpolation alpha factor. If alpha is 0.0, a 

3605 copy of the first image is returned. If alpha is 1.0, a copy of 

3606 the second image is returned. There are no restrictions on the 

3607 alpha value. If necessary, the result is clipped to fit into 

3608 the allowed output range. 

3609 :returns: An :py:class:`~PIL.Image.Image` object. 

3610 """ 

3611 

3612 im1.load() 

3613 im2.load() 

3614 return im1._new(core.blend(im1.im, im2.im, alpha)) 

3615 

3616 

3617def composite(image1: Image, image2: Image, mask: Image) -> Image: 

3618 """ 

3619 Create composite image by blending images using a transparency mask. 

3620 

3621 :param image1: The first image. 

3622 :param image2: The second image. Must have the same mode and 

3623 size as the first image. 

3624 :param mask: A mask image. This image can have mode 

3625 "1", "L", or "RGBA", and must have the same size as the 

3626 other two images. 

3627 """ 

3628 

3629 image = image2.copy() 

3630 image.paste(image1, None, mask) 

3631 return image 

3632 

3633 

3634def eval(image: Image, *args: Callable[[int], float]) -> Image: 

3635 """ 

3636 Applies the function (which should take one argument) to each pixel 

3637 in the given image. If the image has more than one band, the same 

3638 function is applied to each band. Note that the function is 

3639 evaluated once for each possible pixel value, so you cannot use 

3640 random components or other generators. 

3641 

3642 :param image: The input image. 

3643 :param function: A function object, taking one integer argument. 

3644 :returns: An :py:class:`~PIL.Image.Image` object. 

3645 """ 

3646 

3647 return image.point(args[0]) 

3648 

3649 

3650def merge(mode: str, bands: Sequence[Image]) -> Image: 

3651 """ 

3652 Merge a set of single band images into a new multiband image. 

3653 

3654 :param mode: The mode to use for the output image. See: 

3655 :ref:`concept-modes`. 

3656 :param bands: A sequence containing one single-band image for 

3657 each band in the output image. All bands must have the 

3658 same size. 

3659 :returns: An :py:class:`~PIL.Image.Image` object. 

3660 """ 

3661 

3662 if getmodebands(mode) != len(bands) or "*" in mode: 

3663 msg = "wrong number of bands" 

3664 raise ValueError(msg) 

3665 for band in bands[1:]: 

3666 if band.mode != getmodetype(mode): 

3667 msg = "mode mismatch" 

3668 raise ValueError(msg) 

3669 if band.size != bands[0].size: 

3670 msg = "size mismatch" 

3671 raise ValueError(msg) 

3672 for band in bands: 

3673 band.load() 

3674 return bands[0]._new(core.merge(mode, *[b.im for b in bands])) 

3675 

3676 

3677# -------------------------------------------------------------------- 

3678# Plugin registry 

3679 

3680 

3681def register_open( 

3682 id: str, 

3683 factory: ( 

3684 Callable[[IO[bytes], str | bytes], ImageFile.ImageFile] 

3685 | type[ImageFile.ImageFile] 

3686 ), 

3687 accept: Callable[[bytes], bool | str] | None = None, 

3688) -> None: 

3689 """ 

3690 Register an image file plugin. This function should not be used 

3691 in application code. 

3692 

3693 :param id: An image format identifier. 

3694 :param factory: An image file factory method. 

3695 :param accept: An optional function that can be used to quickly 

3696 reject images having another format. 

3697 """ 

3698 id = id.upper() 

3699 if id not in ID: 

3700 ID.append(id) 

3701 OPEN[id] = factory, accept 

3702 

3703 

3704def register_mime(id: str, mimetype: str) -> None: 

3705 """ 

3706 Registers an image MIME type by populating ``Image.MIME``. This function 

3707 should not be used in application code. 

3708 

3709 ``Image.MIME`` provides a mapping from image format identifiers to mime 

3710 formats, but :py:meth:`~PIL.ImageFile.ImageFile.get_format_mimetype` can 

3711 provide a different result for specific images. 

3712 

3713 :param id: An image format identifier. 

3714 :param mimetype: The image MIME type for this format. 

3715 """ 

3716 MIME[id.upper()] = mimetype 

3717 

3718 

3719def register_save( 

3720 id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] 

3721) -> None: 

3722 """ 

3723 Registers an image save function. This function should not be 

3724 used in application code. 

3725 

3726 :param id: An image format identifier. 

3727 :param driver: A function to save images in this format. 

3728 """ 

3729 SAVE[id.upper()] = driver 

3730 

3731 

3732def register_save_all( 

3733 id: str, driver: Callable[[Image, IO[bytes], str | bytes], None] 

3734) -> None: 

3735 """ 

3736 Registers an image function to save all the frames 

3737 of a multiframe format. This function should not be 

3738 used in application code. 

3739 

3740 :param id: An image format identifier. 

3741 :param driver: A function to save images in this format. 

3742 """ 

3743 SAVE_ALL[id.upper()] = driver 

3744 

3745 

3746def register_extension(id: str, extension: str) -> None: 

3747 """ 

3748 Registers an image extension. This function should not be 

3749 used in application code. 

3750 

3751 :param id: An image format identifier. 

3752 :param extension: An extension used for this format. 

3753 """ 

3754 EXTENSION[extension.lower()] = id.upper() 

3755 

3756 

3757def register_extensions(id: str, extensions: list[str]) -> None: 

3758 """ 

3759 Registers image extensions. This function should not be 

3760 used in application code. 

3761 

3762 :param id: An image format identifier. 

3763 :param extensions: A list of extensions used for this format. 

3764 """ 

3765 for extension in extensions: 

3766 register_extension(id, extension) 

3767 

3768 

3769def registered_extensions() -> dict[str, str]: 

3770 """ 

3771 Returns a dictionary containing all file extensions belonging 

3772 to registered plugins 

3773 """ 

3774 init() 

3775 return EXTENSION 

3776 

3777 

3778def register_decoder(name: str, decoder: type[ImageFile.PyDecoder]) -> None: 

3779 """ 

3780 Registers an image decoder. This function should not be 

3781 used in application code. 

3782 

3783 :param name: The name of the decoder 

3784 :param decoder: An ImageFile.PyDecoder object 

3785 

3786 .. versionadded:: 4.1.0 

3787 """ 

3788 DECODERS[name] = decoder 

3789 

3790 

3791def register_encoder(name: str, encoder: type[ImageFile.PyEncoder]) -> None: 

3792 """ 

3793 Registers an image encoder. This function should not be 

3794 used in application code. 

3795 

3796 :param name: The name of the encoder 

3797 :param encoder: An ImageFile.PyEncoder object 

3798 

3799 .. versionadded:: 4.1.0 

3800 """ 

3801 ENCODERS[name] = encoder 

3802 

3803 

3804# -------------------------------------------------------------------- 

3805# Simple display support. 

3806 

3807 

3808def _show(image: Image, **options: Any) -> None: 

3809 from . import ImageShow 

3810 

3811 ImageShow.show(image, **options) 

3812 

3813 

3814# -------------------------------------------------------------------- 

3815# Effects 

3816 

3817 

3818def effect_mandelbrot( 

3819 size: tuple[int, int], extent: tuple[float, float, float, float], quality: int 

3820) -> Image: 

3821 """ 

3822 Generate a Mandelbrot set covering the given extent. 

3823 

3824 :param size: The requested size in pixels, as a 2-tuple: 

3825 (width, height). 

3826 :param extent: The extent to cover, as a 4-tuple: 

3827 (x0, y0, x1, y1). 

3828 :param quality: Quality. 

3829 """ 

3830 return Image()._new(core.effect_mandelbrot(size, extent, quality)) 

3831 

3832 

3833def effect_noise(size: tuple[int, int], sigma: float) -> Image: 

3834 """ 

3835 Generate Gaussian noise centered around 128. 

3836 

3837 :param size: The requested size in pixels, as a 2-tuple: 

3838 (width, height). 

3839 :param sigma: Standard deviation of noise. 

3840 """ 

3841 return Image()._new(core.effect_noise(size, sigma)) 

3842 

3843 

3844def linear_gradient(mode: str) -> Image: 

3845 """ 

3846 Generate 256x256 linear gradient from black to white, top to bottom. 

3847 

3848 :param mode: Input mode. 

3849 """ 

3850 return Image()._new(core.linear_gradient(mode)) 

3851 

3852 

3853def radial_gradient(mode: str) -> Image: 

3854 """ 

3855 Generate 256x256 radial gradient from black to white, centre to edge. 

3856 

3857 :param mode: Input mode. 

3858 """ 

3859 return Image()._new(core.radial_gradient(mode)) 

3860 

3861 

3862# -------------------------------------------------------------------- 

3863# Resources 

3864 

3865 

3866def _apply_env_variables(env: dict[str, str] | None = None) -> None: 

3867 env_dict = env if env is not None else os.environ 

3868 

3869 for var_name, setter in [ 

3870 ("PILLOW_ALIGNMENT", core.set_alignment), 

3871 ("PILLOW_BLOCK_SIZE", core.set_block_size), 

3872 ("PILLOW_BLOCKS_MAX", core.set_blocks_max), 

3873 ]: 

3874 if var_name not in env_dict: 

3875 continue 

3876 

3877 var = env_dict[var_name].lower() 

3878 

3879 units = 1 

3880 for postfix, mul in [("k", 1024), ("m", 1024 * 1024)]: 

3881 if var.endswith(postfix): 

3882 units = mul 

3883 var = var[: -len(postfix)] 

3884 

3885 try: 

3886 var_int = int(var) * units 

3887 except ValueError: 

3888 warnings.warn(f"{var_name} is not int") 

3889 continue 

3890 

3891 try: 

3892 setter(var_int) 

3893 except ValueError as e: 

3894 warnings.warn(f"{var_name}: {e}") 

3895 

3896 

3897_apply_env_variables() 

3898atexit.register(core.clear_cache) 

3899 

3900 

3901if TYPE_CHECKING: 

3902 _ExifBase = MutableMapping[int, Any] 

3903else: 

3904 _ExifBase = MutableMapping 

3905 

3906 

3907class Exif(_ExifBase): 

3908 """ 

3909 This class provides read and write access to EXIF image data:: 

3910 

3911 from PIL import Image 

3912 im = Image.open("exif.png") 

3913 exif = im.getexif() # Returns an instance of this class 

3914 

3915 Information can be read and written, iterated over or deleted:: 

3916 

3917 print(exif[274]) # 1 

3918 exif[274] = 2 

3919 for k, v in exif.items(): 

3920 print("Tag", k, "Value", v) # Tag 274 Value 2 

3921 del exif[274] 

3922 

3923 To access information beyond IFD0, :py:meth:`~PIL.Image.Exif.get_ifd` 

3924 returns a dictionary:: 

3925 

3926 from PIL import ExifTags 

3927 im = Image.open("exif_gps.jpg") 

3928 exif = im.getexif() 

3929 gps_ifd = exif.get_ifd(ExifTags.IFD.GPSInfo) 

3930 print(gps_ifd) 

3931 

3932 Other IFDs include ``ExifTags.IFD.Exif``, ``ExifTags.IFD.MakerNote``, 

3933 ``ExifTags.IFD.Interop`` and ``ExifTags.IFD.IFD1``. 

3934 

3935 :py:mod:`~PIL.ExifTags` also has enum classes to provide names for data:: 

3936 

3937 print(exif[ExifTags.Base.Software]) # PIL 

3938 print(gps_ifd[ExifTags.GPS.GPSDateStamp]) # 1999:99:99 99:99:99 

3939 """ 

3940 

3941 endian: str | None = None 

3942 bigtiff = False 

3943 _loaded = False 

3944 

3945 def __init__(self) -> None: 

3946 self._data: dict[int, Any] = {} 

3947 self._hidden_data: dict[int, Any] = {} 

3948 self._ifds: dict[int, dict[int, Any]] = {} 

3949 self._info: TiffImagePlugin.ImageFileDirectory_v2 | None = None 

3950 self._loaded_exif: bytes | None = None 

3951 

3952 def _fixup(self, value: Any) -> Any: 

3953 try: 

3954 if len(value) == 1 and isinstance(value, tuple): 

3955 return value[0] 

3956 except Exception: 

3957 pass 

3958 return value 

3959 

3960 def _fixup_dict(self, src_dict: dict[int, Any]) -> dict[int, Any]: 

3961 # Helper function 

3962 # returns a dict with any single item tuples/lists as individual values 

3963 return {k: self._fixup(v) for k, v in src_dict.items()} 

3964 

3965 def _get_ifd_dict( 

3966 self, offset: int, group: int | None = None 

3967 ) -> dict[int, Any] | None: 

3968 try: 

3969 # an offset pointer to the location of the nested embedded IFD. 

3970 # It should be a long, but may be corrupted. 

3971 self.fp.seek(offset) 

3972 except (KeyError, TypeError): 

3973 return None 

3974 else: 

3975 from . import TiffImagePlugin 

3976 

3977 info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) 

3978 info.load(self.fp) 

3979 return self._fixup_dict(dict(info)) 

3980 

3981 def _get_head(self) -> bytes: 

3982 version = b"\x2b" if self.bigtiff else b"\x2a" 

3983 if self.endian == "<": 

3984 head = b"II" + version + b"\x00" + o32le(8) 

3985 else: 

3986 head = b"MM\x00" + version + o32be(8) 

3987 if self.bigtiff: 

3988 head += o32le(8) if self.endian == "<" else o32be(8) 

3989 head += b"\x00\x00\x00\x00" 

3990 return head 

3991 

3992 def load(self, data: bytes) -> None: 

3993 # Extract EXIF information. This is highly experimental, 

3994 # and is likely to be replaced with something better in a future 

3995 # version. 

3996 

3997 # The EXIF record consists of a TIFF file embedded in a JPEG 

3998 # application marker (!). 

3999 if data == self._loaded_exif: 

4000 return 

4001 self._loaded_exif = data 

4002 self._data.clear() 

4003 self._hidden_data.clear() 

4004 self._ifds.clear() 

4005 while data and data.startswith(b"Exif\x00\x00"): 

4006 data = data[6:] 

4007 if not data: 

4008 self._info = None 

4009 return 

4010 

4011 self.fp: IO[bytes] = io.BytesIO(data) 

4012 self.head = self.fp.read(8) 

4013 # process dictionary 

4014 from . import TiffImagePlugin 

4015 

4016 self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) 

4017 self.endian = self._info._endian 

4018 self.fp.seek(self._info.next) 

4019 self._info.load(self.fp) 

4020 

4021 def load_from_fp(self, fp: IO[bytes], offset: int | None = None) -> None: 

4022 self._loaded_exif = None 

4023 self._data.clear() 

4024 self._hidden_data.clear() 

4025 self._ifds.clear() 

4026 

4027 # process dictionary 

4028 from . import TiffImagePlugin 

4029 

4030 self.fp = fp 

4031 if offset is not None: 

4032 self.head = self._get_head() 

4033 else: 

4034 self.head = self.fp.read(8) 

4035 self._info = TiffImagePlugin.ImageFileDirectory_v2(self.head) 

4036 if self.endian is None: 

4037 self.endian = self._info._endian 

4038 if offset is None: 

4039 offset = self._info.next 

4040 self.fp.tell() 

4041 self.fp.seek(offset) 

4042 self._info.load(self.fp) 

4043 

4044 def _get_merged_dict(self) -> dict[int, Any]: 

4045 merged_dict = dict(self) 

4046 

4047 # get EXIF extension 

4048 if ExifTags.IFD.Exif in self: 

4049 ifd = self._get_ifd_dict(self[ExifTags.IFD.Exif], ExifTags.IFD.Exif) 

4050 if ifd: 

4051 merged_dict.update(ifd) 

4052 

4053 # GPS 

4054 if ExifTags.IFD.GPSInfo in self: 

4055 merged_dict[ExifTags.IFD.GPSInfo] = self._get_ifd_dict( 

4056 self[ExifTags.IFD.GPSInfo], ExifTags.IFD.GPSInfo 

4057 ) 

4058 

4059 return merged_dict 

4060 

4061 def tobytes(self, offset: int = 8) -> bytes: 

4062 from . import TiffImagePlugin 

4063 

4064 head = self._get_head() 

4065 ifd = TiffImagePlugin.ImageFileDirectory_v2(ifh=head) 

4066 for tag, ifd_dict in self._ifds.items(): 

4067 if tag not in self: 

4068 ifd[tag] = ifd_dict 

4069 for tag, value in self.items(): 

4070 if tag in [ 

4071 ExifTags.IFD.Exif, 

4072 ExifTags.IFD.GPSInfo, 

4073 ] and not isinstance(value, dict): 

4074 value = self.get_ifd(tag) 

4075 if ( 

4076 tag == ExifTags.IFD.Exif 

4077 and ExifTags.IFD.Interop in value 

4078 and not isinstance(value[ExifTags.IFD.Interop], dict) 

4079 ): 

4080 value = value.copy() 

4081 value[ExifTags.IFD.Interop] = self.get_ifd(ExifTags.IFD.Interop) 

4082 ifd[tag] = value 

4083 return b"Exif\x00\x00" + head + ifd.tobytes(offset) 

4084 

4085 def get_ifd(self, tag: int) -> dict[int, Any]: 

4086 if tag not in self._ifds: 

4087 if tag == ExifTags.IFD.IFD1: 

4088 if self._info is not None and self._info.next != 0: 

4089 ifd = self._get_ifd_dict(self._info.next) 

4090 if ifd is not None: 

4091 self._ifds[tag] = ifd 

4092 elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: 

4093 offset = self._hidden_data.get(tag, self.get(tag)) 

4094 if offset is not None: 

4095 ifd = self._get_ifd_dict(offset, tag) 

4096 if ifd is not None: 

4097 self._ifds[tag] = ifd 

4098 elif tag in [ExifTags.IFD.Interop, ExifTags.IFD.MakerNote]: 

4099 if ExifTags.IFD.Exif not in self._ifds: 

4100 self.get_ifd(ExifTags.IFD.Exif) 

4101 tag_data = self._ifds[ExifTags.IFD.Exif][tag] 

4102 if tag == ExifTags.IFD.MakerNote: 

4103 from .TiffImagePlugin import ImageFileDirectory_v2 

4104 

4105 if tag_data.startswith(b"FUJIFILM"): 

4106 ifd_offset = i32le(tag_data, 8) 

4107 ifd_data = tag_data[ifd_offset:] 

4108 

4109 makernote = {} 

4110 for i in range(struct.unpack("<H", ifd_data[:2])[0]): 

4111 ifd_tag, typ, count, data = struct.unpack( 

4112 "<HHL4s", ifd_data[i * 12 + 2 : (i + 1) * 12 + 2] 

4113 ) 

4114 try: 

4115 ( 

4116 unit_size, 

4117 handler, 

4118 ) = ImageFileDirectory_v2._load_dispatch[typ] 

4119 except KeyError: 

4120 continue 

4121 size = count * unit_size 

4122 if size > 4: 

4123 (offset,) = struct.unpack("<L", data) 

4124 data = ifd_data[offset - 12 : offset + size - 12] 

4125 else: 

4126 data = data[:size] 

4127 

4128 if len(data) != size: 

4129 warnings.warn( 

4130 "Possibly corrupt EXIF MakerNote data. " 

4131 f"Expecting to read {size} bytes but only got " 

4132 f"{len(data)}. Skipping tag {ifd_tag}" 

4133 ) 

4134 continue 

4135 

4136 if not data: 

4137 continue 

4138 

4139 makernote[ifd_tag] = handler( 

4140 ImageFileDirectory_v2(), data, False 

4141 ) 

4142 self._ifds[tag] = dict(self._fixup_dict(makernote)) 

4143 elif self.get(0x010F) == "Nintendo": 

4144 makernote = {} 

4145 for i in range(struct.unpack(">H", tag_data[:2])[0]): 

4146 ifd_tag, typ, count, data = struct.unpack( 

4147 ">HHL4s", tag_data[i * 12 + 2 : (i + 1) * 12 + 2] 

4148 ) 

4149 if ifd_tag == 0x1101: 

4150 # CameraInfo 

4151 (offset,) = struct.unpack(">L", data) 

4152 self.fp.seek(offset) 

4153 

4154 camerainfo: dict[str, int | bytes] = { 

4155 "ModelID": self.fp.read(4) 

4156 } 

4157 

4158 self.fp.read(4) 

4159 # Seconds since 2000 

4160 camerainfo["TimeStamp"] = i32le(self.fp.read(12)) 

4161 

4162 self.fp.read(4) 

4163 camerainfo["InternalSerialNumber"] = self.fp.read(4) 

4164 

4165 self.fp.read(12) 

4166 parallax = self.fp.read(4) 

4167 handler = ImageFileDirectory_v2._load_dispatch[ 

4168 TiffTags.FLOAT 

4169 ][1] 

4170 camerainfo["Parallax"] = handler( 

4171 ImageFileDirectory_v2(), parallax, False 

4172 )[0] 

4173 

4174 self.fp.read(4) 

4175 camerainfo["Category"] = self.fp.read(2) 

4176 

4177 makernote = {0x1101: camerainfo} 

4178 self._ifds[tag] = makernote 

4179 else: 

4180 # Interop 

4181 ifd = self._get_ifd_dict(tag_data, tag) 

4182 if ifd is not None: 

4183 self._ifds[tag] = ifd 

4184 ifd = self._ifds.setdefault(tag, {}) 

4185 if tag == ExifTags.IFD.Exif and self._hidden_data: 

4186 ifd = { 

4187 k: v 

4188 for (k, v) in ifd.items() 

4189 if k not in (ExifTags.IFD.Interop, ExifTags.IFD.MakerNote) 

4190 } 

4191 return ifd 

4192 

4193 def hide_offsets(self) -> None: 

4194 for tag in (ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo): 

4195 if tag in self: 

4196 self._hidden_data[tag] = self[tag] 

4197 del self[tag] 

4198 

4199 def __str__(self) -> str: 

4200 if self._info is not None: 

4201 # Load all keys into self._data 

4202 for tag in self._info: 

4203 self[tag] 

4204 

4205 return str(self._data) 

4206 

4207 def __len__(self) -> int: 

4208 keys = set(self._data) 

4209 if self._info is not None: 

4210 keys.update(self._info) 

4211 return len(keys) 

4212 

4213 def __getitem__(self, tag: int) -> Any: 

4214 if self._info is not None and tag not in self._data and tag in self._info: 

4215 self._data[tag] = self._fixup(self._info[tag]) 

4216 del self._info[tag] 

4217 return self._data[tag] 

4218 

4219 def __contains__(self, tag: object) -> bool: 

4220 return tag in self._data or (self._info is not None and tag in self._info) 

4221 

4222 def __setitem__(self, tag: int, value: Any) -> None: 

4223 if self._info is not None and tag in self._info: 

4224 del self._info[tag] 

4225 self._data[tag] = value 

4226 

4227 def __delitem__(self, tag: int) -> None: 

4228 if self._info is not None and tag in self._info: 

4229 del self._info[tag] 

4230 else: 

4231 del self._data[tag] 

4232 

4233 def __iter__(self) -> Iterator[int]: 

4234 keys = set(self._data) 

4235 if self._info is not None: 

4236 keys.update(self._info) 

4237 return iter(keys)