Formule pour déterminer la luminosité de la couleur RVB

387

Je cherche une sorte de formule ou d'algorithme pour déterminer la luminosité d'une couleur compte tenu des valeurs RVB. Je sais que cela ne peut pas être aussi simple que d'ajouter les valeurs RVB ensemble et que les sommes plus élevées soient plus lumineuses, mais je ne sais pas par où commencer.

robmerica
la source
8
La luminosité perçue est ce que je pense que je recherche, merci.
robmerica
2
Il existe un bon article ( Manipuler les couleurs dans .NET - Partie 1 ) sur les espaces colorimétriques et les conversations entre eux, y compris à la fois la théorie et le code (C #). Pour la réponse, consultez la rubrique Conversion entre modèles dans l'article.
souligner le
4
Je suis membre depuis de nombreuses années et je ne l'ai jamais fait auparavant. Puis-je vous suggérer de revoir les réponses et de repenser laquelle accepter?
Jive Dadson

Réponses:

456

Voulez-vous dire la luminosité? Luminosité perçue? Luminance?

  • Luminance (standard pour certains espaces colorimétriques): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminance (option perçue 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminance (option perçue 2, plus lente à calculer): 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 )(merci à @MatthewHerbst ) [3]
Anonyme
la source
26
Notez que les deux mettent l'accent sur les aspects physiologiques: le globe oculaire humain est le plus sensible à la lumière verte, moins au rouge et moins au bleu.
Bob Cross
16
Notez également que tous ces éléments sont probablement pour le RVB 0-1 linéaire, et vous avez probablement un RVB 0-255 corrigé gamma. Ils ne sont pas convertis comme vous le pensez.
alex étrange
4
Pas correcte. Avant d'appliquer la transformation linéaire, il faut d'abord appliquer l'inverse de la fonction gamma pour l'espace colorimétrique. Ensuite, après avoir appliqué la fonction linéaire, la fonction gamma est appliquée.
Jive Dadson
6
Dans la dernière formule, est-ce (0,299 * R) ^ 2 ou est-ce 0,299 * (R ^ 2)?
Kaizer Sozay
3
@KaizerSozay Comme il est écrit ici, cela signifierait 0.299*(R^2)(car l'exponentiation va avant la multiplication)
Dantevg
298

Je pense que ce que vous recherchez est la formule de conversion RVB -> Luma .

Photométrique / numérique ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (donne plus de poids aux composants R et B):

Y = 0.299 R + 0.587 G + 0.114 B

Si vous êtes prêt à échanger l'exactitude contre des performances, il existe deux formules d'approximation pour celle-ci:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Ceux-ci peuvent être calculés rapidement comme

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

Y = (R+R+R+B+G+G+G+G)>>3
Franci Penov
la source
47
J'aime que vous mettiez des valeurs précises, mais que vous ayez également inclus un raccourci de type "assez proche". +1.
Beska
3
@Jonathan Dumaine - les deux formules de calcul rapide incluent toutes les deux le bleu - le premier est (2 * rouge + Blue+ 3 * vert) / 6, le deuxième est (3 * rouge + Blue+ 4 * vert) >> 3. d'accord, dans les deux approximations rapides, Blue a le poids le plus bas, mais il est toujours là.
Franci Penov
84
@JonathanDumaine C'est parce que l'œil humain est le moins perceptif envers Blue ;-)
Christopher Oezbek
4
La version rapide fonctionne bien. Testé et appliqué à une application réelle avec des milliers d'utilisateurs, tout va bien.
milosmns
10
La version rapide est encore plus rapide si vous le faites comme: Y = (R<<1+R+G<<2+B)>>3(c'est seulement 3-4 cycles CPU sur ARM) mais je suppose qu'un bon compilateur fera cette optimisation pour vous.
rjmunro
105

J'ai fait une comparaison des trois algorithmes dans la réponse acceptée. J'ai généré des couleurs en cycle où seulement environ chaque 400e couleur a été utilisée. Chaque couleur est représentée par 2x2 pixels, les couleurs sont triées du plus foncé au plus clair (de gauche à droite, de haut en bas).

1ère photo - Luminance (relative)

0.2126 * R + 0.7152 * G + 0.0722 * B

2e photo - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

3e photo - Modèle couleur HSP

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

4ème image - WCAG 2.0 SC 1.4.3 formule de luminance relative et de rapport de contraste (voir @ Synchro's réponse de ici )

Le motif peut parfois être repéré sur la 1ère et la 2ème image en fonction du nombre de couleurs sur une ligne. Je n'ai jamais repéré de motif sur l'image du 3e ou du 4e algorithme.

Si je devais choisir, j'irais avec l'algorithme numéro 3 car il est beaucoup plus facile à mettre en œuvre et environ 33% plus rapide que le 4ème.

Comparaison de l'algorithme de luminosité perçue

Petr Hurtak
la source
3
Pour moi, c'est la meilleure réponse car vous utilisez un modèle d'image qui vous permet de voir si différentes teintes sont rendues avec la même luminance. Pour moi et mon moniteur actuel, la 3e image est la plus belle car elle est aussi plus rapide que la 4e, ce qui est un plus
CoffeDeveloper
8
Votre image de comparaison est incorrecte car vous n'avez pas fourni la bonne entrée à toutes les fonctions. La première fonction nécessite une entrée RVB linéaire ; Je ne peux reproduire l'effet de bande qu'en fournissant un RVB non linéaire (c'est-à-dire corrigé gamma). En corrigeant ce problème, vous n'obtenez aucun artefact de bande et la 1ère fonction est clairement gagnante.
Max
1
@Max the ^2et sqrtinclus dans la troisième formule sont un moyen plus rapide d'approximer RGB linéaire à partir de RGB non linéaire au lieu de ^2.2et ^(1/2.2)ce serait plus correct. Malheureusement, l'utilisation d'entrées non linéaires plutôt que linéaires est extrêmement courante.
Mark Ransom
53

Vous trouverez ci-dessous le seul algorithme CORRECT pour convertir les images sRGB, telles qu'utilisées dans les navigateurs, etc., en niveaux de gris.

Il est nécessaire d'appliquer une inverse de la fonction gamma pour l'espace colorimétrique avant de calculer le produit intérieur. Ensuite, vous appliquez la fonction gamma à la valeur réduite. Le fait de ne pas intégrer la fonction gamma peut entraîner des erreurs pouvant atteindre 20%.

Pour les trucs informatiques typiques, l'espace colorimétrique est sRGB. Les bons numéros pour sRGB sont d'env. 0,21, 0,72, 0,07. Le gamma pour sRGB est une fonction composite qui rapproche l'exponentiation de 1 / (2,2). Voici le tout 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
la source
5
C'est exactement ainsi que sRGB est défini. Je pense que la raison en est qu'elle évite certains problèmes numériques proches de zéro. Cela ne ferait pas beaucoup de différence si vous augmentiez simplement les nombres aux puissances de 2,2 et 1 / 2,2.
Jive Dadson
8
JMD - dans le cadre d'un travail dans un laboratoire de perception visuelle, j'ai effectué des mesures directes de luminance sur des moniteurs CRT et je peux confirmer qu'il existe une région linéaire de luminance au bas de la plage de valeurs.
Jerry Federspiel
2
Je sais que c'est très ancien, mais c'est toujours là pour être fouillé. Je ne pense pas que cela puisse être correct. Le gris (255,255,255) ne devrait-il pas = gris (255,0,0) + gris (0,255,0) + gris (0,0,255)? Ce n'est pas le cas.
DCBillen
2
@DCBillen: non, puisque les valeurs sont dans un espace sRGB à correction gamma non linéaire, vous ne pouvez pas simplement les additionner. Si vous souhaitez les ajouter, vous devez le faire avant d'appeler gam_sRGB.
rdb
1
@DCBillen Rdb est correct. La façon de les additionner est indiquée dans la fonction int gris (int r, int g, int b), qui "décompose" gam_sRGB. Cela me fait mal qu'au bout de quatre ans, la bonne réponse soit jugée si faible. :-) Pas vraiment .. je m'en remettrai.
Jive Dadson
45

La réponse «acceptée» est incorrecte et incomplète

Les seules réponses exactes sont les réponses @ jive-dadson et @EddingtonsMonkey , et à l'appui de @ nils-pipenbrinck . Les autres réponses (y compris celles acceptées) sont liées à ou citant des sources qui sont erronées, non pertinentes, obsolètes ou cassées.

Brièvement:

  • sRGB doit être LINÉARISÉ avant d'appliquer les coefficients.
  • La luminance (L ou Y) est linéaire, tout comme la lumière.
  • La légèreté perçue (L *) est non linéaire tout comme la perception humaine.
  • HSV et HSL ne sont même pas précis à distance en termes de perception.
  • La norme CEI pour sRGB spécifie un seuil de 0,04045 ce n'est PAS 0,03928 (qui provenait d'un premier projet obsolète).
  • Pour être utiles (c'est-à-dire par rapport à la perception) , les distances euclidiennes nécessitent un espace vectoriel cartésien perceptuellement uniforme tel que CIELAB. sRGB n'en est pas un.

Ce qui suit est une réponse correcte et complète:

Parce que ce fil apparaît fortement dans les moteurs de recherche, j'ajoute cette réponse pour clarifier les diverses idées fausses sur le sujet.

La luminosité est un attribut perceptuel, elle n'a pas de mesure directe.

La légèreté perçue est mesurée par certains modèles de vision tels que CIELAB, ici L * (Lstar) est une mesure de la légèreté perceptuelle , et est non linéaire pour approximer la courbe de réponse non linéaire de la vision humaine.

La luminance est une mesure linéaire de la lumière, spectralement pondérée pour la vision normale mais non ajustée pour la perception non linéaire de la luminosité.

Luma ( prime) est un signal pondéré codé gamma utilisé dans certains codages vidéo. Il ne faut pas le confondre avec la luminance linéaire.

La courbe gamma ou de transfert (TRC) est une courbe qui est souvent similaire à la courbe de perception, et est couramment appliquée aux données d'image pour le stockage ou la diffusion afin de réduire le bruit perçu et / ou d'améliorer l'utilisation des données (et les raisons connexes).

Pour déterminer la luminosité perçue , convertissez d'abord les valeurs d'image R´G´B´ codées gamma en luminance linéaire ( Lou Y) puis en luminosité perçue non linéaire ( L*)


POUR TROUVER LA LUMINANCE:

... Parce qu'apparemment il a été perdu quelque part ...

La première étape:

Convertir toutes les valeurs entières sRGB 8 bits en décimal 0,0-1,0

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

Deuxième étape:

Convertissez un RVB codé gamma en une valeur linéaire. sRGB (standard informatique), par exemple, nécessite une courbe de puissance d'environ V ^ 2,2, bien que la transformation "précise" soit:

sRGB à linéaire

Où V´ est le canal R, G ou B codé gamma de sRGB.
Pseudocode:

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

Troisième étape:

Pour trouver la luminance (Y), appliquez les coefficients standard pour sRGB:

Appliquer les coefficients Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Pseudocode utilisant les fonctions ci-dessus:

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

POUR TROUVER LA LÉGÈRETÉ PERÇUE:

Quatrième étape:

Prenez la luminance Y d'en haut et transformez-la en L *

L * de l'équation Y
Pseudocode:

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 * est une valeur de 0 (noir) à 100 (blanc) où 50 est le "gris moyen" perceptuel. L * = 50 est l'équivalent de Y = 18,4, c'est-à-dire une carte grise à 18%, représentant le milieu d'une exposition photographique (zone Ansel Adams V).

Références:

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

Myndex
la source
@Rotem merci - J'ai vu des déclarations étranges et incomplètes et j'ai pensé qu'il serait utile de les clouer, d'autant plus que ce fil se classe toujours très bien sur les moteurs de recherche.
Myndex
J'ai créé une démonstration comparant BT.601 Luma et CIE 1976 L * Perceptual Grey , en utilisant quelques commandes MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex J'ai utilisé vos formules pour arriver à L *, mais j'obtiens toujours des résultats étranges, quelle que soit la formule que j'utilise ... Avec la vôtre, L * de # d05858 est plus sombre que L * de # c51c2a ... moyen d'obtenir ce droit? Pourquoi aucune formule ne fonctionne-t-elle comme prévu? :(
sjahan
1
@asdfasdfads Oui, L*a*b*ne prend pas en compte un certain nombre d'attributs psychophysiques. L'effet Helmholtz-Kohlrausch en est un, mais il y en a beaucoup d'autres. CIELAB n'est en aucun cas un modèle d'évaluation d'image "complet". Dans mon article, j'essayais de couvrir les concepts de base aussi complètement que possible sans s'aventurer dans les minuties très profondes. Le modèle Hunt, les modèles Fairchild et d'autres font un travail plus complet, mais sont également beaucoup plus complexes.
Myndex
1
@Myndex, nevermind, mon implémentation était basée sur la fatigue et mes mauvais résultats en sont venus :( Merci beaucoup pour votre aide et votre message qui est d'une grande valeur!
sjahan
11

J'ai trouvé ce code (écrit en C #) qui fait un excellent travail de calcul de la "luminosité" d'une couleur. Dans ce scénario, le code tente de déterminer s'il faut mettre du texte blanc ou noir sur la couleur.

sitesbyjoe
la source
1
C'est exactement ce dont j'avais besoin. Je faisais une démo classique de "barres de couleur", et je voulais les étiqueter au-dessus de la couleur avec le meilleur choix en noir ou blanc!
RufusVS
10

Fait intéressant, cette formulation pour RGB => HSV utilise simplement v = MAX3 (r, g, b). En d'autres termes, vous pouvez utiliser le maximum de (r, g, b) comme V dans HSV.

J'ai vérifié et à la page 575 de Hearn & Baker, c'est ainsi qu'ils calculent également la "valeur".

De Hearn & Baker p. 319

bobobobo
la source
Juste pour mémoire, le lien est mort, version archive ici - web.archive.org/web/20150906055359/http://…
Peter
HSV n'est pas perceptuellement uniforme (et il n'est même pas proche). Il n'est utilisé que comme un moyen "pratique" d'ajuster la couleur, mais il n'est pas pertinent pour la perception, et le V n'est pas lié à la vraie valeur de L ou Y (Luminance CIE).
Myndex
9

Plutôt que de vous perdre parmi la sélection aléatoire de formules mentionnées ici, je vous suggère d'opter pour la formule recommandée par les normes W3C.

Voici une implémentation PHP simple mais exacte des formules de luminance relative et de rapport de contraste WCAG 2.0 SC 1.4.3 . Il produit des valeurs qui sont appropriées pour évaluer les ratios requis pour la conformité WCAG, comme sur cette page , et en tant que tel est approprié et approprié pour toute application Web. C'est trivial à porter dans d'autres langues.

/**
 * 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
la source
pourquoi préféreriez-vous la définition w3c? personnellement, j'ai implémenté CCIR 601 et celui recommandé par le w3c et
j'étais
1
Parce que, comme je l'ai dit, c'est recommandé par le W3C et les WCAG?
Synchro
1
La formule W3C est incorrecte à plusieurs niveaux. Il ne tient pas compte de la perception humaine, il utilise un contraste "simple" utilisant une luminance linéaire et pas du tout perceptuellement uniforme. Entre autres choses, il semble qu'ils l'ont basé sur certaines normes aussi anciennes que 1988 (!!!) qui ne sont pas pertinentes aujourd'hui (ces normes étaient basées sur des moniteurs monochromes tels que le vert / noir et faisaient référence au contraste total de marche à arrêt). , sans tenir compte des niveaux de gris ni des couleurs).
Myndex
1
Ce sont des ordures complètes. Luma est spécifiquement perceptuel - c'est pourquoi il a des coefficients différents pour le rouge, le vert et le bleu. L'âge n'a rien à voir avec cela - l'excellent espace colorimétrique perceptuel du CIE Lab date de 1976. L'espace W3C n'est pas aussi bon, mais c'est une bonne approximation pratique qui est facile à calculer. Si vous avez quelque chose de constructif à offrir, postez-le au lieu de vaines critiques.
Synchro
3
Juste pour ajouter / mettre à jour : nous recherchons actuellement des algorithmes de remplacement qui modélisent mieux le contraste perceptuel (discussion dans Github numéro 695) . Cependant, en tant que question distincte pour info , le seuil de sRGB est de 0,04045 , et non de 0,03928 qui a été référencé à partir d'un brouillon de sRGB ancien obsolète. La norme CEI faisant autorité utilise 0,04045 et une demande d'extraction est à venir pour corriger cette erreur dans le WCAG. (ref: IEC 61966-2-1: 1999) C'est dans le numéro 360 de Github, bien que pour mentionner, en 8 bits il n'y a pas de différence réelle - près de la fin du thread 360 j'ai des tableaux d'erreurs dont 0,04045 / 0,03928 en 8 bits.
Myndex
8

Pour ajouter ce que tous les autres ont dit:

Toutes ces équations fonctionnent un peu bien dans la pratique, mais si vous devez être très précis, vous devez d'abord convertir la couleur en espace colorimétrique linéaire (appliquer le gamma d'image inverse), faire la moyenne de poids des couleurs primaires et - si vous voulez afficher la couleur - ramenez la luminance dans le gamma du moniteur.

La différence de luminance entre la création d'un gamma et la réalisation d'un gamma correct peut atteindre 20% dans les gris foncés.

Nils Pipenbrinck
la source
2

Je résolvais une tâche similaire aujourd'hui en javascript. J'ai choisi cette getPerceivedLightness(rgb)fonction pour une couleur HEX RGB. Il traite de l'effet Helmholtz-Kohlrausch via la formule Fairchild et Perrotta pour la correction de la luminance.

/**
 * 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))))
}
catamphétamine
la source
1

L'espace colorimétrique HSV devrait faire l'affaire, voir l' article wikipedia selon la langue dans laquelle vous travaillez, vous pouvez obtenir une conversion de bibliothèque.

H est la teinte qui est une valeur numérique pour la couleur (c'est-à-dire rouge, vert ...)

S est la saturation de la couleur, c'est-à-dire son intensité

V est la «luminosité» de la couleur.

Ian Hopkinson
la source
7
Le problème avec l'espace colorimétrique HSV est que vous pouvez avoir la même saturation et la même valeur, mais des teintes différentes, pour le bleu et le jaune . Le jaune est beaucoup plus brillant que le bleu. Il en va de même pour HSL.
Ian Boyd
hsv vous donne la "luminosité" d'une couleur au sens technique. dans une luminosité perceptuelle HSV échoue vraiment
user151496
HSV et HSL ne sont pas précis sur le plan perceptuel (et ce n'est même pas proche). Ils sont utiles pour les "contrôles" pour ajuster la couleur relative, mais pas pour la prédiction précise de la légèreté perceptuelle. Utilisez L * de CIELAB pour une légèreté perceptuelle.
Myndex
1

Valeur de luminance RVB = 0,3 R + 0,59 G + 0,11 B

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

Si vous cherchez à quel point la couleur est proche du blanc, vous pouvez utiliser Distance euclidienne de (255, 255, 255)

Je pense que l'espace colorimétrique RVB est perceptiblement non uniforme par rapport à la distance euclidienne L2. Les espaces uniformes incluent CIE LAB et LUV.


la source
1

La formule de gamma inverse de Jive Dadson doit avoir le demi-ajustement supprimé lorsqu'elle est implémentée en Javascript, c'est-à-dire que le retour de la fonction gam_sRGB doit être return int (v * 255); ne renvoie pas int (v * 255 + .5); Un demi-ajustement arrondit vers le haut, ce qui peut entraîner une valeur trop élevée sur un trièdre R = G = B, c'est-à-dire gris. La conversion en niveaux de gris sur une triade R = G = B devrait produire une valeur égale à R; c'est une preuve que la formule est valide. Voir Neuf nuances de gris pour la formule en action (sans le demi-réglage).

Dave Collier
la source
On dirait que vous connaissez vos trucs, j'ai donc supprimé le +0,5
Jive Dadson
J'ai fait l'expérience. En C ++, il a besoin de +0,5, donc je l'ai remis. J'ai ajouté un commentaire sur la traduction dans d'autres langues.
Jive Dadson
1

Je me demande comment ces coefficients RVB ont été déterminés. J'ai fait une expérience moi-même et je me suis retrouvé avec ce qui suit:

Y = 0.267 R + 0.642 G + 0.091 B

Proche mais évidemment différent des coefficients UIT établis de longue date. Je me demande si ces coefficients pourraient être différents pour chaque observateur, car nous pouvons tous avoir une quantité différente de cônes et de bâtonnets sur la rétine à nos yeux, et en particulier le rapport entre les différents types de cônes peut différer.

Pour référence:

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

J'ai fait le test en déplaçant rapidement une petite barre grise sur un fond rouge vif, vert vif et bleu vif, et en ajustant le gris jusqu'à ce qu'il se mélange autant que possible. J'ai également répété ce test avec d'autres teintes. J'ai répété le test sur différents écrans, même un avec un facteur gamma fixe de 3,0, mais tout me semble identique. De plus, les coefficients ITU sont littéralement mauvais à mes yeux.

Et oui, j'ai probablement une vision des couleurs normale.

vortex
la source
Dans vos expériences, avez-vous linéarisé pour supprimer le composant gamma en premier? Si vous ne l'avez pas fait, cela pourrait expliquer vos résultats. MAIS AUSSI, les coefficients sont liés aux expériences CIE 1931 et ce sont en moyenne 17 observateurs, donc oui il y a une variance individuelle dans les résultats.
Myndex
1

Voici un peu de code C qui devrait calculer correctement la luminance perçue.

// 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
la source
0

Veuillez définir la luminosité. Si vous cherchez à quel point la couleur est proche du blanc, vous pouvez utiliser Distance euclidienne de (255, 255, 255)

Ben S
la source
1
Non, vous ne pouvez pas utiliser la distance euclidienne entre les valeurs sRGB, sRGB n'est pas un espace cartésien / vectoriel perceptuellement uniforme. Si vous souhaitez utiliser la distance euclidienne comme mesure de la différence de couleur, vous devez au moins convertir en CIELAB, ou mieux encore, utiliser un CAM comme CIECAM02.
Myndex
0

Le «V» de HSV est probablement ce que vous recherchez. MATLAB a une fonction rgb2hsv et l'article de wikipedia cité précédemment est plein de pseudocode. Si une conversion RGB2HSV n'est pas possible, un modèle moins précis serait la version en niveaux de gris de l'image.

Jacob
la source
0

Ce lien explique tout en détail, y compris pourquoi ces constantes multiplicatrices existent avant les valeurs R, G et B.

Edit: Il a une explication à l'une des réponses ici aussi (0,299 * R + 0,587 * G + 0,114 * B)

dsignr
la source
0

Pour déterminer la luminosité d'une couleur avec R, je convertis la couleur du système RVB en couleur du système HSV.

Dans mon script, j'utilise le code système HEX avant pour une autre raison, mais vous pouvez également commencer avec le code système RVB avec rgb2hsv {grDevices}. La documentation est ici .

Voici cette partie de mon code:

 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
la source
0

Pour plus de clarté, les formules qui utilisent une racine carrée doivent être

sqrt(coefficient * (colour_value^2))

ne pas

sqrt((coefficient * colour_value))^2

La preuve en est la conversion d'une triade R = G = B en échelle de gris R. Cela ne sera vrai que si vous ajustez la valeur de la couleur, pas la valeur de la couleur multipliée par le coefficient. Voir neuf nuances de gris

Dave Collier
la source
5
il y a des incohérences entre parenthèses
log0
sauf si le coefficient que vous utilisez est la racine carrée du coefficient correct.
RufusVS