Fórmula para determinar o brilho da cor RGB

387

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

robmerica
fonte
8
Brilho percebido é o que acho que estou procurando, obrigado.
28909 robmerica
2
Há um bom artigo ( Manipulando cores no .NET - Parte 1 ) sobre espaços de cores e conversas entre eles, incluindo a teoria e o código (C #). Para obter a resposta, consulte o tópico Conversão entre modelos no artigo.
sublinhado
4
Sou membro há muitos anos e nunca fiz isso antes. Posso sugerir que você reveja as respostas e repense qual delas aceitar?
Jive Dadson

Respostas:

456

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

  • Luminância (padrão para determinados espaços de cor): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminância (opção percebida 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminância (opção percebida 2, 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]
Anônimo
fonte
26
Observe que ambos enfatizam os aspectos fisiológicos: o globo ocular humano é mais sensível à luz verde, menos ao vermelho e menos ao azul.
Bob Cruz
16
Observe também que tudo isso é provavelmente para linear 0-1 RGB e você provavelmente tem 0-255 RGB corrigido por gama. Eles não são convertidos como você pensa.
alex estranha
4
Incorreto. Antes de aplicar a transformação linear, é preciso primeiro aplicar a inversa da função gama para o espaço de cores. Depois de aplicar a função linear, a função gama é aplicada.
precisa
6
Na última fórmula, é (0,299 * R) ^ 2 ou é 0,299 * (R ^ 2)?
Kaizer Sozay
3
@KaizerSozay Como está escrito aqui, isso significaria 0.299*(R^2)(porque exponenciação vai antes de multiplicação)
Dantevg
298

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

Fotométrico / digital ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

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

Y = 0.299 R + 0.587 G + 0.114 B

Se você deseja trocar precisão por desempenho, há 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
Franci Penov
fonte
47
Eu gosto que você insira valores precisos, mas também incluiu um atalho rápido do tipo "perto o suficiente". +1.
27009 Beska
3
@ Jonathan Dumaine - as duas fórmulas de cálculo rápido incluem azul - a primeira é (2 * vermelho + Blue+ 3 * verde) / 6, a segunda é (3 * vermelho + Blue+ * * verde) >> 3. concedido, em ambas as aproximações rápidas, o azul tem o menor peso, mas ainda está lá.
Franci Penov
84
@JonathanDumaine Isso porque o olho humano é menos perspicaz para azul ;-)
Christopher Oezbek
4
A versão rápida funciona bem. Testado e aplicado a aplicativos do mundo real com milhares de usuários, tudo parece bem.
milosmns
10
A versão rápida é ainda mais rápida se você fizer o seguinte: Y = (R<<1+R+G<<2+B)>>3(são apenas 3-4 ciclos de CPU no ARM), mas acho que um bom compilador fará essa otimização para você.
Rjmunro 11/03/2015
105

Fiz comparação dos três algoritmos na resposta aceita. Gerei cores em ciclo, onde apenas cerca de 400 cores foram usadas. Cada cor é representada por 2x2 pixels, as cores são classificadas do mais escuro ao mais claro (da esquerda para a direita, de cima para baixo).

1ª foto - Luminância (relativa)

0.2126 * R + 0.7152 * G + 0.0722 * B

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

0.299 * R + 0.587 * G + 0.114 * B

Terceira foto - HSP Color Model

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

4ª foto - WCAG 2.0 SC 1.4.3 fórmula de luminância relativa e taxa de contraste (veja a resposta da @ Synchro aqui )

Às vezes, o padrão pode ser visto na 1ª e 2ª figuras, dependendo do número de cores em uma linha. Eu nunca vi nenhum padrão na imagem do terceiro ou quarto algoritmo.

Se eu tivesse que escolher, eu usaria o algoritmo número 3, já que é muito mais fácil de implementar e é cerca de 33% mais rápido que o quarto.

Comparação do algoritmo de brilho percebido

Petr Hurtak
fonte
3
Para mim, esta é a melhor resposta, pois você usa um padrão de imagem que permite perceber se diferentes tons são renderizados com a mesma luminância. Para mim e meu monitor atual da 3ª imagem é o "mais bonito", uma vez que também é mais rápido, em seguida, 4 que é um plus
CoffeDeveloper
8
Sua imagem de comparação está incorreta porque você não forneceu a entrada correta para todas as funções. A primeira função requer entrada RGB linear ; Só posso reproduzir o efeito de faixas fornecendo RGB não linear (ou seja, com correção de gama). Corrigindo esse problema, você não obtém artefatos de faixas e a 1ª função é a clara vencedora.
Max
11
@Max ^2e sqrtincluído na terceira fórmula são uma maneira mais rápida de aproximar o RGB linear do RGB não linear em vez do ^2.2e ^(1/2.2)isso seria mais correto. Usar entradas não lineares em vez de lineares é extremamente comum, infelizmente.
Mark Ransom
53

Abaixo está o único algoritmo CORRETO para converter imagens sRGB, usadas em navegadores etc., em escala de cinza.

É necessário aplicar uma inversa 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 não 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. Gamma para sRGB é uma função composta que aproxima a exponenciação por 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)
    );
}
Jive Dadson
fonte
5
É assim que o sRGB é definido. Penso que o motivo é que evita alguns problemas numéricos próximos de zero. Não faria muita diferença se você apenas aumentasse os números para as potências de 2.2 e 1 / 2.2.
precisa
8
JMD - como parte do trabalho em um laboratório de percepção visual, fiz medições diretas de luminância em monitores CRT e posso confirmar que existe uma região linear de luminância na parte inferior da faixa de valores.
precisa
2
Eu sei que isso é muito antigo, mas ainda está lá para ser pesquisado. Eu não acho que pode estar correto. Cinza (255.255.255) não deve = cinza (255,0,0) + cinza (0,255,0) + cinza (0,0,255)? Não faz.
DCBillen
2
@DCBillen: não, como os valores estão no espaço sRGB corrigido por gama não linear, você não pode adicioná-los. Se você quiser adicioná-los, faça isso antes de chamar gam_sRGB.
Rdb
11
@DCBillen Rdb está correto. A maneira de adicioná-los é mostrada na função int grey (int r, int g, int b), que "desativa" gam_sRGB. Dói-me que, após quatro anos, a resposta correta seja classificada tão baixa. :-) Na verdade não ... eu vou superar isso.
precisa
45

