Global Coordinates

Express every camera in one shared physical reference frame. How this is done depends on which calibration board you use.

Quick Recipe

opinionated defaults -- full reference below
  1. 1.Open the Scale Factor calibration tab to reveal the Calibration Image Viewer, then toggle Global Coords on in the settings bar. This panel lives on that tab only.
  2. 2.On Camera 1: click Set Origin and click your physical (0,0) reference point. Enter the physical X/Y in mm if it's not literally at (0,0).
  3. 3.Still on Camera 1: click Pick F1 and click a feature visible in both Camera 1 and Camera 2. Navigate to Camera 2 and click the same physical point with Pick F1.
  4. 4.For more cameras, each middle camera picks F1 (shared with the previous camera) and F2 (shared with the next). The last camera only needs F1.
  5. 5.Click Compute + Save Global Frame to bake the shared offset into every camera model. Only then does Calibrate Vectors emit coordinates in the shared frame. There is no mirror or flip control here: the axis directions come from each camera's own calibration.

Two paths, split by board type

When you process more than one camera, the fields only line up if every camera reports its vectors in a single common frame. PIVTOOLs reaches that frame in two different ways, and which one applies is decided entirely by the calibration board.

Board typeMechanismHow the shared frame arises
Dotboard / ChArUcoJoint multi-camera calibrationOne shared board solved jointly. The shared frame is intrinsic to the solve -- nothing is baked separately.
Scale factorMulti-camera global frameCameras calibrated independently, then a datum + overlap-pair chain bakes a world_offset_mm into each model.

Which one do I get? You do not choose between them. If you calibrate with a dotboard or ChArUco board, you get the joint solve. If you calibrate with a scale factor, you get the global-frame chain. The rest of this page documents each in turn.

Joint multi-camera model (dotboard / ChArUco)

When every camera observes the same physical board, the calibration solves all cameras together into one shared world frame. This is the system-aware model. A single solve recovers per-camera intrinsics, per-(camera, view) poses, and one released board that every camera agrees on. Because the world frame is part of the solve itself, the cameras are in the same frame by construction -- cross-camera agreement is zero, the DaVis-equivalent result.

  • One shared board with a global dot index -- every camera looks at the same physical target
  • Per-camera intrinsics plus per-(camera, view) poses solved in a single joint optimisation
  • The shared world frame is intrinsic to the solve, not baked on afterwards
  • No world_offset_mm and no separate alignment step -- there is nothing to align
  • Cross-camera agreement is 0 by construction (DaVis-matching)

In the GUI

This is automatic. Put two or more cameras into the Dotboard or ChArUco calibration tab and the solve is always joint -- there is no toggle to enable (a single camera is a mono solve in the same tab). The result is a JointRecord. Dotboard joint calibration is driven from the GUI because it needs the interactive datum and cross-camera picks of the guided wizard (next section), which are saved to a sidecar inputs.mat beside the model.

Tolerant detection + caching. A view that fails detection is dropped and reported, never fatal -- a camera only fails if no image detects. Detections are cached in memory and persisted in the inputs.mat sidecar, so previews reuse them; the Re-detect button forces a refresh after the images change.

No headless dotboard-joint path

Dotboard joint calibration requires the interactive clicks above, so it runs from the GUI only. The CLI detect-joint command is ChArUco-only.

On the CLI (ChArUco only)

detect-joint
# Joint ChArUco solve across cameras into one shared world frame pivtools-cli detect-joint --cameras 1,2,3 \ --model-type pinhole \ --board-release full3d
FlagDescriptionDefault
--camerasCameras to include in the joint solve
--sourceCalibration source (ChArUco)
--model-typepinhole | polynomialpinhole
--board-releasefull3d | z_only | none -- how much of the board geometry is freedfull3d
--model-typeOutput
pinholeOne JointRecord at <root>/joint_<board>/model/joint_model_pinhole.mat
polynomialPer-camera polynomial records

No YAML to configure. The joint path does not use the global_coordinates datum + overlap-pair block below -- its datum and cross-camera ties come from the guided wizard and persist in inputs.mat, and the shared frame is part of the solve itself.

Guided wizard -- Set Global Coordinates

On the dotboard joint path, all interactive picking runs through one auto-advancing wizard. Press Set Global Coordinates and follow the banner -- there are no separate origin, link, or rescue buttons.

The sequence

  1. 1Datum world frame first -- click Origin, +X, +Y on the datum camera's datum view (each click snaps to the nearest detected dot); type the origin mm in the wizard panel
  2. 2Then, per calibration frame: click the origin dot in camera 1
  3. 3Click 2 shared dots in camera 1
  4. 4Click the same 2 dots in camera 2 -- the bridge auto-commits once enough pairs are picked
  • The viewer auto-navigates to the awaited camera and frame; if you browse elsewhere mid-walk, picking pauses with a hint until you return -- a click is never attributed to the wrong image
  • Markers are persistent and colour-coded: the origin is green, shared dots are coloured by pick order with the same colour in both cameras (dot 1 in camera 1 matches dot 1 in camera 2)
  • Bridges walk outward from the datum camera to each adjacent camera (2 from 1, 3 from 2, ...), matching a linear rig's real overlap chain
  • Skip a step if a dot is not visible in a view; re-running the wizard skips views that are already anchored

