5.18. Histogramme und Statistiken¶
Neben den Operationen, die die Pixel eines Bildes verändern, verfügt die Klasse Image über eine Familie von Methoden, die sie messen – sie fassen die Verteilung der Pixelwerte zusammen, liefern die mittlere und mediane Helligkeit, finden den optimalen Trennpunkt zwischen dunklen und hellen Pixeln und geben die Streuung der Farbkanäle an. Die Messungen fließen auf zwei Arten in Anwendungen ein: als Eingaben für den Code, der entscheidet, welcher Schwellenwert verwendet, welche Verstärkung eingestellt und wie das tonale Profil der Szene aussehen soll; und als diagnostische Signale – „ist die Szene hell genug?“ –, auf die eine Anwendung reagieren kann, ohne eine Entscheidung über ein bestimmtes Pixel zu treffen.
Ausgangspunkt für nahezu jede Messung ist das Histogramm.
5.18.1. Das Histogramm¶
Ein Histogramm eines Bildes ist eine Zählung, wie viele Pixel jeden möglichen Helligkeitswert besitzen. Bei einem Graustufenbild ist das eine Liste von Zählwerten, indexiert durch die Werte 0 bis 255. Bei einem Farbbild sind es drei solcher Listen – eine pro Kanal.
get_histogram() berechnet eines:
h = img.get_histogram()
Das zurückgegebene Objekt ist ein histogram-Ergebnis, das die kanalweisen Bin-Listen sowie einige übergeordnete Abfragen darauf bereitstellt. Die Bin-Zählwerte sind normalisiert, sodass sie sich zu 1.0 summieren – das Histogramm beschreibt das Profil der Verteilung statt der absoluten Pixelanzahl, was die Messungen über Bilder unterschiedlicher Größe hinweg vergleichbar macht.
Bei Graustufenbildern hat das Histogramm einen Bin-Kanal, der als h.bins() (oder gleichwertig h[0]) verfügbar ist. Bei RGB565-Bildern wird das Histogramm im LAB-Farbraum berechnet, der auf der Seite zur binären Schwellenwertbildung eingeführt wurde, mit drei Bin-Kanälen, verfügbar als h.l_bins(), h.a_bins(), h.b_bins() (oder h[0], h[1], h[2]). LAB ist dieselbe Wahl, die die Schwellenwert- und Tracking-Methoden verwenden; Histogramme stimmen mit Schwellenwerten darin überein, in welchem Raum die Farbe gemessen wird.
5.18.2. Bins und die Bin-Anzahl¶
Das standardmäßige Histogramm hat ein Bin pro möglichem Pixelwert – 256 Bins für einen 8-Bit-Kanal. Manchmal ist das eine feinere Auflösung, als die Anwendung benötigt. Ein Klassifikator, der sich nur für das grobe Profil der Verteilung interessiert, ist möglicherweise mit einer kleineren Bin-Anzahl besser bedient – 32 oder sogar 8 Bins –, die sowohl schneller läuft als auch ein saubereres Ergebnis gegenüber Rauschen liefert. Das Schlüsselwort bins (und die kanalweisen l_bins, a_bins, b_bins für Farbe) legt die Anzahl fest:
h = img.get_histogram(bins=32)
Die ROI- und Schwellenwert-Eingrenzung funktioniert genauso wie bei jeder anderen Messmethode. Übergeben Sie eine roi, um das Histogramm auf ein Pixelrechteck zu beschränken; übergeben Sie eine thresholds-Liste, um nur Pixel einzubeziehen, die diesen Bereichen entsprechen. Die Schwellenwert-Form macht „berechne das Histogramm nur der übereinstimmenden Pixel“ zu einer Ein-Aufruf-Operation – ein häufiges Muster, wenn eine Anwendung die Textur eines bereits erkannten Bereichs charakterisieren möchte, ohne die Pixel selbst durchlaufen zu müssen.
Ein Graustufenhistogramm mit drei überlagerten zusammenfassenden Messungen: Otsus Schwellenwert (der Trennpunkt, der die dunklen und hellen Cluster am besten teilt), der Mittelwert und der Median. Jede Messung sagt etwas anderes über dieselbe Verteilung aus.¶
5.18.3. Statistiken¶
Ein Histogramm ist eine Beschreibung der Häufigkeit jedes Wertes; Statistiken sind die daraus abgeleiteten numerischen Zusammenfassungen. Das von get_statistics() zurückgegebene statistics-Objekt trägt den Standardsatz:
mean– das arithmetische Mittel der Pixelwerte.median– der Wert, unterhalb dessen die Hälfte der Pixel liegt.mode– der häufigste Einzelwert.stdev– die Standardabweichung, ein Maß für die Streuung um den Mittelwert.minundmax– die hellsten und dunkelsten vorhandenen Pixelwerte.lqunduq– die unteren und oberen Quartil-Trennpunkte.
Bei einem RGB565-Bild liefern die kanalweisen Formen (l_mean, a_median, b_mode usw.) dieselben Messungen Kanal für Kanal.
Die meisten dieser Zahlen kommen in bestimmten Kontexten vor. mean und stdev zusammen ergeben eine Rauschschätzung: Eine Szene, die einheitlich sein sollte, hat eine kleine stdev, während ein verrauschter Sensor derselben Szene eine größere stdev verleiht. min und max ergeben den Kontrast des Bildes: Je näher sie beieinanderliegen, desto flacher die Szene; je weiter sie auseinanderliegen, desto mehr Dynamikumfang steht dem Algorithmus zur Verfügung. median ist das robuste Zentrum, wenn die Verteilung Ausreißer hat (einige sehr helle Pixel ziehen den Median nicht so, wie sie den Mittelwert ziehen). mode ist der häufigste Einzelwert, nützlich, um die Hintergrundebene eines Bildes zu finden, dessen Hintergrund den Großteil der Pixel abdeckt.
get_statistics() führt den Histogramm-Durchlauf intern aus und fasst ihn dann zusammen; das Übergeben derselben thresholds- und roi-Argumente wie bei einem zuvor berechneten Histogramm liefert die Statistiken für dieselbe Pixelmenge.
5.18.4. Perzentile und CDF-Abfragen¶
Das histogram-Objekt stellt eine get_percentile()-Methode bereit, die einen Bruchteil in einen Pixelwert umwandelt – den Wert, unterhalb dessen der angeforderte Bruchteil der Pixel liegt. h.get_percentile(0.5) ist der Median; h.get_percentile(0.05) und h.get_percentile(0.95) ergeben zusammen ein robustes min/max, das die unteren und oberen 5% als Ausreißer ignoriert.
Das ist die Form, die eine Anwendung verwendet, wenn sie den Bereich der Pixelwerte charakterisieren möchte, ohne dass eine Handvoll vereinzelter heller oder dunkler Pixel die Antwort verzerrt. Das robuste min/max aus dem 5. und 95. Perzentil ist auch die natürliche Eingabe für einen Kontrastdehnungs-Durchlauf – die Pixel-für-Pixel-Neuzuordnung, die im Abschnitt Tonale Korrekturen behandelt wird.
5.18.5. Otsus Methode¶
Histogramme beantworten eine weitere Frage, die es wert ist, gesondert hervorgehoben zu werden: Wie lautet bei einem Bild, dessen Pixel sich in einen „dunklen“ und einen „hellen“ Cluster aufteilen, der Trennpunkt zwischen ihnen? Die Seite zur Schwellenwertbildung hat den Mechanismus bereits nach seinem Ergebnis benannt – einen einzelnen globalen Schwellenwert, den die Anwendung an binary() übergeben kann – hat aber das Wie verschoben. Das Wie ist Otsus Methode, und sie befindet sich auf dem Histogramm.
Die Intuition: Ein Bild mit einem klaren Vordergrund und Hintergrund hat zwei Cluster in seinem Helligkeitshistogramm, mit einem Tal dazwischen. Der richtige Ort zum Schwellwertsetzen ist der Boden des Tals – der Wert, bei dem die beiden Cluster am besten getrennt sind. Otsus Methode durchsucht jeden möglichen Trennpunkt und wählt denjenigen aus, bei dem die Varianzen innerhalb der Cluster am kleinsten sind (was dasselbe ist wie zu sagen, dass die Varianz zwischen den Clustern am größten ist), und das Ergebnis ist die optimale binäre Teilung für die Verteilung dieses bestimmten Bildes.
Das histogram-Objekt stellt Otsu über get_threshold bereit:
h = img.get_histogram()
t = h.get_threshold()
Das zurückgegebene threshold-Objekt hat die Attribute value (für Graustufen) oder l_value / a_value / b_value (für Farbe), die den gewählten Trennpunkt tragen. Wird das Ergebnis direkt wieder in binary() eingespeist, ergibt sich ein selbstabstimmender globaler Schwellenwert, dessen Trennpunkt vom Bild selbst gewählt wird:
img.binary([(t.value, 255)])
Dieses Muster löst nicht das Problem der ungleichmäßigen Beleuchtung, das der filterbasierte adaptive Schwellenwert löst; was es löst, ist die Frage „bei welchem Wert sollte ich schneiden?“, wenn globale Schwellenwertbildung bereits der richtige Ansatz ist. Bei einer Szene, deren Unterscheidung zwischen Vordergrund und Hintergrund gut definiert ist, liegt der von Otsu gewählte Wert in der Regel innerhalb weniger Einheiten von dem, was ein Mensch mit bloßem Auge wählen würde.
5.18.6. Berechnung auf einem Differenzbild¶
Ein nützliches Detail zu get_histogram() und get_statistics(): Beide akzeptieren ein difference-Schlüsselwort, das ein weiteres Image entgegennimmt und das Histogramm (oder die Statistiken) der pixelweisen Differenz zwischen der Quelle und diesem Bild berechnet, ohne ein separates Differenzbild zu allokieren. Das ist der günstige Weg zu fragen „wie stark hat sich die Szene seit dem Referenz-Einzelbild verändert?“, ohne einen expliziten difference()-Aufruf zu bezahlen, um ein Bild zu erzeugen, dessen einziger Zweck es ist, gemessen zu werden. Bei einem kontinuierlich laufenden Bewegungserkennungsskript summiert sich die Ersparnis.