A resposta "Aceita" está incorreta e incompleta

As únicas respostas precisas são as respostas @ jive-dadson e @EddingtonsMonkey , e no suporte @ nils-pipenbrinck . As outras respostas (incluindo as aceitas) estão vinculando ou citando fontes erradas, irrelevantes, obsoletas ou quebradas.

Resumidamente:

  • O sRGB deve ser LINEARIZADO antes de aplicar os coeficientes.
  • A luminância (L ou Y) é linear e a luz.
  • A leveza percebida (L *) não é linear, assim como a percepção humana.
  • HSV e HSL não são nem 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 inicial obsoleto).
  • Para serem úteis (isto é, relativas à percepção) , as distâncias euclidianas requerem um espaço vetorial cartesiano perceptivamente uniforme, como o CIELAB. O sRGB não é um deles.

A seguir, uma resposta correta e completa:

Como esse tópico aparece muito nos mecanismos de pesquisa, estou adicionando esta resposta para esclarecer os vários equívocos sobre o assunto.

O brilho é um atributo perceptivo, não possui uma medida direta.

A luminosidade percebida é medida por alguns modelos de visão como CIELAB, aqui L * (Lstar) é uma medida da luminosidade perceptiva e não é linear para aproximar a curva de resposta não linear da visão humana.

A luminância é uma medida linear da luz, ponderada espectralmente para a visão normal, mas não ajustada para a percepção não linear da luminosidade.

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

A curva gama ou transferência (TRC) é uma curva que geralmente é semelhante à curva perceptiva e é comumente aplicada a dados de imagem para armazenamento ou transmissão, a fim de reduzir o ruído percebido e / ou melhorar a utilização dos dados (e razões relacionadas).

Para determinar a luminosidade percebida , primeiro converta os valores da imagem R´G´B´ codificados por gama em luminância linear ( Lou Y) e depois em luminosidade percebida não linear ( L*)


PARA ENCONTRAR A 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 em gama em um valor linear. O sRGB (padrão do computador), por exemplo, requer uma curva de potência de aproximadamente V ^ 2,2, embora a transformação "precisa" seja:

sRGB para Linear

Onde V 'é o canal R, G ou B codificado por gama de 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:

Aplique os coeficientes Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Pseudocódigo usando as 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 em L *

L * da equação Y
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), em que 50 é o "cinza do meio" perceptivo. L * = 50 é o equivalente de Y = 18,4, ou seja, um cartão cinza de 18%, representando o meio de uma exposição fotográfica (zona V de Ansel Adams).

Referências:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Perguntas frequentes sobre a gama de Charles Poynton

