114 votes

Quelles sont les différences pratiques lorsqu'on travaille avec des couleurs dans un espace RVB linéaire ou non linéaire ?

Quelle est la propriété fondamentale d'un espace RVB linéaire et quelle est la propriété fondamentale d'un espace non linéaire ? Lorsque l'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 je comprends clairement.

Mais lorsqu'il s'agit de correction gamma, je ne comprends pas quel est l'effet de travailler dans un espace RVB non linéaire.

Comme je sais comment utiliser une courbe dans un logiciel graphique pour l'édition photo, mon explication est que dans un espace RVB linéaire, vous prenez les valeurs telles qu'elles sont, sans manipulation et sans fonction mathématique, alors que lorsqu'il est non linéaire, chaque canal évolue généralement selon 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, parce qu'après le calcul, tous les espaces RVB non linéaires deviennent linéaires et, surtout, je ne comprends pas la partie où un espace couleur non linéaire est plus adapté à l'œil humain, parce qu'au final, tous les espaces RVB sont linéaires, d'après ce que je comprends.

281voto

Dan Hulme Points 4085

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

Dans un espace couleur linéaire, la relation entre les nombres que vous stockez et les intensités qu'ils représentent est linéaire. En pratique, cela signifie que si vous doublez le nombre, vous doublez l'intensité (la clarté du gris). Si vous souhaitez additionner deux intensités (parce que vous calculez une intensité sur la base des contributions de deux sources lumineuses, ou parce que vous ajoutez un objet transparent à un objet opaque), vous pouvez le faire en additionnant simplement les deux nombres. Si vous effectuez un quelconque mélange 2D ou un ombrage 3D, ou presque tout traitement d'image, vous voulez que vos intensités se situent dans un espace colorimétrique linéaire. Il suffit donc d'ajouter, de soustraire, de multiplier et de diviser des nombres pour obtenir 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 le RVB linéaire, à moins que vous n'ajoutiez des pondérations supplémentaires à tout.

Cela semble très facile, mais il y a un problème. La sensibilité de l'œil humain à la lumière est plus fine aux faibles intensités qu'aux fortes intensités. En d'autres termes, si vous dressez une liste de toutes les intensités que vous pouvez distinguer, il y a plus d'intensités sombres que d'intensités 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 couleur linéaire, vous finirez par avoir 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 bits sur différentes nuances de blanc proche 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, de sorte que vous pouvez avoir plus de gris foncés, et plus raide en haut, de sorte que vous avez moins de gris clairs. Si vous doublez le nombre, vous faites plus que doubler l'intensité. Cela signifie que si vous additionnez des couleurs sRGB, 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 affichez une couleur à l'écran ou que vous la stockez dans une texture de 8 bits par canal, stockez-la en tant que sRGB. afin d'utiliser au mieux 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 en sRGB. Cela signifie que vous finissez par faire une conversion sRGB vers linéaire en lecture, et une conversion linéaire vers sRGB en écriture. Comme nous avons déjà dit que les intensités linéaires de 8 bits n'ont pas assez de sombres, cela poserait des problèmes, il y a donc une autre règle pratique : n'utilisez pas de couleurs linéaires de 8 bits si vous pouvez l'éviter. Il est de plus en plus courant de suivre la règle selon laquelle les couleurs 8 bits sont toujours sRVB, de sorte que vous effectuez votre conversion sRVB-linéaire en même temps que vous élargissez votre intensité de 8 à 16 bits, ou d'entier à virgule flottante ; de même, lorsque vous avez terminé votre traitement en virgule flottante, vous réduisez à 8 bits en même temps que vous convertissez en sRVB. 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 voulez 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 voulez écrire une image en 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 s en virgule flottante est comprise entre 0 et 1. Par conséquent, si vous lisez des entiers 8 bits, vous devez d'abord diviser par 255, et si vous écrivez des entiers 8 bits, vous devez multiplier par 255 en dernier, comme 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 distinguer des luminosités différentes mieux que des teintes différentes (plus techniquement, il a une meilleure résolution de luminance que de chrominance), donc vous pouvez faire un meilleur usage de vos 24 bits en stockant la luminosité séparément de la teinte. C'est ce que les représentations YUV, YCrCb, etc. tentent de faire. Le canal Y représente la luminosité globale de la couleur et utilise plus de bits (ou a une meilleure résolution spatiale) que les deux autres canaux. Ainsi, vous n'avez pas (toujours) besoin d'appliquer une courbe comme vous le faites avec les intensités RVB. YUV est un espace couleur 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 ensemble comme vous pouvez le faire avec les couleurs RGB, donc il n'est pas utilisé pour le traitement d'image, seulement pour le stockage et la transmission.

Je pense que cela répond à votre question, je terminerai donc par une brève note historique. Avant le sRGB, les anciens tubes cathodiques étaient dotés d'une non-linéarité intégrée. Si vous doubliez la tension d'un pixel, vous faisiez plus que doubler l'intensité. La quantité de plus était différente pour chaque moniteur, et ce paramètre était appelé le "sRGB". gamma . Ce comportement était utile car il permettait d'obtenir plus de sombres que de clairs, mais il ne permettait pas non plus de savoir quelle serait la luminosité de vos couleurs sur le tube cathodique de l'utilisateur, à moins de l'étalonner au préalable. Correction gamma signifie transformer les couleurs de départ (probablement linéaires) et les transformer pour le gamma du tube cathodique de l'utilisateur. OpenGL est issu 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 : lorsque vous stockez une intensité de 8 bits dans une texture ou un framebuffer, c'est sRGB, et lorsque vous traitez les couleurs, c'est linéaire. Par exemple, dans OpenGL ES 3.0, chaque framebuffer et texture possède un "drapeau sRGB" que vous pouvez activer pour permettre une conversion automatique lors de la lecture et de l'écriture. Vous n'avez pas besoin d'effectuer explicitement la conversion sRGB ou la correction gamma.

1voto

ern0 Points 2203

Je ne suis pas un "expert en détection des couleurs humaines", mais j'ai rencontré des choses similaires sur la conversion YUV->RGB. Il y a des poids différents pour les canaux R/G/B, donc si vous changez la couleur source par 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 pour corriger les couleurs, vous devriez le faire dans l'espace YUV, puis le convertir en RVB (ou faire l'opération mathématiquement équivalente sur RVB, attention à la 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 les chiffres secrets inclus : http://www.fourcc.org/fccyvrgb.php

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X