Comprehensive Python library providing algorithms and datasets for colour science computations, including chromatic adaptation, colour appearance models, colorimetry, and spectral analysis.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive input/output capabilities including image I/O, LUT processing, spectral data file handling, and various file format support. The I/O system provides robust reading and writing capabilities across multiple formats with automatic format detection and validation.
Core functions for reading and writing images across various formats with support for multiple backends and bit depth handling.
def read_image(path: Union[str, Path], method: str = None, **kwargs) -> NDArray:
"""
Read an image from disk using the specified method.
Parameters:
- path: path to the image file
- method: reading method ('OpenImageIO' or 'Imageio'), auto-detected if None
- **kwargs: additional arguments passed to the specific reading method
Returns:
Image data as numpy array with shape (height, width, channels)
Supported formats: PNG, JPEG, TIFF, EXR, HDR, PFM, and many others
"""
def write_image(image: ArrayLike, path: Union[str, Path], bit_depth: Union[str, int] = None,
method: str = None, **kwargs) -> bool:
"""
Write an image to disk using the specified method.
Parameters:
- image: image data as array-like (height, width, channels)
- path: output file path
- bit_depth: bit depth for output ('uint8', 'uint16', 'float32', etc.)
- method: writing method ('OpenImageIO' or 'Imageio'), auto-detected if None
- **kwargs: additional arguments passed to the specific writing method
Returns:
True if successful, False otherwise
"""
def read_image_OpenImageIO(path: Union[str, Path], bit_depth: str = "float32",
attributes: bool = True, **kwargs) -> Union[NDArray, Tuple[NDArray, dict]]:
"""
Read an image using OpenImageIO backend.
Parameters:
- path: path to the image file
- bit_depth: target bit depth ('uint8', 'uint16', 'float16', 'float32')
- attributes: whether to return image attributes
- **kwargs: additional OpenImageIO parameters
Returns:
Image array, or tuple of (image array, attributes dict) if attributes=True
"""
def write_image_OpenImageIO(image: ArrayLike, path: Union[str, Path], bit_depth: str = "float32",
attributes: List[Image_Specification_Attribute] = None, **kwargs) -> bool:
"""
Write an image using OpenImageIO backend.
Parameters:
- image: image data as array-like
- path: output file path
- bit_depth: output bit depth ('uint8', 'uint16', 'float16', 'float32')
- attributes: list of image attributes to embed
- **kwargs: additional OpenImageIO parameters
Returns:
True if successful
"""
def read_image_Imageio(path: Union[str, Path], **kwargs) -> NDArray:
"""
Read an image using Imageio backend.
Parameters:
- path: path to the image file
- **kwargs: additional imageio parameters
Returns:
Image data as numpy array
"""
def write_image_Imageio(image: ArrayLike, path: Union[str, Path], **kwargs) -> bool:
"""
Write an image using Imageio backend.
Parameters:
- image: image data as array-like
- path: output file path
- **kwargs: additional imageio parameters
Returns:
True if successful
"""Classes and utilities for handling image metadata and bit depth specifications.
@dataclass
class Image_Specification_Attribute:
"""
Define an image specification attribute for metadata embedding.
Attributes:
- name: attribute name (str)
- value: attribute value (Any)
- type_: attribute type hint (Type, optional)
"""
name: str
value: Any
type_: Type = None
@dataclass(frozen=True)
class Image_Specification_BitDepth:
"""
Define a bit-depth specification for image processing.
Attributes:
- name: bit depth name (str)
- numpy: numpy data type
- openimageio: OpenImageIO type specification
"""
name: str
numpy: Type[DTypeReal]
openimageio: Any
def convert_bit_depth(a: ArrayLike, bit_depth: str) -> NDArray:
"""
Convert array to specified bit depth with proper scaling.
Parameters:
- a: input array
- bit_depth: target bit depth ('uint8', 'uint16', 'float32', etc.)
Returns:
Array converted to target bit depth
"""
def as_3_channels_image(a: ArrayLike) -> NDArray:
"""
Convert array to 3-channel image format.
Parameters:
- a: input image array (1, 2, 3, or 4 channels)
Returns:
3-channel image array (RGB)
"""
MAPPING_BIT_DEPTH: dict = {
"uint8": Image_Specification_BitDepth("uint8", np.uint8, ...),
"uint16": Image_Specification_BitDepth("uint16", np.uint16, ...),
"float16": Image_Specification_BitDepth("float16", np.float16, ...),
"float32": Image_Specification_BitDepth("float32", np.float32, ...),
# ... additional bit depth mappings
}
"""Mapping of bit depth names to specifications."""Comprehensive Look-Up Table classes for 1D, 3x1D, and 3D transformations with interpolation and manipulation capabilities.
class LUT1D:
"""
1D Look-Up Table for single-channel transformations.
Parameters:
- table: 1D array of LUT values, or None for linear table
- name: LUT name (str, optional)
- domain: input domain range as [min, max] array
- size: LUT size (int, used if table is None)
- comments: list of comment strings
"""
def __init__(self, table: ArrayLike = None, name: str = None,
domain: ArrayLike = None, size: int = None,
comments: Sequence[str] = None): ...
@property
def table(self) -> NDArray: ...
@property
def name(self) -> str: ...
@property
def domain(self) -> NDArray: ...
@property
def size(self) -> int: ...
@property
def comments(self) -> List[str]: ...
def apply(self, RGB: ArrayLike, interpolator: str = "Linear",
interpolator_kwargs: dict = None, **kwargs) -> NDArray:
"""Apply 1D LUT to input values with interpolation."""
def copy(self) -> "LUT1D":
"""Return a copy of the LUT."""
def invert(self, **kwargs) -> "LUT1D":
"""Return inverted LUT."""
@staticmethod
def linear_table(size: int = 33, domain: ArrayLike = None) -> NDArray:
"""Generate a linear table for the given size and domain."""
class LUT3x1D:
"""
3x1D Look-Up Table for independent per-channel transformations.
Parameters:
- table: 2D array of shape (size, 3) with RGB channel LUTs
- name: LUT name (str, optional)
- domain: input domain range as [[R_min, G_min, B_min], [R_max, G_max, B_max]]
- size: LUT size (int, used if table is None)
- comments: list of comment strings
"""
def __init__(self, table: ArrayLike = None, name: str = None,
domain: ArrayLike = None, size: int = None,
comments: Sequence[str] = None): ...
@property
def table(self) -> NDArray: ...
@property
def name(self) -> str: ...
@property
def domain(self) -> NDArray: ...
@property
def size(self) -> int: ...
@property
def comments(self) -> List[str]: ...
def apply(self, RGB: ArrayLike, interpolator: str = "Linear",
interpolator_kwargs: dict = None, **kwargs) -> NDArray:
"""Apply 3x1D LUT to RGB input with per-channel interpolation."""
def copy(self) -> "LUT3x1D": ...
def invert(self, **kwargs) -> "LUT3x1D": ...
@staticmethod
def linear_table(size: int = 33, domain: ArrayLike = None) -> NDArray: ...
class LUT3D:
"""
3D Look-Up Table for full 3D colour transformations.
Parameters:
- table: 4D array of shape (size_r, size_g, size_b, 3) with RGB output values
- name: LUT name (str, optional)
- domain: input domain range as [[R_min, G_min, B_min], [R_max, G_max, B_max]]
- size: LUT size (int or array-like, used if table is None)
- comments: list of comment strings
"""
def __init__(self, table: ArrayLike = None, name: str = None,
domain: ArrayLike = None, size: Union[int, ArrayLike] = None,
comments: Sequence[str] = None): ...
@property
def table(self) -> NDArray: ...
@property
def name(self) -> str: ...
@property
def domain(self) -> NDArray: ...
@property
def size(self) -> Union[int, NDArray]: ...
@property
def comments(self) -> List[str]: ...
def apply(self, RGB: ArrayLike, interpolator: str = "Trilinear",
interpolator_kwargs: dict = None, **kwargs) -> NDArray:
"""Apply 3D LUT to RGB input with trilinear interpolation."""
def copy(self) -> "LUT3D": ...
def invert(self, **kwargs) -> "LUT3D": ...
@staticmethod
def linear_table(size: Union[int, ArrayLike] = 33, domain: ArrayLike = None) -> NDArray: ...
class LUTSequence:
"""
Sequence of LUTs and operators applied in order.
Parameters:
- sequence: list of LUT objects and operators
- name: sequence name (str, optional)
- comments: list of comment strings
"""
def __init__(self, sequence: List = None, name: str = None,
comments: Sequence[str] = None): ...
def apply(self, RGB: ArrayLike, **kwargs) -> NDArray:
"""Apply the entire LUT sequence to input RGB."""
def append(self, LUT: Union[LUT1D, LUT3x1D, LUT3D]) -> None:
"""Append a LUT to the sequence."""
class LUTOperatorMatrix:
"""
Matrix operator for LUT sequences (scaling, offset, matrix multiplication).
Parameters:
- matrix: 4x4 transformation matrix
- offset: offset vector (4 elements)
- name: operator name (str, optional)
"""
def __init__(self, matrix: ArrayLike = None, offset: ArrayLike = None,
name: str = None): ...
def apply(self, RGB: ArrayLike, **kwargs) -> NDArray:
"""Apply matrix transformation to RGB input."""Functions for reading and writing LUTs in various industry-standard formats with automatic format detection.
def read_LUT(path: Union[str, Path], method: str = None, **kwargs) -> Union[LUT1D, LUT3x1D, LUT3D, LUTSequence, LUTOperatorMatrix]:
"""
Read LUT from file with automatic format detection.
Parameters:
- path: LUT file path
- method: reading method, auto-detected from extension if None
('Cinespace', 'Iridas Cube', 'Resolve Cube', 'Sony SPI1D', 'Sony SPI3D', 'Sony SPImtx')
- **kwargs: additional arguments passed to format-specific readers
Returns:
LUT object (LUT1D, LUT3x1D, LUT3D, LUTSequence, or LUTOperatorMatrix)
Supported formats:
- .cube: Iridas/Resolve Cube format
- .spi1d: Sony SPI 1D format
- .spi3d: Sony SPI 3D format
- .spimtx: Sony SPI Matrix format
- .csp: Cinespace format
"""
def write_LUT(LUT: Union[LUT1D, LUT3x1D, LUT3D, LUTSequence, LUTOperatorMatrix],
path: Union[str, Path], decimals: int = 7, method: str = None, **kwargs) -> bool:
"""
Write LUT to file with automatic format detection.
Parameters:
- LUT: LUT object to write
- path: output file path
- decimals: number of decimal places for formatting
- method: writing method, auto-detected from extension if None
- **kwargs: additional arguments passed to format-specific writers
Returns:
True if successful
"""
def read_LUT_IridasCube(path: Union[str, Path]) -> Union[LUT1D, LUT3x1D, LUT3D]:
"""Read Iridas .cube format LUT file."""
def write_LUT_IridasCube(LUT: Union[LUT1D, LUT3x1D, LUT3D], path: Union[str, Path],
decimals: int = 7) -> bool:
"""Write LUT to Iridas .cube format."""
def read_LUT_ResolveCube(path: Union[str, Path]) -> Union[LUTSequence, LUT3D]:
"""Read DaVinci Resolve .cube format with sequence support."""
def write_LUT_ResolveCube(LUT: Union[LUTSequence, LUT3D], path: Union[str, Path],
decimals: int = 7) -> bool:
"""Write LUT to DaVinci Resolve .cube format."""
def read_LUT_SonySPI1D(path: Union[str, Path]) -> LUT1D:
"""Read Sony .spi1d format LUT file."""
def write_LUT_SonySPI1D(LUT: LUT1D, path: Union[str, Path], decimals: int = 7) -> bool:
"""Write LUT to Sony .spi1d format."""
def read_LUT_SonySPI3D(path: Union[str, Path]) -> LUT3D:
"""Read Sony .spi3d format LUT file."""
def write_LUT_SonySPI3D(LUT: LUT3D, path: Union[str, Path], decimals: int = 7) -> bool:
"""Write LUT to Sony .spi3d format."""
def read_LUT_SonySPImtx(path: Union[str, Path]) -> LUTOperatorMatrix:
"""Read Sony .spimtx matrix format file."""
def write_LUT_SonySPImtx(LUT: LUTOperatorMatrix, path: Union[str, Path], decimals: int = 7) -> bool:
"""Write LUT to Sony .spimtx matrix format."""
def read_LUT_Cinespace(path: Union[str, Path]) -> Union[LUT1D, LUT3D]:
"""Read Cinespace .csp format LUT file."""
def write_LUT_Cinespace(LUT: Union[LUT1D, LUT3D], path: Union[str, Path], decimals: int = 7) -> bool:
"""Write LUT to Cinespace .csp format."""
def LUT_to_LUT(LUT: Union[LUT1D, LUT3x1D, LUT3D], LUT_class: Type,
force_conversion: bool = False, **kwargs) -> Union[LUT1D, LUT3x1D, LUT3D]:
"""
Convert between different LUT types.
Parameters:
- LUT: source LUT to convert
- LUT_class: target LUT class (LUT1D, LUT3x1D, or LUT3D)
- force_conversion: allow potentially lossy conversions
- **kwargs: additional arguments for conversion
Returns:
Converted LUT of target class
"""Functions for reading and writing spectral data from various file formats including CSV, X-Rite, and specialized spectroscopic formats.
def read_spectral_data_from_csv_file(path: Union[str, Path], **kwargs) -> Dict[str, NDArray]:
"""
Read spectral data from CSV file.
Parameters:
- path: CSV file path
- **kwargs: arguments passed to numpy.genfromtxt (delimiter, names, etc.)
Returns:
Dictionary with 'wavelength' key and numbered field keys
Expected CSV format:
wavelength, field1, field2, field3, ...
390, 0.0415, 0.0368, 0.0955, ...
395, 0.1052, 0.0959, 0.2383, ...
"""
def read_sds_from_csv_file(path: Union[str, Path], **kwargs) -> Dict[str, SpectralDistribution]:
"""
Read spectral distributions from CSV file.
Parameters:
- path: CSV file path
- **kwargs: arguments for read_spectral_data_from_csv_file
Returns:
Dictionary mapping field names to SpectralDistribution objects
"""
def write_sds_to_csv_file(sds: Dict[str, SpectralDistribution], path: Union[str, Path],
delimiter: str = ",", fields: List[str] = None) -> bool:
"""
Write spectral distributions to CSV file.
Parameters:
- sds: dictionary of SpectralDistribution objects
- path: output CSV file path
- delimiter: CSV delimiter character
- fields: list of SDS keys to write (all if None)
Returns:
True if successful
"""
def read_sds_from_xrite_file(path: Union[str, Path]) -> Dict[str, SpectralDistribution]:
"""
Read spectral data from X-Rite format file.
Parameters:
- path: X-Rite file path (.txt format)
Returns:
Dictionary mapping sample names to SpectralDistribution objects
Notes:
Supports X-Rite ColorChecker and similar spectral measurement files
"""Specialized classes and functions for reading/writing IES TM-27-14 spectral distribution files.
@dataclass
class Header_IESTM2714:
"""
IES TM-27-14 file header specification.
Attributes:
- manufacturer: manufacturer name
- catalog_number: catalog/model number
- description: description text
- document_creator: document creator information
- unique_identifier: unique file identifier
- measurement_equipment: measurement equipment details
- laboratory: laboratory information
- report_number: report number
- report_date: measurement date
- document_creation_date: file creation date
- comments: additional comments
"""
manufacturer: str = ""
catalog_number: str = ""
description: str = ""
document_creator: str = ""
unique_identifier: str = ""
measurement_equipment: str = ""
laboratory: str = ""
report_number: str = ""
report_date: str = ""
document_creation_date: str = ""
comments: str = ""
class SpectralDistribution_IESTM2714(SpectralDistribution):
"""
SpectralDistribution subclass for IES TM-27-14 format with header support.
Parameters:
- data: spectral data (inherited from SpectralDistribution)
- header: Header_IESTM2714 object with metadata
"""
def __init__(self, data: Union[dict, ArrayLike] = None, domain: ArrayLike = None,
header: Header_IESTM2714 = None, **kwargs): ...
@property
def header(self) -> Header_IESTM2714: ...Support for reading spectral data from specific measurement devices.
class SpectralDistribution_UPRTek(SpectralDistribution):
"""
SpectralDistribution subclass for UPRTek device files.
Specialized for reading spectral data from UPRTek spectrometers
with device-specific metadata and calibration information.
"""
class SpectralDistribution_Sekonic(SpectralDistribution):
"""
SpectralDistribution subclass for Sekonic device files.
Specialized for reading spectral data from Sekonic spectroradiometers
with device-specific metadata and measurement parameters.
"""Advanced support for reading and writing spectral images using the OpenEXR layout proposed by Fichet et al. (2021).
@dataclass
class Specification_Fichet2021:
"""
Specification for Fichet 2021 spectral image format.
Attributes:
- path: file path
- components: spectral components data
- colourspace: target colourspace for RGB preview
- illuminant: illuminant for colourimetric computations
- cmfs: colour matching functions
- chromatic_adaptation_transform: adaptation method
- additional_data: additional metadata
"""
path: Union[str, Path] = ""
components: dict = field(default_factory=dict)
colourspace: str = "sRGB"
illuminant: Union[str, SpectralDistribution] = "D65"
cmfs: Union[str, MultiSpectralDistributions] = "CIE 1931 2 Degree Standard Observer"
chromatic_adaptation_transform: str = "CAT02"
additional_data: bool = True
def read_spectral_image_Fichet2021(path: Union[str, Path], **kwargs) -> Specification_Fichet2021:
"""
Read spectral image in Fichet 2021 OpenEXR format.
Parameters:
- path: path to OpenEXR spectral image file
- **kwargs: additional arguments for processing
Returns:
Specification_Fichet2021 object with spectral components and metadata
"""
def write_spectral_image_Fichet2021(specification: Specification_Fichet2021,
path: Union[str, Path] = None) -> bool:
"""
Write spectral image in Fichet 2021 OpenEXR format.
Parameters:
- specification: Specification_Fichet2021 object to write
- path: output file path (uses specification.path if None)
Returns:
True if successful
"""
ComponentsFichet2021 = Dict[Union[str, float], Tuple[NDArray, NDArray]]
"""Type alias for Fichet 2021 spectral components mapping wavelengths to image data."""
def sd_to_spectrum_attribute_Fichet2021(sd: SpectralDistribution,
colourspace: str = "sRGB") -> str:
"""Convert SpectralDistribution to Fichet 2021 spectrum attribute string."""
def spectrum_attribute_to_sd_Fichet2021(attribute: str) -> SpectralDistribution:
"""Convert Fichet 2021 spectrum attribute string to SpectralDistribution."""Support for Academy Color Transformation Language processing for image color transformations.
def ctl_render(input_image: ArrayLike, csc_in: str, csc_out: str,
ctl_transforms: List[str] = None, input_scale: float = 1.0,
output_scale: float = 1.0, global_params: dict = None,
aces_ctl_directory: Union[str, Path] = None) -> NDArray:
"""
Render input image through CTL (Color Transform Language) pipeline.
Parameters:
- input_image: input image array
- csc_in: input color space conversion
- csc_out: output color space conversion
- ctl_transforms: list of CTL transform files to apply
- input_scale: scaling factor for input
- output_scale: scaling factor for output
- global_params: global CTL parameters dictionary
- aces_ctl_directory: path to ACES CTL transforms directory
Returns:
Processed image array
"""
def process_image_ctl(input_image: ArrayLike, **kwargs) -> NDArray:
"""
Process image through CTL transforms with simplified interface.
Parameters:
- input_image: input image array
- **kwargs: arguments passed to ctl_render
Returns:
Processed image array
"""
def template_ctl_transform_float(input_value: str = "input",
output_value: str = "output") -> str:
"""Generate CTL template for float transformations."""
def template_ctl_transform_float3(input_value: str = "input",
output_value: str = "output") -> str:
"""Generate CTL template for float3 (RGB) transformations."""Integration with OpenColorIO for color management and transformation pipelines.
def process_image_OpenColorIO(image: ArrayLike, *args, **kwargs) -> NDArray:
"""
Process image through OpenColorIO color management pipeline.
Parameters:
- image: input image array
- *args: positional arguments for OpenColorIO processing
- **kwargs: keyword arguments for OCIO configuration and transforms
Returns:
Color-managed image array
Notes:
Requires OpenColorIO installation and valid OCIO configuration
"""Registry mappings for available I/O methods and supported formats.
READ_IMAGE_METHODS: dict = {
"OpenImageIO": read_image_OpenImageIO,
"Imageio": read_image_Imageio,
}
"""Available image reading methods."""
WRITE_IMAGE_METHODS: dict = {
"OpenImageIO": write_image_OpenImageIO,
"Imageio": write_image_Imageio,
}
"""Available image writing methods."""
LUT_READ_METHODS: dict = {
"Cinespace": read_LUT_Cinespace,
"Iridas Cube": read_LUT_IridasCube,
"Resolve Cube": read_LUT_ResolveCube,
"Sony SPI1D": read_LUT_SonySPI1D,
"Sony SPI3D": read_LUT_SonySPI3D,
"Sony SPImtx": read_LUT_SonySPImtx,
}
"""Available LUT reading methods mapped to format names."""
LUT_WRITE_METHODS: dict = {
"Cinespace": write_LUT_Cinespace,
"Iridas Cube": write_LUT_IridasCube,
"Resolve Cube": write_LUT_ResolveCube,
"Sony SPI1D": write_LUT_SonySPI1D,
"Sony SPI3D": write_LUT_SonySPI3D,
"Sony SPImtx": write_LUT_SonySPImtx,
}
"""Available LUT writing methods mapped to format names."""
MAPPING_EXTENSION_TO_LUT_FORMAT: dict = {
".cube": "Iridas Cube",
".spi1d": "Sony SPI1D",
".spi3d": "Sony SPI3D",
".spimtx": "Sony SPImtx",
".csp": "Cinespace",
}
"""Mapping of file extensions to LUT format names for auto-detection."""
XRITE_FILE_ENCODING: str = "utf-8"
"""Default encoding for X-Rite spectral data files."""import colour
# Read an image with automatic format detection
image = colour.read_image("input.exr")
# Process the image (example: apply gamma correction)
processed = image ** (1/2.2)
# Write with specific bit depth
colour.write_image(processed, "output.png", bit_depth="uint8")
# Read with specific backend and attributes
image, attributes = colour.read_image_OpenImageIO("input.exr", attributes=True)
print(f"Image attributes: {attributes}")# Read a LUT file (format auto-detected from extension)
lut = colour.read_LUT("color_correction.cube")
# Apply LUT to image
corrected_image = lut.apply(image)
# Create a custom 1D LUT
import numpy as np
gamma_table = np.power(np.linspace(0, 1, 1024), 1/2.2)
gamma_lut = colour.LUT1D(gamma_table, name="Gamma 2.2")
# Apply and save
gamma_corrected = gamma_lut.apply(image)
colour.write_LUT(gamma_lut, "gamma_correction.spi1d")
# Create and work with 3D LUTs
lut3d = colour.LUT3D(size=33) # Creates linear 33x33x33 LUT
lut3d.table = lut3d.table ** (1/2.2) # Apply gamma to each channel
colour.write_LUT(lut3d, "gamma_3d.cube")# Read spectral data from CSV
spectral_data = colour.read_sds_from_csv_file("reflectances.csv")
print(f"Available samples: {list(spectral_data.keys())}")
# Read X-Rite ColorChecker data
xrite_data = colour.read_sds_from_xrite_file("colorchecker.txt")
# Write processed spectral data back to CSV
colour.write_sds_to_csv_file(xrite_data, "processed_spectra.csv")
# Work with IES TM-27-14 format
from colour.io import Header_IESTM2714, SpectralDistribution_IESTM2714
header = Header_IESTM2714(
manufacturer="Test Lab",
description="Sample measurement",
measurement_equipment="Spectrometer Model XYZ"
)
# Create spectral distribution with metadata
sd_with_header = SpectralDistribution_IESTM2714(
data={380: 0.1, 390: 0.15, 400: 0.2},
header=header
)# Read Fichet 2021 format spectral image
from colour.io import read_spectral_image_Fichet2021
spec_image = read_spectral_image_Fichet2021("spectral_scene.exr")
# Access spectral components
print(f"Available wavelengths: {list(spec_image.components.keys())}")
# Convert to RGB for preview
rgb_preview = colour.spectral_image_to_RGB(spec_image)
colour.write_image(rgb_preview, "preview.png")# Process with OpenColorIO (requires OCIO setup)
ocio_processed = colour.process_image_OpenColorIO(
image,
src="ACES - ACEScg",
dst="Output - sRGB"
)
# CTL processing (requires CTL installation)
ctl_processed = colour.process_image_ctl(
image,
csc_in="ACES",
csc_out="sRGB",
ctl_transforms=["RRT", "ODT.Academy.Rec709_100nits_dim.ctl"]
)The I/O system provides comprehensive support for professional color workflows, supporting industry-standard formats and enabling seamless integration with existing pipelines and color management systems.
Install with Tessl CLI
npx tessl i tessl/pypi-colour-science