Myndex
fonte
@Rotem obrigado - eu vi algumas frases estranhas e incompletas e achei que seria útil identificá-las, principalmente porque esse tópico ainda é altamente classificado nos mecanismos de pesquisa.
Myndex 20/07/19
Criei uma demonstração comparando o BT.601 Luma e o CIE 1976 L * Perceptual Gray , usando alguns comandos do MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex Eu usei suas fórmulas para chegar a L *, mas ainda assim obtive alguns resultados estranhos, qualquer que seja a fórmula que eu use ... Com a sua, L * de # d05858 é mais escuro que L * de # c51c2a ... Existe algum maneira de fazer isso certo? Por que nenhuma fórmula funciona como esperado? :(
sjahan 5/01
11
@asdfasdfads Sim, L*a*b*não leva em consideração vários atributos psicofísicos. O efeito Helmholtz-Kohlrausch é um, mas existem muitos outros. O CIELAB não é um modelo de avaliação de imagem "completo" por qualquer meio. No meu post, eu estava tentando cobrir os conceitos básicos da maneira mais completa possível, sem me aventurar nas minúcias muito profundas. O modelo de Hunt, os modelos de Fairchild e outros fazem um trabalho mais completo, mas também são substancialmente mais complexos.
Myndex
11
@Myndex, deixa pra lá, minha implementação foi baseada em fadiga e meus maus resultados vieram disso :( Muito obrigado por sua ajuda e por seu post, que é de grande valor!
sjahan
11

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 texto branco ou preto sobre a cor.

sitesbyjoe
fonte
11
Isso é exatamente o que eu precisava. Eu estava fazendo uma demo clássica de "barras de cores" e queria rotulá-las em cima da cor com a melhor opção em preto ou branco!
precisa saber é o seguinte
10

Curiosamente, 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 o V no HSV.

Eu verifiquei e, na página 575 da Hearn & Baker, é assim que eles calculam "Valor" também.

De Hearn & Baker, pág. 319

bobobobo
fonte
Apenas para o registro o link está morto, versão de arquivo aqui - web.archive.org/web/20150906055359/http://...
Peter
O HSV não é perceptualmente uniforme (e nem chega perto). É usado apenas como uma maneira "conveniente" de ajustar a cor, mas não é relevante para a percepção, e o V não se relaciona com o valor real de L ou Y (CIE Luminance).
Myndex
9

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

Aqui está uma implementação simples, porém exata, do PHP das fórmulas de luminância relativa e relação de contraste das WCAG 2.0 SC 1.4.3 . Produz valores adequados para avaliar as proporções necessárias para a conformidade com as WCAG, como nesta página , e, como tal, são adequadas e apropriadas para qualquer aplicativo 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 <[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
fonte
por que você prefere a definição w3c? Pessoalmente, tenho implementado tanto CCIR 601 e W3C recomendou um e eu estava muito mais satisfeito com os resultados CCIR 601
user151496
11
Porque, como eu disse, é recomendado pelo W3C e pelo WCAG?
Synchro 16/03
11
A fórmula do W3C está incorreta em vários níveis. Não está levando em consideração a percepção humana, eles estão usando um contraste "simples", usando uma luminância linear e nada perceptivamente uniforme. Entre outras coisas, parece que eles o basearam em alguns padrões tão antigos quanto 1988 (!!!) que não são relevantes hoje (esses padrões foram baseados em monitores monocromáticos como verde / preto e referiram-se ao contraste total de ligar para desligar , sem considerar a escala de cinza nem as cores).
Myndex
11
Isso é lixo total. Luma é especificamente perceptiva - é por isso que tem coeficientes diferentes para vermelho, verde e azul. A idade não tem nada a ver com isso - o excelente espaço de cores perceptivo do CIE Lab data de 1976. O espaço W3C não é tão bom, mas é uma boa aproximação prática que é fácil de calcular. Se você tem algo construtivo a oferecer, publique isso em vez de críticas vazias.
Synchro
3
Apenas para adicionar / atualizar : atualmente estamos pesquisando algoritmos de substituição que melhor modelam o contraste perceptivo (discussão na edição 695 do Github) . No entanto, como uma questão separada para o EFY , o limite para sRGB é 0,04045 , e não 0,03928, que foi referenciado a partir de um rascunho obsoleto do sRGB anterior. O IEC autoritário std usa 0,04045 e uma solicitação de recebimento é apresentada para corrigir esse erro no WCAG. (ref: IEC 61966-2-1: 1999) Isso está na edição 360 do Github, no entanto, para mencionar, em 8 bits não há diferença real - próximo ao final do encadeamento 360 eu tenho gráficos de erros, incluindo 0,04045 / 0,03928 em 8 bits.
Myndex 19/06/19
8

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 você precisa converter a cor em um espaço linear de cores (aplicar imagem-gama inversa), fazer a média ponderada das cores primárias e - se quiser exibir a cor - leve a luminância de volta para a gama do monitor.

A diferença de luminância entre ignorar gama e executar uma gama adequada é de até 20% no cinza escuro.

Nils Pipenbrinck
fonte
2

Hoje eu estava resolvendo uma tarefa semelhante em javascript. Eu decidi nessa getPerceivedLightness(rgb)função para uma cor RGB HEX. Ele lida com o efeito Helmholtz-Kohlrausch via fórmula de Fairchild e Perrotta para correção da luminância.

/**
 * 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))))
}
catanfetamina
fonte
1

O espaço de cores HSV deve fazer o truque, consulte o artigo da wikipedia dependendo do idioma em que você está trabalhando, você pode obter uma conversão de biblioteca.

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

S é a saturação da cor, ou seja, quão 'intensa' é

V é o 'brilho' da cor.

Ian Hopkinson
fonte
7
O problema com o espaço de cores HSV é que você pode ter a mesma saturação e valor, mas com matizes diferentes, para azul e amarelo . Amarelo é muito mais brilhante que azul. O mesmo vale para HSL.
Ian Boyd
O hsv fornece o "brilho" de uma cor em um sentido técnico. em um HSV brilho perceptual realmente falhar
user151496
HSV e HSL não são perceptivamente precisos (e nem chega perto). Eles são úteis para "controles" para ajustar a cor relativa, mas não para a previsão precisa da luminosidade perceptiva. Use L * da CIELAB para obter leveza perceptiva.
Myndex 19/06/19
1

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 branca está, 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. Espaços uniformes incluem CIE LAB e LUV.


fonte
1

A fórmula gama-inversa de Jive Dadson precisa remover o meio-ajuste quando implementada em Javascript, ou seja, o retorno da função gam_sRGB precisa ser return int (v * 255); não retorna int (v * 255 + 0,5); O meio-ajuste é arredondado para cima, e isso pode causar um valor muito alto em uma tríade R = G = B, ou seja, cinza. A conversão em escala de cinza em uma tríade R = G = B deve produzir um valor igual a R; é uma prova de que a fórmula é válida. Consulte Nove tons de escala de cinza para obter a fórmula em ação (sem o meio ajuste).

Dave Collier
fonte
Parece que você conhece as suas coisas, então eu removi o +0,5
Jive Dadson 15/17
Eu fiz o experimento. Em C ++, ele precisa de +0,5, então eu o coloquei novamente. Adicionei um comentário sobre a tradução para outros idiomas.
Jive Dadson
1

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

Perto, mas obviamente diferente dos coeficientes ITU estabelecidos há muito tempo. Eu me pergunto se esses coeficientes poderiam 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 ser diferente.

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

Eu 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 ele se misturasse o máximo possível. Também repeti esse teste com outros tons. Repeti o teste em telas diferentes, mesmo com um fator gama fixo de 3,0, mas tudo parece o mesmo para mim. Além do mais, os coeficientes da UIT literalmente estão errados aos meus olhos.

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

vórtice
fonte
Em seus experimentos, você linearizou para remover o componente gama primeiro? Caso contrário, isso poderia explicar seus resultados. MAS TAMBÉM, os coeficientes estão relacionados às experiências da CIE 1931 e são uma média de 17 observadores; portanto, sim, há uma variação individual nos resultados.
Myndex 19/06/19
1

Aqui está um pouco do 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
}
EddingtonsMonkey
fonte
0

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

Ben S
fonte
11
Não, você não pode usar a distância euclidiana entre os valores de sRGB, o sRGB não é um espaço cartesiano / vetorial perceptivamente uniforme. Se você deseja usar a distância euclidiana como uma medida da diferença de cores, é necessário converter pelo menos para CIELAB ou, melhor ainda, usar um CAM como CIECAM02.
Myndex 19/06/19
0

O 'V' do HSV é provavelmente o que você está procurando. O MATLAB possui uma função rgb2hsv e o artigo da wikipedia mencionado 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.

Jacob
fonte
0

Este link explica tudo em profundidade, incluindo o motivo pelo qual essas constantes multiplicadoras existem antes dos valores R, G e B.

Edit: Tem uma explicação para uma das respostas aqui também (0,299 * R + 0,587 * G + 0,114 * B)

dsignr
fonte
0

Para determinar o brilho de uma cor com R, converto 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 iniciar também com o código do sistema RGB com rgb2hsv {grDevices}. A documentação está 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
Pierre-louis Stenger
fonte
0

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 R. na escala de cinzas. Isso só será verdade se você quadrado o valor da cor, não o valor da cor vezes o coeficiente. Veja Nove tons de escala de cinza

Dave Collier
fonte
5
existem mistmatches parêntese
log0
a menos que o coeficiente que você usa seja a raiz quadrada do coeficiente correto.
RufusVS