Skip to content

API Reference

Thierry Soreze edited this page Oct 28, 2025 · 1 revision

API Reference

Complete reference for DTUTMO classes, functions, and enums.

Table of Contents

  1. Core Classes
  2. Configuration
  3. Photoreceptors
  4. Display Mapping
  5. Color Appearance
  6. Utilities
  7. Enums

Core Classes

CompleteDTUTMO

Main pipeline orchestrator.

class CompleteDTUTMO:
    """Complete DTUTMO pipeline
    
    Orchestrates all 13 processing stages from HDR input to display output.
    """
    
    def __init__(self, config: DTUTMOConfig = None):
        """Initialize pipeline
        
        Parameters
        ----------
        config : DTUTMOConfig, optional
            Configuration object. If None, uses default configuration.
        
        Examples
        --------
        >>> from dtutmo import CompleteDTUTMO, DTUTMOConfig
        >>> 
        >>> # Default configuration
        >>> tmo = CompleteDTUTMO()
        >>> 
        >>> # Custom configuration
        >>> config = DTUTMOConfig(observer_age=35, use_cam=CAMType.DTUCAM)
        >>> tmo = CompleteDTUTMO(config)
        """

Methods

process
def process(
    self,
    img_hdr: np.ndarray,
    return_intermediate: bool = False
) -> Union[np.ndarray, dict]:
    """Process HDR image to display output
    
    Parameters
    ----------
    img_hdr : ndarray, shape (H, W, 3)
        Input HDR image in linear RGB, units of cd/m².
        Must be float32 or float64.
    return_intermediate : bool, default False
        If True, return dict with all intermediate results.
        If False, return only the output image.
    
    Returns
    -------
    img_display : ndarray, shape (H, W, 3)
        Display-encoded output in range [0, 1].
        Ready for 8-bit conversion and display.
    OR
    result : dict
        Dictionary containing:
        - 'output': Final display image
        - 'local_adaptation': Adaptation map (cd/m²)
        - 'photoreceptor_responses': Dict of cone/rod responses
        - 'csf_output': After CSF filtering
        - 'appearance': Appearance attributes (if CAM enabled)
        - 'timings': Stage timing information
    
    Raises
    ------
    ValueError
        If input is not 3D array with 3 channels
    RuntimeError
        If processing encounters numerical issues
    
    Examples
    --------
    >>> import numpy as np
    >>> from dtutmo import CompleteDTUTMO
    >>> 
    >>> # Load HDR image (example with random data)
    >>> hdr = np.random.rand(1080, 1920, 3).astype(np.float32) * 1000
    >>> 
    >>> # Process
    >>> tmo = CompleteDTUTMO()
    >>> ldr = tmo.process(hdr)
    >>> 
    >>> # Access intermediate results
    >>> result = tmo.process(hdr, return_intermediate=True)
    >>> adaptation = result['local_adaptation']
    >>> cone_l = result['photoreceptor_responses']['cones']['L']
    """

TorchDTUTMO

GPU-accelerated PyTorch implementation.

class TorchDTUTMO(nn.Module):
    """GPU-accelerated DTUTMO pipeline
    
    PyTorch implementation for GPU processing. Supports batching
    and mixed precision training.
    """
    
    def __init__(self, config: DTUTMOConfig = None):
        """Initialize GPU pipeline
        
        Parameters
        ----------
        config : DTUTMOConfig, optional
            Configuration object
        
        Examples
        --------
        >>> import torch
        >>> from dtutmo import TorchDTUTMO
        >>> 
        >>> # Create and move to GPU
        >>> tmo = TorchDTUTMO().cuda()
        >>> 
        >>> # Prepare input (BCHW format)
        >>> hdr = torch.randn(1, 3, 1080, 1920).cuda()
        >>> 
        >>> # Process
        >>> with torch.no_grad():
        ...     ldr = tmo(hdr)
        """
    
    def forward(
        self,
        img_hdr: torch.Tensor
    ) -> torch.Tensor:
        """Process HDR tensor
        
        Parameters
        ----------
        img_hdr : torch.Tensor, shape (B, C, H, W)
            Batch of HDR images in linear RGB, cd/m².
            B = batch size, C = 3 (RGB), H = height, W = width
        
        Returns
        -------
        img_display : torch.Tensor, shape (B, C, H, W)
            Display-encoded output in range [0, 1]
        """

Configuration

DTUTMOConfig

Configuration dataclass with validation.

