Skip to content

Commit 106b149

Browse files
authored
Merge pull request #143 from nipreps/create_morph_refmask
Create morph refmask and derivatives
2 parents b48fcbf + 8363a71 commit 106b149

16 files changed

+237
-48
lines changed

docs/outputs.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,9 +323,9 @@ table containing the mean uptake within that region::
323323

324324
sub-<subject_label>/
325325
pet/
326-
sub-<subject_label>_[specifiers]_seg-<seg>_ref-<ref>_desc-preproc_tacs.tsv
326+
sub-<subject_label>_[specifiers]_label-<label>_desc-preproc_tacs.tsv
327327

328-
The ``ref`` entity captures the reference region identifier provided via the
328+
The ``label`` entity captures the reference region identifier provided via the
329329
:ref:`CLI options <cli_refmask>` ``--ref-mask-name`` and ``--ref-mask-index``.
330330
As with the primary TACs, ``desc-preproc`` reflects use of the preprocessed PET
331331
series.

docs/usage.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,12 @@ The available masks are and do not require ``--ref-mask-index`` to be specified:
271271

272272
The presets are defined in ``petprep/data/reference_mask/config.json``.
273273

274+
275+
When a reference mask is created, *PETPrep* also generates a TSV table
276+
``label-<name>_desc-ref_morph.tsv`` saved under the ``anat/`` derivatives folder. This
277+
table mirrors the segmentation morph tables and contains three columns:
278+
``index``, ``name`` and ``volume-mm3``.
279+
274280
If you want to use a custom mask, you can provide it using the ``--ref-mask-name`` and ``--ref-mask-index`` options,
275281
specifying the name and indices of your choice for a given segmentation (``--seg``).
276282

@@ -279,7 +285,7 @@ For example, to extract a mask of thalamus to use as a reference region, you can
279285
$ petprep /data/bids_root /out participant \
280286
--seg gtm --ref-mask-name thalamus --ref-mask-index 10 49
281287

282-
The indices of the regions from a given segmentation can be found in the corresponding ``/anat/sub-<participant_label>_desc-<segmentation>_morph.tsv``.
288+
The indices of the regions from a given segmentation can be found in the corresponding ``/anat/sub-<participant_label>_seg-<segmentation>_morph.tsv``.
283289

284290
Troubleshooting
285291
---------------

docs/workflows.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,10 @@ sufficient disk space is available. Each segmentation produces a labeled NIfTI
559559
image ``seg-<seg>_dseg.nii.gz`` and a TSV table of region volumes
560560
``seg-<seg>_morph.tsv`` saved under the ``anat/`` derivatives folder.
561561

562+
Reference masks generated via ``--ref-mask-name`` create a similar
563+
``label-<name>_desc-ref_morph.tsv`` file. These TSVs share the same columns as the
564+
segmentation morph tables: ``index``, ``name`` and ``volume-mm3``.
565+
562566
For example, the raphe segmentation can be enabled with::
563567

564568
petprep run /data/bids /data/out --seg raphe

petprep/data/io_spec.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"patterns": [
4545
"sub-{subject}[/ses-{session}]/{datatype<func|pet>|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_ref-{ref}][_res-{res}][_label-{label}][_echo-{echo}][_space-{space}][_desc-{desc}]_{suffix<bold|boldref|pet|petref|dseg|mask>}.{extension<nii|nii.gz|json>|nii.gz}",
4646
"sub-{subject}[/ses-{session}]/{datatype<func|pet>|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_run-{run}]_from-{from}_to-{to}_mode-{mode<image|points>|image}_{suffix<xfm>|xfm}.{extension<txt|h5>}",
47-
"sub-{subject}[/ses-{session}]/{datatype<func|pet>|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_ref-{ref}][_run-{run}][_part-{part}][_desc-{desc}]_{suffix<timeseries>}.{extension<tsv|json>}",
47+
"sub-{subject}[/ses-{session}]/{datatype<func|pet>|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_ref-{ref}][_label-{label}][_run-{run}][_part-{part}][_desc-{desc}]_{suffix<timeseries>}.{extension<tsv|json>}",
4848
"sub-{subject}[/ses-{session}]/{datatype<func|pet>|pet}/sub-{subject}[_ses-{session}][_task-{task}][_acq-{acquisition}][_ce-{ceagent}][_rec-{reconstruction}][_ref-{ref}][_space-{space}][_res-{res}][_den-{den}][_hemi-{hemi}[_label-{label}][_desc-{desc}]_{suffix<|boldref|petref|dseg|mask>}.{extension<dtseries.nii|dtseries.json>}"
4949
]
5050
}

