【问题标题】:Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both在 0-255 范围内将 RGB 转换为 HSV 和 HSV 到 RGB 的算法
【发布时间】:2011-03-02 09:23:24
【问题描述】:

我正在寻找从 RGB 到 HSV 的颜色空间转换器,特别是用于两种颜色空间的 0 到 255 范围。

【问题讨论】:

    标签: c++ c algorithm


    【解决方案1】:

    我已经用了很长时间了——现在不知道它们是从哪里来的......请注意,输入和输出,除了以度为单位的角度外,都在 0 到 1.0 的范围内。

    注意:此代码不会对输入进行真正的完整性检查。谨慎行事!

    typedef struct {
        double r;       // a fraction between 0 and 1
        double g;       // a fraction between 0 and 1
        double b;       // a fraction between 0 and 1
    } rgb;
    
    typedef struct {
        double h;       // angle in degrees
        double s;       // a fraction between 0 and 1
        double v;       // a fraction between 0 and 1
    } hsv;
    
    static hsv   rgb2hsv(rgb in);
    static rgb   hsv2rgb(hsv in);
    
    hsv rgb2hsv(rgb in)
    {
        hsv         out;
        double      min, max, delta;
    
        min = in.r < in.g ? in.r : in.g;
        min = min  < in.b ? min  : in.b;
    
        max = in.r > in.g ? in.r : in.g;
        max = max  > in.b ? max  : in.b;
    
        out.v = max;                                // v
        delta = max - min;
        if (delta < 0.00001)
        {
            out.s = 0;
            out.h = 0; // undefined, maybe nan?
            return out;
        }
        if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
            out.s = (delta / max);                  // s
        } else {
            // if max is 0, then r = g = b = 0              
            // s = 0, h is undefined
            out.s = 0.0;
            out.h = NAN;                            // its now undefined
            return out;
        }
        if( in.r >= max )                           // > is bogus, just keeps compilor happy
            out.h = ( in.g - in.b ) / delta;        // between yellow & magenta
        else
        if( in.g >= max )
            out.h = 2.0 + ( in.b - in.r ) / delta;  // between cyan & yellow
        else
            out.h = 4.0 + ( in.r - in.g ) / delta;  // between magenta & cyan
    
        out.h *= 60.0;                              // degrees
    
        if( out.h < 0.0 )
            out.h += 360.0;
    
        return out;
    }
    
    
    rgb hsv2rgb(hsv in)
    {
        double      hh, p, q, t, ff;
        long        i;
        rgb         out;
    
        if(in.s <= 0.0) {       // < is bogus, just shuts up warnings
            out.r = in.v;
            out.g = in.v;
            out.b = in.v;
            return out;
        }
        hh = in.h;
        if(hh >= 360.0) hh = 0.0;
        hh /= 60.0;
        i = (long)hh;
        ff = hh - i;
        p = in.v * (1.0 - in.s);
        q = in.v * (1.0 - (in.s * ff));
        t = in.v * (1.0 - (in.s * (1.0 - ff)));
    
        switch(i) {
        case 0:
            out.r = in.v;
            out.g = t;
            out.b = p;
            break;
        case 1:
            out.r = q;
            out.g = in.v;
            out.b = p;
            break;
        case 2:
            out.r = p;
            out.g = in.v;
            out.b = t;
            break;
    
        case 3:
            out.r = p;
            out.g = q;
            out.b = in.v;
            break;
        case 4:
            out.r = t;
            out.g = p;
            out.b = in.v;
            break;
        case 5:
        default:
            out.r = in.v;
            out.g = p;
            out.b = q;
            break;
        }
        return out;     
    }
    

    【讨论】:

    • @Stargazer712 如果你做数学,它应该是 ==,但如果你使用它,你可能会收到关于比较浮点数的抱怨。虽然理论上它不可能是 >,但使用 ">=" 而不是 "==" 会关闭我使用 llvm/Xcode 在 Mac 上遇到的编译器错误,
    • @Gerard 以度为单位。 60是一整圈的1/6。这不是弧度。
    • &gt;= 和编译器错误的原因是因为double == double 在大多数编译器中是无效和非法的。浮点运算和浮点存储意味着两个值可以在 近似 值上相等,但在存储值上不相等,即使它们在公式上是相同的。你应该做abs(double_a - double_b) &lt;= epsilon,其中epsilon是一些值,通常是1e-4
    • 对于rgb2hsv, where in.r >= max, 为什么代码不使用mod操作符?不应该将 out.h 计算为“out.h = ((in.g - in.b) / delta) % 6;” ?
    • @JoachimBrandonLeBlanc:“double == double 在大多数编译器中是无效和非法的” 这不是真的。比较两个浮点值是否相等是完全明确的并且合法的事情。没有主流和/或兼容的编译器会阻止您这样做。问题是您可能无法得到您真正想要的答案,并且可能打算进行更松散的比较。
    【解决方案2】:

    你也可以试试这个不带浮点数的代码(更快但不太准确):

    typedef struct RgbColor
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
    } RgbColor;
    
    typedef struct HsvColor
    {
        unsigned char h;
        unsigned char s;
        unsigned char v;
    } HsvColor;
    
    RgbColor HsvToRgb(HsvColor hsv)
    {
        RgbColor rgb;
        unsigned char region, remainder, p, q, t;
    
        if (hsv.s == 0)
        {
            rgb.r = hsv.v;
            rgb.g = hsv.v;
            rgb.b = hsv.v;
            return rgb;
        }
    
        region = hsv.h / 43;
        remainder = (hsv.h - (region * 43)) * 6; 
    
        p = (hsv.v * (255 - hsv.s)) >> 8;
        q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
        t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;
    
        switch (region)
        {
            case 0:
                rgb.r = hsv.v; rgb.g = t; rgb.b = p;
                break;
            case 1:
                rgb.r = q; rgb.g = hsv.v; rgb.b = p;
                break;
            case 2:
                rgb.r = p; rgb.g = hsv.v; rgb.b = t;
                break;
            case 3:
                rgb.r = p; rgb.g = q; rgb.b = hsv.v;
                break;
            case 4:
                rgb.r = t; rgb.g = p; rgb.b = hsv.v;
                break;
            default:
                rgb.r = hsv.v; rgb.g = p; rgb.b = q;
                break;
        }
    
        return rgb;
    }
    
    HsvColor RgbToHsv(RgbColor rgb)
    {
        HsvColor hsv;
        unsigned char rgbMin, rgbMax;
    
        rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
        rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
    
        hsv.v = rgbMax;
        if (hsv.v == 0)
        {
            hsv.h = 0;
            hsv.s = 0;
            return hsv;
        }
    
        hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
        if (hsv.s == 0)
        {
            hsv.h = 0;
            return hsv;
        }
    
        if (rgbMax == rgb.r)
            hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
        else if (rgbMax == rgb.g)
            hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
        else
            hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
    
        return hsv;
    }
    

    请注意,此算法使用0-255 作为其范围(不是0-360),正如本问题作者所要求的那样。

    【讨论】:

    • 您可以将所有 16,777,216 种可能的 RGB 颜色转换为 HSV,然后再转换回 RGB。不幸的是,使用这个算法你会发现有些颜色不能很好地往返。也许它们在感知上看起来大致相同,但在数字上存在很大差异,例如(0, 237, 11) 将往返于 (0, 237, 0) 等。使用基于浮点计算的 David H 算法时,情况并非如此。
    • @rightaway717 - 这给了我完整的范围,也许你使用 0-360 作为范围?这个算法(谢天谢地)使用 0x00 - 0xFF 作为它的范围
    • @AnneQuinn 正确!我预计它是 0-360,但是当我看到接受的答案有效时,我只是没有足够的热情来找出问题所在。我认为 Leszek 应该在答案中提到色调范围,不过还是感谢他发布它。
    【解决方案3】:

    我在 HLSL 中为我们的渲染引擎写了这个,里面没有条件:

        float3  HSV2RGB( float3 _HSV )
        {
            _HSV.x = fmod( 100.0 + _HSV.x, 1.0 );                                       // Ensure [0,1[
    
            float   HueSlice = 6.0 * _HSV.x;                                            // In [0,6[
            float   HueSliceInteger = floor( HueSlice );
            float   HueSliceInterpolant = HueSlice - HueSliceInteger;                   // In [0,1[ for each hue slice
    
            float3  TempRGB = float3(   _HSV.z * (1.0 - _HSV.y),
                                        _HSV.z * (1.0 - _HSV.y * HueSliceInterpolant),
                                        _HSV.z * (1.0 - _HSV.y * (1.0 - HueSliceInterpolant)) );
    
            // The idea here to avoid conditions is to notice that the conversion code can be rewritten:
            //    if      ( var_i == 0 ) { R = V         ; G = TempRGB.z ; B = TempRGB.x }
            //    else if ( var_i == 2 ) { R = TempRGB.x ; G = V         ; B = TempRGB.z }
            //    else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V     }
            // 
            //    else if ( var_i == 1 ) { R = TempRGB.y ; G = V         ; B = TempRGB.x }
            //    else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V     }
            //    else if ( var_i == 5 ) { R = V         ; G = TempRGB.x ; B = TempRGB.y }
            //
            // This shows several things:
            //  . A separation between even and odd slices
            //  . If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then
            //      the operation simply amounts to performing a "rotate right" on the RGB components
            //  . The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices
            //
            float   IsOddSlice = fmod( HueSliceInteger, 2.0 );                          // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
            float   ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice);          // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
    
            float3  ScrollingRGBForEvenSlices = float3( _HSV.z, TempRGB.zx );           // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
            float3  ScrollingRGBForOddSlices = float3( TempRGB.y, _HSV.z, TempRGB.x );  // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
            float3  ScrollingRGB = lerp( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
    
            float   IsNotFirstSlice = saturate( ThreeSliceSelector );                   // 1 if NOT the first slice (true for slices 1 and 2)
            float   IsNotSecondSlice = saturate( ThreeSliceSelector-1.0 );              // 1 if NOT the first or second slice (true only for slice 2)
    
            return  lerp( ScrollingRGB.xyz, lerp( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );    // Make the RGB rotate right depending on final slice index
        }
    

    【讨论】:

    • 你有其他方式转换(RGB2HSV)吗?使用相同的方法?
    【解决方案4】:

    这是一个基于 Agoston 的 Computer Graphics and Geometric Modeling: Implementation and Algorithms 的 C 实现。 304, H ∈ [0, 360] 和 S,V ∈ [0, 1]。

    #include <math.h>
    
    typedef struct {
        double r;       // ∈ [0, 1]
        double g;       // ∈ [0, 1]
        double b;       // ∈ [0, 1]
    } rgb;
    
    typedef struct {
        double h;       // ∈ [0, 360]
        double s;       // ∈ [0, 1]
        double v;       // ∈ [0, 1]
    } hsv;
    
    rgb hsv2rgb(hsv HSV)
    {
        rgb RGB;
        double H = HSV.h, S = HSV.s, V = HSV.v,
                P, Q, T,
                fract;
    
        (H == 360.)?(H = 0.):(H /= 60.);
        fract = H - floor(H);
    
        P = V*(1. - S);
        Q = V*(1. - S*fract);
        T = V*(1. - S*(1. - fract));
    
        if      (0. <= H && H < 1.)
            RGB = (rgb){.r = V, .g = T, .b = P};
        else if (1. <= H && H < 2.)
            RGB = (rgb){.r = Q, .g = V, .b = P};
        else if (2. <= H && H < 3.)
            RGB = (rgb){.r = P, .g = V, .b = T};
        else if (3. <= H && H < 4.)
            RGB = (rgb){.r = P, .g = Q, .b = V};
        else if (4. <= H && H < 5.)
            RGB = (rgb){.r = T, .g = P, .b = V};
        else if (5. <= H && H < 6.)
            RGB = (rgb){.r = V, .g = P, .b = Q};
        else
            RGB = (rgb){.r = 0., .g = 0., .b = 0.};
    
        return RGB;
    }
    

    【讨论】:

    【解决方案5】:

    @fins 的答案在您降低饱和度时在 Arduio 上存在溢出问题。为了防止这种情况发生,这里将一些值转换为 int。

    typedef struct RgbColor
    {
        unsigned char r;
        unsigned char g;
        unsigned char b;
    } RgbColor;
    
    typedef struct HsvColor
    {
        unsigned char h;
        unsigned char s;
        unsigned char v;
    } HsvColor;
    
    RgbColor HsvToRgb(HsvColor hsv)
    {
        RgbColor rgb;
        unsigned char region, p, q, t;
        unsigned int h, s, v, remainder;
    
        if (hsv.s == 0)
        {
            rgb.r = hsv.v;
            rgb.g = hsv.v;
            rgb.b = hsv.v;
            return rgb;
        }
    
        // converting to 16 bit to prevent overflow
        h = hsv.h;
        s = hsv.s;
        v = hsv.v;
    
        region = h / 43;
        remainder = (h - (region * 43)) * 6; 
    
        p = (v * (255 - s)) >> 8;
        q = (v * (255 - ((s * remainder) >> 8))) >> 8;
        t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;
    
        switch (region)
        {
            case 0:
                rgb.r = v;
                rgb.g = t;
                rgb.b = p;
                break;
            case 1:
                rgb.r = q;
                rgb.g = v;
                rgb.b = p;
                break;
            case 2:
                rgb.r = p;
                rgb.g = v;
                rgb.b = t;
                break;
            case 3:
                rgb.r = p;
                rgb.g = q;
                rgb.b = v;
                break;
            case 4:
                rgb.r = t;
                rgb.g = p;
                rgb.b = v;
                break;
            default:
                rgb.r = v;
                rgb.g = p;
                rgb.b = q;
                break;
        }
    
        return rgb;
    }
    
    HsvColor RgbToHsv(RgbColor rgb)
    {
        HsvColor hsv;
        unsigned char rgbMin, rgbMax;
    
        rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
        rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);
    
        hsv.v = rgbMax;
        if (hsv.v == 0)
        {
            hsv.h = 0;
            hsv.s = 0;
            return hsv;
        }
    
        hsv.s = 255 * ((long)(rgbMax - rgbMin)) / hsv.v;
        if (hsv.s == 0)
        {
            hsv.h = 0;
            return hsv;
        }
    
        if (rgbMax == rgb.r)
            hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
        else if (rgbMax == rgb.g)
            hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
        else
            hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);
    
        return hsv;
    }
    

    【讨论】:

      【解决方案6】:

      这应该在这里: 无论如何它都有效。而且和上面几张比起来还不错。

      hlsl 代码

              float3 Hue(float H)
              {
                  half R = abs(H * 6 - 3) - 1;
                  half G = 2 - abs(H * 6 - 2);
                  half B = 2 - abs(H * 6 - 4);
                  return saturate(half3(R,G,B));
              }
      
              half4 HSVtoRGB(in half3 HSV)
              {
                  return half4(((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z,1);
              }
      

      float3 是 16 位精度 vector3 数据类型,即 float3 hue() 返回数据类型 (x,y,z),例如(r,g,b), half 与半精度相同, 8bit, float4 是 (r,g,b,a) 4 个值。

      【讨论】:

      • 需要halfhalf4half3float3 等的一些类型定义。
      • half4 是 color(r,g,b,a) 或任何 4x 半精度浮点数,也可以是全精度,它只是一个向量 4
      • 什么是饱和()?
      • saturate() 在 HLSL 代码参考中可用:saturate(x) 将 x 限制在 0 和 1 之间 docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/…
      • 您能解释一下HSVtoRGB中的return语句吗?它似乎是 Hue 返回的 3 元素 RGB 向量乘以标量 - 得到类似 [kr, kg, k*b, 1]
      【解决方案7】:

      这不是 C,但它确实有效。我在这里看到的所有其他方法都是通过将所有内容封装成六边形的一部分,并从中近似“角度”来工作的。相反,通过使用余弦从不同的方程开始,并求解 h s 和 v,您可以在 hsv 和 rgb 之间获得更好的关系,并且补间变得更平滑(代价是它变得更慢)。

      假设一切都是浮点数。如果 r g 和 b 从 0 到 1,h 从 0 到 2pi,v 从 0 到 4/3,s 从 0 到 2/3。

      以下代码是用 Lua 编写的。它很容易翻译成其他任何东西。

      local hsv do
          hsv         ={}
          local atan2 =math.atan2
          local cos   =math.cos
          local sin   =math.sin
      
          function hsv.fromrgb(r,b,g)
              local c=r+g+b
              if c<1e-4 then
                  return 0,2/3,0
              else
                  local p=2*(b*b+g*g+r*r-g*r-b*g-b*r)^0.5
                  local h=atan2(b-g,(2*r-b-g)/3^0.5)
                  local s=p/(c+p)
                  local v=(c+p)/3
                  return h,s,v
              end
          end
      
          function hsv.torgb(h,s,v)
              local r=v*(1+s*(cos(h)-1))
              local g=v*(1+s*(cos(h-2.09439)-1))
              local b=v*(1+s*(cos(h+2.09439)-1))
              return r,g,b
          end
      
          function hsv.tween(h0,s0,v0,h1,s1,v1,t)
              local dh=(h1-h0+3.14159)%6.28318-3.14159
              local h=h0+t*dh
              local s=s0+t*(s1-s0)
              local v=v0+t*(v1-v0)
              return h,s,v
          end
      end
      

      【讨论】:

      • 你能解释一下这个算法的推导,或者至少指出基本关系吗?我希望找到仅由单个 RGB 分量组成的某些色调 - 但是 hsv.torgb 函数表明这在该算法中是不可能的。维基百科显示了HSV and RGB 之间预期关系的图像
      【解决方案8】:

      基于 Patapoms 答案的 GLSL 着色器版本:

      vec3 HSV2RGB( vec3 hsv )
      {
          hsv.x = mod( 100.0 + hsv.x, 1.0 ); // Ensure [0,1[
          float   HueSlice = 6.0 * hsv.x; // In [0,6[
          float   HueSliceInteger = floor( HueSlice );
          float   HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
          vec3  TempRGB = vec3(   hsv.z * (1.0 - hsv.y), hsv.z * (1.0 - hsv.y * HueSliceInterpolant), hsv.z * (1.0 - hsv.y * (1.0 - HueSliceInterpolant)) );
          float   IsOddSlice = mod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
          float   ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
          vec3  ScrollingRGBForEvenSlices = vec3( hsv.z, TempRGB.zx );           // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
          vec3  ScrollingRGBForOddSlices = vec3( TempRGB.y, hsv.z, TempRGB.x );  // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
          vec3  ScrollingRGB = mix( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
          float   IsNotFirstSlice = clamp( ThreeSliceSelector, 0.0,1.0 );                   // 1 if NOT the first slice (true for slices 1 and 2)
          float   IsNotSecondSlice = clamp( ThreeSliceSelector-1.0, 0.0,1. );              // 1 if NOT the first or second slice (true only for slice 2)
          return  mix( ScrollingRGB.xyz, mix( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );    // Make the RGB rotate right depending on final slice index
      }
      

      【讨论】:

        【解决方案9】:

        我不是 C++ 开发人员,所以我不会提供代码。但我可以提供我目前发现的简单 hsv2rgb 算法 (rgb2hsv here) - 我用描述更新 wiki:HSVHLS。主要改进是我仔细观察 r,g,b 作为色调函数并引入更简单的形状函数来描述它们(不失准确性)。 算法 - 在输入上我们有:h (0-255), s (0-255), v(0-255)

        r = 255*f(5),   g = 255*f(3),   b = 255*f(1)
        

        我们使用如下描述的函数f

        f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)
        

        where (mod 可以返回小数部分;k 是浮点数)

        k = (n+h*360/(255*60)) mod 6;
        

        这里是 JS 中 SO 中的 sn-ps/PoV:HSVHSL

        【讨论】:

        • 你好,卡米尔!我正在尝试使用您的算法,但我对该部分 min(k,4-k,1) 有疑问。为什么会有三个值,这里到底发生了什么?提前致谢!
        • @EugeneAlexeev 我修复了 wiki 中的文章(有人破坏了它) - 并在此处更新链接 - 为了更深入地理解,请阅读 this
        【解决方案10】:

        这是一个online converter,在解释了颜色转换的所有算法之后有一篇文章。

        您可能更喜欢现成的 C 版本,但应用时间不长,它可以帮助其他人尝试用另一种语言或另一种色彩空间做同样的事情。

        【讨论】:

          【解决方案11】:

          这是我今天早上根据与上面几乎相同的数学编写的一个:

          /* math adapted from: http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
           * reasonably optimized for speed, without going crazy */
          void rgb_to_hsv (int r, int g, int b, float *r_h, float *r_s, float *r_v) {
            float rp, gp, bp, cmax, cmin, delta, l;
            int cmaxwhich, cminwhich;
          
            rp = ((float) r) / 255;
            gp = ((float) g) / 255;
            bp = ((float) b) / 255;
          
            //debug ("rgb=%d,%d,%d rgbprime=%f,%f,%f", r, g, b, rp, gp, bp);
          
            cmax = rp;
            cmaxwhich = 0; /* faster comparison afterwards */
            if (gp > cmax) { cmax = gp; cmaxwhich = 1; }
            if (bp > cmax) { cmax = bp; cmaxwhich = 2; }
            cmin = rp;
            cminwhich = 0;
            if (gp < cmin) { cmin = gp; cminwhich = 1; }
            if (bp < cmin) { cmin = bp; cminwhich = 2; }
          
            //debug ("cmin=%f,cmax=%f", cmin, cmax);
            delta = cmax - cmin;
          
            /* HUE */
            if (delta == 0) {
              *r_h = 0;
            } else {
              switch (cmaxwhich) {
                case 0: /* cmax == rp */
                  *r_h = HUE_ANGLE * (fmod ((gp - bp) / delta, 6));
                break;
          
                case 1: /* cmax == gp */
                  *r_h = HUE_ANGLE * (((bp - rp) / delta) + 2);
                break;
          
                case 2: /* cmax == bp */
                  *r_h = HUE_ANGLE * (((rp - gp) / delta) + 4);
                break;
              }
              if (*r_h < 0)
                *r_h += 360;
            }
          
            /* LIGHTNESS/VALUE */
            //l = (cmax + cmin) / 2;
            *r_v = cmax;
          
            /* SATURATION */
            /*if (delta == 0) {
              *r_s = 0;
            } else {
              *r_s = delta / (1 - fabs (1 - (2 * (l - 1))));
            }*/
            if (cmax == 0) {
              *r_s = 0;
            } else {
              *r_s = delta / cmax;
            }
            //debug ("rgb=%d,%d,%d ---> hsv=%f,%f,%f", r, g, b, *r_h, *r_s, *r_v);
          }
          
          
          void hsv_to_rgb (float h, float s, float v, int *r_r, int *r_g, int *r_b) {
            if (h > 360)
              h -= 360;
            if (h < 0)
              h += 360;
            h = CLAMP (h, 0, 360);
            s = CLAMP (s, 0, 1);
            v = CLAMP (v, 0, 1);
            float c = v * s;
            float x = c * (1 - fabsf (fmod ((h / HUE_ANGLE), 2) - 1));
            float m = v - c;
            float rp, gp, bp;
            int a = h / 60;
          
            //debug ("h=%f, a=%d", h, a);
          
            switch (a) {
              case 0:
                rp = c;
                gp = x;
                bp = 0;
              break;
          
              case 1:
                rp = x;
                gp = c;
                bp = 0;
              break;
          
              case 2:
                rp = 0;
                gp = c;
                bp = x;
              break;
          
              case 3:
                rp = 0;
                gp = x;
                bp = c;
              break;
          
              case 4:
                rp = x;
                gp = 0;
                bp = c;
              break;
          
              default: // case 5:
                rp = c;
                gp = 0;
                bp = x;
              break;
            }
          
            *r_r = (rp + m) * 255;
            *r_g = (gp + m) * 255;
            *r_b = (bp + m) * 255;
          
            //debug ("hsv=%f,%f,%f, ---> rgb=%d,%d,%d", h, s, v, *r_r, *r_g, *r_b);
          }
          

          【讨论】:

          • 缺少 CLAMP 和 HUE_ANGLE 的符号定义
          【解决方案12】:

          我通过对 RGBS 和 V 使用 0-1 范围以及对色调使用 0-6 范围(避免除法)并将案例分为两类,从而创建了一个可能更快的实现:

          #include <math.h>
          #include <float.h>
          
          void fromRGBtoHSV(float rgb[], float hsv[])
          {
          //    for(int i=0; i<3; ++i)
          //        rgb[i] = max(0.0f, min(1.0f, rgb[i]));
          
               hsv[0] = 0.0f;
               hsv[2] = max(rgb[0], max(rgb[1], rgb[2]));
               const float delta = hsv[2] - min(rgb[0], min(rgb[1], rgb[2]));
          
               if (delta < FLT_MIN)
                   hsv[1] = 0.0f;
               else
               {
                   hsv[1] = delta / hsv[2];
                   if (rgb[0] >= hsv[2])
                   {
                       hsv[0] = (rgb[1] - rgb[2]) / delta;
                       if (hsv[0] < 0.0f)
                           hsv[0] += 6.0f;
                   }
                   else if (rgb[1] >= hsv[2])
                       hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
                   else
                       hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
               }    
          }
          
          void fromHSVtoRGB(const float hsv[], float rgb[])
          {
              if(hsv[1] < FLT_MIN)
                  rgb[0] = rgb[1] = rgb[2] = hsv[2];
              else
              {
                  const float h = hsv[0];
                  const int i = (int)h;
                  const float f = h - i;
                  const float p = hsv[2] * (1.0f - hsv[1]);
          
                  if (i & 1) {
                      const float q = hsv[2] * (1.0f - (hsv[1] * f));
                      switch(i) {
                      case 1:
                          rgb[0] = q;
                          rgb[1] = hsv[2];
                          rgb[2] = p;
                          break;
                      case 3:
                          rgb[0] = p;
                          rgb[1] = q;
                          rgb[2] = hsv[2];
                          break;
                      default:
                          rgb[0] = hsv[2];
                          rgb[1] = p;
                          rgb[2] = q;
                          break;
                      }
                  }
                  else
                  {
                      const float t = hsv[2] * (1.0f - (hsv[1] * (1.0f - f)));
                      switch(i) {
                      case 0:
                          rgb[0] = hsv[2];
                          rgb[1] = t;
                          rgb[2] = p;
                          break;
                      case 2:
                          rgb[0] = p;
                          rgb[1] = hsv[2];
                          rgb[2] = t;
                          break;
                      default:
                          rgb[0] = t;
                          rgb[1] = p;
                          rgb[2] = hsv[2];
                          break;
                      }
                  }
              }
          }
          

          对于 0-255 范围,只需 * 255.0f + 0.5f 并将其分配给无符号字符(或除以 255.0 得到相反的结果)。

          【讨论】:

            【解决方案13】:
            // This pair of functions convert HSL to RGB and vice-versa.
            // It's pretty optimized for execution speed
            
            typedef unsigned char       BYTE
            typedef struct _RGB
            {
                BYTE R;
                BYTE G;
                BYTE B;
            } RGB, *pRGB;
            typedef struct _HSL
            {
                float   H;  // color Hue (0.0 to 360.0 degrees)
                float   S;  // color Saturation (0.0 to 1.0)
                float   L;  // Luminance (0.0 to 1.0)
                float   V;  // Value (0.0 to 1.0)
            } HSL, *pHSL;
            
            float   *fMin       (float *a, float *b)
            {
                return *a <= *b?  a : b;
            }
            
            float   *fMax       (float *a, float *b)
            {
                return *a >= *b? a : b;
            }
            
            void    RGBtoHSL    (pRGB rgb, pHSL hsl)
            {
            // See https://en.wikipedia.org/wiki/HSL_and_HSV
            // rgb->R, rgb->G, rgb->B: [0 to 255]
                float r =       (float) rgb->R / 255;
                float g =       (float) rgb->G / 255;
                float b =       (float) rgb->B / 255;
                float *min =    fMin(fMin(&r, &g), &b);
                float *max =    fMax(fMax(&r, &g), &b);
                float delta =   *max - *min;
            
            // L, V [0.0 to 1.0]
                hsl->L = (*max + *min)/2;
                hsl->V = *max;
            // Special case for H and S
                if (delta == 0)
                {
                    hsl->H = 0.0f;
                    hsl->S = 0.0f;
                }
                else
                {
            // Special case for S
                    if((*max == 0) || (*min == 1))
                        hsl->S = 0;
                    else
            // S [0.0 to 1.0]
                        hsl->S = (2 * *max - 2*hsl->L)/(1 - fabsf(2*hsl->L - 1));
            // H [0.0 to 360.0]
                    if      (max == &r)     hsl->H = fmod((g - b)/delta, 6);    // max is R
                    else if (max == &g)     hsl->H = (b - r)/delta + 2;         // max is G
                    else                    hsl->H = (r - g)/delta + 4;         // max is B
                    hsl->H *= 60;
                }
            }
            
            void    HSLtoRGB    (pHSL hsl, pRGB rgb)
            {
            // See https://en.wikipedia.org/wiki/HSL_and_HSV
                float a, k, fm1, fp1, f1, f2, *f3;
            // L, V, S: [0.0 to 1.0]
            // rgb->R, rgb->G, rgb->B: [0 to 255]
                fm1 = -1;
                fp1 = 1;
                f1 = 1-hsl->L;
                a = hsl->S * *fMin(&hsl->L, &f1);
                k = fmod(0 + hsl->H/30, 12);
                f1 = k - 3;
                f2 = 9 - k;
                f3 = fMin(fMin(&f1, &f2), &fp1) ;
                rgb->R = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
            
                k = fmod(8 + hsl->H/30, 12);
                f1 = k - 3;
                f2 = 9 - k;
                f3 = fMin(fMin(&f1, &f2), &fp1) ;
                rgb->G = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
            
                k = fmod(4 + hsl->H/30, 12);
                f1 = k - 3;
                f2 = 9 - k;
                f3 = fMin(fMin(&f1, &f2), &fp1) ;
                rgb->B = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
            }
            

            【讨论】:

              【解决方案14】:

              This link 有你想要的公式。如果您想要快速,那就是性能问题(数值技术)。

              【讨论】:

                猜你喜欢
                • 2018-03-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-01-24
                • 1970-01-01
                • 2010-12-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多