105 votes

Python - Trouver la couleur dominante/la plus commune dans une image

Je cherche un moyen de trouver la couleur/ton le plus dominant dans une image en utilisant python. Soit la teinte moyenne, soit la plus commune parmi les couleurs RVB fera l'affaire. J'ai regardé la bibliothèque Python Imaging, et je n'ai pas pu trouver quelque chose en rapport avec ce que je cherchais dans leur manuel, et aussi brièvement à VTK.

J'ai cependant trouvé un script PHP qui fait ce dont j'ai besoin, ici (connexion requise pour le téléchargement). Le script semble redimensionner l'image à 150*150, pour faire ressortir les couleurs dominantes. Cependant, après cela, je suis assez perdu. J'ai envisagé d'écrire quelque chose qui redimensionnerait l'image à une petite taille puis vérifierait tous les pixels ou presque pour son image, bien que j'imagine que cela serait très inefficace (bien que la mise en œuvre de cette idée en tant que module C python pourrait être une idée).

Cependant, après tout cela, je suis toujours perplexe. Je me tourne donc vers vous. Existe-t-il un moyen simple et efficace de trouver la couleur dominante d'une image ?

0 votes

Je suppose qu'il redimensionne l'image pour permettre à l'algorithme de redimensionnement de faire une partie du calcul de la moyenne pour vous.

95voto

Peter Hansen Points 8487

Voici un code qui utilise Oreiller et Le paquet cluster de Scipy .

Pour simplifier, j'ai codé en dur le nom de fichier "image.jpg". Le redimensionnement de l'image est une question de rapidité : si l'attente ne vous dérange pas, commentez l'appel au redimensionnement. Lorsqu'il est exécuté sur ce exemple d'image de poivrons bleus il est généralement dit que la couleur dominante est #d8c865, ce qui correspond à peu près à la zone jaunâtre brillante en bas à gauche des deux poivrons. Je dis "habituellement" parce que le algorithme de groupement utilisé a un certain degré d'aléatoire. Il y a plusieurs façons de modifier cela, mais pour vos besoins, cela peut convenir. (Consultez les options de la variante kmeans2() si vous avez besoin de résultats déterministes).

from __future__ import print_function
import binascii
import struct
from PIL import Image
import numpy as np
import scipy
import scipy.misc
import scipy.cluster

NUM_CLUSTERS = 5

print('reading image')
im = Image.open('image.jpg')
im = im.resize((150, 150))      # optional, to reduce time
ar = np.asarray(im)
shape = ar.shape
ar = ar.reshape(scipy.product(shape[:2]), shape[2]).astype(float)

print('finding clusters')
codes, dist = scipy.cluster.vq.kmeans(ar, NUM_CLUSTERS)
print('cluster centres:\n', codes)

vecs, dist = scipy.cluster.vq.vq(ar, codes)         # assign codes
counts, bins = scipy.histogram(vecs, len(codes))    # count occurrences

index_max = scipy.argmax(counts)                    # find most frequent
peak = codes[index_max]
colour = binascii.hexlify(bytearray(int(c) for c in peak)).decode('ascii')
print('most frequent is %s (#%s)' % (peak, colour))

Note : lorsque j'ai étendu le nombre de clusters à trouver de 5 à 10 ou 15, cela a fréquemment donné des résultats verdâtres ou bleutés. Compte tenu de l'image d'entrée, ces résultats sont également raisonnables... Je ne peux pas non plus dire quelle couleur est vraiment dominante dans cette image, donc je ne blâme pas l'algorithme !

Petit bonus également : sauvegarder l'image réduite avec seulement les N couleurs les plus fréquentes :

# bonus: save image using only the N most common colours
import imageio
c = ar.copy()
for i, code in enumerate(codes):
    c[scipy.r_[scipy.where(vecs==i)],:] = code
imageio.imwrite('clusters.png', c.reshape(*shape).astype(np.uint8))
print('saved clustered image')

3 votes

Wow. C'est génial. Presque exactement ce que je cherchais. J'ai regardé scipy, et j'avais le sentiment que la réponse était quelque part là-dedans :P Merci pour votre réponse.

0 votes

Excellente réponse. Elle a fonctionné pour moi. Cependant, j'avais une petite question. Comment puis-je accéder à la deuxième couleur la plus fréquente dans le cas où le noir est le plus fréquent et que je souhaite l'ignorer ?

0 votes

@frakman1, argmax() est juste une fonction de commodité qui donne la première. Ce que vous devriez faire, c'est trier le tableau des comptes (en gardant la trace des indices originaux), puis choisir la deuxième (ou l'avant-dernière) entrée plutôt que la première (ce qui est effectivement ce que fait argmax).

21voto

zvonimir Points 790

La bibliothèque Python Imaging possède une méthode getcolors sur les objets Image :

im.getcolors() \=> une liste de (count, couleur) tuples ou None

Je suppose que vous pouvez toujours essayer de redimensionner l'image avant cela et voir si cela donne de meilleurs résultats.

4voto

Russell Borogove Points 8423

Vous pourriez utiliser la LIP pour réduire de façon répétée l'image d'un facteur 2 dans chaque dimension jusqu'à ce qu'elle atteigne 1x1. Je ne sais pas quel algorithme PIL utilise pour la réduction d'échelle par de grands facteurs, donc passer directement à 1x1 en un seul redimensionnement pourrait perdre des informations. Ce n'est peut-être pas le plus efficace, mais cela vous donnera la couleur "moyenne" de l'image.

4voto

Samuel Clay Points 597

Pour compléter la réponse de Peter, si l'IAP vous donne une image en mode "P" ou tout autre mode qui n'est pas "RGBA", vous devez appliquer un masque alpha pour la convertir en RGBA. Vous pouvez le faire assez facilement avec :

if im.mode == 'P':
    im.putalpha(0)

2voto

Ankur Gupta Points 1306

Voici un exemple en c++ basé sur Qt pour deviner la couleur prédominante d'une image. Vous pouvez utiliser PyQt et traduire la même chose en équivalent Python.

#include <Qt/QtGui>
#include <Qt/QtCore>
#include <QtGui/QApplication>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QPixmap pixmap("logo.png");
    QImage image = pixmap.toImage();
    QRgb col;
    QMap<QRgb,int> rgbcount;
    QRgb greatest = 0;

    int width = pixmap.width();
    int height = pixmap.height();

    int count = 0;
    for (int i = 0; i < width; ++i)
    {
        for (int j = 0; j < height; ++j)
        {
            col = image.pixel(i, j);
            if (rgbcount.contains(col)) {
                rgbcount[col] = rgbcount[col] + 1;
            }
            else  {
                rgbcount[col] = 1;
            }

            if (rgbcount[col] > count)  {
                greatest = col;
                count = rgbcount[col];
            }

        }
    }
    qDebug() << count << greatest;
    return app.exec();
}

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