diff --git a/clinica/pipelines/anatomical/freesurfer/longitudinal/correction/utils.py b/clinica/pipelines/anatomical/freesurfer/longitudinal/correction/utils.py index bc56c521c..ce22bae18 100644 --- a/clinica/pipelines/anatomical/freesurfer/longitudinal/correction/utils.py +++ b/clinica/pipelines/anatomical/freesurfer/longitudinal/correction/utils.py @@ -282,7 +282,7 @@ def get_processed_images( atlas: Optional[str] = None, ) -> list[str]: """ - Extract image IDs (e.g. ["sub-CLNC01_ses-M000_long-M000M018", "sub-CLNC01_ses-M018_long-M000M018"]) of outputs + Extract image IDs (e.g. ["sub-CLNC01_ses-M000_long-M000+M018", "sub-CLNC01_ses-M018_long-M000+M018"]) of outputs already processed by T1FreeSurferLongitudinalCorrection pipeline. """ image_ids = [] diff --git a/clinica/pipelines/anatomical/freesurfer/longitudinal/template/pipeline.py b/clinica/pipelines/anatomical/freesurfer/longitudinal/template/pipeline.py index a1d5b34e8..1fa6a0f92 100644 --- a/clinica/pipelines/anatomical/freesurfer/longitudinal/template/pipeline.py +++ b/clinica/pipelines/anatomical/freesurfer/longitudinal/template/pipeline.py @@ -35,7 +35,7 @@ def get_processed_images( list_participant_id, list_long_id, caps_directory, T1_FS_T_DESTRIEUX ) image_ids = [ - re.search(r"(sub-[a-zA-Z0-9]+)_(long-[a-zA-Z0-9]+)", file).group() + re.search(r"(sub-[a-zA-Z0-9]+)_(long-[a-zA-Z0-9+]+)", file).group() for file in t1_freesurfer_files ] return image_ids diff --git a/clinica/pipelines/anatomical/freesurfer/longitudinal/utils.py b/clinica/pipelines/anatomical/freesurfer/longitudinal/utils.py index f24d899a4..63066347a 100644 --- a/clinica/pipelines/anatomical/freesurfer/longitudinal/utils.py +++ b/clinica/pipelines/anatomical/freesurfer/longitudinal/utils.py @@ -71,7 +71,7 @@ def grab_image_ids_from_caps_directory( part_ids = ["sub-CLNC01", "sub-CLNC01", "sub-CLNC01" ] sess_ids = ["ses-M000", "ses-M018", "ses-M036" ] - long_ids = ["long-M000M018", "long-M000M018", "long-M000M018"] + long_ids = ["long-M000+M018", "long-M000+M018", "long-M000+M018"] (sub-CLNC02 does not have longitudinal ID so it does not appear on the result) Parameters diff --git a/clinica/utils/longitudinal.py b/clinica/utils/longitudinal.py index 082caf44a..e5d347dab 100644 --- a/clinica/utils/longitudinal.py +++ b/clinica/utils/longitudinal.py @@ -26,16 +26,16 @@ def get_long_id(session_ids: List[str]) -> str: >>> get_long_id(['ses-M000']) 'long-M000' >>> get_long_id(['ses-M000', 'ses-M018', 'ses-M036']) - 'long-M000M018M036' + 'long-M000+M018+M036' >>> get_long_id(['ses-M018', 'ses-M036', 'ses-M000']) - 'long-M000M018M036' + 'long-M000+M018+M036' """ if not all([session_id.startswith("ses-") for session_id in session_ids]): raise ValueError( "Expected a list of session IDs of the form ses-XXX, " f"but received {session_ids} instead." ) - return "long-" + "".join( + return "long-" + "+".join( [session_id.lstrip("ses-") for session_id in sorted(session_ids)] ) @@ -62,7 +62,7 @@ def get_participants_long_id( -------- >>> from clinica.utils.longitudinal import get_participants_long_id >>> get_participants_long_id(['sub-CLNC01', 'sub-CLNC01', 'sub-CLNC02'], ['ses-M000', 'ses-M018', 'ses-M000']) - ['long-M000M018', 'long-M000M018', 'long-M000'] + ['long-M000+M018', 'long-M000+M018', 'long-M000'] """ from .participant import get_unique_subjects diff --git a/docs/CAPS/Introduction.md b/docs/CAPS/Introduction.md index 4b8bf7d14..6ae4b9ba8 100644 --- a/docs/CAPS/Introduction.md +++ b/docs/CAPS/Introduction.md @@ -34,7 +34,7 @@ You will find this notion when working on longitudinal datasets. This is simply a label that will define the set of sessions considered. Unlike group, longitudinal template is dedicated to intra-subject analysis. Besides, the longitudinal label for a participant is defined concatenating the different session labels in alphabetical order so that the longitudinal label will be unique. -For instance, if the intra-subject template is computed on `M018` and `M000` sessions, the longitudinal ID will be `long-M000M018`. +For instance, if the intra-subject template is computed on `M018` and `M000` sessions, the longitudinal ID will be `long-M000+M018`. ## Differences with BIDS @@ -123,8 +123,8 @@ groups/ This CAPS folder contains the outputs of longitudinal segmentations performed with [FreeSurfer](../Software/Third-party.md#freesurfer) for a fictional participant `CLNC01` at sessions `M000` and `M018`. First, the [`t1-freesurfer` pipeline](../Pipelines/T1_FreeSurfer.md) is run on the two sessions. -Then, the [`t1-freesurfer-longitudinal` pipeline](../Pipelines/T1_FreeSurfer_Longitudinal.md) will compute the intra-subject template `sub-CLNC01_long-M000M018` using the `M000` and `M018` sessions. -This template is finally used to longitudinally correct the segmentations, whose results are stored in the `sub-CLNC01_ses-M000.long.sub-CLNC01_long-M000M018` and `sub-CLNC01_ses-M018.long.sub-CLNC01_long-M000M018` folders. +Then, the [`t1-freesurfer-longitudinal` pipeline](../Pipelines/T1_FreeSurfer_Longitudinal.md) will compute the intra-subject template `sub-CLNC01_long-M000+M018` using the `M000` and `M018` sessions. +This template is finally used to longitudinally correct the segmentations, whose results are stored in the `sub-CLNC01_ses-M000.long.sub-CLNC01_long-M000+M018` and `sub-CLNC01_ses-M018.long.sub-CLNC01_long-M000+M018` folders. Of note, the `.long.` naming comes from [FreeSurfer](../Software/Third-party.md#freesurfer) when running the longitudinal `recon-all` command. @@ -132,10 +132,10 @@ Of note, the `.long.` naming comes from [FreeSurfer] dataset_description.json subjects/ ├── sub-CLNC01/ -│ ├── long-M000M018/ -│ │ ├── long-M000M018_sessions.tsv +│ ├── long-M000+M018/ +│ │ ├── long-M000+M018_sessions.tsv │ │ └── freesurfer_unbiased_template/ -│ │ └── sub-CLNC01_long-M000M018/ +│ │ └── sub-CLNC01_long-M000+M018/ │ │ ├── base-tps/ │ │ ├── label/ │ │ ├── mri/ @@ -150,10 +150,10 @@ subjects/ │ │ │ ├── mri/ │ │ │ ├── stats/ │ │ │ └── surf/ -│ │ └── long-M000M018/ +│ │ └── long-M000+M018/ │ │ └── freesurfer_longitudinal/ │ │ ├── regional_measures/ -│ │ └── sub-CLNC01_ses-M000.long.sub-CLNC01_long-M000M018/ +│ │ └── sub-CLNC01_ses-M000.long.sub-CLNC01_long-M000+M018/ │ │ ├── label/ │ │ ├── mri/ │ │ ├── stats/ @@ -167,10 +167,10 @@ subjects/ │ │ ├── mri/ │ │ ├── stats/ │ │ └── surf/ -│ └── long-M000M018 +│ └── long-M000+M018 │ └── freesurfer_longitudinal │ ├── regional_measures -│ └── sub-CLNC01_ses-M018.long.sub-CLNC01_long-M000M018/ +│ └── sub-CLNC01_ses-M018.long.sub-CLNC01_long-M000+M018/ │ ├── label/ │ ├── mri/ │ ├── stats/ diff --git a/docs/Pipelines/T1_FreeSurfer_Longitudinal.md b/docs/Pipelines/T1_FreeSurfer_Longitudinal.md index 2ee45a617..19859254c 100644 --- a/docs/Pipelines/T1_FreeSurfer_Longitudinal.md +++ b/docs/Pipelines/T1_FreeSurfer_Longitudinal.md @@ -106,7 +106,7 @@ Results stored in the following folder of the [CAPS hierarchy](../../CAPS/Specifications/#t1-freesurfer-longitudinal-freesurfer-based-longitudinal-processing-of-t1-weighted-mr-images): `subjects///freesurfer_unbiased_template/_`. -`` is an identifier defined by concatenating all the sessions associated with the current `` (e.g. if the template for participant `sub-CLNC01` is built from sessions `M00`, `M01`, `M05`, then `` will be `M00M01M05`). +`` is an identifier defined by concatenating all the sessions associated with the current `` (e.g. if the template for participant `sub-CLNC01` is built from sessions `M00`, `M01`, `M05`, then `` will be `M00+M01+M05`). See [CAPS specifications](../../CAPS/Introduction/#subject-and-group-naming) for full definition and example of ``. This folder contains the standard output structure of the `recon-all` command (`label/`, `mri/`, `surf/`, etc.) already explained in the [`t1-freesurfer`](../T1_FreeSurfer) pipeline. diff --git a/test/unittests/utils/test_longitudinal.py b/test/unittests/utils/test_longitudinal.py index 5d39e9706..41dca392e 100644 --- a/test/unittests/utils/test_longitudinal.py +++ b/test/unittests/utils/test_longitudinal.py @@ -5,9 +5,9 @@ "session_ids,expected", [ (["ses-M000"], "long-M000"), - (["ses-M000", "ses-M018", "ses-M036"], "long-M000M018M036"), - (["ses-M018", "ses-M036", "ses-M000"], "long-M000M018M036"), - (["ses-foo", "ses-bar", "ses-baz", "ses-foobar"], "long-barbazfoofoobar"), + (["ses-M000", "ses-M018", "ses-M036"], "long-M000+M018+M036"), + (["ses-M018", "ses-M036", "ses-M000"], "long-M000+M018+M036"), + (["ses-foo", "ses-bar", "ses-baz", "ses-foobar"], "long-bar+baz+foo+foobar"), ], ) def test_get_long_id(session_ids, expected): @@ -40,7 +40,7 @@ def test_get_long_id_errors(session_ids): ( ["sub-CLNC01", "sub-CLNC01", "sub-CLNC02"], ["ses-M000", "ses-M018", "ses-M000"], - ["long-M000M018", "long-M000M018", "long-M000"], + ["long-M000+M018", "long-M000+M018", "long-M000"], ) ], ) @@ -64,8 +64,8 @@ def test_save_long_id(tmp_path): save_long_id(["ses-M000", "ses-M018", "ses-M036"], output_dir) - assert (output_dir / "long-M000M018M036_sessions.tsv").exists() - saved_ids = (output_dir / "long-M000M018M036_sessions.tsv").read_text() + assert (output_dir / "long-M000+M018+M036_sessions.tsv").exists() + saved_ids = (output_dir / "long-M000+M018+M036_sessions.tsv").read_text() assert saved_ids == "session_id\nses-M000\nses-M018\nses-M036\n"