Source code for colour.io.luts

"""
References
----------
-   :cite:`AdobeSystems2013b` : Adobe Systems. (2013). Cube LUT Specification.
    https://drive.google.com/open?id=143Eh08ZYncCAMwJ1q4gWxVOqR_OSWYvs
-   :cite:`Chamberlain2015` : Chamberlain, P. (2015). LUT documentation (to
    create from another program). Retrieved August 23, 2018, from
    https://forum.blackmagicdesign.com/viewtopic.php?f=21&t=40284#p232952
-   :cite:`RisingSunResearch` : Rising Sun Research. (n.d.). cineSpace LUT
    Library. Retrieved November 30, 2018, from
    https://sourceforge.net/projects/cinespacelutlib/
"""

from __future__ import annotations

import os
from pathlib import Path

from colour.hints import Any, LiteralLUTReadMethod, LiteralLUTWriteMethod
from colour.utilities import (
    CanonicalMapping,
    filter_kwargs,
    validate_method,
)

from .lut import LUT1D, LUT3x1D, LUT3D, LUT_to_LUT
from .operator import AbstractLUTSequenceOperator, LUTOperatorMatrix
from .sequence import LUTSequence
from .iridas_cube import read_LUT_IridasCube, write_LUT_IridasCube
from .resolve_cube import read_LUT_ResolveCube, write_LUT_ResolveCube
from .sony_spi1d import read_LUT_SonySPI1D, write_LUT_SonySPI1D
from .sony_spi3d import read_LUT_SonySPI3D, write_LUT_SonySPI3D
from .sony_spimtx import read_LUT_SonySPImtx, write_LUT_SonySPImtx
from .cinespace_csp import read_LUT_Cinespace, write_LUT_Cinespace

__all__ = [
    "LUT1D",
    "LUT3x1D",
    "LUT3D",
    "LUT_to_LUT",
]
__all__ += [
    "AbstractLUTSequenceOperator",
    "LUTOperatorMatrix",
]
__all__ += [
    "LUTSequence",
]
__all__ += [
    "read_LUT_IridasCube",
    "write_LUT_IridasCube",
]
__all__ += [
    "read_LUT_ResolveCube",
    "write_LUT_ResolveCube",
]
__all__ += [
    "read_LUT_SonySPI1D",
    "write_LUT_SonySPI1D",
]
__all__ += [
    "read_LUT_SonySPI3D",
    "write_LUT_SonySPI3D",
]
__all__ += [
    "read_LUT_SonySPImtx",
    "write_LUT_SonySPImtx",
]
__all__ += [
    "read_LUT_Cinespace",
    "write_LUT_Cinespace",
]

MAPPING_EXTENSION_TO_LUT_FORMAT: CanonicalMapping = CanonicalMapping(
    {
        ".cube": "Iridas Cube",
        ".spi1d": "Sony SPI1D",
        ".spi3d": "Sony SPI3D",
        ".spimtx": "Sony SPImtx",
        ".csp": "Cinespace",
    }
)
"""Extension to *LUT* format."""

LUT_READ_METHODS: CanonicalMapping = CanonicalMapping(
    {
        "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,
    }
)
LUT_READ_METHODS.__doc__ = """
Supported *LUT* reading methods.

References
----------
:cite:`AdobeSystems2013b`, :cite:`Chamberlain2015`
"""


