Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
i
Inhaltsverzeichnis
1 Einfuhrung 1
1.1 OpenCV, Python und ITOM . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Warum 8-Bit Graustufen? . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Das grundsatzliche Vorgehen . . . . . . . . . . . . . . . . . . . . . . 6
2 Punktoperationen 10
2.1 Grauwerttransformation im Einzelbild . . . . . . . . . . . . . . . . . 10
2.2 Lokale Grauwerte bei mehreren Bildern . . . . . . . . . . . . . . . . 15
2.3 Farbe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4 Geometrische Transformation . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Ortsabhangige Transformation . . . . . . . . . . . . . . . . . . . . . 19
3 Lineare Filter 20
3.1 Etwas Systemtheorie: Lineare invariante Systeme . . . . . . . . . . 20
3.2 Fourierformalismus . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.3 Die Fouriertransformation von Bildern . . . . . . . . . . . . . . . . . 33
3.4 Eine typische Anwendung zur Fourierfilterung . . . . . . . . . . . . 37
3.5 Einfache Verkettung linearer Filter . . . . . . . . . . . . . . . . . . . 39
3.6 Tiefpassfilter — Mittelung . . . . . . . . . . . . . . . . . . . . . . . . 40
3.7 Hochpassfilter — Kantendetektion 1 . . . . . . . . . . . . . . . . . . 42
4 Nichtlineare Filter 45
ii
INHALTSVERZEICHNIS iii
4.1 Einfache Kombinationen: Kantendetektion 2 . . . . . . . . . . . . . 45
4.2 Rangordnungsfilter und Rauschunterdruckung . . . . . . . . . . . . 47
4.3 Morphologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5 Segmentierung 54
5.1 Connected Components . . . . . . . . . . . . . . . . . . . . . . . . . 54
5.2 Watershed Algorithmus . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.3 Konturdetektion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6 Hough Transformation 58
7 Objekterkennung und Klassifikation 62
7.1 Merkmale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
7.2 Template Matching und Korrelation . . . . . . . . . . . . . . . . . . 64
7.3 Haar Kaskaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
7.4 Pyramiden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
7.5 Klassifikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
7.6 Neuronale Netze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
8 ITOM, Python und OpenCV 74
8.1 Benutzeroberflache und generelles Konzept . . . . . . . . . . . . . . 75
8.2 Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
8.2.1 Die Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
8.2.2 Module und Pakete . . . . . . . . . . . . . . . . . . . . . . . . 85
8.2.3 Numpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
8.3 ITOM Datenobjekte und OpenCV . . . . . . . . . . . . . . . . . . . . 90
8.4 Zugriff auf die Hardware (Kamera) . . . . . . . . . . . . . . . . . . . 92
8.5 Und dann: auf OpenCV . . . . . . . . . . . . . . . . . . . . . . . . . 94
INHALTSVERZEICHNIS iv
8.6 ITOM-eigene Funktionalitaten . . . . . . . . . . . . . . . . . . . . . 95
8.7 Oft benotigte Funktionen und Idiome . . . . . . . . . . . . . . . . . 96
8.7.1 Verzogerungen und Zeiten . . . . . . . . . . . . . . . . . . . 96
8.7.2 Einige Befehle fur Ausgabe und Hauptfenster . . . . . . . . 97
8.7.3 Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
8.7.4 Externe Programme und Kommandozeilenargumente . . . 98
8.7.5 Sonstiges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
8.8 Abschließende Bemerkung . . . . . . . . . . . . . . . . . . . . . . . . 98
INHALTSVERZEICHNIS v
.
Vorwort
Als gut ausgebildeter Ingenieur sollten Sie — anstatt zu googeln — sich die Zeit
nehmen und ordentliche Lehrbucher lesen. Aber: Wir leben in einer schnelllebigen
Zeit, die gekennzeichnet ist durch Verdichtung der Arbeit, Information-Overflow,
Stress und ganz generell wachsende Anforderungen. Auch wenn es uns nicht
gefallt: Kaum ein Student oder im Beruf stehender Ingenieur klagt nicht uber man-
gelnde Zeit und Uberlastung.
Was also tun, wenn die Prufung bevorsteht und noch Nichts gelernt wurde, das
Meeting ansteht und man von dem entscheidenden Thema uberhaupt keine Ah-
nung hat oder aber bei der taglichen Arbeit auf eine Lucke stoßt und sich scheut,
die simple Googlelosung zu wahlen?
Fur diese Falle sind die Bucher der Serie “100” gedacht. Das absolut Wesentliche
zu einem Thema auf maximal 100 Seiten. Dabei verzichten wir auf Beweise und
Details. Was “absolut wesentlich” ist, ist naturlich sehr fallabhangig und subjek-
tiv und ich orientiere mich an den Tatigkeiten, die typischerweise unsere Absol-
venten am Institut fur Technische Optik spater ausuben (so gut ich das beurtei-
len kann). Zielgruppe sind also allgemeine Ingenieure und Optikingenieure aber
nicht theoretische Physiker. Anschaulichkeit wird immer der Vorzug vor Mathe-
matik gegeben und die Anwendbarkeit steht im Vordergrund. Unterschiedliche
Bucher der Serie richten sich an unterschiedliche Ingenieure und bedurfen unter-
schiedlichem Vorwissen.
Die digitale Bildverarbeitung ist eine Schnittstellendisziplin. Fur ihren erfolgrei-
chen Einsatz braucht es in vielen Applikationen eine Mischung aus Optik, Infor-
matik/Rechentechnik und oft auch Mechanik und Elektronik. Die Anwendungen
sind nahezu unbegrenzt. Uberall wo Menschen ihre Augen verwenden kann po-
tentiell auch maschinelles Sehen eingesetzt werden.
In diesem Buch werden im Sinne eines praktischen Uberblicks die absoluten Ba-
sics zur Programmierung digitaler Bildverarbeitung vermittelt. Um so praxisnah
wie moglich zu sein wird hierzu jeweils die Implementierung in OpenCV unter
Python gezeigt.
Wie jedes Buch dieser Reihe ist auch dieses das genaue Gegenteil eines Referenz-
1
werks und es ersetzt auch kein gutes Lehrbuch (z.B. [1, 2, 3]). Es reicht, um sich
einigermaßen passabel uber eine Prufung zu retten, aber es reicht nicht, um eine
Eins zu bekommen. Es reicht, um einen ersten Uberblick zu haben aber naturlich
keineswegs, um digitale Bildverarbeitung als Schwerpunkt zu betreiben.
Teile dieses Buch sind aus Effizienzgrunden (hemmungslos) aus einem anderen
Buch von mir ubernommen [4].
Das Buch befindet sich im Beta-Stadium. Also lassen Sie mich bitte wissen, wenn
Sie Fehler entdecken. Nur durch Ihre aktive Mitarbeit kann die Qualitat wachsen.
V 0.3, Stuttgart, Marz 2018, Tobias Haist
2
Kapitel 1
Einfuhrung
Die automatische Verarbeitung von Bildern, also die Bildverarbeitung1, hat ein
enormes Anwendungspotenzial. Betrachten Sie dazu einfach sich selbst. Sie nut-
zen die Bildanalyse Ihres Gehirns fortlaufend fur nahezu alle Tatigkeiten. Egal,
ob Sie einen Nagel in die Wand schlagen, Auto fahren, ein Bild malen oder zum
Einkaufen gehen: Ohne das Sehen wurde nichts funktionieren. So wichtig die Bild-
verarbeitung auch in Wissenschaft und Technik heute schon ist, im Vergleich zu
dem was moglich ist, stehen wir erst ganz am Anfang.
Die Bedeutung der Bildverarbeitung wird zunehmen. Viele Tatigkeiten, die bis-
lang nur Menschen verrichten konnen, werden durch sie zunehmend automa-
tisiert, andere Analysen (z.B. Gesichtserkennung zum Screening großen Men-
schenmengen) werden erst durch das maschinelle Sehen moglich. Nicht Alles was
moglich ist, ist dabei fur Alle von uns wunschenswert.
In diesem Buch lassen wir alle hardwaretechnischen Fragen, von der Bildaufnah-
metechnik bis zur Rechnerarchitektur, außer acht und konzentrieren uns auf die
Algorithmik.
Weil die Anwendungsszenarien so vielfaltig sind, sind auch auch die eingesetzten
Algorithmen vielfaltig und Sie werden, auch wenn Sie sich wahrend ihres gesam-
ten Berufslebens damit beschaftigen, dennoch nur einen Ausschnitt des Gebiets
beherrschen konnen.
Bei all der Vielfalt gibt es aber ein gewisses Grund Know-How der Bildverarbei-
tung. Trotz der unterschiedlichen Anwendungen haben sich einige Ansatze als
sehr allgemein bewahrt. Von diesen Ansatzen werden wir die (aus meiner Sicht)
1im Gegensatz zur (manuellen) BildBEarbeitung
1
Zentralsten in diesem Buch behandeln. Die Zielrichtung und Auswahl ist dabei
eher in Richtung der technischen Bildverarbeitung wie dem Bereich “Computer
Vision” zuzuordnen.2
Aus meiner Sicht ist es dabei wichtig, einige wenige Dinge wirklich verstan-
den zu haben. Andererseits werden wir nicht jeden Algorithmus im Detail (und
schon gar nicht Implementierungsdetails) begreifen mussen, um ein gutes Grund-
verstandnis zu entwickeln.
Meine Zielgruppe sind Ingenieure, die allgemein einen ersten schnellen Einblick
in das Gebiet haben wollen, vor allem aber auch unsere Studenten, die im Rahmen
einer studentischen Arbeit optische Messsysteme realisieren sollen.
Im ersten Teil des Buches wird es um die Grundlagen gehen. Dennoch werden hier
jeweils schon praktische Beispiele gezeigt. Basis fur die Beispiele wird die Open-
Source Bildverarbeitungsbibliothek OpenCV in Verbindung mit Python sein. Im
zweiten Teil wird eine schnelle Einfuhrung in die notwendigen Basics von Python,
OpenCV und ITOM gegeben. Wenn Sie mehr am schnellen praktischen Erfolg fur
eine konkrete Aufgabe als an der Theorie interessiert sind, dann kann es Sinn ma-
chen, erst den zweiten Teil des Buchs zu lesen.
1.1 OpenCV, Python und ITOM
Bei einem standig wachsenden, aber immer noch jungen Markt der Bildverarbei-
tung gibt es naturlich viele Hersteller von Bildverarbeitungslosungen aller Art.
Im industriellen Umfeld reicht die Spannweite von (meist mittels C++ zu nutzen-
den) Bibliotheken bis zu grafischen Programmiertools, mit denen auch Laien in
die Lage versetzt werden sollen, einfache Inspektionsaufgaben zu losen. Und ge-
rade auch im Bereich der freien Software gibt es eine unuberschaubare Unmenge
an interessanten Tools und Bibliotheken.
Lassen Sie sich von der immensen Vielfalt nicht abschrecken oder aus der Ruhe
bringen. Das Wesentliche an der Bildverarbeitung ist letztlich nicht die konkrete
Bibliothek, sondern die kreative Kombination von Algorithmen, um eine Aufga-
be zu losen. Die Basisalgorithmen, von denen wir hier die wichtigsten behandeln
2Die Abgrenzung ist nicht klar definiert. Tendenziell geht es bei der Computer Vision darum, dem Computer ein
“Verstandnis” der Szenen zu ermoglichen wahrend bei der technischen Bildverarbeitung meist konkrete Mess- und
Prufaufgaben im Vordergrund stehen.
2
werden, sind in allen großen Bibliotheken vorhanden und es spielt zunachst keine
Rolle, ob Sie Mathematica, Halcon oder ImageJ, C++, Python oder Java verwen-
den. Fur die Beispiele in diesem Buch werden wir OpenCV und Python einsetzen.
OpenCV ist unter den frei verfugbaren großen Bildverarbeitungsbibliotheken der
de-Facto Standard. Alle wesentlichen Funktionalitaten sind enthalten, es lauft auf
einer Vielzahl von Plattformen, es gibt viel Literatur und im Netz findet man zu
allen Aspekten von OpenCV Rat. OpenCV wird nicht nur in der Wissenschaft und
von Hobbyisten sondern auch stark im professionellen Umfeld genutzt.
Wir konnen OpenCV sowohl unter C++ wie auch unter Python einsetzen. Ob-
wohl ich personlich keinen Spaß an Python habe und jederzeit C++ bevorzuge,
ist Python hier dennoch die richtige Wahl. Die Sprache hat eine inzwischen enor-
me Verbreitung und es gibt eine riesige Auswahl an freien Bibliotheken zu allen
moglichen Themengebieten. Insbesondere im Bereich Signalanalyse und numeri-
sche Mathematik stehen zahlreiche qualitativ hochwertige und umfangreiche Pa-
kete zur Verfugung.
Vor allem aber ist der Einstieg deutlich einfacher als in C++ und der Workflow,
der durch die Interpretersprache gegeben ist, bietet sich gut fur den Zweck dieses
Buches aber auch zum Bau von Prototypen an.
Ein weiterer wichtiger Punkt fur Python in Verbindung mit OpenCV ist, dass
das die Kombination ist, die im OpenSource Mess- und Automatisierungssystem
ITOM zur Verfugung steht. Dieses System wurde am Institut fur Technische Optik
entwickelt und hat sich seither in einer Unzahl von Aufbauten und Messsystemen
bewahrt. In aller Regel verwenden unsere Studenten ITOM zur Realisierung ih-
rer Aufbauten. Und da dieses Buch vor allem fur unsere Studenten geschrieben
ist, liegt es nahe, auch hier ITOM zu verwenden. ITOM ist sowohl unter Win-
dows wie auch Linux frei verfugbar und bietet neben einer Vielzahl von Auswer-
temoglichkeiten, OpenCV und Python vor allem auch den bequemen Zugriff auf
viele verschiedene Kameratypen und andere Hardware.
Zum Nachvollziehen der Beispiele und Experimentieren konnen Sie also entwe-
der Python in Verbindung mit OpenCV direkt verwenden oder aber ITOM einset-
zen.
Je nachdem welche Programmiererfahrung Sie schon haben und ob Sie eher an
der Theorie oder eher am schnellen praktischen Nutzen fur ein konkret anste-
hendes Problem interessiert sind konnen Sie nun entweder einfach weiter lesen,
3
oder aber zunachst mit Teil 2 des Buchs, einer minimalistischen Einfuhrung in Py-
thon, ITOM und OpenCV beginnen. (Die Installation der Tools wird im Anhang
beschrieben.)
Als Basisgerust verwenden wir in etwa das folgende kleine Script, das Sie dann
jeweils fur Ihre Versuche anpassen konnen. Sie mussen sich bei den abgedruck-
ten Befehlen und Code-Schnipseln immer das Basisgerust dazu denken, denn
naturlich brauchen Sie immer erst mal ein Bild, das sie auswerten wollen und
am Schluss wollen Sie das Ergebnis auch darstellen.
import cv2 # Wir importieren OpenCV (Version 3.2)
import numpy as np # Wir brauchen auch numpy
import skimage.data as standard # Fur StandardBilder
bild = standard.camera() # Standard "Cameraman" Bild
bildfarbe = cv2.imread("a.png") # Bild aus File laden
bild = bild[:,:,1] # und z.B. Grun-Kanal extrahieren
plot(bild) # Darstellen des Bilds
cv2.imwrite("erg.png", bildfarbe) # Speichern
Das Paket skimage.data haben wir eingebunden, um einige Standardbilder zur
Verfugung zu haben3. Sie konnen naturlich auch gleich mit eigenem Bildmaterial
starten und dieses per File laden.
Falls Sie kein ITOM nutzen (was schade ware, denn allein der integrierte Viewer
ist ein extrem hilfreiches Tool fur den Bildverarbeiter) mussen Sie fur die Ausgabe
die ITOM Variante (plot(...)) durch die Variante cv2.imshow(“title”, bild) ersetzen.
1.2 Warum 8-Bit Graustufen?
Obwohl wir in ITOM, OpenCV und Python Bilder mit beliebiger Bittiefe und Ka-
nalzahl verwenden konnen, werden wir uns fur die Beispiele meist auf 8-Bit Grau-
stufenbilder beschranken.3Weitere Bilder: moon(), clock(), chelsea(), hubble deep field(), immunohistochemistry(), page(), text(), rocket(),
coffee()
4
8-Bit Graustufenbilder? Das wird Ihnen nun vermutlich in Zeiten von High Dyna-
mic Range Imaging und der preisgunstigen Verfugbarkeit von Farbe vermutlich
sehr antiquiert vorkommen. Aber da tauschen Sie sich. Nach wie vor werden mo-
nochrome Bilder mit 8-Bit in den allermeisten professionellen messtechnischen
Anwendungen der Bildverarbeitung verwendet. Und zwar aus gutem Grund.
Farbsensoren werden wirklich nur dann eingesetzt, wenn die Farbinformation
wirklich benotigt wird4. Farbe fuhrt fur die Optik aufgrund der chromatischen
Aberration entweder zu deutlich erhohtem Aufwand/Kosten oder aber zu einer
Verminderung der Auflosung. Auch das Signal-Rauschverhaltnis sinkt, denn ein
nicht geringer Teil des Lichts (Großenordnung: 2/3) wird durch die Filter ver-
nichtet. Dadurch sinkt bei ansonsten gleich aufwandiger Systemauslegung (glei-
che Beleuchtung, gleiche Belichtungszeit, gleiche Scharfentiefe) das Signal-Rausch-
Verhaltnis. Abgesehen davon muss naturlich die dreifache Datenmenge gegenuber
einem Monochromsignal ubertragen, gespeichert und ausgewertet werden. Insge-
samt also viele Einschrankungen und Kosten, die man nur wahlt, wenn auch ein
deutlicher Nutzen besteht.
Kommen wir zur Bittiefe der Bilder. In der Regel benotigen wir eine lineare Kennli-
nie. Mit 8 Bit konnen wir 256 unterschiedliche Quantisierungsstufen unterscheiden.
Selbst bei Ausschalten aller anderen Rauschquellen ist das unvermeidliche Pho-
tonenrauschen durch die Wurzel der Sattigungskapazitat des Sensors gegeben. Bei
einem typischen BV-Sensor mit ca. 10.000 Elektronen kommen wir damit zu ei-
nem maximalen Signal-Rausch-Verhaltnis von 100. Die 8-Bit reichen hierfur in der
Regel vollig aus. Ein Ubergang zu mehr Bits erfordert aufgrund der 8-Bit Zentrie-
rung der Computertechnik entweder den Einsatz von aufwandigen Data-Packing
Technologien oder gleich den Ubergang zu einer 16 Bit Speicherung und Verar-
beitung. Damit verdoppeln sich aber (ohne wirklichen Gewinn) Speicher- und
Performance-Bandbreiten.
Die Details zur Auslegung der Hardware von Bildverarbeitungssystemen werden
recht ausfuhrlich in [4] erklart. Nehmen Sie aber einfach mit, dass Sie ohne guten
Grund nicht von “8-Bit Monochrom” abweichen sollten.5
Und diesen Standard werden wir in diesem Buch verwenden. Ein Ubertrag auf
Farbbilder und andere Bittiefen ist in OpenCV in der Regel kein großeres Problem.
4und selbst dann wird diese Information oft durch Monochromsensoren und zeitliches Multiplexing (z.B. durch
schaltbare LEDs) gewonnen5Keine Sorge, ihre neue 12 Bit Monochrom BV-Kamera kann auch weiterhin 8-Bit Daten ubertragen.
5
1.3 Das grundsatzliche Vorgehen
In diesem Buch geht es um die Algorithmik der Bildverarbeitung. Bildverarbei-
tung beginnt aber viel fruher, namlich beim System, das letztlich Bilder (oder all-
gemeiner: Daten) generiert. Hierzu finden Sie nichts in diesem Buch. Das bedeu-
tet aber nicht, dass dieser Bereich nicht wichtig ware. Ganz im Gegenteil. Er ist so
wichtig, dass ich ein eigenes Buch daruber geschrieben habe.
Was bei der mangelhaften Ausgestaltung der Bildaufnahme schief geht, ist in der
Regel kaum oder gar nicht mehr gut zu machen. Die Zusammenhange sind kom-
plex, denn wir haben es mit verschiedener Hardware (Optik, Bildsensoren, Be-
leuchtung, Zufuhrung, Rechentechnik) zu tun und jede der eingesetzten Kompo-
nenten hat eine Vielzahl von Freiheitsgraden.
Betrachten Sie beispielhaft die anscheinend einfache Wahl des Objektivs: Welche
Perspektive brauchen Sie, in welchem Wellenlangenbereich werden die Ergebnis-
se am besten sein, welche Auflosungen (lateral, Tiefe, Position) sind gewunscht
und wie sollen sie spezifiziert bzw. quantifiziert werden? Wie ist die Brennwei-
te zu wahlen? Aber auch Abbildungsmaßstab und Arbeitsabstand konnen vari-
iert werden. Welche Filter sollten wo eingesetzt werden ... etc ... ? Je nach An-
wendung haben Sie ganz unterschiedliche Anforderungen und die Berechnung
der wesentlichen Großen ist alles andere als einfach, denn die Parameter sind
eng miteinander verwoben (z.B. Blendenzahl mit Belichtungszeit und Lichtfluss
der Beleuchtung in Zusammenhang mit Beleuchtungsgeometrie und Homoge-
nitatsanforderungen).
Sie konnen bei der Algorithmik unendlich viel Aufwand in die Implementierung
stecken, um diese robust zu machen, nur weil die Datengewinnung selbst unvor-
teilhaft implementiert ist. Glauben Sie also nicht, dass Bildverarbeitung erst mit
der eigentlichen Bildverarbeitung beginnt.
Als generelle Faustregel sollten Sie beachten, dass das Bildaufnahmesystem idea-
lerweise so ausgelegt wird, dass es mit einer großen Anderung (i.d.R. starke
Bildanderung) auf Anderungen reagiert, die von Interesse sind. Gleichzeitig sol-
len irrelevante Anderungen (z.B. Anderung des Fremdlichtanteils je nach Sonnen-
stand) moglichst geringe Anderungen im Bild hervorrufen. Ganz generell gilt: Je
weniger ungewollte Anderungen im Bildmaterial vorhanden sind, desto robuster
und zuverlassiger wird ihr System sein.
6
Um ein konkretes Problem mittels Bildverarbeitung zu losen, sollten Sie — bevor
Sie sich uberhastet auf die Algorithmen sturzen — erst wirklich folgende Punkte
durchgehen:
1. Verstehen Sie die Aufgabenstellung genau und sorgen Sie fur eine klare Spe-
zifikation6.
2. Was sollen Sie wirklich “sehen” und was soll moglichst nicht sichtbar sein
bzw. keinen Einfluss haben? Welche Merkmale sind relevant?
3. Wie konnen Sie diese Merkmale besonders gut sichtbar machen?
4. Wie konnen Sie alle anderen Effekte moglichst gut unterdrucken bzw. zumin-
dest stabil halten?
5. Erst jetzt machen Sie sich Gedanken uber die konkrete Wahl der Hardware.
6. Uberlegen Sie bereits im Vorfeld: Woher bekommen Sie genugend sinnvolle
Testbilder/Testobjekte und wie konnte eine Verifikation (und ggf. Abnahme)
aussehen?
Und erst dann, wenn Sie diese wesentlichen Punkte abgearbeitet haben, macht es
Sinn, wirklich die Realisierung der Algorithmik zu starten. Erst jetzt brauchen Sie
den Inhalt dieses Buches. Die eigentliche Software ist in der Regel nur ein (klei-
ner) Aspekt der Gesamtlosung einer Bildverarbeitungsaufgabe. Eine ordentlich
ausgewahlte Hardware fuhrt oft dazu, dass die softwareseitige Implementierung
so einfach wird, dass fertige Komponenten (auch von Laien) parametriert einge-
setzt werden konnen.
Eine Bildverarbeitung programmiert man ublicherweise indem man verschiedene
Basisalgorithmen anwendet und die Ergebnisse kombiniert. Hierbei sind Erfah-
rung, Kreativitat und Experimentierfreude, aber auch systematisches Vorgehen
und Absicherung durch genugend Tests gefragt.
Naturlich gibt es auch Varianten, bei denen automatisiert — basierend auf Bildka-
talogen — der Computer selbst die geeignete Kombination von Basisalgorithmen
ermittelt.7 Aber beim Stand der Technik werden fur die meisten Anwendungen
die Kombinationen von Hand, also von menschlichen Experten, entworfen.
6Auch wichtig: Wie wird sich diese eventuell im Lauf der Zeit andern?7Neuronale Netze (vgl. Kapitel 7.6.5) konnen letztlich als eine Variante gesehen werden.
7
Die Schwierigkeit ist ganz generell, dass die Optimierung einer Bildverarbeitung
naturlich ein Problem mit sehr vielen Freiheitsgraden ist, denn viele Algorithmen
mit vielen freien Parametern sind potenziell einsetzbar.
Oft sind die erzielbaren Ergebnisse stark vom Eingangsbildmaterial abhangig.
Kleine Anderungen reichen hier oft schon aus, um den Algorithmus zum Schei-
tern zu bringen oder eine neue Parametrierung zu erzwingen. Wann immer
moglich wird man in der Praxis daher dafur sorgen, dass das Bildmaterial sich
prinzipiell moglichst wenig andert und dass die Aufnahmebedingungen (z.B.
Beleuchtung) moglichst konstant bleiben. Eine Optimierung der Hardware (z.B.
Farbfilter zur Fremdlichtunterdruckung) ist meist viel einfacher als unter schwie-
rigen oder gar wechselhaften Bedingungen ein System stabil zur korrekten Ar-
beitsweise zu bringen.
Auf hochster Abstraktionsebene wird die Gesamtverarbeitung oft in verschiede-
ne Teilaspekte unterteilt. Besonders die Bildvorverarbeitung kann meist vom Rest
der Verarbeitung getrennt werden. Hierbei geht es zunachst um eine low-Level
Verbesserung des Bildmaterials, z.B. den Einsatz von Filtern zur Rauschunter-
druckung oder auch die Rektifizierung von Bildern.
Teilweise sind die Bereiche aber nicht immer klar trennbar. Die Rotation des Bildes
zu einer korrekten Grundorientierung erfordert z.B. meist, dass zunachst das re-
levante Objekt detektiert und segmentiert wurde; beides Aufgaben die — je nach
Objekt — sehr komplex werden konnen.
Fur eine komplette Verarbeitung werden in der Regel also verschiedene Teilver-
arbeitungsschritte angewendet. Diese konnen (allerdings mit Uberschneidungen
und oft großer Unscharfe) in Klassen eingeteilt werden. Die fur uns zunachst
wichtigsten Basisklassen sind:
• Bildvorverarbeitung und Rektifizierung
• Segmentierung
• Merkmalsdetektion
• Klassifikation
• Messung
• Objekterkennung
8
• Aufmerksamkeitssteuerung / Regions-of-interest
Im Laufe der folgenden Kapitel werden wir die wichtigsten und bekanntesten
Grundalgorithmen aus diesen Bereichen kennenlernen. Teilweise werde ich an-
klingen lassen, wie die Algorithmen ublicherweise kombiniert werden.
9
Kapitel 2
Punktoperationen
In fast allen Anwendungen der Bildverarbeitung sind Bilder zweidimensionale
Arrays wobei jeder Arrayeintrag einer Raumposition entspricht. Die Punkte (Pi-
xel) sind in aller Regel aquidistant angeordnet; etwaige Abweichungen, z.B. auf-
grund von Verzeichnungen der Optik, lassen sich ausgleichen. Bei monochromen
Bildern besteht jeder Arrayeintrag eben aus genau einer Zahl, dem Grauwert des
Bilds an der Position (x,y).
Punktoperationen sind nun einfach Operationen, die — unabhangig von der Nach-
barschaft oder der Position, die Bildpunkte andern.
2.1 Grauwerttransformation im Einzelbild
Die einfachste Bildverarbeitungsoperation ist schlicht, den Grauwert basierend
auf dem bisherigen Grauwert zu andern. Die Nachbarschaft des Pixels wird da-
bei außer acht gelassen und man spricht dementsprechend von lokaler Grauwert-
transformation. Die Grauwerttransfomation selbst kann durch eine mathemati-
sche Funktion oder tabeliert, eine sogenannte Lookup-Table (LUT), erfolgen:
GW ′(x, y) = LUT [GW (x, y)] (2.1)
Entsprechende Transformationen finden sich — ob digital oder analog — in vielen
Geraten, vom Monitor bis zur Empfindlichkeitskurve von Film.
Vor allem der visuelle Eindruck eines Bilds kann maßgeblich durch die entspre-
chende Transformationen beeinflusst werden. Besonders haufig werden einfache
10
Normalisierungen (hier dargestellt fur 8-Bit Bilder)
GW ′(x, y) = 255 · GW (x, y)
max(GW (x, y))(2.2)
cv2.normalize(bild, ergebnisNorm, 0, 255, cv2.NORM_MINMAX)
und Gamma Transformationen verwendet (s. Abb. 2.1.1):
GW ′(x, y) = A GW (x, y)γ (2.3)
Auch die Einstellungen “Kontrast” und “Helligkeit” bei vielen Bildbearbeitungs-
programmen andern letztlich nur die Kennlinie bzw. Lookup-Table des Bildes.
Die Gamma-Transformation in folgendem kleinen Codebeispiel zeigt, wie man
beliebige Lookup Tabellen in OpenCV anwenden kann.
def gamma(img, gamma=1.0):
lut = np.array([((i / 255.0) ** (1.0/gamma)) * 255
for i in np.arange(0, 256)]).astype("uint8")
return cv2.LUT(img, lut)
ergebnis = gamma(bild, 0.5)
Wenn als Funktion eine Binarisierung verwendet wird, dann spricht man von
Thresholding:
GW (x, y) =
{
0 |GW (x, y) < Schwelle
255 |GW (x, y) ≥ Schwelle(2.4)
ret, ergebnis = cv2.threshold(bild, 100 ,255,cv2.THRESH_BINARY)
Die Binarisierungsschwelle hat hier naturlich einen erheblichen Einfluss auf das
Ergebnis und das Verfahren selbst kann in einfachen Fallen bereits zur Segmentie-
rung von Objekten vor einem Hintergrund dienen (s. Abb. 2.1.2). Meist ist das Er-
gebnis noch nicht perfekt und muss dann mit nichtlokalen Operatoren (z.B. Mor-
phologie, Kapitel 4.3) nachbearbeitet werden.
11
(a) Eingangsbild (b) Normalisiert
(c) normalisiert und Gamma = 0.5 (d) normalisiert und Gamma = 2
Abbildung 2.1.1: Beispiele fur einfache Lookup Tabellen
Zur Festlegung der Schwelle wird gerne das Histogramm des Bildes genutzt. Das
Histogramm gibt an, wie oft ein Grauwert im Bild vorkommt, d.h. wieviele Pixel
einen bestimmten Grauwert haben (s. Abb. 2.1.3).
Die Binarisierung nach Otsu sucht im Histogramm nach zwei Peaks der Hellig-
keitsverteilung, einen fur den Hintergrund und einen fur den Vordergrund und
wahlt die Binarisierungsschwelle dazwischen.
ret, ergebnis = cv2.threshold(bild,0,255,cv2.THRESH_BINARY + \
cv2.THRESH_OTSU)
Eine Variante zur subjektiven Bildverbesserung ist der Histogrammausgleich (Hi-
stogrammequalisation, Histogrammeinebnung, Histogrammegalisierung). In die-
12
(a) Eingangsbild (b) Ergebnis
Abbildung 2.1.2: Einfache Segmentierung mittels Binarisierung von Farb und Inten-
sitatsinformation: Das Bild wird zunachst von RGB in den Farbraum HSI transformiert (vgl.
Abschnitt 2.3). Nachfolgend werden alle Pixel mit Farbwert (H) zwischen 100 und 115 und
Helligkeitswert kleiner 190 weiß markiert. Der Rest ist schwarz.
sem Fall werden die Grauwerte (uber die LUT) so transformiert, dass das Hi-
stogramm moglichst “flach” wird. Dadurch wird der subjektive Kontrast meist
deutlich verbessert (Abb. 2.1.3). Der nachfolgende Code demonstriert die Imple-
mentierung mittels OpenCV. Gleichzeitig wird die Berechnung des Histogramms
gezeigt.1
histogram = cv2.calcHist( [bild], [0], None, [255], [0, 256])
ergebnis = cv2.equalizeHist(bild)
Bei Farbbildern hat naturlich jeder der Farbkanale (bei typischen Farbbildern: rot,
grun und blau) ein eigenes Histogramm und eine eigene LUT kann angewendet
werden.
Umgekehrt kann man aber auch ein Graubild in drei (zunachst gleiche) Farb-
kanale auftrennen und dann jedem Farbkanal eine eigene LUT geben. Entspre-
chend ergeben sich Bilder, bei denen die Farbe letztlich die Helligkeit kodiert,
sogenannte Falschfarbenbilder (s. Abb. 2.1.4). Diese Bilder werden oft zur Visua-
lisierung von Details mit geringen Kontrasten eingesetzt.
Das Histogramm erlaubt es auch, zu beurteilen, ob bzw. wie stark ein Bild uber
1In diesem Fall wird der Kanal 0 des einzigen Bilds “bild” (man kann auch gleichzeitig mehrere Eingangsbilder
verwenden) in 256 Bins unterteilt.
13
(a) Eingangsbild (b) Ergebnis nach Histogrammausgleich
(c) Histogramm Eingangsbild (d) Histogramm nach Histogrammausgleich
Abbildung 2.1.3: Beim Histogrammausgleich wird das Histogramm des Bildes durch eine einfa-
che Punkttransformation so geandert, dass im Mittel ein moglichst gleichverteiltes Histogramm
vorliegt. (Eine Verschmierung des Histogramms wurde die Gleichverteilung zeigen.)
oder unterbelichtet ist. Und man kann Histogramm auch zum Vergleich von Bil-
dern (oder Bildteilen) nutzen, um dann sehr schnell zu entscheiden, ob ein Bild
wohl ein bestimmtes Muster bzw. Detail enthalt. Oder aber man wahlt zwei
Kanale und bildet ganz analog ein zweidimensionales Histogramm, das eben gut
Korrelationen zeigt.
bild = standard.astronaut()
hist = cv2.calcHist( [bild], [0,1], None, [16,16], [0, 256,0, 256] )
plot(hist)
So kann man z.B. die Helligkeit mit der Farbinformation korrelieren. Insbesondere
fur eine einfache Vorsortierung von Bildern nach Inhalt konnen entsprechende
Features gut verwendet werden (vgl. Abb. 2.1.5).
14
(a) LUT 1 (b) LUT 2
Abbildung 2.1.4: Falschfarbendarstellung eines monochromen Bildes
Neben dem Histogramm sind fur eine statistische Beurteilung naturlich auch der
Mittelwert und die Standardabweichung der Grauwerteverteilung interessant. Ins-
besondere in eigentlich homogenen Flachen wird die Standardabweichung als
Maß fur das Rauschen im Bild verwendet, aber auch die Bildscharfe lasst sich damit
(z.B. in Kombination mit einem Kantenfilter) sehr gut detektieren.
mittelwert = np.mean(bild)
stdabw = np.std(bild)
median = np.median(bild)
2.2 Lokale Grauwerte bei mehreren Bildern
Naturlich konnen wir beim Vorhandensein mehrerer Bilder diese auch lokal kom-
binieren. Dazu sind vor allem die arithmetischen Grundoperationen und einfache
logische Operatoren (bei Binarbildern) von Belang.
Die Addition b(x, y) = a1(x, y) + a2(x, y) wahlt man zur zeitlichen Mittelung
(und damit Rauschreduktion). Solange zwischen den Aufnahmen die Szene nicht
verandert wird kann bei N Aufnahmen das Rauschen durch einfache Mitte-
lung der N Bilder um den Faktor√N reduziert werden, denn die Standardab-
15
Abbildung 2.1.5: 2D Histogramm eines Farbbilds (Blau- und Grunkanal)
weichung einer zufalligen Messreihe nimmt eben mit der Wurzel der Messrei-
henlange ab. Man kann im Sinne einer eher physikalischen Deutung auch sa-
gen, dass bei N Messungen die letztlich genutzte Anzahl Photonen um den Fak-
tor N gegenuber der Einzelmessung steigt. Damit steigt eben das Signal-Rausch-
Verhaltnis um N/√N =
√N .
Subtraktionen b(x, y) = a1(x, y)− a2(x, y) werden oft zum Ausblenden von Hinter-
grundsinformation aber auch zur Detektion von Bildanderungen (z.B. Bewegungs-
meldung) genutzt. Wenn z.B. bei einem Videostrom fortlaufend die Differenz zwei-
er nacheinander folgender Bilder berechnet wird, dann ergibt sich jeweils ein Null-
bild (Grauwert nahe Null fur alle Pixel) solange sich nichts in der Szene andert.
Zu beachten ist, dass bei der Subtraktion naturlich negative Werte auftreten
konnen bzw. ganz allgemein bei arithmetischen Operationen der Wertebereich
gegenuber dem Wertebereich der Eingangsbilder verandert ist. Daher wird oft
ein sogenanntes Clipping nachgeschaltet, um den Wertebereich wieder einzu-
schranken (z.B. auf 0 bis 255).
Multiplikation bzw. Division wird genutzt, um inhomogene Beleuchtungs-
verhaltnisse auszugleichen. Wenn die Szene mit der Helligkeitsverteilung I(x, y)
beleuchtet wird, dann muss entsprechend das aufgenommene Bild mit 1/I(x, y)
multipliziert werden, um ein beleuchtungsunabhangiges Ergebnis zu erhalten (so-
genannte Shading-Korrektur).
Und, Oder bzw. exklusives Oder werden verwendet, um boolsche Bilder zu ver-
knupfen. Z.B. konnen bereits Binarbilder vorliegen, die Segmentierungsergebnis-
16
se verschiedener Algorithmen oder Kanale reprasentieren. Sowohl Oder wie auch
Und kann hier die geeignete Verknupfung sein, um eine finale Segmentierungs-
entscheidung zu treffen. Oft werden auch komplizierte Masken bzw. Regions-of-
Interest auf diese Weise verknupft.
2.3 Farbe
Ublicherweise werden Farbbilder im RGB Format gespeichert, d.h. — ganz entspre-
chend den menschlichen Sinneszellen — gibt es einen Kanal fur jeweils Rot, Grun
und Blau.
Obwohl unublich, speichert OpenCV die Kanale in der Reihenfolge BGR. (255,0,0)
bedeutet dementsprechend “Blau”. Nun ist naturlich z.B. (230, 30, 20) immer noch
“ziemlich” blau. Und wenn wir beim tiefen Blau bleiben und nur die Helligkeit re-
duzieren landen wir z.B. bei (180, 0, 0), d.h. die Werte unterscheiden sich deutlich,
obwohl es sich in allen Fallen um “Blau” handelt.
Durch sogenannte Farbraumtransformationen kann man dieses Problem umgehen.
Der Farbraum RGB spannt ein Koordinatensystem auf, indem ein bestimmter RGB
Wert liegt. In diesem Fall entspricht der Rot-Wert eben der Projektion dieses Vek-
tors auf die R-Achse. Aber niemand hindert uns daran, ein anderes Koordinaten-
system zu verwenden. Dazu wahlt man eine ublicherweise lineare Transformation
der RGB Vektoren. OpenCV kann entsprechende Transformationen durchfuhren.
Fur die Analyse von Farbbildern ist meist das sogenannte HSI (Hue Saturation
Intensity) bzw. HSV (Hue Saturation Value) System geeignet, denn dort kann man
eben Farbe sehr einfach getrennt von Intensitat (und Sattigung) betrachten. Von
RGB (bzw. bei OpenCV BGR) nach HSI (oder auch zu Graustufen) konnen wir
mittels
hsv = cv2.cvtColor(bild, cv2.COLOR_BGR2HSV)
grau = cv2.cvtColor(bild, cv2.COLOR_BGR2GRAY)
transformieren. Ganz Entsprechend kann man auch zuruck wandeln und in Open-
CV sind naturlich noch viele weitere Transformationen (angegeben uber den letz-
ten Parameter) definiert.
17
2.4 Geometrische Transformation
Oft ist es als Zwischenschritt notwendig, Bilder korrekt auszurichten, positio-
nieren oder zu skalieren. Hierfur werden in der Mathematik sogenannte affine
Transformationen verwendet. In einem ersten Schritt wird dann zunachst die ideale
Transformation bestimmt und dann im zweiten Schritt die Transformation durch-
gefuhrt. Dabei muss fur jeden Bildpunkt des neuen Bildes der ideale Bildpunkt
im Eingangsbild gefunden werden. Da dieser ideale Punkt nicht exakt auf dem
Pixelraster des Eingangsbilds liegt ist meist eine wie auch immer geartete Interpo-
lation notwendig [3]. OpenCV bietet hier verschiedene Interpolationsmethoden; in
der Regel bietet die kubische Interpolation sehr gute Ergebnisse.
Fur einfache Skalierungen benutzen wir
hoehe, breite = bild.shape
ergebnis = cv2.resize(bild,(2*hoehe, 2*breite), \
interpolation = cv2.INTER_CUBIC)
Fur Translationen und Rotationen (um ein beliebiges Rotationszentrum) wird ei-
ne entsprechende Transformationsmatrix benotigt. OpenCV hilft erfreulicherweise
beim Berechnen der Matrix:
breite, hoehe = bild.shape
T = np.float32([[1,0,100],[0,1,50]]) # Verschiebung um (100,50)
verschoben = cv2.warpAffine(bild, T,(hoehe, breite))
R = cv2.getRotationMatrix2D((hoehe/2,breite/2),70,1)
rotiert = cv2.warpAffine(bild, R,(hoehe,breite))
Fur eine allgemeine affine Transformation (dabei bleiben parallele Linien auch nach
der Transformation parallel) bietet OpenCV eine Funktion (getAffineTransform)
mit der eine beliebige Transformationsmatrix basierend auf einer Zuordnung von
drei Punkten bestimmt wird.
Eine etwas andere Transformation wird fur den Ausgleich perspektivischer Verzeich-
nungen benotigt, denn Parallelen bleiben dabei naturlich gerade nicht parallel. Die
beiden entsprechenden Funktionen nennen sich dann warpPerpective und getPer-
spectiveTransform.
18
2.5 Ortsabhangige Transformation
Die bisher betrachteten lokalen Transformationen sind mathematisch trivial. Inter-
essanter (und zunachst konzeptionell nur wenig komplizierter) wird es, wenn wir
die Parameter einer Transformation nicht fur das gesamte Bild konstant wahlen
sondern ortsabhangig festlegen.
Zum Beispiel konnte es sein, dass immer im selben Bereich der Szene (z.B. unten
links) das Bild dunkler ist und daher dort eine andere Transformation wie im Rest
des Bildes angewendet werden soll.
Noch interessanter wird es aber, wenn die Transformation in Abhangigkeit vom
lokalen Bildinhalt selbst gesteuert wird. Zum Beispiel wurde man bei Binarisierun-
gen teilweise gerne eine Schwelle in Abhangigkeit vom mittleren Grauwert des
Bildes wahlen. “Mittel” bezieht sich dabei auf ein bestimmtes Gebiet um den ak-
tuell zu binarisierenden Pixel (Abb. 2.5.6).
(a) Eingangsbild (b) Binarisierung Otsu (c) Adaptive Schwelle
Abbildung 2.5.6
ergebnis = cv2.adaptiveThreshold(bild,255, \
cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 31, 0)
Wir verlassen damit also bereits das Gebiet der lokalen Punkttransformationen,
denn die Intensitaten in der Umgebung eines Pixels werden wichtig. In den fol-
genden Kapiteln werden letztlich nur noch Methoden besprochen, bei denen nicht
allein die lokale Pixelinformation genutzt wird. Hierzu starten wir mir der fur die
Bildverarbeitung wohl wichtigsten Klasse entsprechender Methoden, den linea-
ren Filtern.
19
Kapitel 3
Lineare Filter
Lineare Filter sind das Ruckgrat der Bildverarbeitung. Fast nichts geht zwar ohne
ein gewisses Maß an Nichtlinearitat, aber diese Nichtlinearitaten werden oft auf
Basis von eben linearen Filtern durchgefuhrt. So werden z.B. oft die Ausgangsbil-
der von mehreren linearen Filter bei der Kantendetektion (nichtlinear) kombiniert,
um ein Endergebnis zu erhalten. Lineare Filter konnen auch als Basis vieler haufig
eingesetzter nichtlinearer Filter, z.B. Medianfilter, angesehen werden.
Um die linearen Filter zu verstehen, mussen wir uns mit linearer Systemtheorie
beschaftigen. Dabei wird es notig sein, die Filter aus verschiedenen Perspektiven
zu betrachten. Fur uns essentiell sind dabei Faltung und Fouriertransformation.
3.1 Etwas Systemtheorie: Lineare invariante Systeme
Weil die lineare Systemtheorie so vielseitig und in so vielen verschiedenen Berei-
chen von Bedeutung ist, werden Studenten der Ingenieurs- oder Naturwissen-
schaften vermutlich auf die eine oder andere Weise bereits mit dem Gebiet in
Beruhrung gekommen sein, meist im Sinne einer Beschreibung von Zeitsignalen.
In der Bildverarbeitung werden wir raumliche Filter benotigen.
In Abb. 3.1.1 ist ein System abstrakt dargestellt. Das System liefert ein Ausgangs-
signal o wenn es mit einem Eingangssignal i beaufschlagt wird. “System” kann
dabei sowohl ein aus Hardware bestehendes System (z.B. ein Objektiv) oder auch
ein Algorithmus sein. In der Regel werden die Signale von bestimmten Variablen
(z.B. der Zeit, der Frequenz oder dem Ort) abhangen. Die Signale selbst konnen
ganz verschiedene Großen sein, z.B. Spannungen oder Intensitaten.
20
Abbildung 3.1.1: Black–Box Modell
Eine besondere Bedeutung haben eben lineare Systeme. Ein lineares System liefert
einfach die Summe einzelner Ausgangssignale, wenn es mit der Summe von Ein-
gangssignalen beaufschlagt wird:
wenn i1 → o1 (3.1)
und i2 → o2 (3.2)
dann i1 + i2 → o1 + o2 (3.3)
D.h. die Signal ij werden unabhangig voneinander durch das System geleitet.
Daraus ergibt sich (noch etwas allgemeiner ausgedruckt)
a1 i1 + a2 i2 → a1 o1 + a2 o2 (3.4)
mit den Konstanten a1 und a2.
Sehr viele praktische Systeme sind linear oder werden in erster Naherung durch
linearisierte Systeme ausreichend gut beschrieben. Nehmen Sie als Beispiel ein Te-
lefon: Wenn Sie gleichzeitig sprechen und Klavier spielen, dann wird am anderen
Ende der Leitung die Summe beider Signale”Klavier + Sprache” den Lautspre-
cher verlassen. Erst wenn das Gesamtsignal zu groß wird, ist die Linearitat nicht
mehr gegeben (”Ubersteuerung”).
In der Optik tritt die Linearitat in vielfaltiger Weise auf. Z.B. werden bei einer Ab-
bildung die einzelnen Objektpunkte unabhangig voneinander durch das Abbil-
dungssystem abgebildet. Man prasentiert also quasi am Eingang eine Summe ele-
mentarer Signale (z.B. einzelne Objektpunkte) und erhalt am Ausgang die Summe
aller entsprechender Bildpunkte. Grundsatzlich ergibt sich die Linearitat der Op-
tik aus den elementaren Grundgleichungen der Elektrodynamik1, den Maxwell-
Gleichungen. Da diese linear sind, sind auch die aus ihnen abgeleiteten Gleichun-
gen der Optik linear.2
1Die Optik ist klassisch eine Untermenge der Elektrodynamik.2Naturlich bedeutet das nicht, dass keine nichtlinearen Ausdrucke in Gleichungen auftreten. Linearitat meint hier
die Linearitat in der oben dargestellten Art fur Systeme.
21
(a) Pulsubertragung durch elektrisches System
(b) optische Abbildung
Abbildung 3.1.2: Lineare, verschiebungsinvariante Systeme
In vielen Fallen hat man es daruber hinaus mit sogenannten invarianten Systemen
zu tun. Das Telefon kann wieder gut als anschauliches Beispiel fur ein zeitinvari-
antes System dienen: Zwei exakt gleich klingende Worte, im Abstand einer Sekun-
de gesprochen, werden am Ausgang ebenfalls den Abstand 1 Sekunde aufweisen
und sich hinsichtlich des Klangs (bzw. der Signalform) nicht unterscheiden. All-
gemeiner ausgedruckt: Das System liefert ein Ergebnis, das invariant hinsichtlich
einer (zeitlichen) Verschiebung ist. Beachten Sie: Die Invarianz bezieht sich nicht
darauf, dass eine Sekunde Unterschied am Eingang auch eine Sekunde am Aus-
gang bewirkt. Vielmehr ist entscheidend, dass das Signal zu den beiden unter-
schiedlichen Zeiten gleich bleibt.
Bei der optischen Abbildung haben wir entsprechend eine (naherungsweise) In-
varianz gegenuber raumlichen Verschiebungen.3 Der Abstand zwischen den Bild-
punkten kann durchaus vom Abstand zwischen den Objektpunkten differieren4.
Die Form der Bildpunkte (ausgedruckt z.B. durch die Punktbildverwaschungs-
funktion) ist aber naherungsweise konstant und gleiche Abstande im Objektraum
werden in gleiche Abstande im Bildraum transformiert (s. Abb. 3.1.5).
3Das Gebiet in dem diese naherungsweise Konstanz gilt wird ublicherweise”isoplanatische Region” genannt.
4Nur bei einer 1:1 Abbildung sind die Abstande im Bildraum exakt gleich den Abstanden im Objektraum.
22
Wir wollen zunachst etwas naher auf die Folgen eingehen, die sich ganz allgemein
fur die Beschreibung eines linearen–invarianten Systems ergeben.
Hierzu zerlegen wir eine beliebige Eingangsfunktion, die auf unser System trifft
in unendlich viele unendlich kurze ideale Peaks bzw. ideale punktformige Pixel,
d.h. sogenannte δ–Funktionen5. Die δ–Funktion ist dabei einerseits unendlich kurz
ausgedehnt, andererseits aber auch unendlich hoch so dass die Flache unter der
δ–Funktion letztlich 1 ergibt, also∫∞−∞ δ(x)dx = 1 (vgl. Abb. 3.1.3). Wie dem auch
sei: Anschaulich zerlegen wir unser Objekt in unendlich viele Einzelpunkte, die
wir getrennt abbilden. Weil wir ein lineares System annehmen, konnen wir dann
am Systemausgang die Summe der Systemantworten auf die einzelnen Punkte
bilden.
(a) Delta-Funktion als Grenzwert von Gauß (b) Zerlegung in Delta-Funktionen
Abbildung 3.1.3: Veranschaulichung der Delta-Funktion als Grenzwert von Gaußfunktionen mit
abnehmender Breite (aber konstanter Flache): Der Wert der Deltafunktion ist an der Stelle x=0
unendlich und an allen anderen Stellen (x 6= 0) Null. Die Multiplikation einer kontinuierlichen
Funktion (hellblau) mit einem Deltakamm (rot) fuhrt zu einer Diskretisierung nach Gleichung 3.5
Naturlich sind die δ–Funktionen bei unserer Basiszerlegung gegeneinander ver-
schoben. Fur Objekte (bzw. Bilder) ist dies besonders anschaulich. In diesem Fall
kann man sich die einzelnen δ–Funktionen ja einfach als ideale Punkte in der Ob-
jektebene bzw. (unendlich feine) Pixel vorstellen6. Wir zerlegen also gemaß Gl.
5Mathematisch ist diese Sprechweise extrem lax. Es musste eigentlich von δ–Distributionen gesprochen werden.
Wir werden hier aber — unserer allgemeinen Fahrlassigkeit folgend — auf eine genauere (mathematisch korrekte)
Beschreibung mit Grenzwerten zu Gunsten der Anschaulichkeit und Einfachheit verzichten.6Fur Zeitsignale ist δ(t) entsprechend ein Zeitpunkt.
23
3.16 bzw. Abb. 3.1.3
i(x) = a0 δ(0) + a1 δ(x− 1) + a2 δ(x− 2) + ... =∑
j
aj δ(x− j δxs). (3.5)
Die aj sind bei einem solchen Objekt einfach die Helligkeiten der entsprechenden
Objektpunkte s an den Stellen δxs · j.
Abbildung 3.1.4: Black–Box Modell
Wenn wir die Antwort auf einen elementaren δ-Impuls an der Stelle 0 mit h(x) be-
zeichnen, also δ(0) → h(x), und fordern, dass eine Verschiebung am Eingang auch
nur zu einer Verschiebung am Ausgang fuhrt (invariantes System) dann ergibt
sich fur das lineare System am Ausgang (also z.B. im Bild)
o(x) = a0 h(x) + a1 h(x− 1) + a2 h(x− 2) + ... =∑
j
aj h(x− j) (3.6)
Hierbei ist das Sampling am Eingang und Ausgang jeweils “1”. (Naturlich konnten
wir hier auch beliebig andere Abtastungen verwenden.)
Fur den kontinuierlichen Fall mit unendlich vielen Objekt– und Bildpunkten wird
die Summe naturlich durch ein Integral ersetzt und die Gleichungen 3.4 und 3.6
werden zu
i(x) =
∫ ∞
−∞i(x′) δ(x− x′)dx′ (3.7)
o(x) =
∫ ∞
−∞i(x′) h(x− x′)dx′, (3.8)
dabei haben wir die bisher diskreten Koeffizienten a durch die “kontinuierlichen
Koeffizienten” (sprich: eine Funktion) i ersetzt.
Beide Gleichungen sind sogenannte Faltungsintegrale. Sie sind so gebrauchlich,
dass es sich lohnt, eine eigene Schreibweise fur die Faltung einzufuhren:
24
(a) Invarianz gegenuber raumlicher Verschiebung
(b) Varianz gegenuber raumlicher Verschiebung
Abbildung 3.1.5: Invarianz der Antwort bei raumlicher Verschiebung (Verschiebungsinvarianz)
f(x) ∗ g(x) :=∫ ∞
−∞f(x′)g(x− x′)dx′. (3.9)
Gleichung 3.8 wird damit zu dem wichtigen Ergebnis
o(x) = i(x) ∗ h(x), (3.10)
d.h. das Ausgangssignal eines invarianten linearen Systems bzw. Filters ergibt
sich durch Faltung des Eingangssignals mit der Systemantwort h(x) auf einen δ–
Impuls. Als Merkregel:”Jedes (lineare,zeitinvariante) Filter ist ein Falter.”
D.h. all unsere Systemkomponenten und Algorithmen in der Bildverarbeitung
werden — solange sie naherungsweise als linear verschiebungsinvariant betrach-
tet werden konnen — durch Faltungen beschrieben. Naturlich sind nicht alle
Algorithmen der Bildverarbeitung linear. Aber die linearen Algorithmen bilden,
25
wenn man so will, praktisch den Kern der Theorie und wir werden sehen, dass
viele nichtlineare Algorithmen (z.B. der Kantendetektion) wie schon gesagt letztlich
wieder auf der nichtlinearen Kombination von linearen Filtern basieren.
Wenn wir also das Verhalten h(x) am Ausgang des (linearen invarianten) Systems
fur einen unendlich kurzen Eingangsimpuls kennen, sind wir in der Lage, die
Antwort des Systems auf ein beliebiges Eingangssignal i zu berechnen. Wir wissen
also alles uber das System, wenn wir seine Antwort auf einen δ–Impuls kennen.
Abbildung 3.1.6: Das Verhalten des LIS auf einen unendlich kurzen Eingangspuls ist ausreichend
zur vollstandigen Beschreibung der Systemantwort auf ein beliebiges Eingangssignal. Man denkt
sich das Eingangssignal als aus einer Summe gewichteter Delta-Funktionen bestehend. Am Aus-
gang resultiert entsprechend eine Summe (Linearitat !) von gewichteten Punktbildfunktionen. Be-
achten Sie: Zur Visualisierung wurde hier als Eingangssignal ein diskretes Signal gewahlt. Im Nor-
mallfall sind Ein- und Ausgangssignal aber kontinuierlich.
Naturlich ist bei diskreten Bildern, und Bilder bestehen fur uns sobald sie im Com-
puter sind aus diskreten Punkten, die kontinuierliche Schreibweise aus Gl. 3.8 mit
dem Integral in eine diskrete Summenschreibweise zu ersetzen. Aus Gl. 3.8 wird
damit
h(x) ∗ f(x) :=k=N∑
k=−N
h(k)f(x− k) (3.11)
=
k=−N∑
k=N
h(−k)f(x+ k) (3.12)
26
mit diskretem x. Im Zweidimensionalen ganz entsprechend
h(x, y) ∗ f(x, y) :=∑
k
∑
l
h(k, l)f(x− k, y − l) (3.13)
Wir wollen zunachst die eindimensionale Berechnungvorschrift anschaulich dar-
stellen. Dazu gehen wir von einer raumlichen begrenzten Kernelfunktion h(x) aus.
An einer Position x errechnet sich nach Gl. 3.1.7 das Ergebnis der Faltung wie in
Abb. 3.1.7 dargestellt: Der (gespiegelte, weil Argument negativ) Faltungskernel h
wird an die relevante Position x geschoben (anders ausgedruckt: h(-k) wird um x
verschoben).7 Dann werden an jeder Position die ubereinander liegenden Grau-
werte von Kernel und Bild multipliziert. Alle Multiplikationsergebnisse werden
dann addiert um den Ergebniswert an der Stelle x zu erhalten. Um den Wert an
der nachsten Position (also x+1) zu erhalten wird einfach der Kernel einen Pixel
weiter verschoben und der Vorgang wiederholt. Die Faltung ist also eine kostspie-
lige Operation
Abbildung 3.1.7: Berechnung der (eindimensionalen) diskreten Faltung. Der (gespiegelte) Faltungs-
kernel wird uber die Orginalfunktion geschoben. Zur Berechnung jedes einzelnen Funktionswerts
mussen viele Multiplikationen und Additionen (hier jeweils 5) durchgefuhrt werden.
Umso mehr im zweidimensionalen Fall, der in Abb. 3.1.8 veranschaulicht ist. Wie-
der wird der Kernel uber das Bild geschoben. An jeder moglichen Position wird
wieder fur jeden Pixel der entsprechende Kernelwert mit dem Bildwert multi-
pliziert und wieder wird die Summe aller Multiplikationsergebnisse berechnet.
Wenn der Kernel N x N Pixel und das Bild M x M Pixel groß sind, dann werden
also insgesamt N x N x M x M Multiply-Add (MADD) Operationen zur Berech-
nung des Filterergebnisses notwendig. Fur ein 1 MPixel Bild und einen 11 x 11
Kernel landen wir hier bereits bei 121 Millionen Operationen.
7Vielleicht ist es fur Sie auch einfacher, sich vorzustellen, dass das Bild f unter dem Kernel durchgeschoben wird
(das passt eher zum f(x− k)).
27
Abbildung 3.1.8: Berechnung der (zweidimensionalen) diskreten Faltung. Der (gespiegelte) Fal-
tungskernel wird uber die Orginalfunktion geschoben. Zur Berechnung jedes einzelnen Funkti-
onswerts mussen viele Multiplikationen und Additionen (hier jeweils 9) durchgefuhrt werden.
Erfreulicherweise kann man oft eine zweidimensionale Faltung als das Produkt
(bzw. die Hintereinanderschaltung) einer Faltung in x-Richtung und in y-Richtung
schreiben. In diesem Fall ist der Kernel separierbar. Diese zwei eindimensionalen
Faltungen sind deutlich schneller als eine zweidimensionale Faltung.
Das, was wir hier gerade beschrieben haben, lauft ab, wenn Sie in einem Bildbear-
beitungsprogramm (Gimp, Photoshop etc.) z.B. einen Gaußfilter anwenden.
Die Frage ist noch, was man eigentlich am Rand des Bildes macht. Denn um das
Faltungsergebnis fur z.B. den ersten Pixel einer Zeile zu berechnen musste man ja
auch die Grauwerte links von diesem Pixel vorliegen haben. Es gibt verschiedene
Varianten, mit diesem Problem umzugehen. Im einfachsten Fall nimmt man die
Grauwerte dieser Pixel zu Null an. Oder man nimmt an, dass sie denselben Wert
haben wie Pixel 1.
Sobald man die Faltung durch Multiplikation im Fourierraum berechnet (und die
diskrete Fouriertransformation verwendet, dazu spater mehr) geht man automa-
tisch davon aus, dass sich das Bild periodisch wiederholt. In der Regel eine recht
unwahrscheinliche Variante. Daher wird dann das Bild vor der Fouriertransfor-
28
mation meist mit einer den Rand abdunkelnden Maske multipliziert (sogenannte
Fensterung).
Welche Variante man auch wahlt: In jedem Fall muss man sich bewusst sein, dass
die Pixel nahe des Randes in irgendeiner Form problematisch gefiltert werden weil
schlicht die korrekte Nachbarschaftsinformation durch die Beschrankung des Bil-
des einfach nicht vorliegt und daher eben auch nicht einberechnet werden kann.
3.2 Fourierformalismus
Betrachten Sie die eindimensionale Funktion f(x) = sin(x2). Es mag Ihnen ganz
naturlich erscheinen, dass dieses f(x) die Funktion selbst ist. Um die Funktion
beispielsweise an der Stelle x = 2, 5 zu berechnen setzen wir einfach den Wert
x = 2, 5 in die Funktion ein und erhalten sofort f(x = 2, 5) = sin(2, 52) ≈ −0, 03318.
Soweit so gut.
Sie konnen zu demselben Ergebnis aber auch auf wesentlich kompliziertere Weise
gelangen. Zum Beispiel konnten Sie f(x) mittels
sin(x2) ≈ x2 − 1
6x6 +
1
120x10 − .... (3.14)
bestimmen.
Warum sollten wir diesen Umweg gehen? Eine mogliche Antwort ist sicher, dass
wir damit eine Berechnung komplizierter Funktionen (hier: Sinus) vermeiden.8
Das ist aber bei weitem noch nicht alles.
Man kann hier streiten, was die Funktion “ist”. Letztlich wird der Sinus meist
geometrisch definiert, aber das muss nicht so sein. Man kann ihn z.B. eben auch
uber eine Reihenentwicklung definieren.
Mathematisch betrachtet haben wir die zu berechnende Funktion mit Gl. 3.14
durch eine Summe aus gewichteten Elementarfunktionen (bzw. Basisfunktionen)
8Und in der Tat rechnet Ihr Taschenrechner letztlich intern uber eine entsprechende Reihendarstellung die Funktion
aus.
29
gj(x) beschrieben, d.h.
f(x) =
j=N∑
j=0
ajgj(x) (3.15)
=
j=N∑
j=0
ajxj (3.16)
N muss dabei theoretisch Unendlich sein, um eine exakte Losung zu erzielen. In
der Praxis gibt man sich naturlich mit Naherungen zufrieden und bricht die Sum-
mation an geeigneter Stelle ab. Als Basisfunktionen haben wir hier gj(x) = xj
gewahlt, denn diese Funktionen lassen sich sehr einfach berechnen (durch Addi-
tion und Multiplikation).
Wir konnen aber auch ganz andere Basisfunktionen verwenden. So haben wir im
letzten Abschnitt z.B. verschobene Delta-Funktionen gewahlt. Auch dort ist das
Orginalsignal die gewichtete Summe vieler Delta-Impulse.
Grundsatzlich sind viele solcher Systeme von Basisfunktionen moglich. Nur we-
nige sind allerdings wirklich praxisrelevant und ergeben einen direkten Nutzen.
Das System mit der großten Bedeutung ist das System ebener Wellen (bzw. harmo-
nischer Schwingungen), das uns zur Fouriertransformation fuhrt9
gu(x) = e2πiux = cos(2πux) + i sin(2πux) (3.17)
Eine Funktion wird in diesem System eben durch die Summe
f(x) =∞∑
u=−∞au e
2πiux (3.18)
dargestellt.
Ein Beispiel zeigt Abb. 3.2.9 bei der die Zerlegung einer periodischen Rechteck-
schwingung in Sinusschwingungen (der Imaginarteil von Gleichung 3.18) darge-
stellt ist. Bereits die Uberlagerung von 3 Sinusschwingungen nahert das Signal
einigermaßen brauchbar an.10
9Die verwendeten Konstanten in Gl. 3.17 wurden hier mehr oder weniger willkurlich gewahlt.10Es zeigt sich, dass eine reine Uberlagerung von reellen Sinusschwingungen nur einen Teil aller in der Praxis auftre-
tenden Funktionen beschreiben kann. Um wirklich alle (mathematisch gutartigen) Funktionen darstellen zu konnen
benotigt man zusatzlich phasengeschobene Sinusfunktionen (bzw. den Kosinus) und eine imaginare Gewichtung von
einigen Funktionen. Oder aber man geht direkt zu der Zerlegung mit komplexen Exponentialfunktionen (Gl.3.18)
uber.
30
(a) 3 Komponenten (b) 7 Komponenten
Abbildung 3.2.9: Durch Addition von vielen Sinus- und Kosinusschwingungen passender Ampli-
tude und Phase kann man nahezu jede Funktion nahern.
Die besondere Bedeutung der harmonischen Basisfunktionen liegt in ihrer Eigen-
schaft, Eigenfunktionen von linearen Systemen zu sein. D.h. beim Durchgang durch
das lineare System behalt das Sinussignal seine Form. Es kann zwar phasenge-
schoben werden und die Amplitude des Sinus darf sich andern, aber es bleibt ein
Sinus und auch die Frequenz des Sinus andert sich nicht (siehe Abb. 3.2.10).
Das ist mit Sicherheit eine sehr außergewohnliche Eigenschaft, denn im allgemei-
nen andert sich eine Signalform beim Durchgang durch ein lineares System ja ganz
erheblich. Z.B. wird ja aus einem ideal abgebildeten Punkt bei der optischen Abbil-
dung nicht wieder ein idealer Punkt. Und weil die Antwort des Systems auf die-
se speziellen harmonischen Basisfunktionen so einfach ist, macht es Sinn, genau
diese Basisfunktionen als Grundkomponenten zu verwenden, in die wir unsere
Eingangssignale zerlegen.
Letztlich ist das der Grund fur die Dominanz der Fouriertransformation in vielen
Bereichen der Wissenschaft. Die grundlegenden Systeme sind linear und verschie-
bungsinvariant und es zeigt sich, dass Sinus und Kosinus eben Eigenfunktionen
solcher Systeme sind.
Mathematisch gilt also fur die Eigenfunktion i(x):
o(x) = h(x) ∗ i(x) != k · i(x) (3.19)
=
∫ ∞
−∞i(x′)h(x− x′)dx′ (3.20)
Die Grundidee zur Berechnung der Antwort des Systems auf ein beliebiges Ein-
gangssignal ist somit folgende: Wir zerlegen das Eingangssignal in die Basisfunk-
31
Abbildung 3.2.10: Sinus– und Kosinusschwingungen sind Eigenfunktionen von linear invarianten
Systemen und werden daher beim Durchgang durch das System nicht verzerrt sondern lediglich
abgeschwacht/verstarkt und eventuell phasengeschoben.
tionen gu = exp(2πux), berechnen dann die Antwort des linearen Systems auf die
einzelnen Basisfunktionen (lediglich Multiplikationen mit einem konstanten Fak-
tor a(u) = Au · exp(iφu)), und summieren schließlich diese Antworten.
Dadurch sparen wir uns die oft muhsame Berechnung der Faltung des Eingangs-
signals mit der Punktantwort.
Diese Vorgehensweise kann zum einen rein mathematisch vorteilhaft sein, aber
teilweise konnen auch die Systemantworten auf die verschiedenen Basisfunktio-
nen direkt messtechnisch erfasst werden.
Ein wichtiges Beispiel ist die Modulationstransferfunktion zur Beschreibung der Ab-
bildungsleistung von Objektiven. Letztlich werden dem System, z.B. einem Ob-
jektiv, dabei die Eigenfunktionen (also sinusformige Gitter) angeboten. Das Bild
ist (Eigenfunktion) wieder ein sinusformiges Gitter, aber der Kontrast des Gitters
nimmt mit zunehmender Gitterfrequenz ab.
Wir konnen nun Gl. 3.18 fur ein kontinuierliches Signal f(x) verallgemeinern. Die
Summe wird dabei zu einem Integral:
f(x) =
∫ ∞
−∞a(u) e2πiuxdu (3.21)
Mathematisch betrachtet haben die verwendeten Variablen u und x noch
uberhaupt keine Bedeutung. x steht zwar normalerweise bei uns in der Bildverar-
beitung fur den Ort, aber letztlich konnen wir hier beliebige physikalische Großen
verwenden, z.B. die Zeit, wie es beispielsweise bei der zeitlichen Veranderung ei-
ner elektrischen Spannung notwendig ware.
In Gl. 3.21 haben wir zunachst ein uns unbekanntes a(u) angesetzt. Die Frage ist
naturlich, wie wir dieses a(u) konkret wahlen mussen, um ein ganz spezielles f(x)
32
nach Gl. 3.21 zu erzielen. Das geschieht eben durch die Fouriertransformation
a(u) =
∫ ∞
−∞f(x) e−2πiuxdx (3.22)
Gleichung 3.22 ist die Fouriertransformation fur raumliche Signale11. Gleichung
3.21 ist entsprechend die inverse Fouriertransformation. Wie man durch Einsetzen
verifiziert gilt FT−1[FT[f(x)]] = f(x).
Teilweise werden unterschiedliche Normierungskonstanten bei der (inversen)
Fouriertransformation verwendet. Wir verwenden hier die in der Optik ubliche
Nomenklatur nach Goodman [5]. Letztlich ergeben sich bei anderer Wahl der Nor-
mierung aber keine relevanten Anderungen der Mathematik.
Wir haben damit formal die Fouriertransformation und ihre Umkehrung im Sinne
einer (motivierten) Definition eingefuhrt. Die Basiszerlegung unseres Signals ist
also eine inverse Fouriertransformation und die Koeffizienten unserer Zerlegung
ergeben sich gerade durch die Fouriertransformation.
3.3 Die Fouriertransformation von Bildern
In der Bildverarbeitung interessieren wir uns meist fur zweidimensionale
raumliche Fourierzerlegungen. Das Fourierpaar der Zerlegung in Basisfunktionen
(inverse Fouriertransformation) und Bestimmung der Zerlegungskoeffizienten F
(Fouriertransformation) wird also geschrieben als
IFT : f(x, y) =
∫ ∞
−∞F (u, v) e2πi(ux+vy)du dv (3.23)
FT : F(u, v) =
∫ ∞
−∞f(x, y) e−2πi(ux+vy)dx dy (3.24)
offensichtlich sind beide Formeln nahezu symmetrisch zueinander (Vorzeichen-
wechsel des Exponenten).
Wie in den Gleichungen 3.23 und 3.24 verwendet man ublicherweise Klein- und
Großbuchstaben, um ein zugehoriges Fourierpaar zweier Funktionen zu kenn-
zeichnen. D.h. wir bezeichnen z.B. mit A(u) die Fouriertransformierte von a(x).
11Ganz entsprechend kommt man zu zeitlichen Signalen, indem man das x durch t sowie das u durch f ersetzt.
33
Die Basisfunktion eiux der Zerlegung ist eine periodische Funktion. Bei Zeitsigna-
len kann sie als zeitliche”Schwingung” verstanden werden. Bei Bildern handelt
es sich um eine periodische Variation der Lichtamplitude in der Raumkoordina-
te. Das klingt recht abstrakt, ist aber eigentlich sehr simpel: Wir sprechen von ei-
nem periodischen Gitter. Sowohl der Realteil, als auch der Imaginarteil von eiux
ist ein einfaches Kosinus- bzw. Sinusgitter. In Analogie zur gewohnlichen (Zeit-
)Frequenz nennt man u und v die Raumfrequenzen (in x- und in y-Richtung, Abb.
3.3.12).12
Die Fouriertransformation ist im ubrigen kein reines mathematisches Konstrukt
sondern tritt auch bei der Abbildung auf. Am direktesten sieht man sie dabei
in Abbildungssystemen mit zwei Objektiven, z.B. dem Mikroskop oder doppelt-
telezentrischen Bildverarbeitungsobjektiven (siehe Abb. 3.3.11): Die zweidimen-
sionale Fouriertransformierte des Objekts befindet sich in der Zwischenfokusebe-
ne.
Abbildung 3.3.11: Ein 4-f System fuhrt nacheinander zwei Fouriertransformation durch und erzielt
so letztlich eine (invertierte) Abbildung. Wenn als Objekt ein Sinusgitter verwendet wird, dann
ergibt die Fouriertransformation zwei Delta-Peaks, die in der Fourierebene als isolierte Peaks be-
obachtet werden.
Wenn dort nun ein Filter angebracht wird, dann werden Teile des Spektrums (also
der Fouriertransformierten) auf Null gesetzt und dementsprechend wird das Bild
beeinflusst.13
Wenn wir also dort z.B. eine Kreisblende einsetzen, dann erhalten wir einen Tief-
passfilter, denn hohe Raumfrequenzen liegen weit außerhalb der optischen Achse
und konnen die Blende nicht passieren. Dementsprechend ergibt sich eine Ver-
schmierung im Bild. Feine Details im Objekt fuhren zur Beugung in große Win-
12Oft verwendet man in der Optik auch den Wellenzahlvektor ~k, der einfach das 2π-fache des Raumfrequenzvektors
ist: ~k = 2π(u, v, w)13Bei inkoharenter Bildgebung sind die Zusammenhange ein klein wenig komplizierter.
34
kel und damit Licht weitab der optischen Achse. Diese Lichtanteile werden vom
(Tiefpass-)Filter geblockt.
Wenn als Objekt wieder ein idealer Punkt verwendet wird, dann ergibt sich am
Ausgang des Systems ein (durch die Beugung an der Blende) verschmierter Punkt,
die Antwort h(x,y) auf den Eingangs-Deltaimpuls.
Je enger wir die Blende stellen, desto ausgedehnter die Verschmierung, d.h. desto
großer die Ausdehnung des Punktbilds h(x,y).
Ganz entsprechend ergibt sich ein Hochpassfilter, wenn die tiefen Frequenzen
geblockt werden. Hochpassfilter lassen also hohe Frequenzen passieren und
verstarken somit effektiv alle Bildanteile, die hohe Frequenzanteile beinhalten
(insbesondere Kanten).
(a) Raumfrequenz (b) Fouriertransformierte
Abbildung 3.3.12: Eine konkrete Raumfrequenz (u,v) beschreibt ein Gitter der Periode d. Die “Fre-
quenz” des Gitters ist das Inverse der Gitterperiode. Ein Gitter in x-Richtung mit der Periode d
entspricht also der Raumfrequenz u=1/d. Die Fouriertransformierte einer Sinusschwingung er-
gibt zwei Peaks.
Die zunachst relativ abstrakt motivierten Begriffe “Punktbild” und Fouriertrans-
formation konnen so sehr anschaulich im Rahmen der optischen Abbildungstheo-
rie genutzt werden.
Kommen wir nun zuruck zu unserer ursprunglichen Aufgabe, den Ausgang des
LIS zu berechnen. Wir denken uns also das Eingangssignal i(x) dargestellt durch
seine Fouriertransformierte I(u).
i(x) :=
∫ ∞
−∞I(u)e2πiuxdu (3.25)
Jede Komponente e2πiux wird um einen komplexen Faktor H(u) durch das System
35
modifiziert und daher konnen wir schreiben (Linearitat):
o(x) =
∫ ∞
−∞H(u)I(u)e2πiuxdu (3.26)
o(x) kann aber naturlich genauso wie i(x) zerlegt werden:
o(x) :=
∫ ∞
−∞O(u)e2πiuxdu (3.27)
Durch Gleichsetzen von Gl. 3.26 und 3.27 ergibt sich dann direkt
O(u) = H(u)I(u) (3.28)
Dies ist ein bedeutsames Resultat, der sogenannte Faltungssatz. Es sagt uns, dass
wir den Ausgang des LIS nicht nur durch die recht komplizierte Faltung (im-
merhin eine Integration) sondern auch durch eine einfache Multiplikation be-
schreiben konnen. Dies ist aber eben nur dann moglich, wenn wir uns im (Orts–
)Frequenzraum befinden. Der Faltungssatz gilt ganz generell fur beliebige Funk-
tionen, unabhangig von der systemtheoretischen Betrachtung:
FT[g ∗ h] = FT[g] · FT[h] (3.29)
Abb. 3.3.13 zeigt nochmals zusammengefasst die beiden grundsatzlichen
Moglichkeiten zur Berechnung der Antwort eines LIS.
Abbildung 3.3.13: Im wesentlichen haben wir zwei Moglichkeiten, das Ausgangssignal eines li-
nearen invarianten Systems zu berechnen. Der direkte Weg bleibt im Orts– bzw. Zeitraum und
berechnet die Faltung mit der Punktbildfunktion. Der indirekte (aber teilweise wesentlich einfa-
chere und anschaulichere) Weg berechnet die Faltung durch eine Multiplikation im Orts– bzw.
Zeitfrequenzraum (Faltungssatz).
Und beide Varianten werden auch wirklich in der Praxis angewendet. Solange der
Filterkernel h eine kleine Ausdehnung z.B. 3x3 oder auch 7x7 hat, ist in der Regel
36
die Berechnung der Filterantwort direkt im Ortsraum durch den Faltungsansatz
schneller als die Berechnung uber die Fouriertransformation.
Fur die Berechnung der Fouriertransformation mittels dem Algorithmus zur
schnellen Fouriertransformation (FFT) benotigt man (unabhangig von der Ker-
nelgroße) 2 N2 ld N Operationen. Die Berechnung im Fourierraum benotigt also
insgesamt in der Großenordnung (4 ld N + 1) N2 Operationen wahrend die direkte
Berechnung per Faltung mit dem Kernel der Große MxM eben M2 N2 Operationen
benotigt.
3.4 Eine typische Anwendung zur Fourierfilterung
Periodische Strukturen oder Anteile im Bild fuhren — unabhangig von ihrer Posi-
tion — zu Peaks in der Fourierebene. Dies kann zum Finden von Strukturen, aber
auch zur Analyse oder Eliminierung von Storungen genutzt werden. Wir werden
das an einem besonders drastischen Fall, der Eliminierung von Moires oder auch
dem Ruckgangigmachen von Halftoning bzw. Dithering14 demonstrieren. Abb.
3.4.14 (a) zeigt ein entsprechendes Bild sowie die zugehorige Fouriertransformati-
on.
Das Halftoning-Raster ist klar als Peaks in der Fourierebene zu identifizieren.
Wenn wir nun diese Peaks eliminieren (Abb. 3.4.14 (c)), dann eliminieren wir da-
mit auch das Halftoning (bei Moirestorungen entsprechend die Moires).
Sowohl numpy wie auch OpenCV bieten Varianten zur Fouriertransformation
an. Wir verwenden im folgenden die (etwas einfachere) numpy-Variante. Der zu-
gehorige Code sieht folgendermaßen aus:
def schwarzerKreis(img, radius, y, x):
return cv2.circle(img,(y,x), radius, 0, -1)
def imageFFT(img,shifted=1):
f = np.fft.fft2(img)
if(shifted):
f = np.fft.fftshift(f)
return f
14Grauwerte werden bei binaren Bildern durch fein aufgeloste Punkte realisiert.
37
(a) Eingangsbild (b) Fouriertransformation (FT)
(c) modifizierte FT (d) Rucktransformation
Abbildung 3.4.14: Einsatz der Fourierfilterung zur Moirereduktion bzw. Ruckgangigmachen von
Halftoning
def imageIFFT(img,shifted=1):
f = np.fft.ifft2(img)
if(shifted):
f = np.fft.fftshift(f)
return f
fft = np.abs(imageFFT(bild))
fft = imageFFT(bild)
phase = np.angle(fft)
amplitude = np.abs(fft)
38
radius = 9
amplitude = schwarzerKreis(amplitude, radius, 184, 366)
amplitude = schwarzerKreis(amplitude, radius, 366, 184)
amplitude = schwarzerKreis(amplitude, radius, 372, 362)
amplitude = schwarzerKreis(amplitude, radius, 184, 175)
fftneu = amplitude * np.exp(1j*phase)
erg = np.abs(imageIFFT(fftneu,shifted=0))
erg = cv2.GaussianBlur(erg,(5,5),0)
Naturlich kann man Fouriertransformationen auch auf Bereiche eines Bildes an-
wenden, um dann eben lokal den Frequenzinhalt zu beurteilen.
3.5 Einfache Verkettung linearer Filter
Nehmen wir an, wir schalten zwei lineare Filter F1 und F2 hintereinander. Offen-
sichtlich gilt doch
o1 = h1 ∗ i (3.30)
o2 = h2 ∗ oi = h1 ∗ h2 ∗ i (3.31)
= h3 ∗ i (3.32)
mit h3 := h1 ∗ h2. Das ist ein wichtiges Ergebnis, denn es besagt, dass wir die Hin-
tereinanderschaltung der Filter durch einen neuen linearen Filter ersetzen konnen;
eine durchaus gebrauchlich Variante, um z.B. einen Kantenfilter auf das Ergebnis
eines Mittelungsfilters anzuwenden.
Im Fourierraum wird aus der Faltung wieder eine Multiplikation, so dass die Sy-
stemfunktion eben das Produkt der Systemfunktionen der beiden Teilfilter ist.
Da fur die Faltung ganz generell a * b = b * a gilt, ist auch die Reihenfolge der Filter
ohne Belang.
39
3.6 Tiefpassfilter — Mittelung
Tiefpassfilter begrenzen hohe Raumfrequenzen bzw. lassen tiefe Frequenzen pas-
sieren. Sie werden sehr haufig zur Reduktion von Rauschen eingesetzt.
Wie bereits gesagt sind unerwartete und ungewollte Signalanderungen ganz ge-
nerell ein Problem fur robuste Bildverarbeitung und sollten soweit irgend moglich
vermieden werden. Prinzipiell zufallige Storungen werden meist als Rauschen be-
zeichnet und sind eine Form solch einer Anderung (Fremdlicht ware eine weite-
re). Letztlich versucht man naturlich zunachst, Rauschen soweit moglich an sich
zu vermeiden (genugend Licht, rauscharme Bildsensoren etc.). Dennoch lasst sich
das Rauschen fast nie komplett auf Null reduzieren.
Eine weitere Verringerung des Rauschen kann man dann durch eine wie auch
immer geartete Mittelung erreichen. Entweder man mittelt zeitlich (lange Belich-
tungszeit, mehrere Bilder) oder aber raumlich. Die zeitliche Mittelung verringert
naturlich die zeitliche Auflosung und die raumliche Mittelung die raumliche (la-
terale) Auflosung.
Im einfachsten Fall verwendet man als Kernel zur raumlichen Mittelung ein
Rechteck als Punktbild (sogenannter Boxfilter), d.h. man mittelt alle Nachbar-
schaftspixel einer gewissen Große zu einem Ausgabewert. Der entsprechende Ker-
nel ist also:
h =1
9
1 1 1
1 1 1
1 1 1
(3.33)
Je großer, der Kernel, desto starker der Mittelungseffekt. Im Frequenzraum (Fou-
riertransformation) ergibt sich ein zweidimensionales sinc-Muster (Abb. 3.6.16).
Das Ergebnis zeigt, dass das Filterergebnis von der Orientierung einer Kante
abhangt. Z.B. fuhrt eine Kante in horizontaler oder vertikaler Richtung zu einer
Fouriertransformierten in vertikaler oder horizontaler Richtung. Entsprechend
wird sie starker verschmiert als eine Kante in diagonaler Richtung. Der Filter ist
nicht isotrop. Das ist in der Regel nicht wunschenswert. Außerdem werden hohe
Frequenzen teilweise weniger stark gedampft als etwas tiefere; auch das ein meist
unerwunschtes Verhalten. Gang generell gilt, dass steile Kanten in einem Bereich,
zu Schwingungen (ringing) im jeweils anderen Bereich fuhren. Und so ist es auch
40
z.B. meist wenig sinnvoll, einen harten Rechteckfilter im Frequenzraum einzuset-
zen, denn dieser wurde an Kanten im Ergebnisbild zu entsprechendem Ringing
fuhren.
(a) Rect-Filter (b) Gaußfilter
Abbildung 3.6.15: Fouriertransformation von verschiedenen Mittelungsfiltern
In aller Regel storen die Nachteile des Rechteckfilters wenig und daher wird er —
auch weil er extrem effizient implementiert werden kann — sehr oft eingesetzt.
Ein etwas schoneres Ergebnis liefert allerdings der Gaußfilter. Hierbei wird der
Kernel eben gemaß einer Gaußfunktion
h(x, y) = ex2+y
2
2σ2 =1
16
1 2 1
2 4 2
1 2 1
bzw.
1
273
1 4 7 4 1
4 16 26 16 4
7 26 41 26 7
4 16 26 16 4
1 4 7 4 1
(3.34)
gewahlt. Bei der Mittelung werden also die zentralen Pixel starker gewichtet. Da
es sich — zumindest im Kontinuierlichen — um eine rotationssymmetrische Funk-
tion handelt ist auch die Filterkennlinie (Fouriertransformation) rotationssymme-
trisch und die beim Rechteckfilter storenden Effekte entfallen.15
15Im diskreten Fall ist die Rotationssymmetrie auf einem rechteckigen Raster naturlich nicht mehr komplett sym-
metrisch; dafur lassen sich durch Ausnutzung der Separierbarkeit Effizienzgewinne bei der Berechnung erzielen.
41
(a) Gauß 9 x 9 (b) Rechteck 5 x 5
Abbildung 3.6.16: Mittelung/Tiefpassfilterung mit Gauß- und Rechteckfilter
Ganz allgemein konnen wir in OpenCV beliebige Faltungskernel verwenden; fur
einen Gaußkernel gibt es eine spezielle Funktion:16
kernel = np.ones((5,5)) / 25 # Rechteckfilter
erg = cv2.filter2D(bild, -1, kernel)
erg = cv2.GaussianBlur(bild,(9,9),0) # Gaußfilter
3.7 Hochpassfilter — Kantendetektion 1
Wenn ein Tiefpassfilter Rauschen reduziert, dann wird ein Hochpassfilter das Rau-
schen verstarken. Dies ist in der Tat der Fall, aber naturlich nicht die Motivation
zum Einsatz entsprechender Filter. Vielmehr konnen Hochpassfilter zur Detekti-
on von Kanten eingesetzt werden. Um das zu verstehen konnen wir sowohl im
Ortsraum wie auch im Frequenzraum argumentieren.
Der Kernel
h(x, y) =
−1 0 1
−2 0 2
−1 0 1
(3.35)
implementiert in Jeder Zeile eine Ableitung (in horizontaler Richtung). -1 0 1 be-
deutet letztlich, dass der Funktionswert
f(x+ 1)− f(x− 1) ≈ df
dx(3.36)
16Fur den Rechteckfilter gibt es ebenfalls eine spezielle Funktion: cv2.box()
42
berechnet wird, also der Differenzenquotient bzw. naherungsweise die erste Ab-
leitung in horizontaler Richtung.
Die erste Ableitung einer Funktion ist aber genau an der Stelle einer Kante (be-
tragsmaßig) groß. Der Operator implementiert also (gefolgt von Absolutwertbil-
dung) einen Katendetektor fur Kanten in horizontaler Richtung (Abb. 3.7.17).
-1 0 1 ergibt in der Fouriertransformation einen Sinus, d.h. im Ursprung die Starke
Null, d.h. einen Tiefpasseffekt (wesentlich ist der Betrag der Fouriertransformier-
ten fur den Durchlass. Anschaulich kann man auch folgendermaßen argumentie-
ren: Eine Kante hat (im Gegensatz zu einer homogenen Flache) ein ausgedehntes
Spektrum (senkrecht zur Kante) und damit viele hohe Frequenzen. Genau die-
se charakteristischen hohen Frequenzen werden durchgelassen. Der Gleichanteil
(entsprechend einer homogenen Grundhelligkeit) entspricht dagegen tiefen Fre-
quenzen und wird eliminiert.
Im Ortsraum ist auch direkt verstandlich, dass die zweite Ableitung der Kanten-
detektion verwendet werden kann, denn die zweite Ableitung wechselt an einer
Kante von Negativ nach Positiv, wird also an der Kantenposition selbst Null. Ent-
sprechende Filter der 2. Ableitung werden Laplacefilter genannt:17
h(x, y) =
0 −1 0
−1 4 −1
0 −1 0
bzw.
−1 −1 −1
−1 8 −1
−1 −1 −1
bzw.
0 0 −1 0 0
0 −1 −2 −1 0
−1 −2 16 −2 −1
0 −1 −2 −1 0
0 0 −1 0 0
(3.37)
Der zentrale Koeffizient, z.B. 4 im ersten Fall, muss dann gerade die Summe der
anderen Koeffizienten ausgleichen, wenn wir fordern, dass der Filter in homoge-
nen Gebieten (keine Kanten) als Antwort Null liefert. Dabei wurde fur den 5 x 5
Filter bereits eine Kombination von Mittelung und zweiter Ableitung verwendet.
Man spricht in diesem Fall auch von Laplace-of-Gaussian (LOG) Filter.
Wahrend der Laplacefilter als 2. Ableitung in Bildern mit konstantem Gradient
eben Null ergibt, liefert die 1. Ableitung (Sobel) eine Konstante ungleich Null.
17Beachten Sie, es gibt viele verschiedene Varianten, wie man diskrete Naherungen von entsprechenden Ableitungs-
filtern konstruieren kann. Die generelle Struktur bleibt gleich, die Koeffizienten konnen sich aber im Detail unterschei-
den.
43
Weil die Ableitungsfilter eben das Rauschen verstarken wird oft vor ihrer Anwen-
dung eine Mittelung mittels einem Tiefpassfilter durchgefuhrt (letztlich wird also
ein Bandpass angewendet, um die sehr hohen Frequenzen abzudampfen). Wenn
man weg von den linearen invarianten Filtern geht, dann lassen sich verschiedene
Strategien nutzen, um Kanten sinnvoll zu detektieren und gleichzeitig das Rau-
schen klein zu halten.
Was hier sinnvoll ist hangt allerdings stark von der Aufgabenstellung ab. Eine Va-
riante ist es, den Filterkernel lokal an die vorherrschende Kantenorientierung an-
zupassen und praktisch die Kantendetektion eben senkrecht zur Kante und gleich-
zeitig eine Mittelung in der hierzu senkrechten Richtung durchzufuhren. Das war
z.B. implizit in Gl. 3.35 der Fall, denn dort wurde in y-Richtung offensichtlich noch
eine gaußformige Mittelung durchgefuhrt. Naturlich lassen sich entsprechende
Filter auch fur andere Orientierungen durchfuhren
Eine komplette Vermeidung einer Rauschverstarkung (zumindest eines Teils des
Rauschspektrums) wird man prinzipiell bei der Kantendetektion nicht erzielen,
denn Kanten sind quasi durch eine starke Anderung der Grauwerte definiert. Und
auch Rauschen fuhrt eben zu einer lokalen Grauwertanderung.
(a) horizontale Ableitung (Sobel X) (b) vertikale Ableitung (Sobel Y)
Abbildung 3.7.17: Differenzenquotient als Naherung der Ableitung.
Oft wird eine Kantenverstarkung dadurch erzielt, dass man vom Orginalbild eine
tiefpassgefilterte (z.B. mittels Gaußfilter) Version abzieht (damit wird auch letzt-
lich wieder ein Hochpass realisiert). Dann wird dieses Differenzbild mit einer ge-
wissen Gewichtung zum Orginalbild addiert. Man spricht von Unsharp Masking.
44
Kapitel 4
Nichtlineare Filter
4.1 Einfache Kombinationen: Kantendetektion 2
Wir hatten gerade in Abschnitt 3.7 und mit Gl. 3.35 gesehen, wie man mittels eines
linearen Filters Ableitungen in horizontaler oder vertikaler Richtung detektiert.
Fur eine echte Kantendetektion hatte man naturlich meist gern eine Un-
abhangigkeit der Detektion von der Kantenorientierung, d.h. sowohl horizontale
wie auch vertikale oder auch diagonale Kanten sollen erkannt werden.
Die hierbei typischerweise gewahlte Vorgehensweise ist sehr exemplarisch fur die
Bildverarbeitung: Man berechnet die linearen Filterergebnisse fur den linearen Fil-
ter in X-Richtung und in y-Richtung und kombiniert die Ergebnisse nichtlinear.
Wenn wir im Fall der Kantendetektion also die beiden Filter (sogenannte Sobelfil-
ter)
Sh(x, y) =
−1 0 1
−2 0 2
−1 0 1
undSv(x, y) =
−1 −2 1
0 0 0
1 2 1
(4.1)
anwenden und die Ergebnisse oh = Sh ∗ i und ov = Sv ∗ f nichtlinear gemaß
K(x, y) =√
oh(x, y)2 + ov(x, y)2 (4.2)
kombinieren, dann gibt uns K ein Maß fur die Kantenstarke.1 (Die Kantenorien-
tierung kann im ubrigen durch φ = atan ov/oh geschatzt werden.2) Der beruhmte
1Weil die Kombination nichtlinear ist konnen wir die beiden Sobelfilter nicht zu einer gemeinsamen Matrixopera-
tion verbinden.2Andere Operatoren, z.B. Kompassoperatoren und Kirschoperatoren, konnen zu einer genaueren Orientie-
rungsschatzung eingesetzt werden. Das Prinzip ist letztlich dasselbe, lediglich die Kerneldefinitionen sind eben leicht
anders.
45
Canny Kantendetektor fußt auf diesem Ansatz (und fuhrt noch einige Nachbear-
beitungen des Ergebnisses durch).
Auch beim Laplacefilter, der einer zweiten Ableitung entspricht und ebenfalls li-
near ist, benotigt man eine nichtlineare Nachbearbeitung, denn es mussen aus
dem Filterergebnis nachfolgend noch die Nullstellen gewonnen werden. Dabei
muss aber eben lokal untersucht werden, ob die Nachbarschaft des Pixels mit ge-
ringer Helligkeit eine hohe Helligkeit aufweist (andernfalls ist der Nulldurchgang
irrelevant).
(a) Sobelfilter (b) Cannyfilter (c) Laplacefilter
Abbildung 4.1.1: Gebrauchliche Kantendetektoren
In OpenCV konnen wir die entsprechenden Operationen folgendermaßen
durchfuhren:
median = np.median(bild)
erg = cv2.Canny(bild, 0.6*median, 1.5*median, 5)
erg = cv2.Laplacian(bild,cv2.CV_16S, ksize = 5)
def sobel(img, kernelsize):
sobelx = cv2.Sobel(img,cv2.CV_16S,1,0,ksize=kernelsize)
sobely = cv2.Sobel(img,cv2.CV_16S,0,1,ksize=kernelsize)
abs_sobelx = cv2.convertScaleAbs( sobelx )
abs_sobely = cv2.convertScaleAbs( sobely)
erg = cv2.addWeighted( abs_sobelx, 0.5, abs_sobely, 0.5, 0)
return erg
erg = sobel(bild,3)
46
4.2 Rangordnungsfilter und Rauschunterdruckung
Viele Filter arbeiten — genau wie unsere bereits bekannten linearen Filter — je-
weils um eine Nachbarschaft des aktuellen Pixels. Wieder gibt es also ein Fenster
einer gewissen Große, um den Pixel. Anstatt nun aber wie z.B. beim Rechteck-
filter einfach den Mittelwert dieses Fensterbereichs zu berechnen, werden etwas
kompliziertere Berechnungen durchgefuhrt.
Viele globale Operationen, lassen sich so in der lokalen Umgebung durchfuhren.
Z.B. konnen wir den Histogrammausgleich anstatt fur das ganze Bild fur jeden
Pixel mit einer gewissen Nachbarschaft ausfuhren. D.h. ein Fenster (z.B. 5 x 5 Pi-
xel groß) wird wieder uber das Filter geschoben und an jeder Stelle wird ein Hi-
stogrammausgleich berechnet. Mit dieser lokalen Histogrammausgleichsfunktion
wird dann der aktuelle, mittleren Pixel im Fenster modifiziert. Dann wird das Fen-
ster weiter bewegt und erneut berechnet [3]. Fur ein Bild mit einer Million Pixel
werden so dann eben eine Million “kleine” Histogrammausgleiche durchgefuhrt.
Wir konnen aber auch andere solcher Operationen, z.B. Binarisierungen oder die
Berechnung von Standardabweichungen, anwenden. Oder aber wir verwenden
eine gesteuerte Transformation bei der z.B. ein Mittelwert nur dann gebildet wird,
wenn das Rauschen im Fensterbereich kleiner einer bestimmten Schwelle ist. Die
Moglichkeiten sind vielfaltigst. Was bleibt ist aber eben der Ansatz der lokalen
Bearbeitung, bei der eine Maske Pixel fur Pixel uber das Bild geschoben wird.
Eine Klasse von Filtern, die sogenannten Rangordnungsfilter, sortieren zunachst
die Grauwerte des Fensterbereichs und wahlen dann einen der Werte aus. Das
bekannteste Beispiel sind der Medianfilter, der Maximumfilter und der Minimum-
filter, die eben entsprechend Median, Maximum oder Minimum des Bereichs als
Ergebniswert verwenden.
Vor allem der Medianfilter ist sehr gebrauchlich, um eine Rauschunterdruckung
bei Erhaltung von Kanten zu erzielen.3 Vor allem impulsartiges Rauschen (soge-
nanntes salt-and-pepper Rauschen) wird sehr effizient verringert. Der Maximum-
und der Minimumfilter werden uns noch etwas ausfuhrlicher im Kapitel 4.3 be-
gegnen.
Beim Bilateralen Filter wird ein Pixelwert wieder durch das Mittel seiner benach-
barten Pixel ersetzt (Analog dem Gauß- oder dem Rechteckfilter). Aber fur die
3Ganz generell werden Filter, die Rauschen reduzieren, oft auch als Glattungsfilter bezeichnet.
47
Berechnung werden nun nur Pixel in Betracht gezogen, die einen ahnlichen Grau-
wert wie der Pixel selbst aufweisen.
erg=cv2.bilateralFilter(bild, 7, 9, 9)
erg=cv2.medianBlur(bild,7)
Mehrmals (iterativ) angewandt fuhrt der bilaterale Filter zu eher homogenen
Flachen und damit einer Art cartoonhafter Darstellung (Abb. 4.2.2).4
(a) Original (b) iterative Anwendung Bilateralfilter
Abbildung 4.2.2: Iterative Anwendung (bis keine Bildanderung mehr) des Bilateral Filters, hier mit
9x9 und delta=9
def untilNothingChanges(img, funktion, argumente, schwelle):
while True:
erg = funktion(img, *argumente)
fehler = np.mean(np.abs(erg-img))
print(fehler)
if(fehler < schwelle):
break
else:
img = erg
return erg
erg = untilNothingChanges(bild, blurBilateral, (9,9), 5)
4Diese Vorgehensweise kann bei manchen Aufgabenstellungen als einfache Segmentierung dienen.
48
Abb. 4.2.3 zeigt sehr schon, dass feine Details/Peaks und Kanten mit einem bi-
lateralen Filter sehr gut erhalten bleiben konnen wahrend lineare Filter zu einem
Verlust der Details und starken Kantenverrundungen fuhren.
(a) Original (b) Gaußfilter 15 x 15
(c) Medianfilter 7 x 7 (d) Bilateralfilter 9 x 9 mit delta=7
Abbildung 4.2.3: Nichtlineare Mittelungsfilter (Median und Bilateralfilter ) konnen Kanten und De-
tails (Bilateralfilter, Beachten Sie den Peak rechts) erhalten und gleichzeitig Rauschen reduzieren.
49
4.3 Morphologie
Auch die morphologische Bildverarbeitung nutzt letztlich den selben kernelba-
sierten Ansatz, bei dem eben anstatt einer einfachen linearen Operation (gewich-
tete Summe) im Kernelbereich eine nichtlineare Operation durchgefuhrt wird.
Die Basisoperationen (Dilatation und Erosion) konnen als Erweiterung der bereits
bekannten Rangordnungsfilter um eine Maske (Strukturelement) betrachtet wer-
den. Durch spezielle Wahl des Kernels kann man die Methoden aber auch zur
Detektion von Objekten nutzen.
Bei der Erosion werden alle Pixel, an denen die Maske (sogenanntes Strukturele-
ment) eins ist untersucht. Das Minimum dieser Pixel wird als Ergebniswert ver-
wendet. Bei der Dilatation entsprechend das Maximum. Damit wachsen bei der
Dilatation die hellen Bereiche und bei der Erosion wachsen die dunklen Gebiete.
Sehr gebrauchlich ist der Einsatz fur Binarbilder. Hier fuhrt die Dilatation zu ei-
ner Vergroßerung der hellen Bereiche und die Erosion zu einer entsprechenden
Verkleinerung. Daher lassen sich Erosion und Dilatation auch sehr gut nutzen,
um Lucken in Gebieten zu schließen. Allerdings schrumpfen oder wachsen dabei
auch die Gebiete selbst. Um dies wieder auszugleichen, muss die entgegengesetz-
te Operation angewendet werden (siehe Abb. 4.3.4).5
Eine Erosion gefolgt von einer Dilatation nennt man Opening. Die Dilatation ge-
folgt von der Erosion wird als Closing bezeichnet.
(a) Original (b) Binarisiert (c) Dilatation (d) Erosion
Abbildung 4.3.4: Beispiel fur Schließung von Lucken durch Closing
Wenn man das ganze mit einem inversen Bild durchgefuhrt hatte, dann ware die
Reihenfolge Erosion vor Dilatation gewesen. Diese Operation nennt sich entspre-
chend Opening.
Als Kernel kann man eine beliebige binare Maske verwenden. OpenCV definiert
5Allerdings kann sich die Form des Gebiets durchaus leicht andern.
50
einige gebrauchliche Masken bereits vor. Die Grundoperationen werden durch die
folgenden Befehle ausgefuhrt:
kernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
kernel=cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
erg = cv2.dilate(bild, kernel);
erg = cv2.erode(erg, kernel);
erg = cv2.morphologyEx(bild, cv2.MORPH_CLOSE, kernel)
erg = cv2.morphologyEx(bild, cv2.MORPH_OPEN, kernel)
erg = cv2.morphologyEx(bild, cv2.MORPH_GRADIENT, kernel)
erg = cv2.distanceTransform(bild, cv2.DIST_L2, kernel )
Wenn man das Strukturelement passend zu einem gesuchten Element im Bildwahlt, dann kann man entsprechend dieses Element detektieren. Ein schones Bei-spiel fur die Detektion von horizontalen Strukturen 6 findet man in
https://docs.opencv.org/3.1.0/d1/dee/tutorial\_moprh_lines_detection.html
Der sogenannte Morphologische Gradient ist einfach die Differenz von Opening und
Closing und damit sehr gut geeignet, Konturen von Gebieten zu markieren. Er ist
bei Graustufenbilder fur die Detektion von Kanten einsetzbar (Abb. 4.3.5).
(a) Ergebnis fur Binarbild (b) Ergebnis fur Graustufenbild
Abbildung 4.3.5: Der morphologische Gradient kann sehr gut zur Detektion von Umrissen verwen-
det werden. Hier angewendet auf Dilatationsergebnis.
Die sogenannte Distanztransformation liefert fur jeden Pixel eines Gebiets den Ab-
stand zum Rand des Gebiets. Sie kann (wenn auch ineffizient) durch fortlaufende6dazu eben den Kernel asymmetrisch, z.B. (7,1) statt (5,5), wahlen
51
Erosion durchgefuhrt werden. Die Anzahl der Erosionen bis sich ein bestimmter
Pixel bei der Erosion nicht mehr andert, ist dann der Distanzwert.
Dilatation und Erosion konnen auch genutzt werden, um kleine Details zu eli-
minieren. Dadurch kann dann der Hintergrund oder auch eine ungleichmaßige
Ausleuchtung detektiert und dann nachfolgend ausgeglichen werden. Ein Bei-
spiel zeigt der folgende Code und Abb. 4.3.6. Eine direkte Binarisierung wurde
hier aufgrund der ungleichmaßigen Ausleuchtung nicht funktionieren. Zunachst
muss also die Ausleuchtung bestimmt werden, um dann ein korrigiertes Bild zu
erhalten.
bild = standard.page()
hoehe, breite = bild.shape
kernel = np.ones((7, 7), np.uint8)
dil = cv2.dilate(bild, kernel, iterations=1)
dil2 = cv2.GaussianBlur(dil,(13,13), 0)
hintergrund = cv2.resize(dil2, (breite, hoehe))
korrektur = bild / hintergrund * 255
ret,ergebnis = cv2.threshold(korrektur,200,255,cv2.THRESH_BINARY)
(a) Orginalbild (b) Dilatation und Gaußsches Smoothing
(c) Orginalbild / Beleuchtung (d) Binarisierung
Abbildung 4.3.6: Binarisierung mit vorgelagertem Ausgleich der Beleuchtung. Zur Bestimmung der
Beleuchtung (Bild (b)) wird die Dilatation eingesetzt.
52
Ein weiteres Beispiel ist die Tophat Transformation, die einfach als die Differenz
von Orginalbild und dilatiertem Bild definiert ist. Sie kann eingesetzt werden, um
feine Strukturen zu identifizieren.
In Abb. 4.3.7 wird dabei der Kratzer im Photo detektiert (und dann dieser Bereich
nachfolgend mit passenden Werten aus der Umgebung aufgefullt).
(a) Orginalbild (b) Tophat (c) Ergebnis
Abbildung 4.3.7: Tophat Transformation zur Detektion von Kratzern
53
Kapitel 5
Segmentierung
Segmentierung bedeutet, dass das Bild in unterschiedliche Segmente eingeteilt
wird. Dabei sollen die Segmente unterschiedlichen Objekten oder Objektteilen
entsprechen. Oft ist die Segmentierung ein Teilschritt zur Objekterkennung, so
dass zunachst das Objekt vom z.B. Hintergrund segmentiert wird, bevor dann
entschieden wird, um welches Objekt (Hund, Hand, Henne) es sich handelt.
Haufig ist die Segmentierung auch schlicht notwendig, um an sich bekannte Ob-
jekte (z.B. gefertigte Teile) vom Hintergrund zu trennen und dann z.B. zu zahlen
oder ihre Position zu bestimmen etc.
Segmentierung kann sehr einfach sein (Munzen auf homogenem Untergrund),
aber auch beliebig schwierig (Beispiel: Ein Kind dessen Korper vom parkenden
Auto verdeckt ist). Letztlich wird fur jeden Pixel entschieden, zu welcher Klasse
(z.B. Hintergrund, Hand, Kind, Munze-Nummer-17) er gehort.
Diese Entscheidung kann auf allem moglichen basieren. Der Standardansatz ist
dementsprechend relevante Merkmale (Features) zu definieren und zu finden und
diese Features dann im Sinne einer Klassifikation zu nutzen.
Einfache Varianten fur entsprechende Features sind Grauwert, Farbe (vgl. Ab-
schnitt 2.1.2), Textur, Bewegung, lokale Kantenorientierung oder Kantenstarke.
5.1 Connected Components
Sobald ein Binarbild von Vordergrundobjekten vor einem (schwarzen) Hinter-
grund vorliegt (bei einfachen Aufgaben z.B. durch Thresholding), kann mit der
54
OpenCV Funktion connectedComponents eine Nummerierung der Objekte vor-
genommen werden. D.h. die Funktion liefert ein Bild zuruck, bei dem jeder Pixel
entweder Null (Hintergrund) oder aber die Nummer der Komponente (des Ob-
jekts) beinhaltet. Alle Pixel, die zu Objekt Nummer 2 gehoren haben also den Wert
2 (Abb. 5.1.1).
erg = cv2.connectedComponents(bild)
Abbildung 5.1.1: Connected Components Analyse
Letztlich werden dabei — analog einer fill-Funktion — einfach zusam-
menhangende Gebiete mit demselben Label versehen. Solange, bis alle Pixel un-
gleich Null bearbeitet wurden.
5.2 Watershed Algorithmus
Der Watershed Algorithmus folgt einem recht einfachen Prinzip, um bereits ander-
weitige markierte Positionen, die zu unterschiedlichen Regionen gehoren, zu seg-
mentieren.
Dazu stellt man sich die markierten Positionen als Quellen vor, bei denen Wasser
austritt. Die Graustufen denkt man sich als Hohen eines Gebirges.
Dann lasst man das Wasser immer weiter ansteigen, d.h. zu “Wasser” benachbar-
te Pixel werden zunehmend auch mit Wasser gefullt. In jedem Schritt steigt der
Wasserspiegel (Grauwert) um eine Quantisierungsstufe an. Sobald Wasser einer
Region auf Wasser einer anderen Region trifft wird zwischen beiden Wassersorten
55
eine Grenze eingezogen. Somit ergeben sich automatisch und auf dem Grauwert
des Bildes basierend Grenzen zwischen den Regionen.
Naturlich kann man auch andere Merkmale als den Grauwert verwenden. Z.B.
die Farbe, oder auch den Gradient oder ein Texturmerkmal.
Die Generierung der Startpunkte (Quellen) kann entweder von Hand bzw. ap-
plikationsspezifisch oder aber ebenfalls automatisiert anhand des Bildes erfolgen.
Eine typische Variante ware z.B. die Connected Components Einteilung aus dem
vorangegangenen Abschnitt.
Die OpenCV Implementierung fordert ubrigens explizit ein Farbbild als Eingangs-
bild und ein 32 Bit Integer Quellenbild:
bild = standard.camera()
farbe = cv2.cvtColor(bild, cv2.COLOR_GRAY2BGR)
quellen = np.int32(bild)
quellen[:,:] = 0
quellen[92, 97] = 10
quellen[170, 156] = 20
quellen[272,403] = 30
quellen[324,434] = 40
quellen[380,445] = 50
ergebnis = cv2.watershed(farbe,quellen)
(a) Eingangsbild mit Markierungen
(Quellen)
(b) Ergebnis
Abbildung 5.2.2: Watershed Algorithmus zur Segmentierung von markierten Gebieten
56
5.3 Konturdetektion
Auch die Detektion von Konturen kann als Segmentierungsaufgabe aufgefasst
werden. In diesem Fall werden die Konturen vom Rest des Bildes segmentiert.
Die Konturdetektion in OpenCV erfordert letztlich bereits (nahezu) Binarbilder,
d.h. es wird also empfohlen, dass das Bild bereits binarisiert ist (weißes Ob-
jekt auf schwarzem Hintergrund). OpenCV gibt dann die gefundenen Konturen
zuruck. Dabei kann die Kontur punktweise oder aber approximiert durch Linien
ruckgegeben werden.
import cv2
import skimage.data as standard
# 1. ------------ Segmentierung -----------------
bild = standard.coins()
bild2 = threshold(bild,125)
bild3 = cv2.morphologyEx(bild2, cv2.MORPH_CLOSE, kernel)
# 2. ------------- Konturen finden ------------------
im2, contours, hierarchy = cv2.findContours(bild3, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
# 3. ------------- Ergebnis eintragen in Farbe ------
farbe = cv2.cvtColor(bild, cv2.COLOR_GRAY2BGR)
cv2.drawContours(farbe, contours, -1, (0,255,0), 1)
57
Kapitel 6
Hough Transformation
Die Hough Transformation ist wie die Fouriertransformation eine globale Transfor-
mation. D.h. fur einen Pixel im Ergebnisbild sind (potentiell) alle Pixel des Ein-
gangsbilds relevant.
Mit ihr konnen wir auch in extrem verrauschten Bildern einzelne einfache For-
men1 sehr robust detektieren. Wir besprechen dies am klassischen Beispiel der
Detektion von Linien, aber das Prinzip lasst sich auch auf Kreise, Ellipsen etc.
ubertragen [6].
Die Grundidee ist folgende: Betrachten wir einen willkurlichen Pixel im Eingangs-
bild. Und wir nehmen an, dass er Teil einer Gerade ist. Teil welcher Gerade wissen
wir noch nicht.
Wir legen ein Array “Hough-Space” an, in dem jeder Punkt eine Gerade re-
prasentiert. Z.B. konnte die eine Achse des Array die Steigung a und die andere
Achse den Achsabschnitt b fur die Gerade y = a x + b reprasentieren.
Jeder Pixel des Orginalbilds kann durch viele Geraden entstanden sein und wenn
wir alle diese moglichen Geraden (reprasentiert durch jeweils einen Punkt im
Hough-Space) zeichnen, dann ergibt sich im Hough-Space interessanterweise wie-
der eine Gerade (s. Abb. 6.0.1).
Gehen Sie zunachst von einem Binarbild aus. Der entscheidende Schritt ist nun,
dass wir also fur jeden hellen Pixel des Eingangsbilds eine Gerade in den Hough-
Space malen. “Malen” ist etwas ungenau ausgedruckt. Wir akkumulieren das
Hough-Space Array. Je mehr Geraden im Hough-Space durch einen Pixel im
Hough-Space gehen, desto heller wird dieser Punkt. Bzw.: Der Wert eines Pixels
1genauer: Formen, die durch sehr wenige Parameter parametrierbar sind
58
Abbildung 6.0.1: Jeder Punkt einer Geraden y = a x + b im Ortsraum entspricht einer Geraden im
Hough Space (a,b). Dort wo sich die Geraden (jede Gerade zugeordnet einem Punkt im Ortsraum)
schneiden findet man die Parameter der Gerade (im Beispiel: a = 0.5, b = 1 entsprechend der Ge-
raden im Ortsraum y = 0.5 x + 1). Beachten Sie auch: Jede Gerade im Hough Space reprasentiert
unendlich viele Geraden im Ortsraum. Z.B. reprasentiert die grune Gerade alle denkbaren Gera-
den, die durch den grunen Punkt im Ortsraum laufen.
im Hough-Space gibt schlicht an, wieviele Pixel im Eingangsbild mit der durch
den Hough-Space Pixel reprasentierten Geraden konform gehen.
Nehmen Sie am besten an, dass Sie wirklich nur eine perfekte Gerade im Ein-
gangsbild hatten. Jeder Pixel der Gerade fuhrt zu einer Geraden im Hough-Space.
Alle Geraden im Hough-Space schneiden sich in exakt einem Punkt und genau
dieser Punkt reprasentiert (uber seine Koordinaten a und b) eben die Gerade (sie-
he Abb. 6.0.1).
Fur Graustufenbilder konnen wir bei der Akkumulation im Hough-Space einfach
die Geraden mit der Helligkeit des Bildpunkts akkumulieren.2
Wenn man auf diese Weise ein Bild in den Hough-Space transformiert hat, dann
sucht man nachfolgend im Hough-Space die Peaks und erhalt so alle Geraden
(Abb. 6.0.1).
Die bisherige Darstellung hat in der Praxis einen gewaltigen Nachteil, denn
naturlich kann die Steigung leicht unendlich werden (vertikale Gerade). Daher
ist die Standardparametrierung mit y = ax + b nicht wirklich geeignet.
Stattdessen verwendet man ublicherweise
x cosφ+ y sinφ = d (6.1)2Fur kontinuierliche Variablen nennt man die Transformation auch Radon-Transformation.
59
Naturlich ist die Hough Transformation sehr kostspielig. Je exakter man die Gera-
denparameter bestimmen will, desto mehr Sampling im Hough-Space und de-
sto großer der Aufwand. Zur Beschleunigung kann man Vorinformation (aus
der Aufgabenstellung oder durch einfache Vorverarbeitung) nutzen, oder iterativ
zunachst die Parameter grob bestimmen und dann weiter verfeinern.
In OpenCV ist direkt eine (eigentlich zwei) Funktion zur Berechnung furBinarbilder vorhanden. Der nachfolgende Code zeigt etwas langlich auch den Ein-trag des Ergebnis (s. Abb. 6.0.2) in ein Bild mittels Farbe:
import cv2
import numpy as np
bild = cv2.imread("esa2.pgm", cv2.IMREAD_GRAYSCALE)
ret,bild = cv2.threshold(bild,40,240,cv2.THRESH_BINARY)
minLength = 150 # Minimale Laenge der Linien
lines = cv2.HoughLines(bild,1,np.pi/180,minLength)
for l in range(len(lines)): # Alle Linien prozessieren
for rho,theta in lines[l]:
a, b = np.cos(theta), np.sin(theta)
x0, y0 = a*rho, b*rho
x1, y1 = int(x0 + 1000*(-b)), int(y0 + 1000*(a))
x2, y2 = int(x0 - 1000*(-b)), int(y0 - 1000*(a))
cv2.line(bild,(x1,y1),(x2,y2),255,2)
def farbe(r,g,b):
return (b+g*256+r*256*256 + 255*256*256*256).astype(’int’)
farbbild = dataObject(bild.shape,’rgba32’)
hoehe, breite = bild.shape
for x in range(breite):
for y in range(hoehe):
value = bild[y,x]
if(value == 255):
farbbild[y,x] = int(farbe(0, value, 0))
else:
farbbild[y,x] = int(farbe(value, value, value))
plot(farbbild)
60
(a) Eingangsbild (b) Ergebnis
Abbildung 6.0.2: Hough Transformation zur Findung von Linien in einem verrauschten Binarbild.
61
Kapitel 7
Objekterkennung und Klassifikation
Sehr oft wird folgender Ablauf zur Objekterkennung durchgefuhrt:
1. Finde potentielle Kandidaten fur ein Objekt.
2. Segmentiere das Objekt
3. Merkmalsberechnung
4. Klassifiziere das Objekt.
Die bisher dargestellten Methoden konnen naturlich alle verwendet werden, um
Objekte zu finden und zu segmentieren. Basierend auf dem Segmentierungsergeb-
nis konnen dann Merkmale bestimmt werden und diese werden dann zur Klassi-
fikation verwendet.
Nicht immer sind die einzelnen Punkte klar zu trennen und im Extremfall kann
man mit einem Verfahren (Abschnitt 7.2) auch alle Schritte auf einmal erledigen.
Wir wollen im folgenden, bevor wir eine Klasse von solchen Komplettverfahren
betrachten, vor allem noch einige typische Merkmale vorstellen und etwas zur
Klassifikation sagen.
7.1 Merkmale
Ganz generell ist es fur viele Anwendungen wesentlich, ob die Merkmale invari-
ant unter
• Rotation
62
• Skalierung
• Ansicht (bei 3D Objekten)
sind.
Letztlich kann naturlich Alles ein mogliches Merkmal sein und es hangt ganz
von der Anwendung ab, welche Merkmale sinnvoll sind. Am einfachsten ist es
zunachst im segmentierten Objektgebiet
• Farbe oder Grauwert
• Standardabweichung von Farbe / oder Grauwert
• Texturmerkmale (siehe z.B. [7, 6, 1])
• lokale Histogramme
zu berechnen.
Aber auch die Form der Objekte kann ein wichtiges Kriterium sein. Diese kann
naturlich beliebig kompliziert sein und dementsprechend sind auch beliebig kom-
plizierte Merkmale daraus generierbar. Gebrauchlich sind aber vor allem zunachst
die sogenannten Momente.
Mij =∑
x
∑
y
xiyjGW (x, y) (7.1)
µij =∑
x
∑
y
(x− x)i(y − y)jGW (x, y) (7.2)
Die Ordnung der Momente ist dabei die Summe i+ j.
und daraus dann abgeleitet:
Flache: M00 als Summe aller Pixel des Objekts (bei binaren Objekten)
Schwerpunkt:
xS =M10
M00und yS =
M01
M00(7.3)
Objektorientierung:
tan(2φ) =2µ11
µ20 − µ02(7.4)
63
Exzentrizitat:
e =|µ20 − µ02|2 + 4µ2
11
|µ20 + µ02|2(7.5)
Bounding Box: Das das Gebiet komplett umschließende kleinste Rechteck.
Die Momente Mij selbst konnen mit M(i+j)/200 skaliert werden, um großen- (bzw.)
skalierungsinvariante Momente zu konstruieren.
In OpenCV konnen Momente fur durch Konturen beschriebene Gebiete einfach
berechnet werden.
bild = cv2.imread("test.pgm", cv2.IMREAD_GRAYSCALE)
ret,bild = cv2.threshold(bild,80,250,cv2.THRESH_BINARY)
im2,contours,hierarchy = cv2.findContours(bild, 1, 2)
kontur = contours[0]
M = cv2.moments(kontur)
swpx = M[’m10’]/M[’m00’]
swpy = M[’m01’]/M[’m00’]
flaeche = M[’m00’]
print(swpx, swpy, flaeche, M)
Einige Parameter konnen sehr gut aus dem sogenannten Kettencode des segmen-
tierten Gebiets berechnet werden. Der Kettencode gibt dabei an, wie von einem
Randpixel zum nachsten Randpixel gesprungen wird [6]. Hierzu werden einfach
die moglichen 8 (oder auch nur 4) Richtungen, also Nord, Nord-Ost, Ost, etc. ko-
diert. Der Kettencode selbst ist dann die Abfolge der Richtungswechsel, die notig
sind, um einmal das segmentierte Gebiet zu umrunden.
Eine alternative (und teilweise machtigere) Variante der Kettencodes sind die so-
genannten Fourierdeskriptoren [6].
7.2 Template Matching und Korrelation
Anstatt (muhsam und mit relativ aufwandigen Methoden) Objekte zu finden, seg-
mentieren und dann nachtraglich zu klassifizieren, gibt es auch Verfahren, die di-
64
rekt auf dem Graustufenbild Muster suchen. Dabei wird also direkt ein bestimm-
tes Graustufenmuster im Bild gesucht.
Dieser Ansatz funktioniert nicht immer, denn es ist hierfur notwendig, dass das zu
suchende Muster vergleichsweise unverandert auch im Bild vorhanden ist. Wenn
dies der Fall ist, dann sind entsprechende Verfahren aber sehr gut geeignet und
mathematisch vergleichsweise einfach und klar. Insbesondere beim Suchen von
klar definierten Teilen in klar definierter Große und Orientierung sind die Verfah-
ren sehr leistungsfahig, werden aber auch z.B. fur Deformationsanalyse und in
Stereo-Vision Anwendungen eingesetzt. Der Rechenaufwand ist durchaus hoch,
in Zeiten schneller Rechner aber auch nicht zu problematisch.1
Die Grundidee ist es — ganz analog zum Ansatz unserer linearen und nichtli-
nearen Filter — eine Maske (bzw. Kernel) uber das Bild zu schieben und an jeder
Position die Abweichung von dem entsprechenden Bildausschnitt zum Muster
(Template) zu berechnen.
Der naheliegendste Ansatz ist es, die Differenz von Template zum Bild zu berech-
nen. Da die Differenzen fur jeden Pixel positiv oder negativ sein konnen ware das
Ergebnis auch bei volliger Nichtubereinstimmung nahe Null. Dementsprechend
muss statt der direkten Differenz eher der Absolutwert der Differenz verwendet
werden. In diesem Fall ware also die Abweichung von Bild zu Template an der
Stelle (x,y):
SAD(x, y) =∑
x′,y′
|B(x+ x′, y + y′)− T (x′, y′)| (7.6)
SAD steht dabei fur Sum of Absolute Differences. Ublicher ist allerdings, dem Pro-
blem der negativen Differenzen durch Quadrierung beizukommen:
E(x, y) =∑
x′,y′
(B(x+ x′, y + y′)− T (x′, y′))2 (7.7)
Dabei werden zusatzlich starkere Abweichungen auch starker gewichtet. Vor al-
lem aber kann die Formel weiter vereinfacht werden und fuhrt so schließlich zu
einer deutlichen Effizienzsteigerung. Zu beachten ist namlich, dass Gl. 7.7 bei
großen Templates sehr schnell kostspielig wird. Fur ein Bild mit N x N Pixeln und
ein Template mit M x M Pixeln werden eben O(N2 M2) Operationen notwendig.
1Auf Grafikkarten konnen inzwischen tausende Korrelationen pro Sekunde durchgefuhrt werden.
65
Gl. 7.7 wird ausmultipliziert und es ergibt sich:
E(x, y) =∑
x′,y′
B(x+ x′, y + y′)2 + T (x′, y′)2 − 2B(x+ x′, y + y′)T (x′, y′) (7.8)
=∑
x′,y′
B(x+ x′, y + y′)2 +∑
x′,y′
T (x′, y′)2 −∑
x′,y′
2B(x+ x′, y + y′)T (x′, y′)(7.9)
Wir suchen nun also die Position (x,y), bei der E moglichst gering wird. Der Term∑
T (x′, y′)2 andert sich uber das Bild nicht und ist daher fur die Suche nach der
Position (x,y) vollig irrelevant. Als weitere Naherung nimmt man oft an, dass sich
auch der Term∑
B(x+x′, y+y′)2 nicht zu sehr uber die Position andert. In diesem
Fall wird also
E ′(x, y) =∑
x′,y′
B(x+ x′, y + y′)T (x′, y′) (7.10)
maximiert. Dies (Gl. 7.10) ist die Korrelation von B mit T. Aufgrund der Ahnlichkeit
mit der Faltung lasst sich die Korrelation sehr effizient im Fourierraum berechnen.
Wenn der Term B2 nicht vernachlassigt wird, dann ergibt sich die normalisierte
Korrelation.
import cv2
import skimage.data as standard
bild = standard.camera()
template = bild[80:170, 200:280].copy()
res = cv2.matchTemplate(bild, template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
print(max_val, max_loc)
Anstatt cv2.TM CCOEFF NORMED konnen auch andere Methoden der Fehler-
berechnung verwendet werden. TM SQDIFF ist der direkte quadratische Fehler,
TM CCORR NORMED die normalisierte Kreuzkorrelation und TM CCORR.
Ganz generell ist das Ergebnis umso besser, je großer anteilsmaßig der Template-
bereich ist und naturlich funktioniert das Ganze nur so gut, solange das Template
moglichst ideal mit dem Bild ubereinstimmt. Bereits kleine Rotationen oder Ska-
lierungen fuhren schnell zu einer dramatischen Abnahme des Korrelationspeaks.
66
(a) Szene (b) Template (c) Korrelationsergebnis
Abbildung 7.2.1: Korrelation: Das Maximum in (c), roter Punkt, entspricht genau der Verschie-
bungsposition von Template gegen Szene.
Wenn entsprechende Anderungen in der Anwendung zu erwarten sind, dann
muss entsprechend mit verschiedenen Templates (z.B. bei unterschiedlicher Ska-
lierung) gerechnet werden.2
Eine Variante zur Verbesserung der Korrelationsleistung ist es, die Korrelation ite-
rativ zu verwenden und bei jedem Iterationssschritt (basierend auf dem vorheri-
gen Korrelationsergebnis) das Bild zu maskieren, so dass storende Anteile im Bild
fortlaufend weiter reduziert werden.
7.3 Haar Kaskaden
Eine vergleichsweise schnelle und dennoch leistungsfahige Detektion von Objek-
ten (z.B. Gesichtern) lasst sich mit sogenannten Haar Kaskaden erzielen.
Zunachst werden vergleichsweise einfache Features fur sehr viele Positionen im
Bild berechnet. Die ublicherweise eingesetzten Haar-artigen3 Merkmale sind ein-
fach Differenzen von Helligkeiten in rechteckigen Bereichen und der Hauptvor-
teil gegenuber anderen denkbaren Merkmalen liegt in der sehr effizienten Berech-
nung.
In jedem (rechteckigen) Bildbereich kann man nun sehr, sehr viele entsprechen-
der Merkmale berechnen. Nicht alle von diesen Merkmalen sind fur eine konkrete
Aufgabe (z.B. die Erkennung eines Gesichts) relevant. De-facto reichen sehr we-
2Bis zu einem gewissen Grad konnen auch skalierungs- oder rotationsinvariante Korrelationen erzielt werden,
siehe z.B. [5]3Das “Haar” hat nichts mit Haaren zu tun sondern verdankt seinen Namen den sogenannten Haar-Wavelets.
67
nige. Welche, das wird durch eine Trainingsphase mit vielen Bildern erlernt. (Bei
OpenCV sind entsprechende Lernergebnisse bereits enthalten, so dass man fur
das Beispiel z.B. einer Gesichtserkennung nicht unbedingt eigene Lernvorgange
durchfuhren muss.)
Die “Kaskade” kommt nun noch dazu ins Spiel, dass mehrere (einfache) Detekto-
ren hintereinander geschaltet werden. Aber nur wenn Detektor Nummer i positiv
reagiert wird uberhaupt Detektor i+1 berechnet.4
face_cascade = cv2.CascadeClassifier(’/data/opencv/opencv-3.2.0/\
data/haarcascades/haarcascade_frontalface_default.xml’)
bild = cv2.imread(’astronaut.png’)
bild = cv2.cvtColor(bild, cv2.COLOR_BGR2GRAY)
gesichter = face_cascade.detectMultiScale(bild, 1.1, 5)
print(gesichter)
for (x,y,w,h) in gesichter:
cv2.rectangle(bild,(x,y),(x+w,y+h),(255,0,0),2)
Abbildung 7.3.2: Gesichtsdetektion mit OpenCV
4In der Originalarbeit von Viola und Jones werden 38 Stufen verwendet [8].
68
7.4 Pyramiden
Die Rechenzeit fur die meisten Bildverarbeitungsoperation steigt stark (in der Re-
gel mindestens linear) mit der Pixelanzahl. Deutliche Rechenzeitvorteile lassen
sich also erzielen, wenn man in der jeweils passenden Auflosungsstufe arbeitet.
Dabei ist zu betonen, dass hohe Auflosung nicht immer von Vorteil ist. Wenn
Strukturen einer gewissen Große gesucht werden, dann bringt es nicht nur kei-
nen Vorteil, sondern ist sogar kontraproduktiv, in einer sehr feinen Auflosung zu
arbeiten.
Einige Ansatze, z.B. Wavelet Analysen, arbeiten ganz fundamental auf verschie-
denen Auflosungsstufen.
Wenn man das Bild fortlaufend um den Faktor zwei in der Auflosung reduziert,
landet man bei der klassischen Bildpyramide. Fur die Auflosungsreduktion wird
das Bild zunachst mit einem Gaußfilter gefaltet und dann jede zweite Spalte und
Zeile verwendet.5
(a) Orginalauflosung (b) Reduktion Faktor 2
(c) Reduktion Faktor 4 (d) Reduktion Faktor 8
Abbildung 7.4.3: Orginalbild in unterschiedlichen Auflosungen
bild = standard.camera()
bild = bild[100:300, 100:300]
5Der Platzbedarf fur eine komplette Pyramide ist im ubrigen weniger als 4/3 der Orginalbildgroße.
69
hoehe, breite = bild.shape
erg = cv2.pyrDown(bild, (hoehe/2, breite/2) )
erg2 = cv2.pyrDown(erg, (hoehe/4, breite/4) )
erg3 = cv2.pyrDown(erg2, (hoehe/8, breite/8) )
7.5 Klassifikation
Sobald wir Merkmale gewonnen haben, wird bei der Klassifkation basierend auf
den Merkmalen entschieden, zu welcher Klasse das Objekt gehort (Abb. 7.5.4).
Beim Nearest Neighbour Klassifikator wird fur einen konkreten Merkmalsvektor
einfach der Abstand zu den Schwerpunkten aller Merkmalscluster gebildet. Die
Klasse, die den kleinsten Abstand zum vorliegenden Merkmal aufweist, ist dann
das Klassifikationsergebnis.
Abbildung 7.5.4: Grundsatzliches Problem der Klassifikation, hier dargestellt am Beispiel eines
zweidimensionalen Merkmalsvektors (x,y). Basierend auf bekannten Objekten wird ein neues Ob-
jekt anhand des Merkmalsraums einer Klasse (hier blau oder rot) zugeordnet.
Wenn die Klassencluster unterschiedliche Große aufweisen, dann kann der Ab-
stand noch mit der Klassengroße gewichtet werden.
Teilweise macht es Sinn, die bestehenden Merkmale so mathematisch zu kombi-
nieren, dass sich neue Merkmale ergeben. Von diesen Merkmalen reichen dann
wenige, z.B. zwei oder drei, aus, um eine klare Klassentrennung zu erreichen.
Letztlich kann man jedes Objekt als reprasentiert durch einen Merkmalsvektor
im Vektorraum ansehen. Man kann nun (durch Hauptachsentransformation bzw.
Principal Component Analysis (PCA)) ein neues Koordinatensystem schaffen, in-
dem eben wenige Komponenten des Vektors im neuen Koordinatensystem eine
70
eindeutige Trennung erlauben [6].
Dabei ist die Kombination (gegeben durch die PCA) eine lineare Kombination.
Naturlich lassen sich auch nichtlineare Kombinationen nutzen. Dabei kann man
entweder heuristisch vorgehen oder man nutzt Verfahren wie neuronale Netze,
die die extrahierten Merkmale in komplexer (und zunachst unabsehbarer) Weise
kombinieren.
7.6 Neuronale Netze
Ublicherweise nutzen und kombinieren wir kreativ die bisher dargestellten (und
naturlich weitere) Verfahren als Grundelemente um dann letztlich eine bestimmte
Aufgabenstellung zu erfullen.
Einen anderen Ansatz gehen lernende Verfahren, die automatisch eine Verarbei-
tung der Bilder erzielen. Grundsatzlich unterscheidet man dabei zwischen unsu-
pervised und supervised Learning. “Supervised” bedeutet, dass wir dem Com-
puter zusammen mit dem (in der Regel umfangreichen) Trainingsmaterial auch
jeweils das gewunschte Evaluationsergebnis (z.B. Fußganger / kein-Fußganger)
liefern und dieses gewunschte Ergebnis eben gelernt werden soll.
Grundsatzlich sind verschiedene Varianten des Lernens moglich. Z.B. lassen sich
mittels genetischer Algorithmen ahnliche Abfolgen von Standardbildverarbei-
tungsblocken generieren, wie sie auch der Mensch anwenden wurde. Besonders
prominent sind im Bereich der Bildverarbeitung aber aktuell (wieder) neuronale
Netze.
Dies hat mehrere Grunde. Nicht zuletzt ist die Argumentation, dass diese Vorge-
hensweise nicht schlecht sein kann; schließlich ist das menschliche Gehirn uner-
reicht in der Auswertung von Bildern.
Andererseits sollte bedacht werden, dass a) kunstliche neuronale Netze schon sehr
lange bekannt und im Einsatz sind und b) das menschliche Großhirn (ca. 50% wird
direkt oder indirekt fur den Sehsinn verwendet) aus ca. 20 Milliarden Neuronen
mit im Durchschnitt 10.000 Synapsen pro Neuron besteht.
Das Hauptproblem ist aber gar nicht, dass fur ein entsprechend großes neurona-
les Netz eine immense Rechenleistung notwendig ist, sondern dass fur die Opti-
mierung der Synapsen, also das Lernen, ein wirklich irrsinnig und unvorstellbar
71
hoher Rechenbedarf besteht.6
Das Grundprinzip eines neuronalen Netzes ist in Abb. 7.6.5 dargestellt. Die einzel-
nen Prozessorknoten (“Neuronen”) fuhren eine vergleichsweise einfache Summa-
tion der gewichteten (Synapsen) Eingangssignale durch. Die Summe wird nichtli-
near transformiert (Lookup-Tabelle) und das Ergebnis ist der Ausgang des Neu-
rons, der dann wieder andere Neuronen anregt.
Abbildung 7.6.5: Grundprinzip eines neuronalen Netzes. Das grune Neuron N5 summiert zunachst
die einkommenden, gewichteten Signale: S = 4 N1 + 1 N2 + 3 N3. Nachfolgend wir die Summe
durch eine nichtlineare Funktion transformiert und ergibt dann die (neue) Aktivitat von Neuron
5: N5 = NL(S)
Netze bestehend aus mehreren Schichten von Neuronen (Multilayer NN) werden
heutzutage auch “deep NN” genannt (an sich sind sie nichts neues und werden
schon seit dem Beginn der Forschung zu kunstlichen NN genutzt) [9].
Neuronale Netze konnen auf allen Ebenen der Bildverarbeitung eingesetzt wer-
den. Die Eingangsneuronen konnen entweder durch Grauwerte direkt (Pixel) oder
durch konventionelle Bildverarbeitung berechnete Merkmale (einfach oder kom-
plex) gespeist werden. Oft werden neuronale Netze zur Klassifikation basierend
auf einem Merkmalsvektor verwendet.
Beim Lernvorgang geht es letztlich darum, die Gewichte der Neuronen unterein-
ander (Synapsen) so zu optimieren, dass die Trainingsdaten moglichst gut verar-
beitet werden. Hierfur gibt es verschiedene Ansatze, Standard ist der sogenannte
Backpropagation-Algorithmus, eine Art Fehlerruckfuhrung, ruckwarts durch das
Netz [9].6Das menschliche Gehirn ist im Lauf der Jahrmillionen andauernden Evolution erst zu seiner Leistungsfahigkeit
gekommen. Milliarden von Individuen haben letztlich an der Optimierung mitgewirkt.
72
Generell ist der Aufwand fur die Gewichtsoptimierung (Lernen) bei großen Net-
zen extrem hoch und alles andere als trivial.7 Insbesondere ist die richtige Menge
(und Qualitat) an Trainingsdaten, aber auch die Netzstruktur und Große entschei-
dend. Zuviele Daten konnen dabei auch zu einem schlechteren Ergebnis (man-
gelnde Generalisierung) fuhren.
Sogenannte Convolutional Neural Networks nutzen Schichten, die letztlich eine Fal-
tungsberechnung durchfuhren. Damit kann auch die Verarbeitung von Schichten
mit großen Neuronenzahlen (entsprechend vielen Pixeln) mit vergleichsweise we-
nigen Gewichten erfolgen. Daher gibt es weniger Gewichte zu optimieren und der
Lernvorgang ist erheblich effizienter.
Naturlich hat die Faltung ja auch — wie wir gesehen haben — ihre maßgebliche
Bedeutung in der Bildverarbeitung, denn wir wollen sehr oft, dass die Verarbei-
tung verschiebungsinvariant ist (alle Bildbereiche sollen ahnliche verarbeitet wer-
den). Im Gegensatz zu den linearen Filtern (Kapitel 3) haben wir hier aber jeweils
noch eine Nichtlinearitat (Lookup-Tabelle in den Neuronen), die zu komplexerem
Verhalten fuhrt. Nach den Convolutional Layern wird oft eine weitere Nichtli-
nearitat, ein sogenanntes Pooling eingesetzt, bei dem mehrere Neuronenausgange
durch ein Neuron (meist: Maximum der Eingangsneuronen) ersetzt wird.
Im Prinzip kann man Zwischenschichten mit anderen — nicht Neuron-basierten
— Verarbeitungsschritten einfuhren. Und naturlich kann man auch die einfachen
Neuronen durch komplexere Prozessierungsknoten ersetzen.
7Neben Grafikkarten werden vor allem auch auf die Berechnung optimierte Prozessoren eingesetzt.
73
Kapitel 8
ITOM, Python und OpenCV
ITOM ist ein leistungsfahiges OpenSource System zur Steuerung von (optischen)
Messsystemen und Sensoren und zur Auswertung der sich ergebenden Signa-
le. Als Benutzer arbeiten Sie mit ITOM ublicherweise interaktiv und uber selbst
programmierte (Python) Skripte. Der grundsatzliche Workflow ist am besten mit
Matlab oder Labview vergleichbar. ITOM bietet Ihnen vor allem die folgenden
Vorteile:
• Schnelle und einfache Programmierung auch komplexer Messablaufe in Py-
thon, auf Wunsch mit schonen grafischen Benutzeroberflachen1
• effizient und schnell; lauffahig auch auf alteren Laptops
• Ansteuerung verschiedener Hardware, insbesondere Kameras, Motorsteue-
rungen, AD-Wandler.
• Programmierung auch in C++ moglich.
ITOM wird am Institut fur Technische Optik seit mehreren Jahren praktisch aus-
schließlich fur die vielen realisierten Messsysteme und Experimentalaufbauten
verwendet. ITOM lasst sich aber auch fur professionelle/kommerzielle Systeme
ohne Lizenzkosten nutzen.
Fur die Verarbeitung von Bildern stehen in ITOM viele verschiedene Varianten
zur Verfugung:
• ITOM-interne Filter fur gangige Algorithmen
1Hierzu nutzt ITOM das Framework Qt und dementsprechend lassen sich die Oberflachen sehr schnell und rein
grafisch designen.
74
• numpy/scipy basierte Filter:
• OpenCV
• skimage
• PIL
• Dutzende relevanter frei-verfugbarer Python Bibliotheken, z.B. fur machine
learning, Klassifikation, neuronale Netze
Bei gebrauchlichen Filtern stehen Sie also vor der Qual der Wahl, welche Imple-
mentierung sie denn nun verwenden wollen; dies hat durchaus Auswirkung, weil
nicht zwangslaufig alle Datenformate zueinander passen.
In jedem Fall macht es die Sache fur Sie zunachst unubersichtlich, denn uber alle
Pakete betrachtet haben Sie es mit einem Fundus von tausenden eventuell rele-
vanter Funktionen zu tun. Diese Vielfalt ist schon, aber kommt nicht umsonst.
Neben der Komplexitat bei der Auswahl mussen die verschiedenen Versionen zu-
sammenpassen.
In einem kommerziellen System wie Matlab oder Labview bestehen solche
zusatzlichen Verkomplizierungen nicht. Allerdings mussen Sie bei diesen Syste-
men mit der Abhangigkeit vom Hersteller, der nicht-vorhandenen Einsicht in die
Details der Algorithmik, Kosten und der schlechteren Erweiterbarkeit leben.
Im folgenden gehe ich davon aus, dass Sie ITOM bereits installiert haben (hier-
zu gibt es im Anhang eine Anleitung fur Linux (Debian-basiert, z.B. Ubuntu) und
Windows). Ich werde Ihnen also einen kleinen schnellen Einstieg in ITOM und die
Programmierung in Python geben. Viele Aspekte (z.B. die Generierung eigener
grafischer Oberflachen) werden wir dabei aussparen und im Zweifelsfall sollten
Sie die ausfuhrliche ITOM Dokumentation bzw. die Dokumentationen von num-
py und scipy konsultieren.
8.1 Benutzeroberflache und generelles Konzept
Das Hauptfenster von ITOM unterteilt sich normalerweise in die in Abb. 8.1.1
dargestellten Hauptbereiche. Die verschiedenen Bereiche lassen sich ausblenden
und beliebig zueinander verschieben. Wichtig ist auch, dass Teilfenster jeweils ans
75
Hauptfenster andockbar sind. D.h. ein 2D Plot, wie er z.B. in Abb. 8.1.1 dargestellt
ist, kann eben auch angedockt werden, so dass er immer sichtbar bleibt (bis er
wieder ausgedockt wird). Gleiches gilt fur die Skripte, von denen man oft (im
Sinne einer Modularisierung) gleichzeitig an mehreren arbeitet.
Abbildung 8.1.1: Hauptfenster von ITOM mit den wesentlichen Teilbereichen.
Ublicherweise schreibt man also im Editierbereich/fenster des aktuellen Skripts
das Programm. Startet dann das Programm (F5 oder passendes Icon klicken) und
schaut sich Ergebnisse numerisch oder grafisch an.
Fur die Bildverarbeitung ist naturlich die Darstellung von Bildern von zentraler
Bedeutung. Machen Sie sich daher mit der Bedienung vertraut. Mittels
plot(bildchen)
rufen wir den Viewer fur das Bild “bildchen” auf. Auch Livebilder einer Kame-
ra nutzen diesen Viewer. In Abb. 8.1.2 sind die fur die Bildanalyse wichtigsten
Buttons zur Bedienung beschrieben.
Bei einem Klick mit der rechten Maustaste auf eine Variable konnen wir auch di-
rekt den Viewer fur diese Variable aufrufen. Zum interaktiven Zugriff auf eine
Kamera nutzen wir das Pluginfenster (rechts unten im Hauptfenster) und wahlen
(Klick auf die kleinen Dreiecke) den Bereich “Grabber” im Hauptbereich “Da-
76
Abbildung 8.1.2: 2D Darstellung von Datenobjekten (typischerweise Bilder) mit dem Plot-Befehl.
taIO”. Sie sehen nun die auf Ihrem System installierten Kameras bzw. Framegrab-
ber. Ein Rechtsklick auf eine der Auswahlmoglichkeiten (z.B. Ximea) lasst Sie eine
neue “Instanz” dieser Kameraklasse generieren. Es folgt dann eine Auswahlbox,
bei der Sie erforderliche und optionale Parameter eingeben konnen und generie-
ren so letztlich die Kamerainstanz.
Auf diese Kamerainstanz konnen Sie nun wieder mittels Rechtsklick zugreifen
und haben So nun die Moglichkeit entweder Parameter der Kamera einzustellen
(z.B. Belichtungszeit) oder auch den Live-Viewer fur die Kamera zu starten.
Mittels
help(funktion) bzw. help(objekt)
bekommen wir jeweils Hilfe zu dem angegebenen Objekt in ITOM. Naturlich kann
man auch das grafische Hilfesystem verwenden. Hilfen zu den Plugins und Filtern
bekommt man ganz entsprechend durch
pluginHelp( ... )
filterHelp( ... )
77
8.2 Python
ITOM wird ublicherweise in Python programmiert. Python ist eine skriptbasier-
te Sprache, die Ihnen viele Vorteile bietet. Python ist extrem weit verbreitet und
einfach zu lernen und es gibt hunderte2 von nutzlichen Bibliotheken zu nahezu
allen Bereichen der Softwareentwicklung, von der Spieleprogrammierung bis zur
Finanzbuchhaltung.
Python kann sowohl prozedural wie auch objektorientiert genutzt werden und
aufgrund der riesigen (OpenSource-)Community gibt es im Netz praktisch zu je-
dem Problem Rat (und naturlich auch viele Bucher und Tutorials).
Es ist eine typische Interpretersprache, d.h. das Programm wird nicht einmalig
in Maschinencode ubersetzt (compiliert) sondern bei jeder Befehlseingabe (bzw.
beim Ablauf eines Programms bei jedem Programmschritt) wird jeder Befehl neu
“interpretiert” und ausgefuhrt.
Dies kostet Performance, hat aber auch Vorteile. Insbesondere kann man in die-
sem Workflow etwas schneller als mittels Compilersprachen eine Funktionalitat
implementieren. Beim Debuggen ergeben sich weitere Vorteile und es kann auf
eine strenge Typisierung verzichtet werden.
Dennoch: Eigentlich mag ich — trotz der vielen Vorteile — Python nicht. Das
muss Sie nicht weiter irritieren. Ich will nur kurz die sich aus dem Konzept eben
zwangslaufig ergebenden Nachteile zumindest nennen. Die fehlende Typisierung
kann auch als Nachteil gesehen werden, denn eine automatische Erkennung von
Fehlern (zur Compilierzeit bei Compilersprachen!) entfallt, so dass ein Fehler erst
dann entdeckt wird, wenn das Programm an die entsprechende Stelle im Co-
de kommt. Eine Vorabprufung ist schwieriger (bzw. nur bedingt moglich) und
unublicher als bei Compilersprachen.
Die mangelnde Effizienz fuhrt zwangslaufig zu einem anderen Programmierstil,
insbesondere bei der Bildverarbeitung. Wahrend man in C oder C++ viel mit Itera-
tionen (for-loops) arbeitet, um die Millionen Pixel eines Bildes zu bearbeiten ware
diese Vorgehensweise in einer Interpretersprache wie Matlab oder eben Python zu
langsam. Stattdessen versucht man vorhandene Bibliotheksfunktionen (die z.B. in
C++ programmiert wurden) aufzurufen. Sowohl Python (mit dem ublicherweise
verwendeten Paket numpy (“numeric python”)) wie auch Matlab nutzen Arrays
2vermutlich sogar tausende
78
bzw. Matrizen zur effizienten Berechnung. Ganz allgemein wird das Problem da-
her am besten “vektorisiert”.
Wie sie im vorderen Teil des Buches gesehen haben ist die lineare Algebra Basis
und Grundtheorie der Bildverarbeitung und so gesehen klappt das mit der Vekto-
risierung sehr gut. Aber leider reicht die lineare Algebra fur viele Probleme dann
eben doch nicht so recht und dann wird es ohne einen direkten (und schnellen)
Zugriff auf jeden einzelnen Pixel dann muhsam. Der Code wird dann entweder
langsam oder unleserlich.
Je großer und komplexer das Problem, desto mehr Probleme handelt man sich
ganz generell mit einer Interpretersprache in der Bildverarbeitung ein. Letztlich
mussen dann einzelne Funktionen doch in C oder C++ implementiert und dann
von Python aufgerufen werden. Das ist naturlich auch der Weg, den die Bibliothe-
ken, wie eben OpenCV, gehen.
Zusammengefasst: Freuen Sie sich an den vielen Vorteilen von Python, aber be-
halten Sie die (prinzipbedingten) Nachteile in Erinnerung.
8.2.1 Die Basics
Dies wird kein Programmierkurs. Ich gehe davon aus, dass Sie in irgendeiner
Form schon programmiert haben und das Denken in Algorithmen nichts Neu-
es fur Sie ist. Wir werden uns hier an die prozedurale Programmierung halten.
Wenn Sie einen objektorientierten Programmierstil kennen, ist der Ubergang zu
objektorientiertem Python trivial.3
Wir werden auch wirklich nur das Notigste betrachten. Es wird immer behauptet,
dass Python, sehr gut lesbar sei. Dem kann ich nicht zustimmen und ein Grund
hierfur ist, dass Python durchaus eine nicht zu unterschatzende Komplexitat auf-
weist (das ist kein Nachteil) und dementsprechend zunachst unerwartete Dinge
passieren konnen und auch zunachst wenig einsichtige oder offensichtliche Kon-
strukte moglich sind. Wer hier sinnvoll einsteigen will, der muss wohl oder ubel
ein entsprechend ausfuhrliches Pythonbuch zur Hand nehmen. Und vor allem
uben.
Python ist wie gesagt nicht “dynamisch typisiert”, d.h. Sie mussen nicht festlegen,
welchen Typ eine Variable hat und der Typ kann sich sogar zur Laufzeit ohne
3Was nicht trivial ist, ist das korrekte objektorientierte Softwaredesign.
79
Probleme andern. Sie konnen also folgenden Code schreiben:
a = 18 + 27*3 # a ist ein Integer (Ganzzahl)
a = ’test’ # und jetzt ist a ein String (Zeichenkette)
a = 18.3 * 4.5 # ... und nun eine Fließkommazahl
Kommentare werden offensichtlich durch ’#’ eingeleitet.
Auch um das Anlegen und Freigeben von Variablen bzw. Speicherbereichen
mussen Sie sich nicht kummern. Der Garbage Collector von Python nimmt Ihnen
(auf Kosten der Effizienz) diesen fehlertrachtigen Aspekt ab.
Es braucht nicht viele Kontrollstrukturen, um Python zu sagen was man will.
for-loops
for i in range(100):
print(i)
i durchlauft hier die Zahlen von 0 bis 99.
Der for-loop kann ganz generell uber Mengen und Listen iterieren, z.B. eine Liste
von Filenamen des aktuellen Directorys.
Die “range” Anweisung ist dabei ein besonderes Hilfsmittel, das — auch ohne,
dass explizit eine evtl. sehr lange Liste erzeugt werden musste, die Iteration uber
einen Zahlenbereich ermoglicht:
for i in range(-6,6,2):
print(i)
iteriert z.B. i von -6 bis einschließlich 4 (das letzte Element ist immer ausgenom-
men !) in 2er Schritten.
Mittels “break” kann man Schleifen vorzeitig verlassen und mittels “continue”
kann man vorzeitig den nachsten Schleifendurchgang starten.
Ganz wesentlich bei der Arbeit mit Python ist, dass die Einruckung im Code defi-
niert, zu welchem Block etwas gehort (eine Pythoneigenheit, die leicht zu Fehlern
fuhrt und die ich hasse). Es ist also absolut wichtig, das print(i) entsprechend ein-
geruckt zu haben.
80
while-loops
i = 0
while(i < 100):
print(i)
i = i+1
mit demselben Ergebnis wie beim for-loop.
if-then-else
Naturlich will man im Programm in Abhangigkeit von Daten im Programm den
Ablauf steuern konnnen. Hierzu dient im einfachen Fall
if( a == 3):
machedies(a)
oder wenn es eher um “wenn-dann-wenn-nicht-dann-das” geht
if( a == 3):
machedies(a)
else:
wasanderesmachen(a,7)
Mehr Blocke (analog einer switch Anweisung in C) konnen mittels “elif ...” ein-
gefugt werden.
Funktionen
Wir haben hier schon eine Funktion mit dem Namen “machedies” aufgerufen.
Funktionen werden mittels
def machedies(a):
b = a*3
return b
81
definiert.
Im Sinne einer strukturierten, ubersichtlichen und damit letztlich gut zu warten-
den Programms sollten Sie sich angewohnen, Ihr Gesamtprogramm in sinnvolle
Teilfunktionen zu zerlegen (und sie auch — genau wie die Ubergabeparameter,
aber auch die Variablen — sinnvoll und aussagekraftig zu benennen).
Eine lustige (und sehr sinnvolle) Besonderheit von Python ist, dass man bei Zu-
weisungen gleich mehrere Zuweisungen auf einmal durchfuhren kann, also z.B.
a,b,c = 17, 23.3, "test"
Dementsprechend kann eine Funktion auch mehrere Werte per Return auf einen
Schlag zuruck geben. Und Sie konnen das Konstrukt auch nutzen, um zwei Varia-
blen zu “swapen”.
Variablen des Hauptprogramms konnen auch in Funktionen gelesen, aber nor-
malerweise nicht verandert werden. Eine Anderung wird moglich, wenn in der
Funktion selbst die Variable als “global” definiert wird, also z.B.
def test():
global a
a = a + 1
Funktionsargumente konnen optional sein und dann mit einem Namen versehen
werden, also kann die folgende Funktion
def test(a, anzahl=20, beta=0.3):
...
dann z.B. mittels
test(7)
test(7, anzahl=10, beta=0.3)
test(7, beta=5)
aufgerufen werden.
82
Datentypen
Als Datentypen stehen uns neben den sehr elementaren Ganz- und Fließkomma-
zahlen sowie den Strings4 und den boolschen Variablen True und False vor allem
noch Listen, Mengen und Dictionaries zur Verfugung.
Komplexe Zahlen schreiben wir als
komplexeZahl = 3 + 4j
Typumwandlungen von Zahlen werden auf folgende Weise erzwungen:
a = 3
fliesskommazahl = float(a)
integerzahl = int(fliesskommazahl)
zahlAlsString = str(a)
Listen werden in eckigen Klammern definiert, man kann aber auch andere “Se-
quenzen”, in eine Liste umformen
liste = [1,3,9,2]
liste2 = list(wasanderes)
Mittels “reverse” kann man die Reihenfolge umdrehen, “min” und “max” liefern
das Minimum und Maximum.5
Arrays, sind zunachst auch einfach nur Listen. im Rahmen von numpy werden
wir es aber mit einer anderen, erweiterten Art von Arrays zu tun haben (siehe
Abschnitt 8.2.3).
Eine angenehme Besonderheit ist, dass wir — wie in Matlab — Teile einer Liste
mittels sogenanntem “slicing” extrahieren konnen:
a= [1,3,9,2,7,8]
a[1:3] -> [3,9]
del a[1:3] -> [1,2,7,8]
4Strings konnen sowohl mit einfachen Anfuhrungszeichen ’ oder mit doppelten geschrieben werden. Verwirrend
ist, dass Strings in Python nicht veranderbare Objekte sind.5Die Funktionen max und min sind auch in Numpy fur Arrays definiert.
83
Mittels sogenannter list comprehension konnen wir aus einer Liste durch eine Be-
rechnungsvorschrift eine neue Liste generieren:
a= [i*2 for i in [1,3,9,2,7,8]]
b= [i*2 for i in range(10)]
c= [i*2 for i in range(100) if i%10 == 0]
liefert eben fur a und b eine Liste, die dem doppelten der ersten Liste bzw. der
Zahlen von 0 bis 9 entspricht. c ergibt die Liste [0, 20, 40, 60, 80, 100, 120, 140, 160,
180].
Naturlich steht — wie auch fur die anderen Datentypen — ein ganzer Strauss an
Funktionen vom Sortieren bis zum Suchen in der Liste zur Verfugung.
Dictionaries und Sets (Mengen) sind fur uns zunachst nicht sonderlich
uberlebenswichtig und daher spare ich es mir, sie hier naher darzustellen.
Shallow und Deep Copies
Leider wird es hier aus meiner Sicht im Vergleich zu verschiedenen anderen Pro-
grammiersprachen wieder unubersichtlich.
Zunachst: Der einfache Befehl “a = a + 1” generiert erst ein neues Objekt a und
weist diesem auch einen neuen Speicherbereich zu. Das ist ineffizient aber konsi-
stent, einfach und nicht fehleranfallig. Wenn man das aber bei großeren Datenty-
pen wie Arrays oder Bildern standardmaßig so machen wurde, dann wurde die
Effizienz doch sehr leiden.
Dementsprechend fuhrt der Befehl
liste2 = liste1
dazu, dass nicht die gesamte Liste samt Inhalt kopiert wird, sondern eigentlich
nur ein neuer Name fur die Liste vergeben wird. Es ist also nach wie vor nur ein
Speicherbereich fur die Liste reserviert. Wenn man Elemente von liste2 andert,
dann andern sich automatisch auch die Elemente in liste1.
Das ist manchmal gut, manchmal aber auch gar nicht das, was man will. In letz-
terem Fall muss man die angesprochene “deep copy” der Liste durchfuhren. Und
das geht mittels (hab ich schon erwahnt, dass ich Python nicht mag?):
84
liste2 = liste1[:]
Wenn das Objekt allerdings verschachtelt wieder aus Listen besteht, dann klappt
das Ganze so leider wieder nicht so recht (mag irgendjemand das?). Will man
wirklich eine komplette Hierarchie “tief kopieren”, dann nutzt man die Funktion
deepcopy() aus dem Paket “copy” und ist dann auf der sicheren Seite.
import copy
a = [[1,2],[3,4]]
b = copy.deepcopy(a)
Auf die Besonderheiten bei Bildern bzw. Arrays werden wir in Abschnitt 8.3 noch
eingehen.
8.2.2 Module und Pakete
Code-Bibliotheken — egal ob eigene oder fremde — konnen wir verwenden, so-
bald wir sie importieren. Der Import Mechanismus ist zunachst etwas verwirrend
(wie einiges in Python), denn es gibt verschiedene Arten, den Import einzuleiten:
import numpy
a = numpy.sqrt(3)
import numpy as np
a = np.sqrt(3)
from numpy import *
a = sqrt(3)
Drei (scheinbar) unterschiedliche Varianten, wobei sich die ersten beiden
im Prinzip nicht wirklich unterscheiden und gegenuber der dritten Variante
ublicherweise zu bevorzugen sind.
Beide nutzen das Konzept der Namespaces. Wenn viele verschiedene Bibliotheken
gleichzeitig in einem Projekt verwendet werden, sind die Chancen hoch, dass da-
bei teilweise dieselben Funktionsnamen verwendet werden. So kann Bibliothek
A z.B. eine Funktion “ausgabe()” definieren. Die Probleme beginnen, wenn ich
85
gleichzeitig auch noch Bibliothek B benutzen will, diese aber ebenfalls eine Funk-
tion “ausgabe()” definiert hat. Woher soll der Computer nun wissen, welche Funk-
tion ich eigentlich ausfuhren will, wenn ich “ausgabe()” aufrufe?
Dieses Problem losen (in vielen Programmiersprachen) eben die Namespaces. Der
Standard-Namespace ist in Python einfach der Paketname. Der vollstandige Funk-
tionsname fur die sqrt-Funktion in obigem Beispiel ist dann eben numpy.sqrt.
Wem das zu lang ist6, der wahlt einen kurzeren Alias, mit dem stattdessen auf
den Namespace zugreifen kann. In unserem Beispiel oben ware das “np” und
dementsprechend wird hinter die import Anweisung noch “as np” gehangt.
Wenn man will, dass die Funktionen gar nicht in einem eigenen Namespace lan-
den (mit all den oben beschriebenen potentiellen Nachteilen!), dann kann man die
dritte der obigen import Varianten wahlen. Die in der Bibliothek definierten Funk-
tionen landen dann im globalen Namespace und dementsprechend kann man den
Funktionsnamen direkt, also z.B. sqrt, verwenden.
Jedes File mit Python Definitionen kann als Modul genutzt werden. Wenn
zusatzlich dort eine Funktion init definiert wird, dann spricht man von einem
Paket. Und wie der Name schon sagt wird genau diese init Funktionen beim
import des Moduls bzw. Pakets dann auch aufgerufen.
8.2.3 Numpy
Fur (monochrome) Bilder brauchen wir zweidimensionale Arrays und in Py-
thon bedeutet das standardmaßig eben eine Liste von Listen und eine Reihe von
brauchbaren Methoden stehen ja zur Manipulation von Listen zur Verfugung.
Aber diese Listen sind ganz allgemeine Listen und wir haben es hier mit (großen)
Bildern zu tun und dementsprechend nutzen verschiedene Bibliotheken zur Ver-
arbeitung der Bilder ein anderes, abgewandeltes oder aber erweitertes Arrayob-
jekt. Das macht die Sache fur Sie komplizierter — und zwar umso mehr, je mehr
verschiedene Bibliotheken Sie nutzen.
Fur numerische Fragestellungen und die Arbeit mit Matrizen und Vektoren ist das
Paket numpy der absolute Standard in Python und OpenCV nutzt intern numpy
Arrays zur Reprasentation der Bildern.
6und das wird vor allem bei verschachtelten Paketen in Unterordnern schnell zu lang
86
Im typischen Anwendungsfall haben Sie es also mit Arrays
• von Python,
• von OpenCV/numpy und
• — falls Sie ITOM nutzen — von ITOM (sogenanntes dataObject)
zu tun.
Zum Gluck konnen Python Arrays einfach nach numpy Arrays gewandelt wer-
den und im Prinzip sollten Sie fur numerische Fragen einfach weitestgehend auf
numpy Arrays setzen.7
import numpy as np
m=np.array((3,3)) # ein float Array mit 3 x 3 Elementen
m=np.zeros ....
m=np.array(pythonarray) # konvertiert Python Array nach numpy
m = array([0, 1, 2], dtype=uint8) # Typ 8Bit unsigned
m2 = m.astype(float) # konvertiere nach float
Als Typen sind fur uns meist wesentlich: uint8, uint16, int16, int32, float16, float32,
float64, complex64. Viele Funktionen erlauben ein (meist optionales) Argument
dtype=uint16 (oder anderer Typ), um explizit den Typ anzugeben.
Beachten Sie: In numpy gibt es auch noch “Matrizen” (matrices), die ein we-
nig anders als die numpy Arrays, sogenannte ndarrays, sind. Wir verwenden
ublicherweise Arrays und keine Matrizen.
Der Typ und die Große konnen sowohl fur numpy Arrays, OpenCV Bilder wie
auch ITOM Datenobjekte mittels
m.dtype # liefert den Typ
m.dims # liefert die Dimensionen (typ. 2)
hoehe, breite = m.shape # Beispiel fur Farbbild
hoehe, breite, kanaele = m.shape # Beispiel fur Farbbild
7auch wenn Sie bei diesen die Große nicht nachtraglich andern konnen und alle Elemente denselben Typ haben
mussen.
87
ermittelt werden. Es fallt auf, dass — wie in der Matrizenmathematik ublich —
bei 2D Arrays die vertikale Dimension (y) zuerst gelistet wird8. Eine zunachst ver-
wirrende Tatsache (weil wir kartesische Koordinatensysteme bei grafischen Aus-
gaben gewohnt sind), die aber auch beim Zugriff auf Elemente beachtet werden
muss!
Zugriff auf einzelne Elemente erfolgt mittels
a = m[7][13]
a = m[7,13] # Beachte: x=13, y=7 bei kartesisch!!!
a = m[2:4,3:10] # Bereich 2..3 / 3..9
a = m[2:4,:] # ist dasselbe wie
a = m[2:4]
a = m[2:20:3] # in Dreierschritten Bereich 2..19
Diese y-vor-x Definition ist damit zwangslaufig auch bei OpenCV und damit dann
auch bei dem ITOM dataObject gegeben und zu beachten!
Man kann aber auch komplexere Dinge unternehmen:
m[m<0.1]=0 # setzt alle Elemente < 0.1 auf 0
np.sqrt(m) # berechnet fur alle Elemente die Wurzel
und naturlich sind allerlei typische Funktionalitaten der linearen Algebra moglich,
u.a. inverse Matrizen, Losen von uberbestimmten Gleichungssystemen mittels SV-
Zerlegung.
Ein paar weitere wichtige Funktionen:
a = m.flatten() # macht aus Array einen Vektor
a.shape = (6,4) # macht 6 x 4 Array aus Vektor
np.savetxt("coeffs.txt", tobesaved)
daten = np.loadtxt("coeffs.txt")
# Bilder horizontal od. vertikal zusammenfassen
bild3 = np.concatenate((bild1, bild2),axis=1)
a,b,c = np.hsplit(bild, 3) # Bild horizontal in 3 Bilder teilen
a,b,c = np.vsplit(bild, 3) # Bild vertikal in 3 Bilder teilen
8sogenanntes row-major ordering
88
realteil = bild.real
imagteil = bild.imag
maximum = np.max(bild) # Maximum
maximum = np.min(bild) # Minimum
maximum = np.mean(bild) # Mittelwert
maximum = np.median(bild) # Median
maximum = np.std(bild) # Standardableitung
sortiert = np.msort(a) # a sortieren
diffquot = np.diff(a) # Differenz a[i+1]-a[i]
bild2 = bild.clip(100,200) # Clipping: Werte<100 -> 100 und
# Werte > 200 = 200
bild2 = np.mod(bild, 30) # Modulo
a[np.isnan(a)]=5 # Not-a-Numbers auf 5 setzen
Einfache Faltungen (vor allem fur eindimensionale Funktionen) konnen auch di-
rekt in numpy durchgefuhrt werden (np.convolve). Nutzlich ist auch die Vekto-
risierfunktion, die es erlaubt, einfache Funktionen auf jedes Pixel eines Bildes an-
zuwenden. Dies ist fur die Bildverarbeitung oft wichtig, denn eine Prozessierung
der Pixel mittels konventioneller for-Schleife ist in einer Interpretersprache wie
Python extrem ineffizient.
Die Funktionen, die mit vectorize genutzt werden, mussen dabei nicht als
Lambda-Expressions geschrieben werden, sondern konnen auch “normale” (uber
def definierte) Funktionen sein.9
test = lambda v: (v/255) ** 2 * 255
func = np.vectorize(test)
bild2 = func(bild)
Und noch ein paar weitere Funktionen, die wir nicht direkt fur die Bildverarbei-
tung brauchen, die aber nutzlich sein konnen:
• Funktionen, um Polynome zu berechnen (polyval), Polynome zu fitten (poly-
fit), abzuleiten (polyder) und zu losen (roots)
9Ganz einfache arithmetische Zusammenhange (wie den hier dargestellten) konnen Sie unter Umstanden auch
einfach als arithmetischen Ausdruck schreiben. Numpy vektorisiert dann vollstandig automatisch.
89
• hanning(), kaiser(), hamming(), um Daten (meist vor einer FFT) mit einer
Cosinus-Funktion zu gewichten.
• Typische Funktionen der linearen Algebra (von der Matrixmultiplikation
uber Eigenvektoren bis zur SV-Zerlegung)
• Spezialfunktionen, z.B. sinc, bessel
Und wenn wir scipy (in ITOM ublicherweise enthalten) mitnutzen haben wir auch
• Speichern/Laden im Matlab/Octave Format
• Kubische B-Spline Interpolation
• Least-Squares Optimierung
• Numerische Integration
• Support fur Audio files
• Einige weitere Bildverarbeitungsfunktionen im Paket scipy.ndimage
8.3 ITOM Datenobjekte und OpenCV
ITOM Datenobjekte sind letztlich Matrizen beliebiger Dimension. Bei mehr als
zwei Dimensionen entsprechen die letzten beiden Dimensionen einem zweidi-
mensionalen Bild.
Die eigentlichen Bilddaten werden intern in derselben Datenstruktur wie fur
OpenCV bzw. numpy abgespeichert, so dass eine Konversion vom dataObject zu
OpenCV und zuruck sehr einfach und effizient realisiert werden kann.
bildDataObject=dataObject(bildOpenCV) # OpenCV ->dataObject (deep)
bildOpenCV=np.array(bildDataObject) # dataObject ->OpenCV (deep)
bildOpenCV=np.array(bildDataObject, copy=False)
# dataObject ->OpenCV shallow
a=dataObject() # leeres Datenobjekt
a=dataObject([1280,1028], "uint8") # ein typ. Bild
a=dataObject([3,1280,1028], "uint8") # ein typ. FarbBild
a=dataObject.ones([1280,1028], "uint8") # ein Bild gefullt mit 1
90
Teilweise erfolgt die Konversion auch ganz implizit ohne Ihr Zutun oder ist gar
nicht notwendig, z.B. wenn Sie plot(bildchenAusOpenCV) aufrufen.
Wir kommen nun zu dem wichtigen Thema in-place kontra out-of-place. Wenn
ein Algorithmus direkt auf dem Eingangsbild arbeitet, dieses manipuliert und das
Eingangsbild mit dem Ergebnisbild uberschreibt, dann nennt man das “in-place”.
Ein “out-of-place” Algorithmus erhalt dagegen das Eingangsbild.
Klar ist, fur einfache Algorithmen (z.B. eine Binarisierung) ist die in-place Variante
schneller und platzsparender (wenn das Eingangsbild nicht mehr benotigt wird).
Andererseits ist es oft gar nicht so einfach, einen Algorithmus in-place fahig zu
machen (ohne intern dann doch noch temporaren Speicher fur ein Zusatzbild ge-
nerieren zu mussen).
Was wann also wie effizient ist, ist kompliziert und Plattform abhangig (Speicher
kontra Rechenzeit, Cache-misses etc.). Wichtig fur Sie zu wissen ist aber in jedem
Fall, ob nun das Eingangsbild mit dem Ergebnis uberschrieben ist oder nicht.
Bei den direkten Manipulationen der Bilder (z.B. a[a < 128] = 0) ist das naturlich
der Fall. Die Standard OpenCV Aufrufe in Python sind out-of-place, so dass ihr
Eingangsbild erhalten bleibt.10
Und damit mussen wir uns nochmal mit shallow und deep copies befassen. Ein
Aufruf (egal ob fur numpy, OpenCV oder dataObject) Bilder der Form
bild2 = bild1
ist eine shallow Copy, d.h. es wird nur ein zusatzlicher Verweis auf bild1 generiert.
Keine echten Pixeldaten werden dabei kopiert und wenn wir bild1 andern, dann
wird sich auch automatisch bild2 andern.
Wenn wir eine wirkliche (deep) Kopie herstellen wollen (das kostet Zeit und Spei-
cher!), dann mussen wir das folgendermaßen anfordern:
bild2 = bild1.copy() # funktioniert fur openCV und dataObject
Wahrend der Copy Constructor (also z.B. b = array(a) ) fur numpy normalerweise
eine deep Kopie erzeugt, versucht (aber schafft nicht immer) die dataObject Vari-
ante eine shallow Kopie zu erzeugen.
10Beim Wechsel zur C++ Variante von OpenCV oder der Verwendung anderer BV-Bibliotheken muss man diese
Moglichkeiten bedenken.
91
Hinsichtlich der Arithmetik gibt es einen wichtigen Unterschied zwischen ITOM
Datenobjekten und numpy Arrays. a * b fuhrt im Fall von numpy Arrays zu ei-
ner elementweisen Multiplikation und im Falle von ITOM Datenobjekten zu einer
Matrixmultiplikation. Die elementweise Multiplikation oder Division wird dort
mittels a.mul(b) bzw. a.div(b) durchgefuhrt.
Eine Typkonversion wird durch
bild2 = bild1.astype(’uint8’)
angefordert. squeeze() eliminiert eine Dimension der Große 1.
8.4 Zugriff auf die Hardware (Kamera)
Fur die Bildverarbeitung ist naturlich zunachst das Wichtigste, von einer ange-
schlossenen Kamera Bilder zu holen. Hardware (und auch viele Algorithmen)
werden in ITOM uber ein Plugin-System angesprochen. ITOM besteht also aus
einer Kernfunktionalitat, dem eigentlichen QITOM und Plugins, die ITOM um
Funktionalitaten erweitern.
Jede Kamera hat entsprechend ein eigenes Kamera oder Framegrabber-Plugin.11
Egal, ob es sich nun um eine Kamera oder einen Schrittmotor oder was auch im-
mer handelt: Zunachst wird ein entsprechendes Objekt fur die Hardware angelegt.
Hierzu dient die Funktion “dataIO”:
kamera = dataIO("OpenCVGrabber")
In diesem Fall wird die (hoffentlich vorhandene) Webcam ihres Computers ange-
sprochen.12
Nun kann man bei einer Kamera naturlich (je nach Modell) verschiedene Parame-
ter, wie z.B. die Belichtungszeit, setzen. Die moglichen Parameter unserer konkre-
ten Kamera konnen wir mittels
print(kamera.getParamList())
11So ganz stimmt das nicht, denn oft kann uber dieselbe Funktionalitat bei einem Hersteller eine große Klasse an
Kameras angesprochen werden.12Wenn gar keine Kamera angeschlossen ist, konnen Sie auch kamera=camera = dataIO(”DummyGrabber”, 1024,
800) verwenden.
92
erfragen.
ITOM liefert uns hier
[’bpp’, ’brightness’, ’color_mode’, ’contrast’, ’dump_grabs’,
’exposure’, ’gain’, ’gamma’, ’hue’, ’name’, ’native_parameter’,
’roi’, ’saturation’, ’sharpness’, ’sizex’, ’sizey’]
zuruck. Und wenn wir nun beschließen, dass wir z.B. die Belichtungszeit (expos-
ure) andern wollen, dann geben wir hierzu
kamera.setParam("exposure",10)
ein.
Dumm, an diesem Beispiel ist, dass es mit der Webcam nicht funktioniert. Bzw. am
Bild wird sich nichts andern, denn der OpenCV Treiber kann leider nicht wirklich
die Belichtungszeit der Webcam andern. Bei einer “richtigen” Kamera wurde das
Beispiel aber eben in der Tat tatsachlich die Belichtungszeit setzen.
Entsprechend gibt es auch eine Funktion “getParam” mit der der aktuelle Wert
eines Parameters ausgelesen werden kann.
Nun haben wir ein Kameraobjekt, aber damit die Kamera wirklich ein Bild auf-
nimmt, muss sie zunachst gestartet werden. Dann (nach dem Start) kann ein Bild
geholt und in einem Datenobjekt gespeichert und schließlich angezeigt werden.
Der Ablauf ist also
kamera.startDevice()
kamera.acquire()
bild = dataObject()
kamera.getData(bild)
plot(bild)
kamera.stopDevice()
Ein Livebild kann mit “liveImage(kamera)” aufgerufen werden.
Eine unubersichtliche (aber wichtige) Sache in der Arbeit mit Bildern unter Py-
thon sind die bereits angesprochenen shallow copies kontra deep copies. wenn wir
oben mittels “getData(bild)” ein Bild geholt haben, dann bedeutet das, dass wir ei-
gentlich nur einen Verweis auf das Bild geholt haben. Wenn also irgendwer etwas
93
am Bild andert, dann wirkt sich das auf unser Ergebnisbild aus. Eine echte Ko-
pie der Daten nennt man “deep copy” und wird konnen sie erzwingen indem wir
entweder von Anfang an statt getData(bild) den Befehl “kamera.copyVal(bild)”
verwenden oder aber nachtraglich mit dem Befehl “copy” aus der shallow Kopie
eine Deep Kopie machen: “bildEchteKopie = bild.copy()”
Ganz am Ende unserer Arbeit mit der Kamera kann (und sollte) das Kameraobjekt
geloscht werden:
del kamera
Wir basteln das ganze nun in eine Funktion, die wir dann aufrufen konnen:
def holeBild():
kamera = dataIO("OpenCVGrabber")
kamera.startDevice()
kamera.acquire()
bild = dataObject()
kamera.getVal(bild)
kamera.stopDevice()
del kamera
return bild
a = holeBild()
plot(a)
8.5 Und dann: auf OpenCV
Einige Filter, auch zum Teil OpenCV-basierte Filter, sind in ITOM bereits uber
den Plugin Mechanismus implementiert und werden dann z.B. mittels “fil-
ter(“sobelOpt”,sourceImage, destImage, dir)” aufgerufen. Aber nur ein kleiner
Teil der OpenCV und scipy Filter kann so angesprochen werden.
Wenn wir OpenCV im allgemeinen nutzen wollen, dann verwenden wir das Pa-
ket cv2.13 D.h. an den Beginn unseres Scripts schreiben wir “import cv2”. Aber
13Auch OpenCV in der Version 3.x (wie wir es hier verwenden) wird als Python Paket cv2 genannt.
94
zusatzlich mussen wir das dataObject in ein numpy Array konvertieren.14
Wenn wir z.B. den Laplace Filter in OpenCV anwenden wollen, dann mussen wir
schreiben:
import cv2
...
a = holeBild()
a = dataObject.toGray(a) # Farbe -> Graustufen
b = np.array(a) # Numpy Array daraus machen
b = cv2.Laplacian(b, cv2.CV_16S, ksize = 7) # jetzt der Filter
8.6 ITOM-eigene Funktionalitaten
Ganz generell bietet ITOM einige Filter, die wir auch in OpenCV zur Verfugung
haben (siehe ITOM Dokumentation). Ich werde diese Filter hier nicht nochmals
wiederholen. Daruber hinaus sind noch folgende interessante Funktionen vorhan-
den.
Anzahl = filter("replaceInfAndNaN",srcImg, \
replaceImg, destImg)
# Despeckle Algorithmus analog zu gimp
filter("despeckleAdapted",scrImage, destImage)
# Schwerpunktsberechnung
filter("localCentroidXY",sourceImage, coarseSpots,
centroids, schwelle)
Bei der Schwerpunktsbestimmung sollten Sie in der Praxis (um hohe Genauigkei-
ten zu erzielen) unbedingt darauf achten, dass der Sensor nicht uberbelichtet (im
SWP) ist und das eine sinnvolle Schwelle (ca. 1.5 x Rauschamplitude) verwendet
wird.
14In der Regel ist das keine kostspielige Aktion, denn intern werden die dataObject Daten (meist) wie ein numpy
Array abgelegt.
95
Mit
filter("spikeMeanFilter", bild, erg, 5,5, 10, 255)
kann man isolierte Fehlstellen (Peaks) im Bild finden und auf einen festgelegten
Wert (im Beispiel: 255) setzen. Dazu wird zunachst ein Gaußfilter (hier 5 x 5) an-
gewendet und geschaut, an welchen Pixeln das Gaußfilterergebnis nennenswert
(hier: mindestens um 10) vom Orginalbild abweicht. Den Filter gibt es auch in
einer Median (statt Gauß) Variante.
8.7 Oft benotigte Funktionen und Idiome
Gewohnen Sie sich an, Ablaufe (z.B. Zwei-Bilder-in-ein-PlotFenster-ausgeben),
die Sie oft benotigen, in eine eigene Funktion auszulagern. Letztlich beschleunigt
das Ihren Arbeitsablauf.
Wenn Sie in Ihrem Python Script Umlaute oder andere Sonderzeichen verwenden,
kann es notwendig werden (ansonsten Fehlermeldung) an den Beginn des Skripts
\# -- coding: iso-8859-15 --
zu setzen.
8.7.1 Verzogerungen und Zeiten
import time
time.sleep(0.2) # wartet 0.2 Sekunden
import time
zeit = time.localtime()
tag = zeit.tm_mday # liefert den Tag des Datum
96
8.7.2 Einige Befehle fur Ausgabe und Hauptfenster
clear # Bildschirmausgaben loschen
itom.clc() # Bildschirmausgaben aus Skript loschen
figure.close(’all’) # loscht alle noch offenen Figures
figure.close(3) # loscht Figure Nr. 3
# --- Mehrere Bilder in einen View bringen ---------------
# (Welcher Subplot durch die Bedienelemente des Views
fig = figure(rows = 1, cols = 2)
fig.plot(img1, 0)
fig.plot(img2, 1)
8.7.3 Files
Komplette Messfiles werden ublicherweise im idc Format von ITOM gespeichert,
teilweise ist auch Matlab sinnvoll:
saveIDC("C:/test.idc", {"mat1":obj1, "mat2":obj2, "mat3":obj3})
myDict = loadIDC("C:/test.idc")
obj1new = myDict["mat1"]
obj2new = myDict["mat2"]
# Und dasselbe in Matlab
saveMatlabMat("C:/test.mat", {"mat1":obj1, "mat2":obj2, "mat3":obj3})
myDict = loadMatlabMat("C:/test.mat")
obj1new = myDict["mat1"]
obj2new = myDict["mat2"]
Bilder konnen mittels
a=dataObject()
filter("loadAnyImage",a, ’pic_rgba.tiff’,’asIs’)
filter("loadAnyImage",a, ’pic_rgba.tiff’,’gray’)
97
filter("savePNG", obj1, ’C:/pic_falseColor.png’)
filter("savePNG", obj1, ’C:/pic_falseColor.png’, ’hotIron’)
oder aber der OpenCV Variante
bild = cv2.imread("a.jpeg") # Bild laden und
bildgrau = img[:,:,1] # Grunkanal von Farbbild extrah.
eingelesen werden.
8.7.4 Externe Programme und Kommandozeilenargumente
import os
import sys, getopt
pfad = sys.argv[1:][0]
pfad2 = pfad.replace(’ ’, ’\ ’)
os.system("find %s -type f > /tmp/files" % (pfad2))
8.7.5 Sonstiges
a = input("Gebe eine Zahl ein") # Wartet auf Wert/Texteingabe
fig = plot(image)
fig[-1].call(’savePlot’,’test.png’)
8.8 Abschließende Bemerkung
Ich habe Ihnen — im Sinne des Konzepts dieses Buchs — nur das absolut notwen-
dige Minimalwissen zu Python und ITOM gezeigt. Die Leistungsfahigkeit beider
Systeme ist immens hoher. Ich habe lediglich an der Oberflache gekratzt. Fur einen
produktiven Einsatz lohnt es also, nach den ersten Gehubungen (dafur sind Sie
jetzt gerustet), Bucher zu den Themenfeldern ITOM (die Orginaldokumentation),
Python (z.B. [10, 11]) und OpenCV (sehr zu empfehlen: [12]) zu lesen.
98
Anhang 1: Installation ITOM (inkl. OpenCV)unter WindowsGenerell ist bei der Installation in Windows zu unterscheiden, ob Sie selbst am
Sourcecode des ITOM Kernsystems arbeiten wollen. In der Regel wird das nicht
der Fall sein. Sie brauchen das aber dann, wenn Sie z.B. Plugins fur neue Hardware
entwickeln wollen. In diesem Fall laden Sie eine Entwicklerversion und beachten
die Anleitung in der Dokumentation.
Ansonsten (und davon gehe ich jetzt aus) ist die Sache ganz einfach:
1. www.itom.rocks
2. Im Menu links auf “Download” klicken
3. ”End-User Setup” wahlen.
4. Exe starten und sich durch Installation hangeln
99
Anhang 2: Installation OpenCV und ITOMunter Ubuntu 16.04Wahrend die Installation von ITOM unter Windows normalerweise trivial ist (s.o.)
sieht die Sache unter Linux leider etwas anders aus.
Hier ist grundsatzlich zunachst OpenCV (in der Version 3.2) zu installieren und
mit Python 3 zum Laufen zu bekommen.
Dann kann ITOM kompiliert werden. Es ist namlich so, dass wir dabei ITOM
selbst zu kompilieren haben.
Sowohl ITOM wie auch OpenCV nutzen folgenden Ablauf, um letztlich eine In-
stallation durchzufuhren:
1. Notwendige Pakete von fremden Bibliotheken (z.B. Qt) aus dem Internet in-
stallieren
2. Sourcefiles aus Internet holen
3. Cmake → Makefiles
4. make → Programmcode
5. fur OpenCV: “make install”, um die Bibliotheken systemweit zu installieren
Die Nutzung von Cmake ist dabei ein durchaus komplexes Unterfangen, den dort
werden letztlich die ganzen Abhangigkeiten der Teilbibliotheken, die zur Compi-
lierung notwendig sind, abzufangen
Starten wir mit Schritt 1. Sie sollten zunachst mal ihr System, namlich Ubuntu
16.04, mit einigen Bibliotheken ausstatten (und ganz generell zunachst die aktuel-
len Pakete einspielen):
sudo apt-get update
sudo apt-get upgrade
Dann installieren wir eine Menge an zusatzlichen Paketen, die wir brauchen bzw.
haben wollen:
sudo apt-get install unity-tweak-tool emacs octave \
100
gnuplot subversion
sudo apt-get install cmake cmake-gui git python3 \
python3-dev python3-numpy python3-pip
sudo apt-get install python3-numpy-dbg python3-apt-dbg \
libqt5webkit5 libqt5webkit5-dev libqt5widgets5 \
libqt5xml5 libqt5svg5 libqt5svg5-dev libqt5gui5 \
libqt5designer5 libqt5concurrent5 libqt5scintilla2-dev \
qttools5-dev-tools qttools5-dev build-essential gimp \
gnuplot octave libv4l-dev xsdcxx libpcl-dev libproj-dev \
libgstreamer-plugins-base0.10-dev gstreamer0.10-x \
libxerces-c3.1 libxerces-c-dev
sudo apt-get install libdc1394-22 \
libdc1394-22-dev libjpeg-dev libpng12-dev libtiff5-dev \
libjasper-dev libavcodec-dev libavformat-dev \
libswscale-dev libxine2-dev libgstreamer0.10-dev \
libgstreamer-plugins-base0.10-dev libv4l-dev libtbb-dev \
libtheora-dev libvorbis-dev libxvidcore-dev x264
python-virtualenv libgtk-3-dev
OpenCV
cd ˜/
mkdir opencv
wget "https://github.com/Itseez/opencv/archive/3.2.0.tar.gz" \
-O ˜/opencv/opencv.tar.gz
cd opencv
tar -xzf opencv.tar.gz
cd opencv-3.2.0
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D \
CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_PYTHON_SUPPORT=ON\
-D WITH_XINE=ON -D WITH_OPENGL=ON -D WITH_TBB=ON \
-D WITH_EIGEN=ON -D BUILD_EXAMPLES=ON \
101
-D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON \
-D WITH_GTK=ON ../
make -j4
sudo make install
Jetzt testen wir, ob das so in Python funktioniert:
python3
import cv2
print(cv2.__version__)
Wenn das soweit passt, dann ist es Zeit sich erst einmal zumindest ein wenig zu
freuen. Sie konnen jetzt zumindest direkt unter python3 sinnvoll mit OpenCV
arbeiten.
ITOM (Version xxx)
Um ITOM selbst korrekt zu compilieren mussen die drei wesentlichen Hauptbe-
reiche von ITOM einzeln compiliert werden: ITOM, designerPlugins und plugins.
cd
mkdir itom
cd itom
mkdir sources
cd sources
git clone https://bitbucket.org/itom/itom.git
git clone https://bitbucket.org/itom/plugins.git
git clone https://bitbucket.org/itom/designerPlugins.git
cd /home/haist/itom
mkdir build
cd build
mkdir itom
cd itom
cmake -G "Unix Makefiles" -DBUILD_WITH_PCL=OFF \
102
-DOpenCV_DIR=˜/opencv/opencv-3.2.0/release ../../sources/itom
make -j4
cd ..
mkdir designerPlugins
cd designerPlugins
cmake -G "Unix Makefiles" -DBUILD_WITH_PCL=OFF \
-DITOM_SDK_DIR=../itom/SDK ../../sources/designerPlugins
make -j4
cd /home/haist/itom/sources/plugins/Ximea
git checkout 0c28102644e74dbbc437b9f958eac7433667e9de \
XimeaFuncsImport.h
cd ..
mkdir plugins
cd plugins
cmake -G "Unix Makefiles" -DBUILD_WITH_PCL=OFF \
-DITOM_SDK_DIR=../itom/SDK ../../sources/plugins
make -j4
itom compilieren und schließlich mittels
˜/itom/build/itom/qitom
starten.
In itom kann man prufen, um die passenden Versionen auch verfugbar sind:
>>import sys
>>print(sys.version)
3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609]
>>import cv2
>>print(cv2.__version__)
3.2.0
103
OK ? Dann seien Sie glucklich !
Vermutlich wollen Sie auch das Helpsystem und die Dokumentation in itom nut-
zen. Dazu brauchen Sie noch das Python Paket “sphinx”. Ganz generell installie-
ren Sie Python Pakete am besten mittels dem Tool pip3:
sudo pip3 install sphinx
Jetzt konnen Sie ITOM erneut starten und im Dateibrowser links in den Ordner
/itom/build/itom/docs/userDoc navigieren und dort das Script “create doc.py”
und starten dieses. Es sollte fehlerfrei durchlaufen und Sie konnen danach die
Dokumentation sehen.
Starten Sie danach noch das ebenfalls dort liegende Script “modify doc.py”.
Sollte man noch spezielle Kameras benotigen, dann sind die entsprechenden Trei-
ber nach Vorgaben des Herstellers vor der ITOM Installation eben zu installieren.
(Fur die Ximea Kamera:
cd ˜/
wget http://www.ximea.com/downloads/recent/XIMEA_Linux_SP.tgz
tar xzf XIMEA_Linux_SP.tgz
cd package
sudo ./install
Bei der Compilierung der Plugins gibt man dann die entsprechenden Pfade an:
cmake -G "Unix Makefiles" -DBUILD_WITH_PCL=OFF \
-DPLUGIN_XIMEA=ON
-DXIMEA_APIDIR=/home/haist/package/include\
-DXIMEA_BUILD_API_VERSION=4\.15 \
-DITOM_SDK_DIR=../itom/SDK ../../sources/plugins
make -j4
104
Literaturverzeichnis
[1] Bernd Jahne. Digitale bildverarbeitung. Springer-Verlag, 2013.
[2] William K Pratt. Introduction to digital image processing. CRC Press, 2013.
[3] Rafael C. Gonzalez and Richard E. Woods. Digital Image Processing (3rd Editi-
on). Prentice-Hall, Inc., Upper Saddle River, NJ, USA, 2006.
[4] Tobias Haist. Optik in der industriellen Bildverarbeitung. freies ebook,
www.oimv.de.
[5] Joseph W Goodman. Introduction to Fourier optics. Roberts and Company
Publishers, 2005.
[6] Mark S Nixon and Alberto S Aguado. Feature extraction & image processing for
computer vision. Academic Press, 2012.
[7] K Voss and H Susse. Praktische bildverarbeitung. 1991: Munchen. Wien: Carl
Hanser Verlag.
[8] Paul Viola and Michael Jones. Rapid object detection using a boosted cascade
of simple features. In Computer Vision and Pattern Recognition, 2001. CVPR
2001. Proceedings of the 2001 IEEE Computer Society Conference on, volume 1,
pages I–I. IEEE, 2001.
[9] Helge Ritter, Thomas Martinetz, and Klaus Schulten. Neuronale netze. Mun-
chen: Addison-Wesley, 1991.
[10] Michael Weigend. Objektorientierte Programmierung mit Python. mitp Bonn,
2006.
[11] Ivan Idris. NumPy Beginner’s Guide. Packt Publishing Ltd, 2013.
[12] Adrian Kaehler and Gary Bradski. Learning OpenCV 3: computer vision in C++
with the OpenCV library. O’Reilly Media, Inc.”, 2016.
105
Index
106
Index
Abtastungen, 24
Addition, 15
affine Transformation, 18
affine Transformationen, 18
Antwort, 24
Arrays, 83
Basisfunktionen, 29
Basiszerlegung, 23
Bewegungsmeldung, 16
Bilateralen Filter, 47
Bildscharfe, 15
Binarisierung, 11
Bittiefe, 5
boolsche Bilder, 16
Boxfilter, 40
C++, 3
Canny, 46
Clipping, 16
Closing, 50
Convolutional Neural Networks, 73
Dictionaries, 84
Dilatation, 50
Distanztransformation, 51
Division, 16
ebener Wellen, 30
Eigenfunktionen, 31
Erosion, 50
exklusives Oder, 16
Falschfarbenbilder, 13
Faltung, 24, 27
Faltungsintegrale, 24
Faltungssatz, 36
Farbbilder, 17
Farbe, 5
Farbraum, 17
Farbraumtransformationen, 17
Farbsensoren, 5
Fensterung, 29
Fourierdeskriptoren, 64
Fouriertransformation, 30, 33
Gamma Transformationen, 11
Glattungsfilter, 47
Grauwert, 10
Hintergrundsinformation, 16
Histogramm, 12
Histogrammausgleich, 12
Hochpassfilter, 35
Hough Transformation, 58
HSI, 17
HSV, 17
Interpolationsmethoden, 18
invarianten Systemen, 22
inverse Fouriertransformation, 33
ITOM, 3
Kantenstarke, 45
Kennlinie, 5
Kettencode, 64
107
Kirschoperatoren, 45
Klassifikation, 54
Kompassoperatoren, 45
Korrelation, 66
kubische Interpolation, 18
Laplace-of-Gaussian, 43
Laplacefilter, 43
Lineare Filter, 20
lineare Systeme, 21
Linearitat, 21
list comprehension, 84
Listen, 83
Lookup-Table, 10
Maximumfilter, 47
Medianfilter, 47
Merkmale, 54
Minimumfilter, 47
Mittelung, 40
Mittelwert, 15
Modul, 86
Modulationstransferfunktion, 32
Momente., 63
Morphologische Gradient, 51
Multiplikation, 16
Nachbarschaft, 10
Namespaces, 85
Nearest Neighbour, 70
nichtlinear, 45
nichtlineare Algorithmen, 26
Nichtlinearitat, 20
normalisierte Kreuzkorrelation, 66
Normalisierungen, 11
Oder, 16
OpenCV, 3
Opening, 50
Otsu, 12
Paket, 86
perspektivischer Verzeichnungen, 18
Pixel, 10
Plugin-System, 92
Principal Component Analysis, 70
Punktoperationen, 10
Python, 3
Quantisierungsstufen, 5
Radon-Transformation, 59
Rand, 28
Rangordnungsfilter, 47
Rauschen, 15, 40
Rauschreduktion, 15
Regions-of-Interest, 17
RGB, 17
Rotationen, 18
Sattigungskapazitat, 5
salt-and-pepper, 47
Sampling, 24
separierbar, 28
Sets, 84
Shading-Korrektur, 16
Signal-Rausch-Verhaltnis, 5, 16
Sobelfilter, 45
Standardabweichung, 15
Strukturelement, 50
Subtraktionen, 16
Sum of Absolute Differences, 65
Thresholding, 11
Tiefpassfilter, 40
Tophat Transformation, 53
Transformationsmatrix, 18
108
Translationen, 18
Und, 16
Unsharp Masking, 44
Watershed Algorithmus, 55
109