Formula per determinare la luminosità del colore RGB


387

Sto cercando una sorta di formula o algoritmo per determinare la luminosità di un colore dati i valori RGB. So che non può essere così semplice come sommare i valori RGB e avere somme più alte sarebbe più luminoso, ma sono quasi in perdita da dove iniziare.


8
La luminosità percepita è ciò che penso di cercare, grazie.
robmerica,

2
C'è un buon articolo ( Manipolazione dei colori in .NET - Parte 1 ) sugli spazi cromatici e le conversazioni tra loro, compresi sia la teoria che il codice (C #). Per la risposta guarda l' argomento Conversione tra modelli nell'articolo.
sottolinea il

4
Sono membro da molti anni e non l'ho mai fatto prima. Posso suggerire di rivedere le risposte e ripensare a quale accettare?
Jive Dadson,

Risposte:


456

Intendi luminosità? Luminosità percepita? Luminance?

  • Luminanza (standard per determinati spazi colore): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminanza (opzione percepita 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminanza (opzione percepita 2, più lenta da calcolare): sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )(grazie a @MatthewHerbst ) [3]

26
Si noti che entrambi enfatizzano gli aspetti fisiologici: il bulbo oculare umano è più sensibile alla luce verde, meno al rosso e meno al blu.
Bob Cross,

16
Nota anche che tutti questi sono probabilmente per RGB lineare 0-1 e probabilmente hai RGB 0-255 con correzione gamma. Non vengono convertiti come pensi che siano.
alex strano

4
Non corretto. Prima di applicare la trasformazione lineare, è necessario applicare l'inverso della funzione gamma per lo spazio colore. Quindi, dopo aver applicato la funzione lineare, viene applicata la funzione gamma.
Jive Dadson,

6
Nell'ultima formula, è (0.299 * R) ^ 2 o è 0.299 * (R ^ 2)?
Kaizer Sozay,

