UnitField is a high-performance Python library for N-dimensional unit field transformations with interpolation. It provides efficient tools for mapping unit-space coordinates (values in [0, 1]) to transformed coordinates, with support for various interpolation methods and optimized backends.
- N-dimensional unit field transformations with flexible interpolation
- Multiple interpolation methods: Nearest neighbor (Manhattan/Euclidean), Linear, Cubic, Lanczos4
- Dual backends: NumPy for N-dimensional fields, OpenCV for optimized 2D operations
- 2D image remapping with endomorphism composition
- Type-safe with comprehensive type hints
- Well-tested with extensive test coverage
- Performance optimized with LRU caching and vectorized operations
git clone https://github.com/Grayjou/UnitField.git
cd UnitField
pip install -e .- Python >= 3.8
- NumPy >= 1.20.0
- OpenCV (cv2) >= 4.5.0
- boundednumbers >= 0.1.0
- typing-extensions >= 4.0.0
import numpy as np
from unitfield.core.unitfield import MappedUnitField
from unitfield.core.enums import InterpMethod
# Create a 2D unit field (5x5 grid mapping to 2D vectors)
x, y = np.meshgrid(np.linspace(0, 1, 5), np.linspace(0, 1, 5))
data = np.stack([x, y], axis=-1)
# Create the field with linear interpolation
field = MappedUnitField(data=data, interp_method=InterpMethod.LINEAR)
# Query single coordinate
result = field.get_value((0.5, 0.5))
print(f"Value at (0.5, 0.5): {result}")
# Query multiple coordinates
coords = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
results = field.get_values(coords)
print(f"Batch results shape: {results.shape}")import numpy as np
import cv2
from unitfield.core.unitfield import Unit2DMappedEndomorphism
from unitfield.core.enums import InterpMethod
# Create an identity endomorphism
height, width = 100, 100
xs, ys = np.meshgrid(
np.linspace(0, 1, width),
np.linspace(0, 1, height),
indexing='xy'
)
identity_data = np.stack([xs, ys], axis=-1)
# Create endomorphism with cubic interpolation
endo = Unit2DMappedEndomorphism(
data=identity_data,
interp_method=InterpMethod.CUBIC
)
# Remap an image
image = cv2.imread('input.jpg')
remapped = endo.remap(image, interpolation=cv2.INTER_LINEAR)
# Compose two endomorphisms
endo2 = Unit2DMappedEndomorphism(data=other_data)
composed = endo.compose(endo2)N-dimensional unit field with interpolation.
Parameters:
data(UnitArray): N+1 dimensional array of shape (*spatial_dims, N)interp_method(InterpMethod): Interpolation strategy (default: NEAREST_MANHATTAN)cache_size(int, optional): LRU cache size for single queries (default: 128)
Methods:
get_value(coords): Get value at single coordinateget_values(coords_array): Get values at multiple coordinateswith_interp_method(method): Create copy with different interpolation
2D unit field endomorphism with optimized OpenCV backend.
Parameters:
data(UnitArray): 3-dimensional array of shape (H, W, 2)interp_method(InterpMethod): Interpolation strategycache_size(int, optional): LRU cache size
Methods:
get_value(coords): Get single coordinate valueget_values(coords_array): Get multiple coordinate valuesrasterize_mapping(width, height): Convert to pixel-space mappingremap(data): Remap arbitrary (H, W, J) arraycompose(other): Compose with another endomorphism
from unitfield.core.enums import InterpMethod
# Available methods:
InterpMethod.NEAREST_MANHATTAN # Nearest neighbor (Manhattan distance)
InterpMethod.NEAREST_EUCLIDEAN # Nearest neighbor (Euclidean distance)
InterpMethod.LINEAR # Linear/bilinear interpolation
InterpMethod.CUBIC # Cubic/bicubic interpolation
InterpMethod.LANCZOS4 # Lanczos-4 interpolationRemap arbitrary tensors using pixel-space mappings.
from unitfield.core.unitfield import remap_tensor_cv2
result = remap_tensor_cv2(
data=tensor,
mapping=pixel_mapping,
interpolation=cv2.INTER_LINEAR,
border_mode=cv2.BORDER_REPLICATE,
border_value=0.0
)UnitField operates in unit space where coordinates are in the range [0, 1]:
0.0represents the start of each dimension1.0represents the end of each dimension- Coordinates are automatically scaled to array indices
Important Limitations:
-
Infinite and NaN coordinates are NOT supported and will produce undefined behavior. This is by design to keep the functions simple and avoid overhead:
# ❌ AVOID - undefined behavior field.get_value((np.inf, 0.5)) field.get_value((np.nan, 0.5))
-
Out-of-bounds coordinates (< 0 or > 1) are handled via clipping to [0, 1] range:
# ✓ OK - will be clipped to valid range field.get_value((-0.5, 1.5)) # Treated as (0.0, 1.0)
-
Why this design?
- Unit field transformations are intended for normalized coordinate spaces
- Checking for inf/NaN on every coordinate adds unnecessary overhead
- Out-of-bounds handling beyond [0, 1] is simple to implement externally if needed
- Keeps the core functions fast and focused
Best Practices:
- Validate coordinates before passing to UnitField methods if they may contain special values
- For out-of-bounds behavior beyond simple clipping, preprocess coordinates externally
# 1D field
data_1d = np.linspace(0, 1, 100).reshape(-1, 1)
field_1d = MappedUnitField(data=data_1d, interp_method=InterpMethod.LINEAR)
# 3D field
data_3d = np.random.rand(10, 10, 10, 3)
field_3d = MappedUnitField(data=data_3d, interp_method=InterpMethod.CUBIC)# Create field with one method
field = MappedUnitField(data=data, interp_method=InterpMethod.LINEAR)
# Switch to another method
cubic_field = field.with_interp_method(InterpMethod.CUBIC)# Process large batches efficiently
batch_size = 1000
coords = np.random.rand(batch_size, 2)
results = field.get_values(coords)import numpy as np
import cv2
from unitfield.core.unitfield import Unit2DMappedEndomorphism
# Create a simple distortion field (barrel distortion)
h, w = 256, 256
y, x = np.ogrid[:h, :w]
center_x, center_y = w / 2, h / 2
# Calculate distance from center
dx = (x - center_x) / center_x
dy = (y - center_y) / center_y
r = np.sqrt(dx**2 + dy**2)
# Apply barrel distortion
distortion = 1 + 0.3 * r**2
new_x = center_x + dx * distortion * center_x
new_y = center_y + dy * distortion * center_y
# Normalize to unit space
unit_x = new_x / (w - 1)
unit_y = new_y / (h - 1)
distortion_field = np.stack([unit_x, unit_y], axis=-1).astype(np.float32)
# Create endomorphism and apply
endo = Unit2DMappedEndomorphism(data=distortion_field)
image = cv2.imread('input.jpg')
warped = endo.remap(image)
cv2.imwrite('output.jpg', warped)- Use caching for repeated queries: Set appropriate
cache_sizewhen creating fields - Batch operations: Use
get_values()instead of multipleget_value()calls - Choose the right backend: Use
Unit2DMappedEndomorphismfor 2D operations (faster via OpenCV) - Appropriate interpolation: Nearest neighbor is fastest, cubic/Lanczos are slower but smoother
pip install pytest pytest-benchmark
pytest tests/ -vThis project follows modern Python conventions:
- PEP 8 style guide
- Type hints throughout
- Comprehensive docstrings (Google style)
- Inf/NaN coordinates: Not supported in remapping functions (see Coordinate Constraints section)
- Memory usage: Large N-dimensional fields may consume significant memory
- 2D optimization: Only 2D endomorphisms benefit from OpenCV backend optimization
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
If you use UnitField in your research, please cite:
@software{unitfield2025,
author = {GrayJou},
title = {UnitField: N-dimensional Unit Field Transformations},
year = {2025},
url = {https://github.com/Grayjou/UnitField}
}- Built with NumPy and OpenCV
- Inspired by coordinate transformation needs in computer vision and graphics
For issues, questions, or contributions, please visit: