Open amyspark opened 2 years ago
UPD: I worked around it by replacing the parametric curve by a tabulated version, e.g.
I believe this is definitely a bug, though not sure where inside LCMS.
A quite complicated code, I see. May I ask for a small snippet that demonstrates the issue? I mean, a number across a parametric curve type 4 that results in something not good. Thanks
I mean, a number across a parametric curve type 4 that results in something not good.
Based on my workaround, this issue seems to happen only in conjunction with a complete pipeline. I tried keeping only the gamma-RGB-XYZ part and the result's gamut is correct, though.
@mm2 I generated two separate profiles; one with the parametric curve swapped for the tabulated version, the other without. I sampled both pipelines with a 24x24x24 CLUT on both 16bit and floating point.
The first diverging point I found was:
(lldb) fr s 9
frame #9: 0x0000000100003c1e testClut`sample(In=0x00007ffeefbff160, Out=0x00007ffeefbfef60, cargo=0x00007ffeefbff1f0) at test.cpp:19:9
16 cmsPipelineEvalFloat(In, test, x->b);
17
18 if (Out[0] != test[0] || Out[1] != test[1] || Out[2] != test[2])
-> 19 throw std::runtime_error("FAIL!");
20
21 return TRUE;
22 }
(lldb) fr v
(const cmsFloat32Number *) In = 0x00007ffeefbff160
(cmsFloat32Number *) Out = 0x00007ffeefbfef60
(void *) cargo = 0x00007ffeefbff1f0
(pipelines *) x = 0x00007ffeefbff1f0
(cmsFloat32Number [3]) test = ([0] = 0.111345083, [1] = 0.222674906, [2] = 0.0371099412)
(lldb) parray 3 In
(const cmsFloat32Number *) $0 = 0x00007ffeefbff160 {
(const cmsFloat32Number) [0] = 0
(const cmsFloat32Number) [1] = 0
(const cmsFloat32Number) [2] = 0
}
(lldb) parray 3 Out
(cmsFloat32Number *) $1 = 0x00007ffeefbfef60 {
(cmsFloat32Number) [0] = 0.00772106508
(cmsFloat32Number) [1] = 0.17367819
(cmsFloat32Number) [2] = 0
}
YCbCr [0, 0, 0]
is mapped to XYZ [0.00772106508, 0.17367819, 0]
but XYZ [0.111345083, 0.222674906, 0.0371099412]
in the CLUT version.
For 16-bit, the first diverging point is also [0, 0, 0]:
(cmsUInt16Number [3]) test = ([0] = 7297, [1] = 14593, [2] = 2432)
(lldb) parray 3 In
(const cmsUInt16Number *) $0 = 0x00007ffeefbff180 {
(const cmsUInt16Number) [0] = 0
(const cmsUInt16Number) [1] = 0
(const cmsUInt16Number) [2] = 0
}
(lldb) parray 3 Out
(cmsUInt16Number *) $1 = 0x00007ffeefbff080 {
(cmsUInt16Number) [0] = 506
(cmsUInt16Number) [1] = 11382
(cmsUInt16Number) [2] = 0
}
Let me know if you need the complete profile generation code for each instance.
Not sure about which code are debugging. Anyway if you compare 3D LUT interpolation versus a math expression, the results hardly depends on how you build the LUT. Keep in mind LUT's are interpolated linearly (tetrahedral or trilinear) and that means the indexing space should be as much linear as possible and the domain should be as wide as possible. Linear because the output will be linearly interpolated inside the cube of near nodes, and wide domain because you need to use as many nodes as possible. This is the reason of pre and post linearization tables, to provide linear space for 3D LUT.
Example: a function f(x) = 2x -3 works fine in a 3D LUT, a function f(x) = 2x^2 does not because is is not linear, but using a linearization table t(x) = x^2 things got simplified,
Not further comments so I close the issue
Hi @mm2, I did not comment further because I don't know how else to demonstrate that there is a bug with how LittleCMS processes parametric curves as part of V2 pipelines. I already attached how to generate a profile demonstrating the bug, and how to work around it.
OK let's reopen it
Hi again,
I'm writing some code to generate an YCbCr ICC profile. For V2, in the YCbCr -> XYZ direction, I need to pack the full transformation in the LUT, as follows:
Demo
```cpp #includeHowever, doing so completely breaks the gamut of the profile as viewed in Krita's Color Space Selector. I've narrowed it down to the type 4 parametric curve I use for the linearization step after the YCbCr conversion; if I replace it by another type, e.g.
cmsBuildGamma(ctx, 2.2)
, the problem disappears. This also doesn't occur in V4 profiles, where this step can be stored explicitly in the M curves of the AtoB0 tag.@mm2 I'm not sure what is going on here, would you take a look?