CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-imagecodecs

Image transformation, compression, and decompression codecs for scientific computing

Pending
Overview
Eval results
Files

color-management.mddocs/

Color Management

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.

Capabilities

Color Space Transformation

Transform image data between different color spaces using ICC profiles or built-in color space definitions.

def cms_transform(data, profile, outprofile, *, colorspace=None, planar=None, outcolorspace=None, outplanar=None, outdtype=None, intent=None, flags=None, verbose=None, out=None):
    """
    Return color-transformed array.
    
    Parameters:
    - data: NDArray - Image data to transform (2D grayscale or 3D color)
    - profile: bytes | str - Input ICC profile data or color space name:
        Built-in names: 'srgb', 'adobe_rgb', 'prophoto_rgb', 'lab', 'xyz', 'gray'
    - outprofile: bytes | str - Output ICC profile data or color space name
    - colorspace: str | None - Input color space interpretation:
        'rgb', 'rgba', 'bgr', 'bgra', 'cmyk', 'gray', 'lab', 'xyz'
    - planar: bool | None - Input data is planar (channels as separate arrays)
    - outcolorspace: str | None - Output color space interpretation
    - outplanar: bool | None - Output data as planar format
    - outdtype: numpy.dtype | None - Output data type (default same as input)
    - intent: str | None - Rendering intent:
        'perceptual' (default), 'relative', 'saturation', 'absolute'
    - flags: int | None - Transformation flags (bitwise OR of CMS constants)
    - verbose: bool | None - Enable verbose output
    - out: NDArray | None - Pre-allocated output buffer
    
    Returns:
    NDArray: Color-transformed image data
    """

def cms_encode(data, profile, outprofile, **kwargs):
    """
    Alias for cms_transform for consistency with other codecs.
    
    Returns:
    NDArray: Color-transformed image data
    """

def cms_decode(data, profile, outprofile, **kwargs):
    """
    Alias for cms_transform for consistency with other codecs.
    
    Returns:
    NDArray: Color-transformed image data
    """

ICC Profile Creation

Create ICC profiles for standard color spaces or custom color space definitions.

def cms_profile(profile, *, whitepoint=None, primaries=None, transferfunction=None, gamma=None):
    """
    Return ICC profile data.
    
    Parameters:
    - profile: str - Profile type to create:
        'srgb', 'adobe_rgb', 'prophoto_rgb', 'rec2020', 'dci_p3',
        'lab', 'xyz', 'gray_gamma22', 'gray_gamma18', 'gray_linear'
    - whitepoint: tuple | None - White point coordinates (x, y) or temperature (K)
        Common: (0.3127, 0.3290) for D65, 6504 for D65 temperature
    - primaries: tuple | None - Color primaries as ((rx,ry), (gx,gy), (bx,by))
    - transferfunction: str | None - Transfer function type:
        'gamma', 'srgb', 'rec709', 'linear', 'lab'
    - gamma: float | None - Gamma value for gamma transfer function
    
    Returns:
    bytes: ICC profile data
    """

Profile Validation

Validate ICC profiles and check for common issues.

def cms_profile_validate(profile, *, verbose=False):
    """
    Validate ICC profile and raise CmsError if invalid.
    
    Parameters:
    - profile: bytes - ICC profile data to validate
    - verbose: bool - Print detailed validation information
    
    Returns:
    None: Returns successfully if profile is valid
    
    Raises:
    CmsError: If profile is invalid or corrupted
    """

def cms_check(data):
    """
    Check if data is an ICC profile.
    
    Parameters:
    - data: bytes | bytearray | mmap.mmap - Data to check
    
    Returns:
    bool: True if ICC profile signature detected
    """

Profile Information

Extract information from ICC profiles.

def cms_version():
    """
    Return Little-CMS version string.
    
    Returns:
    str: Version information
    """

Usage Examples

Basic Color Space Conversion

import imagecodecs
import numpy as np

# Create RGB test image
rgb_image = np.random.randint(0, 256, (256, 256, 3), dtype=np.uint8)

# Convert sRGB to Adobe RGB
adobe_rgb = imagecodecs.cms_transform(
    rgb_image,
    profile='srgb',
    outprofile='adobe_rgb',
    intent='perceptual'
)

# Convert to LAB color space for analysis
lab_image = imagecodecs.cms_transform(
    rgb_image,
    profile='srgb', 
    outprofile='lab',
    colorspace='rgb',
    outcolorspace='lab'
)

print(f"RGB shape: {rgb_image.shape}, dtype: {rgb_image.dtype}")
print(f"LAB shape: {lab_image.shape}, dtype: {lab_image.dtype}")
print(f"LAB L* range: {lab_image[:,:,0].min():.1f} to {lab_image[:,:,0].max():.1f}")

Working with Custom ICC Profiles

import imagecodecs
import numpy as np

# Create custom profile
custom_profile = imagecodecs.cms_profile(
    'srgb',
    whitepoint=(0.3127, 0.3290),  # D65 white point
    gamma=2.2
)

