RGB to HSL and back, calculation problems

后端 未结 3 711
半阙折子戏
半阙折子戏 2020-12-09 21:40

I\'m trying to convert RGB to HSL and I also want to convert from HSL to RGB, I have written a class for it but if I do RGB->HSL->RGB to try if it works I get a different va

相关标签:
3条回答
  • 2020-12-09 21:46

    Besides the precision issues I think your actual algorithm is incorrect. This should be your FromRGB:

        public static HSLColor FromRGB(Byte R, Byte G, Byte B)
        {
            float _R = (R / 255f);
            float _G = (G / 255f);
            float _B = (B / 255f);
    
            float _Min = Math.Min(Math.Min(_R, _G), _B);
            float _Max = Math.Max(Math.Max(_R, _G), _B);
            float _Delta = _Max - _Min;
    
            float H = 0;
            float S = 0;
            float L = (float)((_Max + _Min) / 2.0f);
    
            if (_Delta != 0)
            {
                if (L < 0.5f)
                {
                    S = (float)(_Delta / (_Max + _Min));
                }
                else
                {
                    S = (float)(_Delta / (2.0f - _Max - _Min));
                }
    
    
                if (_R == _Max)
                {
                    H = (_G - _B) / _Delta;
                }
                else if (_G == _Max)
                {
                    H = 2f + (_B - _R) / _Delta;
                }
                else if (_B == _Max)
                {
                    H = 4f + (_R - _G) / _Delta;
                }
            }
    
            return new HSLColor(H, S, L);
        }
    

    The next thing you need to understand is that we're taking integer RGB values from 0 to 255 and converting them to decimal values from 0 to 1. The HSL that we get back will thus need to be converted to the normal degree/percent/percent that you're used to. The H value returned should be from 0 to 6 so to convert it to degrees you just multiply by 60. H can actually be negative sometimes so if it is just add 360;

                //Convert to degrees
                H = H * 60f;
                if (H < 0) H += 360;
    

    S and L also need to be multiplied by 100 to give you a percentage from 0 to 100.

    UPDATE

    This code should get you from HSL to RGB. It assumes that the HSL values are still in their decimal format. Also, I used double instead of float in the code below for better precision.

        public Color ToRGB()
        {
            byte r, g, b;
            if (Saturation == 0)
            {
                r = (byte)Math.Round(Luminosity * 255d);
                g = (byte)Math.Round(Luminosity * 255d);
                b = (byte)Math.Round(Luminosity * 255d);
            }
            else
            {
                double t1, t2;
                double th = Hue / 6.0d;
    
                if (Luminosity < 0.5d)
                {
                    t2 = Luminosity * (1d + Saturation);
                }
                else
                {
                    t2 = (Luminosity + Saturation) - (Luminosity * Saturation);
                }
                t1 = 2d * Luminosity - t2;
    
                double tr, tg, tb;
                tr = th + (1.0d / 3.0d);
                tg = th;
                tb = th - (1.0d / 3.0d);
    
                tr = ColorCalc(tr, t1, t2);
                tg = ColorCalc(tg, t1, t2);
                tb = ColorCalc(tb, t1, t2);
                r = (byte)Math.Round(tr * 255d);
                g = (byte)Math.Round(tg * 255d);
                b = (byte)Math.Round(tb * 255d);
            }
            return Color.FromArgb(r, g, b);
        }
        private static double ColorCalc(double c, double t1, double t2)
        {
    
            if (c < 0) c += 1d;
            if (c > 1) c -= 1d;
            if (6.0d * c < 1.0d) return t1 + (t2 - t1) * 6.0d * c;
            if (2.0d * c < 1.0d) return t2;
            if (3.0d * c < 2.0d) return t1 + (t2 - t1) * (2.0d / 3.0d - c) * 6.0d;
            return t1;
        }
    
    0 讨论(0)
  • 2020-12-09 21:59

    The problem I see in your code is the following:

    float _R = (R / 255);
    

    You are basically doing integer division here, so you are losing tons of precision.

    Try changing it to:

    float _R = (R / 255f);
    

    (and the same for the other 2 lines).

    Also, to increase precision even more, better to use double instead of float.

    0 讨论(0)
  • 2020-12-09 22:03

    Common bug. You've got

        public static HSLColor FromRGB(Byte R, Byte G, Byte B)
        {
            float _R = (R / 255);
            float _G = (G / 255);
            float _B = (B / 255);
    

    Tell me precisely what values of R can result in _R not being 0. (Hint: there's only one).

    Edit: you've got the same problem in ToRGB() with 1/3.

    0 讨论(0)
提交回复
热议问题