"""
Functions for sanity checking inputs.
"""
from pathlib import Path
import numpy as np
from typing import Optional, Union
from jaxtyping import Float
[docs]
def assert_numpy(x, name=None):
"""
Assert that x is a numpy array.
Args:
x: Input to check
name: Optional name of the variable for error message
Raises:
ValueError: If x is not a numpy array
"""
if not isinstance(x, np.ndarray):
maybe_name = f" {name}" if name is not None else ""
raise ValueError(f"Expected{maybe_name} to be numpy array, but got {type(x)}.")
[docs]
def assert_K(K: Float[np.ndarray, "3 3"]):
"""
Assert that K is a valid 3x3 camera intrinsic matrix.
The intrinsic matrix K follows the standard form:
.. code-block::
[[fx, s, cx],
[ 0, fy, cy],
[ 0, 0, 1]]
where:
- fx, fy: focal lengths in pixels
- cx, cy: principal point coordinates
- s: skew coefficient (usually 0)
Args:
K: Camera intrinsic matrix to validate
Raises:
ValueError: If K is not a 3x3 matrix
"""
if K.shape != (3, 3):
raise ValueError(f"K must has shape (3, 3), but got {K} of shape {K.shape}.")
[docs]
def assert_T(T: Float[np.ndarray, "4 4"]):
"""
Assert that T is a valid 4x4 camera extrinsic matrix (world-to-camera transformation).
Args:
T: Camera extrinsic matrix to validate
Raises:
ValueError: If T is not a 4x4 matrix or bottom row is not [0, 0, 0, 1]
"""
if T.shape != (4, 4):
raise ValueError(f"T must has shape (4, 4), but got {T} of shape {T.shape}.")
is_valid = np.allclose(T[3, :], np.array([0, 0, 0, 1]))
if not is_valid:
raise ValueError(f"T must has [0, 0, 0, 1] the bottom row, but got {T}.")
[docs]
def assert_pose(pose: Float[np.ndarray, "4 4"]):
"""
Assert that pose is a valid 4x4 camera pose matrix (camera-to-world transformation).
Args:
pose: Camera pose matrix to validate
Raises:
ValueError: If pose is not a 4x4 matrix or bottom row is not [0, 0, 0, 1]
"""
if pose.shape != (4, 4):
raise ValueError(
f"pose must has shape (4, 4), but got {pose} of shape {pose.shape}."
)
is_valid = np.allclose(pose[3, :], np.array([0, 0, 0, 1]))
if not is_valid:
raise ValueError(f"pose must has [0, 0, 0, 1] the bottom row, but got {pose}.")
[docs]
def assert_shape(x: np.ndarray, shape: tuple, name: Optional[str] = None):
"""
Assert that an array has the expected shape.
The shape pattern can contain None values to indicate that dimension can be
any size. For example:
- (None, 3) matches any 2D array where the second dimension is 3
- (3, None, 3) matches any 3D array where first and last dimensions are 3
Args:
x: Array to validate
shape: Tuple of expected dimensions (can contain None for flexible dimensions)
name: Optional name of the variable for error message
Raises:
ValueError: If array dimensions don't match the expected shape pattern
"""
shape_valid = True
if shape_valid and x.ndim != len(shape):
shape_valid = False
if shape_valid:
for i, s in enumerate(shape):
if s is not None:
if x.shape[i] != s:
shape_valid = False
break
if not shape_valid:
name_must = f"{name} must" if name is not None else "Must"
raise ValueError(f"{name_must} has shape {shape}, but got shape {x.shape}.")
[docs]
def assert_shape_ndim(x: np.ndarray, ndim: int, name: Optional[str] = None):
"""
Assert that x has exactly ndim dimensions.
Args:
x: Array to validate
ndim: Expected number of dimensions
name: Optional name of the variable for error message
Raises:
ValueError: If array doesn't have exactly ndim dimensions
"""
if x.ndim != ndim:
name_must = f"{name} must" if name is not None else "Must"
raise ValueError(f"{name_must} have {ndim} dimensions, but got {x.ndim}.")
[docs]
def assert_shape_nx3(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 2D array with shape (N, 3).
This is commonly used for arrays of 3D points or vectors.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (None, 3), name=name)
[docs]
def assert_shape_nx2(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 2D array with shape (N, 2).
This is commonly used for arrays of 2D points or vectors.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (None, 2), name=name)
[docs]
def assert_shape_4x4(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 4x4 matrix.
This is commonly used for transformation matrices like T or pose.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (4, 4), name=name)
[docs]
def assert_shape_3x4(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 3x4 matrix.
This is commonly used for camera projection matrices.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (3, 4), name=name)
[docs]
def assert_shape_3x3(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 3x3 matrix.
This is commonly used for rotation matrices or intrinsic matrices.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (3, 3), name=name)
[docs]
def assert_shape_3(x: np.ndarray, name: Optional[str] = None):
"""
Assert that x is a 1D array with 3 elements.
This is commonly used for 3D vectors or points.
Args:
x: Array to validate
name: Optional name of the variable for error message
"""
assert_shape(x, (3,), name=name)
[docs]
def is_jpg_path(path: Union[str, Path]) -> bool:
"""
Check if a path has a JPG/JPEG file extension.
Args:
path: Path to check, can be string or Path object.
Returns:
True if path ends with .jpg or .jpeg (case insensitive), False otherwise.
"""
return Path(path).suffix.lower() in [".jpg", ".jpeg"]
[docs]
def is_png_path(path: Union[str, Path]) -> bool:
"""
Check if a path has a PNG file extension.
Args:
path: Path to check, can be string or Path object.
Returns:
True if path ends with .png (case insensitive), False otherwise.
"""
return Path(path).suffix.lower() in [".png"]