Reversed transform for a OCIO::DisplayTransform


Troy Sobotka <troy.s...@...>
 

On Apr 21, 2014 1:53 PM, "Dmitry Kazakov" <dimu...@...> wrote:

> But, speaking truly, I didn't fully understand what is 1D transform... is it some term? Could you give me a link to some info or documentation?

A 1D LUT is where a basic “input in” equals “input out.” It can only be used to adjust transfer curves, and is completely invertible.

A 3D LUT is required to adjust saturation intents. Here a single input value can shift values in other channels, and as Mr. Gritz succinctly outlined in the OIIO response, one value out may have more than one entry point, and therefore this is a typically non-invertible transform.

> 3) The color selectors are effectively simple gradients generated in HSV or HSL color spaces [0].

Note here the L or V is color space relative based on the resultant luminance (Y in XYZ) of your three primaries. So L will be different across spaces with different primaries and need to be calculated differently with different weights I believe.

> 2) Right now color selectors generate HSV using QColor object. The HSV color is first converted to RGB by Qt, we assume this color being sRGB, and then convert (using lcms2) into the color space of the image. That is if we have an image in scRGB color space, then the conversions chain is the following: HSV->sRGB->scRGB

This likely means an artist can never select the gamut range that sRGB cannot reach, yet is clearly within scRGB's gamut. This cripples the wider gamut space somewhat.

> 3) Later this color (in scRGB) is converted to the monitor profile in either way: using OCIO or lcms2 with converting it to the monitor profile (say, monitor_sRGB).

Not quite that simple I believe.

To do this with OCIO, you would need to craft a 3D LUT of your display's profile with a specific destination space, and snap it into the OCIO chain _before_ the sRGB values are transfer / TRC curved. Applying the transform on transfer / TRC curved values will give incorrect results.

So linear scRGB values could go to XYZ, then through the display profile 3D LUT to get to say sRGB, then apply the sRGB 1D transfer curve.

> Obviously, we should disable the lcms color management when using OCIO, because the latter one has it's own definition of the source colorspace [1].

And this would require a bit of artist know-how, as I cannot see a way to do it automatically.

> Probably, I should generate color from HSV not using sRGB, but somehow differently?

It should be possible to create a wheel that uses the primaries of scRGB (wider gamut) and select from that. The out of gamut values would effectively be rendered as an absolute colorimetric clipped version.

Little tricky to get the value selected on a reverse transform, however. Maybe keep an off-screen buffer of the untransformed source space color picker, pre-destination transform[1]? Mr. Selan?

> The reverse gamma and exposure would then be applied to the sRGB, right before it is converted to the image color space. I guess it is correct, isn't it?

The typical order for changing color spaces is:

A) Invert any TRC / transfer curve on a source space to get to linearized values.
B) Convert the RGB primaries to XYZ.
C) Convert the XYZ primaries to RGB of the destination. This can be combined with the matrix from B.
D) Apply the destination TRC / transfer curve.

Note that in B and C your white point _must_ be aligned via adaptation matrices on the RGB spaces. Bradford or CAT02, for example.

I apologize for chiming in here, but I thought I could at least hint at some potential issues.

With respect,
TJS

[1] Where a display profile is likely an irreversible 3D LUT in most instances. Not a concern if it is purely a matrix transform for primaries.


Dmitry Kazakov <dimu...@...>
 

Hi, Jeremy!

Thank you for clarifications about 3D-lut! I google'd for it and now I think I understand why the reverse transformation is not possible. But, speaking truly, I didn't fully understand what is 1D transform... is it some term? Could you give me a link to some info or documentation?

Well, I also did some refactoring to the Krita's code so I can state the problem a bit more clearly. I will try to explain the problem again more detailed, if you catch where I am wrong, please tell me:

Requirements:

1) We have an image that may have a arbitrary color space. For example, scRGB 16bit float, which is linear.
2) We also have color selectors, which are gui elements for choosing the color by the painter. Obviously, the chosen color should coincide with the one that produced when painted on the image.
3) The color selectors are effectively simple gradients generated in HSV or HSL color spaces [0].

Implementation:

