Skip to content

Commit

Permalink
[CONV] Implementing new converter for IXI dataset (#1239)
Browse files Browse the repository at this point in the history
* wip1

* wip2

* add todos

* wip2

* add dti

* Fix concat img

* print1

* Sessions/scans.tsv

* Testing1

* Function description

* Testing2

* fix

* Add docstrings

* Changes as suggested

* add func subj_from_file

* Add test func

* Add participants.tsv

* Fix prints

* cleanup

* unittesting1

* Changes according to suggestions

* unittesting2

* unittesting 3

* add mapping class

* more unittesting

* Suggestions + testing

* Finish unit tests

* Add test for check_modalities

* Changes according to suggestions

* Change check_mod

* use ClinicalDataMapping enum
  • Loading branch information
AliceJoubert authored Aug 30, 2024
1 parent 06ec7a1 commit 5d022e1
Show file tree
Hide file tree
Showing 10 changed files with 1,265 additions and 1 deletion.
28 changes: 28 additions & 0 deletions clinica/iotools/bids_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class StudyName(str, Enum):
OASIS = "OASIS"
OASIS3 = "OASIS3"
UKB = "UKB"
IXI = "IXI"


BIDS_VALIDATOR_CONFIG = {
Expand Down Expand Up @@ -90,6 +91,8 @@ def bids_id_factory(study: StudyName) -> Type[BIDSSubjectID]:
return OASIS3BIDSSubjectID
if study == StudyName.HABS:
return HABSBIDSSubjectID
if study == StudyName.IXI:
return IXIBIDSSubjectID


class ADNIBIDSSubjectID(BIDSSubjectID):
Expand Down Expand Up @@ -292,6 +295,31 @@ def to_original_study_id(self) -> str:
return str(self.replace("sub-HABS", "P_"))


class IXIBIDSSubjectID(BIDSSubjectID):
"""Implementation for IXI of the BIDSSubjectIDClass, allowing to go from the source id IXI###
to a bids id sub-IXI### and reciprocally."""

def validate(self, value: str) -> str:
if re.fullmatch(r"sub-IXI\d{3}", value):
return value
raise ValueError(
f"BIDS IXI subject ID {value} is not properly formatted. "
"Expecting a 'sub-IXIXXX' format."
)

@classmethod
def from_original_study_id(cls, study_id: str) -> str:
if re.fullmatch(r"IXI\d{3}", study_id):
return f"sub-{study_id}"
raise ValueError(
f"Raw IXI subject ID {study_id} is not properly formatted. "
"Expecting a 'Y' format."
)

def to_original_study_id(self) -> str:
return str(self.replace("sub-", ""))


# -- Methods for the clinical data --
def create_participants_df(
study_name: StudyName,
Expand Down
1 change: 0 additions & 1 deletion clinica/iotools/converters/adni_to_bids/adni_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def _define_subjects_list(
source_dir: Path,
subjs_list_path: Optional[Path] = None,
) -> List[str]:
# todo : here or in utils for all converters ?
import re

from clinica.utils.stream import cprint
Expand Down
2 changes: 2 additions & 0 deletions clinica/iotools/converters/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .aibl_to_bids import aibl_to_bids_cli
from .genfi_to_bids import genfi_to_bids_cli
from .habs_to_bids import habs_to_bids_cli
from .ixi_to_bids import ixi_to_bids_cli
from .nifd_to_bids import nifd_to_bids_cli
from .oasis3_to_bids import oasis3_to_bids_cli
from .oasis_to_bids import oasis_to_bids_cli
Expand All @@ -24,6 +25,7 @@ def cli() -> None:
cli.add_command(oasis3_to_bids_cli.cli)
cli.add_command(ukb_to_bids_cli.cli)
cli.add_command(genfi_to_bids_cli.cli)
cli.add_command(ixi_to_bids_cli.cli)

if __name__ == "__main__":
cli()
4 changes: 4 additions & 0 deletions clinica/iotools/converters/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ def get_converter_name(study: Union[str, StudyName]) -> str:
return "Oasis3ToBids"
if study == StudyName.UKB:
return "UkbToBids"
if study == StudyName.IXI:
return "IxiToBids"


def converter_factory(study: Union[str, StudyName]) -> Callable:
Expand All @@ -58,4 +60,6 @@ def converter_factory(study: Union[str, StudyName]) -> Callable:
from .oasis3_to_bids import convert
if study == StudyName.UKB:
from .ukb_to_bids import convert
if study == StudyName.IXI:
from .ixi_to_bids import convert
return convert
3 changes: 3 additions & 0 deletions clinica/iotools/converters/ixi_to_bids/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .ixi_to_bids import convert

__all__ = ["convert"]
82 changes: 82 additions & 0 deletions clinica/iotools/converters/ixi_to_bids/ixi_to_bids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""Convert IXI dataset (https://brain-development.org/ixi-dataset/) to BIDS."""

from pathlib import Path
from typing import Optional

import nibabel as nb
import numpy as np

from clinica.iotools.bids_utils import write_modality_agnostic_files
from clinica.iotools.converters.ixi_to_bids.ixi_to_bids_utils import (
check_modalities,
define_participants,
read_clinical_data,
write_participants,
write_scans,
write_sessions,
write_subject_data,
)
from clinica.utils.filemanip import UserProvidedPath

__all__ = ["convert"]


def convert(
path_to_dataset: UserProvidedPath,
bids_dir: UserProvidedPath,
path_to_clinical: UserProvidedPath,
subjects: Optional[UserProvidedPath] = None,
n_procs: Optional[int] = 1,
**kwargs,
):
from clinica.iotools.bids_utils import StudyName
from clinica.iotools.converters.factory import get_converter_name
from clinica.utils.stream import cprint

from ..utils import validate_input_path

path_to_dataset = validate_input_path(path_to_dataset)
bids_dir = validate_input_path(bids_dir, check_exist=False)
path_to_clinical = validate_input_path(path_to_clinical)
if subjects:
subjects = validate_input_path(subjects)

if n_procs != 1:
cprint(
f"{get_converter_name(StudyName.IXI)} converter does not support multiprocessing yet. n_procs set to 1.",
lvl="warning",
)

clinical_data = read_clinical_data(path_to_clinical)
participants = define_participants(path_to_dataset, subjects)
check_modalities(data_directory=path_to_dataset, participants=participants)

write_participants(
bids_dir=bids_dir, clinical_data=clinical_data, participants=participants
)

for participant in participants:
cprint(f"Converting IXI subject {participant} to BIDS", lvl="debug")
write_subject_data(
bids_dir=bids_dir, participant=participant, path_to_dataset=path_to_dataset
)
write_sessions(
bids_dir=bids_dir, participant=participant, clinical_data=clinical_data
)
write_scans(bids_dir=bids_dir, participant=participant)

readme_data = {
"link": "https://brain-development.org/ixi-dataset/",
"desc": (
"IXI is the nickname for the Information eXtraction from Images project, "
"which issued a dataset of nearly 600 images from healthy subjects. The MR"
"acquisition protocol includes T1,T2, PD weighted, MRA and diffusion-weighted"
"images. Three hospitals in London were involved in data collection."
),
}

write_modality_agnostic_files(
study_name=StudyName.IXI, readme_data=readme_data, bids_dir=bids_dir
)

cprint("Conversion to BIDS finished.", lvl="info")
27 changes: 27 additions & 0 deletions clinica/iotools/converters/ixi_to_bids/ixi_to_bids_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from os import PathLike
from typing import Optional

import click

from clinica.iotools.converters import cli_param


@click.command(name="ixi-to-bids")
@cli_param.dataset_directory
@cli_param.bids_directory
@cli_param.clinical_data_directory
@cli_param.subjects_list
def cli(
dataset_directory: PathLike,
bids_directory: PathLike,
clinical_data_directory: PathLike,
subjects_list: Optional[PathLike] = None,
) -> None:
"""IXI to BIDS converter."""
from .ixi_to_bids import convert

convert(dataset_directory, bids_directory, clinical_data_directory, subjects_list)


if __name__ == "__main__":
cli()
Loading

0 comments on commit 5d022e1

Please sign in to comment.