PyNutil 0.5.4__tar.gz → 0.6.2__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.
- pynutil-0.6.2/.gitattributes +2 -0
- pynutil-0.6.2/.github/workflows/benchmark.yml +35 -0
- pynutil-0.6.2/.github/workflows/build.yml +110 -0
- pynutil-0.6.2/.github/workflows/main.yml +21 -0
- pynutil-0.6.2/.github/workflows/publish-to-pypi.yml +30 -0
- pynutil-0.6.2/.github/workflows/tests.yml +33 -0
- pynutil-0.6.2/.gitignore +177 -0
- pynutil-0.6.2/MANIFEST.in +1 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PKG-INFO +14 -12
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/__init__.py +16 -5
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/context.py +10 -26
- pynutil-0.6.2/PyNutil/image_series.py +91 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/atlas_loader.py +4 -27
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/file_operations.py +29 -21
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/loaders.py +8 -4
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/nifti_writer.py +11 -10
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/section_visualisation.py +8 -10
- pynutil-0.6.2/PyNutil/io/volume_nifti.py +136 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/anchoring.py +11 -7
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/base.py +22 -10
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/damage.py +18 -10
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/deformation.py +37 -19
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/registry.py +42 -28
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/segmentation.py +32 -20
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/visualign_deformations.py +89 -50
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/analysis/data_analysis.py +93 -52
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/analysis/region_counting.py +22 -13
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/atlas_map.py +130 -120
- pynutil-0.6.2/PyNutil/processing/pipeline/batch_processor.py +769 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/pipeline/connected_components.py +60 -34
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/pipeline/section_processor.py +76 -68
- pynutil-0.6.2/PyNutil/processing/reorientation.py +161 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/section_volume.py +119 -64
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/utils.py +62 -32
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/results/__init__.py +2 -0
- pynutil-0.6.2/PyNutil/results/atlas.py +17 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/results/extraction.py +56 -2
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/results/section.py +4 -4
- pynutil-0.6.2/PyNutil/results/volume.py +27 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil.egg-info/PKG-INFO +14 -12
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil.egg-info/SOURCES.txt +45 -2
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil.egg-info/requires.txt +4 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/README.md +0 -3
- pynutil-0.6.2/benchmarks/benchmark.py +410 -0
- pynutil-0.6.2/benchmarks/profile_detailed.py +52 -0
- pynutil-0.6.2/demos/basic_example.py +33 -0
- pynutil-0.6.2/demos/basic_example_custom_atlas.py +39 -0
- pynutil-0.6.2/demos/basic_example_intensity.py +32 -0
- pynutil-0.6.2/demos/brainglobe_coordinate_example.py +26 -0
- pynutil-0.6.2/demos/brainglobe_registration_usage.py +29 -0
- pynutil-0.6.2/demos/coordinate_example.py +33 -0
- pynutil-0.6.2/demos/per_section_example.py +37 -0
- pynutil-0.6.2/demos/plot_cells_in_brainrender.py +27 -0
- pynutil-0.6.2/docs/assets/MeshView.mp4 +0 -0
- pynutil-0.6.2/docs/assets/PyNutil_fig1.png +0 -0
- pynutil-0.6.2/docs/assets/Siibra.mp4 +0 -0
- pynutil-0.6.2/gui/Logo_PyNutil.ico +0 -0
- pynutil-0.6.2/gui/Logo_PyNutil.png +0 -0
- pynutil-0.6.2/gui/PyNutilGUI.py +771 -0
- pynutil-0.6.2/gui/gui_smoke_test.py +34 -0
- pynutil-0.6.2/gui/log_manager.py +71 -0
- pynutil-0.6.2/gui/settings_manager.py +81 -0
- pynutil-0.6.2/gui/ui_components.py +230 -0
- pynutil-0.6.2/gui/validation.py +67 -0
- pynutil-0.6.2/gui/workers.py +186 -0
- pynutil-0.6.2/misc/PyNutil_test.json +285 -0
- pynutil-0.6.2/misc/PyNutil_test.waln +1 -0
- pynutil-0.6.2/misc/Tiling_script.py +24 -0
- pynutil-0.6.2/misc/create_test_data.py +64 -0
- pynutil-0.6.2/misc/download_and_pack_atlases_allen.py +43 -0
- pynutil-0.6.2/misc/download_and_pack_atlases_waxholm.py +69 -0
- pynutil-0.6.2/misc/implement_allen_downsampling.py +46 -0
- pynutil-0.6.2/misc/make_white_images.py +8 -0
- pynutil-0.6.2/misc/reformat_label_files.py +153 -0
- pynutil-0.6.2/misc/reorient_allen_volume.py +15 -0
- pynutil-0.6.2/misc/waln_to_json.py +47 -0
- pynutil-0.6.2/pyproject.toml +44 -0
- pynutil-0.5.4/PyNutil/io/volume_nifti.py +0 -91
- pynutil-0.5.4/PyNutil/processing/pipeline/batch_processor.py +0 -508
- pynutil-0.5.4/PyNutil/results/atlas.py +0 -17
- pynutil-0.5.4/setup.py +0 -32
- pynutil-0.5.4/tests/test_helpers.py +0 -79
- {pynutil-0.5.4 → pynutil-0.6.2}/LICENSE +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/config.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/__init__.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/colormap.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/meshview_writer.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/io/reconstruct_dzi.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/logging_utils.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/__init__.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/adapters/__init__.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/analysis/__init__.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/analysis/aggregator.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil/processing/pipeline/__init__.py +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil.egg-info/dependency_links.txt +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/PyNutil.egg-info/top_level.txt +0 -0
- {pynutil-0.5.4 → pynutil-0.6.2}/setup.cfg +0 -0
|
@@ -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
|
pynutil-0.6.2/.gitignore
ADDED
|
@@ -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.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: a package to quantify atlas registered brain data
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,13 @@
|
|
|
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
|
|
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,
|
|
8
|
+
read_segmentation_dir,
|
|
9
|
+
read_image,
|
|
10
|
+
read_image_dir,
|
|
6
11
|
seg_to_coords,
|
|
7
12
|
image_to_coords,
|
|
8
13
|
xy_to_coords,
|
|
@@ -10,7 +15,7 @@ from .processing.pipeline.batch_processor import (
|
|
|
10
15
|
from .processing.analysis.data_analysis import quantify_coords
|
|
11
16
|
from .io.file_operations import save_analysis
|
|
12
17
|
from .processing.section_volume import interpolate_volume
|
|
13
|
-
from .io.volume_nifti import
|
|
18
|
+
from .io.volume_nifti import save_volumes
|
|
14
19
|
|
|
15
20
|
__all__ = [
|
|
16
21
|
"AtlasData",
|
|
@@ -18,13 +23,19 @@ __all__ = [
|
|
|
18
23
|
"PointSetResult",
|
|
19
24
|
"RegistrationData",
|
|
20
25
|
"read_alignment",
|
|
21
|
-
"load_atlas_data",
|
|
22
26
|
"load_custom_atlas",
|
|
27
|
+
"Section",
|
|
28
|
+
"ImageSeries",
|
|
29
|
+
"read_segmentation",
|
|
30
|
+
"read_segmentation_dir",
|
|
31
|
+
"read_image",
|
|
32
|
+
"read_image_dir",
|
|
23
33
|
"seg_to_coords",
|
|
24
34
|
"image_to_coords",
|
|
25
35
|
"xy_to_coords",
|
|
26
36
|
"quantify_coords",
|
|
27
37
|
"save_analysis",
|
|
38
|
+
"VolumeResult",
|
|
28
39
|
"interpolate_volume",
|
|
29
|
-
"
|
|
40
|
+
"save_volumes",
|
|
30
41
|
]
|
|
@@ -7,13 +7,15 @@ processing layers.
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
-
from typing import
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
122
|
-
|
|
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()]
|