1) We have image colorspace managed by lcms2
2) Right now color selectors generate HSV using QColor object. The HSV color is first converted to RGB by Qt, we assume this color being sRGB, and then convert (using lcms2) into the color space of the image. That is if we have an image in scRGB color space, then the conversions chain is the following: HSV->sRGB->scRGB
3) Later this color (in scRGB) is converted to the monitor profile in either way: using OCIO or lcms2 with converting it to the monitor profile (say, monitor_sRGB).

That means that the color goes through the following chains:

When we generate the color: HSV->sRGB->scRGB
When we display the color in image color space on the monitor using lcms2: scRGB->monitor_sRGB
When we display the color in image color space on the monitor using OCIO: scRGB->OCIO

Obviously, we should disable the lcms color management when using OCIO, because the latter one has it's own definition of the source colorspace [1].

So, this is what I have now. Such approach seems to be logical, but I'm not sure it is totally correct. If you see any issue in it (especially, in this HSV part), please tell me :) Probably, I should generate color from HSV not using sRGB, but somehow differently?

The next step for me would be to implement support for OCIO-based reverse exposure and gamma thing for the selectors themselves. I'm going to put it into the first conversion chain (which converts HSV to sRGB and then to the image color space). The reverse gamma and exposure would then be applied to the sRGB, right before it is converted to the image color space. I guess it is correct, isn't it?

If you are still reading this mail, thank you! :) I would really appreciate your comments and feedback :)


[0] - could anyone suggest me something to read about HSV/HSL color spaces? It seems lcms2 doesn't have built-in implementation of them so we should generate it somehow using Lab or something...
[1] - Am I right in this assumption? I guess, yes, but I'm not totally sure. How the OCIO-enabled software is expected to behave in the case?



On Sat, Apr 19, 2014 at 8:29 AM, Jeremy Selan <jeremy...@...> wrote:
Hi!

A bit more info to add to the conversation. Apologies for now chiming in sooner...

As folks have noted, in many real-world color configurations the viewing transform is includes a 3D-LUT in the chain. While 1-D LUTs, matrices, and a few other analytical operators in OCIO are automatically inverted, OCIO does not attempt to do auto-calculate the inverse of 3D-LUTs as this is non-trivial, often ambiguous (in the case of many -> one color mappings, and low-fidelity / error prone).

In the applications I've had a change to integrate with OCIO, this assumption (the non invertibility of the display transform) makes creating a color picker more challenging.  As others have noted, one implementation which avoids this issue is to not generate the pure color gradient in 'display-space', but instead to apply generate color ramps in some intermediate space, and then to apply the display transform to the color picker display itself.

This begs the question of what intermediate space to use for color ramp / color picker math. In our experience using linear tends not to work very well, but any modestly perceptually uniform space looks pretty good.  The critical part is that this intermediate space must be an invertible conversion away from linear (1D lots work great for this), and that 0.0 maps to black.   So you synthesize the gradient assuming its natively in this intermediate space.  apply the forwards 3d viewing transform for display, and then back project (which you can do as it's invertible) into scene-linear.

In spi profiles, this 'color picker' role is a 1d approximation of the viewing transform, except with black pinned at 0.0 in both color spaces to make sure hsv math works out.

Sorry if this is confusing! :)

-- Jeremy



On Tue, Apr 15, 2014 at 5:59 AM, Dmitry Kazakov <dimu...@...> wrote:
Hi, All!

Thank you for your replies! Now I understood that not every display transform is revertible :) So I will have to refactor our color selectors code to make it work with color proof'ed colors :(


On Sat, Apr 12, 2014 at 6:06 AM, bsloan <bsl...@...> wrote:
For most workflows that use a 3D LUT to render extended dynamic range images, it is not necessary to reverse the operation on the picked color. Your application should just look up the un-transformed pixel value from the image (not the displayed frame buffer), based on the selection position.

It would be *very* handy if your application would render the various color pickers and swatches through the OCIO display transform as well. Photoshop does not do this. 




On Friday, April 11, 2014 10:24:38 AM UTC-7, Dmitry Kazakov wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.



--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.



--
Dmitry Kazakov


Jeremy Selan <jeremy...@...>
 

Hi!

A bit more info to add to the conversation. Apologies for now chiming in sooner...

