Skip to content

geometry

Multi-view geometry primitives in JAX, float64, jit- and grad-friendly.

A world point X maps to camera coordinates as R @ X + tvec, where R is R(rvec) for an ordinary camera and the improper diag(-1, 1, 1) @ R(rvec) for a mirrored one (ADR-0053, pose_rmat_one). Intrinsics intr are [fx, fy, cx, cy] or [f, cx, cy] (intr_to_kmat); distortion dists follow OpenCV's order [k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4]; pmat is K @ [R | t]. A function takes its operand first, then rvecs, tvecs, intrs, dists. The batched functions are jitted vmaps of the *_one variants, which compose with jax.jacfwd for bundle adjustment; the arrays are tiny, so JAX runs on the CPU.

A pixel coordinate is in the raw frame of the video the view reads: full resolution, the whole frame, shared by every view of that video (ADR-0055). A point -- a label, a prediction, cx and cy -- sits at a pixel center: pixel i spans [i - 0.5, i + 0.5], so a W-wide frame spans [-0.5, W - 0.5] and centers on (W - 1) / 2. A window bound -- a crop, a detector's box -- is a slice bound, image[y0:y1, x0:x1], on the pixel edges, so the same frame spans [0, W].

backproject_ray_one

backproject_ray_one(
    pixel: Float[Array, 2],
    rvec: Float[Array, 3],
    tvec: Float[Array, 3],
    intr: Float[Array, P],
    dist: Float[Array, K],
    mirrored: Bool[Array, ""],
) -> tuple[Float[Array, 3], Float[Array, 3]]

Back-project an image pixel to its viewing ray in world coordinates.

Inverts project_full_one: the ray origin + s * direction (s >= 0) is the set of world points that project to pixel. Returns (origin, direction), the camera center -R.T @ tvec and the unnormalized world direction; pair it with closest_point_on_ray to move a 3D point onto the ray.

Source code in src/deeperfly/geometry.py
def backproject_ray_one(
    pixel: Float[Array, "2"],
    rvec: Float[Array, "3"],
    tvec: Float[Array, "3"],
    intr: Float[Array, "P"],
    dist: Float[Array, "K"],
    mirrored: Bool[Array, ""],
) -> tuple[Float[Array, "3"], Float[Array, "3"]]:
    """Back-project an image pixel to its viewing ray in world coordinates.

    Inverts [project_full_one][]: the ray `origin + s * direction` (`s >= 0`) is the
    set of world points that project to `pixel`. Returns `(origin, direction)`, the
    camera center `-R.T @ tvec` and the unnormalized world direction; pair it with
    [closest_point_on_ray][] to move a 3D point onto the ray.
    """
    fx, fy = intr[0], intr[-3]
    cx, cy = intr[-2], intr[-1]
    xy_dist = jnp.stack([(pixel[0] - cx) / fx, (pixel[1] - cy) / fy])
    xy = undistort_one(xy_dist, dist)
    dir_cam = jnp.stack([xy[0], xy[1], jnp.ones(())])
    rmat = pose_rmat_one(rvec, mirrored)
    direction = rmat.T @ dir_cam
    origin = -rmat.T @ tvec
    return origin, direction

closest_point_on_ray

closest_point_on_ray(
    origin: Float[ArrayLike, 3],
    direction: Float[ArrayLike, 3],
    target: Float[ArrayLike, 3],
) -> Float[Array, 3]

The point on a ray nearest target, its orthogonal projection onto the line.

The 3D-correction drag moves a point to where its dragged pixel's ray passes closest to its old position, so it reprojects onto the pixel and moves the least.

Source code in src/deeperfly/geometry.py
def closest_point_on_ray(
    origin: Float[ArrayLike, "3"],
    direction: Float[ArrayLike, "3"],
    target: Float[ArrayLike, "3"],
) -> Float[Array, "3"]:
    """The point on a ray nearest `target`, its orthogonal projection onto the line.

    The 3D-correction drag moves a point to where its dragged pixel's ray passes
    closest to its old position, so it reprojects onto the pixel and moves the least.
    """
    origin, direction, target = map(jnp.asarray, (origin, direction, target))
    s = jnp.dot(target - origin, direction) / jnp.dot(direction, direction)
    return origin + s * direction

distort

