Skip to content

Commit ae5be2c

Browse files
committed
🔀 Merge branch 'mri_robust_template' into fix/longitudinal-configuration-subject_id
2 parents dba4765 + dc4c2df commit ae5be2c

12 files changed

Lines changed: 279 additions & 234 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2424
- Required positional parameter "wf" in input and output of `ingress_pipeconfig_paths` function, where a node to reorient templates is added to the `wf`.
2525
- Required positional parameter "orientation" to `resolve_resolution`.
2626
- Optional positional argument "cfg" to `create_lesion_preproc`.
27+
- `mri_robust_template` for longitudinal template generation.
28+
- `max_iter` parameter for longitudinal template generation.
2729

2830
### Changed
2931

3032
- Moved `pygraphviz` from requirements to `graphviz` optional dependencies group.
3133
- Automatically tag untagged `subject_id` and `unique_id` as `!!str` when loading data config files.
3234
- Made orientation configurable (was hard-coded as "RPI").
3335
- Disabled variant image builds.
36+
- Made `mri_robust_template` default implementation for longitudinal template generation.
3437

3538
### Fixed
3639

CPAC/longitudinal/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
from CPAC.utils.docs import DOCS_URL_PREFIX
2020

21+
assert isinstance(__doc__, str)
2122
__doc__ += f"""
2223
2324
See {DOCS_URL_PREFIX}/user/longitudinal

CPAC/longitudinal/preproc.py

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
from collections import Counter
2121
from multiprocessing.dummy import Pool as ThreadPool
22+
from multiprocessing.pool import Pool
2223
import os
24+
from typing import Literal, Optional
2325

2426
import numpy as np
2527
import nibabel as nib
@@ -131,27 +133,23 @@ def norm_transformation(input_mat):
131133

132134

133135
def template_convergence(
134-
mat_file, mat_type="matrix", convergence_threshold=np.finfo(np.float64).eps
135-
):
136+
mat_file: str,
137+
mat_type: Literal["matrix", "ITK"] = "matrix",
138+
convergence_threshold: float | np.float64 = np.finfo(np.float64).eps,
139+
) -> bool:
136140
"""Check that the deistance between matrices is smaller than the threshold.
137141
138142
Calculate the distance between transformation matrix with a matrix of no transformation.
139143
140144
Parameters
141145
----------
142-
mat_file : str
146+
mat_file
143147
path to an fsl flirt matrix
144-
mat_type : str
145-
'matrix'(default), 'ITK'
148+
mat_type
146149
The type of matrix used to represent the transformations
147-
convergence_threshold : float
148-
(numpy.finfo(np.float64).eps (default)) threshold for the convergence
150+
convergence_threshold
149151
The threshold is how different from no transformation is the
150152
transformation matrix.
151-
152-
Returns
153-
-------
154-
bool
155153
"""
156154
if mat_type == "matrix":
157155
translation, oth_transform = read_mat(mat_file)
@@ -347,50 +345,51 @@ def flirt_node(in_img, output_img, output_mat):
347345

348346

349347
def template_creation_flirt(
350-
input_brain_list,
351-
input_skull_list,
352-
init_reg=None,
353-
avg_method="median",
354-
dof=12,
355-
interp="trilinear",
356-
cost="corratio",
357-
mat_type="matrix",
358-
convergence_threshold=-1,
359-
thread_pool=2,
360-
unique_id_list=None,
361-
):
348+
input_brain_list: list[str],
349+
input_skull_list: list[str],
350+
init_reg: Optional[list[pe.Node]] = None,
351+
avg_method: Literal["median", "mean", "std"] = "median",
352+
dof: Literal[12, 9, 7, 6] = 12,
353+
interp: Literal["trilinear", "nearestneighbour", "sinc", "spline"] = "trilinear",
354+
cost: Literal[
355+
"corratio", "mutualinfo", "normmi", "normcorr", "leastsq", "labeldiff", "bbr"
356+
] = "corratio",
357+
mat_type: Literal["matrix", "ITK"] = "matrix",
358+
convergence_threshold: float | np.float64 = -1,
359+
max_iter: int = 5,
360+
thread_pool: int | Pool = 2,
361+
unique_id_list: Optional[list[str]] = None,
362+
) -> tuple[str, str, list[str], list[str], list[str]]:
362363
"""Create a temporary template from a list of images.
363364
364365
Parameters
365366
----------
366-
input_brain_list : list of str
367+
input_brain_list
367368
list of brain images paths
368-
input_skull_list : list of str
369+
input_skull_list
369370
list of skull images paths
370-
init_reg : list of Node
371+
init_reg
371372
(default None so no initial registration performed)
372373
the output of the function register_img_list with another reference
373374
Reuter et al. 2012 (NeuroImage) section "Improved template estimation"
374375
doi:10.1016/j.neuroimage.2012.02.084 uses a ramdomly
375376
selected image from the input dataset
376-
avg_method : str
377-
function names from numpy library such as 'median', 'mean', 'std' ...
378-
dof : integer (int of long)
379-
number of transform degrees of freedom (FLIRT) (12 by default)
380-
interp : str
381-
('trilinear' (default) or 'nearestneighbour' or 'sinc' or 'spline')
377+
avg_method
378+
function names from numpy library
379+
dof
380+
number of transform degrees of freedom (FLIRT)
381+
interp
382382
final interpolation method used in reslicing
383-
cost : str
384-
('mutualinfo' or 'corratio' (default) or 'normcorr' or 'normmi' or
385-
'leastsq' or 'labeldiff' or 'bbr')
383+
cost
386384
cost function
387-
mat_type : str
388-
'matrix'(default), 'ITK'
385+
mat_type
389386
The type of matrix used to represent the transformations
390-
convergence_threshold : float
387+
convergence_threshold
391388
(numpy.finfo(np.float64).eps (default)) threshold for the convergence
392389
The threshold is how different from no transformation is the
393390
transformation matrix.
391+
max_iter
392+
Maximum number of iterations if transformation does not converge
394393
thread_pool : int or multiprocessing.dummy.Pool
395394
(default 2) number of threads. You can also provide a Pool so the
396395
node will be added to it to be run.
@@ -496,7 +495,14 @@ def template_creation_flirt(
496495
and the loop stops when this temporary template is close enough (with a transformation
497496
distance smaller than the threshold) to all the images of the precedent iteration.
498497
"""
499-
while not converged:
498+
iterator = 1
499+
iteration = 0
500+
if max_iter == -1:
501+
# make iteration < max_iter always True
502+
iterator = 0
503+
iteration = -2
504+
while not converged and iteration < max_iter:
505+
iteration += iterator
500506
temporary_brain_template, temporary_skull_template = create_temporary_template(
501507
input_brain_list=output_brain_list,
502508
input_skull_list=output_skull_list,
@@ -628,6 +634,7 @@ def subject_specific_template(
628634
"cost",
629635
"mat_type",
630636
"convergence_threshold",
637+
"max_iter",
631638
"thread_pool",
632639
"unique_id_list",
633640
],
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (C) 2024 C-PAC Developers
3+
4+
# This file is part of C-PAC.
5+
6+
# C-PAC is free software: you can redistribute it and/or modify it under
7+
# the terms of the GNU Lesser General Public License as published by the
8+
# Free Software Foundation, either version 3 of the License, or (at your
9+
# option) any later version.
10+
11+
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
12+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14+
# License for more details.
15+
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
18+
"""Create longitudinal template using ``mri_robust_template``."""

CPAC/longitudinal/wf/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Copyright (C) 2024 C-PAC Developers
2+
3+
# This file is part of C-PAC.
4+
5+
# C-PAC is free software: you can redistribute it and/or modify it under
6+
# the terms of the GNU Lesser General Public License as published by the
7+
# Free Software Foundation, either version 3 of the License, or (at your
8+
# option) any later version.
9+
10+
# C-PAC is distributed in the hope that it will be useful, but WITHOUT
11+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13+
# License for more details.
14+
15+
# You should have received a copy of the GNU Lesser General Public
16+
# License along with C-PAC. If not, see <https://www.gnu.org/licenses/>.
17+
"""Workflows for longitudinal preprocessing."""
18+
19+
from CPAC.utils.docs import DOCS_URL_PREFIX
20+
21+
assert isinstance(__doc__, str)
22+
__doc__ += f"""
23+
24+
See {DOCS_URL_PREFIX}/user/longitudinal
25+
""" # noqa: A001

0 commit comments

Comments
 (0)