PyNutil 0.5.3__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 (107) 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.3 → pynutil-0.6.1}/PKG-INFO +38 -40
  10. pynutil-0.6.1/PyNutil/__init__.py +37 -0
  11. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/config.py +3 -3
  12. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/context.py +39 -18
  13. pynutil-0.6.1/PyNutil/image_series.py +91 -0
  14. pynutil-0.6.1/PyNutil/io/__init__.py +13 -0
  15. pynutil-0.6.1/PyNutil/io/atlas_loader.py +108 -0
  16. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/colormap.py +0 -23
  17. pynutil-0.6.1/PyNutil/io/file_operations.py +177 -0
  18. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/loaders.py +49 -143
  19. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/meshview_writer.py +134 -123
  20. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/nifti_writer.py +1 -5
  21. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/reconstruct_dzi.py +3 -5
  22. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/io/section_visualisation.py +48 -62
  23. pynutil-0.6.1/PyNutil/io/volume_nifti.py +136 -0
  24. pynutil-0.6.1/PyNutil/processing/__init__.py +17 -0
  25. pynutil-0.6.1/PyNutil/processing/adapters/__init__.py +26 -0
  26. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/anchoring.py +38 -27
  27. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/base.py +26 -1
  28. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/damage.py +26 -57
  29. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/deformation.py +55 -21
  30. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/registry.py +4 -5
  31. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/segmentation.py +57 -21
  32. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/adapters/visualign_deformations.py +67 -60
  33. pynutil-0.6.1/PyNutil/processing/analysis/data_analysis.py +279 -0
  34. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/analysis/region_counting.py +48 -72
  35. pynutil-0.6.1/PyNutil/processing/atlas_map.py +453 -0
  36. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/pipeline/__init__.py +4 -4
  37. pynutil-0.6.1/PyNutil/processing/pipeline/batch_processor.py +681 -0
  38. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/pipeline/connected_components.py +34 -42
  39. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/pipeline/section_processor.py +179 -219
  40. pynutil-0.6.1/PyNutil/processing/reorientation.py +143 -0
  41. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/section_volume.py +247 -149
  42. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/utils.py +17 -73
  43. pynutil-0.6.1/PyNutil/results/__init__.py +15 -0
  44. pynutil-0.6.1/PyNutil/results/atlas.py +17 -0
  45. pynutil-0.6.1/PyNutil/results/extraction.py +128 -0
  46. pynutil-0.6.1/PyNutil/results/section.py +61 -0
  47. pynutil-0.6.1/PyNutil/results/volume.py +27 -0
  48. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil.egg-info/PKG-INFO +38 -40
  49. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil.egg-info/SOURCES.txt +48 -6
  50. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil.egg-info/requires.txt +4 -0
  51. {pynutil-0.5.3 → pynutil-0.6.1}/README.md +24 -31
  52. pynutil-0.6.1/benchmarks/benchmark.py +408 -0
  53. pynutil-0.6.1/benchmarks/profile_detailed.py +52 -0
  54. pynutil-0.6.1/demos/basic_example.py +33 -0
  55. pynutil-0.6.1/demos/basic_example_custom_atlas.py +39 -0
  56. pynutil-0.6.1/demos/basic_example_intensity.py +32 -0
  57. pynutil-0.6.1/demos/brainglobe_coordinate_example.py +26 -0
  58. pynutil-0.6.1/demos/brainglobe_registration_usage.py +29 -0
  59. pynutil-0.6.1/demos/coordinate_example.py +33 -0
  60. pynutil-0.6.1/demos/plot_cells_in_brainrender.py +27 -0
  61. pynutil-0.6.1/docs/assets/MeshView.mp4 +0 -0
  62. pynutil-0.6.1/docs/assets/PyNutil_fig1.png +0 -0
  63. pynutil-0.6.1/docs/assets/Siibra.mp4 +0 -0
  64. pynutil-0.6.1/gui/Logo_PyNutil.ico +0 -0
  65. pynutil-0.6.1/gui/Logo_PyNutil.png +0 -0
  66. pynutil-0.6.1/gui/PyNutilGUI.py +771 -0
  67. pynutil-0.6.1/gui/gui_smoke_test.py +34 -0
  68. pynutil-0.6.1/gui/log_manager.py +65 -0
  69. pynutil-0.6.1/gui/settings_manager.py +76 -0
  70. pynutil-0.6.1/gui/ui_components.py +230 -0
  71. pynutil-0.6.1/gui/validation.py +67 -0
  72. pynutil-0.6.1/gui/workers.py +186 -0
  73. pynutil-0.6.1/misc/PyNutil_test.json +285 -0
  74. pynutil-0.6.1/misc/PyNutil_test.waln +1 -0
  75. pynutil-0.6.1/misc/Tiling_script.py +24 -0
  76. pynutil-0.6.1/misc/create_test_data.py +64 -0
  77. pynutil-0.6.1/misc/download_and_pack_atlases_allen.py +43 -0
  78. pynutil-0.6.1/misc/download_and_pack_atlases_waxholm.py +69 -0
  79. pynutil-0.6.1/misc/implement_allen_downsampling.py +46 -0
  80. pynutil-0.6.1/misc/make_white_images.py +8 -0
  81. pynutil-0.6.1/misc/reformat_label_files.py +153 -0
  82. pynutil-0.6.1/misc/reorient_allen_volume.py +15 -0
  83. pynutil-0.6.1/misc/waln_to_json.py +47 -0
  84. pynutil-0.6.1/pyproject.toml +44 -0
  85. pynutil-0.5.3/PyNutil/__init__.py +0 -1
  86. pynutil-0.5.3/PyNutil/io/__init__.py +0 -63
  87. pynutil-0.5.3/PyNutil/io/atlas_loader.py +0 -87
  88. pynutil-0.5.3/PyNutil/io/file_operations.py +0 -221
  89. pynutil-0.5.3/PyNutil/io/propagation.py +0 -133
  90. pynutil-0.5.3/PyNutil/io/volume_nifti.py +0 -91
  91. pynutil-0.5.3/PyNutil/main.py +0 -720
  92. pynutil-0.5.3/PyNutil/processing/__init__.py +0 -94
  93. pynutil-0.5.3/PyNutil/processing/adapters/__init__.py +0 -98
  94. pynutil-0.5.3/PyNutil/processing/analysis/data_analysis.py +0 -354
  95. pynutil-0.5.3/PyNutil/processing/atlas_map.py +0 -440
  96. pynutil-0.5.3/PyNutil/processing/pipeline/batch_processor.py +0 -546
  97. pynutil-0.5.3/PyNutil/processing/transforms.py +0 -221
  98. pynutil-0.5.3/PyNutil/results.py +0 -127
  99. pynutil-0.5.3/setup.py +0 -32
  100. pynutil-0.5.3/tests/test_helpers.py +0 -23
  101. {pynutil-0.5.3 → pynutil-0.6.1}/LICENSE +0 -0
  102. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/logging_utils.py +0 -0
  103. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/analysis/__init__.py +0 -0
  104. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil/processing/analysis/aggregator.py +0 -0
  105. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil.egg-info/dependency_links.txt +0 -0
  106. {pynutil-0.5.3 → pynutil-0.6.1}/PyNutil.egg-info/top_level.txt +0 -0
  107. {pynutil-0.5.3 → 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.3
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,17 +24,16 @@ 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
 
29
- ## PyNutil is still under development and the API is subject to change
34
+ > [!WARNING]
35
+ > PyNutil is still under development and the API is subject to change.
36
+
30
37
 
31
38
  PyNutil is a Python library for brain-wide quantification and spatial analysis of features in serial section images from the brain. It aims to replicate the Quantifier feature of the Nutil software (RRID: SCR_017183).
32
39
 
@@ -71,37 +78,31 @@ As input, PyNutil requires:
71
78
  3. A segmentation file for each brain section with the features to be quantified displayed with a unique RGB colour code (it currently accepts many image formats: png, jpg, jpeg, etc).
72
79
 
73
80
  ```python
74
- from PyNutil import PyNutil
75
-
76
- """
77
- Here we define a quantifier object
78
- The segmentations should be images which come out of ilastik, segmenting objects-of-interest
79
- The alignment json should be from DeepSlice, QuickNII, or VisuAlign, it defines the sections position in an atlas
80
- The colour says which colour is the object you want to quantify in your segmentation. It is defined in RGB
81
- Finally the atlas name is the relevant atlas from brainglobe_atlasapi or a custom atlas in nrrd format.
82
-
83
- basic_example.py (brainglobe_atlasapi)
84
- basic_example_custom_atlas.py (custom atlas)
85
-
86
- """
87
- pnt = PyNutil(
88
- segmentation_folder='../tests/test_data/non_linear_allen_mouse/segmentations/',
89
- alignment_json='../tests/test_data/non_linear_allen_mouse/alignment.json',
90
- colour=[0, 0, 0],
91
- atlas_name='allen_mouse_25um'
92
- #If you would like to use cellpose segmentations add:
93
- #segmentation_format = 'cellpose'
81
+ from brainglobe_atlasapi import BrainGlobeAtlas
82
+ import PyNutil as pnt
83
+
84
+ # Load an atlas (BrainGlobe) and alignment
85
+ atlas = BrainGlobeAtlas("allen_mouse_25um")
86
+ alignment = pnt.read_alignment("path/to/alignment.json")
87
+
88
+ # Extract coordinates from segmentations
89
+ coords = pnt.seg_to_coords(
90
+ "path/to/segmentations/",
91
+ alignment,
92
+ atlas,
93
+ pixel_id=[0, 0, 0],
94
+ # For cellpose segmentations: segmentation_format="cellpose"
94
95
  )
95
96
 
96
- #optionally, if you want to generate a 3D heatmap
97
- pnt.interpolate_volume()
98
-
99
- pnt.get_coordinates(object_cutoff=0)
100
-
101
- pnt.quantify_coordinates()
97
+ # Quantify by atlas region
98
+ label_df = pnt.quantify_coords(coords, atlas)
102
99
 
103
- pnt.save_analysis("PyNutil/test_result/myResults")
100
+ # Save results
101
+ pnt.save_analysis("path/to/output", coords, atlas, label_df=label_df)
104
102
  ```
103
+
104
+ For custom atlases (not from BrainGlobe), use `pnt.load_custom_atlas()` instead.
105
+ See `demos/basic_example.py` and `demos/basic_example_custom_atlas.py` for complete examples.
105
106
  PyNutil generates a series of reports in the folder which you specify.
106
107
 
107
108
  ## Per-Hemisphere Quantification
@@ -155,9 +156,6 @@ PyNutil is developed at the Neural Systems Laboratory at the Institute of Basic
155
156
  # Contributors
156
157
  Harry Carey, Sharon C Yates, Gergely Csucs, Arda Balkir, Ingvild Bjerke, Rembrandt Bakker, Nicolaas Groeneboom, Maja A Puchades, Jan G Bjaalie.
157
158
 
158
- # Licence
159
- GNU General Public License v3
160
-
161
159
  # Related articles
162
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
163
161
 
@@ -0,0 +1,37 @@
1
+ from .results import AtlasData, ExtractionResult, PointSetResult, VolumeResult
2
+ from .processing.adapters.base import RegistrationData
3
+ from .processing.adapters import read_alignment
4
+ from .io.atlas_loader import load_custom_atlas
5
+ from .image_series import Section, ImageSeries
6
+ from .processing.pipeline.batch_processor import (
7
+ read_segmentation_dir,
8
+ read_image_dir,
9
+ seg_to_coords,
10
+ image_to_coords,
11
+ xy_to_coords,
12
+ )
13
+ from .processing.analysis.data_analysis import quantify_coords
14
+ from .io.file_operations import save_analysis
15
+ from .processing.section_volume import interpolate_volume
16
+ from .io.volume_nifti import save_volumes
17
+
18
+ __all__ = [
19
+ "AtlasData",
20
+ "ExtractionResult",
21
+ "PointSetResult",
22
+ "RegistrationData",
23
+ "read_alignment",
24
+ "load_custom_atlas",
25
+ "Section",
26
+ "ImageSeries",
27
+ "read_segmentation_dir",
28
+ "read_image_dir",
29
+ "seg_to_coords",
30
+ "image_to_coords",
31
+ "xy_to_coords",
32
+ "quantify_coords",
33
+ "save_analysis",
34
+ "VolumeResult",
35
+ "interpolate_volume",
36
+ "save_volumes",
37
+ ]
@@ -1,9 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  from dataclasses import dataclass
5
4
  from typing import Any, Dict, Optional
6
5
 
6
+ from .io.loaders import load_json_file
7
+
7
8
 
8
9
  @dataclass
9
10
  class PyNutilConfig:
@@ -25,8 +26,7 @@ class PyNutilConfig:
25
26
 
26
27
  @classmethod
27
28
  def from_settings_file(cls, settings_file: str) -> "PyNutilConfig":
28
- with open(settings_file, "r") as f:
29
- settings = json.load(f)
29
+ settings = load_json_file(settings_file)
30
30
  return cls.from_settings_dict(settings)
31
31
 
32
32
  @classmethod
@@ -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,16 +48,41 @@ 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
64
56
 
57
+ @classmethod
58
+ def from_format(
59
+ cls,
60
+ *,
61
+ segmentation_format: str,
62
+ atlas_labels,
63
+ atlas_volume,
64
+ hemi_map,
65
+ object_cutoff: int,
66
+ pixel_id,
67
+ intensity_channel=None,
68
+ min_intensity=None,
69
+ max_intensity=None,
70
+ ) -> "PipelineContext":
71
+ """Construct a PipelineContext, resolving *segmentation_format* to an adapter."""
72
+ from .processing.adapters.segmentation import SegmentationAdapterRegistry
73
+
74
+ return cls(
75
+ atlas_labels=atlas_labels,
76
+ atlas_volume=atlas_volume,
77
+ hemi_map=hemi_map,
78
+ segmentation_adapter=SegmentationAdapterRegistry.get(segmentation_format),
79
+ object_cutoff=object_cutoff,
80
+ pixel_id=pixel_id,
81
+ intensity_channel=intensity_channel,
82
+ min_intensity=min_intensity,
83
+ max_intensity=max_intensity,
84
+ )
85
+
65
86
 
66
87
  @dataclass(frozen=True)
67
88
  class SectionContext:
@@ -73,13 +94,13 @@ class SectionContext:
73
94
  Numeric section identifier matching the alignment JSON.
74
95
  slice_info : SliceInfo
75
96
  Registration data for this section (anchoring, deformation, damage …).
76
- segmentation_path : str
77
- Path to the segmentation / image file on disk.
78
- flat_file_path : str or None
79
- 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).
80
101
  """
81
102
 
82
103
  section_number: int
83
104
  slice_info: SliceInfo
84
- segmentation_path: str
85
- flat_file_path: Optional[str] = None
105
+ image: np.ndarray
106
+ filename: str = ""