Pergunta

Estou procurando algum tipo de fórmula ou algoritmo para determinar o brilho de uma cor, dados os valores RGB. Sei que não pode ser tão simples quanto adicionar os valores de RGB e ter somas mais altas, mas estou meio que sem saber por onde começar.

Foi útil?

Solução

Você quer dizer brilho? Brilho percebido? Luminância?

  • Luminância (padrão para certos espaços de cores): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminância (opção 1 percebida): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminância (opção 2 percebida, mais 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 ) (graças a @Matthewherbst) [3]

Outras dicas

Eu acho que o que você está procurando é o RGB -> Luma Fórmula de conversão.

Fotométrico/digital ITU BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (dá mais peso aos componentes R e B):

Y = 0.299 R + 0.587 G + 0.114 B

Se você estiver disposto a negociar precisão por perfoma, existem duas fórmulas de aproximação para esta:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Estes podem ser calculados rapidamente como

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

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

Fiz a comparação dos três algoritmos na resposta aceita. Eu gerei cores no ciclo, onde apenas cada 400ª cor era usada. Cada cor é representada por 2x2 pixels, as cores são classificadas de mais escuras a mais claras (da esquerda para a direita, de cima para baixo).

1ª foto - Luminância (parente)

0.2126 * R + 0.7152 * G + 0.0722 * B

2ª foto - http://www.w3.org/tr/aert#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

3ª imagem - Modelo de cores HSP

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

4ª foto - WCAG 2.0 SC 1.4.3 Luminância relativa e taxa de contraste Fórmula (ver @Synchro's responda aqui)

Às vezes, o padrão pode ser avistado na 1ª e 2ª imagem, dependendo do número de cores em uma linha. Eu nunca vi nenhum padrão na figura do 3º ou 4º algoritmo.

Se eu tivesse que escolher, iria com o algoritmo número 3, pois é muito mais fácil de implementar e é cerca de 33% mais rápido que o 4º.

Perceived brightness algorithm comparison

Abaixo está o único algoritmo correto para converter imagens SRGB, conforme usado em navegadores etc., em escala de cinza.

É necessário aplicar um inverso da função gama para o espaço de cores antes de calcular o produto interno. Em seguida, você aplica a função gama ao valor reduzido. A falha na incorporação da função gama pode resultar em erros de até 20%.

Para coisas típicas do computador, o espaço de cores é SRGB. Os números certos para SRGB são aprox. 0,21, 0,72, 0,07. O gama para SRGB é uma função composta que se aproxima da exponenciação em 1/(2.2). Aqui está a coisa toda em 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)
    );
}

Interessantemente, Esta formulação para RGB => HSV Apenas usa v = max3 (r, g, b). Em outras palavras, você pode usar o máximo de (r, g, b) como v no hsv.

Eu verifiquei e na página 575 de Hearn & Baker É assim que eles computam "valor" também.

From Hearn&Baker pg 319

eu encontrei este código (escrito em C#) que faz um excelente trabalho ao calcular o "brilho" de uma cor. Nesse cenário, o código está tentando determinar se deve colocar o texto branco ou preto sobre a cor.

Para adicionar o que todos os outros disseram:

Todas essas equações funcionam bem na prática, mas se você precisar ser muito preciso, primeiro converte a cor em espaço de cores lineares (aplique a gama de imagem inversa), faça a média de peso das cores primárias e - se você quiser Exiba a cor - leve a luminância de volta à gama do monitor.

A diferença de luminância entre a ingestão de gama e a gama adequada é de até 20% nos cinzas escuros.

Em vez de se perder entre a seleção aleatória de fórmulas mencionadas aqui, sugiro que você vá para a fórmula recomendada pelos padrões W3C.

Aqui está uma implementação direta, mas exata PHP, do WCAG 2.0 SC 1.4.3 Luminância relativa e taxa de contraste fórmulas. Produz valores apropriados para avaliar as proporções necessárias para a conformidade com o WCAG, como em esta página, e, como tal, é adequado e apropriado para qualquer aplicativo da web. Isso é trivial para portar para outros 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 <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.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 <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);
}

A resposta "aceita" está incorreta e incompleta

As únicas respostas precisas são as @Jive-Dadson e @Eddingtonsmonkey respostas, e em apoio @Nils-Pipenbrinck. As outras respostas (incluindo o aceito) estão ligando ou citando fontes erradas, irrelevantes, obsoletas ou quebradas.

Brevemente:

  • SRGB deve ser Linearizado Antes de aplicar os coeficientes.
  • A luminância (L ou Y) é linear como é leve.
  • A leveza percebida (L*) é não linear, assim como a percepção humana.
  • HSV e HSL nem são remotamente precisos em termos de percepção.
  • O padrão IEC para SRGB especifica um limite de 0,04045 é NÃO 0,03928 (que era de um rascunho antecipado obsoleto).
  • Ser útil (ou seja, em relação à percepção), As distâncias euclidianas requerem um espaço vetorial cartesiano perceptivamente uniforme, como o Cielab. SRGB não é um.

O que se segue é uma resposta correta e completa:

Como esse tópico aparece altamente nos mecanismos de pesquisa, estou adicionando esta resposta para esclarecer os vários conceitos errôneos sobre o assunto.

Brilho é um atributo perceptivo, ele não tem uma medida direta.

Leveza percebida é medido por alguns modelos de visão, como Cielab, aqui L* (lStar) é uma medida de leveza perceptiva, e não é linear a aproximar a curva de resposta não linear da visão humana.

Luminância é uma medida linear de luz, ponderada espectralmente para a visão normal, mas não ajustada para a percepção não linear de leveza.