3
@KaizerSozay Come è scritto qui significherebbe 0.299*(R^2)(perché l'
espiazione

298

Penso che quello che stai cercando sia la formula di conversione RGB -> Luma .

ITU BT.709 fotometrico / digitale :

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU digitale BT.601 (dà più peso ai componenti R e B):

Y = 0.299 R + 0.587 G + 0.114 B

Se sei disposto a scambiare la precisione con la prestazione, ci sono due formule di approssimazione per questa:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Questi possono essere calcolati rapidamente come

Y = (R+R+B+G+G+G)/6

Y = (R+R+R+B+G+G+G+G)>>3

47
Mi piace che tu abbia inserito valori precisi, ma abbia anche incluso una scorciatoia di tipo "abbastanza vicino". +1.
Beska,

3
@Jonathan Dumaine - le due formule di calcolo rapido includono entrambe il blu - il primo è (2 * rosso + Blue+ 3 * verde) / 6, il secondo è (3 * rosso + Blue+ 4 * verde) >> 3. garantito, in entrambe le rapide approssimazioni, il Blu ha il peso più basso, ma è ancora lì.
Franci Penov,

84
@JonathanDumaine Questo perché l'occhio umano è meno percettivo nei confronti di Blue ;-)
Christopher Oezbek,

4
La versione rapida funziona bene. Testato e applicato all'app del mondo reale con migliaia di utenti, tutto sembra a posto.
milosmns,

10
La versione rapida è ancora più veloce se lo fai come: Y = (R<<1+R+G<<2+B)>>3(questo è solo 3-4 cicli di CPU su ARM) ma immagino che un buon compilatore farà quell'ottimizzazione per te.
rjmunro,

105

Ho fatto il confronto dei tre algoritmi nella risposta accettata. Ho generato colori in ciclo in cui è stato utilizzato solo circa ogni 400 ° colore. Ogni colore è rappresentato da 2x2 pixel, i colori sono ordinati dal più scuro al più chiaro (da sinistra a destra, dall'alto verso il basso).

1a immagine - Luminanza (relativa)

0.2126 * R + 0.7152 * G + 0.0722 * B

2a immagine - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

3a immagine - Modello di colore HSP

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

4a immagine - WCAG 2.0 SC 1.4.3 formula relativa di luminanza e rapporto di contrasto (vedi la risposta di @ Synchro qui )

Il motivo a volte può essere individuato sulla prima e seconda immagine a seconda del numero di colori in una riga. Non ho mai notato alcun motivo sull'immagine dal 3 ° o 4 ° algoritmo.

Se dovessi scegliere andrei con l'algoritmo numero 3 poiché è molto più facile da implementare ed è circa il 33% più veloce del 4 °.

Confronto dell'algoritmo di luminosità percepita


3
Per me questa è la risposta migliore perché puoi usare un modello di immagine che ti consente di percepire se tonalità diverse sono rese con la stessa luminanza. Per me e per il mio attuale monitor, la terza immagine è la "più bella" poiché è anche più veloce della quarta, un vantaggio
CoffeDeveloper

8
L'immagine di confronto non è corretta perché non hai fornito l'input corretto a tutte le funzioni. La prima funzione richiede un input RGB lineare ; Posso riprodurre l'effetto di banding solo fornendo RGB non lineare (cioè con correzione gamma). Correggendo questo problema, non si ottengono artefatti di banding e la prima funzione è il chiaro vincitore.
Max

1
@Max the ^2e sqrtinclusi nella terza formula sono un modo più veloce per approssimare RGB lineare da RGB non lineare invece che ^2.2e ^(1/2.2)sarebbe più corretto. L'utilizzo di input non lineari anziché lineari è purtroppo estremamente comune.
Mark Ransom

53

Di seguito è riportato l'unico algoritmo CORRETTO per convertire in scala di grigi le immagini sRGB, utilizzate nei browser, ecc.

È necessario applicare un inverso della funzione gamma per lo spazio colore prima di calcolare il prodotto interno. Quindi si applica la funzione gamma al valore ridotto. La mancata integrazione della funzione gamma può causare errori fino al 20%.

Per i computer tipici, lo spazio colore è sRGB. I numeri giusti per sRGB sono ca. 0,21, 0,72, 0,07. La gamma per sRGB è una funzione composita che si avvicina all'esponenziazione di 1 / (2.2). Ecco il tutto in C ++.

// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;

// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
    double c = ic/255.0;
    if ( c <= 0.04045 )
        return c/12.92;
    else 
        return pow(((c+0.055)/(1.055)),2.4);
}

// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
    if(v<=0.0031308)
        v *= 12.92;
    else 
        v = 1.055*pow(v,1.0/2.4)-0.055;
    return int(v*255+0.5); // This is correct in C++. Other languages may not
                           // require +0.5
}

// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
    return gam_sRGB(
            rY*inv_gam_sRGB(r) +
            gY*inv_gam_sRGB(g) +
            bY*inv_gam_sRGB(b)
    );
}

5
Questo è solo il modo in cui sRGB è definito. Penso che il motivo sia che evita alcuni problemi numerici vicini allo zero. Non farebbe molta differenza se alzassi i numeri ai poteri di 2.2 e 1 / 2.2.
Jive Dadson,

8
JMD - come parte del lavoro in un laboratorio di percezione visiva, ho eseguito misurazioni di luminanza diretta su monitor CRT e posso confermare che esiste una regione lineare di luminanza nella parte inferiore dell'intervallo di valori.
Jerry Federspiel,

2
So che è molto vecchio, ma è ancora là fuori per essere cercato. Non penso che possa essere corretto. Non dovrebbe grigio (255.255.255) = grigio (255,0,0) + grigio (0,255,0) + grigio (0,0,255)? Non
DCBillen,

2
@DCBillen: no, poiché i valori si trovano nello spazio sRGB non lineare corretto per la gamma, non è possibile semplicemente sommarli. Se vuoi aggiungerli, dovresti farlo prima di chiamare gam_sRGB.
rdb,

1
@DCBillen Rdb è corretto. Il modo per sommarli è mostrato nella funzione int gray (int r, int g, int b), che "sblocca" gam_sRGB. Mi fa male che dopo quattro anni la risposta corretta sia valutata così in basso. :-) Non proprio .. Lo supererò.
Jive Dadson,

