Skip to content

Commit 1d36e32

Browse files
authored
Merge pull request #90 from bilgelm/pet3d
ENH: enable running petprep on 3D PET images
2 parents 188ce2f + ea48821 commit 1d36e32

File tree

5 files changed

+81
-72
lines changed

5 files changed

+81
-72
lines changed

.maint/CONTRIBUTORS.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ Before every release, unlisted contributors will be invited again to add their n
1919
| Jonghyun | Bae | | 0000-0002-3987-6603 | National Institute on Aging |
2020
| Legarreta Gorroño | Jon Haitz | @jhlegarreta | 0000-0002-9661-1396 | Brigham and Women's Hospital, Mass General Brigham, Harvard Medical School, MA, USA |
2121
| Markiewicz | Christopher J. | @effigies | 0000-0002-6533-164X | Department of Psychology, Stanford University, CA, USA |
22-
| Murat | Bilgel | @bilgelm | 0000-0001-5042-7422 | National Institute on Aging |
2322
| Schiavone | Alice | @aliswh | 0009-0006-4844-2088 | Neurobiology Research Unit, Rigshospitalet, Copenhagen and Department of Computer Science, University of Copenhagen |

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ ENV PATH="/opt/afni-latest:$PATH" \
203203
RUN useradd -m -s /bin/bash -G users petprep
204204
WORKDIR /home/petprep
205205
ENV HOME="/home/petprep" \
206-
LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
206+
LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH:-}"
207207

208208
COPY --from=micromamba /bin/micromamba /bin/micromamba
209209
COPY --from=micromamba /opt/conda/envs/petprep /opt/conda/envs/petprep
@@ -212,7 +212,7 @@ ENV MAMBA_ROOT_PREFIX="/opt/conda"
212212
RUN micromamba shell init -s bash && \
213213
echo "micromamba activate petprep" >> $HOME/.bashrc
214214
ENV PATH="/opt/conda/envs/petprep/bin:$PATH" \
215-
CPATH="/opt/conda/envs/petprep/include:$CPATH" \
215+
CPATH="/opt/conda/envs/petprep/include:${CPATH:-}" \
216216
LD_LIBRARY_PATH="/opt/conda/envs/petprep/lib:$LD_LIBRARY_PATH"
217217

218218
# Precaching atlases

