pykrait 0.3.2__tar.gz → 0.4.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.
- pykrait-0.4.0/.gitignore +63 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/LICENSE +1 -1
- pykrait-0.4.0/PKG-INFO +178 -0
- pykrait-0.4.0/README.md +123 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/pyproject.toml +29 -29
- pykrait-0.4.0/src/pykrait/__init__.py +27 -0
- pykrait-0.4.0/src/pykrait/__main__.py +20 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/app.py +19 -19
- pykrait-0.4.0/src/pykrait/gui/async_workers.py +397 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/custom_widgets.py +35 -10
- pykrait-0.4.0/src/pykrait/gui/fileselection.py +753 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/mainwindow.py +364 -309
- pykrait-0.4.0/src/pykrait/io/files.py +292 -0
- pykrait-0.4.0/src/pykrait/io/frame_cache.py +292 -0
- pykrait-0.4.0/src/pykrait/io/images.py +646 -0
- pykrait-0.4.0/src/pykrait/pipeline/pipeline.py +2043 -0
- pykrait-0.4.0/src/pykrait/preprocessing/motion_correction.py +752 -0
- pykrait-0.4.0/src/pykrait/preprocessing/segmentation.py +328 -0
- pykrait-0.4.0/src/pykrait/preprocessing/timeseries_extraction.py +315 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/filtering.py +55 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/oscillations.py +62 -27
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/peak_analysis.py +122 -38
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/synchronicity.py +110 -37
- pykrait-0.4.0/src/pykrait/visualization/__init__.py +1 -0
- pykrait-0.4.0/src/pykrait/visualization/image_overlays.py +300 -0
- pykrait-0.3.2/.gitignore +0 -185
- pykrait-0.3.2/PKG-INFO +0 -81
- pykrait-0.3.2/README.md +0 -30
- pykrait-0.3.2/src/.python-version +0 -1
- pykrait-0.3.2/src/pykrait/__main__.py +0 -10
- pykrait-0.3.2/src/pykrait/gui/async_workers.py +0 -323
- pykrait-0.3.2/src/pykrait/gui/fileselection.py +0 -713
- pykrait-0.3.2/src/pykrait/io/files.py +0 -191
- pykrait-0.3.2/src/pykrait/io/images.py +0 -263
- pykrait-0.3.2/src/pykrait/pipeline/pipeline.py +0 -804
- pykrait-0.3.2/src/pykrait/preprocessing/segmentation.py +0 -201
- pykrait-0.3.2/src/pykrait/preprocessing/timeseries_extraction.py +0 -441
- pykrait-0.3.2/src/pykrait/visualization/image_overlays.py +0 -236
- {pykrait-0.3.2/src/pykrait → pykrait-0.4.0/src/pykrait/gui}/__init__.py +0 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/gui/resources/logo.png +0 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/io/__init__.py +0 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/pipeline/__init__.py +0 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/preprocessing/__init__.py +0 -0
- {pykrait-0.3.2 → pykrait-0.4.0}/src/pykrait/trace_analysis/__init__.py +0 -0
pykrait-0.4.0/.gitignore
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
*.egg
|
|
13
|
+
MANIFEST
|
|
14
|
+
|
|
15
|
+
# Virtual environments
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
|
|
20
|
+
# uv
|
|
21
|
+
.uv/
|
|
22
|
+
uv.lock
|
|
23
|
+
|
|
24
|
+
# Testing
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.coverage
|
|
27
|
+
coverage.xml
|
|
28
|
+
htmlcov/
|
|
29
|
+
|
|
30
|
+
# Linting
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
.mypy_cache/
|
|
33
|
+
|
|
34
|
+
# Jupyter
|
|
35
|
+
.ipynb_checkpoints/
|
|
36
|
+
|
|
37
|
+
# Docs
|
|
38
|
+
docs/_build/
|
|
39
|
+
docs/autoapi/
|
|
40
|
+
docs/source/_autosummary/
|
|
41
|
+
docs/make.bat
|
|
42
|
+
docs/Makefile
|
|
43
|
+
|
|
44
|
+
# IDEs
|
|
45
|
+
.vscode/
|
|
46
|
+
.idea/
|
|
47
|
+
.claude/
|
|
48
|
+
*.swp
|
|
49
|
+
*.swo
|
|
50
|
+
|
|
51
|
+
# macOS
|
|
52
|
+
.DS_Store
|
|
53
|
+
|
|
54
|
+
# Secrets
|
|
55
|
+
.env
|
|
56
|
+
*.pem
|
|
57
|
+
|
|
58
|
+
# cellpose models
|
|
59
|
+
cellpose_models/
|
|
60
|
+
notebooks/batch_notebook.ipynb
|
|
61
|
+
|
|
62
|
+
# local notebooks, may contain real data paths and executed outputs
|
|
63
|
+
notebooks/local_*.ipynb
|
pykrait-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pykrait
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Python Calcium Recording Analysis and Interpretation Toolbox
|
|
5
|
+
Author-email: phaleanopsis <22663502+phaleanopsis@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: aicspylibczi>=3.3.1
|
|
14
|
+
Requires-Dist: bioio-czi<5,>=2.4.0
|
|
15
|
+
Requires-Dist: bioio-imageio<2
|
|
16
|
+
Requires-Dist: bioio-ome-tiff<2,>=1.1.0
|
|
17
|
+
Requires-Dist: bioio-tifffile<2,>=1.3.0
|
|
18
|
+
Requires-Dist: bioio<4,>=3.2.0
|
|
19
|
+
Requires-Dist: cellpose<5,>=4.0.8
|
|
20
|
+
Requires-Dist: dask>=2025.5.1
|
|
21
|
+
Requires-Dist: fsspec>=2022.8.0
|
|
22
|
+
Requires-Dist: imagecodecs>=2025.3.30
|
|
23
|
+
Requires-Dist: matplotlib>=3.10.3
|
|
24
|
+
Requires-Dist: numcodecs<0.16,>=0.15
|
|
25
|
+
Requires-Dist: numpy>2
|
|
26
|
+
Requires-Dist: opencv-python-headless>=4.10.0.84
|
|
27
|
+
Requires-Dist: pandas>=2.3.1
|
|
28
|
+
Requires-Dist: psutil>=6.0
|
|
29
|
+
Requires-Dist: pylibczirw>=6.0; sys_platform != 'darwin' or platform_machine != 'x86_64'
|
|
30
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
31
|
+
Requires-Dist: scikit-image>=0.25.2
|
|
32
|
+
Requires-Dist: scipy>=1.15.3
|
|
33
|
+
Requires-Dist: tifffile>=2025.5.10
|
|
34
|
+
Requires-Dist: tqdm>=4.66
|
|
35
|
+
Requires-Dist: xarray>=2025.12
|
|
36
|
+
Requires-Dist: zarr>=2.15.0
|
|
37
|
+
Requires-Dist: zstandard>=0.23.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: build>=1.2.2.post1; extra == 'dev'
|
|
40
|
+
Requires-Dist: hatch>=1.14.1; extra == 'dev'
|
|
41
|
+
Requires-Dist: ipywidgets>=8.1.7; extra == 'dev'
|
|
42
|
+
Requires-Dist: myst-parser>=5.0.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: pydata-sphinx-theme>=0.16.1; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest>=8.4.1; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.11.13; extra == 'dev'
|
|
47
|
+
Requires-Dist: sphinx-autoapi>=3.6.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: sphinx-favicon>=1.0.1; extra == 'dev'
|
|
49
|
+
Requires-Dist: sphinx>=8.1.3; extra == 'dev'
|
|
50
|
+
Requires-Dist: tomli>=2.2.1; extra == 'dev'
|
|
51
|
+
Requires-Dist: twine>=6.1.0; extra == 'dev'
|
|
52
|
+
Provides-Extra: gui
|
|
53
|
+
Requires-Dist: pyside6>=6.9.1; extra == 'gui'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
<p align="center">
|
|
57
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
|
|
58
|
+
</p>
|
|
59
|
+
|
|
60
|
+
<h1 style="text-align: center;">pyKrait</h1>
|
|
61
|
+
|
|
62
|
+
<p align="center">
|
|
63
|
+
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
|
|
64
|
+
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/pyversions/pykrait" alt="Python versions"></a>
|
|
65
|
+
<a href="https://github.com/Winklerlab-HD/pykrait/actions"><img src="https://img.shields.io/github/actions/workflow/status/Winklerlab-HD/pykrait/CI.yaml" alt="CI"></a>
|
|
66
|
+
<a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
|
|
67
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
|
|
68
|
+
</p>
|
|
69
|
+
<p align="center">
|
|
70
|
+
End-to-end pipeline for segmenting cells, detecting calcium peaks,<br>
|
|
71
|
+
and quantifying oscillation periodicity and spatial synchronicity<br>
|
|
72
|
+
from time-lapse microscopy.
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
<p align="center">
|
|
77
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
|
|
78
|
+
</p>
|
|
79
|
+
|
|
80
|
+
## Overview
|
|
81
|
+
pyKrait (**py**thon **C**alcium **r**ecording **a**nalysis and **i**nterpretation **t**oolbox) automatically processes calcium-imaging videos (.czi, .tif, .tiff) and computes calcium activity, peak statistics, periodicity scores, and neighbour-aware synchronicity z-scores.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
It is recommended to install pyKrait in a virtual environment using either venv or uv. The graphical user interface is optional, install it with `pip install "pykrait[gui]"`.
|
|
86
|
+
|
|
87
|
+
### Using `uv`
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uv venv --python 3.12
|
|
91
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
92
|
+
uv pip install 'pykrait[gui]'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Standard `venv`
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
python -m venv .venv
|
|
99
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
100
|
+
pip install --upgrade pip
|
|
101
|
+
pip install "pykrait[gui]"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## Quickstart
|
|
106
|
+
|
|
107
|
+
### Launch the GUI
|
|
108
|
+
|
|
109
|
+
From inside the activated virtual environment:
|
|
110
|
+
```bash
|
|
111
|
+
python -m pykrait
|
|
112
|
+
```
|
|
113
|
+
or
|
|
114
|
+
```bash
|
|
115
|
+
uv run python -m pykrait
|
|
116
|
+
```
|
|
117
|
+
The GUI then walks you through video selection, segmentation, peak detection, periodicity, and synchronicity, and lets you save results to disk.
|
|
118
|
+
|
|
119
|
+
### Batch process an entire folder
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from pykrait.pipeline.pipeline import BatchExperiment, AnalysisParameters
|
|
123
|
+
from pykrait.io.files import concat_analysis_files
|
|
124
|
+
|
|
125
|
+
experiment = BatchExperiment(
|
|
126
|
+
folder="/path/to/videos",
|
|
127
|
+
params=AnalysisParameters(),
|
|
128
|
+
extension=".czi",
|
|
129
|
+
)
|
|
130
|
+
experiment.run()
|
|
131
|
+
|
|
132
|
+
concat_analysis_files("/path/to/videos", filetype="output")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
|
|
136
|
+
|
|
137
|
+
To analyze videos in subfolders, pass `recursive=True`; to analyze only some of the acquisitions, pass `filter_string` / `exclude_string` (e.g. `filter_string="*_timeseries.czi"`, where a pattern with `*`, `?` or `[]` matches the whole file name and anything else matches as a substring).
|
|
138
|
+
|
|
139
|
+
## Example Notebooks
|
|
140
|
+
|
|
141
|
+
| Scenario | Notebook |
|
|
142
|
+
|----------|----------|
|
|
143
|
+
| Run a folder with custom parameters | [notebooks/example_batch_with_custom_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_with_custom_parameters.ipynb) |
|
|
144
|
+
| Re-run a folder reusing saved parameters and cached masks | [notebooks/example_batch_reusing_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_reusing_parameters.ipynb) |
|
|
145
|
+
| Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
|
|
146
|
+
|
|
147
|
+
## How it works
|
|
148
|
+
|
|
149
|
+
For each video, pyKrait runs the following stages:
|
|
150
|
+
|
|
151
|
+
1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
|
|
152
|
+
2. **Correct motion** *(optional, off by default)* — align every frame to a template, either by a rigid translation or by a mesh-based / polynomial optical-flow warp.
|
|
153
|
+
3. **Project** — STD or SUM projection across time, with optional CLAHE.
|
|
154
|
+
4. **Segment** — Cellpose (`cpsam` by default, or a custom model path).
|
|
155
|
+
5. **Extract** — per-cell mean intensity per frame, computed lazily on Dask.
|
|
156
|
+
6. **Detrend** — Blackman-windowed sinc filter (cf. pyBOAT).
|
|
157
|
+
7. **Detect peaks** — `scipy.signal.find_peaks` with width / height / prominence thresholds.
|
|
158
|
+
8. **Periodicity** — STD and CoV of inter-peak intervals, scored against a shuffled-peaks null.
|
|
159
|
+
9. **Adjacency** — neighbour graph from segmented masks (kernel-based proximity).
|
|
160
|
+
10. **Synchronicity** — co-firing peaks within a time window and topological distance, z-scored against label-shuffled controls.
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
|
|
165
|
+
|
|
166
|
+
## Citation
|
|
167
|
+
|
|
168
|
+
If you use pyKrait, please cite the [preprint](https://www.biorxiv.org/content/10.64898/2026.05.10.723715v1): Hebach, N. R., Olshausen, N., Schlag, J., Mayer, C. D., Henkenjohann, J., Kraft, T., Bang, S., Thommek, C., Nürnberg, C., Horsak, N. E., Hoffmann, D. C., Kourtesakis, A., Porzberg, N., Flores Valle, A., Linke, C. Z., Yang, Y., Azorín, D. D., Hausmann, D., Venkataramani, V., … Karreman, M. A. (2026). Collective cancer cell calcium activity drives brain metastasis. bioRxiv, 2026.05.10.723715. https://doi.org/10.64898/2026.05.10.723715
|
|
169
|
+
|
|
170
|
+
## Acknowledgements
|
|
171
|
+
|
|
172
|
+
pyKrait builds on and heavily uses prior open-source work:
|
|
173
|
+
|
|
174
|
+
- [Cellpose](https://github.com/MouseLand/cellpose) for cell segmentation
|
|
175
|
+
- [pyBOAT](https://github.com/tensionhead/pyBOAT) for the sinc-filter detrending approach
|
|
176
|
+
- [bioio](https://github.com/bioio-devs/bioio) for image I/O
|
|
177
|
+
- [Dask](https://www.dask.org/) for lazy array computation
|
|
178
|
+
|
pykrait-0.4.0/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 style="text-align: center;">pyKrait</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
|
|
9
|
+
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/pyversions/pykrait" alt="Python versions"></a>
|
|
10
|
+
<a href="https://github.com/Winklerlab-HD/pykrait/actions"><img src="https://img.shields.io/github/actions/workflow/status/Winklerlab-HD/pykrait/CI.yaml" alt="CI"></a>
|
|
11
|
+
<a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
|
|
12
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
|
|
13
|
+
</p>
|
|
14
|
+
<p align="center">
|
|
15
|
+
End-to-end pipeline for segmenting cells, detecting calcium peaks,<br>
|
|
16
|
+
and quantifying oscillation periodicity and spatial synchronicity<br>
|
|
17
|
+
from time-lapse microscopy.
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<p align="center">
|
|
22
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
pyKrait (**py**thon **C**alcium **r**ecording **a**nalysis and **i**nterpretation **t**oolbox) automatically processes calcium-imaging videos (.czi, .tif, .tiff) and computes calcium activity, peak statistics, periodicity scores, and neighbour-aware synchronicity z-scores.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
It is recommended to install pyKrait in a virtual environment using either venv or uv. The graphical user interface is optional, install it with `pip install "pykrait[gui]"`.
|
|
31
|
+
|
|
32
|
+
### Using `uv`
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv venv --python 3.12
|
|
36
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
37
|
+
uv pip install 'pykrait[gui]'
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Standard `venv`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python -m venv .venv
|
|
44
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
45
|
+
pip install --upgrade pip
|
|
46
|
+
pip install "pykrait[gui]"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Quickstart
|
|
51
|
+
|
|
52
|
+
### Launch the GUI
|
|
53
|
+
|
|
54
|
+
From inside the activated virtual environment:
|
|
55
|
+
```bash
|
|
56
|
+
python -m pykrait
|
|
57
|
+
```
|
|
58
|
+
or
|
|
59
|
+
```bash
|
|
60
|
+
uv run python -m pykrait
|
|
61
|
+
```
|
|
62
|
+
The GUI then walks you through video selection, segmentation, peak detection, periodicity, and synchronicity, and lets you save results to disk.
|
|
63
|
+
|
|
64
|
+
### Batch process an entire folder
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from pykrait.pipeline.pipeline import BatchExperiment, AnalysisParameters
|
|
68
|
+
from pykrait.io.files import concat_analysis_files
|
|
69
|
+
|
|
70
|
+
experiment = BatchExperiment(
|
|
71
|
+
folder="/path/to/videos",
|
|
72
|
+
params=AnalysisParameters(),
|
|
73
|
+
extension=".czi",
|
|
74
|
+
)
|
|
75
|
+
experiment.run()
|
|
76
|
+
|
|
77
|
+
concat_analysis_files("/path/to/videos", filetype="output")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
|
|
81
|
+
|
|
82
|
+
To analyze videos in subfolders, pass `recursive=True`; to analyze only some of the acquisitions, pass `filter_string` / `exclude_string` (e.g. `filter_string="*_timeseries.czi"`, where a pattern with `*`, `?` or `[]` matches the whole file name and anything else matches as a substring).
|
|
83
|
+
|
|
84
|
+
## Example Notebooks
|
|
85
|
+
|
|
86
|
+
| Scenario | Notebook |
|
|
87
|
+
|----------|----------|
|
|
88
|
+
| Run a folder with custom parameters | [notebooks/example_batch_with_custom_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_with_custom_parameters.ipynb) |
|
|
89
|
+
| Re-run a folder reusing saved parameters and cached masks | [notebooks/example_batch_reusing_parameters](https://github.com/Winklerlab-HD/pykrait/blob/main/notebooks/example_batch_reusing_parameters.ipynb) |
|
|
90
|
+
| Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
|
|
91
|
+
|
|
92
|
+
## How it works
|
|
93
|
+
|
|
94
|
+
For each video, pyKrait runs the following stages:
|
|
95
|
+
|
|
96
|
+
1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
|
|
97
|
+
2. **Correct motion** *(optional, off by default)* — align every frame to a template, either by a rigid translation or by a mesh-based / polynomial optical-flow warp.
|
|
98
|
+
3. **Project** — STD or SUM projection across time, with optional CLAHE.
|
|
99
|
+
4. **Segment** — Cellpose (`cpsam` by default, or a custom model path).
|
|
100
|
+
5. **Extract** — per-cell mean intensity per frame, computed lazily on Dask.
|
|
101
|
+
6. **Detrend** — Blackman-windowed sinc filter (cf. pyBOAT).
|
|
102
|
+
7. **Detect peaks** — `scipy.signal.find_peaks` with width / height / prominence thresholds.
|
|
103
|
+
8. **Periodicity** — STD and CoV of inter-peak intervals, scored against a shuffled-peaks null.
|
|
104
|
+
9. **Adjacency** — neighbour graph from segmented masks (kernel-based proximity).
|
|
105
|
+
10. **Synchronicity** — co-firing peaks within a time window and topological distance, z-scored against label-shuffled controls.
|
|
106
|
+
|
|
107
|
+
## Documentation
|
|
108
|
+
|
|
109
|
+
Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
|
|
110
|
+
|
|
111
|
+
## Citation
|
|
112
|
+
|
|
113
|
+
If you use pyKrait, please cite the [preprint](https://www.biorxiv.org/content/10.64898/2026.05.10.723715v1): Hebach, N. R., Olshausen, N., Schlag, J., Mayer, C. D., Henkenjohann, J., Kraft, T., Bang, S., Thommek, C., Nürnberg, C., Horsak, N. E., Hoffmann, D. C., Kourtesakis, A., Porzberg, N., Flores Valle, A., Linke, C. Z., Yang, Y., Azorín, D. D., Hausmann, D., Venkataramani, V., … Karreman, M. A. (2026). Collective cancer cell calcium activity drives brain metastasis. bioRxiv, 2026.05.10.723715. https://doi.org/10.64898/2026.05.10.723715
|
|
114
|
+
|
|
115
|
+
## Acknowledgements
|
|
116
|
+
|
|
117
|
+
pyKrait builds on and heavily uses prior open-source work:
|
|
118
|
+
|
|
119
|
+
- [Cellpose](https://github.com/MouseLand/cellpose) for cell segmentation
|
|
120
|
+
- [pyBOAT](https://github.com/tensionhead/pyBOAT) for the sinc-filter detrending approach
|
|
121
|
+
- [bioio](https://github.com/bioio-devs/bioio) for image I/O
|
|
122
|
+
- [Dask](https://www.dask.org/) for lazy array computation
|
|
123
|
+
|
|
@@ -1,42 +1,51 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pykrait"
|
|
3
|
-
version = "0.
|
|
4
|
-
description = "Python Calcium Recording Analysis and
|
|
3
|
+
version = "0.4.0"
|
|
4
|
+
description = "Python Calcium Recording Analysis and Interpretation Toolbox"
|
|
5
5
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
6
6
|
license = { text = "MIT" }
|
|
7
7
|
authors = [
|
|
8
8
|
{ name = "phaleanopsis", email = "22663502+phaleanopsis@users.noreply.github.com" }
|
|
9
9
|
]
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
|
+
# bioio plugins and cellpose change their APIs between major versions (bioio-czi 4 removed
|
|
12
|
+
# use_aicspylibczi), so they are capped at the next untested major version
|
|
11
13
|
dependencies = [
|
|
12
14
|
"aicspylibczi>=3.3.1",
|
|
13
|
-
"bioio>=3.
|
|
14
|
-
"bioio-czi>=2.4.0",
|
|
15
|
-
"bioio-ome-tiff>=1.1.0",
|
|
16
|
-
"bioio-tifffile>=1.3.0",
|
|
17
|
-
"bioio-imageio",
|
|
18
|
-
"cellpose>=4.0.5",
|
|
15
|
+
"bioio>=3.2.0,<4",
|
|
16
|
+
"bioio-czi>=2.4.0,<5",
|
|
17
|
+
"bioio-ome-tiff>=1.1.0,<2",
|
|
18
|
+
"bioio-tifffile>=1.3.0,<2",
|
|
19
|
+
"bioio-imageio<2",
|
|
20
|
+
"cellpose>=4.0.8,<5",
|
|
19
21
|
"dask>=2025.5.1",
|
|
20
22
|
"fsspec>=2022.8.0",
|
|
21
23
|
"imagecodecs>=2025.3.30",
|
|
22
|
-
"lxml>=5.4.0",
|
|
23
24
|
"matplotlib>=3.10.3",
|
|
24
|
-
|
|
25
|
+
# zarr 2 imports numcodecs.blosc.cbuffer_sizes, which numcodecs 0.16 removed
|
|
26
|
+
"numcodecs>=0.15,<0.16",
|
|
25
27
|
"numpy>2",
|
|
26
|
-
"
|
|
27
|
-
"ome-zarr>=0.10.2",
|
|
28
|
-
"pip>=25.1.1",
|
|
29
|
-
"pydantic<2",
|
|
28
|
+
"opencv-python-headless>=4.10.0.84",
|
|
30
29
|
"pyyaml>=6.0.2",
|
|
31
|
-
"resource-backed-dask-array>=0.1.0",
|
|
32
30
|
"scikit-image>=0.25.2",
|
|
33
31
|
"scipy>=1.15.3",
|
|
34
|
-
"
|
|
32
|
+
"tifffile>=2025.5.10",
|
|
33
|
+
"tqdm>=4.66",
|
|
34
|
+
"xarray>=2025.12",
|
|
35
35
|
"pandas>=2.3.1",
|
|
36
|
+
"psutil>=6.0",
|
|
37
|
+
# reads CZI files much faster than bioio; there is no wheel for Intel Macs, which
|
|
38
|
+
# fall back to bioio
|
|
39
|
+
"pylibCZIrw>=6.0; sys_platform != 'darwin' or platform_machine != 'x86_64'",
|
|
36
40
|
"zstandard>=0.23.0",
|
|
37
|
-
"
|
|
41
|
+
"zarr>=2.15.0",
|
|
42
|
+
]
|
|
43
|
+
classifiers = [
|
|
44
|
+
"Programming Language :: Python :: 3",
|
|
45
|
+
"Programming Language :: Python :: 3.11",
|
|
46
|
+
"Programming Language :: Python :: 3.12",
|
|
47
|
+
"Programming Language :: Python :: 3.13",
|
|
38
48
|
]
|
|
39
|
-
|
|
40
49
|
[project.optional-dependencies]
|
|
41
50
|
gui = [
|
|
42
51
|
"pyside6>=6.9.1",
|
|
@@ -53,7 +62,8 @@ dev = [
|
|
|
53
62
|
"tomli>=2.2.1",
|
|
54
63
|
"twine>=6.1.0",
|
|
55
64
|
"pytest>=8.4.1",
|
|
56
|
-
"pytest-cov"
|
|
65
|
+
"pytest-cov>=6.0",
|
|
66
|
+
"myst-parser>=5.0.0",
|
|
57
67
|
]
|
|
58
68
|
|
|
59
69
|
[build-system]
|
|
@@ -92,17 +102,7 @@ source = ["pykrait"]
|
|
|
92
102
|
show_missing = true
|
|
93
103
|
skip_covered = true
|
|
94
104
|
|
|
95
|
-
[tool.setuptools.package-data]
|
|
96
|
-
"pykrait.gui.resources" = ["logo.png"]
|
|
97
|
-
|
|
98
105
|
[tool.ruff]
|
|
99
106
|
src = ["src"]
|
|
100
107
|
line-length = 88
|
|
101
108
|
target-version = "py311"
|
|
102
|
-
|
|
103
|
-
[dependency-groups]
|
|
104
|
-
dev = [
|
|
105
|
-
"build>=1.2.2.post1",
|
|
106
|
-
"hatch>=1.14.1",
|
|
107
|
-
"twine>=6.1.0",
|
|
108
|
-
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class _StdoutHandler(logging.StreamHandler):
|
|
6
|
+
"""Writes to the current sys.stdout, which notebooks and tests replace after import."""
|
|
7
|
+
|
|
8
|
+
@property
|
|
9
|
+
def stream(self):
|
|
10
|
+
return sys.stdout
|
|
11
|
+
|
|
12
|
+
@stream.setter
|
|
13
|
+
def stream(self, value):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# pykrait reports its progress through the "pykrait" logger. By default the messages are
|
|
18
|
+
# printed to stdout as before; silence them with
|
|
19
|
+
# logging.getLogger("pykrait").setLevel(logging.WARNING), or remove this handler to
|
|
20
|
+
# route them through your own logging configuration.
|
|
21
|
+
_logger = logging.getLogger(__name__)
|
|
22
|
+
_handler = _StdoutHandler()
|
|
23
|
+
_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
24
|
+
_logger.addHandler(_handler)
|
|
25
|
+
if _logger.level == logging.NOTSET: # keep a level that was set before the import
|
|
26
|
+
_logger.setLevel(logging.INFO)
|
|
27
|
+
_logger.propagate = False
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright © 2026 Phaleanopsis
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# src/pykrait/__main__.py
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
from pykrait.gui.app import AppController
|
|
10
|
+
except ImportError as e:
|
|
11
|
+
if getattr(e, "name", None) and e.name.startswith("PySide6"):
|
|
12
|
+
sys.exit(
|
|
13
|
+
"The pyKrait GUI needs the optional GUI dependencies. "
|
|
14
|
+
'Install them with: pip install "pykrait[gui]"'
|
|
15
|
+
)
|
|
16
|
+
raise
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
app_controller = AppController()
|
|
20
|
+
app_controller.run()
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import sys
|
|
2
3
|
import os
|
|
3
4
|
import warnings
|
|
@@ -8,15 +9,18 @@ from pykrait.gui.fileselection import FileSelectionWindow
|
|
|
8
9
|
from pykrait.gui.mainwindow import MainWindow
|
|
9
10
|
from pykrait.io.files import get_pykrait_version
|
|
10
11
|
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
11
15
|
def warning_no_source_line(message, category, filename, lineno, file=None, line=None):
|
|
12
16
|
return f"{filename}:{lineno}: {category.__name__}: {message}\n"
|
|
13
17
|
|
|
18
|
+
|
|
14
19
|
class AppController:
|
|
15
20
|
def __init__(self):
|
|
16
|
-
|
|
17
21
|
# format warnings to not print the source line every time
|
|
18
22
|
warnings.formatwarning = warning_no_source_line
|
|
19
|
-
|
|
23
|
+
|
|
20
24
|
self.app = QApplication(sys.argv)
|
|
21
25
|
|
|
22
26
|
# Loading icon from resources
|
|
@@ -25,7 +29,6 @@ class AppController:
|
|
|
25
29
|
from importlib.resources import files
|
|
26
30
|
|
|
27
31
|
icon_path = files("pykrait.gui.resources").joinpath("logo.png")
|
|
28
|
-
os.path.exists(icon_path)
|
|
29
32
|
except Exception:
|
|
30
33
|
icon_path = os.path.abspath(
|
|
31
34
|
os.path.join(
|
|
@@ -36,28 +39,21 @@ class AppController:
|
|
|
36
39
|
"logo.png",
|
|
37
40
|
)
|
|
38
41
|
)
|
|
39
|
-
os.path.exists(icon_path)
|
|
40
42
|
if os.path.exists(icon_path):
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
appIcon.addFile(str(icon_path), QSize(32, 32))
|
|
44
|
-
appIcon.addFile(str(icon_path), QSize(48, 48))
|
|
45
|
-
appIcon.addFile(str(icon_path), QSize(64, 64))
|
|
46
|
-
appIcon.addFile(str(icon_path), QSize(256, 256))
|
|
43
|
+
for size in (16, 24, 32, 48, 64, 256):
|
|
44
|
+
appIcon.addFile(str(icon_path), QSize(size, size))
|
|
47
45
|
self.app.setWindowIcon(appIcon)
|
|
48
46
|
else:
|
|
49
|
-
|
|
47
|
+
logger.warning(f"Warning: Icon file not found at {icon_path}")
|
|
50
48
|
|
|
51
49
|
# Set application name and version
|
|
52
50
|
self.app.setApplicationName("pyKrait")
|
|
53
51
|
version = get_pykrait_version()
|
|
54
52
|
if version is not None:
|
|
55
53
|
self.app.setApplicationVersion(version)
|
|
56
|
-
self.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
#updating the style
|
|
54
|
+
self._open_file_selection()
|
|
55
|
+
|
|
56
|
+
# updating the style
|
|
61
57
|
self.app.setStyle("Fusion")
|
|
62
58
|
light_palette = self.app.style().standardPalette()
|
|
63
59
|
self.app.setPalette(light_palette)
|
|
@@ -72,6 +68,7 @@ class AppController:
|
|
|
72
68
|
# Launch main window, keep reference
|
|
73
69
|
self.main_window = MainWindow(
|
|
74
70
|
frames=results["frames"],
|
|
71
|
+
display_channel=results.get("intensity_channel", 0),
|
|
75
72
|
mask=results["masks"],
|
|
76
73
|
rois=results["rois"],
|
|
77
74
|
mean_intensities=results["mean_intensities"],
|
|
@@ -81,6 +78,11 @@ class AppController:
|
|
|
81
78
|
)
|
|
82
79
|
self.main_window.show()
|
|
83
80
|
|
|
81
|
+
def _open_file_selection(self):
|
|
82
|
+
self.file_selection = FileSelectionWindow()
|
|
83
|
+
self.file_selection.analysis_complete.connect(self.on_analysis_complete)
|
|
84
|
+
self.file_selection.show()
|
|
85
|
+
|
|
84
86
|
def run(self):
|
|
85
87
|
sys.exit(self.app.exec())
|
|
86
88
|
|
|
@@ -89,6 +91,4 @@ class AppController:
|
|
|
89
91
|
self.main_window.close()
|
|
90
92
|
self.main_window = None
|
|
91
93
|
|
|
92
|
-
self.
|
|
93
|
-
self.file_selection.analysis_complete.connect(self.on_analysis_complete)
|
|
94
|
-
self.file_selection.show()
|
|
94
|
+
self._open_file_selection()
|