45

La risposta "Accettata" è errata e incompleta

Le uniche risposte accurate sono le risposte @ jive-dadson e @EddingtonsMonkey e nel supporto @ nils-pipenbrinck . Le altre risposte (incluso quello accettato) si collegano o citano fonti errate, irrilevanti, obsolete o rotte.

Brevemente:

  • sRGB deve essere LINEARIZZATO prima di applicare i coefficienti.
  • La luminanza (L o Y) è lineare come la luce.
  • La leggerezza percepita (L *) non è lineare come la percezione umana.
  • HSV e HSL non sono nemmeno lontanamente precisi in termini di percezione.
  • Lo standard IEC per sRGB specifica una soglia di 0,04045 NON è 0,03928 (proveniente da una bozza iniziale obsoleta).
  • Per essere utili (cioè relative alla percezione) , le distanze euclide richiedono uno spazio vettoriale cartesiano percettivamente uniforme come CIELAB. sRGB non è uno.

Quella che segue è una risposta corretta e completa:

Poiché questa discussione appare altamente nei motori di ricerca, sto aggiungendo questa risposta per chiarire le varie idee sbagliate sull'argomento.

La luminosità è un attributo percettivo, non ha una misura diretta.

La luminosità percepita viene misurata da alcuni modelli di visione come CIELAB, qui L * (Lstar) è una misura della leggerezza percettiva ed è non lineare per approssimare la curva di risposta non lineare della visione umana.

La luminanza è una misura lineare della luce, spettralmente ponderata per la visione normale ma non regolata per la percezione non lineare della luminosità.

Luma ( prime) è un segnale con codifica gamma, ponderato utilizzato in alcune codifiche video. Non deve essere confuso con la luminanza lineare.

Gamma o curva di trasferimento (TRC) è una curva che è spesso simile alla curva percettiva e viene comunemente applicata ai dati delle immagini per l'archiviazione o la trasmissione per ridurre il rumore percepito e / o migliorare l'utilizzo dei dati (e i relativi motivi).

Per determinare la luminosità percepita , prima converti i valori dell'immagine R´G´B´ codificata in gamma in luminanza lineare ( Lo Y) e poi in luminosità percepita non lineare ( L*)


PER TROVARE LA LUMINANZA:

... Perché apparentemente si era perso da qualche parte ...

Primo passo:

Converti tutti i valori interi di sRGB a 8 bit in decimale 0,0-1,0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

Passo due:

Converti un RGB con codifica gamma in un valore lineare. sRGB (standard per computer) ad esempio richiede una curva di potenza di circa V ^ 2.2, sebbene la trasformazione "accurata" sia:

sRGB a Lineare

Dove V´ è il canale R, G o B con codifica gamma di sRGB.
pseudocodice:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4));
        }
    }

Terzo passaggio:

Per trovare la luminanza (Y) applicare i coefficienti standard per sRGB:

Applicare i coefficienti Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Pseudocodice usando le funzioni sopra:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

PER TROVARE LA LEGGEREZZA PERCEPITA:

Quarto passaggio:

Prendi la luminanza Y dall'alto e trasformala in L *

L * dall'equazione Y.
pseudocodice:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L * è un valore compreso tra 0 (nero) e 100 (bianco) dove 50 è il "medio grigio" percettivo. L * = 50 è l'equivalente di Y = 18,4, ovvero una carta grigia al 18%, che rappresenta il centro di un'esposizione fotografica (zona V di Ansel Adams).

Riferimenti:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Domande frequenti sulla gamma di Charles Poynton


@Rotem grazie - Ho visto alcune dichiarazioni strane e incomplete e ho pensato che sarebbe stato utile inchiodarlo, in particolare perché questo thread è ancora molto alto nei motori di ricerca.
Myndex,

Ho creato una dimostrazione confrontando BT.601 Luma e CIE 1976 L * Perceptual Grey , usando alcuni comandi MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem

@Myndex Ho usato le tue formule per arrivare a L *, ma ottengo comunque strani risultati, qualunque sia la formula che uso ... Con la tua, L * di # d05858 è più scuro di L * di # c51c2a ... C'è qualche modo per farlo bene? Perché nessuna formula funziona come previsto? :(
sjahan

