pykrait 0.3.3__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.3.3 → pykrait-0.4.0}/.gitignore +5 -1
- {pykrait-0.3.3 → pykrait-0.4.0}/PKG-INFO +54 -44
- {pykrait-0.3.3 → pykrait-0.4.0}/README.md +35 -26
- {pykrait-0.3.3 → pykrait-0.4.0}/pyproject.toml +25 -28
- pykrait-0.4.0/src/pykrait/__init__.py +27 -0
- pykrait-0.4.0/src/pykrait/__main__.py +20 -0
- {pykrait-0.3.3 → 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.3 → 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.3 → pykrait-0.4.0}/src/pykrait/gui/mainwindow.py +363 -310
- 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.3 → pykrait-0.4.0}/src/pykrait/trace_analysis/filtering.py +55 -0
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/trace_analysis/oscillations.py +61 -28
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/trace_analysis/peak_analysis.py +122 -38
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/trace_analysis/synchronicity.py +110 -38
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/visualization/image_overlays.py +116 -61
- pykrait-0.3.3/src/pykrait/__main__.py +0 -10
- pykrait-0.3.3/src/pykrait/gui/async_workers.py +0 -341
- pykrait-0.3.3/src/pykrait/gui/fileselection.py +0 -713
- pykrait-0.3.3/src/pykrait/io/files.py +0 -191
- pykrait-0.3.3/src/pykrait/io/images.py +0 -263
- pykrait-0.3.3/src/pykrait/pipeline/pipeline.py +0 -869
- pykrait-0.3.3/src/pykrait/preprocessing/segmentation.py +0 -201
- pykrait-0.3.3/src/pykrait/preprocessing/timeseries_extraction.py +0 -445
- pykrait-0.3.3/src/pykrait/visualization/__init__.py +0 -1
- {pykrait-0.3.3 → pykrait-0.4.0}/LICENSE +0 -0
- {pykrait-0.3.3/src/pykrait → pykrait-0.4.0/src/pykrait/gui}/__init__.py +0 -0
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/gui/resources/logo.png +0 -0
- {pykrait-0.3.3 → pykrait-0.4.0}/src/pykrait/io/__init__.py +0 -0
- {pykrait-0.3.3/src/pykrait/gui → pykrait-0.4.0/src/pykrait/pipeline}/__init__.py +0 -0
- {pykrait-0.3.3/src/pykrait/pipeline → pykrait-0.4.0/src/pykrait/preprocessing}/__init__.py +0 -0
- {pykrait-0.3.3/src/pykrait/preprocessing → pykrait-0.4.0/src/pykrait/trace_analysis}/__init__.py +0 -0
- {pykrait-0.3.3/src/pykrait/trace_analysis → pykrait-0.4.0/src/pykrait/visualization}/__init__.py +0 -0
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: pykrait
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Python Calcium Recording Analysis and Interpretation Toolbox
|
|
5
5
|
Author-email: phaleanopsis <22663502+phaleanopsis@users.noreply.github.com>
|
|
6
6
|
License: MIT
|
|
7
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
|
|
8
12
|
Requires-Python: >=3.11
|
|
9
13
|
Requires-Dist: aicspylibczi>=3.3.1
|
|
10
|
-
Requires-Dist: bioio-czi
|
|
11
|
-
Requires-Dist: bioio-imageio
|
|
12
|
-
Requires-Dist: bioio-ome-tiff
|
|
13
|
-
Requires-Dist: bioio-tifffile
|
|
14
|
-
Requires-Dist: bioio
|
|
15
|
-
Requires-Dist: cellpose
|
|
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
|
|
16
20
|
Requires-Dist: dask>=2025.5.1
|
|
17
21
|
Requires-Dist: fsspec>=2022.8.0
|
|
18
22
|
Requires-Dist: imagecodecs>=2025.3.30
|
|
19
|
-
Requires-Dist: lxml>=5.4.0
|
|
20
23
|
Requires-Dist: matplotlib>=3.10.3
|
|
21
|
-
Requires-Dist: numcodecs<0.16
|
|
24
|
+
Requires-Dist: numcodecs<0.16,>=0.15
|
|
22
25
|
Requires-Dist: numpy>2
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist: ome-zarr>=0.10.2
|
|
26
|
+
Requires-Dist: opencv-python-headless>=4.10.0.84
|
|
25
27
|
Requires-Dist: pandas>=2.3.1
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist: pylibczirw>=6.0.1
|
|
28
|
+
Requires-Dist: psutil>=6.0
|
|
29
|
+
Requires-Dist: pylibczirw>=6.0; sys_platform != 'darwin' or platform_machine != 'x86_64'
|
|
29
30
|
Requires-Dist: pyyaml>=6.0.2
|
|
30
|
-
Requires-Dist: resource-backed-dask-array>=0.1.0
|
|
31
31
|
Requires-Dist: scikit-image>=0.25.2
|
|
32
|
-
Requires-Dist: scikit-learn>=1.7.2
|
|
33
32
|
Requires-Dist: scipy>=1.15.3
|
|
33
|
+
Requires-Dist: tifffile>=2025.5.10
|
|
34
|
+
Requires-Dist: tqdm>=4.66
|
|
34
35
|
Requires-Dist: xarray>=2025.12
|
|
35
36
|
Requires-Dist: zarr>=2.15.0
|
|
36
37
|
Requires-Dist: zstandard>=0.23.0
|
|
@@ -40,7 +41,7 @@ Requires-Dist: hatch>=1.14.1; extra == 'dev'
|
|
|
40
41
|
Requires-Dist: ipywidgets>=8.1.7; extra == 'dev'
|
|
41
42
|
Requires-Dist: myst-parser>=5.0.0; extra == 'dev'
|
|
42
43
|
Requires-Dist: pydata-sphinx-theme>=0.16.1; extra == 'dev'
|
|
43
|
-
Requires-Dist: pytest-cov; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
44
45
|
Requires-Dist: pytest>=8.4.1; extra == 'dev'
|
|
45
46
|
Requires-Dist: ruff>=0.11.13; extra == 'dev'
|
|
46
47
|
Requires-Dist: sphinx-autoapi>=3.6.0; extra == 'dev'
|
|
@@ -53,16 +54,15 @@ Requires-Dist: pyside6>=6.9.1; extra == 'gui'
|
|
|
53
54
|
Description-Content-Type: text/markdown
|
|
54
55
|
|
|
55
56
|
<p align="center">
|
|
56
|
-
<img src="docs/_static/logo.png" alt="pyKrait" width="200">
|
|
57
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
|
|
57
58
|
</p>
|
|
58
59
|
|
|
59
|
-
<h1
|
|
60
|
-
|
|
60
|
+
<h1 style="text-align: center;">pyKrait</h1>
|
|
61
61
|
|
|
62
62
|
<p align="center">
|
|
63
63
|
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
|
|
64
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
|
|
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
66
|
<a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
|
|
67
67
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
|
|
68
68
|
</p>
|
|
@@ -74,34 +74,34 @@ Description-Content-Type: text/markdown
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
<p align="center">
|
|
77
|
-
<img src="docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
|
|
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
78
|
</p>
|
|
79
79
|
|
|
80
80
|
## Overview
|
|
81
|
-
|
|
82
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.
|
|
83
82
|
|
|
84
83
|
## Installation
|
|
85
84
|
|
|
86
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]"`.
|
|
87
86
|
|
|
88
|
-
###
|
|
87
|
+
### Using `uv`
|
|
89
88
|
|
|
90
89
|
```bash
|
|
91
|
-
|
|
90
|
+
uv venv --python 3.12
|
|
92
91
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
93
|
-
pip install
|
|
94
|
-
pip install pykrait
|
|
92
|
+
uv pip install 'pykrait[gui]'
|
|
95
93
|
```
|
|
96
94
|
|
|
97
|
-
###
|
|
95
|
+
### Standard `venv`
|
|
98
96
|
|
|
99
97
|
```bash
|
|
100
|
-
|
|
98
|
+
python -m venv .venv
|
|
101
99
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
102
|
-
|
|
100
|
+
pip install --upgrade pip
|
|
101
|
+
pip install "pykrait[gui]"
|
|
103
102
|
```
|
|
104
103
|
|
|
104
|
+
|
|
105
105
|
## Quickstart
|
|
106
106
|
|
|
107
107
|
### Launch the GUI
|
|
@@ -110,8 +110,11 @@ From inside the activated virtual environment:
|
|
|
110
110
|
```bash
|
|
111
111
|
python -m pykrait
|
|
112
112
|
```
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
115
118
|
|
|
116
119
|
### Batch process an entire folder
|
|
117
120
|
|
|
@@ -131,12 +134,14 @@ concat_analysis_files("/path/to/videos", filetype="output")
|
|
|
131
134
|
|
|
132
135
|
This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
|
|
133
136
|
|
|
134
|
-
|
|
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
|
|
135
140
|
|
|
136
141
|
| Scenario | Notebook |
|
|
137
142
|
|----------|----------|
|
|
138
|
-
| Run a folder with custom parameters | [
|
|
139
|
-
| Re-run a folder reusing saved parameters and cached masks | [
|
|
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) |
|
|
140
145
|
| Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
|
|
141
146
|
|
|
142
147
|
## How it works
|
|
@@ -144,19 +149,24 @@ This produces one `Analysis_<video>/` directory per video and an `analysis_outpu
|
|
|
144
149
|
For each video, pyKrait runs the following stages:
|
|
145
150
|
|
|
146
151
|
1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
|
|
147
|
-
2. **
|
|
148
|
-
3. **
|
|
149
|
-
4. **
|
|
150
|
-
5. **
|
|
151
|
-
6. **
|
|
152
|
-
7. **
|
|
153
|
-
8. **
|
|
154
|
-
9. **
|
|
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.
|
|
155
161
|
|
|
156
162
|
## Documentation
|
|
157
163
|
|
|
158
164
|
Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
|
|
159
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
|
+
|
|
160
170
|
## Acknowledgements
|
|
161
171
|
|
|
162
172
|
pyKrait builds on and heavily uses prior open-source work:
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="docs/_static/logo.png" alt="pyKrait" width="200">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Winklerlab-HD/pykrait/main/docs/_static/logo.png" alt="pyKrait" width="200">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1
|
|
6
|
-
|
|
5
|
+
<h1 style="text-align: center;">pyKrait</h1>
|
|
7
6
|
|
|
8
7
|
<p align="center">
|
|
9
8
|
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/v/pykrait" alt="PyPI"></a>
|
|
10
9
|
<a href="https://pypi.org/project/pykrait/"><img src="https://img.shields.io/pypi/pyversions/pykrait" alt="Python versions"></a>
|
|
11
|
-
<a href="https://github.com
|
|
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>
|
|
12
11
|
<a href="https://pykrait.readthedocs.io"><img src="https://img.shields.io/readthedocs/pykrait" alt="Docs"></a>
|
|
13
12
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License"></a>
|
|
14
13
|
</p>
|
|
@@ -20,34 +19,34 @@
|
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
<p align="center">
|
|
23
|
-
<img src="docs/_static/pyKrait_steps.png" alt="Analysis Steps of the pykrait package" width="600">
|
|
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">
|
|
24
23
|
</p>
|
|
25
24
|
|
|
26
25
|
## Overview
|
|
27
|
-
|
|
28
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.
|
|
29
27
|
|
|
30
28
|
## Installation
|
|
31
29
|
|
|
32
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]"`.
|
|
33
31
|
|
|
34
|
-
###
|
|
32
|
+
### Using `uv`
|
|
35
33
|
|
|
36
34
|
```bash
|
|
37
|
-
|
|
35
|
+
uv venv --python 3.12
|
|
38
36
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
39
|
-
pip install
|
|
40
|
-
pip install pykrait
|
|
37
|
+
uv pip install 'pykrait[gui]'
|
|
41
38
|
```
|
|
42
39
|
|
|
43
|
-
###
|
|
40
|
+
### Standard `venv`
|
|
44
41
|
|
|
45
42
|
```bash
|
|
46
|
-
|
|
43
|
+
python -m venv .venv
|
|
47
44
|
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
48
|
-
|
|
45
|
+
pip install --upgrade pip
|
|
46
|
+
pip install "pykrait[gui]"
|
|
49
47
|
```
|
|
50
48
|
|
|
49
|
+
|
|
51
50
|
## Quickstart
|
|
52
51
|
|
|
53
52
|
### Launch the GUI
|
|
@@ -56,8 +55,11 @@ From inside the activated virtual environment:
|
|
|
56
55
|
```bash
|
|
57
56
|
python -m pykrait
|
|
58
57
|
```
|
|
59
|
-
|
|
60
|
-
|
|
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.
|
|
61
63
|
|
|
62
64
|
### Batch process an entire folder
|
|
63
65
|
|
|
@@ -77,12 +79,14 @@ concat_analysis_files("/path/to/videos", filetype="output")
|
|
|
77
79
|
|
|
78
80
|
This produces one `Analysis_<video>/` directory per video and an `analysis_output_overview.csv` at the root of the folder.
|
|
79
81
|
|
|
80
|
-
|
|
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
|
|
81
85
|
|
|
82
86
|
| Scenario | Notebook |
|
|
83
87
|
|----------|----------|
|
|
84
|
-
| Run a folder with custom parameters | [
|
|
85
|
-
| Re-run a folder reusing saved parameters and cached masks | [
|
|
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) |
|
|
86
90
|
| Inspect or refine a single video interactively | Launch the GUI: `python -m pykrait` |
|
|
87
91
|
|
|
88
92
|
## How it works
|
|
@@ -90,19 +94,24 @@ This produces one `Analysis_<video>/` directory per video and an `analysis_outpu
|
|
|
90
94
|
For each video, pyKrait runs the following stages:
|
|
91
95
|
|
|
92
96
|
1. **Load** — lazy `Dask` array via `bioio` (`.czi`, `.tif`, `.tiff`).
|
|
93
|
-
2. **
|
|
94
|
-
3. **
|
|
95
|
-
4. **
|
|
96
|
-
5. **
|
|
97
|
-
6. **
|
|
98
|
-
7. **
|
|
99
|
-
8. **
|
|
100
|
-
9. **
|
|
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.
|
|
101
106
|
|
|
102
107
|
## Documentation
|
|
103
108
|
|
|
104
109
|
Full API reference and tutorials: **<https://pykrait.readthedocs.io/en/latest/>**.
|
|
105
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
|
+
|
|
106
115
|
## Acknowledgements
|
|
107
116
|
|
|
108
117
|
pyKrait builds on and heavily uses prior open-source work:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pykrait"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Python Calcium Recording Analysis and Interpretation Toolbox"
|
|
5
5
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
6
6
|
license = { text = "MIT" }
|
|
@@ -8,37 +8,44 @@ 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.2.0",
|
|
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.8",
|
|
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",
|
|
32
|
+
"tifffile>=2025.5.10",
|
|
33
|
+
"tqdm>=4.66",
|
|
34
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
|
-
"scikit-learn>=1.7.2",
|
|
38
41
|
"zarr>=2.15.0",
|
|
39
|
-
"pylibczirw>=6.0.1",
|
|
40
42
|
]
|
|
41
|
-
|
|
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",
|
|
48
|
+
]
|
|
42
49
|
[project.optional-dependencies]
|
|
43
50
|
gui = [
|
|
44
51
|
"pyside6>=6.9.1",
|
|
@@ -55,7 +62,7 @@ dev = [
|
|
|
55
62
|
"tomli>=2.2.1",
|
|
56
63
|
"twine>=6.1.0",
|
|
57
64
|
"pytest>=8.4.1",
|
|
58
|
-
"pytest-cov",
|
|
65
|
+
"pytest-cov>=6.0",
|
|
59
66
|
"myst-parser>=5.0.0",
|
|
60
67
|
]
|
|
61
68
|
|
|
@@ -95,17 +102,7 @@ source = ["pykrait"]
|
|
|
95
102
|
show_missing = true
|
|
96
103
|
skip_covered = true
|
|
97
104
|
|
|
98
|
-
[tool.setuptools.package-data]
|
|
99
|
-
"pykrait.gui.resources" = ["logo.png"]
|
|
100
|
-
|
|
101
105
|
[tool.ruff]
|
|
102
106
|
src = ["src"]
|
|
103
107
|
line-length = 88
|
|
104
108
|
target-version = "py311"
|
|
105
|
-
|
|
106
|
-
classifiers = [
|
|
107
|
-
"Programming Language :: Python :: 3",
|
|
108
|
-
"Programming Language :: Python :: 3.11",
|
|
109
|
-
"Programming Language :: Python :: 3.12",
|
|
110
|
-
"Programming Language :: Python :: 3.13",
|
|
111
|
-
]
|
|
@@ -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()
|