distort(
    pts2d: Float[Array, "V *pts 2"],
    dists: Float[Array, "V K"],
) -> Float[Array, "V *pts 2"]

Apply OpenCV-style radial, tangential and thin-prism distortion.

For normalized (x, y) and r^2 = x^2 + y^2, with the coefficients [k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4] (zero beyond K, as in cv2.projectPoints):

x_d = x * (1 + k1 r^2 + k2 r^4 + k3 r^6)
        / (1 + k4 r^2 + k5 r^4 + k6 r^6)
      + 2 p1 x y + p2 (r^2 + 2 x^2) + s1 r^2 + s2 r^4
y_d = y * (...) / (...)
      + p1 (r^2 + 2 y^2) + 2 p2 x y + s3 r^2 + s4 r^4
Source code in src/deeperfly/geometry.py
@jax.jit
def distort(
    pts2d: Float[Array, "V *pts 2"],
    dists: Float[Array, "V K"],
) -> Float[Array, "V *pts 2"]:
    """Apply OpenCV-style radial, tangential and thin-prism distortion.

    For normalized `(x, y)` and `r^2 = x^2 + y^2`, with the coefficients
    `[k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4]` (zero beyond `K`, as in
    `cv2.projectPoints`):

    ```text
    x_d = x * (1 + k1 r^2 + k2 r^4 + k3 r^6)
            / (1 + k4 r^2 + k5 r^4 + k6 r^6)
          + 2 p1 x y + p2 (r^2 + 2 x^2) + s1 r^2 + s2 r^4
    y_d = y * (...) / (...)
          + p1 (r^2 + 2 y^2) + 2 p2 x y + s3 r^2 + s4 r^4
    ```
    """
    if dists.shape[-1] == 0:
        return pts2d
    v = pts2d.shape[0]
    flat = pts2d.reshape(v, -1, 2)
    out = jax.vmap(jax.vmap(distort_one, in_axes=(0, None)), in_axes=(0, 0))(
        flat, dists
    )
    return out.reshape(pts2d.shape)

distort_one

distort_one(
    xy: Float[Array, 2], dist: Float[Array, K]
) -> Float[Array, 2]

distort for one normalized 2D point; dist holds 0 to 12 coefficients.

Source code in src/deeperfly/geometry.py
def distort_one(
    xy: Float[Array, "2"],
    dist: Float[Array, "K"],
) -> Float[Array, "2"]:
    """[distort][] for one normalized 2D point; `dist` holds 0 to 12 coefficients."""
    n = dist.shape[-1]
    if n == 0:
        return xy
    x, y = xy[0], xy[1]
    x2, y2 = x * x, y * y
    r2 = x2 + y2

    num = 1.0 + dist[0] * r2
    r4 = r6 = jnp.zeros(())
    if n >= 2:
        r4 = r2 * r2
        num = num + dist[1] * r4
    if n >= 5:
        r6 = r4 * r2
        num = num + dist[4] * r6
    den = 1.0
    if n >= 6:
        den = den + dist[5] * r2
    if n >= 7:
        den = den + dist[6] * r4
    if n >= 8:
        den = den + dist[7] * r6
    mult = num / den

    add_x = jnp.zeros(())
    add_y = jnp.zeros(())
    xy_prod = x * y
    if n >= 3:
        add_x = 2 * dist[2] * xy_prod
        add_y = dist[2] * (r2 + 2 * y2)
    if n >= 4:
        add_x = add_x + dist[3] * (r2 + 2 * x2)
        add_y = add_y + 2 * dist[3] * xy_prod
    if n >= 9:
        add_x = add_x + dist[8] * r2
    if n >= 10:
        add_x = add_x + dist[9] * r4
    if n >= 11:
        add_y = add_y + dist[10] * r2
    if n >= 12:
        add_y = add_y + dist[11] * r4

    return jnp.stack([x * mult + add_x, y * mult + add_y])

intr_to_kmat

intr_to_kmat(
    intr: Float[Array, "*batch P"],
) -> Float[Array, "*batch 3 3"]

3x3 intrinsic matrices from packed [fx, fy, cx, cy] or [f, cx, cy].

Source code in src/deeperfly/geometry.py
@jax.jit
def intr_to_kmat(
    intr: Float[Array, "*batch P"],
) -> Float[Array, "*batch 3 3"]:
    """3x3 intrinsic matrices from packed `[fx, fy, cx, cy]` or `[f, cx, cy]`."""
    kmat = jnp.zeros((*intr.shape[:-1], 3, 3))
    kmat = kmat.at[..., 0, 0].set(intr[..., 0])
    kmat = kmat.at[..., 1, 1].set(intr[..., -3])
    kmat = kmat.at[..., :2, 2].set(intr[..., -2:])
    kmat = kmat.at[..., 2, 2].set(1.0)
    return kmat

pmats

pmats(
    rvecs: Float[Array, "*batch 3"],
    tvecs: Float[Array, "*batch 3"],
    intrs: Float[Array, "*batch P"],
    mirrored: Bool[Array, "*batch"],
) -> Float[Array, "*batch 3 4"]

Projection matrices K @ [R | t], the pinhole model without distortion.

The one place the package builds them, so no caller can forget the mirror.

Source code in src/deeperfly/geometry.py
@jax.jit
def pmats(
    rvecs: Float[Array, "*batch 3"],
    tvecs: Float[Array, "*batch 3"],
    intrs: Float[Array, "*batch P"],
    mirrored: Bool[Array, "*batch"],
) -> Float[Array, "*batch 3 4"]:
    """Projection matrices `K @ [R | t]`, the pinhole model without distortion.

    The one place the package builds them, so no caller can forget the mirror.
    """
    rtmat = jnp.concatenate((pose_rmat(rvecs, mirrored), tvecs[..., None]), axis=-1)
    return intr_to_kmat(intrs) @ rtmat

pose_rmat

pose_rmat(
    rvecs: Float[Array, "*batch 3"],
    mirrored: Bool[Array, "*batch"],
) -> Float[Array, "*batch 3 3"]

Effective rotations of poses that may be improper; see pose_rmat_one.

Source code in src/deeperfly/geometry.py
@jax.jit
def pose_rmat(
    rvecs: Float[Array, "*batch 3"],
    mirrored: Bool[Array, "*batch"],
) -> Float[Array, "*batch 3 3"]:
    """Effective rotations of poses that may be improper; see [pose_rmat_one][]."""
    flat = jax.vmap(pose_rmat_one)(rvecs.reshape(-1, 3), mirrored.reshape(-1))
    return flat.reshape(*rvecs.shape[:-1], 3, 3)

pose_rmat_one

pose_rmat_one(
    rvec: Float[ArrayLike, 3], mirrored: Bool[ArrayLike, ""]
) -> Float[Array, "3 3"]

The rotation of a pose that may be improper: S @ R(rvec) when mirrored.

S = diag(-1, 1, 1). A view through a prism sees the world reflected, which no axis-angle vector carries, so it is a proper rvec plus a flag (ADR-0053); S @ R is still orthogonal, so back-projection needs no special case.

Source code in src/deeperfly/geometry.py
def pose_rmat_one(
    rvec: Float[ArrayLike, "3"], mirrored: Bool[ArrayLike, ""]
) -> Float[Array, "3 3"]:
    """The rotation of a pose that may be improper: `S @ R(rvec)` when `mirrored`.

    `S = diag(-1, 1, 1)`. A view through a prism sees the world reflected, which no
    axis-angle vector carries, so it is a proper `rvec` plus a flag (ADR-0053); `S @ R`
    is still orthogonal, so back-projection needs no special case.
    """
    rmat = rvec_to_rmat_one(jnp.asarray(rvec))
    return rmat.at[0].multiply(jnp.where(mirrored, -1.0, 1.0))

project_full

project_full(
    pts3d: Float[Array, "*pts 3"],
    rvecs: Float[Array, "V 3"],
    tvecs: Float[Array, "V 3"],
    intrs: Float[Array, "V P"] | Float[Array, P],
    dists: Float[Array, "V K"] | Float[Array, K],
    mirrored: Bool[Array, V],
) -> Float[Array, "V *pts 2"]

Project 3D world points to 2D image points through full camera models.

X_cam = R X + tvec, divided by depth, distorted by distort and mapped through the intrinsics. intrs and dists may be one row shared by every view; mirrored is per view, never shared.