@dataclass
class DTUTMOConfig:
    """Complete DTUTMO configuration
    
    All parameters have validated defaults based on psychophysical data.
    
    Attributes
    ----------
    observer_age : float, default 24.0
        Observer age in years (18-80)
    field_diameter : float, default 60.0
        Visual field diameter in degrees (10-180)
    pixels_per_degree : float, default 45.0
        Display resolution in pixels/degree (30-60)
    
    use_otf : bool, default True
        Enable optical transfer function
    use_glare : bool, default True
        Enable CIE disability glare
    use_bilateral : bool, default True
        Enable bilateral filtering
    use_local_adapt : bool, default True
        Enable local adaptation
    
    use_cam : CAMType, default CAMType.DTUCAM
        Color appearance model
    viewing_condition : ViewingCondition, default ViewingCondition.AVERAGE
        Viewing surround type
    
    target_display : DisplayStandard, default DisplayStandard.REC_709
        Target display standard
    target_luminance : float, default 100.0
        Target display peak luminance (cd/m²)
    display_mapping : DisplayMapping, default DisplayMapping.PRODUCTION_HYBRID
        Display mapping strategy
    
    gradient_threshold : float, default 0.15
        Gradient threshold for hybrid mapping (0.05-0.30)
    gradient_smoothing : float, default 2.0
        Gradient smoothing sigma (0.5-5.0)
    
    Examples
    --------
    >>> from dtutmo import DTUTMOConfig, CAMType, DisplayStandard
    >>> 
    >>> # Default configuration
    >>> config = DTUTMOConfig()
    >>> 
    >>> # Custom configuration
    >>> config = DTUTMOConfig(
    ...     observer_age=35,
    ...     use_cam=CAMType.DTUCAM,
    ...     target_display=DisplayStandard.REC_2100_PQ,
    ...     target_luminance=1000.0
    ... )
    >>> 
    >>> # Save/load
    >>> config_dict = dataclasses.asdict(config)
    >>> config_new = DTUTMOConfig(**config_dict)
    """

Photoreceptors

CorrectedPhotoreceptorResponse

Hood-adapted photoreceptor model.

class CorrectedPhotoreceptorResponse:
    """Hood & Finkelstein photoreceptor adaptation model
    
    Implements complete photoreceptor response with:
    - Hood adaptation: σ = k₁((O₁+Ia)/O₁)^m
    - Adaptive gain: Rmax = k₂[(O₂+p·Ia)/O₂]^(-1/2)
    - Bleaching: p = B/(B + ln(Ia))
    - Adaptation-dependent offset
    """
    
    def compute_response(
        self,
        I_signal: np.ndarray,
        I_adapt: np.ndarray,
        pupil_diameter: np.ndarray,
        photoreceptor_type: str,
        return_params: bool = False
    ) -> Union[np.ndarray, Tuple[np.ndarray, dict]]:
        """Compute photoreceptor response
        
        Parameters
        ----------
        I_signal : ndarray
            Signal luminance (cd/m²)
        I_adapt : ndarray
            Adaptation luminance (cd/m²), same shape as I_signal
        pupil_diameter : ndarray
            Pupil diameter (mm), same shape or broadcastable
        photoreceptor_type : str
            One of: 'L_cone', 'M_cone', 'S_cone', 'rod'
        return_params : bool, default False
            If True, also return adaptation parameters
        
        Returns
        -------
        response : ndarray
            Neural response (arbitrary units), same shape as I_signal
        params : dict, optional
            Adaptation parameters if return_params=True:
            - 'sigma_hood': Hood semi-saturation
            - 'R_max': Adaptive maximum response
            - 'p': Bleaching factor
            - 's': Adaptation-dependent offset
        
        Examples
        --------
        >>> from dtutmo.photoreceptors import CorrectedPhotoreceptorResponse
        >>> import numpy as np
        >>> 
        >>> photo = CorrectedPhotoreceptorResponse()
        >>> 
        >>> # Compute L-cone response
        >>> L_signal = np.array([1.0, 10.0, 100.0])
        >>> L_adapt = np.array([0.1, 1.0, 10.0])
        >>> pupil = np.array([6.0, 5.0, 4.0])
        >>> 
        >>> R_L = photo.compute_response(L_signal, L_adapt, pupil, 'L_cone')
        >>> print(R_L)  # [0.45, 0.62, 0.78] (example values)
        """

InversePhotoreceptorComplete

Analytical inverse of photoreceptor model.