1
@asdfasdfads Sì, L*a*b*non tiene conto di una serie di attributi psicofisici. L'effetto Helmholtz-Kohlrausch è uno, ma ce ne sono molti altri. CIELAB non è in alcun modo un modello di valutazione dell'immagine "completo". Nel mio post stavo cercando di coprire i concetti di base il più completamente possibile senza avventurarmi nelle minuzie molto profonde. Il modello Hunt, i modelli Fairchild e altri svolgono un lavoro più completo, ma sono anche sostanzialmente più complessi.
Myndex,

1
@Myndex, non importa, la mia implementazione è stata basata sulla fatica e i miei scarsi risultati sono venuti da questo :( Grazie mille per il vostro aiuto e il vostro post che è di grande valore!
sjahan

11

Ho trovato questo codice (scritto in C #) che fa un ottimo lavoro nel calcolare la "luminosità" di un colore. In questo scenario, il codice sta provando a determinare se inserire testo bianco o nero sul colore.


1
Questo è esattamente ciò di cui avevo bisogno. Stavo facendo una demo classica delle "barre dei colori" e volevo etichettarli sopra il colore con la migliore scelta in bianco e nero!
RufusVS,

10

È interessante notare che questa formulazione per RGB => HSV utilizza solo v = MAX3 (r, g, b). In altre parole, puoi usare il massimo di (r, g, b) come V in HSV.

Ho controllato e a pagina 575 di Hearn & Baker questo è il modo in cui calcolano anche "Valore".

Da Hearn & Baker pag. 319


Solo per la cronaca il link è morto, versione di archivio qui - web.archive.org/web/20150906055359/http://…
Peter

L'HSV non è percettivamente uniforme (e non è nemmeno vicino). È usato solo come un modo "conveniente" per regolare il colore, ma non è rilevante per la percezione e la V non si riferisce al valore reale di L o Y (Luminanza CIE).
Myndex,

9

Piuttosto che perdersi tra la selezione casuale di formule menzionate qui, ti suggerisco di scegliere la formula raccomandata dagli standard W3C.

Ecco un'implementazione PHP semplice ma esatta delle formule relative di luminanza e rapporto di contrasto WCAG 2.0 SC 1.4.3 . Produce valori appropriati per la valutazione dei rapporti richiesti per la conformità WCAG, come in questa pagina , e come tali è adatto e appropriato per qualsiasi app Web. Questo è banale da portare in altre lingue.

/**
 * Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
 * @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
 * @param string $col A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function relativeluminance($col) {
    //Remove any leading #
    $col = trim($col, '#');
    //Convert 3-digit to 6-digit
    if (strlen($col) == 3) {
        $col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
    }
    //Convert hex to 0-1 scale
    $components = array(
        'r' => hexdec(substr($col, 0, 2)) / 255,
        'g' => hexdec(substr($col, 2, 2)) / 255,
        'b' => hexdec(substr($col, 4, 2)) / 255
    );
    //Correct for sRGB
    foreach($components as $c => $v) {
        if ($v <= 0.04045) {
            $components[$c] = $v / 12.92;
        } else {
            $components[$c] = pow((($v + 0.055) / 1.055), 2.4);
        }
    }
    //Calculate relative luminance using ITU-R BT. 709 coefficients
    return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}

/**
 * Calculate contrast ratio acording to WCAG 2.0 formula
 * Will return a value between 1 (no contrast) and 21 (max contrast)
 * @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 * @param string $c1 A 3 or 6-digit hex colour string
 * @param string $c2 A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <marcus@synchromedia.co.uk>
 */
function contrastratio($c1, $c2) {
    $y1 = relativeluminance($c1);
    $y2 = relativeluminance($c2);
    //Arrange so $y1 is lightest
    if ($y1 < $y2) {
        $y3 = $y1;
        $y1 = $y2;
        $y2 = $y3;
    }
    return ($y1 + 0.05) / ($y2 + 0.05);
}

perché preferisci la definizione di w3c? personalmente ho implementato sia CCIR 601 che il w3c consigliato e sono rimasto molto più soddisfatto dei risultati CCIR 601
user151496

1
Perché, come ho detto, è raccomandato sia dal W3C che dal WCAG?
Synchro

1
La formula del W3C non è corretta su un numero di livelli. Non sta prendendo in considerazione la percezione umana, stanno usando un contrasto "semplice" usando una luminanza che è lineare e per nulla uniforme dal punto di vista percettivo. Tra le altre cose, sembra che lo abbiano basato su alcuni standard del 1988 (!!!) che non sono rilevanti oggi (tali standard erano basati su monitor monocromatici come il verde / nero e si riferivano al contrasto totale da acceso a spento , senza considerare la scala di grigi né i colori).
Myndex,

1
Questa è spazzatura completa. Luma è specificamente percettivo - ecco perché ha diversi coefficienti per rosso, verde e blu. L'età non ha nulla a che fare con esso: l'eccellente spazio colore percettivo CIE Lab risale al 1976. Lo spazio W3C non è altrettanto buono, tuttavia è una buona approssimazione pratica che è facile da calcolare. Se hai qualcosa di costruttivo da offrire, pubblica quello invece di critiche vuote.
Synchro,

3
Solo per aggiungere / aggiornare : stiamo attualmente ricercando algoritmi di sostituzione che modellano meglio il contrasto percettivo (discussione nel numero 695 di Github) . Tuttavia, come FYI di emissione separata, la soglia per sRGB è 0,04045 e non 0,03928 a cui si fa riferimento da una bozza sRGB obsoleta. L'autorevole IEC std utilizza 0.04045 e una richiesta pull è imminente per correggere questo errore nel WCAG. (rif: IEC 61966-2-1: 1999) Questo è nel numero 360 di Github, anche se per citare, in 8 bit non vi è alcuna differenza effettiva - vicino alla fine del thread 360 ho grafici di errori tra cui 0,04045 / 0,03928 in 8 bit.
Myndex,

8

Per aggiungere ciò che hanno detto tutti gli altri:

Tutte queste equazioni funzionano piuttosto bene nella pratica, ma se devi essere molto preciso devi prima convertire il colore in uno spazio cromatico lineare (applica una gamma di immagini inversa), fai la media ponderale dei colori primari e - se vuoi visualizza il colore - riporta la luminanza nella gamma del monitor.

La differenza di luminanza tra la gamma ignorante e la gamma corretta è fino al 20% nei grigi scuri.


2

Stavo risolvendo un compito simile oggi in JavaScript. Ho optato per questa getPerceivedLightness(rgb)funzione per un colore HEX RGB. Si occupa dell'effetto Helmholtz-Kohlrausch tramite la formula di Fairchild e Perrotta per la correzione della luminanza.

/**
 * Converts RGB color to CIE 1931 XYZ color space.
 * https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
 * @param  {string} hex
 * @return {number[]}
 */
export function rgbToXyz(hex) {
    const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
    const X =  0.4124 * r + 0.3576 * g + 0.1805 * b
    const Y =  0.2126 * r + 0.7152 * g + 0.0722 * b
    const Z =  0.0193 * r + 0.1192 * g + 0.9505 * b
    // For some reason, X, Y and Z are multiplied by 100.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Undoes gamma-correction from an RGB-encoded color.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * /programming/596216/formula-to-determine-brightness-of-rgb-color
 * @param  {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Send this function a decimal sRGB gamma encoded color value
    // between 0.0 and 1.0, and it returns a linearized value.
    if (color <= 0.04045) {
        return color / 12.92
    } else {
        return Math.pow((color + 0.055) / 1.055, 2.4)
    }
}

/**
 * Converts hex color to RGB.
 * /programming/5623838/rgb-to-hex-and-hex-to-rgb
 * @param  {string} hex
 * @return {number[]} [rgb]
 */
function hexToRgb(hex) {
    const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    if (match) {
        match.shift()
        return match.map(_ => parseInt(_, 16))
    }
}

/**
 * Converts CIE 1931 XYZ colors to CIE L*a*b*.
 * The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param   {number[]} color The CIE 1931 XYZ color to convert which refers to
 *                           the D65/2° standard illuminant.
 * @returns {number[]}       The color in the CIE L*a*b* color space.
 */
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
  [x, y, z] = [x, y, z].map((v, i) => {
    v = v / D65[i]
    return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
  })
  const l = 116 * y - 16
  const a = 500 * (x - y)
  const b = 200 * (y - z)
  return [l, a, b]
}

/**
 * Converts Lab color space to Luminance-Chroma-Hue color space.
 * http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
 * @param  {number[]}
 * @return {number[]}
 */
export function labToLch([l, a, b]) {
    const c = Math.sqrt(a * a + b * b)
    const h = abToHue(a, b)
    return [l, c, h]
}

/**
 * Converts a and b of Lab color space to Hue of LCH color space.
 * /programming/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
 * @param  {number} a
 * @param  {number} b
 * @return {number}
 */
function abToHue(a, b) {
    if (a >= 0 && b === 0) {
        return 0
    }
    if (a < 0 && b === 0) {
        return 180
    }
    if (a === 0 && b > 0) {
        return 90
    }
    if (a === 0 && b < 0) {
        return 270
    }
    let xBias
    if (a > 0 && b > 0) {
        xBias = 0
    } else if (a < 0) {
        xBias = 180
    } else if (a > 0 && b < 0) {
        xBias = 360
    }
    return radiansToDegrees(Math.atan(b / a)) + xBias
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI)
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180
}

/**
 * Saturated colors appear brighter to human eye.
 * That's called Helmholtz-Kohlrausch effect.
 * Fairchild and Pirrotta came up with a formula to
 * calculate a correction for that effect.
 * "Color Quality of Semiconductor and Conventional Light Sources":
 * https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
 * @return {number}
 */
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
    const l_ = 2.5 - 0.025 * l
    const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
    return l + l_ * g * c
}