Source code in src/deeperfly/geometry.py
@jax.jit
def project_full(
    pts3d: Float[Array, "*pts 3"],
    rvecs: Float[Array, "V 3"],
    tvecs: Float[Array, "V 3"],
    intrs: Float[Array, "V P"] | Float[Array, "P"],
    dists: Float[Array, "V K"] | Float[Array, "K"],
    mirrored: Bool[Array, "V"],
) -> Float[Array, "V *pts 2"]:
    """Project 3D world points to 2D image points through full camera models.

    `X_cam = R X + tvec`, divided by depth, distorted by [distort][] and mapped through
    the intrinsics. `intrs` and `dists` may be one row shared by every view;
    `mirrored` is per view, never shared.
    """
    v = rvecs.shape[0]
    pts_shape = pts3d.shape[:-1]
    pts_flat = pts3d.reshape(-1, 3)
    if intrs.ndim == 1:
        intrs = jnp.broadcast_to(intrs, (v, intrs.shape[0]))
    if dists.ndim == 1:
        dists = jnp.broadcast_to(dists, (v, dists.shape[0]))
    project_v = jax.vmap(
        jax.vmap(project_full_one, in_axes=(0, None, None, None, None, None)),
        in_axes=(None, 0, 0, 0, 0, 0),
    )
    out = project_v(pts_flat, rvecs, tvecs, intrs, dists, mirrored)
    return out.reshape(v, *pts_shape, 2)

project_full_one

project_full_one(
    pt3d: Float[Array, 3],
    rvec: Float[Array, 3],
    tvec: Float[Array, 3],
    intr: Float[Array, P],
    dist: Float[Array, K],
    mirrored: Bool[Array, ""],
) -> Float[Array, 2]

Project one 3D point through one camera, composable with vmap and jacfwd.

mirrored comes last because it is never differentiated, and is required: a dropped mirror is silent on an unmirrored rig and wrong on a prism one.

Source code in src/deeperfly/geometry.py
def project_full_one(
    pt3d: Float[Array, "3"],
    rvec: Float[Array, "3"],
    tvec: Float[Array, "3"],
    intr: Float[Array, "P"],
    dist: Float[Array, "K"],
    mirrored: Bool[Array, ""],
) -> Float[Array, "2"]:
    """Project one 3D point through one camera, composable with `vmap` and `jacfwd`.

    `mirrored` comes last because it is never differentiated, and is required: a
    dropped mirror is silent on an unmirrored rig and wrong on a prism one.
    """
    rmat = pose_rmat_one(rvec, mirrored)
    p_cam = rmat @ pt3d + tvec
    xy = p_cam[:2] / p_cam[2]
    xy = distort_one(xy, dist)
    fx, fy = intr[0], intr[-3]
    cx, cy = intr[-2], intr[-1]
    return jnp.stack([fx * xy[0] + cx, fy * xy[1] + cy])

project_pmat

project_pmat(
    pts3d: Float[Array, "*pts 3"],
    pmats: Float[Array, "*views 3 4"],
) -> Float[Array, "*views *pts 2"]

Project 3D points to 2D through 3x4 projection matrices, (*views, *pts, 2).

Source code in src/deeperfly/geometry.py
@jax.jit
def project_pmat(
    pts3d: Float[Array, "*pts 3"],
    pmats: Float[Array, "*views 3 4"],
) -> Float[Array, "*views *pts 2"]:
    """Project 3D points to 2D through 3x4 projection matrices, `(*views, *pts, 2)`."""
    output_shape = (*pmats.shape[:-2], *pts3d.shape[:-1], 2)
    pmats_flat = pmats.reshape(-1, 3, 4)
    pts_flat = pts3d.reshape(-1, 3).T
    pts2dh = pmats_flat[:, :, :3] @ pts_flat + pmats_flat[:, :, 3:]
    pts2d = (pts2dh[:, :2] / pts2dh[:, 2:]).transpose(0, 2, 1)
    return pts2d.reshape(output_shape)

rmat_to_rvec

rmat_to_rvec(
    rmat: Float[Array, "*batch 3 3"],
) -> Float[Array, "*batch 3"]

Rotation matrices to axis-angle vectors, as OpenCV's Rodrigues does.

The axis comes from R - R^T, which vanishes at theta = pi; near it the axis is read off (R + I) / 2, with the signs from the off-diagonal entries.