# Validate the profile
try:
    imagecodecs.cms_profile_validate(custom_profile, verbose=True)
    print("Profile is valid")
except imagecodecs.CmsError as e:
    print(f"Profile validation failed: {e}")

# Load image and apply custom profile
image = np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)

# Transform using custom profile
transformed = imagecodecs.cms_transform(
    image,
    profile=custom_profile,
    outprofile='prophoto_rgb',
    intent='relative'
)

Professional Photography Workflow

import imagecodecs
import numpy as np

# Simulate RAW sensor data (linear RGB)
sensor_data = np.random.random((2048, 3072, 3)).astype(np.float32)

# Create linear RGB profile  
linear_profile = imagecodecs.cms_profile('srgb', transferfunction='linear')

# Create output profile for web
web_profile = imagecodecs.cms_profile('srgb')

# Transform from linear sensor RGB to sRGB for web
web_image = imagecodecs.cms_transform(
    sensor_data,
    profile=linear_profile,
    outprofile=web_profile,
    intent='perceptual',
    outdtype=np.uint8
)

# Transform to ProPhoto RGB for printing
print_profile = imagecodecs.cms_profile('prophoto_rgb')
print_image = imagecodecs.cms_transform(
    sensor_data,
    profile=linear_profile,
    outprofile=print_profile,
    intent='relative',
    outdtype=np.uint16
)

print(f"Web image: {web_image.shape}, {web_image.dtype}")
print(f"Print image: {print_image.shape}, {print_image.dtype}")

CMYK Color Separation

import imagecodecs
import numpy as np

# RGB image for printing
rgb_image = np.random.randint(0, 256, (400, 600, 3), dtype=np.uint8)

# Create CMYK profile for printing
cmyk_profile = imagecodecs.cms_profile('cmyk_coated')  # Assuming this profile exists

# Convert RGB to CMYK for printing
try:
    cmyk_image = imagecodecs.cms_transform(
        rgb_image,
        profile='srgb',
        outprofile=cmyk_profile,
        colorspace='rgb',
        outcolorspace='cmyk',
        intent='perceptual'  # Good for photographic content
    )
    
    print(f"RGB: {rgb_image.shape} -> CMYK: {cmyk_image.shape}")
    print(f"CMYK channels - C: {cmyk_image[:,:,0].mean():.1f}, "
          f"M: {cmyk_image[:,:,1].mean():.1f}, "
          f"Y: {cmyk_image[:,:,2].mean():.1f}, "
          f"K: {cmyk_image[:,:,3].mean():.1f}")
          
except imagecodecs.CmsError as e:
    print(f"CMYK conversion failed: {e}")

Scientific Color Analysis

import imagecodecs
import numpy as np

# Multispectral or hyperspectral imaging data
spectral_image = np.random.random((256, 256, 16)).astype(np.float32)

# Convert first 3 bands to approximate RGB
rgb_bands = spectral_image[:, :, [4, 2, 1]]  # Select appropriate bands

# Apply color correction for display
display_image = imagecodecs.cms_transform(
    rgb_bands,
    profile='adobe_rgb',  # Wider gamut for scientific data
    outprofile='srgb',    # For display
    intent='absolute'     # Preserve absolute colorimetric values
)

# Convert to LAB for perceptual analysis
lab_image = imagecodecs.cms_transform(
    display_image,
    profile='srgb',
    outprofile='lab',
    colorspace='rgb',
    outcolorspace='lab'
)

# Analyze color distribution in LAB space
L_channel = lab_image[:, :, 0]  # Lightness
a_channel = lab_image[:, :, 1]  # Green-Red axis
b_channel = lab_image[:, :, 2]  # Blue-Yellow axis

print(f"Lightness range: {L_channel.min():.1f} to {L_channel.max():.1f}")
print(f"Green-Red range: {a_channel.min():.1f} to {a_channel.max():.1f}")
print(f"Blue-Yellow range: {b_channel.min():.1f} to {b_channel.max():.1f}")

Batch Profile Application

import imagecodecs
import numpy as np

# Simulate batch of images with different source profiles
images = [
    (np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'srgb'),
    (np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'adobe_rgb'),
    (np.random.randint(0, 256, (200, 300, 3), dtype=np.uint8), 'prophoto_rgb'),
]

# Target profile for consistent output
target_profile = 'srgb'
target_intent = 'perceptual'

# Process batch with consistent output
processed_images = []
for image, source_profile in images:
    try:
        converted = imagecodecs.cms_transform(
            image,
            profile=source_profile,
            outprofile=target_profile,
            intent=target_intent
        )
        processed_images.append(converted)
        print(f"Converted {source_profile} -> {target_profile}")
    except Exception as e:
        print(f"Failed to convert {source_profile}: {e}")
        processed_images.append(image)  # Use original if conversion fails

print(f"Processed {len(processed_images)} images")

Profile Embedding and Extraction

