Ich suche nach einer Möglichkeit, mit Python die dominanteste Farbe / den dominantesten Ton in einem Bild zu finden. Entweder der durchschnittliche Farbton oder der häufigste von RGB reicht aus. Ich habe mir die Python Imaging-Bibliothek angesehen und konnte nichts in Bezug auf das, wonach ich gesucht habe, in ihrem Handbuch und auch kurz bei VTK finden.
Ich fand jedoch einen PHP - Skript , das tut , was ich brauche, hier (Anmeldung erforderlich zu Download). Das Skript scheint die Größe des Bildes auf 150 * 150 zu ändern, um die dominanten Farben hervorzuheben. Danach bin ich jedoch ziemlich verloren. Ich habe darüber nachgedacht, etwas zu schreiben, das die Größe des Bildes auf eine kleine Größe ändert und dann jedes zweite Pixel auf das Bild überprüft, obwohl ich mir vorstelle, dass dies sehr ineffizient wäre (obwohl die Implementierung dieser Idee als C-Python-Modul eine Idee sein könnte).
Nach all dem bin ich jedoch immer noch ratlos. Also wende ich mich an dich. Gibt es eine einfache und effiziente Möglichkeit, die dominierende Farbe in einem Bild zu finden?
quelle
Antworten:
Hier ist Code, der das Cluster-Paket von Pillow and Scipy verwendet .
Der Einfachheit halber habe ich den Dateinamen als "image.jpg" fest codiert. Das Ändern der Bildgröße dient der Geschwindigkeit: Wenn Ihnen das Warten nichts ausmacht, kommentieren Sie den Größenänderungsaufruf aus. Wenn dieses Beispielbild von blauen Paprikaschoten verwendet wird , heißt es normalerweise, dass die dominierende Farbe # d8c865 ist, was ungefähr dem hellgelben Bereich links unten von den beiden Paprikaschoten entspricht. Ich sage "normalerweise", weil der verwendete Clustering-Algorithmus einen gewissen Grad an Zufälligkeit aufweist. Es gibt verschiedene Möglichkeiten, dies zu ändern, aber für Ihre Zwecke kann es gut passen. (Überprüfen Sie die Optionen für die Variante kmeans2 (), wenn Sie deterministische Ergebnisse benötigen.)
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))
Hinweis: Wenn ich die Anzahl der zu findenden Cluster von 5 auf 10 oder 15 erhöhe, wurden häufig grünliche oder bläuliche Ergebnisse erzielt. Angesichts des Eingabebildes sind dies auch vernünftige Ergebnisse ... Ich kann auch nicht sagen, welche Farbe in diesem Bild wirklich dominiert, also kann ich den Algorithmus nicht bemängeln!
Auch ein kleiner Bonus: Speichern Sie das verkleinerte Bild nur mit den N häufigsten Farben:
# 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')
quelle
.encode('hex')
ist nicht mehr gültige Syntax , und (2)from PIL import Image
QuelleVersuchen Sie es mit Farbdieb . Es basiert auf
PIL
und funktioniert fantastisch.Installation
pip install colorthief
Verwendung
from colorthief import ColorThief color_thief = ColorThief('/path/to/imagefile') # get the dominant color dominant_color = color_thief.get_color(quality=1)
Es kann auch Farbpalette finden
palette = color_thief.get_palette(color_count=6)
quelle
Die Python Imaging Library verfügt über die Methode getcolors für Bildobjekte:
Ich denke, Sie können immer noch versuchen, die Größe des Bildes vorher zu ändern und zu sehen, ob es besser funktioniert.
quelle
Wenn Sie immer noch nach einer Antwort suchen, hat Folgendes für mich funktioniert, wenn auch nicht besonders effizient:
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)
quelle
Sie können PIL verwenden, um die Bildgröße in jeder Dimension wiederholt um den Faktor 2 zu verkleinern, bis sie 1x1 erreicht. Ich weiß nicht, welchen Algorithmus PIL für das Downscaling um große Faktoren verwendet. Wenn Sie also in einer einzigen Größenänderung direkt zu 1x1 wechseln, gehen möglicherweise Informationen verloren. Es ist vielleicht nicht das effizienteste, aber es gibt Ihnen die "durchschnittliche" Farbe des Bildes.
quelle
Es ist nicht notwendig, k-means zu verwenden, um die dominante Farbe zu finden, wie Peter vorschlägt. Dies macht ein einfaches Problem zu kompliziert. Sie beschränken sich auch auf die Anzahl der ausgewählten Cluster, sodass Sie im Grunde eine Vorstellung davon benötigen, was Sie suchen.
Wie Sie erwähnt und von zvone vorgeschlagen, eine schnelle Lösung , um die häufigste / dominante Farbe zu finden , ist durch die Verwendung von Kissen Bibliothek. Wir müssen nur die Pixel nach ihrer Zählnummer sortieren.
from PIL import Image def find_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
Das einzige Problem besteht darin, dass die Methode
getcolors()
None zurückgibt, wenn die Anzahl der Farben mehr als 256 beträgt. Sie können damit umgehen, indem Sie die Größe des Originalbilds ändern.Alles in allem ist es vielleicht nicht die genaueste Lösung, aber es erledigt die Arbeit.
quelle
thumbnail
anstelle der Größenänderung verwenden, um Zuschneiden oder Dehnen zu vermeiden. (2) Wenn Sie ein Bild mit 2 weißen Pixeln und 100 verschiedenen Ebenen schwärzlicher Pixel haben, erhalten Sie immer noch Weiß.Um Peters Antwort zu ergänzen: Wenn PIL Ihnen ein Bild mit dem Modus "P" oder so ziemlich jedem Modus gibt, der nicht "RGBA" ist, müssen Sie eine Alpha-Maske anwenden, um es in RGBA zu konvertieren. Sie können das ziemlich einfach machen mit:
if im.mode == 'P': im.putalpha(0)
quelle
#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(); }
quelle
Sie können dies auf viele verschiedene Arten tun. Und Sie brauchen nicht wirklich scipy und k-means, da Pillow dies intern bereits für Sie erledigt, wenn Sie entweder die Bildgröße ändern oder das Bild auf eine bestimmte Palette reduzieren.
Lösung 1 : Ändern Sie die Bildgröße auf 1 Pixel.
def get_dominant_color(pil_img): img = pil_img.copy() img.convert("RGB") img.resize((1, 1), resample=0) dominant_color = img.getpixel((0, 0)) return dominant_color
Lösung 2 : Reduzieren Sie die Bildfarben auf eine Palette
def get_dominant_color(pil_img, palette_size=16): # Resize image to speed up processing img = pil_img.copy() img.thumbnail((100, 100)) # Reduce colors (uses k-means internally) paletted = img.convert('P', palette=Image.ADAPTIVE, colors=palette_size) # Find the color that occurs most often palette = paletted.getpalette() color_counts = sorted(paletted.getcolors(), reverse=True) palette_index = color_counts[0][1] dominant_color = palette[palette_index*3:palette_index*3+3] return dominant_color
Beide Lösungen liefern ähnliche Ergebnisse. Die letztere Lösung bietet Ihnen wahrscheinlich mehr Genauigkeit, da wir das Seitenverhältnis beim Ändern der Bildgröße beibehalten. Außerdem erhalten Sie mehr Kontrolle, da Sie das optimieren können
pallete_size
.quelle
Hier ist meine Anpassung basierend auf der Lösung von Peter Handsen. Es ist anders, weil es kmeans ++ verwendet, um anfängliche Cluster-Zentren auszuwählen, was bessere Ergebnisse zu liefern scheint. Sie können auch einen random_state für die deterministische Ausgabe hinzufügen.
import scipy.cluster import sklearn.cluster import numpy from PIL import Image def dominant_colors(image): # PIL image input num_clusters = 10 image = image.resize((150, 150)) # optional, to reduce time ar = numpy.asarray(image) shape = ar.shape ar = ar.reshape(numpy.product(shape[:2]), shape[2]).astype(float) kmeans = sklearn.cluster.KMeans( n_clusters=num_clusters, init="k-means++", max_iter=20, random_state=1000 ).fit(ar) codes = kmeans.cluster_centers_ vecs, dist = scipy.cluster.vq.vq(ar, codes) # assign codes counts, bins = numpy.histogram(vecs, len(codes)) # count occurrences colors = [] for index in numpy.argsort(counts)[::-1]: colors.append(tuple([int(code) for code in codes[index]])) return colors # returns colors in order of dominance
quelle