swcgeom 0.16.0__py3-none-any.whl → 0.18.3__py3-none-any.whl
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.
Potentially problematic release.
This version of swcgeom might be problematic. Click here for more details.
- swcgeom/__init__.py +26 -1
- swcgeom/analysis/__init__.py +21 -8
- swcgeom/analysis/feature_extractor.py +43 -18
- swcgeom/analysis/features.py +250 -0
- swcgeom/analysis/lmeasure.py +48 -12
- swcgeom/analysis/sholl.py +25 -28
- swcgeom/analysis/trunk.py +27 -11
- swcgeom/analysis/visualization.py +24 -9
- swcgeom/analysis/visualization3d.py +100 -0
- swcgeom/analysis/volume.py +19 -4
- swcgeom/core/__init__.py +31 -12
- swcgeom/core/branch.py +19 -3
- swcgeom/core/branch_tree.py +18 -4
- swcgeom/core/compartment.py +18 -2
- swcgeom/core/node.py +32 -3
- swcgeom/core/path.py +21 -9
- swcgeom/core/population.py +58 -29
- swcgeom/core/swc.py +26 -10
- swcgeom/core/swc_utils/__init__.py +21 -7
- swcgeom/core/swc_utils/assembler.py +15 -0
- swcgeom/core/swc_utils/base.py +23 -17
- swcgeom/core/swc_utils/checker.py +19 -12
- swcgeom/core/swc_utils/io.py +24 -7
- swcgeom/core/swc_utils/normalizer.py +20 -4
- swcgeom/core/swc_utils/subtree.py +17 -2
- swcgeom/core/tree.py +56 -40
- swcgeom/core/tree_utils.py +28 -17
- swcgeom/core/tree_utils_impl.py +18 -3
- swcgeom/images/__init__.py +17 -2
- swcgeom/images/augmentation.py +18 -3
- swcgeom/images/contrast.py +15 -0
- swcgeom/images/folder.py +27 -26
- swcgeom/images/io.py +94 -117
- swcgeom/transforms/__init__.py +28 -12
- swcgeom/transforms/base.py +17 -2
- swcgeom/transforms/branch.py +74 -8
- swcgeom/transforms/branch_tree.py +82 -0
- swcgeom/transforms/geometry.py +22 -7
- swcgeom/transforms/image_preprocess.py +15 -0
- swcgeom/transforms/image_stack.py +36 -9
- swcgeom/transforms/images.py +121 -14
- swcgeom/transforms/mst.py +15 -0
- swcgeom/transforms/neurolucida_asc.py +20 -7
- swcgeom/transforms/path.py +15 -0
- swcgeom/transforms/population.py +16 -3
- swcgeom/transforms/tree.py +84 -30
- swcgeom/transforms/tree_assembler.py +23 -7
- swcgeom/utils/__init__.py +27 -12
- swcgeom/utils/debug.py +15 -0
- swcgeom/utils/download.py +59 -21
- swcgeom/utils/dsu.py +15 -0
- swcgeom/utils/ellipse.py +18 -4
- swcgeom/utils/file.py +15 -0
- swcgeom/utils/neuromorpho.py +35 -23
- swcgeom/utils/numpy_helper.py +15 -0
- swcgeom/utils/plotter_2d.py +27 -6
- swcgeom/utils/plotter_3d.py +48 -0
- swcgeom/utils/renderer.py +21 -6
- swcgeom/utils/sdf.py +19 -7
- swcgeom/utils/solid_geometry.py +16 -3
- swcgeom/utils/transforms.py +17 -4
- swcgeom/utils/volumetric_object.py +23 -10
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/LICENSE +1 -1
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/METADATA +28 -24
- swcgeom-0.18.3.dist-info/RECORD +67 -0
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/WHEEL +1 -1
- swcgeom/_version.py +0 -16
- swcgeom/analysis/branch_features.py +0 -67
- swcgeom/analysis/node_features.py +0 -121
- swcgeom/analysis/path_features.py +0 -37
- swcgeom-0.16.0.dist-info/RECORD +0 -67
- {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Cut subtree.
|
|
2
17
|
|
|
3
18
|
This module provides a series of low-level topological subtree methods,
|
|
@@ -6,7 +21,7 @@ but in more cases, you can use the high-level methods provided in
|
|
|
6
21
|
high-level API.
|
|
7
22
|
"""
|
|
8
23
|
|
|
9
|
-
from typing import
|
|
24
|
+
from typing import cast
|
|
10
25
|
|
|
11
26
|
import numpy as np
|
|
12
27
|
import numpy.typing as npt
|
|
@@ -18,7 +33,7 @@ __all__ = ["REMOVAL", "to_sub_topology", "propagate_removal"]
|
|
|
18
33
|
REMOVAL = -2 # A marker in utils, place in the ids to mark it removal
|
|
19
34
|
|
|
20
35
|
|
|
21
|
-
def to_sub_topology(sub: Topology) ->
|
|
36
|
+
def to_sub_topology(sub: Topology) -> tuple[Topology, npt.NDArray[np.int32]]:
|
|
22
37
|
"""Create sub tree from origin tree.
|
|
23
38
|
|
|
24
39
|
Mark the node to be removed, then use this method to get a child
|
swcgeom/core/tree.py
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Neuron tree."""
|
|
2
17
|
|
|
3
18
|
import itertools
|
|
4
19
|
import os
|
|
5
|
-
import
|
|
6
|
-
from typing import
|
|
7
|
-
Callable,
|
|
8
|
-
Dict,
|
|
9
|
-
Iterable,
|
|
10
|
-
Iterator,
|
|
11
|
-
List,
|
|
12
|
-
Literal,
|
|
13
|
-
Optional,
|
|
14
|
-
Tuple,
|
|
15
|
-
TypeVar,
|
|
16
|
-
Union,
|
|
17
|
-
overload,
|
|
18
|
-
)
|
|
20
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
21
|
+
from typing import Literal, Optional, TypeVar, Union, overload
|
|
19
22
|
|
|
20
23
|
import numpy as np
|
|
21
24
|
import numpy.typing as npt
|
|
22
25
|
import pandas as pd
|
|
26
|
+
from typing_extensions import deprecated
|
|
23
27
|
|
|
24
28
|
from swcgeom.core.branch import Branch
|
|
25
29
|
from swcgeom.core.compartment import Compartment, Compartments
|
|
@@ -44,17 +48,17 @@ class Tree(DictSWC):
|
|
|
44
48
|
def parent(self) -> Union["Tree.Node", None]:
|
|
45
49
|
return Tree.Node(self.attach, self.pid) if self.pid != -1 else None
|
|
46
50
|
|
|
47
|
-
def children(self) ->
|
|
51
|
+
def children(self) -> list["Tree.Node"]:
|
|
48
52
|
children = self.attach.id()[self.attach.pid() == self.id]
|
|
49
53
|
return [Tree.Node(self.attach, idx) for idx in children]
|
|
50
54
|
|
|
51
55
|
def branch(self) -> "Tree.Branch":
|
|
52
|
-
ns:
|
|
53
|
-
while not ns[-1].
|
|
56
|
+
ns: list["Tree.Node"] = [self]
|
|
57
|
+
while not ns[-1].is_furcation() and (p := ns[-1].parent()) is not None:
|
|
54
58
|
ns.append(p)
|
|
55
59
|
|
|
56
60
|
ns.reverse()
|
|
57
|
-
while not (ns[-1].
|
|
61
|
+
while not (ns[-1].is_furcation() or ns[-1].is_tip()):
|
|
58
62
|
ns.append(ns[-1].children()[0])
|
|
59
63
|
|
|
60
64
|
return Tree.Branch(self.attach, [n.id for n in ns])
|
|
@@ -68,7 +72,7 @@ class Tree(DictSWC):
|
|
|
68
72
|
|
|
69
73
|
Parameters
|
|
70
74
|
----------
|
|
71
|
-
out_mapping : List of int or
|
|
75
|
+
out_mapping : List of int or dict[int, int], optional
|
|
72
76
|
Map from new id to old id.
|
|
73
77
|
"""
|
|
74
78
|
|
|
@@ -160,7 +164,7 @@ class Tree(DictSWC):
|
|
|
160
164
|
|
|
161
165
|
# fmt:off
|
|
162
166
|
@overload
|
|
163
|
-
def __getitem__(self, key: slice) ->
|
|
167
|
+
def __getitem__(self, key: slice) -> list[Node]: ...
|
|
164
168
|
@overload
|
|
165
169
|
def __getitem__(self, key: int) -> Node: ...
|
|
166
170
|
@overload
|
|
@@ -199,18 +203,30 @@ class Tree(DictSWC):
|
|
|
199
203
|
raise ValueError(f"no soma found in: {self.source}")
|
|
200
204
|
return n
|
|
201
205
|
|
|
202
|
-
def
|
|
203
|
-
"""Get all node of
|
|
204
|
-
|
|
206
|
+
def get_furcations(self) -> list[Node]:
|
|
207
|
+
"""Get all node of furcations."""
|
|
208
|
+
furcations: list[int] = []
|
|
205
209
|
|
|
206
|
-
def
|
|
210
|
+
def collect_furcations(n: Tree.Node, children: list[None]) -> None:
|
|
207
211
|
if len(children) > 1:
|
|
208
|
-
|
|
212
|
+
furcations.append(n.id)
|
|
213
|
+
|
|
214
|
+
self.traverse(leave=collect_furcations)
|
|
215
|
+
return [self.node(i) for i in furcations]
|
|
209
216
|
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
@deprecated("Use `get_furcations` instead")
|
|
218
|
+
def get_bifurcations(self) -> list[Node]:
|
|
219
|
+
"""Get all node of furcations.
|
|
220
|
+
|
|
221
|
+
Notes
|
|
222
|
+
-----
|
|
223
|
+
Deprecated due to the wrong spelling of furcation. For now, it
|
|
224
|
+
is just an alias of `get_furcations` and raise a warning. It
|
|
225
|
+
will be change to raise an error in the future.
|
|
226
|
+
"""
|
|
227
|
+
return self.get_furcations()
|
|
212
228
|
|
|
213
|
-
def get_tips(self) ->
|
|
229
|
+
def get_tips(self) -> list[Node]:
|
|
214
230
|
"""Get all node of tips."""
|
|
215
231
|
tip_ids = np.setdiff1d(self.id(), self.pid(), assume_unique=True)
|
|
216
232
|
return [self.node(i) for i in tip_ids]
|
|
@@ -221,16 +237,16 @@ class Tree(DictSWC):
|
|
|
221
237
|
def get_segments(self) -> Compartments[Compartment]: # Alias
|
|
222
238
|
return self.get_compartments()
|
|
223
239
|
|
|
224
|
-
def get_branches(self) ->
|
|
240
|
+
def get_branches(self) -> list[Branch]:
|
|
225
241
|
def collect_branches(
|
|
226
|
-
node: "Tree.Node", pre:
|
|
227
|
-
) ->
|
|
242
|
+
node: "Tree.Node", pre: list[tuple[list[Tree.Branch], list[int]]]
|
|
243
|
+
) -> tuple[list[Tree.Branch], list[int]]:
|
|
228
244
|
if len(pre) == 1:
|
|
229
245
|
branches, child = pre[0]
|
|
230
246
|
child.append(node.id)
|
|
231
247
|
return branches, child
|
|
232
248
|
|
|
233
|
-
branches:
|
|
249
|
+
branches: list[Tree.Branch] = []
|
|
234
250
|
|
|
235
251
|
for sub_branches, child in pre:
|
|
236
252
|
child.append(node.id)
|
|
@@ -244,19 +260,19 @@ class Tree(DictSWC):
|
|
|
244
260
|
branches, _ = self.traverse(leave=collect_branches)
|
|
245
261
|
return branches
|
|
246
262
|
|
|
247
|
-
def get_paths(self) ->
|
|
263
|
+
def get_paths(self) -> list[Path]:
|
|
248
264
|
"""Get all path from soma to tips."""
|
|
249
|
-
path_dic:
|
|
265
|
+
path_dic: dict[int, list[int]] = {}
|
|
250
266
|
|
|
251
|
-
def assign_path(n: Tree.Node, pre_path:
|
|
267
|
+
def assign_path(n: Tree.Node, pre_path: list[int] | None) -> list[int]:
|
|
252
268
|
path = [] if pre_path is None else pre_path.copy()
|
|
253
269
|
path.append(n.id)
|
|
254
270
|
path_dic[n.id] = path
|
|
255
271
|
return path
|
|
256
272
|
|
|
257
273
|
def collect_path(
|
|
258
|
-
n: Tree.Node, children:
|
|
259
|
-
) ->
|
|
274
|
+
n: Tree.Node, children: list[list[list[int]]]
|
|
275
|
+
) -> list[list[int]]:
|
|
260
276
|
if len(children) == 0:
|
|
261
277
|
return [path_dic[n.id]]
|
|
262
278
|
|
|
@@ -283,7 +299,7 @@ class Tree(DictSWC):
|
|
|
283
299
|
@overload
|
|
284
300
|
def traverse(self, *,
|
|
285
301
|
enter: Optional[Callable[[Node, T | None], T]] = ...,
|
|
286
|
-
leave: Callable[[Node,
|
|
302
|
+
leave: Callable[[Node, list[K]], K],
|
|
287
303
|
root: int | np.integer = ..., mode: Literal["dfs"] = ...) -> K: ...
|
|
288
304
|
# fmt: on
|
|
289
305
|
|
|
@@ -293,7 +309,7 @@ class Tree(DictSWC):
|
|
|
293
309
|
Parameters
|
|
294
310
|
----------
|
|
295
311
|
enter : (n: Node, parent: T | None) => T, optional
|
|
296
|
-
leave : (n: Node, children:
|
|
312
|
+
leave : (n: Node, children: list[T]) => T, optional
|
|
297
313
|
|
|
298
314
|
See Also
|
|
299
315
|
--------
|
|
@@ -355,7 +371,7 @@ class Tree(DictSWC):
|
|
|
355
371
|
|
|
356
372
|
@classmethod
|
|
357
373
|
def from_eswc(
|
|
358
|
-
cls, swc_file: str, extra_cols: Optional[
|
|
374
|
+
cls, swc_file: str, extra_cols: Optional[list[str]] = None, **kwargs
|
|
359
375
|
) -> "Tree":
|
|
360
376
|
"""Read neuron tree from eswc file.
|
|
361
377
|
|
swcgeom/core/tree_utils.py
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""SWC util wrapper for tree."""
|
|
2
17
|
|
|
3
18
|
import warnings
|
|
4
|
-
from
|
|
19
|
+
from collections.abc import Callable, Iterable
|
|
20
|
+
from typing import Optional, TypeVar, overload
|
|
5
21
|
|
|
6
22
|
import numpy as np
|
|
23
|
+
from typing_extensions import deprecated
|
|
7
24
|
|
|
8
25
|
from swcgeom.core.swc import SWCLike
|
|
9
26
|
from swcgeom.core.swc_utils import (
|
|
@@ -50,9 +67,9 @@ def sort_tree(tree: Tree) -> Tree:
|
|
|
50
67
|
|
|
51
68
|
# fmt:off
|
|
52
69
|
@overload
|
|
53
|
-
def cut_tree(tree: Tree, *, enter: Callable[[Tree.Node, T | None],
|
|
70
|
+
def cut_tree(tree: Tree, *, enter: Callable[[Tree.Node, T | None], tuple[T, bool]]) -> Tree: ...
|
|
54
71
|
@overload
|
|
55
|
-
def cut_tree(tree: Tree, *, leave: Callable[[Tree.Node,
|
|
72
|
+
def cut_tree(tree: Tree, *, leave: Callable[[Tree.Node, list[K]], tuple[K, bool]]) -> Tree: ...
|
|
56
73
|
# fmt:on
|
|
57
74
|
def cut_tree(tree: Tree, *, enter=None, leave=None):
|
|
58
75
|
"""Traverse and cut the tree.
|
|
@@ -60,11 +77,11 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
|
|
|
60
77
|
Returning a `True` can delete the current node and its children.
|
|
61
78
|
"""
|
|
62
79
|
|
|
63
|
-
removals:
|
|
80
|
+
removals: list[int] = []
|
|
64
81
|
|
|
65
82
|
if enter:
|
|
66
83
|
|
|
67
|
-
def _enter(n: Tree.Node, parent:
|
|
84
|
+
def _enter(n: Tree.Node, parent: tuple[T, bool] | None) -> tuple[T, bool]:
|
|
68
85
|
if parent is not None and parent[1]:
|
|
69
86
|
removals.append(n.id)
|
|
70
87
|
return parent
|
|
@@ -79,7 +96,7 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
|
|
|
79
96
|
|
|
80
97
|
elif leave:
|
|
81
98
|
|
|
82
|
-
def _leave(n: Tree.Node, children:
|
|
99
|
+
def _leave(n: Tree.Node, children: list[K]) -> K:
|
|
83
100
|
res, removal = leave(n, children)
|
|
84
101
|
if removal:
|
|
85
102
|
removals.append(n.id)
|
|
@@ -94,7 +111,8 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
|
|
|
94
111
|
return to_subtree(tree, removals)
|
|
95
112
|
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
@deprecated("Use `to_subtree` instead")
|
|
115
|
+
def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]:
|
|
98
116
|
"""Create subtree from origin tree.
|
|
99
117
|
|
|
100
118
|
You can directly mark the node for removal, and we will remove it,
|
|
@@ -107,16 +125,9 @@ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> Tuple[Tree, Dict[int, int]]
|
|
|
107
125
|
Returns
|
|
108
126
|
-------
|
|
109
127
|
tree : Tree
|
|
110
|
-
id_map :
|
|
128
|
+
id_map : dict[int, int]
|
|
111
129
|
"""
|
|
112
130
|
|
|
113
|
-
warnings.warn(
|
|
114
|
-
"`to_sub_tree` will be removed in v0.6.0, it is replaced by "
|
|
115
|
-
"`to_subtree` beacuse it is easy to use, and this will be "
|
|
116
|
-
"removed in next version",
|
|
117
|
-
DeprecationWarning,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
131
|
sub = propagate_removal(sub)
|
|
121
132
|
(new_id, new_pid), id_map_arr = to_sub_topology(sub)
|
|
122
133
|
|
|
@@ -145,7 +156,7 @@ def to_subtree(
|
|
|
145
156
|
swc_like : SWCLike
|
|
146
157
|
removals : List of int
|
|
147
158
|
A list of id of nodes to be removed.
|
|
148
|
-
out_mapping: List of int or
|
|
159
|
+
out_mapping: List of int or dict[int, int], optional
|
|
149
160
|
Map new id to old id.
|
|
150
161
|
"""
|
|
151
162
|
|
|
@@ -170,7 +181,7 @@ def get_subtree(
|
|
|
170
181
|
swc_like : SWCLike
|
|
171
182
|
n : int
|
|
172
183
|
Id of the root of the subtree.
|
|
173
|
-
out_mapping: List of int or
|
|
184
|
+
out_mapping: List of int or dict[int, int], optional
|
|
174
185
|
Map new id to old id.
|
|
175
186
|
"""
|
|
176
187
|
|
swcgeom/core/tree_utils_impl.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""SWC util wrapper for tree, split to avoid circle imports.
|
|
2
17
|
|
|
3
18
|
Notes
|
|
@@ -5,7 +20,7 @@ Notes
|
|
|
5
20
|
Do not import `Tree` and keep this file minimized.
|
|
6
21
|
"""
|
|
7
22
|
|
|
8
|
-
from typing import Any,
|
|
23
|
+
from typing import Any, Optional
|
|
9
24
|
|
|
10
25
|
import numpy as np
|
|
11
26
|
import numpy.typing as npt
|
|
@@ -15,8 +30,8 @@ from swcgeom.core.swc_utils import Topology, to_sub_topology, traverse
|
|
|
15
30
|
|
|
16
31
|
__all__ = ["get_subtree_impl", "to_subtree_impl"]
|
|
17
32
|
|
|
18
|
-
Mapping =
|
|
19
|
-
TreeArgs =
|
|
33
|
+
Mapping = dict[int, int] | list[int]
|
|
34
|
+
TreeArgs = tuple[int, dict[str, npt.NDArray[Any]], str, SWCNames]
|
|
20
35
|
|
|
21
36
|
|
|
22
37
|
def get_subtree_impl(
|
swcgeom/images/__init__.py
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Image Stack Related."""
|
|
2
17
|
|
|
3
|
-
from swcgeom.images.folder import *
|
|
4
|
-
from swcgeom.images.io import *
|
|
18
|
+
from swcgeom.images.folder import * # noqa: F403
|
|
19
|
+
from swcgeom.images.io import * # noqa: F403
|
swcgeom/images/augmentation.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Play augment in image stack.
|
|
2
17
|
|
|
3
18
|
Notes
|
|
@@ -6,7 +21,7 @@ This is expremental code, and the API is subject to change.
|
|
|
6
21
|
"""
|
|
7
22
|
|
|
8
23
|
import random
|
|
9
|
-
from typing import
|
|
24
|
+
from typing import Literal, Optional
|
|
10
25
|
|
|
11
26
|
import numpy as np
|
|
12
27
|
import numpy.typing as npt
|
|
@@ -54,7 +69,7 @@ class Augmentation:
|
|
|
54
69
|
|
|
55
70
|
def swapaxes(self, x, mode: Optional[Literal["xy", "xz", "yz"]] = None) -> NDArrf32:
|
|
56
71
|
if mode is None:
|
|
57
|
-
modes:
|
|
72
|
+
modes: list[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
|
|
58
73
|
mode = modes[self.rand.randint(0, 2)]
|
|
59
74
|
|
|
60
75
|
match mode:
|
|
@@ -69,7 +84,7 @@ class Augmentation:
|
|
|
69
84
|
|
|
70
85
|
def flip(self, x, mode: Optional[Literal["xy", "xz", "yz"]] = None) -> NDArrf32:
|
|
71
86
|
if mode is None:
|
|
72
|
-
modes:
|
|
87
|
+
modes: list[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
|
|
73
88
|
mode = modes[random.randint(0, 2)]
|
|
74
89
|
|
|
75
90
|
match mode:
|
swcgeom/images/contrast.py
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""The contrast of an image.
|
|
2
17
|
|
|
3
18
|
Notes
|
swcgeom/images/folder.py
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
+
# Copyright 2022-2025 Zexin Yuan
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
"""Image stack folder."""
|
|
2
17
|
|
|
3
18
|
import math
|
|
4
19
|
import os
|
|
5
20
|
import re
|
|
6
|
-
import
|
|
21
|
+
from collections.abc import Callable, Iterable
|
|
7
22
|
from dataclasses import dataclass
|
|
8
|
-
from typing import
|
|
9
|
-
Callable,
|
|
10
|
-
Generic,
|
|
11
|
-
Iterable,
|
|
12
|
-
List,
|
|
13
|
-
Literal,
|
|
14
|
-
Optional,
|
|
15
|
-
Tuple,
|
|
16
|
-
TypeVar,
|
|
17
|
-
overload,
|
|
18
|
-
)
|
|
23
|
+
from typing import Generic, Literal, Optional, TypeVar, overload
|
|
19
24
|
|
|
20
25
|
import numpy as np
|
|
21
26
|
import numpy.typing as npt
|
|
22
27
|
from tqdm import tqdm
|
|
23
|
-
from typing_extensions import Self
|
|
28
|
+
from typing_extensions import Self, deprecated
|
|
24
29
|
|
|
25
30
|
from swcgeom.images.io import ScalarType, read_imgs
|
|
26
31
|
from swcgeom.transforms import Identity, Transform
|
|
@@ -33,7 +38,7 @@ T = TypeVar("T")
|
|
|
33
38
|
class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
34
39
|
"""Image stack folder base."""
|
|
35
40
|
|
|
36
|
-
files:
|
|
41
|
+
files: list[str]
|
|
37
42
|
transform: Transform[npt.NDArray[ScalarType], T]
|
|
38
43
|
|
|
39
44
|
# fmt: off
|
|
@@ -61,7 +66,10 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
|
61
66
|
return read_imgs(fname, dtype=self.dtype).get_full() # type: ignore
|
|
62
67
|
|
|
63
68
|
@staticmethod
|
|
64
|
-
def scan(root: str, *, pattern: Optional[str] = None) ->
|
|
69
|
+
def scan(root: str, *, pattern: Optional[str] = None) -> list[str]:
|
|
70
|
+
if not os.path.isdir(root):
|
|
71
|
+
raise NotADirectoryError(f"not a directory: {root}")
|
|
72
|
+
|
|
65
73
|
is_valid = re.compile(pattern).match if pattern is not None else truthly
|
|
66
74
|
|
|
67
75
|
fs = []
|
|
@@ -71,6 +79,7 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
|
71
79
|
return fs
|
|
72
80
|
|
|
73
81
|
@staticmethod
|
|
82
|
+
@deprecated("Use `~swcgeom.images.io.read_imgs(fname).get_full()` instead")
|
|
74
83
|
def read_imgs(fname: str) -> npt.NDArray[np.float32]:
|
|
75
84
|
"""Read images.
|
|
76
85
|
|
|
@@ -78,14 +87,6 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
|
78
87
|
Use :meth:`~swcgeom.images.io.read_imgs(fname).get_full()` instead.
|
|
79
88
|
"""
|
|
80
89
|
|
|
81
|
-
warnings.warn(
|
|
82
|
-
"`ImageStackFolderBase.read_imgs` serves as a "
|
|
83
|
-
"straightforward wrapper for `~swcgeom.images.io.read_imgs(fname).get_full()`. "
|
|
84
|
-
"However, as it is not utilized within our internal "
|
|
85
|
-
"processes, it is scheduled for removal in the "
|
|
86
|
-
"forthcoming version.",
|
|
87
|
-
DeprecationWarning,
|
|
88
|
-
)
|
|
89
90
|
return read_imgs(fname).get_full()
|
|
90
91
|
|
|
91
92
|
|
|
@@ -168,13 +169,13 @@ class ImageStackFolder(ImageStackFolderBase[ScalarType, T]):
|
|
|
168
169
|
class LabeledImageStackFolder(ImageStackFolderBase[ScalarType, T]):
|
|
169
170
|
"""Image stack folder with label."""
|
|
170
171
|
|
|
171
|
-
labels:
|
|
172
|
+
labels: list[int]
|
|
172
173
|
|
|
173
174
|
def __init__(self, files: Iterable[str], labels: Iterable[int], **kwargs):
|
|
174
175
|
super().__init__(files, **kwargs)
|
|
175
176
|
self.labels = list(labels)
|
|
176
177
|
|
|
177
|
-
def __getitem__(self, idx: int) ->
|
|
178
|
+
def __getitem__(self, idx: int) -> tuple[T, int]:
|
|
178
179
|
return self._get(self.files[idx]), self.labels[idx]
|
|
179
180
|
|
|
180
181
|
@classmethod
|
|
@@ -205,7 +206,7 @@ class PathImageStackFolder(ImageStackFolderBase[ScalarType, T]):
|
|
|
205
206
|
super().__init__(files, **kwargs)
|
|
206
207
|
self.root = root
|
|
207
208
|
|
|
208
|
-
def __getitem__(self, idx: int) ->
|
|
209
|
+
def __getitem__(self, idx: int) -> tuple[T, str]:
|
|
209
210
|
relpath = os.path.relpath(self.files[idx], self.root)
|
|
210
211
|
return self._get(self.files[idx]), relpath
|
|
211
212
|
|