T1Prep 0.3.8__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 (139) hide show
  1. t1prep/__init__.py +25 -0
  2. t1prep/_models.py +222 -0
  3. t1prep/_segment_utils.py +1031 -0
  4. t1prep/data/Names.tsv +44 -0
  5. t1prep/data/__init__.py +1 -0
  6. t1prep/data/atlases_surfaces_32k/atlases_surfaces_32k.md +68 -0
  7. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_100Parcels_17Networks_order.annot +0 -0
  8. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_100Parcels_17Networks_order.txt +17 -0
  9. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_200Parcels_17Networks_order.annot +0 -0
  10. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_200Parcels_17Networks_order.txt +17 -0
  11. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_400Parcels_17Networks_order.annot +0 -0
  12. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_400Parcels_17Networks_order.txt +17 -0
  13. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_600Parcels_17Networks_order.annot +0 -0
  14. t1prep/data/atlases_surfaces_32k/lh.Schaefer2018_600Parcels_17Networks_order.txt +17 -0
  15. t1prep/data/atlases_surfaces_32k/lh.aparc_DK40.freesurfer.annot +0 -0
  16. t1prep/data/atlases_surfaces_32k/lh.aparc_DK40.freesurfer.txt +13 -0
  17. t1prep/data/atlases_surfaces_32k/lh.aparc_HCP_MMP1.freesurfer.annot +0 -0
  18. t1prep/data/atlases_surfaces_32k/lh.aparc_HCP_MMP1.freesurfer.txt +14 -0
  19. t1prep/data/atlases_surfaces_32k/lh.aparc_a2009s.freesurfer.annot +0 -0
  20. t1prep/data/atlases_surfaces_32k/lh.aparc_a2009s.freesurfer.txt +12 -0
  21. t1prep/data/atlases_surfaces_32k/rh.Schaefer2018_100Parcels_17Networks_order.annot +0 -0
  22. t1prep/data/atlases_surfaces_32k/rh.Schaefer2018_200Parcels_17Networks_order.annot +0 -0
  23. t1prep/data/atlases_surfaces_32k/rh.Schaefer2018_400Parcels_17Networks_order.annot +0 -0
  24. t1prep/data/atlases_surfaces_32k/rh.Schaefer2018_600Parcels_17Networks_order.annot +0 -0
  25. t1prep/data/atlases_surfaces_32k/rh.aparc_DK40.freesurfer.annot +0 -0
  26. t1prep/data/atlases_surfaces_32k/rh.aparc_HCP_MMP1.freesurfer.annot +0 -0
  27. t1prep/data/atlases_surfaces_32k/rh.aparc_a2009s.freesurfer.annot +0 -0
  28. t1prep/data/cat_viewsurf_defaults.txt +36 -0
  29. t1prep/data/icon.png +0 -0
  30. t1prep/data/patches.csv +28 -0
  31. t1prep/data/templates_MNI152NLin2009cAsym/AAL3.csv +171 -0
  32. t1prep/data/templates_MNI152NLin2009cAsym/AAL3.nii.gz +0 -0
  33. t1prep/data/templates_MNI152NLin2009cAsym/AAL3.txt +50 -0
  34. t1prep/data/templates_MNI152NLin2009cAsym/Anatomy3.csv +187 -0
  35. t1prep/data/templates_MNI152NLin2009cAsym/Anatomy3.nii.gz +0 -0
  36. t1prep/data/templates_MNI152NLin2009cAsym/Anatomy3.txt +135 -0
  37. t1prep/data/templates_MNI152NLin2009cAsym/CoBra.csv +53 -0
  38. t1prep/data/templates_MNI152NLin2009cAsym/CoBra.nii.gz +0 -0
  39. t1prep/data/templates_MNI152NLin2009cAsym/CoBra.txt +154 -0
  40. t1prep/data/templates_MNI152NLin2009cAsym/Hammers.csv +96 -0
  41. t1prep/data/templates_MNI152NLin2009cAsym/Hammers.nii.gz +0 -0
  42. t1prep/data/templates_MNI152NLin2009cAsym/Hammers.txt +55 -0
  43. t1prep/data/templates_MNI152NLin2009cAsym/Hypothalamus.csv +51 -0
  44. t1prep/data/templates_MNI152NLin2009cAsym/Hypothalamus.nii.gz +0 -0
  45. t1prep/data/templates_MNI152NLin2009cAsym/Hypothalamus.txt +9 -0
  46. t1prep/data/templates_MNI152NLin2009cAsym/IBSR.csv +33 -0
  47. t1prep/data/templates_MNI152NLin2009cAsym/IBSR.nii.gz +0 -0
  48. t1prep/data/templates_MNI152NLin2009cAsym/IBSR.txt +577 -0
  49. t1prep/data/templates_MNI152NLin2009cAsym/JulichBrain.csv +249 -0
  50. t1prep/data/templates_MNI152NLin2009cAsym/JulichBrain.nii.gz +0 -0
  51. t1prep/data/templates_MNI152NLin2009cAsym/JulichBrain.txt +23 -0
  52. t1prep/data/templates_MNI152NLin2009cAsym/LPBA40.csv +57 -0
  53. t1prep/data/templates_MNI152NLin2009cAsym/LPBA40.nii.gz +0 -0
  54. t1prep/data/templates_MNI152NLin2009cAsym/LPBA40.txt +50 -0
  55. t1prep/data/templates_MNI152NLin2009cAsym/Melbourne-Subcortex-Atlas-7T.csv +63 -0
  56. t1prep/data/templates_MNI152NLin2009cAsym/Melbourne-Subcortex-Atlas-7T.nii.gz +0 -0
  57. t1prep/data/templates_MNI152NLin2009cAsym/Melbourne-Subcortex-Atlas-7T.txt +24 -0
  58. t1prep/data/templates_MNI152NLin2009cAsym/Mori.csv +129 -0
  59. t1prep/data/templates_MNI152NLin2009cAsym/Mori.nii.gz +0 -0
  60. t1prep/data/templates_MNI152NLin2009cAsym/Mori.txt +32 -0
  61. t1prep/data/templates_MNI152NLin2009cAsym/Neuromorphometrics.csv +137 -0
  62. t1prep/data/templates_MNI152NLin2009cAsym/Neuromorphometrics.nii.gz +0 -0
  63. t1prep/data/templates_MNI152NLin2009cAsym/Neuromorphometrics.txt +140 -0
  64. t1prep/data/templates_MNI152NLin2009cAsym/SUIT.csv +29 -0
  65. t1prep/data/templates_MNI152NLin2009cAsym/SUIT.nii.gz +0 -0
  66. t1prep/data/templates_MNI152NLin2009cAsym/SUIT.txt +16 -0
  67. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_100Parcels_17Networks_order.csv +101 -0
  68. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_100Parcels_17Networks_order.nii.gz +0 -0
  69. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_100Parcels_17Networks_order.txt +17 -0
  70. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_200Parcels_17Networks_order.csv +201 -0
  71. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_200Parcels_17Networks_order.nii.gz +0 -0
  72. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_200Parcels_17Networks_order.txt +17 -0
  73. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_400Parcels_17Networks_order.csv +401 -0
  74. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_400Parcels_17Networks_order.nii.gz +0 -0
  75. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_400Parcels_17Networks_order.txt +17 -0
  76. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_600Parcels_17Networks_order.csv +601 -0
  77. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_600Parcels_17Networks_order.nii.gz +0 -0
  78. t1prep/data/templates_MNI152NLin2009cAsym/Schaefer2018_600Parcels_17Networks_order.txt +17 -0
  79. t1prep/data/templates_MNI152NLin2009cAsym/Template_05mm_bet.nii.gz +0 -0
  80. t1prep/data/templates_MNI152NLin2009cAsym/Template_4_GS.nii.gz +0 -0
  81. t1prep/data/templates_MNI152NLin2009cAsym/Thalamic-Nuclei.csv +25 -0
  82. t1prep/data/templates_MNI152NLin2009cAsym/Thalamic-Nuclei.nii.gz +0 -0
  83. t1prep/data/templates_MNI152NLin2009cAsym/Thalamic-Nuclei.txt +33 -0
  84. t1prep/data/templates_MNI152NLin2009cAsym/Thalamus.csv +15 -0
  85. t1prep/data/templates_MNI152NLin2009cAsym/Thalamus.nii.gz +0 -0
  86. t1prep/data/templates_MNI152NLin2009cAsym/Thalamus.txt +39 -0
  87. t1prep/data/templates_MNI152NLin2009cAsym/cat_bloodvessels.nii.gz +0 -0
  88. t1prep/data/templates_MNI152NLin2009cAsym/cat_wmh.nii.gz +0 -0
  89. t1prep/data/templates_MNI152NLin2009cAsym/cat_wmh_miccai2017.nii.gz +0 -0
  90. t1prep/data/templates_MNI152NLin2009cAsym/csf_TPM.nii.gz +0 -0
  91. t1prep/data/templates_surfaces_32k/fsavg.index2D_256x128.txt +32768 -0
  92. t1prep/data/templates_surfaces_32k/lh.central.Template_T1.gii +77 -0
  93. t1prep/data/templates_surfaces_32k/lh.central.freesurfer.gii +114 -0
  94. t1prep/data/templates_surfaces_32k/lh.inflated.freesurfer.gii +73 -0
  95. t1prep/data/templates_surfaces_32k/lh.mask +0 -0
  96. t1prep/data/templates_surfaces_32k/lh.mc.freesurfer.gii +32 -0
  97. t1prep/data/templates_surfaces_32k/lh.patch.freesurfer.gii +66 -0
  98. t1prep/data/templates_surfaces_32k/lh.sphere.freesurfer.gii +126 -0
  99. t1prep/data/templates_surfaces_32k/lh.sqrtsulc.freesurfer.gii +32 -0
  100. t1prep/data/templates_surfaces_32k/lh.thickness.Template_T1 +0 -0
  101. t1prep/data/templates_surfaces_32k/mesh.central.Template_T1.gii +39 -0
  102. t1prep/data/templates_surfaces_32k/mesh.central.freesurfer.gii +39 -0
  103. t1prep/data/templates_surfaces_32k/mesh.inflated.freesurfer.gii +39 -0
  104. t1prep/data/templates_surfaces_32k/rh.central.Template_T1.gii +77 -0
  105. t1prep/data/templates_surfaces_32k/rh.central.freesurfer.gii +114 -0
  106. t1prep/data/templates_surfaces_32k/rh.inflated.freesurfer.gii +73 -0
  107. t1prep/data/templates_surfaces_32k/rh.mask +0 -0
  108. t1prep/data/templates_surfaces_32k/rh.mc.freesurfer.gii +32 -0
  109. t1prep/data/templates_surfaces_32k/rh.patch.freesurfer.gii +74 -0
  110. t1prep/data/templates_surfaces_32k/rh.sphere.freesurfer.gii +126 -0
  111. t1prep/data/templates_surfaces_32k/rh.sqrtsulc.freesurfer.gii +32 -0
  112. t1prep/data/templates_surfaces_32k/rh.thickness.Template_T1 +0 -0
  113. t1prep/data/templates_surfaces_32k/templates_surfaces_32k.md +75 -0
  114. t1prep/data/templates_surfaces_4k/lh.central.freesurfer.gii +77 -0
  115. t1prep/data/templates_surfaces_4k/lh.inflated.freesurfer.gii +73 -0
  116. t1prep/data/templates_surfaces_4k/lh.mask +0 -0
  117. t1prep/data/templates_surfaces_4k/lh.sphere.freesurfer.gii +90 -0
  118. t1prep/data/templates_surfaces_4k/rh.central.freesurfer.gii +77 -0
  119. t1prep/data/templates_surfaces_4k/rh.inflated.freesurfer.gii +73 -0
  120. t1prep/data/templates_surfaces_4k/rh.mask +0 -0
  121. t1prep/data/templates_surfaces_4k/rh.sphere.freesurfer.gii +78 -0
  122. t1prep/dice.py +19 -0
  123. t1prep/gui/__init__.py +11 -0
  124. t1prep/gui/cat_viewimage.py +1241 -0
  125. t1prep/gui/cat_viewsurf.py +4043 -0
  126. t1prep/metrics.py +407 -0
  127. t1prep/qa.py +625 -0
  128. t1prep/realign_longitudinal.py +734 -0
  129. t1prep/report.py +126 -0
  130. t1prep/segment.py +1491 -0
  131. t1prep/surface_estimation.py +632 -0
  132. t1prep/t1prep.py +229 -0
  133. t1prep/utils.py +730 -0
  134. t1prep-0.3.8.dist-info/METADATA +765 -0
  135. t1prep-0.3.8.dist-info/RECORD +139 -0
  136. t1prep-0.3.8.dist-info/WHEEL +5 -0
  137. t1prep-0.3.8.dist-info/entry_points.txt +3 -0
  138. t1prep-0.3.8.dist-info/licenses/LICENSE +201 -0
  139. t1prep-0.3.8.dist-info/top_level.txt +1 -0
