Previous Table of Contents Next


Appendix B
RGB/HSV/HLS/HSI Color Model Conversion Routines

#define DEGREES_PER_RADIAN  (180.0 / 3.14159265358979)
#define UNDEFINED_HUE       -1.000
#define ZERO_SATURATION      0.0

struct RgbTriadStruct {
                          USGC Red;
                          USGC Green;
                          USGC Blue;
                       };

struct HsvTriadStruct  {
                          double Hue;
                          double Saturation;
                          double Value;
                       };

struct HlsTriadStruct  {
                          double Hue;
                          double Lightness;
                          double Saturation;
                       };

struct HsiTriadStruct  {
                          double Hue;
                          double Saturation;
                          double Intensity;
                       };

int RgbToHsv(struct RgbTriadStruct *RgbTriadStructPtr,
             struct HsvTriadStruct *HsvTriadStructPtr)
{

USGI MaxPrimaryValue, MinPrimaryValue, MaxMinDelta;

MaxPrimaryValue = (RgbTriadStructPtr->Red  > RgbTriadStructPtr->Green) ?
                 RgbTriadStructPtr->Red  : RgbTriadStructPtr->Green;

MaxPrimaryValue = (RgbTriadStructPtr->Blue > MaxPrimaryValue) ?
                 RgbTriadStructPtr->Blue : MaxPrimaryValue;

HsvTriadStructPtr->Value = ((double) MaxPrimaryValue) / 255.0;

if (!MaxPrimaryValue)
{
     HsvTriadStructPtr->Value      = 0.0;
     HsvTriadStructPtr->Hue        = UNDEFINED_HUE;
     HsvTriadStructPtr->Saturation = ZERO_SATURATION;

     return 0;
} else
{
        HsvTriadStructPtr->Value = ((double) MaxPrimaryValue) / 255.0;
}

MinPrimaryValue = (RgbTriadStructPtr->Red  < RgbTriadStructPtr->Green) ?
                  RgbTriadStructPtr->Red  : RgbTriadStructPtr->Green;

MinPrimaryValue = (RgbTriadStructPtr->Blue < MinPrimaryValue) ?
                  RgbTriadStructPtr->Blue : MinPrimaryValue;

if (!(MaxMinDelta = MaxPrimaryValue - MinPrimaryValue))
{
      HsvTriadStructPtr->Hue        = UNDEFINED_HUE;
      HsvTriadStructPtr->Saturation = ZERO_SATURATION;

      return 0;
}

HsvTriadStructPtr->Saturation =
            ((double) MaxMinDelta) / ((double) MaxPrimaryValue);

if (RgbTriadStructPtr->Red == MaxPrimaryValue)
     HsvTriadStructPtr->Hue =  0.0 +
          ((double) (RgbTriadStructPtr->Green - RgbTriadStructPtr->Blue))
                                                /
                                   ((double) MaxMinDelta);

else if (RgbTriadStructPtr->Green == MaxPrimaryValue)
          HsvTriadStructPtr->Hue =  2.0 +
             ((double) (RgbTriadStructPtr->Blue - RgbTriadStructPtr->Red))
                                                /
                                 ((double) MaxMinDelta);

     else if (RgbTriadStructPtr->Blue == MaxPrimaryValue)
               HsvTriadStructPtr->Hue =  4.0 +
                  ((double) (RgbTriadStructPtr->Red - RgbTriadStructPtr->Green))
                                                /
                                      ((double) MaxMinDelta);

    HsvTriadStructPtr->Hue *= 60.0;

    if (HsvTriadStructPtr->Hue < 0)
        HsvTriadStructPtr->Hue += 360.0;

    return 0;
}

int HsvToRgb(struct HsvTriadStruct *HsvTriadStructPtr,
             struct RgbTriadStruct *RgbTriadStructPtr)
{
    USGI   IntValue;
    double OneSixtythHue, DoubleDecimal, P, Q, T, V;

    if (HsvTriadStructPtr->Saturation == ZERO_SATURATION)
    {
        if (HsvTriadStructPtr->Hue == UNDEFINED_HUE)
        {
            RgbTriadStructPtr->Red   =
            RgbTriadStructPtr->Green =
            RgbTriadStructPtr->Blue  =
                         HsvTriadStructPtr->Value * 255.0 + 0.5;
        } else
        {
            printf("\n\nHsvToRgb: Illegal HSV hue value (saturation == 0)");
            printf("\nHue == %5.3f, Saturation = %5.3f, Value == %5.3f",
                      HsvTriadStructPtr->Hue,
                      HsvTriadStructPtr->Saturation,
                      HsvTriadStructPtr->Value);
        }
        return 0;
    }
    if (HsvTriadStructPtr->Hue > 359.9)
        HsvTriadStructPtr->Hue = 0.0;

    OneSixtythHue = (HsvTriadStructPtr->Hue / 60.0);

    IntValue     = (USGI) OneSixtythHue;
    DoubleDecimal = OneSixtythHue - ((double) IntValue);

    P = HsvTriadStructPtr->Value *
        (1.0 - HsvTriadStructPtr->Saturation);

    Q = HsvTriadStructPtr->Value *
        (1.0 - (HsvTriadStructPtr->Saturation * DoubleDecimal));

    T = HsvTriadStructPtr->Value *
        (1.0 - (HsvTriadStructPtr->Saturation * (1.0 - DoubleDecimal)));

    V = HsvTriadStructPtr->Value;

