From 64a908abd381d6871c0aaa8d2621562400803750 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 15:37:58 +0100 Subject: [PATCH 1/8] add todo for later Signed-off-by: neuronflow --- ereg/registration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ereg/registration.py b/ereg/registration.py index f45baa0..2b23045 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -188,6 +188,7 @@ def update_parameters(self, config_file: Union[str, dict], **kwargs): ) # this is taken directly from the sample_config.yaml + # TODO this is ugly we should probbaly read these defaults from a file? default_optimizer_parameters = { "min_step": 1e-6, # regular_step_gradient_descent "max_step": 1.0, # gradient_descent, regular_step_gradient_descent From 4547d5251665f2897e0aad56e20c036cd80b80f8 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 16:26:31 +0100 Subject: [PATCH 2/8] reading defaults from file Signed-off-by: neuronflow --- ereg/configurations/default_config.yaml | 95 +++++++++++++++++++++++++ ereg/registration.py | 24 ++++++- pyproject.toml | 1 + 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 ereg/configurations/default_config.yaml diff --git a/ereg/configurations/default_config.yaml b/ereg/configurations/default_config.yaml new file mode 100644 index 0000000..c038567 --- /dev/null +++ b/ereg/configurations/default_config.yaml @@ -0,0 +1,95 @@ +# TODO adjust default parameters! +# Whether to use bias correction before registration (helpful to generate more accurate transforms at the cost of time). Default: false +bias: false + +# The registration metric. Options: ["mattes_mutual_information", "ants_neighborhood_correlation", "correlation", "demons", "joint_histogram_mutual_information", "mean_squares"] +# this can be a list as well, which will then become a multi-metric registration using composite transforms [https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1CompositeTransform.html] +metric: mattes + +## metric-specific parameters +metric_parameters: { + histogram_bins: 50, # mattes_mutual_information, joint_histogram_mutual_information + radius: 5, # ants_neighborhood_correlation + intensityDifferenceThreshold: 0.001, # demons + varianceForJointPDFSmoothing: 1.5, # joint_histogram_mutual_information +} + +# Optimizer. Options: ["gradient_descent", "regular_step_gradient_descent", "amoeba", "conjugate", "exhaustive", "gadient_descent_line_search", "lbfgsb", "lbfgsb2", "one_plus_one_evolutionary", "powell"] +optimizer: regular_step_gradient_descent + +## optimizer-specific parameters +optimizer_parameters: { + min_step: 1e-6, # regular_step_gradient_descent + max_step: 1.0, # gradient_descent, regular_step_gradient_descent + maximumStepSizeInPhysicalUnits: 1.0, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, + iterations: 1000, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, conjugate, lbfgsb, lbfgsb2 + learningrate: 1.0, # gradient_descent, gradient_descent_line_search + convergence_minimum: 1e-6, # gradient_descent, gradient_descent_line_search + convergence_window_size: 10, # gradient_descent, gradient_descent_line_search + line_search_lower_limit: 0.0, # gradient_descent_line_search + line_search_upper_limit: 5.0, # gradient_descent_line_search + line_search_epsilon: 0.01, # gradient_descent_line_search + step_length: 0.1, # conjugate, exhaustive, powell + simplex_delta: 0.1, # amoeba + maximum_number_of_corrections: 5, # lbfgsb, lbfgsb2 + maximum_number_of_function_evaluations: 2000, # lbfgsb, lbfgsb2 + solution_accuracy: 1e-5, # lbfgsb2 + hessian_approximate_accuracy: 1e-5, # lbfgsb2 + delta_convergence_distance: 1e-5, # lbfgsb2 + delta_convergence_tolerance: 1e-5, # lbfgsb2 + line_search_maximum_evaluations: 50, # lbfgsb2 + line_search_minimum_step: 1e-20, # lbfgsb2 + line_search_accuracy: 1e-4, # lbfgsb2 + epsilon: 1e-8, # one_plus_one_evolutionary + initial_radius: 1.0, # one_plus_one_evolutionary + growth_factor: -1.0, # one_plus_one_evolutionary + shrink_factor: -1.0, # one_plus_one_evolutionary + maximum_line_iterations: 100, # powell + step_tolerance: 1e-6, # powell + value_tolerance: 1e-6, # powell +} + +# The registration transform. Options: ["translation", "versor", "versor_rigid", "euler", "similarity", "scale", "scale_versor", "scale_skew_versor", "affine", "bspline", "displacement"] +transform: versor + +# Composite transform +composite_transform: false + +# Previous transforms saved to disk: only used if composite_transform is true +previous_transforms: [] + +# Transform initialization. Options: ["moments", "geometry", "selfmoments", "selfgeometry"] +initialization: moments + +# Interpolator. Options: ["linear", "bspline", "nearestneighbor", "gaussian", "labelgaussian"] +interpolator: linear + +# Sampling strategy. Options: ["regular", "random", "none"] +sampling_strategy: none + +# Sampling percentage. Can be a list of percentages with the same length as the number of levels. +sampling_percentage: 0.5 + +# Registration relaxation factor. +relaxation: 0.5 + +# Registration gradient tolerance +tolerance: 1e-4 + +# Maximum number of iterations at each level. +max_step: 5.0 + +# Minimum step size at each level. +min_step: 1e-5 + +# Shrink factor at each level for pyramid registration. +shrink_factors: [2, 1] + +# Smoothing sigma at each level for pyramid registration. +smoothing_sigmas: [1, 0] + +# The number of attempts to try to find a good registration (useful when using random sampling) +attempts: 5 + +# Total number of iterations. +iterations: 1000 diff --git a/ereg/registration.py b/ereg/registration.py index 2b23045..87cb58d 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -7,6 +7,7 @@ # from pprint import pprint import SimpleITK as sitk import yaml +from auxiliary.turbopath.turbopath import turbopath from ereg.utils.io import read_image_and_cast_to_32bit_float from ereg.utils.metrics import get_ssim @@ -15,7 +16,7 @@ class RegistrationClass: def __init__( self, - config_file: Union[str, dict] = None, + configuration: Union[str, dict] = None, **kwargs, ) -> None: """ @@ -65,8 +66,25 @@ def __init__( ] self.total_attempts = 5 self.transform = None - if config_file is not None: - self.update_parameters(config_file) + + if configuration is not None: + self.update_parameters(configuration) + else: + self.parameters = self._generate_default_parameters() + + def _generate_default_parameters(self) -> dict: + defaults_file = turbopath(__file__ + "configurations/default_config.yaml") + default_parameters = self.parameters = yaml.safe_load(open(defaults_file, "r")) + return default_parameters + + @property + def configuration(self) -> dict: + return self.parameters + + @configuration.setter + def configuration(self, new_config_file: Union[str, dict]) -> None: + self.parameters = self._generate_default_parameters() + self.update_parameters(config_file=new_config_file) def update_parameters(self, config_file: Union[str, dict], **kwargs): """ diff --git a/pyproject.toml b/pyproject.toml index 61363df..c53eb28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,6 +27,7 @@ rich = "^13.6.0" scikit-image = "^0.21.0" tqdm = "^4.64.1" SimpleITK = "^2.3.1" +auxiliary = "^0.0.40" [tool.poetry.dev-dependencies] pytest = "^6.2.5" From 07256d5095c8cd6a53633814809878b58a991107 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 16:27:35 +0100 Subject: [PATCH 3/8] new: add todo Signed-off-by: neuronflow --- ereg/registration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ereg/registration.py b/ereg/registration.py index 87cb58d..5e4b68e 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -93,6 +93,7 @@ def update_parameters(self, config_file: Union[str, dict], **kwargs): Args: config_file (Union[str, dict]): The config file or dictionary. """ + # TODO rewrite this function to only update specific entries which are given in the dict if isinstance(config_file, str): self.parameters = yaml.safe_load(open(config_file, "r")) elif isinstance(config_file, dict): From 03b09b14a6e11bb04224d8ddc319d836cce0247b Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 16:33:25 +0100 Subject: [PATCH 4/8] gets rid of auxiliary requirement to still allow python 3.8 legacy users Signed-off-by: neuronflow --- ereg/registration.py | 7 +++++-- pyproject.toml | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ereg/registration.py b/ereg/registration.py index 5e4b68e..d3244b7 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -7,7 +7,6 @@ # from pprint import pprint import SimpleITK as sitk import yaml -from auxiliary.turbopath.turbopath import turbopath from ereg.utils.io import read_image_and_cast_to_32bit_float from ereg.utils.metrics import get_ssim @@ -73,7 +72,11 @@ def __init__( self.parameters = self._generate_default_parameters() def _generate_default_parameters(self) -> dict: - defaults_file = turbopath(__file__ + "configurations/default_config.yaml") + defaults_file = os.path.normpath( + os.path.abspath( + __file__ + "configurations/default_config.yaml", + ) + ) default_parameters = self.parameters = yaml.safe_load(open(defaults_file, "r")) return default_parameters diff --git a/pyproject.toml b/pyproject.toml index c53eb28..61363df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ rich = "^13.6.0" scikit-image = "^0.21.0" tqdm = "^4.64.1" SimpleITK = "^2.3.1" -auxiliary = "^0.0.40" [tool.poetry.dev-dependencies] pytest = "^6.2.5" From e4973e4cc8b74c7a1d7ebef8e4e21a4d3e869f09 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 17:39:01 +0100 Subject: [PATCH 5/8] ymls Signed-off-by: neuronflow --- ereg/configurations/default_config.yaml | 95 ------------------- .../configurations}/default_rigid.yaml | 0 .../configurations}/sample_config.yaml | 0 3 files changed, 95 deletions(-) delete mode 100644 ereg/configurations/default_config.yaml rename {data => ereg/configurations}/default_rigid.yaml (100%) rename {data => ereg/configurations}/sample_config.yaml (100%) diff --git a/ereg/configurations/default_config.yaml b/ereg/configurations/default_config.yaml deleted file mode 100644 index c038567..0000000 --- a/ereg/configurations/default_config.yaml +++ /dev/null @@ -1,95 +0,0 @@ -# TODO adjust default parameters! -# Whether to use bias correction before registration (helpful to generate more accurate transforms at the cost of time). Default: false -bias: false - -# The registration metric. Options: ["mattes_mutual_information", "ants_neighborhood_correlation", "correlation", "demons", "joint_histogram_mutual_information", "mean_squares"] -# this can be a list as well, which will then become a multi-metric registration using composite transforms [https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1CompositeTransform.html] -metric: mattes - -## metric-specific parameters -metric_parameters: { - histogram_bins: 50, # mattes_mutual_information, joint_histogram_mutual_information - radius: 5, # ants_neighborhood_correlation - intensityDifferenceThreshold: 0.001, # demons - varianceForJointPDFSmoothing: 1.5, # joint_histogram_mutual_information -} - -# Optimizer. Options: ["gradient_descent", "regular_step_gradient_descent", "amoeba", "conjugate", "exhaustive", "gadient_descent_line_search", "lbfgsb", "lbfgsb2", "one_plus_one_evolutionary", "powell"] -optimizer: regular_step_gradient_descent - -## optimizer-specific parameters -optimizer_parameters: { - min_step: 1e-6, # regular_step_gradient_descent - max_step: 1.0, # gradient_descent, regular_step_gradient_descent - maximumStepSizeInPhysicalUnits: 1.0, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, - iterations: 1000, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, conjugate, lbfgsb, lbfgsb2 - learningrate: 1.0, # gradient_descent, gradient_descent_line_search - convergence_minimum: 1e-6, # gradient_descent, gradient_descent_line_search - convergence_window_size: 10, # gradient_descent, gradient_descent_line_search - line_search_lower_limit: 0.0, # gradient_descent_line_search - line_search_upper_limit: 5.0, # gradient_descent_line_search - line_search_epsilon: 0.01, # gradient_descent_line_search - step_length: 0.1, # conjugate, exhaustive, powell - simplex_delta: 0.1, # amoeba - maximum_number_of_corrections: 5, # lbfgsb, lbfgsb2 - maximum_number_of_function_evaluations: 2000, # lbfgsb, lbfgsb2 - solution_accuracy: 1e-5, # lbfgsb2 - hessian_approximate_accuracy: 1e-5, # lbfgsb2 - delta_convergence_distance: 1e-5, # lbfgsb2 - delta_convergence_tolerance: 1e-5, # lbfgsb2 - line_search_maximum_evaluations: 50, # lbfgsb2 - line_search_minimum_step: 1e-20, # lbfgsb2 - line_search_accuracy: 1e-4, # lbfgsb2 - epsilon: 1e-8, # one_plus_one_evolutionary - initial_radius: 1.0, # one_plus_one_evolutionary - growth_factor: -1.0, # one_plus_one_evolutionary - shrink_factor: -1.0, # one_plus_one_evolutionary - maximum_line_iterations: 100, # powell - step_tolerance: 1e-6, # powell - value_tolerance: 1e-6, # powell -} - -# The registration transform. Options: ["translation", "versor", "versor_rigid", "euler", "similarity", "scale", "scale_versor", "scale_skew_versor", "affine", "bspline", "displacement"] -transform: versor - -# Composite transform -composite_transform: false - -# Previous transforms saved to disk: only used if composite_transform is true -previous_transforms: [] - -# Transform initialization. Options: ["moments", "geometry", "selfmoments", "selfgeometry"] -initialization: moments - -# Interpolator. Options: ["linear", "bspline", "nearestneighbor", "gaussian", "labelgaussian"] -interpolator: linear - -# Sampling strategy. Options: ["regular", "random", "none"] -sampling_strategy: none - -# Sampling percentage. Can be a list of percentages with the same length as the number of levels. -sampling_percentage: 0.5 - -# Registration relaxation factor. -relaxation: 0.5 - -# Registration gradient tolerance -tolerance: 1e-4 - -# Maximum number of iterations at each level. -max_step: 5.0 - -# Minimum step size at each level. -min_step: 1e-5 - -# Shrink factor at each level for pyramid registration. -shrink_factors: [2, 1] - -# Smoothing sigma at each level for pyramid registration. -smoothing_sigmas: [1, 0] - -# The number of attempts to try to find a good registration (useful when using random sampling) -attempts: 5 - -# Total number of iterations. -iterations: 1000 diff --git a/data/default_rigid.yaml b/ereg/configurations/default_rigid.yaml similarity index 100% rename from data/default_rigid.yaml rename to ereg/configurations/default_rigid.yaml diff --git a/data/sample_config.yaml b/ereg/configurations/sample_config.yaml similarity index 100% rename from data/sample_config.yaml rename to ereg/configurations/sample_config.yaml From 2669a17397dce44a3d72e008f906af840edeb04f Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 22:58:52 +0100 Subject: [PATCH 6/8] change name of yml Signed-off-by: neuronflow --- ereg/registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ereg/registration.py b/ereg/registration.py index afb1c34..26ec582 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -74,7 +74,7 @@ def __init__( def _generate_default_parameters(self) -> dict: defaults_file = os.path.normpath( os.path.abspath( - __file__ + "configurations/default_config.yaml", + __file__ + "configurations/default_rigid.yaml", ) ) default_parameters = self.parameters = yaml.safe_load(open(defaults_file, "r")) From fc92336c2bd00814b84293f76961a20972203da3 Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 23:10:54 +0100 Subject: [PATCH 7/8] be consistent Signed-off-by: neuronflow --- ereg/functional.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ereg/functional.py b/ereg/functional.py index 5469e27..af946af 100644 --- a/ereg/functional.py +++ b/ereg/functional.py @@ -10,7 +10,7 @@ def registration_function( target_image: Union[str, sitk.Image], moving_image: Union[str, sitk.Image], output_image: str, - config_file: str, + configuration: str, transform_file: str = None, log_file: str = None, **kwargs, @@ -28,14 +28,14 @@ def registration_function( Returns: float: The structural similarity index. """ - if isinstance(config_file, str): - assert os.path.isfile(config_file), "Config file does not exist." - elif isinstance(config_file, dict): + if isinstance(configuration, str): + assert os.path.isfile(configuration), "Config file does not exist." + elif isinstance(configuration, dict): pass else: raise ValueError("Config file must be a string or dictionary.") - registration_obj = RegistrationClass(config_file) + registration_obj = RegistrationClass(configuration) registration_obj.register( target_image=target_image, moving_image=moving_image, From 896af5eb7ba7d67d5b9b638dce11c92e3334faea Mon Sep 17 00:00:00 2001 From: neuronflow Date: Fri, 15 Dec 2023 23:40:19 +0100 Subject: [PATCH 8/8] hmm this should update the dict Signed-off-by: neuronflow --- ereg/registration.py | 174 +++++-------------------------------------- 1 file changed, 20 insertions(+), 154 deletions(-) diff --git a/ereg/registration.py b/ereg/registration.py index 74de214..1bc8e3e 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -85,170 +85,36 @@ def configuration(self) -> dict: return self.parameters @configuration.setter - def configuration(self, new_config_file: Union[str, dict]) -> None: + def configuration( + self, + new_config_file: Union[str, dict], + ) -> None: self.parameters = self._generate_default_parameters() - self.update_parameters(config_file=new_config_file) + self.update_parameters(configuration=new_config_file) - def update_parameters(self, config_file: Union[str, dict], **kwargs): + def update_parameters( + self, + configuration: Union[str, dict], + ): """ Update the parameters for the registration. Args: - config_file (Union[str, dict]): The config file or dictionary. + config_file (Union[str, dict]): The tring path pointing to a .yml configuration file or configuration dictionary. """ - # TODO rewrite this function to only update specific entries which are given in the dict - if isinstance(config_file, str): - self.parameters = yaml.safe_load(open(config_file, "r")) - elif isinstance(config_file, dict): - self.parameters = config_file + if isinstance(configuration, str): + config_data = yaml.safe_load(open(configuration, "r")) + elif isinstance(configuration, dict): + config_data = configuration else: - raise ValueError("Config file must be a string or dictionary.") - - self.parameters["metric"] = ( - self.parameters.get("metric", "mean_squares") - .replace("_", "") - .replace("-", "") - .lower() - ) - self.parameters["metric_parameters"] = self.parameters.get( - "metric_parameters", {} - ) - self.parameters["metric_parameters"]["histogram_bins"] = self.parameters[ - "metric_parameters" - ].get("histogram_bins", 50) - self.parameters["metric_parameters"]["radius"] = self.parameters[ - "metric_parameters" - ].get("radius", 5) - self.parameters["metric_parameters"][ - "intensityDifferenceThreshold" - ] = self.parameters["metric_parameters"].get( - "intensityDifferenceThreshold", 0.001 - ) - self.parameters["metric_parameters"][ - "varianceForJointPDFSmoothing" - ] = self.parameters["metric_parameters"].get( - "varianceForJointPDFSmoothing", 1.5 - ) - - self.parameters["transform"] = ( - self.parameters.get("transform", "versor") - .replace("_", "") - .replace("-", "") - .lower() - ) - assert self.parameters["transform"] in self.available_transforms, ( - f"Transform {self.parameters['transform']} not recognized. " - f"Available transforms: {self.available_transforms}" - ) - if self.parameters["transform"] in ["euler", "versorrigid"]: - self.parameters["rigid_registration"] = True - self.parameters["initialization"] = self.parameters.get( - "initialization", "geometry" - ).lower() - self.parameters["bias_correct"] = self.parameters.get( - "bias_correct", self.parameters.get("bias", False) - ) - self.parameters["interpolator"] = ( - self.parameters.get("interpolator", "linear") - .replace("_", "") - .replace("-", "") - .lower() - ) - self.parameters["shrink_factors"] = self.parameters.get( - "shrink_factors", self.parameters.get("shrink", [8, 4, 2]) - ) - self.parameters["smoothing_sigmas"] = self.parameters.get( - "smoothing_sigmas", self.parameters.get("smooth", [3, 2, 1]) - ) - assert len(self.parameters["shrink_factors"]) == len( - self.parameters["smoothing_sigmas"] - ), "The number of shrink factors and smoothing sigmas must be the same." - self.parameters["sampling_strategy"] = self.parameters.get( - "sampling_strategy", "none" - ) - self.parameters["sampling_percentage"] = self.parameters.get( - "sampling_percentage", 0.01 - ) - if isinstance(self.parameters["sampling_percentage"], int) or isinstance( - self.parameters["sampling_percentage"], float - ): - temp_percentage = self.parameters["sampling_percentage"] - self.parameters["sampling_percentage"] = [] - for _ in range(len(self.parameters["shrink_factors"])): - self.parameters["sampling_percentage"].append(temp_percentage) - - assert len(self.parameters["shrink_factors"]) == len( - self.parameters["sampling_percentage"] - ), "The number of shrink factors and sampling percentages must be the same." - - self.parameters["attempts"] = self.parameters.get("attempts", 5) - - # check for composite transforms - self.parameters["composite_transform"] = self.parameters.get( - "composite_transform", None - ) - if self.parameters["composite_transform"]: - self.parameters["previous_transforms"] = self.parameters.get( - "previous_transforms", [] + raise ValueError( + "Configuration must be a string path pointing to a .yml file or dictionary." ) - # checks related to composite transforms - assert isinstance( - self.parameters["previous_transforms"], list - ), "Previous transforms must be a list." - assert ( - len(self.parameters["previous_transforms"]) > 0 - ), "No previous transforms provided." - - self.parameters["optimizer"] = self.parameters.get( - "optimizer", "regular_step_gradient_descent" - ) - - # this is taken directly from the sample_config.yaml - # TODO this is ugly we should probbaly read these defaults from a file? - default_optimizer_parameters = { - "min_step": 1e-6, # regular_step_gradient_descent - "max_step": 1.0, # gradient_descent, regular_step_gradient_descent - "maximumStepSizeInPhysicalUnits": 1.0, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, - "iterations": 1000, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, conjugate, lbfgsb, lbfgsb2 - "learningrate": 1.0, # gradient_descent, gradient_descent_line_search - "convergence_minimum": 1e-6, # gradient_descent, gradient_descent_line_search - "convergence_window_size": 10, # gradient_descent, gradient_descent_line_search - "line_search_lower_limit": 0.0, # gradient_descent_line_search - "line_search_upper_limit": 5.0, # gradient_descent_line_search - "line_search_epsilon": 0.01, # gradient_descent_line_search - "step_length": 0.1, # conjugate, exhaustive, powell - "simplex_delta": 0.1, # amoeba - "maximum_number_of_corrections": 5, # lbfgsb, lbfgsb2 - "maximum_number_of_function_evaluations": 2000, # lbfgsb, lbfgsb2 - "solution_accuracy": 1e-5, # lbfgsb2 - "hessian_approximate_accuracy": 1e-5, # lbfgsb2 - "delta_convergence_distance": 1e-5, # lbfgsb2 - "delta_convergence_tolerance": 1e-5, # lbfgsb2 - "line_search_maximum_evaluations": 50, # lbfgsb2 - "line_search_minimum_step": 1e-20, # lbfgsb2 - "line_search_accuracy": 1e-4, # lbfgsb2 - "epsilon": 1e-8, # one_plus_one_evolutionary - "initial_radius": 1.0, # one_plus_one_evolutionary - "growth_factor": -1.0, # one_plus_one_evolutionary - "shrink_factor": -1.0, # one_plus_one_evolutionary - "maximum_line_iterations": 100, # powell - "step_tolerance": 1e-6, # powell - "value_tolerance": 1e-6, # powell - "relaxation": 0.5, # regular_step_gradient_descent - "tolerance": 1e-4, # regular_step_gradient_descent - "rigid_registration": False, - } - - # check for optimizer parameters in config file - self.parameters["optimizer_parameters"] = self.parameters.get( - "optimizer_parameters", {} - ) - - # for any optimizer parameters not in the config file, use the default values - for key, value in default_optimizer_parameters.items(): - if key not in self.parameters["optimizer_parameters"]: - self.parameters["optimizer_parameters"][key] = value + # Update only the keys present in the YAML file + for key, value in config_data.items(): + if key in self.parameters: + self.parameters[key] = value def register( self,