Changer la luminosité d'un pixel

Changer la luminosité d'un pixel - Algo - Programmation

Marsh Posté le 11-02-2005 à 12:36:59    

Bon, imaginons un pixel, en composantes RGB pour simplifier. Je souhaite changer sa luminosité mais conserver sa "coloration".  
 
Je calcule donc d'abord la luminance. Traditionnellement, on utilise la recommandation CCIR/ITU 601-2:

Y = 0.299 * R + 0.587 * G + 0.114 * B


C'est ce qui est utilisé en Video, pour le NTSC ou le SECAM, et ce qui est utilisé par extension pour le YCbCr et le YUV (Jpeg et Mpeg respectivement).
 
Pourtant, de nos jours, les moniteurs écrans sont apparemment plus proches de :

Y = 0.212671 * R + 0.715160 * G + 0.072169 * B; (Rec. 709)


 
Enfin, bref, on s'en fout.
 
Mon problème à moi, c'est que j'agis sur ce Y, et je voudrais recalculer mes composantes RGB adéquates, et ceci dans le cas de photos numériques (donc pas trop de couleurs saturées). La façon naïve de faire est:

newR = clamp(R/Y * newY)


 
Bon, c'est naïf et ça marche évidemment mal. La façon plus sioux, c'est de conserver les composantes en YCbCr, et de recalculer le RGB à partir du nouvel Y:

newR = clamp(new_y + 1.402 * (Cr-128.0) );


 
Mais là non plus, çe marche pas bien, car cela translate la couleur "verticalement" dans le cube YCbCr, ce qui ne suffit pas à le désaturer en cas de forte modification du Y. Bah oui, un pixel parfaitement rouge devrait devenir blanc ou noir si j'augmente ou baisse fortement sa luminosité. Voir les images sur cette page pour un exemple plus frappant:
   http://softpixel.com/~cwright/prog [...] space/yuv/
 
Donc ma question est: est-ce que quelqu'un sait comment faire ça de façon propre et raisonnable ? En l'occurence, je voudrais pouvoir le faire très rapidement (une image 1600x1200 en moins d'une demi-seconde), sachant que j'ai à ma disposition du RGB+Y ou du YCbCr.
 
Pour l'instant, je pense désaturer la couleur si elle sort du cube, mais ça me paraît douteux comme technique...


Message édité par Lam's le 11-02-2005 à 12:38:47
Reply

Marsh Posté le 11-02-2005 à 12:36:59   

Reply

Marsh Posté le 11-02-2005 à 12:39:51    

bide [:icon8]


---------------
from here and there -- \o__________________________________ -- la révolution de la terre, en silence
Reply

Marsh Posté le 11-02-2005 à 13:17:05    

[:drapo]
 
Je connais que les formules que tu as citées plus haut, mais le sujet m'intéresse...


---------------
Can't buy what I want because it's free -
Reply

Marsh Posté le 11-02-2005 à 13:29:09    

http://forum-images.hardware.fr/themes_static/images/defaut/favorisb.gif


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 11-02-2005 à 13:39:18    

pourquoi tu passes pas en TLS ?

Reply

Marsh Posté le 11-02-2005 à 13:46:54    

Taz a écrit :

pourquoi tu passes pas en TLS ?


C'est généralement déconseillé pour plusieurs raisons:
- c'est encore moins perceptuel que le RGB (sur l'axe de teinte surtout)
- c'est super chiant à calculer en temps-réel, surtout avec cette horrible cassure à 360°.
- Avec une composante L à 50%, toutes les couleurs n'ont pas la même luminosité perceptuelle.
 
Donc, certes, ça règle le problème de la désaturation aux extrèmes, mais je préférerais quand même un truc plus solide...


Message édité par Lam's le 11-02-2005 à 13:49:03
Reply

Marsh Posté le 11-02-2005 à 13:52:51    

Y'avait une formule pour augmenter la luminosité d'une couleur, cherche sur Google :D

Reply

Marsh Posté le 11-02-2005 à 14:09:50    

CIELa*b*?
au pire tu ne calcules que la couche L
 
edit: sinon la couche A du AC1C2


Message édité par Moktar1er le 11-02-2005 à 14:10:29
Reply

Marsh Posté le 11-02-2005 à 14:30:00    

moktar1er a écrit :

CIELa*b*?
au pire tu ne calcules que la couche L


Bah, pour modifier la couleur, faudra bien calculer dans les deux sens. Mais il se dit dans les milieux authorisés que Photoshop fonctionne en Lab en interne.  
 
Je pense que c'est ce que je vais essayer, pour voir.

Reply

Marsh Posté le 13-02-2005 à 20:31:28    

[:drapal] même si je m'en fou de l'imagerie, il semble qu'il y ait des maths :love:

Reply

Marsh Posté le 13-02-2005 à 20:31:28   

Reply

Marsh Posté le 13-02-2005 à 20:38:36    

[:drapo]


---------------
Nos estans firs di nosse pitite patreye...
Reply

Marsh Posté le 14-02-2005 à 00:04:38    

Bah la réponse, vous l'aurez un peu plus tard dans la semaine, le temps que je me palluche les docs sur le sRGB...
 
Ce week-end, j'ai pas mal joué avec du XYZ et du Lab, sachant que le XYZ est adapté aux écrans (il colle bien sur des entiers entre 0 et 255, il est linéaire par rapport au RGB, et il représente beaucoup mieux la façon dont les couleurs sont perçues).  
 
Le Lab par contre, est plutôt conçu pour l'impression, etc. Il est basé sur XYZ avec des puissances et des exponentielles, et il est plutôt chiant à manipuler. Je pense que le Lab manipulé par Photoshop est en fait du XYZ.
 
En gros, et à vue d'oeil, pour changer la luminosité d'un pixel, c'est tout con: il faut se trouver un modèle qui colle avec l'écran (théoriquement, l'ICC ou ICM le fait pour nous, mais il faut que je me documente dessus) , et simplement manipuler les composantes dans un espace linéaire (YCbCr ou bien XYZ qui est l'espace standardisé par le CIE pour les écrans) en interpolant entre la couleur et le point "visé" (noir, blanc, gris).
 
En utilisant XYZ ou YCbCr, et à condition de ne pas faire de gros écarts, la manipulation est très simple: si l'on éclaire un point, il suffit de faire une pondération avec le whitepoint (255,255,255 dans le cas de ces espaces linéaires adaptés aux écrans). Ce qui revient donc à faire un bête:

Code :
  1. void Recolor(uchar old_y, uchar new_y, uchar & r, uchar & g, uchar &b)
  2. {
  3. // old_y est calculé ainsi:
  4. // int old_y  = clamp( 0.2990*r + 0.5870*g + 0.1140*b);
  5.     int cb = clamp(-0.1687*r - 0.3313*g + 0.5000*b +128);
  6.     int cr = clamp(  0.500*r - 0.4187*g - 0.0813*b +128);
  7.     double saturation = 1.0;
  8.     // ici, le test devrait aussi pouvoir convertir si old_y et new_y
  9.     // ne sont pas tous les deux inférieurs ou supérieusr à 128. C'est
  10.     // laissé en exercice au lecteur.
  11.     if (old_y<new_y)
  12.         saturation *= (double)old_y/new_y;
  13.     else if (old_y>new_y)
  14.         saturation *= (double)new_y/old_y;
  15.     else
  16.         return; // rien à faire, new_y==old_y
  17.     r = clamp(new_y + saturation*(1.402 * (cr-128.0)) );
  18.     g = clamp(new_y + saturation*(-0.34414 *(cb-128.0) - 0.71414*(cr-128.0)) );
  19.     b = clamp(new_y + saturation*(1.772 * ( cb -128.0)));
  20. }


A noter que les composantes influent les unes sur les autres: le rouge grossira donc plus ou moins suivant que le bleu et le vert étaient élevés. J'ai essayé sans (avec un simple r= new_y + saturation*(r-new_y);), et les résultats sont moins probants.
 