t1prep/__init__.py ADDED
@@ -0,0 +1,25 @@
1
+ __version__ = "0.3.8"
2
+
3
+ from .t1prep import run_t1prep
4
+ from .utils import (
5
+ progress_bar,
6
+ remove_file,
7
+ resample_and_save_nifti,
8
+ get_resampled_header,
9
+ align_brain,
10
+ get_filenames,
11
+ get_volume_native_space,
12
+ )
13
+ from .metrics import compute_dice_nifti
14
+
15
+ __all__ = [
16
+ "run_t1prep",
17
+ "progress_bar",
18
+ "remove_file",
19
+ "resample_and_save_nifti",
20
+ "get_resampled_header",
21
+ "align_brain",
22
+ "get_filenames",
23
+ "get_volume_native_space",
24
+ "compute_dice_nifti",
25
+ ]
t1prep/_models.py ADDED
@@ -0,0 +1,222 @@
1
+ """Model file management: location constants, presence checks, and downloading.
2
+
3
+ Model weights are not bundled in the PyPI wheel (they exceed the 100 MB
4
+ limit). This module is responsible for locating the model directory,
5
+ checking whether all required files are present, and downloading them from
6
+ the GitHub release asset when they are missing.
7
+
8
+ The download can be triggered:
9
+ - automatically at first use (called by ``segment.py``)
10
+ - manually by the user via the ``t1prep-download-models`` console script
11
+
12
+ Console script entry point: ``t1prep._models:main``
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import shutil
18
+ import sys
19
+ import tempfile
20
+ import urllib.request
21
+ import zipfile
22
+ from pathlib import Path
23
+
24
+ from deepmriprep.utils import DATA_PATH
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Constants
28
+ # ---------------------------------------------------------------------------
29
+
30
+ #: Directory where model weight files are stored at runtime.
31
+ MODEL_DIR: Path = Path(DATA_PATH) / "models"
32
+
33
+ #: All model filenames that must be present for the pipeline to run.
34
+ MODEL_FILES: list[str] = (
35
+ [
36
+ "brain_extraction_bbox_model.pt",
37
+ "brain_extraction_model.pt",
38
+ "segmentation_nogm_model.pt",
39
+ ]
40
+ + [f"segmentation_patch_{i}_model.pt" for i in range(18)]
41
+ + ["segmentation_model.pt", "warp_model.pt"]
42
+ )
43
+
44
+ #: URL of the GitHub release archive that contains the model weights.
45
+ #: Override with the ``T1PREP_MODEL_ZIP_URL`` environment variable if needed.
46
+ import os as _os
47
+ _DEFAULT_MODEL_ZIP_URL = (
48
+ "https://github.com/ChristianGaser/T1Prep/releases/download/"
49
+ "v0.2.0-beta/T1Prep_Models.zip"
50
+ )
51
+ MODEL_ZIP_URL: str = _os.environ.get("T1PREP_MODEL_ZIP_URL", _DEFAULT_MODEL_ZIP_URL)
52
+
53
+ #: Dev-mode fallback: models bundled inside the source tree (not in the wheel).
54
+ _DEV_MODEL_DIR: Path = Path(__file__).resolve().parent / "data" / "models"
55
+
56
+
57
+ # ---------------------------------------------------------------------------
58
+ # Helpers
59
+ # ---------------------------------------------------------------------------
60
+
61
+
62
+ def all_models_present() -> bool:
63
+ """Return ``True`` if every required model file exists in :data:`MODEL_DIR`."""
64
+ return all((MODEL_DIR / f).exists() for f in MODEL_FILES)
65
+
66
+
67
+ def _download_with_progress(url: str, dest: Path) -> None:
68
+ """Download *url* to *dest*, printing a simple progress indicator."""
69
+
70
+ def _reporthook(count: int, block_size: int, total_size: int) -> None:
71
+ if total_size <= 0:
72
+ print(f"\r Downloaded {count * block_size // 1_048_576} MB ...", end="", flush=True)
73
+ else:
74
+ downloaded = count * block_size
75
+ pct = min(100, int(100 * downloaded / total_size))
76
+ bar = "#" * (pct // 2) + "-" * (50 - pct // 2)
77
+ mb_done = downloaded // 1_048_576
78
+ mb_total = total_size // 1_048_576
79
+ print(f"\r [{bar}] {pct:3d}% ({mb_done}/{mb_total} MB)", end="", flush=True)
80
+
81
+ urllib.request.urlretrieve(url, str(dest), reporthook=_reporthook)
82
+ print() # newline after progress bar
83
+
84
+
85
+ def download_models(verbose: bool = True) -> None:
86
+ """Download model weights from GitHub and extract them to :data:`MODEL_DIR`.
87
+
88
+ Parameters
89
+ ----------
90
+ verbose:
91
+ When ``True`` (default) print progress to stdout.
92
+ """
93
+ MODEL_DIR.mkdir(parents=True, exist_ok=True)
94
+
95
+ if verbose:
96
+ print(f"Downloading model weights from:\n {MODEL_ZIP_URL}")
97
+
98
+ with tempfile.TemporaryDirectory() as tmp_dir:
99
+ zip_path = Path(tmp_dir) / "T1Prep_Models.zip"
100
+ try:
101
+ _download_with_progress(MODEL_ZIP_URL, zip_path)
102
+ except Exception as exc:
103
+ raise RuntimeError(
104
+ f"Failed to download model weights from {MODEL_ZIP_URL}: {exc}\n"
105
+ "Check your internet connection or set the T1PREP_MODEL_ZIP_URL "
106
+ "environment variable to an alternative URL."
107
+ ) from exc
108
+
109
+ if verbose:
110
+ print(f"Extracting to {MODEL_DIR} ...")
111
+
112
+ with zipfile.ZipFile(zip_path, "r") as zf:
113
+ for member in zf.namelist():
114
+ filename = Path(member).name
115
+ if not filename or filename not in MODEL_FILES:
116
+ continue
117
+ dest = MODEL_DIR / filename
118
+ with zf.open(member) as src, open(dest, "wb") as out:
119
+ shutil.copyfileobj(src, out)
120
+
121
+ if verbose:
122
+ print("Model weights downloaded successfully.")
123
+
124
+
125
+ # ---------------------------------------------------------------------------
126
+ # Public API
127
+ # ---------------------------------------------------------------------------
128
+
129
+
130
+ def prepare_model_files(verbose: bool = True) -> None:
131
+ """Ensure all model weight files are present, downloading if necessary.
132
+
133
+ Strategy (in order):
134
+ 1. If all files already exist in :data:`MODEL_DIR` → nothing to do.
135
+ 2. Dev-mode: if the source-tree ``data/models/`` directory is populated
136
+ (e.g. a git clone with model files present), copy missing files from
137
+ there — avoids a network round-trip during development.
138
+ 3. Download the model archive from GitHub and extract to :data:`MODEL_DIR`.
139
+
140
+ Parameters
141
+ ----------
142
+ verbose:
143
+ When ``True`` (default) print progress to stdout.
144
+ """
145
+ if all_models_present():
146
+ return
147
+
148
+ MODEL_DIR.mkdir(parents=True, exist_ok=True)
149
+
150
+ # --- dev-mode: copy from source tree if available ---
151
+ if _DEV_MODEL_DIR.is_dir():
152
+ missing = [f for f in MODEL_FILES if not (MODEL_DIR / f).exists()]
153
+ copied = []
154
+ for filename in missing:
155
+ src = _DEV_MODEL_DIR / filename
156
+ if src.exists():
157
+ shutil.copy2(str(src), str(MODEL_DIR / filename))
158
+ copied.append(filename)
159
+ if copied and verbose:
160
+ print(f"Copied {len(copied)} model file(s) from local data directory.")
161
+ if all_models_present():
162
+ return
163
+
164
+ # --- download from GitHub ---
165
+ if verbose:
166
+ missing_count = sum(1 for f in MODEL_FILES if not (MODEL_DIR / f).exists())
167
+ print(f"{missing_count} model file(s) missing — downloading from GitHub ...")
168
+ download_models(verbose=verbose)
169
+
170
+ if not all_models_present():
171
+ still_missing = [f for f in MODEL_FILES if not (MODEL_DIR / f).exists()]
172
+ raise RuntimeError(
173
+ "Model download completed but the following files are still missing:\n"
174
+ + "\n".join(f" {MODEL_DIR / f}" for f in still_missing)
175
+ )
176
+
177
+
178
+ def main() -> None:
179
+ """Console script entry point for ``t1prep-download-models``.
180
+
181
+ Downloads model weights to :data:`MODEL_DIR` if not already present.
182
+ Pass ``--force`` to re-download even if models are present.
183
+ """
184
+ import argparse
185
+
186
+ parser = argparse.ArgumentParser(
187
+ description="Download T1Prep model weights from GitHub.",
188
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
189
+ )
190
+ parser.add_argument(
191
+ "--force",
192
+ action="store_true",
193
+ help="Re-download model weights even if they are already present.",
194
+ )
195
+ parser.add_argument(
196
+ "--model-dir",
197
+ type=str,
198
+ default=None,
199
+ help="Override the target directory for model weights.",
200
+ )
201
+ args = parser.parse_args()
202
+
203
+ if args.model_dir is not None:
204
+ global MODEL_DIR
205
+ MODEL_DIR = Path(args.model_dir)
206
+
207
+ if args.force and MODEL_DIR.exists():
208
+ print(f"--force: removing existing models in {MODEL_DIR}")
209
+ for f in MODEL_FILES:
210
+ p = MODEL_DIR / f
211
+ if p.exists():
212
+ p.unlink()
213
+
214
+ if all_models_present() and not args.force:
215
+ print(f"All model weights already present in {MODEL_DIR}")
216
+ sys.exit(0)
217
+
218
+ try:
219
+ prepare_model_files(verbose=True)
220
+ except RuntimeError as exc:
221
+ print(f"Error: {exc}", file=sys.stderr)
222
+ sys.exit(1)