Skip to content

Conversation

doug-walker
Copy link
Collaborator

@doug-walker doug-walker commented Jul 28, 2025

Per issue #2174, this PR adds a GradingHueCurveTransform to OCIO to allow use of spline-based curves to modify hue, saturation, and luma.

This transform provides eight spline curves to make the following adjustments:

  • Hue-Hue: Map input hue to output hue (where a diagonal line is the identity).
  • Hue-Sat: Adjust saturation as a function of hue (a value of 1.0 is the identity).
  • Hue-Lum: Adjust luma as a function of hue (a value of 1.0 is the identity).
  • Lum-Sat: Adjust saturation as a function of luma (a value of 1.0 is the identity).
  • Sat-Sat: Adjust saturation as a function of saturation (a diagonal is the identity).
  • Lum-Lum: Adjust luma as a function of luma, maintaining hue & sat (diagonal is identity).
  • Sat-Lum: Adjust luma as a function of saturation (a value of 1.0 is the identity).
  • Hue-FX : Map input hue to delta output hue (a value of 0.0 is the identity).

The algorithm is different for scene-linear, logarithmic, and video color spaces, so initialize the style argument appropriately before setting the curves.

An RGB-to-HSY FixedFunction is has been added to convert RGB into a hue, saturation, luma color space. However, there is an option to bypass that conversion to use an outboard transform.

Like the GradingRGBCurveTransform, the curves are defined by the x and y coordinates of a set of control points. A spline will be fit to the control points. Monotonicity is preserved for curves where the diagonal is the identity. For curves that take luma as input, if the style is scene-linear, the units are in photographic stops relative to 0.18. For log and video, the luma is scaled the same as the input RGB.

The hue variable is [0,1] and is periodic. For example, -0.2, 0.8, and 1.8 are equivalent. The domain of the curves is [0,1] and control points outside that domain are mapped into it. A hue of 0 or 1 corresponds to a magenta hue.

The transform is invertible as long as the curves allow it. For example, if saturation is mapped to zero, obviously that cannot be resaturated. Care should be taken with the Hue-FX curve because it is possible to fold hues over on themselves, which also cannot be inverted. In most cases the Hue-FX curve is not necessary since the Hue-Hue curve provides similar functionality with the added benefit of being strictly invertible.

The control points are dynamic, so they may be adjusted even after the Transform is included in a Processor.

Finally, there is a minor improvement to the operator<< method for all transforms to improve readability.

larochj and others added 23 commits July 4, 2025 14:53
(cherry picked from commit 393c0f6)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 5b9ff85)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit d1a5d7a)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 83340c5)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit b243d1d)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 2a96167)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 0bac401)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 051bd7d)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 07aca3c)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit bf78019)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 4a5001e)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Towards clean

(cherry picked from commit d298681)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 3f393f8)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit e7fd3f2)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit d658e70)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit a25c1c7)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 4f276e5)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 7e6378f)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit a9e51c8)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit 6c233c8)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
(cherry picked from commit cf7744e)
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
@doug-walker doug-walker added this to the OCIO 2.5.0 milestone Jul 28, 2025
Copy link

linux-foundation-easycla bot commented Jul 28, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
Signed-off-by: Doug Walker <doug.walker@autodesk.com>
@doug-walker doug-walker removed this from the OCIO 2.5.0 milestone Aug 1, 2025
Copy link
Collaborator

@zachlewis zachlewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is terrific -- I find the API for the GradingHueCurveTransform intuitively similar to the GradingRGBCurveTransform. I really love having the ability to create and represent these kinds of global, invertible* secondary adjustments in OCIO configs / CTF / shader language. In particular, I can see this making life a lot easier for commercials / short form vfx <--> finishing interchange workflows. Also, as I mentioned to Scott on Slack somewhere, this presents a great opportunity to collaborate with the ACES team w.r.t. their LMT library initiative...

Really amazing work.

@ptrpfn
Copy link

ptrpfn commented Sep 16, 2025

I tested with different control point scenarios for hue to hue and might have run into a bug with negative slope across periodic seam in PrepHueCurveData.