export function getPerceivedLightness(hex) {
    return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}

1

Lo spazio dei colori HSV dovrebbe fare il trucco, vedi l' articolo di Wikipedia a seconda della lingua in cui stai lavorando potresti ottenere una conversione della libreria.

H è tonalità che è un valore numerico per il colore (cioè rosso, verde ...)

S è la saturazione del colore, ovvero quanto è "intenso"

V è la "luminosità" del colore.


7
Il problema con lo spazio colore HSV è che puoi avere la stessa saturazione e valore, ma tonalità diverse, per il blu e il giallo . Il giallo è molto più luminoso del blu. Lo stesso vale per HSL.
Ian Boyd,

hsv ti dà la "luminosità" di un colore in senso tecnico. in una luminosità percettiva hsv fallisce davvero
user151496

HSV e HSL non sono percettivamente precisi (e non sono nemmeno vicini). Sono utili per i "controlli" per la regolazione del colore relativo, ma non per la previsione accurata della luminosità percettiva. Usa L * di CIELAB per leggerezza percettiva.
Myndex,

1

Valore di luminanza RGB = 0,3 R + 0,59 G + 0,11 B

http://www.scantips.com/lumin.html

Se stai cercando quanto è vicino al bianco il colore, puoi usare Euclidean Distance da (255, 255, 255)

