Public API contract

StereoComplex is a research prototype, but it exposes a small public API meant to be usable in downstream code.

Stability promise

  • Everything under stereocomplex.api (and the canonical sub-namespaces stereocomplex.advanced, stereocomplex.physics, stereocomplex.synthetic, stereocomplex.rayfields) is the current public API.

  • v0.x is explicitly unstable: renames ship without aliases. Stability commitments start at the 1.0 release.

  • Everything else (stereocomplex.core, stereocomplex.eval, paper/, docs/examples/) is internal and may change without notice.

Sub-namespaces

  • stereocomplex.physics — physical models (Brown-Conrady, CMO, parallel plate, polynomial surrogate) and model selection

  • stereocomplex.advanced — lower-level composition functions for expert pipelines

  • stereocomplex.rayfields — Zernike rayfield models and the compact Zernike fallback candidate

  • stereocomplex.synthetic — synthetic dataset generation and benchmarks

  • stereocomplex.benchmarks — oracle builders, observation simulators, diagnostics

Symbols not in stereocomplex.__all__ should be imported from their canonical sub-namespace. In v0.x, the public surface may still change.

Example

import stereocomplex as sc

report = sc.compare_opencv_stereo_calibration(
    left_dir="left", right_dir="right", board=board,
)
assessment = sc.assess_calibration(report["refined_result"])
K1, d1, K2, d2, R, T = report["refined_result"].to_opencv()

Direct API imports (stable):

from stereocomplex.api import (
    CalibrationAssessment,
    CharucoBoardSpec,
    StereoOpenCVCalibrationResult,
    StereoCentralRayFieldModel,
    assess_calibration,
    build_charuco_board,
    compare_opencv_stereo_calibration,
    detect_charuco_corners,
    fit_opencv_stereo_from_image_dirs,
    fit_stereo_central_rayfield_from_image_dirs,
    load_stereo_central_rayfield,
    refine_charuco_corners,
    save_stereo_central_rayfield,
)

Advanced / composition imports:

from stereocomplex.advanced import (
    fit_stereo_zernike_origin_field,
    fit_physical_model_to_rayfield,
    compare_3d_reconstruction_with_without_origin_field,
    oracle_reconstruction_floor_report,
    compare_rayfields_on_planes,
    reconstruct_points_central_stereo,
    reconstruct_points_with_origin_fields,
    triangulate_two_rays,
)
from stereocomplex.physics import (
    CentralPinholeModel,
    CentralBrownConradyModel,
    PinholeParallelPlateModel,
    PinholeParallelPlateRayField,
    default_physical_model_specs,
)
from stereocomplex.rayfields import (
    ZernikeOriginField,
    ZernikeRayField,
    ZernikeOriginFieldConfig,
)
from stereocomplex.synthetic import (
    ParallelPlateSyntheticParams,
    generate_parallel_plate_stereo_dataset,
    render_parallel_plate_charuco_images,
)

Calibrate from your own images

Standard OpenCV stereo, raw vs Ray2D

If you want to stay in a classic OpenCV pinhole workflow and only compare the 2D preprocessing stage, the stable public entry point is:

import stereocomplex as sc

board = sc.CharucoBoardSpec(
    squares_x=11,
    squares_y=7,
    square_size_mm=39.0713,
    marker_size_mm=27.3499,
    aruco_dictionary="DICT_4X4_1000",
)

raw = sc.fit_opencv_stereo_from_image_dirs(
    left_dir="my_data/left",
    right_dir="my_data/right",
    board=board,
    method2d="raw",
)
refined = sc.fit_opencv_stereo_from_image_dirs(
    left_dir="my_data/left",
    right_dir="my_data/right",
    board=board,
    method2d="rayfield_tps_robust",
)

print(raw.report)
print(refined.report)

This is the exact path used at the beginning of notebook 01_ray2d_vs_opencv.

StereoComplex 3D ray-field calibration

The main high-level calibration entry points are:

import stereocomplex as sc

board = sc.CharucoBoardSpec(
    squares_x=11,
    squares_y=7,
    square_size_mm=39.0713,
    marker_size_mm=27.3499,
    aruco_dictionary="DICT_4X4_1000",
)

result = sc.fit_stereo_central_rayfield_from_image_dirs(
    left_dir="my_data/left",
    right_dir="my_data/right",
    board=board,
    method2d="rayfield_tps_robust",
    export_model_dir="models/my_calibration",
)

model = result.model
print(result.report)

If you already use a versioned dataset v0 scene, the stable dataset wrapper is:

result = sc.fit_stereo_central_rayfield_from_dataset(
    dataset_root="dataset/v0_png",
    split="train",
    scene="scene_0000",
    max_frames=5,
    method2d="rayfield_tps_robust",
    export_model_dir="models/scene0000_rayfield3d",
)

