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.

Files changed (56) hide show
  1. swcgeom/__init__.py +4 -4
  2. swcgeom/_version.py +14 -2
  3. swcgeom/analysis/__init__.py +7 -7
  4. swcgeom/analysis/branch_features.py +1 -1
  5. swcgeom/analysis/feature_extractor.py +25 -12
  6. swcgeom/analysis/node_features.py +1 -1
  7. swcgeom/analysis/path_features.py +1 -1
  8. swcgeom/analysis/sholl.py +11 -7
  9. swcgeom/analysis/trunk.py +5 -5
  10. swcgeom/analysis/visualization.py +2 -2
  11. swcgeom/analysis/volume.py +80 -0
  12. swcgeom/core/__init__.py +9 -9
  13. swcgeom/core/branch.py +8 -4
  14. swcgeom/core/branch_tree.py +4 -5
  15. swcgeom/core/node.py +5 -3
  16. swcgeom/core/path.py +6 -3
  17. swcgeom/core/population.py +2 -2
  18. swcgeom/core/segment.py +8 -4
  19. swcgeom/core/swc.py +24 -3
  20. swcgeom/core/swc_utils/__init__.py +6 -6
  21. swcgeom/core/swc_utils/assembler.py +2 -2
  22. swcgeom/core/swc_utils/base.py +30 -1
  23. swcgeom/core/swc_utils/checker.py +30 -6
  24. swcgeom/core/swc_utils/io.py +31 -30
  25. swcgeom/core/swc_utils/normalizer.py +1 -1
  26. swcgeom/core/swc_utils/subtree.py +1 -1
  27. swcgeom/core/tree.py +38 -14
  28. swcgeom/core/tree_utils.py +47 -41
  29. swcgeom/core/tree_utils_impl.py +39 -0
  30. swcgeom/images/__init__.py +2 -2
  31. swcgeom/images/folder.py +2 -2
  32. swcgeom/images/io.py +48 -9
  33. swcgeom/transforms/__init__.py +10 -8
  34. swcgeom/transforms/branch.py +3 -3
  35. swcgeom/transforms/geometry.py +11 -4
  36. swcgeom/transforms/image_stack.py +3 -3
  37. swcgeom/transforms/images.py +1 -1
  38. swcgeom/transforms/mst.py +68 -13
  39. swcgeom/transforms/path.py +48 -0
  40. swcgeom/transforms/population.py +2 -2
  41. swcgeom/transforms/tree.py +18 -9
  42. swcgeom/transforms/tree_assembler.py +7 -4
  43. swcgeom/utils/__init__.py +10 -7
  44. swcgeom/utils/dsu.py +42 -0
  45. swcgeom/utils/file.py +91 -0
  46. swcgeom/utils/geometry_object.py +299 -0
  47. swcgeom/utils/neuromorpho.py +33 -11
  48. swcgeom/utils/renderer.py +5 -4
  49. swcgeom/utils/transforms.py +26 -1
  50. {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/METADATA +8 -8
  51. swcgeom-0.13.0.dist-info/RECORD +61 -0
  52. {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/WHEEL +1 -1
  53. swcgeom-0.11.1.dist-info/RECORD +0 -55
  54. /swcgeom/utils/{numpy.py → numpy_helper.py} +0 -0
  55. {swcgeom-0.11.1.dist-info → swcgeom-0.13.0.dist-info}/LICENSE +0 -0
  56. {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
@@ -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
- Recommend filter function, use `where=neuromorpho_is_valid`
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", k.decode("utf-8"))
214
+ logging.warning("cng version of '%s' not exists", kk)
203
215
  continue
204
216
 
205
217
  fs = (
206
- os.path.join(dest, grp, k.decode("utf-8") + ".swc")
218
+ os.path.join(dest, grp, f"{kk}.swc")
207
219
  if grp is not None
208
- else os.path.join(dest, k.decode("utf-8") + ".swc")
220
+ else os.path.join(dest, f"{kk}.swc")
209
221
  )
210
- with open(fs, "wb") as f:
211
- f.write(bs) # type: ignore
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", k.decode("utf-8"), e)
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=lambda args: download_neuromorpho(**vars(args)))
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=lambda args: neuromorpho_convert_lmdb_to_swc(**vars(args)))
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 = [
@@ -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.11.1
4
- Summary: A neuron geometry library for swc format
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,swc,geom
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
- # SWC GEOM
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
  [![Release to GitHub](https://github.com/yzx9/swcgeom/actions/workflows/github-publish.yml/badge.svg)](https://github.com/yzx9/swcgeom/releases)
35
-
36
37
  [![Release to PyPI](https://github.com/yzx9/swcgeom/actions/workflows/pypi-publish.yml/badge.svg)](https://pypi.org/project/swcgeom/)
37
-
38
38
  [![Release to Test PyPI](https://github.com/yzx9/swcgeom/actions/workflows/test-pypi-publish.yml/badge.svg)](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 documents for details.
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.1)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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