02 Calibration
Two-step calibration pipeline:
| Step | Script | What it does |
|---|---|---|
| 1. Detection | detect_features.py |
Chessboard corners / IR ellipses → JSON next to each image |
| 2. Calibration | calibrate.py |
Mono intrinsics per camera + stereo lc vs rc/rg/ir |
main.py runs both steps by default (--step all).
Troubleshooting flag
All calibration scripts accept --troubleshooting (default: off).
--troubleshooting |
Logs | Disk output |
|---|---|---|
| False (default) | Minimal summary per camera / stereo pair | Step 1: *.json only (required for step 2). Step 2: params/ only |
| True | Detailed per-image / per-pair logs, progress bars | Step 1: + corners/<camera>/ overlays. Step 2: + pairing_reports/, rectified/ |
# Default — minimal logs, only params/ from step 2
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1
# Debug — verbose logs + intermediate folders
python main.py --project Olsen_wings --date 2026-05-12 --calib_name calib1 --troubleshooting
Legacy mode (--legacy) also respects --troubleshooting (corners, local_coords, images_ncb, rectified).
All CLI parameters (reference)
| Parameter | Default | Used in |
|---|---|---|
--project |
required | all |
--date |
required | all |
--calib_name |
calib1 |
all |
--chessboard_size |
8,7 |
all |
--square_size |
0.045 |
all |
--left_chessboard_size |
= --chessboard_size |
all |
--right_chessboard_size |
= --chessboard_size |
all |
--left_square_size |
= --square_size |
all |
--right_square_size |
= --square_size |
all |
--preprocessing |
None |
step 1 (G, C, T chain) |
--cameras |
all present | detect_features.py |
--ir_mode |
auto |
step 1 (auto / chessboard / ellipse) |
--step |
all |
main.py (detect/calibrate/all); calibrate.py (mono/stereo/all) |
--left_camera |
lc |
step 2 stereo (lc / lc-ir) |
--time_window |
0.1 |
step 2 stereo (seconds) |
--partners |
rc,rg,ir |
step 2 stereo |
--legacy |
off | main.py only |
--right_camera |
rc |
main.py --legacy only |
--troubleshooting |
off | all (False = minimal; True = debug output) |
Folder structure
~/Calib-data/<project>/<date>/<calib_name>/
├── lc/
│ ├── lc_1778599872850705.bmp
│ └── lc_1778599872850705.json ← step 1 (always)
├── rc/
├── rg/ (or rgb/)
├── ir/ (or IR/)
├── corners/ ← step 1, only with --troubleshooting
├── pairing_reports/ ← step 2, only with --troubleshooting
├── rectified/ ← step 2, only with --troubleshooting
└── params/ ← step 2 (always)
├── lc_intrinsics.npz
├── rc_intrinsics.npz
├── lc-rc_parameters.npz
├── lc-rc_stereo_cam_model.yaml
├── lc-rc_Q.cvstore
├── lc-rg_*
└── lc-ir_*
Nested layout (<calib_name>/images/lc/, …) is also supported.
Quick start (full pipeline)
cd ~/Speckle-Scanner/02_Calibration
python main.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--chessboard_size 8,7 --square_size 0.045
Or run steps separately:
# Step 1 — detect features, write JSON
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--chessboard_size 8,7 --square_size 0.045
# Step 2 — calibrate from JSON (writes params/ only)
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--chessboard_size 8,7 --square_size 0.045 \
--time_window 0.1
Step 1 — Feature detection (per camera)
For every image in each camera folder (lc, rc, rg, ir, lc-ir):
- Detects chessboard corners (default for lc/rc/rg)
- For IR: tries chessboard first (
--ir_mode auto), falls back to ellipse center - Writes
<image>.jsonin the same folder as the image (always, even without--troubleshooting)
LC only
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras lc \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--preprocessing None
RC only
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras rc \
--chessboard_size 8,7 --square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045 \
--preprocessing None
RG only
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras rg \
--chessboard_size 8,7 --square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045 \
--preprocessing None
IR only
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras ir \
--chessboard_size 8,7 --square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045 \
--preprocessing C \
--ir_mode auto
LC-IR folder only
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras lc-ir \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--preprocessing None
All cameras
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045 \
--preprocessing None \
--ir_mode auto
Step 1 with troubleshooting
python detect_features.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--cameras lc,rc,ir \
--chessboard_size 8,7 --square_size 0.045 \
--preprocessing C \
--ir_mode auto \
--troubleshooting
JSON contents (chessboard example)
{
"version": 1,
"image": "lc_1778599872850705.bmp",
"camera_folder": "lc",
"feature_type": "chessboard",
"success": true,
"board_size": [8, 7],
"square_size": 0.045,
"timestamp_sec": 1778599872.850705,
"pair_key": "1778599872850705",
"corners": [[412.3, 287.1], ...]
}
Step 2 — Calibration
2a. Mono intrinsics
Reads chessboard JSONs from each camera folder, runs cv2.calibrateCamera, saves:
params/<camera>_intrinsics.npzparams/<camera>_intrinsics.yaml
Requires ≥ 3 successful chessboard detections per camera.
2b. Stereo calibration
- Left camera:
lcby default (--left_camera) - Partners:
rc,rg,ir— each available folder is calibrated against lc - Pairing: time-window match (
--time_window, default 0.1 s), then filenamepair_keyfallback for IR scan ids - Uses mono intrinsics with
CALIB_FIX_INTRINSIC - Saves
lc-rc_*,lc-rg_*,lc-ir_*underparams/
Full step 2 (mono + all stereo pairs)
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step all \
--left_camera lc \
--partners rc,rg,ir \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045
Stereo: LC ↔ RC only
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step stereo \
--left_camera lc \
--partners rc \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045
Stereo: LC ↔ RG only
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step stereo \
--left_camera lc \
--partners rg \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045
Stereo: LC ↔ IR only
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step stereo \
--left_camera lc \
--partners ir \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045
Stereo: left = LC-IR folder
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step stereo \
--left_camera lc-ir \
--partners rc,rg,ir \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045
Mono intrinsics only
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step mono \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045
Step 2 with troubleshooting
python calibrate.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step all \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045 \
--troubleshooting
Writes params/ plus pairing_reports/<pair>.txt and rectified/<pair>/.
Full pipeline (main.py)
python main.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--step all \
--left_camera lc \
--partners rc,rg,ir \
--time_window 0.1 \
--chessboard_size 8,7 --square_size 0.045 \
--left_chessboard_size 8,7 --left_square_size 0.045 \
--right_chessboard_size 8,7 --right_square_size 0.045 \
--preprocessing None \
--ir_mode auto
With troubleshooting:
python main.py \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--troubleshooting \
--chessboard_size 8,7 --square_size 0.045
Legacy one-shot mode
The old in-memory flow (single --right_camera, filename pairing) still works:
# LC-RC
python main.py --legacy \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--left_camera lc --right_camera rc \
--chessboard_size 8,7 --square_size 0.045
# LC-RG
python main.py --legacy \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--left_camera lc --right_camera rg \
--chessboard_size 8,7 --square_size 0.045
# LC-IR
python main.py --legacy \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--left_camera lc --right_camera ir \
--chessboard_size 8,7 --square_size 0.045 \
--preprocessing C
# LC-IR folder + IR partner (with debug output)
python main.py --legacy \
--project Olsen_wings --date 2026-05-12 --calib_name calib1 \
--left_camera lc-ir --right_camera ir \
--chessboard_size 8,7 --square_size 0.045 \
--preprocessing C --troubleshooting
Dependencies
pip install -r ~/Speckle-Scanner/02_Calibration/requirements.txt
# or full pipeline:
pip install -r ~/Speckle-Scanner/requirements.txt
Notes
- Stereo pairing uses timestamps parsed from filenames (
ts…tokens or long numeric ids);ck…suffixes are ignored. - Ellipse-only IR JSONs are stored but cannot produce mono intrinsics (need full chessboard grids). Use chessboard IR images for calibration.
- Per-camera board overrides apply to detection and calibration (
--left_chessboard_size, etc.). - Re-run step 1 if images change; re-run step 2 freely when tuning
time_windowor partners. - With
--troubleshootingoff, step 2 writes onlyparams/(nopairing_reports/, norectified/).