PyNutil 0.5.4__tar.gz → 0.6.1__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 (96) hide show
  1. pynutil-0.6.1/.gitattributes +2 -0
  2. pynutil-0.6.1/.github/workflows/benchmark.yml +35 -0
  3. pynutil-0.6.1/.github/workflows/build.yml +110 -0
  4. pynutil-0.6.1/.github/workflows/main.yml +21 -0
  5. pynutil-0.6.1/.github/workflows/publish-to-pypi.yml +30 -0
  6. pynutil-0.6.1/.github/workflows/tests.yml +33 -0
  7. pynutil-0.6.1/.gitignore +177 -0
  8. pynutil-0.6.1/MANIFEST.in +1 -0
  9. {pynutil-0.5.4 → pynutil-0.6.1}/PKG-INFO +14 -12
  10. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/__init__.py +12 -5
  11. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/context.py +10 -26
  12. pynutil-0.6.1/PyNutil/image_series.py +91 -0
  13. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/atlas_loader.py +4 -27
  14. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/file_operations.py +8 -2
  15. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/nifti_writer.py +1 -5
  16. pynutil-0.6.1/PyNutil/io/volume_nifti.py +136 -0
  17. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/anchoring.py +11 -7
  18. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/deformation.py +21 -11
  19. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/segmentation.py +3 -6
  20. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/analysis/data_analysis.py +37 -22
  21. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/analysis/region_counting.py +5 -4
  22. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/atlas_map.py +23 -64
  23. pynutil-0.6.1/PyNutil/processing/pipeline/batch_processor.py +681 -0
  24. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/pipeline/section_processor.py +20 -39
  25. pynutil-0.6.1/PyNutil/processing/reorientation.py +143 -0
  26. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/section_volume.py +119 -64
  27. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/results/__init__.py +2 -0
  28. pynutil-0.6.1/PyNutil/results/atlas.py +17 -0
  29. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/results/extraction.py +52 -2
  30. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/results/section.py +4 -4
  31. pynutil-0.6.1/PyNutil/results/volume.py +27 -0
  32. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil.egg-info/PKG-INFO +14 -12
  33. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil.egg-info/SOURCES.txt +44 -2
  34. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil.egg-info/requires.txt +4 -0
  35. {pynutil-0.5.4 → pynutil-0.6.1}/README.md +0 -3
  36. pynutil-0.6.1/benchmarks/benchmark.py +408 -0
  37. pynutil-0.6.1/benchmarks/profile_detailed.py +52 -0
  38. pynutil-0.6.1/demos/basic_example.py +33 -0
  39. pynutil-0.6.1/demos/basic_example_custom_atlas.py +39 -0
  40. pynutil-0.6.1/demos/basic_example_intensity.py +32 -0
  41. pynutil-0.6.1/demos/brainglobe_coordinate_example.py +26 -0
  42. pynutil-0.6.1/demos/brainglobe_registration_usage.py +29 -0
  43. pynutil-0.6.1/demos/coordinate_example.py +33 -0
  44. pynutil-0.6.1/demos/plot_cells_in_brainrender.py +27 -0
  45. pynutil-0.6.1/docs/assets/MeshView.mp4 +0 -0
  46. pynutil-0.6.1/docs/assets/PyNutil_fig1.png +0 -0
  47. pynutil-0.6.1/docs/assets/Siibra.mp4 +0 -0
  48. pynutil-0.6.1/gui/Logo_PyNutil.ico +0 -0
  49. pynutil-0.6.1/gui/Logo_PyNutil.png +0 -0
  50. pynutil-0.6.1/gui/PyNutilGUI.py +771 -0
  51. pynutil-0.6.1/gui/gui_smoke_test.py +34 -0
  52. pynutil-0.6.1/gui/log_manager.py +65 -0
  53. pynutil-0.6.1/gui/settings_manager.py +76 -0
  54. pynutil-0.6.1/gui/ui_components.py +230 -0
  55. pynutil-0.6.1/gui/validation.py +67 -0
  56. pynutil-0.6.1/gui/workers.py +186 -0
  57. pynutil-0.6.1/misc/PyNutil_test.json +285 -0
  58. pynutil-0.6.1/misc/PyNutil_test.waln +1 -0
  59. pynutil-0.6.1/misc/Tiling_script.py +24 -0
  60. pynutil-0.6.1/misc/create_test_data.py +64 -0
  61. pynutil-0.6.1/misc/download_and_pack_atlases_allen.py +43 -0
  62. pynutil-0.6.1/misc/download_and_pack_atlases_waxholm.py +69 -0
  63. pynutil-0.6.1/misc/implement_allen_downsampling.py +46 -0
  64. pynutil-0.6.1/misc/make_white_images.py +8 -0
  65. pynutil-0.6.1/misc/reformat_label_files.py +153 -0
  66. pynutil-0.6.1/misc/reorient_allen_volume.py +15 -0
  67. pynutil-0.6.1/misc/waln_to_json.py +47 -0
  68. pynutil-0.6.1/pyproject.toml +44 -0
  69. pynutil-0.5.4/PyNutil/io/volume_nifti.py +0 -91
  70. pynutil-0.5.4/PyNutil/processing/pipeline/batch_processor.py +0 -508
  71. pynutil-0.5.4/PyNutil/results/atlas.py +0 -17
  72. pynutil-0.5.4/setup.py +0 -32
  73. pynutil-0.5.4/tests/test_helpers.py +0 -79
  74. {pynutil-0.5.4 → pynutil-0.6.1}/LICENSE +0 -0
  75. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/config.py +0 -0
  76. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/__init__.py +0 -0
  77. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/colormap.py +0 -0
  78. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/loaders.py +0 -0
  79. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/meshview_writer.py +0 -0
  80. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/reconstruct_dzi.py +0 -0
  81. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/io/section_visualisation.py +0 -0
  82. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/logging_utils.py +0 -0
  83. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/__init__.py +0 -0
  84. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/__init__.py +0 -0
  85. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/base.py +0 -0
  86. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/damage.py +0 -0
  87. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/registry.py +0 -0
  88. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/adapters/visualign_deformations.py +0 -0
  89. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/analysis/__init__.py +0 -0
  90. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/analysis/aggregator.py +0 -0
  91. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/pipeline/__init__.py +0 -0
  92. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/pipeline/connected_components.py +0 -0
  93. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil/processing/utils.py +0 -0
  94. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil.egg-info/dependency_links.txt +0 -0
  95. {pynutil-0.5.4 → pynutil-0.6.1}/PyNutil.egg-info/top_level.txt +0 -0
  96. {pynutil-0.5.4 → pynutil-0.6.1}/setup.cfg +0 -0