Luma ( Prime) é um sinal ponderado codificado e gama usado em algumas codificações de vídeo. Não deve ser confundido com a luminância linear.

Gama ou a curva de transferência (TRC) é uma curva que geralmente é semelhante à curva perceptiva e é comumente aplicada aos dados da imagem para armazenamento ou transmissão para reduzir o ruído percebido e/ou melhorar a utilização de dados (e motivos relacionados).

Para determinar a leveza percebida, primeiro converter valores de imagem codificados gama em luminância linear (L ou Y ) e então para a leveza percebida não linear (L*)


Para encontrar luminância:

... porque aparentemente estava perdido em algum lugar ...

Passo um:

Converta todos os valores inteiros SRGB de 8 bits em decimal 0,0-1.0

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

Passo dois:

Converta um RGB codificado gama em um valor linear. SRGB (padrão do computador), por exemplo, requer uma curva de energia de aproximadamente V^2.2, embora a transformação "precisa" seja:

sRGB to Linear

Onde V´ é o canal R, G ou B codificado por gama do SRGB.
Pseudo-có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));
        }
    }

Passo três:

Para encontrar a luminância (y), aplique os coeficientes padrão para SRGB:

Apply coefficients Y = R * 0.2126 + G * 0.7152 + B *  0.0722

Pseudocódigo usando funções acima:

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

Para encontrar leveza percebida:

Etapa quatro:

Pegue a luminância y de cima e transforme -se para l*

L* from Y equation
Pseudo-có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* é um valor de 0 (preto) a 100 (branco), onde 50 é o perceptivo "cinza médio". L* = 50 é o equivalente a y = 18,4, ou, em outras palavras, uma carta cinza de 18%, representando o meio de uma exposição fotográfica (Ansel Adams Zone V).

Referências:

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

O espaço de cores do HSV deve fazer o truque, veja o Artigo da Wikipedia Dependendo do idioma em que você está trabalhando, você pode obter uma conversão da biblioteca.

H é tonalidade, que é um valor numérico para a cor (ou seja, vermelho, verde ...)

S é a saturação da cor, ou seja, como é 'intenso'

V é o 'brilho' da cor.

Valor de luminância RGB = 0,3 R + 0,59 g + 0,11 b

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

Se você está procurando o quão perto da cor é a cor, você pode usar a distância euclidiana de (255, 255, 255)

Eu acho que o espaço de cores RGB é perceptivamente não uniforme em relação à distância euclidiana L2. Os espaços uniformes incluem o CIE LAB e LUV.

A fórmula inversa-gama de Jive Dadson precisa remover o meio-ajuste quando implementado em JavaScript, ou seja, o retorno da função GAM_SRGB precisa ser retornado int (v*255); não retornar int (v*255+.5); Meio adajusta, e isso pode causar um valor muito alto em uma tríade de cor cinza r = g = b ie. A conversão em escala de cinza em R = g = b Tríade deve produzir um valor igual a r; É uma prova de que a fórmula é válida. Ver Nove tons de escala de cinza para a fórmula em ação (sem o meio ajuste).

Aqui está um pouco de código C que deve calcular corretamente a luminância percebida.

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

Eu me pergunto como esses coeficientes RGB foram determinados. Eu mesmo fiz um experimento e acabei com o seguinte:

Y = 0.267 R + 0.642 G + 0.091 B

Próximo, mas obviamente, mas obviamente diferente dos coeficientes da UTU estabelecidos há muito tempo. Gostaria de saber se esses coeficientes podem ser diferentes para cada observador, porque todos nós podemos ter uma quantidade diferente de cones e hastes na retina em nossos olhos, e especialmente a proporção entre os diferentes tipos de cones pode diferir.

Para referência:

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

Fiz o teste movendo rapidamente uma pequena barra cinza em um fundo vermelho brilhante, verde brilhante e azul brilhante e ajustando o cinza até que ela se misturasse o máximo possível. Eu também repeti esse teste com outros tons. Repeti o teste em telas diferentes, mesmo uma com um fator gama fixo de 3,0, mas tudo parece o mesmo para mim. Além disso, os coeficientes da ITU literalmente estão errados para os meus olhos.

E sim, presumivelmente tenho uma visão de cores normal.

Por favor, defina brilho. Se você está procurando o quão perto da cor é a cor, você pode usar Distância euclidiana De (255, 255, 255)

O 'V' do HSV é provavelmente o que você está procurando. O MATLAB possui uma função RGB2HSV e o artigo da Wikipedia citado anteriormente está cheio de pseudocódigo. Se uma conversão RGB2HSV não for viável, um modelo menos preciso seria a versão em escala de cinza da imagem.

Esse link Explica tudo em profundidade, incluindo por que essas constantes multiplicadoras existem antes dos valores R, G e B.

Edit: Ele também tem uma explicação para uma das respostas aqui (0,299*r + 0,587*g + 0,114*b)

Para determinar o brilho de uma cor com r, converti a cor do sistema RGB na cor do sistema HSV.

No meu script, eu uso o código do sistema Hex antes por outro motivo, mas você pode começar também com o código do sistema RGB com rgb2hsv {grDevices}. A documentação é aqui.

Aqui está esta parte do meu 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

Para maior clareza, as fórmulas que usam uma raiz quadrada precisam ser

sqrt(coefficient * (colour_value^2))

não

sqrt((coefficient * colour_value))^2

A prova disso está na conversão de uma tríade r = g = b em escala de escala Greys, que só será verdadeira se você encaixar o valor da cor, não o coeficiente de tempos de valor de cor. Ver Nove tons de escala de cinza

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top