web-dev-qa-db-fra.com

Quelles sont les différences pratiques lorsque vous travaillez avec des couleurs dans un espace RVB linéaire ou non linéaire?

Quelle est la propriété de base d'un espace RVB linéaire et quelle est la propriété fondamentale d'un espace non linéaire? Quand on parle des valeurs à l'intérieur de chaque canal dans ces 8 (ou plus) bits, qu'est-ce qui change?

Dans OpenGL, les couleurs sont des valeurs 3 + 1, et avec cela je veux dire RGB + alpha, avec 8 bits réservés à chaque canal, et c'est la partie que j'obtiens clairement.

Mais en ce qui concerne la correction gamma, je ne sais pas quel est l'effet du travail dans un espace RVB non linéaire.

Puisque je sais comment utiliser une courbe dans un logiciel graphique pour la retouche photo, mon explication est que dans un espace RVB linéaire, vous prenez les valeurs telles qu'elles sont, sans manipulation ni fonction mathématique attachée, au lieu de cela quand elles sont non linéaires chacune le canal évolue généralement suivant un comportement classique de fonction de puissance.

Même si je considère cette explication comme la vraie, je ne comprends toujours pas ce qu'est un véritable espace linéaire, car après le calcul, tous les espaces RVB non linéaires deviennent linéaires et, surtout, je n'obtiens pas la partie où un non -l'espace colorimétrique linéaire est plus adapté à l'œil humain car à la fin tous les espaces RVB sont linéaires pour ce que je comprends.

66
Ken

Disons que vous travaillez avec des couleurs RVB: chaque couleur est représentée avec trois intensités ou luminosités. Vous devez choisir entre "RGB linéaire" et "sRGB". Pour l'instant, nous allons simplifier les choses en ignorant les trois intensités différentes, et supposons que vous n'avez qu'une seule intensité: c'est-à-dire que vous n'avez affaire qu'à des nuances de gris.

Dans un espace colorimétrique linéaire, la relation entre les nombres que vous stockez et les intensités qu'ils représentent est linéaire. Concrètement, cela signifie que si vous doublez le nombre, vous doublez l'intensité (la légèreté du gris). Si vous souhaitez ajouter deux intensités ensemble (parce que vous calculez une intensité basée sur les contributions de deux sources de lumière, ou parce que vous ajoutez un objet transparent au-dessus d'un objet opaque), vous pouvez le faire en ajoutant simplement le deux nombres ensemble. Si vous faites n'importe quel mélange 2D ou ombrage 3D, ou presque n'importe quel traitement d'image, alors vous voulez vos intensités dans un espace colorimétrique linéaire, donc vous pouvez simplement ajouter, soustraire, multiplier et diviser les nombres pour avoir le même effet sur les intensités. La plupart des algorithmes de traitement et de rendu des couleurs ne donnent des résultats corrects qu'avec RVB linéaire, sauf si vous ajoutez des poids supplémentaires à tout.

Cela semble vraiment facile, mais il y a un problème. La sensibilité de l'œil humain à la lumière est plus fine à faible intensité qu'à forte intensité. C'est-à-dire que si vous faites une liste de toutes les intensités que vous pouvez distinguer, il y en a plus de sombres que de claires. En d'autres termes, vous pouvez mieux distinguer les nuances de gris foncées que les nuances de gris claires. En particulier, si vous utilisez 8 bits pour représenter votre intensité, et que vous le faites dans un espace colorimétrique linéaire, vous vous retrouverez avec trop de nuances claires et pas assez de nuances sombres. Vous obtenez des bandes dans vos zones sombres, tandis que dans vos zones claires, vous gaspillez des morceaux sur différentes nuances de presque blanc que l'utilisateur ne peut pas distinguer.

Pour éviter ce problème et utiliser au mieux ces 8 bits, nous avons tendance à utiliser sRGB . La norme sRGB vous indique une courbe à utiliser pour rendre vos couleurs non linéaires. La courbe est moins profonde en bas, donc vous pouvez avoir plus de gris foncé et plus raide en haut, donc vous avez moins de gris clair. Si vous doublez le nombre, vous doublez l'intensité. Cela signifie que si vous ajoutez des couleurs sRGB ensemble, vous obtenez un résultat plus clair qu'il ne devrait l'être. De nos jours, la plupart des moniteurs interprètent leurs couleurs d'entrée comme sRGB. Donc, lorsque vous mettez une couleur à l'écran ou la stockez dans une texture 8 bits par canal, stockez-la en tant que sRGB, afin que vous tiriez le meilleur parti de ces 8 bits.

Vous remarquerez que nous avons maintenant un problème: nous voulons que nos couleurs soient traitées dans un espace linéaire, mais stockées dans sRGB. Cela signifie que vous finissez par effectuer une conversion sRGB en linéaire à la lecture et une conversion linéaire en sRGB en écriture. Comme nous l'avons déjà dit, les intensités linéaires sur 8 bits n'ont pas assez d'obscurité, cela poserait des problèmes, donc il y a une règle de plus: n'utilisez pas de couleurs linéaires sur 8 bits si vous le pouvez l'éviter. Il devient de plus en plus courant de suivre la règle selon laquelle les couleurs 8 bits sont toujours sRVB, vous effectuez donc votre conversion sRVB en linéaire en même temps que vous augmentez votre intensité de 8 à 16 bits, ou de l'entier à la virgule flottante; de même, lorsque vous avez terminé votre traitement en virgule flottante, vous réduisez à 8 bits en même temps que la conversion en sRGB. Si vous suivez ces règles, vous n'aurez jamais à vous soucier de la correction gamma.

Lorsque vous lisez une image sRGB et que vous souhaitez des intensités linéaires, appliquez cette formule à chaque intensité:

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

Dans l'autre sens, lorsque vous souhaitez écrire une image au format sRGB, appliquez cette formule à chaque intensité linéaire:

float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )

