Quiz/KlausurSS23: Difference between revisions

(Created page + added first 3 questions)
 
(→‎Learning Graph: update template)
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
The SS23 exam with solutions. Try to solve it by yourself before checking the solution.
The SS23 exam with solutions. Try to solve it by yourself before checking the solution.
'''missing LINK!'''
__TOC__
__TOC__
NOTE: All questions and answers are provided in german, as they appeared in the exam.
NOTE: All questions and answers are provided in german, as they appeared in the exam.
'''LINK!!'''


= Grundwissen (12P.) =
= Grundwissen (12P.) =
Line 9: Line 8:
Bitte geben Sie für jede der folgenden Aussagen an, ob sie wahr oder falsch ist, indem Sie "W" oder "F" davorschreiben. Für falsche Antworten gibt es Punktabzug.
Bitte geben Sie für jede der folgenden Aussagen an, ob sie wahr oder falsch ist, indem Sie "W" oder "F" davorschreiben. Für falsche Antworten gibt es Punktabzug.


{{QA|question=Der [[Item:Q21|Bresenham-Algorithmus]] dient dazu, Kanten in Bildern zu finden.|answer=Falsch. Der Bresenham-Algorithmus dient dazu, Linien zu ''zeichnen''.}}
# Der [[Item:Q21|Bresenham-Algorithmus]] dient dazu, Kanten in Bildern zu finden.
# Der [[Item:Q100|''OpenGL Immediate Mode'']] ist effizienter als Shader-basierte Alternativen.
# [[Item:Q99|OpenCV]] bietet Funktionen, um ein GUI zu implementieren.
# Der Painter's Algorithm zeichnet jeden Pixel genau einmal.
# [[Item:Q19|Bezier-Kurven]] können mit dem De-Hondt-Verfahren berechnet werden.
# Wenn man einen [[Item:Q20|Weichzeichnungs-Filter]] auf ein Bild anwendet, bleibt die durchschnittliche Helligkeit aller [[Item:Q105|Pixel]] im Bild gleich.


