Skip to content

calibration

A rig's camera parameters as a document, and their refinement by bundle adjustment.

A Calibration is one estimate of a rig: a label, each view's ViewSpec (a hand-written orbit placement or a solve's raw parameters) and whether it is solved, which keeps it read-only in the GUI and records what it was refined from and the fingerprint of the stage that refined it (ADR-0036). A project keeps a set of them under calibrations/ and its manifest names the active one (ADR-0016, ADR-0081); a run stores the same document as its calibration attribute (ADR-0086). refine_bundle is the solve.

Calibration

Bases: Table

One estimate of a rig's parameters, as a project's calibrations/*.toml has it.

Attributes:

Name Type Description
label str

A human tag ("initial", "bundle-adjustment"), also the file's slug.

views dict[str, ViewSpec]

[views.<name>] in order, the V axis.

solved bool

Whether it came out of bundle adjustment; a hand-edited spec is not.

refined_from int | None

The id of the calibration it was refined from, when solved.

fingerprint str | None

The producing [bundle_adjustment] stage's fingerprint, so a rerun recognizes its own output.

copied_from int | None

On the copy a run stores as its initial parameters, the id of the project calibration the values were copied from, when they were (ADR-0086); a calibration imported from the run records it as refined_from.

Source code in src/deeperfly/calibration.py
class Calibration(Table):
    """One estimate of a rig's parameters, as a project's `calibrations/*.toml` has it.

    Attributes:
        label: A human tag (`"initial"`, `"bundle-adjustment"`), also the file's slug.
        views: `[views.<name>]` in order, the `V` axis.
        solved: Whether it came out of bundle adjustment; a hand-edited spec is not.
        refined_from: The id of the calibration it was refined from, when solved.
        fingerprint: The producing `[bundle_adjustment]` stage's fingerprint, so a
            rerun recognizes its own output.
        copied_from: On the copy a run stores as its initial parameters, the id of
            the project calibration the values were copied from, when they were
            (ADR-0086); a calibration imported from the run records it as
            `refined_from`.
    """

    label: str
    views: dict[str, ViewSpec]
    solved: bool = False
    refined_from: int | None = None
    fingerprint: str | None = None
    copied_from: int | None = None

    @model_validator(mode="after")
    def _placed(self):
        """Refuse a calibration that places no view -- there is no `V` axis."""
        if not self.views:
            raise ValueError("[views]: a calibration places at least one view")
        return self

    @classmethod
    def of_rig(
        cls,
        label: str,
        rig: Rig,
        *,
        solved: bool = False,
        refined_from: int | None = None,
        fingerprint: str | None = None,
        frame_hw: Mapping[str, tuple[int, int]] | None = None,
    ) -> Calibration:
        """A calibration of raw views standing for `rig`, solved in `frame_hw`."""
        views: dict[str, ViewSpec] = {**rig.specs(frame_hw)}
        return cls(
            label=label,
            views=views,
            solved=solved,
            refined_from=refined_from,
            fingerprint=fingerprint,
        )

    @property
    def names(self) -> list[str]:
        """View names, in order (the `V` axis)."""
        return list(self.views)

    @property
    def kind(self) -> str:
        """`"orbit"` or `"raw"` when every view is written that way, else `"mixed"`."""
        kinds = {
            "orbit" if isinstance(v, OrbitView) else "raw" for v in self.views.values()
        }
        return kinds.pop() if len(kinds) == 1 else "mixed"

    def resolve(self, frame_hw: Mapping[str, tuple[int, int]] | None = None) -> Rig:
        """The concrete rig; `frame_hw` sets an orbit view's default principal point."""
        return Rig.from_specs(self.views, frame_hw)

kind property

kind: str

"orbit" or "raw" when every view is written that way, else "mixed".

names property

names: list[str]

View names, in order (the V axis).

of_rig classmethod

of_rig(
    label: str,
    rig: Rig,
    *,
    solved: bool = False,
    refined_from: int | None = None,
    fingerprint: str | None = None,
    frame_hw: Mapping[str, tuple[int, int]] | None = None,
) -> Calibration

A calibration of raw views standing for rig, solved in frame_hw.

Source code in src/deeperfly/calibration.py
@classmethod
def of_rig(
    cls,
    label: str,
    rig: Rig,
    *,
    solved: bool = False,
    refined_from: int | None = None,
    fingerprint: str | None = None,
    frame_hw: Mapping[str, tuple[int, int]] | None = None,
) -> Calibration:
    """A calibration of raw views standing for `rig`, solved in `frame_hw`."""
    views: dict[str, ViewSpec] = {**rig.specs(frame_hw)}
    return cls(
        label=label,
        views=views,
        solved=solved,
        refined_from=refined_from,
        fingerprint=fingerprint,
    )

resolve

resolve(
    frame_hw: Mapping[str, tuple[int, int]] | None = None,
) -> Rig

The concrete rig; frame_hw sets an orbit view's default principal point.

Source code in src/deeperfly/calibration.py
def resolve(self, frame_hw: Mapping[str, tuple[int, int]] | None = None) -> Rig:
    """The concrete rig; `frame_hw` sets an orbit view's default principal point."""
    return Rig.from_specs(self.views, frame_hw)

calibration_files

calibration_files(
    directory: str | Path,
) -> list[tuple[int, Path]]

The (id, path) of each NNNN-<label>.toml in a directory, by id.

Source code in src/deeperfly/calibration.py
def calibration_files(directory: str | Path) -> list[tuple[int, Path]]:
    """The `(id, path)` of each `NNNN-<label>.toml` in a directory, by id."""
    directory = Path(directory)
    if not directory.is_dir():
        return []
    found: list[tuple[int, Path]] = []
    for path in directory.glob("*.toml"):
        m = re.match(r"^(\d+)-", path.name)
        if m is not None:
            found.append((int(m.group(1)), path))
    return sorted(found)

calibration_ids

calibration_ids(directory: str | Path) -> list[int]

The ids of the calibrations in a calibrations/ directory, ascending.

Source code in src/deeperfly/calibration.py
def calibration_ids(directory: str | Path) -> list[int]:
    """The ids of the calibrations in a `calibrations/` directory, ascending."""
    return [cid for cid, _ in calibration_files(directory)]

calibration_path

calibration_path(
    directory: str | Path, cid: int
) -> Path | None

The file of calibration cid in a calibrations/ directory, or None.

Source code in src/deeperfly/calibration.py
def calibration_path(directory: str | Path, cid: int) -> Path | None:
    """The file of calibration `cid` in a `calibrations/` directory, or `None`."""
    return next((p for c, p in calibration_files(directory) if c == cid), None)

calibration_toml

calibration_toml(calib: Calibration) -> str

The TOML text of a calibration: the head keys, then a [views.<name>] each.

A key at its default is left out, so a hand-written file stays as short.

Source code in src/deeperfly/calibration.py
def calibration_toml(calib: Calibration) -> str:
    """The TOML text of a calibration: the head keys, then a `[views.<name>]` each.

    A key at its default is left out, so a hand-written file stays as short.
    """
    return toml.dumps(calib.model_dump(exclude_defaults=True))

parse_calibration

parse_calibration(text: str) -> Calibration

A calibration from the TOML text of a calibrations/*.toml, strictly.

Raises:

Type Description
ConfigError

If the text is not valid TOML, a key is unknown, or a view's placement is not whole.

Source code in src/deeperfly/calibration.py
def parse_calibration(text: str) -> Calibration:
    """A calibration from the TOML text of a `calibrations/*.toml`, strictly.

    Raises:
        ConfigError: If the text is not valid TOML, a key is unknown, or a view's
            placement is not whole.
    """
    try:
        data = tomllib.loads(text)
    except tomllib.TOMLDecodeError as exc:
        raise ConfigError(f"not valid TOML: {exc}") from exc
    return parse(Calibration, data, "")

read_calibration

read_calibration(path: str | Path) -> Calibration

Read a calibrations/*.toml (a run stores the same text as calibration).

Raises:

Type Description
ConfigError

As parse_calibration, naming the file.

Source code in src/deeperfly/calibration.py
def read_calibration(path: str | Path) -> Calibration:
    """Read a `calibrations/*.toml` (a run stores the same text as `calibration`).

    Raises:
        ConfigError: As `parse_calibration`, naming the file.
    """
    try:
        return parse_calibration(Path(path).read_text())
    except ConfigError as exc:
        raise ConfigError(f"{path}: {exc}") from exc

refine_bundle

refine_bundle(
    rig: Rig,
    pts2d: Float[ndarray, "V N 2"],
    weights: Float[ndarray, "V N"] | None = None,
    *,
    fixed: Sequence[str] = ("*.intr", "*.dist"),
    shared: Sequence[Sequence[str]] = (),
    loss: str = "huber",
    threshold_px: float = 8.0,
    max_nfev: int = 200,
) -> Rig

Bundle-adjust rig to fit pts2d, holding the fixed parameters.

Each column of pts2d (V, N, 2) is one 3D track, and one seen in fewer than two views is dropped. By default only the poses move: a ring of cameras around a tethered fly sees 2 mm of depth and cannot tell a focal from a distance. A rig that sees depth should free the focal and share it per physical camera (ADR-0057): fixed without "*.intr" (keep "*.intr[2]", "*.intr[3]") and a shared group per camera. With every pose free the fit has seven flat gauge directions, so fix one view's rvec and tvec to hold the world frame. fixed and shared use build_state's grammar; weights are per observation (confidences), loss and threshold_px the robust loss. Returns the refined rig.

Raises:

Type Description
ValueError

If no track is seen in two views, or a fixed reference names an unknown view or parameter.

Source code in src/deeperfly/calibration.py
def refine_bundle(
    rig: Rig,
    pts2d: Float[np.ndarray, "V N 2"],
    weights: Float[np.ndarray, "V N"] | None = None,
    *,
    fixed: Sequence[str] = ("*.intr", "*.dist"),
    shared: Sequence[Sequence[str]] = (),
    loss: str = "huber",
    threshold_px: float = 8.0,
    max_nfev: int = 200,
) -> Rig:
    """Bundle-adjust `rig` to fit `pts2d`, holding the `fixed` parameters.

    Each column of `pts2d` `(V, N, 2)` is one 3D track, and one seen in fewer than two
    views is dropped. By default only the poses move: a ring of cameras around a
    tethered fly sees 2 mm of depth and cannot tell a focal from a distance. A rig
    that sees depth should free the focal and share it per physical camera (ADR-0057):
    `fixed` without `"*.intr"` (keep `"*.intr[2]"`, `"*.intr[3]"`) and a `shared` group
    per camera. With every pose free the fit has seven flat gauge directions, so fix
    one view's `rvec` and `tvec` to hold the world frame. `fixed` and `shared` use
    [build_state][deeperfly.bundle_adjustment.build_state]'s grammar; `weights` are
    per observation (confidences), `loss` and `threshold_px` the robust loss.
    Returns the refined rig.

    Raises:
        ValueError: If no track is seen in two views, or a `fixed` reference names an
            unknown view or parameter.
    """
    pts2d = np.asarray(pts2d, dtype=float)
    seen = np.isfinite(pts2d).all(axis=-1)  # (V, N)
    keep = seen.sum(axis=0) >= 2
    if not keep.any():
        raise ValueError("no keypoint is labeled in two or more views")
    pts2d = pts2d[:, keep]
    weights = None if weights is None else np.asarray(weights)[:, keep]

    state = build_state(
        rig.rvecs,
        rig.tvecs,
        rig.intrs,
        rig.dists,
        rig.mirrored,
        pts2d,
        names=rig.names,
        fixed=list(fixed),
        shared=[list(g) for g in shared],
    )
    _, sol = bundle_adjust(
        *state,
        loss=loss,
        f_scale=threshold_px,
        max_nfev=max_nfev,
        weights=weights,
    )
    return Rig.from_arrays(
        rig.names,
        sol.rvecs,
        sol.tvecs,
        sol.intrs,
        sol.dists,
        mirrored=rig.mirrored,
    )

write_calibration

write_calibration(
    directory: str | Path,
    calib: Calibration,
    cid: int | None = None,
) -> int

Write a calibration into a calibrations/ directory and return its id.

A new one takes the next free id, from 1; a given cid is overwritten in place, the file renamed if its label changed.

Raises:

Type Description
KeyError

If a given cid is not a calibration of the directory.

Source code in src/deeperfly/calibration.py
def write_calibration(
    directory: str | Path, calib: Calibration, cid: int | None = None
) -> int:
    """Write a calibration into a `calibrations/` directory and return its id.

    A new one takes the next free id, from 1; a given `cid` is overwritten in place,
    the file renamed if its label changed.

    Raises:
        KeyError: If a given `cid` is not a calibration of the directory.
    """
    directory = Path(directory)
    old = None
    if cid is None:
        cid = max(calibration_ids(directory), default=0) + 1
    else:
        old = calibration_path(directory, cid)
        if old is None:
            raise KeyError(f"no calibration {cid} in {directory}")
    path = directory / f"{cid:04d}-{_slug(calib.label)}.toml"
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(calibration_toml(calib))
    if old is not None and old != path:
        old.unlink()
    return cid