Farbanteile eines Bildes mit Python auswerten

Wenn man die Menge der Farbanteile in einem Bild zählen kann, lassen sich viele spannende Auswertungen erstellen. In folgendem kleinen Projekt habe ich Python und OpenCV genutzt, um die Farbanteile von Bildern auszuwerten und die Ergebnisse weiter zu verwenden.

Um die Verkehrssituation einer bestimmten Region auswerten zu können, wäre es prima, auf Google Maps Verkehrsinformationen zurückgreifen zu können. Dies ist per API leider nicht möglich, daher habe ich den Weg gewählt, in regelmäßigen Abständen einen Screenshot der gewünschten Region anzufertigen. Diese Aufgabe übernimmt natürlich ein kleines Python Programm. Mit Hilfe des Moduls selenium lässt sich Screenscraping leicht automatisieren.

Webseiten automatisiert als Bilder einsammeln

Damit die Anzeige einer Webseite als Bild in regelmäßigen Abständen automatisiert eingesammelt werden kann, wird in Python das Modul selenium importiert.

from selenium import webdriver 
from selenium.webdriver.chrome.options import Options

 

Um das Modul nutzen zu können, muss ein Headless Browser installiert sein. Google Chrome simuliert einen headless Browser und wird als Treiber für selenium angegeben. Die Installation ist einfach, es muss der Treiber chromedriver und der Google Browser Chrome installiert werden. Beide sollten versionsseitig aufeinander abgestimmt sein. Nach der Konfiguration sinnvoller Parameter kann eine URL mit einem einfachen driver.get als Image gescraped werden.

 

chrome_options = Options() 
chrome_options.add_argument("--headless") 
chrome_options.add_argument("--window-size=%s" % WINDOW_SIZE)
chrome_options.binary_location = chrome_path
os.environ["webdriver.chrome.driver"] = hbpath
driver.get('https://www.google.de/maps/@47.8554527,12.1209407,14.25z/data=!5m1!1e1')

Im Ergebnis habe ich PNG Bilder mit der Verkehrssituation zwischen fünf Uhr morgens und 20 Uhr abends gesammelt. Die Screenshots wurden in einem Abstand von 15 Minuten gesammelt.

Screenshot aus Google Maps
Screenshot aus Google Maps

Für einen Zeitraum von einem Tag ergibt sich im Zeitraffer ein guter Eindruck über den täglichen Verlauf des Verkehrs:

Neben dem visuellen Eindruck ist die statistische Auswertung interessant. Vor allem, wenn mehr als ein Tag betrachtet werden soll. Als Indikatoren für den Verkehr werden die Rot- und Grünanteile im Bild ausgewertet werden. Eine solche Auswertung erfolgt mit dem OpenCV Moduls für Python. Die Installation von OpenCV unter Linux Ubuntu wird von Adrian Rosebrock in seinem hervorragenden Blog sehr gut erklärt. Dieser Anleitung sollte man folgen.

Farben zählen mit OpenCV

Um die Farbanteile auswerten zu können, werden neben OpenCV die bekannten Pythonmodule numpy und pandas benötigt.

import cv2 # OpenCV
import numpy as np
import pandas as pd
import os # Some Filemanagement

Das Prinzip ist simpel, genau wie der Code:

Für jede Farbe wird ein Bereich im RGB Farbraum definiert. Mittels OpenCV und numpy wird eine Maske über das Bild gelegt, die alle Werte umfasst, die innerhalb des Farbbereiches liegen. Diese Maske kann anschließend einfach ausgezählt werden: Bildpixel im Farbbereich werden mit dem Wert 255 geflagged, alle anderen mit dem Wert 0 (bei Bildern mit höherer Farbtiefe entsprechend andere Werte).

Mittels eines Bildbearbeitungsprogramms wie z.B. GIMP lässt sich feststellen, welche Farbbereiche die roten und grünen Markierungen im Verkehrsmodus von Google Maps umfassen.

  • Rot: von (192, 0, 0) bis (255, 0, 0)
  • Grün: von (0, 180, 32) bis (132, 202, 80)

