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.common.desired_capabilities import DesiredCapabilities

 

Um das Modul nutzen zu können, muss PhantomJS installiert sein. PhantomJS simuliert einen headless Browser und wird als Treiber für selenium angegeben. Nach der Konfiguration sinnvoller Parameter kann eine URL mit einem einfachen driver.get als Image gescraped werden.

 

driver = webdriver.PhantomJS(executable_path='/opt/phantomjs/phantomjs-2.1.1-linux-x86_64/bin/phantomjs',desired_capabilities=dcap)
driver.set_window_size(1280, 800)
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.

Ein Gedanke zu „Farbanteile eines Bildes mit Python auswerten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.