-
-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance #18
Comments
Thanks for sharing your findings and code! I agree that I have not designed the library with speed in mind, but correctness and readability. To be honest, doing all the math, and even storage, in Going from RGB to LAB passes through XYZ, which passes through LinearRGB. There is an implementation of FastLinearRGB in go-colorful, but one has to go through that explicitly oneself, that could be done via something like Doing everything in What would it take to close this issue, besides a full rewrite of the library? I guess we can also leave it open forever. 😄 Of course, if you find a low-hanging optimization opportunity, I'd happily accept a PR. |
Here are some more benchmarks. Note that RgbInt (same as Rgb last time) is slower here due to defeating a compiler optimisation, but it's still much faster than Lab.
RgbFloat is DistanceRgb(). I think for my usecase, I do need a rewrite of the library. Question is, how to do it in int-space. |
Uhm, In integer-space, either use a fixed-point library, or use uint32 for high precision and good speed. I believe most conversions in the library only use simple Another opportunity for speed-up would be vectorization and use of SIMD instructions. Especially if you're interested in mainly one use-case in a tight loop, I feel like it should be possible to implement it efficiently in SIMD. I'm curious what you will come up with 😄 |
So as an experiment, I changed
So I think the question is "How inaccurate is a gamma of 2.0?", because that's about a 3x speedup by avoiding three calls to Edit: I actually plotted the 2.0 gamma curve. It's ~15% off in mean-absolute error compared to your 10%, but actually more accurate than your approximation in mean-squared error. Go figure. |
This is following the discussion in #18. Documentation and a notebook for deriving the constants will follow.
See the related pull-request. I'm going to bed now and will finish documentation/derivation during the week-end before merging it. Please have a look and let me know what you think. |
This is following the discussion in #18. Documentation and a notebook for deriving the constants will follow.
Merged! I'm closing this issue now as it's quite some improvement without the need to dramatically change the library, but if there's more, feel free to open re-open! |
Yes, for special cases, I think approximating the whole transform is the way to go. I think you could use the notebook relatively easily for that too. If you come up with a much faster, but still pretty good, approximation, I will happily accept a PR. Both of those pictures are beautiful, thank you for sharing them here! |
Hi guys just chipping in. What is the proportion of the time taken to convert Xyz to Lab before doing the actual difference between 2 Lab colors? The way stdlib implements its colors is that there is a struct per color space. Here everything is stored in Xyz then converted back to the proper color space every time. If we had a struct But it involves a rewrite of the lib and some thinking... @ZirconiumX If you're looking into absolute performance, I suggest you write some custom code for your needs. |
I see where you're going with different types for the color spaces. It is an interesting suggestion and the resulting library could be quite interesting actually! Though that's too much work for me now, and even more work to do in a backwards-compatible way 😄 Currently, it's up to you not to convert the colors every single time if you can avoid it. You can use the library to convert your colors to Lab, store the |
Well, the issue with said custom code is it's a massive eyesore. Here's my code for a 2nd order Taylor series from linear RGB to L*a*b*. I didn't feel capable of making the 4th order Taylor series readable. // Convert from linear RGB to CIE L*a*b* colour space via a 2nd order Taylor series.
// It looks horrific.
func (c Color) fastLab() (l, a, b float64) {
r1, g1, b1 := c.R - 0.5, c.G - 0.5, c.B - 0.5
r2, g2, b2 := r1*r1, g1*g1, b1*b1
l = 0.08353953840061758*b2 - (0.1488331284221014*c.B - 0.0744165642110507)*g1 +
0.1648123392330386*g2 + (-0.0442601911000214*c.B - 0.438555913233124*c.G + 0.24140805216657268)*r1 +
0.2030808998232748*r2 +
0.0721895235504632*c.B + 0.7152961078498866*c.G + 0.2127156955053038*c.R +
0.03378900708797039
a = 0.764133475692812*b2 - (0.7899279687584632*c.B - 0.3949639843792316)*g1 +
1.064211606797602*g2 + (-1.02100612767776*c.B - 1.720893065989955*c.G + 1.3709495968338574)*r1 +
1.055288756843222*r2 +
0.7844013275154139*c.B + 1.061153953700172*c.G + 1.751917661948025*c.R +
0.6970103827379495
b = 0.139473739305493*b2 + (0.1921736638486771*c.B - 0.09608683192433855)*g1 +
0.08734031911725595*g2 + (-0.003514546282512035*c.B - 0.7470004222339538*c.G + 0.3752574842582329)*r1 +
0.3143891636818394*r2 -
1.297608304391595*c.B + 1.054905483418373*c.G + 0.3378191791989842*c.R +
0.01842879440624068
// If you still have eyes at this point, congratulations!
return
} This fails accuracy tests, so I would not recommend using it. Attached is a Sage Jupyter notebook (SymPy is far too slow for this, but Sage is lightning fast). |
Thanks for sharing, also your notebook! This is how most numerical code looks like by the way 😉 |
Like, bits are close friends of mine (see something like Dorpsgek). But this math? Ugh. I think the 6th order Taylor series might be efficient enough, or you could investigate Pade approximants. In any case, this is very much math beyond my understanding. |
Yes there are definitely faster/better approximations around, I just went with Taylor because I'm used to it and it seemed good enough for this use-case :) |
While I can tell that this code focuses on accuracy, or something like it, it uses
float64
throughout, which is very slow.I wrote a micro-benchmark to demonstrate:
where ColourDiffRgb is this, ColourDiffLab is this and ColourDiffLabNoConversion is calling DistanceLab() between two colorful.Colors without converting from color.NRGBA.
Perhaps either use some approximations, or do math in either float32, or possibly a fixed-point format.
The text was updated successfully, but these errors were encountered: