Synthetic dataset generator for camera + laser-stripe calibration.
Project description
synthcal
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:
target.png: chessboard under normal illuminationstripe.png: black background with only a laser stripe (when laser is enabled)corners_px.npy(float32,N x 2) +corners_visible.npy(bool,N)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 includesversion: 1)- per-frame
T_base_tcp.npy(identity for all frames unlessscenariopose 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 pixelsnoise_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
seedis the single global seed recorded inconfig.yamlandmanifest.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-configsynthcal previewsynthcal 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_cammaps TCP-frame points into the camera frame.T_cam_targetmaps target-frame points into the camera frame:X_cam = T_cam_target @ [X_target, 1].- The chessboard target lies in plane
Z=0in 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.mddocs/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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file synthcal-0.1.0.tar.gz.
File metadata
- Download URL: synthcal-0.1.0.tar.gz
- Upload date:
- Size: 43.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3aa67b7b831822680c7c22e84c338326c782ea5497f7aeda5d8cb7b7048c9de6
|
|
| MD5 |
10ca9d4e53fdbfad7321463b12fa189d
|
|
| BLAKE2b-256 |
820992a8c2af3a77774e68cad5d211b12e1a7593794d2fc74c7a27f9dbe7ab48
|
Provenance
The following attestation bundles were made for synthcal-0.1.0.tar.gz:
Publisher:
publish.yml on VitalyVorobyev/calib-synth
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synthcal-0.1.0.tar.gz -
Subject digest:
3aa67b7b831822680c7c22e84c338326c782ea5497f7aeda5d8cb7b7048c9de6 - Sigstore transparency entry: 833803822
- Sigstore integration time:
-
Permalink:
VitalyVorobyev/calib-synth@a3f4a3e7cd27038faf6a3b30c40159cdc4c9db5e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/VitalyVorobyev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a3f4a3e7cd27038faf6a3b30c40159cdc4c9db5e -
Trigger Event:
push
-
Statement type:
File details
Details for the file synthcal-0.1.0-py3-none-any.whl.
File metadata
- Download URL: synthcal-0.1.0-py3-none-any.whl
- Upload date:
- Size: 46.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a51f9403814d569d8fe863678f23c14bfe9a1b2c7bea442e603f6d2c8975700
|
|
| MD5 |
bda60ea0d5dea9243f0d502cbe462ac0
|
|
| BLAKE2b-256 |
bb01792b9edc7ff8da37e1a3c23a748e0f0c69c92b3e705f8acc48e06114099d
|
Provenance
The following attestation bundles were made for synthcal-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on VitalyVorobyev/calib-synth
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
synthcal-0.1.0-py3-none-any.whl -
Subject digest:
8a51f9403814d569d8fe863678f23c14bfe9a1b2c7bea442e603f6d2c8975700 - Sigstore transparency entry: 833803823
- Sigstore integration time:
-
Permalink:
VitalyVorobyev/calib-synth@a3f4a3e7cd27038faf6a3b30c40159cdc4c9db5e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/VitalyVorobyev
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@a3f4a3e7cd27038faf6a3b30c40159cdc4c9db5e -
Trigger Event:
push
-
Statement type: