swcgeom 0.14.0__tar.gz → 0.15.0__tar.gz
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-0.14.0 → swcgeom-0.15.0}/.gitignore +1 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/CHANGELOG.md +33 -0
- {swcgeom-0.14.0/swcgeom.egg-info → swcgeom-0.15.0}/PKG-INFO +1 -1
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/ImageStack.ipynb +7 -1
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/_version.py +2 -2
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/branch_tree.py +2 -3
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/population.py +6 -7
- swcgeom-0.15.0/swcgeom/core/swc_utils/assembler.py +26 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/subtree.py +2 -2
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/tree.py +19 -12
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/tree_utils.py +23 -5
- swcgeom-0.15.0/swcgeom/core/tree_utils_impl.py +55 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/images/folder.py +42 -17
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/images/io.py +65 -36
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/base.py +41 -21
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/branch.py +5 -5
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/geometry.py +42 -18
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/image_stack.py +49 -28
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/images.py +2 -2
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/mst.py +5 -13
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/population.py +2 -2
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/tree.py +7 -13
- swcgeom-0.15.0/swcgeom/transforms/tree_assembler.py +173 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0/swcgeom.egg-info}/PKG-INFO +1 -1
- swcgeom-0.14.0/swcgeom/core/swc_utils/assembler.py +0 -155
- swcgeom-0.14.0/swcgeom/core/tree_utils_impl.py +0 -39
- swcgeom-0.14.0/swcgeom/transforms/tree_assembler.py +0 -107
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.github/workflows/build.yml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.github/workflows/github-publish.yml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.github/workflows/pypi-publish.yml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.github/workflows/test.yml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.pylintrc +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/.vscode/settings.json +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/LICENSE +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/README.md +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/Branch.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/BranchTree.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/CollectTips.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/CutTree.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/Features.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/GeometryTransform.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/MST.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/SpectralClustering.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/Tree.ipynb +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/data/101711-10_4p5-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/data/101711-11_16-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/data/1059283677_15257_2226-X16029-Y23953.swc +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/data/toydata.swc +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/dgl/graph.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/pytorch/branch.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/pytorch/branch_dataset.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/examples/pytorch/tree_folder_dataset.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/git-conventional-commits.yaml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/pyproject.toml +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/setup.cfg +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/branch_features.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/feature_extractor.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/node_features.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/path_features.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/sholl.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/trunk.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/visualization.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/analysis/volume.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/branch.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/node.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/path.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/segment.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/base.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/checker.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/io.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/core/swc_utils/normalizer.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/images/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/images/augmentation.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/transforms/path.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/debug.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/download.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/dsu.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/ellipse.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/file.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/neuromorpho.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/numpy_helper.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/renderer.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/sdf.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/solid_geometry.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/transforms.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom/utils/volumetric_object.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom.egg-info/SOURCES.txt +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom.egg-info/dependency_links.txt +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom.egg-info/requires.txt +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/swcgeom.egg-info/top_level.txt +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/analysis/test_volume.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/__init__.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/test_dsu.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/test_sdf.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/test_solid_geometry.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/test_transforms.py +0 -0
- {swcgeom-0.14.0 → swcgeom-0.15.0}/tests/utils/test_volumetric_object.py +0 -0
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## **0.15.0** <sub><sup>2024-01-28 ([d300b41...8c9e0ab](https://github.com/yzx9/swcgeom/compare/d300b41689ee892fe5a23dad57f92e1054c0f9e9...8c9e0ab45c447d3d8121b0cc4178be5f65d18a56?diff=split))</sup></sub>
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
##### `core`
|
|
8
|
+
|
|
9
|
+
- expose subtree id mapping ([35738a3](https://github.com/yzx9/swcgeom/commit/35738a31b2373a43ab29550335a457c51b7befce))
|
|
10
|
+
|
|
11
|
+
##### `images`
|
|
12
|
+
|
|
13
|
+
- add dtype support ([d300b41](https://github.com/yzx9/swcgeom/commit/d300b41689ee892fe5a23dad57f92e1054c0f9e9))
|
|
14
|
+
|
|
15
|
+
##### `transforms`
|
|
16
|
+
|
|
17
|
+
- generate image stack patch ([decd3a1](https://github.com/yzx9/swcgeom/commit/decd3a1ee63e1657aafa24ec5dfbf7439d25a551))
|
|
18
|
+
- add \`extra\_repr\` api ([ca19361](https://github.com/yzx9/swcgeom/commit/ca1936146783ebccc2ffe25596799580e89b3432))
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
##### `core`
|
|
23
|
+
|
|
24
|
+
- fix classmethod typing annotations ([0123eeb](https://github.com/yzx9/swcgeom/commit/0123eeb46142c3c3a695a5cf2495588a2effe275))
|
|
25
|
+
|
|
26
|
+
##### `transforms`
|
|
27
|
+
|
|
28
|
+
- should stack images ([8c9e0ab](https://github.com/yzx9/swcgeom/commit/8c9e0ab45c447d3d8121b0cc4178be5f65d18a56))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### BREAKING CHANGES
|
|
32
|
+
- `core` remove assembler module ([414533d](https://github.com/yzx9/swcgeom/commit/414533d17238ed45972dff1909cf025a19b7fa1e))
|
|
33
|
+
<br>
|
|
34
|
+
|
|
35
|
+
|
|
3
36
|
## **0.14.0** <sub><sup>2023-12-26 ([b9c95f5...cc22018](https://github.com/yzx9/swcgeom/compare/b9c95f58f725490f3b624233cc0be20b31605e53...cc22018ad79af18ecd812b932812a0ff40a0fe40?diff=split))</sup></sub>
|
|
4
37
|
|
|
5
38
|
### Features
|
|
@@ -49,6 +49,12 @@
|
|
|
49
49
|
"outputs": [],
|
|
50
50
|
"source": [
|
|
51
51
|
"trans = ToImageStack(resolution=[1, 1, 1])\n",
|
|
52
|
+
"\n",
|
|
53
|
+
"# load entire image stack into memory, should be avoided for large images\n",
|
|
54
|
+
"x = trans(tree1)\n",
|
|
55
|
+
"print(x.dtype, x.shape)\n",
|
|
56
|
+
"\n",
|
|
57
|
+
"# save image stack to file\n",
|
|
52
58
|
"trans.transform_and_save(\"test.tif\", tree1)"
|
|
53
59
|
]
|
|
54
60
|
}
|
|
@@ -69,7 +75,7 @@
|
|
|
69
75
|
"name": "python",
|
|
70
76
|
"nbconvert_exporter": "python",
|
|
71
77
|
"pygments_lexer": "ipython3",
|
|
72
|
-
"version": "3.
|
|
78
|
+
"version": "3.12.1"
|
|
73
79
|
},
|
|
74
80
|
"orig_nbformat": 4,
|
|
75
81
|
"vscode": {
|
|
@@ -5,7 +5,6 @@ from typing import Dict, List
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pandas as pd
|
|
8
|
-
from typing_extensions import Self
|
|
9
8
|
|
|
10
9
|
from swcgeom.core.branch import Branch
|
|
11
10
|
from swcgeom.core.swc_utils import to_sub_topology
|
|
@@ -31,7 +30,7 @@ class BranchTree(Tree):
|
|
|
31
30
|
return self.branches[idx]
|
|
32
31
|
|
|
33
32
|
@classmethod
|
|
34
|
-
def from_tree(cls, tree: Tree) ->
|
|
33
|
+
def from_tree(cls, tree: Tree) -> "BranchTree":
|
|
35
34
|
"""Generating a branch tree from tree."""
|
|
36
35
|
|
|
37
36
|
branches = tree.get_branches()
|
|
@@ -56,6 +55,6 @@ class BranchTree(Tree):
|
|
|
56
55
|
return branch_tree
|
|
57
56
|
|
|
58
57
|
@classmethod
|
|
59
|
-
def from_data_frame(cls, df: pd.DataFrame, *args, **kwargs) ->
|
|
58
|
+
def from_data_frame(cls, df: pd.DataFrame, *args, **kwargs) -> "BranchTree":
|
|
60
59
|
tree = super().from_data_frame(df, *args, **kwargs)
|
|
61
60
|
return cls.from_tree(tree)
|
|
@@ -17,7 +17,6 @@ from typing import (
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
import numpy.typing as npt
|
|
20
|
-
from typing_extensions import Self
|
|
21
20
|
|
|
22
21
|
from swcgeom.core.swc import eswc_cols
|
|
23
22
|
from swcgeom.core.tree import Tree
|
|
@@ -155,7 +154,7 @@ class Population:
|
|
|
155
154
|
return f"Neuron population in '{self.root}'"
|
|
156
155
|
|
|
157
156
|
@classmethod
|
|
158
|
-
def from_swc(cls, root: str, ext: str = ".swc", **kwargs) ->
|
|
157
|
+
def from_swc(cls, root: str, ext: str = ".swc", **kwargs) -> "Population":
|
|
159
158
|
if not os.path.exists(root):
|
|
160
159
|
raise FileNotFoundError(
|
|
161
160
|
f"the root does not refers to an existing directory: {root}"
|
|
@@ -171,7 +170,7 @@ class Population:
|
|
|
171
170
|
ext: str = ".eswc",
|
|
172
171
|
extra_cols: Optional[Iterable[str]] = None,
|
|
173
172
|
**kwargs,
|
|
174
|
-
) ->
|
|
173
|
+
) -> "Population":
|
|
175
174
|
extra_cols = list(extra_cols) if extra_cols is not None else []
|
|
176
175
|
extra_cols.extend(k for k, t in eswc_cols)
|
|
177
176
|
return cls.from_swc(root, ext, extra_cols=extra_cols, **kwargs)
|
|
@@ -244,7 +243,7 @@ class Populations:
|
|
|
244
243
|
check_same: bool = False,
|
|
245
244
|
labels: Optional[Iterable[str]] = None,
|
|
246
245
|
**kwargs,
|
|
247
|
-
) ->
|
|
246
|
+
) -> "Populations":
|
|
248
247
|
"""Get population from dirs.
|
|
249
248
|
|
|
250
249
|
Parameters
|
|
@@ -268,7 +267,7 @@ class Populations:
|
|
|
268
267
|
|
|
269
268
|
fs = [inter for _ in roots]
|
|
270
269
|
elif check_same:
|
|
271
|
-
assert
|
|
270
|
+
assert [fs[0] == a for a in fs[1:]], "not the same among populations"
|
|
272
271
|
|
|
273
272
|
populations = [
|
|
274
273
|
Population(
|
|
@@ -276,7 +275,7 @@ class Populations:
|
|
|
276
275
|
)
|
|
277
276
|
for i, d in enumerate(roots)
|
|
278
277
|
]
|
|
279
|
-
return
|
|
278
|
+
return Populations(populations, labels=labels)
|
|
280
279
|
|
|
281
280
|
@classmethod
|
|
282
281
|
def from_eswc(
|
|
@@ -286,7 +285,7 @@ class Populations:
|
|
|
286
285
|
*,
|
|
287
286
|
ext: str = ".eswc",
|
|
288
287
|
**kwargs,
|
|
289
|
-
) ->
|
|
288
|
+
) -> "Populations":
|
|
290
289
|
extra_cols = list(extra_cols) if extra_cols is not None else []
|
|
291
290
|
extra_cols.extend(k for k, t in eswc_cols)
|
|
292
291
|
return cls.from_swc(roots, extra_cols=extra_cols, ext=ext, **kwargs)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Assemble lines to swc.
|
|
2
|
+
|
|
3
|
+
Notes
|
|
4
|
+
-----
|
|
5
|
+
This module is deprecated, please use `~.transforms.LinesToTree`
|
|
6
|
+
instead.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = ["assemble_lines", "try_assemble_lines"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def assemble_lines(*args, **kwargs):
|
|
14
|
+
raise DeprecationWarning(
|
|
15
|
+
"`assemble_lines` has been replaced by "
|
|
16
|
+
"`~.transforms.LinesToTree` because it can be easy assemble "
|
|
17
|
+
"with other tansformations.",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def try_assemble_lines(*args, **kwargs):
|
|
22
|
+
raise DeprecationWarning(
|
|
23
|
+
"`try_assemble_lines` has been replaced by "
|
|
24
|
+
"`~.transforms.LinesToTree` because it can be easy assemble "
|
|
25
|
+
"with other tansformations.",
|
|
26
|
+
)
|
|
@@ -27,8 +27,8 @@ def to_sub_topology(sub: Topology) -> Tuple[Topology, npt.NDArray[np.int32]]:
|
|
|
27
27
|
Returns
|
|
28
28
|
-------
|
|
29
29
|
sub_topology : Topology
|
|
30
|
-
|
|
31
|
-
Map from new id to
|
|
30
|
+
mapping : List of int
|
|
31
|
+
Map from new id to old id.
|
|
32
32
|
|
|
33
33
|
See Also
|
|
34
34
|
--------
|
|
@@ -20,7 +20,6 @@ from typing import (
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
import numpy.typing as npt
|
|
22
22
|
import pandas as pd
|
|
23
|
-
from typing_extensions import Self
|
|
24
23
|
|
|
25
24
|
from swcgeom.core.branch import Branch
|
|
26
25
|
from swcgeom.core.node import Node
|
|
@@ -28,7 +27,7 @@ from swcgeom.core.path import Path
|
|
|
28
27
|
from swcgeom.core.segment import Segment, Segments
|
|
29
28
|
from swcgeom.core.swc import DictSWC, eswc_cols
|
|
30
29
|
from swcgeom.core.swc_utils import SWCNames, get_names, read_swc, traverse
|
|
31
|
-
from swcgeom.core.tree_utils_impl import get_subtree_impl
|
|
30
|
+
from swcgeom.core.tree_utils_impl import Mapping, get_subtree_impl
|
|
32
31
|
from swcgeom.utils import PathOrIO, padding1d
|
|
33
32
|
|
|
34
33
|
__all__ = ["Tree"]
|
|
@@ -73,9 +72,18 @@ class Tree(DictSWC):
|
|
|
73
72
|
"""The end-to-end straight-line distance to soma."""
|
|
74
73
|
return self.distance(self.attach.soma())
|
|
75
74
|
|
|
76
|
-
def subtree(self) -> "Tree":
|
|
77
|
-
"""Get subtree from node.
|
|
78
|
-
|
|
75
|
+
def subtree(self, *, out_mapping: Optional[Mapping] = None) -> "Tree":
|
|
76
|
+
"""Get subtree from node.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
out_mapping : List of int or Dict[int, int], optional
|
|
81
|
+
Map from new id to old id.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
n_nodes, ndata, source, names = get_subtree_impl(
|
|
85
|
+
self.attach, self.id, out_mapping=out_mapping
|
|
86
|
+
)
|
|
79
87
|
return Tree(n_nodes, **ndata, source=source, names=names)
|
|
80
88
|
|
|
81
89
|
def is_root(self) -> bool:
|
|
@@ -260,11 +268,11 @@ class Tree(DictSWC):
|
|
|
260
268
|
paths = self.traverse(enter=assign_path, leave=collect_path)
|
|
261
269
|
return [self.Path(self, idx) for idx in paths]
|
|
262
270
|
|
|
263
|
-
def get_neurites(self, type_check: bool = True) -> Iterable[
|
|
271
|
+
def get_neurites(self, type_check: bool = True) -> Iterable["Tree"]:
|
|
264
272
|
"""Get neurites from soma."""
|
|
265
273
|
return (n.subtree() for n in self.soma(type_check).children())
|
|
266
274
|
|
|
267
|
-
def get_dendrites(self, type_check: bool = True) -> Iterable[
|
|
275
|
+
def get_dendrites(self, type_check: bool = True) -> Iterable["Tree"]:
|
|
268
276
|
"""Get dendrites."""
|
|
269
277
|
types = [self.types.apical_dendrite, self.types.basal_dendrite]
|
|
270
278
|
children = self.soma(type_check).children()
|
|
@@ -312,15 +320,14 @@ class Tree(DictSWC):
|
|
|
312
320
|
"""Get length of tree."""
|
|
313
321
|
return sum(s.length() for s in self.get_segments())
|
|
314
322
|
|
|
315
|
-
@
|
|
323
|
+
@staticmethod
|
|
316
324
|
def from_data_frame(
|
|
317
|
-
cls,
|
|
318
325
|
df: pd.DataFrame,
|
|
319
326
|
source: str = "",
|
|
320
327
|
*,
|
|
321
328
|
comments: Optional[Iterable[str]] = None,
|
|
322
329
|
names: Optional[SWCNames] = None,
|
|
323
|
-
) ->
|
|
330
|
+
) -> "Tree":
|
|
324
331
|
"""Read neuron tree from data frame."""
|
|
325
332
|
names = get_names(names)
|
|
326
333
|
tree = Tree(
|
|
@@ -333,7 +340,7 @@ class Tree(DictSWC):
|
|
|
333
340
|
return tree
|
|
334
341
|
|
|
335
342
|
@classmethod
|
|
336
|
-
def from_swc(cls, swc_file: PathOrIO, **kwargs) ->
|
|
343
|
+
def from_swc(cls, swc_file: PathOrIO, **kwargs) -> "Tree":
|
|
337
344
|
"""Read neuron tree from swc file.
|
|
338
345
|
|
|
339
346
|
See Also
|
|
@@ -352,7 +359,7 @@ class Tree(DictSWC):
|
|
|
352
359
|
@classmethod
|
|
353
360
|
def from_eswc(
|
|
354
361
|
cls, swc_file: str, extra_cols: Optional[List[str]] = None, **kwargs
|
|
355
|
-
) ->
|
|
362
|
+
) -> "Tree":
|
|
356
363
|
"""Read neuron tree from eswc file.
|
|
357
364
|
|
|
358
365
|
See Also
|
|
@@ -17,7 +17,7 @@ from swcgeom.core.swc_utils import (
|
|
|
17
17
|
to_sub_topology,
|
|
18
18
|
)
|
|
19
19
|
from swcgeom.core.tree import Tree
|
|
20
|
-
from swcgeom.core.tree_utils_impl import get_subtree_impl, to_subtree_impl
|
|
20
|
+
from swcgeom.core.tree_utils_impl import Mapping, get_subtree_impl, to_subtree_impl
|
|
21
21
|
|
|
22
22
|
__all__ = [
|
|
23
23
|
"sort_tree",
|
|
@@ -128,7 +128,12 @@ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> Tuple[Tree, Dict[int, int]]
|
|
|
128
128
|
return subtree, id_map
|
|
129
129
|
|
|
130
130
|
|
|
131
|
-
def to_subtree(
|
|
131
|
+
def to_subtree(
|
|
132
|
+
swc_like: SWCLike,
|
|
133
|
+
removals: Iterable[int],
|
|
134
|
+
*,
|
|
135
|
+
out_mapping: Optional[Mapping] = None,
|
|
136
|
+
) -> Tree:
|
|
132
137
|
"""Create subtree from origin tree.
|
|
133
138
|
|
|
134
139
|
Parameters
|
|
@@ -136,17 +141,24 @@ def to_subtree(swc_like: SWCLike, removals: Iterable[int]) -> Tree:
|
|
|
136
141
|
swc_like : SWCLike
|
|
137
142
|
removals : List of int
|
|
138
143
|
A list of id of nodes to be removed.
|
|
144
|
+
out_mapping: List of int or Dict[int, int], optional
|
|
145
|
+
Map new id to old id.
|
|
139
146
|
"""
|
|
147
|
+
|
|
140
148
|
new_ids = swc_like.id().copy()
|
|
141
149
|
for i in removals:
|
|
142
150
|
new_ids[i] = REMOVAL
|
|
143
151
|
|
|
144
152
|
sub = propagate_removal((new_ids, swc_like.pid()))
|
|
145
|
-
n_nodes, ndata, source, names = to_subtree_impl(
|
|
153
|
+
n_nodes, ndata, source, names = to_subtree_impl(
|
|
154
|
+
swc_like, sub, out_mapping=out_mapping
|
|
155
|
+
)
|
|
146
156
|
return Tree(n_nodes, **ndata, source=source, names=names)
|
|
147
157
|
|
|
148
158
|
|
|
149
|
-
def get_subtree(
|
|
159
|
+
def get_subtree(
|
|
160
|
+
swc_like: SWCLike, n: int, *, out_mapping: Optional[Mapping] = None
|
|
161
|
+
) -> Tree:
|
|
150
162
|
"""Get subtree rooted at n.
|
|
151
163
|
|
|
152
164
|
Parameters
|
|
@@ -154,8 +166,13 @@ def get_subtree(swc_like: SWCLike, n: int) -> Tree:
|
|
|
154
166
|
swc_like : SWCLike
|
|
155
167
|
n : int
|
|
156
168
|
Id of the root of the subtree.
|
|
169
|
+
out_mapping: List of int or Dict[int, int], optional
|
|
170
|
+
Map new id to old id.
|
|
157
171
|
"""
|
|
158
|
-
|
|
172
|
+
|
|
173
|
+
n_nodes, ndata, source, names = get_subtree_impl(
|
|
174
|
+
swc_like, n, out_mapping=out_mapping
|
|
175
|
+
)
|
|
159
176
|
return Tree(n_nodes, **ndata, source=source, names=names)
|
|
160
177
|
|
|
161
178
|
|
|
@@ -171,6 +188,7 @@ def redirect_tree(tree: Tree, new_root: int, sort: bool = True) -> Tree:
|
|
|
171
188
|
sort : bool, default `True`
|
|
172
189
|
If true, sort indices of nodes after redirect.
|
|
173
190
|
"""
|
|
191
|
+
|
|
174
192
|
tree = tree.copy()
|
|
175
193
|
path = [tree.node(new_root)]
|
|
176
194
|
while (p := path[-1].parent()) is not None:
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""SWC util wrapper for tree, split to avoid circle imports.
|
|
2
|
+
|
|
3
|
+
Notes
|
|
4
|
+
-----
|
|
5
|
+
Do not import `Tree` and keep this file minimized.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
from swcgeom.core.swc import SWCLike, SWCNames
|
|
14
|
+
from swcgeom.core.swc_utils import Topology, to_sub_topology, traverse
|
|
15
|
+
|
|
16
|
+
__all__ = ["get_subtree_impl", "to_subtree_impl"]
|
|
17
|
+
|
|
18
|
+
Mapping = Dict[int, int] | List[int]
|
|
19
|
+
TreeArgs = Tuple[int, Dict[str, npt.NDArray[Any]], str, SWCNames]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_subtree_impl(
|
|
23
|
+
swc_like: SWCLike, n: int, *, out_mapping: Optional[Mapping] = None
|
|
24
|
+
) -> TreeArgs:
|
|
25
|
+
ids = []
|
|
26
|
+
topo = (swc_like.id(), swc_like.pid())
|
|
27
|
+
traverse(topo, enter=lambda n, _: ids.append(n), root=n)
|
|
28
|
+
|
|
29
|
+
sub_ids = np.array(ids, dtype=np.int32)
|
|
30
|
+
sub_pid = swc_like.pid()[sub_ids]
|
|
31
|
+
sub_pid[0] = -1
|
|
32
|
+
return to_subtree_impl(swc_like, (sub_ids, sub_pid), out_mapping=out_mapping)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def to_subtree_impl(
|
|
36
|
+
swc_like: SWCLike,
|
|
37
|
+
sub: Topology,
|
|
38
|
+
*,
|
|
39
|
+
out_mapping: Optional[Mapping] = None,
|
|
40
|
+
) -> TreeArgs:
|
|
41
|
+
(new_id, new_pid), mapping = to_sub_topology(sub)
|
|
42
|
+
|
|
43
|
+
n_nodes = new_id.shape[0]
|
|
44
|
+
ndata = {k: swc_like.get_ndata(k)[mapping].copy() for k in swc_like.keys()}
|
|
45
|
+
ndata.update(id=new_id, pid=new_pid)
|
|
46
|
+
|
|
47
|
+
if isinstance(out_mapping, list):
|
|
48
|
+
out_mapping.clear()
|
|
49
|
+
out_mapping.extend(mapping)
|
|
50
|
+
elif isinstance(out_mapping, dict):
|
|
51
|
+
out_mapping.clear()
|
|
52
|
+
for new_id, old_id in enumerate(mapping):
|
|
53
|
+
out_mapping[new_id] = old_id # returning a dict may leads to bad perf
|
|
54
|
+
|
|
55
|
+
return n_nodes, ndata, swc_like.source, swc_like.names
|
|
@@ -2,14 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
|
+
import warnings
|
|
5
6
|
from abc import ABC, abstractmethod
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import (
|
|
8
|
+
Callable,
|
|
9
|
+
Generic,
|
|
10
|
+
Iterable,
|
|
11
|
+
List,
|
|
12
|
+
Literal,
|
|
13
|
+
Optional,
|
|
14
|
+
Tuple,
|
|
15
|
+
TypeVar,
|
|
16
|
+
overload,
|
|
17
|
+
)
|
|
7
18
|
|
|
8
19
|
import numpy as np
|
|
9
20
|
import numpy.typing as npt
|
|
10
21
|
from typing_extensions import Self
|
|
11
22
|
|
|
12
|
-
from swcgeom.images.io import read_imgs
|
|
23
|
+
from swcgeom.images.io import ScalarType, read_imgs
|
|
13
24
|
from swcgeom.transforms import Identity, Transform
|
|
14
25
|
|
|
15
26
|
__all__ = [
|
|
@@ -21,20 +32,23 @@ __all__ = [
|
|
|
21
32
|
T = TypeVar("T")
|
|
22
33
|
|
|
23
34
|
|
|
24
|
-
class ImageStackFolderBase(Generic[T], ABC):
|
|
35
|
+
class ImageStackFolderBase(Generic[ScalarType, T], ABC):
|
|
25
36
|
"""Image stack folder base."""
|
|
26
37
|
|
|
27
38
|
files: List[str]
|
|
28
|
-
transform: Transform[npt.NDArray[
|
|
39
|
+
transform: Transform[npt.NDArray[ScalarType], T]
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
41
|
+
# fmt: off
|
|
42
|
+
@overload
|
|
43
|
+
def __init__(self, files: Iterable[str], *, dtype: None = ..., transform: Optional[Transform[npt.NDArray[np.float32], T]] = None) -> None: ...
|
|
44
|
+
@overload
|
|
45
|
+
def __init__(self, files: Iterable[str], *, dtype: ScalarType, transform: Optional[Transform[npt.NDArray[ScalarType], T]] = None) -> None: ...
|
|
46
|
+
# fmt: on
|
|
47
|
+
|
|
48
|
+
def __init__(self, files: Iterable[str], *, dtype=None, transform=None) -> None:
|
|
36
49
|
super().__init__()
|
|
37
50
|
self.files = list(files)
|
|
51
|
+
self.dtype = dtype or np.float32
|
|
38
52
|
self.transform = transform or Identity() # type: ignore
|
|
39
53
|
|
|
40
54
|
@abstractmethod
|
|
@@ -45,13 +59,12 @@ class ImageStackFolderBase(Generic[T], ABC):
|
|
|
45
59
|
return len(self.files)
|
|
46
60
|
|
|
47
61
|
def _get(self, fname: str) -> T:
|
|
48
|
-
imgs = self.
|
|
62
|
+
imgs = self._read(fname)
|
|
49
63
|
imgs = self.transform(imgs)
|
|
50
64
|
return imgs
|
|
51
65
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return read_imgs(fname).get_full()
|
|
66
|
+
def _read(self, fname: str) -> npt.NDArray[ScalarType]:
|
|
67
|
+
return read_imgs(fname, dtype=self.dtype).get_full() # type: ignore
|
|
55
68
|
|
|
56
69
|
@staticmethod
|
|
57
70
|
def scan(root: str, *, pattern: Optional[str] = None) -> List[str]:
|
|
@@ -63,8 +76,20 @@ class ImageStackFolderBase(Generic[T], ABC):
|
|
|
63
76
|
|
|
64
77
|
return fs
|
|
65
78
|
|
|
79
|
+
@staticmethod
|
|
80
|
+
def read_imgs(fname: str) -> npt.NDArray[np.float32]:
|
|
81
|
+
warnings.warn(
|
|
82
|
+
"`ImageStackFolderBase.read_imgs` serves as a "
|
|
83
|
+
"straightforward wrapper for `~swcgeom.images.io.read_imgs(fname).get_full()`. "
|
|
84
|
+
"However, as it is not utilized within our internal "
|
|
85
|
+
"processes, it is scheduled for removal in the "
|
|
86
|
+
"forthcoming version.",
|
|
87
|
+
DeprecationWarning,
|
|
88
|
+
)
|
|
89
|
+
return read_imgs(fname).get_full()
|
|
90
|
+
|
|
66
91
|
|
|
67
|
-
class ImageStackFolder(
|
|
92
|
+
class ImageStackFolder(ImageStackFolderBase[ScalarType, T]):
|
|
68
93
|
"""Image stack folder."""
|
|
69
94
|
|
|
70
95
|
def __getitem__(self, idx: int, /) -> T:
|
|
@@ -84,7 +109,7 @@ class ImageStackFolder(Generic[T], ImageStackFolderBase[T]):
|
|
|
84
109
|
return cls(cls.scan(root, pattern=pattern), **kwargs)
|
|
85
110
|
|
|
86
111
|
|
|
87
|
-
class LabeledImageStackFolder(
|
|
112
|
+
class LabeledImageStackFolder(ImageStackFolderBase[ScalarType, T]):
|
|
88
113
|
"""Image stack folder with label."""
|
|
89
114
|
|
|
90
115
|
labels: List[int]
|
|
@@ -115,7 +140,7 @@ class LabeledImageStackFolder(Generic[T], ImageStackFolderBase[T]):
|
|
|
115
140
|
return cls(files, labels, **kwargs)
|
|
116
141
|
|
|
117
142
|
|
|
118
|
-
class PathImageStackFolder(
|
|
143
|
+
class PathImageStackFolder(ImageStackFolder[ScalarType, T]):
|
|
119
144
|
"""Image stack folder with relpath."""
|
|
120
145
|
|
|
121
146
|
root: str
|