Affine#
Spline-interpolated pull-back affine transforms on 2-D or 3-D spatial data.
The module provides a general matrix-and-offset function, a rotation
convenience function, and a reusable geometry plan. Explicit spatial axes
allow remaining dimensions to act as independent batch or channel axes.
One-shot execution uses bounded coordinate tiles; cached plans retain geometry
behind a caller-controlled memory cap. AffinePlan.prepare_coefficients
returns an immutable, compatibility-checked AffineCoefficientField for
deliberate sharing when one sampled field is transformed through several
geometries. The raw-array prefilter path remains available for explicit
output-buffer workflows. AffineCoefficientField.save and
AffinePlan.load_coefficients provide a JSON-plus-numeric NPZ round trip
with atomic replacement, endian-portable schema-2 values, schema-1 reading,
compatibility validation, and no NumPy object-pickle loading. Equivalent SciPy
comparisons establish numerical parity, not a universal performance win.
- class splineops.affine.affine.AffineCoefficientField(values, spatial_axes, signature, configuration, serialization_metadata, *, _token=None)#
Bases:
objectImmutable tagged cardinal coefficients prepared by an affine plan.
Construct fields with
AffinePlan.prepare_coefficients(). The tag prevents accidentally applying coefficients with a different input grid, spline degree, boundary mode, or precision. Transform matrix and output shape are intentionally not part of the tag, so one prepared field can be reused across compatible affine geometries.- save(path) None#
Atomically save values and their compatibility contract to NPZ.
The file contains JSON metadata and a numeric array only; loading never enables NumPy object pickles. Restore it through
AffinePlan.load_coefficients(), which checks the stored contract against the receiving plan. A temporary file in the destination directory is flushed and atomically replaces the target, so a failed write cannot leave a partially updated archive.
- class splineops.affine.affine.AffinePlan(input_shape: tuple[int, ...], matrix: ~numpy._typing._array_like.ArrayLike, offset: ~numpy._typing._array_like.ArrayLike | None = None, *, output_shape: tuple[int, ...] | None = None, degree: int = 3, mode: str = 'zero', dtype: ~numpy._typing._dtype_like.DTypeLike = <class 'numpy.float64'>, cache_geometry: bool = True, max_retained_bytes: int = 268435456)#
Bases:
objectReusable pull-back affine transform on a fixed spatial geometry.
The plan can cache spline support indexes and weights for repeated frames. Non-spatial axes are treated independently, so explicit
spatial_axesprovide batch and channel support without changingTensorSpline.- Parameters:
input_shape (tuple of int) – Spatial input shape; two and three dimensions are supported.
matrix (ndarray) – Pull-back matrix mapping output coordinates to input coordinates.
offset (ndarray, optional) – Pull-back offset. Defaults to zero.
output_shape (tuple of int, optional) – Spatial output shape. Defaults to
input_shape.degree (int, optional) – B-spline degree from 0 through 7.
mode (str, optional) – TensorSpline boundary extension mode.
dtype (dtype, optional) – Floating execution precision.
cache_geometry (bool, optional) – Cache support indexes and weights. Disable for one-shot transforms.
max_retained_bytes (int, optional) – Hard limit for cached geometry arrays.
- apply(data: NDArray, *, spatial_axes: Sequence[int] | None = None, out: NDArray | None = None) ndarray#
Apply the fixed transform to samples or independent batches/channels.
- apply_coefficients(coefficients: NDArray | AffineCoefficientField, *, spatial_axes: Sequence[int] | None = None, out: NDArray | None = None) ndarray#
Apply the transform without prefiltering cardinal coefficients again.
Tagged fields from
prepare_coefficients()are checked against this plan. Raw arrays fromprefilter()remain supported for backward compatibility; their provenance is necessarily the caller’s responsibility.
- property configuration: dict[str, object]#
Copy of the fixed numerical contract represented by this plan.
- load_coefficients(path) AffineCoefficientField#
Load and validate a field saved by
AffineCoefficientField.save.
- prefilter(data: NDArray, *, spatial_axes: Sequence[int] | None = None, out: NDArray | None = None) ndarray#
Return cardinal coefficients for reuse across affine geometries.
- prepare_coefficients(data: NDArray, *, spatial_axes: Sequence[int] | None = None) AffineCoefficientField#
Return immutable coefficients with a checked compatibility tag.
Prefer this method when coefficients will be reused.
prefilterremains available for raw-array and explicit-output workflows.
- splineops.affine.affine.affine_transform(data: NDArray, matrix: ArrayLike, offset: ArrayLike | None = None, *, output_shape: tuple[int, ...] | None = None, spatial_axes: Sequence[int] | None = None, degree: int = 3, mode: str = 'zero', out: NDArray | None = None) ndarray#
Apply a one-shot two- or three-dimensional pull-back affine transform.
- splineops.affine.affine.rotate(data: NDArray, angle: float, axis: Tuple[float, float, float] | None = None, center: Tuple[float, float, float] | None = None, degree: int = 3, mode: str = 'zero', *, spatial_axes: Sequence[int] | None = None, out: NDArray | None = None) NDArray#
Rotate 2D or 3D data around a specified center using spline interpolation.
- Parameters:
data (ndarray) – 2D or 3D input data array to rotate.
angle (float) – Rotation angle in degrees.
axis (tuple of float, optional) – The axis of rotation for 3D data. Defaults to (0, 0, 1).
center (tuple of float, optional) – The center of rotation. Defaults to the array center.
degree (int, optional) – B-spline degree (0 to 7). Default is 3.
mode (str, optional) – Boundary handling mode (e.g., “zero”, “mirror”). Default is “zero”.
spatial_axes (sequence of int, optional) – Two or three axes to rotate. Required for arrays that also contain batch or channel dimensions; every non-spatial slice is transformed independently.
out (ndarray, optional) – Exact-shape and exact-dtype output buffer.
- Returns:
rotated_data – The data array after rotation.
- Return type:
ndarray
Examples
Rotate a 2D array by 45 degrees:
>>> import numpy as np >>> from splineops.affine import rotate >>> data = np.array([[1, 2], [3, 4]]) >>> rotated_data = rotate(data, angle=45) >>> rotated_data.shape (2, 2)
Rotate a 3D array around a custom axis:
>>> data_3d = np.random.rand(4, 4, 4) >>> rotated_data_3d = rotate(data_3d, angle=30, axis=(1, 0, 0)) >>> rotated_data_3d.shape (4, 4, 4)
See also#
TensorSplineThe independent continuous spline model used internally for interpolation.