web-dev-qa-db-fra.com

Python - Recherche la couleur dominante / la plus courante dans une image

Je cherche un moyen de trouver la couleur/tonalité la plus dominante dans une image à l'aide de python. Soit la teinte moyenne soit la plus courante en RVB fera l'affaire. J'ai regardé la bibliothèque d'imagerie Python, et je n'ai rien trouvé concernant ce que je cherchais dans leur manuel, et aussi brièvement sur VTK.

J'ai cependant trouvé un PHP qui fait ce dont j'ai besoin, ici (connexion requise pour télécharger). Le script semble redimensionner l'image à 150 * 150, à 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 autres pixels pour son image, bien que j'imagine que ce serait très inefficace ( bien que la mise en œuvre de cette idée en tant que C python pourrait être une idée).

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

53
Blue Peppers

Voici le code utilisant Pillow et package de cluster de Scipy .

Pour plus de simplicité, j'ai codé en dur le nom de fichier comme "image.jpg". Le redimensionnement de l'image est pour la vitesse: si cela ne vous dérange pas d'attendre, commentez l'appel de redimensionnement. Lorsqu'il est exécuté sur ce exemple d'image de poivrons bleus il indique généralement 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 algorithme de clustering utilisé a un certain degré d'aléatoire. Il existe plusieurs façons de changer cela, mais cela peut convenir à vos besoins. (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))

Remarque: lorsque j'augmente le nombre de grappes pour en trouver de 5 à 10 ou 15, cela donne fréquemment des résultats verdâtres ou bleuâtres. Compte tenu de l'image d'entrée, ce sont des résultats raisonnables aussi ... Je ne peux pas non plus dire quelle couleur est vraiment dominante dans cette image, donc je ne blâme pas l'algorithme!

Aussi un petit bonus: enregistrez l'image de taille réduite avec uniquement 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')
55
Peter Hansen

La bibliothèque d'imagerie Python possède des méthodes getcolors sur les objets Image:

im.getcolors () => une liste de (nombre, couleur) tuples ou Aucun

Je suppose que vous pouvez toujours essayer de redimensionner l'image avant cela et voir si elle fonctionne mieux.

14
zvone

Si vous cherchez toujours une réponse, voici ce qui a fonctionné pour moi, mais pas terriblement efficace:

from PIL import Image

def compute_average_image_color(img):
    width, height = img.size

    r_total = 0
    g_total = 0
    b_total = 0

    count = 0
    for x in range(0, width):
        for y in range(0, height):
            r, g, b = img.getpixel((x,y))
            r_total += r
            g_total += g
            b_total += b
            count += 1

    return (r_total/count, g_total/count, b_total/count)

img = Image.open('image.png')
#img = img.resize((50,50))  # Small optimization
average_color = compute_average_image_color(img)
print(average_color)
6
Tim S

Vous pouvez utiliser PIL pour redimensionner l'image à plusieurs reprises 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.

5
Russell Borogove

Pour ajouter à la réponse de Peter, si PIL vous donne une image avec le mode "P" ou à peu près n'importe quel mode qui n'est pas "RGBA", alors vous devez appliquer un masque alpha pour le convertir en RGBA. Vous pouvez le faire assez facilement avec:

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

Essayez Color-voleur . Il est basé sur PIL et fonctionne très bien.

Installation

pip install colorthief

Utilisation

from colorthief import ColorThief
color_thief = ColorThief('/path/to/imagefile')
# get the dominant color
dominant_color = color_thief.get_color(quality=1)

Il peut également trouver une palette de couleurs

palette = color_thief.get_palette(color_count=6)
3
Artem Bernatskyi

Il n'est pas nécessaire d'utiliser k-means pour trouver la couleur dominante comme le suggère Peter. Cela complique un problème simple. Vous vous limitez également par le nombre de clusters que vous sélectionnez, vous avez donc fondamentalement besoin d'une idée de ce que vous regardez.

Comme vous l'avez mentionné et comme suggéré par zvone, une solution rapide pour trouver la couleur la plus courante/dominante consiste à utiliser la bibliothèque Pillow . Nous avons juste besoin de trier les pixels par leur nombre de comptage.

from PIL import Image

    def dominant_color(filename):
        #Resizing parameters
        width, height = 150,150
        image = Image.open(filename)
        image = image.resize((width, height),resample = 0)
        #Get colors from image object
        pixels = image.getcolors(width * height)
        #Sort them by count number(first element of Tuple)
        sorted_pixels = sorted(pixels, key=lambda t: t[0])
        #Get the most frequent color
        dominant_color = sorted_pixels[-1][1]
        return dominant_color

Le seul problème est que la méthode getcolors() renvoie None lorsque la quantité de couleurs est supérieure à 256. Vous pouvez y faire face en redimensionnant l'image d'origine.

Dans l'ensemble, ce n'est peut-être pas la solution la plus précise, mais elle fait le travail.

2
mobiuscreek

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

#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();
}
2
Ankur Gupta