2009-02-27 40 views
308

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 agregar los valores RGB juntos y tener sumas más altas ser más brillante, pero estoy algo perdido en cuanto a dónde empezar.Fórmula para determinar el brillo del color RGB

+7

brillo percibido es lo que pienso que estoy buscando, gracias. – robmerica

+2

Hay un buen artículo ([Manipulación de colores en .NET - Parte 1] (http://www.codeproject.com/Articles/19045/Manipulating-colors-in-NET-Part-1)) sobre espacios de color y conversaciones entre ellos, incluida la teoría y el código (C#). Para la respuesta mire *** Conversión entre modelos *** tema en el artículo. – underscore

+0

He sido miembro por muchos años, y nunca he hecho esto antes. ¿Puedo sugerir que revise las respuestas y vuelva a pensar cuál aceptar? –

Respuesta

363

¿Quiere decir brillo? ¿Brillo percibido? Luminancia?

  • luminancia (de serie en determinados 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 (percepción de la opción 2, más lenta para 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]
+17

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. –

+0

Sí, todo depende de la aplicación. Todos estos modelos, incluida la percepción subjetiva humana ... – Anonymous

+13

Tenga en cuenta también que todos estos son probablemente para 0-1 RGB lineales, y es probable que tenga gamma-corrected 0-255 RGB. No se convierten como cree que son. –

0

Defina el brillo. Si está buscando qué tan cerca del blanco es el color, puede usar Euclidean Distance desde (255, 255, 255)

246

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

fotométrica/digital de 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 usted está dispuesto a la precisión de perfomance de comercio, hay dos fórmulas de aproximación para este:

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 
+31

Me gusta que ponga valores precisos, pero también incluye un atajo rápido tipo "lo suficientemente cerca". +1. – Beska

+1

¿Cómo es que los valores "calculados rápidamente" no incluyen azul en la aproximación? –

+2

@Jonathan Dumaine - las dos fórmulas de cálculo rápidas ambas incluyen azul - 1er es (2 * Rojo + 'Azul' + 3 * Verde)/6, 2do es (3 * Rojo +' Azul' + 4 * Verde)> > 3. concedido, en ambas aproximaciones rápidas, Azul tiene el peso más bajo, pero todavía está allí. –

1

El espacio de color HSV debe hacer el truco, vea t él wikipedia article dependiendo del idioma en el que trabaje, puede obtener una conversión de biblioteca.

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

S es la saturación del color, es decir, cómo 'intensa' es

V es el 'brillo' del color.

+7

El problema con el espacio de color HSV es que puede tener la misma saturación y valor, pero con diferentes matices, para ** azul ** y ** amarillo **. El amarillo es ** mucho ** más brillante que el azul. Lo mismo ocurre con HSL. –

+0

hsv le da el "brillo" de un color en un sentido técnico. en un brillo perceptual hsv realmente falla – user151496

1

RGB valor de luminancia = 0,3 R + G + 0,59 0,11 B

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

Si usted está buscando lo cerca que el color blanco es que se puede utilizar la distancia euclídea entre (255, 255 , 255)

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

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.

6

Para añadir lo que dijeron todos los demás:

Todas estas ecuaciones trabajan un poco bien en la práctica, pero si tiene que ser muy precisa lo que tiene que convertir primero el color al espacio de color lineal (se aplican inversa imagen-gamma), haga el promedio ponderado de los colores primarios y, si desea visualizar el color, vuelva a tomar la luminancia en el monitor gamma.

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

9

Encontré this code (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 poner texto blanco o negro sobre el color.

+1

Eso es exactamente lo que necesitaba. Estaba haciendo una demostración clásica de "barras de color", y quería etiquetarlas en la parte superior del color con la mejor opción en blanco y negro. – RufusVS

1

This link explica todo en profundidad, incluyendo por qué esas constantes de multiplicador existen antes de los valores R, G y B.

Editar: Tiene una explicación a una de las respuestas aquí también (0.299 * R + 0,587 * 0,114 * G + B)

26

A continuación es el único algoritmo correcto para imágenes sRGB, tal como se utiliza en los navegadores etc.

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 un 20%.

Para cosas típicas de computadora, 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 aproxima 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) 
    ); 
} 
+0

¿Por qué usaste una función compuesta para aproximar el exponente? ¿Por qué no hacer un cálculo directo? Gracias – JMD

+5

Así es como se define sRGB. Creo que la razón es que evita algunos problemas numéricos cercanos a cero. No haría mucha diferencia si elevaras los números a los poderes de 2.2 y 1/2.2. –

+6

JMD: como parte del trabajo en un laboratorio de percepción visual, he realizado mediciones de luminancia directa en monitores CRT y puedo confirmar que hay una región lineal de luminancia en la parte inferior del rango de valores. –

10

Curiosamente, this formulation for RGB=>HSV sólo utiliza v = MAX3 (r, g, b). En otras palabras, puede usar como máximo de (r, g, b) como la V en HSV.

Lo revisé y en la página 575 de Hearn & Baker esta es la forma en que calculan "Valor" también.

From Hearn&Baker pg 319

+0

Solo para el registro, el enlace está muerto, versión de archivo aquí - https://web.archive.org/web/20150906055359/http://en.literateprograms.org/RGB_to_HSV_space_conversion_(C) – Peter

8

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

Aquí hay una implementación de PHP sencilla pero exacta de las fórmulas WCAG 2.0 SC 1.4.3relative luminance y contrast ratio. Produce valores que son apropiados para evaluar las relaciones requeridas para el cumplimiento de WCAG, como en this page, 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.03928) { 
      $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); 
} 
+0

