From f4d0740d720d33a8245bb884e94fb28eb0e3e133 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 11:34:25 +0100 Subject: [PATCH 01/30] initial commit Co-authored-by: Isra Mekki Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 brainles_preprocessing/registration/ANTs/ANTs.py diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py new file mode 100644 index 0000000..21db36e --- /dev/null +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -0,0 +1,118 @@ +# TODO add typing and docs +import os +import shutil + +import ants +from auxiliary.nifti.io import read_nifti, write_nifti +from auxiliary.runscript import ScriptRunner +from auxiliary.turbopath import turbopath + +from brainles_preprocessing.registration.registrator import Registrator + +# from auxiliary import ScriptRunner + + +class ANTsRegistrator(Registrator): + def __init__( + self, + type_of_transform: str = "Rigid", + ): + """ + TODO + """ + self.type_of_transform = type_of_transform + + def register( + self, + fixed_image_path: str, + moving_image_path: str, + transformed_image_path: str, + matrix_path: str, + log_file_path: str, + ) -> None: + """ + Register images using NiftyReg. + + Args: + fixed_image_path (str): Path to the fixed image. + moving_image_path (str): Path to the moving image. + transformed_image_path (str): Path to the transformed image (output). + matrix_path (str): Path to the transformation matrix (output). + log_file_path (str): Path to the log file. + """ + transformed_image_path = turbopath(transformed_image_path) + matrix_path = turbopath(matrix_path) + + fixed_image = ants.image_read(fixed_image_path) + moving_image = ants.image_read(moving_image_path) + + registration_result = ants.registration( + fixed=fixed_image, + moving=moving_image, + type_of_transform=self.type_of_transform, + ) + transformed_image = registration_result["warpedmovout"] + # make sure the parent exists + os.makedirs(transformed_image_path.parent, exist_ok=True) + ants.image_write(transformed_image, transformed_image_path) + + # write matrix + os.makedirs(matrix_path.parent, exist_ok=True) + shutil.copyfile(registration_result["fwdtransforms"][0], matrix_path) + + # TODO logging + + def transform( + self, + fixed_image_path: str, + moving_image_path: str, + transformed_image_path: str, + matrix_path: str, + log_file_path: str, + ) -> None: + """ + Apply a transformation using NiftyReg. + + Args: + fixed_image_path (str): Path to the fixed image. + moving_image_path (str): Path to the moving image. + transformed_image_path (str): Path to the transformed image (output). + matrix_path (str): Path to the transformation matrix. + log_file_path (str): Path to the log file. + """ + runner = ScriptRunner( + script_path=self.transformation_script, + log_path=log_file_path, + ) + + niftyreg_executable = str( + turbopath(__file__).parent + "/niftyreg_scripts/reg_resample", + ) + + input_params = [ + turbopath(niftyreg_executable), + turbopath(fixed_image_path), + turbopath(moving_image_path), + turbopath(transformed_image_path), + turbopath(matrix_path), + ] + + # Call the run method to execute the script and capture the output in the log file + success, error = runner.run(input_params) + + # if success: + # print("Script executed successfully. Check the log file for details.") + # else: + # print("Script execution failed:", error) + + +if __name__ == "__main__": + reg = ANTsRegistrator() + + reg.register( + fixed_image_path="example/example_data/TCGA-DU-7294/AX_T1_POST_GD_FLAIR_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T1_POST_GD_FLAIR_RM_13_t1c.nii.gz", + moving_image_path="example/example_data/TCGA-DU-7294/AX_T2_FR-FSE_RF2_150_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T2_FR-FSE_RF2_150_RM_4_t2.nii.gz", + transformed_image_path="example/example_ants/transformed_image.nii.gz", + matrix_path="example/example_ants_matrix/matrix.txt", + log_file_path="example/example_ants/log.txt", + ) From 0bc3c6cb6756a70049758107545ae7ade78f79e0 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 11:40:44 +0100 Subject: [PATCH 02/30] make matrix endings backend agnostic Signed-off-by: neuronflow --- brainles_preprocessing/modality.py | 7 +++++-- brainles_preprocessing/registration/niftyreg/niftyreg.py | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/brainles_preprocessing/modality.py b/brainles_preprocessing/modality.py index 476bfb8..0029ee2 100644 --- a/brainles_preprocessing/modality.py +++ b/brainles_preprocessing/modality.py @@ -6,7 +6,8 @@ from auxiliary.normalization.normalizer_base import Normalizer from auxiliary.turbopath import turbopath -from brainles_preprocessing.brain_extraction.brain_extractor import BrainExtractor +from brainles_preprocessing.brain_extraction.brain_extractor import \ + BrainExtractor from brainles_preprocessing.registration.registrator import Registrator @@ -170,7 +171,9 @@ def register( str: Path to the registration matrix. """ registered = os.path.join(registration_dir, f"{moving_image_name}.nii.gz") - registered_matrix = os.path.join(registration_dir, f"{moving_image_name}.txt") + registered_matrix = os.path.join( + registration_dir, f"{moving_image_name}" + ) # note, add file ending depending on registration backend! registered_log = os.path.join(registration_dir, f"{moving_image_name}.log") registrator.register( diff --git a/brainles_preprocessing/registration/niftyreg/niftyreg.py b/brainles_preprocessing/registration/niftyreg/niftyreg.py index 778484b..f84effa 100644 --- a/brainles_preprocessing/registration/niftyreg/niftyreg.py +++ b/brainles_preprocessing/registration/niftyreg/niftyreg.py @@ -72,7 +72,7 @@ def register( turbopath(fixed_image_path), turbopath(moving_image_path), turbopath(transformed_image_path), - turbopath(matrix_path), + turbopath(matrix_path) + ".txt", ] # Call the run method to execute the script and capture the output in the log file @@ -115,7 +115,8 @@ def transform( turbopath(fixed_image_path), turbopath(moving_image_path), turbopath(transformed_image_path), - turbopath(matrix_path), + # we need to add txt as this is the format for niftyreg matrixes + turbopath(matrix_path) + ".txt", ] # Call the run method to execute the script and capture the output in the log file From b7411f61d5d050c00cbca60fc4d0d3546d93ba20 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:12:17 +0100 Subject: [PATCH 03/30] add example Co-authored-by: Isra Mekki Signed-off-by: neuronflow --- example/example_modality_centric_preprocessor.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/example/example_modality_centric_preprocessor.py b/example/example_modality_centric_preprocessor.py index 384a139..13f419a 100644 --- a/example/example_modality_centric_preprocessor.py +++ b/example/example_modality_centric_preprocessor.py @@ -6,7 +6,8 @@ from brainles_preprocessing.brain_extraction import HDBetExtractor from brainles_preprocessing.modality import Modality from brainles_preprocessing.preprocessor import Preprocessor -from brainles_preprocessing.registration import NiftyRegRegistrator +from brainles_preprocessing.registration import (ANTsRegistrator, + NiftyRegRegistrator) def preprocess(inputDir): @@ -100,7 +101,9 @@ def preprocess(inputDir): preprocessor = Preprocessor( center_modality=center, moving_modalities=moving_modalities, - registrator=NiftyRegRegistrator(), + # choose the registration backend you want to use + # registrator=NiftyRegRegistrator(), + registrator=ANTsRegistrator(), brain_extractor=HDBetExtractor(), temp_folder="temporary_directory", limit_cuda_visible_devices="0", From e0c42ab784dd2c1e7aa17a1b851133baed085d57 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:12:33 +0100 Subject: [PATCH 04/30] fix import Signed-off-by: neuronflow --- brainles_preprocessing/registration/ANTs/__init__.py | 0 brainles_preprocessing/registration/__init__.py | 1 + 2 files changed, 1 insertion(+) create mode 100644 brainles_preprocessing/registration/ANTs/__init__.py diff --git a/brainles_preprocessing/registration/ANTs/__init__.py b/brainles_preprocessing/registration/ANTs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/brainles_preprocessing/registration/__init__.py b/brainles_preprocessing/registration/__init__.py index 6f56b85..0550373 100644 --- a/brainles_preprocessing/registration/__init__.py +++ b/brainles_preprocessing/registration/__init__.py @@ -1 +1,2 @@ +from .ANTs.ANTs import ANTsRegistrator from .niftyreg.niftyreg import NiftyRegRegistrator From b37925244e6f3107f5941013e9241a9cc9284b8c Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:13:32 +0100 Subject: [PATCH 05/30] add todo for tests Signed-off-by: neuronflow --- brainles_preprocessing/registration/ANTs/ANTs.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 21db36e..5d61ecd 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -107,12 +107,21 @@ def transform( if __name__ == "__main__": + # TODO move this into unit tests reg = ANTsRegistrator() reg.register( fixed_image_path="example/example_data/TCGA-DU-7294/AX_T1_POST_GD_FLAIR_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T1_POST_GD_FLAIR_RM_13_t1c.nii.gz", moving_image_path="example/example_data/TCGA-DU-7294/AX_T2_FR-FSE_RF2_150_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T2_FR-FSE_RF2_150_RM_4_t2.nii.gz", transformed_image_path="example/example_ants/transformed_image.nii.gz", - matrix_path="example/example_ants_matrix/matrix.txt", + matrix_path="example/example_ants_matrix/matrix", + log_file_path="example/example_ants/log.txt", + ) + + reg.transform( + fixed_image_path="example/example_data/TCGA-DU-7294/AX_T1_POST_GD_FLAIR_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T1_POST_GD_FLAIR_RM_13_t1c.nii.gz", + moving_image_path="example/example_data/OtherEXampleFromTCIA/T1_AX_OtherEXampleTCIA_TCGA-FG-6692_Si_TCGA-FG-6692_T1_AX_SE_10_se2d1_t1.nii.gz", + transformed_image_path="example/example_ants_transformed/transformed_image.nii.gz", + matrix_path="example/example_ants_matrix/matrix", log_file_path="example/example_ants/log.txt", ) From defda26e4b558109ed32c1b8954d2113d2eeb424 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:14:42 +0100 Subject: [PATCH 06/30] transform Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 5d61ecd..c51c17c 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -80,30 +80,22 @@ def transform( matrix_path (str): Path to the transformation matrix. log_file_path (str): Path to the log file. """ - runner = ScriptRunner( - script_path=self.transformation_script, - log_path=log_file_path, - ) - niftyreg_executable = str( - turbopath(__file__).parent + "/niftyreg_scripts/reg_resample", + fixed_image = ants.image_read(fixed_image_path) + moving_image = ants.image_read(moving_image_path) + + transformed_image_path = turbopath(transformed_image_path) + os.makedirs(transformed_image_path.parent, exist_ok=True) + + matrix_path = turbopath(matrix_path) + ".mat" + + transformed_image = ants.apply_transforms( + fixed=fixed_image, moving=moving_image, transformlist=[matrix_path] ) - input_params = [ - turbopath(niftyreg_executable), - turbopath(fixed_image_path), - turbopath(moving_image_path), - turbopath(transformed_image_path), - turbopath(matrix_path), - ] - - # Call the run method to execute the script and capture the output in the log file - success, error = runner.run(input_params) - - # if success: - # print("Script executed successfully. Check the log file for details.") - # else: - # print("Script execution failed:", error) + ants.image_write(transformed_image, transformed_image_path) + + # TODO logging if __name__ == "__main__": From e3e9b35e654f505c746071295e4d8e8d55956146 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:15:02 +0100 Subject: [PATCH 07/30] reg working Signed-off-by: neuronflow --- brainles_preprocessing/registration/ANTs/ANTs.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index c51c17c..1b8ba2b 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -16,6 +16,7 @@ class ANTsRegistrator(Registrator): def __init__( self, type_of_transform: str = "Rigid", + # TODO add registration parameters here ): """ TODO @@ -41,11 +42,13 @@ def register( log_file_path (str): Path to the log file. """ transformed_image_path = turbopath(transformed_image_path) - matrix_path = turbopath(matrix_path) - + # we need to add .mat as this is the format for ANTs matrixes + matrix_path = turbopath(matrix_path) + ".mat" + fixed_image = ants.image_read(fixed_image_path) moving_image = ants.image_read(moving_image_path) + # TODO finetune registration parameters registration_result = ants.registration( fixed=fixed_image, moving=moving_image, From b2ed6f8e74eea486c2daafaee50606955a837547 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 12:20:17 +0100 Subject: [PATCH 08/30] add todo Signed-off-by: neuronflow --- .../registration/ANTs/TODO_ANTs_parameters.py | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 brainles_preprocessing/registration/ANTs/TODO_ANTs_parameters.py diff --git a/brainles_preprocessing/registration/ANTs/TODO_ANTs_parameters.py b/brainles_preprocessing/registration/ANTs/TODO_ANTs_parameters.py new file mode 100644 index 0000000..2fce65b --- /dev/null +++ b/brainles_preprocessing/registration/ANTs/TODO_ANTs_parameters.py @@ -0,0 +1,195 @@ +import os +import shlex + +from app.project_e.image_processing.utilities.utils import eleSubprocess +from flask_socketio import SocketIO + + +def ants_registrator( + fixed_image, moving_image, outputmat, transformationalgorithm="rigid" +): + # ants call parameters + dimensionality = "-d 3" + initial_moving_transform = "-r [" + fixed_image + ", " + moving_image + ", 0]" + + # transformations + if transformationalgorithm == "rigid": + # rigid ants_transformation + transform_rigid = "-t rigid[0.1]" + metric_rigid = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.5]" + ) + convergence_rigid = "-c [1000x500x250, 1e-6, 10]" + smoothing_sigmas_rigid = "-s 3x2x1vox" + shrink_factors_rigid = "-f 8x4x2" + elif transformationalgorithm == "rigid+affine": + # rigid ants_transformation + transform_rigid = "-t rigid[0.1]" + metric_rigid = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.5]" + ) + convergence_rigid = "-c [1000x500x250, 1e-6, 10]" + smoothing_sigmas_rigid = "-s 3x2x1vox" + shrink_factors_rigid = "-f 8x4x2" + + # affine ants_transformation + transform_affine = "-t affine[0.1]" + metric_affine = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.5]" + ) + convergence_affine = "-c [1000x500x250, 1e-6, 10]" + smoothing_sigmas_affine = "-s 3x2x1vox" + shrink_factors_affine = "-f 8x4x2" + elif transformationalgorithm == "rex-dfc": + # translation + transform_translation = "-t translation[0.1]" + metric_translation = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.05]" + ) + convergence_translation = "-c [1000, 1e-8, 20]" + smoothing_sigmas_translation = "-s 4vox" + shrink_factors_translation = "-f 6" + + # rigid ants_transformation + transform_rigid = "-t rigid[0.1]" + metric_rigid = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.1]" + ) + convergence_rigid = "-c [1000x1000, 1e-8, 20]" + smoothing_sigmas_rigid = "-s 4x2vox" + shrink_factors_rigid = "-f 4x2" + + # affine ants_transformation + transform_affine = "-t affine[0.1]" + metric_affine = ( + "-m Mattes[" + fixed_image + "," + moving_image + ", 1, 32, Regular, 0.1]" + ) + convergence_affine = "-c [10000x1111x5, 1e-8, 20]" + smoothing_sigmas_affine = "-s 3x2x1vox" + shrink_factors_affine = "-f 8x4x2" + + # other parameters + use_estimate_learning_rate_once = "-l 1" + collapse_output_transforms = "-z 1" + interpolation = "-n BSpline[3]" + precision = "--float 1" + output = "-o " + "[" + outputmat + "]" + + # generate calls + if transformationalgorithm == "rigid": + ants_cmd = ( + "antsRegistration", + dimensionality, + initial_moving_transform, + # rigid ants_transformation + transform_rigid, + metric_rigid, + convergence_rigid, + smoothing_sigmas_rigid, + shrink_factors_rigid, + # other parameters + use_estimate_learning_rate_once, + collapse_output_transforms, + interpolation, + precision, + output, + ) + ants_call = shlex.split("%s %s %s %s %s %s %s %s %s %s %s %s %s" % ants_cmd) + + elif transformationalgorithm == "rigid+affine": + ants_cmd = ( + "antsRegistration", + dimensionality, + initial_moving_transform, + # rigid ants_transformation + transform_rigid, + metric_rigid, + convergence_rigid, + smoothing_sigmas_rigid, + shrink_factors_rigid, + # affine ants_transformation + transform_affine, + metric_affine, + convergence_affine, + smoothing_sigmas_affine, + shrink_factors_affine, + # other parameters + use_estimate_learning_rate_once, + collapse_output_transforms, + interpolation, + precision, + output, + ) + ants_call = shlex.split( + "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s" % ants_cmd + ) + + elif transformationalgorithm == "rex-dfc": + ants_cmd = ( + "antsRegistration", + dimensionality, + initial_moving_transform, + # translation + transform_translation, + metric_translation, + convergence_translation, + smoothing_sigmas_translation, + shrink_factors_translation, + # rigid ants_transformation + transform_rigid, + metric_rigid, + convergence_rigid, + smoothing_sigmas_rigid, + shrink_factors_rigid, + # affine ants_transformation + transform_affine, + metric_affine, + convergence_affine, + smoothing_sigmas_affine, + shrink_factors_affine, + # other parameters + use_estimate_learning_rate_once, + collapse_output_transforms, + interpolation, + precision, + output, + ) + ants_call = shlex.split( + "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s" + % ants_cmd + ) + + # construct call + readable_ants_call = " ".join(ants_cmd) + print("calling ants with the following call:") + print(readable_ants_call) + + # log file + logFilePath = outputmat + "registration.log" + # call it + eleSubprocess(logFilePath=logFilePath, call=ants_call) + + +def modality_registrator(examid, modality): + socketio = SocketIO(message_queue="redis://") + socketio.emit( + "ipstatus", {"examid": examid, "ipstatus": modality + " ants registration"} + ) + + niftipath = os.path.normpath(os.path.join("data/tmp/", examid, "raw_niftis")) + # fixed image + native_t1 = os.path.join(niftipath, examid + "_native_t1.nii.gz") + + # moving images + moving_image = os.path.join(niftipath, examid + "_native_" + modality + ".nii.gz") + + # output mats + exportpath = os.path.normpath(os.path.join("data/tmp/", examid, "registrations")) + os.makedirs(exportpath, exist_ok=True) + filename = examid + "_" + modality + "_to_t1_" + outputmat = os.path.join(exportpath, filename) + + # call it + ants_registrator( + native_t1, moving_image, outputmat, transformationalgorithm="rigid" + ) From a3ba11d2aec4b5cff1f16af19e1b9fc8375aaa04 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:32:35 +0000 Subject: [PATCH 09/30] Autoformat with black --- brainles_preprocessing/modality.py | 3 +-- example/example_modality_centric_preprocessor.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/brainles_preprocessing/modality.py b/brainles_preprocessing/modality.py index 0029ee2..b4a4c03 100644 --- a/brainles_preprocessing/modality.py +++ b/brainles_preprocessing/modality.py @@ -6,8 +6,7 @@ from auxiliary.normalization.normalizer_base import Normalizer from auxiliary.turbopath import turbopath -from brainles_preprocessing.brain_extraction.brain_extractor import \ - BrainExtractor +from brainles_preprocessing.brain_extraction.brain_extractor import BrainExtractor from brainles_preprocessing.registration.registrator import Registrator diff --git a/example/example_modality_centric_preprocessor.py b/example/example_modality_centric_preprocessor.py index 13f419a..4bf7c0e 100644 --- a/example/example_modality_centric_preprocessor.py +++ b/example/example_modality_centric_preprocessor.py @@ -6,8 +6,7 @@ from brainles_preprocessing.brain_extraction import HDBetExtractor from brainles_preprocessing.modality import Modality from brainles_preprocessing.preprocessor import Preprocessor -from brainles_preprocessing.registration import (ANTsRegistrator, - NiftyRegRegistrator) +from brainles_preprocessing.registration import ANTsRegistrator, NiftyRegRegistrator def preprocess(inputDir): From 93eb06e84e247073245e30829123a22a32281ae9 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 23:27:39 +0100 Subject: [PATCH 10/30] docstring Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 1b8ba2b..615bd29 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -19,9 +19,22 @@ def __init__( # TODO add registration parameters here ): """ - TODO + Initialize an ANTsRegistrator instance. + + Parameters: + - registration_params (dict, optional): Dictionary of parameters for the registration method. + Defaults to None, which implies using default registration parameters with a rigid transformation. + - transformation_params (dict, optional): Dictionary of parameters for the transformation method. + Defaults to an empty dictionary. + + The registration_params dictionary may include the following keys: + - type_of_transform (str, optional): Type of transformation to use (default is "Rigid"). + + Example: + >>> reg_params = {'type_of_transform': 'Affine', 'reg_iterations': (30, 20, 10)} + >>> transform_params = {'interpolator': 'linear', 'imagetype': 1} + >>> registrator = ANTsRegistrator(registration_params=reg_params, transformation_params=transform_params) """ - self.type_of_transform = type_of_transform def register( self, From de82e99e2dc19f4c226a7ba8bebc2744192b4a8a Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 23:27:54 +0100 Subject: [PATCH 11/30] kwargs Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 615bd29..326c313 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -3,20 +3,16 @@ import shutil import ants -from auxiliary.nifti.io import read_nifti, write_nifti -from auxiliary.runscript import ScriptRunner from auxiliary.turbopath import turbopath from brainles_preprocessing.registration.registrator import Registrator -# from auxiliary import ScriptRunner - class ANTsRegistrator(Registrator): def __init__( self, - type_of_transform: str = "Rigid", - # TODO add registration parameters here + registration_params: dict = None, + transformation_params: dict = None, ): """ Initialize an ANTsRegistrator instance. @@ -35,6 +31,12 @@ def __init__( >>> transform_params = {'interpolator': 'linear', 'imagetype': 1} >>> registrator = ANTsRegistrator(registration_params=reg_params, transformation_params=transform_params) """ + # Set default registration parameters + default_registration_params = {"type_of_transform": "Rigid"} + self.registration_params = registration_params or default_registration_params + + # Set default transformation parameters + self.transformation_params = transformation_params or {} def register( self, @@ -43,9 +45,10 @@ def register( transformed_image_path: str, matrix_path: str, log_file_path: str, + **kwargs ) -> None: """ - Register images using NiftyReg. + Register images using ANTs. Args: fixed_image_path (str): Path to the fixed image. @@ -53,29 +56,27 @@ def register( transformed_image_path (str): Path to the transformed image (output). matrix_path (str): Path to the transformation matrix (output). log_file_path (str): Path to the log file. + **kwargs: Additional registration parameters. """ + registration_kwargs = {**self.registration_params, **kwargs} transformed_image_path = turbopath(transformed_image_path) - # we need to add .mat as this is the format for ANTs matrixes - matrix_path = turbopath(matrix_path) + ".mat" + + matrix_path = turbopath(matrix_path) + if matrix_path.suffix != ".mat": + matrix_path = matrix_path.with_suffix(".mat") fixed_image = ants.image_read(fixed_image_path) moving_image = ants.image_read(moving_image_path) - - # TODO finetune registration parameters registration_result = ants.registration( fixed=fixed_image, moving=moving_image, - type_of_transform=self.type_of_transform, + **registration_kwargs, ) transformed_image = registration_result["warpedmovout"] - # make sure the parent exists os.makedirs(transformed_image_path.parent, exist_ok=True) ants.image_write(transformed_image, transformed_image_path) - - # write matrix os.makedirs(matrix_path.parent, exist_ok=True) shutil.copyfile(registration_result["fwdtransforms"][0], matrix_path) - # TODO logging def transform( @@ -85,9 +86,10 @@ def transform( transformed_image_path: str, matrix_path: str, log_file_path: str, + **kwargs ) -> None: """ - Apply a transformation using NiftyReg. + Apply a transformation using ANTs. Args: fixed_image_path (str): Path to the fixed image. @@ -95,22 +97,24 @@ def transform( transformed_image_path (str): Path to the transformed image (output). matrix_path (str): Path to the transformation matrix. log_file_path (str): Path to the log file. + **kwargs: Additional transformation parameters. """ - + transform_kwargs = {**self.transformation_params, **kwargs} fixed_image = ants.image_read(fixed_image_path) moving_image = ants.image_read(moving_image_path) - transformed_image_path = turbopath(transformed_image_path) os.makedirs(transformed_image_path.parent, exist_ok=True) - matrix_path = turbopath(matrix_path) + ".mat" - + matrix_path = turbopath(matrix_path) + if matrix_path.suffix != ".mat": + matrix_path = matrix_path.with_suffix(".mat") transformed_image = ants.apply_transforms( - fixed=fixed_image, moving=moving_image, transformlist=[matrix_path] + fixed=fixed_image, + moving=moving_image, + transformlist=[matrix_path], + **transform_kwargs, ) - ants.image_write(transformed_image, transformed_image_path) - # TODO logging @@ -130,6 +134,6 @@ def transform( fixed_image_path="example/example_data/TCGA-DU-7294/AX_T1_POST_GD_FLAIR_TCGA-DU-7294_TCGA-DU-7294_GE_TCGA-DU-7294_AX_T1_POST_GD_FLAIR_RM_13_t1c.nii.gz", moving_image_path="example/example_data/OtherEXampleFromTCIA/T1_AX_OtherEXampleTCIA_TCGA-FG-6692_Si_TCGA-FG-6692_T1_AX_SE_10_se2d1_t1.nii.gz", transformed_image_path="example/example_ants_transformed/transformed_image.nii.gz", - matrix_path="example/example_ants_matrix/matrix", + matrix_path="example/example_ants_matrix/matrix.mat", log_file_path="example/example_ants/log.txt", ) From a94b321317561a700a39c331a7a3b9d0fe123126 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 23:32:12 +0100 Subject: [PATCH 12/30] nicer Signed-off-by: neuronflow --- .../registration/niftyreg/niftyreg.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/registration/niftyreg/niftyreg.py b/brainles_preprocessing/registration/niftyreg/niftyreg.py index f84effa..90c02b0 100644 --- a/brainles_preprocessing/registration/niftyreg/niftyreg.py +++ b/brainles_preprocessing/registration/niftyreg/niftyreg.py @@ -66,13 +66,17 @@ def register( niftyreg_executable = str( turbopath(__file__).parent + "/niftyreg_scripts/reg_aladin", ) + + turbopath(matrix_path) + if matrix_path.suffix != ".txt": + matrix_path = matrix_path.with_suffix(".txt") input_params = [ turbopath(niftyreg_executable), turbopath(fixed_image_path), turbopath(moving_image_path), turbopath(transformed_image_path), - turbopath(matrix_path) + ".txt", + turbopath(matrix_path), ] # Call the run method to execute the script and capture the output in the log file @@ -110,13 +114,17 @@ def transform( turbopath(__file__).parent + "/niftyreg_scripts/reg_resample", ) + turbopath(matrix_path) + if matrix_path.suffix != ".txt": + matrix_path = matrix_path.with_suffix(".txt") + input_params = [ turbopath(niftyreg_executable), turbopath(fixed_image_path), turbopath(moving_image_path), turbopath(transformed_image_path), + turbopath(matrix_path), # we need to add txt as this is the format for niftyreg matrixes - turbopath(matrix_path) + ".txt", ] # Call the run method to execute the script and capture the output in the log file From a022cd8f372a33e27043cc45b926758257fd925d Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 23 Feb 2024 23:35:57 +0100 Subject: [PATCH 13/30] bump version requirements Signed-off-by: neuronflow --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4df8f63..f824fb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,7 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.10" # core -path = "^16.2.0" +path = "^16.10.0" ttictoc = "^0.5.6" pathlib = "^1.0.1" nibabel = "^3.2.1" @@ -55,7 +55,7 @@ BrainLes-HD-BET = ">=0.0.5" # utils tqdm = "^4.64.1" -auxiliary = "^0.0.40" +auxiliary = "^0.0.41" rich = "^13.6.0" [tool.poetry.dev-dependencies] From b722d8847b828482b4137cf81e41f4f2670254af Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:37:04 +0000 Subject: [PATCH 14/30] Autoformat with black --- brainles_preprocessing/registration/niftyreg/niftyreg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainles_preprocessing/registration/niftyreg/niftyreg.py b/brainles_preprocessing/registration/niftyreg/niftyreg.py index 90c02b0..afbe089 100644 --- a/brainles_preprocessing/registration/niftyreg/niftyreg.py +++ b/brainles_preprocessing/registration/niftyreg/niftyreg.py @@ -66,7 +66,7 @@ def register( niftyreg_executable = str( turbopath(__file__).parent + "/niftyreg_scripts/reg_aladin", ) - + turbopath(matrix_path) if matrix_path.suffix != ".txt": matrix_path = matrix_path.with_suffix(".txt") From d050a406e596eeca611ce0295993d8ea9f291058 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Tue, 27 Feb 2024 18:16:14 +0100 Subject: [PATCH 15/30] add comment Signed-off-by: neuronflow --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f824fb1..7350673 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ BrainLes-HD-BET = ">=0.0.5" # utils tqdm = "^4.64.1" -auxiliary = "^0.0.41" +auxiliary = "^0.0.41" # TODO why does this try to install 0.0.42? rich = "^13.6.0" [tool.poetry.dev-dependencies] From 8060dcc4d7facc401ea6bb57c4ab2c79cece23bf Mon Sep 17 00:00:00 2001 From: neuronflow Date: Tue, 27 Feb 2024 18:20:56 +0100 Subject: [PATCH 16/30] clean Signed-off-by: neuronflow --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7350673..f824fb1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ BrainLes-HD-BET = ">=0.0.5" # utils tqdm = "^4.64.1" -auxiliary = "^0.0.41" # TODO why does this try to install 0.0.42? +auxiliary = "^0.0.41" rich = "^13.6.0" [tool.poetry.dev-dependencies] From d2e22274e5ca88660a04b33d721ce9f45c352cff Mon Sep 17 00:00:00 2001 From: IsraMekki0 Date: Fri, 1 Mar 2024 08:18:14 +0100 Subject: [PATCH 17/30] Make ants an optional dependency --- brainles_preprocessing/registration/__init__.py | 10 +++++++++- pyproject.toml | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/registration/__init__.py b/brainles_preprocessing/registration/__init__.py index 0550373..70b18fd 100644 --- a/brainles_preprocessing/registration/__init__.py +++ b/brainles_preprocessing/registration/__init__.py @@ -1,2 +1,10 @@ -from .ANTs.ANTs import ANTsRegistrator +import warnings + +try: + from .ANTs.ANTs import ANTsRegistrator +except ImportError: + warnings.warn( + "ANTS package not found. If you want to use it, please install it using 'pip install antspyx'" + ) + from .niftyreg.niftyreg import NiftyRegRegistrator diff --git a/pyproject.toml b/pyproject.toml index f824fb1..a7e5b42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,6 @@ classifiers = [ "Operating System :: OS Independent", ] - [tool.poetry.dependencies] python = "^3.10" # core @@ -58,6 +57,12 @@ tqdm = "^4.64.1" auxiliary = "^0.0.41" rich = "^13.6.0" + +antspyx = { version = "^0.4.2", optional = true } + +[tool.poetry.extras] +ants = ["antspyx"] + [tool.poetry.dev-dependencies] pytest = "^6.2" From cb92dbe0122415fdc3b0585a768cbcf5d9a8da6e Mon Sep 17 00:00:00 2001 From: IsraMekki0 Date: Fri, 1 Mar 2024 09:44:34 +0100 Subject: [PATCH 18/30] Modify warning to install ants via brainles --- brainles_preprocessing/registration/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brainles_preprocessing/registration/__init__.py b/brainles_preprocessing/registration/__init__.py index 70b18fd..b4c8bcb 100644 --- a/brainles_preprocessing/registration/__init__.py +++ b/brainles_preprocessing/registration/__init__.py @@ -4,7 +4,7 @@ from .ANTs.ANTs import ANTsRegistrator except ImportError: warnings.warn( - "ANTS package not found. If you want to use it, please install it using 'pip install antspyx'" + "ANTS package not found. If you want to use it, please install it using 'pip install brainles_preprocessing[ants]'" ) from .niftyreg.niftyreg import NiftyRegRegistrator From 197c02bee2a842f54976fb8b59ceb2a6ebfdea08 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 09:48:28 +0100 Subject: [PATCH 19/30] introduce all Signed-off-by: neuronflow --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a7e5b42..899614b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,12 +57,14 @@ tqdm = "^4.64.1" auxiliary = "^0.0.41" rich = "^13.6.0" - +# optional registration backends antspyx = { version = "^0.4.2", optional = true } [tool.poetry.extras] +all = ["antspyx"] ants = ["antspyx"] + [tool.poetry.dev-dependencies] pytest = "^6.2" From 06e627b9c0c657479940966654e5bf497a9ccc7f Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 10:15:33 +0100 Subject: [PATCH 20/30] add comments so we understand our own code :) Co-authored-by: Isra Mekki Signed-off-by: neuronflow --- brainles_preprocessing/registration/ANTs/ANTs.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 326c313..335d7f0 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -45,7 +45,7 @@ def register( transformed_image_path: str, matrix_path: str, log_file_path: str, - **kwargs + **kwargs, ) -> None: """ Register images using ANTs. @@ -56,8 +56,9 @@ def register( transformed_image_path (str): Path to the transformed image (output). matrix_path (str): Path to the transformation matrix (output). log_file_path (str): Path to the log file. - **kwargs: Additional registration parameters. + **kwargs: Additional registration parameters to update the instantiated defaults. """ + # we update the transformation parameters with the provided kwargs registration_kwargs = {**self.registration_params, **kwargs} transformed_image_path = turbopath(transformed_image_path) @@ -86,7 +87,7 @@ def transform( transformed_image_path: str, matrix_path: str, log_file_path: str, - **kwargs + **kwargs, ) -> None: """ Apply a transformation using ANTs. @@ -97,8 +98,9 @@ def transform( transformed_image_path (str): Path to the transformed image (output). matrix_path (str): Path to the transformation matrix. log_file_path (str): Path to the log file. - **kwargs: Additional transformation parameters. + **kwargs: Additional transformation parameters to update the instantiated defaults. """ + # we update the transformation parameters with the provided kwargs transform_kwargs = {**self.transformation_params, **kwargs} fixed_image = ants.image_read(fixed_image_path) moving_image = ants.image_read(moving_image_path) From 66eb52e0318c7be9473dcefa11e3982c0e55bc3e Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 10:19:33 +0100 Subject: [PATCH 21/30] clean Signed-off-by: neuronflow --- tests/test_niftyreg_registrator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_niftyreg_registrator.py b/tests/test_niftyreg_registrator.py index 94bcaca..3458fbe 100644 --- a/tests/test_niftyreg_registrator.py +++ b/tests/test_niftyreg_registrator.py @@ -28,7 +28,6 @@ def tearDown(self): shutil.rmtree(self.output_dir) def test_register_creates_output_files(self): - # we try to run the fastest possible skullstripping on GPU self.registrator.register( fixed_image_path=self.fixed_image, moving_image_path=self.moving_image, From 13036c43298b9006c4d8bfd1995291b10e265fd8 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 10:24:32 +0100 Subject: [PATCH 22/30] introduce tests for ANTs Signed-off-by: neuronflow --- tests/test_niftyreg_registrator.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tests/test_niftyreg_registrator.py b/tests/test_niftyreg_registrator.py index 3458fbe..cd6fefa 100644 --- a/tests/test_niftyreg_registrator.py +++ b/tests/test_niftyreg_registrator.py @@ -1,20 +1,27 @@ import os import shutil import unittest +from abc import abstractmethod from auxiliary.turbopath import turbopath -from brainles_preprocessing.registration.niftyreg.niftyreg import NiftyRegRegistrator +from brainles_preprocessing.registration.ANTs.ANTs import ANTsRegistrator +from brainles_preprocessing.registration.niftyreg.niftyreg import \ + NiftyRegRegistrator -class TestNiftyRegRegistrator(unittest.TestCase): +class TestRegistratorBase(unittest.TestCase): + @abstractmethod + def get_registrator(self): + pass + def setUp(self): test_data_dir = turbopath(__file__).parent + "/test_data" input_dir = test_data_dir + "/input" self.output_dir = test_data_dir + "/temp_output_niftyreg" os.makedirs(self.output_dir, exist_ok=True) - self.registrator = NiftyRegRegistrator() + self.registrator = get_registrator() self.fixed_image = input_dir + "/tcia_example_t1c.nii.gz" self.moving_image = input_dir + "/bet_tcia_example_t1c_mask.nii.gz" @@ -53,3 +60,13 @@ def test_register_creates_output_files(self): # TODO also test transform + + +class TestANTsRegistratorBase(TestRegistratorBase): + def get_registrator(self): + return ANTsRegistrator() + + +class TestNiftyRegRegistratorRegistratorBase(TestRegistratorBase): + def get_registrator(self): + return NiftyRegRegistrator() From d0ad94dac68ed5dded5433508e8d861c7d963d03 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 10:25:37 +0100 Subject: [PATCH 23/30] registrator tests Signed-off-by: neuronflow --- tests/{test_niftyreg_registrator.py => test_registrators.py} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename tests/{test_niftyreg_registrator.py => test_registrators.py} (97%) diff --git a/tests/test_niftyreg_registrator.py b/tests/test_registrators.py similarity index 97% rename from tests/test_niftyreg_registrator.py rename to tests/test_registrators.py index cd6fefa..79b27a3 100644 --- a/tests/test_niftyreg_registrator.py +++ b/tests/test_registrators.py @@ -21,7 +21,7 @@ def setUp(self): self.output_dir = test_data_dir + "/temp_output_niftyreg" os.makedirs(self.output_dir, exist_ok=True) - self.registrator = get_registrator() + self.registrator = self.get_registrator() self.fixed_image = input_dir + "/tcia_example_t1c.nii.gz" self.moving_image = input_dir + "/bet_tcia_example_t1c_mask.nii.gz" @@ -66,7 +66,6 @@ class TestANTsRegistratorBase(TestRegistratorBase): def get_registrator(self): return ANTsRegistrator() - class TestNiftyRegRegistratorRegistratorBase(TestRegistratorBase): def get_registrator(self): return NiftyRegRegistrator() From 7ce7a04766f2bbaeba4573e5dabf60934d1aaff6 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:08:06 +0100 Subject: [PATCH 24/30] basic logging Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index 335d7f0..d947609 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -1,4 +1,5 @@ # TODO add typing and docs +import datetime import os import shutil @@ -59,6 +60,9 @@ def register( **kwargs: Additional registration parameters to update the instantiated defaults. """ # we update the transformation parameters with the provided kwargs + + start_time = datetime.datetime.now() + registration_kwargs = {**self.registration_params, **kwargs} transformed_image_path = turbopath(transformed_image_path) @@ -78,7 +82,19 @@ def register( ants.image_write(transformed_image, transformed_image_path) os.makedirs(matrix_path.parent, exist_ok=True) shutil.copyfile(registration_result["fwdtransforms"][0], matrix_path) - # TODO logging + + end_time = datetime.datetime.now() + + # TODO nicer logging + # we create a dummy log file for the moment to pass the tests + with open(log_file_path, "w") as f: + f.write("*** Registration with antspyx at: ***\n") + f.write(f"start time: {start_time} \n") + f.write(f"fixed image: {fixed_image_path} \n") + f.write(f"moving image: {moving_image_path} \n") + f.write(f"transformed image: {transformed_image_path} \n") + f.write(f"matrix: {matrix_path} \n") + f.write(f"end time: {end_time} \n") def transform( self, @@ -100,6 +116,8 @@ def transform( log_file_path (str): Path to the log file. **kwargs: Additional transformation parameters to update the instantiated defaults. """ + start_time = datetime.datetime.now() + # we update the transformation parameters with the provided kwargs transform_kwargs = {**self.transformation_params, **kwargs} fixed_image = ants.image_read(fixed_image_path) @@ -117,7 +135,19 @@ def transform( **transform_kwargs, ) ants.image_write(transformed_image, transformed_image_path) - # TODO logging + + end_time = datetime.datetime.now() + + # TODO nicer logging + # we create a dummy log file for the moment to pass the tests + with open(log_file_path, "w") as f: + f.write("*** Registration with antspyx at: ***\n") + f.write(f"start time: {start_time} \n") + f.write(f"fixed image: {fixed_image_path} \n") + f.write(f"moving image: {moving_image_path} \n") + f.write(f"transformed image: {transformed_image_path} \n") + f.write(f"matrix: {matrix_path} \n") + f.write(f"end time: {end_time} \n") if __name__ == "__main__": From 36750ebadc92f1d1af92330ec7d996f4ce074452 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:42:33 +0100 Subject: [PATCH 25/30] create matrixes for transform tests Signed-off-by: neuronflow --- tests/test_data/input/matrix.mat | Bin 0 -> 132 bytes tests/test_data/input/niftyreg_matrix.txt | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 tests/test_data/input/matrix.mat create mode 100644 tests/test_data/input/niftyreg_matrix.txt diff --git a/tests/test_data/input/matrix.mat b/tests/test_data/input/matrix.mat new file mode 100644 index 0000000000000000000000000000000000000000..d5e7571991b35419381c159cdc74893420fb746c GIT binary patch literal 132 zcmd;LU|`?@Vn!ea0VyDMOiRnmOARSX%qvdIFUpNi%gIkHi8qcnW|;A}-kxd8HfvGA ztGg#0+PHh$+j@Jah&?uDrZ=ryc=qh^0`hZS+_(F%!)EV?sIvWBK+~9k7-l*fke!xU Uk($C#lJ~$a_e|fx>#os`07|bbq5uE@ literal 0 HcmV?d00001 diff --git a/tests/test_data/input/niftyreg_matrix.txt b/tests/test_data/input/niftyreg_matrix.txt new file mode 100644 index 0000000..e4ca998 --- /dev/null +++ b/tests/test_data/input/niftyreg_matrix.txt @@ -0,0 +1,4 @@ +0.9999356 0.009583665 0.006066407 -0.2090089 +-0.009398761 0.9995115 -0.02980891 -0.7436395 +-0.006349129 0.02974998 0.9995372 0.3484979 +0 0 0 1 From c2cef507f9c585323c085450e0e272f31b43d0a8 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:44:43 +0100 Subject: [PATCH 26/30] logging Signed-off-by: neuronflow --- .../registration/ANTs/ANTs.py | 57 ++++++++++++++++--- .../input/{niftyreg_matrix.txt => matrix.txt} | 0 2 files changed, 48 insertions(+), 9 deletions(-) rename tests/test_data/input/{niftyreg_matrix.txt => matrix.txt} (100%) diff --git a/brainles_preprocessing/registration/ANTs/ANTs.py b/brainles_preprocessing/registration/ANTs/ANTs.py index d947609..0d3b214 100644 --- a/brainles_preprocessing/registration/ANTs/ANTs.py +++ b/brainles_preprocessing/registration/ANTs/ANTs.py @@ -87,14 +87,17 @@ def register( # TODO nicer logging # we create a dummy log file for the moment to pass the tests - with open(log_file_path, "w") as f: - f.write("*** Registration with antspyx at: ***\n") - f.write(f"start time: {start_time} \n") - f.write(f"fixed image: {fixed_image_path} \n") - f.write(f"moving image: {moving_image_path} \n") - f.write(f"transformed image: {transformed_image_path} \n") - f.write(f"matrix: {matrix_path} \n") - f.write(f"end time: {end_time} \n") + + self._log_to_file( + log_file_path=log_file_path, + fixed_image_path=fixed_image_path, + moving_image_path=moving_image_path, + transformed_image_path=transformed_image_path, + matrix_path=matrix_path, + operation_name="registration", + start_time=start_time, + end_time=end_time, + ) def transform( self, @@ -140,14 +143,50 @@ def transform( # TODO nicer logging # we create a dummy log file for the moment to pass the tests + + self._log_to_file( + log_file_path=log_file_path, + fixed_image_path=fixed_image_path, + moving_image_path=moving_image_path, + transformed_image_path=transformed_image_path, + matrix_path=matrix_path, + operation_name="transformation", + start_time=start_time, + end_time=end_time, + ) + + @staticmethod + def _log_to_file( + log_file_path: str, + fixed_image_path: str, + moving_image_path: str, + transformed_image_path: str, + matrix_path: str, + operation_name: str, + start_time, + end_time, + ): + + # Calculate the duration and make it human readable + duration = (end_time - start_time).total_seconds() + + hours = int(duration // 3600) + minutes = int((duration % 3600) // 60) + seconds = int(duration % 60) + milliseconds = int((duration - int(duration)) * 1000) + + # Format the duration as "0:0:0:0" + duration_formatted = f"{hours}h {minutes}m {seconds}s {milliseconds}ms" + with open(log_file_path, "w") as f: - f.write("*** Registration with antspyx at: ***\n") + f.write(f"*** {operation_name} with antspyx ***\n") f.write(f"start time: {start_time} \n") f.write(f"fixed image: {fixed_image_path} \n") f.write(f"moving image: {moving_image_path} \n") f.write(f"transformed image: {transformed_image_path} \n") f.write(f"matrix: {matrix_path} \n") f.write(f"end time: {end_time} \n") + f.write(f"duration: {duration_formatted}\n") if __name__ == "__main__": diff --git a/tests/test_data/input/niftyreg_matrix.txt b/tests/test_data/input/matrix.txt similarity index 100% rename from tests/test_data/input/niftyreg_matrix.txt rename to tests/test_data/input/matrix.txt From 8fadd543d115e331717b508a926c71e7c6cc84bb Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:45:11 +0100 Subject: [PATCH 27/30] registration and transformation tests Signed-off-by: neuronflow --- tests/test_registrators.py | 67 +++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/tests/test_registrators.py b/tests/test_registrators.py index 79b27a3..bcc18e1 100644 --- a/tests/test_registrators.py +++ b/tests/test_registrators.py @@ -6,55 +6,83 @@ from auxiliary.turbopath import turbopath from brainles_preprocessing.registration.ANTs.ANTs import ANTsRegistrator -from brainles_preprocessing.registration.niftyreg.niftyreg import \ - NiftyRegRegistrator +from brainles_preprocessing.registration.niftyreg.niftyreg import NiftyRegRegistrator -class TestRegistratorBase(unittest.TestCase): +class RegistratorBase: @abstractmethod def get_registrator(self): pass + @abstractmethod + def get_method_and_extension(self): + pass + def setUp(self): + self.registrator = self.get_registrator() + self.method_name, self.matrix_extension = self.get_method_and_extension() + test_data_dir = turbopath(__file__).parent + "/test_data" input_dir = test_data_dir + "/input" - self.output_dir = test_data_dir + "/temp_output_niftyreg" + self.output_dir = test_data_dir + f"/temp_output_{self.method_name}" os.makedirs(self.output_dir, exist_ok=True) - self.registrator = self.get_registrator() - self.fixed_image = input_dir + "/tcia_example_t1c.nii.gz" self.moving_image = input_dir + "/bet_tcia_example_t1c_mask.nii.gz" - self.transformed_image = self.output_dir + "/transformed_image.nii.gz" - self.matrix = self.output_dir + "/matrix.txt" - self.log_file = self.output_dir + "/registration.log" + self.matrix = self.output_dir + "/matrix" + self.transform_matrix = f"{self.matrix}.{self.matrix_extension}" def tearDown(self): # Clean up created files if they exist shutil.rmtree(self.output_dir) def test_register_creates_output_files(self): + transformed_image = self.output_dir + "/registered_image.nii.gz" + log_file = self.output_dir + "/registration.log" + self.registrator.register( fixed_image_path=self.fixed_image, moving_image_path=self.moving_image, - transformed_image_path=self.transformed_image, + transformed_image_path=transformed_image, matrix_path=self.matrix, - log_file_path=self.log_file, + log_file_path=log_file, ) self.assertTrue( - os.path.exists(self.transformed_image), + os.path.exists(transformed_image), "transformed file was not created.", ) self.assertTrue( - os.path.exists(self.matrix), + os.path.exists(f"{self.matrix}.{self.matrix_extension}"), "matrix file was not created.", ) self.assertTrue( - os.path.exists(self.log_file), + os.path.exists(log_file), + "log file was not created.", + ) + + def test_transform_creates_output_files(self): + transformed_image = self.output_dir + "/transformed_image.nii.gz" + log_file = self.output_dir + "/transformation.log" + + self.registrator.transform( + fixed_image_path=self.fixed_image, + moving_image_path=self.moving_image, + transformed_image_path=transformed_image, + matrix_path=self.transform_matrix, + log_file_path=log_file, + ) + + self.assertTrue( + os.path.exists(transformed_image), + "transformed file was not created.", + ) + + self.assertTrue( + os.path.exists(log_file), "log file was not created.", ) @@ -62,10 +90,17 @@ def test_register_creates_output_files(self): # TODO also test transform -class TestANTsRegistratorBase(TestRegistratorBase): +class TestANTsRegistrator(RegistratorBase, unittest.TestCase): def get_registrator(self): return ANTsRegistrator() -class TestNiftyRegRegistratorRegistratorBase(TestRegistratorBase): + def get_method_and_extension(self): + return "ants", "mat" + + +class TestNiftyRegRegistratorRegistrator(RegistratorBase, unittest.TestCase): def get_registrator(self): return NiftyRegRegistrator() + + def get_method_and_extension(self): + return "niftyreg", "txt" From 45f24581e835ca54a66b7c412712a207f9f252f3 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:50:13 +0100 Subject: [PATCH 28/30] working tests Signed-off-by: neuronflow --- tests/test_registrators.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_registrators.py b/tests/test_registrators.py index bcc18e1..1eadf91 100644 --- a/tests/test_registrators.py +++ b/tests/test_registrators.py @@ -6,7 +6,8 @@ from auxiliary.turbopath import turbopath from brainles_preprocessing.registration.ANTs.ANTs import ANTsRegistrator -from brainles_preprocessing.registration.niftyreg.niftyreg import NiftyRegRegistrator +from brainles_preprocessing.registration.niftyreg.niftyreg import \ + NiftyRegRegistrator class RegistratorBase: @@ -31,7 +32,7 @@ def setUp(self): self.moving_image = input_dir + "/bet_tcia_example_t1c_mask.nii.gz" self.matrix = self.output_dir + "/matrix" - self.transform_matrix = f"{self.matrix}.{self.matrix_extension}" + self.transform_matrix = input_dir + f"/matrix.{self.matrix_extension}" def tearDown(self): # Clean up created files if they exist From df8dca292386bbe270b53d5e99d36125f6ff41c0 Mon Sep 17 00:00:00 2001 From: "brainless-bot[bot]" <153751247+brainless-bot[bot]@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:51:47 +0000 Subject: [PATCH 29/30] Autoformat with black --- tests/test_registrators.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_registrators.py b/tests/test_registrators.py index 1eadf91..1661926 100644 --- a/tests/test_registrators.py +++ b/tests/test_registrators.py @@ -6,8 +6,7 @@ from auxiliary.turbopath import turbopath from brainles_preprocessing.registration.ANTs.ANTs import ANTsRegistrator -from brainles_preprocessing.registration.niftyreg.niftyreg import \ - NiftyRegRegistrator +from brainles_preprocessing.registration.niftyreg.niftyreg import NiftyRegRegistrator class RegistratorBase: From d7731e86cb39d02b1af58d8cb3bc9f80d49731c0 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 1 Mar 2024 11:56:53 +0100 Subject: [PATCH 30/30] change tests to contain all dependencies Signed-off-by: neuronflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8617802..fb59acb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install flake8 pytest - pip install -e . + pip install -e .[all] - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names