swcgeom 0.11.1__py3-none-any.whl → 0.13.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/__init__.py +4 -4
- swcgeom/_version.py +14 -2
- swcgeom/analysis/__init__.py +7 -7
- swcgeom/analysis/branch_features.py +1 -1
- swcgeom/analysis/feature_extractor.py +25 -12
- swcgeom/analysis/node_features.py +1 -1
- swcgeom/analysis/path_features.py +1 -1
- swcgeom/analysis/sholl.py +11 -7
- swcgeom/analysis/trunk.py +5 -5
- swcgeom/analysis/visualization.py +2 -2
- swcgeom/analysis/volume.py +80 -0
- swcgeom/core/__init__.py +9 -9
- swcgeom/core/branch.py +8 -4
- swcgeom/core/branch_tree.py +4 -5
- swcgeom/core/node.py +5 -3
- swcgeom/core/path.py +6 -3
- swcgeom/core/population.py +2 -2
- swcgeom/core/segment.py +8 -4
- swcgeom/core/swc.py +24 -3
- swcgeom/core/swc_utils/__init__.py +6 -6
- swcgeom/core/swc_utils/assembler.py +2 -2
- swcgeom/core/swc_utils/base.py +30 -1
- swcgeom/core/swc_utils/checker.py +30 -6
- swcgeom/core/swc_utils/io.py +31 -30
- swcgeom/core/swc_utils/normalizer.py +1 -1
- swcgeom/core/swc_utils/subtree.py +1 -1
- swcgeom/core/tree.py +38 -14
- swcgeom/core/tree_utils.py +47 -41
- swcgeom/core/tree_utils_impl.py +39 -0
- swcgeom/images/__init__.py +2 -2
- swcgeom/images/folder.py +2 -2
- swcgeom/images/io.py +48 -9
- swcgeom/transforms/__init__.py +10 -8
- swcgeom/transforms/branch.py +3 -3
- swcgeom/transforms/geometry.py +11 -4
- swcgeom/transforms/image_stack.py +3 -3
- swcgeom/transforms/images.py +1 -1
- swcgeom/transforms/mst.py +68 -13
- swcgeom/transforms/path.py +48 -0
- swcgeom/transforms/population.py +2 -2
- swcgeom/transforms/tree.py +18 -9
- swcgeom/transforms/tree_assembler.py +7 -4
- swcgeom/utils/__init__.py +10 -7
- swcgeom/utils/dsu.py +42 -0
- swcgeom/utils/file.py +91 -0
- swcgeom/utils/geometry_object.py +299 -0
- swcgeom/utils/neuromorpho.py +33 -11
- swcgeom/utils/renderer.py +5 -4
- swcgeom/utils/transforms.py +26 -1
- {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/METADATA +8 -8
- swcgeom-0.13.0.dist-info/RECORD +61 -0
- {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/WHEEL +1 -1
- swcgeom-0.11.1.dist-info/RECORD +0 -55
- /swcgeom/utils/{numpy.py → numpy_helper.py} +0 -0
- {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/LICENSE +0 -0
- {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""Geometry object."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
from typing import List, Tuple
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
9
|
+
from sympy import Eq, solve, symbols
|
|
10
|
+
|
|
11
|
+
__all__ = ["GeomObject", "GeomSphere", "GeomFrustumCone"]
|
|
12
|
+
|
|
13
|
+
eps = 1e-6
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GeomObject(ABC):
|
|
17
|
+
"""Geometry object."""
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def get_volume(self) -> float:
|
|
21
|
+
"""Get volume."""
|
|
22
|
+
raise NotImplementedError()
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def get_intersect_volume(self, obj: "GeomObject") -> float:
|
|
26
|
+
"""Get intersect volume.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
obj : GeometryObject
|
|
31
|
+
Another geometry object.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
volume : float
|
|
36
|
+
Intersect volume.
|
|
37
|
+
"""
|
|
38
|
+
raise NotImplementedError()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class GeomSphere(GeomObject):
|
|
42
|
+
"""Geometry Sphere."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, center: npt.ArrayLike, radius: float):
|
|
45
|
+
super().__init__()
|
|
46
|
+
|
|
47
|
+
self.center = np.array(center)
|
|
48
|
+
assert len(self.center) == 3
|
|
49
|
+
|
|
50
|
+
self.radius = radius
|
|
51
|
+
|
|
52
|
+
def get_volume(self) -> float:
|
|
53
|
+
return self.calc_volume(self.radius)
|
|
54
|
+
|
|
55
|
+
def get_volume_spherical_cap(self, h: float) -> float:
|
|
56
|
+
return self.calc_volume_spherical_cap(self.radius, h)
|
|
57
|
+
|
|
58
|
+
def get_intersect_volume_sphere(self, obj: "GeomSphere") -> float:
|
|
59
|
+
return self.calc_intersect_volume_sphere(self, obj)
|
|
60
|
+
|
|
61
|
+
def get_intersect_volume_sphere_frustum_cone(
|
|
62
|
+
self, frustum_cone: "GeomFrustumCone"
|
|
63
|
+
) -> float:
|
|
64
|
+
return calc_intersect_volume_sphere_frustum_cone(self, frustum_cone)
|
|
65
|
+
|
|
66
|
+
def get_intersect_volume(self, obj: GeomObject) -> float:
|
|
67
|
+
if isinstance(obj, GeomSphere):
|
|
68
|
+
return self.get_intersect_volume_sphere(obj)
|
|
69
|
+
|
|
70
|
+
if isinstance(obj, GeomFrustumCone):
|
|
71
|
+
return self.get_intersect_volume_sphere_frustum_cone(obj)
|
|
72
|
+
|
|
73
|
+
classname = obj.__class__.__name__
|
|
74
|
+
raise NotImplementedError(f"unsupported geometry object: {classname}")
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def calc_volume(radius: float) -> float:
|
|
78
|
+
r"""Calculate volume of sphere.
|
|
79
|
+
|
|
80
|
+
\being{equation}
|
|
81
|
+
V = \frac{4}{3} * π * r^3
|
|
82
|
+
\end{equation}
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
volume : float
|
|
87
|
+
Volume.
|
|
88
|
+
"""
|
|
89
|
+
return 4 / 3 * np.pi * radius**3
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def calc_volume_spherical_cap(r: float, h: float) -> float:
|
|
93
|
+
r"""Calculate the volume of a spherical cap.
|
|
94
|
+
|
|
95
|
+
\being{equation}
|
|
96
|
+
V = π * h^2 * (3r - h) / 3
|
|
97
|
+
\end{equation}
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
r : float
|
|
102
|
+
radius of the sphere
|
|
103
|
+
h : float
|
|
104
|
+
height of the spherical cap
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
volume : float
|
|
109
|
+
volume of the spherical cap
|
|
110
|
+
"""
|
|
111
|
+
return np.pi * h**2 * (3 * r - h) / 3
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def calc_intersect_volume_sphere(
|
|
115
|
+
cls, obj1: "GeomSphere", obj2: "GeomSphere"
|
|
116
|
+
) -> float:
|
|
117
|
+
r"""Calculate intersect volume of two spheres.
|
|
118
|
+
|
|
119
|
+
\being{equation}
|
|
120
|
+
V = \frac{\pi}{12d} * (r_1 + r_2 - d)^2 (d^2 + 2d r_1 - 3r_1^2 + 2d r_2 - 3r_2^2 + 6 r_1r_2)
|
|
121
|
+
\end{equation}
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
volume : float
|
|
126
|
+
Intersect volume.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
r1, r2 = obj1.radius, obj2.radius
|
|
130
|
+
d = np.linalg.norm(obj1.center - obj2.center).item()
|
|
131
|
+
if d > r1 + r2:
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
if d <= abs(r1 - r2):
|
|
135
|
+
return cls.calc_volume(min(r1, r2))
|
|
136
|
+
|
|
137
|
+
part1 = (np.pi / (12 * d)) * (r1 + r2 - d) ** 2
|
|
138
|
+
part2 = (
|
|
139
|
+
d**2 + 2 * d * r1 - 3 * r1**2 + 2 * d * r2 - 3 * r2**2 + 6 * r1 * r2
|
|
140
|
+
)
|
|
141
|
+
return part1 * part2
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class GeomFrustumCone(GeomObject):
|
|
145
|
+
"""Geometry Frustum."""
|
|
146
|
+
|
|
147
|
+
def __init__(self, c1: npt.ArrayLike, r1: float, c2: npt.ArrayLike, r2: float):
|
|
148
|
+
super().__init__()
|
|
149
|
+
|
|
150
|
+
self.c1 = np.array(c1)
|
|
151
|
+
assert len(self.c1) == 3
|
|
152
|
+
|
|
153
|
+
self.c2 = np.array(c2)
|
|
154
|
+
assert len(self.c2) == 3
|
|
155
|
+
|
|
156
|
+
self.r1 = r1
|
|
157
|
+
self.r2 = r2
|
|
158
|
+
|
|
159
|
+
def height(self) -> float:
|
|
160
|
+
"""Get height of frustum."""
|
|
161
|
+
return np.linalg.norm(self.c1 - self.c2).item()
|
|
162
|
+
|
|
163
|
+
def get_volume(self) -> float:
|
|
164
|
+
return self.calc_volume(self.r1, self.r2, self.height())
|
|
165
|
+
|
|
166
|
+
def get_intersect_volume_sphere(self, sphere: GeomSphere) -> float:
|
|
167
|
+
return calc_intersect_volume_sphere_frustum_cone(sphere, self)
|
|
168
|
+
|
|
169
|
+
def get_intersect_volume(self, obj: GeomObject) -> float:
|
|
170
|
+
if isinstance(obj, GeomSphere):
|
|
171
|
+
return self.get_intersect_volume_sphere(obj)
|
|
172
|
+
|
|
173
|
+
classname = obj.__class__.__name__
|
|
174
|
+
raise NotImplementedError(f"unsupported geometry object: {classname}")
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def calc_volume(r1: float, r2: float, height: float) -> float:
|
|
178
|
+
r"""Calculate volume of frustum.
|
|
179
|
+
|
|
180
|
+
\being{equation}
|
|
181
|
+
V = \frac{1}{3} * π * h * (r^2 + r * R + R^2)
|
|
182
|
+
\end{equation}
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
volume : float
|
|
187
|
+
Volume.
|
|
188
|
+
"""
|
|
189
|
+
return (1 / 3) * np.pi * height * (r1**2 + r1 * r2 + r2**2)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@lru_cache
|
|
193
|
+
def calc_intersect_volume_sphere_frustum_cone(
|
|
194
|
+
sphere: GeomSphere, frustum_cone: GeomFrustumCone
|
|
195
|
+
) -> float:
|
|
196
|
+
r"""Calculate intersect volume of sphere and frustum cone.
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
volume : float
|
|
201
|
+
Intersect volume.
|
|
202
|
+
"""
|
|
203
|
+
h = frustum_cone.height()
|
|
204
|
+
c1, r1 = sphere.center, sphere.radius
|
|
205
|
+
if np.allclose(c1, frustum_cone.c1) and np.allclose(r1, frustum_cone.r1):
|
|
206
|
+
c2, r2 = frustum_cone.c2, frustum_cone.r2
|
|
207
|
+
elif np.allclose(c1, frustum_cone.c2) and np.allclose(r1, frustum_cone.r2):
|
|
208
|
+
c2, r2 = frustum_cone.c1, frustum_cone.r1
|
|
209
|
+
else:
|
|
210
|
+
raise NotImplementedError("unsupported to calculate intersect volume")
|
|
211
|
+
|
|
212
|
+
# Fast-Path: The surface of the frustum concentric with the sphere
|
|
213
|
+
# is the surface with smaller radius
|
|
214
|
+
if r2 >= r1:
|
|
215
|
+
if h >= r1:
|
|
216
|
+
# The hemisphere is completely inside the frustum cone
|
|
217
|
+
return GeomSphere.calc_volume_spherical_cap(r1, r1)
|
|
218
|
+
|
|
219
|
+
# The frustum cone is lower than the hemisphere
|
|
220
|
+
v_himisphere = GeomSphere.calc_volume_spherical_cap(r1, r1)
|
|
221
|
+
v_cap = GeomSphere.calc_volume_spherical_cap(r1, r1 - h)
|
|
222
|
+
return v_himisphere - v_cap
|
|
223
|
+
|
|
224
|
+
up = (c2 - c1) / np.linalg.norm(c2 - c1)
|
|
225
|
+
v = _find_unit_vector_on_plane(up)
|
|
226
|
+
|
|
227
|
+
intersections = _find_sphere_line_intersection(c1, r1, c1 + r1 * v, c2 + r2 * v)
|
|
228
|
+
assert len(intersections) == 2
|
|
229
|
+
t, p = max(intersections, key=lambda x: x[0])
|
|
230
|
+
|
|
231
|
+
# Fast-Path: The frustum cone is completely inside the sphere
|
|
232
|
+
if t > 1 + eps:
|
|
233
|
+
return frustum_cone.get_volume()
|
|
234
|
+
|
|
235
|
+
M = _project_point_on_line(c1, up, p)
|
|
236
|
+
h1 = np.linalg.norm(M - c1).item()
|
|
237
|
+
r3 = np.linalg.norm(M - p).item()
|
|
238
|
+
v_cap1 = GeomSphere.calc_volume_spherical_cap(r1, r1 - h1)
|
|
239
|
+
v_frustum = GeomFrustumCone.calc_volume(r1, r3, h1)
|
|
240
|
+
|
|
241
|
+
# Fast-Path: The frustum cone is higher than the sphere
|
|
242
|
+
if h >= r1:
|
|
243
|
+
return v_cap1 + v_frustum
|
|
244
|
+
|
|
245
|
+
v_cap2 = GeomSphere.calc_volume_spherical_cap(r1, r1 - h)
|
|
246
|
+
return v_cap1 + v_frustum - v_cap2
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _find_unit_vector_on_plane(normal_vec3: npt.NDArray) -> npt.NDArray:
|
|
250
|
+
r = np.random.rand(3)
|
|
251
|
+
while np.allclose(r, normal_vec3) or np.allclose(r, -normal_vec3):
|
|
252
|
+
r = np.random.rand(3)
|
|
253
|
+
|
|
254
|
+
u = np.cross(r, normal_vec3)
|
|
255
|
+
unit_vector = u / np.linalg.norm(u)
|
|
256
|
+
return unit_vector
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _find_sphere_line_intersection(
|
|
260
|
+
sphere_center: npt.NDArray,
|
|
261
|
+
sphere_radius: float,
|
|
262
|
+
line_point_a: npt.NDArray,
|
|
263
|
+
line_point_b: npt.NDArray,
|
|
264
|
+
) -> List[Tuple[float, npt.NDArray[np.float64]]]:
|
|
265
|
+
x1, y1, z1 = sphere_center
|
|
266
|
+
x2, y2, z2 = line_point_a
|
|
267
|
+
x3, y3, z3 = line_point_b
|
|
268
|
+
t = symbols("t")
|
|
269
|
+
|
|
270
|
+
# line
|
|
271
|
+
x = x2 + t * (x3 - x2)
|
|
272
|
+
y = y2 + t * (y3 - y2)
|
|
273
|
+
z = z2 + t * (z3 - z2)
|
|
274
|
+
|
|
275
|
+
# sphere
|
|
276
|
+
sphere_eq = Eq((x - x1) ** 2 + (y - y1) ** 2 + (z - z1) ** 2, sphere_radius**2)
|
|
277
|
+
|
|
278
|
+
# solve
|
|
279
|
+
t_values = solve(sphere_eq, t)
|
|
280
|
+
intersections = [
|
|
281
|
+
np.array(
|
|
282
|
+
[float(x.subs(t, t_val)), float(y.subs(t, t_val)), float(z.subs(t, t_val))]
|
|
283
|
+
)
|
|
284
|
+
for t_val in t_values
|
|
285
|
+
]
|
|
286
|
+
return list(zip(t_values, intersections))
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _project_point_on_line(
|
|
290
|
+
point_a: npt.ArrayLike, direction_vector: npt.ArrayLike, point_p: npt.ArrayLike
|
|
291
|
+
) -> npt.NDArray:
|
|
292
|
+
A = np.array(point_a)
|
|
293
|
+
n = np.array(direction_vector)
|
|
294
|
+
P = np.array(point_p)
|
|
295
|
+
print(A.dtype, n.dtype, P.dtype)
|
|
296
|
+
|
|
297
|
+
AP = P - A
|
|
298
|
+
projection = A + np.dot(AP, n) / np.dot(n, n) * n
|
|
299
|
+
return projection
|
swcgeom/utils/neuromorpho.py
CHANGED
|
@@ -83,6 +83,8 @@ import os
|
|
|
83
83
|
import urllib.parse
|
|
84
84
|
from typing import Any, Callable, Dict, Iterable, List, Optional
|
|
85
85
|
|
|
86
|
+
from swcgeom.utils import FileReader
|
|
87
|
+
|
|
86
88
|
__all__ = [
|
|
87
89
|
"neuromorpho_is_valid",
|
|
88
90
|
"neuromorpho_convert_lmdb_to_swc",
|
|
@@ -95,14 +97,19 @@ URL_CNG_VERSION = (
|
|
|
95
97
|
)
|
|
96
98
|
API_NEURON_MAX_SIZE = 500
|
|
97
99
|
|
|
98
|
-
# about 1.1 GB and 18 GB in version 8.5.25 released in 2023-08-01
|
|
99
100
|
KB = 1024
|
|
100
101
|
MB = 1024 * KB
|
|
101
102
|
GB = 1024 * MB
|
|
103
|
+
|
|
104
|
+
# Test version: 8.5.25 (2023-08-01)
|
|
105
|
+
# About 1.1 GB and 18 GB
|
|
106
|
+
# No ETAs for future version
|
|
102
107
|
SIZE_METADATA = 2 * GB
|
|
103
108
|
SIZE_DATA = 20 * GB
|
|
104
109
|
|
|
105
110
|
# fmt:off
|
|
111
|
+
# Test version: 8.5.25 (2023-08-01)
|
|
112
|
+
# No ETAs for future version
|
|
106
113
|
invalid_ids = [
|
|
107
114
|
# bad file
|
|
108
115
|
81062, 86970, 79791,
|
|
@@ -132,6 +139,7 @@ def neuromorpho_convert_lmdb_to_swc(
|
|
|
132
139
|
*,
|
|
133
140
|
group_by: Optional[str | Callable[[Dict[str, Any]], str | None]] = None,
|
|
134
141
|
where: Optional[Callable[[Dict[str, Any]], bool]] = None,
|
|
142
|
+
encoding: str | None = "utf-8",
|
|
135
143
|
verbose: bool = False,
|
|
136
144
|
) -> None:
|
|
137
145
|
"""Convert lmdb format to SWCs.
|
|
@@ -147,6 +155,9 @@ def neuromorpho_convert_lmdb_to_swc(
|
|
|
147
155
|
attribute name for grouping, e.g.: `archive`, `species`.
|
|
148
156
|
where : (metadata: Dict[str, Any]) -> bool, optional
|
|
149
157
|
Filter neurons by metadata.
|
|
158
|
+
encoding : str | None, default to `utf-8`
|
|
159
|
+
Change swc encoding, part of the original data is not utf-8
|
|
160
|
+
encoded. If is None, keep the original encoding format.
|
|
150
161
|
verbose : bool, default False
|
|
151
162
|
Print verbose info.
|
|
152
163
|
|
|
@@ -165,7 +176,7 @@ def neuromorpho_convert_lmdb_to_swc(
|
|
|
165
176
|
See Also
|
|
166
177
|
--------
|
|
167
178
|
neuromorpho_is_valid :
|
|
168
|
-
|
|
179
|
+
Recommended filter function, try `where=neuromorpho_is_valid`
|
|
169
180
|
"""
|
|
170
181
|
import lmdb
|
|
171
182
|
from tqdm import tqdm
|
|
@@ -196,21 +207,31 @@ def neuromorpho_convert_lmdb_to_swc(
|
|
|
196
207
|
env_c = lmdb.Environment(os.path.join(root, "cng_version"), readonly=True)
|
|
197
208
|
with env_c.begin() as tx_c:
|
|
198
209
|
for k, grp in tqdm(items) if verbose else items:
|
|
210
|
+
kk = k.decode("utf-8")
|
|
199
211
|
try:
|
|
200
212
|
bs = tx_c.get(k)
|
|
201
213
|
if bs is None:
|
|
202
|
-
logging.warning("cng version of '%s' not exists",
|
|
214
|
+
logging.warning("cng version of '%s' not exists", kk)
|
|
203
215
|
continue
|
|
204
216
|
|
|
205
217
|
fs = (
|
|
206
|
-
os.path.join(dest, grp,
|
|
218
|
+
os.path.join(dest, grp, f"{kk}.swc")
|
|
207
219
|
if grp is not None
|
|
208
|
-
else os.path.join(dest,
|
|
220
|
+
else os.path.join(dest, f"{kk}.swc")
|
|
209
221
|
)
|
|
210
|
-
|
|
211
|
-
|
|
222
|
+
|
|
223
|
+
if encoding is None:
|
|
224
|
+
with open(fs, "wb") as f:
|
|
225
|
+
f.write(bs) # type: ignore
|
|
226
|
+
else:
|
|
227
|
+
bs = io.BytesIO(bs) # type: ignore
|
|
228
|
+
with (
|
|
229
|
+
open(fs, "w", encoding=encoding) as fw,
|
|
230
|
+
FileReader(bs, encoding="detect") as fr,
|
|
231
|
+
):
|
|
232
|
+
fw.writelines(fr.readlines())
|
|
212
233
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
213
|
-
logging.warning("fails to convert of %s, err: %s",
|
|
234
|
+
logging.warning("fails to convert of %s, err: %s", kk, e)
|
|
214
235
|
|
|
215
236
|
env_c.close()
|
|
216
237
|
|
|
@@ -432,16 +453,17 @@ if __name__ == "__main__":
|
|
|
432
453
|
sub.add_argument("--retry", type=int, default=3)
|
|
433
454
|
sub.add_argument("--proxy", type=str, default=None)
|
|
434
455
|
sub.add_argument("--verbose", type=bool, default=True)
|
|
435
|
-
sub.set_defaults(func=
|
|
456
|
+
sub.set_defaults(func=download_neuromorpho)
|
|
436
457
|
|
|
437
458
|
sub = subparsers.add_parser("convert")
|
|
438
459
|
sub.add_argument("-i", "--root", type=str, required=True)
|
|
439
460
|
sub.add_argument("-o", "--dest", type=str, default=None)
|
|
440
461
|
sub.add_argument("--group_by", type=str, default=None)
|
|
462
|
+
sub.add_argument("--encoding", type=str, default="utf-8")
|
|
441
463
|
sub.add_argument("--verbose", type=bool, default=True)
|
|
442
|
-
sub.set_defaults(func=
|
|
464
|
+
sub.set_defaults(func=neuromorpho_convert_lmdb_to_swc)
|
|
443
465
|
|
|
444
466
|
args = parser.parse_args()
|
|
445
467
|
func = args.func
|
|
446
468
|
del args.func # type: ignore
|
|
447
|
-
func(args)
|
|
469
|
+
func(**vars(args))
|
swcgeom/utils/renderer.py
CHANGED
|
@@ -9,12 +9,12 @@ import numpy.typing as npt
|
|
|
9
9
|
from matplotlib import cm
|
|
10
10
|
from matplotlib.axes import Axes
|
|
11
11
|
from matplotlib.collections import LineCollection, PatchCollection
|
|
12
|
-
from matplotlib.colors import Normalize
|
|
12
|
+
from matplotlib.colors import Colormap, Normalize
|
|
13
13
|
from matplotlib.figure import Figure
|
|
14
14
|
from matplotlib.patches import Circle
|
|
15
15
|
from typing_extensions import Self
|
|
16
16
|
|
|
17
|
-
from .transforms import (
|
|
17
|
+
from swcgeom.utils.transforms import (
|
|
18
18
|
Vec3f,
|
|
19
19
|
model_view_transformation,
|
|
20
20
|
orthographic_projection_simple,
|
|
@@ -208,14 +208,15 @@ def draw_circles(
|
|
|
208
208
|
*,
|
|
209
209
|
y_min: Optional[float] = None,
|
|
210
210
|
y_max: Optional[float] = None,
|
|
211
|
-
cmap: str = "viridis",
|
|
211
|
+
cmap: str | Colormap = "viridis",
|
|
212
212
|
) -> PatchCollection:
|
|
213
213
|
"""Draw a sequential of circles."""
|
|
214
|
+
|
|
214
215
|
y_min = y.min() if y_min is None else y_min
|
|
215
216
|
y_max = y.max() if y_max is None else y_max
|
|
216
217
|
norm = Normalize(y_min, y_max)
|
|
217
218
|
|
|
218
|
-
color_map = cm.get_cmap(name=cmap)
|
|
219
|
+
color_map = cmap if isinstance(cmap, Colormap) else cm.get_cmap(name=cmap)
|
|
219
220
|
colors = color_map(norm(y))
|
|
220
221
|
|
|
221
222
|
circles = [
|
swcgeom/utils/transforms.py
CHANGED
|
@@ -212,6 +212,31 @@ def to_homogeneous(xyz: npt.ArrayLike, w: float) -> npt.NDArray[np.float32]:
|
|
|
212
212
|
Parameters
|
|
213
213
|
----------
|
|
214
214
|
xyz : ArrayLike
|
|
215
|
+
Coordinate of shape (..., 3)
|
|
216
|
+
w : float
|
|
217
|
+
w of homogeneous coordinate, 1 for dot, 0 for vector.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
xyz4 : npt.NDArray[np.float32]
|
|
222
|
+
Array of shape (..., 4)
|
|
223
|
+
"""
|
|
224
|
+
xyz = np.array(xyz)
|
|
225
|
+
if xyz.ndim == 1:
|
|
226
|
+
return _to_homogeneous(xyz[None, ...], w)[0]
|
|
227
|
+
|
|
228
|
+
shape = xyz.shape[:-1]
|
|
229
|
+
xyz = xyz.reshape(-1, xyz.shape[-1])
|
|
230
|
+
xyz4 = _to_homogeneous(xyz, w).reshape(*shape, 4)
|
|
231
|
+
return xyz4
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _to_homogeneous(xyz: npt.NDArray, w: float) -> npt.NDArray[np.float32]:
|
|
235
|
+
"""Fill xyz to homogeneous coordinates.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
xyz : npt.NDArray
|
|
215
240
|
Coordinate of shape (N, 3)
|
|
216
241
|
w : float
|
|
217
242
|
w of homogeneous coordinate, 1 for dot, 0 for vector.
|
|
@@ -221,10 +246,10 @@ def to_homogeneous(xyz: npt.ArrayLike, w: float) -> npt.NDArray[np.float32]:
|
|
|
221
246
|
xyz4 : npt.NDArray[np.float32]
|
|
222
247
|
Array of shape (N, 4)
|
|
223
248
|
"""
|
|
224
|
-
xyz = np.array(xyz)
|
|
225
249
|
if xyz.shape[1] == 4:
|
|
226
250
|
return xyz
|
|
227
251
|
|
|
252
|
+
assert xyz.shape[1] == 3
|
|
228
253
|
filled = np.full((xyz.shape[0], 1), fill_value=w)
|
|
229
254
|
xyz4 = np.concatenate([xyz, filled], axis=1)
|
|
230
255
|
return xyz4
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: swcgeom
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.13.0
|
|
4
|
+
Summary: Neuron geometry library for swc format
|
|
5
5
|
Author-email: yzx9 <yuan.zx@outlook.com>
|
|
6
6
|
License: CC4.0 BY-NC-SA
|
|
7
7
|
Project-URL: repository, https://github.com/yzx9/swcgeom
|
|
8
|
-
Keywords: neuron,
|
|
8
|
+
Keywords: neuronscience,neuron,neuroanatomy,neuron-morphology
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: chardet >=5.2.0
|
|
13
12
|
Requires-Dist: imagecodecs >=2023.3.16
|
|
14
13
|
Requires-Dist: matplotlib >=3.5.2
|
|
15
14
|
Requires-Dist: numpy >=1.22.3
|
|
@@ -17,31 +16,32 @@ Requires-Dist: pandas >=1.4.2
|
|
|
17
16
|
Requires-Dist: pynrrd >=1.0.0
|
|
18
17
|
Requires-Dist: scipy >=1.9.1
|
|
19
18
|
Requires-Dist: seaborn >=0.12.0
|
|
19
|
+
Requires-Dist: sympy >=1.12
|
|
20
20
|
Requires-Dist: tifffile >=2022.8.12
|
|
21
21
|
Requires-Dist: typing-extensions >=4.4.0
|
|
22
|
+
Requires-Dist: v3d-py-helper-0.1.0
|
|
22
23
|
Provides-Extra: all
|
|
23
24
|
Requires-Dist: beautifulsoup4 >=4.11.1 ; extra == 'all'
|
|
24
25
|
Requires-Dist: certifi >=2023.5.7 ; extra == 'all'
|
|
26
|
+
Requires-Dist: chardet >=5.2.0 ; extra == 'all'
|
|
25
27
|
Requires-Dist: lmdb >=1.4.1 ; extra == 'all'
|
|
26
28
|
Requires-Dist: pycurl >=7.0.0 ; extra == 'all'
|
|
27
29
|
Requires-Dist: tqdm >=4.46.1 ; extra == 'all'
|
|
28
30
|
Requires-Dist: urllib3 >=1.26.0 ; extra == 'all'
|
|
29
31
|
|
|
30
|
-
#
|
|
32
|
+
# SWCGEOM
|
|
31
33
|
|
|
32
34
|
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a><br />
|
|
33
35
|
|
|
34
36
|
[](https://github.com/yzx9/swcgeom/releases)
|
|
35
|
-
|
|
36
37
|
[](https://pypi.org/project/swcgeom/)
|
|
37
|
-
|
|
38
38
|
[](https://test.pypi.org/project/swcgeom/)
|
|
39
39
|
|
|
40
40
|
A neuron geometry library for swc format.
|
|
41
41
|
|
|
42
42
|
## Usage
|
|
43
43
|
|
|
44
|
-
See
|
|
44
|
+
See examples for details.
|
|
45
45
|
|
|
46
46
|
## Development
|
|
47
47
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
swcgeom/__init__.py,sha256=z88Zwcjv-ii7c7dYd9QPg9XrUVorQjtrgGbQCsEnQhc,265
|
|
2
|
+
swcgeom/_version.py,sha256=Y4u7iBqF7QJAbpBNSFA2tk5t2mrMGrI-nonxUAhVkPU,413
|
|
3
|
+
swcgeom/analysis/__init__.py,sha256=c8X_oMxaOijEVjHexFseYs49WMalG_mSxmlQZr5CZeI,339
|
|
4
|
+
swcgeom/analysis/branch_features.py,sha256=s6PTMwwvxrVtZXRZlQbUSIw4M9-1IG63kf-Nxc0tMB0,1958
|
|
5
|
+
swcgeom/analysis/feature_extractor.py,sha256=coe07_bJau96BkimcnXzuf4KqjY5_QRLwqaumFsu_tQ,14031
|
|
6
|
+
swcgeom/analysis/node_features.py,sha256=fevnyrF-t4PX39ifLypiDW6EUWB8i-a3PpBnQZU3VOc,3407
|
|
7
|
+
swcgeom/analysis/path_features.py,sha256=iE21HBxAoGLxk_qK7MBwQhyUOBqNPcnk4urVHr9SVqk,889
|
|
8
|
+
swcgeom/analysis/sholl.py,sha256=9hSW8rZm1gvSIgcEZg8IVPT8kzBgBfwqbwP4E8R7L44,6390
|
|
9
|
+
swcgeom/analysis/trunk.py,sha256=L2tjUIUmrRQpah_W3ZETGWd16bDXJ5F8Sk2XBNGms0Q,5558
|
|
10
|
+
swcgeom/analysis/visualization.py,sha256=mKOpzTPkLpr1ggGL1MZPZRTG92GEg4idLT4eN5z5KOs,5654
|
|
11
|
+
swcgeom/analysis/volume.py,sha256=bMQ9N9Ow1-h27SLQ9AzUm9dyF_jhoTNBMNVqQWDcHMw,2103
|
|
12
|
+
swcgeom/core/__init__.py,sha256=ZUudZavxAIUU6Q0lBHrQ4ybmL5lBfvzyYsTtpuih9wg,332
|
|
13
|
+
swcgeom/core/branch.py,sha256=uuJCxaByRu-OdDZVWEffSFcmZWY-6ZWUhHN1M2Awj1s,3980
|
|
14
|
+
swcgeom/core/branch_tree.py,sha256=sN0viBVg5A4r9dMCkGNAaVttrdR4bEoZZBbHZFKdXj4,1892
|
|
15
|
+
swcgeom/core/node.py,sha256=HvfgsW4WU01hkRIPci8KF4bQMAkwtAxOGfUL4yUbuBs,3623
|
|
16
|
+
swcgeom/core/path.py,sha256=CsEelHiDR0JPBP1dTvoCSRvX3kBlZxkQilosnncV4nQ,4188
|
|
17
|
+
swcgeom/core/population.py,sha256=YZLAtkZKJeErOsE5MwhqieGjoMa7sWjFl5xxmXVPTco,8786
|
|
18
|
+
swcgeom/core/segment.py,sha256=yabRdFj7KlkJP4V67jAlCIRzpHduNnq3bRBIRMuANfA,3158
|
|
19
|
+
swcgeom/core/swc.py,sha256=lSYxAa25l6O8WZ9JtSSET-RZMr6EA1Tq_aXL_x0H9Rc,6795
|
|
20
|
+
swcgeom/core/tree.py,sha256=oAx31r2jFzxSW7vnM17m8sMVVI3_xzxRyDQh19MPTlQ,12126
|
|
21
|
+
swcgeom/core/tree_utils.py,sha256=xPy9b3MO43QR7VSvJvrwioXyQLGAPLotQjekn_-UiLg,7308
|
|
22
|
+
swcgeom/core/tree_utils_impl.py,sha256=5Cb63ziVVLADnkjfuq1T-ePw2TQQ5TKk4gcPZR6f_wY,1123
|
|
23
|
+
swcgeom/core/swc_utils/__init__.py,sha256=qghRxjtzvq5KKfN4HhvLpZNsGPfZQu-Jj2x62_5-TbQ,575
|
|
24
|
+
swcgeom/core/swc_utils/assembler.py,sha256=RoMJ3RjLC4O7mk62QxXVTQ5SUHagrFmpEw3nOnnqeJo,4563
|
|
25
|
+
swcgeom/core/swc_utils/base.py,sha256=huVxjuMLlTHbEb-KSEFDLgU0Ss3723t2Gr4Z_gQtl00,4737
|
|
26
|
+
swcgeom/core/swc_utils/checker.py,sha256=E72GtLZ_1IqQQ7aWQGs0dZ3Z609__bw3EYQqeWrk-EI,2657
|
|
27
|
+
swcgeom/core/swc_utils/io.py,sha256=6_--Qoe8kDja4PWsjwqRAvPJZNMFILFgauHaeWeGikU,6444
|
|
28
|
+
swcgeom/core/swc_utils/normalizer.py,sha256=_Ysi8bSJ2JBnIGB8o6BvAg2mcz6xuJp9rgNLZqpLuR8,5083
|
|
29
|
+
swcgeom/core/swc_utils/subtree.py,sha256=bd4XOLmRDfQSn_ktfQM3Hn8ONpCuZ_TdTWhE9-7QXW4,1999
|
|
30
|
+
swcgeom/images/__init__.py,sha256=QBP1ZGGo2nWAcV7Krz-vbvW_jN4ChqXrrpoScXcUURs,96
|
|
31
|
+
swcgeom/images/augmentation.py,sha256=v9zluYXmBEbafaDBTpvJovi4_KWJmHZZSvcYHzG0oWo,4099
|
|
32
|
+
swcgeom/images/folder.py,sha256=urh60ITreGgEwSbCbgexJFM8_-C9WLAQ9jUN50sox10,4034
|
|
33
|
+
swcgeom/images/io.py,sha256=VZWBq-_TMrKKdMaqpbZWZP6fCGIxWdhdIfb0ePcDy-4,20272
|
|
34
|
+
swcgeom/transforms/__init__.py,sha256=Mi2mOgkQ50JbZ9LmgXgJIuAA5eio67oe2AOs2CCCxTQ,463
|
|
35
|
+
swcgeom/transforms/base.py,sha256=22kkpQ11YCrH1SWWMHVqYjHp6cAlhslhdi1wKgAnT_s,3416
|
|
36
|
+
swcgeom/transforms/branch.py,sha256=mdrTC9T4zENS9DdwlFaoFHcWDuFAgKgiNicJZ1gLsOc,5445
|
|
37
|
+
swcgeom/transforms/geometry.py,sha256=zPmEf6ApJKzeZBbZ7qJYZXpY3-mrABN2dr7AUNqENiQ,6619
|
|
38
|
+
swcgeom/transforms/image_stack.py,sha256=DSIkJzK_SxLB4bvwYBt0ACW5pbB_NJLT8raq2tjOE6E,7235
|
|
39
|
+
swcgeom/transforms/images.py,sha256=tUl3dtqWfrhWR7WrOUc6LzWHnOzz3fwEgV3mMLRhn0c,908
|
|
40
|
+
swcgeom/transforms/mst.py,sha256=B6ZRqeEfCHLHFu0tdOsOgplbYHra4VPYjnneXMU-cNY,6413
|
|
41
|
+
swcgeom/transforms/path.py,sha256=Gk2iunGQMX7vE83bdo8xoDO-KAT1Vvep0iZs7oFLzFQ,1089
|
|
42
|
+
swcgeom/transforms/population.py,sha256=ZrKfMAMx4l729f-JLgw0dnGIPtPUoV0ZZoNNyA5cBw8,826
|
|
43
|
+
swcgeom/transforms/tree.py,sha256=Q5OnSti0ZeTNb-WpA_UZsDN7dhLN8YweEF_Siyrj66c,6420
|
|
44
|
+
swcgeom/transforms/tree_assembler.py,sha256=UZ9OUg1bQNecYIY_7ippg3S8gpuoi617ZUE0jg6BrQE,3177
|
|
45
|
+
swcgeom/utils/__init__.py,sha256=Ys04ObDfoNOX0mft2s4PGTpgelkdgwuM0qfeZRSx6VM,382
|
|
46
|
+
swcgeom/utils/debug.py,sha256=qay2qJpViLX82mzxdndxQFn-pi1vaEj9CbLGuGt8Y9k,465
|
|
47
|
+
swcgeom/utils/download.py,sha256=By2qZezo6h1Ke_4YpSIhDgcisOrpjVqRmNzbhynC2xs,2834
|
|
48
|
+
swcgeom/utils/dsu.py,sha256=3aCbtpnl_D0OXnowTS8-kuwnCS4BKBYL5ECiFQ1fUW8,1435
|
|
49
|
+
swcgeom/utils/ellipse.py,sha256=LB3q5CIy75GEUdTauIpKySwIHaDrwXzzkBhOCnjJ8Vw,3259
|
|
50
|
+
swcgeom/utils/file.py,sha256=1hchQDsPgn-i-Vz5OQtcogxav_ajCQ_OaEZCLmqczRg,2515
|
|
51
|
+
swcgeom/utils/geometry_object.py,sha256=y3Ikg0ywYQ6c_KKZOb0oq5ovHhuFiwI_VH-f4tAWZAI,8520
|
|
52
|
+
swcgeom/utils/neuromorpho.py,sha256=CDK2tUM2pNwHv_lEserHhQs_VlY3Rn557-jtV63EFlk,14420
|
|
53
|
+
swcgeom/utils/numpy_helper.py,sha256=A-F-eFdGktCHVAQ_HcXiFB3Y1YhhSNfAmtOl8483Dvo,1292
|
|
54
|
+
swcgeom/utils/renderer.py,sha256=xHVZ06Z1MeKBPC3nKzuwA1HryzR0ga79y6johZA4-q0,7290
|
|
55
|
+
swcgeom/utils/sdf.py,sha256=cmkHwdnh33u1gAXlzol5u9ZI3_SdMoMiY4pIkOgEcK0,5206
|
|
56
|
+
swcgeom/utils/transforms.py,sha256=PmP5fL_iVguq4GR2aqXhM0TeCsiFVnrPZMZG6zLohrE,6983
|
|
57
|
+
swcgeom-0.13.0.dist-info/LICENSE,sha256=aiTJ_1to1Xx6PaByy-pSXg42VYzE4FvF6GIt69WSDDI,202
|
|
58
|
+
swcgeom-0.13.0.dist-info/METADATA,sha256=vsgLGIGz97A-V8n1DZsAhGAc-uVJuX_CdFbB2BCQZ5g,2605
|
|
59
|
+
swcgeom-0.13.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
60
|
+
swcgeom-0.13.0.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
|
|
61
|
+
swcgeom-0.13.0.dist-info/RECORD,,
|
swcgeom-0.11.1.dist-info/RECORD
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
swcgeom/__init__.py,sha256=c526zEY5s-s7MQ7QSdBEImghkaEdT5RrjyTFY8pTUcs,238
|
|
2
|
-
swcgeom/_version.py,sha256=faVe-ZqiCxNFY2N3XG4067QpoPpTt1Bvjudys2n4bGY,162
|
|
3
|
-
swcgeom/analysis/__init__.py,sha256=CdaUgeokSLDrkQN3YuDpd0Ss-e9dqN9AG1mzuvYI66g,227
|
|
4
|
-
swcgeom/analysis/branch_features.py,sha256=sJPAMTXXId9xyM-s9TmlS24kK65qyZ6Lva4trP816gs,1952
|
|
5
|
-
swcgeom/analysis/feature_extractor.py,sha256=a0FwPw2l5Q5wUuNkgl_JBPsnx_Tw3Qa8QPECq3i3Q8c,13657
|
|
6
|
-
swcgeom/analysis/node_features.py,sha256=Gg5DvZUYaySBHP12Y4H29GnBB0cy8hBlyrjEglpFuco,3401
|
|
7
|
-
swcgeom/analysis/path_features.py,sha256=i9FHZtU98KnCrfpOp4NwVuXeqjzAzLQ9U__Jl5sdIow,883
|
|
8
|
-
swcgeom/analysis/sholl.py,sha256=xNhnbmZ9etcewRHJpPQ2bnt4OKF0oq7KkN7jRjbBcb0,6225
|
|
9
|
-
swcgeom/analysis/trunk.py,sha256=TONREUzvUY846aJg4U1YL_gwOrX3y_ZdSWmLS9fN6Fk,5568
|
|
10
|
-
swcgeom/analysis/visualization.py,sha256=JX-DvIq0zZph86Z8AJbFHkgRGL_jBlv6GolqMyEw7QE,5642
|
|
11
|
-
swcgeom/core/__init__.py,sha256=zGy4TORhn4ZAgRMqt50ZMSk9ywCCk3HL6HI7XUKQst4,225
|
|
12
|
-
swcgeom/core/branch.py,sha256=89DDg2SzY8Dl1UjlO9fqSZ_wTe5TE-Bgt1ERJ8vnMZM,3870
|
|
13
|
-
swcgeom/core/branch_tree.py,sha256=57yagwK8ME6KBVlz3rFUGVAD-oHzCkQIF5xRBc53yBM,1885
|
|
14
|
-
swcgeom/core/node.py,sha256=_fINcUeJIGNOy3puZZTHAfdS-aJa35Yugk9lHX8TA0Y,3547
|
|
15
|
-
swcgeom/core/path.py,sha256=j7e4d8hTGOjAEgHfdifjl53bN1ZG6HE6CJ_JAcIdlrc,4078
|
|
16
|
-
swcgeom/core/population.py,sha256=nCD4wXndPCCE3Ke14v8ys5f8vL1V-dYK3AWNjVokah4,8762
|
|
17
|
-
swcgeom/core/segment.py,sha256=vOUtAXEwslXgE1T1nYJnM6nOiLjIs9-s8usEM_NiXn8,3048
|
|
18
|
-
swcgeom/core/swc.py,sha256=trHtDiWADPFy8wabAdLYL0HTgYdyEB3RmKJmzfSxmGI,6462
|
|
19
|
-
swcgeom/core/tree.py,sha256=90sWEfkg6UkrVafEBqp63v6mwtxJJO_ZLeelM0bO1RM,10809
|
|
20
|
-
swcgeom/core/tree_utils.py,sha256=4iFCcltQRq2RHbarC1kbYxtXIf4GZsHbWTjku7BNr1I,6830
|
|
21
|
-
swcgeom/core/swc_utils/__init__.py,sha256=1QJgPaFf6oWP5teBy73BpYy1XrFLG7H-NZl7g1q5RiA,443
|
|
22
|
-
swcgeom/core/swc_utils/assembler.py,sha256=LwBb6UaaQ9b8JstF6lXosm-gIpwGAncgJL3JiQjn77s,4519
|
|
23
|
-
swcgeom/core/swc_utils/base.py,sha256=0vSsiVdoOsejqXGFmRBF5x8aR8S0C6hoaJjHpqc5vfc,4159
|
|
24
|
-
swcgeom/core/swc_utils/checker.py,sha256=8a_KuXmCGBZTLGA7BrDccsBtkFalUcqbcTIuugs80G4,2070
|
|
25
|
-
swcgeom/core/swc_utils/io.py,sha256=5qvJ-h3NNoHTbvG_iCzhlV7RClVb_LelYVvr4MbNtKA,6226
|
|
26
|
-
swcgeom/core/swc_utils/normalizer.py,sha256=ROnJa5Zdc2ibIxr59iv8IyjvAfm5utddrl4WYb5eorM,5061
|
|
27
|
-
swcgeom/core/swc_utils/subtree.py,sha256=Fbj0H_keA4j2HpvdAaUo41ydWDaV_EPPhPZ5SuzyeVE,1977
|
|
28
|
-
swcgeom/images/__init__.py,sha256=pagh9n_RiGQGQn2c-UpUtlrnSO5xhKERViqJpQQpcVU,68
|
|
29
|
-
swcgeom/images/augmentation.py,sha256=v9zluYXmBEbafaDBTpvJovi4_KWJmHZZSvcYHzG0oWo,4099
|
|
30
|
-
swcgeom/images/folder.py,sha256=FBmcNKQXwscjZ4JJeFpTNGVTIbF_QBlOOIY0Wiw4Jzc,4014
|
|
31
|
-
swcgeom/images/io.py,sha256=i9FFWs3gxDK-1rLPq_hherTtqCi6f0XZ8hqplnBRQhE,19266
|
|
32
|
-
swcgeom/transforms/__init__.py,sha256=Vt3Qext6EohfTXTNY8gg6LI9SaBfWnK9KFVQfLCA1yk,233
|
|
33
|
-
swcgeom/transforms/base.py,sha256=22kkpQ11YCrH1SWWMHVqYjHp6cAlhslhdi1wKgAnT_s,3416
|
|
34
|
-
swcgeom/transforms/branch.py,sha256=XVWs6STOsa2EnM9H28jLo3sWjBwrqEMlvGQ97cY2hUY,5415
|
|
35
|
-
swcgeom/transforms/geometry.py,sha256=3GCA2maAgmalD6HV7r8KRVMRnY71JYusgqJh49iNZZ4,6554
|
|
36
|
-
swcgeom/transforms/image_stack.py,sha256=0TKG_7aZGVlymkDhM8zLWBinJ3IuRxarqF-eETpSSr4,7205
|
|
37
|
-
swcgeom/transforms/images.py,sha256=bH2rAdHmzoQKERQ9AOj4yc-PmXOewTuVjjQVfdfqc78,890
|
|
38
|
-
swcgeom/transforms/mst.py,sha256=EFUVn2HBe5jbdpz0dEXjdFdaHHnzRmKwBJzwzECojzQ,4664
|
|
39
|
-
swcgeom/transforms/population.py,sha256=aVN0Gi-nrqOVZ7vpPME6lP7T8bI-tvBC-lorVG2ZOcE,802
|
|
40
|
-
swcgeom/transforms/tree.py,sha256=GH-cGmwBWGC4ARj_7TirCYfKVLu88i9kfWiVAhXefmI,5948
|
|
41
|
-
swcgeom/transforms/tree_assembler.py,sha256=NjDkY6dRayjcwnXrerqqW2u7Dmpkeu1dwIyOQboYzHo,3128
|
|
42
|
-
swcgeom/utils/__init__.py,sha256=7NhXMh5WbjyJr-_00eD1JnBMZTjcIcfopoxMCR7J4J4,175
|
|
43
|
-
swcgeom/utils/debug.py,sha256=qay2qJpViLX82mzxdndxQFn-pi1vaEj9CbLGuGt8Y9k,465
|
|
44
|
-
swcgeom/utils/download.py,sha256=By2qZezo6h1Ke_4YpSIhDgcisOrpjVqRmNzbhynC2xs,2834
|
|
45
|
-
swcgeom/utils/ellipse.py,sha256=LB3q5CIy75GEUdTauIpKySwIHaDrwXzzkBhOCnjJ8Vw,3259
|
|
46
|
-
swcgeom/utils/neuromorpho.py,sha256=eyGZzVpAZuScJeoeCjHRdQgPYUh-Fyl5TQI4Oa28uxw,13728
|
|
47
|
-
swcgeom/utils/numpy.py,sha256=A-F-eFdGktCHVAQ_HcXiFB3Y1YhhSNfAmtOl8483Dvo,1292
|
|
48
|
-
swcgeom/utils/renderer.py,sha256=WakzSNgU21th4yA5YobqNVOcF549YjsO8EtHhIdqGBA,7215
|
|
49
|
-
swcgeom/utils/sdf.py,sha256=cmkHwdnh33u1gAXlzol5u9ZI3_SdMoMiY4pIkOgEcK0,5206
|
|
50
|
-
swcgeom/utils/transforms.py,sha256=4U6flX8lfnMoAAR-uc4uSYjSd2vg-AOWGLgZ6_RVxJU,6352
|
|
51
|
-
swcgeom-0.11.1.dist-info/LICENSE,sha256=aiTJ_1to1Xx6PaByy-pSXg42VYzE4FvF6GIt69WSDDI,202
|
|
52
|
-
swcgeom-0.11.1.dist-info/METADATA,sha256=WbJ_C4EWS72SIDQzpa-hy5It3EyKNUee8YyI_31cNmw,2495
|
|
53
|
-
swcgeom-0.11.1.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92
|
|
54
|
-
swcgeom-0.11.1.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
|
|
55
|
-
swcgeom-0.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|