Bei der Definition dieser Bereich für OpenCV muss bedacht werden, dass OpenCV im BGR Modus arbeitet, also:

farben = ("rot", "gruen")
farbraum = [([0, 0, 192], [0,0,255]), ([32,180,0], [80,202,132])]
d = dict(zip(farben, farbraum))

Diese Farbräume durchzugehen und sie per numpy auszuzählen ist einfach. Das Ergebnis wird in einem data.frame gespeichert, der exportiert wird.

h = '/path/scraped-pictures/'
l = []
for f in os.listdir(h):
    if f.endswith('png'):
        l.append(h + f)

df = pd.DataFrame({'value':[], 'Frequency':[], 'Datum':[], 'Zeit':[]})
for i in l:
    img = cv2.imread(i, 1)
    for f in farben:
        fr = d[f]
        mask = cv2.inRange(img, np.array(fr[0]), np.array(fr[1]))
        u, c = np.unique(mask, return_counts = True)
        freq = np.asarray((u, c)).T
        freq_df = pd.DataFrame(freq, index = ['black', f], columns = ['value', 'Frequency'])
        freq_df['Datum'] = i.split("/")[4][5:13]
        freq_df['Zeit'] = i.split("/")[4][13:17]
        df = df.append(freq_df)

df.to_csv('/home/user/count.csv')

Es wäre möglich, die weiteren Schritte mittels numpy, mathplotlib etc. in Python durchzuführen. Als alter R-Haudegen, wechsle ich hier allerdings die Umgebung.

Auswertung der gesammelten Daten in R

Die folgende Heatmap aus R zeigt die täglichen Hotspots.

So eine Heatmap lässt sich mit ggplot einfach aus den Daten erzeugen:

ggplot(to.plot, aes(tag, tm, fill = red)) + 
  geom_raster() + 
  scale_fill_gradient(low = "#bfffbf", high = "#9A0000") +
  labs(x = "", y = "") +
  guides(fill = FALSE)

Dabei ist die Datenstruktur direkt aus der im obigen Code erzeugen CSV Datei übernommen:

Diese Boxplots zeigen zum einen, wann der Verkehr besonders stark ist, aber auch an welchen Tagen die Situation stark variiert. Vor allem Sonntags ist gut zu erkennen, dass es sowohl ruhige als auch etwas stärker belastete Sonntage gibt.

Solche Boxplots können mit dem entsprechenden ggplot Code erzeugt werden:

p <- ggplot(df, aes(tm, red.pct.std))
p + geom_boxplot(colour = "#ffd700", fill = "#9A0000") + facet_grid(. ~ tag) + 
  labs(x = "", y = "") +
  theme(axis.ticks = element_blank(), axis.text.y = element_blank())
dev.off()

Aktuell werden zu den Verkehrsdaten zusätzlich Wetterdaten gesammelt. Auf Basis dieser Daten kann mit Hilfe eines statistischen Modells eine Verkehrsprognose entwickelt werden. Durch kleine Erweiterungen des Python Skriptes lassen sich die Hotspots geographisch weiter eingrenzen und Auswerten.

3 Gedanken zu „Farbanteile eines Bildes mit Python auswerten

  1. Hallo,

    ein super Beitrag.
    Ich bin gerade daran das gleiche für eine bestimmte Region umzusetzen. Der Beitrag hilft mir dabei unwahrscheinlich.

    Für die Erzeugung des Quellmaterials habe ich jedoch eine etwas andere Herangehensweise gewählt.
    Ich habe für den Bereich der mich interessiert die Kachel Koordinaten von Google Maps ermittelt, die betreffenden Kacheln heruntergeladen und dann wieder zussamengesetzt.

    1. Hallo Manuel. Das freut mich. Ich bin gerade dabei, einen Container mit dieser Lösung aufzusetzen. Demnächst dann hier. Inzwischen bin ich zudem bei einigen Treibern und beim Headless Browser auf andere Lösungen geswitched. Würde mich freuen, Deine Lösung zu sehen, wenn sie fertig ist.

Schreibe einen Kommentar