petprep/data/reports-spec-pet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ sections:
99
- bids: {datatype: figures, desc: carpetplot, suffix: pet}
1010
- bids: {datatype: figures, desc: confoundcorr, suffix: pet}
1111
- bids: {datatype: figures, desc: coreg, suffix: pet}
12-
- bids: {datatype: figures, desc: refmask, suffix: pet}
12+
- bids: {datatype: figures, desc: ref, suffix: pet}
1313
caption: Reference region mask overlaid on the PET reference and anatomical data.
1414
static: false
1515
subtitle: Reference mask check

petprep/data/reports-spec.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ sections:
138138
caption: PET to anatomical alignment check
139139
static: false
140140
subtitle: Additional PET Visualizations
141-
- bids: {datatype: figures, desc: refmask, suffix: pet}
141+
- bids: {datatype: figures, desc: ref, suffix: pet}
142142
caption: Reference region mask overlaid on the PET reference and anatomical.
143143
static: false
144144
subtitle: Reference mask check

petprep/utils/reference_mask.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,30 @@ def generate_reference_region(
6767
mask = mask.astype(np.uint8)
6868

6969
return nib.Nifti1Image(mask, affine, header)
70+
71+
72+
def mask_to_stats(mask_file: str, mask_name: str) -> str:
73+
"""Generate a TSV table of morphological statistics from a binary mask."""
74+
from pathlib import Path
75+
76+
import nibabel as nb
77+
import numpy as np
78+
import pandas as pd
79+
80+
mask_file = Path(mask_file)
81+
img = nb.load(mask_file)
82+
data = img.get_fdata() > 0
83+
voxel_vol_mm3 = np.prod(img.header.get_zooms())
84+
volume_mm3 = float(data.sum() * voxel_vol_mm3)
85+
86+
df = pd.DataFrame(
87+
{
88+
'index': [1],
89+
'name': [mask_name],
90+
'volume-mm3': [volume_mm3],
91+
}
92+
)
93+
94+
out_file = mask_file.with_suffix('.tsv')
95+
df.to_csv(out_file, sep='\t', index=False)
96+
return str(out_file)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import nibabel as nb
2+
import numpy as np
3+
import pandas as pd
4+
5+
from petprep.utils.reference_mask import mask_to_stats
6+
7+
8+
def test_mask_to_stats(tmp_path):
9+
data = np.ones((2, 2, 2), dtype=np.uint8)
10+
img = nb.Nifti1Image(data, np.eye(4))
11+
mask_file = tmp_path / 'mask.nii.gz'
12+
img.to_filename(mask_file)
13+
14+
out = mask_to_stats(str(mask_file), 'testmask')
15+
df = pd.read_csv(out, sep='\t')
16+
assert list(df.columns) == ['index', 'name', 'volume-mm3']
17+
assert df.iloc[0]['index'] == 1
18+
assert df.iloc[0]['name'] == 'testmask'
19+
expected_vol = data.sum() * np.prod(img.header.get_zooms())
20+
assert df.iloc[0]['volume-mm3'] == expected_vol

petprep/workflows/pet/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -730,10 +730,9 @@ def init_pet_wf(
730730
DerivativesDataSink(
731731
base_directory=petprep_dir,
732732
suffix='tacs',
733-
seg=config.workflow.seg,
734733
desc='preproc',
735-
ref=config.workflow.ref_mask_name,
736-
allowed_entities=('seg', 'ref'),
734+
label=config.workflow.ref_mask_name,
735+
allowed_entities=('label',),
737736
TaskName=all_metadata[0].get('TaskName'),
738737
**prepare_timing_parameters(all_metadata[0]),
739738
),

petprep/workflows/pet/fit.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -445,10 +445,9 @@ def init_pet_fit_wf(
445445
DerivativesDataSink(
446446
base_directory=config.execution.petprep_dir,
447447
suffix='tacs',
448-
seg=config.workflow.seg,
449448
desc='preproc',
450-
ref=config.workflow.ref_mask_name,
451-
allowed_entities=('seg', 'ref'),
449+
label=config.workflow.ref_mask_name,
450+
allowed_entities=('label',),
452451
TaskName=metadata.get('TaskName'),
453452
**timing_parameters,
454453
),
@@ -489,10 +488,18 @@ def init_pet_fit_wf(
489488
],
490489
),
491490
(
492-
petref_buffer,
491+
inputnode,
492+
ds_refmask_wf,
493+
[
494+
('segmentation', 'inputnode.segmentation'),
495+
('t1w_preproc', 'inputnode.anat_sources'),
496+
],
497+
),
498+
(
499+
gm_select,
493500
ds_refmask_wf,
494501
[
495-
('pet_file', 'inputnode.source_files'),
502+
('out', 'inputnode.source_files'),
496503
],
497504
),
498505
(

0 commit comments

Comments
 (0)