Python Color Management — Core Concepts

Color management ensures that colors in your images, designs, or data visualizations look the same across different displays, printers, and operating systems. Python provides several tools for working with color spaces and ICC profiles.

Key Concepts

Color space — A system for representing colors as numbers. Common ones:

  • sRGB — The web standard. Most monitors target this.
  • Adobe RGB — Wider gamut, popular in photography.
  • CMYK — Used for print (Cyan, Magenta, Yellow, Key/Black).
  • Display P3 — Used by modern Apple and Samsung displays.

ICC profile — A file that maps a device’s color behavior to a standard reference. Embedded in images (JPEG, TIFF, PNG) to preserve color intent.

Gamut — The range of colors a device can reproduce. A wider gamut means more colors.

Reading ICC Profiles with Pillow

from PIL import Image
from PIL.ImageCms import getOpenProfile, profileToProfile

img = Image.open("photo.jpg")

# Check for embedded profile
icc = img.info.get("icc_profile")
if icc:
    profile = getOpenProfile(io.BytesIO(icc))
    print(profile.profile.profile_description)
    # e.g., "sRGB IEC61966-2.1"

Converting Between Color Spaces

from PIL import Image, ImageCms
import io

img = Image.open("photo.jpg")

# Load profiles
srgb = ImageCms.createProfile("sRGB")
lab = ImageCms.createProfile("LAB")

# Convert sRGB to LAB (device-independent)
transform = ImageCms.buildTransform(srgb, lab, "RGB", "LAB")
lab_img = ImageCms.applyTransform(img, transform)

sRGB to CMYK for Print

# Load a CMYK ICC profile (download from manufacturer or use a standard one)
cmyk_profile = ImageCms.getOpenProfile("USWebCoatedSWOP.icc")
srgb_profile = ImageCms.createProfile("sRGB")

transform = ImageCms.buildTransform(
    srgb_profile, cmyk_profile,
    "RGB", "CMYK",
    renderingIntent=ImageCms.Intent.RELATIVE_COLORIMETRIC
)

cmyk_img = ImageCms.applyTransform(img, transform)
cmyk_img.save("print_ready.tiff")

Rendering Intents

When converting between color spaces with different gamuts, some colors won’t map perfectly. Rendering intents control what happens:

IntentBehaviorBest for
PerceptualCompresses entire gamut proportionallyPhotos
Relative ColorimetricMaps white point, clips out-of-gamutMost general use
Absolute ColorimetricPreserves exact colors, clips out-of-gamutProofing
SaturationMaximizes vividnessCharts, graphics
from PIL.ImageCms import Intent

# Use perceptual for photographic content
transform = ImageCms.buildTransform(
    src, dst, "RGB", "CMYK",
    renderingIntent=Intent.PERCEPTUAL
)

Working with colour-science

For scientific color work, the colour library provides comprehensive color space support:

# pip install colour-science
import colour
import numpy as np

# Convert sRGB to CIE XYZ
rgb = np.array([0.8, 0.2, 0.3])
xyz = colour.sRGB_to_XYZ(rgb)

# Convert XYZ to CIELAB
lab = colour.XYZ_to_Lab(xyz)

# Compute color difference (Delta E)
lab1 = np.array([50.0, 25.0, -10.0])
lab2 = np.array([52.0, 23.0, -8.0])
delta_e = colour.delta_E(lab1, lab2, method="CIE 2000")
# Delta E < 1.0 is imperceptible to most people

Embedding Profiles in Output

from PIL import Image, ImageCms

img = Image.open("photo.jpg")
srgb_profile = ImageCms.createProfile("sRGB")
srgb_bytes = ImageCms.ImageCmsProfile(srgb_profile).tobytes()

img.save("output.jpg", icc_profile=srgb_bytes)

Always embed an ICC profile when saving images for the web or print. Without one, software guesses — and guesses vary.

Common Misconception

“sRGB is enough for everything.” sRGB covers only about 35% of visible colors. Modern displays (P3, Rec. 2020) can show significantly more. If your workflow involves photography, design, or HDR content, you’ll need to work with wider gamuts and convert carefully.

The one thing to remember: Color management is about translating colors between devices using ICC profiles and rendering intents — Pillow handles the practical workflow while colour-science handles the math.

pythoncolor-managementICCPillowcolor-spaces

See Also

  • Ci Cd Why big apps can ship updates every day without turning your phone into a glitchy mess — CI/CD is the behind-the-scenes quality gate and delivery truck.
  • Containerization Why does software that works on your computer break on everyone else's? Containers fix that — and they're why Netflix can deploy 100 updates a day without the site going down.
  • Python 310 New Features Python 3.10 gave programmers a shape-sorting machine, friendlier error messages, and cleaner ways to say 'this or that' in type hints.
  • Python 311 New Features Python 3.11 made everything faster, error messages smarter, and let you catch several mistakes at once instead of stopping at the first one.
  • Python 312 New Features Python 3.12 made type hints shorter, f-strings more powerful, and started preparing Python's engine for a world without the GIL.