import imagecodecs
import numpy as np

# Create image with embedded profile
image = np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
adobe_profile = imagecodecs.cms_profile('adobe_rgb')

# Simulate saving image with embedded profile (conceptual)
# In practice, this would be done by the image format encoder
image_with_profile = {
    'data': image,
    'profile': adobe_profile,
    'colorspace': 'rgb'
}

# Later, when loading the image, use the embedded profile
if 'profile' in image_with_profile:
    # Convert from embedded profile to working space
    working_image = imagecodecs.cms_transform(
        image_with_profile['data'],
        profile=image_with_profile['profile'],
        outprofile='srgb',
        intent='perceptual'
    )
    print("Applied embedded color profile")
else:
    # Assume sRGB if no profile
    working_image = image_with_profile['data']
    print("No embedded profile, assuming sRGB")

Color Space Reference

Built-in Color Spaces

RGB Color Spaces:

  • 'srgb' - Standard RGB (IEC 61966-2-1)
  • 'adobe_rgb' - Adobe RGB (1998)
  • 'prophoto_rgb' - ProPhoto RGB (ROMM RGB)
  • 'rec2020' - ITU-R BT.2020 (Ultra HDTV)
  • 'dci_p3' - DCI-P3 (Digital Cinema)

Device-Independent Color Spaces:

  • 'lab' - CIE Lab* (perceptually uniform)
  • 'xyz' - CIE XYZ (colorimetric)
  • 'luv' - CIE Luv* (alternative uniform space)

Grayscale:

  • 'gray' - Linear grayscale
  • 'gray_gamma22' - Gamma 2.2 grayscale
  • 'gray_gamma18' - Gamma 1.8 grayscale

Rendering Intents

Intent Selection Guidelines:

  • 'perceptual' - Best for photographic content, maintains visual relationships
  • 'relative' - Good for graphics, maintains white point mapping
  • 'saturation' - Preserves color saturation, good for business graphics
  • 'absolute' - Exact colorimetric match, for proofing and scientific applications

Constants and Configuration

CMS Constants

class CMS:
    available: bool
    
    class INTENT:
        PERCEPTUAL = 0
        RELATIVE_COLORIMETRIC = 1
        SATURATION = 2
        ABSOLUTE_COLORIMETRIC = 3
    
    class FLAGS:
        NOTPRECALC = 0x0100        # Disable pre-calculation
        GAMUTCHECK = 0x1000        # Enable gamut checking
        SOFTPROOFING = 0x4000      # Soft proofing mode
        BLACKPOINTCOMPENSATION = 0x2000  # Black point compensation
        NOWHITEONWHITEFIXUP = 0x0004     # Disable white on white fixup
        HIGHRESPRECALC = 0x0400    # Use high resolution pre-calculation
        LOWRESPRECALC = 0x0800     # Use low resolution pre-calculation
    
    class PT:
        # Pixel types for colorspace specification
        GRAY = 0
        RGB = 1
        CMY = 2
        CMYK = 3
        YCbCr = 4
        YUV = 5
        XYZ = 6
        Lab = 7
        YUVK = 8
        HSV = 9
        HLS = 10
        Yxy = 11

Common White Points

# Standard illuminants (x, y coordinates)
D50_WHITEPOINT = (0.3457, 0.3585)  # Printing standard
D65_WHITEPOINT = (0.3127, 0.3290)  # Daylight, sRGB standard
A_WHITEPOINT = (0.4476, 0.4074)    # Incandescent light
E_WHITEPOINT = (0.3333, 0.3333)    # Equal energy

Performance Considerations

Optimization Guidelines

  • Pre-create profiles for repeated transformations
  • Use appropriate rendering intent for your use case
  • Cache transformation objects for batch processing
  • Consider data type precision (uint8 vs uint16 vs float32)

Memory Management

  • Pre-allocate output buffers for large images
  • Use in-place transformations when possible
  • Process images in batches for memory efficiency

Accuracy vs Speed

  • Higher precision calculations may be slower
  • Black point compensation improves perceptual quality
  • Gamut checking adds overhead but prevents out-of-gamut colors

Error Handling

class CmsError(Exception):
    """CMS codec exception for color management errors."""
    
    # Common error scenarios:
    # - Invalid ICC profile data
    # - Incompatible color space conversion
    # - Unsupported pixel format
    # - Profile creation failure

Common error handling patterns:

import imagecodecs

try:
    transformed = imagecodecs.cms_transform(image, 'srgb', 'adobe_rgb')
except imagecodecs.CmsError as e:
    print(f"Color transformation failed: {e}")
    # Fallback to original image or alternative profile
    transformed = image
except imagecodecs.DelayedImportError:
    print("Color management not available, using original image")
    transformed = image

Install with Tessl CLI

npx tessl i tessl/pypi-imagecodecs

docs

array-processing.md

color-management.md

image-formats.md

image-io.md

index.md

lossless-compression.md

scientific-compression.md

utilities.md

tile.json