supervoxel-splitter 0.1.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.
Files changed (37) hide show
  1. supervoxel_splitter-0.1.0/.github/workflows/test.yml +79 -0
  2. supervoxel_splitter-0.1.0/.gitignore +79 -0
  3. supervoxel_splitter-0.1.0/.pre-commit-config.yaml +15 -0
  4. supervoxel_splitter-0.1.0/LICENSE +21 -0
  5. supervoxel_splitter-0.1.0/MANIFEST.in +2 -0
  6. supervoxel_splitter-0.1.0/PKG-INFO +139 -0
  7. supervoxel_splitter-0.1.0/README.md +83 -0
  8. supervoxel_splitter-0.1.0/codecov.yml +11 -0
  9. supervoxel_splitter-0.1.0/pyproject.toml +54 -0
  10. supervoxel_splitter-0.1.0/setup.cfg +4 -0
  11. supervoxel_splitter-0.1.0/supervoxel_splitter/__init__.py +29 -0
  12. supervoxel_splitter-0.1.0/supervoxel_splitter/_snap.py +198 -0
  13. supervoxel_splitter-0.1.0/supervoxel_splitter/_utils.py +165 -0
  14. supervoxel_splitter-0.1.0/supervoxel_splitter/api.py +48 -0
  15. supervoxel_splitter-0.1.0/supervoxel_splitter/geodesic/__init__.py +8 -0
  16. supervoxel_splitter-0.1.0/supervoxel_splitter/geodesic/arrival.py +88 -0
  17. supervoxel_splitter-0.1.0/supervoxel_splitter/geodesic/resolve.py +172 -0
  18. supervoxel_splitter-0.1.0/supervoxel_splitter/geodesic/ridge.py +354 -0
  19. supervoxel_splitter-0.1.0/supervoxel_splitter/geodesic/splitter.py +502 -0
  20. supervoxel_splitter-0.1.0/supervoxel_splitter/learned/__init__.py +7 -0
  21. supervoxel_splitter-0.1.0/supervoxel_splitter/learned/splitter.py +52 -0
  22. supervoxel_splitter-0.1.0/supervoxel_splitter/state.py +76 -0
  23. supervoxel_splitter-0.1.0/supervoxel_splitter/watershed/__init__.py +7 -0
  24. supervoxel_splitter-0.1.0/supervoxel_splitter/watershed/splitter.py +111 -0
  25. supervoxel_splitter-0.1.0/supervoxel_splitter.egg-info/PKG-INFO +139 -0
  26. supervoxel_splitter-0.1.0/supervoxel_splitter.egg-info/SOURCES.txt +35 -0
  27. supervoxel_splitter-0.1.0/supervoxel_splitter.egg-info/dependency_links.txt +1 -0
  28. supervoxel_splitter-0.1.0/supervoxel_splitter.egg-info/requires.txt +18 -0
  29. supervoxel_splitter-0.1.0/supervoxel_splitter.egg-info/top_level.txt +1 -0
  30. supervoxel_splitter-0.1.0/tests/conftest.py +21 -0
  31. supervoxel_splitter-0.1.0/tests/test_geodesic.py +75 -0
  32. supervoxel_splitter-0.1.0/tests/test_protocols.py +54 -0
  33. supervoxel_splitter-0.1.0/tests/test_ridge.py +31 -0
  34. supervoxel_splitter-0.1.0/tests/test_snap.py +90 -0
  35. supervoxel_splitter-0.1.0/tests/test_state.py +43 -0
  36. supervoxel_splitter-0.1.0/tests/test_utils.py +115 -0
  37. supervoxel_splitter-0.1.0/tests/test_watershed.py +29 -0
@@ -0,0 +1,79 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ test:
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.14"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ pip install -e ".[fast,dev]"
31
+
32
+ - name: Run tests with coverage
33
+ run: pytest --cov=supervoxel_splitter --cov-report=xml tests/
34
+
35
+ - name: Upload coverage to Codecov
36
+ uses: codecov/codecov-action@v5
37
+ with:
38
+ files: ./coverage.xml
39
+ fail_ci_if_error: false
40
+ env:
41
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
42
+
43
+ validate-tag:
44
+ if: startsWith(github.ref, 'refs/tags/v')
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - name: Reject non-SemVer tag
48
+ env:
49
+ TAG: ${{ github.ref_name }}
50
+ run: |
51
+ re='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
52
+ [[ "$TAG" =~ $re ]] || { echo "Tag '$TAG' is not SemVer (vMAJOR.MINOR.PATCH[-pre][+build])"; exit 1; }
53
+
54
+ publish:
55
+ if: startsWith(github.ref, 'refs/tags/v')
56
+ needs: [test, validate-tag]
57
+ runs-on: ubuntu-latest
58
+ environment: pypi
59
+ permissions:
60
+ id-token: write
61
+
62
+ steps:
63
+ - uses: actions/checkout@v4
64
+ with:
65
+ fetch-depth: 0
66
+
67
+ - name: Set up Python
68
+ uses: actions/setup-python@v5
69
+ with:
70
+ python-version: "3.14"
71
+
72
+ - name: Install build tools
73
+ run: pip install build
74
+
75
+ - name: Build package
76
+ run: python -m build
77
+
78
+ - name: Publish to PyPI
79
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,79 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # Installer logs
31
+ pip-log.txt
32
+ pip-delete-this-directory.txt
33
+
34
+ # Unit test / coverage reports
35
+ htmlcov/
36
+ .tox/
37
+ .nox/
38
+ .coverage
39
+ .coverage.*
40
+ .cache
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ *.py,cover
45
+ .hypothesis/
46
+ .pytest_cache/
47
+
48
+ # Jupyter Notebook
49
+ .ipynb_checkpoints
50
+
51
+ # IPython
52
+ profile_default/
53
+ ipython_config.py
54
+
55
+ # pyenv
56
+ .python-version
57
+
58
+ # Environments
59
+ .env
60
+ .venv
61
+ env/
62
+ venv/
63
+ ENV/
64
+ env.bak/
65
+ venv.bak/
66
+
67
+ # mypy
68
+ .mypy_cache/
69
+ .dmypy.json
70
+ dmypy.json
71
+
72
+ # Ruff
73
+ .ruff_cache/
74
+
75
+ # Editors
76
+ .devcontainer/
77
+ .vscode/
78
+ *.code-workspace
79
+ .claude/
@@ -0,0 +1,15 @@
1
+ repos:
2
+ - repo: https://github.com/psf/black
3
+ rev: 26.1.0
4
+ hooks:
5
+ - id: black
6
+
7
+ - repo: local
8
+ hooks:
9
+ - id: pytest
10
+ name: pytest
11
+ entry: pytest
12
+ language: system
13
+ types: [python]
14
+ pass_filenames: false
15
+ always_run: true
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CAVE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include LICENSE
2
+ include README.md
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: supervoxel-splitter
3
+ Version: 0.1.0
4
+ Summary: Pluggable supervoxel splitting algorithms with a uniform interface.
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 CAVE
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/CAVEconnectome/supervoxel-splitter
28
+ Project-URL: Issues, https://github.com/CAVEconnectome/supervoxel-splitter/issues
29
+ Project-URL: Source, https://github.com/CAVEconnectome/supervoxel-splitter
30
+ Keywords: connectomics,segmentation,supervoxel,watershed,geodesic,image-processing
31
+ Classifier: Intended Audience :: Science/Research
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: POSIX :: Linux
34
+ Classifier: Operating System :: MacOS
35
+ Classifier: Programming Language :: Python :: 3
36
+ Classifier: Programming Language :: Python :: 3.14
37
+ Requires-Python: >=3.14
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: numpy
41
+ Requires-Dist: scipy>=1.6
42
+ Requires-Dist: fastremap
43
+ Requires-Dist: connected-components-3d
44
+ Requires-Dist: scikit-image
45
+ Requires-Dist: edt
46
+ Provides-Extra: fast
47
+ Requires-Dist: dijkstra3d; extra == "fast"
48
+ Provides-Extra: learned
49
+ Provides-Extra: dev
50
+ Requires-Dist: pytest; extra == "dev"
51
+ Requires-Dist: pytest-cov; extra == "dev"
52
+ Requires-Dist: black; extra == "dev"
53
+ Requires-Dist: pre-commit; extra == "dev"
54
+ Requires-Dist: build; extra == "dev"
55
+ Dynamic: license-file
56
+
57
+ # supervoxel-splitter
58
+
59
+ [![codecov](https://codecov.io/gh/CAVEconnectome/supervoxel-splitter/graph/badge.svg)](https://codecov.io/gh/CAVEconnectome/supervoxel-splitter)
60
+
61
+ Pluggable supervoxel-splitting algorithms behind a uniform `Splitter` interface.
62
+
63
+ ## What it does
64
+
65
+ Given a 3-D foreground mask, source seeds, and sink seeds, return a labeled
66
+ volume that partitions the foreground into a source side and a sink side.
67
+ Useful for proofreading workflows that need to split a single supervoxel
68
+ without touching the rest of the segmentation.
69
+
70
+ The package ships multiple techniques behind the same `Splitter` protocol:
71
+
72
+ - **GeodesicSplitter** — anisotropic geodesic carve via an EDT-derived speed
73
+ field. Default technique; preserves the neck-aware behavior used in
74
+ PyChunkedGraph today.
75
+ - **WatershedSplitter** — seeded watershed on the distance transform.
76
+ - **NoopSplitter** — reference implementation; documents the protocol shape
77
+ for downstream plugin authors (e.g. a learned splitter).
78
+
79
+ ## Install
80
+
81
+ Requires Python 3.14 or newer.
82
+
83
+ ```
84
+ pip install supervoxel-splitter # core
85
+ pip install "supervoxel-splitter[fast]" # adds dijkstra3d as the geodesic backend
86
+ ```
87
+
88
+ ## The uniform interface
89
+
90
+ Every splitter satisfies the same `Splitter` protocol. Technique-specific
91
+ knobs live on the constructor; `split()` itself stays clean.
92
+
93
+ ```python
94
+ from supervoxel_splitter import GeodesicSplitter
95
+
96
+ splitter = GeodesicSplitter(backend="dj3d")
97
+ result = splitter.split(
98
+ mask=foreground_3d_bool,
99
+ sources=src_seeds_xyz,
100
+ sinks=snk_seeds_xyz,
101
+ voxel_size=(4.0, 4.0, 40.0),
102
+ )
103
+
104
+ # result.labels: uint8 in {0, SOURCE, SINK, STRAY}
105
+ # result.side_of_label: {SOURCE: 1, SINK: 2}
106
+ # result.diagnostics: per-stage timings, label counts, etc.
107
+ ```
108
+
109
+ Plugin authors implement the protocol structurally — no base class to
110
+ inherit:
111
+
112
+ ```python
113
+ class MyLearnedSplitter:
114
+ def __init__(self, *, model_path):
115
+ ...
116
+ def split(self, mask, sources, sinks, *,
117
+ voxel_size=(1.0, 1.0, 1.0),
118
+ vol_order="xyz", vox_order="xyz", seed_order="xyz"):
119
+ ...
120
+ return SplitResult(...)
121
+
122
+ assert isinstance(MyLearnedSplitter(model_path="..."), Splitter)
123
+ ```
124
+
125
+ ## Boundary
126
+
127
+ The package knows nothing about ChunkedGraph, BigTable, IDs, or edges. It
128
+ takes voxels in and gives labels back. New-ID minting, edge updates, and
129
+ persistence are the consumer's responsibility.
130
+
131
+ ## Status
132
+
133
+ v0.1.0 covers `GeodesicSplitter`. `WatershedSplitter` and `NoopSplitter` ship
134
+ as small reference impls demonstrating the protocol; production-quality
135
+ parity with geodesic is out of scope for v0.1.0.
136
+
137
+ ## License
138
+
139
+ MIT.
@@ -0,0 +1,83 @@
1
+ # supervoxel-splitter
2
+
3
+ [![codecov](https://codecov.io/gh/CAVEconnectome/supervoxel-splitter/graph/badge.svg)](https://codecov.io/gh/CAVEconnectome/supervoxel-splitter)
4
+
5
+ Pluggable supervoxel-splitting algorithms behind a uniform `Splitter` interface.
6
+
7
+ ## What it does
8
+
9
+ Given a 3-D foreground mask, source seeds, and sink seeds, return a labeled
10
+ volume that partitions the foreground into a source side and a sink side.
11
+ Useful for proofreading workflows that need to split a single supervoxel
12
+ without touching the rest of the segmentation.
13
+
14
+ The package ships multiple techniques behind the same `Splitter` protocol:
15
+
16
+ - **GeodesicSplitter** — anisotropic geodesic carve via an EDT-derived speed
17
+ field. Default technique; preserves the neck-aware behavior used in
18
+ PyChunkedGraph today.
19
+ - **WatershedSplitter** — seeded watershed on the distance transform.
20
+ - **NoopSplitter** — reference implementation; documents the protocol shape
21
+ for downstream plugin authors (e.g. a learned splitter).
22
+
23
+ ## Install
24
+
25
+ Requires Python 3.14 or newer.
26
+
27
+ ```
28
+ pip install supervoxel-splitter # core
29
+ pip install "supervoxel-splitter[fast]" # adds dijkstra3d as the geodesic backend
30
+ ```
31
+
32
+ ## The uniform interface
33
+
34
+ Every splitter satisfies the same `Splitter` protocol. Technique-specific
35
+ knobs live on the constructor; `split()` itself stays clean.
36
+
37
+ ```python
38
+ from supervoxel_splitter import GeodesicSplitter
39
+
40
+ splitter = GeodesicSplitter(backend="dj3d")
41
+ result = splitter.split(
42
+ mask=foreground_3d_bool,
43
+ sources=src_seeds_xyz,
44
+ sinks=snk_seeds_xyz,
45
+ voxel_size=(4.0, 4.0, 40.0),
46
+ )
47
+
48
+ # result.labels: uint8 in {0, SOURCE, SINK, STRAY}
49
+ # result.side_of_label: {SOURCE: 1, SINK: 2}
50
+ # result.diagnostics: per-stage timings, label counts, etc.
51
+ ```
52
+
53
+ Plugin authors implement the protocol structurally — no base class to
54
+ inherit:
55
+
56
+ ```python
57
+ class MyLearnedSplitter:
58
+ def __init__(self, *, model_path):
59
+ ...
60
+ def split(self, mask, sources, sinks, *,
61
+ voxel_size=(1.0, 1.0, 1.0),
62
+ vol_order="xyz", vox_order="xyz", seed_order="xyz"):
63
+ ...
64
+ return SplitResult(...)
65
+
66
+ assert isinstance(MyLearnedSplitter(model_path="..."), Splitter)
67
+ ```
68
+
69
+ ## Boundary
70
+
71
+ The package knows nothing about ChunkedGraph, BigTable, IDs, or edges. It
72
+ takes voxels in and gives labels back. New-ID minting, edge updates, and
73
+ persistence are the consumer's responsibility.
74
+
75
+ ## Status
76
+
77
+ v0.1.0 covers `GeodesicSplitter`. `WatershedSplitter` and `NoopSplitter` ship
78
+ as small reference impls demonstrating the protocol; production-quality
79
+ parity with geodesic is out of scope for v0.1.0.
80
+
81
+ ## License
82
+
83
+ MIT.
@@ -0,0 +1,11 @@
1
+ coverage:
2
+ status:
3
+ project:
4
+ default:
5
+ target: auto
6
+ threshold: 1%
7
+ informational: true
8
+ patch:
9
+ default:
10
+ target: 75%
11
+ informational: true
@@ -0,0 +1,54 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "setuptools_scm>=8"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "supervoxel-splitter"
7
+ dynamic = ["version"]
8
+ requires-python = ">=3.14"
9
+ description = "Pluggable supervoxel splitting algorithms with a uniform interface."
10
+ readme = "README.md"
11
+ license = { file = "LICENSE" }
12
+ keywords = [
13
+ "connectomics",
14
+ "segmentation",
15
+ "supervoxel",
16
+ "watershed",
17
+ "geodesic",
18
+ "image-processing",
19
+ ]
20
+ classifiers = [
21
+ "Intended Audience :: Science/Research",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: POSIX :: Linux",
24
+ "Operating System :: MacOS",
25
+ "Programming Language :: Python :: 3",
26
+ "Programming Language :: Python :: 3.14",
27
+ ]
28
+ dependencies = [
29
+ "numpy",
30
+ "scipy>=1.6",
31
+ "fastremap",
32
+ "connected-components-3d",
33
+ "scikit-image",
34
+ "edt",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/CAVEconnectome/supervoxel-splitter"
39
+ Issues = "https://github.com/CAVEconnectome/supervoxel-splitter/issues"
40
+ Source = "https://github.com/CAVEconnectome/supervoxel-splitter"
41
+
42
+ [project.optional-dependencies]
43
+ fast = ["dijkstra3d"]
44
+ learned = []
45
+ dev = ["pytest", "pytest-cov", "black", "pre-commit", "build"]
46
+
47
+ [tool.setuptools.packages.find]
48
+ include = ["supervoxel_splitter*"]
49
+
50
+ [tool.setuptools_scm]
51
+ fallback_version = "0.0.0.dev0"
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """Pluggable supervoxel-splitting algorithms behind a uniform `Splitter`
2
+ protocol. See `api.py` for the protocol shapes and `state.py` for
3
+ `SplitResult`.
4
+ """
5
+
6
+ from .api import (
7
+ SOURCE,
8
+ SINK,
9
+ STRAY,
10
+ Splitter,
11
+ SeedPrep,
12
+ )
13
+ from .geodesic import GeodesicSplitter, RidgeConnectPrep
14
+ from .learned import NoopSplitter
15
+ from .state import SplitResult
16
+ from .watershed import WatershedSplitter
17
+
18
+ __all__ = [
19
+ "SOURCE",
20
+ "SINK",
21
+ "STRAY",
22
+ "Splitter",
23
+ "SeedPrep",
24
+ "SplitResult",
25
+ "GeodesicSplitter",
26
+ "RidgeConnectPrep",
27
+ "WatershedSplitter",
28
+ "NoopSplitter",
29
+ ]