Skip to main content

synthcal

CI PyPI

Synthetic dataset generator for camera + laser-stripe calibration.

Install

pip install synthcal
# Optional (recommended for faster Gaussian blur):
pip install "synthcal[scipy]"

Quickstart

Initialize an example config:

synthcal init-config config.yaml

Preview one frame/camera:

synthcal preview config.yaml --frame 0 --cam cam00

Generate a dataset:

synthcal generate config.yaml out_dataset/

python -m synthcal ... is also supported.

Scope

This repository focuses on:

  • Generating datasets of static robot poses for an eye-in-hand multi-camera rig.
  • For each frame and camera, producing paired outputs from the same pose:
    1. target.png: chessboard under normal illumination
    2. stripe.png: black background with only a laser stripe (when laser is enabled)
    3. corners_px.npy (float32, N x 2) + corners_visible.npy (bool, N)
    4. stripe_centerline_px.npy (float32, M x 2) + stripe_centerline_visible.npy (bool, M)

Units: millimeters (mm) everywhere for geometry. Pixel coordinates are in pixels.

Camera model: OpenCV-style intrinsics K + distortion dist = (k1, k2, k3, p1, p2) (no OpenCV dependency).

Examples

See examples/minimal_no_laser.yaml, examples/minimal_with_laser.yaml, and examples/multicam_medium.yaml.

Camera model

Projection uses the standard pinhole model with distortion applied in normalized coordinates (x, y):

r2 = x^2 + y^2
radial = 1 + k1*r2 + k2*r2^2 + k3*r2^3
x_tan = 2*p1*x*y + p2*(r2 + 2*x^2)
y_tan = p1*(r2 + 2*y^2) + 2*p2*x*y
xd = x*radial + x_tan
yd = y*radial + y_tan

Undistortion is implemented as a simple fixed-point iteration starting from (xu, yu) = (xd, yd) and repeatedly subtracting the forward-model error until convergence (works well for small/moderate distortion and points near the principal point).

Outputs

The dataset layout is described in manifest.yaml (schema v1). Outputs include:

  • config.yaml (the normalized config used to generate)
  • manifest.yaml (stable schema v1; also includes version: 1)
  • per-frame T_base_tcp.npy (identity for all frames unless scenario pose sampling is enabled)
  • per-frame/per-camera *_target.png + *_corners_*.npy
  • when laser is enabled: per-frame/per-camera *_stripe.png + *_stripe_centerline_*.npy
  • placeholder rig/camera YAML files (rig/)

Effects

Rendered images can be post-processed to add simple realism:

  • blur_sigma_px: Gaussian defocus blur sigma in pixels
  • noise_sigma: zero-mean Gaussian read noise sigma in intensity units (0..255)

Order of operations: blur → noise → clamp → quantize to uint8. Blur uses SciPy (scipy.ndimage.gaussian_filter) when available; otherwise synthcal falls back to a deterministic NumPy implementation (slower).

Determinism: noise is seeded from the global dataset seed and a stable per-output key (frame_index, camera_name, modality) so re-generating with the same config+seed produces identical images. Effects are applied to both *_target.png and *_stripe.png (when present).

Scenario

Optional scenario pose sampling can generate a different T_base_tcp per frame while enforcing in-view constraints (all corners inside the image with a margin). Presets easy|medium|hard provide reasonable default ranges; all randomness is derived from the global seed.

Laser

Laser output is optional. When enabled, synthcal models the laser as a single infinite plane in the TCP frame: n·X + d = 0 (X in mm). The stripe is rendered only where the laser plane intersects the target plane (Z=0 in target frame). If the planes are nearly parallel, synthcal outputs a black stripe image and empty centerline arrays for that (frame, camera).

Reproducibility

  • seed is the single global seed recorded in config.yaml and manifest.yaml.
  • Noise/effects use deterministic per-output RNG streams derived from (seed, frame_index, camera_name, modality).
  • Scenario pose sampling uses a deterministic per-frame stream derived from (seed, frame_index, "__scenario__", "pose").

Public API

Supported library entry points live in src/synthcal/api.py:

from synthcal import generate_dataset, render_frame_preview

CLI

The CLI provides:

  • synthcal init-config
  • synthcal preview
  • synthcal generate

Preview options:

  • --no-effects: show the raw render (before blur/noise/quantize)
  • --all-cams: show a grid for all cameras
  • --show-stripe: with --all-cams, also show a stripe grid when laser is enabled

Coordinate conventions

  • Frames (v0): world == base (eye-in-hand only).
  • T_tcp_cam maps TCP-frame points into the camera frame.
  • T_cam_target maps target-frame points into the camera frame: X_cam = T_cam_target @ [X_target, 1].
  • The chessboard target lies in plane Z=0 in the target frame, with outer corner at (0,0,0).
  • Inner corners are ordered row-major (rows first, then cols), matching OpenCV’s convention.

Docs

  • docs/config_reference.md
  • docs/manifest_reference.md

Extending synthcal

Targets, sensors, sampling, and effects are implemented in src/synthcal/. Only the functions in src/synthcal/api.py are intended to be stable for external users.

Metadata

Release files for synthcal 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for synthcal 0.1.0
File Size Uploaded
synthcal-0.1.0.tar.gz 43.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for synthcal 0.1.0
File Interpreter ABI Platform
synthcal-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 89.5 kB

Release files / synthcal-0.1.0.tar.gz

Download URL synthcal-0.1.0.tar.gz
Size 43.2 kB
Tags Source
SHA-256 checksum
How to use checksums
3aa67b7b831822680c7c22e84c338326c782ea5497f7aeda5d8cb7b7048c9de6
BLAKE2b-256 checksum
How to use checksums
820992a8c2af3a77774e68cad5d211b12e1a7593794d2fc74c7a27f9dbe7ab48
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 18, 2026.

Transparency log

Release files / synthcal-0.1.0-py3-none-any.whl

Download URL synthcal-0.1.0-py3-none-any.whl
Size 46.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8a51f9403814d569d8fe863678f23c14bfe9a1b2c7bea442e603f6d2c8975700
BLAKE2b-256 checksum
How to use checksums
bb01792b9edc7ff8da37e1a3c23a748e0f0c69c92b3e705f8acc48e06114099d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jan 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page