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.

Files changed (72) hide show
  1. swcgeom/__init__.py +26 -1
  2. swcgeom/analysis/__init__.py +21 -8
  3. swcgeom/analysis/feature_extractor.py +43 -18
  4. swcgeom/analysis/features.py +250 -0
  5. swcgeom/analysis/lmeasure.py +48 -12
  6. swcgeom/analysis/sholl.py +25 -28
  7. swcgeom/analysis/trunk.py +27 -11
  8. swcgeom/analysis/visualization.py +24 -9
  9. swcgeom/analysis/visualization3d.py +100 -0
  10. swcgeom/analysis/volume.py +19 -4
  11. swcgeom/core/__init__.py +31 -12
  12. swcgeom/core/branch.py +19 -3
  13. swcgeom/core/branch_tree.py +18 -4
  14. swcgeom/core/compartment.py +18 -2
  15. swcgeom/core/node.py +32 -3
  16. swcgeom/core/path.py +21 -9
  17. swcgeom/core/population.py +58 -29
  18. swcgeom/core/swc.py +26 -10
  19. swcgeom/core/swc_utils/__init__.py +21 -7
  20. swcgeom/core/swc_utils/assembler.py +15 -0
  21. swcgeom/core/swc_utils/base.py +23 -17
  22. swcgeom/core/swc_utils/checker.py +19 -12
  23. swcgeom/core/swc_utils/io.py +24 -7
  24. swcgeom/core/swc_utils/normalizer.py +20 -4
  25. swcgeom/core/swc_utils/subtree.py +17 -2
  26. swcgeom/core/tree.py +56 -40
  27. swcgeom/core/tree_utils.py +28 -17
  28. swcgeom/core/tree_utils_impl.py +18 -3
  29. swcgeom/images/__init__.py +17 -2
  30. swcgeom/images/augmentation.py +18 -3
  31. swcgeom/images/contrast.py +15 -0
  32. swcgeom/images/folder.py +27 -26
  33. swcgeom/images/io.py +94 -117
  34. swcgeom/transforms/__init__.py +28 -12
  35. swcgeom/transforms/base.py +17 -2
  36. swcgeom/transforms/branch.py +74 -8
  37. swcgeom/transforms/branch_tree.py +82 -0
  38. swcgeom/transforms/geometry.py +22 -7
  39. swcgeom/transforms/image_preprocess.py +15 -0
  40. swcgeom/transforms/image_stack.py +36 -9
  41. swcgeom/transforms/images.py +121 -14
  42. swcgeom/transforms/mst.py +15 -0
  43. swcgeom/transforms/neurolucida_asc.py +20 -7
  44. swcgeom/transforms/path.py +15 -0
  45. swcgeom/transforms/population.py +16 -3
  46. swcgeom/transforms/tree.py +84 -30
  47. swcgeom/transforms/tree_assembler.py +23 -7
  48. swcgeom/utils/__init__.py +27 -12
  49. swcgeom/utils/debug.py +15 -0
  50. swcgeom/utils/download.py +59 -21
  51. swcgeom/utils/dsu.py +15 -0
  52. swcgeom/utils/ellipse.py +18 -4
  53. swcgeom/utils/file.py +15 -0
  54. swcgeom/utils/neuromorpho.py +35 -23
  55. swcgeom/utils/numpy_helper.py +15 -0
  56. swcgeom/utils/plotter_2d.py +27 -6
  57. swcgeom/utils/plotter_3d.py +48 -0
  58. swcgeom/utils/renderer.py +21 -6
  59. swcgeom/utils/sdf.py +19 -7
  60. swcgeom/utils/solid_geometry.py +16 -3
  61. swcgeom/utils/transforms.py +17 -4
  62. swcgeom/utils/volumetric_object.py +23 -10
  63. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/LICENSE +1 -1
  64. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/METADATA +28 -24
  65. swcgeom-0.18.3.dist-info/RECORD +67 -0
  66. {swcgeom-0.16.0.dist-info → swcgeom-0.18.3.dist-info}/WHEEL +1 -1
  67. swcgeom/_version.py +0 -16
  68. swcgeom/analysis/branch_features.py +0 -67
  69. swcgeom/analysis/node_features.py +0 -121
  70. swcgeom/analysis/path_features.py +0 -37
  71. swcgeom-0.16.0.dist-info/RECORD +0 -67
  72. {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 Tuple, cast
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) -> Tuple[Topology, npt.NDArray[np.int32]]:
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 warnings
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) -> List["Tree.Node"]:
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: List["Tree.Node"] = [self]
53
- while not ns[-1].is_bifurcation() and (p := ns[-1].parent()) is not None:
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].is_bifurcation() or ns[-1].is_tip()):
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 Dict[int, int], optional
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) -> List[Node]: ...
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 get_bifurcations(self) -> List[Node]:
203
- """Get all node of bifurcations."""
204
- bifurcations: List[int] = []
206
+ def get_furcations(self) -> list[Node]:
207
+ """Get all node of furcations."""
208
+ furcations: list[int] = []
205
209
 
