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.
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.
[…] Dieser Beitrag findet sich jetzt unter http://blog.dsquare.de/farbanteile-eines-bildes-mit-python-auswerten/ […]
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.
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.