Experimental non-central calibration from image directories

If you suspect the rig is not well represented by a central/pinhole model, the public experimental entry point is:

from pathlib import Path

import stereocomplex as sc

board = sc.CharucoBoardSpec(
    squares_x=9,
    squares_y=6,
    square_size_mm=20.0,
    marker_size_mm=15.0,
    aruco_dictionary="DICT_4X4_50",
)

fit = sc.fit_stereo_zernike_origin_field_from_image_dirs(
    left_dir=Path("left"),
    right_dir=Path("right"),
    board=board,
    max_order=4,
    method2d="rayfield_tps_robust",
)

print(fit.residual_rms)
left_field = fit.left_field
right_field = fit.right_field

This API currently fits a Zernike origin field O(u,v) initialized from an OpenCV stereo calibration. It is intended for experimental non-central stereo systems where a pinhole model leaves systematic ray gaps or reconstruction bias.

The complete O(u,v), d(u,v), poses, rig BA remains an advanced benchmark path and is documented separately from this practical origin-field API in :doc:PARALLEL_PLATE_ORIGIN_FIELD. For the guided practical workflow, use examples/notebooks/05_noncentral_calibration_from_images.ipynb.

Experimental physical plate fit from a measured rayfield

Notebook 04 also exposes a second-stage interpretation tool:

plate_fit = sc.fit_parallel_plate_to_zernike_rayfield(
    zernike_field=fit.left_field,
    K=fit.left_field.K,
    image_size=fit.left_field.config.image_size,
    eta=1.5,
    support_pixels=observed_pixels,
)

print(plate_fit.params)
print(plate_fit.rayfield_rms_support_mm)

This does not replace the generic Zernike fit. It treats the fitted rayfield as a measured geometric object, then asks whether a compact pinhole + inclined parallel-plate model can explain it in ray space. The residual is computed from intersections with two z-planes, not by comparing raw ray origins.

Optical model identification

After fitting a non-central Zernike origin field, ask which physical optics model best compresses it in ray space:

import stereocomplex as sc

# fit is a StereoZernikeOriginFieldFitResult
report = sc.select_physical_model_from_rayfield(
    target_field=fit.left_field,
    candidate_specs=None,          # default: pinhole, Brown-Conrady, inclined plate
    K=fit.left_field.K,
    image_size=fit.left_field.config.image_size,
)

print(report.best_by_bic)         # "pinhole_parallel_plate", "central_brown_conrady", ...
for row in report.rows():
    print(row)                     # model, n_params, rms_mm, support_rms_mm, bic, selected_bic

OpticalModelSelectionReport.best_by_bic gives the name of the winning candidate under the Bayesian Information Criterion. rows() returns a list of dicts suitable for pandas.DataFrame(report.rows()).

To add a custom candidate, implement the PhysicalRayFieldModel protocol and wrap it in a PhysicalModelSpec:

from stereocomplex.physics import PhysicalModelSpec
from stereocomplex.advanced import fit_physical_model_to_rayfield

my_spec = PhysicalModelSpec(
    name="my_model",
    model_class=MyModel,
    initial_parameters=np.array([...]),
    bounds=(np.array([...]), np.array([...])),
    model_kwargs={"eta": 1.5},
)
result = fit_physical_model_to_rayfield(
    model_class=my_spec.model_class,
    target_field=fit.left_field,
    K=K,
    image_size=image_size,
    name=my_spec.name,
    **my_spec.model_kwargs,
)

Full tutorial: :doc:IDENTIFY_MY_OPTICS

Corner refinement API

For ChArUco refinement, the stable entry point is:

import stereocomplex as sc

board = sc.CharucoBoardSpec(
    squares_x=11,
    squares_y=7,
    square_size_mm=39.0713,
    marker_size_mm=27.3499,
    aruco_dictionary="DICT_4X4_1000",
)

detections = sc.detect_charuco_corners(image="left/000000.png", board=board)
refined_xy = sc.refine_charuco_corners(
    method="rayfield_tps_robust",
    board=board,
    detections=detections,
)

This keeps the workflow inside the public API:

  1. detect ArUco / ChArUco,

  2. refine corners with the planar geometric prior,

  3. send the refined points to OpenCV or to the public StereoComplex calibration wrappers.

Note: the default package install already includes OpenCV ArUco support through opencv-contrib-python-headless.

Experimental non-central rayfield API

The inclined parallel-plate benchmark is exposed as a public experimental API. It is meant for synthetic non-central ray-field experiments, not for calibrating the physical parameters of a glass plate.

The shortest complete benchmark is:

import stereocomplex as sc

report = sc.run_parallel_plate_origin_field_benchmark(
    max_order=4,
    noise_std_px=0.05,
)