As folks have noted, in many real-world color configurations the viewing transform is includes a 3D-LUT in the chain. While 1-D LUTs, matrices, and a few other analytical operators in OCIO are automatically inverted, OCIO does not attempt to do auto-calculate the inverse of 3D-LUTs as this is non-trivial, often ambiguous (in the case of many -> one color mappings, and low-fidelity / error prone).

In the applications I've had a change to integrate with OCIO, this assumption (the non invertibility of the display transform) makes creating a color picker more challenging.  As others have noted, one implementation which avoids this issue is to not generate the pure color gradient in 'display-space', but instead to apply generate color ramps in some intermediate space, and then to apply the display transform to the color picker display itself.

This begs the question of what intermediate space to use for color ramp / color picker math. In our experience using linear tends not to work very well, but any modestly perceptually uniform space looks pretty good.  The critical part is that this intermediate space must be an invertible conversion away from linear (1D lots work great for this), and that 0.0 maps to black.   So you synthesize the gradient assuming its natively in this intermediate space.  apply the forwards 3d viewing transform for display, and then back project (which you can do as it's invertible) into scene-linear.

In spi profiles, this 'color picker' role is a 1d approximation of the viewing transform, except with black pinned at 0.0 in both color spaces to make sure hsv math works out.

Sorry if this is confusing! :)

-- Jeremy



On Tue, Apr 15, 2014 at 5:59 AM, Dmitry Kazakov <dimu...@...> wrote:
Hi, All!

Thank you for your replies! Now I understood that not every display transform is revertible :) So I will have to refactor our color selectors code to make it work with color proof'ed colors :(


On Sat, Apr 12, 2014 at 6:06 AM, bsloan <bsl...@...> wrote:
For most workflows that use a 3D LUT to render extended dynamic range images, it is not necessary to reverse the operation on the picked color. Your application should just look up the un-transformed pixel value from the image (not the displayed frame buffer), based on the selection position.

It would be *very* handy if your application would render the various color pickers and swatches through the OCIO display transform as well. Photoshop does not do this. 




On Friday, April 11, 2014 10:24:38 AM UTC-7, Dmitry Kazakov wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.



--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.


Dmitry Kazakov <dimu...@...>
 

Hi, All!

Thank you for your replies! Now I understood that not every display transform is revertible :) So I will have to refactor our color selectors code to make it work with color proof'ed colors :(


On Sat, Apr 12, 2014 at 6:06 AM, bsloan <bsl...@...> wrote:
For most workflows that use a 3D LUT to render extended dynamic range images, it is not necessary to reverse the operation on the picked color. Your application should just look up the un-transformed pixel value from the image (not the displayed frame buffer), based on the selection position.

It would be *very* handy if your application would render the various color pickers and swatches through the OCIO display transform as well. Photoshop does not do this. 




On Friday, April 11, 2014 10:24:38 AM UTC-7, Dmitry Kazakov wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.



--
Dmitry Kazakov


bsloan <bsl...@...>
 

For most workflows that use a 3D LUT to render extended dynamic range images, it is not necessary to reverse the operation on the picked color. Your application should just look up the un-transformed pixel value from the image (not the displayed frame buffer), based on the selection position.

It would be *very* handy if your application would render the various color pickers and swatches through the OCIO display transform as well. Photoshop does not do this. 



On Friday, April 11, 2014 10:24:38 AM UTC-7, Dmitry Kazakov wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov


Joseph Slomka <joseph...@...>
 

Remember that colorspaces can be defined to and from the reference. You can manually define the inverse and come up with an inverse 3d lut for the color picker that is close enough to fully inverted.

I don't believe that OCIO inverts any 3d luts, even if you had a unity lut where rgb in = rgb out.

Typically the design of OCIO does not automatically do anything that will get you into trouble. You have to deliberately make those choices. 


Example code below.

-Joseph

cs = OCIO.ColorSpace(family='kodak',name='kdk2383')
cs.setDescription("kdk2383: Kodak Vision stock")
cs.setBitDepth(OCIO.Constants.BIT_DEPTH_UINT10)

# this sets the conversion to reference XYZ from film log
groupTransform = OCIO.GroupTransform()
groupTransform.push_back(OCIO.FileTransform('2383_to_bt1886.3dl',interpolation=OCIO.Constants.INTERP_LINEAR))
groupTransform.push_back(OCIO.ColorSpaceTransform(src='bt1886', dst='lxyz'))
cs.setTransform(groupTransform, OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE)
config.addColorSpace(cs)