{{QA|question=Der [[Item:Q100|''OpenGL Immediate Mode'']] ist effizienter als Shader-basierte Alternativen.|answer=TODO}}
{{QA|question=Lösung|answer=Die Richtigen Antworten lauten:
 
# Falsch. Der Bresenham-Algorithmus dient dazu, Linien zu ''zeichnen''
{{QA|question=[[Item:Q99|OpenCV]] bietet Funktionen, um ein GUI zu implementieren.|answer=TODO}}
# TODO
 
# TODO
{{QA|question=Der Painter's Algorithm zeichnet jeden Pixel genau einmal.|answer=TODO}}
# TODO
 
# TODO
{{QA|question=[[Item:Q19|Bezier-Kurven]] können mit dem De-Hondt-Verfahren berechnet werden.|answer=TODO}}
# TODO
 
}}
{{QA|question=Wenn man einen [[Item:Q20|Weichzeichnungs-Filter]] auf ein Bild anwendet, bleibt die durchschnittliche Helligkeit aller [[Item:Q105|Pixel]] im Bild gleich.}}


== Außenseiter ==
== Außenseiter ==
Line 56: Line 59:


= 2D/3D-Grafik (39 P.) =
= 2D/3D-Grafik (39 P.) =
ntenstehender Code soll einen sich drehenden, pulsierenden 3D-Würfel zeigen, dessen Farbe sich kontinuierlich ändert.  
Untenstehender Code soll einen sich drehenden, pulsierenden 3D-Würfel zeigen, dessen Farbe sich kontinuierlich ändert.  
Leider sind einige Bugs im Code. Beheben Sie diese:
Leider sind einige Bugs im Code. Beheben Sie diese:


* drei Syntaxfehler (9 P.)
* drei Syntaxfehler (9 P.)
* irgendwie ist der Würfel defekt. Korrigieren Sie seine Definition (5 P.)
* irgendwie ist der Würfel defekt. Korrigieren Sie seine Definition (5 P.)
* die orthogonale Projektion sieht nicht schön aus. Verwenden Sie die perspektivische Projektion (5 P.)
* die orthogonale Projektion sieht nicht schön aus. Verwenden Sie die perspektivische [[Item:Q111|Projektion]] (5 P.)
 


Außerdem fehlt noch etwas Code. Ergänzen Sie diesen:
Außerdem fehlt noch etwas Code. Ergänzen Sie diesen:
 
* kontinuierliche Farbänderung - volle Punktzahl für loopenden Farbwechsel (15 P.)
* kontinuierliche Farbänderung - volle Punktzahl für loopenden Farbwechsel *(Tipp 1: HSV macht das einfacher. Tipp 2: wenn Sie fremden Code verwenden, achten Sie darauf, welchen Wertebereich die zurückgegebenen Werte haben. Tipp 3: Sie können die Werte in einem Numpy-Array einfach skalieren, indem Sie dieses mit einer Zahl multiplizieren)* (15 P.)
**Tipp 1: [[Item:Q73|HSV]] macht das einfacher.  
**Tipp 2: wenn Sie fremden Code verwenden, achten Sie darauf, welchen Wertebereich die zurückgegebenen Werte haben.  
**Tipp 3: Sie können die Werte in einem Numpy-Array einfach skalieren, indem Sie dieses mit einer Zahl multiplizieren.
* Lassen Sie den Würfel pulsieren, indem Sie seine Größe ändern (5 P.)
* Lassen Sie den Würfel pulsieren, indem Sie seine Größe ändern (5 P.)


Line 169: Line 175:


= Bildverarbeitung (35 P.) =
= Bildverarbeitung (35 P.) =
Laden Sie das Bild "objects2.jpg". Schreiben Sie Code, der folgendes macht:
* Zeichnen Sie die Positionen '''aller''' [[Item:Q15|ARUco-Marker]] ein. (10 P.)
* Ermitteln Sie, welche Farbe im Bild am häufigsten vorkommt und geben Sie diese aus. (5 P.)
* Übermalen Sie alle [[Item:Q15|ARUco-Marker]] mit dieser Farbe (5 P.)
* Markieren Sie alle blauen/violetten Legosteine, die im Bild sind. (15 P.)
(''Tipp: [[Item:Q73|HSV]], [[Item:Q139|Threshold]] um blaue Steine zu extrahieren, findContours() + contourArea() um Objekte der passenden Größe zu finden'').
{{CodeBlock|code=
orig = cv2.imread("images/objects2.jpg")
# magic needs to happen here!
show(orig)
|lang=python}}
----
= GLSL (20 P.) =
Zeichnen Sie eine simple 2D-Szene mit einem [[Item:Q62|Fragment Shader]] und ggf. [[Item:Q334|SDFs]].
Die Szene soll mindestens folgende Objekte oder ähnlich komplexe Elemente beinhalten:
* einen blauen Himmel / Hintergrund (5 P.)
* eine gelbbraune flache Wüste im Vordergrund (5 P.)
* eine kreisförmige Sonne am Himmel. (10 P.)
Schreiben Sie einen passenden Shader in [[Item:Q66|GLSL]].
Sie können dazu untenstehenden Code erweitern oder einen Shader in [https://www.shadertoy.com/ ShaderToy] schreiben und in die folgende Zelle einfügen.
{{QA|question=Code|answer=
{{CodeBlock|code=
# Hier ggf. ShaderToy-Code einfügen
|lang=python}}
{{CodeBlock|code=
vertex_code = """
    attribute vec2 position;
    void main(){ gl_Position = vec4(position, 0.0, 1.0); } """
fragment_code = """
    void main() {
        gl_FragColor = vec4(0.4, 1.0, 1.0, 1.0);
    } """
# -----------------------------------------------------------------------------
# Python & OpenGL for Scientific Visualization
# www.labri.fr/perso/nrougier/python+opengl
# Copyright (c) 2017, Nicolas P. Rougier
# Distributed under the 2-Clause BSD License.
# -----------------------------------------------------------------------------
import sys
import ctypes
import numpy as np
import OpenGL.GL as gl
import OpenGL.GLUT as glut
def display():
    gl.glClear(gl.GL_COLOR_BUFFER_BIT)
    gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
    glut.glutSwapBuffers()
def reshape(width,height):
    gl.glViewport(0, 0, width, height)
def keyboard( key, x, y ):
    if key == b'\x1b':
        sys.exit( )
# GLUT init
# --------------------------------------
glut.glutInit()
glut.glutInitDisplayMode(glut.GLUT_DOUBLE | glut.GLUT_RGBA)
glut.glutCreateWindow('Hello world!')
glut.glutReshapeWindow(512,512)
glut.glutReshapeFunc(reshape)
glut.glutDisplayFunc(display)
glut.glutKeyboardFunc(keyboard)
# Build data
# --------------------------------------
data = np.zeros(4, [("position", np.float32, 2)])
data['position'] = [(-1,+1), (+1,+1), (-1,-1), (+1,-1)]
# Build & activate program
# --------------------------------------
# Request a program and shader slots from GPU
program  = gl.glCreateProgram()
vertex  = gl.glCreateShader(gl.GL_VERTEX_SHADER)
fragment = gl.glCreateShader(gl.GL_FRAGMENT_SHADER)
# Set shaders source
gl.glShaderSource(vertex, vertex_code)
gl.glShaderSource(fragment, fragment_code)
# Compile shaders
gl.glCompileShader(vertex)
if not gl.glGetShaderiv(vertex, gl.GL_COMPILE_STATUS):
    error = gl.glGetShaderInfoLog(vertex).decode()
    print(error)
    raise RuntimeError("Shader compilation error")
               
gl.glCompileShader(fragment)
gl.glCompileShader(fragment)
if not gl.glGetShaderiv(fragment, gl.GL_COMPILE_STATUS):
    error = gl.glGetShaderInfoLog(fragment).decode()
    print(error)
    raise RuntimeError("Shader compilation error")               
# Attach shader objects to the program
gl.glAttachShader(program, vertex)
gl.glAttachShader(program, fragment)
# Build program
gl.glLinkProgram(program)
if not gl.glGetProgramiv(program, gl.GL_LINK_STATUS):
    print(gl.glGetProgramInfoLog(program))
    raise RuntimeError('Linking error')
# Get rid of shaders (no more needed)
gl.glDetachShader(program, vertex)
gl.glDetachShader(program, fragment)
# Make program the default program
gl.glUseProgram(program)
# Build buffer
# --------------------------------------
# Request a buffer slot from GPU
buffer = gl.glGenBuffers(1)
# Make this buffer the default one
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
# Upload data
gl.glBufferData(gl.GL_ARRAY_BUFFER, data.nbytes, data, gl.GL_DYNAMIC_DRAW)
# Bind the position attribute
# --------------------------------------
stride = data.strides[0]
offset = ctypes.c_void_p(0)
loc = gl.glGetAttribLocation(program, "position")
gl.glEnableVertexAttribArray(loc)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, buffer)
gl.glVertexAttribPointer(loc, 2, gl.GL_FLOAT, False, stride, offset)
# Enter the mainloop
# --------------------------------------
glut.glutMainLoop()
|lang=python}}
}}
{{QA|question=Lösung|answer=
{{CodeBlock|code=
# add Musterlösung
|lang=python}}
}}
----
= Learning Graph =
A Graph that shows all items mentioned in the exam and their direct dependencies.
{{Learnpath|quiz=Q390}}

Latest revision as of 09:11, 9 October 2023

The SS23 exam with solutions. Try to solve it by yourself before checking the solution. missing LINK!

NOTE: All questions and answers are provided in german, as they appeared in the exam.

Grundwissen (12P.)

Aussagen

Bitte geben Sie für jede der folgenden Aussagen an, ob sie wahr oder falsch ist, indem Sie "W" oder "F" davorschreiben. Für falsche Antworten gibt es Punktabzug.

  1. Der Bresenham-Algorithmus dient dazu, Kanten in Bildern zu finden.
  2. Der OpenGL Immediate Mode ist effizienter als Shader-basierte Alternativen.
  3. OpenCV bietet Funktionen, um ein GUI zu implementieren.
  4. Der Painter's Algorithm zeichnet jeden Pixel genau einmal.
  5. Bezier-Kurven können mit dem De-Hondt-Verfahren berechnet werden.
  6. Wenn man einen Weichzeichnungs-Filter auf ein Bild anwendet, bleibt die durchschnittliche Helligkeit aller Pixel im Bild gleich.
Lösung
Die Richtigen Antworten lauten:
  1. Falsch. Der Bresenham-Algorithmus dient dazu, Linien zu zeichnen
  2. TODO
  3. TODO
  4. TODO
  5. TODO
  6. TODO


Außenseiter

In jeder Zeile gehört ein Begriff nicht zu den anderen. Markiern Sie diesen fett.

  1. GLSL - GLEW - GLUT - GIMP
  2. Translucent - Ambient - Specular - Diffuse
  3. NeRF - SDF - Mesh - Graph
  4. Faltung - Rotation - Translation - Skalierung
  5. Vertex - Voxel - Vektor - Voltage
  6. ORB - ATOL - SURF - SIFT
Lösung
1: GIMP



Bildbearbeitung (30 P.)

Laden Sie das Bild "images/objects.jpg" (5 P.) und verbessern Sie es mittels OpenCV-Funktionen wie folgt:

  • der Kontrast soll maximiert werden (d.h. der hellste Pixel im Bild soll weiß werden, der dunkelste Pixel im Bild soll schwarz werden) (5 P.)
  • das Bild soll stark weichgezeichnet werden (5 P.)
  • über das Bild soll - halbtransparent und bildfüllend - der Schriftzug "CGBV" als Wasserzeichen gelegt werden (15 P.)

Verwenden Sie nur Numpy-/OpenCV-Funktionen.

Zeigen Sie das Bild im Notebook an (es muss nicht gespeichert werden)

img = None
# Hier könnte Ihre Lösung stehen!
show(img)



Lösung
# add Musterlösung


2D/3D-Grafik (39 P.)

Untenstehender Code soll einen sich drehenden, pulsierenden 3D-Würfel zeigen, dessen Farbe sich kontinuierlich ändert. Leider sind einige Bugs im Code. Beheben Sie diese:

  • drei Syntaxfehler (9 P.)
  • irgendwie ist der Würfel defekt. Korrigieren Sie seine Definition (5 P.)
  • die orthogonale Projektion sieht nicht schön aus. Verwenden Sie die perspektivische Projektion (5 P.)


Außerdem fehlt noch etwas Code. Ergänzen Sie diesen:

  • kontinuierliche Farbänderung - volle Punktzahl für loopenden Farbwechsel (15 P.)
    • Tipp 1: HSV macht das einfacher.
    • Tipp 2: wenn Sie fremden Code verwenden, achten Sie darauf, welchen Wertebereich die zurückgegebenen Werte haben.
    • Tipp 3: Sie können die Werte in einem Numpy-Array einfach skalieren, indem Sie dieses mit einer Zahl multiplizieren.
  • Lassen Sie den Würfel pulsieren, indem Sie seine Größe ändern (5 P.)
Code
# Define our simple 3D cube

#    1 - - - - - 7
#    - \         - \
#    -   3 - - - - - 5
#    -   -       -   -
#    -   -       -   -
#    -   -       -   -
#    0 - - - - - 6   -
#      \ -         \ -
#        2 - - - - - 4

cube_corners=[[-0.5, -0.5, -0.5],  # 0
              [-0.5, -0.5, 0.5],    # 1
              [-0.5, 0.5, -0.5],   # 2
              [-0.5, 0.5, 0.5],     # 3
              [0.5, 0.5, -0.5],    # 4
              [0.5, 0.5, 0.5],      # 5
              [0.5, -0.5, -0.5],   # 6
              [0.5, -0.5, 0.5]]     # 7

cube_edges = [(0, 2), (2, 4), (4, 6), (6, 0),
              (1, 3), (3, 5), (5, 7), (6, 1)
              (0, 1), (2, 3), (4, 5), (6, 2)]


def transform(points, angle=45):
    T = M([[1, 0, 0, 0],
           [0, 1, 0, 0],
           [0, 0, 1, 5],
           [0, 0, 0, 1]])
    
    cosa = cos(pi*angle/180)
    sina = sin(pi*angle/180)
    R = M([[cosa, 0, sina, 0],
           [0, 1, 0, 0],
           [-sina, 0, cosa, 0],
           [0, 0, 0, 1]])
 
    transformed_points = []
    for p in points:
        p_hom = p + [1]
        p_new = T @ R @ p_hom
        transformed_points.append(p_new)
    return transformed_points
        
def project(points, width=200, height=200, camera_distance = 2):
    P = M([[1, 0, 0, 0],
           [0, 1, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 1]])
    projected_points = []
    for p in points:
        p_new = P @ np.transpose(p)
        # assume points between -0.5 and 0.5
        x = int((float(p_new[0]/p_new[3]) + 0.5) * width)
        y = int((float(p_new[1]/p_new[3]) + 0.5) * width)
        projected_points.append((x,y))
    return projected_points

def cube(rotation=0, color=(255,255,255))
    projected_points = project(transform(cube_corners, rotation))
    cube_preview = np.zeros((200,200,3), np.unit8)
    for edge in cube_edges:
        p1 = projected_points[edge[0]]
        p2 = projected_points[edge[1]]
        cv2.line(cube_preview, p1, p2, color, 2)
    cube_preview = cv2.cvtColor(cube_preview, cv2.COLOR_BGR2RGB) # Converting BGR to RGB
    return(Image.fromarray(cube_preview))


from IPython.display import DisplayHandle
from time import sleep
d = DisplayHandle()
d.display(cube())


angle = 0
color = (255,255,0)
while True:
    angle += 5
    if angle > 360:
        angle = 0
    d.update(cube(angle, color))
    sleep(0.016)


Achtung: Hier muss man ggf. auf den "Interrupt Kernel"-Button klicken bzw. zweimal die Taste "I" drücken, um den Loop abzubrechen

Lösung
# add Musterlösung



Bildverarbeitung (35 P.)

Laden Sie das Bild "objects2.jpg". Schreiben Sie Code, der folgendes macht:

  • Zeichnen Sie die Positionen aller ARUco-Marker ein. (10 P.)
  • Ermitteln Sie, welche Farbe im Bild am häufigsten vorkommt und geben Sie diese aus. (5 P.)
  • Übermalen Sie alle ARUco-Marker mit dieser Farbe (5 P.)
  • Markieren Sie alle blauen/violetten Legosteine, die im Bild sind. (15 P.)

(Tipp: HSV, Threshold um blaue Steine zu extrahieren, findContours() + contourArea() um Objekte der passenden Größe zu finden).

orig = cv2.imread("images/objects2.jpg")
# magic needs to happen here!
show(orig)



GLSL (20 P.)

Zeichnen Sie eine simple 2D-Szene mit einem Fragment Shader und ggf. SDFs. Die Szene soll mindestens folgende Objekte oder ähnlich komplexe Elemente beinhalten:

  • einen blauen Himmel / Hintergrund (5 P.)
  • eine gelbbraune flache Wüste im Vordergrund (5 P.)
  • eine kreisförmige Sonne am Himmel. (10 P.)

Schreiben Sie einen passenden Shader in GLSL. Sie können dazu untenstehenden Code erweitern oder einen Shader in ShaderToy schreiben und in die folgende Zelle einfügen.

Code
# Hier ggf. ShaderToy-Code einfügen


vertex_code = """
    attribute vec2 position;
    void main(){ gl_Position = vec4(position, 0.0, 1.0); } """

fragment_code = """

    void main() {
        gl_FragColor = vec4(0.4, 1.0, 1.0, 1.0); 
    } """

# -----------------------------------------------------------------------------
# Python & OpenGL for Scientific Visualization
# www.labri.fr/perso/nrougier/python+opengl
# Copyright (c) 2017, Nicolas P. Rougier
# Distributed under the 2-Clause BSD License.
# -----------------------------------------------------------------------------
import sys
import ctypes
import numpy as np
import OpenGL.GL as gl
import OpenGL.GLUT as glut

def display():
    gl.glClear(gl.GL_COLOR_BUFFER_BIT)
    gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
    glut.glutSwapBuffers()

def reshape(width,height):
    gl.glViewport(0, 0, width, height)

def keyboard( key, x, y ):
    if key == b'\x1b':
        sys.exit( )

# GLUT init
# --------------------------------------
glut.glutInit()
glut.glutInitDisplayMode(glut.GLUT_DOUBLE


Lösung
# add Musterlösung



Learning Graph

A Graph that shows all items mentioned in the exam and their direct dependencies.

The learning path to Q390