swcgeom 0.14.0__py3-none-any.whl → 0.16.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/lmeasure.py +821 -0
- swcgeom/analysis/sholl.py +31 -2
- swcgeom/core/__init__.py +4 -0
- swcgeom/core/branch.py +9 -4
- swcgeom/core/branch_tree.py +2 -3
- swcgeom/core/{segment.py → compartment.py} +14 -9
- swcgeom/core/node.py +0 -8
- swcgeom/core/path.py +21 -6
- swcgeom/core/population.py +42 -3
- swcgeom/core/swc_utils/assembler.py +20 -138
- swcgeom/core/swc_utils/base.py +12 -5
- swcgeom/core/swc_utils/checker.py +12 -2
- swcgeom/core/swc_utils/subtree.py +2 -2
- swcgeom/core/tree.py +53 -49
- swcgeom/core/tree_utils.py +27 -5
- swcgeom/core/tree_utils_impl.py +22 -6
- swcgeom/images/augmentation.py +6 -1
- swcgeom/images/contrast.py +107 -0
- swcgeom/images/folder.py +111 -29
- swcgeom/images/io.py +79 -40
- swcgeom/transforms/__init__.py +2 -0
- swcgeom/transforms/base.py +41 -21
- swcgeom/transforms/branch.py +5 -5
- swcgeom/transforms/geometry.py +42 -18
- swcgeom/transforms/image_preprocess.py +100 -0
- swcgeom/transforms/image_stack.py +46 -28
- swcgeom/transforms/images.py +76 -6
- swcgeom/transforms/mst.py +10 -18
- swcgeom/transforms/neurolucida_asc.py +495 -0
- swcgeom/transforms/population.py +2 -2
- swcgeom/transforms/tree.py +12 -14
- swcgeom/transforms/tree_assembler.py +85 -19
- 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.14.0.dist-info → swcgeom-0.16.0.dist-info}/METADATA +3 -3
- swcgeom-0.16.0.dist-info/RECORD +67 -0
- {swcgeom-0.14.0.dist-info → swcgeom-0.16.0.dist-info}/WHEEL +1 -1
- swcgeom-0.14.0.dist-info/RECORD +0 -62
- {swcgeom-0.14.0.dist-info → swcgeom-0.16.0.dist-info}/LICENSE +0 -0
- {swcgeom-0.14.0.dist-info → swcgeom-0.16.0.dist-info}/top_level.txt +0 -0
|
@@ -25,6 +25,7 @@ from sdflit import (
|
|
|
25
25
|
Scene,
|
|
26
26
|
SDFObject,
|
|
27
27
|
)
|
|
28
|
+
from tqdm import tqdm
|
|
28
29
|
|
|
29
30
|
from swcgeom.core import Population, Tree
|
|
30
31
|
from swcgeom.transforms.base import Transform
|
|
@@ -37,7 +38,7 @@ class ToImageStack(Transform[Tree, npt.NDArray[np.uint8]]):
|
|
|
37
38
|
|
|
38
39
|
resolution: npt.NDArray[np.float32]
|
|
39
40
|
|
|
40
|
-
def __init__(self, resolution: float | npt.ArrayLike = 1) -> None:
|
|
41
|
+
def __init__(self, resolution: int | float | npt.ArrayLike = 1) -> None:
|
|
41
42
|
"""Transform tree to image stack.
|
|
42
43
|
|
|
43
44
|
Parameters
|
|
@@ -46,11 +47,11 @@ class ToImageStack(Transform[Tree, npt.NDArray[np.uint8]]):
|
|
|
46
47
|
Resolution of image stack.
|
|
47
48
|
"""
|
|
48
49
|
|
|
49
|
-
if isinstance(resolution, float):
|
|
50
|
-
resolution = [resolution, resolution, resolution]
|
|
50
|
+
if isinstance(resolution, (int, float, np.integer, np.floating)):
|
|
51
|
+
resolution = [resolution, resolution, resolution] # type: ignore
|
|
51
52
|
|
|
52
|
-
self.resolution =
|
|
53
|
-
assert
|
|
53
|
+
self.resolution = np.array(resolution, dtype=np.float32)
|
|
54
|
+
assert len(self.resolution) == 3, "resolution shoule be vector of 3d."
|
|
54
55
|
|
|
55
56
|
def __call__(self, x: Tree) -> npt.NDArray[np.uint8]:
|
|
56
57
|
"""Transform tree to image stack.
|
|
@@ -58,57 +59,74 @@ class ToImageStack(Transform[Tree, npt.NDArray[np.uint8]]):
|
|
|
58
59
|
Notes
|
|
59
60
|
-----
|
|
60
61
|
This method loads the entire image stack into memory, so it
|
|
61
|
-
ONLY works for small image stacks, use
|
|
62
|
-
|
|
62
|
+
ONLY works for small image stacks, use :meth`transform_and_save`
|
|
63
|
+
for big image stack.
|
|
63
64
|
"""
|
|
64
|
-
return np.
|
|
65
|
-
|
|
66
|
-
def __repr__(self) -> str:
|
|
67
|
-
return "ToImageStack" + f"-resolution-{'-'.join(self.resolution)}"
|
|
65
|
+
return np.stack(list(self.transfrom(x, verbose=False)), axis=0)
|
|
68
66
|
|
|
69
67
|
def transfrom(
|
|
70
|
-
self,
|
|
68
|
+
self,
|
|
69
|
+
x: Tree,
|
|
70
|
+
verbose: bool = True,
|
|
71
|
+
*,
|
|
72
|
+
ranges: Optional[Tuple[npt.ArrayLike, npt.ArrayLike]] = None,
|
|
71
73
|
) -> Iterable[npt.NDArray[np.uint8]]:
|
|
72
|
-
from tqdm import tqdm
|
|
73
|
-
|
|
74
74
|
if verbose:
|
|
75
75
|
print("To image stack: " + x.source)
|
|
76
76
|
time_start = time.time()
|
|
77
77
|
|
|
78
78
|
scene = self._get_scene(x)
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
if ranges is None:
|
|
81
|
+
xyz, r = x.xyz(), x.r().reshape(-1, 1)
|
|
82
|
+
coord_min = np.floor(np.min(xyz - r, axis=0))
|
|
83
|
+
coord_max = np.ceil(np.max(xyz + r, axis=0))
|
|
84
|
+
else:
|
|
85
|
+
assert len(ranges) == 2
|
|
86
|
+
coord_min = np.array(ranges[0])
|
|
87
|
+
coord_max = np.array(ranges[1])
|
|
88
|
+
assert len(coord_min) == len(coord_max) == 3
|
|
83
89
|
|
|
84
90
|
samplers = self._get_samplers(coord_min, coord_max)
|
|
85
|
-
total = (
|
|
86
|
-
((coord_max[2] - coord_min[2]) / self.resolution[2]).astype(np.int64).item()
|
|
87
|
-
)
|
|
88
91
|
|
|
89
92
|
if verbose:
|
|
93
|
+
total = (coord_max[2] - coord_min[2]) / self.resolution[2]
|
|
94
|
+
samplers = tqdm(samplers, total=total.astype(np.int64).item())
|
|
95
|
+
|
|
90
96
|
time_end = time.time()
|
|
91
97
|
print("Prepare in: ", time_end - time_start, "s") # type: ignore
|
|
92
98
|
|
|
93
|
-
for sampler in
|
|
94
|
-
voxel = sampler.sample(scene) #
|
|
99
|
+
for sampler in samplers:
|
|
100
|
+
voxel = sampler.sample(scene) # should be shape of (x, y, z, 3) and z = 1
|
|
95
101
|
frame = (255 * voxel[..., 0, 0]).astype(np.uint8)
|
|
96
102
|
yield frame
|
|
97
103
|
|
|
98
|
-
def transform_and_save(
|
|
99
|
-
self
|
|
104
|
+
def transform_and_save(
|
|
105
|
+
self, fname: str, x: Tree, verbose: bool = True, **kwargs
|
|
106
|
+
) -> None:
|
|
107
|
+
self.save_tif(fname, self.transfrom(x, verbose=verbose, **kwargs))
|
|
100
108
|
|
|
101
109
|
def transform_population(
|
|
102
110
|
self, population: Population | str, verbose: bool = True
|
|
103
111
|
) -> None:
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
trees = (
|
|
113
|
+
Population.from_swc(population)
|
|
114
|
+
if isinstance(population, str)
|
|
115
|
+
else population
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if verbose:
|
|
119
|
+
trees = tqdm(trees)
|
|
106
120
|
|
|
107
121
|
# TODO: multiprocess
|
|
108
|
-
for tree in
|
|
122
|
+
for tree in trees:
|
|
109
123
|
tif = re.sub(r".swc$", ".tif", tree.source)
|
|
110
124
|
if not os.path.isfile(tif):
|
|
111
|
-
self.transform_and_save(tif, tree, verbose=
|
|
125
|
+
self.transform_and_save(tif, tree, verbose=False)
|
|
126
|
+
|
|
127
|
+
def extra_repr(self):
|
|
128
|
+
res = ",".join(f"{a:.4f}" for a in self.resolution)
|
|
129
|
+
return f"resolution=({res})"
|
|
112
130
|
|
|
113
131
|
def _get_scene(self, x: Tree) -> Scene:
|
|
114
132
|
material = ColoredMaterial((1, 0, 0)).into()
|
swcgeom/transforms/images.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Image stack related transform."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import warnings
|
|
4
4
|
from typing import Tuple
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
@@ -8,10 +8,20 @@ import numpy.typing as npt
|
|
|
8
8
|
|
|
9
9
|
from swcgeom.transforms.base import Transform
|
|
10
10
|
|
|
11
|
-
__all__ = [
|
|
11
|
+
__all__ = [
|
|
12
|
+
"ImagesCenterCrop",
|
|
13
|
+
"ImagesScale",
|
|
14
|
+
"ImagesClip",
|
|
15
|
+
"ImagesNormalizer",
|
|
16
|
+
"ImagesMeanVarianceAdjustment",
|
|
17
|
+
"Center", # legacy
|
|
18
|
+
]
|
|
19
|
+
|
|
12
20
|
|
|
21
|
+
NDArrayf32 = npt.NDArray[np.float32]
|
|
13
22
|
|
|
14
|
-
|
|
23
|
+
|
|
24
|
+
class ImagesCenterCrop(Transform[NDArrayf32, NDArrayf32]):
|
|
15
25
|
"""Get image stack center."""
|
|
16
26
|
|
|
17
27
|
def __init__(self, shape_out: int | Tuple[int, int, int]):
|
|
@@ -22,11 +32,71 @@ class Center(Transform[npt.NDArray[np.float32], npt.NDArray[np.float32]]):
|
|
|
22
32
|
else (shape_out, shape_out, shape_out)
|
|
23
33
|
)
|
|
24
34
|
|
|
25
|
-
def __call__(self, x:
|
|
35
|
+
def __call__(self, x: NDArrayf32) -> NDArrayf32:
|
|
26
36
|
diff = np.subtract(x.shape[:3], self.shape_out)
|
|
27
37
|
s = diff // 2
|
|
28
38
|
e = np.add(s, self.shape_out)
|
|
29
39
|
return x[s[0] : e[0], s[1] : e[1], s[2] : e[2], :]
|
|
30
40
|
|
|
31
|
-
def
|
|
32
|
-
return f"
|
|
41
|
+
def extra_repr(self) -> str:
|
|
42
|
+
return f"shape_out=({','.join(str(a) for a in self.shape_out)})"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Center(ImagesCenterCrop):
|
|
46
|
+
"""Get image stack center.
|
|
47
|
+
|
|
48
|
+
.. deprecated:: 0.5.0
|
|
49
|
+
Use :class:`ImagesCenterCrop` instead.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, shape_out: int | Tuple[int, int, int]):
|
|
53
|
+
warnings.warn(
|
|
54
|
+
"`Center` is deprecated, use `ImagesCenterCrop` instead",
|
|
55
|
+
DeprecationWarning,
|
|
56
|
+
stacklevel=2,
|
|
57
|
+
)
|
|
58
|
+
super().__init__(shape_out)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ImagesScale(Transform[NDArrayf32, NDArrayf32]):
|
|
62
|
+
def __init__(self, scaler: float) -> None:
|
|
63
|
+
super().__init__()
|
|
64
|
+
self.scaler = scaler
|
|
65
|
+
|
|
66
|
+
def __call__(self, x: NDArrayf32) -> NDArrayf32:
|
|
67
|
+
return self.scaler * x
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ImagesClip(Transform[NDArrayf32, NDArrayf32]):
|
|
71
|
+
def __init__(self, vmin: float = 0, vmax: float = 1, /) -> None:
|
|
72
|
+
super().__init__()
|
|
73
|
+
self.vmin, self.vmax = vmin, vmax
|
|
74
|
+
|
|
75
|
+
def __call__(self, x: NDArrayf32) -> NDArrayf32:
|
|
76
|
+
return np.clip(x, self.vmin, self.vmax)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ImagesNormalizer(Transform[NDArrayf32, NDArrayf32]):
|
|
80
|
+
"""Normalize image stack."""
|
|
81
|
+
|
|
82
|
+
def __call__(self, x: NDArrayf32) -> NDArrayf32:
|
|
83
|
+
mean = np.mean(x)
|
|
84
|
+
variance = np.var(x)
|
|
85
|
+
return (x - mean) / variance
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ImagesMeanVarianceAdjustment(Transform[NDArrayf32, NDArrayf32]):
|
|
89
|
+
"""Adjust image stack mean and variance.
|
|
90
|
+
|
|
91
|
+
See Also
|
|
92
|
+
--------
|
|
93
|
+
~swcgeom.images.ImageStackFolder.stat
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self, mean: float, variance: float) -> None:
|
|
97
|
+
super().__init__()
|
|
98
|
+
self.mean = mean
|
|
99
|
+
self.variance = variance
|
|
100
|
+
|
|
101
|
+
def __call__(self, x: NDArrayf32) -> NDArrayf32:
|
|
102
|
+
return (x - self.mean) / self.variance
|
swcgeom/transforms/mst.py
CHANGED
|
@@ -23,11 +23,11 @@ class PointsToCuntzMST(Transform[npt.NDArray[np.float32], Tree]):
|
|
|
23
23
|
|
|
24
24
|
References
|
|
25
25
|
----------
|
|
26
|
-
[1] Cuntz, H., Forstner, F., Borst, A. & Häusser, M. One Rule to
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
[2] Cuntz, H., Borst, A. & Segev, I. Optimization principles of
|
|
30
|
-
|
|
26
|
+
.. [1] Cuntz, H., Forstner, F., Borst, A. & Häusser, M. One Rule to
|
|
27
|
+
Grow Them Al: A General Theory of Neuronal Branching and Its
|
|
28
|
+
Practical Application. PLOS Comput Biol 6, e1000877 (2010).
|
|
29
|
+
.. [2] Cuntz, H., Borst, A. & Segev, I. Optimization principles of
|
|
30
|
+
dendritic structure. Theor Biol Med Model 4, 21 (2007).
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
@@ -140,13 +140,8 @@ class PointsToCuntzMST(Transform[npt.NDArray[np.float32], Tree]):
|
|
|
140
140
|
t = sort_tree(t)
|
|
141
141
|
return t
|
|
142
142
|
|
|
143
|
-
def
|
|
144
|
-
return
|
|
145
|
-
f"PointsToCuntzMST"
|
|
146
|
-
f"-bf-{self.bf}"
|
|
147
|
-
f"-furcations-{self.furcations}"
|
|
148
|
-
f"-{'exclude-soma' if self.exclude_soma else 'include-soma'}"
|
|
149
|
-
) # TODO: names, types
|
|
143
|
+
def extra_repr(self) -> str: # TODO: names, types
|
|
144
|
+
return f"bf={self.bf:.4f}, furcations={self.furcations}, exclude_soma={self.exclude_soma}, sort={self.sort}"
|
|
150
145
|
|
|
151
146
|
|
|
152
147
|
class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
|
|
@@ -173,6 +168,7 @@ class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
|
|
|
173
168
|
names : SWCNames, optional
|
|
174
169
|
types : SWCTypes, optional
|
|
175
170
|
"""
|
|
171
|
+
|
|
176
172
|
if k_furcations is not None:
|
|
177
173
|
warnings.warn(
|
|
178
174
|
"`PointsToMST(k_furcations=...)` has been renamed to "
|
|
@@ -191,9 +187,5 @@ class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
|
|
|
191
187
|
**kwargs,
|
|
192
188
|
)
|
|
193
189
|
|
|
194
|
-
def
|
|
195
|
-
return
|
|
196
|
-
f"PointsToMST"
|
|
197
|
-
f"-furcations-{self.furcations}"
|
|
198
|
-
f"-{'exclude-soma' if self.exclude_soma else 'include-soma'}"
|
|
199
|
-
)
|
|
190
|
+
def extra_repr(self) -> str:
|
|
191
|
+
return f"furcations-{self.furcations}, exclude-soma={self.exclude_soma}"
|