or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

array-processing.mdcolor-management.mdimage-formats.mdimage-io.mdindex.mdlossless-compression.mdscientific-compression.mdutilities.md

color-management.mddocs/

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

```