codex/stereoscopy-depth-from-disparity.py

stereoscopy-depth-from-disparity.py
"""Render original ideal-stereo model curves into the current directory.

Build with ``make _media/stereoscopy-depth-from-disparity.png``.
The baseline, focal scale and disparity uncertainty are stipulated examples,
not measurements or specifications of a particular camera.
"""
from pathlib import Path

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    baseline_m = 0.12
    focal_px = 800.0
    disparity_uncertainty_px = 0.5
    disparity_px = np.linspace(10.0, 120.0, 400)
    depth_m = focal_px * baseline_m / disparity_px
    test_depth_m = np.linspace(0.8, 10.0, 400)
    uncertainty_m = (test_depth_m**2 / (focal_px * baseline_m)
                     * disparity_uncertainty_px)
    fig, axes = plt.subplots(1, 2, figsize=(9, 3.9), layout="constrained")
    fig.patch.set_facecolor("white")
    left, right = axes
    left.plot(disparity_px, depth_m, color="#176b91", lw=2)
    left.scatter([24], [4], color="#a64b16", zorder=3)
    left.annotate("24 pixels → 4 m", (24, 4), (48, 5.2),
                  arrowprops={"arrowstyle": "->", "color": "#a64b16"},
                  color="#a64b16", fontsize=10)
    left.set(xlabel="Stereo disparity d (pixels)", ylabel="Depth Z (m)",
             title=r"Depth: $Z=fB/d$", xlim=(0, 125), ylim=(0, 10))
    right.plot(test_depth_m, uncertainty_m, color="#557b38", lw=2)
    right.scatter([4], [4**2 / 96 * 0.5], color="#a64b16", zorder=3)
    right.set(xlabel="Depth Z (m)", ylabel="Approximate depth uncertainty (m)",
              title=r"Fixed disparity uncertainty: $\sigma_Z\simeq Z^2\sigma_d/(fB)$",
              xlim=(0, 10), ylim=(0, 0.55))
    for ax in axes:
        ax.grid(alpha=0.2)
    fig.suptitle("Ideal aligned stereo: B = 0.12 m; f = 800 pixels; σd = 0.5 pixel",
                 fontsize=11)
    fig.savefig(Path("stereoscopy-depth-from-disparity.png"), dpi=100,
                facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()