Penso che lo spazio cromatico RGB sia percettivamente non uniforme rispetto alla distanza euclidea L2. Gli spazi uniformi includono CIE LAB e LUV.


1

La formula di gamma inversa di Jive Dadson deve essere rimossa per metà quando viene implementata in Javascript, ovvero il ritorno dalla funzione gam_sRGB deve essere return int (v * 255); non restituisce int (v * 255 + .5); La semiregolazione arrotonda per eccesso e ciò può causare un valore uno troppo alto su una triade di colore grigio R = G = B. La conversione in scala di grigi su una triade R = G = B dovrebbe produrre un valore uguale a R; è una prova che la formula è valida. Vedi Nove sfumature di scala di grigi per la formula in azione (senza la mezza regolazione).


Sembra che tu sappia le tue cose, quindi ho rimosso il +0.5
Jive Dadson il

Ho fatto l'esperimento. In C ++ ha bisogno del +0.5, quindi lo rimetto a posto. Ho aggiunto un commento sulla traduzione in altre lingue.
Jive Dadson,

1

Mi chiedo come siano stati determinati quei coefficienti rgb. Ho fatto un esperimento me stesso e ho finito con il seguente:

Y = 0.267 R + 0.642 G + 0.091 B

Vicino ma ovviamente diverso dai coefficienti ITU consolidati. Mi chiedo se quei coefficienti potrebbero essere diversi per ogni singolo osservatore, perché tutti potremmo avere una diversa quantità di coni e bastoncelli sulla retina nei nostri occhi, e in particolare il rapporto tra i diversi tipi di coni potrebbe differire.