As far as I understand PrepHueCurveData and other functions are built to ensure that hue-to-hue mapping does not have negative slopes for preserving reversibility – in cases where control points would create such slopes.

A problematic config block is

        - !<GradingHueCurveTransform>
          style: linear
          hue_hue: {control_points: [0.15, 0.15, 0.16, 0.16, 0.84, 0.83, 0.85, 0.85, 0.95, 1.25]}

The y value of 1.25 is higher (even if wrapped to 0.25) than the "next" (wrapped) control point's y value of 0.15, but the PrepHueCurveData does not catch that to ensure slopes don't get negative (it catches a lot of other scenarios, though).

Here is a processed image (left: original, right: processed) with that block (see multiple pink areas):
Screenshot 2025-09-16 at 12 48 27

An LLM in my test project proposed a patch by adding a block of code that prevents that case in periodic seam scenarios:

         if (!isHorizontal && isPeriodic && !outCtrlPnts.empty())
         {
             const float y_first = outCtrlPnts.front().m_y;
             const float y_last  = outCtrlPnts[numCtrlPnts - 1].m_y;

             // Positive epsilon, robust to sign of (y_last - y_first).
             const float eps    = std::max(1e-6f,
                                           std::fabs(y_last - y_first) * tol * 2.f);
             const float min_y0 = (y_last - 1.f) + eps;

             if (outCtrlPnts.front().m_y < min_y0)
             {
                 outCtrlPnts.front().m_y = min_y0;

                 // Propagate forward so we keep y non-decreasing among real points.
                 for (unsigned i = 1; i < numCtrlPnts; ++i)
                 {
                     const float min_yi = outCtrlPnts[i - 1].m_y + eps;
                     if (outCtrlPnts[i].m_y < min_yi)
                     {
                         outCtrlPnts[i].m_y = min_yi;
                     }
                 }
             }
         }

As a patch file: OpenColorIO-49048ae-Fix negative slope across periodic seam in PrepHueCurveData.patch

With that code the result looks like that:

Screenshot 2025-09-16 at 12 51 05

I do not fully understand the patch in context of the logic with the control points. So I leave it to someone with more understanding of the code to decide if that's a valid patch or if that issue needs a totally different approach.

@ptrpfn
Copy link

ptrpfn commented Sep 16, 2025

One more comment: I struggled a bit with the curve interpolation at first, as typically such controls in software (ours and others) leave most of the range to identity (see screenshot).

Screenshot 2025-09-16 at 13 07 07

The quadratic splines (are they?) in the GradingHueCurveTransform don't behave like that, but I found that you can achieve linear/identity segments by adding secondary control points very close the to ones that limit the modified segments.

It is up to the host applications then to create such shadow-control points if the behavior for identity segments is desired.

@doug-walker
Copy link
Collaborator Author

Thank you @ptrpfn, that is an excellent point regarding the handling of out-of-range values. I realize that in our implementation, the UI is imposing some limitations on the placement of the control points that prevents the example you provided from happening. But as you point out, additional checks are needed in OCIO since values could come from a CTF file and it needs to be robust to that. I will make a commit in the next few days to address this.

Regarding your comment about curve interpolation, I think the proposed hue curve transform is able to do what you are asking for, I'm attaching a screenshot. The curve shown only consists of four control points (no "shadow points" are needed). If you want a "hue vs. hue" type curve that is horizontal like this in its identity position, please try the HUE_FX curve. (The HUE_HUE curve is diagonal in its identity position. The benefit of the HUE_HUE is that it prevents hue from folding over on itself and therefore becoming non-invertible. The HUE_FX curve does not have this benefit, but it is more in line with what users are familiar with from a variety of applications.)

image

Thanks again for the helpful feedback!

@ptrpfn
Copy link

ptrpfn commented Sep 18, 2025

@doug-walker Thank you for clarifying the differences between hue-hue and hue-FX! That makes a few things much clearer.

Signed-off-by: Doug Walker <doug.walker@autodesk.com>
@doug-walker
Copy link
Collaborator Author

@ptrpfn, my latest commit adds some missing validation checking. This will cause validation to fail on the example you provided above. I've added a comment to explain that applications that edit curves must constrain the UI so that the validation function succeeds. Thanks again for calling this out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants