Fórmula para determinar el brillo del color RGB.

387

Estoy buscando algún tipo de fórmula o algoritmo para determinar el brillo de un color dados los valores RGB. Sé que no puede ser tan simple como sumar los valores RGB y que las sumas más altas sean más brillantes, pero no sé por dónde empezar.

robmerica
fuente
8
El brillo percibido es lo que creo que estoy buscando, gracias.
robmerica
2
Hay un buen artículo ( Manipulación de colores en .NET - Parte 1 ) sobre espacios de color y conversaciones entre ellos, incluida la teoría y el código (C #). Para la respuesta, vea el tema Conversión entre modelos en el artículo.
subrayar
44
He sido miembro durante muchos años, y nunca antes había hecho esto. ¿Puedo sugerirle que revise las respuestas y vuelva a pensar cuál aceptar?
Jive Dadson

Respuestas:

456

¿Te refieres al brillo? Brillo percibido? Luminancia?

  • Luminancia (estándar para ciertos espacios de color): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminancia (opción percibida 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminancia (opción percibida 2, más lenta de calcular): 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 )(gracias a @MatthewHerbst ) [3]
Anónimo
fuente
26
Tenga en cuenta que ambos enfatizan los aspectos fisiológicos: el globo ocular humano es más sensible a la luz verde, menos al rojo y menos al azul.
Bob Cross
16
Tenga en cuenta también que todos estos son probablemente para 0-1 RGB lineal, y probablemente tenga 0-255 RGB con corrección gamma. No se convierten como crees que son.
alex extraño
44
Incorrecto. Antes de aplicar la transformación lineal, primero se debe aplicar el inverso de la función gamma para el espacio de color. Luego, después de aplicar la función lineal, se aplica la función gamma.
Jive Dadson
66
En la última fórmula, ¿es (0.299 * R) ^ 2 o es 0.299 * (R ^ 2)?
Kaizer Sozay
3
@KaizerSozay Como está escrito aquí significaría 0.299*(R^2)(porque la exponenciación va antes de la multiplicación)
Dantevg
298

Creo que lo que estás buscando es la fórmula de conversión RGB -> Luma .

Fotométrico / digital ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (da más peso a los componentes R y B):

Y = 0.299 R + 0.587 G + 0.114 B

Si está dispuesto a cambiar la precisión por el rendimiento, hay dos fórmulas de aproximación para esta:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Estos se pueden calcular rápidamente como

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

Y = (R+R+R+B+G+G+G+G)>>3
Franci Penov
fuente
47
Me gusta que pongas valores precisos, pero también incluiste un atajo rápido tipo "lo suficientemente cerca". +1.
Beska
3
@ Jonathan Dumaine: las dos fórmulas de cálculo rápido incluyen azul: la primera es (2 * Rojo + Blue+ 3 * Verde) / 6, la segunda es (3 * Rojo + Blue+ 4 * Verde) >> 3. concedido, en ambas aproximaciones rápidas, el azul tiene el peso más bajo, pero sigue ahí.
Franci Penov
84
@JonathanDumaine Eso es porque el ojo humano es menos perceptivo con el azul ;-)
Christopher Oezbek
44
La versión rápida funciona bien. Probado y aplicado a aplicaciones del mundo real con miles de usuarios, todo se ve bien.
milosmns
10
La versión rápida es aún más rápida si lo hace como: Y = (R<<1+R+G<<2+B)>>3(eso es solo 3-4 ciclos de CPU en ARM) pero supongo que un buen compilador hará esa optimización por usted.
rjmunro
105

He hecho una comparación de los tres algoritmos en la respuesta aceptada. Generé colores en un ciclo donde solo se usaba cada 400 colores. Cada color está representado por 2x2 píxeles, los colores se ordenan del más oscuro al más claro (de izquierda a derecha, de arriba a abajo).

Primera imagen - Luminancia (relativa)

0.2126 * R + 0.7152 * G + 0.0722 * B

Segunda imagen: http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

3a imagen - Modelo de color HSP

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

4a imagen: WCAG 2.0 SC 1.4.3 fórmula relativa de luminancia y relación de contraste (consulte la respuesta de @ Synchro aquí )

El patrón a veces se puede ver en la primera y segunda imagen según la cantidad de colores en una fila. Nunca vi ningún patrón en la imagen del tercer o cuarto algoritmo.

Si tuviera que elegir, usaría el algoritmo número 3, ya que es mucho más fácil de implementar y es aproximadamente un 33% más rápido que el 4to.

Comparación del algoritmo de brillo percibido