petprep/utils/misc.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ def estimate_pet_mem_usage(pet_fname: str) -> tuple[int, dict]:
7878
nvox = int(np.prod(img.shape, dtype='u8'))
7979
# Assume tools will coerce to 8-byte floats to be safe
8080
pet_size_gb = 8 * nvox / (1024**3)
81-
pet_tlen = img.shape[-1]
81+
82+
if img.ndim == 4:
83+
pet_tlen = img.shape[3]
84+
elif img.ndim == 3:
85+
pet_tlen = 1
86+
else:
87+
raise ValueError('PET image must be 3D or 4D')
88+
8289
mem_gb = {
8390
'filesize': pet_size_gb,
8491
'resampled': pet_size_gb * 4,

petprep/workflows/pet/base.py

Lines changed: 62 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,6 @@ def init_pet_wf(
166166
all_metadata = [config.execution.layout.get_metadata(file) for file in pet_series]
167167

168168
nvols, mem_gb = estimate_pet_mem_usage(pet_file)
169-
if nvols <= 1 - config.execution.sloppy:
170-
config.loggers.workflow.warning(
171-
f'Too short PET series (<= 5 timepoints). Skipping processing of <{pet_file}>.'
172-
)
173-
return
174169

175170
config.loggers.workflow.debug(
176171
'Creating pet processing workflow for <%s> (%.2f GB / %d frames). '
@@ -755,81 +750,82 @@ def init_pet_wf(
755750
(pet_ref_tacs_wf, ds_ref_tacs, [('outputnode.timeseries', 'in_file')]),
756751
]) # fmt:skip
757752

758-
pet_confounds_wf = init_pet_confs_wf(
759-
mem_gb=mem_gb['largemem'],
760-
metadata=all_metadata[0],
761-
freesurfer=config.workflow.run_reconall,
762-
regressors_all_comps=config.workflow.regressors_all_comps,
763-
regressors_fd_th=config.workflow.regressors_fd_th,
764-
regressors_dvars_th=config.workflow.regressors_dvars_th,
765-
name='pet_confounds_wf',
766-
)
767-
768-
ds_confounds = pe.Node(
769-
DerivativesDataSink(
770-
base_directory=petprep_dir,
771-
desc='confounds',
772-
suffix='timeseries',
773-
),
774-
name='ds_confounds',
775-
run_without_submitting=True,
776-
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
777-
)
778-
ds_confounds.inputs.source_file = pet_file
779-
780-
workflow.connect([
781-
(inputnode, pet_confounds_wf, [
782-
('t1w_tpms', 'inputnode.t1w_tpms'),
783-
('t1w_mask', 'inputnode.t1w_mask'),
784-
]),
785-
(pet_fit_wf, pet_confounds_wf, [
786-
('outputnode.pet_mask', 'inputnode.pet_mask'),
787-
('outputnode.petref', 'inputnode.petref'),
788-
('outputnode.motion_xfm', 'inputnode.motion_xfm'),
789-
('outputnode.petref2anat_xfm', 'inputnode.petref2anat_xfm'),
790-
]),
791-
(pet_native_wf, pet_confounds_wf, [
792-
('outputnode.pet_native', 'inputnode.pet'),
793-
]),
794-
(pet_confounds_wf, ds_confounds, [
795-
('outputnode.confounds_file', 'in_file'),
796-
('outputnode.confounds_metadata', 'meta_dict'),
797-
]),
798-
]) # fmt:skip
799-
800-
if spaces.get_spaces(nonstandard=False, dim=(3,)):
801-
carpetplot_wf = init_carpetplot_wf(
802-
mem_gb=mem_gb['resampled'],
753+
if nvols > 1: # run these only if 4-D PET
754+
pet_confounds_wf = init_pet_confs_wf(
755+
mem_gb=mem_gb['largemem'],
803756
metadata=all_metadata[0],
804-
cifti_output=config.workflow.cifti_output,
805-
name='carpetplot_wf',
757+
freesurfer=config.workflow.run_reconall,
758+
regressors_all_comps=config.workflow.regressors_all_comps,
759+
regressors_fd_th=config.workflow.regressors_fd_th,
760+
regressors_dvars_th=config.workflow.regressors_dvars_th,
761+
name='pet_confounds_wf',
806762
)
807763

808-
if config.workflow.cifti_output:
809-
workflow.connect(
810-
pet_grayords_wf, 'outputnode.cifti_pet', carpetplot_wf, 'inputnode.cifti_pet',
811-
) # fmt:skip
812-
813-
def _last(inlist):
814-
return inlist[-1]
764+
ds_confounds = pe.Node(
765+
DerivativesDataSink(
766+
base_directory=petprep_dir,
767+
desc='confounds',
768+
suffix='timeseries',
769+
),
770+
name='ds_confounds',
771+
run_without_submitting=True,
772+
mem_gb=config.DEFAULT_MEMORY_MIN_GB,
773+
)
774+
ds_confounds.inputs.source_file = pet_file
815775

816776
workflow.connect([
817-
(inputnode, carpetplot_wf, [
818-
('mni2009c2anat_xfm', 'inputnode.std2anat_xfm'),
777+
(inputnode, pet_confounds_wf, [
778+
('t1w_tpms', 'inputnode.t1w_tpms'),
779+
('t1w_mask', 'inputnode.t1w_mask'),
819780
]),
820-
(pet_fit_wf, carpetplot_wf, [
781+
(pet_fit_wf, pet_confounds_wf, [
821782
('outputnode.pet_mask', 'inputnode.pet_mask'),
783+
('outputnode.petref', 'inputnode.petref'),
784+
('outputnode.motion_xfm', 'inputnode.motion_xfm'),
822785
('outputnode.petref2anat_xfm', 'inputnode.petref2anat_xfm'),
823786
]),
824-
(pet_native_wf, carpetplot_wf, [
787+
(pet_native_wf, pet_confounds_wf, [
825788
('outputnode.pet_native', 'inputnode.pet'),
826789
]),
827-
(pet_confounds_wf, carpetplot_wf, [
828-
('outputnode.confounds_file', 'inputnode.confounds_file'),
829-
('outputnode.crown_mask', 'inputnode.crown_mask'),
790+
(pet_confounds_wf, ds_confounds, [
791+
('outputnode.confounds_file', 'in_file'),
792+
('outputnode.confounds_metadata', 'meta_dict'),
830793
]),
831794
]) # fmt:skip
832795

796+
if spaces.get_spaces(nonstandard=False, dim=(3,)):
797+
carpetplot_wf = init_carpetplot_wf(
798+
mem_gb=mem_gb['resampled'],
799+
metadata=all_metadata[0],
800+
cifti_output=config.workflow.cifti_output,
801+
name='carpetplot_wf',
802+
)
803+
804+
if config.workflow.cifti_output:
805+
workflow.connect(
806+
pet_grayords_wf, 'outputnode.cifti_pet', carpetplot_wf, 'inputnode.cifti_pet',
807+
) # fmt:skip
808+
809+
def _last(inlist):
810+
return inlist[-1]
811+
812+
workflow.connect([
813+
(inputnode, carpetplot_wf, [
814+
('mni2009c2anat_xfm', 'inputnode.std2anat_xfm'),
815+
]),
816+
(pet_fit_wf, carpetplot_wf, [
817+
('outputnode.pet_mask', 'inputnode.pet_mask'),
818+
('outputnode.petref2anat_xfm', 'inputnode.petref2anat_xfm'),
819+
]),
820+
(pet_native_wf, carpetplot_wf, [
821+
('outputnode.pet_native', 'inputnode.pet'),
822+
]),
823+
(pet_confounds_wf, carpetplot_wf, [
824+
('outputnode.confounds_file', 'inputnode.confounds_file'),
825+
('outputnode.crown_mask', 'inputnode.crown_mask'),
826+
]),
827+
]) # fmt:skip
828+
833829
# Fill-in datasinks of reportlets seen so far
834830
for node in workflow.list_node_names():
835831
if node.split('.')[-1].startswith('ds_report'):

petprep/workflows/pet/fit.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import nibabel as nb
2626
from nipype.interfaces import utility as niu
2727
from nipype.pipeline import engine as pe
28+
from nitransforms.linear import Affine
2829
from niworkflows.interfaces.header import ValidateImage
2930
from niworkflows.utils.connections import listify
3031

@@ -198,12 +199,18 @@ def init_pet_fit_wf(
198199
)
199200
hmc_buffer = pe.Node(niu.IdentityInterface(fields=['hmc_xforms']), name='hmc_buffer')
200201

202+
if pet_tlen <= 1: # 3D PET
203+
petref = pet_file
204+
idmat_fname = config.execution.work_dir / 'idmat.tfm'
205+
Affine().to_filename(idmat_fname, fmt='itk')
206+
hmc_xforms = idmat_fname
207+
config.loggers.workflow.debug('3D PET file - motion correction not needed')
201208
if petref:
202209
petref_buffer.inputs.petref = petref
203-
config.loggers.workflow.debug('Reusing motion correction reference: %s', petref)
210+
config.loggers.workflow.debug('(Re)using motion correction reference: %s', petref)
204211
if hmc_xforms:
205212
hmc_buffer.inputs.hmc_xforms = hmc_xforms
206-
config.loggers.workflow.debug('Reusing motion correction transforms: %s', hmc_xforms)
213+
config.loggers.workflow.debug('(Re)using motion correction transforms: %s', hmc_xforms)
207214

208215
timing_parameters = prepare_timing_parameters(metadata)
209216
frame_durations = timing_parameters.get('FrameDuration')

0 commit comments

Comments
 (0)