Ceci-dit, cette méthode n'est intéressante que dans l'absolu. Il y a certaines utilisations qui sont plus particulières. Par exemple, lors du réglage du contraste/luminosité (comme sur une télé), alors la saturation des couleurs doit être proportionnelle non pas à l'écart entre l'ancienne et la nouvelle luminosité (pour simuler la convergence vers le noir ou le blanc), mais tout simplement au changement de contraste :

Code :
  1. saturation=(1-abs(contraste-0.5)*2);
  2. // contraste va de 0 (couleur unie) à 1 (augmentation par 2), en passant par 0,5 (aucun changement)


C'est à dire que mettre le contraste à fond désature  les couleurs (sinon, c'est dégueu, on dirait du Warhol). De la même manière, avec un contraste faible, une couleur à 50% de luminosité doit être désaturée pour devenir grise...
 
 
A part ça, j'ai été très déçu par le XYZ:

  • d'une part parce que les coefficients changent tous lorsque l'on change la luminosité;
  • d'autre part parce que la luminosité du canal Y colle mal avec ce que me donne mon écran LCD (passer de la couleur au gris marche mieux avec les coeffs du YCbCr que ceux du XYZ)
  • et enfin parce que même en essayant de tout faire proprement, il me fusille les composantes rouges qui sont très saturées. Probablement parce qu'elles sont hors du gamut supposé de l'espace RGB...


Message édité par Lam's le 14-02-2005 à 00:18:29
Reply

Marsh Posté le 14-02-2005 à 09:23:51    

les double cai sal, vive les float

Reply

Marsh Posté le 23-02-2005 à 02:57:46    

oui mais les floats c'est faux.
 
surtout quand il y a une solution entière (et dans RVB),
si on attire les couleurs vers le noir (ou le blanc)  
la seule altération de "coloration" ne peut être qu'une erreur (crénelage, déphasage...) dans la fonction d'interpolation des composantes :
 
unsigned mir_color(unsigned c1, unsigned c2, unsigned ratio)
{
    unsigned r1 = GetRValue (c1);
    unsigned g1 = GetGValue (c1);
    unsigned b1 = GetBValue (c1);
 
    return RGB (
        ( ratio * ( GetRValue (c2) - r1 ) / 1024 ) + r1,
        ( ratio * ( GetGValue (c2) - g1 ) / 1024 ) + g1,
        ( ratio * ( GetBValue (c2) - b1 ) / 1024 ) + b1
    );
}
 
//
 
un appel à mir_color(color,-(LIGHT>0),abs(LIGHT));
 
renvoie la couleur "color" modulée par l'intensité lumineuse "LIGHT" qui va de -1024 (noir) à 1024 (blanc) en passant par 0 (la couleur d'origine). Les dégradés sont propres, telle quelle, la méthode éclaire (ou obscurcit) une image de 1600x1200x32b en quelques dizaines de millisecondes et... on peut jouer sur les couleurs ambiantes aux « limites » avec le paramètre c2, donc dans le pire des cas, convertir des couleurs entre du noir-bleu et du blanc-vert :D  

Reply

Marsh Posté le 23-02-2005 à 07:27:14    

Image "deux fois" plus lumineuse :
R'= 2 * R
G'= 2 * G
B'= 2 * B

Reply

Marsh Posté le 23-02-2005 à 07:40:12    

LeGreg a écrit :

Image "deux fois" plus lumineuse :
R'= 2 * R
G'= 2 * G
B'= 2 * B


bah non.
(0, 0, 255)  ==> (0,0,255).
C'est pas 2 fois plus lumineux :)
 
Faut que l'essaye le  truc de fra ceci-dit...

Reply

Marsh Posté le 23-02-2005 à 16:27:29    

c'est ce qu'il te faut si j'ai bien compris la question.
 
La luminosité de (0,0,255) est déja maximale (sa saturation aussi),  
 
mir_color(RGB(0,0,255),RGB(255,255,255),512) renvoie (127,127,255) ne pouvant agir sur la luminosité, elle diminue la saturation pour atteindre l'objectif, la teinte reste intacte.
 
Après rien ne t'interdit de suivre des trajectoires plus fines que la droite à intervales constants.

Reply

Marsh Posté le 23-02-2005 à 21:50:50    

Lam's a écrit :

bah non.
(0, 0, 255)  ==> (0,0,255).
C'est pas 2 fois plus lumineux :)


 
(0, 0, 255)  ==> (0,0,510).
 
c'est pas ma faute si tu satures par derrière.

Reply

Marsh Posté le 23-02-2005 à 21:56:25    

LeGreg a écrit :

(0, 0, 255)  ==> (0,0,510).
 
c'est pas ma faute si tu satures par derrière.


 
faut que tu me dises comment tu fait pour mettre 510 dans un octet :o


---------------
Nos estans firs di nosse pitite patreye...
Reply

Marsh Posté le 23-02-2005 à 22:23:27    

KangOl a écrit :

faut que tu me dises comment tu fait pour mettre 510 dans un octet :o


 
C'est effectivement la quadrature du cercle. Tu as un parallélépipède qui représente ton espace de couleurs représentables sur ton écran et un plan qui est l'espace des couleurs à luminosité fixe. Quel que soit l'endroit où tu te places, il y aura toujours une partie de ton plan qui sera en dehors de ton parallélépipède: tu n'y peux rien ce sont les lois de la physique et des mathématiques qui t'en veulent.  
 
Sur un écran classique tu ne peux donc pas mais il y a d'autres formes de représentations (écrans HDR, tone mapping, etc.. ).

Reply

Marsh Posté le 24-02-2005 à 01:22:02    

tu peux nous montrer une image qui illustre ce que tu dis :pt1cable:  

Reply

Marsh Posté le 24-02-2005 à 14:36:58    

Citation :

Ce week-end, j'ai pas mal joué avec du XYZ et du Lab, sachant que le XYZ est adapté aux écrans (il colle bien sur des entiers entre 0 et 255, il est linéaire par rapport au RGB, et il représente beaucoup mieux la façon dont les couleurs sont perçues).


Pas de bol pour toi. J'ai un expert de la couleur avec qui je bosse qui est parti en vacances hier soir... Alors je vais être innexact mais voilà ce que j'ai compris.
XYZ c'est le triplet de base en colorimétrie. Si j'ai bien compris il reflète la sensibilité des cônes de l'oeil humain sous une certaine observation et sous un certain illuminant. Tu vas me dire mais c'est les cones RVB. Ben pas complètement, ou pas du tout, car c'est plus subtil. RVB ça marche bien pour du matos industriel genre tube cathodique. Dans l'oeil c'est moins délimité. Les cones rouge sont aussi un peu influencés par le bleu je crois, y'en a des plus sensibles que d'autre, etc...  
Donc en lui même, XYZ n'est qu'une mesure, c'est pas vraiment exploitable (ça n'a pas été conçu pour). Je suis pas étonné que t'arrives pas à grand chose avec. Y'a qu'à voir la gueule de l'espace colorimétrique pour s'en convaincre:
http://upload.wikimedia.org/wikipe [...] length.png
XYZ ça sert de base à plein d'autres triplets, y'en a des tonnes.
Chaque triplet effectue en quelque sorte une translation des points XYZ dans un espace dont chacun des 3 axes a une valeur plus significative. Par exemple pour RVB c'est les 3 couleurs qui ont donné ce nom.
Ici tu as tout plein de formules de conversion:
http://www.easyrgb.com/math.php?MATH=M7#text7
Mais ça prend jamais en compte l'illuminant, à savoir l'écran pour toi. Donc je suppose que c'est valable que pour un illuminant standard prédéfini. Car moi je bosse avec ça dans un autre contexte que le tiens (dans la colorimétrie).
 

Citation :

Le Lab par contre, est plutôt conçu pour l'impression, etc. Il est basé sur XYZ avec des puissances et des exponentielles, et il est plutôt chiant à manipuler. Je pense que le Lab manipulé par Photoshop est en fait du XYZ.


Lab est très utilisé dans l'imprimerie en effet. A partir du Lab (coordonnées cartésiennes) tu obtiens le LCh (coordonnées polaires), et LCh c'est peut être mieux pour toi (Luminosité-saturation-teinte), car si j'ai bien compris tu cherches à jouer sur la saturation en fait et pas vraiment sur la luminosité.
 

Citation :

En gros, et à vue d'oeil, pour changer la luminosité d'un pixel, c'est tout con: il faut se trouver un modèle qui colle avec l'écran (théoriquement, l'ICC ou ICM le fait pour nous, mais il faut que je me documente dessus) , et simplement manipuler les composantes dans un espace linéaire (YCbCr ou bien XYZ qui est l'espace standardisé par le CIE pour les écrans) en interpolant entre la couleur et le point "visé" (noir, blanc, gris).


Sur le même site que plus haut y'a un outil de calibration de l'écran:
http://www.easyrgb.com/calibrate.php
mais je sais pas comment il marche.  
 
Bon je sais pas si ce que je t'ai dit est appliquable à ton utilisation (travailler sur un pixel RVB) car moi je bosse encore en amont de XYZ à partir de mesures instrumentales brutes, et avec des illuminants variables. Peut être que y'a un filtre tout pret qui existe pour ce que tu veux faire.
Tu as des infos pas trop mal sur wikipedia:
http://fr.wikipedia.org/wiki/LAB
et j'ai aussi ce site avec plein d'utilitaires dans mes bookmarks:
http://www.efg2.com/Lab/
ça peut être sympa pour faire des tests.
Arf... apparement tous les logiciels intéressants sur les espaces de couleur ont été virés...:/


Message édité par HelloWorld le 24-02-2005 à 14:40:08

---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 25-02-2005 à 22:09:59    

Bon les gars, faudrait songer à réfléchir en 3D avant de trop s'avancer sur les couleurs  :hello: ...
 
c'est pour vous, mais si vous insistez, voici la solution en YCbCr :
 

Code :
  1. enum screen {NORMAL, CCIR_601_1, CCIR_709};
  2. double factors[9][3]={{1/3.,0.299,0.2125}, {1/3.,0.587,0.7154}, {1/3.,0.114,0.0721}};
  3. #define R 0
  4. #define G 1
  5. #define B 2
  6. #define PROJECTED (R+G+B)
  7. #define BACKWARD (2*PROJECTED)
  8. #define SR (R+PROJECTED)
  9. #define SB (B+PROJECTED)
  10. #define SG (G+PROJECTED)
  11. #define CR (R+BACKWARD)
  12. #define CB (B+BACKWARD)


/// init, évite quelques opérations répétitives au niveau de la redondance
 

Code :
  1. for(int mode=NORMAL;mode<=CCIR_709;mode++)
  2. {
  3.     factors[CR][mode]=2.0-2.0*factors[R][mode];
  4.     factors[CB][mode]=2.0-2.0*factors[B][mode];
  5.     factors[SR][mode]=1.0/factors[CR][mode];
  6.     factors[SG][mode]=1.0/factors[G][mode];
  7.     factors[SB][mode]=1.0/factors[CB][mode];
  8. }


/// ratio est dans [-1;1] (0% correspond à l'image d'origine)
/// scale est strictement positif... attention les yeux !
/// mode est 0, 1 ou 2 en fonction du profil matriel
 

Code :
  1. unsigned lightYCbCr(unsigned color,double ratio,double scale,screen mode)
  2. {
  3. double r = GetRValue(color);
  4. double g = GetGValue(color);
  5. double b = GetBValue(color);
  6. double y = r*factors[R][mode]+g*factors[G][mode]+b*factors[B][mode];
  7. double cb = (b-y)*factors[SB][mode];
  8. double cr = (r-y)*factors[SR][mode];
  9. y+=ratio*scale;
  10. r = cr*factors[CR][mode]+y;
  11. b = cb*factors[CB][mode]+y;
  12. g = (y-b*factors[B][mode]-r*factors[R][mode])*factors[SG][mode];
  13. return RGB(min(max((int)r,0),255),min(max((int)g,0),255),min(max((int)b,0),255));
  14. }


/// étonnant non ?


Message édité par fra0 le 25-02-2005 à 22:41:12
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed