cmpl 0.2.2__tar.gz → 0.2.4__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 (47) hide show
  1. {cmpl-0.2.2/src/cmpl.egg-info → cmpl-0.2.4}/PKG-INFO +454 -156
  2. cmpl-0.2.4/README.md +794 -0
  3. {cmpl-0.2.2 → cmpl-0.2.4}/pyproject.toml +4 -1
  4. cmpl-0.2.4/src/cmpl/cli/__init__.py +3 -0
  5. cmpl-0.2.4/src/cmpl/cli/dicom_to_nifti.py +113 -0
  6. cmpl-0.2.4/src/cmpl/cli/t2star.py +469 -0
  7. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/dicom/enhanced_dicom.py +205 -0
  8. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/quantitative_MRI/mapping.py +23 -6
  9. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/utilities/io.py +745 -23
  10. {cmpl-0.2.2 → cmpl-0.2.4/src/cmpl.egg-info}/PKG-INFO +454 -156
  11. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl.egg-info/SOURCES.txt +5 -0
  12. cmpl-0.2.4/src/cmpl.egg-info/entry_points.txt +3 -0
  13. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_qmr.py +6 -1
  14. cmpl-0.2.4/tests/test_t2star_cli.py +159 -0
  15. cmpl-0.2.2/README.md +0 -496
  16. {cmpl-0.2.2 → cmpl-0.2.4}/LICENSE +0 -0
  17. {cmpl-0.2.2 → cmpl-0.2.4}/setup.cfg +0 -0
  18. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/__init__.py +0 -0
  19. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/_version.py +0 -0
  20. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/dicom/__init__.py +0 -0
  21. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/dicom/geometry.py +0 -0
  22. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/dicom/metadata.py +0 -0
  23. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/quantitative_MRI/__init__.py +0 -0
  24. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/__init__.py +0 -0
  25. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/grappa/__init__.py +0 -0
  26. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/grappa/grappa_1D.py +0 -0
  27. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/grappa/grappa_2D.py +0 -0
  28. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/grappa/utils.py +0 -0
  29. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/sense/__init__.py +0 -0
  30. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/reconstruction/sense/cg.py +0 -0
  31. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/segmentation/MRISegmentationTool.py +0 -0
  32. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/segmentation/__init__.py +0 -0
  33. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/segmentation/tools.py +0 -0
  34. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/utilities/__init__.py +0 -0
  35. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/utilities/df_build.py +0 -0
  36. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/utilities/numerical.py +0 -0
  37. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/utilities/utils.py +0 -0
  38. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/visualization/__init__.py +0 -0
  39. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl/visualization/visualization.py +0 -0
  40. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl.egg-info/dependency_links.txt +0 -0
  41. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl.egg-info/requires.txt +0 -0
  42. {cmpl-0.2.2 → cmpl-0.2.4}/src/cmpl.egg-info/top_level.txt +0 -0
  43. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_data.py +0 -0
  44. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_installation.py +0 -0
  45. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_io.py +0 -0
  46. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_reconstruction.py +0 -0
  47. {cmpl-0.2.2 → cmpl-0.2.4}/tests/test_visualization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmpl
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: CMRR MRI Processing Libraries
5
5
  Author: Eisa Hedayati
6
6
  License: Copyright (c) 2025 Eisa Hedayati
@@ -71,23 +71,32 @@ Dynamic: license-file
71
71
 
72
72
  # CMPL — CMRR MRI Processing Libraries
73
73
 
