Einblicke in die Industrie

Aktuelle Entwicklungen, Trends und Fachanalysen aus Bau, Technik und digitaler Medienwelt – inklusive Updates zu Drohneneinsätzen, Solarwartung und Weblösungen.

Veröffentlicht am

30 November 2025

Autor

Maurice Lübeck

Überblick: Pipeline vom Bild zur sparse Pointcloud

Sehr grob besteht eine typische Pipeline aus:

  1. Bilder einlesen (RAW/JPG/PNG)
  2. Kameramodelle & Intrinsics bestimmen oder laden
  3. Undistortion: verzerrte Bilder → „Pinhole“-Ansicht
  4. Feature-Extraktion (z. B. SIFT) pro Bild
  5. Feature-Matching zwischen Bildpaaren
  6. Geometrische Verifikation: Fundamentalmatrix/Essentialmatrix, RANSAC
  7. Relative Kameraposen schätzen (Rotation $R_i$, Translation $t_i$)
  8. Triangulation von 3D-Punkten aus 2D-Korrespondenzen
  9. Bundle Adjustment: globales Optimieren von Kameras und 3D-Punkten
  10. Ergebnis: sparse Point Cloud + Kamera-Posen

Im Folgenden bauen wir das Schritt für Schritt mathematisch auf.


1. Pinhole-Kamera: von der 3D-Welt zum 2D-Sensor

1.1 Koordinatensysteme

Weltkoordinaten (globales Bezugssystem):

$$ X_w = \begin{pmatrix} X_w \\ Y_w \\ Z_w \end{pmatrix} $$

Kamerakoordinaten (Ursprung im Kamerazentrum):

$$ X_c = \begin{pmatrix} X_c \\ Y_c \\ Z_c \end{pmatrix} $$

Konvention (OpenCV-typisch):

  • $X$: nach rechts
  • $Y$: nach unten
  • $Z$: nach vorne aus der Kamera heraus

Extrinsics transformieren Welt → Kamera:

$$ X_c = R X_w + t $$

mit

  • $R \in SO(3)$: Rotationsmatrix (3×3)
  • $t \in \mathbb{R}^3$: Translation

In homogenen Koordinaten:

$$ \begin{pmatrix} X \\ Y \\ Z \end{pmatrix} = \begin{pmatrix} R & t \end{pmatrix} \begin{pmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{pmatrix} $$

1.2 Projektion in den Normalraum

Pinhole-Modell: Die Bildebene liegt bei $Z = 1$ vor der Kamera.

Ein 3D-Punkt im Kameraraum $X_c = (X, Y, Z)^T$ wird projiziert zu:

$$ x_n = \frac{X}{Z}, \qquad y_n = \frac{Y}{Z} $$

$(x_n, y_n)$ nennt man normierte Bildkoordinaten (Normalraum).

Bildlich: Vom Kamerazentrum aus geht ein Strahl durch den Punkt $(X,Y,Z)$ und schneidet die Ebene $Z=1$. Dieser Schnittpunkt hat die Koordinaten $(x_n, y_n, 1)$.

1.3 Intrinsische Matrix $K$: von Normalraum zu Pixeln

Der Sensor hat Pixel, also skalieren wir in Pixelkoordinaten. Die intrinsische Matrix ist:

$$ K = \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} $$

mit

  • $f_x, f_y$: Brennweiten in Pixeln (Skalierung in $x$- und $y$-Richtung)
  • $c_x, c_y$: Principal Point (typischerweise die Bildmitte)

Übergang zu Pixeln:

$$ \begin{pmatrix} u \\ v \\ 1 \end{pmatrix} = K \begin{pmatrix} x_n \\ y_n \\ 1 \end{pmatrix} = \begin{pmatrix} f_x x_n + c_x \\ f_y y_n + c_y \\ 1 \end{pmatrix} $$

Also ganz konkret:

$$ u = f_x x_n + c_x, \qquad v = f_y y_n + c_y $$

1.4 Gesamtprojektionsmatrix $P$

Die komplette Kette Welt → Kamera → Normalraum → Pixel kann als eine 3×4-Matrix geschrieben werden:

$$ \lambda \begin{pmatrix} u \\ v \\ 1 \end{pmatrix} = K [R \mid t] \begin{pmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{pmatrix} = P \begin{pmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{pmatrix} $$

mit

$$ P = K [R \mid t] \in \mathbb{R}^{3 \times 4} $$

$\lambda$ ist ein homogener Skalierungsfaktor. Nach der Multiplikation normieren wir:

$$ (u, v, 1)^T = \frac{1}{w'} (u', v', w')^T = \frac{1}{w'} \, P \, \begin{pmatrix} X_w \\ Y_w \\ Z_w \\ 1 \end{pmatrix} $$

1.5 Beispielprojektion

Gegeben:

  • Bildgröße: $1920 \times 1080$
  • $f_x = f_y = 1000$
  • $c_x = 960,\; c_y = 540$ (Bildmitte)
  • Keine Rotation/Translation: $R = I,\; t = 0$

3D-Punkt:

$$ X = \begin{pmatrix} 1 \\ 1 \\ 5 \end{pmatrix} $$

Normierte Koordinaten:

$$ x_n = \frac{1}{5} = 0.2, \qquad y_n = \frac{1}{5} = 0.2 $$

Pixelprojektion:

$$ u = 1000 \cdot 0.2 + 960 = 1160 $$ $$ v = 1000 \cdot 0.2 + 540 = 740 $$

Ergebnis: Der Punkt erscheint bei Pixel $(1160, 740)$.


2. OpenCV-Kameramodell & Verzerrung

Reale Linsen sind nicht ideal. OpenCV modelliert typischerweise:

  • radiale Verzerrung: $k_1, k_2, k_3, \dots$
  • tangentiale Verzerrung: $p_1, p_2$

Ausgangspunkt sind die normierten Koordinaten $(x_n, y_n)$.

2.1 Radiale Verzerrung

Radius zum Bildzentrum:

$$ r^2 = x_n^2 + y_n^2 $$

Radial verzerrte Koordinaten:

$$ x_r = x_n \left(1 + k_1 r^2 + k_2 r^4 + k_3 r^6 + \dots \right) $$ $$ y_r = y_n \left(1 + k_1 r^2 + k_2 r^4 + k_3 r^6 + \dots \right) $$

Interpretation:

  • $k_1 > 0$: tonnenförmig (Ränder „nach außen“)
  • $k_1 < 0$: kissenförmig (Ränder „nach innen“)

2.2 Tangentiale Verzerrung

Wenn Linse/Sensor schief sitzt oder dezentriert ist:

$$ x_t = 2 p_1 x_n y_n + p_2 (r^2 + 2 x_n^2) $$ $$ y_t = p_1 (r^2 + 2 y_n^2) + 2 p_2 x_n y_n $$

Gesamtverzerrte Normalkoordinaten:

$$ \tilde{x} = x_r + x_t, \qquad \tilde{y} = y_r + y_t $$

Anschließend kommen wieder die Intrinsics:

$$ u = f_x \tilde{x} + c_x, \qquad v = f_y \tilde{y} + c_y $$


3. Undistortion – warum COLMAP das macht

Viele 3D-Pipelines (SfM, NeRF, Gaussian Splatting) arbeiten lieber mit einem einfachen Pinhole-Modell ohne Verzerrung. Deshalb:

  • Eingangsbilder sind verzerrt (OpenCV-Parameter $k_i, p_i$).
  • Wir erzeugen daraus „undistorted“ Bilder.
  • Die neuen Bilder haben ein reines Pinhole-Kameramodell.

3.1 Idee

  1. Für jeden Pixel im neuen undistorted Bild definiert man Normalkoordinaten $(x_n', y_n')$ mit einer neuen Matrix $K'$.
  2. Das Verzerrungsmodell wird rückwärts angewendet, um den dazugehörigen verzerrten Normalkoordinatenpunkt $(\tilde{x}, \tilde{y})$ im alten Modell zu finden.
  3. Mit der alten Matrix $K$ wird $(u_{\text{alt}}, v_{\text{alt}})$ berechnet.
  4. Farbwerte werden aus $(u_{\text{alt}}, v_{\text{alt}})$ entnommen (bilineare Interpolation) und nach $(u', v')$ im neuen Bild geschrieben.

Ergebnis:

  • Neue, undistorted Bilder
  • Neue Intrinsics $K'$ (ohne Verzerrung)
  • Kameramodell: z. B. PINHOLE oder SIMPLE_PINHOLE in COLMAP

4. Feature-Extraktion: von Pixeln zu Merkmalpunkten

Um 3D aufzubauen, brauchen wir Bildpunkte, die sich in vielen Bildern wiederfinden. Typischer Ansatz: SIFT.

4.1 SIFT ganz grob

  • Wir bauen einen Scale-Space: Bild $I(x,y)$ wird mit Gaußfiltern verschiedener $\sigma$ gefiltert.

$$ L(x,y,\sigma) = G(x,y,\sigma) * I(x,y) $$

$$ G(x,y,\sigma) = \frac{1}{2\pi\sigma^2} \exp\left( -\frac{x^2 + y^2}{2\sigma^2} \right) $$

  • Berechnung Difference-of-Gaussians (DoG):

$$ D(x,y,\sigma) = L(x,y,k\sigma) - L(x,y,\sigma) $$

Lokale Maxima/Minima in $(x,y,\sigma)$ sind SIFT-Keypoints.

Für jeden Keypoint:

  • Gradientenrichtung und -stärke bestimmen.
  • Ein Histogramm der Richtungen erstellen.
  • Einen 128-dimensionalen Deskriptor erzeugen. (4×4 Zellen × 8 Richtungsbins)

5. Feature-Matching zwischen Bildern

In Bild A gibt es Deskriptoren $d_i^A \in \mathbb{R}^{128}$ und in Bild B $d_j^B \in \mathbb{R}^{128}$.

Distanz:

$$ \mathrm{Dist}(d_i^A, d_j^B) = \lVert d_i^A - d_j^B \rVert_2 $$

Für jeden Deskriptor in A:

  1. Suchen wir die zwei nächsten Nachbarn in B.
  2. Nutzen den Lowe-Ratio-Test: $$ \frac{\lVert d_1 \rVert}{\lVert d_2 \rVert} < \tau $$ typischerweise $\tau \approx 0{,}7$–$0{,}8$.

So entstehen viele Punktkorrespondenzen

$$ x_i^A \leftrightarrow x_j^B, \quad x = (u, v, 1)^T. $$


6. Epipolargeometrie: Fundamentalmatrix und Essentialmatrix

6.1 Fundamentalmatrix $F$

Seien $x$ und $x'$ korrespondierende Pixelpunkte in zwei Bildern:

$$ x = \begin{pmatrix} u \\ v \\ 1 \end{pmatrix}, \quad x' = \begin{pmatrix} u' \\ v' \\ 1 \end{pmatrix} $$

Im idealen Fall gilt die Epipolargeometrie:

$$ x'^T F x = 0 $$

$F \in \mathbb{R}^{3 \times 3}$ hat Rang 2 und beschreibt die Beziehung zwischen den beiden Bildern ohne Intrinsics.

Zu einem Punkt $x$ in Bild 1 gehört in Bild 2 eine Epipolarlinie

$$ l' = F x $$

und der korrespondierende Punkt $x'$ muss auf $l'$ liegen:

$$ x'^T l' = 0. $$

6.1.1 Schätzung von $F$

  • Nutzung ausreichend vieler Punktpaare $x \leftrightarrow x'$.
  • 8-Punkt-Algorithmus oder 7-Punkt-Algorithmus.
  • RANSAC:
    • Ziehe zufällig 8 Korrespondenzen.
    • Schätze $F$.
    • Prüfe Epipolarfehler für alle Paare.
    • Wähle das $F$ mit den meisten Inliers.

6.2 Essentialmatrix $E$

Wenn Intrinsics $K, K'$ bekannt sind, normalisiert man die Bildpunkte:

$$ \hat{x} = K^{-1} x, \qquad \hat{x}' = K'^{-1} x' $$

Dann gilt:

$$ \hat{x}'^T E \hat{x} = 0 $$

mit

$$ E = K'^T F K $$

Struktur von $E$:

$$ E = [t]_\times R $$

  • $R$: Rotation von Kamera 1 zu Kamera 2
  • $t$: Translation (Richtung) zwischen Kamerazentren
  • $[t]_\times$: Kreuzprodukt-Matrix von $t$

7. Relative Kamerapose aus $E$

7.1 SVD von $E$

$$ E = U \Sigma V^T $$

Man erzwingt

$$ \Sigma = \mathrm{diag}(1, 1, 0) $$

Mit einer Hilfsmatrix

$$ W = \begin{pmatrix} 0 & -1 & 0 \\ 1 & 0 & 0 \\ 0 & 0 & 1 \end{pmatrix} $$

erhält man zwei Rotationen:

$$ R_1 = U W V^T, \qquad R_2 = U W^T V^T $$

Die Translation (bis auf Skala) ist

$$ t = U \begin{pmatrix} 0 \\ 0 \\ 1 \end{pmatrix} $$

Damit ergeben sich 4 mögliche Kombinationen:

  • $(R_1, \phantom{-}t)$
  • $(R_1, -t)$
  • $(R_2, \phantom{-}t)$
  • $(R_2, -t)$

Die richtige Kombination ist die, bei der die triangulierten 3D-Punkte vor beiden Kameras liegen ($Z > 0$ in beiden Kamerasystemen).


8. Triangulation eines 3D-Punktes (zwei Bilder)

Gegeben:

  • Projektionsmatrizen $P_1, P_2$
  • Pixelpunkte $$ x_1 = (u_1, v_1, 1)^T,\quad x_2 = (u_2, v_2, 1)^T $$

Gesucht: 3D-Punkt $X = (X, Y, Z, 1)^T$.

8.1 Lineares Gleichungssystem $AX = 0$

Man schreibt $P_i$ zeilenweise als

$$ P_i = \begin{pmatrix} p_{1,i}^T \\ p_{2,i}^T \\ p_{3,i}^T \end{pmatrix} $$

Aus $x_i \sim P_i X$ folgen zwei Gleichungen:

$$ (u_i p_{3,i}^T - p_{1,i}^T) X = 0 $$ $$ (v_i p_{3,i}^T - p_{2,i}^T) X = 0 $$

Für zwei Kameras erhält man 4 Zeilen → Matrix $A \in \mathbb{R}^{4 \times 4}$ mit

$$ A X = 0 $$

8.2 Lösung per SVD

$$ A = U \Sigma V^T $$

Gesucht ist der Vektor $X$, der zum kleinsten Singulärwert gehört → letzte Spalte von $V$.

Homogene Normierung:

$$ X = \begin{pmatrix} X \\ Y \\ Z \\ W \end{pmatrix} \;\Rightarrow\; \tilde{X} = \begin{pmatrix} X/W \\ Y/W \\ Z/W \end{pmatrix} $$

$\tilde{X}$ ist der kartesische 3D-Punkt.

8.3 Durchgerechnetes Beispiel (wie oben)

Intrinsics:

$$ K = \begin{pmatrix} 1000 & 0 & 960 \\ 0 & 1000 & 540 \\ 0 & 0 & 1 \end{pmatrix} $$

Kamera 1: $R_1 = I,\; t_1 = 0$

$$ P_1 = K [I \mid 0] $$

Kamera 2: um 1 m nach rechts verschoben

$$ C_2 = (1,0,0)^T,\quad t_2 = -C_2 = (-1,0,0)^T,\quad P_2 = K [I \mid t_2] $$

Wahrer 3D-Punkt:

$$ X_\text{true} = \begin{pmatrix} 2 \\ 1 \\ 5 \\ 1 \end{pmatrix} $$

Projektion in Kamera 1 führt zu $x_1 = (1160, 740, 1)^T$, in Kamera 2 zu $x_2 = (960, 740, 1)^T$.

Aufbau von $A$ mit diesen Werten und $P_1, P_2$ → SVD liefert

$$ X_\text{est} \approx (2, 1, 5, 1)^T $$

Der Fehler ist numerisch praktisch 0.

8.4 Einfluss von Pixelrauschen

Gemessene Pixel sind nicht perfekt:

$$ u_\text{mess} = u_\text{true} + \Delta u, \qquad v_\text{mess} = v_\text{true} + \Delta v $$

$$ \Delta u, \Delta v \sim \mathcal{N}(0, \sigma^2) $$

  • $\sigma = 0{,}5$ Pixel → Fehler oft im mm-Bereich
  • $\sigma = 2{,}0$ Pixel → Fehler leicht im cm-Bereich

Der Tiefenfehler hängt stark ab von:

  • Basislinie (Abstand der Kameras)
  • Entfernung des Punktes
  • Brennweite

9. Mehrere Bilder & Bundle Adjustment → sparse Cloud

9.1 Multi-View-Situation

Für jeden 3D-Punkt $X_j$ gibt es viele Beobachtungen:

$$ x_{ij} = \begin{pmatrix} u_{ij} \\ v_{ij} \end{pmatrix} $$

in Kamera $i$.

Pose der Kamera $i$:

$$ \theta_i = (R_i, t_i, K_i) $$

Projektionsfunktion:

$$ \pi(\theta_i, X_j) = \begin{pmatrix} u_{ij}^{\text{pred}} \\ v_{ij}^{\text{pred}} \end{pmatrix} $$

9.2 Reprojektionsfehler

Gemessener Pixel:

$$ x_{ij}^{\text{obs}} = \begin{pmatrix} u_{ij} \\ v_{ij} \end{pmatrix} $$

Fehler:

$$ e_{ij} = x_{ij}^{\text{obs}} - \pi(\theta_i, X_j) $$

9.3 Bundle Adjustment

Alle Kamera-Parameter $\{\theta_i\}$ und 3D-Punkte $\{X_j\}$ müssen optimiert werden:

$$ \min_{\{\theta_i\}, \{X_j\}} \sum_{i,j} \rho\left( \lVert e_{ij} \rVert^2 \right) $$

$\rho$ ist eine robuste Verlustfunktion (z. B. Huber), damit Ausreißer das Ergebnis nicht zerstören.

Das Problem wird typischerweise mit Levenberg-Marquardt und Schur-Komplement-Tricks gelöst (z. B. im Ceres Solver).

Ergebnis:

  • optimierte Kameraposen
  • optimierte 3D-Punkte

Das ist die sparse Point Cloud, die man am Ende sieht.


10. Unterschiedliche Inputs: Fotos vs. Video

10.1 Fotos

  • hohe Auflösung (z. B. 6000×4000)
  • RAW oder wenig Kompression
  • weniger Rauschen, gute Schärfe
  • sehr gute Feature-Detektion & -Matching

10.2 Video-Frames

  • oft nur 1920×1080 oder weniger
  • H.264/H.265-Kompression → Blockartefakte
  • Motion Blur, Rolling Shutter
  • viele sehr ähnliche Frames (redundant)

Folge:

  • weniger stabile Features
  • mehr Rauschen in Pixelpositionen
  • größere Triangulationsfehler

Deshalb machen viele Pipelines bei Video:

  • Frame-Subsampling (z. B. jedes 5. Frame)
  • Keyframe-Selektion
  • Schärfe-/Qualitätsfilter

11. Mehr Bilder – was passiert mit der Qualität?

  • 2 Bilder → minimale Information für Triangulation
  • Viele Bilder → Fehler mitteln sich
  • Große Baseline → gute Tiefenauflösung
  • Viele Beobachtungen → robuste und präzise 3D-Punkte

Geometrisch schneiden sich die Projektionsstrahlen aller Kameras nicht perfekt (wegen Rauschen). Bundle Adjustment sucht den 3D-Punkt, der zu allen Beobachtungen am besten passt → der „Fehlerkegel“ wird kleiner.


12. Die komplette Pipeline in Worten

  1. Man nimmt Bilder mit einer Kamera auf (Fotos oder Video-Frames).
  2. Kalibrierung der Intrinsics $K$ und das Verzerrungsmodell.
  3. Undistortion der Bilder → reines Pinhole-Modell.
  4. Für jedes Bild werden Features (z. B. SIFT) und Deskriptoren extrahiert.
  5. Feature matching zwischen Bildern um Korrespondenzen zu erhalten.
  6. Schätzung der Fundamentalmatrix $F$ und Entfernung der Ausreißer (RANSAC).
  7. Mit $F$ und $K$ berechnet man die Essentialmatrix $E$ und daraus die relative Pose $(R,t)$.
  8. Fixierung einer Referenzkamera mit $[I \mid 0]$ und bestimmst Posen für weitere Kameras.
  9. Triangulation der 3D-Punkte aus den 2D-Korrespondenzen (linear/nichtlinear).
  10. Bundle Adjustment für alle Kameras und Punkte.

Am Ende bekommt man:

  • optimierte Kamera-Posen und Intrinsics
  • eine sparsche 3D-Punktwolke – die Basis für dichte Rekonstruktionen, NeRFs, Gaussian Splatting & Co.