unitfield 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unitfield-0.1.0/LICENSE +21 -0
- unitfield-0.1.0/PKG-INFO +377 -0
- unitfield-0.1.0/README.md +324 -0
- unitfield-0.1.0/pyproject.toml +201 -0
- unitfield-0.1.0/setup.cfg +4 -0
- unitfield-0.1.0/tests/test_core.py +972 -0
- unitfield-0.1.0/tests/test_interp.py +771 -0
- unitfield-0.1.0/unitfield/__init__.py +21 -0
- unitfield-0.1.0/unitfield/core/__init__.py +23 -0
- unitfield-0.1.0/unitfield/core/enums.py +43 -0
- unitfield-0.1.0/unitfield/core/types.py +31 -0
- unitfield-0.1.0/unitfield/core/unitfield.py +609 -0
- unitfield-0.1.0/unitfield/interpolation/__init__.py +13 -0
- unitfield-0.1.0/unitfield/interpolation/interp_cv2.py +142 -0
- unitfield-0.1.0/unitfield/interpolation/interp_np.py +342 -0
- unitfield-0.1.0/unitfield/py.typed +2 -0
- unitfield-0.1.0/unitfield.egg-info/PKG-INFO +377 -0
- unitfield-0.1.0/unitfield.egg-info/SOURCES.txt +19 -0
- unitfield-0.1.0/unitfield.egg-info/dependency_links.txt +1 -0
- unitfield-0.1.0/unitfield.egg-info/requires.txt +23 -0
- unitfield-0.1.0/unitfield.egg-info/top_level.txt +1 -0
unitfield-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 GrayJou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
unitfield-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: unitfield
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance N-dimensional unit field transformations with interpolation
|
|
5
|
+
Author-email: GrayJou <grayjou@users.noreply.github.com>
|
|
6
|
+
Maintainer-email: GrayJou <grayjou@users.noreply.github.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/Grayjou/UnitField
|
|
9
|
+
Project-URL: Documentation, https://github.com/Grayjou/UnitField#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/Grayjou/UnitField
|
|
11
|
+
Project-URL: Issues, https://github.com/Grayjou/UnitField/issues
|
|
12
|
+
Project-URL: Changelog, https://github.com/Grayjou/UnitField/blob/main/CHANGELOG.md
|
|
13
|
+
Keywords: interpolation,transformation,unit-field,computer-vision,image-processing,numpy,opencv
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
27
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
28
|
+
Classifier: Typing :: Typed
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: numpy>=1.20.0
|
|
33
|
+
Requires-Dist: opencv-python>=4.5.0
|
|
34
|
+
Requires-Dist: boundednumbers>=0.1.0
|
|
35
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-benchmark>=4.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
41
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
44
|
+
Provides-Extra: docs
|
|
45
|
+
Requires-Dist: sphinx>=5.0.0; extra == "docs"
|
|
46
|
+
Requires-Dist: sphinx-rtd-theme>=1.2.0; extra == "docs"
|
|
47
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.22.0; extra == "docs"
|
|
48
|
+
Provides-Extra: test
|
|
49
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
50
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
51
|
+
Requires-Dist: pytest-benchmark>=4.0.0; extra == "test"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# UnitField
|
|
55
|
+
|
|
56
|
+
[](https://opensource.org/licenses/MIT)
|
|
57
|
+
[](https://www.python.org/downloads/)
|
|
58
|
+
|
|
59
|
+
**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.
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **N-dimensional unit field transformations** with flexible interpolation
|
|
64
|
+
- **Multiple interpolation methods**: Nearest neighbor (Manhattan/Euclidean), Linear, Cubic, Lanczos4
|
|
65
|
+
- **Dual backends**: NumPy for N-dimensional fields, OpenCV for optimized 2D operations
|
|
66
|
+
- **2D image remapping** with endomorphism composition
|
|
67
|
+
- **Type-safe** with comprehensive type hints
|
|
68
|
+
- **Well-tested** with extensive test coverage
|
|
69
|
+
- **Performance optimized** with LRU caching and vectorized operations
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
### From Source
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/Grayjou/UnitField.git
|
|
77
|
+
cd UnitField
|
|
78
|
+
pip install -e .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Requirements
|
|
82
|
+
|
|
83
|
+
- Python >= 3.8
|
|
84
|
+
- NumPy >= 1.20.0
|
|
85
|
+
- OpenCV (cv2) >= 4.5.0
|
|
86
|
+
- boundednumbers >= 0.1.0
|
|
87
|
+
- typing-extensions >= 4.0.0
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
### Basic Unit Field Usage
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import numpy as np
|
|
95
|
+
from unitfield.core.unitfield import MappedUnitField
|
|
96
|
+
from unitfield.core.enums import InterpMethod
|
|
97
|
+
|
|
98
|
+
# Create a 2D unit field (5x5 grid mapping to 2D vectors)
|
|
99
|
+
x, y = np.meshgrid(np.linspace(0, 1, 5), np.linspace(0, 1, 5))
|
|
100
|
+
data = np.stack([x, y], axis=-1)
|
|
101
|
+
|
|
102
|
+
# Create the field with linear interpolation
|
|
103
|
+
field = MappedUnitField(data=data, interp_method=InterpMethod.LINEAR)
|
|
104
|
+
|
|
105
|
+
# Query single coordinate
|
|
106
|
+
result = field.get_value((0.5, 0.5))
|
|
107
|
+
print(f"Value at (0.5, 0.5): {result}")
|
|
108
|
+
|
|
109
|
+
# Query multiple coordinates
|
|
110
|
+
coords = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]])
|
|
111
|
+
results = field.get_values(coords)
|
|
112
|
+
print(f"Batch results shape: {results.shape}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 2D Image Remapping with Endomorphisms
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import numpy as np
|
|
119
|
+
import cv2
|
|
120
|
+
from unitfield.core.unitfield import Unit2DMappedEndomorphism
|
|
121
|
+
from unitfield.core.enums import InterpMethod
|
|
122
|
+
|
|
123
|
+
# Create an identity endomorphism
|
|
124
|
+
height, width = 100, 100
|
|
125
|
+
xs, ys = np.meshgrid(
|
|
126
|
+
np.linspace(0, 1, width),
|
|
127
|
+
np.linspace(0, 1, height),
|
|
128
|
+
indexing='xy'
|
|
129
|
+
)
|
|
130
|
+
identity_data = np.stack([xs, ys], axis=-1)
|
|
131
|
+
|
|
132
|
+
# Create endomorphism with cubic interpolation
|
|
133
|
+
endo = Unit2DMappedEndomorphism(
|
|
134
|
+
data=identity_data,
|
|
135
|
+
interp_method=InterpMethod.CUBIC
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Remap an image
|
|
139
|
+
image = cv2.imread('input.jpg')
|
|
140
|
+
remapped = endo.remap(image, interpolation=cv2.INTER_LINEAR)
|
|
141
|
+
|
|
142
|
+
# Compose two endomorphisms
|
|
143
|
+
endo2 = Unit2DMappedEndomorphism(data=other_data)
|
|
144
|
+
composed = endo.compose(endo2)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## API Reference
|
|
148
|
+
|
|
149
|
+
### Core Classes
|
|
150
|
+
|
|
151
|
+
#### `MappedUnitField`
|
|
152
|
+
|
|
153
|
+
N-dimensional unit field with interpolation.
|
|
154
|
+
|
|
155
|
+
**Parameters:**
|
|
156
|
+
- `data` (UnitArray): N+1 dimensional array of shape (*spatial_dims, N)
|
|
157
|
+
- `interp_method` (InterpMethod): Interpolation strategy (default: NEAREST_MANHATTAN)
|
|
158
|
+
- `cache_size` (int, optional): LRU cache size for single queries (default: 128)
|
|
159
|
+
|
|
160
|
+
**Methods:**
|
|
161
|
+
- `get_value(coords)`: Get value at single coordinate
|
|
162
|
+
- `get_values(coords_array)`: Get values at multiple coordinates
|
|
163
|
+
- `with_interp_method(method)`: Create copy with different interpolation
|
|
164
|
+
|
|
165
|
+
#### `Unit2DMappedEndomorphism`
|
|
166
|
+
|
|
167
|
+
2D unit field endomorphism with optimized OpenCV backend.
|
|
168
|
+
|
|
169
|
+
**Parameters:**
|
|
170
|
+
- `data` (UnitArray): 3-dimensional array of shape (H, W, 2)
|
|
171
|
+
- `interp_method` (InterpMethod): Interpolation strategy
|
|
172
|
+
- `cache_size` (int, optional): LRU cache size
|
|
173
|
+
|
|
174
|
+
**Methods:**
|
|
175
|
+
- `get_value(coords)`: Get single coordinate value
|
|
176
|
+
- `get_values(coords_array)`: Get multiple coordinate values
|
|
177
|
+
- `rasterize_mapping(width, height)`: Convert to pixel-space mapping
|
|
178
|
+
- `remap(data)`: Remap arbitrary (H, W, J) array
|
|
179
|
+
- `compose(other)`: Compose with another endomorphism
|
|
180
|
+
|
|
181
|
+
### Interpolation Methods
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from unitfield.core.enums import InterpMethod
|
|
185
|
+
|
|
186
|
+
# Available methods:
|
|
187
|
+
InterpMethod.NEAREST_MANHATTAN # Nearest neighbor (Manhattan distance)
|
|
188
|
+
InterpMethod.NEAREST_EUCLIDEAN # Nearest neighbor (Euclidean distance)
|
|
189
|
+
InterpMethod.LINEAR # Linear/bilinear interpolation
|
|
190
|
+
InterpMethod.CUBIC # Cubic/bicubic interpolation
|
|
191
|
+
InterpMethod.LANCZOS4 # Lanczos-4 interpolation
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Utility Functions
|
|
195
|
+
|
|
196
|
+
#### `remap_tensor_cv2`
|
|
197
|
+
|
|
198
|
+
Remap arbitrary tensors using pixel-space mappings.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from unitfield.core.unitfield import remap_tensor_cv2
|
|
202
|
+
|
|
203
|
+
result = remap_tensor_cv2(
|
|
204
|
+
data=tensor,
|
|
205
|
+
mapping=pixel_mapping,
|
|
206
|
+
interpolation=cv2.INTER_LINEAR,
|
|
207
|
+
border_mode=cv2.BORDER_REPLICATE,
|
|
208
|
+
border_value=0.0
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Coordinate System
|
|
213
|
+
|
|
214
|
+
UnitField operates in **unit space** where coordinates are in the range [0, 1]:
|
|
215
|
+
- `0.0` represents the start of each dimension
|
|
216
|
+
- `1.0` represents the end of each dimension
|
|
217
|
+
- Coordinates are automatically scaled to array indices
|
|
218
|
+
|
|
219
|
+
### Coordinate Constraints and Behavior
|
|
220
|
+
|
|
221
|
+
**Important Limitations:**
|
|
222
|
+
|
|
223
|
+
1. **Infinite and NaN coordinates are NOT supported** and will produce undefined behavior. This is by design to keep the functions simple and avoid overhead:
|
|
224
|
+
```python
|
|
225
|
+
# ❌ AVOID - undefined behavior
|
|
226
|
+
field.get_value((np.inf, 0.5))
|
|
227
|
+
field.get_value((np.nan, 0.5))
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. **Out-of-bounds coordinates** (< 0 or > 1) are handled via clipping to [0, 1] range:
|
|
231
|
+
```python
|
|
232
|
+
# ✓ OK - will be clipped to valid range
|
|
233
|
+
field.get_value((-0.5, 1.5)) # Treated as (0.0, 1.0)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
3. **Why this design?**
|
|
237
|
+
- Unit field transformations are intended for normalized coordinate spaces
|
|
238
|
+
- Checking for inf/NaN on every coordinate adds unnecessary overhead
|
|
239
|
+
- Out-of-bounds handling beyond [0, 1] is simple to implement externally if needed
|
|
240
|
+
- Keeps the core functions fast and focused
|
|
241
|
+
|
|
242
|
+
**Best Practices:**
|
|
243
|
+
- Validate coordinates before passing to UnitField methods if they may contain special values
|
|
244
|
+
- For out-of-bounds behavior beyond simple clipping, preprocess coordinates externally
|
|
245
|
+
|
|
246
|
+
## Examples
|
|
247
|
+
|
|
248
|
+
### Creating Different Field Types
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
# 1D field
|
|
252
|
+
data_1d = np.linspace(0, 1, 100).reshape(-1, 1)
|
|
253
|
+
field_1d = MappedUnitField(data=data_1d, interp_method=InterpMethod.LINEAR)
|
|
254
|
+
|
|
255
|
+
# 3D field
|
|
256
|
+
data_3d = np.random.rand(10, 10, 10, 3)
|
|
257
|
+
field_3d = MappedUnitField(data=data_3d, interp_method=InterpMethod.CUBIC)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Switching Interpolation Methods
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# Create field with one method
|
|
264
|
+
field = MappedUnitField(data=data, interp_method=InterpMethod.LINEAR)
|
|
265
|
+
|
|
266
|
+
# Switch to another method
|
|
267
|
+
cubic_field = field.with_interp_method(InterpMethod.CUBIC)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Batch Processing
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
# Process large batches efficiently
|
|
274
|
+
batch_size = 1000
|
|
275
|
+
coords = np.random.rand(batch_size, 2)
|
|
276
|
+
results = field.get_values(coords)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Image Warping Example
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
import numpy as np
|
|
283
|
+
import cv2
|
|
284
|
+
from unitfield.core.unitfield import Unit2DMappedEndomorphism
|
|
285
|
+
|
|
286
|
+
# Create a simple distortion field (barrel distortion)
|
|
287
|
+
h, w = 256, 256
|
|
288
|
+
y, x = np.ogrid[:h, :w]
|
|
289
|
+
center_x, center_y = w / 2, h / 2
|
|
290
|
+
|
|
291
|
+
# Calculate distance from center
|
|
292
|
+
dx = (x - center_x) / center_x
|
|
293
|
+
dy = (y - center_y) / center_y
|
|
294
|
+
r = np.sqrt(dx**2 + dy**2)
|
|
295
|
+
|
|
296
|
+
# Apply barrel distortion
|
|
297
|
+
distortion = 1 + 0.3 * r**2
|
|
298
|
+
new_x = center_x + dx * distortion * center_x
|
|
299
|
+
new_y = center_y + dy * distortion * center_y
|
|
300
|
+
|
|
301
|
+
# Normalize to unit space
|
|
302
|
+
unit_x = new_x / (w - 1)
|
|
303
|
+
unit_y = new_y / (h - 1)
|
|
304
|
+
distortion_field = np.stack([unit_x, unit_y], axis=-1).astype(np.float32)
|
|
305
|
+
|
|
306
|
+
# Create endomorphism and apply
|
|
307
|
+
endo = Unit2DMappedEndomorphism(data=distortion_field)
|
|
308
|
+
image = cv2.imread('input.jpg')
|
|
309
|
+
warped = endo.remap(image)
|
|
310
|
+
cv2.imwrite('output.jpg', warped)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Performance Tips
|
|
314
|
+
|
|
315
|
+
1. **Use caching for repeated queries**: Set appropriate `cache_size` when creating fields
|
|
316
|
+
2. **Batch operations**: Use `get_values()` instead of multiple `get_value()` calls
|
|
317
|
+
3. **Choose the right backend**: Use `Unit2DMappedEndomorphism` for 2D operations (faster via OpenCV)
|
|
318
|
+
4. **Appropriate interpolation**: Nearest neighbor is fastest, cubic/Lanczos are slower but smoother
|
|
319
|
+
|
|
320
|
+
## Development
|
|
321
|
+
|
|
322
|
+
### Running Tests
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
pip install pytest pytest-benchmark
|
|
326
|
+
pytest tests/ -v
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Code Style
|
|
330
|
+
|
|
331
|
+
This project follows modern Python conventions:
|
|
332
|
+
- PEP 8 style guide
|
|
333
|
+
- Type hints throughout
|
|
334
|
+
- Comprehensive docstrings (Google style)
|
|
335
|
+
|
|
336
|
+
## Known Limitations
|
|
337
|
+
|
|
338
|
+
1. **Inf/NaN coordinates**: Not supported in remapping functions (see Coordinate Constraints section)
|
|
339
|
+
2. **Memory usage**: Large N-dimensional fields may consume significant memory
|
|
340
|
+
3. **2D optimization**: Only 2D endomorphisms benefit from OpenCV backend optimization
|
|
341
|
+
|
|
342
|
+
## Contributing
|
|
343
|
+
|
|
344
|
+
Contributions are welcome! Please:
|
|
345
|
+
1. Fork the repository
|
|
346
|
+
2. Create a feature branch
|
|
347
|
+
3. Add tests for new functionality
|
|
348
|
+
4. Ensure all tests pass
|
|
349
|
+
5. Submit a pull request
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
354
|
+
|
|
355
|
+
## Citation
|
|
356
|
+
|
|
357
|
+
If you use UnitField in your research, please cite:
|
|
358
|
+
|
|
359
|
+
```bibtex
|
|
360
|
+
@software{unitfield2025,
|
|
361
|
+
author = {GrayJou},
|
|
362
|
+
title = {UnitField: N-dimensional Unit Field Transformations},
|
|
363
|
+
year = {2025},
|
|
364
|
+
url = {https://github.com/Grayjou/UnitField}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Acknowledgments
|
|
369
|
+
|
|
370
|
+
- Built with NumPy and OpenCV
|
|
371
|
+
- Inspired by coordinate transformation needs in computer vision and graphics
|
|
372
|
+
|
|
373
|
+
## Support
|
|
374
|
+
|
|
375
|
+
For issues, questions, or contributions, please visit:
|
|
376
|
+
- **Issues**: https://github.com/Grayjou/UnitField/issues
|
|
377
|
+
- **Discussions**: https://github.com/Grayjou/UnitField/discussions
|