Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/ImageCms.py: 35%
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
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
1# The Python Imaging Library.
2# $Id$
4# Optional color management support, based on Kevin Cazabon's PyCMS
5# library.
7# Originally released under LGPL. Graciously donated to PIL in
8# March 2009, for distribution under the standard PIL license
10# History:
12# 2009-03-08 fl Added to PIL.
14# Copyright (C) 2002-2003 Kevin Cazabon
15# Copyright (c) 2009 by Fredrik Lundh
16# Copyright (c) 2013 by Eric Soroos
18# See the README file for information on usage and redistribution. See
19# below for the original description.
20from __future__ import annotations
22import operator
23import sys
24from enum import IntEnum, IntFlag
25from functools import reduce
26from typing import Any, Literal, SupportsFloat, SupportsInt, Union
28from . import Image, __version__
29from ._deprecate import deprecate
30from ._typing import SupportsRead
32try:
33 from . import _imagingcms as core
35 _CmsProfileCompatible = Union[
36 str, SupportsRead[bytes], core.CmsProfile, "ImageCmsProfile"
37 ]
38except ImportError as ex:
39 # Allow error import for doc purposes, but error out when accessing
40 # anything in core.
41 from ._util import DeferredError
43 core = DeferredError.new(ex)
45_DESCRIPTION = """
46pyCMS
48 a Python / PIL interface to the littleCMS ICC Color Management System
49 Copyright (C) 2002-2003 Kevin Cazabon
50 kevin@cazabon.com
51 https://www.cazabon.com
53 pyCMS home page: https://www.cazabon.com/pyCMS
54 littleCMS home page: https://www.littlecms.com
55 (littleCMS is Copyright (C) 1998-2001 Marti Maria)
57 Originally released under LGPL. Graciously donated to PIL in
58 March 2009, for distribution under the standard PIL license
60 The pyCMS.py module provides a "clean" interface between Python/PIL and
61 pyCMSdll, taking care of some of the more complex handling of the direct
62 pyCMSdll functions, as well as error-checking and making sure that all
63 relevant data is kept together.
65 While it is possible to call pyCMSdll functions directly, it's not highly
66 recommended.
68 Version History:
70 1.0.0 pil Oct 2013 Port to LCMS 2.
72 0.1.0 pil mod March 10, 2009
74 Renamed display profile to proof profile. The proof
75 profile is the profile of the device that is being
76 simulated, not the profile of the device which is
77 actually used to display/print the final simulation
78 (that'd be the output profile) - also see LCMSAPI.txt
79 input colorspace -> using 'renderingIntent' -> proof
80 colorspace -> using 'proofRenderingIntent' -> output
81 colorspace
83 Added LCMS FLAGS support.
84 Added FLAGS["SOFTPROOFING"] as default flag for
85 buildProofTransform (otherwise the proof profile/intent
86 would be ignored).
88 0.1.0 pil March 2009 - added to PIL, as PIL.ImageCms
90 0.0.2 alpha Jan 6, 2002
92 Added try/except statements around type() checks of
93 potential CObjects... Python won't let you use type()
94 on them, and raises a TypeError (stupid, if you ask
95 me!)
97 Added buildProofTransformFromOpenProfiles() function.
98 Additional fixes in DLL, see DLL code for details.
100 0.0.1 alpha first public release, Dec. 26, 2002
102 Known to-do list with current version (of Python interface, not pyCMSdll):
104 none
106"""
108_VERSION = "1.0.0 pil"
111def __getattr__(name: str) -> Any:
112 if name == "DESCRIPTION":
113 deprecate("PIL.ImageCms.DESCRIPTION", 12)
114 return _DESCRIPTION
115 elif name == "VERSION":
116 deprecate("PIL.ImageCms.VERSION", 12)
117 return _VERSION
118 elif name == "FLAGS":
119 deprecate("PIL.ImageCms.FLAGS", 12, "PIL.ImageCms.Flags")
120 return _FLAGS
121 msg = f"module '{__name__}' has no attribute '{name}'"
122 raise AttributeError(msg)
125# --------------------------------------------------------------------.
128#
129# intent/direction values
132class Intent(IntEnum):
133 PERCEPTUAL = 0
134 RELATIVE_COLORIMETRIC = 1
135 SATURATION = 2
136 ABSOLUTE_COLORIMETRIC = 3
139class Direction(IntEnum):
140 INPUT = 0
141 OUTPUT = 1
142 PROOF = 2
145#
146# flags
149class Flags(IntFlag):
150 """Flags and documentation are taken from ``lcms2.h``."""
152 NONE = 0
153 NOCACHE = 0x0040
154 """Inhibit 1-pixel cache"""
155 NOOPTIMIZE = 0x0100
156 """Inhibit optimizations"""
157 NULLTRANSFORM = 0x0200
158 """Don't transform anyway"""
159 GAMUTCHECK = 0x1000
160 """Out of Gamut alarm"""
161 SOFTPROOFING = 0x4000
162 """Do softproofing"""
163 BLACKPOINTCOMPENSATION = 0x2000
164 NOWHITEONWHITEFIXUP = 0x0004
165 """Don't fix scum dot"""
166 HIGHRESPRECALC = 0x0400
167 """Use more memory to give better accuracy"""
168 LOWRESPRECALC = 0x0800
169 """Use less memory to minimize resources"""
170 # this should be 8BITS_DEVICELINK, but that is not a valid name in Python:
171 USE_8BITS_DEVICELINK = 0x0008
172 """Create 8 bits devicelinks"""
173 GUESSDEVICECLASS = 0x0020
174 """Guess device class (for ``transform2devicelink``)"""
175 KEEP_SEQUENCE = 0x0080
176 """Keep profile sequence for devicelink creation"""
177 FORCE_CLUT = 0x0002
178 """Force CLUT optimization"""
179 CLUT_POST_LINEARIZATION = 0x0001
180 """create postlinearization tables if possible"""
181 CLUT_PRE_LINEARIZATION = 0x0010
182 """create prelinearization tables if possible"""
183 NONEGATIVES = 0x8000
184 """Prevent negative numbers in floating point transforms"""
185 COPY_ALPHA = 0x04000000
186 """Alpha channels are copied on ``cmsDoTransform()``"""
187 NODEFAULTRESOURCEDEF = 0x01000000
189 _GRIDPOINTS_1 = 1 << 16
190 _GRIDPOINTS_2 = 2 << 16
191 _GRIDPOINTS_4 = 4 << 16
192 _GRIDPOINTS_8 = 8 << 16
193 _GRIDPOINTS_16 = 16 << 16
194 _GRIDPOINTS_32 = 32 << 16
195 _GRIDPOINTS_64 = 64 << 16
196 _GRIDPOINTS_128 = 128 << 16
198 @staticmethod
199 def GRIDPOINTS(n: int) -> Flags:
200 """
201 Fine-tune control over number of gridpoints
203 :param n: :py:class:`int` in range ``0 <= n <= 255``
204 """
205 return Flags.NONE | ((n & 0xFF) << 16)
208_MAX_FLAG = reduce(operator.or_, Flags)
211_FLAGS = {
212 "MATRIXINPUT": 1,
213 "MATRIXOUTPUT": 2,
214 "MATRIXONLY": (1 | 2),
215 "NOWHITEONWHITEFIXUP": 4, # Don't hot fix scum dot
216 # Don't create prelinearization tables on precalculated transforms
217 # (internal use):
218 "NOPRELINEARIZATION": 16,
219 "GUESSDEVICECLASS": 32, # Guess device class (for transform2devicelink)
220 "NOTCACHE": 64, # Inhibit 1-pixel cache
221 "NOTPRECALC": 256,
222 "NULLTRANSFORM": 512, # Don't transform anyway
223 "HIGHRESPRECALC": 1024, # Use more memory to give better accuracy
224 "LOWRESPRECALC": 2048, # Use less memory to minimize resources
225 "WHITEBLACKCOMPENSATION": 8192,
226 "BLACKPOINTCOMPENSATION": 8192,
227 "GAMUTCHECK": 4096, # Out of Gamut alarm
228 "SOFTPROOFING": 16384, # Do softproofing
229 "PRESERVEBLACK": 32768, # Black preservation
230 "NODEFAULTRESOURCEDEF": 16777216, # CRD special
231 "GRIDPOINTS": lambda n: (n & 0xFF) << 16, # Gridpoints
232}
235# --------------------------------------------------------------------.
236# Experimental PIL-level API
237# --------------------------------------------------------------------.
239##
240# Profile.
243class ImageCmsProfile:
244 def __init__(self, profile: str | SupportsRead[bytes] | core.CmsProfile) -> None:
245 """
246 :param profile: Either a string representing a filename,
247 a file like object containing a profile or a
248 low-level profile object
250 """
252 if isinstance(profile, str):
253 if sys.platform == "win32":
254 profile_bytes_path = profile.encode()
255 try:
256 profile_bytes_path.decode("ascii")
257 except UnicodeDecodeError:
258 with open(profile, "rb") as f:
259 self._set(core.profile_frombytes(f.read()))
260 return
261 self._set(core.profile_open(profile), profile)
262 elif hasattr(profile, "read"):
263 self._set(core.profile_frombytes(profile.read()))
264 elif isinstance(profile, core.CmsProfile):
265 self._set(profile)
266 else:
267 msg = "Invalid type for Profile" # type: ignore[unreachable]
268 raise TypeError(msg)
270 def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None:
271 self.profile = profile
272 self.filename = filename
273 self.product_name = None # profile.product_name
274 self.product_info = None # profile.product_info
276 def tobytes(self) -> bytes:
277 """
278 Returns the profile in a format suitable for embedding in
279 saved images.
281 :returns: a bytes object containing the ICC profile.
282 """
284 return core.profile_tobytes(self.profile)
287class ImageCmsTransform(Image.ImagePointHandler):
288 """
289 Transform. This can be used with the procedural API, or with the standard
290 :py:func:`~PIL.Image.Image.point` method.
292 Will return the output profile in the ``output.info['icc_profile']``.
293 """
295 def __init__(
296 self,
297 input: ImageCmsProfile,
298 output: ImageCmsProfile,
299 input_mode: str,
300 output_mode: str,
301 intent: Intent = Intent.PERCEPTUAL,
302 proof: ImageCmsProfile | None = None,
303 proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
304 flags: Flags = Flags.NONE,
305 ):
306 supported_modes = (
307 "RGB",
308 "RGBA",
309 "RGBX",
310 "CMYK",
311 "I;16",
312 "I;16L",
313 "I;16B",
314 "YCbCr",
315 "LAB",
316 "L",
317 "1",
318 )
319 for mode in (input_mode, output_mode):
320 if mode not in supported_modes:
321 deprecate(
322 mode,
323 12,
324 {
325 "L;16": "I;16 or I;16L",
326 "L:16B": "I;16B",
327 "YCCA": "YCbCr",
328 "YCC": "YCbCr",
329 }.get(mode),
330 )
331 if proof is None:
332 self.transform = core.buildTransform(
333 input.profile, output.profile, input_mode, output_mode, intent, flags
334 )
335 else:
336 self.transform = core.buildProofTransform(
337 input.profile,
338 output.profile,
339 proof.profile,
340 input_mode,
341 output_mode,
342 intent,
343 proof_intent,
344 flags,
345 )
346 # Note: inputMode and outputMode are for pyCMS compatibility only
347 self.input_mode = self.inputMode = input_mode
348 self.output_mode = self.outputMode = output_mode
350 self.output_profile = output
352 def point(self, im: Image.Image) -> Image.Image:
353 return self.apply(im)
355 def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image:
356 if imOut is None:
357 imOut = Image.new(self.output_mode, im.size, None)
358 self.transform.apply(im.getim(), imOut.getim())
359 imOut.info["icc_profile"] = self.output_profile.tobytes()
360 return imOut
362 def apply_in_place(self, im: Image.Image) -> Image.Image:
363 if im.mode != self.output_mode:
364 msg = "mode mismatch"
365 raise ValueError(msg) # wrong output mode
366 self.transform.apply(im.getim(), im.getim())
367 im.info["icc_profile"] = self.output_profile.tobytes()
368 return im
371def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None:
372 """
373 (experimental) Fetches the profile for the current display device.
375 :returns: ``None`` if the profile is not known.
376 """
378 if sys.platform != "win32":
379 return None
381 from . import ImageWin # type: ignore[unused-ignore, unreachable]
383 if isinstance(handle, ImageWin.HDC):
384 profile = core.get_display_profile_win32(int(handle), 1)
385 else:
386 profile = core.get_display_profile_win32(int(handle or 0))
387 if profile is None:
388 return None
389 return ImageCmsProfile(profile)
392# --------------------------------------------------------------------.
393# pyCMS compatible layer
394# --------------------------------------------------------------------.
397class PyCMSError(Exception):
398 """(pyCMS) Exception class.
399 This is used for all errors in the pyCMS API."""
401 pass
404def profileToProfile(
405 im: Image.Image,
406 inputProfile: _CmsProfileCompatible,
407 outputProfile: _CmsProfileCompatible,
408 renderingIntent: Intent = Intent.PERCEPTUAL,
409 outputMode: str | None = None,
410 inPlace: bool = False,
411 flags: Flags = Flags.NONE,
412) -> Image.Image | None:
413 """
414 (pyCMS) Applies an ICC transformation to a given image, mapping from
415 ``inputProfile`` to ``outputProfile``.
417 If the input or output profiles specified are not valid filenames, a
418 :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and
419 ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised.
420 If an error occurs during application of the profiles,
421 a :exc:`PyCMSError` will be raised.
422 If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS),
423 a :exc:`PyCMSError` will be raised.
425 This function applies an ICC transformation to im from ``inputProfile``'s
426 color space to ``outputProfile``'s color space using the specified rendering
427 intent to decide how to handle out-of-gamut colors.
429 ``outputMode`` can be used to specify that a color mode conversion is to
430 be done using these profiles, but the specified profiles must be able
431 to handle that mode. I.e., if converting im from RGB to CMYK using
432 profiles, the input profile must handle RGB data, and the output
433 profile must handle CMYK data.
435 :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...)
436 or Image.open(...), etc.)
437 :param inputProfile: String, as a valid filename path to the ICC input
438 profile you wish to use for this image, or a profile object
439 :param outputProfile: String, as a valid filename path to the ICC output
440 profile you wish to use for this image, or a profile object
441 :param renderingIntent: Integer (0-3) specifying the rendering intent you
442 wish to use for the transform
444 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
445 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
446 ImageCms.Intent.SATURATION = 2
447 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
449 see the pyCMS documentation for details on rendering intents and what
450 they do.
451 :param outputMode: A valid PIL mode for the output image (i.e. "RGB",
452 "CMYK", etc.). Note: if rendering the image "inPlace", outputMode
453 MUST be the same mode as the input, or omitted completely. If
454 omitted, the outputMode will be the same as the mode of the input
455 image (im.mode)
456 :param inPlace: Boolean. If ``True``, the original image is modified in-place,
457 and ``None`` is returned. If ``False`` (default), a new
458 :py:class:`~PIL.Image.Image` object is returned with the transform applied.
459 :param flags: Integer (0-...) specifying additional flags
460 :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on
461 the value of ``inPlace``
462 :exception PyCMSError:
463 """
465 if outputMode is None:
466 outputMode = im.mode
468 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
469 msg = "renderingIntent must be an integer between 0 and 3"
470 raise PyCMSError(msg)
472 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
473 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
474 raise PyCMSError(msg)
476 try:
477 if not isinstance(inputProfile, ImageCmsProfile):
478 inputProfile = ImageCmsProfile(inputProfile)
479 if not isinstance(outputProfile, ImageCmsProfile):
480 outputProfile = ImageCmsProfile(outputProfile)
481 transform = ImageCmsTransform(
482 inputProfile,
483 outputProfile,
484 im.mode,
485 outputMode,
486 renderingIntent,
487 flags=flags,
488 )
489 if inPlace:
490 transform.apply_in_place(im)
491 imOut = None
492 else:
493 imOut = transform.apply(im)
494 except (OSError, TypeError, ValueError) as v:
495 raise PyCMSError(v) from v
497 return imOut
500def getOpenProfile(
501 profileFilename: str | SupportsRead[bytes] | core.CmsProfile,
502) -> ImageCmsProfile:
503 """
504 (pyCMS) Opens an ICC profile file.
506 The PyCMSProfile object can be passed back into pyCMS for use in creating
507 transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
509 If ``profileFilename`` is not a valid filename for an ICC profile,
510 a :exc:`PyCMSError` will be raised.
512 :param profileFilename: String, as a valid filename path to the ICC profile
513 you wish to open, or a file-like object.
514 :returns: A CmsProfile class object.
515 :exception PyCMSError:
516 """
518 try:
519 return ImageCmsProfile(profileFilename)
520 except (OSError, TypeError, ValueError) as v:
521 raise PyCMSError(v) from v
524def buildTransform(
525 inputProfile: _CmsProfileCompatible,
526 outputProfile: _CmsProfileCompatible,
527 inMode: str,
528 outMode: str,
529 renderingIntent: Intent = Intent.PERCEPTUAL,
530 flags: Flags = Flags.NONE,
531) -> ImageCmsTransform:
532 """
533 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
534 ``outputProfile``. Use applyTransform to apply the transform to a given
535 image.
537 If the input or output profiles specified are not valid filenames, a
538 :exc:`PyCMSError` will be raised. If an error occurs during creation
539 of the transform, a :exc:`PyCMSError` will be raised.
541 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
542 (or by pyCMS), a :exc:`PyCMSError` will be raised.
544 This function builds and returns an ICC transform from the ``inputProfile``
545 to the ``outputProfile`` using the ``renderingIntent`` to determine what to do
546 with out-of-gamut colors. It will ONLY work for converting images that
547 are in ``inMode`` to images that are in ``outMode`` color format (PIL mode,
548 i.e. "RGB", "RGBA", "CMYK", etc.).
550 Building the transform is a fair part of the overhead in
551 ImageCms.profileToProfile(), so if you're planning on converting multiple
552 images using the same input/output settings, this can save you time.
553 Once you have a transform object, it can be used with
554 ImageCms.applyProfile() to convert images without the need to re-compute
555 the lookup table for the transform.
557 The reason pyCMS returns a class object rather than a handle directly
558 to the transform is that it needs to keep track of the PIL input/output
559 modes that the transform is meant for. These attributes are stored in
560 the ``inMode`` and ``outMode`` attributes of the object (which can be
561 manually overridden if you really want to, but I don't know of any
562 time that would be of use, or would even work).
564 :param inputProfile: String, as a valid filename path to the ICC input
565 profile you wish to use for this transform, or a profile object
566 :param outputProfile: String, as a valid filename path to the ICC output
567 profile you wish to use for this transform, or a profile object
568 :param inMode: String, as a valid PIL mode that the appropriate profile
569 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
570 :param outMode: String, as a valid PIL mode that the appropriate profile
571 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
572 :param renderingIntent: Integer (0-3) specifying the rendering intent you
573 wish to use for the transform
575 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
576 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
577 ImageCms.Intent.SATURATION = 2
578 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
580 see the pyCMS documentation for details on rendering intents and what
581 they do.
582 :param flags: Integer (0-...) specifying additional flags
583 :returns: A CmsTransform class object.
584 :exception PyCMSError:
585 """
587 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
588 msg = "renderingIntent must be an integer between 0 and 3"
589 raise PyCMSError(msg)
591 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
592 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
593 raise PyCMSError(msg)
595 try:
596 if not isinstance(inputProfile, ImageCmsProfile):
597 inputProfile = ImageCmsProfile(inputProfile)
598 if not isinstance(outputProfile, ImageCmsProfile):
599 outputProfile = ImageCmsProfile(outputProfile)
600 return ImageCmsTransform(
601 inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
602 )
603 except (OSError, TypeError, ValueError) as v:
604 raise PyCMSError(v) from v
607def buildProofTransform(
608 inputProfile: _CmsProfileCompatible,
609 outputProfile: _CmsProfileCompatible,
610 proofProfile: _CmsProfileCompatible,
611 inMode: str,
612 outMode: str,
613 renderingIntent: Intent = Intent.PERCEPTUAL,
614 proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
615 flags: Flags = Flags.SOFTPROOFING,
616) -> ImageCmsTransform:
617 """
618 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
619 ``outputProfile``, but tries to simulate the result that would be
620 obtained on the ``proofProfile`` device.
622 If the input, output, or proof profiles specified are not valid
623 filenames, a :exc:`PyCMSError` will be raised.
625 If an error occurs during creation of the transform,
626 a :exc:`PyCMSError` will be raised.
628 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
629 (or by pyCMS), a :exc:`PyCMSError` will be raised.
631 This function builds and returns an ICC transform from the ``inputProfile``
632 to the ``outputProfile``, but tries to simulate the result that would be
633 obtained on the ``proofProfile`` device using ``renderingIntent`` and
634 ``proofRenderingIntent`` to determine what to do with out-of-gamut
635 colors. This is known as "soft-proofing". It will ONLY work for
636 converting images that are in ``inMode`` to images that are in outMode
637 color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
639 Usage of the resulting transform object is exactly the same as with
640 ImageCms.buildTransform().
642 Proof profiling is generally used when using an output device to get a
643 good idea of what the final printed/displayed image would look like on
644 the ``proofProfile`` device when it's quicker and easier to use the
645 output device for judging color. Generally, this means that the
646 output device is a monitor, or a dye-sub printer (etc.), and the simulated
647 device is something more expensive, complicated, or time consuming
648 (making it difficult to make a real print for color judgement purposes).
650 Soft-proofing basically functions by adjusting the colors on the
651 output device to match the colors of the device being simulated. However,
652 when the simulated device has a much wider gamut than the output
653 device, you may obtain marginal results.
655 :param inputProfile: String, as a valid filename path to the ICC input
656 profile you wish to use for this transform, or a profile object
657 :param outputProfile: String, as a valid filename path to the ICC output
658 (monitor, usually) profile you wish to use for this transform, or a
659 profile object
660 :param proofProfile: String, as a valid filename path to the ICC proof
661 profile you wish to use for this transform, or a profile object
662 :param inMode: String, as a valid PIL mode that the appropriate profile
663 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
664 :param outMode: String, as a valid PIL mode that the appropriate profile
665 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
666 :param renderingIntent: Integer (0-3) specifying the rendering intent you
667 wish to use for the input->proof (simulated) transform
669 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
670 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
671 ImageCms.Intent.SATURATION = 2
672 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
674 see the pyCMS documentation for details on rendering intents and what
675 they do.
676 :param proofRenderingIntent: Integer (0-3) specifying the rendering intent
677 you wish to use for proof->output transform
679 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
680 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
681 ImageCms.Intent.SATURATION = 2
682 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
684 see the pyCMS documentation for details on rendering intents and what
685 they do.
686 :param flags: Integer (0-...) specifying additional flags
687 :returns: A CmsTransform class object.
688 :exception PyCMSError:
689 """
691 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
692 msg = "renderingIntent must be an integer between 0 and 3"
693 raise PyCMSError(msg)
695 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
696 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
697 raise PyCMSError(msg)
699 try:
700 if not isinstance(inputProfile, ImageCmsProfile):
701 inputProfile = ImageCmsProfile(inputProfile)
702 if not isinstance(outputProfile, ImageCmsProfile):
703 outputProfile = ImageCmsProfile(outputProfile)
704 if not isinstance(proofProfile, ImageCmsProfile):
705 proofProfile = ImageCmsProfile(proofProfile)
706 return ImageCmsTransform(
707 inputProfile,
708 outputProfile,
709 inMode,
710 outMode,
711 renderingIntent,
712 proofProfile,
713 proofRenderingIntent,
714 flags,
715 )
716 except (OSError, TypeError, ValueError) as v:
717 raise PyCMSError(v) from v
720buildTransformFromOpenProfiles = buildTransform
721buildProofTransformFromOpenProfiles = buildProofTransform
724def applyTransform(
725 im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False
726) -> Image.Image | None:
727 """
728 (pyCMS) Applies a transform to a given image.
730 If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised.
732 If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a
733 :exc:`PyCMSError` is raised.
735 If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not
736 supported by pyCMSdll or the profiles you used for the transform, a
737 :exc:`PyCMSError` is raised.
739 If an error occurs while the transform is being applied,
740 a :exc:`PyCMSError` is raised.
742 This function applies a pre-calculated transform (from
743 ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
744 to an image. The transform can be used for multiple images, saving
745 considerable calculation time if doing the same conversion multiple times.
747 If you want to modify im in-place instead of receiving a new image as
748 the return value, set ``inPlace`` to ``True``. This can only be done if
749 ``transform.input_mode`` and ``transform.output_mode`` are the same, because we
750 can't change the mode in-place (the buffer sizes for some modes are
751 different). The default behavior is to return a new :py:class:`~PIL.Image.Image`
752 object of the same dimensions in mode ``transform.output_mode``.
754 :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same
755 as the ``input_mode`` supported by the transform.
756 :param transform: A valid CmsTransform class object
757 :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is
758 returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the
759 transform applied is returned (and ``im`` is not changed). The default is
760 ``False``.
761 :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object,
762 depending on the value of ``inPlace``. The profile will be returned in
763 the image's ``info['icc_profile']``.
764 :exception PyCMSError:
765 """
767 try:
768 if inPlace:
769 transform.apply_in_place(im)
770 imOut = None
771 else:
772 imOut = transform.apply(im)
773 except (TypeError, ValueError) as v:
774 raise PyCMSError(v) from v
776 return imOut
779def createProfile(
780 colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0
781) -> core.CmsProfile:
782 """
783 (pyCMS) Creates a profile.
785 If colorSpace not in ``["LAB", "XYZ", "sRGB"]``,
786 a :exc:`PyCMSError` is raised.
788 If using LAB and ``colorTemp`` is not a positive integer,
789 a :exc:`PyCMSError` is raised.
791 If an error occurs while creating the profile,
792 a :exc:`PyCMSError` is raised.
794 Use this function to create common profiles on-the-fly instead of
795 having to supply a profile on disk and knowing the path to it. It
796 returns a normal CmsProfile object that can be passed to
797 ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
798 to images.
800 :param colorSpace: String, the color space of the profile you wish to
801 create.
802 Currently only "LAB", "XYZ", and "sRGB" are supported.
803 :param colorTemp: Positive number for the white point for the profile, in
804 degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
805 illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
806 profiles, and is ignored for XYZ and sRGB.
807 :returns: A CmsProfile class object
808 :exception PyCMSError:
809 """
811 if colorSpace not in ["LAB", "XYZ", "sRGB"]:
812 msg = (
813 f"Color space not supported for on-the-fly profile creation ({colorSpace})"
814 )
815 raise PyCMSError(msg)
817 if colorSpace == "LAB":
818 try:
819 colorTemp = float(colorTemp)
820 except (TypeError, ValueError) as e:
821 msg = f'Color temperature must be numeric, "{colorTemp}" not valid'
822 raise PyCMSError(msg) from e
824 try:
825 return core.createProfile(colorSpace, colorTemp)
826 except (TypeError, ValueError) as v:
827 raise PyCMSError(v) from v
830def getProfileName(profile: _CmsProfileCompatible) -> str:
831 """
833 (pyCMS) Gets the internal product name for the given profile.
835 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
836 a :exc:`PyCMSError` is raised If an error occurs while trying
837 to obtain the name tag, a :exc:`PyCMSError` is raised.
839 Use this function to obtain the INTERNAL name of the profile (stored
840 in an ICC tag in the profile itself), usually the one used when the
841 profile was originally created. Sometimes this tag also contains
842 additional information supplied by the creator.
844 :param profile: EITHER a valid CmsProfile object, OR a string of the
845 filename of an ICC profile.
846 :returns: A string containing the internal name of the profile as stored
847 in an ICC tag.
848 :exception PyCMSError:
849 """
851 try:
852 # add an extra newline to preserve pyCMS compatibility
853 if not isinstance(profile, ImageCmsProfile):
854 profile = ImageCmsProfile(profile)
855 # do it in python, not c.
856 # // name was "%s - %s" (model, manufacturer) || Description ,
857 # // but if the Model and Manufacturer were the same or the model
858 # // was long, Just the model, in 1.x
859 model = profile.profile.model
860 manufacturer = profile.profile.manufacturer
862 if not (model or manufacturer):
863 return (profile.profile.profile_description or "") + "\n"
864 if not manufacturer or (model and len(model) > 30):
865 return f"{model}\n"
866 return f"{model} - {manufacturer}\n"
868 except (AttributeError, OSError, TypeError, ValueError) as v:
869 raise PyCMSError(v) from v
872def getProfileInfo(profile: _CmsProfileCompatible) -> str:
873 """
874 (pyCMS) Gets the internal product information for the given profile.
876 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
877 a :exc:`PyCMSError` is raised.
879 If an error occurs while trying to obtain the info tag,
880 a :exc:`PyCMSError` is raised.
882 Use this function to obtain the information stored in the profile's
883 info tag. This often contains details about the profile, and how it
884 was created, as supplied by the creator.
886 :param profile: EITHER a valid CmsProfile object, OR a string of the
887 filename of an ICC profile.
888 :returns: A string containing the internal profile information stored in
889 an ICC tag.
890 :exception PyCMSError:
891 """
893 try:
894 if not isinstance(profile, ImageCmsProfile):
895 profile = ImageCmsProfile(profile)
896 # add an extra newline to preserve pyCMS compatibility
897 # Python, not C. the white point bits weren't working well,
898 # so skipping.
899 # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
900 description = profile.profile.profile_description
901 cpright = profile.profile.copyright
902 elements = [element for element in (description, cpright) if element]
903 return "\r\n\r\n".join(elements) + "\r\n\r\n"
905 except (AttributeError, OSError, TypeError, ValueError) as v:
906 raise PyCMSError(v) from v
909def getProfileCopyright(profile: _CmsProfileCompatible) -> str:
910 """
911 (pyCMS) Gets the copyright for the given profile.
913 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
914 :exc:`PyCMSError` is raised.
916 If an error occurs while trying to obtain the copyright tag,
917 a :exc:`PyCMSError` is raised.
919 Use this function to obtain the information stored in the profile's
920 copyright tag.
922 :param profile: EITHER a valid CmsProfile object, OR a string of the
923 filename of an ICC profile.
924 :returns: A string containing the internal profile information stored in
925 an ICC tag.
926 :exception PyCMSError:
927 """
928 try:
929 # add an extra newline to preserve pyCMS compatibility
930 if not isinstance(profile, ImageCmsProfile):
931 profile = ImageCmsProfile(profile)
932 return (profile.profile.copyright or "") + "\n"
933 except (AttributeError, OSError, TypeError, ValueError) as v:
934 raise PyCMSError(v) from v
937def getProfileManufacturer(profile: _CmsProfileCompatible) -> str:
938 """
939 (pyCMS) Gets the manufacturer for the given profile.
941 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
942 :exc:`PyCMSError` is raised.
944 If an error occurs while trying to obtain the manufacturer tag, a
945 :exc:`PyCMSError` is raised.
947 Use this function to obtain the information stored in the profile's
948 manufacturer tag.
950 :param profile: EITHER a valid CmsProfile object, OR a string of the
951 filename of an ICC profile.
952 :returns: A string containing the internal profile information stored in
953 an ICC tag.
954 :exception PyCMSError:
955 """
956 try:
957 # add an extra newline to preserve pyCMS compatibility
958 if not isinstance(profile, ImageCmsProfile):
959 profile = ImageCmsProfile(profile)
960 return (profile.profile.manufacturer or "") + "\n"
961 except (AttributeError, OSError, TypeError, ValueError) as v:
962 raise PyCMSError(v) from v
965def getProfileModel(profile: _CmsProfileCompatible) -> str:
966 """
967 (pyCMS) Gets the model for the given profile.
969 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
970 :exc:`PyCMSError` is raised.
972 If an error occurs while trying to obtain the model tag,
973 a :exc:`PyCMSError` is raised.
975 Use this function to obtain the information stored in the profile's
976 model tag.
978 :param profile: EITHER a valid CmsProfile object, OR a string of the
979 filename of an ICC profile.
980 :returns: A string containing the internal profile information stored in
981 an ICC tag.
982 :exception PyCMSError:
983 """
985 try:
986 # add an extra newline to preserve pyCMS compatibility
987 if not isinstance(profile, ImageCmsProfile):
988 profile = ImageCmsProfile(profile)
989 return (profile.profile.model or "") + "\n"
990 except (AttributeError, OSError, TypeError, ValueError) as v:
991 raise PyCMSError(v) from v
994def getProfileDescription(profile: _CmsProfileCompatible) -> str:
995 """
996 (pyCMS) Gets the description for the given profile.
998 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
999 :exc:`PyCMSError` is raised.
1001 If an error occurs while trying to obtain the description tag,
1002 a :exc:`PyCMSError` is raised.
1004 Use this function to obtain the information stored in the profile's
1005 description tag.
1007 :param profile: EITHER a valid CmsProfile object, OR a string of the
1008 filename of an ICC profile.
1009 :returns: A string containing the internal profile information stored in an
1010 ICC tag.
1011 :exception PyCMSError:
1012 """
1014 try:
1015 # add an extra newline to preserve pyCMS compatibility
1016 if not isinstance(profile, ImageCmsProfile):
1017 profile = ImageCmsProfile(profile)
1018 return (profile.profile.profile_description or "") + "\n"
1019 except (AttributeError, OSError, TypeError, ValueError) as v:
1020 raise PyCMSError(v) from v
1023def getDefaultIntent(profile: _CmsProfileCompatible) -> int:
1024 """
1025 (pyCMS) Gets the default intent name for the given profile.
1027 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
1028 :exc:`PyCMSError` is raised.
1030 If an error occurs while trying to obtain the default intent, a
1031 :exc:`PyCMSError` is raised.
1033 Use this function to determine the default (and usually best optimized)
1034 rendering intent for this profile. Most profiles support multiple
1035 rendering intents, but are intended mostly for one type of conversion.
1036 If you wish to use a different intent than returned, use
1037 ImageCms.isIntentSupported() to verify it will work first.
1039 :param profile: EITHER a valid CmsProfile object, OR a string of the
1040 filename of an ICC profile.
1041 :returns: Integer 0-3 specifying the default rendering intent for this
1042 profile.
1044 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1045 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1046 ImageCms.Intent.SATURATION = 2
1047 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1049 see the pyCMS documentation for details on rendering intents and what
1050 they do.
1051 :exception PyCMSError:
1052 """
1054 try:
1055 if not isinstance(profile, ImageCmsProfile):
1056 profile = ImageCmsProfile(profile)
1057 return profile.profile.rendering_intent
1058 except (AttributeError, OSError, TypeError, ValueError) as v:
1059 raise PyCMSError(v) from v
1062def isIntentSupported(
1063 profile: _CmsProfileCompatible, intent: Intent, direction: Direction
1064) -> Literal[-1, 1]:
1065 """
1066 (pyCMS) Checks if a given intent is supported.
1068 Use this function to verify that you can use your desired
1069 ``intent`` with ``profile``, and that ``profile`` can be used for the
1070 input/output/proof profile as you desire.
1072 Some profiles are created specifically for one "direction", can cannot
1073 be used for others. Some profiles can only be used for certain
1074 rendering intents, so it's best to either verify this before trying
1075 to create a transform with them (using this function), or catch the
1076 potential :exc:`PyCMSError` that will occur if they don't
1077 support the modes you select.
1079 :param profile: EITHER a valid CmsProfile object, OR a string of the
1080 filename of an ICC profile.
1081 :param intent: Integer (0-3) specifying the rendering intent you wish to
1082 use with this profile
1084 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1085 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1086 ImageCms.Intent.SATURATION = 2
1087 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1089 see the pyCMS documentation for details on rendering intents and what
1090 they do.
1091 :param direction: Integer specifying if the profile is to be used for
1092 input, output, or proof
1094 INPUT = 0 (or use ImageCms.Direction.INPUT)
1095 OUTPUT = 1 (or use ImageCms.Direction.OUTPUT)
1096 PROOF = 2 (or use ImageCms.Direction.PROOF)
1098 :returns: 1 if the intent/direction are supported, -1 if they are not.
1099 :exception PyCMSError:
1100 """
1102 try:
1103 if not isinstance(profile, ImageCmsProfile):
1104 profile = ImageCmsProfile(profile)
1105 # FIXME: I get different results for the same data w. different
1106 # compilers. Bug in LittleCMS or in the binding?
1107 if profile.profile.is_intent_supported(intent, direction):
1108 return 1
1109 else:
1110 return -1
1111 except (AttributeError, OSError, TypeError, ValueError) as v:
1112 raise PyCMSError(v) from v
1115def versions() -> tuple[str, str | None, str, str]:
1116 """
1117 (pyCMS) Fetches versions.
1118 """
1120 deprecate(
1121 "PIL.ImageCms.versions()",
1122 12,
1123 '(PIL.features.version("littlecms2"), sys.version, PIL.__version__)',
1124 )
1125 return _VERSION, core.littlecms_version, sys.version.split()[0], __version__