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?.
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')
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.
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)
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.
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)
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)
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.
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();
}