1

Hello ^^
J'avais il y a quelques temps demandé conseil pour une réduction de couleurs assez spéciale. Pour le coeur de la réduction j'avais utilisé octree, une méthode qui fonctionne bien et qui est rapide mais qui a tendance à produire une palette assez décolorée. Dans une conversion octree on aura rarement un rouge pétant par exemple.
Sauf que là le but serait de pouvoir faire du dithering, et avec des couleurs délavées c'est pas possible de reproduire des couleurs pétantes... alors que l'inverse oui.
Donc je me demandais quelles autres méthodes de réduction je pourrais utiliser pour obtenir des résultats avec des couleurs très contrastées.
Merci d'avance smile



[Edit] Je poste ma méthode courante de calcul qui donne un résultat beaucoup plus coloré (mais moche et trop lent... vous verrez pourquoi):
Je parcours l'image pixel par pixel, à chaque fois je prends la couleur et je la compare à toutes les autres de la palette, si elle peut être approximée à une autre avec une certaine tolérance*, je prends celle-ci, sinon je l'ajoute à la palette.
* tolérance = distance dans le colorspace entre les couleurs: racine((r1-r2)^2+(v1-v2)^2+(b1-b2)^2).
Pour trouver la tolérance idéale qui donnera un max de couleurs mais pas trop, je fais une approximation dans la même méthode du style "devine un nombre?" "1000" -> plus grand, "8000" -> plus petit. Mais à chaque fois faut refaire une passe avec l'autre tolérance, donc d'ici qu'on trouve "5248" entre "0" et "30000" ben y'a un moment ^^
Si vous avez des suggestions pour l'améliorer (déjà en vitesse, mais surtout qu'elle soit un peu plus sérieuse tripo).