@@ -0,0 +1,2 @@
1
+ # Auto detect text files and perform LF normalization
2
+ * text=auto
@@ -0,0 +1,35 @@
1
+ name: Benchmark
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ benchmark:
7
+ runs-on: ubuntu-latest
8
+
9
+ steps:
10
+ - name: Checkout code
11
+ uses: actions/checkout@v4
12
+
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v5
15
+ with:
16
+ python-version: '3.12'
17
+
18
+ - name: Install dependencies
19
+ run: |
20
+ python -m pip install --upgrade pip
21
+ pip install .
22
+
23
+ - name: Run benchmark
24
+ run: python benchmarks/benchmark.py 2>&1
25
+
26
+ - name: Write job summary
27
+ run: cat benchmarks/results.md >> "$GITHUB_STEP_SUMMARY"
28
+
29
+ - name: Upload benchmark results
30
+ uses: actions/upload-artifact@v4
31
+ with:
32
+ name: benchmark-results
33
+ path: |
34
+ benchmarks/results.md
35
+ benchmarks/results.json
@@ -0,0 +1,110 @@
1
+ name: Build Application
2
+
3
+ on:
4
+ release:
5
+ types: [published] # Only trigger on published releases
6
+ workflow_dispatch: # Allow manual triggering
7
+
8
+ # Add global permissions
9
+ permissions:
10
+ contents: write
11
+
12
+ jobs:
13
+ build-windows:
14
+ runs-on: windows-latest
15
+ steps:
16
+ - uses: actions/checkout@v3
17
+
18
+ - name: Set up Python 3.10
19
+ uses: actions/setup-python@v4
20
+ with:
21
+ python-version: '3.10'
22
+
23
+ - name: Install dependencies
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install pyinstaller
27
+ pip install ".[gui]"
28
+ - name: Build with PyInstaller
29
+ run: |
30
+ pyinstaller --windowed --icon=gui/Logo_PyNutil.ico --name PyNutil gui/PyNutilGUI.py
31
+
32
+ - name: Create Windows ZIP archive
33
+ run: |
34
+ cd dist
35
+ powershell Compress-Archive -Path PyNutil -DestinationPath PyNutil-Windows.zip
36
+
37
+ - name: Upload Windows artifact
38
+ uses: actions/upload-artifact@v4
39
+ with:
40
+ name: PyNutil-Windows
41
+ path: dist/PyNutil-Windows.zip
42
+ retention-days: 5
43
+
44
+ build-macos:
45
+ runs-on: macos-latest
46
+ steps:
47
+ - uses: actions/checkout@v3
48
+
49
+ - name: Set up Python 3.10
50
+ uses: actions/setup-python@v4
51
+ with:
52
+ python-version: '3.10'
53
+
54
+ - name: Install dependencies
55
+ run: |
56
+ python -m pip install --upgrade pip
57
+ pip install pyinstaller
58
+ pip install dmgbuild
59
+ pip install ".[gui]"
60
+ - name: Build with PyInstaller
61
+ run: |
62
+ # Build without specifying an icon for macOS to avoid format issues
63
+ pyinstaller --windowed --osx-bundle-identifier com.pynutil.app --name PyNutil gui/PyNutilGUI.py
64
+
65
+ - name: Create DMG
66
+ run: |
67
+ pip install dmgbuild
68
+ cat > dmg_settings.py << EOF
69
+ app = 'dist/PyNutil.app'
70
+ appname = 'PyNutil'
71
+ format = 'UDBZ'
72
+ size = '500M'
73
+ files = [app]
74
+ symlinks = {'Applications': '/Applications'}
75
+ # Remove badge_icon to avoid icon format issues
76
+ icon_locations = {
77
+ appname + '.app': (140, 120),
78
+ 'Applications': (360, 120)
79
+ }
80
+ background = 'builtin-arrow'
81
+ EOF
82
+ dmgbuild -s dmg_settings.py "PyNutil" "dist/PyNutil-macOS.dmg" || true
83
+
84
+ - name: Upload DMG artifact
85
+ uses: actions/upload-artifact@v4
86
+ with:
87
+ name: PyNutil-macOS
88
+ path: dist/PyNutil-macOS.dmg
89
+ retention-days: 5
90
+
91
+ create-release:
92
+ needs: [build-windows, build-macos]
93
+ runs-on: ubuntu-latest
94
+ permissions:
95
+ contents: write
96
+ steps:
97
+ - name: Download all artifacts
98
+ uses: actions/download-artifact@v4
99
+
100
+ - name: List downloaded artifacts
101
+ run: |
102
+ find . -type f | sort
103
+
104
+ - name: Attach Artifacts to Release
105
+ uses: softprops/action-gh-release@v1
106
+ with:
107
+ files: |
108
+ PyNutil-Windows/PyNutil-Windows.zip
109
+ PyNutil-macOS/PyNutil-macOS.dmg
110
+ token: ${{ github.token }}
@@ -0,0 +1,21 @@
1
+ name: Mirror to Ebrains
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+
7
+
8
+ jobs:
9
+ # set the job name
10
+ to_ebrains:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+
14
+ - name: syncmain
15
+ uses: wei/git-sync@v3
16
+
17
+ with:
18
+ source_repo: "Neural-Systems-at-UIO/PyNutil"
19
+ source_branch: "main"
20
+ destination_repo: "https://ghpusher:${{ secrets.EBRAINS_GITLAB_ACCESS_TOKEN }}@gitlab.ebrains.eu/polarbean/PyNutil.git"
21
+ destination_branch: "main"
@@ -0,0 +1,30 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [created]
6
+
7
+ jobs:
8
+ deploy:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ id-token: write # Required for trusted publisher authentication
12
+ contents: read # Required for checkout
13
+ steps:
14
+ - uses: actions/checkout@v3
15
+ with:
16
+ fetch-depth: 0
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v4
19
+ with:
20
+ python-version: '3.x'
21
+ - name: Install dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install build
25
+ - name: Build package
26
+ run: python -m build
27
+ - name: Publish package
28
+ uses: pypa/gh-action-pypi-publish@release/v1
29
+ with:
30
+ verbose: true
@@ -0,0 +1,33 @@
1
+ name: Run Tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Checkout code
15
+ uses: actions/checkout@v2
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v2
19
+ with:
20
+ python-version: '3.x' # Specify your Python version
21
+
22
+ - name: Install dependencies
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ pip install .
26
+
27
+ - name: Download atlas
28
+ run: |
29
+ brainglobe install -a allen_mouse_25um
30
+
31
+ - name: Run tests
32
+ run: |
33
+ python -m unittest discover -s tests -v
@@ -0,0 +1,177 @@
1
+ dmg.py
2
+ # dev environment
3
+ .vscode
4
+ .claude
5
+ .complexipy_cache
6
+ .# Byte-compiled / optimized / DLL files
7
+ __pycache__/
8
+ *.py[cod]
9
+ *$py.class
10
+ demo_data/*
11
+ # C extensions
12
+ *.so
13
+ # WSL zone identifiers
14
+ *:Zone.Identifier*
15
+ *:encryptable*
16
+ # Distribution / packaging
17
+ .Python
18
+ build/
19
+ develop-eggs/
20
+ dist/
21
+ downloads/
22
+ eggs/
23
+ .eggs/
24
+ lib/
25
+ lib64/
26
+ parts/
27
+ sdist/
28
+ var/
29
+ wheels/
30
+ share/python-wheels/
31
+ *.egg-info/
32
+ .installed.cfg
33
+ *.egg
34
+ MANIFEST
35
+
36
+ # PyInstaller
37
+ # Usually these files are written by a python script from a template
38
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
39
+ *.manifest
40
+ *.spec
41
+
42
+ # Installer logs
43
+ pip-log.txt
44
+ pip-delete-this-directory.txt
45
+
46
+ # Unit test / coverage reports
47
+ htmlcov/
48
+ .tox/
49
+ .nox/
50
+ .coverage
51
+ .coverage.*
52
+ .cache
53
+ nosetests.xml
54
+ coverage.xml
55
+ *.cover
56
+ *.py,cover
57
+ .hypothesis/
58
+ .pytest_cache/
59
+ cover/
60
+
61
+ # Translations
62
+ *.mo
63
+ *.pot
64
+
65
+ # Django stuff:
66
+ *.log
67
+ local_settings.py
68
+ db.sqlite3
69
+ db.sqlite3-journal
70
+
71
+ # Flask stuff:
72
+ instance/
73
+ .webassets-cache
74
+
75
+ # Scrapy stuff:
76
+ .scrapy
77
+
78
+ # Sphinx documentation
79
+ docs/_build/
80
+
81
+ # PyBuilder
82
+ .pybuilder/
83
+ target/
84
+
85
+ # Jupyter Notebook
86
+ .ipynb_checkpoints
87
+
88
+ # IPython
89
+ profile_default/
90
+ ipython_config.py
91
+
92
+ # pyenv
93
+ # For a library or package, you might want to ignore these files since the code is
94
+ # intended to run in multiple environments; otherwise, check them in:
95
+ # .python-version
96
+
97
+ # pipenv
98
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
99
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
100
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
101
+ # install all needed dependencies.
102
+ #Pipfile.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ #poetry.lock
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ #pdm.lock
114
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
115
+ # in version control.
116
+ # https://pdm.fming.dev/#use-with-ide
117
+ .pdm.toml
118
+
119
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
120
+ __pypackages__/
121
+
122
+ # Celery stuff
123
+ celerybeat-schedule
124
+ celerybeat.pid
125
+
126
+ # SageMath parsed files
127
+ *.sage.py
128
+
129
+ # Environments
130
+ .env
131
+ .venv
132
+ env/
133
+ venv/
134
+ ENV/
135
+ env.bak/
136
+ venv.bak/
137
+
138
+ # Spyder project settings
139
+ .spyderproject
140
+ .spyproject
141
+
142
+ # Rope project settings
143
+ .ropeproject
144
+
145
+ # mkdocs documentation
146
+ /site
147
+
148
+ # mypy
149
+ .mypy_cache/
150
+ .dmypy.json
151
+ dmypy.json
152
+
153
+ # Pyre type checker
154
+ .pyre/
155
+
156
+ # pytype static type analyzer
157
+ .pytype/
158
+
159
+ # Cython debug symbols
160
+ cython_debug/
161
+
162
+ # PyCharm
163
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
164
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
165
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
166
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
167
+ #.idea/
168
+
169
+
170
+ #inputs
171
+
172
+ #outputs
173
+ test_result/
174
+
175
+ # Benchmark generated data
176
+ benchmarks/results.json
177
+ benchmarks/results.md
@@ -0,0 +1 @@
1
+ prune tests
@@ -1,9 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyNutil
3
- Version: 0.5.4
3
+ Version: 0.6.1
4
4
  Summary: a package to quantify atlas registered brain data
5
- Home-page: https://github.com/Neural-Systems-at-UIO/PyNutil
6
- License: MIT
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/Neural-Systems-at-UIO/PyNutil
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Scientific/Engineering
12
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
13
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
14
+ Requires-Python: >=3.8
7
15
  Description-Content-Type: text/markdown
8
16
  License-File: LICENSE
9
17
  Requires-Dist: numpy
@@ -16,13 +24,10 @@ Requires-Dist: scipy
16
24
  Requires-Dist: nibabel
17
25
  Requires-Dist: orjson
18
26
  Requires-Dist: tqdm
19
- Dynamic: description
20
- Dynamic: description-content-type
21
- Dynamic: home-page
22
- Dynamic: license
27
+ Requires-Dist: tifffile
28
+ Provides-Extra: gui
29
+ Requires-Dist: PyQt6; extra == "gui"
23
30
  Dynamic: license-file
24
- Dynamic: requires-dist
25
- Dynamic: summary
26
31
 
27
32
  # PyNutil
28
33
 
@@ -151,9 +156,6 @@ PyNutil is developed at the Neural Systems Laboratory at the Institute of Basic
151
156
  # Contributors
152
157
  Harry Carey, Sharon C Yates, Gergely Csucs, Arda Balkir, Ingvild Bjerke, Rembrandt Bakker, Nicolaas Groeneboom, Maja A Puchades, Jan G Bjaalie.
153
158
 
154
- # Licence
155
- GNU General Public License v3
156
-
157
159
  # Related articles
158
160
  Yates SC, Groeneboom NE, Coello C, et al. & Bjaalie JG (2019) QUINT: Workflow for Quantification and Spatial Analysis of Features in Histological Images From Rodent Brain. Front. Neuroinform. 13:75. https://doi.org/10.3389/fninf.2019.00075
159
161
 
@@ -1,8 +1,11 @@
1
- from .results import AtlasData, ExtractionResult, PointSetResult
1
+ from .results import AtlasData, ExtractionResult, PointSetResult, VolumeResult
2
2
  from .processing.adapters.base import RegistrationData
3
3
  from .processing.adapters import read_alignment
4
- from .io.atlas_loader import load_atlas_data, load_custom_atlas
4
+ from .io.atlas_loader import load_custom_atlas
5
+ from .image_series import Section, ImageSeries
5
6
  from .processing.pipeline.batch_processor import (
7
+ read_segmentation_dir,
8
+ read_image_dir,
6
9
  seg_to_coords,
7
10
  image_to_coords,
8
11
  xy_to_coords,
@@ -10,7 +13,7 @@ from .processing.pipeline.batch_processor import (
10
13
  from .processing.analysis.data_analysis import quantify_coords
11
14
  from .io.file_operations import save_analysis
12
15
  from .processing.section_volume import interpolate_volume
13
- from .io.volume_nifti import save_volume_niftis
16
+ from .io.volume_nifti import save_volumes
14
17
 
15
18
  __all__ = [
16
19
  "AtlasData",
@@ -18,13 +21,17 @@ __all__ = [
18
21
  "PointSetResult",
19
22
  "RegistrationData",
20
23
  "read_alignment",
21
- "load_atlas_data",
22
24
  "load_custom_atlas",
25
+ "Section",
26
+ "ImageSeries",
27
+ "read_segmentation_dir",
28
+ "read_image_dir",
23
29
  "seg_to_coords",
24
30
  "image_to_coords",
25
31
  "xy_to_coords",
26
32
  "quantify_coords",
27
33
  "save_analysis",
34
+ "VolumeResult",
28
35
  "interpolate_volume",
29
- "save_volume_niftis",
36
+ "save_volumes",
30
37
  ]
@@ -7,13 +7,15 @@ processing layers.
7
7
  from __future__ import annotations
8
8
 
9
9
  from dataclasses import dataclass
10
- from typing import List, Optional, Union
10
+ from typing import TYPE_CHECKING, Optional
11
11
 
12
12
  import numpy as np
13
13
  import pandas as pd
14
14
 
15
15
  from .processing.adapters.base import SliceInfo
16
- from .processing.adapters.segmentation import SegmentationAdapter
16
+
17
+ if TYPE_CHECKING:
18
+ from .processing.adapters.segmentation import SegmentationAdapter
17
19
 
18
20
 
19
21
  @dataclass(frozen=True)
@@ -30,16 +32,10 @@ class PipelineContext:
30
32
  3-D hemisphere mask (same shape as *atlas_volume*).
31
33
  segmentation_adapter : SegmentationAdapter
32
34
  Pre-resolved adapter for the current segmentation format.
33
- non_linear : bool
34
- Whether to apply non-linear deformation.
35
35
  object_cutoff : int
36
36
  Minimum connected-component area (binary pipeline).
37
- use_flat : bool
38
- If True, load flat-file atlas maps instead of slicing the volume.
39
37
  pixel_id : object
40
38
  Pixel colour to match, or ``"auto"`` for auto-detection.
41
- apply_damage_mask : bool
42
- Apply the damage / exclusion mask when available.
43
39
  intensity_channel : str or None
44
40
  Channel name for the intensity pipeline (``None`` in binary mode).
45
41
  min_intensity : int or None
@@ -52,12 +48,8 @@ class PipelineContext:
52
48
  atlas_volume: Optional[np.ndarray]
53
49
  hemi_map: Optional[np.ndarray]
54
50
  segmentation_adapter: SegmentationAdapter
55
- non_linear: bool
56
51
  object_cutoff: int
57
- use_flat: bool
58
52
  pixel_id: object
59
- apply_damage_mask: bool
60
- flat_label_path: Optional[str] = None
61
53
  intensity_channel: Optional[str] = None
62
54
  min_intensity: Optional[int] = None
63
55
  max_intensity: Optional[int] = None
@@ -70,12 +62,8 @@ class PipelineContext:
70
62
  atlas_labels,
71
63
  atlas_volume,
72
64
  hemi_map,
73
- non_linear: bool,
74
65
  object_cutoff: int,
75
- use_flat: bool,
76
66
  pixel_id,
77
- apply_damage_mask: bool,
78
- flat_label_path=None,
79
67
  intensity_channel=None,
80
68
  min_intensity=None,
81
69
  max_intensity=None,
@@ -88,12 +76,8 @@ class PipelineContext:
88
76
  atlas_volume=atlas_volume,
89
77
  hemi_map=hemi_map,
90
78
  segmentation_adapter=SegmentationAdapterRegistry.get(segmentation_format),
91
- non_linear=non_linear,
92
79
  object_cutoff=object_cutoff,
93
- use_flat=use_flat,
94
80
  pixel_id=pixel_id,
95
- apply_damage_mask=apply_damage_mask,
96
- flat_label_path=flat_label_path,
97
81
  intensity_channel=intensity_channel,
98
82
  min_intensity=min_intensity,
99
83
  max_intensity=max_intensity,
@@ -110,13 +94,13 @@ class SectionContext:
110
94
  Numeric section identifier matching the alignment JSON.
111
95
  slice_info : SliceInfo
112
96
  Registration data for this section (anchoring, deformation, damage …).
113
- segmentation_path : str
114
- Path to the segmentation / image file on disk.
115
- flat_file_path : str or None
116
- Path to the corresponding flat-file atlas, if any.
97
+ image : np.ndarray
98
+ Pre-loaded image array for this section.
99
+ filename : str
100
+ Source filename, used for output tracking (empty string if unknown).
117
101
  """
118
102
 
119
103
  section_number: int
120
104
  slice_info: SliceInfo
121
- segmentation_path: str
122
- flat_file_path: Optional[str] = None
105
+ image: np.ndarray
106
+ filename: str = ""
@@ -0,0 +1,91 @@
1
+ """Image series and section data containers.
2
+
3
+ These classes represent a series of sections (images or segmentations) to be
4
+ processed through the PyNutil pipeline. Users with custom segmentation types
5
+ can construct :class:`Section` and :class:`ImageSeries` objects directly,
6
+ providing their own ``numpy`` arrays instead of reading from disk.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import Dict, List, Optional
13
+
14
+ import numpy as np
15
+
16
+
17
+ @dataclass
18
+ class Section:
19
+ """A single section, identified by number and backed by an image.
20
+
21
+ Either *image* or *path* must be provided. When only *path* is given the
22
+ image is loaded lazily the first time it is needed by the pipeline.
23
+
24
+ Parameters
25
+ ----------
26
+ section_number:
27
+ Numeric identifier that must match a section in the alignment JSON.
28
+ filename:
29
+ Display name used in :attr:`~PyNutil.ExtractionResult.section_filenames`.
30
+ Defaults to *path* when not set explicitly.
31
+ image:
32
+ Pre-loaded image array (2-D or 3-D ``numpy`` array). Provide this
33
+ when you have already loaded or generated the image data yourself.
34
+ path:
35
+ Path to the image file on disk. The image is loaded on demand by the
36
+ configured segmentation adapter when the section is processed.
37
+ """
38
+
39
+ section_number: int
40
+ filename: str = ""
41
+ image: Optional[np.ndarray] = field(default=None, repr=False)
42
+ path: Optional[str] = None
43
+
44
+ def get_image(self, adapter) -> np.ndarray:
45
+ """Return the image array, loading from *path* if not pre-loaded.
46
+
47
+ Parameters
48
+ ----------
49
+ adapter:
50
+ A :class:`~PyNutil.processing.adapters.segmentation.SegmentationAdapter`
51
+ used to load the file when *image* is ``None``.
52
+ """
53
+ if self.image is not None:
54
+ return self.image
55
+ if self.path is not None:
56
+ return adapter.load(self.path)
57
+ raise ValueError(
58
+ f"Section {self.section_number} has neither image data nor a file path."
59
+ )
60
+
61
+
62
+ @dataclass
63
+ class ImageSeries:
64
+ """An ordered collection of :class:`Section` objects.
65
+
66
+ Construct this directly when you want to supply custom image data, or use
67
+ :func:`~PyNutil.read_segmentation_dir` / :func:`~PyNutil.read_image_dir`
68
+ to build one from a folder of image files.
69
+
70
+ Parameters
71
+ ----------
72
+ sections:
73
+ Dictionary mapping ``section_number`` to :class:`Section` objects.
74
+ pixel_id:
75
+ RGB value (or label) identifying the segmented class of interest.
76
+ Set by :func:`~PyNutil.read_segmentation_dir` and consumed by
77
+ :func:`~PyNutil.seg_to_coords`.
78
+ segmentation_format:
79
+ Name of the segmentation adapter to use (e.g. ``"binary"`` or
80
+ ``"cellpose"``). Set by :func:`~PyNutil.read_segmentation_dir` and
81
+ consumed by :func:`~PyNutil.seg_to_coords`.
82
+ """
83
+
84
+ sections: Dict[int, Section] = field(default_factory=dict)
85
+ pixel_id: object = field(default_factory=lambda: [0, 0, 0])
86
+ segmentation_format: str = "binary"
87
+
88
+ @property
89
+ def filenames(self) -> List[str]:
90
+ """Display filenames for all sections."""
91
+ return [s.filename for s in self.sections.values()]