From c5e9bbb1a6cd4c5d21421b90b9c14451e8c8d698 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 29 Jan 2026 16:38:51 +0100 Subject: [PATCH 1/8] update: bump junifer-data version to v6 --- junifer/data/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/junifer/data/utils.py b/junifer/data/utils.py index 0446fd29b..1a1357ba3 100644 --- a/junifer/data/utils.py +++ b/junifer/data/utils.py @@ -24,10 +24,10 @@ __all__ = [ # junifer-data version constant -JUNIFER_DATA_VERSION = "5" +JUNIFER_DATA_VERSION = "6" # junifer-data hexsha constant -JUNIFER_DATA_HEXSHA = "62a0e3d187259a3c3ba7be638ee39cfb40df0a61" +JUNIFER_DATA_HEXSHA = "5a371a9abbfa11191513ada8aedbc1dbc2539f61" JUNIFER_DATA_PARAMS = { "tag": JUNIFER_DATA_VERSION, -- 2.52.0 From ff4ca6bda4035793a3bb31c44b2a5eaddd63e033 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 29 Jan 2026 16:41:19 +0100 Subject: [PATCH 2/8] ci: use junifer-data/v6 for workflows --- .github/workflows/ci.yml | 4 ++-- .github/workflows/docs-preview.yml | 4 ++-- .github/workflows/docs.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81cf0e23a..01fb4bf99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,8 +54,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v4 - cp -ar /root/junifer_data/v4/. $HOME/junifer_data/v4/ + mkdir -p $HOME/junifer_data/v6 + cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ - name: Checkout repository uses: actions/checkout@v5 with: diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index d79c1af51..f326c1efe 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -40,8 +40,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v4 - cp -ar /root/junifer_data/v4/. $HOME/junifer_data/v4/ + mkdir -p $HOME/junifer_data/v6 + cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ - name: Checkout repository uses: actions/checkout@v5 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 555d1321b..5e819d7e0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,8 +31,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v4 - cp -ar /root/junifer_data/v4/. $HOME/junifer_data/v4/ + mkdir -p $HOME/junifer_data/v6 + cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ - name: Checkout repository uses: actions/checkout@v5 with: -- 2.52.0 From 9b346bb3d590c782addc872084d660c7333e2862 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 17:13:11 +0100 Subject: [PATCH 3/8] update: bump junifer-data version to v7 --- junifer/data/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/junifer/data/utils.py b/junifer/data/utils.py index 1a1357ba3..f31c1f570 100644 --- a/junifer/data/utils.py +++ b/junifer/data/utils.py @@ -24,10 +24,10 @@ __all__ = [ # junifer-data version constant -JUNIFER_DATA_VERSION = "6" +JUNIFER_DATA_VERSION = "7" # junifer-data hexsha constant -JUNIFER_DATA_HEXSHA = "5a371a9abbfa11191513ada8aedbc1dbc2539f61" +JUNIFER_DATA_HEXSHA = "f5144e6fef7d6f4f26508c3653d94a622f10207c" JUNIFER_DATA_PARAMS = { "tag": JUNIFER_DATA_VERSION, -- 2.52.0 From 5fdb25cf576b4264f36146dfc563900dd23ceb4b Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 17:14:56 +0100 Subject: [PATCH 4/8] ci: use junifer-data/v7 for workflows --- .github/workflows/ci.yml | 4 ++-- .github/workflows/docs-preview.yml | 4 ++-- .github/workflows/docs.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01fb4bf99..e01bcd93f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,8 +54,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v6 - cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ + mkdir -p $HOME/junifer_data/v7 + cp -ar /root/junifer_data/v7/. $HOME/junifer_data/v7/ - name: Checkout repository uses: actions/checkout@v5 with: diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index f326c1efe..42b278f7d 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -40,8 +40,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v6 - cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ + mkdir -p $HOME/junifer_data/v7 + cp -ar /root/junifer_data/v7/. $HOME/junifer_data/v7/ - name: Checkout repository uses: actions/checkout@v5 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5e819d7e0..62a92d793 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,8 +31,8 @@ jobs: jq - name: Copy junifer-data directory run: | - mkdir -p $HOME/junifer_data/v6 - cp -ar /root/junifer_data/v6/. $HOME/junifer_data/v6/ + mkdir -p $HOME/junifer_data/v7 + cp -ar /root/junifer_data/v7/. $HOME/junifer_data/v7/ - name: Checkout repository uses: actions/checkout@v5 with: -- 2.52.0 From 45a2d4c33ee791d3cc5f1bd190fa4dedd8e717db Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 18:16:52 +0100 Subject: [PATCH 5/8] feat: add Glasser to ParcellationRegistry --- junifer/data/parcellations/_parcellations.py | 58 +++++++++++++++++++ .../parcellations/tests/test_parcellations.py | 21 +++++++ 2 files changed, 79 insertions(+) diff --git a/junifer/data/parcellations/_parcellations.py b/junifer/data/parcellations/_parcellations.py index 04160b822..b2b55d8be 100644 --- a/junifer/data/parcellations/_parcellations.py +++ b/junifer/data/parcellations/_parcellations.py @@ -199,6 +199,15 @@ class ParcellationRegistry(BasePipelineDataRegistry): } } ) + # Add Glasser + self._builtin.update( + { + "Glasser": { + "family": "Glasser", + "space": "MNI152NLin2009cAsym", + } + } + ) # Update registry with built-in ones self._registry.update(self._builtin) @@ -365,6 +374,7 @@ class ParcellationRegistry(BasePipelineDataRegistry): "Yan2023", "Brainnetome", "FreeSurfer", + "Glasser", ]: # Load parcellation and labels if t_family == "Schaefer2018": @@ -408,6 +418,10 @@ class ParcellationRegistry(BasePipelineDataRegistry): parcellation_fname, parcellation_labels = _retrieve_aseg( resolution=resolution, ) + elif t_family == "Glasser": + parcellation_fname, parcellation_labels = _retrieve_glasser( + resolution=resolution, + ) else: raise_error(f"Unknown parcellation family: {t_family}") @@ -1421,6 +1435,50 @@ def _retrieve_aseg( return parcellation_img_path, labels +def _retrieve_glasser( + resolution: Optional[float] = None, +) -> tuple[Path, list[str]]: + """Retrieve Glasser v1.0 . + + Parameters + ---------- + resolution : 1.0, optional + The desired resolution of the parcellation to load. If it is not + available, the closest resolution will be loaded. Preferably, use a + resolution higher than the desired one. By default, will load the + highest one (default None). Available resolution for this + parcellation is 1mm. + + Returns + ------- + pathlib.Path + File path to the parcellation image. + list of str + Parcellation labels. + + """ + logger.info("Parcellation parameters:") + logger.info(f"\tresolution: {resolution}") + + _valid_resolutions = [1.0] + _ = closest_resolution(resolution, _valid_resolutions) + + path_prefix = Path("parcellations/Glasser/2021") + parcellation_img_path = get( + file_path=path_prefix / "MNI_Glasser_HCP_v1.0.nii.gz", + dataset_path=get_dataset_path(), + **JUNIFER_DATA_PARAMS, + ) + parcellation_label_path = get( + file_path=path_prefix / "labels.csv", + dataset_path=get_dataset_path(), + **JUNIFER_DATA_PARAMS, + ) + labels = pd.read_csv(parcellation_label_path, sep=",")["label"].to_list() + + return parcellation_img_path, labels + + def merge_parcellations( parcellations_list: list["Nifti1Image"], parcellations_names: list[str], diff --git a/junifer/data/parcellations/tests/test_parcellations.py b/junifer/data/parcellations/tests/test_parcellations.py index 7fda6784e..a8e16a8cb 100644 --- a/junifer/data/parcellations/tests/test_parcellations.py +++ b/junifer/data/parcellations/tests/test_parcellations.py @@ -962,6 +962,27 @@ def test_aseg() -> None: ) +def test_glasser() -> None: + """Test Glasser parcellation.""" + parcellations = list_data(kind="parcellation") + assert "Glasser" in parcellations + + # Load parcellation + img, label, img_path, space = load_data( + kind="parcellation", + name="Glasser", + target_space="MNI152NLin2009cAsym", + ) + assert img is not None + assert img_path.name == "MNI_Glasser_HCP_v1.0.nii.gz" + assert space == "MNI152NLin2009cAsym" + assert len(label) == 360 + assert_array_equal( + img.header["pixdim"][1:4], + 3 * [1], + ) + + def test_merge_parcellations() -> None: """Test merging parcellations.""" # load some parcellations for testing -- 2.52.0 From 2f14b8c2e9129522463e7e72739ca7b8aff22ef6 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 18:18:25 +0100 Subject: [PATCH 6/8] feat: add Julich-Brain to ParcellationRegistry --- junifer/data/parcellations/_parcellations.py | 80 +++++++++++++++++++ .../parcellations/tests/test_parcellations.py | 49 ++++++++++++ 2 files changed, 129 insertions(+) diff --git a/junifer/data/parcellations/_parcellations.py b/junifer/data/parcellations/_parcellations.py index b2b55d8be..24352eb4e 100644 --- a/junifer/data/parcellations/_parcellations.py +++ b/junifer/data/parcellations/_parcellations.py @@ -208,6 +208,17 @@ class ParcellationRegistry(BasePipelineDataRegistry): } } ) + # Add Julich-Brain + for v in ["V1_18", "V2_9", "V3_0_3", "V3_1"]: + self._builtin.update( + { + f"Julich-Brain_{v}": { + "family": "Julich-Brain", + "version": v, + "space": "MNI152NLin2009cAsym", + } + } + ) # Update registry with built-in ones self._registry.update(self._builtin) @@ -375,6 +386,7 @@ class ParcellationRegistry(BasePipelineDataRegistry): "Brainnetome", "FreeSurfer", "Glasser", + "Julich-Brain", ]: # Load parcellation and labels if t_family == "Schaefer2018": @@ -422,6 +434,13 @@ class ParcellationRegistry(BasePipelineDataRegistry): parcellation_fname, parcellation_labels = _retrieve_glasser( resolution=resolution, ) + elif t_family == "Julich-Brain": + parcellation_fname, parcellation_labels = ( + _retrieve_julich_brain( + resolution=resolution, + **parcellation_definition, + ) + ) else: raise_error(f"Unknown parcellation family: {t_family}") @@ -1479,6 +1498,67 @@ def _retrieve_glasser( return parcellation_img_path, labels +def _retrieve_julich_brain( + resolution: Optional[float] = None, + version: str = "v3_1", +) -> tuple[Path, list[str]]: + """Retrieve Julich-Brain labelled parcellations. + + Parameters + ---------- + resolution : 1.0, optional + The desired resolution of the parcellation to load. If it is not + available, the closest resolution will be loaded. Preferably, use a + resolution higher than the desired one. By default, will load the + highest one (default None). Available resolution for this + parcellation is 1mm. + version : {"V1_18", "V2_9", "V3_0_3", "V3_1"}, optional + The version of the parcellation to use (default "V3_1"). + + Returns + ------- + pathlib.Path + File path to the parcellation image. + list of str + Parcellation labels. + + Raises + ------ + ValueError + If invalid value is provided for ``version``. + + """ + logger.info("Parcellation parameters:") + logger.info(f"\tresolution: {resolution}") + logger.info(f"\tversion: {version}") + + # Check version + _valid_version = ["V1_18", "V2_9", "V3_0_3", "V3_1"] + if version not in _valid_version: + raise_error( + f"The parameter `version` ({version}) needs to be one of the " + f"following: {_valid_version}" + ) + + _valid_resolutions = [1.0] + _ = closest_resolution(resolution, _valid_resolutions) + + path_prefix = Path(f"parcellations/Julich-Brain/{version}") + parcellation_img_path = get( + file_path=path_prefix / "labelled.nii.gz", + dataset_path=get_dataset_path(), + **JUNIFER_DATA_PARAMS, + ) + parcellation_label_path = get( + file_path=path_prefix / "labels.csv", + dataset_path=get_dataset_path(), + **JUNIFER_DATA_PARAMS, + ) + labels = pd.read_csv(parcellation_label_path, sep=",")["label"].to_list() + + return parcellation_img_path, labels + + def merge_parcellations( parcellations_list: list["Nifti1Image"], parcellations_names: list[str], diff --git a/junifer/data/parcellations/tests/test_parcellations.py b/junifer/data/parcellations/tests/test_parcellations.py index a8e16a8cb..99fbf8fb2 100644 --- a/junifer/data/parcellations/tests/test_parcellations.py +++ b/junifer/data/parcellations/tests/test_parcellations.py @@ -23,6 +23,7 @@ from junifer.data.parcellations import merge_parcellations from junifer.data.parcellations._parcellations import ( _retrieve_aicha, _retrieve_brainnetome, + _retrieve_julich_brain, _retrieve_schaefer, _retrieve_shen, _retrieve_suit, @@ -983,6 +984,54 @@ def test_glasser() -> None: ) +@pytest.mark.parametrize( + "version, rois", + [ + ("V1_18", 202), + ("V2_9", 294), + ("V3_0_3", 314), + ("V3_1", 414), + ], +) +def test_julich_brain(version: str, rois: int) -> None: + """Test Julich-Brain parcellation. + + Parameters + ---------- + version : str + The parametrized version. + rois : int + The parametrized # of ROIs. + + """ + parcellations = list_data(kind="parcellation") + n = f"Julich-Brain_{version}" + assert n in parcellations + + # Load parcellation + img, label, img_path, space = load_data( + kind="parcellation", + name=n, + target_space="MNI152NLin2009cAsym", + ) + assert img is not None + assert img_path.name == "labelled.nii.gz" + assert space == "MNI152NLin2009cAsym" + assert len(label) == rois + assert_array_equal( + img.header["pixdim"][1:4], + 3 * [1], + ) + + +def test_retrieve_julich_brain_incorrect_version() -> None: + """Test retrieve Julich-Brain with incorrect version.""" + with pytest.raises(ValueError, match=r"The parameter `version`"): + _retrieve_julich_brain( + version="v0", + ) + + def test_merge_parcellations() -> None: """Test merging parcellations.""" # load some parcellations for testing -- 2.52.0 From 94abfc6572a46f4aea79a16a3c93424d845ce647 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 18:27:09 +0100 Subject: [PATCH 7/8] docs: update parcellation list in builtin.rst --- docs/builtin.rst | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/builtin.rst b/docs/builtin.rst index 10683255c..9935470c2 100644 --- a/docs/builtin.rst +++ b/docs/builtin.rst @@ -463,6 +463,24 @@ Available | structures in the human brain. | Neuron., Volume 33(3), Pages 341-355 (2002). | https://doi:10.1016/s0896-6273(02)00569-x + * - Glasser + - None + - ``Glasser`` + - ``MNI152NLin2009cAsym`` + - 0.0.7 + - | Glasser, M.F., Coalson, T.S., Robinson, E.C. et al. + | A multi-modal parcellation of human cerebral cortex. + | Nature (2016). + | http://doi.org/10.1038/nature18933 + * - Julich-Brain + - ``version`` + - ``Julich-Brain_V1_18``, ``Julich-Brain_V2_9``, ``Julich-Brain_V3_0_3``, ``Julich-Brain_V3_1`` + - ``MNI152NLin2009cAsym`` + - 0.0.7 + - | Amunts, K. et al. + | Julich-Brain: A 3D probabilistic atlas of the human brain’s cytoarchitecture. + | Science, 369, 988-992 (2020) + | https://doi.org/10.1126/science.abb4588 Planned @@ -480,11 +498,6 @@ Planned | on MRI scans into gyral based regions of interest. | NeuroImage, Volume 31(3), Pages 968-980 (2006). | http://doi.org/10.1016/j.neuroimage.2006.01.021 - * - Glasser - - | Glasser, M.F., Coalson, T.S., Robinson, E.C. et al. - | A multi-modal parcellation of human cerebral cortex. - | Nature (2016). - | http://doi.org/10.1038/nature18933 * - AAL - | Rolls, E.T., Huang, C.C., Lin, C.P., et al. | Automated anatomical labelling atlas 3. -- 2.52.0 From 5bd239f313bcb80054033487915c37c4e19df974 Mon Sep 17 00:00:00 2001 From: Synchon Mandal Date: Thu, 5 Feb 2026 18:30:27 +0100 Subject: [PATCH 8/8] chore: add changelogs 486.{enh,misc} --- docs/changes/newsfragments/486.enh | 1 + docs/changes/newsfragments/486.misc | 1 + 2 files changed, 2 insertions(+) create mode 100644 docs/changes/newsfragments/486.enh create mode 100644 docs/changes/newsfragments/486.misc diff --git a/docs/changes/newsfragments/486.enh b/docs/changes/newsfragments/486.enh new file mode 100644 index 000000000..a6c3344b2 --- /dev/null +++ b/docs/changes/newsfragments/486.enh @@ -0,0 +1 @@ +Add Glasser and Julich-Brain parcellations to :class:`.ParcellationRegistry` by `Synchon Mandal`_ diff --git a/docs/changes/newsfragments/486.misc b/docs/changes/newsfragments/486.misc new file mode 100644 index 000000000..6f701d9c3 --- /dev/null +++ b/docs/changes/newsfragments/486.misc @@ -0,0 +1 @@ +Bump ``junifer-data`` to ``v7`` by `Synchon Mandal`_ -- 2.52.0