2010-07-13 50 views
38

Estoy buscando una forma de encontrar el color/tono más dominante en una imagen usando python. O bien el tono promedio o el más común de RGB hará. Miré la biblioteca de Python Imaging y no pude encontrar nada relacionado con lo que estaba buscando en su manual, y también brevemente en VTK.Python - Encuentra el color dominante/más común en una imagen

Sin embargo, encontré un script PHP que hace lo que necesito, here (se requiere iniciar sesión). El script parece cambiar el tamaño de la imagen a 150 * 150, para resaltar los colores dominantes. Sin embargo, después de eso, estoy bastante perdido. Consideré escribir algo que redimensionaría la imagen a un tamaño pequeño y luego verifico cada dos píxeles más o menos para su imagen, aunque me imagino que esto sería muy ineficaz (aunque implementar esta idea como un módulo C python podría ser una idea).

Sin embargo, después de todo eso, todavía estoy perplejo. Entonces me dirijo a ti, TAN. ¿Existe una manera fácil y eficiente de encontrar el color dominante en una imagen?

+0

I' Supongo que cambia el tamaño de la imagen para permitir que el algoritmo de reajuste haga algo del promedio por usted. – Skurmedel

Respuesta

39

Aquí está el código haciendo uso de PIL y Scipy's cluster package.

Para simplificar he codificado el nombre del archivo como "image.jpg". Cambiar el tamaño de la imagen es para la velocidad: si no le molesta la espera, comente la llamada de cambio de tamaño. Cuando se ejecuta en este sample image of blue peppers, generalmente dice que el color dominante es # d8c865, que corresponde aproximadamente al área amarillenta brillante en la parte inferior izquierda de los dos pimientos. Digo "generalmente" porque el clustering algorithm utilizado tiene un grado de aleatoriedad. Hay varias maneras en que puede cambiar esto, pero para sus propósitos puede ser adecuado. (Echa un vistazo a las opciones de la variante kmeans2() si necesita resultados deterministas.)

import struct 
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 = ''.join(chr(int(c)) for c in peak).encode('hex') 
print 'most frequent is %s (#%s)' % (peak, colour) 

Nota: cuando se amplío el número de grupos de encontrar desde 5 a 10 o 15, que con frecuencia dio resultados que eran de color verdoso o azulado Dada la imagen de entrada, esos también son resultados razonables ... No puedo decir qué color es realmente dominante en esa imagen, ¡así que no fallo el algoritmo!

también una pequeña bonificación: guardar la imagen en tamaño reducido, con sólo los N colores más frecuentes:

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

Wow. Eso es genial. Casi exactamente lo que estaba buscando. Lo miré mal y tuve la sensación de que la respuesta estaba en algún lugar: P Gracias por su respuesta. –

+0

Gran respuesta. Trabajó para mi. Sin embargo, tuve una pequeña pregunta. ¿Cómo accedo al segundo color más frecuente en el caso de que el negro sea el más frecuente y deseo ignorarlo? – frakman1

+0

@ frakman1, argmax() es solo una función de conveniencia que le da la primera. Lo que tendría que hacer es ordenar la matriz de recuentos (haciendo un seguimiento de los índices originales), luego elegir la segunda entrada (o la última) en lugar de la primera (que es efectivamente lo que hace argmax). –

10

Python Imaging Library tiene getcolors método de objetos de imagen:

im.getcolors() => a list of (count, color) tuples or None

supongo que todavía puede tratar de cambiar el tamaño de la imagen antes de eso y ver si funciona mejor.

5

Puede usar PIL para cambiar el tamaño de la imagen en un factor de 2 en cada dimensión hasta que alcance 1x1. No sé qué algoritmo PIL utiliza para reducir la escala de factores grandes, por lo que ir directamente a 1x1 en un único cambio de tamaño puede perder información. Puede que no sea el más eficiente, pero le dará el color "promedio" de la imagen.

3

Para añadir a la respuesta de Pedro, si PIL le está dando una imagen con el modo "P" o prácticamente cualquier modo que no sea "RGBA", entonces necesitas aplicar una máscara alfa para convertirla a RGBA.Usted puede hacer eso con bastante facilidad con:

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

Below is a c++ Qt based example to guess the predominant image color. You can use PyQt and translate the same to Python equivalent.

#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

Si usted todavía está buscando una respuesta, esto es lo que funcionó para mí, aunque no muy eficiente:

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) 
+0

Para png, debe ajustarlo ligeramente para controlar el hecho de que img.getpixel devuelve r, g, b, a (cuatro valores en lugar de tres). O lo hizo por mí de todos modos. – rossdavidh

+0

Esto pesa píxeles de forma desigual. El píxel final tocado contribuye con la mitad del valor total. El píxel anterior aporta la mitad de eso. Solo los últimos 8 píxeles afectarán el promedio, de hecho. –

+0

Tienes razón, error tonto. Acabo de editar la respuesta; hágame saber si eso funciona. –

Cuestiones relacionadas