MNutils 1.2.3__tar.gz → 1.3.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.
Files changed (43) hide show
  1. {mnutils-1.2.3 → mnutils-1.3.0}/PKG-INFO +6 -12
  2. {mnutils-1.2.3 → mnutils-1.3.0}/README.md +5 -8
  3. {mnutils-1.2.3 → mnutils-1.3.0}/pyproject.toml +4 -5
  4. {mnutils-1.2.3 → mnutils-1.3.0}/pyproject.toml.orig +5 -5
  5. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/GEExam.py +63 -84
  6. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/GESeries.py +177 -189
  7. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/__init__.py +14 -2
  8. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/_scripts.py +34 -16
  9. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/__init__.py +1 -13
  10. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/image_grid.css +21 -5
  11. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/image_grid.js +7 -0
  12. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/image_grid.py +8 -0
  13. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/images.py +228 -96
  14. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/saving.py +23 -45
  15. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/spectra.py +18 -17
  16. mnutils-1.3.0/src/mnutils/rcparams.py +449 -0
  17. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/testing/synthetic.py +176 -34
  18. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/data_loaders.py +163 -5
  19. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/file_helpers.py +305 -175
  20. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/images.py +9 -9
  21. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/nifti.py +277 -287
  22. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/partial_volume.py +105 -20
  23. mnutils-1.2.3/src/mnutils/fitting/AMARES.py +0 -831
  24. mnutils-1.2.3/src/mnutils/fitting/__init__.py +0 -5
  25. {mnutils-1.2.3 → mnutils-1.3.0}/LICENSE +0 -0
  26. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/matlab/__init__.py +0 -0
  27. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/matlab/setup_matlab.py +0 -0
  28. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/__init__.py +0 -0
  29. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/_html.py +0 -0
  30. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/_shared/__init__.py +0 -0
  31. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/_shared/dom.js +0 -0
  32. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/_shared/theme.css +0 -0
  33. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/mrsi_inspector.css +0 -0
  34. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/mrsi_inspector.js +0 -0
  35. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/mrsi_inspector.py +0 -0
  36. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/slice_viewer.css +0 -0
  37. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/slice_viewer.js +0 -0
  38. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/plotting/_widgets/slice_viewer.py +0 -0
  39. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/py.typed +0 -0
  40. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/testing/__init__.py +0 -0
  41. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/testing/_spectra.py +0 -0
  42. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/__init__.py +0 -0
  43. {mnutils-1.2.3 → mnutils-1.3.0}/src/mnutils/utils/spectra.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: MNutils
3
- Version: 1.2.3
3
+ Version: 1.3.0
4
4
  Summary: Utility functions for multinuclear MR data processing
5
5
  Keywords: mri,mrs,xarray,spectroscopy,nmr
6
6
  Author: Sebastian Bauer
@@ -14,7 +14,6 @@ Classifier: Intended Audience :: Science/Research
14
14
  Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Framework :: Jupyter
17
- Requires-Dist: dcm2niiw>=0.1.5
18
17
  Requires-Dist: ipython>=8.0
19
18
  Requires-Dist: loguru>=0.7.3
20
19
  Requires-Dist: mat73
@@ -26,7 +25,6 @@ Requires-Dist: scipy>=1.15.3
26
25
  Requires-Dist: seaborn>=0.13.2
27
26
  Requires-Dist: xarray>=2025.6.1
28
27
  Requires-Dist: xmris[fitting]
29
- Requires-Dist: matlabengine==25.1.* ; extra == '2025a'
30
28
  Requires-Dist: hd-bet>=2.0.1 ; extra == 'bet'
31
29
  Requires-Dist: torch>=2.10.0 ; extra == 'bet'
32
30
  Requires-Python: >=3.12
@@ -34,7 +32,6 @@ Project-URL: Homepage, https://github.com/bastigw/mnutils
34
32
  Project-URL: Documentation, https://bastigw.github.io/mnutils/
35
33
  Project-URL: Repository, https://github.com/bastigw/mnutils
36
34
  Project-URL: Issues, https://github.com/bastigw/mnutils/issues
37
- Provides-Extra: 2025a
38
35
  Provides-Extra: bet
39
36
  Description-Content-Type: text/markdown
40
37
 
@@ -58,7 +55,7 @@ scanners.
58
55
  ## Features
59
56
 
60
57
  - **Data loading** — read GE raw/reconstructed data via `GEExam`/`GESeries`.
61
- - **NIfTI/DICOM conversion** — convert scanner output to NIfTI.
58
+ - **NIfTI handling** — read dcm2niix-converted scanner output (NIfTI + JSON sidecar); DICOMs are not read.
62
59
  - **Plotting** — spectral and slice plotting utilities.
63
60
  - **Spectral fitting** — AMARES fitting delegated to [`xmris`](https://github.com/andrewendlinger/xmris)
64
61
  (`xmris[fitting]`, which wraps pyAMARES).
@@ -78,7 +75,6 @@ Optional extras:
78
75
 
79
76
  ```bash
80
77
  uv add "mnutils[bet]" # brain extraction (hd-bet, torch)
81
- uv add "mnutils[2025a]" # MATLAB Engine pinned to MATLAB 2025a
82
78
  ```
83
79
 
84
80
  To track the development version instead:
@@ -87,10 +83,8 @@ To track the development version instead:
87
83
  uv add git+https://github.com/bastigw/mnutils.git
88
84
  ```
89
85
 
90
- `2025a` pins `matlabengine==25.1.*`. If your local MATLAB install is a different version, install
91
- the matching `matlabengine` release yourself instead (see [MATLAB Engine
92
- Dependency](#matlab-engine-dependency) below) — there's no way to pin one version project-wide
93
- across contributors' machines.
86
+ The MATLAB Engine is not an extra: its version has to match your local MATLAB install, so you
87
+ install it yourself (see [MATLAB Engine Dependency](#matlab-engine-dependency) below).
94
88
 
95
89
  ## Usage
96
90
 
@@ -123,8 +117,8 @@ already know a series's type, construct it directly (e.g. `MRSSeries(DATA_FOLDER
123
117
  the exam-level overview entirely.
124
118
 
125
119
  From there, spectral fitting is handled by [**xmris**](https://github.com/andrewendlinger/xmris)
126
- — MNUtils deliberately doesn't reimplement fitting, it wraps xmris's AMARES implementation
127
- (itself built on pyAMARES) so a series's spectrum can go straight into a fit:
120
+ — MNUtils deliberately doesn't reimplement fitting, a series hands its FIDs straight to xmris's
121
+ AMARES implementation (itself built on pyAMARES) and returns the xmris result:
128
122
 
129
123
  ```bash
130
124
  uv add "xmris[fitting]"
@@ -18,7 +18,7 @@ scanners.
18
18
  ## Features
19
19
 
20
20
  - **Data loading** — read GE raw/reconstructed data via `GEExam`/`GESeries`.
21
- - **NIfTI/DICOM conversion** — convert scanner output to NIfTI.
21
+ - **NIfTI handling** — read dcm2niix-converted scanner output (NIfTI + JSON sidecar); DICOMs are not read.
22
22
  - **Plotting** — spectral and slice plotting utilities.
23
23
  - **Spectral fitting** — AMARES fitting delegated to [`xmris`](https://github.com/andrewendlinger/xmris)
24
24
  (`xmris[fitting]`, which wraps pyAMARES).
@@ -38,7 +38,6 @@ Optional extras:
38
38
 
39
39
  ```bash
40
40
  uv add "mnutils[bet]" # brain extraction (hd-bet, torch)
41
- uv add "mnutils[2025a]" # MATLAB Engine pinned to MATLAB 2025a
42
41
  ```
43
42
 
44
43
  To track the development version instead:
@@ -47,10 +46,8 @@ To track the development version instead:
47
46
  uv add git+https://github.com/bastigw/mnutils.git
48
47
  ```
49
48
 
50
- `2025a` pins `matlabengine==25.1.*`. If your local MATLAB install is a different version, install
51
- the matching `matlabengine` release yourself instead (see [MATLAB Engine
52
- Dependency](#matlab-engine-dependency) below) — there's no way to pin one version project-wide
53
- across contributors' machines.
49
+ The MATLAB Engine is not an extra: its version has to match your local MATLAB install, so you
50
+ install it yourself (see [MATLAB Engine Dependency](#matlab-engine-dependency) below).
54
51
 
55
52
  ## Usage
56
53
 
@@ -83,8 +80,8 @@ already know a series's type, construct it directly (e.g. `MRSSeries(DATA_FOLDER
83
80
  the exam-level overview entirely.
84
81
 
85
82
  From there, spectral fitting is handled by [**xmris**](https://github.com/andrewendlinger/xmris)
86
- — MNUtils deliberately doesn't reimplement fitting, it wraps xmris's AMARES implementation
87
- (itself built on pyAMARES) so a series's spectrum can go straight into a fit:
83
+ — MNUtils deliberately doesn't reimplement fitting, a series hands its FIDs straight to xmris's
84
+ AMARES implementation (itself built on pyAMARES) and returns the xmris result:
88
85
 
89
86
  ```bash
90
87
  uv add "xmris[fitting]"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "MNutils"
3
- version = "1.2.3"
3
+ version = "1.3.0"
4
4
  description = "Utility functions for multinuclear MR data processing"
5
5
  readme = "README.md"
6
6
  license = "BSD-3-Clause"
@@ -23,7 +23,6 @@ classifiers = [
23
23
  ]
24
24
  requires-python = ">=3.12"
25
25
  dependencies = [
26
- "dcm2niiw>=0.1.5",
27
26
  "ipython>=8.0",
28
27
  "loguru>=0.7.3",
29
28
  "mat73",
@@ -48,7 +47,6 @@ Repository = "https://github.com/bastigw/mnutils"
48
47
  Issues = "https://github.com/bastigw/mnutils/issues"
49
48
 
50
49
  [project.optional-dependencies]
51
- 2025a = ["matlabengine==25.1.*"]
52
50
  bet = [
53
51
  "hd-bet>=2.0.1",
54
52
  "torch>=2.10.0",
@@ -62,7 +60,7 @@ test-mnutils-gen = "mnutils._scripts:generate_test_notebooks"
62
60
  test-mnutils = "mnutils._scripts:run_tests"
63
61
 
64
62
  [build-system]
65
- requires = ["uv_build>=0.8.13,<0.13.0"]
63
+ requires = ["uv_build>=0.8.13,<0.14.0"]
66
64
  build-backend = "uv_build"
67
65
 
68
66
  [dependency-groups]
@@ -78,10 +76,11 @@ dev = [
78
76
  "griffe<0.40.0",
79
77
  "jupyter-server>=2.17.0",
80
78
  "jupytext>=1.19.1",
79
+ "pytest-xdist>=3.8.0",
81
80
  ]
82
81
 
83
82
  [tool.pytest.ini_options]
84
- addopts = "--capture=no --nbmake"
83
+ addopts = "--nbmake -n auto -v --durations=0"
85
84
  testpaths = ["tests"]
86
85
 
87
86
  [tool.uv.sources.mat73]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "MNutils"
3
- version = "1.2.3"
3
+ version = "1.3.0"
4
4
 
5
5
  authors = [
6
6
  {name = "Sebastian Bauer", email = "28663646+bastigw@users.noreply.github.com"}
@@ -25,7 +25,6 @@ classifiers = [
25
25
  requires-python = ">=3.12"
26
26
 
27
27
  dependencies = [
28
- "dcm2niiw>=0.1.5",
29
28
  "ipython>=8.0",
30
29
  "loguru>=0.7.3",
31
30
  "mat73",
@@ -46,7 +45,6 @@ Repository = "https://github.com/bastigw/mnutils"
46
45
  Issues = "https://github.com/bastigw/mnutils/issues"
47
46
 
48
47
  [project.optional-dependencies]
49
- 2025a = ["matlabengine==25.1.*"]
50
48
  bet = ["hd-bet>=2.0.1", "torch>=2.10.0"]
51
49
 
52
50
  [project.scripts]
@@ -57,7 +55,7 @@ test-mnutils-gen = "mnutils._scripts:generate_test_notebooks" # docs/*.md -> te
57
55
  test-mnutils = "mnutils._scripts:run_tests" # test-gen, then run the full pytest suite
58
56
 
59
57
  [build-system]
60
- requires = ["uv_build>=0.8.13,<0.13.0"]
58
+ requires = ["uv_build>=0.8.13,<0.14.0"]
61
59
  build-backend = "uv_build"
62
60
 
63
61
  [dependency-groups]
@@ -77,10 +75,12 @@ dev = [
77
75
  "griffe<0.40.0", # Explicit pin to fix quartodoc parsing bug
78
76
  "jupyter-server>=2.17.0", # Required for rendering docs
79
77
  "jupytext>=1.19.1",
78
+ "pytest-xdist>=3.8.0",
80
79
  ]
81
80
 
82
81
  [tool.pytest.ini_options]
83
- addopts = "--capture=no --nbmake"
82
+ # See conftest.py for further details
83
+ addopts = "--nbmake -n auto -v --durations=0"
84
84
  testpaths = ["tests"]
85
85
 
86
86
  # [tool.uv]
@@ -16,6 +16,11 @@ from .utils import file_helpers
16
16
  ANATOMICAL_SERIES_TO_SKIP = ["FGRE", "localizer", "GE_HOS", "localiser"]
17
17
 
18
18
 
19
+ def _normalise_description(description: object) -> str:
20
+ """Lower-case, spaces as underscores: ``MRS washin`` -> ``mrs_washin``."""
21
+ return description.strip().lower().replace(" ", "_") if isinstance(description, str) else ""
22
+
23
+
19
24
  class ExamBase:
20
25
  """Base class for a GE exam, loading series metadata and providing series access."""
21
26
 
@@ -24,6 +29,7 @@ class ExamBase:
24
29
  BASE_FOLDER: str | Path,
25
30
  DATA_FOLDER: str | Path | None = None,
26
31
  OUTPUT_FOLDER: str | Path | None = None,
32
+ show_overview: bool = False,
27
33
  ) -> None:
28
34
  self.BASE_FOLDER = Path(BASE_FOLDER)
29
35
  if DATA_FOLDER is None:
@@ -37,7 +43,9 @@ class ExamBase:
37
43
  else:
38
44
  self.OUTPUT_FOLDER = Path(OUTPUT_FOLDER)
39
45
 
40
- self.exam_overview = file_helpers.get_exam_overview(self.DATA_FOLDER, print_overview=True)
46
+ self.exam_overview = file_helpers.get_exam_overview(
47
+ self.DATA_FOLDER, print_overview=show_overview
48
+ )
41
49
  self.series = self.exam_overview.index.tolist()
42
50
  self.series_dict = self._create_series_dict()
43
51
 
@@ -69,60 +77,45 @@ class ExamBase:
69
77
  """Iterate over loaded series as (series ID, series object) pairs."""
70
78
  yield from self.all.items()
71
79
 
80
+ def _ids_with_description(self, fragment: str) -> npt.NDArray[np.int_]:
81
+ """Series IDs whose normalised description contains `fragment` (also normalised)."""
82
+ normalised = self.exam_overview["Description"].map(_normalise_description)
83
+ mask = normalised.str.contains(_normalise_description(fragment), regex=False)
84
+ return self.exam_overview[mask].index.to_numpy().astype(int)
85
+
72
86
  def _create_series_dict(self) -> dict[int, type[GESeries.MRISeries]]:
87
+ """Pick a series class per series from its description, never from a folder name."""
73
88
  series_class_dict = {}
74
89
  for series_num in self.series:
75
- folder_name = self.exam_overview.at[series_num, "Folder Name"]
90
+ raw_description = self.exam_overview.at[series_num, "Description"]
91
+ description = _normalise_description(raw_description)
76
92
  has_exam = self.exam_overview.at[series_num, "Has Exam"]
77
- has_dicom = self.exam_overview.at[series_num, "Has DICOM"]
78
-
79
- if has_exam and not has_dicom:
80
- logger.debug(
81
- f"Series {series_num} does not have DICOM. Assuming it is an MRS series."
82
- )
83
- self.exam_overview.at[series_num, "Folder Name"] = "ASSUMING MRS"
84
- series_class_dict[series_num] = GESeries.MRSSeries
85
- continue
86
-
87
- # Make sure folder name is a string
88
- if isinstance(folder_name, str) and has_dicom:
89
- folder_name = folder_name.strip().lower()
90
- else:
91
- logger.warning(
92
- f"Folder name for series {series_num} is not a string: {folder_name}. Skipping."
93
- )
94
- continue
95
-
96
- if not has_exam and not has_dicom:
97
- logger.debug(f"Series {series_num} does not have exam or DICOM data. Skipping")
98
- continue
99
93
 
100
- if has_dicom and not has_exam:
101
- logger.trace(f"Series {series_num} does not have raw data.")
102
- # Based on the folder name determine if it is anatomical data we want to load
103
- if any(skip_name.lower() in folder_name for skip_name in ANATOMICAL_SERIES_TO_SKIP):
94
+ if not has_exam:
95
+ if any(skip_name.lower() in description for skip_name in ANATOMICAL_SERIES_TO_SKIP):
104
96
  logger.debug(f"Series {series_num} is anatomical data to skip.")
105
97
  continue
106
98
  series_class_dict[series_num] = GESeries.MRISeries
107
99
  continue
108
100
 
109
- if has_exam and has_dicom:
110
- logger.trace(
111
- f"Series {series_num} has both exam and DICOM data. Checking folder name."
101
+ if not description:
102
+ logger.debug(
103
+ f"Series {series_num} has no series description. Assuming it is an MRS series."
104
+ )
105
+ series_class_dict[series_num] = GESeries.MRSSeries
106
+ elif "mrs_washin" in description:
107
+ series_class_dict[series_num] = GESeries.MRSWashinSeries
108
+ elif "mrsi" in description:
109
+ series_class_dict[series_num] = GESeries.MRSISeries
110
+ elif "mrs_" in description:
111
+ series_class_dict[series_num] = GESeries.MRSSeries
112
+ elif "bs_prescan" in description:
113
+ continue # Skip the bs_prescan series
114
+ else:
115
+ logger.warning(
116
+ f"Series {series_num} with description '{raw_description}' does not "
117
+ f"match any known series types. Skipping."
112
118
  )
113
- if "mrs_washin" in folder_name:
114
- series_class_dict[series_num] = GESeries.MRSWashinSeries
115
- elif "mrsi_pseudo" in folder_name:
116
- series_class_dict[series_num] = GESeries.MRSISeries
117
- elif "mrs_" in folder_name:
118
- series_class_dict[series_num] = GESeries.MRSSeries
119
- elif "bs_prescan" in folder_name:
120
- continue # Skip the bs_prescan series
121
- else:
122
- logger.warning(
123
- f"Series {series_num} with folder name '{folder_name}' does not "
124
- f"match any known series types. Skipping."
125
- )
126
119
 
127
120
  return series_class_dict
128
121
 
@@ -180,8 +173,9 @@ class DMIExam(ExamBase):
180
173
  DATA_FOLDER: str | Path | None = None,
181
174
  OUTPUT_FOLDER: str | Path | None = None,
182
175
  ANATOMICAL_series: str | int | None = None,
176
+ show_overview: bool = False,
183
177
  ) -> None:
184
- super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER)
178
+ super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER, show_overview)
185
179
 
186
180
  if isinstance(ANATOMICAL_series, int):
187
181
  self.ANATOMICAL_series = ANATOMICAL_series
@@ -190,33 +184,22 @@ class DMIExam(ExamBase):
190
184
  identifier = ANATOMICAL_series.strip().lower()
191
185
  else:
192
186
  identifier = "t1_bravo"
193
- self.ANATOMICAL_series = (
194
- self.exam_overview[
195
- self.exam_overview["Folder Name"].str.contains(identifier, na=False, case=False)
196
- ]
197
- .index.to_numpy()
198
- .astype(int)[0]
199
- )
187
+ self.ANATOMICAL_series = self._ids_with_description(identifier)[0]
200
188
 
201
- self.MRS_series = (
202
- self.exam_overview[
203
- self.exam_overview["Folder Name"].str.contains("MRS_unloc", na=False)
204
- ]
205
- .index.to_numpy()
206
- .astype(int)
189
+ self.MRS_series = self._ids_with_description("MRS_unloc")
190
+ # By default append all series without a NIfTI (raw data only)
191
+ no_nifti_series = (
192
+ self.exam_overview[~self.exam_overview["Has NIfTI"]].index.to_numpy().astype(int)
207
193
  )
208
- # By default append all series where Has Dicom in the exam overview table is False
209
- no_DICOM_series = (
210
- self.exam_overview[~self.exam_overview["Has DICOM"]].index.to_numpy().astype(int)
211
- )
212
- self.MRS_series = np.union1d(self.MRS_series, no_DICOM_series)
213
-
214
- self.MRSI_series = (
215
- self.exam_overview[
216
- self.exam_overview["Folder Name"].str.contains("MRSI_pseudo", na=False)
217
- ]
218
- .index.to_numpy()
219
- .astype(int)
194
+ self.MRS_series = np.union1d(self.MRS_series, no_nifti_series)
195
+
196
+ self.MRSI_series = np.array(
197
+ [
198
+ series_id
199
+ for series_id, series_class in self.series_dict.items()
200
+ if issubclass(series_class, GESeries.MRSISeries)
201
+ ],
202
+ dtype=int,
220
203
  )
221
204
 
222
205
  self.anatomical: GESeries.MRISeries
@@ -253,9 +236,9 @@ class DMIExam(ExamBase):
253
236
  logger.warning(
254
237
  f"Multiple anatomical datasets detected. "
255
238
  f"Previous: Series ID {self.anatomical.SERIES_ID}, "
256
- f"Name '{self.exam_overview.at[self.anatomical.SERIES_ID, 'Folder Name']}'."
239
+ f"Name '{self.exam_overview.at[self.anatomical.SERIES_ID, 'Description']}'."
257
240
  f" New: Series ID {series_obj.SERIES_ID}, "
258
- f"Name '{self.exam_overview.at[series_obj.SERIES_ID, 'Folder Name']}'."
241
+ f"Name '{self.exam_overview.at[series_obj.SERIES_ID, 'Description']}'."
259
242
  )
260
243
  self.anatomical = series_obj
261
244
 
@@ -269,9 +252,10 @@ class MS_DMIExam(DMIExam):
269
252
  DATA_FOLDER: str | Path | None = None,
270
253
  OUTPUT_FOLDER: str | Path | None = None,
271
254
  ANATOMICAL_series: str | int | None = None,
255
+ show_overview: bool = False,
272
256
  ) -> None:
273
- super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER, ANATOMICAL_series)
274
- logger.info("MS DMI Exam initialized.")
257
+ super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER, ANATOMICAL_series, show_overview)
258
+ logger.debug("MS DMI Exam initialized.")
275
259
 
276
260
  self.MRSI: GESeries.MRSISeries
277
261
  self.pre_MRS: GESeries.MRSSeries
@@ -330,16 +314,11 @@ class DMIinjExam(DMIExam):
330
314
  DATA_FOLDER: str | Path | None = None,
331
315
  OUTPUT_FOLDER: str | Path | None = None,
332
316
  ANATOMICAL_series: str | int | None = None,
317
+ show_overview: bool = False,
333
318
  ) -> None:
334
- super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER, ANATOMICAL_series)
335
-
336
- self.WASHIN_series = (
337
- self.exam_overview[
338
- self.exam_overview["Folder Name"].str.contains("MRS_washin", na=False)
339
- ]
340
- .index.to_numpy()
341
- .astype(int)[0]
342
- )
319
+ super().__init__(BASE_FOLDER, DATA_FOLDER, OUTPUT_FOLDER, ANATOMICAL_series, show_overview)
320
+
321
+ self.WASHIN_series = self._ids_with_description("MRS_washin")[0]
343
322
 
344
323
  # Define pre and post series based on wash-in series
345
324
  self.pre_MRS_series, self.post_MRS_series = self._split_series(
@@ -349,7 +328,7 @@ class DMIinjExam(DMIExam):
349
328
  self.MRSI_series, self.WASHIN_series, "MRSI"
350
329
  )
351
330
 
352
- logger.info("DMI Intravenous Injection Exam initialized.")
331
+ logger.debug("DMI Intravenous Injection Exam initialized.")
353
332
 
354
333
  self.washin: GESeries.MRSWashinSeries
355
334
  self.pre_MRS: GESeries.MRSSeries