[Re-edit] De gauche à droite, ma méthode, uniforme + tramage (pas utilisable pour ce que je veux faire car les palettes doivent être de 16 couleurs et là en uniforme c'est trop moche), octree.
_yn_methode_moi.PNG_yn_methode_tramage.PNG_yn_methode_octree.PNG
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

2

peut être une piste: calcule les histogrammes de ton image, détecte les pics et pour chaque pic important, tu ajoute une couleur à ta palette
avatar
fabetal_ > Hier, je me suis fait monter par un pote
redangel > et en chevals, ça donne quoi?
Nil> OMG I think I'm gay

3

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.
avatar
la Nature nous montre seulement la queue du lion. Mais je suis certain que le lion a qui elle appartient pense qu'il ne peut pas se révéler en une fois en raison de son immense taille.

- Fondateur de Ti-Gen -: http://www.tigen.org

- Membre du Groupe Orage Studio -: http://oragestudio.free.fr/

- Mon site perso -: http://tisofts.free.fr

Projets TI68K en cours:
GFA-Basic = http://www.tigen.org/gfabasic
Arkanoid.
PolySnd 3.0.

4

Merci de vos réponses, mais est-ce que j'ose dire que c'est légèrement du chinois pour moi tout ça? grin
J'ai fait pas mal de recherches sur le sujet mais dans tous les cas je bloque d'une part par mon mauvais anglais et d'autre par mon manque de connaissances en algorithmie et en maths sad
Convertir en RGB <=> YUV je veux bien mais je vois mal ce que ça m'apporte au final? "Et aide-toi des imperfections de l'oeil" confus
Et pour les histogrammes, tu veux dire que les pixels qui apparaissent le plus souvent méritent une couleur dans la palette?
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

5

Y correspond à la lumière de ton image et UV aux couleurs.
Je pense que la majorité de ton travail va porter sur la composante Y. COmme elle te donne la lumière de ton image il suffira de virer les couleurs qui selon toi on une lumière très faible et de ressortir celle qui on une très bonne lumière.
Si tu as une couleur qui n'est pas dans ta palette est que ta palette est saturée il te suffit je pense de trouver la couleur assez proche grâce aux composantes UV et qui à une lumière assez proche.

Tu comprendras mieux mon charabia en décomposants ton image en YUV et en affichant les composantes unes par unes.
avatar
la Nature nous montre seulement la queue du lion. Mais je suis certain que le lion a qui elle appartient pense qu'il ne peut pas se révéler en une fois en raison de son immense taille.

- Fondateur de Ti-Gen -: http://www.tigen.org

- Membre du Groupe Orage Studio -: http://oragestudio.free.fr/

- Mon site perso -: http://tisofts.free.fr

Projets TI68K en cours:
GFA-Basic = http://www.tigen.org/gfabasic
Arkanoid.
PolySnd 3.0.

6

Ok merci, je vais tester smile
Y'a quand même un truc qui me titille, c'est que si on prend l'algo de ma méthode, qui est toute pourrie, le résultat est pourtant impressionnant comparé à un octree beaucoup plus évolué...
Mais il doit bien y avoir un moyen de faire autrement qu'en itératif non? confus Mais je bloque sad
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

7

Tout ça dépend énormément des paramètres... Les couleurs de l'image source, et le nombre de couleurs que tu désires obtenir...
Toutes les couleurs de ton image se trouvent dans une portion assez réduite de l'espace de couleur (espace de couleurs HSL hein), et sont donc très proches.
A mon avis, avec des couleurs plus variées tu obtiendrais des résultats bien différents...

Par contre je saurais pas te conseiller de méthode (bien que je pense comme BookeldOr que l'histogramme soit une bonne idée) parce que je me suis jamais vraiment penché là dessus... (JE vais faire quelques tests avec histogrammes pour voir ce que ça donne...)
Je dirais juste que en réalité, l'algorithme que tu utilisera, et la valeur de ses éventuels paramètres, dépend de ce que toi tu veux faire ressortir... Les algorithmes "généraux" comme l'octree permettent la réduction de couleurs d'une image quelconque de sorte que le résultat soit toujours relativement correct, mais il y a toujours mieux pour une image donnée...
Quand a développer un algorithme générique qui donne de meilleurs résultats que ceux présentés ici ce n'est pas nécéssairement impossible, mais il y a grand nombre de paramètres a traiter... Et par exemple l'algorithme présenté par geogeo, peut être pas dans l'idée, mais au moins sur la forme est mauvais, et ne fonctionnera certainement pas correctement... Je ne vais pas rentrer dans les détails parce que toutes ces considérations sur les couleurs sont très complexes, mais globalement ça se basse sur des aproximations trop grosses pour obtenir avec une quelconque image un résultat correct, il ne se base que sur la luminance, et en dernier recours sur la chrominance, sans tenir compte des autres poaramètres tels que la largeur de l'histogramme ou la proportion des différentes couleurs dans la représentation de l'image...
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

8

J'ai essayé de coder un histogramme mais mon algo ne doit pas être très bon cheeky
- Ca consomme bcp de ressources
- C'est extrêmement lent
- Le résultat est plus que moyen
Par contre je saurais pas te conseiller de méthode (bien que je pense comme BookeldOr que l'histogramme soit une bonne idée) parce que je me suis jamais vraiment penché là dessus... (JE vais faire quelques tests avec histogrammes pour voir ce que ça donne...)

Est-ce que tu as testé? Ta méthode donne un bon résultat? smile

Sinon pour reprendre ma première méthode, est-ce que quelqu'un pourrait me donner des astuces pour l'améliorer, au niveau de l'algo? C'est-à-dire au lieu de prendre bêtement les couleurs dans l'ordre de scanning, de faire plein de passes pour trouver la précision idéale qui donnera un max de couleurs mais pas trop, etc. Merci d'avance smile
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

9

Bah c'est quoi ton algo actuel ?
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

10

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 fou.
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 hehe...


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 DCa-b = sqrt( (Ub-Ua)² + (Vb-Va)² ).
Cependant, DC est rarement utilisée directement, puisqu'il faut analyser chaque plan U-V à Y constant , ou plus exactement dans chaque tranche avec Y € [Y0;Y0+dY].
Et il faut tout d'abord choisir un rapport entre dY et la distance DC 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 hehe) 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 sorry.
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 tongue) !
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)
avatar
Je ne suis pas développeur Java : je suis artiste Java.
Ce que l’on conçoit bien s’énonce clairement, / Et le code pour l’écrire arrive aisément.
Hâtez-vous lentement ; toujours, avec méthode, / Vingt fois dans l’IDE travaillez votre code.
La perfection est atteinte, non pas lorsqu’il n’y a plus rien à ajouter, mais lorsqu’il n’y a plus rien à retirer.
You don't use science to show that you're right, you use science to become right.

11

C'est intéressant, mais par pitié, n'hésite pas à sauter des lignes. Un simple retour à la ligne ne suffit pas à faire une coupure, parce qu'il n'y a pas de séparation automatique entre paragraphes.

12

C'est mieux ?
Remarque que je vais déjà à la ligne à chaque phrases (j'ai toujours fait comme ça), donc à moins de sauter une ligne après chaque phrase, je peux difficilement faire mieux, là sorry...
avatar
Je ne suis pas développeur Java : je suis artiste Java.
Ce que l’on conçoit bien s’énonce clairement, / Et le code pour l’écrire arrive aisément.
Hâtez-vous lentement ; toujours, avec méthode, / Vingt fois dans l’IDE travaillez votre code.
La perfection est atteinte, non pas lorsqu’il n’y a plus rien à ajouter, mais lorsqu’il n’y a plus rien à retirer.
You don't use science to show that you're right, you use science to become right.

13

Ethaniel :
Note annexe : la luminosité et la luminosité ne sont pas identiques
Ha bon ?
avatar

14

Ben non cheeky !
Selon cette page : La luminance détermine certes dans une large mesure l'impression de luminosité que dégage un objet, mais ne constitue pas pour autant la mesure absolue de la sensation de clarté. En effet, dans des environnements de luminosités différentes, des surfaces de même luminance peuvent être perçues subjectivement comme ayant des luminosités différentes. Autrement dit, la luminance est une grandeur physique mensurable, alors que la notion de luminosité décrit une sensation subjective. En vision diurne, la luminosité est à peu près proportionnelle au logarithme de la luminance.
avatar
Je ne suis pas développeur Java : je suis artiste Java.
Ce que l’on conçoit bien s’énonce clairement, / Et le code pour l’écrire arrive aisément.
Hâtez-vous lentement ; toujours, avec méthode, / Vingt fois dans l’IDE travaillez votre code.
La perfection est atteinte, non pas lorsqu’il n’y a plus rien à ajouter, mais lorsqu’il n’y a plus rien à retirer.
You don't use science to show that you're right, you use science to become right.

15

Sasume :
Bah c'est quoi ton algo actuel ?

Celui que j'ai postée en premier post smile
Je poste ma méthode courante de calcul qui donne un résultat beaucoup plus coloré (mais moche et trop lent... vous verrez pourquoi):
Je parcours l'image pixel par pixel, à chaque fois je prends la couleur et je la compare à toutes les autres de la palette, si elle peut être approximée à une autre avec une certaine tolérance*, je prends celle-ci, sinon je l'ajoute à la palette.
* tolérance = distance dans le colorspace entre les couleurs: racine((r1-r2)^2+(v1-v2)^2+(b1-b2)^2).
Pour trouver la tolérance idéale qui donnera un max de couleurs mais pas trop, je fais une approximation dans la même méthode du style "devine un nombre?" "1000" -> plus grand, "8000" -> plus petit. Mais à chaque fois faut refaire une passe avec l'autre tolérance, donc d'ici qu'on trouve "5248" entre "0" et "30000" ben y'a un moment ^^
Si vous avez des suggestions pour l'améliorer (déjà en vitesse, mais surtout qu'elle soit un peu plus sérieuse tripo).

Ethaniel> Merci, pas encore eu le temps de tout lire, je me pencherai dessus... smile
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

16

./10> "Ethaniel, ou l'art de dire en 50 lignes ce qui peut se dire en 10 lignes" hehe

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

17

Ethaniel :
Ben non cheeky !
Selon cette page :La luminance détermine certes dans une large mesure l'impression de luminosité que dégage un objet, mais ne constitue pas pour autant la mesure absolue de la sensation de clarté. En effet, dans des environnements de luminosités différentes, des surfaces de même luminance peuvent être perçues subjectivement comme ayant des luminosités différentes. Autrement dit, la luminance est une grandeur physique mensurable, alors que la notion de luminosité décrit une sensation subjective. En vision diurne, la luminosité est à peu près proportionnelle au logarithme de la luminance.

Tu n'as pas compris le ./13
avatar
I'm on a boat motherfucker, don't you ever forget