# this sets the conversion from reference XYZ to film log values
groupTransform = OCIO.GroupTransform()
groupTransform.push_back(OCIO.ColorSpaceTransform(src='lxyz', dst='bt1886'))
groupTransform.push_back(OCIO.FileTransform('bt1886_to_2383.3dl',interpolation=OCIO.Constants.INTERP_LINEAR))
cs.setTransform(groupTransform, OCIO.Constants.COLORSPACE_DIR_FROM_REFERENCE)
config.addColorSpace(cs)


On Fri, Apr 11, 2014 at 4:29 PM, Kevin Wheatley <kevin.j....@...> wrote:
As Piotr points out this is in general not mathematically possible, in particular if any LUTs are not invertible, you'll not be able to do this. in a 1D sense imagine if you have a curve which has a turning point which results in there being multiple cases where two values of x give y having the same value, when generalised into a 3D LUT this problem becomes even more difficult to invert in a general sense even in typical outputs this is not necessarily going to give you predictable outcomes (typical for a display transform emulating another device/output especially a subtractive one like film prints or other CMY(K) outputs). These 3D luts often feature gamut compression which is where a lot of the loss comes from.

In specific cases there can be techniques to invert, but these need hand selecting, which is probably why OCIO won't do it, something like Argyll CMS or another ICC profile tool might do something for you, which might be interesting to implement, alternatively the CTL code has a scattered data interpolation code that might work.

Kevin


On Fri, Apr 11, 2014 at 10:29 PM, Piotr Stanczyk <piotr.s...@...> wrote:
From what I remember, you can do this if the transforms are analytical. If you are referring to a baked in file based one then you will have to provide your own from for that. 

Piotr




On 11 April 2014 10:24, Dmitry Kazakov <dimu...@...> wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.


Kevin Wheatley <kevin.j....@...>
 

As Piotr points out this is in general not mathematically possible, in particular if any LUTs are not invertible, you'll not be able to do this. in a 1D sense imagine if you have a curve which has a turning point which results in there being multiple cases where two values of x give y having the same value, when generalised into a 3D LUT this problem becomes even more difficult to invert in a general sense even in typical outputs this is not necessarily going to give you predictable outcomes (typical for a display transform emulating another device/output especially a subtractive one like film prints or other CMY(K) outputs). These 3D luts often feature gamut compression which is where a lot of the loss comes from.

In specific cases there can be techniques to invert, but these need hand selecting, which is probably why OCIO won't do it, something like Argyll CMS or another ICC profile tool might do something for you, which might be interesting to implement, alternatively the CTL code has a scattered data interpolation code that might work.

Kevin


On Fri, Apr 11, 2014 at 10:29 PM, Piotr Stanczyk <piotr.s...@...> wrote:
From what I remember, you can do this if the transforms are analytical. If you are referring to a baked in file based one then you will have to provide your own from for that. 

Piotr




On 11 April 2014 10:24, Dmitry Kazakov <dimu...@...> wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.


Piotr Stanczyk <piotr.s...@...>
 

From what I remember, you can do this if the transforms are analytical. If you are referring to a baked in file based one then you will have to provide your own from for that. 

Piotr




On 11 April 2014 10:24, Dmitry Kazakov <dimu...@...> wrote:
Hi, all!

I am a developer of Krita [0] painting application and we use OCIO for displaying wide range images. I'm having a bit of a problem now. Since we are a painting application, we need to be able not only to display a color on screen (which we do using DisplayTransform), but also to pick an already displayed color back into app (for usage in color selectors or for drag-and-dropped colors). So, effectively, I need to get a reversed transform for a DisplayTransform. But it seems to be explicitly prohibited (it throws an exception in the case). I'm wondering, is it possible to get a reversed transform for a DisplayTransform without reimplementing it completely using GroupTransform?

Thank you in advance!

[0] - krita.org

--
Dmitry Kazakov

--
You received this message because you are subscribed to the Google Groups "OpenColorIO Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ocio-dev+u...@....
For more options, visit https://groups.google.com/d/optout.