Skip to content

Python API: Override Transform "+" and "-" operators as shorthand for concatenation and inversion #1240

Open
@zachlewis

Description

@zachlewis

Objective

Override the Transform class addition and subtraction operators to provide a convenient, intuitive, "magical" mechanism for combining and inverting transforms with a syntax similar OCIO's "Look Expression Language"

Motivation

Vastly improves the API user-experience and code intelligibility. Inspired by a feature written by @rminsk for our in-house OCIO config-authoring library. I'm the only person who knows the feature exists, and it's really nice.

The Pitch

Say you've got these three Transforms you'd like to string together:

lin_to_log = ColorSpaceTransform("scene_linear", "color_timing")
dailies_grade = FileTransform("dailies.ccc", cccid="$SHOT")
show_lut = FileTransform("show_lut_rec709.csp")

Right now, there's a traditional way of concatenating as a GroupTransform:

gt = GroupTransform()

gt.append(lin_to_log)
gt.append(dailies_grade)
gt.append(show_lut)

...and a fancy way:

gt = GroupTransform( [lin_to_log, dailies_grade, show_lut] )

Proposed is an extra fancy way, via the Transform addition operator:

gt = lin_to_log + dailies_grade + show_lut

Additionally, the Transform subtraction operator would denote direction inversion:

Currently, inverting a Transform involves first determining what the inverted direction would be:

inv_dir =  combineTransformDirections(lin_to_log.getDirection(), TRANSFORM_DIRECTION_INVERSE)
log_to_lin = lin_to_log.setDirection(inv_dir)

(note: the above example mutates the state of the original "lin_to_log" instance)

Sure would be nice to be able to do the following:

log_to_lin = -lin_to_log

But like Peter Falk says in Wings of Desire, "To concatenate, to invert... and if you do it together, it's fantastic":

# convert to working space, invert neutral grade, apply dailies grade, apply show_lut
inv_neutral_shot_look = lin_to_log - neutral_grade + dailies_grade + show_lut

# concatenate discrete inverse-shaper LUT1Ds and shaped LUT3Ds
lut1d3d = -ColorSpaceTransform("shaper_to_linear") + FileTransform("shaped_lattice.spi3d")

Implementation

I can't publicly share our actual code, but I can show you guys how it works in private, and / or quickly mock up a pure python example. The basic heuristic is very straightforward:

Addition:

  1. Create a new GroupTransform that concatenates the left-hand operand followed by the right-hand operand.

Subtraction:

  1. Enclose the operand in a new GroupTransform, and set the GroupTransform's direction to TRANSFORM_DIR_INVERSE

In practice, there are a some considerations for reducing unnecessary nested or adjacent GroupTransforms; and "inverted" GroupTransforms are converted to "forward" GroupTransforms by recursively unpacking, "flipping", and re-packing each Transform in the sequence.

In our implementation, when our library is imported, we're actually subclassing PyOpenColorIO.Transform, overriding the operators, and injecting the modified classes back into the PyOpenColorIO namespace, which is certainly one way of going about this... I'm not much for C++, but it seems like this wouldn't take too much time or energy to implement with pybind11. If there's interest in seeing a PR, I'd be more than happy to harass @remia and @michdolan for help.

What do you guys think... too much magic? Not enough magic?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Feature RequestNew addition to OCIO functionality.Needs DiscussionNeeds discussion before implmentation, which could result in changes, or a decision not to proceed.PythonIssues that involve majority python development (vs C++).

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions