geogeo a écrit :
Alors déjà je dirais que tu devrais convertir ton image au format Luminance, Chrominance,
Passage RGB en YUV
Y = 0.299R + 0.587G + 0.114B
U = (B-Y)*0.565
V = (R-Y)*0.713
Passage de YUV en RGB
R = Y + 1.403V
G = Y - 0.344U - 0.714V
B = Y + 1.770U
Et aide toi des imperfections de l'oeil pour calculer au mieux ta couleur.
J'avais déjà parlé de la conversion RGB <-> YUV il y a plus de 2 ans :
J'ai écrit dans ce post :
Mhhh, ce n'est pas évident, ce que tu demandes ...
En effet, il faut faire le bon compromis entre représenter les nuances de la couleur prédominante, et ne pas oublier les couleurs peu représentées mais chromatiquement éloignées de la dominante.
Tout d'abord, il faut que tu fasses une table donnant, pour chacune des Y couleurs de départ, le nombre de pixels de cette couleur, afin d'éliminer toutes les couleurs à 0, et avoir une idée de la dominante.
Ensuite, pour réduire le nombre de couleur en perdant le minimum de qualité, je te conseille de passer du codage RGB au codage YUV (luminance-chrominance).Luminance :
Y = 1 x (0.299 R + 0.587 G + 0.114 B)
Chrominance (codée sur deux vecteurs) :
U = 0.492 x (B - Y)
V = 0.877 x (R - Y)
Pour U et V, il faut faire attention au fait que l'on peut avoir une valeur négative, il faut donc les coder sur des variables plus grandes (words au lieu de bytes, ou dwords au lieu de words).
Ensuite, pour chacune des Y couleurs, il faut que tu donnes une note en fonction du nombre de pixels de cette couleur et d'autres critères, comme la distance du plus proche voisin ( DYUV 1-22 = (Y1-Y2)2 + (U1-U2)2 + (V1-V2)2 ), ainsi que des choses plus subtiles et tordues
.
Enfin, tu sélectionnes les X couleurs ayant les plus hautes notes (ou plus basses, ça dépend comment tu attribues les notes), et pour les Y-X couleurs restantes, tu les remplaces par leur plus proche voisin (toujours en codage YUV).
Voilà, j'espère t'avoir donné quelques pistes et idées ...
@++
Attention ! Les coefficients multiplicateurs de U et V sont différents !!!
Mais la différence s'explique très simplement

...
643(B-Y)
V = (R-Y)/1.402 ~= 0.7133(R-Y)
Formules RGB -> YUV données par geogeo :Y = 0.299R + 0.587G + 0.114B
U = (B-Y)/1.772 ~= 0.5
Pour chaque composante R, G et B variant indépendamment dans [0;255] (codage 8 bits par composante), on a Y € [0;255], U € [-127.5;127.5] et V € [-127.5;127.5], donc il suffit d'un décalage de 127.5 sur U et V pour pouvoir coder facilement chaque composante Y, U et V sur 1 octet chacune.
Les formules de geogeo (le passage YUV -> RGB est trivial) sont donc utilisées pour
stocker au format YUV.
+ 0.587G + 0.114B
U = 0.492(B-Y)
V = 0.877(R-Y)
Formules RGB -> YUV données par votre serviteur :Y = 0.299R Avec R, G et B dans [0;255], on a toujours Y € [0;255], mais U € [-111.16;111.16] et V € [-156.77;156.77], ce qui ne permet pas un stockage immédiat de ces valeurs.
Par contre, ces coefficients tiennent comptent de la sensibilité de l'œil humain aux couleurs, et sont donc utilisées pour
réduire le nombre de couleurs en minimisant la dégadation perçue par l'oeil.
En effet, l'œil humain est plus sensible aux nuances de luminosité (d'où la projection sur l'axe Y de la luminance) qu'aux nuances de couleurs (plan U-V des chrominances).
Note annexe : la luminosité et la luminosité ne sont pas identiques, mais représentent la même chose avec une de ces grandeurs (je ne sais plus laquelle, la luminance je crois) en échelle linéaire, et l'autre en échelle logarithmique.
Et, dans le plan des chrominances, l'œil humain est moins sensible au bleu (en gros, l'axe U) qu'au rouge et au vert.
C'est pourquoi le coefficient 0.492 de U pour la réduction de couleur est plus petit que le coefficient 0.5643 pour le stockage (et inverserment, le coefficient de V est plus grand).
Aussi, grâce à ces coefficients, deux couleurs ayant une différence de x unités en U, et ayant mêmes Y et V, paraîtront avoir le même “écart chromatique” que deux couleurs ayant une différence de x unités en V, et ayant mêmes Y et U.
On peut alors définir,
à Y constant, la distance chromatique entre deux couleurs a et b par D
Ca-b = sqrt( (U
b-U
a)² + (V
b-V
a)² ).
Cependant, D
C est rarement utilisée directement, puisqu'il faut analyser chaque plan U-V à Y constant , ou plus exactement dans chaque tranche avec Y € [Y
0;Y
0+
dY].
Et il faut tout d'abord choisir un rapport entre
dY et la distance D
C telles que deux couleurs plus proches que ça seront transformées en la même couleur.
En pratique, le plus simple est de découper l'espace YUV en parallélépipèdes (j'utiliserai ensuite le mot
pavé, plus facile à écrire

) de hauteur H selon Y et de largeur L selon U et V (même L selon U et V, les coefficients multiplicateurs ont déjà été déterminés pour ça).
Habituellement, on utilise L=2H, c'est-à-dire un échantillonnage deux fois plus fin en luminance qu'en chrominance (cf. l'échantillonnage YUV 4:2:2 utilisé en télévision : 4 échantillonnages en Y contre 2 en U et V (pourquoi ça ne s'appelle pas 2:1:1 ? Je ne le sais pas vraiment...)), l'œil humain étant, comme déjà dit, plus sensible aux nuances de luminance que de chrominance.
Par exemple, avec H=1, on découpe l'intervalle de Y en 255 morceaux ([0;1], [1;2], ..., [254;255]), celui de U en 112 morceaux ([-111.16;-110], [-110;-108], ..., [110;111.16]) et celui de V en 157 morceaux ([-156.77;-155], [-155;-153], ..., [155;156.77]).
Ainsi, tous les pixels dont la couleur YUV sont dans le pavé [26;27],[-54;-52],[41;43] seront codés par la couleur YUV (26.5,-53,42).
Mais attention, tous les 255*112*157 pavés n'existent pas !
Par exemple, si Y € [0;1], on a forcément (R, G et B étant chacun dans [0;255]) U € [-0.492;3.8238] et V € [-0.877;2.0561] (et encore, on n'a pas le rectangle complet, seulement la moitié, le triangle (-0.492,-0.877),(-0.492,2.0561),(3.8238,-0.877)) : dans la tranche Y € [0;1], on n'a que 5 pavés possibles, et non 112*157.
Cependant, algorithmiquement, le plus simple est évidemment de considérer que les 255*112*157 pavés existent, de tous les passer en revue en comptant le nombre de pixels dont la couleur YUV est dans le pavé, et de ne conserver que ceux dont le nombre de pixels existants est non nul.
On pourrait éventuellement ‘éliminer’ les pavés qui ont strictement moins de N pixels (N ne dépassant pas la dizaine, N=8 me paraît bien) en ‘déplaçant’ ses pixels dans un pavé immédiatement voisin (après, il faut choisir la définition de
voisin immédiat : les 6 ayant une face commune ? les 18 ayant une face ou une arête commune ? les 26 ayant une face, une arête ou un sommet commun ?) ayant plus de pixels que lui-même (mais, pour une meilleure qualité, il faut découper le pavé en 8 sous-pavés (d'où mon choix de N=8, d'ailleurs), puisque si tous les pixels sont rassemblés dans un coin, il ne vaut mieux pas les déplacer dans le pavé voisin au sommet opposé, il est vraiment trop loin).
Ou, au lieu de ‘déplacer’ des pixels, on peut aussi fusionner des pavés ayant une face commune pour que le double pavé résultant ait au moins N pixels (on peut répéter l'opération entre 2 double pavés pour former une quadruple pavé dont les 4 pavés initiaux ont une arête commune, puis entre 2 quadruples pavés pour former un octuple pavé dont les 8 pavés initiaux ont un sommet commun).
Le nombre de pavés restants donne donc le nombre de couleurs au final : s'il y a trop de couleurs, on augmente H, et s'il y a trop peu de couleurs, on diminue H, et on recommence tout le travail.
Note : si on parcourt les pavés un par un pour compter les pixels, il faut scanner l'image complète à chaque fois... pas top

.
On peut grandement accéler le traitement si on a beaucoup de place mémoire : on alloue dès le départ un tableau de 255*112*157 entiers (pour H=1, avec toute la place perdue que cela implique, mais tant pis, si on a de la mémoire, autant l'utiliser pour booster la vitesse) initialisés à 0, et on parcourt une seule fois l'image en incrémentant dans le tableau l'entier correspondant au pavé (voire un tableau 8 fois plus grand pour faire les sous-pavés en prévision des déplacements de pixels).
Pour le déplacement de pixels, le tableau convient bien, mais pour la fusion, par contre, il faut utiliser des structures annexes pour coder les doubles, quadruples et octoples pavés créés.
Voilà voilà, j'espère que tu pourras tirer quelque chose de ce pavé (désolé, je n'ai pas pu m'empêcher d'en faire un

) !
R = Y + V/0.877
G = Y - 0.114U/(0.587*0.492) - 0.299V/(0.587*0.877)
B = Y + U/0.492
Juste pour rappel, je recopie les formules de conversion, dans les deux sens cette fois :Y = 0.299R + 0.587G + 0.114B
U = 0.492(B-Y)
V = 0.877(R-Y)