Open sobotka opened 8 years ago
Hi sobotka,
First off, I have to say that the cube format is the one I work with most, and which LUTCalc was initially designed around. As I have learned about and written parsers for other formats, I have also added builders, which I then test out (although as you found with spi3d, I have been known to change things which cause breakages ;-) ), but .cube is the most mature format in there, so the one I'm going to use as an example.
There are at least two different and incompatible ways to express input values in .cube files, one used by Adobe/Lumetri, the other by Resolve. As each bit of software has a hissy fit if you use the wrong approach, I have set the default 'General Cube LUT' to not include input scaling. If you choose 'DaVinci Resolve (.cube)' or 'Lumetri/Speedgrade (.cube)', you should have an 'input scaling' box come up. For now it's a matter of 'suck it and see' between those two for other bits of software (Color Finale takes the Lumetri approach in it's preset).
If you are working with scene linear floating point values, make sure that 'Input Range' is set to 'Legal' (ie 0.0=0%, 0.18=18% and 1.0=100%) then set the min and max inputs you are after, eg min 0, max 16.3 in the example you gave, or min -1, max 16.3 if you want to go into the superblacks.
If you set the output gamma to Scene Linear Reflectance, then make sure that the output range is also set to 'legal' and generate a 1D cube LUT, you should end up with a file which smoothly and linearly progresses from 0.0 to 1.0 (if you are using the 'general' preset), or from the minimum to the maximum values if you are using a preset which offers input scaling. I've just tested that myself to be sure.
For floating point linear values as input, I would expect that 'input range' should always be set to 'legal'. What the output range should be is dependent upon the output gamma/tone curve and choice of software.
Testing out on various bits of hardware and software, I have come to realise that (when it comes to LUTs) different manufacturers seem to take different interpretations for what legal / full / data / extended means for input and output values.
I've been made aware that my interpretation (legal - floating point 0 = 0%, floating point 1.0 = 100%, data - floating point 0 = -7%, floating point 1.0 = 109% whether on input or output) is not the same as can be found in other software. My feeling is that I am consistent with it, it is the approach that I have taken in explaining LUTCalc usage and I feel it is logical and makes sense to me.
With that in mind, Resolve, Lumetri and Color Finale all seem to have differing expectations of input and output ranges to LUTs, so over time I have added presets that hopefully give consistent results.
I do notice that setting input gamma to scene linear does not automatically change the range to 'legal' in the .cube presets. I'll have a check through as to what is best to do about that - my aim is that LUTCalc be useful to anyone, so I'm very keen to not cause issues for people who dabble in this stuff in order to simplify things for a more advanced workflow. I'll aim to fix the logic so that linear input goes to legal range, but not as quickly as with the spi3d fix!
Please do let me know whether or not this makes any sense or helps, and if not feel free to give me a bit more specific detail so I can hopefully get to the bottom of it.
Thanks,
Ben
Greets Ben.
Trying to use it with OpenColorIO, which is a key part of Nuke and other software, including the whole ACES toolchain.
A few comments on the UI:
Hi sobotka,
'Linear / Rec709' is the umbrella for all conventional gammas (linear being effectively gamma 1.0). I take your point and I did originally have it laid out differently, but LUTCalc has grown out of being a tool for camera people to use with cameras like the F5/F55/FS7, so I'm conscious of keeping simpler workflows clear at the front and not radically changing things that people are used to.
For the purposes of LUTCalc, all colour processing is separated out from tone curve or gamma processing. Linear/Rec709 is the title of those options because it is where all the simple power curve options are, with linear being a power curve exponent of 1.0.
Colour processing is carried out in linear space - for colours which would originally have been matrices applied to tone curves (eg LC709A) I currently store them as colour-only LUTs. I'm currently working on tone-mapped matrix colourspaces in the hope of reducing the size and improving flexibility, but the basis of the tool is that any colour space / gamut can be consistently paired with any tone curve / gamma.
I have to be honest and say that I haven't come across any LUT formats which include inversions. I am basically a cameraman building a tool for my use but making it available to others, so I add stuff as I learn about it, and learn about it when someone points me at something that piques my interest ;-).
Inverting 3D LUTs is a bit of a minefield (there is no expectation or likelihood of them being monotonic all directions), but the 1D reversal code is pretty robust, particularly with the code I've uploaded today. The Canon CP lock output gamuts are an inversion of the Canon ACES transform using multivariate Newton-Raphson and Broydon's (I have spent FAR to long playing with them), and not to put too fine a point on it they are the part of LUTCalc I'm least happy with!
If you read in a LUT with the LUTAnalyst tool - whether 1D or 3D - the reverse of the 1D tone/gamma component should be available in the 'Rec Gamma' box.
Thanks,
Ben
All 1D LUTs and matrices are strictly invertible. 3D LUTs are not simply due to the many to one result collisions.
If it interests you, I would encourage investigating OpenColorIO. It has a very solid architecture for transforms, and it might interest you with regard to your tool here.
Hi sobotka,
I am aware of OpenColorIO; in fact I have used their parser code for various lut formats as a source when I have been checking that my own code is correct (even if I missed that spi3d bug you spotted!). It is surprising how poorly documented most LUT formats are, and often with conflicting variations out there (such as the input scaling in .cube files), so for that OpenColorIO has been a really useful way to confirm things where I don't have access to the relevent software (eg Nuke).
In regard to 1D LUTs I'm afraid that's simply not true. It is the case that gamma curves, log curves and the basic Rec709 based tone curves are all monotonic and therefore invertible, but it is eminently possible to create valid 1D LUTs which bounce up and down in value. It may not be useful, but it is eminently possible. As I mentioned before, the reversal code in LUTCalc is pretty robust with regard to that; no problem with monotonic curves, and will make a pretty good stab at keeping things sensible if a 1D LUT dips back on itself.
OpenColorIO may well specify that all 1D LUTs be invertible; that is not to say that all 1D LUTs are invertible. :-)
As for 3D LUTs, yes if the LUT is based on a matrix operation performed in linear space (regardless of the input and output tone curves) inversion is pretty straightforward (almost all the recorded gamut options in LUTCalc are generated on the fly at startup from white point and primaries creating matrices to an internal working space, with the output versions just being matrix inversions), but the point of LUTAnalyst is to read arbitrary 1D and 3D LUTs, and in that case the potential for multiple roots at arbitrary positions makes accurate inversion the exception rather than the rule.
I can make code to 'fill in the gaps' or make guesstimates where there are multiple roots, but then I would not be comfortable in telling people to trust the reliability of that approach across a mesh.
I'm currently working on analysing LUTs which are the result of a input tone curve -> linear space -> matrix -> arbitrary (though monotonic) tone mapping -> 2nd matrix -> gamma correction which is significantly more complex to disentangle if you do not initially know the matrix values or tone curves (particularly if values are clamped at each tone mapping). If there is a nifty way of doing that I really would be all ears! ;-)
An example of an inversion horror show is the C300mkI IDT ACES code. There are two versions; one for tungsten, one for daylight.
For most of the manufacturer-supplied ACES IDTs the process goes: log curve to scene referred linear (with 18% gray = 0.18), matrix to ACES colourspace. I've seen both Bradford and CIECAT2000 used for generating that matrix, but ACES itself seems to lean toward Bradford.
Anyway, back to the Canon. The C300mkI is an 8-bit camera; it shoots C-Log to cover a decent range of stops reasonably well, but the codec would be pushing picture quality with a wide gamut such as Canon Cinema Gamut. Instead, CP lock shoots a colourspace based on Rec709, but tweaked to give a Canon 'look' after the C-Log has been (1D) tonemapped to something pretty. Internally, the colourspace may well be a matrix applied to tonemapped data, or may be something else. I just don't know. The only tangible info I have been able to find is the inversion of it to a more clear-cut space, the Canon-supplied ACES IDT.
The IDT there goes log curve to scene referred, trivariate cubic equation expressed as a 19x3 matrix, 3x3 matrix to ACES colourspace.
Now I've had a pretty good stab at inverting that cubic equation using numerical methods. Since it is there to invert what is done in camera, it should be a pretty clean inversion. The results I have managed are consistently close but no cigar. I may have missed something obvious but my conclusion is that the cubic volume is an approximate inversion of what is done in camera, close enough to work with and to keep the IDT 'simple', but not a mathematically exact inversion.
With the 10-bit C300mkII there is Canon Cinema Gamut and it is a nice, simple linearisation->matrix operation, like all the other grown-up IDTs. ;-)
My day job is as a lighting cameraman, and I work on LUTCalc when I have the time and it does not interfere too much with my wife and daughter. It now does pretty much everything that I need it to do, so additions come either when I get a bee in my bonnet about something, or when people ask me to add something interesting and useful. I continue to spend rather more time on it than my wife is happy about!
If there is anything within the OpenColorIO code you think might be useful in this, please do point me in the right direction, but I would struggle to find the time to go through studying all the code in it.
Thanks,
Ben
Just thought,
a monochrome matrix is not invertible!
;-)
Ben
True! Which I probably should have been more clear about given I just bumped into that via an error recently. Bad on me.
I was thinking that in terms of your code, OCIO would be a pretty good foundation for all of the transforms.
hpd's official ACES configurations are excellent (in addition to the CTL transforms in the other repository) as they outline the Python functions for generation. https://github.com/hpd/OpenColorIO-Configs/tree/master/aces_1.0.1
Between the CTL, OCIO and such, it might be a more robust backbone for LUT creation, or at least provide a reasonable path for future development.
Very good points - and actually I have used that ACES data along with that on the main ACES repo as 'Rosetta Stones' for confirming things like CAT/CAM choices and log/linear conversions where documentation is limited.
Unless I'm mistaken, all the log curves in LUTCalc are derived from fundamental documentation (there are links to most of them on the lutcalc.net site), along with all the basic gamma and wide-DR tone curves (such as Hybrid-log, PQ and the earlier BBC283). I have then use the OCIO luts and ACES IDTs along with other sources as sanity checks for the values I get.
On the gamma / tone curve side, I've built up a pretty consistent, flexible and robust system for adding new options, whether as power curves, parameter-based log curves or multi-part cubic splines. I can also easily add less standard approaches (such as the variable parameters and sometime knee of LogC as the ISO changes). The spline code includes reversability, though I have deliberately included the ability to stop the reverse option where I think it's going to do more harm than good.
An example of what I mean by that is that on Sony cameras you can shoot hypergammas in custom mode, but people with that workflow will generally also tweak other settings, particularly in my experience the master black and black gamma (that's certainly what I do!). Do any tweaking like that and try to reverse it with a 1D LUT derived from the basic hypergamma and you are likely to get unexpected results. I'd rather not have people thinking it an issue with LUTCalc!
I have now included the reverse of Rec709(800%), as I've come across a number of people accidentally baking in the built-in LUT in the middle of log material, so since in that instance the curve should be consistent, so being able to have messed-up material fit in with the generally log rushes seems like a sensible idea.
It is also possible to reverse any of the built-in curves by generating a LUT and then re-reading it with LUTAnalyst. Fiddly I know, but there if really needed.
On the colour side, LUTCalc has a very robust implementation of basic 'white point and primaries' colour spaces. LUTCalc generates the conversion matrices to and from a 'working' colourspace (currently S-Gamut3.cine). All processing is done with 64-bit floats and parameters such as luma coefficients for a given colourspace are calculated for consistency, so the choice of working colourspace should have no consequence in terms of data loss, but it should be possible to change it to any of the base colourspace options.
I've also included a lut-based colourspace option. This stores 3D LUTs (to date 65x65x65) which are S-Log3 in to S-Log3 out and encode just the colour adjustment element of LUTs that I have analysed. I use log in to log out because LUTs work best with evenly spread data, and log gives an even spread stop to stop. The lut-based colourspaces get fed linear data, which is then converted to S-Log3, fed into the LUT and the output converted back to linear. This has allowed me to relatively easily include colourspaces like Sony's LC709 Type A and LC709.
I have just been adding the option of tonecurve-based matrix colourspaces. This entails a linear space matrix followed by a tonecurve, followed by a second matrix followed by the reverse of the tonecurve. This should allow me to include colourspaces such as the Alexa709 in a compact, accurate form yet still keeping the separation of colour space adjustment and tonemap / gamma choice. This approach is by definition reversible. I just have to figure out some useful tonecurves and matrix parameters! If I can crack things like LC709 and LC709A then LUTCalc would get orders of magnitude smaller.
I have to say that binning all the LUTCalc code and replacing it with OpenColorIO would mean that it isn't LUTCalc any more, and to be honest aside from the use of 'filesaver.js' and 'blob.js' in the online version there isn't a line of code that I haven't figured out for myself.
There are other things that I am thinking of adding, and I feel that the way I have it laid out now makes that fairly painless to me. An example would be the addition of matrix or whitepoint and primary-based custom colourspaces which I added in a few hours after reading a thread on LiftGammaGain that discussed the usefulness of being able to enter matrices in Resolve. I could do it pretty quickly because the calculation base was already there and just needed a GUI.
There is a lot of (as far as I'm aware) robust colour science in LUTCalc. There is plenty that I can add or tweak; a number of times I have mulled the possibilty / suitability of using different CAT/CAMs for different colour spaces where the manufacturers themselves make different choices - something which as far as I can see ACES itself skips over.
To be honest I'm not inclined to throw that out and start again! I do, however, spend a lot of time learning from whatever robust sources I can find; I enjoy the challenge of figuring stuff out for myself, but have no desire to reinvent the wheel! I do meant it when I say that if there is some 3D LUT inversion code or techniques that work better than Newton-Raphson (for calculable Jacobians) or Broyden's (for numerical solutions) you could point me to, yes please! ;-)
Thanks,
Be
Hi,
Subscribing to the thread for interest. Great to see colour nerds around. Congratulations on the tool @cameramanben! Looks like you have poured quite a few hours into it.
I'm quite in line with Troy (@sobotka) regarding the Linear option, it is actually the first thing I tried to find :)
I've seen both Bradford and CIECAT2000 used for generating that matrix, but ACES itself seems to lean toward Bradford.
It is something we were meant to document with hpd on the OpenColorIO configurations, I'm not sure where that is left at. I'll have to dig.
Sadly this is a [Citation Needed] moment, but alas, I can't find it.
I recall reading somewhere that of all the CA transforms, including the most recent CAT02 and Sharp, in terms of CA for image manipulation, Bradford fares the most reliably and robustly.
Will keep hunting for it...
Bianco (2010) proposes 2 CAT computed by numerical optimisation that perform a bit better than the regular CAT (Bradford, CAT02, etc...).
Bianco, S., & Schettini, R. (2010). Two New von Kries Based Chromatic Adaptation Transforms Found by Numerical Optimization. Color Research & Application, 35(3), 184–192. doi:10.1002/col.20573
Thanks for this!
Oh no, a new rabbit hole to dive down :-)
I have considered a few times adding the option for the user to set the base CAT used by LUTCalc internally. LUTCalc grew out of my buying a Sony camera, and the use of CIECAT02 (oops, didn't mean CIECAT2000) came from me noticing that Sony's published matrices fitted with that CAT.
With that in mind, one thing I would be interested in hearing thoughts on is: if one manufacturer tends to use one CAT in their documentation, and another uses a different one, is it better to be consistent across the board to a working colourspace, or to use the manufacturer's preferred one on a case-by case basis, with the potential for different CATs being used into the working space and then out to the destination space.
I could set LUTCalc to take either approach, but up to now I have had one CAT handle all transforms (custom colour space aside).
Ben
CAT02 is the chromatic adaptation transform of CIECAM02 colour appearance model and is the one recommended by CIE. Now it is not without problem as some colour samples can yield negative tristimulus values with CAT02.
I haven't quantified that precisely but I'm tempted to say that which CAT you adopt (Bradford or CAT02) doesn't matters much for practical application.
I would try to be consistent and always use the same to avoid round-trip surprises.
Any plan for that Linear input option? :D
I'm with Thomas on the consistency, but if you are trying to match existing references from say ARRI you might want CAT02, but matching Photoshop - "Bradford". there have been modifications to CAT02 by Brill and Süsstrunk.
On a related note if you are interested in the whole of CIECAM02 there have been similar robustness modifications by Kuo, Chunghui; Zeise, Eric; Lai, Di for use with ICC profiles, although the copy of the paper I had had a couple of typos in it!
Kevin
Hi All,
thanks for the tips, got to figure out how to get access to those articles. Glad that the consensus seems to lean towards consistency - that has been my feeling since putting in the colourspace generation code.
CIECAT02 was originally hardwired in, then when I added the custom colourspace option (it appears when you select 'custom' in the Rec Gamut or Out Gamut boxes), I also added in matrix values for Bradford, CIECAT97s, original Von Kries, 'Sharp', CMCCAT2000 and straight XYZ Scaling. These can be selected for the custom colour space matrix calculation.
It's easy to add Von Kries-style matrices, so I put in the ones that seemed to crop up in several places. I haven't gone so far as to include nonlinear corrections like the full Bradford - baby steps for me and after all, it is Javascript!
I had an update in with Apple's App review just before this thread got going; if you replace a build you go to the back of the queue, but for the next one I've added a specific 'Linear / γ' category, along with changing the 'Linear / Rec709' option to 'Linear / γ'. Processing-wise, the linear options (reflectance or IRE), Rec709, sRGB and all the straight (non-piecewise) gammas go through the same functions. Linear is treated as a gamma of 1.0. Hopefully that should make things a bit clearer without being a scary change to those already used to my UI!
I've been mulling adding in an adjustment option that would appear when any of the tone-curvy transfer options are selected (ie LC709, V709 and the Hypergammas), which would offer to change the destination display gamma, ie assume them to incorporate a Rec709 gamma correction by default, but allow the user to replace that with say DCI or sRGB. Easy enough to add - at the end of the processing chain degamma and then regamma with something else. The options would be the Linear / γ list and maybe the HDR choices.
I thought that might be useful for quickly generating LUT versions of the same look but targeting different screen types. Does that sound like good idea?
Found the Bianco one.
We have quite a few if needed: https://github.com/colour-science/colour/blob/develop/colour/adaptation/dataset/cat.py
You are maybe missing CMCCAT2000
and CAT02_BRILL_CAT
from there.
Does that sound like good idea?
Providing I can specify linear easily on either ends, I'm fine with anything really :)
Something is up with your scene linear value ranges.
I've tried many times to make them work but it isn't working. For example, the shaper LMT LUT that ships with ACES generates scene linear ranges about six and a half stops above a middle grey anchored at 0.18ish. These values end up at around 16.3 or so scene linear.
Your LUT generator doesn't seem to have a means to indicate that the values are scene linear for an input, and even a strictly scene linear to scene linear passthrough generates odd values.
Is there a clear method to generate scene linear values with direction?