p-brain 3.0.0__py3-none-any.whl

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 (116) hide show
  1. p_brain-3.0.0.dist-info/METADATA +596 -0
  2. p_brain-3.0.0.dist-info/RECORD +116 -0
  3. p_brain-3.0.0.dist-info/WHEEL +5 -0
  4. p_brain-3.0.0.dist-info/entry_points.txt +2 -0
  5. p_brain-3.0.0.dist-info/licenses/LICENSE +21 -0
  6. p_brain-3.0.0.dist-info/top_level.txt +1 -0
  7. pbrain/__init__.py +48 -0
  8. pbrain/__main__.py +6 -0
  9. pbrain/_console.py +35 -0
  10. pbrain/_paths.py +34 -0
  11. pbrain/aggregation/__init__.py +10 -0
  12. pbrain/aggregation/base.py +62 -0
  13. pbrain/aggregation/median_curve.py +257 -0
  14. pbrain/aggregation/parcel.py +124 -0
  15. pbrain/aggregation/region.py +122 -0
  16. pbrain/aggregation/slice_wise.py +101 -0
  17. pbrain/aggregation/voxelwise.py +83 -0
  18. pbrain/aif/__init__.py +10 -0
  19. pbrain/aif/_backshift.py +156 -0
  20. pbrain/aif/base.py +74 -0
  21. pbrain/aif/cnn.py +610 -0
  22. pbrain/aif/cnn_sss_shifted.py +121 -0
  23. pbrain/aif/curve_file.py +105 -0
  24. pbrain/aif/deterministic.py +138 -0
  25. pbrain/aif/from_file.py +87 -0
  26. pbrain/aif/manual.py +334 -0
  27. pbrain/aif/ranker.py +178 -0
  28. pbrain/cli/__init__.py +1 -0
  29. pbrain/cli/__main__.py +145 -0
  30. pbrain/cli/_config_file.py +108 -0
  31. pbrain/cli/_deps.py +106 -0
  32. pbrain/cli/_fetch.py +164 -0
  33. pbrain/cli/_setup.py +136 -0
  34. pbrain/cli/cohort.py +220 -0
  35. pbrain/cli/run.py +398 -0
  36. pbrain/core/__init__.py +30 -0
  37. pbrain/core/config.py +94 -0
  38. pbrain/core/devices.py +141 -0
  39. pbrain/core/discovery.py +92 -0
  40. pbrain/core/logs.py +77 -0
  41. pbrain/core/manifest.py +72 -0
  42. pbrain/core/path_scheme.py +66 -0
  43. pbrain/core/pipeline.py +176 -0
  44. pbrain/core/plugin.py +36 -0
  45. pbrain/core/qc.py +166 -0
  46. pbrain/core/stage.py +69 -0
  47. pbrain/demo/__init__.py +8 -0
  48. pbrain/demo/__main__.py +452 -0
  49. pbrain/diagnostics/__init__.py +10 -0
  50. pbrain/diagnostics/_generic.py +135 -0
  51. pbrain/diagnostics/base.py +51 -0
  52. pbrain/diagnostics/conversion.py +116 -0
  53. pbrain/diagnostics/montage.py +221 -0
  54. pbrain/diagnostics/patlak.py +166 -0
  55. pbrain/diagnostics/tikhonov.py +278 -0
  56. pbrain/diagnostics/tikhonov_bayes.py +179 -0
  57. pbrain/diffusion/__init__.py +15 -0
  58. pbrain/diffusion/base.py +102 -0
  59. pbrain/diffusion/csd.py +128 -0
  60. pbrain/diffusion/dki.py +101 -0
  61. pbrain/diffusion/dki_micro.py +113 -0
  62. pbrain/diffusion/dti.py +92 -0
  63. pbrain/diffusion/fwdti.py +88 -0
  64. pbrain/diffusion/noddi.py +118 -0
  65. pbrain/diffusion/rsi.py +159 -0
  66. pbrain/diffusion/tractography.py +203 -0
  67. pbrain/io/__init__.py +1 -0
  68. pbrain/io/ir_assembly.py +388 -0
  69. pbrain/io/loaders/__init__.py +31 -0
  70. pbrain/io/loaders/base.py +62 -0
  71. pbrain/io/loaders/dicom.py +116 -0
  72. pbrain/io/loaders/dwi.py +182 -0
  73. pbrain/io/loaders/nifti.py +113 -0
  74. pbrain/io/loaders/parrec.py +164 -0
  75. pbrain/io/path_schemes/__init__.py +10 -0
  76. pbrain/io/path_schemes/base.py +5 -0
  77. pbrain/io/path_schemes/bids_like.py +131 -0
  78. pbrain/io/path_schemes/legacy.py +103 -0
  79. pbrain/io/subject_discovery.py +130 -0
  80. pbrain/models/__init__.py +17 -0
  81. pbrain/models/_baseline_shift.py +359 -0
  82. pbrain/models/_patlak_tools.py +528 -0
  83. pbrain/models/base.py +168 -0
  84. pbrain/models/extended_tofts.py +366 -0
  85. pbrain/models/patlak.py +475 -0
  86. pbrain/models/patlak_legacy.py +59 -0
  87. pbrain/models/tikhonov.py +1046 -0
  88. pbrain/models/tikhonov_bayes.py +93 -0
  89. pbrain/models/tikhonov_legacy.py +60 -0
  90. pbrain/normalisation/__init__.py +10 -0
  91. pbrain/normalisation/base.py +37 -0
  92. pbrain/normalisation/baseline_p95.py +90 -0
  93. pbrain/normalisation/identity.py +55 -0
  94. pbrain/normalisation/legacy_shift.py +260 -0
  95. pbrain/signal_to_conc/__init__.py +10 -0
  96. pbrain/signal_to_conc/base.py +41 -0
  97. pbrain/signal_to_conc/baseline.py +113 -0
  98. pbrain/signal_to_conc/saturation_recovery.py +117 -0
  99. pbrain/signal_to_conc/spgr.py +95 -0
  100. pbrain/signal_to_conc/vfa_spgr.py +75 -0
  101. pbrain/stages/__init__.py +92 -0
  102. pbrain/stages/_builtin.py +1195 -0
  103. pbrain/stages/_diagnostics.py +306 -0
  104. pbrain/t1_m0/__init__.py +10 -0
  105. pbrain/t1_m0/base.py +56 -0
  106. pbrain/t1_m0/inversion_recovery.py +315 -0
  107. pbrain/t1_m0/preloaded.py +77 -0
  108. pbrain/t1_m0/vfa_spgr.py +98 -0
  109. pbrain/tissue_roi/__init__.py +10 -0
  110. pbrain/tissue_roi/base.py +67 -0
  111. pbrain/tissue_roi/command.py +113 -0
  112. pbrain/tissue_roi/fastsurfer.py +38 -0
  113. pbrain/tissue_roi/manual.py +38 -0
  114. pbrain/tissue_roi/preloaded.py +202 -0
  115. pbrain/tissue_roi/synthseg.py +248 -0
  116. pbrain/tissue_roi/voxelwise.py +69 -0
