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:
| Intent | Behavior | Best for |
|---|---|---|
| Perceptual | Compresses entire gamut proportionally | Photos |
| Relative Colorimetric | Maps white point, clips out-of-gamut | Most general use |
| Absolute Colorimetric | Preserves exact colors, clips out-of-gamut | Proofing |
| Saturation | Maximizes vividness | Charts, 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.
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.