diff --git a/README.md b/README.md index 4dd7614..2cc1f20 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,95 @@ ## Installation -With a Python 3.8+ environment, you can install eReg from [pypi.org](https://pypi.org/project/eReg/) +With a Python 3.8+ environment, you can install eReg from [pypi.org](https://pypi.org/project/eReg/). + +1. Create a virtual environment + +```sh +python3 -m venv venv_ereg ## using native python venv +# conda create -n venv_ereg python=3.8 ## using conda +``` + +2. Activate the virtual environment + +```sh +source venv_ereg/bin/activate ## using native python venv +# conda activate venv_ereg ## using conda +``` + +3. Install eReg ```sh pip install ereg ``` -## TODO - +## Extending eReg + +To extend eReg, you first need to install eReg from source. Clone the repository and install the package: + +```sh +git clone https://github.com/BrainLesion/eReg.git +cd eReg +pip install -e . +``` + +## Usage + +eReg can be used via the command line or as a Python package. + +### Command Line Interface + +The command line interface is available via the `ereg` command: + +```sh +(venv_ereg) ~> ereg -h +usage: eReg version0.0.4.post76.dev0+0d89ce7 [-h] -m -t -o -c [-tff] [-lf] [-gt] + +Simple registration. + +options: + -h, --help show this help message and exit + -m , --movingImg The moving image to register. Can be comma-separated list of images or directory of images. + -t , --targetImg The target image to register to. + -o , --output The output. Can be single file or a directory. + -c , --config The configuration file to use. + -tff , --transfile Registration transform file; if provided, will use this transform instead of computing a new one or will save. Defaults to None. + -lf , --log_file The log file to write to. Defaults to None. + -gt , --gt The ground truth image. +``` + +### Pythonic Interface + +The Pythonic interface is available via the `ereg` package, and can be used in two ways: as a functional interface or as an object-oriented interface. + +#### Functional Interface + +```python +from ereg import registration_function + +ssim = registration_function( + target_image=target_image_file, # the target image, which can be either a file or SimpleITK.Image object + moving_image=moving_image_file, # the moving image, which can be either a file or SimpleITK.Image object + output_image=output_file, # the output image to save the registered image to + transform_file=transform_file, # the transform file to save the transform to; if already present, will use this transform instead of computing a new one + log_file=log_file, # the log file to write to + configuration=configuration_file, # the configuration file to use to customize the registration, and is optional +) +``` + +#### Object-Oriented Interface + +```python +from ereg.registration import RegistrationClass + +registration_obj = RegistrationClass(configuration_file) # the configuration file to use to customize the registration, and is optional +registration_obj.register( + target_image=target_image_file, # the target image, which can be either a file or SimpleITK.Image object + moving_image=moving_image_file, # the moving image, which can be either a file or SimpleITK.Image object + output_image=output_file, # the output image to save the registered image to + transform_file=transform_file, # the transform file to save the transform to; if already present, will use this transform instead of computing a new one + log_file=log_file, # the log file to write to +) +``` + +## TODO diff --git a/ereg/configurations/default_rigid.yaml b/ereg/configurations/default_rigid.yaml index be634d0..f0328eb 100644 --- a/ereg/configurations/default_rigid.yaml +++ b/ereg/configurations/default_rigid.yaml @@ -1,23 +1,18 @@ # 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: mean_squares - ## metric-specific parameters metric_parameters: { + type: "mean_squares", # Options: ["mattes_mutual_information", "ants_neighborhood_correlation", "correlation", "demons", "joint_histogram_mutual_information", "mean_squares"] 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: { + type: "regular_step_gradient_descent", # Options: ["gradient_descent", "regular_step_gradient_descent", "amoeba", "conjugate", "exhaustive", "gradient_descent_line_search", "lbfgsb", "lbfgsb2", "one_plus_one_evolutionary", "powell"] min_step: 1e-4, # regular_step_gradient_descent max_step: 1.0, # gradient_descent, regular_step_gradient_descent iterations: 200, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, conjugate, lbfgsb, lbfgsb2 diff --git a/ereg/configurations/sample_config.yaml b/ereg/configurations/sample_config.yaml index 0c2ddb0..d65dd5a 100644 --- a/ereg/configurations/sample_config.yaml +++ b/ereg/configurations/sample_config.yaml @@ -1,23 +1,18 @@ # 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: { + type: "mean_squares", # Options: ["mattes_mutual_information", "ants_neighborhood_correlation", "correlation", "demons", "joint_histogram_mutual_information", "mean_squares"] 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: { + type: "regular_step_gradient_descent", # Options: ["gradient_descent", "regular_step_gradient_descent", "amoeba", "conjugate", "exhaustive", "gadient_descent_line_search", "lbfgsb", "lbfgsb2", "one_plus_one_evolutionary", "powell"] min_step: 1e-6, # regular_step_gradient_descent max_step: 1.0, # gradient_descent, regular_step_gradient_descent iterations: 1000, # regular_step_gradient_descent, gradient_descent_line_search, gradient_descent, conjugate, lbfgsb, lbfgsb2 diff --git a/ereg/functional.py b/ereg/functional.py index ef86138..4bed1f9 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, - configuration: str, + configuration: str = None, transform_file: str = None, log_file: str = None, **kwargs, @@ -22,18 +22,19 @@ def registration_function( target_image (Union[str, sitk.Image]): The target image. moving_image (Union[str, sitk.Image]): The moving image. output_image (str): The output image. - config_file (str): The config file for the registration. + config_file (str, optional): The config file for the registration. Defaults to None. transform_file (str, optional): The transform file. Defaults to None. Returns: float: The structural similarity index. """ - 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.") + if configuration is not None: + 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(configuration) registration_obj.register( diff --git a/ereg/registration.py b/ereg/registration.py index 0e0007b..acf0c31 100644 --- a/ereg/registration.py +++ b/ereg/registration.py @@ -107,7 +107,7 @@ def update_parameters( config_data = configuration else: raise ValueError( - "Configuration must be a string path pointing to a .yml file or dictionary." + "Configuration must be a string path pointing to a yaml/yml file or dictionary." ) # Update only the keys present in the YAML file @@ -294,8 +294,10 @@ def _get_transform_wrapper(self, transform: str, dim: int) -> sitk.Transform: else: return eval("sitk.Euler%dDTransform()" % (dim)) elif transform_wrap == "scaleversor": + assert dim == 3, "ScaleVersor only works for 3D images." return sitk.ScaleVersor3DTransform() elif transform_wrap == "scaleskewversor": + assert dim == 3, "ScaleSkewVersor only works for 3D images." return sitk.ScaleSkewVersor3DTransform() # transforms that have specifically defined dimensions elif transform_wrap == "euler":