class InversePhotoreceptorComplete:
    """Explicit inverse of Hood photoreceptor model
    
    Provides O(1) closed-form inverse formulas without iteration.
    """
    
    def inverse_response(
        self,
        R: np.ndarray,
        I_adapt: np.ndarray,
        pupil_diameter: np.ndarray,
        n: float,
        photoreceptor_type: str
    ) -> np.ndarray:
        """Compute luminance from desired response
        
        Parameters
        ----------
        R : ndarray
            Desired response (0 to R_max)
        I_adapt : ndarray
            Display adaptation luminance (cd/m²)
        pupil_diameter : ndarray
            Viewer pupil diameter (mm)
        n : float
            Hill coefficient (typically 0.74)
        photoreceptor_type : str
            One of: 'L_cone', 'M_cone', 'S_cone', 'rod'
        
        Returns
        -------
        L_required : ndarray
            Required luminance (cd/m²) to achieve desired response
        
        Examples
        --------
        >>> from dtutmo.photoreceptors import InversePhotoreceptorComplete
        >>> 
        >>> inverse = InversePhotoreceptorComplete()
        >>> 
        >>> # Compute required luminance for 50% response
        >>> R_desired = 0.5 * R_max
        >>> L_adapt_display = 100.0
        >>> pupil_viewer = 4.5
        >>> 
        >>> L_req = inverse.inverse_response(
        ...     R_desired, L_adapt_display, pupil_viewer, 0.74, 'L_cone'
        ... )
        """

Display Mapping

HybridDisplayMapper

Production gradient-adaptive hybrid mapper.

class HybridDisplayMapper:
    """Gradient-aware hybrid display mapping
    
    Blends whiteboard (fast) and full inverse (accurate) based on
    local gradient magnitude.
    """
    
    def __init__(
        self,
        gradient_threshold: float = 0.15,
        gradient_smoothing: float = 2.0
    ):
        """Initialize hybrid mapper
        
        Parameters
        ----------
        gradient_threshold : float, default 0.15
            Gradient threshold for blending (0.05-0.30)
        gradient_smoothing : float, default 2.0
            Smoothing sigma for gradient map (0.5-5.0)
        """
    
    def map_to_display(
        self,
        responses: dict,
        adaptation: np.ndarray,
        config: DTUTMOConfig
    ) -> np.ndarray:
        """Map photoreceptor responses to display
        
        Parameters
        ----------
        responses : dict
            Photoreceptor responses:
            {'L': ndarray, 'M': ndarray, 'S': ndarray}
        adaptation : ndarray
            Display adaptation map (cd/m²)
        config : DTUTMOConfig
            Configuration object
        
        Returns
        -------
        img_display : ndarray, shape (H, W, 3)
            Display-encoded RGB [0, 1]
        
        Examples
        --------
        >>> from dtutmo.display import HybridDisplayMapper
        >>> 
        >>> mapper = HybridDisplayMapper(
        ...     gradient_threshold=0.15,
        ...     gradient_smoothing=2.0
        ... )
        >>> 
        >>> img_display = mapper.map_to_display(
        ...     responses={'L': R_L, 'M': R_M, 'S': R_S},
        ...     adaptation=L_adapt_display,
        ...     config=config
        ... )
        """

WhiteboardMapper

Fast analytical approximation.

class WhiteboardMapper:
    """Fast whiteboard formula display mapping
    
    Provides 3-5× speedup over full inverse with good quality.
    """
    
    def map_to_display(
        self,
        responses: dict,
        L_mean_display: float,
        n: float = 0.74
    ) -> np.ndarray:
        """Map responses using whiteboard formula
        
        Parameters
        ----------
        responses : dict
            Normalized photoreceptor responses [0, 1)
        L_mean_display : float
            Target mean display luminance (cd/m²)
        n : float, default 0.74
            Hill coefficient
        
        Returns
        -------
        img_display : ndarray
            Display RGB [0, 1]
        
        Examples
        --------
        >>> from dtutmo.display import WhiteboardMapper
        >>> 
        >>> mapper = WhiteboardMapper()
        >>> img_display = mapper.map_to_display(
        ...     responses=R_normalized,
        ...     L_mean_display=100.0
        ... )
        """

Color Appearance

DTUCAM

DTU Color Appearance Model with dual adaptation.

