Funktionale Bilder - AG Programmiersprachen und

Werbung
Funktionale Bilder
Seminar Programmiersprachen und Programmiersysteme“
”
Arbeitsgruppe Programmiersprachen und Übersetzerkonstruktion,
Institut für Informatik, Technische Fakultät,
Christian-Albrechts-Universität zu Kiel.
Seminarbeitrag von Torsten Krause, Sommersemester 2010.
Basierend auf Conal Elliott, Functional Images“.
”
Betreuung durch Prof. Dr. Michael Hanus.
2
Inhaltsverzeichnis
1 Einführung
4
2 Hintergrund
2.1 Nichtfunktionale Bilder . . .
2.1.1 Rastergrafiken . . . .
2.1.2 Vektorgrafiken . . . .
2.1.3 Prozedurale Texturen
2.2 Funktionale Bilder . . . . . .
2.2.1 Polymorphie . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
5
6
7
7
8
3 Funktionale Bildbeschreibung mit Pan
3.1 Schwarzweißbilder, Regionen und Bildkomposition
3.2 Graustufenbilder, Farbverläufe und Interpolation .
3.2.1 Rastergrafiken einbinden . . . . . . . . . . .
3.3 Punkt- und Bildtransformationen . . . . . . . . . .
3.3.1 Polare Transformationen . . . . . . . . . . .
3.4 Animationen . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
11
13
15
18
21
22
4 Pan als eingebettete Sprache
4.1 Weitere Entwicklung . . . . . . . . . . . . . . . . . . . . . . . . . .
23
24
5 Fazit
25
6 Literatur
26
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
1 Einführung
Der Grundgedanke hinter funktionalen Bildern besteht darin, Bilder als Funktionen über einem unendlichen, kontinuierlichen, zweidimensionalen Raum aufzufassen.
Hier sollen im folgenden Kapitel einige der üblichen Ansätze der Bildbeschreibung
dargestellt werden. Dabei wird erwähnt, welche Konzepte bei einer funktionalen Bildbeschreibung verwendet werden können. Des weiteren wird die von Conal
Elliott in seinem Artikel Functional Images“ beschriebene Sprache Pan“ vor”
”
gestellt. Hierbei handelt es sich um eine in Haskell implementierte, eingebettete,
anwendungsspezifische Sprache zur funktionalen Bidbeschreibung. Bei der Implementierung von Pan wurde besonderes Augenmerk auf eine effiziente Ausführung
gelegt. Nachdem in Kapitel 3 die Sprachmittel von Pan ausführlich beschrieben
werden, wird im vierten Kapitel kurz auf wichtigsten Eigenheiten der Implementierung eingegangen.
Eine weitere, bildbeschreibende, funktionale Sprache wird die von Jerzy Karczmarczuk in seinem Artikel Functional Approach to Texture Generation“ beschriebe”
ne, in Clean eingebette Sprache Clastic“. Sie teilt sich mit Pan das Konzept
”
der Bilder als Funktionen über einem unendlichen, kontinuierlichen, zweidimensionalen Raum, ihr Schwerpunkt liegt aber in der generierung von Texturen, also
speziellen Bildern, die z.B. als Oberflächenfarbe für 3D-Objekte in Computergrafiksystemen verwendet werden. Hier wird der Schwerpunkt auf fortgeschrittene
Ideen wie periodische Wiederholungen und Rauschen als Quelle für Bildeffekte
gelegt. Die grundlegenden Konzepte sind allerdings die selben, die auch Pan verwendet, so dass dise problemlos am Beispiel von Pan gezeigt werden können.
4
2 Hintergrund
2.1 Nichtfunktionale Bilder
Bilder lassen sich am Computer in vielfältiger Weise beschreiben und darstellen.
Die gänigsten Beschreibungsformen sind Raster- und Vektorgrafiken.
2.1.1 Rastergrafiken
Die einfachste Form der Darstellung ist ein endliches, zweidimensionales Feld das
mit Farbinformationen gefüllt wird. Handelt es sich bei der Farbinformation lediglich um Wahrheitswerte, so kann damit ein Schwarzweißbild dargestellt werden. In diesem Fall nennt man ein solches Feld eine Bitmap“. Die ist ein Begriff
”
der sich mittlerweile auch im Falle anderer Farbinformationen durchgesetzt hat.
Beispielsweise könnte mit Farbinformationen aus dem Wertebereich {0, . . . , 255}
eine 8-Bit-Graustufen-Bitmap gefüllt werden. Analog könnte man ein Graustufenbild auch mit kontinuierlichen Werten aus dem Intervall [0, 1] darstellen. Eine
farbige Bitmap kann man mit Rot-Gelb-Blauen (RGB) Farbinformationen aus
dem dreidimensionalen Intervall [0, 1]3 befüllen. Derartige Felder werden auch als
Rastergrafik“ bezeichnet.
”
In einer Bitmap wird ein Bild also über eine endliche Menge von Farbinformationen an fest vorgegebenen Positionen, die den Feldeinträgen entsprechen, definiert.
Für jede Position muss explizit ein Farbwert angegeben werden. Transformationen sind nicht möglich. Will man ein Bitmap ändern, so muss mit einem externen
Programm eine neue Bitmap erstellt und für jede Position ein neuer Farbwert
errechnet werden.
Aufgrund der einfachen Datenstruktur hinter einer Bitmap eignet sich diese Darstellung allerdings sehr gut, um Bilder zu speichern. Es existiert eine Fülle von
Dateiformaten in denen solche Bitmaps abgespeichert werden können. Sie eigenen
sich besonders zur Ausgabe auf gerasterten“ Ausgabegeräten wie Monitoren oder
”
Druckern.
Die obige Feststellung, dass verschiedene Arten von Bildern als Bitmaps über
unterschiedliche Arten von Farbinformationen aufgefasst werden können, kann in
einer Bildbeschreibungssprache besonders elegant ausgenutzt werden, wenn diese
Sprache polymorph ist.
5
2 Hintergrund
2.1.2 Vektorgrafiken
Ein anderer Ansatz der Bilddarstellung in Computern sind die sogenannten Vektorgrafiken. Hierbei werden Bilder als Folge von einfachen Zeichenoperationen dargestellt. Solche Zeichenoperationen können beispielsweise einzelne Linien und (geschlossene) Linienzüge, Kreise und Ellipsen, Rechtecke und andere Vielecke oder
Pfade sein.
Alle diese Zeichenoperationen nehmen kontinuierliche Koordinaten als Parameter. Sie können auch mit weiteren Parametern, etwa der Linienstärke und -farbe,
ergänzt werden.
Will man eine Vektorgrafik transformieren, so muss mit einem externen Programm
lediglich die Folge von Zeichenoperationen manipuliert werden. Um eine Vektorgrafik zu speichern muss die Folge von Zeichenoperationen geeignet kodiert werden. Ein Beispiel ist das XML-basierte, also baumartige Format Scalable Vector
Graphics (SVG). Wird in einem Knoten zum Beispiel die aktuelle Zeichenfarbe
geändert, so hat dies Auswirkung auf alle darunterliegenden Knoten. Zur Interpretation einer SVG-Grafk ist also wenigstens ein Kellerautomat notwendig.
Eine Vektorgrafik lässt sich auf einem Ausgabegerät mit kontinuierlichen Koordinaten, wie einem Plotter, direkt ausgeben. Für eine Ausgabe auf gerasterten
Geräten muss die Vektorgrafik selbst gerastert werden, dass heisst ein Interpreter,
der eine diskrete Rasterkoordinate und die Vektorgrafik als Eingabe bekommt,
führt die Folge von Zeichenoperationen aus und entscheidet, welcher Farbwert an
der übergebenen Koordinate gesetzt wird.
Pfade werden in Vektorgrafiken häufig durch Bézierkurven oder Splines dargestellt. Dabei handelt es sich um parametrisierte Beschreibungen von Kurven, also
Funktionen.
Die letzten beiden Feststellungen motivieren es, Bilder nicht als zu interpretierende Folge von Zeichenoperationen anzusehen, sondern direkt als Funktionen zu
modellieren.
Ein weiteres Beispiel für ein Vektorgrafiksystem ist die Sprache Postscript (PS).
Hierbei handelt es sich um eine sogenannte Seitenbeschreibungssprache, also um
eine Sprache, die das Aussehen einer (zu druckenden) Seite, im weiteren Sinne also eines Bildes, beschreibt. Eine Postscript-Datei ist dabei ein Programm, das auf
einer abstrakten, zustandsbehafteten Kellermaschine ausgeführt wird. Diese ist
zusätzlich mit einer Zeichentabelle ausgestattet, wodurch Turingvollständigkeit
und damit ein universeller Beschreibungsmechanismus erreicht wird. Postscriptprogramme werden als Folgen von Anweisungen in Präfixnotation geschrieben, die
den Zustand der Kellermaschine modifizieren. Konzeptionell ist Postscript also eine imperative Sprache.
6
2.2 Funktionale Bilder
2.1.3 Prozedurale Texturen
In der Computergrafik werden Bilder, mit denen die Oberflächen von dreidimensionalen Objekten als ortsabhängige Farbe“ eingefärbt werden, als Textur“ be”
”
zeichnet. Der Vorgang, eine Oberfläche mit einer Textur zu überziehen, wird als
texture mapping“ bezeichnet.
”
Früher war es üblich, die dazu verwendeten Texturen als Rastergrafiken zu speichern und zum texture mapping in den Grafikspeicher zu laden. In heutigen
Grafikanwendungen ist dies wegen des umfangreichen Speicherbedarfs oft nicht
mehr praktikabel. Texturen werden daher vermehrt zur Laufzeit berechnet. Man
spricht in diesem Zusammenhang von prozeduralen Texturen“ oder shadern“.
”
”
Ein shader ist eine Rechenvorschrift im mathematischen Sinne, die Koordinaten
als Eingabe bekommt und einen Farbwert als Ausgabe liefert, also (vereinfacht)
ein Abbildung R2 7→ [0, 1]3 .
Shader werden in sogenannten Shadersprachen geschrieben. Diese können beispielsweise Assemblersprachen sein, wie etwa die ARB – OpenGL Assembly Lan”
guage“ die verwendet wird, um die ebenfalls shader“ genannten Hardwareein”
heiten moderner Grafikprozessoren anzusteuern. Andere Vertreter sind eher C”
artig“ wie die OpenGL Shading Language“ die nach ARB übersetzt wird oder
”
die RenderMan Shading Language“ (RSL) die Teil der RenderMan Interface
”
”
Specification“ der Firma PIXAR ist.
Auch wenn Programme der typischen Vertreter der Shadersprachen als Anweisungsfolgen notiert werden, sind sie Seiteneffektfrei. Es gibt also auch keinen
Programmstatus und damit fallen sie weder unter die typische Definition einer
imperativen, noch einer deklarativen Programmiersprache.
2.2 Funktionale Bilder
In der Beschreibung der nichtfunktionalen Bilder hat sich gezeigt, dass es durchaus sinnvoll sein kann, Bilder direkt als Funktionen darzustellen. Verschiedene
Arten von Bildern können dann gleichartig behandelt werden, wenn Polymorphie
unterstützt wird. Wenn der Grundgedanke hinter funktionalen Bildern – wie in
der Einleitung erwähnt – darin besteht, Bilder als Funktionen über einem unendlichen, kontinuierlichen, zweidimensionalen Raum aufzufassen, dann ist ein Bild
in einer funktionalen, polymorphen Sprache nichts weiter als eine Abbildung des
Typs
type Image α = Point → α.
Hierbei ist Point der Typ von Koordinaten im betrachteten Raum. Für Bilder sind
dies zweidimensionale kartesische Koordinaten
7
2 Hintergrund
type Point = (Float, Float)
oder Polarkoordinaten
type PolarPoint = (Float, Float).
Sinnigerweise bietet man dann Funktionen fromPolar :: PolarPoint → Point und
toPolar :: Point → PolarPoint an.
Übliche Operationen auf Bildern sind die folgenden:
◦ Komposition: Erstellen neuer Bilder aus vorhandenen Bildern. Da Bilder
Funktionen sind, entspricht dies einer Funktionskomposition.
◦ Transformation: Die Veränderung der Lage oder Form eines Bildes kann
durch eine Funktion realisiert werden, die die Koordinaten modifiziert, bevor
auf diese das Bild appliziert wird.
◦ Interpolation und Gradienten: Farbverläufe zwischen fest gewählten Bezugspunkten.
2.2.1 Polymorphie
Wie in Abschnitt 2.1 erwähnt, kann es sich anbieten Polymorphie auszunutzen, um
verschiedene Arten von Bildern auf gleiche Art verarbeiten zu können. Der obige
Typ Image α ist daher im Zielbereich polymorph bezüglich der Farbinformation
gestaltet.
Als konkrete Bildtypen bieten sich beispielsweise die folgenden an:
◦ Image Bool: Verwendet man die Wahrheitswerte als Farbinformation können
damit einerseits Schwarzweißbilder definiert werden. Andererseits können
hiermit auch die Bereiche definiert werden, in denen ein Bild dargestellt
werden soll. Ein derariges Bild wirkt dann als Filter“ für ein anderes Bild.
”
Um dies zu verdeutlichen kann man ein Typsynonym
type Region = Image Bool
einführen. Solche Regionen können dann für die Bildkomposition verwendet
werden, indem man aus einfachen Regionen komplexe Regionen erstellt und
anschließend ein anderes Bild mit solchen komplexeren Regionen filtert. Da
eine Region als Menge von Bildpunkten aufgefasst werden kann, in denen
gerade nicht weggefiltert wird, können zum Erstellen komplexerer Regionen
die üblichen Mengenoperationen wie Schnitt, Vereinigung oder Komplement
verwendet werden.
◦ Image [0, 1]: Verwendet man als Farbinformation relle Werte aus dem Intervall [0, 1], so lassen sich damit beispielsweise Bilder in Graustufen darstellen.
Diese können ähnlich wie Regionen als Filter auf andere Bilder angewandt
werden. Hierbei wird die Graustufe als Maß dafür verwendet, wie viel von
8
2.2 Funktionale Bilder
dem zu filternden Bild verwendet wird. Hiermit kann also ein Bild ausgeblendet werden. Verwendet man diese Technik mehrfach, kann ein Bild mit einem
anderen gemischt werden. Handelt es sich dabei um vollflächige, einfarbige
Bilder, so lässt sich mit einer solchen Filterung ein Farbverlauf beschreiben. Handelt es sich um Bilder, die nur an diskreten Punkten definiert sind,
beispielsweise um eine aus einer Datei geladene Rastergrafik, so kann der
Zwischenraum mit geeigneten Farbverläufen interpoliert werden.
◦ Image [0, 1]k : Verwendet man als Farbinformation relle Werte aus dem mehrdimensionalen Intervall [0, 1]k , können hiermit farbige Bilder definiert werden. Das k muss dabei je nach verwendetem Farbraum gewählt werden. Mit
k = 3 lassen sich RGB-Bilder beschreiben. Mit k = 4 lassen sich BRGABilder beschreiben. Dabei handelt es sich um RGB-Bilder, die um einen
zusätzlichen Wert, den sogenannten Alphakanal“, erweitert sind. Dieser
”
wird als Transparenzwert interpretiert. Üblich ist es, die Farben so anzugeben, dass kein Wert der drei Farbkanäle höher ist, als der Wert des Alphakanals (premultiplied alpha). Die völlig transparente Farbe ist dann eindeutig
als
transparent :: Image [0, 1]4
transparent = (0,0,0,0)
gegeben. Andere Grundfarben sind beispielsweise
white, black, red :: Image [0, 1]4
white = (0,0,0,1)
black = (1,1,1,1)
red = (0,0,1,1).
Das hier betrachtete, funktionale Bildsystem Pan ist nur im Zielbereich Polymorph. Polymorphie kann aber auch im Wertebereich sinnvoll sein. Beispielsweise
könnte man Point × Time als Definitionsbereich für animierte Bilder verwenden, oder einen eindimensionalen Definitionsbereich verwenden um mit den selben
funktionalen Mitteln auch Audiosignale zu definieren.
9
3 Funktionale Bildbeschreibung mit Pan
In Pan werden einige Typsynonyme für die bisher beschriebenen Dinge verwendet.
Für die [0, 1]-Intervalle wird
type Frac = Float
benutzt, wobei die Zusicherung, dass die Werte tatsächlich nur im [0, 1]-Intervall
liegen zur Schnittstellendefinition gehört und nicht im Programcode selbst geprüft
wird. Für BRGA-Farben existiert der Typ
type Colour = (Frac, Frac, Frac, Frac)
und für farbige Bilder der Typ
type ImageC = Image Colour.
Es werden einige Liftingoperationen generell definiert, um andere Operationen
damit einfach ausdrücken zu können. Beispielsweise kann mit
lift3 :: (α → β → γ → δ) → (p → α) → (p → β) → (p → γ) → (p → δ)
lift3 h f1 f2 f3 p = h (f1 p) (f2 p) (f3 p)
eine Funktion condR definiert werden, mit der in Abhängigkeit einer Region eines
von zwei Bildern angezeigt wird:
condR :: Region → Image α → Image α → Image α
condR = lift3 (λ a b c → if a then b else c)
Die Funktion const kann als nullstelliges Lifting betrachtet werden, mit dem beispielsweise das komplett leere Bild
emptyI :: ImageC
emptyI = const transparent
oder andere vollflächige, einfarbige Bilder definiert werden können:
whiteI, blackI, redI :: ImageC
whiteI = const white
blackI = const black
redI = const red
10
3.1 Schwarzweißbilder, Regionen und Bildkomposition
Abbildung 3.1: disk1
3.1 Schwarzweißbilder, Regionen und Bildkomposition
Schwarzweißbilder können als Regionen interpretiert werden. Diese können als
Punktmengen aufgefasst werden. Eine einfache Region ist eispielsweise
disk :: Float → Region
disk r p = distO p < r
disk1 :: Region
disk1 p = disk 1
wobei distO den Abstand zwischen einem gegebenen Punkt und dem Nullpunkt
der Zeichenebene berechnet. Hier zeigt sich ein Vorteil funktionaler Bilder, denn
disk kann über einen Parameter variiert werden. Abbildung 3.1 zeigt die Region
disk1. Mit einer solchen Region und condR kann dann ein einfaches Bild erstellt
werden. Beispielsweise zeigt Abbildung 3.2 das Bild, das als
ybDisk :: imageC
ybDisk = condR disk1 blueI yellowI
definiert ist. Eine etwas kompliziertere Region ist beispielsweise
altRingsR :: Region
altRingsR p = even ⌊ distO p ⌋
Diese ist in Abbildung 3.3 zu sehen. Hier zeigt sich ein weiterer Vorteil funktio-
11
3 Funktionale Bildbeschreibung mit Pan
Abbildung 3.2: ybDisk
naler Bilder, denn altRingsR ist unendlich groß. Nun wäre es sicher mühsam für
jede Form, die eine Region haben kann, eine entsprechende boolsche Funktion zu
schreiben. Pan besitzt daher Funktionen für die üblichen Mengenoperationen, um
aus einfachen Regionen neue Regionen zu kombinieren.
universeR, emptyR :: Region
compR :: Region → Region
(∩), (∪), (⊕), (\) :: Region → Region → Region
universeR = const True
emptyR = const False
compR = lift1 not
(∩) = lift2 and
(∪) = lift2 or
(⊕) = lift2 6=
r \ r ′ = r ∩ compR r ′
Die Abbildungen 3.4 und 3.5 zeigen zwei komplexere Regionen, die wie folgt definiert sind:
ringR :: Region
ringR = disk1 \ disk 0.5
complexR :: Region
12
3.2 Graustufenbilder, Farbverläufe und Interpolation
Abbildung 3.3: altRingsR
complexR (x, y) = altRingsR (x + 2, y) ⊕ altRingsR (x − 2, y)
Ein wichtiger Spezialfall für die Anwendung von Regionen ist das Zurückführen
der prinzipiell unendlich großen funktionalen Bilder auf einen endlichen Bereich.
In vielen Grafiksystemen wird hier eine rechteckige Boundingbox verwendet, die
definiert, in welchem Bereich das Bild angezeigt wird. Bei funktionalen Bildern
ist man hier nicht auf eine rechteckige Form beschränkt. Das Einschränken eines
Bildes auf einen endlichen Bereich geschieht einfach mit einer endlichen Region,
über die mittels crop alles Außenliegende durch das leere Bild ersetzt wird.
crop :: Region → ImageC → ImageC
crop r i =cond r i emptyI
3.2 Graustufenbilder, Farbverläufe und Interpolation
Regionen haben dan Nachteil harter Kanten. Oft ist ein solches aussehen aber nicht
gewünscht. Um einen weichen Übergang zu bekommen muss man von Schwarzweißzu Graustufenbildern übergehen. Beispielsweise können die alternierenden Ringe
aus Bild 3.3 mit sanften Übergängen folgendermaßen definiert werden:
waves :: image Frac
waves p = (1 + cos(π × distO p)) / 2
13
3 Funktionale Bildbeschreibung mit Pan
Abbildung 3.4: ringR
Abbildung 3.6 zeigt das so entstehende Wellenmuster. Ein solcher Graustufenverlauf kann nun als Grundlage für einen Farbverlauf genutzt werden, indem der
Graustufenwert benutzt wird, um zwischen zwei Farben zu interpolieren.
Dazu wird in Pan eine Funktion lerpI verwendet, die folgendermaßen definiert ist:
lerpC :: Frac → Colour → Colour → Colour
lerpC w (b1 , g1 , r1 , a1 ) (b2 , g2 , r2 , a2 ) = (h b1 b2 , h g1 g2 , h r1 r2 , h a1 a2 )
where
h x1 x2 = w × x1 + (1 − w) × x2
lerpI :: Image Frac → ImageC → ImageC → ImageC
lerpI = lift3 lerpC
Abbilddung 3.7 zeigt das Bild
ybWaves :: ImageC
ybWaves = lerpI waves blueI yellowI
Die Funktion lerpI interpoliert linear zwischen zwei Bildern und wird punktweise auf die Funktion lerpC zurückgeführt, die in Abhängigkeit eines Frac-Wertes
zwischen zwei Farben interpoliert. Der vorher definierte Graustufenverlauf ist die
Quelle für die Frac-Werte, mit denen lerpC aufgerufen wird.
Eine ähnliche Funktion ist overI mit der zwei Bilder übereinandergelegt werden
können (overlay). Hier wird kein zusätzlicher Graustufenwert benötigt. Wieviel
14
3.2 Graustufenbilder, Farbverläufe und Interpolation
Abbildung 3.5: complexR
vom unteren Bild zu sehen ist, hängt lediglich vom Transparenzwert des darüberliegenden Bildes ab. Auch overI ist punktweise mittels einer weiteren Funktion
definiert. Die Funktion overC berechnet das Übereinanderlegen für zwei Farben.
overC :: Colour → Colour → Colour
(b1 , g1 , r1 , a1 ) ‘overC‘ (b2 , g2 , r2 , a2 ) = (h b1 b2 , h g1 g2 , h r1 r2 , h a1 a2 )
where
h x1 x2 = × x1 + (1 − a1 ) × x2
overI :: ImageC → ImageC → ImageC
overI = lift2 overC
Abbildung 3.8 zeigt eine einfache Überlagerung. Das entsprechende Bild ist wie
folgt definiert:
ybWavesRed :: ImageC
ybWavesRed = overI (const (0,0,0.5,0.5)) ybWaves
3.2.1 Rastergrafiken einbinden
Im Gegensatz zu anderen Grafiksystemen, die selbst rasterbasiert sind, ist es nicht
trivial, Rastergrafiken als funktionales Bild zu laden. Während Rastergrafiken begrenzt und ortsdiskret sind, sind funktionale Bilder prinzipiell unbegrenzt groß
und haben einen kontinuirlichen Definitionsbereich. Dennoch unterstützt Pan das
15
3 Funktionale Bildbeschreibung mit Pan
Abbildung 3.6: waves
Einbinden von Rastergrafiken. Die interne Darstellung geschieht über den Datentypkonstruktor
data Array2 α = Array2 Int Int ((Int,Int) → α)
Generell können also Rastergrafiken über verschiedene Wertebereiche eingebunden werden. Typischerweise wird dies aber Colour sein. Der Konstruktor Array2
nimmt neben der Größe des geladenen Bildes in x- und y-Richtung eine Funktion auf, die diskrete Punkte in den entsprechenden Wertebereich abbildet. Eine
Rastergrafik Array2 n m f ist also für alle x-Werte 0 ≤ x < n und alle y-Werte
0 ≤ y < m definiert und f (x, y) liefert den Farbwert an der entsprechenden
Position. Für jedes unterstützte Bildformat muss eine entsprechende Funktion bereitgestellt werden, die das Format (z.B. aus einer Datei geladen) lesen kann und
einen entsprechenden Array2 α-Wert zurückgibt. Hier wurde ein Funktionstyp und
kein Array-Datentyp gewählt, um das Bild nicht tatsächlich als Array laden zu
müssen, sondern nur nach Notwendigkeit die Farbinformationen zu laden.
Um nun in den unbeschränkten Wertebereich eines funktionalen Bildes zu gelangen, werden lediglich alle außerhalb der Rastergrafik liegenden Punkte mittels
crop auf die leere Grafik zurückgeführt. Eine entsprechende Region wird mit den
Bildgrößeninformationen und der Funktion inBounds generiert:
inBounds :: Int → Int → Region
inBounds w h (x, y) =
16
3.2 Graustufenbilder, Farbverläufe und Interpolation
Abbildung 3.7: ybWaves
0 ≤ x ∧ x ≤ fromInt (w − 1) ∧
0 ≤ y ∧ y ≤ fromInt (h − 1)
Um in den kontinuierlichen Wertebereich zu gelangen, werden die Farbinformationen für alle Punkte, die nicht einem der diskreten Punkte des Bildes entsprechen,
bilinear zwischen den vier umgebenden Punkten interpoliert:
bilerpC :: Colour → Colour → Colour → Colour → (Frac, Frac)→ Colour
bilerpC ll lr ul ur (wx, wy) =
lerpC wy (lerpC wx ll lr) (lerpC wx ul ur)
bilerpArray2 :: ((Int,Int) → Colour) → ImageC
bilerpArray2 f (x, y) =
let
i=⌊x⌋
j=⌊y⌋
wx = x − fromInt i
wy = y − fromInt j
in
bilerpC (f (i, j
)) (f (i + 1, j
))
(f (i, j + 1)) (f (i + 1, j + 1))
(wx, wy)
Die bilineare Interpolation bilerpC wird auf mehrere Anwendungen der eindimen-
17
3 Funktionale Bildbeschreibung mit Pan
Abbildung 3.8: ybWavesRed
sionalen Interpolation lerpC zurückgeführt. Die Funktion bilerpArray2 berechnet
zu einem Punkt innerhalb des Bildes die vier umliegenden Punkte der Rastergrafiken und führt die bilineare Interpolation mit den entsprechenden Farbwerten aus
der Rastergrafik durch. Abbildung 3.9 zeigt eine solche bilineare Interpolation aus
vier verschiedenen Farbwerten:
testBilerpC :: ImageC
testBilerpC = bilerpC black red blue white
Um nun eine komplette Rastergrafik zu rekonstruieren werden die vorhandenen
Funktionen zusammengesetzt:
reconstruct :: Array2 Colour → ImageC
reconstruct (Array2 n m f ) = crop (inBounds n m) (bilerpArray2 f )
3.3 Punkt- und Bildtransformationen
Die üblichen Bildtransformationen in der Copmutergrafik sind Rotation, Translation, Skalierung, Scheerung und Projektion. Alle diese werden normalerweise als
Matrix dargestellt. Mehrere Transformationen werden mittels Matrixmultiplikation kombiniert und ihre Anwendung entspricht der Multiplikation der Transformationsmatrix mit einem Punkt. Die Matrixform wird hier als kompakte Darstellung
für lineare, affine oder projektive Abbildungen verwendet. In der funktionalen
18
3.3 Punkt- und Bildtransformationen
Abbildung 3.9: testBilerpC
Bildbeschreibung ist ein solcher Umweg“ über Matrizen aber unnötig. In Pan
”
werden die entsprechenden Operationen direkt als Punkttransformation definiert.
Für Transformationen wird wieder ein Typsynonym eingeführt.
type Transform = Point → Point
Einige Beispiele für Punkttransformationen sind die folgenden Funktionen:
translateP, scaleP :: (Float, Float) → Transform
translateP (dx, dy) (x, y) = (x + dx, y + dy)
scaleP (sx, sy) (x, y) = (x × sx, y × sy)
rotateP :: Float → Transform
rotateP θ (x, y) = (x × cos θ − y × sin θ, y × cos θ + x × sin θ)
Ideal wäre es, wenn man eine beliebige Punkttransformation trans mit einem Bild
image kombinieren könnte, beispielsweise als Hintereinanderausführung image .
trans. Hier werden aber zunächst die Punkte transformiert und dann von dem Bild
eingefärbt. Skaliert man ein Bild mit dem Vektor (2, 2), so wird (x, y) zunächst
auf (2 · x, 2 · y) abgebildet und bekommt dann den Farbwert des Bildes an dieser
Stelle.
(image . scaleP (2, 2)) (x, y) = image (2 · x, 2 · y)
Das heißt an der Stelle (x, y) wir der Punkt angezeigt, der vorher bei (2 · x, 2 · y)
lag. Das Bild wird also nicht um den Faktor 2 vergrößert, sondern verkleinert. Dies
19
3 Funktionale Bildbeschreibung mit Pan
entspricht aber kaum der Intuition beim Anwenden von scaleP (2, 2). Dies liegt
daran, dass die Transformation vor dem Bild auf die Punkte angewandt wird. Was
eigentlich der Intuition entspräche wäre der Zusammenhang
(image . scaleP (2, 2)) (x, y) = image ( 21 · x,
1
2
· x)
Hieran erkennt man, dass eine beliebige Punkttransformation trans mit einem
Bild image kombiniert werden können, indem image . trans −1 berechnet wird.
Die Umkehrung kann allerdings nicht vom Programmiersystem bestimmt werden und muss auch nicht immer existieren, andererseits wird die Originalabbildung trans gar nicht benötigt, so dass es genügt, die Inverse anzugeben. Für die
oben angegebenen affinen Transformationen kann die Inverse auf die ursprünlichen
Punkttransformation zurückgeführt werden. Da nun immer das Bild bekannt sein
muss, um image . trans −1 zu definieren, können nur noch Bildtransformationen
angegeben werden. Diese werden in Pan als Filter bezeichnet.
type Filter α = Image α → Image α
type FilterC = Filter Colour
translate, scale :: (Float, Float) → Filter α
translate (dx, dy) (x, y) im = im . translateP (− dx, − dy)
scale (sx, sy) (x, y) im = im . scaleP (− sx, − sy)
rotate :: Float → Filter α
rotate θ (x, y) im = im . rotateP (− θ)
Ein Vorteil der Darstellung eines Filters als Funktion ist es, dass man nicht auf
lineare, affine und projektive Abbildungen beschränkt ist. Filter müssen nicht einmal invertierbar sein, aber wenn sie es sind, ist es oft sinnvoll, zunächst die inverse
Transformation für Punkte zu definieren und den Filter darauf zurückzuführen.
Der folgende Filter rotiert Punkte in Abhängigkeit ihrer Entfernung zum Nullpunkt. Der zusätzliche Parameter gibt an, bei welchem Abstand eine vollständige
Umdrehung erreicht werden soll.
swirlP :: Float → Transform
swirlP r p = rotate (distO p × 2 × π / r) p
swirl :: Float → Filter α
swirl r p = im . swirlP (− r)
Abbildung 3.10 zeigt diesen Filter angewandt auf eine einfache Region, die einen
vertikalen Streifen darstellt.
vstrip :: Region
vstrip (x, y) = | x | ≤
1
2
vstripSwirl :: Region
vstripSwirl = swirl 1 vstrip
20
3.3 Punkt- und Bildtransformationen
Abbildung 3.10: vstripSwirl
An diesem Beispiel zeigt sich erneut der Vorteil polymorpher Definitionen, denn
die Filter können problemlos auf Regionen und farbige Bilder angewandt werden.
3.3.1 Polare Transformationen
In der Definition von swirlP fällt auf, dass diese Transformation nur den Winkel eines Punktes modifiziert, nicht aber dessen Abstand zum Nullpunkt. Es ist
also ggf. günstiger Transformationen in Polarkoordinaten zu definiere. Um dies
komfortabel berwerkstelligen zu können bietet Pan die Funktion polarXf :
polarxf :: Transform → Transform
polarxf t = fromPloar . t . toPolar
Hiermit kann swirlP auch durch eine Transformation für Punkte in Polarkoordinaten definiert werden:
swirlP :: Float → Transform
swirlP r p = (λ (ρ, θ) → (ρ, θ + ρ × 2 × π / r))
21
3 Funktionale Bildbeschreibung mit Pan
Abbildung 3.11: swirlT
3.4 Animationen
Pan unterstützt neben statischen Bildern auch Animationen. Hier werden die gleichen Konzepte benutzt wie bei der Darstellung von Bildern. Animationen sind
zeitabhängige Bilder. Die Zeitkoordinate ist prinzipiell unbeschränkt und kontinuierlich. Dies motiviert die folgenden beiden Typsynonyme:
type Time = Float
type Anim α = Time → Image α
Um eine Animation zu definieren muss man lediglich eine entsprechende Funktion
definieren. Mit der Region, die die positive Halbebene in x-Richtung darstellt,
und der swirl-Funktion, kann dann beispielsweise das in Abbildung 3.11 gezeigte,
zeitabhängige Bild erstellt werden.
xPos :: Region
xPos (x, y) = x > 0
swirlT :: Anim Bool
swirlT = (λ t → swirl (t2 ) xPos)
22
4 Pan als eingebettete Sprache
Die von Conal Elliott beschriebene, domänenspezifische Sprache Pan wurde von
ihm in Haskell implementiert und bis etwa 2000 aktiv gepflegt. Pan ist insofern
auch eine eingebettete Sprache, als das Haskell hier nicht nur um Methoden der
Bildbeschreibung erweitert wurde, sondern auch als Übersetzer verwendet wird,
um in Pan beschriebene Bilder für verschiedenen Zielsysteme zu übersetzen. Hierzu zählt unter anderem das Erzeugen von Standalone Programmen, die ein Bild
anzeigen können, Code der im Webbrowser Internet Explorer“ ausgeführt werden
”
kann und Plugincode für das Programm Adobe PhotoShop. Alle Varianten basieren auf DirectX zur Visualisierung und waren daher nur für Microsoft Windows
verfügbar. Pan legt den Schwerpunkt auf die funktionale Beschreibung von Bilden
und deren effizienten Darstellung.
Um eine effiziente Darstellung zu gewährleisten, werden die funktionalen Bilder
zunächst mit einem optimierenden Compiler nach C++, welches als Zwischensprache dient, übersetzt. In einem weiteren Übersetzungsvorgang wird dann Code für das jeweilige Zielsystem erzeugt. Optimierender Compiler heißt hier, dass
nicht eifach ein Haskellübersetzer wie der GHC zum Einsatz kommt. Das PanSystem hat stattdessen einen eigenen Haskell-Compiler, der für funktionale Bilder
optimiert ist. In Pan geschriebene Bilder werden also nicht einfach als HaskellSprachkonstrukte verwendet. Hier unterscheidet sich Pan von vielen anderen eingebetteten Sprachen, die eher eine API für die Hostsprache darstellen und zur
Laufzeit interpretiert werden.
Dies hat den Nachteil, dass nicht mehr alle Sprachkonstrukte der Hostsprache
in der anwendungsspezifischen Sprache zur Verfügung stehen. Im vorherigen Kapitel wurde beispielsweise weder Rekursion noch der Listendatentyp verwendet,
daher ist hierauf beim Design des Übersetzers auch nicht besondere Rücksicht zu
nehmen.
Der GHC wird nicht als Übersetzer verwendet, da die Autoren von Pan etliche
Optimierungen manuell gefunden und zunächst mit den rewrite rules des GHC
implementiert hatten. Diese kollidierten aber mit den sonstigen Optimierungen
des GHC und brachten daher nicht die gewünschten Verbesserungen.
Im Pan-System wird daher der Haskellcode der funktionalen Bilder zunächst in
einen Syntaxbaum umgewandelt, wie es auch ein Interpreter tun würde. Auf diesem wird während des Erstellens eine Bottom-Up-Analyse durchgeführt, um die
gewünschten Optimierungen zu realisieren.
23
4 Pan als eingebettete Sprache
Ein großer Vorteil dieser Methode ist, dass der Syntaxbaum für jedes darzustellende Bild erzeugt wird und nicht mehr modifiziert werden muss. Dies wird folgendermaßen zur Performanceverbesserung genutzt: Bei einem interpretierendem System
muss für jeden Punkt, der angezeigt werden soll, ein Funktionsaufruf stattfinden,
der den Farbwert aus der Bildfunktion ermittelt (i.A. zwei ineinandergeschachtelte
for“-Schleifen). Im Pan-System liegt diese Funktion bereits als Syntaxbaum vor.
”
Dieser wird in den Code, der die beiden Schleifen realisiert, eingesetzt, welcher
damit partiell ausgewertet werden kann. Hier kommt insbesonder loop-unrolling“
”
zum Tragen. Erst der so optimierte Code wird dann nach C++ übersetzt.
4.1 Weitere Entwicklung
Wie erwähnt wurde das Pan-System nur bis ins Jahr 2000 gepflegt. Die Sprache
Pan wurde mehrfach neuimplementiert. Eine Implementierung Pan#“ portierte
”
das Pan-System inklusive Compiler nach .NET“. Diese Version benutzte nicht
”
Haskell, sondern eine eigene, Haskell-ähnliche Spache als Hostsprache. Pan wurde
geringfügig erweitert und mit Pan# konnten auch Audiosignale (d.h. Bilder“ mit
”
eindimensionelem Definitionsbereich) beschrieben werden.
Eine weitere, bis etwa 2005 aktiv gepflegte Neuimplementierung ist Panic“; eben”
falls für Haskell. Panic verwendet zur Bilddarstellung das Toolkit wxWidgets“
”
und ist damit Plattform- und Betriebssystemunabhängig. Wie die Orginalimplementierung ist Panic eine eingebettete Sprache, bei der die funktionalen Bilder
nicht nur interpretiert werden, sondern übersetzt werden. Hier kommt allerdings
der Haskell-Übersetzer GHC zum Einsatz. Der Quelltext eines funktional beschriebenes Bildes wird geladen und an den GHC zur Üersetzung übergeben. Das dabei entstehenden Haskellmodul wird dann dynamisch geladen und an wxWidgets
übergeben. Eine Methode effect innerhalb des erzeugten Moduls wird von wxWidgets aufgerufen, um das eigentliche Bild zu erzeugen.
24
5 Fazit
Bei der Darstellung von Bilder als Funktionen über einem unendlichen, kontinuierlichen, zweidimensionalen Raum eignen sich naturgemäß funktionale Programmiersprachen wie Haskell als Beschreibungssprache. Es werden zwar kaum höhere
Konzepte der funktionalen Sprache wie Rekursion genutzt, da fast alle Aspekte funktionaler Bilder auf einfache Funktionskompositionen zurückgeführt werden können, aber hier bieten funktionale Sprachen sauberere Konzepte als andere
Sprachklassen.
Der größte Unterschied zwische funktionalen Bildern und den klassischen Beschreibungsformaten Raster- und Vektorgrafik ist jedoch, dass funktionale Bilder direkt programmiert werden, wohingegen die anderen Formate nur zur Speicherung
verwendet werden. Derartige Bilder werden i.A. in WYSIWYG1 -Editoren per
”
Mausklick“ erstellt. Hierin mag auch der Grund dafür liegen, dass keine der PanImplementierungen dauerhaft gepflegt wurde und sich auch kein anderes, funktionales Bildbeschreibungssystem durchgesetzt hat. Auch wenn funktionale Bilder
konzeptionell schön seien mögen, so werden Bilder doch eher mit dem Auge“ er”
stellt. Erstaunlicherweise werden von Pan auch kaum Konzepte der Vektorgrafiken
(Linienzüge, Bézierkurven) unterstützt, obwohl dies eigentlich naheliegend wäre.
Die vergleichsweise eher junge Disziplin der prozeduralen Texturen mag am ehesten ein Anwendungsgebiet für funktionale Bilder sein, denn diese müssen algorithmisch beschrieben werden und profitieren stark von Parametrisierbarkeit. Die
Standards, die sich hier bisher durchgesetzt haben, sind allerdings eher Hardwarenah und damit auch weit von funktionalen Bildern entfernt.
1
What you see is what you get.
25
6 Literatur
(1) http://conal.net/papers/functional-images/
(2) Conal Elliott. Functional Images. Aus Jeremy Gibbons, Oege de Moor (Herausgeber), The Fun of Programming Cornerstones of Computing. Palgrave,
2003.
(3) Conal Elliott, Sigbjørn Finne, Oege de Moor, Compiling Embedded Languages. Aus Journal of Functional Programming, 13(2), 2003.
(4) Jerzy Karczmarczuk. Functional approach to texture generation. Aus Shriram Krishnamurthi, C. R. Ramakrishnan (Herausgeber), Practical Aspects
of Declarative Languages, volume 2257 of Lecture Notes in Computer Science.
Springer, 2002.
(5) http://www.haskell.org/haskellwiki/Applications_and_libraries/Graphics#Pan
26
Herunterladen