Petr Hurtak
fuente
3
Para mí, esta es la mejor respuesta porque oyu usa un patrón de imagen que le permite percibir si se representan diferentes tonos con la misma luminancia. Para mí y mi monitor actual, la tercera imagen es la "mejor futuro", ya que también es más rápido entonces cuarto que es un plus
CoffeDeveloper
8
Su imagen de comparación es incorrecta porque no proporcionó la entrada correcta a todas las funciones. La primera función requiere entrada RGB lineal ; Solo puedo reproducir el efecto de bandas proporcionando RGB no lineal (es decir, con corrección de gamma). Al corregir este problema, no obtiene artefactos de bandas y la primera función es la clara ganadora.
Max
1
@Max the ^2e sqrtincluidos en la tercera fórmula son una forma más rápida de aproximar RGB lineal de RGB no lineal en lugar de ^2.2y ^(1/2.2)eso sería más correcto. Desafortunadamente, el uso de entradas no lineales en lugar de lineales es extremadamente común.
Mark Ransom
53

A continuación se muestra el único algoritmo CORRECTO para convertir imágenes sRGB, como se usa en navegadores, etc., en escala de grises.

Es necesario aplicar un inverso de la función gamma para el espacio de color antes de calcular el producto interno. Luego aplica la función gamma al valor reducido. Si no se incorpora la función gamma, pueden producirse errores de hasta el 20%.

Para material informático típico, el espacio de color es sRGB. Los números correctos para sRGB son aprox. 0.21, 0.72, 0.07. Gamma para sRGB es una función compuesta que se aproxima a la exponenciación en 1 / (2.2). Aquí está todo en 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)
    );
}
Jive Dadson
fuente
55
Así es como se define sRGB. Creo que la razón es que evita algunos problemas numéricos cerca de cero. No habría mucha diferencia si elevara los números a las potencias de 2.2 y 1 / 2.2.
Jive Dadson
8
JMD: como parte del trabajo en un laboratorio de percepción visual, he realizado mediciones directas de luminancia en monitores CRT y puedo confirmar que hay una región lineal de luminancia en la parte inferior del rango de valores.
Jerry Federspiel
2
Sé que esto es muy antiguo, pero todavía está ahí para ser buscado. No creo que pueda ser correcto. ¿No debería gris (255,255,255) = gris (255,0,0) + gris (0,255,0) + gris (0,0,255)? No lo hace.
DCBillen
2
@DCBillen: no, dado que los valores están en un espacio sRGB con corrección gamma no lineal, no puede simplemente sumarlos. Si desea agregarlos, debe hacerlo antes de llamar a gam_sRGB.
rdb
1
@DCBillen Rdb es correcto. La forma de sumarlos se muestra en la función int grey (int r, int g, int b), que "deshabilita" gam_sRGB. Me duele que después de cuatro años, la respuesta correcta sea tan baja. :-) En realidad no ... Lo superaré.
Jive Dadson
45

La respuesta "aceptada" es incorrecta e incompleta

Las únicas respuestas que son precisas son las respuestas @ jive-dadson y @EddingtonsMonkey , y en soporte @ nils-pipenbrinck . Las otras respuestas (incluidas las aceptadas) se vinculan o citan fuentes que son incorrectas, irrelevantes, obsoletas o rotas.

Brevemente:

  • sRGB debe estar LINEALIZADO antes de aplicar los coeficientes.
  • La luminancia (L o Y) es lineal al igual que la luz.
  • La ligereza percibida (L *) es no lineal como lo es la percepción humana.
  • HSV y HSL ni siquiera son remotamente precisos en términos de percepción.
  • El estándar IEC para sRGB especifica un umbral de 0.04045 NO es 0.03928 (que era de un borrador anterior obsoleto).
  • Para ser útiles (es decir, en relación con la percepción) , las distancias euclidianas requieren un espacio vectorial cartesiano perceptivamente uniforme como CIELAB. sRGB no es uno.

Lo que sigue es una respuesta correcta y completa:

Debido a que este hilo aparece altamente en los motores de búsqueda, estoy agregando esta respuesta para aclarar los diversos conceptos erróneos sobre el tema.

El brillo es un atributo perceptual, no tiene una medida directa.

La ligereza percibida se mide mediante algunos modelos de visión como CIELAB, aquí L * (Lstar) es una medida de la ligereza perceptiva y no es lineal para aproximar la curva de respuesta no lineal de la visión humana.

La luminancia es una medida lineal de la luz, espectralmente ponderada para la visión normal pero no ajustada para la percepción no lineal de la luminosidad.