class DTUCAM:
    """DTU Color Appearance Model
    
    Novel CAM with physiologically-consistent dual adaptation:
    - Semi-saturation (σ) adapts
    - Response ceiling (R_max) adapts
    
    Range: 0.001 to 100,000 cd/m²
    Inverse: Explicit formulas (O(1))
    """
    
    def forward(
        self,
        img_xyz: np.ndarray,
        white_xyz: np.ndarray,
        viewing_condition: str
    ) -> dict:
        """XYZ → Appearance attributes
        
        Parameters
        ----------
        img_xyz : ndarray, shape (H, W, 3)
            Image in CIE XYZ
        white_xyz : ndarray, shape (3,)
            White point [X_w, Y_w, Z_w]
        viewing_condition : str
            One of: 'dark', 'dim', 'average'
        
        Returns
        -------
        appearance : dict
            Appearance attributes:
            - 'lightness' (J): 0-100
            - 'colorfulness' (M): Absolute
            - 'hue' (h): 0-360 degrees
            - 'chroma' (C): Relative
            - 'brightness' (Q): Absolute
            - 'saturation' (s): 0-100
        
        Examples
        --------
        >>> from dtutmo.appearance import DTUCAM
        >>> 
        >>> cam = DTUCAM()
        >>> 
        >>> # Forward transform
        >>> appearance = cam.forward(
        ...     img_xyz,
        ...     white_xyz=np.array([95.05, 100.0, 108.88]),
        ...     viewing_condition='average'
        ... )
        >>> 
        >>> J = appearance['lightness']
        >>> M = appearance['colorfulness']
        >>> h = appearance['hue']
        """
    
    def inverse(
        self,
        J: np.ndarray,
        M: np.ndarray,
        h: np.ndarray,
        display_white_xyz: np.ndarray,
        display_max_lum: float,
        viewing_condition: str
    ) -> np.ndarray:
        """Appearance → Display XYZ
        
        Parameters
        ----------
        J : ndarray
            Lightness (0-100)
        M : ndarray
            Colorfulness (absolute)
        h : ndarray
            Hue angle (0-360 degrees)
        display_white_xyz : ndarray, shape (3,)
            Display white point
        display_max_lum : float
            Display peak luminance (cd/m²)
        viewing_condition : str
            Display viewing condition
        
        Returns
        -------
        img_xyz : ndarray, shape (H, W, 3)
            Display XYZ tristimulus values
        
        Examples
        --------
        >>> # Inverse transform
        >>> display_xyz = cam.inverse(
        ...     J=lightness,
        ...     M=colorfulness,
        ...     h=hue,
        ...     display_white_xyz=np.array([95.05, 100.0, 108.88]),
        ...     display_max_lum=100.0,
        ...     viewing_condition='dim'
        ... )
        """

XLRCAM

Extended Luminance Range CAM.

class XLRCAM:
    """Extended Luminance Range Color Appearance Model
    
    HDR-specific CAM with psychophysical validation.
    Range: 0.01 to 16,860 cd/m²
    """
    
    def forward(self, img_xyz, white_xyz, viewing_condition) -> dict:
        """XYZ → Appearance (see DTUCAM for details)"""
    
    def inverse(self, J, M, h, display_white_xyz, 
                display_max_lum, viewing_condition) -> np.ndarray:
        """Appearance → Display XYZ (see DTUCAM for details)"""

CIECAM16

CIE 2016 Color Appearance Model.

class CIECAM16:
    """CIE 2016 Color Appearance Model
    
    CIE standard (CIE 248:2022).
    Range: 0.1 to 1,000 cd/m²
    Inverse: Iterative (5-7 iterations)
    """
    
    def forward(self, img_xyz, white_xyz, viewing_condition,
                L_A=64.0, Y_b=20.0) -> dict:
        """XYZ → Appearance
        
        Additional Parameters
        ---------------------
        L_A : float, default 64.0
            Adapting field luminance (cd/m²)
        Y_b : float, default 20.0
            Background luminance factor (% of white)
        """
    
    def inverse(self, J, M, h, display_white_xyz,
                display_max_lum, viewing_condition,
                L_A=64.0, Y_b=20.0) -> np.ndarray:
        """Appearance → Display XYZ (iterative)"""

Utilities

Color Space Conversions

def rgb_to_xyz(rgb: np.ndarray, color_space: str = 'sRGB') -> np.ndarray:
    """Convert RGB to CIE XYZ
    
    Parameters
    ----------
    rgb : ndarray, shape (..., 3)
        Linear RGB values
    color_space : str, default 'sRGB'
        RGB color space: 'sRGB', 'Adobe', 'ProPhoto'
    
    Returns
    -------
    xyz : ndarray, shape (..., 3)
        CIE XYZ tristimulus values
    """

def xyz_to_rgb(xyz: np.ndarray, color_space: str = 'sRGB') -> np.ndarray:
    """Convert CIE XYZ to RGB"""

