swcgeom 0.15.0__py3-none-any.whl → 0.17.0__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/_version.py +2 -2
- swcgeom/analysis/__init__.py +1 -3
- swcgeom/analysis/feature_extractor.py +3 -3
- swcgeom/analysis/{node_features.py → features.py} +105 -3
- swcgeom/analysis/lmeasure.py +821 -0
- swcgeom/analysis/sholl.py +31 -2
- swcgeom/core/__init__.py +4 -0
- swcgeom/core/branch.py +9 -4
- swcgeom/core/{segment.py → compartment.py} +14 -9
- swcgeom/core/node.py +0 -8
- swcgeom/core/path.py +21 -6
- swcgeom/core/population.py +47 -7
- swcgeom/core/swc_utils/assembler.py +12 -1
- swcgeom/core/swc_utils/base.py +12 -5
- swcgeom/core/swc_utils/checker.py +12 -2
- swcgeom/core/tree.py +34 -37
- swcgeom/core/tree_utils.py +4 -0
- swcgeom/images/augmentation.py +6 -1
- swcgeom/images/contrast.py +107 -0
- swcgeom/images/folder.py +71 -14
- swcgeom/images/io.py +74 -88
- swcgeom/transforms/__init__.py +2 -0
- swcgeom/transforms/image_preprocess.py +100 -0
- swcgeom/transforms/image_stack.py +1 -4
- swcgeom/transforms/images.py +176 -5
- swcgeom/transforms/mst.py +5 -5
- swcgeom/transforms/neurolucida_asc.py +495 -0
- swcgeom/transforms/tree.py +5 -1
- swcgeom/utils/__init__.py +1 -0
- swcgeom/utils/neuromorpho.py +425 -300
- swcgeom/utils/numpy_helper.py +14 -4
- swcgeom/utils/plotter_2d.py +130 -0
- swcgeom/utils/renderer.py +28 -139
- swcgeom/utils/sdf.py +5 -1
- {swcgeom-0.15.0.dist-info → swcgeom-0.17.0.dist-info}/METADATA +3 -3
- swcgeom-0.17.0.dist-info/RECORD +65 -0
- {swcgeom-0.15.0.dist-info → swcgeom-0.17.0.dist-info}/WHEEL +1 -1
- swcgeom/analysis/branch_features.py +0 -67
- swcgeom/analysis/path_features.py +0 -37
- swcgeom-0.15.0.dist-info/RECORD +0 -62
- {swcgeom-0.15.0.dist-info → swcgeom-0.17.0.dist-info}/LICENSE +0 -0
- {swcgeom-0.15.0.dist-info → swcgeom-0.17.0.dist-info}/top_level.txt +0 -0
swcgeom/_version.py
CHANGED
swcgeom/analysis/__init__.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"""Analysis for neuron trees."""
|
|
2
2
|
|
|
3
|
-
from swcgeom.analysis.branch_features import *
|
|
4
3
|
from swcgeom.analysis.feature_extractor import *
|
|
5
|
-
from swcgeom.analysis.
|
|
6
|
-
from swcgeom.analysis.path_features import *
|
|
4
|
+
from swcgeom.analysis.features import *
|
|
7
5
|
from swcgeom.analysis.sholl import *
|
|
8
6
|
from swcgeom.analysis.trunk import *
|
|
9
7
|
from swcgeom.analysis.visualization import *
|
|
@@ -17,13 +17,13 @@ import numpy.typing as npt
|
|
|
17
17
|
import seaborn as sns
|
|
18
18
|
from matplotlib.axes import Axes
|
|
19
19
|
|
|
20
|
-
from swcgeom.analysis.
|
|
21
|
-
from swcgeom.analysis.node_features import (
|
|
20
|
+
from swcgeom.analysis.features import (
|
|
22
21
|
BifurcationFeatures,
|
|
22
|
+
BranchFeatures,
|
|
23
23
|
NodeFeatures,
|
|
24
|
+
PathFeatures,
|
|
24
25
|
TipFeatures,
|
|
25
26
|
)
|
|
26
|
-
from swcgeom.analysis.path_features import PathFeatures
|
|
27
27
|
from swcgeom.analysis.sholl import Sholl
|
|
28
28
|
from swcgeom.analysis.volume import get_volume
|
|
29
29
|
from swcgeom.core import Population, Populations, Tree
|
|
@@ -1,15 +1,26 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Feature anlysis of tree."""
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from functools import cached_property
|
|
5
|
+
from typing import List, TypeVar
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
import numpy.typing as npt
|
|
8
9
|
from typing_extensions import Self
|
|
9
10
|
|
|
10
|
-
from swcgeom.core import BranchTree, Tree
|
|
11
|
+
from swcgeom.core import Branch, BranchTree, Tree
|
|
11
12
|
|
|
12
|
-
__all__ = [
|
|
13
|
+
__all__ = [
|
|
14
|
+
"NodeFeatures",
|
|
15
|
+
"BifurcationFeatures",
|
|
16
|
+
"TipFeatures",
|
|
17
|
+
"PathFeatures",
|
|
18
|
+
"BranchFeatures",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
T = TypeVar("T", bound=Branch)
|
|
22
|
+
|
|
23
|
+
# Node Level
|
|
13
24
|
|
|
14
25
|
|
|
15
26
|
class NodeFeatures:
|
|
@@ -31,6 +42,7 @@ class NodeFeatures:
|
|
|
31
42
|
-------
|
|
32
43
|
count : array of shape (1,)
|
|
33
44
|
"""
|
|
45
|
+
|
|
34
46
|
return np.array([self.tree.number_of_nodes()], dtype=np.float32)
|
|
35
47
|
|
|
36
48
|
def get_radial_distance(self) -> npt.NDArray[np.float32]:
|
|
@@ -41,6 +53,7 @@ class NodeFeatures:
|
|
|
41
53
|
radial_distance : npt.NDArray[np.float32]
|
|
42
54
|
Array of shape (N,).
|
|
43
55
|
"""
|
|
56
|
+
|
|
44
57
|
xyz = self.tree.xyz() - self.tree.soma().xyz()
|
|
45
58
|
radial_distance = np.linalg.norm(xyz, axis=1)
|
|
46
59
|
return radial_distance
|
|
@@ -58,6 +71,7 @@ class NodeFeatures:
|
|
|
58
71
|
order : npt.NDArray[np.int32]
|
|
59
72
|
Array of shape (N,), which k is the number of branchs.
|
|
60
73
|
"""
|
|
74
|
+
|
|
61
75
|
order = np.zeros_like(self._branch_tree.id(), dtype=np.int32)
|
|
62
76
|
|
|
63
77
|
def assign_depth(n: Tree.Node, pre_depth: int | None) -> int:
|
|
@@ -88,6 +102,7 @@ class _SubsetNodesFeatures(ABC):
|
|
|
88
102
|
count : npt.NDArray[np.float32]
|
|
89
103
|
Array of shape (1,).
|
|
90
104
|
"""
|
|
105
|
+
|
|
91
106
|
return np.array([np.count_nonzero(self.nodes)], dtype=np.float32)
|
|
92
107
|
|
|
93
108
|
def get_radial_distance(self) -> npt.NDArray[np.float32]:
|
|
@@ -98,6 +113,7 @@ class _SubsetNodesFeatures(ABC):
|
|
|
98
113
|
radial_distance : npt.NDArray[np.float32]
|
|
99
114
|
Array of shape (N,).
|
|
100
115
|
"""
|
|
116
|
+
|
|
101
117
|
return self._features.get_radial_distance()[self.nodes]
|
|
102
118
|
|
|
103
119
|
@classmethod
|
|
@@ -119,3 +135,89 @@ class TipFeatures(_SubsetNodesFeatures):
|
|
|
119
135
|
@cached_property
|
|
120
136
|
def nodes(self) -> npt.NDArray[np.bool_]:
|
|
121
137
|
return np.array([n.is_tip() for n in self._features.tree])
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Path Level
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class PathFeatures:
|
|
144
|
+
"""Path analysis of tree."""
|
|
145
|
+
|
|
146
|
+
tree: Tree
|
|
147
|
+
|
|
148
|
+
def __init__(self, tree: Tree) -> None:
|
|
149
|
+
self.tree = tree
|
|
150
|
+
|
|
151
|
+
def get_count(self) -> int:
|
|
152
|
+
return len(self._paths)
|
|
153
|
+
|
|
154
|
+
def get_length(self) -> npt.NDArray[np.float32]:
|
|
155
|
+
"""Get length of paths."""
|
|
156
|
+
|
|
157
|
+
length = [path.length() for path in self._paths]
|
|
158
|
+
return np.array(length, dtype=np.float32)
|
|
159
|
+
|
|
160
|
+
def get_tortuosity(self) -> npt.NDArray[np.float32]:
|
|
161
|
+
"""Get tortuosity of path."""
|
|
162
|
+
|
|
163
|
+
return np.array([path.tortuosity() for path in self._paths], dtype=np.float32)
|
|
164
|
+
|
|
165
|
+
@cached_property
|
|
166
|
+
def _paths(self) -> List[Tree.Path]:
|
|
167
|
+
return self.tree.get_paths()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class BranchFeatures:
|
|
171
|
+
"""Analysis bransh of tree."""
|
|
172
|
+
|
|
173
|
+
tree: Tree
|
|
174
|
+
|
|
175
|
+
def __init__(self, tree: Tree) -> None:
|
|
176
|
+
self.tree = tree
|
|
177
|
+
|
|
178
|
+
def get_count(self) -> int:
|
|
179
|
+
return len(self._branches)
|
|
180
|
+
|
|
181
|
+
def get_length(self) -> npt.NDArray[np.float32]:
|
|
182
|
+
"""Get length of branches."""
|
|
183
|
+
|
|
184
|
+
length = [br.length() for br in self._branches]
|
|
185
|
+
return np.array(length, dtype=np.float32)
|
|
186
|
+
|
|
187
|
+
def get_tortuosity(self) -> npt.NDArray[np.float32]:
|
|
188
|
+
"""Get tortuosity of path."""
|
|
189
|
+
|
|
190
|
+
return np.array([br.tortuosity() for br in self._branches], dtype=np.float32)
|
|
191
|
+
|
|
192
|
+
def get_angle(self, eps: float = 1e-7) -> npt.NDArray[np.float32]:
|
|
193
|
+
"""Get agnle between branches.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
angle : npt.NDArray[np.float32]
|
|
198
|
+
An array of shape (N, N), which N is length of branches.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
return self.calc_angle(self._branches, eps=eps)
|
|
202
|
+
|
|
203
|
+
@staticmethod
|
|
204
|
+
def calc_angle(branches: List[T], eps: float = 1e-7) -> npt.NDArray[np.float32]:
|
|
205
|
+
"""Calc agnle between branches.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
angle : npt.NDArray[np.float32]
|
|
210
|
+
An array of shape (N, N), which N is length of branches.
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
vector = np.array([br[-1].xyz() - br[0].xyz() for br in branches])
|
|
214
|
+
vector_dot = np.matmul(vector, vector.T)
|
|
215
|
+
vector_norm = np.linalg.norm(vector, ord=2, axis=1, keepdims=True)
|
|
216
|
+
vector_norm_dot = np.matmul(vector_norm, vector_norm.T) + eps
|
|
217
|
+
arccos = np.clip(vector_dot / vector_norm_dot, -1, 1)
|
|
218
|
+
angle = np.arccos(arccos)
|
|
219
|
+
return angle
|
|
220
|
+
|
|
221
|
+
@cached_property
|
|
222
|
+
def _branches(self) -> List[Tree.Branch]:
|
|
223
|
+
return self.tree.get_branches()
|