Skip to main content

affine transformations with units support

Project description

kaffine

codecov PyPI version Maintainer PyPI pyversions PyPI status PyPI download day

kaffine

Kaffine is inspired by pint and affine. nd aims to make the creation of unit-aware affine transformations as easy as possible. kaffine simplifies constructing complex 4x4 affine transformation matrices. It r offers native integration with Pint for physical units (mixing microns, millimeters, and inches), and bridges seamlessly with Scipy for advanced usage.

Designed for Computer Vision, Microscopy, and Robotics applications where "Sample-to-Camera" or "Stage-to-World" coordinates are critical.

🚀 Features

  • Fluent Interface: Chain operations like .translate_x(10).rotate_z(90).
  • Unit Aware: Native support for Pint. Move 100 microns on X and 2 mm on Y without manual conversion.
  • Hardware Ready: Includes decompose() to extract pixel size, rotation, and stage position from a raw matrix.
  • Type Safe: Built with modern Python 3.11+ type hints (typing.Self, | unions).
  • Scipy Bridge: Cast to/from scipy.spatial.transform.Rotation for quaternions and slerp.
  • Zero-Crash Defaults: Scipy and Pint are optional dependencies. The library falls back to pure NumPy if they aren't installed.

📦 Installation

uv add kaffine

⚡ Quick Start

1. The Simple Math Interface

Perfect for standard graphics or geometry.

from kaffine import Affine

# Create a transform: Move 10 units X, then Rotate 45 deg Z
# (Note: Order is intuitive "Operation 1 -> Operation 2")
tf = Affine.new().translate_x(10).rotate_z(45)

# Apply to a point
point = [0, 0, 0]
result = tf * point 

print(result) 
# Output (4D homogeneous): [10.0, 0.0, 0.0, 1.0]

2. The "Hardware" Interface (with Units)

Perfect for controlling stages, cameras, or mixing scales.

from kaffine import Affine
import pint

ureg = pint.UnitRegistry()

# Define your setup's units
ureg.define("step = 0.1 um")  # Piezo stepper resolution
ureg.define("pixel = 3.45 um") # Camera sensor pixel size

# Construct a Matrix that converts Pixels to Stage Coordinates (mm)
# Scenario: 
# 1. Scale pixels to physical size
# 2. Rotate camera 90 degrees
# 3. Translate to stage position (defined in steps)
tf = Affine.new(base_unit="um", registry=ureg) \
    .scale_uniform("1 pixel") \
    .rotate_z(90) \
    .translate_x("50000 step") 

# Transform a point from Pixel Space to Millimeters
pixel_point = [100, 100, 0] # 100x100 px on image
world_point = tf.apply(pixel_point)

print(world_point)
# Output: <Quantity([5.0 -0.345 0. 1.], 'millimeter')>

📚 Advanced Usage

Matrix Decomposition (Calibration)

If you have a calibration matrix and need to know the physical properties of your setup (e.g., "What is my effective pixel size?"), use .decompose().

# Assume 'tf' is a calibrated matrix loaded from a file
info = tf.decompose()

print(f"Position:   {info.translation}") # e.g., [10.5, 2.0, 0] mm
print(f"Pixel Size: {info.scale}")       # e.g., [0.00345, 0.00345, 1.0]
print(f"Rotation:   {info.rotation}")    # e.g., [0, 0, 1.5] degree

Scipy Interoperability

Need Quaternions or Spherical Interpolation?

tf = Affine.new().rotate_x(45).rotate_y(30)

# Cast to Scipy
r_scipy = tf.to_scipy_rotation()
print(r_scipy.as_quat())

# ... do complex math ...
r_scipy_inverted = r_scipy.inv()

# Cast back
tf_inv = Affine.from_scipy(r_scipy_inverted)

🛠 API Reference

Affine.new(base_unit="mm", registry=None)

Starts a new transformation chain.

  • base_unit: The physical unit the internal matrix numbers represent.
  • registry: Your pint.UnitRegistry.

Builder Methods

All methods return a new Affine instance (immutable-style chaining).

  • .translate(x, y, z) / .translate_x(val) ...
  • .rotate(angle, axis='z') / .rotate_x(angle) ...
  • .scale(x, y, z) / .scale_uniform(s)

Application

  • tf * [x, y, z]: Returns a raw Numpy array (4D).
  • tf * other_tf: Composes two transforms.
  • tf.apply(points): Accepts/Returns Pint Quantities.

Utilities

  • tf.decompose(): Returns Decomposition(translation, scale, rotation).
  • tf.to_scipy_rotation(): Returns scipy.spatial.transform.Rotation.

🧪 Running Tests

The project includes a comprehensive pytest suite covering math accuracy, unit conversion, and edge cases.

uv run pytest tests/

📄 License

MIT License. Feel free to use this in your commercial or open-source projects.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kaffine-1.1.0.tar.gz (7.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kaffine-1.1.0-py3-none-any.whl (6.9 kB view details)

Uploaded Python 3

File details

Details for the file kaffine-1.1.0.tar.gz.

File metadata

  • Download URL: kaffine-1.1.0.tar.gz
  • Upload date:
  • Size: 7.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for kaffine-1.1.0.tar.gz
Algorithm Hash digest
SHA256 097ce8f7550a1c9724e3cf77b3bee2527ec1bd64d14e410b4113d7101bd58109
MD5 8aadd3a14aaafdba7bf8a73c7c9d34cf
BLAKE2b-256 d6150246c7fd3bb1b8319fd2a9226329d3acf3a4abbede61675636f4bca21257

See more details on using hashes here.

File details

Details for the file kaffine-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: kaffine-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 6.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for kaffine-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1bb4e11784a056639d45261323b3ca4d8a010b25f450cb187128d8a59a872ba7
MD5 bc144d0c4d5bc6a15a92202785366003
BLAKE2b-256 a62e839811feabd8e8a7899dbd97f8948f86165c57f57fa11910c64eb5ae1347

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page