print(report.reconstruction_comparison.central.rms_3d)
print(report.oracle_floor.oracle_observed_pixels.rms_3d)
print(report.reconstruction_comparison.with_origin_field.rms_3d)
print(report.reconstruction_comparison.improvement_rms_factor)

The rendered-image front-end check is also exposed:

from pathlib import Path

import stereocomplex as sc

image_report = sc.run_parallel_plate_rendered_image_benchmark(
    out_dir=Path("outputs/parallel_plate_rendered"),
    max_order=3,
    method2d="raw",
)
refined_image_report = sc.run_parallel_plate_rendered_image_benchmark(
    out_dir=Path("outputs/parallel_plate_rendered_ray2d"),
    max_order=3,
    method2d="rayfield_tps_robust",
)

print(image_report.n_frames)
print(image_report.n_common_corners)
print(image_report.n_points_total)
print(image_report.reconstruction_comparison.central.rms_3d)
print(image_report.oracle_detected.rms_3d)
print(image_report.reconstruction_comparison.with_origin_field.rms_3d)
print(refined_image_report.reconstruction_comparison.with_origin_field.rms_3d)

This renders non-central ChArUco images with vignetting, edge blur and noise, detects the corners with OpenCV, optionally applies the public rayfield_tps_robust planar refinement, then runs the complete geometric BA over O(u,v), d(u,v), board poses and stereo rig. The oracle_detected report is an evaluation-only floor: it triangulates the front-end pixel coordinates with the exact synthetic rayfield, so it separates image/detector error from BA error.

The lower-level path is:

import stereocomplex as sc

dataset = sc.make_default_parallel_plate_dataset(noise_std_px=0.05)
dataset_clean = sc.make_default_parallel_plate_dataset(noise_std_px=0.0)
config = sc.ZernikeOriginFieldConfig(image_size=dataset.image_size, max_order=4)

fit = sc.fit_stereo_zernike_origin_field(
    observations=dataset,
    K_left=dataset.K_left,
    K_right=dataset.K_right,
    T_right_left_initial=dataset.T_right_left,
    board_poses_initial=dataset.board_poses,
    config_left=config,
    config_right=config,
    regularization=1e-3,
)

comparison = sc.compare_3d_reconstruction_with_without_origin_field(
    dataset=dataset,
    central_model_result=None,
    origin_field_result=fit,
)

oracle_floor = sc.oracle_reconstruction_floor_report(
    dataset_observed=dataset,
    dataset_clean=dataset_clean,
)

left_rayfield = sc.compare_rayfields_on_planes(
    fitted_field=fit.left_field,
    oracle_ray_function=dataset.oracle_left_ray_function,
    image_size=dataset.image_size,
    z_planes=(100.0, 1000.0),
)

fit_ba = sc.fit_stereo_zernike_origin_field(
    observations=dataset,
    K_left=dataset.K_left,
    K_right=dataset.K_right,
    T_right_left_initial=dataset.T_right_left,
    board_poses_initial=dataset.board_poses,
    config_left=sc.ZernikeOriginFieldConfig(image_size=dataset.image_size, max_order=3),
    config_right=sc.ZernikeOriginFieldConfig(image_size=dataset.image_size, max_order=3),
    optimize_directions=True,
    optimize_board_poses=True,
    optimize_stereo_extrinsics=True,
    regularization=1e-5,
    direction_regularization=1e-2,
    pose_regularization=10.0,
    rig_regularization=100.0,
)

This API deliberately separates:

  • oracle generation: ParallelPlateSyntheticParams, generate_parallel_plate_stereo_dataset;

  • rendered image generation: render_parallel_plate_charuco_images, detected_observations_from_rendered_parallel_plate, run_parallel_plate_rendered_image_benchmark;

  • generic identified models: ZernikeOriginField for O-only fits and ZernikeRayField for experimental O(u,v)+d(u,v) fits;

  • fitting: fit_stereo_zernike_origin_field, with optional optimize_directions=True, optimize_board_poses=True, and optimize_stereo_extrinsics=True for the complete geometric BA mode over O(u,v), d(u,v), poses, and rig;

  • evaluation: compare_3d_reconstruction_with_without_origin_field, oracle_reconstruction_floor_report, compare_rayfields_on_planes.

See the theory and figures in :doc:PARALLEL_PLATE_ORIGIN_FIELD, and the guided notebook examples/notebooks/04_parallel_plate_origin_field.ipynb.

Public method2d values

Method

Status

Description

raw

public stable

OpenCV ChArUco corners are used unchanged.

rayfield_tps_robust

public stable

Robust planar TPS/ray-field 2D correction before calibration.

other methods

internal benchmark

Experimental or historical methods are not guaranteed by the public API.