Per riferimento:

ITU BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU BT.601:

Y = 0.299 R + 0.587 G + 0.114 B

Ho fatto il test spostando rapidamente una piccola barra grigia su uno sfondo rosso brillante, verde brillante e blu brillante e regolando il grigio fino a quando non si fondeva il più possibile. Ho anche ripetuto quel test con altre tonalità. Ho ripetuto il test su diversi display, anche uno con un fattore gamma fisso di 3.0, ma a me sembra tutto uguale. Inoltre, i coefficienti ITU sono letteralmente sbagliati per i miei occhi.

E sì, presumibilmente ho una visione dei colori normale.


Nei tuoi esperimenti hai linearizzato per rimuovere prima il componente gamma? In caso contrario, ciò potrebbe spiegare i risultati. MA ANCHE, i coefficienti sono correlati agli esperimenti CIE 1931 e quelli sono in media 17 osservatori, quindi sì, c'è una varianza individuale nei risultati.
Myndex,

1

Ecco un po 'di codice C che dovrebbe calcolare correttamente la luminanza percepita.

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}

0

Si prega di definire la luminosità. Se stai cercando quanto è vicino al bianco il colore, puoi usare Euclidean Distance da (255, 255, 255)


1
No, non puoi usare la distanza euclidea tra i valori di sRGB, sRGB non è uno spazio cartesiano / vettoriale percettivamente uniforme. Se si desidera utilizzare la distanza euclidea come misura della differenza di colore, è necessario almeno convertirsi in CIELAB, o meglio ancora, utilizzare una CAM come CIECAM02.
Myndex,

0

La "V" di HSV è probabilmente ciò che stai cercando. MATLAB ha una funzione rgb2hsv e l'articolo di Wikipedia precedentemente citato è pieno di pseudocodice. Se una conversione RGB2HSV non è possibile, un modello meno accurato sarebbe la versione in scala di grigi dell'immagine.


0

Questo collegamento spiega tutto in profondità, incluso il motivo per cui tali costanti del moltiplicatore esistono prima dei valori R, G e B.

Modifica: ha una spiegazione anche a una delle risposte qui (0.299 * R + 0.587 * G + 0.114 * B)


0

Per determinare la luminosità di un colore con R, converto il colore di sistema RGB in colore di sistema HSV.

Nel mio script, utilizzo prima il codice di sistema HEX per altri motivi, ma puoi iniziare anche con il codice di sistema RGB con rgb2hsv {grDevices}. La documentazione è qui .

Ecco questa parte del mio codice:

 sample <- c("#010101", "#303030", "#A6A4A4", "#020202", "#010100")
 hsvc <-rgb2hsv(col2rgb(sample)) # convert HEX to HSV
 value <- as.data.frame(hsvc) # create data.frame
 value <- value[3,] # extract the information of brightness
 order(value) # ordrer the color by brightness

0

Per chiarezza, devono essere le formule che usano una radice quadrata

sqrt(coefficient * (colour_value^2))

non

sqrt((coefficient * colour_value))^2

La prova di ciò risiede nella conversione di una triade R = G = B in scala di grigi R. Questo sarà vero solo se si quadrano il valore del colore, non il valore del colore per il coefficiente. Vedi Nove sfumature di scala di grigi


5
ci sono mistmatch tra parentesi
log0

a meno che il coefficiente utilizzato non sia la radice quadrata del coefficiente corretto.
RufusVS,
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.