Format:
Sie bearbeiten in Teams von 3 Personen wöchentlich ein Übungsblatt. Wenn Sie keinen Übungspartner haben, steht Ihnen einen Teams-Kanal zu Verfügung, der dem Zweck dient Teams zu bilden. Die Aufgaben werden in Theorie & Praxis in den Übungen diskutiert, d.h. Sie stellen Ihre erarbeiteten Lösung(en) vor und beantworten Fragen aus dem Publikum.
Klausurzulassung:
Es wird keine korrekturen der Abgaben geben. Dennoch ist es zwingend notwendig, dass alle Übungsaufgaben bearbeitet werden. Die notwendige Bedingung für die Klausurzulassung ist in diesem Semester eine erfolgreiche Präsentation einer Aufgabe während einer Übung pro Person. Falls dies aus technischen oder organisatorischen Gründen in Ausnahmefällen nicht möglich sein sollte, können Sie auch eine schriftliche Ausarbeitung einer Aufgabe abgeben.
Wenn Sie eine Aufgabe präsentieren möchten, geben Sie bitte am Freitagabend vor der Übung per Email Bescheid. Somit können wir Ihnen die Aufgabe zuteilen und somit sichergehen, dass nicht schon eine andere Gruppe diese Aufgabe vorstellt und Sie die Präsentation umsonst anfertigen.
Übungsblätter
Die Übungsblätter erscheinen Freitags. Die Aufgaben können gerne in den jeweiligen Lehreinheiten-Gruppen in Teams diskutiert werden.
Python:
Dieses Semester werden die praktischen Übungen in Python angeleitet. Verwendet werden vorallem die Packages NumPy, Matplotlib, Trimesh und Pyrender. Alle hier verwendeten Packages können einfach mit dem Befehl pip install [packagename]
installiert werden. Ich empfehle Ihnen sich vorallem mit Python und NumPy vertraut zu machen, falls dies noch nicht der Fall sein sollte. Der Trick ist hier den pythonic way anzustreben: vermeiden Sie wenn möglich immer explizite for-Schleifen und nutzen Sie stattdessen Python und NumPy Funktionen für einen eleganteren, besser lesbaren und insbesondere schnelleren Code. Falls der Sinn einer Aufgabe das implementieren einer solchen Funktion ist und die entsprechende Funktion nicht genutzt werden darf, ist dies explizit in der Aufgabe angegeben.
Als Erstes wollen wir uns mit der Rekonstruktion verrauschter Bilder befassen. Um uns abstrakteren Aufgaben witmen zu können, müssen wir zunächst einige einfache i/o Operationen durchführen.
dom.jpg
).img = plt.imread([path])
ein und prüfen Sie dessen Dimensionalität (img.shape
). Was bedeutet dieses Tupel?img
ein Numpy-Objekt und diese können einfach mit den Operatoren *
, +
, -
, /
verarbeitet werden (mehr dazu in der ersten Übung). img
-Objet im float
also [0-1]
Format zu haben, anstatt im int
also [0-255]
Format.img.dtype
ermitteln.255.0
(wichtig ist hier, dass wir eine Floating-Point rechnung durchführen, siehe nächstes Übungsblatt).plt.imshow(img)
ausgeben zu lassen.Als nächstes werden wir das Bild künstlich verrauschen - und das gleich zehn mal!
numpy.random.random([shape])
).Wir wollen nun aus den verrauschten Bildern so gut es geht das Originalbild wiederherstellen. Dafür verwenden wir einen alten Trick aus der Signaltheorie : Signalmittelung (engl. signal averaging).
Die Stärke des Rauschens hat durch die Signalmittelung offensichtlich abgenommen. Können Sie eine mathematische Begründung dafür liefern?
Abschließend möchten wir uns mit 3D Geometrien beschäftigen. Für den Anfang sollen Sie die altbekannten Modelle des Stanford 3D Scanning Repositories einlesen. Wir empfehlen den Stanford Bunny (50k Punkte), es gibt jedoch auch weitere, weitaus höher aufgelöste Modelle (Dragon (2,7kk Punkte), Armadillo (3,3kk Punkte), Happy Buddha (4,5kk Punkte) und für die sehr ambitionierten Lucy (58kk Punkte)). Mithilfe des packages Trimesh ist dies sehr einfach mit dem Befehl trimesh.load([path])
. Sie können nun das gelesene Mesh mit dem in trimesh integrierten viewer mit dem Befehl [mesh].show()
visualisieren.
Der in Trimesh integrierte viewer ist zwar einfach aufzurufen, erlaubt allerdings keine Animationen bzw. veränderungen des Meshes während der Visualisierung. Zu diesem Zweck werden wir zusätzlich das package Pyrender benutzen, dass glücklicherweise eine Trimesh Integration besitzt. Als einfaches Beispiel einer Animation möchten wir ein Mesh zum Nullpunkt "zusammenschrumpfen" und dann wieder expandieren lassen. Im Folgenden finden Sie ein kurzes Codebeispiel, wie Sie mit dem Pyrender viewer ein Trimesh mesh visualisieren.
import trimesh
import pyrender
import time
import numpy as np
import copy
# Lese das Mesh in Trimesh. Viele Formate werden von Trimesh nativ unterstützt, siehe Dokumentation
# Die Vertices sind in [mesh].vertices abgelegt und können auch verändert werden
# bunny_trimesh = trimesh.load("bunny.obj")
bunny_trimesh = trimesh.load("bun_zipper_res4.ply")
# Übertrage das Trimesh-Objekt in ein pyrender-Objekt
mesh = pyrender.Mesh.from_trimesh(bunny_trimesh)
# Erstelle Scene mit Kamera und Beleuchtung
scene = pyrender.Scene(ambient_light=[0.02, 0.02, 0.02], bg_color=[1.0, 1.0, 1.0])
node = pyrender.Node(name="Node", mesh=mesh)
scene.add_node(node)
# Starte Viewer in Thread
v = pyrender.Viewer(scene, run_in_thread=True)
Der Vorteil ist hierbei, dass der Viewer in einem separaten Thread weiterläuft, während im Hauptthread Veränderungen an der Scene vorgenommen werden können. In Pyrender ist es vorgesehen, dass man mit dem Befehl "[scene].set_pose([mesh_node],[pose])" eine lineare Transformation in homogenen Koordinaten (Pose) übergibt, die dann bei laufendem Viewer ausgeführt wird. Da wir allerdings direkt auf den Punktkoordinaten arbeiten wollen, können wir einfach das Mesh in jedem Visualisierungsschritt neu einlesen. Beachten Sie dabei den Threadlock des Renderers zu öffnen und zu schließen während Sie Veränderungen an der Scene vornehmen.
while(True):
time.sleep(0.05)
bunny_copy = copy.deepcopy(bunny_trimesh)
### Hier Veränderungen an bunny_copy vornehmen ###
mesh = Pyrender.Mesh.from_Trimesh(bunny_copy)
# Öffne den Threadlock
v.render_lock.acquire()
# Verändere die Scene
scene.remove_node(node)
node = Pyrender.Node(name="Node", mesh=mesh)
scene.add_node(node)
# Schließe Threadlock
v.render_lock.release()
Ergänzen Sie den Code um die oben beschriebene Animation zu erzeugen.