swcgeom 0.11.1__tar.gz → 0.13.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.11.1 → swcgeom-0.13.0}/.vscode/settings.json +6 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/CHANGELOG.md +73 -0
- {swcgeom-0.11.1/swcgeom.egg-info → swcgeom-0.13.0}/PKG-INFO +24 -8
- {swcgeom-0.11.1 → swcgeom-0.13.0}/README.md +2 -4
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/pytorch/tree_folder_dataset.py +2 -2
- swcgeom-0.13.0/pyproject.toml +43 -0
- swcgeom-0.13.0/swcgeom/__init__.py +6 -0
- swcgeom-0.13.0/swcgeom/_version.py +16 -0
- swcgeom-0.13.0/swcgeom/analysis/__init__.py +9 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/branch_features.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/feature_extractor.py +25 -12
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/node_features.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/path_features.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/sholl.py +11 -7
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/trunk.py +5 -5
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/analysis/visualization.py +2 -2
- swcgeom-0.13.0/swcgeom/analysis/volume.py +80 -0
- swcgeom-0.13.0/swcgeom/core/__init__.py +11 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/branch.py +8 -4
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/branch_tree.py +4 -5
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/node.py +5 -3
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/path.py +6 -3
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/population.py +2 -2
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/segment.py +8 -4
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc.py +24 -3
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/__init__.py +6 -6
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/assembler.py +2 -2
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/base.py +30 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/checker.py +30 -6
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/io.py +31 -30
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/normalizer.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/swc_utils/subtree.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/tree.py +38 -14
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/core/tree_utils.py +47 -41
- swcgeom-0.13.0/swcgeom/core/tree_utils_impl.py +39 -0
- swcgeom-0.13.0/swcgeom/images/__init__.py +4 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/images/folder.py +2 -2
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/images/io.py +48 -9
- swcgeom-0.13.0/swcgeom/transforms/__init__.py +12 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/branch.py +3 -3
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/geometry.py +11 -4
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/image_stack.py +3 -3
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/images.py +1 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/mst.py +68 -13
- swcgeom-0.13.0/swcgeom/transforms/path.py +48 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/population.py +2 -2
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/tree.py +18 -9
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/tree_assembler.py +7 -4
- swcgeom-0.13.0/swcgeom/utils/__init__.py +12 -0
- swcgeom-0.13.0/swcgeom/utils/dsu.py +42 -0
- swcgeom-0.13.0/swcgeom/utils/file.py +91 -0
- swcgeom-0.13.0/swcgeom/utils/geometry_object.py +299 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/neuromorpho.py +33 -11
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/renderer.py +5 -4
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/transforms.py +26 -1
- {swcgeom-0.11.1 → swcgeom-0.13.0/swcgeom.egg-info}/PKG-INFO +24 -8
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom.egg-info/SOURCES.txt +13 -2
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom.egg-info/requires.txt +3 -1
- swcgeom-0.13.0/tests/__init__.py +0 -0
- swcgeom-0.13.0/tests/utils/__init__.py +0 -0
- swcgeom-0.13.0/tests/utils/test_dsu.py +34 -0
- swcgeom-0.13.0/tests/utils/test_geometry_object.py +140 -0
- swcgeom-0.13.0/tests/utils/test_transforms.py +33 -0
- swcgeom-0.11.1/pyproject.toml +0 -43
- swcgeom-0.11.1/swcgeom/__init__.py +0 -6
- swcgeom-0.11.1/swcgeom/_version.py +0 -4
- swcgeom-0.11.1/swcgeom/analysis/__init__.py +0 -9
- swcgeom-0.11.1/swcgeom/core/__init__.py +0 -11
- swcgeom-0.11.1/swcgeom/images/__init__.py +0 -4
- swcgeom-0.11.1/swcgeom/transforms/__init__.py +0 -10
- swcgeom-0.11.1/swcgeom/utils/__init__.py +0 -9
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.github/workflows/build.yml +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.github/workflows/github-publish.yml +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.github/workflows/pypi-publish.yml +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.github/workflows/test-pypi-publish.yml +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.gitignore +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/.pylintrc +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/LICENSE +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/Branch.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/BranchTree.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/CollectTips.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/CutTree.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/Features.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/GeometryTransform.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/ImageStack.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/MST.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/SpectralClustering.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/Tree.ipynb +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/data/101711-10_4p5-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/data/101711-11_16-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/data/1059283677_15257_2226-X16029-Y23953.swc +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/data/toydata.swc +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/dgl/graph.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/pytorch/branch.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/examples/pytorch/branch_dataset.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/git-conventional-commits.yaml +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/setup.cfg +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/images/augmentation.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/transforms/base.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/debug.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/download.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/ellipse.py +0 -0
- /swcgeom-0.11.1/swcgeom/utils/numpy.py → /swcgeom-0.13.0/swcgeom/utils/numpy_helper.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom/utils/sdf.py +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom.egg-info/dependency_links.txt +0 -0
- {swcgeom-0.11.1 → swcgeom-0.13.0}/swcgeom.egg-info/top_level.txt +0 -0
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## **0.13.0** <sub><sup>2023-12-14 ([e2add59...06239bd](https://github.com/yzx9/swcgeom/compare/e2add59652bfc02d802f6770ea2c5fbc3fd7d729...06239bd129e6fab329ec80326352b48049fb504e?diff=split))</sup></sub>
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
##### `analysis`
|
|
8
|
+
* import sholl plot ([b03b45c](https://github.com/yzx9/swcgeom/commit/b03b45c4f20f2ed57263b1c2398316533b45b837))
|
|
9
|
+
* calc volume of tree \(close \#9\) ([a5004da](https://github.com/yzx9/swcgeom/commit/a5004dab71e71e68fd4a512757c9310f557878cf))
|
|
10
|
+
|
|
11
|
+
##### `core`
|
|
12
|
+
* check if it has a cyclic \(\#1\) ([e2add59](https://github.com/yzx9/swcgeom/commit/e2add59652bfc02d802f6770ea2c5fbc3fd7d729))
|
|
13
|
+
|
|
14
|
+
##### `utils`
|
|
15
|
+
* transform batch of vec3 to vec4 ([d2d660c](https://github.com/yzx9/swcgeom/commit/d2d660ca53b9886a81b02193ea77f76da4620ffa))
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
##### `utils`
|
|
20
|
+
* should support \`StringIO\` ([de439db](https://github.com/yzx9/swcgeom/commit/de439dba00ce7407d4ae18c9427eab1e5af4d95e))
|
|
21
|
+
|
|
22
|
+
### Performance Improvements
|
|
23
|
+
* improve dsu ([8b414c3](https://github.com/yzx9/swcgeom/commit/8b414c37f4fc3f4ded9c8b19eb8ee0ad52dedd53))
|
|
24
|
+
|
|
25
|
+
<br>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## **0.12.0** <sub><sup>2023-10-12 ([d9ba943...0824e9b](https://github.com/yzx9/swcgeom/compare/d9ba9433735c69edf979013632278e5f498a6fe0...0824e9b4110f820cd11c469ca6e319c1b2f14145?diff=split))</sup></sub>
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
##### `core`
|
|
33
|
+
|
|
34
|
+
* support read from io ([8fe9df8](https://github.com/yzx9/swcgeom/commit/8fe9df8459e8cef3cd6bde4ff3546e1a83871eda))
|
|
35
|
+
* get neurites and dendrites ([a9acfde](https://github.com/yzx9/swcgeom/commit/a9acfde5ab77bac22d36e7c461089f5159e6330a))
|
|
36
|
+
* add swc types ([7439288](https://github.com/yzx9/swcgeom/commit/7439288d199d558cd170600b07d1ab51a8489bc4))
|
|
37
|
+
* add type check in \`Tree\.Node\.is\_soma\` ([35b53d6](https://github.com/yzx9/swcgeom/commit/35b53d68c30e444b0b8ae57519f8196c5dbdcb4d))
|
|
38
|
+
|
|
39
|
+
##### `images`
|
|
40
|
+
|
|
41
|
+
* support \`v3dpbd\` and \`v3draw\` \(close \#6\) ([ca8267d](https://github.com/yzx9/swcgeom/commit/ca8267d694f62abc59b9c8174efc91512a9ccec9))
|
|
42
|
+
|
|
43
|
+
##### `transforms`
|
|
44
|
+
|
|
45
|
+
* sort mst tree by default ([7878f3f](https://github.com/yzx9/swcgeom/commit/7878f3fdb9beeebd31ecfa7649611fdd03e34eff))
|
|
46
|
+
* add path transforms ([aaa1b1e](https://github.com/yzx9/swcgeom/commit/aaa1b1e3c718017ae18a52d91918b29c02f302b3))
|
|
47
|
+
|
|
48
|
+
##### `utils`
|
|
49
|
+
|
|
50
|
+
* change to utf\-8 encode ([45e971e](https://github.com/yzx9/swcgeom/commit/45e971eef9cc88abb8e1fca5f26b99e46fcb5aaf))
|
|
51
|
+
|
|
52
|
+
### Bug Fixes
|
|
53
|
+
|
|
54
|
+
##### `core`
|
|
55
|
+
|
|
56
|
+
* avoid duplicate cols comments ([f99eaf3](https://github.com/yzx9/swcgeom/commit/f99eaf3946846522d7eac1769f6a9a4fd20e8bf0))
|
|
57
|
+
* inherit source ([702efab](https://github.com/yzx9/swcgeom/commit/702efabb5db9a27e8eaed9d1feb45f24e6b4e808))
|
|
58
|
+
* remove original point when cat tree ([065125e](https://github.com/yzx9/swcgeom/commit/065125e7ccf0d3f6ef9ff9c7689a0e7aeb6be349))
|
|
59
|
+
|
|
60
|
+
##### `images`
|
|
61
|
+
|
|
62
|
+
* shape should be vec4i ([0824e9b](https://github.com/yzx9/swcgeom/commit/0824e9b4110f820cd11c469ca6e319c1b2f14145))
|
|
63
|
+
|
|
64
|
+
##### `transforms`
|
|
65
|
+
|
|
66
|
+
* add missing exports ([93bf8e6](https://github.com/yzx9/swcgeom/commit/93bf8e6076d8d7a3898a1ff88cbe05d855d1173c))
|
|
67
|
+
|
|
68
|
+
### Performance Improvements
|
|
69
|
+
|
|
70
|
+
##### `transforms`
|
|
71
|
+
|
|
72
|
+
* disable \`detach\` operation ([204d44c](https://github.com/yzx9/swcgeom/commit/204d44cbab563309d85f9c6f64793e5eda028547))
|
|
73
|
+
|
|
74
|
+
<br>
|
|
75
|
+
|
|
3
76
|
## **0.11.1** <sub><sup>2023-08-12 ([36fa413...36fa413](https://github.com/yzx9/swcgeom/compare/36fa4135f2001694b8caea74e82c5ffa1118e90d...36fa4135f2001694b8caea74e82c5ffa1118e90d?diff=split))</sup></sub>
|
|
4
77
|
|
|
5
78
|
*no relevant changes*
|
|
@@ -1,31 +1,47 @@
|
|
|
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
|
-
Provides-Extra: all
|
|
12
11
|
License-File: LICENSE
|
|
12
|
+
Requires-Dist: imagecodecs>=2023.3.16
|
|
13
|
+
Requires-Dist: matplotlib>=3.5.2
|
|
14
|
+
Requires-Dist: numpy>=1.22.3
|
|
15
|
+
Requires-Dist: pandas>=1.4.2
|
|
16
|
+
Requires-Dist: pynrrd>=1.0.0
|
|
17
|
+
Requires-Dist: scipy>=1.9.1
|
|
18
|
+
Requires-Dist: seaborn>=0.12.0
|
|
19
|
+
Requires-Dist: sympy>=1.12
|
|
20
|
+
Requires-Dist: tifffile>=2022.8.12
|
|
21
|
+
Requires-Dist: typing_extensions>=4.4.0
|
|
22
|
+
Requires-Dist: v3d-py-helper-0.1.0
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: beautifulsoup4>=4.11.1; extra == "all"
|
|
25
|
+
Requires-Dist: certifi>=2023.5.7; extra == "all"
|
|
26
|
+
Requires-Dist: chardet>=5.2.0; extra == "all"
|
|
27
|
+
Requires-Dist: lmdb>=1.4.1; extra == "all"
|
|
28
|
+
Requires-Dist: pycurl>=7.0.0; extra == "all"
|
|
29
|
+
Requires-Dist: tqdm>=4.46.1; extra == "all"
|
|
30
|
+
Requires-Dist: urllib3>=1.26.0; extra == "all"
|
|
13
31
|
|
|
14
|
-
#
|
|
32
|
+
# SWCGEOM
|
|
15
33
|
|
|
16
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 />
|
|
17
35
|
|
|
18
36
|
[](https://github.com/yzx9/swcgeom/releases)
|
|
19
|
-
|
|
20
37
|
[](https://pypi.org/project/swcgeom/)
|
|
21
|
-
|
|
22
38
|
[](https://test.pypi.org/project/swcgeom/)
|
|
23
39
|
|
|
24
40
|
A neuron geometry library for swc format.
|
|
25
41
|
|
|
26
42
|
## Usage
|
|
27
43
|
|
|
28
|
-
See
|
|
44
|
+
See examples for details.
|
|
29
45
|
|
|
30
46
|
## Development
|
|
31
47
|
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SWCGEOM
|
|
2
2
|
|
|
3
3
|
<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 />
|
|
4
4
|
|
|
5
5
|
[](https://github.com/yzx9/swcgeom/releases)
|
|
6
|
-
|
|
7
6
|
[](https://pypi.org/project/swcgeom/)
|
|
8
|
-
|
|
9
7
|
[](https://test.pypi.org/project/swcgeom/)
|
|
10
8
|
|
|
11
9
|
A neuron geometry library for swc format.
|
|
12
10
|
|
|
13
11
|
## Usage
|
|
14
12
|
|
|
15
|
-
See
|
|
13
|
+
See examples for details.
|
|
16
14
|
|
|
17
15
|
## Development
|
|
18
16
|
|
|
@@ -4,8 +4,8 @@ from typing import Generic, TypeVar, cast
|
|
|
4
4
|
|
|
5
5
|
import torch.utils.data
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
7
|
+
from swcgeom import Population, Tree
|
|
8
|
+
from swcgeom.transforms import Identity, Transform
|
|
9
9
|
|
|
10
10
|
__all__ = ["TreeFolderDataset"]
|
|
11
11
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64", "setuptools_scm[toml]>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "swcgeom"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Neuron geometry library for swc format"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
authors = [{ name = "yzx9", email = "yuan.zx@outlook.com" }]
|
|
12
|
+
keywords = ["neuronscience", "neuron", "neuroanatomy", "neuron-morphology"]
|
|
13
|
+
license = { text = "CC4.0 BY-NC-SA" }
|
|
14
|
+
dependencies = [
|
|
15
|
+
"imagecodecs>=2023.3.16",
|
|
16
|
+
"matplotlib>=3.5.2",
|
|
17
|
+
"numpy>=1.22.3",
|
|
18
|
+
"pandas>=1.4.2",
|
|
19
|
+
"pynrrd>=1.0.0",
|
|
20
|
+
"scipy>=1.9.1",
|
|
21
|
+
"seaborn>=0.12.0",
|
|
22
|
+
"sympy>=1.12",
|
|
23
|
+
"tifffile>=2022.8.12",
|
|
24
|
+
"typing_extensions>=4.4.0",
|
|
25
|
+
"v3d-py-helper-0.1.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
all = [
|
|
30
|
+
"beautifulsoup4>=4.11.1",
|
|
31
|
+
"certifi>=2023.5.7",
|
|
32
|
+
"chardet>=5.2.0",
|
|
33
|
+
"lmdb>=1.4.1",
|
|
34
|
+
"pycurl>=7.0.0",
|
|
35
|
+
"tqdm>=4.46.1",
|
|
36
|
+
"urllib3>=1.26.0",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
repository = "https://github.com/yzx9/swcgeom"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools_scm]
|
|
43
|
+
write_to = "swcgeom/_version.py"
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""A neuron geometry library for swc format."""
|
|
2
|
+
|
|
3
|
+
from swcgeom import analysis, core, images, transforms
|
|
4
|
+
from swcgeom._version import __version__, __version_tuple__
|
|
5
|
+
from swcgeom.analysis import draw
|
|
6
|
+
from swcgeom.core import BranchTree, Population, Populations, Tree
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file generated by setuptools_scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
TYPE_CHECKING = False
|
|
4
|
+
if TYPE_CHECKING:
|
|
5
|
+
from typing import Tuple, Union
|
|
6
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
7
|
+
else:
|
|
8
|
+
VERSION_TUPLE = object
|
|
9
|
+
|
|
10
|
+
version: str
|
|
11
|
+
__version__: str
|
|
12
|
+
__version_tuple__: VERSION_TUPLE
|
|
13
|
+
version_tuple: VERSION_TUPLE
|
|
14
|
+
|
|
15
|
+
__version__ = version = '0.13.0'
|
|
16
|
+
__version_tuple__ = version_tuple = (0, 13, 0)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Analysis for neuron trees."""
|
|
2
|
+
|
|
3
|
+
from swcgeom.analysis.branch_features import *
|
|
4
|
+
from swcgeom.analysis.feature_extractor import *
|
|
5
|
+
from swcgeom.analysis.node_features import *
|
|
6
|
+
from swcgeom.analysis.path_features import *
|
|
7
|
+
from swcgeom.analysis.sholl import *
|
|
8
|
+
from swcgeom.analysis.trunk import *
|
|
9
|
+
from swcgeom.analysis.visualization import *
|
|
@@ -17,17 +17,23 @@ import numpy.typing as npt
|
|
|
17
17
|
import seaborn as sns
|
|
18
18
|
from matplotlib.axes import Axes
|
|
19
19
|
|
|
20
|
-
from
|
|
21
|
-
from
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
from swcgeom.analysis.branch_features import BranchFeatures
|
|
21
|
+
from swcgeom.analysis.node_features import (
|
|
22
|
+
BifurcationFeatures,
|
|
23
|
+
NodeFeatures,
|
|
24
|
+
TipFeatures,
|
|
25
|
+
)
|
|
26
|
+
from swcgeom.analysis.path_features import PathFeatures
|
|
27
|
+
from swcgeom.analysis.sholl import Sholl
|
|
28
|
+
from swcgeom.analysis.volume import get_volume
|
|
29
|
+
from swcgeom.core import Population, Populations, Tree
|
|
30
|
+
from swcgeom.utils import padding1d
|
|
26
31
|
|
|
27
32
|
__all__ = ["Feature", "extract_feature"]
|
|
28
33
|
|
|
29
34
|
Feature = Literal[
|
|
30
35
|
"length",
|
|
36
|
+
"volume",
|
|
31
37
|
"sholl",
|
|
32
38
|
# node
|
|
33
39
|
"node_count",
|
|
@@ -50,7 +56,7 @@ Feature = Literal[
|
|
|
50
56
|
NDArrayf32 = npt.NDArray[np.float32]
|
|
51
57
|
FeatAndKwargs = Feature | Tuple[Feature, Dict[str, Any]]
|
|
52
58
|
|
|
53
|
-
Feature1D = set(["length", "node_count", "bifurcation_count", "tip_count"])
|
|
59
|
+
Feature1D = set(["length", "volume", "node_count", "bifurcation_count", "tip_count"])
|
|
54
60
|
|
|
55
61
|
|
|
56
62
|
class Features:
|
|
@@ -101,6 +107,9 @@ class Features:
|
|
|
101
107
|
def get_length(self, **kwargs) -> NDArrayf32:
|
|
102
108
|
return np.array([self.tree.length(**kwargs)], dtype=np.float32)
|
|
103
109
|
|
|
110
|
+
def get_volume(self, **kwargs) -> NDArrayf32:
|
|
111
|
+
return np.array([get_volume(self.tree, **kwargs)], dtype=np.float32)
|
|
112
|
+
|
|
104
113
|
def get_sholl(self, **kwargs) -> NDArrayf32:
|
|
105
114
|
return self.sholl.get(**kwargs).astype(np.float32)
|
|
106
115
|
|
|
@@ -284,9 +293,11 @@ class PopulationFeatureExtractor(FeatureExtractor):
|
|
|
284
293
|
v = np.stack([padding1d(len_max, v, dtype=np.float32) for v in vals])
|
|
285
294
|
return v
|
|
286
295
|
|
|
287
|
-
def _get_sholl_impl(
|
|
296
|
+
def _get_sholl_impl(
|
|
297
|
+
self, steps: int = 20, **kwargs
|
|
298
|
+
) -> Tuple[NDArrayf32, NDArrayf32]:
|
|
288
299
|
rmax = max(t.sholl.rmax for t in self._features)
|
|
289
|
-
rs = Sholl.get_rs(rmax=rmax, steps=
|
|
300
|
+
rs = Sholl.get_rs(rmax=rmax, steps=steps)
|
|
290
301
|
vals = self._get_impl("sholl", steps=rs, **kwargs)
|
|
291
302
|
return vals, rs
|
|
292
303
|
|
|
@@ -356,10 +367,12 @@ class PopulationsFeatureExtractor(FeatureExtractor):
|
|
|
356
367
|
|
|
357
368
|
return out
|
|
358
369
|
|
|
359
|
-
def _get_sholl_impl(
|
|
370
|
+
def _get_sholl_impl(
|
|
371
|
+
self, steps: int = 20, **kwargs
|
|
372
|
+
) -> Tuple[NDArrayf32, NDArrayf32]:
|
|
360
373
|
rmaxs = chain.from_iterable((t.sholl.rmax for t in p) for p in self._features)
|
|
361
374
|
rmax = max(rmaxs)
|
|
362
|
-
rs = Sholl.get_rs(rmax=rmax, steps=
|
|
375
|
+
rs = Sholl.get_rs(rmax=rmax, steps=steps)
|
|
363
376
|
vals = self._get_impl("sholl", steps=rs, **kwargs)
|
|
364
377
|
return vals, rs
|
|
365
378
|
|
|
@@ -404,7 +417,7 @@ class PopulationsFeatureExtractor(FeatureExtractor):
|
|
|
404
417
|
return ax
|
|
405
418
|
|
|
406
419
|
|
|
407
|
-
def extract_feature(obj: Tree | Population) -> FeatureExtractor:
|
|
420
|
+
def extract_feature(obj: Tree | Population | Populations) -> FeatureExtractor:
|
|
408
421
|
if isinstance(obj, Tree):
|
|
409
422
|
return TreeFeatureExtractor(obj)
|
|
410
423
|
|
|
@@ -9,10 +9,10 @@ import seaborn as sns
|
|
|
9
9
|
from matplotlib.axes import Axes
|
|
10
10
|
from matplotlib.figure import Figure
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
14
|
-
from
|
|
15
|
-
from .
|
|
12
|
+
from swcgeom.analysis.visualization import draw
|
|
13
|
+
from swcgeom.core import Tree
|
|
14
|
+
from swcgeom.transforms import TranslateOrigin
|
|
15
|
+
from swcgeom.utils import draw_circles, get_fig_ax
|
|
16
16
|
|
|
17
17
|
__all__ = ["Sholl"]
|
|
18
18
|
|
|
@@ -42,9 +42,12 @@ class Sholl:
|
|
|
42
42
|
step: Optional[float] = None,
|
|
43
43
|
) -> None:
|
|
44
44
|
tree = Tree.from_swc(tree) if isinstance(tree, str) else tree
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
try:
|
|
46
|
+
self.tree = TranslateOrigin.transform(tree) # shift
|
|
47
|
+
self.rs = np.linalg.norm(self.tree.get_segments().xyz(), axis=2)
|
|
48
|
+
self.rmax = self.rs.max()
|
|
49
|
+
except Exception as e:
|
|
50
|
+
raise ValueError(f"invalid tree: {tree.source or ''}") from e
|
|
48
51
|
|
|
49
52
|
if step is not None:
|
|
50
53
|
warnings.warn(
|
|
@@ -96,6 +99,7 @@ class Sholl:
|
|
|
96
99
|
**kwargs :
|
|
97
100
|
Forwarding to plot method.
|
|
98
101
|
"""
|
|
102
|
+
|
|
99
103
|
if plot_type is not None:
|
|
100
104
|
warnings.warn(
|
|
101
105
|
"`plot_type` has been renamed to `kind` since v0.5.0, "
|
|
@@ -12,9 +12,9 @@ from matplotlib.axes import Axes
|
|
|
12
12
|
from matplotlib.figure import Figure
|
|
13
13
|
from matplotlib.patches import Circle, Ellipse, Patch, Rectangle
|
|
14
14
|
|
|
15
|
-
from
|
|
16
|
-
from
|
|
17
|
-
from .
|
|
15
|
+
from swcgeom.analysis.visualization import draw
|
|
16
|
+
from swcgeom.core import Tree, get_subtree, to_subtree
|
|
17
|
+
from swcgeom.utils import get_fig_ax, mvee
|
|
18
18
|
|
|
19
19
|
__all__ = ["draw_trunk"]
|
|
20
20
|
|
|
@@ -178,5 +178,5 @@ def create_point_2d(
|
|
|
178
178
|
# Helpers
|
|
179
179
|
|
|
180
180
|
|
|
181
|
-
def get_dendrites(tree: Tree) -> Iterable[int]:
|
|
182
|
-
return
|
|
181
|
+
def get_dendrites(tree: Tree) -> Iterable[int]:
|
|
182
|
+
return (t.node(0).id for t in tree.get_dendrites())
|
|
@@ -9,8 +9,8 @@ from matplotlib.axes import Axes
|
|
|
9
9
|
from matplotlib.figure import Figure
|
|
10
10
|
from matplotlib.legend import Legend
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from swcgeom.core import SWCLike, Tree
|
|
13
|
+
from swcgeom.utils import (
|
|
14
14
|
CameraOptions,
|
|
15
15
|
SimpleCamera,
|
|
16
16
|
draw_direction_indicator,
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Analysis of volume of a SWC tree."""
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from swcgeom.core import Tree
|
|
6
|
+
from swcgeom.utils import GeomFrustumCone, GeomSphere
|
|
7
|
+
|
|
8
|
+
__all__ = ["get_volume"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_volume(tree: Tree):
|
|
12
|
+
"""Get the volume of the tree.
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
tree : Tree
|
|
17
|
+
SWC tree.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
volume : float
|
|
22
|
+
Volume of the tree.
|
|
23
|
+
|
|
24
|
+
Notes
|
|
25
|
+
-----
|
|
26
|
+
The SWC format is a method for representing neurons, which includes
|
|
27
|
+
both the radius of individual points and their interconnectivity.
|
|
28
|
+
Consequently, there are multiple distinct approaches to
|
|
29
|
+
representation within this framework.
|
|
30
|
+
|
|
31
|
+
Currently, we support a standard approach to volume calculation.
|
|
32
|
+
This method involves treating each node as a sphere and
|
|
33
|
+
representing the connections between them as truncated cone-like
|
|
34
|
+
structures, or frustums, with varying radii at their top and bottom
|
|
35
|
+
surfaces.
|
|
36
|
+
|
|
37
|
+
More representation methods will be supported in the future.
|
|
38
|
+
"""
|
|
39
|
+
volume = 0.0
|
|
40
|
+
|
|
41
|
+
def leave(node: Tree.Node, children: List[GeomSphere]) -> GeomSphere:
|
|
42
|
+
sphere = GeomSphere(node.xyz(), node.r)
|
|
43
|
+
frustum_cones = [
|
|
44
|
+
GeomFrustumCone(node.xyz(), node.r, c.center, c.radius) for c in children
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
v = sphere.get_volume()
|
|
48
|
+
v += sum(fc.get_volume() for fc in frustum_cones)
|
|
49
|
+
v -= sum(sphere.get_intersect_volume(fc) for fc in frustum_cones)
|
|
50
|
+
v -= sum(s.get_intersect_volume(fc) for s, fc in zip(children, frustum_cones))
|
|
51
|
+
|
|
52
|
+
# TODO
|
|
53
|
+
# remove volume of intersection between frustum cones
|
|
54
|
+
# v -= sum(
|
|
55
|
+
# fc1.get_intersect_volume(fc2)
|
|
56
|
+
# for fc1 in frustum_cones
|
|
57
|
+
# for fc2 in frustum_cones
|
|
58
|
+
# if fc1 != fc2
|
|
59
|
+
# )
|
|
60
|
+
|
|
61
|
+
nonlocal volume
|
|
62
|
+
volume += v
|
|
63
|
+
return sphere
|
|
64
|
+
|
|
65
|
+
tree.traverse(leave=leave)
|
|
66
|
+
return volume
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
from io import StringIO
|
|
71
|
+
|
|
72
|
+
swc = """
|
|
73
|
+
1 1 0 0 0 1 -1
|
|
74
|
+
2 1 2 0 0 1 1
|
|
75
|
+
3 1 4 0 0 1 2
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
tree = Tree.from_swc(StringIO(swc))
|
|
79
|
+
volume = get_volume(tree)
|
|
80
|
+
print(volume)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Neuron trees."""
|
|
2
|
+
|
|
3
|
+
from swcgeom.core import swc_utils
|
|
4
|
+
from swcgeom.core.branch import *
|
|
5
|
+
from swcgeom.core.branch_tree import *
|
|
6
|
+
from swcgeom.core.node import *
|
|
7
|
+
from swcgeom.core.path import *
|
|
8
|
+
from swcgeom.core.population import *
|
|
9
|
+
from swcgeom.core.swc import *
|
|
10
|
+
from swcgeom.core.tree import *
|
|
11
|
+
from swcgeom.core.tree_utils import *
|
|
@@ -5,9 +5,9 @@ from typing import Generic, Iterable, List
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import numpy.typing as npt
|
|
7
7
|
|
|
8
|
-
from .path import Path
|
|
9
|
-
from .segment import Segment, Segments
|
|
10
|
-
from .swc import DictSWC, SWCTypeVar
|
|
8
|
+
from swcgeom.core.path import Path
|
|
9
|
+
from swcgeom.core.segment import Segment, Segments
|
|
10
|
+
from swcgeom.core.swc import DictSWC, SWCTypeVar
|
|
11
11
|
|
|
12
12
|
__all__ = ["Branch"]
|
|
13
13
|
|
|
@@ -42,7 +42,11 @@ class Branch(Path, Generic[SWCTypeVar]):
|
|
|
42
42
|
def detach(self) -> "Branch[DictSWC]":
|
|
43
43
|
"""Detach from current attached object."""
|
|
44
44
|
# pylint: disable=consider-using-dict-items
|
|
45
|
-
attact = DictSWC(
|
|
45
|
+
attact = DictSWC(
|
|
46
|
+
**{k: self[k] for k in self.keys()},
|
|
47
|
+
source=self.attach.source,
|
|
48
|
+
names=self.names,
|
|
49
|
+
)
|
|
46
50
|
attact.ndata[self.names.id] = self.id()
|
|
47
51
|
attact.ndata[self.names.pid] = self.pid()
|
|
48
52
|
return Branch(attact, self.id())
|
|
@@ -7,9 +7,9 @@ import numpy as np
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
from typing_extensions import Self
|
|
9
9
|
|
|
10
|
-
from .branch import Branch
|
|
11
|
-
from .swc_utils import to_sub_topology
|
|
12
|
-
from .tree import Tree
|
|
10
|
+
from swcgeom.core.branch import Branch
|
|
11
|
+
from swcgeom.core.swc_utils import to_sub_topology
|
|
12
|
+
from swcgeom.core.tree import Tree
|
|
13
13
|
|
|
14
14
|
__all__ = ["BranchTree"]
|
|
15
15
|
|
|
@@ -45,8 +45,7 @@ class BranchTree(Tree):
|
|
|
45
45
|
ndata = {k: tree.get_ndata(k)[id_map].copy() for k in tree.keys()}
|
|
46
46
|
ndata.update(id=new_id, pid=new_pid)
|
|
47
47
|
|
|
48
|
-
branch_tree = cls(n_nodes, **ndata, names=tree.names)
|
|
49
|
-
branch_tree.source = tree.source # TODO
|
|
48
|
+
branch_tree = cls(n_nodes, **ndata, source=tree.source, names=tree.names)
|
|
50
49
|
|
|
51
50
|
branch_tree.branches = {}
|
|
52
51
|
for br in branches:
|
|
@@ -6,8 +6,8 @@ from typing import Any, Generic, Iterable
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
8
8
|
|
|
9
|
-
from .swc import DictSWC, SWCTypeVar
|
|
10
|
-
from .swc_utils import SWCNames
|
|
9
|
+
from swcgeom.core.swc import DictSWC, SWCTypeVar
|
|
10
|
+
from swcgeom.core.swc_utils import SWCNames
|
|
11
11
|
|
|
12
12
|
__all__ = ["Node"]
|
|
13
13
|
|
|
@@ -113,7 +113,9 @@ class Node(Generic[SWCTypeVar]):
|
|
|
113
113
|
"""Detach from current attached object."""
|
|
114
114
|
# pylint: disable=consider-using-dict-items
|
|
115
115
|
attact = DictSWC(
|
|
116
|
-
**{k: np.array([self[k]]) for k in self.keys()},
|
|
116
|
+
**{k: np.array([self[k]]) for k in self.keys()},
|
|
117
|
+
source=self.attach.source,
|
|
118
|
+
names=self.names,
|
|
117
119
|
)
|
|
118
120
|
attact.ndata[self.names.id] = np.array([0])
|
|
119
121
|
attact.ndata[self.names.pid] = np.array([-1])
|
|
@@ -5,8 +5,8 @@ from typing import Generic, Iterable, Iterator, List, overload
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import numpy.typing as npt
|
|
7
7
|
|
|
8
|
-
from .node import Node
|
|
9
|
-
from .swc import DictSWC, SWCLike, SWCTypeVar
|
|
8
|
+
from swcgeom.core.node import Node
|
|
9
|
+
from swcgeom.core.swc import DictSWC, SWCLike, SWCTypeVar
|
|
10
10
|
|
|
11
11
|
__all__ = ["Path"]
|
|
12
12
|
|
|
@@ -28,6 +28,7 @@ class Path(SWCLike, Generic[SWCTypeVar]):
|
|
|
28
28
|
self.attach = attach
|
|
29
29
|
self.names = attach.names
|
|
30
30
|
self.idx = np.array(idx, dtype=np.int32)
|
|
31
|
+
self.source = self.attach.source
|
|
31
32
|
|
|
32
33
|
def __iter__(self) -> Iterator[Node]:
|
|
33
34
|
return (self.get_node(i) for i in range(len(self)))
|
|
@@ -79,7 +80,9 @@ class Path(SWCLike, Generic[SWCTypeVar]):
|
|
|
79
80
|
"""Detach from current attached object."""
|
|
80
81
|
# pylint: disable-next=consider-using-dict-items
|
|
81
82
|
attact = DictSWC(
|
|
82
|
-
**{k: self.get_ndata(k) for k in self.keys()},
|
|
83
|
+
**{k: self.get_ndata(k) for k in self.keys()},
|
|
84
|
+
source=self.source,
|
|
85
|
+
names=self.names,
|
|
83
86
|
)
|
|
84
87
|
attact.ndata[self.names.id] = self.id()
|
|
85
88
|
attact.ndata[self.names.pid] = self.pid()
|
|
@@ -19,8 +19,8 @@ import numpy as np
|
|
|
19
19
|
import numpy.typing as npt
|
|
20
20
|
from typing_extensions import Self
|
|
21
21
|
|
|
22
|
-
from .swc import eswc_cols
|
|
23
|
-
from .tree import Tree
|
|
22
|
+
from swcgeom.core.swc import eswc_cols
|
|
23
|
+
from swcgeom.core.tree import Tree
|
|
24
24
|
|
|
25
25
|
__all__ = ["LazyLoadingTrees", "ChainTrees", "Population", "Populations"]
|
|
26
26
|
|