ChArUco joint needs no wizard. Corner ids resolve the global grid with zero clicks. The origin of the shared frame is the corner-id default -- picking a chosen corner as the origin is not yet available on the joint ChArUco path.

Global frame (scale factor)

Scale-factor calibration produces one model per camera, each in its own local frame. The global-frame step ties them together. It takes a datum camera and a chain of overlap pairs, computes the per-camera shift needed to put them all in one frame, and bakes a world_offset_mm into each model.

Coordinates only. The offset is applied at apply-calibration time as a pure constant translation of the coordinate grid. Because it is constant, velocities and Reynolds stresses are unaffected -- only where each field sits in space changes.

Regenerating clears the offset

Regenerating any camera's model builds a fresh world frame, so its baked world_offset_mm is cleared. Re-save the global frame after recalibrating any camera.

Datum point

The datum is a pixel on the datum camera that defines where the physical origin sits. It carries physical coordinates (in mm), so you can place the origin on the datum feature or offset it from there.

Overlap pairs

Each overlap pair names the same physical feature as seen by two cameras: pixel_on_a on camera_a and pixel_on_b on camera_b. Chaining pairs from the datum camera outward fixes every camera's shift relative to the shared origin.

In the GUI

This is a deliberate step on the Scale Factor tab (shown when more than one camera is configured). After calibrating the cameras, pick the datum and overlap points on the image and use Compute + Save Global Frame to write the datum/overlap configuration and bake the offsets into the models. The saved per-camera offsets in mm are listed on the tab.

On the CLI

The global-frame command reads the datum and overlap pairs from the calibration settings sidecar at <source>/calibration/settings.yaml (normally written by the GUI). The example below shows the multi-camera scale-factor sequence end to end.

Multi-camera scale-factor sequence
# 1. Calibrate each camera independently pivtools-cli scale-factor --px-per-mm <v> --origin <x> <y> # 2. Bake the shared frame into every model # (needs global_coordinates in the settings sidecar) pivtools-cli global-frame --board scale_factor # 3. Emit coordinates in the shared frame pivtools-cli apply-calibration --board scale_factor --all-paths
global-frame flagDescription
--sourceCalibration source to operate on
--boardcharuco | dotboard | scale_factor
--model-typeCamera model type
Prerequisite

The datum and overlap pairs must exist in the settings sidecar at <source>/calibration/settings.yaml before global-frame runs. Set them in the GUI, or edit the sidecar directly using the block below. If datum_pixel is missing the command stops and names the file it expected to find.

YAML Configuration

The datum and overlap configuration is a top-level global_coordinates block in the per-source settings sidecar, not in config.yaml. Anything written under calibration: in the config is stripped on the next save. It applies to the global-frame path only: the joint solve shares a frame intrinsically and needs no such block.

global_coordinates: enabled: true datum_camera: 1 # Camera holding the origin datum_pixel: [512.0, 384.0] # Pixel position of origin on datum camera datum_physical: [0.0, 0.0] # Physical coordinates (mm) at datum datum_frame: 1 overlap_pairs: - camera_a: 1 camera_b: 2 pixel_on_a: [950.0, 400.0] # Shared feature pixel on camera_a pixel_on_b: [120.0, 400.0] # Same feature pixel on camera_b - camera_a: 2 camera_b: 3 pixel_on_a: [930.0, 410.0] pixel_on_b: [100.0, 405.0]

Configuration Fields

FieldTypeDescription
enabledboolEnable the global-frame chain
datum_cameraintCamera that holds the origin
datum_pixel[x, y]Pixel position of the origin on the datum camera image
datum_physical[x, y]Physical coordinates (mm) at the datum point
datum_frameintCalibration frame used for the datum pixel-to-physical conversion
overlap_pairslistChain of shared-feature pairs linking the cameras
overlap_pairs[].camera_aintFirst camera in the pair
overlap_pairs[].camera_bintSecond camera in the pair
overlap_pairs[].pixel_on_a[x, y]Shared feature pixel on camera_a
overlap_pairs[].pixel_on_b[x, y]Same feature pixel on camera_b

CLI quick reference

Pick the path by board type. Dotboard and ChArUco get the joint solve; scale factor gets the global-frame chain.

Dotboard / ChArUco (joint)

In the GUI, two or more cameras in the calibration tab is always a joint solve -- no extra command. On the CLI, ChArUco joint calibration uses detect-joint (see the joint section above). Dotboard joint has no headless path.

Scale factor (global frame)

Scale-factor multi-camera workflow
# Per camera pivtools-cli scale-factor --px-per-mm <v> --origin <x> <y> # Bake shared frame into models pivtools-cli global-frame --board scale_factor # Emit coordinates in the shared frame pivtools-cli apply-calibration --board scale_factor --all-paths

Next: Create Videos

Visualise your aligned, calibrated velocity fields with animated videos.

Continue to Video Maker
PIVtools - High-Performance PIV Processing