JGU Logo JGU Logo JGU Logo JGU Logo

Institut für Informatik

Markus Blumenstock
Wintersemester 2024/25

Mathematische Modellierung am Rechner II

Übungsblatt 5: Symmetrie
Letzte Änderung : 09:41 Uhr, 01 November 2020
Abnahmetermin : 16. Januar 2024


Symmetry is what we see at a glance; based on the fact that there is no reason for any difference...
Blaise Pascal, Pensées


Aufgabe 1: Symmetriegruppen, Erzeugendensysteme und Kristalle

Bewertung: 30 Punkte


VectorGUI Workspace VectorGUI Workspace VectorGUI Workspace VectorGUI Workspace
Ein Bild mit einer kristallographischen Symmetriestruktur
(Quelle: Wikimedia, nicht computergeneriert)


Aufgabe
Als erstes Versuchen wir uns an einer einfachen generierenden Struktur. Im Folgenden wollen wir ein Malprogramm schreiben, in dem wir mit der Maus malen können, und das Programm immer einen Kristall anzeigt, also alle Pixel symmetrisch kopiert.
Das Ergebnis könnte in etwa so aussehen.


Kristallographische Gruppen (Erklärungen zur Aufgabe)
In dieser Aufgabe bauen wir zweidimensionale Kristalle.
Ein Kristall ist eine Struktur, in der ein einzelnes Element immer wieder als Kopie vorkommt. Der Kristall wird erzeugt, indem man einige Transformationen definiert, die auf ein Objekt wirken. Danach lässt man alle möglichen Kombinationen wirken. In 2D geschieht das immer indem man zwei verschiedene Translationen (Verschiebungen, nicht in die gleiche Richtung!) definiert, die man in allen Kombinationen wiederholt. Dies erzeugt ein Gitter in der 2D Ebene.
Zusätzlich kann man, je nach Gitter, weitere Symmetrien einbauen: z.B. kann ein Gitter, das einen 90° Winkel zwischen den Translationsvektoren hat, weitere Spiegelsymmetrien aufweisen (bei krummen Winkeln ist das schwieriger, aber möglich). Sind die Zellen des Gitters quadratisch, ist eine 90° Drehsymmetrie (auch zusätzlich) möglich; bei einem 60° Winkel ergeben sich Drehungen um bis zu \(6\times 60^\circ\). Ein Beispiel für einen Kristall sieht man in der Abbildung am Ende der Aufgabe.
Etwas Mathematik
Schauen wir uns das ganze formal an: \(E(2)\) ist die Gruppe aller Euklidischen Isometrien. Auf deutsch: Alle starren Transformationen der 2D Ebene, also solche, die Abstände zwischen Punkten geometrischer Objekte nicht ändern.
Diese Transformationen sind genau durch (alle) Kombinationen von Translationen, Rotationen und Spiegelungen gegeben. Die Untergruppe der reinen Translationen ist kommutativ; sobald man Rotationen oder Spiegelungen hinzunimmt, ist dem nicht mehr so.
Wir betrachten nun Untergruppen von \(E(2)\), die Kristallgitter erzeugen. Man erzeugt eine Gruppe, indem man (ähnlich, aber nicht genauso wie bei einer Basis) Generatortransformationen (Basiselemente, sozusagen) angibt, die man dann in allen denkbaren Reihenfolgen hintereinander ausführt. In Formeln benutzt man die \(\langle ... \rangle\)- Notation: \[ \langle g_1,g_2,...,g_n \rangle := \{g_{i_1}^{j_1} \circ g_{i_2}^{j_2} \circ \cdots \circ g_{i_k}^{j_k} | i_1,...,i_n \in \{1,...,n\}, j_1,...,j_n \in \mathbb{Z} \} \] (Mit anderen Worten, enthält diese Menge alle Kombinationen der Gruppenoperationen \(g_1,\dots g_n\).)
Einen 2D-Kristall erzeugt man, indem man zwei nicht-kollineare Translationen als Generatoren verwendet. Zusätzlich kann man solche Rotationen und Spiegelungen hinzunehmen, die das von den beiden Translationen erzeugte Gitter wieder genau auf sich selbst abbilden (hier muss man ein bisschen probieren, oder den Wikipedia-Artikel über Wallpaper-Groups lesen).

Teilaufgaben zur Hilfestellung

  1. Schreiben Sie ein Zeichenprogramm, mit dem man auf einem Bitmapbild oder alternativ als Vektorgraphik mit der Maus in verschiedenen Farben malen kann.

    Schauen Sie sich dazu nochmals die entsprechenden vergangenen Aufgabenblätter an Bitmap: Aufgabe: Snake, Aufgabe: Mandelbrot, Aufgabe Artillery.
    Vektorgraphik: Aufgabe: GPS, Aufgabe: 3D-Visualisierung,
  2. Ergänzen Sie Ihr Programm so, dass automatisch alle symmetrischen Kopien mitgemalt werden.
    Transformieren Sie dazu den Punkt, den man mit der Maus bemalt so, dass alle symmetrischen Kopien entstehen. Testen Sie dies an verschiedenen Symmetriegruppen:
    1. Rotationsgruppen um \(360^\circ / k\) für ein \(k \in \mathbb{N}^{\ge1}\)
    2. Transformationsgruppen mit zwei translatorischen Generatoren
    3. Eine kristallographische Gruppe, die zwei translatorische mit einem kompatiblen rotatorischen und/oder Spiegelungsgenerator kombiniert. Suchen Sie sich ein schönes Beispiel aus.
  3. Theorie: Ist es möglich Skalierungen einzubauen? Was muss die kristallographische Struktur für Eigenschaften haben? Denken Sie zum Beispiel an die Koch-Kurve.

Referenzen

Wikipedia: „Ebene kristallographische Gruppen“
Wikipedia: „Wallpaper groups (engl.)”

Aufgabe 2: Symmetrieerkennung (auf Bildern)

Bewertung: 70 Punkte


In der letzten Aufgabe haben wir Symmetrien erzeugt. In dieser Aufgabe möchten wir die Symmetrien eines Bildes erkennen. Abstrakt gesprochen werden wir die Symmetriegruppe vorgeben und testen, welche Gruppenelemente für Teile des Bildes zutreffen.


Das Ziel dieser Aufgabe ist es, die ähnlichen Teile eines Bildes automatisch zu erkennen. Entwickeln Sie einen Algorithmus, der automatisch (anhand gegebener Symmetrieeigenschaften) bestimmt, welches die wiederholenden Elemente im Datensatz sind.
Ein (eher) einfaches Beispiel sind hierbei Hausfassaden. Das schöne an diesen ist, dass die Fenster (als Symmetrische Objekte) auf einem regelmäßigen Gitter auftreten. Unser Ziel is es das Bild (links) einzugeben und die symmetrischen (im Bild rechts eingefärbten) Teile automatisch zu ermitteln.


Foto einer Hausfassade.
Quelle: CMP Facade Dataset

Segmentiertes Bild der selben Hausfassade. Die Farbe gibt an wo ähnliche Bildteile im Foto zu finden sind. Quelle: Wie links.


Tipp:
Beginnen Sie in jeder Aufgabe mit dem einfachsten Datensatz und prüfen Sie bei Erfolg, ob der nächst schwierigere Datensatz auch „gelöst“ werden kann. Testen Sie ihr Programm ruhig immer wieder an einem der folgenden Datensätze.