Source code in src/deeperfly/geometry.py
@jax.jit
def rmat_to_rvec(
    rmat: Float[Array, "*batch 3 3"],
) -> Float[Array, "*batch 3"]:
    """Rotation matrices to axis-angle vectors, as OpenCV's `Rodrigues` does.

    The axis comes from `R - R^T`, which vanishes at `theta = pi`; near it the axis is
    read off `(R + I) / 2`, with the signs from the off-diagonal entries.
    """
    flat = rmat.reshape(-1, 3, 3)
    out = jax.vmap(rmat_to_rvec_one)(flat)
    return out.reshape(*rmat.shape[:-2], 3)

rmat_to_rvec_one

rmat_to_rvec_one(
    rmat: Float[Array, "3 3"],
) -> Float[Array, 3]

Rotation matrix to axis-angle vector, for one rotation.

Source code in src/deeperfly/geometry.py
def rmat_to_rvec_one(rmat: Float[Array, "3 3"]) -> Float[Array, "3"]:
    """Rotation matrix to axis-angle vector, for one rotation."""
    r00, r01, r02 = rmat[0, 0], rmat[0, 1], rmat[0, 2]
    r10, r11, r12 = rmat[1, 0], rmat[1, 1], rmat[1, 2]
    r20, r21, r22 = rmat[2, 0], rmat[2, 1], rmat[2, 2]

    rho = jnp.array([r21 - r12, r02 - r20, r10 - r01])
    sin_theta = jnp.linalg.norm(rho) / 2
    cos_theta = jnp.clip((r00 + r11 + r22 - 1) / 2, -1.0, 1.0)
    theta = jnp.arccos(cos_theta)
    near_pi = sin_theta < _NEAR_PI_SIN_THRESH

    rvec = rho * (theta / jnp.where(near_pi, 1.0, 2.0 * sin_theta))

    ax = jnp.sqrt(jnp.maximum((r00 + 1.0) / 2, 0.0))
    ay = jnp.sqrt(jnp.maximum((r11 + 1.0) / 2, 0.0)) * jnp.where(r01 < 0, -1.0, 1.0)
    az = jnp.sqrt(jnp.maximum((r22 + 1.0) / 2, 0.0)) * jnp.where(r02 < 0, -1.0, 1.0)
    flip = (
        (jnp.abs(ax) < jnp.abs(ay))
        & (jnp.abs(ax) < jnp.abs(az))
        & ((r12 > 0) != (ay * az > 0))
    )
    az = jnp.where(flip, -az, az)
    axis = jnp.array([ax, ay, az])
    axis_norm = jnp.linalg.norm(axis)
    rvec_pi = axis * (theta / jnp.where(axis_norm == 0, 1.0, axis_norm))
    rvec_degenerate = jnp.where(cos_theta > 0, 0.0, rvec_pi)

    return jnp.where(near_pi, rvec_degenerate, rvec)

rvec_to_rmat

rvec_to_rmat(
    rvec: Float[Array, "*batch 3"],
) -> Float[Array, "*batch 3 3"]

Axis-angle rotation vectors to rotation matrices (Rodrigues).

R = I + a W + b W^2 with W = skew(rvec), a = sin(t) / t and b = (1 - cos(t)) / t^2, taken from their Taylor series for small t, so the result stays orthogonal to machine precision and there is no 0/0 at t = 0.

Source code in src/deeperfly/geometry.py
@jax.jit
def rvec_to_rmat(
    rvec: Float[Array, "*batch 3"],
) -> Float[Array, "*batch 3 3"]:
    """Axis-angle rotation vectors to rotation matrices (Rodrigues).

    `R = I + a W + b W^2` with `W = skew(rvec)`, `a = sin(t) / t` and
    `b = (1 - cos(t)) / t^2`, taken from their Taylor series for small `t`, so the
    result stays orthogonal to machine precision and there is no `0/0` at `t = 0`.
    """
    flat = rvec.reshape(-1, 3)
    out = jax.vmap(rvec_to_rmat_one)(flat)
    return out.reshape(*rvec.shape[:-1], 3, 3)

rvec_to_rmat_one

rvec_to_rmat_one(
    rvec: Float[Array, 3],
) -> Float[Array, "3 3"]

Rodrigues' rotation for one rotation vector (axis times angle in radians).

Source code in src/deeperfly/geometry.py
def rvec_to_rmat_one(rvec: Float[Array, "3"]) -> Float[Array, "3 3"]:
    """Rodrigues' rotation for one rotation vector (axis times angle in radians)."""
    theta_sq = jnp.dot(rvec, rvec)
    small = theta_sq < _SMALL_THETA_SQ
    # Guard the sqrt too: its derivative is infinite at `theta_sq = 0`, and reverse-mode
    # AD multiplies that by the unused branch's zero cotangent into a NaN the `where`
    # keeps, which the BA Jacobian hits for any camera at `rvec = 0`.
    theta = jnp.sqrt(jnp.where(small, 1.0, theta_sq))
    sinc_theta = jnp.where(
        small,
        1 - theta_sq / 6 + theta_sq**2 / 120,
        jnp.sin(theta) / theta,
    )
    cosc_theta = jnp.where(
        small,
        0.5 - theta_sq / 24 + theta_sq**2 / 720,
        (1 - jnp.cos(theta)) / jnp.where(small, 1.0, theta_sq),
    )
    rx, ry, rz = rvec[0], rvec[1], rvec[2]
    skew = jnp.array(
        [
            [0.0, -rz, ry],
            [rz, 0.0, -rx],
            [-ry, rx, 0.0],
        ]
    )
    outer = jnp.outer(rvec, rvec)
    return (
        (1 - cosc_theta * theta_sq) * jnp.eye(3)
        + sinc_theta * skew
        + cosc_theta * outer
    )

triangulate_dlt

triangulate_dlt(
    pts2d: Float[Array, "V *pts 2"],
    pmats: Float[Array, "V 3 4"],
    weights: Float[Array, V * pts] | None = None,
    method: str = "invit",
) -> Float[Array, "*pts 3"]

Triangulate 3D points by direct linear transformation (DLT).

The homogeneous point is the null vector of the rows [x * p3 - p1; y * p3 - p2] of every view, the smallest eigenvector of A^T A. A point with fewer than two valid views, or degenerate views, is NaN. weights scale each view's rows by sqrt(weight), as bundle adjustment weighs confidences; a zero, negative or non-finite weight drops the view. method is "invit", inverse iteration with an explicit adjugate, or "eigh", slower but robust on degenerate systems; they agree to ~1e-12 where the triangulation is well posed (ADR-0043).

Source code in src/deeperfly/geometry.py
@partial(jax.jit, static_argnames="method")
def triangulate_dlt(
    pts2d: Float[Array, "V *pts 2"],
    pmats: Float[Array, "V 3 4"],
    weights: Float[Array, "V *pts"] | None = None,
    method: str = "invit",
) -> Float[Array, "*pts 3"]:
    """Triangulate 3D points by direct linear transformation (DLT).

    The homogeneous point is the null vector of the rows `[x * p3 - p1; y * p3 - p2]`
    of every view, the smallest eigenvector of `A^T A`. A point with fewer than two
    valid views, or degenerate views, is NaN. `weights` scale each view's rows by
    `sqrt(weight)`, as bundle adjustment weighs confidences; a zero, negative or
    non-finite weight drops the view. `method` is `"invit"`, inverse iteration with an
    explicit adjugate, or `"eigh"`, slower but robust on degenerate systems; they agree
    to ~1e-12 where the triangulation is well posed (ADR-0043).
    """
    ata, enough = _dlt_normal_eqs(pts2d, pmats, weights)
    if method == "eigh":
        eigvals, eigvecs = jnp.linalg.eigh(ata)
        vec = eigvecs[..., :, 0]
        # A near-zero second-smallest eigenvalue is the degenerate rig the adjugate
        # flags for "invit"; NaN it so both methods agree.
        tr = eigvals.sum(axis=-1)
        degenerate = eigvals[..., 1] < _DLT_DEGENERATE_TOL * jnp.where(tr > 0, tr, 1.0)
    else:
        vec, _, degenerate = _smallest_eigvec_invit(ata)
    return _dehomogenize(vec, enough & ~degenerate)

triangulate_dlt_flagged

triangulate_dlt_flagged(
    pts2d: Float[Array, "V *pts 2"],
    pmats: Float[Array, "V 3 4"],
    weights: Float[Array, V * pts] | None = None,
) -> tuple[Float[Array, "*pts 3"], Bool[Array, "*pts"]]

