Ein Objektfilm besteht aus einzelnen Aufnahmen eines ruhenden Objektes aus verschiedenen Positionen. Durch das Verketten der Aufnahmen soll ein möglichst ruhiger Bewegungsablauf um das Objekt und damit ein räumlicher Eindruck entstehen.
Werden die Aufnahmepositionen nicht sorgfältig gewählt, so wirkt der Bewegungsablauf störend verwackelt. Hier wird versucht, auch aus unregelmäßigen Aufnahmepositionen brauchbare Bewegungsabläufe zu erzeugen.
Abbildung 1 und 2 zeigen zwei Beispielbilder aus unterschiedlichen Aufnahmepositionen. Die unregelmäßigen Vierecke und der einzelne Punkt definieren die Transformation und werden manuell ausgewählt. In der folgenden Tabelle können die einfache Überblendung und eine passende Transformation miteinander verglichen werden.
| Übergang | GIF-Animation |
|---|---|
| Überblendung | GIF-Animation (6 MB), GIF-Animation (22 MB) |
| Transformation | GIF-Animation (6 MB), GIF-Animation (21 MB) |
In der folgenden Tabelle sind der zum Beispiel passende Objektfilm und weitere Objektfilme angegeben.
Der Film vom Neuen Rathaus in Hannover zeigt, dass auch große Objekte möglich sind. Architektur ist dabei ein gnädiges Motiv, da ausreichend Flächen zur Markierung geeigneter (koplanarer) Punkte zu finden sind.
Die beiden Objektfilme der Modellbahn zeigen die unterschiedliche Wirkung von Transformationen, die einmal auf den Hintergrund und das andere Mal auf das Objekt bezogen sind.
Beim Objektfilm mit dem Motorrad werden teilweise sehr unterschiedliche Kamerapositionen transformiert.
| Objektfilm | Beschreibung |
|---|---|
| Flash-Animation | Objektfilm zum Beispiel oben |
| Flash-Animation | Rotation um großes Objekt (Neues Rathaus in Hannover) |
| Flash-Animation | Kamerafahrt, Hintergrund wird transformiert (Modellbahn) |
| Flash-Animation | Kamerafahrt, Objekt wird transformiert (Modellbahn) |
| Flash-Animation | unregelmäßige Rotation (Motorrad) |
Betrachten wir zwei Bilder (zum Beispiel Abbildungen 1 und 2) mit unterschiedlicher Aufnahmeposition der gleichen Szene. Die Bildinhalte werden hier als projektive Abbildungen der 3D-Szene aufgefasst. In beiden Bildern werden je vier paarweise identische Punkte markiert. Diese 8 Punkte sind die Bilder zweier projektiver Abbildungen von 4 ursprünglichen Szene-Punkten. Wichtig ist, dass diese 4 ursprünglichen Szene-Punkte in einer Ebene im Raum liegen.
Projektive Abbildungen von Ebenen im Raum können durch ganz bestimme Abbildungen ineinander überführt werden1). Die Benennung dieser Transformationen ist nicht ganz einheitlich: homographic transformations, linear fractional transformations, fractional linear transformations, möbius transformation etc.
Diese Abbildungen haben im vorliegenden Fall 8 Freiheitsgrade. Aus den Bildern der vier ursprünglichen koplanaren Punkte (4×2 Koordinaten) werden diese 8 Parameter bestimmt.
Es werden also zwei Sachverhalte ausgenutzt:
Der fünfte grüne Punkt in den Abbildungen 1 und 2 markiert jeweils den Punkt, der während der Transformation nicht bewegt werden soll – dadurch scheint das Objekt im Raum zu ruhen.
Es sind da immer nur Details, die den Teufel verbergen: Flex kann Hardware-beschleunigt (beginBitmapFill2)) nur mit affin-linearen Abbildungen umgehen3). Die nicht-linearen „fractional linear transformations“ müssen also linearisiert werden. Das sind die Dinge, die man liebt oder hasst.
Das Bild wird trianguliert. Jedes Teildreieck bekommt seine eigene affin-lineare Transformation zugewiesen, die dann gemeinsam die nicht-lineare Transformation des Gesamtbildes approximieren.
Wir betrachten ein Teildreieck. Die Flex-Matrix4) der affin-linearen Abbildung hat die 6 Parameter a, b, c, d, tx und ty. Ein Dreieck hat die Punkte p1, p2 und p3. Wir betrachten für ein Dreieck der Triangulation das Urbild-Dreieck a und das Bild-Dreieck b. Für die xy-Koordinaten der Eckpunkte der Dreiecke schreiben wir dann (p1ax,p1ay), (p2ax,p2ay) usw. bis (p3bx,p3by). Nun müssen aus den Dreieckskoordinaten die Komponenten der Transformnationsmatrix berechnet werden. In homogenen Koordinaten5) ergibt sich folgendes Gleichungssystem:
Dieses System löst man nach a, b, c, d, tx und ty auf. Mit Mathematica ist das schnell gemacht:
pb = {p1bx, p1by, 1, p2bx, p2by, 1, p3bx, p3by, 1};
pa = {p1ax, p1ay, 1, p2ax, p2ay, 1, p3ax, p3ay, 1};
mm = {
{a, b, tx, 0, 0, 0, 0, 0, 0},
{c, d, ty, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, a, b, tx, 0, 0, 0},
{0, 0, 0, c, d, ty, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, a, b, tx},
{0, 0, 0, 0, 0, 0, c, d, ty},
{0, 0, 0, 0, 0, 0, 0, 0, 1}
};
t = {a, b, c, d, tx, ty} /. Solve[pb == mm.pa, {a, b, c, d, tx, ty}]
Map[CForm, t]
Die gefundene Lösung steckt man in eine Flex-Funktion triangle mit 12 Parametern (2x3x2 Eckpunkt-Koordinaten), mit der man dann die Triangulation aufbaut:
private function triangle(
dest:Graphics,bitmap:Bitmap,
p1ax:Number, p1ay:Number,
p2ax:Number, p2ay:Number,
p3ax:Number, p3ay:Number,
p1bx:Number, p1by:Number,
p2bx:Number, p2by:Number,
p3bx:Number, p3by:Number):void
{
var m:Matrix = new Matrix();
m.a = -((p1bx*p2ay - p1ay*p2bx - p1bx*p3ay + p2bx*p3ay + p1ay*p3bx - p2ay*p3bx)/
(p1ay*p2ax - p1ax*p2ay - p1ay*p3ax + p2ay*p3ax + p1ax*p3ay - p2ax*p3ay));
m.c = -((p1bx*p2ax - p1ax*p2bx - p1bx*p3ax + p2bx*p3ax + p1ax*p3bx - p2ax*p3bx)/
(-(p1ay*p2ax) + p1ax*p2ay + p1ay*p3ax - p2ay*p3ax - p1ax*p3ay + p2ax*p3ay));
m.b = -((p1by*p2ay - p1ay*p2by - p1by*p3ay + p2by*p3ay + p1ay*p3by - p2ay*p3by)/
(p1ay*p2ax - p1ax*p2ay - p1ay*p3ax + p2ay*p3ax + p1ax*p3ay - p2ax*p3ay));
m.d = -((-(p1by*p2ax) + p1ax*p2by + p1by*p3ax - p2by*p3ax - p1ax*p3by + p2ax*p3by)/
(p1ay*p2ax - p1ax*p2ay - p1ay*p3ax + p2ay*p3ax + p1ax*p3ay - p2ax*p3ay));
m.tx = -((p1bx*p2ay*p3ax - p1ay*p2bx*p3ax - p1bx*p2ax*p3ay + p1ax*p2bx*p3ay + p1ay*p2ax*p3bx - p1ax*p2ay*p3bx)/
(-(p1ay*p2ax) + p1ax*p2ay + p1ay*p3ax - p2ay*p3ax - p1ax*p3ay + p2ax*p3ay));
m.ty = -((-(p1by*p2ay*p3ax) + p1ay*p2by*p3ax + p1by*p2ax*p3ay - p1ax*p2by*p3ay - p1ay*p2ax*p3by + p1ax*p2ay*p3by)/
(p1ay*p2ax - p1ax*p2ay - p1ay*p3ax + p2ay*p3ax + p1ax*p3ay - p2ax*p3ay));
dest.beginBitmapFill(bitmap.bitmapData , m, true, true);
dest.moveTo(p1bx,p1by);
dest.lineTo(p2bx,p2by);
dest.lineTo(p3bx,p3by);
dest.endFill();
}