Dans les deux cas, la valeur en virgule flottante va de 0 à 1, donc si vous lisez des entiers 8 bits, vous voulez d'abord les diviser par 255, et si vous écrivez des entiers 8 bits, vous voulez les multiplier par 255 enfin, de la même manière que vous le feriez habituellement. C'est tout ce que vous devez savoir pour travailler avec sRGB.

Jusqu'à présent, je n'ai traité qu'une seule intensité, mais il y a des choses plus intelligentes à faire avec les couleurs. L'œil humain peut mieux distinguer les différentes luminosités que les différentes teintes (plus techniquement, il a une meilleure résolution de luminance que la chrominance), vous pouvez donc utiliser encore mieux vos 24 bits en stockant la luminosité séparément de la teinte. C'est ce que les représentations YUV, YCrCb, etc. essaient de faire. Le canal Y est la luminosité globale de la couleur et utilise plus de bits (ou a une résolution spatiale plus grande) que les deux autres canaux. De cette façon, vous n'avez pas (toujours) besoin d'appliquer une courbe comme vous le faites avec des intensités RVB. YUV est un espace colorimétrique linéaire, donc si vous doublez le nombre dans le canal Y, vous doublez la luminosité de la couleur, mais vous ne pouvez pas ajouter ou multiplier les couleurs YUV comme vous le pouvez avec les couleurs RVB, donc il n'est pas utilisé pour traitement d'image, uniquement pour le stockage et la transmission.

Je pense que cela répond à votre question, donc je terminerai par une brève note historique. Avant sRGB, les anciens CRT avaient une non-linéarité intégrée. Si vous doubliez la tension d'un pixel, vous doubleriez plus que l'intensité. Combien plus était différent pour chaque moniteur, et ce paramètre a été appelé le gamma . Ce comportement était utile car il signifiait que vous pouviez obtenir plus d'obscurités que de lumières, mais cela signifiait également que vous ne pouviez pas dire à quel point vos couleurs seraient brillantes sur le CRT de l'utilisateur, à moins que vous ne l'étalonniez d'abord. La correction gamma signifie transformer les couleurs avec lesquelles vous commencez (probablement linéaire) et les transformer pour le gamma du CRT de l'utilisateur. OpenGL vient de cette époque, c'est pourquoi son comportement sRGB est parfois un peu déroutant. Mais les fournisseurs de GPU ont maintenant tendance à travailler avec la convention que j'ai décrite ci-dessus: que lorsque vous stockez une intensité de 8 bits dans une texture ou un tampon d'image, c'est sRGB, et lorsque vous traitez des couleurs, c'est linéaire. Par exemple, un OpenGL ES 3.0, chaque framebuffer et texture a un "indicateur sRGB" que vous pouvez activer pour activer la conversion automatique lors de la lecture et de l'écriture. Vous n'avez pas du tout besoin de faire explicitement la conversion sRGB ou la correction gamma.

178
Dan Hulme

Je ne suis pas un "expert en détection des couleurs humaines", mais j'ai rencontré quelque chose de similaire sur la conversion YUV-> RGB. Il existe différents poids pour les canaux R/G/B, donc si vous changez la couleur source de x, les valeurs RVB changent de quantité différente.

Comme je l'ai dit, je ne suis pas un expert, de toute façon, je pense que si vous voulez faire une transformation correcte des couleurs, vous devez le faire dans l'espace YUV, puis le convertir en RVB (ou faire l'opération mathématiquement équivalente sur RVB, attention de perte de données). De plus, je ne suis pas sûr que YUV soit la meilleure représentation native des couleurs, mais les caméras vidéo fournissent ce format, c'est là que j'ai rencontré le problème.

Voici la formule magique YUV-> RGB avec des nombres secrets inclus: http://www.fourcc.org/fccyvrgb.php

1
ern0