web-dev-qa-db-de.com

Wie finde ich die durchschnittliche Farbe eines Bildes in Python mit OpenCV?

Ich habe diesen Code ausprobiert:

import cv2
image = cv2.imread("sample.jpg")
pixel = image[200, 550]
print pixel

Aber ich bekomme Fehler wie:

'Nonetype' no attributes error getitem

Dieser Fehler wird nach Ausführung der dritten Codezeile angezeigt.

19

So beheben Sie den Fehler

Es gibt zwei mögliche Ursachen für diesen Fehler:

  1. Der Dateiname ist falsch geschrieben.
  2. Die Bilddatei befindet sich nicht im aktuellen Arbeitsverzeichnis.

Um dieses Problem zu beheben, sollten Sie sicherstellen, dass der Dateiname richtig geschrieben ist (Groß- und Kleinschreibung beachten) und sich die Bilddatei im aktuellen Arbeitsverzeichnis befindet (es gibt zwei Möglichkeiten: Sie können entweder das aktuelle Arbeitsverzeichnis in Ihrem = ändern IDE oder geben Sie den vollständigen Pfad der Datei an).

Durchschnittliche Farbe vs. dominante Farbe

Um dann die "durchschnittliche Farbe" zu berechnen, müssen Sie sich entscheiden, was Sie damit meinen. In einem Graustufenbild ist es einfach der Mittelwert der Graustufen im gesamten Bild, aber bei Farben gibt es keinen "Durchschnitt". In der Tat werden Farben normalerweise durch dreidimensionale Vektoren dargestellt, während Graustufen Skalare sind. Es ist in Ordnung, Skalare zu mitteln, aber es macht keinen Sinn, Vektoren zu mitteln.

Das Bild in seine chromatischen Komponenten aufzuteilen und den Durchschnitt jeder Komponente zu bilden, ist ein möglicher Weg. Dieser Ansatz kann jedoch zu einer bedeutungslosen Farbe führen. Was Sie wirklich wollen, ist dominante Farbe anstatt durchschnittliche Farbe.

Implementierung

Lass uns den Code langsam durchgehen. Wir importieren zunächst die erforderlichen Module und lesen das Bild:

import cv2
import numpy as np
from skimage import io

img = io.imread('https://i.stack.imgur.com/DNM65.png')[:, :, :-1]

Dann können wir den Mittelwert jedes chromatischen Kanals nach einer Methode berechnen, die der von @Ruan B. vorgeschlagenen analog ist:

average = img.mean(axis=0).mean(axis=0)

Als nächstes wenden wir k-means clustering an, um eine Palette mit den repräsentativsten Farben des Bildes zu erstellen (in diesem Spielzeugbeispiel wurde n_colors Auf 5 Gesetzt).

pixels = np.float32(img.reshape(-1, 3))

n_colors = 5
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
flags = cv2.KMEANS_RANDOM_CENTERS

_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, flags)
_, counts = np.unique(labels, return_counts=True)

Und schließlich ist die dominierende Farbe die Palettenfarbe, die auf dem quantisierten Bild am häufigsten vorkommt:

dominant = palette[np.argmax(counts)]

Vergleich der Ergebnisse

Um die Unterschiede zwischen beiden Ansätzen zu veranschaulichen, habe ich das folgende Beispielbild verwendet:

lego

Die erhaltenen Werte für die Durchschnittsfarbe, d. H. Eine Farbe, deren Komponenten das Mittel der drei Farbkanäle sind, und die dominante Farbe, berechnet durch k-Mittel-Clustering, sind ziemlich unterschiedlich:

In [30]: average
Out[30]: array([91.63179156, 69.30190754, 58.11971896])

In [31]: dominant
Out[31]: array([179.3999  ,  27.341282,   2.294441], dtype=float32)

Schauen wir uns an, wie diese Farben aussehen, um die Unterschiede zwischen beiden Ansätzen besser zu verstehen. Im linken Teil der Abbildung unten wird die durchschnittliche Farbe angezeigt. Es zeigt sich deutlich, dass die berechnete Durchschnittsfarbe den Farbgehalt des Originalbildes nicht richtig beschreibt. Tatsächlich gibt es im Originalbild kein einziges Pixel mit dieser Farbe. Der rechte Teil der Abbildung zeigt die fünf repräsentativsten Farben, sortiert von oben nach unten in absteigender Reihenfolge der Wichtigkeit (Häufigkeit des Auftretens). Diese Palette macht deutlich, dass die dominierende Farbe das Rot ist, was der Tatsache entspricht, dass der größte Bereich mit einheitlicher Farbe im Originalbild dem roten Legostück entspricht.

Results

Dies ist der Code, der zum Generieren der obigen Abbildung verwendet wird:

import matplotlib.pyplot as plt

avg_patch = np.ones(shape=img.shape, dtype=np.uint8)*np.uint8(average)

indices = np.argsort(counts)[::-1]   
freqs = np.cumsum(np.hstack([[0], counts[indices]/counts.sum()]))
rows = np.int_(img.shape[0]*freqs)

dom_patch = np.zeros(shape=img.shape, dtype=np.uint8)
for i in range(len(rows) - 1):
    dom_patch[rows[i]:rows[i + 1], :, :] += np.uint8(palette[indices[i]])

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12,6))
ax0.imshow(avg_patch)
ax0.set_title('Average color')
ax0.axis('off')
ax1.imshow(dom_patch)
ax1.set_title('Dominant colors')
ax1.axis('off')
plt.show(fig)

TL; DR antworten

Obwohl die Berechnung der durchschnittlichen Farbe - wie in der Antwort von @Ruan B. vorgeschlagen - aus mathematischer Sicht technisch korrekt ist, kann es sein, dass das Ergebnis den Farbgehalt des Bildes nicht angemessen wiedergibt. Ein sinnvollerer Ansatz ist die Bestimmung der dominanten Farbe durch Vektorquantisierung (Clustering).

51
Tonechas

Ich konnte die durchschnittliche Farbe folgendermaßen ermitteln:

import cv2
import numpy
myimg = cv2.imread('image.jpg')
avg_color_per_row = numpy.average(myimg, axis=0)
avg_color = numpy.average(avg_color_per_row, axis=0)
print(avg_color)

Ergebnis:

[ 197.53434769  217.88439451  209.63799938]

Große Ressource, auf die ich verwiesen habe

19
Ruan B.