74
- [![PyPI](https://img.shields.io/pypi/v/cmpl.svg)](https://pypi.org/project/cmpl/)
75
- [![Python](https://img.shields.io/pypi/pyversions/cmpl.svg)](https://pypi.org/project/cmpl/)
74
+ [![PyPI version](https://img.shields.io/pypi/v/cmpl.svg?cacheSeconds=300)](https://pypi.org/project/cmpl/)
75
+ [![Python versions](https://img.shields.io/pypi/pyversions/cmpl.svg)](https://pypi.org/project/cmpl/)
76
76
 
77
- CMPL is a Python package for MRI processing workflows developed at CMRR. It provides tools for MRI reconstruction, quantitative MRI, visualization, DICOM/NIfTI I/O, and supporting numerical/data utilities.
77
+ **PyPI:** https://pypi.org/project/cmpl/
78
+ **GitHub:** https://github.com/ehedayati/cmpl
78
79
 
79
- CMPL is organized as a modular library: the base installation stays lightweight, while larger or domain-specific dependencies are installed only when the corresponding functionality is needed.
80
+ CMPL is a Python package for MRI processing workflows developed at CMRR. It provides tools for MRI reconstruction, quantitative MRI, visualization, DICOM/NIfTI conversion and I/O, and supporting numerical and data utilities.
81
+
82
+ The package is modular by design: the base installation remains lightweight, while larger or domain-specific dependencies are installed only when the corresponding functionality is needed.
80
83
 
81
84
  ## Highlights
82
85
 
86
+ - Geometry-aware conventional and Enhanced DICOM to NIfTI conversion
87
+ - JSON metadata sidecars with acquisition and source-geometry information
88
+ - Multi-echo DICOM support with 4D NIfTI output ordered by echo time
89
+ - Packaged `cmpl-dicom-to-nifti` command-line converter
90
+ - Packaged `cmpl-t2star` command-line T2* and S0 mapper
91
+ - DICOM geometry and acquisition-metadata utilities
83
92
  - Parallel MRI reconstruction with 1D/2D GRAPPA and conjugate-gradient SENSE
84
93
  - Quantitative MRI tools for T2* fitting, signal reconstruction, and fitting-error analysis
85
- - MRI visualization utilities for 2D comparisons, 3D volume browsing, and segmentation overlays
86
- - DICOM, enhanced-DICOM, NIfTI, and SimpleITK utilities
94
+ - MRI visualization utilities for 2D comparisons and 3D volume browsing
95
+ - Conventional and Enhanced DICOM, NIfTI, HDF5, and SimpleITK utilities
87
96
  - Lightweight numerical utilities shared across CMPL
88
97
  - Optional pandas-based indexing for CMPL-style medical-data directory structures
89
- - Lazy package imports so `import cmpl` does not eagerly load large optional dependencies
90
- - Backward-compatible convenience aliases such as `cmpl.recon`, `cmpl.qmr`, `cmpl.vis`, and `cmpl.utils`
98
+ - Lazy imports so unrelated optional dependencies are not loaded unnecessarily
99
+ - Convenient aliases such as `cmpl.recon`, `cmpl.qmr`, `cmpl.vis`, and `cmpl.io`
91
100
 
92
101
  ## Requirements
93
102
 
@@ -98,7 +107,7 @@ CMPL requires:
98
107
  - SciPy >= 1.13, < 2
99
108
  - tqdm >= 4.66
100
109
 
101
- Additional dependencies are installed through optional extras.
110
+ Additional functionality is provided through optional dependency groups.
102
111
 
103
112
  ## Installation
104
113
 
@@ -116,23 +125,29 @@ Install only the functionality you need:
116
125
  | `data` | pandas-based data indexing |
117
126
  | `viz` | Matplotlib and Jupyter visualization |
118
127
  | `torch` | PyTorch-based reconstruction and quantitative MRI |
119
- | `all` | All optional functionality declared by CMPL |
128
+ | `all` | All optional CMPL functionality |
120
129
  | `dev` | Testing, linting, build, and release tools |
121
130
 
122
131
  Examples:
123
132
 
124
133
  ```bash
125
134
  python -m pip install "cmpl[io]"
126
- python -m pip install "cmpl[viz]"
127
135
  python -m pip install "cmpl[torch]"
128
- python -m pip install "cmpl[io,data,viz,torch]"
136
+ python -m pip install "cmpl[viz]"
137
+ python -m pip install "cmpl[io,torch]"
129
138
  python -m pip install "cmpl[all]"
130
139
  ```
131
140
 
132
- Quantitative-MRI fitting currently uses both PyTorch and Matplotlib, so for qMRI workflows install:
141
+ For NIfTI-based T2* mapping:
133
142
 
134
143
  ```bash
135
- python -m pip install "cmpl[torch,viz]"
144
+ python -m pip install "cmpl[io,torch]"
145
+ ```
146
+
147
+ Matplotlib is only required when plotting is explicitly requested:
148
+
149
+ ```bash
150
+ python -m pip install "cmpl[viz]"
136
151
  ```
137
152
 
138
153
  ## Quick start
@@ -149,109 +164,262 @@ CMPL exposes convenient aliases for commonly used subpackages:
149
164
  cmpl.recon # reconstruction
150
165
  cmpl.qmr # quantitative MRI
151
166
  cmpl.vis # visualization
152
- cmpl.utils # utilities
153
167
  cmpl.io # I/O utilities
168
+ cmpl.dicom # DICOM metadata and geometry utilities
169
+ cmpl.utils # utilities
154
170
  ```
155
171
 
156
- The aliases are resolved lazily, so importing CMPL itself does not require every optional dependency to be loaded.
172
+ These aliases are resolved lazily so `import cmpl` does not require every optional dependency to be installed or imported.
157
173
 
158
- ## Visualization
174
+ ---
159
175
 
160
- Install the visualization extra:
176
+ ## DICOM and NIfTI I/O
177
+
178
+ Install the I/O extra:
161
179
 
162
180
  ```bash
163
- python -m pip install "cmpl[viz]"
181
+ python -m pip install "cmpl[io]"
164
182
  ```
165
183
 
166
- ### Browse or display a 3D MRI volume
184
+ ### Convert a DICOM series to NIfTI
185
+
186
+ CMPL supports direct conversion of both conventional and Enhanced DICOM series to NIfTI.
187
+
188
+ The converter:
189
+
190
+ - detects the DICOM representation automatically
191
+ - preserves spatial geometry
192
+ - supports single-echo and multi-echo acquisitions
193
+ - writes a NIfTI image
194
+ - writes a matching JSON metadata sidecar
195
+ - orders multi-echo volumes by echo time
167
196
 
168
197
  ```python
169
- from cmpl.visualization import plot_3D_mri
198
+ import cmpl
170
199
 
171
- plot_3D_mri(
172
- volume,
173
- slice_number=volume.shape[2] // 2,
174
- alpha=0.5,
175
- direction="sagittal",
176
- cmap="gray",
177
- vmin=0,
178
- vmax=1,
179
- dpi=300,
200
+ metadata = cmpl.io.dicom_to_nifti(
201
+ "/path/to/dicom_series",
202
+ "output.nii.gz",
180
203
  )
181
204
  ```
182
205
 
183
- If an interactive Matplotlib backend is available, `plot_3D_mri` can use interactive controls. Otherwise it falls back to static redraw mode.
206
+ This creates:
184
207
 
208
+ ```text
209
+ output.nii.gz
210
+ output.json
211
+ ```
185
212
 
186
- ### Compare images side by side
213
+ If the output path does not end in `.nii` or `.nii.gz`, CMPL appends `.nii.gz`.
214
+
215
+ For a single echo, the output is a 3D image. For a multi-echo acquisition, CMPL writes a 4D NIfTI with echoes in the last dimension:
216
+
217
+ ```text
218
+ x, y, z, echo
219
+ ```
220
+
221
+ The matching JSON sidecar contains acquisition metadata and source-geometry information. For multi-echo data, echo times are stored in milliseconds under:
222
+
223
+ ```json
224
+ {
225
+ "Acquisition": {
226
+ "EchoTimes": [2.5, 5.0, 7.5, 10.0],
227
+ "TimeUnit": "ms"
228
+ }
229
+ }
230
+ ```
231
+
232
+ If a directory contains multiple DICOM series, select a specific `SeriesInstanceUID`:
187
233
 
188
234
  ```python
189
- from cmpl.visualization import side_by_side_view
235
+ metadata = cmpl.io.dicom_to_nifti(
236
+ "/path/to/dicom_directory",
237
+ "output.nii.gz",
238
+ series_id="1.2.840...",
239
+ )
240
+ ```
190
241
 
191
- side_by_side_view(
192
- image_a,
193
- image_b,
194
- titles=["Reference", "Reconstruction"],
195
- color_palette="gray",
242
+ ### Command-line DICOM conversion
243
+
244
+ The I/O extra installs:
245
+
246
+ ```text
247
+ cmpl-dicom-to-nifti
248
+ ```
249
+
250
+ Convert a DICOM series with automatic conventional/Enhanced-DICOM detection:
251
+
252
+ ```bash
253
+ cmpl-dicom-to-nifti /path/to/dicom_series
254
+ ```
255
+
256
+ The same CLI can be invoked as a Python module:
257
+
258
+ ```bash
259
+ python -m cmpl.cli.dicom_to_nifti /path/to/dicom_series
260
+ ```
261
+
262
+ If the output path is omitted, CMPL writes the NIfTI and JSON sidecar to the current directory using the DICOM directory name:
263
+
264
+ ```text
265
+ ./<series_directory_name>.nii.gz
266
+ ./<series_directory_name>.json
267
+ ```
268
+
269
+ Specify an explicit output path if needed:
270
+
271
+ ```bash
272
+ cmpl-dicom-to-nifti \
273
+ /path/to/dicom_series \
274
+ /path/to/output.nii.gz
275
+ ```
276
+
277
+ Progress output is enabled by default. Disable it with:
278
+
279
+ ```bash
280
+ cmpl-dicom-to-nifti /path/to/dicom_series --no-verbose
281
+ ```
282
+
283
+ The same command handles conventional single-frame DICOM and Enhanced multi-frame DICOM.
284
+
285
+ ### Read a NIfTI file
286
+
287
+ ```python
288
+ from cmpl.utilities.io import nifti_read
289
+
290
+ nifti_image, data = nifti_read("image.nii.gz")
291
+ ```
292
+
293
+ ### Replace NIfTI data while preserving geometry
294
+
295
+ ```python
296
+ from cmpl.utilities.io import update_nifti_data
297
+
298
+ updated = update_nifti_data(
299
+ "reference.nii.gz",
300
+ new_data,
301
+ output_path="updated.nii.gz",
196
302
  )
197
303
  ```
198
304
 
199
- ### Overlay a segmentation
305
+ ### Save a scalar map using reference NIfTI geometry
200
306
 
201
307
  ```python
202
- from cmpl.visualization import visualize_segmentation_slice
308
+ from cmpl.utilities.io import save_scalar_map_like
203
309
 
204
- visualize_segmentation_slice(
205
- grayscale_image,
206
- segmentation,
207
- slice_number=20,
208
- dimension="axial",
310
+ save_scalar_map_like(
311
+ reference_image,
312
+ scalar_map,
313
+ "map.nii.gz",
209
314
  )
210
315
  ```
211
316
 
212
- ## Quantitative MRI
317
+ This is useful for quantitative maps such as T2* and S0 because the spatial geometry of the source NIfTI is preserved.
213
318
 
214
- Quantitative MRI functionality is available under:
319
+ ### Load a DICOM directory as a NumPy array
215
320
 
216
321
  ```python
217
- cmpl.qmr
322
+ from cmpl.utilities.io import load_dicom_scan_from_dir
323
+
324
+ volume = load_dicom_scan_from_dir(
325
+ "/path/to/dicom_directory",
326
+ reshape=True,
327
+ )
218
328
  ```
219
329
 
220
- Install the PyTorch and visualization dependencies:
330
+ For multi-echo data, the loader can return:
221
331
 
222
- ```bash
223
- python -m pip install "cmpl[torch,viz]"
332
+ ```text
333
+ x, y, z, echo
224
334
  ```
225
335
 
226
- ### Reconstruct a multi-echo signal from T2* and S0 maps
336
+ depending on the acquisition metadata and requested reshaping behavior.
337
+
338
+ ### Read a DICOM series as SimpleITK
227
339
 
228
340
  ```python
229
- import numpy as np
341
+ from cmpl.utilities.io import dicom_to_SimpleITK
230
342
 
231
- from cmpl.quantitative_MRI import reconstruct_images
343
+ image = dicom_to_SimpleITK("/path/to/dicom_directory")
344
+ ```
232
345
 
233
- t2_star = np.full((64, 64, 8), 20.0, dtype=np.float32)
234
- s0 = np.full((64, 64, 8), 100.0, dtype=np.float32)
235
- echo_times = np.array([0.0, 5.0, 10.0, 15.0], dtype=np.float32)
346
+ The returned image is 3D for single-echo data and 4D when multiple echoes are detected.
236
347
 
237
- images = reconstruct_images(
238
- t2_star,
239
- s0,
240
- echo_times,
241
- device="cpu",
242
- return_numpy=True,
348
+ ### Write a SimpleITK image as NIfTI
349
+
350
+ ```python
351
+ from cmpl.utilities.io import itk_to_nifti
352
+
353
+ output_path = itk_to_nifti(
354
+ image,
355
+ "output.nii.gz",
243
356
  )
357
+ ```
244
358
 
245
- print(images.shape)
246
- # (64, 64, 8, 4)
359
+ ### DICOM geometry and metadata helpers
360
+
361
+ CMPL separates DICOM geometry and acquisition-metadata handling into dedicated modules under `cmpl.dicom`.
362
+
363
+ ```python
364
+ from cmpl.dicom import (
365
+ extract_slice_geometry,
366
+ get_slice_position,
367
+ )
368
+
369
+ geometry = extract_slice_geometry("slice001.dcm")
370
+ position = get_slice_position("slice001.dcm")
371
+ ```
372
+
373
+ Enhanced-DICOM helpers are also available:
374
+
375
+ ```python
376
+ from cmpl.dicom.enhanced_dicom import (
377
+ get_slice_thickness,
378
+ get_spacing_between_slices,
379
+ voxel_sizes_detailed,
380
+ )
381
+
382
+ details = voxel_sizes_detailed(dataset)
247
383
  ```
248
384
 
249
- The signal model is:
385
+ ---
386
+
387
+ ## Quantitative MRI
388
+
389
+ Quantitative MRI functionality is available under:
390
+
391
+ ```python
392
+ cmpl.qmr
393
+ ```
394
+
395
+ Install PyTorch support:
396
+
397
+ ```bash
398
+ python -m pip install "cmpl[torch]"
399
+ ```
400
+
401
+ For NIfTI-based quantitative MRI workflows:
402
+
403
+ ```bash
404
+ python -m pip install "cmpl[io,torch]"
405
+ ```
406
+
407
+ ### Signal model
408
+
409
+ The current two-parameter T2* implementation uses the mono-exponential signal model:
250
410
 
251
411
  ```text
252
412
  S(TE) = S0 * exp(-TE / T2*)
253
413
  ```
254
414
 
415
+ where:
416
+
417
+ - `S0` is the extrapolated signal at TE = 0
418
+ - `T2*` is the transverse relaxation time
419
+ - TE and T2* must use the same time unit
420
+
421
+ CMPL conventionally uses milliseconds for T2* workflows.
422
+
255
423
  ### Fit a 3D two-parameter T2* model
256
424
 
257
425
  ```python
@@ -271,7 +439,133 @@ t2_star_map = result["T2_star_map"]
271
439
  s0_map = result["S0_map"]
272
440
  ```
273
441
 
274
- If CUDA is available and no device is supplied, the function can select a CUDA device automatically. CUDA usage will increase computation speed significantly.
442
+ The expected image layout is:
443
+
444
+ ```text
445
+ x, y, z, echo
446
+ ```
447
+
448
+ If CUDA is available and no device is supplied, the fitter can select CUDA automatically.
449
+
450
+ ### Command-line 3D T2* mapping
451
+
452
+ CMPL includes a command-line interface for calculating T2* and S0 maps directly from a 4D multi-echo NIfTI file and its JSON metadata sidecar.
453
+
454
+ Install the required dependencies:
455
+
456
+ ```bash
457
+ python -m pip install "cmpl[io,torch]"
458
+ ```
459
+
460
+ Given:
461
+
462
+ ```text
463
+ multi_echo.nii.gz
464
+ multi_echo.json
465
+ ```
466
+
467
+ run:
468
+
469
+ ```bash
470
+ cmpl-t2star multi_echo.nii.gz
471
+ ```
472
+
473
+ The JSON sidecar is detected automatically when it has the same basename as the NIfTI file.
474
+
475
+ The CLI reads echo times from:
476
+
477
+ ```json
478
+ {
479
+ "Acquisition": {
480
+ "EchoTimes": [2.5, 5.0, 7.5, 10.0],
481
+ "TimeUnit": "ms"
482
+ }
483
+ }
484
+ ```
485
+
486
+ The input NIfTI must be 4D:
487
+
488
+ ```text
489
+ x, y, z, echo
490
+ ```
491
+
492
+ and the number of entries in `EchoTimes` must match the number of volumes in the fourth dimension.
493
+
494
+ The command writes:
495
+
496
+ ```text
497
+ multi_echo_T2star.nii.gz
498
+ multi_echo_S0.nii.gz
499
+ ```
500
+
501
+ The T2* map is written in milliseconds. The S0 map retains the signal-intensity units of the input data. Output maps preserve the spatial geometry of the source NIfTI.
502
+
503
+ Specify a different JSON file:
504
+
505
+ ```bash
506
+ cmpl-t2star multi_echo.nii.gz \
507
+ --json metadata.json
508
+ ```
509
+
510
+ Specify a custom output prefix:
511
+
512
+ ```bash
513
+ cmpl-t2star multi_echo.nii.gz \
514
+ -o results/subject01
515
+ ```
516
+
517
+ This creates:
518
+
519
+ ```text
520
+ results/subject01_T2star.nii.gz
521
+ results/subject01_S0.nii.gz
522
+ ```
523
+
524
+ Request CUDA explicitly:
525
+
526
+ ```bash
527
+ cmpl-t2star multi_echo.nii.gz --device cuda
528
+ ```
529
+
530
+ If no device is specified, CMPL uses CUDA when available and otherwise uses CPU.
531
+
532
+ Optimization settings can also be adjusted:
533
+
534
+ ```bash
535
+ cmpl-t2star multi_echo.nii.gz \
536
+ --device cuda \
537
+ --iterations 10000 \
538
+ --lr 0.01 \
539
+ --initial-t2star 20
540
+ ```
541
+
542
+ If echo times are present but not ordered, the CLI sorts the echo times and their corresponding NIfTI volumes together before fitting.
543
+
544
+ ### Reconstruct a multi-echo signal from T2* and S0 maps
545
+
546
+ ```python
547
+ import numpy as np
548
+
549
+ from cmpl.quantitative_MRI import reconstruct_images
550
+
551
+ t2_star = np.full((64, 64, 8), 20.0, dtype=np.float32)
552
+ s0 = np.full((64, 64, 8), 100.0, dtype=np.float32)
553
+ echo_times = np.array(
554
+ [0.0, 5.0, 10.0, 15.0],
555
+ dtype=np.float32,
556
+ )
557
+
558
+ images = reconstruct_images(
559
+ t2_star,
560
+ s0,
561
+ echo_times,
562
+ device="cpu",
563
+ return_numpy=True,
564
+ )
565
+
566
+ print(images.shape)
567
+ # (64, 64, 8, 4)
568
+ ```
275
569
 
276
570
  ### Calculate normalized fitting error
277
571
 
@@ -286,7 +580,11 @@ rmse_pct, rse_pct = calculate_rmse_percentage_s0(
286
580
  )
287
581
  ```
288
582
 
289
- CMPL also contains two- and three-parameter 2D/3D T2* fitting functions.
583
+ CMPL also contains additional 2D/3D T2* fitting functions.
584
+
585
+ Plotting is optional. Matplotlib is imported only when plotting is requested.
586
+
587
+ ---
290
588
 
291
589
  ## Reconstruction
292
590
 
@@ -296,7 +594,7 @@ Reconstruction functionality is available under:
296
594
  cmpl.recon
297
595
  ```
298
596
 
299
- PyTorch is required for the current reconstruction implementations:
597
+ Install PyTorch support:
300
598
 
301
599
  ```bash
302
600
  python -m pip install "cmpl[torch]"
@@ -316,7 +614,7 @@ reconstructed_kspace = grappa_1d_recon(
316
614
  )
317
615
  ```
318
616
 
319
- `calibration_kspace` and `undersampled_kspace` are expected to contain coil-resolved k-space data. The current implementation uses the ordering:
617
+ The current implementation expects coil-resolved k-space in the order:
320
618
 
321
619
  ```text
322
620
  frequency, phase, slice, coils
@@ -338,7 +636,7 @@ reconstructed_kspace = grappa_2d_recon(
338
636
  ### Conjugate-gradient SENSE
339
637
 
340
638
  ```python
341
- from cmpl.reconstruction.sense import CG_sense_2D
639
+ from cmpl.reconstruction.sense.cg import CG_sense_2D
342
640
 
343
641
  reconstructed_image = CG_sense_2D(
344
642
  undersampled_image_space,
@@ -348,87 +646,53 @@ reconstructed_image = CG_sense_2D(
348
646
 
349
647
  Inputs to the current SENSE implementation are PyTorch tensors.
350
648
 
351
- ## I/O
352
-
353
- Install the I/O extra:
354
-
355
- ```bash
356
- python -m pip install "cmpl[io]"
357
- ```
358
-
359
- ### Read a NIfTI file
360
-
361
- ```python
362
- from cmpl.utilities.io import nifti_read
649
+ ---
363
650
 
364
- nifti_image, data = nifti_read("image.nii.gz")
365
- ```
366
-
367
- ### Replace NIfTI data while preserving geometry
651
+ ## Visualization
368
652
 
369
- ```python
370
- from cmpl.utilities.io import update_nifti_data
653
+ Install the visualization extra:
371
654
 
372
- updated = update_nifti_data(
373
- "reference.nii.gz",
374
- new_data,
375
- output_path="updated.nii.gz",
376
- )
655
+ ```bash
656
+ python -m pip install "cmpl[viz]"
377
657
  ```
378
658
 
379
- ### Load a DICOM directory
659
+ ### Browse or display a 3D MRI volume
380
660
 
381
661
  ```python
382
- from cmpl.utilities.io import load_dicom_scan_from_dir
662
+ from cmpl.visualization import plot_3D_mri
383
663
 
384
- volume = load_dicom_scan_from_dir(
385
- "/path/to/dicom_directory",
386
- reshape=True,
664
+ plot_3D_mri(
665
+ volume,
666
+ slice_number=volume.shape[2] // 2,
667
+ alpha=0.5,
668
+ direction="sagittal",
669
+ cmap="gray",
670
+ vmin=0,
671
+ vmax=1,
672
+ dpi=300,
387
673
  )
388
674
  ```
389
675
 
390
- For multi-echo data, the loader can return data arranged as:
391
-
392
- ```text
393
- x, y, z, echo
394
- ```
395
-
396
- depending on the acquisition metadata and requested reshaping behavior.
397
-
398
- ### DICOM to SimpleITK
399
-
400
- ```python
401
- from cmpl.utilities.io import dicom_to_SimpleITK
402
-
403
- image = dicom_to_SimpleITK("/path/to/dicom_directory")
404
- ```
676
+ If an interactive Matplotlib backend is available, `plot_3D_mri` can use interactive controls. Otherwise it falls back to static redraw mode.
405
677
 
406
- ### Write a SimpleITK image as NIfTI
678
+ ### Compare images side by side
407
679
 
408
680
  ```python
409
- from cmpl.utilities.io import itk_to_nifti
681
+ from cmpl.visualization import side_by_side_view
410
682
 
411
- output_path = itk_to_nifti(
412
- image,
413
- "output.nii.gz",
683
+ side_by_side_view(
684
+ image_a,
685
+ image_b,
686
+ titles=["Reference", "Reconstruction"],
687
+ color_palette="gray",
414
688
  )
415
689
  ```
416
690
 
417
- ### Enhanced-DICOM helpers
418
-
419
- ```python
420
- from cmpl.dicom.enhanced_dicom import (
421
- get_slice_thickness,
422
- get_spacing_between_slices,
423
- voxel_sizes_detailed,
424
- )
425
-
426
- details = voxel_sizes_detailed(dataset)
427
- ```
691
+ ---
428
692
 
429
693
  ## Numerical utilities
430
694
 
431
- Lightweight numerical helpers are kept separate from heavier I/O modules so visualization and other numerical workflows do not require unrelated optional dependencies.
695
+ Lightweight numerical helpers are kept separate from heavier I/O modules.
432
696
 
433
697
  ```python
434
698
  from cmpl.utilities.numerical import resize_matrix
@@ -439,15 +703,17 @@ resized = resize_matrix(
439
703
  )
440
704
  ```
441
705
 
442
- `resize_matrix` accepts NumPy arrays and PyTorch tensors. PyTorch is imported at runtime only when a Torch tensor is actually passed to the function.
706
+ `resize_matrix` accepts NumPy arrays and PyTorch tensors. PyTorch is imported only when a Torch tensor is actually passed.
443
707
 
444
- For backward compatibility, older imports such as:
708
+ For backward compatibility:
445
709
 
446
710
  ```python
447
711
  from cmpl.utilities.utils import resize_matrix
448
712
  ```
449
713
 
450
- continue to work.
714
+ continues to work.
715
+
716
+ ---
451
717
 
452
718
  ## Data indexing
453
719
 
@@ -457,7 +723,7 @@ Install the data extra:
457
723
  python -m pip install "cmpl[data]"
458
724
  ```
459
725
 
460
- CMPL includes a pandas-based utility for indexing a directory tree that follows the CMPL medical-data convention:
726
+ CMPL includes a pandas-based utility for indexing directory trees that follow the CMPL medical-data convention:
461
727
 
462
728
  ```python
463
729
  from cmpl.utilities.df_build import build_medical_data_frame
@@ -465,28 +731,26 @@ from cmpl.utilities.df_build import build_medical_data_frame
465
731
  df = build_medical_data_frame("/path/to/root")
466
732
  ```
467
733
 
468
- The utility is designed around a structure such as:
734
+ The expected structure is similar to:
469
735
 
470
736
  ```text
471
737
  root/
472
738
  ├── Study001/
473
739
  │ ├── Dicoms/
474
740
  │ │ └── <contrast>/
475
- │ ├── h5_files/
476
- │ │ └── <contrast>.h5
477
- │ └── Segmentations/
478
- │ └── <contrast>/
479
- │ └── <group>/
480
- │ └── <segmentation>.nii.gz
741
+ │ └── h5_files/
742
+ │ └── <contrast>.h5
481
743
  └── Study002/
482
744
  └── ...
483
745
  ```
484
746
 
485
- This utility is convention-specific rather than a general filesystem indexer.
747
+ This utility is convention-specific rather than a general-purpose filesystem indexer.
748
+
749
+ ---
486
750
 
487
751
  ## Lazy loading and optional dependencies
488
752
 
489
- CMPL is designed so that unrelated optional packages are not imported simply because the top-level package is imported.
753
+ CMPL is designed so unrelated optional packages are not imported simply because the top-level package is imported.
490
754
 
491
755
  For example:
492
756
 
@@ -494,9 +758,13 @@ For example:
494
758
  import cmpl
495
759
  ```
496
760
 
497
- does not immediately require PyTorch, Matplotlib, pandas, nibabel, pydicom, SimpleITK, or h5py.
761
+ does not immediately import PyTorch, Matplotlib, pandas, nibabel, pydicom, SimpleITK, h5py, or the Jupyter visualization stack.
762
+
763
+ Optional functionality is loaded only when the corresponding module or function is accessed.
498
764
 
499
- Optional functionality is loaded when its corresponding module or function is accessed. This keeps startup lightweight and allows users to install only the dependencies required for their workflow.
765
+ Within quantitative MRI, Matplotlib is also loaded lazily: non-plotting T2* workflows do not require Matplotlib.
766
+
767
+ ---
500
768
 
501
769
  ## Development
502
770
 
@@ -506,40 +774,68 @@ Clone the project and install it in editable mode with development dependencies:
506
774
  python -m pip install -e ".[dev]"
507
775
  ```
508
776
 
509
- For development across the currently tested major feature groups:
777
+ For development across the main optional feature groups:
510
778
 
511
779
  ```bash
512
780
  python -m pip install -e ".[dev,io,data,viz,torch]"
513
781
  ```
514
782
 
515
- Run the test suite:
783
+ Run the full test suite:
516
784
 
517
785
  ```bash
518
786
  python -m pytest tests/ -v
519
787
  ```
520
788
 
521
- The test suite includes:
789
+ The test suite covers:
522
790
 
523
- - lazy-import and dependency-boundary tests
524
- - qMRI numerical tests
525
- - GRAPPA and SENSE reconstruction tests
791
+ - lazy-import and dependency-boundary behavior
792
+ - qMRI numerical fitting
793
+ - T2* CLI validation and NIfTI output
794
+ - GRAPPA and SENSE reconstruction
526
795
  - visualization smoke tests
527
- - synthetic NIfTI, DICOM, and SimpleITK I/O tests
528
- - data-indexing tests
796
+ - synthetic NIfTI, DICOM, and SimpleITK I/O
797
+ - conventional and Enhanced multi-echo DICOM-to-NIfTI conversion
798
+ - JSON sidecar generation
799
+ - DICOM geometry and metadata
800
+ - data indexing
529
801
 
530
802
  ### Build the package
531
803
 
804
+ Build distributions:
805
+
532
806
  ```bash
807
+ rm -rf build dist *.egg-info
533
808
  python -m build
809
+ ```
810
+
811
+ Validate them:
812
+
813
+ ```bash
534
814
  python -m twine check dist/*
535
815
  ```
536
816
 
817
+ Before publishing, it is also useful to install the built wheel into a clean environment and verify the packaged CLI commands:
818
+
819
+ ```bash
820
+ cmpl-dicom-to-nifti --help
821
+ cmpl-t2star --help
822
+ ```
823
+
824
+ ---
825
+
537
826
  ## Package layout
538
827
 
539
828
  ```text
540
829
  src/cmpl/
830
+ ├── _version.py
831
+ ├── cli/
832
+ │ ├── __init__.py
833
+ │ ├── dicom_to_nifti.py
834
+ │ └── t2star.py
541
835
  ├── dicom/
542
- │ └── enhanced_dicom.py
836
+ │ ├── enhanced_dicom.py
837
+ │ ├── geometry.py
838
+ │ └── metadata.py
543
839
  ├── quantitative_MRI/
544
840
  │ └── mapping.py
545
841
  ├── reconstruction/
@@ -558,6 +854,8 @@ src/cmpl/
558
854
  └── visualization.py
559
855
  ```
560
856
 
857
+ ---
858
+
561
859
  ## License
562
860
 
563
861
  See the `LICENSE` file included with the project for licensing terms.