Aufgaben

  1. Als erstes kümmern wir uns um eine geeignete GUI zur Visualisierung und — viel wichtiger — zum Debuggen. Sie können sich dabei an meinen Vorschlag halten (siehe Bild), oder aber sich etwas eigenes Überlegen.
    1. Wir benötigen als erstes eine Möglichkeit Bilder einzulesen und anzuzeigen.
    2. Zusätzlich benötigen wir eine Liste, in der wir häufig auftretende Teilbilder (a.k.a. Patches) auflisten.
    3. Zuletzt benötigen wir auch ein Ausgabefeld, auf dem wir Vorkommen von Bildpatches visualisieren können.
  2. Kümmern wir uns zuerst um den Basisalgorithmus.
    Konkret möchten wir erst einmal Bildpatches finden, um sie dann nach ihrem „Häufigkeit des Wiedervorkommens im Bild“ (in der Liste) sortieren zu können.

    Ein guter Ansatz ist es, erst einmal kleine Teilbilder von \(w \times w\) Pixeln (mit z.B. \(w=4,5,6,...,64\) aus dem Bild zu extrahieren. (Rechteckige Teilbilder wären hierbei auch eine Möglichkeit, diese könnten zusätzlich auch „längliche“ Symmetrien erkennen.)
    • Eine Möglichkeit wäre, einfach alle Teilbilder des Ursprungsbild zu extrahieren. Dies sind jedoch viele. (Wie viele?)
    • Teilbilder eines Bildes direkt zu extrahieren und zu testen wie oft es im Bild vorkommt ist natürlich sehr zeitintensiv (erst recht mit Python). Im Prinzip testen wir damit nämlich alle Paare von Bildpatches aus.
      Daher verfolgen wir einen sehr ähnlichen Ansatz: Wir probieren zufällige Kandidaten aus und merken uns wie gut diese auf das gesamte Bild passen. Nachher möchten wir die \(n\) besten Kandidaten ausgeben lassen. So können wir den Algorithmus im Grunde jederzeit abbrechen und stets die zu diesem Zeitpunkt besten Funde ausgeben lassen. Wir sparen dann zwar keine Laufzeit um den besten Patch zu finden, finden jedoch schneller „mittel-gute“ Ergebnisse. Also entweder:
      • es werden so lange zufällige Bildpatches in die Liste eingefügt und ausgewertet bis der User einen "Stop"-Button klickt.
      • etwas abgeschwächter: ziehe nur \(m>n\) Kandidaten und wähle von diesen aus.
  3. Bewertung der Patches Wir wissen schon einmal, dass alle Patches die wir aus Aufgabenteil 2 erhalten haben zumindest einmal im Bild vorkommen. Wir sind jedoch nur an solchen interessiert, die häufiger vorkommen. Diese guten Treffer wollen wir uns merken (die anderen können verworfen werden).

    Ähnlichkeit von Patches
    Ein guter Treffer soll hier bedeuten, dass viele Pixel der „Patches“ mit einigen Teilen des Ursprungsbildes übereinstimmen. Konkret bedeutet das, dass für zwei \(k\times k\) Patches \(p\) und \(q\), zählen, wie nah die Patches pixelweise eigentlich liegen, \[ s_1(p,q) = \sum_{0 \le i,j \le k} (p_{ij} - q_{ij})^2, \] wobei \(p_{ij}\) den \(j\)-ten Pixel in der \(i\)-ten Reihe bezeichnet.
    Eine andere Möglichkeit wäre zu testen wann die Summe der pixelweisen Produkte hoch ist, \[ s_2(p,q) = \sum_{0 \le i,j \le k} p_{ij} \cdot q_{ij}. \] (Probieren sie ruhig beides aus, der Aufwand dazu ist sehr gering. Wie unterscheiden sich die Ergebnisse?)

    Achtung
    Während \(s_1\) möglichst klein sein sollte, muss \(s_2\) möglichst groß sein, damit die beiden Patches \(p\) und \(q\) gleicher sind.

    Welche Patches sind Interessant?
    Bleibt noch die Frage nach der Bewertung eines Patches aus — wie gut passt ein Patch auf ein Bild? Mit welchen Patches \(q\) sollen wir einen Kandidaten \(p\) mittels obiger Formel bewerten?
    Dazu können wir entweder davon ausgehen, dass das Bild bereits eine Symmetriegruppe enthält oder wir suchen nach beliebigen Symmetrien.


    • Methode 1 (Symmetriegruppe gegeben):
      Ersteres könnte wie folgt aussehen. Wir gehen davon aus, dass der Patch mehrmals im Bild vorkommt, wir möchten nur herausfinden welche Symmetrien dies sind. Dazu probieren wir verschiedene translatorische Generatoren aus und zählen dann wie viele Pixel des symmetrisch generierten Patchs mit dem Bild übereinstimmen (und summieren dabei auch die Scores \(s\) durch alle generierten Patches).

      Sobald man die Translationen kennt, ist es nicht mehr so schwer, die angegebenen Objekte (hier Fassaden) zu erkennen.
      Wir bewerten hier also eher die Symmetrien selbst, als die Patches, letzteres bekommen wir dann nämlich Geschenkt. Warum ist das so?
    • Methode 2 (beliebige Symmetrien):
      Zufällige Paare von Patches durchzuprobieren und zu messen wie gut ein Patch auf das Bild passt wird leider zu langsam laufen. (Wie viele Paare von Patches der Größe \(k\) gibt es für ein quadratisches Bild mit einer Kantenlänge von \(w\) Pixeln?)

      Statt also immer Paare von Patches durchzuprobieren falten wir den Patch mit dem Bild:
      Das heißt wir setzen den Patch in eine Ecke des Bildes und bestimmen die Ähnlichkeit des Patches zum darunter liegenden Bild (z.B. mit dem Score \(s\)). Danach verschieben wir den Patch horizontal um einen Pixel (sukzessive über das ganze Bild, also auch für die anderen Zeilen) und bestimmen wieder die Ähnlichkeit des Patches. Wenn wir nun die Pixel des Bildes so einfärben, dass höhere Ähnlichkeiten beispielsweise heller sind, können wir so direkt die Vorkommnisse des Patches im Bild visualisieren.

      Am Ende Summieren wir die Ähnlichkeiten auf und definieren so die Gesamtähnlichkeit des Patches zum Bild als die aufsummierte Ähnlichkeit zu allen Bildteilen.
      (Wir suchen hier also allgemeinere Symmetrien im Bild, zum Beispiel Gesichter auf einem Gruppenfoto — alle ungefähr gleich groß, die Translationen sind jedoch nicht gleichmäßig und somit nicht Teil der Symmetriegruppe.)


    Hinweis:
    Sie können sich gerne für eines der beiden Herangehensweisen entscheiden. Der Arbeitsaufwand ist ähnlich hoch, beide Verfahren werden jedoch unterschiedliche Arten von Patches finden. Sie können natürlich auch gerne beides implementieren. ;)

    Tipp: Versuchen sie numpy zu verwenden, um die Ähnlichkeitsmaße der Patches zu implementieren. Dies ist sehr viel schneller.
  4. Zuletzt müssen wir nur noch die Vorkommen der Top-\(k\) (\(k\) kann vom User gewählt werden) Patches im Bild mit der gleichen Farbe einfärben.
  5. Überlegen Sie sich eine robustere Methode, die auch mit Fotos (etwas besser) funktioniert.
    Auch das Distanzmaß aus dem vierten Aufgabenteil hat noch einige Schwächen, wenn noch mehr Variabilität im Bild vorhanden ist. (Mit echten Fotos, etwa Datensatz 3 funktioniert das in der Regel nicht.)

    Nutzen Sie als Daten hierzu etwa Beispiele aus Datensatz 3 (oder denken erzeugen Sie sich selbst ein geeignetes Beispiel aus, das nicht ganz so schwer ist).


    Vorschläge:
    • Normalized Cross-Correlation NCC) ist deutlich robuster als das quadratische „SSD” Fehlermaß in Teil 2. Es ist nicht so schwer zu implementieren:
      Es seien \(p_{ij}\) der Farbwert an der Stelle \((x,y)\) im Bild-Patch (mit \(N\times N\) Pixeln) und \(q_{ij}\) entsprechend der Wert eines zweiten Patches. \[ \frac{1}{N^2}\sum_{0 \le i,j \le N} \frac{1}{σ_pσ_q} \left( p_{ij} - \overline{p} \right) \cdot \left( q_{ij} - \overline{q} \right), \] \(\overline{p}\) bzw. \(\overline{q}\) bezeichnet den Mittelwert des Patches, \(σ_p\) bzw. \(σ_q\) die Standardabweichung.

      Frage für den interessierten Leser: Was macht diese Funktion eigentlich bzw. warum ist dies besser als in Teilaufgabe 1 oder 2?
    • Es hilft auch, bei der Suche nach gleichen Stellen im Bild immer alle Gitterelemente gleichzeitig zu vergleichen; dadurch mitteln sich Fehler raus und man bekommt ein besseres Ergebnis. Das Ganze ist aber deutlich schwieriger zu implementieren.
    • Fortgeschrittene Methode: Besonders gut funktionieren Gradientenhistogramme (HOG).
      Videoerklärung auf YouTube
      Erklärung:
      • Man extrahiert zunächst scharfe Kanten und vergleicht nur diese (statt alle Pixel im Bildausschnitt).
      • Außerdem merkt man sich nicht genau wo die Gradienten sich befinden (man mittelt über z.B. 8x8 Pixel) und auch nicht genau, in welche Richtung diese Zeigen (hier mittelt man Orientierungen in einem 45° Raster in jeder 8x8 Pixel Zelle). Dadurch werden Verformungen der Objekte ignoriert.
      Wichtig:
      Für diese Übungsaufgabe ist es nicht nötig, ein solches schwieriges Verfahren zu implementieren.
      In Aufgabe 2.5 wird lediglich verlangt, ein wenig mit Ideen zu experimentieren, die das Verfahren robuster machen könnten, und über die Erfahrungen in der Übungsstunde zu berichten. Der Vollständigkeit halber trotzdem der Hinweis: Für diese HOG-Repräsentationen gibt es Python-Bibliotheken (z.B. OpenCV oder scikit-image).