Défi de l'algorithme: générer un schéma de couleurs à partir d'une image
Contexte
Donc, je travaille sur une nouvelle itération d'une application web. Et, nous avons constaté que nos utilisateurs sont obsédés par la paresse. Vraiment paresseux. En fait, plus nous faisons de travail pour eux, plus ils aiment le service. Une partie de l'application existante nécessite que l'utilisateur sélectionne un schéma de couleurs à utiliser. Cependant, nous avons une image (une capture d'écran du site Web de l'utilisateur), alors pourquoi ne pouvons-nous pas simplement rassasier leur paresse et le faire pour eux? Réponse: Nous pouvons, et ce sera une programmation amusante l'exercice! :)
Le Défi
Compte tenu d'une image, comment créez-vous un schéma de couleurs correspondant? en d'autres termes, comment sélectionnez-vous les couleurs primaires X dans une image (où X est défini par L'application web). L'image utilisée dans notre situation particulière est une capture d'écran du site Web de l'utilisateur, Prise en pleine résolution (par exemple 1280x1024). (Note: {[10] } veuillez simplement décrire votre algorithme - il n'est pas nécessaire de publier un pseudocode réel.)
Points Bonus (street cred points, pas réels points SO) pour:
- décrivant un algorithme simple mais efficace. Le Code est la façon dont nous créons-gardez-le simple et beau.
- permettant à l'utilisateur de modifier le schéma de couleurs en fonction de diverses "humeurs" telles que "Coloré", "Lumineux", "coupé", "profond", etc. (a la Kuler)
- décrivant une méthode pour déterminer de manière fiable la couleur principaletext utilisée dans la capture d'écran du site (nécessitera probablement sa propre, séparée, algo).
Inspiration
Il existe plusieurs sites existants qui remplissent une fonction similaire. N'hésitez pas à les vérifier et à vous demander: "Comment pourrais-je dupliquer cela? Comment pourrais-je l'améliorer?"
11 réponses
Pour trouver les couleurs primaires X, screencap l'application. Exécuter un histogramme de couleur sur l'image. Les couleurs top X dans l'histogramme sont le thème. Edit: si des dégradés sont utilisés, vous voudrez choisir des "pics" distincts de couleurs; c'est-à-dire que vous pouvez avoir tout un tas de couleurs autour de "orange" si l'orange est l'une des couleurs principales utilisées dans les dégradés. Effectivement, il suffit d'appliquer une certaine distance entre vos couleurs choisies dans l'histogramme.
Peaufiner le le schéma de couleurs peut mieux être fait dans l'espace HSV; Convertissez vos couleurs en espace HSV, et si les utilisateurs veulent qu'il soit "plus lumineux", augmentez la valeur, s'ils veulent qu'il soit plus" coloré", augmentez la Saturation, etc.
Déterminer la couleur du texte peut être mieux fait en caractérisant les zones de grande variabilité (haute fréquence dans L'espace de Fourier). Dans ces zones, vous devriez avoir soit: deux couleurs, texte et arrière-plan, auquel cas votre texte est la couleur moins utilisée; ou vous aurez plusieurs couleurs, texte et couleurs d'image d'arrière-plan, auquel cas la couleur du texte est la couleur la plus courante.
Vous pouvez jeter un oeil à:
Https://github.com/dcollien/Dreamcoat
Qui fait cela dans CoffeeScript (café alphabétisé, donc c'est bien documenté)
Démo ici: http://dcollien.github.io/Dreamcoat/test.html
Il a à la fois une approche de quantification des couleurs et une approche KMeans qui sont combinées.
La quantification des couleurs est le même processus utilisé pour choisir la palette des GIF de faible couleur. Pour obtenir une palette de couleurs à partir d'une image photographique, j'ai utilisé Nick Rabinowitz’ quantifier.js qui est basé sur le MMCQ de LEPTONICA (quantification de coupe médiane modifiée).
Je fais ceci pour trouver la palette utilisée pour les images (de l'illustration).
Je commence avec imagemagick et redimensionne une grande image à une taille réalisable (c'est-à-dire 400 px sur la plus grande dimension.) Cela permet en fait de convertir les différences de couleurs locales subtiles en moins de pixels avec une moyenne de ces couleurs.
Parcourez chaque pixel présent dans l'image redimensionnée, lisez les valeurs RVB pour chaque pixel, convertissez le RVB en HSB et stockez les valeurs HSB dans un tableau.
-
Pour chaque couleur de pixel trouvée, je divise ensuite la plage de teintes (0,255) par 16, la plage de Saturation (0,100) par 10 et la plage de luminosité (0,100) par 10. Arrondir le résultat vers le haut ou vers le bas à un entier. Cela permet de regrouper les pixels en catégories de couleurs similaires.
Donc un pixel avec HSB de 223,64,76 serait dans la catégorie 14,6,8
Dans chaque catégorie, vous pouvez toujours trouver la couleur exacte de chaque pixel, mais pour la plupart les catégories elles-mêmes sont un fermer la correspondance de couleur à l'image source.
Choisissez de diviser le HSB en divisions plus fines si vous voulez une meilleure réplication des couleurs à partir des catégories. IE. diviser chaque H, S, B par 8,5,5 au lieu de 16,10,10.
Comptez les catégories de couleurs les plus répandues, trier et afficher. Je rejette les catégories de couleurs avec très peu de nombres de pixels.
Note: Ceci est vraiment conçu pour les œuvres d'art qui ont très peu de pixels avec des valeurs de couleur identiques (c.-à-d. dégradé.)
Pour la plupart, une page HTML a probablement plus de pixels qui correspondent exactement à une valeur de couleur spécifique (c.-À-D. Couleur d'arrière-plan, Couleur de texte, etc. allez tous être de la même couleur partout où ils apparaissent.)
-
Divisez l'image de l'écran en une grille de R-plusieurs rectangles, dans une "grille" n Par m, chacun avec la largeur (largeur totale / N) et la hauteur (hauteur totale / m).
1a. attribuez un poids aux zones de haut profil de l'écran, telles que la zone de gauche au centre.
1b. Pour chaque rectangle, affecter les pixels dans un espace de (couleur,fréquence)
-
Pour chaque rectangle R, distribution de fréquence f_R, et poids W_R:
2a. déterminer le , je-ème schéma de couleur (par exemple, i = 1 couleur d'arrière-plan), en analysant la "fréquence supérieure", "deuxième fréquence" (savoir f_R[, je,:]) pour chaque bloc.
2b. Pour chaque , je, le mettre dans une table de partition, (color_i,score) où le score = f_R[, je,"fréquence"] * W_R
2c. Le meilleur buteur de l'histoire pour chaque , je sera , je-ème schéma de couleur.
, Théoriquement, si vous avez beaucoup de "bleu sur blanc" ou "rouge sur noir", vous devriez obtenir blanc primaire, bleu secondaire, ou noir primaire, rouge secondaire, par exemple.
Pour votre couleur de texte, basez-la directement sur un calcul de la couleur d'arrière-plan, ou choisissez une couleur secondaire, et si la différence V de HSV est trop faible, basez la couleur de la couleur de schéma calculée, mais augmentez la valeur V.
Pseudo:
float[][] weights =
{ { 1.0, 3.0, 5.0, 5.0, 3.0, 1.0, 1.0, 1.0, 1.0 },
{ 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 2.0 },
{ 2.0, 8.0, 9.0, 9.0, 7.0, 3.0, 6.0, 6.0, 3.0 },
{ 2.0, 8.0, 9.0, 9.0, 7.0, 2.0, 3.0, 3.0, 2.0 },
{ 2.0, 7.0, 9.0, 9.0, 7.0, 2.0, 1.0, 1.0, 1.0 },
{ 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 1.0 },
{ 1.0, 3.0, 5.0, 5.0, 3.0, 2.0, 6.0, 6.0, 2.0 },
{ 1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 6.0, 6.0, 2.0 },
{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0, 3.0, 1.0 } };
// Leave the following implementations to the imagination:
void DivideImageIntoRegions( Image originalImage, out Image[][] regions );
void GetNthMostCommonColorInRegion( Image region, int n, out Color color );
TKey FindMaximum<TKey, TValue>( Map<TKey, TValue> map );
// The method:
Color[] GetPrimaryScheme( Image image, int ncolors, int M = 9, int N = 9 )
{
Color[] scheme = new Color[ncolors];
Image[][] regions = new Image[M][N];
DivideImageIntoRegions( image, regions );
for( int i = 0; i < ncolors; i++ )
{
Map<Color, float> colorScores = new Map<Color, float>();
for( int m = 0; m < M; m++ )
for( int n = 0; n < N; n++ )
{
Color theColor;
GetNthMostCommonColorInRegion( region, i, theColor );
if( colorScores[theColor] == null )
{ colorScores[theColor] = 0; }
colorScores[theColor] += weights[m][n];
}
scheme[i] = FindMaximum( colorScores );
}
return scheme;
}
En regardant ce qui précède, il est clair que s'il y a une région avec peu de variabilité, elle aura la même deuxième couleur la plus commune que la couleur la plus commune. Pour ajuster, la deuxième couleur la plus commune dans un tel cas peut être null, contre laquelle on pourrait se prémunir:
if( theColor != null )
continue;
if( colorScores[theColor] == null )
{ colorScores[theColor] = 0; }
colorScores[theColor] += weights[m][n];
}
Le nom du type d'algorithme que vous voulez est quantification des couleurs .
Malheureusement, je n'ai pas de code source disponible pour vous, mais je suis sûr qu'une recherche google pourrait transformer quelque chose.
En particulier, l'article de Journal du Dr Dobb sur le sujet semble prometteur.
Similaire à la solution de McWafflestix, les spécificités devront être modifiées, mais mon approche générale serait...
(Je suis d'accord que HSV est le bon espace)
Prenez un histogramme de l'image, filtrez-le pour lisser le bruit et trouvez le score le plus élevé où V et S sont dans une gamme (éventuellement dynamique) de couleurs "sujet" probables. Un oiseau rouge sur un ciel bleu exigera que nous soyons assez intelligents pour ne pas baser notre schéma sur le bleu, mais sur le rouge. Cela peut nécessiter quelques suppositions A propos de la composition photo, comme" centré dans le cadre "et" règle des tiers " analyse pourrait vous donner une probabilité d'une couleur étant pertinente. Peu importe, c'est notre couleur de base.
Le long des lignes de Kuler, calculer les couleurs qui complètent la base en se déplaçant autour de la roue chromatique. Points supplémentaires pour un compliment calculé s'il apparaît également en bonne place dans l'histogramme de l'étape 1.
Utilisez la couleur de base et les compliments calculés pour dériver un complément agréable couleurs, telles que des versions plus claires et plus foncées de chacune, plus ou moins saturées, etc.
Il y a déjà beaucoup de bonnes suggestions pour trouver les couleurs primaires et j'essaierais des approches similaires. Pour trouver la couleur du texte, j'ai une autre suggestion.
Calculer l'histogramme pour chaque ligne de l'image de haut en bas. Chaque fois que vous atteignez la ligne de base d'une ligne, il devrait y avoir une très forte baisse de la fréquence de la couleur du texte. La fréquence restera faible jusqu'à ce que vous atteigniez les lettres majuscules de la ligne suivante suivie d'une deuxième étape lorsque vous atteignez le lettres minuscules.
S'il y a un autre pic fort qui devient encore plus grand lorsque vous appuyez sur la ligne de base, vous avez trouvé la couleur d'arrière-plan. Un fond dégradé lissera ce pic et les changements des pics-lorsque vous entrez ou quittez une nouvelle ligne - seront lissés par anticrénelage.
Je suis un peu en retard, mais je mettrais en œuvre une carte Kohonen (http://en.wikipedia.org/wiki/Self-organizing_map) dans un espace couleur 3d. Le nombre de points sur la carte serait le nombre de couleurs distinctes que vous vouliez pour votre palette, puis entraînez votre carte en utilisant tous les pixels de l'image. Je ne l'ai pas essayé moi-même, mais je suis sûr que quelqu'un d'autre y a déjà pensé.
Voici quelques suggestions et discussions autour de différentes approches pour générer un schéma de couleurs à partir d'une image:
Tout d'abord, intégrez/tracez vos pixels dans un espace colorimétrique. Cela peut être RVB, HSL ou un autre espace colorimétrique. Ensuite, vous pouvez utiliser l'une des options suivantes pour générer un schéma de couleurs:
création d'un histogramme de l'espace colorimétrique - cela implique de diviser l'espace en une grille et de compter les pixels dans chaque cellule de la grille. Sélectionnez les N cellules supérieures (histogramme seaux) avec le plus de pixels, et la moyenne des pixels dans chacun pour produire une couleur par cellule. Cela peut être votre schéma de couleurs.
coupe médiane ou une autre technique de partitionnement de l'espace - c'est une belle amélioration par rapport à #1, car elle divisera l'espace en regardant les données.
Clustering Pixels - Cluster les pixels en groupes en utilisant l'une des nombreuses techniques de clustering (k-means, mean-shift, etc.). Ensuite, faites la moyenne des pixels dans chaque groupe pour générer un schéma de couleur.
J'ai écrit un message plus détaillé sur les trois approches ci-dessus ici
J'ai également écrit une application web interactive {[26] } qui vous permet de charger une image et de créer générer une palette de couleurs en utilisant l'une des trois approches ci-dessus. Vous pouvez trouver le code pour sur github
Faire la moyenne de la teinte, de la saturation et de la luminosité séparément tout en conservant les valeurs min/max.
Verrouille la teinte cible de toutes les couleurs à la moyenne et interpole la saturation et la luminosité des x points entre les limites. Cela devrait renvoyer un schéma avec une distribution de couleur identique à la photo, mais avec une variation simple. Peut-être que vous aurez même le look Apple.
J'espère juste que vous n'aurez pas 3 nuances de vomi de chien.