swcgeom 0.18.3__py3-none-any.whl → 0.19.1__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/analysis/feature_extractor.py +22 -24
- swcgeom/analysis/features.py +18 -40
- swcgeom/analysis/lmeasure.py +227 -323
- swcgeom/analysis/sholl.py +17 -23
- swcgeom/analysis/trunk.py +23 -28
- swcgeom/analysis/visualization.py +37 -44
- swcgeom/analysis/visualization3d.py +16 -25
- swcgeom/analysis/volume.py +33 -47
- swcgeom/core/__init__.py +1 -6
- swcgeom/core/branch.py +10 -17
- swcgeom/core/branch_tree.py +3 -2
- swcgeom/core/compartment.py +1 -1
- swcgeom/core/node.py +3 -6
- swcgeom/core/path.py +11 -16
- swcgeom/core/population.py +32 -51
- swcgeom/core/swc.py +25 -16
- swcgeom/core/swc_utils/__init__.py +4 -6
- swcgeom/core/swc_utils/assembler.py +5 -12
- swcgeom/core/swc_utils/base.py +40 -31
- swcgeom/core/swc_utils/checker.py +3 -8
- swcgeom/core/swc_utils/io.py +32 -47
- swcgeom/core/swc_utils/normalizer.py +17 -23
- swcgeom/core/swc_utils/subtree.py +13 -20
- swcgeom/core/tree.py +61 -51
- swcgeom/core/tree_utils.py +36 -49
- swcgeom/core/tree_utils_impl.py +4 -6
- swcgeom/images/augmentation.py +23 -39
- swcgeom/images/contrast.py +22 -46
- swcgeom/images/folder.py +32 -34
- swcgeom/images/io.py +108 -126
- swcgeom/transforms/base.py +28 -19
- swcgeom/transforms/branch.py +31 -41
- swcgeom/transforms/branch_tree.py +3 -1
- swcgeom/transforms/geometry.py +13 -4
- swcgeom/transforms/image_preprocess.py +2 -0
- swcgeom/transforms/image_stack.py +40 -35
- swcgeom/transforms/images.py +31 -24
- swcgeom/transforms/mst.py +27 -40
- swcgeom/transforms/neurolucida_asc.py +13 -13
- swcgeom/transforms/path.py +4 -0
- swcgeom/transforms/population.py +4 -0
- swcgeom/transforms/tree.py +16 -11
- swcgeom/transforms/tree_assembler.py +37 -54
- swcgeom/utils/download.py +7 -14
- swcgeom/utils/dsu.py +12 -0
- swcgeom/utils/ellipse.py +26 -14
- swcgeom/utils/file.py +8 -13
- swcgeom/utils/neuromorpho.py +78 -92
- swcgeom/utils/numpy_helper.py +15 -12
- swcgeom/utils/plotter_2d.py +10 -16
- swcgeom/utils/plotter_3d.py +7 -9
- swcgeom/utils/renderer.py +16 -8
- swcgeom/utils/sdf.py +12 -23
- swcgeom/utils/solid_geometry.py +58 -2
- swcgeom/utils/transforms.py +164 -100
- swcgeom/utils/volumetric_object.py +29 -53
- {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/METADATA +6 -5
- swcgeom-0.19.1.dist-info/RECORD +67 -0
- {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/WHEEL +1 -1
- swcgeom-0.18.3.dist-info/RECORD +0 -67
- {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info/licenses}/LICENSE +0 -0
- {swcgeom-0.18.3.dist-info → swcgeom-0.19.1.dist-info}/top_level.txt +0 -0
|
@@ -15,10 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
"""Easy way to compute and visualize common features for feature.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
For development, see method `Features.get_evaluator` to confirm the
|
|
21
|
-
naming specification.
|
|
18
|
+
NOTE: For development, see method `Features.get_evaluator` to confirm the naming
|
|
19
|
+
specification.
|
|
22
20
|
"""
|
|
23
21
|
|
|
24
22
|
from abc import ABC, abstractmethod
|
|
@@ -83,23 +81,31 @@ class Features:
|
|
|
83
81
|
|
|
84
82
|
tree: Tree
|
|
85
83
|
|
|
86
|
-
# fmt:off
|
|
87
84
|
# Modules
|
|
88
85
|
@cached_property
|
|
89
|
-
def node_features(self) -> NodeFeatures:
|
|
86
|
+
def node_features(self) -> NodeFeatures:
|
|
87
|
+
return NodeFeatures(self.tree)
|
|
88
|
+
|
|
90
89
|
@cached_property
|
|
91
|
-
def furcation_features(self) -> FurcationFeatures:
|
|
90
|
+
def furcation_features(self) -> FurcationFeatures:
|
|
91
|
+
return FurcationFeatures(self.node_features)
|
|
92
|
+
|
|
92
93
|
@cached_property
|
|
93
|
-
def tip_features(self) -> TipFeatures:
|
|
94
|
+
def tip_features(self) -> TipFeatures:
|
|
95
|
+
return TipFeatures(self.node_features)
|
|
96
|
+
|
|
94
97
|
@cached_property
|
|
95
|
-
def branch_features(self) -> BranchFeatures:
|
|
98
|
+
def branch_features(self) -> BranchFeatures:
|
|
99
|
+
return BranchFeatures(self.tree)
|
|
100
|
+
|
|
96
101
|
@cached_property
|
|
97
|
-
def path_features(self) -> PathFeatures:
|
|
102
|
+
def path_features(self) -> PathFeatures:
|
|
103
|
+
return PathFeatures(self.tree)
|
|
98
104
|
|
|
99
105
|
# Caches
|
|
100
106
|
@cached_property
|
|
101
|
-
def sholl(self) -> Sholl:
|
|
102
|
-
|
|
107
|
+
def sholl(self) -> Sholl:
|
|
108
|
+
return Sholl(self.tree)
|
|
103
109
|
|
|
104
110
|
def __init__(self, tree: Tree) -> None:
|
|
105
111
|
self.tree = tree
|
|
@@ -136,22 +142,17 @@ class Features:
|
|
|
136
142
|
class FeatureExtractor(ABC):
|
|
137
143
|
"""Extract features from tree."""
|
|
138
144
|
|
|
139
|
-
# fmt:off
|
|
140
145
|
@overload
|
|
141
146
|
def get(self, feature: Feature, **kwargs) -> NDArrayf32: ...
|
|
142
147
|
@overload
|
|
143
148
|
def get(self, feature: list[FeatAndKwargs]) -> list[NDArrayf32]: ...
|
|
144
149
|
@overload
|
|
145
150
|
def get(self, feature: dict[Feature, dict[str, Any]]) -> dict[str, NDArrayf32]: ...
|
|
146
|
-
# fmt:on
|
|
147
151
|
def get(self, feature, **kwargs):
|
|
148
152
|
"""Get feature.
|
|
149
153
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
Shape of returned array is not uniform, `TreeFeatureExtractor`
|
|
153
|
-
returns array of shape (L, ), `PopulationFeatureExtracor`
|
|
154
|
-
returns array of shape (N, L).
|
|
154
|
+
NOTE: Shape of returned array is not uniform, `TreeFeatureExtractor` returns
|
|
155
|
+
array of shape (L, ), `PopulationFeatureExtracor` returns array of shape (N, L).
|
|
155
156
|
"""
|
|
156
157
|
if isinstance(feature, dict):
|
|
157
158
|
return {k: self._get(k, **v) for k, v in feature.items()}
|
|
@@ -164,11 +165,8 @@ class FeatureExtractor(ABC):
|
|
|
164
165
|
def plot(self, feature: FeatAndKwargs, title: str | bool = True, **kwargs) -> Axes:
|
|
165
166
|
"""Plot feature with appropriate way.
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
The drawing method is different in different classes, different
|
|
170
|
-
in different features, and may different between versions,
|
|
171
|
-
there are NO guarantees.
|
|
168
|
+
NOTE: The drawing method is different in different classes, different in
|
|
169
|
+
different features, and may different between versions, there are NO guarantees.
|
|
172
170
|
"""
|
|
173
171
|
feat, feat_kwargs = _get_feat_and_kwargs(feature)
|
|
174
172
|
if callable(custom_plot := getattr(self, f"plot_{feat}", None)):
|
swcgeom/analysis/features.py
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
"""Feature
|
|
16
|
+
"""Feature analysis of tree."""
|
|
17
17
|
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
19
|
from functools import cached_property
|
|
@@ -53,22 +53,17 @@ class NodeFeatures:
|
|
|
53
53
|
def get_count(self) -> npt.NDArray[np.float32]:
|
|
54
54
|
"""Get number of nodes.
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
count : array of shape (1,)
|
|
56
|
+
Args:
|
|
57
|
+
count: array of shape (1,)
|
|
59
58
|
"""
|
|
60
|
-
|
|
61
59
|
return np.array([self.tree.number_of_nodes()], dtype=np.float32)
|
|
62
60
|
|
|
63
61
|
def get_radial_distance(self) -> npt.NDArray[np.float32]:
|
|
64
62
|
"""Get the end-to-end straight-line distance to soma.
|
|
65
63
|
|
|
66
|
-
Returns
|
|
67
|
-
|
|
68
|
-
radial_distance : npt.NDArray[np.float32]
|
|
69
|
-
Array of shape (N,).
|
|
64
|
+
Returns:
|
|
65
|
+
radial_distance: Array of shape (N,).
|
|
70
66
|
"""
|
|
71
|
-
|
|
72
67
|
xyz = self.tree.xyz() - self.tree.soma().xyz()
|
|
73
68
|
radial_distance = np.linalg.norm(xyz, axis=1)
|
|
74
69
|
return radial_distance
|
|
@@ -81,12 +76,9 @@ class NodeFeatures:
|
|
|
81
76
|
|
|
82
77
|
Criticle node means that soma, bifucation nodes, tips.
|
|
83
78
|
|
|
84
|
-
Returns
|
|
85
|
-
|
|
86
|
-
order : npt.NDArray[np.int32]
|
|
87
|
-
Array of shape (N,), which k is the number of branchs.
|
|
79
|
+
Returns:
|
|
80
|
+
order: Array of shape (N,), which k is the number of branches.
|
|
88
81
|
"""
|
|
89
|
-
|
|
90
82
|
order = np.zeros_like(self._branch_tree.id(), dtype=np.int32)
|
|
91
83
|
|
|
92
84
|
def assign_depth(n: Tree.Node, pre_depth: int | None) -> int:
|
|
@@ -112,23 +104,17 @@ class _SubsetNodesFeatures(ABC):
|
|
|
112
104
|
def get_count(self) -> npt.NDArray[np.float32]:
|
|
113
105
|
"""Get number of nodes.
|
|
114
106
|
|
|
115
|
-
Returns
|
|
116
|
-
|
|
117
|
-
count : npt.NDArray[np.float32]
|
|
118
|
-
Array of shape (1,).
|
|
107
|
+
Returns:
|
|
108
|
+
count: Array of shape (1,).
|
|
119
109
|
"""
|
|
120
|
-
|
|
121
110
|
return np.array([np.count_nonzero(self.nodes)], dtype=np.float32)
|
|
122
111
|
|
|
123
112
|
def get_radial_distance(self) -> npt.NDArray[np.float32]:
|
|
124
113
|
"""Get the end-to-end straight-line distance to soma.
|
|
125
114
|
|
|
126
|
-
Returns
|
|
127
|
-
|
|
128
|
-
radial_distance : npt.NDArray[np.float32]
|
|
129
|
-
Array of shape (N,).
|
|
115
|
+
Returns:
|
|
116
|
+
radial_distance: Array of shape (N,).
|
|
130
117
|
"""
|
|
131
|
-
|
|
132
118
|
return self._features.get_radial_distance()[self.nodes]
|
|
133
119
|
|
|
134
120
|
@classmethod
|
|
@@ -148,11 +134,9 @@ class FurcationFeatures(_SubsetNodesFeatures):
|
|
|
148
134
|
class BifurcationFeatures(FurcationFeatures):
|
|
149
135
|
"""Evaluate bifurcation node feature of tree.
|
|
150
136
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
is just an alias of `FurcationFeatures` and raise a warning. It
|
|
155
|
-
will be change to raise an error in the future.
|
|
137
|
+
NOTE: Deprecated due to the wrong spelling of furcation. For now, it is just an
|
|
138
|
+
alias of `FurcationFeatures` and raise a warning. It will be change to raise an
|
|
139
|
+
error in the future.
|
|
156
140
|
"""
|
|
157
141
|
|
|
158
142
|
|
|
@@ -219,24 +203,18 @@ class BranchFeatures:
|
|
|
219
203
|
def get_angle(self, eps: float = 1e-7) -> npt.NDArray[np.float32]:
|
|
220
204
|
"""Get agnle between branches.
|
|
221
205
|
|
|
222
|
-
Returns
|
|
223
|
-
|
|
224
|
-
angle : npt.NDArray[np.float32]
|
|
225
|
-
An array of shape (N, N), which N is length of branches.
|
|
206
|
+
Returns:
|
|
207
|
+
angle: An array of shape (N, N), which N is length of branches.
|
|
226
208
|
"""
|
|
227
|
-
|
|
228
209
|
return self.calc_angle(self._branches, eps=eps)
|
|
229
210
|
|
|
230
211
|
@staticmethod
|
|
231
212
|
def calc_angle(branches: list[T], eps: float = 1e-7) -> npt.NDArray[np.float32]:
|
|
232
213
|
"""Calc agnle between branches.
|
|
233
214
|
|
|
234
|
-
Returns
|
|
235
|
-
|
|
236
|
-
angle : npt.NDArray[np.float32]
|
|
237
|
-
An array of shape (N, N), which N is length of branches.
|
|
215
|
+
Returns:
|
|
216
|
+
angle: An array of shape (N, N), which N is length of branches.
|
|
238
217
|
"""
|
|
239
|
-
|
|
240
218
|
vector = np.array([br[-1].xyz() - br[0].xyz() for br in branches])
|
|
241
219
|
vector_dot = np.matmul(vector, vector.T)
|
|
242
220
|
vector_norm = np.linalg.norm(vector, ord=2, axis=1, keepdims=True)
|