    switch (IntValue)
    {
        case 0:
                RgbTriadStructPtr->Red   = (USGI) (V * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (T * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (P * 255.0 + 0.5);

                break;

        case 1:
                RgbTriadStructPtr->Red   = (USGI) (Q * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (V * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (P * 255.0 + 0.5);

                break;

        case 2:
                RgbTriadStructPtr->Red   = (USGI) (P * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (V * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (T * 255.0 + 0.5);

                break;

        case 3:
                RgbTriadStructPtr->Red   = (USGI) (P * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (Q * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (V * 255.0 + 0.5);

                break;

        case 4:
                RgbTriadStructPtr->Red   = (USGI) (T * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (P * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (V * 255.0 + 0.5);

                break;

        case 5:
                RgbTriadStructPtr->Red   = (USGI) (V * 255.0 + 0.5);
                RgbTriadStructPtr->Green = (USGI) (P * 255.0 + 0.5);
                RgbTriadStructPtr->Blue  = (USGI) (Q * 255.0 + 0.5);

                break;
    }

    return 0;
}

int RgbToHls(struct RgbTriadStruct *RgbTriadStructPtr,
             struct HlsTriadStruct *HlsTriadStructPtr)
{
    USGI   MaxPrimaryValue, MinPrimaryValue, MaxMinDelta;
    double H;

    MaxPrimaryValue = (RgbTriadStructPtr->Red  > RgbTriadStructPtr->Green)?
                     RgbTriadStructPtr->Red  : RgbTriadStructPtr->Green;

    MaxPrimaryValue = (RgbTriadStructPtr->Blue > MaxPrimaryValue) ?
                     RgbTriadStructPtr->Blue : MaxPrimaryValue;

    MinPrimaryValue = (RgbTriadStructPtr->Red  < RgbTriadStructPtr->Green)?
                     RgbTriadStructPtr->Red  : RgbTriadStructPtr->Green;

    MinPrimaryValue = (RgbTriadStructPtr->Blue < MinPrimaryValue) ?
                     RgbTriadStructPtr->Blue : MinPrimaryValue;

    HlsTriadStructPtr->Lightness =
                ((double) (MaxPrimaryValue + MinPrimaryValue))
                                          /
                                     (2.0 * 255.0);

    if (MaxPrimaryValue == MinPrimaryValue)
    {
        HlsTriadStructPtr->Hue        = UNDEFINED_HUE;
        HlsTriadStructPtr->Saturation = ZERO_SATURATION;

        return 0;
    }

    MaxMinDelta = MaxPrimaryValue - MinPrimaryValue;

    if (HlsTriadStructPtr->Lightness < 0.5)
        HlsTriadStructPtr->Saturation =
                                        ((double) MaxMinDelta)
                                                /
                   ((double) (MaxPrimaryValue + MinPrimaryValue));
    else
        HlsTriadStructPtr->Saturation =
                                     ((double) MaxMinDelta)
                                                /
                   ((double) (2 * 255 - (MaxPrimaryValue + MinPrimaryValue)));

    if (RgbTriadStructPtr->Red == MaxPrimaryValue)
        HlsTriadStructPtr->Hue =  0.0 +
             ((double) (RgbTriadStructPtr->Green - RgbTriadStructPtr->Blue))
                                                /
                                      ((double) MaxMinDelta);

    else if (RgbTriadStructPtr->Green == MaxPrimaryValue)
              HlsTriadStructPtr->Hue =  2.0 +
                ((double) (RgbTriadStructPtr->Blue - RgbTriadStructPtr->Red))
                                                /
                                    ((double) MaxMinDelta);

    else if (RgbTriadStructPtr->Blue == MaxPrimaryValue)
             HlsTriadStructPtr->Hue =  4.0 +
               ((double) (RgbTriadStructPtr->Red - RgbTriadStructPtr->Green))
                                                /
                                    ((double) MaxMinDelta);

    HlsTriadStructPtr->Hue *= 60.0;
 
    if (HlsTriadStructPtr->Hue < 0)
        HlsTriadStructPtr->Hue += 360.0;

    return 0;
}

int HlsToRgb(struct HlsTriadStruct *HlsTriadStructPtr,
            struct RgbTriadStruct *RgbTriadStructPtr)
{
    double M1, M2;

    if (HlsTriadStructPtr->Lightness < 0.5)
      M2 = HlsTriadStructPtr->Lightness *
           (1.0 + HlsTriadStructPtr->Saturation);
    else
      M2 = HlsTriadStructPtr->Lightness  +
           HlsTriadStructPtr->Saturation -
          (HlsTriadStructPtr->Lightness * HlsTriadStructPtr->Saturation);

    M1 = 2.0 * HlsTriadStructPtr->Lightness - M2;

    if (HlsTriadStructPtr->Saturation == ZERO_SATURATION)
    {
        if (HlsTriadStructPtr->Hue == UNDEFINED_HUE)
        {
            RgbTriadStructPtr->Red   =
            RgbTriadStructPtr->Green =
            RgbTriadStructPtr->Blue  =
                       HlsTriadStructPtr->Lightness * 255.0 + 0.5;
        } else
        {
            printf("\n\nHlsToRgb: Illegal Hls hue value (saturation == 0)");
            printf("\nHue == %5.3f, Lightness = %5.3f, Saturation == %5.3f",
                      HlsTriadStructPtr->Hue,
                      HlsTriadStructPtr->Lightness,
                      HlsTriadStructPtr->Saturation);
        }

        return 0;
    }

    RgbTriadStructPtr->Red   = RgbCubeValue(M1, M2,
                                  HlsTriadStructPtr->Hue + 120.0);
    RgbTriadStructPtr->Green = RgbCubeValue(M1, M2,
                                  HlsTriadStructPtr->Hue);
    RgbTriadStructPtr->Blue  = RgbCubeValue(M1, M2,
                                  HlsTriadStructPtr->Hue - 120.0);

    return 0;
}

int RgbCubeValue(double N1, double N2, double Hue)
{
    double Value;

    if (Hue > 360.0) Hue -= 360.0;

    if (Hue <   0.0) Hue += 360.0;

    if      (Hue <  60.0) Value = N1 + (N2 - N1) * Hue / 60.0;
    else if (Hue < 180.0) Value = N2;
    else if (Hue < 240.0) Value = N1 + (N2 - N1) * (240 - Hue) / 60.0;
    else                  Value = N1;

    return (USGI) (Value * 255.0 + 0.5);
}

int HlsToRgb2(struct HlsTriadStruct *HlsTriadStructPtr,
              struct RgbTriadStruct *RgbTriadStructPtr)
{
    int    Sextant;
    double Min, V, Sv, H, Vsf;
    double Mid1, Mid2, Fract;

    if (HlsTriadStructPtr->Lightness <= 0.5)
      V = HlsTriadStructPtr->Lightness *
          (1.0 + HlsTriadStructPtr->Saturation);
    else
      V = HlsTriadStructPtr->Lightness  +
          HlsTriadStructPtr->Saturation -
         (HlsTriadStructPtr->Lightness * HlsTriadStructPtr->Saturation);

    if (V <= 0.0)
    {
        RgbTriadStructPtr->Red   =
        RgbTriadStructPtr->Green =
        RgbTriadStructPtr->Blue  = (USGC) 0;
    } else
    {
        Min     = HlsTriadStructPtr->Lightness +
                  HlsTriadStructPtr->Lightness - V;
        Sv      = (V - Min) / V;
        H       = HlsTriadStructPtr->Hue / 60.0;
        Sextant = (int) H;
        Fract   = H - (double) Sextant;
        Vsf     = V * Sv * Fract;
        Mid1    = Min + Vsf;
        Mid2    = V   - Vsf;

        V    *= 255.0; if (V    > 255.0) V    = 255.0;
        Min  *= 255.0; if (Min  > 255.0) Min  = 255.0;
        Mid1 *= 255.0; if (Mid1 > 255.0) Mid1 = 255.0;
        Mid2 *= 255.0; if (Mid2 > 255.0) Mid2 = 255.0;

        switch (Sextant)
        {
            case 0 :    RgbTriadStructPtr->Red   = (USGC) V;
                        RgbTriadStructPtr->Green = (USGC) Mid1;
                        RgbTriadStructPtr->Blue  = (USGC) Min;
                        break;

            case 1 :    RgbTriadStructPtr->Red   = (USGC) Mid2;
                        RgbTriadStructPtr->Green = (USGC) V;
                        RgbTriadStructPtr->Blue  = (USGC) Min;
                        break;

            case 2 :    RgbTriadStructPtr->Red   = (USGC) Min;
                        RgbTriadStructPtr->Green = (USGC) V;
                        RgbTriadStructPtr->Blue  = (USGC) Mid1;
                        break;

            case 3 :    RgbTriadStructPtr->Red   = (USGC) Min;
                        RgbTriadStructPtr->Green = (USGC) Mid2;
                        RgbTriadStructPtr->Blue  = (USGC) V;
                        break;

            case 4 :    RgbTriadStructPtr->Red   = (USGC) Mid1;
                        RgbTriadStructPtr->Green = (USGC) Min;
                        RgbTriadStructPtr->Blue  = (USGC) V;
                        break;

            case 5 :    RgbTriadStructPtr->Red   = (USGC) V;
                        RgbTriadStructPtr->Green = (USGC) Min;
                        RgbTriadStructPtr->Blue  = (USGC) Mid2;
                        break;

            default:    RgbTriadStructPtr->Red   = (USGC) 255;
                        RgbTriadStructPtr->Green = (USGC) 0;
                        RgbTriadStructPtr->Blue  = (USGC) 0;
                        break;
        }

    }

    return 0;
}

int RgbToHsi(struct RgbTriadStruct *RgbTriadStructPtr,
             struct HsiTriadStruct *HsiTriadStructPtr)
{
    double R, G, B, Sum, Quotient;
    double Radians, Angle, MinValue, MaxValue, TempDouble1, TempDouble2;

    R = ((double) RgbTriadStructPtr->Red)   / 255.0;
    G = ((double) RgbTriadStructPtr->Green) / 255.0;
    B = ((double) RgbTriadStructPtr->Blue)  / 255.0;

    Sum = R + G + B;

    HsiTriadStructPtr->Intensity = Sum / 3.0;

    MinValue = (R < G) ? R : G;
    MinValue = (B < MinValue) ? B : MinValue;

    MaxValue = (R > G) ? R : G;
    MaxValue = (B > MaxValue) ? B : MaxValue;

    if (HsiTriadStructPtr->Intensity < 0.00001)
        HsiTriadStructPtr->Saturation = ZERO_SATURATION;
    else
        HsiTriadStructPtr->Saturation = 1.0 - (3.0 * MinValue) / Sum;

    if (MinValue == MaxValue)
    {
        HsiTriadStructPtr->Hue        = UNDEFINED_HUE;
        HsiTriadStructPtr->Saturation = ZERO_SATURATION;

        return 0;
    }

    TempDouble1 = (((R - G) + (R - B)) / 2.0);
    TempDouble2 = (R - G)*(R - G) + (R - B)*(G - B);
    Quotient = (TempDouble1 / sqrt(TempDouble2));

    if (Quotient > 0.99999999999999999)
        Radians = 0.0;
    else if (Quotient < -0.99999999999999999)
        Radians = 3.1415926535;
    else
        Radians = acos(TempDouble1 / sqrt(TempDouble2));

    Angle = Radians * DEGREES_PER_RADIAN;

    if (B > G)
        HsiTriadStructPtr->Hue = 360.0 - Angle;
    else
        HsiTriadStructPtr->Hue = Angle;

    return 0;
}

int HsiToRgb(struct HsiTriadStruct *HsiTriadStructPtr,
             struct RgbTriadStruct *RgbTriadStructPtr)
{
    double r, g, b, Hue, Fraction, MaxDouble;

    if (HsiTriadStructPtr->Saturation == ZERO_SATURATION)
    {
        if (HsiTriadStructPtr->Hue == UNDEFINED_HUE)
        {
            RgbTriadStructPtr->Red   =
            RgbTriadStructPtr->Green =
            RgbTriadStructPtr->Blue  =
                       HsiTriadStructPtr->Intensity * 255.0 + 0.5;
        } else
        {
            printf("\n\nHsiToRgb: Illegal HSI hue value (saturation == 0)");
            printf("\nHue == %5.3f, Saturation = %5.3f, Intensity == %5.3f",
                      HsiTriadStructPtr->Hue,
                      HsiTriadStructPtr->Saturation,
                      HsiTriadStructPtr->Intensity);
        }

        return 0;
    }

    if (HsiTriadStructPtr->Hue >    0.0  &&
        HsiTriadStructPtr->Hue <= 120.0     )
    {
        b = (1.0 - HsiTriadStructPtr->Saturation) / 3.0;

        Hue = HsiTriadStructPtr->Hue;

        Fraction = (HsiTriadStructPtr->Saturation *
                             cos(Hue / DEGREES_PER_RADIAN))
                                     /
                    cos((60.0 - Hue) / DEGREES_PER_RADIAN);

        r = (1.0 + Fraction) / 3.0;

        g = 1.0 - (r + b);

    } else if (HsiTriadStructPtr->Hue >  120.0  &&
               HsiTriadStructPtr->Hue <= 240.0     )
    {

        Hue = HsiTriadStructPtr->Hue - 120.0;

        r = (1.0 - HsiTriadStructPtr->Saturation) / 3.0;

        Fraction =  (HsiTriadStructPtr->Saturation *
                                       cos(Hue / DEGREES_PER_RADIAN))
                                               /
                              cos((60.0 - Hue) / DEGREES_PER_RADIAN);

        g = (1.0 + Fraction) / 3.0;

        b = 1.0 - (r + g);

    } else
    {
                                                                            Hue=
HsiTriadStructPtr->Hue - 240.0;

        g = (1.0 - HsiTriadStructPtr->Saturation) / 3.0;

        Fraction =  (HsiTriadStructPtr->Saturation *
                         cos(Hue / DEGREES_PER_RADIAN))
                                 /
                cos((60.0 - Hue) / DEGREES_PER_RADIAN);

        b = (1.0 + Fraction) / 3.0;

        r = 1.0 - (g + b);

    }

    MaxDouble = ((r > g)         ? r         : g);
    MaxDouble = ((MaxDouble > b) ? MaxDouble : b);

    if (3.0 * HsiTriadStructPtr->Intensity * MaxDouble > 1.0)
        HsiTriadStructPtr->Intensity = (1.0 / (3.0 * MaxDouble));

    r *= 255.0;
    g *= 255.0;
    b *= 255.0;

    RgbTriadStructPtr->Red   =
          (USGC) ((3.0 * HsiTriadStructPtr->Intensity * r) + 0.5);

    RgbTriadStructPtr->Green =
          (USGC) ((3.0 * HsiTriadStructPtr->Intensity * g) + 0.5);

    RgbTriadStructPtr->Blue  =
          (USGC) ((3.0 * HsiTriadStructPtr->Intensity * b) + 0.5);

    return 0;
}

/*=================== End of File ===================*/


Previous Table of Contents Next