@@ -0,0 +1,596 @@
1
+ Metadata-Version: 2.4
2
+ Name: p-brain
3
+ Version: 3.0.0
4
+ Summary: A modular, cross-platform framework for automated DCE-MRI and diffusion MRI research.
5
+ Author-email: Edis Devin Tireli <aizibuzi@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2023 Edis Devin Tireli
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/edtireli/p-brain
29
+ Project-URL: Repository, https://github.com/edtireli/p-brain
30
+ Project-URL: Issues, https://github.com/edtireli/p-brain/issues
31
+ Keywords: MRI,DCE-MRI,diffusion,pharmacokinetics,neuroimaging,Patlak,Tikhonov
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Science/Research
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
41
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: numpy>=1.24
46
+ Requires-Dist: scipy>=1.10
47
+ Requires-Dist: matplotlib>=3.7
48
+ Requires-Dist: nibabel>=5.0
49
+ Provides-Extra: cnn
50
+ Requires-Dist: tensorflow>=2.13; extra == "cnn"
51
+ Provides-Extra: diffusion
52
+ Requires-Dist: dipy>=1.7; extra == "diffusion"
53
+ Provides-Extra: noddi
54
+ Requires-Dist: dmri-amico>=2.0; extra == "noddi"
55
+ Provides-Extra: dicom
56
+ Requires-Dist: pydicom>=2.4; extra == "dicom"
57
+ Provides-Extra: yaml
58
+ Requires-Dist: pyyaml>=6.0; extra == "yaml"
59
+ Provides-Extra: torch
60
+ Requires-Dist: torch>=2.0; extra == "torch"
61
+ Provides-Extra: dev
62
+ Requires-Dist: pytest>=7.0; extra == "dev"
63
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
64
+ Provides-Extra: docs
65
+ Requires-Dist: mkdocs>=1.5; extra == "docs"
66
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
67
+ Requires-Dist: mkdocs-material>=9.0; extra == "docs"
68
+ Provides-Extra: all
69
+ Requires-Dist: tensorflow>=2.13; extra == "all"
70
+ Requires-Dist: dipy>=1.7; extra == "all"
71
+ Requires-Dist: dmri-amico>=2.0; extra == "all"
72
+ Requires-Dist: pydicom>=2.4; extra == "all"
73
+ Requires-Dist: pyyaml>=6.0; extra == "all"
74
+ Requires-Dist: pytest>=7.0; extra == "all"
75
+ Requires-Dist: pytest-cov>=4.0; extra == "all"
76
+ Dynamic: license-file
77
+
78
+ # _p_-Brain — a modular framework for automated DCE-MRI & diffusion research
79
+
80
+ [![CI](https://github.com/edtireli/p-brain/actions/workflows/ci.yml/badge.svg)](https://github.com/edtireli/p-brain/actions/workflows/ci.yml)
81
+ [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://www.python.org/)
82
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
83
+ [![Platforms](https://img.shields.io/badge/os-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey.svg)](#install)
84
+
85
+ <img width="2500" height="549" alt="pbrainplatform_banner" src="https://github.com/user-attachments/assets/e0c55c26-5c31-468f-9380-3b045e3a495c" />
86
+
87
+ **_p_-Brain is a cross-platform Python command-line tool.** Install it with
88
+ `pip` on Linux, macOS, or Windows (Python 3.10–3.12), point `pbrain run` at a
89
+ subject's scans, and it produces the full derivatives tree — no notebook, no
90
+ GUI, no server required. The CLI is the product; an optional macOS desktop app
91
+ is just one front-end on top of it (see [below](#optional-macos-app)).
92
+
93
+ _p_-Brain takes raw dynamic-contrast-enhanced (and diffusion) MRI through the
94
+ whole analysis — T1/M0 mapping, arterial-input-function extraction,
95
+ signal-to-concentration conversion, tissue parcellation, pharmacokinetic and
96
+ diffusion modelling — and produces standardised voxel-, tissue-, and
97
+ parcel-level results, fully automatically.
98
+
99
+ It is two things at once. **As shipped it is a validated, ready-to-run
100
+ pipeline** you can point at real scanner data today and get publication-grade
101
+ maps (Ki, CBF, MTT, CTH, FA, …). And it is a **template you extend**: each step
102
+ is a self-contained plug-in, so adding your own kinetic model — or a different
103
+ AIF, segmentation backend, or whole stage — is a single file and **no changes
104
+ to the core**. Drop a model into `pbrain/models/`, call it with `--models
105
+ yourmodel`, and it is run on every subject, aggregated to every anatomical
106
+ level, written as NIfTI/CSV/JSON, and given diagnostics automatically.
107
+
108
+ The aim is to let groups stop re-implementing the same plumbing: use it as-is,
109
+ modify what you need, and extend it to go beyond — while everyone's outputs
110
+ stay directly comparable.
111
+
112
+ > If you use _p_-Brain in your research, please **cite our paper** (Tireli et
113
+ > al.; see [Citation](#citation)).
114
+
115
+ > **Author:** Edis Devin Tireli, M.Sc., Ph.D. student
116
+ > **Affiliations:** Functional Imaging Unit, Copenhagen University Hospital – Rigshospitalet; Department of Neuroscience and Department of Clinical Medicine, University of Copenhagen.
117
+
118
+ ---
119
+
120
+ ## Contents
121
+
122
+ 1. [Why a framework](#why-a-framework) — the idea, and how the pieces fit
123
+ 2. [Install](#install)
124
+ 3. [How to run](#how-to-run) — first steps, the flags explained, quick start
125
+ 4. [Add your own model — it's one file](#add-your-own) — the headline feature
126
+ 5. [Config files](#config-files)
127
+ 6. [Models](#models) — what's shipped, defaults, and every option
128
+ 7. [Diffusion & connectomics](#diffusion)
129
+ 8. [Outputs](#outputs) — the standardised result tree
130
+ 9. [Representative output](#representative-output)
131
+ 10. [Demo](#demo) · [Repository structure](#repository-structure) · [Citation](#citation)
132
+
133
+ ---
134
+
135
+ ## Why a framework
136
+
137
+ DCE-MRI analysis is a chain of stages — fit T1, find the artery, convert signal
138
+ to contrast concentration, segment tissue, fit a kinetic model, summarise. In
139
+ most labs each of these is bespoke code, so results are hard to compare and a
140
+ new model means re-plumbing the whole pipeline.
141
+
142
+ _p_-Brain makes each stage a **plug-in**: a single file that declares what it
143
+ needs and what it produces, discovered automatically at runtime. The
144
+ orchestrator wires the stages together by those declarations — so:
145
+
146
+ - **adding a method changes one file, never the core;**
147
+ - **every model is run, aggregated, and reported the same way**, giving
148
+ standardised, directly-comparable outputs across groups;
149
+ - you can **swap any step** (a different AIF, your lab's segmentation tool, a
150
+ new deconvolution) by name on the command line.
151
+
152
+ There are 12 such plug-points. The full contract and copy-paste templates live
153
+ in **[`docs/ADDING_PLUGINS.md`](docs/ADDING_PLUGINS.md)**; the design rationale
154
+ in **[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)**. Read those two when you
155
+ want to extend the framework — the rest of this README gets you running first.
156
+
157
+ ---
158
+
159
+ ## Install
160
+
161
+ _p_-Brain is a normal `pip`-installable Python package. It runs on **Linux,
162
+ macOS, and Windows** with **Python 3.10–3.12**.
163
+
164
+ ```bash
165
+ pip install p-brain # core install — installs the `pbrain` command
166
+ pbrain --help
167
+ ```
168
+
169
+ That gives you the `pbrain` command (and `python -m pbrain`) plus the light core
170
+ dependencies — `numpy, scipy, matplotlib, nibabel`. Everything heavier is an
171
+ **opt-in extra**, installed only if you select a plug-in that needs it:
172
+
173
+ ```bash
174
+ pip install "p-brain[cnn]" # TensorFlow — CNN arterial-input-function (default AIF)
175
+ pip install "p-brain[diffusion]" # dipy — the diffusion track (DTI/DKI/CSD/…)
176
+ pip install "p-brain[dicom]" # pydicom — DICOM input (see DICOM input below)
177
+ pip install "p-brain[all]" # everything in one go
178
+ ```
179
+
180
+ The default AIF (`cnn_sss_shifted`) needs the CNN extra **and** its trained
181
+ `.keras` weights (~1.2 GB), archived on Zenodo. Download them once — they cache
182
+ under `~/.p-brain/AI` and every later run finds them automatically:
183
+
184
+ ```bash
185
+ pbrain setup # interactive: installs extras + offers to fetch weights & data
186
+ pbrain fetch-weights # just the CNN weights (Zenodo 10.5281/zenodo.15697443)
187
+ pbrain fetch-data # the example subject sub-01 (Zenodo 10.5281/zenodo.20826857)
188
+ ```
189
+
190
+ To run weights-free, pick a model-free AIF (`--aif deterministic`, or `from_file`
191
+ / `manual` for your own ROIs/curves), or try `python -m pbrain.demo`, which needs
192
+ no weights or data at all.
193
+
194
+ **From source** (for development or the bleeding edge):
195
+
196
+ ```bash
197
+ git clone https://github.com/edtireli/p-brain.git
198
+ cd p-brain
199
+ pip install -e ".[dev]" # editable install + test tooling
200
+ pytest -q # run the test suite
201
+ ```
202
+
203
+ **Check your environment.** `python -m pbrain check-deps` verifies the
204
+ third-party Python deps and offers to install any that are missing;
205
+ `python -m pbrain setup` additionally inspects external tooling (`dcm2niix`,
206
+ optionally FreeSurfer for segmentation, GPU support) and walks you through it.
207
+
208
+ ### DICOM input
209
+
210
+ _p_-Brain reads **NIfTI** (`.nii` / `.nii.gz`) and **Philips PAR/REC** natively.
211
+ **DICOM** is supported through [`dcm2niix`](https://github.com/rordenlab/dcm2niix),
212
+ the standard, well-validated DICOM→NIfTI converter: point `--dce` / `--ir` /
213
+ `--dwi` at a DICOM file or a folder of DICOMs and _p_-Brain calls `dcm2niix`
214
+ under the hood, picking up the reconstructed NIfTI (and, for diffusion, the
215
+ `.bval` / `.bvec` gradient tables it writes).
216
+
217
+ Install `dcm2niix` from its own channel — it is a compiled binary, not a pip
218
+ package:
219
+
220
+ ```bash
221
+ conda install -c conda-forge dcm2niix # any OS (recommended)
222
+ brew install dcm2niix # macOS
223
+ sudo apt install dcm2niix # Debian / Ubuntu
224
+ # Windows: download the release .zip from the dcm2niix GitHub and add it to PATH
225
+ ```
226
+
227
+ `pip install "p-brain[dicom]"` adds `pydicom` for header inspection;
228
+ `dcm2niix` must be on your `PATH` for the actual conversion. Run
229
+ `python -m pbrain check-deps` to confirm it is found.
230
+
231
+ ### Optional macOS app
232
+
233
+ A native macOS desktop app wraps this CLI in a point-and-click GUI for users who
234
+ prefer not to touch a terminal. It is **entirely optional** — the Python CLI
235
+ above is the product and the canonical interface; the app simply drives it.
236
+
237
+ ---
238
+
239
+ ## How to run
240
+
241
+ A run takes one subject's raw data and produces its full derivatives tree.
242
+ The first three steps:
243
+
244
+ 1. **Point at your data.** `--dce` is the 4-D DCE series (NIfTI, PAR/REC, or
245
+ DICOM — converted automatically). `--ir` is the inversion-recovery series
246
+ used to fit T1/M0; `--dwi` is an optional diffusion scan. Each of `--dce`,
247
+ `--t1`, `--ir` accepts a **full path, a filename, a protocol-name substring,
248
+ or `auto`** — so you can write `--dce hperf --t1 auto --ir auto` once and
249
+ reuse it across subjects whose scan numbers differ (raw PAR/REC are matched
250
+ by their Philips `Protocol name`; `--ir auto` assembles the `TI_*` series).
251
+ 2. **Choose your methods.** `--models patlak,tikhonov` selects the kinetic
252
+ models; `--aif`, `--tissue-roi`, `--t1m0` select how each upstream step is
253
+ done. Sensible defaults mean you can omit most of them.
254
+ 3. **Choose your output levels.** `--aggregations voxelwise,region,parcel`
255
+ controls whether you get whole-brain maps, tissue-class summaries, and/or
256
+ per-parcel tables.
257
+
258
+ ### The flags, explained
259
+
260
+ | flag | meaning | default |
261
+ |---|---|---|
262
+ | `--subject-dir` | where the derivatives tree is written | (required) |
263
+ | `--dce` | 4-D DCE series (NIfTI / PAR-REC / DICOM) | (required) |
264
+ | `--ir` | inversion-recovery series for the T1/M0 fit | — |
265
+ | `--dwi` | diffusion series (for the diffusion track) | — |
266
+ | `--t1m0` | how T1 & M0 are obtained (`inversion_recovery`, `vfa_spgr`, …) | `inversion_recovery` |
267
+ | `--aif` | arterial-input-function method | `cnn_sss_shifted` |
268
+ | `--tissue-roi` | parcellation source (`synthseg`, `fastsurfer`, `command`, `preloaded`, …) | `voxelwise` |
269
+ | `--models` | comma-list of kinetic models to run | `patlak,tikhonov` |
270
+ | `--diffusion` | comma-list of diffusion models, or `default`/`all` | (auto when `--dwi` given) |
271
+ | `--aggregations` | output levels: `voxelwise,region,parcel,slice_wise` | `voxelwise,parcel,region` |
272
+ | `--device` | `cpu` / `mps` / `cuda` / `auto` | `cpu` |
273
+ | `--config` | read all of the above from a `.toml`/`.yaml` file | — |
274
+
275
+ Two niceties: runs are **resumable** (a finished stage is skipped on re-run;
276
+ `--force` recomputes), and every output carries **provenance** (the `pbrain`
277
+ version and exact options that made it). Use `--quiet` / `--verbose` /
278
+ `--log-file` to control logging.
279
+
280
+ ### Quick start
281
+
282
+ ```bash
283
+ # Minimal: DCE + IR, the default models, all output levels
284
+ python -m pbrain run \
285
+ --subject-dir /data/sub-01 \
286
+ --dce dce.nii.gz --ir ir.nii.gz \
287
+ --models patlak,tikhonov \
288
+ --aggregations voxelwise,parcel,region
289
+
290
+ # With diffusion (FA/MD + tractography) in the same command
291
+ python -m pbrain run \
292
+ --subject-dir /data/sub-01 \
293
+ --dce dce.nii.gz --ir ir.nii.gz --dwi dwi.nii.gz \
294
+ --models patlak,tikhonov --diffusion default
295
+ ```
296
+
297
+ **See what's available** — every plug-in, its inputs/outputs, its diagnostic:
298
+
299
+ ```bash
300
+ python -m pbrain list # all plug-points at a glance
301
+ python -m pbrain list models # one plug-point in detail
302
+ ```
303
+
304
+ **Run a whole cohort** — parallel, resumable, error-isolated:
305
+
306
+ ```bash
307
+ # ── one flag, raw scanner data: point --cohort at a folder of subjects ──
308
+ # Each sub-directory is a subject of raw Philips PAR/REC. Inputs are
309
+ # auto-discovered by protocol name (DCE = hperf*; the TI_* saturation-recovery
310
+ # series is assembled into an IR; a 3-D T1 anatomical for SynthSeg), then the
311
+ # full pipeline runs with ALL kinetic models at tissue (region) + parcel level.
312
+ # No config needed. Add --force for a fresh re-run. Pass several roots to run
313
+ # patients + controls + follow-ups in one go.
314
+ python -m pbrain run-cohort --cohort /data/patients /data/controls --workers 4 --force
315
+
316
+ # pick models / levels, or skip known-bad subjects:
317
+ python -m pbrain run-cohort --cohort /data/patients --workers 4 \
318
+ --models tikhonov,inverse_gaussian --aggregations region,parcel \
319
+ --exclude 20221003x1 # --voxelwise to fit per-voxel (slower)
320
+
321
+ # ── config mode, pre-converted NIfTI cohorts: inputs from a shared config ──
322
+ python -m pbrain run-cohort --config study.toml --data-dir /data --workers 8
323
+ python -m pbrain run-cohort --config study.toml --subjects-glob '/data/sub-*' --workers 8
324
+ ```
325
+
326
+ `--cohort` is the one-flag "do the whole study" path: it resolves each subject's
327
+ DCE / IR / T1 itself (scan numbers vary between subjects, so they can't be
328
+ templated by name) and fits every model at the parcel level by default
329
+ (average-then-fit — exactly the resolution these models support, and tractable
330
+ across hundreds of subjects). Use `--config` mode when inputs are already
331
+ NIfTI and named consistently.
332
+
333
+ **Override any option** with `--opt <plug-point>.<plugin>.<key>=<value>`, e.g.
334
+ `--opt models.tikhonov.lambda_selection=evidence`. Every knob is documented
335
+ under [Models](#models).
336
+
337
+ ---
338
+
339
+ ## Add your own
340
+
341
+ > **Step-by-step guide:** [`docs/ADDING_PLUGINS.md`](docs/ADDING_PLUGINS.md) —
342
+ > templates for models, AIF methods, segmentation backends, diffusion models,
343
+ > and whole pipeline stages. Start there.
344
+
345
+ A new kinetic model is one file and no core changes. You write the maths; the
346
+ framework runs it on every voxel/curve, aggregates the result to tissue classes
347
+ and parcels, writes NIfTI + CSV + JSON, and renders fit diagnostics.
348
+
349
+ `pbrain/models/two_cxm.py`:
350
+
351
+ ```python
352
+ from dataclasses import dataclass
353
+ from typing import Any, ClassVar
354
+ import numpy as np
355
+ from .base import CurveInputs, ModelResult
356
+
357
+ @dataclass(frozen=True, slots=True)
358
+ class TwoCXM:
359
+ key: ClassVar[str] = "two_cxm" # the name you call it by
360
+ name: ClassVar[str] = "Two-compartment exchange model"
361
+ description: ClassVar[str] = "Fp, PS, vp, ve via 2CXM least-squares."
362
+ outputs: ClassVar[tuple] = ("fp", "ps", "vp", "ve")
363
+ units: ClassVar[dict] = {"fp": "mL/100g/min", "ps": "mL/100g/min",
364
+ "vp": "fraction", "ve": "fraction"}
365
+
366
+ def fit(self, inputs: CurveInputs, **opts: Any) -> ModelResult:
367
+ ... # your maths → fp, ps, vp, ve
368
+ return ModelResult(maps={"fp": fp, "ps": ps, "vp": vp, "ve": ve},
369
+ units=dict(self.units))
370
+
371
+ PLUGIN = TwoCXM()
372
+ ```
373
+
374
+ That's the entire integration. Now:
375
+
376
+ ```bash
377
+ python -m pbrain run --models two_cxm,patlak ...
378
+ ```
379
+
380
+ runs your model alongside Patlak, produces `fp/ps/vp/ve` maps, aggregates each
381
+ to region and parcel level, and draws per-tissue fit plots — automatically.
382
+
383
+ The **step-by-step guide** for this and the other 11 plug-points (AIF methods,
384
+ segmentation backends, diffusion models, whole stages) is
385
+ **[`docs/ADDING_PLUGINS.md`](docs/ADDING_PLUGINS.md)** — start there.
386
+
387
+ ---
388
+
389
+ ## Config files
390
+
391
+ For reproducibility, put the whole run in a versioned file —
392
+ `pbrain run --config study.toml` (CLI flags still override it):
393
+
394
+ ```toml
395
+ subject_dir = "/data/sub-01"
396
+
397
+ [inputs]
398
+ dce = "dce.nii.gz"
399
+ ir = "ir.nii.gz"
400
+ dwi = "dwi.nii.gz"
401
+
402
+ [pipeline]
403
+ t1m0 = "inversion_recovery"
404
+ aif = "cnn_sss_shifted"
405
+ tissue_roi = "synthseg"
406
+ models = ["patlak", "tikhonov"]
407
+ diffusion = "default"
408
+ aggregations = ["region", "parcel"]
409
+
410
+ [acquisition]
411
+ flip_angle_deg = 30.0
412
+ tr_s = 0.01118
413
+
414
+ [options] # same keys as --opt
415
+ "models.tikhonov.lambda_selection" = "evidence"
416
+ ```
417
+
418
+ TOML works out of the box; YAML needs `pip install pyyaml`.
419
+
420
+ ---
421
+
422
+ ## Models
423
+
424
+ Set any option with `--opt models.<key>.<opt>=<value>` (or in a config file).
425
+ Defaults are what you get without setting anything.
426
+
427
+ **`patlak`** — blood–brain-barrier influx **Ki** and blood volume **vp** from
428
+ the Patlak graphical analysis.
429
+
430
+ | option | default | what it does |
431
+ |---|---|---|
432
+ | `regression` | `huber` | slope fit: `huber` (robust to leverage points) or `ols`. |
433
+ | `tail_mode` | `smart` | which late points enter the fit: `smart` (curvature-detected linear tail) or `legacy` (fixed upper-2⁄3 window). |
434
+ | `aif_min_fraction` | `0.05` | drop AIF samples below this fraction of the peak (avoids a near-zero AIF blowing Ki up). |
435
+
436
+ **`tikhonov`** — **CBF, MTT, CTH** by regularised deconvolution of the residue
437
+ function.
438
+
439
+ | option | default | what it does |
440
+ |---|---|---|
441
+ | `lambda_selection` | `gcv` | regularisation strength: `gcv` (cross-validation), `lcurve`, or `evidence` (marginal likelihood — most robust on smooth curves). |
442
+ | `lambda_spacing` | `log` | λ grid spacing (`log`/`linear`). |
443
+ | `n_lambdas` | `121` | number of λ values searched. |
444
+ | `mtt_cth_method` | `residue_integral` | MTT/CTH from the residue integral or the central-volume theorem. |
445
+
446
+ **`extended_tofts`** — **Ktrans, ve, vp, kep** by constrained
447
+ Levenberg–Marquardt (no tuning needed for the default fit).
448
+
449
+ You are meant to add to this list — see [Add your own](#add-your-own).
450
+
451
+ ---
452
+
453
+ ## Diffusion
454
+
455
+ Give a diffusion scan with `--dwi` (NIfTI, PAR/REC, or DICOM — converted
456
+ automatically, gradients extracted) and the diffusion track runs in native DWI
457
+ space and resamples to your parcellation. Select models with `--diffusion`
458
+ (`dti`, a comma-list, `default` = shell-aware, or `all`); options via
459
+ `--opt diffusion.<key>.<opt>=<value>`.
460
+
461
+ ### Which model, when
462
+
463
+ - **`dti`** — the workhorse: **FA, MD, AD, RD** (+ colour-FA). Any DWI with a
464
+ b0 and one shell. **Start here for FA/MD.**
465
+ - **`dki`** — adds mean/axial/radial **kurtosis** and KFA. Needs ≥ 2 shells.
466
+ - **`dki_micro`** — WMTI microstructure (axonal water fraction, tortuosity) and
467
+ **μFA**. Multi-shell.
468
+ - **`fwdti`** — **free-water elimination**: tissue FA/MD with CSF/oedema removed
469
+ + the free-water fraction. Multi-shell.
470
+ - **`csd`** — constrained spherical deconvolution: fibre orientations for
471
+ tractography + GFA. Multi-shell preferred.
472
+ - **`rsi`** — restriction-spectrum fractions (restricted/hindered/free); needs a
473
+ high-b shell.
474
+ - **`noddi`** — neurite density / orientation dispersion; needs AMICO + high-b.
475
+
476
+ ### Connectomics (tractography → connectome)
477
+
478
+ With a fibre-orientation model (`csd` by default, or the `dti` tensor) the
479
+ diffusion track can run **tractography** and build a **structural connectome**
480
+ between parcels:
481
+
482
+ ```bash
483
+ python -m pbrain run --dwi dwi.nii.gz --diffusion csd --connectome ...
484
+ ```
485
+
486
+ This writes the streamlines (`.tck`, openable in MRtrix/TrackVis and rendered
487
+ as a track-density NIfTI for 3-D exploration) and a parcel × parcel connectivity
488
+ matrix (CSV/JSON) under `09_diffusion/`.
489
+
490
+ ---
491
+
492
+ ## Outputs
493
+
494
+ A BIDS-like derivatives tree under `<subject-dir>/derivatives/`, numbered for
495
+ natural sort order:
496
+
497
+ ```
498
+ 00_diagnostics/ fit plots + whole-brain map montages
499
+ 01_load/ loaded DCE/IR/DWI (+ timing)
500
+ 02_t1m0/ T1 map + M0 map (t1_map.nii.gz, m0_map.nii.gz)
501
+ 03_aif/ arterial input function
502
+ 04_tissue_roi/ parcellation + tissue region map
503
+ 05_signal_to_conc/ 4-D contrast concentration (concentration.nii.gz)
504
+ <converter>/diagnostics/ conversion QC plot (conversion_qc.png)
505
+ 06_normalisation/ normalised curves
506
+ 07_kinetic/<model>/ per model:
507
+ voxelwise/ whole-brain maps (nii.gz)
508
+ region/ parcel/ tissue & parcel summaries (nii.gz + csv + json)
509
+ diagnostics/{voxel,tissue,parcel,montage}/ fit plots & map montages
510
+ 08_summary/ run summary + QC
511
+ 09_diffusion/<model>/ FA/MD/… maps, + tractography & connectome
512
+ ```
513
+
514
+ Every model output exists as **nii.gz, csv, and json** at the region/parcel
515
+ levels. The T1 map, M0 map, and the **4-D concentration volume** are written as
516
+ NIfTI so you can use them directly. Each stage writes a `manifest.json` with its
517
+ provenance and a QC block (physiological-range flags). Per-model diagnostics —
518
+ the same fit plots shown in the paper — render every run.
519
+
520
+ ---
521
+
522
+ ## Representative output
523
+
524
+ Whole-brain parameter maps for one subject, masked to the brain segmentation
525
+ (see the paper for the full set and quantitative validation):
526
+
527
+ **Ki — blood–brain-barrier influx (Patlak)**
528
+ ![Ki](docs/img/ki_voxel_montage_patlak.png)
529
+
530
+ **CBF — cerebral blood flow (Tikhonov deconvolution)**
531
+ ![CBF](docs/img/cbf_montage.png)
532
+
533
+ **FA — fractional anisotropy (DTI)**
534
+ ![FA](docs/img/fa_montage.png)
535
+
536
+ These montages are produced by the pipeline itself (the `diagnostics` stage),
537
+ with data-adaptive slice layout and brain-segmentation masking. Every map is
538
+ also aggregated to tissue classes and DKT parcels.
539
+
540
+ ---
541
+
542
+ ## Demo
543
+
544
+ ```bash
545
+ python -m pbrain.demo --clean
546
+ ```
547
+
548
+ Synthesises a small phantom (no patient data), runs the **entire** pipeline
549
+ end-to-end, and writes parameter-map montages to `demo/maps/` — a self-contained
550
+ way to see the output format and confirm your install works.
551
+
552
+ ---
553
+
554
+ ## Repository structure
555
+
556
+ ```
557
+ pbrain/ the framework — everything lives here
558
+ core/ Plugin/Stage contracts, discovery, Config, Pipeline, logging, QC
559
+ io/ loaders (nifti/parrec/dicom/dwi) + output path schemes
560
+ t1_m0/ aif/ tissue_roi/ signal_to_conc/ normalisation/ upstream stages
561
+ models/ kinetic models diffusion/ diffusion models
562
+ aggregation/ voxel/region/parcel/slice rollups
563
+ diagnostics/ per-model fit plots + the montage generator
564
+ stages/ the pipeline steps (a discoverable, topologically-ordered plug-point)
565
+ cli/ demo/
566
+ docs/ ADDING_PLUGINS.md · ARCHITECTURE.md · mkdocs API reference
567
+ tests/ the test suite validation/ cohort runners
568
+ ```
569
+
570
+ ---
571
+
572
+ ## Documentation
573
+
574
+ - **API reference** — a rendered reference generated from the package
575
+ docstrings (every public class, the plug-in contracts, the kinetic and
576
+ diffusion models, the pipeline stages, and the QC functions). Build it
577
+ locally with:
578
+
579
+ ```bash
580
+ pip install "p-brain[docs]"
581
+ mkdocs build # → ./site/ (or `mkdocs serve` for a live preview)
582
+ ```
583
+
584
+ Entry points: [`docs/index.md`](docs/index.md) and the contributor
585
+ [architecture overview](docs/architecture-overview.md).
586
+ - **Extending the framework** —
587
+ [`docs/ADDING_PLUGINS.md`](docs/ADDING_PLUGINS.md) (copy-paste templates)
588
+ and [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) (full design rationale
589
+ and output layout).
590
+
591
+ ---
592
+
593
+ ## Citation
594
+
595
+ If _p_-Brain contributes to your work, please cite the accompanying paper
596
+ (Tireli et al.) and this repository. See [`LICENSE`](LICENSE) for terms.