Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/ImageCms.py: 34%
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 """
251 self.filename = None
252 self.product_name = None # profile.product_name
253 self.product_info = None # profile.product_info
255 if isinstance(profile, str):
256 if sys.platform == "win32":
257 profile_bytes_path = profile.encode()
258 try:
259 profile_bytes_path.decode("ascii")
260 except UnicodeDecodeError:
261 with open(profile, "rb") as f:
262 self.profile = core.profile_frombytes(f.read())
263 return
264 self.filename = profile
265 self.profile = core.profile_open(profile)
266 elif hasattr(profile, "read"):
267 self.profile = core.profile_frombytes(profile.read())
268 elif isinstance(profile, core.CmsProfile):
269 self.profile = profile
270 else:
271 msg = "Invalid type for Profile" # type: ignore[unreachable]
272 raise TypeError(msg)
274 def tobytes(self) -> bytes:
275 """
276 Returns the profile in a format suitable for embedding in
277 saved images.
279 :returns: a bytes object containing the ICC profile.
280 """
282 return core.profile_tobytes(self.profile)
285class ImageCmsTransform(Image.ImagePointHandler):
286 """
287 Transform. This can be used with the procedural API, or with the standard
288 :py:func:`~PIL.Image.Image.point` method.
290 Will return the output profile in the ``output.info['icc_profile']``.
291 """
293 def __init__(
294 self,
295 input: ImageCmsProfile,
296 output: ImageCmsProfile,
297 input_mode: str,
298 output_mode: str,
299 intent: Intent = Intent.PERCEPTUAL,
300 proof: ImageCmsProfile | None = None,
301 proof_intent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
302 flags: Flags = Flags.NONE,
303 ):
304 supported_modes = (
305 "RGB",
306 "RGBA",
307 "RGBX",
308 "CMYK",
309 "I;16",
310 "I;16L",
311 "I;16B",
312 "YCbCr",
313 "LAB",
314 "L",
315 "1",
316 )
317 for mode in (input_mode, output_mode):
318 if mode not in supported_modes:
319 deprecate(
320 mode,
321 12,
322 {
323 "L;16": "I;16 or I;16L",
324 "L:16B": "I;16B",
325 "YCCA": "YCbCr",
326 "YCC": "YCbCr",
327 }.get(mode),
328 )
329 if proof is None:
330 self.transform = core.buildTransform(
331 input.profile, output.profile, input_mode, output_mode, intent, flags
332 )
333 else:
334 self.transform = core.buildProofTransform(
335 input.profile,
336 output.profile,
337 proof.profile,
338 input_mode,
339 output_mode,
340 intent,
341 proof_intent,
342 flags,
343 )
344 # Note: inputMode and outputMode are for pyCMS compatibility only
345 self.input_mode = self.inputMode = input_mode
346 self.output_mode = self.outputMode = output_mode
348 self.output_profile = output
350 def point(self, im: Image.Image) -> Image.Image:
351 return self.apply(im)
353 def apply(self, im: Image.Image, imOut: Image.Image | None = None) -> Image.Image:
354 if imOut is None:
355 imOut = Image.new(self.output_mode, im.size, None)
356 self.transform.apply(im.getim(), imOut.getim())
357 imOut.info["icc_profile"] = self.output_profile.tobytes()
358 return imOut
360 def apply_in_place(self, im: Image.Image) -> Image.Image:
361 if im.mode != self.output_mode:
362 msg = "mode mismatch"
363 raise ValueError(msg) # wrong output mode
364 self.transform.apply(im.getim(), im.getim())
365 im.info["icc_profile"] = self.output_profile.tobytes()
366 return im
369def get_display_profile(handle: SupportsInt | None = None) -> ImageCmsProfile | None:
370 """
371 (experimental) Fetches the profile for the current display device.
373 :returns: ``None`` if the profile is not known.
374 """
376 if sys.platform != "win32":
377 return None
379 from . import ImageWin # type: ignore[unused-ignore, unreachable]
381 if isinstance(handle, ImageWin.HDC):
382 profile = core.get_display_profile_win32(int(handle), 1)
383 else:
384 profile = core.get_display_profile_win32(int(handle or 0))
385 if profile is None:
386 return None
387 return ImageCmsProfile(profile)
390# --------------------------------------------------------------------.
391# pyCMS compatible layer
392# --------------------------------------------------------------------.
395class PyCMSError(Exception):
396 """(pyCMS) Exception class.
397 This is used for all errors in the pyCMS API."""
399 pass
402def profileToProfile(
403 im: Image.Image,
404 inputProfile: _CmsProfileCompatible,
405 outputProfile: _CmsProfileCompatible,
406 renderingIntent: Intent = Intent.PERCEPTUAL,
407 outputMode: str | None = None,
408 inPlace: bool = False,
409 flags: Flags = Flags.NONE,
410) -> Image.Image | None:
411 """
412 (pyCMS) Applies an ICC transformation to a given image, mapping from
413 ``inputProfile`` to ``outputProfile``.
415 If the input or output profiles specified are not valid filenames, a
416 :exc:`PyCMSError` will be raised. If ``inPlace`` is ``True`` and
417 ``outputMode != im.mode``, a :exc:`PyCMSError` will be raised.
418 If an error occurs during application of the profiles,
419 a :exc:`PyCMSError` will be raised.
420 If ``outputMode`` is not a mode supported by the ``outputProfile`` (or by pyCMS),
421 a :exc:`PyCMSError` will be raised.
423 This function applies an ICC transformation to im from ``inputProfile``'s
424 color space to ``outputProfile``'s color space using the specified rendering
425 intent to decide how to handle out-of-gamut colors.
427 ``outputMode`` can be used to specify that a color mode conversion is to
428 be done using these profiles, but the specified profiles must be able
429 to handle that mode. I.e., if converting im from RGB to CMYK using
430 profiles, the input profile must handle RGB data, and the output
431 profile must handle CMYK data.
433 :param im: An open :py:class:`~PIL.Image.Image` object (i.e. Image.new(...)
434 or Image.open(...), etc.)
435 :param inputProfile: String, as a valid filename path to the ICC input
436 profile you wish to use for this image, or a profile object
437 :param outputProfile: String, as a valid filename path to the ICC output
438 profile you wish to use for this image, or a profile object
439 :param renderingIntent: Integer (0-3) specifying the rendering intent you
440 wish to use for the transform
442 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
443 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
444 ImageCms.Intent.SATURATION = 2
445 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
447 see the pyCMS documentation for details on rendering intents and what
448 they do.
449 :param outputMode: A valid PIL mode for the output image (i.e. "RGB",
450 "CMYK", etc.). Note: if rendering the image "inPlace", outputMode
451 MUST be the same mode as the input, or omitted completely. If
452 omitted, the outputMode will be the same as the mode of the input
453 image (im.mode)
454 :param inPlace: Boolean. If ``True``, the original image is modified in-place,
455 and ``None`` is returned. If ``False`` (default), a new
456 :py:class:`~PIL.Image.Image` object is returned with the transform applied.
457 :param flags: Integer (0-...) specifying additional flags
458 :returns: Either None or a new :py:class:`~PIL.Image.Image` object, depending on
459 the value of ``inPlace``
460 :exception PyCMSError:
461 """
463 if outputMode is None:
464 outputMode = im.mode
466 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
467 msg = "renderingIntent must be an integer between 0 and 3"
468 raise PyCMSError(msg)
470 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
471 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
472 raise PyCMSError(msg)
474 try:
475 if not isinstance(inputProfile, ImageCmsProfile):
476 inputProfile = ImageCmsProfile(inputProfile)
477 if not isinstance(outputProfile, ImageCmsProfile):
478 outputProfile = ImageCmsProfile(outputProfile)
479 transform = ImageCmsTransform(
480 inputProfile,
481 outputProfile,
482 im.mode,
483 outputMode,
484 renderingIntent,
485 flags=flags,
486 )
487 if inPlace:
488 transform.apply_in_place(im)
489 imOut = None
490 else:
491 imOut = transform.apply(im)
492 except (OSError, TypeError, ValueError) as v:
493 raise PyCMSError(v) from v
495 return imOut
498def getOpenProfile(
499 profileFilename: str | SupportsRead[bytes] | core.CmsProfile,
500) -> ImageCmsProfile:
501 """
502 (pyCMS) Opens an ICC profile file.
504 The PyCMSProfile object can be passed back into pyCMS for use in creating
505 transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
507 If ``profileFilename`` is not a valid filename for an ICC profile,
508 a :exc:`PyCMSError` will be raised.
510 :param profileFilename: String, as a valid filename path to the ICC profile
511 you wish to open, or a file-like object.
512 :returns: A CmsProfile class object.
513 :exception PyCMSError:
514 """
516 try:
517 return ImageCmsProfile(profileFilename)
518 except (OSError, TypeError, ValueError) as v:
519 raise PyCMSError(v) from v
522def buildTransform(
523 inputProfile: _CmsProfileCompatible,
524 outputProfile: _CmsProfileCompatible,
525 inMode: str,
526 outMode: str,
527 renderingIntent: Intent = Intent.PERCEPTUAL,
528 flags: Flags = Flags.NONE,
529) -> ImageCmsTransform:
530 """
531 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
532 ``outputProfile``. Use applyTransform to apply the transform to a given
533 image.
535 If the input or output profiles specified are not valid filenames, a
536 :exc:`PyCMSError` will be raised. If an error occurs during creation
537 of the transform, a :exc:`PyCMSError` will be raised.
539 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
540 (or by pyCMS), a :exc:`PyCMSError` will be raised.
542 This function builds and returns an ICC transform from the ``inputProfile``
543 to the ``outputProfile`` using the ``renderingIntent`` to determine what to do
544 with out-of-gamut colors. It will ONLY work for converting images that
545 are in ``inMode`` to images that are in ``outMode`` color format (PIL mode,
546 i.e. "RGB", "RGBA", "CMYK", etc.).
548 Building the transform is a fair part of the overhead in
549 ImageCms.profileToProfile(), so if you're planning on converting multiple
550 images using the same input/output settings, this can save you time.
551 Once you have a transform object, it can be used with
552 ImageCms.applyProfile() to convert images without the need to re-compute
553 the lookup table for the transform.
555 The reason pyCMS returns a class object rather than a handle directly
556 to the transform is that it needs to keep track of the PIL input/output
557 modes that the transform is meant for. These attributes are stored in
558 the ``inMode`` and ``outMode`` attributes of the object (which can be
559 manually overridden if you really want to, but I don't know of any
560 time that would be of use, or would even work).
562 :param inputProfile: String, as a valid filename path to the ICC input
563 profile you wish to use for this transform, or a profile object
564 :param outputProfile: String, as a valid filename path to the ICC output
565 profile you wish to use for this transform, or a profile object
566 :param inMode: String, as a valid PIL mode that the appropriate profile
567 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
568 :param outMode: String, as a valid PIL mode that the appropriate profile
569 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
570 :param renderingIntent: Integer (0-3) specifying the rendering intent you
571 wish to use for the transform
573 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
574 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
575 ImageCms.Intent.SATURATION = 2
576 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
578 see the pyCMS documentation for details on rendering intents and what
579 they do.
580 :param flags: Integer (0-...) specifying additional flags
581 :returns: A CmsTransform class object.
582 :exception PyCMSError:
583 """
585 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
586 msg = "renderingIntent must be an integer between 0 and 3"
587 raise PyCMSError(msg)
589 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
590 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
591 raise PyCMSError(msg)
593 try:
594 if not isinstance(inputProfile, ImageCmsProfile):
595 inputProfile = ImageCmsProfile(inputProfile)
596 if not isinstance(outputProfile, ImageCmsProfile):
597 outputProfile = ImageCmsProfile(outputProfile)
598 return ImageCmsTransform(
599 inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
600 )
601 except (OSError, TypeError, ValueError) as v:
602 raise PyCMSError(v) from v
605def buildProofTransform(
606 inputProfile: _CmsProfileCompatible,
607 outputProfile: _CmsProfileCompatible,
608 proofProfile: _CmsProfileCompatible,
609 inMode: str,
610 outMode: str,
611 renderingIntent: Intent = Intent.PERCEPTUAL,
612 proofRenderingIntent: Intent = Intent.ABSOLUTE_COLORIMETRIC,
613 flags: Flags = Flags.SOFTPROOFING,
614) -> ImageCmsTransform:
615 """
616 (pyCMS) Builds an ICC transform mapping from the ``inputProfile`` to the
617 ``outputProfile``, but tries to simulate the result that would be
618 obtained on the ``proofProfile`` device.
620 If the input, output, or proof profiles specified are not valid
621 filenames, a :exc:`PyCMSError` will be raised.
623 If an error occurs during creation of the transform,
624 a :exc:`PyCMSError` will be raised.
626 If ``inMode`` or ``outMode`` are not a mode supported by the ``outputProfile``
627 (or by pyCMS), a :exc:`PyCMSError` will be raised.
629 This function builds and returns an ICC transform from the ``inputProfile``
630 to the ``outputProfile``, but tries to simulate the result that would be
631 obtained on the ``proofProfile`` device using ``renderingIntent`` and
632 ``proofRenderingIntent`` to determine what to do with out-of-gamut
633 colors. This is known as "soft-proofing". It will ONLY work for
634 converting images that are in ``inMode`` to images that are in outMode
635 color format (PIL mode, i.e. "RGB", "RGBA", "CMYK", etc.).
637 Usage of the resulting transform object is exactly the same as with
638 ImageCms.buildTransform().
640 Proof profiling is generally used when using an output device to get a
641 good idea of what the final printed/displayed image would look like on
642 the ``proofProfile`` device when it's quicker and easier to use the
643 output device for judging color. Generally, this means that the
644 output device is a monitor, or a dye-sub printer (etc.), and the simulated
645 device is something more expensive, complicated, or time consuming
646 (making it difficult to make a real print for color judgement purposes).
648 Soft-proofing basically functions by adjusting the colors on the
649 output device to match the colors of the device being simulated. However,
650 when the simulated device has a much wider gamut than the output
651 device, you may obtain marginal results.
653 :param inputProfile: String, as a valid filename path to the ICC input
654 profile you wish to use for this transform, or a profile object
655 :param outputProfile: String, as a valid filename path to the ICC output
656 (monitor, usually) profile you wish to use for this transform, or a
657 profile object
658 :param proofProfile: String, as a valid filename path to the ICC proof
659 profile you wish to use for this transform, or a profile object
660 :param inMode: String, as a valid PIL mode that the appropriate profile
661 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
662 :param outMode: String, as a valid PIL mode that the appropriate profile
663 also supports (i.e. "RGB", "RGBA", "CMYK", etc.)
664 :param renderingIntent: Integer (0-3) specifying the rendering intent you
665 wish to use for the input->proof (simulated) transform
667 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
668 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
669 ImageCms.Intent.SATURATION = 2
670 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
672 see the pyCMS documentation for details on rendering intents and what
673 they do.
674 :param proofRenderingIntent: Integer (0-3) specifying the rendering intent
675 you wish to use for proof->output transform
677 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
678 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
679 ImageCms.Intent.SATURATION = 2
680 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
682 see the pyCMS documentation for details on rendering intents and what
683 they do.
684 :param flags: Integer (0-...) specifying additional flags
685 :returns: A CmsTransform class object.
686 :exception PyCMSError:
687 """
689 if not isinstance(renderingIntent, int) or not (0 <= renderingIntent <= 3):
690 msg = "renderingIntent must be an integer between 0 and 3"
691 raise PyCMSError(msg)
693 if not isinstance(flags, int) or not (0 <= flags <= _MAX_FLAG):
694 msg = f"flags must be an integer between 0 and {_MAX_FLAG}"
695 raise PyCMSError(msg)
697 try:
698 if not isinstance(inputProfile, ImageCmsProfile):
699 inputProfile = ImageCmsProfile(inputProfile)
700 if not isinstance(outputProfile, ImageCmsProfile):
701 outputProfile = ImageCmsProfile(outputProfile)
702 if not isinstance(proofProfile, ImageCmsProfile):
703 proofProfile = ImageCmsProfile(proofProfile)
704 return ImageCmsTransform(
705 inputProfile,
706 outputProfile,
707 inMode,
708 outMode,
709 renderingIntent,
710 proofProfile,
711 proofRenderingIntent,
712 flags,
713 )
714 except (OSError, TypeError, ValueError) as v:
715 raise PyCMSError(v) from v
718buildTransformFromOpenProfiles = buildTransform
719buildProofTransformFromOpenProfiles = buildProofTransform
722def applyTransform(
723 im: Image.Image, transform: ImageCmsTransform, inPlace: bool = False
724) -> Image.Image | None:
725 """
726 (pyCMS) Applies a transform to a given image.
728 If ``im.mode != transform.input_mode``, a :exc:`PyCMSError` is raised.
730 If ``inPlace`` is ``True`` and ``transform.input_mode != transform.output_mode``, a
731 :exc:`PyCMSError` is raised.
733 If ``im.mode``, ``transform.input_mode`` or ``transform.output_mode`` is not
734 supported by pyCMSdll or the profiles you used for the transform, a
735 :exc:`PyCMSError` is raised.
737 If an error occurs while the transform is being applied,
738 a :exc:`PyCMSError` is raised.
740 This function applies a pre-calculated transform (from
741 ImageCms.buildTransform() or ImageCms.buildTransformFromOpenProfiles())
742 to an image. The transform can be used for multiple images, saving
743 considerable calculation time if doing the same conversion multiple times.
745 If you want to modify im in-place instead of receiving a new image as
746 the return value, set ``inPlace`` to ``True``. This can only be done if
747 ``transform.input_mode`` and ``transform.output_mode`` are the same, because we
748 can't change the mode in-place (the buffer sizes for some modes are
749 different). The default behavior is to return a new :py:class:`~PIL.Image.Image`
750 object of the same dimensions in mode ``transform.output_mode``.
752 :param im: An :py:class:`~PIL.Image.Image` object, and ``im.mode`` must be the same
753 as the ``input_mode`` supported by the transform.
754 :param transform: A valid CmsTransform class object
755 :param inPlace: Bool. If ``True``, ``im`` is modified in place and ``None`` is
756 returned, if ``False``, a new :py:class:`~PIL.Image.Image` object with the
757 transform applied is returned (and ``im`` is not changed). The default is
758 ``False``.
759 :returns: Either ``None``, or a new :py:class:`~PIL.Image.Image` object,
760 depending on the value of ``inPlace``. The profile will be returned in
761 the image's ``info['icc_profile']``.
762 :exception PyCMSError:
763 """
765 try:
766 if inPlace:
767 transform.apply_in_place(im)
768 imOut = None
769 else:
770 imOut = transform.apply(im)
771 except (TypeError, ValueError) as v:
772 raise PyCMSError(v) from v
774 return imOut
777def createProfile(
778 colorSpace: Literal["LAB", "XYZ", "sRGB"], colorTemp: SupportsFloat = 0
779) -> core.CmsProfile:
780 """
781 (pyCMS) Creates a profile.
783 If colorSpace not in ``["LAB", "XYZ", "sRGB"]``,
784 a :exc:`PyCMSError` is raised.
786 If using LAB and ``colorTemp`` is not a positive integer,
787 a :exc:`PyCMSError` is raised.
789 If an error occurs while creating the profile,
790 a :exc:`PyCMSError` is raised.
792 Use this function to create common profiles on-the-fly instead of
793 having to supply a profile on disk and knowing the path to it. It
794 returns a normal CmsProfile object that can be passed to
795 ImageCms.buildTransformFromOpenProfiles() to create a transform to apply
796 to images.
798 :param colorSpace: String, the color space of the profile you wish to
799 create.
800 Currently only "LAB", "XYZ", and "sRGB" are supported.
801 :param colorTemp: Positive number for the white point for the profile, in
802 degrees Kelvin (i.e. 5000, 6500, 9600, etc.). The default is for D50
803 illuminant if omitted (5000k). colorTemp is ONLY applied to LAB
804 profiles, and is ignored for XYZ and sRGB.
805 :returns: A CmsProfile class object
806 :exception PyCMSError:
807 """
809 if colorSpace not in ["LAB", "XYZ", "sRGB"]:
810 msg = (
811 f"Color space not supported for on-the-fly profile creation ({colorSpace})"
812 )
813 raise PyCMSError(msg)
815 if colorSpace == "LAB":
816 try:
817 colorTemp = float(colorTemp)
818 except (TypeError, ValueError) as e:
819 msg = f'Color temperature must be numeric, "{colorTemp}" not valid'
820 raise PyCMSError(msg) from e
822 try:
823 return core.createProfile(colorSpace, colorTemp)
824 except (TypeError, ValueError) as v:
825 raise PyCMSError(v) from v
828def getProfileName(profile: _CmsProfileCompatible) -> str:
829 """
831 (pyCMS) Gets the internal product name for the given profile.
833 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
834 a :exc:`PyCMSError` is raised If an error occurs while trying
835 to obtain the name tag, a :exc:`PyCMSError` is raised.
837 Use this function to obtain the INTERNAL name of the profile (stored
838 in an ICC tag in the profile itself), usually the one used when the
839 profile was originally created. Sometimes this tag also contains
840 additional information supplied by the creator.
842 :param profile: EITHER a valid CmsProfile object, OR a string of the
843 filename of an ICC profile.
844 :returns: A string containing the internal name of the profile as stored
845 in an ICC tag.
846 :exception PyCMSError:
847 """
849 try:
850 # add an extra newline to preserve pyCMS compatibility
851 if not isinstance(profile, ImageCmsProfile):
852 profile = ImageCmsProfile(profile)
853 # do it in python, not c.
854 # // name was "%s - %s" (model, manufacturer) || Description ,
855 # // but if the Model and Manufacturer were the same or the model
856 # // was long, Just the model, in 1.x
857 model = profile.profile.model
858 manufacturer = profile.profile.manufacturer
860 if not (model or manufacturer):
861 return (profile.profile.profile_description or "") + "\n"
862 if not manufacturer or (model and len(model) > 30):
863 return f"{model}\n"
864 return f"{model} - {manufacturer}\n"
866 except (AttributeError, OSError, TypeError, ValueError) as v:
867 raise PyCMSError(v) from v
870def getProfileInfo(profile: _CmsProfileCompatible) -> str:
871 """
872 (pyCMS) Gets the internal product information for the given profile.
874 If ``profile`` isn't a valid CmsProfile object or filename to a profile,
875 a :exc:`PyCMSError` is raised.
877 If an error occurs while trying to obtain the info tag,
878 a :exc:`PyCMSError` is raised.
880 Use this function to obtain the information stored in the profile's
881 info tag. This often contains details about the profile, and how it
882 was created, as supplied by the creator.
884 :param profile: EITHER a valid CmsProfile object, OR a string of the
885 filename of an ICC profile.
886 :returns: A string containing the internal profile information stored in
887 an ICC tag.
888 :exception PyCMSError:
889 """
891 try:
892 if not isinstance(profile, ImageCmsProfile):
893 profile = ImageCmsProfile(profile)
894 # add an extra newline to preserve pyCMS compatibility
895 # Python, not C. the white point bits weren't working well,
896 # so skipping.
897 # info was description \r\n\r\n copyright \r\n\r\n K007 tag \r\n\r\n whitepoint
898 description = profile.profile.profile_description
899 cpright = profile.profile.copyright
900 elements = [element for element in (description, cpright) if element]
901 return "\r\n\r\n".join(elements) + "\r\n\r\n"
903 except (AttributeError, OSError, TypeError, ValueError) as v:
904 raise PyCMSError(v) from v
907def getProfileCopyright(profile: _CmsProfileCompatible) -> str:
908 """
909 (pyCMS) Gets the copyright for the given profile.
911 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
912 :exc:`PyCMSError` is raised.
914 If an error occurs while trying to obtain the copyright tag,
915 a :exc:`PyCMSError` is raised.
917 Use this function to obtain the information stored in the profile's
918 copyright tag.
920 :param profile: EITHER a valid CmsProfile object, OR a string of the
921 filename of an ICC profile.
922 :returns: A string containing the internal profile information stored in
923 an ICC tag.
924 :exception PyCMSError:
925 """
926 try:
927 # add an extra newline to preserve pyCMS compatibility
928 if not isinstance(profile, ImageCmsProfile):
929 profile = ImageCmsProfile(profile)
930 return (profile.profile.copyright or "") + "\n"
931 except (AttributeError, OSError, TypeError, ValueError) as v:
932 raise PyCMSError(v) from v
935def getProfileManufacturer(profile: _CmsProfileCompatible) -> str:
936 """
937 (pyCMS) Gets the manufacturer for the given profile.
939 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
940 :exc:`PyCMSError` is raised.
942 If an error occurs while trying to obtain the manufacturer tag, a
943 :exc:`PyCMSError` is raised.
945 Use this function to obtain the information stored in the profile's
946 manufacturer tag.
948 :param profile: EITHER a valid CmsProfile object, OR a string of the
949 filename of an ICC profile.
950 :returns: A string containing the internal profile information stored in
951 an ICC tag.
952 :exception PyCMSError:
953 """
954 try:
955 # add an extra newline to preserve pyCMS compatibility
956 if not isinstance(profile, ImageCmsProfile):
957 profile = ImageCmsProfile(profile)
958 return (profile.profile.manufacturer or "") + "\n"
959 except (AttributeError, OSError, TypeError, ValueError) as v:
960 raise PyCMSError(v) from v
963def getProfileModel(profile: _CmsProfileCompatible) -> str:
964 """
965 (pyCMS) Gets the model for the given profile.
967 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
968 :exc:`PyCMSError` is raised.
970 If an error occurs while trying to obtain the model tag,
971 a :exc:`PyCMSError` is raised.
973 Use this function to obtain the information stored in the profile's
974 model tag.
976 :param profile: EITHER a valid CmsProfile object, OR a string of the
977 filename of an ICC profile.
978 :returns: A string containing the internal profile information stored in
979 an ICC tag.
980 :exception PyCMSError:
981 """
983 try:
984 # add an extra newline to preserve pyCMS compatibility
985 if not isinstance(profile, ImageCmsProfile):
986 profile = ImageCmsProfile(profile)
987 return (profile.profile.model or "") + "\n"
988 except (AttributeError, OSError, TypeError, ValueError) as v:
989 raise PyCMSError(v) from v
992def getProfileDescription(profile: _CmsProfileCompatible) -> str:
993 """
994 (pyCMS) Gets the description for the given profile.
996 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
997 :exc:`PyCMSError` is raised.
999 If an error occurs while trying to obtain the description tag,
1000 a :exc:`PyCMSError` is raised.
1002 Use this function to obtain the information stored in the profile's
1003 description tag.
1005 :param profile: EITHER a valid CmsProfile object, OR a string of the
1006 filename of an ICC profile.
1007 :returns: A string containing the internal profile information stored in an
1008 ICC tag.
1009 :exception PyCMSError:
1010 """
1012 try:
1013 # add an extra newline to preserve pyCMS compatibility
1014 if not isinstance(profile, ImageCmsProfile):
1015 profile = ImageCmsProfile(profile)
1016 return (profile.profile.profile_description or "") + "\n"
1017 except (AttributeError, OSError, TypeError, ValueError) as v:
1018 raise PyCMSError(v) from v
1021def getDefaultIntent(profile: _CmsProfileCompatible) -> int:
1022 """
1023 (pyCMS) Gets the default intent name for the given profile.
1025 If ``profile`` isn't a valid CmsProfile object or filename to a profile, a
1026 :exc:`PyCMSError` is raised.
1028 If an error occurs while trying to obtain the default intent, a
1029 :exc:`PyCMSError` is raised.
1031 Use this function to determine the default (and usually best optimized)
1032 rendering intent for this profile. Most profiles support multiple
1033 rendering intents, but are intended mostly for one type of conversion.
1034 If you wish to use a different intent than returned, use
1035 ImageCms.isIntentSupported() to verify it will work first.
1037 :param profile: EITHER a valid CmsProfile object, OR a string of the
1038 filename of an ICC profile.
1039 :returns: Integer 0-3 specifying the default rendering intent for this
1040 profile.
1042 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1043 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1044 ImageCms.Intent.SATURATION = 2
1045 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1047 see the pyCMS documentation for details on rendering intents and what
1048 they do.
1049 :exception PyCMSError:
1050 """
1052 try:
1053 if not isinstance(profile, ImageCmsProfile):
1054 profile = ImageCmsProfile(profile)
1055 return profile.profile.rendering_intent
1056 except (AttributeError, OSError, TypeError, ValueError) as v:
1057 raise PyCMSError(v) from v
1060def isIntentSupported(
1061 profile: _CmsProfileCompatible, intent: Intent, direction: Direction
1062) -> Literal[-1, 1]:
1063 """
1064 (pyCMS) Checks if a given intent is supported.
1066 Use this function to verify that you can use your desired
1067 ``intent`` with ``profile``, and that ``profile`` can be used for the
1068 input/output/proof profile as you desire.
1070 Some profiles are created specifically for one "direction", can cannot
1071 be used for others. Some profiles can only be used for certain
1072 rendering intents, so it's best to either verify this before trying
1073 to create a transform with them (using this function), or catch the
1074 potential :exc:`PyCMSError` that will occur if they don't
1075 support the modes you select.
1077 :param profile: EITHER a valid CmsProfile object, OR a string of the
1078 filename of an ICC profile.
1079 :param intent: Integer (0-3) specifying the rendering intent you wish to
1080 use with this profile
1082 ImageCms.Intent.PERCEPTUAL = 0 (DEFAULT)
1083 ImageCms.Intent.RELATIVE_COLORIMETRIC = 1
1084 ImageCms.Intent.SATURATION = 2
1085 ImageCms.Intent.ABSOLUTE_COLORIMETRIC = 3
1087 see the pyCMS documentation for details on rendering intents and what
1088 they do.
1089 :param direction: Integer specifying if the profile is to be used for
1090 input, output, or proof
1092 INPUT = 0 (or use ImageCms.Direction.INPUT)
1093 OUTPUT = 1 (or use ImageCms.Direction.OUTPUT)
1094 PROOF = 2 (or use ImageCms.Direction.PROOF)
1096 :returns: 1 if the intent/direction are supported, -1 if they are not.
1097 :exception PyCMSError:
1098 """
1100 try:
1101 if not isinstance(profile, ImageCmsProfile):
1102 profile = ImageCmsProfile(profile)
1103 # FIXME: I get different results for the same data w. different
1104 # compilers. Bug in LittleCMS or in the binding?
1105 if profile.profile.is_intent_supported(intent, direction):
1106 return 1
1107 else:
1108 return -1
1109 except (AttributeError, OSError, TypeError, ValueError) as v:
1110 raise PyCMSError(v) from v
1113def versions() -> tuple[str, str | None, str, str]:
1114 """
1115 (pyCMS) Fetches versions.
1116 """
1118 deprecate(
1119 "PIL.ImageCms.versions()",
1120 12,
1121 '(PIL.features.version("littlecms2"), sys.version, PIL.__version__)',
1122 )
1123 return _VERSION, core.littlecms_version, sys.version.split()[0], __version__