[
  {
    "path": ".gitattributes",
    "content": "*.pt filter=lfs diff=lfs merge=lfs -text\n"
  },
  {
    "path": ".gitignore",
    "content": "build/\nhtml/\ndata/tests/\ndist/\nlatex/\nmonaifbs/models/\nprototyping/\n*.nii\n*.nii.gz\n*.tar.gz\n*.deb\n*.zip\n*.pyc\n*iterate.dat\n*.egg-info\n*.idea\n*.wiki\n/.project\n.DS_Store\n**/.DS_Store\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "# -----------------------------------Set Up------------------------------------\nvariables:\n    PY_VERSION: 3\n    PRIVATE: 0\n    TMPDIR: ./tmp\n    DATADIR: /home/mebner/data/ci/FetalBrain\n    VENV: pysitk-test-py${PY_VERSION}\n    ITK_DIR: /mnt/shared/mebner/environment/ITK/ITK_NiftyMIC-python${PY_VERSION}-build\n    FSL_DIR: /mnt/shared/mebner/environment/FSL/fsl\n    TEST_DIR: /home/mebner/Development/NiftyMIC/data/tests\n    NIFTYREG_INSTALL: /mnt/shared/mebner/environment/NiftyReg/NiftyReg-master-install\n    CONVERT3D_INSTALL: /mnt/shared/mebner/environment/Convert3D/c3d-git-install\n\nbefore_script:\n    # add NiftyReg to PATH\n    - export PATH=\"${NIFTYREG_INSTALL}/bin:$PATH\"\n\n    # add FSL\n    - PATH=${FSL_DIR}/bin:${PATH}\n    - export PATH=\"${FSL_INSTALL}/bin:$PATH\"\n    - export FSLOUTPUTTYPE=NIFTI_GZ\n\n    # add Convert3D to PATH\n    - export PATH=\"${CONVERT3D_INSTALL}/bin:$PATH\"\n\n    # save current folder path\n    - cwd_dir=$(pwd)\n\n    # create virtual environment\n    - rm -rf ${VENV}\n    - mypython=$(which python${PY_VERSION})\n    - virtualenv -p $mypython ${VENV}\n    - cd ${VENV}\n    - venv_dir=$(pwd)\n    - source bin/activate\n    \n    # print Python version to CI output\n    - which python\n    - python --version\n\n    # copy ITK_NiftyMIC-build WrapITK to site-packages of python venv\n    - py_sitepkg=${venv_dir}/lib/python*/site-packages\n    - cp -v ${ITK_DIR}/Wrapping/Generators/Python/WrapITK.pth ${py_sitepkg}\n\n    - cd $cwd_dir\n    # If PRIVATE is used:\n    # add CI_JOB_TOKEN for cloning dependent repositories in requirements.txt\n    # (https://docs.gitlab.com/ee/user/project/new_ci_build_permissions_model.html#dependent-repositories)\n    - >\n      (if [ ${PRIVATE} == 1 ];\n      then sed -i -- \"s#github.com/gift-surg#gitlab-ci-token:${CI_JOB_TOKEN}@PRIVATE.cs.ucl.ac.uk/GIFT-Surg#g\" requirements.txt;\n      fi);\n    # install requirements\n    - pip install -r requirements.txt\n\n    # set environment variables for installation\n    - export NIFTYMIC_ITK_DIR=$ITK_DIR\n\n    # run installation\n    - pip install -e .\n\n    # replace TEST_DIR in niftymic/definitions.py file\n    - sed -i -- 's:DIR_TEST = os.path.join(DIR_ROOT, \"data\", \"tests\"):DIR_TEST = \"'\"$TEST_DIR\"'\":g' niftymic/definitions.py\n    - cat niftymic/definitions.py\n\nafter_script:\n    # delete tmp-directory\n    - rm -rfv ${TMPDIR}\n\n# ----------------------------------Test Jobs----------------------------------\nbuilddocs:\n  # only:\n  #   - master\n  script:\n    - cd doc\n    - doxygen doxyfile\n  tags:\n    - gift-adelie\n\ninstallation:\n  # only:\n  #   - master\n  script:\n    - python -m nose tests/installation_test.py\n  tags:\n    - gift-adelie\n\nreconstruct_volume_tk1l2:\n  # only:\n  #   - master\n  script:\n    - >\n      niftymic_reconstruct_volume\n      --filenames ${DATADIR}/input_data/axial.nii.gz ${DATADIR}/input_data/coronal.nii.gz ${DATADIR}/input_data/sagittal.nii.gz\n      --output ${TMPDIR}/srr_from_slices_tk1l2.nii.gz\n      --suffix-mask _mask\n      --verbose 0 \n      --isotropic-resolution 2\n      --reconstruction-type TK1L2\n      --two-step-cycles 1\n      --iter-max 2\n      --iter-max-first 2\n  tags:\n    - gift-adelie\n\nreconstruct_volume_huberl2:\n  # only:\n  #   - master\n  script:\n    - >\n      niftymic_reconstruct_volume\n      --filenames ${DATADIR}/input_data/axial.nii.gz ${DATADIR}/input_data/coronal.nii.gz ${DATADIR}/input_data/sagittal.nii.gz \n      --output ${TMPDIR}/srr_from_slices_huberl2.nii.gz\n      --suffix-mask _mask\n      --verbose 0 \n      --isotropic-resolution 2\n      --reconstruction-type HuberL2\n      --two-step-cycles 1\n      --iter-max 2\n      --iter-max-first 2\n      --iterations 1\n  tags:\n    - gift-adelie\n\nreconstruct_volume_from_slices:\n  # only:\n  #   - master\n  script:    \n    - >\n      niftymic_reconstruct_volume_from_slices\n      --filenames ${DATADIR}/input_data/axial.nii.gz ${DATADIR}/input_data/coronal.nii.gz ${DATADIR}/input_data/sagittal.nii.gz \n      --dir-input-mc ${DATADIR}/motion_correction_oriented\n      --suffix-mask _mask\n      --reconstruction-space ${DATADIR}/SRR_stacks3_TK1_lsmr_alpha0p03_itermax10_oriented.nii.gz \n      --output ${TMPDIR}/srr_from_slices.nii.gz\n      --verbose 0\n      --isotropic-resolution 2\n      --iter-max 2\n  tags:\n    - gift-adelie  \n\nrun_reconstruction_pipeline:\n  # only:\n  #   - master\n  script:\n    - >\n      niftymic_run_reconstruction_pipeline\n      --filenames ${DATADIR}/input_data/axial.nii.gz\n      --filenames-masks ${DATADIR}/input_data/axial_mask.nii.gz\n      --dir-output ${TMPDIR} \n      --suffix-mask _mask\n      --bias-field-correction 1\n      --two-step-cycles 0\n      --iter-max 1\n      --run-diagnostics 1\n      --slice-thicknesses 3.85\n      --verbose 0 \n  tags:\n    - gift-adelie\n\nparam_study_huberl2:\n  # only:\n  #   - master\n  script:\n    - recon_type=HuberL2\n    - >\n      niftymic_run_reconstruction_parameter_study\n      --filenames ${DATADIR}/input_data/axial.nii.gz ${DATADIR}/input_data/coronal.nii.gz ${DATADIR}/input_data/sagittal.nii.gz \n      --dir-input-mc ${DATADIR}/motion_correction_oriented\n      --suffix-mask _mask\n      --reference ${DATADIR}/SRR_stacks3_TK1_lsmr_alpha0p03_itermax10_oriented.nii.gz \n      --dir-output ${TMPDIR}/param_study \n      --alphas 0.01 0.05 \n      --iter-max 2 \n      --verbose 0 \n      --iterations 2\n      --reconstruction-type ${recon_type} \n  tags:\n    - gift-adelie\n\nrsfmri_estimate_motion:\n  # only:\n  #   - master\n  script:\n    - recon_type=HuberL2\n    - >\n      niftymic_rsfmri_estimate_motion\n      --filename ${DATADIR}/rsfmri/bold.nii.gz\n      --filename-mask ${DATADIR}/rsfmri/bold_4Dmask.nii.gz\n      --dir-output ${TMPDIR}/rsfmri \n      --two-step-cycles 1\n      --iterations 2\n      --sda\n      --alpha 1\n      --verbose 0 \n      --prototyping\n  tags:\n    - gift-adelie\n\nrsfmri_reconstruct_volume_from_slices:\n  # only:\n  #   - master\n  script:\n    - recon_type=TK1L2\n    - >\n      niftymic_rsfmri_reconstruct_volume_from_slices\n      --filename ${DATADIR}/rsfmri/bold.nii.gz\n      --filename-mask ${DATADIR}/rsfmri/bold_4Dmask.nii.gz\n      --output ${TMPDIR}/rsfmri/foo.nii.gz \n      --alpha 0.05\n      --iter-max 2 \n      --verbose 0 \n      --reconstruction-type ${recon_type} \n      --reconstruction-spacing 2 2 5\n      --use-masks-srr 1\n      --prototyping\n  tags:\n    - gift-adelie\n\n##\n# Results can (slightly) change depending on the downloaded library version;\n# enough so that the accuracy limits are not met. Run them locally instead.\n# unit_tests:\n#   # only:\n#   #   - master\n#   script:\n#     - python -m nose tests/brain_stripping_test.py\n#     - python -m nose tests/case_study_fetal_brain_test.py\n#     - python -m nose tests/data_reader_test.py\n#     - python -m nose tests/image_similarity_evaluator_test.py\n#     - python -m nose tests/intensity_correction_test.py\n#     - python -m nose tests/linear_operators_test.py\n#     - python -m nose tests/niftyreg_test.py\n#     - python -m nose tests/residual_evaluator_test.py\n#     - python -m nose tests/segmentation_propagation_test.py\n#     - python -m nose tests/simulator_slice_acquisition_test.py\n#     - python -m nose tests/stack_test.py\n#   tags:\n#     - gift-adelie\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"MONAIfbs\"]\n\tpath = MONAIfbs\n\turl = https://github.com/gift-surg/MONAIfbs\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2017, University College London\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this \n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice, \n   this list of conditions and the following disclaimer in the documentation \n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors \n   may be used to endorse or promote products derived from this software without\n   specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED \nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE \nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL \nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR \nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER \nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, \nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE \nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "MANIFEST.in",
    "content": "include LICENSE\ninclude requirements.txt\n"
  },
  {
    "path": "README.md",
    "content": "# Motion Correction and Volumetric Image Reconstruction of 2D Ultra-fast MRI\n\nNiftyMIC is a Python-based open-source toolkit for research developed within the [GIFT-Surg][giftsurg] project to reconstruct an isotropic, high-resolution volume from multiple, possibly motion-corrupted, stacks of low-resolution 2D slices. The framework relies on slice-to-volume registration algorithms for motion correction and reconstruction-based Super-Resolution (SR) techniques for the volumetric reconstruction. \n\nThe algorithm and software were developed by [Michael Ebner][mebner]\nat the [Wellcome/EPSRC Centre for Interventional and Surgical Sciences][weiss], [University College London (UCL)][ucl] (2015 -- 2019), and the [Department of Surgical and Interventional Sciences][sie], [King's College London (KCL)][kcl] (since 2019).\n\nA detailed description of the NiftyMIC algorithm is found in [EbnerWang2020][ebner-wang-2020]:\n* Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Aughwane, R., Melbourne, A., Doel, T., Dymarkowski, S., De Coppi, P., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2020). An automated framework for localization, segmentation and super-resolution reconstruction of fetal brain MRI. NeuroImage, 206, 116324.\n\nA later extension to this paper for fetal brain automatic segmentation building on [MONAI][monai] was provided by [Marta B.M. Ranzini][martaranzini]. \nMore information about `MONAIfbs` can be found [here][monaifbs].\n\nAn extension of NiftyMIC for fetal functional MRI was developed in collaboration with members of the [Computational Image Research Lab][cir], Department of Biomedical Imaging and Image-guided Therapy at the [Medical University of Vienna][muw]. A detailed description of the NiftyMIC algorithm for fetal fMRI is found in [Sobotka2022][sobotka-2022]:\n* Sobotka, D., Ebner, M., Schwartz, E., Nenning, K.-H., Taymourtash, A., Vercauteren, T., Ourselin, S., Kasprian, G., Prayer, D., Langs, G., Licandro, R. (2022). Motion Correction and Volumetric Reconstruction for Fetal fMRI. arXiv.\n\nIf you have any questions or comments, please drop an email to `michael.ebner@kcl.ac.uk`.\n\n## NiftyMIC applied to Fetal Brain MRI\nGiven a set of low-resolution, possibly motion-corrupted, stacks of 2D slices, NiftyMIC produces an isotropic, high-resolution 3D volume. As an example, we illustrate its use for fetal MRI by computing a high-resolution visualization of the brain for a neck mass subject. Standard clinical HASTE sequences were used to acquire the low-resolution images in multiple orientations. \nThe associated brain masks for motion correction can be obtained with the included automated segmentation tool [MONAIfbs][monaifbs] (a legacy method for automated segmentation can also be found in the [fetal_brain_seg][fetal_brain_seg] package).   \nFull working examples on automated segmentation and high-resolution reconstruction of fetal brain MRI using NiftyMIC is described in the [Usage](https://github.com/gift-surg/NiftyMIC#automatic-segmentation-and-high-resolution-reconstruction-of-fetal-brain-mri) section below. \n\n<p align=\"center\">\n   <img src=\"./data/demo/NiftyMIC_Algorithm.png\" align=\"center\" width=\"700\">\n</p>\n<p align=\"center\">Figure 1. NiftyMIC -- a volumetric MRI reconstruction tool based on rigid slice-to-volume registration and outlier-robust super-resolution reconstruction steps -- applied to fetal brain MRI.<p align=\"center\">\n\n\n<p align=\"center\">\n   <img src=\"./data/demo/NiftyMIC_VolumetricReconstructionOutput.png\" align=\"center\" width=\"1000\">\n</p>\n<p align=\"center\">Figure 2. Qualitative comparison of the original low-resolution input data and the obtained high-resolution volumetric reconstructions in both the original patient-specific and standard anatomical orientations. Five input stacks (two axial, one coronal and two sagittal) were used. <p align=\"center\">\n\n## Algorithm\nSeveral methods have been implemented to solve the **Robust Super-Resolution Reconstruction (SRR)** problem \n<!-- ```math\n\\vec{x}^* := \\text{argmin}_{\\vec{x}\\ge 0} \\Big[\\sum_{k\\in\\mathcal{K}_\\sigma} \\sum_{i=1}^{N_k} \\varrho\\big( (A_k\\vec{x} - \\vec{y}_k)_i^2 \\big) + \\alpha\\,\\text{Reg}(\\vec{x}) \\Big]\n``` -->\n<p align=\"center\">\n<img src=\"http://latex.codecogs.com/svg.latex?\\vec{x}^*&space;:=&space;\\text{argmin}_{\\vec{x}\\ge&space;0}&space;\\Big[\\sum_{k\\in\\mathcal{K}_\\sigma}&space;\\sum_{i=1}^{N_k}&space;\\varrho\\big(&space;(A_k\\vec{x}&space;-&space;\\vec{y}_k)_i^2&space;\\big)&space;&plus;&space;\\alpha\\,\\text{Reg}(\\vec{x})&space;\\Big]\" title=\"http://latex.codecogs.com/svg.latex?\\vec{x}^* := \\text{argmin}_{\\vec{x}\\ge 0} \\Big[\\sum_{k\\in\\mathcal{K}_\\sigma} \\sum_{i=1}^{N_k} \\varrho\\big( (A_k\\vec{x} - \\vec{y}_k)_i^2 \\big) + \\alpha\\,\\text{Reg}(\\vec{x}) \\Big]\" />\n</p>\n\n<!--to obtain the (vectorized) high-resolution 3D MRI volume $`\\vec{x}\\in\\mathbb{R}^N`$ from multiple, possibly motion corrupted, low-resolution stacks of (vectorized) 2D MR slices $`\\vec{y}_k \\in\\mathbb{R}^{N_k}`$ with $`N_k\\ll N`$ for $`k=1,...,\\,K`$-->\n<!--for a variety of regularizers $`\\text{Reg}`$ and data loss functions $`\\varrho`$.-->\n<!--The linear operator $`A_k := D_k B_k W_k`$ represents the comd operator descri the (rigid) motion $`W_k`$, the blurring operator $`B_k`$ and the downsampling operator $`D_k`$.-->\nto obtain the (vectorized) high-resolution 3D MRI volume ![img](http://latex.codecogs.com/svg.latex?%5Cvec%7Bx%7D%5Cin%5Cmathbb%7BR%7D%5EN) from multiple, possibly motion corrupted, low-resolution stacks of (vectorized) 2D MR slices ![img](http://latex.codecogs.com/svg.latex?%5Cvec%7By%7D_k%5Cin%5Cmathbb%7BR%7D%5E%7BN_k%7D) with ![img](http://latex.codecogs.com/svg.latex?N_k%5Cll%7BN%7D) for ![img](https://latex.codecogs.com/svg.latex?k\\in\\mathcal{K}:=\\\\{1,\\dots,K\\\\})\nfor a variety of regularizers ![img](http://latex.codecogs.com/svg.latex?%5Ctext%7BReg%7D) and data loss functions ![img](http://latex.codecogs.com/svg.latex?%5Cvarrho).\nThe linear operator ![img](http://latex.codecogs.com/svg.latex?A_k%3A%3DD_kB_kW_k) represents the combined operator describing the (rigid) motion ![img](http://latex.codecogs.com/svg.latex?W_k), the blurring operator ![img](http://latex.codecogs.com/svg.latex?B_k) and the downsampling operator ![img](http://latex.codecogs.com/svg.latex?D_k).\n\nThe toolkit relies on an iterative motion-correction/reconstruction approach whereby **complete outlier rejection** of misregistered slices is achieved by iteratively solving the SRR problem for a slice-index set \n![img](https://latex.codecogs.com/svg.latex?\\mathcal{K}_\\sigma:=\\\\{1\\le&space;k&space;\\le&space;K:&space;\\text{Sim}(A_k^{\\iota}\\vec{x}^{\\iota},&space;\\vec{y}^{\\iota}_k)\\ge\\sigma\\\\}\\subset&space;\\mathcal{K}) containing only slices that are in high agreement with their simulated counterparts projected from a previous high-resolution iterate ![img](http://latex.codecogs.com/svg.latex?\\vec{x}^{\\iota}) according to a similarity measure ![img](http://latex.codecogs.com/svg.latex?\\text{Sim}) and parameter ![img](http://latex.codecogs.com/svg.latex?\\sigma>0). In the current implementation, the similarity measure ![img](http://latex.codecogs.com/svg.latex?\\text{Sim}) corresponds to Normalized Cross Correlation (NCC).\n\n---\n\nThe provided **data loss functions** ![img](http://latex.codecogs.com/svg.latex?%5Cvarrho) are motivated by [SciPy](https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.optimize.least_squares.html) and allow for additional robust outlier handling during the SRR step. Implemented data loss functions are:\n<!--$`\\varrho(e)=e`$-->\n<!--$`\\varrho(e)=2(\\sqrt{1+e}-1)`$ -->\n<!--$`\\varrho(e)=|e|_\\gamma=\\begin{cases}e,&e<\\gamma^2\\\\2\\gamma\\sqrt{e}-\\gamma^2,&e\\ge\\gamma^2\\end{cases}`$-->\n<!--$`\\varrho(e)=\\arctan(e)`$-->\n<!--$`\\varrho(e)=\\ln(1 + e)`$-->\n* `linear`: <img src=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=e\" title=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=e\" />\n* `soft_l1`: <img src=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=2(\\sqrt{1&plus;e}-1)\" title=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=2(\\sqrt{1+e}-1)\" />\n* `huber`: <img src=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=|e|_\\gamma=\\begin{cases}e,&e<\\gamma^2\\\\2\\gamma\\sqrt{e}-\\gamma^2,&e\\ge\\gamma^2\\end{cases}\" title=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=|e|_\\gamma=\\begin{cases}e,&e<\\gamma^2\\\\2\\gamma\\sqrt{e}-\\gamma^2,&e\\ge\\gamma^2\\end{cases}\" />\n* `arctan`: <img src=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=\\arctan(e)\" title=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=\\arctan(e)\" />\n* `cauchy`: <img src=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=\\ln(1&space;&plus;&space;e)\" title=\"http://latex.codecogs.com/svg.latex?\\varrho(e)=\\ln(1 + e)\" />\n\n---\n\nThe **available regularizers** include\n<!-- * Zeroth-order Tikhonov (TK0): $`\\text{Reg}(\\vec{x}) = \\frac{1}{2}\\Vert \\vec{x} \\Vert_{\\ell^2}^2`$ -->\n<!-- * First-order Tikhonov (TK1): $`\\text{Reg}(\\vec{x}) = \\frac{1}{2}\\Vert \\nabla \\vec{x} \\Vert_{\\ell^2}^2`$ -->\n<!-- * Isotropic Total Variation (TV): $`\\text{Reg}(\\vec{x}) = \\text{TV}_\\text{iso}(\\vec{x}) = \\big\\Vert |\\nabla \\vec{x}| \\big\\Vert_{\\ell^1}`$ -->\n<!-- * Huber Function: $`\\text{Reg}(\\vec{x}) = \\frac{1}{2\\gamma} \\big| |\\nabla \\vec{x}| \\big|_{\\gamma}`$ -->\n* Zeroth-order Tikhonov (TK0): <img src=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x})&space;=&space;\\frac{1}{2}\\Vert&space;\\vec{x}&space;\\Vert_{\\ell^2}^2\" title=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x}) = \\frac{1}{2}\\Vert \\vec{x} \\Vert_{\\ell^2}^2\" />\n* First-order Tikhonov (TK1): <img src=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x})&space;=&space;\\frac{1}{2}\\Vert&space;\\nabla&space;\\vec{x}&space;\\Vert_{\\ell^2}^2\" title=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x}) = \\frac{1}{2}\\Vert \\nabla \\vec{x} \\Vert_{\\ell^2}^2\" />\n* Isotropic Total Variation (TV): <img src=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x})&space;=&space;\\text{TV}_\\text{iso}(\\vec{x})&space;=&space;\\big\\Vert&space;|\\nabla&space;\\vec{x}|&space;\\big\\Vert_{\\ell^1}\" title=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x}) = \\text{TV}_\\text{iso}(\\vec{x}) = \\big\\Vert |\\nabla \\vec{x}| \\big\\Vert_{\\ell^1}\" />\n* Huber Function: <img src=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x})&space;=&space;\\frac{1}{2\\gamma}&space;\\big|&space;|\\nabla&space;\\vec{x}|&space;\\big|_{\\gamma}\" title=\"http://latex.codecogs.com/svg.latex?\\text{Reg}(\\vec{x}) = \\frac{1}{2\\gamma} \\big| |\\nabla \\vec{x}| \\big|_{\\gamma}\" />\n---\n\nAdditionally, the choice of finding **optimal reconstruction parameters** is facilitated by the [Numerical Solver Library (NSoL)][nsol].\n\n## Disclaimer\nNiftyMIC supports medical image registration and volumetric reconstruction for ultra-fast 2D MRI. **NiftyMIC is not intended for clinical use**.\n\n## How to cite\nIf you use this software in your work for structural MRI reconstruction, please cite\n\n* [[EbnerWang2020]][ebner-wang-2020] Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Aughwane, R., Melbourne, A., Doel, T., Dymarkowski, S., De Coppi, P., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2020). An automated framework for localization, segmentation and super-resolution reconstruction of fetal brain MRI. NeuroImage, 206, 116324.\n* [[EbnerWang2018]][ebner-wang-2018] Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Melbourne, A., Doel, T., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2018). An Automated Localization, Segmentation and Reconstruction Framework for Fetal Brain MRI. In Medical Image Computing and Computer-Assisted Intervention -- MICCAI 2018 (pp. 313–320). Springer.\n* [[Ebner2018]](https://www.sciencedirect.com/science/article/pii/S1053811917308042) Ebner, M., Chung, K. K., Prados, F., Cardoso, M. J., Chard, D. T., Vercauteren, T., Ourselin, S. (2018). Volumetric reconstruction from printed films: Enabling 30 year longitudinal analysis in MR neuroimaging. NeuroImage, 165, 238–250.\n\nIf you use this software in your work for functional MRI reconstruction, please cite\n* [[Sobotka2022]][sobotka-2022] Sobotka, D., Ebner, M., Schwartz, E., Nenning, K.-H., Taymourtash, A., Vercauteren, T., Ourselin, S., Kasprian, G., Prayer, D., Langs, G., Licandro, R. (2022). Motion Correction and Volumetric Reconstruction for Fetal fMRI. arXiv.\n* [[EbnerWang2020]][ebner-wang-2020] Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Aughwane, R., Melbourne, A., Doel, T., Dymarkowski, S., De Coppi, P., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2020). An automated framework for localization, segmentation and super-resolution reconstruction of fetal brain MRI. NeuroImage, 206, 116324.\n\n## Installation\n\n### From Source\nNiftyMIC was developed in Ubuntu 16.04, 18.04 and Mac OS X 10.12 and tested for Python 2.7, 3.5 and 3.6.\nIt builds on a couple of additional libraries developed within the [GIFT-Surg][giftsurg] project including \n* [NSoL][nsol]\n* [SimpleReg][simplereg]\n* [PySiTK][pysitk]\n* [ITK_NiftyMIC][itkniftymic]\n\nwhose installation requirements need to be met. Therefore, the local installation comes in three steps:\n\n1. [Installation of ITK_NiftyMIC][itkniftymic]\n1. [Installation of SimpleReg dependencies][simplereg-dependencies]\n1. [Installation of NiftyMIC (including optional MONAIfbs)][niftymic-install]\n\nNote: The optional use of the automated fetal brain segmentation tool [MONAIfbs][monaifbs] requires Python version >= 3.6.\n\n### Virtual Machine and Docker\n\nTo avoid manual installation from source, NiftyMIC is also available as a [virtual machine][niftymic-vm] and [Docker image][niftymic-docker].\n\n## Usage\n\nProvided the input MR image data in NIfTI format (`nii` or `nii.gz`), NiftyMIC can reconstruct an isotropic, high-resolution volume from multiple, possibly motion-corrupted, stacks of low-resolution 2D slices.\n\nA recommended workflow is [associated applications in square brackets]:\n\n1. Segmentation of the anatomy of interest for all input images. For fetal brain MRI reconstructions, we recommend the use of the fully automatic segmentation tool [MONAIfbs][monaifbs] already integrated in NiftyMIC [`niftymic_segment_fetal_brains`].\n1. Bias-field correction [`niftymic_correct_bias_field`]\n1. Volumetric reconstruction in subject space using two-step iterative approach based on rigid slice-to-volume registration and SRR cycles [`niftymic_reconstruct_volume`]\n\nIn case reconstruction in a template space is desired (like for fetal MRI) additional steps could be:\n1. Register obtained SRR to template and update respective slice motion corrections [`niftymic_register_image`]\n1. Volumetric reconstruction in template space [`niftymic_reconstruct_volume_from_slices`]\n\nAdditional information on how to use NiftyMIC and its applications is provided in the following.\n\n### Volumetric MR Reconstruction from Motion Corrupted 2D Slices\nLeveraging a two-step registration-reconstruction approach an isotropic, high-resolution 3D volume can be generated from multiple stacks of low-resolution slices.\n\nAn example for a basic usage reads\n```\nniftymic_reconstruct_volume \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--filenames-masks path-to-stack-1_mask.nii.gz ... path-to-stack-N_mask.nii.gz \\\n--output path-to-srr.nii.gz \\\n```\nwhereby complete outlier removal during SRR is activated by default (`--outlier-rejection 1`).\n\nA more elaborate example could be\n```\nniftymic_reconstruct_volume \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--filenames-masks path-to-stack-1_mask.nii.gz ... path-to-stack-N_mask.nii.gz \\\n--alpha 0.01 \\\n--outlier-rejection 1 \\\n--threshold-first 0.5 \\\n--threshold 0.85 \\\n--intensity-correction 1 \\\n--isotropic-resolution 0.8 \\\n--two-step-cycles 3 \\\n--output path-to-output-dir/srr.nii.gz \\\n--subfolder-motion-correction motion_correction \\ # created in 'path-to-output-dir'\n--verbose 1\n```\n\nThe obtained motion-correction transformations in `motion_correction` can be used for further processing, e.g. by using `niftymic_reconstruct_volume_from_slices.py` to solve the SRR problem for a variety of different regularization and data loss function types. \n\n### Transformation to Template Space\nIf a template is available, it is possible to obtain a SRR in its associated standard anatomical space. Using the subject-space SRR outcome of `niftymic_reconstruct_volume` a rigid alignment step maps all slice motion correction transformations accordingly using\n```\nniftymic_register_image \\\n--fixed path-to-template.nii.gz \\\n--fixed-mask path-to-template_mask.nii.gz \\\n--moving path-to-subject-space-srr.nii.gz \\\n--moving-mask path-to-subject-space-srr_mask.nii.gz \\\n--dir-input-mc dir-to-motion_correction \\\n--output path-to-registration-transform.txt\n```\nFor fetal brain template space alignment, a [spatio-temporal atlas][gholipour_atlas] is provided in [`data/templates`](data/templates). If you make use of it, please cite\n\n* Gholipour, A., Rollins, C. K., Velasco-Annis, C., Ouaalam, A., Akhondi-Asl, A., Afacan, O., Ortinau, C. M., Clancy, S., Limperopoulos, C., Yang, E., Estroff, J. A. & Warfield, S. K. (2017). A normative spatiotemporal MRI atlas of the fetal brain for automatic segmentation and analysis of early brain growth. Scientific Reports 7, 476.\n\nand abide by the license agreement as described in [`data/templates/LICENSE`](data/templates/LICENSE).\n\n### SRR Methods for Motion Corrected (or Static) Data\n\nAfter performed/updated motion correction (or having static data in the first place) several options are available:\n\n* Volumetric reconstruction in template space\n* Parameter tuning for SRR:\n    * different solvers and regularizers can be used to solve the SRR problem for comparison\n    * parameter studies can be performed to find optimal reconstruction parameters.\n\n#### SRR from Motion Corrected (or Static) Slice Acquisitions\n\nSolve the SRR problem for motion corrected data (or static data if `--dir-input-mc` is omitted):\n```\nniftymic_reconstruct_volume_from_slices \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--filenames-masks path-to-stack-1_mask.nii.gz ... path-to-stack-N_mask.nii.gz \\\n--dir-input-mc dir-to-motion_correction \\ # optional\n--output path-to-srr.nii.gz \\\n--reconstruction-type TK1L2 \\\n--reconstruction-space path-to-template.nii.gz \\ # optional\n--alpha 0.01\n```\n```\nniftymic_reconstruct_volume_from_slices \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--dir-input-mc dir-to-motion_correction \\\n--output path-to-srr.nii.gz \\\n--reconstruction-type HuberL2 \\\n--alpha 0.003\n```\n\nSlices that were rejected during the `niftymic_reconstruct_volume` run are recognized as outliers based on the content of `dir-input-mc` and will not be incorporated during the volumetric reconstruction.\n\n\n#### Parameter Studies to Determine Optimal SRR Parameters\nThe optimal choice for reconstruction parameters like the regularization parameter or data loss function can be found by running parameter studies. This includes L-curve studies and direct comparison against a reference volume for various cost functions.\nIn case a reference is available, similarity measures are evaluated against this \"ground-truth\" as well.\n\nExample are:\n```\nniftymic_run_reconstruction_parameter_study \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--filenames-masks path-to-stack-1_mask.nii.gz ... path-to-stack-N_mask.nii.gz \\\n--dir-input-mc dir-to-motion_correction \\\n--dir-output dir-to-param-study-output \\\n--reconstruction-type TK1L2 \\\n--reconstruction-space path-to-reconstruction-space.nii.gz \\ # define reconstruction space\n--alphas 0.005 0.01 0.02 0.05 0.1 # regularization parameters to sweep through\n--append # if given, append a previously performed parameter study in output directory (if available)\n```\n```\nniftymic_run_reconstruction_parameter_study \\\n--filenames path-to-stack-1.nii.gz ... path-to-stack-N.nii.gz \\\n--filenames-masks path-to-stack-1_mask.nii.gz ... path-to-stack-N_mask.nii.gz \\\n--dir-input-mc dir-to-motion_correction \\\n--dir-output dir-to-param-study-output \\\n--reconstruction-type HuberL2 \\\n--reference path-to-reference-volume.nii.gz \\ # in case reference (\"ground-truth\") is available (reconstruction space is defined by this reference)\n--measures MAE RMSE PSNR NCC NMI SSIM \\ # evaluate reconstruction similarities against reference\n--reference-mask path-to-reference-volume_mask.nii.gz \\ # if given, evaluate similarities (--measures) on masked region only\n--alphas 0.001 0.003 0.005 0.001 0.003 \\ # regularization parameters to sweep through\n--append # if given, append a previously performed parameter study in output directory (if available)\n```\n\nThe results can be assessed by accessing the [NSoL][nsol]-script `show_parameter_study.py` via\n```\nniftymic_show_parameter_study \\\n--dir-input dir-to-param-study-output \\\n--study-name TK1L2 \\\n--dir-output-figures dir-to-figures\n```\n\n### Automatic Segmentation and High-Resolution Reconstruction of Fetal Brain MRI\nAn automated framework is implemented to obtain a high-resolution fetal brain MRI reconstruction in the standard anatomical planes (Figure 2).\nThis includes two main subsequent blocks:\n1. Automatic segmentation to generate fetal brain masks\n2. Automatic high-resolution reconstruction.\n\nThe latter is based on the work described in [EbnerWang2018][ebner-wang-2018] and [EbnerWang2020][ebner-wang-2020].\nCompared to the segmentation approach proposed in [EbnerWang2020][ebner-wang-2020], a new automated segmentation tool has\nbeen included in the NiftyMIC package, called [MONAIfbs][monaifbs]. \nThis implements a single-step segmentation approach based on the [dynUNet][dynUNet] implemented in [MONAI][monai].\n\nThe current NiftyMIC version is still compatible with the older segmentation pipeline based on the [fetal_brain_seg][fetal_brain_seg] \npackage and presented in [EbnerWang2020][ebner-wang-2020]. Details on its use are available at [this wiki page][wikifetalbrainseg], \nalthough `MONAIfbs` is the recommended option.\n\n\n#### Automatic segmentation\nProvided the dependencies for `MONAIfbs` are [installed][niftymic-install], create the automatic fetal brain masks of HASTE-like images:\n```\nniftymic_segment_fetal_brains \\\n--filenames \\\nnifti/name-of-stack-1.nii.gz \\\nnifti/name-of-stack-2.nii.gz \\\nnifti/name-of-stack-N.nii.gz \\\n--filenames-masks \\\nseg/name-of-stack-1.nii.gz \\\nseg/name-of-stack-2.nii.gz \\\nseg/name-of-stack-N.nii.gz\n```\n\n#### Automatic reconstruction\nAfterwards, four consecutive steps including\n1. bias field correction (`niftymic_correct_bias_field`),\n1. subject-space reconstruction (`niftymic_reconstruct_volume`),\n1. template-space alignment (`niftymic_register_image`), and\n1. template-space reconstruction (`niftymic_reconstruct_volume_from_slices`)\n\nare performed to create a high-resolution fetal brain MRI reconstruction in the standard anatomical planes:\n```\nniftymic_run_reconstruction_pipeline \\\n--filenames \\\nnifti/name-of-stack-1.nii.gz \\\nnifti/name-of-stack-2.nii.gz \\\nnifti/name-of-stack-N.nii.gz \\\n--filenames-masks \\\nseg/name-of-stack-1.nii.gz \\\nseg/name-of-stack-2.nii.gz \\\nseg/name-of-stack-N.nii.gz \\\n--dir-output srr\n```\n\nAdditional parameters such as the regularization parameter `alpha` can be specified too. For more information please execute `niftymic_run_reconstruction_pipeline -h`.\n\n*Note*: In case a suffix distinguishes image segmentation (`--filenames-masks`) from the associated image filenames (`--filenames`), the argument `--suffix-mask` needs to be provided for reconstructing the HR brain volume mask as part of the pipeline. E.g. if images `name-of-stack-i.nii.gz` are associated with the mask `name-of-stack-i_mask.nii.gz`, then the additional argument `--suffix-mask _mask` needs to be specified.\n\n### NiftyMIC applied to Fetal Brain Functional MRI\n\nAn extension of NiftyMIC for fetal functional MRI is described in [Sobotka2022][sobotka-2022]:\n\n<p align=\"center\">\n   <img src=\"./data/demo/NiftyMIC_rsfmri_Algorithm.png\" align=\"center\" width=\"700\">\n</p>\n\n<p align=\"center\">\nFigure 3: Overview of the proposed motion correction and volumetric reconstruction algorithm for rs-fMRI. With the first n=15 (default) time points a HR reference volume with outlier rejection is estimated (Ebner, Wang et al., 2020). Afterwards each slice of a time point is registered to the HR reference volume following which individual time points are reconstructed using Huber L2 regularization.<p align=\"center\">\n\n<p align=\"center\">\n   <img src=\"./data/demo/NiftyMIC_rsfmri_Reconstruction.png\" align=\"center\" width=\"700\">\n</p>\n<p align=\"center\">\nFigure 4: Qualitative comparison of the original low-resolution input sequence (top row) and the obtained volumetric reconstruction of the functional MRI (bottom row) in patient-specific orientation. A sequence of axial stacks were used as input.<p align=\"center\">\n\n\nA recommended workflow is [associated applications in square brackets]:\n\n1. Volumetric segmentation of the fetal brain for all input images in the sequence. We tested the algorithm with fetal functional brains masks created using the Brain Extraction Tool incorporated in the [FSL Toolbox][fsl] (Smith et al. 2002).\n\n1. Motion estimation [`rsfmri_estimate_motion`]\n   ```\n    rsfmri_estimate_motion \\\n    --filename path-to-bold.nii.gz  \\\n    --filename-mask path-to-bold_mask.nii.gz \\\n    --dir-output path-to-dir-output\n    ```\n1. Functional volumetric reconstruction [`rsfmri_reconstruct_volume_from_slices`]\n    \n   An example for a basic usage reads\n    ```\n    rsfmri_reconstruct_volume_from_slices \\\n    --filename path-to-bold.nii.gz  \\\n    --filename-mask path-to-bold_mask.nii.gz \\\n    --dir-input-mc path-to-motion_correction \\ # created in `dir-output` in step 2 above\n    --output path-to-bold_reconstructed.nii.gz\n    ```\n    \n   A more elaborate example using a different reconstruction approach `HuberL2` could be\n    ```\n    rsfmri_reconstruct_volume_from_slices \\\n    --filename path-to-bold.nii.gz \\\n    --filename-mask path-to-bold_mask.nii.gz \\\n    --dir-input-mc path-to-motion_correction \\ # created in `dir-output` in step 2 above\n    --output path-to-bold_reconstructed.nii.gz \\\n    --reconstruction-type HuberL2\n    --alpha 0.1 \\\n    --iter-max 20\n   ```\n\nIf you have any questions or comments with regards to the use of NiftyMIC for functional MRI, please drop an email to `daniel.sobotka@meduniwien.ac.at`.\n   \n   \n## Licensing and Copyright\nCopyright (c) 2022 Michael Ebner and contributors.\nThis framework is made available as free open-source software under the [BSD-3-Clause License][bsd]. Other licenses may apply for dependencies.\n\n\n## Funding\nThis work is partially funded by the UCL [Engineering and Physical Sciences Research Council (EPSRC)][epsrc] Centre for Doctoral Training in Medical Imaging (EP/L016478/1), the Innovative Engineering for Health award ([Wellcome Trust][wellcometrust] [WT101957] and [EPSRC][epsrc] [NS/A000027/1]), and supported by researchers at the [National Institute for Health Research][nihr] [University College London Hospitals (UCLH)][uclh] Biomedical Research Centre.\n\n## References\nSelected publications associated with NiftyMIC are:\n* [[Sobotka2022]][sobotka-2022] Sobotka, D., Ebner, M., Schwartz, E., Nenning, K.-H., Taymourtash, A., Vercauteren, T., Ourselin, S., Kasprian, G., Prayer, D., Langs, G., Licandro, R. (2022). Motion Correction and Volumetric Reconstruction for Fetal fMRI. arXiv.\n* [[EbnerWang2020]][ebner-wang-2020] Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Aughwane, R., Melbourne, A., Doel, T., Dymarkowski, S., De Coppi, P., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2020). An automated framework for localization, segmentation and super-resolution reconstruction of fetal brain MRI. NeuroImage, 206, 116324.\n* [[Ebner2019]](https://onlinelibrary.wiley.com/doi/abs/10.1002/mrm.27852) Ebner, M., Patel, P. A., Atkinson, D., Caselton, C., Firmin, F., Amin, Z., Bainbridge, A., De Coppi, P., Taylor, S. A., Ourselin, S., Chouhan, M. D., Vercauteren, T. (2019). Super‐resolution for upper abdominal MRI: Acquisition and post‐processing protocol optimization using brain MRI control data and expert reader validation. Magnetic Resonance in Medicine, 82(5), 1905–1919.\n* [[Sobotka2019]](http://link.springer.com/10.1007/978-3-030-32875-7_14) Sobotka, D., Licandro, R., Ebner, M., Schwartz, E., Vercauteren, T., Ourselin, S., Kasprian, G., Prayer, D., Langs, G. (2019). Reproducibility of Functional Connectivity Estimates in Motion Corrected Fetal fMRI. Smart Ultrasound Imaging and Perinatal, Preterm and Paediatric Image Analysis (pp. 123–132). Cham: Springer International Publishing.\n* [[EbnerWang2018]][ebner-wang-2018] Ebner, M., Wang, G., Li, W., Aertsen, M., Patel, P. A., Melbourne, A., Doel, T., David, A. L., Deprest, J., Ourselin, S., Vercauteren, T. (2018). An Automated Localization, Segmentation and Reconstruction Framework for Fetal Brain MRI. In Medical Image Computing and Computer-Assisted Intervention -- MICCAI 2018 (pp. 313–320). Springer\n* [[Ebner2018]](https://www.sciencedirect.com/science/article/pii/S1053811917308042) Ebner, M., Chung, K. K., Prados, F., Cardoso, M. J., Chard, D. T., Vercauteren, T., Ourselin, S. (2018). Volumetric reconstruction from printed films: Enabling 30 year longitudinal analysis in MR neuroimaging. NeuroImage, 165, 238–250.\n* [[Ranzini2017]](https://mski2017.files.wordpress.com/2017/09/miccai-mski2017.pdf) Ranzini, M. B., Ebner, M., Cardoso, M. J., Fotiadou, A., Vercauteren, T., Henckel, J., Hart, A., Ourselin, S., and Modat, M. (2017). Joint Multimodal Segmentation of Clinical CT and MR from Hip Arthroplasty Patients. MICCAI Workshop on Computational Methods and Clinical Applications in Musculoskeletal Imaging (MSKI) 2017.\n* [[Ebner2017]](https://link.springer.com/chapter/10.1007%2F978-3-319-52280-7_1) Ebner, M., Chouhan, M., Patel, P. A., Atkinson, D., Amin, Z., Read, S., Punwani, S., Taylor, S., Vercauteren, T., Ourselin, S. (2017). Point-Spread-Function-Aware Slice-to-Volume Registration: Application to Upper Abdominal MRI Super-Resolution. In Zuluaga, M. A., Bhatia, K., Kainz, B., Moghari, M. H., and Pace, D. F., editors, Reconstruction, Segmentation, and Analysis of Medical Images. RAMBO 2016, volume 10129 of Lecture Notes in Computer Science, pages 3–13. Springer International Publishing.\n\n[bsd]: https://opensource.org/licenses/BSD-3-Clause\n[cir]: https://www.cir.meduniwien.ac.at/\n[cmic]: http://cmic.cs.ucl.ac.uk\n[dynUNet]: https://github.com/Project-MONAI/tutorials/blob/master/modules/dynunet_tutorial.ipynb\n[ebner-wang-2018]: http://link.springer.com/10.1007/978-3-030-00928-1_36\n[ebner-wang-2020]: https://www.sciencedirect.com/science/article/pii/S1053811919309152\n[epsrc]: http://www.epsrc.ac.uk\n[fetal_brain_seg]: https://github.com/gift-surg/fetal_brain_seg\n[fsl]: https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/BET\n[gholipour_atlas]: https://www.nature.com/articles/s41598-017-00525-w\n[giftsurg]: http://www.gift-surg.ac.uk\n[guarantors]: https://guarantorsofbrain.org/\n[itkniftymic]: https://github.com/gift-surg/ITK_NiftyMIC/wikis/home\n[kcl]: https://www.kcl.ac.uk\n[martaranzini]: https://www.linkedin.com/in/marta-bianca-maria-ranzini\n[mebner]: https://www.linkedin.com/in/ebnermichael\n[monai]: https://monai.io/\n[monaifbs]: https://github.com/gift-surg/MONAIfbs\n[mssociety]: https://www.mssociety.org.uk/\n[muw]: https://www.meduniwien.ac.at\n[niftymic-docker]: https://hub.docker.com/r/renbem/niftymic\n[niftymic-install]: https://github.com/gift-surg/NiftyMIC/wikis/niftymic-installation\n[niftymic-vm]: https://github.com/gift-surg/NiftyMIC/wiki/niftymic-virtualbox\n[nihr]: http://www.nihr.ac.uk/research\n[nsol]: https://github.com/gift-surg/NSoL\n[pysitk]: https://github.com/gift-surg/PySiTK\n[sie]: https://www.kcl.ac.uk/bmeis/our-departments/surgical-interventional-engineering\n[simplereg-dependencies]: https://github.com/gift-surg/SimpleReg/wikis/simplereg-dependencies\n[simplereg]: https://github.com/gift-surg/SimpleReg\n[sobotka-2022]: https://arxiv.org/abs/2202.05863\n[ucl]: http://www.ucl.ac.uk\n[uclh]: http://www.uclh.nhs.uk\n[weiss]: https://www.ucl.ac.uk/interventional-surgical-sciences\n[wellcometrust]: http://www.wellcome.ac.uk\n[wikifetalbrainseg]: https://github.com/gift-surg/NiftyMIC/wiki/Legacy-Segmentation-Tool-(fetal_brain_seg)\n"
  },
  {
    "path": "data/templates/LICENSE",
    "content": "CRL (Computational Radiology Laboratory) hereby grants a license to use, including the right to share, copy, distribute, and transmit, the information, images and data that appears on this webpage solely for non-commercial educational and research purposes, subject to the following restrictions: (1) You must at all times acknowledge and attribute ownership of the information, images and data to CRL, including reproducing CRL's copyright to the extent that the same appears thereon; for publications referencing the fetal structural atlas, cite Gholipour et al., Scientific Reports, 2017; for publications referencing the DTI atlas, cite Khan et al., NeuroImage, 2018 and Khan et al., MICCAI, 2018 (full citations found on the Fetal Brain Atlas webpage); and (2) You may not post the information, images and data on any website, electronic bulletin board or any other electronic medium without the express written permission of CRL or required acknowledgements. As the information, images and data is experimental in nature and is being provided solely to facilitate academic and scientific research, you agree that you will use this information, images and data at your own risk and without liability of any kind to CRL. CRL MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED WITH RESPECT TO THE INFORMATION, IMAGES AND DATA, AND THERE ARE NO EXPRESS OR IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Use of the information, images or data for any commercial purposes is strictly prohibited without the express written consent of CRL. By requesting access through submitting this form, you acknowledge that you understand and accept all the terms and conditions in this license agreement.\n"
  },
  {
    "path": "data/templates/README",
    "content": "2016 Fetal Brain Atlas\nCopyright 2016, Computational Radiology Lab, Boston Childrens Hospital\n\nContact:\nAli Gholipour: Ali.Gholipour@childrens.harvard.edu\nClemente Velasco-Annis: Clemente.Velasco-Annis@childrens.harvard.edu\n\nThis version of the Spatio-Temporal Atlas (STA) was last edited on 11/21/16\n\nThis spatio-temporal atlas and the included parcellations are not intended for medical use and have no warranty. Structures received varying amounts of attention and refinement based on the research goals at the time of creation and thus no guarantees can be made regarding the accuracy or precision of the segmentations, nor the withholding to any particular labeling convention or protocol between structures. Any output derived from the use of these atlases or parcellations shouled be checked in order to validate the accuracy of the results.\n\n# # # # # # # # # # # # # # # # # # # # # #\nNew to this update (11/21/16) \"Olympic edition\":\n\nCHANGES TO LABELING SCHEME:\n\tThe parcellation scheme (label numbers) has been reorganized\n\t\tAll atlas images now share the same label key\n\t\tThe full key can be found below\n\tLeft and right white matter have been separated into separate labels\n\tLabels for left and right internal capsule have been added to all images\n\t\nREFINEMENT TO PARCELLATIONS:\n\t\"Miscellanous_brain_tissue\" has been mostly removed as most of the label was internal capsule\n\tDeveloping white matter zones (SP, IZ & VZ) have been removed from STA31-33 because they were not felt to be accurate (differentiation between layers was arbitrary)\n\t\t*There are two versions of the STA30 parcellation, one with developing white matter zones, one without*\n\tCortical plate was reduced in the region anterior to the corpus callosum (reduced the portion \"wrapping\" into hemisphere)\n\tSubplate has been expanded to the appropriate width as per reviewer feedback\n\tThe intermediate zone/ventricular zone has been edited to shrink the ventricular zone in line with reviewer feedback\n\tDeep gray matter structures have been edited to increase segmentation consistency between gestational ages\n\n# # # # # # # # # # # # # # # # # # # # # #\n# # # # # # # # # # # # # # # # # # # # # #\nNew to this update (06/07/16):\n\n\tWhite and gray cerebellum have been recombined because we could not ensure the accuracy of the delineation (very little refinement had been done since initial propagation of the parcellation scheme to this atlas).\n\t\n\tThe files have been renamed slightly.\n\n# # # # # # # # # # # # # # # # # # # # # #\n# # # # # # # # # # # # # # # # # # # # # #\nNew to this update (03/30/16):\n\nADDED TEMPLATES:\n\tThere are now atlas images and labels for gestational age (GA) weeks 21-37.\n\nDEVELOPING WHITE MATTER ZONES:\n\tLabels for developing white matter zones have been added for GA 21-33\n\tThis includes left and right \"ventricular zone\", \"intermediate zone\" (similar to subventricular zone), and \"subplate\"\n\tIn these images, label 51 is now \"miscellaneous brain tissue\", mostly deep gray matter found between the larger subcortical structures\n\tImages with the labeling convention are marked with \"wmz\"; for example, STA27_WMZparc.nii.gz\n\n\tThere are two versions of labels for STA31, STA32, and STA33\n\tSTA31_parc.nii.gz = All white matter is label 51\n\tSTA31_WMZparc.nii.gz = Developing white matter layers have been separated into miscellaneous brain tissue (#51), subplate (#73,74), intermediate zone (#75,76), and ventricular zone (#77,78)\n\n\tNote: As the white matter homogenizes in composition during development, the visual differentiation between the layers also decreases. Because of this the accuracy of the developmental layers may not be great for the older brains (i.e. STA30-33). This primarily pertains to the border between intermediate zone and subplate.\n\nADDITIONAL ADDED LABELS:\n\t#5: Hippocampal commisure\n\t#6: Fornix\n\nLABEL REFINEMENTS:\n\tMany of the labels have been refined to increase accuracy, in particular:\n\t\thippocampus\n\t\tcaudate\n\t\tthalamus\n\t\tlentiform\n\t\tcorpus callosum\n\t\tcortical plate\n# # # # # # # # # # # # # # # # # # # # # # \n\nLabel key (all images):\n\n   37 Hippocampus_L\n   38 Hippocampus_R\n   41 Amygdala_L\n   42 Amygdala_R\n   71 Caudate_L\n   72 Caudate_R\n   73 Lentiform_L\n   74 Lentiform_R\n   77 Thalamus_L\n   78 Thalamus_R\n   91 CorpusCallosum\n   92 Lateral_Ventricle_L\n   93 Lateral_Ventricle_R\n   94 Brainstem\n  100 Cerebellum_L\n  101 Cerebellum_R\n  108 Subthalamic_Nuc_L\n  109 Subthalamic_Nuc_R\n  110 Hippocampal_Comm\n  111 Fornix\n  112 Cortical_Plate_L\n  113 Cortical_Plate_R\n  114 Subplate_L\n  115 Subplate_R\n  116 Inter_Zone_L\n  117 Inter_Zone_R\n  118 Vent_Zone_L\n  119 Vent_Zone_R\n  120 White_Matter_L\n  121 White_Matter_R\n  122 Internal_Capsule_L\n  123 Internal_Capsule_R\n  124 CSF\n  125 Misc\n\n# # # # # # # # # # # # # # # # # # # # # # \n\nMichael Ebner, Feb 16, 2018:\n- All image headers have been updated using fslreorient2std using `for i in *.nii.gz; do echo $i; fslreorient2std $i $i; done`\n- _mask: binarized image labels\n- _mask_dil: dilated binary masks; applied to SRR it approximates brain tissue only\n- templates_info.json: Additional information on templates like volume"
  },
  {
    "path": "data/templates/templates_info.json",
    "content": "{\n    \"21\": {\n        \"image\": \"STA21.nii.gz\", \n        \"mask\": \"STA21_mask.nii.gz\", \n        \"mask_dil\": \"STA21_mask_dil.nii.gz\", \n        \"volume_mask\": 89246.38487071976, \n        \"volume_mask_dil\": 120038.46124333153\n    }, \n    \"22\": {\n        \"image\": \"STA22.nii.gz\", \n        \"mask\": \"STA22_mask.nii.gz\", \n        \"mask_dil\": \"STA22_mask_dil.nii.gz\", \n        \"volume_mask\": 101490.30689354343, \n        \"volume_mask_dil\": 134909.44540126887\n    }, \n    \"23\": {\n        \"image\": \"STA23.nii.gz\", \n        \"mask\": \"STA23_mask.nii.gz\", \n        \"mask_dil\": \"STA23_mask_dil.nii.gz\", \n        \"volume_mask\": 106235.50507484014, \n        \"volume_mask_dil\": 140715.503598928\n    }, \n    \"24\": {\n        \"image\": \"STA24.nii.gz\", \n        \"mask\": \"STA24_mask.nii.gz\", \n        \"mask_dil\": \"STA24_mask_dil.nii.gz\", \n        \"volume_mask\": 130878.99653601555, \n        \"volume_mask_dil\": 170357.1202916332\n    }, \n    \"25\": {\n        \"image\": \"STA25.nii.gz\", \n        \"mask\": \"STA25_mask.nii.gz\", \n        \"mask_dil\": \"STA25_mask_dil.nii.gz\", \n        \"volume_mask\": 178492.25774336213, \n        \"volume_mask_dil\": 227175.59493246424\n    }, \n    \"26\": {\n        \"image\": \"STA26.nii.gz\", \n        \"mask\": \"STA26_mask.nii.gz\", \n        \"mask_dil\": \"STA26_mask_dil.nii.gz\", \n        \"volume_mask\": 201635.08283969283, \n        \"volume_mask_dil\": 253806.15093200956\n    }, \n    \"27\": {\n        \"image\": \"STA27.nii.gz\", \n        \"mask\": \"STA27_mask.nii.gz\", \n        \"mask_dil\": \"STA27_mask_dil.nii.gz\", \n        \"volume_mask\": 210435.81779203523, \n        \"volume_mask_dil\": 263941.1528740433\n    }, \n    \"28\": {\n        \"image\": \"STA28.nii.gz\", \n        \"mask\": \"STA28_mask.nii.gz\", \n        \"mask_dil\": \"STA28_mask_dil.nii.gz\", \n        \"volume_mask\": 233175.18840337868, \n        \"volume_mask_dil\": 290329.5337829808\n    }, \n    \"29\": {\n        \"image\": \"STA29.nii.gz\", \n        \"mask\": \"STA29_mask.nii.gz\", \n        \"mask_dil\": \"STA29_mask_dil.nii.gz\", \n        \"volume_mask\": 257051.194746539, \n        \"volume_mask_dil\": 317684.56706204003\n    }, \n    \"30\": {\n        \"image\": \"STA30.nii.gz\", \n        \"mask\": \"STA30_mask.nii.gz\", \n        \"mask_dil\": \"STA30_mask_dil.nii.gz\", \n        \"volume_mask\": 282285.5319890282, \n        \"volume_mask_dil\": 346364.6513653975\n    }, \n    \"31\": {\n        \"image\": \"STA31.nii.gz\", \n        \"mask\": \"STA31_mask.nii.gz\", \n        \"mask_dil\": \"STA31_mask_dil.nii.gz\", \n        \"volume_mask\": 312587.62620157294, \n        \"volume_mask_dil\": 380993.6413300073\n    }, \n    \"32\": {\n        \"image\": \"STA32.nii.gz\", \n        \"mask\": \"STA32_mask.nii.gz\", \n        \"mask_dil\": \"STA32_mask_dil.nii.gz\", \n        \"volume_mask\": 340744.4484698328, \n        \"volume_mask_dil\": 413634.54276009236\n    }, \n    \"33\": {\n        \"image\": \"STA33.nii.gz\", \n        \"mask\": \"STA33_mask.nii.gz\", \n        \"mask_dil\": \"STA33_mask_dil.nii.gz\", \n        \"volume_mask\": 356126.40670901025, \n        \"volume_mask_dil\": 430765.4864316511\n    }, \n    \"34\": {\n        \"image\": \"STA34.nii.gz\", \n        \"mask\": \"STA34_mask.nii.gz\", \n        \"mask_dil\": \"STA34_mask_dil.nii.gz\", \n        \"volume_mask\": 392819.77292167663, \n        \"volume_mask_dil\": 472869.6483262277\n    }, \n    \"35\": {\n        \"image\": \"STA35.nii.gz\", \n        \"mask\": \"STA35_mask.nii.gz\", \n        \"mask_dil\": \"STA35_mask_dil.nii.gz\", \n        \"volume_mask\": 418491.86852033855, \n        \"volume_mask_dil\": 502023.33085117553\n    }, \n    \"36\": {\n        \"image\": \"STA36.nii.gz\", \n        \"mask\": \"STA36_mask.nii.gz\", \n        \"mask_dil\": \"STA36_mask_dil.nii.gz\", \n        \"volume_mask\": 422497.7414778769, \n        \"volume_mask_dil\": 506874.512634493\n    }, \n    \"37\": {\n        \"image\": \"STA37.nii.gz\", \n        \"mask\": \"STA37_mask.nii.gz\", \n        \"mask_dil\": \"STA37_mask_dil.nii.gz\", \n        \"volume_mask\": 390531.14151572104, \n        \"volume_mask_dil\": 470833.9439705052\n    }\n}"
  },
  {
    "path": "doc/doxyfile",
    "content": "# Doxyfile 1.8.9.1\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"Motion Correction and Volumetric Image Reconstruction of 2D Ultra-fast MRI\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         = \n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          = \n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           = \n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = \"./\"\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = NO\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = YES\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        = \n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    = \n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                = \n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              = \n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = YES\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      = \n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See http://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = YES\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = YES\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = YES\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       = \n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    = \n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            = \n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         = \n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces.\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = \"../niftymic\"\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: http://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank the\n# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,\n# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,\n# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,\n# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,\n# *.qsf, *.as and *.js.\n\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.java \\\n                         *.ii \\\n                         *.ixx \\\n                         *.ipp \\\n                         *.i++ \\\n                         *.inl \\\n                         *.idl \\\n                         *.ddl \\\n                         *.odl \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++ \\\n                         *.cs \\\n                         *.d \\\n                         *.php \\\n                         *.php4 \\\n                         *.php5 \\\n                         *.phtml \\\n                         *.inc \\\n                         *.m \\\n                         *.markdown \\\n                         *.md \\\n                         *.mm \\\n                         *.dox \\\n                         *.py \\\n                         *.f90 \\\n                         *.f \\\n                         *.for \\\n                         *.tcl \\\n                         *.vhd \\\n                         *.vhdl \\\n                         *.ucf \\\n                         *.qsf \\\n                         *.as \\\n                         *.js\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                = \n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       = \n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        = \n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           = \n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       = *\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             = \n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n\nINPUT_FILTER           = \n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n\nFILTER_PATTERNS        = \n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS = \n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE = \n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = YES\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see http://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the\n# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the\n# cost of reduced performance. This can be particularly helpful with template\n# rich C++ code for which doxygen's built-in parser lacks the necessary type\n# information.\n# Note: The availability of this option depends on whether or not doxygen was\n# compiled with the --with-libclang option.\n# The default value is: NO.\n\nCLANG_ASSISTED_PARSING = NO\n\n# If clang assisted parsing is enabled you can provide the compiler with command\n# line options that you would normally use when invoking the compiler. Note that\n# the include paths will already be set by doxygen for the files and directories\n# specified with INPUT and INCLUDE_PATH.\n# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.\n\nCLANG_OPTIONS          = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            = \n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            = \n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        = \n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  = \n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       = \n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# http://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = YES\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: http://developer.apple.com/tools/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               = \n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           = \n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     = \n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               = \n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   = \n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  = \n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  = \n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           = \n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = YES\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# http://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from http://www.mathjax.org before deployment.\n# The default value is: http://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     = \n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       = \n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       = \n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     = \n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. To get the times font for\n# instance you can specify\n# EXTRA_PACKAGES=times\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         = amsmath,amssymb,latexsym\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           = \n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           = \n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET = \n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      = \n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# http://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    = \n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    = \n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             = \n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sf.net) file that captures the\n# structure of the code including all documentation. Note that this feature is\n# still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX = \n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           = \n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  = \n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             = \n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      = \n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               = \n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       = \n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            = \n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               = \n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = NO\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = YES\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           = \n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = YES\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 100\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = YES\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = YES\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot.\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif and svg.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = svg\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = YES\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               = \n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           = \n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           = \n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           = \n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      = \n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  = \n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "docker/docker-compose.yml",
    "content": "# Comments:\n#  - should work in principle, but not tested\n#  - seems not possible to add two tags per \"service\" with one run\n#  - existing docker images were created by executing Dockerfiles individually\n\n# SSH_PRIVATE_KEY=\"$(cat ~/.ssh/id_rsa)\" docker-compose build \n\nversion: \"3.7\"\n\nservices:\n\n  itk_niftymic:\n    build:\n      context: ./itk_niftymic\n      args:\n        VERSION: v4.13.1-niftymic-v1\n    image: itk_niftymic:v4.13.1-niftymic-v1\n\n  simplereg_dependencies:\n    build:\n      context: ./simplereg_dependencies\n    image: simplereg_dependencies:noitksnap\n\n  niftymic:\n    build:\n      context: ./niftymic\n      args:\n        VERSION: latest\n        FETAL_SEG_TOOL: monaifbs\n    image: niftymic:latest\n\n"
  },
  {
    "path": "docker/itk_niftymic/.dockerignore",
    "content": ".git*\n.cache"
  },
  {
    "path": "docker/itk_niftymic/Dockerfile",
    "content": "#\n# Building of Docker image:\n#   docker build --build-arg VERSION=v? -t renbem/itk_niftymic:v? -t renbem/itk_niftymic .\n\nARG VERSION=latest\nARG REPO=ITK_NiftyMIC\nARG IMAGE=python:3.6-slim\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE as compile-image\n\nARG REPO\nARG VERSION\n\nRUN apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        cmake \\\n        git \\\n        && \\\n    rm -rf /var/lib/apt/lists/* \n\nRUN if [ \"$VERSION\" = \"latest\" ] ; then \\\n        git clone \\\n        https://github.com/gift-surg/${REPO}.git /code/${REPO}/${REPO} \\\n    ;else \\\n        git clone \\\n        --branch ${VERSION} \\\n        https://github.com/gift-surg/${REPO}.git /code/${REPO}/${REPO} \\\n    ;fi\n\nRUN mkdir -p /code/${REPO}/${REPO}-build && \\\n    cd /code/${REPO}/${REPO}-build && \\\n    cmake \\\n        -D BUILD_EXAMPLES=OFF \\\n        -D BUILD_SHARED_LIBS=ON \\\n        -D BUILD_TESTING=OFF \\\n        -D CMAKE_BUILD_TYPE=Release \\\n        -D ITK_LEGACY_SILENT=ON \\\n        -D ITK_WRAP_covariant_vector_double=ON \\\n        -D ITK_WRAP_double=ON \\\n        -D ITK_WRAP_float=ON \\\n        -D ITK_WRAP_PYTHON=ON \\\n        -D ITK_WRAP_signed_char=ON \\\n        -D ITK_WRAP_signed_long=ON \\\n        -D ITK_WRAP_signed_short=ON \\\n        -D ITK_WRAP_unsigned_char=ON \\\n        -D ITK_WRAP_unsigned_long=ON \\\n        -D ITK_WRAP_unsigned_short=ON \\\n        -D ITK_WRAP_vector_double=ON \\\n        -D ITK_WRAP_vector_float=ON \\\n        -D Module_BridgeNumPy=ON \\\n        -D Module_ITKReview=ON \\\n        -D Module_SmoothingRecursiveYvvGaussianFilter=ON \\\n        /code/${REPO}/${REPO}\nRUN cd /code/${REPO}/${REPO}-build && make -j 4\n\n# install files to /usr/local\nRUN cd /code/${REPO}/${REPO}-build && make install\n\n# make shared libraries available to Python\nENV LD_LIBRARY_PATH=\"/usr/local/lib:$LD_LIBRARY_PATH\"\n\n# remove unnecessary .git folders\nRUN rm -r /code/${REPO}/${REPO}/.git*\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE AS runtime-image\n\nARG REPO\nARG VERSION\n\nLABEL author=\"Michael Ebner\"\nLABEL email=\"michael.ebner@kcl.ac.uk\"\nLABEL title=\"$REPO\"\nLABEL version=\"$VERSION\"\nLABEL uri=\"https://github.com/gift-surg/${REPO}\"\n\n# copy compiled ITK files and make libraries available to Python\nCOPY --from=compile-image /usr/local /usr/local\nENV LD_LIBRARY_PATH=\"/usr/local/lib:$LD_LIBRARY_PATH\"\n\n# add Dockerfile to image\nADD Dockerfile /\n\n# use bash with color output\nRUN echo 'alias ls=\"ls --color=auto\"' >> ~/.bashrc\nCMD bash\n"
  },
  {
    "path": "docker/niftymic/.dockerignore",
    "content": ".git*\n.cache"
  },
  {
    "path": "docker/niftymic/Dockerfile",
    "content": "#\n# Building of Docker image with default monaifbs segmentation tool:\n#   docker build --build-arg VERSION=v? -t renbem/niftymic:v? -t renbem/niftymic .\n#\n# If building with fetal_brain_seg as segmentation pipeline:\n#   docker build --build-arg VERSION=v? --build-arg FETAL_SEG_TOOL=fetal_brain_seg -t renbem/niftymic:v? -t renbem/niftymic .\n\nARG VERSION=latest\nARG REPO=NiftyMIC\n# default use monaifbs for segmentation. Define this arg as fetal_brain_seg to use previous app\n# https://github.com/gift-surg/fetal_brain_seg.git\nARG FETAL_SEG_TOOL=monaifbs\n\n\n# GUI with ITK-Snap does not work at the moment, unfortunately\nARG IMAGE=renbem/simplereg_dependencies:noitksnap\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE as compile-image\n\nARG REPO\nARG VERSION\nARG FETAL_SEG_TOOL\n\nRUN apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        git \\\n        && \\\n    rm -rf /var/lib/apt/lists/* \n\n# download NiftyMIC\nRUN if [ \"$VERSION\" = \"latest\" ] ; then \\\n        git clone \\\n        https://github.com/gift-surg/${REPO}.git /app/${REPO} \\\n    ;else \\\n        git clone \\\n        --branch ${VERSION} \\\n        https://github.com/gift-surg/${REPO}.git /app/${REPO} \\\n    ;fi\n\n# fetch MONAIfbs and download pretrained model for MONAIfbs\nRUN if [ \"$FETAL_SEG_TOOL\" = \"monaifbs\" ] ; then \\\n        cd /app/${REPO} && \\\n        git submodule update --init && \\\n        # fetch the pretrained model\n        cd /app && \\\n        pip install zenodo-get && \\\n        zenodo_get 10.5281/zenodo.4282679 && \\\n        tar xvf models.tar.gz && \\\n        mv models /app/${REPO}/MONAIfbs/monaifbs/ && \\\n        # remove the downloaded compressed file\n        rm -r /app/models.tar.gz \\\n    ;fi\n\n# download fetal_brain_seg if required (need to create an empty directory for following copy, line 105)\nRUN mkdir /app/fetal_brain_seg\nADD https://github.com/taigw/Demic/archive/v0.1.tar.gz /app/Demic-0.1.tar.gz\nRUN if [ \"$FETAL_SEG_TOOL\" = \"fetal_brain_seg\" ] ; then \\\n        git clone \\\n        https://github.com/gift-surg/fetal_brain_seg.git /app/fetal_brain_seg  && \\\n        cd /app && \\\n        tar xvf Demic-0.1.tar.gz && \\\n        mv Demic-0.1 /app/fetal_brain_seg/Demic && \\\n        # remove unecessary .git folders\n        rm -r /app/fetal_brain_seg/.git* && \\\n        rm -r /app/fetal_brain_seg/Demic/.git* \\\n    ;fi\n\n# remove unnecessary folders\nRUN rm -r /app/${REPO}/.git*\nRUN rm -r /app/Demic-0.1.tar.gz\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE AS runtime-image\n\nARG REPO\nARG VERSION\nARG FETAL_SEG_TOOL\n\nLABEL author=\"Michael Ebner\"\nLABEL email=\"michael.ebner@kcl.ac.uk\"\nLABEL title=\"$REPO\"\nLABEL version=\"$VERSION\"\nLABEL uri=\"https://github.com/gift-surg/${REPO}\"\n\n# install NiftyMIC with specific python library versions\nCOPY --from=compile-image /app/${REPO} /app/${REPO}\nWORKDIR /app/${REPO}\nRUN apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        nifti2dicom \\\n        && \\\n    rm -rf /var/lib/apt/lists/*\n\nRUN pip install \\\n    matplotlib==3.1.1 \\\n    natsort==6.0.0 \\\n    nibabel==2.4.1 \\\n    nipype==1.2.0 \\\n    nose==1.3.7 \\\n    numpy==1.16.4 \\\n    pandas==0.25.0 \\\n    pydicom==1.3.0 \\\n    scikit_image==0.15.0 \\\n    scipy==1.3.0 \\\n    seaborn==0.9.0 \\\n    SimpleITK==1.2.4 \\\n    six==1.12.0 \\\n    pysitk==0.2.19 \\\n    simplereg==0.3.2 \\\n    nsol==0.1.14\n# install monaifbs dependencies\nRUN if [ \"$FETAL_SEG_TOOL\" = \"monaifbs\" ] ; then \\\n        pip install \\\n        torch==1.4.0 \\\n        torch-summary==1.4.3 \\\n        monai==0.3.0  \\\n        pyyaml==5.3.1 \\\n        pytorch-ignite==0.4.2 \\\n        tensorboard==2.3.0 \\\n    ;fi\n# install packages for niftymic and monaifbs\nRUN pip install -e .\nRUN if [ \"$FETAL_SEG_TOOL\" = \"monaifbs\" ] ; then \\\n        pip install -e /app/${REPO}/MONAIfbs/ \\\n    ;fi\n\n# prepare fetal_brain_seg with specific python library versions if required\nCOPY --from=compile-image /app/fetal_brain_seg /app/fetal_brain_seg\nRUN if [ \"$FETAL_SEG_TOOL\" = \"fetal_brain_seg\" ] ; then \\\n        cd /app/fetal_brain_seg && \\\n        pip install \\\n        niftynet==0.2 \\\n        tensorflow==1.12.0 && \\\n        SITEDIR=$(python -m site --user-site) && \\\n        mkdir -p $SITEDIR && \\\n        echo /app/fetal_brain_seg > $SITEDIR/Demic.pth && \\\n        export FETAL_BRAIN_SEG=/app/fetal_brain_seg \\\n    ;else \\\n        rm -r /app/fetal_brain_seg \\\n    ;fi\n\n# add Dockerfile to image\nADD Dockerfile /\n\nWORKDIR /app\n\n# use bash with color output\nRUN echo 'alias ls=\"ls --color=auto\"' >> ~/.bashrc\nCMD bash"
  },
  {
    "path": "docker/simplereg_dependencies/.dockerignore",
    "content": ".git*\n.cache"
  },
  {
    "path": "docker/simplereg_dependencies/Dockerfile",
    "content": "#\n# Building of Docker image:\n#   docker build -t renbem/simplereg_dependencies .\n#   \n# Run with GUI (however, does not work unfortunately):\n#   xhost +local:docker  # needed only the first time\n#   docker run --rm -ti --net=host --env=\"DISPLAY\" --volume=\"$HOME/.Xauthority:/root/.Xauthority:rw\" renbem/simplereg_dependencies\n\nARG VERSION=noitksnap\nARG REPO=SimpleReg-dependencies\nARG IMAGE=renbem/itk_niftymic:v4.13.1-niftymic-v1\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE as compile-image-fsl\n\nARG REPO\nARG VERSION\n\nRUN apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        wget \\\n        && \\\n    rm -rf /var/lib/apt/lists/* \n\n# install FSL\nRUN wget -O- http://neuro.debian.net/lists/stretch.au.full | \\\n    tee /etc/apt/sources.list.d/neurodebian.sources.list\nRUN apt-key adv --recv-keys --keyserver \\\n    hkp://pool.sks-keyservers.net:80 0xA5D32F012649A5A9\nRUN apt-get update && \\\n    DEBIAN_FRONTEND=noninteractive \\\n        apt-get install -y fsl-core \\\n        && \\\n    rm -rf /var/lib/apt/lists/* \n\n# -----------------------------------------------------------------------------\nFROM $IMAGE as compile-image-niftyreg\n\nRUN apt-get update && \\\n    apt-get install -y \\\n        build-essential \\\n        cmake \\\n        git \\\n        && \\\n    rm -rf /var/lib/apt/lists/* \n\n# install NiftyReg\nRUN git clone https://github.com/KCL-BMEIS/niftyreg.git /code/niftyreg\nRUN mkdir -p /code/niftyreg-build && \\\n    mkdir -p /usr/share/niftyreg\nRUN cd /code/niftyreg-build && \\\n    cmake \\\n        -D CMAKE_INSTALL_PREFIX=/usr/share/niftyreg \\\n        /code/niftyreg\nRUN cd /code/niftyreg-build && make -j4\nRUN cd /code/niftyreg-build && make install\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE as compile-image-itksnap\n# itksnap GUI not working unfortunately\n\n# convert3D\nADD c3d-1.0.0-Linux-x86_64.tar.gz /code/\nRUN mv /code/c3d-1.0.0-Linux-x86_64 /code/c3d\n\n# itksnap with QT4 opens but GUI has issues then\n# a)\nADD itksnap-3.8.0-20190612-Linux-x86_64-qt4.tar.gz /code/\nRUN mv /code/itksnap-3.8.0-20190612-Linux-gcc64-qt4 /code/itksnap\n# b)\n# ADD itksnap-nightly-master-Linux-x86_64-qt4.tar.gz /code/\n# RUN mv /code/itksnap-3.6.0-20170401-Linux-x86_64-qt4 /code/itksnap\n\n# versions do not work: after 'itksnap', the following error:\n# \n# [7]: The last reference on a connection was dropped without closing the connection. This is a bug in an application. See dbus_connection_unref() documentation for details.\n# Most likely, the application was supposed to call dbus_connection_close(), since this is a private connection.\n# D-Bus not built with -rdynamic so unable to print a backtrace\n# Aborted (core dumped)\n# \n# a)\n# ADD itksnap-3.8.0-20190612-Linux-x86_64.tar.gz /code/\n# RUN mv /code/itksnap-3.8.0-20190612-Linux-gcc64 /code/itksnap\n# b)\n# ADD itksnap-nightly-master-Linux-x86_64.tar.gz /code/\n# RUN mv /code/itksnap-3.6.0-20170401-Linux-x86_64 /code/itksnap\n\n\n# -----------------------------------------------------------------------------\nFROM $IMAGE AS runtime-image\n\nARG REPO\nARG VERSION\n\nLABEL author=\"Michael Ebner\"\nLABEL email=\"michael.ebner@kcl.ac.uk\"\nLABEL title=\"$REPO\"\nLABEL version=\"$VERSION\"\nLABEL uri=\"https://github.com/gift-surg/SimpleReg/wiki/simplereg-dependencies\"\n\n# copy compiled FSL files and link associated binaries\nCOPY --from=compile-image-fsl /etc/fsl /etc/fsl\nCOPY --from=compile-image-fsl /usr/lib /usr/lib\nCOPY --from=compile-image-fsl /usr/share/fsl /usr/share/fsl\nCOPY --from=compile-image-fsl /usr/bin/fsl5.0-* /usr/bin/\nRUN ln -s /usr/bin/fsl5.0-flirt /usr/local/bin/flirt && \\\n    ln -s /usr/bin/fsl5.0-fslhd /usr/local/bin/fslhd && \\\n    ln -s /usr/bin/fsl5.0-fslmodhd /usr/local/bin/fslmodhd && \\\n    ln -s /usr/bin/fsl5.0-fslorient /usr/local/bin/fslorient && \\\n    ln -s /usr/bin/fsl5.0-fslreorient2std /usr/local/bin/fslreorient2std && \\\n    ln -s /usr/bin/fsl5.0-fslswapdim /usr/local/bin/fslswapdim && \\\n    ln -s /usr/bin/fsl5.0-bet /usr/local/bin/bet\n\n# copy compiled NiftyReg files and link associated binaries\nCOPY --from=compile-image-niftyreg /usr/share/niftyreg /usr/share/niftyreg\nENV PATH=\"/usr/share/niftyreg/bin:$PATH\"\n\n# copy compiled itksnap files\nCOPY --from=compile-image-itksnap /code/c3d/bin /usr/local/bin\nCOPY --from=compile-image-itksnap /code/c3d/lib /usr/local/lib\nCOPY --from=compile-image-itksnap /code/c3d/share /usr/local/share\n\nCOPY --from=compile-image-itksnap /code/itksnap/bin /usr/local/bin\nCOPY --from=compile-image-itksnap /code/itksnap/lib /usr/local/lib\n\n# to make use of itksnap GUI within docker (in principle;\n# but errors/problems as above; thus, deactivated for now)\n# RUN apt-get update && \\\n#     apt-get install -y \\\n#         wget \\\n#         libglu1 \\\n#         libcurl4-openssl-dev \\\n#         libsm6 \\\n#         libxt6 \\\n#         libfreetype6 \\\n#         libxrender1 \\\n#         libfontconfig1 \\\n#         libglib2.0-0 \\\n#         libqt4-dev \\\n#         libgtk2.0-dev \\\n#         curl \\\n#         libgtk2.0 \\\n#         qt5dxcb-plugin \\\n#         && \\\n#     rm -rf /var/lib/apt/lists/*\n# RUN apt-get update && \\\n#     apt-get install -y \\\n#         libxcb1 libxcb1-dev \\\n#         libx11-dev \\\n#         libgl1-mesa-dev \\\n#         libxt-dev libxft-dev \\\n#         && \\\n#     rm -rf /var/lib/apt/lists/*\n# ADD libpng12-0_1.2.54-1ubuntu1.1_amd64.deb /code/\n# RUN dpkg -i /code/libpng12-0_1.2.54-1ubuntu1.1_amd64.deb\n\n# add Dockerfile to image\nADD Dockerfile /\n\n# use bash with color output\nRUN echo 'alias ls=\"ls --color=auto\"' >> ~/.bashrc\nCMD bash"
  },
  {
    "path": "niftymic/__about__.py",
    "content": "__author__ = \"Michael Ebner\"\n__email__ = \"michael.ebner@kcl.ac.uk\"\n__license__ = \"BSD-3-Clause\"\n__version__ = \"0.9\"\n__summary__ = \"NiftyMIC is a research-focused toolkit for \" \\\n    \"motion-correction and volumetric image reconstruction of \" \\\n    \"2D ultra-fast MRI.\"\n"
  },
  {
    "path": "niftymic/__init__.py",
    "content": "from niftymic.__about__ import (\n    __author__,\n    __email__,\n    __license__,\n    __summary__,\n    __version__,\n )\n\n__all__ = [\n    \"__author__\",\n    \"__email__\",\n    \"__license__\",\n    \"__summary__\",\n    \"__version__\",\n]\n"
  },
  {
    "path": "niftymic/application/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/application/correct_bias_field.py",
    "content": "##\n# \\file correct_bias_field.py\n# \\brief      Script to correct for bias field. Based on N4ITK\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\n# Import libraries\nimport numpy as np\nimport os\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_writer as dw\nimport niftymic.utilities.n4_bias_field_correction as n4itk\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Perform Bias Field correction using N4ITK.\",\n    )\n    input_parser.add_filename(required=True)\n    input_parser.add_output(required=True)\n    input_parser.add_filename_mask()\n    input_parser.add_option(\n        option_string=\"--convergence-threshold\",\n        type=float,\n        help=\"Specify the convergence threshold.\",\n        default=1e-6,\n    )\n    input_parser.add_option(\n        option_string=\"--spline-order\",\n        type=int,\n        help=\"Specify the spline order defining the bias field estimate.\",\n        default=3,\n    )\n    input_parser.add_option(\n        option_string=\"--wiener-filter-noise\",\n        type=float,\n        help=\"Specify the noise estimate defining the Wiener filter.\",\n        default=0.11,\n    )\n    input_parser.add_option(\n        option_string=\"--bias-field-fwhm\",\n        type=float,\n        help=\"Specify the full width at half maximum parameter characterizing \"\n        \"the width of the Gaussian deconvolution.\",\n        default=0.15,\n    )\n    input_parser.add_log_config(default=1)\n    input_parser.add_verbose(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]):\n        raise ValueError(\n            \"output filename invalid; allowed extensions are: %s\" %\n            \", \".join(ALLOWED_EXTENSIONS))\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # Read data\n    stack = st.Stack.from_filename(\n        file_path=args.filename,\n        file_path_mask=args.filename_mask,\n        extract_slices=False,\n    )\n\n    # Perform Bias Field Correction\n    # ph.print_title(\"Perform Bias Field Correction\")\n    bias_field_corrector = n4itk.N4BiasFieldCorrection(\n        stack=stack,\n        use_mask=True if args.filename_mask is not None else False,\n        convergence_threshold=args.convergence_threshold,\n        spline_order=args.spline_order,\n        wiener_filter_noise=args.wiener_filter_noise,\n        bias_field_fwhm=args.bias_field_fwhm,\n    )\n    ph.print_info(\"N4ITK Bias Field Correction ... \", newline=False)\n    bias_field_corrector.run_bias_field_correction()\n    stack_corrected = bias_field_corrector.get_bias_field_corrected_stack()\n    print(\"done\")\n\n    dw.DataWriter.write_image(stack_corrected.sitk, args.output)\n\n    elapsed_time = ph.stop_timing(time_start)\n\n    if args.verbose:\n        ph.show_niftis([args.filename, args.output])\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Bias Field Correction: %s\" % (\n        exe_file_info, elapsed_time))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/correct_intensities.py",
    "content": "##\n# \\file correct_intensities.py\n# \\brief      Script to perform intensity correction across images given a\n#             reference image\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\n# Import libraries\nimport numpy as np\nimport os\n\n# Import modules\nimport niftymic.base.data_reader as dr\nimport niftymic.base.stack as st\nimport niftymic.registration.flirt as regflirt\nimport niftymic.utilities.intensity_correction as ic\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Perform (linear) intensity correction across \"\n        \"stacks/images given a reference stack/image\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_dir_output(required=True)\n    input_parser.add_reference(required=True)\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_search_angle(default=180)\n    input_parser.add_prefix_output(default=\"IC_\")\n    input_parser.add_log_config(default=1)\n    input_parser.add_option(\n        option_string=\"--registration\",\n        type=int,\n        help=\"Turn on/off registration from image to reference prior to \"\n        \"intensity correction.\",\n        default=0)\n    input_parser.add_verbose(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    if args.reference in args.filenames:\n        args.filenames.remove(args.reference)\n\n    # Read data\n    data_reader = dr.MultipleImagesReader(\n        args.filenames, suffix_mask=args.suffix_mask, extract_slices=False)\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    data_reader = dr.MultipleImagesReader(\n        [args.reference], suffix_mask=args.suffix_mask, extract_slices=False)\n    data_reader.read_data()\n    reference = data_reader.get_data()[0]\n\n    if args.registration:\n        # Define search angle ranges for FLIRT in all three dimensions\n        search_angles = [\"-searchr%s -%d %d\" %\n                         (x, args.search_angle, args.search_angle)\n                         for x in [\"x\", \"y\", \"z\"]]\n        search_angles = (\" \").join(search_angles)\n        registration = regflirt.FLIRT(\n            moving=reference,\n            registration_type=\"Rigid\",\n            use_fixed_mask=True,\n            use_moving_mask=True,\n            options=search_angles,\n            use_verbose=False,\n        )\n\n    # Perform Intensity Correction\n    ph.print_title(\"Perform Intensity Correction\")\n    intensity_corrector = ic.IntensityCorrection(\n        use_reference_mask=True,\n        use_individual_slice_correction=False,\n        prefix_corrected=args.prefix_output,\n        use_verbose=False,\n    )\n    stacks_corrected = [None] * len(stacks)\n    for i, stack in enumerate(stacks):\n        if args.registration:\n            ph.print_info(\"Image %d/%d: Registration ... \"\n                          % (i+1, len(stacks)), newline=False)\n            registration.set_fixed(stack)\n            registration.run()\n            transform_sitk = registration.get_registration_transform_sitk()\n            stack.update_motion_correction(transform_sitk)\n            print(\"done\")\n\n        ph.print_info(\"Image %d/%d: Intensity Correction ... \"\n                      % (i+1, len(stacks)), newline=False)\n\n        ref = reference.get_resampled_stack(stack.sitk)\n        ref = st.Stack.from_sitk_image(\n            image_sitk=ref.sitk,\n            image_sitk_mask=stack.sitk_mask*ref.sitk_mask,\n            filename=reference.get_filename()\n        )\n        intensity_corrector.set_stack(stack)\n        intensity_corrector.set_reference(ref)\n        intensity_corrector.run_linear_intensity_correction()\n        # intensity_corrector.run_affine_intensity_correction()\n        stacks_corrected[i] = \\\n            intensity_corrector.get_intensity_corrected_stack()\n        print(\"done (c1 = %g) \" % intensity_corrector.get_intensity_correction_coefficients())\n\n        # Write Data\n        stacks_corrected[i].write(\n            args.dir_output, write_mask=True, suffix_mask=args.suffix_mask)\n\n        if args.verbose:\n            sitkh.show_stacks([\n                reference, stacks_corrected[i],\n                # stacks[i],\n            ],\n                segmentation=stacks_corrected[i])\n            # ph.pause()\n\n    # Write reference too (although not intensity corrected)\n    reference.write(args.dir_output,\n                    filename=args.prefix_output+reference.get_filename(),\n                    write_mask=True, suffix_mask=args.suffix_mask)\n\n    elapsed_time = ph.stop_timing(time_start)\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Intensity Correction(s): %s\" %\n          (exe_file_info, elapsed_time))\n\n    return 0\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/multiply.py",
    "content": "##\n# \\file multiply.py\n# \\brief      Script multiply images with each other.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\nimport os\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.data_writer as dw\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Multiply images. \"\n        \"Pixel type is determined by first given image.\",\n    )\n\n    input_parser.add_filenames(required=True)\n    input_parser.add_output(required=True)\n    input_parser.add_verbose(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if len(args.filenames) < 2:\n        raise IOError(\"At least two images must be provided\")\n\n    out_sitk = sitk.ReadImage(args.filenames[0])\n    for f in args.filenames[1:]:\n        im_sitk = sitk.Cast(sitk.ReadImage(f), out_sitk.GetPixelIDValue())\n        out_sitk = out_sitk * im_sitk\n\n    dw.DataWriter.write_image(out_sitk, args.output)\n\n    if args.verbose:\n        args.filenames.insert(0, args.output)\n        ph.show_niftis(args.filenames)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/nifti2dicom.py",
    "content": "##\n# \\file convert_nifti_to_dicom.py\n# \\brief      Script to convert a 3D NIfTI image to DICOM.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Mar 2018\n#\n\n# NOTES (quite some candidates were tried to get a working solution):\n#\n# - nifti2dicom (https://packages.ubuntu.com/xenial/science/nifti2dicom; tested version 0.4.11):\n#   Although nifti2dicom allows the import of a DICOM header from a template\n#   (-d) not all tags would be set correctly. E.g. if DOB is not given at\n#   template, it would just be set to 01.01.1990 which would prevent the\n#   resulting dcm file to be grouped correctly with the original data.\n#   Moreover, annoying tags like 'InstitutionName' are set to their predefined\n#   value which cannot be deleted (only overwritten).\n#   Apart from that, only a relatively small selection of tags can be edited.\n#   However, it does a good job in creating a series of 2D DICOM slices from a\n#   NIfTI file (including correct image orientation!).\n#\n# - medcon (https://packages.ubuntu.com/xenial/medcon; tested version 0.14.1):\n#   A single 3D dcm file can be created but image orientation is flawed\n#   when created from a nifti file directly.\n#   However, if a 3D stack is created from a set of 2D dicoms, the orientation\n#   stays correct.\n#\n# - pydicom (https://github.com/pydicom/pydicom; tested version 1.0.2):\n#   Can only read a single 3D dcm file. In particular, it is not possible\n#   to read a set of 2D slices unless a DICOMDIR is provided which is not\n#   always guaranteed to exist (I tried to create it from 2D slices using\n#   dcmmkdir from dcmtk and dcm4che -- neither seemed to work reliably)\n#   Once the dicom file is read, pydicom does a really good job of updating\n#   DICOM tags + there are plenty of tags available to be chosen from!\n#   Saving a single 3D DICOM file is very easy too then.\n\nimport os\nimport pydicom\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.data_reader as dr\nfrom niftymic.utilities.input_arparser import InputArgparser\nfrom niftymic.definitions import DIR_TMP\n\n\nCOPY_DICOM_TAGS = {\n    # important for grouping\n    \"PatientID\",\n    \"PatientName\",\n    \"PatientBirthDate\",\n    \"StudyInstanceUID\",\n\n    # additional information\n    \"StudyID\",\n    \"AcquisitionDate\",\n    \"PatientSex\",\n    \"MagneticFieldStrength\",\n    \"Manufacturer\",\n    \"ManufacturerModelName\",\n    \"Modality\",\n    \"StudyDescription\",\n}\n\nACCESSION_NUMBER = \"1\"\nSERIES_NUMBER = \"0\"\nIMAGE_COMMENTS = \"*** NOT APPROVED FOR CLINICAL USE ***\"\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Convert NIfTI to DICOM image\",\n    )\n    input_parser.add_filename(required=True)\n    input_parser.add_option(\n        option_string=\"--template\",\n        type=str,\n        required=True,\n        help=\"Template DICOM to extract relevant DICOM tags.\",\n    )\n    input_parser.add_dir_output(required=True)\n    input_parser.add_label(\n        help=\"Label used for series description of DICOM output.\",\n        default=\"SRR_NiftyMIC\")\n    input_parser.add_argument(\n        \"--volume\", \"-volume\",\n        action='store_true',\n        help=\"If given, the output DICOM file is combined as 3D volume\"\n    )\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    # Prepare for final DICOM output\n    ph.create_directory(args.dir_output)\n\n    if args.volume:\n        dir_output_2d_slices = os.path.join(DIR_TMP, \"dicom_slices\")\n    else:\n        dir_output_2d_slices = os.path.join(args.dir_output, args.label)\n    ph.create_directory(dir_output_2d_slices, delete_files=True)\n\n    # read NiftyMIC version (if available)\n    data_reader = dr.ImageHeaderReader(args.filename)\n    data_reader.read_data()\n    niftymic_version = data_reader.get_niftymic_version()\n    if niftymic_version is None:\n        niftymic_version = \"NiftyMIC\"\n    else:\n        niftymic_version = \"NiftyMIC-v%s\" % niftymic_version\n\n    # Create set of 2D DICOM slices from 3D NIfTI image\n    # (correct image orientation!)\n    ph.print_title(\"Create set of 2D DICOM slices from 3D NIfTI image\")\n    cmd_args = [\"nifti2dicom\"]\n    cmd_args.append(\"-i '%s'\" % args.filename)\n    cmd_args.append(\"-o '%s'\" % dir_output_2d_slices)\n    cmd_args.append(\"-d '%s'\" % args.template)\n    cmd_args.append(\"--prefix ''\")\n    cmd_args.append(\"--seriesdescription '%s'\" % args.label)\n    cmd_args.append(\"--accessionnumber '%s'\" % ACCESSION_NUMBER)\n    cmd_args.append(\"--seriesnumber '%s'\" % SERIES_NUMBER)\n    cmd_args.append(\"--institutionname '%s'\" % IMAGE_COMMENTS)\n\n    # Overwrite default \"nifti2dicom\" tags which would be added otherwise\n    # (no deletion/update with empty '' sufficient to overwrite them)\n    cmd_args.append(\"--manufacturersmodelname '%s'\" % \"NiftyMIC\")\n    cmd_args.append(\"--protocolname '%s'\" % niftymic_version)\n\n    cmd_args.append(\"-y\")\n    ph.execute_command(\" \".join(cmd_args))\n\n    if args.volume:\n        path_to_output = os.path.join(args.dir_output, \"%s.dcm\" % args.label)\n        # Combine set of 2D DICOM slices to form 3D DICOM image\n        # (image orientation stays correct)\n        ph.print_title(\"Combine set of 2D DICOM slices to form 3D DICOM image\")\n        cmd_args = [\"medcon\"]\n        cmd_args.append(\"-f '%s'/*.dcm\" % dir_output_2d_slices)\n        cmd_args.append(\"-o '%s'\" % path_to_output)\n        cmd_args.append(\"-c dicom\")\n        cmd_args.append(\"-stack3d\")\n        cmd_args.append(\"-n\")\n        cmd_args.append(\"-qc\")\n        cmd_args.append(\"-w\")\n        ph.execute_command(\" \".join(cmd_args))\n\n        # Update all relevant DICOM tags accordingly\n        ph.print_title(\"Update all relevant DICOM tags accordingly\")\n        print(\"\")\n        dataset_template = pydicom.dcmread(args.template)\n        dataset = pydicom.dcmread(path_to_output)\n\n        # Copy tags from template (to guarantee grouping with original data)\n        update_dicom_tags = {}\n        for tag in COPY_DICOM_TAGS:\n            try:\n                update_dicom_tags[tag] = getattr(dataset_template, tag)\n            except:\n                update_dicom_tags[tag] = \"\"\n\n        # Additional tags\n        update_dicom_tags[\"SeriesDescription\"] = args.label\n        update_dicom_tags[\"InstitutionName\"] = institution_name\n        update_dicom_tags[\"ImageComments\"] = IMAGE_COMMENTS\n        update_dicom_tags[\"AccessionNumber\"] = ACCESSION_NUMBER\n        update_dicom_tags[\"SeriesNumber\"] = SERIES_NUMBER\n\n        for tag in sorted(update_dicom_tags.keys()):\n            value = update_dicom_tags[tag]\n            setattr(dataset, tag, value)\n            ph.print_info(\"%s: '%s'\" % (tag, value))\n\n        dataset.save_as(path_to_output)\n        print(\"\")\n        ph.print_info(\"3D DICOM image written to '%s'\" % path_to_output)\n\n    else:\n        ph.print_info(\"DICOM images written to '%s'\" % dir_output_2d_slices)\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/propagate_mask.py",
    "content": "##\n# \\file propagate_mask.py\n# \\brief      Script to propagate an image mask using rigid registration\n#\n# \\author     Michael Ebner (michael.ebner@kcl.ac.uk)\n# \\date       Aug 2019\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.data_writer as dw\nimport niftymic.base.stack as st\nimport niftymic.registration.flirt as regflirt\nimport niftymic.registration.niftyreg as niftyreg\nimport niftymic.utilities.stack_mask_morphological_operations as stmorph\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import V2V_METHOD_OPTIONS, ALLOWED_EXTENSIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Propagate image mask using rigid registration.\",\n    )\n    input_parser.add_moving(required=True)\n    input_parser.add_moving_mask(required=True)\n    input_parser.add_fixed(required=True)\n    input_parser.add_output(required=True)\n    input_parser.add_v2v_method(\n        option_string=\"--method\",\n        help=\"Registration method used for the registration (%s).\" % (\n            \", or \".join(V2V_METHOD_OPTIONS)),\n        default=\"RegAladin\",\n    )\n    input_parser.add_option(\n        option_string=\"--use-moving-mask\",\n        type=int,\n        help=\"Turn on/off use of moving mask to constrain the registration.\",\n        default=0,\n    )\n    input_parser.add_dilation_radius(default=1)\n    input_parser.add_verbose(default=0)\n    input_parser.add_log_config(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]):\n        raise ValueError(\n            \"output filename invalid; allowed extensions are: %s\" %\n            \", \".join(ALLOWED_EXTENSIONS))\n\n    if args.method not in V2V_METHOD_OPTIONS:\n        raise ValueError(\"method must be in {%s}\" % (\n            \", \".join(V2V_METHOD_OPTIONS)))\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    stack = st.Stack.from_filename(\n        file_path=args.fixed,\n        extract_slices=False,\n    )\n    template = st.Stack.from_filename(\n        file_path=args.moving,\n        file_path_mask=args.moving_mask,\n        extract_slices=False,\n    )\n\n    if args.method == \"FLIRT\":\n        # Define search angle ranges for FLIRT in all three dimensions\n        # search_angles = [\"-searchr%s -%d %d\" %\n        #                  (x, args.search_angle, args.search_angle)\n        #                  for x in [\"x\", \"y\", \"z\"]]\n        # options = (\" \").join(search_angles)\n        # options += \" -noresample\"\n\n        registration = regflirt.FLIRT(\n            registration_type=\"Rigid\",\n            fixed=stack,\n            moving=template,\n            use_fixed_mask=False,\n            use_moving_mask=args.use_moving_mask,\n            # options=options,\n            use_verbose=False,\n        )\n    else:\n        registration = niftyreg.RegAladin(\n            registration_type=\"Rigid\",\n            fixed=stack,\n            moving=template,\n            use_fixed_mask=False,\n            use_moving_mask=args.use_moving_mask,\n            # options=\"-ln 2\",\n            use_verbose=False,\n        )\n\n    try:\n        registration.run()\n    except RuntimeError as e:\n        raise RuntimeError(\n            \"%s\\n\\n\"\n            \"Have you tried running the script with '--use-moving-mask 0'?\" % e)\n\n    transform_sitk = registration.get_registration_transform_sitk()\n    stack.sitk_mask = sitk.Resample(\n        template.sitk_mask,\n        stack.sitk_mask,\n        transform_sitk,\n        sitk.sitkNearestNeighbor,\n        0,\n        template.sitk_mask.GetPixelIDValue()\n    )\n    if args.dilation_radius > 0:\n        stack_mask_morpher = stmorph.StackMaskMorphologicalOperations.from_sitk_mask(\n            mask_sitk=stack.sitk_mask,\n            dilation_radius=args.dilation_radius,\n            dilation_kernel=\"Ball\",\n            use_dilation_in_plane_only=True,\n        )\n        stack_mask_morpher.run_dilation()\n        stack.sitk_mask = stack_mask_morpher.get_processed_mask_sitk()\n\n    dw.DataWriter.write_mask(stack.sitk_mask, args.output)\n\n    elapsed_time = ph.stop_timing(time_start)\n\n    if args.verbose:\n        ph.show_nifti(args.fixed, segmentation=args.output)\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Segmentation Propagation: %s\" % (\n        exe_file_info, elapsed_time))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/reconstruct_volume.py",
    "content": "##\n# \\file reconstruct_volume.py\n# \\brief      Script to reconstruct an isotropic, high-resolution volume from\n#             multiple stacks of low-resolution 2D slices including\n#             motion-correction.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       March 2017\n#\n\nimport os\nimport numpy as np\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.registration.flirt as regflirt\nimport niftymic.registration.niftyreg as niftyreg\nimport niftymic.registration.simple_itk_registration as regsitk\nimport niftymic.reconstruction.tikhonov_solver as tk\nimport niftymic.reconstruction.primal_dual_solver as pd\nimport niftymic.reconstruction.scattered_data_approximation as sda\nimport niftymic.utilities.data_preprocessing as dp\nimport niftymic.utilities.outlier_rejector as outre\nimport niftymic.utilities.intensity_correction as ic\nimport niftymic.utilities.joint_image_mask_builder as imb\nimport niftymic.utilities.segmentation_propagation as segprop\nimport niftymic.utilities.volumetric_reconstruction_pipeline as pipeline\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import V2V_METHOD_OPTIONS, ALLOWED_EXTENSIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Volumetric MRI reconstruction framework to reconstruct \"\n        \"an isotropic, high-resolution 3D volume from multiple stacks of 2D \"\n        \"slices with motion correction. The resolution of the computed \"\n        \"Super-Resolution Reconstruction (SRR) is given by the in-plane \"\n        \"spacing of the selected target stack. A region of interest can be \"\n        \"specified by providing a mask for the selected target stack. Only \"\n        \"this region will then be reconstructed by the SRR algorithm which \"\n        \"can substantially reduce the computational time.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks()\n    input_parser.add_output(required=True)\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_target_stack(default=None)\n    input_parser.add_search_angle(default=45)\n    input_parser.add_multiresolution(default=0)\n    input_parser.add_shrink_factors(default=[3, 2, 1])\n    input_parser.add_smoothing_sigmas(default=[1.5, 1, 0])\n    input_parser.add_sigma(default=1)\n    input_parser.add_reconstruction_type(default=\"TK1L2\")\n    input_parser.add_iterations(default=15)\n    input_parser.add_alpha(default=0.015)\n    input_parser.add_alpha_first(default=0.2)\n    input_parser.add_iter_max(default=10)\n    input_parser.add_iter_max_first(default=5)\n    input_parser.add_dilation_radius(default=3)\n    input_parser.add_extra_frame_target(default=10)\n    input_parser.add_bias_field_correction(default=0)\n    input_parser.add_intensity_correction(default=1)\n    input_parser.add_isotropic_resolution(default=1)\n    input_parser.add_log_config(default=1)\n    input_parser.add_subfolder_motion_correction()\n    input_parser.add_write_motion_correction(default=1)\n    input_parser.add_verbose(default=0)\n    input_parser.add_two_step_cycles(default=3)\n    input_parser.add_use_masks_srr(default=0)\n    input_parser.add_boundary_stacks(default=[10, 10, 0])\n    input_parser.add_metric(default=\"Correlation\")\n    input_parser.add_metric_radius(default=10)\n    input_parser.add_reference()\n    input_parser.add_reference_mask()\n    input_parser.add_outlier_rejection(default=1)\n    input_parser.add_threshold_first(default=0.5)\n    input_parser.add_threshold(default=0.8)\n    input_parser.add_interleave(default=3)\n    input_parser.add_slice_thicknesses(default=None)\n    input_parser.add_viewer(default=\"itksnap\")\n    input_parser.add_v2v_method(default=\"RegAladin\")\n    input_parser.add_argument(\n        \"--v2v-robust\", \"-v2v-robust\",\n        action='store_true',\n        help=\"If given, a more robust volume-to-volume registration step is \"\n        \"performed, i.e. four rigid registrations are performed using four \"\n        \"rigid transform initializations based on \"\n        \"principal component alignment of associated masks.\"\n    )\n    input_parser.add_argument(\n        \"--s2v-hierarchical\", \"-s2v-hierarchical\",\n        action='store_true',\n        help=\"If given, a hierarchical approach for the first slice-to-volume \"\n        \"registration cycle is used, i.e. sub-packages defined by the \"\n        \"specified interleave (--interleave) are registered until each \"\n        \"slice is registered independently.\"\n    )\n    input_parser.add_argument(\n        \"--sda\", \"-sda\",\n        action='store_true',\n        help=\"If given, the volumetric reconstructions are performed using \"\n        \"Scattered Data Approximation (Vercauteren et al., 2006). \"\n        \"'alpha' is considered the final 'sigma' for the \"\n        \"iterative adjustment. \"\n        \"Recommended value is, e.g., --alpha 0.8\"\n    )\n    input_parser.add_option(\n        option_string=\"--transforms-history\",\n        type=int,\n        help=\"Write entire history of applied slice motion correction \"\n        \"transformations to motion correction output directory\",\n        default=0,\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    rejection_measure = \"NCC\"\n    threshold_v2v = -2  # 0.3\n    debug = False\n\n    if args.v2v_method not in V2V_METHOD_OPTIONS:\n        raise ValueError(\"v2v-method must be in {%s}\" % (\n            \", \".join(V2V_METHOD_OPTIONS)))\n\n    if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]):\n        raise ValueError(\n            \"output filename invalid; allowed extensions are: %s\" %\n            \", \".join(ALLOWED_EXTENSIONS))\n\n    if args.alpha_first < args.alpha and not args.sda:\n        raise ValueError(\"It must hold alpha-first >= alpha\")\n\n    if args.threshold_first > args.threshold:\n        raise ValueError(\"It must hold threshold-first <= threshold\")\n\n    dir_output = os.path.dirname(args.output)\n    ph.create_directory(dir_output)\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        file_paths_masks=args.filenames_masks,\n        suffix_mask=args.suffix_mask,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n\n    if len(args.boundary_stacks) is not 3:\n        raise IOError(\n            \"Provide exactly three values for '--boundary-stacks' to define \"\n            \"cropping in i-, j-, and k-dimension of the input stacks\")\n\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n    ph.print_info(\"%d input stacks read for further processing\" % len(stacks))\n\n    if all(s.is_unity_mask() is True for s in stacks):\n        ph.print_warning(\"No mask is provided! \"\n                         \"Generated reconstruction space may be very big!\")\n        ph.print_warning(\"Consider using a mask to speed up computations\")\n\n        # args.extra_frame_target = 0\n        # ph.wrint_warning(\"Overwritten: extra-frame-target set to 0\")\n\n    # Specify target stack for intensity correction and reconstruction space\n    if args.target_stack is None:\n        target_stack_index = 0\n    else:\n        try:\n            target_stack_index = args.filenames.index(args.target_stack)\n        except ValueError as e:\n            raise ValueError(\n                \"--target-stack must correspond to an image as provided by \"\n                \"--filenames\")\n\n    # ---------------------------Data Preprocessing---------------------------\n    ph.print_title(\"Data Preprocessing\")\n\n    segmentation_propagator = segprop.SegmentationPropagation(\n        # registration_method=regflirt.FLIRT(use_verbose=args.verbose),\n        # registration_method=niftyreg.RegAladin(use_verbose=False),\n        dilation_radius=args.dilation_radius,\n        dilation_kernel=\"Ball\",\n    )\n\n    data_preprocessing = dp.DataPreprocessing(\n        stacks=stacks,\n        segmentation_propagator=segmentation_propagator,\n        use_cropping_to_mask=True,\n        use_N4BiasFieldCorrector=args.bias_field_correction,\n        target_stack_index=target_stack_index,\n        boundary_i=args.boundary_stacks[0],\n        boundary_j=args.boundary_stacks[1],\n        boundary_k=args.boundary_stacks[2],\n        unit=\"mm\",\n    )\n    data_preprocessing.run()\n    time_data_preprocessing = data_preprocessing.get_computational_time()\n\n    # Get preprocessed stacks\n    stacks = data_preprocessing.get_preprocessed_stacks()\n\n    # Define reference/target stack for registration and reconstruction\n    if args.reference is not None:\n        reference = st.Stack.from_filename(\n            file_path=args.reference,\n            file_path_mask=args.reference_mask,\n            extract_slices=False)\n\n    else:\n        reference = st.Stack.from_stack(stacks[target_stack_index])\n\n    # ------------------------Volume-to-Volume Registration--------------------\n    if len(stacks) > 1:\n\n        if args.v2v_method == \"FLIRT\":\n            # Define search angle ranges for FLIRT in all three dimensions\n            search_angles = [\"-searchr%s -%d %d\" %\n                             (x, args.search_angle, args.search_angle)\n                             for x in [\"x\", \"y\", \"z\"]]\n            options = (\" \").join(search_angles)\n            # options += \" -noresample\"\n\n            vol_registration = regflirt.FLIRT(\n                registration_type=\"Rigid\",\n                use_fixed_mask=True,\n                use_moving_mask=True,\n                options=options,\n                use_verbose=False,\n            )\n        else:\n            vol_registration = niftyreg.RegAladin(\n                registration_type=\"Rigid\",\n                use_fixed_mask=True,\n                use_moving_mask=True,\n                # options=\"-ln 2 -voff\",\n                use_verbose=False,\n            )\n        v2vreg = pipeline.VolumeToVolumeRegistration(\n            stacks=stacks,\n            reference=reference,\n            registration_method=vol_registration,\n            verbose=debug,\n            robust=args.v2v_robust,\n        )\n        v2vreg.run()\n        stacks = v2vreg.get_stacks()\n        time_registration = v2vreg.get_computational_time()\n\n    else:\n        time_registration = ph.get_zero_time()\n\n    # ---------------------------Intensity Correction--------------------------\n    if args.intensity_correction:\n        ph.print_title(\"Intensity Correction\")\n        intensity_corrector = ic.IntensityCorrection()\n        intensity_corrector.use_individual_slice_correction(False)\n        intensity_corrector.use_reference_mask(True)\n        intensity_corrector.use_stack_mask(True)\n        intensity_corrector.use_verbose(False)\n\n        for i, stack in enumerate(stacks):\n            if i == target_stack_index:\n                ph.print_info(\"Stack %d (%s): Reference image. Skipped.\" % (\n                    i + 1, stack.get_filename()))\n                continue\n            else:\n                ph.print_info(\"Stack %d (%s): Intensity Correction ... \" % (\n                    i + 1, stack.get_filename()), newline=False)\n            intensity_corrector.set_stack(stack)\n            intensity_corrector.set_reference(\n                stacks[target_stack_index].get_resampled_stack(\n                    resampling_grid=stack.sitk,\n                    interpolator=\"NearestNeighbor\",\n                ))\n            intensity_corrector.run_linear_intensity_correction()\n            stacks[i] = intensity_corrector.get_intensity_corrected_stack()\n            print(\"done (c1 = %g) \" %\n                  intensity_corrector.get_intensity_correction_coefficients())\n\n    # ---------------------------Create first volume---------------------------\n    time_tmp = ph.start_timing()\n\n    # Isotropic resampling to define HR target space\n    ph.print_title(\"Reconstruction Space Generation\")\n    HR_volume = reference.get_isotropically_resampled_stack(\n        resolution=args.isotropic_resolution)\n    ph.print_info(\n        \"Isotropic reconstruction space with %g mm resolution is created\" %\n        HR_volume.sitk.GetSpacing()[0])\n\n    if args.reference is None:\n        # Create joint image mask in target space\n        joint_image_mask_builder = imb.JointImageMaskBuilder(\n            stacks=stacks,\n            target=HR_volume,\n            dilation_radius=1,\n        )\n        joint_image_mask_builder.run()\n        HR_volume = joint_image_mask_builder.get_stack()\n        ph.print_info(\n            \"Isotropic reconstruction space is centered around \"\n            \"joint stack masks. \")\n\n        # Crop to space defined by mask (plus extra margin)\n        HR_volume = HR_volume.get_cropped_stack_based_on_mask(\n            boundary_i=args.extra_frame_target,\n            boundary_j=args.extra_frame_target,\n            boundary_k=args.extra_frame_target,\n            unit=\"mm\",\n        )\n\n        # Create first volume\n        # If outlier rejection is activated, eliminate obvious outliers early\n        # from stack and re-run SDA to get initial volume without them\n        ph.print_title(\"First Estimate of HR Volume\")\n        if args.outlier_rejection and threshold_v2v > -1:\n            ph.print_subtitle(\"SDA Approximation\")\n            SDA = sda.ScatteredDataApproximation(\n                stacks, HR_volume, sigma=args.sigma)\n            SDA.run()\n            HR_volume = SDA.get_reconstruction()\n\n            # Identify and reject outliers\n            ph.print_subtitle(\"Eliminate slice outliers (%s < %g)\" % (\n                rejection_measure, threshold_v2v))\n            outlier_rejector = outre.OutlierRejector(\n                stacks=stacks,\n                reference=HR_volume,\n                threshold=threshold_v2v,\n                measure=rejection_measure,\n                verbose=True,\n            )\n            outlier_rejector.run()\n            stacks = outlier_rejector.get_stacks()\n\n        ph.print_subtitle(\"SDA Approximation Image\")\n        SDA = sda.ScatteredDataApproximation(\n            stacks, HR_volume, sigma=args.sigma)\n        SDA.run()\n        HR_volume = SDA.get_reconstruction()\n\n        ph.print_subtitle(\"SDA Approximation Image Mask\")\n        SDA = sda.ScatteredDataApproximation(\n            stacks, HR_volume, sigma=args.sigma, sda_mask=True)\n        SDA.run()\n        # HR volume contains updated mask based on SDA\n        HR_volume = SDA.get_reconstruction()\n\n        HR_volume.set_filename(SDA.get_setting_specific_filename())\n\n    time_reconstruction = ph.stop_timing(time_tmp)\n\n    if args.verbose:\n        tmp = list(stacks)\n        tmp.insert(0, HR_volume)\n        sitkh.show_stacks(tmp, segmentation=HR_volume, viewer=args.viewer)\n\n    # -----------Two-step Slice-to-Volume Registration-Reconstruction----------\n    if args.two_step_cycles > 0:\n\n        # Slice-to-volume registration set-up\n        if args.metric == \"ANTSNeighborhoodCorrelation\":\n            metric_params = {\"radius\": args.metric_radius}\n        else:\n            metric_params = None\n        registration = regsitk.SimpleItkRegistration(\n            moving=HR_volume,\n            use_fixed_mask=True,\n            use_moving_mask=True,\n            interpolator=\"Linear\",\n            metric=args.metric,\n            metric_params=metric_params,\n            use_multiresolution_framework=args.multiresolution,\n            shrink_factors=args.shrink_factors,\n            smoothing_sigmas=args.smoothing_sigmas,\n            initializer_type=\"SelfGEOMETRY\",\n            optimizer=\"ConjugateGradientLineSearch\",\n            optimizer_params={\n                \"learningRate\": 1,\n                \"numberOfIterations\": 100,\n                \"lineSearchUpperLimit\": 2,\n            },\n            scales_estimator=\"Jacobian\",\n            use_verbose=debug,\n        )\n\n        # Volumetric reconstruction set-up\n        if args.sda:\n            recon_method = sda.ScatteredDataApproximation(\n                stacks,\n                HR_volume,\n                sigma=args.sigma,\n                use_masks=args.use_masks_srr,\n            )\n            alpha_range = [args.sigma, args.alpha]\n        else:\n            recon_method = tk.TikhonovSolver(\n                stacks=stacks,\n                reconstruction=HR_volume,\n                reg_type=\"TK1\",\n                minimizer=\"lsmr\",\n                alpha=args.alpha_first,\n                iter_max=np.min([args.iter_max_first, args.iter_max]),\n                verbose=True,\n                use_masks=args.use_masks_srr,\n            )\n            alpha_range = [args.alpha_first, args.alpha]\n\n        # Define the regularization parameters for the individual\n        # reconstruction steps in the two-step cycles\n        alphas = np.linspace(\n            alpha_range[0], alpha_range[1], args.two_step_cycles)\n\n        # Define outlier rejection threshold after each S2V-reg step\n        thresholds = np.linspace(\n            args.threshold_first, args.threshold, args.two_step_cycles)\n\n        two_step_s2v_reg_recon = \\\n            pipeline.TwoStepSliceToVolumeRegistrationReconstruction(\n                stacks=stacks,\n                reference=HR_volume,\n                registration_method=registration,\n                reconstruction_method=recon_method,\n                cycles=args.two_step_cycles,\n                alphas=alphas[0:args.two_step_cycles - 1],\n                outlier_rejection=args.outlier_rejection,\n                threshold_measure=rejection_measure,\n                thresholds=thresholds,\n                interleave=args.interleave,\n                viewer=args.viewer,\n                verbose=args.verbose,\n                use_hierarchical_registration=args.s2v_hierarchical,\n            )\n        two_step_s2v_reg_recon.run()\n        HR_volume_iterations = \\\n            two_step_s2v_reg_recon.get_iterative_reconstructions()\n        time_registration += \\\n            two_step_s2v_reg_recon.get_computational_time_registration()\n        time_reconstruction += \\\n            two_step_s2v_reg_recon.get_computational_time_reconstruction()\n        stacks = two_step_s2v_reg_recon.get_stacks()\n\n    # no two-step s2v-registration/reconstruction iterations\n    else:\n        HR_volume_iterations = []\n\n    # Write motion-correction results\n    ph.print_title(\"Write Motion Correction Results\")\n    if args.write_motion_correction:\n        dir_output_mc = os.path.join(\n            dir_output, args.subfolder_motion_correction)\n        ph.clear_directory(dir_output_mc)\n\n        for stack in stacks:\n            stack.write(\n                dir_output_mc,\n                write_stack=False,\n                write_mask=False,\n                write_slices=False,\n                write_transforms=True,\n                write_transforms_history=args.transforms_history,\n            )\n\n        if args.outlier_rejection:\n            deleted_slices_dic = {}\n            for i, stack in enumerate(stacks):\n                deleted_slices = stack.get_deleted_slice_numbers()\n                deleted_slices_dic[stack.get_filename()] = deleted_slices\n\n            # check whether any stack was removed entirely\n            stacks0 = data_preprocessing.get_preprocessed_stacks()\n            if len(stacks) != len(stacks0):\n                stacks_remain = [s.get_filename() for s in stacks]\n                for stack in stacks0:\n                    if stack.get_filename() in stacks_remain:\n                        continue\n\n                    # add info that all slices of this stack were rejected\n                    deleted_slices = [\n                        slice.get_slice_number()\n                        for slice in stack.get_slices()\n                    ]\n                    deleted_slices_dic[stack.get_filename()] = deleted_slices\n                    ph.print_info(\n                        \"All slices of stack '%s' were rejected entirely. \"\n                        \"Information added.\" % stack.get_filename())\n\n            ph.write_dictionary_to_json(\n                deleted_slices_dic,\n                os.path.join(\n                    dir_output,\n                    args.subfolder_motion_correction,\n                    \"rejected_slices.json\"\n                )\n            )\n\n    # ---------------------Final Volumetric Reconstruction---------------------\n    ph.print_title(\"Final Volumetric Reconstruction\")\n    if args.sda:\n        recon_method = sda.ScatteredDataApproximation(\n            stacks,\n            HR_volume,\n            sigma=args.alpha,\n            use_masks=args.use_masks_srr,\n        )\n    else:\n        if args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n            recon_method = pd.PrimalDualSolver(\n                stacks=stacks,\n                reconstruction=HR_volume,\n                reg_type=\"TV\" if args.reconstruction_type == \"TVL2\" else \"huber\",\n                iterations=args.iterations,\n                use_masks=args.use_masks_srr,\n            )\n        else:\n            recon_method = tk.TikhonovSolver(\n                stacks=stacks,\n                reconstruction=HR_volume,\n                reg_type=\"TK1\" if args.reconstruction_type == \"TK1L2\" else \"TK0\",\n                use_masks=args.use_masks_srr,\n            )\n        recon_method.set_alpha(args.alpha)\n        recon_method.set_iter_max(args.iter_max)\n        recon_method.set_verbose(True)\n    recon_method.run()\n    time_reconstruction += recon_method.get_computational_time()\n    HR_volume_final = recon_method.get_reconstruction()\n\n    ph.print_subtitle(\"Final SDA Approximation Image Mask\")\n    SDA = sda.ScatteredDataApproximation(\n        stacks, HR_volume_final, sigma=args.sigma, sda_mask=True)\n    SDA.run()\n    # HR volume contains updated mask based on SDA\n    HR_volume_final = SDA.get_reconstruction()\n    time_reconstruction += SDA.get_computational_time()\n\n    elapsed_time_total = ph.stop_timing(time_start)\n\n    # Write SRR result\n    filename = recon_method.get_setting_specific_filename()\n    HR_volume_final.set_filename(filename)\n    dw.DataWriter.write_image(\n        HR_volume_final.sitk,\n        args.output,\n        description=filename)\n    dw.DataWriter.write_mask(\n        HR_volume_final.sitk_mask,\n        ph.append_to_filename(args.output, \"_mask\"),\n        description=SDA.get_setting_specific_filename())\n\n    HR_volume_iterations.insert(0, HR_volume_final)\n    for stack in stacks:\n        HR_volume_iterations.append(stack)\n\n    if args.verbose:\n        sitkh.show_stacks(\n            HR_volume_iterations,\n            segmentation=HR_volume_final,\n            viewer=args.viewer,\n        )\n\n    # Summary\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Data Preprocessing: %s\" %\n          (exe_file_info, time_data_preprocessing))\n    print(\"%s | Computational Time for Registrations: %s\" %\n          (exe_file_info, time_registration))\n    print(\"%s | Computational Time for Reconstructions: %s\" %\n          (exe_file_info, time_reconstruction))\n    print(\"%s | Computational Time for Entire Reconstruction Pipeline: %s\" %\n          (exe_file_info, elapsed_time_total))\n\n    ph.print_line_separator()\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/reconstruct_volume_from_slices.py",
    "content": "##\n# \\file reconstruct_volume_from_slices.py\n# \\brief      Script to reconstruct an isotropic, high-resolution volume from\n#             multiple motion-corrected (or static) stacks of low-resolution 2D\n#             slices.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       March 2017\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.reconstruction.admm_solver as admm\nimport niftymic.utilities.intensity_correction as ic\nimport niftymic.reconstruction.primal_dual_solver as pd\nimport niftymic.reconstruction.tikhonov_solver as tk\nimport niftymic.reconstruction.scattered_data_approximation as sda\nimport niftymic.utilities.binary_mask_from_mask_srr_estimator as bm\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    # Read input\n    input_parser = InputArgparser(\n        description=\"Volumetric MRI reconstruction framework to reconstruct \"\n        \"an isotropic, high-resolution 3D volume from multiple \"\n        \"motion-corrected (or static) stacks of low-resolution slices.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks()\n    input_parser.add_dir_input_mc()\n    input_parser.add_output(required=True)\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_target_stack(default=None)\n    input_parser.add_extra_frame_target(default=10)\n    input_parser.add_isotropic_resolution(default=None)\n    input_parser.add_intensity_correction(default=1)\n    input_parser.add_reconstruction_space(default=None)\n    input_parser.add_minimizer(default=\"lsmr\")\n    input_parser.add_iter_max(default=10)\n    input_parser.add_reconstruction_type(default=\"TK1L2\")\n    input_parser.add_data_loss(default=\"linear\")\n    input_parser.add_data_loss_scale(default=1)\n    input_parser.add_alpha(\n        default=0.01  # TK1L2 @ isotropic_resolution = 0.8\n        # default=0.006  #TVL2, HuberL2 @ isotropic_resolution = 0.8\n    )\n    input_parser.add_rho(default=0.1)\n    input_parser.add_tv_solver(default=\"PD\")\n    input_parser.add_pd_alg_type(default=\"ALG2\")\n    input_parser.add_iterations(default=15)\n    input_parser.add_log_config(default=1)\n    input_parser.add_use_masks_srr(default=0)\n    input_parser.add_slice_thicknesses(default=None)\n    input_parser.add_verbose(default=0)\n    input_parser.add_viewer(default=\"itksnap\")\n    input_parser.add_argument(\n        \"--mask\", \"-mask\",\n        action='store_true',\n        help=\"If given, input images are interpreted as image masks. \"\n        \"Obtained volumetric reconstruction will be exported in uint8 format.\"\n    )\n    input_parser.add_argument(\n        \"--sda\", \"-sda\",\n        action='store_true',\n        help=\"If given, the volume is reconstructed using \"\n        \"Scattered Data Approximation (Vercauteren et al., 2006). \"\n        \"--alpha is considered the value for the standard deviation then. \"\n        \"Recommended value is, e.g., --alpha 0.8\"\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.reconstruction_type not in [\"TK1L2\", \"TVL2\", \"HuberL2\"]:\n        raise IOError(\"Reconstruction type unknown\")\n\n    if np.alltrue([not args.output.endswith(t) for t in ALLOWED_EXTENSIONS]):\n        raise ValueError(\n            \"output filename '%s' invalid; \"\n            \"allowed image extensions are: %s\" % (\n                args.output, \", \".join(ALLOWED_EXTENSIONS)))\n\n    dir_output = os.path.dirname(args.output)\n    ph.create_directory(dir_output)\n\n    debug = 0\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    if args.verbose:\n        show_niftis = []\n        # show_niftis = [f for f in args.filenames]\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n\n    if args.mask:\n        filenames_masks = args.filenames\n    else:\n        filenames_masks = args.filenames_masks\n\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        file_paths_masks=filenames_masks,\n        suffix_mask=args.suffix_mask,\n        dir_motion_correction=args.dir_input_mc,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    ph.print_info(\"%d input stacks read for further processing\" % len(stacks))\n\n    # Specify target stack for intensity correction and reconstruction space\n    if args.target_stack is None:\n        target_stack_index = 0\n    else:\n        # TODO: deal with case when target stack got rejected in previous step\n        filenames = [\"%s.nii.gz\" % s.get_filename() for s in stacks]\n        filename_target_stack = os.path.basename(args.target_stack)\n        try:\n            target_stack_index = filenames.index(filename_target_stack)\n        except ValueError as e:\n            raise ValueError(\n                \"--target-stack must correspond to an image as provided by \"\n                \"--filenames\")\n\n    # ---------------------------Intensity Correction--------------------------\n    if args.intensity_correction and not args.mask:\n        ph.print_title(\"Intensity Correction\")\n        intensity_corrector = ic.IntensityCorrection()\n        intensity_corrector.use_individual_slice_correction(False)\n        intensity_corrector.use_stack_mask(True)\n        intensity_corrector.use_reference_mask(True)\n        intensity_corrector.use_verbose(False)\n\n        for i, stack in enumerate(stacks):\n            if i == target_stack_index:\n                ph.print_info(\"Stack %d (%s): Reference image. Skipped.\" % (\n                    i + 1, stack.get_filename()))\n                continue\n            else:\n                ph.print_info(\"Stack %d (%s): Intensity Correction ... \" % (\n                    i + 1, stack.get_filename()), newline=False)\n            intensity_corrector.set_stack(stack)\n            intensity_corrector.set_reference(\n                stacks[target_stack_index].get_resampled_stack(\n                    resampling_grid=stack.sitk,\n                    interpolator=\"NearestNeighbor\",\n                ))\n            intensity_corrector.run_linear_intensity_correction()\n            stacks[i] = intensity_corrector.get_intensity_corrected_stack()\n            print(\"done (c1 = %g) \" %\n                  intensity_corrector.get_intensity_correction_coefficients())\n\n    # -------------------------Volumetric Reconstruction-----------------------\n    ph.print_title(\"Volumetric Reconstruction\")\n\n    # Reconstruction space defined by isotropically resampled,\n    # bounding box-cropped target stack\n    if args.reconstruction_space is None:\n        recon0 = stacks[target_stack_index].get_isotropically_resampled_stack(\n            resolution=args.isotropic_resolution,\n            extra_frame=args.extra_frame_target,\n        )\n        recon0 = recon0.get_cropped_stack_based_on_mask(\n            boundary_i=args.extra_frame_target,\n            boundary_j=args.extra_frame_target,\n            boundary_k=args.extra_frame_target,\n            unit=\"mm\",\n        )\n\n    # Reconstruction space was provided by user\n    else:\n        recon0 = st.Stack.from_filename(args.reconstruction_space,\n                                        extract_slices=False)\n\n        # Change resolution for isotropic resolution if provided by user\n        if args.isotropic_resolution is not None:\n            recon0 = recon0.get_isotropically_resampled_stack(\n                args.isotropic_resolution)\n\n        # Use image information of selected target stack as recon0 serves\n        # as initial value for reconstruction\n        recon0 = stacks[target_stack_index].get_resampled_stack(recon0.sitk)\n        recon0 = recon0.get_stack_multiplied_with_mask()\n\n    ph.print_info(\n        \"Reconstruction space defined with %s mm3 resolution\" %\n        \" x \".join([\"%.2f\" % s for s in recon0.sitk.GetSpacing()])\n    )\n\n    if debug:\n        # visualize (intensity corrected) data alongside recon0 init\n        show = [st.Stack.from_stack(s) for s in stacks]\n        show.insert(0, recon0)\n        sitkh.show_stacks(show)\n\n    if args.sda:\n        ph.print_title(\"Compute SDA reconstruction\")\n        SDA = sda.ScatteredDataApproximation(\n            stacks, recon0, sigma=args.alpha, sda_mask=args.mask)\n        SDA.run()\n        recon = SDA.get_reconstruction()\n        filename = SDA.get_setting_specific_filename()\n        if args.mask:\n            dw.DataWriter.write_mask(\n                recon.sitk_mask, args.output, description=filename)\n        else:\n            dw.DataWriter.write_image(\n                recon.sitk, args.output, description=filename)\n\n        if args.verbose:\n            show_niftis.insert(0, args.output)\n\n    else:\n        if args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n            ph.print_title(\n                \"Compute Initial value for %s\" % args.reconstruction_type)\n            SRR0 = sda.ScatteredDataApproximation(stacks, recon0, sigma=0.8)\n        else:\n            ph.print_title(\n                \"Compute %s reconstruction\" % args.reconstruction_type)\n            SRR0 = tk.TikhonovSolver(\n                stacks=stacks,\n                reconstruction=recon0,\n                alpha=args.alpha,\n                iter_max=args.iter_max,\n                reg_type=\"TK1\",\n                minimizer=args.minimizer,\n                data_loss=args.data_loss,\n                data_loss_scale=args.data_loss_scale,\n                use_masks=args.use_masks_srr,\n                # verbose=args.verbose,\n            )\n        SRR0.run()\n\n        recon = SRR0.get_reconstruction()\n        filename = SRR0.get_setting_specific_filename()\n\n        if args.verbose and args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n            output = ph.append_to_filename(args.output, \"_init\")\n\n            if args.mask:\n                mask_estimator = bm.BinaryMaskFromMaskSRREstimator(recon.sitk)\n                mask_estimator.run()\n                mask_sitk = mask_estimator.get_mask_sitk()\n                dw.DataWriter.write_mask(\n                    mask_sitk, output, description=filename)\n            else:\n                dw.DataWriter.write_image(\n                    recon.sitk, output, description=filename)\n\n            show_niftis.insert(0, output)\n\n        if args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n            ph.print_title(\"Compute %s reconstruction\" %\n                           args.reconstruction_type)\n            if args.tv_solver == \"ADMM\":\n                SRR = admm.ADMMSolver(\n                    stacks=stacks,\n                    reconstruction=st.Stack.from_stack(\n                        SRR0.get_reconstruction()),\n                    minimizer=args.minimizer,\n                    alpha=args.alpha,\n                    iter_max=args.iter_max,\n                    rho=args.rho,\n                    data_loss=args.data_loss,\n                    iterations=args.iterations,\n                    use_masks=args.use_masks_srr,\n                    verbose=args.verbose,\n                )\n\n            else:\n                SRR = pd.PrimalDualSolver(\n                    stacks=stacks,\n                    reconstruction=st.Stack.from_stack(\n                        SRR0.get_reconstruction()),\n                    minimizer=args.minimizer,\n                    alpha=args.alpha,\n                    iter_max=args.iter_max,\n                    iterations=args.iterations,\n                    alg_type=args.pd_alg_type,\n                    reg_type=\"TV\" if args.reconstruction_type == \"TVL2\" else \"huber\",\n                    data_loss=args.data_loss,\n                    use_masks=args.use_masks_srr,\n                    verbose=args.verbose,\n                )\n            SRR.run()\n            recon = SRR.get_reconstruction()\n            filename = SRR.get_setting_specific_filename()\n\n        if args.mask:\n            mask_estimator = bm.BinaryMaskFromMaskSRREstimator(recon.sitk)\n            mask_estimator.run()\n            mask_sitk = mask_estimator.get_mask_sitk()\n            dw.DataWriter.write_mask(\n                mask_sitk, args.output, description=filename)\n\n        else:\n            dw.DataWriter.write_image(\n                recon.sitk, args.output, description=filename)\n\n        if args.verbose:\n            show_niftis.insert(0, args.output)\n\n    if args.verbose:\n        ph.show_niftis(show_niftis, viewer=args.viewer)\n\n    ph.print_line_separator()\n\n    elapsed_time = ph.stop_timing(time_start)\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Volumetric Reconstruction: %s\" % (\n        exe_file_info, elapsed_time))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/register_image.py",
    "content": "##\n# \\file register_image.py\n# \\brief      Script to register the obtained reconstruction to a template\n#             space.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\nimport re\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.registration.niftyreg as niftyreg\nimport niftymic.registration.transform_initializer as tinit\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import REGEX_FILENAMES, DIR_TMP\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Register an obtained reconstruction (moving) \"\n        \"to a template image/space (fixed) using rigid registration. \"\n        \"The resulting registration can optionally be applied to previously \"\n        \"obtained motion correction slice transforms so that a volumetric \"\n        \"reconstruction is possible in the (standard anatomical) space \"\n        \"defined by the fixed.\",\n    )\n    input_parser.add_fixed(required=True)\n    input_parser.add_moving(required=True)\n    input_parser.add_output(\n        help=\"Path to registration transform (.txt)\",\n        required=True)\n    input_parser.add_fixed_mask(required=False)\n    input_parser.add_moving_mask(required=False)\n    input_parser.add_option(\n        option_string=\"--initial-transform\",\n        type=str,\n        help=\"Path to initial transform. \"\n        \"If not provided, registration will be initialized based on \"\n        \"rigid alignment of eigenbasis of the fixed/moving image masks \"\n        \"using principal component analysis\",\n        default=None)\n    input_parser.add_v2v_method(\n        option_string=\"--method\",\n        help=\"Registration method used for the registration.\",\n        default=\"RegAladin\",\n    )\n    input_parser.add_argument(\n        \"--init-pca\", \"-init-pca\",\n        action='store_true',\n        help=\"If given, PCA-based initializations will be refined using \"\n        \"RegAladin registrations.\"\n    )\n    input_parser.add_dir_input_mc()\n    input_parser.add_verbose(default=0)\n    input_parser.add_log_config(default=1)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    if not args.output.endswith(\".txt\"):\n        raise IOError(\n            \"output filename '%s' invalid; \"\n            \"allowed transformation extensions are: '.txt'\" % (\n                args.output))\n\n    if args.initial_transform is not None and args.init_pca:\n        raise IOError(\n            \"Both --initial-transform and --init-pca cannot be activated. \"\n            \"Choose one.\")\n\n    dir_output = os.path.dirname(args.output)\n    ph.create_directory(dir_output)\n\n    debug = False\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n    fixed = st.Stack.from_filename(\n        file_path=args.fixed,\n        file_path_mask=args.fixed_mask,\n        extract_slices=False)\n    moving = st.Stack.from_filename(\n        file_path=args.moving,\n        file_path_mask=args.moving_mask,\n        extract_slices=False)\n\n    path_to_tmp_output = os.path.join(\n        DIR_TMP,\n        ph.append_to_filename(os.path.basename(args.moving), \"_warped\"))\n\n    # ---------------------------- Initialization ----------------------------\n    if args.initial_transform is None and args.init_pca:\n        ph.print_title(\"Estimate (initial) transformation using PCA\")\n\n        if args.moving_mask is None or args.fixed_mask is None:\n            ph.print_warning(\"Fixed and moving masks are strongly recommended\")\n        transform_initializer = tinit.TransformInitializer(\n            fixed=fixed,\n            moving=moving,\n            similarity_measure=\"NMI\",\n            refine_pca_initializations=True,\n        )\n        transform_initializer.run()\n        transform_init_sitk = transform_initializer.get_transform_sitk()\n\n    elif args.initial_transform is not None:\n        transform_init_sitk = sitkh.read_transform_sitk(args.initial_transform)\n\n    else:\n        transform_init_sitk = None\n\n    if transform_init_sitk is not None:\n        sitk.WriteTransform(transform_init_sitk, args.output)\n\n    # -------------------Register Reconstruction to Template-------------------\n    ph.print_title(\"Registration\")\n\n    # If --init-pca given, RegAladin run already performed\n    if args.method == \"RegAladin\" and not args.init_pca:\n\n        path_to_transform_regaladin = os.path.join(\n            DIR_TMP, \"transform_regaladin.txt\")\n\n        # Convert SimpleITK to RegAladin transform\n        if transform_init_sitk is not None:\n            cmd = \"simplereg_transform -sitk2nreg %s %s\" % (\n                args.output, path_to_transform_regaladin)\n            ph.execute_command(cmd, verbose=False)\n\n        # Run NiftyReg\n        cmd_args = [\"reg_aladin\"]\n        cmd_args.append(\"-ref '%s'\" % args.fixed)\n        cmd_args.append(\"-flo '%s'\" % args.moving)\n        cmd_args.append(\"-res '%s'\" % path_to_tmp_output)\n        if transform_init_sitk is not None:\n            cmd_args.append(\"-inaff '%s'\" % path_to_transform_regaladin)\n        cmd_args.append(\"-aff '%s'\" % path_to_transform_regaladin)\n        cmd_args.append(\"-rigOnly\")\n        cmd_args.append(\"-ln 2\")  # seems to perform better for spina bifida\n        cmd_args.append(\"-voff\")\n        if args.fixed_mask is not None:\n            cmd_args.append(\"-rmask '%s'\" % args.fixed_mask)\n\n        # To avoid error \"0 correspondences between blocks were found\" that can\n        # occur for some cases. Also, disable moving mask, as this would be ignored\n        # anyway\n        cmd_args.append(\"-noSym\")\n        # if args.moving_mask is not None:\n        #     cmd_args.append(\"-fmask '%s'\" % args.moving_mask)\n\n        ph.print_info(\"Run Registration (RegAladin) ... \", newline=False)\n        ph.execute_command(\" \".join(cmd_args), verbose=debug)\n        print(\"done\")\n\n        # Convert RegAladin to SimpleITK transform\n        cmd = \"simplereg_transform -nreg2sitk '%s' '%s'\" % (\n            path_to_transform_regaladin, args.output)\n        ph.execute_command(cmd, verbose=False)\n\n    elif args.method == \"FLIRT\":\n        path_to_transform_flirt = os.path.join(DIR_TMP, \"transform_flirt.txt\")\n\n        # Convert SimpleITK into FLIRT transform\n        if transform_init_sitk is not None:\n            cmd = \"simplereg_transform -sitk2flirt '%s' '%s' '%s' '%s'\" % (\n                args.output, args.fixed, args.moving, path_to_transform_flirt)\n            ph.execute_command(cmd, verbose=False)\n\n        # Define search angle ranges for FLIRT in all three dimensions\n        # search_angles = [\"-searchr%s -%d %d\" % (x, 180, 180)\n        #                  for x in [\"x\", \"y\", \"z\"]]\n\n        cmd_args = [\"flirt\"]\n        cmd_args.append(\"-in '%s'\" % args.moving)\n        cmd_args.append(\"-ref '%s'\" % args.fixed)\n        if transform_init_sitk is not None:\n            cmd_args.append(\"-init '%s'\" % path_to_transform_flirt)\n        cmd_args.append(\"-omat '%s'\" % path_to_transform_flirt)\n        cmd_args.append(\"-out '%s'\" % path_to_tmp_output)\n        cmd_args.append(\"-dof 6\")\n        # cmd_args.append((\" \").join(search_angles))\n        if args.moving_mask is not None:\n            cmd_args.append(\"-inweight '%s'\" % args.moving_mask)\n        if args.fixed_mask is not None:\n            cmd_args.append(\"-refweight '%s'\" % args.fixed_mask)\n        ph.print_info(\"Run Registration (FLIRT) ... \", newline=False)\n        ph.execute_command(\" \".join(cmd_args), verbose=debug)\n        print(\"done\")\n\n        # Convert FLIRT to SimpleITK transform\n        cmd = \"simplereg_transform -flirt2sitk '%s' '%s' '%s' '%s'\" % (\n            path_to_transform_flirt, args.fixed, args.moving, args.output)\n        ph.execute_command(cmd, verbose=False)\n    ph.print_info(\"Registration transformation written to '%s'\" % args.output)\n\n    if args.dir_input_mc is not None:\n        ph.print_title(\"Update Motion-Correction Transformations\")\n        transform_sitk = sitkh.read_transform_sitk(\n            args.output, inverse=1)\n\n        if args.dir_input_mc.endswith(\"/\"):\n            subdir_mc = args.dir_input_mc.split(\"/\")[-2]\n        else:\n            subdir_mc = args.dir_input_mc.split(\"/\")[-1]\n        dir_output_mc = os.path.join(dir_output, subdir_mc)\n\n        ph.create_directory(dir_output_mc, delete_files=True)\n        pattern = REGEX_FILENAMES + \"[.]tfm\"\n        p = re.compile(pattern)\n        trafos = [t for t in os.listdir(args.dir_input_mc) if p.match(t)]\n        for t in trafos:\n            path_to_input_transform = os.path.join(args.dir_input_mc, t)\n            path_to_output_transform = os.path.join(dir_output_mc, t)\n            t_sitk = sitkh.read_transform_sitk(path_to_input_transform)\n            t_sitk = sitkh.get_composite_sitk_affine_transform(\n                transform_sitk, t_sitk)\n            sitk.WriteTransform(t_sitk, path_to_output_transform)\n        ph.print_info(\"%d transformations written to '%s'\" % (\n            len(trafos), dir_output_mc))\n\n        # Copy rejected_slices.json file\n        path_to_rejected_slices = os.path.join(\n            args.dir_input_mc, \"rejected_slices.json\")\n        if ph.file_exists(path_to_rejected_slices):\n            ph.copy_file(path_to_rejected_slices, dir_output_mc)\n\n    if args.verbose:\n        cmd_args = [\"simplereg_resample\"]\n        cmd_args.append(\"-f '%s'\" % args.fixed)\n        cmd_args.append(\"-m '%s'\" % args.moving)\n        cmd_args.append(\"-t '%s'\" % args.output)\n        cmd_args.append(\"-o '%s'\" % path_to_tmp_output)\n        ph.execute_command(\" \".join(cmd_args))\n\n        ph.show_niftis([args.fixed, path_to_tmp_output])\n\n    elapsed_time_total = ph.stop_timing(time_start)\n\n    # Summary\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time: %s\" % (exe_file_info, elapsed_time_total))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/rsfmri_estimate_motion.py",
    "content": "##\n# \\file rsfmri_estimate_motion.py\n# \\brief      Estimate motion in resting-state fMRI volumes\n#\n# \\author     Michael Ebner (michael.ebner@kcl.ac.uk)\n# \\date       July 2019\n#\n\nimport os\nimport numpy as np\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.registration.flirt as regflirt\nimport niftymic.registration.niftyreg as regniftyreg\nimport niftymic.registration.simple_itk_registration as regsitk\nimport niftymic.reconstruction.tikhonov_solver as tk\nimport niftymic.reconstruction.primal_dual_solver as pd\nimport niftymic.reconstruction.scattered_data_approximation as sda\nimport niftymic.utilities.data_preprocessing as dp\nimport niftymic.utilities.joint_image_mask_builder as imb\nimport niftymic.utilities.segmentation_propagation as segprop\nimport niftymic.utilities.volumetric_reconstruction_pipeline as pipeline\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import V2V_METHOD_OPTIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    # Read input\n    input_parser = InputArgparser(\n        description=\"Estimate motion in resting-state fMRI volumes\",\n    )\n    input_parser.add_filename(required=True)\n    input_parser.add_filename_mask()\n    input_parser.add_dir_output(required=True)\n    input_parser.add_reference(required=False)\n    input_parser.add_reference_mask(required=False)\n    input_parser.add_alpha(default=0.03)\n    input_parser.add_alpha_first(default=0.05)\n    input_parser.add_data_loss(default=\"linear\")\n    input_parser.add_dilation_radius(default=3)\n    input_parser.add_extra_frame_target(default=5)\n    input_parser.add_isotropic_resolution(default=1)\n    input_parser.add_iter_max(default=10)\n    input_parser.add_iter_max_first(default=5)\n    input_parser.add_iterations(default=10)\n    input_parser.add_log_config(default=1)\n    input_parser.add_minimizer(default=\"lsmr\")\n    input_parser.add_argument(\n        \"--prototyping\", \"-prototyping\",\n        action='store_true',\n        help=\"If given, only a small subset of all time points is selected \"\n        \"for quicker test computations.\"\n    )\n    input_parser.add_reconstruction_type(default=\"TK1L2\")\n    input_parser.add_rho(default=0.5)\n    input_parser.add_sigma(default=0.8)\n    input_parser.add_stack_recon_range(default=15)\n    input_parser.add_target_stack_index(default=0)\n    input_parser.add_two_step_cycles(default=3)\n    input_parser.add_use_masks_srr(default=0)\n    input_parser.add_verbose(default=0)\n    input_parser.add_v2v_method(default=\"RegAladin\")\n    input_parser.add_outlier_rejection(default=1)\n    input_parser.add_threshold_first(default=0.5)\n    input_parser.add_threshold(default=0.8)\n    input_parser.add_argument(\n        \"--sda\", \"-sda\",\n        action='store_true',\n        help=\"If given, the volumetric reconstructions are performed using \"\n        \"Scattered Data Approximation (Vercauteren et al., 2006). \"\n        \"'alpha' is considered the final 'sigma' for the \"\n        \"iterative adjustment. \"\n        \"Recommended value is, e.g., --alpha 0.8\"\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.v2v_method not in V2V_METHOD_OPTIONS:\n        raise ValueError(\"v2v-method must be in {%s}\" % (\n            \", \".join(V2V_METHOD_OPTIONS)))\n\n    if args.alpha_first < args.alpha and not args.sda:\n        raise ValueError(\"It must hold alpha-first >= alpha\")\n\n    if args.threshold_first > args.threshold:\n        raise ValueError(\"It must hold threshold-first <= threshold\")\n\n    # Write script execution call\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n    data_reader = dr.MultiComponentImageReader(\n        args.filename, args.filename_mask)\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    # ------------------------------DELETE LATER------------------------------\n    if args.prototyping:\n        stacks = stacks[0:2]\n    # ------------------------------DELETE LATER------------------------------\n\n    # ---------------------------Data Preprocessing---------------------------\n    ph.print_title(\"Data Preprocessing\")\n\n    segmentation_propagator = segprop.SegmentationPropagation(\n        # registration_method=regniftyreg.RegAladin(use_verbose=args.verbose),\n        # registration_method=regsitk.SimpleItkRegistration(use_verbose=args.verbose),\n        dilation_radius=args.dilation_radius,\n        dilation_kernel=\"Ball\",\n    )\n\n    data_preprocessing = dp.DataPreprocessing(\n        stacks=stacks,\n        segmentation_propagator=segmentation_propagator,\n\n        # Not ideal: Entire FOV more desirable but registration is worse if not\n        # cropped\n        use_cropping_to_mask=args.use_masks_srr,\n\n        target_stack_index=args.target_stack_index,\n        boundary_i=0,\n        boundary_j=0,\n        boundary_k=0,\n        unit=\"mm\",\n    )\n    data_preprocessing.run()\n    time_data_preprocessing = data_preprocessing.get_computational_time()\n\n    # Get preprocessed stacks\n    stacks = data_preprocessing.get_preprocessed_stacks()\n\n    # Define volume-to-volume registration method\n    if args.v2v_method == \"FLIRT\":\n        registration_v2v = regflirt.FLIRT(\n            registration_type=\"Rigid\",\n            use_fixed_mask=True,\n            use_moving_mask=True,\n            use_verbose=False,\n        )\n    else:\n        registration_v2v = regniftyreg.RegAladin(\n            registration_type=\"Rigid\",\n            use_fixed_mask=True,\n            use_moving_mask=True,\n            # options=\"-ln 2\",\n            use_verbose=False,\n        )\n\n    # Define slice-to-volume registration method\n    registration_s2v = regsitk.SimpleItkRegistration(\n        use_fixed_mask=True,\n        use_moving_mask=True,\n        use_verbose=False,\n        interpolator=\"Linear\",\n        metric=\"Correlation\",\n        # metric=\"MattesMutualInformation\",  # Might cause error messages\n        # like \"Too many samples map outside moving image buffer.\"\n        # use_multiresolution_framework=True,\n        shrink_factors=[2, 1],\n        smoothing_sigmas=[1, 0],\n        initializer_type=\"SelfGEOMETRY\",\n        optimizer=\"ConjugateGradientLineSearch\",\n        optimizer_params={\n            \"learningRate\": 1,\n            \"numberOfIterations\": 100,\n            \"lineSearchUpperLimit\": 2,\n        },\n        # optimizer=\"RegularStepGradientDescent\",\n        # optimizer_params={\n        #     \"minStep\": 1e-6,\n        #     \"numberOfIterations\": 200,\n        #     \"gradientMagnitudeTolerance\": 1e-6,\n        #     \"learningRate\": 1,\n        # },\n        scales_estimator=\"Jacobian\",\n    )\n\n    if args.reference is None:\n        time_ref_estimate_start = ph.start_timing()\n\n        # Stacks used for outlier-robust SRR algorithm\n        i_min = args.target_stack_index\n        i_max = np.min([args.target_stack_index + args.stack_recon_range,\n                        len(stacks)])\n        stacks_srr = [st.Stack.from_stack(s) for s in stacks[i_min: i_max]]\n\n        # ----------------------Volume-to-Volume Registration------------------\n        if args.two_step_cycles > 0:\n\n            v2vreg = pipeline.VolumeToVolumeRegistration(\n                stacks=stacks_srr,\n                reference=stacks_srr[0],\n                registration_method=registration_v2v,\n                verbose=args.verbose,\n            )\n            v2vreg.run()\n            stacks_srr = v2vreg.get_stacks()\n            time_registration = v2vreg.get_computational_time()\n\n        else:\n            time_registration = ph.get_zero_time()\n\n        # ---------------------------Create first volume-----------------------\n        time_tmp = ph.start_timing()\n        # Isotropic resampling to define HR target space\n        ph.print_title(\"Isotropic Resampling\")\n        reference = stacks_srr[0].get_isotropically_resampled_stack(\n            resolution=args.isotropic_resolution,\n            extra_frame=args.extra_frame_target)\n\n        # Scattered Data Approximation to get first estimate of HR volume\n        ph.print_title(\"Scattered Data Approximation\")\n        SDA = sda.ScatteredDataApproximation(\n            stacks=stacks_srr,\n            HR_volume=reference,\n            sigma=args.sigma,\n            use_masks=args.use_masks_srr,\n        )\n        SDA.run()\n        reference = SDA.get_reconstruction()\n\n        joint_image_mask_builder = imb.JointImageMaskBuilder(\n            stacks=stacks_srr,\n            target=reference,\n            dilation_radius=1,\n        )\n        joint_image_mask_builder.run()\n        reference = joint_image_mask_builder.get_stack()\n        reference.set_filename(SDA.get_setting_specific_filename())\n\n        # Crop to space defined by mask (plus extra margin)\n        reference = reference.get_cropped_stack_based_on_mask(\n            boundary_i=args.extra_frame_target,\n            boundary_j=args.extra_frame_target,\n            boundary_k=args.extra_frame_target,\n            unit=\"mm\",\n        )\n\n        time_reconstruction = ph.stop_timing(time_tmp)\n\n        # ----------------Two-step Slice-to-Volume Registration SRR------------\n        if args.two_step_cycles > 0:\n\n            # Volumetric reconstruction set-up\n            if args.sda:\n                recon_method = sda.ScatteredDataApproximation(\n                    stacks=stacks_srr,\n                    HR_volume=reference,\n                    sigma=args.sigma,\n                    use_masks=args.use_masks_srr,\n                )\n                alpha_range = [args.sigma, args.alpha]\n            else:\n                recon_method = tk.TikhonovSolver(\n                    stacks=stacks_srr,\n                    reconstruction=reference,\n                    reg_type=\"TK1\",\n                    minimizer=\"lsmr\",\n                    alpha=args.alpha_first,\n                    iter_max=np.min([args.iter_max_first, args.iter_max]),\n                    verbose=True,\n                    use_masks=args.use_masks_srr,\n                )\n                alpha_range = [args.alpha_first, args.alpha]\n\n            # Define the regularization parameters for the individual\n            # reconstruction steps in the two-step cycles\n            alphas = np.linspace(\n                alpha_range[0], alpha_range[1], args.two_step_cycles)\n\n            # Define outlier rejection threshold after each S2V-reg step\n            thresholds = np.linspace(\n                args.threshold_first, args.threshold, args.two_step_cycles)\n\n            two_step_s2v_reg_recon = \\\n                pipeline.TwoStepSliceToVolumeRegistrationReconstruction(\n                    stacks=stacks_srr,\n                    reference=reference,\n                    registration_method=registration_s2v,\n                    reconstruction_method=recon_method,\n                    cycles=args.two_step_cycles,\n                    alphas=alphas[0:args.two_step_cycles - 1],\n                    verbose=args.verbose,\n                    outlier_rejection=args.outlier_rejection,\n                    thresholds=thresholds,\n                )\n            two_step_s2v_reg_recon.run()\n            reference_iterations = \\\n                two_step_s2v_reg_recon.get_iterative_reconstructions()\n            time_registration += \\\n                two_step_s2v_reg_recon.get_computational_time_registration()\n            time_reconstruction += \\\n                two_step_s2v_reg_recon.get_computational_time_reconstruction()\n\n            if args.verbose:\n                sitkh.show_stacks(reference_iterations, segmentation=reference)\n\n            # # Write to output\n            # HR_volume_tmp.write(args.dir_output)\n\n        ph.print_title(\"Final Reference Reconstruction\")\n        if args.sda:\n            recon_method = sda.ScatteredDataApproximation(\n                stacks_srr,\n                reference,\n                sigma=args.alpha,\n                use_masks=args.use_masks_srr,\n            )\n        else:\n            if args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n                recon_method = pd.PrimalDualSolver(\n                    stacks=stacks_srr,\n                    reconstruction=reference,\n                    reg_type=\"TV\" if args.reconstruction_type == \"TVL2\" else \"huber\",\n                    iterations=args.iterations,\n                    use_masks=args.use_masks_srr,\n                )\n            else:\n                recon_method = tk.TikhonovSolver(\n                    stacks=stacks_srr,\n                    reconstruction=reference,\n                    reg_type=\"TK1\" if args.reconstruction_type == \"TK1L2\" else \"TK0\",\n                    use_masks=args.use_masks_srr,\n                )\n            recon_method.set_alpha(args.alpha)\n            recon_method.set_iter_max(args.iter_max)\n            recon_method.set_verbose(True)\n        recon_method.run()\n        reference = recon_method.get_reconstruction()\n        time_reconstruction += recon_method.get_computational_time()\n\n        ph.print_subtitle(\"Final SDA Approximation Image Mask\")\n        SDA = sda.ScatteredDataApproximation(\n            stacks_srr, reference, sigma=args.sigma, sda_mask=True)\n        SDA.run()\n        # Reference contains updated mask based on SDA\n        reference = SDA.get_reconstruction()\n        time_reconstruction += SDA.get_computational_time()\n\n        description = recon_method.get_setting_specific_filename()\n        reference.set_filename(description)\n        name = \"SDA\" if args.sda else \"SRR\"\n        path_to_reference = os.path.join(\n            args.dir_output, \"%s_reference.nii.gz\" % name)\n        dw.DataWriter.write_image(\n            image_sitk=reference.sitk,\n            path_to_file=path_to_reference,\n            description=description)\n        dw.DataWriter.write_mask(\n            reference.sitk_mask,\n            ph.append_to_filename(path_to_reference, \"_mask\"),\n            description=SDA.get_setting_specific_filename())\n        time_ref_estimate = ph.stop_timing(time_ref_estimate_start)\n\n    else:\n        reference = st.Stack.from_filename(args.reference, args.reference_mask)\n        time_ref_estimate = ph.get_zero_time()\n        ph.print_info(\"Reference image for V2V and S2V registrations provided\")\n\n    # --------------------Volume-to-Volume Registrations-----------------\n    v2vreg = pipeline.VolumeToVolumeRegistration(\n        stacks=stacks,\n        reference=reference,\n        registration_method=registration_v2v,\n        verbose=False,\n    )\n    v2vreg.run()\n    time_v2v_reg = v2vreg.get_computational_time()\n\n    # --------------------Slice-to-Volume Registrations-----------------\n    s2vreg = pipeline.SliceToVolumeRegistration(\n        stacks=stacks,\n        reference=reference,\n        registration_method=registration_s2v,\n        verbose=False,\n    )\n    s2vreg.run()\n    time_s2v_reg = s2vreg.get_computational_time()\n\n    # ------------------Write Slice Motion Correction Results------------------\n    ph.print_title(\"Write Slice Motion Correction Results\")\n    dir_output_mc = os.path.join(\n        args.dir_output,\n        \"motion_correction\",\n    )\n    ph.clear_directory(dir_output_mc)\n    for stack in stacks:\n        stack.write(\n            dir_output_mc,\n            write_stack=False,\n            write_mask=False,\n            write_slices=False,\n            write_transforms=True,\n        )\n\n    elapsed_time_total = ph.stop_timing(time_start)\n\n    # Summary\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Data Preprocessing: %s\" % (\n        exe_file_info, time_data_preprocessing))\n    print(\"%s | Computational Time for Reference (Estimate): %s\" % (\n        exe_file_info, time_ref_estimate))\n    print(\"%s | Computational Time for V2V-Registration: %s\" % (\n        exe_file_info, time_v2v_reg))\n    print(\"%s | Computational Time for S2V-Registration: %s\" % (\n        exe_file_info, time_s2v_reg))\n    print(\"%s | Computational Time for Pipeline: %s\" % (\n        exe_file_info, elapsed_time_total))\n\n    ph.print_line_separator()\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/rsfmri_reconstruct_volume_from_slices.py",
    "content": "##\n# \\file reconstruct_volume_from_slices_rsfmri.py\n# \\brief      Script to reconstruct an isotropic, high-resolution volume from\n#             multiple motion-corrected (or static) stacks of low-resolution 2D\n#             slices.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2019\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.reconstruction.admm_solver as admm\nimport niftymic.utilities.intensity_correction as ic\nimport niftymic.reconstruction.primal_dual_solver as pd\nimport niftymic.reconstruction.tikhonov_solver as tk\nimport niftymic.reconstruction.scattered_data_approximation as sda\nimport niftymic.utilities.binary_mask_from_mask_srr_estimator as bm\nfrom niftymic.utilities.input_arparser import InputArgparser\nimport niftymic.utilities.volumetric_reconstruction_pipeline as pipeline\n\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    # Read input\n    input_parser = InputArgparser(\n        description=\"Volumetric MRI reconstruction framework to reconstruct \"\n        \"resting-state fMRI based on motion-corrected slice transformations \"\n        \"obtained using reconstruct_volume_rsfmri.\",\n    )\n    input_parser.add_filename(required=True)\n    input_parser.add_filename_mask()\n    input_parser.add_output(required=True)\n    input_parser.add_dir_input_mc()\n    input_parser.add_argument(\n        \"--volume\", \"-vol\",\n        action='store_true',\n        help=\"If given, reconstructions for each time point are performed \"\n        \"based on volumetric stack position update only. \"\n        \"Otherwise, reconstructions are based after performed motion updates \"\n        \"for each individual slice for each time point.\"\n    )\n    input_parser.add_reconstruction_space(default=None)\n    input_parser.add_alpha(default=0.05)\n    input_parser.add_reconstruction_type(default=\"TK1L2\")\n    input_parser.add_data_loss(default=\"linear\")\n    input_parser.add_minimizer(default=\"lsmr\")\n    input_parser.add_iter_max(default=20)\n    input_parser.add_iterations(default=10)\n    input_parser.add_argument(\n        \"--prototyping\", \"-prototyping\",\n        action='store_true',\n        help=\"If given, only a small subset of all time points is selected \"\n        \"for quicker test computations.\"\n    )\n    input_parser.add_option(\n        option_string=\"--reconstruction-spacing\",\n        type=float,\n        nargs=\"+\",\n        help=\"Specify spacing of reconstruction space in case a change is desired\",\n        default=None)\n    input_parser.add_argument(\n        \"--sda\", \"-sda\",\n        action='store_true',\n        help=\"If given, the volumetric reconstructions are performed using \"\n        \"Scattered Data Approximation (Vercauteren et al., 2006). \"\n        \"'alpha' is considered the final 'sigma' for the \"\n        \"iterative adjustment. \"\n        \"Recommended value is, e.g., --alpha 0.8\"\n    )\n    input_parser.add_option(\n        \"--beta\",\n        help=\"Regularization parameter beta to solve the Super-Resolution \"\n        \"Reconstruction problem with temporal regularization: \"\n        \"SRR = argmin_{x^t} [\"\n        \"sum_t sum_k ||y_k^t - A_k^t x^t||^2 \"\n        \"+ alpha * R(x) \"\n        \"+ beta * sum_t ||x^{t+1} - x^t||^2\"\n        \"].\",\n        type=float,\n        default=0,\n    )\n    input_parser.add_use_masks_srr(default=1)\n    input_parser.add_log_config(default=1)\n    input_parser.add_verbose(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    # Write script execution call\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n    data_reader = dr.MultiComponentImageReader(\n        path_to_image=args.filename,\n        path_to_image_mask=args.filename_mask,\n        dir_motion_correction=args.dir_input_mc,\n        volume_motion_only=args.volume,\n    )\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    # Define reconstruction space for rsfmri\n    if args.reconstruction_space is None:\n        path_to_recon_space = args.filename\n    else:\n        path_to_recon_space = args.reconstruction_space\n    image_sitk = sitk.ReadImage(path_to_recon_space)\n\n    # Standard 3D image\n    # Multi-component 3D image\n    if len(image_sitk.GetSize()) == 4:\n        # Extract first component (3D image) of the 4D image\n        shape = list(image_sitk.GetSize())\n        shape[-1] = 0\n        index = [0] * 4\n        recon_space_sitk = sitk.Extract(image_sitk, shape, index)\n    elif len(image_sitk.GetSize()) == 3:\n        recon_space_sitk = image_sitk\n    else:\n        raise ValueError(\"Provide either a multi-component or a standard \"\n                         \"3D image to define the reconstruction space\")\n\n    reconstruction_space = st.Stack.from_sitk_image(\n        recon_space_sitk * 0,\n        slice_thickness=recon_space_sitk.GetSpacing()[-1],\n        filename=ph.strip_filename_extension(\n            os.path.basename(path_to_recon_space))[0],\n    )\n\n    if args.reconstruction_spacing is not None:\n        reconstruction_space = reconstruction_space.get_resampled_stack(\n            spacing=args.reconstruction_spacing)\n\n    # ------------------------------DELETE LATER------------------------------\n    if args.prototyping:\n        stacks = stacks[0:2]\n    # ------------------------------DELETE LATER------------------------------\n\n    # ----Define solver for rsfMRI reconstructions of individual timepoints----\n    if args.sda:\n        recon_method = sda.ScatteredDataApproximation(\n            stacks,\n            reconstruction_space,\n            sigma=args.alpha,\n            use_masks=args.use_masks_srr,\n        )\n    else:\n        if args.beta < 0:\n            if args.reconstruction_type in [\"TVL2\", \"HuberL2\"]:\n                recon_method = pd.PrimalDualSolver(\n                    stacks=stacks,\n                    reconstruction=reconstruction_space,\n                    reg_type=\"TV\" if args.reconstruction_type == \"TVL2\" else \"huber\",\n                    iterations=args.iterations,\n                    use_masks=args.use_masks_srr,\n                )\n            else:\n                recon_method = tk.TikhonovSolver(\n                    stacks=stacks,\n                    reconstruction=reconstruction_space,\n                    reg_type=\"TK1\" if args.reconstruction_type == \"TK1L2\" else \"TK0\",\n                    use_masks=args.use_masks_srr,\n                )\n            recon_method.set_alpha(args.alpha)\n            recon_method.set_iter_max(args.iter_max)\n            recon_method.set_verbose(True)\n\n            # ------Update individual timepoints based on updated slice positio\n            multi_component_reconstruction = pipeline.MultiComponentReconstruction(\n                stacks=stacks,\n                reconstruction_method=recon_method,\n                suffix=\"_recon_v2v\")\n            multi_component_reconstruction.run()\n            time_reconstruction = \\\n                multi_component_reconstruction.get_computational_time()\n            stacks_recon = multi_component_reconstruction.get_reconstructions()\n            description = multi_component_reconstruction.\\\n                get_reconstruction_method().get_setting_specific_filename()\n\n        else:\n            if not args.reconstruction_type in [\"TK0L2\", \"TK1L2\"]:\n                raise ValueError(\n                    \"Temporal Tikhonov regularization, i.e. beta>0, \"\n                    \"only possible for TK0L2 and TK1L2 currently\")\n            recon_method = tk.TemporalTikhonovSolver(\n                stacks=stacks,\n                reconstruction=reconstruction_space,\n                reg_type=\"TK1\" if args.reconstruction_type == \"TK1L2\" else \"TK0\",\n                use_masks=args.use_masks_srr,\n                beta=args.beta,\n                alpha=args.alpha,\n                iter_max=args.iter_max,\n                verbose=True,\n            )\n            recon_method.run()\n            time_reconstruction = recon_method.get_computational_time()\n            stacks_recon = recon_method.get_reconstructions()\n            description = recon_method.get_setting_specific_filename()\n\n    # --------------------------------Write Data------------------------------\n    ph.print_title(\"Write Data\")\n    data_writer = dw.MultiComponentImageWriter(\n        stacks_recon, args.output, description=description)\n    data_writer.write_data()\n\n    if args.verbose:\n        ph.show_nifti(args.output)\n\n    elapsed_time = ph.stop_timing(time_start)\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Volumetric Reconstruction: %s\" %\n          (exe_file_info, elapsed_time))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/run_reconstruction_parameter_study.py",
    "content": "##\n# \\file run_reconstruction_parameter_study.py\n# \\brief      Script to study reconstruction parameters and their impact on the\n#             volumetric reconstruction quality.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       September 2017\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport nsol.deconvolution_solver_parameter_study_interface as deconv_interface\n\nimport niftymic.base.data_reader as dr\nimport niftymic.base.stack as st\nimport niftymic.reconstruction.tikhonov_solver as tk\nimport niftymic.reconstruction.scattered_data_approximation as sda\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options for numpy\n    np.set_printoptions(precision=3)\n\n    # Read input\n    input_parser = InputArgparser(\n        description=\"Script to study reconstruction parameters and their \"\n        \"impact on the volumetric reconstruction quality. \"\n        \"This script can only be used to sweep through one single parameter, \"\n        \"e.g. the regularization parameter 'alpha'. \"\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks()\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_dir_input_mc()\n    input_parser.add_dir_output(required=True)\n    input_parser.add_reconstruction_space()\n    input_parser.add_reference(\n        help=\"Path to reference NIfTI image file. If given the volumetric \"\n        \"reconstructed is performed in this physical space. \"\n        \"Either a reconstruction space or a reference must be provided\",\n        required=False)\n    input_parser.add_reference_mask(default=None)\n    input_parser.add_study_name()\n    input_parser.add_reconstruction_type(default=\"TK1L2\")\n    input_parser.add_measures(\n        default=[\"PSNR\", \"MAE\", \"RMSE\", \"SSIM\", \"NCC\", \"NMI\"])\n    input_parser.add_tv_solver(default=\"PD\")\n    input_parser.add_iterations(default=50)\n    input_parser.add_rho(default=0.1)\n    input_parser.add_iter_max(default=10)\n    input_parser.add_minimizer(default=\"lsmr\")\n    input_parser.add_log_config(default=1)\n    input_parser.add_use_masks_srr(default=0)\n    input_parser.add_verbose(default=1)\n    input_parser.add_slice_thicknesses(default=None)\n    input_parser.add_argument(\n        \"--append\", \"-append\",\n        action='store_true',\n        help=\"If given, results are appended to previously executed parameter \"\n        \"study with identical parameters and study name store in the output \"\n        \"directory.\"\n    )\n\n    # Range for parameter sweeps\n    input_parser.add_alphas(default=list(np.linspace(0.01, 0.5, 5)))\n    input_parser.add_data_losses(\n        default=[\"linear\"]\n        # default=[\"linear\", \"arctan\"]\n    )\n    input_parser.add_data_loss_scales(\n        default=[1]\n        # default=[0.1, 0.5, 1.5]\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.reference is None and args.reconstruction_space is None:\n        raise IOError(\"Either reference (--reference) or reconstruction space \"\n                      \"(--reconstruction-space) must be provided.\")\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        file_paths_masks=args.filenames_masks,\n        suffix_mask=args.suffix_mask,\n        dir_motion_correction=args.dir_input_mc,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n    ph.print_info(\"%d input stacks read for further processing\" % len(stacks))\n\n    ph.print_title(\"Compute Initial Value\")\n    if args.reference is not None:\n        reference = st.Stack.from_filename(\n            file_path=args.reference,\n            file_path_mask=args.reference_mask,\n            extract_slices=False)\n        x_ref = sitk.GetArrayFromImage(reference.sitk).flatten()\n        x_ref_mask = sitk.GetArrayFromImage(reference.sitk_mask).flatten()\n    else:\n        reference = st.Stack.from_filename(\n            file_path=args.reconstruction_space,\n            extract_slices=False)\n        x_ref = None\n        x_ref_mask = None\n\n    SRR0 = sda.ScatteredDataApproximation(stacks, reference, sigma=0.8)\n    SRR0.run()\n    reconstruction_space = SRR0.get_reconstruction()\n\n    if args.use_masks_srr:\n        # Add mask based on SDA\n        SRR0 = sda.ScatteredDataApproximation(\n            stacks, reconstruction_space, sigma=1, sda_mask=True)\n        SRR0.run()\n        reconstruction_space = SRR0.get_reconstruction()\n\n        reconstruction_space = \\\n            reconstruction_space.get_stack_multiplied_with_mask()\n\n    # ----------------------------Set Up Parameters----------------------------\n    parameters = {}\n    parameters[\"alpha\"] = args.alphas\n    if len(args.data_losses) > 1:\n        parameters[\"data_loss\"] = args.data_losses\n    if len(args.data_loss_scales) > 1:\n        parameters[\"data_loss_scale\"] = args.data_loss_scales\n\n    # --------------------------Set Up Parameter Study-------------------------\n    ph.print_title(\"Run Parameter Study\")\n    if args.study_name is None:\n        name = args.reconstruction_type\n    else:\n        name = args.study_name\n\n    reconstruction_info = {\n        \"shape\": reconstruction_space.sitk.GetSize()[::-1],\n        \"origin\": reconstruction_space.sitk.GetOrigin(),\n        \"spacing\": reconstruction_space.sitk.GetSpacing(),\n        \"direction\": reconstruction_space.sitk.GetDirection(),\n    }\n\n    # Create Tikhonov solver from which all information can be extracted\n    # (also for other reconstruction types)\n    tmp = tk.TikhonovSolver(\n        stacks=stacks,\n        reconstruction=reconstruction_space,\n        alpha=args.alphas[0],\n        iter_max=args.iter_max,\n        data_loss=args.data_losses[0],\n        data_loss_scale=args.data_loss_scales[0],\n        reg_type=\"TK1\",\n        minimizer=args.minimizer,\n        verbose=args.verbose,\n        use_masks=args.use_masks_srr,\n    )\n    solver = tmp.get_solver()\n\n    parameter_study_interface = \\\n        deconv_interface.DeconvolutionParameterStudyInterface(\n            A=solver.get_A(),\n            A_adj=solver.get_A_adj(),\n            D=solver.get_B(),\n            D_adj=solver.get_B_adj(),\n            b=solver.get_b(),\n            x0=solver.get_x0(),\n            alpha=solver.get_alpha(),\n            x_scale=solver.get_x_scale(),\n            data_loss=solver.get_data_loss(),\n            data_loss_scale=solver.get_data_loss_scale(),\n            iter_max=solver.get_iter_max(),\n            minimizer=solver.get_minimizer(),\n            iterations=args.iterations,\n            measures=args.measures,\n            dimension=3,\n            L2=16. / reconstruction_space.sitk.GetSpacing()[0]**2,\n            reconstruction_type=args.reconstruction_type,\n            rho=args.rho,\n            dir_output=args.dir_output,\n            parameters=parameters,\n            name=name,\n            reconstruction_info=reconstruction_info,\n            x_ref=x_ref,\n            x_ref_mask=x_ref_mask,\n            tv_solver=args.tv_solver,\n            verbose=args.verbose,\n            append=args.append,\n        )\n    parameter_study_interface.set_up_parameter_study()\n    parameter_study = parameter_study_interface.get_parameter_study()\n\n    # Run parameter study\n    parameter_study.run()\n\n    print(\"\\nComputational time for Deconvolution Parameter Study %s: %s\" %\n          (name, parameter_study.get_computational_time()))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/run_reconstruction_pipeline.py",
    "content": "##\n# \\file run_reconstruction_pipeline.py\n# \\brief      Script to execute entire reconstruction pipeline\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\nimport os\nimport re\nimport numpy as np\n\nimport pysitk.python_helper as ph\n\nimport niftymic.validation.simulate_stacks_from_reconstruction as \\\n    simulate_stacks_from_reconstruction\nimport niftymic.validation.evaluate_simulated_stack_similarity as \\\n    evaluate_simulated_stack_similarity\nimport niftymic.validation.show_evaluated_simulated_stack_similarity as \\\n    show_evaluated_simulated_stack_similarity\nimport niftymic.application.show_slice_coverage as show_slice_coverage\nimport niftymic.validation.export_side_by_side_simulated_vs_original_slice_comparison as \\\n    export_side_by_side_simulated_vs_original_slice_comparison\nimport niftymic.utilities.target_stack_estimator as ts_estimator\nfrom niftymic.utilities.input_arparser import InputArgparser\nimport niftymic.utilities.template_stack_estimator as tse\n\nfrom niftymic.definitions import DIR_TEMPLATES\n\n\ndef main():\n\n    time_start_total = ph.start_timing()\n\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Run reconstruction pipeline including \"\n        \"(i) bias field correction, \"\n        \"(ii) volumetric reconstruction in subject space, \"\n        \"(iii) volumetric reconstruction in template space, \"\n        \"and (iv) some diagnostics to assess the obtained reconstruction.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks(required=True)\n    input_parser.add_target_stack(required=False)\n    input_parser.add_suffix_mask(default=\"\")\n    input_parser.add_dir_output(required=True)\n    input_parser.add_alpha(default=0.01)\n    input_parser.add_verbose(default=0)\n    input_parser.add_prefix_output(default=\"srr_\")\n    input_parser.add_search_angle(default=180)\n    input_parser.add_multiresolution(default=0)\n    input_parser.add_log_config(default=1)\n    input_parser.add_isotropic_resolution()\n    input_parser.add_reference()\n    input_parser.add_reference_mask()\n    input_parser.add_bias_field_correction(default=1)\n    input_parser.add_intensity_correction(default=1)\n    input_parser.add_iter_max(default=10)\n    input_parser.add_two_step_cycles(default=3)\n    input_parser.add_slice_thicknesses(default=None)\n    input_parser.add_option(\n        option_string=\"--template\",\n        type=str,\n        required=False,\n        help=\"Template image used for template space alignment and to define \"\n        \"the reconstruction space. \"\n        \"If not given, it is automatically estimated using the fetal brain \"\n        \"atlas\",\n    )\n    input_parser.add_option(\n        option_string=\"--template-mask\",\n        type=str,\n        required=False,\n        help=\"Template image mask. \"\n        \"Must be given in case template is specified.\",\n    )\n    input_parser.add_option(\n        option_string=\"--run-bias-field-correction\",\n        type=int,\n        help=\"Turn on/off bias field correction. \"\n        \"If off, it is assumed that this step was already performed \"\n        \"if --bias-field-correction is active.\",\n        default=1)\n    input_parser.add_option(\n        option_string=\"--run-recon-subject-space\",\n        type=int,\n        help=\"Turn on/off reconstruction in subject space. \"\n        \"If off, it is assumed that this step was already performed.\",\n        default=1)\n    input_parser.add_option(\n        option_string=\"--run-recon-template-space\",\n        type=int,\n        help=\"Turn on/off reconstruction in template space. \"\n        \"If off, it is assumed that this step was already performed.\",\n        default=1)\n    input_parser.add_option(\n        option_string=\"--run-diagnostics\",\n        type=int,\n        help=\"Turn on/off diagnostics of the obtained volumetric \"\n        \"reconstruction. \",\n        default=0)\n    input_parser.add_option(\n        option_string=\"--initial-transform\",\n        type=str,\n        help=\"Set initial transform to be used for register_image.\",\n        default=None)\n    input_parser.add_outlier_rejection(default=1)\n    input_parser.add_threshold_first(default=0.5)\n    input_parser.add_threshold(default=0.8)\n    input_parser.add_argument(\n        \"--sda\", \"-sda\",\n        action='store_true',\n        help=\"If given, the volume is reconstructed using \"\n        \"Scattered Data Approximation (Vercauteren et al., 2006). \"\n        \"--alpha is considered the value for the standard deviation then. \"\n        \"Recommended value is, e.g., --alpha 0.8\"\n    )\n    input_parser.add_argument(\n        \"--v2v-robust\", \"-v2v-robust\",\n        action='store_true',\n        help=\"If given, a more robust volume-to-volume registration step is \"\n        \"performed, i.e. four rigid registrations are performed using four \"\n        \"rigid transform initializations based on \"\n        \"principal component alignment of associated masks.\"\n    )\n    input_parser.add_interleave(default=3)\n    input_parser.add_argument(\n        \"--s2v-hierarchical\", \"-s2v-hierarchical\",\n        action='store_true',\n        help=\"If given, a hierarchical approach for the first slice-to-volume \"\n        \"registration cycle is used, i.e. sub-packages defined by the \"\n        \"specified interleave (--interleave) are registered until each \"\n        \"slice is registered independently.\"\n    )\n    input_parser.add_option(\n        option_string=\"--automatic-target-stack\",\n        type=int,\n        help=\"If true, and no specific target stack is provided, \"\n        \"a target stack is automatically estimated. \"\n        \"A motion score similar to the one presented in Kainz et al. (2015). \"\n        \"is used to estimate the least motion-affected stack as initial \"\n        \"reference/target stack.\",\n        default=1,\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.template is not None:\n        if args.template_mask is None:\n            raise ValueError(\n                \"If template image is given, also its mask needs to be \"\n                \"provided\")\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    dir_output_preprocessing = os.path.join(\n        args.dir_output, \"preprocessing_n4itk\")\n    dir_output_recon_subject_space = os.path.join(\n        args.dir_output, \"recon_subject_space\")\n    dir_output_recon_template_space = os.path.join(\n        args.dir_output, \"recon_template_space\")\n    dir_output_diagnostics = os.path.join(\n        args.dir_output, \"diagnostics\")\n\n    srr_subject = os.path.join(\n        dir_output_recon_subject_space,\n        \"%ssubject.nii.gz\" % args.prefix_output)\n    srr_subject_mask = ph.append_to_filename(srr_subject, \"_mask\")\n    srr_template = os.path.join(\n        dir_output_recon_template_space,\n        \"%stemplate.nii.gz\" % args.prefix_output)\n    srr_template_mask = ph.append_to_filename(srr_template, \"_mask\")\n    trafo_template = os.path.join(\n        dir_output_recon_template_space,\n        \"%stemplate_transform_sitk.txt\" % args.prefix_output)\n    srr_slice_coverage = os.path.join(\n        dir_output_diagnostics,\n        \"%stemplate_slicecoverage.nii.gz\" % args.prefix_output)\n\n    if args.bias_field_correction and args.run_bias_field_correction:\n        time_start = ph.start_timing()\n        for i, f in enumerate(args.filenames):\n            output = os.path.join(\n                dir_output_preprocessing, os.path.basename(f))\n            cmd_args = []\n            cmd_args.append(\"--filename '%s'\" % f)\n            cmd_args.append(\"--filename-mask '%s'\" % args.filenames_masks[i])\n            cmd_args.append(\"--output '%s'\" % output)\n            # cmd_args.append(\"--verbose %d\" % args.verbose)\n            cmd_args.append(\"--log-config %d\" % args.log_config)\n            cmd = \"niftymic_correct_bias_field %s\" % (\" \").join(cmd_args)\n            exit_code = ph.execute_command(cmd)\n            if exit_code != 0:\n                raise RuntimeError(\"Bias field correction failed\")\n        elapsed_time_bias = ph.stop_timing(time_start)\n        filenames = [os.path.join(dir_output_preprocessing, os.path.basename(f))\n                     for f in args.filenames]\n    elif args.bias_field_correction and not args.run_bias_field_correction:\n        elapsed_time_bias = ph.get_zero_time()\n        filenames = [os.path.join(dir_output_preprocessing, os.path.basename(f))\n                     for f in args.filenames]\n    else:\n        elapsed_time_bias = ph.get_zero_time()\n        filenames = args.filenames\n\n    # Specify target stack for intensity correction and reconstruction space\n    elapsed_time_target_stack = ph.get_zero_time()\n    if args.target_stack is None:\n\n        if args.automatic_target_stack:\n            ph.print_info(\n                \"Searching for suitable target stack ... \", newline=False)\n            target_stack_estimator = \\\n                ts_estimator.TargetStackEstimator.from_motion_score(\n                    file_paths=filenames,\n                    file_paths_masks=args.filenames_masks,\n                )\n            print(\"done\")\n            elapsed_time_target_stack = \\\n                target_stack_estimator.get_computational_time()\n            ph.print_info(\n                \"Computational time for target stack selection: %s\" % (elapsed_time_target_stack))\n            target_stack_index = \\\n                target_stack_estimator.get_target_stack_index()\n            target_stack = filenames[target_stack_index]\n            ph.print_info(\"Chosen target stack: %s\" % target_stack)\n\n        else:\n            target_stack = filenames[0]\n\n    else:\n        try:\n            target_stack_index = args.filenames.index(args.target_stack)\n        except ValueError as e:\n            raise ValueError(\n                \"--target-stack must correspond to an image as provided by \"\n                \"--filenames\")\n        target_stack = filenames[target_stack_index]\n\n    # Add single quotes around individual filenames to account for whitespaces\n    filenames = [\"'\" + f + \"'\" for f in filenames]\n    filenames_masks = [\"'\" + f + \"'\" for f in args.filenames_masks]\n\n    if args.run_recon_subject_space:\n        time_start = ph.start_timing()\n\n        cmd_args = [\"niftymic_reconstruct_volume\"]\n        cmd_args.append(\"--filenames %s\" % (\" \").join(filenames))\n        cmd_args.append(\"--filenames-masks %s\" % (\" \").join(filenames_masks))\n        cmd_args.append(\"--multiresolution %d\" % args.multiresolution)\n        cmd_args.append(\"--target-stack '%s'\" % target_stack)\n        cmd_args.append(\"--output '%s'\" % srr_subject)\n        cmd_args.append(\"--suffix-mask '%s'\" % args.suffix_mask)\n        cmd_args.append(\"--intensity-correction %d\" %\n                        args.intensity_correction)\n        cmd_args.append(\"--alpha %s\" % args.alpha)\n        cmd_args.append(\"--iter-max %d\" % args.iter_max)\n        cmd_args.append(\"--two-step-cycles %d\" % args.two_step_cycles)\n        cmd_args.append(\"--outlier-rejection %d\" % args.outlier_rejection)\n        cmd_args.append(\"--threshold-first %f\" % args.threshold_first)\n        cmd_args.append(\"--threshold %f\" % args.threshold)\n        cmd_args.append(\"--verbose %d\" % args.verbose)\n        cmd_args.append(\"--log-config %d\" % args.log_config)\n\n        if args.isotropic_resolution is not None:\n            isotropic_resolution = args.isotropic_resolution\n        else:\n            # Gholipour et al. (2015) atlas grid spacing (0.7999989986419678)\n            # for comparability\n            isotropic_resolution = 0.8\n        cmd_args.append(\"--isotropic-resolution %f\" % isotropic_resolution)\n\n        if args.slice_thicknesses is not None:\n            cmd_args.append(\"--slice-thicknesses %s\" %\n                            \" \".join(map(str, args.slice_thicknesses)))\n\n        if args.reference is not None:\n            cmd_args.append(\"--reference '%s'\" % args.reference)\n        if args.reference_mask is not None:\n            cmd_args.append(\"--reference-mask '%s'\" % args.reference_mask)\n        if args.sda:\n            cmd_args.append(\"--sda\")\n        if args.v2v_robust:\n            cmd_args.append(\"--v2v-robust\")\n        if args.s2v_hierarchical:\n            cmd_args.append(\"--s2v-hierarchical\")\n\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"Reconstruction in subject space failed\")\n        elapsed_time_recon_subject_space = ph.stop_timing(time_start)\n\n        # Compute SRR mask in subject space\n        # (Approximated using SDA within reconstruct_volume)\n        if 0:\n            dir_motion_correction = os.path.join(\n                dir_output_recon_subject_space, \"motion_correction\")\n            cmd_args = [\"niftymic_reconstruct_volume_from_slices\"]\n            cmd_args.append(\"--filenames %s\" % \" \".join(filenames_masks))\n            cmd_args.append(\"--dir-input-mc '%s'\" % dir_motion_correction)\n            cmd_args.append(\"--output '%s'\" % srr_subject_mask)\n            cmd_args.append(\"--reconstruction-space '%s'\" % srr_subject)\n            cmd_args.append(\"--suffix-mask '%s'\" % args.suffix_mask)\n            cmd_args.append(\"--mask\")\n            cmd_args.append(\"--log-config %d\" % args.log_config)\n            if args.slice_thicknesses is not None:\n                cmd_args.append(\"--slice-thicknesses %s\" %\n                                \" \".join(map(str, args.slice_thicknesses)))\n            if args.sda:\n                cmd_args.append(\"--sda\")\n                cmd_args.append(\"--alpha 1\")\n            else:\n                cmd_args.append(\"--alpha 0.1\")\n                cmd_args.append(\"--iter-max 5\")\n            cmd = (\" \").join(cmd_args)\n            ph.execute_command(cmd)\n\n    else:\n        elapsed_time_recon_subject_space = ph.get_zero_time()\n\n    if args.run_recon_template_space:\n        time_start = ph.start_timing()\n\n        if args.template is not None:\n            template = args.template\n            template_mask = args.template_mask\n        else:\n            template_stack_estimator = \\\n                tse.TemplateStackEstimator.from_mask(srr_subject_mask)\n            gestational_age = template_stack_estimator.get_estimated_gw()\n            ph.print_info(\"Estimated gestational age: %d\" % gestational_age)\n\n            template = os.path.join(\n                DIR_TEMPLATES, \"STA%d.nii.gz\" % gestational_age)\n            template_mask = os.path.join(\n                DIR_TEMPLATES, \"STA%d_mask.nii.gz\" % gestational_age)\n\n        # Register SRR to template space\n        cmd_args = [\"niftymic_register_image\"]\n        cmd_args.append(\"--fixed '%s'\" % template)\n        cmd_args.append(\"--moving '%s'\" % srr_subject)\n        cmd_args.append(\"--fixed-mask '%s'\" % template_mask)\n        cmd_args.append(\"--moving-mask '%s'\" % srr_subject_mask)\n        cmd_args.append(\"--dir-input-mc '%s'\" % os.path.join(\n            dir_output_recon_subject_space, \"motion_correction\"))\n        cmd_args.append(\"--output '%s'\" % trafo_template)\n        cmd_args.append(\"--verbose %s\" % args.verbose)\n        cmd_args.append(\"--log-config %d\" % args.log_config)\n        if args.initial_transform is None:\n            cmd_args.append(\"--init-pca\")\n        else:\n            cmd_args.append(\n                \"--initial-transform '%s'\" % args.initial_transform)\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"Registration to template space failed\")\n        elapsed_time_register_image = ph.stop_timing(time_start)\n        time_start = ph.start_timing()\n\n        # Compute SRR in template space\n        dir_input_mc = os.path.join(\n            dir_output_recon_template_space, \"motion_correction\")\n        cmd_args = [\"niftymic_reconstruct_volume_from_slices\"]\n        cmd_args.append(\"--filenames %s\" % (\" \").join(filenames))\n        cmd_args.append(\"--filenames-masks %s\" % (\" \").join(filenames_masks))\n        cmd_args.append(\"--dir-input-mc '%s'\" % dir_input_mc)\n        cmd_args.append(\"--output '%s'\" % srr_template)\n        cmd_args.append(\"--reconstruction-space '%s'\" % template)\n        cmd_args.append(\"--target-stack '%s'\" % target_stack)\n        cmd_args.append(\"--iter-max %d\" % args.iter_max)\n        cmd_args.append(\"--alpha %s\" % args.alpha)\n        cmd_args.append(\"--suffix-mask '%s'\" % args.suffix_mask)\n        cmd_args.append(\"--log-config %d\" % args.log_config)\n        if args.isotropic_resolution is not None:\n            cmd_args.append(\"--isotropic-resolution %f\" %\n                            args.isotropic_resolution)\n        if args.slice_thicknesses is not None:\n            cmd_args.append(\"--slice-thicknesses %s\" %\n                            \" \".join(map(str, args.slice_thicknesses)))\n        if args.sda:\n            cmd_args.append(\"--sda\")\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"Reconstruction in template space failed\")\n        elapsed_time_recon_template_space = ph.stop_timing(time_start)\n\n        # Compute mask in template space\n        if 1:\n            time_start = ph.start_timing()\n            dir_motion_correction = os.path.join(\n                dir_output_recon_template_space, \"motion_correction\")\n            cmd_args = [\"niftymic_reconstruct_volume_from_slices\"]\n            cmd_args.append(\"--filenames %s\" % \" \".join(filenames_masks))\n            cmd_args.append(\"--dir-input-mc '%s'\" % dir_motion_correction)\n            cmd_args.append(\"--output '%s'\" % srr_template_mask)\n            cmd_args.append(\"--reconstruction-space '%s'\" % srr_template)\n            cmd_args.append(\"--suffix-mask '%s'\" % args.suffix_mask)\n            cmd_args.append(\"--log-config %d\" % args.log_config)\n            cmd_args.append(\"--mask\")\n            if args.isotropic_resolution is not None:\n                cmd_args.append(\"--isotropic-resolution %f\" %\n                                args.isotropic_resolution)\n            if args.slice_thicknesses is not None:\n                cmd_args.append(\"--slice-thicknesses %s\" %\n                                \" \".join(map(str, args.slice_thicknesses)))\n\n            # SRR approach\n            # cmd_args.append(\"--alpha 0.1\")\n            # cmd_args.append(\"--iter-max 5\")\n\n            # SDA much faster than SRR and visually barely different for mask\n            cmd_args.append(\"--sda\")\n            cmd_args.append(\"--alpha 1\")\n\n            cmd = (\" \").join(cmd_args)\n            ph.execute_command(cmd)\n            elapsed_time_recon_template_space_mask = ph.stop_timing(\n                time_start)\n        \n        if args.verbose:\n            ph.show_nifti(srr_template, segmentation=srr_template_mask)\n\n        # Copy SRR to output directory\n        if 0:\n            output = \"%sSRR_Stacks%d.nii.gz\" % (\n                args.prefix_output, len(args.filenames))\n            path_to_output = os.path.join(args.dir_output, output)\n            cmd = \"cp -p '%s' '%s'\" % (srr_template, path_to_output)\n            exit_code = ph.execute_command(cmd)\n            if exit_code != 0:\n                raise RuntimeError(\"Copy of SRR to output directory failed\")\n\n        # Multiply template mask with reconstruction\n        if 0:\n            cmd_args = [\"niftymic_multiply\"]\n            fnames = [\n                srr_template,\n                srr_template_mask,\n            ]\n            output_masked = \"Masked_%s\" % output\n            path_to_output_masked = os.path.join(\n                args.dir_output, output_masked)\n            cmd_args.append(\"--filenames %s\" % \" \".join(fnames))\n            cmd_args.append(\"--output '%s'\" % path_to_output_masked)\n            cmd = (\" \").join(cmd_args)\n            exit_code = ph.execute_command(cmd)\n            if exit_code != 0:\n                raise RuntimeError(\"SRR brain masking failed\")\n\n    else:\n        elapsed_time_register_image = ph.get_zero_time()\n        elapsed_time_recon_template_space = ph.get_zero_time()\n        elapsed_time_recon_template_space_mask = ph.get_zero_time()\n\n    if args.run_diagnostics:\n        time_start = ph.start_timing()\n\n        dir_input_mc = os.path.join(\n            dir_output_recon_template_space, \"motion_correction\")\n        dir_output_orig_vs_proj = os.path.join(\n            dir_output_diagnostics, \"original_vs_projected\")\n        dir_output_selfsimilarity = os.path.join(\n            dir_output_diagnostics, \"selfsimilarity\")\n        dir_output_orig_vs_proj_pdf = os.path.join(\n            dir_output_orig_vs_proj, \"pdf\")\n\n        # Show slice coverage over reconstruction space\n        exe = os.path.abspath(show_slice_coverage.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filenames %s\" % (\" \").join(filenames))\n        cmd_args.append(\"--dir-input-mc '%s'\" % dir_input_mc)\n        cmd_args.append(\"--reconstruction-space '%s'\" % srr_template)\n        cmd_args.append(\"--output '%s'\" % srr_slice_coverage)\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"Slice coverage visualization failed\")\n\n        # Get simulated/projected slices\n        exe = os.path.abspath(simulate_stacks_from_reconstruction.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filenames %s\" % (\" \").join(filenames))\n        if args.filenames_masks is not None:\n            cmd_args.append(\"--filenames-masks %s\" %\n                            (\" \").join(filenames_masks))\n        cmd_args.append(\"--dir-input-mc '%s'\" % dir_input_mc)\n        cmd_args.append(\"--dir-output '%s'\" % dir_output_orig_vs_proj)\n        cmd_args.append(\"--reconstruction '%s'\" % srr_template)\n        cmd_args.append(\"--copy-data 1\")\n        if args.slice_thicknesses is not None:\n            cmd_args.append(\"--slice-thicknesses %s\" %\n                            \" \".join(map(str, args.slice_thicknesses)))\n        # cmd_args.append(\"--verbose %s\" % args.verbose)\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"SRR slice projections failed\")\n\n        filenames_simulated = [\n            \"'%s\" % os.path.join(dir_output_orig_vs_proj, os.path.basename(f))\n            for f in filenames]\n\n        # Evaluate slice similarities to ground truth\n        exe = os.path.abspath(evaluate_simulated_stack_similarity.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filenames %s\" % (\" \").join(filenames_simulated))\n        if args.filenames_masks is not None:\n            cmd_args.append(\"--filenames-masks %s\" %\n                            (\" \").join(filenames_masks))\n        cmd_args.append(\"--measures NCC SSIM\")\n        cmd_args.append(\"--dir-output '%s'\" % dir_output_selfsimilarity)\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            raise RuntimeError(\"Evaluation of stack similarities failed\")\n\n        # Generate figures showing the quantitative comparison\n        exe = os.path.abspath(\n            show_evaluated_simulated_stack_similarity.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--dir-input '%s'\" % dir_output_selfsimilarity)\n        cmd_args.append(\"--dir-output '%s'\" % dir_output_selfsimilarity)\n        cmd = (\" \").join(cmd_args)\n        exit_code = ph.execute_command(cmd)\n        if exit_code != 0:\n            ph.print_warning(\"Visualization of stack similarities failed\")\n\n        # Generate pdfs showing all the side-by-side comparisons\n        if 0:\n            exe = os.path.abspath(\n                export_side_by_side_simulated_vs_original_slice_comparison.__file__)\n            cmd_args = [\"python %s\" % exe]\n            cmd_args.append(\"--filenames %s\" % (\" \").join(filenames_simulated))\n            cmd_args.append(\"--dir-output '%s'\" % dir_output_orig_vs_proj_pdf)\n            cmd = \"python %s %s\" % (exe, (\" \").join(cmd_args))\n            cmd = (\" \").join(cmd_args)\n            exit_code = ph.execute_command(cmd)\n            if exit_code != 0:\n                raise RuntimeError(\"Generation of PDF overview failed\")\n        elapsed_time_diagnostics = ph.stop_timing(time_start)\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time for Bias Field Corrections: %s\" % (\n          exe_file_info, elapsed_time_bias))\n    print(\"%s | Computational Time for Automatic Target Stack Selection: %s\" %\n          (exe_file_info, elapsed_time_target_stack))\n    print(\"%s | Computational Time for Subject Space Reconstruction: %s\" % (\n          exe_file_info, elapsed_time_recon_subject_space))\n    print(\"%s | Computational Time for Template Space Alignment: %s\" % (\n          exe_file_info, elapsed_time_register_image))\n    print(\"%s | Computational Time for Template Space Reconstruction: %s\" % (\n          exe_file_info, elapsed_time_recon_template_space))\n    print(\"%s | Computational Time for Template Space Reconstruction (Mask): %s\" % (\n          exe_file_info, elapsed_time_recon_template_space_mask))\n    if args.run_diagnostics:\n        print(\"%s | Computational Time for Diagnostics: %s\" % (\n              exe_file_info, elapsed_time_diagnostics))\n    print(\"%s | Computational Time for Pipeline: %s\" % (\n          exe_file_info, ph.stop_timing(time_start_total)))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/segment_fetal_brains.py",
    "content": "##\n# \\file correct_bias_field.py\n# \\brief      Script to apply automated fetal brain mask segmentation using monaifbs/fetal_brain_seg.py\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       October 2017\n#\n\n# Import libraries\nimport numpy as np\nimport os\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_writer as dw\nimport niftymic.utilities.n4_bias_field_correction as n4itk\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\n\n\ndef main():\n    time_start = ph.start_timing()\n\n    np.set_printoptions(precision=3)\n\n    input_parser = InputArgparser(\n        description=\"Perform automatic brain masking using \"\n        \"fetal_brain_seg, part of the MONAIfbs package \"\n        \"(https://github.com/gift-surg/MONAIfbs). \",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks(required=False)\n    input_parser.add_dir_output(required=False)\n    input_parser.add_verbose(default=0)\n    input_parser.add_log_config(default=0)\n    input_parser.add_option(\n        option_string=\"--neuroimage-legacy-seg\",\n        type=int,\n        required=False,\n        default=0,\n        help=\"If set to 1, use the legacy method for fetal brain segmentation \"\n             \"i.e. the two-step approach proposed in Ebner, Wang et al \"\n             \"NeuroImage (2020)\"\n    )\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.neuroimage_legacy_seg:\n        try:\n            DIR_FETAL_BRAIN_SEG = os.environ[\"FETAL_BRAIN_SEG\"]\n        except KeyError as e:\n            raise RuntimeError(\n                \"Environment variable FETAL_BRAIN_SEG is not specified. \"\n                \"Specify the root directory of fetal_brain_seg \"\n                \"(https://github.com/gift-surg/fetal_brain_seg) \"\n                \"using \"\n                \"'export FETAL_BRAIN_SEG=path_to_fetal_brain_seg_dir' \"\n                \"(in bashrc).\")\n    else:\n        try:\n            import monaifbs\n            DIR_FETAL_BRAIN_SEG = os.path.dirname(monaifbs.__file__)\n        except ImportError as e:\n            raise RuntimeError(\n                \"monaifbs not correctly installed. \"\n                \"Please check its installation running \"\n                \"pip install -e MONAIfbs/ \"\n            )\n\n    print(\"Using executable from {}\".format(DIR_FETAL_BRAIN_SEG))\n\n    if args.filenames_masks is None and args.dir_output is None:\n        raise IOError(\"Either --filenames-masks or --dir-output must be set\")\n\n    if args.dir_output is not None:\n        args.filenames_masks = [\n            os.path.join(args.dir_output, os.path.basename(f))\n            for f in args.filenames\n        ]\n\n    if len(args.filenames) != len(args.filenames_masks):\n        raise IOError(\"Number of filenames and filenames-masks must match\")\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    cd_fetal_brain_seg = \"cd %s\" % DIR_FETAL_BRAIN_SEG\n\n    for f, m in zip(args.filenames, args.filenames_masks):\n\n        if not ph.file_exists(f):\n            raise IOError(\"File '%s' does not exist\" % f)\n\n        # use absolute path for input image\n        f = os.path.abspath(f)\n\n        # use absolute path for output image\n        dir_output = os.path.dirname(m)\n        if not os.path.isabs(dir_output):\n            dir_output = os.path.realpath(\n                os.path.join(os.getcwd(), dir_output))\n            m = os.path.join(dir_output, os.path.basename(m))\n\n        ph.create_directory(dir_output)\n\n        # Change to root directory of fetal_brain_seg\n        cmds = [cd_fetal_brain_seg]\n\n        # Run masking independently (Takes longer but ensures that it does\n        # not terminate because of provided 'non-brain images')\n        cmd_args = [\"python fetal_brain_seg.py\"]\n        cmd_args.append(\"--input_names '%s'\" % f)\n        cmd_args.append(\"--segment_output_names '%s'\" % m)\n        cmds.append(\" \".join(cmd_args))\n\n        # Execute both steps\n        cmd = \" && \".join(cmds)\n        flag = ph.execute_command(cmd)\n\n        if flag != 0:\n            ph.print_warning(\n                \"Error using fetal_brain_seg. \\n\"\n                \"Execute '%s' for further investigation\" %\n                cmd)\n\n        ph.print_info(\"Fetal brain segmentation written to '%s'\" % m)\n\n        if args.verbose:\n            ph.show_nifti(f, segmentation=m)\n\n    elapsed_time_total = ph.stop_timing(time_start)\n\n    ph.print_title(\"Summary\")\n    exe_file_info = os.path.basename(os.path.abspath(__file__)).split(\".\")[0]\n    print(\"%s | Computational Time: %s\" % (exe_file_info, elapsed_time_total))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/application/show_slice_coverage.py",
    "content": "##\n# \\file show_slice_coverage.py\n# \\brief      Script to show slice coverage available over reconstruction\n#             space.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Feb 2019\n#\n\nimport os\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.validation.slice_coverage as sc\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Show data/slice coverage over specified reconstruction \"\n        \"space.\",\n    )\n\n    input_parser.add_filenames(required=True)\n    input_parser.add_reconstruction_space(required=True)\n    input_parser.add_output(required=True)\n    input_parser.add_dir_input_mc()\n    input_parser.add_slice_thicknesses()\n    input_parser.add_verbose(default=0)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        dir_motion_correction=args.dir_input_mc,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    reconstruction_space_sitk = sitk.ReadImage(args.reconstruction_space)\n    slice_coverage = sc.SliceCoverage(\n        stacks=stacks,\n        reconstruction_sitk=reconstruction_space_sitk,\n    )\n    slice_coverage.run()\n\n    coverage_sitk = slice_coverage.get_coverage_sitk()\n\n    dw.DataWriter.write_mask(coverage_sitk, args.output)\n\n    if args.verbose:\n        niftis = [\n            args.reconstruction_space,\n            args.output,\n        ]\n        ph.show_niftis(niftis)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/base/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/base/data_reader.py",
    "content": "##\n# \\file DataReader.py\n# \\brief      Reads data and returns Stack objects\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2017\n#\n\n\nimport os\nimport re\nimport six\nimport natsort\nimport numpy as np\nimport nibabel as nib\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.exceptions as exceptions\nimport niftymic.utilities.motion_updater as mu\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\nfrom niftymic.definitions import REGEX_FILENAMES\nfrom niftymic.definitions import REGEX_FILENAME_EXTENSIONS\n\n\n##\n# DataReader is an abstract class to read data.\n# \\date       2017-07-12 11:38:07+0100\n#\nclass DataReader(object):\n    __metaclass__ = ABCMeta\n\n    @abstractmethod\n    def read_data(self):\n        pass\n\n    @abstractmethod\n    def get_data(self):\n        pass\n\n\nclass ImageHeaderReader(DataReader):\n\n    def __init__(self, path_to_image):\n        self._path_to_image = path_to_image\n        self._image_nib = None\n\n    def read_data(self):\n        self._image_nib = nib.load(self._path_to_image)\n\n    def get_data(self):\n        return self._image_nib.header\n\n    def get_niftymic_version(self):\n        if self._image_nib is None:\n            raise RuntimeError(\"Execute 'read_data' first.\")\n\n        # NiftyMIC version embedded in the aux_file field\n        aux_file_entry = str(self._image_nib.header[\"aux_file\"])\n\n        if \"NiftyMIC\" in aux_file_entry:\n            version_str = re.sub(\"NiftyMIC-v\", \"\", aux_file_entry)\n\n            # remove annoying byte code format embedded in string \"b'...'\":\n            if \"b'\" == version_str[0:2] and \"'\" == version_str[-1]:\n                version_str = version_str[2:-1]\n\n            return version_str\n        else:\n            return None\n\n\n##\n# DataReader is an abstract class to read 3D images.\n# \\date       2017-07-12 11:38:07+0100\n#\nclass ImageDataReader(DataReader):\n    __metaclass__ = ABCMeta\n\n    def __init__(self):\n        DataReader.__init__(self)\n        self._stacks = None\n\n    ##\n    # Returns the read data as list of Stack objects\n    # \\date       2017-07-12 11:38:52+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The stacks.\n    #\n    def get_data(self):\n\n        if type(self._stacks) is not list:\n            raise exceptions.ObjectNotCreated(\"read_data\")\n\n        return [st.Stack.from_stack(s) for s in self._stacks]\n\n\n##\n# ImageDirectoryReader reads images and their masks from a given directory and\n# returns them as a list of Stack objects.\n# \\date       2017-07-12 11:36:22+0100\n#\nclass ImageDirectoryReader(ImageDataReader):\n\n    ##\n    # Store relevant information to images and their potential masks from a\n    # specified directory\n    # \\date       2017-07-11 19:04:25+0100\n    #\n    # \\param      self               The object\n    # \\param      path_to_directory  String to specify the path to input\n    #                                directory.\n    # \\param      suffix_mask        extension of stack filename as string\n    #                                indicating associated mask, e.g. \"_mask\"\n    #                                for \"A_mask.nii\".\n    # \\param      extract_slices     Boolean to indicate whether given 3D image\n    #                                shall be split into its slices along the\n    #                                k-direction.\n    #\n    def __init__(self,\n                 path_to_directory,\n                 suffix_mask=\"_mask\",\n                 extract_slices=True):\n\n        super(self.__class__, self).__init__()\n\n        self._path_to_directory = path_to_directory\n        self._suffix_mask = suffix_mask\n        self._extract_slices = extract_slices\n\n    ##\n    # Reads the image data from the given folder.\n    # \\date       2017-07-11 17:10:40+0100\n    #\n    def read_data(self):\n\n        if not ph.directory_exists(self._path_to_directory):\n            raise exceptions.DirectoryNotExistent(self._path_to_directory)\n\n        abs_path_to_directory = os.path.abspath(self._path_to_directory)\n\n        # Get data filenames of images without filename extension\n        pattern = \"(\" + REGEX_FILENAMES + \")[.]\" + REGEX_FILENAME_EXTENSIONS\n        pattern_mask = \"(\" + REGEX_FILENAMES + \")\" + self._suffix_mask + \\\n            \"[.]\" + REGEX_FILENAME_EXTENSIONS\n        p = re.compile(pattern)\n        p_mask = re.compile(pattern_mask)\n\n        # TODO:\n        #  - If folder contains A.nii and A.nii.gz that ambiguity will not\n        # be detected\n        #  - exclude potential mask filenames\n        #  - hidden files are not excluded\n        dic_filenames = {p.match(f).group(1): p.match(f).group(0)\n                         for f in os.listdir(abs_path_to_directory)\n                         if p.match(f) and not p_mask.match(f)}\n\n        dic_filenames_mask = {p_mask.match(f).group(1):\n                              p_mask.match(f).group(0)\n                              for f in os.listdir(abs_path_to_directory)\n                              if p_mask.match(f)}\n\n        # Filenames without filename ending as sorted list\n        filenames = natsort.natsorted(\n            dic_filenames.keys(), key=lambda y: y.lower())\n\n        self._stacks = [None] * len(filenames)\n        for i, filename in enumerate(filenames):\n\n            abs_path_image = os.path.join(abs_path_to_directory,\n                                          dic_filenames[filename])\n\n            if filename in dic_filenames_mask.keys():\n                abs_path_mask = os.path.join(abs_path_to_directory,\n                                             dic_filenames_mask[filename])\n            else:\n                ph.print_info(\"No mask found for '%s'.\" %\n                              (abs_path_image))\n                abs_path_mask = None\n\n            self._stacks[i] = st.Stack.from_filename(\n                abs_path_image,\n                abs_path_mask,\n                extract_slices=self._extract_slices)\n\n\n##\n# MultipleImagesReader reads multiple nifti images and returns them as a list\n# of Stack objects.\n# \\date       2017-07-12 11:28:10+0100\n#\nclass MultipleImagesReader(ImageDataReader):\n\n    ##\n    # Store relevant information to read multiple images and their potential\n    # masks.\n    # \\date       2017-07-11 19:04:25+0100\n    #\n    # \\param      self              The object\n    # \\param      file_paths        The paths to filenames as list of strings,\n    #                               e.g. [\"A.nii.gz\", \"B.nii\", \"C.nii.gz\"]\n    # \\param      file_paths_masks  The paths to the filename masks as list of\n    #                               strings. If given 'suffix_mask' is ignored.\n    #                               It is assumed the the sequence matches the\n    #                               filename order.\n    # \\param      suffix_mask       extension of stack filename as string\n    #                               indicating associated mask, e.g. \"_mask\"\n    #                               for \"A_mask.nii\".\n    # \\param      extract_slices    Boolean to indicate whether given 3D image\n    #                               shall be split into its slices along the\n    #                               k-direction.\n    #\n    def __init__(self,\n                 file_paths,\n                 file_paths_masks=None,\n                 suffix_mask=\"_mask\",\n                 extract_slices=True,\n                 dir_motion_correction=None,\n                 prefix_slice=\"_slice\",\n                 stacks_slice_thicknesses=None,\n                 ):\n\n        super(self.__class__, self).__init__()\n\n        if stacks_slice_thicknesses is not None:\n            if len(stacks_slice_thicknesses) is not len(file_paths):\n                raise IOError(\"Number of given slice thicknesses must \"\n                              \"correspond to the number of input images.\")\n            self._stacks_slice_thicknesses = stacks_slice_thicknesses\n        else:\n            self._stacks_slice_thicknesses = [None] * len(file_paths)\n\n        # Get list of paths to image\n        self._file_paths = file_paths\n        self._file_paths_masks = file_paths_masks\n        self._suffix_mask = suffix_mask\n        self._dir_motion_correction = dir_motion_correction\n        self._extract_slices = extract_slices\n        self._prefix_slice = prefix_slice\n\n    ##\n    # Reads the data of multiple images.\n    # \\date       2017-07-12 11:30:35+0100\n    #\n    def read_data(self):\n\n        self._check_input()\n\n        self._stacks = [None] * len(self._file_paths)\n\n        for i, file_path in enumerate(self._file_paths):\n\n            if self._file_paths_masks is None:\n                file_path_mask = self._get_path_to_potential_mask(file_path)\n            else:\n                if i < len(self._file_paths_masks):\n                    file_path_mask = self._file_paths_masks[i]\n                else:\n                    file_path_mask = None\n\n            self._stacks[i] = st.Stack.from_filename(\n                file_path,\n                file_path_mask,\n                slice_thickness=self._stacks_slice_thicknesses[i],\n                extract_slices=self._extract_slices,\n            )\n\n            # if given image is actually a mask, update the filename so that\n            # subsequent MotionUpdater can associate the slice transformation\n            # files\n            if file_path == file_path_mask:\n                filename = self._stacks[i].get_filename()\n                filename = re.sub(self._suffix_mask, \"\", filename)\n                self._stacks[i].set_filename(filename)\n\n        if self._dir_motion_correction is not None:\n            motion_updater = mu.MotionUpdater(\n                stacks=self._stacks,\n                dir_motion_correction=self._dir_motion_correction)\n            motion_updater.run()\n            self._stacks = motion_updater.get_data()\n\n    def _check_input(self):\n        if type(self._file_paths) is not list:\n            raise IOError(\"file_paths must be provided as list\")\n\n        if self._file_paths_masks is not None:\n            if type(self._file_paths_masks) is not list:\n                raise IOError(\"file_paths_masks must be provided as list\")\n\n    ##\n    # Gets the path to potential mask for a given file_path.\n    # \\date       2018-01-25 12:34:23+0000\n    #\n    # \\param      self       The object\n    # \\param      file_path  The file path\n    #\n    # \\return     The path the mask associated with the file or None in case no\n    #             mask was found.\n    #\n    def _get_path_to_potential_mask(self, file_path):\n        # Build absolute path to directory of image\n        path_to_directory = os.path.dirname(file_path)\n        filename = os.path.basename(file_path)\n\n        if not ph.directory_exists(path_to_directory):\n            raise exceptions.DirectoryNotExistent(path_to_directory)\n        abs_path_to_directory = os.path.abspath(path_to_directory)\n\n        # Get absolute path mask to image\n        pattern = \"(\" + REGEX_FILENAMES + \\\n            \")[.]\" + REGEX_FILENAME_EXTENSIONS\n        p = re.compile(pattern)\n        # filename = [p.match(f).group(1) if p.match(file_path)][0]\n        if not file_path.endswith(tuple(ALLOWED_EXTENSIONS)):\n            raise IOError(\"Input image type not correct. Allowed types %s\"\n                          % \"(\" + (\", or \").join(ALLOWED_EXTENSIONS) + \")\")\n\n        # Strip extension from filename to find associated mask\n        filename = [re.sub(\".\" + ext, \"\", filename)\n                    for ext in ALLOWED_EXTENSIONS\n                    if file_path.endswith(ext)][0]\n        pattern_mask = filename + self._suffix_mask + \"[.]\" + \\\n            REGEX_FILENAME_EXTENSIONS\n        p_mask = re.compile(pattern_mask)\n        filename_mask = [p_mask.match(f).group(0)\n                         for f in os.listdir(abs_path_to_directory)\n                         if p_mask.match(f)]\n\n        if len(filename_mask) == 0:\n            abs_path_mask = None\n        else:\n            # exclude non-integer valued image as candidate (to avoid using\n            # the same image as mask in case of suffix_mask = '')\n            candidate = os.path.join(abs_path_to_directory, filename_mask[0])\n            candidate_sitk = sitk.ReadImage(candidate)\n            if \"int\" in candidate_sitk.GetPixelIDTypeAsString():\n                abs_path_mask = candidate\n            else:\n                abs_path_mask = None\n\n        return abs_path_mask\n\n\n##\n# ImageSlicesDirectoryReader reads multiple stacks and their associated\n# individual slices from a directory.\n# Rationale: Read individual slices after performed slice-to-volume\n# registration steps.\n# \\date       2017-07-17 22:32:11+0100\n#\nclass ImageSlicesDirectoryReader(ImageDataReader):\n\n    ##\n    # Store relevant information to images, slices and their potential masks\n    # from a specified directory\n    # \\date       2017-07-17 22:32:01+0100\n    #\n    # \\param      self               The object\n    # \\param      path_to_directory  String to specify the path to input\n    #                                directory where images and associated\n    #                                slices are stored.\n    # \\param      suffix_mask        extension of stack filename as string\n    #                                indicating associated mask, e.g. \"_mask\"\n    #                                for \"A_mask.nii\".\n    #\n    def __init__(self,\n                 path_to_directory,\n                 image_selection=None,\n                 suffix_mask=\"_mask\",\n                 prefix_slice=\"_slice\"):\n\n        super(self.__class__, self).__init__()\n\n        self._path_to_directory = path_to_directory\n        self._suffix_mask = suffix_mask\n        self._prefix_slice = prefix_slice\n        self._image_selection = image_selection\n        self._transforms_sitk = None\n\n    def read_data(self):\n\n        if not ph.directory_exists(self._path_to_directory):\n            raise exceptions.DirectoryNotExistent(self._path_to_directory)\n\n        abs_path_to_directory = os.path.abspath(self._path_to_directory)\n\n        # Get data filenames of images by finding the prefixes associated\n        # to the slices which are build as filename_slice[0-9]+.nii.gz\n        pattern = \"(\" + REGEX_FILENAMES + \")\" + \\\n            self._prefix_slice + \"[0-9]+[.]\" + REGEX_FILENAME_EXTENSIONS\n\n        p = re.compile(pattern)\n\n        dic_filenames = {\n            p.match(f).group(1): p.match(f).group(0)\n            for f in os.listdir(abs_path_to_directory) if p.match(f)\n        }\n\n        # Filenames without filename ending as sorted list\n        filenames = natsort.natsorted(\n            dic_filenames.keys(), key=lambda y: y.lower())\n\n        # Reduce filenames to be read to selection only\n        if self._image_selection is not None:\n            filenames = [f for f in self._image_selection if f in filenames]\n\n        self._stacks = [None] * len(filenames)\n        self._slice_transforms_sitk = [None] * len(filenames)\n        for i, filename in enumerate(filenames):\n\n            # Get slice names associated to stack\n            pattern = \"(\" + filenames[i] + self._prefix_slice + \\\n                \")([0-9]+)[.]\" + REGEX_FILENAME_EXTENSIONS\n            p = re.compile(pattern)\n\n            # Dictionary linking slice number with filename (without extension)\n            dic_slice_filenames = {\n                int(p.match(f).group(2)): p.match(f).group(1) + p.match(f).group(2)\n                for f in os.listdir(abs_path_to_directory) if p.match(f)\n            }\n\n            # Build stack from image and its found slices\n            self._stacks[i] = st.Stack.from_slice_filenames(\n                dir_input=self._path_to_directory,\n                prefix_stack=filename,\n                suffix_mask=self._suffix_mask,\n                dic_slice_filenames=dic_slice_filenames)\n\n            # Read\n            self._slice_transforms_sitk[i] = [\n                sitk.ReadTransform(os.path.join(\n                    self._path_to_directory,\n                    \"%s.tfm\" % dic_slice_filenames[k]))\n                for k in sorted(dic_slice_filenames.keys())\n            ]\n\n    ##\n    # Gets the transforms associated with each individual slice for all stacks.\n    # \\date       2017-09-20 01:20:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     List of slice transform lists. Each transform is of type\n    #             sitk.Transform\n    #\n    def get_slice_transforms_sitk(self):\n        return self._slice_transforms_sitk\n\n\n##\n# MultiComponentImageReader reads a single image which has multiple components\n# \\date       2017-08-05 23:39:24+0100\n#\nclass MultiComponentImageReader(ImageDataReader):\n\n    def __init__(self,\n                 path_to_image,\n                 path_to_image_mask=None,\n                 dir_motion_correction=None,\n                 volume_motion_only=False,\n                 slice_thickness=None,\n                 ):\n\n        super(self.__class__, self).__init__()\n\n        self._path_to_image = path_to_image\n        self._path_to_image_mask = path_to_image_mask\n        self._dir_motion_correction = dir_motion_correction\n        self._volume_motion_only = volume_motion_only\n        self._slice_thickness = slice_thickness\n\n    def read_data(self):\n        vector_image_sitk = sitkh.read_sitk_vector_image(\n            self._path_to_image,\n            dtype=np.float64)\n\n        if self._path_to_image_mask is not None:\n            vector_image_sitk_mask = sitkh.read_sitk_vector_image(\n                self._path_to_image_mask,\n                dtype=np.uint8,\n            )\n\n        N_components = vector_image_sitk.GetNumberOfComponentsPerPixel()\n\n        self._stacks = [None] * N_components\n\n        filename_base = os.path.basename(self._path_to_image).split(\".\")[0]\n        for i in range(N_components):\n            image_sitk = sitk.VectorIndexSelectionCast(\n                vector_image_sitk, i)\n            if self._path_to_image_mask is not None:\n                image_sitk_mask = sitk.VectorIndexSelectionCast(\n                    vector_image_sitk_mask, i)\n            else:\n                image_sitk_mask = None\n\n            if self._slice_thickness is None:\n                slice_thickness = image_sitk.GetSpacing()[-1]\n            else:\n                slice_thickness = self._slice_thickness\n\n            filename = filename_base + \"_\" + str(i)\n            self._stacks[i] = st.Stack.from_sitk_image(\n                image_sitk=image_sitk,\n                filename=filename,\n                image_sitk_mask=image_sitk_mask,\n                slice_thickness=float(slice_thickness),\n            )\n\n        if self._dir_motion_correction is not None:\n            motion_updater = mu.MotionUpdater(\n                stacks=self._stacks,\n                dir_motion_correction=self._dir_motion_correction,\n                volume_motion_only=self._volume_motion_only,\n            )\n            motion_updater.run()\n            self._stacks = motion_updater.get_data()\n\n\nclass TransformationDataReader(DataReader):\n    __metaclass__ = ABCMeta\n\n    def __init__(self):\n        DataReader.__init__(self)\n        self._transforms_sitk = None\n\n        # Third line in *.tfm file contains information on the transform type\n        self._transform_type = {\n            \"Euler3DTransform_double_3_3\": sitk.Euler3DTransform,\n            \"AffineTransform_double_3_3\": sitk.AffineTransform,\n        }\n\n    def get_data(self):\n        return self._transforms_sitk\n\n    def _get_sitk_transform_from_filepath(self, path_to_sitk_transform):\n        # Read transform as type sitk.Transform\n        transform_sitk = sitk.ReadTransform(path_to_sitk_transform)\n\n        # Convert transform to respective type, e.g. Euler, Affine etc\n        # Third line in *.tfm file contains information on the transform type\n        with open(path_to_sitk_transform) as f:\n            content = f.readlines()\n        transform_type = content[2]\n        transform_type = re.sub(\"\\n\", \"\", transform_type)\n        transform_type = transform_type.split(\" \")[1]\n        transform_sitk = self._transform_type[transform_type](transform_sitk)\n\n        return transform_sitk\n\n\n##\n# Reads slice transformations stored in the format 'filename_slice#.tfm'.\n#\n# Rationale: Read only slice transformations associated with\n# 'motion_correction' export achieved by the volumetric reconstruction\n# algorithm\n# \\date       2018-01-31 19:16:00+0000\n#\nclass SliceTransformationDirectoryReader(TransformationDataReader):\n\n    def __init__(self, directory, suffix_slice=\"_slice\"):\n        TransformationDataReader.__init__(self)\n        self._directory = directory\n        self._suffix_slice = suffix_slice\n\n    def read_data(self):\n\n        if not ph.directory_exists(self._directory):\n            raise exceptions.DirectoryNotExistent(self._directory)\n\n        # Create absolute path for directory\n        directory = os.path.abspath(self._directory)\n\n        pattern = \"(\" + REGEX_FILENAMES + \\\n            \")%s([0-9]+)[.]tfm\" % self._suffix_slice\n        p = re.compile(pattern)\n\n        dic_tmp = {\n            (p.match(f).group(1), int(p.match(f).group(2))):\n            os.path.join(directory, p.match(f).group(0))\n            for f in os.listdir(directory) if p.match(f)\n        }\n        fnames = list(set([k[0] for k in dic_tmp.keys()]))\n        self._transforms_sitk = {fname: {} for fname in fnames}\n        for (fname, slice_number), path in six.iteritems(dic_tmp):\n            self._transforms_sitk[fname][slice_number] = \\\n                self._get_sitk_transform_from_filepath(path)\n\n\n##\n# Reads all transformations in a given directory and stores them in an ordered\n# list\n# \\date       2018-01-31 19:34:52+0000\n#\nclass TransformationDirectoryReader(TransformationDataReader):\n\n    def __init__(self, directory):\n        TransformationDataReader.__init__(self)\n        self._directory = directory\n\n    ##\n    # Reads all transformations in a given directory and stores them in an\n    # ordered list\n    # \\date       2018-01-31 19:32:45+0000\n    #\n    # \\param      self       The object\n    # \\param      extension  The extension\n    # \\post       self._transforms_sitk contains transformations as list of\n    #             sitk.Transformation objects\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def read_data(self, extension=\"tfm\"):\n        pattern = REGEX_FILENAMES + \"[.]\" + extension\n        p = re.compile(pattern)\n\n        filenames = [\n            os.path.join(self._directory, f)\n            for f in os.listdir(self._directory) if p.match(f)\n        ]\n        filenames = natsort.natsorted(filenames, key=lambda y: y.lower())\n\n        transforms_reader = MultipleTransformationsReader(filenames)\n        transforms_reader.read_data()\n        self._transforms_sitk = transforms_reader.get_data()\n\n\n##\n# Reads multiple transformations and store them as lists\n# \\date       2018-01-31 19:33:51+0000\n#\nclass MultipleTransformationsReader(TransformationDataReader):\n\n    def __init__(self, file_paths):\n        super(self.__class__, self).__init__()\n        self._file_paths = file_paths\n\n    ##\n    # Reads multiple transformations and store them as lists\n    # \\date       2018-01-31 19:32:45+0000\n    #\n    # \\param      self  The object\n    # \\post       self._transforms_sitk contains transformations as list of\n    #             sitk.Transformation objects\n    #\n    def read_data(self):\n        self._transforms_sitk = [\n            self._get_sitk_transform_from_filepath(self._file_paths[i])\n            for i in range(len(self._file_paths))\n        ]\n"
  },
  {
    "path": "niftymic/base/data_writer.py",
    "content": "##\n# \\file DataWriter.py\n# \\brief      Writes data to HDD\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\nimport os\nimport sys\nimport numpy as np\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic\n\n\nclass DataWriter(object):\n\n    ##\n    # Gets the header update.\n    #\n    # aux_file: 23 characters max\n    # descrip: 79 characters max\n    #\n    # \\see        https://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields\n    # \\date       2019-07-05 18:09:40+0100\n    #\n    # \\param      description  description of NIfTI image (max 79 chars)\n    #\n    # \\return     dictionary that carries NIfTI header information updates.\n    #\n    @staticmethod\n    def _get_header_update(description=None):\n\n        header_update = {\n            \"aux_file\": \"NiftyMIC-v%s\" % niftymic.__version__\n        }\n\n        if description is None:\n            header_update[\"descrip\"] = \"\"\n        else:\n            header_update[\"descrip\"] = description\n\n        return header_update\n\n    @staticmethod\n    def write_image(\n        image_sitk,\n        path_to_file,\n        compress=True,\n        verbose=True,\n        description=None,\n    ):\n        info = \"Write image to %s\" % path_to_file\n        if compress:\n            image_sitk = sitk.Cast(image_sitk, sitk.sitkFloat32)\n            info += \" (float32)\"\n        if verbose:\n            ph.print_info(\"%s ... \" % info, newline=False)\n        header_update = DataWriter._get_header_update(description=description)\n\n        sitkh.write_nifti_image_sitk(\n            image_sitk, path_to_file, header_update=header_update)\n        if verbose:\n            print(\"done\")\n\n    @staticmethod\n    def write_mask(\n        mask_sitk,\n        path_to_file,\n        compress=True,\n        verbose=True,\n        description=None,\n    ):\n        info = \"Write mask to %s\" % path_to_file\n        if compress:\n            mask_sitk = sitk.Cast(mask_sitk, sitk.sitkUInt8)\n            info += \" (uint8)\"\n        if verbose:\n            ph.print_info(\"%s ... \" % info, newline=False)\n        header_update = DataWriter._get_header_update(description=description)\n\n        sitkh.write_nifti_image_sitk(\n            mask_sitk, path_to_file, header_update=header_update)\n        if verbose:\n            print(\"done\")\n\n\nclass StacksWriter(object):\n    __metaclass__ = ABCMeta\n\n    def __init__(self, stacks):\n        self._stacks = stacks\n\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n\n    @abstractmethod\n    def write_data(self):\n        pass\n\n\nclass MultipleStacksWriter(StacksWriter):\n\n    def __init__(self,\n                 stacks,\n                 directory,\n                 write_mask=False,\n                 write_slices=False,\n                 write_transforms=False,\n                 suffix_mask=\"_mask\",\n                 ):\n\n        StacksWriter.__init__(self, stacks=stacks)\n        self._directory = directory\n        self._write_mask = write_mask\n        self._write_slices = write_slices\n        self._write_transforms = write_transforms\n        self._suffix_mask = suffix_mask\n\n    def set_directory(self, directory):\n        self._directory = directory\n\n    def write_data(self):\n        for stack in self._stacks:\n            stack.write(self._directory,\n                        write_mask=self._write_mask,\n                        write_slices=self._write_slices,\n                        write_transforms=self._write_transforms,\n                        suffix_mask=self._suffix_mask)\n\n\nclass MultiComponentImageWriter(StacksWriter):\n\n    def __init__(self,\n                 stacks,\n                 filename=None,\n                 write_mask=False,\n                 suffix_mask=\"_mask\",\n                 compress=True,\n                 description=None,\n                 ):\n\n        StacksWriter.__init__(self, stacks=stacks)\n        self._filename = filename\n        self._write_mask = write_mask\n        self._suffix_mask = suffix_mask\n        self._compress = compress\n        self._description = description\n\n    def set_filename(self, filename):\n        self._filename = filename\n\n    def write_data(self):\n\n        if self._filename is None:\n            raise ValueError(\"Filename is not set\")\n\n        ph.create_directory(os.path.dirname(self._filename))\n        header_update = DataWriter._get_header_update(\n            description=self._description)\n\n        info = \"Write image to '%s'\" % self._filename\n        vector_image_sitk = sitkh.get_sitk_vector_image_from_components(\n            [stack.sitk for stack in self._stacks])\n        if self._compress:\n            if not \"integer\" in vector_image_sitk.GetPixelIDTypeAsString():\n                vector_image_sitk = sitk.Cast(\n                    vector_image_sitk, sitk.sitkVectorFloat32)\n                info += \" (float32)\"\n\n        ph.print_info(\"%s ... \" % info, newline=False)\n        sitkh.write_sitk_vector_image(\n            vector_image_sitk,\n            self._filename,\n            verbose=False,\n            header_update=header_update,\n        )\n        print(\"done\")\n\n        if self._write_mask:\n            info = \"Write image mask to '%s'\" % self._filename\n            filename_split = (self._filename).split(\".\")\n            filename = filename_split[0]\n            filename += self._suffix_mask + \".\" + \\\n                (\".\").join(filename_split[1:])\n            vector_image_sitk = sitkh.get_sitk_vector_image_from_components(\n                [stack.sitk_mask for stack in self._stacks])\n\n            if self._compress:\n                vector_image_sitk = sitk.Cast(\n                    vector_image_sitk, sitk.sitkVectorUInt8)\n                info += \" (uint8)\"\n            ph.print_info(\"%s ... \" % info, newline=False)\n            sitkh.write_sitk_vector_image(\n                vector_image_sitk,\n                filename,\n                verbose=False,\n                header_update=header_update,\n            )\n            print(\"done\")\n"
  },
  {
    "path": "niftymic/base/exceptions.py",
    "content": "##\n# \\file exceptions.py\n# \\brief      User-specific exceptions\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       June 2017\n#\n\n\n##\n# Error handling in case the directory does not contain valid nifti file\n# \\date       2017-06-14 11:11:37+0100\n#\nclass InputFilesNotValid(Exception):\n\n    ##\n    # \\date       2017-06-14 11:12:55+0100\n    #\n    # \\param      self       The object\n    # \\param      directory  Path to empty folder, string\n    #\n    def __init__(self, directory):\n        self.directory = directory\n\n    def __str__(self):\n        error = \"Folder '%s' does not contain valid nifti files.\" % (\n            self.directory)\n        return error\n\n\n##\n# Error handling in case of an attempted object access which is not being\n# created yet\n# \\date       2017-06-14 11:20:33+0100\n#\nclass ObjectNotCreated(Exception):\n\n    ##\n    # Store name of function which shall be executed to create desired object.\n    # \\date       2017-06-14 11:20:52+0100\n    #\n    # \\param      self           The object\n    # \\param      function_call  function call missing to create the object\n    #\n    def __init__(self, function_call):\n        self.function_call = function_call\n\n    def __str__(self):\n        error = \"Object has not been created yet. Run '%s' first.\" % (\n            self.function_call)\n        return error\n\n\n##\n# Error handling in case specified file does not exist\n#\nclass FileNotExistent(Exception):\n\n    ##\n    # Store information on the missing file\n    # \\date       2017-06-29 12:49:18+0100\n    #\n    # \\param      self          The object\n    # \\param      missing_file  string of missing file\n    #\n    def __init__(self, missing_file):\n        self.missing_file = missing_file\n\n    def __str__(self):\n        error = \"File '%s' does not exist\" % (self.missing_file)\n        return error\n\n\n##\n# Error handling in case specified directory does not exist\n# \\date       2017-07-11 17:02:12+0100\n#\nclass DirectoryNotExistent(Exception):\n\n    ##\n    # Store information on the missing directory\n    # \\date       2017-07-11 17:02:46+0100\n    #\n    # \\param      self            The object\n    # \\param      missing_directory  string of missing directory\n    #\n    def __init__(self, missing_directory):\n        self.missing_directory = missing_directory\n\n    def __str__(self):\n        error = \"Directory '%s' does not exist\" % (self.missing_directory)\n        return error\n\n\n##\n# Error handling in case multiple filenames exist\n# (e.g. same filename but two different extensions)\n# \\date       2017-06-29 14:09:27+0100\n#\nclass FilenameAmbiguous(Exception):\n\n    ##\n    # Store information on the ambiguous file\n    # \\date       2017-06-29 14:10:34+0100\n    #\n    # \\param      self                The object\n    # \\param      ambiguous_filename  string of ambiguous file\n    #\n    def __init__(self, ambiguous_filename):\n        self.ambiguous_filename = ambiguous_filename\n\n    def __str__(self):\n        error = \"Filename '%s' ambiguous\" % (self.ambiguous_filename)\n        return error\n\n##\n# Error handling in case IO is not correct\n# \\date       2017-07-11 20:21:19+0100\n#\nclass IOError(Exception):\n\n    ##\n    # Store information on the IO error\n    # \\date       2017-07-11 20:21:38+0100\n    #\n    # \\param      self   The object\n    # \\param      error  The error\n    #\n    def __init__(self, error):\n        self.error = error\n\n    def __str__(self):\n        return self.error"
  },
  {
    "path": "niftymic/base/psf.py",
    "content": "##\n# \\file psf.py\n# \\brief      Compute the Gaussian point spread function (PSF) associated with\n#             a slice acquisition in the coordinates of the reconstruction\n#             space\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       April 2016\n#\n\n\n# Import libraries\nimport numpy as np\n\n\nclass PSF:\n\n    ##\n    # Compute rotated covariance matrix which expresses the PSF of the slice in\n    # the coordinates of the HR volume\n    # \\date       2017-11-01 16:16:20+0000\n    #\n    # \\param      self            The object\n    # \\param      slice           Slice object which is aimed to be simulated\n    #                             according to the slice acquisition model\n    # \\param      reconstruction  Stack object containing the HR volume\n    #\n    # \\return     Covariance matrix U*Sigma_diag*U' where U represents the\n    #             orthogonal trafo between slice and reconstruction\n    #\n    def get_covariance_matrix_in_reconstruction_space(\n            self,\n            slice,\n            reconstruction):\n\n        cov = self.get_covariance_matrix_in_reconstruction_space_sitk(\n            reconstruction.sitk.GetDirection(),\n            slice.sitk.GetDirection(),\n            slice.sitk.GetSpacing())\n\n        return cov\n\n    ##\n    # Gets the axis-aligned covariance matrix describing the PSF in\n    # reconstruction space coordinates.\n    # \\date       2017-11-01 16:21:31+0000\n    #\n    # \\param      self                           The object\n    # \\param      reconstruction_direction_sitk  Image header (sitk) direction\n    #                                            of reconstruction space\n    # \\param      slice_direction_sitk           Image header (sitk) direction\n    #                                            of slice space\n    # \\param      slice_spacing                  Spacing of slice space\n    #\n    # \\return     Axis-aligned covariance matrix describing the PSF.\n    #\n    def get_covariance_matrix_in_reconstruction_space_sitk(\n            self,\n            reconstruction_direction_sitk,\n            slice_direction_sitk,\n            slice_spacing):\n\n        # Compute rotation matrix to express the PSF in the coordinate system\n        # of the reconstruction space\n        U = self._get_relative_rotation_matrix(\n            slice_direction_sitk, reconstruction_direction_sitk)\n\n        # Get axis-aligned PSF\n        cov = self.get_gaussian_psf_covariance_matrix_from_spacing(\n            slice_spacing)\n\n        # Return Gaussian blurring variance covariance matrix of slice in\n        # reconstruction space coordinates\n        return U.dot(cov).dot(U.transpose())\n\n    ##\n    # Gets the predefined covariance matrix in reconstruction space.\n    # \\date       2017-10-31 23:27:44+0000\n    #\n    # \\param      self                           The object\n    # \\param      reconstruction_direction_sitk  Image header (sitk) direction\n    #                                            of reconstruction space\n    # \\param      slice_direction_sitk           Image header (sitk) direction\n    #                                            of slice space\n    # \\param      cov                            Axis-aligned covariance matrix\n    #                                            describing the PSF\n    #\n    # \\return     The predefined covariance matrix in reconstruction space\n    #             coordinates.\n    #\n    def get_predefined_covariance_matrix_in_reconstruction_space(\n            self,\n            reconstruction_direction_sitk,\n            slice_direction_sitk,\n            cov):\n\n        # Compute rotation matrix to express the PSF in the coordinate system\n        # of the reconstruction space\n        U = self._get_relative_rotation_matrix(\n            slice_direction_sitk, reconstruction_direction_sitk)\n\n        # Return Gaussian blurring variance covariance matrix of slice in\n        # reconstruction space coordinates\n        return U.dot(cov).dot(U.transpose())\n\n    ##\n    # Compute (axis aligned) covariance matrix from spacing The PSF is modelled\n    # as Gaussian with\n    #   FWHM = 1.2*in-plane-resolution (in-plane)\n    #   FWHM = slice thickness (through-plane)\n    # \\date       2017-11-01 16:16:36+0000\n    #\n    # \\param      spacing  3D array containing in-plane and through-plane\n    #                      dimensions\n    #\n    # \\return     (axis aligned) covariance matrix representing PSF modelled\n    #             Gaussian as 3x3 np.array\n    #\n    @staticmethod\n    def get_gaussian_psf_covariance_matrix_from_spacing(spacing):\n\n        # Compute Gaussian to approximate in-plane PSF:\n        sigma_x2 = (1.2*spacing[0])**2/(8*np.log(2))\n        sigma_y2 = (1.2*spacing[1])**2/(8*np.log(2))\n\n        # Compute Gaussian to approximate through-plane PSF:\n        sigma_z2 = spacing[2]**2/(8*np.log(2))\n\n        return np.diag([sigma_x2, sigma_y2, sigma_z2])\n\n    ##\n    # Gets the relative rotation matrix to express slice-axis aligned\n    # covariance matrix in coordinates of HR volume\n    # \\date       2016-10-14 16:37:57+0100\n    #\n    # \\param      slice_direction_sitk           Image header (sitk) direction\n    #                                            of slice space\n    # \\param      reconstruction_direction_sitk  Image header (sitk) direction\n    #                                            of reconstruction space\n    #\n    # \\return     The relative rotation matrix as 3x3 numpy array\n    #\n    @staticmethod\n    def _get_relative_rotation_matrix(slice_direction_sitk,\n                                      reconstruction_direction_sitk):\n\n        # Compute rotation matrix to express the PSF in the coordinate system\n        # of the HR volume\n        dim = np.sqrt(len(slice_direction_sitk)).astype('int')\n        direction_matrix_reconstruction = np.array(\n            reconstruction_direction_sitk).reshape(dim, dim)\n        direction_matrix_slice = np.array(\n            slice_direction_sitk).reshape(dim, dim)\n\n        U = direction_matrix_reconstruction.transpose().dot(\n            direction_matrix_slice)\n\n        return U\n"
  },
  {
    "path": "niftymic/base/slice.py",
    "content": "##\n# \\file Slice.py\n# \\brief      { item_description }\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       September 2015\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.data_writer as dw\nimport niftymic.base.exceptions as exceptions\nfrom niftymic.definitions import VIEWER\n\n\n# In addition to the nifti-image as being stored as sitk.Image for a single\n#  3D slice \\f$ \\in R^3 \\times R^3 \\times 1\\f$ the class Slice\n#  also contains additional variables helpful to work with the data\nclass Slice:\n\n    # Create Slice instance with additional information to actual slice\n    #  \\param[in] slice_sitk 3D slice in \\R x \\R x 1, sitk.Image object\n    #  \\param[in] filename of parent stack, string\n    #  \\param[in] slice_number number of slice within parent stack, integer\n    #  \\param[in] slice_sitk_mask associated mask of slice, sitk.Image object (optional)\n    @classmethod\n    def from_sitk_image(cls,\n                        slice_sitk,\n                        slice_number,\n                        slice_thickness,\n                        filename=\"unknown\",\n                        slice_sitk_mask=None,\n                        ):\n\n        slice = cls()\n\n        # Directory\n        # dir_input = \"/\".join(filename.split(\"/\")[0:-1]) + \"/\"\n\n        # Filename without extension\n        # filename = filename.split(\"/\")[-1:][0].split(\".\")[0]\n\n        slice._dir_input = None\n        slice._filename = filename\n        slice._slice_number = slice_number\n        slice._slice_thickness = slice_thickness\n\n        # Explicit cast (+ creation of other image instance)\n        slice.sitk = sitk.Cast(slice_sitk, sitk.sitkFloat64)\n        slice.itk = sitkh.get_itk_from_sitk_image(slice.sitk)\n\n        # Append masks (if provided)\n        if slice_sitk_mask is not None:\n            slice.sitk_mask = sitk.Cast(slice_sitk_mask, sitk.sitkUInt8)\n            try:\n                # ensure mask occupies the same physical space\n                slice.sitk_mask.CopyInformation(slice.sitk)\n            except RuntimeError as e:\n                raise IOError(\n                    \"Given image and its mask do not occupy the same space: %s\" %\n                    e.message)\n            slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask)\n        else:\n            slice.sitk_mask = slice._generate_identity_mask()\n            slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask)\n\n        # slice._sitk_upsampled = None\n\n        # HACK (for current Slice-to-Volume Registration)\n        #  See class SliceToVolumeRegistration\n        # slice._sitk_upsampled = slice._get_upsampled_isotropic_resolution_slice(slice_sitk)\n        # slice._itk_upsampled =\n        # sitkh.get_itk_from_sitk_image(slice._sitk_upsampled)\n\n        # if slice_sitk_mask is not None:\n        #     slice._sitk_mask_upsampled = slice._get_upsampled_isotropic_resolution_slice(slice_sitk_mask)\n        #     slice._itk_mask_upsampled = sitkh.get_itk_from_sitk_image(slice._sitk_mask_upsampled)\n        # else:\n        #     slice._sitk_mask_upsampled = None\n        #     slice._itk_mask_upsampled = None\n\n        # Store current affine transform of image\n        slice._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            slice.sitk)\n\n        # Prepare history of affine transforms, i.e. encoded spatial\n        #  position+orientation of slice, and rigid motion estimates of slice\n        #  obtained in the course of the registration/reconstruction process\n        slice._history_affine_transforms = []\n        slice._history_affine_transforms.append(slice._affine_transform_sitk)\n\n        slice._history_motion_corrections = []\n        slice._history_motion_corrections.append(sitk.Euler3DTransform())\n\n        return slice\n\n    # Create Stack instance from file and add corresponding mask. Mask is\n    #  either provided in the directory or created as binary mask consisting\n    #  of ones.\n    #  \\param[in] dir_input string to input directory of nifti-file to read\n    #  \\param[in] filename string of nifti-file to read\n    #  \\param[in] stack_filename filename extension of parent stack, string\n    #  \\param[in] slice_number number of slice within parent stack, integer\n    #  \\param[in] suffix_mask extension of slice filename which indicates associated mask\n    #  \\return Stack object including its slices with corresponding masks\n    @classmethod\n    def from_filename(cls,\n                      file_path,\n                      slice_number,\n                      slice_thickness,\n                      file_path_mask=None,\n                      verbose=False,\n                      ):\n\n        slice = cls()\n\n        if not ph.file_exists(file_path):\n            raise exceptions.FileNotExistent(file_path)\n\n        slice._dir_input = os.path.dirname(file_path)\n        slice._filename = os.path.basename(file_path).split(\".\")[0]\n        slice._slice_number = slice_number\n        slice._slice_thickness = slice_thickness\n\n        # Append stacks as SimpleITK and ITK Image objects\n        slice.sitk = sitkh.read_nifti_image_sitk(file_path, sitk.sitkFloat64)\n        slice.itk = sitkh.get_itk_from_sitk_image(slice.sitk)\n\n        # Append masks (if provided)\n        if file_path_mask is None:\n            slice.sitk_mask = slice._generate_identity_mask()\n            if verbose:\n                ph.print_info(\n                    \"Identity mask created for '%s'.\" % (file_path))\n\n        else:\n            if not ph.file_exists(file_path_mask):\n                raise exceptions.FileNotExistent(file_path_mask)\n            slice.sitk_mask = sitkh.read_nifti_image_sitk(\n                file_path_mask, sitk.sitkUInt8)\n            try:\n                # ensure mask occupies the same physical space\n                slice.sitk_mask.CopyInformation(slice.sitk)\n            except RuntimeError as e:\n                raise IOError(\n                    \"Given image and its mask do not occupy the same space: %s\" %\n                    e.message)\n\n        slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask)\n\n        # Store current affine transform of image\n        slice._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            slice.sitk)\n\n        # Prepare history of affine transforms, i.e. encoded spatial\n        #  position+orientation of slice, and motion estimates of slice\n        #  obtained in the course of the registration/reconstruction process\n        slice._history_affine_transforms = []\n        slice._history_affine_transforms.append(slice._affine_transform_sitk)\n\n        slice._history_motion_corrections = []\n        slice._history_motion_corrections.append(sitk.Euler3DTransform())\n\n        return slice\n\n    # Copy constructor\n    #  \\param[in] slice_to_copy Slice object to be copied\n    #  \\return copied Slice object\n    # TODO: That's not really well done!\n    @classmethod\n    def from_slice(cls, slice_to_copy):\n        slice = cls()\n\n        if not isinstance(slice_to_copy, Slice):\n            raise ValueError(\"Input must be of type Slice. Given: %s\" %\n                             type(slice_to_copy))\n\n        # Copy image slice and mask\n        slice.sitk = sitk.Image(slice_to_copy.sitk)\n        slice.itk = sitkh.get_itk_from_sitk_image(slice.sitk)\n\n        slice.sitk_mask = sitk.Image(slice_to_copy.sitk_mask)\n        slice.itk_mask = sitkh.get_itk_from_sitk_image(slice.sitk_mask)\n\n        slice._filename = slice_to_copy.get_filename()\n        slice._slice_number = slice_to_copy.get_slice_number()\n        slice._dir_input = slice_to_copy.get_directory()\n        slice._slice_thickness = slice_to_copy.get_slice_thickness()\n\n        # slice._history_affine_transforms, slice._history_motion_corrections =\n        # slice_to_copy.get_registration_history()\n\n        # Store current affine transform of image\n        slice._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            slice.sitk)\n\n        # Prepare history of affine transforms, i.e. encoded spatial\n        #  position+orientation of slice, and rigid motion estimates of slice\n        #  obtained in the course of the registration/reconstruction process\n\n        slice._history_affine_transforms, slice._history_motion_corrections = slice_to_copy.get_registration_history()\n\n        return slice\n\n    ##\n    #       Motion correction update.\n    # \\date       2016-09-21 00:50:08+0100\n    #\n    # Update motion correction of slice and update its position in physical\n    # space accordingly.\n    #\n    # \\param      self                   The object\n    # \\param[in]  affine_transform_sitk  transform as sitk.AffineTransform\n    #                                    object\n    # \\post       origin and direction of slice gets updated based on transform\n    #\n    def update_motion_correction(self, affine_transform_sitk):\n\n        # Update rigid motion estimate\n        current_rigid_motion_estimate = sitkh.get_composite_sitk_affine_transform(\n            affine_transform_sitk, self._history_motion_corrections[-1])\n        self._history_motion_corrections.append(current_rigid_motion_estimate)\n\n        # New affine transform of slice after rigid motion correction\n        affine_transform = sitkh.get_composite_sitk_affine_transform(\n            affine_transform_sitk, self._affine_transform_sitk)\n\n        # Update affine transform of slice, i.e. change image origin and\n        # direction in physical space\n        self._update_affine_transform(affine_transform)\n\n    # ## Update rigid motion estimate of slice and update its position in\n    # #  physical space accordingly.\n    # #  \\param[in] rigid_transform_sitk rigid transform as sitk object\n    # #  \\post origin and direction of slice gets updated based on rigid transform\n    # def update_rigid_motion_estimate(self, rigid_transform_sitk):\n\n    #     ## Update rigid motion estimate\n    #     current_rigid_motion_estimate = sitkh.get_composite_sitk_euler_transform(rigid_transform_sitk, self._history_rigid_motion_estimates[-1])\n    #     self._history_rigid_motion_estimates.append(current_rigid_motion_estimate)\n\n    #     ## New affine transform of slice after rigid motion correction\n    # affine_transform =\n    # sitkh.get_composite_sitk_affine_transform(rigid_transform_sitk,\n    # self._affine_transform_sitk)\n\n    #     ## Update affine transform of slice, i.e. change image origin and direction in physical space\n    #     self._update_affine_transform(affine_transform)\n\n    # Get filename of slice, e.g. name of parent stack\n    #  \\return filename, string\n    def get_filename(self):\n        return self._filename\n\n    def set_filename(self, filename):\n        self._filename = filename\n\n    # Get number of slice within parent stack\n    #  \\return slice number, integer\n    def get_slice_number(self):\n        return self._slice_number\n\n    def get_slice_thickness(self):\n        return float(self._slice_thickness)\n\n    def get_inplane_resolution(self):\n        return float(self.sitk.GetSpacing()[0])\n\n    # Get directory where parent stack is stored\n    #  \\return directory, string\n    def get_directory(self):\n        return self._dir_input\n\n    # Get current affine transformation defining the spatial position in\n    #  physical space of slice\n    #  \\return affine transformation, sitk.AffineTransform object\n    def get_affine_transform(self):\n        return self._affine_transform_sitk\n\n    ##\n    # Get applied motion correction transform to slice\n    # \\date       2017-08-08 13:16:28+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The motion correction transform.\n    #\n    def get_motion_correction_transform(self):\n        return self._history_motion_corrections[-1]\n\n    # Get history history of affine transforms, i.e. encoded spatial\n    #  position+orientation of slice, and rigid motion estimates of slice\n    #  obtained in the course of the registration/reconstruction process\n    #  \\return list of sitk.AffineTransform and sitk.Euler3DTransform objects\n    def get_registration_history(self):\n        affine_transforms = list(self._history_affine_transforms)\n        motion_corrections = list(self._history_motion_corrections)\n        return affine_transforms, motion_corrections\n\n    def set_registration_history(self, registration_history):\n        affine_transform_sitk = registration_history[0][-1]\n        self._update_affine_transform(affine_transform_sitk)\n\n        self._history_affine_transforms = [a for a in registration_history[0]]\n        self._history_motion_corrections = [t for t in registration_history[1]]\n\n    # Display slice with external viewer (ITK-Snap)\n    #  \\param[in] show_segmentation display slice with or without associated segmentation (default=0)\n    def show(self, show_segmentation=0, label=None, viewer=VIEWER, verbose=True):\n\n        if label is None:\n            label = self._filename + \"_\" + str(self._slice_number)\n\n        if show_segmentation:\n            segmentation = self.sitk_mask\n        else:\n            segmentation = None\n\n        sitkh.show_sitk_image(\n            self.sitk,\n            segmentation=segmentation,\n            label=label,\n            viewer=viewer,\n            verbose=verbose)\n\n    # Write information of Slice to HDD to given diretory:\n    #  - sitk.Image object of slice\n    #  - affine transformation describing physical space position of slice\n    #  \\param[in] directory string specifying where the output will be written to (default=\"/tmp/\")\n    #  \\param[in] filename string specifyig the filename. If not given, filename of parent stack is used\n    def write(self,\n              directory,\n              filename=None,\n              write_slice=True,\n              write_transform=True,\n              suffix_mask=\"_mask\",\n              prefix_slice=\"_slice\",\n              write_transforms_history=False,\n              ):\n\n        # Create directory if not existing\n        ph.create_directory(directory)\n\n        # Construct filename\n        if filename is None:\n            filename_out = self._filename + \\\n                prefix_slice + str(self._slice_number)\n        else:\n            filename_out = filename + prefix_slice + str(self._slice_number)\n\n        full_file_name = os.path.join(directory, filename_out)\n\n        # Write slice and affine transform\n        if write_slice:\n            dw.DataWriter.write_image(\n                self.sitk, \"%s.nii.gz\" % full_file_name, verbose=False)\n\n            # Write mask to specified location if given\n            if self.sitk_mask is not None:\n                nda = sitk.GetArrayFromImage(self.sitk_mask)\n\n                # Write mask if it does not consist of only ones\n                if not np.all(nda):\n                    dw.DataWriter.write_mask(\n                        self.sitk_mask,\n                        \"%s%s.nii.gz\" % (full_file_name, suffix_mask),\n                        verbose=False,\n                    )\n\n        if write_transform:\n            sitk.WriteTransform(\n                # self.get_affine_transform(),\n                self.get_motion_correction_transform(),\n                full_file_name + \".tfm\")\n\n        if write_transforms_history:\n            dir_output = os.path.join(directory, \"motion_correction_history\")\n            ph.create_directory(dir_output)\n            registration_transforms = self.get_registration_history()[1]\n            for i, transform_sitk in enumerate(registration_transforms):\n                path_to_transform = os.path.join(\n                    dir_output, \"%s_%d.tfm\" % (filename_out, i))\n                sitk.WriteTransform(transform_sitk, path_to_transform)\n\n        # print(\"Slice %r of stack %s was successfully written to %s\" %(self._slice_number, self._filename, full_file_name))\n        # print(\"Transformation of slice %r of stack %s was successfully\n        # written to %s\" %(self._slice_number, self._filename, full_file_name))\n\n    # Update slice with new affine transform, specifying updated spatial\n    #  position of slice in physical space. The transform is obtained via\n    #  slice-to-volume registration step, e.g.\n    #  \\param[in] affine_transform_sitk affine transform as sitk-object\n    def _update_affine_transform(self, affine_transform_sitk):\n\n        # Ensure correct object type\n        self._affine_transform_sitk = sitk.AffineTransform(\n            affine_transform_sitk)\n\n        # Append transform to registration history\n        self._history_affine_transforms.append(affine_transform_sitk)\n\n        # Get origin and direction of transformed 3D slice given the new\n        # spatial transform\n        origin = sitkh.get_sitk_image_origin_from_sitk_affine_transform(\n            affine_transform_sitk, self.sitk)\n        direction = sitkh.get_sitk_image_direction_from_sitk_affine_transform(\n            affine_transform_sitk, self.sitk)\n\n        # Update image objects\n        self.sitk.SetOrigin(origin)\n        self.sitk.SetDirection(direction)\n\n        self.itk.SetOrigin(origin)\n        self.itk.SetDirection(sitkh.get_itk_from_sitk_direction(direction))\n\n        # Update image mask objects\n        if self.sitk_mask is not None:\n            self.sitk_mask.SetOrigin(origin)\n            self.sitk_mask.SetDirection(direction)\n\n            self.itk_mask.SetOrigin(origin)\n            self.itk_mask.SetDirection(\n                sitkh.get_itk_from_sitk_direction(direction))\n\n    # ## Upsample slices in k-direction to in-plane resolution.\n    # #  \\param[in] slice_sitk slice as sitk.Image object to be upsampled\n    # #  \\return upsampled slice as sitk.Image object\n    # #  \\warning only used for Slice-to-Volume Registration and shall me removed at some point\n    # def _get_upsampled_isotropic_resolution_slice(self, slice_sitk):\n\n    #     ## Fetch info used for upsampling\n    #     spacing = np.array(slice_sitk.GetSpacing())\n    #     size = np.array(slice_sitk.GetSize())\n\n    #     ## Set dimension of each slice in k-direction accordingly\n    #     size[2] = np.round(spacing[2]/spacing[0])\n\n    #     ## Update spacing in k-direction to be equal to in-plane spacing\n    #     spacing[2] = spacing[0]\n\n    #     ## Upsample slice to isotropic resolution\n    #     default_pixel_value = 0\n\n    #     slice_upsampled_sitk = sitk.Resample(\n    #         slice_sitk,\n    #         size,\n    #         sitk.Euler3DTransform(),\n    #         sitk.sitkNearestNeighbor,\n    #         slice_sitk.GetOrigin(),\n    #         spacing,\n    #         slice_sitk.GetDirection(),\n    #         default_pixel_value,\n    #         slice_sitk.GetPixelIDValue())\n\n    #     return slice_upsampled_sitk\n\n    # Create a binary mask consisting of ones\n    #  \\return binary_mask as sitk.Image object consisting of ones\n    def _generate_identity_mask(self):\n        shape = sitk.GetArrayFromImage(self.sitk).shape\n        nda = np.ones(shape, dtype=np.uint8)\n\n        binary_mask = sitk.GetImageFromArray(nda)\n        binary_mask.CopyInformation(self.sitk)\n\n        return binary_mask\n"
  },
  {
    "path": "niftymic/base/stack.py",
    "content": "##\n# \\file stack.py\n# \\brief      { item_description }\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       September 2015\n#\n\n\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nimport simplereg.resampler\n\nimport niftymic.base.slice as sl\nimport niftymic.base.exceptions as exceptions\nimport niftymic.base.data_writer as dw\n\nfrom niftymic.definitions import ALLOWED_EXTENSIONS, VIEWER\n\n\n##\n# In addition to the nifti-image (stored as sitk.Image object) this class Stack\n# also contains additional variables helpful to work with the data.\n#\nclass Stack:\n\n    def __init__(self):\n        self._is_unity_mask = True\n        self._deleted_slices = []\n        self._history_affine_transforms = []\n        self._history_motion_corrections = []\n\n    ##\n    # Create Stack instance from file and add corresponding mask. Mask is\n    # either provided in the directory or created as binary mask consisting of\n    # ones.\n    # \\param[in]  dir_input    string to input directory of nifti-file to read\n    # \\param[in]  filename     string of nifti-file to read\n    # \\param[in]  suffix_mask  extension of stack filename which indicates\n    #                          associated mask\n    # \\return     Stack object including its slices with corresponding masks\n    #\n    @classmethod\n    def from_filename(cls,\n                      file_path,\n                      file_path_mask=None,\n                      extract_slices=True,\n                      verbose=False,\n                      slice_thickness=None,\n                      ):\n\n        stack = cls()\n\n        if not ph.file_exists(file_path):\n            raise exceptions.FileNotExistent(file_path)\n\n            path_to_directory = os.path.dirname(file_path)\n\n        # Strip extension from filename and remove potentially included \".\"\n        filename = [re.sub(\".\" + ext, \"\", os.path.basename(file_path))\n                    for ext in ALLOWED_EXTENSIONS\n                    if file_path.endswith(ext)][0]\n        # filename = filename.replace(\".\", \"p\")\n\n        stack._dir = os.path.dirname(file_path)\n        stack._filename = filename\n\n        # Append stacks as SimpleITK and ITK Image objects\n        stack.sitk = sitkh.read_nifti_image_sitk(file_path, sitk.sitkFloat64)\n        stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk)\n\n        # Set slice thickness of acquisition\n        if slice_thickness is None:\n            stack._slice_thickness = stack.sitk.GetSpacing()[-1]\n        else:\n            stack._slice_thickness = slice_thickness\n\n        # Append masks (either provided or binary mask)\n        if file_path_mask is None:\n            stack.sitk_mask = stack._generate_identity_mask()\n            if verbose:\n                ph.print_info(\n                    \"Identity mask created for '%s'.\" % (file_path))\n\n        else:\n            if not ph.file_exists(file_path_mask):\n                raise exceptions.FileNotExistent(file_path_mask)\n            stack.sitk_mask = sitkh.read_nifti_image_sitk(\n                file_path_mask, sitk.sitkUInt8)\n            try:\n                # ensure masks occupy same physical space\n                stack.sitk_mask.CopyInformation(stack.sitk)\n            except RuntimeError as e:\n                raise IOError(\n                    \"Given image and its mask do not occupy the same space: %s\" %\n                    e.message)\n            stack._is_unity_mask = False\n\n            # Check that binary mask is provided\n            nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n            if nda_mask.max() > 1:\n                raise ValueError(\n                    \"Mask values > 1 encountered in '%s'. \"\n                    \"Only binary masks are allowed.\" % file_path_mask)\n\n        # Append itk object\n        stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n\n        # Store current affine transform of image\n        stack._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            stack.sitk)\n\n        # Prepare history of affine transforms, i.e. encoded spatial\n        #  position+orientation of stack, and motion estimates of stack\n        #  obtained in the course of the registration/reconstruction process\n        stack._history_affine_transforms = []\n        stack._history_affine_transforms.append(stack._affine_transform_sitk)\n\n        stack._history_motion_corrections = []\n        stack._history_motion_corrections.append(sitk.Euler3DTransform())\n\n        # Extract all slices and their masks from the stack and store them\n        if extract_slices:\n            dimenson = stack.sitk.GetDimension()\n            if dimenson == 3:\n                stack._N_slices = stack.sitk.GetSize()[-1]\n                stack._slices = stack._extract_slices(\n                    slice_thickness=stack.get_slice_thickness())\n            elif dimenson == 2:\n                stack._N_slices = 1\n                stack._slices = [stack.sitk[:, :]]\n        else:\n            stack._N_slices = 0\n            stack._slices = None\n\n        if verbose:\n            ph.print_info(\n                \"Stack (image + mask) associated to '%s' successfully read.\" %\n                (file_path))\n\n        return stack\n\n    ##\n    # Create Stack instance from stack slices in specified directory and add\n    # corresponding mask.\n    # \\date       2017-08-15 19:18:56+0100\n    #\n    # \\param      cls                  The cls\n    # \\param[in]  dir_input            string to input directory where bundle\n    #                                  of slices are stored\n    # \\param[in]  prefix_stack         prefix indicating the corresponding\n    #                                  stack\n    # \\param[in]  suffix_mask          extension of stack filename which\n    #                                  indicates associated mask\n    # \\param      dic_slice_filenames  Dictionary linking slice number (int)\n    #                                  with filename (without extension)\n    # \\param      prefix_slice         The prefix slice\n    #\n    # \\return     Stack object including its slices with corresponding masks\n    # \\example    mask (suffix_mask) of slice j of stack i (prefix_stack)\n    # reads: i_slicej_mask.nii.gz\n    #\n    # TODO: Code cleaning\n    @classmethod\n    def from_slice_filenames(cls,\n                             dir_input,\n                             prefix_stack,\n                             suffix_mask=None,\n                             dic_slice_filenames=None,\n                             prefix_slice=\"_slice\",\n                             slice_thickness=None,\n                             ):\n\n        stack = cls()\n\n        if dir_input[-1] is not \"/\":\n            dir_input += \"/\"\n\n        stack._dir = dir_input\n        stack._filename = prefix_stack\n\n        # Get 3D images\n        stack.sitk = sitkh.read_nifti_image_sitk(\n            dir_input + prefix_stack + \".nii.gz\", sitk.sitkFloat64)\n        stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk)\n\n        # Store current affine transform of image\n        stack._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            stack.sitk)\n\n        # Prepare history of affine transforms, i.e. encoded spatial\n        #  position+orientation of stack, and motion estimates of stack\n        #  obtained in the course of the registration/reconstruction process\n        stack._history_affine_transforms = []\n        stack._history_affine_transforms.append(stack._affine_transform_sitk)\n\n        stack._history_motion_corrections = []\n        stack._history_motion_corrections.append(sitk.Euler3DTransform())\n\n        # Set slice thickness of acquisition\n        if slice_thickness is None:\n            stack._slice_thickness = float(stack.sitk.GetSpacing()[-1])\n        else:\n            stack._slice_thickness = float(slice_thickness)\n\n        # Append masks (either provided or binary mask)\n        if suffix_mask is not None and \\\n            os.path.isfile(dir_input +\n                           prefix_stack + suffix_mask + \".nii.gz\"):\n            stack.sitk_mask = sitkh.read_nifti_image_sitk(\n                dir_input + prefix_stack + suffix_mask + \".nii.gz\",\n                sitk.sitkUInt8)\n            stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n            stack._is_unity_mask = False\n        else:\n            stack.sitk_mask = stack._generate_identity_mask()\n            stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n            stack._is_unity_mask = True\n\n        # Get slices\n        if dic_slice_filenames is None:\n            stack._N_slices = stack.sitk.GetDepth()\n            stack._slices = [None] * stack._N_slices\n\n            # Append slices as Slice objects\n            for i in range(0, stack._N_slices):\n                path_to_slice = os.path.join(\n                    dir_input,\n                    prefix_stack + prefix_slice + str(i) + \".nii.gz\")\n                path_to_slice_mask = os.path.join(\n                    dir_input,\n                    prefix_stack + prefix_slice + str(i) + suffix_mask + \".nii.gz\")\n\n                if ph.file_exists(path_to_slice_mask):\n                    stack._slices[i] = sl.Slice.from_filename(\n                        file_path=path_to_slice,\n                        slice_number=i,\n                        file_path_mask=path_to_slice_mask)\n                else:\n                    stack._slices[i] = sl.Slice.from_filename(\n                        file_path=path_to_slice,\n                        slice_number=i)\n        else:\n            slice_numbers = sorted(dic_slice_filenames.keys())\n            stack._N_slices = len(slice_numbers)\n            stack._slices = [None] * stack._N_slices\n\n            for i, slice_number in enumerate(slice_numbers):\n                path_to_slice = os.path.join(\n                    dir_input,\n                    dic_slice_filenames[slice_number] + \".nii.gz\")\n                path_to_slice_mask = os.path.join(\n                    dir_input, dic_slice_filenames[slice_number] + suffix_mask + \".nii.gz\")\n\n                if ph.file_exists(path_to_slice_mask):\n                    stack._slices[i] = sl.Slice.from_filename(\n                        file_path=path_to_slice,\n                        slice_number=slice_number,\n                        file_path_mask=path_to_slice_mask,\n                        slice_thickness=stack.get_slice_thickness(),\n                    )\n                else:\n                    stack._slices[i] = sl.Slice.from_filename(\n                        file_path=path_to_slice,\n                        slice_number=slice_number,\n                        slice_thickness=stack.get_slice_thickness(),\n                    )\n\n        return stack\n\n    # Create Stack instance from exisiting sitk.Image instance. Slices are\n    #  not extracted and stored separately in the object. The idea is to use\n    #  this function when the stack is regarded as entire volume (like the\n    #  reconstructed HR volume).\n    #  \\param[in] image_sitk sitk.Image created from nifti-file\n    #  \\param[in] name string containing the chosen name for the stack\n    #  \\param[in] image_sitk_mask associated mask of stack, sitk.Image object (optional)\n    #  \\return Stack object without slice information\n    @classmethod\n    def from_sitk_image(cls,\n                        image_sitk,\n                        slice_thickness,\n                        filename=\"unknown\",\n                        image_sitk_mask=None,\n                        extract_slices=True,\n                        slice_numbers=None,\n                        ):\n        stack = cls()\n\n        # Explicit cast (+ creation of other image instance)\n        stack.sitk = sitk.Cast(image_sitk, sitk.sitkFloat64)\n        stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk)\n\n        # Set slice thickness of acquisition\n        if not ph.is_float(slice_thickness):\n            raise ValueError(\"Slice thickness must be of type float\")\n        stack._slice_thickness = float(slice_thickness)\n\n        stack._filename = filename\n        stack._dir = None\n\n        # Append masks (if provided)\n        if image_sitk_mask is not None:\n            stack.sitk_mask = sitk.Cast(image_sitk_mask, sitk.sitkUInt8)\n            try:\n                # ensure mask occupies the same physical space\n                stack.sitk_mask.CopyInformation(stack.sitk)\n            except RuntimeError as e:\n                raise IOError(\n                    \"Given image and its mask do not occupy the same space: %s\" %\n                    e.message)\n            stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n            if sitk.GetArrayFromImage(stack.sitk_mask).prod() == 1:\n                stack._is_unity_mask = True\n            else:\n                stack._is_unity_mask = False\n        else:\n            stack.sitk_mask = stack._generate_identity_mask()\n            stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n            stack._is_unity_mask = True\n\n        # Extract all slices and their masks from the stack and store them\n        if extract_slices:\n            stack._N_slices = stack.sitk.GetSize()[-1]\n            stack._slices = stack._extract_slices(\n                slice_numbers=slice_numbers,\n                slice_thickness=slice_thickness,\n            )\n        else:\n            stack._N_slices = 0\n            stack._slices = None\n\n        # Store current affine transform of image\n        stack._affine_transform_sitk = sitkh.get_sitk_affine_transform_from_sitk_image(\n            stack.sitk)\n\n        stack._history_affine_transforms = []\n        stack._history_affine_transforms.append(stack._affine_transform_sitk)\n\n        stack._history_motion_corrections = []\n        stack._history_motion_corrections.append(sitk.Euler3DTransform())\n\n        return stack\n\n    ##\n    # Copy constructor\n    # \\date       2019-01-15 16:55:09+0000\n    #\n    # \\param      cls            The cls\n    # \\param      stack_to_copy  Stack object to be copied\n    # \\param      filename       The filename\n    #\n    # \\return     copied Stack object TODO: That's not really well done\n    #\n    @classmethod\n    def from_stack(cls, stack_to_copy, filename=None):\n        stack = cls()\n\n        if not isinstance(stack_to_copy, Stack):\n            raise ValueError(\"Input must be of type Stack. Given: %s\" %\n                             type(stack_to_copy))\n\n        # Copy image stack and mask\n        stack.sitk = sitk.Image(stack_to_copy.sitk)\n        stack.itk = sitkh.get_itk_from_sitk_image(stack.sitk)\n\n        stack._slice_thickness = stack_to_copy.get_slice_thickness()\n\n        stack.sitk_mask = sitk.Image(stack_to_copy.sitk_mask)\n        stack.itk_mask = sitkh.get_itk_from_sitk_image(stack.sitk_mask)\n        stack._is_unity_mask = stack_to_copy.is_unity_mask()\n\n        if filename is None:\n            stack._filename = stack_to_copy.get_filename()\n        else:\n            stack._filename = filename\n        stack._dir = stack_to_copy.get_directory()\n        stack._deleted_slices = stack_to_copy.get_deleted_slice_numbers()\n\n        # Store current affine transform of image\n        stack.set_registration_history(\n            stack_to_copy.get_registration_history())\n\n        # Extract all slices and their masks from the stack and store them if\n        # given\n        if stack_to_copy.get_slices() is not None:\n            stack._N_slices = stack_to_copy.get_number_of_slices()\n            stack._slices = [None] * stack._N_slices\n            slices_to_copy = stack_to_copy.get_slices()\n\n            for j, slice_j in enumerate(slices_to_copy):\n                stack._slices[j] = sl.Slice.from_slice(slice_j)\n        else:\n            stack._N_slices = 0\n            stack._slices = None\n\n        return stack\n\n    # @classmethod\n    # def from_Stack(cls, class_instance):\n    #     data = copy.deepcopy(class_instance) # if deepcopy is necessary\n    #     return cls(data)\n\n    # def __deepcopy__(self, memo):\n    #     print '__deepcopy__(%s)' % str(memo)\n    #     return Stack(copy.deepcopy(memo))\n\n    # def copy(self):\n        # return copy.deepcopy(self)\n\n    # Get all slices of current stack\n    #  \\return Array of sitk.Images containing slices in 3D space\n    def get_slices(self):\n        if self._slices is None:\n            return None\n        else:\n            return [s for s in self._slices if s is not None]\n\n    ##\n    # Get one particular slice of current stack\n    # \\date       2018-04-18 22:06:38-0600\n    #\n    # \\param      self   The object\n    # \\param      index  slice index as integer\n    #\n    # \\return     requested 3D slice of stack as Slice object\n    #\n    def get_slice(self, index):\n\n        index = int(index)\n        if abs(index) > self._N_slices - 1:\n            raise ValueError(\n                \"Enter a valid index between -%s and %s. Tried: %s\" %\n                (self._N_slices - 1, self._N_slices - 1, index))\n\n        return self._slices[index]\n\n    def get_slice_thickness(self):\n        return float(self._slice_thickness)\n\n    def get_inplane_resolution(self):\n        return float(self.sitk.GetSpacing()[0])\n\n    ##\n    # Gets the deleted slice numbers, i.e. misregistered slice numbers detected\n    # by robust outlier algorithm. Indices refer to slice numbers within\n    # original stack\n    # \\date       2018-07-08 23:06:24-0600\n    #\n    # \\param      self  The object\n    #\n    # \\return     The deleted slice numbers as list of integers.\n    #\n    def get_deleted_slice_numbers(self):\n        return list(self._deleted_slices)\n\n    ##\n    # Sets the slice.\n    # \\date       2018-04-18 22:05:28-0600\n    #\n    # \\param      self   The object\n    # \\param      slice  slice as Slice object\n    # \\param      index  slice index as integer\n    #\n    def set_slice(self, slice, index):\n        if not isinstance(slice, sl.Slice):\n            raise IOError(\"Input must be of type Slice\")\n\n        index = int(index)\n        if abs(index) > self._N_slices - 1:\n            raise ValueError(\n                \"Enter a valid index between -%s and %s. Tried: %s\" %\n                (self._N_slices - 1, self._N_slices - 1, index))\n\n        self._slices[index] = slice\n\n    ##\n    # Delete slice at given index\n    # \\date       2017-12-01 00:38:56+0000\n    #\n    # Note that index refers to list index of slices (0 ... N_slices_current) whereas\n    # \"deleted slice index\" refers to actual slice number within original stack\n    #\n    # \\param      self   The object\n    # \\param      index  slice as Slice object to be deleted\n    #\n    def delete_slice(self, slice):\n        if not isinstance(slice, sl.Slice):\n            raise IOError(\"Input must be of type Slice\")\n\n        # keep slice number (w.r.t. original stack)\n        self._deleted_slices.append(int(slice.get_slice_number()))\n        self._deleted_slices = sorted((self._deleted_slices))\n\n        # delete slice\n        index = self._slices.index(slice)\n        self._slices[index] = None\n\n    def get_deleted_slice_numbers(self):\n        return list(self._deleted_slices)\n\n    # Get name of directory where nifti was read from\n    #  \\return string of directory wher nifti was read from\n    #  \\bug Does not exist for all created instances! E.g. Stack.from_sitk_image\n    def get_directory(self):\n        return self._dir\n\n    def set_filename(self, filename):\n        self._filename = filename\n\n        slices = self.get_slices()\n        if slices is not None:\n            for s in slices:\n                s.set_filename(filename)\n\n    # Get filename of read/assigned nifti file (Stack.from_filename vs Stack.from_sitk_image)\n    #  \\return string of filename\n    def get_filename(self):\n        return self._filename\n\n    # Get history history of affine transforms, i.e. encoded spatial\n    #  position+orientation of slice, and rigid motion estimates of slice\n    #  obtained in the course of the registration/reconstruction process\n    #  \\return list of sitk.AffineTransform and sitk.Euler3DTransform objects\n    def get_registration_history(self):\n        affine_transforms = list(self._history_affine_transforms)\n        motion_corrections = list(self._history_motion_corrections)\n        return affine_transforms, motion_corrections\n\n    def set_registration_history(self, registration_history):\n        affine_transform_sitk = registration_history[0][-1]\n        self._update_affine_transform(affine_transform_sitk)\n\n        self._history_affine_transforms = [a for a in registration_history[0]]\n        self._history_motion_corrections = [t for t in registration_history[1]]\n\n    # Get number of slices of stack\n    #  \\return number of slices of stack\n    def get_number_of_slices(self):\n        return len(self.get_slices())\n\n    def is_unity_mask(self):\n        return self._is_unity_mask\n\n    # Display stack with external viewer (ITK-Snap)\n    #  \\param[in][in] show_segmentation display stack with or without associated segmentation (default=0)\n    def show(self, show_segmentation=0, label=None, viewer=VIEWER, verbose=True):\n\n        if label is None:\n            label = self._filename\n\n        if show_segmentation:\n            sitk_mask = self.sitk_mask\n        else:\n            sitk_mask = None\n\n        sitkh.show_sitk_image(\n            self.sitk,\n            label=label,\n            segmentation=sitk_mask,\n            viewer=viewer,\n            verbose=verbose)\n\n    def show_slices(self):\n        sitkh.plot_stack_of_slices(\n            self.sitk, cmap=\"Greys_r\", title=self.get_filename())\n\n    # Write information of Stack to HDD to given directory:\n    #  - sitk.Image object as entire volume\n    #  - each single slice with its associated spatial transformation (optional)\n    #  \\param[in] directory string specifying where the output will be written to (default=\"/tmp/\")\n    #  \\param[in] filename string specifying the filename. If not given the assigned one within Stack will be chosen.\n    #  \\param[in] write_slices boolean indicating whether each Slice of the stack shall be written (default=False)\n    def write(self,\n              directory,\n              filename=None,\n              write_stack=True,\n              write_mask=False,\n              write_slices=False,\n              write_transforms=False,\n              suffix_mask=\"_mask\",\n              write_transforms_history=False,\n              ):\n\n        # Create directory if not existing\n        ph.create_directory(directory)\n\n        # Construct filename\n        if filename is None:\n            filename = self._filename\n\n        full_file_name = os.path.join(directory, filename)\n\n        # Write file to specified location\n        if write_stack:\n            dw.DataWriter.write_image(self.sitk, \"%s.nii.gz\" % full_file_name)\n\n        # Write mask to specified location if given\n        if self.sitk_mask is not None:\n            # nda = sitk.GetArrayFromImage(self.sitk_mask)\n\n            # Write mask if it does not consist of only ones\n            if not self._is_unity_mask and write_mask:\n                dw.DataWriter.write_mask(\n                    self.sitk_mask, \"%s%s.nii.gz\" % (full_file_name, suffix_mask))\n\n        if write_transforms:\n            stack_transform_sitk = self._history_motion_corrections[-1]\n            sitk.WriteTransform(\n                stack_transform_sitk,\n                os.path.join(directory, self.get_filename() + \".tfm\")\n            )\n\n        # Write each separate Slice of stack (if they exist)\n        if write_slices or write_transforms:\n            try:\n                # Check whether variable exists\n                # if 'self._slices' not in locals() or all(i is None for i in\n                # self._slices):\n                if not hasattr(self, '_slices'):\n                    raise ValueError(\n                        \"Error occurred in attempt to write %s.nii.gz: \"\n                        \"No separate slices of object Slice are found\" %\n                        full_file_name)\n\n                # Write slices\n                else:\n                    if write_transforms and write_slices:\n                        ph.print_info(\n                            \"Write %s image slices and slice transforms to %s ... \" % (\n                                self.get_filename(), directory),\n                            newline=False)\n                    elif write_transforms and not write_slices:\n                        ph.print_info(\n                            \"Write %s slice transforms to %s ... \" % (\n                                self.get_filename(), directory),\n                            newline=False)\n                    else:\n                        ph.print_info(\n                            \"Write %s image slices to %s ... \" % (\n                                self.get_filename(), directory),\n                            newline=False)\n                    for slice in self.get_slices():\n                        slice.write(\n                            directory=directory,\n                            filename=filename,\n                            write_transform=write_transforms,\n                            write_slice=write_slices,\n                            suffix_mask=suffix_mask,\n                            write_transforms_history=write_transforms_history,\n                        )\n                    print(\"done\")\n\n            except ValueError as err:\n                print(err.message)\n\n    ##\n    #       Apply transform on stack and all its slices\n    # \\date       2016-11-05 19:15:57+0000\n    #\n    # \\param      self                   The object\n    # \\param      affine_transform_sitk  The affine transform sitk\n    #\n    def update_motion_correction(self, affine_transform_sitk):\n\n        # Update rigid motion estimate\n        current_rigid_motion_estimate = sitkh.get_composite_sitk_affine_transform(\n            affine_transform_sitk, self._history_motion_corrections[-1])\n        self._history_motion_corrections.append(current_rigid_motion_estimate)\n\n        # New affine transform of slice after rigid motion correction\n        affine_transform = sitkh.get_composite_sitk_affine_transform(\n            affine_transform_sitk, self._affine_transform_sitk)\n\n        # Update affine transform of stack, i.e. change image origin and\n        # direction in physical space\n        self._update_affine_transform(affine_transform)\n\n        # Update slices\n        if self.get_slices() is not None:\n            for i in range(0, self._N_slices):\n                self._slices[i].update_motion_correction(affine_transform_sitk)\n\n    ##\n    #       Apply transforms on all the slices of the stack. Stack itself\n    #             is not getting transformed\n    # \\date       2016-11-05 19:16:33+0000\n    #\n    # \\param      self                    The object\n    # \\param      affine_transforms_sitk  List of sitk transform instances\n    #\n    def update_motion_correction_of_slices(self, affine_transforms_sitk):\n        if [type(affine_transforms_sitk) is list or type(affine_transforms_sitk) is np.array] \\\n                and len(affine_transforms_sitk) is self._N_slices:\n            for i in range(0, self._N_slices):\n                self._slices[i].update_motion_correction(\n                    affine_transforms_sitk[i])\n\n        else:\n            raise ValueError(\"Number of affine transforms does not match the \"\n                             \"number of slices\")\n\n    def _update_affine_transform(self, affine_transform_sitk):\n\n        # Ensure correct object type\n        self._affine_transform_sitk = sitk.AffineTransform(\n            affine_transform_sitk)\n\n        # Append transform to registration history\n        self._history_affine_transforms.append(affine_transform_sitk)\n\n        # Get origin and direction of transformed 3D slice given the new\n        # spatial transform\n        origin = sitkh.get_sitk_image_origin_from_sitk_affine_transform(\n            affine_transform_sitk, self.sitk)\n        direction = sitkh.get_sitk_image_direction_from_sitk_affine_transform(\n            affine_transform_sitk, self.sitk)\n\n        # Update image objects\n        self.sitk.SetOrigin(origin)\n        self.sitk.SetDirection(direction)\n\n        self.sitk_mask.SetOrigin(origin)\n        self.sitk_mask.SetDirection(direction)\n\n        self.itk.SetOrigin(origin)\n        self.itk.SetDirection(sitkh.get_itk_from_sitk_direction(direction))\n\n        self.itk_mask.SetOrigin(origin)\n        self.itk_mask.SetDirection(\n            sitkh.get_itk_from_sitk_direction(direction))\n\n    ##\n    #       Gets the resampled stack from slices.\n    # \\date       2016-09-26 17:28:43+0100\n    #\n    # After slice-based registrations slice j does not correspond to the\n    # physical space of stack[:,:,j:j+1] anymore. With this method resample all\n    # containing slices to the physical space defined by the stack itself (or\n    # by a given resampling_pace). Overlapping slices get averaged.\n    #\n    # \\param      self              The object\n    # \\param      resampling_grid  Define the space to which the stack of\n    #                               slices shall be resampled; given as Stack\n    #                               object\n    # \\param      interpolator      The interpolator\n    #\n    # \\return     resampled stack based on current position of slices as Stack\n    #             object\n    #\n    def get_resampled_stack_from_slices(self, resampling_grid=None, interpolator=\"NearestNeighbor\", default_pixel_value=0.0, filename=None):\n\n        # Choose interpolator\n        try:\n            interpolator_str = interpolator\n            interpolator = eval(\"sitk.sitk\" + interpolator_str)\n        except:\n            raise ValueError(\"Error: interpolator is not known\")\n\n        # Use resampling grid defined by original volumetric image\n        if resampling_grid is None:\n            resampling_grid = Stack.from_sitk_image(\n                image_sitk=self.sitk,\n                slice_thickness=self.get_slice_thickness(),\n            )\n\n        else:\n            # Use resampling grid defined by first slice (which might be\n            # shifted already)\n            if resampling_grid in [\"on_first_slice\"]:\n                stack_sitk = sitk.Image(self.sitk)\n                foo_sitk = sitk.Image(self._slices[0].sitk)\n                stack_sitk.SetDirection(foo_sitk.GetDirection())\n                stack_sitk.SetOrigin(foo_sitk.GetOrigin())\n                stack_sitk.SetSpacing(foo_sitk.GetSpacing())\n                resampling_grid = Stack.from_sitk_image(stack_sitk)\n\n            # Use resampling grid defined by given sitk.Image\n            elif type(resampling_grid) is sitk.Image:\n                resampling_grid = Stack.from_sitk_image(resampling_grid)\n\n        # Get shape of image data array\n        nda_shape = resampling_grid.sitk.GetSize()[::-1]\n\n        # Create zero image and its mask aligned with sitk.Image\n        nda = np.zeros(nda_shape)\n\n        stack_resampled_sitk = sitk.GetImageFromArray(nda)\n        stack_resampled_sitk.CopyInformation(resampling_grid.sitk)\n        stack_resampled_sitk = sitk.Cast(\n            stack_resampled_sitk, resampling_grid.sitk.GetPixelIDValue())\n\n        stack_resampled_sitk_mask = sitk.GetImageFromArray(nda.astype(\"uint8\"))\n        stack_resampled_sitk_mask.CopyInformation(resampling_grid.sitk_mask)\n        stack_resampled_sitk_mask = sitk.Cast(\n            stack_resampled_sitk_mask, resampling_grid.sitk_mask.GetPixelIDValue())\n\n        # Create helper used for normalization at the end\n        nda_stack_covered_indices = np.zeros(nda_shape)\n\n        for i in range(0, self._N_slices):\n            slice = self._slices[i]\n\n            # Resample slice and its mask to stack space (volume)\n            stack_resampled_slice_sitk = sitk.Resample(\n                slice.sitk,\n                resampling_grid.sitk,\n                sitk.Euler3DTransform(),\n                interpolator,\n                default_pixel_value,\n                resampling_grid.sitk.GetPixelIDValue())\n\n            stack_resampled_slice_sitk_mask = sitk.Resample(\n                slice.sitk_mask,\n                resampling_grid.sitk_mask,\n                sitk.Euler3DTransform(),\n                sitk.sitkNearestNeighbor,\n                0,\n                resampling_grid.sitk_mask.GetPixelIDValue())\n\n            # Add resampled slice and mask to stack space\n            stack_resampled_sitk += stack_resampled_slice_sitk\n            stack_resampled_sitk_mask += stack_resampled_slice_sitk_mask\n\n            # Get indices which are updated in stack space\n            nda_stack_resampled_slice_ind = sitk.GetArrayFromImage(\n                stack_resampled_slice_sitk)\n            ind = np.nonzero(nda_stack_resampled_slice_ind)\n\n            # Increment counter for respective updated voxels\n            nda_stack_covered_indices[ind] += 1\n\n        # Set voxels with zero counter to 1 so as to have well-defined\n        # normalization\n        nda_stack_covered_indices[nda_stack_covered_indices == 0] = 1\n\n        # Normalize resampled image\n        stack_normalization = sitk.GetImageFromArray(nda_stack_covered_indices)\n        stack_normalization.CopyInformation(resampling_grid.sitk)\n        stack_normalization = sitk.Cast(\n            stack_normalization, resampling_grid.sitk.GetPixelIDValue())\n        stack_resampled_sitk /= stack_normalization\n\n        # Get valid binary mask\n        stack_resampled_slice_sitk_mask /= stack_resampled_slice_sitk_mask\n\n        if filename is None:\n            filename = self._filename + \"_\" + interpolator_str\n\n        stack = self.from_sitk_image(\n            image_sitk=stack_resampled_sitk,\n            filename=filename,\n            image_sitk_mask=stack_resampled_sitk_mask,\n            slice_thickness=stack_resampled_sitk.GetSpacing()[-1],\n        )\n\n        return stack\n\n    ##\n    # Gets the resampled stack.\n    # \\date       2016-12-02 17:05:10+0000\n    #\n    # \\param      self                 The object\n    # \\param      resampling_grid      The resampling grid as SimpleITK image\n    # \\param      interpolator         The interpolator\n    # \\param      default_pixel_value  The default pixel value\n    #\n    # \\return     The resampled stack as Stack object\n    #\n    def get_resampled_stack(self, resampling_grid=None, spacing=None, interpolator=\"Linear\", default_pixel_value=0.0, filename=None):\n\n        if (resampling_grid is None and spacing is None) or \\\n                (resampling_grid is not None and spacing is not None):\n            raise IOError(\n                \"Either 'resampling_grid' or 'spacing' must be specified\")\n\n        # Get SimpleITK-interpolator\n        try:\n            interpolator_str = interpolator\n            interpolator = eval(\"sitk.sitk\" + interpolator_str)\n        except:\n            raise ValueError(\n                \"Error: interpolator is not known. \"\n                \"Must fit sitk.InterpolatorEnum format. \"\n                \"Possible examples include \"\n                \"'NearestNeighbor', 'Linear', or 'BSpline'.\")\n\n        if resampling_grid is not None:\n            resampled_stack_sitk = sitk.Resample(\n                self.sitk,\n                resampling_grid,\n                sitk.Euler3DTransform(),\n                interpolator,\n                default_pixel_value,\n                self.sitk.GetPixelIDValue())\n\n            resampled_stack_sitk_mask = sitk.Resample(\n                self.sitk_mask,\n                resampling_grid,\n                sitk.Euler3DTransform(),\n                sitk.sitkNearestNeighbor,\n                0,\n                self.sitk_mask.GetPixelIDValue())\n        else:\n            resampler = simplereg.resampler.Resampler\n            resampled_stack_sitk = resampler.get_resampled_image_sitk(\n                image_sitk=self.sitk,\n                spacing=spacing,\n                interpolator=interpolator,\n                padding=default_pixel_value,\n                add_to_grid_unit=\"mm\",\n            )\n            resampled_stack_sitk_mask = resampler.get_resampled_image_sitk(\n                image_sitk=self.sitk_mask,\n                spacing=spacing,\n                interpolator=sitk.sitkNearestNeighbor,\n                padding=0,\n                add_to_grid_unit=\"mm\",\n            )\n\n        # Create Stack instance\n        if filename is None:\n            filename = self._filename + \"_\" + interpolator_str\n        stack = self.from_sitk_image(\n            image_sitk=resampled_stack_sitk,\n            slice_thickness=resampled_stack_sitk.GetSpacing()[-1],\n            filename=filename,\n            image_sitk_mask=resampled_stack_sitk_mask,\n        )\n\n        return stack\n\n    ##\n    # Gets the stack multiplied with its mask. Rationale behind is to obtain\n    # \"cleaner\" looking HR images after the SRR step where motion-correction\n    # might have dispersed some slices\n    # \\date       2017-05-26 13:50:39+0100\n    #\n    # \\param      self      The object\n    # \\param      filename  The filename\n    #\n    # \\return     The stack multiplied with its mask.\n    #\n    def get_stack_multiplied_with_mask(self, filename=None, mask_sitk=None):\n\n        if mask_sitk is None:\n            mask_sitk = self.sitk_mask\n\n        # Multiply stack with its mask\n        image_sitk = self.sitk * \\\n            sitk.Cast(mask_sitk, self.sitk.GetPixelIDValue())\n\n        if filename is None:\n            filename = self.get_filename()\n\n        return Stack.from_sitk_image(\n            image_sitk=image_sitk,\n            filename=filename,\n            image_sitk_mask=mask_sitk,\n            slice_thickness=self.get_slice_thickness(),\n        )\n\n    # Get stack resampled on isotropic grid based on the actual position of\n    #  its slices\n    #  \\param[in] resolution length of voxel side, scalar\n    #  \\return isotropically, resampled stack as Stack object\n    def get_isotropically_resampled_stack_from_slices(self, resolution=None, interpolator=\"NearestNeighbor\", default_pixel_value=0.0, filename=None):\n        resampled_stack = self.get_resampled_stack_from_slices()\n\n        # Choose interpolator\n        try:\n            interpolator_str = interpolator\n            interpolator = eval(\"sitk.sitk\" + interpolator_str)\n        except:\n            raise ValueError(\"Error: interpolator is not known\")\n\n        # Read original spacing (voxel dimension) and size of target stack:\n        spacing = np.array(resampled_stack.sitk.GetSpacing())\n        size = np.array(resampled_stack.sitk.GetSize()).astype(\"int\")\n\n        if resolution is None:\n            size_new = size\n            spacing_new = spacing\n            # Update information according to isotropic resolution\n            size_new[2] = np.round(\n                spacing[2] / spacing[0] * size[2]).astype(\"int\")\n            spacing_new[2] = spacing[0]\n        else:\n            spacing_new = np.ones(3) * resolution\n            size_new = np.round(spacing / spacing_new * size).astype(\"int\")\n\n        # For Python3: sitk.Resample in Python3 does not like np.int types!\n        size_new = [int(i) for i in size_new]\n\n        # Resample image and its mask to isotropic grid\n        isotropic_resampled_stack_sitk = sitk.Resample(\n            resampled_stack.sitk,\n            size_new,\n            sitk.Euler3DTransform(),\n            interpolator,\n            resampled_stack.sitk.GetOrigin(),\n            spacing_new,\n            resampled_stack.sitk.GetDirection(),\n            default_pixel_value,\n            resampled_stack.sitk.GetPixelIDValue())\n\n        isotropic_resampled_stack_sitk_mask = sitk.Resample(\n            resampled_stack.sitk_mask,\n            size_new,\n            sitk.Euler3DTransform(),\n            sitk.sitkNearestNeighbor,\n            resampled_stack.sitk.GetOrigin(),\n            spacing_new,\n            resampled_stack.sitk.GetDirection(),\n            0,\n            resampled_stack.sitk_mask.GetPixelIDValue())\n\n        # Create Stack instance\n        if filename is None:\n            filename = self._filename + \"_\" + interpolator_str + \"Iso\"\n        stack = self.from_sitk_image(\n            isotropic_resampled_stack_sitk, filename, isotropic_resampled_stack_sitk_mask)\n\n        return stack\n\n    ##\n    # Gets the isotropically resampled stack.\n    # \\date       2017-02-03 16:34:24+0000\n    #\n    # \\param      self                  The object\n    # \\param      resolution    length of voxel side, scalar\n    # \\param      interpolator          choose type of interpolator for\n    #                                   resampling\n    # \\param      extra_frame           additional extra frame of zero\n    #                                   intensities surrounding the stack in mm\n    # \\param      filename              Filename of resampled stack\n    # \\param      mask_dilation_radius  The mask dilation radius\n    # \\param      mask_dilation_kernel  The kernel in \"Ball\", \"Box\", \"Annulus\"\n    #                                   or \"Cross\"\n    #\n    # \\return     The isotropically resampled stack.\n    #\n    def get_isotropically_resampled_stack(self, resolution=None, interpolator=\"Linear\", extra_frame=0, filename=None, mask_dilation_radius=0, mask_dilation_kernel=\"Ball\"):\n\n        # Choose interpolator\n        try:\n            interpolator_str = interpolator\n            interpolator = eval(\"sitk.sitk\" + interpolator_str)\n        except:\n            raise ValueError(\"Error: interpolator is not known\")\n\n        if resolution is None:\n            spacing = self.sitk.GetSpacing()[0]\n        else:\n            spacing = resolution\n\n        # Resample image and its mask to isotropic grid\n        resampler = simplereg.resampler.Resampler\n        isotropic_resampled_stack_sitk = resampler.get_resampled_image_sitk(\n            image_sitk=self.sitk,\n            spacing=spacing,\n            interpolator=interpolator,\n            padding=0.0,\n            add_to_grid=extra_frame,\n            add_to_grid_unit=\"mm\",\n        )\n        isotropic_resampled_stack_sitk_mask = resampler.get_resampled_image_sitk(\n            image_sitk=self.sitk_mask,\n            spacing=spacing,\n            interpolator=sitk.sitkNearestNeighbor,\n            padding=0,\n            add_to_grid=extra_frame,\n            add_to_grid_unit=\"mm\",\n        )\n\n        if mask_dilation_radius > 0:\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(eval(\"sitk.sitk\" + mask_dilation_kernel))\n            dilater.SetKernelRadius(mask_dilation_radius)\n            isotropic_resampled_stack_sitk_mask = dilater.Execute(\n                isotropic_resampled_stack_sitk_mask)\n\n        # Create Stack instance\n        if filename is None:\n            filename = self._filename + \"_\" + interpolator_str + \"Iso\"\n        stack = self.from_sitk_image(\n            image_sitk=isotropic_resampled_stack_sitk,\n            filename=filename,\n            slice_thickness=isotropic_resampled_stack_sitk.GetSpacing()[-1],\n            image_sitk_mask=isotropic_resampled_stack_sitk_mask,\n        )\n\n        return stack\n\n    # Increase stack by adding zero voxels in respective directions\n    #  \\remark Used for MS project to add empty slices on top of (chopped) brain\n    #  \\param[in] resolution length of voxel side, scalar\n    #  \\param[in] interpolator choose type of interpolator for resampling\n    #  \\param[in] extra_frame additional extra frame of zero intensities surrounding the stack in mm\n    #  \\return isotropically, resampled stack as Stack object\n    def get_increased_stack(self, extra_slices_z=0):\n\n        interpolator = sitk.sitkNearestNeighbor\n\n        # Read original spacing (voxel dimension) and size of target stack:\n        spacing = np.array(self.sitk.GetSpacing())\n        size = np.array(self.sitk.GetSize()).astype(\"int\")\n        origin = np.array(self.sitk.GetOrigin())\n        direction = self.sitk.GetDirection()\n\n        # Update information according to isotropic resolution\n        size[2] += extra_slices_z\n\n        # Resample image and its mask to isotropic grid\n        default_pixel_value = 0.0\n\n        isotropic_resampled_stack_sitk = sitk.Resample(\n            self.sitk,\n            size,\n            sitk.Euler3DTransform(),\n            interpolator,\n            origin,\n            spacing,\n            direction,\n            default_pixel_value,\n            self.sitk.GetPixelIDValue())\n\n        isotropic_resampled_stack_sitk_mask = sitk.Resample(\n            self.sitk_mask,\n            size,\n            sitk.Euler3DTransform(),\n            sitk.sitkNearestNeighbor,\n            origin,\n            spacing,\n            direction,\n            0,\n            self.sitk_mask.GetPixelIDValue())\n\n        # Create Stack instance\n        stack = self.from_sitk_image(\n            isotropic_resampled_stack_sitk, \"zincreased_\" + self._filename, isotropic_resampled_stack_sitk_mask)\n\n        return stack\n\n    def get_cropped_stack_based_on_mask(self, boundary_i=0, boundary_j=0, boundary_k=0, unit=\"mm\"):\n\n        # Get rectangular region surrounding the masked voxels\n        [x_range, y_range, z_range] = self._get_rectangular_masked_region(\n            self.sitk_mask)\n\n        if np.array([x_range, y_range, z_range]).all() is None:\n            raise RuntimeError(\n                \"Cropping to bounding box of mask led to an empty image. \"\n                \"Check the image stack to see whether the region of interest \"\n                \"is presented in '%s'.\" % self._filename)\n\n        if unit == \"mm\":\n            spacing = self.sitk.GetSpacing()\n            boundary_i = np.round(boundary_i / float(spacing[0]))\n            boundary_j = np.round(boundary_j / float(spacing[1]))\n            boundary_k = np.round(boundary_k / float(spacing[2]))\n\n        shape = self.sitk.GetSize()\n        x_range[0] = np.max([0, x_range[0] - boundary_i])\n        x_range[1] = np.min([shape[0], x_range[1] + boundary_i])\n\n        y_range[0] = np.max([0, y_range[0] - boundary_j])\n        y_range[1] = np.min([shape[1], y_range[1] + boundary_j])\n\n        z_range[0] = np.max([0, z_range[0] - boundary_k])\n        z_range[1] = np.min([shape[2], z_range[1] + boundary_k])\n\n        # Crop to image region defined by rectangular mask\n        image_crop_sitk = self._crop_image_to_region(\n            self.sitk, x_range, y_range, z_range)\n        mask_crop_sitk = self._crop_image_to_region(\n            self.sitk_mask, x_range, y_range, z_range)\n\n        slice_numbers = range(z_range[0], z_range[1])\n        stack = self.from_sitk_image(\n            image_sitk=image_crop_sitk,\n            slice_thickness=self.get_slice_thickness(),\n            filename=self._filename,\n            image_sitk_mask=mask_crop_sitk,\n            slice_numbers=slice_numbers)\n\n        return stack\n\n    # Return rectangular region surrounding masked region.\n    #  \\param[in] mask_sitk sitk.Image representing the mask\n    #  \\return range_x pair defining x interval of mask in voxel space\n    #  \\return range_y pair defining y interval of mask in voxel space\n    #  \\return range_z pair defining z interval of mask in voxel space\n    def _get_rectangular_masked_region(self, mask_sitk):\n\n        spacing = np.array(mask_sitk.GetSpacing())\n\n        # Get mask array\n        nda = sitk.GetArrayFromImage(mask_sitk)\n\n        # Return in case no masked pixel available\n        if np.sum(abs(nda)) == 0:\n            return None, None, None\n\n        # Get shape defining the dimension in each direction\n        shape = nda.shape\n\n        # Compute sum of pixels of each slice along specified directions\n        sum_xy = np.sum(nda, axis=(0, 1))  # sum within x-y-plane\n        sum_xz = np.sum(nda, axis=(0, 2))  # sum within x-z-plane\n        sum_yz = np.sum(nda, axis=(1, 2))  # sum within y-z-plane\n\n        # Find masked regions (non-zero sum!)\n        range_x = np.zeros(2)\n        range_y = np.zeros(2)\n        range_z = np.zeros(2)\n\n        # Non-zero elements of numpy array nda defining x_range\n        ran = np.nonzero(sum_yz)[0]\n        range_x[0] = np.max([0,         ran[0]])\n        range_x[1] = np.min([shape[0], ran[-1] + 1])\n\n        # Non-zero elements of numpy array nda defining y_range\n        ran = np.nonzero(sum_xz)[0]\n        range_y[0] = np.max([0,         ran[0]])\n        range_y[1] = np.min([shape[1], ran[-1] + 1])\n\n        # Non-zero elements of numpy array nda defining z_range\n        ran = np.nonzero(sum_xy)[0]\n        range_z[0] = np.max([0,         ran[0]])\n        range_z[1] = np.min([shape[2], ran[-1] + 1])\n\n        # Numpy reads the array as z,y,x coordinates! So swap them accordingly\n        return range_z.astype(int), range_y.astype(int), range_x.astype(int)\n\n    # Crop given image to region defined by voxel space ranges\n    #  \\param[in] image_sitk image which will be cropped\n    #  \\param[in] range_x pair defining x interval in voxel space for image cropping\n    #  \\param[in] range_y pair defining y interval in voxel space for image cropping\n    #  \\param[in] range_z pair defining z interval in voxel space for image cropping\n    #  \\return image cropped to defined region\n    def _crop_image_to_region(self, image_sitk, range_x, range_y, range_z):\n\n        image_cropped_sitk = image_sitk[\n            range_x[0]:range_x[1],\n            range_y[0]:range_y[1],\n            range_z[0]:range_z[1]\n        ]\n\n        return image_cropped_sitk\n\n    # Burst the stack into its slices and return all slices of the stack\n    #  return list of Slice objects\n    def _extract_slices(self, slice_thickness, slice_numbers=None):\n\n        slices = [None] * self._N_slices\n\n        if slice_numbers is None:\n            slice_numbers = range(0, self._N_slices)\n\n        if len(slice_numbers) != self._N_slices:\n            raise ValueError(\n                \"slice_numbers must correspond to the number of slices \"\n                \"of the image volume\")\n\n        # Extract slices and add masks\n        for i in range(0, self._N_slices):\n            slices[i] = sl.Slice.from_sitk_image(\n                slice_sitk=self.sitk[:, :, i:i + 1],\n                filename=self._filename,\n                slice_number=slice_numbers[i],\n                slice_sitk_mask=self.sitk_mask[:, :, i:i + 1],\n                slice_thickness=slice_thickness,\n            )\n\n        return slices\n\n    # Create a binary mask consisting of ones\n    #  \\return binary_mask as sitk.Image object consisting of ones\n    def _generate_identity_mask(self):\n        shape = sitk.GetArrayFromImage(self.sitk).shape\n        nda = np.ones(shape, dtype=np.uint8)\n\n        binary_mask = sitk.GetImageFromArray(nda)\n        binary_mask.CopyInformation(self.sitk)\n\n        return binary_mask\n"
  },
  {
    "path": "niftymic/definitions.py",
    "content": "import os\nimport sys\n\nfrom pysitk.definitions import DIR_TMP\nfrom pysitk.definitions import ITKSNAP_EXE\nfrom pysitk.definitions import FSLVIEW_EXE\nfrom pysitk.definitions import NIFTYVIEW_EXE\n\nDIR_ROOT = os.path.realpath(\n    os.path.join(os.path.dirname(os.path.abspath(__file__)), \"..\"))\nDIR_TEST = os.path.join(DIR_ROOT, \"data\", \"tests\")\nDIR_TEMPLATES = os.path.join(DIR_ROOT, \"data\", \"templates\")\nDIR_CPP_BUILD = os.path.join(DIR_ROOT, \"build\", \"cpp\")\n\nALLOWED_EXTENSIONS = [\"nii.gz\", \"nii\"]\nREGEX_FILENAMES = \"[A-Za-z0-9+-_]+\"\nREGEX_FILENAME_EXTENSIONS = \"(\" + \"|\".join(ALLOWED_EXTENSIONS) + \")\"\nALLOWED_INTERPOLATORS = [\"NearestNeighbor\", \"Linear\", \"BSpline\"]\n\nTEMPLATES_INFO = \"templates_info.json\"\n\n# Set default viewer\nVIEWER = ITKSNAP_EXE\nVIEWER_OPTIONS = [\"itksnap\", \"fsleyes\"]\nV2V_METHOD_OPTIONS = [\"FLIRT\", \"RegAladin\"]\n"
  },
  {
    "path": "niftymic/reconstruction/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/reconstruction/admm_solver.py",
    "content": "##\n# \\file admm_solver.py\n# \\brief      Implementation to get an approximate solution of the TVL2-SRR\n#             problem via the Alternating Direction Method of Multipliers (ADMM)\n#             method.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2016\n#\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\n\nimport nsol.admm_linear_solver as admm\nimport nsol.linear_operators as linop\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.reconstruction.solver import Solver\n\n\n# This class implements the framework to iteratively solve\n#  \\f$ \\vec{y}_k = A_k \\vec{x} \\f$ for every slice \\f$ \\vec{y}_k,\\,k=1,\\dots,K \\f$\n#  via TV-L2-regularization via an augmented least-square approach\n#  TODO\nclass ADMMSolver(Solver):\n\n    ##\n    # Constructor\n    # \\date          2016-08-01 22:57:21+0100\n    #\n    # \\param         self                   The object\n    # \\param[in]     stacks                 list of Stack objects containing\n    #                                       all stacks used for the\n    #                                       reconstruction\n    # \\param[in,out] reconstruction         Stack object containing the current\n    #                                       estimate of the reconstruction\n    #                                       volume (used as initial value +\n    #                                       space definition)\n    # \\param[in]     alpha_cut              Cut-off distance for Gaussian\n    #                                       blurring filter\n    # \\param[in]     alpha                  regularization parameter, scalar\n    # \\param[in]     iter_max               number of maximum iterations,\n    #                                       scalar\n    # \\param         minimizer              The minimizer\n    # \\param         deconvolution_mode     The deconvolution mode\n    # \\param         predefined_covariance  The predefined covariance\n    # \\param[in]     rho                    regularization parameter of\n    #                                       augmented Lagrangian term, scalar\n    # \\param[in]     iterations             number of ADMM iterations, scalar\n    # \\param         verbose                The verbose\n    #\n    def __init__(self,\n                 stacks,\n                 reconstruction,\n                 alpha_cut=3,\n                 alpha=0.03,\n                 iter_max=10,\n                 minimizer=\"lsmr\",\n                 x_scale=\"max\",\n                 data_loss=\"linear\",\n                 data_loss_scale=1,\n                 huber_gamma=1.345,\n                 deconvolution_mode=\"full_3D\",\n                 predefined_covariance=None,\n                 rho=0.5,\n                 iterations=10,\n                 use_masks=1,\n                 verbose=1,\n                 ):\n\n        # Run constructor of superclass\n        Solver.__init__(self,\n                        stacks=stacks,\n                        reconstruction=reconstruction,\n                        alpha=alpha,\n                        alpha_cut=alpha_cut,\n                        iter_max=iter_max,\n                        minimizer=minimizer,\n                        x_scale=x_scale,\n                        data_loss=data_loss,\n                        data_loss_scale=data_loss_scale,\n                        huber_gamma=huber_gamma,\n                        deconvolution_mode=deconvolution_mode,\n                        predefined_covariance=predefined_covariance,\n                        use_masks=use_masks,\n                        verbose=verbose,\n                        )\n\n        # Settings for optimizer\n        self._rho = rho\n        self._iterations = iterations\n\n    # Set regularization parameter used for augmented Lagrangian in TV-L2 regularization\n    #  \\[$\n    #   \\sum_{k=1}^K \\frac{1}{2} \\Vert y_k - A_k x \\Vert_{\\ell^2}^2 + \\alpha\\,\\Psi(x)\n    #   + \\mu \\cdot (\\nabla x - v) + \\frac{\\rho}{2} \\Vert \\nabla x - v \\Vert_{\\ell^2}^2\n    #  \\]$\n    #  \\param[in] rho regularization parameter of augmented Lagrangian term, scalar\n    def set_rho(self, rho):\n        self._rho = rho\n\n    # Get regularization parameter used for augmented Lagrangian in TV-L2 regularization\n    #  \\return regularization parameter of augmented Lagrangian term, scalar\n    def get_rho(self):\n        return self._rho\n\n    # Set ADMM iterations to solve TV-L2 reconstruction problem\n    #  \\[$\n    #   \\sum_{k=1}^K \\frac{1}{2} \\Vert y_k - A_k x \\Vert_{\\ell^2}^2 + \\alpha\\,\\Psi(x)\n    #   + \\mu \\cdot (\\nabla x - v) + \\frac{\\rho}{2} \\Vert \\nabla x - v \\Vert_{\\ell^2}^2\n    #  \\]$\n    #  \\param[in] iterations number of ADMM iterations, scalar\n    def set_iterations(self, iterations):\n        self._iterations = iterations\n\n    # Get chosen value of ADMM iterations to solve TV-L2 reconstruction problem\n    #  \\return number of ADMM iterations, scalar\n    def get_iterations(self):\n        return self._iterations\n\n    ##\n    #       Gets the setting specific filename indicating the information\n    #             used for the reconstruction step\n    # \\date       2016-11-17 15:41:58+0000\n    #\n    # \\param      self    The object\n    # \\param      prefix  The prefix as string\n    #\n    # \\return     The setting specific filename as string.\n    #\n    def get_setting_specific_filename(self, prefix=\"SRR_\"):\n\n        # Build filename\n        filename = prefix\n        filename += \"stacks\" + str(len(self._stacks))\n        if self._alpha > 0:\n            filename += \"_ADMM\"\n            filename += \"_TV\"\n        filename += \"_\" + self._minimizer\n        filename += \"_alpha\" + str(self._alpha)\n        filename += \"_itermax\" + str(self._iter_max)\n        filename += \"_rho\" + str(self._rho)\n        filename += \"_ADMMiterations\" + str(self._iterations)\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n\n    def get_solver(self):\n\n        # Get operators\n        A = self.get_A()\n        A_adj = self.get_A_adj()\n        b = self.get_b()\n        x0 = self.get_x0()\n        x_scale = self.get_x_scale()\n\n        spacing = np.array(self._reconstruction.sitk.GetSpacing())\n        linear_operators = linop.LinearOperators3D(spacing=spacing)\n        grad, grad_adj = linear_operators.get_gradient_operators()\n\n        X_shape = self._reconstruction_shape\n        Z_shape = grad(x0.reshape(*X_shape)).shape\n\n        B = lambda x: grad(x.reshape(*X_shape)).flatten()\n        B_adj = lambda x: grad_adj(x.reshape(*Z_shape)).flatten()\n\n        # Set up solver\n        solver = admm.ADMMLinearSolver(\n            dimension=3,\n            A=A, A_adj=A_adj,\n            B=B, B_adj=B_adj,\n            b=b,\n            x0=x0,\n            x_scale=x_scale,\n            alpha=self._alpha,\n            data_loss=self._data_loss,\n            minimizer=self._minimizer,\n            iter_max=self._iter_max,\n            rho=self._rho,\n            iterations=self._iterations,\n            verbose=self._verbose,\n        )\n\n        return solver\n\n    ##\n    #       Reconstruct volume using TV-L2 regularization via Alternating\n    #             Direction Method of Multipliers (ADMM) method.\n    # \\post       self._reconstruction is updated with new volume and can be fetched\n    #             via \\p get_recon\n    # \\date       2016-08-01 23:22:50+0100\n    #\n    # \\param      self                    The object\n    # \\param      estimate_initial_value  Estimate initial value by running one\n    #                                     first-order Tikhonov reconstruction\n    #                                     step prior the ADMM algorithm\n    #\n    def _run(self):\n\n        solver = self.get_solver()\n\n        self._print_info_text()\n\n        # Run reconstruction\n        solver.run()\n\n        # Get computational time\n        self._computational_time = solver.get_computational_time()\n\n        # Update volume\n        self._reconstruction.itk = self._get_itk_image_from_array_vec(\n            solver.get_x(), self._reconstruction.itk)\n        self._reconstruction.sitk = sitkh.get_sitk_from_itk_image(\n            self._reconstruction.itk)\n\n    def _print_info_text(self):\n        ph.print_subtitle(\"ADMM Solver:\")\n        ph.print_info(\"Chosen regularization type: TV\")\n        ph.print_info(\"Regularization parameter alpha: \" + str(self._alpha))\n        ph.print_info(\n            \"Regularization parameter of augmented Lagrangian term rho: \" + str(self._rho))\n        ph.print_info(\"Number of ADMM iterations: \" + str(self._iterations))\n        ph.print_info(\n            \"Maximum number of TK1 solver iterations: \" + str(self._iter_max))\n        # ph.print_info(\"Tolerance: %.0e\" %(self._tolerance))\n"
  },
  {
    "path": "niftymic/reconstruction/linear_operators.py",
    "content": "##\n# \\file linear_operators.py\n# \\brief      Implementation of linear operations associated with the physical\n#             slice acquisition model.\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date October 2017\n\n\n# Import libraries\nimport itk\nimport numpy as np\n\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.psf as psf\nimport niftymic.base.slice as sl\nimport niftymic.base.stack as st\n\n\n##\n# Class implementing linear operations associated with the physical slice\n# acquisition model\n# \\date       2017-11-28 22:25:27+0000\n#\nclass LinearOperators(object):\n\n    ##\n    # Store relevant information\n    # \\date       2017-11-01 16:29:41+0000\n    #\n    # \\param      self                   The object\n    # \\param      deconvolution_mode     Either \"full_3D\" or \"only_in_plane\".\n    #                                    Indicates whether full 3D or only\n    #                                    in-plane deconvolution is considered\n    # \\param      predefined_covariance  Either only diagonal entries\n    #                                    (sigma_x2, sigma_y2, sigma_z2) or as\n    #                                    full 3x3 numpy array\n    # \\param      alpha_cut              Cut-off distance for Gaussian blurring\n    #                                    filter\n    # \\param      image_type             itk.Image type\n    # \\param      default_pixel_type     The default pixel type for resampling\n    #\n    def __init__(self,\n                 deconvolution_mode=\"full_3D\",\n                 predefined_covariance=None,\n                 alpha_cut=3,\n                 image_type=itk.Image.D3,\n                 default_pixel_type=0.0,\n                 ):\n\n        self._deconvolution_mode = deconvolution_mode\n\n        # In case only diagonal entries are given, create diagonal matrix\n        if predefined_covariance is not None:\n            if predefined_covariance.size is 3:\n                self._predefined_covariance = np.diag(\n                    np.array(predefined_covariance))\n            else:\n                self._predefined_covariance = np.array(predefined_covariance)\n\n        self._psf = psf.PSF()\n\n        # Allocate and initialize Oriented Gaussian Interpolate Image Filter\n        self._filter_oriented_gaussian = \\\n            itk.OrientedGaussianInterpolateImageFilter[\n                image_type, image_type].New()\n        self._filter_oriented_gaussian.SetDefaultPixelValue(default_pixel_type)\n        self._filter_oriented_gaussian.SetAlpha(alpha_cut)\n\n        # Allocate and initialize Adjoint Oriented Gaussian Interpolate Image\n        # Filter\n        self._filter_adjoint_oriented_gaussian = \\\n            itk.AdjointOrientedGaussianInterpolateImageFilter[\n                image_type, image_type].New()\n        self._filter_adjoint_oriented_gaussian.SetDefaultPixelValue(\n            default_pixel_type)\n        self._filter_adjoint_oriented_gaussian.SetAlpha(alpha_cut)\n\n        # Allocate and initialize masking image filter\n        self._masking = itk.MultiplyImageFilter[\n            image_type, image_type, image_type].New()\n\n        self._get_covariance = {\n            \"full_3D\": self._get_covariance_full_3d,\n            \"only_in_plane\": self._get_covariance_only_in_plane,\n            \"predefined_covariance\": self._get_covariance_predefined,\n        }\n\n    ##\n    # Perform forward operation on reconstruction image, i.e.\n    # \\f$y = D B x =: A(x)\n    # \\f$ with\n    # \\f$ D\\f$ and\n    # \\f$ B\n    # \\f$ being the downsampling and blurring operators, respectively.\n    # \\date       2017-10-31 23:36:18+0000\n    #\n    # \\param      self                The object\n    # \\param      reconstruction_itk  Reconstruction image as itk.Image object\n    # \\param      slice_itk           Slice image as itk.Image object. Required\n    #                                 to define output space and orientation\n    #                                 for PSF.\n    # \\param      slice_spacing       Slice spacing as list/array that holds\n    #                                 [in-plane x, in-plane y, slice-thickness]\n    #                                 resolution information. Required to\n    #                                 estimate Gaussian blurring.\n    #\n    # \\return     Image A(x) as itk.Image object in slice_itk image space\n    #\n    def A_itk(self, reconstruction_itk, slice_itk, slice_spacing):\n\n        # Get covariance describing PSF orientation of slice in reconstruction\n        # space\n        cov = self._get_covariance[self._deconvolution_mode](\n            reconstruction_itk, slice_itk, slice_spacing)\n\n        reconstruction_itk.Update()\n        self._filter_oriented_gaussian.SetCovariance(cov.flatten())\n        self._filter_oriented_gaussian.SetInput(reconstruction_itk)\n        self._filter_oriented_gaussian.SetOutputParametersFromImage(slice_itk)\n        self._filter_oriented_gaussian.UpdateLargestPossibleRegion()\n        self._filter_oriented_gaussian.Update()\n\n        A_itk_reconstruction = self._filter_oriented_gaussian.GetOutput()\n        A_itk_reconstruction.DisconnectPipeline()\n\n        return A_itk_reconstruction\n\n    ##\n    # Perform forward operation using Stack/Slice objects.\n    #\n    # If reconstruction holds a (non-unity) mask, it is mapped to the\n    # Stack/Slice objects as well using standard interpolation techniques.\n    # \\date       2017-11-01 19:35:08+0000\n    #\n    # \\param      self               The object\n    # \\param      reconstruction     Reconstruction image as Stack object\n    # \\param      stack_slice        Slice image as Slice object. Required to\n    #                                define output space and orientation for\n    #                                PSF.\n    # \\param      interpolator_mask  Interpolator used for resampling\n    #                                reconstruction mask (if given) to\n    #                                Stack/Slice object space as string.\n    #                                Examples are \"NearestNeighbor\", or\n    #                                \"Linear\".\n    #\n    # \\return     Image A(x) as Slice object in slice image space\n    #\n    def A(self, reconstruction, stack_slice, interpolator_mask=\"Linear\"):\n\n        # Get slice spacing relevant for Gaussian blurring estimate\n        in_plane_res = stack_slice.get_inplane_resolution()\n        slice_thickness = stack_slice.get_slice_thickness()\n        slice_spacing = np.array([in_plane_res, in_plane_res, slice_thickness])\n\n        simulated_itk = self.A_itk(\n            reconstruction_itk=reconstruction.itk,\n            slice_itk=stack_slice.itk,\n            slice_spacing=slice_spacing,\n        )\n        simulated_sitk = sitkh.get_sitk_from_itk_image(simulated_itk)\n\n        # Update stack/slice mask, in case provided for reconstruction\n        if not reconstruction.is_unity_mask():\n            slice_tmp = reconstruction.get_resampled_stack(\n                stack_slice.sitk, interpolator=interpolator_mask)\n            simulated_sitk_mask = slice_tmp.sitk_mask\n\n            # PSF-aware resampling omitted as results less plausible for mask\n            # simulated_itk_mask = self.A_itk(\n            #     reconstruction.itk_mask, stack_slice.itk_mask)\n            # simulated_sitk_mask = sitkh.get_sitk_from_itk_image(\n            #     simulated_itk_mask)\n        else:\n            simulated_sitk_mask = None\n\n        if isinstance(stack_slice, sl.Slice):\n            simulated = sl.Slice.from_sitk_image(\n                slice_sitk=simulated_sitk,\n                slice_number=stack_slice.get_slice_number(),\n                filename=stack_slice.get_filename(),\n                slice_sitk_mask=simulated_sitk_mask,\n                slice_thickness=stack_slice.get_slice_thickness(),\n            )\n        elif isinstance(stack_slice, st.Stack):\n            simulated = st.Stack.from_sitk_image(\n                image_sitk=simulated_sitk,\n                image_sitk_mask=simulated_sitk_mask,\n                filename=stack_slice.get_filename(),\n                slice_thickness=stack_slice.get_slice_thickness(),\n            )\n\n        return simulated\n\n    ##\n    # Perform backward operation on slice image, i.e.\n    # \\f$z = B^* D^* y =: A^*(y)\n    # \\f$ with\n    # \\f$ D^*\n    # \\f$ and\n    # \\f$ B^*\n    # \\f$ being the adjoint downsampling and blurring operators, respectively.\n    # \\date       2017-10-31 23:44:41+0000\n    #\n    # \\param      self                The object\n    # \\param      slice_itk           Slice image as itk.Image object\n    # \\param      reconstruction_itk  Reconstruction image as itk.Image object.\n    #                                 Required to define output space and\n    #                                 orientation for PSF\n    # \\param      slice_spacing       Slice spacing as list/array that holds\n    #                                 [in-plane x, in-plane y, slice-thickness]\n    #                                 resolution information. Required to\n    #                                 estimate Gaussian blurring.\n    #\n    # \\return     Image A^*(y) as itk.Image object in reconstruction_itk image\n    #             space\n    #\n    def A_adj_itk(self, slice_itk, reconstruction_itk, slice_spacing):\n\n        # Get covariance describing PSF orientation of slice in reconstruction\n        # space\n        cov = self._get_covariance[self._deconvolution_mode](\n            reconstruction_itk, slice_itk, slice_spacing)\n\n        reconstruction_itk.Update()\n        self._filter_adjoint_oriented_gaussian.SetCovariance(cov.flatten())\n        self._filter_adjoint_oriented_gaussian.SetInput(slice_itk)\n        self._filter_adjoint_oriented_gaussian.SetOutputParametersFromImage(\n            reconstruction_itk)\n        self._filter_adjoint_oriented_gaussian.UpdateLargestPossibleRegion()\n        self._filter_adjoint_oriented_gaussian.Update()\n\n        A_adj_itk_slice = self._filter_adjoint_oriented_gaussian.GetOutput()\n        A_adj_itk_slice.DisconnectPipeline()\n\n        return A_adj_itk_slice\n\n    ##\n    # Perform masking operation on itk.Image object\n    # \\date       2017-10-31 23:59:00+0000\n    #\n    # \\param      self            The object\n    # \\param      image_itk       Image as itk.Image object\n    # \\param      image_itk_mask  Image mask as itk.Image object\n    #\n    # \\return     Masked image as itk.Image object\n    #\n    def M_itk(self, image_itk, image_itk_mask):\n\n        self._masking.SetInput1(image_itk_mask)\n        self._masking.SetInput2(image_itk)\n        self._masking.UpdateLargestPossibleRegion()\n        self._masking.Update()\n\n        Mk_slice_itk = self._masking.GetOutput()\n        Mk_slice_itk.DisconnectPipeline()\n\n        return Mk_slice_itk\n\n    def _get_covariance_full_3d(\n        self,\n        reconstruction_itk,\n        slice_itk,\n        slice_spacing,\n    ):\n\n        reconstruction_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            reconstruction_itk.GetDirection())\n        slice_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            slice_itk.GetDirection())\n\n        cov = self._psf.get_covariance_matrix_in_reconstruction_space_sitk(\n            reconstruction_direction_sitk=reconstruction_direction_sitk,\n            slice_direction_sitk=slice_direction_sitk,\n            slice_spacing=slice_spacing)\n\n        return cov\n\n    def _get_covariance_only_in_plane(\n        self,\n        reconstruction_itk,\n        slice_itk,\n        slice_spacing,\n    ):\n        reconstruction_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            reconstruction_itk.GetDirection())\n        slice_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            slice_itk.GetDirection())\n\n        # Get spacing of slice and set it very small so that the corresponding\n        # covariance is negligibly small in through-plane direction. Hence,\n        # only in-plane deconvolution is approximated\n        slice_spacing[2] = 1e-6\n\n        cov = self._psf.get_covariance_matrix_in_reconstruction_space_sitk(\n            reconstruction_direction_sitk=reconstruction_direction_sitk,\n            slice_direction_sitk=slice_direction_sitk,\n            slice_spacing=slice_spacing)\n\n        return cov\n\n    def _get_covariance_predefined(\n        self,\n        reconstruction_itk,\n        slice_itk,\n        slice_spacing=None,\n    ):\n        if slice_spacing is not None:\n            raise ValueError(\n                \"Slice spacing cannot be specified for predefined covariance \"\n                \"use.\")\n        reconstruction_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            reconstruction_itk.GetDirection())\n        slice_direction_sitk = sitkh.get_sitk_from_itk_direction(\n            slice_itk.GetDirection())\n\n        cov = \\\n            self._psf.get_predefined_covariance_matrix_in_reconstruction_space(\n                reconstruction_direction_sitk=reconstruction_direction_sitk,\n                slice_direction_sitk=slice_direction_sitk,\n                cov=self._predefined_covariance)\n\n        return cov\n"
  },
  {
    "path": "niftymic/reconstruction/primal_dual_solver.py",
    "content": "##\n# \\file primal_dual_solver.py\n# \\brief      Solve reconstruction problem A_k x = y_k for all slices k via\n#             Primal-Dual solver.\n#\n# Implementation to get an approximate solution of the inverse problem\n# \\f$ y_k = A_k x\n# \\f$ for each slice\n# \\f$ y_k,\\,k=1,\\dots,K\n# \\f$ by using first-order primal-dual algorithms for convex problems as\n# introduced in Chambolle, A. & Pock, T., 2011. A First-Order Primal-Dual\n# Algorithm for Convex Problems with Applications to Imaging. Journal of\n# Mathematical Imaging and Vision, 40(1), pp.120-145.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2017\n#\n\n# Import libraries\nimport numpy as np\n\nimport nsol.linear_operators as linop\nimport nsol.primal_dual_solver as pd\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.reconstruction.solver import Solver\nfrom nsol.proximal_operators import ProximalOperators as prox\n\n\n# This class implements the framework to iteratively solve\n#  \\f$ \\vec{y}_k = A_k \\vec{x} \\f$ for every slice \\f$ \\vec{y}_k,\\,k=1,\\dots,K \\f$\n#  via first-order primal dual algorithms.\n#  TODO\nclass PrimalDualSolver(Solver):\n\n    def __init__(self,\n                 stacks,\n                 reconstruction,\n                 alpha=0.03,\n                 alpha_cut=3,\n                 iter_max=10,\n                 minimizer=\"lsmr\",\n                 x_scale=\"max\",\n                 data_loss=\"linear\",\n                 data_loss_scale=1,\n                 huber_gamma=1.345,\n                 deconvolution_mode=\"full_3D\",\n                 predefined_covariance=None,\n                 reg_type=\"TV\",\n                 reg_huber_gamma=0.05,\n                 iterations=10,\n                 alg_type=\"ALG2\",\n                 use_masks=1,\n                 verbose=0,\n                 ):\n\n        super(self.__class__, self).__init__(\n            stacks=stacks,\n            reconstruction=reconstruction,\n            alpha=alpha,\n            alpha_cut=alpha_cut,\n            iter_max=iter_max,\n            minimizer=minimizer,\n            x_scale=x_scale,\n            data_loss=data_loss,\n            data_loss_scale=data_loss_scale,\n            huber_gamma=huber_gamma,\n            deconvolution_mode=deconvolution_mode,\n            predefined_covariance=predefined_covariance,\n            use_masks=use_masks,\n            verbose=verbose,\n        )\n\n        # regularization type\n        self._reg_type = reg_type\n\n        # number of primal-dual iterations\n        self._iterations = iterations\n\n        # parameter used for Huber regularizer\n        self._reg_huber_gamma = reg_huber_gamma\n\n        # define method to update parameter\n        self._alg_type = alg_type\n\n    def get_setting_specific_filename(self, prefix=\"SRR_\"):\n\n        # Build filename\n        filename = prefix\n        filename += \"stacks\" + str(len(self._stacks))\n        if self._alpha > 0:\n            filename += \"_PrimalDual\"\n            filename += \"_\" + self._reg_type\n            if self._reg_type == \"huber\":\n                filename += \"_gamma\" + str(self._reg_huber_gamma)\n            # filename += \"_\" + self._alg_type\n        filename += \"_\" + self._minimizer\n        if self._data_loss not in [\"linear\"] or self._minimizer in [\"L-BFGS-B\"]:\n            filename += \"_\" + self._data_loss\n            if self._data_loss in [\"huber\"]:\n                filename += str(self._huber_gamma)\n        filename += \"_alpha\" + str(self._alpha)\n        filename += \"_itermax\" + str(self._iter_max)\n        filename += \"_PDiterations\" + str(self._iterations)\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n\n    def get_solver(self):\n\n        if self._reg_type not in [\"TV\", \"huber\"]:\n            raise ValueError(\"Error: regularization type can only be either \"\n                             \"'TV' or 'huber'\")\n\n        # L^2 = ||K||^2 = ||\\nabla||^2 = ||div||^2 <= 16/h^2 in 3D\n        # However, it seems that the smaller L2 the bigger the effect of TV\n        # regularization. Try, e.g. L2 = 1.\n        L2 = 16. / self._reconstruction.sitk.GetSpacing()[0]**2\n\n        # Get operators\n        A = self.get_A()\n        A_adj = self.get_A_adj()\n        b = self.get_b()\n        x0 = self.get_x0()\n        x_scale = self.get_x_scale()\n\n        spacing = np.array(self._reconstruction.sitk.GetSpacing())\n        linear_operators = linop.LinearOperators3D(spacing=spacing)\n        grad, grad_adj = linear_operators.get_gradient_operators()\n\n        X_shape = self._reconstruction_shape\n        Z_shape = grad(x0.reshape(*X_shape)).shape\n\n        B = lambda x: grad(x.reshape(*X_shape)).flatten()\n        B_adj = lambda x: grad_adj(x.reshape(*Z_shape)).flatten()\n\n        prox_f = lambda x, tau: prox.prox_linear_least_squares(\n            x=x, tau=tau,\n            A=A, A_adj=A_adj,\n            b=b, x0=x0,\n            iter_max=self._iter_max,\n            x_scale=x_scale,\n            data_loss=self._data_loss,\n            data_loss_scale=self._data_loss_scale,\n            minimizer=self._minimizer,\n            verbose=self._verbose)\n\n        if self._reg_type == \"TV\":\n            prox_g_conj = prox.prox_tv_conj\n        elif self._reg_type == \"huber\":\n            prox_g_conj = lambda x, sigma: prox.prox_huber_conj(\n                x, sigma, self._reg_huber_gamma)\n\n        # Set up solver\n        solver = pd.PrimalDualSolver(\n            prox_f=prox_f,\n            prox_g_conj=prox_g_conj,\n            B=B,\n            B_conj=B_adj,\n            L2=L2,\n            x0=x0,\n            x_scale=x_scale,\n            alpha=self._alpha,\n            iterations=self._iterations,\n            verbose=self._verbose,\n            alg_type=self._alg_type,\n        )\n\n        return solver\n\n    def _run(self, verbose=0):\n\n        solver = self.get_solver()\n\n        self._print_info_text()\n\n        # Run reconstruction\n        solver.run()\n\n        # Get computational time\n        self._computational_time = solver.get_computational_time()\n\n        # Update volume\n        self._reconstruction.itk = self._get_itk_image_from_array_vec(\n            solver.get_x(), self._reconstruction.itk)\n        self._reconstruction.sitk = sitkh.get_sitk_from_itk_image(\n            self._reconstruction.itk)\n\n    def _print_info_text(self):\n        ph.print_subtitle(\"Primal-Dual Solver:\")\n        ph.print_info(\"Chosen regularization type: %s\" %\n                      (self._reg_type), newline=False)\n        if self._reg_type == \"huber\":\n            print(\" (gamma = %g)\" % (self._reg_huber_gamma))\n        else:\n            print(\"\")\n        ph.print_info(\"Strategy for parameter update: %s\"\n                      % (self._alg_type))\n        ph.print_info(\n            \"Regularization parameter alpha: %g\" % (self._alpha))\n        if self._data_loss in [\"huber\"]:\n            ph.print_info(\"Loss function: %s (gamma = %g)\" %\n                          (self._data_loss, self._huber_gamma))\n        else:\n            ph.print_info(\"Loss function: %s\" % (self._data_loss))\n        ph.print_info(\"Number of Primal-Dual iterations: %d\" %\n                      (self._iterations))\n        ph.print_info(\"Minimizer: %s\" % (self._minimizer))\n        ph.print_info(\n            \"Maximum number of iterations: %d\" % (self._iter_max))\n"
  },
  {
    "path": "niftymic/reconstruction/scattered_data_approximation.py",
    "content": "##\n# \\file ScatteredDataApproximation.py\n# \\brief      Implementation of two different approaches for Scattered Data\n#             Approximation (SDA)\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       April 2016\n#\n\n\nimport os\nimport sys\nimport itk\nimport time\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.binary_mask_from_mask_srr_estimator as bm\n\n\n# Class implementing Scattered Data Approximation\nclass ScatteredDataApproximation:\n\n    ##\n    # Constructor\n    # \\date          2017-07-12 15:48:33+0100\n    #\n    # \\param         self         The object\n    # \\param         stacks       list of Stack objects containing all stacks\n    #                             used for the reconstruction\n    # \\param[in,out] HR_volume    Stack object containing the current estimate\n    #                             of the HR volume (required for defining HR\n    #                             space)\n    # \\param         sigma        Sigma is measured in the units of image\n    #                             spacing\n    # \\param         sigma_array  Sigma is measured in the units of image\n    #                             spacing; set sigma_array if you need\n    #                             different values along each axis\n    # \\post          HR_volume is updated with current volumetric estimate\n    #\n    def __init__(self,\n                 stacks,\n                 HR_volume,\n                 sigma=1,\n                 sigma_array=None,\n                 use_masks=False,\n                 sda_mask=False,\n                 verbose=True,\n                 ):\n\n        # Initialize variables\n        self._stacks = stacks\n        self._N_stacks = len(stacks)\n        self._HR_volume = HR_volume\n        self._use_masks = use_masks\n        self._sda_mask = sda_mask\n        self._verbose = verbose\n\n        self._get_slice = {\n            # (use_mask, sda_mask)\n            (False, False): self._get_image_slice,\n            (True, False): self._get_masked_image_slice,\n            (False, True): self._get_mask_slice,\n            (True, True): self._get_mask_slice,\n        }\n\n        # Define sigma for recursive smoothing filter\n        if sigma_array is None:\n            self._sigma_array = np.ones(3) * sigma\n        elif len(sigma_array) is not 3:\n            raise ValueError(\"Error: Sigma array must contain 3 elements\")\n        else:\n            self._sigma_array = np.array(sigma_array)\n\n        # Define dictionary to choose computational approach for SDA\n        self._run = {\n            \"Shepard-YVV\":   self._run_discrete_shepard_reconstruction,\n            \"Shepard-Deriche\":   self._run_discrete_shepard_based_on_Deriche_reconstruction,\n        }\n        self._sda_approach = \"Shepard-YVV\"    # default approximation approach\n\n    # Set sigma used for recursive Gaussian smoothing. Same sigma is used\n    #  in each axis, i.e. isotropic smoothing is applied. Sigma is measured\n    #  in the units of image spacing.\n    #  \\param[in] sigma, scalar\n    def set_sigma(self, sigma):\n        self._sigma_array = np.ones(3) * sigma\n\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n        self._N_stacks = len(stacks)\n\n    # ## Get sigma used for recursive Gaussian smoothing.\n    # #  \\return sigma array, numpy array\n    # def get_sigma(self):\n    #     return self._sigma_array\n\n    # Set array of standard deviations used for recursive Gaussian smoothing\n    #  in each direction. Sigmas are measured in the units of image spacing.\n    #  You may use the method SetSigma to set the same value across each axis or\n    #  use the method SetSigmaArray if you need different values along each axis\n    #  \\param[in] sigma_array 3D array containing the standard deviation in each direction\n    def set_sigma_array(self, sigma_array):\n        if len(sigma_array) is not 3:\n            raise ValueError(\"Error: Sigma array must contain 3 elements\")\n\n        self._sigma_array = np.array(sigma_array)\n\n    # Get array of standard deviations used for recursive Gaussian smoothing\n    #  in each direction. Sigmas are measured in the units of image spacing.\n    #  \\return sigma array, numpy array\n    def get_sigma_array(self):\n        return self._sigma_array\n\n    # Set approach for approximating the HR volume. It can be either\n    #  'Shepard-YVV' or 'Shepard-Deriche'\n    #  \\param[in] sda_approach either 'Shepard-YVV' or 'Shepard-Deriche', string\n    def set_approach(self, sda_approach):\n        if sda_approach not in [\"Shepard-YVV\", \"Shepard-Deriche\"]:\n            raise ValueError(\n                \"Error: SDA approach can only be either 'Shepard-YVV' or 'Shepard-Deriche'\")\n\n        self._sda_approach = sda_approach\n\n    # Get chosen type of regularization.\n    #  \\return regularization type as string\n    def get_approach(self):\n        return self._sda_approach\n\n    # Get current estimate of HR volume\n    #  \\return current estimate of HR volume, instance of Stack\n    def get_reconstruction(self):\n        return self._HR_volume\n\n    def get_setting_specific_filename(self, prefix=\"SDA_\"):\n\n        # Build filename\n        filename = prefix\n        filename += \"stacks\" + str(len(self._stacks))\n\n        # Only prints the first entry, i.e. assumes identical sigmas\n        filename += \"_sigma\" + str(self._sigma_array[0])\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n\n    ##\n    # Gets the computational time it took to obtain the numerical estimate.\n    # \\date       2017-07-20 23:40:17+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The computational time as string\n    #\n    def get_computational_time(self):\n        return self._computational_time\n\n    # Computed reconstructed volume based on current estimated positions of\n    # slices\n    def run(self):\n        ph.print_info(\"Chosen SDA approach: \" + self._sda_approach)\n        ph.print_info(\"Smoothing parameter sigma = \" + str(self._sigma_array))\n\n        time_start = ph.start_timing()\n\n        self._run[self._sda_approach]()\n\n        # Get computational time\n        self._computational_time = ph.stop_timing(time_start)\n\n        if self._verbose:\n            ph.print_info(\"Required computational time: %s\" %\n                          (self.get_computational_time()))\n        # print(\"Elapsed time for SDA: %s seconds\" %(time_elapsed))\n\n    ##\n    # Add mask based on union of all masks\n    # \\date       2017-02-03 16:46:33+0000\n    #\n    # \\param      self                  The object\n    # \\param      mask_dilation_radius  The mask dilation radius\n    # \\param      mask_dilation_kernel  The kernel in \"Ball\", \"Box\", \"Annulus\"\n    #                                   or \"Cross\"\n    #\n    def generate_mask_from_stack_mask_unions(self,\n                                             mask_dilation_radius=0,\n                                             mask_dilation_kernel=\"Ball\",\n                                             ):\n\n        # Define helpers to obtain averaged stack\n        shape = sitk.GetArrayFromImage(self._HR_volume.sitk).shape\n        array_mask = np.zeros(shape, dtype=np.uint8)\n\n        # Average over domain specified by the joint mask (\"union mask\")\n        for i in range(0, self._N_stacks):\n\n            # Resample warped stack masks\n            stack_sitk_mask = sitk.Resample(\n                self._stacks[i].sitk_mask,\n                self._HR_volume.sitk_mask,\n                sitk.Euler3DTransform(),\n                sitk.sitkNearestNeighbor,\n                0,\n                self._HR_volume.sitk_mask.GetPixelIDValue())\n\n            # Get arrays of resampled warped stack and mask\n            array_mask_tmp = sitk.GetArrayFromImage(\n                stack_sitk_mask).astype(np.uint8)\n\n            # Sum intensities of stack and mask\n            array_mask += array_mask_tmp\n\n        # Create (joint) binary mask. Mask represents union of all masks\n        array_mask[array_mask > 0] = 1\n\n        HR_volume_mask_sitk = sitk.GetImageFromArray(array_mask)\n        HR_volume_mask_sitk.CopyInformation(self._HR_volume.sitk)\n\n        if mask_dilation_radius > 0:\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(eval(\"sitk.sitk\" + mask_dilation_kernel))\n            dilater.SetKernelRadius(mask_dilation_radius)\n            HR_volume_mask_sitk = dilater.Execute(HR_volume_mask_sitk)\n\n        self._HR_volume = st.Stack.from_sitk_image(\n            image_sitk=self._HR_volume.sitk,\n            filename=self._HR_volume.get_filename(),\n            image_sitk_mask=HR_volume_mask_sitk,\n            slice_thickness=self._HR_volume.get_slice_thickness(),\n        )\n\n    ##\n    # Add mask based on union of intersection of masks\n    # \\date       2017-02-03 16:46:33+0000\n    #\n    # \\param      self                  The object\n    # \\param      mask_dilation_radius  The mask dilation radius\n    # \\param      mask_dilation_kernel  The kernel in \"Ball\", \"Box\", \"Annulus\"\n    #                                   or \"Cross\"\n    def generate_mask_from_stack_mask_intersections(self,\n                                                    mask_dilation_radius=0,\n                                                    mask_dilation_kernel=\"Ball\",\n                                                    ):\n\n        # Define helpers to obtain averaged stack\n        shape = sitk.GetArrayFromImage(self._HR_volume.sitk).shape\n        array_mask = np.ones(shape, dtype=np.uint8)\n\n        # Average over domain specified by the joint mask (\"union mask\")\n        for i in range(0, self._N_stacks):\n\n            # Resample warped stack masks\n            stack_sitk_mask = sitk.Resample(\n                self._stacks[i].sitk_mask,\n                self._HR_volume.sitk_mask,\n                sitk.Euler3DTransform(),\n                sitk.sitkNearestNeighbor,\n                0,\n                self._HR_volume.sitk_mask.GetPixelIDValue())\n\n            # Get arrays of resampled warped stack and mask\n            array_mask_tmp = sitk.GetArrayFromImage(\n                stack_sitk_mask).astype(np.uint8)\n\n            # Sum intensities of stack and mask\n            array_mask *= array_mask_tmp\n\n        # Create (joint) binary mask. Mask represents union of all masks\n        array_mask[array_mask > 0] = 1\n\n        HR_volume_mask_sitk = sitk.GetImageFromArray(array_mask)\n        HR_volume_mask_sitk.CopyInformation(self._HR_volume.sitk)\n\n        if mask_dilation_radius > 0:\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(eval(\"sitk.sitk\" + mask_dilation_kernel))\n            dilater.SetKernelRadius(mask_dilation_radius)\n            HR_volume_mask_sitk = dilater.Execute(HR_volume_mask_sitk)\n\n        self._HR_volume = st.Stack.from_sitk_image(\n            image_sitk=self._HR_volume.sitk,\n            filename=self._HR_volume.get_filename(),\n            image_sitk_mask=HR_volume_mask_sitk,\n            slice_thickness=self._HR_volume.get_slice_thickness(),\n        )\n\n    @staticmethod\n    def _get_image_slice(slice):\n        return slice.sitk\n\n    @staticmethod\n    def _get_masked_image_slice(slice):\n        slice_sitk = slice.sitk * \\\n            sitk.Cast(slice.sitk_mask, slice.sitk.GetPixelIDValue())\n        return slice_sitk\n\n    @staticmethod\n    def _get_mask_slice(slice):\n        return slice.sitk_mask\n\n    # Recontruct volume based on discrete Shepard's like method, cf. Vercauteren2006, equation (19).\n    #  The computation here is based on the YVV variant of Recursive Gaussian Filter and executed\n    #  via ITK\n    #  \\remark Obtained intensity values are positive.\n    def _run_discrete_shepard_reconstruction(self):\n\n        shape = sitk.GetArrayFromImage(self._HR_volume.sitk).shape\n        helper_N_nda = np.zeros(shape)\n        helper_D_nda = np.zeros(shape)\n\n        default_pixel_value = 0.0\n\n        for i in range(0, self._N_stacks):\n            if self._verbose:\n                ph.print_info(\"Stack %s/%s\" % (i + 1, self._N_stacks))\n            stack = self._stacks[i]\n            slices = stack.get_slices()\n            N_slices = stack.get_number_of_slices()\n\n            # for j in range(10, 11):\n            for j in range(0, N_slices):\n                # print(\"\\t\\tSlice %s/%s\" %(j,N_slices-1))\n                slice = slices[j]\n                slice_sitk = self._get_slice[(\n                    bool(self._use_masks), bool(self._sda_mask))](slice)\n\n                # Add intensity offset so that a \"zero\" intensity can be\n                # identified as contribution of image slice (line 353/356)\n                slice_sitk += 1\n\n                # Nearest neighbour resampling of slice to target space (HR\n                # volume)\n                slice_resampled_sitk = sitk.Resample(\n                    slice_sitk,\n                    self._HR_volume.sitk,\n                    sitk.Euler3DTransform(),\n                    sitk.sitkNearestNeighbor,\n                    default_pixel_value,\n                    self._HR_volume.sitk.GetPixelIDValue())\n\n                # sitkh.show_sitk_image(slice_resampled_sitk)\n\n                # Extract array of pixel intensities\n                nda_slice = sitk.GetArrayFromImage(slice_resampled_sitk)\n\n                # Get voxels in HR volume space which are struck by the slice\n                ind_nonzero = nda_slice > 0\n\n                # update numerator (correct previous intensity offset)\n                helper_N_nda[ind_nonzero] += nda_slice[ind_nonzero] - 1\n\n                # update denominator\n                helper_D_nda[ind_nonzero] += 1\n\n                # test = sitk.GetImageFromArray(helper_N_nda)\n                # sitkh.show_sitk_image(test,title=\"N\")\n\n                # test = sitk.GetImageFromArray(helper_D_nda)\n                # sitkh.show_sitk_image(test,title=\"D\")\n\n                # print(\"helper_N_nda: (min, max) = (%s, %s)\" %(np.min(helper_N_nda), np.max(helper_N_nda)))\n                # print(\"helper_D_nda: (min, max) = (%s, %s)\" %(np.min(helper_D_nda), np.max(helper_D_nda)))\n\n        # TODO: Set zero entries to one; Otherwise results are very weird!?\n        helper_D_nda[helper_D_nda == 0] = 1\n\n        # Create itk-images with correct header data\n        pixel_type = itk.D\n        dimension = 3\n        image_type = itk.Image[pixel_type, dimension]\n\n        itk2np = itk.PyBuffer[image_type]\n        helper_N = itk2np.GetImageFromArray(helper_N_nda)\n        helper_D = itk2np.GetImageFromArray(helper_D_nda)\n\n        helper_N.SetSpacing(self._HR_volume.sitk.GetSpacing())\n        helper_N.SetDirection(\n            sitkh.get_itk_direction_from_sitk_image(self._HR_volume.sitk))\n        helper_N.SetOrigin(self._HR_volume.sitk.GetOrigin())\n\n        helper_D.SetSpacing(self._HR_volume.sitk.GetSpacing())\n        helper_D.SetDirection(\n            sitkh.get_itk_direction_from_sitk_image(self._HR_volume.sitk))\n        helper_D.SetOrigin(self._HR_volume.sitk.GetOrigin())\n\n        # Apply Recursive Gaussian YVV filter\n        gaussian = itk.SmoothingRecursiveYvvGaussianImageFilter[\n            image_type, image_type].New()   # YVV-based Filter\n        # gaussian = itk.SmoothingRecursiveGaussianImageFilter[image_type,\n        # image_type].New()    # Deriche-based Filter\n        gaussian.SetSigmaArray(self._sigma_array)\n        gaussian.SetInput(helper_N)\n        gaussian.Update()\n        HR_volume_update_N = gaussian.GetOutput()\n        HR_volume_update_N.DisconnectPipeline()\n\n        gaussian.SetInput(helper_D)\n        gaussian.Update()\n        HR_volume_update_D = gaussian.GetOutput()\n        HR_volume_update_D.DisconnectPipeline()\n\n        # Convert numerator and denominator back to data array\n        nda_N = itk2np.GetArrayFromImage(HR_volume_update_N)\n        nda_D = itk2np.GetArrayFromImage(HR_volume_update_D)\n\n        # Compute data array of HR volume:\n        # nda_D[nda_D==0]=1\n        nda = nda_N / nda_D.astype(float)\n\n        # Update HR volume image file within Stack-object HR_volume\n        HR_volume_update = sitk.GetImageFromArray(nda)\n        HR_volume_update.CopyInformation(self._HR_volume.sitk)\n\n        if not self._sda_mask:\n            self._HR_volume.sitk = HR_volume_update\n            self._HR_volume.itk = sitkh.get_itk_from_sitk_image(\n                HR_volume_update)\n        else:\n            # Approximate uint8 mask from float SDA outcome\n            mask_estimator = bm.BinaryMaskFromMaskSRREstimator(\n                HR_volume_update)\n            mask_estimator.run()\n            HR_volume_update = mask_estimator.get_mask_sitk()\n\n            self._HR_volume.sitk_mask = HR_volume_update\n            self._HR_volume.itk_mask = sitkh.get_itk_from_sitk_image(\n                HR_volume_update)\n\n    # Recontruct volume based on discrete Shepard's like method, cf. Vercauteren2006, equation (19).\n    #  The computation here is based on the Deriche variant of Recursive Gaussian Filter and executed\n    #  via SimpleITK.\n    #  \\remark Obtained intensity values can be negative.\n    def _run_discrete_shepard_based_on_Deriche_reconstruction(self):\n\n        shape = sitk.GetArrayFromImage(self._HR_volume.sitk).shape\n        helper_N_nda = np.zeros(shape)\n        helper_D_nda = np.zeros(shape)\n\n        default_pixel_value = 0.0\n\n        for i in range(0, self._N_stacks):\n            if self._verbose:\n                ph.print_info(\"Stack %s/%s\" % (i + 1, self._N_stacks))\n            stack = self._stacks[i]\n            slices = stack.get_slices()\n            N_slices = stack.get_number_of_slices()\n\n            for j in range(0, N_slices):\n\n                slice = slices[j]\n                slice_sitk = self._get_slice[(\n                    bool(self._use_masks), bool(self._sda_mask))](slice)\n\n                # Nearest neighbour resampling of slice to target space (HR\n                # volume)\n                slice_resampled_sitk = sitk.Resample(\n                    slice_sitk,\n                    self._HR_volume.sitk,\n                    sitk.Euler3DTransform(),\n                    sitk.sitkNearestNeighbor,\n                    default_pixel_value,\n                    self._HR_volume.sitk.GetPixelIDValue())\n\n                # Extract array of pixel intensities\n                nda_slice = sitk.GetArrayFromImage(slice_resampled_sitk)\n\n                # Look for indices which are stroke by the slice in the\n                # isotropic grid\n                ind_nonzero = nda_slice > 0\n\n                # update arrays of numerator and denominator\n                helper_N_nda[ind_nonzero] += nda_slice[ind_nonzero]\n                helper_D_nda[ind_nonzero] += 1\n\n                # print(\"helper_N_nda: (min, max) = (%s, %s)\" %(np.min(helper_N_nda), np.max(helper_N_nda)))\n                # print(\"helper_D_nda: (min, max) = (%s, %s)\" %(np.min(helper_D_nda), np.max(helper_D_nda)))\n\n        # TODO: Set zero entries to one; Otherwise results are very weird!?\n        helper_D_nda[helper_D_nda == 0] = 1\n\n        # Create sitk-images with correct header data\n        helper_N = sitk.GetImageFromArray(helper_N_nda)\n        helper_D = sitk.GetImageFromArray(helper_D_nda)\n\n        helper_N.CopyInformation(self._HR_volume.sitk)\n        helper_D.CopyInformation(self._HR_volume.sitk)\n\n        # Apply recursive Gaussian smoothing\n        gaussian = sitk.SmoothingRecursiveGaussianImageFilter()\n        gaussian.SetSigma(self._sigma_array[1])\n\n        HR_volume_update_N = gaussian.Execute(helper_N)\n        HR_volume_update_D = gaussian.Execute(helper_D)\n\n        # ## Avoid undefined division by zero\n        # \"\"\"\n        # HACK start\n        # \"\"\"\n        # ## HACK for denominator\n        # nda = sitk.GetArrayFromImage(HR_volume_update_D)\n        # ind_min = np.unravel_index(np.argmin(nda), nda.shape)\n        # # print(nda[nda<0])\n        # # print(nda[ind_min])\n\n        # eps = 1e-8\n        # # nda[nda<=eps]=1\n        # print(\"denominator min = %s\" % np.min(nda))\n\n        # HR_volume_update_D = sitk.GetImageFromArray(nda)\n        # HR_volume_update_D.CopyInformation(self._HR_volume.sitk)\n\n        # ## HACK for numerator given that some intensities are negative!?\n        # nda = sitk.GetArrayFromImage(HR_volume_update_N)\n        # ind_min = np.unravel_index(np.argmin(nda), nda.shape)\n        # # nda[nda<=eps]=0\n        # # print(nda[nda<0])\n        # print(\"numerator min = %s\" % np.min(nda))\n        # \"\"\"\n        # HACK end\n        # \"\"\"\n\n        # Compute HR volume based on scattered data approximation with correct\n        # header (might be redundant):\n        HR_volume_update = HR_volume_update_N / HR_volume_update_D\n        HR_volume_update.CopyInformation(self._HR_volume.sitk)\n\n        if not self._sda_mask:\n            self._HR_volume.sitk = HR_volume_update\n            self._HR_volume.itk = sitkh.get_itk_from_sitk_image(\n                HR_volume_update)\n        else:\n            # Approximate uint8 mask from float SDA outcome\n            mask_estimator = bm.BinaryMaskFromMaskSRREstimator(\n                HR_volume_update)\n            mask_estimator.run()\n            HR_volume_update = mask_estimator.get_mask_sitk()\n\n            self._HR_volume.sitk_mask = HR_volume_update\n            self._HR_volume.itk_mask = sitkh.get_itk_from_sitk_image(\n                HR_volume_update)\n\n        \"\"\"\n        Additional info\n        \"\"\"\n        if self._verbose:\n            nda = sitk.GetArrayFromImage(HR_volume_update)\n            print(\"Minimum of data array = %s\" % np.min(nda))\n"
  },
  {
    "path": "niftymic/reconstruction/solver.py",
    "content": "##\n# \\file solver.py\n# \\brief      Base class to solve the SRR problem y_k = D_k B_k W_k x = A_k x\n#             for all slices k = 1, ..., K.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2016\n#\n\n\n# Import libraries\nfrom abc import ABCMeta, abstractmethod\nimport sys\nimport itk\nimport SimpleITK as sitk\nimport numpy as np\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.reconstruction.linear_operators as lin_op\n\n# Allowed data loss functions\nDATA_LOSS = ['linear', 'soft_l1', 'huber', 'cauchy', 'arctan']\n\n\n##\n# This class contains the common functions/attributes of the solvers\n# \\date       2017-11-01 01:04:31+0000\n#\nclass Solver(object):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Constructor\n    # \\date          2016-08-01 22:53:37+0100\n    #\n    # \\param         self                   The object\n    # \\param         stacks                 list of Stack objects containing\n    #                                       all stacks used for the\n    #                                       reconstruction\n    # \\param[in,out] reconstruction         Stack object containing the current\n    #                                       estimate of the reconstruction\n    #                                       (used as initial value + space\n    #                                       definition)\n    # \\param         alpha_cut              Cut-off distance for Gaussian\n    #                                       blurring filter\n    # \\param         alpha                  regularization parameter, scalar\n    # \\param         iter_max               number of maximum iterations,\n    #                                       scalar\n    # \\param         minimizer              The minimizer\n    # \\param         deconvolution_mode     Either \"full_3D\" or\n    #                                       \"only_in_plane\". Indicates whether\n    #                                       full 3D or only in-plane\n    #                                       deconvolution is considered\n    # \\param         data_loss              The data loss\n    # \\param         huber_gamma            The huber gamma\n    # \\param         predefined_covariance  Either only diagonal entries\n    #                                       (sigma_x2, sigma_y2, sigma_z2) or\n    #                                       as full 3x3 numpy array\n    # \\param         verbose                The verbose\n    #\n    def __init__(self,\n                 stacks,\n                 reconstruction,\n                 alpha_cut,\n                 alpha,\n                 iter_max,\n                 minimizer,\n                 x_scale,\n                 data_loss,\n                 data_loss_scale,\n                 huber_gamma,\n                 deconvolution_mode,\n                 predefined_covariance,\n                 verbose,\n                 image_type=itk.Image.D3,\n                 use_masks=True,\n                 ):\n\n        # Initialize variables\n        self._stacks = stacks\n        self._reconstruction = reconstruction\n\n        # Cut-off distance for Gaussian blurring filter\n        self._alpha_cut = alpha_cut\n\n        self._deconvolution_mode = deconvolution_mode\n        self._predefined_covariance = predefined_covariance\n        self._linear_operators = lin_op.LinearOperators(\n            deconvolution_mode=self._deconvolution_mode,\n            predefined_covariance=self._predefined_covariance,\n            alpha_cut=self._alpha_cut,\n            image_type=image_type\n        )\n\n        # Settings for solver\n        self._alpha = alpha\n        self._iter_max = iter_max\n\n        self._use_masks = use_masks\n\n        self._minimizer = minimizer\n        self._data_loss = data_loss\n        self._data_loss_scale = data_loss_scale\n\n        if x_scale == \"max\":\n            self._x_scale = sitk.GetArrayFromImage(\n                reconstruction.sitk).max()\n\n            # Avoid zero in case zero-image is given\n            if self._x_scale == 0:\n                self._x_scale = 1\n        else:\n            self._x_scale = x_scale\n\n        self._huber_gamma = huber_gamma\n\n        self._verbose = verbose\n\n        # Allocate variables containing information about statistics of\n        # reconstruction\n        self._computational_time = None\n        self._residual_ell2 = None\n        self._residual_prior = None\n\n        # Create PyBuffer object for conversion between NumPy arrays and ITK\n        # images\n        self._itk2np = itk.PyBuffer[image_type]\n\n        # -----------------------------Set helpers-----------------------------\n        self._N_stacks = len(self._stacks)\n\n        # Compute total amount of pixels for all slices\n        self._N_total_slice_voxels = 0\n        for i in range(0, self._N_stacks):\n            N_stack_voxels = np.array(self._stacks[i].sitk.GetSize()).prod()\n            self._N_total_slice_voxels += N_stack_voxels\n\n        # Extract information ready to use for itk image conversion operations\n        self._reconstruction_shape = sitk.GetArrayFromImage(\n            self._reconstruction.sitk).shape\n\n        # Compute total amount of voxels of x:\n        self._N_voxels_recon = np.array(\n            self._reconstruction.sitk.GetSize()).prod()\n\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n\n        # Update helpers\n        self._N_stacks = len(self._stacks)\n\n        # Compute total amount of pixels for all slices\n        self._N_total_slice_voxels = 0\n        for i in range(0, self._N_stacks):\n            N_stack_voxels = np.array(self._stacks[i].sitk.GetSize()).prod()\n            self._N_total_slice_voxels += N_stack_voxels\n\n    ##\n    # Specify whether masks shall be used during reconstruction, i.e. whether\n    # masking operator is applied (and, thus, zeros everything outside it).\n    # \\date       2018-11-12 11:26:58+0000\n    #\n    # \\param      self       The object\n    # \\param      use_masks  boolean\n    #\n    def set_use_masks(self, use_masks):\n        self._use_masks = use_masks\n\n    def set_reconstruction(self, reconstruction):\n        self._reconstruction = reconstruction\n\n        # Extract information ready to use for itk image conversion operations\n        self._reconstruction_shape = sitk.GetArrayFromImage(\n            self._reconstruction.sitk).shape\n\n        # Compute total amount of voxels of x:\n        self._N_voxels_recon = np.array(\n            self._reconstruction.sitk.GetSize()).prod()\n\n    #\n    # Set regularization parameter for Tikhonov regularization\n    # \\date       2017-07-25 15:15:54+0100\n    #\n    # \\param      self   The object\n    # \\param      alpha  regularization parameter, scalar\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def set_alpha(self, alpha):\n        self._alpha = alpha\n\n    # Get value of chosen regularization parameter for Tikhonov regularization\n    #  \\return regularization parameter, scalar\n    def get_alpha(self):\n        return self._alpha\n\n    ##\n    # Sets the maximum number of iterations for Tikhonov solver.\n    # \\date       2016-08-01 16:35:09+0100\n    #\n    # \\param      self      The object\n    # \\param      iter_max  number of maximum iterations, scalar\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def set_iter_max(self, iter_max):\n        self._iter_max = iter_max\n\n    # Get chosen value of maximum number of iterations for minimizer for Tikhonov regularization\n    #  \\return maximum number of iterations set for minimizer, scalar\n    def get_iter_max(self):\n        return self._iter_max\n\n    ##\n    #       Sets the minimizer.\n    # \\date       2016-11-05 23:40:31+0000\n    #\n    # \\param      self       The object\n    # \\param      minimizer  The minimizer\n    #\n    def set_minimizer(self, minimizer):\n        self._minimizer = minimizer\n\n    def get_minimizer(self):\n        return self._minimizer\n\n    ##\n    # Sets the data loss rho in 1/2 ||rho(Ax-b)||^2\n    # \\date       2017-05-15 11:30:25+0100\n    #\n    # \\param      self       The object\n    # \\param      data_loss  string\n    #\n    def set_data_loss(self, data_loss):\n        if data_loss not in DATA_LOSS:\n            raise ValueError(\"Loss function must be in \" + str(DATA_LOSS))\n        self._data_loss = data_loss\n\n    def set_huber_gamma(self, huber_gamma):\n        self._huber_gamma = huber_gamma\n\n    def get_huber_gamma(self):\n        return self._huber_gamma\n\n    def set_verbose(self, verbose):\n        self._verbose = verbose\n\n    def get_verbose(self):\n        return self._verbose\n\n    def run(self):\n\n        # Run solver specific reconstruction\n        self._run()\n\n    # Get current estimate of reconstruction\n    #  \\return current estimate of reconstruction, instance of Stack\n    def get_reconstruction(self):\n        return self._reconstruction\n\n    # Get cut-off distance\n    #  \\return scalar value\n    def get_alpha_cut(self):\n        return self._alpha_cut\n\n    # Get computational time for reconstruction\n    #  \\return computational time in seconds\n    def get_computational_time(self):\n        return self._computational_time\n\n    ##\n    # Get function call A = lambda x: A(x) with A: R^n -> R^m\n    # \\date       2017-07-25 16:02:47+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     Function call mapping from and to 1D numpy array.\n    #\n    def get_A(self):\n        return lambda x: self._MA(x)\n\n    ##\n    # Gets function call A^* = lambda y: A^*(y) with A: R^m -> R^n\n    # \\date       2017-07-25 16:18:52+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     Function call mapping from and to 1D numpy array.\n    #\n    def get_A_adj(self):\n        return lambda x: self._A_adj_M(x)\n\n    ##\n    # Gets the right hand-side vector b \\in R^m\n    # \\date       2017-07-25 16:19:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     1D numpy array\n    #\n    def get_b(self):\n        return self._get_M_y()\n\n    ##\n    # Gets the initial value given by the flattened reconstruction numpy data\n    # array in R^n.\n    # \\date       2017-07-25 16:20:00+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     1D numpy array\n    #\n    def get_x0(self):\n        return sitk.GetArrayFromImage(self._reconstruction.sitk).flatten()\n\n    def get_x_scale(self):\n        return self._x_scale\n\n    ##\n    #       Gets the setting specific filename indicating the information\n    #             used for the reconstruction step\n    # \\date       2016-11-17 15:41:58+0000\n    #\n    # \\param      self    The object\n    # \\param      prefix  The prefix as string\n    #\n    # \\return     The setting specific filename as string.\n    #\n    @abstractmethod\n    def get_setting_specific_filename(self, prefix=\"\"):\n        pass\n\n    @abstractmethod\n    def _run(self):\n        pass\n\n    @abstractmethod\n    def get_solver(self):\n        pass\n\n    ##\n    #       Gets the predefined covariance.\n    # \\date       2016-10-14 16:52:10+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The predefined covariance as 3x3 numpy array\n    #\n    def get_predefined_covariance(self):\n        return self._predefined_covariance\n\n    ##\n    # Evaluate\n    # \\f$ M \\vec{y}\n    # \\f$\n    # \\f$ = \\begin{pmatrix} M_1 \\vec{y}_1 \\\\ M_2 \\vec{y}_2 \\\\ \\vdots \\\\ M_K\n    # \\vec{y}_K \\end{pmatrix} \\vec{x}\\f$\n    # \\date       2017-11-01 00:17:28+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     My, i.e. all masked slices stacked to 1D array\n    #\n    def _get_M_y(self):\n\n        # Allocate memory\n        My = np.zeros(self._N_total_slice_voxels)\n\n        # Define index for first voxel of first slice within array\n        i_min = 0\n\n        for i, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n\n            # Get number of voxels of each slice in current stack\n            N_slice_voxels = np.array(slices[0].sitk.GetSize()).prod()\n\n            for j, slice_j in enumerate(slices):\n\n                # Define index for last voxel to specify current slice\n                # (exlusive)\n                i_max = i_min + N_slice_voxels\n\n                # Apply M_k y_k\n                if self._use_masks:\n                    slice_itk = self._linear_operators.M_itk(\n                        slice_j.itk, slice_j.itk_mask)\n                else:\n                    slice_itk = slice_j.itk\n                slice_nda_vec = self._itk2np.GetArrayFromImage(\n                    slice_itk).flatten()\n\n                # Fill respective elements\n                My[i_min:i_max] = slice_nda_vec\n\n                # Define index for first voxel to specify subsequent slice\n                # (inclusive)\n                i_min = i_max\n\n        return My\n\n    ##\n    # Operation M_k A_k x\n    # \\date       2017-07-25 15:15:53+0100\n    #\n    # \\param      self       The object\n    # \\param      reconstruction_itk  reconstruction image as itk.Image object\n    # \\param      slice_k    Slice object which defines operator M_k and A_k\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def _Mk_Ak(self, reconstruction_itk, slice_k):\n\n        # Get slice spacing relevant for Gaussian blurring estimate\n        in_plane_res = slice_k.get_inplane_resolution()\n        slice_thickness = slice_k.get_slice_thickness()\n        slice_spacing = np.array([in_plane_res, in_plane_res, slice_thickness])\n\n        # Compute A_k x\n        Ak_reconstruction_itk = self._linear_operators.A_itk(\n            reconstruction_itk, slice_k.itk, slice_spacing)\n\n        if not self._use_masks:\n            return Ak_reconstruction_itk\n\n        # Compute M_k A_k x\n        Ak_reconstruction_itk = self._linear_operators.M_itk(\n            Ak_reconstruction_itk, slice_k.itk_mask)\n\n        return Ak_reconstruction_itk\n\n    ##\n    # Operation A_k^* M_k y_k\n    # \\date       2017-07-25 15:15:53+0100\n    #\n    # \\param      self       The object\n    # \\param      slice_itk  LR image as itk.Image object\n    # \\param      slice_k    Slice object which defines operator A_k^*\n    #\n    # \\return     image in reconstruction space as itk.Image object after\n    #             performed backward operation\n    #\n    def _Ak_adj_Mk(self, slice_itk, slice_k):\n\n        # Compute M_k y_k\n        if self._use_masks:\n            Mk_slice_itk = self._linear_operators.M_itk(\n                slice_itk, slice_k.itk_mask)\n        else:\n            Mk_slice_itk = slice_itk\n\n        # Get slice spacing relevant for Gaussian blurring estimate\n        in_plane_res = slice_k.get_inplane_resolution()\n        slice_thickness = slice_k.get_slice_thickness()\n        slice_spacing = np.array([in_plane_res, in_plane_res, slice_thickness])\n\n        # Compute A_k^* M_k y_k\n        Mk_slice_itk = self._linear_operators.A_adj_itk(\n            Mk_slice_itk, self._reconstruction.itk, slice_spacing)\n\n        return Mk_slice_itk\n\n    #\n    # Evaluate\n    # \\f$ MA \\vec{x}\n    # \\f$\n    # \\f$ = \\b egin{pmatrix} M_1 A_1 \\\\ M_2 A_2 \\\\ \\vdots \\\\ M_K A_K \\em\n    # nd{pmatrix} \\vec{x}\n    # \\f$\n    # \\date       2017-07-25 15:15:53+0100\n    #\n    # \\param      self           The object\n    # \\param      reconstruction_nda_vec  reconstruction data as 1D array\n    #\n    # \\return     evaluated MAx as part of augmented linear operator as 1D\n    #             array\n    #\n    def _MA(self, reconstruction_nda_vec):\n\n        # Convert reconstruction data array back to itk.Image object\n        x_itk = self._get_itk_image_from_array_vec(\n            reconstruction_nda_vec, self._reconstruction.itk)\n\n        # Allocate memory\n        MA_x = np.zeros(self._N_total_slice_voxels)\n\n        # Define index for first voxel of first slice within array\n        i_min = 0\n\n        for i, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n\n            # Get number of voxels of each slice in current stack\n            N_slice_voxels = np.array(slices[0].sitk.GetSize()).prod()\n\n            for j, slice_j in enumerate(slices):\n\n                # Define index for last voxel to specify current slice\n                # (exclusive)\n                i_max = i_min + N_slice_voxels\n\n                # Compute M_k A_k y_k\n                slice_itk = self._Mk_Ak(x_itk, slice_j)\n                slice_nda = self._itk2np.GetArrayFromImage(slice_itk)\n\n                # Fill corresponding elements\n                MA_x[i_min:i_max] = slice_nda.flatten()\n\n                # Define index for first voxel to specify subsequent slice\n                # (inclusive)\n                i_min = i_max\n\n        return MA_x\n\n    ##\n    # Evaluate\n    # \\f$ A^* M \\vec{y} = \\begin{bmatrix} A_1^* M_1 && A_2^* M_2 && \\c dots &&\n    # A_K^* M_K \\end{bmatrix} \\vec{y}\n    # \\f$\n    # \\date       2017-07-18 22:21:53+0100\n    #\n    # \\param      self                    The object\n    # \\param      stacked_slices_nda_vec  stacked slice data as 1D array\n    #\n    # \\return     evaluated A'My as part of augmented adjoint linear operator\n    #             as 1D array\n    #\n    def _A_adj_M(self, stacked_slices_nda_vec):\n\n        # Allocate memory\n        A_adj_M_y = np.zeros(self._N_voxels_recon)\n\n        # Define index for first voxel of first slice within array\n        i_min = 0\n\n        for i, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n\n            # Get number of voxels of each slice in current stack\n            N_slice_voxels = np.array(slices[0].sitk.GetSize()).prod()\n\n            for j, slice_j in enumerate(slices):\n\n                # Define index for last voxel to specify current slice\n                # (exlusive)\n                i_max = i_min + N_slice_voxels\n\n                # Extract 1D corresponding to current slice and convert it to\n                # itk.Object\n                slice_itk = self._get_itk_image_from_array_vec(\n                    stacked_slices_nda_vec[i_min:i_max], slice_j.itk)\n\n                # Apply A_k' M_k on current slice\n                Ak_adj_Mk_slice_itk = self._Ak_adj_Mk(slice_itk, slice_j)\n                Ak_adj_Mk_slice_nda_vec = self._itk2np.GetArrayFromImage(\n                    Ak_adj_Mk_slice_itk).flatten()\n\n                # Add contribution\n                A_adj_M_y += Ak_adj_Mk_slice_nda_vec\n\n                # Define index for first voxel to specify subsequent slice\n                # (inclusive)\n                i_min = i_max\n\n        return A_adj_M_y\n\n    #\n    # Convert numpy data array (vector format) back to itk.Image object\n    # \\date       2017-07-25 15:15:53+0100\n    #\n    # \\param      self           The object\n    # \\param      nda_vec        reconstruction data as 1D array\n    # \\param      image_itk_ref  The image itk reference\n    #\n    # \\return     reconstruction with intensities according to reconstruction_nda_vec as\n    #             itk.Image object\n    #\n    def _get_itk_image_from_array_vec(self, nda_vec, image_itk_ref):\n\n        shape_nda = np.array(\n            image_itk_ref.GetLargestPossibleRegion().GetSize())[::-1]\n\n        image_itk = self._itk2np.GetImageFromArray(nda_vec.reshape(shape_nda))\n        image_itk.SetOrigin(image_itk_ref.GetOrigin())\n        image_itk.SetSpacing(image_itk_ref.GetSpacing())\n        image_itk.SetDirection(image_itk_ref.GetDirection())\n\n        return image_itk\n"
  },
  {
    "path": "niftymic/reconstruction/tikhonov_solver.py",
    "content": "##\n# \\file tikhonov_solver.py\n# \\brief      Implementation to get an approximate solution of the SRR\n#             problem using Tikhonov-regularization\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2016\n#\n\nimport scipy\nimport numpy as np\nimport SimpleITK as sitk\n\nfrom nsol.definitions import EPS\nimport nsol.linear_operators as linop\nimport nsol.tikhonov_linear_solver as tk\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nfrom niftymic.reconstruction.solver import Solver\nimport niftymic.base.stack as st\n\n\n# This class implements the framework to iteratively solve\n#  \\f$ \\vec{y}_k = A_k \\vec{x} \\f$ for every slice \\f$ \\vec{y}_k,\\,k=1,\\dots,K \\f$\n#  via Tikhonov-regularization via an augmented least-square approach\n#  where \\f$A_k=D_k B_k W_k\\in\\mathbb{R}^{N_k}\\f$ denotes the combined warping, blurring and downsampling\n#  operation, \\f$ M_k \\f$ the masking operator and \\f$G\\f$ represents either\n#  the identity matrix \\f$I\\f$ (zeroth-order Tikhonov) or\n#  the (flattened, stacked vector) gradient\n#  \\f$ \\nabla  = \\begin{pmatrix} D_x \\\\ D_y \\\\ D_z \\end{pmatrix} \\f$\n#  (first-order Tikhonov).\n#  The minimization problem reads\n#  \\f[\n#       \\text{arg min}_{\\vec{x}} \\Big( \\sum_{k=1}^K \\frac{1}{2} \\Vert M_k (\\vec{y}_k - A_k \\vec{x} )\\Vert_{\\ell^2}^2\n#                       + \\frac{\\alpha}{2}\\,\\Vert G\\vec{x} \\Vert_{\\ell^2}^2 \\Big)\n#       =\n#       \\text{arg min}_{\\vec{x}} \\Bigg( \\Bigg\\Vert\n#           \\begin{pmatrix} M_1 A_1 \\\\ M_2 A_2 \\\\ \\vdots \\\\ M_K A_K \\\\ \\sqrt{\\alpha} G \\end{pmatrix} \\vec{x}\n#           - \\begin{pmatrix} M_1 \\vec{y}_1 \\\\ M_2 \\vec{y}_2 \\\\ \\vdots \\\\ M_K \\vec{y}_K \\\\ \\vec{0} \\end{pmatrix}\n#       \\Bigg\\Vert_{\\ell^2}^2 \\Bigg)\n#  \\f]\n#  By defining the shorthand\n#  \\f[\n#   MA := \\begin{pmatrix} M_1 A_1 \\\\ M_2 A_2 \\\\ \\vdots \\\\ M_K A_K \\end{pmatrix}\\in\\mathbb{R}^{\\sum_k N_k} \\quad\\text{and}\\quad\n#   M\\vec{y} := \\begin{pmatrix} M_1 \\vec{y}_1 \\\\ M_2 \\vec{y}_2 \\\\ \\vdots \\\\ M_K \\vec{y}_K \\end{pmatrix}\\in\\mathbb{R}^{\\sum_k N_k}\n#  \\f]\n#  the problem can be compactly written as\n#  \\f[\n#       \\text{arg min}_{\\vec{x}} \\Bigg( \\Bigg\\Vert\n#           \\begin{pmatrix} MA \\\\ \\sqrt{\\alpha} G \\end{pmatrix} \\vec{x}\n#           - \\begin{pmatrix} M\\vec{y} \\\\ \\vec{0} \\end{pmatrix}\n#       \\Bigg\\Vert_{\\ell^2}^2 \\Bigg)\n#  \\f]\n#  with \\f$ G\\in\\mathbb{R}^N \\f$ in case of \\f$G=I\\f$ or\n#  \\f$G\\in\\mathbb{R}^{3N}\\f$ in case of \\f$G\\f$ representing the gradient.\n#  \\see \\p itkAdjointOrientedGaussianInterpolateImageFilter of \\p ITK\n#  \\see \\p itOrientedGaussianInterpolateImageFunction of \\p ITK\nclass TikhonovSolver(Solver):\n\n    ##\n    # Constructor\n    # \\date          2016-08-01 23:00:04+0100\n    #\n    # \\param         self                   The object\n    # \\param         stacks                 list of Stack objects containing\n    #                                       all stacks used for the\n    #                                       reconstruction\n    # \\param[in,out] reconstruction         Stack object containing the current\n    #                                       estimate of the reconstruction\n    #                                       volume (used as initial value +\n    #                                       space definition)\n    # \\param         alpha_cut              Cut-off distance for Gaussian\n    #                                       blurring filter\n    # \\param         alpha                  regularization parameter, scalar\n    # \\param         iter_max               number of maximum iterations,\n    #                                       scalar\n    # \\param         reg_type               Type of Tikhonov regualrization,\n    #                                       i.e. TK0 or TK1 for either zeroth-\n    #                                       or first order Tikhonov\n    # \\param         minimizer              Type of minimizer used to solve\n    #                                       minimization problem, possible\n    #                                       types: 'lsmr', 'lsqr', 'L-BFGS-B' #\n    # \\param         deconvolution_mode     Either \"full_3D\" or\n    #                                       \"only_in_plane\". Indicates whether\n    #                                       full 3D or only in-plane\n    #                                       deconvolution is considered\n    # \\param         data_loss              The loss\n    # \\param         huber_gamma            The huber gamma\n    # \\param         predefined_covariance  The predefined covariance\n    # \\param         verbose                The verbose\n    #\n    def __init__(self,\n                 stacks,\n                 reconstruction,\n                 alpha_cut=3,\n                 alpha=0.03,\n                 iter_max=10,\n                 reg_type=\"TK1\",\n                 minimizer=\"lsmr\",\n                 deconvolution_mode=\"full_3D\",\n                 x_scale=\"max\",\n                 data_loss=\"linear\",\n                 data_loss_scale=1,\n                 huber_gamma=1.345,\n                 predefined_covariance=None,\n                 use_masks=True,\n                 verbose=1,\n                 ):\n\n        # Run constructor of superclass\n        Solver.__init__(self,\n                        stacks=stacks,\n                        reconstruction=reconstruction,\n                        alpha_cut=alpha_cut,\n                        alpha=alpha,\n                        iter_max=iter_max,\n                        minimizer=minimizer,\n                        deconvolution_mode=deconvolution_mode,\n                        x_scale=x_scale,\n                        data_loss=data_loss,\n                        data_loss_scale=data_loss_scale,\n                        huber_gamma=huber_gamma,\n                        predefined_covariance=predefined_covariance,\n                        verbose=verbose,\n                        use_masks=use_masks,\n                        )\n\n        # Settings for optimizer\n        self._reg_type = reg_type\n\n    #\n    # Set type of regularization. It can be either 'TK0' or 'TK1'\n    # \\date       2017-07-25 15:19:17+0100\n    #\n    # \\param      self      The object\n    # \\param      reg_type  Either 'TK0' or 'TK1', string\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def set_regularization_type(self, reg_type):\n        self._reg_type = reg_type\n\n    # Get chosen type of regularization.\n    #  \\return regularization type as string\n    def get_regularization_type(self):\n        return self._reg_type\n\n    ##\n    #       Gets the setting specific filename indicating the information\n    #             used for the reconstruction step\n    # \\date       2016-11-17 15:41:58+0000\n    #\n    # \\param      self    The object\n    # \\param      prefix  The prefix as string\n    #\n    # \\return     The setting specific filename as string.\n    #\n    def get_setting_specific_filename(self, prefix=\"SRR_\"):\n\n        # Build filename\n        filename = prefix\n        filename += \"stacks\" + str(len(self._stacks))\n        if self._alpha > 0:\n            filename += \"_\" + self._reg_type\n        filename += \"_\" + self._minimizer\n        if self._data_loss not in [\"linear\"]:\n            filename += \"_\" + self._data_loss\n            if self._data_loss in [\"huber\"]:\n                filename += str(self._huber_gamma)\n            filename += \"_fscale%g\" % self._data_loss_scale\n        filename += \"_alpha\" + str(self._alpha)\n        filename += \"_itermax\" + str(self._iter_max)\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n\n    def get_solver(self):\n        if self._reg_type not in [\"TK0\", \"TK1\"]:\n            raise ValueError(\n                \"Error: regularization type can only be either 'TK0' or 'TK1'\")\n\n        # Get operators\n        A = self.get_A()\n        A_adj = self.get_A_adj()\n        b = self.get_b()\n        x0 = self.get_x0()\n        x_scale = self.get_x_scale()\n\n        if self._reg_type == \"TK0\":\n            B = lambda x: x.flatten()\n            B_adj = lambda x: x.flatten()\n\n        elif self._reg_type == \"TK1\":\n            spacing = np.array(self._reconstruction.sitk.GetSpacing())\n            linear_operators = linop.LinearOperators3D(spacing=spacing)\n            grad, grad_adj = linear_operators.get_gradient_operators()\n\n            X_shape = self._reconstruction_shape\n            Z_shape = grad(x0.reshape(*X_shape)).shape\n\n            B = lambda x: grad(x.reshape(*X_shape)).flatten()\n            B_adj = lambda x: grad_adj(x.reshape(*Z_shape)).flatten()\n\n        # Set up solver\n        solver = tk.TikhonovLinearSolver(\n            A=A,\n            A_adj=A_adj,\n            B=B,\n            B_adj=B_adj,\n            b=b,\n            x0=x0,\n            x_scale=x_scale,\n            alpha=self._alpha,\n            data_loss=self._data_loss,\n            data_loss_scale=self._data_loss_scale,\n            verbose=self._verbose,\n            minimizer=self._minimizer,\n            iter_max=self._iter_max,\n            bounds=(0, np.inf),\n        )\n        return solver\n\n    ##\n    # Run the reconstruction algorithm based on Tikhonov regularization\n    # \\date       2016-07-29 12:35:01+0100\n    # \\post       self._reconstruction is updated with new volume and can be\n    #             fetched by \\p get_recon\n    #\n    # \\param      self  The object\n    # \\param      provide_initial_value  Use reconstruction volume during\n    #                                    initialization as initial value, boolean.\n    #                                    Otherwise, assume zero initial vale.\n    #\n    def _run(self):\n\n        solver = self.get_solver()\n\n        self._print_info_text()\n\n        # Run reconstruction\n        solver.run()\n\n        # Get computational time\n        self._computational_time = solver.get_computational_time()\n\n        # After reconstruction: Update member attribute\n        self._reconstruction.itk = self._get_itk_image_from_array_vec(\n            solver.get_x(), self._reconstruction.itk)\n        self._reconstruction.sitk = sitkh.get_sitk_from_itk_image(\n            self._reconstruction.itk)\n\n    def _print_info_text(self):\n\n        ph.print_subtitle(\"Tikhonov Solver:\")\n        ph.print_info(\"Chosen regularization type: \", newline=False)\n        if self._reg_type in [\"TK0\"]:\n            print(\"Zeroth-order Tikhonov\")\n\n        else:\n            print(\"First-order Tikhonov\")\n\n        if self._deconvolution_mode in [\"only_in_plane\"]:\n            ph.print_info(\"(Only in-plane deconvolution is performed)\")\n\n        elif self._deconvolution_mode in [\"predefined_covariance\"]:\n            ph.print_info(\"(Predefined covariance used: cov = %s)\"\n                          % (np.diag(self._predefined_covariance)))\n\n        if self._data_loss in [\"huber\"]:\n            ph.print_info(\"Loss function: %s (gamma = %g)\" %\n                          (self._data_loss, self._huber_gamma))\n        else:\n            ph.print_info(\"Loss function: %s\" % (self._data_loss))\n\n        if self._data_loss != \"linear\":\n            ph.print_info(\"Loss function scale: %g\" % (self._data_loss_scale))\n\n        ph.print_info(\"Regularization parameter: \" + str(self._alpha))\n        ph.print_info(\"Minimizer: \" + self._minimizer)\n        ph.print_info(\n            \"Maximum number of iterations: \" + str(self._iter_max))\n        # ph.print_info(\"Tolerance: %.0e\" %(self._tolerance))\n\n\nclass TemporalTikhonovSolver(object):\n\n    def __init__(self,\n                 stacks,\n                 reconstruction,\n                 alpha_cut=3,\n                 alpha=0.03,\n                 beta=0.1,\n                 iter_max=10,\n                 reg_type=\"TK1\",\n                 minimizer=\"lsmr\",\n                 deconvolution_mode=\"full_3D\",\n                 x_scale=\"max\",\n                 data_loss=\"linear\",\n                 data_loss_scale=1,\n                 huber_gamma=1.345,\n                 predefined_covariance=None,\n                 use_masks=True,\n                 verbose=1,\n                 ):\n\n        self._solvers = [\n            TikhonovSolver(\n                stacks=[s],\n                reconstruction=st.Stack.from_stack(reconstruction),\n                alpha_cut=alpha_cut,\n                alpha=alpha,\n                iter_max=iter_max,\n                reg_type=reg_type,\n                minimizer=minimizer,\n                deconvolution_mode=deconvolution_mode,\n                x_scale=x_scale,\n                data_loss=data_loss,\n                data_loss_scale=data_loss_scale,\n                huber_gamma=huber_gamma,\n                predefined_covariance=predefined_covariance,\n                use_masks=use_masks,\n                verbose=verbose,\n            )\n            for s in stacks\n        ]\n\n        self._reg_type = reg_type\n\n        self._alpha = alpha\n        self._beta = beta\n        self._iter_max = iter_max\n        self._verbose = verbose\n\n        self._stacks = stacks\n        self._reconstruction = reconstruction\n\n        self._computational_time = None\n        self._reconstructions = None\n        self._bounds = (0, np.inf)\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    def get_reconstructions(self):\n        return self._reconstructions\n\n    def run(self):\n        time_start = ph.start_timing()\n        self._print_info_text()\n\n        shape_x = self._solvers[0]._reconstruction_shape\n        self._n_x = np.array(shape_x).prod()\n        self._n_x_total = len(self._solvers) * self._n_x\n\n        x0 = self._solvers[0].get_x0()\n        if self._reg_type == \"TK0\":\n            self._B = lambda x: x.flatten()\n            self._B_adj = lambda x: x.flatten()\n\n        elif self._reg_type == \"TK1\":\n            spacing = np.array(self._reconstruction.sitk.GetSpacing())\n            linear_operators = linop.LinearOperators3D(spacing=spacing)\n            grad, grad_adj = linear_operators.get_gradient_operators()\n\n            X_shape = shape_x\n            Z_shape = grad(x0.reshape(*X_shape)).shape\n\n            self._B = lambda x: grad(x.reshape(*X_shape)).flatten()\n            self._B_adj = lambda x: grad_adj(x.reshape(*Z_shape)).flatten()\n\n        self._B_shape = (self._B(x0).size, x0.size)\n\n        n_rhs = 0\n        self._rhs = []\n        self._A = []\n        self._A_adj = []\n        self._D = []\n        self._D_adj = []\n        for solver in self._solvers:\n            b = solver.get_b()\n            n_rhs += len(b)\n            self._rhs.append(b)\n            self._A.append(solver.get_A())\n            self._A_adj.append(solver.get_A_adj())\n\n        if self._alpha > EPS:\n            n_rhs += len(self._solvers) * self._B_shape[0]\n\n        if self._beta > EPS:\n            n_rhs += (len(self._solvers) - 1) * self._n_x\n\n        self._x_1D = np.zeros(self._n_x_total)\n        self._rhs_1D = np.zeros(n_rhs)\n\n        A_fw = lambda x: self._A_fw(\n            x, np.sqrt(self._alpha), np.sqrt(self._beta))\n        A_bw = lambda x: self._A_bw(\n            x, np.sqrt(self._alpha), np.sqrt(self._beta))\n\n        # Construct (sparse) linear operator A\n        A = scipy.sparse.linalg.LinearOperator(\n            shape=(self._rhs_1D.size, self._x_1D.size),\n            matvec=A_fw,\n            rmatvec=A_bw)\n        b = np.zeros_like(A(self._x_1D))\n        b_upper = np.concatenate(self._rhs)\n        b[:b_upper.size] = b_upper\n\n        x = scipy.sparse.linalg.lsmr(\n            A, b,\n            maxiter=self._iter_max,\n            show=self._verbose,\n            atol=0,\n            btol=0)[0]\n\n        if self._bounds is not None:\n            # Clip to bounds\n            x = np.clip(x, self._bounds[0], self._bounds[1])\n\n        self._reconstructions = self._get_reconstructions(x)\n\n        # y_vec = s.get_b()\n\n        self._computational_time = ph.stop_timing(time_start)\n\n    def _A_fw(self, x, sqrt_alpha, sqrt_beta):\n\n        i0 = 0\n\n        # cost\n        for i, solver in enumerate(self._solvers):\n            i1 = i0 + len(self._rhs[i])\n            self._rhs_1D[i0:i1] = self._A[i](\n                x[i * self._n_x:(i + 1) * self._n_x]\n            )\n            i0 = i1\n\n        # tikhonov\n        if sqrt_alpha > EPS:\n            for i, solver in enumerate(self._solvers):\n                self._rhs_1D[\n                    i0 + self._B_shape[0] * i:\n                    i0 + self._B_shape[0] * (i + 1)\n                ] = sqrt_alpha * self._B(x[i * self._n_x:(i + 1) * self._n_x])\n                i1 = i0 + self._B_shape[0] * (i + 1)\n\n        # temporal\n        if sqrt_beta > EPS:\n            for i in range(len(self._solvers) - 1):\n                self._rhs_1D[\n                    i1 + self._n_x * i:\n                    i1 + self._n_x * (i + 1)\n                ] = sqrt_beta * (\n                    x[(i + 1) * self._n_x:(i + 2) * self._n_x]\n                    - x[i * self._n_x:(i + 1) * self._n_x]\n                )\n\n        return self._rhs_1D\n\n    def _A_bw(self, b, sqrt_alpha, sqrt_beta):\n        i0 = 0\n        self._x_1D[:] = 0\n\n        # cost\n        for i, solver in enumerate(self._solvers):\n            i1 = i0 + len(self._rhs[i])\n            self._x_1D[i * self._n_x:(i + 1) * self._n_x] = \\\n                self._A_adj[i](b[i0:i1])\n            i0 = i1\n\n        # tikhonov\n        if sqrt_alpha > EPS:\n            for i, solver in enumerate(self._solvers):\n                self._x_1D[i * self._n_x:(i + 1) * self._n_x] += \\\n                    sqrt_alpha * self._B_adj(\n                        b[i0:i0 + self._B_shape[0]]\n                )\n                i0 += self._B_shape[0]\n\n        # temporal\n        if sqrt_beta > EPS:\n            i = 0\n            self._x_1D[i * self._n_x:(i + 1) * self._n_x] += \\\n                - sqrt_beta * b[i0 + self._n_x * i: i0 + self._n_x * (i + 1)]\n\n            for i in range(1, len(self._solvers) - 1):\n                self._x_1D[i * self._n_x:(i + 1) * self._n_x] += \\\n                    sqrt_beta * (\n                        b[i0 + self._n_x * (i - 1): i0 + self._n_x * i]\n                        - b[i0 + self._n_x * i: i0 + self._n_x * (i + 1)]\n                )\n\n            i = len(self._solvers) - 1\n            self._x_1D[i * self._n_x:(i + 1) * self._n_x] += \\\n                sqrt_beta * (\n                    b[i0 + self._n_x * (i - 1): i0 + self._n_x * i]\n            )\n\n        return self._x_1D\n\n    def _get_reconstructions(self, x):\n\n        reconstructions = []\n        for i, solver in enumerate(self._solvers):\n            x_vec = x[i * self._n_x:(i + 1) * self._n_x]\n            recon_itk = solver._get_itk_image_from_array_vec(\n                x_vec, self._reconstruction.itk)\n            recon_sitk = sitkh.get_sitk_from_itk_image(recon_itk)\n            reconstructions.append(\n                st.Stack.from_sitk_image(\n                    image_sitk=recon_sitk,\n                    slice_thickness=self._reconstruction.get_slice_thickness(),\n                    image_sitk_mask=self._reconstruction.sitk_mask,\n                )\n            )\n        return reconstructions\n\n    def _print_info_text(self):\n\n        ph.print_subtitle(\"Temporal Tikhonov Solver:\")\n        ph.print_info(\"Chosen regularization type: \", newline=False)\n        if self._reg_type in [\"TK0\"]:\n            print(\"Zeroth-order Tikhonov\")\n\n        else:\n            print(\"First-order Tikhonov\")\n\n        # if self._deconvolution_mode in [\"only_in_plane\"]:\n        #     ph.print_info(\"(Only in-plane deconvolution is performed)\")\n\n        # elif self._deconvolution_mode in [\"predefined_covariance\"]:\n        #     ph.print_info(\"(Predefined covariance used: cov = %s)\"\n        #                   % (np.diag(self._predefined_covariance)))\n\n        # if self._data_loss in [\"huber\"]:\n        #     ph.print_info(\"Loss function: %s (gamma = %g)\" %\n        #                   (self._data_loss, self._huber_gamma))\n        # else:\n        #     ph.print_info(\"Loss function: %s\" % (self._data_loss))\n\n        # if self._data_loss != \"linear\":\n        #     ph.print_info(\"Loss function scale: %g\" % (self._data_loss_scale))\n\n        ph.print_info(\n            \"Regularization parameter alpha (spatial reg): \" + str(self._alpha))\n        ph.print_info(\n            \"Regularization parameter beta (temporal reg): \" + str(self._beta))\n        # ph.print_info(\"Minimizer: \" + self._minimizer)\n        ph.print_info(\"Maximum number of iterations: \" + str(self._iter_max))\n        # ph.print_info(\"Tolerance: %.0e\" %(self._tolerance))\n\n    def get_setting_specific_filename(self, prefix=\"SRR_\"):\n\n        # Build filename\n        filename = prefix\n        filename += \"stacks\" + str(len(self._stacks))\n        if self._alpha > 0 or self._beta > 0:\n            filename += \"_\" + self._reg_type\n        # filename += \"_\" + self._minimizer\n        # if self._data_loss not in [\"linear\"]:\n        #     filename += \"_\" + self._data_loss\n        #     if self._data_loss in [\"huber\"]:\n        #         filename += str(self._huber_gamma)\n        #     filename += \"_fscale%g\" % self._data_loss_scale\n        filename += \"_alpha\" + str(self._alpha)\n        filename += \"_beta\" + str(self._beta)\n        filename += \"_itermax\" + str(self._iter_max)\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n"
  },
  {
    "path": "niftymic/registration/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/registration/flirt.py",
    "content": "##\n# \\file flirt.py\n# \\brief      Class to use registration method FLIRT\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\n\n# Import libraries\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport simplereg.flirt\n\nimport niftymic.base.stack as st\nfrom niftymic.registration.registration_method \\\n    import AffineRegistrationMethod\n\n\n##\n# Class to use registration method FLIRT\n# \\date       2017-08-09 11:22:33+0100\n#\nclass FLIRT(AffineRegistrationMethod):\n\n    def __init__(self,\n                 fixed=None,\n                 moving=None,\n                 use_fixed_mask=False,\n                 use_moving_mask=False,\n                 use_verbose=False,\n                 registration_type=\"Rigid\",\n                 options=\"\",\n                 ):\n\n        AffineRegistrationMethod.__init__(self,\n                                          fixed=fixed,\n                                          moving=moving,\n                                          use_fixed_mask=use_fixed_mask,\n                                          use_moving_mask=use_moving_mask,\n                                          use_verbose=use_verbose,\n                                          registration_type=registration_type,\n                                          )\n\n        # Allowed registration types for FLIRT\n        self._REGISTRATION_TYPES = [\"Rigid\", \"Affine\"]\n\n        self._options = options\n\n    ##\n    # Sets the options used for FLIRT\n    # \\date       2017-08-08 19:57:47+0100\n    #\n    # \\param      self     The object\n    # \\param      options  The options as string\n    #\n    def set_options(self, options):\n        self._options = options\n\n    ##\n    # Gets the options.\n    # \\date       2017-08-08 19:58:14+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The options as string.\n    #\n    def get_options(self):\n        return self._options\n\n    def _run(self):\n\n        if self._use_fixed_mask:\n            fixed_sitk_mask = self._fixed.sitk_mask\n        else:\n            fixed_sitk_mask = None\n\n        if self._use_moving_mask:\n            moving_sitk_mask = self._moving.sitk_mask\n        else:\n            moving_sitk_mask = None\n\n        options = self._options\n        if self.get_registration_type() == \"Rigid\":\n            options += \" -dof 6\"\n\n        elif self.get_registration_type() == \"Affine\":\n            options += \" -dof 12\"\n\n        self._registration_method = simplereg.flirt.FLIRT(\n            fixed_sitk=self._fixed.sitk,\n            moving_sitk=self._moving.sitk,\n            fixed_sitk_mask=fixed_sitk_mask,\n            moving_sitk_mask=moving_sitk_mask,\n            options=options,\n            verbose=self._use_verbose,\n        )\n        self._registration_method.run()\n\n        self._registration_transform_sitk = \\\n            self._registration_method.get_registration_transform_sitk()\n\n    def _get_warped_moving_sitk(self):\n        return self._registration_method.get_warped_moving_sitk()\n"
  },
  {
    "path": "niftymic/registration/intra_stack_registration.py",
    "content": "##\n# \\file intra_stack_registration.py\n# \\brief      Intra-stack registration steps where slices are only transformed\n#             2D in-plane.\n#\n# Class has been mainly developed for the CIS30FU project.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2016\n#\n\n\n# Import libraries\nimport SimpleITK as sitk\nimport itk\nimport numpy as np\n\nimport niftymic.base.slice as sl\nimport niftymic.base.stack as st\nimport niftymic.utilities.intensity_correction as ic\n# Import modules\nimport pysitk.simple_itk_helper as sitkh\nfrom niftymic.registration.stack_registration_base import StackRegistrationBase\n\n\nclass IntraStackRegistration(StackRegistrationBase):\n\n    ##\n    # { constructor_description }\n    # \\date       2017-07-14 14:24:00+0100\n    #\n    # \\param      self                                           The object\n    # \\param      stack                                          The stack to\n    #                                                            be aligned as\n    #                                                            Stack object\n    # \\param      reference                                      The reference\n    #                                                            used for\n    #                                                            alignment as\n    #                                                            Stack object\n    # \\param      use_stack_mask                                 Use stack mask\n    #                                                            for\n    #                                                            registration,\n    #                                                            bool\n    # \\param      use_reference_mask                             Use reference\n    #                                                            mask for\n    #                                                            registration,\n    #                                                            bool\n    # \\param      use_verbose                                    Verbose\n    #                                                            output, bool\n    # \\param      transform_initializer_type                     The transform\n    #                                                            initializer\n    #                                                            type, e.g.\n    #                                                            \"identity\",\n    #                                                            \"moments\" or\n    #                                                            \"geometry\"\n    # \\param      interpolator                                   The interpolator\n    # \\param      alpha_neighbour                                Weight >= 0\n    #                                                            for neighbour\n    #                                                            term\n    # \\param      alpha_reference                                Weight >= 0\n    #                                                            for reference\n    #                                                            term\n    # \\param      alpha_parameter                                Weight >= 0\n    #                                                            for prior term\n    # \\param      transform_type                                 The transform\n    #                                                            type, \"rigid\",\n    #                                                            \"similarity\",\n    #                                                            \"affine\"\n    # \\param      optimizer                                      Either\n    #                                                            \"least_squares\"\n    #                                                            to use\n    #                                                            scipy.optimize.least_squares\n    #                                                            or any method\n    #                                                            used in\n    #                                                            \"scipy.optimize.minimize\",\n    #                                                            e.g.\n    #                                                            \"L-BFGS-B\".\n    # \\param      optimizer_iter_max                             Maximum number\n    #                                                            of\n    #                                                            iterations/function\n    #                                                            evaluations\n    # \\param      optimizer_loss                                 Loss function,\n    #                                                            e.g. \"linear\",\n    #                                                            \"soft_l1\" or\n    #                                                            \"huber\".\n    # \\param      optimizer_method                               The optimizer\n    #                                                            method used\n    #                                                            for\n    #                                                            \"least_squares\"\n    #                                                            algorithm.\n    #                                                            E.g. \"trf\"\n    # \\param      use_parameter_normalization                    Use parameter\n    #                                                            normalization\n    #                                                            for optimizer,\n    #                                                            bool\n    # \\param      intensity_correction_initializer_type          The intensity\n    #                                                            correction\n    #                                                            initializer\n    #                                                            type; None,\n    #                                                            \"linear\" or\n    #                                                            \"affine\"\n    # \\param      intensity_correction_type_slice_neighbour_fit  The intensity\n    #                                                            correction\n    #                                                            type used for\n    #                                                            slice\n    #                                                            neighbour\n    #                                                            term, None,\n    #                                                            \"linear\" or\n    #                                                            \"affine\"\n    # \\param      prior_intensity_correction_coefficients        Prior used for\n    #                                                            intensity\n    #                                                            correction\n    #                                                            coefficients\n    # \\param      prior_scale                                    Prior used for\n    #                                                            scaling; only\n    #                                                            valid for\n    #                                                            \"similarity\"\n    # \\param      image_transform_reference_fit_term             The image\n    #                                                            transform\n    #                                                            reference fit\n    #                                                            term; Either\n    #                                                            \"identity\",\n    #                                                            \"gradient_magnitude\",\n    #                                                            \"partial_derivative\"\n    #\n    def __init__(self,\n                 stack=None,\n                 reference=None,\n                 use_stack_mask=False,\n                 use_reference_mask=False,\n                 use_verbose=False,\n                 transform_initializer_type=\"identity\",\n                 interpolator=\"Linear\",\n                 alpha_neighbour=1,\n                 alpha_reference=1,\n                 alpha_parameter=0,\n                 transform_type=\"rigid\",\n                 optimizer=\"least_squares\",\n                 optimizer_iter_max=20,\n                 optimizer_loss=\"soft_l1\",\n                 optimizer_method=\"trf\",\n                 use_parameter_normalization=False,\n                 intensity_correction_initializer_type=None,\n                 intensity_correction_type_slice_neighbour_fit=None,\n                 prior_intensity_correction_coefficients=np.array([1, 0]),\n                 prior_scale=1.0,\n                 image_transform_reference_fit_term=\"identity\",\n                 ):\n\n        # Run constructor of superclass\n        StackRegistrationBase.__init__(\n            self,\n            stack=stack,\n            reference=reference,\n            use_stack_mask=use_stack_mask,\n            use_reference_mask=use_reference_mask,\n            use_verbose=use_verbose,\n            transform_initializer_type=transform_initializer_type,\n            use_parameter_normalization=use_parameter_normalization,\n            optimizer=optimizer,\n            optimizer_iter_max=optimizer_iter_max,\n            optimizer_loss=optimizer_loss,\n            optimizer_method=optimizer_method,\n            interpolator=interpolator,\n            alpha_neighbour=alpha_neighbour,\n            alpha_reference=alpha_reference,\n            alpha_parameter=alpha_parameter,\n        )\n\n        # Chosen transform type\n        self._transform_type = transform_type\n\n        # Dictionaries to create new transform depending on the chosen\n        # transform type\n        self._new_transform_sitk = {\n            \"rigid\": self._new_rigid_transform_sitk,\n            \"similarity\": self._new_similarity_transform_sitk,\n            \"affine\": self._new_affine_transform_sitk\n        }\n        self._new_transform_itk = {\n            \"rigid\": self._new_rigid_transform_itk,\n            \"similarity\": self._new_similarity_transform_itk,\n            \"affine\": self._new_affine_transform_itk\n        }\n\n        # Chosen intensity correction type\n        self._intensity_correction_type_slice_neighbour_fit = \\\n            intensity_correction_type_slice_neighbour_fit\n        self._intensity_correction_type_reference_fit = \\\n            intensity_correction_type_slice_neighbour_fit\n\n        # Define image type for reference cost\n        self._image_transform_reference_fit_term = image_transform_reference_fit_term\n\n        # Dictionary to apply requested intensity correction\n        self._apply_intensity_correction = {\n            None:  self._apply_intensity_correction_None,\n            \"linear\":  self._apply_intensity_correction_linear,\n            \"affine\":  self._apply_intensity_correction_affine,\n        }\n\n        self._add_gradient_with_respect_to_intensity_correction_parameters = {\n            None: self._add_gradient_with_respect_to_intensity_correction_parameters_None,\n            \"linear\": self._add_gradient_with_respect_to_intensity_correction_parameters_linear,\n            \"affine\": self._add_gradient_with_respect_to_intensity_correction_parameters_affine,\n        }\n\n        # Specifies how the initial values for the intensity correction shall\n        # be computed\n        self._intensity_correction_initializer_type = intensity_correction_initializer_type\n\n        # Dictionary to get initial values for intensity correction\n        self._get_initial_intensity_correction_parameters = {\n            None: self._get_initial_intensity_correction_parameters_None,\n            \"linear\": self._get_initial_intensity_correction_parameters_linear,\n            \"affine\": self._get_initial_intensity_correction_parameters_affine\n        }\n\n        # Scale prior\n        self._prior_scale = prior_scale\n\n        # Intensity correction coefficient priors\n        self._prior_intensity_correction_coefficients = prior_intensity_correction_coefficients\n\n        ##\n        self._get_residual_intensity_coefficients = {\n            None: self._get_residual_intensity_coefficients_None,\n            \"linear\": self._get_residual_intensity_coefficients_linear,\n            \"affine\": self._get_residual_intensity_coefficients_affine\n        }\n        self._get_jacobian_residual_intensity_coefficients = {\n            None: self._get_jacobian_residual_intensity_coefficients_None,\n            \"linear\": self._get_jacobian_residual_intensity_coefficients_linear,\n            \"affine\": self._get_jacobian_residual_intensity_coefficients_affine\n        }\n\n        # Dictionary, to update the the slices according to the obtained\n        # registration\n        self._apply_motion_correction_and_compute_slice_transforms = {\n            \"rigid\":  self._apply_rigid_motion_correction_and_compute_slice_transforms,\n            \"similarity\":  self._apply_similarity_motion_correction_and_compute_slice_transforms,\n            \"affine\":  self._apply_affine_motion_correction_and_compute_slice_transforms\n        }\n\n        # Gradient Magnitude Filter\n        self._gradient_magnitude_filter_sitk = sitk.GradientMagnitudeImageFilter()\n        self._gradient_magnitude_filter_sitk.SetUseImageSpacing(True)\n\n        # Gradient Image Filter\n        self._gradient_image_filter_sitk = sitk.GradientImageFilter()\n        self._gradient_image_filter_sitk.SetUseImageDirection(True)\n        self._gradient_image_filter_sitk.SetUseImageSpacing(True)\n\n        self._apply_image_transform = {\n            \"identity\":   self._apply_image_transform_identity,\n            \"dx\":   self._apply_image_transform_dx,\n            \"dy\":   self._apply_image_transform_dy,\n            \"gradient_magnitude\":   self._apply_image_transform_gradient_magnitude\n        }\n\n        # Costs\n        self._final_cost = 0\n        self._residual_paramters_ell2 = 0\n        self._residual_reference_fit_ell2 = 0\n        self._residual_slice_neighbours_ell2 = 0\n\n        self._use_stack_mask_reference_fit_term = self._use_stack_mask\n        self._use_stack_mask_neighbour_fit_term = self._use_stack_mask\n\n    ##\n    # Sets the transform type.\n    # \\date       2016-11-10 01:53:58+0000\n    #\n    # \\param      self            The object\n    # \\param      transform_type  The transform type\n    #\n    def set_transform_type(self, transform_type):\n        if transform_type not in self._new_transform_sitk.keys():\n            raise ValueError(\"Transform type \" + transform_type +\n                             \" not possible.\\nAllowed values: \" +\n                             str(self._new_transform_sitk.keys()))\n        self._transform_type = transform_type\n\n    def get_transform_type(self):\n        return self._transform_type\n\n    ##\n    # Set the intensity correction type\n    # \\date       2016-11-10 01:58:39+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag\n    #\n    def set_intensity_correction_type_slice_neighbour_fit(self, intensity_correction_type_slice_neighbour_fit):\n        if intensity_correction_type_slice_neighbour_fit \\\n                not in self._apply_intensity_correction.keys():\n            raise ValueError(\"Intensity correction type \" +\n                             intensity_correction_type_slice_neighbour_fit +\n                             \" not possible.\\nAllowed values: \" + str(self._apply_intensity_correction.keys()))\n        self._intensity_correction_type_slice_neighbour_fit = \\\n            intensity_correction_type_slice_neighbour_fit\n\n    def get_intensity_correction_type_slice_neighbour_fit(self):\n        return self._intensity_correction_type_slice_neighbour_fit\n\n    ##\n    # Set the intensity correction type for neighbour fit\n    # \\date       2016-11-10 01:58:39+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag\n    #\n    def set_intensity_correction_type_reference_fit(self, intensity_correction_type_slice_reference_fit):\n        if intensity_correction_type_slice_reference_fit \\\n                not in self._apply_intensity_correction.keys():\n            raise ValueError(\"Intensity correction type \" +\n                             intensity_correction_type_slice_reference_fit +\n                             \" not possible.\\nAllowed values: \" +\n                             str(self._apply_intensity_correction.keys()))\n        self._intensity_correction_type_reference_fit = \\\n            intensity_correction_type_slice_reference_fit\n\n    def get_intensity_correction_type_reference_fit(self):\n        return self._intensity_correction_type_reference_fit\n\n    ##\n    # Sets the intensity correction initializer type. It specifies how the\n    # initial values for the intensity correction shall be computed.\n    # \\date       2016-11-21 19:36:26+0000\n    #\n    # \\param      self                                   The object\n    # \\param      intensity_correction_initializer_type  The intensity\n    #                                                    correction initializer\n    #                                                    type\n    #\n    def set_intensity_correction_initializer_type(self, intensity_correction_initializer_type):\n        if intensity_correction_initializer_type \\\n                not in self._apply_intensity_correction.keys():\n            raise ValueError(\"Intensity correction initializer type \" +\n                             intensity_correction_initializer_type +\n                             \" not possible.\\nAllowed values: \" +\n                             str(self._apply_intensity_correction.keys()))\n        self._intensity_correction_initializer_type = \\\n            intensity_correction_initializer_type\n\n    def get_intensity_correction_initializer_type(self):\n        return self._intensity_correction_initializer_type\n\n    ##\n    # Sets the estimated scale.\n    # \\date       2016-11-21 15:45:14+0000\n    #\n    # \\param      self             The object\n    # \\param      prior_scale  The estimated scale\n    #\n    def set_prior_scale(self, prior_scale):\n        self._prior_scale = prior_scale\n        # self._parameters_prior_transform[\"similarity\"][0] = prior_scale\n\n    def set_prior_intensity_coefficients(self, coefficients):\n        coefficients = np.array(coefficients)\n\n        if coefficients.size is 1:\n            self._prior_intensity_correction_coefficients[0] = coefficients\n        elif coefficients.size is 2:\n            self._prior_intensity_correction_coefficients = coefficients\n        else:\n            raise ValueError(\"Coefficients must be of length 1 or 2\")\n\n    ##\n    # Set image type used to compute the reference cost\n    # \\date       2016-11-30 14:14:36+0000\n    #\n    # \\param      self                       The object\n    # \\param      image_transform_reference_fit_term  The image type reference cost\n    #\n    def set_image_transform_reference_fit_term(self, image_transform_reference_fit_term):\n        if image_transform_reference_fit_term \\\n                not in [\"identity\", \"gradient_magnitude\", \"partial_derivative\"]:\n            raise ValueError(\"Registration image type\" +\n                             image_transform_reference_fit_term +\n                             \" for the reference residuals is not possible.\")\n        self._image_transform_reference_fit_term = \\\n            image_transform_reference_fit_term\n\n    def use_stack_mask_reference_fit_term(self, flag):\n        self._use_stack_mask_reference_fit_term = flag\n\n    def use_stack_mask_neighbour_fit_term(self, flag):\n        self._use_stack_mask_neighbour_fit_term = flag\n\n    def get_final_cost(self):\n        if self._final_cost is None:\n            self._compute_statistics_residuals_ell2()\n        return self._final_cost\n\n    def print_statistics(self):\n\n        # Compute ell2-norm of residuals\n        self._compute_statistics_residuals_ell2()\n\n        StackRegistrationBase.print_statistics(self)\n\n        if self._alpha_reference > self._ZERO:\n            print(\"\\tell^2-residual sum_k ||slice_k(T(theta_k)) - ref||_2^2 = %.3e\" %\n                  (self._residual_reference_fit_ell2))\n\n        if self._alpha_neighbour > self._ZERO:\n            print(\"\\tell^2-residual sum_k ||slice_k(T(theta_k)) - slice_{k+1}(T(theta_{k+1}))||_2^2 = %.3e\" % (\n                self._residual_slice_neighbours_ell2))\n\n        if self._alpha_parameter > self._ZERO:\n            print(\"\\tell^2-residual sum_k ||theta_k - theta_k0||_2^2 = %.3e\" %\n                  (self._residual_paramters_ell2))\n\n        print(\"\\tFinal cost: %.3e\" % (self._final_cost))\n\n    def get_setting_specific_filename(self, prefix=\"_\"):\n\n        dictionary_method = {\n            \"trf\": \"TRF\",\n            \"dogbox\": \"DogBox\",\n            \"lm\": \"LM\"\n        }\n        dictionary_loss = {\n            \"linear\": \"Linear\",\n            \"soft_l1\": \"Softl1\",\n            \"huber\": \"Huber\"\n        }\n\n        # Build filename\n        filename = prefix\n        filename += self._transform_type.capitalize()\n        filename += \"_IC\" + \\\n            str(self._intensity_correction_type_slice_neighbour_fit)\n        filename += \"_Opt\"\n        filename += dictionary_method[self._optimizer_method]\n        filename += dictionary_loss[self._optimizer_loss]\n        filename += \"_maskStack\" + str(int(self._use_stack_mask))\n        if self._reference is not None:\n            filename += \"_maskRef\" + str(int(self._use_reference_mask))\n        filename += \"_Nfevmax\" + str(self._optimizer_iter_max)\n        filename += \"_alphaR\" + \"%.g\" % (self._alpha_reference)\n        filename += \"_alphaN\" + \"%.g\" % (self._alpha_neighbour)\n        filename += \"_alphaP\" + \"%.g\" % (self._alpha_parameter)\n\n        # Replace dots by 'p'\n        filename = filename.replace(\".\", \"p\")\n\n        return filename\n\n    def _print_info_text_least_squares(self):\n        print(\"Minimization via least_squares solver (scipy.optimize.least_squares)\")\n        print(\"\\tMethod: \" + self._optimizer_method)\n        print(\"\\tLoss: \" + self._optimizer_loss)\n        print(\"\\tMaximum number of function evaluations: \" +\n              str(self._optimizer_iter_max))\n        self._print_into_text_common()\n\n    def _print_info_text_minimize(self):\n        print(\"Minimization via %s solver (scipy.optimize.minimize)\" %\n              (self._optimizer))\n        print(\"\\tLoss: \" + self._optimizer_loss)\n        print(\"\\tMaximum number of iterations: \" +\n              str(self._optimizer_iter_max))\n        self._print_into_text_common()\n\n    def _print_into_text_common(self):\n        print(\"\\tTransform type: \" + self._transform_type +\n              \" (Initialization: \" + str(self._transform_initializer_type) + \")\")\n        if self._alpha_neighbour > self._ZERO:\n            print(\"\\tSlice neighbour fit term:\")\n            print(\"\\t\\tIntensity correction type: \" +\n                  str(self._intensity_correction_type_slice_neighbour_fit) +\n                  \" (Initialization: \" +\n                  str(self._intensity_correction_initializer_type) + \")\")\n            print(\"\\t\\tStack mask used: \" +\n                  str(self._use_stack_mask_neighbour_fit_term))\n        if self._alpha_reference > self._ZERO:\n            print(\"\\tReference fit term:\")\n            print(\"\\t\\tIntensity correction type: \" +\n                  str(self._intensity_correction_type_reference_fit) +\n                  \" (Initialization: \" +\n                  str(self._intensity_correction_initializer_type) + \")\")\n            print(\"\\t\\tImage transform: \" +\n                  self._image_transform_reference_fit_term)\n            print(\"\\t\\tStack mask used: \" +\n                  str(self._use_stack_mask_reference_fit_term))\n            print(\"\\t\\tReference mask used: \" + str(self._use_reference_mask))\n        print(\"\\tRegularization coefficients: %.g (reference), %.g (neighbour), %.g (parameter)\" % (\n            self._alpha_reference,\n            self._alpha_neighbour,\n            self._alpha_parameter))\n\n    ##\n    # { function_description }\n    # \\date       2016-11-08 14:59:26+0000\n    #\n    # \\param      self  The object\n    #\n    def _run_registration_pipeline_initialization(self):\n\n        self._transform_type_dofs = len(\n            self._new_transform_sitk[self._transform_type]().GetParameters())\n\n        # Get number of voxels in the x-y image plane\n        self._N_slice_voxels = self._stack.sitk.GetWidth() * \\\n            self._stack.sitk.GetHeight()\n\n        # Get projected 2D slices onto x-y image plane\n        self._slices_2D = self._get_projected_2D_slices_of_stack(\n            self._stack, registration_image_type=\"identity\")\n\n        # If reference is given, precompute required data\n        if self._reference is not None:\n\n            # Get numpy data arrays from reference image mask\n            self._reference_nda_mask = sitk.GetArrayFromImage(\n                self._reference.sitk_mask)\n\n            # Since self._intensity_correction_type_slice_neighbour_fit defines\n            # the used intensity correction type, i.e. the intensity parameters\n            # for optimisation, set them equal in case no neighbour desired.\n            if abs(self._alpha_neighbour) < self._ZERO:\n                self._intensity_correction_type_slice_neighbour_fit = self._intensity_correction_type_reference_fit\n\n            # slice_i(T(theta_i, x)) - ref(x))\n            if self._image_transform_reference_fit_term in [\"identity\"]:\n\n                # Used to get initial intensity correction\n                # parameters/coefficients\n                self._init_stack = self._stack\n                self._init_reference = self._reference\n\n                # Used to get initial transform parameters\n                self._init_slices_2D_stack_reference_term = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._stack, registration_image_type=\"identity\")\n                self._init_slices_2D_reference = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._reference, registration_image_type=\"identity\")\n\n                # Used to compare slice data arrays against in residual\n                # evaluation\n                self._reference_nda = sitk.GetArrayFromImage(\n                    self._reference.sitk)\n\n            # |grad slice_i|(T(theta_i, x)) - |grad ref|(x))\n            elif self._image_transform_reference_fit_term in [\"gradient_magnitude\"]:\n\n                # Used to get initial intensity correction\n                # parameters/coefficients\n                gradient_magnitude_stack_sitk = \\\n                    self._gradient_magnitude_filter_sitk.Execute(\n                        self._stack.sitk)\n                self._init_stack = st.Stack.from_sitk_image(\n                    gradient_magnitude_stack_sitk,\n                    \"GradMagn_\" + self._stack.get_filename(),\n                    self._stack.sitk_mask)\n                gradient_magnitude_reference_sitk = \\\n                    self._gradient_magnitude_filter_sitk.Execute(\n                        self._reference.sitk)\n                self._init_reference = st.Stack.from_sitk_image(\n                    gradient_magnitude_reference_sitk,\n                    \"GradMagn_\" + self._reference.get_filename(),\n                    self._stack.sitk_mask)\n\n                # Used to get initial transform parameters\n                self._init_slices_2D_stack_reference_term = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._stack,\n                        registration_image_type=\"gradient_magnitude\")\n                self._init_slices_2D_reference = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._reference,\n                        registration_image_type=\"gradient_magnitude\")\n\n                # Used to compare slice data arrays against in residual\n                # evaluation\n                self._gradient_magnitude_reference_nda = np.zeros(\n                    np.array(self._reference.sitk.GetSize())[::-1])\n                for i in range(0, self._N_slices):\n                    self._gradient_magnitude_reference_nda[i, :, :] = \\\n                        sitk.GetArrayFromImage(\n                            self._init_slices_2D_reference[i].sitk)\n\n            # ||dx(slice_i)(T(theta_i)) - dx(ref)|| + ||dy(slice_i)(T(theta_i)) - dy(ref)||\n            elif self._image_transform_reference_fit_term in [\"partial_derivative\"]:\n\n                # Used to get initial intensity correction\n                # parameters/coefficients\n                gradient_magnitude_stack_sitk = \\\n                    self._gradient_magnitude_filter_sitk.Execute(\n                        self._stack.sitk)\n                self._init_stack = st.Stack.from_sitk_image(\n                    gradient_magnitude_stack_sitk,\n                    \"GradMagn_\" + self._stack.get_filename(),\n                    self._stack.sitk_mask)\n                gradient_magnitude_reference_sitk = \\\n                    self._gradient_magnitude_filter_sitk.Execute(\n                        self._reference.sitk)\n                self._init_reference = st.Stack.from_sitk_image(\n                    gradient_magnitude_reference_sitk,\n                    \"GradMagn_\" + self._reference.get_filename(),\n                    self._stack.sitk_mask)\n\n                # Used to get initial transform parameters\n                self._init_slices_2D_stack_reference_term = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._stack,\n                        registration_image_type=\"gradient_magnitude\")\n                self._init_slices_2D_reference = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._reference,\n                        registration_image_type=\"gradient_magnitude\")\n\n                # Used to compare slice data arrays against in residual\n                # evaluation\n                dx_slices_2D_reference, dy_slices_2D_reference = \\\n                    self._get_projected_2D_slices_of_stack(\n                        self._reference,\n                        registration_image_type=\"partial_derivative\")\n                self._dx_reference_nda = np.zeros(\n                    np.array(self._reference.sitk.GetSize())[::-1])\n                self._dy_reference_nda = np.zeros_like(self._dx_reference_nda)\n                for i in range(0, self._N_slices):\n                    self._dx_reference_nda[i, :, :] = sitk.GetArrayFromImage(\n                        dx_slices_2D_reference[i].sitk)\n                    self._dy_reference_nda[i, :, :] = sitk.GetArrayFromImage(\n                        dy_slices_2D_reference[i].sitk)\n\n            # Resampling grid, i.e. the fixed image space during registration\n            self._slice_grid_2D_sitk = sitk.Image(\n                self._init_slices_2D_reference[0].sitk)\n\n        else:\n            # Resampling grid, i.e. the fixed image space during registration\n            self._slice_grid_2D_sitk = sitk.Image(self._slices_2D[0].sitk)\n\n        # Get inital transform and the respective initial transform parameters\n        # used for further optimisation\n        self._transforms_2D_sitk, parameters = \\\n            self._get_initial_transforms_and_parameters[\n                self._transform_initializer_type]()\n\n        if self._intensity_correction_type_slice_neighbour_fit is not None:\n            parameters_intensity = \\\n                self._get_initial_intensity_correction_parameters[\n                    self._intensity_correction_initializer_type]()\n            parameters = np.concatenate(\n                (parameters, parameters_intensity),\n                axis=1)\n\n        # Parameters for initialization and for regularization term\n        self._parameters0_vec = parameters.flatten()\n\n        # Create copy for member variable\n        self._parameters = np.array(parameters)\n\n        # Store number of degrees of freedom for overall optimization\n        self._optimization_dofs = self._parameters.shape[1]\n\n    ##\n    # Based on the residual functions below and the chosen settings, this\n    # function returns the residual call used for the least_squares method\n    # \\date       2016-11-21 20:02:32+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The residual call.\n    #\n    def _get_residual_call(self):\n\n        alpha_neighbour = abs(float(self._alpha_neighbour))\n        alpha_parameter = abs(float(self._alpha_parameter))\n        alpha_reference = abs(float(self._alpha_reference))\n\n        # ---------------------------------------------------------------------\n        # 1) Defines the prior term on the parameters\n        if alpha_parameter > self._ZERO:\n            if self._transform_type in [\"similarity\"]:\n                self._get_residual_parameters = lambda x: np.concatenate((\n                    self._get_residual_scale(x),\n                    self._get_residual_intensity_coefficients[\n                        self._intensity_correction_type_slice_neighbour_fit](x)\n                ))\n            else:\n                self._get_residual_parameters = \\\n                    lambda x: self._get_residual_intensity_coefficients[\n                        self._intensity_correction_type_slice_neighbour_fit](x)\n\n        # ---------------------------------------------------------------------\n        # 2) Construct overall residual\n        if self._reference is None:\n            if alpha_neighbour < self._ZERO:\n                raise ValueError(\n                    \"A weight of alpha_neighbour <= 0 is not meaningful.\")\n\n            if alpha_parameter < self._ZERO:\n                residual = lambda x: self._get_residual_slice_neighbours_fit(x)\n\n            else:\n                residual = lambda x: np.concatenate((\n                    self._get_residual_slice_neighbours_fit(x),\n                    alpha_parameter / alpha_neighbour *\n                    self._get_residual_parameters(x)\n                ))\n        else:\n            # Build total residual for reference fit\n            if self._image_transform_reference_fit_term in [\"identity\"]:\n                self._get_residual_reference_fit_total = lambda x: \\\n                    self._get_residual_reference_fit(\n                        self._slices_2D,\n                        self._reference_nda,\n                        \"identity\",\n                        x)\n\n            if self._image_transform_reference_fit_term in [\"gradient_magnitude\"]:\n                self._get_residual_reference_fit_total = \\\n                    lambda x: self._get_residual_reference_fit(\n                        self._slices_2D,\n                        self._gradient_magnitude_reference_nda,\n                        \"gradient_magnitude\",\n                        x)\n\n            elif self._image_transform_reference_fit_term in [\"partial_derivative\"]:\n                self._get_residual_reference_fit_total = \\\n                    lambda x: np.concatenate((\n                        self._get_residual_reference_fit(\n                            self._slices_2D,\n                            self._dx_reference_nda,\n                            \"dx\",\n                            x),\n                        self._get_residual_reference_fit(\n                            self._slices_2D,\n                            self._dy_reference_nda,\n                            \"dy\",\n                            x)\n                    ))\n\n            # Combine all the residuals\n            if alpha_reference < self._ZERO:\n                raise ValueError(\n                    \"A weight of alpha_reference <= 0 is not meaningful in case reference is given\")\n\n            if alpha_neighbour < self._ZERO and alpha_parameter < self._ZERO:\n                residual = lambda x: self._get_residual_reference_fit_total(x)\n\n            elif alpha_neighbour > self._ZERO and alpha_parameter < self._ZERO:\n                residual = lambda x: np.concatenate((\n                    self._get_residual_reference_fit_total(x),\n                    alpha_neighbour / alpha_reference *\n                    self._get_residual_slice_neighbours_fit(x)\n                ))\n\n            elif alpha_neighbour < self._ZERO and alpha_parameter > self._ZERO:\n                residual = lambda x: np.concatenate((\n                    self._get_residual_reference_fit_total(x),\n                    alpha_parameter / alpha_reference *\n                    self._get_residual_parameters(x)\n                ))\n\n            elif alpha_neighbour > self._ZERO and alpha_parameter > self._ZERO:\n                residual = lambda x: np.concatenate((\n                    self._get_residual_reference_fit_total(x),\n                    alpha_neighbour / alpha_reference *\n                    self._get_residual_slice_neighbours_fit(x),\n                    alpha_parameter / alpha_reference *\n                    self._get_residual_parameters(x)\n                ))\n\n        return residual\n\n    ##\n    # Based on the Jacobian of the residual functions below and the chosen\n    # settings, this function returns the Jacobian call used for the\n    # least_squares method.\n    # \\date       2016-11-21 20:04:37+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The Jacobian call.\n    #\n    def _get_jacobian_residual_call(self):\n\n        alpha_neighbour = abs(float(self._alpha_neighbour))\n        alpha_parameter = abs(float(self._alpha_parameter))\n        alpha_reference = abs(float(self._alpha_reference))\n\n        # ---------------------------------------------------------------------\n        # 1) Define Jacobian of the prior term on the parameters\n        if alpha_parameter > self._ZERO:\n            if self._transform_type in [\"similarity\"]:\n                self._get_jacobian_residual_parameters = \\\n                    lambda x: np.concatenate((\n                        self._get_jacobian_residual_scale(x),\n                        self._get_jacobian_residual_intensity_coefficients[\n                            self._intensity_correction_type_slice_neighbour_fit](x)\n                    ))\n            else:\n                self._get_jacobian_residual_parameters = \\\n                    lambda x: self._get_jacobian_residual_intensity_coefficients[\n                        self._intensity_correction_type_slice_neighbour_fit](x)\n\n        # ---------------------------------------------------------------------\n        # 2) Construct overall Jacobian of residual\n        if self._reference is None:\n            self._alpha_reference = 0\n\n            if alpha_neighbour < self._ZERO:\n                raise ValueError(\n                    \"A weight of alpha_neighbour <= 0 is not meaningful.\")\n\n            if alpha_parameter < self._ZERO:\n                jacobian = \\\n                    lambda x: self._get_jacobian_residual_slice_neighbours_fit(\n                        x)\n\n            else:\n                jacobian = lambda x: np.concatenate((\n                    self._get_jacobian_residual_slice_neighbours_fit(x),\n                    alpha_parameter / alpha_neighbour *\n                    self._get_jacobian_residual_parameters(x)\n                ))\n\n        else:\n\n            if self._image_transform_reference_fit_term in [\"identity\"]:\n                self._get_jacobian_residual_reference_fit_total = \\\n                    lambda x: self._get_jacobian_residual_reference_fit(\n                        self._slices_2D, \"identity\", x)\n\n            elif self._image_transform_reference_fit_term in [\"gradient_magnitude\"]:\n                self._get_jacobian_residual_reference_fit_total = \\\n                    lambda x: self._get_jacobian_residual_reference_fit(\n                        self._slices_2D, \"gradient_magnitude\", x)\n\n            elif self._image_transform_reference_fit_term in [\"partial_derivative\"]:\n                self._get_jacobian_residual_reference_fit_total = \\\n                    lambda x: np.concatenate((\n                        self._get_jacobian_residual_reference_fit(\n                            self._slices_2D, \"dx\", x),\n                        self._get_jacobian_residual_reference_fit(\n                            self._slices_2D, \"dy\", x)\n                    ))\n\n            if alpha_reference < self._ZERO:\n                raise ValueError(\n                    \"A weight of alpha_reference <= 0 is not meaningful in case reference is given\")\n\n            if alpha_neighbour < self._ZERO and alpha_parameter < self._ZERO:\n                jacobian = \\\n                    lambda x: self._get_jacobian_residual_reference_fit_total(\n                        x)\n\n            elif alpha_neighbour > self._ZERO and alpha_parameter < self._ZERO:\n                jacobian = lambda x: np.concatenate((\n                    self._get_jacobian_residual_reference_fit_total(x),\n                    alpha_neighbour / alpha_reference *\n                    self._get_jacobian_residual_slice_neighbours_fit(x)\n                ))\n\n            elif alpha_neighbour < self._ZERO and alpha_parameter > self._ZERO:\n                jacobian = lambda x: np.concatenate((\n                    self._get_jacobian_residual_reference_fit_total(x),\n                    alpha_parameter / alpha_reference *\n                    self._get_jacobian_residual_parameters(x)\n                ))\n\n            elif alpha_neighbour > self._ZERO and alpha_parameter > self._ZERO:\n                jacobian = lambda x: np.concatenate((\n                    self._get_jacobian_residual_reference_fit_total(x),\n                    alpha_neighbour / alpha_reference *\n                    self._get_jacobian_residual_slice_neighbours_fit(x),\n                    alpha_parameter / alpha_reference *\n                    self._get_jacobian_residual_parameters(x)\n                ))\n\n        return jacobian\n\n    ##\n    # Gets the residual indicating the alignment between slices and reference.\n    # \\date       2016-11-08 20:37:49+0000\n    #\n    # It returns the stacked residual of slice_i(T(theta_i, x)) - ref(x)) for\n    # all slices i.\n    #\n    # \\param      self            The object\n    # \\param      slices_2D       The slices 2d\n    # \\param      reference_nda   The reference nda\n    # \\param      trafo           The trafo\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The residual reference fit as (N_slices * N_slice_voxels)\n    #             numpy array\n    #\n    def _get_residual_reference_fit(self,\n                                    slices_2D,\n                                    reference_nda,\n                                    trafo,\n                                    parameters_vec):\n\n        # Allocate memory for residual\n        residual = np.zeros((self._N_slices, self._N_slice_voxels))\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        # Compute residuals between each slice and reference\n        for i in range(0, self._N_slices):\n\n            # Get slice_i(T(theta_i, x))\n            self._transforms_2D_sitk[i].SetParameters(\n                parameters[i, 0:self._transform_type_dofs])\n            slice_i_sitk = sitk.Resample(\n                slices_2D[i].sitk,\n                self._slice_grid_2D_sitk,\n                self._transforms_2D_sitk[i],\n                self._interpolator_sitk)\n\n            # Apply image transform, i.e. gradients etc\n            slice_i_sitk = self._apply_image_transform[trafo](slice_i_sitk)\n\n            # Extract data array\n            slice_i_nda = sitk.GetArrayFromImage(slice_i_sitk)\n\n            # Correct intensities according to chosen model\n            slice_i_nda = self._apply_intensity_correction[\n                self._intensity_correction_type_reference_fit](\n                slice_i_nda, parameters[i, self._transform_type_dofs:])\n\n            # Compute residual slice_i(T(theta_i, x)) - ref(x))\n            residual_slice_nda = slice_i_nda - reference_nda[i, :, :]\n\n            # Incorporate mask computations\n            if self._use_stack_mask_reference_fit_term:\n                slice_i_sitk_mask = sitk.Resample(\n                    slices_2D[i].sitk_mask,\n                    self._slice_grid_2D_sitk,\n                    self._transforms_2D_sitk[i],\n                    sitk.sitkNearestNeighbor)\n                slice_i_nda_mask = sitk.GetArrayFromImage(slice_i_sitk_mask)\n                residual_slice_nda *= slice_i_nda_mask\n\n            if self._use_reference_mask:\n                residual_slice_nda *= self._reference_nda_mask[i, :, :]\n\n            # ph.show_2D_array_list([residual_slice_nda, slice_i_nda_mask, self._reference_nda_mask[i,:,:]])\n            # ph.pause()\n\n            # Set residual for current slice difference\n            residual[i, :] = residual_slice_nda.flatten()\n\n        return residual.flatten()\n\n    ##\n    # Gets the Jacobian to \\p _get_residual_reference_fit used for the\n    # least_squares method.\n    # \\date       2016-11-21 20:09:36+0000\n    #\n    # \\param      self            The object\n    # \\param      slices_2D       The slices 2d\n    # \\param      trafo           The trafo\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The jacobian residual reference fit as [N_slices *\n    #             N_slice_voxels] x [transform_type_dofs * N_slices] numpy\n    #             array\n    #\n    def _get_jacobian_residual_reference_fit(self,\n                                             slices_2D,\n                                             trafo,\n                                             parameters_vec):\n\n        # Allocate memory for Jacobian of residual\n        jacobian = np.zeros(\n            (self._N_slices * self._N_slice_voxels,\n                self._optimization_dofs * self._N_slices))\n\n        jacobian_slice_i = np.zeros(\n            (self._N_slice_voxels, self._optimization_dofs))\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        # Compute Jacobian of residuals between each slice and reference\n        for i in range(0, self._N_slices):\n\n            # Update transforms\n            parameters_slice = parameters[i, 0:self._transform_type_dofs]\n            self._transforms_2D_sitk[i].SetParameters(parameters_slice)\n            self._transforms_2D_itk[i].SetParameters(\n                itk.OptimizerParameters[itk.D](parameters_slice))\n\n            # Get slice_i(T(theta, x))\n            slice_i_sitk = sitk.Resample(\n                slices_2D[i].sitk,\n                self._slice_grid_2D_sitk,\n                self._transforms_2D_sitk[i],\n                self._interpolator_sitk)\n\n            # Apply image transform, i.e. gradients etc\n            slice_i_sitk = self._apply_image_transform[trafo](slice_i_sitk)\n\n            # Get d[slice(T(theta, x))]/dx as (Ny x Nx x dim)-array\n            dslice_i_nda = self._get_gradient_image_nda_from_sitk_image(\n                slice_i_sitk)\n\n            # Get slice data array (used for intensity correction parameter\n            # gradient)\n            slice_i_nda = sitk.GetArrayFromImage(slice_i_sitk)\n\n            # Incorporate mask computations\n            if self._use_stack_mask_reference_fit_term:\n                # Slice mask\n                slice_i_sitk_mask = sitk.Resample(\n                    slices_2D[i].sitk_mask,\n                    self._slice_grid_2D_sitk,\n                    self._transforms_2D_sitk[i],\n                    sitk.sitkNearestNeighbor)\n                slice_i_nda_mask = sitk.GetArrayFromImage(slice_i_sitk_mask)\n\n                # Mask data\n                slice_i_nda *= slice_i_nda_mask\n\n                # Mask gradient data\n                dslice_i_nda *= slice_i_nda_mask[:, :, np.newaxis]\n\n                # ph.show_2D_array_list\n\n            if self._use_reference_mask:\n                # Reference mask\n                reference_i_nda_mask = self._reference_nda_mask[i, :, :]\n\n                # Mask data\n                slice_i_nda *= reference_i_nda_mask\n\n                # Mask gradient data\n                dslice_i_nda *= reference_i_nda_mask[:, :, np.newaxis]\n\n            # Get Jacobian of slice w.r.t to transform parameters\n            jacobian_slice_nda = \\\n                self._get_gradient_with_respect_to_transform_parameters(\n                    dslice_i_nda, self._transforms_2D_itk[i], slice_i_sitk)\n\n            # Get d[slice_i(T(theta_i, x))]/dtheta_i:\n            # Add Jacobian w.r.t. to intensity correction parameters\n            jacobian_slice_i_tmp = \\\n                self._add_gradient_with_respect_to_intensity_correction_parameters[\n                    self._intensity_correction_type_reference_fit](\n                        jacobian_slice_nda, slice_i_nda)\n\n            # Second dimension is decided by intensity_correction_type_slice_neighbour_fit\n            # as being of \"higher order\"\n            # (e.g. affine for slice fit term and linear for reference fit term)\n            jacobian_slice_i[:, 0:jacobian_slice_i_tmp.shape[\n                1]] = jacobian_slice_i_tmp\n\n            # Set elements in Jacobian for entire stack\n            jacobian[\n                i * self._N_slice_voxels:\n                (i + 1) * self._N_slice_voxels,\n                i * self._optimization_dofs:\n                (i + 1) * self._optimization_dofs] = jacobian_slice_i\n\n        return jacobian\n\n    ##\n    # Gets the residual indicating the alignment between neighbouring slices.\n    # \\date       2016-11-21 20:07:41+0000\n    #\n    # It returns the stacked residual of slice_i(T(theta_i, x)) -\n    # slice_{i+1}(T(theta_{i+1}, x)) for all voxels x of all slices i.\n    #\n    # \\param      self            The object\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The residual slice neighbours fit as\n    #             (N_slices-1) * N_slice_voxels numpy array\n    #\n    def _get_residual_slice_neighbours_fit(self, parameters_vec):\n\n        # Allocate memory for residual\n        residual = np.zeros((self._N_slices - 1, self._N_slice_voxels))\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        # Update transform\n        i = 0\n        parameters_slice_i = parameters[i, 0:self._transform_type_dofs]\n        self._transforms_2D_sitk[i].SetParameters(parameters_slice_i)\n\n        # Get slice_i(T(theta_i, x)) for i=0\n        slice_i_sitk = sitk.Resample(\n            self._slices_2D[i].sitk,\n            self._slice_grid_2D_sitk,\n            self._transforms_2D_sitk[i],\n            self._interpolator_sitk)\n        slice_i_nda = sitk.GetArrayFromImage(slice_i_sitk)\n\n        # Correct intensities according to chosen model\n        slice_i_nda = self._apply_intensity_correction[\n            self._intensity_correction_type_slice_neighbour_fit](\n            slice_i_nda, parameters[i, self._transform_type_dofs:])\n\n        if self._use_stack_mask_neighbour_fit_term:\n            slice_i_sitk_mask = sitk.Resample(\n                self._slices_2D[i].sitk_mask,\n                self._slice_grid_2D_sitk,\n                self._transforms_2D_sitk[i],\n                sitk.sitkNearestNeighbor)\n            slice_i_nda_mask = sitk.GetArrayFromImage(slice_i_sitk_mask)\n\n        # Compute residuals for neighbouring slices\n        for i in range(0, self._N_slices - 1):\n\n            # Update transform\n            parameters_slice_ip1 = parameters[\n                i + 1, 0:self._transform_type_dofs]\n            self._transforms_2D_sitk[i + 1].SetParameters(parameters_slice_ip1)\n\n            # Get slice_{i+1}(T(theta_{i+1}, x))\n            slice_ip1_sitk = sitk.Resample(\n                self._slices_2D[i + 1].sitk,\n                self._slice_grid_2D_sitk,\n                self._transforms_2D_sitk[i + 1],\n                self._interpolator_sitk)\n            slice_ip1_nda = sitk.GetArrayFromImage(slice_ip1_sitk)\n\n            # Correct intensities according to chosen model\n            slice_ip1_nda = self._apply_intensity_correction[\n                self._intensity_correction_type_slice_neighbour_fit](\n                slice_ip1_nda, parameters[i + 1, self._transform_type_dofs:])\n\n            # Compute residual slice_i(T(theta_i, x)) -\n            # slice_{i+1}(T(theta_{i+1}, x))\n            residual_slice_nda = slice_i_nda - slice_ip1_nda\n\n            # Eliminate residual for non-masked regions\n            if self._use_stack_mask_neighbour_fit_term:\n                slice_ip1_sitk_mask = sitk.Resample(\n                    self._slices_2D[i + 1].sitk_mask,\n                    self._slice_grid_2D_sitk,\n                    self._transforms_2D_sitk[i + 1],\n                    sitk.sitkNearestNeighbor)\n                slice_ip1_nda_mask = sitk.GetArrayFromImage(\n                    slice_ip1_sitk_mask)\n\n                residual_slice_nda = residual_slice_nda * slice_i_nda_mask * \\\n                    slice_ip1_nda_mask\n\n                slice_i_nda_mask = slice_ip1_nda_mask\n\n            # Set residual for current slice difference\n            residual[i, :] = residual_slice_nda.flatten()\n\n            # Prepare for next iteration\n            slice_i_nda = slice_ip1_nda\n\n        return residual.flatten()\n\n    ##\n    # Gets the Jacobian to \\p _get_residual_slice_neighbours_fit used for the\n    # least_squares method.\n    # \\date       2016-11-21 20:08:48+0000\n    #\n    # \\param      self            The object\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The Jacobian residual slice neighbours fit as [(N_slices-1) *\n    #             N_slice_voxels] x [transform_type_dofs * N_slices] numpy\n    #             array\n    #\n    def _get_jacobian_residual_slice_neighbours_fit(self, parameters_vec):\n\n        # Allocate memory for Jacobian of residual\n        jacobian = np.zeros((\n            (self._N_slices - 1) * self._N_slice_voxels,\n            self._optimization_dofs * self._N_slices))\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        # Update transforms\n        i = 0\n        parameters_slice_i = parameters[i, 0:self._transform_type_dofs]\n        self._transforms_2D_sitk[i].SetParameters(parameters_slice_i)\n        self._transforms_2D_itk[i].SetParameters(\n            itk.OptimizerParameters[itk.D](parameters_slice_i))\n\n        # Get d[slice_i(T(theta_i, x))]/dtheta_i\n        jacobian_slice_i = self._get_jacobian_slice_in_slice_neighbours_fit(\n            self._slices_2D[i],\n            self._transforms_2D_sitk[i],\n            self._transforms_2D_itk[i])\n\n        # Compute Jacobian of residuals\n        for i in range(0, self._N_slices - 1):\n\n            # Update transforms\n            parameters_slice_ip1 = parameters[\n                i + 1, 0:self._transform_type_dofs]\n            self._transforms_2D_sitk[i + 1].SetParameters(parameters_slice_ip1)\n            self._transforms_2D_itk[i + 1].SetParameters(\n                itk.OptimizerParameters[itk.D](parameters_slice_ip1))\n\n            # Get d[slice_{i+1}(T(theta_{i+1}, x))]/dtheta_{i+1}\n            jacobian_slice_ip1 = \\\n                self._get_jacobian_slice_in_slice_neighbours_fit(\n                    self._slices_2D[i + 1],\n                    self._transforms_2D_sitk[i + 1],\n                    self._transforms_2D_itk[i + 1])\n\n            # Set elements in Jacobian for entire stack\n            jacobian[i * self._N_slice_voxels:\n                     (i + 1) * self._N_slice_voxels,\n                     i * self._optimization_dofs:\n                     (i + 1) * self._optimization_dofs] = jacobian_slice_i\n            jacobian[i * self._N_slice_voxels:\n                     (i + 1) * self._N_slice_voxels,\n                     (i + 1) * self._optimization_dofs:\n                     (i + 2) * self._optimization_dofs] = -jacobian_slice_ip1\n\n            # Prepare for next iteration\n            jacobian_slice_i = jacobian_slice_ip1\n\n        return jacobian\n\n    ##\n    # Gets the Jacobian of a slice based on the spatial transformation.\n    # \\date       2016-11-21 18:23:53+0000\n    #\n    # Compute the Jacobian\n    # \\f$ \\frac{dI(T(\\theta, x))}{d\\theta} =\n    # \\frac{dI}{dy}(T(\\theta,x))\\,\\frac{dT}{d\\theta}(\\theta, x)\n    # \\f$. It also considers the (affine) intensity correction model\n    #\n    # \\param      self            The object\n    # \\param      slice           The slice\n    # \\param      transform_sitk  The transform sitk\n    # \\param      transform_itk   The transform itk\n    #\n    # \\return     The Jacobian of a slice as (N_slice_voxels x\n    #             transform_type_dofs)-array.\n    #\n    def _get_jacobian_slice_in_slice_neighbours_fit(self,\n                                                    slice,\n                                                    transform_sitk,\n                                                    transform_itk):\n\n        # Get slice(T(theta, x))\n        slice_sitk = sitk.Resample(\n            slice.sitk,\n            self._slice_grid_2D_sitk,\n            transform_sitk,\n            self._interpolator_sitk)\n\n        # Get d[slice(T(theta, x))]/dx as (Ny x Nx x dim)-array\n        dslice_nda = self._get_gradient_image_nda_from_sitk_image(slice_sitk)\n\n        # Get slice data array (used for intensity correction parameter\n        # gradient)\n        slice_nda = sitk.GetArrayFromImage(slice_sitk)\n\n        if self._use_stack_mask_neighbour_fit_term:\n            slice_sitk_mask = sitk.Resample(\n                slice.sitk_mask,\n                self._slice_grid_2D_sitk,\n                transform_sitk,\n                sitk.sitkNearestNeighbor)\n            slice_nda_mask = sitk.GetArrayFromImage(slice_sitk_mask)\n\n            # slice_nda *= slice_nda_mask[:,:,np.newaxis]\n            slice_nda *= slice_nda_mask\n\n        # Get Jacobian of slice w.r.t to transform parameters\n        jacobian_slice_nda = \\\n            self._get_gradient_with_respect_to_transform_parameters(\n                dslice_nda, transform_itk, slice_sitk)\n\n        # Add Jacobian w.r.t. to intensity correction parameters\n        jacobian_slice_nda = \\\n            self._add_gradient_with_respect_to_intensity_correction_parameters[\n                self._intensity_correction_type_slice_neighbour_fit](\n                    jacobian_slice_nda, slice_nda)\n\n        return jacobian_slice_nda\n\n    ##\n    # Gets the gradient with respect to transform parameters of all voxels\n    # within a slice.\n    # \\date       2017-07-15 23:03:10+0100\n    #\n    # \\param      self           The object\n    # \\param      dslice_nda     The dslice nda\n    # \\param      transform_itk  The transform itk\n    # \\param      slice_sitk     The slice sitk\n    #\n    # \\return     The gradient with respect to transform parameters;\n    #             (N_slice_voxels x transform_type_dofs) numpy array\n    #\n    def _get_gradient_with_respect_to_transform_parameters(self,\n                                                           dslice_nda,\n                                                           transform_itk,\n                                                           slice_sitk):\n\n        # Reshape to (N_slice_voxels x dim)-array\n        dslice_nda = dslice_nda.reshape(self._N_slice_voxels, -1)\n\n        # Get d[T(theta, x)]/dtheta as (N_slice_voxels x dim x\n        # transform_type_dofs)-array\n        dT_nda = \\\n            sitkh.get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image(\n                transform_itk, slice_sitk)\n\n        # Compute Jacobian for slice as (N_slice_voxels x\n        # transform_type_dofs)-array\n        jacobian_slice = np.sum(dslice_nda[:, :, np.newaxis] * dT_nda, axis=1)\n\n        return jacobian_slice\n\n    def _get_gradient_image_nda_from_sitk_image(self, slice_sitk):\n\n        # Compute d[slice(T(theta, x))]/dx\n        dslice_sitk = self._gradient_image_filter_sitk.Execute(slice_sitk)\n\n        # Get associated (Ny x Nx x dim)-array\n        dslice_nda = sitk.GetArrayFromImage(dslice_sitk)\n\n        return dslice_nda\n\n    # ##\n    # # Gets the residual parameters for all optimization parameters\n    # # \\date       2016-11-21 18:09:24+0000\n    # #\n    # # \\param      self            The object\n    # # \\param      parameters_vec  The parameters vector\n    # #\n    # # \\return     The residual parameters.\n    # #\n    # def _get_residual_parameters(self, parameters_vec):\n\n    #     ## Reshape parameters for easier access\n    #     parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n    #     parameters_prior = np.zeros(parameters.shape)\n\n    #     ## Prior for transform parameters\n    #     parameters_prior[:, 0: self._transform_type_dofs] = self._parameters_prior_transform[self._transform_type]\n\n    #     ## Prior for intensity correction parameters\n    #     parameters_prior[:,self._transform_type_dofs:] = self._parameters_prior_intensity_correction[self._intensity_correction_type_slice_neighbour_fit]\n\n    #     # return parameters_vec/self._parameters0_vec\n    #     return parameters_vec - parameters_prior.flatten()\n\n    # def _get_jacobian_residual_parameters(self, parameters_vec):\n    #     ## Reshape parameters for easier access\n    #     parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n    #     parameters_prior = np.zeros(parameters.shape)\n\n    #     # parameters_prior[:, self._transform_type_dofs:] = np.array([10.,50.])\n\n    #     ## Allocate memory for Jacobian of residual\n    #     jacobian = np.eye(self._N_slices*self._optimization_dofs)\n    #     # jacobian = np.diag(1/parameters_prior.flatten())\n\n    #     return jacobian\n\n    ##\n    # Gets the residual scale.\n    # \\date       2016-11-21 18:09:42+0000\n    #\n    # \\param      self            The object\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The residual scale.\n    #\n    def _get_residual_scale(self, parameters_vec):\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        parameters_scale = parameters[:, 0]\n\n        return parameters_scale - self._prior_scale\n\n    def _get_jacobian_residual_scale(self, parameters_vec):\n\n        jacobian = np.zeros(\n            (self._N_slices, self._N_slices * self._optimization_dofs))\n\n        for i in range(0, self._N_slices):\n            jacobian[i, i * self._optimization_dofs] = 1\n\n        return jacobian\n\n    ##\n    # Gets the residual intensity coefficients for different intensity\n    # correction models.\n    # \\date       2016-11-21 18:12:27+0000\n    #\n    # \\param      self            The object\n    # \\param      parameters_vec  The parameters vector\n    #\n    # \\return     The residual intensity coefficients for different types.\n    #\n    def _get_residual_intensity_coefficients_None(self, parameters_vec):\n        return np.zeros(1)\n\n    def _get_jacobian_residual_intensity_coefficients_None(self,\n                                                           parameters_vec):\n        return np.zeros((1, self._N_slices * self._optimization_dofs))\n\n    def _get_residual_intensity_coefficients_linear(self, parameters_vec):\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        parameters_coefficients = parameters[:, self._transform_type_dofs]\n\n        return parameters_coefficients - \\\n            self._prior_intensity_correction_coefficients[0]\n\n    def _get_jacobian_residual_intensity_coefficients_linear(self,\n                                                             parameters_vec):\n\n        jacobian = np.zeros(\n            (self._N_slices, self._N_slices * self._optimization_dofs))\n\n        for i in range(0, self._N_slices):\n            jacobian[i, self._transform_type_dofs +\n                     i * self._optimization_dofs] = 1\n\n        return jacobian\n\n    def _get_residual_intensity_coefficients_affine(self, parameters_vec):\n\n        # Reshape parameters for easier access\n        parameters = parameters_vec.reshape(-1, self._optimization_dofs)\n\n        parameters_coefficients = parameters[:, self._transform_type_dofs:]\n\n        return (parameters_coefficients -\n                self._prior_intensity_correction_coefficients).flatten()\n\n    def _get_jacobian_residual_intensity_coefficients_affine(self,\n                                                             parameters_vec):\n\n        jacobian = np.zeros(\n            (2 * self._N_slices, self._N_slices * self._optimization_dofs))\n\n        for i in range(0, self._N_slices):\n            jacobian[2 * i, self._transform_type_dofs +\n                     i * self._optimization_dofs] = 1\n            jacobian[2 * i + 1, self._transform_type_dofs +\n                     i * self._optimization_dofs + 1] = 1\n\n        return jacobian\n\n    ##\n    # Compute several transforms on image like identity, \\f$ \\partial_x \\f$,\n    # \\f$ \\partial_y \\f$ and \\f$ |\\nabla | \\f$.\n    # \\date       2016-12-01 03:08:50+0000\n    #\n    # \\param      self           The object\n    # \\param      slice_2D_sitk  The slice 2d sitk\n    #\n    def _apply_image_transform_identity(self, slice_2D_sitk):\n        return slice_2D_sitk\n\n    def _apply_image_transform_dx(self, slice_2D_sitk):\n        dx_slice_2D_sitk = self._get_dx_image_sitk(slice_2D_sitk)\n        # Debug\n        # sitkh.show_sitk_image([slice_2D_sitk, dx_slice_2D_sitk], title=[\"original\", \"dx\"])\n        return dx_slice_2D_sitk\n\n    def _apply_image_transform_dy(self, slice_2D_sitk):\n        dy_slice_2D_sitk = self._get_dy_image_sitk(slice_2D_sitk)\n        # Debug\n        # sitkh.show_sitk_image([slice_2D_sitk, dy_slice_2D_sitk], title=[\"original\", \"dy\"])\n        return dy_slice_2D_sitk\n\n    def _apply_image_transform_gradient_magnitude(self, slice_2D_sitk):\n        gradient_magnitude_slice_2D_sitk = \\\n            self._gradient_magnitude_filter_sitk.Execute(\n                slice_2D_sitk)\n        # Debug\n        # sitkh.show_sitk_image([slice_2D_sitk, gradient_magnitude_slice_2D_sitk], title=[\"original\", \"gradient_magnitude\"])\n        return gradient_magnitude_slice_2D_sitk\n\n    def _get_dx_image_sitk(self, image_sitk):\n        dimage_sitk = self._gradient_image_filter_sitk.Execute(image_sitk)\n        dx_image_sitk = sitk.VectorIndexSelectionCast(dimage_sitk, 0)\n\n        return dx_image_sitk\n\n    def _get_dy_image_sitk(self, image_sitk):\n        dimage_sitk = self._gradient_image_filter_sitk.Execute(image_sitk)\n        dy_image_sitk = sitk.VectorIndexSelectionCast(dimage_sitk, 1)\n\n        return dy_image_sitk\n\n    ##\n    # Calculates the statistics of residuals based on ell^2 norm\n    # \\date       2016-11-30 14:16:20+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The statistics residuals ell 2.\n    #\n    def _compute_statistics_residuals_ell2(self):\n\n        self._final_cost = 0\n\n        if self._alpha_reference > self._ZERO:\n            self._residual_reference_fit_ell2 = np.sum(\n                self._get_residual_reference_fit_total(\n                    self._parameters.flatten())**2)\n            self._final_cost += self._alpha_reference * \\\n                self._residual_reference_fit_ell2\n\n        if self._alpha_neighbour > self._ZERO:\n            self._residual_slice_neighbours_ell2 = np.sum(\n                self._get_residual_slice_neighbours_fit(\n                    self._parameters.flatten())**2)\n            self._final_cost += self._alpha_neighbour * \\\n                self._residual_slice_neighbours_ell2\n\n        if self._alpha_parameter > self._ZERO:\n            self._residual_paramters_ell2 = np.sum(\n                self._get_residual_parameters(self._parameters.flatten())**2)\n            self._final_cost += self._alpha_parameter * \\\n                self._residual_paramters_ell2\n\n    ##\n    #       Gets the initial parameters for 'None', i.e. for identity\n    #             transform.\n    # \\date       2016-11-08 15:06:54+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The initial parameters corresponding to identity transform as\n    #             (N_slices x DOF)-array\n    #\n    def _get_initial_transforms_and_parameters_identity(self):\n\n        # Create list of identity transforms for all slices\n        transforms_2D_sitk = [None] * self._N_slices\n\n        # Get list of identity transform parameters for all slices\n        parameters = np.zeros((self._N_slices, self._transform_type_dofs))\n        for i in range(0, self._N_slices):\n            transforms_2D_sitk[i] = self._new_transform_sitk[\n                self._transform_type]()\n            parameters[i, :] = transforms_2D_sitk[i].GetParameters()\n\n        return transforms_2D_sitk, parameters\n\n    ##\n    # Gets the initial parameters for either 'GEOMETRY' or 'MOMENTS'.\n    # \\date       2016-11-08 15:08:07+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The initial parameters corresponding to 'GEOMETRY' or\n    #             'MOMENTS' as (N_slices x DOF)-array\n    #\n    def _get_initial_transforms_and_parameters_geometry_moments(self):\n\n        transform_initializer_type_sitk = \\\n            self._dictionary_transform_initializer_type_sitk[\n                self._transform_initializer_type]\n\n        # Create list of identity transforms\n        transforms_2D_sitk = [self._new_transform_sitk[\n            self._transform_type]()] * self._N_slices\n\n        # Get list of identity transform parameters for all slices\n        parameters = np.zeros((self._N_slices, self._transform_type_dofs))\n\n        # Set identity parameters for first slice\n        parameters[0, :] = transforms_2D_sitk[0].GetParameters()\n\n        # No reference is given and slices are initialized to align with\n        # neighbouring slice\n        if self._reference is None:\n\n            # Create identity transform for first slice\n            compensation_transform_sitk = self._new_transform_sitk[\n                self._transform_type]()\n\n            # First slice is kept at position and others are aligned\n            # accordingly\n            for i in range(1, self._N_slices):\n\n                # Take into account the initialization of slice i-1\n                slice_im1_sitk = sitk.Image(self._slices_2D[i - 1].sitk)\n                if self._use_stack_mask_neighbour_fit_term:\n                    slice_im1_sitk *= sitk.Cast(\n                        self._slices_2D[i - 1].sitk_mask,\n                        slice_im1_sitk.GetPixelIDValue())\n                slice_im1_sitk = sitkh.get_transformed_sitk_image(\n                    slice_im1_sitk, compensation_transform_sitk)\n\n                # Use sitk.CenteredTransformInitializerFilter to get initial\n                # transform\n                fixed_sitk = slice_im1_sitk\n                moving_sitk = sitk.Image(self._slices_2D[i].sitk)\n                if self._use_stack_mask_neighbour_fit_term:\n                    moving_sitk *= sitk.Cast(self._slices_2D[i].sitk_mask,\n                                             moving_sitk.GetPixelIDValue())\n                initial_transform_sitk = self._new_transform_sitk[\n                    self._transform_type]()\n                operation_mode_sitk = eval(\n                    \"sitk.CenteredTransformInitializerFilter.\" +\n                    transform_initializer_type_sitk)\n\n                # Get transform\n                try:\n                    # For operation_mode_sitk=\"MOMENTS\" errors can occur!\n                    initial_transform_sitk = sitk.CenteredTransformInitializer(\n                        fixed_sitk, moving_sitk, initial_transform_sitk, operation_mode_sitk)\n                except:\n                    print(\"WARNING: Slice %d/%d\" % (i, self._N_slices - 1))\n                    print(\"\\tsitk.CenteredTransformInitializerFilter with \" +\n                          transform_initializer_type_sitk +\n                          \" does not work. Identity transform is used instead for initialization\")\n                    initial_transform_sitk = self._new_transform_sitk[\n                        self._transform_type]()\n                transforms_2D_sitk[i] = eval(\n                    \"sitk.\" + initial_transform_sitk.GetName() +\n                    \"(initial_transform_sitk)\")\n\n                # Get parameters\n                parameters[i, :] = transforms_2D_sitk[i].GetParameters()\n\n                # Store compensation transform for subsequent slice\n                compensation_transform_sitk.SetParameters(\n                    transforms_2D_sitk[i].GetParameters())\n                compensation_transform_sitk.SetFixedParameters(\n                    transforms_2D_sitk[i].GetFixedParameters())\n                compensation_transform_sitk = eval(\n                    \"sitk.\" + compensation_transform_sitk.GetName() +\n                    \"(compensation_transform_sitk.GetInverse())\")\n\n        # Initialize transform to match each slice with the reference\n        else:\n\n            # print self._use_reference_mask\n            # print self._use_stack_mask_reference_fit_term\n            for i in range(0, self._N_slices):\n\n                # Use sitk.CenteredTransformInitializerFilter to get initial\n                # transform\n                fixed_sitk = self._init_slices_2D_reference[i].sitk\n                if self._use_reference_mask:\n                    fixed_sitk *= sitk.Cast(\n                        self._init_slices_2D_reference[i].sitk_mask,\n                        fixed_sitk.GetPixelIDValue())\n                moving_sitk = self._init_slices_2D_stack_reference_term[i].sitk\n                if self._use_stack_mask_reference_fit_term:\n                    moving_sitk *= sitk.Cast(\n                        self._init_slices_2D_stack_reference_term[i].sitk_mask,\n                        moving_sitk.GetPixelIDValue())\n                initial_transform_sitk = self._new_transform_sitk[\n                    self._transform_type]()\n                operation_mode_sitk = eval(\n                    \"sitk.CenteredTransformInitializerFilter.\" +\n                    transform_initializer_type_sitk)\n\n                # Get transform\n                try:\n                    # For operation_mode_sitk=\"MOMENTS\" errors can occur!\n                    initial_transform_sitk = sitk.CenteredTransformInitializer(\n                        fixed_sitk, moving_sitk, initial_transform_sitk, operation_mode_sitk)\n                except:\n                    print(\"WARNING: Slice %d/%d\" % (i, self._N_slices - 1))\n                    print(\"\\tsitk.CenteredTransformInitializerFilter with \" +\n                          transform_initializer_type_sitk +\n                          \" does not work. Identity transform is used instead for initialization\")\n                    initial_transform_sitk = \\\n                        self._new_transform_sitk[self._transform_type]()\n                transforms_2D_sitk[i] = eval(\n                    \"sitk.\" + initial_transform_sitk.GetName() +\n                    \"(initial_transform_sitk)\")\n\n                # Get parameters\n                parameters[i, :] = transforms_2D_sitk[i].GetParameters()\n\n        return transforms_2D_sitk, parameters\n\n    ##\n    # Gets the initial intensity correction parameters.\n    # \\date       2016-11-10 02:38:17+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The initial intensity correction parameters as (N_slices x\n    #             DOF)-array with DOF being either 1 (linear) or 2 (affine)\n    #\n    def _get_initial_intensity_correction_parameters_None(self):\n\n        # Set intensity correction parameters to identity\n        if self._intensity_correction_type_slice_neighbour_fit in [\"linear\"]:\n            return np.ones((self._N_slices, 1))\n\n        # affine intensity correction type requires additional column (but set\n        # to zero)\n        elif self._intensity_correction_type_slice_neighbour_fit in [\"affine\"]:\n            return np.concatenate((np.ones((self._N_slices, 1)),\n                                   np.zeros((self._N_slices, 1))),\n                                  axis=1)\n\n    def _get_initial_intensity_correction_parameters_linear(self):\n\n        if self._reference is None:\n            print(\n                \"No reference given. Initial intensity correction parameters are set to identity\")\n            intensity_corrections_coefficients = \\\n                self._get_initial_intensity_correction_parameters_None()\n\n        else:\n            intensity_correction = ic.IntensityCorrection(\n                stack=self._init_stack,\n                reference=self._init_reference.get_resampled_stack_from_slices(\n                    resampling_grid=self._init_stack.sitk),\n                use_individual_slice_correction=True,\n                use_verbose=False)\n            intensity_correction.run_linear_intensity_correction()\n            intensity_corrections_coefficients = intensity_correction.get_intensity_correction_coefficients()\n\n            # affine intensity correction type requires additional column (but\n            # set to zero)\n            if self._intensity_correction_type_slice_neighbour_fit in [\"affine\"]:\n                intensity_corrections_coefficients = np.concatenate(\n                    (intensity_corrections_coefficients,\n                        np.zeros((self._N_slices, 1))),\n                    axis=1)\n\n        return intensity_corrections_coefficients\n\n    def _get_initial_intensity_correction_parameters_affine(self):\n\n        if self._reference is not None:\n            intensity_correction = ic.IntensityCorrection(\n                stack=self._init_stack,\n                reference=self._init_reference.get_resampled_stack_from_slices(\n                    resampling_grid=self._init_stack.sitk),\n                use_individual_slice_correction=False,\n                use_verbose=False)\n            intensity_correction.run_affine_intensity_correction()\n            intensity_corrections_coefficients = \\\n                intensity_correction.get_intensity_correction_coefficients()\n\n        else:\n            print(\n                \"No reference given. Initial intensity correction parameters are set to identity\")\n            intensity_corrections_coefficients = np.ones((self._N_slices, 1))\n\n        return intensity_corrections_coefficients\n\n    ##\n    # Correct intensity implementations\n    # \\date       2016-11-10 23:01:34+0000\n    #\n    # \\param      self                     The object\n    # \\param      slice_nda                The slice nda\n    # \\param      correction_coefficients  The correction coefficients\n    #\n    # \\return     intensity corrected slice / 2D data array\n    #\n    def _apply_intensity_correction_None(self,\n                                         slice_nda,\n                                         correction_coefficients):\n        return slice_nda\n\n    def _apply_intensity_correction_linear(self,\n                                           slice_nda,\n                                           correction_coefficients):\n        return slice_nda * correction_coefficients[0]\n\n    def _apply_intensity_correction_affine(self,\n                                           slice_nda,\n                                           correction_coefficients):\n        return slice_nda * correction_coefficients[0] + \\\n            correction_coefficients[1]\n\n    ##\n    # Adds the Jacobian w.r.t to the intensity correction coefficients\n    # depending on the chosen correction model to the existing Jacobian.\n    # \\date       2016-11-21 19:47:41+0000\n    #\n    # \\param      self            The object\n    # \\param      jacobian_slice  The jacobian slice\n    # \\param      slice_sitk      The slice sitk\n    # \\param      mask_nda        The mask nda\n    #\n    # \\return     Jacobian including intensity correction parameters\n    #\n    def _add_gradient_with_respect_to_intensity_correction_parameters_None(\n            self,\n            jacobian_slice_nda,\n            slice_nda):\n        return jacobian_slice_nda\n\n    def _add_gradient_with_respect_to_intensity_correction_parameters_linear(\n            self,\n            jacobian_slice_nda,\n            slice_nda):\n\n        # Add the Jacobian w.r.t. intensity correction parameter (slope) to\n        # existing Jacobian\n        jacobian_slice_nda = np.concatenate(\n            (jacobian_slice_nda, slice_nda.reshape(self._N_slice_voxels, -1)),\n            axis=1)\n\n        return jacobian_slice_nda\n\n    def _add_gradient_with_respect_to_intensity_correction_parameters_affine(\n            self,\n            jacobian_slice_nda,\n            slice_nda):\n\n        # Add the Jacobian w.r.t. intensity correction parameter (slope) to\n        # existing Jacobian\n        jacobian_slice_nda = \\\n            self._add_gradient_with_respect_to_intensity_correction_parameters_linear(\n                jacobian_slice_nda, slice_nda)\n\n        # Add the Jacobian w.r.t. intensity correction parameter (bias) to\n        # existing Jacobian\n        jacobian_slice_nda = np.concatenate(\n            (jacobian_slice_nda, np.ones((self._N_slice_voxels, 1))), axis=1)\n\n        return jacobian_slice_nda\n\n    ##\n    # Gets the projected 2d slices of stack.\n    # \\date       2016-11-21 19:59:13+0000\n    #\n    # \\param      self                     The object\n    # \\param      stack                    The stack\n    # \\param      image_transform_reference_fit_term  Either \"identity\" or \"gradient_magnitude\"\n    #\n    # \\return     The projected 2d slices of stack.\n    #\n    def _get_projected_2D_slices_of_stack(self,\n                                          stack,\n                                          registration_image_type=\"identity\"):\n\n        slices_3D = stack.get_slices()\n        slices_2D = [None] * self._N_slices\n\n        if registration_image_type in [\"partial_derivative\"]:\n            dy_slices_2D = [None] * self._N_slices\n\n        for i in range(0, self._N_slices):\n\n            # Create copy of the slices (since its header will be updated)\n            slice_3D = sl.Slice.from_slice(slices_3D[i])\n\n            # Get transform to get axis aligned slice of original stack\n            # T_PP = self._get_TPP_transform(slice_3D.sitk)\n            T_PP = self._get_TPP_transform(slices_3D[0].sitk)\n\n            # Get current transform from image to physical space of slice\n            T_PI = sitkh.get_sitk_affine_transform_from_sitk_image(\n                slice_3D.sitk)\n\n            # Get transform to align slice with physical coordinate system\n            # (perhaps already shifted there)\n            T_PI_align = sitkh.get_composite_sitk_affine_transform(T_PP, T_PI)\n\n            # Set direction and origin of image accordingly\n            origin_3D_sitk = \\\n                sitkh.get_sitk_image_origin_from_sitk_affine_transform(\n                    T_PI_align, slice_3D.sitk)\n            direction_3D_sitk = \\\n                sitkh.get_sitk_image_direction_from_sitk_affine_transform(\n                    T_PI_align, slice_3D.sitk)\n\n            slice_3D.sitk.SetDirection(direction_3D_sitk)\n            slice_3D.sitk.SetOrigin(origin_3D_sitk)\n            slice_3D.sitk_mask.SetDirection(direction_3D_sitk)\n            slice_3D.sitk_mask.SetOrigin(origin_3D_sitk)\n\n            # Get filename and slice number for name propagation\n            filename = slice_3D.get_filename()\n            slice_number = slice_3D.get_slice_number()\n\n            slice_2D_sitk = slice_3D.sitk[:, :, 0]\n            slice_2D_sitk_mask = slice_3D.sitk_mask[:, :, 0]\n\n            if registration_image_type in [\"identity\"]:\n\n                slices_2D[i] = sl.Slice.from_sitk_image(\n                    slice_sitk=slice_2D_sitk,\n                    filename=filename,\n                    slice_number=slice_number,\n                    slice_sitk_mask=slice_2D_sitk_mask,\n                    slice_thickness=slice_3D.get_slice_thickness(),\n                )\n\n            elif registration_image_type in [\"gradient_magnitude\"]:\n                # print(\"Gradient magnitude of image\")\n                gradient_magnitude_slice_2D_sitk = \\\n                    self._gradient_magnitude_filter_sitk.Execute(slice_2D_sitk)\n\n                slices_2D[i] = sl.Slice.from_sitk_image(\n                    slice_sitk=gradient_magnitude_slice_2D_sitk,\n                    filename=\"GradMagn_\" + filename,\n                    slice_number=slice_number,\n                    slice_sitk_mask=slice_2D_sitk_mask,\n                    slice_thickness=slice_3D.get_slice_thickness(),\n                )\n\n            elif registration_image_type in [\"partial_derivative\"]:\n                # print(\"Partial derivatives of image\")\n\n                dx_slice_2D_sitk = self._get_dx_image_sitk(slice_2D_sitk)\n                dy_slice_2D_sitk = self._get_dy_image_sitk(slice_2D_sitk)\n\n                slices_2D[i] = sl.Slice.from_sitk_image(\n                    slice_sitk=dx_slice_2D_sitk,\n                    dir_input=None,\n                    filename=\"dx_\" + filename,\n                    slice_number=slice_number,\n                    slice_sitk_mask=slice_2D_sitk_mask,\n                    slice_thickness=slice_3D.get_slice_thickness(),\n                )\n                dy_slices_2D[i] = sl.Slice.from_sitk_image(\n                    slice_sitk=dy_slice_2D_sitk,\n                    dir_input=None,\n                    filename=\"dy_\" + filename,\n                    slice_number=slice_number,\n                    slice_sitk_mask=slice_2D_sitk_mask,\n                    slice_thickness=slice_3D.get_slice_thickness(),\n                )\n\n                # Debug\n                # sitkh.show_sitk_image([slice_3D.sitk[:,:,0],slice_2D_sitk], title=[\"standard_slice\"+str(i), \"gradient_magnitude_slice\"+str(i)])\n                # ph.pause()\n                # ph.killall_itksnap()\n\n        if registration_image_type in [\"partial_derivative\"]:\n            return slices_2D, dy_slices_2D\n        else:\n            return slices_2D\n\n    ##\n    # Get the 3D rigid transforms to arrive at the positions of original 3D\n    # slices starting from the physically aligned space with the main image\n    # axes.\n    # \\date       2016-09-20 23:37:05+0100\n    #\n    # The rigid transform is given as composed translation and rotation\n    # transform, i.e. T_PP = (T_t \\c irc T_rot)^{-1}.\n    #\n    # \\param      self  The object\n    #\n    # \\return     List of 3D rigid transforms (sitk.AffineTransform(3) objects)\n    #             to arrive at the positions of the original 3D slices.\n    #\n    # TODO: Change to make simpler\n    #\n    def _get_TPP_transform(self, slice_sitk):\n\n        origin_3D_sitk = np.array(slice_sitk.GetOrigin())\n        direction_3D_sitk = np.array(slice_sitk.GetDirection())\n        T_PP = sitk.AffineTransform(3)\n        T_PP.SetMatrix(direction_3D_sitk)\n        T_PP.SetTranslation(origin_3D_sitk)\n        T_PP = sitk.AffineTransform(T_PP.GetInverse())\n\n        return T_PP\n\n    \"\"\"\n    Transform specific parts from here\n    \"\"\"\n\n    def _new_rigid_transform_sitk(self):\n        return sitk.Euler2DTransform()\n\n    def _new_rigid_transform_itk(self):\n        return itk.Euler2DTransform.New()\n\n    def _new_similarity_transform_sitk(self):\n        return sitk.Similarity2DTransform()\n\n    def _new_similarity_transform_itk(self):\n        return itk.Similarity2DTransform.New()\n\n    def _new_affine_transform_sitk(self):\n        return sitk.AffineTransform(2)\n\n    def _new_affine_transform_itk(self):\n        return itk.AffineTransform.D2.New()\n\n    ##\n    # Perform motion correction based on performed registration to get motion\n    # corrected stack and associated slice transforms.\n    # \\date       2016-11-21 20:11:53+0000\n    #\n    # \\param      self  The object\n    # \\post       self._stack_corrected updated\n    # \\post       self._slice_transforms_sitk updated\n    #\n    def _apply_motion_correction(self):\n        self._apply_motion_correction_and_compute_slice_transforms[\n            self._transform_type]()\n\n    ##\n    # Apply motion correction after rigid registration\n    # \\date       2016-11-21 20:14:05+0000\n    #\n    # \\param      self  The object\n    # \\post       self._stack_corrected updated\n    # \\post       self._slice_transforms_sitk updated\n    #\n    def _apply_rigid_motion_correction_and_compute_slice_transforms(self):\n\n        stack_corrected = st.Stack.from_stack(self._stack)\n        slices_corrected = stack_corrected.get_slices()\n\n        slices = self._stack.get_slices()\n\n        slice_transforms_sitk = [None] * self._N_slices\n\n        for i in range(0, self._N_slices):\n\n            # Set transform for the 2D slice based on registration transform\n            self._transforms_2D_sitk[i].SetParameters(\n                self._parameters[i, 0:self._transform_type_dofs])\n\n            # Invert it to physically move the slice\n            transform_2D_sitk = sitk.Euler2DTransform(\n                self._transforms_2D_sitk[i].GetInverse())\n\n            # Expand to 3D transform\n            transform_3D_sitk = self._get_3D_from_2D_rigid_transform_sitk(\n                transform_2D_sitk)\n\n            # Get transform to get axis aligned slice\n            # T_PP = self._get_TPP_transform(slices[i].sitk)\n            T_PP = self._get_TPP_transform(slices[0].sitk)\n\n            # Compose to 3D in-plane transform\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                transform_3D_sitk, T_PP)\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                sitk.AffineTransform(T_PP.GetInverse()), affine_transform_sitk)\n\n            # Update motion correction of slice\n            slices_corrected[i].update_motion_correction(affine_transform_sitk)\n\n            # Keep slice transform\n            slice_transforms_sitk[i] = affine_transform_sitk\n\n        self._stack_corrected = stack_corrected\n        self._slice_transforms_sitk = slice_transforms_sitk\n\n    ##\n    # Apply motion correction after similarity registration\n    # \\date       2016-11-21 20:14:42+0000\n    #\n    # \\param      self  The object\n    # \\post       self._stack_corrected updated\n    # \\post       self._slice_transforms_sitk updated\n    # \\return     { description_of_the_return_value }\n    #\n    def _apply_similarity_motion_correction_and_compute_slice_transforms(self):\n\n        stack_corrected = st.Stack.from_stack(self._stack)\n        slices_corrected = stack_corrected.get_slices()\n\n        slices = self._stack.get_slices()\n\n        slice_transforms_sitk = [None] * self._N_slices\n\n        for i in range(0, self._N_slices):\n\n            # Set transform for the 2D slice based on registration transform\n            self._transforms_2D_sitk[i].SetParameters(\n                self._parameters[i, 0:self._transform_type_dofs])\n\n            # Invert it to physically move the slice\n            similarity_2D_sitk = sitk.Similarity2DTransform(\n                self._transforms_2D_sitk[i].GetInverse())\n\n            # Convert to 2D rigid registration transform\n            scale = similarity_2D_sitk.GetScale()\n            origin = np.array(self._slices_2D[i].sitk.GetOrigin())\n            center = np.array(similarity_2D_sitk.GetCenter())\n            angle = similarity_2D_sitk.GetAngle()\n            translation = np.array(similarity_2D_sitk.GetTranslation())\n            R = np.array(similarity_2D_sitk.GetMatrix()).reshape(2, 2) / scale\n\n            # if self._use_verbose:\n            #     print(\"Slice %2d/%d: in-plane scaling factor = %.3f\" %(i, self._N_slices-1, 1/scale))\n\n            rigid_2D_sitk = sitk.Euler2DTransform()\n            rigid_2D_sitk.SetAngle(angle)\n            rigid_2D_sitk.SetTranslation(\n                scale * R.dot(origin - center) - R.dot(origin) + translation + center)\n\n            # Expand to 3D rigid transform\n            rigid_3D_sitk = self._get_3D_from_2D_rigid_transform_sitk(\n                rigid_2D_sitk)\n\n            # Get transform to get axis aligned slice\n            # T_PP = self._get_TPP_transform(slices[i].sitk)\n            T_PP = self._get_TPP_transform(slices[0].sitk)\n\n            # Compose to 3D in-plane transform\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                rigid_3D_sitk, T_PP)\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                sitk.AffineTransform(T_PP.GetInverse()), affine_transform_sitk)\n\n            # Update motion correction of slice\n            slices_corrected[i].update_motion_correction(affine_transform_sitk)\n\n            # Update spacing of slice accordingly\n            spacing = np.array(slices[i].sitk.GetSpacing())\n            spacing[0:-1] *= scale\n\n            slices_corrected[i].sitk.SetSpacing(spacing)\n            slices_corrected[i].sitk_mask.SetSpacing(spacing)\n            slices_corrected[i].itk = sitkh.get_itk_from_sitk_image(\n                slices_corrected[i].sitk)\n            slices_corrected[i].itk_mask = \\\n                sitkh.get_itk_from_sitk_image(slices_corrected[i].sitk_mask)\n\n            # Update affine transform (including scaling information)\n            affine_3D_sitk = sitk.AffineTransform(3)\n            affine_matrix_sitk = np.array(\n                rigid_3D_sitk.GetMatrix()).reshape(3, 3)\n            affine_matrix_sitk[0:-1, 0:-1] *= scale\n            affine_3D_sitk.SetMatrix(affine_matrix_sitk.flatten())\n            affine_3D_sitk.SetCenter(rigid_3D_sitk.GetCenter())\n            affine_3D_sitk.SetTranslation(rigid_3D_sitk.GetTranslation())\n\n            affine_3D_sitk = sitkh.get_composite_sitk_affine_transform(\n                affine_3D_sitk, T_PP)\n            affine_3D_sitk = sitkh.get_composite_sitk_affine_transform(\n                sitk.AffineTransform(T_PP.GetInverse()), affine_3D_sitk)\n\n            # Keep affine slice transform\n            slice_transforms_sitk[i] = affine_3D_sitk\n\n        self._stack_corrected = stack_corrected\n        self._slice_transforms_sitk = slice_transforms_sitk\n\n    ##\n    # Apply motion correction after affine registration\n    # \\date       2016-11-21 20:14:05+0000\n    #\n    # \\param      self  The object\n    # \\post       self._stack_corrected updated\n    # \\post       self._slice_transforms_sitk updated\n    #\n    def _apply_affine_motion_correction_and_compute_slice_transforms(self):\n\n        stack_corrected = st.Stack.from_stack(self._stack)\n        slices_corrected = stack_corrected.get_slices()\n\n        slices = self._stack.get_slices()\n\n        slice_transforms_sitk = [None] * self._N_slices\n\n        for i in range(0, self._N_slices):\n\n            # Set transform for the 2D slice based on registration transform\n            self._transforms_2D_sitk[i].SetParameters(\n                self._parameters[i, 0:self._transform_type_dofs])\n\n            # Invert it to physically move the slice\n            transform_2D_sitk = sitk.AffineTransform(\n                self._transforms_2D_sitk[i].GetInverse())\n\n            # Expand to 3D transform\n            transform_3D_sitk = self._get_3D_from_2D_affine_transform_sitk(\n                transform_2D_sitk)\n\n            # Get transform to get axis aligned slice\n            # T_PP = self._get_TPP_transform(slices[i].sitk)\n            T_PP = self._get_TPP_transform(slices[0].sitk)\n\n            # Compose to 3D in-plane transform\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                transform_3D_sitk, T_PP)\n            affine_transform_sitk = sitkh.get_composite_sitk_affine_transform(\n                sitk.AffineTransform(T_PP.GetInverse()), affine_transform_sitk)\n\n            # Update motion correction of slice\n            slices_corrected[i].update_motion_correction(affine_transform_sitk)\n\n            # Keep slice transform\n            slice_transforms_sitk[i] = affine_transform_sitk\n\n        self._stack_corrected = stack_corrected\n        self._slice_transforms_sitk = slice_transforms_sitk\n\n    ##\n    # Create 3D from 2D transform.\n    # \\date       2016-09-20 23:18:55+0100\n    #\n    # The generated 3D transform performs in-plane operations in case the\n    # physical coordinate system is aligned with the axis of the stack/slice\n    #\n    # \\param      self                     The object\n    # \\param      rigid_transform_2D_sitk  sitk.Euler2DTransform object\n    #\n    # \\return     sitk.Euler3DTransform object.\n    #\n    def _get_3D_from_2D_rigid_transform_sitk(self, rigid_transform_2D_sitk):\n\n        # Get parameters of 2D registration\n        angle_z, translation_x, translation_y = \\\n            rigid_transform_2D_sitk.GetParameters()\n        center_x, center_y = rigid_transform_2D_sitk.GetCenter()\n\n        # Expand obtained translation to 3D vector\n        translation_3D = (translation_x, translation_y, 0)\n        center_3D = (center_x, center_y, 0)\n\n        # Create 3D rigid transform based on 2D\n        rigid_transform_3D = sitk.Euler3DTransform()\n        rigid_transform_3D.SetRotation(0, 0, angle_z)\n        rigid_transform_3D.SetTranslation(translation_3D)\n\n        # Append zero for m_ComputeZYX = 0 (part of fixed params in SimpleITK\n        # 1.0.0)\n        rigid_transform_3D.SetFixedParameters(center_3D + (0.,))\n\n        return rigid_transform_3D\n\n    ##\n    # Create 3D from 2D transform.\n    # \\date       2016-09-20 23:18:55+0100\n    #\n    # The generated 3D transform performs in-plane operations in case the\n    # physical coordinate system is aligned with the axis of the stack/slice\n    #\n    # \\param      self                     The object\n    # \\param      rigid_transform_2D_sitk  sitk.Euler2DTransform object\n    #\n    # \\return     sitk.Euler3DTransform object.\n    #\n    def _get_3D_from_2D_affine_transform_sitk(self, affine_transform_2D_sitk):\n\n        # Get parameters of 2D registration\n        a00, a01, a10, a11, translation_x, translation_y = \\\n            affine_transform_2D_sitk.GetParameters()\n        center_x, center_y = affine_transform_2D_sitk.GetCenter()\n\n        # Expand obtained translation to 3D vector\n        translation_3D = (translation_x, translation_y, 0)\n        center_3D = (center_x, center_y, 0)\n        matrix_3D = np.eye(3).flatten()\n        matrix_3D[0] = a00\n        matrix_3D[1] = a01\n        matrix_3D[3] = a10\n        matrix_3D[4] = a11\n\n        # Create 3D affine transform based on 2D\n        affine_transform_3D = sitk.AffineTransform(3)\n        affine_transform_3D.SetMatrix(matrix_3D)\n        affine_transform_3D.SetTranslation(translation_3D)\n        affine_transform_3D.SetFixedParameters(center_3D)\n\n        return affine_transform_3D\n"
  },
  {
    "path": "niftymic/registration/niftyreg.py",
    "content": "##\n# \\file niftyreg.py\n# \\brief      Class to use registration method NiftyReg\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\n\n# Import libraries\nimport os\nimport numpy as np\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\nimport simplereg.niftyreg\n\nimport niftymic.base.stack as st\nfrom niftymic.registration.registration_method \\\n    import RegistrationMethod\nfrom niftymic.registration.registration_method \\\n    import AffineRegistrationMethod\n\n\nclass RegAladin(AffineRegistrationMethod):\n\n    def __init__(self,\n                 fixed=None,\n                 moving=None,\n                 use_fixed_mask=False,\n                 use_moving_mask=False,\n                 use_verbose=False,\n                 options=\"-voff\",\n                 registration_type=\"Rigid\",\n                 ):\n\n        AffineRegistrationMethod.__init__(self,\n                                          fixed=fixed,\n                                          moving=moving,\n                                          use_fixed_mask=use_fixed_mask,\n                                          use_moving_mask=use_moving_mask,\n                                          use_verbose=use_verbose,\n                                          registration_type=registration_type,\n                                          )\n\n        # Allowed registration types for NiftyReg\n        self._REGISTRATION_TYPES = [\"Rigid\", \"Affine\"]\n\n        self._options = options\n\n    ##\n    # Sets the options used for FLIRT\n    # \\date       2017-08-08 19:57:47+0100\n    #\n    # \\param      self     The object\n    # \\param      options  The options as string\n    #\n    def set_options(self, options):\n        self._options = options\n\n    ##\n    # Gets the options.\n    # \\date       2017-08-08 19:58:14+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The options as string.\n    #\n    def get_options(self):\n        return self._options\n\n    def _run(self):\n\n        if self._use_fixed_mask:\n            fixed_sitk_mask = self._fixed.sitk_mask\n        else:\n            fixed_sitk_mask = None\n\n        if self._use_moving_mask:\n            moving_sitk_mask = self._moving.sitk_mask\n        else:\n            moving_sitk_mask = None\n\n        options = self._options\n        if self.get_registration_type() == \"Rigid\":\n            options += \" -rigOnly\"\n\n        self._registration_method = simplereg.niftyreg.RegAladin(\n            fixed_sitk=self._fixed.sitk,\n            moving_sitk=self._moving.sitk,\n            fixed_sitk_mask=fixed_sitk_mask,\n            moving_sitk_mask=moving_sitk_mask,\n            options=options,\n            verbose=self._use_verbose,\n        )\n        try:\n            self._registration_method.run()\n        except RuntimeError as e:\n            raise RuntimeError(\n                \"%s\\n\\n\"\n                \"Check whether image/mask coverage is sufficient between the \"\n                \"images '%s' (fixed) and '%s' (moving).\\n\" % (\n                    e,\n                    self._fixed.get_filename(),\n                    self._moving.get_filename()),\n            )\n\n        self._registration_transform_sitk = \\\n            self._registration_method.get_registration_transform_sitk()\n\n    def _get_warped_moving_sitk(self):\n        return self._registration_method.get_warped_moving_sitk()\n\n\nclass RegF3D(RegistrationMethod):\n\n    def __init__(self,\n                 fixed=None,\n                 moving=None,\n                 use_fixed_mask=False,\n                 use_moving_mask=False,\n                 use_verbose=False,\n                 options=\"-voff\",\n                 ):\n\n        RegistrationMethod.__init__(self,\n                                    fixed=fixed,\n                                    moving=moving,\n                                    use_fixed_mask=use_fixed_mask,\n                                    use_moving_mask=use_moving_mask,\n                                    use_verbose=use_verbose,\n                                    )\n        self._options = options\n\n    ##\n    # Sets the options used for FLIRT\n    # \\date       2017-08-08 19:57:47+0100\n    #\n    # \\param      self     The object\n    # \\param      options  The options as string\n    #\n    def set_options(self, options):\n        self._options = options\n\n    ##\n    # Gets the options.\n    # \\date       2017-08-08 19:58:14+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The options as string.\n    #\n    def get_options(self):\n        return self._options\n\n    def _run(self):\n\n        if self._use_fixed_mask:\n            fixed_sitk_mask = self._fixed.sitk_mask\n        else:\n            fixed_sitk_mask = None\n\n        if self._use_moving_mask:\n            moving_sitk_mask = self._moving.sitk_mask\n        else:\n            moving_sitk_mask = None\n\n        options = self._options\n\n        self._registration_method = simplereg.niftyreg.RegF3D(\n            fixed_sitk=self._fixed.sitk,\n            moving_sitk=self._moving.sitk,\n            fixed_sitk_mask=fixed_sitk_mask,\n            moving_sitk_mask=moving_sitk_mask,\n            options=options,\n            verbose=self._use_verbose,\n        )\n        self._registration_method.run()\n\n        self._registration_transform_sitk = \\\n            self._registration_method.get_registration_transform_sitk()\n\n    ##\n    # Gets the warped moving image, i.e. moving image warped and resampled to\n    # the fixed grid\n    # \\date       2017-08-08 16:58:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The warped moving image as Stack/Slice object\n    #\n    def get_warped_moving(self):\n\n        warped_moving_mask_sitk = \\\n            self._registration_method.get_deformed_image_sitk(\n                fixed_sitk=self._fixed.sitk_mask,\n                moving_sitk=self._moving.sitk_mask,\n                interpolation_order=0,\n            )\n\n        if isinstance(self._moving, st.Stack):\n            warped_moving = st.Stack.from_sitk_image(\n                image_sitk=self._registration_method.get_warped_moving_sitk(),\n                filename=self._moving.get_filename(),\n                image_sitk_mask=warped_moving_mask_sitk\n            )\n        else:\n            warped_moving = sl.Slice.from_sitk_image(\n                image_sitk=self._registration_method.get_warped_moving_sitk(),\n                filename=self._moving.get_filename(),\n                image_sitk_mask=warped_moving_mask_sitk\n            )\n        return warped_moving\n"
  },
  {
    "path": "niftymic/registration/registration_method.py",
    "content": "##\n# \\file registration_method.py\n# \\brief      Abstract class to define a registration method\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\n# Import libraries\nimport numpy as np\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nimport niftymic.base.slice as sl\n\n\n##\n# Abstract class for registration methods\n# \\date       2017-08-09 11:22:51+0100\n#\nclass RegistrationMethod(object):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Store information for registration methods and initialize additional\n    # variables\n    # \\date       2017-08-09 11:23:14+0100\n    #\n    # \\param      self             The object\n    # \\param      fixed            Fixed image as Stack/Slice object\n    # \\param      moving           The moving\n    # \\param      use_fixed_mask   The use fixed mask\n    # \\param      use_moving_mask  The use moving mask\n    # \\param      use_verbose          The use_verbose\n    #\n    def __init__(self,\n                 fixed,\n                 moving,\n                 use_fixed_mask,\n                 use_moving_mask,\n                 use_verbose):\n\n        self._fixed = fixed\n        self._moving = moving\n        self._use_fixed_mask = use_fixed_mask\n        self._use_moving_mask = use_moving_mask\n        self._use_verbose = use_verbose\n\n        self._computational_time = ph.get_zero_time()\n        self._registration_method = None\n\n    ##\n    # Sets the fixed image\n    # \\date       2017-08-08 16:45:45+0100\n    #\n    # \\param      self   The object\n    # \\param      fixed  The fixed image as Stack/Slice object\n    #\n    def set_fixed(self, fixed):\n        self._fixed = fixed\n\n    ##\n    # Gets the fixed image\n    # \\date       2017-08-08 16:45:58+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The fixed image as Stack/Slice object.\n    #\n    def get_fixed(self):\n        return self._fixed\n\n    ##\n    # Sets the moving image\n    # \\date       2017-08-08 16:45:45+0100\n    #\n    # \\param      self    The object\n    # \\param      moving  The moving image as Stack/Slice object\n    #\n    #\n    def set_moving(self, moving):\n        self._moving = moving\n\n    ##\n    # Gets the moving image\n    # \\date       2017-08-08 16:45:58+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The moving image as Stack/Slice object.\n    #\n    def get_moving(self):\n        return self._moving\n\n    ##\n    # Specify whether fixed mask shall be used for registration\n    # \\date       2017-08-08 16:48:03+0100\n    #\n    # \\param      self            The object\n    # \\param      use_fixed_mask  Turn on/off use of fixed mask; bool\n    #\n    def use_fixed_mask(self, use_fixed_mask):\n        self._use_fixed_mask = use_fixed_mask\n\n    ##\n    # Specify whether moving mask shall be used for registration\n    # \\date       2017-08-08 16:48:03+0100\n    #\n    # \\param      self             The object\n    # \\param      use_moving_mask  Turn on/off use of moving mask; bool\n    #\n    def use_moving_mask(self, use_moving_mask):\n        self._use_moving_mask = use_moving_mask\n\n    ##\n    # Sets the use_verbose.\n    # \\date       2017-08-08 16:50:13+0100\n    #\n    # \\param      self     The object\n    # \\param      use_verbose  Turn on/off use_verbose output; bool\n    #\n    def use_verbose(self, use_verbose):\n        self._use_verbose = use_verbose\n\n    ##\n    # Gets the computational time it took to perform the registration\n    # \\date       2017-08-08 16:59:45+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The computational time.\n    #\n    def get_computational_time(self):\n        return self._computational_time\n\n    ##\n    # Gets the obtained registration transform.\n    # \\date       2017-08-08 16:52:36+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The registration transform as sitk object.\n    #\n    def get_registration_transform_sitk(self):\n        return self._registration_transform_sitk\n\n    ##\n    # Run the registration method\n    # \\date       2017-08-08 17:01:01+0100\n    #\n    # \\param      self  The object\n    #\n    def run(self):\n\n        if not isinstance(self._fixed, st.Stack) and \\\n                not isinstance(self._fixed, sl.Slice):\n            raise TypeError(\"Fixed image must be of type 'Stack' or 'Slice'\")\n\n        if not isinstance(self._moving, st.Stack) and \\\n                not isinstance(self._moving, sl.Slice):\n            raise TypeError(\"Moving image must be of type 'Stack' or 'Slice'\")\n\n        time_start = ph.start_timing()\n\n        # Execute registration method\n        self._run()\n\n        # Get computational time\n        self._computational_time = ph.stop_timing(time_start)\n\n        if self._use_verbose:\n            ph.print_info(\"Required computational time: %s\" %\n                          (self.get_computational_time()))\n\n    @abstractmethod\n    def _run(self):\n        pass\n\n    ##\n    # Gets the warped moving image, i.e. moving image warped and resampled to\n    # the fixed grid\n    # \\date       2017-08-08 16:58:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The warped moving image as Stack/Slice object\n    #\n    @abstractmethod\n    def get_warped_moving(self):\n        pass\n\n\n##\n# Abstract class for affine registration methods\n# \\date       2017-08-09 11:22:51+0100\n#\nclass AffineRegistrationMethod(RegistrationMethod):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Store information for registration methods and initialize additional\n    # variables\n    # \\date       2017-08-09 11:23:14+0100\n    #\n    # \\param      self             The object\n    # \\param      fixed            Fixed image as Stack/Slice object\n    # \\param      moving           The moving\n    # \\param      use_fixed_mask   The use fixed mask\n    # \\param      use_moving_mask  The use moving mask\n    # \\param      use_verbose          The use_verbose\n    #\n    def __init__(self,\n                 fixed,\n                 moving,\n                 use_fixed_mask,\n                 use_moving_mask,\n                 use_verbose,\n                 registration_type,\n                 ):\n\n        RegistrationMethod.__init__(self,\n                                    fixed=fixed,\n                                    moving=moving,\n                                    use_fixed_mask=use_fixed_mask,\n                                    use_moving_mask=use_moving_mask,\n                                    use_verbose=use_verbose,\n                                    )\n        self._registration_type = registration_type\n\n    ##\n    # Sets the registration type.\n    # \\date       2017-02-02 16:42:13+0000\n    #\n    # \\param      self               The object\n    # \\param      registration_type  The registration type\n    #\n    def set_registration_type(self, registration_type):\n        if registration_type not in self._REGISTRATION_TYPES:\n            raise ValueError(\"Possible registration types: \" +\n                             str(self._REGISTRATION_TYPES))\n        self._registration_type = registration_type\n\n    ##\n    # Gets the registration type.\n    # \\date       2017-08-08 19:58:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The registration type as string.\n    #\n    def get_registration_type(self):\n        return self._registration_type\n\n    ##\n    # Gets the warped moving image, i.e. moving image warped and resampled to\n    # the fixed grid\n    # \\date       2017-08-08 16:58:30+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The warped moving image as Stack/Slice object\n    #\n    def get_warped_moving(self):\n\n        warped_moving_sitk_mask = sitk.Resample(\n            self._moving.sitk_mask,\n            self._fixed.sitk,\n            self.get_registration_transform_sitk(),\n            sitk.sitkNearestNeighbor,\n            0,\n            self._moving.sitk_mask.GetPixelIDValue(),\n        )\n\n        if isinstance(self._moving, st.Stack):\n            warped_moving = st.Stack.from_sitk_image(\n                image_sitk=self._get_warped_moving_sitk(),\n                filename=self._moving.get_filename(),\n                image_sitk_mask=warped_moving_sitk_mask,\n                slice_thickness=self._fixed.get_slice_thickness(),\n            )\n        else:\n            warped_moving = sl.Slice.from_sitk_image(\n                image_sitk=self._get_warped_moving_sitk(),\n                filename=self._moving.get_filename(),\n                image_sitk_mask=warped_moving_sitk_mask,\n                slice_thickness=self._fixed.get_slice_thickness(),\n            )\n        return warped_moving\n\n    ##\n    # Gets the fixed image transformed by the obtained registration transform.\n    #\n    # The returned image will align the fixed image with the moving image as\n    # found during the registration.\n    # \\date       2017-08-08 16:53:21+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The transformed fixed as Stack/Slice object\n    #\n    def get_transformed_fixed(self):\n        fixed = st.Stack.from_stack(self._fixed)\n        fixed.update_motion_correction(self.get_registration_transform_sitk())\n        return fixed\n\n    @abstractmethod\n    def _get_warped_moving_sitk(self):\n        pass\n"
  },
  {
    "path": "niftymic/registration/simple_itk_registration.py",
    "content": "##\n# \\file simple_itk_registration.py\n# \\brief      Class to use registration method based on SimpleITK\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n\n\n# Import libraries\nimport os\nimport numpy as np\nimport itk\nimport SimpleITK as sitk\n\n# Used to parse variable arguments to SimpleITK object, see\n# http://stackoverflow.com/questions/20263839/python-convert-a-string-to-arguments-list:\nfrom ast import literal_eval\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\nimport simplereg.simple_itk_registration\n\nimport niftymic.base.psf as psf\nimport niftymic.base.stack as st\nfrom niftymic.registration.registration_method \\\n    import AffineRegistrationMethod\n\n\n##\n# Class to use registration method FLIRT\n# \\date       2017-08-09 11:22:33+0100\n#\nclass SimpleItkRegistration(AffineRegistrationMethod):\n\n    def __init__(\n        self,\n        fixed=None,\n        moving=None,\n        use_fixed_mask=False,\n        use_moving_mask=False,\n        registration_type=\"Rigid\",\n        interpolator=\"Linear\",\n        metric=\"Correlation\",\n        metric_params=None,\n        # optimizer=\"ConjugateGradientLineSearch\",\n        # optimizer_params={\n        #     \"learningRate\": 1,\n        #     \"numberOfIterations\": 100,\n        # },\n        optimizer=\"RegularStepGradientDescent\",\n        optimizer_params={\n            \"minStep\": 1e-6,\n            \"numberOfIterations\": 200,\n            \"gradientMagnitudeTolerance\": 1e-6,\n            \"learningRate\": 1,\n        },\n        scales_estimator=\"PhysicalShift\",\n        initializer_type=None,\n        use_oriented_psf=False,\n        use_multiresolution_framework=False,\n        shrink_factors=[2, 1],\n        smoothing_sigmas=[1, 0],\n        use_verbose=False,\n    ):\n\n        AffineRegistrationMethod.__init__(self,\n                                          fixed=fixed,\n                                          moving=moving,\n                                          use_fixed_mask=use_fixed_mask,\n                                          use_moving_mask=use_moving_mask,\n                                          use_verbose=use_verbose,\n                                          registration_type=registration_type,\n                                          )\n\n        self._REGISTRATION_TYPES = [\"Rigid\", \"Similarity\", \"Affine\"]\n        self._INITIALIZER_TYPES = [None, \"MOMENTS\", \"GEOMETRY\",\n                                   \"SelfGEOMETRY\", \"SelfMOMENTS\"]\n        self._SCALES_ESTIMATORS = [\"IndexShift\", \"PhysicalShift\", \"Jacobian\"]\n\n        self._interpolator = interpolator\n        self._metric = metric\n        self._metric_params = metric_params\n\n        self._optimizer = optimizer\n        self._optimizer_params = optimizer_params\n\n        self._scales_estimator = scales_estimator\n\n        self._initializer_type = initializer_type\n\n        self._use_oriented_psf = use_oriented_psf\n\n        self._use_multiresolution_framework = use_multiresolution_framework\n        self._shrink_factors = shrink_factors\n        self._smoothing_sigmas = smoothing_sigmas\n\n    # Use multiresolution framework\n    #  \\param[in] flag boolean\n    def use_multiresolution_framework(self, flag):\n        self._use_multiresolution_framework = flag\n\n    # Decide whether oriented PSF shall be applied, i.e. blur moving image\n    #  with (axis aligned) Gaussian kernel given by the relative position of\n    #  the coordinate systems of fixed and moving\n    #  \\param[in] flag boolean\n    def use_oriented_psf(self, flag):\n        self._use_oriented_psf = flag\n\n    # Set type of centered transform initializer\n    #  \\param[in] initializer_type\n    def set_initializer_type(self, initializer_type):\n        if initializer_type not in self._INITIALIZER_TYPES:\n            raise ValueError(\"Possible initializer types: \" +\n                             str(self._INITIALIZER_TYPES))\n        else:\n            self._initializer_type = initializer_type\n\n    # Get type of centered transform initializer\n    def get_initializer_type(self):\n        return self._initializer_type\n\n    # Set interpolator\n    #  \\param[in] interpolator_type\n    def set_interpolator(self, interpolator_type):\n        self._interpolator = interpolator_type\n\n    # Get interpolator\n    #  \\return interpolator as string\n    def get_interpolator(self):\n        return self._interpolator\n\n    def set_metric(self, metric):\n        self._metric = metric\n\n    def set_metric_params(self, metric_params):\n        self._metric_params = metric_params\n\n    def set_optimizer(self, optimizer):\n        self._optimizer = optimizer\n\n    def set_optimizer_params(self, optimizer_params):\n        self._optimizer_params = optimizer_params\n\n    # Set optimizer scales\n    #  \\param[in] scales\n    def set_scales_estimator(self, scales_estimator):\n        if scales_estimator not in self._SCALES_ESTIMATORS:\n            raise ValueError(\"Possible optimizer scales: \" +\n                             str(self._SCALES_ESTIMATORS))\n        else:\n            self._scales_estimator = scales_estimator\n\n    def _run(self):\n\n        if self._use_fixed_mask:\n            fixed_sitk_mask = self._fixed.sitk_mask\n        else:\n            fixed_sitk_mask = None\n\n        if self._use_moving_mask:\n            moving_sitk_mask = self._moving.sitk_mask\n        else:\n            moving_sitk_mask = None\n\n        # Blur moving image with oriented Gaussian prior to the registration\n        if self._use_oriented_psf:\n\n            # Get oriented Gaussian covariance matrix\n            cov_HR_coord = psf.PSF(\n            ).get_covariance_matrix_in_reconstruction_space(\n                self._fixed, self._moving)\n\n            # Create recursive YVV Gaussianfilter\n            image_type = itk.Image[itk.D, self._fixed.sitk.GetDimension()]\n            gaussian_yvv = itk.SmoothingRecursiveYvvGaussianImageFilter[\n                image_type, image_type].New()\n\n            # Feed Gaussian filter with axis aligned covariance matrix\n            sigma_axis_aligned = np.sqrt(np.diagonal(cov_HR_coord))\n            print(\"Oriented PSF blurring with (axis aligned) sigma = \" +\n                  str(sigma_axis_aligned))\n            print(\"\\t(Based on computed covariance matrix = \")\n            for i in range(0, 3):\n                print(\"\\t\\t\" + str(cov_HR_coord[i, :]))\n            print(\"\\twith square root of diagonal \" +\n                  str(np.diagonal(cov_HR_coord)) + \")\")\n\n            gaussian_yvv.SetInput(self._moving.itk)\n            gaussian_yvv.SetSigmaArray(sigma_axis_aligned)\n            gaussian_yvv.Update()\n            moving_itk = gaussian_yvv.GetOutput()\n            moving_itk.DisconnectPipeline()\n            moving_sitk = sitkh.get_sitk_from_itk_image(moving_itk)\n\n        else:\n            moving_sitk = self._moving.sitk\n\n        self._registration_method = \\\n            simplereg.simple_itk_registration.SimpleItkRegistration(\n                fixed_sitk=self._fixed.sitk,\n                moving_sitk=moving_sitk,\n                fixed_sitk_mask=fixed_sitk_mask,\n                moving_sitk_mask=moving_sitk_mask,\n                registration_type=self._registration_type,\n                interpolator=self._interpolator,\n                metric=self._metric,\n                metric_params=self._metric_params,\n                optimizer=self._optimizer,\n                optimizer_params=self._optimizer_params,\n                initializer_type=self._initializer_type,\n                use_multiresolution_framework=self._use_multiresolution_framework,\n                optimizer_scales=self._scales_estimator,\n                shrink_factors=self._shrink_factors,\n                smoothing_sigmas=self._smoothing_sigmas,\n                verbose=self._use_verbose,\n            )\n\n        self._registration_method.run()\n\n        self._registration_transform_sitk = \\\n            self._registration_method.get_registration_transform_sitk()\n\n    def _get_warped_moving_sitk(self):\n        warped_moving_sitk = sitk.Resample(\n            self._moving.sitk,\n            self._fixed.sitk,\n            self.get_registration_transform_sitk(),\n            eval(\"sitk.sitk%s\" % (self._interpolator)),\n            0.,\n            self._moving.sitk.GetPixelIDValue()\n        )\n        return warped_moving_sitk\n"
  },
  {
    "path": "niftymic/registration/stack_registration_base.py",
    "content": "##\n# \\file stack_registration_base.py\n# \\brief      Abstract class containing the shared attributes and functions for\n#             registrations of stack of slices.\n#\n# Class has been mainly developed for the CIS30FU project.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2016\n#\n\n\n# Import libraries\nfrom abc import ABCMeta, abstractmethod\nimport sys\nimport SimpleITK as sitk\nimport itk\nimport numpy as np\nimport time\nfrom datetime import timedelta\nfrom scipy.optimize import least_squares\nfrom scipy.optimize import minimize\n\n# Import modules\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\nfrom nsol.loss_functions import LossFunctions as lf\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.parameter_normalization as pn\n\n\n##\n#       Abstract class containing the shared attributes and functions for\n#             registrations of stack of slices\n# \\date       2016-11-06 16:58:15+0000\n#\nclass StackRegistrationBase(object):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Constructor\n    # \\date       2016-11-06 16:58:43+0000\n    #\n    # \\param      self                              The object\n    # \\param      stack                             The stack to be aligned as\n    #                                               Stack object\n    # \\param      reference                         The reference used for\n    #                                               alignment as Stack object\n    # \\param      use_stack_mask                    Use stack mask for\n    #                                               registration, bool\n    # \\param      use_reference_mask                Use reference mask for\n    #                                               registration, bool\n    # \\param      use_verbose                       Verbose output, bool\n    # \\param      transform_initializer_type        The transform initializer\n    #                                               type, e.g. \"identity\",\n    #                                               \"moments\" or \"geometry\"\n    # \\param      interpolator                      The interpolator\n    # \\param      alpha_neighbour                   Weight >= 0 for neighbour\n    #                                               term\n    # \\param      alpha_reference                   Weight >= 0 for reference\n    #                                               term\n    # \\param      alpha_parameter                   Weight >= 0 for prior term\n    # \\param      use_parameter_normalization       Use parameter\n    #                                               normalization for optimizer, bool\n    # \\param      optimizer                         Either \"least_squares\" to\n    #                                               use\n    #                                               scipy.optimize.least_squares\n    #                                               or any method used in\n    #                                               \"scipy.optimize.minimize\",\n    #                                               e.g. \"L-BFGS-B\".\n    # \\param      optimizer_iter_max                Maximum number of\n    #                                               iterations/function\n    #                                               evaluations\n    # \\param      optimizer_loss                    Loss function, e.g.\n    #                                               \"linear\", \"soft_l1\" or\n    #                                               \"huber\".\n    # \\param      optimizer_method                  The optimizer method used\n    #                                               for \"least_squares\"\n    #                                               algorithm. E.g. \"trf\"\n    #\n    def __init__(self,\n                 stack=None,\n                 reference=None,\n                 use_stack_mask=False,\n                 use_reference_mask=False,\n                 use_verbose=False,\n                 transform_initializer_type=\"identity\",\n                 interpolator=\"Linear\",\n                 alpha_neighbour=1,\n                 alpha_reference=1,\n                 alpha_parameter=1,\n                 use_parameter_normalization=False,\n                 optimizer=\"L-BFGS-B\",\n                 optimizer_iter_max=20,\n                 optimizer_loss=\"soft_l1\",\n                 optimizer_method=\"trf\",  # Only counts for least_squares\n                 ):\n\n        # Set Fixed and reference stacks\n        if stack is not None:\n            self._stack = st.Stack.from_stack(stack)\n            self._N_slices = self._stack.get_number_of_slices()\n        else:\n            self._stack = None\n\n        if reference is not None:\n            self._reference = st.Stack.from_stack(reference)\n        else:\n            self._reference = None\n\n        # Set booleans to use mask\n        self._use_stack_mask = use_stack_mask\n        self._use_reference_mask = use_reference_mask\n\n        # Parameters for solver\n        self._optimizer = optimizer\n        self._optimizer_iter_max = optimizer_iter_max\n        self._optimizer_loss = optimizer_loss\n        self._optimizer_method = optimizer_method\n\n        # Verbose computation\n        self._use_verbose = use_verbose\n\n        # Initializer type\n        self._get_initial_transforms_and_parameters = {\n            \"identity\":   self._get_initial_transforms_and_parameters_identity,\n            \"moments\":   self._get_initial_transforms_and_parameters_geometry_moments,\n            \"geometry\":   self._get_initial_transforms_and_parameters_geometry_moments\n        }\n        self._dictionary_transform_initializer_type_sitk = {\n            \"identity\":   None,\n            \"moments\":   \"MOMENTS\",\n            \"geometry\":   \"GEOMETRY\"\n        }\n        self._transform_initializer_type = transform_initializer_type\n\n        # Interpolator\n        self._interpolator = interpolator\n        self._interpolator_sitk = eval(\"sitk.sitk\" + self._interpolator)\n\n        # Set weights for cost function of each term/residual\n        self._alpha_neighbour = alpha_neighbour\n        self._alpha_reference = alpha_reference\n        self._alpha_parameter = alpha_parameter\n\n        self._use_parameter_normalization = use_parameter_normalization\n\n        self._ZERO = 1e-8\n\n    ##\n    #       Sets stack/reference/target image.\n    # \\date       2016-11-06 16:59:14+0000\n    #\n    # \\param      self   The object\n    # \\param      stack  stack as Stack object\n    #\n    def set_stack(self, stack):\n        self._stack = st.Stack.from_stack(stack)\n        self._N_slices = self._stack.get_number_of_slices()\n\n    def get_stack(self):\n        return self._stack\n\n    ##\n    #       Sets reference stack.\n    # \\date       2016-11-06 17:00:50+0000\n    #\n    # \\param      self       The object\n    # \\param      reference  reference stack as Stack object\n    #\n    def set_reference(self, reference):\n        self._reference = st.Stack.from_Stack(reference)\n\n    def get_reference(self):\n        return self._reference\n\n    ##\n    #       Specify whether mask of stack image shall be used for\n    #             registration\n    # \\date       2016-11-06 17:03:05+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag as boolean\n    #\n    def use_stack_mask(self, flag):\n        self._use_stack_mask = flag\n\n    ##\n    #       Specify whether mask of reference image shall be used for\n    #             registration\n    # \\date       2016-11-06 17:03:05+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag as boolean\n    #\n    def use_reference_mask(self, flag):\n        self._use_reference_mask = flag\n\n    ##\n    #       Specify whether output information shall be produced.\n    # \\date       2016-11-06 17:07:01+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag\n    #\n    def use_verbose(self, flag):\n        self._use_verbose = flag\n\n    ##\n    #       Perform parameter normalization for optimizer\n    # \\date       2016-11-17 16:10:14+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag, boolean\n    #\n    def use_parameter_normalization(self, flag):\n        self._use_parameter_normalization = flag\n\n    ##\n    #       Sets the initializer type used to initialize the registration\n    # \\date       2016-11-08 00:20:29+0000\n    #\n    # The initial transform can either be the identity ('None') or be based on\n    # the moments ('moments') or geometry ('geometry') of the stack and\n    # reference image.\n    #\n    # \\param      self              The object\n    # \\param      transform_initializer_type  The initializer type to be either 'None',\n    #                               'moments' or 'geometry'\n    #\n    def set_transform_initializer_type(self, transform_initializer_type):\n        if transform_initializer_type not in [\"identity\", \"moments\", \"geometry\"]:\n            raise ValueError(\n                \"Error: centered transform initializer type can only be 'identity', moments' or 'geometry'\")\n\n        self._transform_initializer_type = transform_initializer_type\n\n    def get_transform_initializer_type(self):\n        return self._transform_initializer_type\n\n    ##\n    #       Sets the interpolator used for resampling operations\n    # \\date       2016-11-08 16:19:33+0000\n    #\n    # \\param      self          The object\n    # \\param      interpolator  The interpolator as string\n    #\n    def set_interpolator(self, interpolator):\n        self._interpolator = interpolator\n        self._interpolator_sitk = eval(\"sitk.sitk\" + self._interpolator)\n\n    def get_interpolator(self):\n        return self._interpolator\n\n    ##\n    #       Sets the weight for the residual between the slice neighbours\n    # \\date       2016-11-10 00:59:59+0000\n    #\n    # \\param      self             The object\n    # \\param      alpha_neighbour  The alpha neighbour\n    #\n    def set_alpha_neighbour(self, alpha_neighbour):\n        self._alpha_neighbour = alpha_neighbour\n\n    def get_alpha_neighbour(self):\n        return self._alpha_neighbour\n\n    ##\n    #       Sets the weight for the residual between the slice neighbours\n    #             and the reference\n    # \\date       2016-11-10 01:00:41+0000\n    #\n    # \\param      self             The object\n    # \\param      alpha_reference  The alpha reference\n    #\n    def set_alpha_reference(self, alpha_reference):\n        self._alpha_reference = alpha_reference\n\n    def get_alpha_reference(self):\n        return self._alpha_reference\n\n    ##\n    #       Sets the weight for the residual between the slice neighbours\n    # \\date       2016-11-10 01:01:18+0000\n    #\n    # \\param      self             The object\n    # \\param      alpha_parameter  The alpha parameter\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def set_alpha_parameter(self, alpha_parameter):\n        self._alpha_parameter = alpha_parameter\n\n    def get_alpha_parameter(self):\n        return self._alpha_parameter\n\n    def set_optimizer(self, optimizer):\n        self._optimizer = optimizer\n\n    def get_optimizer(self):\n        return self._optimizer\n\n    ##\n    # Set maximum number of iterations for optimizer.\n    #\n    # least_squares: Corresponds to maximum number of function evaluations\n    # L-BFGS-B: Corresponds to maximum number of iterations\n    # \\date       2016-11-10 19:24:35+0000\n    #\n    # \\param      self                The object\n    # \\param      optimizer_iter_max  The nfev maximum\n    #\n    # \\see        https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares\n    #\n    def set_optimizer_iter_max(self, optimizer_iter_max):\n        self._optimizer_iter_max = optimizer_iter_max\n\n    def get_optimizer_iter_max(self):\n        return self._optimizer_iter_max\n\n    ##\n    #       Sets the optimizer_loss function for least_squares optimizer\n    # \\date       2016-11-17 16:07:29+0000\n    #\n    # \\param      self  The object\n    # \\param      optimizer_loss  The optimizer_loss in [\"linear\", \"soft_l1\", \"huber\", \"cauchy\",\n    #                   \"arctan\"]\n    #\n    # \\see        https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares\n    #\n    def set_optimizer_loss(self, optimizer_loss):\n        if optimizer_loss not in [\"linear\", \"soft_l1\", \"huber\", \"cauchy\", \"arctan\"]:\n            raise ValueError(\n                \"Optimizer optimizer_loss for least_squares must either be 'linear', 'soft_l1', 'huber', 'cauchy' or 'arctan'.\")\n\n        self._optimizer_loss = optimizer_loss\n\n    def get_optimizer_loss(self):\n        return self._optimizer_loss\n\n    ##\n    #       Sets the optimizer_method for least_squares optimizer\n    # \\date       2016-11-17 16:08:37+0000\n    #\n    # \\param      self    The object\n    # \\param      optimizer_method  The optimizer_method in [\"trf\", \"lm\", \"dogbox\"]\n    #\n    # \\see        https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.least_squares.html#scipy.optimize.least_squares\n    #\n    def set_optimizer_method(self, optimizer_method):\n        if optimizer_method not in [\"trf\", \"lm\", \"dogbox\"]:\n            raise ValueError(\n                \"Optimizer optimizer_method for least_squares must either be 'trf', 'lm' or 'dogbox'.\")\n\n        self._optimizer_method = optimizer_method\n\n    def get_optimizer_method(self):\n        return self._optimizer_method\n\n    ##\n    #       Gets the parameters estimated by registration algorithm.\n    # \\date       2016-11-06 17:05:38+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The parameters.\n    #\n    def get_parameters(self):\n        return np.array(self._parameters)\n\n    ##\n    #       Gets the registered stack.\n    # \\date       2016-11-08 19:44:15+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The registered stack with motion corrected slices\n    #\n    def get_corrected_stack(self):\n        return st.Stack.from_stack(self._stack_corrected)\n\n    ##\n    #       Gets the parameters information.\n    # \\date       2016-11-08 14:56:12+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The parameters information as list of strings describing the\n    #             meaning of each element in parameters\n    #\n    # @abstractmethod\n    # def get_parameters_info(self):\n    #     pass\n\n    ##\n    #       Gets the registraton transform sitk.\n    # \\date       2016-11-06 17:10:14+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The registraton transforms sitk.\n    #\n    def get_slice_transforms_sitk(self):\n        return np.array(self._slice_transforms_sitk)\n\n    ##\n    #       Print statistics associated to performed registration\n    # \\date       2016-11-06 17:07:56+0000\n    #\n    # \\param      self  The object\n    #\n    def print_statistics(self):\n        # print(\"\\nStatistics for performed registration:\" %(self._reg_type))\n        # if self._elapsed_time_sec < 0:\n        #     raise ValueError(\"Error: Elapsed time has not been measured. Run 'run' first.\")\n        # else:\n        print(\"\\tElapsed time: %s\" % (self._elapsed_time))\n        # print(\"\\tell^2-residual sum_k ||M_k(A_k x - y_k||_2^2 = %.3e\" %(self._residual_ell2))\n        # print(\"\\tprior residual = %.3e\" %(self._residual_prior))\n\n    ##\n    #       Run the registration\n    # \\date       2016-11-10 01:39:03+0000\n    #\n    # \\param      self  The object\n    #\n    def run(self):\n\n        print_precisicion = 3\n        print_suppress = True\n\n        if self._optimizer_method in [\"lm\"]:\n            verbose = 1\n            if self._optimizer_loss not in [\"linear\"]:\n                self._optimizer_loss = \"linear\"\n                print(\"Optimizer method 'lm' only supports 'linear' loss function. \")\n\n        else:\n            verbose = 2\n\n        jac = '2-point'\n        # jac = '3-point'\n        x_scale = 1.0  # or array\n        # x_scale = 'jac' #or array\n\n        # Initialize registration pipeline\n        self._run_registration_pipeline_initialization()\n\n        if self._use_verbose:\n            print(\"Initial values = \")\n            ph.print_numpy_array(\n                self._parameters, precision=print_precisicion, suppress=print_suppress)\n\n        # Parameter normalization\n        if self._use_parameter_normalization:\n            parameter_normalization = pn.ParameterNormalization(\n                self._parameters)\n            parameter_normalization.compute_normalization_coefficients()\n            coefficients = parameter_normalization.get_normalization_coefficients()\n\n            # Use absolute mean for normalization\n            scale = abs(np.array(coefficients[0]))\n\n            # scale could be zero (like for rotation)\n            scale[np.where(scale == 0)] = 1\n\n            if self._use_verbose:\n                print(\"Normalization parameters:\")\n                ph.print_numpy_array(\n                    scale, precision=print_precisicion, suppress=print_suppress)\n\n            # Each slice with the same scaling\n            x_scale = np.tile(scale, self._parameters.shape[0])\n\n        # HACK\n        self._transforms_2D_itk = [None]*self._N_slices\n        for i in range(0, self._N_slices):\n            self._transforms_2D_itk[i] = self._new_transform_itk[\n                self._transform_type]()\n            self._transforms_2D_itk[i].SetParameters(\n                itk.OptimizerParameters[itk.D](self._transforms_2D_sitk[i].GetParameters()))\n            self._transforms_2D_itk[i].SetFixedParameters(itk.OptimizerParameters[itk.D](\n                self._transforms_2D_sitk[i].GetFixedParameters()))\n\n        # Get cost function and its Jacobian w.r.t. the parameters\n        fun = self._get_residual_call()\n        jac = self._get_jacobian_residual_call()\n        x0 = self._parameters0_vec.flatten()\n\n        time_start = ph.start_timing()\n\n        if self._optimizer == \"least_squares\":\n            self._print_info_text_least_squares()\n            res = self._run_optimizer_least_squares(\n                fun=fun,\n                jac=jac,\n                x0=x0,\n                method=self._optimizer_method,\n                loss=self._optimizer_loss,\n                iter_max=self._optimizer_iter_max,\n                verbose=verbose,\n                x_scale=x_scale)\n        else:\n            self._print_info_text_minimize()\n            res = self._run_optimizer_minimize(\n                fun=fun,\n                jac=jac,\n                x0=x0,\n                method=self._optimizer,\n                loss=self._optimizer_loss,\n                iter_max=self._optimizer_iter_max,\n                verbose=verbose,\n                x_scale=x_scale)\n\n        self._elapsed_time = ph.stop_timing(time_start)\n\n        # Get and reshape final transform parameters for each slice\n        self._parameters = res.reshape(self._parameters.shape)\n\n        # Denormalize parameters\n        # self._parameters = self._parameter_normalizer.denormalize_parameters(self._parameters)\n\n        if self._use_verbose:\n            print(\"Final values = \")\n            ph.print_numpy_array(\n                self._parameters, precision=print_precisicion, suppress=print_suppress)\n        # if self._use_verbose:\n        #     print(\"Final values = \")\n        #     print(self._parameters)\n\n        # Apply motion correction and compute slice transforms\n        self._apply_motion_correction()\n\n    ##\n    # Use scipy.opimize.least_squares solver\n    #\n    def _run_optimizer_least_squares(self, fun, jac, x0, method, loss, iter_max, verbose, x_scale):\n        # Non-linear least-squares optimizer_method:\n        res = least_squares(\n            fun=fun,\n            jac=jac,\n            x0=x0,\n            method=method,\n            loss=loss,\n            max_nfev=iter_max,\n            verbose=verbose,\n            x_scale=x_scale)\n        return res.x\n\n    ##\n    # Use scipy.opimize.minimize solver\n    #\n    def _run_optimizer_minimize(self, fun, jac, x0, method, loss, iter_max, verbose, x_scale):\n\n        # Convert to cost and gradient of cost function.\n        fun_ = lambda x: lf.get_ell2_cost_from_residual(\n            fun(x),\n            loss=loss)\n        jac_ = lambda x: lf.get_gradient_ell2_cost_from_residual(\n            fun(x),\n            jac(x),\n            loss=loss)\n\n        # Use scipy.optimize.minimize method\n        res = minimize(\n            method=method,\n            fun=fun_,\n            jac=jac_,\n            x0=x0,\n            options={'maxiter': iter_max, 'disp': verbose},\n        )\n        return res.x\n\n    @abstractmethod\n    def _print_info_text_least_squares(self):\n        pass\n\n    @abstractmethod\n    def _print_info_text_minimize(self):\n        pass\n\n    ##\n    #       optimizer_Method to initialize the registration with all\n    #             precomputations which can be done before the actual\n    #             optimization.\n    # \\date       2016-11-10 01:37:13+0000\n    #\n    # \\param      self  The object\n    #\n    @abstractmethod\n    def _run_registration_pipeline_initialization(self):\n        pass\n\n    ##\n    #       Gets the residual call used for the least_squares\n    #             optimization routine\n    # \\date       2016-11-10 01:38:08+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The residual call.\n    #\n    @abstractmethod\n    def _get_residual_call(self):\n        pass\n\n    ##\n    #       Gets the initial parameters in case of identity transform.\n    # \\date       2016-11-08 15:06:54+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The initial parameters corresponding to identity transform.\n    #\n    @abstractmethod\n    def _get_initial_transforms_and_parameters_identity(self):\n        pass\n\n    ##\n    #       Gets the initial parameters for either 'geometry' or\n    #             'moments'.\n    # \\date       2016-11-08 15:08:07+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The initial parameters corresponding to 'geometry' or\n    #             'moments'.\n    #\n    @abstractmethod\n    def _get_initial_transforms_and_parameters_geometry_moments(self):\n        pass\n\n    ##\n    #       optimizer_Method that applies the obtained registration transforms to\n    #             update the slices positions and to get the affine slice\n    #             transforms capturing the performed motion correction.\n    # \\date       2016-11-10 01:34:42+0000\n    #\n    # \\param      self  The object\n    #\n    @abstractmethod\n    def _apply_motion_correction(self):\n        pass\n"
  },
  {
    "path": "niftymic/registration/transform_initializer.py",
    "content": "# \\file transform_initializer.py\n# \\brief      Class to obtain transform estimate to align fixed with moving\n#             image\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Feb 2019\n#\n\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nimport nsol.principal_component_analysis as pca\nfrom nsol.similarity_measures import SimilarityMeasures\n\nimport niftymic.base.stack as st\nimport niftymic.validation.image_similarity_evaluator as ise\nimport niftymic.utilities.template_stack_estimator as tse\n\nfrom niftymic.definitions import DIR_TMP\n\n\n##\n# Class to obtain transform estimate to align fixed with moving image\n# \\date       2019-02-20 17:47:54+0000\n#\nclass TransformInitializer(object):\n\n    def __init__(self,\n                 fixed,\n                 moving,\n                 similarity_measure=\"NMI\",\n                 refine_pca_initializations=False,\n                 ):\n        if not isinstance(fixed, st.Stack):\n            raise TypeError(\"Fixed image must be of type 'Stack'.\")\n\n        if not isinstance(moving, st.Stack):\n            raise TypeError(\"Moving image must be of type 'Stack'.\")\n\n        self._fixed = fixed\n        self._moving = moving\n        self._similarity_measure = similarity_measure\n        self._refine_pca_initializations = refine_pca_initializations\n\n        self._initial_transform_sitk = None\n\n    def get_transform_sitk(self):\n        return self._initial_transform_sitk\n\n    def run(self, debug=False):\n        # perform PCAs for fixed and moving images\n        pca_moving = self.get_pca_from_mask(self._moving.sitk_mask)\n        eigvec_moving = pca_moving.get_eigvec()\n        mean_moving = pca_moving.get_mean()\n\n        pca_fixed = self.get_pca_from_mask(self._fixed.sitk_mask)\n        eigvec_fixed = pca_fixed.get_eigvec()\n        mean_fixed = pca_fixed.get_mean()\n\n        # test different initializations based on eigenvector orientations\n        orientations = [\n            [1, 1],\n            [1, -1],\n            [-1, 1],\n            [-1, -1],\n        ]\n        transformations = []\n        for i_o, orientation in enumerate(orientations):\n            eigvec_moving_o = np.array(eigvec_moving)\n            eigvec_moving_o[:, 0] *= orientation[0]\n            eigvec_moving_o[:, 1] *= orientation[1]\n\n            # get right-handed coordinate system\n            cross = np.cross(eigvec_moving_o[:, 0], eigvec_moving_o[:, 1])\n            eigvec_moving_o[:, 2] = cross\n\n            # transformation to align fixed with moving eigenbasis\n            R = eigvec_moving_o.dot(eigvec_fixed.transpose())\n            t = mean_moving - R.dot(mean_fixed)\n\n            # build rigid transformation as sitk object\n            rigid_transform_sitk = sitk.Euler3DTransform()\n            rigid_transform_sitk.SetMatrix(R.flatten())\n            rigid_transform_sitk.SetTranslation(t)\n            transformations.append(rigid_transform_sitk)\n\n        # get best transformation according to selected similarity measure\n        self._initial_transform_sitk = self._get_best_transform(\n            transformations, debug=debug)\n\n    @staticmethod\n    def get_pca_from_mask(mask_sitk, robust=False):\n        mask_nda = sitk.GetArrayFromImage(mask_sitk)\n\n        # get largest connected region (if more than one connected region)\n        mask_nda = tse.TemplateStackEstimator.get_largest_connected_region_mask(\n            mask_nda)\n\n        # [z, y, x] x n_points to [x, y, z] x n_points\n        points = np.array(np.where(mask_nda > 0))[::-1, :]\n        n_points = len(points[0])\n        for i in range(n_points):\n            points[:, i] = mask_sitk.TransformIndexToPhysicalPoint(\n                [int(j) for j in points[:, i]])\n\n        if robust:\n            pca_mask = pca.AdmmRobustPrincipalComponentAnalysis(\n                points.transpose())\n            res = pca_mask.run()\n\n            pca_mask = pca.PrincipalComponentAnalysis(res[\"X3_admm\"])\n            pca_mask.run()\n\n        else:\n            pca_mask = pca.PrincipalComponentAnalysis(points.transpose())\n            pca_mask.run()\n\n        return pca_mask\n\n    def _get_best_transform(self, transformations, debug=False):\n\n        if self._refine_pca_initializations:\n            transformations = self._run_registrations(transformations)\n\n        warps = []\n        for transform_sitk in transformations:\n            warped_moving_sitk = sitk.Resample(\n                self._moving.sitk,\n                self._fixed.sitk,\n                transform_sitk,\n                sitk.sitkLinear,\n            )\n            warps.append(\n                st.Stack.from_sitk_image(\n                    warped_moving_sitk,\n                    extract_slices=False,\n                    slice_thickness=self._fixed.get_slice_thickness(),\n                ))\n\n        image_similarity_evaluator = ise.ImageSimilarityEvaluator(\n            stacks=warps,\n            reference=self._fixed,\n            measures=[self._similarity_measure],\n            use_reference_mask=True,\n            verbose=False,\n        )\n        ph.print_info(\n            \"Find best aligning transform as measured by %s\" %\n            self._similarity_measure)\n        image_similarity_evaluator.compute_similarities()\n        similarities = image_similarity_evaluator.get_similarities()\n\n        # get transform which leads to highest similarity\n        index = np.argmax(similarities[self._similarity_measure])\n        transform_init_sitk = transformations[index]\n\n        if debug:\n            labels = [\"attempt%d\" % (d + 1)\n                      for d in range(len(transformations))]\n            labels[index] = \"best\"\n            foo = [w.sitk for w in warps]\n            foo.insert(0, self._fixed.sitk)\n            labels.insert(0, \"fixed\")\n            sitkh.show_sitk_image(\n                foo,\n                segmentation=self._fixed.sitk_mask,\n                label=labels,\n            )\n            for i in range(len(transformations)):\n                print(\"%s: %.6f\" % (\n                    labels[1 + i], similarities[self._similarity_measure][i])\n                )\n\n        return transform_init_sitk\n\n    def _run_registrations(self, transformations):\n        path_to_fixed = os.path.join(DIR_TMP, \"fixed.nii.gz\")\n        path_to_moving = os.path.join(DIR_TMP, \"moving.nii.gz\")\n        path_to_fixed_mask = os.path.join(DIR_TMP, \"fixed_mask.nii.gz\")\n        path_to_moving_mask = os.path.join(DIR_TMP, \"moving_mask.nii.gz\")\n        path_to_tmp_output = os.path.join(DIR_TMP, \"foo.nii.gz\")\n        path_to_transform_regaladin = os.path.join(\n            DIR_TMP, \"transform_regaladin.txt\")\n        path_to_transform_sitk = os.path.join(\n            DIR_TMP, \"transform_sitk.txt\")\n\n        sitkh.write_nifti_image_sitk(self._fixed.sitk, path_to_fixed)\n        sitkh.write_nifti_image_sitk(self._moving.sitk, path_to_moving)\n        sitkh.write_nifti_image_sitk(self._fixed.sitk_mask, path_to_fixed_mask)\n        # sitkh.write_nifti_image_sitk(\n        #     self._moving.sitk_mask, path_to_moving_mask)\n\n        for i in range(len(transformations)):\n            sitk.WriteTransform(transformations[i], path_to_transform_sitk)\n\n            # Convert SimpleITK to RegAladin transform\n            cmd = \"simplereg_transform -sitk2nreg %s %s\" % (\n                path_to_transform_sitk, path_to_transform_regaladin)\n            ph.execute_command(cmd, verbose=False)\n\n            # Run NiftyReg\n            cmd_args = [\"reg_aladin\"]\n            cmd_args.append(\"-ref %s\" % path_to_fixed)\n            cmd_args.append(\"-flo %s\" % path_to_moving)\n            cmd_args.append(\"-res %s\" % path_to_tmp_output)\n            cmd_args.append(\"-inaff %s\" % path_to_transform_regaladin)\n            cmd_args.append(\"-aff %s\" % path_to_transform_regaladin)\n            cmd_args.append(\"-rigOnly\")\n            cmd_args.append(\"-ln 2\")\n            cmd_args.append(\"-voff\")\n            cmd_args.append(\"-rmask %s\" % path_to_fixed_mask)\n            # To avoid error \"0 correspondences between blocks were found\" that can\n            # occur for some cases. Also, disable moving mask, as this would be ignored\n            # anyway\n            cmd_args.append(\"-noSym\")\n            ph.print_info(\n                \"Run Registration (RegAladin) based on PCA-init %d ... \"\n                % (i + 1))\n            ph.execute_command(\" \".join(cmd_args), verbose=False)\n\n            # Convert RegAladin to SimpleITK transform\n            cmd = \"simplereg_transform -nreg2sitk %s %s\" % (\n                path_to_transform_regaladin, path_to_transform_sitk)\n            ph.execute_command(cmd, verbose=False)\n\n            transformations[i] = sitkh.read_transform_sitk(\n                path_to_transform_sitk)\n\n        return transformations\n"
  },
  {
    "path": "niftymic/registration/wrap_itk_registration.py",
    "content": "##\n# \\file wrap_itk_registration.py\n# \\brief      Class to use registration method based on SimpleITK\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\n\n# Import libraries\nimport os\nimport numpy as np\nimport itk\nimport SimpleITK as sitk\n\n# Used to parse variable arguments to SimpleITK object, see\n# http://stackoverflow.com/questions/20263839/python-convert-a-string-to-arguments-list:\nfrom ast import literal_eval\n\nimport pysitk.python_helper as ph\nimport simplereg.wrap_itk_registration\n\nimport niftymic.base.psf as psf\nimport niftymic.base.stack as st\nfrom niftymic.registration.simple_itk_registration \\\n    import SimpleItkRegistration\n\n\n##\n# Class to use registration method FLIRT\n# \\date       2017-08-09 11:22:33+0100\n#\nclass WrapItkRegistration(SimpleItkRegistration):\n\n    def __init__(\n        self,\n        fixed=None,\n        moving=None,\n        use_fixed_mask=False,\n        use_moving_mask=False,\n        registration_type=\"Rigid\",\n        interpolator=\"Linear\",\n        metric=\"Correlation\",\n        metric_params=None,\n        # optimizer=\"ConjugateGradientLineSearch\",\n        # optimizer_params={\n        #     \"learningRate\": 1,\n        #     \"numberOfIterations\": 100,\n        # },\n        optimizer=\"RegularStepGradientDescent\",\n        optimizer_params={\n            \"MinimumStepLength\": 1e-6,\n            \"NumberOfIterations\": 200,\n            \"GradientMagnitudeTolerance\": 1e-6,\n            \"LearningRate\": 1,\n            # \"RelaxationFactor\": 0.5,\n        },\n        scales_estimator=\"PhysicalShift\",\n        initializer_type=None,\n        use_oriented_psf=False,\n        use_multiresolution_framework=False,\n        shrink_factors=[4, 2, 1],\n        smoothing_sigmas=[2, 1, 0],\n        use_verbose=False,\n        alpha_cut=3,\n    ):\n\n        SimpleItkRegistration.__init__(\n            self,\n            fixed=fixed,\n            moving=moving,\n            use_fixed_mask=use_fixed_mask,\n            use_moving_mask=use_moving_mask,\n            registration_type=registration_type,\n            interpolator=interpolator,\n            metric=metric,\n            metric_params=metric_params,\n            optimizer=optimizer,\n            optimizer_params=optimizer_params,\n            scales_estimator=scales_estimator,\n            initializer_type=initializer_type,\n            use_oriented_psf=use_oriented_psf,\n            use_multiresolution_framework=use_multiresolution_framework,\n            shrink_factors=shrink_factors,\n            smoothing_sigmas=smoothing_sigmas,\n            use_verbose=use_verbose,\n        )\n\n        self._alpha_cut = alpha_cut\n        self._pixel_type = itk.D\n\n    def _run(self):\n\n        dimension = self._fixed.sitk.GetDimension()\n\n        if self._use_fixed_mask:\n            fixed_itk_mask = self._fixed.itk_mask\n        else:\n            fixed_itk_mask = None\n\n        if self._use_moving_mask:\n            moving_itk_mask = self._moving.itk_mask\n        else:\n            moving_itk_mask = None\n\n        # Blur moving image with oriented Gaussian prior to the registration\n        if self._use_oriented_psf:\n\n            image_type = itk.Image[self._pixel_type, dimension]\n\n            # Get oriented Gaussian covariance matrix\n            cov_HR_coord = psf.PSF(\n            ).get_covariance_matrix_in_reconstruction_space(\n                self._fixed, self._moving)\n            itk_gaussian_interpolator = itk.OrientedGaussianInterpolateImageFunction[\n                image_type, self._pixel_type].New()\n            itk_gaussian_interpolator.SetCovariance(cov_HR_coord.flatten())\n            itk_gaussian_interpolator.SetAlpha(self._alpha_cut)\n\n        else:\n            itk_gaussian_interpolator = None\n\n        self._registration_method = \\\n            simplereg.wrap_itk_registration.WrapItkRegistration(\n                dimension=dimension,\n                fixed_itk=self._fixed.itk,\n                moving_itk=self._moving.itk,\n                fixed_itk_mask=fixed_itk_mask,\n                moving_itk_mask=moving_itk_mask,\n                registration_type=self._registration_type,\n                interpolator=self._interpolator,\n                metric=self._metric,\n                # metric_params=self._metric_params,\n                optimizer=self._optimizer,\n                optimizer_params=self._optimizer_params,\n                initializer_type=self._initializer_type,\n                use_multiresolution_framework=self._use_multiresolution_framework,\n                # optimizer_scales=self._scales_estimator,\n                shrink_factors=self._shrink_factors,\n                smoothing_sigmas=self._smoothing_sigmas,\n                verbose=self._use_verbose,\n                itk_oriented_gaussian_interpolate_image_filter=itk_gaussian_interpolator,\n            )\n\n        self._registration_method.run()\n\n        self._registration_transform_sitk = \\\n            self._registration_method.get_registration_transform_sitk()\n"
  },
  {
    "path": "niftymic/utilities/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/utilities/binary_mask_from_mask_srr_estimator.py",
    "content": "##\n# \\file binary_mask_from_mask_srr_estimator.py\n# \\brief      Class to estimate binary mask from mask SRR stack\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       January 2019\n#\n\n\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.template_stack_estimator as tse\n\n\n##\n# Class to estimate binary mask from mask SRR stack\n# \\date       2019-01-15 16:35:36+0000\n#\nclass BinaryMaskFromMaskSRREstimator(object):\n\n    def __init__(self,\n                 srr_mask_sitk,\n                 suffix=\"_mask\",\n                 sigma=2,\n                 lower=0.5,\n                 upper=100,\n                 ):\n\n        if not isinstance(srr_mask_sitk, sitk.Image):\n            raise ValueError(\"Input must be of type sitk.Image\")\n\n        self._srr_mask_sitk = srr_mask_sitk\n        self._suffix = suffix\n        self._sigma = sigma\n        self._lower = lower\n        self._upper = upper\n\n        self._mask_sitk = None\n        self._mask = None\n\n    def get_mask_sitk(self):\n        return sitk.Image(self._mask_sitk)\n\n    def run(self):\n        mask_sitk = self._srr_mask_sitk\n\n        # Smooth mask\n        mask_sitk = sitk.SmoothingRecursiveGaussian(mask_sitk, self._sigma)\n\n        # Binarize images given thresholds\n        mask_sitk = sitk.BinaryThreshold(\n            mask_sitk, lowerThreshold=self._lower, upperThreshold=self._upper)\n\n        # Keep largest connected region only\n        nda = sitk.GetArrayFromImage(mask_sitk)\n        nda = tse.TemplateStackEstimator.get_largest_connected_region_mask(nda)\n\n        self._mask_sitk = sitk.GetImageFromArray(nda)\n        self._mask_sitk.CopyInformation(mask_sitk)\n"
  },
  {
    "path": "niftymic/utilities/brain_stripping.py",
    "content": "##\n# \\file brain_stripping.py\n# \\brief      This class implements the interface to the Brain Extraction Tool\n#             (BET) to automatically segment the brain and/or the skull.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Oct 2016\n#\n\n\nimport os\nimport sys\nimport itk\nimport SimpleITK as sitk\nimport numpy as np\nimport nipype.interfaces.fsl\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nfrom niftymic.definitions import DIR_TMP\n\n\n##\n# This class implements the interface to the Brain Extraction Tool (BET)\n# \\date       2017-10-26 18:11:17+0100\n#\nclass BrainStripping(object):\n\n    ##\n    # Constructor\n    # \\date       2016-10-12 12:43:38+0100\n    #\n    # \\param      self                 The object\n    # \\param      compute_brain_image  Boolean flag for computing brain image\n    # \\param      compute_brain_mask   Boolean flag for computing brain image\n    #                                  mask\n    # \\param      compute_skull_image  Boolean flag for computing skull mask\n    # \\param      dir_tmp              Directory where temporary results are\n    #                                  written to, string\n    # \\param      bet_options          The bet options\n    #\n    def __init__(self,\n                 compute_brain_image=False,\n                 compute_brain_mask=True,\n                 compute_skull_image=False,\n                 dir_tmp=os.path.join(DIR_TMP, \"BrainExtractionTool\"),\n                 bet_options=\"\"):\n\n        self._compute_brain_image = compute_brain_image\n        self._compute_brain_mask = compute_brain_mask\n        self._compute_skull_image = compute_skull_image\n        self._dir_tmp = dir_tmp\n        self._bet_options = bet_options\n\n        self._sitk = None\n        self._sitk_brain_image = None\n        self._sitk_brain_mask = None\n        self._sitk_skull_image = None\n\n        self._stack = None\n\n    ##\n    # Initialize brain stripping class based on image to be read\n    # \\date       2016-10-12 12:19:18+0100\n    #\n    # \\param      cls                  The cls\n    # \\param      dir_input            The dir input\n    # \\param      filename             The filename\n    # \\param      compute_brain_image  Boolean flag for computing brain image\n    # \\param      compute_brain_mask   Boolean flag for computing brain image\n    #                                  mask\n    # \\param      compute_skull_image  Boolean flag for computing skull mask\n    # \\param      dir_tmp              Directory where temporary results are\n    #                                  written to, string\n    #\n    # \\return     object\n    #\n    @classmethod\n    def from_filename(cls,\n                      dir_input,\n                      filename,\n                      compute_brain_image=False,\n                      compute_brain_mask=True,\n                      compute_skull_image=False,\n                      dir_tmp=os.path.join(DIR_TMP, \"BrainExtractionTool\")):\n\n        self = cls(compute_brain_image=compute_brain_image,\n                   compute_brain_mask=compute_brain_mask,\n                   compute_skull_image=compute_skull_image,\n                   dir_tmp=dir_tmp)\n        self._sitk = sitkh.read_nifti_image_sitk(\n            os.path.join(dir_input, \"%s.nii.gz\" % filename),\n            sitk.sitkFloat64)\n\n        return self\n\n    ##\n    # Initialize brain stripping class based on given sitk.Image object\n    # \\date       2016-10-12 12:18:35+0100\n    #\n    # \\param      cls                  The cls\n    # \\param      sitk_image           The sitk image\n    # \\param      compute_brain_image  Boolean flag for computing brain image\n    # \\param      compute_brain_mask   Boolean flag for computing brain image\n    #                                  mask\n    # \\param      compute_skull_image  Boolean flag for computing skull mask\n    # \\param      dir_tmp              Directory where temporary results are\n    #                                  written to, string\n    #\n    # \\return     object\n    #\n    @classmethod\n    def from_sitk_image(cls,\n                        sitk_image,\n                        compute_brain_image=False,\n                        compute_brain_mask=True,\n                        compute_skull_image=False,\n                        dir_tmp=os.path.join(DIR_TMP, \"BrainExtractionTool\")):\n\n        self = cls(compute_brain_image=compute_brain_image,\n                   compute_brain_mask=compute_brain_mask,\n                   compute_skull_image=compute_skull_image,\n                   dir_tmp=dir_tmp)\n        self._sitk = sitk.Image(sitk_image)\n\n        return self\n\n    ##\n    # Initialize brain stripping class based on given Stack object\n    # \\date       2018-01-18 00:38:53+0000\n    #\n    # \\param      cls                  The cls\n    # \\param      stack                image as Stack object\n    # \\param      compute_brain_image  Boolean flag for computing brain image\n    # \\param      compute_brain_mask   Boolean flag for computing brain image\n    #                                  mask\n    # \\param      compute_skull_image  Boolean flag for computing skull mask\n    # \\param      dir_tmp              Directory where temporary results are\n    #                                  written to, string\n    #\n    # \\return     object\n    #\n    @classmethod\n    def from_stack(cls,\n                   stack,\n                   compute_brain_image=False,\n                   compute_brain_mask=True,\n                   compute_skull_image=False,\n                   dir_tmp=os.path.join(DIR_TMP, \"BrainExtractionTool\")):\n\n        self = cls(compute_brain_image=compute_brain_image,\n                   compute_brain_mask=compute_brain_mask,\n                   compute_skull_image=compute_skull_image,\n                   dir_tmp=dir_tmp)\n\n        self._stack = stack\n        self._sitk = sitk.Image(stack.sitk)\n\n        return self\n\n    ##\n    #       Sets the sitk image for brain stripping\n    # \\date       2016-10-12 15:46:20+0100\n    #\n    # \\param      self        The object\n    # \\param      sitk_image  The sitk image as sitk.Image object\n    #\n    #\n    def set_input_image_sitk(self, sitk_image):\n        self._sitk = sitk.Image(sitk_image)\n\n    ##\n    #       Set flag of whether or not to compute the brain image\n    # \\date       2016-10-12 12:35:46+0100\n    #\n    # \\param      self                 The object\n    # \\param      compute_brain_image  Boolean flag\n    #\n    def compute_brain_image(self, compute_brain_image):\n        self._compute_brain_image = compute_brain_image\n\n    ##\n    #       Set flag of whether or not to compute the brain image mask\n    # \\date       2016-10-12 12:36:46+0100\n    #\n    # \\param      self                The object\n    # \\param      compute_brain_mask  Boolean flag\n    #\n    def compute_brain_mask(self, compute_brain_mask):\n        self._compute_brain_mask = compute_brain_mask\n\n    ##\n    #       Set flag of whether or not to compute the skull mask\n    # \\date       2016-10-12 12:37:06+0100\n    #\n    # \\param      self                The object\n    # \\param      compute_skull_image  Boolean flag\n    #\n    def compute_skull_image(self, compute_skull_image):\n        self._compute_skull_image = compute_skull_image\n\n    ##\n    #       Set Brain Extraction Tool specific options\n    # \\date       2016-10-12 14:38:38+0100\n    #\n    # \\param      self         The object\n    # \\param      bet_options  The bet options, string\n    #\n    def set_bet_options(self, bet_options):\n        self._bet_options = bet_options\n\n    ##\n    #       Gets the input image\n    # \\date       2016-10-12 14:41:05+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The input image as sitk.Image object\n    #\n    def get_input_image_sitk(self):\n        if self._sitk is None:\n            raise ValueError(\"Input image was not read yet.\")\n\n        return sitk.Image(self._sitk)\n\n    ##\n    # Gets the brain masked stack.\n    # \\date       2018-01-18 00:44:49+0000\n    #\n    # \\param      self            The object\n    # \\param      filename        The filename\n    # \\param      extract_slices  Extract slices of stack; boolean\n    #\n    # \\return     Returns image as Stack object holding obtained brain mask\n    #\n    def get_brain_masked_stack(self, filename=\"Unknown\", extract_slices=False):\n        if self._sitk_brain_mask is None:\n            raise ValueError(\"Brain mask was not asked for. \"\n                             \"Set option '-m' and run again.\")\n\n        if self._stack is not None:\n            filename = self._stack.get_filename()\n\n        stack = st.Stack.from_sitk_image(\n            image_sitk=self._sitk,\n            image_sitk_mask=self._sitk_brain_mask,\n            filename=filename,\n            extract_slices=extract_slices\n        )\n        return stack\n\n    ##\n    #       Get computed brain image\n    # \\date       2016-10-12 14:33:53+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The brain image as sitk object.\n    #\n    def get_brain_image_sitk(self):\n        if self._sitk_brain_image is None:\n            raise ValueError(\"Brain was not asked for. \"\n                             \"Do not set option '-n' and run again.\")\n\n        return self._sitk_brain_image\n\n    ##\n    #       Get computed brain image mask\n    # \\date       2016-10-12 14:33:53+0100\n    #\n    # \\param      self  The object\n    #\n    # \\return     The brain mask as sitk.Image object\n    #\n    def get_brain_mask_sitk(self, dilate_radius=0):\n        if self._sitk_brain_mask is None:\n            raise ValueError(\"Brain mask was not asked for. \"\n                             \"Set option '-m' and run again.\")\n\n        if dilate_radius > 0:\n            # Chose kernel\n            kernel_sitk = sitk.sitkBall\n            # kernel_sitk = sitk.sitkBox\n            # kernel_sitk = sitk.sitkAnnulus\n            # kernel_sitk = sitk.sitkCross\n\n            # Define dilate and erode image filter\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(kernel_sitk)\n            dilater.SetKernelRadius(dilate_radius)\n            brain_mask_sitk = dilater.Execute(self._sitk_brain_mask)\n        else:\n            brain_mask_sitk = sitk.Image(self._sitk_brain_mask)\n\n        return brain_mask_sitk\n\n    ##\n    # Get computed skull image mask\n    # \\date       2016-10-12 14:33:53+0100\n    #\n    # \\param      self           The object\n    # \\param      dilate_radius  The dilate radius\n    # \\param      erode_radius   The erode radius\n    # \\param      kernel         The kernel in \"Ball\", \"Box\", \"Annulus\" or\n    #                            \"Cross\"\n    #\n    # \\return     The skull mask image as sitk object.\n    #\n    def get_skull_mask_sitk(self,\n                            dilate_radius=10,\n                            erode_radius=0,\n                            kernel=\"Ball\"):\n        if self._sitk_skull_image is None:\n            raise ValueError(\n                \"Skull mask was not asked for. Set option '-s' and run again.\")\n\n        skull_mask_sitk = sitk.Image(self._sitk_skull_image)\n\n        # Skull mask from BET has values of either 0 or 100. Threshold to 0,1\n        thresholder = sitk.BinaryThresholdImageFilter()\n        thresholder.SetUpperThreshold(255)\n        thresholder.SetLowerThreshold(1)\n        skull_mask_sitk = thresholder.Execute(skull_mask_sitk)\n\n        # Translate kernel\n        kernel_sitk = eval(\"sitk.sitk\" + kernel)\n\n        # Define dilate and erode image filter\n        if dilate_radius > 0:\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(kernel_sitk)\n            dilater.SetKernelRadius(dilate_radius)\n            skull_mask_sitk = dilater.Execute(skull_mask_sitk)\n\n        if erode_radius > 0:\n            eroder = sitk.BinaryErodeImageFilter()\n            eroder.SetKernelType(kernel_sitk)\n            eroder.SetKernelRadius(erode_radius)\n            skull_mask_sitk = eroder.Execute(skull_mask_sitk)\n\n        return skull_mask_sitk\n\n    ##\n    # Gets the mask around skull which covers also a bit of the brain. (It was\n    # used for the MS project)\n    # \\date       2016-11-06 22:54:28+0000\n    #\n    # \\param      self           The object\n    # \\param      dilate_radius  The dilate radius\n    # \\param      erode_radius   The erode radius\n    # \\param      kernel         The kernel in \"Ball\", \"Box\", \"Annulus\" or\n    #                            \"Cross\"\n    #\n    # \\return     The mask around skull.\n    #\n    def get_mask_around_skull(self,\n                              dilate_radius=10,\n                              erode_radius=0,\n                              kernel=\"Ball\"):\n\n        # Translate kernel\n        kernel_sitk = eval(\"sitk.sitk\" + kernel)\n\n        # Define dilate and erode image filter\n        dilater = sitk.BinaryDilateImageFilter()\n        dilater.SetKernelType(kernel_sitk)\n        dilater.SetKernelRadius(dilate_radius)\n\n        eroder = sitk.BinaryErodeImageFilter()\n        eroder.SetKernelType(kernel_sitk)\n        eroder.SetKernelRadius(erode_radius)\n\n        # Get complement of brain mask\n        mask_sitk = 1 - self._sitk_brain_mask\n\n        shape = np.array(self._sitk_brain_mask.GetSize()[::-1])\n        mask_nda = np.zeros((shape[0], shape[1], shape[2]))\n\n        # Go slice by slice\n        for i in range(0, shape[0]):\n            slice_mask_sitk = mask_sitk[:, :, i:i + 1]\n\n            # Dilate mask of slice\n            if dilate_radius > 0:\n                slice_mask_sitk = dilater.Execute(slice_mask_sitk)\n\n            # Erode mask of slice\n            if erode_radius > 0:\n                slice_mask_sitk = eroder.Execute(slice_mask_sitk)\n\n            # Fill data array information\n            mask_nda[i, :, :] = sitk.GetArrayFromImage(slice_mask_sitk)\n\n        # Convert mask back to 3D image\n        skull_mask_sitk = sitk.GetImageFromArray(mask_nda)\n        skull_mask_sitk.CopyInformation(self._sitk_brain_mask)\n\n        # Debug:\n        # sitkh.show_sitk_image(\n        #     self._sitk,\n        #     segmentation=skull_mask_sitk,\n        #     title=\"stack_brain_mask\")\n\n        return skull_mask_sitk\n\n    ##\n    # Run Brain Extraction Tool given the chosen set of parameters\n    # \\date       2016-10-12 14:59:01+0100\n    #\n    # \\param      self  The object\n    #\n    def run(self):\n        self._run_bet_for_brain_stripping()\n\n    ##\n    # Run Brain Extraction Tool\n    # \\date       2016-10-12 14:59:24+0100\n    #\n    # \\param      self  The object\n    # \\post       self._sitk* are filled with respective images\n    #\n    def _run_bet_for_brain_stripping(self, debug=0):\n\n        filename_out = \"image\"\n\n        self._dir_tmp = ph.create_directory(self._dir_tmp, delete_files=True)\n\n        path_to_image = os.path.join(\n            self._dir_tmp, filename_out + \".nii.gz\")\n        path_to_res = os.path.join(\n            self._dir_tmp, filename_out + \"_bet.nii.gz\")\n        path_to_res_mask = os.path.join(\n            self._dir_tmp, filename_out + \"_bet_mask.nii.gz\")\n        path_to_res_skull = os.path.join(\n            self._dir_tmp, filename_out + \"_bet_skull.nii.gz\")\n\n        sitkh.write_nifti_image_sitk(self._sitk, path_to_image)\n\n        bet = nipype.interfaces.fsl.BET()\n        bet.inputs.in_file = path_to_image\n        bet.inputs.out_file = path_to_res\n\n        options = \"\"\n        if not self._compute_brain_image:\n            options += \"-n \"\n\n        if self._compute_brain_mask:\n            options += \"-m \"\n\n        if self._compute_skull_image:\n            options += \"-s \"\n\n        options += self._bet_options\n        bet.inputs.args = options\n\n        if debug:\n            print(bet.cmdline)\n        bet.run()\n\n        if self._compute_brain_image:\n            self._sitk_brain_image = sitkh.read_nifti_image_sitk(\n                path_to_res, sitk.sitkFloat64)\n\n        if self._compute_brain_mask:\n            self._sitk_brain_mask = sitkh.read_nifti_image_sitk(\n                path_to_res_mask, sitk.sitkUInt8)\n\n        if self._compute_skull_image:\n            self._sitk_skull_image = sitkh.read_nifti_image_sitk(\n                path_to_res_skull)\n"
  },
  {
    "path": "niftymic/utilities/data_preprocessing.py",
    "content": "##\n# \\file data_preprocessing.py\n# \\brief      Performs preprocessing steps\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       May 2017\n#\n\nimport numpy as np\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.intensity_correction as ic\nimport niftymic.utilities.n4_bias_field_correction as n4bfc\nimport niftymic.base.exceptions as exceptions\nimport pysitk.python_helper as ph\n\n\n##\n# Class implementing data preprocessing steps\n#\nclass DataPreprocessing:\n\n    ##\n    # Initialize data preprocessing class based on list of Stacks\n    # \\date       2017-05-12 00:49:43+0100\n    #\n    # \\param      self                      The object\n    # \\param      stacks                    List of Stack instances\n    # \\param      use_N4BiasFieldCorrector  Use N4 bias field corrector, bool\n    # \\param      use_intensity_correction  Use linear intensity correction\n    # \\param      segmentation_propagator   None or SegmentationPropagation\n    #                                       instance\n    # \\param      target_stack_index        Index of template stack.\n    # \\param      use_cropping_to_mask      The use crop to mask\n    # \\param      boundary_i                added value to first coordinate\n    #                                       (can also be negative)\n    # \\param      boundary_j                added value to second coordinate\n    #                                       (can also be negative)\n    # \\param      boundary_k                added value to third coordinate\n    #                                       (can also be negative)\n    # \\param      unit                      Unit can either be \"mm\" or \"voxel\"\n    # \\param      cls   The cls\n    #\n    def __init__(self,\n                 stacks,\n                 use_N4BiasFieldCorrector=False,\n                 use_intensity_correction=False,\n                 segmentation_propagator=None,\n                 target_stack_index=0,\n                 use_cropping_to_mask=True,\n                 boundary_i=0,\n                 boundary_j=0,\n                 boundary_k=0,\n                 unit=\"mm\",\n                 ):\n\n        self._use_N4BiasFieldCorrector = use_N4BiasFieldCorrector\n        self._use_intensity_correction = use_intensity_correction\n        self._segmentation_propagator = segmentation_propagator\n        self._target_stack_index = target_stack_index\n        self._use_cropping_to_mask = use_cropping_to_mask\n        self._boundary_i = boundary_i\n        self._boundary_j = boundary_j\n        self._boundary_k = boundary_k\n        self._unit = unit\n\n        # Number of stacks\n        self._N_stacks = len(stacks)\n\n        # Use stacks provided\n        self._stacks = [st.Stack.from_stack(s) for s in stacks]\n\n        ph.print_info(\n            \"%s stacks were loaded for data preprocessing\" % (self._N_stacks))\n\n    # Specify whether bias field correction based on N4 Bias Field Correction\n    #  Filter shall be used\n    #  \\param[in] flag\n    def use_N4BiasFieldCorrector(self, flag):\n        self._use_N4BiasFieldCorrector = flag\n\n    #\n    # Perform data preprocessing\n    # \\date       2017-07-25 21:13:19+0100\n    #\n    # \\param      self  The object\n    #\n    def run(self):\n\n        time_start = ph.start_timing()\n\n        # if no mask is provided, use unity stacks for all masks\n        is_unity_mask = np.alltrue([s.is_unity_mask() for s in self._stacks])\n        if is_unity_mask:\n            ph.print_info(\n                \"Keep unity masks for all stacks. \"\n                \"It is recommended to provide anatomical masks for increased \"\n                \"accuracy.\")\n\n        # Segmentation propagation\n        if self._segmentation_propagator is not None and not is_unity_mask:\n\n            stacks_to_propagate_indices = []\n            for i in range(0, self._N_stacks):\n                if self._stacks[i].is_unity_mask():\n                    stacks_to_propagate_indices.append(i)\n\n            stacks_to_propagate_indices = \\\n                list(set(stacks_to_propagate_indices) -\n                     set([self._target_stack_index]))\n\n            # Set target mask\n            target = self._stacks[self._target_stack_index]\n\n            # Propagate masks\n            self._segmentation_propagator.set_template(target)\n            for i in stacks_to_propagate_indices:\n                ph.print_info(\"Propagate mask from stack '%s' to '%s'\" % (\n                    target.get_filename(),\n                    self._stacks[i].get_filename()))\n                self._segmentation_propagator.set_stack(\n                    self._stacks[i])\n                self._segmentation_propagator.run_segmentation_propagation()\n                self._stacks[i] = \\\n                    self._segmentation_propagator.get_segmented_stack()\n\n                # self._stacks[i].show(1)\n\n        # Crop to mask\n        if self._use_cropping_to_mask and not is_unity_mask:\n            ph.print_info(\"Crop stacks to their masks\")\n\n            for i in range(0, self._N_stacks):\n                self._stacks[i] = self._stacks[i].get_cropped_stack_based_on_mask(\n                    boundary_i=self._boundary_i,\n                    boundary_j=self._boundary_j,\n                    boundary_k=self._boundary_k,\n                    unit=self._unit)\n\n        # N4 Bias Field Correction\n        if self._use_N4BiasFieldCorrector:\n            bias_field_corrector = n4bfc.N4BiasFieldCorrection()\n\n            for i in range(0, self._N_stacks):\n                ph.print_info(\n                    \"Perform N4 Bias Field Correction for stack %d ... \"\n                    % (i + 1), newline=False)\n                bias_field_corrector.set_stack(self._stacks[i])\n                bias_field_corrector.run_bias_field_correction()\n                self._stacks[i] = \\\n                    bias_field_corrector.get_bias_field_corrected_stack()\n                print(\"done\")\n\n        # Linear Intensity Correction\n        if self._use_intensity_correction:\n            stacks_to_intensity_correct = list(\n                set(range(0, self._N_stacks)) - set([self._target_stack_index]))\n\n            intensity_corrector = ic.IntensityCorrection()\n            intensity_corrector.use_individual_slice_correction(False)\n            intensity_corrector.use_reference_mask(True)\n            intensity_corrector.use_verbose(True)\n\n            for i in stacks_to_intensity_correct:\n                stack = self._stacks[i]\n                intensity_corrector.set_stack(stack)\n                intensity_corrector.set_reference(\n                    target.get_resampled_stack(resampling_grid=stack.sitk))\n                # intensity_corrector.run_affine_intensity_correction()\n                intensity_corrector.run_linear_intensity_correction()\n                self._stacks[i] = \\\n                    intensity_corrector.get_intensity_corrected_stack()\n        self._computational_time = ph.stop_timing(time_start)\n\n    # Get preprocessed stacks\n    #  \\return preprocessed stacks as list of Stack objects\n    def get_preprocessed_stacks(self):\n\n        # Return a copy of preprocessed stacks\n        return [st.Stack.from_stack(stack) for stack in self._stacks]\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    # Write preprocessed data to specified output directory\n    #  \\param[in] dir_output output directory\n    def write_preprocessed_data(self, dir_output):\n        if all(x is None for x in self._stacks):\n            raise exceptions.ObjectNotCreated(\"run\")\n\n        # Write all slices\n        for i in range(0, self._N_stacks):\n            slices = self._stacks[i].write(\n                directory=dir_output, write_mask=True, write_slices=False)\n"
  },
  {
    "path": "niftymic/utilities/input_arparser.py",
    "content": "##\n# \\file input_argparser.py\n# \\brief      Class holding a collection of possible arguments to parse for\n#             scripts\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       August 2017\n#\n\nimport os\nimport re\nimport six\nimport sys\nimport inspect\nimport argparse\nimport platform\nimport datetime\n\nimport pysitk.python_helper as ph\nfrom nsol.similarity_measures import SimilarityMeasures as SimilarityMeasures\nfrom nsol.loss_functions import LossFunctions as LossFunctions\n\nimport niftymic\nfrom niftymic.definitions import ALLOWED_EXTENSIONS\nfrom niftymic.definitions import ALLOWED_INTERPOLATORS\nfrom niftymic.definitions import VIEWER_OPTIONS, V2V_METHOD_OPTIONS\n\n# Allowed image types\nIMAGE_TYPES = \"(\" + (\", or \").join(ALLOWED_EXTENSIONS) + \")\"\nINTERPOLATOR_TYPES = \"(%s, or %s)\" % (\n    (\", \").join(ALLOWED_INTERPOLATORS[0:-1]), ALLOWED_INTERPOLATORS[-1])\n\n\n##\n# Class holding a collection of possible arguments to parse for scripts\n# \\date       2017-08-07 01:26:11+0100\n#\nclass InputArgparser(object):\n\n    def __init__(self,\n                 description=None,\n                 prog=None,\n                 epilog=\"NiftyMIC version: %s, Author: %s (%s)\" % (\n                     niftymic.__version__,\n                     niftymic.__author__,\n                     niftymic.__email__\n                 ),\n                 config_arg=\"--config\"\n                 ):\n\n        config_helper = \"Args that start with '--' (eg. --dir-output) \" \\\n            \"can also be set in a config file (specified via %s). \" \\\n            \"If an arg is specified in more than one place, then \" \\\n            \"commandline values override config file values which \" \\\n            \"override defaults.\" % (config_arg)\n\n        kwargs = {}\n        if description is not None:\n            kwargs['description'] = \"%s %s\" % (description, config_helper)\n        if prog is not None:\n            kwargs['prog'] = prog\n        if epilog is not None:\n            kwargs['epilog'] = epilog\n\n        self._parser = argparse.ArgumentParser(**kwargs)\n        self._parser.add_argument(\n            config_arg,\n            help=\"Configuration file in JSON format.\")\n        self._parser.add_argument(\n            \"--version\",\n            action=\"version\",\n            help=\"Show NiftyMIC's version number and exit\",\n            version=\"%s\" % niftymic.__version__,\n        )\n        self._config_arg = config_arg\n\n    def get_parser(self):\n        return self._parser\n\n    def parse_args(self):\n\n        # read config file if available\n        if self._config_arg in sys.argv:\n            self._parse_config_file()\n\n        return self._parser.parse_args()\n\n    def print_arguments(self, args, title=\"Configuration:\"):\n        ph.print_title(title)\n        for arg in sorted(vars(args)):\n            ph.print_info(\"%s: \" % (arg), newline=False)\n            vals = getattr(args, arg)\n\n            if type(vals) is list:\n                # print list element in new lines, unless only one entry in list\n                # if len(vals) == 1:\n                #     print(vals[0])\n                # else:\n                print(\"\")\n                for val in vals:\n                    print(\"\\t%s\" % val)\n            else:\n                print(vals)\n        print(\"\\nNiftyMIC version: %s\" % niftymic.__version__)\n        ph.print_line_separator(add_newline=False)\n        print(\"\")\n\n    ##\n    # Writes a performed script execution.\n    # \\date       2018-01-16 16:05:53+0000\n    #\n    # \\param      self    The object\n    # \\param      file    path to executed file obtained, e.g. via\n    #                     os.path.abspath(__file__)\n    # \\param      prefix  filename prefix\n    #\n    def log_config(self,\n                   file,\n                   prefix=\"config\"):\n\n        # parser returns options with underscores, e.g. 'dir_output'\n        dic_with_underscores = vars(self._parser.parse_args())\n\n        # get output directory to write log/config file\n        try:\n            dir_output = dic_with_underscores[\"dir_output\"]\n        except KeyError:\n            dir_output = os.path.dirname(dic_with_underscores[\"output\"])\n\n        # build output file name\n        name = os.path.basename(file).split(\".\")[0]\n        now = datetime.datetime.now()\n        time_stamp = now.strftime(\"%Y%m%d-%H%M%S\")\n        path_to_config_file = os.path.join(\n            dir_output,\n            \"%s_%s_%s.json\" % (prefix, name, time_stamp))\n\n        # exclude config file (as setting in parameters reflected anyway)\n        dic_with_underscores.pop(re.sub(\"--\", \"\", self._config_arg))\n\n        # replace underscore by dashes for correct commandline parsing,\n        # e.g. \"dir-output\" instead of \"dir_output\"\n        dic = {\n            re.sub(\"_\", \"-\", k): v\n            for k, v in six.iteritems(dic_with_underscores)}\n\n        # add user info to config file\n        try:\n            login = os.getlogin()\n        except OSError as e:\n            login = \"unknown_login\"\n        node = platform.node()\n        info_args = []\n        info_args.append(\"Python %s\" % platform.python_version())\n        info_args.append(platform.system())\n        info_args.append(platform.release())\n        info_args.append(platform.version())\n        info_args.append(platform.machine())\n        info_args.append(platform.processor())\n        user = \"%s @ %s (%s)\" % (login, node, \", \".join(info_args))\n        dic[\"user\"] = user\n\n        # add date of execution to config file\n        dic[\"date\"] = now.strftime(\"%Y-%m-%d %H:%M:%S\")\n\n        # add version number to config file\n        dic[\"version\"] = niftymic.__version__\n\n        # write config file to output\n        ph.write_dictionary_to_json(dic, path_to_config_file, verbose=True)\n\n    def add_filename(\n        self,\n        option_string=\"--filename\",\n        type=str,\n        help=\"Path to NIfTI file %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=True,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_filename_mask(\n        self,\n        option_string=\"--filename-mask\",\n        type=str,\n        help=\"Path to NIfTI file mask %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_dir_input(\n        self,\n        option_string=\"--dir-input\",\n        type=str,\n        help=\"Input directory with NIfTI files %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_dir_input_mc(\n        self,\n        option_string=\"--dir-input-mc\",\n        type=str,\n        help=\"Input directory where transformation files (.tfm) for \"\n        \"motion-corrected slices are stored.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_subfolder_motion_correction(\n        self,\n        option_string=\"--subfolder-motion-correction\",\n        type=str,\n        help=\"Name of folder within output directory where all motion \"\n        \"correction results are stored\",\n        default=\"motion_correction\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_subfolder_comparison(\n        self,\n        option_string=\"--subfolder-comparison\",\n        type=str,\n        help=\"Name of folder within output directory where all comparison \"\n        \"results are stored\",\n        default=\"comparison\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_dir_inputs(\n        self,\n        option_string=\"--dir-inputs\",\n        nargs=\"+\",\n        type=str,\n        help=\"Input directories with NIfTI files %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_filenames(\n        self,\n        option_string=\"--filenames\",\n        nargs=\"+\",\n        help=\"Paths to NIfTI file images %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_filenames_masks(\n        self,\n        option_string=\"--filenames-masks\",\n        nargs=\"+\",\n        help=\"Paths to NIfTI file image masks %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_label(\n        self,\n        option_string=\"--label\",\n        type=str,\n        help=\"Label for image given by filename.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_fixed(\n        self,\n        option_string=\"--fixed\",\n        type=str,\n        help=\"Path to fixed image %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_fixed_mask(\n        self,\n        option_string=\"--fixed-mask\",\n        type=str,\n        help=\"Path to fixed image mask %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_moving(\n        self,\n        option_string=\"--moving\",\n        nargs=None,\n        type=str,\n        help=\"Path to moving image %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_moving_mask(\n        self,\n        option_string=\"--moving-mask\",\n        type=str,\n        help=\"Path to moving image mask %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_metric(\n        self,\n        option_string=\"--metric\",\n        type=str,\n        help=\"Metric for image registration method.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_metric_radius(\n        self,\n        option_string=\"--metric-radius\",\n        type=int,\n        help=\"Radius in case metric 'ANTSNeighborhoodCorrelation' is chosen.\",\n        default=10,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_labels(\n        self,\n        option_string=\"--labels\",\n        nargs=\"+\",\n        help=\"Labels for images given by filenames.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_image_selection(\n        self,\n        option_string=\"--image-selection\",\n        nargs=\"+\",\n        help=\"Specify image filenames without filename extension which will \"\n        \"be used only. \",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_reference(\n        self,\n        option_string=\"--reference\",\n        type=str,\n        help=\"Path to reference image file %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_reference_mask(\n        self,\n        option_string=\"--reference-mask\",\n        type=str,\n        help=\"Path to reference mask image file %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_output(\n        self,\n        option_string=\"--output\",\n        type=str,\n        help=\"Path to output image file %s.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_dir_output(\n        self,\n        option_string=\"--dir-output\",\n        type=str,\n        help=\"Output directory.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_suffix_mask(\n        self,\n        option_string=\"--suffix-mask\",\n        type=str,\n        help=\"Suffix used to associate a mask with an image. \"\n        \"E.g. suffix_mask='_mask' means an existing \"\n        \"image_i_mask.nii.gz represents the mask to \"\n        \"image_i.nii.gz for all images image_i in the input \"\n        \"directory.\",\n        default=\"_mask\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_use_masks_srr(\n        self,\n        option_string=\"--use-masks-srr\",\n        type=int,\n        help=\"Use masks in SRR step to confine volumetric reconstruction only \"\n        \"to the masked slice regions.\",\n        default=1,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_outlier_rejection(\n        self,\n        option_string=\"--outlier-rejection\",\n        type=int,\n        help=\"Turn on/off use of outlier rejection mechanism to eliminate \"\n        \"misregistered slices.\",\n        default=0,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_boundary_stacks(\n        self,\n        option_string=\"--boundary-stacks\",\n        type=int,\n        nargs=\"+\",\n        help=\"Specify boundary in i-, j- and k-direction in mm \"\n        \"for cropping the given input stacks. \"\n        \"Stack will be cropped to bounding box encompassing the mask plus \"\n        \"the added boundary.\",\n        default=[0, 0, 0],\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_prefix_output(\n        self,\n        option_string=\"--prefix-output\",\n        type=str,\n        help=\"Prefix for SRR output file name.\",\n        default=\"SRR_\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_target_stack_index(\n        self,\n        option_string=\"--target-stack-index\",\n        type=int,\n        help=\"Index of input image stack that defines physical space for SRR. \"\n        \"First index is 0.\",\n        default=0,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_target_stack(\n        self,\n        option_string=\"--target-stack\",\n        type=str,\n        help=\"Choose target stack for reconstruction/pre-processing %s.\" % (\n            IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_reconstruction(\n        self,\n        option_string=\"--reconstruction\",\n        type=str,\n        help=\"Path to NIfTI file %s of the obtained volumetric reconstruction \"\n        % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_reconstruction_space(\n        self,\n        option_string=\"--reconstruction-space\",\n        type=str,\n        help=\"Path to NIfTI file %s which defines the physical space \"\n        \"for the volumetric reconstruction/SRR.\" % (IMAGE_TYPES),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_gestational_age(\n        self,\n        option_string=\"--gestational-age\",\n        type=int,\n        help=\"Gestational age in weeks of the fetal brain to \"\n        \"be reconstructed.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_search_angle(\n        self,\n        option_string=\"--search-angle\",\n        type=int,\n        help=\"Maximum search angle to be used to find correct orientation.\",\n        default=180,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_multiresolution(\n        self,\n        option_string=\"--multiresolution\",\n        type=int,\n        help=\"Turn on/off multiresolution approach for motion correction.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_shrink_factors(\n        self,\n        option_string=\"--shrink-factors\",\n        type=int,\n        nargs=\"+\",\n        help=\"Specify shrink factors for multiresolution approach.\",\n        default=[2, 1],\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_smoothing_sigmas(\n        self,\n        option_string=\"--smoothing-sigmas\",\n        type=float,\n        nargs=\"+\",\n        help=\"Specify smoothing sigmas for multiresolution approach.\",\n        default=[1, 0],\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_two_step_cycles(\n        self,\n        option_string=\"--two-step-cycles\",\n        type=int,\n        help=\"Number of two-step-cycles, i.e. number of \"\n        \"Slice-to-Volume Registration and Super-Resolution Reconstruction \"\n        \"cycles\",\n        default=3,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_sigma(\n        self,\n        option_string=\"--sigma\",\n        type=float,\n        help=\"Standard deviation for Scattered Data Approximation approach \"\n        \"to reconstruct first estimate of HR volume from all 3D input stacks.\",\n        default=0.9,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_minimizer(\n        self,\n        option_string=\"--minimizer\",\n        type=str,\n        help=\"Choice of minimizer used for the inverse problem associated to \"\n        \"the SRR. Possible choices are 'lsmr' or any solver in \"\n        \"scipy.optimize.minimize like 'L-BFGS-B'. Note, in case of a chosen \"\n        \"non-linear data loss only non-linear solvers like 'L-BFGS-B' are \"\n        \"viable.\",\n        default=\"lsmr\",\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_alpha(\n        self,\n        option_string=\"--alpha\",\n        type=float,\n        help=\"Regularization parameter alpha to solve the Super-Resolution \"\n        \"Reconstruction problem: SRR = argmin_x \"\n        \"[0.5 * sum_k ||y_k - A_k x||^2 + alpha * R(x)].\",\n        default=0.03,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_alpha_first(\n        self,\n        option_string=\"--alpha-first\",\n        type=float,\n        help=\"Regularization parameter like 'alpha' but used for the first\"\n        \"SRR step.\",\n        default=0.1,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_threshold(\n        self,\n        option_string=\"--threshold\",\n        type=float,\n        help=\"Threshold between 0 and 1 to detect misregistered slices based \"\n        \"on NCC in final cycle.\",\n        default=0.8,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_threshold_first(\n        self,\n        option_string=\"--threshold-first\",\n        type=float,\n        help=\"Threshold between 0 and 1 to detect misregistered slices based \"\n        \"on NCC in first cycle.\",\n        default=0.5,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_s2v_smoothing(\n        self,\n        option_string=\"--s2v-smoothing\",\n        type=float,\n        help=\"Value for Gaussian process parameter smoothing.\",\n        default=0.5,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_interleave(\n        self,\n        option_string=\"--interleave\",\n        type=int,\n        help=\"Interleave used for slice acquisition\",\n        default=2,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_iter_max(\n        self,\n        option_string=\"--iter-max\",\n        type=int,\n        help=\"Number of maximum iterations for the numerical solver.\",\n        default=10,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_iter_max_first(\n        self,\n        option_string=\"--iter-max-first\",\n        type=int,\n        help=\"Number of maximum iterations for the numerical solver like \"\n        \"'iter-max' but used for the first SRR step\",\n        default=5,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_rho(\n        self,\n        option_string=\"--rho\",\n        type=float,\n        help=\"Regularization parameter for augmented Lagrangian term required \"\n        \"by ADMM approach for TV regularization\",\n        default=0.5,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_iterations(\n        self,\n        option_string=\"--iterations\",\n        type=int,\n        help=\"Number of ADMM/Primal Dual iterations.\",\n        default=10,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_tv_solver(\n        self,\n        option_string=\"--tv-solver\",\n        type=str,\n        help=\"Type of TV solver. Either 'ADMM' or 'PD'.\",\n        default=\"PD\",\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_data_loss(\n        self,\n        option_string=\"--data-loss\",\n        type=str,\n        help=\"Loss function rho used for data term, i.e. rho((y_k - A_k x)^2) \"\n        \"Possible choices are 'linear', 'soft_l1, 'huber', 'arctan' and \"\n        \"'cauchy'.\",\n        default=\"linear\",\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_data_loss_scale(\n        self,\n        option_string=\"--data-loss-scale\",\n        type=float,\n        help=\"Value of soft margin between inlier and outlier residuals, \"\n        \"default is 1.0. The loss function is evaluated as \"\n        \"rho_(f2) = C**2 * rho(f2 / C**2), where C is data_loss_scale. \"\n        \"This parameter has no effect with data_loss='linear', but for other \"\n        \"loss values it is of crucial importance.\",\n        default=1,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_pd_alg_type(\n            self,\n            option_string=\"--pd-alg-type\",\n            type=str,\n            help=\"Algorithm used to dynamically update parameters for each \"\n            \"iteration of the dual algorithm. \"\n            \"Possible choices are 'ALG2', 'ALG2_AHMOD' and 'ALG3' as \"\n            \"described in Chambolle et al., 2011\",\n            default=\"ALG2\",\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_dilation_radius(\n        self,\n        option_string=\"--dilation-radius\",\n        type=int,\n        help=\"Dilation radius in number of voxels used for segmentation \"\n        \"propagation from target stack in case masks are not provided for all \"\n        \"images.\",\n        default=3,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_extra_frame_target(\n        self,\n        option_string=\"--extra-frame-target\",\n        type=float,\n        help=\"Increase chosen target space uniformly in each direction by \"\n        \"extra frame given in mm.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_bias_field_correction(\n        self,\n        option_string=\"--bias-field-correction\",\n        type=int,\n        help=\"Turn on/off bias field correction step during data \"\n        \"preprocessing.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_intensity_correction(\n        self,\n        option_string=\"--intensity-correction\",\n        type=int,\n        help=\"Turn on/off linear intensity correction step during data \"\n        \"preprocessing.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_isotropic_resolution(\n        self,\n        option_string=\"--isotropic-resolution\",\n        type=float,\n        help=\"Specify isotropic resolution for obtained SRR volume. Default \"\n        \"resolution is specified by in-plane resolution of chosen target \"\n        \"stack.\",\n        default=None,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_log_config(\n        self,\n        option_string=\"--log-config\",\n        type=int,\n        help=\"Turn on/off configuration log of executed script.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_write_motion_correction(\n        self,\n        option_string=\"--write-motion-correction\",\n        type=int,\n        help=\"Turn on/off functionality to write final result of motion \"\n        \"correction. This includes the rigidly aligned stacks with their \"\n        \"respective motion corrected individual slices and the overall \"\n        \"transform applied to each individual slice.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_provide_comparison(\n        self,\n        option_string=\"--provide-comparison\",\n        type=int,\n        help=\"Turn on/off functionality to create files \"\n        \"allowing for a visual comparison between original \"\n        \"data and the obtained SRR. A folder 'comparison' \"\n        \"will be created in the output directory containing \"\n        \"the obtained SRR along with the linearly resampled \"\n        \"original data. An additional script \"\n        \"'show_comparison.py' will be provided whose \"\n        \"execution will open all images in ITK-Snap \"\n        \"(http://www.itksnap.org/).\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_verbose(\n        self,\n        option_string=\"--verbose\",\n        type=int,\n        help=\"Turn on/off verbose output.\",\n        default=1,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_option(\n        self,\n        option_string=\"--option\",\n        nargs=None,\n        type=float,\n        help=\"Add option.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_argument(\n        self,\n        *a,\n        **k\n    ):\n        self._parser.add_argument(*a, **k)\n\n    def add_psf_aware(\n        self,\n        option_string='--psf-aware',\n        type=int,\n        help=\"Turn on/off use of PSF-aware registration.\",\n        default=0,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_stack_recon_range(\n        self,\n        option_string=\"--stack-recon-range\",\n        type=int,\n        help=\"Number of components used for SRR.\",\n        default=15,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_alphas(\n        self,\n        option_string=\"--alphas\",\n        nargs=\"+\",\n        type=float,\n        help=\"Specify regularization parameters to be looped through.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_data_losses(\n        self,\n        option_string=\"--data-losses\",\n        nargs=\"+\",\n        help=\"Specify data losses to be looped through.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_data_loss_scales(\n        self,\n        option_string=\"--data-loss-scales\",\n        nargs=\"+\",\n        type=float,\n        help=\"Specify data loss scales to be looped through.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_study_name(\n        self,\n        option_string=\"--study-name\",\n        type=str,\n        help=\"Name of parameter study (no white spaces).\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_measures(\n        self,\n        option_string=\"--measures\",\n        type=str,\n        nargs=\"+\",\n        help=\"Measures to be evaluated between reference (if given) and \"\n        \"reconstruction %s. \" % (\"(\" + (\", \").join(\n            SimilarityMeasures.similarity_measures.keys()) + \")\"),\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_reconstruction_type(\n        self,\n        option_string=\"--reconstruction-type\",\n        type=str,\n        help=\"Define reconstruction type. Allowed values are \"\n        \"'TK0L2', 'TK1L2', 'TVL2', and 'HuberL2'.\",\n        default=\"TVL1\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_interpolator(\n        self,\n        option_string=\"--interpolator\",\n        type=str,\n        help=\"Choose type of interpolator %s.\" % (INTERPOLATOR_TYPES),\n        default=\"Linear\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_slice_thicknesses(\n        self,\n        option_string=\"--slice-thicknesses\",\n        nargs=\"+\",\n        type=float,\n        help=\"Manually specify slice thicknesses of input image stacks. \"\n        \"If not provided, the slice thicknesses of each acquired stack \"\n        \"is assumed to be the image spacing in through-plane direction.\",\n        default=None,\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_viewer(\n        self,\n        option_string=\"--viewer\",\n        type=str,\n        help=\"Viewer to be used for visualizations during verbose output \"\n        \"(%s).\" % \", \".join(VIEWER_OPTIONS),\n        default=\"itksnap\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    def add_v2v_method(\n        self,\n        option_string=\"--v2v-method\",\n        type=str,\n        help=\"Registration method used for first rigid volume-to-volume \"\n        \"registration step \"\n        \"(%s).\" % \", \".join(V2V_METHOD_OPTIONS),\n        default=\"FLIRT\",\n        required=False,\n    ):\n        self._add_argument(dict(locals()))\n\n    ##\n    # Parse the provided configuration file\n    #\n    # Additional arguments provided in the commandline will be preferred. E.g.\n    # 'script.py --config config.json --dir-output path-to-output-dir' will set\n    # dir-output to path-to-output-dir regardless the setting in config.json.\n    # \\date       2018-01-16 15:56:38+0000\n    #\n    # \\param      self  The object\n    # \\post       sys.argv extended by the arguments provided in the config\n    #             file\n    #\n    def _parse_config_file(self):\n\n        # Read path to config file\n        path_to_config_file = sys.argv[sys.argv.index(self._config_arg) + 1]\n\n        # Read config file and insert all config entries into sys.argv (read by\n        # argparse later)\n        dic = ph.read_dictionary_from_json(path_to_config_file)\n\n        # Insert all config entries into sys.argv\n        for k, v in six.iteritems(dic):\n\n            # ignore log info in config files\n            if k in [\"version\", \"user\", \"date\"]:\n                continue\n\n            # A 'None' entry should be ignored\n            if v is None:\n                continue\n\n            # Insert values as string right at the beginning of arguments\n            # Rationale: Later options, outside of the config file, will\n            # overwrite the config values\n            if type(v) is list:\n                for vi in reversed(v):\n                    sys.argv.insert(1, str(vi))\n            # 'store_true' values are converted to True/False; ignore the value\n            # of this key as the existence of the key indicates 'True'\n            elif type(v) is bool:\n                if v == True:\n                    sys.argv.insert(1, \"--%s\" % k)\n                continue\n            else:\n                sys.argv.insert(1, str(v))\n\n            sys.argv.insert(1, \"--%s\" % k)\n\n    ##\n    # Adds an argument to argument parser.\n    #\n    # Rationale: Make interface as generic as possible so that function call\n    # works regardless the name of the desired option\n    # \\date       2017-08-06 21:54:51+0100\n    #\n    # \\param      self     The object\n    # \\param      allvars  all variables set at respective function call as\n    #                      dictionary\n    #\n    def _add_argument(self, allvars):\n\n        # Skip variable 'self'\n        allvars.pop('self')\n\n        # Get name of argument to add\n        option_string = allvars.pop('option_string')\n\n        # Build dictionary for additional, optional parameters\n        kwargs = {}\n        for key, value in six.iteritems(allvars):\n            kwargs[key] = value\n\n        # Add information on default value in case provided\n        if 'default' in kwargs.keys():\n\n            if type(kwargs['default']) == list:\n                txt = \" \".join([str(i) for i in kwargs['default']])\n            else:\n                txt = str(kwargs['default'])\n            txt_default = \" [default: %s]\" % txt\n\n            # Case where 'required' key is given:\n            if 'required' in kwargs.keys():\n\n                # Only add information in case argument is not mandatory to\n                # parse\n                if kwargs['default'] is not None and not kwargs['required']:\n                    kwargs['help'] += txt_default\n\n            # Case where no such field was provided\n            else:\n                if kwargs['default'] is not None:\n                    kwargs['help'] += txt_default\n\n        # Add argument with its options\n        self._parser.add_argument(option_string, **kwargs)\n"
  },
  {
    "path": "niftymic/utilities/intensity_correction.py",
    "content": "##\n# \\file intensity_correction.py\n# \\brief      Class containing functions to correct for intensities\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2016\n#\n\n\n# Import libraries\nimport sys\nimport SimpleITK as sitk\nimport numpy as np\nfrom scipy.optimize import least_squares\nimport time\nimport matplotlib.pyplot as plt\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\n\n\n##\n#       Class to correct intensities\n# \\date       2016-11-01 20:12:46+0000\n#\nclass IntensityCorrection(object):\n\n    ##\n    #       Constructor\n    # \\date       2016-11-01 21:57:13+0000\n    #\n    # \\param      self                             The object\n    # \\param      stack                            Stack object to be intensity\n    #                                              corrected\n    # \\param      reference                        Stack object used as\n    #                                              reference for intensities\n    #                                              (needs to be in the physical\n    #                                              space as stack)\n    # \\param      use_reference_mask               Use reference mask (as given\n    #                                              in \\p reference) to reduce\n    #                                              focus for intensity\n    #                                              correction; bool\n    # \\param      use_individual_slice_correction  State whether intensity\n    #                                              correction is performed for\n    #                                              each slice independently;\n    #                                              bool\n    # \\param      use_verbose                      Verbose; bool\n    #\n    def __init__(self,\n                 stack=None,\n                 reference=None,\n                 use_stack_mask=True,\n                 use_reference_mask=True,\n                 use_individual_slice_correction=False,\n                 use_verbose=False,\n                 additional_stack=None,\n                 prefix_corrected=\"\",\n                 ):\n\n        if stack is not None:\n            self._stack = st.Stack.from_stack(stack)\n        else:\n            self._stack = None\n\n        # Additional stack to correct alongside given stack\n        if additional_stack is not None:\n            self._additional_stack = st.Stack.from_stack(additional_stack)\n        else:\n            self._additional_stack = None\n\n        # Check that stack and reference are in the same space\n        if reference is not None:\n            try:\n                self._stack.sitk - reference.sitk\n            except:\n                raise ValueError(\n                    \"Reference and stack are not in the same space\")\n            self._reference = st.Stack.from_stack(reference)\n        else:\n            self._reference = None\n\n        self._apply_intensity_correction = {\n            \"linear\": self._apply_linear_intensity_correction,\n            \"affine\": self._apply_affine_intensity_correction,\n        }\n\n        self._use_verbose = use_verbose\n        self._use_reference_mask = use_reference_mask\n        self._use_stack_mask = use_stack_mask\n        self._use_individual_slice_correction = use_individual_slice_correction\n        self._prefix_corrected = prefix_corrected\n\n    ##\n    #       Sets the stack.\n    # \\date       2016-11-05 22:58:01+0000\n    #\n    # \\param      self   The object\n    # \\param      stack  The stack as Stack object\n    #\n    def set_stack(self, stack):\n        self._stack = st.Stack.from_stack(stack)\n\n    ##\n    #       Sets the reference.\n    # \\date       2016-11-05 22:58:10+0000\n    #\n    # \\param      self       The object\n    # \\param      reference  The reference as Stack object\n    #\n    def set_reference(self, reference):\n        self._reference = st.Stack.from_stack(reference)\n\n    ##\n    # Sets additional stack to correct alongside given stack\n    # \\date       2016-12-05 12:19:25+0000\n    #\n    # \\param      self              The object\n    # \\param      additional_stack  The additional stack\n    #\n    # \\return     { description_of_the_return_value }\n    #\n    def set_additional_stack(self, additional_stack):\n        self._additional_stack = st.Stack.from_stack(additional_stack)\n\n    ##\n    #       Use verbose\n    # \\date       2016-11-05 22:58:28+0000\n    #\n    # \\param      self     The object\n    # \\param      verbose  The verbose as boolean\n    #\n    def use_verbose(self, verbose):\n        self._use_verbose = verbose\n\n    def use_reference_mask(self, use_reference_mask):\n        self._use_reference_mask = use_reference_mask\n\n    def use_stack_mask(self, use_stack_mask):\n        self._use_stack_mask = use_stack_mask\n\n    ##\n    # Sets the use individual slice correction.\n    # \\date       2016-11-22 22:47:47+0000\n    #\n    # \\param      self  The object\n    # \\param      flag  The flag\n    #\n    def use_individual_slice_correction(self, flag):\n        self._use_individual_slice_correction = flag\n\n    ##\n    #       Gets the intensity corrected stack\n    # \\date       2016-11-05 22:58:50+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The intensity corrected stack as Stack object\n    #\n    def get_intensity_corrected_stack(self):\n        s = st.Stack.from_stack(self._stack)\n        s.set_filename(self._prefix_corrected + self._stack.get_filename())\n        return s\n\n    def get_intensity_corrected_additional_stack(self):\n        return st.Stack.from_stack(self._additional_stack)\n\n    ##\n    #       Gets the intensity correction coefficients obtained for each\n    #             slice of the stack.\n    # \\date       2016-11-10 02:22:40+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The intensity correction coefficients as (N_slices x\n    #             DOF)-array\n    #\n    def get_intensity_correction_coefficients(self):\n        return np.array(self._correction_coefficients)\n\n    ##\n    #       Clip lower intensities based on percentile threshold\n    # \\date       2016-11-05 22:59:08+0000\n    #\n    # \\param      self        The object\n    # \\param      percentile  The percentile defining the threshold\n    #\n    def run_lower_percentile_capping_of_stack(self, percentile=10):\n\n        if self._use_verbose:\n            ph.print_info(\n                \"Cap lower intensities at %d%%-percentile\" % (percentile))\n\n        nda = sitk.GetArrayFromImage(self._stack.sitk)\n\n        # Clip lower intensity values\n        i0 = np.percentile(nda, percentile)\n        nda[np.where(nda < i0)] = 0\n        nda[np.where(nda >= i0)] -= i0\n\n        # Create Stack instance with correct image header information\n        self._stack = self._create_stack_from_corrected_intensity_array(\n            nda, self._stack)\n\n        if self._additional_stack is not None:\n            nda_additional_stack = sitk.GetArrayFromImage(\n                self._additional_stack.sitk)\n\n            nda_additional_stack[np.where(nda_additional_stack < i0)] = 0\n            nda_additional_stack[np.where(nda_additional_stack >= i0)] -= i0\n\n            # Create Stack instance with correct image header information\n            self._additional_stack = self._create_stack_from_corrected_intensity_array(\n                nda_additional_stack, self._additional_stack)\n\n    ##\n    #       Run linear intensity correction model.\n    # \\date       2016-11-05 23:02:46+0000\n    #\n    # Perform linear intensity correction, i.e.\n    #    minimize || reference - c1*stack || in ell^2-sense.\n    #\n    # \\param      self  The object\n    #\n    def run_linear_intensity_correction(self):\n        self._stack, self._correction_coefficients, self._additional_stack = self._run_intensity_correction(\n            \"linear\")\n\n    ##\n    #       Run affine intensity correction model.\n    # \\date       2016-11-05 23:05:48+0000\n    #\n    # Perform affine intensity correction, i.e.\n    #    minimize || reference - (c1*stack + c0)|| in ell^2-sense.\n    #\n    # \\param      self  The object\n    #\n    def run_affine_intensity_correction(self):\n        self._stack, self._correction_coefficients, self._additional_stack = self._run_intensity_correction(\n            \"affine\")\n\n    ##\n    #       Execute respective intensity correction model.\n    # \\date       2016-11-05 23:06:37+0000\n    #\n    # \\param      self              The object\n    # \\param      correction_model  The correction model. Either 'linear' or\n    #                               'affine'\n    #\n    def _run_intensity_correction(self, correction_model):\n\n        N_slices = self._stack.get_number_of_slices()\n\n        if correction_model in [\"linear\"]:\n            correction_coefficients = np.zeros((N_slices, 1))\n        elif correction_model in [\"affine\"]:\n            correction_coefficients = np.zeros((N_slices, 2))\n\n        # Gets the required data arrays to perform intensity correction\n        nda, nda_reference, nda_mask, nda_additional_stack = self._get_data_arrays_prior_to_intensity_correction()\n\n        if self._use_individual_slice_correction:\n            if self._use_verbose:\n                ph.print_info(\"Run \" + correction_model +\n                              \" intensity correction for each slice individually\")\n            for i in range(0, N_slices):\n                if self._use_verbose:\n                    sys.stdout.write(\"Slice %2d/%d: \" %\n                                     (i, self._stack.get_number_of_slices() - 1))\n                    sys.stdout.flush()\n                if self._additional_stack is None:\n                    nda[i, :, :], correction_coefficients[i, :] = self._apply_intensity_correction[\n                        correction_model](nda[i, :, :], nda_reference[i, :, :], nda_mask[i, :, :])\n                else:\n                    nda[i, :, :], correction_coefficients[i, :], nda_additional_stack[i, :, :] = self._apply_intensity_correction[\n                        correction_model](nda[i, :, :], nda_reference[i, :, :], nda_mask[i, :, :], nda_additional_stack[i, :, :])\n        else:\n            if self._use_verbose:\n                ph.print_info(\"Run \" + correction_model +\n                              \" intensity correction uniformly for entire stack\")\n            if self._additional_stack is None:\n                nda, cc = \\\n                    self._apply_intensity_correction[\n                        correction_model](nda, nda_reference, nda_mask)\n            else:\n                nda, cc, nda_additional_stack = self._apply_intensity_correction[\n                    correction_model](nda, nda_reference, nda_mask, nda_additional_stack)\n            correction_coefficients = cc\n\n        # debug\n        # tmp_corr = sitk.GetImageFromArray(nda)\n        # tmp_corr.CopyInformation(self._stack.sitk)\n        # tmp_mask = sitk.GetImageFromArray(nda_mask)\n        # tmp_mask.CopyInformation(self._stack.sitk_mask)\n        # sitkh.show_sitk_image(\n        #     [\n        #         tmp_corr,\n        #         self._stack.sitk,\n        #         self._reference.sitk,\n        #     ],\n        #     segmentation=tmp_mask,\n        #     label=[\"corr\", \"orig\", \"ref\"]\n        # )\n\n        # Create Stack instance with correct image header information\n        if self._additional_stack is None:\n            return self._create_stack_from_corrected_intensity_array(nda, self._stack), correction_coefficients, None\n        else:\n            return self._create_stack_from_corrected_intensity_array(nda, self._stack), correction_coefficients, self._create_stack_from_corrected_intensity_array(nda_additional_stack, self._additional_stack)\n\n    ##\n    #       Perform affine intensity correction via normal equations\n    # \\date       2016-11-05 23:10:49+0000\n    #\n    # \\param      self                  The object\n    # \\param      nda                   Data array to be corrected\n    # \\param      nda_mask              Mask to be used\n    # \\param      nda_reference  Masked reference data array used to\n    #                                   compute coefficients\n    #\n    # \\return     intensity corrected data array as np.array\n    #\n    def _apply_affine_intensity_correction(self, nda, nda_reference, nda_mask, nda_additional_stack=None):\n\n        # Find masked indices\n        indices = np.where(nda_mask > 0)\n\n        # Model: y = x*c1 + c0 = [x, 1]*[c1, c0]' = A*[c1,c0]\n        x = nda[indices].flatten().astype('double')\n        y = nda_reference[indices].flatten().astype('double')\n\n        # Solve via normal equations: [c1, c0] = (A'A)^{-1}A'y\n        A = np.ones((x.size, 2))\n        A[:, 0] = x\n\n        B = np.linalg.pinv(A.transpose().dot(A)).dot(A.transpose())\n        c1, c0 = B.dot(y)\n\n        if np.isnan(c1) or np.isnan(c0):\n            raise RuntimeError(\n                \"Invalid value encountered during affine intensity correction \"\n                \"(c1, c0) = (%f, %f)\" % (c1, c0))\n\n        if self._use_verbose:\n            ph.print_info(\"(c1, c0) = (%.3f, %.3f)\" % (c1, c0))\n\n        if nda_additional_stack is None:\n            return nda * c1 + c0, np.array([c1, c0])\n        else:\n            return nda * c1 + c0, np.array([c1, c0]), nda_additional_stack * c1 + c0\n\n    ##\n    #       Perform linear intensity correction via normal equations\n    # \\date       2016-11-05 23:12:13+0000\n    #\n    # \\param      self                  The object\n    # \\param      nda                   Data array to be corrected\n    # \\param      nda_mask            Mask to be used\n    # \\param      nda_reference  Masked reference data array used to\n    #                                   compute coefficients\n    #\n    # \\return     intensity corrected data array as np.array\n    #\n    def _apply_linear_intensity_correction(self, nda, nda_reference, nda_mask, nda_additional_stack=None):\n\n        # Find masked indices\n        indices = np.where(nda_mask > 0)\n\n        # Model: y = x*c1\n        x = nda[indices].flatten().astype('double')\n        y = nda_reference[indices].flatten().astype('double')\n\n        # ph.show_2D_array_list([nda, nda_reference])\n        # Solve via normal equations: c1 = x'y/(x'x)\n        c1 = x.dot(y) / x.dot(x)\n\n        if np.isnan(c1):\n            raise RuntimeError(\n                \"Invalid value encountered during linear intensity correction \"\n                \"(c1 = %f)\" % c1)\n\n        if self._use_verbose:\n            ph.print_info(\"c1 = %.3f\" % (c1))\n\n        if nda_additional_stack is None:\n            return nda * c1, c1\n        else:\n            return nda * c1, c1, nda_additional_stack * c1\n\n    ##\n    #       Gets the data arrays prior to intensity correction.\n    # \\date       2016-11-05 23:07:37+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The data arrays prior to intensity correction.\n    #\n    def _get_data_arrays_prior_to_intensity_correction(self):\n\n        # Get required data arrays for intensity correction\n        nda = sitk.GetArrayFromImage(self._stack.sitk)\n        nda_reference = sitk.GetArrayFromImage(self._reference.sitk)\n\n        if self._use_reference_mask:\n            nda_mask_ref = sitk.GetArrayFromImage(self._reference.sitk_mask)\n        else:\n            nda_mask_ref = np.ones_like(nda)\n\n        if self._use_stack_mask:\n            nda_mask_stack = sitk.GetArrayFromImage(self._stack.sitk_mask)\n        else:\n            nda_mask_stack = np.ones_like(nda)\n        nda_mask = nda_mask_ref * nda_mask_stack\n\n        if self._additional_stack is None:\n            nda_additional_stack = None\n        else:\n            nda_additional_stack = sitk.GetArrayFromImage(\n                self._additional_stack.sitk)\n\n        # debug\n        # tmp_mask = sitk.GetImageFromArray(nda_mask)\n        # tmp_mask.CopyInformation(self._stack.sitk_mask)\n        # sitkh.show_sitk_image(\n        #     [\n        #         self._stack.sitk_mask,\n        #         self._reference.sitk_mask,\n        #     ],\n        #     segmentation=tmp_mask,\n        #     label=[\"orig\", \"ref\"]\n        # )\n\n        return nda, nda_reference, nda_mask, nda_additional_stack\n\n    ##\n    #       Creates a Stack object from corrected intensity array with\n    #             same image header information as input \\p stack.\n    # \\date       2016-11-05 23:15:33+0000\n    #\n    # \\param      self  The object\n    # \\param      nda   The nda\n    #\n    # \\return     Stack object with image containing the given array\n    #             information.\n    #\n    def _create_stack_from_corrected_intensity_array(self, nda, stack):\n\n        # Convert back to image with correct header\n        image_sitk = sitk.GetImageFromArray(nda)\n        image_sitk.CopyInformation(stack.sitk)\n\n        # Potentially, #slices < #slices_ic as some slices might have been\n        # deleted.\n        slices = stack.get_slices()\n\n        helper_slice_numbers = stack.get_deleted_slice_numbers()\n        helper_slice_numbers.append(slices[0].get_slice_number())\n        helper_slice_numbers.append(slices[-1].get_slice_number())\n        slice_numbers = np.arange(np.min(helper_slice_numbers),\n                                  np.max(helper_slice_numbers) + 1)\n\n        stack_ic = st.Stack.from_sitk_image(\n            image_sitk=image_sitk,\n            slice_thickness=stack.get_slice_thickness(),\n            filename=stack.get_filename(),\n            image_sitk_mask=stack.sitk_mask,\n            slice_numbers=slice_numbers,\n        )\n\n        # Update registration history of stack\n        stack_ic.set_registration_history(\n            stack.get_registration_history())\n\n        # Update registration history of (kept) slices\n        kept_slice_numbers = [s.get_slice_number() for s in slices]\n        slices_ic = stack_ic.get_slices()\n        for slice_ic in slices_ic:\n            slice_number = slice_ic.get_slice_number()\n\n            # Update registration of kept slice\n            if slice_number in kept_slice_numbers:\n                index = kept_slice_numbers.index(slice_number)\n                slice_ic.set_registration_history(\n                    slices[index].get_registration_history())\n\n            # Otherwise, delete slices\n            else:\n                stack_ic.delete_slice(slice_ic)\n\n        return stack_ic\n"
  },
  {
    "path": "niftymic/utilities/joint_image_mask_builder.py",
    "content": "##\n# \\file joint_image_mask_builder.py\n# \\brief      Build common mask from multiple, individual ones\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\nimport SimpleITK as sitk\nimport numpy as np\n\nimport pysitk.simple_itk_helper as sitkh\nimport niftymic.base.stack as st\n\n\nclass JointImageMaskBuilder(object):\n\n    def __init__(self,\n                 stacks,\n                 target,\n                 dilation_radius=1,\n                 dilation_kernel=\"Ball\",\n                 max_distance=50):\n\n        self._stacks = stacks\n        self._target = target\n        self._dilation_radius = dilation_radius\n        self._dilation_kernel = dilation_kernel\n        self._max_distance = max_distance\n\n        self._joint_image_mask = None\n\n    def run(self):\n\n        recon_space = self._target.get_isotropically_resampled_stack(\n            extra_frame=self._max_distance,\n        )\n        mask_sitk = 0 * recon_space.sitk_mask\n        dim = mask_sitk.GetDimension()\n\n        for stack in self._stacks:\n            stack_mask_sitk = sitk.Resample(\n                stack.sitk_mask,\n                mask_sitk,\n                eval(\"sitk.Euler%dDTransform()\" % dim),\n                sitk.sitkNearestNeighbor,\n                0,\n                mask_sitk.GetPixelIDValue())\n            mask_sitk += stack_mask_sitk\n\n        thresholder = sitk.BinaryThresholdImageFilter()\n        mask_sitk = thresholder.Execute(mask_sitk, 0, 0.5, 0, 1)\n\n        if self._dilation_radius > 0:\n            dilater = sitk.BinaryDilateImageFilter()\n            dilater.SetKernelType(eval(\"sitk.sitk\" + self._dilation_kernel))\n            dilater.SetKernelRadius(self._dilation_radius)\n            mask_sitk = dilater.Execute(mask_sitk)\n\n        self._joint_image_mask = st.Stack.from_sitk_image(\n            image_sitk=recon_space.sitk,\n            image_sitk_mask=mask_sitk,\n            filename=self._target.get_filename(),\n            slice_thickness=recon_space.get_slice_thickness(),\n        )\n\n    def get_stack(self):\n        return st.Stack.from_stack(self._joint_image_mask)\n"
  },
  {
    "path": "niftymic/utilities/motion_updater.py",
    "content": "##\n# \\file motion_updater.py\n# \\brief      Class to apply stack and individual slice motion transformations\n#             from a 'motion_correction' directory.\n#\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2018\n#\n\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.slice as sl\nimport niftymic.base.exceptions as exceptions\n\n\n##\n# Class to update motion correction of both stacks and slices given a directory\n# that contains the respective transformation files.\n#\n# Provided motion-correction directory must contain \"*.tfm\" files in the\n# following format:\n#\n# -# filenameA.tfm: Transformation to be applied to stack with filename\n#    'filenameA'\n# -# filenameA_slice[0-9]+.tfm: Transformations to be applied to individual\n#    slices of stack with filename 'filenameA'. If a slice transformation file\n#    is not provided, the respective slice will be deleted from the stack\n# \\date       2018-11-11 16:21:00+0000\n#\nclass MotionUpdater(object):\n\n    ##\n    # { constructor_description }\n    # \\date       2018-11-11 16:26:58+0000\n    #\n    # \\param      self                   The object\n    # \\param      stacks                 Stacks as list of Stack objects\n    # \\param      dir_motion_correction  Path to motion-correction files\n    #                                    [*.tfm]\n    # \\param      volume_motion_only     Update only stack/volumetric motion\n    #                                    (and ignore individual slice motion\n    #                                    transforms)\n    # \\param      prefix_slice           Prefix of slices to indicate slice\n    #                                    transformations. E.g. \"_slice\" refers\n    #                                    to filenameA_slice[0-9]+.tfm files as\n    #                                    slice transformations to stack\n    #                                    \"filenameA\"\n    #\n    def __init__(\n        self,\n        stacks,\n        dir_motion_correction,\n        volume_motion_only=False,\n        prefix_slice=\"_slice\",\n    ):\n\n        self._stacks = [st.Stack.from_stack(s) for s in stacks]\n        self._dir_motion_correction = dir_motion_correction\n        self._volume_motion_only = volume_motion_only\n        self._prefix_slice = prefix_slice\n\n        self._check_against_json = {\n            True: self._check_against_json_true,\n            False: self._check_against_json_false,\n        }\n        self._rejected_slices = None\n\n    def run(self, older_than_v3=False):\n        if not ph.directory_exists(self._dir_motion_correction):\n            raise exceptions.DirectoryNotExistent(\n                self._dir_motion_correction)\n        abs_path_to_directory = os.path.abspath(\n            self._dir_motion_correction)\n\n        path_to_rejected_slices = os.path.join(\n            abs_path_to_directory, \"rejected_slices.json\")\n        if ph.file_exists(path_to_rejected_slices):\n            self._rejected_slices = ph.read_dictionary_from_json(\n                path_to_rejected_slices)\n            bool_check = True\n        else:\n            self._rejected_slices = None\n            bool_check = False\n\n        for i in range(len(self._stacks)):\n            stack_name = self._stacks[i].get_filename()\n\n            if not older_than_v3:\n                # update stack position\n                path_to_stack_transform = os.path.join(\n                    abs_path_to_directory, \"%s.tfm\" % stack_name)\n                if ph.file_exists(path_to_stack_transform):\n                    transform_stack_sitk = sitkh.read_transform_sitk(\n                        path_to_stack_transform)\n                    transform_stack_sitk_inv = sitkh.read_transform_sitk(\n                        path_to_stack_transform, inverse=True)\n                    self._stacks[i].update_motion_correction(\n                        transform_stack_sitk)\n                    ph.print_info(\n                        \"Stack '%s': Stack position updated\" % stack_name)\n                else:\n                    transform_stack_sitk_inv = sitk.Euler3DTransform()\n\n                if self._volume_motion_only:\n                    continue\n\n                # update slice positions\n                pattern_trafo_slices = stack_name + self._prefix_slice + \\\n                    \"([0-9]+)[.]tfm\"\n                p = re.compile(pattern_trafo_slices)\n                dic_slice_transforms = {\n                    int(p.match(f).group(1)): os.path.join(\n                        abs_path_to_directory, p.match(f).group(0))\n                    for f in os.listdir(abs_path_to_directory) if p.match(f)\n                }\n                slices = self._stacks[i].get_slices()\n                for i_slice in range(self._stacks[i].get_number_of_slices()):\n                    if i_slice in dic_slice_transforms.keys():\n                        transform_slice_sitk = sitkh.read_transform_sitk(\n                            dic_slice_transforms[i_slice])\n                        transform_slice_sitk = \\\n                            sitkh.get_composite_sitk_affine_transform(\n                                transform_slice_sitk, transform_stack_sitk_inv)\n                        slices[i_slice].update_motion_correction(\n                            transform_slice_sitk)\n\n                    else:\n                        self._stacks[i].delete_slice(slices[i_slice])\n\n            # ----------------------------- HACK -----------------------------\n            # 18 Jan 2019\n            # HACK to use results of a previous version where image slices were\n            # still exported.\n            # (There was a bug after stack intensity correction, which resulted\n            # in v2v-reg transforms not being part of in the final registration\n            # transforms; Thus, slice transformations (tfm's) were flawed and\n            # could not be used):\n            else:\n                # Recover suffix for mask\n                pattern = stack_name + self._prefix_slice + \\\n                    \"[0-9]+[_]([a-zA-Z]+)[.]nii.gz\"\n                pm = re.compile(pattern)\n                matches = list(set([pm.match(f).group(1) for f in os.listdir(\n                    abs_path_to_directory) if pm.match(f)]))\n                if len(matches) > 1:\n                    raise RuntimeError(\"Suffix mask cannot be determined\")\n                suffix_mask = \"_%s\" % matches[0]\n\n                # Recover stack\n                path_to_stack = os.path.join(\n                    abs_path_to_directory, \"%s.nii.gz\" % stack_name)\n                path_to_stack_mask = os.path.join(\n                    abs_path_to_directory, \"%s%s.nii.gz\" % (\n                        stack_name, suffix_mask))\n                stack = st.Stack.from_filename(\n                    path_to_stack, path_to_stack_mask)\n\n                # Recover slices\n                pattern_trafo_slices = stack_name + self._prefix_slice + \\\n                    \"([0-9]+)[.]tfm\"\n                p = re.compile(pattern_trafo_slices)\n                dic_slice_transforms = {\n                    int(p.match(f).group(1)): os.path.join(\n                        abs_path_to_directory, p.match(f).group(0))\n                    for f in os.listdir(abs_path_to_directory) if p.match(f)\n                }\n                slices = self._stacks[i].get_slices()\n                for i_slice in range(self._stacks[i].get_number_of_slices()):\n                    if i_slice in dic_slice_transforms.keys():\n                        path_to_slice = re.sub(\n                            \".tfm\", \".nii.gz\", dic_slice_transforms[i_slice])\n                        path_to_slice_mask = re.sub(\n                            \".tfm\", \"%s.nii.gz\" % suffix_mask,\n                            dic_slice_transforms[i_slice])\n                        slice_sitk = sitk.ReadImage(path_to_slice)\n                        slice_sitk_mask = sitk.ReadImage(path_to_slice_mask)\n                        hack = sl.Slice.from_sitk_image(\n                            slice_sitk=slice_sitk,\n                            # slice_sitk=slice_sitk_mask,  # mask for Mask-SRR!\n                            slice_sitk_mask=slice_sitk_mask,\n                            slice_number=slices[i_slice].get_slice_number(),\n                            slice_thickness=slices[\n                                i_slice].get_slice_thickness(),\n                        )\n                        self._stacks[i]._slices[i_slice] = hack\n                    else:\n                        self._stacks[i].delete_slice(slices[i_slice])\n\n                self._stacks[i].sitk = stack.sitk\n                self._stacks[i].sitk_mask = stack.sitk_mask\n                self._stacks[i].itk = stack.itk\n                self._stacks[i].itk_mask = stack.itk_mask\n            # -----------------------------------------------------------------\n\n            # print update information\n            ph.print_info(\n                \"Stack '%s': Slice positions updated \"\n                \"(%d/%d slices deleted)\" % (\n                    stack_name,\n                    len(self._stacks[i].get_deleted_slice_numbers()),\n                    self._stacks[i].sitk.GetSize()[-1],\n                )\n            )\n\n            # delete entire stack if all slices were rejected\n            if self._stacks[i].get_number_of_slices() == 0:\n                ph.print_info(\n                    \"Stack '%s' removed as all slices were deleted\" %\n                    stack_name)\n                self._stacks[i] = None\n\n        # only return maintained stacks\n        self._stacks = [s for s in self._stacks if s is not None]\n\n        if len(self._stacks) == 0:\n            raise RuntimeError(\n                \"All stacks removed. \"\n                \"Did you check that the correct motion-correction directory \"\n                \"was provided? \"\n                \"Or, in case of 3D mask SRR, that --suffix-mask input is \"\n                \"correct?\")\n\n    def get_data(self):\n        return self._stacks\n\n    ##\n    # Check slice_number of stack_name with entries in rejected_slices.json\n    # file. If there is a match, reject the slice\n    #\n    # rejected_slices.json serves as additional means of checking whether a\n    # slice shall be kept.\n    # Rationale: Potentially \"old\" slice transformations that were not deleted\n    # in the motion_correction folder can be detected here.\n    # \\date       2019-04-09 09:55:39+0100\n    #\n    # \\param      self          The object\n    # \\param      stack_name    The stack name; string\n    # \\param      slice_number  The slice number; integer\n    #\n    # \\return     False (reject slice) or True (keep it)\n    #\n    def _check_against_json_true(self, stack_name, slice_number):\n\n        # if slice_number of stack_name is in rejected slices, discard it\n        if stack_name in self._rejected_slices.keys():\n            if slice_number in self._rejected_slices[stack_name]:\n                ph.print_warning(\n                    \"Reject slice '%s-slice%d' as detected by \"\n                    \"rejected_slices.json\" % (\n                        stack_name, slice_number)\n                )\n                return False\n\n        return True\n\n    ##\n    # Dummy function in case no rejected_slices.json is in directory\n    # \\date       2019-04-09 09:58:02+0100\n    #\n    # \\param      self          The object\n    # \\param      stack_name    The stack name\n    # \\param      slice_number  The slice number\n    #\n    def _check_against_json_false(self, stack_name, slice_number):\n        return True\n"
  },
  {
    "path": "niftymic/utilities/n4_bias_field_correction.py",
    "content": "##\n# \\file n4_bias_field_correction.py\n# \\brief      N4ITK Bias-Field Correction interface\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       May 2017\n#\n\n\n# Import libraries\nimport os\nimport sys\nimport itk\nimport SimpleITK as sitk\nimport numpy as np\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\n\n\n##\n# Class implementing the segmentation propagation from one image to another\n# \\date       2017-05-10 23:48:08+0100\n#\nclass N4BiasFieldCorrection(object):\n\n    def __init__(self,\n                 stack=None,\n                 use_mask=True,\n                 convergence_threshold=1e-6,\n                 spline_order=3,\n                 wiener_filter_noise=0.11,\n                 bias_field_fwhm=0.15,\n                 prefix_corrected=\"\",\n                 ):\n\n        self._stack = stack\n        self._use_mask = use_mask\n        self._convergence_threshold = convergence_threshold\n        self._spline_order = spline_order\n        self._wiener_filter_noise = wiener_filter_noise\n        self._bias_field_fwhm = bias_field_fwhm\n        self._prefix_corrected = prefix_corrected\n\n        self._stack_corrected = None\n        self._computational_time = ph.get_zero_time()\n\n    def set_stack(self, stack):\n        self._stack = stack\n\n    def get_bias_field_corrected_stack(self):\n        return st.Stack.from_stack(self._stack_corrected)\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    def run_bias_field_correction(self):\n\n        time_start = ph.start_timing()\n\n        bias_field_corrector = sitk.N4BiasFieldCorrectionImageFilter()\n\n        bias_field_corrector.SetBiasFieldFullWidthAtHalfMaximum(\n            self._bias_field_fwhm)\n        bias_field_corrector.SetConvergenceThreshold(\n            self._convergence_threshold)\n        bias_field_corrector.SetSplineOrder(self._spline_order)\n        bias_field_corrector.SetWienerFilterNoise(self._wiener_filter_noise)\n\n        if self._use_mask:\n            image_sitk = bias_field_corrector.Execute(\n                self._stack.sitk, self._stack.sitk_mask)\n        else:\n            image_sitk = bias_field_corrector.Execute(self._stack.sitk)\n\n        # Reading of image might lead to slight differences\n        stack_corrected_sitk_mask = sitk.Resample(\n            self._stack.sitk_mask,\n            image_sitk,\n            sitk.Euler3DTransform(),\n            sitk.sitkNearestNeighbor,\n            0,\n            self._stack.sitk_mask.GetPixelIDValue())\n\n        self._stack_corrected = st.Stack.from_sitk_image(\n            image_sitk=image_sitk,\n            image_sitk_mask=stack_corrected_sitk_mask,\n            filename=self._prefix_corrected + self._stack.get_filename(),\n            slice_thickness=self._stack.get_slice_thickness(),\n        )\n\n        # Get computational time\n        self._computational_time = ph.stop_timing(time_start)\n\n        # Debug\n        # sitkh.show_stacks([self._stack, self._stack_corrected], label=[\"orig\", \"corr\"])\n"
  },
  {
    "path": "niftymic/utilities/outlier_rejector.py",
    "content": "##\n# \\file outlier_rejector.py\n# \\brief      Class to identify and reject outliers.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Jan 2019\n#\n\nimport os\nimport scipy\nimport numpy as np\nimport SimpleITK as sitk\nimport matplotlib.pyplot as plt\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\nimport niftymic.validation.residual_evaluator as re\n\n\n##\n# Class to identify and reject outliers\n# \\date       2019-01-28 19:24:52+0100\n#\nclass OutlierRejector(object):\n\n    def __init__(self,\n                 stacks,\n                 reference,\n                 threshold,\n                 use_slice_masks=False,\n                 use_reference_mask=True,\n                 measure=\"NCC\",\n                 verbose=True,\n                 ):\n\n        self._stacks = stacks\n        self._reference = reference\n        self._threshold = threshold\n        self._measure = measure\n        self._use_slice_masks = use_slice_masks\n        self._use_reference_mask = use_reference_mask\n        self._verbose = verbose\n\n    def get_stacks(self):\n        return self._stacks\n\n    def run(self):\n        residual_evaluator = re.ResidualEvaluator(\n            stacks=self._stacks,\n            reference=self._reference,\n            use_slice_masks=self._use_slice_masks,\n            use_reference_mask=self._use_reference_mask,\n            verbose=False,\n            measures=[self._measure],\n        )\n        residual_evaluator.compute_slice_projections()\n        residual_evaluator.evaluate_slice_similarities()\n        slice_sim = residual_evaluator.get_slice_similarities()\n        # residual_evaluator.show_slice_similarities(\n        #     threshold=self._threshold,\n        #     measures=[self._measure],\n        #     directory=\"/tmp/spina/figs%s\" % self._print_prefix[0:7],\n        # )\n\n        remove_stacks = []\n        for i, stack in enumerate(self._stacks):\n            nda_sim = np.nan_to_num(\n                slice_sim[stack.get_filename()][self._measure])\n            indices = np.where(nda_sim < self._threshold)[0]\n            slices = stack.get_slices()\n\n            # only those indices that match the available slice numbers\n            rejections = [\n                j for j in [s.get_slice_number() for s in slices]\n                if j in indices\n            ]\n\n            for slice in slices:\n                if slice.get_slice_number() in rejections:\n                    stack.delete_slice(slice)\n\n            if self._verbose:\n                txt = \"Stack %d/%d (%s): Slice rejections %d/%d [%s]\" % (\n                    i + 1,\n                    len(self._stacks),\n                    stack.get_filename(),\n                    len(stack.get_deleted_slice_numbers()),\n                    stack.sitk.GetSize()[-1],\n                    ph.convert_numbers_to_hyphenated_ranges(\n                        stack.get_deleted_slice_numbers()),\n                )\n                if len(rejections) > 0:\n                    res_values = nda_sim[rejections]\n                    txt += \" | Latest rejections: \" \\\n                        \"%d [%s] (%s < %g): %s\" % (\n                            len(rejections),\n                            ph.convert_numbers_to_hyphenated_ranges(\n                                rejections),\n                            self._measure,\n                            self._threshold,\n                            np.round(res_values, 2).tolist(),\n                        )\n                ph.print_info(txt)\n\n            # Log stack where all slices were rejected\n            if stack.get_number_of_slices() == 0:\n                remove_stacks.append(stack)\n\n        # Remove stacks where all slices where rejected\n        for stack in remove_stacks:\n            self._stacks.remove(stack)\n            if self._verbose:\n                ph.print_info(\"Stack '%s' removed entirely.\" %\n                              stack.get_filename())\n"
  },
  {
    "path": "niftymic/utilities/parameter_normalization.py",
    "content": "##\n# \\file parameter_normalization.py\n# \\brief      Class containing functions to normalize parameters. This can be\n#             used to normalize parameters used for optimization e.g.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2016\n#\n\n\n## Import libraries\nimport sys\n# import SimpleITK as sitk\n# import itk\nimport numpy as np\n\n## Import modules\nimport pysitk.simple_itk_helper as sitkh\n\n\n##\n#       Class to normalize parameters\n# \\date       2016-11-10 17:05:30+0000\n#\nclass ParameterNormalization(object):\n\n    ##\n    #       Constructor\n    # \\date       2016-11-10 17:08:25+0000\n    #\n    # \\param      self              The object\n    # \\param      parameters_array  (N x N_param)-np.array. N_param\n    #                               different types of parameters and N amount\n    #                               of each of them.\n    #\n    def __init__(self, parameters_array):\n\n        ## Create copy of parameters\n        self._parameters_array = np.array(parameters_array)\n\n        ## Amount of different parameters\n        self._N_parameters = self._parameters_array.shape[1]\n\n        self._coefficients = 1.* np.concatenate((np.zeros((1,self._N_parameters)), np.ones((1,self._N_parameters))))\n\n\n    ##\n    #       Gets the normalization coefficients as (2 x\n    #             N_param)-np.array.\n    # \\date       2016-11-10 18:00:52+0000\n    #\n    # The first row denotes the mean and the second the computed standard\n    # deviation of the originally provided parameter array.\n    #\n    # \\param      self  The object\n    #\n    # \\return     The normalization coefficients as (2 x N_param)-np.array.\n    #\n    def get_normalization_coefficients(self):\n        return np.array(self._coefficients)\n\n\n    ##\n    #       Calculates the normalization coefficients which will be used\n    #             for normalization and denormalization routines.\n    # \\date       2016-11-10 18:01:17+0000\n    #\n    # \\param      self  The object\n    #\n    def compute_normalization_coefficients(self):\n        \n        coefficients = np.zeros((2, self._N_parameters))\n\n        for i in range(0, self._N_parameters):\n            coefficients[0,i] = np.mean(self._parameters_array[:,i])\n\n            sigma = np.std(self._parameters_array[:,i])\n            if abs(sigma) < 1e-8:\n                coefficients[1,i] = 1.\n            else:\n                coefficients[1,i] = sigma\n\n        self._coefficients = coefficients\n\n\n    ##\n    #       Normalize parameters based on previously computed\n    #             coefficients.\n    # \\date       2016-11-10 18:04:05+0000\n    #\n    # \\remark I would like to not make a copy of the parameters\n    #\n    # \\param      self        The object\n    # \\param      parameters  (N x N_params)-np.array to be normalized\n    #\n    # \\return     normalized parameter array\n    #\n    def normalize_parameters(self, parameters):\n\n        parameters = np.array(parameters)\n\n        ## Compute p_norm = (p - mean)/std\n        for i in range(0, self._N_parameters):\n            parameters[:,i] = (parameters[:,i] - self._coefficients[0,i])/self._coefficients[1,i]\n\n        return parameters\n\n\n    ##\n    #       Denormalize parameters based on previously computed\n    #             coefficients.\n    # \\date       2016-11-10 18:05:49+0000\n    #\n    # \\param      self        The object\n    # \\param      parameters  (N x N_params)-np.array to be normalized\n    #\n    # \\return     denormalized parameter array\n    #\n    def denormalize_parameters(self, parameters):\n\n        parameters = np.array(parameters)\n\n        ## Compute p = p_norm*std + mean\n        for i in range(0, self._N_parameters):\n            parameters[:,i] = parameters[:,i]*self._coefficients[1,i] + self._coefficients[0,i]\n\n        return parameters\n\n"
  },
  {
    "path": "niftymic/utilities/segmentation_propagation.py",
    "content": "##\n# \\file SegmentationPropagation.py\n# \\brief      { item_description }\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       May 2017\n#\n\n\n# Import libraries\nimport sys\nimport itk\nimport SimpleITK as sitk\nimport numpy as np\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.stack_mask_morphological_operations as stmorph\n\n\n##\n# Class implementing the segmentation propagation from one image to another\n# \\date       2017-05-10 23:48:08+0100\n#\nclass SegmentationPropagation(object):\n\n    # Constructor\n    def __init__(self,\n                 stack=None,\n                 template=None,\n                 registration_method=None,\n                 use_template_mask=True,\n                 dilation_radius=0,\n                 dilation_kernel=\"Ball\",\n                 use_dilation_in_plane_only=True,\n                 interpolator=\"NearestNeighbor\"):\n\n        self._stack = stack\n        self._template = template\n\n        self._registration_method = registration_method\n\n        self._dilation_radius = dilation_radius\n        self._dilation_kernel = dilation_kernel\n        self._use_dilation_in_plane_only = use_dilation_in_plane_only\n        self._interpolator = interpolator\n\n        self._stack_sitk = None\n        self._stack_sitk_mask = None\n        self._registration_transform_sitk = None\n        self._use_template_mask = use_template_mask\n\n    def set_stack(self, stack):\n        self._stack = stack\n\n    def get_stack(self):\n        return self._stack\n\n    def set_template(self, template):\n        self._template = template\n\n    def get_template(self):\n        return self._template\n\n    def set_dilation_radius(self, dilation_radius):\n        self._dilation_radius = dilation_radius\n\n    def get_dilation_radius(self):\n        return self._dilation_radius\n\n    def set_dilation_kernel(self, dilation_kernel):\n        if dilation_kernel not in ['Ball', 'Box', 'Annulus', 'Cross']:\n            raise ValueError(\n                \"Dilation kernel must be 'Ball', 'Box', 'Annulus' or 'Cross'.\")\n        self._dilation_kernel = dilation_kernel\n\n    def get_dilation_kernel(self):\n        return self._dilation_kernel\n\n    def get_segmented_stack(self):\n\n        # Create new Stack instance\n        stack_aligned_masked = st.Stack.from_sitk_image(\n            image_sitk=self._stack_sitk,\n            filename=self._stack.get_filename(),\n            image_sitk_mask=self._stack_sitk_mask,\n            slice_thickness=self._stack.get_slice_thickness(),\n        )\n\n        return stack_aligned_masked\n\n    def get_registration_transform_sitk(self):\n        return self._registration_transform_sitk\n\n    def run_segmentation_propagation(self):\n\n        if self._stack is None or self._template is None:\n            raise ValueError(\"Specify stack and template first\")\n\n        # Choose interpolator\n        try:\n            interpolator_str = self._interpolator\n            interpolator = eval(\"sitk.sitk\" + interpolator_str)\n        except:\n            raise ValueError(\"Error: interpolator is not known\")\n\n        self._stack_sitk = sitk.Image(self._stack.sitk)\n\n        # Register stack to template\n        if self._registration_method is not None:\n            self._registration_method.set_fixed(self._template)\n            self._registration_method.set_moving(self._stack)\n            self._registration_method.use_fixed_mask(self._use_template_mask)\n            self._registration_method.run()\n\n            self._registration_transform_sitk = self._registration_method.get_registration_transform_sitk()\n            self._registration_transform_sitk = eval(\n                \"sitk.\" + self._registration_transform_sitk.GetName() + \"(self._registration_transform_sitk.GetInverse())\")\n\n            self._stack_sitk = sitkh.get_transformed_sitk_image(\n                self._stack_sitk, self._registration_transform_sitk)\n\n        # Propagate mask\n        self._stack_sitk_mask = sitk.Resample(self._template.sitk_mask, self._stack_sitk, sitk.Euler3DTransform(\n        ), interpolator, 0, self._template.sitk_mask.GetPixelIDValue())\n\n        # Dilate mask\n        if self._dilation_radius > 0:\n\n            stack_mask_morpher = stmorph.StackMaskMorphologicalOperations.from_sitk_mask(\n                mask_sitk=self._stack_sitk_mask,\n                dilation_radius=self._dilation_radius,\n                dilation_kernel=self._dilation_kernel,\n                use_dilation_in_plane_only=self._use_dilation_in_plane_only,\n            )\n            stack_mask_morpher.run_dilation()\n            self._stack_sitk_mask = stack_mask_morpher.get_processed_mask_sitk()\n\n            # sitkh.show_sitk_image(self._stack_sitk_mask)\n"
  },
  {
    "path": "niftymic/utilities/siena.py",
    "content": "##\n# \\file siena.py\n# \\brief\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Nov 2016\n#\n\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport sys\nimport os\nimport re\nfrom skimage.measure import compare_ssim as ssim\n\n# Import modules\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nfrom niftymic.definitions import DIR_TMP\n\n\nclass Siena(object):\n\n    def __init__(self,\n                 stack1,\n                 stack2,\n                 dir_output=\"./siena/\",\n                 options='-B \"-B -f 0.1\" -2',\n                 dir_tmp=os.path.join(DIR_TMP, \"siena/\")):\n\n        self._stack1 = st.Stack.from_stack(stack1)\n        self._stack2 = st.Stack.from_stack(stack2)\n        self._dir_output = dir_output\n        self._dir_tmp = dir_tmp\n        self._options = options\n\n    def run(self):\n        ph.create_directory(dir_tmp, delete_files=True)\n\n        # Write images\n        sitkh.write_nifti_image_sitk(self._stack1.sitk, self._dir_tmp +\n                                  self._stack1.get_filename() + \".nii.gz\")\n        sitkh.write_nifti_image_sitk(self._stack2.sitk, self._dir_tmp +\n                                  self._stack2.get_filename() + \".nii.gz\")\n\n        cmd = \"siena \"\n        cmd += self._dir_tmp + self._stack1.get_filename() + \".nii.gz \"\n        cmd += self._dir_tmp + self._stack2.get_filename() + \".nii.gz \"\n        cmd += \"-o \" + self._dir_output + \" \"\n        cmd += self._options\n\n        time_start = ph.start_timing()\n        ph.execute_command(cmd)\n        self._elapsed_time = ph.stop_timing(time_start)\n\n        # Extract measures from report\n        self._extract_percentage_brain_volume_change()\n\n    def print_statistics(self):\n        print(\"\\tElapsed time: %s\" % (self._elapsed_time))\n        print(\"\\tPercentage Brain Volume Change (PBVC): %.2f%%\" %\n              (self._percentage_brain_volume_change))\n\n    ##\n    # Percentage Brain Volume Change\n    # \\date       2016-11-27 17:42:55+0000\n    #\n    # \\param      self  The object\n    #\n    def _extract_percentage_brain_volume_change(self):\n        datafile = file(self._dir_output + \"report.siena\")\n\n        for line in datafile:\n            if \"finalPBVC\" in line:\n                parts = line.split(\" \")\n                break\n        self._percentage_brain_volume_change = float(parts[1])\n\n    def get_percentage_brain_volume_change(self):\n        return self._percentage_brain_volume_change\n"
  },
  {
    "path": "niftymic/utilities/stack_mask_morphological_operations.py",
    "content": "##\n# \\file stack_mask_morphological_operations.py\n# \\brief      { item_description }\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       May 2017\n#\n\n\nimport sys\nimport itk\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\n\n\n##\n# Class implementing the segmentation propagation from one image to another\n# \\date       2017-05-10 23:48:08+0100\n#\nclass StackMaskMorphologicalOperations(object):\n\n    ##\n    # { constructor_description }\n    # \\date       2017-05-18 16:58:23+0100\n    #\n    # \\param      self                        The object\n    # \\param      mask_sitk                   The mask sitk\n    # \\param      dilation_radius             The dilation radius\n    # \\param      dilation_kernel             The dilation kernel\n    # \\param      use_dilation_in_plane_only  The use dilation in plane only\n    #\n    def __init__(self, dilation_radius, dilation_kernel, use_dilation_in_plane_only):\n\n        self._dilation_radius = dilation_radius\n        self._dilation_kernel = dilation_kernel\n        self._use_dilation_in_plane_only = use_dilation_in_plane_only\n\n    @classmethod\n    def from_sitk_mask(cls, mask_sitk=None, dilation_radius=0, dilation_kernel=\"Ball\", use_dilation_in_plane_only=True):\n\n        self = cls(dilation_radius=dilation_radius, dilation_kernel=dilation_kernel,\n                   use_dilation_in_plane_only=use_dilation_in_plane_only)\n\n        self._mask_sitk = mask_sitk\n        self._stack = None\n\n        return self\n\n    @classmethod\n    def from_stack(cls, stack=None, dilation_radius=0, dilation_kernel=\"Ball\", use_dilation_in_plane_only=True):\n\n        self = cls(dilation_radius=dilation_radius, dilation_kernel=dilation_kernel,\n                   use_dilation_in_plane_only=use_dilation_in_plane_only)\n\n        self._mask_sitk = stack.sitk_mask\n        self._stack = stack\n\n        return self\n\n    def set_mask_sitk(self, mask_sitk):\n        self._mask_sitk = mask_sitk\n\n    def get_mask_sitk(self):\n        return self._mask_sitk\n\n    def get_stack(self):\n        return st.Stack.from_stack(self._stack)\n\n    def set_dilation_radius(self, dilation_radius):\n        self._dilation_radius = dilation_radius\n\n    def get_dilation_radius(self):\n        return self._dilation_radius\n\n    def set_dilation_kernel(self, dilation_kernel):\n        if dilation_kernel not in ['Ball', 'Box', 'Annulus', 'Cross']:\n            raise ValueError(\n                \"Dilation kernel must be 'Ball', 'Box', 'Annulus' or 'Cross'.\")\n        self._dilation_kernel = dilation_kernel\n\n    def get_dilation_kernel(self):\n        return self._dilation_kernel\n\n    def get_processed_mask_sitk(self):\n        return sitk.Image(self._mask_sitk)\n\n    def get_processed_stack(self):\n        if self._stack is None:\n            raise ValueError(\"No Stack instance was provided\")\n        else:\n            return st.Stack.from_sitk_image(self._stack.sitk, self._stack.get_filename(), self._mask_sitk)\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    def run_dilation(self):\n\n        time_start = ph.start_timing()\n\n        dilater = sitk.BinaryDilateImageFilter()\n        dilater.SetKernelType(eval(\"sitk.sitk\" + self._dilation_kernel))\n        dilater.SetKernelRadius(self._dilation_radius)\n\n        if self._use_dilation_in_plane_only:\n\n            shape = self._mask_sitk.GetSize()\n            N_slices = shape[2]\n            nda_mask = np.zeros(shape[::-1], dtype=np.uint8)\n\n            for i in range(0, N_slices):\n                slice_mask_sitk = self._mask_sitk[:, :, i:i + 1]\n                mask_sitk = dilater.Execute(slice_mask_sitk)\n                nda_mask[i, :, :] = sitk.GetArrayFromImage(mask_sitk)\n\n            mask_sitk = sitk.GetImageFromArray(nda_mask)\n            mask_sitk.CopyInformation(self._mask_sitk)\n            self._mask_sitk = mask_sitk\n\n        else:\n            self._mask_sitk = dilater.Execute(self._mask_sitk)\n\n        if self._stack is not None:\n            self._stack = st.Stack.from_sitk_image(\n                self._stack.sitk,\n                image_sitk_mask=self._mask_sitk,\n                filename=self._stack.get_filename(),\n                slice_thickness=self._stack.get_slice_thickness(),\n            )\n\n        self._computational_time = ph.stop_timing(time_start)\n"
  },
  {
    "path": "niftymic/utilities/target_stack_estimator.py",
    "content": "##\n# \\file target_stack_estimator.py\n# \\brief      Class to estimate target stack automatically\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       January 2018\n#\n\n\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\n\n\n##\n# Class to estimate target stack automatically\n# \\date       2018-01-26 16:32:11+0000\n#\nclass TargetStackEstimator(object):\n\n    def __init__(self):\n        self._target_stack_index = None\n\n        self._compute_motion_score = {\n\n            # implementation according to github fetalReconstruction\n            \"github\": self._compute_motion_score_github,\n\n            # implementation according to TMI paper Kainz2015\n            \"kainz2015\": self._compute_motion_score_kainz2015,\n        }\n        # appears more meaningful to me (see comments below)\n        self._mode = \"kainz2015\"\n\n        self._computational_time = ph.get_zero_time()\n\n    def get_target_stack_index(self):\n        return self._target_stack_index\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    @staticmethod\n    def _compute_volume(file_path):\n        mask_sitk = sitkh.read_nifti_image_sitk(str(file_path), sitk.sitkUInt8)\n\n        # Compute mask volume\n        mask_nda = sitk.GetArrayFromImage(mask_sitk)\n        spacing = np.array(mask_sitk.GetSpacing())\n        volume = np.sum(mask_nda) * spacing.prod()\n\n        return volume\n\n    @staticmethod\n    def _compute_singular_values(stack):\n\n        # (z,y,x) array\n        nda = sitk.GetArrayFromImage(stack.sitk)\n\n        # reshape to M x K, M number of slice pixels, K number of slices\n        A = nda.reshape(nda.shape[0], -1).transpose()\n\n        U, s, Vt = np.linalg.svd(A)\n\n        return s\n\n    ##\n    # Calculates the motion score similar to TMI Kainz2015 paper. Instead, a\n    # relative rank is used in order to not penalize stacks with more slices.\n    # (Motion score: A lower score indicates less motion).\n    # @date       2019-08-31 16:01:12+0100\n    #\n    # @param      sing_values  singular values, vector\n    # @param      threshold    error threshold, float\n    #\n    # @return     The motion score, float.\n    #\n    @staticmethod\n    def _compute_motion_score_kainz2015(\n            sing_values, threshold=np.sqrt(1 - 0.99**2)):\n\n        sing_values_2 = np.square(sing_values)\n\n        # compute Frobenius norm\n        A_norm = np.sum(sing_values_2)\n\n        # compute relative rank-approximation error\n        # the higher r, the smaller the delta_r\n        delta_r = np.sqrt(np.array([\n            np.sum(sing_values_2[r + 1:]) / A_norm\n            for r in range(len(sing_values_2))\n        ]))\n\n        # find lowest rank approximation that leads to error < threshold\n        r = np.where(delta_r < threshold)[0][0] + 1\n\n        # use relative rank so as to not \"penalise\" stack with more slices\n        r_rel = r / float(len(sing_values))\n\n        # surrogate motion score, the lower the better\n        motion_score = r_rel * delta_r[r - 1]\n\n        return motion_score\n\n    ##\n    # Calculates the motion score based on TMI Kainz2015 paper, but the version\n    # as found on the GitHub fetalReconstruction repo. (A lower motion score\n    # shall indicate less motion).\n    # @date       2019-08-31 16:01:12+0100\n    #\n    # @param      sing_values  singular values, vector\n    # @param      threshold    error threshold, float\n    #\n    # @return     The motion score, float.\n    #\n    @staticmethod\n    def _compute_motion_score_github(sing_values, threshold=0.99):\n        sing_values_2 = np.square(sing_values)\n\n        # compute Frobenius norm\n        A_norm = np.sum(sing_values_2)\n\n        # compute relative rank-approximation quality\n        # the higher r, the higher the delta_r\n        delta_r = np.sqrt(np.array([\n            np.sum(sing_values_2[0:r + 1]) / A_norm\n            for r in range(len(sing_values_2))\n        ]))\n\n        # find highest rank approximation that leads to error <\n        # threshold\n        r = np.where(delta_r < threshold)[0][-1] + 1\n        # r = np.where(delta_r > threshold)[0][0] + 1  # lowest\n\n        # surrogate motion score\n        # NOTE: that's weird to me, should be the lower the better,\n        # but delta_r refers to relative rank-approximation quality,\n        # thus, the higher the better!\n        motion_score = r * delta_r[r - 1]\n\n        return motion_score\n\n    ##\n    # Use stack with largest mask volume as target stack\n    # \\date       2018-01-26 16:52:39+0000\n    #\n    # \\param      cls               The cls\n    # \\param      file_paths_masks  paths to image masks as list of strings\n    #\n    @classmethod\n    def from_volume(cls, file_paths_masks):\n        t0 = ph.start_timing()\n        target_stack_estimator = cls()\n\n        volumes = np.array([\n            TargetStackEstimator._compute_volume(f) for f in file_paths_masks\n        ])\n\n        # find index to smallest \"valid\" volume, i.e. volume > q * median\n        index = np.argmax(\n            volumes[np.argsort(volumes)] > 0.7 * np.median(volumes))\n        index = np.argsort(volumes)[index]\n\n        # Get index corresponding to maximum volume stack mask\n        # index = np.argmax(volumes)\n        # index = np.argmin(volumes)\n\n        # Get index corresponding to median volume stack mask\n        # index = np.argsort(volumes)[len(volumes)//2]\n\n        target_stack_estimator._target_stack_index = index\n\n        # computational time\n        target_stack_estimator._computational_time = ph.stop_timing(t0)\n\n        return target_stack_estimator\n\n    ##\n    # Compute target stack based on method presented in TMI Kainz2015. However,\n    # only on masked anatomy (bounding box) is used in addition to a relative\n    # rank weighting.\n    # @date       2019-08-30 14:33:24+0100\n    #\n    # @param      cls               The cls\n    # @param      file_paths        The file paths\n    # @param      file_paths_masks  The file paths masks\n    #\n    # @return     target_stack_estimator instance\n    #\n    @classmethod\n    def from_motion_score(cls, file_paths, file_paths_masks):\n\n        if len(file_paths) != len(file_paths_masks):\n            raise ValueError(\n                \"Number of provided images and masks must match\")\n\n        t0 = ph.start_timing()\n        tse = cls()\n\n        volumes = np.array([\n            TargetStackEstimator._compute_volume(f) for f in file_paths_masks\n        ])\n\n        # only allow stacks with minimum volume, i.e. anatomical/brain coverage\n        vol_min = 0.7 * np.median(volumes)\n        indices = [i for i in range(len(volumes)) if volumes[i] > vol_min]\n\n        # read all eligible stacks\n        stacks = [\n            st.Stack.from_filename(\n                file_path=file_paths[i],\n                file_path_mask=file_paths_masks[i],\n                extract_slices=False,\n            ) for i in indices\n        ]\n\n        # crop stack to bounding box of mask\n        stacks = [s.get_cropped_stack_based_on_mask() for s in stacks]\n\n        # debug\n        # for i_stack, stack in enumerate(stacks):\n        #     stack.show(label=str(indices[i_stack]))\n\n        # compute motion scores of eligible stacks\n        motion_scores = [None] * len(indices)\n        for i_stack in range(len(indices)):\n\n            # compute singular values\n            s = TargetStackEstimator._compute_singular_values(stacks[i_stack])\n\n            # compute motion score\n            motion_scores[i_stack] = tse._compute_motion_score[tse._mode](s)\n\n        # select stack with minimum motion (score)\n        selection_best = np.argmin(motion_scores)\n\n        # reference back to input file_paths index\n        target_stack_index = indices[selection_best]\n\n        tse._target_stack_index = target_stack_index\n\n        # computational time\n        tse._computational_time = ph.stop_timing(t0)\n\n        # debug\n        # print(indices, len(indices), len(file_paths))\n        # print(\"Best: %d\" % target_stack_index)\n        # print(motion_scores)\n        # print(tse.get_computational_time())\n        # ph.killall_itksnap()\n\n        return tse"
  },
  {
    "path": "niftymic/utilities/template_stack_estimator.py",
    "content": "##\n# \\file template_stack_estimator.py\n# \\brief      Class to estimate template stack automatically\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       January 2018\n#\n\n\nimport os\nimport re\nimport json\nimport numpy as np\nimport skimage.measure\nimport SimpleITK as sitk\n\nimport pysitk.simple_itk_helper as sitkh\n\nfrom niftymic.definitions import DIR_TEMPLATES, TEMPLATES_INFO\n\n\n##\n# Class to estimate template stack automatically\n# \\date       2018-01-26 16:32:11+0000\n#\nclass TemplateStackEstimator(object):\n\n    def __init__(self):\n        self._template_path = None\n        self._estimated_gw = None\n\n    ##\n    # Gets the path to estimated template.\n    # \\date       2018-01-27 02:14:53+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The path to template.\n    #\n    def get_path_to_template(self):\n        return self._template_path\n\n    def get_estimated_gw(self):\n        return self._estimated_gw\n\n    ##\n    # Select template with similar brain volume\n    # \\date       2018-01-26 16:52:39+0000\n    #\n    # \\param      cls               The cls\n    # \\param      file_paths_masks  paths to image masks as list of strings\n    #\n    @classmethod\n    def from_mask(cls, file_path_mask):\n        template_stack_estimator = cls()\n\n        mask_sitk = sitkh.read_nifti_image_sitk(\n            file_path_mask, sitk.sitkUInt8)\n        mask_nda = sitk.GetArrayFromImage(mask_sitk)\n\n        # get largest connected region (if more than one connected region)\n        mask_nda = TemplateStackEstimator.get_largest_connected_region_mask(\n            mask_nda)\n        spacing = np.array(mask_sitk.GetSpacing())\n        volume = len(np.where(mask_nda > 0)[0]) * spacing.prod()\n\n        # Read in template info\n        path_to_template_info = os.path.join(DIR_TEMPLATES, TEMPLATES_INFO)\n        with open(path_to_template_info) as json_file:\n            dic = json.load(json_file)\n\n        # Get gestational ages as list of integers\n        gestational_ages = sorted([int(gw) for gw in dic.keys()])\n\n        # Get matching gestational age\n        template_volumes = np.array(\n            [dic[str(k)][\"volume_mask\"] for k in gestational_ages])\n        index = np.argmin(np.abs(template_volumes - volume))\n        template_stack_estimator._estimated_gw = int(gestational_ages[index])\n        template_stack_estimator._template_path = os.path.join(\n            DIR_TEMPLATES, dic[str(gestational_ages[index])][\"image\"])\n\n        return template_stack_estimator\n\n        # # Ensure valid index after correction\n        # index = np.max([0, index - 1])\n        # # index = np.min([index + 1, len(template_volumes)-1])\n\n        # # Matching gestational age/week\n        # gw_match = str(gestational_ages[index])\n\n        # template_stack_estimator._template_path = os.path.join(\n        #     DIR_TEMPLATES, dic[gw_match][\"image\"])\n\n        # return template_stack_estimator\n\n        # # Find template which has slightly smaller mask volume\n        # for k in gestational_ages:\n        #     if dic[str(k)][\"volume_mask_dil\"] > volume:\n        #         key = str(np.max([gestational_ages[0], k - 1]))\n        #         template_stack_estimator._estimated_gw = int(key)\n\n        #         template_stack_estimator._template_path = os.path.join(\n        #             DIR_TEMPLATES, dic[key][\"image\"])\n        #         return template_stack_estimator\n\n        # # Otherwise, return path to oldest template image available\n        # template_stack_estimator._estimated_gw = int(gestational_ages[-1])\n\n        # template_stack_estimator._template_path = os.path.join(\n        #     DIR_TEMPLATES, dic[str(gestational_ages[-1])][\"image\"])\n        # return template_stack_estimator\n\n    ##\n    # Gets the label/mask representing the largest connected region.\n    # \\date       2019-02-26 16:30:01+0000\n    #\n    # \\param      mask_nda  The mask nda\n    #\n    # \\return     The largest connected region as np.array.\n    #\n    @staticmethod\n    def get_largest_connected_region_mask(mask_nda):\n\n        # get label for each connected component\n        labels_nda = skimage.measure.label(mask_nda)\n\n        # only pick largest connected region\n        if labels_nda.max() > 1:\n            volumes = [\n                labels_nda[np.where(labels_nda == i)].sum()\n                for i in range(1, labels_nda.max() + 1)\n            ]\n            label_max = np.argmax(np.array(volumes)) + 1\n            mask_nda = np.zeros_like(mask_nda)\n            mask_nda[np.where(labels_nda == label_max)] = 1\n\n        return mask_nda\n"
  },
  {
    "path": "niftymic/utilities/toolkit_executor.py",
    "content": "##\n# \\file toolkit_executor.py\n# \\brief      generates function calls to execute other reconstruction toolkits\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Jan 2018\n#\n\nimport os\nimport pysitk.python_helper as ph\n\nEXE_IRTK = {\n    \"workstation\": \"/home/mebner/Development/VolumetricReconstruction_ImperialCollege/source/bin/SVRreconstructionGPU\"\n}\n\n\n##\n# Class to generate functions calls to execute other reconstruction toolkits\n# \\date       2018-01-27 02:12:00+0000\n#\nclass ToolkitExecuter(object):\n\n    def __init__(self, paths_to_images, paths_to_masks, dir_output):\n        self._paths_to_images = paths_to_images\n        self._paths_to_masks = paths_to_masks\n        self._dir_output = dir_output\n\n        # separator for command line export\n        self._sep = \" \\\\\\n\"\n\n        self._subdir_temp = \"temp\"\n\n    ##\n    # Gets the function call for fetalReconstruction toolkit provided by\n    # Bernhard Kainz.\n    # \\date       2018-01-27 02:12:26+0000\n    #\n    # \\param      self         The object\n    # \\param      option_args  The option arguments\n    # \\param      exe          The executable\n    # \\param      output_name  The output name\n    #\n    # \\return     The function call irtk.\n    #\n    def get_function_call_irtk(\n        self,\n        option_args=['-d 0', '--useCPU', '--resolution 1'],\n        exe=None,\n        output_name=\"IRTK_SRR.nii.gz\",\n        kernel_mask_dilation=None,\n        verbose=False,\n    ):\n        if exe is None:\n            exe = EXE_IRTK[\"workstation\"]\n\n        self._dir_temp = os.path.join(\"${DIR_OUT}\", self._subdir_temp)\n\n        cmd_args = []\n\n        # store pwd\n        cmd_args.append(\"CWD=$(pwd)\")\n        cmd_args.append(\"DIR_OUT=%s\" % self._dir_output)\n\n        # change to output directory\n        cmd_args.append(\"printf 'Change to output directory: %s\\n' ${DIR_OUT}\")\n        # cmd_args.append(\"\\necho 'Change to output directory'\")\n        cmd_args.append(\"mkdir -p ${DIR_OUT}\")\n        cmd_args.append(\"cd ${DIR_OUT}\")\n\n        # create temp directory if required\n        cmd_args.append(\"\\necho 'Create temp directory'\")\n        cmd_args.append(\"mkdir -p %s\" % self._dir_temp)\n        cmd_args.append(\"cd %s\" % self._dir_temp)\n\n        # dilate masks\n        if kernel_mask_dilation is not None:\n            cmd_args.append(\"\\necho 'Dilate masks'\")\n            cmd_args.append(self._exe_dilate_masks(\n                kernel_mask_dilation, self._paths_to_masks))\n\n        # exe to determine slice thickness for toolkit\n        cmd_args.append(\"\\necho 'Fetch slice thickness for all stacks'\")\n        cmd_args.append(self._exe_to_fetch_slice_thickness(\n            self._paths_to_images))\n\n        # toolkit execution\n        cmd_args.append(\"\\necho 'IRTK Toolkit Execution'\")\n        exe_args = [exe]\n        exe_args.append(\"-o %s\" % output_name)\n        exe_args.append(\"-i %s%s\" %\n                        (self._sep, self._sep.join(self._paths_to_images)))\n        # exe_args.append(\"--manualMask %s\" % self._paths_to_masks[0])  #\n        # causes cuda sync error!?\n        exe_args.append(\"-m %s%s\" %\n                        (self._sep, self._sep.join(self._paths_to_masks)))\n        exe_args.append(\"--thickness `printf \\\"%s\\\" \\\"${thickness}\\\"`\")\n        exe_args.extend(option_args)\n        toolkit_execution = \"%s\" % self._sep.join(exe_args)\n        cmd_args.append(toolkit_execution)\n\n        cmd_args.append(\"cp -p %s ${DIR_OUT}/\" % (output_name))\n        cmd_args.append(\"cd ${DIR_OUT}\")\n\n        if not verbose:\n            cmd_args.append(\"\\necho 'Delete temp directory'\")\n            cmd_args.append(\"rm -rf %s\" % self._dir_temp)\n\n        # cmd_args.append(\"\\necho 'Change back to original directory'\")\n        cmd_args.append(\"printf 'Change back to original directory: %s\\n' ${CWD}\")\n        cmd_args.append(\"cd ${CWD}\")\n        cmd_args.append(\"\\n\")\n\n        cmd = (\" \\n\").join(cmd_args)\n        return cmd\n\n    @staticmethod\n    def write_function_call_to_file(function_call, path_to_file):\n        text = \"#!/bin/zsh\\n\\n%s\" % function_call\n        ph.write_to_file(path_to_file, text, verbose=False)\n        ph.execute_command(\"chmod +x %s\" % path_to_file, verbose=False)\n\n    ##\n    # Provide bash-commands to read out slice thickness on-the-fly\n    #\n    # Rationale: IRTK recon toolkit assumes otherwise a thickness of twice the\n    # voxel spacing by default\n    # \\date       2018-01-27 02:12:52+0000\n    #\n    # \\param      paths_to_images  The paths to images\n    #\n    # \\return     bash command as string\n    #\n    def _exe_to_fetch_slice_thickness(self, paths_to_images):\n        cmd_args = []\n        cmd_args.append(\"args=()\")\n        cmd_args.append(\"for i in %s\" % (self._sep.join(paths_to_images)))\n        cmd_args.append(\"do\")\n        cmd_args.append(\n            \"t=$(fslhd ${i} | grep pixdim3 | awk -F ' ' '{print $2}')\")\n        cmd_args.append(\"args+=(\\\" ${t}\\\")\")\n        cmd_args.append(\"done\")\n        cmd_args.append(\"thickness=${args[@]}\")\n        cmd_args.append(\"\")\n        cmd = (\"\\n\").join(cmd_args)\n        return cmd\n\n    ##\n    # Provide bash-commands to read out slice thickness on-the-fly\n    #\n    # Rationale: IRTK recon toolkit assumes otherwise a thickness of twice the\n    # voxel spacing by default\n    # \\date       2018-01-27 02:12:52+0000\n    #\n    # \\param      paths_to_images  The paths to images\n    #\n    # \\return     bash command as string\n    # #\n    def _exe_dilate_masks(self, kernel, paths_to_masks, label=1):\n        cmd_loop = []\n        kernel_str = [str(k) for k in kernel]\n\n        # Export dilated mask to temp directory\n        for i_mask, path_to_mask in enumerate(paths_to_masks):\n            directory = os.path.dirname(path_to_mask)\n            mask_filename = os.path.basename(path_to_mask)\n            path_to_mask_dilated = os.path.join(\n                self._dir_temp, ph.append_to_filename(mask_filename, \"_dil\"))\n\n            cmd_args = [\"c3d\"]\n            cmd_args.append(path_to_mask)\n            cmd_args.append(\"-dilate %s %smm\" % (label, \"x\".join(kernel_str)))\n            cmd_args.append(\"-o %s\" % path_to_mask_dilated)\n            cmd = self._sep.join(cmd_args)\n\n            cmd_loop.append(cmd)\n            paths_to_masks[i_mask] = path_to_mask_dilated\n        return \"\\n\".join(cmd_loop)\n"
  },
  {
    "path": "niftymic/utilities/volumetric_reconstruction_pipeline.py",
    "content": "##\n# \\file volumetric_reconstruction_pipeline.py\n# \\brief      Collection of modules useful for registration and\n#             reconstruction tasks.\n#\n# E.g. Volume-to-Volume Registration, Slice-to-Volume registration,\n# Multi-component Reconstruction.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Aug 2017\n#\n\nimport six\nimport numpy as np\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.validation.motion_evaluator as me\nimport niftymic.utilities.outlier_rejector as outre\nimport niftymic.registration.transform_initializer as tinit\nimport niftymic.reconstruction.scattered_data_approximation as sda\nimport niftymic.utilities.binary_mask_from_mask_srr_estimator as bm\n\nfrom niftymic.definitions import VIEWER\n\n\n##\n# Class which holds basic interface for all modules\n# \\date       2017-08-08 02:20:40+0100\n#\nclass Pipeline(object):\n    __metaclass__ = ABCMeta\n\n    def __init__(self, stacks, verbose, viewer):\n        self._stacks = stacks\n        self._verbose = verbose\n        self._viewer = viewer\n\n        self._computational_time = ph.get_zero_time()\n\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n\n    def get_stacks(self):\n        return [st.Stack.from_stack(stack) for stack in self._stacks]\n\n    def set_verbose(self, verbose):\n        self._verbose = verbose\n\n    def get_verbose(self):\n        return self._verbose\n\n    def get_computational_time(self):\n        return self._computational_time\n\n    def run(self):\n\n        time_start = ph.start_timing()\n\n        self._run()\n\n        self._computational_time = ph.stop_timing(time_start)\n\n        if self._verbose:\n            ph.print_info(\"Required computational time: %s\" %\n                          (self.get_computational_time()))\n\n    @abstractmethod\n    def _run(self):\n        pass\n\n\n##\n# Class which holds basic interface for all registration associated modules\n# \\date       2017-08-08 02:21:17+0100\n#\nclass RegistrationPipeline(Pipeline):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Store variables relevant to register stacks to a certain reference volume\n    # \\date       2017-08-08 02:21:56+0100\n    #\n    # \\param      self                 The object\n    # \\param      verbose              Verbose output, bool\n    # \\param      stacks               List of Stack objects\n    # \\param      reference            Reference as Stack object\n    # \\param      registration_method  Registration method, e.g.\n    #                                  SimpleItkRegistration\n    #\n    def __init__(self, verbose, stacks, reference, registration_method, viewer):\n\n        Pipeline.__init__(self, stacks=stacks, verbose=verbose, viewer=viewer)\n\n        self._reference = reference\n        self._registration_method = registration_method\n\n    def set_reference(self, reference):\n        self._reference = reference\n\n    def get_reference(self):\n        return st.Stack.from_stack(self._reference)\n\n\n##\n# Class to perform Volume-to-Volume registration\n# \\date       2017-08-08 02:28:56+0100\n#\nclass VolumeToVolumeRegistration(RegistrationPipeline):\n\n    ##\n    # Store relevant information to perform Volume-to-Volume registration\n    # \\date       2017-08-08 02:29:13+0100\n    #\n    # \\param      self                 The object\n    # \\param      stacks               The stacks\n    # \\param      reference            The reference\n    # \\param      registration_method  The registration method\n    # \\param      verbose              The verbose\n    #\n    def __init__(self,\n                 stacks,\n                 reference,\n                 registration_method,\n                 verbose=1,\n                 viewer=VIEWER,\n                 robust=False,\n                 ):\n        RegistrationPipeline.__init__(\n            self,\n            stacks=stacks,\n            reference=reference,\n            registration_method=registration_method,\n            viewer=viewer,\n            verbose=verbose,\n        )\n        self._robust = robust\n\n    def _run(self):\n\n        ph.print_title(\"Volume-to-Volume Registration\")\n\n        for i in range(0, len(self._stacks)):\n            txt = \"Volume-to-Volume Registration -- \" \\\n                \"Stack %d/%d\" % (i + 1, len(self._stacks))\n            if self._verbose:\n                ph.print_subtitle(txt)\n            else:\n                ph.print_info(txt)\n\n            if self._robust:\n                transform_initializer = tinit.TransformInitializer(\n                    fixed=self._reference,\n                    moving=self._stacks[i],\n                    similarity_measure=\"NCC\",\n                    refine_pca_initializations=True,\n                )\n                transform_initializer.run()\n                transform_sitk = transform_initializer.get_transform_sitk()\n                transform_sitk = sitk.AffineTransform(\n                    transform_sitk.GetInverse())\n\n            else:\n                self._registration_method.set_moving(self._reference)\n                self._registration_method.set_fixed(self._stacks[i])\n                self._registration_method.run()\n                transform_sitk = self._registration_method.get_registration_transform_sitk()\n\n            # Update position of stack\n            self._stacks[i].update_motion_correction(transform_sitk)\n\n\n##\n# Class to perform Slice-To-Volume registration\n# \\date       2017-08-08 02:30:03+0100\n#\nclass SliceToVolumeRegistration(RegistrationPipeline):\n\n    ##\n    # { constructor_description }\n    # \\date       2017-08-08 02:30:18+0100\n    #\n    # \\param      self                 The object\n    # \\param      stacks               The stacks\n    # \\param      reference            The reference\n    # \\param      registration_method  Registration method, e.g.\n    #                                  SimpleItkRegistration\n    # \\param      verbose              The verbose\n    # \\param      print_prefix         Print at each iteration at the\n    #                                  beginning, string\n    #\n    def __init__(self,\n                 stacks,\n                 reference,\n                 registration_method,\n                 verbose=1,\n                 print_prefix=\"\",\n                 interleave=2,\n                 viewer=VIEWER,\n                 ):\n        RegistrationPipeline.__init__(\n            self,\n            stacks=stacks,\n            reference=reference,\n            registration_method=registration_method,\n            verbose=verbose,\n            viewer=viewer,\n        )\n        self._print_prefix = print_prefix\n        self._interleave = interleave\n\n    def set_print_prefix(self, print_prefix):\n        self._print_prefix = print_prefix\n\n    def _run(self):\n\n        ph.print_title(\"Slice-to-Volume Registration\")\n\n        self._registration_method.set_moving(self._reference)\n\n        for i, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n\n            transforms_sitk = {}\n\n            for j, slice_j in enumerate(slices):\n\n                txt = \"%sSlice-to-Volume Registration -- \" \\\n                    \"Stack %d/%d (%s) -- Slice %d/%d\" % (\n                        self._print_prefix,\n                        i + 1, len(self._stacks), stack.get_filename(),\n                        j + 1, len(slices))\n                if self._verbose:\n                    ph.print_subtitle(txt)\n                else:\n                    ph.print_info(txt)\n\n                self._registration_method.set_fixed(slice_j)\n                self._registration_method.run()\n\n                # Store information on registration transform\n                transform_sitk = \\\n                    self._registration_method.get_registration_transform_sitk()\n                transforms_sitk[slice_j.get_slice_number()] = transform_sitk\n\n            # Update position of slice\n            for slice in slices:\n                slice_number = slice.get_slice_number()\n                slice.update_motion_correction(transforms_sitk[slice_number])\n\n\n##\n# Class to perform registration for the stack based on a specified set of\n# slices\n# \\date       2017-10-16 12:52:18+0100\n#\nclass SliceSetToVolumeRegistration(RegistrationPipeline):\n\n    ##\n    # { constructor_description }\n    # \\date       2017-10-16 12:53:04+0100\n    #\n    # \\param      self                        The object\n    # \\param      stacks                      The stacks\n    # \\param      reference                   The reference\n    # \\param      registration_method         The registration method\n    # \\param      slice_index_sets_of_stacks  Dictionary specifying the slice\n    #                                         index sets for all stacks\n    # \\param      verbose                     The verbose\n    # \\param      print_prefix                The print prefix\n    #\n    def __init__(self,\n                 stack,\n                 reference,\n                 registration_method,\n                 slice_set_indices,\n                 verbose=1,\n                 print_prefix=\"\",\n                 viewer=VIEWER,\n                 ):\n        RegistrationPipeline.__init__(\n            self,\n            stacks=[stack],\n            reference=reference,\n            registration_method=registration_method,\n            verbose=verbose,\n            viewer=viewer,\n        )\n\n        self._print_prefix = print_prefix\n        self._slice_set_indices = slice_set_indices\n\n    def _run(self, debug=1):\n\n        stack = self._stacks[0]\n        slices = stack.get_slices()\n        for i, indices in enumerate(self._slice_set_indices):\n            txt = \"%s Split %d/%d -- Slices %s\" % (\n                self._print_prefix, i + 1,\n                len(self._slice_set_indices), str(indices))\n            if self._verbose:\n                ph.print_subtitle(txt)\n            else:\n                ph.print_info(txt)\n\n                image = self._get_stack_subgroup(indices)\n\n                if debug:\n                    first = np.linalg.norm(\n                        stack.get_slice(indices[0]).sitk.GetOrigin() -\n                        np.array(image.sitk[:, :, 0:1].GetOrigin()))\n                    last = np.linalg.norm(\n                        stack.get_slice(indices[-1]).sitk.GetOrigin() -\n                        np.array(image.sitk[:, :, -1:].GetOrigin()))\n                    if first > 1e-6:\n                        raise RuntimeError(\n                            \"Hierarchical S2V: first slice position flawed\")\n                    if last > 1e-6:\n                        raise RuntimeError(\n                            \"Hierarchical S2V: last slice position flawed\")\n\n                self._registration_method.set_fixed(image)\n                self._registration_method.run()\n                transform_sitk = self._registration_method.\\\n                    get_registration_transform_sitk()\n\n                for j in indices:\n                    slices[j].update_motion_correction(transform_sitk)\n\n                # if debug:\n                #     image_after = self._get_stack_subgroup(indices)\n                #     ph.killall_itksnap()\n                #     print(stack.get_filename())\n                #     sitkh.show_stacks(\n                #         [self._reference, image, image_after],\n                #         label=[\"reference\", \"before\", \"after\"]\n                #         # segmentation=image,\n                #     )\n\n    ##\n    # Gets the bundled stack of selected slices.\n    # \\date       2017-10-16 13:10:32+0100\n    #\n    # \\param      self     The object\n    # \\param      stack    Stack as Stack object\n    # \\param      indices  Indices of slices as list\n    #\n    # \\return     Stack object holding image of selected slices.\n    #\n    def _get_stack_subgroup(self, indices):\n\n        stack = self._stacks[0]\n\n        # For some reason simple element indexing does not work for sitk\n        # Problem: indices = [ 8 10 12 14]; but only 3 (!) slices are indexed!\n        # But this happens quite irregularly!?\n        # print all_[indices[0]:indices[-1]+1:self._interleave]\n        # print all_\n        # image_sitk = stack.sitk[\n        #     :,\n        #     :,\n        #     indices[0]:indices[-1]+self._interleave:self._interleave]\n        # image_sitk_mask = stack.sitk_mask[\n        #     :,\n        #     :,\n        #     indices[0]:indices[-1]+self._interleave:self._interleave]\n\n        # Build image from selected slices\n        nda = sitk.GetArrayFromImage(stack.sitk)\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n\n        image_sitk = sitk.GetImageFromArray(nda[indices, :, :])\n        image_sitk_mask = sitk.GetImageFromArray(nda_mask[indices, :, :])\n\n        # Update stack/slice subgroup position in space according to first\n        # slice which has undergone same motion as all remaining slices in the\n        # list\n        slice_sitk = stack.get_slice(indices[0]).sitk\n\n        direction = slice_sitk.GetDirection()\n        origin = slice_sitk.GetOrigin()\n        spacing = np.array(slice_sitk.GetSpacing())\n\n        # Update slice spacing according to selected interleave\n        if len(indices) > 1:\n            spacing[2] *= (indices[1] - indices[0])\n\n        # Update information for image and its mask\n        image_sitk.SetSpacing(spacing)\n        image_sitk.SetDirection(direction)\n        image_sitk.SetOrigin(origin)\n        image_sitk_mask.CopyInformation(image_sitk)\n\n        filename = stack.get_filename() + \"-\"\n        filename += (\"_\").join([str(j) for j in indices])\n        image = st.Stack.from_sitk_image(\n            image_sitk=image_sitk,\n            filename=filename,\n            image_sitk_mask=image_sitk_mask,\n            extract_slices=False,\n            slice_thickness=stack.get_slice_thickness(),\n        )\n\n        return image\n\n\nclass ReconstructionRegistrationPipeline(RegistrationPipeline):\n    __metaclass__ = ABCMeta\n\n    ##\n    # Store variables relevant for two-step registration-reconstruction\n    # pipeline.\n    # \\date       2017-10-16 10:30:39+0100\n    #\n    # \\param      self                   The object\n    # \\param      verbose                Verbose output, bool\n    # \\param      stacks                 List of Stack objects\n    # \\param      reference              Reference as Stack object\n    # \\param      registration_method    Registration method, e.g.\n    #                                    SimpleItkRegistration\n    # \\param      reconstruction_method  Reconstruction method, e.g. TK1\n    # \\param      alpha_range            Specify regularization parameter\n    #                                    range, i.e. list [alpha_min,\n    #                                    alpha_max]\n    #\n    def __init__(self,\n                 verbose,\n                 stacks,\n                 reference,\n                 registration_method,\n                 reconstruction_method,\n                 alphas,\n                 viewer,\n                 ):\n\n        RegistrationPipeline.__init__(\n            self,\n            verbose=verbose,\n            stacks=stacks,\n            reference=reference,\n            registration_method=registration_method,\n            viewer=viewer,\n        )\n\n        self._reconstruction_method = reconstruction_method\n        self._alphas = alphas\n\n        self._reconstructions = [st.Stack.from_stack(\n            self._reference,\n            filename=\"Iter0_\" + self._reference.get_filename())]\n        self._computational_time_reconstruction = ph.get_zero_time()\n        self._computational_time_registration = ph.get_zero_time()\n\n    def get_iterative_reconstructions(self):\n        return self._reconstructions\n\n    def get_computational_time_reconstruction(self):\n        return self._computational_time_reconstruction\n\n    def get_computational_time_registration(self):\n        return self._computational_time_registration\n\n\n##\n# Class to perform the two-step Slice-to-Volume registration and volumetric\n# reconstruction iteratively\n# \\date       2017-08-08 02:30:43+0100\n#\nclass TwoStepSliceToVolumeRegistrationReconstruction(\n        ReconstructionRegistrationPipeline):\n\n    ##\n    # Store information to perform the two-step S2V reg and recon\n    # \\date       2017-08-08 02:31:24+0100\n    #\n    # \\param      self                           The object\n    # \\param      stacks                         The stacks\n    # \\param      reference                      The reference\n    # \\param      registration_method            Registration method, e.g.\n    #                                            SimpleItkRegistration\n    # \\param      reconstruction_method          Reconstruction method, e.g.\n    #                                            TK1\n    # \\param      alphas                         List of alphas\n    #                                            array\n    # \\param      verbose                        The verbose\n    # \\param      cycles                         Number of cycles, int\n    # \\param      outlier_rejection              The outlier rejection\n    # \\param      threshold_measure              The threshold measure\n    # \\param      thresholds                     The threshold range\n    # \\param      use_hierarchical_registration  The use hierarchical registration\n    # \\param      interleave                     The interleave\n    # \\param      viewer                         The viewer\n    # \\param      sigma_sda_mask                 The sigma sda mask\n    #\n    def __init__(self,\n                 stacks,\n                 reference,\n                 registration_method,\n                 reconstruction_method,\n                 alphas,\n                 verbose=1,\n                 cycles=3,\n                 outlier_rejection=False,\n                 threshold_measure=\"NCC\",\n                 thresholds=[0.6, 0.7, 0.8],\n                 use_hierarchical_registration=False,\n                 interleave=3,\n                 viewer=VIEWER,\n                 sigma_sda_mask=1.,\n                 ):\n\n        # Last volumetric reconstruction step is performed outside\n        if len(alphas) != cycles - 1:\n            raise ValueError(\n                \"Elements in alpha list must correspond to cycles-1\")\n\n        if outlier_rejection and len(thresholds) != cycles:\n            raise ValueError(\n                \"Elements in outlier rejection threshold list must \"\n                \"correspond to the number of cycles\")\n\n        ReconstructionRegistrationPipeline.__init__(\n            self,\n            stacks=stacks,\n            reference=reference,\n            registration_method=registration_method,\n            reconstruction_method=reconstruction_method,\n            alphas=alphas,\n            viewer=viewer,\n            verbose=verbose,\n        )\n\n        self._sigma_sda_mask = sigma_sda_mask\n\n        self._cycles = cycles\n        self._outlier_rejection = outlier_rejection\n        self._threshold_measure = threshold_measure\n        self._thresholds = thresholds\n        self._use_hierarchical_registration = use_hierarchical_registration\n        self._interleave = interleave\n\n    def _run(self):\n\n        ph.print_title(\"Two-step S2V-Registration and SRR Reconstruction\")\n\n        s2vreg = SliceToVolumeRegistration(\n            stacks=self._stacks,\n            reference=self._reference,\n            registration_method=self._registration_method,\n            verbose=False,\n            interleave=self._interleave,\n        )\n\n        reference = self._reference\n\n        for cycle in range(0, self._cycles):\n\n            if cycle == 0 and self._use_hierarchical_registration:\n                hs2vreg = HieararchicalSliceSetRegistration(\n                    stacks=self._stacks,\n                    reference=reference,\n                    registration_method=self._registration_method,\n                    interleave=self._interleave,\n                    viewer=self._viewer,\n                    min_slices=1,\n                    verbose=False,\n                )\n                hs2vreg.run()\n                self._computational_time_registration += \\\n                    hs2vreg.get_computational_time()\n            else:\n                # Slice-to-volume registration step\n                s2vreg.set_reference(reference)\n                s2vreg.set_print_prefix(\"Cycle %d/%d: \" %\n                                        (cycle + 1, self._cycles))\n                s2vreg.run()\n\n            self._computational_time_registration += \\\n                s2vreg.get_computational_time()\n\n            # Reject misregistered slices\n            if self._outlier_rejection:\n                ph.print_subtitle(\"Slice Outlier Rejection (%s < %g)\" % (\n                    self._threshold_measure, self._thresholds[cycle]))\n                outlier_rejector = outre.OutlierRejector(\n                    stacks=self._stacks,\n                    reference=self._reference,\n                    threshold=self._thresholds[cycle],\n                    measure=self._threshold_measure,\n                    verbose=True,\n                )\n                outlier_rejector.run()\n                self._reconstruction_method.set_stacks(\n                    outlier_rejector.get_stacks())\n\n                if len(self._stacks) == 0:\n                    raise RuntimeError(\n                        \"All slices of all stacks were rejected \"\n                        \"as outliers. Volumetric reconstruction is aborted.\")\n\n            # SRR step\n            if cycle < self._cycles - 1:\n                # ---------------- Perform Image Reconstruction ---------------\n                ph.print_subtitle(\"Volumetric Image Reconstruction\")\n                if isinstance(\n                    self._reconstruction_method,\n                    sda.ScatteredDataApproximation\n                ):\n                    self._reconstruction_method.set_sigma(self._alphas[cycle])\n                else:\n                    self._reconstruction_method.set_alpha(self._alphas[cycle])\n                self._reconstruction_method.run()\n\n                self._computational_time_reconstruction += \\\n                    self._reconstruction_method.get_computational_time()\n\n                reference = self._reconstruction_method.get_reconstruction()\n\n                # ------------------ Perform Image Mask SDA -------------------\n                ph.print_subtitle(\"Volumetric Image Mask Reconstruction\")\n                SDA = sda.ScatteredDataApproximation(\n                    self._stacks,\n                    reference,\n                    sigma=self._sigma_sda_mask,\n                    sda_mask=True,\n                )\n                SDA.run()\n\n                # reference contains updated mask based on SDA\n                reference = SDA.get_reconstruction()\n\n                # -------------------- Store Reconstruction -------------------\n                filename = \"Iter%d_%s\" % (\n                    cycle + 1,\n                    self._reconstruction_method.get_setting_specific_filename()\n                )\n                self._reconstructions.insert(0, st.Stack.from_stack(\n                    reference, filename=filename))\n\n                if self._verbose:\n                    sitkh.show_stacks(self._reconstructions,\n                                      segmentation=self._reference,\n                                      viewer=self._viewer)\n\n\n##\n# Class to perform hierarchical slice alignment.\n#\n# Given an interleave, associated subpackages with a decreasing number of\n# slices are jointly registered to reference volume\n# \\date       2017-10-16 10:17:09+0100\n#\nclass HieararchicalSliceSetRegistration(RegistrationPipeline):\n\n    ##\n    # Store relevant variables\n    # \\date       2017-10-16 10:18:58+0100\n    #\n    # \\param      self                 The object\n    # \\param      stacks               List of stacks to be registered\n    # \\param      reference            Reference image as Stack object.\n    # \\param      registration_method  method, e.g. SimpleItkRegistration\n    # \\param      interleave           Interleave of scans, integer\n    # \\param      min_slices           The minimum slices\n    # \\param      verbose              The verbose\n    # \\param      viewer               The viewer\n    #\n    def __init__(self,\n                 stacks,\n                 reference,\n                 registration_method,\n                 interleave,\n                 min_slices=1,\n                 verbose=1,\n                 viewer=VIEWER,\n                 ):\n\n        RegistrationPipeline.__init__(\n            self,\n            stacks=stacks,\n            reference=reference,\n            registration_method=registration_method,\n            verbose=verbose,\n            viewer=VIEWER,\n        )\n        self._interleave = interleave\n        self._min_slices = min_slices\n\n    def _run(self, debug=0):\n        ph.print_title(\n            \"Hierarchical SliceSet2V-Registration\")\n\n        N_stacks = len(self._stacks)\n\n        self._registration_method.set_moving(self._reference)\n\n        for i_stack, stack in enumerate(self._stacks):\n            n_slices = stack.get_number_of_slices()\n            for i in range(self._interleave):\n                package = list(np.arange(i, n_slices, self._interleave))\n                if len(package) / 2 >= self._min_slices:\n                    indices_splits = self._recursive_split(\n                        package, [], self._min_slices)\n                else:\n                    indices_splits = [package]\n\n                prefix = \"Hierarchical S2V-Reg: \" \\\n                    \"Stack %d/%d (%s) -- Interleave %d/%d --\" % (\n                        i_stack + 1, len(self._stacks), stack.get_filename(),\n                        i + 1, self._interleave,\n                    )\n                if debug:\n                    ph.print_subtitle(\n                        \"%s %d splits: %s\" % (\n                            prefix, len(indices_splits), indices_splits),\n                    )\n\n                ss2vreg = SliceSetToVolumeRegistration(\n                    print_prefix=prefix,\n                    stack=stack,\n                    reference=self._reference,\n                    registration_method=self._registration_method,\n                    slice_set_indices=indices_splits,\n                    verbose=self._verbose,\n                )\n                ss2vreg.run()\n\n    ##\n    # Split list of arrays into halfs.\n    # \\date       2017-10-16 13:16:50+0100\n    #\n    # \\param      self           The object\n    # \\param      indices        The indices\n    # \\param      indices_split  The indices split\n    # \\param      N_min          Minimum number of elements at which no further\n    #                            split shall be performed\n    #\n    # \\return     List of arrays holding slice indices.\n    #\n    def _recursive_split(self, indices, indices_split, N_min):\n        mid = int(len(indices) / 2)\n\n        a = indices[0:mid]\n        b = indices[mid:]\n\n        indices_split.append(a)\n        indices_split.append(b)\n\n        if len(a) / 2 >= N_min:\n            self._recursive_split(a, indices_split, N_min)\n\n        if len(b) / 2 >= N_min:\n            self._recursive_split(b, indices_split, N_min)\n\n        return indices_split\n\n\n##\n# Class to perform multi-component reconstruction\n#\n# Each stack is individually reconstructed at a given reconstruction space\n# \\date       2017-08-08 02:34:40+0100\n#\nclass MultiComponentReconstruction(Pipeline):\n\n    ##\n    # Store information relevant for multi-component reconstruction\n    # \\date       2017-08-08 02:37:40+0100\n    #\n    # \\param      self                   The object\n    # \\param      stacks                 The stacks\n    # \\param      reconstruction_method  The reconstruction method\n    # \\param      suffix                 Suffix added to filenames of each\n    #                                    individual stack, string\n    # \\param      verbose                The verbose\n    #\n    def __init__(self,\n                 stacks,\n                 reconstruction_method,\n                 suffix=\"_recon\",\n                 verbose=0,\n                 viewer=VIEWER,\n                 ):\n\n        Pipeline.__init__(self, stacks=stacks, verbose=verbose, viewer=viewer)\n\n        self._reconstruction_method = reconstruction_method\n        self._reconstructions = None\n        self._suffix = suffix\n\n    def set_reconstruction_method(self, reconstruction_method):\n        self._reconstruction_method = reconstruction_method\n\n    def get_reconstruction_method(self):\n        return self._reconstruction_method\n\n    def set_suffix(self, suffix):\n        self._suffix = suffix\n\n    def get_suffix(self):\n        return self._suffix\n\n    def get_reconstructions(self):\n        return [st.Stack.from_stack(stack) for stack in self._reconstructions]\n\n    def _run(self):\n\n        ph.print_title(\"Multi-Component Reconstruction\")\n\n        self._reconstructions = [None] * len(self._stacks)\n\n        for i in range(0, len(self._stacks)):\n            ph.print_subtitle(\"Multi-Component Reconstruction -- \"\n                              \"Stack %d/%d\" % (i + 1, len(self._stacks)))\n            stack = self._stacks[i]\n            self._reconstruction_method.set_stacks([stack])\n            self._reconstruction_method.run()\n            self._reconstructions[i] = st.Stack.from_stack(\n                self._reconstruction_method.get_reconstruction())\n            self._reconstructions[i].set_filename(\n                stack.get_filename() + self._suffix)\n"
  },
  {
    "path": "niftymic/validation/__init__.py",
    "content": ""
  },
  {
    "path": "niftymic/validation/evaluate_image_similarity.py",
    "content": "##\n# \\file evaluate_image_similarity.py\n# \\brief      Evaluate similarity to a reference of one or more images\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Feb 2018\n#\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport SimpleITK as sitk\n\nimport nsol.observer as obs\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.registration.niftyreg as regniftyreg\nfrom niftymic.utilities.input_arparser import InputArgparser\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\n\ndef main():\n\n    # Set print options\n    np.set_printoptions(precision=3)\n    pd.set_option('display.width', 1000)\n\n    input_parser = InputArgparser(\n        description=\".\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_reference(required=True)\n    input_parser.add_reference_mask()\n    input_parser.add_dir_output(required=False)\n    input_parser.add_measures(\n        default=[\"PSNR\", \"RMSE\", \"MAE\", \"SSIM\", \"NCC\", \"NMI\"])\n    input_parser.add_verbose(default=0)\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    ph.print_title(\"Image similarity\")\n    data_reader = dr.MultipleImagesReader(args.filenames)\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    reference = st.Stack.from_filename(args.reference, args.reference_mask)\n\n    for stack in stacks:\n        try:\n            stack.sitk - reference.sitk\n        except RuntimeError as e:\n            raise IOError(\n                \"All provided images must be at the same image space\")\n\n    x_ref = sitk.GetArrayFromImage(reference.sitk)\n\n    if args.reference_mask is None:\n        indices = np.where(x_ref != np.inf)\n    else:\n        x_ref_mask = sitk.GetArrayFromImage(reference.sitk_mask)\n        indices = np.where(x_ref_mask > 0)\n\n    measures_dic = {\n        m: lambda x, m=m:\n        SimilarityMeasures.similarity_measures[m](\n            x[indices], x_ref[indices])\n        # SimilarityMeasures.similarity_measures[m](x, x_ref)\n        for m in args.measures\n    }\n\n    observer = obs.Observer()\n    observer.set_measures(measures_dic)\n    for stack in stacks:\n        nda = sitk.GetArrayFromImage(stack.sitk)\n        observer.add_x(nda)\n\n    if args.verbose:\n        stacks_comparison = [s for s in stacks]\n        stacks_comparison.insert(0, reference)\n        sitkh.show_stacks(\n            stacks_comparison,\n            segmentation=reference,\n        )\n\n    observer.compute_measures()\n    measures = observer.get_measures()\n\n    # Store information in array\n    error = np.zeros((len(stacks), len(measures)))\n    cols = measures\n    rows = []\n    for i_stack, stack in enumerate(stacks):\n        error[i_stack, :] = np.array([measures[m][i_stack] for m in measures])\n        rows.append(stack.get_filename())\n\n    header = \"# Ref: %s, Ref-Mask: %d, %s \\n\" % (\n        reference.get_filename(),\n        args.reference_mask is None,\n        ph.get_time_stamp(),\n    )\n    header += \"# %s\\n\" % (\"\\t\").join(measures)\n\n    path_to_file_filenames = os.path.join(\n        args.dir_output, \"filenames.txt\")\n    path_to_file_similarities = os.path.join(\n        args.dir_output, \"similarities.txt\")\n\n    # Write to files\n    ph.write_to_file(path_to_file_similarities, header)\n    ph.write_array_to_file(\n        path_to_file_similarities, error, verbose=False)\n    text = header\n    text += \"%s\\n\" %\"\\n\".join(rows)\n    ph.write_to_file(path_to_file_filenames, text)\n\n    # Print to screen\n    ph.print_subtitle(\"Computed Similarities\")\n    df = pd.DataFrame(error, rows, cols)\n    print(df)\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/evaluate_simulated_stack_similarity.py",
    "content": "##\n# \\file evaluate_simulated_stack_similarity.py\n# \\brief      Script to evaluate the similarity of simulated stack from\n#             obtained reconstruction against the original stack.\n#\n# This function takes the result of simulate_stacks_from_reconstruction.py as\n# input.\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport os\n\nimport pysitk.python_helper as ph\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\n\nimport niftymic.base.data_reader as dr\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    # Read input\n    input_parser = InputArgparser(\n        description=\"Script to evaluate the similarity of simulated stack \"\n        \"from obtained reconstruction against the original stack. \"\n        \"This function takes the result of \"\n        \"simulate_stacks_from_reconstruction.py as input.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks()\n    input_parser.add_dir_output(required=True)\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_measures(default=[\"NCC\", \"SSIM\"])\n    input_parser.add_option(\n        option_string=\"--prefix-simulated\",\n        type=str,\n        help=\"Specify the prefix of the simulated stacks to distinguish them \"\n        \"from the original data.\",\n        default=\"Simulated_\",\n    )\n    input_parser.add_option(\n        option_string=\"--dir-input-simulated\",\n        type=str,\n        help=\"Specify the directory where the simulated stacks are. \"\n        \"If not given, it is assumed that they are in the same directory \"\n        \"as the original ones.\",\n        default=None\n    )\n    input_parser.add_slice_thicknesses(default=None)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n\n    # Read original data\n    filenames_original = args.filenames\n    data_reader = dr.MultipleImagesReader(\n        file_paths=filenames_original,\n        file_paths_masks=args.filenames_masks,\n        suffix_mask=args.suffix_mask,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n    data_reader.read_data()\n    stacks_original = data_reader.get_data()\n\n    # Read data simulated from obtained reconstruction\n    if args.dir_input_simulated is None:\n        dir_input_simulated = os.path.dirname(filenames_original[0])\n    else:\n        dir_input_simulated = args.dir_input_simulated\n    filenames_simulated = [\n        os.path.join(\"%s\", \"%s%s\") %\n        (dir_input_simulated, args.prefix_simulated, os.path.basename(f))\n        for f in filenames_original\n    ]\n    data_reader = dr.MultipleImagesReader(\n        filenames_simulated, suffix_mask=args.suffix_mask)\n    data_reader.read_data()\n    stacks_simulated = data_reader.get_data()\n\n    for i in range(len(stacks_original)):\n        try:\n            stacks_original[i].sitk - stacks_simulated[i].sitk\n        except:\n            raise IOError(\"Images '%s' and '%s' do not occupy the same space!\"\n                          % (filenames_original[i], filenames_simulated[i]))\n\n    similarity_measures = {\n        m: SimilarityMeasures.similarity_measures[m] for m in args.measures\n    }\n    similarities = np.zeros(len(args.measures))\n\n    for i in range(len(stacks_original)):\n        nda_3D_original = sitk.GetArrayFromImage(stacks_original[i].sitk)\n        nda_3D_simulated = sitk.GetArrayFromImage(stacks_simulated[i].sitk)\n        nda_3D_mask = sitk.GetArrayFromImage(stacks_original[i].sitk_mask)\n\n        path_to_file = os.path.join(\n            args.dir_output, \"Similarity_%s.txt\" %\n            stacks_original[i].get_filename())\n        text = \"# Similarity: %s vs %s (%s).\" % (\n            os.path.basename(filenames_original[i]),\n            os.path.basename(filenames_simulated[i]), ph.get_time_stamp())\n        text += \"\\n#\\t\" + (\"\\t\").join(args.measures)\n        text += \"\\n\"\n        ph.write_to_file(path_to_file, text, \"w\")\n        for k in range(nda_3D_original.shape[0]):\n            x_2D_original = nda_3D_original[k, :, :]\n            x_2D_simulated = nda_3D_simulated[k, :, :]\n\n            # zero slice, i.e. rejected during motion correction\n            if np.abs(x_2D_simulated).sum() < 1e-6:\n                x_2D_simulated[:] = np.nan\n            x_2D_mask = nda_3D_mask[k, :, :]\n\n            indices = np.where(x_2D_mask > 0)\n\n            for m, measure in enumerate(args.measures):\n                if len(indices[0]) > 0:\n                    similarities[m] = similarity_measures[measure](\n                        x_2D_original[indices], x_2D_simulated[indices])\n                else:\n                    similarities[m] = np.nan\n            ph.write_array_to_file(path_to_file, similarities.reshape(1, -1))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/evaluate_slice_residual_similarity.py",
    "content": "##\n# \\file evaluate_slice_residual_similarity.py\n# \\brief      Evaluate similarity to a reference of one or more images\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Feb 2018\n#\n\nimport os\nimport numpy as np\nimport pandas as pd\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.utilities.intensity_correction as ic\nimport niftymic.registration.niftyreg as regniftyreg\nimport niftymic.validation.residual_evaluator as res_ev\nfrom niftymic.utilities.input_arparser import InputArgparser\n\n\ndef main():\n\n    time_start = ph.start_timing()\n\n    # Set print options\n    np.set_printoptions(precision=3)\n    pd.set_option('display.width', 1000)\n\n    input_parser = InputArgparser(\n        description=\".\",\n    )\n    input_parser.add_filenames()\n    input_parser.add_filenames_masks()\n    input_parser.add_dir_input_mc()\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_reference(required=True)\n    input_parser.add_reference_mask()\n    input_parser.add_dir_output(required=False)\n    input_parser.add_log_config(default=1)\n    input_parser.add_measures(\n        default=[\"PSNR\", \"MAE\", \"RMSE\", \"SSIM\", \"NCC\", \"NMI\"])\n    input_parser.add_verbose(default=0)\n    input_parser.add_target_stack(default=None)\n    input_parser.add_intensity_correction(default=1)\n    input_parser.add_slice_thicknesses(default=None)\n    input_parser.add_option(\n        option_string=\"--use-reference-mask\", type=int, default=1)\n    input_parser.add_option(\n        option_string=\"--use-slice-masks\", type=int, default=1)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        file_paths_masks=args.filenames_masks,\n        suffix_mask=args.suffix_mask,\n        dir_motion_correction=args.dir_input_mc,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n    ph.print_info(\"%d input stacks read for further processing\" % len(stacks))\n\n    # Specify target stack for intensity correction and reconstruction space\n    if args.target_stack is None:\n        target_stack_index = 0\n    else:\n        filenames = [\"%s.nii.gz\" % s.get_filename() for s in stacks]\n        filename_target_stack = os.path.basename(args.target_stack)\n        try:\n            target_stack_index = filenames.index(filename_target_stack)\n        except ValueError as e:\n            raise ValueError(\n                \"--target-stack must correspond to an image as provided by \"\n                \"--filenames\")\n\n    # ---------------------------Intensity Correction--------------------------\n    if args.intensity_correction:\n        ph.print_title(\"Intensity Correction\")\n        intensity_corrector = ic.IntensityCorrection()\n        intensity_corrector.use_individual_slice_correction(False)\n        intensity_corrector.use_stack_mask(True)\n        intensity_corrector.use_reference_mask(True)\n        intensity_corrector.use_verbose(False)\n\n        for i, stack in enumerate(stacks):\n            if i == target_stack_index:\n                ph.print_info(\"Stack %d (%s): Reference image. Skipped.\" % (\n                    i + 1, stack.get_filename()))\n                continue\n            else:\n                ph.print_info(\"Stack %d (%s): Intensity Correction ... \" % (\n                    i + 1, stack.get_filename()), newline=False)\n            intensity_corrector.set_stack(stack)\n            intensity_corrector.set_reference(\n                stacks[target_stack_index].get_resampled_stack(\n                    resampling_grid=stack.sitk,\n                    interpolator=\"NearestNeighbor\",\n                ))\n            intensity_corrector.run_linear_intensity_correction()\n            stacks[i] = intensity_corrector.get_intensity_corrected_stack()\n            print(\"done (c1 = %g) \" %\n                  intensity_corrector.get_intensity_correction_coefficients())\n\n    # ----------------------- Slice Residual Similarity -----------------------\n    reference = st.Stack.from_filename(args.reference, args.reference_mask)\n\n    ph.print_title(\"Slice Residual Similarity\")\n    residual_evaluator = res_ev.ResidualEvaluator(\n        stacks=stacks,\n        reference=reference,\n        measures=args.measures,\n        use_reference_mask=args.use_reference_mask,\n        use_slice_masks=args.use_slice_masks,\n    )\n    residual_evaluator.compute_slice_projections()\n    residual_evaluator.evaluate_slice_similarities()\n    residual_evaluator.write_slice_similarities(args.dir_output)\n\n    elapsed_time = ph.stop_timing(time_start)\n    ph.print_title(\"Summary\")\n    print(\"Computational Time for Slice Residual Evaluation: %s\" %\n          (elapsed_time))\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/export_side_by_side_simulated_vs_original_slice_comparison.py",
    "content": "##\n# \\file export_side_by_side_simulated_vs_original_slice_comparison.py\n# \\brief      Script to generate a pdf holding all side-by-side comparisons.\n#\n# This function takes the result of simulate_stacks_from_reconstruction.py as\n# input. The script relies on ImageMagick.\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport natsort\nimport os\nimport re\n\nimport pysitk.python_helper as ph\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\n\nimport niftymic.base.data_reader as dr\nfrom niftymic.utilities.input_arparser import InputArgparser\nfrom niftymic.definitions import DIR_TMP\n\n\n##\n# Export a side-by-side comparison to a (pdf) file\n# \\date       2017-11-28 23:28:12+0000\n#\n# \\param      nda_original   numpy data 3D array of original data\n# \\param      nda_projected  numpy data 3D array of projected/simulated data\n# \\param      path_to_file   path to file, string\n# \\param      resize         factor to resize images (otherwise they\n#                            might be very small depending on the FOV)\n# \\param      extension      extension of images produced for tmp results.\n#\ndef export_comparison_to_file(nda_original,\n                              nda_projected,\n                              path_to_file,\n                              resize,\n                              extension=\"png\"):\n    dir_tmp = os.path.join(DIR_TMP, \"ImageMagick\")\n    ph.clear_directory(dir_tmp, verbose=False)\n    for k in range(nda_original.shape[0]):\n        ctr = k + 1\n\n        # Export as individual image side-by-side\n        _export_image_side_by_side(\n            nda_left=nda_original[k, :, :],\n            nda_right=nda_projected[k, :, :],\n            label_left=\"original\",\n            label_right=\"projected\",\n            path_to_file=os.path.join(\n                dir_tmp, \"%03d.%s\" % (ctr, extension)),\n            ctr=ctr,\n            resize=resize,\n            extension=extension,\n        )\n\n    # Combine all side-by-side images to single pdf\n    _export_pdf_from_side_by_side_images(\n        dir_tmp, path_to_file, extension=extension)\n    ph.print_info(\"Side-by-side comparison exported to '%s'\" % path_to_file)\n\n    # Delete tmp directory\n    ph.delete_directory(dir_tmp, verbose=False)\n\n\n##\n# Export a single side-by-side comparison of two images\n# \\date       2017-11-28 23:30:23+0000\n#\ndef _export_image_side_by_side(\n        nda_left,\n        nda_right,\n        label_left,\n        label_right,\n        path_to_file,\n        ctr,\n        resize,\n        extension,\n        border=10,\n        background=\"black\",\n        fill_ctr=\"orange\",\n        fill_label=\"white\",\n        pointsize=12,\n):\n\n    dir_output = os.path.join(DIR_TMP, \"ImageMagick\", \"side-by-side\")\n    ph.clear_directory(dir_output, verbose=False)\n\n    path_to_left = os.path.join(dir_output, \"left.%s\" % extension)\n    path_to_right = os.path.join(dir_output, \"right.%s\" % extension)\n\n    nda_left = np.round(np.array(nda_left)).astype(np.uint8)\n    nda_right = np.round(np.array(nda_right)).astype(np.uint8)\n    ph.write_image(nda_left, path_to_left, verbose=False)\n    ph.write_image(nda_right, path_to_right, verbose=False)\n\n    _resize_image(path_to_left, resize=resize)\n    _resize_image(path_to_right, resize=resize)\n\n    cmd_args = []\n    cmd_args.append(\"-geometry +%d+%d\" % (border, border))\n    cmd_args.append(\"-background %s\" % background)\n    cmd_args.append(\"-pointsize %s\" % pointsize)\n    cmd_args.append(\"-fill %s\" % fill_ctr)\n    cmd_args.append(\"-gravity SouthWest -draw \\\"text 0,0 '%d'\\\"\" % ctr)\n    cmd_args.append(\"-fill %s\" % fill_label)\n    cmd_args.append(\"-label '%s' %s\" % (label_left, path_to_left))\n    cmd_args.append(\"-label '%s' %s\" % (label_right, path_to_right))\n    cmd_args.append(\"%s\" % path_to_file)\n    cmd = \"montage %s\" % (\" \").join(cmd_args)\n    ph.execute_command(cmd, verbose=False)\n\n\n##\n# Resize image\n# \\date       2017-11-28 23:31:07+0000\n#\ndef _resize_image(path_to_file, resize):\n\n    factor = resize * 100\n    cmd_args = []\n    cmd_args.append(\"%s\" % path_to_file)\n    cmd_args.append(\"-resize %dx%d%%\\\\!\" % (factor, factor))\n    cmd = \"convert %s %s\" % ((\" \").join(cmd_args), path_to_file)\n    ph.execute_command(cmd, verbose=False)\n\n\n##\n# Create single pdf from multiple side-by-side (png) images\n# \\date       2017-11-28 23:33:46+0000\n#\n# \\param      directory     Path to directory with side-by-side png images\n# \\param      path_to_file  Path to combined pdf result\n# \\param      extension     The extension\n#\ndef _export_pdf_from_side_by_side_images(directory, path_to_file, extension):\n\n    # Read all sidy-by-side (png) images in directory\n    pattern = \"[a-zA-Z0-9_]+[.]%s\" % extension\n    p = re.compile(pattern)\n    files = [os.path.join(directory, f)\n             for f in os.listdir(directory) if p.match(f)]\n\n    # Convert consecutive sequence of images into single pdf\n    files = natsort.natsorted(files, key=lambda y: y.lower())\n    cmd = \"convert %s %s\" % ((\" \").join(files), path_to_file)\n    ph.execute_command(cmd, verbose=False)\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Script to export a side-by-side comparison of originally \"\n        \"acquired and simulated/projected slice given the estimated \"\n        \"volumetric reconstruction.\"\n        \"This function takes the result of \"\n        \"simulate_stacks_from_reconstruction.py as input.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_dir_output(required=True)\n    input_parser.add_option(\n        option_string=\"--prefix-simulated\",\n        type=str,\n        help=\"Specify the prefix of the simulated stacks to distinguish them \"\n        \"from the original data.\",\n        default=\"Simulated_\",\n    )\n    input_parser.add_option(\n        option_string=\"--dir-input-simulated\",\n        type=str,\n        help=\"Specify the directory where the simulated stacks are. \"\n        \"If not given, it is assumed that they are in the same directory \"\n        \"as the original ones.\",\n        default=None\n    )\n    input_parser.add_option(\n        option_string=\"--resize\",\n        type=float,\n        help=\"Factor to resize images (otherwise they might be very small \"\n        \"depending on the FOV)\",\n        default=3)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    # --------------------------------Read Data--------------------------------\n    ph.print_title(\"Read Data\")\n\n    # Read original data\n    filenames_original = args.filenames\n    data_reader = dr.MultipleImagesReader(filenames_original)\n    data_reader.read_data()\n    stacks_original = data_reader.get_data()\n\n    # Read data simulated from obtained reconstruction\n    if args.dir_input_simulated is None:\n        dir_input_simulated = os.path.dirname(filenames_original[0])\n    else:\n        dir_input_simulated = args.dir_input_simulated\n    filenames_simulated = [\n        os.path.join(\"%s\", \"%s%s\") %\n        (dir_input_simulated, args.prefix_simulated, os.path.basename(f))\n        for f in filenames_original\n    ]\n    data_reader = dr.MultipleImagesReader(filenames_simulated)\n    data_reader.read_data()\n    stacks_simulated = data_reader.get_data()\n\n    ph.create_directory(args.dir_output)\n\n    for i in range(len(stacks_original)):\n        try:\n            stacks_original[i].sitk - stacks_simulated[i].sitk\n        except:\n            raise IOError(\"Images '%s' and '%s' do not occupy the same space!\"\n                          % (filenames_original[i], filenames_simulated[i]))\n\n    # ---------------------Create side-by-side comparisons---------------------\n    ph.print_title(\"Create side-by-side comparisons\")\n    intensity_max = 255\n    intensity_min = 0\n    for i in range(len(stacks_original)):\n        ph.print_subtitle(\"Stack %d/%d\" % (i + 1, len(stacks_original)))\n        nda_3D_original = sitk.GetArrayFromImage(stacks_original[i].sitk)\n        nda_3D_simulated = sitk.GetArrayFromImage(stacks_simulated[i].sitk)\n\n        # Scale uniformly between 0 and 255 according to the simulated stack\n        # for export to png\n        scale = np.max(nda_3D_simulated)\n        nda_3D_original = intensity_max * nda_3D_original / scale\n        nda_3D_simulated = intensity_max * nda_3D_simulated / scale\n\n        nda_3D_simulated = np.clip(\n            nda_3D_simulated, intensity_min, intensity_max)\n        nda_3D_original = np.clip(\n            nda_3D_original, intensity_min, intensity_max)\n\n        filename = stacks_original[i].get_filename()\n        path_to_file = os.path.join(args.dir_output, \"%s.pdf\" % filename)\n\n        # Export side-by-side comparison of each stack to a pdf file\n        export_comparison_to_file(\n            nda_3D_original, nda_3D_simulated, path_to_file,\n            resize=args.resize)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/image_similarity_evaluator.py",
    "content": "##\n# \\file image_similarity_evaluator.py\n# \\brief      Class to evaluate image similarity between stacks and a reference\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       February 2018\n#\n\n\n# Import libraries\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\n\nimport nsol.observer as obs\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\nimport pysitk.python_helper as ph\n\nimport niftymic.reconstruction.linear_operators as lin_op\nimport niftymic.base.exceptions as exceptions\n\n\n##\n# Class to evaluate image similarity between stacks and a reference\n# \\date       2018-02-08 16:16:08+0000\n#\nclass ImageSimilarityEvaluator(object):\n\n    ##\n    # { constructor_description }\n    # \\date       2018-02-08 14:13:19+0000\n    #\n    # \\param      self                The object\n    # \\param      stacks              List of Stack objects\n    # \\param      reference           Reference as Stack object\n    # \\param      use_reference_mask  The use reference mask\n    # \\param      measures            Similarity measures as given in\n    #                                 nsol.similarity_measures, list of strings\n    # \\param      verbose             The verbose\n    #\n    def __init__(\n            self,\n            stacks=None,\n            reference=None,\n            use_reference_mask=True,\n            measures=[\"NCC\", \"NMI\", \"PSNR\", \"SSIM\", \"RMSE\" ,\"MAE\"],\n            verbose=True,\n    ):\n        self._stacks = stacks\n        self._reference = reference\n        self._measures = measures\n        self._use_reference_mask = use_reference_mask\n        self._verbose = verbose\n\n        self._similarities = None\n\n        self._filename_filenames = \"filenames.txt\"\n        self._filename_similarities = \"similarities.txt\"\n\n    ##\n    # Sets the stacks.\n    # \\date       2018-02-08 14:13:27+0000\n    #\n    # \\param      self    The object\n    # \\param      stacks  List of Stack objects\n    #\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n\n    ##\n    # Sets the reference from which the slices shall be simulated/projected.\n    # \\date       2018-01-19 17:26:14+0000\n    #\n    # \\param      self       The object\n    # \\param      reference  The reference\n    #\n    def set_reference(self, reference):\n        self._reference = reference\n\n    ##\n    # Gets the computed similarities.\n    # \\date       2018-02-08 14:36:15+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The similarities as dictionary. E.g. { \"NCC\": np.array()}\n    #\n    def get_similarities(self):\n        return self._similarities\n\n    def get_measures(self):\n        return self._measures\n\n    ##\n    # Calculates the similarities. Outcome can be fetched using\n    # 'get_similarities'\n    # \\date       2018-02-08 16:16:41+0000\n    #\n    # \\param      self  The object\n    #\n    # \\post       self._similarities updated.\n    #\n    def compute_similarities(self):\n        for stack in self._stacks:\n            try:\n                stack.sitk - self._reference.sitk\n            except RuntimeError as e:\n                raise IOError(\n                    \"All provided images must be at the same image space\")\n\n        x_ref = sitk.GetArrayFromImage(self._reference.sitk)\n\n        x_ref_mask = np.ones_like(x_ref)\n        if self._use_reference_mask:\n            x_ref_mask *= sitk.GetArrayFromImage(self._reference.sitk_mask)\n        indices = np.where(x_ref_mask > 0)\n\n        if len(indices[0]) == 0:\n            raise RuntimeError(\n                \"Support to evaluate similarity measures is zero\")\n\n        # Define similarity measures as dic\n        measures_dic = {\n            m: lambda x, m=m:\n            SimilarityMeasures.similarity_measures[m](\n                x[indices], x_ref[indices])\n            # SimilarityMeasures.similarity_measures[m](x, x_ref)\n            for m in self._measures\n        }\n\n        # Compute similarities\n        observer = obs.Observer()\n        observer.set_measures(measures_dic)\n        for stack in self._stacks:\n            nda = sitk.GetArrayFromImage(stack.sitk)\n            observer.add_x(nda)\n        observer.compute_measures()\n        self._similarities = observer.get_measures()\n\n        # Add filenames to dictionary\n        image_names = [s.get_filename() for s in self._stacks]\n        self._similarities[\"filenames\"] = image_names\n\n    ##\n    # Writes the evaluated similarities to two files; one containing the\n    # similarity information, the other the filename information.\n    # \\date       2018-02-08 14:58:29+0000\n    #\n    # \\param      self       The object\n    # \\param      directory  The directory\n    #\n    def write_similarities(self, directory):\n\n        # Store information in array\n        similarities_nda = np.zeros((len(self._stacks), len(self._measures)))\n        filenames = []\n        for i_stack, stack in enumerate(self._stacks):\n            similarities_nda[i_stack, :] = np.array(\n                [self._similarities[m][i_stack] for m in self._measures])\n            filenames.append(stack.get_filename())\n\n        # Build header of files\n        header = \"# Ref: %s, Ref-Mask: %d, %s \\n\" % (\n            self._reference.get_filename(),\n            self._use_reference_mask,\n            ph.get_time_stamp(),\n        )\n        header += \"# %s\\n\" % (\"\\t\").join(self._measures)\n\n        # Get filename paths\n        path_to_file_filenames, path_to_file_similarities = self._get_filename_paths(\n            directory)\n\n        # Write similarities\n        ph.write_to_file(path_to_file_similarities, header)\n        ph.write_array_to_file(\n            path_to_file_similarities, similarities_nda, verbose=self._verbose)\n\n        # Write stack filenames\n        text = header\n        text += \"%s\\n\" % \"\\n\".join(filenames)\n        ph.write_to_file(path_to_file_filenames, text, verbose=self._verbose)\n\n    ##\n    # Reads similarities.\n    # \\date       2018-02-08 15:32:04+0000\n    #\n    # \\param      self       The object\n    # \\param      directory  The directory\n    #\n    def read_similarities(self, directory):\n        \n        if not ph.directory_exists(directory):\n            raise IOError(\"Directory '%s' does not exist.\" % directory)\n\n        # Get filename paths\n        path_to_file_filenames, path_to_file_similarities = self._get_filename_paths(\n            directory)\n\n        for f in [path_to_file_filenames, path_to_file_similarities]:\n            if not ph.file_exists(path_to_file_filenames):\n                raise IOError(\"File '%s' does not exist\" % f)\n\n        lines = ph.read_file_line_by_line(path_to_file_filenames)\n\n        # Get image filenames\n        image_names = [re.sub(\"\\n\", \"\", f) for f in lines[2:]]\n\n        # Get computed measures\n        measures = lines[1]\n        measures = re.sub(\"# \", \"\", measures)\n        measures = re.sub(\"\\n\", \"\", measures)\n        self._measures = measures.split(\"\\t\")\n\n        # Get computed similarities\n        similarities_nda = np.loadtxt(path_to_file_similarities, skiprows=2)\n\n        # Reconstruct similarity dictionary\n        self._similarities = {}\n        self._similarities[\"filenames\"] = image_names\n        for i_m, m in enumerate(self._measures):\n            self._similarities[m] = similarities_nda[:, i_m]\n\n    def _get_filename_paths(self, directory):\n\n        # Define filename paths\n        path_to_file_filenames = os.path.join(\n            directory, self._filename_filenames)\n        path_to_file_similarities = os.path.join(\n            directory, self._filename_similarities)\n\n        return path_to_file_filenames, path_to_file_similarities\n"
  },
  {
    "path": "niftymic/validation/motion_evaluator.py",
    "content": "##\n# \\file motion_evaluator.py\n# \\brief      Class to evaluate computed motions\n#\n# Should help to assess the registration accuracy.\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       January 2018\n#\n\n\n# Import libraries\nimport os\nimport re\nimport numpy as np\nimport pandas as pd\nimport SimpleITK as sitk\nimport matplotlib.pyplot as plt\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.exceptions as exceptions\n\n\n##\n# Class to evaluate computed motions\n# \\date       2018-01-25 22:53:37+0000\n#\nclass MotionEvaluator(object):\n\n    def __init__(self, transforms_sitk):\n        self._transforms_sitk = transforms_sitk\n        self._transform_params = None\n\n        self._scale = {\n            # convert radiant to degree for angles\n            6: np.array([180. / np.pi, 180. / np.pi, 180. / np.pi, 1, 1, 1]),\n        }\n        self._labels_long = {\n            6: [\"angle_x [deg]\",\n                \"angle_y [deg]\",\n                \"angle_z [deg]\",\n                \"t_x [mm]\",\n                \"t_y [mm]\",\n                \"t_z [mm]\"],\n        }\n        self._labels_short = {\n            6: [\"angle_x\",\n                \"angle_y\",\n                \"angle_z\",\n                \"t_x\",\n                \"t_y\",\n                \"t_z\"],\n        }\n\n    def run(self):\n\n        # # Eliminate center information\n        # if self._transforms_sitk[0].GetName() in \\\n        #         [\"Euler2DTransform\", \"Euler3DTransform\"]:\n        #     identity = eval(\"sitk.Euler%dDTransform()\"\n        # transforms_sitk = [\n        #     sitkh.get_composite_sitk_euler_transform()\n        # ]\n\n        # Create (#transforms x DOF) numpy array\n        self._transform_params = np.zeros((\n            len(self._transforms_sitk),\n            len(self._transforms_sitk[0].GetParameters())\n        ))\n\n        for j in range(self._transform_params.shape[0]):\n            self._transform_params[j, :] = \\\n                self._transforms_sitk[j].GetParameters()\n\n    def display(self, title=None, dir_output=None):\n        pd.set_option('display.width', 1000)\n        N_trafos, dof = self._transform_params.shape\n        if dof == 6:\n            params = self._get_scaled_params(self._transform_params)\n\n            # add mean value\n            params = np.concatenate(\n                (params,\n                 np.mean(params, axis=0).reshape(1, -1),\n                 np.std(params, axis=0).reshape(1, -1)\n                 ))\n\n            cols = self._labels_long[dof]\n\n        else:\n            params = self._transform_params\n            cols = [\"a%d\" % (d + 1) for d in range(0, dof)]\n\n        rows = [\"Trafo %d\" % (d + 1) for d in range(0, N_trafos)]\n        rows.append(\"Mean\")\n        rows.append(\"Std\")\n\n        df = pd.DataFrame(params, rows, cols)\n        print(df)\n\n        if dir_output is not None:\n            title = self._replace_string(title)\n            filename = \"%s.csv\" % title\n            ph.create_directory(dir_output)\n            df.to_csv(os.path.join(dir_output, filename))\n\n    ##\n    # Plot figure to show parameter distribution.\n    # Only works for 3D rigid transforms for now.\n    # \\date       2018-01-25 23:30:45+0000\n    #\n    # \\param      self  The object\n    #\n    def show(self, title=None, dir_output=None):\n        params = self._get_scaled_params(self._transform_params)\n\n        N_trafos, dof = self._transform_params.shape\n\n        fig = plt.figure(title)\n        fig.clf()\n\n        x = range(1, N_trafos+1)\n        ax = plt.subplot(2, 1, 1)\n        for i_param in range(0, 3):\n            ax.plot(\n                x, params[:, i_param],\n                marker=ph.MARKERS[i_param],\n                color=ph.COLORS_TABLEAU20[i_param*2],\n                linestyle=\":\",\n                label=self._labels_short[dof][i_param],\n                markerfacecolor=\"w\",\n            )\n        ax.set_xticks(x)\n        plt.ylabel('Rotation [deg]')\n        plt.legend(loc=\"best\")\n\n        ax = plt.subplot(2, 1, 2)\n        for i_param in range(0, 3):\n            ax.plot(\n                x, params[:, 3+i_param],\n                marker=ph.MARKERS[i_param],\n                color=ph.COLORS_TABLEAU20[i_param*2],\n                linestyle=\":\",\n                label=self._labels_short[dof][3+i_param],\n                markerfacecolor=\"w\",\n            )\n        ax.set_xticks(x)\n        plt.xlabel('Slice')\n        plt.ylabel('Translation [mm]')\n        plt.legend(loc=\"best\")\n        plt.suptitle(title)\n\n        try:\n            # Open windows (and also save them) in full screen\n            manager = plt.get_current_fig_manager()\n            manager.full_screen_toggle()\n        except:\n            pass\n\n        plt.show(block=False)\n\n        if dir_output is not None:\n            title = self._replace_string(title)\n            filename = \"%s.pdf\" % title\n            ph.save_fig(fig, dir_output, filename)\n\n    def _get_scaled_params(self, transform_params):\n        dof = self._transform_params.shape[1]\n        return self._transform_params * self._scale[dof]\n\n    def _replace_string(self, string):\n        string = re.sub(\" \", \"_\", string)\n        string = re.sub(\":\", \"\", string)\n        string = re.sub(\"/\", \"_\", string)\n        return string\n"
  },
  {
    "path": "niftymic/validation/motion_simulator.py",
    "content": "##\n# \\file MotionSimulator.py\n# \\brief      Abstract class to define interface for motion simulator\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       September 2017\n#\n\n# Import libraries\nimport os\nimport numpy as np\nimport SimpleITK as sitk\nfrom abc import ABCMeta, abstractmethod\n\nimport pysitk.python_helper as ph\n\n\nclass MotionSimulator(object):\n    __metaclass__ = ABCMeta\n\n    def __init__(self, dimension, verbose):\n        self._transforms_sitk = None\n        self._dimension = dimension\n        self._verbose = verbose\n\n    @abstractmethod\n    def simulate_motion(self):\n        pass\n\n    def get_transforms_sitk(self):\n\n        # Return copy of transforms\n        transforms_sitk = [\n            eval(\"sitk.\" + self._transforms_sitk[0].GetName() +\n                 \"(t)\") for t in self._transforms_sitk\n        ]\n        return transforms_sitk\n\n    def write_transforms_sitk(self,\n                              directory,\n                              prefix_filename=\"Euler3dTransform_\"):\n        ph.create_directory(directory)\n        for i, transform in enumerate(self._transforms_sitk):\n            path_to_file = os.path.join(\n                directory, \"%s%d.tfm\" % (prefix_filename, i))\n            sitk.WriteTransform(transform, path_to_file)\n            if self._verbose:\n                ph.print_info(\"Transform written to %s\" % path_to_file)\n\n\nclass RigidMotionSimulator(MotionSimulator):\n    __metaclass__ = ABCMeta\n\n    def __init__(self, dimension, verbose):\n        MotionSimulator.__init__(self, dimension=dimension, verbose=verbose)\n        self._transform0_sitk = eval(\"sitk.Euler%dDTransform\" % (dimension))\n\n\nclass RandomRigidMotionSimulator(RigidMotionSimulator):\n\n    def __init__(self,\n                 dimension,\n                 angle_max_deg=5,\n                 translation_max=5,\n                 verbose=False):\n        RigidMotionSimulator.__init__(\n            self, dimension=dimension, verbose=verbose)\n        self._angle_max_deg = angle_max_deg\n        self._translation_max = translation_max\n\n    def simulate_motion(self, seed=None, simulations=1):\n\n        np.random.seed(seed)\n\n        # Create random translation \\f$\\in\\f$ [\\p -translation_max, \\p\n        # translation_max]\n        translation = 2. * np.random.rand(simulations, self._dimension) * \\\n            self._translation_max - self._translation_max\n\n        # Create random rotation \\f$\\in\\f$ [\\p -angle_deg_max, \\p\n        # angle_deg_max]\n        angle_rad = \\\n            (2. * np.random.rand(simulations, self._dimension) * self._angle_max_deg -\n                self._angle_max_deg) * np.pi / 180.\n\n        # Set resulting rigid motion transform\n        self._transforms_sitk = [None] * simulations\n\n        for i in range(simulations):\n            self._transforms_sitk[i] = self._transform0_sitk()\n\n            parameters = list(angle_rad[i, :])\n            parameters.extend(translation[i, :])\n\n            self._transforms_sitk[i].SetParameters(parameters)\n"
  },
  {
    "path": "niftymic/validation/residual_evaluator.py",
    "content": "##\n# \\file residual_evaluator.py\n# \\brief      Class to evaluate computed residuals between a\n#             simulated/projected and original/acquired slices of stacks\n#\n# Should help to assess the registration accuracy.\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       January 2018\n#\n\n\n# Import libraries\nimport os\nimport re\nimport numpy as np\nimport SimpleITK as sitk\nimport matplotlib.pyplot as plt\n\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\nimport pysitk.python_helper as ph\nimport pysitk.statistics_helper as sh\n\nimport niftymic.reconstruction.linear_operators as lin_op\nimport niftymic.base.exceptions as exceptions\n\n\n##\n# Class to evaluate computed residuals between a simulated/projected and\n# original/acquired slices of stacks\n#\n# \\date       2018-01-19 17:24:35+0000\n#\nclass ResidualEvaluator(object):\n\n    ##\n    # { constructor_description }\n    # \\date       2018-01-19 17:24:46+0000\n    #\n    # \\param      self       The object\n    # \\param      stacks     List of Stack objects\n    # \\param      reference  Reference as Stack object. Used to simulate slices\n    #                        at the position of the slices in stacks\n    # \\param      use_masks  Turn on/off using masks for the residual\n    #                        evaluation\n    # \\param      measures   Similarity measures as given in\n    #                        nsol.similarity_measures, list of strings\n    #\n    def __init__(\n            self,\n            stacks=None,\n            reference=None,\n            use_slice_masks=True,\n            use_reference_mask=True,\n            measures=[\"NCC\", \"NMI\", \"PSNR\", \"SSIM\", \"RMSE\"],\n            verbose=True,\n    ):\n        self._stacks = stacks\n        self._reference = reference\n        self._measures = measures\n        self._use_slice_masks = use_slice_masks\n        self._use_reference_mask = use_reference_mask\n        self._verbose = verbose\n\n        self._slice_projections = None\n        self._similarities = None\n        self._slice_similarities = None\n        self._init_value = np.nan\n\n    @staticmethod\n    def _get_original_number_of_slices(stack):\n        slices = stack.get_slices()\n\n        # Make sure that number of slices are either given by the stack-z-dim\n        # or, in case it was cropped at some point, at least comprises as\n        # many slices (whereby original slice number counts!)\n        N_slices = np.max([\n            stack.sitk.GetSize()[-1],\n            np.max([s.get_slice_number() + 1 for s in slices])\n        ])\n\n        return N_slices\n\n    ##\n    # Sets the stacks.\n    # \\date       2018-01-19 17:26:04+0000\n    #\n    # \\param      self    The object\n    # \\param      stacks  List of Stack objects\n    #\n    def set_stacks(self, stacks):\n        self._stacks = stacks\n\n    ##\n    # Sets the reference from which the slices shall be simulated/projected.\n    # \\date       2018-01-19 17:26:14+0000\n    #\n    # \\param      self       The object\n    # \\param      reference  The reference\n    #\n    def set_reference(self, reference):\n        self._reference = reference\n\n    def get_measures(self):\n        return self._measures\n\n    ##\n    # Gets the slice similarities computed between simulated/projected and\n    # original/acquired slices.\n    # \\date       2018-01-19 17:26:44+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The slice similarities for all slices and measures as\n    #             dictionary. E.g. {\n    #             fetal_brain_1: {'NCC': 1D-array[...], 'NMI': 1D-array[..]},\n    #             ...\n    #             fetal_brain_N: {'NCC': 1D-array[...], 'NMI': 1D-array[..]}\n    #             }\n    #\n    def get_slice_similarities(self):\n        return self._slice_similarities\n\n    ##\n    # Gets the slice projections.\n    # \\date       2018-01-19 17:27:41+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The slice projections as list of lists. E.g. [\n    #             [stack1_slice1_sim, stack1_slice2_sim, ...], ...\n    #             [stackN_slice1_sim, stackN_slice2_sim, ...]\n    #             ]\n    #\n    def get_slice_projections(self):\n        return self._slice_projections\n\n    ##\n    # Calculates the slice simulations/projections from the reference given the\n    # assumed slice acquisition protocol.\n    # \\date       2018-01-19 17:29:20+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     The slice projections.\n    #\n    def compute_slice_projections(self):\n\n        linear_operators = lin_op.LinearOperators()\n        self._slice_projections = [None] * len(self._stacks)\n\n        for i_stack, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n\n            N_slices = self._get_original_number_of_slices(stack)\n\n            self._slice_projections[i_stack] = [self._init_value] * N_slices\n\n            if self._verbose:\n                ph.print_info(\n                    \"Stack %d/%d: Compute slice projections ... \" % (\n                        i_stack + 1, len(self._stacks)),\n                    newline=False)\n\n            # Compute slice projections based on assumed slice acquisition\n            # protocol\n            for slice in slices:\n                i_slice = slice.get_slice_number()\n                self._slice_projections[i_stack][i_slice] = linear_operators.A(\n                    self._reference, slice)\n\n            if self._verbose:\n                print(\"done\")\n\n    ##\n    # Evaluate slice similarities for all simulated slices of all stacks for\n    # all similarity measures.\n    # \\date       2018-01-19 17:30:37+0000\n    #\n    # \\param      self  The object\n    #\n    def evaluate_slice_similarities(self):\n        if self._slice_projections is None:\n            raise exceptions.ObjectNotCreated(\"compute_slice_projections\")\n\n        self._slice_similarities = {\n            stack.get_filename(): {} for stack in self._stacks\n        }\n\n        similarity_measures = {\n            m: SimilarityMeasures.similarity_measures[m]\n            for m in self._measures\n        }\n\n        for i_stack, stack in enumerate(self._stacks):\n            slices = stack.get_slices()\n            N_slices = self._get_original_number_of_slices(stack)\n            stack_name = stack.get_filename()\n            self._slice_similarities[stack_name] = {\n                m: np.ones(N_slices) * self._init_value for m in self._measures\n            }\n            if self._verbose:\n                ph.print_info(\n                    \"Stack %d/%d: Compute similarity measures ... \" % (\n                        i_stack + 1, len(self._stacks)),\n                    newline=False)\n\n            for slice in slices:\n                i_slice = slice.get_slice_number()\n                slice_nda = np.squeeze(sitk.GetArrayFromImage(slice.sitk))\n                slice_proj_nda = np.squeeze(sitk.GetArrayFromImage(\n                    self._slice_projections[i_stack][i_slice].sitk))\n\n                mask_nda = np.ones_like(slice_nda)\n\n                if self._use_slice_masks:\n                    mask_nda *= np.squeeze(\n                        sitk.GetArrayFromImage(slice.sitk_mask))\n                if self._use_reference_mask:\n                    mask_nda *= np.squeeze(\n                        sitk.GetArrayFromImage(\n                            self._slice_projections[i_stack][i_slice].sitk_mask))\n                indices = np.where(mask_nda > 0)\n\n                if len(indices[0]) > 0:\n                    for m in self._measures:\n                        try:\n                            self._slice_similarities[stack_name][m][i_slice] = \\\n                                similarity_measures[m](\n                                    slice_nda[indices], slice_proj_nda[indices])\n                        except ValueError as e:\n                            # Error in case only a few/to less non-zero entries\n                            # exist\n                            if m == \"SSIM\":\n                                self._slice_similarities[\n                                    stack_name][m][i_slice] = \\\n                                    SimilarityMeasures.UNDEF[m]\n                            else:\n                                raise ValueError(e.message)\n                else:\n                    for m in self._measures:\n                        self._slice_similarities[\n                            stack_name][m][i_slice] = \\\n                            SimilarityMeasures.UNDEF[m]\n            if self._verbose:\n                print(\"done\")\n\n    ##\n    # Writes the computed slice similarities for all stacks to output directory\n    # \\date       2018-01-19 17:42:27+0000\n    #\n    # \\param      self       The object\n    # \\param      directory  path to output directory, string\n    #\n    def write_slice_similarities(self, directory):\n        for i_stack, stack in enumerate(self._stacks):\n            stack_name = stack.get_filename()\n            path_to_file = os.path.join(\n                directory, \"%s.txt\" % stack_name)\n\n            # Write header info\n            header = \"# %s, %s\\n\" % (stack.get_filename(), ph.get_time_stamp())\n            header += \"# %s\\n\" % (\"\\t\").join(self._measures)\n            ph.write_to_file(path_to_file, header, verbose=self._verbose)\n\n            # Write array information\n            N_slices = self._get_original_number_of_slices(stack)\n            array = np.ones(\n                (N_slices, len(self._measures))) * self._init_value\n            for i_m, m in enumerate(self._measures):\n                array[:, i_m] = self._slice_similarities[stack_name][m]\n            ph.write_array_to_file(path_to_file, array, verbose=self._verbose)\n\n    ##\n    # Reads computed slice similarities for all files in directory.\n    # \\date       2018-01-19 17:42:54+0000\n    #\n    # \\param      self       The object\n    # \\param      directory  The directory\n    # \\param      ext        The extent\n    # \\post       self._slice_similarities updated\n    #\n    def read_slice_similarities(self, directory, ext=\"txt\"):\n\n        if not ph.directory_exists(directory):\n            raise IOError(\"Given directory '%s' does not exist\" % (\n                directory))\n\n        pattern = \"([a-zA-Z0-9_\\+\\-]+)[.]%s\" % ext\n        p = re.compile(pattern)\n\n        stack_names = [\n            p.match(f).group(1)\n            for f in os.listdir(directory) if p.match(f)\n        ]\n\n        self._slice_similarities = {\n            stack_name: {} for stack_name in stack_names\n        }\n\n        for stack_name in stack_names:\n            path_to_file = os.path.join(directory, \"%s.%s\" % (stack_name, ext))\n\n            # Read computed measures\n            self._measures = ph.read_file_line_by_line(path_to_file)[1]\n            self._measures = re.sub(\"# \", \"\", self._measures)\n            self._measures = re.sub(\"\\n\", \"\", self._measures)\n            self._measures = self._measures.split(\"\\t\")\n\n            # Read array\n            array = np.loadtxt(path_to_file, skiprows=2)\n\n            # Ensure correct shape in case only a single slice available\n            array = array.reshape(-1, len(self._measures))\n\n            if array.ndim == 1:\n                array = array.reshape(len(array), 1)\n\n            for i_m, m in enumerate(self._measures):\n                self._slice_similarities[stack_name][m] = array[:, i_m]\n\n    ##\n    # Shows the slice similarities in plots.\n    # \\date       2018-02-09 18:28:45+0000\n    #\n    # \\param      self       The object\n    # \\param      directory  The directory\n    # \\param      title      The title\n    # \\param      measures   The measures\n    # \\param      threshold  The threshold\n    #\n    def show_slice_similarities(\n            self,\n            directory=None,\n            title=None,\n            measures=[\"NCC\"],\n            threshold=0.8,\n    ):\n\n        for i_m, measure in enumerate(measures):\n            fig = plt.figure(measure)\n            fig.clf()\n            if title is not None:\n                title = \"%s: %s\" % (title, measure)\n            else:\n                title = measure\n            plt.suptitle(title)\n\n            stack_names = self._slice_similarities.keys()\n            for i_name, stack_name in enumerate(stack_names):\n                ax = plt.subplot(np.ceil(len(stack_names) / 2.), 2, i_name + 1)\n                nda = self._slice_similarities[stack_name][measure]\n\n                nda = np.nan_to_num(nda)\n                x = np.arange(nda.size)\n\n                # indices_in = np.where(nda >= threshold)\n                indices_out = np.where(nda < threshold)\n\n                plt.plot(x, nda,\n                         color=ph.COLORS_TABLEAU20[0],\n                         markerfacecolor=\"w\",\n                         marker=ph.MARKERS[0],\n                         linestyle=\":\",\n                         )\n                plt.plot(indices_out[0], nda[indices_out],\n                         color=ph.COLORS_TABLEAU20[6],\n                         # markerfacecolor=\"w\",\n                         marker=ph.MARKERS[0],\n                         linestyle=\"\",\n                         )\n                plt.plot([x[0], x[-1]], np.ones(2) * threshold,\n                         color=ph.COLORS_TABLEAU20[6],\n                         linestyle=\"-.\",\n                         )\n\n                plt.xlabel(\"Slice\")\n                # plt.ylabel(measure)\n                plt.title(stack_name)\n\n                ax.set_xticks(x)\n                # ax.set_xticklabels(x + 1)\n                ax.set_ylim([0, 1])\n\n            sh.make_figure_fullscreen()\n            plt.show(block=False)\n\n            if directory is not None:\n                filename = \"slice_similarities_%s.pdf\" % measure\n                ph.save_fig(fig, directory, filename)\n"
  },
  {
    "path": "niftymic/validation/show_evaluated_simulated_stack_similarity.py",
    "content": "##\n# \\file show_evaluated_simulated_stack_similarity.py\n# \\brief      Script to show the evaluated similarity between simulated stack\n#             from obtained reconstruction and original stack.\n#\n# This function takes the result of evaluate_simulated_stack_similarity.py as\n# input.\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport os\nimport re\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport seaborn as sns\nfrom natsort import natsorted\n\nimport pysitk.python_helper as ph\nfrom nsol.similarity_measures import SimilarityMeasures as \\\n    SimilarityMeasures\n\nfrom niftymic.utilities.input_arparser import InputArgparser\nimport niftymic.base.exceptions as exceptions\nfrom niftymic.definitions import REGEX_FILENAMES\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Script to show the evaluated similarity between \"\n        \"simulated stack from obtained reconstruction and original stack. \"\n        \"This function takes the result of \"\n        \"evaluate_simulated_stack_similarity.py as input. \"\n        \"Provide --dir-output in order to save the results.\"\n    )\n    input_parser.add_dir_input(required=True)\n    input_parser.add_dir_output(required=False)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if not ph.directory_exists(args.dir_input):\n        raise exceptions.DirectoryNotExistent(args.dir_input)\n\n    # --------------------------------Read Data--------------------------------\n    pattern = \"Similarity_(\" + REGEX_FILENAMES + \")[.]txt\"\n    p = re.compile(pattern)\n    dic_filenames = {\n        p.match(f).group(1): p.match(f).group(0)\n        for f in os.listdir(args.dir_input) if p.match(f)\n    }\n\n    dic_stacks = {}\n    for filename in dic_filenames.keys():\n        path_to_file = os.path.join(args.dir_input, dic_filenames[filename])\n\n        # Extract evaluated measures written as header in second line\n        measures = open(path_to_file).readlines()[1]\n        measures = re.sub(\"#\\t\", \"\", measures)\n        measures = re.sub(\"\\n\", \"\", measures)\n        measures = measures.split(\"\\t\")\n\n        # Extract errors\n        similarities = np.loadtxt(path_to_file, skiprows=2)\n\n        # Build dictionary holding all similarity information for stack\n        dic_stack_similarity = {\n            measures[i]: similarities[:, i] for i in range(len(measures))\n        }\n        # dic_stack_similarity[\"measures\"] = measures\n\n        # Store information of to dictionary\n        dic_stacks[filename] = dic_stack_similarity\n\n    # -----------Visualize stacks individually per similarity measure----------\n    ctr = [0]\n    N_stacks = len(dic_stacks)\n    N_measures = len(measures)\n    rows = 2 if N_measures < 6 else 3\n    filenames = natsorted(dic_stacks.keys(), key=lambda y: y.lower())\n\n    for i, filename in enumerate(filenames):\n        fig = plt.figure(ph.add_one(ctr))\n        fig.clf()\n\n        for m, measure in enumerate(measures):\n            ax = plt.subplot(rows, np.ceil(N_measures/float(rows)), m+1)\n\n            y = dic_stacks[filename][measure]\n            x = range(1, y.size+1)\n            lines = plt.plot(x, y)\n            line = lines[0]\n            line.set_linestyle(\"\")\n            line.set_marker(ph.MARKERS[0])\n            # line.set_markerfacecolor(\"w\")\n            plt.xlabel(\"Slice\")\n            plt.ylabel(measure)\n            ax.set_xticks(x)\n\n            if measure in [\"SSIM\", \"NCC\"]:\n                ax.set_ylim([0, 1])\n\n        plt.suptitle(filename)\n        try:\n            # Open windows (and also save them) in full screen\n            manager = plt.get_current_fig_manager()\n            manager.full_screen_toggle()\n        except:\n            pass\n        plt.show(block=False)\n        if args.dir_output is not None:\n            filename = \"Similarity_%s.pdf\" % filename\n            ph.save_fig(fig, args.dir_output, filename)\n\n    # -----------All in one (meaningful in case of similar scaling)----------\n    fig = plt.figure(ph.add_one(ctr))\n    fig.clf()\n    data = {}\n    for m, measure in enumerate(measures):\n        for i, filename in enumerate(filenames):\n            similarities = dic_stacks[filename][measure]\n            labels = [filename] * similarities.size\n            if m == 0:\n                if \"Stack\" not in data.keys():\n                    data[\"Stack\"] = labels\n                else:\n                    data[\"Stack\"] = np.concatenate((data[\"Stack\"], labels))\n            if measure not in data.keys():\n                data[measure] = similarities\n            else:\n                data[measure] = np.concatenate(\n                    (data[measure], similarities))\n    df_melt = pd.DataFrame(data).melt(\n        id_vars=\"Stack\",\n        var_name=\"\",\n        value_name=\" \",\n        value_vars=measures,\n    )\n    ax = plt.subplot(1, 1, 1)\n    b = sns.boxplot(\n        data=df_melt,\n        hue=\"Stack\",  # different colors for different \"Stack\"\n        x=\"\",\n        y=\" \",\n        order=measures,\n    )\n    ax.set_axisbelow(True)\n    try:\n        # Open windows (and also save them) in full screen\n        manager = plt.get_current_fig_manager()\n        manager.full_screen_toggle()\n    except:\n        pass\n    plt.show(block=False)\n    if args.dir_output is not None:\n        filename = \"Boxplot.pdf\"\n        ph.save_fig(fig, args.dir_output, filename)\n\n    # # -------------Boxplot: Plot individual similarity measures v1----------\n    # for m, measure in enumerate(measures):\n    #     fig = plt.figure(ph.add_one(ctr))\n    #     fig.clf()\n    #     data = {}\n    #     for i, filename in enumerate(filenames):\n    #         similarities = dic_stacks[filename][measure]\n    #         labels = [filename] * similarities.size\n    #         if \"Stack\" not in data.keys():\n    #             data[\"Stack\"] = labels\n    #         else:\n    #             data[\"Stack\"] = np.concatenate((data[\"Stack\"], labels))\n    #         if measure not in data.keys():\n    #             data[measure] = similarities\n    #         else:\n    #             data[measure] = np.concatenate(\n    #                 (data[measure], similarities))\n    #     df_melt = pd.DataFrame(data).melt(\n    #         id_vars=\"Stack\",\n    #         var_name=\"\",\n    #         value_name=measure,\n    #     )\n    #     ax = plt.subplot(1, 1, 1)\n    #     b = sns.boxplot(\n    #         data=df_melt,\n    #         hue=\"Stack\",  # different colors for different \"Stack\"\n    #         x=\"\",\n    #         y=measure,\n    #     )\n    #     ax.set_axisbelow(True)\n    #     plt.show(block=False)\n\n    # # -------------Boxplot: Plot individual similarity measures v2----------\n    # for m, measure in enumerate(measures):\n    #     fig = plt.figure(ph.add_one(ctr))\n    #     fig.clf()\n    #     data = {}\n    #     for i, filename in enumerate(filenames):\n    #         similarities = dic_stacks[filename][measure]\n    #         labels = [filename] * len(filenames)\n    #         if filename not in data.keys():\n    #             data[filename] = similarities\n    #         else:\n    #             data[filename] = np.concatenate(\n    #                 (data[filename], similarities))\n    #     for filename in filenames:\n    #         data[filename] = pd.Series(data[filename])\n    #     df = pd.DataFrame(data)\n    #     df_melt = df.melt(\n    #         var_name=\"\",\n    #         value_name=measure,\n    #         value_vars=filenames,\n    #     )\n    #     ax = plt.subplot(1, 1, 1)\n    #     b = sns.boxplot(\n    #         data=df_melt,\n    #         x=\"\",\n    #         y=measure,\n    #         order=filenames,\n    #     )\n    #     ax.set_axisbelow(True)\n    #     plt.show(block=False)\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/simulate_stacks_from_reconstruction.py",
    "content": "##\n# \\file simulate_stacks_from_reconstruction.py\n# \\brief      Simulate stacks from obtained reconstruction\n#\n# Example call:\n# python simulate_stacks_from_reconstruction.py \\\n# --dir-input dir-to-motion-correction \\\n# --reconstruction volumetric_reconstruction.nii.gz \\\n# --copy-data 1 \\\n# --dir-output dir-to-output\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\n# Import libraries\nimport os\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.data_writer as dw\nimport niftymic.reconstruction.linear_operators as lin_op\nfrom niftymic.utilities.input_arparser import InputArgparser\nfrom niftymic.definitions import ALLOWED_INTERPOLATORS\n\nINTERPOLATOR_TYPES = \"%s, or %s\" % (\n    (\", \").join(ALLOWED_INTERPOLATORS[0:-1]), ALLOWED_INTERPOLATORS[-1])\n\n\ndef main():\n\n    input_parser = InputArgparser(\n        description=\"Simulate stacks from obtained reconstruction. \"\n        \"Script simulates/projects the slices at estimated positions \"\n        \"within reconstructed volume. Ideally, if motion correction was \"\n        \"correct, the resulting stack of such obtained projected slices, \"\n        \"corresponds to the originally acquired (motion corrupted) data.\",\n    )\n    input_parser.add_filenames(required=True)\n    input_parser.add_filenames_masks()\n    input_parser.add_dir_input_mc(required=True)\n    input_parser.add_reconstruction(required=True)\n    input_parser.add_dir_output(required=True)\n    input_parser.add_suffix_mask(default=\"_mask\")\n    input_parser.add_prefix_output(default=\"Simulated_\")\n    input_parser.add_option(\n        option_string=\"--copy-data\",\n        type=int,\n        help=\"Turn on/off copying of original data (including masks) to \"\n        \"output folder.\",\n        default=0)\n    input_parser.add_option(\n        option_string=\"--reconstruction-mask\",\n        type=str,\n        help=\"If given, reconstruction image mask is propagated to \"\n        \"simulated stack(s) of slices as well\",\n        default=None)\n    input_parser.add_interpolator(\n        option_string=\"--interpolator-mask\",\n        help=\"Choose the interpolator type to propagate the reconstruction \"\n        \"mask (%s).\" % (INTERPOLATOR_TYPES),\n        default=\"NearestNeighbor\")\n    input_parser.add_log_config(default=0)\n    input_parser.add_verbose(default=0)\n    input_parser.add_slice_thicknesses(default=None)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    if args.interpolator_mask not in ALLOWED_INTERPOLATORS:\n        raise IOError(\n            \"Unknown interpolator provided. Possible choices are %s\" % (\n                INTERPOLATOR_TYPES))\n\n    if args.log_config:\n        input_parser.log_config(os.path.abspath(__file__))\n\n    # Read motion corrected data\n    data_reader = dr.MultipleImagesReader(\n        file_paths=args.filenames,\n        file_paths_masks=args.filenames_masks,\n        suffix_mask=args.suffix_mask,\n        dir_motion_correction=args.dir_input_mc,\n        stacks_slice_thicknesses=args.slice_thicknesses,\n    )\n    data_reader.read_data()\n    stacks = data_reader.get_data()\n\n    reconstruction = st.Stack.from_filename(\n        args.reconstruction, args.reconstruction_mask, extract_slices=False)\n\n    linear_operators = lin_op.LinearOperators()\n\n    for i, stack in enumerate(stacks):\n\n        # initialize image data array(s)\n        nda = np.zeros_like(sitk.GetArrayFromImage(stack.sitk))\n        nda[:] = np.nan\n\n        if args.reconstruction_mask:\n            nda_mask = np.zeros_like(sitk.GetArrayFromImage(stack.sitk_mask))\n\n        slices = stack.get_slices()\n        kept_indices = [s.get_slice_number() for s in slices]\n\n        # Fill stack information \"as if slice was acquired consecutively\"\n        # Therefore, simulated stack slices correspond to acquired slices\n        # (in case motion correction was correct)\n        for j in range(nda.shape[0]):\n            if j in kept_indices:\n                index = kept_indices.index(j)\n                simulated_slice = linear_operators.A(\n                    reconstruction,\n                    slices[index],\n                    interpolator_mask=args.interpolator_mask\n                )\n                nda[j, :, :] = sitk.GetArrayFromImage(simulated_slice.sitk)\n\n                if args.reconstruction_mask:\n                    nda_mask[j, :, :] = sitk.GetArrayFromImage(\n                        simulated_slice.sitk_mask)\n\n        # Create nifti image with same image header as original stack\n        simulated_stack_sitk = sitk.GetImageFromArray(nda)\n        simulated_stack_sitk.CopyInformation(stack.sitk)\n\n        if args.reconstruction_mask:\n            simulated_stack_sitk_mask = sitk.GetImageFromArray(nda_mask)\n            simulated_stack_sitk_mask.CopyInformation(stack.sitk_mask)\n        else:\n            simulated_stack_sitk_mask = None\n\n        simulated_stack = st.Stack.from_sitk_image(\n            image_sitk=simulated_stack_sitk,\n            image_sitk_mask=simulated_stack_sitk_mask,\n            filename=args.prefix_output + stack.get_filename(),\n            extract_slices=False,\n            slice_thickness=stack.get_slice_thickness(),\n        )\n\n        if args.verbose:\n            sitkh.show_stacks([\n                stack, simulated_stack],\n                segmentation=stack)\n\n        simulated_stack.write(\n            args.dir_output,\n            write_mask=False,\n            write_slices=False,\n            suffix_mask=args.suffix_mask)\n\n        if args.copy_data:\n            stack.write(\n                args.dir_output,\n                write_mask=True,\n                write_slices=False,\n                suffix_mask=\"_mask\")\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic/validation/slice_acquisition.py",
    "content": "##\n# \\file slice_acquisition.py\n# \\brief      Based on a given volume, this class aims to simulate the slice\n#             acquisition.\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       May 2016\n#\n\n\n# Import libraries\nimport itk\nimport SimpleITK as sitk\nimport numpy as np\nimport os\nimport sys\nfrom abc import ABCMeta, abstractmethod\n\n# Import modules from src-folder\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.psf as psf\n\n\n# Class simulating the slice acquisition\nclass SliceAcqusition(object):\n    __metaclass__ = ABCMeta\n\n    def __init__(self,\n                 reference,\n                 interpolator,\n                 alpha_cut,\n                 pixel_type=itk.D):\n        self._reference = reference\n        self._interpolator = interpolator\n        self._alpha_cut = alpha_cut\n        self._pixel_type = pixel_type\n        self._image_type = itk.Image[pixel_type, reference.sitk.GetDimension()]\n\n        self._output = None\n\n    def run(self):\n        self._run()\n\n    @abstractmethod\n    def _run(self):\n        pass\n\n    def _get_interpolator(self, stack_slice):\n        if self._interpolator == \"OrientedGaussian\":\n            # Get oriented PSF covariance matrix\n            cov = psf.PSF().get_covariance_matrix_in_reconstruction_space(\n                stack_slice, self._reference)\n\n            # Specify oriented Gaussian interpolator\n            interpolator_itk = itk.OrientedGaussianInterpolateImageFunction[\n                self._image_type, self._pixel_type].New()\n            interpolator_itk.SetCovariance(cov.flatten())\n            interpolator_itk.SetAlpha(self._alpha_cut)\n\n        else:\n            interpolator_itk = eval(\n                \"itk.%sInterpolateImageFunction\"\n                \"[self._image_type, self._pixel_type].New()\" %\n                self._interpolator)\n\n        return interpolator_itk\n\n    def get_output(self):\n        return st.Stack.from_stack(self._output)\n\n\nclass StaticSliceAcquisition(SliceAcqusition):\n\n    def __init__(self,\n                 stack_slice,\n                 reference,\n                 interpolator=\"Linear\",\n                 alpha_cut=3):\n\n        SliceAcqusition.__init__(self,\n                                 reference=reference,\n                                 interpolator=interpolator,\n                                 alpha_cut=alpha_cut,\n                                 )\n        self._stack_slice = stack_slice\n\n    def set_stack_slice(self, stack_slice):\n        self._stack_slice = stack_slice\n\n    def _run(self):\n        resampler_itk = itk.ResampleImageFilter[\n            self._image_type, self._image_type].New()\n        resampler_itk.SetOutputParametersFromImage(self._stack_slice.itk)\n        resampler_itk.SetInterpolator(\n            self._get_interpolator(self._stack_slice))\n        resampler_itk.SetInput(self._reference.itk)\n        resampler_itk.UpdateLargestPossibleRegion()\n        resampler_itk.Update()\n\n        output_itk = resampler_itk.GetOutput()\n        output_itk.DisconnectPipeline()\n        output_sitk = sitkh.get_sitk_from_itk_image(output_itk)\n\n        self._output = st.Stack.from_sitk_image(\n            image_sitk=output_sitk,\n            image_sitk_mask=self._stack_slice.sitk_mask,\n            filename=self._stack_slice.get_filename()\n        )\n"
  },
  {
    "path": "niftymic/validation/slice_coverage.py",
    "content": "##\n# \\file slice_coverage.py\n# \\brief      Class to visualize slice coverage over reconstruction space\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       Feb 2019\n#\n\nimport numpy as np\nimport SimpleITK as sitk\n\n\n##\n# Class to visualize slice coverage over reconstruction space\n# \\date       2019-02-23 21:17:49+0000\n#\nclass SliceCoverage(object):\n\n    def __init__(self, stacks, reconstruction_sitk):\n        self._stacks = stacks\n        self._reconstruction_sitk = reconstruction_sitk\n\n        self._coverage_sitk = None\n\n    ##\n    # Gets the slice coverage as Image. The (integer) intensity values reflect\n    # the number of slices that have contributed to this particular voxel.\n    # \\date       2019-02-23 21:18:10+0000\n    #\n    # \\param      self  The object\n    #\n    # \\return     Slice coverage as sitk.Image uint8 image.\n    #\n    def get_coverage_sitk(self):\n        if self._coverage_sitk is None:\n            raise RuntimeError(\"Execute 'run' first\")\n        return sitk.Image(self._coverage_sitk)\n\n    ##\n    # Compute slice coverage\n    # \\date       2019-02-23 21:19:55+0000\n    #\n    # \\param      self  The object\n    #\n    def run(self):\n\n        # create zero image\n        coverage_sitk = sitk.Image(self._reconstruction_sitk) * 0\n\n        for i, stack in enumerate(self._stacks):\n            print(\"Slices of stack %d/%d ... \" % (i + 1, len(self._stacks)))\n\n            # Add each individual slice contribution\n            for slice in stack.get_slices():\n                coverage_sitk = self._add_slice_contribution(\n                    slice, coverage_sitk)\n\n        # Cast to unsigned integer\n        self._coverage_sitk = sitk.Cast(coverage_sitk, sitk.sitkUInt8)\n\n    ##\n    # Adds a slice contribution.\n    # \\date       2019-02-23 21:27:12+0000\n    #\n    # \\param      slice          Slice as sl.Slice object\n    # \\param      coverage_sitk  sitk.Image reflecting the current iteration of\n    #                            slice coverage\n    #\n    # \\return     Updated slice contribution, sitk.Image\n    #\n    @staticmethod\n    def _add_slice_contribution(slice, coverage_sitk):\n\n        #\n        slice_sitk = sitk.Image(slice.sitk)\n        spacing = np.array(slice_sitk.GetSpacing())\n        spacing[-1] = slice.get_slice_thickness()\n        slice_sitk.SetSpacing(spacing)\n\n        contrib_nda = sitk.GetArrayFromImage(slice_sitk)\n        contrib_nda[:] = 1\n        contrib_sitk = sitk.GetImageFromArray(contrib_nda)\n        contrib_sitk.CopyInformation(slice_sitk)\n\n        coverage_sitk += sitk.Resample(\n            contrib_sitk,\n            coverage_sitk,\n            sitk.Euler3DTransform(),\n            sitk.sitkNearestNeighbor,\n            0,\n            coverage_sitk.GetPixelIDValue(),\n        )\n\n        return coverage_sitk\n"
  },
  {
    "path": "niftymic/validation/write_random_motion_transforms.py",
    "content": "##\n# \\file write_random_motion_transforms.py\n# \\brief      Create and write random motion transforms\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       November 2017\n#\n\nimport numpy as np\n\nfrom niftymic.utilities.input_arparser import InputArgparser\nimport niftymic.validation.motion_simulator as ms\n\n\ndef main():\n    input_parser = InputArgparser(\n        description=\"Create and write random rigid motion transformations. \"\n        \"Simulated transformations are exported as (Simple)ITK transforms. \",\n    )\n    input_parser.add_dir_output(required=True)\n    input_parser.add_option(\n        option_string=\"--simulations\",\n        type=int,\n        required=True,\n        help=\"Number of simulated motion transformations.\"\n    )\n    input_parser.add_option(\n        option_string=\"--angle-max\",\n        default=10,\n        help=\"random angles (in degree) are drawn such \"\n        \"that |angle| <= angle_max.\"\n    )\n    input_parser.add_option(\n        option_string=\"--translation-max\",\n        default=10,\n        help=\"random translations (in millimetre) are drawn such \"\n        \"that |translation| <= translation_max.\"\n    )\n    input_parser.add_option(\n        option_string=\"--seed\",\n        type=int,\n        default=1,\n        help=\"Seed for pseudo-random data generation\"\n    )\n    input_parser.add_option(\n        option_string=\"--dimension\",\n        type=int,\n        default=3,\n        help=\"Spatial dimension for transformations.\"\n    )\n    input_parser.add_prefix_output(default=\"EulerTransform_slice\")\n    input_parser.add_verbose(default=1)\n\n    args = input_parser.parse_args()\n    input_parser.print_arguments(args)\n\n    motion_simulator = ms.RandomRigidMotionSimulator(\n        dimension=args.dimension,\n        angle_max_deg=args.angle_max,\n        translation_max=args.translation_max,\n        verbose=args.verbose)\n    motion_simulator.simulate_motion(\n        seed=args.seed,\n        simulations=args.simulations,\n    )\n\n    motion_simulator.write_transforms_sitk(\n        directory=args.dir_output,\n        prefix_filename=args.prefix_output,\n    )\n\n    return 0\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "niftymic_correct_bias_field.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.correct_bias_field import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_multiply.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.multiply import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_nifti2dicom.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.nifti2dicom import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_reconstruct_volume.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.reconstruct_volume import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_reconstruct_volume_from_slices.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.reconstruct_volume_from_slices import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_register_image.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.register_image import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_rsfmri_estimate_motion.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.rsfmri_estimate_motion import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_rsfmri_reconstruct_volume_from_slices.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.rsfmri_reconstruct_volume_from_slices import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_run_reconstruction_parameter_study.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.run_reconstruction_parameter_study import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_run_reconstruction_pipeline.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.multiply_stack_with_mask import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_segment_fetal_brains.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom niftymic.application.segment_fetal_brains import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "niftymic_show_reconstruction_parameter_study.py",
    "content": "# -*- coding: utf-8 -*-\nimport sys\n\nfrom nsol.application.show_parameter_study import main\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "requirements-monaifbs.txt",
    "content": "torch>=1.4.0\ntorch-summary>=1.3.2\nmonai==0.3.0\npyyaml>=5.3.1\npytorch-ignite>=0.3.0\ntensorboard>=2.2.1\n"
  },
  {
    "path": "requirements.txt",
    "content": "matplotlib>=2.2.2\nnatsort>=5.3.0\nnibabel>=2.4.1\nnipype>=1.0.3\nnose>=1.3.7\nnsol>=0.1.14\nnumpy>=1.14.2,!=1.16.0\npandas>=0.22.0\npydicom>=1.2.0\npysitk>=0.2.19\nscikit_image>=0.14.1\nscipy>=1.0.1\nseaborn>=0.8.1\nSimpleITK>=1.2.0\nsimplereg>=0.3.2\nsix>=1.11.0\n"
  },
  {
    "path": "setup.py",
    "content": "##\n# \\file setup.py\n#\n# Instructions:\n# 1) Set environment variables with prefix NIFTYMIC_, e.g.\n#   `export NIFTYMIC_ITK_DIR=path-to-ITK_NIFTYMIC-build`\n#   to incorporate `-D ITK_DIR=path-to-ITK_NIFTYMIC-build` in `cmake` build.\n# 2) `pip install -e .`\n#   All python packages and command line tools are then installed during\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       July 2017\n#\n\n\nimport re\nimport os\nimport sys\nfrom setuptools import setup, find_packages\n\n\nabout = {}\nwith open(os.path.join(\"niftymic\", \"__about__.py\")) as fp:\n    exec(fp.read(), about)\n\n\nwith open(\"README.md\", \"r\") as fh:\n    long_description = fh.read()\n\n\ndef install_requires(fname=\"requirements.txt\"):\n    with open(fname) as f:\n        content = f.readlines()\n    content = [x.strip() for x in content]\n    return content\n\n\nsetup(name='NiftyMIC',\n      version=about[\"__version__\"],\n      description=about[\"__summary__\"],\n      long_description=long_description,\n      long_description_content_type=\"text/markdown\",\n      url='https://github.com/gift-surg/NiftyMIC',\n      author=about[\"__author__\"],\n      author_email=about[\"__email__\"],\n      license=about[\"__license__\"],\n      packages=find_packages(),\n      install_requires=install_requires(),\n      # data_files=[(d, [os.path.join(d, f) for f in files])\n      #             for d, folders, files\n      #             in os.walk(os.path.join(\"data\", \"demo\"))],\n      zip_safe=False,\n      keywords='development numericalsolver convexoptimisation',\n      classifiers=[\n          'Intended Audience :: Developers',\n          'Intended Audience :: Healthcare Industry',\n          'Intended Audience :: Science/Research',\n\n          'License :: OSI Approved :: BSD License',\n\n          'Topic :: Software Development :: Build Tools',\n          'Topic :: Scientific/Engineering :: Medical Science Apps.',\n\n          'Programming Language :: Python',\n          'Programming Language :: Python :: 2',\n          'Programming Language :: Python :: 3',\n      ],\n      entry_points={\n          'console_scripts': [\n              'niftymic_correct_bias_field = niftymic.application.correct_bias_field:main',\n              'niftymic_reconstruct_volume = niftymic.application.reconstruct_volume:main',\n              'niftymic_reconstruct_volume_from_slices = niftymic.application.reconstruct_volume_from_slices:main',\n              'niftymic_register_image = niftymic.application.register_image:main',\n              'niftymic_multiply = niftymic.application.multiply:main',\n              'niftymic_run_reconstruction_parameter_study = niftymic.application.run_reconstruction_parameter_study:main',\n              'niftymic_run_reconstruction_pipeline = niftymic.application.run_reconstruction_pipeline:main',\n              'niftymic_nifti2dicom = niftymic.application.nifti2dicom:main',\n              'niftymic_show_reconstruction_parameter_study = nsol.application.show_parameter_study:main',\n              'niftymic_segment_fetal_brains = niftymic.application.segment_fetal_brains:main',\n\n              # rs-fMRI \n              'niftymic_rsfmri_estimate_motion = niftymic.application.rsfmri_estimate_motion:main',\n              'niftymic_rsfmri_reconstruct_volume_from_slices = niftymic.application.rsfmri_reconstruct_volume_from_slices:main',\n          ],\n      },\n      )\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/brain_stripping_test.py",
    "content": "# \\file TestBrainStripping.py\n#  \\brief  Class containing unit tests for module BrainStripping\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date December 2015\n\n\nimport os\nimport unittest\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.utilities.brain_stripping as bs\nfrom niftymic.definitions import DIR_TEST\n\n\nclass BrainStrippingTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 7\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_data = os.path.join(\n            DIR_TEST, \"case-studies\", \"fetal-brain\", \"input-data\")\n        self.filename = \"axial\"\n\n    def test_01_input_output(self):\n\n        brain_stripping = bs.BrainStripping.from_filename(\n            self.dir_data, self.filename)\n        brain_stripping.compute_brain_image(0)\n        brain_stripping.compute_brain_mask(0)\n        brain_stripping.compute_skull_image(0)\n        brain_stripping.run()\n\n        with self.assertRaises(ValueError) as ve:\n            brain_stripping.get_brain_image_sitk()\n        self.assertEqual(\n            \"Brain was not asked for. Do not set option '-n' and run again.\",\n            str(ve.exception))\n\n        with self.assertRaises(ValueError) as ve:\n            brain_stripping.get_brain_mask_sitk()\n        self.assertEqual(\n            \"Brain mask was not asked for. Set option '-m' and run again.\",\n            str(ve.exception))\n\n        with self.assertRaises(ValueError) as ve:\n            brain_stripping.get_skull_mask_sitk()\n        self.assertEqual(\n            \"Skull mask was not asked for. Set option '-s' and run again.\",\n            str(ve.exception))\n\n    def test_02_brain_mask(self):\n        path_to_reference = os.path.join(\n            DIR_TEST, \"case-studies\", \"fetal-brain\", \"brain_stripping\", \"axial_seg.nii.gz\")\n\n        brain_stripping = bs.BrainStripping.from_filename(\n            self.dir_data, self.filename)\n        brain_stripping.compute_brain_image(0)\n        brain_stripping.compute_brain_mask(1)\n        brain_stripping.compute_skull_image(0)\n        # brain_stripping.set_bet_options(\"-f 0.3\")\n\n        brain_stripping.run()\n        original_sitk = brain_stripping.get_input_image_sitk()\n        res_sitk = brain_stripping.get_brain_mask_sitk()\n\n        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference)\n\n        diff_sitk = res_sitk - ref_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n"
  },
  {
    "path": "tests/case_study_fetal_brain_test.py",
    "content": "##\n# \\file case_study_fetal_brain_test.py\n#  \\brief  Unit tests based on fetal brain case study\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date November 2017\n\n\nimport os\nimport unittest\nimport numpy as np\nimport re\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nfrom niftymic.definitions import DIR_TMP, DIR_TEST, REGEX_FILENAMES, DIR_TEMPLATES\n\n\nclass CaseStudyFetalBrainTest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_data = os.path.join(DIR_TEST, \"case-studies\", \"fetal-brain\")\n        self.filenames = [\n            os.path.join(self.dir_data,\n                         \"input-data\",\n                         \"%s.nii.gz\" % f)\n            for f in [\"axial\", \"coronal\", \"sagittal\"]]\n        self.dir_output = os.path.join(DIR_TMP, \"case-studies\", \"fetal-brain\")\n        self.suffix_mask = \"_mask\"\n\n    def test_reconstruct_volume(self):\n        filename = \"SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz\"\n        output = os.path.join(self.dir_output, filename)\n        dir_reference = os.path.join(self.dir_data, \"reconstruct_volume\")\n        dir_reference_mc = os.path.join(dir_reference, \"motion_correction\")\n        path_to_reference = os.path.join(dir_reference, filename)\n        path_to_reference_mask = ph.append_to_filename(\n            os.path.join(dir_reference, filename), self.suffix_mask)\n\n        two_step_cycles = 1\n        iter_max = 5\n        threshold = 0.75\n        alpha = 0.02\n        alpha_first = 0.2\n        sigma = 1\n        intensity_correction = 1\n        isotropic_resolution = 1.02\n        v2v_method = \"RegAladin\"\n\n        cmd_args = []\n        cmd_args.append(\"--filenames %s\" % \" \".join(self.filenames))\n        cmd_args.append(\"--output %s\" % output)\n        cmd_args.append(\"--suffix-mask %s\" % self.suffix_mask)\n        cmd_args.append(\"--two-step-cycles %s\" % two_step_cycles)\n        cmd_args.append(\"--iter-max %d\" % iter_max)\n        cmd_args.append(\"--threshold-first %f\" % threshold)\n        cmd_args.append(\"--sigma %f\" % sigma)\n        cmd_args.append(\"--threshold %f\" % threshold)\n        cmd_args.append(\"--intensity-correction %d\" % intensity_correction)\n        cmd_args.append(\"--isotropic-resolution %s\" % isotropic_resolution)\n        cmd_args.append(\"--alpha %f\" % alpha)\n        cmd_args.append(\"--alpha-first %f\" % alpha_first)\n        cmd_args.append(\"--v2v-method %s\" % v2v_method)\n        # cmd_args.append(\"--verbose 1\")\n\n        cmd = \"niftymic_reconstruct_volume %s\" % (\n            \" \").join(cmd_args)\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        # Check SRR volume\n        res_sitk = sitkh.read_nifti_image_sitk(output)\n        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference)\n        diff_sitk = res_sitk - ref_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n        # Check SRR mask volume\n        res_sitk = sitkh.read_nifti_image_sitk(\n            ph.append_to_filename(output, \"_mask\"))\n        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference_mask)\n        diff_sitk = res_sitk - ref_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n        # Check transforms\n        pattern = REGEX_FILENAMES + \"[.]tfm\"\n        p = re.compile(pattern)\n        dir_res_mc = os.path.join(self.dir_output, \"motion_correction\")\n        trafos_res = sorted(\n            [os.path.join(dir_res_mc, t)\n             for t in os.listdir(dir_res_mc) if p.match(t)])\n        trafos_ref = sorted(\n            [os.path.join(dir_reference_mc, t)\n             for t in os.listdir(dir_reference_mc) if p.match(t)])\n        self.assertEqual(len(trafos_res), len(trafos_ref))\n        for i in range(len(trafos_ref)):\n            nda_res = sitkh.read_transform_sitk(trafos_res[i]).GetParameters()\n            nda_ref = sitkh.read_transform_sitk(trafos_ref[i]).GetParameters()\n            nda_diff = np.linalg.norm(np.array(nda_res) - nda_ref)\n            self.assertAlmostEqual(nda_diff, 0, places=self.precision)\n\n    def test_reconstruct_volume_from_slices(self):\n        filename = \"SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz\"\n        output = os.path.join(self.dir_output, filename)\n        dir_reference = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\")\n        dir_input_mc = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\", \"motion_correction\")\n        path_to_reference = os.path.join(dir_reference, filename)\n\n        iter_max = 5\n        alpha = 0.02\n        intensity_correction = 1\n\n        cmd_args = []\n        cmd_args.append(\"--filenames %s\" % \" \".join(self.filenames))\n        cmd_args.append(\"--dir-input-mc %s\" % dir_input_mc)\n        cmd_args.append(\"--output %s\" % output)\n        cmd_args.append(\"--iter-max %d\" % iter_max)\n        cmd_args.append(\"--intensity-correction %d\" % intensity_correction)\n        cmd_args.append(\"--alpha %f\" % alpha)\n        cmd_args.append(\"--reconstruction-space %s\" % path_to_reference)\n\n        cmd = \"niftymic_reconstruct_volume_from_slices %s\" % (\n            \" \").join(cmd_args)\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        # Check whether identical reconstruction has been created\n        reconstruction_sitk = sitkh.read_nifti_image_sitk(output)\n        reference_sitk = sitkh.read_nifti_image_sitk(path_to_reference)\n\n        difference_sitk = reconstruction_sitk - reference_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(difference_sitk))\n\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n    def test_register_image(self):\n        filename = \"registration_transform_sitk.txt\"\n        gestational_age = 28\n\n        path_to_recon = os.path.join(\n            self.dir_data, \"register_image\",\n            \"SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz\")\n        dir_input_mc = os.path.join(\n            self.dir_data, \"register_image\", \"motion_correction\")\n\n        path_to_transform_res = os.path.join(self.dir_output, filename)\n        path_to_transform_ref = os.path.join(\n            self.dir_data, \"register_image\", filename)\n        dir_ref_mc = os.path.join(\n            self.dir_data, \"register_image\", \"motion_correction_ref\")\n        path_to_rejected_slices_ref = os.path.join(\n            dir_ref_mc, \"rejected_slices.json\")\n\n        template = os.path.join(\n            DIR_TEMPLATES,\n            \"STA%d.nii.gz\" % gestational_age)\n        template_mask = os.path.join(\n            DIR_TEMPLATES,\n            \"STA%d_mask.nii.gz\" % gestational_age)\n\n        cmd_args = [\"niftymic_register_image\"]\n        cmd_args.append(\"--fixed %s\" % template)\n        cmd_args.append(\"--moving %s\" % path_to_recon)\n        cmd_args.append(\"--fixed-mask %s\" % template_mask)\n        cmd_args.append(\"--moving-mask %s\" %\n                        ph.append_to_filename(path_to_recon, self.suffix_mask))\n        cmd_args.append(\"--dir-input-mc %s\" % dir_input_mc)\n        cmd_args.append(\"--output %s\" % path_to_transform_res)\n        cmd_args.append(\"--init-pca\")\n        # cmd_args.append(\"--verbose 1\")\n        self.assertEqual(ph.execute_command(\" \".join(cmd_args)), 0)\n\n        # Check registration transform\n        res_sitk = sitkh.read_transform_sitk(path_to_transform_res)\n        ref_sitk = sitkh.read_transform_sitk(path_to_transform_ref)\n\n        res_nda = res_sitk.GetParameters()\n        ref_nda = ref_sitk.GetParameters()\n        diff_nda = np.array(res_nda) - ref_nda\n\n        self.assertAlmostEqual(\n            np.linalg.norm(diff_nda), 0, places=self.precision)\n\n        # Check individual slice transforms\n        pattern = REGEX_FILENAMES + \"[.]tfm\"\n        p = re.compile(pattern)\n        dir_res_mc = os.path.join(self.dir_output, \"motion_correction\")\n        trafos_res = sorted(\n            [os.path.join(dir_res_mc, t)\n             for t in os.listdir(dir_res_mc) if p.match(t)])\n        trafos_ref = sorted(\n            [os.path.join(dir_ref_mc, t)\n             for t in os.listdir(dir_res_mc) if p.match(t)])\n        self.assertEqual(len(trafos_res), len(trafos_ref))\n        for i in range(len(trafos_ref)):\n            nda_res = sitkh.read_transform_sitk(trafos_res[i]).GetParameters()\n            nda_ref = sitkh.read_transform_sitk(trafos_ref[i]).GetParameters()\n            nda_diff = np.linalg.norm(np.array(nda_res) - nda_ref)\n            self.assertAlmostEqual(nda_diff, 0, places=self.precision)\n\n        # Check rejected_slices.json\n        path_to_rejected_slices_res = os.path.join(\n            dir_res_mc, \"rejected_slices.json\")\n        self.assertEqual(\n            ph.file_exists(path_to_rejected_slices_res), True)\n        rejected_slices_res = ph.read_dictionary_from_json(\n            path_to_rejected_slices_res)\n        rejected_slices_ref = ph.read_dictionary_from_json(\n            path_to_rejected_slices_ref)\n        self.assertEqual(rejected_slices_res == rejected_slices_ref, True)\n"
  },
  {
    "path": "tests/case_study_rsfmri_test.py",
    "content": "##\n# \\file case_study_fetal_brain_test.py\n#  \\brief  Unit tests based on fetal brain case study\n#\n#  \\author Michael Ebner (michael.ebner@kcl.ac.uk)\n#  \\date July 2019\n\n\nimport re\nimport os\nimport unittest\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nfrom niftymic.definitions import DIR_TMP, DIR_TEST, REGEX_FILENAMES\n\nimport niftymic.application.rsfmri_estimate_motion as rsfmri_estimate_motion\nimport niftymic.application.rsfmri_reconstruct_volume_from_slices as rsfmri_reconstruct_volume_from_slices\n\n\nclass CaseStudyRestingStateFMRITest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_data = os.path.join(DIR_TEST, \"case-studies\", \"rsfmri\")\n        self.filename = os.path.join(\n            self.dir_data, \"data\", \"1000AB97_bold_3componentsonly.nii.gz\")\n        self.dir_output = os.path.join(DIR_TMP, \"case-studies\", \"rsfmri\")\n        self.suffix_mask = \"_mask\"\n\n    def test_estimate_motion(self):\n        filename = \"SRR_reference.nii.gz\"\n        output = os.path.join(self.dir_output, filename)\n        dir_reference = os.path.join(self.dir_data, \"estimate_motion\")\n        dir_reference_mc = os.path.join(dir_reference, \"motion_correction\")\n        path_to_reference = os.path.join(dir_reference, filename)\n        path_to_reference_mask = ph.append_to_filename(\n            os.path.join(dir_reference, filename), self.suffix_mask)\n\n        two_step_cycles = 1\n        iter_max = 5\n\n        exe = os.path.abspath(rsfmri_estimate_motion.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filename %s\" % self.filename)\n        cmd_args.append(\"--filename-mask %s\" % ph.append_to_filename(\n            self.filename, self.suffix_mask))\n        cmd_args.append(\"--dir-output %s\" % self.dir_output)\n        cmd_args.append(\"--two-step-cycles %s\" % two_step_cycles)\n        cmd_args.append(\"--iter-max %d\" % iter_max)\n        cmd = (\" \").join(cmd_args)\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        # Check SRR volume\n        res_sitk = sitkh.read_nifti_image_sitk(output)\n        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference)\n        diff_sitk = res_sitk - ref_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n        # Check SRR mask volume\n        res_sitk = sitkh.read_nifti_image_sitk(\n            ph.append_to_filename(output, self.suffix_mask))\n        ref_sitk = sitkh.read_nifti_image_sitk(path_to_reference_mask)\n        diff_sitk = res_sitk - ref_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(diff_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n        # Check transforms\n        pattern = REGEX_FILENAMES + \"[.]tfm\"\n        p = re.compile(pattern)\n        dir_res_mc = os.path.join(self.dir_output, \"motion_correction\")\n        trafos_res = sorted(\n            [os.path.join(dir_res_mc, t)\n             for t in os.listdir(dir_res_mc) if p.match(t)])\n        trafos_ref = sorted(\n            [os.path.join(dir_reference_mc, t)\n             for t in os.listdir(dir_reference_mc) if p.match(t)])\n        self.assertEqual(len(trafos_res), len(trafos_ref))\n        for i in range(len(trafos_ref)):\n            nda_res = sitkh.read_transform_sitk(trafos_res[i]).GetParameters()\n            nda_ref = sitkh.read_transform_sitk(trafos_ref[i]).GetParameters()\n            nda_diff = np.linalg.norm(np.array(nda_res) - nda_ref)\n            self.assertAlmostEqual(nda_diff, 0, places=self.precision)\n\n    def test_reconstruct_volume_from_slices(self):\n        filename = \"bold_s2v.nii.gz\"\n        output = os.path.join(self.dir_output, filename)\n        dir_reference = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\")\n        dir_input_mc = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\", \"motion_correction\")\n        path_to_reference = os.path.join(dir_reference, filename)\n\n        iter_max = 3\n        alpha = 0.05\n        beta = -1\n\n        cmd_args = []\n        exe = os.path.abspath(rsfmri_reconstruct_volume_from_slices.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filename %s\" % self.filename)\n        cmd_args.append(\"--filename-mask %s\" % ph.append_to_filename(\n            self.filename, self.suffix_mask))\n        cmd_args.append(\"--dir-input-mc %s\" % dir_input_mc)\n        cmd_args.append(\"--output %s\" % output)\n        cmd_args.append(\"--iter-max %d\" % iter_max)\n        cmd_args.append(\"--alpha %f\" % alpha)\n        cmd_args.append(\"--beta %f\" % beta)\n        cmd = (\" \").join(cmd_args)\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        # Check whether identical reconstruction has been created\n        reconstruction_sitk = sitkh.read_sitk_vector_image(output)\n        reference_sitk = sitkh.read_sitk_vector_image(path_to_reference)\n\n        difference_sitk = reconstruction_sitk - reference_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(difference_sitk))\n\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n    def test_reconstruct_volume_from_slices_temporal_reg(self):\n        filename = \"bold_s2v_alpha0p05_beta0p5.nii.gz\"\n        output = os.path.join(self.dir_output, filename)\n        dir_reference = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\")\n        dir_input_mc = os.path.join(\n            self.dir_data, \"reconstruct_volume_from_slices\", \"motion_correction\")\n        path_to_reference = os.path.join(dir_reference, filename)\n\n        iter_max = 3\n        alpha = 0.05\n        beta = 0.5\n\n        cmd_args = []\n        exe = os.path.abspath(rsfmri_reconstruct_volume_from_slices.__file__)\n        cmd_args = [\"python %s\" % exe]\n        cmd_args.append(\"--filename %s\" % self.filename)\n        cmd_args.append(\"--filename-mask %s\" % ph.append_to_filename(\n            self.filename, self.suffix_mask))\n        cmd_args.append(\"--dir-input-mc %s\" % dir_input_mc)\n        cmd_args.append(\"--output %s\" % output)\n        cmd_args.append(\"--iter-max %d\" % iter_max)\n        cmd_args.append(\"--alpha %f\" % alpha)\n        cmd_args.append(\"--beta %f\" % beta)\n        # cmd_args.append(\"--reconstruction-type TVL2\")\n        cmd = (\" \").join(cmd_args)\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        # Check whether identical reconstruction has been created\n        reconstruction_sitk = sitkh.read_sitk_vector_image(output)\n        reference_sitk = sitkh.read_sitk_vector_image(path_to_reference)\n\n        difference_sitk = reconstruction_sitk - reference_sitk\n        error = np.linalg.norm(sitk.GetArrayFromImage(difference_sitk))\n\n        self.assertAlmostEqual(error, 0, places=self.precision)\n"
  },
  {
    "path": "tests/data_reader_test.py",
    "content": "##\n# \\file data_reader_test.py\n#  \\brief  Unit tests for data reader\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date January 2018\n\n\nimport os\nimport unittest\nimport numpy as np\nimport re\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport niftymic.base.data_reader as dr\n\nfrom niftymic.definitions import DIR_TMP, DIR_TEST\n\n\nclass DataReaderTest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_data = os.path.join(DIR_TEST, \"case-studies\", \"fetal-brain\")\n        self.filenames = [\n            os.path.join(self.dir_data,\n                         \"input-data\",\n                         \"%s.nii.gz\" % f)\n            for f in [\"axial\", \"coronal\", \"sagittal\"]]\n        self.dir_output = os.path.join(DIR_TMP, \"case-studies\", \"fetal-brain\")\n        self.suffix_mask = \"_mask\"\n\n    ##\n    # Check that the same number of stacks (and slices therein) are read\n    # \\date       2018-01-31 23:03:52+0000\n    #\n    # \\param      self  The object\n    #\n    def test_read_transformations(self):\n\n        directory_motion_correction = os.path.join(\n            DIR_TEST,\n            \"case-studies\",\n            \"fetal-brain\",\n            \"reconstruct_volume\",\n            \"motion_correction\",\n        )\n\n        data_reader = dr.MultipleImagesReader(\n            file_paths=self.filenames,\n            dir_motion_correction=directory_motion_correction)\n        data_reader.read_data()\n        stacks = data_reader.get_data()\n\n        data_reader = dr.SliceTransformationDirectoryReader(\n            directory_motion_correction)\n        data_reader.read_data()\n        transformations_dic = data_reader.get_data()\n\n        self.assertEqual(len(stacks) - len(transformations_dic.keys()), 0)\n\n        for stack in stacks:\n            N_slices = stack.get_number_of_slices()\n            N_slices2 = len(transformations_dic[stack.get_filename()].keys())\n            self.assertEqual(N_slices - N_slices2, 0)\n\n\n"
  },
  {
    "path": "tests/image_similarity_evaluator_test.py",
    "content": "##\n# \\file image_similarity_evaluator_test.py\n#  \\brief  Test ImageSimilarityEvaluator class\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date February 2018\n\n\nimport os\nimport unittest\nimport numpy as np\nimport re\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nimport niftymic.validation.image_similarity_evaluator as ise\nimport niftymic.base.exceptions as exceptions\nfrom niftymic.definitions import DIR_TMP, DIR_TEST\n\n\nclass ImageSimilarityEvaluatorTest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n\n    def test_compute_write_read_similarities(self):\n\n        paths_to_stacks = [\n            os.path.join(\n                DIR_TEST, \"fetal_brain_%d.nii.gz\" % d) for d in range(0, 3)\n        ]\n        path_to_reference = os.path.join(\n            DIR_TEST, \"FetalBrain_reconstruction_3stacks_myAlg.nii.gz\")\n\n        reference = st.Stack.from_filename(\n            path_to_reference, extract_slices=False)\n\n        stacks = [\n            st.Stack.from_filename(p, ph.append_to_filename(p, \"_mask\"))\n            for p in paths_to_stacks\n        ]\n        stacks = [s.get_resampled_stack(reference.sitk) for s in stacks]\n\n        residual_evaluator = ise.ImageSimilarityEvaluator(stacks, reference)\n        residual_evaluator.compute_similarities()\n        residual_evaluator.write_similarities(DIR_TMP)\n        similarities = residual_evaluator.get_similarities()\n\n        similarities1 = ise.ImageSimilarityEvaluator()\n        similarities1.read_similarities(DIR_TMP)\n        similarities1 = similarities1.get_similarities()\n\n        for m in residual_evaluator.get_measures():\n            rho_res = similarities[m]\n            rho_res1 = similarities1[m]\n            error = np.linalg.norm(rho_res - rho_res1)\n            self.assertAlmostEqual(error, 0, places=self.precision)\n\n    def test_results_not_created(self):\n        residual_evaluator = ise.ImageSimilarityEvaluator()\n\n        # Directory does not exist\n        self.assertRaises(\n            IOError, lambda:\n            residual_evaluator.read_similarities(\n                os.path.join(DIR_TMP, \"whatevertestasdfsfasdasf\")))\n\n        # Directory does exist but does not contain similarity result files\n        self.assertRaises(IOError, lambda:\n                          residual_evaluator.read_similarities(DIR_TEST))\n"
  },
  {
    "path": "tests/installation_test.py",
    "content": "##\n# \\file installation_test.py\n#  \\brief  Class to test installation\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date October 2017\n\n\n# Import libraries\nimport unittest\n\nfrom nipype.testing import example_data\n\nimport niftymic.base.stack as st\nimport niftymic.registration.flirt as flirt\nimport niftymic.registration.niftyreg as niftyreg\nimport niftymic.utilities.brain_stripping as bs\n\n\nclass InstallationTest(unittest.TestCase):\n\n    def setUp(self):\n\n        self.accuracy = 10\n\n        self.path_to_fixed = example_data(\"segmentation0.nii.gz\")\n        self.path_to_moving = example_data(\"segmentation1.nii.gz\")\n\n        self.fixed = st.Stack.from_filename(self.path_to_fixed)\n        self.moving = st.Stack.from_filename(self.path_to_moving)\n\n    ##\n    # Test whether FSL installation was successful\n    # \\date       2017-10-26 15:02:44+0100\n    #\n    def test_fsl(self):\n\n        # Run flirt registration\n        registration_method = flirt.FLIRT(\n            fixed=self.fixed, moving=self.moving)\n        registration_method.run()\n\n        # Run BET brain stripping\n        brain_stripper = bs.BrainStripping.from_sitk_image(self.fixed.sitk)\n        brain_stripper.run()\n\n    ##\n    # Test whether NiftyReg installation was successful\n    # \\date       2017-10-26 15:08:59+0100\n    #\n    def test_niftyreg(self):\n\n        # Run reg_aladin registration\n        registration_method = niftyreg.RegAladin(\n            fixed=self.fixed, moving=self.moving)\n        registration_method.run()\n\n        # Run reg_f3d registration\n        registration_method = niftyreg.RegF3D(\n            fixed=self.fixed, moving=self.moving)\n        registration_method.run()\n\n    ##\n    # Test whether ITK_NiftyMIC installation was successful\n    # \\date       2017-10-26 15:12:26+0100\n    #\n    def test_itk_niftymic(self):\n\n        import itk\n        image_itk = itk.Image.D3.New()\n        filter_itk = itk.OrientedGaussianInterpolateImageFilter.ID3ID3.New()\n\n"
  },
  {
    "path": "tests/installation_test_fetal_brain_seg.py",
    "content": "##\n# \\file installation_test_fetal_brain_seg.py\n# \\brief      Class to test installation of fetal_brain_seg\n#             (https://github.com/gift-surg/fetal_brain_seg)\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       March 2019\n#\n\nimport os\nimport unittest\n\nimport pysitk.python_helper as ph\n\nfrom niftymic.definitions import DIR_TMP, DIR_TEMPLATES\n\n\nclass InstallationTest(unittest.TestCase):\n\n    def setUp(self):\n\n        self.accuracy = 10\n\n        self.path_to_image = os.path.join(DIR_TEMPLATES, \"STA23.nii.gz\")\n\n    ##\n    # Test whether fetal_brain_seg can be called\n    # \\date       2019-03-24 14:14:18+0000\n    #\n    def test_fetal_brain_seg(self):\n\n        dir_output = os.path.join(DIR_TMP, \"seg\")\n        cmd_args = [\"niftymic_segment_fetal_brains\"]\n        cmd_args.append(\"--filenames %s\" % self.path_to_image)\n        cmd_args.append(\"--dir-output %s\" % dir_output)\n        cmd_args.append(\"--neuroimage-legacy-seg 1\")\n        # cmd_args.append(\"--verbose 1\")\n        cmd = \" \".join(cmd_args)\n\n        flag = ph.execute_command(cmd)\n"
  },
  {
    "path": "tests/installation_test_monaifbs.py",
    "content": "##\n# \\file installation_test_monaifbs.py\n# \\brief      Class to test installation of MONAIfbs\n#             (https://github.com/gift-surg/MONAIfbs)\n#\n# \\author     Marta Ranzini (marta.ranzini@kcl.ac.uk)\n# \\date       December 2020\n#\n\nimport os\nimport unittest\n\nimport pysitk.python_helper as ph\n\nfrom niftymic.definitions import DIR_TMP, DIR_TEMPLATES\n\n\nclass InstallationTestMonaiFbs(unittest.TestCase):\n\n    def setUp(self):\n\n        self.accuracy = 10\n\n        self.path_to_image = os.path.join(DIR_TEMPLATES, \"STA23.nii.gz\")\n\n    ##\n    # Test whether monaifbs can be called\n    # \\date       2019-03-24 14:14:18+0000\n    #\n    def test_fetal_brain_seg(self):\n\n        dir_output = os.path.join(DIR_TMP, \"seg\")\n        cmd_args = [\"niftymic_segment_fetal_brains\"]\n        cmd_args.append(\"--filenames %s\" % self.path_to_image)\n        cmd_args.append(\"--dir-output %s\" % dir_output)\n        # cmd_args.append(\"--verbose 1\")\n        cmd = \" \".join(cmd_args)\n\n        flag = ph.execute_command(cmd)\n"
  },
  {
    "path": "tests/intensity_correction_test.py",
    "content": "# \\file TestIntensityCorrection.py\n\n##\n#  \\brief  Class containing unit tests for module IntensityCorrection\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date October 2016\n\nimport os\nimport unittest\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.intensity_correction as ic\nfrom niftymic.definitions import DIR_TEST\n\n\nclass IntensityCorrectionTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 6\n    use_verbose = False\n\n    def setUp(self):\n        pass\n\n    def test_linear_intensity_correction(self):\n\n        # Create stack of Lena slices\n        shape_z = 15\n\n        # Original stack\n        nda_2D = ph.read_image(\n            os.path.join(self.dir_test_data, \"2D_Lena_256.png\"))\n\n        nda_3D = np.tile(nda_2D, (shape_z, 1, 1)).astype('double')\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack = st.Stack.from_sitk_image(\n            image_sitk=stack_sitk,\n            filename=\"Lena\",\n            slice_thickness=stack_sitk.GetSpacing()[-1],\n        )\n\n        # 1) Create linearly corrupted intensity stack\n        nda_3D_corruped = np.zeros_like(nda_3D)\n        for i in range(0, shape_z):\n            nda_3D_corruped[i, :, :] = nda_3D[i, :, :] / (i + 1.)\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corruped)\n        stack_corrupted = st.Stack.from_sitk_image(\n            image_sitk=stack_corrupted_sitk,\n            filename=\"stack_corrupted\",\n            slice_thickness=stack_corrupted_sitk.GetSpacing()[-1],\n        )\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # Ground truth-parameter:\n        ic_values = np.zeros((shape_z, 1))\n        for i in range(0, shape_z):\n            ic_values[i, :] = (i + 1.)\n\n        intensity_correction = ic.IntensityCorrection(\n            stack=stack_corrupted,\n            reference=stack,\n            use_individual_slice_correction=True,\n            use_verbose=self.use_verbose)\n        intensity_correction.run_linear_intensity_correction()\n        ic_values_est = intensity_correction.get_intensity_correction_coefficients()\n\n        nda_diff = ic_values - ic_values_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n    def test_affine_intensity_correction(self):\n\n        # Create stack of Lena slices\n        shape_z = 15\n\n        # Original stack\n        nda_2D = ph.read_image(\n            os.path.join(self.dir_test_data, \"2D_Lena_256.png\"))\n        nda_3D = np.tile(nda_2D, (shape_z, 1, 1)).astype('double')\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack = st.Stack.from_sitk_image(\n            image_sitk=stack_sitk,\n            filename=\"Lena\",\n            slice_thickness=stack_sitk.GetSpacing()[-1],\n        )\n\n        # 1) Create linearly corrupted intensity stack\n        nda_3D_corruped = np.zeros_like(nda_3D)\n        for i in range(0, shape_z):\n            nda_3D_corruped[i, :, :] = (nda_3D[i, :, :] - 10 * i) / (i + 1.)\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corruped)\n        stack_corrupted = st.Stack.from_sitk_image(\n            image_sitk=stack_corrupted_sitk,\n            filename=\"stack_corrupted\",\n            slice_thickness=stack_corrupted_sitk.GetSpacing()[-1],\n        )\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # Ground truth-parameter:\n        ic_values = np.zeros((shape_z, 2))\n        for i in range(0, shape_z):\n            ic_values[i, :] = (i + 1, 10 * i)\n\n        intensity_correction = ic.IntensityCorrection(\n            stack=stack_corrupted,\n            reference=stack,\n            use_individual_slice_correction=True,\n            use_verbose=self.use_verbose)\n        intensity_correction.run_affine_intensity_correction()\n        ic_values_est = intensity_correction.get_intensity_correction_coefficients()\n\n        nda_diff = ic_values - ic_values_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n"
  },
  {
    "path": "tests/intra_stack_registration_test.py",
    "content": "##\n# \\file intra_stack_registration_test.py\n#  \\brief  Class containing unit tests for module IntraStackRegistration\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date October 2016\n\n\n# Import libraries\nimport SimpleITK as sitk\nimport itk\nimport numpy as np\nimport unittest\nimport sys\nimport os\nfrom scipy.ndimage import imread\n\nimport pysitk.simple_itk_helper as sitkh\nimport pysitk.python_helper as ph\n\n# Import modules\nimport niftymic.base.stack as st\nimport niftymic.registration.intra_stack_registration as inplanereg\n\nfrom niftymic.definitions import DIR_TEST\n\n\ndef get_inplane_corrupted_stack(stack,\n                                angle_z,\n                                center_2D,\n                                translation_2D,\n                                scale=1,\n                                intensity_scale=1,\n                                intensity_bias=0,\n                                debug=0,\n                                random=False):\n\n    # Convert to 3D:\n    translation_3D = np.zeros(3)\n    translation_3D[0:-1] = translation_2D\n\n    center_3D = np.zeros(3)\n    center_3D[0:-1] = center_2D\n\n    # Transform to align physical coordinate system with stack-coordinate\n    # system\n    affine_centering_sitk = sitk.AffineTransform(3)\n    affine_centering_sitk.SetMatrix(stack.sitk.GetDirection())\n    affine_centering_sitk.SetTranslation(stack.sitk.GetOrigin())\n\n    # Corrupt first stack towards positive direction\n    if random:\n        angle_z_1 = -angle_z*np.random.rand(1)[0]\n    else:\n        angle_z_1 = -angle_z\n\n    in_plane_motion_sitk = sitk.Euler3DTransform()\n    in_plane_motion_sitk.SetRotation(0, 0, angle_z_1)\n    in_plane_motion_sitk.SetCenter(center_3D)\n    in_plane_motion_sitk.SetTranslation(translation_3D)\n    motion_sitk = sitkh.get_composite_sitk_affine_transform(\n        in_plane_motion_sitk, sitk.AffineTransform(\n            affine_centering_sitk.GetInverse()))\n    motion_sitk = sitkh.get_composite_sitk_affine_transform(\n        affine_centering_sitk, motion_sitk)\n    stack_corrupted_resampled_sitk = sitk.Resample(\n        stack.sitk, motion_sitk, sitk.sitkLinear)\n    stack_corrupted_resampled_sitk_mask = sitk.Resample(\n        stack.sitk_mask, motion_sitk, sitk.sitkLinear)\n\n    # Corrupt first stack towards negative direction\n    if random:\n        angle_z_2 = -angle_z*np.random.rand(1)[0]\n    else:\n        angle_z_2 = -angle_z\n\n    in_plane_motion_2_sitk = sitk.Euler3DTransform()\n    in_plane_motion_2_sitk.SetRotation(0, 0, angle_z_2)\n    in_plane_motion_2_sitk.SetCenter(center_3D)\n    in_plane_motion_2_sitk.SetTranslation(-translation_3D)\n    motion_2_sitk = sitkh.get_composite_sitk_affine_transform(\n        in_plane_motion_2_sitk, sitk.AffineTransform(\n            affine_centering_sitk.GetInverse()))\n    motion_2_sitk = sitkh.get_composite_sitk_affine_transform(\n        affine_centering_sitk, motion_2_sitk)\n    stack_corrupted_2_resampled_sitk = sitk.Resample(\n        stack.sitk, motion_2_sitk, sitk.sitkLinear)\n    stack_corrupted_2_resampled_sitk_mask = sitk.Resample(\n        stack.sitk_mask, motion_2_sitk, sitk.sitkLinear)\n\n    # Create stack based on those two corrupted stacks\n    nda = sitk.GetArrayFromImage(stack_corrupted_resampled_sitk)\n    nda_mask = sitk.GetArrayFromImage(stack_corrupted_resampled_sitk_mask)\n    nda_neg = sitk.GetArrayFromImage(stack_corrupted_2_resampled_sitk)\n    nda_neg_mask = sitk.GetArrayFromImage(\n        stack_corrupted_2_resampled_sitk_mask)\n    for i in range(0, stack.sitk.GetDepth(), 2):\n        nda[i, :, :] = nda_neg[i, :, :]\n        nda_mask[i, :, :] = nda_neg_mask[i, :, :]\n    stack_corrupted_sitk = sitk.GetImageFromArray(\n        (nda-intensity_bias)/intensity_scale)\n    stack_corrupted_sitk_mask = sitk.GetImageFromArray(nda_mask)\n    stack_corrupted_sitk.CopyInformation(stack.sitk)\n    stack_corrupted_sitk_mask.CopyInformation(stack.sitk_mask)\n\n    # Debug: Show corrupted stacks (before scaling)\n    if debug:\n        sitkh.show_sitk_image(\n            [stack.sitk,\n             stack_corrupted_resampled_sitk,\n             stack_corrupted_2_resampled_sitk,\n             stack_corrupted_sitk],\n            title=[\"original\",\n                   \"corrupted_1\",\n                   \"corrupted_2\",\n                   \"corrupted_final_from_1_and_2\"])\n\n    # Update in-plane scaling\n    spacing = np.array(stack.sitk.GetSpacing())\n    spacing[0:-1] /= scale\n    stack_corrupted_sitk.SetSpacing(spacing)\n    stack_corrupted_sitk_mask.SetSpacing(spacing)\n\n    # Create Stack object\n    stack_corrupted = st.Stack.from_sitk_image(\n        stack_corrupted_sitk, \"stack_corrupted\", stack_corrupted_sitk_mask)\n\n    # Debug: Show corrupted stacks (after scaling)\n    if debug:\n        stack_corrupted_resampled_sitk = sitk.Resample(\n            stack_corrupted.sitk, stack.sitk)\n        sitkh.show_sitk_image(\n            [stack.sitk,\n             stack_corrupted_resampled_sitk],\n            title=[\"original\", \"corrupted\"])\n\n    return stack_corrupted, motion_sitk, motion_2_sitk\n\n\nclass IntraStackRegistrationTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 6\n\n    def setUp(self):\n        pass\n\n    ##\n    #       Test whether the function\n    #             _get_initial_transforms_and_parameters_geometry_moments\n    #             works.\n    # \\date       2016-11-09 23:59:25+0000\n    #\n    # \\param      self  The object\n    #\n    def test_initial_transform_computation_1(self):\n\n        # Create stack of slice with only a dot in the middle\n        shape_xy = 15\n        shape_z = 15\n\n        # Original stack\n        nda_3D = np.zeros((shape_z, shape_xy, shape_xy))\n        nda_3D[:, 0, 0] = 1\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack = st.Stack.from_sitk_image(stack_sitk, \"stack\")\n\n        # Create 'motion corrupted stack', i.e. point moves diagonally with\n        # step one\n        nda_3D_corruped = np.zeros_like(nda_3D)\n        for i in range(0, shape_z):\n            nda_3D_corruped[i, i, i] = 1\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corruped)\n        stack_corrupted = st.Stack.from_sitk_image(\n            stack_corrupted_sitk, \"stack_corrupted\")\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # Ground truth-parameter: zero angle but translation = (1, 1) from one\n        # slice to the next\n        parameters = np.ones((shape_z, 3))\n        parameters[:, 0] = 0\n        for i in range(0, shape_z):\n            parameters[i, :] *= i\n\n        # 1) Get initial transform in case no reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n        # 2) Get initial transform in case reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_image_transform_reference_fit_term(\"gradient_magnitude\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration._run_registration_pipeline_initialization()\n        inplane_registration._apply_motion_correction()\n        # stack_corrected = inplane_registration.get_corrected_stack()\n        # sitkh.show_stacks([stack, stack_corrupted, stack_corrected.get_resampled_stack_from_slices(interpolator=\"Linear\")])\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        # print(nda_diff)\n        # print(parameters)\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n    ##\n    #       Test whether the function\n    #             _get_initial_transforms_and_parameters_geometry_moments\n    #             works.\n    # \\date       2016-11-09 23:59:25+0000\n    #\n    # \\param      self  The object\n    #\n    def test_initial_transform_computation_2(self):\n\n        # Create stack of slice with a pyramid in the middle\n        shape_xy = 250\n        shape_z = 15\n\n        intensity_mask = 10\n\n        length = 50\n        nda_2D = ph.read_image(os.path.join(\n            DIR_TEST, \"2D_Pyramid_Midpoint_\" + str(length) + \".png\"))\n\n        # Original stack\n        nda_3D = np.zeros((shape_z, shape_xy, shape_xy))\n        i0 = (shape_xy - length) / 2\n\n        for i in range(0, shape_z):\n            nda_3D[i, i0:-i0, i0:-i0] = nda_2D\n\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack = st.Stack.from_sitk_image(stack_sitk, \"stack\")\n\n        # Create 'motion corrupted stack', i.e. in-plane translation, and\n        # associated ground-truth parameters\n        parameters = np.zeros((shape_z, 3))\n        parameters[:, 0] = 0\n\n        nda_3D_corrupted = np.zeros_like(nda_3D)\n        nda_3D_corrupted[0, :, :] = nda_3D[0, :, :]\n        for i in range(1, shape_z):\n            # Get random translation\n            [tx, ty] = np.random.randint(0, 50, 2)\n\n            # Get image based on corruption\n            inew = i0 + tx\n            jnew = i0 + ty\n            nda_3D_corrupted[i, inew:, jnew:] = \\\n                nda_3D[i, i0:2*i0+length-tx, i0:2*i0+length-ty]\n\n            # Get ground-truth parameters\n            parameters[i, 1] = ty\n            parameters[i, 2] = tx\n\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corrupted)\n        stack_corrupted = st.Stack.from_sitk_image(\n            stack_corrupted_sitk, \"stack_corrupted\")\n\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # 1) Get initial transform in case no reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        # inplane_registration.set_transform_initializer_type(\"geometry\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        # Debug:\n        # inplane_registration._apply_motion_correction()\n        # stack_corrected = inplane_registration.get_corrected_stack()\n        # sitkh.show_stacks(\n        #     [stack,\n        #      stack_corrupted,\n        #      stack_corrected.get_resampled_stack_from_slices(\n        #          interpolator=\"Linear\", filename=\"stack_corrected\")])\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n        # 2) Get initial transform in case reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        # Debug:\n        # inplane_registration._apply_motion_correction()\n        # stack_corrected = inplane_registration.get_corrected_stack()\n        # sitkh.show_stacks(\n        #     [stack,\n        #      stack_corrupted,\n        #      stack_corrected.get_resampled_stack_from_slices(\n        #          interpolator=\"Linear\", filename=\"stack_corrected\")])\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        # print(nda_diff)\n        # print(parameters)\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n    ##\n    #       Test whether the function\n    #             _get_initial_transforms_and_parameters_geometry_moments\n    #             works.\n    # \\date       2016-11-09 23:59:25+0000\n    #\n    # \\param      self  The object\n    #\n    def test_initial_transform_computation_3(self):\n\n        # Create stack of slice with a pyramid in the middle\n        shape_xy = 250\n        shape_z = 15\n\n        intensity_mask = 10\n\n        length = 50\n        nda_2D = ph.read_image(os.path.join(\n            DIR_TEST, \"2D_Pyramid_Midpoint_\" + str(length) + \".png\"))\n\n        # Original stack\n        nda_3D = np.zeros((shape_z, shape_xy, shape_xy))\n        i0 = (shape_xy - length) / 2\n\n        for i in range(0, shape_z):\n            nda_3D[i, i0:-i0, i0:-i0] = nda_2D\n\n        nda_3D_mask = np.array(nda_3D).astype(np.uint8)\n        nda_3D_mask[np.where(nda_3D_mask <= intensity_mask)] = 0\n        nda_3D_mask[np.where(nda_3D_mask > intensity_mask)] = 1\n\n        # Add additional weight s.t. initialization without mask fails\n        for i in range(0, shape_z):\n            nda_3D[i, -i0:, -i0:] = 10\n\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack_sitk_mask = sitk.GetImageFromArray(nda_3D_mask)\n        stack = st.Stack.from_sitk_image(stack_sitk, \"stack\", stack_sitk_mask)\n\n        # Create 'motion corrupted stack', i.e. in-plane translation, and\n        # associated ground-truth parameters\n        parameters = np.zeros((shape_z, 3))\n        parameters[:, 0] = 0\n\n        nda_3D_corrupted = np.zeros_like(nda_3D)\n        nda_3D_corrupted[0, :, :] = nda_3D[0, :, :]\n        nda_3D_corrupted_mask = np.zeros_like(nda_3D_mask)\n        nda_3D_corrupted_mask[0, :, :] = nda_3D_mask[0, :, :]\n        for i in range(1, shape_z):\n            # Get random translation\n            [tx, ty] = np.random.randint(0, 50, 2)\n\n            # Get image based on corruption\n            inew = i0 + tx\n            jnew = i0 + ty\n            nda_3D_corrupted[i, inew:, jnew:] = \\\n                nda_3D[i, i0:2*i0+length-tx, i0:2*i0+length-ty]\n\n            nda_3D_corrupted_mask[i, inew:, jnew:] = \\\n                nda_3D_mask[i, i0:2*i0+length-tx, i0:2*i0+length-ty]\n\n            # Get ground-truth parameters\n            parameters[i, 1] = ty\n            parameters[i, 2] = tx\n\n        # nda_3D_corrupted = np.zeros_like(nda_3D)\n        # nda_3D_corrupted[0, i0:-i0, i0:-i0] = nda_2D\n        # for i in range(1, shape_z):\n        #     # Get random translation\n        #     [tx, ty] = np.random.randint(0, 50, 2)\n\n        #     # Get image based on corruption\n        #     inew = i0 + tx\n        #     jnew = i0 + ty\n        #     nda_3D_corrupted[i, inew:inew+length, jnew:jnew+length] = nda_2D\n\n        #     # Get ground-truth parameters\n        #     parameters[i, 1] = ty\n        #     parameters[i, 2] = tx\n\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corrupted)\n        stack_corrupted_sitk_mask = sitk.GetImageFromArray(\n            nda_3D_corrupted_mask)\n        stack_corrupted = st.Stack.from_sitk_image(\n            stack_corrupted_sitk, \"stack_corrupted\", stack_corrupted_sitk_mask)\n        # stack_corrupted.show(1)\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted],\n        #     segmentation=stack)\n\n        # 1) Get initial transform in case no reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted,\n            use_stack_mask=True,\n        )\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        # inplane_registration.set_transform_initializer_type(\"geometry\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        # Debug:\n        # inplane_registration._apply_motion_correction()\n        # stack_corrected = inplane_registration.get_corrected_stack()\n        # sitkh.show_stacks(\n        #     [stack,\n        #      stack_corrupted,\n        #      stack_corrected.get_resampled_stack_from_slices(\n        #          interpolator=\"Linear\", filename=\"stack_corrected\")])\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n        # 2) Get initial transform in case reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration.use_reference_mask(True)\n        inplane_registration.use_stack_mask_reference_fit_term(True)\n        inplane_registration._run_registration_pipeline_initialization()\n        # Debug:\n        # inplane_registration._apply_motion_correction()\n        # stack_corrected = inplane_registration.get_corrected_stack()\n        # sitkh.show_stacks(\n        #     [stack,\n        #      stack_corrupted,\n        #      stack_corrected.get_resampled_stack_from_slices(\n        #          interpolator=\"Linear\", filename=\"stack_corrected\")])\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        # print(nda_diff)\n        # print(parameters)\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n    ##\n    #       Test that initial intensity coefficients are computed\n    #             correctly\n    # \\date       2016-11-10 04:28:06+0000\n    #\n    # \\param      self  The object\n    #\n    def test_initial_intensity_coefficient_computation(self):\n        # Create stack\n        shape_z = 15\n        nda_2D = imread(self.dir_test_data + \"2D_Lena_256.png\", flatten=True)\n        nda_3D = np.tile(nda_2D, (shape_z, 1, 1)).astype('double')\n        stack_sitk = sitk.GetImageFromArray(nda_3D)\n        stack = st.Stack.from_sitk_image(stack_sitk, \"Lena\")\n\n        # 1) Create linearly corrupted intensity stack\n        nda_3D_corruped = np.zeros_like(nda_3D)\n        for i in range(0, shape_z):\n            nda_3D_corruped[i, :, :] = nda_3D[i, :, :]/(i+1.)\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corruped)\n        stack_corrupted = st.Stack.from_sitk_image(\n            stack_corrupted_sitk, \"stack_corrupted\")\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # Ground truth-parameter: zero angle but translation = (1, 1) from one\n        # slice to the next\n        parameters = np.zeros((shape_z, 4))\n        parameters[:, 0] = 0\n        for i in range(0, shape_z):\n            parameters[i, 3:] = 1*(i+1.)  # intensity\n\n        # Get initial transform in case no reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"linear\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"linear\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n        # 2) Create affinely corrupted intensity stack\n        # HINT: In case of individual slice correction is active!!\n        nda_3D_corruped = np.zeros_like(nda_3D)\n        for i in range(0, shape_z):\n            nda_3D_corruped[i, :, :] = (nda_3D[i, :, :]-10*i)/(i+1.)\n        stack_corrupted_sitk = sitk.GetImageFromArray(nda_3D_corruped)\n        stack_corrupted = st.Stack.from_sitk_image(\n            stack_corrupted_sitk, \"stack_corrupted\")\n        # stack_corrupted.show_slices()\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        # Ground truth-parameter: zero angle but translation = (1, 1) from one\n        # slice to the next\n        parameters = np.zeros((shape_z, 5))\n        parameters[:, 0] = 0\n        for i in range(0, shape_z):\n            parameters[i, 3:] = (i+1, 10*i)  # intensity\n\n        # Get initial transform in case no reference is given\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"affine\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"affine\")\n        inplane_registration._run_registration_pipeline_initialization()\n\n        parameters_est = inplane_registration.get_parameters()\n        nda_diff = parameters - parameters_est\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_diff), decimals=self.accuracy), 0)\n\n    ##\n    #       Verify that in-plane rigid registration works\n    # \\date       2016-11-02 21:56:19+0000\n    #\n    # Verify that in-plane rigid registration works, i.e. test\n    #   1) registration parameters are close to ground truth (up to zero dp)\n    #   2) affine transformations for each slice correctly describes the\n    #      registration\n    #\n    # \\param      self  The object\n    #\n    def test_inplane_rigid_alignment_to_neighbour(self):\n\n        filename_stack = \"fetal_brain_0\"\n        # filename_recon = \"FetalBrain_reconstruction_3stacks_myAlg\"\n\n        # stack_sitk = sitk.ReadImage(self.dir_test_data + filename_stack + \".nii.gz\")\n        # recon_sitk = sitk.ReadImage(self.dir_test_data + filename_recon + \".nii.gz\")\n\n        # recon_resampled_sitk = sitk.Resample(recon_sitk, stack_sitk)\n        # stack = st.Stack.from_sitk_image(recon_resampled_sitk, \"original\")\n\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename_stack + \"_mask.nii.gz\")\n        )\n\n        nda = sitk.GetArrayFromImage(stack.sitk)\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        i = 5\n        nda_slice = np.array(nda[i, :, :])\n        nda_mask_slice = np.array(nda_mask[i, :, :])\n\n        for i in range(0, nda.shape[0]):\n            nda[i, :, :] = nda_slice\n            nda_mask[i, :, :] = nda_mask_slice\n\n        stack_sitk = sitk.GetImageFromArray(nda)\n        stack_sitk_mask = sitk.GetImageFromArray(nda_mask)\n        stack_sitk.CopyInformation(stack.sitk)\n        stack_sitk_mask.CopyInformation(stack.sitk_mask)\n\n        stack = st.Stack.from_sitk_image(\n            stack_sitk, stack.get_filename(), stack_sitk_mask)\n\n        # Create in-plane motion corruption\n        angle_z = 0.1\n        center_2D = (0, 0)\n        translation_2D = np.array([1, -2])\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, random=True)\n\n        # stack.show(1)\n        # stack_corrupted.show(1)\n\n        # Perform in-plane rigid registration\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_optimizer_iter_max(20)\n        inplane_registration.set_alpha_neighbour(1)\n        inplane_registration.set_alpha_reference(2)\n        # inplane_registration.use_parameter_normalization(True)\n        inplane_registration.use_stack_mask(1)\n        inplane_registration.use_reference_mask(0)\n        # inplane_registration.set_optimizer_loss(\"linear\") # linear, soft_l1,\n        # huber\n        inplane_registration.set_optimizer_method(\"trf\")  # trf, lm, dogbox\n        # inplane_registration._run_registration_pipeline_initialization()\n        # inplane_registration._apply_motion_correction()\n        inplane_registration.use_verbose(True)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_stacks([stack, stack_corrupted, stack_registered.get_resampled_stack_from_slices(\n            interpolator=\"Linear\")])\n\n        # self.assertEqual(np.round(\n        #     np.linalg.norm(nda_diff)\n        # , decimals = self.accuracy), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    def test_inplane_rigid_alignment_to_reference(self):\n\n        filename_stack = \"fetal_brain_0\"\n        # filename_recon = \"FetalBrain_reconstruction_3stacks_myAlg\"\n\n        # stack_sitk = sitk.ReadImage(self.dir_test_data + filename_stack + \".nii.gz\")\n        # recon_sitk = sitk.ReadImage(self.dir_test_data + filename_recon + \".nii.gz\")\n\n        # recon_resampled_sitk = sitk.Resample(recon_sitk, stack_sitk)\n        # stack = st.Stack.from_sitk_image(recon_resampled_sitk, \"original\")\n\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename_stack + \"_mask.nii.gz\")\n        )\n\n        # Create in-plane motion corruption\n        angle_z = 0.1\n        center_2D = (0, 0)\n        translation_2D = np.array([1, -2])\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D)\n\n        # stack.show(1)\n        # stack_corrupted.show(1)\n\n        # Perform in-plane rigid registration\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_optimizer_iter_max(10)\n        inplane_registration.set_alpha_neighbour(0)\n        inplane_registration.set_alpha_parameter(0)\n        inplane_registration.use_stack_mask(1)\n        inplane_registration.use_reference_mask(0)\n        inplane_registration.set_optimizer_loss(\"linear\")\n        # inplane_registration.set_optimizer_method(\"trf\")\n        # inplane_registration._run_registration_pipeline_initialization()\n        # inplane_registration._apply_motion_correction()\n        # inplane_registration.use_verbose(True)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_stacks([stack, stack_corrupted, stack_registered.get_resampled_stack_from_slices(\n            interpolator=\"Linear\", resampling_grid=stack.sitk)])\n\n        print(parameters)\n\n        # self.assertEqual(np.round(\n        #     np.linalg.norm(nda_diff)\n        # , decimals = self.accuracy), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    def test_inplane_rigid_alignment_to_reference_with_intensity_correction_linear(self):\n\n        filename_stack = \"fetal_brain_0\"\n        filename_recon = \"FetalBrain_reconstruction_3stacks_myAlg\"\n\n        stack_sitk = sitk.ReadImage(\n            self.dir_test_data + filename_stack + \".nii.gz\")\n        recon_sitk = sitk.ReadImage(\n            self.dir_test_data + filename_recon + \".nii.gz\")\n\n        recon_resampled_sitk = sitk.Resample(recon_sitk, stack_sitk)\n        stack = st.Stack.from_sitk_image(recon_resampled_sitk, \"original\")\n\n        # Create in-plane motion corruption\n        angle_z = 0.05\n        center_2D = (0, 0)\n        translation_2D = np.array([1, -2])\n\n        intensity_scale = 10\n        intensity_bias = 0\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, intensity_scale=intensity_scale, intensity_bias=intensity_bias)\n\n        # Perform in-plane rigid registration\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_transform_type(\"rigid\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"linear\")\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"linear\")\n        inplane_registration.set_intensity_correction_type_reference_fit(\n            \"linear\")\n        inplane_registration.set_optimizer_loss(\n            \"linear\")  # linear, soft_l1, huber\n        inplane_registration.use_parameter_normalization(True)\n        inplane_registration.use_verbose(True)\n        inplane_registration.set_alpha_reference(1)\n        inplane_registration.set_alpha_neighbour(0)\n        inplane_registration.set_alpha_parameter(0)\n        inplane_registration.set_optimizer_iter_max(30)\n        inplane_registration.use_verbose(True)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_stacks([stack, stack_corrupted, stack_registered.get_resampled_stack_from_slices(\n            resampling_grid=None, interpolator=\"Linear\")])\n\n        print(\"Final parameters:\")\n        print(parameters)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(parameters[:, -1] - intensity_scale), decimals=0), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    ##\n    # \\bug There is some issue with slice based and uniform intensity correction.\n    # Unit test needs to be fixed at some point\n    # \\date       2017-07-12 12:40:01+0100\n    #\n    # \\param      self  The object\n    #\n    def test_inplane_rigid_alignment_to_reference_with_intensity_correction_affine(self):\n\n        filename_stack = \"fetal_brain_0\"\n        filename_recon = \"FetalBrain_reconstruction_3stacks_myAlg\"\n\n        stack_sitk = sitk.ReadImage(\n            self.dir_test_data + filename_stack + \".nii.gz\")\n        recon_sitk = sitk.ReadImage(\n            self.dir_test_data + filename_recon + \".nii.gz\")\n\n        recon_resampled_sitk = sitk.Resample(recon_sitk, stack_sitk)\n        stack = st.Stack.from_sitk_image(recon_resampled_sitk, \"original\")\n\n        # Create in-plane motion corruption\n        angle_z = 0.01\n        center_2D = (0, 0)\n        translation_2D = np.array([1, 0])\n\n        intensity_scale = 5\n        intensity_bias = 5\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, intensity_scale=intensity_scale, intensity_bias=intensity_bias)\n\n        # Perform in-plane rigid registration\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_type(\"rigid\")\n        inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration.set_optimizer_loss(\"linear\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"affine\")\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"affine\")\n        inplane_registration.use_parameter_normalization(True)\n        inplane_registration.use_verbose(True)\n        inplane_registration.use_stack_mask(True)\n        inplane_registration.set_prior_intensity_coefficients(\n            (intensity_scale-0.4, intensity_bias+0.7))\n        inplane_registration.set_alpha_reference(1)\n        inplane_registration.set_alpha_neighbour(1)\n        inplane_registration.set_alpha_parameter(1e3)\n        inplane_registration.set_optimizer_iter_max(15)\n        inplane_registration.use_verbose(True)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_stacks([stack, stack_corrupted, stack_registered.get_resampled_stack_from_slices(\n            resampling_grid=None, interpolator=\"Linear\")])\n\n        self.assertEqual(np.round(\n            np.linalg.norm(parameters[:, -2:] - np.array([intensity_scale, intensity_bias])), decimals=0), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    def test_inplane_similarity_alignment_to_reference(self):\n\n        filename_stack = \"fetal_brain_0\"\n        # filename_stack = \"3D_SheppLoganPhantom_64\"\n\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename_stack + \"_mask.nii.gz\")\n        )\n        # stack.show(1)\n\n        nda = sitk.GetArrayFromImage(stack.sitk)\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        i = 5\n        nda_slice = np.array(nda[i, :, :])\n        nda_mask_slice = np.array(nda_mask[i, :, :])\n\n        for i in range(0, nda.shape[0]):\n            nda[i, :, :] = nda_slice\n            nda_mask[i, :, :] = nda_mask_slice\n\n        stack_sitk = sitk.GetImageFromArray(nda)\n        stack_sitk_mask = sitk.GetImageFromArray(nda_mask)\n        stack_sitk.CopyInformation(stack.sitk)\n        stack_sitk_mask.CopyInformation(stack.sitk_mask)\n\n        stack = st.Stack.from_sitk_image(\n            stack_sitk, stack.get_filename(), stack_sitk_mask)\n\n        # Create in-plane motion corruption\n        scale = 1.2\n        angle_z = 0.05\n        center_2D = (0, 0)\n        # translation_2D = np.array([0,0])\n        translation_2D = np.array([1, -1])\n\n        intensity_scale = 10\n        intensity_bias = 50\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, scale=scale, intensity_scale=intensity_scale, intensity_bias=intensity_bias, debug=0)\n\n        # stack_corrupted.show(1)\n        # stack.show(1)\n\n        # Perform in-plane rigid registrations\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack=stack_corrupted, reference=stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"geometry\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"affine\")\n        inplane_registration.set_transform_type(\"similarity\")\n        inplane_registration.set_interpolator(\"Linear\")\n        inplane_registration.set_optimizer_loss(\"linear\")\n        # inplane_registration.use_reference_mask(True)\n        inplane_registration.use_stack_mask(True)\n        inplane_registration.use_parameter_normalization(True)\n        inplane_registration.set_prior_scale(1/scale)\n        inplane_registration.set_prior_intensity_coefficients(\n            (intensity_scale, intensity_bias))\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"affine\")\n        inplane_registration.set_intensity_correction_type_reference_fit(\n            \"affine\")\n        inplane_registration.use_verbose(True)\n        inplane_registration.set_alpha_reference(1)\n        inplane_registration.set_alpha_neighbour(0)\n        inplane_registration.set_alpha_parameter(1e10)\n        inplane_registration.set_optimizer_iter_max(20)\n        inplane_registration.use_verbose(True)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        # inplane_registration._run_registration_pipeline_initialization()\n        # inplane_registration._apply_motion_correction()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_sitk_image([stack.sitk, stack_corrupted.get_resampled_stack_from_slices(interpolator=\"Linear\", resampling_grid=stack.sitk).sitk,\n                               stack_registered.get_resampled_stack_from_slices(interpolator=\"Linear\", resampling_grid=stack.sitk).sitk], label=[\"original\", \"corrupted\", \"recovered\"])\n\n        # self.assertEqual(np.round(\n        #     np.linalg.norm(nda_diff)\n        # , decimals = self.accuracy), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    def test_inplane_rigid_alignment_to_reference_multimodal(self):\n\n        filename_stack = \"fetal_brain_0\"\n        filename_recon = \"FetalBrain_reconstruction_3stacks_myAlg\"\n\n        stack_tmp = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename_stack + \"_mask.nii.gz\")\n        )\n\n        recon = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_recon)\n        )\n\n        recon_sitk = recon.get_resampled_stack_from_slices(\n            resampling_grid=stack_tmp.sitk, interpolator=\"Linear\").sitk\n\n        stack = st.Stack.from_sitk_image(\n            recon_sitk, \"original\", stack_tmp.sitk_mask)\n\n        # recon_resampled_sitk = sitk.Resample(recon_sitk, stack_sitk)\n        # stack = st.Stack.from_sitk_image(recon_resampled_sitk, \"original\")\n\n        # Create in-plane motion corruption\n        scale = 1.05\n        angle_z = 0.05\n        center_2D = (0, 0)\n        translation_2D = np.array([1, -2])\n\n        intensity_scale = 1\n        intensity_bias = 0\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, intensity_scale=intensity_scale, scale=scale, intensity_bias=intensity_bias)\n\n        # stack_corrupted.show(1)\n        # stack.show(1)\n\n        # Perform in-plane rigid registration\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        # inplane_registration.set_image_transform_reference_fit_term(\"gradient_magnitude\")\n        inplane_registration.set_image_transform_reference_fit_term(\n            \"partial_derivative\")\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        # inplane_registration.set_transform_type(\"similarity\")\n        inplane_registration.set_intensity_correction_initializer_type(None)\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            None)\n        inplane_registration.set_intensity_correction_type_reference_fit(None)\n        inplane_registration.use_parameter_normalization(True)\n        inplane_registration.use_verbose(True)\n        inplane_registration.set_optimizer_loss(\n            \"linear\")  # linear, soft_l1, huber\n        inplane_registration.set_alpha_reference(100)\n        inplane_registration.set_alpha_neighbour(0)\n        inplane_registration.set_alpha_parameter(1)\n        # inplane_registration.use_stack_mask(True)\n        # inplane_registration.use_reference_mask(True)\n        inplane_registration.set_optimizer_iter_max(10)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_stacks([stack, stack_corrupted, stack_registered.get_resampled_stack_from_slices(\n            resampling_grid=None, interpolator=\"Linear\")])\n\n        # print(\"Final parameters:\")\n        # print(parameters)\n\n        # self.assertEqual(np.round(\n        #     np.linalg.norm(parameters[:,-1] - intensity_scale)\n        # , decimals = 0), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n\n    def test_inplane_uniform_scale_similarity_alignment_to_reference(self):\n\n        filename_stack = \"fetal_brain_0\"\n        # filename_stack = \"3D_SheppLoganPhantom_64\"\n\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename_stack + \"_mask.nii.gz\")\n        )\n        # stack.show(1)\n\n        nda = sitk.GetArrayFromImage(stack.sitk)\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        i = 5\n        nda_slice = np.array(nda[i, :, :])\n        nda_mask_slice = np.array(nda_mask[i, :, :])\n\n        for i in range(0, nda.shape[0]):  # 23 slices\n            nda[i, :, :] = nda_slice\n            nda_mask[i, :, :] = nda_mask_slice\n\n        stack_sitk = sitk.GetImageFromArray(nda)\n        stack_sitk_mask = sitk.GetImageFromArray(nda_mask)\n        stack_sitk.CopyInformation(stack.sitk)\n        stack_sitk_mask.CopyInformation(stack.sitk_mask)\n\n        stack = st.Stack.from_sitk_image(\n            stack_sitk, stack.get_filename(), stack_sitk_mask)\n\n        # Create in-plane motion corruption\n        # scale = 1.2\n        scale = 1\n        angle_z = 0.05\n        center_2D = (0, 0)\n        # translation_2D = np.array([0,0])\n        translation_2D = np.array([1, -1])\n\n        intensity_scale = 1\n        intensity_bias = 0\n\n        # Get corrupted stack and corresponding motions\n        stack_corrupted, motion_sitk, motion_2_sitk = get_inplane_corrupted_stack(\n            stack, angle_z, center_2D, translation_2D, scale=scale, intensity_scale=intensity_scale, intensity_bias=intensity_bias, debug=0)\n\n        # stack_corrupted.show(1)\n        # stack.show(1)\n\n        # Perform in-plane rigid registrations\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack=stack_corrupted,\n            reference=stack,\n            use_stack_mask=True,\n            use_reference_mask=True,\n            interpolator=\"Linear\",\n            use_verbose=True,\n        )\n        # inplane_registration = inplanereg.IntraStackRegistration(stack_corrupted)\n        inplane_registration.set_transform_initializer_type(\"geometry\")\n        # inplane_registration.set_transform_initializer_type(\"identity\")\n        inplane_registration.set_intensity_correction_initializer_type(\n            \"affine\")\n        # inplane_registration.set_transform_type(\"similarity\")\n        inplane_registration.set_transform_type(\"rigid\")\n        # inplane_registration.set_optimizer(\"least_squares\")\n        # inplane_registration.set_optimizer(\"BFGS\")\n        # inplane_registration.set_optimizer(\"L-BFGS-B\")\n        inplane_registration.set_optimizer(\"TNC\")\n        # inplane_registration.set_optimizer(\"Powell\")\n        # inplane_registration.set_optimizer(\"CG\")\n        # inplane_registration.set_optimizer(\"Newton-CG\")\n        inplane_registration.set_optimizer_loss(\"linear\")\n        # inplane_registration.set_optimizer_loss(\"soft_l1\")\n        # inplane_registration.set_optimizer_loss(\"arctan\")\n        # inplane_registration.use_parameter_normalization(True)\n        inplane_registration.set_prior_scale(1/scale)\n        inplane_registration.set_prior_intensity_coefficients(\n            (intensity_scale, intensity_bias))\n        # inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n        # \"affine\")\n        # inplane_registration.set_intensity_correction_type_reference_fit(\n        # \"affine\")\n        inplane_registration.set_alpha_reference(1)\n        inplane_registration.set_alpha_neighbour(0)\n        inplane_registration.set_alpha_parameter(0)\n        inplane_registration.set_optimizer_iter_max(30)\n        inplane_registration.run()\n        inplane_registration.print_statistics()\n\n        # inplane_registration._run_registration_pipeline_initialization()\n        # inplane_registration._apply_motion_correction()\n\n        stack_registered = inplane_registration.get_corrected_stack()\n        parameters = inplane_registration.get_parameters()\n\n        sitkh.show_sitk_image([stack.sitk, stack_corrupted.get_resampled_stack_from_slices(interpolator=\"Linear\", resampling_grid=stack.sitk).sitk,\n                               stack_registered.get_resampled_stack_from_slices(interpolator=\"Linear\", resampling_grid=stack.sitk).sitk], label=[\"original\", \"corrupted\", \"recovered\"])\n\n        # self.assertEqual(np.round(\n        #     np.linalg.norm(nda_diff)\n        # , decimals = self.accuracy), 0)\n\n        # 2) Test slice transforms\n        slice_transforms_sitk = inplane_registration.get_slice_transforms_sitk()\n\n        stack_tmp = st.Stack.from_stack(stack_corrupted)\n        stack_tmp.update_motion_correction_of_slices(slice_transforms_sitk)\n\n        stack_diff_sitk = stack_tmp.get_resampled_stack_from_slices(\n            resampling_grid=stack.sitk).sitk - stack_registered.get_resampled_stack_from_slices(resampling_grid=stack.sitk).sitk\n\n        stack_diff_nda = sitk.GetArrayFromImage(stack_diff_sitk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(stack_diff_nda), decimals=8), 0)\n"
  },
  {
    "path": "tests/linear_image_quality_transfer_test.py",
    "content": "# \\file TestLinearImageQualityTransfer.py\n#  \\brief  Class containing unit tests for module DifferentialOperations\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date July 2016\n\n\nimport unittest\n\n# Import libraries\nimport numpy as np\nfrom scipy import ndimage\n\nfrom niftymic.definitions import DIR_TEST\n\n\n# Import modules from src-folder\n\n\nclass LinearImageQualityTransferTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 6\n\n    def setUp(self):\n        pass\n\n    ##\n    #       Test whether interpretation of a 2D kernel in 3D is correct\n    # \\date       2016-11-06 15:26:19+0000\n    #\n    # \\param      self  The object\n    #\n    def test_kernel_2D_as_kernel_3D(self):\n\n        ## Shape in (z,y,x)-coordinates\n        nda_shape = (40, 200, 200)\n\n        # Define size of kernel\n        N = 6\n\n        # Create random data array\n        nda = 255 * np.random.rand(nda_shape[0], nda_shape[1], nda_shape[2])\n\n        # Create kernel with elements 1:N^2\n        kernel = np.arange(1, N*N+1)\n\n        # Define 2D- and equivalent 3D-kernel\n        kernel_2D = kernel.reshape(N, N)\n        kernel_3D = kernel.reshape(1, N, N)\n\n        # Create data array copies\n        nda_2D = np.array(nda)\n        nda_3D = np.array(nda)\n\n        # 2D\n        for i in range(0, nda.shape[0]):\n            nda_2D[i, :, :] = ndimage.convolve(nda_2D[i, :, :], kernel_2D)\n\n        # 3D\n        nda_3D = ndimage.convolve(nda_3D, kernel_3D)\n\n        self.assertEqual(np.around(\n            np.linalg.norm(nda_2D-nda_3D), decimals=self.accuracy), 0)\n"
  },
  {
    "path": "tests/linear_operators_test.py",
    "content": "##\n# \\file linear_operators_test.py\n#  \\brief  unit tests of linear operators\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date November 2017\n\n\nimport os\nimport unittest\nimport numpy as np\nimport re\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.reconstruction.linear_operators as lin_op\nimport niftymic.validation.simulate_stacks_from_reconstruction as \\\n    simulate_stacks_from_reconstruction\nfrom niftymic.definitions import DIR_TMP, DIR_TEST\n\n\nclass LinearOperatorsTest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_output = os.path.join(DIR_TMP, \"reconstruction\")\n\n        self.dir_data = os.path.join(DIR_TEST, \"case-studies\", \"fetal-brain\")\n        self.filename = \"axial\"\n        self.suffix_mask = \"_mask\"\n        self.path_to_file = os.path.join(\n            self.dir_data, \"input-data\", \"%s.nii.gz\" % self.filename)\n        self.filename_recon = \"SRR_stacks3_TK1_lsmr_alpha0p02_itermax5.nii.gz\"\n        self.path_to_recon = os.path.join(\n            self.dir_data, \"recon_projections\", self.filename_recon)\n        self.path_to_recon_mask = ph.append_to_filename(\n            self.path_to_recon, self.suffix_mask)\n\n    ##\n    # Test forward simulation of stack and associated propagation of\n    # (potentially existing) mask\n    # \\date       2017-11-28 22:37:54+0000\n    #\n    def test_forward_operator_stack(self):\n\n        stack = st.Stack.from_filename(self.path_to_file)\n        reconstruction = st.Stack.from_filename(\n            self.path_to_recon, self.path_to_recon_mask)\n\n        linear_operators = lin_op.LinearOperators()\n        simulated_stack = linear_operators.A(\n            reconstruction, stack, interpolator_mask=\"Linear\")\n        simulated_stack.set_filename(stack.get_filename() + \"_sim\")\n\n        # sitkh.show_stacks(\n        #     [stack, simulated_stack], segmentation=simulated_stack)\n\n        filename_reference = os.path.join(\n            self.dir_data,\n            \"recon_projections\",\n            \"stack\",\n            \"%s_sim.nii.gz\" % self.filename)\n        filename_reference_mask = os.path.join(\n            self.dir_data,\n            \"recon_projections\",\n            \"stack\",\n            \"%s_sim%s.nii.gz\" % (self.filename, self.suffix_mask))\n\n        reference_simulated_stack = st.Stack.from_filename(\n            filename_reference, filename_reference_mask)\n\n        # Error simulated stack\n        difference_sitk = simulated_stack.sitk - \\\n            reference_simulated_stack.sitk\n        error = np.linalg.norm(\n            sitk.GetArrayFromImage(difference_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n        # Error propagated mask\n        difference_sitk = simulated_stack.sitk_mask - \\\n            reference_simulated_stack.sitk_mask\n        error = np.linalg.norm(\n            sitk.GetArrayFromImage(difference_sitk))\n        self.assertAlmostEqual(error, 0, places=self.precision)\n\n    ##\n    # Test script to simulate stacks from slices\n    # \\date       2017-11-28 23:13:02+0000\n    #\n    def test_simulate_stacks_from_slices(self):\n\n        cmd_args = []\n        cmd_args.append(\"--filenames %s\" % self.path_to_file)\n        cmd_args.append(\"--dir-input-mc %s\" %\n                        os.path.join(\n                            self.dir_data,\n                            \"recon_projections\",\n                            \"motion_correction\"))\n        cmd_args.append(\"--reconstruction %s\" % self.path_to_recon)\n        cmd_args.append(\"--reconstruction-mask %s\" % self.path_to_recon_mask)\n        cmd_args.append(\"--copy-data 1\")\n        cmd_args.append(\"--suffix-mask %s\" % self.suffix_mask)\n        cmd_args.append(\"--dir-output %s\" % self.dir_output)\n\n        exe = os.path.abspath(simulate_stacks_from_reconstruction.__file__)\n        cmd = \"python %s %s\" % (exe, (\" \").join(cmd_args))\n        self.assertEqual(ph.execute_command(cmd), 0)\n\n        path_orig = os.path.join(self.dir_output, \"%s.nii.gz\" % self.filename)\n        path_sim = os.path.join(\n            self.dir_output, \"Simulated_%s.nii.gz\" % self.filename)\n\n        path_orig_ref = os.path.join(self.dir_data,\n                                     \"recon_projections\",\n                                     \"slices\",\n                                     \"%s.nii.gz\" % self.filename)\n        path_sim_ref = os.path.join(self.dir_data,\n                                    \"recon_projections\",\n                                    \"slices\",\n                                    \"Simulated_%s.nii.gz\" % self.filename)\n\n        for res, ref in zip(\n                [path_orig, path_sim], [path_orig_ref, path_sim_ref]):\n            res_sitk = sitk.ReadImage(res)\n            ref_sitk = sitk.ReadImage(ref)\n\n            nda_diff = np.nan_to_num(\n                sitk.GetArrayFromImage(res_sitk - ref_sitk))\n            self.assertAlmostEqual(np.linalg.norm(\n                nda_diff), 0, places=self.precision)\n"
  },
  {
    "path": "tests/niftyreg_test.py",
    "content": "# \\file TestNiftyReg.py\n#  \\brief  Class containing unit tests for module Stack\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date May 2016\n\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport unittest\nimport sys\nimport os\n\nimport pysitk.python_helper as ph\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.registration.niftyreg as nreg\nimport niftymic.base.stack as st\n\nfrom niftymic.definitions import DIR_TEST\n\n\nclass NiftyRegTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 7\n\n    def setUp(self):\n        pass\n\n    def test_affine_transform_reg_aladin(self):\n\n        # Read data\n        filename_fixed = \"stack1_rotated_angle_z_is_pi_over_10.nii.gz\"\n        filename_moving = \"FetalBrain_reconstruction_3stacks_myAlg.nii.gz\"\n\n        diff_ref = os.path.join(\n            DIR_TEST,  \"stack1_rotated_angle_z_is_pi_over_10_nreg_diff.nii.gz\")\n        moving = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_moving),\n        )\n        fixed = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_fixed)\n        )\n\n        # Set up NiftyReg\n        nifty_reg = nreg.RegAladin()\n        nifty_reg.set_fixed(fixed)\n        nifty_reg.set_moving(moving)\n        nifty_reg.set_registration_type(\"Rigid\")\n        nifty_reg.use_verbose(False)\n\n        # Register via NiftyReg\n        nifty_reg.run()\n\n        # Get associated results\n        affine_transform_sitk = nifty_reg.get_registration_transform_sitk()\n        moving_warped = nifty_reg.get_warped_moving()\n\n        # Get SimpleITK result with \"similar\" interpolator (NiftyReg does not\n        # state what interpolator is used but it seems to be BSpline)\n        moving_warped_sitk = sitk.Resample(\n            moving.sitk, fixed.sitk, affine_transform_sitk, sitk.sitkBSpline, 0.0, moving.sitk.GetPixelIDValue())\n\n        diff_res_sitk = moving_warped.sitk - moving_warped_sitk\n        sitkh.write_nifti_image_sitk(diff_res_sitk, diff_ref)\n        diff_ref_sitk = sitk.ReadImage(diff_ref)\n\n        res_diff_nda = sitk.GetArrayFromImage(diff_res_sitk - diff_ref_sitk)\n\n        self.assertAlmostEqual(\n            np.linalg.norm(res_diff_nda), 0, places=self.accuracy)\n"
  },
  {
    "path": "tests/parameter_normalization_test.py",
    "content": "##\n# \\file parameter_normalization_test.py\n#  \\brief  Class containing unit tests for module ParameterNormalization\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date Nov 2016\n\n\nimport os\nimport sys\nimport unittest\nimport numpy as np\nimport SimpleITK as sitk\n\n# Import modules\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.stack as st\nimport niftymic.utilities.parameter_normalization as pn\nimport niftymic.registration.intra_stack_registration as inplanereg\n\nfrom niftymic.definitions import DIR_TEST\n\n\n#\nclass ParameterNormalizationTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 6\n\n    def setUp(self):\n        pass\n\n    def test_parameter_normalization(self):\n\n        use_verbose = 0\n\n        filename_stack = \"FetalBrain_reconstruction_3stacks_myAlg\"\n        filename_stack_corrupted = \"FetalBrain_reconstruction_3stacks_myAlg_corrupted_inplane\"\n\n        stack_sitk = sitk.ReadImage(\n            os.path.join(self.dir_test_data, filename_stack + \".nii.gz\"))\n        stack_corrupted_sitk = sitk.ReadImage(\n            os.path.join(self.dir_test_data, filename_stack_corrupted + \".nii.gz\"))\n\n        stack_corrupted = st.Stack.from_sitk_image(\n            image_sitk=stack_corrupted_sitk,\n            filename=\"stack_corrupted\",\n            slice_thickness=stack_corrupted_sitk.GetSpacing()[-1],\n        )\n        stack = st.Stack.from_sitk_image(\n            image_sitk=sitk.Resample(stack_sitk, stack_corrupted.sitk),\n            filename=\"stack\",\n            slice_thickness=stack_corrupted.get_slice_thickness(),\n        )\n\n        # sitkh.show_stacks([stack, stack_corrupted])\n\n        inplane_registration = inplanereg.IntraStackRegistration(\n            stack_corrupted, stack)\n        inplane_registration.set_transform_initializer_type(\"moments\")\n        inplane_registration.set_intensity_correction_type_slice_neighbour_fit(\n            \"affine\")\n        inplane_registration.set_transform_type(\"rigid\")\n        inplane_registration._run_registration_pipeline_initialization()\n        parameters = inplane_registration.get_parameters()\n\n        # Normalization routine\n        parameters_tmp = np.array(parameters)\n        parameter_normalization = pn.ParameterNormalization(parameters_tmp)\n        parameter_normalization.compute_normalization_coefficients()\n\n        coefficients = parameter_normalization.get_normalization_coefficients()\n\n        # Check correct normalization\n        parameters_tmp = parameter_normalization.normalize_parameters(\n            parameters_tmp)\n\n        if use_verbose:\n            print(\"Normalization:\")\n        for i in range(0, parameters_tmp.shape[1]):\n            mean = np.mean(parameters_tmp[:, i])\n            std = np.std(parameters_tmp[:, i])\n\n            if use_verbose:\n                print(\"\\tmean = %.4f\" % (mean))\n                print(\"\\tstd = %.4f\" % (std))\n\n            # Check mean\n            self.assertEqual(np.round(\n                abs(mean), decimals=self.accuracy), 0)\n\n            # Check standard deviation\n            if abs(std) > 1e-8:\n                self.assertEqual(np.round(\n                    abs(std - 1), decimals=self.accuracy), 0)\n\n        # Check correct normalization\n        parameters_tmp = parameter_normalization.denormalize_parameters(\n            parameters_tmp)\n        if use_verbose:\n            print(\"\\nDenormalization:\")\n        for i in range(0, parameters_tmp.shape[1]):\n            mean = np.mean(parameters_tmp[:, i])\n            std = np.std(parameters_tmp[:, i])\n\n            if use_verbose:\n                print(\"\\tmean = %.4f\" % (mean))\n                print(\"\\tstd = %.4f\" % (std))\n\n            # Check mean\n            self.assertEqual(np.round(\n                abs(mean - coefficients[0, i]), decimals=self.accuracy), 0)\n\n            # Check standard deviation\n            if abs(std) > 1e-8:\n                self.assertEqual(np.round(\n                    abs(std - coefficients[1, i]), decimals=self.accuracy), 0)\n\n            # Check parameter values\n            self.assertEqual(np.round(\n                np.linalg.norm(parameters_tmp - parameters), decimals=self.accuracy), 0)\n"
  },
  {
    "path": "tests/registration_test.py",
    "content": "# \\file TestRegistration.py\n#  \\brief  Class containing unit tests for module Stack\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date September 2016\n\n\nimport unittest\n\n# Import libraries\nimport SimpleITK as sitk\nimport itk\nimport numpy as np\nimport os\n\n# Import modules\nimport niftymic.base.stack as st\nimport niftymic.prototyping.registration as myreg\nimport pysitk.python_helper as ph\nfrom niftymic.definitions import DIR_TEST\n\n# Pixel type of used 3D ITK image\nPIXEL_TYPE = itk.D\n\n# ITK image type\nIMAGE_TYPE = itk.Image[PIXEL_TYPE, 3]\nIMAGE_TYPE_CV33 = itk.Image.CVD33\nIMAGE_TYPE_CV183 = itk.Image.CVD183\nIMAGE_TYPE_CV363 = itk.Image.CVD363\n\n\nclass RegistrationTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = os.path.join(DIR_TEST, \"registration/\")\n\n    accuracy = 2\n\n    def setUp(self):\n        # Set print options for numpy\n        np.set_printoptions(precision=3)\n\n    \"\"\"\n    def test_GradientEuler3DTransformImageFilter(self):\n\n        filename_HRVolume = \"HRVolume\"\n\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\")\n        )\n\n        DOF_transform = 6\n        parameters = np.random.rand(\n            DOF_transform)*(2*np.pi, 2*np.pi, 2*np.pi, 10, 10, 10)\n\n        itk2np = itk.PyBuffer[IMAGE_TYPE]\n        itk2np_CVD33 = itk.PyBuffer[IMAGE_TYPE_CV33]\n        itk2np_CVD183 = itk.PyBuffer[IMAGE_TYPE_CV183]\n\n        # Create Euler3DTransform and update with random parameters\n        transform_itk = itk.Euler3DTransform.New()\n        parameters_itk = transform_itk.GetParameters()\n        sitkh.update_itk_parameters(parameters_itk, parameters)\n        transform_itk.SetParameters(parameters_itk)\n        # sitkh.print_itk_array(parameters_itk)\n\n        # ---------------------------------------------------------------------\n        filter_gradient_transform = itk.GradientEuler3DTransformImageFilter[\n            IMAGE_TYPE, PIXEL_TYPE, PIXEL_TYPE].New()\n        filter_gradient_transform.SetInput(HR_volume.itk)\n        filter_gradient_transform.SetTransform(transform_itk)\n        time_start = ph.start_timing()  # Above is required only once in Registration\n\n        filter_gradient_transform.Update()\n        gradient_transform_itk = filter_gradient_transform.GetOutput()\n        # Get data array of Jacobian of transform w.r.t. parameters  and\n        # reshape to N_HR_volume_voxels x DIMENSION x DOF\n        nda_gradient_transform_1 = itk2np_CVD183.GetArrayFromImage(\n            gradient_transform_itk).reshape(-1, 3, DOF_transform)\n\n        print(\"GradientEuler3DTransformImageFilter: \" +\n              str(ph.stop_timing(time_start)))\n\n        # ---------------------------------------------------------------------\n        time_start = ph.start_timing()\n\n        nda_gradient_transform_2 = sitkh.get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image(\n            transform_itk, HR_volume.sitk)\n\n        print(\"get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image: \" +\n              str(ph.stop_timing(time_start)))\n\n        # ---------------------------------------------------------------------\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_gradient_transform_2-nda_gradient_transform_1), decimals=6), 0)\n\n    def test_GradientAffine3DTransformImageFilter(self):\n\n        filename_HRVolume = \"HRVolume\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\")\n        )\n\n        DOF_transform = 12\n        parameters = np.random.rand(DOF_transform)*10\n\n        itk2np = itk.PyBuffer[IMAGE_TYPE]\n        itk2np_CVD33 = itk.PyBuffer[IMAGE_TYPE_CV33]\n        itk2np_CVD363 = itk.PyBuffer[IMAGE_TYPE_CV363]\n\n        # Create Euler3DTransform and update with random parameters\n        transform_itk = itk.AffineTransform[PIXEL_TYPE, 3].New()\n        parameters_itk = transform_itk.GetParameters()\n        sitkh.update_itk_parameters(parameters_itk, parameters)\n        transform_itk.SetParameters(parameters_itk)\n        # sitkh.print_itk_array(parameters_itk)\n\n        # ---------------------------------------------------------------------\n        filter_gradient_transform = itk.GradientAffine3DTransformImageFilter[\n            IMAGE_TYPE, PIXEL_TYPE, PIXEL_TYPE].New()\n        filter_gradient_transform.SetInput(HR_volume.itk)\n        filter_gradient_transform.SetTransform(transform_itk)\n        time_start = ph.start_timing()  # Above is required only once in Registration\n\n        filter_gradient_transform.Update()\n        gradient_transform_itk = filter_gradient_transform.GetOutput()\n        # Get data array of Jacobian of transform w.r.t. parameters  and\n        # reshape to N_HR_volume_voxels x DIMENSION x DOF\n        nda_gradient_transform_1 = itk2np_CVD363.GetArrayFromImage(\n            gradient_transform_itk).reshape(-1, 3, DOF_transform)\n\n        print(\"GradientAffine3DTransformImageFilter: \" +\n              str(ph.stop_timing(time_start)))\n\n        # ---------------------------------------------------------------------\n        time_start = ph.start_timing()\n\n        nda_gradient_transform_2 = sitkh.get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image(\n            transform_itk, HR_volume.sitk)\n\n        print(\"get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image: \" +\n              str(ph.stop_timing(time_start)))\n\n        # ---------------------------------------------------------------------\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_gradient_transform_2-nda_gradient_transform_1), decimals=6), 0)\n\n    def test_reshaping_of_structures(self):\n\n        # filename_prefix = \"RigidTransform_\"\n        filename_prefix = \"TranslationOnly_\"\n\n        filename_HRVolume = \"HRVolume\"\n        filename_StackSim = filename_prefix + \"StackSimulated\"\n        filename_transforms_prefix = filename_prefix + \"TransformGroundTruth_slice\"\n\n        stack_sim = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_StackSim + \".nii.gz\")\n        )\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\")\n        )\n\n        slices_sim = stack_sim.get_slices()\n        N_slices = len(slices_sim)\n\n        itk2np = itk.PyBuffer[itk.Image.D3]\n        itk2np_CVD33 = itk.PyBuffer[itk.Image.CVD33]\n\n        filter_OrientedGaussian_3D = itk.OrientedGaussianInterpolateImageFilter[\n            IMAGE_TYPE, IMAGE_TYPE].New()\n        filter_OrientedGaussian_3D.SetInput(HR_volume.itk)\n        filter_OrientedGaussian_3D.SetUseJacobian(True)\n\n        for j in range(0, N_slices):\n            slice = slices_sim[j]\n\n            filter_OrientedGaussian_3D.SetOutputParametersFromImage(slice.itk)\n            filter_OrientedGaussian_3D.UpdateLargestPossibleRegion()\n            filter_OrientedGaussian_3D.Update()\n\n            slice_simulated_nda = itk2np.GetArrayFromImage(\n                filter_OrientedGaussian_3D.GetOutput())\n            dslice_simulated_nda = itk2np_CVD33.GetArrayFromImage(\n                filter_OrientedGaussian_3D.GetJacobian())\n\n            shape = slice_simulated_nda.shape\n\n            slice_simulated_nda_flat = slice_simulated_nda.flatten()\n            dslice_simulated_nda_flat = dslice_simulated_nda.reshape(-1, 3)\n\n            array0 = np.zeros(3)\n            array1 = np.zeros(3)\n            abs_diff = 0\n\n            iter = 0\n            for i in range(0, shape[0]):\n                for j in range(0, shape[1]):\n                    for k in range(0, shape[2]):\n                        array0 = slice_simulated_nda[\n                            i, j, k] - dslice_simulated_nda[i, j, k, :]\n                        array1 = slice_simulated_nda_flat[\n                            iter] - dslice_simulated_nda_flat[iter, :]\n                        abs_diff += np.linalg.norm(array0-array1)\n                        iter += 1\n\n            self.assertEqual(np.round(\n                abs_diff, decimals=self.accuracy), 0)\n\n    def test_vectorization_of_dImage_times_dT(self):\n\n        shape = np.array([200, 200])\n        slice_nda = np.random.rand(shape[0], shape[1])*255\n\n        slice_sitk = sitk.GetImageFromArray(slice_nda)\n        N_voxels = shape.prod()\n\n        gradient_image_filter_sitk = sitk.GradientImageFilter()\n        dslice_sitk = gradient_image_filter_sitk.Execute(slice_sitk)\n\n        # Reshape to (N_slice_voxels x dim)-array\n        dslice_nda = sitk.GetArrayFromImage(dslice_sitk).reshape(-1, 2)\n\n        euler_sitk = sitk.Euler2DTransform()\n        euler_itk = itk.Euler2DTransform.New()\n\n        parameters_sitk = (0.5, -4, 10)\n        euler_sitk.SetParameters(parameters_sitk)\n        euler_itk.SetParameters(itk.OptimizerParameters[\n                                itk.D](parameters_sitk))\n\n        # Get d[T(theta, x)]/dtheta as (N_slice_voxels x dim x\n        # transform_type_dofs)\n        dT_nda = sitkh.get_numpy_array_of_jacobian_itk_transform_applied_on_sitk_image(\n            euler_itk, slice_sitk)\n\n        jacobian = np.zeros((N_voxels, euler_itk.GetNumberOfParameters()))\n\n        time0 = ph.start_timing()\n        for i in range(0, N_voxels):\n            jacobian[i, :] = dslice_nda[i, :].dot(dT_nda[i, :, :])\n        print(\"For-loop:   \" + str(ph.stop_timing(time0)))\n\n        time0 = ph.start_timing()\n        jacobian_2 = np.sum((dslice_nda[:, :, np.newaxis]*dT_nda), axis=1)\n        print(\"Vectorized: \" + str(ph.stop_timing(time0)))\n\n        self.assertEqual(np.round(\n            np.linalg.norm(jacobian - jacobian_2), decimals=8), 0)\n    \"\"\"\n\n    def test_translation_registration_of_slices(self):\n\n        filename_prefix = \"TranslationOnly_\"\n        # filename_prefix = \"RigidTransform_\"\n\n        filename_HRVolume = \"HRVolume\"\n        filename_StackSim = filename_prefix + \"StackSimulated\"\n        filename_transforms_prefix = filename_prefix + \"TransformGroundTruth_slice\"\n        stack_sim = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_StackSim + \".nii.gz\"))\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\"))\n\n        slices_sim = stack_sim.get_slices()\n        N_slices = len(slices_sim)\n\n        scale = np.array(\n            [180. / np.pi, 180. / np.pi, 180. / np.pi, 1., 1., 1.])\n        time_start = ph.start_timing()\n\n        for j in range(0, N_slices):\n            # for j in range(20, N_slices):\n            rigid_transform_groundtruth_sitk = sitk.ReadTransform(\n                self.dir_test_data + filename_transforms_prefix + str(j) + \".tfm\")\n            parameters_gd = np.array(\n                rigid_transform_groundtruth_sitk.GetParameters())\n\n            angle_max = 5. * np.pi / 180.\n            t_max = 5.\n            registration = myreg.Registration(\n                fixed=slices_sim[j], moving=HR_volume,\n                # initializer_type=\"SelfGEOMETRY\",\n                use_verbose=0,\n                # data_loss=\"soft_l1\",\n                # x_scale=[angle_max, angle_max, angle_max, t_max, t_max, t_max],\n            )\n            registration.run_registration()\n            # registration.print_statistics()\n\n            # Check parameters\n            transform_sitk = registration.get_registration_transform_sitk()\n            parameters = np.array(transform_sitk.GetParameters())\n            norm_diff = np.linalg.norm(parameters-parameters_gd)\n\n            params = parameters * scale\n            params_gt = parameters_gd * scale\n            print(\"Slice %s/%s: |parameters-parameters_gd| = %s\" %\n                  (j, N_slices-1, str(norm_diff)))\n            print(\"\\tEst:  \" + str(params) + \" (deg/mmm)\")\n            print(\"\\tGT:   \" + str(params_gt) + \" (deg/mmm)\")\n            print(\"\\tDiff: \" + str(params - params_gt) + \" (deg/mmm)\")\n\n            self.assertEqual(np.round(\n                norm_diff, decimals=self.accuracy), 0)\n\n        # Set elapsed time\n        print(\"Translation: \" + str(ph.stop_timing(time_start)))\n\n    \"\"\"\n    def test_rigid_registration_of_slices(self):\n\n        filename_prefix = \"RigidTransform_\"\n\n        filename_HRVolume = \"HRVolume\"\n        filename_StackSim = filename_prefix + \"StackSimulated\"\n        filename_transforms_prefix = filename_prefix + \"TransformGroundTruth_slice\"\n        stack_sim = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_StackSim + \".nii.gz\"))\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\"))\n\n        slices_sim = stack_sim.get_slices()\n        N_slices = len(slices_sim)\n\n        time_start = time.time()\n\n        for j in range(0, N_slices):\n            rigid_transform_groundtruth_sitk = sitk.ReadTransform(\n                self.dir_test_data + filename_transforms_prefix + str(j) + \".tfm\")\n            parameters_gd = np.array(\n                rigid_transform_groundtruth_sitk.GetParameters())\n\n            registration = myreg.Registration(\n                fixed=slices_sim[j], moving=HR_volume)\n            registration.run_registration()\n            # registration.print_statistics()\n\n            # Check parameters\n            parameters = registration.get_parameters()\n\n            norm_diff = np.linalg.norm(parameters-parameters_gd)\n            # print(\"Slice %s/%s: |parameters-parameters_gd| = %s\" %(j, N_slices-1, str(norm_diff)) )\n\n            self.assertEqual(np.round(\n                norm_diff, decimals=self.accuracy), 0)\n\n        # Set elapsed time\n        time_end = time.time()\n        # print(\"Rigid registration test: Elapsed time = %s\" %(timedelta(seconds=time_end-time_start)))\n\n    def test_rigid_registration_of_stack(self):\n        filename_prefix = \"NoMotion_\"\n        parameters_gd = (0.1, 0.1, 0.2, -1, 3, 2)\n\n        filename_HRVolume = \"HRVolume\"\n        filename_StackSim = filename_prefix + \"StackSimulated\"\n        stack_sim = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_StackSim + \".nii.gz\"))\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HRVolume + \".nii.gz\"))\n\n        # Apply motion\n        transform_sitk = sitk.Euler3DTransform()\n        transform_sitk.SetParameters(parameters_gd)\n        stack_sitk = sitkh.get_transformed_sitk_image(\n            stack_sim.sitk, transform_sitk)\n        stack_sitk_mask = sitkh.get_transformed_sitk_image(\n            stack_sim.sitk_mask, transform_sitk)\n        stack_sim = st.Stack.from_sitk_image(\n            stack_sitk, filename=stack_sim.get_filename(), image_sitk_mask=stack_sitk_mask)\n\n        # PSF-aware Registration algorithm\n        time_start = time.time()\n        registration = myreg.Registration(\n            fixed=stack_sim,\n            moving=HR_volume,\n            use_verbose=True,\n            # data_loss=\"huber\",\n            # minimizer=\"L-BFGS-B\",\n            # minimizer=\"Newton-CG\",\n        )\n        # registration.use_fixed_mask(True)\n        registration.use_verbose(True)\n        registration.run_registration()\n\n        # Check parameters (should be the negative of parameters_gd)\n        parameters = registration.get_parameters()\n        norm_diff = np.linalg.norm(parameters+parameters_gd)\n        print(\"parameters = \" + str(parameters))\n        print(\"|parameters-parameters_gd| = %s\" % (str(norm_diff)))\n\n        self.assertEqual(np.round(\n            norm_diff*0.1, decimals=0), 0)\n\n        # Set elapsed time\n        time_end = time.time()\n        print(\"Rigid registration test: Elapsed time = %s\" %\n              (registration.get_computational_time()))\n\n        # Comparison with ITK registrations\n        scales_estimator = \"PhysicalShift\"\n        use_verbose = 1\n\n        # ----------------SimpleITK registration for comparison----------------\n        ph.print_title(\"SimpleITK registration for comparison:\")\n        registration = regsitk.SimpleItkRegistration(\n            fixed=stack_sim,\n            moving=HR_volume,\n            optimizer=\"RegularStepGradientDescent\",\n            optimizer_params={\n                \"minStep\": 1e-6,\n                \"numberOfIterations\": 500,\n                \"gradientMagnitudeTolerance\": 1e-6,\n                \"learningRate\": 1,\n            },\n        )\n        registration.set_metric(\"MeanSquares\")\n        registration.use_verbose(use_verbose)\n        registration.set_scales_estimator(scales_estimator)\n        registration.run_registration()\n\n        parameters = np.array(\n            registration.get_registration_transform_sitk().GetParameters())\n        norm_diff = np.linalg.norm(parameters+parameters_gd)\n        print(\"\\tparameters = \" + str(parameters))\n        print(\"\\t|parameters-parameters_gd| = %s\" % (str(norm_diff)))\n        print(\"\\tRigid registration test: Elapsed time = %s\" %\n              (registration.get_computational_time()))\n    \"\"\"\n"
  },
  {
    "path": "tests/residual_evaluator_test.py",
    "content": "##\n# \\file residual_evaluator_test.py\n#  \\brief  Test ResidualEvaluator class\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date November 2017\n\n\nimport os\nimport unittest\nimport numpy as np\nimport re\nimport SimpleITK as sitk\n\nimport pysitk.python_helper as ph\n\nimport niftymic.base.stack as st\nimport niftymic.validation.residual_evaluator as res_ev\nimport niftymic.base.exceptions as exceptions\nfrom niftymic.definitions import DIR_TMP, DIR_TEST\n\n\nclass ResidualEvaluatorTest(unittest.TestCase):\n\n    def setUp(self):\n        self.precision = 7\n        self.dir_tmp = os.path.join(DIR_TMP, \"residual_evaluator\")\n\n    def test_compute_write_read_slice_similarities(self):\n\n        paths_to_stacks = [\n            os.path.join(\n                DIR_TEST, \"fetal_brain_%d.nii.gz\" % d) for d in range(0, 3)\n        ]\n        path_to_reference = os.path.join(\n            DIR_TEST, \"FetalBrain_reconstruction_3stacks_myAlg.nii.gz\")\n\n        stacks = [\n            st.Stack.from_filename(p, ph.append_to_filename(p, \"_mask\"))\n            for p in paths_to_stacks\n        ]\n        reference = st.Stack.from_filename(\n            path_to_reference, extract_slices=False)\n\n        residual_evaluator = res_ev.ResidualEvaluator(stacks, reference)\n        residual_evaluator.compute_slice_projections()\n        residual_evaluator.evaluate_slice_similarities()\n        residual_evaluator.write_slice_similarities(self.dir_tmp)\n        slice_similarities = residual_evaluator.get_slice_similarities()\n\n        residual_evaluator1 = res_ev.ResidualEvaluator()\n        residual_evaluator1.read_slice_similarities(self.dir_tmp)\n        slice_similarities1 = residual_evaluator1.get_slice_similarities()\n\n        for stack_name in slice_similarities.keys():\n            for m in slice_similarities[stack_name].keys():\n                rho_res = slice_similarities[stack_name][m]\n                rho_res1 = slice_similarities1[stack_name][m]\n                error = np.linalg.norm(rho_res - rho_res1)\n                self.assertAlmostEqual(error, 0, places=self.precision)\n\n    def test_slice_projections_not_created(self):\n        paths_to_stacks = [\n            os.path.join(\n                DIR_TEST, \"fetal_brain_%d.nii.gz\" % d) for d in range(0, 1)\n        ]\n        path_to_reference = os.path.join(\n            DIR_TEST, \"FetalBrain_reconstruction_3stacks_myAlg.nii.gz\")\n\n        stacks = [\n            st.Stack.from_filename(p, ph.append_to_filename(p, \"_mask\"))\n            for p in paths_to_stacks\n        ]\n        reference = st.Stack.from_filename(\n            path_to_reference, extract_slices=False)\n\n        residual_evaluator = res_ev.ResidualEvaluator(stacks, reference)\n        self.assertRaises(exceptions.ObjectNotCreated, lambda:\n            residual_evaluator.evaluate_slice_similarities())"
  },
  {
    "path": "tests/run_tests.py",
    "content": "#!/usr/bin/python\n\n##\n# \\file run_tests.py\n# \\brief      main-file to run specified unit tests\n#\n# \\author     Michael Ebner (michael.ebner.14@ucl.ac.uk)\n# \\date       September 2015\n#\n\n\nimport unittest\n\nfrom brain_stripping_test import *\nfrom case_study_fetal_brain_test import *\nfrom case_study_rsfmri_test import *\nfrom data_reader_test import *\nfrom image_similarity_evaluator_test import *\nfrom intensity_correction_test import *\nfrom linear_operators_test import *\nfrom niftyreg_test import *\nfrom residual_evaluator_test import *\nfrom segmentation_propagation_test import *\n# from simulator_slice_acquisition_test import *  # only in dev branch\nfrom stack_test import *\n\n\n# from parameter_normalization_test import *\n# from registration_test import *  # TBC\n# from intra_stack_registration_test import *  # TBC\n\nif __name__ == '__main__':\n    print(\"\\nUnit tests:\\n--------------\")\n    unittest.main()\n"
  },
  {
    "path": "tests/segmentation_propagation_test.py",
    "content": "# \\file TestSegmentationPropagation.py\n#  \\brief  Class containing unit tests for module SegmentationPropagation\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date May 2017\n\n\nimport unittest\n\n# Import libraries\nimport SimpleITK as sitk\nimport numpy as np\nimport os\n\nimport pysitk.simple_itk_helper as sitkh\nimport niftymic.base.stack as st\nimport niftymic.registration.simple_itk_registration as regsitk\nimport niftymic.registration.niftyreg as nreg\nimport niftymic.utilities.segmentation_propagation as segprop\nfrom niftymic.definitions import DIR_TEST\n\n\nclass SegmentationPropagationTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 6\n\n    def setUp(self):\n        pass\n\n    def test_registration(self):\n\n        filename = \"fetal_brain_0\"\n\n        parameters_gd = (0.1, 0.2, -0.3, 0, 0, 0)\n        # parameters_gd = np.zeros(6)\n\n        template = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename + \"_mask.nii.gz\"),\n            extract_slices=False,\n        )\n\n        transform_sitk_gd = sitk.Euler3DTransform()\n        transform_sitk_gd.SetParameters(parameters_gd)\n\n        stack_sitk = sitkh.get_transformed_sitk_image(\n            template.sitk, transform_sitk_gd)\n\n        stack = st.Stack.from_sitk_image(\n            image_sitk=stack_sitk,\n            filename=\"stack\",\n            slice_thickness=template.get_slice_thickness(),\n        )\n\n        optimizer = \"RegularStepGradientDescent\"\n        optimizer_params = {\n            'learningRate': 1,\n            'minStep': 1e-6,\n            'numberOfIterations': 300\n        }\n\n        registration = regsitk.SimpleItkRegistration(\n            initializer_type=\"SelfGEOMETRY\",\n            use_verbose=True,\n            metric=\"MeanSquares\",\n            optimizer=optimizer,\n            optimizer_params=optimizer_params,\n            use_multiresolution_framework=False,\n        )\n\n        segmentation_propagation = segprop.SegmentationPropagation(\n            stack=stack,\n            template=template,\n            registration_method=registration,\n            dilation_radius=10,\n        )\n        segmentation_propagation.run_segmentation_propagation()\n        foo = segmentation_propagation.get_segmented_stack()\n\n        # Get transform and force center = 0\n        transform = segmentation_propagation.get_registration_transform_sitk()\n        transform = sitkh.get_composite_sitk_euler_transform(\n            transform, sitk.Euler3DTransform())\n        parameters = sitk.Euler3DTransform(\n            transform.GetInverse()).GetParameters()\n\n        self.assertEqual(np.round(\n            np.linalg.norm(np.array(parameters) - parameters_gd), decimals=4), 0)\n"
  },
  {
    "path": "tests/simulator_slice_acquisition_test.py",
    "content": "# \\file simulator_slice_acquisition_test.py\n#  \\brief  Class containing unit tests for module SimulatorSliceAcqusition\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date May 2016\n\n\n\nimport os\nimport itk\nimport unittest\nimport numpy as np\nimport SimpleITK as sitk\n\nimport pysitk.simple_itk_helper as sitkh\n\nimport niftymic.base.psf as psf\nimport niftymic.base.stack as st\nimport niftymic.prototyping.simulator_slice_acqusition as sa\nfrom niftymic.definitions import DIR_TEST\n\n# Pixel type of used 3D ITK image\npixel_type = itk.D\n\n# ITK image type\nimage_type = itk.Image[pixel_type, 3]\n\n\nclass SimulatorSliceAcqusitionTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n\n    accuracy = 7\n\n    def setUp(self):\n        pass\n\n    def test_conversion_image_direction(self):\n\n        filename_HR_volume = \"HR_volume_postmortem\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HR_volume + \".nii.gz\"),\n            os.path.join(self.dir_test_data,\n                         filename_HR_volume + \"_mask.nii.gz\")\n        )\n        # Get unit vectors defining image grid in physical space and construct\n        # direction matrix\n        origin_HR_volume = np.array(HR_volume.sitk.GetOrigin())\n        a_x = HR_volume.sitk.TransformIndexToPhysicalPoint(\n            (1, 0, 0)) - origin_HR_volume\n        a_y = HR_volume.sitk.TransformIndexToPhysicalPoint(\n            (0, 1, 0)) - origin_HR_volume\n        a_z = HR_volume.sitk.TransformIndexToPhysicalPoint(\n            (0, 0, 1)) - origin_HR_volume\n\n        e_x = a_x/np.linalg.norm(a_x)\n        e_y = a_y/np.linalg.norm(a_y)\n        e_z = a_z/np.linalg.norm(a_z)\n\n        direction_matrix_test = np.array([e_x, e_y, e_z]).transpose()\n        direction_test = direction_matrix_test.flatten()\n\n        # Get respective vectors from image direction\n        direction = np.array(HR_volume.sitk.GetDirection())\n\n        e_x_test = direction[0::3]\n        e_y_test = direction[1::3]\n        e_z_test = direction[2::3]\n\n        # Check correspondences\n        self.assertEqual(np.round(np.linalg.norm(\n            e_x_test - e_x), decimals=self.accuracy), 0)\n        self.assertEqual(np.round(np.linalg.norm(\n            e_y_test - e_y), decimals=self.accuracy), 0)\n        self.assertEqual(np.round(np.linalg.norm(\n            e_z_test - e_z), decimals=self.accuracy), 0)\n\n        self.assertEqual(np.round(np.linalg.norm(\n            direction - direction_test), decimals=self.accuracy), 0)\n\n    # Test whether the slices, and hence stacks, are correctly simulated\n    #  in each orthogonal direction by assuming no subject motion\n    def test_run_simulation_view(self):\n\n        filename_HR_volume = \"HR_volume_postmortem\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HR_volume + \".nii.gz\"),\n            os.path.join(self.dir_test_data,\n                         filename_HR_volume + \"_mask.nii.gz\")\n        )\n\n        # 1) Test for Nearest Neighbor Interpolator\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"NearestNeighbor\")\n        slice_acquistion.set_motion_type(\"NoMotion\")\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n\n        for i in range(0, len(stacks_simulated)):\n            HR_volume_resampled_sitk = sitk.Resample(\n                HR_volume.sitk, stacks_simulated[i].sitk, sitk.Euler3DTransform(\n                ), sitk.sitkNearestNeighbor, 0.0, stacks_simulated[i].sitk.GetPixelIDValue()\n            )\n\n        self.assertEqual(np.round(\n            np.linalg.norm(sitk.GetArrayFromImage(stacks_simulated[i].sitk - HR_volume_resampled_sitk)), decimals=self.accuracy), 0)\n\n        # 2) Test for Linear Interpolator\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"Linear\")\n        slice_acquistion.set_motion_type(\"NoMotion\")\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n\n        for i in range(0, len(stacks_simulated)):\n            HR_volume_resampled_sitk = sitk.Resample(\n                HR_volume.sitk, stacks_simulated[i].sitk, sitk.Euler3DTransform(\n                ), sitk.sitkLinear, 0.0, stacks_simulated[i].sitk.GetPixelIDValue()\n            )\n\n        self.assertEqual(np.round(\n            np.linalg.norm(sitk.GetArrayFromImage(stacks_simulated[i].sitk - HR_volume_resampled_sitk)), decimals=self.accuracy), 0)\n\n        # 3) Test for Oriented Gaussian Interpolator\n        alpha_cut = 3\n\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"OrientedGaussian\")\n        slice_acquistion.set_motion_type(\"NoMotion\")\n        slice_acquistion.set_alpha_cut(alpha_cut)\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n\n        resampler = itk.ResampleImageFilter[image_type, image_type].New()\n        resampler.SetDefaultPixelValue(0.0)\n        resampler.SetInput(HR_volume.itk)\n\n        interpolator = itk.OrientedGaussianInterpolateImageFunction[\n            image_type, pixel_type].New()\n        interpolator.SetAlpha(alpha_cut)\n        resampler.SetInterpolator(interpolator)\n\n        PSF = psf.PSF()\n\n        for i in range(0, len(stacks_simulated)):\n            resampler.SetOutputParametersFromImage(stacks_simulated[i].itk)\n\n            # Set covariance based on oblique PSF\n            Cov_HR_coord = PSF.get_covariance_matrix_in_reconstruction_space(\n                stacks_simulated[i], HR_volume)\n            interpolator.SetCovariance(Cov_HR_coord.flatten())\n\n            resampler.UpdateLargestPossibleRegion()\n            resampler.Update()\n\n            HR_volume_resampled_itk = resampler.GetOutput()\n            HR_volume_resampled_sitk = sitkh.get_sitk_from_itk_image(\n                HR_volume_resampled_itk)\n\n        self.assertEqual(np.round(\n            np.linalg.norm(sitk.GetArrayFromImage(stacks_simulated[i].sitk - HR_volume_resampled_sitk)), decimals=self.accuracy), 0)\n\n    # Test whether the ground truth affine transforms set during the\n    #  simulation correspond to the actually acquired positions within the\n    #  sliced volume whereby no motion is applied to the HR volume\n    def test_ground_truth_affine_transforms_no_motion(self):\n\n        filename_HR_volume = \"HR_volume_postmortem\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HR_volume + \".nii.gz\"),\n            os.path.join(self.dir_test_data,\n                         filename_HR_volume + \"_mask.nii.gz\")\n        )\n\n        # Run simulation for Nearest Neighbor interpolation (shouldn't not\n        # matter anyway and is quicker)\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"NearestNeighbor\")\n        slice_acquistion.set_motion_type(\"NoMotion\")\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        # Get simulated stack of slices + corresponding ground truth affine\n        #  transforms indicating the correct acquisition of the slice\n        #  within the volume\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n        affine_transforms_ground_truth, rigid_motion_transforms_ground_truth = slice_acquistion.get_ground_truth_data()\n\n        for i in range(0, len(stacks_simulated)):\n            stack = st.Stack.from_stack(stacks_simulated[i])\n\n            slices = stack.get_slices()\n            N_slices = stack.get_number_of_slices()\n\n            for j in range(0, N_slices):\n                # print(\"Stack %s: Slice %s/%s\" %(i,j,N_slices-1))\n                slice = slices[j]\n                # slice.update_motion_correction(rigid_motion_transforms_ground_truth[i][j])\n\n                HR_volume_resampled_slice_sitk = sitk.Resample(\n                    HR_volume.sitk, slice.sitk, sitk.Euler3DTransform(\n                    ), sitk.sitkNearestNeighbor, 0.0, slice.sitk.GetPixelIDValue()\n                )\n\n                self.assertEqual(np.round(\n                    np.linalg.norm(sitk.GetArrayFromImage(slice.sitk - HR_volume_resampled_slice_sitk)), decimals=self.accuracy), 0)\n\n    # Test whether the ground truth affine transforms set during the\n    #  simulation correspond to the actually acquired positions within the\n    #  sliced volume whereby motion is applied to the HR volume\n    def test_ground_truth_affine_transforms_with_motion_NearestNeighbor(self):\n\n        filename_HR_volume = \"HR_volume_postmortem\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HR_volume + \".nii.gz\"),\n            os.path.join(self.dir_test_data,\n                         filename_HR_volume + \"_mask.nii.gz\")\n        )\n\n        # Run simulation for Nearest Neighbor interpolation (shouldn't not\n        # matter anyway and is quicker)\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"NearestNeighbor\")\n        slice_acquistion.set_motion_type(\"Random\")\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        # Get simulated stack of slices + corresponding ground truth affine\n        #  transforms indicating the correct acquisition of the slice\n        #  within the volume\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n        affine_transforms_ground_truth, rigid_motion_transforms_ground_truth = slice_acquistion.get_ground_truth_data()\n\n        for i in range(0, len(stacks_simulated)):\n            stack = stacks_simulated[i]\n\n            slices = stack.get_slices()\n            N_slices = stack.get_number_of_slices()\n\n            for j in range(0, N_slices):\n                # print(\"Stack %s: Slice %s/%s\" %(i,j,N_slices-1))\n                slice = slices[j]\n                slice.update_motion_correction(\n                    rigid_motion_transforms_ground_truth[i][j])\n\n                HR_volume_resampled_slice_sitk = sitk.Resample(\n                    HR_volume.sitk, slice.sitk, sitk.Euler3DTransform(\n                    ), sitk.sitkNearestNeighbor, 0.0, slice.sitk.GetPixelIDValue()\n                )\n\n                self.assertEqual(np.round(\n                    np.linalg.norm(sitk.GetArrayFromImage(slice.sitk - HR_volume_resampled_slice_sitk)), decimals=self.accuracy), 0)\n\n    # Test whether the ground truth affine transforms set during the\n    #  simulation correspond to the actually acquired positions within the\n    #  sliced volume whereby motion is applied to the HR volume\n    def test_ground_truth_affine_transforms_with_motion_OrientedGaussian(self):\n\n        filename_HR_volume = \"HR_volume_postmortem\"\n        HR_volume = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename_HR_volume + \".nii.gz\"),\n            os.path.join(self.dir_test_data,\n                         filename_HR_volume + \"_mask.nii.gz\")\n        )\n\n        alpha_cut = 3\n\n        # Run simulation for Oriented Gaussian interpolation, hence more\n        # \"realistic\" case\n        slice_acquistion = sa.SliceAcqusition(HR_volume)\n        slice_acquistion.set_interpolator_type(\"OrientedGaussian\")\n        # slice_acquistion.set_interpolator_type(\"NearestNeighbor\")\n        # slice_acquistion.set_interpolator_type(\"Linear\")\n        slice_acquistion.set_motion_type(\"Random\")\n        # slice_acquistion.set_motion_type(\"NoMotion\")\n        slice_acquistion.set_alpha_cut(alpha_cut)\n\n        slice_acquistion.run_simulation_view_1()\n        slice_acquistion.run_simulation_view_2()\n        slice_acquistion.run_simulation_view_3()\n\n        # Get simulated stack of slices + corresponding ground truth affine\n        #  transforms indicating the correct acquisition of the slice\n        #  within the volume\n        stacks_simulated = slice_acquistion.get_simulated_stacks()\n        affine_transforms_ground_truth, rigid_motion_transforms_ground_truth = slice_acquistion.get_ground_truth_data()\n\n        resampler = itk.ResampleImageFilter[image_type, image_type].New()\n        resampler.SetDefaultPixelValue(0.0)\n        resampler.SetInput(HR_volume.itk)\n\n        interpolator = itk.OrientedGaussianInterpolateImageFunction[\n            image_type, pixel_type].New()\n        interpolator.SetAlpha(alpha_cut)\n        # interpolator = itk.LinearInterpolateImageFunction[image_type, pixel_type].New()\n        # interpolator = itk.NearestNeighborInterpolateImageFunction[image_type, pixel_type].New()\n        resampler.SetInterpolator(interpolator)\n\n        PSF = psf.PSF()\n\n        for i in range(0, len(stacks_simulated)):\n            stack = stacks_simulated[i]\n\n            slices = stack.get_slices()\n            N_slices = stack.get_number_of_slices()\n\n            for j in range(0, N_slices):\n                # print(\"Stack %s: Slice %s/%s\" %(i,j,N_slices-1))\n                slice = slices[j]\n                slice.update_motion_correction(\n                    rigid_motion_transforms_ground_truth[i][j])\n\n                # Get covariance based on oblique PSF\n                Cov_HR_coord = PSF.get_covariance_matrix_in_reconstruction_space(\n                    slice, HR_volume)\n\n                # Update resampler\n                interpolator.SetCovariance(Cov_HR_coord.flatten())\n                resampler.SetOutputParametersFromImage(slice.itk)\n                resampler.UpdateLargestPossibleRegion()\n                resampler.Update()\n\n                HR_volume_resampled_slice_itk = resampler.GetOutput()\n                HR_volume_resampled_slice_sitk = sitkh.get_sitk_from_itk_image(\n                    HR_volume_resampled_slice_itk)\n\n                # HR_volume_resampled_slice_sitk = sitk.Resample(\n                #     HR_volume.sitk, slice.sitk, sitk.Euler3DTransform(), sitk.sitkNearestNeighbor, 0.0, slice.sitk.GetPixelIDValue()\n                #     )\n\n                norm_diff = np.linalg.norm(sitk.GetArrayFromImage(\n                    slice.sitk - HR_volume_resampled_slice_sitk))\n                # try:\n                self.assertEqual(\n                    np.round(norm_diff, decimals=self.accuracy), 0)\n                # except:\n                #     print(\"Stack %s: Slice %s/%s\" %(i,j,N_slices-1))\n\n                #     print(norm_diff)\n"
  },
  {
    "path": "tests/stack_test.py",
    "content": "# \\file TestStack.py\n#  \\brief  Class containing unit tests for module Stack\n#\n#  \\author Michael Ebner (michael.ebner.14@ucl.ac.uk)\n#  \\date December 2015\n\n\nimport SimpleITK as sitk\nimport numpy as np\nimport unittest\nimport random\nimport os\n\nimport niftymic.base.stack as st\nimport niftymic.base.data_reader as dr\nimport niftymic.base.exceptions as exceptions\nimport niftymic.validation.motion_simulator as ms\n\nfrom niftymic.definitions import DIR_TEST, DIR_TMP\n\n\nclass StackTest(unittest.TestCase):\n\n    # Specify input data\n    dir_test_data = DIR_TEST\n    dir_test_data_io = os.path.join(DIR_TEST, \"IO\")\n\n    accuracy = 7\n\n    def setUp(self):\n        pass\n\n    def test_get_resampled_stack_from_slices(self):\n\n        filename = \"stack0\"\n\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data, filename + \".nii.gz\"),\n            os.path.join(self.dir_test_data, filename + \"_mask.nii.gz\")\n        )\n\n        nda_stack = sitk.GetArrayFromImage(stack.sitk)\n        nda_stack_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n\n        # Resample stack based on slices\n        stack_resampled_from_slice = stack.get_resampled_stack_from_slices()\n\n        # Check alignment of image\n        nda_stack_resampled = sitk.GetArrayFromImage(\n            stack_resampled_from_slice.sitk)\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_stack - nda_stack_resampled), decimals=self.accuracy), 0)\n\n        # Check alignment of image mask\n        nda_stack_resampled_mask = sitk.GetArrayFromImage(\n            stack_resampled_from_slice.sitk_mask)\n        self.assertEqual(np.round(\n            np.linalg.norm(nda_stack_mask - nda_stack_resampled_mask), decimals=self.accuracy), 0)\n\n    def test_io_image_not_existent(self):\n        # Neither fetal_brain_2.nii.gz nor fetal_brain_2.nii exists\n        filename = \"fetal_brain_2\"\n        self.assertRaises(exceptions.FileNotExistent, lambda:\n                          st.Stack.from_filename(\n                              os.path.join(self.dir_test_data_io,\n                                           filename + \".nii.gz\"))\n                          )\n        self.assertRaises(exceptions.FileNotExistent, lambda:\n                          st.Stack.from_filename(\n                              os.path.join(self.dir_test_data_io,\n                                           filename + \".nii\"))\n                          )\n\n    def test_io_image_and_mask_1(self):\n        # Read *.nii + *_mask.nii\n\n        filename = \"fetal_brain_3\"\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data_io, filename + \".nii\"),\n            os.path.join(self.dir_test_data_io, filename + \"_mask.nii\")\n        )\n\n        # If everything was correctly read the mask will have zeros and ones\n        # in mask\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        self.assertEqual(nda_mask.prod(), 0)\n\n    def test_io_image_and_mask_2(self):\n        # Read *.nii + *_mask.nii.gz\n\n        filename = \"fetal_brain_4\"\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data_io, filename + \".nii\"),\n            os.path.join(self.dir_test_data_io, filename + \"_mask.nii.gz\")\n        )\n\n        # If everything was correctly read the mask will have zero and ones\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        self.assertEqual(nda_mask.prod(), 0)\n\n    def test_io_image_and_mask_3(self):\n        # Read *.nii.gz + *_mask.nii\n\n        filename = \"fetal_brain_5\"\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data_io, filename + \".nii.gz\"),\n            os.path.join(self.dir_test_data_io, filename + \"_mask.nii\")\n        )\n\n        # If everything was correctly read the mask will have zero and ones\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        self.assertEqual(nda_mask.prod(), 0)\n\n    def test_io_image_and_mask_4(self):\n        # Read *.nii.gz + *_mask.nii.gz\n\n        filename = \"fetal_brain_6\"\n        stack = st.Stack.from_filename(\n            os.path.join(self.dir_test_data_io, filename + \".nii.gz\"),\n            os.path.join(self.dir_test_data_io, filename + \"_mask.nii.gz\")\n        )\n\n        # If everything was correctly read the mask will have zero and ones\n        nda_mask = sitk.GetArrayFromImage(stack.sitk_mask)\n        self.assertEqual(nda_mask.prod(), 0)\n\n    def test_update_write_transform(self):\n\n        motion_simulator = ms.RandomRigidMotionSimulator(\n            dimension=3,\n            angle_max_deg=20,\n            translation_max=30)\n\n        filenames = [\"fetal_brain_%d\" % d for d in range(3)]\n        stacks = [st.Stack.from_filename(\n            os.path.join(self.dir_test_data, \"%s.nii.gz\" % f))\n            for f in filenames]\n\n        # Generate random motions for all slices of each stack\n        motions_sitk = {f: {} for f in filenames}\n        for i, stack in enumerate(stacks):\n            motion_simulator.simulate_motion(\n                seed=i, simulations=stack.get_number_of_slices())\n            motions_sitk[stack.get_filename()] = \\\n                motion_simulator.get_transforms_sitk()\n\n        # Apply random motion to all slices of all stacks\n        dir_output = os.path.join(DIR_TMP, \"test_update_write_transform\")\n        for i, stack in enumerate(stacks):\n            for j, slice in enumerate(stack.get_slices()):\n                slice.update_motion_correction(\n                    motions_sitk[stack.get_filename()][j])\n\n            # Write stacks to directory\n            stack.write(dir_output, write_slices=True, write_transforms=True)\n\n        # Read written stacks/slices/transformations\n        data_reader = dr.ImageSlicesDirectoryReader(\n            dir_output)\n        data_reader.read_data()\n        stacks_2 = data_reader.get_data()\n\n        data_reader = dr.SliceTransformationDirectoryReader(\n            dir_output)\n        data_reader.read_data()\n        transformations_dic = data_reader.get_data()\n\n        filenames_2 = [s.get_filename() for s in stacks_2]\n        for i, stack in enumerate(stacks):\n            stack_2 = stacks_2[filenames_2.index(stack.get_filename())]\n            slices = stack.get_slices()\n            slices_2 = stack_2.get_slices()\n\n            # test number of slices match\n            self.assertEqual(len(slices), len(slices_2))\n\n            # Test whether header of written slice coincides with transformed\n            # slice\n            for j in range(stack.get_number_of_slices()):\n\n                # Check Spacing\n                self.assertAlmostEqual(\n                    np.max(np.abs(np.array(slices[j].sitk.GetSpacing()) -\n                                  np.array(slices_2[j].sitk.GetSpacing()))),\n                    0, places=10)\n                # Check Origin\n                self.assertAlmostEqual(\n                    np.max(np.abs(np.array(slices[j].sitk.GetOrigin()) -\n                                  np.array(slices_2[j].sitk.GetOrigin()))),\n                    0, places=4)\n                # Check Direction\n                self.assertAlmostEqual(\n                    np.max(np.abs(np.array(slices[j].sitk.GetDirection()) -\n                                  np.array(slices_2[j].sitk.GetDirection()))),\n                    0, places=4)\n\n            # Test whether parameters of written slice transforms match\n            params = np.array(\n                motions_sitk[stack.get_filename()][j].GetParameters())\n            params_2 = np.array(\n                transformations_dic[stack.get_filename()][j].GetParameters())\n            self.assertAlmostEqual(\n                np.max(np.abs(params - params_2)), 0, places=16)\n"
  }
]