[docs] def read_LUT( path: str | Path, method: LiteralLUTReadMethod | str | None = None, **kwargs: Any, ) -> LUT1D | LUT3x1D | LUT3D | LUTSequence | LUTOperatorMatrix: """ Read given *LUT* file using given method. Parameters ---------- path *LUT* path. method Reading method, if *None*, the method will be auto-detected according to extension. Returns ------- :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or :class:`colour.LUT3D` \ or :class:`colour.LUTSequence` or :class:`colour.LUTOperatorMatrix` :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or :class:`colour.LUT3D` or :class:`colour.LUTSequence` or :class:`colour.LUTOperatorMatrix` class instance. References ---------- :cite:`AdobeSystems2013b`, :cite:`Chamberlain2015`, :cite:`RisingSunResearch` Examples -------- Reading a 3x1D *Iridas* *.cube* *LUT*: >>> path = os.path.join( ... os.path.dirname(__file__), ... "tests", ... "resources", ... "iridas_cube", ... "ACES_Proxy_10_to_ACES.cube", ... ) >>> print(read_LUT(path)) LUT3x1D - ACES Proxy 10 to ACES ------------------------------- <BLANKLINE> Dimensions : 2 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (32, 3) Reading a 1D *Sony* *.spi1d* *LUT*: >>> path = os.path.join( ... os.path.dirname(__file__), ... "tests", ... "resources", ... "sony_spi1d", ... "eotf_sRGB_1D.spi1d", ... ) >>> print(read_LUT(path)) LUT1D - eotf sRGB 1D -------------------- <BLANKLINE> Dimensions : 1 Domain : [-0.1 1.5] Size : (16,) Comment 01 : Generated by "Colour 0.3.11". Comment 02 : "colour.models.eotf_sRGB". Reading a 3D *Sony* *.spi3d* *LUT*: >>> path = os.path.join( ... os.path.dirname(__file__), ... "tests", ... "resources", ... "sony_spi3d", ... "Colour_Correct.spi3d", ... ) >>> print(read_LUT(path)) LUT3D - Colour Correct ---------------------- <BLANKLINE> Dimensions : 3 Domain : [[ 0. 0. 0.] [ 1. 1. 1.]] Size : (4, 4, 4, 3) Comment 01 : Adapted from a LUT generated by Foundry::LUT. Reading a *Sony* *.spimtx* *LUT*: >>> path = os.path.join( ... os.path.dirname(__file__), ... "tests", ... "resources", ... "sony_spimtx", ... "dt.spimtx", ... ) >>> print(read_LUT(path)) LUTOperatorMatrix - dt ---------------------- <BLANKLINE> Matrix : [[ 0.864274 0. 0. 0. ] [ 0. 0.864274 0. 0. ] [ 0. 0. 0.864274 0. ] [ 0. 0. 0. 1. ]] Offset : [ 0. 0. 0. 0.] """ path = str(path) method = ( MAPPING_EXTENSION_TO_LUT_FORMAT[os.path.splitext(path)[-1]].lower() if method is None else validate_method(method, tuple(LUT_WRITE_METHODS)) ) function = LUT_READ_METHODS[method] try: return function(path, **filter_kwargs(function, **kwargs)) except ValueError as error: # Case where a "Resolve Cube" with "LUT3x1D" shaper was read as an # "Iridas Cube" "LUT". if method == "iridas cube": function = LUT_READ_METHODS["Resolve Cube"] return function(path, **filter_kwargs(function, **kwargs)) else: raise ValueError from error
LUT_WRITE_METHODS = CanonicalMapping( { "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, } ) LUT_WRITE_METHODS.__doc__ = """ Supported *LUT* reading methods. References ---------- :cite:`AdobeSystems2013b`, :cite:`Chamberlain2015` """
[docs] def write_LUT( LUT: LUT1D | LUT3x1D | LUT3D | LUTSequence | LUTOperatorMatrix, path: str | Path, decimals: int = 7, method: LiteralLUTWriteMethod | str | None = None, **kwargs: Any, ) -> bool: """ Write given *LUT* to given file using given method. Parameters ---------- LUT :class:`colour.LUT1D` or :class:`colour.LUT3x1D` or :class:`colour.LUT3D` or :class:`colour.LUTSequence` or :class:`colour.LUTOperatorMatrix` class instance to write at given path. path *LUT* path. decimals Formatting decimals. method Writing method, if *None*, the method will be auto-detected according to extension. Returns ------- :class:`bool` Definition success. References ---------- :cite:`AdobeSystems2013b`, :cite:`Chamberlain2015`, :cite:`RisingSunResearch` Examples -------- Writing a 3x1D *Iridas* *.cube* *LUT*: >>> import numpy as np >>> from colour.algebra import spow >>> domain = np.array([[-0.1, -0.2, -0.4], [1.5, 3.0, 6.0]]) >>> LUT = LUT3x1D( ... spow(LUT3x1D.linear_table(16, domain), 1 / 2.2), ... "My LUT", ... domain, ... comments=["A first comment.", "A second comment."], ... ) >>> write_LUT(LUT, "My_LUT.cube") # doctest: +SKIP Writing a 1D *Sony* *.spi1d* *LUT*: >>> domain = np.array([-0.1, 1.5]) >>> LUT = LUT1D( ... spow(LUT1D.linear_table(16, domain), 1 / 2.2), ... "My LUT", ... domain, ... comments=["A first comment.", "A second comment."], ... ) >>> write_LUT(LUT, "My_LUT.spi1d") # doctest: +SKIP Writing a 3D *Sony* *.spi3d* *LUT*: >>> LUT = LUT3D( ... LUT3D.linear_table(16) ** (1 / 2.2), ... "My LUT", ... np.array([[0, 0, 0], [1, 1, 1]]), ... comments=["A first comment.", "A second comment."], ... ) >>> write_LUT(LUT, "My_LUT.cube") # doctest: +SKIP """ path = str(path) method = ( MAPPING_EXTENSION_TO_LUT_FORMAT[os.path.splitext(path)[-1]].lower() if method is None else validate_method(method, tuple(LUT_WRITE_METHODS)) ) if method == "iridas cube" and isinstance(LUT, LUTSequence): method = "resolve cube" function = LUT_WRITE_METHODS[method] return function(LUT, path, decimals, **filter_kwargs(function, **kwargs))
__all__ += [ "LUT_READ_METHODS", "read_LUT", "LUT_WRITE_METHODS", "write_LUT", ]