Fast DLT triangulation, plus a mask of the points to re-solve with eigh.

triangulate_dlt with method="invit", and the illcond mask of solved points too ill-conditioned (condition number above ~1e11) to trust; NaN points are not in it, since no solver rescues them.

Source code in src/deeperfly/geometry.py
@jax.jit
def triangulate_dlt_flagged(
    pts2d: Float[Array, "V *pts 2"],
    pmats: Float[Array, "V 3 4"],
    weights: Float[Array, "V *pts"] | None = None,
) -> tuple[Float[Array, "*pts 3"], Bool[Array, "*pts"]]:
    """Fast DLT triangulation, plus a mask of the points to re-solve with `eigh`.

    [triangulate_dlt][deeperfly.geometry.triangulate_dlt] with `method="invit"`, and
    the `illcond` mask of solved points too ill-conditioned (condition number above
    ~1e11) to trust; NaN points are not in it, since no solver rescues them.
    """
    ata, enough = _dlt_normal_eqs(pts2d, pmats, weights)
    vec, illcond, degenerate = _smallest_eigvec_invit(ata)
    solvable = enough & ~degenerate
    return _dehomogenize(vec, solvable), illcond & solvable

undistort_one

undistort_one(
    xy_dist: Float[Array, 2], dist: Float[Array, K]
) -> Float[Array, 2]

Invert distort_one: a distorted normalized coordinate to an undistorted one.

OpenCV's fixed-point iteration (cv2.undistortPoints), starting from xy_dist, which has the intrinsics already removed. It turns a clicked pixel into a back-projection direction (backproject_ray_one); where the iteration diverges, outside the lens model's invertible region, the result falls back to xy_dist.

Source code in src/deeperfly/geometry.py
def undistort_one(
    xy_dist: Float[Array, "2"],
    dist: Float[Array, "K"],
) -> Float[Array, "2"]:
    """Invert [distort_one][]: a distorted normalized coordinate to an undistorted one.

    OpenCV's fixed-point iteration (`cv2.undistortPoints`), starting from `xy_dist`,
    which has the intrinsics already removed. It turns a clicked pixel into a
    back-projection direction ([backproject_ray_one][]); where the iteration diverges,
    outside the lens model's invertible region, the result falls back to `xy_dist`.
    """
    n = dist.shape[-1]
    if n == 0:
        return xy_dist
    xd, yd = xy_dist[0], xy_dist[1]
    x, y = xd, yd
    for _ in range(_UNDISTORT_ITERS):
        x2, y2 = x * x, y * y
        r2 = x2 + y2
        r4 = r2 * r2
        r6 = r4 * r2

        num = 1.0 + dist[0] * r2
        if n >= 2:
            num = num + dist[1] * r4
        if n >= 5:
            num = num + dist[4] * r6
        den = 1.0
        if n >= 6:
            den = den + dist[5] * r2
        if n >= 7:
            den = den + dist[6] * r4
        if n >= 8:
            den = den + dist[7] * r6
        icdist = den / num

        xy_prod = x * y
        add_x = jnp.zeros(())
        add_y = jnp.zeros(())
        if n >= 3:
            add_x = 2 * dist[2] * xy_prod
            add_y = dist[2] * (r2 + 2 * y2)
        if n >= 4:
            add_x = add_x + dist[3] * (r2 + 2 * x2)
            add_y = add_y + 2 * dist[3] * xy_prod
        if n >= 9:
            add_x = add_x + dist[8] * r2
        if n >= 10:
            add_x = add_x + dist[9] * r4
        if n >= 11:
            add_y = add_y + dist[10] * r2
        if n >= 12:
            add_y = add_y + dist[11] * r4

        x = (xd - add_x) * icdist
        y = (yd - add_y) * icdist

    # Outside the lens model's invertible region (a point near or behind the camera)
    # the iteration diverges: keep it only where it inverts better than no correction.
    result = jnp.stack([x, y])
    resid_iter = distort_one(result, dist) - xy_dist
    resid_none = distort_one(xy_dist, dist) - xy_dist
    good = jnp.all(jnp.isfinite(result)) & (
        jnp.dot(resid_iter, resid_iter) <= jnp.dot(resid_none, resid_none)
    )
    return jnp.where(good, result, xy_dist)