206
- def collect_bifurcations(n: Tree.Node, children: List[None]) -> None:
210
+ def collect_furcations(n: Tree.Node, children: list[None]) -> None:
207
211
  if len(children) > 1:
208
- bifurcations.append(n.id)
212
+ furcations.append(n.id)
213
+
214
+ self.traverse(leave=collect_furcations)
215
+ return [self.node(i) for i in furcations]
209
216
 
210
- self.traverse(leave=collect_bifurcations)
211
- return [self.node(i) for i in bifurcations]
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) -> List[Node]:
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) -> List[Branch]:
240
+ def get_branches(self) -> list[Branch]:
225
241
  def collect_branches(
226
- node: "Tree.Node", pre: List[Tuple[List[Tree.Branch], List[int]]]
227
- ) -> Tuple[List[Tree.Branch], List[int]]:
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: List[Tree.Branch] = []
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) -> List[Path]:
263
+ def get_paths(self) -> list[Path]:
248
264
  """Get all path from soma to tips."""
249
- path_dic: Dict[int, List[int]] = {}
265
+ path_dic: dict[int, list[int]] = {}
250
266
 
251
- def assign_path(n: Tree.Node, pre_path: List[int] | None) -> List[int]:
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: List[List[List[int]]]
259
- ) -> List[List[int]]:
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, List[K]], K],
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: List[T]) => T, optional
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[List[str]] = None, **kwargs
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
 
@@ -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 typing import Callable, Dict, Iterable, List, Optional, Tuple, TypeVar, overload
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], Tuple[T, bool]]) -> Tree: ...
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, List[K]], Tuple[K, bool]]) -> Tree: ...
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: List[int] = []
80
+ removals: list[int] = []
64
81
 
65
82
  if enter:
66
83
 
67
- def _enter(n: Tree.Node, parent: Tuple[T, bool] | None) -> Tuple[T, bool]:
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: List[K]) -> K:
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
- def to_sub_tree(swc_like: SWCLike, sub: Topology) -> Tuple[Tree, Dict[int, int]]:
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 : Dict[int, int]
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 Dict[int, int], optional
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 Dict[int, int], optional
184
+ out_mapping: List of int or dict[int, int], optional
174
185
  Map new id to old id.
175
186
  """
176
187
 
@@ -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, Dict, List, Optional, Tuple
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 = Dict[int, int] | List[int]
19
- TreeArgs = Tuple[int, Dict[str, npt.NDArray[Any]], str, SWCNames]
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(
@@ -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
@@ -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 List, Literal, Optional
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: List[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
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: List[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
87
+ modes: list[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
73
88
  mode = modes[random.randint(0, 2)]
74
89
 
75
90
  match mode:
@@ -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 warnings
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: List[str]
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) -> List[str]:
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: List[int]
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) -> Tuple[T, 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) -> Tuple[T, str]:
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