Algoritmo per convertire RGB in HSV e HSV in RGB nell'intervallo 0-255 per entrambi


93

Sto cercando un convertitore di spazio colore da RGB a HSV, in particolare per la gamma da 0 a 255 per entrambi gli spazi colore.

Risposte:


138

Li ho usati per molto tempo - non ho idea da dove provengano a questo punto ... Notare che gli ingressi e le uscite, ad eccezione dell'angolo in gradi, sono compresi tra 0 e 1,0.

NOTA: questo codice non esegue il vero controllo di integrità sugli input. Procedi con cautela!

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;     
}

13
@ Stargazer712 Se fai i conti, dovrebbe essere ==, ma se lo usi potresti ricevere un reclamo sul confronto dei float. Anche se in teoria è impossibile che sia>, l'uso di "> =" invece di "==" chiude l'errore del compilatore che ottengo sul Mac utilizzando llvm / Xcode,
David H

4
@ Gerard out è in gradi. 60 è 1/6 di un cerchio completo. Questo non è radianti.
David H

4
Il motivo >=dell'errore del compilatore e è perché double == doublenon è valido e illegale nella maggior parte dei compilatori. L'aritmetica in virgola mobile e la memorizzazione in virgola mobile significano che due valori possono essere uguali in valore approssimativo , ma non uguali in valore memorizzato anche se formalmente sono uguali. Dovresti fare abs(double_a - double_b) <= epsilondove epsilon ha un valore, in genere 1e-4.
Brandon LeBlanc

1
Per rgb2hsv, dove in.r> = max, perché il codice non utilizza l'operatore mod? Il valore out.h non dovrebbe essere calcolato come "out.h = ((in.g - in.b) / delta)% 6;" ?
craigrf

5
@JoachimBrandonLeBlanc: "double == double non è valido e illegale nella maggior parte dei compilatori" Non è vero. Il confronto di due valori in virgola mobile per l'uguaglianza è completamente ben definito e una cosa legale da fare . Nessun compilatore mainstream e / o conforme ti impedirà di farlo. Il problema è che potresti non ottenere la risposta che effettivamente volevi e che probabilmente intendevi eseguire un confronto più flessibile.
Gare di leggerezza in orbita il

39

Puoi anche provare questo codice senza float (più veloce ma meno accurato):

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;
}

Nota che questo algoritmo utilizza 0-255come intervallo (non 0-360) come richiesto dall'autore di questa domanda.


8
Puoi convertire tutti i 16.777.216 possibili colori RGB in HSV e di nuovo in RGB. Sfortunatamente, usando questo algoritmo scoprirai che alcuni colori non andranno bene. Forse percettivamente sembrano più o meno lo stesso ma numericamente c'è una differenza sostanziale, ad es. (0, 237, 11) andrà di ritorno a (0, 237, 0) ecc. Questo non è il caso quando si utilizza l'algoritmo di David H basato su calcoli in virgola mobile .
Martin Liversage

3
@ rightaway717 - questo mi dà l'intera gamma, forse stai usando 0-360 come gamma? Questo algoritmo (per fortuna) utilizza 0x00 - 0xFF come intervallo
Anne Quinn,

@AnneQuinn corretto! Mi aspettavo che fosse 0-360, ma non avevo abbastanza passione per capire cosa c'era che non andava, quando ho visto che la risposta accettata funzionava. Penso che Leszek avrebbe dovuto menzionare la gamma di tonalità nella risposta, anche se lo ringrazio comunque per averla pubblicata.
rightaway717

24

L'ho scritto in HLSL per il nostro motore di rendering, non ha condizioni in esso:

    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
    }

2
Hai la conversione nell'altro modo (RGB2HSV)? usando lo stesso approccio?
Carlos Barcellos

8

Ecco un'implementazione C basata sulla Computer Graphics and Geometric Modeling di Agoston : implementazione e algoritmi p. 304, con H ∈ [0, 360] e S , V ∈ [0, 1].


Esiste un codice C simile per la conversione da HSV a RGB? Grazie!
user3236841

@ user3236841 Lo pseudo-codice per questo si trova nella pagina precedente (p. 303) di Computer Graphics and Geometric Modeling: Implementation and Algorithms di Agoston .
Geremia

Verifica: questo è HSL2RGB, non HSV.
Kromster

7

questo dovrebbe essere qui: funziona comunque. E sembra buono rispetto a quelli sopra.

