transformnd 0.7.0__tar.gz → 0.7.2__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.
- {transformnd-0.7.0 → transformnd-0.7.2}/PKG-INFO +2 -2
- {transformnd-0.7.0 → transformnd-0.7.2}/README.md +1 -1
- {transformnd-0.7.0 → transformnd-0.7.2}/pyproject.toml +5 -3
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/__init__.py +6 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/base.py +8 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/__init__.py +1 -3
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/project_axis.py +19 -85
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/__init__.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/base.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/bounding_box.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/pandas.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/polars.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/adapters/shapely.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/constants.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/extents/__init__.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/extents/base.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/extents/bounding_box.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/graph.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/py.typed +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/affine.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/bijection.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/by_dimension.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/map_axis.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/moving_least_squares.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/reflection.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/simple.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/thinplate.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/transforms/vector_field.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/types.py +0 -0
- {transformnd-0.7.0 → transformnd-0.7.2}/src/transformnd/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: transformnd
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: ND coordinate transformations
|
|
5
5
|
Author: Chris Barnes
|
|
6
6
|
Author-email: Chris Barnes <chris.barnes@gerbi-gmb.de>
|
|
@@ -84,7 +84,7 @@ and implements these adapters for a few common types.
|
|
|
84
84
|
|
|
85
85
|
See the [tutorial here](https://github.com/clbarnes/transformnd/blob/main/examples/tutorial.py).
|
|
86
86
|
It is a [marimo](https://marimo.io) notebook.
|
|
87
|
-
Open it with `uv run --group
|
|
87
|
+
Open it with `uv run --group examples marimo edit examples/tutorial.py`.
|
|
88
88
|
|
|
89
89
|
## Implemented transforms
|
|
90
90
|
|
|
@@ -29,7 +29,7 @@ and implements these adapters for a few common types.
|
|
|
29
29
|
|
|
30
30
|
See the [tutorial here](https://github.com/clbarnes/transformnd/blob/main/examples/tutorial.py).
|
|
31
31
|
It is a [marimo](https://marimo.io) notebook.
|
|
32
|
-
Open it with `uv run --group
|
|
32
|
+
Open it with `uv run --group examples marimo edit examples/tutorial.py`.
|
|
33
33
|
|
|
34
34
|
## Implemented transforms
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "transformnd"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.2"
|
|
4
4
|
description = "ND coordinate transformations"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [{ name = "Chris Barnes", email = "chris.barnes@gerbi-gmb.de" }]
|
|
@@ -62,7 +62,7 @@ dev = [
|
|
|
62
62
|
{include-group = "test"},
|
|
63
63
|
{include-group = "lint"},
|
|
64
64
|
{include-group = "doc"},
|
|
65
|
-
{include-group = "
|
|
65
|
+
{include-group = "examples"},
|
|
66
66
|
{include-group = "bench"},
|
|
67
67
|
]
|
|
68
68
|
test = [
|
|
@@ -80,10 +80,12 @@ lint = [
|
|
|
80
80
|
doc = [
|
|
81
81
|
"pdoc>=16.0.0",
|
|
82
82
|
]
|
|
83
|
-
|
|
83
|
+
examples = [
|
|
84
84
|
"marimo>=0.9",
|
|
85
85
|
"matplotlib>=3.10.8",
|
|
86
86
|
"pandas>=3.0.2",
|
|
87
|
+
"pooch>=1.9.0",
|
|
88
|
+
"scikit-image>=0.26.0",
|
|
87
89
|
]
|
|
88
90
|
bench = [
|
|
89
91
|
"pytest-benchmark>=5.2.3",
|
|
@@ -304,6 +304,14 @@ class TransformSequence(Transform[ArrayT], Sequence[Transform[ArrayT]]):
|
|
|
304
304
|
if not ts:
|
|
305
305
|
raise ValueError("Empty transform sequence")
|
|
306
306
|
|
|
307
|
+
for idx, (t1, t2) in enumerate(pairwise(ts)):
|
|
308
|
+
if t1.ndims.target != t2.ndims.source:
|
|
309
|
+
raise ValueError(
|
|
310
|
+
"Incompatible dimensionality: "
|
|
311
|
+
f"transform {idx}'s target is {t1.ndims.target}D "
|
|
312
|
+
f"and the next source is {t2.ndims.source}D"
|
|
313
|
+
)
|
|
314
|
+
|
|
307
315
|
spaces = Spaces(ts[0].spaces.source, ts[-1].spaces.target)
|
|
308
316
|
ndims = NDims(ts[0].ndims.source, ts[-1].ndims.target)
|
|
309
317
|
|
|
@@ -5,7 +5,7 @@ from .reflection import Reflect
|
|
|
5
5
|
from .simple import Identity, Scale, Translate
|
|
6
6
|
from .map_axis import MapAxis
|
|
7
7
|
from .bijection import Bijection
|
|
8
|
-
from .project_axis import ProjectAxis
|
|
8
|
+
from .project_axis import ProjectAxis
|
|
9
9
|
from .by_dimension import ByDimension, SubTransform
|
|
10
10
|
from .vector_field import Coordinates, Displacements
|
|
11
11
|
from .moving_least_squares import MovingLeastSquares
|
|
@@ -15,8 +15,6 @@ __all__ = [
|
|
|
15
15
|
"Affine",
|
|
16
16
|
"Identity",
|
|
17
17
|
"ProjectAxis",
|
|
18
|
-
"Insert",
|
|
19
|
-
"Remove",
|
|
20
18
|
"Reflect",
|
|
21
19
|
"Scale",
|
|
22
20
|
"Translate",
|
|
@@ -1,69 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from
|
|
3
|
-
from copy import copy
|
|
4
|
-
from typing import Self, Sequence
|
|
2
|
+
from typing import Self
|
|
5
3
|
|
|
6
4
|
import numpy as np
|
|
7
5
|
from array_api_compat import array_namespace
|
|
8
6
|
from transformnd.transforms import Affine
|
|
9
7
|
from transformnd.types import NDims, Spaces
|
|
10
|
-
from dataclasses import dataclass
|
|
11
8
|
from ..base import Transform
|
|
12
9
|
from ..types import ArrayT
|
|
13
10
|
|
|
14
11
|
|
|
15
|
-
@dataclass(frozen=True, eq=True)
|
|
16
|
-
class BaseOperation(ABC):
|
|
17
|
-
idx: int
|
|
18
|
-
"""Which axis to apply the operation to."""
|
|
19
|
-
|
|
20
|
-
def __post_init__(self):
|
|
21
|
-
if self.idx < 0:
|
|
22
|
-
raise ValueError("insert/remove idx must be positive")
|
|
23
|
-
|
|
24
|
-
@abstractmethod
|
|
25
|
-
def check(self, ndim: int) -> int: ...
|
|
26
|
-
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def invert(self) -> BaseOperation: ...
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclass(frozen=True, eq=True)
|
|
32
|
-
class Insert(BaseOperation):
|
|
33
|
-
"""Component of the `ProjectAxis` transform which inserts a new axis."""
|
|
34
|
-
|
|
35
|
-
def check(self, ndim: int) -> int:
|
|
36
|
-
if self.idx > ndim or self.idx <= -ndim:
|
|
37
|
-
raise ValueError(
|
|
38
|
-
f"Index {self.idx} is out of range for dimensionality {ndim}"
|
|
39
|
-
)
|
|
40
|
-
return ndim + 1
|
|
41
|
-
|
|
42
|
-
def invert(self) -> Remove:
|
|
43
|
-
return Remove(self.idx)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@dataclass(frozen=True, eq=True)
|
|
47
|
-
class Remove(BaseOperation):
|
|
48
|
-
"""Component of the `ProjectAxis` transform which removes an existing axis."""
|
|
49
|
-
|
|
50
|
-
def check(self, ndim: int) -> int:
|
|
51
|
-
if self.idx >= ndim or self.idx <= -ndim:
|
|
52
|
-
raise ValueError(
|
|
53
|
-
f"Index {self.idx} is out of range for dimensionality {ndim}"
|
|
54
|
-
)
|
|
55
|
-
return ndim - 1
|
|
56
|
-
|
|
57
|
-
def invert(self) -> Insert:
|
|
58
|
-
if self.idx == -1:
|
|
59
|
-
raise ValueError("Removal of the -1th axis is not invertible")
|
|
60
|
-
return Insert(self.idx)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
Operation = Insert | Remove
|
|
64
|
-
"""Insert or remove an axis."""
|
|
65
|
-
|
|
66
|
-
|
|
67
12
|
class ProjectAxis(Transform):
|
|
68
13
|
"""Transform for adding and removing axes.
|
|
69
14
|
|
|
@@ -72,7 +17,8 @@ class ProjectAxis(Transform):
|
|
|
72
17
|
|
|
73
18
|
def __init__(
|
|
74
19
|
self,
|
|
75
|
-
|
|
20
|
+
dropped: set[int] | None = None,
|
|
21
|
+
created: set[int] | None = None,
|
|
76
22
|
source_ndim: int | None = None,
|
|
77
23
|
target_ndim: int | None = None,
|
|
78
24
|
*,
|
|
@@ -84,8 +30,10 @@ class ProjectAxis(Transform):
|
|
|
84
30
|
|
|
85
31
|
Parameters
|
|
86
32
|
----------
|
|
87
|
-
|
|
88
|
-
|
|
33
|
+
dropped
|
|
34
|
+
Set of INPUT dimension indices to drop, if any.
|
|
35
|
+
created
|
|
36
|
+
Set of OUTPUT dimension indices which are new, if any.
|
|
89
37
|
source_ndim
|
|
90
38
|
If omitted, can be inferred from `target_ndim`.
|
|
91
39
|
target_ndim
|
|
@@ -99,22 +47,18 @@ class ProjectAxis(Transform):
|
|
|
99
47
|
Operations are inconsistent with given dimensionality,
|
|
100
48
|
or insufficient dimensionality information was given.
|
|
101
49
|
"""
|
|
102
|
-
self.
|
|
103
|
-
self.
|
|
50
|
+
self.dropped = dropped or set()
|
|
51
|
+
self.created = created or set()
|
|
104
52
|
|
|
105
53
|
if source_ndim is not None:
|
|
106
|
-
nd = source_ndim
|
|
107
|
-
for op in operations:
|
|
108
|
-
nd = op.check(nd)
|
|
54
|
+
nd = source_ndim - len(self.dropped) + len(self.created)
|
|
109
55
|
if target_ndim is None:
|
|
110
56
|
target_ndim = nd
|
|
111
57
|
elif target_ndim != nd:
|
|
112
58
|
raise ValueError("Operations do not match expected target ndim")
|
|
113
59
|
|
|
114
60
|
elif target_ndim is not None:
|
|
115
|
-
nd = target_ndim
|
|
116
|
-
for op in reversed(operations):
|
|
117
|
-
nd = op.invert().check(nd)
|
|
61
|
+
nd = target_ndim - len(self.created) + len(self.dropped)
|
|
118
62
|
if source_ndim is None:
|
|
119
63
|
source_ndim = nd
|
|
120
64
|
elif source_ndim != nd:
|
|
@@ -124,20 +68,17 @@ class ProjectAxis(Transform):
|
|
|
124
68
|
raise ValueError("At least one of source_ndim or target_ndim must be given")
|
|
125
69
|
|
|
126
70
|
idxs: list[int | None] = list(range(source_ndim))
|
|
127
|
-
for
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
elif isinstance(op, Remove):
|
|
132
|
-
idxs.pop(op.idx)
|
|
133
|
-
self.operations.append(op)
|
|
71
|
+
for drop in sorted(self.dropped, reverse=True):
|
|
72
|
+
idxs.pop(drop)
|
|
73
|
+
for create in sorted(self.created):
|
|
74
|
+
idxs.insert(create, None)
|
|
134
75
|
self._idxs = idxs
|
|
135
76
|
|
|
136
77
|
super().__init__(NDims(source_ndim, target_ndim), spaces=spaces)
|
|
137
78
|
|
|
138
79
|
def apply(self, coords: ArrayT) -> ArrayT:
|
|
139
80
|
coords = self._validate_coords(coords)
|
|
140
|
-
if self.
|
|
81
|
+
if self.created:
|
|
141
82
|
xp = array_namespace(coords)
|
|
142
83
|
out = xp.zeros_like(coords, shape=(xp.shape(coords)[0], self.ndims.target))
|
|
143
84
|
for idx, orig_idx in enumerate(self._idxs):
|
|
@@ -149,15 +90,7 @@ class ProjectAxis(Transform):
|
|
|
149
90
|
return out
|
|
150
91
|
|
|
151
92
|
def is_identity(self) -> bool:
|
|
152
|
-
|
|
153
|
-
dims = copy(orig)
|
|
154
|
-
for op in self.operations:
|
|
155
|
-
if isinstance(op, Insert):
|
|
156
|
-
dims.insert(op.idx, None)
|
|
157
|
-
elif isinstance(op, Remove):
|
|
158
|
-
dims.pop(op.idx)
|
|
159
|
-
|
|
160
|
-
return dims == orig
|
|
93
|
+
return not self.created and not self.dropped
|
|
161
94
|
|
|
162
95
|
def to_affine(self) -> Affine | None:
|
|
163
96
|
m = np.eye(self.ndims.source)
|
|
@@ -166,7 +99,8 @@ class ProjectAxis(Transform):
|
|
|
166
99
|
|
|
167
100
|
def invert(self) -> Self | None:
|
|
168
101
|
return type(self)(
|
|
169
|
-
|
|
102
|
+
self.created,
|
|
103
|
+
self.dropped,
|
|
170
104
|
source_ndim=self.ndims.target,
|
|
171
105
|
target_ndim=self.ndims.source,
|
|
172
106
|
spaces=self.spaces.invert(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|