Closed MrLixm closed 1 year ago
Given that the only recourse appears to be the dummy LUT, surely there is a mechanism to apply this pre-log?
I am still wondering why a simple clip post log, at the appropriate value range, is not working here, so perhaps you can elaborate?
At any rate, the allocation transform can also serve up a uniform
distribution such that the desired range is normalized to the range specified. I would think that a reasonable range could work without introducing tremendous quantisation errors?
Hey Troy,
Here is why I am applying the clip post-log : ↳ white = negatives corresponding transform :
from_reference: !<GroupTransform>
children:
- !<MatrixTransform> { matrix: [ 0.842479062253094, 0.0784335999999992, 0.0792237451477643, 0, 0.0423282422610123, 0.878468636469772, 0.0791661274605434, 0, 0.0423756549057051, 0.0784336, 0.879142973793104, 0, 0, 0, 0, 1 ] }
- !<AllocationTransform> { allocation: lg2, vars: [ -12.47393, 4.026069] }
As you can see the lg2 transform introduced negative on all pure black values and leave the original negatives value that were not clip.
So by clipping I' making sure that at least the initially pure black value, get back to (0.0,0.0,0.0)
. Inital negatives are still fucked up but that is the reason of this ticket.
Also nice tip for the uniform transform, didn't even thought of it. I think using it in the pre-clip might actually fix my issue. Let me try.
Alright quick test of your proposition but no success. Using :
- !<ColorSpace>
name: AgX Log (Kraken)
family: AgX
equalitygroup: ""
bitdepth: 32f
description: AgX Log (Kraken)
isdata: false
allocation: uniform
allocationvars: [ -12.47393, 4.026069 ]
from_reference: !<GroupTransform>
children:
- !<AllocationTransform> { allocation: uniform, vars: [ -12.47393, 4.026069 ] }
- !<FileTransform> { src: dummy.spi1d, interpolation: linear }
- !<AllocationTransform> { allocation: uniform, vars: [ -12.47393, 4.026069 ], direction: inverse }
so with an uniform transform this time, no luck same issue and I don't know why.
The negatives are not clipped at all :
AllocationTransform still looks like black magic to me and I don't understand what's going on, because if the LUT hack is working prost-log, why it isn't working pre-log ?
The transform itself could be a cause of error potentially? Are the allocation_vars
set here? With V1 it was dark magic, and apparently not required with V2.
Logs of course produce negative values in the log encoding with less than one to zero values if they are just logs, but my understanding has always been that the AllocationTransform
was normalized log.
Worth some experimenting to see if it can get sorted, or ask on the Slack.
Oi, I got something working !
https://github.com/MrLixm/AgXc/blob/1137d57b8b78a3d7ff86da59ded707fdd6d27469/ocio/config.ocio#L78-L81
Did a quick search on the OCIO slack using the word "clamp" with not much hope as it has the 30days restrictions .but ... I got something, someone mentioned that the CDLTransform
was updated in v2 because it had an inconsistent negative clamp behaviour ! Exactly what I want.
So I just dropped 2 CDLTransform that cancel each other and ... Of course doesn't work cause OCIO smart enough to detect a no-op and remove the transforms. So I get even deeper into the hack and added a very small offset so it still register.
And success ! the link I send above is a working version ! I got negatives properly clamped and keep all the dynamic range.
Of course the only issue is that the data is slightly modified by the small increment in the last CDLTransform. It's really minimal but it can be visible by eye when zoomed in. This annoys me but I'm not sure I got a better choice.
Also this works on v1 and v2 (because the behaviour was kept for v1 config.) So for now it's a big win.
My only question left is :
should I clamp negatives produces BY the lg2 transform now ?
Because I clamp negatives before, but of course the [0,0,0]
values produce negatives after the log transform. Should I clip them using the same hack ? Or are they expected (I guess not).
Though on original AgX config running on OCIOv2, they are here.
Anyway, added the post-log negative clamp : https://github.com/MrLixm/AgXc/blob/589157178fb5a002dca5326a4606479c8a208b82/ocio/config.ocio#L77-L86
but of course the [0,0,0] values produce negatives after the log transform.
I am not sure “of course” is logical here.
The clip at BT.709 relative tristimulus asserts that everything is within the chromaticity footprint, and that there is no way to escape the footprint.
Converting to a normalized log2 should also result in 0.0, representing the low value, to 1.0, representing the high value relative to the normalized log2.
The only issue is that when we apply a curve on a per channel basis, we in fact increase the purity of the tristimulus in a range of output triplets. Given we have an outset to restore the values to BT.709 positions, effectively stretching the values back outwards, we induce negatives based on those aforementioned mixtures and the curve interaction.
As best as I can tell, until the alpha of AgX drops, the clips are required at two positions:
If you are getting negatives anywhere else, I would be concerned.
Thanks for the explanation. My course made reference to the fact that a log transform cannot represent 0 ?
>>> import math
>>> math.log2(0.0)
ValueError: math domain error
So it make sense that instead we got negatives out. This is the behavior describe in the documentation :
https://opencolorio.readthedocs.io/en/latest/guides/authoring/allocation_vars.html one downside of this approach is that it can’t represent 0.0, which is why we optionally allow a 3d allocation var, a black point offset.
Which is what I used initially, but was producing offset black as pointed out in issue #9.
And that's why I'm now clipping those negatives.
Yes, but I believe the AllocationTransform
is a normalized log encoding.
That is, the range covered is normalized to the 0.0 to 1.0 range. This is also the technique that was used for zero to one shaders as a compression technique. The original Filmic configuration used the AllocationTransform
as a mirror of the normalized log2 in the scripts, and it worked.
So negatives I do not believe represent the classic less than one range of a pure log, but rather values that are offset below the range.
I believe it mirrors something like the function below:
def open_domain_to_normalized_log2(
in_od,
in_middle_grey=0.18,
minimum_ev=-7.0,
maximum_ev=+7.0
):
total_exposure = maximum_ev - minimum_ev
in_od = numpy.asarray(in_od)
in_od[in_od <= 0.0] = numpy.finfo(float).eps
output_log = numpy.clip(
numpy.log2(in_od / in_middle_grey),
minimum_ev,
maximum_ev
)
So while logs indeed cannot represent true zero, a normalized log is a slightly different beast I believe, and this normalized log is the form in OpenColorIO.
Alrrright I got it thanks ! So I should not clip post-log cause those negatives are not a mistake.
Post log I think makes sense? Let me outline:
Any negatives in the log, after the open domain clip, suggest that the values are within BT.709 but beyond the floor of the lower log2 range that forms the clay of the picture.
In the normalized log2, the 0.0 mark is the -log2
floor, and the 1.0 mark is the +log2
ceiling we choose.
Assuming the log2 normalization is correct in fact, you can clip zero to one here, and it should be valid!
Post log I think makes sense ?
That what I though first, then your explanation made me though of the opposite. And there is a few point that validate it :
Then also the log normalize everything as you mentioned. But it normalize everything that is in the range you gave it. If the input data has a maximum value higher than the maximum value of the log range, then it got above 1 and clipping result would result in loss of data.
Here is an example in Nuke :
But anyway my goal was not to clip positives values above 1, only negatives. But if positives above 1 are still useful data, maybe negatives are also ?
Not gonna lie I'm kind of lost here. Something tells me that it makes sense to just clip negatives after the log transform, but as mentioned before, I have other arguments that make me think that this is not the approach to take.
and here you are clipping negatives under -7, so there is still negatives.
There are no negatives in the pre-log. And none post log.
The source at BT.709 depends heavily on all values being zero or greater, otherwise the inset technique will not work; the inset footprint must be “clean”.
your AgX implementation on v2 actually produce negatives like when I don't use the post-clip, so it again make sense to remove it.
The inset will increase the purity of some values based on the curve. That is, the footprint has values that expand beyond it. When the outset puts the chromaticities back at the origin tristimulus, those values become negative.
If we try to order the operations, using regular language:
So we can see a few places where the relative domains will result in negatives.
Thanks for taking the time to put out a detailed explanation, I think I got it.
I see you are talking about outset and display since few messages and I feel like I was maybe not clear enough. Until now I was focused only on the AgX Log
"colourspace", that only includes the inset+log2 transform. That is the only thing that was problematic here.
Also I don't see the log as only an intermediate space for display, where if I understand well, indeed log limit give the maximum that reach display.
But what about when the artist simply want to use it as a grading space ? I.e. workspace -> AgX Log, grading, AgX Log -> workspace
. In that case we doesn't want to clip the data to the log limits yet.
your AgX implementation on v2 actually produce negatives like when I don't use the post-clip, so it again make sense to remove it.
Again I should have been more specific. Here I was talking about the AgX Log
. Here is your original config used in Nuke, where I'm only applying a scene_linear -> AgX Log
"colorspace" transform.
As you can see it spit out negatives on everything that was pure black initially.
Does those negatives matters ? I don't know, they will not make it out to the display anyway as you mentioned.
Deployed the new version 0.4.2 that adress the negative issue pre-log. It has been tested on various software and seems to works fine. The log "colorspace" transform still produces negatives in the output, and I still don't know if I should keep them (even though thanks Troy for all the explanations), but this seems to work fine and make the behaviour similar to the v2 config.
Following #9, I discovered that the ocio config is not handling negative properly in the
AgX Log
transform.Initially I was using this kind of combinaison of transform to create a negative clamp :
The dummy LUT clipping to 0-1 range, but this actually doesn't work and introduce more negatives because of the
<AllocationTransform>
.As such this has been removed in #10 .
Objective
Find a way to clamp negative values before the Matrix + Log2 transform are applied
Comparison
↳ img1 current behavior
↳ img2 original AgX on OCIOv2
↳ img3 reference image (unprocessed) - negatives mask
You can see some hard clipping on the small ColorsBars that initially have a lot of negatives.