From 12cdddcbcfe511597895d737538496642edfbe4b Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Tue, 26 May 2026 13:53:43 +0200 Subject: [PATCH 1/4] autodetect license or quit in long_prepare_template --- recon_surf/long_prepare_template.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/recon_surf/long_prepare_template.sh b/recon_surf/long_prepare_template.sh index a9f5da8f5..d0cd75f10 100755 --- a/recon_surf/long_prepare_template.sh +++ b/recon_surf/long_prepare_template.sh @@ -226,6 +226,8 @@ fi # check that SUBJECTS_DIR exists check_create_subjects_dir_properties "$SUBJECTS_DIR" +auto_detect_fs_license "the longitudinal template preparation" || exit $? + ################################## SETUP and LOGFILE ############################## @@ -456,4 +458,3 @@ do RunIt "$cmd" "$LF" done - From 9c5a685bfaa147945341d58eb0c0f2189cfcc388 Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Tue, 26 May 2026 14:22:53 +0200 Subject: [PATCH 2/4] replace mri_coreg with neuroreg coreg --- HypVINN/README.md | 6 ++--- HypVINN/run_prediction.py | 14 +++++----- HypVINN/utils/__init__.py | 2 +- HypVINN/utils/preproc.py | 57 ++++++++++----------------------------- run_fastsurfer.sh | 21 +++++++-------- 5 files changed, 34 insertions(+), 66 deletions(-) diff --git a/HypVINN/README.md b/HypVINN/README.md index 558e175c5..e6fc67eb1 100644 --- a/HypVINN/README.md +++ b/HypVINN/README.md @@ -7,7 +7,7 @@ Hypothalamic subfields segmentation pipeline ### Requirements * Same as FastSurfer. -* If the T1w and T2w images are available and not co-registered, FreeSurfer should be sourced to run the registration code, and the mri_coreg and mri_vol2vol binaries should also be available. +* If the T1w and T2w images are available and not co-registered, HypVINN can register the T2w image to the T1w reference internally. ### Model weights * EUDAT (FZ Jülich) data repository: https://b2share.fz-juelich.de/records/2af6da63d5c1414b832c1f606bbd068a @@ -30,7 +30,7 @@ Note: These weights (version 1.1) are retrained compared to paper ([version 1.0] * `--t2 ` : T2 image path * `--seg_log` : Path to file in which run logs will be saved. If not set logs will be stored in `/sd/sid/scripts/hypvinn_seg.log` ### Image processing options - * `--reg_mode` : Ignored, if no T2 image is passed. Specifies the registration method used to register T1 and T2 images. Options are 'coreg' (default) for mri_coreg, 'robust' for mri_robust_register, and 'none' to skip registration (this requires T1 and T2 are externally co-registered). + * `--reg_mode` : Ignored, if no T2 image is passed. Specifies the registration method used to register T1 and T2 images. Options are 'coreg' (default) for `neuroreg.coreg` and 'none' to skip registration (this requires T1 and T2 are externally co-registered). * `--qc_snap`: Activate the creation of QC snapshots of the predicted HypVINN segmentation in `/sd/sid/qc_snapshots`. The created QC snapshots are created to simplify the visual quality control process. ### FastSurfer Technical parameters (see FastSurfer documentation) * `--device` @@ -104,7 +104,7 @@ Note: These weights (version 1.1) are retrained compared to paper ([version 1.0] |--hypothalamus.HypVINN.nii.gz(Hypothalamus Segmentation) |-- hypothalamus_mask.HypVINN.nii.gz (Hypothalamus Segmentation Mask) |-- transforms - |-- t2tot1.lta (FreeSurfer registration file, only available if registration is performed) + |-- t2tot1.lta (T2-to-T1 registration transform, only available if registration is performed) |-- qc_snapshots : QC outputs (optional) |-- hypothalamus.HypVINN_qc_screenshoot.png (Coronal quality control image) |-- stats : Statistics outputs diff --git a/HypVINN/run_prediction.py b/HypVINN/run_prediction.py index 95fbb2c88..0abe34829 100644 --- a/HypVINN/run_prediction.py +++ b/HypVINN/run_prediction.py @@ -109,11 +109,11 @@ def option_parse() -> argparse.ArgumentParser: "--reg_mode", type=str, default="coreg", - choices=["none", "coreg", "robust"], - help="Freesurfer Registration type to run. coreg: mri_coreg, " - "robust : mri_robust_register, none: entirely deactivates " - "registration of T2 to T1, if both images are passed, " - "images need to be register properly externally.", + choices=["none", "coreg"], + help="Registration type to run for T2-to-T1 alignment when both images " + "are passed. coreg: neuroreg.coreg, none: entirely deactivates " + "registration of T2 to T1, so images need to be registered " + "properly externally.", ) parser.add_argument( @@ -156,7 +156,7 @@ def main( hypo_maskfile: str = HYPVINN_MASK_NAME, qc_snapshots: bool = False, threads: int | None = None, - reg_mode: Literal["coreg", "robust", "none"] = "coreg", + reg_mode: Literal["coreg", "none"] = "coreg", batch_size: int = 1, async_io: bool = False, device: str = "auto", @@ -195,7 +195,7 @@ def main( Whether to create QC snapshots. threads : int, optional If not None, updates the FastSurfer global setting in `FastSurfer.utils.parallel`. - reg_mode : "coreg", "robust", "none", default="coreg" + reg_mode : "coreg", "none", default="coreg" The registration mode to use. batch_size : int, default=1 The batch size to use. diff --git a/HypVINN/utils/__init__.py b/HypVINN/utils/__init__.py index 64cd0b1c4..0753985d1 100644 --- a/HypVINN/utils/__init__.py +++ b/HypVINN/utils/__init__.py @@ -14,4 +14,4 @@ class ViewOperationDefinition(TypedDict): ViewOperations = dict[Plane, ViewOperationDefinition | None] ModalityMode = Literal["t1", "t2", "t1t2"] ModalityDict = dict[Literal["t1", "t2"], ndarray] -RegistrationMode = Literal["robust", "coreg", "none"] +RegistrationMode = Literal["coreg", "none"] diff --git a/HypVINN/utils/preproc.py b/HypVINN/utils/preproc.py index 8460250ab..f558555e4 100644 --- a/HypVINN/utils/preproc.py +++ b/HypVINN/utils/preproc.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import time from pathlib import Path from typing import cast @@ -36,7 +35,7 @@ def t1_to_t2_registration( threads: int = -1, ) -> Path: """ - Register T1 to T2 images using either mri_coreg or mri_robust_register. + Register the T2 image to the T1 reference using neuroreg. Parameters ---------- @@ -49,7 +48,7 @@ def t1_to_t2_registration( lta_path : Path The path to the lta transform. registration_type : RegistrationMode, default="coreg" - The type of registration to be used. It can be either "coreg" or "robust". + The type of registration to be used. It can be either "coreg" or "none". threads : int, default=-1 The number of threads to be used. If it is less than or equal to 0, the number of threads will be automatically determined. @@ -62,49 +61,20 @@ def t1_to_t2_registration( Raises ------ RuntimeError - If mri_coreg, mri_vol2vol, or mri_robust_register fails to run or if they cannot - be found. + If the requested neuroreg registration backend fails. """ - import shutil - - from FastSurferCNN.utils.parallel import get_num_threads - from FastSurferCNN.utils.run_tools import Popen - - if threads <= 0: - threads = get_num_threads() - - def from_freesurfer_home(fs_binary: str) -> str: - if not os.environ.get("FREESURFER_HOME", ""): - raise RuntimeError( - f"Could not find {fs_binary}, source FreeSurfer or set the FREESURFER_HOME environment variable" - ) - return os.environ["FREESURFER_HOME"] + "/bin/" + fs_binary - - def run_fs_binary(fs_binary: str, args: list[str]) -> int: - fs_binary = shutil.which(fs_binary) or from_freesurfer_home(fs_binary) - args = [fs_binary] + list(map(str, args)) - LOGGER.info("Running " + " ".join(args)) - retval = Popen(args).finish() - if retval.retcode != 0: - LOGGER.error(f"{fs_binary} failed with error code {retval.retcode}.") - raise RuntimeError(f"{fs_binary} failed") - - LOGGER.info(f"{fs_binary} finished in {retval.runtime}!") + from neuroreg import coreg if registration_type == "coreg": - run_fs_binary( - "mri_coreg", - ["--mov", t2_path, "--targ", t1_path, "--reg", lta_path, "--threads", str(threads)], - ) - run_fs_binary( - "mri_vol2vol", - ["--mov", t2_path, "--targ", t1_path, "--reg", lta_path, "--o", output_path, "--cubic", "--keep-precision"], + LOGGER.info("Running neuroreg.coreg for T2-to-T1 registration.") + coreg( + str(t2_path), + str(t1_path), + lta_name=str(lta_path), + mapped_name=str(output_path), ) else: - run_fs_binary( - "mri_robust_register", - ["--mov", t2_path, "--dst", t1_path, "--lta", lta_path, "--mapmov", output_path, "--cost", "NMI"], - ) + raise ValueError(f"Unknown registration type: {registration_type}") return output_path @@ -125,7 +95,8 @@ def hypvinn_preproc( mode : ModalityMode The mode for HypVINN. It should be "t1t2". reg_mode : RegistrationMode - The registration mode. If it is not "none", the function will register T1 to T2 images. + The registration mode. If it is not "none", the function will register the + T2 image to the T1 reference. t1_path : Path The path to the T1 image. t2_path : Path @@ -162,7 +133,7 @@ def hypvinn_preproc( f"T2 image will be interpolated to the resolution of the T1 image." ) - LOGGER.info("Registering T1 to T2 ...") + LOGGER.info("Registering T2 to T1 ...") t1_to_t2_registration( t1_path=t1_path, t2_path=t2_path, diff --git a/run_fastsurfer.sh b/run_fastsurfer.sh index 45a36262e..e22540d79 100755 --- a/run_fastsurfer.sh +++ b/run_fastsurfer.sh @@ -239,13 +239,13 @@ SEGMENTATION PIPELINE: --t2 *Optional* T2 full head input (must be externally biasfield corrected when called with --no_biasfield). Requires an ABSOLUTE Path! - --reg_mode + --reg_mode Ignored, if no T2 image is passed. - Specifies the registration method used to register T1 - and T2 images. Options are 'coreg' (default) for - mri_coreg, 'robust' for mri_robust_register, and 'none' - to skip registration (this requires T1 and T2 are - externally co-registered). + Specifies the registration method used to register T1 + and T2 images. Options are 'coreg' (default) for + neuroreg.coreg and 'none' to skip registration + (this requires T1 and T2 are externally + co-registered). --qc_snap Create QC snapshots in \$SUBJECTS_DIR/\$sid/qc_snapshots to simplify the QC process. @@ -515,8 +515,8 @@ case $key in --hypo_statsfile) hypo_statsfile="$1" ; shift ;; --reg_mode) mode=$(echo "$1" | tr "[:upper:]" "[:lower:]") - if [[ "$mode" =~ ^(none|coreg|robust)$ ]] ; then hypvinn_regmode="$mode" - else echo "Invalid --reg_mode option, must be 'none', 'coreg' or 'robust'." ; exit 1 + if [[ "$mode" =~ ^(none|coreg)$ ]] ; then hypvinn_regmode="$mode" + else echo "Invalid --reg_mode option, must be 'none' or 'coreg'." ; exit 1 fi shift # past value ;; @@ -844,9 +844,6 @@ if [[ "$run_seg_pipeline" == "true" ]] ; then if [[ "$run_biasfield" == "true" ]] && [[ "$run_talairach_registration" == "true" ]] ; then what_needs_license+=" and the talairach-registration in the segmentation pipeline" fi - if [[ -n "$t2" ]] && [[ "$hypvinn_regmode" != "none" ]] ; then - what_needs_license+=" and the T1-T2 registration in the segmentation pipeline" - fi fi if [[ -n "$what_needs_license" ]] then @@ -1402,7 +1399,7 @@ then if [[ "$run_hypvinn_module" == "true" ]] then echo "MODULE: HypVINN hypothalamus segmentation" >> "$exec_time_log" - # currently, the order of the T2 preprocessing only is registration to T1w + # currently, the T2 preprocessing step in HypVINN is registration to T1w cmd=($python "$hypvinndir/run_prediction.py" --sd "${sd}" --sid "${subject}" --reg_mode "$hypvinn_regmode" "${hypvinn_flags[@]}" --threads "$threads_seg" --async_io --batch_size "$batch_size" --seg_log "$seg_log" --device "$device" --viewagg_device "$viewagg" --t1) From 48629a1aa3d79362c7382a836b91b2c6e94e550b Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Tue, 26 May 2026 14:59:52 +0200 Subject: [PATCH 3/4] add os import --- HypVINN/utils/preproc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/HypVINN/utils/preproc.py b/HypVINN/utils/preproc.py index f558555e4..a2f599972 100644 --- a/HypVINN/utils/preproc.py +++ b/HypVINN/utils/preproc.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os import time from pathlib import Path from typing import cast From 0907ff37963bc6f6d0b6b4dc295ffeac2d830aea Mon Sep 17 00:00:00 2001 From: Martin Reuter Date: Thu, 28 May 2026 18:46:07 +0200 Subject: [PATCH 4/4] coreg keep-dtype --- HypVINN/utils/preproc.py | 1 + pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HypVINN/utils/preproc.py b/HypVINN/utils/preproc.py index a2f599972..020aa46fa 100644 --- a/HypVINN/utils/preproc.py +++ b/HypVINN/utils/preproc.py @@ -73,6 +73,7 @@ def t1_to_t2_registration( str(t1_path), lta_name=str(lta_path), mapped_name=str(output_path), + keep_dtype=True, ) else: raise ValueError(f"Unknown registration type: {registration_type}") diff --git a/pyproject.toml b/pyproject.toml index 7b9146115..254b3a08a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ 'matplotlib>=3.7.1', 'meshpy>=2025.1.1', # needed for FastSurfer-CC 'monai>=1.4.0', # needed for FastSurfer-CC - 'neuroreg>=0.6.2', # needed for registration / etiv + 'neuroreg>=0.6.3', # needed for registration / etiv 'nibabel>=5.4.0', # needed to fix a bug in nibabel 'numpy>=1.25', 'packaging', diff --git a/requirements.txt b/requirements.txt index cfea3ea10..0a289f96a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -60,7 +60,7 @@ multipledispatch==1.0.0 narwhals==2.21.0 networkx==3.6.1 neurolit==0.6.1 -neuroreg==0.6.2 +neuroreg==0.6.3 nibabel==5.4.2 numpy==2.4.4 packaging==26.2