Luma ( Y ' prime) es una señal ponderada codificada en gamma utilizada en algunas codificaciones de video. No debe confundirse con la luminancia lineal.

La curva de transferencia o gamma (TRC) es una curva que a menudo es similar a la curva perceptiva, y se aplica comúnmente a los datos de imagen para almacenamiento o transmisión para reducir el ruido percibido y / o mejorar la utilización de datos (y razones relacionadas).

Para determinar la luminosidad percibida , primero convierta los valores de imagen R´G´B´ codificados con gamma en luminancia lineal ( Lo Y) y luego en luminosidad percibida no lineal ( L*)


PARA ENCONTRAR LA LUMINANCIA:

... Porque aparentemente se perdió en alguna parte ...

Paso uno:

Convierta todos los valores enteros de 8 bits sRGB a decimal 0.0-1.0

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

Segundo paso:

Convierta un RGB codificado en gamma a un valor lineal. sRGB (estándar de la computadora), por ejemplo, requiere una curva de potencia de aproximadamente V ^ 2.2, aunque la transformación "precisa" es:

sRGB a lineal

Donde V´ es el canal R, G o B codificado en gamma de sRGB.
Pseudocódigo:

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

Paso tres:

Para encontrar la luminancia (Y) aplique los coeficientes estándar para sRGB:

Aplicar coeficientes Y = R * 0.2126 + G * 0.7152 + B * 0.0722

Pseudocódigo que utiliza las funciones anteriores:

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

PARA ENCONTRAR LA LUZ PERCIBIDA:

Paso cuatro:

Tome la luminancia Y desde arriba y transfórmela en L *

L * de la ecuación Y
Pseudocódigo:

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 * es un valor de 0 (negro) a 100 (blanco) donde 50 es el "gris medio" perceptivo. L * = 50 es el equivalente de Y = 18.4, o en otras palabras, una tarjeta gris al 18%, que representa la mitad de una exposición fotográfica (zona V de Ansel Adams).

Referencias

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Preguntas frecuentes sobre Gamma de Charles Poynton

Myndex
fuente
@Rotem gracias: vi algunas declaraciones extrañas e incompletas y sentí que sería útil precisarlo, especialmente porque este hilo todavía ocupa un lugar destacado en los motores de búsqueda.
Myndex
Creé una demostración comparando BT.601 Luma y CIE 1976 L * Perceptual Gray , usando algunos comandos MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex Usé sus fórmulas para llegar a L *, pero aún obtengo algunos resultados extraños, cualquiera que sea la fórmula que use ... Con la suya, L * de # d05858 es más oscuro que L * de # c51c2a ... ¿Hay alguna manera de hacer esto bien? ¿Por qué ninguna fórmula funciona como se esperaba? :(
sjahan
1
@asdfasdfads Sí, L*a*b*no tiene en cuenta una serie de atributos psicofísicos. El efecto Helmholtz-Kohlrausch es uno, pero hay muchos otros. CIELAB no es un modelo de evaluación de imagen "completo" de ninguna manera. En mi publicación estaba tratando de cubrir los conceptos básicos lo más completamente posible sin aventurarme en las minucias muy profundas. El modelo Hunt, los modelos de Fairchild y otros hacen un trabajo más completo, pero también son sustancialmente más complejos.
Myndex
1
@Myndex, no importa, mi implementación se basó en la fatiga y mis malos resultados vinieron de eso :( ¡Muchas gracias por su ayuda y su publicación, que es de gran valor!
Sjahan
11

Encontré este código (escrito en C #) que hace un excelente trabajo al calcular el "brillo" de un color. En este escenario, el código está tratando de determinar si se debe colocar texto blanco o negro sobre el color.

sitiosbyjoe
fuente
1
Eso es exactamente lo que necesitaba. ¡Estaba haciendo una demostración clásica de "barras de colores", y quería etiquetarlas sobre el color con la mejor opción en blanco y negro!
RufusVS
10

Curiosamente, esta formulación para RGB => HSV solo usa v = MAX3 (r, g, b). En otras palabras, puedes usar el máximo de (r, g, b) como V en HSV.

Verifiqué y en la página 575 de Hearn & Baker, así es como también calculan el "Valor".

De Hearn & Baker pg 319

bobobobo
fuente
Solo para el registro, el enlace está muerto, versión de archivo aquí - web.archive.org/web/20150906055359/http://…
Peter
HSV no es perceptualmente uniforme (y ni siquiera está cerca). Se usa solo como una forma "conveniente" de ajustar el color, pero no es relevante para la percepción, y la V no se relaciona con el verdadero valor de L o Y (luminancia CIE).
Myndex
9

En lugar de perderse entre la selección aleatoria de fórmulas mencionadas aquí, le sugiero que elija la fórmula recomendada por los estándares del W3C.

Aquí hay una implementación PHP sencilla pero exacta de las fórmulas de relación de luminancia relativa y contraste WCAG 2.0 SC 1.4.3 . Produce valores que son apropiados para evaluar las proporciones requeridas para el cumplimiento de WCAG, como en esta página , y como tal es adecuado y apropiado para cualquier aplicación web. Esto es trivial para portar a otros idiomas.

/**
 * 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 <[email protected]>
 */
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 <[email protected]>
 */
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);
}
Synchro
fuente
¿por qué preferirías la definición w3c? personalmente, he implementado CCIR 601 y el w3c me recomendó uno y estaba mucho más satisfecho con los resultados de CCIR 601
user151496
1
Porque, como dije, ¿lo recomiendan tanto el W3C como el WCAG?
Synchro
1
La fórmula W3C es incorrecta en varios niveles. No está tomando en cuenta la percepción humana, están usando un contraste "simple" usando una luminancia que es lineal y no es perceptiblemente uniforme. Entre otras cosas, parece que lo basaron en algunos estándares tan antiguos como 1988 (!!!) que no son relevantes hoy en día (esos estándares se basaron en monitores monocromos como verde / negro, y se refirieron al contraste total de encendido a apagado , sin tener en cuenta la escala de grises ni los colores).
Myndex
1
Eso es basura completa. Luma es específicamente perceptiva, es por eso que tiene diferentes coeficientes para rojo, verde y azul. La edad no tiene nada que ver con eso: el excelente espacio de color perceptual CIE Lab data de 1976. El espacio W3C no es tan bueno, sin embargo, es una buena aproximación práctica que es fácil de calcular. Si tiene algo constructivo que ofrecer, publíquelo en lugar de críticas vacías.
Synchro
3
Solo para agregar / actualizar : actualmente estamos investigando algoritmos de reemplazo que modelen mejor el contraste perceptual (discusión en Github Issue 695) . Sin embargo, como un tema separado para su información , el umbral para sRGB es 0.04045 , y no 0.03928 al que se hizo referencia desde un borrador obsoleto de sRGB. La norma IEC autorizada utiliza 0.04045 y se recibe una solicitud de extracción para corregir este error en el WCAG. (ref: IEC 61966-2-1: 1999) Esto está en el número 360 de Github, aunque para mencionar, en 8 bits no hay diferencia real: cerca del final del hilo 360 tengo gráficos de errores que incluyen 0.04045 / 0.03928 en 8 bits.
Myndex
8

Para agregar lo que todos los demás dijeron:

Todas estas ecuaciones funcionan bastante bien en la práctica, pero si necesita ser muy preciso, primero debe convertir el color en un espacio de color lineal (aplicar gamma de imagen inversa), hacer el promedio de peso de los colores primarios y, si desea muestra el color: recupera la luminancia en el monitor gamma.

La diferencia de luminancia entre ignorar gamma y hacer gamma adecuada es de hasta 20% en los grises oscuros.

Nils Pipenbrinck
fuente
2

Estaba resolviendo una tarea similar hoy en JavaScript. Me he decidido por esta getPerceivedLightness(rgb)función para un color HEX RGB. Se trata del efecto Helmholtz-Kohlrausch a través de la fórmula Fairchild y Perrotta para la corrección de luminancia.

/**
 * 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))))
}
catamphetamine
fuente
1

El espacio de color HSV debería ser el truco, consulte el artículo de Wikipedia, dependiendo del idioma en el que esté trabajando, puede obtener una conversión de biblioteca.

H es un tono que es un valor numérico para el color (es decir, rojo, verde ...)

S es la saturación del color, es decir, cuán 'intenso' es

V es el 'brillo' del color.

Ian Hopkinson
fuente
77
El problema con el espacio de color HSV es que puede tener la misma saturación y valor, pero diferentes tonos, para azul y amarillo . El amarillo es mucho más brillante que el azul. Lo mismo va para HSL.
Ian Boyd,
hsv le brinda el "brillo" de un color en un sentido técnico. en un brillo perceptual hsv realmente falla
user151496
HSV y HSL no son perceptivamente precisos (y ni siquiera están cerca). Son útiles para los "controles" para ajustar el color relativo, pero no para la predicción precisa de la ligereza perceptiva. Use L * de CIELAB para la ligereza perceptiva.
Myndex
1

Valor de luminancia RGB = 0.3 R + 0.59 G + 0.11 B

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

Si está buscando qué tan cerca está el color blanco, puede usar la distancia euclidiana desde (255, 255, 255)

Creo que el espacio de color RGB es perceptiblemente no uniforme con respecto a la distancia euclidiana L2. Los espacios uniformes incluyen CIE LAB y LUV.


fuente
1

La fórmula de gamma inversa de Jive Dadson necesita que se elimine el medio ajuste cuando se implementa en Javascript, es decir, el retorno de la función gam_sRGB debe ser devuelto int (v * 255); no devuelve int (v * 255 + .5); La mitad del ajuste se redondea hacia arriba, y esto puede causar un valor demasiado alto en un R = G = B, es decir, una tríada de color gris. La conversión en escala de grises en una tríada R = G = B debería producir un valor igual a R; Es una prueba de que la fórmula es válida. Ver Nueve sombras de escala de grises para la fórmula en acción (sin el medio ajuste).

Dave Collier
fuente
Parece que sabes tus cosas, así que eliminé el +0.5
Jive Dadson
Yo hice el experimento. En C ++ necesita el +0.5, así que lo puse de nuevo. Agregué un comentario sobre la traducción a otros idiomas.
Jive Dadson
1

Me pregunto cómo se determinaron esos coeficientes rgb. Hice un experimento yo mismo y terminé con lo siguiente:

Y = 0.267 R + 0.642 G + 0.091 B

Cercano pero obviamente diferente a los coeficientes ITU establecidos desde hace mucho tiempo. Me pregunto si esos coeficientes podrían ser diferentes para cada observador, porque todos podemos tener una cantidad diferente de conos y bastones en la retina en nuestros ojos, y especialmente la relación entre los diferentes tipos de conos puede diferir.

Para referencia:

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

Hice la prueba moviendo rápidamente una pequeña barra gris sobre un fondo rojo brillante, verde brillante y azul brillante, y ajustando el gris hasta que se mezclara lo más posible. También repetí esa prueba con otros tonos. Repetí la prueba en diferentes pantallas, incluso una con un factor de gamma fijo de 3.0, pero todo me parece igual. Más aún, los coeficientes ITU son literalmente incorrectos para mis ojos.

Y sí, presumiblemente tengo una visión normal del color.

vórtice
fuente
En sus experimentos, ¿linealizó para eliminar primero el componente gamma? Si no lo hiciera, eso podría explicar sus resultados. PERO TAMBIÉN, los coeficientes están relacionados con los experimentos CIE 1931 y esos son un promedio de 17 observadores, por lo que sí, hay una variación individual en los resultados.
Myndex
1

Aquí hay un poco de código C que debería calcular correctamente la luminancia percibida.

// 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
}
EddingtonsMonkey
fuente
0

Por favor, defina el brillo. Si está buscando qué tan cerca está el color blanco, puede usar la distancia euclidiana desde (255, 255, 255)

Ben S
fuente
1
No, no puede usar la distancia euclidiana entre los valores de sRGB, sRGB no es un espacio cartesiano / vectorial perceptualmente uniforme. Si desea usar la distancia euclidiana como una medida de la diferencia de color, necesita al menos convertir a CIELAB, o mejor aún, usar un CAM como CIECAM02.
Myndex
0

La 'V' de HSV es probablemente lo que estás buscando. MATLAB tiene una función rgb2hsv y el artículo de wikipedia citado anteriormente está lleno de pseudocódigo. Si una conversión RGB2HSV no es factible, un modelo menos preciso sería la versión en escala de grises de la imagen.

Jacob
fuente
0

Este enlace explica todo en profundidad, incluido por qué esas constantes multiplicadoras existen antes de los valores R, G y B.

Editar: tiene una explicación para una de las respuestas aquí también (0.299 * R + 0.587 * G + 0.114 * B)

dsignr
fuente
0

Para determinar el brillo de un color con R, convierto el color del sistema RGB en color del sistema HSV.

En mi script, utilizo el código del sistema HEX antes por otra razón, pero también puede comenzar con el código del sistema RGB con rgb2hsv {grDevices}. La documentación está aquí .

Aquí está esta parte de mi código:

 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
Pierre-louis Stenger
fuente
0

Para mayor claridad, las fórmulas que usan una raíz cuadrada deben ser

sqrt(coefficient * (colour_value^2))

no

sqrt((coefficient * colour_value))^2

La prueba de esto radica en la conversión de una tríada R = G = B a escala de grises R. Eso solo será cierto si cuadras el valor del color, no el valor del color por el coeficiente. Ver Nueve sombras de escala de grises

Dave Collier
fuente
55
hay emparejamientos erróneos entre paréntesis
log0
a menos que el coeficiente que utilice sea la raíz cuadrada del coeficiente correcto.
RufusVS