codice 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 è un tipo di dati vector3 con precisione a 16 bit, cioè float3 hue () restituisce un tipo di dati (x, y, z) ad es. (r, g, b), metà è uguale a metà precisione, 8 bit, un float4 è (r, g, b, a) 4 valori.


3
Esigenze alcune definizioni di tipo per half, half4, half3, float3, eccetera.
Quuxplusone

1
half4 è il colore (r, g, b, a) o qualsiasi float 4x metà precisione, può anche essere la massima precisione, è solo un vettore4
alienziale

cos'è saturate ()?
TatiOverflow

saturate () è disponibile nel riferimento al codice HLSL: saturate (x) fornisce x clamped / clipped tra 0 e 1 docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/…
aliential

Potresti spiegare la dichiarazione di ritorno in HSVtoRGB? Sembra essere il vettore RGB a 3 elementi restituito da Hue moltiplicato per uno scalare - risultante in qualcosa come [k r, k g, k * b, 1]
pathfinder

5

La risposta di @fins ha un problema di overflow su Arduio quando si abbassa la saturazione. Eccolo con alcuni valori convertiti in int per impedirlo.

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;
}

4

Questo non è C, ma sicuramente funziona. Tutti gli altri metodi che vedo qui funzionano racchiudendo ogni cosa in parti di un esagono e approssimando "angoli" da quello. Iniziando invece con un'equazione diversa usando i coseni e risolvendo per hs e v, si ottiene una relazione molto più piacevole tra hsv e rgb, e il tweening diventa più fluido (a costo di essere molto più lento).

Supponiamo che tutto sia in virgola mobile. Se rg e b vanno da 0 a 1, h va da 0 a 2pi, v va da 0 a 4/3 e s va da 0 a 2/3.

Il codice seguente è scritto in Lua. È facilmente traducibile in qualsiasi altra cosa.

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

Puoi spiegare la derivazione di questo algoritmo, o almeno indicare la relazione fondamentale? Mi aspettavo di trovare alcune tonalità composte da un solo componente RGB, tuttavia la funzione hsv.torgb indica che questo è impossibile in questo algoritmo. Wikipedia mostra un'immagine della relazione prevista tra HSV e RGB
oclyke

2

Versione GLSL Shader basata sulla risposta di Patapoms:

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
}

1

Non sono uno sviluppatore C ++ quindi non fornirò codice. Ma posso fornire un semplice algoritmo hsv2rgb (rgb2hsv qui ) che attualmente scopro - aggiorno wiki con la descrizione: HSV e HLS . Il miglioramento principale è che osservo attentamente r, g, b come funzioni di tonalità e introduco una funzione di forma più semplice per descriverle (senza perdere la precisione). L'algoritmo - in ingresso abbiamo: h (0-255), s (0-255), v (0-255)

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

Usiamo la funzione f descritta come segue

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

dove (mod può restituire una parte frazionaria; k è il numero in virgola mobile)

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

Ecco gli snippet / PoV in SO in JS: HSV e HSL


Ciao Kamil! Sto cercando di utilizzare il tuo algoritmo e ho una domanda su quella parte min(k,4-k,1). Perché ci sono tre valori e cosa sta succedendo esattamente qui? Grazie in anticipo!
Eugene Alexeev

@EugeneAlexeev Ho corretto l'articolo in wiki (qualcuno lo ha rotto) - e
aggiorno i

1

Ecco un convertitore online con un articolo dopo aver spiegato tutti gli algoritmi per la conversione del colore.

Probabilmente preferiresti una versione C già pronta, ma non dovrebbe essere lunga da applicare e potrebbe aiutare altre persone che cercano di fare lo stesso in un'altra lingua o con un altro spazio colore.


0

Questo collegamento ha formule per ciò che desideri. Quindi è una questione di prestazioni (tecniche numeriche) se lo vuoi veloce.


0

Eccone uno che ho appena scritto questa mattina basandosi più o meno sulla stessa matematica di cui sopra:

/* 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);
}

definizioni di simboli mancanti per CLAMP e HUE_ANGLE
Dmitry

0

Ho creato un'implementazione forse più veloce utilizzando l'intervallo 0-1 per RGBS e V e l'intervallo 0-6 per Hue (evitando la divisione) e raggruppando i casi in due categorie:

#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;
            }
        }
    }
}

Per l'intervallo 0-255 solo * 255.0f + 0.5f e assegnarlo a un carattere senza segno (o dividere per 255.0 per ottenere il contrario).


0
// 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)));
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.