¿por qué prefiere la definición de w3c?Personalmente, he implementado tanto el CCIR 601 como el w3c recomendado y estuve mucho más satisfecho con los resultados del CCIR 601 – user151496

+0

¿Porque, como dije, es recomendado tanto por el W3C como por las WCAG? – Synchro

82

He hecho la comparación de los tres algoritmos en la respuesta aceptada. Genere colores en el ciclo donde solo se utilizaban aproximadamente 400 colores. Cada color está representado por 2x2 píxeles, los colores se ordenan de más oscuro a más claro (de izquierda a derecha, de arriba a abajo).

primera imagen - Luminance (relative)

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 

tercera foto - HSP Color Model

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

cuarta foto - WCAG 2.0 SC 1.4.3relative luminance yFórmula(ver @Synchro's respuesta)

El patrón a veces se puede observar en la 1ª y la 2da imagen dependiendo del número de colores en una fila. Nunca vi ningún patrón en la imagen del 3er o 4to algoritmo.

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

Perceived brightness algorithm comparison

+2

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 opción", ya que también es más rápida que la 4ta que es una más – GameDeveloper

+3

Su imagen de comparación es incorrecta porque no proporcionó la entrada correcta para todas las funciones. La primera función requiere _linear_ entrada RGB; Solo puedo reproducir el efecto de anillado proporcionando RGB no lineal (es decir, corregido con gamma). Al corregir este problema, no obtienes artefactos de anillado y la primera función es el claro ganador. – Max

1

La fórmula inversa-gamma por Jive Dadson necesita tener el medio a ajustar retira cuando se implementa en Javascript, es decir, el retorno de la función gam_sRGB necesario que haya retorno int (v * 255); not return int (v * 255 + .5); Los redondeos de ajuste a la mitad, y esto pueden causar un valor uno demasiado alto en un R = G = B, es decir tríada de color gris. La conversión en escala de grises en una tríada R = G = B debe producir un valor igual a R; es una prueba de que la fórmula es válida. Ver Nine Shades of Greyscale para la fórmula en acción (sin el medio ajuste).

+0

Parece que sabes lo que tienes, así que eliminé el +0.5 –

+0

que hice el experimento. En C++ necesita el +0.5, así que lo vuelvo a poner. Agregué un comentario sobre la traducción a otros idiomas. –

0

Para mayor claridad, las fórmulas que utilizan una necesidad raíz cuadrada para ser

sqrt(coefficient * (colour_value^2)

no

sqrt((coefficient * colour_value)^2

La prueba de esto radica en la conversión de un R = G = B tríada a escala de grises R. Eso solo será cierto si cuadra el valor del color, no el valor del color por coeficiente. Ver Nine Shades of Greyscale

+5

hay paréntesis coincidentes – log0

+0

a menos que el coeficiente que utiliza sea la raíz cuadrada del coeficiente correcto. – RufusVS

1

Aquí hay un poco de código C que debe 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 
} 
0

Me pregunto cómo se determinaron esos coeficientes de rgb. Hice un experimento a mí mismo y que terminó con la siguiente:

Y = 0.267 R + 0.642 G + 0.091 B 

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

Como referencia:

UIT BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B 

UIT 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 azul verde y rojo brillante, brillante brillante, y ajustando el gris hasta que mezclaba tanto como sea 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 a mí me parece igual. Además, los coeficientes de ITU literalmente son incorrectos para mis ojos.

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

0

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

En mi secuencia de comandos, uso 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 es here.

Aquí es 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 
Cuestiones relacionadas