0
# Color Management
1
2
Color space transformations and ICC profile handling using Little-CMS for accurate color reproduction and conversion between different color spaces. This enables precise color workflows for photography, printing, and scientific imaging applications.
3
4
## Capabilities
5
6
### Color Space Transformation
7
8
Transform image data between different color spaces using ICC profiles or built-in color space definitions.
9
10
```python { .api }
11
def cms_transform(data, profile, outprofile, *, colorspace=None, planar=None, outcolorspace=None, outplanar=None, outdtype=None, intent=None, flags=None, verbose=None, out=None):
12
"""
13
Return color-transformed array.
14
15
Parameters:
16
- data: NDArray - Image data to transform (2D grayscale or 3D color)
17
- profile: bytes | str - Input ICC profile data or color space name:
18
Built-in names: 'srgb', 'adobe_rgb', 'prophoto_rgb', 'lab', 'xyz', 'gray'
19
- outprofile: bytes | str - Output ICC profile data or color space name
20
- colorspace: str | None - Input color space interpretation:
21
'rgb', 'rgba', 'bgr', 'bgra', 'cmyk', 'gray', 'lab', 'xyz'
22
- planar: bool | None - Input data is planar (channels as separate arrays)
23
- outcolorspace: str | None - Output color space interpretation
24
- outplanar: bool | None - Output data as planar format
25
- outdtype: numpy.dtype | None - Output data type (default same as input)
26
- intent: str | None - Rendering intent:
27
'perceptual' (default), 'relative', 'saturation', 'absolute'
28
- flags: int | None - Transformation flags (bitwise OR of CMS constants)
29
- verbose: bool | None - Enable verbose output
30
- out: NDArray | None - Pre-allocated output buffer
31
32
Returns:
33
NDArray: Color-transformed image data
34
"""
35
36
def cms_encode(data, profile, outprofile, **kwargs):
37
"""
38
Alias for cms_transform for consistency with other codecs.
39
40
Returns:
41
NDArray: Color-transformed image data
42
"""
43
44
def cms_decode(data, profile, outprofile, **kwargs):
45
"""
46
Alias for cms_transform for consistency with other codecs.
47
48
Returns:
49
NDArray: Color-transformed image data
50
"""
51
```
52
53
### ICC Profile Creation
54
55
Create ICC profiles for standard color spaces or custom color space definitions.
56
57
```python { .api }
58
def cms_profile(profile, *, whitepoint=None, primaries=None, transferfunction=None, gamma=None):
59
"""
60
Return ICC profile data.
61
62
Parameters:
63
- profile: str - Profile type to create:
64
'srgb', 'adobe_rgb', 'prophoto_rgb', 'rec2020', 'dci_p3',
65
'lab', 'xyz', 'gray_gamma22', 'gray_gamma18', 'gray_linear'
66
- whitepoint: tuple | None - White point coordinates (x, y) or temperature (K)
67
Common: (0.3127, 0.3290) for D65, 6504 for D65 temperature
68
- primaries: tuple | None - Color primaries as ((rx,ry), (gx,gy), (bx,by))
69
- transferfunction: str | None - Transfer function type:
70
'gamma', 'srgb', 'rec709', 'linear', 'lab'
71
- gamma: float | None - Gamma value for gamma transfer function
72
73
Returns:
74
bytes: ICC profile data
75
"""
76
```
77
78
### Profile Validation
79
80
Validate ICC profiles and check for common issues.
81
82
```python { .api }
83
def cms_profile_validate(profile, *, verbose=False):
84
"""
85
Validate ICC profile and raise CmsError if invalid.
86
87
Parameters:
88
- profile: bytes - ICC profile data to validate
89
- verbose: bool - Print detailed validation information
90
91
Returns:
92
None: Returns successfully if profile is valid
93
94
Raises:
95
CmsError: If profile is invalid or corrupted
96
"""
97
98
def cms_check(data):
99
"""
100
Check if data is an ICC profile.
101
102
Parameters:
103
- data: bytes | bytearray | mmap.mmap - Data to check
104
105
Returns:
106
bool: True if ICC profile signature detected
107
"""
108
```
109
110
### Profile Information
111
112
Extract information from ICC profiles.
113
114
```python { .api }
115
def cms_version():
116
"""
117
Return Little-CMS version string.
118
119
Returns:
120
str: Version information
121
"""
122
```
123
124
## Usage Examples
125
126
### Basic Color Space Conversion
127
128
```python
129
import imagecodecs
130
import numpy as np
131
132
# Create RGB test image
133
rgb_image = np.random.randint(0, 256, (256, 256, 3), dtype=np.uint8)
134
135
# Convert sRGB to Adobe RGB
136
adobe_rgb = imagecodecs.cms_transform(
137
rgb_image,
138
profile='srgb',
139
outprofile='adobe_rgb',
140
intent='perceptual'
141
)
142
143
# Convert to LAB color space for analysis
144
lab_image = imagecodecs.cms_transform(
145
rgb_image,
146
profile='srgb',
147
outprofile='lab',
148
colorspace='rgb',
149
outcolorspace='lab'
150
)
151
152
print(f"RGB shape: {rgb_image.shape}, dtype: {rgb_image.dtype}")
153
print(f"LAB shape: {lab_image.shape}, dtype: {lab_image.dtype}")
154
print(f"LAB L* range: {lab_image[:,:,0].min():.1f} to {lab_image[:,:,0].max():.1f}")
155
```
156
157
### Working with Custom ICC Profiles
158
159
```python
160
import imagecodecs
161
import numpy as np
162
163
# Create custom profile
164
custom_profile = imagecodecs.cms_profile(
165
'srgb',
166
whitepoint=(0.3127, 0.3290), # D65 white point
167
gamma=2.2
168
)
169
170
# Validate the profile
171
try:
172
imagecodecs.cms_profile_validate(custom_profile, verbose=True)
173
print("Profile is valid")
174
except imagecodecs.CmsError as e:
175
print(f"Profile validation failed: {e}")
176
177
# Load image and apply custom profile
178
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
179
180
# Transform using custom profile
181
transformed = imagecodecs.cms_transform(
182
image,
183
profile=custom_profile,
184
outprofile='prophoto_rgb',
185
intent='relative'
186
)
187
```
188
189
### Professional Photography Workflow
190
191
```python
192
import imagecodecs
193
import numpy as np
194
195
# Simulate RAW sensor data (linear RGB)
196
sensor_data = np.random.random((2048, 3072, 3)).astype(np.float32)
197
198
# Create linear RGB profile
199
linear_profile = imagecodecs.cms_profile('srgb', transferfunction='linear')
200
201
# Create output profile for web
202
web_profile = imagecodecs.cms_profile('srgb')
203
204
# Transform from linear sensor RGB to sRGB for web
205
web_image = imagecodecs.cms_transform(
206
sensor_data,
207
profile=linear_profile,
208
outprofile=web_profile,
209
intent='perceptual',
210
outdtype=np.uint8
211
)
212
213
# Transform to ProPhoto RGB for printing
214
print_profile = imagecodecs.cms_profile('prophoto_rgb')
215
print_image = imagecodecs.cms_transform(
216
sensor_data,
217
profile=linear_profile,
218
outprofile=print_profile,
219
intent='relative',
220
outdtype=np.uint16
221
)
222
223
print(f"Web image: {web_image.shape}, {web_image.dtype}")
224
print(f"Print image: {print_image.shape}, {print_image.dtype}")
225
```
226
227
### CMYK Color Separation
228
229
```python
230
import imagecodecs
231
import numpy as np
232
233
# RGB image for printing
234
rgb_image = np.random.randint(0, 256, (400, 600, 3), dtype=np.uint8)
235
236
# Create CMYK profile for printing
237
cmyk_profile = imagecodecs.cms_profile('cmyk_coated') # Assuming this profile exists
238
239
# Convert RGB to CMYK for printing
240
try:
241
cmyk_image = imagecodecs.cms_transform(
242
rgb_image,
243
profile='srgb',
244
outprofile=cmyk_profile,
245
colorspace='rgb',
246
outcolorspace='cmyk',
247
intent='perceptual' # Good for photographic content
248
)
249
250
print(f"RGB: {rgb_image.shape} -> CMYK: {cmyk_image.shape}")
251
print(f"CMYK channels - C: {cmyk_image[:,:,0].mean():.1f}, "
252
f"M: {cmyk_image[:,:,1].mean():.1f}, "
253
f"Y: {cmyk_image[:,:,2].mean():.1f}, "
254
f"K: {cmyk_image[:,:,3].mean():.1f}")
255
256
except imagecodecs.CmsError as e:
257
print(f"CMYK conversion failed: {e}")
258
```
259
260
### Scientific Color Analysis
261
262
```python
263
import imagecodecs
264
import numpy as np
265
266
# Multispectral or hyperspectral imaging data
267
spectral_image = np.random.random((256, 256, 16)).astype(np.float32)
268
269
# Convert first 3 bands to approximate RGB
270
rgb_bands = spectral_image[:, :, [4, 2, 1]] # Select appropriate bands
271
272
# Apply color correction for display
273
display_image = imagecodecs.cms_transform(
274
rgb_bands,
275
profile='adobe_rgb', # Wider gamut for scientific data
276
outprofile='srgb', # For display
277
intent='absolute' # Preserve absolute colorimetric values
278
)
279
280
# Convert to LAB for perceptual analysis
281
lab_image = imagecodecs.cms_transform(
282
display_image,
283
profile='srgb',
284
outprofile='lab',
285
colorspace='rgb',
286
outcolorspace='lab'
287
)
288
289
# Analyze color distribution in LAB space
290
L_channel = lab_image[:, :, 0] # Lightness
291
a_channel = lab_image[:, :, 1] # Green-Red axis
292
b_channel = lab_image[:, :, 2] # Blue-Yellow axis
293
294
print(f"Lightness range: {L_channel.min():.1f} to {L_channel.max():.1f}")
295
print(f"Green-Red range: {a_channel.min():.1f} to {a_channel.max():.1f}")
296
print(f"Blue-Yellow range: {b_channel.min():.1f} to {b_channel.max():.1f}")
297
```
298
299
### Batch Profile Application
300
301
```python
302
import imagecodecs
303
import numpy as np
304
305
# Simulate batch of images with different source profiles
306
images = [
307
(np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'srgb'),
308
(np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'adobe_rgb'),
309
(np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'prophoto_rgb'),
310
]
311
312
# Target profile for consistent output
313
target_profile = 'srgb'
314
target_intent = 'perceptual'
315
316
# Process batch with consistent output
317
processed_images = []
318
for image, source_profile in images:
319
try:
320
converted = imagecodecs.cms_transform(
321
image,
322
profile=source_profile,
323
outprofile=target_profile,
324
intent=target_intent
325
)
326
processed_images.append(converted)
327
print(f"Converted {source_profile} -> {target_profile}")
328
except Exception as e:
329
print(f"Failed to convert {source_profile}: {e}")
330
processed_images.append(image) # Use original if conversion fails
331
332
print(f"Processed {len(processed_images)} images")
333
```
334
335
### Profile Embedding and Extraction
336
337
```python
338
import imagecodecs
339
import numpy as np
340
341
# Create image with embedded profile
342
image = np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
343
adobe_profile = imagecodecs.cms_profile('adobe_rgb')
344
345
# Simulate saving image with embedded profile (conceptual)
346
# In practice, this would be done by the image format encoder
347
image_with_profile = {
348
'data': image,
349
'profile': adobe_profile,
350
'colorspace': 'rgb'
351
}
352
353
# Later, when loading the image, use the embedded profile
354
if 'profile' in image_with_profile:
355
# Convert from embedded profile to working space
356
working_image = imagecodecs.cms_transform(
357
image_with_profile['data'],
358
profile=image_with_profile['profile'],
359
outprofile='srgb',
360
intent='perceptual'
361
)
362
print("Applied embedded color profile")
363
else:
364
# Assume sRGB if no profile
365
working_image = image_with_profile['data']
366
print("No embedded profile, assuming sRGB")
367
```
368
369
## Color Space Reference
370
371
### Built-in Color Spaces
372
373
**RGB Color Spaces:**
374
- `'srgb'` - Standard RGB (IEC 61966-2-1)
375
- `'adobe_rgb'` - Adobe RGB (1998)
376
- `'prophoto_rgb'` - ProPhoto RGB (ROMM RGB)
377
- `'rec2020'` - ITU-R BT.2020 (Ultra HDTV)
378
- `'dci_p3'` - DCI-P3 (Digital Cinema)
379
380
**Device-Independent Color Spaces:**
381
- `'lab'` - CIE L*a*b* (perceptually uniform)
382
- `'xyz'` - CIE XYZ (colorimetric)
383
- `'luv'` - CIE L*u*v* (alternative uniform space)
384
385
**Grayscale:**
386
- `'gray'` - Linear grayscale
387
- `'gray_gamma22'` - Gamma 2.2 grayscale
388
- `'gray_gamma18'` - Gamma 1.8 grayscale
389
390
### Rendering Intents
391
392
**Intent Selection Guidelines:**
393
- `'perceptual'` - Best for photographic content, maintains visual relationships
394
- `'relative'` - Good for graphics, maintains white point mapping
395
- `'saturation'` - Preserves color saturation, good for business graphics
396
- `'absolute'` - Exact colorimetric match, for proofing and scientific applications
397
398
## Constants and Configuration
399
400
### CMS Constants
401
402
```python { .api }
403
class CMS:
404
available: bool
405
406
class INTENT:
407
PERCEPTUAL = 0
408
RELATIVE_COLORIMETRIC = 1
409
SATURATION = 2
410
ABSOLUTE_COLORIMETRIC = 3
411
412
class FLAGS:
413
NOTPRECALC = 0x0100 # Disable pre-calculation
414
GAMUTCHECK = 0x1000 # Enable gamut checking
415
SOFTPROOFING = 0x4000 # Soft proofing mode
416
BLACKPOINTCOMPENSATION = 0x2000 # Black point compensation
417
NOWHITEONWHITEFIXUP = 0x0004 # Disable white on white fixup
418
HIGHRESPRECALC = 0x0400 # Use high resolution pre-calculation
419
LOWRESPRECALC = 0x0800 # Use low resolution pre-calculation
420
421
class PT:
422
# Pixel types for colorspace specification
423
GRAY = 0
424
RGB = 1
425
CMY = 2
426
CMYK = 3
427
YCbCr = 4
428
YUV = 5
429
XYZ = 6
430
Lab = 7
431
YUVK = 8
432
HSV = 9
433
HLS = 10
434
Yxy = 11
435
```
436
437
### Common White Points
438
439
```python
440
# Standard illuminants (x, y coordinates)
441
D50_WHITEPOINT = (0.3457, 0.3585) # Printing standard
442
D65_WHITEPOINT = (0.3127, 0.3290) # Daylight, sRGB standard
443
A_WHITEPOINT = (0.4476, 0.4074) # Incandescent light
444
E_WHITEPOINT = (0.3333, 0.3333) # Equal energy
445
```
446
447
## Performance Considerations
448
449
### Optimization Guidelines
450
- Pre-create profiles for repeated transformations
451
- Use appropriate rendering intent for your use case
452
- Cache transformation objects for batch processing
453
- Consider data type precision (uint8 vs uint16 vs float32)
454
455
### Memory Management
456
- Pre-allocate output buffers for large images
457
- Use in-place transformations when possible
458
- Process images in batches for memory efficiency
459
460
### Accuracy vs Speed
461
- Higher precision calculations may be slower
462
- Black point compensation improves perceptual quality
463
- Gamut checking adds overhead but prevents out-of-gamut colors
464
465
## Error Handling
466
467
```python { .api }
468
class CmsError(Exception):
469
"""CMS codec exception for color management errors."""
470
471
# Common error scenarios:
472
# - Invalid ICC profile data
473
# - Incompatible color space conversion
474
# - Unsupported pixel format
475
# - Profile creation failure
476
```
477
478
Common error handling patterns:
479
480
```python
481
import imagecodecs
482
483
try:
484
transformed = imagecodecs.cms_transform(image, 'srgb', 'adobe_rgb')
485
except imagecodecs.CmsError as e:
486
print(f"Color transformation failed: {e}")
487
# Fallback to original image or alternative profile
488
transformed = image
489
except imagecodecs.DelayedImportError:
490
print("Color management not available, using original image")
491
transformed = image
492
```