def xyz_to_lms(xyz: np.ndarray, matrix: str = 'HPE') -> np.ndarray:
    """Convert XYZ to LMS cone fundamentals
    
    Parameters
    ----------
    xyz : ndarray
        CIE XYZ
    matrix : str, default 'HPE'
        Transform matrix: 'HPE' (Hunt-Pointer-Estevez) or 'CAT02'
    """

def lms_to_xyz(lms: np.ndarray, matrix: str = 'HPE') -> np.ndarray:
    """Convert LMS to XYZ"""

def linearize_srgb(img_gamma: np.ndarray) -> np.ndarray:
    """Convert sRGB gamma to linear
    
    Parameters
    ----------
    img_gamma : ndarray
        sRGB gamma-encoded image [0, 1]
    
    Returns
    -------
    img_linear : ndarray
        Linear RGB
    """

def encode_srgb(img_linear: np.ndarray) -> np.ndarray:
    """Convert linear RGB to sRGB gamma"""

Display Transfer Functions

def apply_pq_eotf(signal: np.ndarray, L_max: float = 10000.0) -> np.ndarray:
    """PQ EOTF (SMPTE ST 2084)
    
    Parameters
    ----------
    signal : ndarray
        Display code values [0, 1]
    L_max : float, default 10000.0
        Display peak luminance (cd/m²)
    
    Returns
    -------
    luminance : ndarray
        Linear luminance (cd/m²)
    """

def apply_pq_oetf(luminance: np.ndarray, L_max: float = 10000.0) -> np.ndarray:
    """PQ inverse EOTF"""

def apply_hlg_eotf(signal: np.ndarray, L_max: float = 1000.0) -> np.ndarray:
    """HLG EOTF (ITU-R BT.2100)"""

def apply_hlg_oetf(luminance: np.ndarray, L_max: float = 1000.0) -> np.ndarray:
    """HLG inverse EOTF"""

Enums

CAMType

class CAMType(Enum):
    """Color appearance model selection"""
    NONE = "none"            # No CAM (direct mapping)
    DTUCAM = "dtucam"       # DTU CAM (recommended)
    XLRCAM = "xlrcam"       # Extended luminance range
    CIECAM16 = "ciecam16"   # CIE standard

DisplayStandard

class DisplayStandard(Enum):
    """Display standards and color spaces"""
    REC_709 = "rec709"           # sRGB, HDTV
    REC_2020 = "rec2020"         # UHDTV
    DCI_P3 = "dcip3"             # Digital cinema
    REC_2100_PQ = "rec2100pq"    # HDR10
    REC_2100_HLG = "rec2100hlg"  # HLG HDR

ViewingCondition

class ViewingCondition(Enum):
    """Viewing environment surround"""
    DARK = "dark"        # <0.2 cd/m² (cinema)
    DIM = "dim"          # 0.2-20 cd/m² (home)
    AVERAGE = "average"  # >20 cd/m² (office)

DisplayMapping

class DisplayMapping(Enum):
    """Display mapping strategies"""
    LEGACY = "legacy"                      # v1.x compatibility
    WHITEBOARD = "whiteboard"              # Fast approximation
    FULL_INVERSE = "full_inverse"          # Accurate inverse
    HYBRID = "hybrid"                      # Basic hybrid
    PRODUCTION_HYBRID = "production_hybrid"  # Gradient-aware (best)

Type Hints

from typing import Union, Tuple, Dict, Optional
import numpy as np
import torch

# Common types
ImageArray = np.ndarray  # (H, W, 3)
TensorImage = torch.Tensor  # (B, C, H, W)
ResponseDict = Dict[str, np.ndarray]  # {'L': ..., 'M': ..., 'S': ...}
AppearanceDict = Dict[str, np.ndarray]  # {'lightness': ..., 'colorfulness': ..., ...}

Exception Handling

# Configuration errors
try:
    config = DTUTMOConfig(observer_age=150)  # Out of range
except ValueError as e:
    print(f"Configuration error: {e}")

# Processing errors
try:
    ldr = tmo.process(hdr)
except RuntimeError as e:
    print(f"Processing error: {e}")

# Input validation
try:
    ldr = tmo.process(wrong_shape)
except ValueError as e:
    print(f"Input error: {e}")

Version Information

import dtutmo

print(dtutmo.__version__)  # '2.1'
print(dtutmo.__author__)   # 'Soreze, Thierry Silvio Claude'

Next Steps

Clone this wiki locally