Unbounded Floating Point Pipelines

I have the impression, we have to define the meaning of unbounded

I just double-checked Marti’s article on Unbounded Color Engines (http://www.littlecms.com/CIC18_UnboundedCMM.pdf). There is no mention of using an RGB color space as a PCS. The PCS for ICC profile conversions is either XYZ or LAB, as his article assumes, states clearly (quoting ". . . the ICC PCS, which can either be XYZ or CIE Lab*. "), and also uses in his various outlines of profile conversions.

I’m an engineer in the aerospace domain by trade, and from bitter experience I know that engineering is less about the technology and more about the constructs of communication needed to get large numbers of people marching the right direction to build things no single one of them could fabricate on their own. So, @anon11264400, I do get your point about clarity in terminology. This discourse is producing clarity, but discourse is an iterative process.

When I selected the term for the title of the thread, I didn’t realize there was a prior claim. Frankly, I still assert a claim for it as we discuss our display-referenced ways out here on the edge. But, don’t think we’re not realizing the problem; that’s what this thread was premised to discuss, at least one aspect of it.

1 Like

That’s also what I understand by unbounded. I just was a bit confused by this (not from you)

`
Unbounded means that you can also have negative values.

Historically this has been referred to as clipping and clamping,
`

Quoting from Marti Maria’s paper “Unbounded Color Engines”:

Up to the ICC specification 4.2, profiles had a limit on the precision they could deliver. The internal encoding of profiles did force them to have a precision of 8 or 16 bits at most. That limit on precision was a stopper in the adoption of ICC profiles by some applications, like RAW photo processing or digital cinema, where greater levels of accuracy are required. See figure 2 for a DPX real world example, where input values 0-40 map to same output value 2, and 16-bit integer encoding cause severe quantization in shadows.

To overcome this limitation, in November 2006 the ICC approved the Floating Point Encoding Range addendum to the profile specification. With the introduction of floating-point data in the spec, ICC profiles are no longer limited to 8 or 16 bit, but to the broad range of 32-bit IEEE 754 floating point. That was a huge improvement when regarding precision and dynamic range, which is now only limited by floating point representation and can take as much as 10^39 [10 to the 39th power].

Figure 2. KODAK VISION2 500T Color Negative Film 5218 / 7218

This addendum, however, introduced another improvement perhaps not so evident but equally important. Floating point encoding has a huge domain, which is nearly infinite, much larger than any real gamut. Based on that, one could think that a capable CMM using such kind of profiles could operate in an “unbounded mode” that would not have the limits that traditional CMMs have.

Ok, i stand myself corrected then. I probably confused a discussion during the introduction of this concept in the development of GIMP with GEGL (i.e. the idea of being able to encode infinite colour gamut in RGB could allow a simple colorspace to be used as PCS. A colorspace to rule them all. I didn’t made that up :grin:)

But yes, if that’s not in Marti’s paper i was wrong.

However, as @anon11264400 pointed out, the problem is still encoding out-of-gamut values in an emission model based on three emissive primaries.
And that’s definitely in Marti’s paper, as you just quoted

Okay, bear-of-little-brain here is trying to get to understanding the simple root of the problem. Based on the quotes above, I’ll state a few assertions, feel free to respond “correct” or “incorrect”, and any explanation anyone feels to present.

  1. “primaries” are used in both display- and scene-referred models to define color gamut. In the reading I’ve done to date, I don’t see anything to refute that.

  2. “emissive primaries” refer to the gamut of a display device.

  3. “emission model” I think refers to the assertion that ICC color management is based on transforms that map both gamut and luminance.

  4. If ICC’s gamut transforms are a problem, then LittleCMS’s unbounded transforms appear to be a step in the right direction.

  5. If ICC’s tone response curves are a problem, then setting them to “linear” nulls out their effect in a transform. Yes, unnecessary computing, but the image is not harmed in the production of this movie (sorry, a perhaps obtuse bit of American humor there)

  6. Soooo… bear-of-little-brain here has come to believe that the fundamental problem with ICC is the coupling of gamut and luminance. N’est ce pas? (that is the extent of my French, a sad commentary for someone from south Louisiana)

I’m trying to figure out if the next addition to my hack software is OCIO or some other scene-reference color management model, or lens correction using lensfun (they got the “lens” part right, the “fun” part not so much). Right now, I’m getting the color I want using LittleCMS, so I’m leaning toward lensfun. I’ll eventually get to what we’ve been discussing here, as retirement looms and I don’t want to spend all my time going out there and taking pictures… :sunny:

Hmm, I couldn’t find the words “emissive” or “emission model” in Marti’s paper. So I looked in the V2 and V4 specs, and also the iccMAX specs.

There is a “lumi” tag that can be in a profile, but isn’t required, and the only profile I could find it in is the sRGB.icm profile supplied by ArgyllCMS. The original sRGB profile contained a lot of tags that weren’t actually ever used. But here’s what the sRGB lumi tag looks like:

TagSignature>lumi</TagSignature>
XYZNumber X="76.04003906" Y="80.00000000" Z="87.12036133"/>

And here’s what the tag holds, quoting from V4 specs:

9.2.32 luminanceTag
Tag signature: ‘lumi’ (6C756D69h)
Permitted tag type: XYZType
This tag contains the absolute luminance of emissive devices in candelas per square metre as described by the Y channel.
NOTE The X and Z values are set to zero

iccMAX has a section on emissive devices, that on quick reading considers changes in how something looks on a screen based on viewing angle, but I don’t think that’s what @anon11264400 or @gez are talking about when they use the words “emissive primaries” and “emission model”.

Hi @ggbutcher - I’m going to tag along with you and wait for clarification from @anon11264400 and @gez.

I’m not sure what you might mean by “coupling of gamut and luminance”. Do you mean that RGB matrix color spaces per se (apart from any considerations of and about ICC color management and profiles) define a color gamut (the triangle on the xy plane of the xyY color space), but don’t put a limit on luminance?

I was thinking about the tone response curves. “Luminance” may not have been the right term.

One of my personal challenges with ICC has been to wrap my head around the application of gamma in the transforms.

:roll_eyes: I’m talking about RGB. A model based on emission with three primaries. Conflating out of gamut values in a model that is defined by the primaries chromaticity is the problem.

OK, now I’m really confused. Doesn’t your OCIO workflow also use RGB color spaces for editing? I already know the answer is yes, but I thought all the references to “emissive” and “emission model” were about how ICC profile color-managed workflows were different from and somehow inadequate compared to what you do in an OCIO workflow.

In an OCIO workflow, do you never, ever do any editing operations that produce any negative channel values? Well, Subtract can produce images with negative channel values (yes, of course this is an extreme example - I put a layer with R=G=B=1.0 everywhere, over your colours.exr image, and set the blend mode to Subtract):

colours-subtracted-from-white

Are such negative channel values immediately clipped upon being produced in an OCIO workflow?

What about unsharp mask? The algorithm of itself can easily produce channel values slightly outside the range of the original colors in the image, including values less than 0 in the shadows.

Programmers tell me that resizing an image is also such an operation. I rather suspect there are quite a few operations that produce channel values outside the range of colors in the “pre-operation” image (notice I’m carefully avoiding “>1.0” and confining my inquiry to “<0.0”). How does an OCIO workflow deal with these kinds of colors produced during the course of editing the image?

Hmm, well, as you know the color gamut is set by the primaries and the TRC determines how rapidly colors get lighter as the RGB values go up. So by “application of gamma in the transforms”, do you mean “when to operate on linear RGB” vs “when to switch to a more perceptually uniform TRC”?

It is possible that I, not being an english native speaker, expressed myself in an unclear manner. If that’s the case, apologies.
I’ll try to be as clear as possible:
RGB is a model based on light emission. Would you say that is correct or not?
Colour is produced in the RGB model by mixing different intensities of three primaries. What colours can or can’t be reproduced depend on the chromaticity of each one of the three primaries of a certain device (out-of-gamut being colours that can’t be reproduced with those primaries? Is that correct?
So, an RGB pixel is an emission. In a display-referred model the intensity of that emission is limited by the maximum intensity allowed by that display. In a scene-referred model there is no limit for that intensity.
So we’re basically talking about three lanterns, one red, one green, one blue. Those lanterns can emit light, not suck it, hence negative values are meaningless in the model.
Do we all agree on that? (I’m talking about the MODEL, not what you can do with that data).

Of course it is possible that processing those values will eventually produce negative values after some operations. But such values are meaningless in the RGB model. Below zero there is no emission.
Colour management should make sure that the values produced are valid and have meaning in the reference space when you ingest images in your processing pipe, and ensure the consistence of colour when you take it out.
If you start by allowing colour management to produce meaningless values by encoding out-of-gamut colours in RGB values, you’re feeding your processing pipe with values that are NOT emissions, hence invalid in the RGB model.
What your processing pipe does afterwards producing negative emissions and how you deal with them is a different issue.
Does anyone here disagree with the above?

So what are we trying to say? Just don’t let your CM screw your RGB. It’s as simple as that. If LCMS “unbounded CM” feeds your processing pipe with invalid RGB values, stay away from it. It’s a failed approach for editing. Keep your data IN reference.

Is it so hard to process?

Isn’t it possible that you start out with emission values [0,inf), then apply some operations to your image values (that may push to invalid values), but ultimately, at the end of the line, you push them back (by whatever way) to in-gamut values?
Or would any intermediate invalid value be immediately inadmissible by your approach (i.e. clip it to zero).

It depends on the software and operations I think. In a nodal compositor artists control the operations and decide whether to clip/clamp or not because they have control over low-level nodes (i.e.: it is possible that you want to keep negative produced by a substraction because you know you’re going to take them back to positive in the next operation).
But there are higher-level operations that should produce valid RGB and it would be pointless to leave them poduce invalid RGB (for example, producing negative RGB from a saturation op would be silly).
In other software that hides low-level ops and only presents high-level canned operations, It’s probably a better idea to stick with valid RGB during the pipe, as users won’t have fine-grained control to deal with (or take advantage of) invalid values.

It’s a personal opinion. Don’t take this as part of any recommendation from OCIO or ACES.

Personally I don’t think that this is a good example… in fact, it demonstrates the opposite.

Suppose you have an initial floating-point RGB buffer in linear sRGB colorspace. You apply a saturation boost on the LCh Chroma channel. This implies a conversion from sRGB to LCh, an increase of the C channel, and a conversion back to sRGB. The operation can easily bring some pixel outside of the sRGB gamut, and thus produce negative channel values.

Is this bad? Not necessarily… as long as the output sRGB values are used to represent over-saturated colors, they are perfectly fine even if some channel values go negative. However, such values are likely problematic if they are used as input for further editing.

However, like in the subtraction example given just before, the user can “take them back to positive” before applying the next edit, for example by converting the data from sRGB to a wider colorspace that can accommodate such highly-saturated colors.

Clipping the output of a saturation op will introduce hue shifts at the gamut boundaries of the working RGB colorspace, and IMHO this is definitely not the correct behaviour…

1 Like

such values are likely problematic if they are used as input for further editing.

You answered yourself here. One is always bound to the limits of the reference space primaries. Beyond gamut becomes non data in terms of pixel manipulations[1].

example by converting the data from sRGB to a wider colorspace that can accommodate such highly-saturated colors.

This is why reference spaces need to be chosen wisely.

Clipping the output of a saturation op will introduce hue shifts at the gamut boundaries of the working RGB colorspace, and IMHO this is definitely not the correct behaviour…

Except it is the only behaviour for subsequent pixel manipulations that is feasible. Should the actual node in question perform such a task? No. That should be up to the pixel pusher to decide exactly what is happening in the situation. Inevitably however, if the task is further pixel manipulations, a clamp is the only option.

What about unsharp mask? The algorithm of itself can easily produce channel values slightly outside the range of the original colors in the image, including values less than 0 in the shadows.

This is apples and oranges. In this particular case, this is because your sampling formula has negative lobes. This means that the data is incorrect already because the sampling has overshot the actual intended value. In the case of undershooting some low value into a negative, well… that tells you everything you need to know given that the original value was small, not negative.

[1] Rolling the data selection from the UI element back into the reference would be a poor paradigm here as well, in this case Foo to Lch. Better would be to have a UI buffer that remains always in reference, and present the view of the UI using the Lch transform. The selection then would simply be from the original reference, likely saving performance cycles as well.

Hmm, so you are saying that my little tutorial on using high bit depth GIMP’s ability to retain out of gamut channel values, and saving up til the last step the task of bringing everything back into gamut before saving the image to disk for output as a print or for display on the web, is a completely wrong way to edit an image?

I find the approach to editing outlined in my tutorial to be very useful and convenient. I rather suspect I’ll continue doing what I’ve been doing regardless of how many people want to tell me I’m violating some principle or another.

Now that sounds hostile, and I don’t mean it to sound hostile. But what you are suggesting seems rather like tying one’s feet together before setting out to take a walk. I’m just not sure where the advantage might be in disallowing the generation and subsequent use of out of gamut channel values, until such time as it becomes convenient to bring any remaining out of gamut channel values back into gamut.

Note the processing I outline in my tutorial doesn’t involve multiplying or dividing the out of gamut colors by a non-neutral color, so there’s no generation of what I earlier referred to as “squirrelly” colors.

Hmm, so you are saying that my little tutorial on using high bit depth GIMP’s ability to retain out of gamut channel values, and saving up til the last step the task of bringing everything back into gamut before saving the image to disk for output as a print or for display on the web, is a completely wrong way to edit an image?

I’m not going to judge your particular workflow. I know beyond a shadow of a doubt though that for typical photographic / physically plausible workflows, it is about as far away from WYSIWYG as one could be. Every single value that exceeds a display referred 1.0 value isn’t the correct output, as are a majority that fit within the display referred range, as the trivial demonstration with the coloured swatches above illustrates.

That strikes me to being akin to hex editing an image blind, which some folks enjoy doing.

Again though, this thread has drifted, as has my reluctant reason for wading into it. I am absolutely skeptical about artificial terms that have little to no need to exist when other sufficient terms do. I also see plenty of conflation. A good example:

convenient to bring any remaining out of gamut channel values back into gamut.

This is a precarious statement for example. Is the value out of gamut or negative? There is a difference here, and such a statement conflates the issues. For example, as I stated above. some sampling will yield negative lobes. That isn’t out of gamut. That is simply a byproduct of unfortunate sampling.

Note the processing I outline in my tutorial doesn’t involve multiplying

Which is exactly how one simulates indirect lighting using imagery.

TL;DR All negative values do not represent identical phenomena, and in a vast majority of manipulations, negative values will corrupt your compositing. No amount of spurious terminology will dig one out of such a hole.

Please explain the difference betwen “out of gamut” and “negative”.

But I wasn’t trying to simulate lighting indirectly. If I had need to multiply by a color, I would have brought the channel values back into gamut before doing the multiplication.