Skip to content

IdentifyBuiltinColorSpace heuristics failure edgecases: ACEScc, ADX16, CIE-XYZ-D65 #2222

@zachlewis

Description

@zachlewis

Apparently, ACEScc, ADX16, and both CIE XYZ-D65 spaces from ocio://studio-config-latest (as of OCIO v2.5.0) are evading our out-of-the-box matching / identification algorithms.

I've been noticing that, very occasionally, Config::IdentifyBuiltinColorSpace has trouble matching certain color spaces among configs that really do share functionally identical definitions, and should, by all accounts, be recognized as equivalent.

I wrote a little function to assess which color spaces in a given config IdentifyBuiltinColorSpace seems to have the most trouble with -- essentially, I'm using the exact same config for both the srcConfig and the builtinConfig, and just collecting the color spaces that can't be found in their own configs.

In exploring this, I found that both optimization flags (unsurprisingly) and color space visibility (surprisingly) affect the rate of success.

Insights:

  • It would be useful if there were a way to force Config.IdentifyBuiltinColorSpace to always attempt to failover to searching inactive color spaces.
  • It would be nice if IdentifyBuiltinColorSpace exposed an optional optimizationFlags argument as a means of fine-tuning the equivalency "tolerance"

Findings:

  • "ADX16" color spaces based off of the ADX16_to_ACES2065-1 BuiltinTransform can be matched when using DRAFT optimization or higher
  • "ACEScc" and "ACESproxy10i" color spaces based off of the ACEScc_to_ACES2065-1 ACESproxy10i_to_ACES2065-1 BuiltinTransform styles respectively remain "unmatchable", regardless of optimization settings.

Here's a python snippet if you wanna mess around:

import PyOpenColorIO as ocio
import os

def get_unmatchable_color_spaces(
    config: ocio.Config, 
    optimization: ocio.OptimizationFlags | int | None = None,
    exclude_inactive: bool = False, 
    verbose: bool = False,
) -> list[str]:
    
    # Optimization Flags affect the identification heuristics. The API doesn't expose a way
    # to directly control the optimization flags used for identifying color spaces, but we
    # can temporarily adjust the $OCIO_OPTIMIZATION_FLAGS environment variable
    # if we want to temporarily override the global optimization behavior

    # Preserve the previous environment to restore later.
    prev_env = dict(os.environ)
    if optimization:
        # Temporarily set $OCIO_OPTIMIZATION_FLAGS to the requested value.
        os.environ[ocio.OCIO_OPTIMIZATION_FLAGS_ENVVAR] = str(int(optimization))

    
    # Color space visiblity affects the identification heuristics.
    # To maximize the likelihood of finding a match, we can temporarily
    # activate all color spaces. 

    # Store the old inactive color spaces to restore later.
    old_invisible_color_spaces = config.getInactiveColorSpaces()
    
    if not exclude_inactive:
        # Temporarily make all color spaces visible.
        config.setInactiveColorSpaces("")

    
    # Iterate over all the color spaces in the config, and collect names of 
    # color spaces that steadfastly refuse to be matched!
    problem_spaces = []
    for cs_name in config.getColorSpaceNames(
        ocio.SEARCH_REFERENCE_SPACE_ALL, 
        ocio.COLORSPACE_ALL
    ):
        # Try to match each color space in the config to... itself!
        try:
            matched_space = ocio.Config.IdentifyBuiltinColorSpace(config, config, cs_name)
        except ocio.Exception as e:
            if verbose:
                ocio.LogMessage(ocio.LOGGING_LEVEL_WARNING, str(e))
            problem_spaces.append(cs_name)

    # Cleanup -- restore the previous environment and config state.`
    os.environ.clear()
    os.environ.update(prev_env)
    config.setInactiveColorSpaces(old_invisible_color_spaces)
    
    return sorted(problem_spaces)
studio_config_modified = ocio.Config.CreateFromFile("ocio://studio-config-latest")

# Add an ACESproxy10i color space to the studio-config, because I suspect it'll also fail to match itself.
studio_config_modified.addColorSpace(
    ocio.ColorSpace(
        ocio.REFERENCE_SPACE_SCENE,
        name="ACESproxy10i",
        toReference= ocio.BuiltinTransform("ACESproxy10i_to_ACES2065-1")
    )
)

print(f"""
Unmatchable color spaces: {studio_config_modified.getName()}
---

 - Default IdentifyBuiltinColorSpace behavior: 
   {get_unmatchable_color_spaces(studio_config_modified, exclude_inactive=True)}
 
 - Ignore visibility + no optimizations: 
   {get_unmatchable_color_spaces(studio_config_modified, optimization=ocio.OPTIMIZATION_NONE)}
 
-  Ignore visibility + draft optimizations: 
   {get_unmatchable_color_spaces(studio_config_modified, optimization=ocio.OPTIMIZATION_DRAFT)}
""")

Unmatchable color spaces: studio-config-v4.0.0_aces-v2.0_ocio-v2.5
---

 - Default IdentifyBuiltinColorSpace behavior: 
   ['ACEScc', 'ACESproxy10i', 'ADX16', 'CIE XYZ-D65 - Display-referred', 'CIE XYZ-D65 - Scene-referred']
 
 - Ignore visibility + no optimizations: 
   ['ACEScc', 'ACESproxy10i', 'ADX16']
 
-  Ignore visibility + draft optimizations: 
   ['ACEScc', 'ACESproxy10i']

Metadata

Metadata

Assignees

No one assigned

    Labels

    Feature RequestNew addition to OCIO functionality.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions